Compare commits

..
354 Commits
Author SHA1 Message Date
Axionize c4b30d940a Replace Bukkit Vector with new Vector impl 2024-12-05 03:36:42 -05:00
Axionize c42b0f5b56 Implement Factory, interface, fallback, and Scalar and SIMD methods 2024-12-05 03:36:18 -05:00
Axionize 47ea342731 Expand API definitions 2024-12-05 00:44:56 -05:00
Axionize 2f6ffc5d81 Example SIMD code 2024-11-22 22:17:17 -05:00
Axionize e9337f88e4 Make whether or not to relocate deps read from project properties -> environment variable"
Prevents having to modify build.gradle.kts when disabling relocating.
2024-11-22 21:24:49 -05:00
Axionize fb7295b396 Build with Java 21 toolchain to prevent requiring downloading JDK 18 for contributors 2024-11-22 21:19:28 -05:00
Axionize 2441ab68de Clean up Gradle configuration 2024-11-22 21:17:57 -05:00
Axionize 5784943d8e Create initial project structure proof of concept for adding SIMD support 2024-11-22 20:22:36 -05:00
Axionize d0b94bbda5 Merge remote-tracking branch 'origin/update/1.21.3' into merge 2024-11-22 12:15:58 -05:00
Axionize 6f18622643 Merge branch 'flatten-complexcollisionboxes' into merge 2024-11-22 09:14:08 -05:00
Axionize 6b863655f1 Merge remote-tracking branch 'origin/2.0' into flatten-complexcollisionboxes
# Conflicts:
#	src/main/java/ac/grim/grimac/utils/collisions/HitboxData.java
2024-11-22 09:13:17 -05:00
Axionize 631be06ec2 Remove unused calls to makeShapes() 2024-11-22 09:11:04 -05:00
Axionize 1c4361f2c6 Make makeShapes() CCD extra maxIndex explicit; Fix DynamicCollisionWall error 2024-11-22 09:10:04 -05:00
Cristian Milani 2b5016442b Fix race condition and exception when registering check permissions during rapid consecutive player joins on server start 2024-11-21 20:21:59 -05:00
Axionize e389e4b5e1 Fix Pink Petals Hitbox (Coral Fan) for > 1.12.2 and =< 1.19.3 players 2024-11-21 19:21:28 -05:00
Axionize 7d9cdb91b0 Merge branch 'flatten-complexcollisionboxes' into merge 2024-11-21 18:43:20 -05:00
Axionize 83e06bce77 Make CCD length private var 2024-11-21 18:32:33 -05:00
Axionize 42fd3e1cc7 Rename variable for clarity 2024-11-21 17:14:13 -05:00
Axionize 26a771e4da Fix copying CCBs in edge case 2024-11-21 17:12:38 -05:00
Axionize fad1eb5736 Merge branch 'flatten-complexcollisionboxes' into merge
# Conflicts:
#	src/main/java/ac/grim/grimac/utils/collisions/CollisionData.java
#	src/main/java/ac/grim/grimac/utils/collisions/HitboxData.java
#	src/main/java/ac/grim/grimac/utils/collisions/blocks/DynamicStair.java
#	src/main/java/ac/grim/grimac/utils/collisions/blocks/PistonHeadCollision.java
#	src/main/java/ac/grim/grimac/utils/collisions/blocks/connecting/DynamicConnecting.java
#	src/main/java/ac/grim/grimac/utils/collisions/blocks/connecting/DynamicHitboxFence.java
#	src/main/java/ac/grim/grimac/utils/collisions/datatypes/ComplexCollisionBox.java
2024-11-21 12:18:11 -05:00
Axionize 1dc4361938 Remove debug print statement 2024-11-21 12:06:03 -05:00
Axionize 3d5491de4f Fix logic error again in CCB constructor 2024-11-21 12:04:04 -05:00
Axionize 75ff92c326 Optimize DynamicHitboxFence 2024-11-21 11:59:19 -05:00
Axionize c530dcfa17 Fix logic error in CCB constructor 2024-11-21 11:58:04 -05:00
Axionize d720b8864f Fix new downCast() for SimpleCollisionBox 2024-11-21 11:41:52 -05:00
Axionize 3e3dbf0f30 Manually size ComplexCollisionBoxes Pt. 2 2024-11-21 11:38:32 -05:00
Axionize 0bd154974b Manually size ComplexCollisionBoxes Pt. 1 2024-11-21 10:28:34 -05:00
Axionize 3f57026960 implement new downCast() API for Dynamic, Simple, and No Collision Boxes 2024-11-21 10:26:46 -05:00
Axionize be9841fb9f Port everything to new internal .downCast() API besides getCollisionBoxes 2024-11-20 12:37:52 -05:00
Axionize d8b529e837 Optimize ComplexCollisionBoxes by using primitives internally; Make new more efficient constructors available 2024-11-20 12:37:01 -05:00
Axionize d01805ad13 Merge remote-tracking branch 'machinebreaker/inventory-checks' into merge
# Conflicts:
#	src/main/java/ac/grim/grimac/checks/impl/inventory/InventoryD.java
#	src/main/java/ac/grim/grimac/checks/impl/inventory/InventoryG.java
2024-11-20 12:23:05 -05:00
Axionize f638772c5f Merge remote-tracking branch 'maninmyvan/packetorder' into merge 2024-11-20 12:21:54 -05:00
Axionize b84b21a68f Revert "Remove old .downCast() method for CollisionBoxes"
This reverts commit c91ef450
2024-11-20 12:14:16 -05:00
Axionize da230781e3 Port everything to new internal .downCast() API besides getCollisionBoxes 2024-11-20 12:01:02 -05:00
Axionize c91ef45062 Remove old .downCast() method for CollisionBoxes 2024-11-20 11:23:28 -05:00
Axionize 9a87329618 Optimize ComplexCollisionBoxes by using primitives internally; Make new more efficient constructors available 2024-11-20 11:15:53 -05:00
Axionize 24ee315c0b Merge remote-tracking branch 'origin/2.0' into merge
# Conflicts:
#	src/main/java/ac/grim/grimac/checks/impl/combat/Reach.java
2024-11-19 20:15:21 -05:00
ManInMyVan a4decb57f8 fix more falses 2024-11-17 18:03:13 -07:00
ManInMyVan 81feb5be3f fix falses 2024-11-17 17:25:57 -07:00
MachineBreaker 2253fd0ec3 Use correct operator in InventoryF 2024-11-17 19:46:08 -04:00
MachineBreaker db4ad8e16d Remove InventoryG millis check 2024-11-17 19:41:53 -04:00
MachineBreaker 11571a2b8a Implement inventory desync status 2024-11-17 19:40:28 -04:00
MachineBreaker c615c98d09 Clean imports 2024-11-17 19:24:53 -04:00
MachineBreaker 922b134467 Fix more false positives 2024-11-17 19:20:56 -04:00
MachineBreaker 8714e0e5a9 Merge remote-tracking branch 'origin/inventory-checks' into inventory-checks 2024-11-17 13:53:44 -04:00
MachineBreaker 24570aa658 Update config version 2024-11-17 13:53:35 -04:00
Axionize 743a2d9cdb Ensure ComplexCollisionBoxes can only contain SimpleCollisionBoxes 2024-11-17 11:45:55 -05:00
Axionize 4b28aa7604 Ensure ComplexCollisionBoxes can only contain SimpleCollisionBoxes 2024-11-17 11:36:10 -05:00
Axionize 50425ada16 Remove debug print statement 2024-11-17 10:34:45 -05:00
MachineBreaker 6d4f3650cc Merge branch 'GrimAnticheat:2.0' into inventory-checks 2024-11-17 10:22:07 -04:00
MachineBreaker a5ced59a35 Clean imports 2024-11-17 08:13:07 -04:00
MachineBreaker e9c6fac17a Apply suggested changes 2024-11-17 08:11:54 -04:00
Axionize c68f7bd5f8 Finish merging new inventory checks and packetorder checks; backport to Java 8 2024-11-17 05:36:08 -05:00
Axionize 7015a9efa2 Merge remote-tracking branch 'maninmyvan/packetorder' into merge
# Conflicts:
#	src/main/java/ac/grim/grimac/manager/CheckManager.java
2024-11-17 05:19:11 -05:00
ManInMyVan 06bd96fcae fix a noslow 2024-11-16 18:29:05 -07:00
ManInMyVan db7bbef9b5 fix false when swapping offhand items 2024-11-16 18:14:50 -07:00
ManInMyVan fa0d147e04 fix false when dropping items 2024-11-16 18:14:31 -07:00
ManInMyVan 39e7cbe9ca only setback on item use 2024-11-16 18:14:04 -07:00
ManInMyVan 84a1641968 remove invalid variable default 2024-11-16 18:13:45 -07:00
ManInMyVan 26acd7b3a5 remove sent variable 2024-11-16 18:13:15 -07:00
ManInMyVan f51e5e4eae and use them 2024-11-16 18:12:40 -07:00
ManInMyVan 636eaf3d5d add remaining entity actions to PacketOrderProcessor 2024-11-16 18:10:13 -07:00
ManInMyVan 5b9fdd00c8 rename invalidSlots to invalid 2024-11-16 16:45:53 -07:00
MachineBreaker bc42035866 Fix some false positives 2024-11-16 19:32:50 -04:00
Axionize 917eb371c8 Merge remote-tracking branch 'machinebreaker/inventory-checks' into inventory-checks 2024-11-16 18:09:23 -05:00
Axionize ef45df9ea3 New buffer system 2024-11-16 16:44:39 -05:00
Axionize 9f09fcd103 Fix rapidly breaking/placing instabreak blocks and lighting tnt falses; currently LOSP no longer stops any illegal placements just flags. Will fix later 2024-11-16 14:41:03 -05:00
MachineBreaker e1baecd32e Resync InventoryE 2024-11-16 15:31:41 -04:00
MachineBreaker 44d66e0e5b Requested changes 2024-11-16 15:26:45 -04:00
MachineBreaker 0368f930b8 Woops change check name 2024-11-16 13:12:40 -04:00
MachineBreaker a83a0c8579 Implement experimental entity action while inventory is open check 2024-11-16 13:07:17 -04:00
MachineBreaker 92b1ad28c2 Fix 1.8 clients never letting 1.9+ server know about their inventory status
(Via hack)
2024-11-16 12:59:15 -04:00
MachineBreaker 5302f15b9d Implement experimental 1.8 click window while inventory not open check 2024-11-16 12:50:09 -04:00
Axionize 6a3eec1960 Add optional success field to HitData used to indicate if raytrace hit intended target 2024-11-16 11:44:38 -05:00
Axionize 11425840ad Add getBlockPlaceCheck() method 2024-11-16 11:21:39 -05:00
Axionize 8515df8224 Early exit optimiztion 2024-11-16 11:08:47 -05:00
Axionize fe5d6e34f2 Initial work to eliminate final LOSP falses by tracking recently changed blocks; improve performance by using primitive array for SimpleCollisionBox 2024-11-16 10:56:37 -05:00
MachineBreaker e3e797e19a Woops fix compilation 2024-11-16 11:55:32 -04:00
MachineBreaker 2293092048 Make an inventory check type 2024-11-16 11:54:58 -04:00
MachineBreaker 147081ccc5 Clean 2024-11-16 11:09:47 -04:00
MachineBreaker b924f65f91 Implement held item change while inventory is open check
Also add verbose for the other inventory checks
2024-11-16 11:01:02 -04:00
MachineBreaker 259dd72f87 Use packets to close player inventory 2024-11-16 10:53:40 -04:00
MachineBreaker ab726bda78 Fix conflicts 2024-11-16 10:47:34 -04:00
MachineBreaker 163bbd7b0e Merge remote-tracking branch 'origin/2.0' into inventory-checks
# Conflicts:
#	src/main/java/ac/grim/grimac/manager/CheckManager.java
#	src/main/resources/config/zh.yml
2024-11-16 10:40:51 -04:00
Axionize 968e166bde Fix Button hitboxes for pre-1.13 players 2024-11-15 03:13:01 -05:00
Axionize efe729c4d1 Add new Reach config option to skip checks against non-player-entities 2024-11-15 01:43:47 -05:00
Axionize d40d19d6e3 Bump packetevents back to 2.6.1 (custom) to fix bugs 2024-11-15 01:06:48 -05:00
Axionize b476b143f4 Spotless changes 2024-11-15 01:06:35 -05:00
Axionize bb54697d9c Make switch cases Java 8 compatible 2024-11-15 01:05:39 -05:00
Axionize 24102c15e6 Bump version number back to 2.3.69 2024-11-15 00:46:20 -05:00
Axionize 5100ef06c8 Build with Java 8 compatibility instead of Java 17 2024-11-15 00:44:54 -05:00
Axionize 9b892eab85 Revert "Java 17 update"
This reverts commit 5a6f87fe
2024-11-15 00:43:05 -05:00
Axionize eab5ca7f6d Merge remote-tracking branch 'origin/2.0' into merge
# Conflicts:
#	build.gradle.kts
#	src/main/java/ac/grim/grimac/checks/impl/scaffolding/RotationPlace.java
#	src/main/java/ac/grim/grimac/events/packets/CheckManagerListener.java
#	src/main/java/ac/grim/grimac/utils/collisions/HitboxData.java
#	src/main/java/ac/grim/grimac/utils/collisions/blocks/connecting/DynamicHitboxPane.java
#	src/main/java/ac/grim/grimac/utils/collisions/blocks/connecting/DynamicHitboxWall.java
2024-11-15 00:40:09 -05:00
Axionize fd055c163e Use new packetevents metrics class 2024-11-15 00:32:03 -05:00
Axionize ea78d2ff40 Update packetevents (built locally using custom changes) to fix #3 2024-11-15 00:22:52 -05:00
SamB440 6f074513f2 Merge pull request #1787 from Onako2/2.0
Update dependencies so it works on 1.21.3
2024-11-14 16:18:42 +00:00
ManInMyVan 0719c5bdc6 add PacketOrderO and PacketOrderP 2024-11-11 19:19:41 -07:00
ManInMyVan 36a394d142 add PacketOrderN 2024-11-11 19:19:41 -07:00
ManInMyVan 0e08dbc6f2 Add PacketOrderM 2024-11-11 19:19:41 -07:00
ManInMyVan 003aa67a41 remove unused method 2024-11-11 19:19:41 -07:00
ManInMyVan be45a43524 rename exempt-digging to exempt-placing-while-digging in PAcketOrderI
and make it work
2024-11-11 19:19:41 -07:00
ManInMyVan 0d700848f8 rename PacketOrderM to PacketOrderA 2024-11-11 19:19:41 -07:00
ManInMyVan 1ff402036d revert renaming post 2024-11-11 19:19:40 -07:00
ManInMyVan 9d36dbd2b8 add PacketOrderM 2024-11-11 19:19:40 -07:00
ManInMyVan c53c4378d8 use PacketOrderProcessor in PacketOrderK 2024-11-11 19:19:40 -07:00
ManInMyVan eacf176f88 rewrite 2024-11-11 19:19:40 -07:00
ManInMyVan 13afb067bb fix falses 2024-11-11 19:19:39 -07:00
ManInMyVan 7af2f4b2e2 don't cancel the tick 2024-11-11 19:19:39 -07:00
ManInMyVan a1d8bfa6a1 more PacketOrderC stuff 2024-11-11 19:19:39 -07:00
ManInMyVan 47b3fe9ca2 fixes 2024-11-11 19:19:39 -07:00
ManInMyVan 0ce52ea107 oops 2024-11-11 19:19:39 -07:00
ManInMyVan d98fe31412 register PacketOrderL 2024-11-11 19:19:39 -07:00
ManInMyVan d8f338aada fix falses 2024-11-11 19:19:39 -07:00
ManInMyVan f5965c48b3 exemptBreaking -> exempt-breaking 2024-11-11 19:19:39 -07:00
ManInMyVan 0df7077ddb Add PacketOrderK 2024-11-11 19:19:38 -07:00
ManInMyVan 222a9341bf exemptBreaking option in PacketOrdlerL 2024-11-11 19:19:38 -07:00
ManInMyVan 35f6f7870a make PacketOrderE setback
https://github.com/CCBlueX/LiquidBounce/blob/nextgen/src/main/kotlin/net/ccbluex/liquidbounce/features/module/modules/movement/noslow/modes/blocking/NoSlowBlockingSwitch.kt
2024-11-11 19:19:38 -07:00
ManInMyVan 15b6e0ba27 check ENTITY_ACTION packets in PacketOrderL 2024-11-11 19:19:38 -07:00
ManInMyVan 968754efe0 fix some PacketOrderF falses 2024-11-11 19:19:38 -07:00
ManInMyVan a03c9e0194 Add PacketOrderL 2024-11-11 19:19:38 -07:00
ManInMyVan 734e7cbae8 nvm it is valid on 1.8 2024-11-11 19:19:38 -07:00
ManInMyVan 52fe6691ac this packet order is only valid in 1.7 client 2024-11-11 19:19:38 -07:00
ManInMyVan acd8d1192c Check entity in PacketOrderC 2024-11-11 19:19:38 -07:00
ManInMyVan 8fc309cb76 these are already packet checks 2024-11-11 19:19:37 -07:00
ManInMyVan 45f16ec10f Check digging packets in PacketOrderF 2024-11-11 19:19:37 -07:00
ManInMyVan 5ff8cb21c8 Update PacketOrderI.java 2024-11-11 19:19:37 -07:00
ManInMyVan f8997b8739 Add PacketOrderJ 2024-11-11 19:19:37 -07:00
ManInMyVan ed21464c3a Add PacketOrderI 2024-11-11 19:19:37 -07:00
ManInMyVan 7954575216 Add PacketOrderH 2024-11-11 19:19:37 -07:00
ManInMyVan 1a11d3e733 Add PacketOrderG 2024-11-11 19:19:37 -07:00
ManInMyVan 487aa45a27 add PacketOrderF 2024-11-11 19:19:37 -07:00
ManInMyVan b7f9aeab06 add PacketOrderE 2024-11-11 19:19:36 -07:00
ManInMyVan 6aaee382a6 PacketOrder check catagory 2024-11-11 19:19:30 -07:00
Onako2 8c84c506bf Update dependencies for 1.21.3 support 2024-11-11 22:03:35 +01:00
Axionize 0622f92cf1 Revert "Remove unncessary NO_COLLISION entry for torchcrop"
This reverts commit e149ca6be8.
2024-11-03 18:14:12 -05:00
Axionize a8f81c141a Revert "Remove unncessary NO_COLLISION entries"
This reverts commit 1761d853de.
2024-11-03 18:14:11 -05:00
Axionize efffceb9f2 Fix scaffolding hitbox for pre-1.14 clients 2024-11-03 07:05:40 -05:00
Axionize 75d1fc32c8 Enable checks against scaffolding 2024-11-03 06:58:37 -05:00
Axionize e087e93e2a Fix Weeping Vines and Vine Plant for all versions; Fix Weeping + Twisted for pre 1.16 players 2024-11-03 06:34:04 -05:00
Axionize e83c9967b4 Fix pitcher crop for pre-1.20 players 2024-11-03 05:51:34 -05:00
Axionize 2b1896020d Fix sniffer egg for pre 1.20 players 2024-11-03 05:46:06 -05:00
Axionize 61b17907ee Fix frogspawn for pre-1.19 players 2024-11-03 05:45:50 -05:00
Axionize 339fb42f37 Fix Sculk Shrieker hit box for all versions and collision box for pre 1.18.2 2024-11-03 05:33:50 -05:00
Axionize 7985275697 Fix Spore Blossom hitbox on < 1.17 2024-11-03 05:18:42 -05:00
Axionize 661b60d07c Fix glow lichen and sculk vein boxes 2024-11-03 05:15:31 -05:00
Axionize d4f647e18b Fix Cauldron Hit/Collision Box 2024-11-03 03:51:39 -05:00
Axionize 1761d853de Remove unncessary NO_COLLISION entries 2024-11-03 02:59:10 -05:00
Axionize 42a6543f9b Fix Pink Petals Boxes 2024-11-03 02:51:23 -05:00
Axionize 9356c6c323 Add new union() function to collision boxes 2024-11-03 02:41:27 -05:00
Axionize a7a92f7e10 Fix Coral Fan Collision box 2024-11-03 01:44:04 -04:00
Axionize e149ca6be8 Remove unncessary NO_COLLISION entry for torchcrop 2024-11-03 01:17:36 -04:00
Axionize d44493743d Cleanup and documnet why 2nd rayTrace in getNearestReachHitResult is intentionally non-optimal 2024-11-03 00:48:51 -04:00
Axionize 7eb030de7d Remove unsued getFacing() from vector code 2024-11-02 22:34:03 -04:00
Axionize 8fd4a4a87d 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:33:08 -04:00
Axionize 8f71a427fd Annotate LOSP functions 2024-11-02 04:36:56 -04:00
Axionize 8b9bc356f2 Merge remote-tracking branch 'origin/2.0' into merge
# Conflicts:
#	src/main/java/ac/grim/grimac/checks/impl/scaffolding/RotationPlace.java
#	src/main/java/ac/grim/grimac/player/GrimPlayer.java
2024-11-01 21:31:23 -04:00
Axionize 89b6c8306e Fix falses on dead corals 2024-11-01 21:25:08 -04:00
Axionize 32a09aa36b Merge remote-tracking branch 'origin/2.0' into merge
# Conflicts:
#	src/main/java/ac/grim/grimac/player/GrimPlayer.java
2024-10-27 04:31:43 -04:00
Axionize 3ea716c580 Merge remote-tracking branch 'origin/2.0' into merge 2024-10-20 03:01:42 -04:00
Axionize 328d347845 Merge remote-tracking branch 'origin/2.0' into merge 2024-10-14 13:05:26 -04:00
Axionize d394c3d67c Revert Java 17 changes 2024-10-10 23:59:36 -04:00
Axionize 17b6b8a4e7 Merge branch 'los-containers' into merge 2024-10-10 23:54:56 -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
Axionize 91ee057f4a Merge remote-tracking branch 'origin/2.0' into merge
# Conflicts:
#	src/main/java/ac/grim/grimac/checks/impl/combat/Reach.java
2024-10-10 23:38:40 -04:00
Axionize c26afbc0af Merge branch 'GrimAnticheat:2.0' into merge 2024-10-08 00:08:05 -04:00
Axionize be56e28009 Merge branch 'fix-attribute-clamp' into merge 2024-10-05 17:35:57 -04:00
Axionize 0697c425ca Document SimpleCollisionBox and HexCollisionBox with Javadoc 2024-10-03 03:40:32 -04:00
Axionize ca0ca471fb Document SimpleCollisionBox and HexCollisionBox with Javadoc 2024-10-03 03:38:58 -04:00
Axionize 7b2aac3874 Version Bump 2024-10-03 02:25:59 -04:00
Axionize fbe8376ca0 Remove experimental check flag from Reach 2024-10-03 02:15:33 -04:00
Axionize 22bc18859b Modernize reach check 2024-10-03 01:29:18 -04:00
Axionize 25f9608d58 Merge branch 'reach-wall-hit' into LOSP-reach-wall-check
# Conflicts:
#	src/main/java/ac/grim/grimac/events/packets/CheckManagerListener.java
#	src/main/java/ac/grim/grimac/utils/nmsutil/BlockRayTrace.java
2024-10-03 01:26:45 -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
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
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
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
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
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
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
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
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
Axionize 8854b00b46 Support for buttons in 1.7 2024-09-05 21:03:12 -04:00
Axionize 4be835de07 Skip redstone wires 2024-09-05 20:38:05 -04: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
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
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
Axionize 050c22ce27 Clean up 2024-08-29 17:02:29 -04:00
Axionize ded614f30f MVP commit 2024-08-29 17:02:28 -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
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
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
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
SamB440 831f68b00c Check for hitting through walls 2024-06-29 18:11:05 +01:00
MachineBreaker bccda6cb9e Merge branch 'GrimAnticheat:2.0' into inventory-checks 2023-11-04 00:17:56 -04:00
MachineBreaker d0e77d2959 InventoryD: Exempt last block place use item if < 50L 2023-11-03 09:06:48 -04:00
MachineBreaker 441ff72e1e Merge branch 'GrimAnticheat:2.0' into inventory-checks 2023-11-03 09:05:45 -04:00
MachineBreaker aff131394e Don't handle InventoryD predictions if slowed down by a item. 2023-10-17 04:25:06 -04:00
MachineBreaker 7a6f758853 Merge branch 'GrimAnticheat:2.0' into inventory-checks 2023-10-17 04:24:24 -04:00
MachineBreaker eef8f538bc Merge branch 'GrimAnticheat:2.0' into inventory-checks 2023-09-25 19:55:46 -04:00
MachineBreaker 42d79ea01f Exempt teleports for InventoryD
Fixes a false positive
2023-09-18 17:42:08 -04:00
MachineBreaker df2c92555f Update fr.yml 2023-09-18 17:40:42 -04:00
MachineBreaker 380f6b5581 Update fr.yml 2023-09-18 17:40:12 -04:00
MachineBreaker fb6450fb72 Merge branch '2.0' into inventory-checks 2023-09-18 17:38:59 -04:00
Alejandro Martinez bb01c25605 Close inventory synchronously 2023-05-25 19:32:55 -04:00
Alejandro Martinez d812e7dd8b Don't setback for inventory checks 2023-05-21 13:20:28 -04:00
Alejandro Martinez 60f3a765f3 Fix section typo 2023-05-21 11:26:10 -04:00
Alejandro Martinez 31d6bd3664 Bump config version & add missing config upgrade 2023-05-21 11:24:49 -04:00
MachineBreaker 4c0018b04c Merge branch 'GrimAnticheat:2.0' into inventory-checks 2023-05-21 10:49:06 -04:00
MachineBreaker 4b0b3df9d2 Merge pull request #2 from UserNugget/inventory-checks-hotfix
Make transaction id "NONE" really impossible
2023-04-05 06:05:47 -06:00
UserNugget f699be1a65 Make transaction id "NONE" really impossible 2023-04-05 14:39:43 +03:00
MachineBreaker b6496b31f0 Merge pull request #1 from UserNugget/inventory-checks
InventoryWalk check
2023-04-05 05:17:56 -06:00
UserNugget 1e601c3de9 Remove pointless optimization 2023-04-05 10:57:39 +03:00
MachineBreaker 00b39fec38 Merge branch 'GrimAnticheat:2.0' into inventory-checks 2023-04-04 20:50:49 -06:00
UserNugget 9224ab8a76 Correct some comments 2023-04-05 05:03:29 +03:00
UserNugget 5307aebbf5 Some small fixes 2023-04-04 23:00:34 +03:00
UserNugget e7d8d613aa Most likely fixed false flags 2023-04-04 22:56:22 +03:00
UserNugget f469b08505 Close inventory to prevent infinity setbacks 2023-04-04 22:34:48 +03:00
UserNugget 60b2a07659 Initial InventoryD rewrite 2023-04-04 15:28:46 +03:00
MachineBreaker 8d6d448073 Merge branch 'GrimAnticheat:2.0' into inventory-checks 2023-04-02 17:17:22 -06:00
Alejandro Martinez 798156fedc Merge branch 'inventory-checks' of https://github.com/MachineBreaker/Grim into inventory-checks
 Conflicts:
	src/main/java/ac/grim/grimac/utils/latency/CompensatedInventory.java
2023-03-19 08:23:30 -06:00
MachineBreaker e18510fc6b Merge branch 'GrimAnticheat:2.0' into inventory-checks 2023-03-19 08:23:09 -06:00
Alejandro Martinez 0fe96bf32d Implement InventoryD (Clicking inventory while moving) 2023-03-19 08:22:04 -06:00
noobi 6d1f491b80 Fix InventoryC missing setback vls 2023-03-19 00:27:49 +01:00
noobi 07ac8b99f0 Implement InventoryC (Placing while inventory is open) 2023-03-19 00:16:05 +01:00
Alejandro Martinez 622c35e935 Implement InventoryB (Digging while inventory is open) 2023-03-18 17:03:00 -06:00
Alejandro Martinez c6adad5992 Make code more readable 2023-03-18 16:24:23 -06:00
Alejandro Martinez 211722a8a7 Properly exempt beacon inventory 2023-03-18 16:22:51 -06:00
Alejandro Martinez a0082a4ce7 Merge remote-tracking branch 'origin/inventory-checks' into inventory-checks 2023-03-18 16:17:42 -06:00
Alejandro Martinez a4eb540d23 Implement InventoryA (Attack while inventory is open) 2023-03-18 16:16:51 -06:00
MachineBreaker 2d427d6c47 Merge branch 'GrimAnticheat:2.0' into inventory-checks 2023-03-18 14:19:25 -06:00
Alejandro Martinez 0d00168aaf Initial work of Inventory Checks 2023-03-17 04:25:35 -06:00
163 changed files with 5406 additions and 1470 deletions

No files matched your search

+46 -10
View File
@@ -26,16 +26,15 @@ spotless {
}
group = "ac.grim.grimac"
version = "2.3.68"
version = "2.3.69"
description = "Libre simulation anticheat designed for 1.21 with 1.8-1.21 support, powered by PacketEvents 2.0."
java {
toolchain.languageVersion.set(JavaLanguageVersion.of(17))
}
// Set to false for debug builds
// You cannot live reload classes if the jar relocates dependencies
var relocate = true;
// Checks Project properties -> environment variable -> defaults true
val relocate: Boolean = project.findProperty("relocate")?.toString()?.toBoolean()
?: System.getenv("RELOCATE_JAR")?.toBoolean()
?: true
repositories {
mavenLocal()
@@ -57,10 +56,10 @@ repositories {
}
dependencies {
implementation("com.github.retrooper:packetevents-spigot:2.5.1-SNAPSHOT")
implementation("com.github.retrooper:packetevents-spigot:2.6.1-SNAPSHOT")
implementation("co.aikar:acf-paper:0.5.1-SNAPSHOT")
implementation("club.minnced:discord-webhooks:0.8.0") // Newer versions include kotlin-stdlib, which leads to incompatibility with plugins that use Kotlin
implementation("it.unimi.dsi:fastutil:8.5.13")
implementation("it.unimi.dsi:fastutil:8.5.15")
implementation("github.scarsz:configuralize:1.4.0")
//implementation("com.github.grimanticheat:grimapi:1193c4fa41")
@@ -68,9 +67,9 @@ dependencies {
implementation("com.github.grimanticheat:grimapi:fc5634e444")
implementation("org.jetbrains:annotations:24.1.0")
compileOnly("org.geysermc.floodgate:api:2.0-SNAPSHOT")
compileOnly("org.geysermc.floodgate:api:2.2.3-SNAPSHOT")
compileOnly("org.spigotmc:spigot-api:1.18.2-R0.1-SNAPSHOT")
compileOnly("com.viaversion:viaversion-api:5.0.4-SNAPSHOT")
compileOnly("com.viaversion:viaversion-api:5.1.1-SNAPSHOT")
//
compileOnly("io.netty:netty-all:4.1.85.Final")
}
@@ -156,6 +155,43 @@ publishing.publications.create<MavenPublication>("maven") {
artifact(tasks["shadowJar"])
}
java {
sourceSets {
create("java18") {
java {
srcDirs("src/main/java18")
}
compileClasspath += main.get().output
runtimeClasspath += main.get().output
}
}
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
toolchain {
languageVersion.set(JavaLanguageVersion.of(21))
}
}
tasks.withType<JavaCompile> {
if (name == "compileJava18Java") {
options.compilerArgs.addAll(listOf("--add-modules", "jdk.incubator.vector", "-Xlint:unchecked"))
sourceCompatibility = "18"
targetCompatibility = "18"
}
}
tasks.withType<Jar> {
manifest {
attributes("Multi-Release" to "true")
}
duplicatesStrategy = DuplicatesStrategy.INCLUDE // Important for multi-release JARs
from(sourceSets.main.get().output)
from(sourceSets.getByName("java18").output) {
into("META-INF/versions/18")
}
}
tasks.shadowJar {
minimize()
archiveFileName.set("${project.name}-${project.version}.jar")
@@ -0,0 +1,149 @@
package ac.grim.grimac.utils.vector;
import jdk.incubator.vector.*;
public final class SIMDVector3D implements Vector3D {
private static final VectorSpecies<Double> SPECIES = DoubleVector.SPECIES_PREFERRED;
private static final VectorMask<Double> LENGTH_3_ARRAY_MASK = SPECIES.indexInRange(0, 3);
private static final VectorShuffle<Double> CROSS_PRODUCT_SHUFFLE1 = VectorShuffle.fromArray(SPECIES, new int[]{1, 2, 0, 3}, 0);
private static final VectorShuffle<Double> CROSS_PRODUCT_SHUFFLE2 = VectorShuffle.fromArray(SPECIES, new int[]{2, 0, 1, 3}, 0);
private static final DoubleVector FOURTH_COMPONENT_ZERO = DoubleVector.zero(SPECIES).withLane(3, 1.0);
private DoubleVector vector;
public SIMDVector3D(double x, double y, double z) {
this.vector = DoubleVector.fromArray(SPECIES, new double[]{x, y, z, 0}, 0);
}
@Override
public double getX() {
return this.vector.lane(0);
}
@Override
public double getY() {
return this.vector.lane(1);
}
@Override
public double getZ() {
return this.vector.lane(2);
}
@Override
public Vector3D setX(double x) {
this.vector = this.vector.withLane(0, x);
return this;
}
@Override
public Vector3D setY(double y) {
this.vector = this.vector.withLane(1, y);
return this;
}
@Override
public Vector3D setZ(double z) {
this.vector = this.vector.withLane(2, z);
return this;
}
@Override
public double length() {
return Math.sqrt(lengthSquared());
}
@Override
public double lengthSquared() {
return this.vector.mul(this.vector).reduceLanes(VectorOperators.ADD);
}
@Override
public Vector3D multiply(double m) {
this.vector = this.vector.mul(m);
return this;
}
@Override
public Vector3D normalize() {
// DoubleVector squared = this.vector.mul(this.vector);
// double sum = squared.reduceLanes(VectorOperators.ADD);
// double length = Math.sqrt(sum);
// this.vector = this.vector.div(length);
// return this;
double length = length();
this.vector = this.vector.div(length).blend(FOURTH_COMPONENT_ZERO, LENGTH_3_ARRAY_MASK.not());
return this;
}
@Override
public Vector3D crossProduct(Vector3D other) {
SIMDVector3D o = (SIMDVector3D) other;
DoubleVector tmp0 = this.vector.rearrange(CROSS_PRODUCT_SHUFFLE1);
DoubleVector tmp1 = o.vector.rearrange(CROSS_PRODUCT_SHUFFLE2);
DoubleVector tmp2 = tmp0.mul(o.vector);
DoubleVector tmp3 = tmp0.mul(tmp1);
DoubleVector tmp4 = tmp2.rearrange(CROSS_PRODUCT_SHUFFLE1);
this.vector = tmp3.sub(tmp4).blend(FOURTH_COMPONENT_ZERO, LENGTH_3_ARRAY_MASK.not());
return this;
}
@Override
public Vector3D add(Vector3D o) {
this.vector = this.vector.add(((SIMDVector3D) o).vector);
return this;
}
@Override
public Vector3D subtract(Vector3D o) {
this.vector = this.vector.sub(((SIMDVector3D) o).vector);
return this;
}
@Override
public Vector3D multiply(Vector3D o) {
this.vector = this.vector.mul(((SIMDVector3D) o).vector);
return this;
}
@Override
public double distance(Vector3D o) {
return Math.sqrt(distanceSquared(o));
}
@Override
public double distanceSquared(Vector3D o) {
DoubleVector diff = this.vector.sub(((SIMDVector3D) o).vector);
return diff.mul(diff).reduceLanes(VectorOperators.ADD);
}
@Override
public double dot(Vector3D o) {
return this.vector.mul(((SIMDVector3D) o).vector).reduceLanes(VectorOperators.ADD);
}
@Override
public Vector3D clone() {
// I think this makes a shallow clone if I do it like this?
// try {
// return (Vector3D) super.clone();
// } catch (CloneNotSupportedException e) {
// throw new Error(e);
// }
try {
SIMDVector3D cloned = (SIMDVector3D) super.clone();
// Create a new DoubleVector with the same values
cloned.vector = DoubleVector.fromArray(SPECIES,
new double[]{this.getX(), this.getY(), this.getZ(), 0}, 0);
return cloned;
} catch (CloneNotSupportedException e) {
throw new Error(e);
}
}
}
@@ -0,0 +1,13 @@
package ac.grim.grimac.utils.vector;
import jdk.incubator.vector.VectorMask;
import jdk.incubator.vector.VectorSpecies;
import jdk.incubator.vector.DoubleVector;
public class VectorOperationsJava18 implements VectorOperations {
@Override
public Vector3D newVector(double x, double y, double z) {
return new SIMDVector3D(x, y, z);
}
}
@@ -59,15 +59,15 @@ public class AimProcessor extends Check implements RotationCheck {
if (this.xRotMode.size() > SIGNIFICANT_SAMPLES_THRESHOLD) {
Pair<Double, Integer> modeX = this.xRotMode.getMode();
if (modeX.second() > SIGNIFICANT_SAMPLES_THRESHOLD) {
this.modeX = modeX.first();
if (modeX.getSecond() > SIGNIFICANT_SAMPLES_THRESHOLD) {
this.modeX = modeX.getFirst();
this.sensitivityX = convertToSensitivity(this.modeX);
}
}
if (this.yRotMode.size() > SIGNIFICANT_SAMPLES_THRESHOLD) {
Pair<Double, Integer> modeY = this.yRotMode.getMode();
if (modeY.second() > SIGNIFICANT_SAMPLES_THRESHOLD) {
this.modeY = modeY.first();
if (modeY.getSecond() > SIGNIFICANT_SAMPLES_THRESHOLD) {
this.modeY = modeY.getFirst();
this.sensitivityY = convertToSensitivity(this.modeY);
}
}
@@ -39,7 +39,7 @@ public class BadPacketsO extends Check implements PacketCheck {
boolean hasID = false;
for (Pair<Long, Long> iterator : keepaliveMap) {
if (iterator.first() == id) {
if (iterator.getFirst() == id) {
hasID = true;
break;
}
@@ -55,7 +55,7 @@ public class BadPacketsO extends Check implements PacketCheck {
do {
data = keepaliveMap.poll();
if (data == null) break;
} while (data.first() != id);
} while (data.getFirst() != id);
}
}
}
@@ -1,6 +1,5 @@
package ac.grim.grimac.checks.impl.badpackets;
import ac.grim.grimac.GrimAPI;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PacketCheck;
@@ -9,48 +8,22 @@ import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.protocol.item.type.ItemTypes;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.player.DiggingAction;
import com.github.retrooper.packetevents.protocol.world.states.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 com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerDigging;
@CheckData(name = "BadPacketsX")
@CheckData(name = "BadPacketsX", experimental = true)
public class BadPacketsX extends Check implements PacketCheck {
public BadPacketsX(GrimPlayer player) {
super(player);
}
private int lastTick;
private boolean didLastFlag;
private Vector3i lastBreakLoc;
private StateType lastBlockType;
public final boolean noFireHitbox = player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_15_2);
public final void handle(PacketReceiveEvent event, WrapperPlayClientPlayerDigging dig, StateType block) {
if (dig.getAction() != DiggingAction.START_DIGGING && dig.getAction() != DiggingAction.FINISHED_DIGGING)
return;
// Fixes false from breaking kelp underwater
// The client sends two start digging packets to the server both in the same tick. BadPacketsX gets called twice, doesn't false the first time, but falses the second
// One ends up breaking the kelp, the other ends up doing nothing besides falsing this check because we think they're trying to mine water
// I am explicitly making this patch as narrow and specific as possible to potentially discover other blocks that exhibit similar behaviour
int newTick = GrimAPI.INSTANCE.getTickManager().currentTick;
if (lastTick == newTick
&& lastBreakLoc.equals(dig.getBlockPosition())
&& !didLastFlag
&& lastBlockType.getHardness() == 0.0F
&& lastBlockType.getBlastResistance() == 0.0F
&& block == StateTypes.WATER
) {
return;
}
lastTick = newTick;
lastBreakLoc = dig.getBlockPosition();
lastBlockType = block;
// the block does not have a hitbox
boolean invalid = (block == StateTypes.LIGHT && !(player.getInventory().getHeldItem().is(ItemTypes.LIGHT) || player.getInventory().getOffHand().is(ItemTypes.LIGHT)))
|| block.isAir()
@@ -63,11 +36,8 @@ public class BadPacketsX extends Check implements PacketCheck {
|| block.getHardness() == -1.0f && dig.getAction() == DiggingAction.FINISHED_DIGGING;
if (invalid && flagAndAlert("block=" + block.getName() + ", type=" + dig.getAction()) && shouldModifyPackets()) {
didLastFlag = true;
event.setCancelled(true);
player.onPacketCancel();
} else {
didLastFlag = false;
}
}
}
@@ -21,9 +21,13 @@ import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PacketCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
import ac.grim.grimac.utils.data.HitData;
import ac.grim.grimac.utils.data.Pair;
import ac.grim.grimac.utils.data.packetentity.PacketEntity;
import ac.grim.grimac.utils.nmsutil.BlockRayTrace;
import ac.grim.grimac.utils.data.packetentity.dragon.PacketEntityEnderDragonPart;
import ac.grim.grimac.utils.nmsutil.ReachUtils;
import ac.grim.grimac.utils.vector.Vector3D;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.protocol.attribute.Attributes;
import com.github.retrooper.packetevents.protocol.entity.type.EntityType;
@@ -31,14 +35,24 @@ import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.player.GameMode;
import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState;
import com.github.retrooper.packetevents.util.Vector3d;
import com.github.retrooper.packetevents.util.Vector3i;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientInteractEntity;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerFlying;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import org.bukkit.util.Vector;
import ac.grim.grimac.utils.vector.Vector3D;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
// You may not copy the check unless you are licensed under GPL
@CheckData(name = "Reach", configName = "Reach", setback = 10)
@@ -46,11 +60,15 @@ public class Reach extends Check implements PacketCheck {
// Only one flag per reach attack, per entity, per tick.
// We store position because lastX isn't reliable on teleports.
private final Int2ObjectMap<Vector3d> playerAttackQueue = new Int2ObjectOpenHashMap<>();
// Used to prevent falses in the wall hit check
private final Set<Vector3i> blocksChangedThisTick = new HashSet<>();
private static final List<EntityType> blacklisted = Arrays.asList(
EntityTypes.BOAT,
EntityTypes.CHEST_BOAT,
EntityTypes.SHULKER);
private boolean ignoreNonPlayerTargets;
private boolean cancelImpossibleHits;
private double threshold;
private double cancelBuffer; // For the next 4 hits after using reach, we aggressively cancel reach
@@ -84,6 +102,10 @@ public class Reach extends Check implements PacketCheck {
return;
}
if (ignoreNonPlayerTargets && !entity.getType().equals(EntityTypes.PLAYER)) {
return;
}
// Dead entities cause false flags (https://github.com/GrimAnticheat/Grim/issues/546)
if (entity.isDead) return;
@@ -108,10 +130,11 @@ public class Reach extends Check implements PacketCheck {
}
// If the player set their look, or we know they have a new tick
if (WrapperPlayClientPlayerFlying.isFlying(event.getPacketType()) ||
final boolean isFlying = WrapperPlayClientPlayerFlying.isFlying(event.getPacketType());
if (isFlying ||
event.getPacketType() == PacketType.Play.Client.PONG ||
event.getPacketType() == PacketType.Play.Client.WINDOW_CONFIRMATION) {
tickBetterReachCheckWithAngle();
tickBetterReachCheckWithAngle(isFlying);
}
}
@@ -143,7 +166,7 @@ public class Reach extends Check implements PacketCheck {
}
}
private void tickBetterReachCheckWithAngle() {
private void tickBetterReachCheckWithAngle(boolean isFlying) {
for (Int2ObjectMap.Entry<Vector3d> attack : playerAttackQueue.int2ObjectEntrySet()) {
PacketEntity reachEntity = player.compensatedEntities.entityMap.get(attack.getIntKey());
if (reachEntity != null) {
@@ -158,6 +181,9 @@ public class Reach extends Check implements PacketCheck {
}
}
playerAttackQueue.clear();
// We can't use transactions for this because of this problem:
// transaction -> block changed applied -> 2nd transaction -> list cleared -> attack packet -> flying -> reach block hit checked, falses
if (isFlying) blocksChangedThisTick.clear();
}
private String checkReach(PacketEntity reachEntity, Vector3d from, boolean isPrediction) {
@@ -185,7 +211,7 @@ public class Reach extends Check implements PacketCheck {
double minDistance = Double.MAX_VALUE;
// https://bugs.mojang.com/browse/MC-67665
List<Vector> possibleLookDirs = new ArrayList<>(Collections.singletonList(ReachUtils.getLook(player, player.xRot, player.yRot)));
List<Vector3D> possibleLookDirs = new ArrayList<>(Collections.singletonList(ReachUtils.getLook(player, player.xRot, player.yRot)));
// If we are a tick behind, we don't know their next look so don't bother doing this
if (!isPrediction) {
@@ -205,12 +231,12 @@ public class Reach extends Check implements PacketCheck {
// +3 would be 3 + 3 = 6, which is the pre-1.20.5 behaviour, preventing "Missed Hitbox"
final double distance = player.compensatedEntities.getSelf().getAttributeValue(Attributes.PLAYER_ENTITY_INTERACTION_RANGE) + 3;
final double[] possibleEyeHeights = player.getPossibleEyeHeights();
for (Vector lookVec : possibleLookDirs) {
for (Vector3D lookVec : possibleLookDirs) {
for (double eye : possibleEyeHeights) {
Vector eyePos = new Vector(from.getX(), from.getY() + eye, from.getZ());
Vector endReachPos = eyePos.clone().add(new Vector(lookVec.getX() * distance, lookVec.getY() * distance, lookVec.getZ() * distance));
ac.grim.grimac.utils.vector.Vector3D eyePos = newVector3D(from.getX(), from.getY() + eye, from.getZ());
ac.grim.grimac.utils.vector.Vector3D endReachPos = eyePos.clone().add(newVector3D(lookVec.getX() * distance, lookVec.getY() * distance, lookVec.getZ() * distance));
Vector intercept = ReachUtils.calculateIntercept(targetBox, eyePos, endReachPos).first();
Vector3D intercept = ReachUtils.calculateIntercept(targetBox, eyePos, endReachPos).getFirst();
if (ReachUtils.isVecInside(targetBox, eyePos)) {
minDistance = 0;
@@ -223,9 +249,25 @@ public class Reach extends Check implements PacketCheck {
}
}
HitData foundHitData = null;
// If the entity is within range of the player (we'll flag anyway if not, so no point checking blocks in this case)
// Ignore when could be hitting through a moving shulker, piston blocks. They are just too glitchy/uncertain to check.
if (minDistance <= 3 && !player.compensatedWorld.isNearHardEntity(player.boundingBox.copy().expand(4))) {
final @Nullable Pair<Double, HitData> targetBlock = getTargetBlock(player, possibleLookDirs, from, minDistance);
// And if the target block is closer to the player than the entity box, they should hit the block instead
// So, this hit is invalid.
if (targetBlock != null && targetBlock.getFirst() < (minDistance * minDistance)) { // targetBlock is squared
minDistance = Double.MIN_VALUE;
foundHitData = targetBlock.getSecond();
}
}
// if the entity is not exempt and the entity is alive
if ((!blacklisted.contains(reachEntity.getType()) && reachEntity.isLivingEntity()) || reachEntity.getType() == EntityTypes.END_CRYSTAL) {
if (minDistance == Double.MAX_VALUE) {
if (minDistance == Double.MIN_VALUE && foundHitData != null) {
cancelBuffer = 1;
return "Hit block block=" + foundHitData.getState().getType().getName();
} else if (minDistance == Double.MAX_VALUE) {
cancelBuffer = 1;
return "Missed hitbox";
} else if (minDistance > player.compensatedEntities.getSelf().getAttributeValue(Attributes.PLAYER_ENTITY_INTERACTION_RANGE)) {
@@ -239,8 +281,48 @@ public class Reach extends Check implements PacketCheck {
return null;
}
public void handleBlockChange(Vector3i vector3i, WrappedBlockState state) {
if (blocksChangedThisTick.size() >= 40) return; // Don't let players freeze movement packets to grow this
// Only do this for nearby blocks
if (newVector3D(vector3i.x, vector3i.y, vector3i.z).distanceSquared(newVector3D(player.x, player.y, player.z)) > 6) return;
// Only do this if the state really had any world impact
if (state.equals(player.compensatedWorld.getWrappedBlockStateAt(vector3i))) return;
blocksChangedThisTick.add(vector3i);
}
// Returns a pair so we can check the block type in the flag
@Nullable
private Pair<Double, HitData> getTargetBlock(GrimPlayer player, List<Vector3D> possibleLookDirs, Vector3d from, double minDistance) {
// Check every possible look direction and every possible eye height
// IF *NONE* of them allow the player to hit the entity, this is an invalid hit
HitData bestHitData = null;
double min = Double.MAX_VALUE;
for (Vector3D lookVec : possibleLookDirs) {
for (double eye : player.getPossibleEyeHeights()) {
Vector3D eyes = newVector3D(from.getX(), from.getY() + eye, from.getZ());
final double reach = player.compensatedEntities.getSelf().getAttributeValue(Attributes.PLAYER_BLOCK_INTERACTION_RANGE);
final HitData hitResult = BlockRayTrace.getNearestReachHitResult(player, eyes, lookVec, minDistance, reach);
if (hitResult == null) {
return null;
}
final double distance = eyes.distanceSquared(hitResult.getBlockHitLocation());
// Block changes are uncertain, can't check this tick
if (distance < (minDistance * minDistance) && blocksChangedThisTick.contains(hitResult.getPosition())) {
return null;
}
bestHitData = hitResult;
min = Math.min(min, distance);
}
}
return bestHitData == null ? null : Pair.of(min, bestHitData);
}
@Override
public void onReload(ConfigManager config) {
this.ignoreNonPlayerTargets = config.getBooleanElse("Reach.ignore-non-player-targets", false);
this.cancelImpossibleHits = config.getBooleanElse("Reach.block-impossible-hits", true);
this.threshold = config.getDoubleElse("Reach.threshold", 0.0005);
}
@@ -0,0 +1,41 @@
package ac.grim.grimac.checks.impl.inventory;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.InventoryCheck;
import ac.grim.grimac.player.GrimPlayer;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientInteractEntity;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientInteractEntity.InteractAction;
@CheckData(name = "InventoryA", setback = 3, description = "Attacked an entity while inventory is open")
public class InventoryA extends InventoryCheck {
public InventoryA(GrimPlayer player) {
super(player);
}
@Override
public void onPacketReceive(PacketReceiveEvent event) {
super.onPacketReceive(event);
if (event.getPacketType() == PacketType.Play.Client.INTERACT_ENTITY) {
WrapperPlayClientInteractEntity wrapper = new WrapperPlayClientInteractEntity(event);
if (wrapper.getAction() != InteractAction.ATTACK) return;
// Is not possible to attack while the inventory is open.
if (player.hasInventoryOpen) {
if (flagAndAlert()) {
// Cancel the packet
if (shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
}
closeInventory();
}
} else {
reward();
}
}
}
}
@@ -0,0 +1,33 @@
package ac.grim.grimac.checks.impl.inventory;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.InventoryCheck;
import ac.grim.grimac.player.GrimPlayer;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.protocol.player.DiggingAction;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerDigging;
@CheckData(name = "InventoryB", setback = 3, description = "Started digging blocks while inventory is open")
public class InventoryB extends InventoryCheck {
public InventoryB(GrimPlayer player) {
super(player);
}
public void handle(PacketReceiveEvent event, WrapperPlayClientPlayerDigging wrapper) {
if (wrapper.getAction() != DiggingAction.START_DIGGING) return;
// Is not possible to start digging a block while the inventory is open.
if (player.hasInventoryOpen) {
if (flagAndAlert()) {
// Cancel the packet
if (shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
}
closeInventory();
}
} else {
reward();
}
}
}
@@ -0,0 +1,28 @@
package ac.grim.grimac.checks.impl.inventory;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.InventoryCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.BlockPlace;
@CheckData(name = "InventoryC", setback = 3, description = "Placed a block while inventory is open")
public class InventoryC extends InventoryCheck {
public InventoryC(GrimPlayer player) {
super(player);
}
public void onBlockPlace(final BlockPlace place) {
// It is not possible to place a block while the inventory is open
if (player.hasInventoryOpen) {
if (flagAndAlert()) {
if (shouldModifyPackets()) {
place.resync();
}
closeInventory();
}
} else {
reward();
}
}
}
@@ -0,0 +1,84 @@
package ac.grim.grimac.checks.impl.inventory;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.InventoryCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
import ac.grim.grimac.utils.data.VectorData;
import ac.grim.grimac.utils.data.VectorData.MoveVectorData;
import ac.grim.grimac.utils.data.VehicleData;
import java.util.StringJoiner;
@CheckData(name = "InventoryD", setback = 1, decay = 0.25)
public class InventoryD extends InventoryCheck {
private int horseJumpVerbose;
public InventoryD(GrimPlayer player) {
super(player);
}
@Override
public void onPredictionComplete(final PredictionComplete predictionComplete) {
if (!predictionComplete.isChecked() ||
predictionComplete.getData().isTeleport() ||
player.getSetbackTeleportUtil().blockOffsets ||
player.packetStateData.lastPacketWasTeleport ||
player.packetStateData.isSlowedByUsingItem() ||
System.currentTimeMillis() - player.lastBlockPlaceUseItem < 50L) {
return;
}
if (player.hasInventoryOpen) {
boolean inVehicle = player.compensatedEntities.getSelf().inVehicle();
boolean isJumping, isMoving;
if (inVehicle) {
VehicleData vehicle = player.vehicleData;
// Will flag once if player open anything with pressed space bar
isJumping = vehicle.nextHorseJump > 0 && horseJumpVerbose++ >= 1;
isMoving = vehicle.nextVehicleForward != 0 || vehicle.nextVehicleHorizontal != 0;
} else {
MoveVectorData move = findMovement(player.predictedVelocity);
isJumping = player.predictedVelocity.isJump();
isMoving = move != null && (move.x != 0 || move.z != 0);
}
if (!isMoving && !isJumping) {
reward();
return;
}
if (flag()) {
closeInventory();
StringJoiner joiner = new StringJoiner(" ");
if (isMoving) joiner.add("moving");
if (isJumping) joiner.add("jumping");
if (inVehicle) joiner.add("inVehicle");
alert(joiner.toString());
}
} else {
horseJumpVerbose = 0;
}
}
private MoveVectorData findMovement(VectorData vectorData) {
if (vectorData instanceof MoveVectorData) {
return (MoveVectorData) vectorData;
}
while (vectorData != null) {
vectorData = vectorData.lastVector;
if (vectorData instanceof MoveVectorData) {
return (MoveVectorData) vectorData;
}
}
return null;
}
}
@@ -0,0 +1,48 @@
package ac.grim.grimac.checks.impl.inventory;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.InventoryCheck;
import ac.grim.grimac.player.GrimPlayer;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.event.PacketSendEvent;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
@CheckData(name = "InventoryE", setback = 3, description = "Sent a held item change packet while inventory is open")
public class InventoryE extends InventoryCheck {
private long lastTransaction = Long.MAX_VALUE; // Impossible transaction ID
public InventoryE(GrimPlayer player) {
super(player);
}
@Override
public void onPacketReceive(PacketReceiveEvent event) {
super.onPacketReceive(event);
if (event.getPacketType() == PacketType.Play.Client.HELD_ITEM_CHANGE) {
// It is not possible to change hotbar slots with held item change while the inventory is open
// A container click packet would be sent instead
if (player.hasInventoryOpen) {
if (this.lastTransaction < player.lastTransactionReceived.get()
&& flagAndAlert()) {
// Cancel the packet
if (shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
player.getInventory().needResend = true;
}
closeInventory();
}
} else {
reward();
}
}
}
@Override
public void onPacketSend(PacketSendEvent event) {
if (event.getPacketType() == PacketType.Play.Server.HELD_ITEM_CHANGE) {
this.lastTransaction = player.lastTransactionSent.get();
}
}
}
@@ -0,0 +1,44 @@
package ac.grim.grimac.checks.impl.inventory;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.InventoryCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.inventory.InventoryDesyncStatus;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
@CheckData(name = "InventoryF", setback = 3, description = "Sent a click window packet without a open inventory", experimental = true)
public class InventoryF extends InventoryCheck {
public InventoryF(GrimPlayer player) {
super(player);
}
@Override
public void onPacketReceive(PacketReceiveEvent event) {
// Exempt on 1.9+ server version due to the Via hack done in PacketPlayerWindow, the exemption can be deleted
// once we are ahead of ViaVersion
if (PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_9)
|| player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_9)) return;
super.onPacketReceive(event);
if (event.getPacketType() == PacketType.Play.Client.CLICK_WINDOW) {
if (!player.hasInventoryOpen && player.inventoryDesyncStatus == InventoryDesyncStatus.NOT_DESYNCED) {
if (flagAndAlert()) {
// Cancel the packet
if (shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
}
closeInventory();
}
} else {
reward();
}
}
}
}
@@ -0,0 +1,40 @@
package ac.grim.grimac.checks.impl.inventory;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.InventoryCheck;
import ac.grim.grimac.player.GrimPlayer;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientEntityAction;
@CheckData(name = "InventoryG", setback = 3, description = "Sent a entity action packet while inventory is open", experimental = true)
public class InventoryG extends InventoryCheck {
public InventoryG(GrimPlayer player) {
super(player);
}
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (player.packetStateData.lastPacketWasTeleport) return;
super.onPacketReceive(event);
if (event.getPacketType() == PacketType.Play.Client.ENTITY_ACTION) {
WrapperPlayClientEntityAction wrapper = new WrapperPlayClientEntityAction(event);
WrapperPlayClientEntityAction.Action action = wrapper.getAction();
if (action == WrapperPlayClientEntityAction.Action.STOP_SNEAKING
|| action == WrapperPlayClientEntityAction.Action.STOP_SPRINTING) {
return;
}
if (player.hasInventoryOpen) {
if (flagAndAlert()) {
closeInventory();
}
} else {
reward();
}
}
}
}
@@ -1,16 +0,0 @@
package ac.grim.grimac.checks.impl.misc;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PacketCheck;
import ac.grim.grimac.player.GrimPlayer;
@CheckData(name = "TransactionOrder", experimental = false)
public class TransactionOrder extends Check implements PacketCheck {
public TransactionOrder(GrimPlayer player) {
super(player);
}
}
@@ -0,0 +1,54 @@
package ac.grim.grimac.checks.impl.packetorder;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PostPredictionCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientClickWindow;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientClickWindow.WindowClickType;
@CheckData(name = "PacketOrderA", experimental = true)
public class PacketOrderA extends Check implements PostPredictionCheck {
public PacketOrderA(final GrimPlayer player) {
super(player);
}
private int invalid;
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (event.getPacketType() == PacketType.Play.Client.CLICK_WINDOW) {
final WindowClickType clickType = new WrapperPlayClientClickWindow(event).getWindowClickType();
if ((clickType == WindowClickType.PICKUP || clickType == WindowClickType.PICKUP_ALL) && player.packetOrderProcessor.isQuickMoveClicking()
|| clickType == WindowClickType.QUICK_MOVE && player.packetOrderProcessor.isPickUpClicking()) {
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) {
if (flagAndAlert() && shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
}
} else {
invalid++;
}
}
}
}
@Override
public void onPredictionComplete(PredictionComplete predictionComplete) {
// we don't need to check pre-1.9 players here (no tick skipping)
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) return;
if (player.isTickingReliablyFor(3)) {
for (; invalid >= 1; invalid--) {
flagAndAlert();
}
}
invalid = 0;
}
}
@@ -1,4 +1,4 @@
package ac.grim.grimac.checks.impl.badpackets;
package ac.grim.grimac.checks.impl.packetorder;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
@@ -11,15 +11,15 @@ import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientInteractEntity;
@CheckData(name = "BadPacketsH")
public class BadPacketsH extends Check implements PacketCheck {
@CheckData(name = "PacketOrderB")
public class PacketOrderB extends Check implements PacketCheck {
// 1.9 packet order: INTERACT -> ANIMATION
// 1.8 packet order: ANIMATION -> INTERACT
// I personally think 1.8 made much more sense. You swing and THEN you hit!
private boolean sentAnimation = player.getClientVersion().isNewerThan(ClientVersion.V_1_8);
public BadPacketsH(final GrimPlayer player) {
public PacketOrderB(final GrimPlayer player) {
super(player);
}
@@ -1,4 +1,4 @@
package ac.grim.grimac.checks.impl.badpackets;
package ac.grim.grimac.checks.impl.packetorder;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
@@ -9,25 +9,33 @@ import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.player.InteractionHand;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientInteractEntity;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerFlying;
@CheckData(name = "BadPacketsM", experimental = true)
public class BadPacketsM extends Check implements PacketCheck {
public BadPacketsM(final GrimPlayer player) {
@CheckData(name = "PacketOrderC", experimental = true)
public class PacketOrderC extends Check implements PacketCheck {
public PacketOrderC(final GrimPlayer player) {
super(player);
}
// 1.7 players do not send INTERACT_AT, so we cannot check them
private final boolean exempt = player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_7_10);
private boolean sentInteractAt = false;
private int requiredEntity;
private InteractionHand requiredHand;
private boolean requiredSneaking;
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (event.getPacketType() == PacketType.Play.Client.INTERACT_ENTITY && !exempt) {
if (exempt) {
return;
}
final WrapperPlayClientInteractEntity wrapper = new WrapperPlayClientInteractEntity(event);
if (event.getPacketType() == PacketType.Play.Client.INTERACT_ENTITY) {
final WrapperPlayClientInteractEntity packet = new WrapperPlayClientInteractEntity(event);
final PacketEntity entity = player.compensatedEntities.entityMap.get(wrapper.getEntityId());
final PacketEntity entity = player.compensatedEntities.entityMap.get(packet.getEntityId());
// For armor stands, vanilla clients send:
// - when renaming the armor stand or in spectator mode: INTERACT_AT + INTERACT
@@ -35,27 +43,49 @@ public class BadPacketsM extends Check implements PacketCheck {
// Just exempt armor stands to be safe
if (entity != null && entity.getType() == EntityTypes.ARMOR_STAND) return;
switch (wrapper.getAction()) {
final boolean sneaking = packet.isSneaking().orElse(false);
switch (packet.getAction()) {
// INTERACT_AT then INTERACT
case INTERACT:
if (!sentInteractAt) {
if (flagAndAlert("Missed Interact-At") && shouldModifyPackets()) {
if (flagAndAlert("Skipped Interact-At") && shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
}
} else if (packet.getEntityId() != requiredEntity || packet.getHand() != requiredHand || sneaking != requiredSneaking) {
String verbose = "requiredEntity=" + requiredEntity + ", entity=" + packet.getEntityId()
+ ", requiredHand=" + requiredHand + ", hand=" + packet.getHand()
+ ", requiredSneaking=" + requiredSneaking + ", sneaking=" + sneaking;
if (flagAndAlert(verbose) && shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
}
}
sentInteractAt = false;
break;
case INTERACT_AT:
if (sentInteractAt) {
if (flagAndAlert("Missed Interact") && shouldModifyPackets()) {
if (flagAndAlert("Skipped Interact") && shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
}
}
requiredHand = packet.getHand();
requiredEntity = packet.getEntityId();
requiredSneaking = sneaking;
sentInteractAt = true;
break;
}
}
if (WrapperPlayClientPlayerFlying.isFlying(event.getPacketType())) {
if (sentInteractAt) {
sentInteractAt = false;
flagAndAlert("Skipped Interact (Tick)");
}
}
}
}
@@ -0,0 +1,13 @@
package ac.grim.grimac.checks.impl.packetorder;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PacketCheck;
import ac.grim.grimac.player.GrimPlayer;
@CheckData(name = "PacketOrderD")
public class PacketOrderD extends Check implements PacketCheck {
public PacketOrderD(GrimPlayer player) {
super(player);
}
}
@@ -0,0 +1,66 @@
package ac.grim.grimac.checks.impl.packetorder;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PostPredictionCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
@CheckData(name = "PacketOrderE", experimental = true)
public class PacketOrderE extends Check implements PostPredictionCheck {
public PacketOrderE(final GrimPlayer player) {
super(player);
}
private int invalid;
private boolean setback;
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (event.getPacketType() == PacketType.Play.Client.HELD_ITEM_CHANGE) {
if (player.packetOrderProcessor.isAttacking()
|| player.packetOrderProcessor.isRightClicking()
|| player.packetOrderProcessor.isOpeningInventory()
|| player.packetOrderProcessor.isReleasing()
|| player.packetOrderProcessor.isSneaking()
|| player.packetOrderProcessor.isSprinting()
|| player.packetOrderProcessor.isLeavingBed()
|| player.packetOrderProcessor.isStartingToGlide()
|| player.packetOrderProcessor.isJumpingWithMount()
) {
if (player.getClientVersion().isNewerThan(ClientVersion.V_1_8) || flagAndAlert()) {
invalid++;
if (player.packetOrderProcessor.isUsing()) {
setback = true;
}
}
}
}
}
@Override
public void onPredictionComplete(PredictionComplete predictionComplete) {
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) {
if (setback) {
setback = false;
setbackIfAboveSetbackVL();
}
return;
}
if (player.isTickingReliablyFor(3)) {
for (; invalid >= 1; invalid--) {
if (flagAndAlert() && setback) {
setback = false;
setbackIfAboveSetbackVL();
}
}
}
invalid = 0;
}
}
@@ -0,0 +1,61 @@
package ac.grim.grimac.checks.impl.packetorder;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PostPredictionCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.player.DiggingAction;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientClientStatus;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerDigging;
@CheckData(name = "PacketOrderF", experimental = true)
public class PacketOrderF extends Check implements PostPredictionCheck {
public PacketOrderF(GrimPlayer player) {
super(player);
}
private int invalid;
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (event.getPacketType() == PacketType.Play.Client.INTERACT_ENTITY
|| event.getPacketType() == PacketType.Play.Client.PLAYER_BLOCK_PLACEMENT
|| event.getPacketType() == PacketType.Play.Client.USE_ITEM
|| event.getPacketType() == PacketType.Play.Client.PICK_ITEM
|| event.getPacketType() == PacketType.Play.Client.PLAYER_DIGGING
|| (event.getPacketType() == PacketType.Play.Client.CLIENT_STATUS
&& new WrapperPlayClientClientStatus(event).getAction() == WrapperPlayClientClientStatus.Action.OPEN_INVENTORY_ACHIEVEMENT)
) if (player.packetOrderProcessor.isSprinting() || player.packetOrderProcessor.isSneaking()) {
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) {
if (flagAndAlert() && shouldModifyPackets()) {
if (event.getPacketType() == PacketType.Play.Client.PLAYER_DIGGING
&& new WrapperPlayClientPlayerDigging(event).getAction() == DiggingAction.RELEASE_USE_ITEM
) return; // don't cause a noslow
event.setCancelled(true);
player.onPacketCancel();
}
} else {
invalid++;
}
}
}
@Override
public void onPredictionComplete(PredictionComplete predictionComplete) {
// we don't need to check pre-1.9 players here (no tick skipping)
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) return;
if (player.isTickingReliablyFor(3)) {
for (; invalid >= 1; invalid--) {
flagAndAlert();
}
}
invalid = 0;
}
}
@@ -0,0 +1,69 @@
package ac.grim.grimac.checks.impl.packetorder;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PostPredictionCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.player.DiggingAction;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientClientStatus;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerDigging;
@CheckData(name = "PacketOrderG", experimental = true)
public class PacketOrderG extends Check implements PostPredictionCheck {
public PacketOrderG(GrimPlayer player) {
super(player);
}
private int invalid;
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (event.getPacketType() == PacketType.Play.Client.PLAYER_DIGGING
|| (event.getPacketType() == PacketType.Play.Client.CLIENT_STATUS
&& new WrapperPlayClientClientStatus(event).getAction() == WrapperPlayClientClientStatus.Action.OPEN_INVENTORY_ACHIEVEMENT)
) {
if (event.getPacketType() == PacketType.Play.Client.PLAYER_DIGGING) {
final DiggingAction action = new WrapperPlayClientPlayerDigging(event).getAction();
if (action == DiggingAction.RELEASE_USE_ITEM
|| action == DiggingAction.START_DIGGING
|| action == DiggingAction.CANCELLED_DIGGING
|| action == DiggingAction.FINISHED_DIGGING
) return;
}
if (player.packetOrderProcessor.isAttacking()
|| player.packetOrderProcessor.isReleasing()
|| player.packetOrderProcessor.isRightClicking()
|| player.packetOrderProcessor.isPicking()
|| player.packetOrderProcessor.isDigging()
) {
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) {
if (flagAndAlert() && shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
}
} else {
invalid++;
}
}
}
}
@Override
public void onPredictionComplete(PredictionComplete predictionComplete) {
// we don't need to check pre-1.9 players here (no tick skipping)
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) return;
if (player.isTickingReliablyFor(3)) {
for (; invalid >= 1; invalid--) {
flagAndAlert();
}
}
invalid = 0;
}
}
@@ -0,0 +1,51 @@
package ac.grim.grimac.checks.impl.packetorder;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PostPredictionCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientEntityAction;
@CheckData(name = "PacketOrderH", experimental = true)
public class PacketOrderH extends Check implements PostPredictionCheck {
public PacketOrderH(final GrimPlayer player) {
super(player);
}
private int invalid;
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (event.getPacketType() == PacketType.Play.Client.ENTITY_ACTION) {
switch (new WrapperPlayClientEntityAction(event).getAction()) {
case START_SPRINTING:
case STOP_SPRINTING:
if (player.packetOrderProcessor.isSneaking()) {
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) {
flagAndAlert();
} else {
invalid++;
}
}
}
}
}
@Override
public void onPredictionComplete(PredictionComplete predictionComplete) {
// we don't need to check pre-1.9 players here (no tick skipping)
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) return;
if (player.isTickingReliablyFor(3)) {
for (; invalid >= 1; invalid--) {
flagAndAlert();
}
}
invalid = 0;
}
}
@@ -0,0 +1,130 @@
package ac.grim.grimac.checks.impl.packetorder;
import ac.grim.grimac.api.config.ConfigManager;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PostPredictionCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
import ac.grim.grimac.utils.nmsutil.BlockBreakSpeed;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.player.GameMode;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientInteractEntity;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerDigging;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerFlying;
import java.util.ArrayDeque;
@CheckData(name = "PacketOrderI", experimental = true)
public class PacketOrderI extends Check implements PostPredictionCheck {
public PacketOrderI(final GrimPlayer player) {
super(player);
}
private boolean exemptPlacingWhileDigging;
private boolean setback;
private boolean digging; // for placing
private final ArrayDeque<String> flags = new ArrayDeque<>();
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (event.getPacketType() == PacketType.Play.Client.INTERACT_ENTITY) {
if (new WrapperPlayClientInteractEntity(event).getAction() == WrapperPlayClientInteractEntity.InteractAction.ATTACK) {
if (player.packetOrderProcessor.isRightClicking() || player.packetOrderProcessor.isPicking() || player.packetOrderProcessor.isReleasing() || player.packetOrderProcessor.isDigging()) {
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) {
if (flagAndAlert("attack") && shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
}
} else {
flags.add("attack");
}
}
} else if (player.packetOrderProcessor.isReleasing() || player.packetOrderProcessor.isDigging()) {
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) {
if (flagAndAlert("interact") && shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
}
} else {
flags.add("interact");
}
}
}
if (event.getPacketType() == PacketType.Play.Client.PLAYER_BLOCK_PLACEMENT || event.getPacketType() == PacketType.Play.Client.USE_ITEM) {
if (player.packetOrderProcessor.isReleasing() || digging) {
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) {
if (flagAndAlert("place/use") && shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
}
} else {
flags.add("place");
}
}
}
if (event.getPacketType() == PacketType.Play.Client.PLAYER_DIGGING) {
WrapperPlayClientPlayerDigging packet = new WrapperPlayClientPlayerDigging(event);
switch (packet.getAction()) {
case RELEASE_USE_ITEM:
if (player.packetOrderProcessor.isAttacking() || player.packetOrderProcessor.isRightClicking() || player.packetOrderProcessor.isPicking() || player.packetOrderProcessor.isDigging()) {
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) {
if (flagAndAlert("release")) {
setback = true;
}
} else {
flags.add("release");
setback = true;
}
}
break;
case START_DIGGING:
double damage = BlockBreakSpeed.getBlockDamage(player, packet.getBlockPosition());
if (damage >= 1 || damage <= 0 && player.gamemode == GameMode.CREATIVE) {
return;
}
case CANCELLED_DIGGING:
case FINISHED_DIGGING:
if (exemptPlacingWhileDigging || player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_7_10)) {
return;
}
digging = true;
}
}
if (WrapperPlayClientPlayerFlying.isFlying(event.getPacketType()) && player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8) && !player.packetStateData.lastPacketWasTeleport) {
digging = false;
}
}
@Override
public void onPredictionComplete(PredictionComplete predictionComplete) {
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) {
if (setback) {
setbackIfAboveSetbackVL();
setback = false;
}
} else if (player.isTickingReliablyFor(3)) {
for (String verbose : flags) {
if (flagAndAlert(verbose) && setback) {
setbackIfAboveSetbackVL();
setback = false;
}
}
}
flags.clear();
setback = digging = false;
}
@Override
public void onReload(ConfigManager config) {
exemptPlacingWhileDigging = config.getBooleanElse(getConfigName() + ".exempt-placing-while-digging", false);
}
}
@@ -0,0 +1,49 @@
package ac.grim.grimac.checks.impl.packetorder;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PostPredictionCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
@CheckData(name = "PacketOrderJ", experimental = true)
public class PacketOrderJ extends Check implements PostPredictionCheck {
public PacketOrderJ(final GrimPlayer player) {
super(player);
}
private int invalid;
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (event.getPacketType() == PacketType.Play.Client.PLAYER_BLOCK_PLACEMENT || event.getPacketType() == PacketType.Play.Client.USE_ITEM) {
if (player.packetOrderProcessor.isAttacking() && !player.packetOrderProcessor.isInteracting()) {
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) {
if (flagAndAlert() && shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
}
} else {
invalid++;
}
}
}
}
@Override
public void onPredictionComplete(PredictionComplete predictionComplete) {
// we don't need to check pre-1.9 players here (no tick skipping)
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) return;
if (player.isTickingReliablyFor(3)) {
for (; invalid >= 1; invalid--) {
flagAndAlert();
}
}
invalid = 0;
}
}
@@ -0,0 +1,64 @@
package ac.grim.grimac.checks.impl.packetorder;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PostPredictionCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientClientStatus;
import java.util.ArrayDeque;
@CheckData(name = "PacketOrderK", experimental = true)
public class PacketOrderK extends Check implements PostPredictionCheck {
public PacketOrderK(final GrimPlayer player) {
super(player);
}
private final ArrayDeque<String> flags = new ArrayDeque<>();
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (event.getPacketType() == PacketType.Play.Client.CLIENT_STATUS) {
if (new WrapperPlayClientClientStatus(event).getAction() == WrapperPlayClientClientStatus.Action.OPEN_INVENTORY_ACHIEVEMENT) {
if (player.packetOrderProcessor.isClickingInInventory() || player.packetOrderProcessor.isClosingInventory()) {
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) {
flagAndAlert("open");
} else {
flags.add("open");
}
}
}
}
if (event.getPacketType() == PacketType.Play.Client.CLICK_WINDOW || event.getPacketType() == PacketType.Play.Client.CLOSE_WINDOW) {
if (player.packetOrderProcessor.isOpeningInventory()) {
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) {
if (flagAndAlert(event.getPacketType() == PacketType.Play.Client.CLICK_WINDOW ? "click" : "close") && shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
}
} else {
flags.add(event.getPacketType() == PacketType.Play.Client.CLICK_WINDOW ? "click" : "close");
}
}
}
}
@Override
public void onPredictionComplete(PredictionComplete predictionComplete) {
// we don't need to check pre-1.9 players here (no tick skipping)
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) return;
if (player.isTickingReliablyFor(3)) {
for (String verbose : flags) {
flagAndAlert(verbose);
}
}
flags.clear();
}
}
@@ -0,0 +1,64 @@
package ac.grim.grimac.checks.impl.packetorder;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PostPredictionCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.player.DiggingAction;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientClientStatus;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerDigging;
import java.util.ArrayDeque;
@CheckData(name = "PacketOrderL", experimental = true)
public class PacketOrderL extends Check implements PostPredictionCheck {
public PacketOrderL(final GrimPlayer player) {
super(player);
}
private final ArrayDeque<String> flags = new ArrayDeque<>();
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (event.getPacketType() == PacketType.Play.Client.CLIENT_STATUS) {
if (new WrapperPlayClientClientStatus(event).getAction() == WrapperPlayClientClientStatus.Action.OPEN_INVENTORY_ACHIEVEMENT) {
if (player.packetOrderProcessor.isDropping()) {
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) {
if (flagAndAlert("inventory") && shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
}
} else {
flags.add("inventory");
}
}
}
}
if (event.getPacketType() == PacketType.Play.Client.PLAYER_DIGGING) {
if (new WrapperPlayClientPlayerDigging(event).getAction() == DiggingAction.SWAP_ITEM_WITH_OFFHAND) {
if (player.packetOrderProcessor.isDropping()) {
flags.add("swap");
}
}
}
}
@Override
public void onPredictionComplete(PredictionComplete predictionComplete) {
// we don't need to check pre-1.9 players here (no tick skipping)
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) return;
if (player.isTickingReliablyFor(3)) {
for (String verbose : flags) {
flagAndAlert(verbose);
}
}
flags.clear();
}
}
@@ -0,0 +1,72 @@
package ac.grim.grimac.checks.impl.packetorder;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PostPredictionCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.world.BlockFace;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientInteractEntity;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerBlockPlacement;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerFlying;
@CheckData(name = "PacketOrderM", experimental = true)
public class PacketOrderM extends Check implements PostPredictionCheck {
public PacketOrderM(final GrimPlayer player) {
super(player);
}
private int invalid;
private boolean usingWithoutInteract, interacting;
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (event.getPacketType() == PacketType.Play.Client.INTERACT_ENTITY) {
if (new WrapperPlayClientInteractEntity(event).getAction() != WrapperPlayClientInteractEntity.InteractAction.ATTACK) {
interacting = true;
if (usingWithoutInteract) {
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) {
if (flagAndAlert() && shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
}
} else {
invalid++;
}
}
}
}
if (event.getPacketType() == PacketType.Play.Client.USE_ITEM
|| event.getPacketType() == PacketType.Play.Client.PLAYER_BLOCK_PLACEMENT
&& new WrapperPlayClientPlayerBlockPlacement(event).getFace() == BlockFace.OTHER) {
if (!interacting) {
usingWithoutInteract = true;
}
interacting = false;
}
if (WrapperPlayClientPlayerFlying.isFlying(event.getPacketType()) && player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8) && !player.packetStateData.lastPacketWasTeleport) {
usingWithoutInteract = interacting = false;
}
}
@Override
public void onPredictionComplete(PredictionComplete predictionComplete) {
// we don't need to check pre-1.9 players here (no tick skipping)
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) return;
if (player.isTickingReliablyFor(3)) {
for (; invalid >= 1; invalid--) {
flagAndAlert();
}
}
invalid = 0;
usingWithoutInteract = interacting = false;
}
}
@@ -0,0 +1,69 @@
package ac.grim.grimac.checks.impl.packetorder;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.BlockPlaceCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.BlockPlace;
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.world.BlockFace;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerBlockPlacement;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerFlying;
@CheckData(name = "PacketOrderN", experimental = true)
public class PacketOrderN extends BlockPlaceCheck {
public PacketOrderN(final GrimPlayer player) {
super(player);
}
private int invalid;
private boolean usingWithoutPlacing, placing;
@Override
public void onBlockPlace(BlockPlace place) {
placing = true;
if (usingWithoutPlacing) {
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) {
if (flagAndAlert() && shouldModifyPackets() && shouldCancel()) {
place.resync();
}
} else {
invalid++;
}
}
}
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (event.getPacketType() == PacketType.Play.Client.USE_ITEM
|| event.getPacketType() == PacketType.Play.Client.PLAYER_BLOCK_PLACEMENT
&& new WrapperPlayClientPlayerBlockPlacement(event).getFace() == BlockFace.OTHER) {
if (!placing) {
usingWithoutPlacing = true;
}
placing = false;
}
if (WrapperPlayClientPlayerFlying.isFlying(event.getPacketType()) && player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8) && !player.packetStateData.lastPacketWasTeleport) {
usingWithoutPlacing = placing = false;
}
}
@Override
public void onPredictionComplete(PredictionComplete predictionComplete) {
// we don't need to check pre-1.9 players here (no tick skipping)
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) return;
if (player.isTickingReliablyFor(3)) {
for (; invalid >= 1; invalid--) {
flagAndAlert();
}
}
invalid = 0;
usingWithoutPlacing = placing = false;
}
}
@@ -0,0 +1,60 @@
package ac.grim.grimac.checks.impl.packetorder;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.BlockPlaceCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.BlockPlace;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.player.InteractionHand;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerFlying;
@CheckData(name = "PacketOrderO", experimental = true)
public class PacketOrderO extends BlockPlaceCheck {
public PacketOrderO(final GrimPlayer player) {
super(player);
}
private boolean sentMainhand;
@Override
public void onBlockPlace(BlockPlace place) {
if (!isSupported()) {
return;
}
if (place.getHand() == InteractionHand.OFF_HAND) {
if (!sentMainhand) {
if (flagAndAlert("Skipped Mainhand") && shouldModifyPackets() && shouldCancel()) {
place.resync();
}
}
sentMainhand = false;
} else {
if (sentMainhand) {
if (flagAndAlert("Skipped Offhand") && shouldModifyPackets() && shouldCancel()) {
place.resync();
}
}
sentMainhand = !place.isBlock();
}
}
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (WrapperPlayClientPlayerFlying.isFlying(event.getPacketType()) && !player.packetStateData.lastPacketWasTeleport && !player.packetStateData.lastPacketWasOnePointSeventeenDuplicate) {
if (sentMainhand) {
sentMainhand = false;
flagAndAlert("Skipped Offhand (Tick)");
}
}
}
private boolean isSupported() {
return player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_13) && PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_9);
}
}
@@ -0,0 +1,63 @@
package ac.grim.grimac.checks.impl.packetorder;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PacketCheck;
import ac.grim.grimac.player.GrimPlayer;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.player.InteractionHand;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientInteractEntity;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientInteractEntity.InteractAction;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerFlying;
@CheckData(name = "PacketOrderP", experimental = true)
public class PacketOrderP extends Check implements PacketCheck {
public PacketOrderP(final GrimPlayer player) {
super(player);
}
private boolean sentMainhand;
private int requiredEntity;
private boolean requiredSneaking;
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (event.getPacketType() == PacketType.Play.Client.INTERACT_ENTITY && player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_9)) {
final WrapperPlayClientInteractEntity packet = new WrapperPlayClientInteractEntity(event);
InteractAction action = packet.getAction();
if (action != InteractAction.ATTACK) {
final boolean sneaking = packet.isSneaking().orElse(false);
final int entity = packet.getEntityId();
if (packet.getHand() == InteractionHand.OFF_HAND) {
if (action == InteractAction.INTERACT) {
if (!sentMainhand) {
if (flagAndAlert("Skipped Mainhand") && shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
}
}
sentMainhand = false;
} else if (sneaking != requiredSneaking || entity != requiredEntity) {
String verbose = "requiredEntity=" + requiredEntity + ", entity=" + entity
+ ", requiredSneaking=" + requiredSneaking + ", sneaking=" + sneaking;
if (flagAndAlert(verbose) && shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
}
}
} else {
requiredEntity = entity;
requiredSneaking = sneaking;
sentMainhand = true;
}
}
}
if (WrapperPlayClientPlayerFlying.isFlying(event.getPacketType()) && !player.packetStateData.lastPacketWasTeleport && !player.packetStateData.lastPacketWasOnePointSeventeenDuplicate) {
sentMainhand = false;
}
}
}
@@ -0,0 +1,178 @@
package ac.grim.grimac.checks.impl.packetorder;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.type.PostPredictionCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.protocol.packettype.PacketTypeCommon;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.world.BlockFace;
import com.github.retrooper.packetevents.wrapper.play.client.*;
import lombok.Getter;
import org.jetbrains.annotations.Contract;
@Getter
public final class PacketOrderProcessor extends Check implements PostPredictionCheck {
public PacketOrderProcessor(final GrimPlayer player) {
super(player);
}
private boolean openingInventory; // only pre 1.9 clients on pre 1.9 servers
private boolean swapping;
private boolean dropping;
private boolean interacting;
private boolean attacking;
private boolean releasing;
private boolean digging;
private boolean sprinting;
private boolean sneaking;
private boolean placing;
private boolean using;
private boolean picking;
private boolean clickingInInventory;
private boolean closingInventory;
private boolean quickMoveClicking;
private boolean pickUpClicking;
private boolean leavingBed;
private boolean startingToGlide;
private boolean jumpingWithMount;
@Override
public void onPacketReceive(PacketReceiveEvent event) {
final PacketTypeCommon packetType = event.getPacketType();
if (packetType == PacketType.Play.Client.CLIENT_STATUS) {
if (new WrapperPlayClientClientStatus(event).getAction() == WrapperPlayClientClientStatus.Action.OPEN_INVENTORY_ACHIEVEMENT) {
openingInventory = true;
}
}
if (packetType == PacketType.Play.Client.INTERACT_ENTITY) {
if (new WrapperPlayClientInteractEntity(event).getAction() == WrapperPlayClientInteractEntity.InteractAction.ATTACK) {
attacking = true;
} else {
interacting = true;
}
}
if (packetType == PacketType.Play.Client.PLAYER_DIGGING) {
switch (new WrapperPlayClientPlayerDigging(event).getAction()) {
case SWAP_ITEM_WITH_OFFHAND:
swapping = true;
break;
case DROP_ITEM:
case DROP_ITEM_STACK:
dropping = true;
break;
case RELEASE_USE_ITEM:
releasing = true;
break;
case FINISHED_DIGGING:
case CANCELLED_DIGGING:
case START_DIGGING:
digging = true;
break;
}
}
if (packetType == PacketType.Play.Client.ENTITY_ACTION) {
switch (new WrapperPlayClientEntityAction(event).getAction()) {
case START_SPRINTING:
case STOP_SPRINTING:
sprinting = true;
break;
case STOP_SNEAKING:
case START_SNEAKING:
sneaking = true;
break;
case LEAVE_BED:
leavingBed = true;
break;
case START_FLYING_WITH_ELYTRA:
startingToGlide = true;
break;
case OPEN_HORSE_INVENTORY:
openingInventory = true;
break;
case START_JUMPING_WITH_HORSE:
case STOP_JUMPING_WITH_HORSE:
jumpingWithMount = true;
break;
}
}
if (packetType == PacketType.Play.Client.USE_ITEM) {
using = true;
}
if (packetType == PacketType.Play.Client.PLAYER_BLOCK_PLACEMENT) {
if (new WrapperPlayClientPlayerBlockPlacement(event).getFace() == BlockFace.OTHER) {
using = true;
} else {
placing = true;
}
}
if (packetType == PacketType.Play.Client.PICK_ITEM) {
picking = true;
}
if (packetType == PacketType.Play.Client.CLICK_WINDOW) {
clickingInInventory = true;
switch (new WrapperPlayClientClickWindow(event).getWindowClickType()) {
case QUICK_MOVE:
quickMoveClicking = true;
break;
case PICKUP:
case PICKUP_ALL:
pickUpClicking = true;
break;
}
}
if (packetType == PacketType.Play.Client.CLOSE_WINDOW) {
closingInventory = true;
}
if (WrapperPlayClientPlayerFlying.isFlying(event.getPacketType()) && player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8) && !player.packetStateData.lastPacketWasTeleport) {
onTick();
}
}
@Override
public void onPredictionComplete(PredictionComplete predictionComplete) {
if (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_9)) {
onTick();
}
}
private void onTick() {
openingInventory = false;
swapping = false;
dropping = false;
attacking = false;
interacting = false;
releasing = false;
digging = false;
placing = false;
using = false;
picking = false;
sprinting = false;
sneaking = false;
clickingInInventory = false;
closingInventory = false;
quickMoveClicking = false;
pickUpClicking = false;
leavingBed = false;
startingToGlide = false;
jumpingWithMount = false;
}
@Contract(pure = true)
public boolean isRightClicking() {
return placing || using || interacting;
}
}
@@ -2,7 +2,6 @@ package ac.grim.grimac.checks.impl.post;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PacketCheck;
import ac.grim.grimac.checks.type.PostPredictionCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
@@ -25,7 +24,7 @@ import java.util.Locale;
import static com.github.retrooper.packetevents.protocol.packettype.PacketType.Play.Client.*;
@CheckData(name = "Post")
public class PostCheck extends Check implements PacketCheck, PostPredictionCheck {
public class Post extends Check implements PostPredictionCheck {
private final ArrayDeque<PacketTypeCommon> post = new ArrayDeque<>();
// Due to 1.9+ missing the idle packet, we must queue flags
// 1.8 clients will have the same logic for simplicity, although it's not needed
@@ -33,7 +32,7 @@ public class PostCheck extends Check implements PacketCheck, PostPredictionCheck
private boolean sentFlying = false;
private int isExemptFromSwingingCheck = Integer.MIN_VALUE;
public PostCheck(GrimPlayer playerData) {
public Post(GrimPlayer playerData) {
super(playerData);
}
@@ -9,7 +9,7 @@ import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
import ac.grim.grimac.utils.lists.EvictingQueue;
import org.bukkit.ChatColor;
import org.bukkit.entity.Player;
import org.bukkit.util.Vector;
import ac.grim.grimac.utils.vector.Vector3D;
import java.util.HashSet;
import java.util.Set;
@@ -45,8 +45,8 @@ public class DebugHandler extends Check implements PostPredictionCheck {
ChatColor color = pickColor(offset, offset);
Vector predicted = player.predictedVelocity.vector;
Vector actually = player.actualMovement;
Vector3D predicted = player.predictedVelocity.vector;
Vector3D actually = player.actualMovement;
ChatColor xColor = pickColor(Math.abs(predicted.getX() - actually.getX()), offset);
ChatColor yColor = pickColor(Math.abs(predicted.getY() - actually.getY()), offset);
@@ -9,7 +9,9 @@ import ac.grim.grimac.utils.math.VectorUtils;
import com.github.retrooper.packetevents.protocol.attribute.Attributes;
import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
import com.github.retrooper.packetevents.util.Vector3i;
import org.bukkit.util.Vector;
import ac.grim.grimac.utils.vector.Vector3D;
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
@CheckData(name = "FarPlace")
public class FarPlace extends BlockPlaceCheck {
@@ -27,8 +29,8 @@ public class FarPlace extends BlockPlaceCheck {
final double[] possibleEyeHeights = player.getPossibleEyeHeights();
for (double d : possibleEyeHeights) {
SimpleCollisionBox box = new SimpleCollisionBox(blockPos);
Vector eyes = new Vector(player.x, player.y + d, player.z);
Vector best = VectorUtils.cutBoxToVector(eyes, box);
Vector3D eyes = newVector3D(player.x, player.y + d, player.z);
Vector3D best = VectorUtils.cutBoxToVector(eyes, box);
min = Math.min(min, eyes.distanceSquared(best));
}
@@ -0,0 +1,224 @@
package ac.grim.grimac.checks.impl.scaffolding;
import ac.grim.grimac.api.config.ConfigManager;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.BlockPlaceCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.BlockPlace;
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
import ac.grim.grimac.utils.data.HitData;
import ac.grim.grimac.utils.nmsutil.BlockRayTrace;
import com.github.retrooper.packetevents.protocol.attribute.Attributes;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.player.GameMode;
import com.github.retrooper.packetevents.protocol.world.BlockFace;
import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState;
import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
import com.github.retrooper.packetevents.util.Vector3d;
import com.github.retrooper.packetevents.util.Vector3i;
import java.util.HashSet;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
@CheckData(name = "LineOfSightPlace")
public class LineOfSightPlace extends BlockPlaceCheck {
private double flagBuffer = 0; // If the player flags once, force them to play legit, or we will cancel the tick before.
private boolean ignorePost = false;
private boolean useBlockWhitelist;
private HashSet<StateType> blockWhitelist;
// 15 is the maximum set of Collision boxes that will be used in the ray trace check
// it corresponds to the size of the collision boxes from the modern Cauldron
// Since this is used per-player we can avoid calling new[] by allocating it per-player in the check
private final SimpleCollisionBox[] collisionBoxBuffer = new SimpleCollisionBox[15];
public final ConcurrentHashMap<Vector3i, WrappedBlockState> blocksChangedList = new ConcurrentHashMap<>();
public LineOfSightPlace(GrimPlayer player) {
super(player);
}
@Override
public void onBlockPlace(final BlockPlace place) {
if (checkIfShouldSkip(place)) return;
// if (flagBuffer > 0 && !didRayTraceHit(place)) {
// ignorePost = true;
// // If the player hit and has flagged this check recently
// if (flagAndAlert("pre-flying: " + player.compensatedWorld.getWrappedBlockStateAt(place.getPlacedAgainstBlockLocation()).getType()) && shouldModifyPackets() && shouldCancel()) {
// place.resync(); // Deny the block placement.
// }
// }
// Only flag (which will only happen in post) if we miss pre
if (didRayTraceHit(place)) {
ignorePost = true;
} else if (flagBuffer > 0) {
if (flagAndAlert("pre-flying: " + player.compensatedWorld.getWrappedBlockStateAt(place.getPlacedAgainstBlockLocation()).getType()) && shouldModifyPackets() && shouldCancel()) {
place.resync(); // Deny the block placement.
}
}
}
// Use post flying because it has the correct rotation, and can't false easily.
@Override
public void onPostFlyingBlockPlace(BlockPlace place) {
if (checkIfShouldSkip(place)) return;
// Don't flag twice
if (ignorePost) {
ignorePost = false;
return;
}
blocksChangedList.clear();
// Ray trace to try and hit the target block.
boolean hit = didRayTraceHit(place);
// This can false with rapidly moving yaw in 1.8+ clients
if (!hit) {
flagBuffer = 1;
flagAndAlert("post-flying: " + player.compensatedWorld.getWrappedBlockStateAt(place.getPlacedAgainstBlockLocation()).getType());
}
else {
flagBuffer = Math.max(0, flagBuffer - 0.1);
}
}
private boolean checkIfShouldSkip(BlockPlace place) {
StateType targetBlockStateType = player.compensatedWorld.getWrappedBlockStateAt(place.getPlacedAgainstBlockLocation()).getType();
if (player.gamemode == GameMode.SPECTATOR) return true; // A waste to check creative mode players
if (targetBlockStateType == StateTypes.REDSTONE_WIRE) return true; // Redstone too buggy
if (player.compensatedWorld.isNearHardEntity(player.boundingBox.copy().expand(4))) return true; // Shulkers and Pistons are too buggy
if (useBlockWhitelist) {
return !isBlockTypeWhitelisted(targetBlockStateType);
}
return false;
}
public void handleBlockChange(Vector3i vector3i, WrappedBlockState state) {
if (blocksChangedList.size() >= 60) return; // Don't let players freeze movement packets to grow this
// Only do this for nearby blocks
if (new Vector3d(vector3i.x, vector3i.y, vector3i.z).distanceSquared(new Vector3d(player.x, player.y, player.z)) > 36) return;
// Only do this if the state really had any world impact
// this line always just returns the current world state in compensatedWorld
// if (state.equals(player.compensatedWorld.getWrappedBlockStateAt(vector3i))) return;
blocksChangedList.put(vector3i, state);
}
private boolean didRayTraceHit(BlockPlace place) {
double[] possibleEyeHeights = player.getPossibleEyeHeights();
// Start checking if player is in the block
double minEyeHeight = Double.MAX_VALUE;
double maxEyeHeight = Double.MIN_VALUE;
for (double height : possibleEyeHeights) {
minEyeHeight = Math.min(minEyeHeight, height);
maxEyeHeight = Math.max(maxEyeHeight, height);
}
double movementThreshold = player.getMovementThreshold();
SimpleCollisionBox eyePositions = new SimpleCollisionBox(player.x, player.y + minEyeHeight, player.z, player.x, player.y + maxEyeHeight, player.z);
eyePositions.expand(movementThreshold);
int[] interactBlockVec = new int[]{place.blockPosition.x, place.blockPosition.y, place.blockPosition.z};
BlockFace expectedBlockFace = place.getDirection();
// If the player is inside a block, then they can ray trace through the block and hit the other side of the block
// This may potentially be exploitable as a minor bypass
if (eyePositions.isIntersected(new SimpleCollisionBox(interactBlockVec[0], interactBlockVec[1], interactBlockVec[2]))) {
return true;
}
// End checking if the player is in the block
double[][] possibleLookDirs;
// 1.9+ players could be a tick behind because we don't get skipped ticks
if (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_9)) {
possibleLookDirs = new double[][]{
{player.xRot, player.yRot},
{player.lastXRot, player.lastYRot},
{player.lastXRot, player.yRot}
};
} else if (player.getClientVersion().isOlderThan(ClientVersion.V_1_8)) {
// 1.7 players do not have any of these issues! They are always on the latest look vector
possibleLookDirs = new double[][]{{player.xRot, player.yRot}};
} else {
possibleLookDirs = new double[][]{
{player.xRot, player.yRot},
{player.lastXRot, player.yRot}
};
}
// We do not need to add 0.03/0.0002 to maxDistance to ensure our raytrace hits blocks
// Since we expand the hitboxes of the expectedTargetBlock by 0.03/0.002 already later
double maxDistance = player.compensatedEntities.getSelf().getAttributeValue(Attributes.PLAYER_BLOCK_INTERACTION_RANGE);
// Define possible offsets
// TODO, vectorize this with SIMD or AVX for performance
double[][] offsets = {
{0, 0, 0},
{movementThreshold, 0, 0},
{-movementThreshold, 0, 0},
{0, movementThreshold, 0},
{0, -movementThreshold, 0},
{0, 0, movementThreshold},
{0, 0, -movementThreshold}
};
double[] eyePosition = new double[3];
double[] eyeLookDir = new double[3];
for (double eyeHeight : possibleEyeHeights) {
for (double[] lookDir : possibleLookDirs) {
for (double[] offset : offsets) {
eyePosition[0] = player.x + offset[0];
eyePosition[1] = player.y + eyeHeight + offset[1];
eyePosition[2] = player.z + offset[2];
calculateDirection(eyeLookDir, lookDir[0], lookDir[1]);
if (didRayTraceHitTargetBlock(eyePosition, eyeLookDir, maxDistance, interactBlockVec, expectedBlockFace)) {
return true; // If any possible face matches the client-side placement, assume it's legitimate
}
}
}
}
return false; // No matching face found
}
// Helper method to calculate direction (replace the Ray class method)
private void calculateDirection(double[] result, double xRot, double yRot) {
float rotX = (float) Math.toRadians(xRot);
float rotY = (float) Math.toRadians(yRot);
result[1] = -player.trigHandler.sin(rotY);
double xz = player.trigHandler.cos(rotY);
result[0] = -xz * player.trigHandler.sin(rotX);
result[2] = xz * player.trigHandler.cos(rotX);
}
private boolean didRayTraceHitTargetBlock(double[] eyePos, double[] eyeDir, double maxDistance, int[] targetBlockVec, BlockFace expectedBlockFace) {
HitData hitData = BlockRayTrace.getNearestReachHitResult(player, eyePos, eyeDir, maxDistance, maxDistance, targetBlockVec, expectedBlockFace, collisionBoxBuffer, null, false);
if (hitData == null || !hitData.success)
hitData = BlockRayTrace.getNearestReachHitResult(player, eyePos, eyeDir, maxDistance, maxDistance, targetBlockVec,
expectedBlockFace, collisionBoxBuffer, player.checkManager.getBlockPlaceCheck(LineOfSightPlace.class).blocksChangedList, false);
// we check for hitdata != null because of being in expanded hitbox, or there was no result, do we still need this?
return hitData != null && hitData.success;
}
private boolean isBlockTypeWhitelisted(StateType type) {
return blockWhitelist.contains(type);
}
@Override
public void onReload(ConfigManager config) {
useBlockWhitelist = config.getBooleanElse("LineOfSightPlace.use-block-whitelist", false);
blockWhitelist = new HashSet<>();
List<String> blocks = config.getStringList("LineOfSightPlace.block-whitelist");
for (String block : blocks) {
blockWhitelist.add(StateTypes.getByName(block));
}
}
}
@@ -48,21 +48,27 @@ public class PositionPlace extends BlockPlaceCheck {
// So now we have the player's possible eye positions
// So then look at the face that the player has clicked
boolean flag = switch (place.getDirection()) {
case NORTH -> // Z- face
eyePositions.minZ > combined.minZ;
case SOUTH -> // Z+ face
eyePositions.maxZ < combined.maxZ;
case EAST -> // X+ face
eyePositions.maxX < combined.maxX;
case WEST -> // X- face
eyePositions.minX > combined.minX;
case UP -> // Y+ face
eyePositions.maxY < combined.maxY;
case DOWN -> // Y- face
eyePositions.minY > combined.minY;
default -> false;
};
boolean flag = false;
switch (place.getDirection()) {
case NORTH: // Z- face
flag = eyePositions.minZ > combined.minZ;
break;
case SOUTH: // Z+ face
flag = eyePositions.maxZ < combined.maxZ;
break;
case EAST: // X+ face
flag = eyePositions.maxX < combined.maxX;
break;
case WEST: // X- face
flag = eyePositions.minX > combined.minX;
break;
case UP: // Y+ face
flag = eyePositions.maxY < combined.maxY;
break;
case DOWN: // Y- face
flag = eyePositions.minY > combined.minY;
break;
}
if (flag) {
if (flagAndAlert() && shouldModifyPackets() && shouldCancel()) {
@@ -1,122 +1 @@
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.xRot, player.yRot, 0),
new Vector3f(player.lastXRot, player.yRot, 0)
));
final double[] possibleEyeHeights = player.getPossibleEyeHeights();
// Start checking if player is in the block
double minEyeHeight = Double.MAX_VALUE;
double maxEyeHeight = Double.MIN_VALUE;
for (double height : possibleEyeHeights) {
minEyeHeight = Math.min(minEyeHeight, height);
maxEyeHeight = Math.max(maxEyeHeight, height);
}
SimpleCollisionBox eyePositions = new SimpleCollisionBox(player.x, player.y + minEyeHeight, player.z, player.x, player.y + maxEyeHeight, player.z);
eyePositions.expand(player.getMovementThreshold());
// If the player is inside a block, then they can ray trace through the block and hit the other side of the block
if (eyePositions.isIntersected(box)) {
return true;
}
// End checking if the player is in the block
// 1.9+ players could be a tick behind because we don't get skipped ticks
if (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_9)) {
possibleLookDirs.add(new Vector3f(player.lastXRot, player.lastYRot, 0));
}
// 1.7 players do not have any of these issues! They are always on the latest look vector
if (player.getClientVersion().isOlderThan(ClientVersion.V_1_8)) {
possibleLookDirs = Collections.singletonList(new Vector3f(player.xRot, player.yRot, 0));
}
final double distance = player.compensatedEntities.getSelf().getAttributeValue(Attributes.PLAYER_BLOCK_INTERACTION_RANGE);
for (double d : possibleEyeHeights) {
for (Vector3f lookDir : possibleLookDirs) {
// x, y, z are correct for the block placement even after post tick because of code elsewhere
Vector3d starting = new Vector3d(player.x, player.y + d, player.z);
// xRot and yRot are a tick behind
Ray trace = new Ray(player, starting.getX(), starting.getY(), starting.getZ(), lookDir.getX(), lookDir.getY());
Pair<Vector, BlockFace> intercept = ReachUtils.calculateIntercept(box, trace.getOrigin(), trace.getPointAtDistance(distance));
if (intercept.first() != null) return true;
}
}
return false;
}
}
@@ -19,12 +19,14 @@ import com.github.retrooper.packetevents.util.Vector3f;
import com.github.retrooper.packetevents.util.Vector3i;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerExplosion;
import lombok.Getter;
import org.bukkit.util.Vector;
import ac.grim.grimac.utils.vector.Vector3D;
import org.jetbrains.annotations.Nullable;
import java.util.Deque;
import java.util.LinkedList;
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
@CheckData(name = "AntiExplosion", configName = "Explosion", setback = 10)
public class ExplosionHandler extends Check implements PostPredictionCheck {
Deque<VelocityData> firstBreadMap = new LinkedList<>();
@@ -121,7 +123,7 @@ public class ExplosionHandler extends Check implements PostPredictionCheck {
}
public void addPlayerExplosion(int breadOne, Vector3f explosion) {
firstBreadMap.add(new VelocityData(-1, breadOne, player.getSetbackTeleportUtil().isSendingSetback, new Vector(explosion.getX(), explosion.getY(), explosion.getZ())));
firstBreadMap.add(new VelocityData(-1, breadOne, player.getSetbackTeleportUtil().isSendingSetback, newVector3D(explosion.getX(), explosion.getY(), explosion.getZ())));
}
public void setPointThree(boolean isPointThree) {
@@ -15,12 +15,14 @@ import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.util.Vector3d;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerEntityVelocity;
import lombok.Getter;
import org.bukkit.util.Vector;
import ac.grim.grimac.utils.vector.Vector3D;
import org.jetbrains.annotations.NotNull;
import java.util.Deque;
import java.util.LinkedList;
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
// We are making a velocity sandwich between two pieces of transaction packets (bread)
@CheckData(name = "AntiKB", alternativeName = "AntiKnockback", configName = "Knockback", setback = 10, decay = 0.025)
public class KnockbackHandler extends Check implements PostPredictionCheck {
@@ -70,12 +72,12 @@ public class KnockbackHandler extends Check implements PostPredictionCheck {
// Wrap velocity between two transactions
player.sendTransaction();
addPlayerKnockback(entityId, player.lastTransactionSent.get(), new Vector(playerVelocity.getX(), playerVelocity.getY(), playerVelocity.getZ()));
addPlayerKnockback(entityId, player.lastTransactionSent.get(), newVector3D(playerVelocity.getX(), playerVelocity.getY(), playerVelocity.getZ()));
event.getTasksAfterSend().add(player::sendTransaction);
}
}
@NotNull public Pair<VelocityData, Vector> getFutureKnockback() {
@NotNull public Pair<VelocityData, Vector3D> getFutureKnockback() {
// Chronologically in the future
if (firstBreadMap.size() > 0) {
VelocityData data = firstBreadMap.peek();
@@ -97,7 +99,7 @@ public class KnockbackHandler extends Check implements PostPredictionCheck {
return new Pair<>(null, null);
}
private void addPlayerKnockback(int entityID, int breadOne, Vector knockback) {
private void addPlayerKnockback(int entityID, int breadOne, Vector3D knockback) {
firstBreadMap.add(new VelocityData(entityID, breadOne, player.getSetbackTeleportUtil().isSendingSetback, knockback));
}
@@ -6,6 +6,7 @@ import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.BlockPlace;
import ac.grim.grimac.utils.collisions.HitboxData;
import ac.grim.grimac.utils.collisions.datatypes.CollisionBox;
import ac.grim.grimac.utils.collisions.datatypes.ComplexCollisionBox;
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
import com.github.retrooper.packetevents.protocol.world.states.defaulttags.BlockTags;
import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
@@ -18,6 +19,7 @@ import java.util.List;
public class BlockPlaceCheck extends Check implements RotationCheck, PostPredictionCheck {
private static final List<StateType> weirdBoxes = new ArrayList<>();
private static final List<StateType> buggyBoxes = new ArrayList<>();
private final SimpleCollisionBox[] boxes = new SimpleCollisionBox[ComplexCollisionBox.DEFAULT_MAX_COLLISION_BOX_SIZE];
protected int cancelVL;
@@ -69,11 +71,11 @@ public class BlockPlaceCheck extends Check implements RotationCheck, PostPredict
Vector3i clicked = place.getPlacedAgainstBlockLocation();
CollisionBox placedOn = HitboxData.getBlockHitbox(player, place.getMaterial(), player.getClientVersion(), player.compensatedWorld.getWrappedBlockStateAt(clicked), clicked.getX(), clicked.getY(), clicked.getZ());
List<SimpleCollisionBox> boxes = new ArrayList<>();
placedOn.downCast(boxes);
int size = placedOn.downCast(boxes);
SimpleCollisionBox combined = new SimpleCollisionBox(clicked.getX(), clicked.getY(), clicked.getZ());
for (SimpleCollisionBox box : boxes) {
for (int i = 0; i < size; i++) {
SimpleCollisionBox box = boxes[i];
double minX = Math.max(box.minX, combined.minX);
double minY = Math.max(box.minY, combined.minY);
double minZ = Math.max(box.minZ, combined.minZ);
@@ -0,0 +1,67 @@
package ac.grim.grimac.checks.type;
import ac.grim.grimac.player.GrimPlayer;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientCloseWindow;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerCloseWindow;
import org.jetbrains.annotations.MustBeInvokedByOverriders;
public class InventoryCheck extends BlockPlaceCheck implements PacketCheck {
// Impossible transaction ID
protected static final long NONE = Long.MAX_VALUE;
protected long closeTransaction = NONE;
protected int closePacketsToSkip;
public InventoryCheck(GrimPlayer player) {
super(player);
}
@Override
@MustBeInvokedByOverriders
public void onPacketReceive(final PacketReceiveEvent event) {
if (event.getPacketType() == PacketType.Play.Client.CLICK_WINDOW) {
// Disallow any clicks if inventory is closing
if (closeTransaction != NONE && shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
player.getInventory().needResend = true;
}
} else if (event.getPacketType() == PacketType.Play.Client.CLOSE_WINDOW) {
// Players with high ping can close inventory faster than send transaction back
if (closeTransaction != NONE && closePacketsToSkip-- <= 0) {
closeTransaction = NONE;
}
}
}
public void closeInventory() {
if (closeTransaction != NONE) {
return;
}
int windowId = player.getInventory().openWindowID;
player.user.writePacket(new WrapperPlayServerCloseWindow(windowId));
// Force close inventory on server side
closePacketsToSkip = 1; // Sending close packet to itself, so skip it
PacketEvents.getAPI().getProtocolManager().receivePacket(
player.user.getChannel(), new WrapperPlayClientCloseWindow(windowId)
);
player.sendTransaction();
int transaction = player.lastTransactionSent.get();
closeTransaction = transaction;
player.latencyUtils.addRealTimeTask(transaction, () -> {
if (closeTransaction == transaction) {
closeTransaction = NONE;
}
});
player.user.flushPackets();
}
}
@@ -23,7 +23,8 @@ public class GrimSpectate extends BaseCommand {
@CommandPermission("grim.spectate")
@CommandCompletion("@players")
public void onSpectate(CommandSender sender, @Optional OnlinePlayer target) {
if (!(sender instanceof Player player)) return;
if (!(sender instanceof Player)) return;
Player player = (Player) sender;
if (target != null && target.getPlayer().getUniqueId().equals(player.getUniqueId())) {
String message = GrimAPI.INSTANCE.getConfigManager().getConfig().getStringElse("cannot-run-on-self", "%prefix% &cYou cannot use this command on yourself!");
@@ -17,7 +17,8 @@ public class GrimStopSpectating extends BaseCommand {
@CommandCompletion("here")
public void onStopSpectate(CommandSender sender, String[] args) {
String string = args.length > 0 ? args[0] : null;
if (!(sender instanceof Player player)) return;
if (!(sender instanceof Player)) return;
Player player = (Player) sender;
if (GrimAPI.INSTANCE.getSpectateManager().isSpectating(player.getUniqueId())) {
boolean teleportBack = string == null || !string.equalsIgnoreCase("here");
GrimAPI.INSTANCE.getSpectateManager().disable(player, teleportBack);
@@ -3,18 +3,15 @@ package ac.grim.grimac.events.packets;
import ac.grim.grimac.GrimAPI;
import ac.grim.grimac.checks.impl.badpackets.BadPacketsX;
import ac.grim.grimac.checks.impl.badpackets.BadPacketsZ;
import ac.grim.grimac.checks.impl.inventory.InventoryB;
import ac.grim.grimac.events.packets.patch.ResyncWorldUtil;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.*;
import ac.grim.grimac.utils.blockplace.BlockPlaceResult;
import ac.grim.grimac.utils.blockplace.ConsumesBlockPlace;
import ac.grim.grimac.utils.collisions.HitboxData;
import ac.grim.grimac.utils.collisions.datatypes.CollisionBox;
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
import ac.grim.grimac.utils.data.*;
import ac.grim.grimac.utils.inventory.Inventory;
import ac.grim.grimac.utils.latency.CompensatedWorld;
import ac.grim.grimac.utils.math.GrimMath;
import ac.grim.grimac.utils.math.VectorUtils;
import ac.grim.grimac.utils.nmsutil.*;
import com.github.retrooper.packetevents.PacketEvents;
@@ -24,7 +21,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;
@@ -47,11 +43,7 @@ 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;
import java.util.function.Function;
public class CheckManagerListener extends PacketListenerAbstract {
@@ -60,79 +52,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);
@@ -227,8 +148,9 @@ public class CheckManagerListener extends PacketListenerAbstract {
private static void handleBlockPlaceOrUseItem(PacketWrapper<?> packet, GrimPlayer player) {
// Legacy "use item" packet
if (packet instanceof WrapperPlayClientPlayerBlockPlacement place &&
if (packet instanceof WrapperPlayClientPlayerBlockPlacement &&
PacketEvents.getAPI().getServerManager().getVersion().isOlderThan(ServerVersion.V_1_9)) {
WrapperPlayClientPlayerBlockPlacement place = (WrapperPlayClientPlayerBlockPlacement) packet;
if (player.gamemode == GameMode.SPECTATOR || player.gamemode == GameMode.ADVENTURE) return;
@@ -243,7 +165,9 @@ public class CheckManagerListener extends PacketListenerAbstract {
}
}
if (packet instanceof WrapperPlayClientUseItem place) {
if (packet instanceof WrapperPlayClientUseItem) {
WrapperPlayClientUseItem place = (WrapperPlayClientUseItem) packet;
if (player.gamemode == GameMode.SPECTATOR || player.gamemode == GameMode.ADVENTURE) return;
ItemStack placedWith = player.getInventory().getHeldItem();
@@ -255,7 +179,9 @@ public class CheckManagerListener extends PacketListenerAbstract {
}
// Check for interactable first (door, etc)
if (packet instanceof WrapperPlayClientPlayerBlockPlacement place) {
if (packet instanceof WrapperPlayClientPlayerBlockPlacement) {
WrapperPlayClientPlayerBlockPlacement place = (WrapperPlayClientPlayerBlockPlacement) packet;
ItemStack placedWith = player.getInventory().getHeldItem();
ItemStack offhand = player.getInventory().getOffHand();
@@ -264,7 +190,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();
@@ -291,7 +217,8 @@ public class CheckManagerListener extends PacketListenerAbstract {
}
}
if (packet instanceof WrapperPlayClientPlayerBlockPlacement place) {
if (packet instanceof WrapperPlayClientPlayerBlockPlacement) {
WrapperPlayClientPlayerBlockPlacement place = (WrapperPlayClientPlayerBlockPlacement) packet;
Vector3i blockPosition = place.getBlockPosition();
BlockFace face = place.getFace();
@@ -303,7 +230,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);
@@ -460,6 +387,8 @@ public class CheckManagerListener extends PacketListenerAbstract {
WrappedBlockState block = player.compensatedWorld.getWrappedBlockStateAt(digPosition);
final StateType type = block.getType();
player.checkManager.getPacketCheck(InventoryB.class).handle(event, dig);
player.checkManager.getPacketCheck(BadPacketsX.class).handle(event, dig, type);
player.checkManager.getPacketCheck(BadPacketsZ.class).handle(event, dig);
@@ -510,7 +439,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)) {
@@ -584,7 +513,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);
@@ -750,7 +679,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
@@ -780,56 +709,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.first() == null) continue; // No intercept
Vector hitLoc = intercept.first();
if (hitLoc.distanceSquared(startingVec) < bestHitResult) {
bestHitResult = hitLoc.distanceSquared(startingVec);
bestHitLoc = hitLoc;
bestFace = intercept.second();
}
}
if (bestHitLoc != null) {
return new HitData(vector3i, bestHitLoc, bestFace, block);
}
if (sourcesHaveHitbox &&
(player.compensatedWorld.isWaterSourceBlock(vector3i.getX(), vector3i.getY(), vector3i.getZ())
|| player.compensatedWorld.getLavaFluidLevelAt(vector3i.getX(), vector3i.getY(), vector3i.getZ()) == (8 / 9f))) {
double waterHeight = player.compensatedWorld.getFluidLevelAt(vector3i.getX(), vector3i.getY(), vector3i.getZ());
SimpleCollisionBox box = new SimpleCollisionBox(vector3i.getX(), vector3i.getY(), vector3i.getZ(), vector3i.getX() + 1, vector3i.getY() + waterHeight, vector3i.getZ() + 1);
Pair<Vector, BlockFace> intercept = ReachUtils.calculateIntercept(box, trace.getOrigin(), trace.getPointAtDistance(distance));
if (intercept.first() != null) {
return new HitData(vector3i, intercept.first(), intercept.second(), block);
}
}
return null;
});
}
@Override
public void onPacketSend(PacketSendEvent event) {
if (event.getConnectionState() != ConnectionState.PLAY) return;
@@ -227,8 +227,9 @@ public class PacketEntityReplication extends Check implements PacketCheck {
if (status.getStatus() == 31) {
PacketEntity hook = player.compensatedEntities.getEntity(status.getEntityId());
if (!(hook instanceof PacketEntityHook hookEntity)) return;
if (!(hook instanceof PacketEntityHook)) return;
PacketEntityHook hookEntity = (PacketEntityHook) hook;
if (hookEntity.attached == player.entityID) {
player.sendTransaction();
// We don't transaction sandwich this, it's too rare to be a real problem.
@@ -453,7 +454,8 @@ public class PacketEntityReplication extends Check implements PacketCheck {
player.latencyUtils.addRealTimeTask(lastTrans, () -> {
PacketEntity entity = player.compensatedEntities.getEntity(entityId);
if (entity == null) return;
if (entity instanceof PacketEntityTrackXRot xRotEntity && yaw != null) {
if (entity instanceof PacketEntityTrackXRot && yaw != null) {
PacketEntityTrackXRot xRotEntity = (PacketEntityTrackXRot) entity;
xRotEntity.packetYaw = yaw;
xRotEntity.steps = entity.isBoat() ? 10 : 3;
}
@@ -19,11 +19,13 @@ import com.github.retrooper.packetevents.util.Vector3d;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerJoinGame;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerRespawn;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerUpdateHealth;
import org.bukkit.util.Vector;
import ac.grim.grimac.utils.vector.Vector3D;
import java.util.List;
import java.util.Objects;
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
/**
* PlayerRespawnS2CPacket info (1.20.2+):
* If the world is different (check via registry key), world is recreated (all entities etc destroyed).
@@ -185,7 +187,7 @@ public class PacketPlayerRespawn extends PacketListenerAbstract {
player.compensatedEntities.hasSprintingAttributeEnabled = false;
}
player.pose = Pose.STANDING;
player.clientVelocity = new Vector();
player.clientVelocity = newVector3D();
player.gamemode = respawn.getGameMode();
if (PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_17)) {
player.compensatedWorld.setDimension(respawn.getDimensionType(), event.getUser());
@@ -0,0 +1,165 @@
package ac.grim.grimac.events.packets;
import ac.grim.grimac.GrimAPI;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.data.packetentity.PacketEntitySelf;
import ac.grim.grimac.utils.inventory.InventoryDesyncStatus;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.event.PacketListenerAbstract;
import com.github.retrooper.packetevents.event.PacketListenerPriority;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.event.PacketSendEvent;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientClientStatus;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientClientStatus.Action;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerFlying;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerOpenWindow;
public class PacketPlayerWindow extends PacketListenerAbstract {
public PacketPlayerWindow() {
super(PacketListenerPriority.LOWEST);
}
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (WrapperPlayClientPlayerFlying.isFlying(event.getPacketType()) && !event.isCancelled()) {
GrimPlayer player = GrimAPI.INSTANCE.getPlayerDataManager().getPlayer(event.getUser());
if (player == null) return;
if (player.hasInventoryOpen && isNearNetherPortal(player)) {
handleInventoryClose(player, InventoryDesyncStatus.NETHER_PORTAL);
}
}
// Client Status is sent in 1.7-1.8
if (event.getPacketType() == PacketType.Play.Client.CLIENT_STATUS) {
WrapperPlayClientClientStatus wrapper = new WrapperPlayClientClientStatus(event);
if (wrapper.getAction() == Action.OPEN_INVENTORY_ACHIEVEMENT) {
GrimPlayer player = GrimAPI.INSTANCE.getPlayerDataManager().getPlayer(event.getUser());
if (player == null) return;
handleInventoryOpen(player);
}
}
// We need to do this due to 1.9 not sending anymore the inventory action in the Client Status
if (event.getPacketType() == PacketType.Play.Client.CLICK_WINDOW) {
GrimPlayer player = GrimAPI.INSTANCE.getPlayerDataManager().getPlayer(event.getUser());
if (player == null) return;
// TODO: Remove this check after we finish the before ViaVersion injection
// Explanation: On 1.7 and 1.8 we have OPEN_INVENTORY_ACHIEVEMENT on CLIENT_STATUS packet
// but after Via translation this information gets lost
// This is a workaround to atleast make our inventory checks work "decently" in 1.8 clients for 1.9+ servers
if (PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_9)
&& player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) {
handleInventoryOpen(player);
}
if (player.getClientVersion().isNewerThan(ClientVersion.V_1_8)) {
handleInventoryOpen(player);
}
}
if (event.getPacketType() == PacketType.Play.Client.CLOSE_WINDOW) {
GrimPlayer player = GrimAPI.INSTANCE.getPlayerDataManager().getPlayer(event.getUser());
if (player == null) return;
handleInventoryClose(player, InventoryDesyncStatus.NOT_DESYNCED);
}
}
@Override
public void onPacketSend(PacketSendEvent event) {
if (event.getPacketType() == PacketType.Play.Server.RESPAWN) {
GrimPlayer player = GrimAPI.INSTANCE.getPlayerDataManager().getPlayer(event.getUser());
if (player == null) return;
player.sendTransaction();
player.latencyUtils.addRealTimeTask(player.lastTransactionSent.get(),
() -> handleInventoryClose(player, InventoryDesyncStatus.NOT_DESYNCED));
} else if (event.getPacketType() == PacketType.Play.Server.OPEN_WINDOW) {
WrapperPlayServerOpenWindow wrapper = new WrapperPlayServerOpenWindow(event);
GrimPlayer player = GrimAPI.INSTANCE.getPlayerDataManager().getPlayer(event.getUser());
if (player == null) return;
player.sendTransaction();
String legacyType = wrapper.getLegacyType();
int modernType = wrapper.getType();
InventoryDesyncStatus inventoryDesyncStatus = getContainerDesyncStatus(player, legacyType, modernType);
player.latencyUtils.addRealTimeTask(player.lastTransactionSent.get(),
() -> {
if (inventoryDesyncStatus == InventoryDesyncStatus.NOT_DESYNCED) {
handleInventoryOpen(player);
} else {
handleInventoryClose(player, inventoryDesyncStatus);
}
});
} else if (event.getPacketType() == PacketType.Play.Server.OPEN_HORSE_WINDOW) {
GrimPlayer player = GrimAPI.INSTANCE.getPlayerDataManager().getPlayer(event.getUser());
if (player == null) return;
player.sendTransaction();
player.latencyUtils.addRealTimeTask(player.lastTransactionSent.get(),
() -> handleInventoryOpen(player));
} else if (event.getPacketType() == PacketType.Play.Server.CLOSE_WINDOW) {
GrimPlayer player = GrimAPI.INSTANCE.getPlayerDataManager().getPlayer(event.getUser());
if (player == null) return;
player.sendTransaction();
player.latencyUtils.addRealTimeTask(player.lastTransactionSent.get(),
() -> handleInventoryClose(player, InventoryDesyncStatus.NOT_DESYNCED));
}
}
private void handleInventoryOpen(GrimPlayer player) {
if (!player.hasInventoryOpen) {
player.lastInventoryOpen = System.currentTimeMillis();
}
player.hasInventoryOpen = true;
}
private void handleInventoryClose(GrimPlayer player, InventoryDesyncStatus desyncStatus) {
player.hasInventoryOpen = false;
player.inventoryDesyncStatus = desyncStatus;
}
public InventoryDesyncStatus getContainerDesyncStatus(GrimPlayer player, String legacyType, int modernType) {
// Closing beacon with the cross button cause desync in 1.7-1.8
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8) &&
("minecraft:beacon".equals(legacyType) || modernType == 8)) {
return player.inventoryDesyncStatus = InventoryDesyncStatus.BEACON;
}
if (isNearNetherPortal(player)) {
return player.inventoryDesyncStatus = InventoryDesyncStatus.NETHER_PORTAL;
}
return player.inventoryDesyncStatus = InventoryDesyncStatus.NOT_DESYNCED;
}
public boolean isNearNetherPortal(GrimPlayer player) {
// Going inside nether portal with opened inventory cause desync, fixed in 1.12.2
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_12_1) &&
player.pointThreeEstimator.isNearNetherPortal) {
PacketEntitySelf playerEntity = player.compensatedEntities.getSelf();
// Client ignore nether portal if player has passengers or riding an entity
if (!playerEntity.inVehicle() && playerEntity.passengers.isEmpty()) {
return true;
}
}
return false;
}
}
@@ -15,7 +15,7 @@ import java.util.BitSet;
public class PacketWorldReaderEight extends BasePacketWorldReader {
@Override
public void handleMapChunkBulk(final GrimPlayer player, final PacketSendEvent event) {
PacketWrapper<?> wrapper = new PacketWrapper<>(event);
PacketWrapper wrapper = new PacketWrapper(event);
ByteBuf buffer = (ByteBuf) wrapper.getBuffer();
boolean skylight = wrapper.readBoolean();
@@ -47,7 +47,7 @@ public class PacketWorldReaderEight extends BasePacketWorldReader {
@Override
public void handleMapChunk(final GrimPlayer player, final PacketSendEvent event) {
PacketWrapper<?> wrapper = new PacketWrapper<>(event);
PacketWrapper wrapper = new PacketWrapper(event);
final int chunkX = wrapper.readInt();
final int chunkZ = wrapper.readInt();
@@ -12,12 +12,13 @@ import ac.grim.grimac.checks.impl.crash.*;
import ac.grim.grimac.checks.impl.exploit.ExploitA;
import ac.grim.grimac.checks.impl.exploit.ExploitB;
import ac.grim.grimac.checks.impl.groundspoof.NoFallA;
import ac.grim.grimac.checks.impl.inventory.*;
import ac.grim.grimac.checks.impl.misc.ClientBrand;
import ac.grim.grimac.checks.impl.misc.FastBreak;
import ac.grim.grimac.checks.impl.misc.GhostBlockMitigation;
import ac.grim.grimac.checks.impl.misc.TransactionOrder;
import ac.grim.grimac.checks.impl.movement.*;
import ac.grim.grimac.checks.impl.post.PostCheck;
import ac.grim.grimac.checks.impl.packetorder.*;
import ac.grim.grimac.checks.impl.post.Post;
import ac.grim.grimac.checks.impl.prediction.DebugHandler;
import ac.grim.grimac.checks.impl.prediction.NoFallB;
import ac.grim.grimac.checks.impl.prediction.OffsetHandler;
@@ -85,12 +86,10 @@ public class CheckManager {
.put(BadPacketsE.class, new BadPacketsE(player))
.put(BadPacketsF.class, new BadPacketsF(player))
.put(BadPacketsG.class, new BadPacketsG(player))
.put(BadPacketsH.class, new BadPacketsH(player))
.put(BadPacketsI.class, new BadPacketsI(player))
.put(BadPacketsJ.class, new BadPacketsJ(player))
.put(BadPacketsK.class, new BadPacketsK(player))
.put(BadPacketsL.class, new BadPacketsL(player))
.put(BadPacketsM.class, new BadPacketsM(player))
.put(BadPacketsN.class, new BadPacketsN(player))
.put(BadPacketsP.class, new BadPacketsP(player))
.put(BadPacketsQ.class, new BadPacketsQ(player))
@@ -103,8 +102,16 @@ public class CheckManager {
.put(BadPacketsX.class, new BadPacketsX(player))
.put(BadPacketsY.class, new BadPacketsY(player))
.put(BadPacketsZ.class, new BadPacketsZ(player))
.put(InventoryA.class, new InventoryA(player))
.put(InventoryB.class, new InventoryB(player))
.put(InventoryE.class, new InventoryE(player))
.put(InventoryF.class, new InventoryF(player))
.put(InventoryG.class, new InventoryG(player))
.put(FastBreak.class, new FastBreak(player))
.put(TransactionOrder.class, new TransactionOrder(player))
.put(PacketOrderB.class, new PacketOrderB(player))
.put(PacketOrderC.class, new PacketOrderC(player))
.put(PacketOrderD.class, new PacketOrderD(player))
.put(PacketOrderP.class, new PacketOrderP(player))
.put(NoSlowB.class, new NoSlowB(player))
.put(SetbackBlocker.class, new SetbackBlocker(player)) // Must be last class otherwise we can't check while blocking packets
.build();
@@ -127,8 +134,20 @@ public class CheckManager {
.put(ExplosionHandler.class, new ExplosionHandler(player))
.put(KnockbackHandler.class, new KnockbackHandler(player))
.put(GhostBlockDetector.class, new GhostBlockDetector(player))
.put(InventoryD.class, new InventoryD(player))
.put(Phase.class, new Phase(player))
.put(PostCheck.class, new PostCheck(player))
.put(PacketOrderProcessor.class, player.packetOrderProcessor)
.put(Post.class, new Post(player))
.put(PacketOrderA.class, new PacketOrderA(player))
.put(PacketOrderE.class, new PacketOrderE(player))
.put(PacketOrderF.class, new PacketOrderF(player))
.put(PacketOrderG.class, new PacketOrderG(player))
.put(PacketOrderH.class, new PacketOrderH(player))
.put(PacketOrderI.class, new PacketOrderI(player))
.put(PacketOrderJ.class, new PacketOrderJ(player))
.put(PacketOrderK.class, new PacketOrderK(player))
.put(PacketOrderL.class, new PacketOrderL(player))
.put(PacketOrderM.class, new PacketOrderM(player))
.put(NoFallB.class, new NoFallB(player))
.put(OffsetHandler.class, new OffsetHandler(player))
.put(SuperDebug.class, new SuperDebug(player))
@@ -147,6 +166,7 @@ public class CheckManager {
.build();
blockPlaceCheck = new ImmutableClassToInstanceMap.Builder<BlockPlaceCheck>()
.put(InventoryC.class, new InventoryC(player))
.put(InvalidPlaceA.class, new InvalidPlaceA(player))
.put(InvalidPlaceB.class, new InvalidPlaceB(player))
.put(AirLiquidPlace.class, new AirLiquidPlace(player))
@@ -154,8 +174,10 @@ public class CheckManager {
.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(PacketOrderN.class, new PacketOrderN(player))
.put(PacketOrderO.class, new PacketOrderO(player))
.put(DuplicateRotPlace.class, new DuplicateRotPlace(player))
.put(LineOfSightPlace.class, new LineOfSightPlace(player))
.put(GhostBlockMitigation.class, new GhostBlockMitigation(player))
.build();
@@ -286,11 +308,17 @@ public class CheckManager {
return (T) packetChecks.get(check);
}
@SuppressWarnings("unchecked")
public <T extends BlockPlaceCheck> T getBlockPlaceCheck(Class<T> check) {
return (T) blockPlaceCheck.get(check);
}
@SuppressWarnings("unchecked")
public <T extends PacketCheck> T getPrePredictionCheck(Class<T> check) {
return (T) prePredictionChecks.get(check);
}
private PacketEntityReplication packetEntityReplication = null;
public PacketEntityReplication getEntityReplication() {
@@ -19,6 +19,7 @@ import ac.grim.grimac.utils.math.VectorUtils;
import ac.grim.grimac.utils.nmsutil.Collisions;
import ac.grim.grimac.utils.nmsutil.GetBoundingBox;
import ac.grim.grimac.utils.nmsutil.ReachUtils;
import ac.grim.grimac.utils.vector.Vector3D;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.player.GameMode;
@@ -31,13 +32,15 @@ import lombok.Getter;
import lombok.Setter;
import org.bukkit.Location;
import org.bukkit.entity.Entity;
import org.bukkit.util.Vector;
import ac.grim.grimac.utils.vector.Vector3D;
import java.util.Collections;
import java.util.HashSet;
import java.util.Random;
import java.util.concurrent.ConcurrentLinkedQueue;
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
public class SetbackTeleportUtil extends Check implements PostPredictionCheck {
// Sync to netty
public final ConcurrentLinkedQueue<TeleportData> pendingTeleports = new ConcurrentLinkedQueue<>();
@@ -65,7 +68,7 @@ public class SetbackTeleportUtil extends Check implements PostPredictionCheck {
@Override
public void onPredictionComplete(final PredictionComplete predictionComplete) {
// Grab friction now when we know player on ground and other variables
Vector afterTickFriction = player.clientVelocity.clone();
Vector3D afterTickFriction = player.clientVelocity.clone();
// We must first check if the player has accepted their setback
// If the setback isn't complete, then this position is illegitimate
@@ -115,7 +118,7 @@ public class SetbackTeleportUtil extends Check implements PostPredictionCheck {
return false;
}
private void simulateFriction(Vector vector) {
private void simulateFriction(Vector3D vector) {
// We must always do this before simulating positions, as this is the last actual (safe) movement
// We must not do this for knockback or explosions, as they are at the start of the tick
if (player.wasTouchingWater) {
@@ -123,9 +126,9 @@ public class SetbackTeleportUtil extends Check implements PostPredictionCheck {
} else if (player.wasTouchingLava) {
vector.multiply(0.5D);
if (player.hasGravity)
vector.add(new Vector(0.0D, -player.gravity / 4.0D, 0.0D));
vector.add(newVector3D(0.0D, -player.gravity / 4.0D, 0.0D));
} else if (player.isGliding) {
PredictionEngineElytra.getElytraMovement(player, vector, ReachUtils.getLook(player, player.xRot, player.yRot)).multiply(player.stuckSpeedMultiplier).multiply(new Vector(0.99F, 0.98F, 0.99F));
PredictionEngineElytra.getElytraMovement(player, vector, ReachUtils.getLook(player, player.xRot, player.yRot)).multiply(player.stuckSpeedMultiplier).multiply(newVector3D(0.99F, 0.98F, 0.99F));
vector.setY(vector.getY() - 0.05); // Make the player fall a bit
} else { // Gliding doesn't have friction, we handle it differently
PredictionEngineNormal.staticVectorEndOfTick(player, vector); // Lava and normal movement
@@ -147,18 +150,18 @@ public class SetbackTeleportUtil extends Check implements PostPredictionCheck {
lastWorldResync = System.currentTimeMillis();
}
Vector clientVel = lastKnownGoodPosition.vector.clone();
Vector3D clientVel = lastKnownGoodPosition.vector.clone();
Pair<VelocityData, Vector> futureKb = player.checkManager.getKnockbackHandler().getFutureKnockback();
Pair<VelocityData, Vector3D> futureKb = player.checkManager.getKnockbackHandler().getFutureKnockback();
VelocityData futureExplosion = player.checkManager.getExplosionHandler().getFutureExplosion();
// Velocity sets
if (futureKb.first() != null) {
clientVel = futureKb.second();
if (futureKb.getFirst() != null) {
clientVel = futureKb.getSecond();
}
// Explosion adds
if (futureExplosion != null && (futureKb.first() == null || futureKb.first().transaction < futureExplosion.transaction)) {
if (futureExplosion != null && (futureKb.getFirst() == null || futureKb.getFirst().transaction < futureExplosion.transaction)) {
clientVel.add(futureExplosion.vector);
}
@@ -169,7 +172,7 @@ public class SetbackTeleportUtil extends Check implements PostPredictionCheck {
// Mini prediction engine - simulate collisions
if (simulateNextTickPosition) {
Vector collide = Collisions.collide(player, clientVel.getX(), clientVel.getY(), clientVel.getZ());
Vector3D collide = Collisions.collide(player, clientVel.getX(), clientVel.getY(), clientVel.getZ());
position = position.withX(position.getX() + collide.getX());
// 1.8 players need the collision epsilon to not phase into blocks when being setback
@@ -328,16 +331,16 @@ public class SetbackTeleportUtil extends Check implements PostPredictionCheck {
while (true) {
Pair<Integer, Vector3d> teleportPos = player.vehicleData.vehicleTeleports.peek();
if (teleportPos == null) break;
if (lastTransaction < teleportPos.first()) {
if (lastTransaction < teleportPos.getFirst()) {
break;
}
Vector3d position = teleportPos.second();
Vector3d position = teleportPos.getSecond();
if (position.getX() == x && position.getY() == y && position.getZ() == z) {
player.vehicleData.vehicleTeleports.poll();
return true;
} else if (lastTransaction > teleportPos.first() + 1) {
} else if (lastTransaction > teleportPos.getFirst() + 1) {
player.vehicleData.vehicleTeleports.poll();
// Vehicles have terrible netcode so just ignore it if the teleport wasn't from us setting the player back
@@ -412,7 +415,7 @@ public class SetbackTeleportUtil extends Check implements PostPredictionCheck {
data = new TeleportData(safePosition, flags, transaction, teleportId);
requiredSetBack = new SetBackData(data, player.xRot, player.yRot, null, false, plugin);
this.lastKnownGoodPosition = new SetbackPosWithVector(safePosition, new Vector());
this.lastKnownGoodPosition = new SetbackPosWithVector(safePosition, newVector3D());
}
@AllArgsConstructor
@@ -420,6 +423,6 @@ public class SetbackTeleportUtil extends Check implements PostPredictionCheck {
@Setter
public static class SetbackPosWithVector {
private Vector3d pos;
private Vector vector;
private Vector3D vector;
}
}
@@ -71,8 +71,14 @@ public class SpectateManager implements Initable {
spectatingPlayers.remove(uuid);
}
private record PreviousState(org.bukkit.GameMode gameMode, Location location) {
private static class PreviousState {
public PreviousState(org.bukkit.GameMode gameMode, Location location) {
this.gameMode = gameMode;
this.location = location;
}
private final org.bukkit.GameMode gameMode;
private final Location location;
}
}
@@ -4,6 +4,7 @@ import ac.grim.grimac.manager.tick.Tickable;
import ac.grim.grimac.manager.tick.impl.ClientVersionSetter;
import ac.grim.grimac.manager.tick.impl.ResetTick;
import ac.grim.grimac.manager.tick.impl.TickInventory;
import ac.grim.grimac.manager.tick.impl.UpdateChangedBlocksList;
import com.google.common.collect.ClassToInstanceMap;
import com.google.common.collect.ImmutableClassToInstanceMap;
@@ -22,6 +23,7 @@ public class TickManager {
asyncTick = new ImmutableClassToInstanceMap.Builder<Tickable>()
.put(ClientVersionSetter.class, new ClientVersionSetter()) // Async because permission lookups might take a while, depending on the plugin
.put(TickInventory.class, new TickInventory()) // Async because I've never gotten an exception from this. It's probably safe.
.put(UpdateChangedBlocksList.class, new UpdateChangedBlocksList())
.build();
}
@@ -32,6 +32,7 @@ public class ConfigManagerFileImpl implements ConfigManager, BasicReloadable {
GrimAPI.INSTANCE.getPlugin().getDataFolder().mkdirs();
if (!initialized) {
initialized = true;
upgrade();
config.addSource(GrimAC.class, "config", getConfigFile("config.yml"));
config.addSource(GrimAC.class, "messages", getConfigFile("messages.yml"));
config.addSource(GrimAC.class, "discord", getConfigFile("discord.yml"));
@@ -81,7 +82,7 @@ public class ConfigManagerFileImpl implements ConfigManager, BasicReloadable {
configVersion = Integer.parseInt(configStringVersion);
// TODO: Do we have to hardcode this?
configString = configString.replaceAll("config-version: " + configStringVersion, "config-version: 9");
configString = configString.replaceAll("config-version: " + configStringVersion, "config-version: 10");
Files.write(config.toPath(), configString.getBytes());
upgradeModernConfig(config, configString, configVersion);
@@ -123,6 +124,9 @@ public class ConfigManagerFileImpl implements ConfigManager, BasicReloadable {
if (configVersion < 9) {
newOffsetHandlingAntiKB(config, configString);
}
if (configVersion < 10) {
addInventoryPunishments();
}
}
private void removeLegacyTwoPointOne(File config) throws IOException {
@@ -173,6 +177,28 @@ public class ConfigManagerFileImpl implements ConfigManager, BasicReloadable {
}
}
private void addInventoryPunishments() {
File config = new File(GrimAPI.INSTANCE.getPlugin().getDataFolder(), "punishments.yml");
String configString;
if (config.exists()) {
try {
configString = new String(Files.readAllBytes(config.toPath()));
// If it works, it isn't stupid. Only replace it if it exactly matches the default config.
String reachSection = " Reach:";
String inventorySection = " Inventory:\n remove-violations-after: 300\n checks:\n - \"Inventory\"\n commands:\n - \"10:10 [alert]\"\n - \"20:20 [webhook]\"\n - \"20:20 [proxy]\"\n";
int index = configString.indexOf(reachSection);
if (index != -1) {
configString = configString.substring(0, index) + inventorySection + configString.substring(index);
}
Files.write(config.toPath(), configString.getBytes());
} catch (IOException ignored) {
}
}
}
private void fixBadPacketsAndAdjustPingConfig(File config, String configString) {
try {
configString = configString.replaceAll("max-ping: \\d+", "max-transaction-time: 60");
@@ -8,7 +8,7 @@ public class BStats implements Initable {
public void start() {
int pluginId = 12820; // <-- Replace with the id of your plugin!
try {
new io.github.retrooper.packetevents.bstats.Metrics(GrimAPI.INSTANCE.getPlugin(), pluginId);
new io.github.retrooper.packetevents.bstats.bukkit.Metrics(GrimAPI.INSTANCE.getPlugin(), pluginId);
} catch (Exception ignored) {
}
}
@@ -19,6 +19,7 @@ public class PacketManager implements Initable {
PacketEvents.getAPI().getEventManager().registerListener(new PacketConfigurationListener());
PacketEvents.getAPI().getEventManager().registerListener(new PacketPlayerJoinQuit());
PacketEvents.getAPI().getEventManager().registerListener(new PacketPingListener());
PacketEvents.getAPI().getEventManager().registerListener(new PacketPlayerWindow());
PacketEvents.getAPI().getEventManager().registerListener(new PacketPlayerDigging());
PacketEvents.getAPI().getEventManager().registerListener(new PacketPlayerAttack());
PacketEvents.getAPI().getEventManager().registerListener(new PacketEntityAction());
@@ -10,26 +10,29 @@ import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
import ac.grim.grimac.utils.data.VectorData;
import ac.grim.grimac.utils.lists.EvictingQueue;
import ac.grim.grimac.utils.math.GrimMath;
import ac.grim.grimac.utils.vector.Vector3D;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState;
import com.github.retrooper.packetevents.util.Vector3d;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import lombok.AllArgsConstructor;
import org.bukkit.util.Vector;
import java.util.*;
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
public final class SuperDebug extends Check implements PostPredictionCheck {
private static final StringBuilder[] flags = new StringBuilder[256]; // 17 MB of logs in memory
Object2IntMap<StringBuilder> continuedDebug = new Object2IntOpenHashMap<>();
List<VectorData> predicted = new EvictingQueue<>(60);
List<Vector> actually = new EvictingQueue<>(60);
List<Vector3D> actually = new EvictingQueue<>(60);
List<Location> locations = new EvictingQueue<>(60);
List<Vector> startTickClientVel = new EvictingQueue<>(60);
List<Vector> baseTickAddition = new EvictingQueue<>(60);
List<Vector> baseTickWater = new EvictingQueue<>(60);
List<Vector3D> startTickClientVel = new EvictingQueue<>(60);
List<Vector3D> baseTickAddition = new EvictingQueue<>(60);
List<Vector3D> baseTickWater = new EvictingQueue<>(60);
public SuperDebug(GrimPlayer player) {
super(player);
@@ -80,11 +83,11 @@ public final class SuperDebug extends Check implements PostPredictionCheck {
for (int i = 0; i < predicted.size(); i++) {
VectorData predict = predicted.get(i);
Vector actual = actually.get(i);
Vector3D actual = actually.get(i);
Location loc = locations.get(i);
Vector startTickVel = startTickClientVel.get(i);
Vector addition = baseTickAddition.get(i);
Vector water = baseTickWater.get(i);
Vector3D startTickVel = startTickClientVel.get(i);
Vector3D addition = baseTickAddition.get(i);
Vector3D water = baseTickWater.get(i);
appendDebug(sb, predict, actual, loc, startTickVel, addition, water);
}
@@ -189,7 +192,7 @@ public final class SuperDebug extends Check implements PostPredictionCheck {
continuedDebug.put(sb, 40);
}
private void appendDebug(StringBuilder sb, VectorData predict, Vector actual, Location location, Vector startTick, Vector addition, Vector water) {
private void appendDebug(StringBuilder sb, VectorData predict, Vector3D actual, Location location, Vector3D startTick, Vector3D addition, Vector3D water) {
if (predict.isZeroPointZeroThree()) {
sb.append("Movement threshold/tick skipping\n");
}
@@ -225,11 +228,11 @@ public final class SuperDebug extends Check implements PostPredictionCheck {
// Apply 0.003/0.005 to make numbers more accurate
Set<VectorData> set = new HashSet<>(Collections.singletonList(new VectorData(startTick.clone(), VectorData.VectorType.BestVelPicked)));
new PredictionEngine().applyMovementThreshold(player, set);
Vector trueStartVel = ((VectorData) set.toArray()[0]).vector;
Vector3D trueStartVel = ((VectorData) set.toArray()[0]).vector;
Vector clientMovement = getPlayerMathMovement(player, actual.clone().subtract(trueStartVel), location.xRot);
Vector simulatedMovement = getPlayerMathMovement(player, predict.vector.clone().subtract(trueStartVel), location.xRot);
Vector offset = actual.clone().subtract(predict.vector);
Vector3D clientMovement = getPlayerMathMovement(player, actual.clone().subtract(trueStartVel), location.xRot);
Vector3D simulatedMovement = getPlayerMathMovement(player, predict.vector.clone().subtract(trueStartVel), location.xRot);
Vector3D offset = actual.clone().subtract(predict.vector);
trueStartVel.add(addition);
trueStartVel.add(water);
@@ -268,14 +271,14 @@ public final class SuperDebug extends Check implements PostPredictionCheck {
sb.append("\n\n");
}
private Vector getPlayerMathMovement(GrimPlayer player, Vector wantedMovement, float f2) {
private Vector3D getPlayerMathMovement(GrimPlayer player, Vector3D wantedMovement, float f2) {
float f3 = player.trigHandler.sin(f2 * 0.017453292f);
float f4 = player.trigHandler.cos(f2 * 0.017453292f);
float bestTheoreticalX = (float) (f3 * wantedMovement.getZ() + f4 * wantedMovement.getX()) / (f3 * f3 + f4 * f4);
float bestTheoreticalZ = (float) (-f3 * wantedMovement.getX() + f4 * wantedMovement.getZ()) / (f3 * f3 + f4 * f4);
return new Vector(bestTheoreticalX, 0, bestTheoreticalZ);
return newVector3D(bestTheoreticalX, 0, bestTheoreticalZ);
}
@AllArgsConstructor
@@ -42,7 +42,7 @@ public class TickEndEvent implements Initable {
// but whatever. At least plugins can't break it, I guess.
//
// Pledge injects into another list, so we should be safe injecting into this one
List<?> wrapper = Collections.synchronizedList(new HookedListWrapper<>(endOfTickObject) {
List<?> wrapper = Collections.synchronizedList(new HookedListWrapper<Object>(endOfTickObject) {
@Override
public void onIterator() {
hasTicked = true;
@@ -0,0 +1,15 @@
package ac.grim.grimac.manager.tick.impl;
import ac.grim.grimac.GrimAPI;
import ac.grim.grimac.manager.tick.Tickable;
import ac.grim.grimac.player.GrimPlayer;
public class UpdateChangedBlocksList implements Tickable {
@Override
public void tick() {
for (GrimPlayer player : GrimAPI.INSTANCE.getPlayerDataManager().getEntries()) {
}
}
}
@@ -7,7 +7,8 @@ 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;
import ac.grim.grimac.checks.impl.packetorder.PacketOrderD;
import ac.grim.grimac.checks.impl.packetorder.PacketOrderProcessor;
import ac.grim.grimac.events.packets.CheckManagerListener;
import ac.grim.grimac.manager.*;
import ac.grim.grimac.predictionengine.MovementCheckRunner;
@@ -21,6 +22,7 @@ import ac.grim.grimac.utils.data.packetentity.PacketEntitySelf;
import ac.grim.grimac.utils.data.tags.SyncedTags;
import ac.grim.grimac.utils.enums.FluidTag;
import ac.grim.grimac.utils.enums.Pose;
import ac.grim.grimac.utils.inventory.InventoryDesyncStatus;
import ac.grim.grimac.utils.latency.*;
import ac.grim.grimac.utils.math.GrimMath;
import ac.grim.grimac.utils.math.TrigHandler;
@@ -54,7 +56,7 @@ import net.kyori.adventure.text.TranslatableComponent;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.entity.Player;
import org.bukkit.util.Vector;
import ac.grim.grimac.utils.vector.Vector3D;
import org.jetbrains.annotations.Nullable;
import java.util.*;
@@ -64,6 +66,10 @@ import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import static ac.grim.grimac.utils.data.VectorData.VectorType;
import static ac.grim.grimac.utils.data.VectorData.VectorType.Normal;
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
// Everything in this class should be sync'd to the anticheat thread.
// Put variables sync'd to the netty thread in PacketStateData
// Variables that need lag compensation should have their own class
@@ -90,8 +96,9 @@ public class GrimPlayer implements GrimUser {
public MovementCheckRunner movementCheckRunner;
public SyncedTags tagManager;
// End manager like classes
public Vector clientVelocity = new Vector();
public ac.grim.grimac.utils.vector.Vector3D clientVelocity = newVector3D();
PacketTracker packetTracker;
public final PacketOrderProcessor packetOrderProcessor = new PacketOrderProcessor(this);
private long transactionPing = 0;
public long lastTransSent = 0;
public long lastTransReceived = 0;
@@ -103,9 +110,9 @@ public class GrimPlayer implements GrimUser {
public boolean hasGravity = true;
public final long joinTime = System.currentTimeMillis();
public boolean playerEntityHasGravity = true;
public VectorData predictedVelocity = new VectorData(new Vector(), VectorData.VectorType.Normal);
public Vector actualMovement = new Vector();
public Vector stuckSpeedMultiplier = new Vector(1, 1, 1);
public VectorData predictedVelocity = new VectorData(newVector3D(), Normal);
public Vector3D actualMovement = newVector3D();
public Vector3D stuckSpeedMultiplier = newVector3D(1, 1, 1);
public UncertaintyHandler uncertaintyHandler;
public double gravity;
public float friction;
@@ -184,9 +191,9 @@ public class GrimPlayer implements GrimUser {
public TrigHandler trigHandler;
public PacketStateData packetStateData;
// Keep track of basetick stuff
public Vector baseTickAddition = new Vector();
public Vector baseTickWaterPushing = new Vector();
public Vector startTickClientVel = new Vector();
public Vector3D baseTickAddition = newVector3D();
public Vector3D baseTickWaterPushing = newVector3D();
public Vector3D startTickClientVel = newVector3D();
// For syncing the player's full swing in 1.9+
public int movementPackets = 0;
public VelocityData firstBreadKB = null;
@@ -213,6 +220,9 @@ public class GrimPlayer implements GrimUser {
}
public int totalFlyingPacketsSent;
public boolean hasInventoryOpen;
public long lastInventoryOpen;
public InventoryDesyncStatus inventoryDesyncStatus;
public Queue<BlockPlaceSnapshot> placeUseItemPackets = new LinkedBlockingQueue<>();
// This variable is for support with test servers that want to be able to disable grim
// Grim disabler 2022 still working!
@@ -326,7 +336,7 @@ public class GrimPlayer implements GrimUser {
boolean hasID = false;
int skipped = 0;
for (Pair<Short, Long> iterator : transactionsSent) {
if (iterator.first() == id) {
if (iterator.getFirst() == id) {
hasID = true;
break;
}
@@ -338,7 +348,7 @@ public class GrimPlayer implements GrimUser {
if (packetTracker != null) packetTracker.setIntervalPackets(packetTracker.getIntervalPackets() - 1);
if (skipped > 0 && System.currentTimeMillis() - joinTime > 5000)
checkManager.getPacketCheck(TransactionOrder.class).flagAndAlert("skipped: " + skipped);
checkManager.getPacketCheck(PacketOrderD.class).flagAndAlert("skipped: " + skipped);
do {
data = transactionsSent.poll();
@@ -347,9 +357,9 @@ public class GrimPlayer implements GrimUser {
lastTransactionReceived.incrementAndGet();
lastTransReceived = System.currentTimeMillis();
transactionPing = (System.nanoTime() - data.second());
playerClockAtLeast = data.second();
} while (data.first() != id);
transactionPing = (System.nanoTime() - data.getSecond());
playerClockAtLeast = data.getSecond();
} while (data.getFirst() != id);
// A transaction means a new tick, so apply any block places
CheckManagerListener.handleQueuedPlaces(this, false, 0, 0, System.currentTimeMillis());
@@ -357,18 +367,18 @@ public class GrimPlayer implements GrimUser {
}
// Were we the ones who sent the packet?
return data != null && data.first() == id;
return data != null && data.getFirst() == id;
}
public void baseTickAddWaterPushing(Vector vector) {
public void baseTickAddWaterPushing(ac.grim.grimac.utils.vector.Vector3D vector) {
baseTickWaterPushing.add(vector);
}
public void baseTickAddVector(Vector vector) {
public void baseTickAddVector(Vector3D vector) {
clientVelocity.add(vector);
}
public void trackBaseTickAddition(Vector vector) {
public void trackBaseTickAddition(Vector3D vector) {
baseTickAddition.add(vector);
}
@@ -443,7 +453,8 @@ public class GrimPlayer implements GrimUser {
public void disconnect(Component reason) {
String textReason;
if (reason instanceof TranslatableComponent translatableComponent) {
if (reason instanceof TranslatableComponent) {
TranslatableComponent translatableComponent = (TranslatableComponent) reason;
textReason = translatableComponent.key();
} else {
textReason = LegacyComponentSerializer.legacySection().serialize(reason);
@@ -38,7 +38,11 @@ import com.github.retrooper.packetevents.protocol.player.GameMode;
import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState;
import com.github.retrooper.packetevents.protocol.world.states.defaulttags.BlockTags;
import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
import org.bukkit.util.Vector;
import ac.grim.grimac.utils.vector.Vector3D;
import static ac.grim.grimac.utils.data.VectorData.VectorType;
import static ac.grim.grimac.utils.data.VectorData.VectorType.Dead;
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
public class MovementCheckRunner extends Check implements PositionCheck {
// Averaged over 500 predictions (Defaults set slightly above my 3600x results)
@@ -138,10 +142,10 @@ public class MovementCheckRunner extends Check implements PositionCheck {
double negX = Math.min(-0.05, GrimMath.clamp(player.actualMovement.getX(), -16, 16) - 0.05);
double negZ = Math.min(-0.05, GrimMath.clamp(player.actualMovement.getZ(), -16, 16) - 0.05);
Vector NE = Collisions.maybeBackOffFromEdge(new Vector(posX, 0, negZ), player, true);
Vector NW = Collisions.maybeBackOffFromEdge(new Vector(negX, 0, negZ), player, true);
Vector SE = Collisions.maybeBackOffFromEdge(new Vector(posX, 0, posZ), player, true);
Vector SW = Collisions.maybeBackOffFromEdge(new Vector(negX, 0, posZ), player, true);
Vector3D NE = Collisions.maybeBackOffFromEdge(newVector3D(posX, 0, negZ), player, true);
Vector3D NW = Collisions.maybeBackOffFromEdge(newVector3D(negX, 0, negZ), player, true);
Vector3D SE = Collisions.maybeBackOffFromEdge(newVector3D(posX, 0, posZ), player, true);
Vector3D SW = Collisions.maybeBackOffFromEdge(newVector3D(negX, 0, posZ), player, true);
boolean isEast = NE.getX() != posX || SE.getX() != posX;
boolean isWest = NW.getX() != negX || SW.getX() != negX;
@@ -184,7 +188,7 @@ public class MovementCheckRunner extends Check implements PositionCheck {
player.vehicleData.wasVehicleSwitch = false;
if (riding != null) {
Vector pos = new Vector(player.x, player.y, player.z);
Vector3D pos = newVector3D(player.x, player.y, player.z);
SimpleCollisionBox interTruePositions = riding.getPossibleCollisionBoxes();
// We shrink the expanded bounding box to what the packet positions can be, for a smaller box
@@ -194,7 +198,7 @@ public class MovementCheckRunner extends Check implements PositionCheck {
interTruePositions.expand(-width, 0, -width);
interTruePositions.expandMax(0, -height, 0);
Vector cutTo = VectorUtils.cutBoxToVector(pos, interTruePositions);
Vector3D cutTo = VectorUtils.cutBoxToVector(pos, interTruePositions);
// Now we need to simulate a tick starting at the most optimal position
// The start position is never sent, so we assume the most optimal start position
@@ -212,14 +216,14 @@ public class MovementCheckRunner extends Check implements PositionCheck {
} else {
// Server always teleports the player when they eject anyways,
// so just let the player control where they eject within reason, they get set back anyways
if (new Vector(player.lastX, player.lastY, player.lastZ).distance(new Vector(player.x, player.y, player.z)) > 3) {
if (newVector3D(player.lastX, player.lastY, player.lastZ).distance(newVector3D(player.x, player.y, player.z)) > 3) {
player.getSetbackTeleportUtil().executeForceResync(); // Too far! (I think this value is sane)
}
handleTeleport(update);
if (player.isClimbing) {
Vector ladder = player.clientVelocity.clone().setY(0.2);
Vector3D ladder = player.clientVelocity.clone().setY(0.2);
PredictionEngineNormal.staticVectorEndOfTick(player, ladder);
player.lastWasClimbing = ladder.getY();
}
@@ -258,7 +262,8 @@ public class MovementCheckRunner extends Check implements PositionCheck {
// When in control of the entity, the player sets the entity position to their current position
riding.setPositionRaw(GetBoundingBox.getPacketEntityBoundingBox(player, player.x, player.y, player.z, riding));
if (riding instanceof PacketEntityTrackXRot boat) {
if (riding instanceof PacketEntityTrackXRot) {
PacketEntityTrackXRot boat = (PacketEntityTrackXRot) riding;
boat.packetYaw = player.xRot;
boat.interpYaw = player.xRot;
boat.steps = 0;
@@ -299,7 +304,7 @@ public class MovementCheckRunner extends Check implements PositionCheck {
player.clientControlledVerticalCollision = Math.abs(player.y % (1 / 64D)) < 0.00001;
// This isn't the final velocity of the player in the tick, only the one applied to the player
player.actualMovement = new Vector(player.x - player.lastX, player.y - player.lastY, player.z - player.lastZ);
player.actualMovement = newVector3D(player.x - player.lastX, player.y - player.lastY, player.z - player.lastZ);
if (player.isSprinting != player.lastSprinting) {
player.compensatedEntities.hasSprintingAttributeEnabled = player.isSprinting;
@@ -338,7 +343,7 @@ public class MovementCheckRunner extends Check implements PositionCheck {
boolean clientClaimsRiptide = player.packetStateData.tryingToRiptide;
if (player.packetStateData.tryingToRiptide) {
long currentTime = System.currentTimeMillis();
boolean isInWater = player.compensatedWorld.isRaining || Collisions.hasMaterial(player, player.boundingBox.copy().expand(0.1f), (block) -> Materials.isWater(CompensatedWorld.blockVersion, block.first()));
boolean isInWater = player.compensatedWorld.isRaining || Collisions.hasMaterial(player, player.boundingBox.copy().expand(0.1f), (block) -> Materials.isWater(CompensatedWorld.blockVersion, block.getFirst()));
if (currentTime - player.packetStateData.lastRiptide < 450 || !isInWater) {
player.packetStateData.tryingToRiptide = false;
@@ -349,7 +354,7 @@ public class MovementCheckRunner extends Check implements PositionCheck {
SimpleCollisionBox steppingOnBB = GetBoundingBox.getCollisionBoxForPlayer(player, player.x, player.y, player.z).expand(0.03).offset(0, -1, 0);
Collisions.hasMaterial(player, steppingOnBB, (pair) -> {
WrappedBlockState data = pair.first();
WrappedBlockState data = pair.getFirst();
if (data.getType() == StateTypes.SLIME_BLOCK && player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_8)) {
player.uncertaintyHandler.isSteppingOnSlime = true;
player.uncertaintyHandler.isSteppingOnBouncyBlock = true;
@@ -395,8 +400,8 @@ public class MovementCheckRunner extends Check implements PositionCheck {
player.uncertaintyHandler.isNearGlitchyBlock = player.getClientVersion().isOlderThan(ClientVersion.V_1_9)
&& Collisions.hasMaterial(player, expandedBB.copy().expand(0.2),
checkData -> BlockTags.ANVIL.contains(checkData.first().getType())
|| checkData.first().getType() == StateTypes.CHEST || checkData.first().getType() == StateTypes.TRAPPED_CHEST);
checkData -> BlockTags.ANVIL.contains(checkData.getFirst().getType())
|| checkData.getFirst().getType() == StateTypes.CHEST || checkData.getFirst().getType() == StateTypes.TRAPPED_CHEST);
player.uncertaintyHandler.isOrWasNearGlitchyBlock = isGlitchy || player.uncertaintyHandler.isNearGlitchyBlock;
player.uncertaintyHandler.checkForHardCollision();
@@ -426,8 +431,8 @@ public class MovementCheckRunner extends Check implements PositionCheck {
// Exempt if the player is dead or is riding a dead entity
if (player.compensatedEntities.getSelf().isDead || (riding != null && riding.isDead)) {
// Dead players can't cheat, if you find a way how they could, open an issue
player.predictedVelocity = new VectorData(new Vector(), VectorData.VectorType.Dead);
player.clientVelocity = new Vector();
player.predictedVelocity = new VectorData(newVector3D(), Dead);
player.clientVelocity = newVector3D();
} else if (player.disableGrim || (PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_8) && player.gamemode == GameMode.SPECTATOR) || player.isFlying) {
// We could technically check spectator but what's the point...
// Added complexity to analyze a gamemode used mainly by moderators
@@ -450,7 +455,7 @@ public class MovementCheckRunner extends Check implements PositionCheck {
// Riptiding while on the ground moves the hitbox upwards before any movement code runs
// It's a pain to support and this is my best attempt
if (player.lastOnGround && player.packetStateData.tryingToRiptide && !player.compensatedEntities.getSelf().inVehicle()) {
Vector pushingMovement = Collisions.collide(player, 0, 1.1999999F, 0);
Vector3D pushingMovement = Collisions.collide(player, 0, 1.1999999F, 0);
player.verticalCollision = pushingMovement.getY() != 1.1999999F;
double currentY = player.clientVelocity.getY();
@@ -462,7 +467,7 @@ public class MovementCheckRunner extends Check implements PositionCheck {
player.lastY += pushingMovement.getY();
new PlayerBaseTick(player).updatePlayerPose();
player.boundingBox = GetBoundingBox.getPlayerBoundingBox(player, player.lastX, player.lastY, player.lastZ);
player.actualMovement = new Vector(player.x - player.lastX, player.y - player.lastY, player.z - player.lastZ);
player.actualMovement = newVector3D(player.x - player.lastX, player.y - player.lastY, player.z - player.lastZ);
player.couldSkipTick = true;
@@ -514,7 +519,7 @@ public class MovementCheckRunner extends Check implements PositionCheck {
//
// Checking for oldClientVel being too high fixes BleachHack vertical scaffold
if (player.getSetbackTeleportUtil().getRequiredSetBack() != null && player.getSetbackTeleportUtil().getRequiredSetBack().getTicksComplete() == 1) {
Vector setbackVel = player.getSetbackTeleportUtil().getRequiredSetBack().getVelocity();
Vector3D setbackVel = player.getSetbackTeleportUtil().getRequiredSetBack().getVelocity();
// A player must have velocity going INTO the ground to be able to jump
// Otherwise they could ignore upwards velocity that isn't useful into more useful upwards velocity (towering)
// So if they are supposed to be going upwards, or are supposed to be off the ground, resync
@@ -543,7 +548,7 @@ public class MovementCheckRunner extends Check implements PositionCheck {
// Patch sprint jumping with elytra exploit
if (player.bukkitPlayer != null && player.isGliding && player.predictedVelocity.isJump() && player.isSprinting && !allowSprintJumpingWithElytra) {
SetbackTeleportUtil.SetbackPosWithVector lastKnownGoodPosition = player.getSetbackTeleportUtil().lastKnownGoodPosition;
lastKnownGoodPosition.setVector(lastKnownGoodPosition.getVector().multiply(new Vector(0.6 * 0.91, 1, 0.6 * 0.91)));
lastKnownGoodPosition.setVector(lastKnownGoodPosition.getVector().multiply(newVector3D(0.6 * 0.91, 1, 0.6 * 0.91)));
player.getSetbackTeleportUtil().executeNonSimulatingSetback();
}
@@ -620,7 +625,7 @@ public class MovementCheckRunner extends Check implements PositionCheck {
* @param pushingMovement The collision result when trying to move the player upwards by 1.2f
* @return Whether it is more likely that this player was on the ground the tick they riptided
*/
private boolean likelyGroundRiptide(Vector pushingMovement) {
private boolean likelyGroundRiptide(Vector3D pushingMovement) {
// Y velocity gets reset if the player collides vertically
double riptideYResult = Riptide.getRiptideVelocity(player).getY();
@@ -9,16 +9,19 @@ import ac.grim.grimac.utils.enums.Pose;
import ac.grim.grimac.utils.latency.CompensatedEntities;
import ac.grim.grimac.utils.math.GrimMath;
import ac.grim.grimac.utils.nmsutil.*;
import ac.grim.grimac.utils.vector.Vector3D;
import com.github.retrooper.packetevents.protocol.attribute.Attributes;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.world.BlockFace;
import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
import com.github.retrooper.packetevents.util.Vector3d;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerUpdateAttributes;
import org.bukkit.util.Vector;
import ac.grim.grimac.utils.vector.Vector3D;
import java.util.Optional;
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
public class PlayerBaseTick {
GrimPlayer player;
@@ -40,11 +43,11 @@ public class PlayerBaseTick {
public void doBaseTick() {
// Keep track of basetick stuff
player.baseTickAddition = new Vector();
player.baseTickWaterPushing = new Vector();
player.baseTickAddition = newVector3D();
player.baseTickWaterPushing = newVector3D();
if (player.isFlying && player.isSneaking && !player.compensatedEntities.getSelf().inVehicle()) {
Vector flyingShift = new Vector(0, player.flySpeed * -3, 0);
Vector3D flyingShift = newVector3D(0, player.flySpeed * -3, 0);
player.baseTickAddVector(flyingShift);
player.trackBaseTickAddition(flyingShift);
}
@@ -60,7 +63,7 @@ public class PlayerBaseTick {
// You cannot crouch while flying, only shift - could be specific to 1.14?
// pre-1.13 clients don't have this code
if (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_13) && player.wasTouchingWater && player.isSneaking && !player.isFlying && !player.compensatedEntities.getSelf().inVehicle()) {
Vector waterPushVector = new Vector(0, -0.04f, 0);
Vector3D waterPushVector = newVector3D(0, -0.04f, 0);
player.baseTickAddVector(waterPushVector);
player.trackBaseTickAddition(waterPushVector);
}
@@ -151,7 +154,7 @@ public class PlayerBaseTick {
// Might be null after respawn?
final Optional<WrapperPlayServerUpdateAttributes.Property> property = playerSpeed.property();
if (property.isEmpty()) return;
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"));
@@ -360,12 +363,22 @@ public class PlayerBaseTick {
double d7 = direction2 == BlockFace.WEST || direction2 == BlockFace.EAST ? relativeXMovement : relativeZMovement;
d6 = direction2 == BlockFace.EAST || direction2 == BlockFace.SOUTH ? 1.0 - d7 : d7;
// d7 and d6 flip the movement direction based on desired movement direction
boolean doesSuffocate = switch (direction2) {
case EAST -> this.suffocatesAt(blockX + 1, blockZ);
case WEST -> this.suffocatesAt(blockX - 1, blockZ);
case NORTH -> this.suffocatesAt(blockX, blockZ - 1);
default -> this.suffocatesAt(blockX, blockZ + 1);
};
boolean doesSuffocate;
switch (direction2) {
case EAST:
doesSuffocate = this.suffocatesAt(blockX + 1, blockZ);
break;
case WEST:
doesSuffocate = this.suffocatesAt(blockX - 1, blockZ);
break;
case NORTH:
doesSuffocate = this.suffocatesAt(blockX, blockZ - 1);
break;
default:
case SOUTH:
doesSuffocate = this.suffocatesAt(blockX, blockZ + 1);
break;
}
if (d6 >= lowestValue || doesSuffocate) continue;
lowestValue = d6;
@@ -413,7 +426,7 @@ public class PlayerBaseTick {
}
boolean hasPushed = false;
Vector vec3 = new Vector();
Vector3D vec3 = newVector3D();
for (int x = floorX; x < ceilX; ++x) {
for (int y = floorY; y < ceilY; ++y) {
@@ -463,7 +476,7 @@ public class PlayerBaseTick {
}
double d2 = 0.0;
boolean hasTouched = false;
Vector vec3 = new Vector();
Vector3D vec3 = newVector3D();
int n7 = 0;
for (int x = floorX; x < ceilX; ++x) {
@@ -488,7 +501,7 @@ public class PlayerBaseTick {
d2 = Math.max(fluidHeightToWorld - aABB.minY, d2);
if (!player.isFlying) {
Vector vec32 = FluidTypeFlowing.getFlow(player, x, y, z);
Vector3D vec32 = FluidTypeFlowing.getFlow(player, x, y, z);
if (d2 < 0.4) {
vec32 = vec32.multiply(d2);
}
@@ -1,5 +1,7 @@
package ac.grim.grimac.predictionengine;
import ac.grim.grimac.checks.impl.combat.Reach;
import ac.grim.grimac.checks.impl.scaffolding.LineOfSightPlace;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.predictionengine.predictions.PredictionEngine;
import ac.grim.grimac.utils.collisions.CollisionData;
@@ -16,13 +18,16 @@ import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState
import com.github.retrooper.packetevents.protocol.world.states.defaulttags.BlockTags;
import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
import com.github.retrooper.packetevents.util.Vector3i;
import lombok.Getter;
import lombok.Setter;
import org.bukkit.util.Vector;
import ac.grim.grimac.utils.vector.Vector3D;
import java.util.OptionalInt;
import java.util.Set;
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
/**
* A lot of care was put into handling all the stupid stuff occurring between events
* <p>
@@ -104,6 +109,7 @@ public class PointThreeEstimator {
private boolean isNearHorizontalFlowingLiquid = false; // We can't calculate the direction, only a toggle
private boolean isNearVerticalFlowingLiquid = false; // We can't calculate exact values, once again a toggle
private boolean isNearBubbleColumn = false; // We can't calculate exact values once again
public boolean isNearNetherPortal = false; // We can't calculate exact values once again
private int maxPositiveLevitation = Integer.MIN_VALUE; // Positive potion effects [0, 128]
private int minNegativeLevitation = Integer.MAX_VALUE; // Negative potion effects [-127, -1]r
@@ -140,7 +146,7 @@ public class PointThreeEstimator {
isNearBubbleColumn = true;
}
Vector fluidVector = FluidTypeFlowing.getFlow(player, x, y, z);
Vector3D fluidVector = FluidTypeFlowing.getFlow(player, x, y, z);
if (fluidVector.getX() != 0 || fluidVector.getZ() != 0) {
isNearHorizontalFlowingLiquid = true;
}
@@ -151,6 +157,10 @@ public class PointThreeEstimator {
isNearFluid = true;
}
player.checkManager.getPacketCheck(Reach.class).handleBlockChange(new Vector3i(x, y, z), state);
player.checkManager.getBlockPlaceCheck(LineOfSightPlace.class).handleBlockChange(new Vector3i(x, y, z), state);
if (pointThreeBox.isIntersected(new SimpleCollisionBox(x, y, z))) {
// https://github.com/MWHunter/Grim/issues/613
int controllingEntityId = player.compensatedEntities.getSelf().inVehicle() ? player.getRidingVehicleId() : player.entityID;
@@ -245,27 +255,32 @@ public class PointThreeEstimator {
isNearClimbable = false;
isNearBubbleColumn = false;
isNearFluid = false;
isNearNetherPortal = false;
// Check for flowing water
Collisions.hasMaterial(player, pointThreeBox, (pair) -> {
final WrappedBlockState state = pair.first();
final WrappedBlockState state = pair.getFirst();
final StateType stateType = state.getType();
if (player.tagManager.block(SyncedTags.CLIMBABLE).contains(stateType) || (stateType == StateTypes.POWDER_SNOW && !player.compensatedEntities.getSelf().inVehicle() && player.getInventory().getBoots().getType() == ItemTypes.LEATHER_BOOTS)) {
isNearClimbable = true;
}
if (BlockTags.TRAPDOORS.contains(stateType)) {
isNearClimbable = isNearClimbable || Collisions.trapdoorUsableAsLadder(player, pair.second().getX(), pair.second().getY(), pair.second().getZ(), state);
isNearClimbable = isNearClimbable || Collisions.trapdoorUsableAsLadder(player, pair.getSecond().getX(), pair.getSecond().getY(), pair.getSecond().getZ(), state);
}
if (stateType == StateTypes.BUBBLE_COLUMN) {
isNearBubbleColumn = true;
}
if (Materials.isWater(player.getClientVersion(), pair.first()) || pair.first().getType() == StateTypes.LAVA) {
if (Materials.isWater(player.getClientVersion(), pair.getFirst()) || pair.getFirst().getType() == StateTypes.LAVA) {
isNearFluid = true;
}
if (state.getType() == StateTypes.NETHER_PORTAL) {
isNearNetherPortal = true;
}
return false;
});
}
@@ -303,7 +318,7 @@ public class PointThreeEstimator {
player.boundingBox = player.boundingBox.copy().expand(0.03, 0, 0.03).offset(0, 0.03, 0);
// 0.16 magic value -> 0.03 plus gravity, plus some additional lenience
double searchDistance = -0.2 + Math.min(0, y);
Vector collisionResult = Collisions.collide(player, 0, searchDistance, 0);
Vector3D collisionResult = Collisions.collide(player, 0, searchDistance, 0);
player.boundingBox = playerBox;
return collisionResult.getY() != searchDistance;
}
@@ -337,9 +352,9 @@ public class PointThreeEstimator {
// Takes 0.01 millis, on average, to compute... this should be improved eventually
for (VectorData data : init) {
// Try to get the vector as close to zero as possible to give the best chance at 0.03...
Vector toZeroVec = new PredictionEngine().handleStartingVelocityUncertainty(player, data, new Vector());
Vector3D toZeroVec = new PredictionEngine().handleStartingVelocityUncertainty(player, data, newVector3D());
// Collide to handle mostly gravity, but other scenarios similar to this.
Vector collisionResult = Collisions.collide(player, toZeroVec.getX(), toZeroVec.getY(), toZeroVec.getZ(), Integer.MIN_VALUE, null);
Vector3D collisionResult = Collisions.collide(player, toZeroVec.getX(), toZeroVec.getY(), toZeroVec.getZ(), Integer.MIN_VALUE, null);
// If this tick is the tick after y velocity was by 0, a stepping movement is POSSIBLE to have been hidden
// A bit hacky... is there a better way? I'm unsure...
@@ -13,11 +13,12 @@ import ac.grim.grimac.utils.nmsutil.ReachUtils;
import com.github.retrooper.packetevents.protocol.attribute.Attributes;
import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes;
import com.github.retrooper.packetevents.protocol.world.BlockFace;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import org.bukkit.util.Vector;
import ac.grim.grimac.utils.vector.Vector3D;
import java.util.*;
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
public class UncertaintyHandler {
private final GrimPlayer player;
// Handles uncertainty when a piston could have pushed a player in a direction
@@ -148,15 +149,15 @@ public class UncertaintyHandler {
entityBox.maxY -= height;
entityBox.expand(-width / 2, 0, -width / 2);
Vector maxLocation = new Vector(entityBox.maxX, entityBox.maxY, entityBox.maxZ);
Vector minLocation = new Vector(entityBox.minX, entityBox.minY, entityBox.minZ);
Vector3D maxLocation = newVector3D(entityBox.maxX, entityBox.maxY, entityBox.maxZ);
Vector3D minLocation = newVector3D(entityBox.minX, entityBox.minY, entityBox.minZ);
Vector diff = minLocation.subtract(new Vector(player.lastX, player.lastY + 0.8 * 1.8, player.lastZ)).multiply(0.1);
Vector3D diff = minLocation.subtract(newVector3D(player.lastX, player.lastY + 0.8 * 1.8, player.lastZ)).multiply(0.1);
fishingRodPullBox.minX = Math.min(0, diff.getX());
fishingRodPullBox.minY = Math.min(0, diff.getY());
fishingRodPullBox.minZ = Math.min(0, diff.getZ());
diff = maxLocation.subtract(new Vector(player.lastX, player.lastY + 0.8 * 1.8, player.lastZ)).multiply(0.1);
diff = maxLocation.subtract(newVector3D(player.lastX, player.lastY + 0.8 * 1.8, player.lastZ)).multiply(0.1);
fishingRodPullBox.maxX = Math.max(0, diff.getX());
fishingRodPullBox.maxY = Math.max(0, diff.getY());
fishingRodPullBox.maxZ = Math.max(0, diff.getZ());
@@ -171,8 +172,8 @@ public class UncertaintyHandler {
fireworksBox = new SimpleCollisionBox();
Vector currentLook = ReachUtils.getLook(player, player.xRot, player.yRot);
Vector lastLook = ReachUtils.getLook(player, player.lastXRot, player.lastYRot);
Vector3D currentLook = ReachUtils.getLook(player, player.xRot, player.yRot);
ac.grim.grimac.utils.vector.Vector3D lastLook = ReachUtils.getLook(player, player.lastXRot, player.lastYRot);
double antiTickSkipping = player.isPointThree() ? 0 : 0.05; // With 0.03, let that handle tick skipping
@@ -304,7 +305,8 @@ public class UncertaintyHandler {
// This is the end of that section.
// I can't figure out how the client exactly tracks boost time
if (player.compensatedEntities.getSelf().getRiding() instanceof PacketEntityRideable vehicle) {
if (player.compensatedEntities.getSelf().getRiding() instanceof PacketEntityRideable) {
PacketEntityRideable vehicle = (PacketEntityRideable) player.compensatedEntities.getSelf().getRiding();
if (vehicle.currentBoostTime < vehicle.boostTimeMax + 20)
offset -= 0.01;
}
@@ -24,7 +24,9 @@ import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
import com.github.retrooper.packetevents.util.Vector3d;
import com.viaversion.viaversion.api.Via;
import io.github.retrooper.packetevents.util.viaversion.ViaVersionUtil;
import org.bukkit.util.Vector;
import ac.grim.grimac.utils.vector.Vector3D;
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
public class MovementTicker {
public final GrimPlayer player;
@@ -77,9 +79,9 @@ public class MovementTicker {
player.uncertaintyHandler.collidingEntities.add(possibleCollidingEntities);
}
public void move(Vector inputVel, Vector collide) {
public void move(Vector3D inputVel, Vector3D collide) {
if (player.stuckSpeedMultiplier.getX() < 0.99) {
player.clientVelocity = new Vector();
player.clientVelocity = newVector3D();
}
if (inputVel.getX() != collide.getX()) {
@@ -172,21 +174,21 @@ public class MovementTicker {
// The game disregards movements smaller than 1e-7 (such as in boats)
if (collide.lengthSquared() < 1e-7) {
collide = new Vector();
collide = newVector3D();
}
// This is where vanilla moves the bounding box and sets it
player.predictedVelocity = new VectorData(collide.clone(), player.predictedVelocity.lastVector, player.predictedVelocity.vectorType);
float f = BlockProperties.getBlockSpeedFactor(player, player.mainSupportingBlockData, new Vector3d(player.x, player.y, player.z));
player.clientVelocity.multiply(new Vector(f, 1, f));
player.clientVelocity.multiply(newVector3D(f, 1, f));
// Reset stuck speed so it can update
if (player.stuckSpeedMultiplier.getX() < 0.99) {
player.uncertaintyHandler.lastStuckSpeedMultiplier.reset();
}
player.stuckSpeedMultiplier = new Vector(1, 1, 1);
player.stuckSpeedMultiplier = newVector3D(1, 1, 1);
// 1.15 and older clients use the handleInsideBlocks method for lava
if (player.getClientVersion().isOlderThan(ClientVersion.V_1_16))
@@ -201,7 +203,7 @@ public class MovementTicker {
// Flying players are not affected by cobwebs/sweet berry bushes
if (player.isFlying) {
player.stuckSpeedMultiplier = new Vector(1, 1, 1);
player.stuckSpeedMultiplier = newVector3D(1, 1, 1);
}
}
@@ -388,14 +390,14 @@ public class MovementTicker {
// Lava movement changed in 1.16
if (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_16) && player.slightlyTouchingLava) {
player.clientVelocity = player.clientVelocity.multiply(new Vector(0.5D, 0.800000011920929D, 0.5D));
player.clientVelocity = player.clientVelocity.multiply(newVector3D(0.5D, 0.800000011920929D, 0.5D));
player.clientVelocity = FluidFallingAdjustedMovement.getFluidFallingAdjustedMovement(player, playerGravity, isFalling, player.clientVelocity);
} else {
player.clientVelocity.multiply(0.5D);
}
if (player.hasGravity)
player.clientVelocity.add(new Vector(0.0D, -playerGravity / 4.0D, 0.0D));
player.clientVelocity.add(newVector3D(0.0D, -playerGravity / 4.0D, 0.0D));
} else if (player.isGliding) {
player.friction = 0.99F; // Not vanilla, just useful for other grim stuff
@@ -5,7 +5,9 @@ import ac.grim.grimac.utils.data.packetentity.PacketEntityHorse;
import ac.grim.grimac.utils.nmsutil.Collisions;
import com.github.retrooper.packetevents.protocol.attribute.Attributes;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import org.bukkit.util.Vector;
import ac.grim.grimac.utils.vector.Vector3D;
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
public class MovementTickerHorse extends MovementTickerLivingVehicle {
@@ -26,7 +28,7 @@ public class MovementTickerHorse extends MovementTickerLivingVehicle {
forwardsInput *= 0.25F;
}
this.movementInput = new Vector(horizInput, 0, forwardsInput);
this.movementInput = newVector3D(horizInput, 0, forwardsInput);
if (movementInput.lengthSquared() > 1) movementInput.normalize();
}
@@ -7,10 +7,13 @@ import ac.grim.grimac.predictionengine.predictions.rideable.PredictionEngineRide
import ac.grim.grimac.predictionengine.predictions.rideable.PredictionEngineRideableWaterLegacy;
import ac.grim.grimac.utils.nmsutil.BlockProperties;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import org.bukkit.util.Vector;
import ac.grim.grimac.utils.vector.Vector3D;
import ac.grim.grimac.utils.vector.Vector3D;
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
public class MovementTickerLivingVehicle extends MovementTicker {
Vector movementInput = new Vector();
Vector3D movementInput = newVector3D();
public MovementTickerLivingVehicle(GrimPlayer player) {
super(player);
@@ -3,12 +3,14 @@ package ac.grim.grimac.predictionengine.movementtick;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.data.packetentity.PacketEntityRideable;
import com.github.retrooper.packetevents.protocol.attribute.Attributes;
import org.bukkit.util.Vector;
import ac.grim.grimac.utils.vector.Vector3D;
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
public class MovementTickerPig extends MovementTickerRideable {
public MovementTickerPig(GrimPlayer player) {
super(player);
movementInput = new Vector(0, 0, 1);
movementInput = newVector3D(0, 0, 1);
}
@Override
@@ -11,15 +11,17 @@ 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 ac.grim.grimac.utils.vector.Vector3D;
import java.util.ArrayList;
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
public class MovementTickerStrider extends MovementTickerRideable {
public MovementTickerStrider(GrimPlayer player) {
super(player);
movementInput = new Vector(0, 0, 1);
movementInput = newVector3D(0, 0, 1);
}
public static void floatStrider(GrimPlayer player) {
@@ -27,7 +29,7 @@ public class MovementTickerStrider extends MovementTickerRideable {
if (isAbove(player) && player.compensatedWorld.getLavaFluidLevelAt((int) Math.floor(player.x), (int) Math.floor(player.y + 1), (int) Math.floor(player.z)) == 0) {
player.onGround = true;
} else {
player.clientVelocity.multiply(0.5).add(new Vector(0, 0.05, 0));
player.clientVelocity.multiply(0.5).add(newVector3D(0, 0.05, 0));
}
}
}
@@ -11,20 +11,31 @@ import ac.grim.grimac.utils.nmsutil.Collisions;
import ac.grim.grimac.utils.nmsutil.GetBoundingBox;
import ac.grim.grimac.utils.nmsutil.JumpPower;
import ac.grim.grimac.utils.nmsutil.Riptide;
import ac.grim.grimac.utils.vector.Vector3D;
import com.github.retrooper.packetevents.protocol.attribute.Attributes;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import org.bukkit.util.Vector;
import ac.grim.grimac.utils.vector.Vector3D;
import java.util.*;
import static ac.grim.grimac.utils.data.VectorData.VectorType;
import static ac.grim.grimac.utils.data.VectorData.VectorType.ZeroPointZeroThree;
import static ac.grim.grimac.utils.nmsutil.Collisions.isEmpty;
import static ac.grim.grimac.utils.nmsutil.GetBoundingBox.getBoundingBoxFromPosAndSize;
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
import static com.github.retrooper.packetevents.protocol.attribute.Attributes.GENERIC_GRAVITY;
import static com.github.retrooper.packetevents.protocol.player.ClientVersion.V_1_13;
import static java.lang.Math.*;
import static java.util.Collections.max;
public class PredictionEngine {
public static Vector clampMovementToHardBorder(GrimPlayer player, Vector outputVel) {
public static Vector3D clampMovementToHardBorder(GrimPlayer player, Vector3D outputVel) {
// TODO: Reimplement
return outputVel;
}
public static Vector transformInputsToVector(GrimPlayer player, Vector theoreticalInput) {
public static Vector3D transformInputsToVector(GrimPlayer player, Vector3D theoreticalInput) {
float bestPossibleX;
float bestPossibleZ;
@@ -42,15 +53,15 @@ public class PredictionEngine {
bestPossibleZ *= 0.2F;
}
Vector inputVector = new Vector(bestPossibleX, 0, bestPossibleZ);
Vector3D inputVector = newVector3D(bestPossibleX, 0, bestPossibleZ);
inputVector.multiply(0.98F);
// Simulate float rounding imprecision
inputVector = new Vector((float) inputVector.getX(), (float) inputVector.getY(), (float) inputVector.getZ());
inputVector = newVector3D((float) inputVector.getX(), (float) inputVector.getY(), (float) inputVector.getZ());
if (inputVector.lengthSquared() > 1) {
double d0 = Math.sqrt(inputVector.getX() * inputVector.getX() + inputVector.getY() * inputVector.getY() + inputVector.getZ() * inputVector.getZ());
inputVector = new Vector(inputVector.getX() / d0, inputVector.getY() / d0, inputVector.getZ() / d0);
double d0 = sqrt(inputVector.getX() * inputVector.getX() + inputVector.getY() * inputVector.getY() + inputVector.getZ() * inputVector.getZ());
inputVector = newVector3D(inputVector.getX() / d0, inputVector.getY() / d0, inputVector.getZ() / d0);
}
return inputVector;
@@ -62,7 +73,7 @@ public class PredictionEngine {
if (player.uncertaintyHandler.influencedByBouncyBlock()) {
for (VectorData data : init) {
// Try to get the vector as close to zero as possible to give the best chance at 0.03...
Vector toZeroVec = new PredictionEngine().handleStartingVelocityUncertainty(player, data, new Vector(0, -1000000000, 0)); // Downwards without overflow risk
Vector3D toZeroVec = new PredictionEngine().handleStartingVelocityUncertainty(player, data, newVector3D(0, -1000000000, 0)); // Downwards without overflow risk
player.uncertaintyHandler.nextTickSlimeBlockUncertainty = Math.max(Math.abs(toZeroVec.getY()), player.uncertaintyHandler.nextTickSlimeBlockUncertainty);
}
@@ -105,8 +116,8 @@ public class PredictionEngine {
double bestInput = Double.MAX_VALUE;
VectorData bestCollisionVel = null;
Vector beforeCollisionMovement = null;
Vector originalClientVel = player.clientVelocity.clone();
Vector3D beforeCollisionMovement = null;
Vector3D originalClientVel = player.clientVelocity.clone();
SimpleCollisionBox originalBB = player.boundingBox;
// 0.03 doesn't exist with vehicles, thank god
@@ -116,9 +127,9 @@ public class PredictionEngine {
player.skippedTickInActualMovement = false;
for (VectorData clientVelAfterInput : possibleVelocities) {
Vector primaryPushMovement = handleStartingVelocityUncertainty(player, clientVelAfterInput, player.actualMovement);
Vector3D primaryPushMovement = handleStartingVelocityUncertainty(player, clientVelAfterInput, player.actualMovement);
Vector bestTheoreticalCollisionResult = VectorUtils.cutBoxToVector(player.actualMovement, new SimpleCollisionBox(0, Math.min(0, primaryPushMovement.getY()), 0, primaryPushMovement.getX(), Math.max(0.6, primaryPushMovement.getY()), primaryPushMovement.getZ()).sort());
Vector3D bestTheoreticalCollisionResult = VectorUtils.cutBoxToVector(player.actualMovement, new SimpleCollisionBox(0, Math.min(0, primaryPushMovement.getY()), 0, primaryPushMovement.getX(), Math.max(0.6, primaryPushMovement.getY()), primaryPushMovement.getZ()).sort());
// Check if this vector could ever possible beat the last vector in terms of accuracy
// This is quite a good optimization :)
if (bestTheoreticalCollisionResult.distanceSquared(player.actualMovement) > bestInput && !clientVelAfterInput.isKnockback() && !clientVelAfterInput.isExplosion()) {
@@ -132,9 +143,9 @@ public class PredictionEngine {
}
// Returns pair of primary push movement, and then outputvel
Pair<Vector, Vector> output = doSeekingWallCollisions(player, primaryPushMovement, originalClientVel, clientVelAfterInput);
primaryPushMovement = output.first();
Vector outputVel = clampMovementToHardBorder(player, output.second());
Pair<Vector3D, Vector3D> output = doSeekingWallCollisions(player, primaryPushMovement, originalClientVel, clientVelAfterInput);
primaryPushMovement = output.getFirst();
Vector3D outputVel = clampMovementToHardBorder(player, output.getSecond());
double resultAccuracy = outputVel.distanceSquared(player.actualMovement);
@@ -208,7 +219,7 @@ public class PredictionEngine {
}
}
private Pair<Vector, Vector> doSeekingWallCollisions(GrimPlayer player, Vector primaryPushMovement, Vector originalClientVel, VectorData clientVelAfterInput) {
private Pair<Vector3D, Vector3D> doSeekingWallCollisions(GrimPlayer player, Vector3D primaryPushMovement, Vector3D originalClientVel, VectorData clientVelAfterInput) {
boolean vehicleKB = player.compensatedEntities.getSelf().inVehicle() && clientVelAfterInput.isKnockback() && clientVelAfterInput.vector.getY() == 0;
// Extra collision epsilon required for vehicles to be accurate
double xAdditional = Math.signum(primaryPushMovement.getX()) * SimpleCollisionBox.COLLISION_EPSILON;
@@ -221,9 +232,9 @@ public class PredictionEngine {
double testX = primaryPushMovement.getX() + xAdditional;
double testY = primaryPushMovement.getY() + yAdditional;
double testZ = primaryPushMovement.getZ() + zAdditional;
primaryPushMovement = new Vector(testX, testY, testZ);
primaryPushMovement = newVector3D(testX, testY, testZ);
Vector outputVel = Collisions.collide(player, primaryPushMovement.getX(), primaryPushMovement.getY(), primaryPushMovement.getZ(), originalClientVel.getY(), clientVelAfterInput);
Vector3D outputVel = Collisions.collide(player, primaryPushMovement.getX(), primaryPushMovement.getY(), primaryPushMovement.getZ(), originalClientVel.getY(), clientVelAfterInput);
if (testX == outputVel.getX()) { // the player didn't have X collision, don't ruin offset by collision epsilon
primaryPushMovement.setX(primaryPushMovement.getX() - xAdditional);
@@ -249,20 +260,20 @@ public class PredictionEngine {
// For now just let the player control their Y velocity within 0.03. Gravity should stop exploits.
// 0.03 - 0.784 < -0.03 = can't skip next tick
Vector pointThreeVector = new Vector();
Vector3D pointThreeVector = newVector3D();
// Stop a bypass (and fix falses) by carrying over the player's current velocity IF they couldn't have modified it
if (!player.pointThreeEstimator.controlsVerticalMovement()) {
pointThreeVector.setY(player.clientVelocity.getY());
} else { // Carry over the current Y velocity to try and help with gravity issues
pointThreePossibilities.add(new VectorData(new Vector(0, player.clientVelocity.getY(), 0), VectorData.VectorType.ZeroPointZeroThree));
pointThreePossibilities.add(new VectorData(newVector3D(0, player.clientVelocity.getY(), 0), ZeroPointZeroThree));
}
pointThreePossibilities.add(new VectorData(pointThreeVector, VectorData.VectorType.ZeroPointZeroThree));
// Swim hop
if (player.pointThreeEstimator.isNearFluid && !Collisions.isEmpty(player, player.boundingBox.copy().expand(0.4, 0, 0.4)) && !player.onGround) { // onGround can still be used here, else generic 0.03
pointThreePossibilities.add(new VectorData(new Vector(0, 0.3, 0), VectorData.VectorType.ZeroPointZeroThree));
pointThreePossibilities.add(new VectorData(newVector3D(0, 0.3, 0), ZeroPointZeroThree));
}
// Swimming vertically can add more Y velocity than normal
@@ -279,7 +290,7 @@ public class PredictionEngine {
player.clientVelocity.getX(), 0, player.clientVelocity.getZ()).expand(0.5, -SimpleCollisionBox.COLLISION_EPSILON, 0.5)))) {
// Calculate the Y velocity after friction
Vector hackyClimbVector = new Vector(0, 0.2, 0);
Vector3D hackyClimbVector = newVector3D(0, 0.2, 0);
PredictionEngineNormal.staticVectorEndOfTick(player, hackyClimbVector);
pointThreePossibilities.add(new VectorData(hackyClimbVector, VectorData.VectorType.ZeroPointZeroThree));
@@ -290,8 +301,8 @@ public class PredictionEngine {
addExplosionToPossibilities(player, pointThreePossibilities);
if (player.packetStateData.tryingToRiptide) {
Vector riptideAddition = Riptide.getRiptideVelocity(player);
pointThreePossibilities.add(new VectorData(player.clientVelocity.clone().add(riptideAddition), new VectorData(new Vector(), VectorData.VectorType.ZeroPointZeroThree), VectorData.VectorType.Trident));
Vector3D riptideAddition = Riptide.getRiptideVelocity(player);
pointThreePossibilities.add(new VectorData(player.clientVelocity.clone().add(riptideAddition), new VectorData(newVector3D(), ZeroPointZeroThree), VectorData.VectorType.Trident));
}
possibleVelocities.addAll(applyInputsToVelocityPossibilities(player, pointThreePossibilities, speed));
@@ -312,7 +323,7 @@ public class PredictionEngine {
// Water pushing movement is affected by initial velocity due to 0.003 eating pushing in the past
if (vectorData.isKnockback() && player.baseTickWaterPushing.lengthSquared() != 0) {
if (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_13)) {
Vector vec3 = player.baseTickWaterPushing.clone();
Vector3D vec3 = player.baseTickWaterPushing.clone();
if (Math.abs(vectorData.vector.getX()) < 0.003 && Math.abs(vectorData.vector.getZ()) < 0.003 && player.baseTickWaterPushing.length() < 0.0045000000000000005D) {
vec3 = vec3.normalize().multiply(0.0045000000000000005);
}
@@ -332,7 +343,7 @@ public class PredictionEngine {
addExplosionToPossibilities(player, velocities);
if (player.packetStateData.tryingToRiptide) {
Vector riptideAddition = Riptide.getRiptideVelocity(player);
Vector3D riptideAddition = Riptide.getRiptideVelocity(player);
velocities.add(new VectorData(player.clientVelocity.clone().add(riptideAddition), VectorData.VectorType.Trident));
}
@@ -361,7 +372,7 @@ public class PredictionEngine {
private void addAttackSlowToPossibilities(GrimPlayer player, Set<VectorData> velocities) {
for (int x = 1; x <= Math.min(player.maxPlayerAttackSlow, 5); x++) {
for (VectorData data : new HashSet<>(velocities)) {
velocities.add(data.returnNewModified(data.vector.clone().multiply(new Vector(0.6, 1, 0.6)), VectorData.VectorType.AttackSlow));
velocities.add(data.returnNewModified(data.vector.clone().multiply(newVector3D(0.6, 1, 0.6)), VectorData.VectorType.AttackSlow));
}
}
}
@@ -411,52 +422,63 @@ public class PredictionEngine {
// Order priority (to avoid false positives and false flagging future predictions):
// Knockback and explosions
// 0.03 ticks
// Movement without input
// Normal movement
// First bread knockback and explosions
// Flagging groundspoof
// Flagging flip items
if (a.isExplosion())
aScore -= 5;
aScore -= 10;
if (a.isKnockback())
aScore -= 5;
aScore -= 10;
if (b.isExplosion())
bScore -= 5;
bScore -= 10;
if (b.isKnockback())
bScore -= 5;
bScore -= 10;
if (a.isFirstBreadExplosion())
aScore += 1;
aScore += 2;
if (b.isFirstBreadExplosion())
bScore += 1;
bScore += 2;
if (a.isFirstBreadKb())
aScore += 1;
aScore += 2;
if (b.isFirstBreadKb())
bScore += 1;
bScore += 2;
if (a.isFlipItem())
aScore += 3;
aScore += 6;
if (b.isFlipItem())
bScore += 3;
bScore += 6;
if (a.isZeroPointZeroThree())
aScore -= 1;
aScore -= 2;
if (b.isZeroPointZeroThree())
bScore -= 2;
if (a.isWithInput() || a.isJump())
aScore += 1;
else
aScore -= 1;
if (b.isWithInput() || b.isJump())
bScore += 1;
else
bScore -= 1;
// If the player is on the ground but the vector leads the player off the ground
if ((player.compensatedEntities.getSelf().inVehicle() ? player.clientControlledVerticalCollision : player.onGround) && a.vector.getY() >= 0)
aScore += 2;
aScore += 4;
if ((player.compensatedEntities.getSelf().inVehicle() ? player.clientControlledVerticalCollision : player.onGround) && b.vector.getY() >= 0)
bScore += 2;
bScore += 4;
if (aScore != bScore)
return Integer.compare(aScore, bScore);
@@ -464,15 +486,15 @@ public class PredictionEngine {
return Double.compare(a.vector.distanceSquared(player.actualMovement), b.vector.distanceSquared(player.actualMovement));
}
public Vector handleStartingVelocityUncertainty(GrimPlayer player, VectorData vector, Vector targetVec) {
double avgColliding = Collections.max(player.uncertaintyHandler.collidingEntities);
public Vector3D handleStartingVelocityUncertainty(GrimPlayer player, VectorData vector, Vector3D targetVec) {
double avgColliding = max(player.uncertaintyHandler.collidingEntities);
double additionHorizontal = player.uncertaintyHandler.getOffsetHorizontal(vector);
double additionVertical = player.uncertaintyHandler.getVerticalOffset(vector);
double pistonX = Collections.max(player.uncertaintyHandler.pistonX);
double pistonY = Collections.max(player.uncertaintyHandler.pistonY);
double pistonZ = Collections.max(player.uncertaintyHandler.pistonZ);
double pistonX = max(player.uncertaintyHandler.pistonX);
double pistonY = max(player.uncertaintyHandler.pistonY);
double pistonZ = max(player.uncertaintyHandler.pistonZ);
additionHorizontal += player.uncertaintyHandler.lastHorizontalOffset;
additionVertical += player.uncertaintyHandler.lastVerticalOffset;
@@ -515,13 +537,13 @@ public class PredictionEngine {
// 0.075 seems safe?
//
// Be somewhat careful as there is an antikb (for horizontal) that relies on this lenience
Vector uncertainty = new Vector(avgColliding * 0.08, additionVertical, avgColliding * 0.08);
Vector3D uncertainty = newVector3D(avgColliding * 0.08, additionVertical, avgColliding * 0.08);
Vector min = new Vector(player.uncertaintyHandler.xNegativeUncertainty - additionHorizontal, -bonusY + player.uncertaintyHandler.yNegativeUncertainty, player.uncertaintyHandler.zNegativeUncertainty - additionHorizontal);
Vector max = new Vector(player.uncertaintyHandler.xPositiveUncertainty + additionHorizontal, bonusY + player.uncertaintyHandler.yPositiveUncertainty, player.uncertaintyHandler.zPositiveUncertainty + additionHorizontal);
Vector3D min = newVector3D(player.uncertaintyHandler.xNegativeUncertainty - additionHorizontal, -bonusY + player.uncertaintyHandler.yNegativeUncertainty, player.uncertaintyHandler.zNegativeUncertainty - additionHorizontal);
Vector3D max = newVector3D(player.uncertaintyHandler.xPositiveUncertainty + additionHorizontal, bonusY + player.uncertaintyHandler.yPositiveUncertainty, player.uncertaintyHandler.zPositiveUncertainty + additionHorizontal);
Vector minVector = vector.vector.clone().add(min.subtract(uncertainty));
Vector maxVector = vector.vector.clone().add(max.add(uncertainty));
Vector3D minVector = vector.vector.clone().add(min.subtract(uncertainty));
Vector3D maxVector = vector.vector.clone().add(max.add(uncertainty));
// Handle the player landing within 0.03 movement, which resets Y velocity
if (player.uncertaintyHandler.onGroundUncertain && vector.vector.getY() < 0) {
@@ -548,7 +570,7 @@ public class PredictionEngine {
// We can't simulate the player's Y velocity, unknown number of ticks with a gravity change
// Feel free to simulate all 104857600000000000000000000 possibilities!
if (!player.pointThreeEstimator.canPredictNextVerticalMovement()) {
minVector.setY(minVector.getY() - player.compensatedEntities.getSelf().getAttributeValue(Attributes.GENERIC_GRAVITY));
minVector.setY(minVector.getY() - player.compensatedEntities.getSelf().getAttributeValue(GENERIC_GRAVITY));
}
// Hidden slime block bounces by missing idle tick and 0.03
@@ -588,9 +610,9 @@ public class PredictionEngine {
box.maxZ += sneaking.getSneakingPotentialHiddenVelocity().maxZ;
if (player.uncertaintyHandler.fireworksBox != null) {
double minXdiff = Math.min(0, player.uncertaintyHandler.fireworksBox.minX - originalVec.vector.getX());
double minYdiff = Math.min(0, player.uncertaintyHandler.fireworksBox.minY - originalVec.vector.getY());
double minZdiff = Math.min(0, player.uncertaintyHandler.fireworksBox.minZ - originalVec.vector.getZ());
double minXdiff = min(0, player.uncertaintyHandler.fireworksBox.minX - originalVec.vector.getX());
double minYdiff = min(0, player.uncertaintyHandler.fireworksBox.minY - originalVec.vector.getY());
double minZdiff = min(0, player.uncertaintyHandler.fireworksBox.minZ - originalVec.vector.getZ());
double maxXdiff = Math.max(0, player.uncertaintyHandler.fireworksBox.maxX - originalVec.vector.getX());
double maxYdiff = Math.max(0, player.uncertaintyHandler.fireworksBox.maxY - originalVec.vector.getY());
double maxZdiff = Math.max(0, player.uncertaintyHandler.fireworksBox.maxZ - originalVec.vector.getZ());
@@ -646,7 +668,7 @@ public class PredictionEngine {
//
// Or the player is switching in and out of controlling a vehicle, in which friction messes it up
//
if (player.uncertaintyHandler.lastVehicleSwitch.hasOccurredSince(0) || player.uncertaintyHandler.lastHardCollidingLerpingEntity.hasOccurredSince(3) || (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_13) && vector.vector.getY() > 0 && vector.isZeroPointZeroThree() && !Collisions.isEmpty(player, GetBoundingBox.getBoundingBoxFromPosAndSize(player, player.lastX, vector.vector.getY() + player.lastY + 0.6, player.lastZ, 0.6f, 1.26f)))) {
if (player.uncertaintyHandler.lastVehicleSwitch.hasOccurredSince(0) || player.uncertaintyHandler.lastHardCollidingLerpingEntity.hasOccurredSince(3) || (player.getClientVersion().isNewerThanOrEquals(V_1_13) && vector.vector.getY() > 0 && vector.isZeroPointZeroThree() && !isEmpty(player, getBoundingBoxFromPosAndSize(player, player.lastX, vector.vector.getY() + player.lastY + 0.6, player.lastZ, 0.6f, 1.26f)))) {
box.expandToAbsoluteCoordinates(0, 0, 0);
}
@@ -658,9 +680,9 @@ public class PredictionEngine {
if (player.wasTouchingWater) trueFriction = 0.96;
double maxY = Math.max(box.maxY, box.maxY + ((box.maxY - player.gravity) * 0.91));
double minY = Math.min(box.minY, box.minY + ((box.minY - player.gravity) * 0.91));
double minX = Math.min(box.minX, box.minX + (-player.speed * trueFriction));
double minZ = Math.min(box.minZ, box.minZ + (-player.speed * trueFriction));
double minY = min(box.minY, box.minY + ((box.minY - player.gravity) * 0.91));
double minX = min(box.minX, box.minX + (-player.speed * trueFriction));
double minZ = min(box.minZ, box.minZ + (-player.speed * trueFriction));
double maxX = Math.max(box.maxX, box.maxX + (player.speed * trueFriction));
double maxZ = Math.max(box.maxZ, box.maxZ + (player.speed * trueFriction));
@@ -676,15 +698,15 @@ public class PredictionEngine {
maxVector = box.max();
if (pistonX != 0) {
minVector.setX(Math.min(minVector.getX() - pistonX, pistonX));
minVector.setX(min(minVector.getX() - pistonX, pistonX));
maxVector.setX(Math.max(maxVector.getX() + pistonX, pistonX));
}
if (pistonY != 0) {
minVector.setY(Math.min(minVector.getY() - pistonY, pistonY));
minVector.setY(min(minVector.getY() - pistonY, pistonY));
maxVector.setY(Math.max(maxVector.getY() + pistonY, pistonY));
}
if (pistonZ != 0) {
minVector.setZ(Math.min(minVector.getZ() - pistonZ, pistonZ));
minVector.setZ(min(minVector.getZ() - pistonZ, pistonZ));
maxVector.setZ(Math.max(maxVector.getZ() + pistonZ, pistonZ));
}
return VectorUtils.cutBoxToVector(targetVec, minVector, maxVector);
@@ -714,7 +736,7 @@ public class PredictionEngine {
if (loopSlowed == 1 && !possibleLastTickOutput.isZeroPointZeroThree()) continue;
for (int x = -1; x <= 1; x++) {
for (int z = zMin; z <= 1; z++) {
VectorData result = new VectorData(possibleLastTickOutput.vector.clone().add(getMovementResultFromInput(player, transformInputsToVector(player, new Vector(x, 0, z)), speed, player.xRot)), possibleLastTickOutput, VectorData.VectorType.InputResult);
VectorData result = new VectorData.MoveVectorData(possibleLastTickOutput.vector.clone().add(getMovementResultFromInput(player, transformInputsToVector(player, newVector3D(x, 0, z)), speed, player.xRot)), possibleLastTickOutput, VectorData.VectorType.InputResult, x, z);
result = result.returnNewModified(result.vector.clone().multiply(player.stuckSpeedMultiplier), VectorData.VectorType.StuckMultiplier);
result = result.returnNewModified(handleOnClimbable(result.vector.clone(), player), VectorData.VectorType.Climbable);
// Signal that we need to flip sneaking bounding box
@@ -781,21 +803,21 @@ public class PredictionEngine {
// This is just the vanilla equation, which accepts invalid inputs greater than 1
// We need it because of collision support when a player is using speed
public Vector getMovementResultFromInput(GrimPlayer player, Vector inputVector, float f, float f2) {
public Vector3D getMovementResultFromInput(GrimPlayer player, Vector3D inputVector, float f, float f2) {
float f3 = player.trigHandler.sin(f2 * 0.017453292f);
float f4 = player.trigHandler.cos(f2 * 0.017453292f);
double xResult = inputVector.getX() * f4 - inputVector.getZ() * f3;
double zResult = inputVector.getZ() * f4 + inputVector.getX() * f3;
return new Vector(xResult * f, 0, zResult * f);
return newVector3D(xResult * f, 0, zResult * f);
}
public Vector handleOnClimbable(Vector vector, GrimPlayer player) {
public Vector3D handleOnClimbable(Vector3D vector, GrimPlayer player) {
return vector;
}
public void doJump(GrimPlayer player, Vector vector) {
public void doJump(GrimPlayer player, Vector3D vector) {
if (!player.lastOnGround || player.onGround)
return;
@@ -3,28 +3,35 @@ package ac.grim.grimac.predictionengine.predictions;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.data.VectorData;
import ac.grim.grimac.utils.nmsutil.ReachUtils;
import ac.grim.grimac.utils.vector.Vector3D;
import com.github.retrooper.packetevents.protocol.attribute.Attributes;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import org.bukkit.util.Vector;
import ac.grim.grimac.utils.vector.Vector3D;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
import static com.github.retrooper.packetevents.protocol.attribute.Attributes.GENERIC_GRAVITY;
import static com.github.retrooper.packetevents.protocol.player.ClientVersion.V_1_18_2;
import static com.github.retrooper.packetevents.protocol.player.ClientVersion.V_1_20_5;
import static java.lang.Math.*;
public class PredictionEngineElytra extends PredictionEngine {
// Inputs have no effect on movement
@Override
public List<VectorData> applyInputsToVelocityPossibilities(GrimPlayer player, Set<VectorData> possibleVectors, float speed) {
List<VectorData> results = new ArrayList<>();
Vector currentLook = ReachUtils.getLook(player, player.xRot, player.yRot);
Vector3D currentLook = ReachUtils.getLook(player, player.xRot, player.yRot);
for (VectorData data : possibleVectors) {
Vector elytraResult = getElytraMovement(player, data.vector.clone(), currentLook).multiply(player.stuckSpeedMultiplier).multiply(new Vector(0.99F, 0.98F, 0.99F));
Vector3D elytraResult = getElytraMovement(player, data.vector.clone(), currentLook).multiply(player.stuckSpeedMultiplier).multiply(newVector3D(0.99F, 0.98F, 0.99F));
results.add(data.returnNewModified(elytraResult, VectorData.VectorType.InputResult));
// We must bruteforce Optifine ShitMath
player.trigHandler.toggleShitMath();
elytraResult = getElytraMovement(player, data.vector.clone(), ReachUtils.getLook(player, player.xRot, player.yRot)).multiply(player.stuckSpeedMultiplier).multiply(new Vector(0.99F, 0.98F, 0.99F));
elytraResult = getElytraMovement(player, data.vector.clone(), ReachUtils.getLook(player, player.xRot, player.yRot)).multiply(player.stuckSpeedMultiplier).multiply(newVector3D(0.99F, 0.98F, 0.99F));
player.trigHandler.toggleShitMath();
results.add(data.returnNewModified(elytraResult, VectorData.VectorType.InputResult));
}
@@ -32,42 +39,42 @@ public class PredictionEngineElytra extends PredictionEngine {
return results;
}
public static Vector getElytraMovement(GrimPlayer player, Vector vector, Vector lookVector) {
public static Vector3D getElytraMovement(GrimPlayer player, Vector3D vector, Vector3D lookVector) {
float yRotRadians = player.yRot * 0.017453292F;
double horizontalSqrt = Math.sqrt(lookVector.getX() * lookVector.getX() + lookVector.getZ() * lookVector.getZ());
double horizontalSqrt = sqrt(lookVector.getX() * lookVector.getX() + lookVector.getZ() * lookVector.getZ());
double horizontalLength = vector.clone().setY(0).length();
double length = lookVector.length();
// Mojang changed from using their math to using regular java math in 1.18.2 elytra movement
double vertCosRotation = player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_18_2) ? Math.cos(yRotRadians) : player.trigHandler.cos(yRotRadians);
vertCosRotation = (float) (vertCosRotation * vertCosRotation * Math.min(1.0D, length / 0.4D));
double vertCosRotation = player.getClientVersion().isNewerThanOrEquals(V_1_18_2) ? cos(yRotRadians) : player.trigHandler.cos(yRotRadians);
vertCosRotation = (float) (vertCosRotation * vertCosRotation * min(1.0D, length / 0.4D));
// So we actually use the player's actual movement to get the gravity/slow falling status
// However, this is wrong with elytra movement because players can control vertical movement after gravity is calculated
// Yeah, slow falling needs a refactor in grim.
double recalculatedGravity = player.compensatedEntities.getSelf().getAttributeValue(Attributes.GENERIC_GRAVITY);
double recalculatedGravity = player.compensatedEntities.getSelf().getAttributeValue(GENERIC_GRAVITY);
if (player.clientVelocity.getY() <= 0 && player.compensatedEntities.getSlowFallingAmplifier().isPresent()) {
recalculatedGravity = player.getClientVersion().isOlderThan(ClientVersion.V_1_20_5) ? 0.01 : Math.min(recalculatedGravity, 0.01);
recalculatedGravity = player.getClientVersion().isOlderThan(V_1_20_5) ? 0.01 : min(recalculatedGravity, 0.01);
}
vector.add(new Vector(0.0D, recalculatedGravity * (-1.0D + vertCosRotation * 0.75D), 0.0D));
vector.add(newVector3D(0.0D, recalculatedGravity * (-1.0D + vertCosRotation * 0.75D), 0.0D));
double d5;
// Handle slowing the player down when falling
if (vector.getY() < 0.0D && horizontalSqrt > 0.0D) {
d5 = vector.getY() * -0.1D * vertCosRotation;
vector.add(new Vector(lookVector.getX() * d5 / horizontalSqrt, d5, lookVector.getZ() * d5 / horizontalSqrt));
vector.add(newVector3D(lookVector.getX() * d5 / horizontalSqrt, d5, lookVector.getZ() * d5 / horizontalSqrt));
}
// Handle accelerating the player when they are looking down
if (yRotRadians < 0.0F && horizontalSqrt > 0.0D) {
d5 = horizontalLength * (double) (-player.trigHandler.sin(yRotRadians)) * 0.04D;
vector.add(new Vector(-lookVector.getX() * d5 / horizontalSqrt, d5 * 3.2D, -lookVector.getZ() * d5 / horizontalSqrt));
vector.add(newVector3D(-lookVector.getX() * d5 / horizontalSqrt, d5 * 3.2D, -lookVector.getZ() * d5 / horizontalSqrt));
}
// Handle accelerating the player sideways
if (horizontalSqrt > 0) {
vector.add(new Vector((lookVector.getX() / horizontalSqrt * horizontalLength - vector.getX()) * 0.1D, 0.0D, (lookVector.getZ() / horizontalSqrt * horizontalLength - vector.getZ()) * 0.1D));
vector.add(newVector3D((lookVector.getX() / horizontalSqrt * horizontalLength - vector.getX()) * 0.1D, 0.0D, (lookVector.getZ() / horizontalSqrt * horizontalLength - vector.getZ()) * 0.1D));
}
return vector;
@@ -3,11 +3,13 @@ package ac.grim.grimac.predictionengine.predictions;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.data.VectorData;
import ac.grim.grimac.utils.math.GrimMath;
import org.bukkit.util.Vector;
import ac.grim.grimac.utils.vector.Vector3D;
import java.util.HashSet;
import java.util.Set;
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
public class PredictionEngineLava extends PredictionEngine {
@Override
public void addJumpsToPossibilities(GrimPlayer player, Set<VectorData> existingVelocities) {
@@ -16,11 +18,11 @@ public class PredictionEngineLava extends PredictionEngine {
double extraVelFromVertTickSkipUpwards = GrimMath.clamp(player.actualMovement.getY(), vector.vector.clone().getY(), vector.vector.clone().getY() + 0.05f);
existingVelocities.add(new VectorData(vector.vector.clone().setY(extraVelFromVertTickSkipUpwards), vector, VectorData.VectorType.Jump));
} else {
existingVelocities.add(new VectorData(vector.vector.clone().add(new Vector(0, 0.04f, 0)), vector, VectorData.VectorType.Jump));
existingVelocities.add(new VectorData(vector.vector.clone().add(newVector3D(0, 0.04f, 0)), vector, VectorData.VectorType.Jump));
}
if (player.slightlyTouchingLava && player.lastOnGround && !player.onGround) {
Vector withJump = vector.vector.clone();
Vector3D withJump = vector.vector.clone();
super.doJump(player, withJump);
existingVelocities.add(new VectorData(withJump, vector, VectorData.VectorType.Jump));
}
@@ -6,20 +6,26 @@ import ac.grim.grimac.utils.data.VectorData;
import ac.grim.grimac.utils.math.GrimMath;
import ac.grim.grimac.utils.nmsutil.Collisions;
import ac.grim.grimac.utils.nmsutil.JumpPower;
import ac.grim.grimac.utils.vector.Vector3D;
import com.github.retrooper.packetevents.protocol.item.ItemStack;
import com.github.retrooper.packetevents.protocol.item.type.ItemTypes;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.potion.PotionTypes;
import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
import org.bukkit.util.Vector;
import ac.grim.grimac.utils.vector.Vector3D;
import java.util.HashSet;
import java.util.OptionalInt;
import java.util.Set;
import static ac.grim.grimac.utils.math.GrimMath.clamp;
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
import static com.github.retrooper.packetevents.protocol.world.states.type.StateTypes.SCAFFOLDING;
import static java.lang.Math.max;
public class PredictionEngineNormal extends PredictionEngine {
public static void staticVectorEndOfTick(GrimPlayer player, Vector vector) {
public static void staticVectorEndOfTick(GrimPlayer player, Vector3D vector) {
double adjustedY = vector.getY();
final OptionalInt levitation = player.compensatedEntities.getPotionLevelForPlayer(PotionTypes.LEVITATION);
if (levitation.isPresent()) {
@@ -38,7 +44,7 @@ public class PredictionEngineNormal extends PredictionEngine {
@Override
public void addJumpsToPossibilities(GrimPlayer player, Set<VectorData> existingVelocities) {
for (VectorData vector : new HashSet<>(existingVelocities)) {
Vector jump = vector.vector.clone();
Vector3D jump = vector.vector.clone();
if (!player.isFlying) {
// Negative jump boost does not allow the player to leave the ground
@@ -47,14 +53,14 @@ public class PredictionEngineNormal extends PredictionEngine {
// And 0.03 didn't affect onGround status
// The player cannot jump
final OptionalInt jumpBoost = player.compensatedEntities.getPotionLevelForPlayer(PotionTypes.JUMP_BOOST);
if (((jumpBoost.isEmpty() || jumpBoost.getAsInt() >= 0) && player.onGround) || !player.lastOnGround)
if (((!jumpBoost.isPresent() || jumpBoost.getAsInt() >= 0) && player.onGround) || !player.lastOnGround)
return;
JumpPower.jumpFromGround(player, jump);
} else {
jump.add(new Vector(0, player.flySpeed * 3, 0));
jump.add(newVector3D(0, player.flySpeed * 3, 0));
if (!player.wasFlying) {
Vector edgeCaseJump = jump.clone();
Vector3D edgeCaseJump = jump.clone();
JumpPower.jumpFromGround(player, edgeCaseJump);
existingVelocities.add(vector.returnNewModified(edgeCaseJump, VectorData.VectorType.Jump));
}
@@ -82,7 +88,7 @@ public class PredictionEngineNormal extends PredictionEngine {
if (player.lastWasClimbing == 0 && (player.pointThreeEstimator.isNearClimbable() || player.isClimbing) && (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_14)
|| !Collisions.isEmpty(player, player.boundingBox.copy().expand(
player.clientVelocity.getX(), 0, player.clientVelocity.getZ()).expand(0.5, -SimpleCollisionBox.COLLISION_EPSILON, 0.5))) || walkingOnPowderSnow) {
Vector ladderVelocity = player.clientVelocity.clone().setY(0.2);
Vector3D ladderVelocity = player.clientVelocity.clone().setY(0.2);
staticVectorEndOfTick(player, ladderVelocity);
player.lastWasClimbing = ladderVelocity.getY();
}
@@ -93,17 +99,17 @@ public class PredictionEngineNormal extends PredictionEngine {
}
@Override
public Vector handleOnClimbable(Vector vector, GrimPlayer player) {
public Vector3D handleOnClimbable(Vector3D vector, GrimPlayer player) {
if (player.isClimbing) {
// Reset fall distance when climbing
player.fallDistance = 0;
vector.setX(GrimMath.clamp(vector.getX(), -0.15F, 0.15F));
vector.setZ(GrimMath.clamp(vector.getZ(), -0.15F, 0.15F));
vector.setY(Math.max(vector.getY(), -0.15F));
vector.setX(clamp(vector.getX(), -0.15F, 0.15F));
vector.setZ(clamp(vector.getZ(), -0.15F, 0.15F));
vector.setY(max(vector.getY(), -0.15F));
// Yes, this uses shifting not crouching
if (vector.getY() < 0.0 && !(player.compensatedWorld.getStateTypeAt(player.lastX, player.lastY, player.lastZ) == StateTypes.SCAFFOLDING) && player.isSneaking && !player.isFlying) {
if (vector.getY() < 0.0 && !(player.compensatedWorld.getStateTypeAt(player.lastX, player.lastY, player.lastZ) == SCAFFOLDING) && player.isSneaking && !player.isFlying) {
vector.setY(0.0);
}
}
@@ -9,11 +9,15 @@ import ac.grim.grimac.utils.nmsutil.Collisions;
import ac.grim.grimac.utils.nmsutil.FluidFallingAdjustedMovement;
import ac.grim.grimac.utils.nmsutil.ReachUtils;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import org.bukkit.util.Vector;
import ac.grim.grimac.utils.vector.Vector3D;
import java.util.HashSet;
import java.util.Set;
import static ac.grim.grimac.utils.data.VectorData.VectorType;
import static ac.grim.grimac.utils.data.VectorData.VectorType.SwimmingSpace;
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
public class PredictionEngineWater extends PredictionEngine {
boolean isFalling;
double playerGravity;
@@ -21,9 +25,9 @@ public class PredictionEngineWater extends PredictionEngine {
float swimmingFriction;
double lastY;
public static void staticVectorEndOfTick(GrimPlayer player, Vector vector, float swimmingFriction, double playerGravity, boolean isFalling) {
vector.multiply(new Vector(swimmingFriction, 0.8F, swimmingFriction));
Vector fluidVector = FluidFallingAdjustedMovement.getFluidFallingAdjustedMovement(player, playerGravity, isFalling, vector);
public static void staticVectorEndOfTick(GrimPlayer player, Vector3D vector, float swimmingFriction, double playerGravity, boolean isFalling) {
vector.multiply(newVector3D(swimmingFriction, 0.8F, swimmingFriction));
Vector3D fluidVector = FluidFallingAdjustedMovement.getFluidFallingAdjustedMovement(player, playerGravity, isFalling, vector);
vector.setX(fluidVector.getX());
vector.setY(fluidVector.getY());
vector.setZ(fluidVector.getZ());
@@ -47,7 +51,7 @@ public class PredictionEngineWater extends PredictionEngine {
double scalar = lookYAmount < -0.2 ? 0.085 : 0.06;
// The player can always press jump and activate this
swimmingVelocities.add(vector.returnNewModified(new Vector(vector.vector.getX(), vector.vector.getY() + ((lookYAmount - vector.vector.getY()) * scalar), vector.vector.getZ()), VectorData.VectorType.SwimmingSpace));
swimmingVelocities.add(vector.returnNewModified(newVector3D(vector.vector.getX(), vector.vector.getY() + ((lookYAmount - vector.vector.getY()) * scalar), vector.vector.getZ()), SwimmingSpace));
// This scenario will occur if the player does not press jump and the other conditions are met
// Theoretically we should check this BEFORE allowing no look, but there isn't a cheat that takes advantage of this yet
@@ -80,11 +84,11 @@ public class PredictionEngineWater extends PredictionEngine {
double extraVelFromVertTickSkipUpwards = GrimMath.clamp(player.actualMovement.getY(), vector.vector.clone().getY(), vector.vector.clone().getY() + 0.05f);
existingVelocities.add(new VectorData(vector.vector.clone().setY(extraVelFromVertTickSkipUpwards), vector, VectorData.VectorType.Jump));
} else {
existingVelocities.add(new VectorData(vector.vector.clone().add(new Vector(0, 0.04f, 0)), vector, VectorData.VectorType.Jump));
existingVelocities.add(new VectorData(vector.vector.clone().add(newVector3D(0, 0.04f, 0)), vector, VectorData.VectorType.Jump));
}
if (player.slightlyTouchingWater && player.lastOnGround && !player.onGround) {
Vector withJump = vector.vector.clone();
Vector3D withJump = vector.vector.clone();
super.doJump(player, withJump);
existingVelocities.add(new VectorData(withJump, vector, VectorData.VectorType.Jump));
}
@@ -2,11 +2,15 @@ package ac.grim.grimac.predictionengine.predictions;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.data.VectorData;
import org.bukkit.util.Vector;
import ac.grim.grimac.utils.vector.Vector3D;
import ac.grim.grimac.utils.vector.Vector3D;
import java.util.HashSet;
import java.util.Set;
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
import static java.lang.Math.sqrt;
public class PredictionEngineWaterLegacy extends PredictionEngine {
double playerGravity;
float swimmingSpeed;
@@ -23,11 +27,11 @@ public class PredictionEngineWaterLegacy extends PredictionEngine {
// This is just the vanilla equation for legacy water movement
@Override
public Vector getMovementResultFromInput(GrimPlayer player, Vector inputVector, float f, float f2) {
public Vector3D getMovementResultFromInput(GrimPlayer player, Vector3D inputVector, float f, float f2) {
float lengthSquared = (float) inputVector.lengthSquared();
if (lengthSquared >= 1.0E-4F) {
lengthSquared = (float) Math.sqrt(lengthSquared);
lengthSquared = (float) sqrt(lengthSquared);
if (lengthSquared < 1.0F) {
lengthSquared = 1.0F;
@@ -38,21 +42,20 @@ public class PredictionEngineWaterLegacy extends PredictionEngine {
float sinResult = player.trigHandler.sin(player.xRot * 0.017453292F);
float cosResult = player.trigHandler.cos(player.xRot * 0.017453292F);
return new Vector(inputVector.getX() * cosResult - inputVector.getZ() * sinResult,
inputVector.getY(), inputVector.getZ() * cosResult + inputVector.getX() * sinResult);
return newVector3D(inputVector.getX() * cosResult - inputVector.getZ() * sinResult, inputVector.getY(), inputVector.getZ() * cosResult + inputVector.getX() * sinResult);
}
return new Vector();
return newVector3D();
}
@Override
public void addJumpsToPossibilities(GrimPlayer player, Set<VectorData> existingVelocities) {
for (VectorData vector : new HashSet<>(existingVelocities)) {
existingVelocities.add(new VectorData(vector.vector.clone().add(new Vector(0, 0.04f, 0)), vector, VectorData.VectorType.Jump));
existingVelocities.add(new VectorData(vector.vector.clone().add(newVector3D(0, 0.04f, 0)), vector, VectorData.VectorType.Jump));
if (player.skippedTickInActualMovement) {
existingVelocities.add(new VectorData(vector.vector.clone().add(new Vector(0, 0.02f, 0)), vector, VectorData.VectorType.Jump));
existingVelocities.add(new VectorData(vector.vector.clone().add(newVector3D(0, 0.02f, 0)), vector, VectorData.VectorType.Jump));
}
}
}
@@ -62,7 +65,7 @@ public class PredictionEngineWaterLegacy extends PredictionEngine {
super.endOfTick(player, playerGravity);
for (VectorData vector : player.getPossibleVelocitiesMinusKnockback()) {
vector.vector.multiply(new Vector(swimmingFriction, 0.8F, swimmingFriction));
vector.vector.multiply(newVector3D(swimmingFriction, 0.8F, swimmingFriction));
// Gravity
vector.vector.setY(vector.vector.getY() - 0.02D);
@@ -13,12 +13,15 @@ import ac.grim.grimac.utils.nmsutil.GetBoundingBox;
import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState;
import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
import org.bukkit.util.Vector;
import ac.grim.grimac.utils.vector.Vector3D;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
import static java.lang.Math.PI;
public class BoatPredictionEngine extends PredictionEngine {
public BoatPredictionEngine(GrimPlayer player) {
player.uncertaintyHandler.collidingEntities.add(0); // We don't do collisions like living entities
@@ -147,7 +150,7 @@ public class BoatPredictionEngine extends PredictionEngine {
// so if a player tries to move in both directions, a packet will
// show that the player is staying, but the boat will move anyway
if (player.vehicleData.vehicleForward == 0) {
Vector vector = data.vector.clone();
Vector3D vector = data.vector.clone();
controlBoat(player, vector, true);
vector.multiply(player.stuckSpeedMultiplier);
vectors.add(data.returnNewModified(vector, VectorData.VectorType.InputResult));
@@ -184,7 +187,7 @@ public class BoatPredictionEngine extends PredictionEngine {
return false;
}
private void floatBoat(GrimPlayer player, Vector vector) {
private void floatBoat(GrimPlayer player, Vector3D vector) {
double d1 = player.hasGravity ? -0.04f : 0;
double d2 = 0.0D;
float invFriction = 0.05F;
@@ -194,7 +197,7 @@ public class BoatPredictionEngine extends PredictionEngine {
player.lastY = getWaterLevelAbove(player) - 0.5625F + 0.101D;
player.boundingBox = GetBoundingBox.getCollisionBoxForPlayer(player, player.lastX, player.lastY, player.lastZ);
player.actualMovement = new Vector(player.x - player.lastX, player.y - player.lastY, player.z - player.lastZ);
player.actualMovement = newVector3D(player.x - player.lastX, player.y - player.lastY, player.z - player.lastZ);
vector.setY(0);
player.vehicleData.lastYd = 0.0D;
@@ -260,7 +263,7 @@ public class BoatPredictionEngine extends PredictionEngine {
return (float) (l + 1);
}
private void controlBoat(GrimPlayer player, Vector vector, boolean intermediate) {
private void controlBoat(GrimPlayer player, Vector3D vector, boolean intermediate) {
float f = 0.0F;
if (player.vehicleData.vehicleHorizontal != 0 && (!intermediate && player.vehicleData.vehicleForward == 0)) {
f += 0.005F;
@@ -275,6 +278,6 @@ public class BoatPredictionEngine extends PredictionEngine {
f -= 0.005F;
}
vector.add(new Vector(player.trigHandler.sin(-player.xRot * ((float) Math.PI / 180F)) * f, 0, (double) (player.trigHandler.cos(player.xRot * ((float) Math.PI / 180F)) * f)));
vector.add(newVector3D(player.trigHandler.sin(-player.xRot * ((float) PI / 180F)) * f, 0, (double) (player.trigHandler.cos(player.xRot * ((float) PI / 180F)) * f)));
}
}
@@ -3,16 +3,16 @@ package ac.grim.grimac.predictionengine.predictions.rideable;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.predictionengine.predictions.PredictionEngineLava;
import ac.grim.grimac.utils.data.VectorData;
import org.bukkit.util.Vector;
import ac.grim.grimac.utils.vector.Vector3D;
import java.util.List;
import java.util.Set;
public class PredictionEngineRideableLava extends PredictionEngineLava {
Vector movementVector;
Vector3D movementVector;
public PredictionEngineRideableLava(Vector movementVector) {
public PredictionEngineRideableLava(Vector3D movementVector) {
this.movementVector = movementVector;
}
@@ -3,16 +3,16 @@ package ac.grim.grimac.predictionengine.predictions.rideable;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.predictionengine.predictions.PredictionEngineNormal;
import ac.grim.grimac.utils.data.VectorData;
import org.bukkit.util.Vector;
import ac.grim.grimac.utils.vector.Vector3D;
import java.util.List;
import java.util.Set;
public class PredictionEngineRideableNormal extends PredictionEngineNormal {
Vector movementVector;
Vector3D movementVector;
public PredictionEngineRideableNormal(Vector movementVector) {
public PredictionEngineRideableNormal(Vector3D movementVector) {
this.movementVector = movementVector;
}
@@ -8,16 +8,20 @@ import ac.grim.grimac.utils.data.packetentity.PacketEntityHorse;
import ac.grim.grimac.utils.nmsutil.JumpPower;
import com.github.retrooper.packetevents.protocol.attribute.Attributes;
import com.github.retrooper.packetevents.protocol.potion.PotionTypes;
import org.bukkit.util.Vector;
import ac.grim.grimac.utils.vector.Vector3D;
import java.util.ArrayList;
import java.util.List;
import java.util.OptionalInt;
import java.util.Set;
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
public class PredictionEngineRideableUtils {
public static Set<VectorData> handleJumps(GrimPlayer player, Set<VectorData> possibleVectors) {
if (!(player.compensatedEntities.getSelf().getRiding() instanceof PacketEntityHorse horse)) return possibleVectors;
if (!(player.compensatedEntities.getSelf().getRiding() instanceof PacketEntityHorse)) return possibleVectors;
PacketEntityHorse horse = (PacketEntityHorse) player.compensatedEntities.getSelf().getRiding();
// Setup player inputs
float f = player.vehicleData.vehicleHorizontal * 0.5F;
@@ -55,7 +59,7 @@ public class PredictionEngineRideableUtils {
for (VectorData vectorData : possibleVectors) {
vectorData.vector.setY(d1);
if (f1 > 0.0F) {
vectorData.vector.add(new Vector(-0.4F * f2 * player.vehicleData.horseJump, 0.0D, 0.4F * f3 * player.vehicleData.horseJump));
vectorData.vector.add(newVector3D(-0.4F * f2 * player.vehicleData.horseJump, 0.0D, 0.4F * f3 * player.vehicleData.horseJump));
}
}
@@ -71,7 +75,7 @@ public class PredictionEngineRideableUtils {
return possibleVectors;
}
public static List<VectorData> applyInputsToVelocityPossibilities(Vector movementVector, GrimPlayer player, Set<VectorData> possibleVectors, float speed) {
public static List<VectorData> applyInputsToVelocityPossibilities(Vector3D movementVector, GrimPlayer player, Set<VectorData> possibleVectors, float speed) {
List<VectorData> returnVectors = new ArrayList<>();
for (VectorData possibleLastTickOutput : possibleVectors) {
@@ -3,16 +3,16 @@ package ac.grim.grimac.predictionengine.predictions.rideable;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.predictionengine.predictions.PredictionEngineWater;
import ac.grim.grimac.utils.data.VectorData;
import org.bukkit.util.Vector;
import ac.grim.grimac.utils.vector.Vector3D;
import java.util.List;
import java.util.Set;
public class PredictionEngineRideableWater extends PredictionEngineWater {
Vector movementVector;
Vector3D movementVector;
public PredictionEngineRideableWater(Vector movementVector) {
public PredictionEngineRideableWater(Vector3D movementVector) {
this.movementVector = movementVector;
}
@@ -3,15 +3,15 @@ package ac.grim.grimac.predictionengine.predictions.rideable;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.predictionengine.predictions.PredictionEngineWaterLegacy;
import ac.grim.grimac.utils.data.VectorData;
import org.bukkit.util.Vector;
import ac.grim.grimac.utils.vector.Vector3D;
import java.util.List;
import java.util.Set;
public class PredictionEngineRideableWaterLegacy extends PredictionEngineWaterLegacy {
Vector movementVector;
Vector3D movementVector;
public PredictionEngineRideableWaterLegacy(Vector movementVector) {
public PredictionEngineRideableWaterLegacy(Vector3D movementVector) {
this.movementVector = movementVector;
}
@@ -7,6 +7,7 @@ import ac.grim.grimac.utils.collisions.AxisUtil;
import ac.grim.grimac.utils.collisions.CollisionData;
import ac.grim.grimac.utils.collisions.blocks.DoorHandler;
import ac.grim.grimac.utils.collisions.datatypes.CollisionBox;
import ac.grim.grimac.utils.collisions.datatypes.ComplexCollisionBox;
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
import ac.grim.grimac.utils.data.HitData;
import ac.grim.grimac.utils.data.packetentity.PacketEntity;
@@ -16,6 +17,7 @@ import ac.grim.grimac.utils.nmsutil.BoundingBoxSize;
import ac.grim.grimac.utils.nmsutil.GetBoundingBox;
import ac.grim.grimac.utils.nmsutil.Materials;
import ac.grim.grimac.utils.nmsutil.ReachUtils;
import ac.grim.grimac.utils.vector.Vector3D;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.attribute.Attributes;
@@ -34,19 +36,23 @@ import com.github.retrooper.packetevents.util.Vector3f;
import com.github.retrooper.packetevents.util.Vector3i;
import lombok.Getter;
import lombok.Setter;
import org.bukkit.util.Vector;
import ac.grim.grimac.utils.vector.Vector3D;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import static ac.grim.grimac.utils.nmsutil.ReachUtils.calculateIntercept;
import static ac.grim.grimac.utils.nmsutil.ReachUtils.getLook;
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
import static com.github.retrooper.packetevents.protocol.attribute.Attributes.PLAYER_BLOCK_INTERACTION_RANGE;
public class BlockPlace {
protected static final BlockFace[] UPDATE_SHAPE_ORDER = new BlockFace[]{BlockFace.WEST, BlockFace.EAST, BlockFace.NORTH, BlockFace.SOUTH, BlockFace.DOWN, BlockFace.UP};
private static final BlockFace[] BY_2D = new BlockFace[]{BlockFace.SOUTH, BlockFace.WEST, BlockFace.NORTH, BlockFace.EAST};
static final BlockFace[] BY_3D = new BlockFace[]{BlockFace.DOWN, BlockFace.UP, BlockFace.NORTH, BlockFace.SOUTH, BlockFace.WEST, BlockFace.EAST};
@Setter
Vector3i blockPosition;
public Vector3i blockPosition;
@Getter
InteractionHand hand;
@Getter
@@ -72,6 +78,9 @@ public class BlockPlace {
@Getter private final boolean block;
// Allocated once instead of in functions to reduce new[] calls that need to be made. Since per-instance BlockPlace is always dealt with on the same thread we can use 1 buffer array
private final SimpleCollisionBox[] collisions = new SimpleCollisionBox[ComplexCollisionBox.DEFAULT_MAX_COLLISION_BOX_SIZE];
public BlockPlace(GrimPlayer player, InteractionHand hand, Vector3i blockPosition, int faceId, BlockFace face, ItemStack itemStack, HitData hitData) {
this.player = player;
this.hand = hand;
@@ -200,12 +209,12 @@ public class BlockPlace {
if (BlockTags.LEAVES.contains(data.getType())) return false;
if (BlockTags.FENCE_GATES.contains(data.getType())) return false;
List<SimpleCollisionBox> collisions = new ArrayList<>();
box.downCast(collisions);
int size = box.downCast(collisions);
AxisSelect axis = AxisUtil.getAxis(facing.getOppositeFace());
for (SimpleCollisionBox simpleBox : collisions) {
for (int i = 0; i < size; i++) {
SimpleCollisionBox simpleBox = collisions[i];
simpleBox = axis.modify(simpleBox);
if (simpleBox.minX <= 7 / 16d && simpleBox.maxX >= 7 / 16d
&& simpleBox.minY <= 0 && simpleBox.maxY >= 10 / 16d
@@ -225,12 +234,12 @@ public class BlockPlace {
if (isFullFace(facing)) return true;
if (BlockTags.LEAVES.contains(data.getType())) return false;
List<SimpleCollisionBox> collisions = new ArrayList<>();
box.downCast(collisions);
int size = box.downCast(collisions);
AxisSelect axis = AxisUtil.getAxis(facing.getOppositeFace());
for (SimpleCollisionBox simpleBox : collisions) {
for (int i = 0; i < size; i++) {
SimpleCollisionBox simpleBox = collisions[i];
simpleBox = axis.modify(simpleBox);
if (simpleBox.minX <= 2 / 16d && simpleBox.maxX >= 14 / 16d
&& simpleBox.minY <= 0 && simpleBox.maxY >= 1
@@ -289,10 +298,10 @@ public class BlockPlace {
}
}
List<SimpleCollisionBox> collisions = new ArrayList<>();
box.downCast(collisions);
int size = box.downCast(collisions);
for (SimpleCollisionBox simpleBox : collisions) {
for (int i = 0; i < size; i++) {
SimpleCollisionBox simpleBox = collisions[i];
if (axis.modify(simpleBox).isFullBlockNoCache()) return true;
}
@@ -318,12 +327,12 @@ public class BlockPlace {
if (isFullFace(facing)) return true;
if (BlockTags.LEAVES.contains(data.getType())) return false;
List<SimpleCollisionBox> collisions = new ArrayList<>();
box.downCast(collisions);
int size = box.downCast(collisions);
AxisSelect axis = AxisUtil.getAxis(facing.getOppositeFace());
for (SimpleCollisionBox simpleBox : collisions) {
for (int i = 0; i < size; i++) {
SimpleCollisionBox simpleBox = collisions[i];
simpleBox = axis.modify(simpleBox);
// If all sides to the box have width, there is collision.
switch (facing) {
@@ -537,14 +546,21 @@ public class BlockPlace {
}
public Vector3i getNormalBlockFace() {
return switch (face) {
case DOWN -> new Vector3i(0, -1, 0);
case SOUTH -> new Vector3i(0, 0, 1);
case NORTH -> new Vector3i(0, 0, -1);
case WEST -> new Vector3i(-1, 0, 0);
case EAST -> new Vector3i(1, 0, 0);
default -> new Vector3i(0, 1, 0);
};
switch (face) {
default:
case UP:
return new Vector3i(0, 1, 0);
case DOWN:
return new Vector3i(0, -1, 0);
case SOUTH:
return new Vector3i(0, 0, 1);
case NORTH:
return new Vector3i(0, 0, -1);
case WEST:
return new Vector3i(-1, 0, 0);
case EAST:
return new Vector3i(1, 0, 0);
}
}
public void set(StateType material) {
@@ -658,18 +674,18 @@ public class BlockPlace {
// You also have the desync caused by eye height as apparently tracking the player's ticks wasn't important to you
// No mojang, you really do need to track client ticks to get their accurate eye height.
// another damn desync added... maybe next decade it will get fixed and double the amount of issues.
public Vector getClickedLocation() {
public Vector3D getClickedLocation() {
SimpleCollisionBox box = new SimpleCollisionBox(getPlacedAgainstBlockLocation());
Vector look = ReachUtils.getLook(player, player.xRot, player.yRot);
Vector3D look = getLook(player, player.xRot, player.yRot);
final double distance = player.compensatedEntities.getSelf().getAttributeValue(Attributes.PLAYER_BLOCK_INTERACTION_RANGE) + 3;
Vector eyePos = new Vector(player.x, player.y + player.getEyeHeight(), player.z);
Vector endReachPos = eyePos.clone().add(new Vector(look.getX() * distance, look.getY() * distance, look.getZ() * distance));
Vector intercept = ReachUtils.calculateIntercept(box, eyePos, endReachPos).first();
final double distance = player.compensatedEntities.getSelf().getAttributeValue(PLAYER_BLOCK_INTERACTION_RANGE) + 3;
Vector3D eyePos = newVector3D(player.x, player.y + player.getEyeHeight(), player.z);
Vector3D endReachPos = eyePos.clone().add(newVector3D(look.getX() * distance, look.getY() * distance, look.getZ() * distance));
Vector3D intercept = calculateIntercept(box, eyePos, endReachPos).getFirst();
// Bring this back to relative to the block
// The player didn't even click the block... (we should force resync BEFORE we get here!)
if (intercept == null) return new Vector();
if (intercept == null) return newVector3D();
intercept.setX(intercept.getX() - box.minX);
intercept.setY(intercept.getY() - box.minY);
@@ -21,11 +21,13 @@ import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
import com.github.retrooper.packetevents.protocol.world.states.type.StateValue;
import com.github.retrooper.packetevents.util.Vector3i;
import org.bukkit.util.Vector;
import ac.grim.grimac.utils.vector.Vector3D;
import java.util.*;
import java.util.stream.Collectors;
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
public enum BlockPlaceResult {
// If the block only has directional data
@@ -80,7 +82,7 @@ public enum BlockPlaceResult {
}, ItemTypes.SNOW),
SLAB((player, place) -> {
Vector clickedPos = place.getClickedLocation();
Vector3D clickedPos = place.getClickedLocation();
WrappedBlockState slabData = place.getMaterial().createBlockState(CompensatedWorld.blockVersion);
WrappedBlockState existing = place.getExistingBlockData();
@@ -909,7 +911,7 @@ public enum BlockPlaceResult {
WrappedBlockState ccwState = place.getDirectionalState(ccw);
CollisionBox ccwBox = CollisionData.getData(ccwState.getType()).getMovementCollisionBox(player, player.getClientVersion(), ccwState);
Vector aboveCCWPos = place.getClickedLocation().add(new Vector(ccw.getModX(), ccw.getModY(), ccw.getModZ())).add(new Vector(0, 1, 0));
Vector3D aboveCCWPos = place.getClickedLocation().add(newVector3D(ccw.getModX(), ccw.getModY(), ccw.getModZ())).add(newVector3D(0, 1, 0));
WrappedBlockState aboveCCWState = player.compensatedWorld.getWrappedBlockStateAt(aboveCCWPos);
CollisionBox aboveCCWBox = CollisionData.getData(aboveCCWState.getType()).getMovementCollisionBox(player, player.getClientVersion(), aboveCCWState);
@@ -917,7 +919,7 @@ public enum BlockPlaceResult {
WrappedBlockState cwState = place.getDirectionalState(cw);
CollisionBox cwBox = CollisionData.getData(cwState.getType()).getMovementCollisionBox(player, player.getClientVersion(), cwState);
Vector aboveCWPos = place.getClickedLocation().add(new Vector(cw.getModX(), cw.getModY(), cw.getModZ())).add(new Vector(0, 1, 0));
Vector3D aboveCWPos = place.getClickedLocation().add(newVector3D(cw.getModX(), cw.getModY(), cw.getModZ())).add(newVector3D(0, 1, 0));
WrappedBlockState aboveCWState = player.compensatedWorld.getWrappedBlockStateAt(aboveCWPos);
CollisionBox aboveCWBox = CollisionData.getData(aboveCWState.getType()).getMovementCollisionBox(player, player.getClientVersion(), aboveCWState);
@@ -934,7 +936,7 @@ public enum BlockPlaceResult {
if ((!isCWLower || isCCWLower) && i >= 0) {
int j = playerFacing.getModX();
int k = playerFacing.getModZ();
Vector vec3 = place.getClickedLocation();
Vector3D vec3 = place.getClickedLocation();
double d0 = vec3.getX();
double d1 = vec3.getY();
hinge = (j >= 0 || d1 >= 0.5D) && (j <= 0 || d1 <= 0.5D) && (k >= 0 || d0 <= 0.5D) && (k <= 0 || d0 >= 0.5D) ? Hinge.LEFT : Hinge.RIGHT;
@@ -107,11 +107,17 @@ public class ConsumesBlockPlace {
Attachment attachment = bell.getAttachment();
BlockFace dir2 = BlockFace.valueOf(direction.name());
return switch (attachment) {
case FLOOR -> AxisUtil.isSameAxis(dir, dir2);
case SINGLE_WALL, DOUBLE_WALL -> !AxisUtil.isSameAxis(dir, dir2);
case CEILING -> true;
};
switch (attachment) {
case FLOOR:
return AxisUtil.isSameAxis(dir, dir2);
case SINGLE_WALL:
case DOUBLE_WALL:
return !AxisUtil.isSameAxis(dir, dir2);
case CEILING:
return true;
default:
return false;
}
} else {
return false;
}
@@ -1,7 +1,8 @@
package ac.grim.grimac.utils.blockstate.helper;
import ac.grim.grimac.utils.vector.Vector3D;
import com.github.retrooper.packetevents.protocol.world.BlockFace;
import org.bukkit.util.Vector;
import ac.grim.grimac.utils.vector.Vector3D;
public class BlockFaceHelper {
public static boolean isFaceVertical(BlockFace face) {
@@ -18,45 +19,67 @@ public class BlockFaceHelper {
*/
@Deprecated
public static BlockFace fromBukkitFace(org.bukkit.block.BlockFace face) {
return switch (face) {
case NORTH -> BlockFace.NORTH;
case SOUTH -> BlockFace.SOUTH;
case WEST -> BlockFace.WEST;
case EAST -> BlockFace.EAST;
case UP -> BlockFace.UP;
case DOWN -> BlockFace.DOWN;
default -> BlockFace.OTHER;
};
switch (face) {
case NORTH:
return BlockFace.NORTH;
case SOUTH:
return BlockFace.SOUTH;
case WEST:
return BlockFace.WEST;
case EAST:
return BlockFace.EAST;
case UP:
return BlockFace.UP;
case DOWN:
return BlockFace.DOWN;
default:
return BlockFace.OTHER;
}
}
public static BlockFace getClockWise(BlockFace face) {
return switch (face) {
case NORTH -> BlockFace.EAST;
case SOUTH -> BlockFace.WEST;
case WEST -> BlockFace.NORTH;
default -> BlockFace.SOUTH;
};
switch (face) {
case NORTH:
return BlockFace.EAST;
case SOUTH:
return BlockFace.WEST;
case WEST:
return BlockFace.NORTH;
case EAST:
default:
return BlockFace.SOUTH;
}
}
public static BlockFace getPEClockWise(BlockFace face) {
return switch (face) {
case NORTH -> BlockFace.EAST;
case SOUTH -> BlockFace.WEST;
case WEST -> BlockFace.NORTH;
default -> BlockFace.SOUTH;
};
switch (face) {
case NORTH:
return BlockFace.EAST;
case SOUTH:
return BlockFace.WEST;
case WEST:
return BlockFace.NORTH;
case EAST:
default:
return BlockFace.SOUTH;
}
}
public static BlockFace getCounterClockwise(BlockFace face) {
return switch (face) {
case NORTH -> BlockFace.WEST;
case SOUTH -> BlockFace.EAST;
case WEST -> BlockFace.SOUTH;
default -> BlockFace.NORTH;
};
switch (face) {
case NORTH:
return BlockFace.WEST;
case SOUTH:
return BlockFace.EAST;
case WEST:
return BlockFace.SOUTH;
case EAST:
default:
return BlockFace.NORTH;
}
}
public Vector offset(Vector toOffset, BlockFace face) {
public Vector3D offset(Vector3D toOffset, BlockFace face) {
toOffset.setX(toOffset.getX() + face.getModX());
toOffset.setY(toOffset.getY() + face.getModY());
toOffset.setZ(toOffset.getZ() + face.getModZ());
@@ -54,22 +54,36 @@ public enum AxisUtil {
}
public static AxisSelect getAxis(BlockFace face) {
return switch (face) {
case EAST -> EAST.select;
case WEST -> WEST.select;
case NORTH -> NORTH.select;
case SOUTH -> SOUTH.select;
case UP -> UP.select;
default -> DOWN.select;
};
switch (face) {
case EAST:
return EAST.select;
case WEST:
return WEST.select;
case NORTH:
return NORTH.select;
case SOUTH:
return SOUTH.select;
case UP:
return UP.select;
case DOWN:
default:
return DOWN.select;
}
}
public static boolean isSameAxis(BlockFace one, BlockFace two) {
return switch (one) {
case WEST, EAST -> two == BlockFace.WEST || two == BlockFace.EAST;
case NORTH, SOUTH -> two == BlockFace.NORTH || two == BlockFace.SOUTH;
case UP, DOWN -> two == BlockFace.UP || two == BlockFace.DOWN;
default -> false;
};
switch (one) {
case WEST:
case EAST:
return two == BlockFace.WEST || two == BlockFace.EAST;
case NORTH:
case SOUTH:
return two == BlockFace.NORTH || two == BlockFace.SOUTH;
case UP:
case DOWN:
return two == BlockFace.UP || two == BlockFace.DOWN;
default:
return false;
}
}
}
@@ -36,7 +36,7 @@ import java.util.stream.Stream;
// This is actually mean to be put into PacketEvents, but I don't like proprietary plugins stealing my code...
public enum CollisionData {
VINE((player, version, block, x, y, z) -> {
ComplexCollisionBox boxes = new ComplexCollisionBox();
ComplexCollisionBox boxes = new ComplexCollisionBox(5);
if (PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_13) && block.isUp())
boxes.add(new HexCollisionBox(0.0D, 15.0D, 0.0D, 16.0D, 16.0D, 16.0D));
@@ -73,11 +73,19 @@ public enum CollisionData {
BREWING_STAND((player, version, block, x, y, z) -> {
int base = 0;
// maxIndex is 3 instead of 2 for legacy clients because for 1.8 players there is a very rare bug
// That we handle later in the code requiring us to add a box 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
int maxIndex = 3;
if (version.isNewerThanOrEquals(ClientVersion.V_1_13))
// Yes I know we only need maxIndex = 3 for 1.8 specifically
// No I'm not adding a special clause for which would require another if check, I'll take compute > memory any day
if (version.isNewerThanOrEquals(ClientVersion.V_1_13)) {
maxIndex = 2;
base = 1;
}
return new ComplexCollisionBox(
return new ComplexCollisionBox(maxIndex,
new HexCollisionBox(base, 0, base, 16 - base, 2, 16 - base),
new SimpleCollisionBox(0.4375, 0.0, 0.4375, 0.5625, 0.875, 0.5625, false));
@@ -100,7 +108,7 @@ public enum CollisionData {
if (version.isOlderThanOrEquals(ClientVersion.V_1_12_2))
height = 0.3125;
return new ComplexCollisionBox(
return new ComplexCollisionBox(5,
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),
@@ -119,7 +127,7 @@ public enum CollisionData {
// The base is 0.75×0.75, and its floor is 0.25b high.
// The top is 1×0.625, and its ceiling is 0.375b low.
if (version.isNewerThanOrEquals(ClientVersion.V_1_13)) {
ComplexCollisionBox complexAnvil = new ComplexCollisionBox();
ComplexCollisionBox complexAnvil = new ComplexCollisionBox(4);
// Base of the anvil
complexAnvil.add(new HexCollisionBox(2, 0, 2, 14, 4, 14));
if (face == BlockFace.NORTH || face == BlockFace.SOUTH) {
@@ -166,29 +174,37 @@ public enum CollisionData {
// Overwrite previous SKULL enum for legacy, where head and wall skull isn't separate
WALL_SKULL((player, version, data, x, y, z) -> {
return switch (data.getFacing()) {
case SOUTH -> new SimpleCollisionBox(0.25F, 0.25F, 0.0F, 0.75F, 0.75F, 0.5F, false);
case WEST -> new SimpleCollisionBox(0.5F, 0.25F, 0.25F, 1.0F, 0.75F, 0.75F, false);
case EAST -> new SimpleCollisionBox(0.0F, 0.25F, 0.25F, 0.5F, 0.75F, 0.75F, false);
default -> new SimpleCollisionBox(0.25F, 0.25F, 0.5F, 0.75F, 0.75F, 1.0F, false);
};
switch (data.getFacing()) {
case SOUTH:
return new SimpleCollisionBox(0.25F, 0.25F, 0.0F, 0.75F, 0.75F, 0.5F, false);
case WEST:
return new SimpleCollisionBox(0.5F, 0.25F, 0.25F, 1.0F, 0.75F, 0.75F, false);
case EAST:
return new SimpleCollisionBox(0.0F, 0.25F, 0.25F, 0.5F, 0.75F, 0.75F, false);
default:
return new SimpleCollisionBox(0.25F, 0.25F, 0.5F, 0.75F, 0.75F, 1.0F, false);
}
}, StateTypes.CREEPER_WALL_HEAD, StateTypes.DRAGON_WALL_HEAD, StateTypes.PLAYER_WALL_HEAD, StateTypes.ZOMBIE_WALL_HEAD,
StateTypes.SKELETON_WALL_SKULL, StateTypes.WITHER_SKELETON_WALL_SKULL),
PIGLIN_WALL_HEAD((player, version, data, x, y, z) -> {
return switch (data.getFacing()) {
case SOUTH -> new HexCollisionBox(3.0D, 4.0D, 0.0D, 13.0D, 12.0D, 8.0D);
case EAST -> new HexCollisionBox(0.0D, 4.0D, 3.0D, 8.0D, 12.0D, 13.0D);
case WEST -> new HexCollisionBox(8.0D, 4.0D, 3.0D, 16.0D, 12.0D, 13.0D);
default -> new HexCollisionBox(3.0D, 4.0D, 8.0D, 13.0D, 12.0D, 16.0D);
};
switch (data.getFacing()) {
case SOUTH:
return new HexCollisionBox(3.0D, 4.0D, 0.0D, 13.0D, 12.0D, 8.0D);
case EAST:
return new HexCollisionBox(0.0D, 4.0D, 3.0D, 8.0D, 12.0D, 13.0D);
case WEST:
return new HexCollisionBox(8.0D, 4.0D, 3.0D, 16.0D, 12.0D, 13.0D);
default:
return new HexCollisionBox(3.0D, 4.0D, 8.0D, 13.0D, 12.0D, 16.0D);
}
}, StateTypes.PIGLIN_WALL_HEAD),
DOOR(new DoorHandler(), BlockTags.DOORS.getStates().toArray(new StateType[0])),
HOPPER((player, version, data, x, y, z) -> {
if (version.isNewerThanOrEquals(ClientVersion.V_1_13)) {
ComplexCollisionBox hopperBox = new ComplexCollisionBox();
ComplexCollisionBox hopperBox = new ComplexCollisionBox(7);
switch (data.getFacing()) {
case DOWN:
@@ -219,7 +235,7 @@ public enum CollisionData {
} else {
double height = 0.125 * 5;
return new ComplexCollisionBox(
return new ComplexCollisionBox(5,
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),
@@ -237,9 +253,7 @@ public enum CollisionData {
return new SimpleCollisionBox(eatenPosition, 0, 0.0625, 1 - 0.0625, height, 1 - 0.0625, false);
}, StateTypes.CAKE),
COCOA_BEANS((player, version, data, x, y, z) -> {
return getCocoa(version, data.getAge(), data.getFacing());
}, StateTypes.COCOA),
COCOA_BEANS((player, version, data, x, y, z) -> getCocoa(version, data.getAge(), data.getFacing()), StateTypes.COCOA),
STONE_CUTTER((player, version, data, x, y, z) -> {
if (version.isOlderThanOrEquals(ClientVersion.V_1_13_2))
@@ -290,7 +304,7 @@ public enum CollisionData {
}
ComplexCollisionBox complex = new ComplexCollisionBox(
ComplexCollisionBox complex = new ComplexCollisionBox(3,
new HexCollisionBox(5.0D, 6.0D, 5.0D, 11.0D, 13.0D, 11.0D),
new HexCollisionBox(4.0D, 4.0D, 4.0D, 12.0D, 6.0D, 12.0D));
@@ -324,7 +338,8 @@ public enum CollisionData {
return new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true);
if (player.lastY > y + 1 - 1e-5 && !player.isSneaking) {
return new ComplexCollisionBox(new HexCollisionBox(0.0D, 14.0D, 0.0D, 16.0D, 16.0D, 16.0D),
return new ComplexCollisionBox(5,
new HexCollisionBox(0.0D, 14.0D, 0.0D, 16.0D, 16.0D, 16.0D),
new HexCollisionBox(0.0D, 0.0D, 0.0D, 2.0D, 16.0D, 2.0D),
new HexCollisionBox(14.0D, 0.0D, 0.0D, 16.0D, 16.0D, 2.0D),
new HexCollisionBox(0.0D, 0.0D, 14.0D, 2.0D, 16.0D, 16.0),
@@ -341,12 +356,16 @@ public enum CollisionData {
if (version.isOlderThanOrEquals(ClientVersion.V_1_8))
width = 2;
return switch (data.getFacing()) {
case NORTH -> new HexCollisionBox(0.0D, 0.0D, 16.0D - width, 16.0D, 16.0D, 16.0D);
case SOUTH -> new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 16.0D, width);
case WEST -> new HexCollisionBox(16.0D - width, 0.0D, 0.0D, 16.0D, 16.0D, 16.0D);
default -> new HexCollisionBox(0.0D, 0.0D, 0.0D, width, 16.0D, 16.0D);
};
switch (data.getFacing()) {
case NORTH:
return new HexCollisionBox(0.0D, 0.0D, 16.0D - width, 16.0D, 16.0D, 16.0D);
case SOUTH:
return new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 16.0D, width);
case WEST:
return new HexCollisionBox(16.0D - width, 0.0D, 0.0D, 16.0D, 16.0D, 16.0D);
default:
return new HexCollisionBox(0.0D, 0.0D, 0.0D, width, 16.0D, 16.0D);
}
}, StateTypes.LADDER),
CAMPFIRE((player, version, data, x, y, z) -> {
@@ -368,11 +387,13 @@ public enum CollisionData {
return new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true);
if (data.isHanging()) {
return new ComplexCollisionBox(new HexCollisionBox(5.0D, 1.0D, 5.0D, 11.0D, 8.0D, 11.0D),
return new ComplexCollisionBox(2,
new HexCollisionBox(5.0D, 1.0D, 5.0D, 11.0D, 8.0D, 11.0D),
new HexCollisionBox(6.0D, 8.0D, 6.0D, 10.0D, 10.0D, 10.0D));
}
return new ComplexCollisionBox(new HexCollisionBox(5.0D, 0.0D, 5.0D, 11.0D, 7.0D, 11.0D),
return new ComplexCollisionBox(2,
new HexCollisionBox(5.0D, 0.0D, 5.0D, 11.0D, 7.0D, 11.0D),
new HexCollisionBox(6.0D, 7.0D, 6.0D, 10.0D, 9.0D, 10.0D));
}, StateTypes.LANTERN, StateTypes.SOUL_LANTERN),
@@ -382,7 +403,7 @@ public enum CollisionData {
if (version.isOlderThanOrEquals(ClientVersion.V_1_13_2))
return new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true);
return new ComplexCollisionBox(
return new ComplexCollisionBox(2,
new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 2.0D, 16.0D), // base
new HexCollisionBox(4.0D, 2.0D, 4.0D, 12.0D, 14.0D, 12.0D)); // post
}, StateTypes.LECTERN),
@@ -411,7 +432,7 @@ public enum CollisionData {
}
if (version.isOlderThanOrEquals(ClientVersion.V_1_13_2)) {
ComplexCollisionBox complexAnvil = new ComplexCollisionBox();
ComplexCollisionBox complexAnvil = new ComplexCollisionBox(4);
// Base of the anvil
complexAnvil.add(new HexCollisionBox(2, 0, 2, 14, 4, 14));
@@ -431,13 +452,15 @@ public enum CollisionData {
Face attachment = data.getFace();
if (attachment == Face.FLOOR) {
if (facing == BlockFace.NORTH || facing == BlockFace.SOUTH) {
return new ComplexCollisionBox(new HexCollisionBox(2.0D, 0.0D, 6.0D, 4.0D, 7.0D, 10.0D),
return new ComplexCollisionBox(5,
new HexCollisionBox(2.0D, 0.0D, 6.0D, 4.0D, 7.0D, 10.0D),
new HexCollisionBox(12.0D, 0.0D, 6.0D, 14.0D, 7.0D, 10.0D),
new HexCollisionBox(2.0D, 7.0D, 5.0D, 4.0D, 13.0D, 11.0D),
new HexCollisionBox(12.0D, 7.0D, 5.0D, 14.0D, 13.0D, 11.0D),
new HexCollisionBox(4.0D, 4.0D, 2.0D, 12.0D, 16.0D, 14.0D));
} else {
return new ComplexCollisionBox(new HexCollisionBox(6.0D, 0.0D, 2.0D, 10.0D, 7.0D, 4.0D),
return new ComplexCollisionBox(5,
new HexCollisionBox(6.0D, 0.0D, 2.0D, 10.0D, 7.0D, 4.0D),
new HexCollisionBox(6.0D, 0.0D, 12.0D, 10.0D, 7.0D, 14.0D),
new HexCollisionBox(5.0D, 7.0D, 2.0D, 11.0D, 13.0D, 4.0D),
new HexCollisionBox(5.0D, 7.0D, 12.0D, 11.0D, 13.0D, 14.0D),
@@ -446,25 +469,29 @@ public enum CollisionData {
} else if (attachment == Face.WALL) {
switch (facing) {
case NORTH:
return new ComplexCollisionBox(new HexCollisionBox(2.0D, 6.0D, 7.0D, 4.0D, 10.0D, 16.0D),
return new ComplexCollisionBox(5,
new HexCollisionBox(2.0D, 6.0D, 7.0D, 4.0D, 10.0D, 16.0D),
new HexCollisionBox(12.0D, 6.0D, 7.0D, 14.0D, 10.0D, 16.0D),
new HexCollisionBox(2.0D, 5.0D, 3.0D, 4.0D, 11.0D, 9.0D),
new HexCollisionBox(12.0D, 5.0D, 3.0D, 14.0D, 11.0D, 9.0D),
new HexCollisionBox(4.0D, 2.0D, 0.0D, 12.0D, 14.0D, 12.0D));
case WEST:
return new ComplexCollisionBox(new HexCollisionBox(7.0D, 6.0D, 2.0D, 16.0D, 10.0D, 4.0D),
return new ComplexCollisionBox(5,
new HexCollisionBox(7.0D, 6.0D, 2.0D, 16.0D, 10.0D, 4.0D),
new HexCollisionBox(7.0D, 6.0D, 12.0D, 16.0D, 10.0D, 14.0D),
new HexCollisionBox(3.0D, 5.0D, 2.0D, 9.0D, 11.0D, 4.0D),
new HexCollisionBox(3.0D, 5.0D, 12.0D, 9.0D, 11.0D, 14.0D),
new HexCollisionBox(0.0D, 2.0D, 4.0D, 12.0D, 14.0D, 12.0D));
case SOUTH:
return new ComplexCollisionBox(new HexCollisionBox(2.0D, 6.0D, 0.0D, 4.0D, 10.0D, 7.0D),
return new ComplexCollisionBox(5,
new HexCollisionBox(2.0D, 6.0D, 0.0D, 4.0D, 10.0D, 7.0D),
new HexCollisionBox(12.0D, 6.0D, 0.0D, 14.0D, 10.0D, 7.0D),
new HexCollisionBox(2.0D, 5.0D, 7.0D, 4.0D, 11.0D, 13.0D),
new HexCollisionBox(12.0D, 5.0D, 7.0D, 14.0D, 11.0D, 13.0D),
new HexCollisionBox(4.0D, 2.0D, 4.0D, 12.0D, 14.0D, 16.0D));
case EAST:
return new ComplexCollisionBox(new HexCollisionBox(0.0D, 6.0D, 2.0D, 9.0D, 10.0D, 4.0D),
return new ComplexCollisionBox(5,
new HexCollisionBox(0.0D, 6.0D, 2.0D, 9.0D, 10.0D, 4.0D),
new HexCollisionBox(0.0D, 6.0D, 12.0D, 9.0D, 10.0D, 14.0D),
new HexCollisionBox(7.0D, 5.0D, 2.0D, 13.0D, 11.0D, 4.0D),
new HexCollisionBox(7.0D, 5.0D, 12.0D, 13.0D, 11.0D, 14.0D),
@@ -472,13 +499,15 @@ public enum CollisionData {
}
} else {
if (facing == BlockFace.NORTH || facing == BlockFace.SOUTH) {
return new ComplexCollisionBox(new HexCollisionBox(2.0D, 9.0D, 6.0D, 4.0D, 16.0D, 10.0D),
return new ComplexCollisionBox(5,
new HexCollisionBox(2.0D, 9.0D, 6.0D, 4.0D, 16.0D, 10.0D),
new HexCollisionBox(12.0D, 9.0D, 6.0D, 14.0D, 16.0D, 10.0D),
new HexCollisionBox(2.0D, 3.0D, 5.0D, 4.0D, 9.0D, 11.0D),
new HexCollisionBox(12.0D, 3.0D, 5.0D, 14.0D, 9.0D, 11.0D),
new HexCollisionBox(4.0D, 0.0D, 2.0D, 12.0D, 12.0D, 14.0D));
} else {
return new ComplexCollisionBox(new HexCollisionBox(6.0D, 9.0D, 2.0D, 10.0D, 16.0D, 4.0D),
return new ComplexCollisionBox(5,
new HexCollisionBox(6.0D, 9.0D, 2.0D, 10.0D, 16.0D, 4.0D),
new HexCollisionBox(6.0D, 9.0D, 12.0D, 10.0D, 16.0D, 14.0D),
new HexCollisionBox(5.0D, 3.0D, 2.0D, 11.0D, 9.0D, 4.0D),
new HexCollisionBox(5.0D, 3.0D, 12.0D, 11.0D, 9.0D, 14.0D),
@@ -513,12 +542,16 @@ public enum CollisionData {
if (data.isOpen())
return NoCollisionBox.INSTANCE;
return switch (data.getFacing()) {
case NORTH, SOUTH -> new SimpleCollisionBox(0.0F, 0.0F, 0.375F, 1.0F, 1.5F, 0.625F, false);
case WEST, EAST -> new SimpleCollisionBox(0.375F, 0.0F, 0.0F, 0.625F, 1.5F, 1.0F, false);
default -> // This code is unreachable but the compiler does not know this
NoCollisionBox.INSTANCE;
};
switch (data.getFacing()) {
case NORTH:
case SOUTH:
return new SimpleCollisionBox(0.0F, 0.0F, 0.375F, 1.0F, 1.5F, 0.625F, false);
case WEST:
case EAST:
return new SimpleCollisionBox(0.375F, 0.0F, 0.0F, 0.625F, 1.5F, 1.0F, false);
default: // This code is unreachable but the compiler does not know this
return NoCollisionBox.INSTANCE;
}
}, BlockTags.FENCE_GATES.getStates().toArray(new StateType[0])),
FENCE(new DynamicCollisionFence(), BlockTags.FENCES.getStates().toArray(new StateType[0])),
@@ -551,7 +584,7 @@ public enum CollisionData {
StateTypes.ENCHANTING_TABLE),
FRAME((player, version, data, x, y, z) -> {
ComplexCollisionBox complexCollisionBox = new ComplexCollisionBox(new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 13.0D, 16.0D));
ComplexCollisionBox complexCollisionBox = new ComplexCollisionBox(2, new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 13.0D, 16.0D));
if (data.isEye()) {
if (version.isNewerThanOrEquals(ClientVersion.V_1_13)) { // 1.13 players have a 0.5x0.5 eye
@@ -622,7 +655,7 @@ public enum CollisionData {
if (version.isOlderThan(ClientVersion.V_1_14))
return new SimpleCollisionBox(0.0F, 0.0F, 0.0F, 1.0F, 0.5625, 1.0F, false);
ComplexCollisionBox baseBox = new ComplexCollisionBox(new HexCollisionBox(0.0D, 3.0D, 0.0D, 16.0D, 9.0D, 16.0D));
ComplexCollisionBox baseBox = new ComplexCollisionBox(3, new HexCollisionBox(0.0D, 3.0D, 0.0D, 16.0D, 9.0D, 16.0D));
switch (data.getFacing()) {
case NORTH:
@@ -656,13 +689,11 @@ public enum CollisionData {
0.625, 0.625, 0.625, false),
StateTypes.STRUCTURE_VOID),
END_ROD((player, version, data, x, y, z) -> {
return getEndRod(version, data.getFacing());
}, StateTypes.END_ROD, StateTypes.LIGHTNING_ROD),
END_ROD((player, version, data, x, y, z) -> getEndRod(version, data.getFacing()), StateTypes.END_ROD, StateTypes.LIGHTNING_ROD),
CAULDRON((player, version, data, x, y, z) -> {
if (version.isNewerThan(ClientVersion.getById(467))) { // changed in 19w13a, 1.14 Snapshot
return new ComplexCollisionBox(
return new ComplexCollisionBox(15,
new SimpleCollisionBox(0.0, 0.0, 0.0, 0.125, 1.0, 0.25, false),
new SimpleCollisionBox(0.0, 0.0, 0.75, 0.125, 1.0, 1.0, false),
new SimpleCollisionBox(0.125, 0.0, 0.0, 0.25, 1.0, 0.125, false),
@@ -684,7 +715,7 @@ public enum CollisionData {
if (version.isOlderThan(ClientVersion.V_1_13))
height = 0.3125;
return new ComplexCollisionBox(
return new ComplexCollisionBox(5,
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),
@@ -704,9 +735,7 @@ public enum CollisionData {
SOULSAND(new SimpleCollisionBox(0, 0, 0, 1, 0.875, 1, false),
StateTypes.SOUL_SAND),
PICKLE((player, version, data, x, y, z) -> {
return getPicklesBox(version, data.getPickles());
}, StateTypes.SEA_PICKLE),
PICKLE((player, version, data, x, y, z) -> getPicklesBox(version, data.getPickles()), StateTypes.SEA_PICKLE),
TURTLEEGG((player, version, data, x, y, z) -> {
// ViaVersion replacement block (West facing cocoa beans)
@@ -733,27 +762,34 @@ public enum CollisionData {
BlockTags.FLOWER_POTS.getStates().toArray(new StateType[0])),
WALL_SIGN((player, version, data, x, y, z) -> {
return switch (data.getFacing()) {
case NORTH -> new HexCollisionBox(0.0D, 4.5D, 14.0D, 16.0D, 12.5D, 16.0D);
case SOUTH -> new HexCollisionBox(0.0D, 4.5D, 0.0D, 16.0D, 12.5D, 2.0D);
case WEST -> new HexCollisionBox(14.0D, 4.5D, 0.0D, 16.0D, 12.5D, 16.0D);
case EAST -> new HexCollisionBox(0.0D, 4.5D, 0.0D, 2.0D, 12.5D, 16.0D);
default -> NoCollisionBox.INSTANCE;
};
switch (data.getFacing()) {
case NORTH:
return new HexCollisionBox(0.0D, 4.5D, 14.0D, 16.0D, 12.5D, 16.0D);
case SOUTH:
return new HexCollisionBox(0.0D, 4.5D, 0.0D, 16.0D, 12.5D, 2.0D);
case WEST:
return new HexCollisionBox(14.0D, 4.5D, 0.0D, 16.0D, 12.5D, 16.0D);
case EAST:
return new HexCollisionBox(0.0D, 4.5D, 0.0D, 2.0D, 12.5D, 16.0D);
default:
return NoCollisionBox.INSTANCE;
}
}, BlockTags.WALL_SIGNS.getStates().toArray(new StateType[0])),
WALL_FAN((player, version, data, x, y, z) -> {
return switch (data.getFacing()) {
case NORTH -> new HexCollisionBox(0.0D, 4.0D, 5.0D, 16.0D, 12.0D, 16.0D);
case SOUTH -> new HexCollisionBox(0.0D, 4.0D, 0.0D, 16.0D, 12.0D, 11.0D);
case WEST -> new HexCollisionBox(5.0D, 4.0D, 0.0D, 16.0D, 12.0D, 16.0D);
default -> new HexCollisionBox(0.0D, 4.0D, 0.0D, 11.0D, 12.0D, 16.0D);
};
switch (data.getFacing()) {
case NORTH:
return new HexCollisionBox(0.0D, 4.0D, 5.0D, 16.0D, 12.0D, 16.0D);
case SOUTH:
return new HexCollisionBox(0.0D, 4.0D, 0.0D, 16.0D, 12.0D, 11.0D);
case WEST:
return new HexCollisionBox(5.0D, 4.0D, 0.0D, 16.0D, 12.0D, 16.0D);
default:
return new HexCollisionBox(0.0D, 4.0D, 0.0D, 11.0D, 12.0D, 16.0D);
}
}, BlockTags.WALL_CORALS.getStates().toArray(new StateType[0])),
CORAL_PLANT((player, version, data, x, y, z) -> {
return new HexCollisionBox(2.0D, 0.0D, 2.0D, 14.0D, 15.0D, 14.0D);
}, Stream.concat(
CORAL_PLANT((player, version, data, x, y, z) -> new HexCollisionBox(2.0D, 0.0D, 2.0D, 14.0D, 15.0D, 14.0D), 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)
@@ -797,47 +833,64 @@ public enum CollisionData {
}, StateTypes.TRIPWIRE),
TRIPWIRE_HOOK((player, version, data, x, y, z) -> {
return switch (data.getFacing()) {
case NORTH -> new HexCollisionBox(5.0D, 0.0D, 10.0D, 11.0D, 10.0D, 16.0D);
case SOUTH -> new HexCollisionBox(5.0D, 0.0D, 0.0D, 11.0D, 10.0D, 6.0D);
case WEST -> new HexCollisionBox(10.0D, 0.0D, 5.0D, 16.0D, 10.0D, 11.0D);
default -> new HexCollisionBox(0.0D, 0.0D, 5.0D, 6.0D, 10.0D, 11.0D);
};
switch (data.getFacing()) {
case NORTH:
return new HexCollisionBox(5.0D, 0.0D, 10.0D, 11.0D, 10.0D, 16.0D);
case SOUTH:
return new HexCollisionBox(5.0D, 0.0D, 0.0D, 11.0D, 10.0D, 6.0D);
case WEST:
return new HexCollisionBox(10.0D, 0.0D, 5.0D, 16.0D, 10.0D, 11.0D);
default:
return new HexCollisionBox(0.0D, 0.0D, 5.0D, 6.0D, 10.0D, 11.0D);
}
}, StateTypes.TRIPWIRE_HOOK),
TORCH(new HexCollisionBox(6.0D, 0.0D, 6.0D, 10.0D, 10.0D, 10.0D),
StateTypes.TORCH, StateTypes.REDSTONE_TORCH),
WALL_TORCH((player, version, data, x, y, z) -> {
return switch (data.getFacing()) {
case NORTH -> new HexCollisionBox(5.5D, 3.0D, 11.0D, 10.5D, 13.0D, 16.0D);
case SOUTH -> new HexCollisionBox(5.5D, 3.0D, 0.0D, 10.5D, 13.0D, 5.0D);
case WEST -> new HexCollisionBox(11.0D, 3.0D, 5.5D, 16.0D, 13.0D, 10.5D);
case EAST -> new HexCollisionBox(0.0D, 3.0D, 5.5D, 5.0D, 13.0D, 10.5D);
switch (data.getFacing()) {
case NORTH:
return new HexCollisionBox(5.5D, 3.0D, 11.0D, 10.5D, 13.0D, 16.0D);
case SOUTH:
return new HexCollisionBox(5.5D, 3.0D, 0.0D, 10.5D, 13.0D, 5.0D);
case WEST:
return new HexCollisionBox(11.0D, 3.0D, 5.5D, 16.0D, 13.0D, 10.5D);
case EAST:
return new HexCollisionBox(0.0D, 3.0D, 5.5D, 5.0D, 13.0D, 10.5D);
// 1.13 separates wall and normal torches, 1.12 does not
default -> new HexCollisionBox(6.0D, 0.0D, 6.0D, 10.0D, 10.0D, 10.0D);
};
default:
return new HexCollisionBox(6.0D, 0.0D, 6.0D, 10.0D, 10.0D, 10.0D);
}
}, StateTypes.WALL_TORCH, StateTypes.REDSTONE_WALL_TORCH),
// 1.17 blocks
CANDLE((player, version, data, x, y, z) -> {
if (version.isNewerThanOrEquals(ClientVersion.V_1_17)) {
return switch (data.getCandles()) {
case 1 -> new HexCollisionBox(7.0, 0.0, 7.0, 9.0, 6.0, 9.0);
case 2 -> new HexCollisionBox(5.0, 0.0, 6.0, 11.0, 6.0, 9.0);
case 3 -> new HexCollisionBox(5.0, 0.0, 6.0, 10.0, 6.0, 11.0);
default -> new HexCollisionBox(5.0, 0.0, 5.0, 11.0, 6.0, 10.0);
};
switch (data.getCandles()) {
case 1:
return new HexCollisionBox(7.0, 0.0, 7.0, 9.0, 6.0, 9.0);
case 2:
return new HexCollisionBox(5.0, 0.0, 6.0, 11.0, 6.0, 9.0);
case 3:
return new HexCollisionBox(5.0, 0.0, 6.0, 10.0, 6.0, 11.0);
default:
return new HexCollisionBox(5.0, 0.0, 5.0, 11.0, 6.0, 10.0);
}
}
return getPicklesBox(version, data.getCandles());
}, BlockTags.CANDLES.getStates().toArray(new StateType[0])),
CANDLE_CAKE((player, version, data, x, y, z) -> {
ComplexCollisionBox cake = new ComplexCollisionBox(new HexCollisionBox(1.0, 0.0, 1.0, 15.0, 8.0, 15.0));
if (version.isNewerThanOrEquals(ClientVersion.V_1_17))
cake.add(new HexCollisionBox(7.0, 8.0, 7.0, 9.0, 14.0, 9.0));
return cake;
SimpleCollisionBox cake = new HexCollisionBox(1.0, 0.0, 1.0, 15.0, 8.0, 15.0);
if (version.isOlderThan(ClientVersion.V_1_17)) {
return cake;
} else {
return new ComplexCollisionBox(2,
cake,
new HexCollisionBox(7.0, 8.0, 7.0, 9.0, 14.0, 9.0));
}
}, BlockTags.CANDLE_CAKES.getStates().toArray(new StateType[0])),
SCULK_SENSOR(new HexCollisionBox(0.0, 0.0, 0.0, 16.0, 8.0, 16.0), StateTypes.SCULK_SENSOR, StateTypes.CALIBRATED_SCULK_SENSOR),
@@ -913,26 +966,17 @@ public enum CollisionData {
return new HexCollisionBox(6.0D, 0.0D, 0.0D, 10.0D, 16.0D, 16.0D);
}, StateTypes.NETHER_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));
}, StateTypes.AZALEA, StateTypes.FLOWERING_AZALEA),
AZALEA((player, version, data, x, y, z) -> new ComplexCollisionBox(2,
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)), StateTypes.AZALEA, StateTypes.FLOWERING_AZALEA),
AMETHYST_CLUSTER((player, version, data, x, y, z) -> {
return getAmethystBox(version, data.getFacing(), 7, 3);
}, StateTypes.AMETHYST_CLUSTER),
AMETHYST_CLUSTER((player, version, data, x, y, z) -> getAmethystBox(version, data.getFacing(), 7, 3), StateTypes.AMETHYST_CLUSTER),
SMALL_AMETHYST_BUD((player, version, data, x, y, z) -> {
return getAmethystBox(version, data.getFacing(), 3, 4);
}, StateTypes.SMALL_AMETHYST_BUD),
SMALL_AMETHYST_BUD((player, version, data, x, y, z) -> getAmethystBox(version, data.getFacing(), 3, 4), StateTypes.SMALL_AMETHYST_BUD),
MEDIUM_AMETHYST_BUD((player, version, data, x, y, z) -> {
return getAmethystBox(version, data.getFacing(), 4, 3);
}, StateTypes.MEDIUM_AMETHYST_BUD),
MEDIUM_AMETHYST_BUD((player, version, data, x, y, z) -> getAmethystBox(version, data.getFacing(), 4, 3), StateTypes.MEDIUM_AMETHYST_BUD),
LARGE_AMETHYST_BUD((player, version, data, x, y, z) -> {
return getAmethystBox(version, data.getFacing(), 5, 3);
}, StateTypes.LARGE_AMETHYST_BUD),
LARGE_AMETHYST_BUD((player, version, data, x, y, z) -> getAmethystBox(version, data.getFacing(), 5, 3), StateTypes.LARGE_AMETHYST_BUD),
MUD_BLOCK((player, version, data, x, y, z) -> {
if (version.isNewerThanOrEquals(ClientVersion.V_1_19)) {
@@ -945,13 +989,18 @@ public enum CollisionData {
if (!data.isHanging()) {
return new HexCollisionBox(7.0D, 0.0D, 7.0D, 9.0D, 16.0D, 9.0D);
}
return switch (data.getAge()) {
case 0 -> new HexCollisionBox(7.0D, 13.0D, 7.0D, 9.0D, 16.0D, 9.0D);
case 1 -> new HexCollisionBox(7.0D, 10.0D, 7.0D, 9.0D, 16.0D, 9.0D);
case 2 -> new HexCollisionBox(7.0D, 7.0D, 7.0D, 9.0D, 16.0D, 9.0D);
case 3 -> new HexCollisionBox(7.0D, 3.0D, 7.0D, 9.0D, 16.0D, 9.0D);
default -> new HexCollisionBox(7.0D, 0.0D, 7.0D, 9.0D, 16.0D, 9.0D);
};
switch (data.getAge()) {
case 0:
return new HexCollisionBox(7.0D, 13.0D, 7.0D, 9.0D, 16.0D, 9.0D);
case 1:
return new HexCollisionBox(7.0D, 10.0D, 7.0D, 9.0D, 16.0D, 9.0D);
case 2:
return new HexCollisionBox(7.0D, 7.0D, 7.0D, 9.0D, 16.0D, 9.0D);
case 3:
return new HexCollisionBox(7.0D, 3.0D, 7.0D, 9.0D, 16.0D, 9.0D);
default:
return new HexCollisionBox(7.0D, 0.0D, 7.0D, 9.0D, 16.0D, 9.0D);
}
}, StateTypes.MANGROVE_PROPAGULE),
SCULK_SHRIKER((player, version, data, x, y, z) -> {
@@ -986,11 +1035,16 @@ public enum CollisionData {
}, StateTypes.PITCHER_CROP),
WALL_HANGING_SIGNS((player, version, data, x, y, z) -> {
return switch (data.getFacing()) {
case NORTH, SOUTH -> new HexCollisionBox(0.0, 14.0, 6.0, 16.0, 16.0, 10.0);
case WEST, EAST -> new HexCollisionBox(6.0, 14.0, 0.0, 10.0, 16.0, 16.0);
default -> NoCollisionBox.INSTANCE;
};
switch (data.getFacing()) {
case NORTH:
case SOUTH:
return new HexCollisionBox(0.0, 14.0, 6.0, 16.0, 16.0, 10.0);
case WEST:
case EAST:
return new HexCollisionBox(6.0, 14.0, 0.0, 10.0, 16.0, 16.0);
default:
return NoCollisionBox.INSTANCE;
}
}, BlockTags.WALL_HANGING_SIGNS.getStates().toArray(new StateType[0])),
DEFAULT(new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true), StateTypes.STONE);
@@ -1028,14 +1082,20 @@ public enum CollisionData {
if (version.isOlderThanOrEquals(ClientVersion.V_1_16_4))
return NoCollisionBox.INSTANCE;
return switch (facing) {
case DOWN -> new HexCollisionBox(param_1, 16 - param_0, param_1, 16 - param_1, 16.0, 16 - param_1);
case NORTH -> new HexCollisionBox(param_1, param_1, 16 - param_0, 16 - param_1, 16 - param_1, 16.0);
case SOUTH -> new HexCollisionBox(param_1, param_1, 0.0, 16 - param_1, 16 - param_1, param_0);
case EAST -> new HexCollisionBox(0.0, param_1, param_1, param_0, 16 - param_1, 16 - param_1);
case WEST -> new HexCollisionBox(16 - param_0, param_1, param_1, 16.0, 16 - param_1, 16 - param_1);
default -> new HexCollisionBox(param_1, 0.0, param_1, 16 - param_1, param_0, 16 - param_1);
};
switch (facing) {
case DOWN:
return new HexCollisionBox(param_1, 16 - param_0, param_1, 16 - param_1, 16.0, 16 - param_1);
case NORTH:
return new HexCollisionBox(param_1, param_1, 16 - param_0, 16 - param_1, 16 - param_1, 16.0);
case SOUTH:
return new HexCollisionBox(param_1, param_1, 0.0, 16 - param_1, 16 - param_1, param_0);
case EAST:
return new HexCollisionBox(0.0, param_1, param_1, param_0, 16 - param_1, 16 - param_1);
case WEST:
return new HexCollisionBox(16 - param_0, param_1, param_1, 16.0, 16 - param_1, 16 - param_1);
default:
return new HexCollisionBox(param_1, 0.0, param_1, 16 - param_1, param_0, 16 - param_1);
}
}
private static CollisionBox getPicklesBox(ClientVersion version, int pickles) {
@@ -1044,13 +1104,18 @@ public enum CollisionData {
return getCocoa(version, pickles, BlockFace.WEST);
}
return switch (pickles) {
case 1 -> new HexCollisionBox(6.0D, 0.0D, 6.0D, 10.0D, 6.0D, 10.0D);
case 2 -> new HexCollisionBox(3.0D, 0.0D, 3.0D, 13.0D, 6.0D, 13.0D);
case 3 -> new HexCollisionBox(2.0D, 0.0D, 2.0D, 14.0D, 6.0D, 14.0D);
case 4 -> new HexCollisionBox(2.0D, 0.0D, 2.0D, 14.0D, 7.0D, 14.0D);
default -> NoCollisionBox.INSTANCE;
};
switch (pickles) {
case 1:
return new HexCollisionBox(6.0D, 0.0D, 6.0D, 10.0D, 6.0D, 10.0D);
case 2:
return new HexCollisionBox(3.0D, 0.0D, 3.0D, 13.0D, 6.0D, 13.0D);
case 3:
return new HexCollisionBox(2.0D, 0.0D, 2.0D, 14.0D, 6.0D, 14.0D);
case 4:
return new HexCollisionBox(2.0D, 0.0D, 2.0D, 14.0D, 7.0D, 14.0D);
default:
return NoCollisionBox.INSTANCE;
}
}
public static CollisionBox getCocoa(ClientVersion version, int age, BlockFace direction) {
@@ -1105,11 +1170,16 @@ public enum CollisionData {
if (version.isOlderThan(ClientVersion.V_1_9))
return NoCollisionBox.INSTANCE;
return switch (face) {
case NORTH, SOUTH -> new HexCollisionBox(6.0D, 6.0D, 0.0D, 10.0D, 10.0D, 16.0D);
case EAST, WEST -> new HexCollisionBox(0.0D, 6.0D, 6.0D, 16.0D, 10.0D, 10.0D);
default -> new HexCollisionBox(6.0D, 0.0D, 6.0D, 10.0D, 16.0D, 10.0);
};
switch (face) {
case NORTH:
case SOUTH:
return new HexCollisionBox(6.0D, 6.0D, 0.0D, 10.0D, 10.0D, 16.0D);
case EAST:
case WEST:
return new HexCollisionBox(0.0D, 6.0D, 6.0D, 16.0D, 10.0D, 10.0D);
default:
return new HexCollisionBox(6.0D, 0.0D, 6.0D, 10.0D, 16.0D, 10.0);
}
}
// Would pre-computing all states be worth the memory cost? I doubt it
@@ -20,29 +20,32 @@ import java.util.*;
public enum HitboxData {
RAILS((player, item, version, data, x, y, z) -> {
return switch (data.getShape()) {
case ASCENDING_NORTH, ASCENDING_SOUTH, ASCENDING_EAST, ASCENDING_WEST -> {
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())) {
yield 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.125F, 1.0F, false);
}
yield new SimpleCollisionBox(0.0F, 0.0F, 0.0F, 1.0F, 0.625F, 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)) {
yield new SimpleCollisionBox(0.0F, 0.0F, 0.0F, 1.0F, 0.625F, 1.0F, false);
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
yield new SimpleCollisionBox(0.0F, 0.0F, 0.0F, 1.0F, 0.1875F, 1.0F, false);
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
yield new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true);
return new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true);
}
yield new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 8.0D, 16.0D);
}
default -> new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 2.0D, 16.0D);
};
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) -> {
@@ -106,31 +109,35 @@ public enum HitboxData {
}
}
return switch (face) {
case FLOOR -> {
switch (face) {
case FLOOR:
// X-AXIS
if (facing == BlockFace.EAST || facing == BlockFace.WEST) {
yield new SimpleCollisionBox(0.25, 0.0, 0.3125, 0.75, 0.375, 0.6875, false);
return new SimpleCollisionBox(0.25, 0.0, 0.3125, 0.75, 0.375, 0.6875, false);
}
// Z-AXIS
yield new SimpleCollisionBox(0.3125, 0.0, 0.25, 0.6875, 0.375, 0.75, false);
// Z-AXIS
}
case WALL -> switch (facing) {
case EAST -> new SimpleCollisionBox(0.0, 0.25, 0.3125, 0.375, 0.75, 0.6875, false);
case WEST -> new SimpleCollisionBox(0.625, 0.25, 0.3125, 1.0, 0.75, 0.6875, false);
case SOUTH -> new SimpleCollisionBox(0.3125, 0.25, 0.0, 0.6875, 0.75, 0.375, false);
default -> new SimpleCollisionBox(0.3125, 0.25, 0.625, 0.6875, 0.75, 1.0, false);
};
default -> {
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) {
yield new SimpleCollisionBox(0.25, 0.625, 0.3125, 0.75, 1.0, 0.6875, false);
return new SimpleCollisionBox(0.25, 0.625, 0.3125, 0.75, 1.0, 0.6875, false);
}
// Z-Axis
yield new SimpleCollisionBox(0.3125, 0.625, 0.25, 0.6875, 1.0, 0.75, false);
}
};
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) -> {
@@ -164,17 +171,27 @@ public enum HitboxData {
switch (face) {
case WALL:
return switch (facing) {
case EAST ->
powered ? new HexCollisionBox(0.0, 6.0, 5.0, 1.0, 10.0, 11.0) : new HexCollisionBox(0.0, 6.0, 5.0, 2.0, 10.0, 11.0);
case WEST ->
powered ? new HexCollisionBox(15.0, 6.0, 5.0, 16.0, 10.0, 11.0) : new HexCollisionBox(14.0, 6.0, 5.0, 16.0, 10.0, 11.0);
case SOUTH ->
powered ? new HexCollisionBox(5.0, 6.0, 0.0, 11.0, 10.0, 1.0) : new HexCollisionBox(5.0, 6.0, 0.0, 11.0, 10.0, 2.0);
case NORTH, UP, DOWN ->
powered ? new HexCollisionBox(5.0, 6.0, 15.0, 11.0, 10.0, 16.0) : new HexCollisionBox(5.0, 6.0, 14.0, 11.0, 10.0, 16.0);
default -> NoCollisionBox.INSTANCE;
};
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)) {
@@ -205,22 +222,32 @@ public enum HitboxData {
WALL(new DynamicHitboxWall(), BlockTags.WALLS.getStates().toArray(new StateType[0])),
WALL_SIGN((player, item, version, data, x, y, z) -> {
return switch (data.getFacing()) {
case NORTH -> new HexCollisionBox(0.0, 4.5, 14.0, 16.0, 12.5, 16.0);
case SOUTH -> new HexCollisionBox(0.0, 4.5, 0.0, 16.0, 12.5, 2.0);
case EAST -> new HexCollisionBox(0.0, 4.5, 0.0, 2.0, 12.5, 16.0);
case WEST -> new HexCollisionBox(14.0, 4.5, 0.0, 16.0, 12.5, 16.0);
default -> NoCollisionBox.INSTANCE;
};
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) -> {
return switch (data.getFacing()) {
case NORTH, SOUTH -> 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 -> 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));
};
switch (data.getFacing()) {
case NORTH:
case SOUTH:
return new ComplexCollisionBox(2,
new HexCollisionBox(0.0D, 14.0D, 6.0D, 16.0D, 16.0D, 10.0D),
new HexCollisionBox(1.0D, 0.0D, 7.0D, 15.0D, 10.0D, 9.0D));
default:
return new ComplexCollisionBox(2,
new HexCollisionBox(6.0D, 14.0D, 0.0D, 10.0D, 16.0D, 16.0D),
new HexCollisionBox(7.0D, 0.0D, 1.0D, 9.0D, 10.0D, 15.0D));
}
}, BlockTags.WALL_HANGING_SIGNS.getStates().toArray(new StateType[0])),
STANDING_SIGN((player, item, version, data, x, y, z) ->
@@ -245,13 +272,18 @@ public enum HitboxData {
return WALL_SIGN.dynamic.fetch(player, item, version, data, x, y, z);
}
return switch (data.getFacing()) {
case NORTH -> new HexCollisionBox(0.0, 0.0, 14.0, 16.0, 12.5, 16.0);
case EAST -> new HexCollisionBox(0.0, 0.0, 0.0, 2.0, 12.5, 16.0);
case WEST -> new HexCollisionBox(14.0, 0.0, 0.0, 16.0, 12.5, 16.0);
case SOUTH -> new HexCollisionBox(0.0, 0.0, 0.0, 16.0, 12.5, 2.0);
default -> throw new IllegalStateException("Impossible Banner Facing State; Something very wrong is going on");
};
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,
@@ -263,7 +295,7 @@ public enum HitboxData {
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(
return new ComplexCollisionBox(2,
new HexCollisionBox(1.0, 0.0, 1.0, 15.0, 2.0, 15.0),
new SimpleCollisionBox(0.4375, 0.0, 0.4375, 0.5625, 0.875, 0.5625, false));
}
@@ -303,7 +335,8 @@ public enum HitboxData {
}, StateTypes.SNOW),
LECTERN_BLOCK((player, item, version, data, x, y, z) -> {
ComplexCollisionBox common = new ComplexCollisionBox(new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 2.0D, 16.0D),
ComplexCollisionBox common = new ComplexCollisionBox(5,
new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 2.0D, 16.0D),
new HexCollisionBox(4.0D, 2.0D, 4.0D, 12.0D, 14.0D, 12.0D));
if (data.getFacing() == BlockFace.WEST) {
@@ -329,7 +362,7 @@ public enum HitboxData {
GLOW_LICHEN_SCULK_VEIN((player, item, version, data, x, y, z) -> {
if (version.isNewerThan(ClientVersion.V_1_16_4)) {
ComplexCollisionBox box = new ComplexCollisionBox();
ComplexCollisionBox box = new ComplexCollisionBox(6);
if (data.isUp()) {
box.add(new HexCollisionBox(0.0D, 15.0D, 0.0D, 16.0D, 16.0D, 16.0D));
@@ -395,12 +428,17 @@ public enum HitboxData {
if (version.isOlderThan(ClientVersion.V_1_13))
return new HexCollisionBox(7.0D, 0.0D, 7.0D, 9.0D, 16.0D, 9.0D);
return switch (data.getFacing()) {
case SOUTH -> new HexCollisionBox(6.0D, 0.0D, 6.0D, 10.0D, 10.0D, 16.0D);
case WEST -> new HexCollisionBox(0.0D, 0.0D, 6.0D, 10.0D, 10.0D, 10.0D);
case NORTH -> new HexCollisionBox(6.0D, 0.0D, 0.0D, 10.0D, 10.0D, 10.0D);
default -> new HexCollisionBox(6.0D, 0.0D, 6.0D, 16.0D, 10.0D, 10.0D);
};
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) -> {
@@ -491,7 +529,7 @@ public enum HitboxData {
}
// STABLE_SHAPE for the scaffolding
ComplexCollisionBox box = new ComplexCollisionBox(
ComplexCollisionBox box = new ComplexCollisionBox(9,
new HexCollisionBox(0.0D, 14.0D, 0.0D, 16.0D, 16.0D, 16.0D),
new HexCollisionBox(0.0D, 0.0D, 0.0D, 2.0D, 16.0D, 2.0D),
new HexCollisionBox(14.0D, 0.0D, 0.0D, 16.0D, 16.0D, 2.0D),
@@ -512,7 +550,7 @@ public enum HitboxData {
if (version.isOlderThanOrEquals(ClientVersion.V_1_16_4))
return new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true);
ComplexCollisionBox box = new ComplexCollisionBox();
ComplexCollisionBox box = new ComplexCollisionBox(2);
if (data.getFacing() == BlockFace.NORTH) { // Stem
box.add(new HexCollisionBox(5.0D, 0.0D, 9.0D, 11.0D, 15.0D, 15.0D));
@@ -635,11 +673,16 @@ public enum HitboxData {
}
private static int getPropaguleMinHeight(int age) {
return switch (age) {
case 0, 1, 2 -> 13 - age * 3;
case 3, 4 -> (4 - age) * 3;
default -> throw new IllegalStateException("Impossible Propagule Height");
};
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) {
@@ -653,7 +696,7 @@ public enum HitboxData {
}
} else {
// Via replacement - 4 sided vine
return new ComplexCollisionBox(
return new ComplexCollisionBox(4,
new HexCollisionBox(0.0D, 0.0D, 0.0D, 1.0D, 16.0D, 16.0D),
new HexCollisionBox(15.0D, 0.0D, 0.0D, 16.0D, 16.0D, 16.0D),
new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 16.0D, 1.0D),
@@ -663,13 +706,19 @@ public enum HitboxData {
}
private static int getHorizontalID(BlockFace facing) {
return switch (facing) {
case DOWN, UP -> -1;
case NORTH -> 2;
case SOUTH -> 0;
case WEST -> 1;
case EAST -> 3;
default -> throw new IllegalStateException("Impossible blockface for getHorizontalID");
};
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,97 @@
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) -> {
HexCollisionBox 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;
}
}
@@ -21,14 +21,18 @@ public class DoorHandler implements CollisionFactory {
@Override
public CollisionBox fetch(GrimPlayer player, ClientVersion version, WrappedBlockState block, int x, int y, int z) {
return switch (fetchDirection(player, version, block, x, y, z)) {
case NORTH -> NORTH_AABB.copy();
case SOUTH -> SOUTH_AABB.copy();
case EAST -> EAST_AABB.copy();
case WEST -> WEST_AABB.copy();
default -> NoCollisionBox.INSTANCE;
};
switch (fetchDirection(player, version, block, x, y, z)) {
case NORTH:
return NORTH_AABB.copy();
case SOUTH:
return SOUTH_AABB.copy();
case EAST:
return EAST_AABB.copy();
case WEST:
return WEST_AABB.copy();
}
return NoCollisionBox.INSTANCE;
}
public BlockFace fetchDirection(GrimPlayer player, ClientVersion version, WrappedBlockState door, int x, int y, int z) {
@@ -76,11 +80,16 @@ public class DoorHandler implements CollisionFactory {
isRightHinge = door.getHinge() == Hinge.RIGHT;
}
return switch (facingDirection) {
case SOUTH -> isClosed ? BlockFace.SOUTH : (isRightHinge ? BlockFace.EAST : BlockFace.WEST);
case WEST -> isClosed ? BlockFace.WEST : (isRightHinge ? BlockFace.SOUTH : BlockFace.NORTH);
case NORTH -> isClosed ? BlockFace.NORTH : (isRightHinge ? BlockFace.WEST : BlockFace.EAST);
default -> isClosed ? BlockFace.EAST : (isRightHinge ? BlockFace.NORTH : BlockFace.SOUTH);
};
switch (facingDirection) {
case EAST:
default:
return isClosed ? BlockFace.EAST : (isRightHinge ? BlockFace.NORTH : BlockFace.SOUTH);
case SOUTH:
return isClosed ? BlockFace.SOUTH : (isRightHinge ? BlockFace.EAST : BlockFace.WEST);
case WEST:
return isClosed ? BlockFace.WEST : (isRightHinge ? BlockFace.SOUTH : BlockFace.NORTH);
case NORTH:
return isClosed ? BlockFace.NORTH : (isRightHinge ? BlockFace.WEST : BlockFace.EAST);
}
}
}
@@ -31,7 +31,7 @@ public class DynamicChorusPlant implements CollisionFactory {
float f = 0.5F - (float) 0.3125;
float f1 = 0.5F + (float) 0.3125;
SimpleCollisionBox baseShape = new SimpleCollisionBox(f, f, f, f1, f1, f1, false);
CollisionBox[] avoxelshape = new CollisionBox[directions.length];
SimpleCollisionBox[] avoxelshape = new SimpleCollisionBox[directions.length];
for (int i = 0; i < directions.length; ++i) {
BlockFace direction = directions[i];
@@ -41,7 +41,7 @@ public class DynamicChorusPlant implements CollisionFactory {
CollisionBox[] avoxelshape1 = new CollisionBox[64];
for (int k = 0; k < 64; ++k) {
ComplexCollisionBox directionalShape = new ComplexCollisionBox(baseShape);
ComplexCollisionBox directionalShape = new ComplexCollisionBox(7, baseShape); // how big is this one??
for (int j = 0; j < directions.length; ++j) {
if ((k & 1 << j) != 0) {
@@ -1,10 +1,7 @@
package ac.grim.grimac.utils.collisions.blocks;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.collisions.datatypes.CollisionBox;
import ac.grim.grimac.utils.collisions.datatypes.CollisionFactory;
import ac.grim.grimac.utils.collisions.datatypes.ComplexCollisionBox;
import ac.grim.grimac.utils.collisions.datatypes.HexCollisionBox;
import ac.grim.grimac.utils.collisions.datatypes.*;
import ac.grim.grimac.utils.nmsutil.Materials;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
@@ -18,16 +15,16 @@ import com.github.retrooper.packetevents.protocol.world.states.enums.Shape;
import java.util.stream.IntStream;
public class DynamicStair implements CollisionFactory {
protected static final CollisionBox TOP_AABB = new HexCollisionBox(0, 8, 0, 16, 16, 16);
protected static final CollisionBox BOTTOM_AABB = new HexCollisionBox(0, 0, 0, 16, 8, 16);
protected static final CollisionBox OCTET_NNN = new HexCollisionBox(0.0D, 0.0D, 0.0D, 8.0D, 8.0D, 8.0D);
protected static final CollisionBox OCTET_NNP = new HexCollisionBox(0.0D, 0.0D, 8.0D, 8.0D, 8.0D, 16.0D);
protected static final CollisionBox OCTET_NPN = new HexCollisionBox(0.0D, 8.0D, 0.0D, 8.0D, 16.0D, 8.0D);
protected static final CollisionBox OCTET_NPP = new HexCollisionBox(0.0D, 8.0D, 8.0D, 8.0D, 16.0D, 16.0D);
protected static final CollisionBox OCTET_PNN = new HexCollisionBox(8.0D, 0.0D, 0.0D, 16.0D, 8.0D, 8.0D);
protected static final CollisionBox OCTET_PNP = new HexCollisionBox(8.0D, 0.0D, 8.0D, 16.0D, 8.0D, 16.0D);
protected static final CollisionBox OCTET_PPN = new HexCollisionBox(8.0D, 8.0D, 0.0D, 16.0D, 16.0D, 8.0D);
protected static final CollisionBox OCTET_PPP = new HexCollisionBox(8.0D, 8.0D, 8.0D, 16.0D, 16.0D, 16.0D);
protected static final SimpleCollisionBox TOP_AABB = new HexCollisionBox(0, 8, 0, 16, 16, 16);
protected static final SimpleCollisionBox BOTTOM_AABB = new HexCollisionBox(0, 0, 0, 16, 8, 16);
protected static final SimpleCollisionBox OCTET_NNN = new HexCollisionBox(0.0D, 0.0D, 0.0D, 8.0D, 8.0D, 8.0D);
protected static final SimpleCollisionBox OCTET_NNP = new HexCollisionBox(0.0D, 0.0D, 8.0D, 8.0D, 8.0D, 16.0D);
protected static final SimpleCollisionBox OCTET_NPN = new HexCollisionBox(0.0D, 8.0D, 0.0D, 8.0D, 16.0D, 8.0D);
protected static final SimpleCollisionBox OCTET_NPP = new HexCollisionBox(0.0D, 8.0D, 8.0D, 8.0D, 16.0D, 16.0D);
protected static final SimpleCollisionBox OCTET_PNN = new HexCollisionBox(8.0D, 0.0D, 0.0D, 16.0D, 8.0D, 8.0D);
protected static final SimpleCollisionBox OCTET_PNP = new HexCollisionBox(8.0D, 0.0D, 8.0D, 16.0D, 8.0D, 16.0D);
protected static final SimpleCollisionBox OCTET_PPN = new HexCollisionBox(8.0D, 8.0D, 0.0D, 16.0D, 16.0D, 8.0D);
protected static final SimpleCollisionBox OCTET_PPP = new HexCollisionBox(8.0D, 8.0D, 8.0D, 16.0D, 16.0D, 16.0D);
protected static final CollisionBox[] TOP_SHAPES = makeShapes(TOP_AABB, OCTET_NNN, OCTET_PNN, OCTET_NNP, OCTET_PNP);
protected static final CollisionBox[] BOTTOM_SHAPES = makeShapes(BOTTOM_AABB, OCTET_NPN, OCTET_PPN, OCTET_NPP, OCTET_PPP);
private static final int[] SHAPE_BY_STATE = new int[]{12, 5, 3, 10, 14, 13, 7, 11, 13, 7, 11, 14, 8, 4, 1, 2, 4, 1, 2, 8};
@@ -77,20 +74,25 @@ public class DynamicStair implements CollisionFactory {
}
private static BlockFace rotateYCCW(BlockFace face) {
return switch (face) {
case EAST -> BlockFace.NORTH;
case SOUTH -> BlockFace.EAST;
case WEST -> BlockFace.SOUTH;
default -> BlockFace.WEST;
};
switch (face) {
default:
case NORTH:
return BlockFace.WEST;
case EAST:
return BlockFace.NORTH;
case SOUTH:
return BlockFace.EAST;
case WEST:
return BlockFace.SOUTH;
}
}
private static CollisionBox[] makeShapes(CollisionBox p_199779_0_, CollisionBox p_199779_1_, CollisionBox p_199779_2_, CollisionBox p_199779_3_, CollisionBox p_199779_4_) {
private static CollisionBox[] makeShapes(SimpleCollisionBox p_199779_0_, SimpleCollisionBox p_199779_1_, SimpleCollisionBox p_199779_2_, SimpleCollisionBox p_199779_3_, SimpleCollisionBox p_199779_4_) {
return IntStream.range(0, 16).mapToObj((p_199780_5_) -> makeStairShape(p_199780_5_, p_199779_0_, p_199779_1_, p_199779_2_, p_199779_3_, p_199779_4_)).toArray(CollisionBox[]::new);
}
private static CollisionBox makeStairShape(int p_199781_0_, CollisionBox p_199781_1_, CollisionBox p_199781_2_, CollisionBox p_199781_3_, CollisionBox p_199781_4_, CollisionBox p_199781_5_) {
ComplexCollisionBox voxelshape = new ComplexCollisionBox(p_199781_1_);
private static CollisionBox makeStairShape(int p_199781_0_, SimpleCollisionBox p_199781_1_, SimpleCollisionBox p_199781_2_, SimpleCollisionBox p_199781_3_, SimpleCollisionBox p_199781_4_, SimpleCollisionBox p_199781_5_) {
ComplexCollisionBox voxelshape = new ComplexCollisionBox(5, p_199781_1_); // can we do better than 5?
if ((p_199781_0_ & 1) != 0) {
voxelshape.add(p_199781_2_);
}
@@ -129,23 +131,36 @@ public class DynamicStair implements CollisionFactory {
}
private int directionToValue(BlockFace face) {
return switch (face) {
case NORTH -> 2;
case SOUTH -> 0;
case WEST -> 1;
case EAST -> 3;
default -> -1;
};
switch (face) {
default:
case UP:
case DOWN:
return -1;
case NORTH:
return 2;
case SOUTH:
return 0;
case WEST:
return 1;
case EAST:
return 3;
}
}
private EnumShape toEnumShape(Shape shape) {
return switch (shape) {
case INNER_LEFT -> EnumShape.INNER_LEFT;
case INNER_RIGHT -> EnumShape.INNER_RIGHT;
case OUTER_LEFT -> EnumShape.OUTER_LEFT;
case OUTER_RIGHT -> EnumShape.OUTER_RIGHT;
default -> EnumShape.STRAIGHT;
};
switch (shape) {
default:
case STRAIGHT:
return EnumShape.STRAIGHT;
case INNER_LEFT:
return EnumShape.INNER_LEFT;
case INNER_RIGHT:
return EnumShape.INNER_RIGHT;
case OUTER_LEFT:
return EnumShape.OUTER_LEFT;
case OUTER_RIGHT:
return EnumShape.OUTER_RIGHT;
}
}
enum EnumShape {
@@ -14,13 +14,20 @@ public class PistonBaseCollision implements CollisionFactory {
public CollisionBox fetch(GrimPlayer player, ClientVersion version, WrappedBlockState block, int x, int y, int z) {
if (!block.isExtended()) return new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true);
return switch (block.getFacing()) {
case UP -> new HexCollisionBox(0, 0, 0, 16, 12, 16);
case NORTH -> new HexCollisionBox(0, 0, 4, 16, 16, 16);
case SOUTH -> new HexCollisionBox(0, 0, 0, 16, 16, 12);
case WEST -> new HexCollisionBox(4, 0, 0, 16, 16, 16);
case EAST -> new HexCollisionBox(0, 0, 0, 12, 16, 16);
default -> new HexCollisionBox(0, 4, 0, 16, 16, 16);
};
switch (block.getFacing()) {
default:
case DOWN:
return new HexCollisionBox(0, 4, 0, 16, 16, 16);
case UP:
return new HexCollisionBox(0, 0, 0, 16, 12, 16);
case NORTH:
return new HexCollisionBox(0, 0, 4, 16, 16, 16);
case SOUTH:
return new HexCollisionBox(0, 0, 0, 16, 16, 12);
case WEST:
return new HexCollisionBox(4, 0, 0, 16, 16, 16);
case EAST:
return new HexCollisionBox(0, 0, 0, 12, 16, 16);
}
}
}
@@ -38,35 +38,46 @@ public class PistonHeadCollision implements CollisionFactory {
longAmount = 0;
return switch (block.getFacing()) {
case UP -> new ComplexCollisionBox(new HexCollisionBox(0, 12, 0, 16, 16, 16),
new HexCollisionBox(6, 0 - longAmount, 6, 10, 12, 10));
case NORTH -> new ComplexCollisionBox(new HexCollisionBox(0, 0, 0, 16, 16, 4),
new HexCollisionBox(6, 6, 4, 10, 10, 16 + longAmount));
case SOUTH -> {
switch (block.getFacing()) {
case DOWN:
default:
return new ComplexCollisionBox(2,
new HexCollisionBox(0, 0, 0, 16, 4, 16),
new HexCollisionBox(6, 4, 6, 10, 16 + longAmount, 10));
case UP:
return new ComplexCollisionBox(2,
new HexCollisionBox(0, 12, 0, 16, 16, 16),
new HexCollisionBox(6, 0 - longAmount, 6, 10, 12, 10));
case NORTH:
return new ComplexCollisionBox(2,
new HexCollisionBox(0, 0, 0, 16, 16, 4),
new HexCollisionBox(6, 6, 4, 10, 10, 16 + longAmount));
case SOUTH:
// SOUTH piston is glitched in 1.7 and 1.8, fixed in 1.9
// Don't bother with short piston boxes as 1.7/1.8 clients don't have them
if (version.isOlderThanOrEquals(ClientVersion.V_1_8))
yield new ComplexCollisionBox(new HexCollisionBox(0, 0, 12, 16, 16, 16),
return new ComplexCollisionBox(2,
new HexCollisionBox(0, 0, 12, 16, 16, 16),
new HexCollisionBox(4, 6, 0, 12, 10, 12));
yield new ComplexCollisionBox(new HexCollisionBox(0, 0, 12, 16, 16, 16),
return new ComplexCollisionBox(2,
new HexCollisionBox(0, 0, 12, 16, 16, 16),
new HexCollisionBox(6, 6, 0 - longAmount, 10, 10, 12));
}
case WEST -> {
case WEST:
// WEST piston is glitched in 1.7 and 1.8, fixed in 1.9
// Don't bother with short piston boxes as 1.7/1.8 clients don't have them
if (version.isOlderThanOrEquals(ClientVersion.V_1_8))
yield new ComplexCollisionBox(new HexCollisionBox(0, 0, 0, 4, 16, 16),
return new ComplexCollisionBox(2,
new HexCollisionBox(0, 0, 0, 4, 16, 16),
new HexCollisionBox(6, 4, 4, 10, 12, 16));
yield new ComplexCollisionBox(new HexCollisionBox(0, 0, 0, 4, 16, 16),
return new ComplexCollisionBox(2,
new HexCollisionBox(0, 0, 0, 4, 16, 16),
new HexCollisionBox(4, 6, 6, 16 + longAmount, 10, 10));
}
case EAST -> new ComplexCollisionBox(new HexCollisionBox(12, 0, 0, 16, 16, 16),
new HexCollisionBox(0 - longAmount, 6, 4, 12, 10, 12));
default -> new ComplexCollisionBox(new HexCollisionBox(0, 0, 0, 16, 4, 16),
new HexCollisionBox(6, 4, 6, 10, 16 + longAmount, 10));
};
case EAST:
return new ComplexCollisionBox(2,
new HexCollisionBox(12, 0, 0, 16, 16, 16),
new HexCollisionBox(0 - longAmount, 6, 4, 12, 10, 12));
}
}
}
@@ -18,7 +18,7 @@ import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
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);
private static final CollisionBox[] COLLISION_BOXES = makeShapes(2.0F, 2.0F, 24.0F, 0.0F, 24.0F, true, 1);
@Override
public CollisionBox fetch(GrimPlayer player, ClientVersion version, WrappedBlockState block, int x, int y, int z) {
@@ -21,7 +21,7 @@ import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
public class DynamicCollisionPane extends DynamicConnecting implements CollisionFactory {
private static final CollisionBox[] COLLISION_BOXES = makeShapes(1.0F, 1.0F, 16.0F, 0.0F, 16.0F, true);
private static final CollisionBox[] COLLISION_BOXES = makeShapes(1.0F, 1.0F, 16.0F, 0.0F, 16.0F, true, 1);
@Override
public CollisionBox fetch(GrimPlayer player, ClientVersion version, WrappedBlockState block, int x, int y, int z) {
@@ -52,7 +52,7 @@ public class DynamicCollisionPane extends DynamicConnecting implements Collision
if (version.isNewerThanOrEquals(ClientVersion.V_1_9)) {
return COLLISION_BOXES[getAABBIndex(north, east, south, west)].copy();
} else { // 1.8 and below clients have pane bounding boxes one pixel less
ComplexCollisionBox boxes = new ComplexCollisionBox();
ComplexCollisionBox boxes = new ComplexCollisionBox(2);
if ((!west || !east) && (west || east || north || south)) {
if (west) {
boxes.add(new SimpleCollisionBox(0.0F, 0.0F, 0.4375F, 0.5F, 1.0F, 0.5625F));
@@ -16,10 +16,9 @@ import com.github.retrooper.packetevents.protocol.world.states.enums.West;
import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
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);
private static final CollisionBox[] COLLISION_BOXES = makeShapes(4.0F, 3.0F, 24.0F, 0.0F, 24.0F, false, 1);
/**
* @deprecated use DynamicHitboxWall
@@ -53,12 +52,11 @@ public class DynamicCollisionWall extends DynamicConnecting implements Collision
// On 1.13+ clients the bounding box is much more complicated
if (version.isNewerThanOrEquals(ClientVersion.V_1_13)) {
ComplexCollisionBox box = new ComplexCollisionBox();
ComplexCollisionBox box = new ComplexCollisionBox(5);
// Proper and faster way would be to compute all this beforehand
if (up == 1) {
box.add(new HexCollisionBox(4, 0, 4, 12, 16, 12));
return box;
}
if (north == 1) {
@@ -81,6 +79,7 @@ public class DynamicCollisionWall extends DynamicConnecting implements Collision
} else if (east == 2) {
box.add(new HexCollisionBox(5, 0, 5, 16, 16, 11));
}
return box;
}
// Magic 1.8 code for walls that I copied over, 1.12 below uses this mess
@@ -143,9 +142,7 @@ public class DynamicCollisionWall extends DynamicConnecting implements Collision
if (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_13)) {
// Proper and faster way would be to compute all this beforehand
if (up) {
ComplexCollisionBox box = new ComplexCollisionBox(COLLISION_BOXES[getAABBIndex(north, east, south, west)].copy());
box.add(new HexCollisionBox(4, 0, 4, 12, 24, 12));
return box;
return COLLISION_BOXES[getAABBIndex(north, east, south, west)].copy().union(new HexCollisionBox(4, 0, 4, 12, 24, 12));
}
return COLLISION_BOXES[getAABBIndex(north, east, south, west)].copy();
@@ -18,7 +18,7 @@ import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
public class DynamicConnecting {
public static CollisionBox[] makeShapes(float p_196408_1_, float p_196408_2_, float p_196408_3_, float p_196408_4_, float p_196408_5_, boolean includeCenter) {
public static CollisionBox[] makeShapes(float p_196408_1_, float p_196408_2_, float p_196408_3_, float p_196408_4_, float p_196408_5_, boolean includeCenter, int additionalMaxIndex) {
float middleMin = 8.0F - p_196408_1_;
float middleMax = 8.0F + p_196408_1_;
float f2 = 8.0F - p_196408_2_;
@@ -29,14 +29,21 @@ public class DynamicConnecting {
SimpleCollisionBox voxelshape3 = new HexCollisionBox(0.0D, p_196408_4_, f2, f3, p_196408_5_, f3);
SimpleCollisionBox voxelshape4 = new HexCollisionBox(f2, p_196408_4_, f2, 16.0D, p_196408_5_, f3);
ComplexCollisionBox voxelshape5 = new ComplexCollisionBox(voxelshape1, voxelshape4);
ComplexCollisionBox voxelshape6 = new ComplexCollisionBox(voxelshape2, voxelshape3);
ComplexCollisionBox voxelshape5 = new ComplexCollisionBox(2 + additionalMaxIndex, voxelshape1, voxelshape4);
ComplexCollisionBox voxelshape6 = new ComplexCollisionBox(2 + additionalMaxIndex, voxelshape2, voxelshape3);
CollisionBox[] avoxelshape = new CollisionBox[]{NoCollisionBox.INSTANCE, voxelshape2, voxelshape3, voxelshape6, voxelshape1, new ComplexCollisionBox(voxelshape2, voxelshape1), new ComplexCollisionBox(voxelshape3, voxelshape1), new ComplexCollisionBox(voxelshape6, voxelshape1), voxelshape4, new ComplexCollisionBox(voxelshape2, voxelshape4), new ComplexCollisionBox(voxelshape3, voxelshape4), new ComplexCollisionBox(voxelshape6, voxelshape4), voxelshape5, new ComplexCollisionBox(voxelshape2, voxelshape5), new ComplexCollisionBox(voxelshape3, voxelshape5), new ComplexCollisionBox(voxelshape6, voxelshape5)};
CollisionBox[] avoxelshape = new CollisionBox[]{
NoCollisionBox.INSTANCE, voxelshape2, voxelshape3, voxelshape6, voxelshape1,
new ComplexCollisionBox(2 + additionalMaxIndex, voxelshape2, voxelshape1), new ComplexCollisionBox(2 + additionalMaxIndex, voxelshape3, voxelshape1),
new ComplexCollisionBox(3 + additionalMaxIndex, voxelshape2, voxelshape3, voxelshape1), voxelshape4,
new ComplexCollisionBox(2 + additionalMaxIndex, voxelshape2, voxelshape4), new ComplexCollisionBox(2 + additionalMaxIndex, voxelshape3, voxelshape4),
new ComplexCollisionBox(3 + additionalMaxIndex, voxelshape2, voxelshape3, voxelshape4), voxelshape5,
new ComplexCollisionBox(3 + additionalMaxIndex, voxelshape2, voxelshape1, voxelshape4), new ComplexCollisionBox(3 + additionalMaxIndex, voxelshape3, voxelshape1, voxelshape4),
new ComplexCollisionBox(4 + additionalMaxIndex, voxelshape1, voxelshape2, voxelshape3, voxelshape4)};
if (includeCenter) {
for (int i = 0; i < 16; ++i) {
avoxelshape[i] = new ComplexCollisionBox(up, avoxelshape[i]);
avoxelshape[i] = avoxelshape[i].union(up);
}
}
@@ -16,27 +16,30 @@ 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);
private static final CollisionBox[] MODERN_HITBOXES = makeShapes(2.0F, 2.0F, 24.0F, 0.0F, 24.0F, true, 1);
public static SimpleCollisionBox[] LEGACY_HITBOXES = new SimpleCollisionBox[] {new SimpleCollisionBox(0.375D, 0.0D, 0.375D, 0.625D, 1.0D, 0.625D), new SimpleCollisionBox(0.375D, 0.0D, 0.375D, 0.625D, 1.0D, 1.0D), new SimpleCollisionBox(0.0D, 0.0D, 0.375D, 0.625D, 1.0D, 0.625D), new SimpleCollisionBox(0.0D, 0.0D, 0.375D, 0.625D, 1.0D, 1.0D), new SimpleCollisionBox(0.375D, 0.0D, 0.0D, 0.625D, 1.0D, 0.625D), new SimpleCollisionBox(0.375D, 0.0D, 0.0D, 0.625D, 1.0D, 1.0D), new SimpleCollisionBox(0.0D, 0.0D, 0.0D, 0.625D, 1.0D, 0.625D), new SimpleCollisionBox(0.0D, 0.0D, 0.0D, 0.625D, 1.0D, 1.0D), new SimpleCollisionBox(0.375D, 0.0D, 0.375D, 1.0D, 1.0D, 0.625D), new SimpleCollisionBox(0.375D, 0.0D, 0.375D, 1.0D, 1.0D, 1.0D), new SimpleCollisionBox(0.0D, 0.0D, 0.375D, 1.0D, 1.0D, 0.625D), new SimpleCollisionBox(0.0D, 0.0D, 0.375D, 1.0D, 1.0D, 1.0D), new SimpleCollisionBox(0.375D, 0.0D, 0.0D, 1.0D, 1.0D, 0.625D), new SimpleCollisionBox(0.375D, 0.0D, 0.0D, 1.0D, 1.0D, 1.0D), new SimpleCollisionBox(0.0D, 0.0D, 0.0D, 1.0D, 1.0D, 0.625D), new SimpleCollisionBox(0.0D, 0.0D, 0.0D, 1.0D, 1.0D, 1.0D)};
static {
for (int i = 0; i < MODERN_HITBOXES.length; i++) {
CollisionBox collisionBox = MODERN_HITBOXES[i];
List<SimpleCollisionBox> boxes = new ArrayList<>();
collisionBox.downCast(boxes);
// no ComplexCollisionBox produced by makeShapes is every larger than 5 SimpleCollisionBoxes
private static final int MAX_MODERN_HITBOX_COMPLEX_COLLISION_BOX_SIZE = 5;
for (SimpleCollisionBox box : boxes) {
if (box.maxY > 1) {
box.maxY = 1;
static {
SimpleCollisionBox[] boxes = new SimpleCollisionBox[MAX_MODERN_HITBOX_COMPLEX_COLLISION_BOX_SIZE];
// we start from one because MODERN_HITBOXES[0] is a NoCollisionBox
for (int i = 1; i < MODERN_HITBOXES.length; i++) {
CollisionBox collisionBox = MODERN_HITBOXES[i];
int size = collisionBox.downCast(boxes);
for (int j = 0; j < size; j++) {
if (boxes[j].maxY > 1) {
boxes[j].maxY = 1;
}
}
MODERN_HITBOXES[i] = new ComplexCollisionBox(boxes.toArray(new SimpleCollisionBox[0]));
MODERN_HITBOXES[i] = size == 1 ? boxes[0] : new ComplexCollisionBox(size, boxes);
}
}
@@ -15,7 +15,7 @@ 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);
private static final CollisionBox[] COLLISION_BOXES = makeShapes(1.0F, 1.0F, 16.0F, 0.0F, 16.0F, true, 1);
@Override
public CollisionBox fetch(GrimPlayer player, StateType item, ClientVersion version, WrappedBlockState block, int x, int y, int z) {
Loaded 100 of 163 files, more files were not shown because too many files have changed in this diff. Show more