Compare commits

...
Author SHA1 Message Date
NotAlexNoyle b195cb658f Exclude CODEOWNERS from language statistics 2026-10-29 16:18:57 -04:00
NotAlexNoyle 8437088abf Add CODEOWNERS (one staff approval) 2026-10-29 16:18:57 -04:00
NotAlexNoyle 6a72263909 Update lang 2026-09-14 00:54:39 -04:00
NotAlexNoyle ff04996ca9 Tune fastbreak for 1.21.11 ViaVersion clients 2026-08-17 10:43:04 -04:00
NotAlexNoyle 46358b3079 Normalize adventure version with server 2026-08-10 07:49:27 -04:00
NotAlexNoyle d06bb80891 Fix postgres, SLF4J 2026-06-17 13:23:42 -04:00
NotAlexNoyle e29e8b6e9f Fix postgres, slf4j providers 2026-06-16 23:10:23 -04:00
NotAlexNoyle 9d1d5a4295 Harmonize config with TrueOG NCP 2026-06-16 11:21:44 -04:00
NotAlexNoyle 9ed8ae9dba New PacketEvents repo 2026-06-03 18:19:47 -04:00
NotAlexNoyle bebcab70b0 Standardize PacketEvents 2026-06-03 17:58:54 -04:00
NotAlexNoyle db66c035c2 Backport to PacketEvents 2.11.2 API 2026-06-01 19:50:43 -04:00
NotAlexNoyle 67da895f25 Merge PR #3 upstream updates while preserving legacy item bobbing fix 2026-06-01 14:57:29 -04:00
NotAlexNoyle 8909e710ae Fix bobbing on 1.8-1.15.2 clients 2026-06-01 14:30:17 -04:00
Axionize 65815fc73b Merge remote-tracking branch 'origin/2.0' into lightning
# Conflicts:
#	common/src/main/java/ac/grim/grimac/checks/impl/combat/Reach.java
2026-04-15 10:17:13 -04:00
Axionize c169d9c682 Fix incorrect entity metadata indexes 2026-04-14 04:24:31 -04:00
ManInMyVan 99dce8c28d don't check the same value twice 2026-04-13 19:51:59 -06:00
Bram 1b60cb82c5 Fixed: MultiActionsD not checking if the inventory got opened this tick (#2596)
Fixed: MultiActionsD cancelling the close window packet causing desync / compatibility issues
Fixed: CheckManagerListener not using the open horse window to set serverOpenedInventoryThisTick
2026-04-13 16:33:41 -06:00
NotAlexNoyle 11facb64c0 Disable update checking 2026-04-13 07:29:50 -04:00
Milan Ensink 35fe1f0768 Fix MYSQL 8 support (#2534)
* Fix MYSQL 8 support

* Prevent double index error from sending everytime.
2026-04-13 04:51:40 -04:00
ManInMyVan fc75e51a09 fix PacketOrderC viabackwards false 2026-04-12 18:21:25 -06:00
ManInMyVan 13ad8bd823 fix #2579 2026-04-12 18:18:54 -06:00
Tyler 890799ddf9 Bumped Gradle Wrapper to fix build (#2591) 2026-04-12 09:01:19 -04:00
Axionize 3e07a0ee2d Downgrade PE to fix promise/future execution breaking in PE's netty listeners 2026-04-12 08:22:07 -04:00
ManInMyVan e8dc9ae997 RESOURCE_PACK_STATUS is async 2026-04-10 14:50:24 -06:00
ManInMyVan b55a12c721 minor changes to PacketHidePlayerInfo 2026-04-10 13:12:05 -06:00
Axionize ece9f3cae4 Bump PE to support Bukkit 26.1 servers 2026-04-09 11:12:12 -04:00
Fussel efd796cdd6 Fix PostgreSQL UUID retrieval in history command (#2575)
* Fix PostgreSQL UUID retrieval in history command

* Improve error handling for UUID retrieval in DatabaseUtils

* Use if-else if in DatabaseUtils.getUuid
2026-04-08 20:36:45 -04:00
ManInMyVan b9da63c8e2 fix #1990 2026-04-06 17:37:54 -06:00
SamB440 7fd7bc1d7b Fix soul torch collision data 2026-04-06 17:50:14 +01:00
ManInMyVan d1b2135191 add BadPacketsC to check if the LEAVE_BED packet is valid 2026-04-04 19:57:24 -06:00
SamB440 10cb176992 Fix glow squid bounding box 2026-04-04 17:55:26 +01:00
ManInMyVan 77c48f4942 quilt is a normal client 2026-04-03 14:12:54 -06:00
ManInMyVan cd3576b196 fix #2563 2026-04-02 20:54:03 -06:00
ManInMyVan 89eedbeb85 actually check sneaking in MultiInteractA 2026-04-02 20:36:39 -06:00
ManInMyVan 7d9dc86ec7 account for INTERACT_ENTITY being split in 26.1 2026-04-02 20:32:24 -06:00
ManInMyVan 11d572f8fd move ElytraA onStartGliding() to before the client version check in PacketEntityAction 2026-04-01 19:06:19 -06:00
SamB440 6a46c359c4 Check if isPacketInventoryActive was false before allowing revert 2026-03-31 16:32:12 +01:00
Axionize 9b06c8670e Fix PE 26.1 update (deferred fabric folder) 2026-03-29 11:40:53 -04:00
Axionize f380fcd9af Bump PE for initial 26.1 support 2026-03-29 10:39:26 -04:00
AoElite f0e15b0521 add separate logic for publishing release builds in workflow 2026-03-27 05:33:49 -04:00
ManInMyVan bf10e23e8e spear stabs are attacks, handle them as such 2026-03-26 20:56:41 -06:00
NotAlexNoyle 598a44ed56 Fix PacketEvents API 2.11.2 on 1.19.4 for 1.8-1.21.11 clients 2026-03-25 07:55:01 -04:00
NotAlexNoyle 8897fc87e6 Use PacketEvents API 2.11.2 2026-03-25 07:45:13 -04:00
NotAlexNoyle 252deb7fd7 Merge latest version from upstream 2026-03-25 05:45:28 -04:00
Axionize 5efefe59ec Fix compilation issues, eliminate allocations and fix distanceSquared() > 36 instead of 6 2026-03-25 03:59:12 -04:00
Axionize 162a8d1b23 Merge branch '2.0' into lightning
# Conflicts:
#	common/src/main/java/ac/grim/grimac/checks/impl/combat/Reach.java
#	common/src/main/java/ac/grim/grimac/manager/CheckManager.java
#	common/src/main/java/ac/grim/grimac/player/GrimPlayer.java
2026-03-25 03:36:40 -04:00
Axionize 740f7f4aa7 Make DefaultResyncHandler a record 2026-03-25 03:28:58 -04:00
Axionize 5f9b9ddb65 Bump Kotlin Plugin Serialization 2.3.0 2026-03-25 03:28:32 -04:00
ManInMyVan e40ec015b9 footer text does not get parsed as markdown 2026-03-21 15:56:50 -06:00
ManInMyVan e954124802 check for keepsprint on 1.9 servers (#2070)
* check for keepsprint on 1.9 servers

* update held item in AttackCooldownHandler every time the slot is changed

* fix #2389

* change AttackCooldownHandler package
2026-03-21 15:46:58 -06:00
NotAlexNoyle 4a374d84be Merge pull request #1 from true-og/codex/rename-plugin-to-lightninggrim
Rename remaining branding to LightningGrim
2026-03-21 13:51:15 -04:00
ManInMyVan 598aae0ec8 require hand to be MAIN_HAND in PacketOrderB 2026-03-20 17:47:11 -06:00
ManInMyVan 48115eb7aa fix noslow with invalid drop item packets 2026-03-19 21:16:43 -06:00
ManInMyVan 68f34f2a31 make TransactionOrder verbose consistent with other checks 2026-03-18 20:26:23 -06:00
ManInMyVan 3055cd10de set CheckManager#packetEntityReplication on init 2026-03-18 20:20:54 -06:00
ManInMyVan 177c00355c rename BadPacketsC to SelfInteract 2026-03-18 20:15:14 -06:00
NotAlexNoyle f6df1b768d Rename remaining plugin branding to LightningGrim 2026-03-18 14:29:45 -04:00
ManInMyVan 539e822615 slightly change BadPacketsP description 2026-03-15 19:25:20 -06:00
ManInMyVan 29c0757bbb exempt spectators in 1.21.11+ from PacketOrderB (fixes #2493) 2026-03-15 18:38:18 -06:00
RareMen 13fb273501 Fix ghost block mitigation on floating block placement (#2530) 2026-03-12 23:58:37 -06:00
SamB440 574946391c Reset food level on respawn (#2528) 2026-03-11 17:25:47 +00:00
Axionize 0a1ce1d88b Fix encoding of Polymer blocks breaking when resyncing with world, optimize allocations Fabric PlatformChunk.getBlockId(x,y,z) 2026-03-10 20:42:26 -04:00
Cyramek 7e8b6b620e add support for viaversion attack range component for 1.21.11+ clients on legacy servers (#2474)
add support for https://github.com/ViaVersion/ViaVersion/pull/4733
2026-03-09 21:56:12 +01:00
cyramek 546abf07fb bump version to 2.3.74 2026-03-09 20:17:54 +01:00
Cyramek 8a0d1e274a account for 1.21.2+ clients ignoring teleport packets while in vehicle (#2470)
fix: https://github.com/GrimAnticheat/Grim/issues/2463
2026-03-09 19:53:29 +01:00
Cyramek 302bed1bc1 fix many issues with collisions while riding nautilus/happyghast (#2460)
thanks LaserSlime and Axionize for help!
2026-03-09 19:53:15 +01:00
Axionize 879d566c05 Add support disable-webhook-validation boolean in discord.yml for custom domains with Discord webhooks
Closes https://github.com/GrimAnticheat/Grim/issues/2520
2026-03-09 04:48:25 -04:00
Axionize d4611f1b31 discord.yml translation fixes 2026-03-09 04:37:22 -04:00
Axionize 53cf3e8e99 Fix compile issues from package references not being refactored in Discord refactor 2026-03-09 04:05:17 -04:00
Axionize c356f5fa4d Fix Discord Webhook PAPI Placeholders (#2518)
* Fix support for Discord markdown parsing of several edge cases

* Fix PAPI name mangling

* Cleanup Implementation of new Discord Webhook handling

* Document exploit choice to not use volatile when technically correct to

* Address all code review concerns

* Cleanup unused code

* Refactor packages for discord related data types
2026-03-09 03:58:17 -04:00
ManInMyVan d273229487 remove unused imports in Reach.java 2026-03-08 15:34:03 -06:00
ManInMyVan c7983b7e37 Revert "load after ZNPCsPlus"
This reverts commit 1d0cbb7078.
2026-03-08 15:33:53 -06:00
dxw1dandDawixTV 272a8892db Add Polish configuration file for GrimAC (#2501)
Co-authored-by: DawixTV <92529087+DawixTV@users.noreply.github.com>
2026-03-04 14:41:23 -05:00
Axionize 439477eff1 Merge PR #2267 - Improved Chinese Translations 2026-03-04 14:35:43 -05:00
Axionize 1f55742d76 Clean up typos and translator notes 2026-03-04 14:31:52 -05:00
Axionize b281a2d158 add support for attribute swapping to and from items with attack range components 2026-03-03 16:24:35 -05:00
Axionize 7d487a4279 history rewrite marker 2026-03-02 15:25:25 -05:00
Axionize f0fbc9db8c Fix getting hitbox for unstable scaffolding 2026-03-02 14:09:50 -05:00
Axionize 0a6ac7a47e Merge branch '2.0' into lightning
# Conflicts:
#	.github/workflows/build-and-publish.yml
#	common/src/main/java/ac/grim/grimac/checks/impl/combat/Reach.java
#	common/src/main/java/ac/grim/grimac/predictionengine/PointThreeEstimator.java
2026-02-26 03:02:12 -05:00
Axionize 4ca65b1670 Update Grim API to 1.2.4.0 2026-02-23 02:23:51 -05:00
AoElite 7be8f51267 bump grim-api version 2026-02-22 17:34:04 -05:00
ManInMyVan 1d0cbb7078 load after ZNPCsPlus 2026-02-22 10:24:30 -07:00
MatrixU5er b65b7f0f58 Improve comments in Chinese configuration file (#2490)
Updated comments in zh.yml for clarity and translation.
2026-02-15 13:34:45 +00:00
ManInMyVan 327b947c08 Add /grim testwebhook 2026-02-13 15:37:14 -07:00
CubicakeandManInMyVan 2b621483e7 Add method exposing int block type from CompensatedWorld (#2464)
* Implement feature

* Update common/src/main/java/ac/grim/grimac/utils/latency/CompensatedWorld.java

Co-authored-by: ManInMyVan <113963219+ManInMyVan@users.noreply.github.com>

* Add @Override annotation

---------

Co-authored-by: ManInMyVan <113963219+ManInMyVan@users.noreply.github.com>
2026-02-01 08:29:08 -05:00
runningbird2 f118b5c781 fix: honor per-check nomodifypacket (#2477) 2026-01-30 08:35:33 -07:00
ManInMyVan f95d877025 fix elytra packet abuse on 1.8 servers 2026-01-27 17:29:13 -07:00
Axionize 6c49a01853 Bump PE: Fixes Packet Ordering and mapping issues
- Previa packet mapping failing due to incorrect upstream call to getPacketVersion() instead of getClientVersion(), affects previa on all versions
- Fix Krypton + Via + Grim compat form listener relocation
2026-01-24 00:15:40 -05:00
SamB440 80afaae74f Evoker fangs are unhittable 2026-01-20 19:47:01 +00:00
Axionize 35174fdca9 Refactor Resolver Lifecycle 2026-01-18 16:56:10 -05:00
Axionize 88cfffc5d6 Rename Fabric1211PlatformServer -> Fabric12111PlatformServer 2026-01-18 16:56:10 -05:00
SamB440 cd86c14709 Don't let player reuse setback velocity (fixes #2445) (#2450) 2026-01-17 10:20:13 +00:00
cyramek b7a719dc2e revert this for now, until we find a better way to fix it 2026-01-15 15:31:52 +01:00
Ayou c04335773b Fix fast break false when switching tools via shulker box (#2454) 2026-01-14 13:33:34 +00:00
Axionize 9d1682d397 Rename Fabric mc1211 -> mc12111 module 2026-01-14 00:23:21 -05:00
Axionize fefe0d150f Fix early init of CommandManager on Bukkit causing startup issues 2026-01-13 16:44:45 -05:00
Axionize a54329ad45 Bump PE 2026-01-13 14:18:45 -05:00
Axionize 36590605b1 Update supported fabric versions 2026-01-13 14:17:55 -05:00
Axionize 075cec8c12 Fabric 1.21.11 support for fabric-permissions-api-v0 not being included 2026-01-13 02:34:42 -05:00
Axionize b9fa89ccb9 Refactor Fabric to make permissions API and cloud optional dependencies 2026-01-13 01:49:52 -05:00
Axionize 08770f0c6b Command API fallback handling 2026-01-12 21:42:01 -05:00
Axionize 50a25d9921 Bump API to no longer shade Player shim 2026-01-12 20:02:48 -05:00
Axionize 21f69886b8 Bump PE 2026-01-10 16:30:34 -05:00
Axionize 8001f6cb1d Fix support for messing legacy getGrimUser(Player player) method 2026-01-07 21:49:44 -05:00
Axionize c5855fd58d Unify all APIs 2026-01-07 21:17:45 -05:00
CyramekandAxionize bf8586393a 1.21.11 support (#2420)
* fix horse modern/legacy jumping mechanics, camel dash cooldown tracking (#2199)

* implement UseEffects component

fixes: https://github.com/GrimAnticheat/Grim/issues/2396

* initial support for hitbox margin

* compensableDashableEntities -> dashableEntities

* implement floating in water

* implement nautilus movement

todo: fix dashing, fix step

* fix 0.03 fluid pushing

this code was dropped in:
https://github.com/GrimAnticheat/Grim/commit/1a6a8c57583ef12556241383829eb5f7dcc3595f

* fix nautilus dashing

* improve precision while dashing

* actually fix dashing

I'm really dumb ngl, vanilla packet sequence:
1. rot packet with PLAYER look angle
2. vehicle move packet with ENTITY look angle

vehicle move packet overrides player's yaw and pitch from first rot packet

* implement all prediction engines for nautilus

I will refactor this later

* support new gliding behavior

* refactor nautilus prediction engines

* implement parched boundingbox

* add support for fast lava gameplay attribute in fluid pushing

* camels have variants now

* WIP tick-based world borders

* fix off ground camel dashing

thanks thisisatest8406

* add onGround tracking for entities

* revert tracking entity onGround

I decided that I don't care enough, camels are bugged as hell, everything desync

* remove unneeded diff

* fix pending jumps and camel off-ground dashing

* remove outdated comment regarding nautilus jumping logic

* Add support for max_reach AttackRange component

---------

Co-authored-by: Axionize <154778082+Axionize@users.noreply.github.com>
2026-01-07 11:25:27 -05:00
Axionize 95e2774f59 Add Back Support for Legacy API (#2409)
* Add back support for the legacy API temporarily

* Bump API to release version
2026-01-06 18:04:54 -05:00
Axionize 5c17fba090 Merge remote-tracking branch 'origin/2.0' into lightning 2026-01-06 15:12:16 -05:00
Axionize 02f6a3b51b Faster temporary fix for Folia breaking getTPS() 2026-01-06 15:03:45 -05:00
Axionize dbd75108c8 Temporary fix for Folia breaking getTPS() 2026-01-06 15:00:23 -05:00
Axionize 9cb45476ea Fix rot only accounting for reach/hitbox in 1.15 - 1.20.1 and 1.21.5 - 1.21.8 once and for all 2026-01-06 14:35:42 -05:00
Axionize be35b9b481 Update .gitignore 2026-01-05 15:10:21 -05:00
Axionize ca64aabaa2 Fix merge errors in hitbox patch 2026-01-05 15:08:41 -05:00
Axionize 801658600c Add forgotten util class file for hitbox fix 2026-01-05 15:06:23 -05:00
Axionize a2074d0fe4 Fix Coral hitboxes 2026-01-05 14:50:41 -05:00
Axionize 14573dace1 Fix Mushroom and Fungus Hitboxes 2026-01-04 21:00:20 -05:00
Axionize 639490a825 Attempt to fix disconnect.timeout cascade issues 2026-01-04 14:49:44 -05:00
Axionize 07d201004e improve FabricatedPlace patch 2026-01-04 14:49:44 -05:00
ManInMyVan 6186abc7e7 make CrashC verbose more consistent with other checks 2026-01-03 19:01:25 -07:00
cyramek 29d28c64fc refactor BlockEffectsResolver to accept movement list 2026-01-03 22:19:41 +01:00
Axionize 98fd065ddb Patch FabricatedPlace falses 2026-01-01 12:59:53 -05:00
Axionize d0b30ded6c Only update profiles if PLAYER_INFO_UPDATE contains ADD_PLAYER action 2026-01-01 10:56:32 -05:00
Axionize b79d5fdaaf Fix Sculk Vein replacement Phase/Simulation falses & Optimize canBeReplaced logic
- Added replacement logic for SCULK_VEIN (mirrors GLOW_LICHEN).
- Refactored sequential 'if' statements into an 'else-if' chain to improve branch prediction and allow early exits.
- Reordered block checks based on frequency (Slabs, Snow, and Vines are now checked first).
- Merged duplicate logic for multiface blocks.
2026-01-01 08:01:05 -05:00
Axionize 7c68947731 Add option to disable resyncing without API with a NoOp resync handler 2026-01-01 07:14:46 -05:00
Axionize 80f90e658f resync on netty thread instead of trying to send packets in region thread 2025-12-31 20:02:55 -05:00
Axionize 101473b913 Prevent torn reads and race condition on respawn on Fabric from reconstruction of ServerPlayerEntity 2025-12-31 16:22:35 -05:00
SamB440 42b6630c55 Use config history.database.port for database connection port. Fixes #2415 2025-12-31 20:51:54 +00:00
Axionize daa3bfe186 Merge branch '2.0' into lightning 2025-12-31 09:10:14 -05:00
Axionize 4292e93327 Merge branch 'fix/reach-infinite-expanding-hitbox' into 2.0 2025-12-31 09:03:06 -05:00
Axionize 60b70d1a11 Fix reach regression in not flagging clients from versions 1.15 to 1.20.1 due to rot only regression 2025-12-31 09:00:26 -05:00
Axionize ce0dad56ad Add PlatformPlayerFactory#getOfflinePlayers() 2025-12-30 07:38:44 -05:00
Axionize 90193b47d6 Stash work 2025-12-30 06:25:24 -05:00
cyramek 9e745fdba5 fix NPE due to incorrect initialization order 2025-12-29 19:49:20 +01:00
ManInMyVan d374c2bc08 support CAMERA packet (#2303) 2025-12-29 11:13:28 -07:00
ManInMyVan 2d8def6eec clean up OptifineFastMath.java a bit 2025-12-27 16:56:34 -07:00
Axionize 83794571d5 Merge remote-tracking branch 'origin/2.0' into lightning
# Conflicts:
#	.github/workflows/build-and-publish.yml
2025-12-24 02:22:13 -05:00
Rothes 8f0036e4e9 Cache map values for performance (#2407) 2025-12-24 02:14:44 -05:00
Axionize b6f9f86d11 chore: Bump PE 2025-12-21 06:24:39 -05:00
Axionize 55da3842f5 chore: Increase default building memory allocation 2025-12-20 00:24:11 -05:00
Axionize 955b725884 chore: Bump shadow 2025-12-20 00:20:14 -05:00
Axionize 96267128fc Fix: build ci changes 2025-12-20 00:16:07 -05:00
Axionize 92d06e4222 Bump PE 2025-12-20 00:02:35 -05:00
uRyanxD 5f02ccc4fe Fix NPE when Spigot late-bind option is enabled & improve injection logic
This PR fixes a NullPointerException that occurred when the Spigot "late-bind" option was enabled on servers prior to 1.14.4.

Changes:
- Changed injection priority: The plugin now attempts to register the Paper TickEndEvent first. It only falls back to NMS reflection if the Paper API is unavailable.
- Added a null check for the server connection instance to prevent the NPE during reflection injection.
- Added specific error logging: If injection fails on legacy servers (< 1.14.4), the plugin now reflectively checks `spigot.yml` settings to explicitly warn the user if `late-bind` is the cause.
- Refactored reflection logic to use `ReflectionUtils` for better safety and code cleanliness.
2025-12-19 23:54:00 -05:00
Axionize f772b745cc ci: make runner configurable to work around Tenki issues (#2398)
Tenki's self-hosted runners started failing on Dec 19, 2025 with
"Invalid URL" errors during artifact upload. This adds a configurable
runner input to all workflows, allowing easy switching between
GitHub-hosted and Tenki runners.

- Add `runner` input to reusable build workflow (default: ubuntu-latest)
- Expose runner option in gradle-publish.yml for manual runs  
- Explicitly set runner in release.yml
- Bump upload-artifact v4 → v6
2025-12-19 23:22:02 -05:00
Axionize 9bd12cc7a2 Bump github actions-upload-artifact versions 2025-12-19 18:08:30 -05:00
iLemon 32f607b597 Fix gliders not working in offhand slot (#2397) 2025-12-19 03:28:29 -05:00
Axionize c2afc62241 fix: bump gradle wrapper to fix build 2025-12-17 05:49:51 -05:00
Axionize 97b875860f build: add initial support for 1.21.11 and update dependencies
- Update GitHub workflow game-versions to >=1.21.11
- Update Cloud Command Framework (Paper: beta.14, Fabric: beta.15)
- Update Fabric Permissions API to 0.6.1
- Update Fabric Loom to 1.14.6
- Update Shadow Gradle plugin to 9.0.0 (stable)
2025-12-17 01:37:50 -05:00
Axionize c019566255 fix: Bubble Column, Azalea, Bed, and Ceiling Wall Sign hit/collision boxes 2025-12-17 00:42:32 -05:00
Axionize 12f6af3715 Bump PE 2025-12-17 00:42:32 -05:00
Axionize 965008f7e1 build: Refactor repo logic to support mavenLocalOverride
- Implements a `RepositoryHandler.exclusive` extension in buildSrc to handle repository priority.
- Fixes an issue where strict `exclusiveContent` blocks prevented `mavenLocal()` from being checked even when `-PmavenLocalOverride=true` was active.
- Ensures efficient dependency resolution: checks Local (if enabled) -> Remote -> Stop, preventing leaked HTTP requests.
- Reduces boilerplate code across common, bukkit, and fabric build configurations.
- Adds overload support for existing repositories (e.g., mavenCentral) within exclusive blocks.
2025-12-17 00:42:32 -05:00
Axionize 4a104e287c fix: exclude Azalea from Sapling hitbox definitions
Azaleas were incorrectly treated as saplings giving them an incorrect hitbox. This removes them from the sapling list in HitboxData so they fall back to the correct default collision box.
2025-12-17 00:42:32 -05:00
AoElite 0153ce3a4d Revert "fix incorrect EquipmentSlot mappings in isGlider logic"
This reverts commit d96d683b14.
2025-12-14 05:01:46 -05:00
AoElite d96d683b14 fix incorrect EquipmentSlot mappings in isGlider logic 2025-12-14 04:56:42 -05:00
AoElite acf273fd1f adjust isGlider logic to account for items that can't be depleted 2025-12-14 04:49:16 -05:00
AoElite 679e869a00 adjust canGlide logic to try to improve compatibility 2025-12-14 04:37:35 -05:00
Axionize 324469a78a Merge remote-tracking branch 'origin/2.0' into lightning 2025-12-11 14:32:13 -05:00
AoElite a1b4893d88 fix null pointer when checking if bread was a setback 2025-12-07 22:12:38 -05:00
NotAlexNoyle e4c4642984 Fix compatibility with TrueOG Bootstrap 2025-12-07 21:26:44 -05:00
SamB440 4d28f85665 More setback changes 2025-12-06 13:24:27 +00:00
AoElite 0d22e34c5a refactor BlockBreakSpeed class for improved maintainability 2025-12-05 08:41:41 -05:00
AoElite 2d053d0923 update lunar's brand format in all the configs 2025-12-05 06:01:34 -05:00
AoElite 3bf3226fa5 make discord webhook pattern stricter and support different api versions 2025-11-29 06:13:36 -05:00
Nils Gereke bf637f5fb3 feat: discord forum channel support (#2380) 2025-11-28 22:25:53 -07:00
NotAlexNoyle 3ae1e32813 Downgrade gradle to 8.14.3, remove fabric support, update README.md 2025-11-28 09:25:26 -05:00
Axionize e2e25206ec Update packetevents 2025-11-27 17:38:23 -05:00
Nils Gereke c2c044f61b feat: support for postgresql (#2368)
* feat: support for postgresql
feat: fabric now includes jdbc driver

* fix: missing auto commit

* chore: remove database drivers

* feat: update de and en configs

* feat: support for other languages
2025-11-25 10:31:46 -05:00
Axionize 2d785fdc2c Bump PacketEvents version 2025-11-23 18:59:59 -05:00
Axionize 8f3b7db844 Merge remote-tracking branch 'origin/2.0' into lightning
# Conflicts:
#	.github/workflows/build-and-publish.yml
#	.github/workflows/codeql-analysis.yml
#	bukkit/src/main/java/ac/grim/grimac/platform/bukkit/events/PistonEvent.java
2025-11-23 18:49:49 -05:00
Nils Gereke 403fd36830 fix(fabric): world isLoaded throws method not found (#2372)
fix(fabric): world name is always 'world'
2025-11-23 18:14:52 -05:00
Greened e386b936ff Fixed Reach false positive with Happy Ghast and clients under 1.21.6 (#2352) 2025-11-22 06:14:32 -05:00
GroupXyz 3fc985c8f3 Fix spectate command resetting gamemode when spectating someone in another dimension (#2365)
* Fix gamemode change on dimension change 

This issue happened when using the spectate command on a player in another dimension. the spectator was set to survival mode.

* Fix player gamemode reset bug

When spectating a player in another dimension, you would previously get set to survival mode, this is now fixed.
2025-11-22 06:11:15 -05:00
ManInMyVan 499bf7e2f9 more consistent issue templates 2025-11-19 17:02:06 -07:00
ManInMyVan ba91251307 improve bypass template 2025-11-19 16:39:03 -07:00
SamB440 8ddd7f18a5 Setback changes 2025-11-19 13:42:40 +00:00
cyramek 3f67272721 optimise bounding box calculations 2025-11-18 23:04:02 +01:00
AoElite 3380d7f9fd remove some unnecessary publishing paths 2025-11-18 11:14:20 -05:00
tenki-cloud[bot] e1bd2edc7c ci: migrate workflows to tenki (#2362)
* chore: migrate runner to tenki

* chore: migrate runner to tenki

---------

Co-authored-by: tenki-cloud[bot] <204909232+tenki-cloud[bot]@users.noreply.github.com>
2025-11-18 11:00:08 -05:00
Axionize 9b500a2617 Bump PacketEvents version 2025-11-13 03:39:44 -05:00
Weigurde 6df484bcc6 Update CrashC.java (#2350) 2025-11-11 17:10:24 -07:00
cyramek a46d29620f make sure that we use packet-based inventory state after closing the inventory 2025-11-09 23:28:48 +01:00
ManInMyVan b14628c578 hopefully make installation instructions more clear
also moves it higher on the page
2025-11-09 11:39:53 -07:00
AoElite 5c715315c9 tweak GrimDump output and include some non-default alert settings 2025-11-09 06:34:07 -05:00
AoElite ba1d1dbc16 small piston optimization 2025-11-09 05:39:44 -05:00
Axionize 0d67045f66 Include build number on modrinth 2025-11-08 19:27:49 -05:00
Axionize 49742a9ddc Fix build increment conditional 2025-11-08 18:32:22 -05:00
Axionize 612e0f8f67 Fix permissions for increment build number creating tags/refs 2025-11-08 18:18:52 -05:00
Axionize 5d4af9365b Forget to make workflows run on lightning branch 2025-11-08 17:41:56 -05:00
Axionize 5489f118c1 Fix buildscripts again 2025-11-08 17:40:38 -05:00
Axionize ded5c0f320 Restructure to help upstream forks.
People have started building forks of LightningGrim...

I never intended for this but regardless to help them I'll stop rebasing and use merges only.
2025-11-08 17:37:59 -05:00
Axionize 238b77c002 Fix buildscripts 2025-11-08 17:18:09 -05:00
Axionize 71d93c97cf Minor build fixes after rebase on upstream 2025-11-08 17:12:49 -05:00
Axionize 43f4c7fcd7 README updates for LightningGrim 2025-11-08 17:12:49 -05:00
Axionize 03714c9b97 Fix compile issues from rebasing upstream 2025-11-08 17:12:49 -05:00
Axionize 004804c09e Potentially fix LightningGrim-specific vector culling Reach False? 2025-11-08 17:12:49 -05:00
Axionize 853d02b9c8 Temp stash 2025-11-08 17:12:49 -05:00
Axionize 2272d4df16 Optimize Bukkit PistonEvent() usage 2025-11-08 17:12:49 -05:00
Axionize fa2dc7b00b * Remove space in names of new checks:
* "Wall Hit" -> "WallHit"
    * "Entity Pierce" -> "EntityPierce"
* Fix modeling experience orb as unhittable entity
* Fix sending hitbox with /grim hitboxdebug in the hitboxdebug handler
* Update upstream to 35811fe551
2025-11-08 17:12:49 -05:00
Axionize c8347ed146 Heavy memory use optimization
- 2.2x reduction in queued task overhead
- reduction in size of queued single block changes
- up to 8x reduction in size of queued multi block changes
    - fixed blocks disappearing in B73 caused by incorrect shifting in new block udpate handler
2025-11-08 17:12:49 -05:00
Axionize c94260bea6 Buildscript Update & Dirty Painting Hack
- Update gradle buildscript to not include branch-name if branch is called "lightning"
 - Update GitHub Actions to automatically include changelog and to rename the jar LightningGrim-${platform}-${version}.jar
 - Mark Paintings as entities that cannot be hit to prevent EntityPierce (formally HitboxEntity) falses.
2025-11-08 17:12:48 -05:00
Axionize bde2328511 Update buildscript
Notes:
- Mark actions builds automatically as alpha on Modrinth
- Shorten Fabric version string to be same as Bukkit on Modrinth
2025-11-08 17:12:48 -05:00
Axionize b87eeaf490 Cull dead entities from canHit() 2025-11-08 17:12:48 -05:00
Axionize 3abc904590 Add support for Hitbox Debugging 2025-11-08 17:12:48 -05:00
Axionize 1b416fc841 - Re-add HitboxBlock and HitboxEntity (renamed to WallHit and EntityPierce)
- Reduce unnecessary reach vectors used for calculations in versions
2025-11-08 17:12:48 -05:00
Axionize 224f464eff Add Inventory checks 2025-11-08 17:12:48 -05:00
Axionize ac35735fff Runtime startup check of PE version 2025-11-08 17:12:48 -05:00
ManInMyVan def21633e2 disable ExploitB "invalid page characters" check 2025-11-03 19:54:02 -07:00
ItzBloxxy 918e6575e8 New translation ro.yml (#2285)
Config Romanian translation for grim ac.
2025-11-03 17:02:42 +00:00
SamB440 ea1f1615b6 Disable PacketOrderP, fixes #2327 2025-11-03 16:59:53 +00:00
SamB440 c323cf06ce Bump cloud to beta.13, fixes #2318 2025-11-03 16:30:53 +00:00
SamB440 0ba331780e Don't simulate movement in unloaded chunks?
Who knows what other problems this could cause...
2025-11-03 16:26:30 +00:00
ManInMyVan fc47c5b663 refactor via 1.11->1.10 chat hook 2025-11-01 17:10:11 -06:00
ManInMyVan 17cf067d58 fix #2330 2025-11-01 16:05:03 -06:00
ManInMyVan 6280b28e16 show size for applicable reach/hitbox flags 2025-11-01 12:23:17 -06:00
Axionize bfd2bd0e22 Reorder publication of artifacts to Modrinth and update supported versions 2025-10-31 22:29:16 -04:00
ManInMyVan 0df77dd32a slight cleanup of fabric code 2025-10-31 14:59:26 -06:00
ManInMyVan b373e5f1c4 switch to mojmap for fabric
see https://fabricmc.net/2025/10/31/obfuscation.html
2025-10-31 14:38:23 -06:00
Szypkoo 115c32916f fix: useless player fetch (#2329) 2025-10-31 11:03:27 +00:00
ManInMyVan 5f8692deda refactor plugin message wrappers 2025-10-30 17:32:01 -06:00
SamB440 f8a5315fff We do not need high bound if player ticks reliably. 2025-10-28 13:27:54 +00:00
AnnotationVisitor 102b5842e7 Add copper golem statue, shelf collision boxes and other missing 1.21.10 stuff (#2315)
* add copper golem statue

* Include ViaVersion substitution

* add shelves

* also add copper armor

* add mannequin box sizes

* Add copper golem size
2025-10-27 11:17:09 -06:00
ManInMyVan a76bf9ce09 add copper golem statue collision box 2025-10-26 19:01:29 -06:00
ManInMyVan 4f3e715f94 add shelf collision box 2025-10-26 18:41:25 -06:00
ManInMyVan 1b47e31776 allow up to one PLAYER_INPUT per tick (#2306) 2025-10-26 11:54:15 -06:00
BT (calcastor/mame) b69906f809 Clear active item before updating inventory on legacy (#2317)
* Apply spotless

* Clear active item before updating inventory on legacy
2025-10-23 22:56:12 -06:00
ManInMyVan 9ba4052c35 possibly fix build 2025-10-23 00:51:27 -06:00
ManInMyVan a86bf57a39 mark MultiActionsD as stable 2025-10-23 00:49:50 -06:00
ManInMyVan a82f2b888a mark MultiActionsC as stable 2025-10-23 00:49:23 -06:00
ManInMyVan 89cf181772 mark PositionBreakB as stable 2025-10-23 00:43:47 -06:00
ManInMyVan 6e5ec332dd remove double space in ChatB 2025-10-23 00:40:59 -06:00
ManInMyVan 79bf7e4fd1 mark ElytraF as stable 2025-10-23 00:39:32 -06:00
ManInMyVan d0aafdd9f7 mark ElytraA as stable 2025-10-22 18:06:48 -06:00
ManInMyVan 3f19abc859 mark ElytraC as stable 2025-10-22 18:06:29 -06:00
ManInMyVan 8a6863b9d3 mark ElytraB as stable 2025-10-22 17:47:05 -06:00
Axionize 26492fd8e3 Optimize new Vector3dm() multiply calls 2025-10-20 23:23:39 -04:00
Axionize 15e809835b Optimize new object creation on vector add() calls 2025-10-20 23:15:23 -04:00
Axionize 1eae5484c7 Micro optimize DefaultResyncHandler 2025-10-20 22:56:11 -04:00
Axionize 90036e2bf2 Optimize Messages (#2309)
* Optimize MessageUtil.replacePlaceHolders()

* Send user messages at packet level bypassing backend server for performance

* Stash temporary changes

* Make Grim send fast chat messages bypasses platform by default
2025-10-20 22:16:21 -04:00
Axionize faf2fb3afb Refactor Grim startup/system args handling 2025-10-20 21:48:49 -04:00
cyramek 7461aca13f fix offground precision loss <= 1.20.4
we're now in 1e-15 average
2025-10-20 19:26:05 +02:00
AoElite f381805cad add translations for the sender-not-found message 2025-10-20 12:25:34 -04:00
AoElite 2fa022eb00 fix GrimDebug command exception if the sender is exempt or not found 2025-10-20 12:25:10 -04:00
cyramek 44900e584b support copper variants of chains 2025-10-20 10:06:45 +02:00
cyramek a2f4dc1979 get rid of a buffer in vector format conversion 2025-10-19 09:18:29 +02:00
Cyramek 0237f956ff [1.21.10] Rewrite block effects, LpVector support, partially implemented collision boxes (#2300) 2025-10-18 11:24:15 +02:00
ManInMyVan e222df8e87 check if we're already sending a bundle packet 2025-10-15 16:50:39 -06:00
ManInMyVan bbab7e222c require CHUNK_BATCH_ACK be sent only when it's supposed to be sent 2025-10-15 15:26:40 -06:00
Axionize 41c7e41a0e Fix Fabric Auto JiJ script 2025-10-15 12:44:15 -04:00
Axionize 5551ef68e1 More fastutil micro optimizations 2025-10-14 19:40:14 -04:00
Axionize ca6e83de4d Use FastUtil Map for violations 2025-10-14 19:37:00 -04:00
Axionize 4d6ac75c73 Fix ClassNotFoundException with new ChatB Via rewriter hook 2025-10-14 17:52:00 -04:00
ManInMyVan 1317ae0bb4 add 2 more rules to .editorconfig 2025-10-14 13:40:20 -06:00
ManInMyVan d538c59a79 CHUNK_BATCH_ACK is async 2025-10-12 15:43:52 -06:00
ManInMyVan 6a223a30c7 don't specify baby scale factor twice 2025-10-12 12:58:00 -06:00
cyramek 2e1c79b4e3 correct bounding box calculations for baby turtles, happy ghasts, dolphins, armadillos, and camels 2025-10-12 20:21:58 +02:00
SamB440 bb143d7993 cancelLerp fixed in 1.21.9, apparently 2025-10-12 17:58:14 +01:00
SamB440 322d2567c0 Bump Gradle to 9.1.0 2025-10-12 17:44:58 +01:00
AoElite 8d7a4cce87 ignore unhittable entities for block placements 2025-10-12 01:21:02 -04:00
ManInMyVan 832194a1e9 fix #2294 2025-10-11 18:46:37 -06:00
ManInMyVan 0321e0bceb fix some warnings 2025-10-11 12:32:57 -06:00
ManInMyVan 63854605b6 slightly more configurable webhook embed 2025-10-11 12:11:03 -06:00
AoElite 5c4602188e allow the embed images to be customizable and show grim's version in the footer 2025-10-11 08:55:29 -04:00
AoElite 163e120f00 any 200 status codes are acceptable 2025-10-11 08:22:32 -04:00
AoElite ce6e1d3e9f ensure the short commit is used 2025-10-11 08:22:02 -04:00
Axionize f6024a4308 build(workflow) Fix not attempting to push to Grim publishing API on fork build 2025-10-10 18:33:08 -04:00
Axionize b4d031c81e build(workflow) Fix changelog generation 2025-10-10 18:30:32 -04:00
Axionize d7cb4aa4e3 Allow builds to run in forks without attempting to publish to modrinth 2025-10-10 18:16:35 -04:00
Axionize b85c65d623 Improve build performance by explicitly specifying repositories 2025-10-10 18:01:29 -04:00
ManInMyVan c208824b16 rewrite BukkitItemResetHandler & add isUsingItem() & docs 2025-10-10 13:50:38 -06:00
ManInMyVan 7f6b7d0bfa forgot 1.21.9/10 2025-10-10 10:00:29 -06:00
ManInMyVan a56e99e3e2 support latest spigot in BukkitItemResetHandler 2025-10-10 09:55:17 -06:00
SamB440 9e7dabea34 Now fix geyser dependency 2025-10-10 15:34:12 +01:00
SamB440 42c6d08ff1 Fix silent failing on build 2025-10-10 15:27:53 +01:00
SamB440 0bbf472c55 Split version compute and build steps 2025-10-10 15:16:56 +01:00
Axionize d9e279a949 Undisable fabric by accident and minor cleanup 2025-10-10 02:41:38 -04:00
Axionize cccc5fdfb6 Optimize Bukkit dependency fetching 2025-10-10 02:40:20 -04:00
Axionize a7bdc869d4 Improve performance and cleaniness of downloading dependencies 2025-10-10 02:33:10 -04:00
Axionize 513633c645 Bump dependencies 2025-10-10 01:31:00 -04:00
ManInMyVan c3eb0e4196 allow multiple instances of TridentItem 2025-10-09 16:24:46 -06:00
ManInMyVan 7137b36d62 remove unnecessary parenthesis on folia scheduler tasks 2025-10-09 16:23:45 -06:00
ManInMyVan 2c0b4a2a60 make MovementTickerLivingVehicle#movementInput protected 2025-10-09 16:11:07 -06:00
ManInMyVan 8f437d6bec clean up some comments and spacing 2025-10-09 16:10:20 -06:00
ManInMyVan c0bd2dd685 make FabricPermissionRegistrationManager#fabricSenderFactory private 2025-10-09 16:08:16 -06:00
ManInMyVan 258d94b525 make LastInstance#lastInstance private 2025-10-09 16:07:53 -06:00
ManInMyVan 6a02c93204 use GrimMath.radians instead of manual conversion 2025-10-09 16:07:33 -06:00
ManInMyVan 0c86d017ac deobf some methods in PredictionEngineBoat 2025-10-09 16:06:05 -06:00
ManInMyVan 6d3aa4784d move bstats plugin id to a constants class 2025-10-09 16:05:40 -06:00
ManInMyVan cec4a85454 make BlockPlaceSnapshot a record 2025-10-09 16:05:07 -06:00
ManInMyVan 438e1accf9 fix build 2025-10-09 16:04:04 -06:00
ManInMyVan f9fff4ee0c make VehiclePositionUpdate a record 2025-10-09 16:02:52 -06:00
ManInMyVan b102afb8c8 undeprecate BukkitConversionUtils#fromBukkitFace(BlockFace) 2025-10-09 16:01:27 -06:00
ManInMyVan ef0ea352ad don't use Preconditions for null-checking 2025-10-09 16:00:53 -06:00
ManInMyVan e36e61987d remove unused class TaskWrapperHandle 2025-10-09 15:59:45 -06:00
ManInMyVan 3cf06bf00b use lombok in some places 2025-10-09 15:59:02 -06:00
ManInMyVan 7b243d3a44 use jetbrains nullability annotations 2025-10-09 13:59:48 -06:00
ManInMyVan d24726c7df less messy build script 2025-10-09 13:44:41 -06:00
ManInMyVan 1bb95e796d slight cleanup (mostly weakening package-private) 2025-10-09 13:33:49 -06:00
SamB440 642e580e61 Fix BLOCK_INTERACTION_RANGE creative mode on < 1.20.5 2025-10-09 20:14:31 +01:00
ManInMyVan 8884b0a126 bump version 2025-10-09 10:40:02 -06:00
ManInMyVan 7c5aae956e fix 1.9 sloped rail height 2025-10-07 22:15:56 -06:00
ManInMyVan 0e6d116ec6 nullability annotations & deobfing 2025-10-07 15:35:30 -06:00
ManInMyVan 141ccae1c2 deobf getNearestLookingDirections() 2025-10-07 14:51:12 -06:00
ManInMyVan 662d02cc2a xRot & yRot -> yaw & pitch 2025-10-07 14:10:37 -06:00
ManInMyVan c42217375b fix a few warns 2025-10-07 14:03:28 -06:00
ManInMyVan 258ef064ba make ExplosionHandler#firstBreadMap private 2025-10-07 14:03:12 -06:00
Axionize 12e79a1ea0 Implement API v1.2.0: Improve EventBus usability and fix reflection bugs
This update contains numerous fixes and improvements to the API, focusing on developer experience and robustness.

feat(api): Simplify EventBus registration with context resolvers
- Removes the requirement for developers to manually construct a GrimPlugin instance to use the EventBus.
- The new GrimExtensionManager uses platform-specific resolvers to automatically determine the context from common object types, reducing boilerplate.
    - On Bukkit, developers can now pass:
      - A `Plugin` instance (e.g., `this` from their main plugin class)
      - Any `Class` from the plugin's JAR (e.g., `MyListener.class`)
      - The plugin's name as a `String`

    - On Fabric, developers can now pass:
      - A mod's entrypoint instance (e.g., `this` from an Initializer)
      - The mod ID as a `String`
      - A `ModContainer` instance
      - Any `Class` from the mod's JAR

fix(api): Correctly invoke reflected event listeners
- Fixes a critical bug causing a `ClassCastException` for listeners registered via `registerAnnotatedListeners`. Listeners are now correctly fired only for their declared event type. `subscribe()` remains the recommended registration method.

perf(api): Optimize invocation of reflected listeners
- Slightly improves performance of reflected listeners by removing a redundant runtime null-check from the hot path of every event call.

refactor(core): Moved concrete EventBus implementation from API to common module
- The `OptimizedEventBus` implementation has been relocated to the `common` module to better separate the public API contract from its internal logic.
2025-10-06 06:26:16 -04:00
Axionize e0093897dc Make devvelocity build scan integration only run on --scan being applied; Update foojay resolver convention; 2025-10-06 06:26:06 -04:00
ManInMyVan 8d1ee003d9 small cleanup (mostly making things private & final) 2025-10-05 12:41:18 -06:00
ManInMyVan aa4c6e0f1d add reset-item-usage-on-item-use 2025-10-04 12:38:32 -06:00
Axionize 0fd2a96aca Even more robust sanitization 2025-10-03 02:12:13 -04:00
Axionize d15a2dd23a Buildscript patches 2025-10-03 01:18:33 -04:00
Axionize 0095f8acc1 Fix using secrets for MODRINTH_ID in workflows again 2025-10-03 01:06:02 -04:00
Axionize b824ce107d Fix using secrets for MODRINTH_ID in workflows 2025-10-03 00:51:19 -04:00
Axionize 50f3d84207 Use secrets for MODRINTH_ID 2025-10-02 07:28:52 -04:00
Axionize a26951cc95 Fix escpaing some stings in preparing final changelog 2025-10-02 07:16:37 -04:00
Axionize b72485da7e Print out build scan url not found when --scan enabled and url not found 2025-10-02 06:05:26 -04:00
Axionize 2f8a4738b3 Remove -q from printVersion so when --scan is applied logs print out 2025-10-02 06:03:46 -04:00
Axionize a74e10e888 Build script fix 2025-10-02 05:38:27 -04:00
Axionize dda13325cf Bugfix 2025-10-02 05:22:42 -04:00
Axionize 2df4c23bf2 Update buildscript to be more flexible; add support for gradle build scans 2025-10-02 05:18:00 -04:00
Axionize 8f785564d7 Run workflow on .github/workflows/** update 2025-10-02 04:48:40 -04:00
Axionize cbc2f51a0c Update new release/alpha build workflow 2025-10-02 04:43:56 -04:00
Axionize 3ac26d0601 Modify grim startup update check failure message 2025-10-01 01:02:24 -04:00
ManInMyVan cc10288e0d fix segmented hitboxes 2025-09-30 15:27:03 -06:00
ManInMyVan c10e74cd72 fix leaf litter hitbox 2025-09-29 18:02:20 -06:00
AoElite fb926ab0fb specify java version fpr codeql 2025-09-28 11:50:30 -04:00
AoElite 4be254939d update readme 2025-09-28 11:15:10 -04:00
AoElite 40a4c081c7 fix repo property including org 2025-09-28 10:27:05 -04:00
AoElite 1f16d7b0de correct formatting for conditional checks in gradle-publish workflow 2025-09-28 10:21:29 -04:00
AoElite 8d25519ace add build properties to dump 2025-09-28 10:11:58 -04:00
AoElite fefaa57044 fix workflow 2025-09-28 10:11:50 -04:00
AoElite 01ff3ecea7 exclude pull requests 2025-09-28 09:59:11 -04:00
AoElite f54ceb6531 remove unused mainBranch property from BuildConfig 2025-09-28 09:55:18 -04:00
AoElite 18d1602b3a use GitHub's default branch instead of assuming 2.0 2025-09-28 09:54:56 -04:00
AoElite c90d7ab941 fixed some minor build warnings 2025-09-28 09:02:41 -04:00
AoElite c428bf65be add a build properties file to include more details about the build 2025-09-28 08:25:57 -04:00
AoElite 088621faff simplify version extraction 2025-09-28 05:12:04 -04:00
AoElite 2a5979cc3c use different approach in Gradle version extraction in the publishing workflow 2025-09-28 04:49:32 -04:00
AoElite 9618912072 bump lombok plugin to 9.0.0 2025-09-28 04:39:13 -04:00
AoElite a0bd388109 update the checkout action to v5 in CodeQL workflow 2025-09-28 04:36:41 -04:00
AoElite 2403bc5df7 update gradle publishing actions, remove action dependency requirement, disabled building and publishing of lite 2025-09-28 04:36:01 -04:00
AoElite 9b2b080d1e enable parallel building 2025-09-28 04:33:37 -04:00
AoElite b70abbe6cd disable CodeQL workflow schedule 2025-09-28 04:25:38 -04:00
Axionize 293f6ea8b1 Add repo.grim.ac for 1.16.1 so fabric-permissions-api will be downloaded 2025-09-18 15:52:40 -04:00
Pablo Herrera fe7a263b01 Optimize away entity collision check in 1.8 (#2269) 2025-09-17 15:26:59 -06:00
SamB440andcyramek cb13221b8d Revert pre via (#2280)
* revert pre-via part 1

* don't register checks multiple times

* revert pre-via part 1

* don't register checks multiple times

* fix merge conflicts

---------

Co-authored-by: cyramek <xcyramek@gmail.com>
2025-09-17 18:07:34 +01:00
AoElite c2a07bd6f1 add back workflow paths 2025-09-16 11:45:33 -04:00
AoElite 184050bfc6 remove secret requirement 2025-09-16 11:17:47 -04:00
AoElite e368bc88ad tweak workflow 2025-09-16 11:14:22 -04:00
AoElite becbba3647 remove workflow paths 2025-09-16 11:07:47 -04:00
AoElite db92c6587e update workflow 2025-09-16 11:02:18 -04:00
AoElite a02f49ef87 update packetevents 2025-09-16 05:45:02 -04:00
AoElite 846e9ad4ed ensure fabric uses our packetevents fork 2025-09-16 03:20:04 -04:00
EternalHuman 57a9f8f432 Fix FastBreak falses white riding entity (#2273) 2025-09-08 23:11:00 -06:00
cyramek b7c5837305 only do NoSlow checks if trident is not enchanted with Riptide
This is not vanilla, but this is what the legacy handler does. To implement this properly, we would need to track biomes AND skylight, which is very memory-expensive.

fix: https://github.com/GrimAnticheat/Grim/issues/2259
2025-09-07 22:42:27 +02:00
tjshtqwq f54ca0e231 Update zh.yml 2025-09-06 17:11:33 +08:00
tjshtqwq 75b5405160 Merge branch '2.0' into patch-1 2025-09-06 16:59:44 +08:00
ManInMyVan f739b1e396 remove unused imports 2025-09-05 10:24:25 -06:00
tjshtqwq c2acfae296 Update translation 2025-09-05 22:43:31 +08:00
ManInMyVan 070116b037 PacketServerTeleport/PLAYER_POSITION_AND_LOOK: read event after getting player 2025-09-04 17:58:41 -06:00
ManInMyVan a8693c300e PacketServerTeleport/VEHICLE_MOVE: read event after getting player 2025-09-04 17:45:12 -06:00
Ayou 0586ea5bf6 Fix riptide velocity (#2262) 2025-09-04 07:50:13 -06:00
ManInMyVan bd4cd314c6 remove GrimPlayer#getInventory() 2025-09-03 22:53:57 -06:00
ManInMyVan 0bf3a21677 protected -> private in final class 2025-09-03 16:56:54 -06:00
ManInMyVan dc030603c8 deobf FluidFallingAdjustedMovement.getFluidFallingAdjustedMovement(...) 2025-09-03 16:52:08 -06:00
ManInMyVan 4e1320f032 remove GrimPlayer#isEyeInFluid(FluidTag) 2025-09-03 16:44:55 -06:00
ManInMyVan 9f4c321f44 make Dripstone.update(...) void 2025-09-03 16:42:08 -06:00
ManInMyVan 2b037554fb use GrimMath.radians in JumpPower 2025-09-03 16:39:39 -06:00
ManInMyVan 64ebdf21b2 make MainSupportingBlockData a record 2025-09-03 16:39:25 -06:00
ManInMyVan e26cf0f73d clean up EntityPredicates 2025-09-03 16:32:27 -06:00
ManInMyVan 42d247b641 simplify ViaVersionUtil 2025-09-03 16:20:55 -06:00
ManInMyVan ea5ed0610c move ComprensatedInventory instance to GrimPlayer 2025-09-03 16:08:11 -06:00
ManInMyVan 3f1755722c replace ChannelHelper.runInEventLoop with GrimPlayer#runSafely 2025-09-03 15:37:49 -06:00
ManInMyVan d2a8dceb2d don't use deprecated method ItemStack.getEnchantmentLevel(EnchantmentType, ClientVersion) 2025-09-02 17:45:55 -06:00
ManInMyVan f87a077e23 make Ray fields final 2025-09-02 17:36:41 -06:00
ManInMyVan 9ea389bc6a fix RunningMode field modifiers 2025-09-02 17:33:07 -06:00
ManInMyVan 566dc9bf3b mark GrimMath methods as pure 2025-09-02 17:28:51 -06:00
ManInMyVan b44dd89136 normalize trighandler param names 2025-09-02 17:26:31 -06:00
ManInMyVan eddb14b92c cache default input value; reduce memory usage for pre-1.21.2 players 2025-09-02 16:58:12 -06:00
ManInMyVan 21ef46a291 reduce runnables with world borders 2025-09-02 16:51:09 -06:00
ManInMyVan cecf5fbab2 make PacketStateData.isSlowedByUsingItem() based on slowedByUsingItemSlot
the slowedByUsingItem is redundant
2025-09-02 16:31:29 -06:00
ManInMyVan b37393c75d PacketStateData.eatingHand -> itemInUseHand 2025-09-02 16:28:44 -06:00
ManInMyVan aab6908da0 only set NoSlow.didSlotChangeLastTick if the slot actually changed 2025-09-02 16:26:29 -06:00
ManInMyVan d71239bb22 use getItemInHand in PacketPlayerDigging 2025-09-02 16:24:27 -06:00
ManInMyVan 345502b43c fix deprecated method call ItemStack.getEnchantmentLevel(EnchantmentType, ClientVersion) 2025-09-02 16:20:38 -06:00
ManInMyVan a7f8c7790e get item in PacketPlayerDigging.handleUseItem instead of it being passed 2025-09-02 16:17:39 -06:00
ManInMyVan ff9af985bf don't allow sending OFF_HAND in USE_ITEM to bypass noslow 2025-09-02 16:13:38 -06:00
AoElite dbac8bc43e add GrimList command for debugging exemptions 2025-08-31 12:41:14 -04:00
AoElite 4702add980 reduce the number of tasks for updating permissions 2025-08-31 10:21:09 -04:00
AoElite 090d1a843b add some utilities 2025-08-31 10:12:29 -04:00
AoElite 8f1e993913 fix CrashH false 2025-08-31 03:04:46 -04:00
SamB440 17a8d3b3c9 Add support for SET_PLAYER_INVENTORY packet 2025-08-28 19:58:52 +01:00
ManInMyVan 1c46546c15 remove unused imports in ChatA/B 2025-08-27 17:56:21 -06:00
ManInMyVan 97c1bddc16 make PacketPlayerDigging pre-via: fix #2250
also move it before PreViaCheckManagerListener
2025-08-27 17:56:08 -06:00
AoElite b997b6b10f allow platform to be configured via system argument 2025-08-22 23:04:06 -04:00
ManInMyVan 28d83206c1 more readable PredictionEngineElytra.applyInputsToVelocityPossibilities 2025-08-16 17:24:05 -06:00
ManInMyVan 6320fa68ce fix(PredictionEngineElytra): use stuckSpeedMultiplier instead of 0.99, 0.98, 0.99 2025-08-16 17:10:16 -06:00
ManInMyVan 0340e07547 fix(PredictionEngineElytra): shitmath look vector 2025-08-16 17:01:25 -06:00
cyramek fd59498b9b limit 1.21.5+ sprint attribute desync to minecarts 2025-08-16 20:12:33 +02:00
AoElite c0b96ea512 update README.md and CONTRIBUTING.md 2025-08-16 04:47:48 -04:00
ManInMyVan 38ccc559eb fix ChatA/ChatB falses from 31107ec4b 2025-08-14 17:38:51 -06:00
ManInMyVan 0dec74390f add force-slow-movement and force-stuck-speed (intended for anarchy servers) 2025-08-14 17:35:42 -06:00
ManInMyVan a953f501cd roll up shitmath bruteforce in PredictionEngineElytra 2025-08-14 17:05:44 -06:00
ManInMyVan 03e15141d7 cleaner stuckSpeedMultiplier constants 2025-08-14 17:00:38 -06:00
ManInMyVan 57618963e7 ExploitB: allow line break in pages 2025-08-14 15:37:14 -06:00
ManInMyVan 8cb4189554 remove unused import in BlockFaceHelper 2025-08-14 15:37:10 -06:00
Axionize 94813c0566 Fix typo in help: section of en messages.yml causing error message on /grim help. Also make /grim help handle null lines 2025-08-13 01:53:39 -04:00
Axionize b0906d936f chore(ci): Fix supported versions for Bukkit in Modrinth upload and Fabric dependences 2025-08-11 17:50:35 -04:00
ManInMyVan 6ce610e0b5 remove Killaura from punishments.yml + add Interact in japanese punishments.yml 2025-08-11 15:13:26 -06:00
Axionize ba305cf224 chore(ci): Fix escaping characters in changelog generation 2025-08-11 17:07:04 -04:00
ManInMyVan a3108c7850 merge FloodgateUtil & GeyserUtil 2025-08-11 14:51:19 -06:00
ManInMyVan 1ba072fe80 rewrite GeyserUtil to not use reflection
except once
2025-08-11 14:51:19 -06:00
ManInMyVan 376e7a9344 don't use deprecated method ItemStack.getEnchantmentLevel(EnchantmentType, ClientVersion) in Riptide 2025-08-11 14:51:18 -06:00
ManInMyVan 380e3d9094 mark many utility classes as UtilityClass 2025-08-11 14:51:18 -06:00
ManInMyVan 796a573ca1 remove unused method BlockFaceHelper.offset 2025-08-11 14:51:18 -06:00
ManInMyVan e433652ca3 don't use deprecated method ItemStack.getEnchantmentLevel(EnchantmentType, ClientVersion) in TridentItem 2025-08-11 14:51:18 -06:00
ManInMyVan 7376468701 use Math.PI in VanillaMath instead of the literal 2025-08-11 14:51:18 -06:00
ManInMyVan d2df1b76d2 simplify FloodgateUtil 2025-08-11 14:51:18 -06:00
ManInMyVan 6787d1bcd1 split isJumping and lastJumping into separate declarations 2025-08-11 14:51:18 -06:00
ManInMyVan 0a4a6cd7ef make Check.isTransaction static 2025-08-11 14:51:18 -06:00
ManInMyVan be120f062f remove Check.isFlying 2025-08-11 14:51:18 -06:00
Axionize 2bf1260a22 chore(ci): Fix generating changelog content in GitHub Actions 2025-08-11 16:25:54 -04:00
ManInMyVan 2e625e260c validate STEER_BOAT values (Add VehicleF) 2025-08-10 18:47:40 -06:00
ManInMyVan 4287f62cd7 fix PacketOrderO falses in vehicles 2025-08-10 14:13:10 -06:00
ManInMyVan b5c14f47ff check if a player is in a boat when sending STEER_BOAT (Add VehicleE) 2025-08-10 13:59:37 -06:00
ManInMyVan 647b13c083 add getVehicle() and getVehicleType() in GrimPlayer 2025-08-10 13:57:53 -06:00
AoElite cd8d78dd5d recode grim version command to use new api 2025-08-10 10:11:40 -04:00
AoElite a9a203466c allow all functional urls to be changeable, move all grim system property usage to a dedicated class 2025-08-10 10:11:04 -04:00
AoElite a695dac39b Refactor GrimDump: extract basic info generation to reusable getBasicInfo method 2025-08-10 10:07:34 -04:00
Axionize 5183ad8631 chore(ci): Replace vars.MODRINTH_ID with secrets.MODRINTH_ID to match repository Actions' settings 2025-08-08 21:58:01 -04:00
Axionize 0374b8c345 chore(ci): Create GitHub Actions script for auto-building lite version and publishing to Modrinth w/changelog 2025-08-08 21:51:46 -04:00
Axionize a2395606de chore(ci): Add cliff.toml file for defining git-cliff formatting of changelogs 2025-08-08 21:51:39 -04:00
SamB440 61f7ad8f71 [ci skip] Add client version to bypass issue template 2025-08-08 13:16:27 +01:00
SamB440 1136936ba1 Disallow step after setback 2025-08-07 15:20:29 +01:00
SamB440 25b0582074 Zero point zero three 2025-08-07 15:18:44 +01:00
SamB440 cec0738e32 [ci skip] Bump run-paper version 2025-08-06 18:28:11 +01:00
Axionize 31107ec4b5 Make ChatC and ChatB postViaChecks as temporary workaround to PE parsing limitations. 2025-08-06 10:59:09 -04:00
Axionize 4b514e7098 Fix FastBreak falses with 1.20.5+ custom tool rules. 2025-08-04 21:38:22 -04:00
Axionize 527c87e4cf Amend previous Reach false fix to only apply in versions > 1.14.4 2025-07-31 13:48:30 -04:00
Axionize 3875a6b238 Fix very rare Reach false for versions < 1.20.2 and > 1.21.4 involving rot only packets. See https://bugs.mojang.com/browse/MC/issues/MC-255263 2025-07-31 12:44:18 -04:00
Zongle Wang 5a4471c585 Shadow 9.0.0-rc2 (#2229)
```
OLD: grimac-bukkit-2.3.72-shadow-9-ba4bbef66-before.jar
NEW: grimac-bukkit-2.3.72-shadow-9-a14b4044a-after.jar

 JAR   │ old      │ new      │ diff
───────┼──────────┼──────────┼──────
 class │ 20.9 MiB │ 20.9 MiB │  0 B
 other │  1.5 MiB │  1.5 MiB │  0 B
───────┼──────────┼──────────┼──────
 total │ 22.4 MiB │ 22.4 MiB │  0 B

 CLASSES │ old   │ new   │ diff
─────────┼───────┼───────┼───────────
 classes │  4603 │  4603 │ 0 (+0 -0)
 methods │ 53656 │ 53656 │ 0 (+0 -0)
  fields │ 22173 │ 22173 │ 0 (+0 -0)

=================
====   JAR   ====
=================

 size │ diff │ path
──────┼──────┼─────────────────────────────────────────────────────────────────
  0 B │  0 B │ + META-INF/versions/9/ac/grim/grimac/shaded/snakeyaml/internal/
  0 B │  0 B │ + META-INF/versions/9/ac/grim/grimac/shaded/snakeyaml/
      │  0 B │ - META-INF/versions/9/org/yaml/snakeyaml/internal/
  0 B │  0 B │ + META-INF/versions/9/ac/grim/grimac/shaded/
      │  0 B │ - META-INF/versions/9/org/yaml/snakeyaml/
  0 B │  0 B │ + META-INF/versions/9/ac/grim/grimac/
      │  0 B │ - META-INF/versions/9/org/yaml/
  0 B │  0 B │ + META-INF/versions/9/ac/grim/
      │  0 B │ - META-INF/versions/9/org/
  0 B │  0 B │ + META-INF/versions/9/ac/
  0 B │  0 B │ + META-INF/services/
──────┼──────┼─────────────────────────────────────────────────────────────────
  0 B │  0 B │ (total)
```
2025-07-30 12:49:15 -04:00
Axionize ba4bbef66f Add mavenCentral() to repositsories to download me.lucko:fabric-permissions-api:0.1-SNAPSHOT for Fabric 1.16.1 2025-07-30 00:45:05 -04:00
Axionize 29e0c6e42c Bump PacketEvents 2025-07-30 00:17:32 -04:00
ManInMyVan 2528ff525d Revert "temp revert pre-via"
This reverts commit 77c165cfa8.
2025-07-24 08:54:57 -06:00
ManInMyVan 77c165cfa8 temp revert pre-via 2025-07-24 08:54:38 -06:00
ManInMyVan fe1c6bf3dd fix #2221 2025-07-24 08:01:11 -06:00
ManInMyVan 3de670eb1c fix exception 2025-07-23 15:32:29 -06:00
LokiandManInMyVan 533ae70f05 Hardcode more 1.21.4+ hand harvestables (#2213)
* Hardcode more 1.21.4+ Hand Harvestables

Fixes issue #2117 temporarily until PacketEvents resolves their api shortcoming (https://github.com/retrooper/packetevents/issues/1217)

* Adjust variable name

* Cleanup spacing in BlockBreakSpeed util

Co-authored-by: ManInMyVan <113963219+ManInMyVan@users.noreply.github.com>

---------

Co-authored-by: ManInMyVan <113963219+ManInMyVan@users.noreply.github.com>
2025-07-23 10:51:56 -04:00
cyramek b88dfa717a fix getting block jump factor in older versions 2025-07-23 04:11:00 +02:00
SamB440 933f6c7454 Add option to disable cancelling pong packets 2025-07-20 15:09:39 +01:00
Ayou e2cf0fa25b fix NPE in BlockPlaceCheck#getCombinedBox (#2215) 2025-07-20 15:10:57 +02:00
ManInMyVan d8b719daf8 more cleanup 2025-07-19 19:08:06 -06:00
ManInMyVan 95b080ae6e more small changes 2025-07-19 17:30:23 -06:00
ManInMyVan 1f052f6107 small stuff 2025-07-19 16:56:32 -06:00
ManInMyVan 02426ad592 these don't need to implement anything 2025-07-19 15:25:47 -06:00
ManInMyVan 85acf78db6 and it was not a teleport 2025-07-18 23:09:40 -06:00
ManInMyVan 354216f9b0 BadPacketsO optimizations 2025-07-18 14:53:59 -06:00
ManInMyVan 408c7d5773 pre-via block break checks 2025-07-18 14:37:53 -06:00
ManInMyVan 2e01399c13 PacketOrderO 2025-07-18 13:29:33 -06:00
Axionize 2ad98188f6 Temporary fix for BadPacketsE not working on vehicles 2025-07-17 23:55:22 -04:00
ManInMyVan ac2c9eafcb HOW DID I FORGET 2025-07-17 20:22:29 -06:00
ManInMyVan 2a7403be62 small cleanup 2025-07-17 20:22:23 -06:00
ManInMyVan 51bbb3162b pre-via chat checks 2025-07-17 20:10:41 -06:00
ManInMyVan d90de8fcca more pre-via
hopefully I didn't miss any getter changes
2025-07-17 20:08:09 -06:00
ManInMyVan 1b62b1dfb0 don't use ordinal for WindowClickType in CrashF 2025-07-17 19:57:04 -06:00
ManInMyVan c7fbc4b0b2 remove some empty lines 2025-07-17 19:55:30 -06:00
ManInMyVan 39b74b29f3 packet cancelling in VehicleD 2025-07-17 19:55:03 -06:00
ManInMyVan ed727cc923 SprintA verbose 2025-07-17 19:54:45 -06:00
ManInMyVan 9da9c30d9a there is no more via here 2025-07-17 19:38:37 -06:00
ManInMyVan 8d3e77f492 no it shouldn't 2025-07-17 11:33:16 -06:00
ManInMyVan 6e58e8c9c5 this should now be usable 2025-07-17 11:32:20 -06:00
ManInMyVan 9ab2f2e5ed fix #2211 2025-07-17 11:31:12 -06:00
Axionize 4942401c08 1.21.8 Server Support - Bump PE 2025-07-17 11:07:11 -04:00
Axionize 6e28434692 Downgrade from shadow 9.0.0-rc1 to 9.0.0-beta8 to fix jar inflation bug with shading fastutil 2025-07-17 10:58:54 -04:00
Axionize bbc1bee14d Bump Dependencies
gradlew-wrapper 8.13 -> 8.14.3
adventure 4.21.0 -> 4.23.0
adventure-platform-bukkit 4.3.4 -> 4.4.0
cloud-paper 2.0.0.-beta.10 -> 2.0.0.-beta.11
cloud-fabric 2.0.0.-beta.10 -> 2.0.0.-beta.11
packetevents 2.9.0-SNAPSHOT -> 2.9.2-SNAPSHOT
fabric-loader 0.16.10 -> 0.16.14
fabric-loom 1.10.5 -> 1.11.4
shadow 9.0.0-beta6 -> 9.0.0-rc1
2025-07-17 09:06:42 -04:00
cyramek c37a79bf9e fix: https://github.com/GrimAnticheat/Grim/issues/2209 2025-07-16 19:33:52 +02:00
ManInMyVan 2e9b086829 make some checks run before via 2025-07-15 12:03:00 -06:00
cyramek c66de10b10 fix HappyGhast false positives when holding space
fix: https://github.com/GrimAnticheat/Grim/issues/2203
2025-07-14 17:57:35 +02:00
Cyramek 4156768cae fix vehicle relative teleport/vehicle 1.12.2+ teleport system (#2200)
* fix vehicle relative teleport/vehicle 1.12.2+ teleport system

* only mark teleports for re-encoding if we’re actually rewriting them
2025-07-13 12:08:47 +02:00
Axionize 3e9b53e77e Add /grim history (#2201)
* Add /grim history command to see historical alerts

- Add basic runtime reloading support.
  - Will only actually reload if current database is different/has different setting than the one requested in config.

- Added [log] statements matching [alert] statements

* Add more detailed error message if Grim history subsystem fails to load.

* Refactor /grim history code; Fix autocomplete of online player names in /grim history

* Fix error swapping checkName and verbose in SQLite implementation

* Refactor /grim history to use more efficient long-term storage with compact UUIDs and string deduplication.

Grim old grim history data will not be migrated!

* Replace %version% with grim version in /grim history entry output

* Add support for %grim_version% %client_brand% %client_version% %server_version% substtuions in grim-history-entry
2025-07-13 05:59:52 -04:00
ManInMyVan 7b82060921 add include-timestamp to discord.yml 2025-07-09 15:35:52 -06:00
ManInMyVan e16979b09c finish ExploitC, replaces ExploitB 2025-07-09 15:20:32 -06:00
ManInMyVan 0ccd03a669 ExploitC fail-fast page parsing 2025-07-09 12:49:12 -06:00
ManInMyVan 3b76ded044 BadPacketsJ should be stable 2025-07-09 10:06:30 -06:00
ManInMyVan ba18d4c358 add missing return 2025-07-08 23:01:47 -06:00
ManInMyVan 18f0a2af31 fix ExploitC on 1.9-1.12 servers 2025-07-08 22:58:37 -06:00
ManInMyVan 8a2ca76e69 this took too long 2025-07-08 20:00:30 -06:00
ManInMyVan 1a6b6fc59f payload system 2025-07-08 17:32:59 -06:00
ManInMyVan 2518ced135 fix ExploitB false on pre 1.16 clients 2025-07-08 17:32:44 -06:00
ManInMyVan 4b53457851 validate anvil item name length 2025-07-07 16:55:14 -06:00
ManInMyVan df31a39e2b descriptions for almost every place check 2025-07-07 13:53:50 -06:00
ManInMyVan 4992cf67dc check if chat is hidden 2025-07-07 13:41:30 -06:00
ManInMyVan b4714b019d add Chat and Exploit to punishments 2025-07-07 12:22:22 -06:00
ManInMyVan 135c90c251 check for moving while chatting 2025-07-07 12:07:53 -06:00
ManInMyVan d1b0f4ddf6 remove whitespace in EquipmentType 2025-07-07 11:57:48 -06:00
ManInMyVan 0844fc646e playerData -> player 2025-07-07 11:57:31 -06:00
ManInMyVan 5fb8cd92be add KnownInput#moving 2025-07-07 11:39:28 -06:00
ManInMyVan b563792fa6 move ExploitA & C to chat category 2025-07-07 11:36:28 -06:00
Cyramek 978364a683 fix various vehicle issues [1.21.2] (#2185)
* fix happyghast step, collisions with fluids
warn about viabackwards compatibility

* fix 1.21.2+ riding without control

* use known input sneaking status if server support that

* fix fluid current pushing logic for happyghasts

* actually fix fluid pushing for happyghast

* fix vehicle input calculations for <=1.21.1 servers

* fix elytra inputs

* Revert 'fix elytra inputs'

* use pre-via listeners for sneaking

* remove warnings for sneaking issues on newer server versions

* fix float/double error causing 1e-8 imprecision when jumping on a horse

* workaround false positives when jumping on a horse

* add camel entity type

* only do this for 1.21.2+ client on 1.21.2+ servers

* build with new Grim PacketEvents fork snapshot with support for pre-via injection on Fabric
2025-07-06 23:38:15 -04:00
ManInMyVan 38a2ea4ccd slightly change ignore-duplicate-packet-rotation logic 2025-07-06 20:16:41 -06:00
ManInMyVan af2a53ac65 Merge pull request #2193 from Ayouuuu/2.0
Add ItemTypes.TURTLE_HELMET
2025-07-04 20:52:14 -06:00
Ayou d657aec5c2 Add ItemTypes.TURTLE_HELMET 2025-07-05 10:23:27 +08:00
ManInMyVan d8353143c9 fix more SprintE falses 2025-07-04 16:08:46 -06:00
ManInMyVan bb7ba08c29 fix #2190 2025-07-04 15:11:10 -06:00
SamB440 ddcc5a2dd9 Merge pull request #2188 from Cyramek/fix-vehicle-soft-collisions
fix soft collisions for vehicles
2025-07-04 15:09:59 +01:00
cyramek c4a18a50cc fix elytra inputs 2025-07-04 14:48:32 +02:00
cyramek fec88069f4 fix soft collisions for vehicles 2025-07-04 14:38:33 +02:00
ManInMyVan b8e70b89b4 fix build 2025-07-03 18:55:26 -06:00
ManInMyVan d59a9faa62 fix #2181 2025-07-03 18:49:02 -06:00
ManInMyVan d2913b3680 a bit more cleanup 2025-07-03 18:48:40 -06:00
ManInMyVan 3c562353df normalize PredictionEngine class names 2025-07-03 17:51:57 -06:00
ManInMyVan 9a22a282d4 fix NPE 2025-07-03 17:51:03 -06:00
ManInMyVan 1bfef2e580 minor cleanup 2025-07-03 17:18:19 -06:00
ManInMyVan c30be94875 why does this only not affect 1.21.4 2025-07-03 15:57:33 -06:00
ManInMyVan 0f54849351 SprintE on 1.18.2+ 2025-07-03 15:55:00 -06:00
ManInMyVan 1ab7f9f47f fix #2180 2025-07-02 16:03:55 -06:00
Cyramek c8df737498 implement HappyGhast movement simulation (#2182)
* implement happyghast movement simulation

* use grim movement input calculations for happy ghast

* remove unused jump possibilities method from HappyGhastPredictionEngine
2025-07-02 20:12:17 +01:00
SamB440 710d614c33 Check for any menu that isn't the player's 2025-07-02 14:03:35 +01:00
ManInMyVan dea9659da6 fix vine hitbox on pre-1.13 clients 2025-07-01 14:01:34 -06:00
SamB440 b8013d64fc Fix issue with server sending player inventory slot updates whilst in an unsupported inventory 2025-07-01 20:54:34 +01:00
SamB440 56130ed8c8 Add run-paper so I can debug easily 2025-07-01 19:03:54 +01:00
Axionize 8c64b48433 Fix minor spelling mistake in comment 2025-07-01 00:27:46 -04:00
Axionize 0fb2e00d96 Fix DRIED_GHAST CollisionBox for 1.7.10-1.21.5 2025-07-01 00:13:22 -04:00
cyramek 06dc5a54af properly support hasSaddle 2025-06-30 21:16:06 +02:00
Cyramek aaeded18c7 1.21.5/.6/.7 (#2079)
* 1.21.5 partial changes

* leftover

* cleanup

* check if player is in vehicle

* 1;1 input transformation

* forgot to push that

* notes

* i hate mutability

* not the case

* this is the right way

* note

* java style

* initial block collisions support

* incorrect fix for powder snow issues (1.21.4)

* support 1.18.2+ horizontal collision detection

* support elytra changes

* point three

* readability

* missed that

* fix merge conflicts

* support minecraft:blocks_attacks

* minecraft:blocks_attacks can override minecraft:food

* remove debug

* rewrite data consumable components support

* simplify consumable component handling

* as always, I forgot to remove this

* note things todo

* one more thing

* POC item behaviour

* workaround https://github.com/retrooper/packetevents/pull/1255

* 1.21.6 initial support

* fix .5/.6 riding issues

* create happy ghast entity

* todo list

* remove workarounds

* properly fix riding issues

* only use item behaviour system on reliable data component server

* replace happy ghast checks

* fix jumping on honey blocks, implement dried ghast block

* partially implement dried ghast ViaVersion replacement

* update sneaking behavior for newer client versions

* things pointed out in review

* reduce movement buffer size

* rename that field

* little refactor

* don't duplicate code

* leave it as what it was

* rename SERVER_USE_INPUT_FOR_SNEAKING to SERVER_USES_INPUT_FOR_SNEAKING

* grim's packetevents fork is here!

* simplify isHappyGhast type check
2025-06-30 08:18:49 -04:00
Axionize fcc78519a6 Include fabric-permissions-api:0.4.0 for Fabric 1.21.6 2025-06-30 04:07:25 -04:00
Axionize 4ade97a08f 1.21.6 MVP
Changes from https://github.com/GrimAnticheat/Grim/pull/2079 remain unmerged. New movement, entity, and attribute fixes have not been accounted for. This merely allows Grim to boot on 1.21.6/1.21.7-rc2 servers and 1.21.6/1.21.7-rc2 players to join.
2025-06-30 03:44:53 -04:00
ManInMyVan 0ac0a788b0 reduce object creation on fabric servers 2025-06-25 17:04:20 -06:00
ManInMyVan 6c79bafde8 a bit more cleanup 2025-06-25 15:53:54 -06:00
ManInMyVan d3e3b5877c cleanup 2025-06-25 15:38:21 -06:00
ManInMyVan bc258de6fb slight optimization 2025-06-25 14:24:41 -06:00
SamB440 b6e481dff5 Check if entityId is an active firework before adding to removal set 2025-06-22 14:13:08 +01:00
ManInMyVan d190e42b71 fix #2156 2025-06-19 11:19:45 -06:00
ManInMyVan 5de34f52f3 support reobf folia jars 2025-06-17 19:10:44 -06:00
ManInMyVan 9484870e8c fire can't be placed in liquids 2025-06-16 12:00:10 -06:00
ManInMyVan e81716acff iron trapdoors can only be opened in 1.7 2025-06-16 11:38:02 -06:00
ManInMyVan ea848fa919 fix #2149 2025-06-15 20:24:02 -06:00
SamB440 5e5fbe6283 Support plugins updating ATTACK_DAMAGE (fixes #2159) 2025-06-14 12:30:15 +01:00
ManInMyVan 984cfeecbe fix skinsrestorer compat 2025-06-07 20:12:57 -06:00
ManInMyVan 983872a585 patch #2140 2025-06-04 18:24:49 -06:00
AoElite 63f2c054a6 Merge pull request #2143 from Souipi/fix/ka-ping-method-cce
Fix GrimPlayer#getKeepAlivePing() method throwing a CCE.
2025-06-03 01:04:40 -04:00
Souipi b821f12503 fix: Fix GrimPlayer#getKeepAlivePing() method throwing a CCE. 2025-06-02 23:26:52 +02:00
ManInMyVan d22f8d8a76 fix swift sneak on pre-1.20.5 clients on 1.20.5+ servers 2025-06-01 10:52:47 -06:00
AoElite ee2ccc5708 add CONTRIBUTING.md and improve the README.md 2025-06-01 11:06:26 -04:00
AoElite 6ad8050923 improve piston performance 2025-06-01 07:15:10 -04:00
ManInMyVan 9786f0ddbf fix false with ender dragon attack slowdown 2025-05-30 20:33:04 -06:00
ManInMyVan 0d6d4daaaa minor refactor 2025-05-30 20:28:47 -06:00
ManInMyVan 32d778dd80 remove unused import 2025-05-30 18:47:03 -06:00
ManInMyVan 485f2672a7 wtf mojang 2025-05-30 18:37:47 -06:00
ManInMyVan b8600bc167 exempt ElytraC for 1 tick on respawn 2025-05-29 19:59:47 -06:00
ManInMyVan 3d68d2652a remove unused import 2025-05-29 19:33:06 -06:00
ManInMyVan e6a75183d9 probably fix #2134 2025-05-29 19:25:13 -06:00
ManInMyVan ef195ea941 fix potential false in MultiActionsE 2025-05-29 14:06:28 -06:00
ManInMyVan 5308c35c63 make MultiActionsB a BlockBreakCheck 2025-05-29 14:06:12 -06:00
ManInMyVan ee2e198335 check sneaking in MultiActionsC/D 2025-05-28 18:27:45 -06:00
ManInMyVan 76a262d592 update MultiActionsC/D description 2025-05-28 17:52:37 -06:00
Axionize 0c52c208cf Revert "Remove Unused legacy calls to event.setLastUsedWrapper(null) predating PE re-encode by default and markForReEncode()"
This reverts commit 4798903fb1.
2025-05-28 11:34:49 -04:00
ManInMyVan 438b0a5890 check knownInput in MultiActionsC/D 2025-05-27 20:05:16 -06:00
ManInMyVan 781d019dcd make MultiActionsC/D slightly more strict 2025-05-27 19:10:32 -06:00
ManInMyVan 3664b77910 BadPacketsP description 2025-05-27 19:05:12 -06:00
ManInMyVan a470d849ec maybe fix the folia BukkitItemResetHandler "issue" 2025-05-27 16:30:44 -06:00
Axionize 4798903fb1 Remove Unused legacy calls to event.setLastUsedWrapper(null) predating PE re-encode by default and markForReEncode() 2025-05-27 18:09:47 -04:00
Axionize 5a0783dbc4 Temporary fix for 1.16.5 Dirt Path/Grass Path Simulation errors 2025-05-26 21:23:11 -04:00
Axionize 8d0bb8fe19 Remove inaccurate comment 2025-05-26 21:23:11 -04:00
ManInMyVan 4442231626 don't pollData() if they don't exist 2025-05-26 18:29:44 -06:00
Axionize 895936b4b4 Optimize ViaVersionUtil
Tested on
* PandaSpigot (1.8) Java 17
* PaperSpigot (1.21.4) Java 21
* Fabric (1.21.5) Java 21

Tested-by: Axionize <154778082+axionize@users.noreply.github.com>
2025-05-26 18:51:43 -04:00
Axionize 493666537c Update PacketEvents version to new grim/2.0 branch state 2025-05-25 21:32:15 -04:00
ManInMyVan 062fe8c3eb maybe fix the disconnect.timeout issue 2025-05-25 13:24:25 -06:00
ManInMyVan 8dee1a5f84 Merge pull request #2135 from RegotlyLong/2.0
Add Chinese Translation ---- Update zh.yml
2025-05-25 12:49:30 -06:00
Ty_Regotly 20e6546d54 Update zh.yml 2025-05-25 14:52:06 +08:00
Ty_Regotly 7429cd9210 Merge branch 'GrimAnticheat:2.0' into 2.0 2025-05-25 14:47:45 +08:00
polymorphicengine b9e858d025 Add config option to disable/enable the recently added Forge blacklist (#2131)
* add config option to disable forge blacklist

* change the formatting

* Add the config setting to the rest of the languages.

* Include the comment in all available languages.

* Minor formatting changes.
2025-05-24 17:06:32 +01:00
ManInMyVan 9c8900bd3c fix BadPacketsH false when changing worlds with same dimension 2025-05-23 16:13:31 -06:00
ManInMyVan af4f8a6ddb and for pre-1.17 servers 2025-05-23 14:58:49 -06:00
ManInMyVan 815406b7b8 fix #2132 2025-05-23 14:34:15 -06:00
ManInMyVan ce39a84470 fix going into end portals in the end falsing badpacketsm 2025-05-23 14:00:14 -06:00
ManInMyVan 192b5f3818 remove unused imports 2025-05-23 13:59:28 -06:00
ManInMyVan fd8c9bb6e6 fix IndexOutOfBoundsException 2025-05-20 21:11:49 -06:00
ManInMyVan 1d0b3a856f move exploit and non-movement crash checks to packetChecks 2025-05-20 18:11:00 -06:00
ManInMyVan 415a88f007 move BadPacketsS handling to BadPacketsS class 2025-05-20 18:10:57 -06:00
ManInMyVan 008cb466e0 move BadPacketsO to be alphabetically 2025-05-20 18:09:28 -06:00
ManInMyVan 44cdd860df make BadPacketsJ a PacketCheck 2025-05-20 18:09:27 -06:00
AoElite f7670d8a9b Create config.yml 2025-05-20 17:23:17 -04:00
Axionize 2a8f2d504a Fix CME with accessing blockDataToId on Folia 2025-05-19 23:27:37 -04:00
Axionize 58128122fc Optimize BukkitPlatformWorld.getBlockAt() for legacy servers 2025-05-19 23:25:43 -04:00
Axionize 0cc819fea9 Revert incorrect Folia patch for ClassNotFoundException causing inadvertent firing of BlockReceiveGameEvent 2025-05-19 23:06:34 -04:00
ManInMyVan ab7ad0be98 reset server item usage on WINDOW_ITEMS
also check hand and item
2025-05-19 12:20:02 -06:00
SamB440 69c291de9a Blacklist forge 1.18.2-1.19.3.
Also added packet error key to language files.
2025-05-17 16:56:04 +01:00
SamB440 24f95fd0b9 Fix being able to use an elytra when it is in broken state 2025-05-17 14:50:08 +01:00
ManInMyVan 05d96aeaa1 Merge pull request #2122 from Cyramek/legacy-block-at
fix legacy BukkitPlatformWorld#getBlockAt
2025-05-16 20:44:49 -06:00
cyramek 0ab93d3fb2 fix legacy BukkitPlatformWorld#getBlockAt 2025-05-17 04:43:18 +02:00
ManInMyVan 1a39f97e22 check for respawning while alive 2025-05-16 17:01:02 -06:00
ManInMyVan f69e244363 move Post class to misc 2025-05-16 15:02:04 -06:00
ManInMyVan 740b9fe35a Timer - Vehicle -> VehicleTimer 2025-05-16 14:48:46 -06:00
ManInMyVan 829084c564 bruh 2025-05-16 13:49:59 -06:00
ManInMyVan b6e9eb0944 players don't tick in unloaded chunks 2025-05-15 20:14:38 -06:00
ManInMyVan 7555e2422b this just stops pre from flagging on 1.9 2025-05-14 16:16:57 -06:00
ManInMyVan 82f8bbf89f check the hand the player is using the item in 2025-05-14 15:48:12 -06:00
SamB440 537739095c Fix disabling threshold (it shouldn't have been working before either) 2025-05-14 17:20:08 +01:00
SamB440 60ff6d7a18 Split ping-abuse-limit-threshold into a separate check 2025-05-14 17:17:33 +01:00
SamB440 077ad7690b We do need to throw for this to work 2025-05-14 16:25:15 +01:00
ManInMyVan 9144638ef5 make PacketOrderB stricter 2025-05-13 16:06:43 -06:00
ManInMyVan 463189c35e make badpacketse stricter 2025-05-12 19:42:10 -06:00
AoElite 9099764f10 handle discord formatting better 2025-05-12 09:39:47 -04:00
AoElite 025e4dee4f handle error logging more appropriately 2025-05-12 07:35:13 -04:00
AoElite e56778d1c2 dump command changes: renamed the properties field to states, added detected platform, renamed system field names 2025-05-12 07:12:34 -04:00
AoElite 460d4db6cf fix item reset usage error with folia 2025-05-12 06:49:19 -04:00
AoElite 2514bba15b fixed folia not being detected 2025-05-12 06:48:38 -04:00
ManInMyVan c0d388548c remove exempt players from the alert manager on disconnect 2025-05-11 16:00:14 -06:00
ManInMyVan 9f0990d412 fix /gl 2025-05-11 13:15:22 -06:00
ManInMyVan e3d5b7d7a9 bubble columns were added in 1.13
still falses
2025-05-11 13:05:53 -06:00
ManInMyVan 544f3bc732 fix #2116 2025-05-10 16:00:30 -06:00
Ty_Regotly bdd8225d7c Merge branch 'GrimAnticheat:2.0' into 2.0 2025-02-05 19:41:43 +08:00
Ty_Regotly d6a36fcea7 Merge branch '2.0' into 2.0 2025-02-05 15:31:35 +08:00
Ty_Regotly dce77cfdba Update zh.yml 2025-02-04 08:34:46 +08:00
Ty_Regotly 31444ef010 Update tr.yml 2025-02-04 08:15:16 +08:00
Ty_Regotly f5c3fe6669 Update ru.yml 2025-02-04 08:15:08 +08:00
Ty_Regotly 8a75b40695 Update pt.yml 2025-02-04 08:14:56 +08:00
Ty_Regotly 210d756a2d Update nl.yml 2025-02-04 08:14:47 +08:00
Ty_Regotly cc65b3cc2c Update ja.yml 2025-02-04 08:14:34 +08:00
Ty_Regotly d77b073b10 Update it.yml 2025-02-04 08:14:25 +08:00
Ty_Regotly 421acef99a Update fr.yml 2025-02-04 08:14:16 +08:00
Ty_Regotly 9af21596ef Update es.yml 2025-02-04 08:14:06 +08:00
Ty_Regotly 0603c72c89 Update en.yml 2025-02-04 08:13:51 +08:00
Ty_Regotly 106a1d2f6a Update de.yml 2025-02-04 08:13:41 +08:00
612 changed files with 16999 additions and 9666 deletions

No files matched your search

+5
View File
@@ -7,6 +7,8 @@ indent_style = space
insert_final_newline = true
max_line_length = 100
tab_width = 4
end_of_line = crlf
trim_trailing_whitespace = true
# intellij specific configuration
ij_continuation_indent_size = 8
@@ -15,3 +17,6 @@ ij_formatter_on_tag = @formatter:on
ij_formatter_tags_enabled = false
ij_smart_tabs = false
ij_wrap_on_typing = false
[.github/ISSUE_TEMPLATE/*.yml]
indent_size = 2
+1
View File
@@ -0,0 +1 @@
.gitea/CODEOWNERS linguist-detectable=false
+3
View File
@@ -0,0 +1,3 @@
# Pull requests request review from the eligible staff team(s).
# One approval from either an Admin or Developer satisfies branch protection.
* @trueog/Admins @trueog/Developers
+14 -6
View File
@@ -5,7 +5,7 @@ body:
- type: textarea
attributes:
label: Describe the bypass and how to replicate it
description: Provide a video, or detailed instructions to help replicate it.
description: Provide a video or detailed instructions to help replicate it.
placeholder: |
Step 1. Do this
Step 2. Then do this
@@ -17,8 +17,8 @@ body:
attributes:
label: Grim version
description: |
Provide the git commit or the version in the jar's name.
placeholder: A example would be b85c2d0 or grim version 2.3.5, etc
Provide the version of Grim running on the server. You can run `/grim version` to check.
placeholder: Examples include 2.3.73-8ddd7f1, 2.3.72, etc.
validations:
required: true
@@ -26,15 +26,23 @@ body:
attributes:
label: Server version
description: |
Run `/version` on your server to check. Provide both the server type (paper, airplane, etc) & the minecraft version.
Run `/version` on your server to check. Provide both the server type (ie Paper, Airplane, etc) & the Minecraft version.
validations:
required: true
- type: input
attributes:
label: Client version
description: |
Provide the version of your Minecraft client. Also provide the protocol version if changed.
validations:
required: true
- type: textarea
attributes:
label: Plugins
label: Plugins/Mods
description: |
List all plugins or mods running on your server. To list plugins on bukkit, run `/plugins`.
List all plugins or mods installed on your server. To list plugins on Bukkit-based servers, run `/plugins`.
If your server is behind a proxy such as Bungeecord, you should list the proxy's plugins as well.
validations:
required: true
+1
View File
@@ -0,0 +1 @@
blank_issues_enabled: false
+14 -14
View File
@@ -6,7 +6,7 @@ body:
attributes:
label: Stack trace
description: |
Please include the entire stack trace with no attachments. If you would like, you can use a text pasting site such as https://paste.gg.
Provide the entire stack trace with no attachments. If you would like, you can use a text pasting site such as https://mclo.gs.
placeholder: Paste the stack trace or link to the stack trace here
validations:
required: true
@@ -14,7 +14,7 @@ body:
- type: textarea
attributes:
label: How to replicate it if known
description: A video, or detailed instructions to help replicate the issue would help.
description: A video or detailed instructions to help replicate the issue would help.
placeholder: |
Step 1. Do this
Step 2. Then do this
@@ -26,8 +26,8 @@ body:
attributes:
label: Grim version
description: |
Provide the git commit or the version in the jar's name.
placeholder: A example would be b85c2d0 or grim version 2.3.5, etc
Provide the version of Grim running on the server. You can run `/grim version` to check.
placeholder: Examples include 2.3.73-8ddd7f1, 2.3.72, etc.
validations:
required: true
@@ -35,27 +35,27 @@ body:
attributes:
label: Server version
description: |
Run `/version` on your server to check. Provide both the server type (paper, airplane, etc) & the minecraft version.
Run `/version` on your server to check. Provide both the server type (ie Paper, Airplane, etc) & the Minecraft version.
validations:
required: true
- type: dropdown
id: type
attributes:
label: Affected Platform(s)
description: Which platforms does this issue affect? If you aren't sure, select both.
options:
- "Bukkit"
- "Fabric"
multiple: true
label: Affected Platform(s)
description: Which platforms does this issue affect? If you aren't sure, select both.
options:
- "Bukkit"
- "Fabric"
multiple: true
validations:
required: true
required: true
- type: textarea
attributes:
label: Plugins/mods
label: Plugins/Mods
description: |
List all plugins or mods running on your server. To list plugins on bukkit, run `/plugins`.
List all plugins or mods installed on your server. To list plugins on Bukkit-based servers, run `/plugins`.
If your server is behind a proxy such as Bungeecord, you should list the proxy's plugins as well.
validations:
required: true
+7 -7
View File
@@ -20,8 +20,8 @@ body:
attributes:
label: Grim version
description: |
Provide the git commit or the version in the jar's name.
placeholder: A example would be b85c2d0 or grim version 2.3.5, etc
Provide the version of Grim running on the server. You can run `/grim version` to check.
placeholder: Examples include 2.3.73-8ddd7f1, 2.3.72, etc.
validations:
required: true
@@ -29,7 +29,7 @@ body:
attributes:
label: Server version
description: |
Run `/version` on your server to check. Provide both the server type (paper, airplane, etc) & the minecraft version.
Run `/version` on your server to check. Provide both the server type (ie Paper, Airplane, etc) & the Minecraft version.
validations:
required: true
@@ -37,16 +37,16 @@ body:
attributes:
label: Client version
description: |
Provide the version of your Minecraft client. You must be able to reproduce the false positive using the vanilla client.
Lunar Client, TLauncher etc. will result in your issue being closed.
Provide the version of your Minecraft client. You must be able to reproduce the false positive using a vanilla client.
Using Lunar Client, TLauncher, Fabric, etc, will result in your issue being closed.
validations:
required: true
- type: textarea
attributes:
label: Plugins
label: Plugins/Mods
description: |
List all plugins or mods running on your server. To list plugins on bukkit, run `/plugins`.
List all plugins or mods installed on your server. To list plugins on Bukkit-based servers, run `/plugins`.
If your server is behind a proxy such as Bungeecord, you should list the proxy's plugins as well.
validations:
required: true
+2 -2
View File
@@ -5,6 +5,6 @@ body:
- type: textarea
attributes:
label: Feature description
description: Describe the feature and why it should be added
description: Describe the feature and why it should be added.
validations:
required: true
required: true
+13 -14
View File
@@ -5,7 +5,7 @@ body:
- type: textarea
attributes:
label: Describe the bug and how to replicate it
description: A video, or detailed instructions to help replicate the issue would help.
description: A video or detailed instructions to help replicate the issue would help.
placeholder: |
Step 1. Do this
Step 2. Then do this
@@ -16,9 +16,8 @@ body:
- type: textarea
attributes:
label: Grim version
description: |
Provide the git commit or the version in the jar's name.
placeholder: A example would be b85c2d0 or grim version 2.3.5, etc
description: Provide the version of Grim running on the server. You can run `/grim version` to check.
placeholder: Examples include 2.3.73-8ddd7f1, 2.3.72, etc.
validations:
required: true
@@ -26,27 +25,27 @@ body:
attributes:
label: Server version
description: |
Run `/version` on your server to check. Provide both the server type (paper, airplane, etc) & the minecraft version.
Run `/version` on your server to check. Provide both the server type (ie Paper, Airplane, etc) & the Minecraft version.
validations:
required: true
- type: dropdown
id: type
attributes:
label: Affected Platform(s)
description: Which platforms does this issue affect? If you aren't sure, select both.
options:
- "Bukkit"
- "Fabric"
multiple: true
label: Affected Platform(s)
description: Which platforms does this issue affect? If you aren't sure, select both.
options:
- "Bukkit"
- "Fabric"
multiple: true
validations:
required: true
required: true
- type: textarea
attributes:
label: Plugins
label: Plugins/Mods
description: |
List all plugins or mods running on your server. To list plugins on bukkit, run `/plugins`.
List all plugins or mods installed on your server. To list plugins on Bukkit-based servers, run `/plugins`.
If your server is behind a proxy such as Bungeecord, you should list the proxy's plugins as well.
validations:
required: true
@@ -19,8 +19,15 @@ body:
- type: input
attributes:
label: Grim Log
description: |
Provide the generated /grim log link.
description: Provide the generated /grim log link.
validations:
required: true
- type: textarea
attributes:
label: Grim version
description: Provide the version of Grim running on the server. You can run `/grim version` to check.
placeholder: Examples include 2.3.73-8ddd7f1, 2.3.72, etc.
validations:
required: true
@@ -28,7 +35,7 @@ body:
attributes:
label: Server version
description: |
Run `/version` on your server to check. Provide both the server type (paper, airplane, etc) & the minecraft version.
Run `/version` on your server to check. Provide both the server type (ie Paper, Airplane, etc) & the Minecraft version.
validations:
required: true
@@ -36,16 +43,16 @@ body:
attributes:
label: Client version
description: |
Provide the version of your Minecraft client. You must be able to reproduce the false positive using the vanilla client.
Lunar Client, TLauncher etc. will result in your issue being closed.
Provide the version of your Minecraft client. You must be able to reproduce the false positive using a vanilla client.
Using Lunar Client, TLauncher, Fabric, etc, will result in your issue being closed.
validations:
required: true
- type: textarea
attributes:
label: Plugins
label: Plugins/Mods
description: |
List all plugins or mods running on your server. To list plugins on bukkit, run `/plugins`.
List all plugins or mods installed on your server. To list plugins on Bukkit-based servers, run `/plugins`.
If your server is behind a proxy such as Bungeecord, you should list the proxy's plugins as well.
validations:
required: true
required: true
+253
View File
@@ -0,0 +1,253 @@
name: Reusable Build and Publish
on:
workflow_call:
inputs:
runner:
description: 'The runner to use for the build job.'
required: false
type: string
default: 'ubuntu-latest'
is-release-build:
description: 'True if this is a release build, which affects versioning and publishing.'
required: true
type: boolean
release-version-type:
description: 'The version type for Modrinth (e.g., release, beta, alpha).'
required: true
type: string
release-changelog:
description: 'The changelog content for the release.'
required: false
type: string
default: ''
run-build-scan:
description: 'Whether to enable Develocity build scan'
required: true
type: boolean
publish-artifacts:
description: 'If true, uploads artifacts to Modrinth and other distribution sites.'
required: true
type: boolean
secrets:
MODRINTH_ID:
required: false
MODRINTH_TOKEN:
required: false
BUILD_UPDATE_TOKEN:
required: true
BUILD_UPDATE_URL:
required: true
jobs:
build:
runs-on: ${{ inputs.runner }}
permissions:
# Grant permission to write contents (for creating tags/refs)
contents: write
packages: write
env:
MODRINTH_ID: ${{ secrets.MODRINTH_ID }}
MODRINTH_TOKEN: ${{ secrets.MODRINTH_TOKEN }}
GRIM_MAIN_BRANCH: ${{ github.event.repository.default_branch }}
BUILD_UPDATE_TOKEN: ${{ secrets.BUILD_UPDATE_TOKEN }}
steps:
- name: Checkout repository with full history
uses: actions/checkout@v4
with:
persist-credentials: false
fetch-depth: 200
- name: Set up JDK 21
uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'temurin'
server-id: github
settings-path: ${{ github.workspace }}
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v3
with:
cache-read-only: ${{ github.ref_name != github.event.repository.default_branch && github.ref_name != 'dev' }}
- name: Cache Gradle Loom Cache
uses: actions/cache@v4
with:
path: .gradle/loom-cache
key: loom-cache-${{ runner.os }}-${{ hashFiles('**/libs.versions.toml', 'fabric/**/build.gradle.kts') }}
restore-keys: |
loom-cache-${{ runner.os }}
- name: Set Git Environment Variables
run: |
echo "GRIM_GIT_REPO=${{ github.event.repository.name }}" >> $GITHUB_ENV
echo "GRIM_GIT_ORG=${{ github.repository_owner }}" >> $GITHUB_ENV
- name: Compute version and Build with Gradle
id: build_with_version
run: |
# If any command in a pipeline fails, treat the pipeline as failed.
set -o pipefail
BUILD_ARGS="build"
if [[ "${{ inputs.is-release-build }}" == "true" ]]; then
BUILD_ARGS="$BUILD_ARGS -Prelease=true"
fi
# Conditionally add the --scan flag
if [[ "${{ inputs.run-build-scan }}" == "true" ]]; then
BUILD_ARGS="$BUILD_ARGS --scan"
fi
echo "Running: ./gradlew printVersion $BUILD_ARGS"
# Use 'tee' to both display the output live and save it to a file for parsing
VERSION=$(./gradlew printVersion $BUILD_ARGS | tee build-output.log | grep '^VERSION=' | tail -n1 | cut -d'=' -f2)
echo "build_version=$VERSION" >> $GITHUB_OUTPUT
echo "BUILD_VERSION=$VERSION" >> $GITHUB_ENV
echo "Build version: $VERSION"
- name: Publish Build Scan URL
# Only run this step if a scan was requested
if: ${{ inputs.run-build-scan == true }}
run: |
# Search the log file for the build scan URL and extract it
SCAN_URL=$(grep -o 'https://gradle.com/s/[^ ]*' build-output.log | head -n 1 || true)
if [[ -n "$SCAN_URL" ]]; then
echo "Found build scan URL: $SCAN_URL"
# Post a highly visible link to the Job Summary page
echo "### Gradle Build Scan" >> $GITHUB_STEP_SUMMARY
echo "A public build scan has been published and is available for inspection." >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "[**View Build Scan →**]($SCAN_URL)" >> $GITHUB_STEP_SUMMARY
else
echo "Could not find build scan URL in build-output.log" >> $GITHUB_STEP_SUMMARY
fi
- name: Upload Bukkit Artifact
uses: actions/upload-artifact@v6
with:
name: grimac-bukkit
path: ${{ github.workspace }}/bukkit/build/libs/grimac-bukkit-${{ env.BUILD_VERSION }}.jar
if-no-files-found: error
- name: Upload Fabric Artifact
uses: actions/upload-artifact@v6
with:
name: grimac-fabric
path: ${{ github.workspace }}/fabric/build/libs/grimac-fabric-${{ env.BUILD_VERSION }}.jar
if-no-files-found: error
#------------------------------------------------------------------
# Generate/Prepare Changelog
#------------------------------------------------------------------
- name: Generate Changelog with git-cliff (for Dev Builds)
if: ${{ !inputs.is-release-build }}
id: git-cliff
uses: orhun/git-cliff-action@v4
with:
config: cliff.toml
args: --verbose ${{ github.event.before }}..${{ github.event.after }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # git-cliff needs a token
- name: Prepare Final Changelog
id: changelog
# Add this 'env' block to safely load the changelogs as environment variables
env:
RELEASE_NOTES: ${{ inputs.release-changelog }}
DEV_NOTES: ${{ steps.git-cliff.outputs.content }}
run: |
NOTES=""
if [[ "${{ inputs.is-release-build }}" == "true" ]]; then
# Use the environment variable for the release changelog.
# The double quotes are critical to preserve newlines!
NOTES="$RELEASE_NOTES"
else
# Use the environment variable for the dev changelog.
NOTES="$DEV_NOTES"
fi
if [ -z "$NOTES" ]; then
NOTES="* No new code changes in this build."
fi
# This is the correct way to set a multi-line output variable in Actions
{
echo "notes<<EOF"
echo "$NOTES"
echo "EOF"
} >> "$GITHUB_OUTPUT"
- name: Generate build number
if: contains(fromJSON('["merge","release","main","lightning"]'), github.ref_name) && inputs.publish-artifacts == true && env.MODRINTH_ID != '' && env.MODRINTH_TOKEN != ''
id: buildnumber
uses: onyxmueller/build-tag-number@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
#------------------------------------------------------------------
# Publish to Modrinth & API
#------------------------------------------------------------------
- name: Publish Bukkit to Modrinth
if: ${{ inputs.publish-artifacts == true && env.MODRINTH_ID != '' && env.MODRINTH_TOKEN != '' }}
uses: Kir-Antipov/mc-publish@v3.3
with:
modrinth-id: ${{ secrets.MODRINTH_ID }}
modrinth-token: ${{ secrets.MODRINTH_TOKEN }}
modrinth-featured: ${{ inputs.is-release-build }} # Only feature release builds
files: bukkit/build/libs/grimac-bukkit-${{ env.BUILD_VERSION }}.jar
name: LightningGrim Anticheat (Bukkit) ${{ env.BUILD_VERSION }}-b${{ steps.buildnumber.outputs.build_number }}
version: ${{ env.BUILD_VERSION }}-b${{ steps.buildnumber.outputs.build_number }}
version-type: ${{ inputs.release-version-type }}
changelog: ${{ steps.changelog.outputs.notes }}
loaders: |
bukkit
spigot
paper
folia
purpur
game-versions: '>=1.7 <=1.21.11'
- name: Publish Fabric to Modrinth
if: ${{ inputs.publish-artifacts == true && env.MODRINTH_ID != '' && env.MODRINTH_TOKEN != '' }}
uses: Kir-Antipov/mc-publish@v3.3
with:
modrinth-id: ${{ secrets.MODRINTH_ID }}
modrinth-token: ${{ secrets.MODRINTH_TOKEN }}
modrinth-featured: ${{ inputs.is-release-build }} # Only feature release builds
files: fabric/build/libs/grimac-fabric-${{ env.BUILD_VERSION }}.jar
name: LightningGrim Anticheat (Fabric) ${{ env.BUILD_VERSION }}-b${{ steps.buildnumber.outputs.build_number }}
version: ${{ env.BUILD_VERSION }}-b${{ steps.buildnumber.outputs.build_number }}
version-type: ${{ inputs.release-version-type }}
changelog: ${{ steps.changelog.outputs.notes }}
loaders: |
fabric
game-versions: '>=1.16.1 <=1.21.10'
dependencies: 'fabric-api'
- name: Publish to API
if: ${{ inputs.publish-artifacts == true && env.BUILD_UPDATE_TOKEN != '' && inputs.is-release-build == false }}
run: |
curl -X POST \
-H "Content-Type: application/json" \
-H "User-Agent: GrimACBuilder/v1" \
-H "Authorization: Bearer ${{ secrets.BUILD_UPDATE_TOKEN }}" \
-d '{"org": "${{ github.repository_owner }}", "repository": "${{ github.event.repository.name }}", "branch": "${{ github.ref_name }}", "version": "${{ env.BUILD_VERSION }}"}' \
"${{ secrets.BUILD_UPDATE_URL }}"
- name: Publish Release to API
if: ${{ inputs.publish-artifacts == true && env.BUILD_UPDATE_TOKEN != '' && inputs.is-release-build == true }}
run: |
curl -X POST \
-H "Content-Type: application/json" \
-H "User-Agent: GrimACBuilder/v1" \
-H "Authorization: Bearer ${{ secrets.BUILD_UPDATE_TOKEN }}" \
-d '{"org": "${{ github.repository_owner }}", "repository": "${{ github.event.repository.name }}", "branch": "release", "version": "${{ env.BUILD_VERSION }}"}' \
"${{ secrets.BUILD_UPDATE_URL }}"
+51 -47
View File
@@ -12,60 +12,64 @@
name: "CodeQL"
on:
push:
branches: [ master ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ master ]
schedule:
- cron: '30 15 * * 2'
workflow_dispatch:
push:
branches: [dev]
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
analyze:
name: Analyze
runs-on: tenki-standard-autoscale
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'java' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
# Learn more:
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
strategy:
fail-fast: false
matrix:
language: ['java-kotlin']
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
# Learn more:
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
steps:
- name: Checkout repository
uses: actions/checkout@v4
steps:
- name: Checkout repository
uses: actions/checkout@v5
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v3
- name: Set up JDK 21
uses: actions/setup-java@v5
with:
java-version: '21'
distribution: 'temurin'
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
settings-path: ${{ github.workspace }} # location for the settings.xml file
# ℹ️ Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v3
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
# ℹ️ Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
#- run: |
# make bootstrap
# make release
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
+47 -57
View File
@@ -1,59 +1,49 @@
# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.
# This workflow will build a package using Gradle and then publish it to GitHub packages when a release is created
# For more information see: https://github.com/actions/setup-java/blob/main/docs/advanced-usage.md#Publishing-using-gradle
name: Gradle Package
on: [push, workflow_dispatch]
name: Gradle Package (Development)
on:
workflow_dispatch:
inputs:
run-build-scan:
description: 'Run with public build scan (adds --scan)'
required: true
type: boolean
default: false
publish-artifacts:
description: 'Upload to Modrinth & API? (Uncheck for a dry run)'
type: boolean
default: true
runner:
description: 'The runner to use for the build job.'
required: false
type: string
default: 'tenki-standard-autoscale'
push:
branches:
- 'main'
- '2.0'
- 'dev'
- 'release'
- 'release/**'
- 'lightning'
paths:
- 'bukkit/**'
- 'common/**'
- 'fabric/**'
- '**.java'
- '**.kt'
- '**.kts'
- '**.toml'
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- name: Set up JDK 21
uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'temurin'
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
settings-path: ${{ github.workspace }} # location for the settings.xml file
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4 # Handles Gradle wrapper validation and basic caching
- name: Cache Gradle Loom Cache
uses: actions/cache@v4
with:
path: .gradle/loom-cache # Path to Loom cache relative to workspace root
key: loom-cache-${{ runner.os }}-${{ hashFiles('**/libs.versions.toml', 'fabric/**/build.gradle.kts') }}
restore-keys: |
loom-cache-${{ runner.os }}
- name: Build with Gradle (Actual Build for Artifacts)
run: ./gradlew build
- name: Upload Bukkit Artifact
uses: actions/upload-artifact@v4
with:
name: grimac-bukkit
# Adding if-no-files-found for robustness
path: ${{ github.workspace }}/bukkit/build/libs/grimac-bukkit-*.jar
if-no-files-found: error
- name: Upload Fabric Artifact
uses: actions/upload-artifact@v4
with:
name: grimac-fabric
# Adding if-no-files-found for robustness
path: ${{ github.workspace }}/fabric/build/libs/grimac-fabric-*.jar
if-no-files-found: error
call-build-workflow:
uses: ./.github/workflows/build-and-publish.yml
with:
runner: ${{ github.event.inputs.runner || 'tenki-standard-autoscale' }}
is-release-build: false
release-version-type: alpha # Dev builds are always 'alpha'
run-build-scan: ${{ github.event.inputs.run-build-scan == 'true' }}
publish-artifacts: ${{ github.event.inputs.publish-artifacts != 'false' }}
secrets:
MODRINTH_ID: ${{ secrets.MODRINTH_ID }}
MODRINTH_TOKEN: ${{ secrets.MODRINTH_TOKEN }}
BUILD_UPDATE_TOKEN: ${{ secrets.BUILD_UPDATE_TOKEN }}
BUILD_UPDATE_URL: ${{ secrets.BUILD_UPDATE_URL }}
+22
View File
@@ -0,0 +1,22 @@
name: Publish Release
on:
release:
types: [published]
jobs:
call-release-workflow:
uses: ./.github/workflows/build-and-publish.yml
with:
runner: 'tenki-standard-autoscale'
is-release-build: true
release-version-type: release # Official releases are 'release'
# Use the body of the GitHub Release as the changelog
release-changelog: ${{ github.event.release.body }}
run-build-scan: false
publish-artifacts: true
secrets:
MODRINTH_ID: ${{ secrets.MODRINTH_ID }}
MODRINTH_TOKEN: ${{ secrets.MODRINTH_TOKEN }}
BUILD_UPDATE_TOKEN: ${{ secrets.BUILD_UPDATE_TOKEN }}
BUILD_UPDATE_URL: ${{ secrets.BUILD_UPDATE_URL }}
+2
View File
@@ -73,3 +73,5 @@ nb-configuration.xml
##############################
run/
fabric/run/
.ignored/
+40
View File
@@ -0,0 +1,40 @@
# Contributing to LightningGrim
Thank you for your interest in contributing to LightningGrim. This document outlines the guidelines for
making pull
requests to the project. *We're usually pretty lenient with pull requests, but this guide will help
make the process go more smoothly.*
### Pull Request Guidelines
- **Compatibility**
- Any changes must be compatible with
the [supported environments](https://github.com/GrimAnticheat/Grim/wiki/Supported-environments) (Spigot, Paper, Folia, Fabric, etc.)
- The plugin must be compatible with minecraft versions 1.8 and higher. Exempting checks from specific versions is acceptable.
- The plugin must be able to run on Java 17 or higher. Changes that don't support Java 17 at runtime will not be accepted.
- **Non-acceptable pull requests**
- Heuristic-based checks will not be accepted; however, basic rate limiting such as blocking attacks based on CPS is acceptable.
- Checks that can be easily circumvented that don't block packets or fix anything are likely to not be accepted.
- Checks or features that are too specific to a single environment or minecraft version are likely to not be accepted.
- Changes that require large or unnecessary dependencies will likely not be accepted.
- **Pull request formatting**
- Create a new branch for your feature or fix when forking the repository.
- Reference related issues in your pull request description if applicable.
- Write clear and descriptive commit messages.
- **Code styling**
- Add code comments for complex logic or significant changes.
- Try to keep your code clean and avoid duplication.
- Thoroughly test your changes before submitting your pull request.
### Development Notes
- LightningGrim is built using [Gradle](https://gradle.org/) kotlin scripts.
- Java 21 is currently required to build the project. A minimum of Java 17 is required to run it.
### Questions & Support
- Join our [Discord](https://discord.grim.ac) if you have questions or need assistance.
- Refer to the [Wiki](https://github.com/GrimAnticheat/Grim/wiki) for project documentation.
+96
View File
@@ -0,0 +1,96 @@
To honour the JVM settings for this build a single-use Daemon process will be forked. For more on this, please refer to https://docs.gradle.org/8.14.3/userguide/gradle_daemon.html#sec:disabling_the_daemon in the Gradle documentation.
Daemon will be stopped at the end of the build
> Configure project :buildSrc
WARNING: Unsupported Kotlin plugin version.
The `embedded-kotlin` and `kotlin-dsl` plugins rely on features of Kotlin `2.0.21` that might work differently than in the requested version `2.2.20`.
> Task :buildSrc:checkKotlinGradlePluginConfigurationErrors SKIPPED
> Task :buildSrc:generateExternalPluginSpecBuilders UP-TO-DATE
> Task :buildSrc:extractPrecompiledScriptPluginPlugins UP-TO-DATE
> Task :buildSrc:compilePluginsBlocks UP-TO-DATE
> Task :buildSrc:generatePrecompiledScriptPluginAccessors UP-TO-DATE
> Task :buildSrc:generateScriptPluginAdapters UP-TO-DATE
> Task :buildSrc:pluginDescriptors UP-TO-DATE
> Task :buildSrc:processResources UP-TO-DATE
> Task :buildSrc:compileKotlin
w: Language version 1.8 is deprecated and its support will be removed in a future version of Kotlin
> Task :buildSrc:compileJava NO-SOURCE
> Task :buildSrc:compileGroovy NO-SOURCE
> Task :buildSrc:classes UP-TO-DATE
> Task :buildSrc:jar
> Configure project :
Build configuration:
shadePE = false
relocate = true
mavenLocalOverride = true
release = false
version = 2.3.73-HEAD-bebcab7+lite
> Task :clean
> Task :bukkit:clean
> Task :common:clean
> Task :assemble UP-TO-DATE
> Task :check UP-TO-DATE
> Task :bukkit:generateBukkitPluginDescription
> Task :bukkit:processResources
> Task :bukkit:generateTestEffectiveLombokConfig
> Task :common:processResources
> Task :bukkit:sourcesJar
> Task :spotlessInternalRegisterDependencies
> Task :bukkit:spotlessJava
> Task :bukkit:spotlessJavaApply
> Task :bukkit:spotlessJavaCheck
> Task :bukkit:spotlessKotlinGradle
> Task :bukkit:spotlessKotlinGradleApply
> Task :bukkit:spotlessKotlinGradleCheck
> Task :bukkit:spotlessCheck
> Task :bukkit:processTestResources NO-SOURCE
> Task :bukkit:spotlessApply
> Task :common:sourcesJar
> Task :common:spotlessJava
> Task :common:spotlessJavaApply
> Task :common:spotlessJavaCheck
> Task :common:spotlessKotlinGradle
> Task :common:spotlessKotlinGradleApply
> Task :common:spotlessKotlinGradleCheck
> Task :common:spotlessCheck
> Task :common:generateTestEffectiveLombokConfig
> Task :common:processTestResources NO-SOURCE
> Task :common:spotlessApply
> Task :common:eclipseClasspath
Could not resolve: com.github.retrooper:packetevents-api:2.11.2-SNAPSHOT
> Task :common:eclipseJdt
> Task :common:eclipseProject
> Task :common:eclipse
> Task :bukkit:generateEffectiveLombokConfig
> Task :common:generateEffectiveLombokConfig
> Task :common:compileJava FAILED
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':common:compileJava'.
> Could not resolve all files for configuration ':common:compileClasspath'.
> Could not find com.github.retrooper:packetevents-api:2.11.2-SNAPSHOT.
Searched in the following locations:
- https://repo.grim.ac/snapshots/com/github/retrooper/packetevents-api/2.11.2-SNAPSHOT/maven-metadata.xml
- https://repo.grim.ac/snapshots/com/github/retrooper/packetevents-api/2.11.2-SNAPSHOT/packetevents-api-2.11.2-SNAPSHOT.pom
- file:/home/admin/.m2/repository/com/github/retrooper/packetevents-api/2.11.2-SNAPSHOT/maven-metadata.xml
- file:/home/admin/.m2/repository/com/github/retrooper/packetevents-api/2.11.2-SNAPSHOT/packetevents-api-2.11.2-SNAPSHOT.pom
Required by:
project :common
* Try:
> Run with --stacktrace option to get the stack trace.
> Run with --info or --debug option to get more log output.
> Run with --scan to get full insights.
> Get more help at https://help.gradle.org.
BUILD FAILED in 17s
38 actionable tasks: 31 executed, 7 up-to-date
+66 -30
View File
@@ -1,42 +1,75 @@
# GrimAC
<div align="center">
<h1>LightningGrim</h1>
GrimAC is an open source Minecraft anticheat designed for 1.21 and supports 1.8-1.21. It is free while in beta. It will eventually become paid and/or will include offering additional subscription based paid checks. Geyser players are fully exempt.
<div>
<a href="https://github.com/GrimAnticheat/Grim/actions/workflows/gradle-publish.yml">
<img alt="Workflow" src="https://img.shields.io/github/actions/workflow/status/GrimAnticheat/Grim/gradle-publish.yml?style=flat&logo=github"/>
</a>&nbsp;&nbsp;
<a href="https://modrinth.com/plugin/grimac">
<img alt="Modrinth" src="https://img.shields.io/modrinth/v/LJNGWSvH?style=flat&label=version&logo=modrinth">
</a>&nbsp;&nbsp;
<a href="https://modrinth.com/plugin/grimac#download">
<img alt="Downloads" src="https://img.shields.io/modrinth/dt/LJNGWSvH?style=flat&logo=modrinth&label=downloads&link=https%3A%2F%2Fmodrinth.com%2Fplugin%2Fgrimac%23download">
</a>&nbsp;&nbsp;
<a href="https://discord.grim.ac">
<img alt="Discord" src="https://img.shields.io/discord/811396969670901800?style=flat&label=discord&logo=discord">
</a>
</div>
<br>
</div>
This project is considered feature complete for the 2.0 (open-source) branch of this project. If you would like a bugfix or enhancement and cannot sponsor the work, pull requests are welcome.
LightningGrim is an open source Minecraft anticheat fork designed to support the latest versions of Minecraft.
It currently supports minecraft versions 1.8–1.21. Geyser players are fully exempt from the anticheat to prevent false positives.
This project is considered feature-complete for the 2.0 (open-source) branch. If you would like a bug fix or enhancement and cannot sponsor the work, pull requests are welcome.
A premium version is planned, which will offer additional subscription-based paid checks, such as heuristics.
## Downloads
- [Modrinth](https://modrinth.com/plugin/grimac)
- [Hangar](https://hangar.papermc.io/GrimAnticheat/GrimAnticheat)
- [SpigotMC](https://www.spigotmc.org/resources/grim-anticheat.99923/)
- *For bleeding edge builds use GitHub artifacts*: [Bukkit](https://nightly.link/GrimAnticheat/Grim/workflows/gradle-publish/2.0/grimac-bukkit.zip), [Fabric](https://nightly.link/GrimAnticheat/Grim/workflows/gradle-publish/2.0/grimac-fabric.zip)
## Changes over GrimAC:
## Installation notes
> [!WARNING]
> Java 17 is now required. More information [here](https://github.com/GrimAnticheat/Grim/wiki/Updating-to-Java-17).
- Paper, Spigot, Folia, and Fabric are currently supported.
- If you use Geyser, place Floodgate on the backend server so grim can exempt bedrock players. Grim cannot access the Floodgate API if it is on the proxy.
- If you use ViaVersion, it should be on the backend server as movement is highly dependent on client version.
- WallHit - checks for hitting through walls
- EntityPierce - checks for hitting through other entities
- Bukkit Event Piston Optimizations
- Optimized Hitbox/Collision Boxes
- Optimized Reach calculations
- Checks for PacketOrder (patches lots of noslow, autoblocks, etc...)
- Checks for Inventory interactions (prevents using crafting slots as extra storage, moving while having inventory open, etc...)
## Support & wiki information
- Support & discussion: [Discord](https://discord.com/invite/kqQAhTmkUF)
- Report issues: [Issues](https://github.com/GrimAnticheat/Grim/issues/new/choose)
- Wiki & examples: [Wiki](https://github.com/GrimAnticheat/Grim/wiki)
## Requirements & Installation
## Developer API
Grim's API allows you to integrate Grim into your own plugins.
- API repository: [GrimAPI](https://github.com/GrimAnticheat/GrimAPI)
- Wiki info: [Wiki](https://github.com/GrimAnticheat/GrimAPI)
- Java 17 or higher. *For more details, see [Updating-to-Java-17](https://github.com/GrimAnticheat/Grim/wiki/Updating-to-Java-17).*
- A Spigot, Paper, Folia, or Fabric server environment. *For more details, see [Supported-environments](https://github.com/GrimAnticheat/Grim/wiki/Supported-environments).*
## How to compile
If you use a proxy such as Velocity or Bungeecord:
- If you use Geyser, Floodgate must be installed on the backend server (where Grim is) so Grim can access the Floodgate API.
- If you use ViaVersion, it must be installed on the backend server (where Grim is) ONLY.
Grim does not support having ViaVersion installed on the proxy, even if it is also installed on the backend.
1. `git clone https://github.com/GrimAnticheat/Grim.git`
2. `cd Grim`
3. `./gradlew build`
## Resources
- For documentation and examples visit the [Wiki](https://github.com/GrimAnticheat/Grim/wiki).
- For answers to commonly asked questions visit the [FAQ](https://github.com/GrimAnticheat/Grim/wiki/FAQ).
- For community support and project discussion join our [Discord](https://discord.grim.ac).
## Pull Requests
See [Contributing](CONTRIBUTING.md) for more information about contributing and what our guidelines
are.
## Developer Plugin API
LightningGrim's plugin API allows you to integrate LightningGrim into your own plugins. Visit
the [plugin API repository](https://github.com/GrimAnticheat/GrimAPI) for the source code and more
information.
## Compiling From Source
1. `git clone https://github.com/Axionize/LightningGrim`
2. `cd LightningGrim`
3. `./gradlew build clean eclipse --warning-mode all`
4. The final jars will compile into the `<platform>/build/libs` folders
## Grim supremacy
## LightningGrim Supremacy
What makes Grim stand out against other anticheats?
What makes LightningGrim stand out against other anticheats?
### Movement Simulation Engine
@@ -44,7 +77,9 @@ What makes Grim stand out against other anticheats?
* This covers everything from basic walking, swimming, knockback, cobwebs, to bubble columns
* It even covers riding entities from boats to pigs to striders
* Built upon covering edge cases to confirm accuracy
* 1.13+ clients on 1.13+ servers, 1.12- clients on 1.13+ servers, 1.13+ clients on 1.12- servers, and 1.12- clients on 1.12- servers are all supported regardless of the large technical changes between these versions.
* 1.13+ clients on 1.13+ servers, 1.12- clients on 1.13+ servers, 1.13+ clients on 1.12- servers,
and 1.12- clients on 1.12- servers are all supported regardless of the large technical changes
between these versions.
* The order of collisions depends on the client version and is correct
* Accounts for minor bounding box differences between versions, for example:
* Single glass panes will be a + shape for 1.7-1.8 players and * for 1.9+ players
@@ -71,7 +106,8 @@ What makes Grim stand out against other anticheats?
* Per player, the cache allows for multithreaded design
* Sending players fake blocks with packets is safe and does not lead to falses
* The world is recreated for each player to allow lag compensation
* Client sided blocks cause no issues with packet based blocks. Block glitching does not false the anticheat.
* Client sided blocks cause no issues with packet based blocks. Block glitching does not false the
anticheat.
### Latency compensation
+39 -16
View File
@@ -1,32 +1,42 @@
/**
* ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
* ┃ GrimAC Build Configuration ┃
* ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
* GrimAC Build Configuration
*
* 🔧 Build Flags:
* -PshadePE=true → Enables 'lite' mode
* -Prelocate=false → Adds 'no_relocate' modifier
* -Prelease=true → Removes commit/modifiers for release build
* Build Flags:
* -PshadePE=true - Bundles PacketEvents into the jar (default: external/lite).
* -Prelocate=false - Adds 'no_relocate' modifier
* -Prelease=true - Removes commit/modifiers for release build
*
* Logic in: buildSrc/versioning/BuildConfig.kt & VersionUtil.kt
*/
plugins {
base
}
import org.gradle.api.tasks.Copy
import org.gradle.api.tasks.compile.JavaCompile
import versioning.BuildConfig
import versioning.VersionUtil
BuildConfig.init(project)
val baseVersion = "2.3.72"
val baseVersion = "2.3.73"
group = "ac.grim.grimac"
version = VersionUtil.computeVersion(baseVersion)
description = "Libre simulation anticheat designed for 1.21 with 1.8–1.21 support, powered by PacketEvents 2.0."
description = "Libre simulation anticheat designed for 1.21 with 1.8–1.21 support, powered by PacketEvents 2.11.2."
println("⚙️ Build configuration:")
println(" shadePE = ${BuildConfig.shadePE}")
println(" relocate = ${BuildConfig.relocate}")
println(" mavenLocalOverride = ${BuildConfig.mavenLocalOverride}")
println(" release = ${BuildConfig.release}")
println(" version = $version")
ext["timestamp"] = System.currentTimeMillis().toString()
ext["git_branch"] = VersionUtil.getGitBranch(true)
ext["git_commit"] = VersionUtil.getGitCommitHash(true)
ext["git_org"] = System.getenv("GRIM_GIT_ORG") ?: VersionUtil.getGitUser()
ext["git_repo"] = System.getenv("GRIM_GIT_REPO") ?: "Grim"
println("Build configuration:")
println(" shadePE = ${BuildConfig.shadePE}")
println(" relocate = ${BuildConfig.relocate}")
println(" mavenLocalOverride = ${BuildConfig.mavenLocalOverride}")
println(" release = ${BuildConfig.release}")
println(" version = $version")
tasks.register("printVersion") {
group = "versioning"
@@ -42,4 +52,17 @@ subprojects {
options.isFork = true
options.isIncremental = true
}
}
}
tasks.register<Copy>("copyBukkitJarToRoot") {
dependsOn(":bukkit:shadowJar")
from(project(":bukkit").layout.buildDirectory.dir("libs")) {
include("LightningGrim-bukkit-$version.jar") // current build only; stale jars from prior commits would collide on rename
rename("LightningGrim-bukkit-.*\\.jar", "LightningGrim-$baseVersion.jar")
}
into(layout.buildDirectory.dir("libs"))
}
tasks.named("build") {
dependsOn("copyBukkitJarToRoot")
}
+1 -2
View File
@@ -1,11 +1,10 @@
plugins {
`kotlin-dsl`
kotlin("plugin.serialization") version "1.9.0" // Use the same Kotlin version as your main project
kotlin("plugin.serialization") version "2.2.20"
}
repositories {
mavenCentral()
gradlePluginPortal()
}
dependencies {
+42
View File
@@ -0,0 +1,42 @@
import org.gradle.api.artifacts.dsl.RepositoryHandler
import org.gradle.api.artifacts.repositories.MavenArtifactRepository
import org.gradle.api.artifacts.repositories.InclusiveRepositoryContentDescriptor
import org.gradle.kotlin.dsl.maven
import versioning.BuildConfig
/**
* Registers a repository with strict content exclusivity.
*
* Logic:
* 1. If BuildConfig.mavenLocalOverride is true, checks Maven Local first.
* 2. Checks the provided remote URL second.
* 3. Enforces strict filtering so Gradle never checks this repo for other deps,
* and never checks other repos (like Central) for these deps.
*/
fun RepositoryHandler.exclusive(
url: String,
repoConfig: MavenArtifactRepository.() -> Unit = {},
filterConfig: InclusiveRepositoryContentDescriptor.() -> Unit
) {
// We define the repositories inside the function scope so they are added to the handler
val remote = maven(url, repoConfig)
exclusive(remote, filterConfig)
}
fun RepositoryHandler.exclusive(
remote: MavenArtifactRepository,
filterConfig: InclusiveRepositoryContentDescriptor.() -> Unit
) {
// We access BuildConfig here again to check for local override
val local = if (BuildConfig.mavenLocalOverride) mavenLocal() else null
exclusiveContent {
if (local != null) {
forRepositories(local, remote)
} else {
forRepositories(remote)
}
filter(filterConfig)
}
}
@@ -1,3 +1,5 @@
import versioning.BuildConfig
plugins {
`java-library`
id("io.freefair.lombok")
@@ -16,11 +18,6 @@ java {
withJavadocJar()
}
// Lombok configuration
//lombok {
// version.set("1.18.30") // Use the version from your version catalog if available
//}
// Spotless configuration
spotless {
java {
@@ -38,7 +35,6 @@ spotless {
}
}
tasks {
withType<JavaCompile>().configureEach {
options.encoding = "UTF-8"
@@ -52,11 +48,28 @@ tasks {
// Process resources (e.g., for plugin metadata files)
processResources {
inputs.property("version", project.version)
filesMatching(listOf( // "plugin.yml", Bukkit variable expansion taken care of by net.minecrell.plugin-yml.bukkit
"bungee.yml", "velocity-plugin.json", "fabric.mod.json")
val properties = mapOf(
"timestamp" to rootProject.ext["timestamp"],
"version" to project.version.toString(),
"git_commit" to rootProject.ext["git_commit"],
"git_branch" to rootProject.ext["git_branch"],
"git_repo" to rootProject.ext["git_repo"],
"git_org" to rootProject.ext["git_org"],
"build_shade_pe" to BuildConfig.shadePE,
"build_relocate" to BuildConfig.relocate,
"build_release" to BuildConfig.release,
)
properties.forEach { (key, value) -> inputs.property(key, value) }
filesMatching(
listOf(
"bungee.yml",
"velocity-plugin.json",
"grimac.properties"
)
) {
expand("version" to project.version)
expand(properties)
}
}
@@ -95,7 +95,7 @@ fun extractEmbeddedJars(jarFile: File, project: Project): Set<DependencyIdentifi
?: throw IllegalStateException("Missing 'id' in fabric.mod.json of embedded JAR: $entryName")
val idWithDots = id.replace("_", ".")
val lastDotIndex = idWithDots.lastIndexOf(".")
val group = if (lastDotIndex != -1) idWithDots.substring(0, lastDotIndex) else ""
val group = if (lastDotIndex != -1) idWithDots.take(lastDotIndex) else ""
// Extract name and version from the JAR filename
val jarName = entryName.substringAfterLast("/").removeSuffix(".jar")
@@ -127,7 +127,7 @@ fun parseNameAndVersionFromJarName(jarName: String): Pair<String, String>? {
val versionStartIndex = versionStartMatch.range.first
// Split the JAR name into name and version at the version start index
val name = jarName.substring(0, versionStartIndex)
val name = jarName.take(versionStartIndex)
val version = jarName.substring(versionStartIndex + 1) // Skip the hyphen
return if (name.isNotEmpty() && version.isNotEmpty()) {
@@ -300,7 +300,7 @@ project.afterEvaluate {
val allEmbeddedDependencies = mutableMapOf<DependencyIdentifier, File>()
val actuallyIncludedDependencies = mutableSetOf<DependencyIdentifier>()
fun collectAllEmbeddedDependencies(dependencies: Set<ResolvedDependency>) {
fun collectAllEmbeddedDependenciesForAfterEvaluate(dependencies: Set<ResolvedDependency>) {
dependencies.forEach { dep ->
val classifier = dep.moduleArtifacts.firstOrNull()?.classifier ?: ""
val depKey = "${dep.moduleGroup}:${dep.moduleName}:${dep.moduleVersion}${if (classifier.isNotEmpty()) ":$classifier" else ""}"
@@ -318,13 +318,13 @@ project.afterEvaluate {
)
}
}
collectAllEmbeddedDependencies(dep.children)
collectAllEmbeddedDependenciesForAfterEvaluate(dep.children)
}
}
}
processed.clear()
collectAllEmbeddedDependencies(resolvedDependencies)
collectAllEmbeddedDependenciesForAfterEvaluate(resolvedDependencies)
processed.clear()
processDependencies(
@@ -345,7 +345,7 @@ tasks.withType<Jar>().configureEach {
val allEmbeddedDependencies = mutableMapOf<DependencyIdentifier, File>()
val actuallyIncludedDependencies = mutableSetOf<DependencyIdentifier>()
fun collectAllEmbeddedDependencies(dependencies: Set<ResolvedDependency>) {
fun collectAllEmbeddedDependenciesForJar(dependencies: Set<ResolvedDependency>) {
dependencies.forEach { dep ->
val depKey = "${dep.moduleGroup}:${dep.moduleName}:${dep.moduleVersion}"
if (!processed.contains(depKey)) {
@@ -362,13 +362,13 @@ tasks.withType<Jar>().configureEach {
)
}
}
collectAllEmbeddedDependencies(dep.children)
collectAllEmbeddedDependenciesForJar(dep.children)
}
}
}
processed.clear()
collectAllEmbeddedDependencies(resolvedDependencies)
collectAllEmbeddedDependenciesForJar(resolvedDependencies)
processed.clear()
@@ -5,17 +5,16 @@ plugins {
id("com.gradleup.shadow")
}
// Create a configuration for shading dependencies from common
val shadowCommon: Configuration by project.configurations.creating {
isCanBeResolved = true
isCanBeConsumed = false
extendsFrom(project.configurations.getByName("implementation"))
}
tasks.named<ShadowJar>("shadowJar") {
minimize()
archiveFileName.set("${rootProject.name}-${project.name}-${rootProject.version}.jar")
from(shadowCommon) // Use from() instead of direct assignment
minimize {
// The PostgreSQL JDBC driver is loaded reflectively via java.sql ServiceLoader,
// so minimize() sees no direct references and would strip it. Keep it whole.
exclude(dependency("org.postgresql:postgresql:.*"))
// Same for the SLF4J NOP provider, loaded via SLF4JServiceProvider ServiceLoader.
exclude(dependency("org.slf4j:slf4j-nop:.*"))
}
archiveFileName = "${rootProject.name}-${project.name}-${rootProject.version}.jar"
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
if (BuildConfig.relocate) {
if (BuildConfig.shadePE) {
@@ -38,6 +37,8 @@ tasks.named<ShadowJar>("shadowJar") {
relocate("org.jetbrains", "ac.grim.grimac.shaded.jetbrains")
relocate("org.incendo", "ac.grim.grimac.shaded.incendo")
relocate("io.leangen.geantyref", "ac.grim.grimac.shaded.geantyref") // Required by cloud
relocate("com.zaxxer", "ac.grim.grimac.shaded.zaxxer") // Database history
relocate("org.postgresql", "ac.grim.grimac.shaded.postgresql") // Database history (JDBC driver); service file rewritten by mergeServiceFiles
}
mergeServiceFiles()
}
@@ -2,6 +2,11 @@ package versioning
import org.gradle.api.Project
import org.gradle.internal.extensions.stdlib.toDefaultLowerCase
import versioning.BuildConfig.init
import versioning.BuildConfig.mavenLocalOverride
import versioning.BuildConfig.release
import versioning.BuildConfig.relocate
import versioning.BuildConfig.shadePE
/**
* BuildConfig provides access to user-defined build flags that control how a Grim
@@ -10,9 +15,9 @@ import org.gradle.internal.extensions.stdlib.toDefaultLowerCase
*
* Flags can be defined in three ways:
*
* ① JVM system properties (-Dflag=value)
* ② Gradle project properties (-Pflag=value or in gradle.properties)
* ③ Environment variables (FLAG=value)
* 1. JVM system properties (-Dflag=value)
* 2. Gradle project properties (-Pflag=value or in gradle.properties)
* 3. Environment variables (FLAG=value)
*
* You can use these to enable/disable features like shading, relocation, or release mode.
*
@@ -33,7 +38,7 @@ import org.gradle.internal.extensions.stdlib.toDefaultLowerCase
* ./gradlew build -DshadePE=true -Drelease=true
* ```
*
* @property shadePE If true, shades PacketEvents into the jar. Default: true.
* @property shadePE Bundles PacketEvents into the jar when true; consumes an external provider when false (default; emits the `-lite` artifact).
* @property relocate If true, relocates shaded dependencies to avoid conflicts. Default: true.
* @property release If true, omits commit hash and modifiers from version string. Default: false.
* @property mavenLocalOverride If true, will make artifacts in mavenLocal() will be used instead of their remote counterparts for this build. Default: false
@@ -48,32 +53,23 @@ object BuildConfig {
* ```
*/
fun init(project: Project) {
_shadePE = resolveBool(project, "shadePE", altKey = "SHADE_PE", default = true)
_shadePE = resolveBool(project, "shadePE", altKey = "SHADE_PE", default = false)
_relocate = resolveBool(project, "relocate", altKey = "RELOCATE_JAR", default = true)
_release = resolveBool(project, "release", default = false)
_mavenLocalOverride = resolveBool(project, "mavenLocalOverride", default = false)
_mavenLocalOverride = resolveBool(project, "mavenLocalOverride", altKey = "MAVEN_LOCAL_OVERRIDE", default = false)
}
// Unified resolution logic (System > Gradle > Env)
private fun resolveRaw(project: Project, key: String): String? =
System.getProperty(key) // ① JVM (-Dkey=value)
?: project.findProperty(key)?.toString() // ② Gradle (-Pkey=value or gradle.properties)
?: System.getenv(key.uppercase()) // ③ ENV (KEY=value)
System.getProperty(key) // 1. JVM (-Dkey=value)
?: project.findProperty(key)?.toString() // 2. Gradle (-Pkey=value or gradle.properties)
?: System.getenv(key.uppercase()) // 3. ENV (KEY=value)
private fun resolveBool(project: Project, key: String, altKey: String? = null, default: Boolean): Boolean {
val primaryValue = resolveRaw(project, key)?.toDefaultLowerCase()?.toBooleanStrictOrNull()
if (primaryValue != null) {
return primaryValue
}
if (altKey != null) {
val altValue = resolveRaw(project, altKey)?.toDefaultLowerCase()?.toBooleanStrictOrNull()
if (altValue != null) {
return altValue
}
}
return default
return resolveRaw(project, key)?.toDefaultLowerCase()?.toBooleanStrictOrNull()
?: altKey?.let {
resolveRaw(project, it)?.toDefaultLowerCase()?.toBooleanStrictOrNull()
} ?: default
}
// Private backing vars (nullable because we can't use lateinit with primitives)
@@ -82,7 +78,7 @@ object BuildConfig {
private var _release: Boolean? = null
private var _mavenLocalOverride: Boolean? = null
/** If true, shades PacketEvents into the jar. Default: true. */
/** If true, shades PacketEvents into the jar. Default: false (external provider). */
val shadePE: Boolean get() = _shadePE
?: error("BuildConfig.shadePE accessed before init() was called")
@@ -96,4 +92,5 @@ object BuildConfig {
val mavenLocalOverride: Boolean get() = _mavenLocalOverride
?: error("BuildConfig.release accessed before init() was called")
}
}
@@ -3,7 +3,7 @@ package versioning
import java.io.ByteArrayOutputStream
/**
* Utility for computing the version string of GrimAC artifacts.
* Utility for computing the version string of LightningGrim artifacts.
*
* The version string is constructed based on:
* - A base semantic version (e.g., "2.3.72")
@@ -37,29 +37,31 @@ object VersionUtil {
val modifiers = buildList {
if (!BuildConfig.shadePE) add("lite")
if (!BuildConfig.relocate) add("no_relocate")
}.takeIf { it.isNotEmpty() }?.joinToString("-")
}.joinToString("-").takeIf { it.isNotEmpty() }
return buildString {
append(baseVersion)
append("-")
branch?.let { append("$it-") }
append(commitHash)
modifiers?.let { append("+$modifiers") }
modifiers?.let { append("+$it") }
}
}
/**
* Retrieves the current Git commit as a short hash.
* Retrieves the current Git commit
*/
private fun getGitCommitHash(): String {
fun getGitCommitHash(full: Boolean = false): String {
val stdout = ByteArrayOutputStream()
ProcessBuilder("git", "rev-parse", "--short", "HEAD")
val command = listOfNotNull("git", "rev-parse", if (full) null else "--short", "HEAD")
ProcessBuilder(command)
.redirectErrorStream(true)
.start()
.apply { waitFor() }
.inputStream
.use { stdout.writeBytes(it.readAllBytes()) }
return stdout.toString().trim()
val fullCommit = stdout.toString().trim()
return fullCommit.take(minOf(fullCommit.length, 7))
}
/**
@@ -69,7 +71,7 @@ object VersionUtil {
* Any slash (/) in the branch name is replaced with an underscore (_)
* to avoid filesystem issues.
*/
private fun getGitBranch(): String? {
fun getGitBranch(raw: Boolean = false): String? {
val stdout = ByteArrayOutputStream()
ProcessBuilder("git", "rev-parse", "--abbrev-ref", "HEAD")
@@ -78,11 +80,35 @@ object VersionUtil {
.apply { waitFor() }
.inputStream.use { stdout.writeBytes(it.readAllBytes()) }
if (raw) return stdout.toString().trim()
val branch = stdout.toString().trim()
.replace(Regex("[^a-zA-Z0-9_.-]+"), "_")
.replace(Regex("_{2,}"), "_")
.trim(' ', '.', '_', '-')
.removePrefix("heads_")
val mainBranch = System.getenv("GRIM_MAIN_BRANCH") ?: "2.0"
return when (branch) {
"main", "2.0" -> null // ← ignore these branches
else -> branch.replace("/", "_")
"lightning" -> null
"main", mainBranch -> null // ← ignore these branches
else -> branch
}
}
}
fun getGitUser(): String {
try {
val stdout = ByteArrayOutputStream()
ProcessBuilder("git", "config", "user.name")
.redirectErrorStream(true)
.start()
.apply { waitFor() }
.inputStream.use { stdout.writeBytes(it.readAllBytes()) }
return stdout.toString().trim()
} catch (_: Exception) {
return "unknown"
}
}
}
+68 -30
View File
@@ -5,31 +5,41 @@ plugins {
`maven-publish`
grim.`base-conventions`
grim.`shadow-conventions`
id("net.minecrell.plugin-yml.bukkit") version "0.6.0"
id("de.eldoria.plugin-yml.bukkit") version "0.8.0"
id("xyz.jpenilla.run-paper") version "3.0.0-beta.1"
}
repositories {
if (BuildConfig.mavenLocalOverride) {
mavenLocal()
// 1. Fallback for non-exclusive deps (e.g. Maven Central deps)
if (BuildConfig.mavenLocalOverride) mavenLocal()
// 2. Exclusive Repositories (One HTTP request per dep)
exclusive("https://repo.papermc.io/repository/maven-public/", { name = "papermc" }) {
includeGroup("io.papermc.paper")
includeGroup("net.md-5")
}
maven {
name = "papermc"
url = uri("https://repo.papermc.io/repository/maven-public/")
exclusive("https://libraries.minecraft.net", { mavenContent { releasesOnly() } }) {
includeModule("com.mojang", "brigadier")
}
maven("https://hub.spigotmc.org/nexus/content/repositories/snapshots/") // Spigot
maven("https://repo.grim.ac/snapshots") { // Grim API
content {
includeGroup("ac.grim.grimac")
includeGroup("com.github.retrooper")
}
exclusive("https://repo.extendedclip.com/content/repositories/placeholderapi/") {
includeGroup("me.clip")
}
maven("https://repo.viaversion.com") // ViaVersion
maven("https://nexus.scarsz.me/content/repositories/releases") // Configuralize
maven("https://repo.opencollab.dev/maven-snapshots/") // Floodgate
maven("https://repo.opencollab.dev/maven-releases/") // Cumulus (for Floodgate)
maven("https://s01.oss.sonatype.org/content/repositories/snapshots/")
maven("https://repo.extendedclip.com/content/repositories/placeholderapi/") // placeholderapi
mavenCentral() // FastUtil
exclusive("https://repo.codemc.io/repository/maven-releases/", { mavenContent { releasesOnly() } }) {
includeGroup("com.github.retrooper")
}
exclusive("https://repo.grim.ac/snapshots") {
includeGroup("ac.grim.grimac")
}
exclusive("https://nexus.scarsz.me/content/repositories/releases", { mavenContent { releasesOnly() } }) {
includeGroup("github.scarsz")
}
mavenCentral()
}
dependencies {
@@ -42,22 +52,24 @@ dependencies {
compileOnly(libs.packetevents.spigot)
}
implementation(libs.cloud.paper)
implementation(libs.adventure.platform.bukkit)
implementation(libs.grim.bukkit.internal)
implementation("org.slf4j:slf4j-nop:2.0.17") // Provide SLF4J NOP provider to suppress missing-provider warning.
implementation("org.slf4j:slf4j-api:2.0.17") // Bundle a relocated SLF4J API for shaded dependencies.
implementation(project(":common"))
shadow(project(":common"))
}
bukkit {
name = "GrimAC"
author = "GrimAC"
name = "LightningGrim"
author = "LightningGrim"
main = "ac.grim.grimac.platform.bukkit.GrimACBukkitLoaderPlugin"
website = "https://grim.ac/"
apiVersion = "1.13"
foliaSupported = true
if (!BuildConfig.shadePE) {
depend = listOf("packetevents")
// Bukkit `depend` is case-sensitive; canonical PE plugin name is PascalCase.
depend = listOf("PacketEvents")
}
softDepend = listOf(
@@ -71,6 +83,7 @@ bukkit {
"floodgate",
"FastLogin",
"PlaceholderAPI",
"ZNPCsPlus",
)
permissions {
@@ -125,24 +138,49 @@ bukkit {
}
register("grim.verbose") {
description = "Receive verbose alerts for violations. Requires grim.alerts"
description = "Receive verbose alerts for violations"
default = Permission.Default.OP
}
register("grim.verbose.enable-on-join") {
description =
"Enable verbose alerts on join. Requires grim.alerts and grim.alerts.enable-on-join"
"Enable verbose alerts on join"
default = Permission.Default.FALSE
}
register("grim.list") {
description =
"Shows lists of specific data"
default = Permission.Default.FALSE
}
}
}
publishing.publications.create<MavenPublication>("maven") {
artifact(tasks["shadowJar"])
artifact(tasks.named("shadowJar"))
}
tasks.shadowJar {
manifest {
attributes["paperweight-mappings-namespace"] = "mojang"
apply(from = "$projectDir/gradle/packetevents-provider.gradle.kts")
@Suppress("UNCHECKED_CAST")
val registerStagePE = project.extra["registerStagePacketEventsTask"]
as (org.gradle.api.Project, String, java.io.File) -> org.gradle.api.tasks.TaskProvider<Copy>
val stagePEJar = registerStagePE(project, "stageLocalPacketEvents", file("run"))
tasks {
runServer {
minecraftVersion("1.21.11")
// External mode stages the locally built True-OG PacketEvents JAR.
if (!BuildConfig.shadePE) {
dependsOn(stagePEJar)
}
}
shadowJar {
mergeServiceFiles()
manifest {
attributes["paperweight-mappings-namespace"] = "mojang"
}
}
}
@@ -0,0 +1,76 @@
// True-OG PacketEvents JAR staging convention.
// Resolution order (first hit wins): -PtruogPacketEventsJar, TRUEOG_PACKETEVENTS_JAR env, default glob under ~/eclipse-workspace/PacketEvents/spigot/build/libs (case-insensitive, latest by mtime). See context/refs/packetevents-provider.md.
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.Paths
import java.util.Comparator
fun resolveTrueOgPacketEventsJar(project: Project): File {
val home = System.getProperty("user.home")
val defaultDir = Paths.get(home, "eclipse-workspace", "PacketEvents", "spigot", "build", "libs")
// Every resolution path is reported in the failure message regardless of which got tried.
val propValue = (project.findProperty("truogPacketEventsJar") as? String)?.takeIf { it.isNotBlank() } ?: "<unset>"
val envValue = System.getenv("TRUEOG_PACKETEVENTS_JAR")?.takeIf { it.isNotBlank() } ?: "<unset>"
val attempts = listOf(
"Gradle property truogPacketEventsJar=$propValue",
"Environment variable TRUEOG_PACKETEVENTS_JAR=$envValue",
"Default glob $defaultDir/PacketEvents-*.jar (case-insensitive; falls back to packetevents-spigot-*.jar; latest by mtime)",
)
val gradleProp = (project.findProperty("truogPacketEventsJar") as? String)?.trim()
if (!gradleProp.isNullOrEmpty()) {
val f = File(gradleProp)
if (f.isFile) return f
}
val envVar = System.getenv("TRUEOG_PACKETEVENTS_JAR")?.trim()
if (!envVar.isNullOrEmpty()) {
val f = File(envVar)
if (f.isFile) return f
}
if (Files.isDirectory(defaultDir)) {
val nameRegex = Regex("(?i)(PacketEvents|packetevents-spigot|packetevents)-.*\\.jar")
val match = Files.list(defaultDir).use { stream ->
stream
.filter { nameRegex.matches(it.fileName.toString()) }
.sorted(Comparator.comparingLong<Path> { Files.getLastModifiedTime(it).toMillis() }.reversed())
.findFirst()
.orElse(null)
}
if (match != null) return match.toFile()
}
throw GradleException(
buildString {
appendLine("True-OG PacketEvents JAR not found. Tried:")
attempts.forEach { appendLine(" - $it") }
appendLine("Provide one of:")
appendLine(" ./gradlew -PtruogPacketEventsJar=/abs/path/to/PacketEvents.jar ...")
appendLine(" TRUEOG_PACKETEVENTS_JAR=/abs/path/to/PacketEvents.jar ./gradlew ...")
appendLine(" Build the fork at \${user.home}/eclipse-workspace/PacketEvents (default location).")
},
)
}
// Registers a Copy task that stages the resolved JAR into runDir/plugins/PacketEvents.jar. See context/refs/packetevents-provider.md for the consumer integration snippet.
val registerStagePacketEventsTask: (Project, String, File) -> TaskProvider<Copy> = { p, taskName, runDir ->
p.tasks.register(taskName, Copy::class.java) {
group = "packetevents"
description = "Stage the locally built True-OG PacketEvents JAR into plugins/."
// Eager filesystem resolution at configuration time is incompatible with the config cache.
notCompatibleWithConfigurationCache(
"Resolves the JAR via filesystem inspection during configuration.",
)
val jar = resolveTrueOgPacketEventsJar(p)
from(jar)
into(runDir.resolve("plugins"))
rename { "PacketEvents.jar" }
}
}
extra["resolveTrueOgPacketEventsJar"] = { p: Project -> resolveTrueOgPacketEventsJar(p) }
extra["registerStagePacketEventsTask"] = registerStagePacketEventsTask
@@ -20,6 +20,7 @@ public class BukkitPlatformPlugin implements PlatformPlugin {
return plugin.getName();
}
@SuppressWarnings("deprecation")
@Override
public String getVersion() {
return plugin.getDescription().getVersion();
@@ -1,5 +1,7 @@
package ac.grim.grimac.platform.bukkit;
import ac.grim.grimac.GrimAPI;
import ac.grim.grimac.platform.api.Platform;
import ac.grim.grimac.platform.api.PlatformServer;
import ac.grim.grimac.platform.api.sender.Sender;
import io.github.retrooper.packetevents.util.SpigotReflectionUtil;
@@ -7,6 +9,7 @@ import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
public class BukkitPlatformServer implements PlatformServer {
@Override
@@ -32,6 +35,10 @@ public class BukkitPlatformServer implements PlatformServer {
@Override
public double getTPS() {
// Folia throws UnsupportedOperationException on calling getTPS(), there is no API for getting TPS on Folia
if (GrimAPI.INSTANCE.getPlatform() == Platform.FOLIA) {
return Double.NaN;
}
return SpigotReflectionUtil.getTPS();
}
}
@@ -1,21 +1,25 @@
package ac.grim.grimac.platform.bukkit;
import ac.grim.grimac.api.plugin.BasicGrimPlugin;
import ac.grim.grimac.GrimAPI;
import ac.grim.grimac.GrimExternalAPI;
import ac.grim.grimac.api.GrimAPIProvider;
import ac.grim.grimac.api.GrimAbstractAPI;
import ac.grim.grimac.api.event.EventBus;
import ac.grim.grimac.api.plugin.GrimPlugin;
import ac.grim.grimac.command.CloudCommandService;
import ac.grim.grimac.internal.platform.bukkit.resolver.BukkitResolverRegistrar;
import ac.grim.grimac.manager.init.Initable;
import ac.grim.grimac.manager.init.load.PacketEventsInit;
import ac.grim.grimac.manager.init.start.ExemptOnlinePlayersOnReload;
import ac.grim.grimac.manager.init.start.StartableInitable;
import ac.grim.grimac.platform.api.Platform;
import ac.grim.grimac.platform.api.PlatformLoader;
import ac.grim.grimac.platform.api.PlatformServer;
import ac.grim.grimac.platform.api.command.CommandService;
import ac.grim.grimac.platform.api.manager.ItemResetHandler;
import ac.grim.grimac.platform.api.manager.MessagePlaceHolderManager;
import ac.grim.grimac.platform.api.manager.ParserDescriptorFactory;
import ac.grim.grimac.platform.api.manager.PermissionRegistrationManager;
import ac.grim.grimac.platform.api.manager.PlatformPluginManager;
import ac.grim.grimac.platform.api.manager.cloud.CloudCommandAdapter;
import ac.grim.grimac.platform.api.player.PlatformPlayerFactory;
import ac.grim.grimac.platform.api.scheduler.PlatformScheduler;
import ac.grim.grimac.platform.api.sender.Sender;
@@ -33,9 +37,11 @@ import ac.grim.grimac.platform.bukkit.scheduler.bukkit.BukkitPlatformScheduler;
import ac.grim.grimac.platform.bukkit.scheduler.folia.FoliaPlatformScheduler;
import ac.grim.grimac.platform.bukkit.sender.BukkitSenderFactory;
import ac.grim.grimac.platform.bukkit.utils.placeholder.PlaceholderAPIExpansion;
import ac.grim.grimac.utils.anticheat.LogUtil;
import ac.grim.grimac.utils.lazy.LazyHolder;
import com.github.retrooper.packetevents.PacketEventsAPI;
import io.github.retrooper.packetevents.factory.spigot.SpigotPacketEventsBuilder;
import lombok.Getter;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.plugin.ServicePriority;
@@ -46,35 +52,36 @@ import org.incendo.cloud.brigadier.CloudBrigadierManager;
import org.incendo.cloud.bukkit.CloudBukkitCapabilities;
import org.incendo.cloud.execution.ExecutionCoordinator;
import org.incendo.cloud.paper.LegacyPaperCommandManager;
import org.incendo.cloud.setting.Configurable;
import org.jetbrains.annotations.NotNull;
public final class GrimACBukkitLoaderPlugin extends JavaPlugin implements PlatformLoader {
public static GrimACBukkitLoaderPlugin LOADER;
private final LazyHolder<PlatformScheduler> scheduler = LazyHolder.simple(this::createScheduler);
private final LazyHolder<PacketEventsAPI<?>> packetEvents = LazyHolder.simple(() -> SpigotPacketEventsBuilder.build(this));
private final LazyHolder<PacketEventsAPI<?>> packetEvents = LazyHolder.simple(() -> {
// External mode reuses the installed provider's API (plugin.yml depend orders load before this).
if (PacketEventsInit.isShadePE()) {
return SpigotPacketEventsBuilder.build(this);
}
return com.github.retrooper.packetevents.PacketEvents.getAPI();
});
private final LazyHolder<BukkitSenderFactory> senderFactory = LazyHolder.simple(BukkitSenderFactory::new);
private final LazyHolder<CommandManager<Sender>> commandManager = LazyHolder.simple(this::createCommandManager);
private final LazyHolder<ItemResetHandler> itemResetHandler = LazyHolder.simple(BukkitItemResetHandler::new);
private final LazyHolder<CommandService> commandService = LazyHolder.simple(this::createCommandService);
private final CloudCommandAdapter commandAdapter = new BukkitParserDescriptorFactory();
private final PlatformPlayerFactory playerFactory = new BukkitPlatformPlayerFactory();
private final ParserDescriptorFactory parserFactory = new BukkitParserDescriptorFactory();
private final PlatformPluginManager platformPluginManager = new BukkitPlatformPluginManager();
private final GrimPlugin plugin;
private final PlatformServer platformServer = new BukkitPlatformServer();
private final MessagePlaceHolderManager messagePlaceHolderManager = new BukkitMessagePlaceHolderManager();
private final BukkitPermissionRegistrationManager bukkitPermissionRegistrationManager = new BukkitPermissionRegistrationManager();
@Getter private final PlatformPlayerFactory platformPlayerFactory = new BukkitPlatformPlayerFactory();
@Getter private final PlatformPluginManager pluginManager = new BukkitPlatformPluginManager();
@Getter private final GrimPlugin plugin;
@Getter private final PlatformServer platformServer = new BukkitPlatformServer();
@Getter private final MessagePlaceHolderManager messagePlaceHolderManager = new BukkitMessagePlaceHolderManager();
@Getter private final BukkitPermissionRegistrationManager permissionManager = new BukkitPermissionRegistrationManager();
public GrimACBukkitLoaderPlugin() {
this.plugin = new BasicGrimPlugin(
this.getLogger(),
this.getDataFolder(),
this.getDescription().getVersion(),
this.getDescription().getDescription(),
this.getDescription().getAuthors()
);
BukkitResolverRegistrar registrar = new BukkitResolverRegistrar();
registrar.registerAll(GrimAPI.INSTANCE.getExtensionManager());
this.plugin = registrar.resolvePlugin(this);
}
@Override
@@ -112,82 +119,133 @@ public final class GrimACBukkitLoaderPlugin extends JavaPlugin implements Platfo
return scheduler.get();
}
@Override
public PlatformPlayerFactory getPlatformPlayerFactory() {
return playerFactory;
}
@Override
public ParserDescriptorFactory getParserDescriptorFactory() {
return parserFactory;
}
@Override
public PacketEventsAPI<?> getPacketEvents() {
return packetEvents.get();
}
@Override
public CommandManager<Sender> getCommandManager() {
return commandManager.get();
}
@Override
public ItemResetHandler getItemResetHandler() {
return itemResetHandler.get();
}
@Override
public CommandService getCommandService() {
return commandService.get();
}
@Override
public SenderFactory<CommandSender> getSenderFactory() {
return senderFactory.get();
}
@Override
public GrimPlugin getPlugin() {
return plugin;
}
@Override
public PlatformPluginManager getPluginManager() {
return platformPluginManager;
}
@Override
public PlatformServer getPlatformServer() {
return platformServer;
}
@Override
public void registerAPIService() {
GrimAPIProvider.init(GrimAPI.INSTANCE.getExternalAPI());
Bukkit.getServicesManager().register(GrimAbstractAPI.class, GrimAPI.INSTANCE.getExternalAPI(), GrimACBukkitLoaderPlugin.LOADER, ServicePriority.Normal);
}
final GrimExternalAPI externalAPI = GrimAPI.INSTANCE.getExternalAPI();
final EventBus eventBus = externalAPI.getEventBus();
final ac.grim.grimac.api.plugin.GrimPlugin context = GrimAPI.INSTANCE.getGrimPlugin();
@Override
public @NotNull MessagePlaceHolderManager getMessagePlaceHolderManager() {
return messagePlaceHolderManager;
}
eventBus.subscribe(context, ac.grim.grimac.api.event.events.GrimJoinEvent.class, (event) -> {
ac.grim.grimac.api.events.GrimJoinEvent bukkitEvent =
new ac.grim.grimac.api.events.GrimJoinEvent(event.getUser());
@Override
public PermissionRegistrationManager getPermissionManager() {
return bukkitPermissionRegistrationManager;
Bukkit.getPluginManager().callEvent(bukkitEvent);
});
eventBus.subscribe(context, ac.grim.grimac.api.event.events.GrimQuitEvent.class, (event) -> {
ac.grim.grimac.api.events.GrimQuitEvent bukkitEvent =
new ac.grim.grimac.api.events.GrimQuitEvent(event.getUser());
Bukkit.getPluginManager().callEvent(bukkitEvent);
});
eventBus.subscribe(context, ac.grim.grimac.api.event.events.GrimReloadEvent.class, (event) -> {
ac.grim.grimac.api.events.GrimReloadEvent bukkitEvent =
new ac.grim.grimac.api.events.GrimReloadEvent(event.isSuccess());
Bukkit.getPluginManager().callEvent(bukkitEvent);
});
eventBus.subscribe(context, ac.grim.grimac.api.event.events.FlagEvent.class, (event) -> {
ac.grim.grimac.api.events.FlagEvent bukkitEvent =
new ac.grim.grimac.api.events.FlagEvent(
event.getUser(),
event.getCheck(),
event.getVerbose()
);
Bukkit.getPluginManager().callEvent(bukkitEvent);
if (bukkitEvent.isCancelled()) {
event.setCancelled(true);
}
});
eventBus.subscribe(context, ac.grim.grimac.api.event.events.CommandExecuteEvent.class, (event) -> {
ac.grim.grimac.api.events.CommandExecuteEvent bukkitEvent =
new ac.grim.grimac.api.events.CommandExecuteEvent(
event.getUser(),
event.getCheck(),
event.getVerbose(),
event.getCommand()
);
Bukkit.getPluginManager().callEvent(bukkitEvent);
if (bukkitEvent.isCancelled()) {
event.setCancelled(true);
}
});
eventBus.subscribe(context, ac.grim.grimac.api.event.events.CompletePredictionEvent.class, (event) -> {
// Note: New event doesn't have verbose, passing null or check name is standard fallback
ac.grim.grimac.api.events.CompletePredictionEvent bukkitEvent =
new ac.grim.grimac.api.events.CompletePredictionEvent(
event.getUser(),
event.getCheck(),
"",
event.getOffset()
);
Bukkit.getPluginManager().callEvent(bukkitEvent);
if (bukkitEvent.isCancelled()) {
event.setCancelled(true);
}
});
GrimAPIProvider.init(externalAPI);
Bukkit.getServicesManager().register(GrimAbstractAPI.class, externalAPI, GrimACBukkitLoaderPlugin.LOADER, ServicePriority.Normal);
}
private PlatformScheduler createScheduler() {
return GrimAPI.INSTANCE.getPlatform() == Platform.FOLIA ? new FoliaPlatformScheduler() : new BukkitPlatformScheduler();
}
private CommandManager<Sender> createCommandManager() {
private CommandService createCommandService() {
try {
return new CloudCommandService(this::createCloudCommandManager, commandAdapter);
} catch (Throwable t) {
LogUtil.warn("CRITICAL: Failed to initialize Command Framework. " +
"Grim will continue to run with no commands.", t);
return () -> {};
}
}
private CommandManager<Sender> createCloudCommandManager() {
LegacyPaperCommandManager<Sender> manager = new LegacyPaperCommandManager<>(
this,
ExecutionCoordinator.simpleCoordinator(),
senderFactory.get()
);
if (manager.hasCapability(CloudBukkitCapabilities.NATIVE_BRIGADIER)) {
manager.registerBrigadier();
CloudBrigadierManager<Sender, ?> cbm = manager.brigadierManager();
Configurable<BrigadierSetting> settings = cbm.settings();
settings.set(BrigadierSetting.FORCE_EXECUTABLE, true);
try {
manager.registerBrigadier();
CloudBrigadierManager<Sender, ?> cbm = manager.brigadierManager();
cbm.settings().set(BrigadierSetting.FORCE_EXECUTABLE, true);
} catch (Throwable t) {
LogUtil.error("Failed to register Brigadier native completions. Falling back to standard completions.", t);
}
} else if (manager.hasCapability(CloudBukkitCapabilities.ASYNCHRONOUS_COMPLETION)) {
manager.registerAsynchronousCompletions();
}
@@ -4,16 +4,15 @@ import ac.grim.grimac.GrimAPI;
import ac.grim.grimac.platform.api.command.PlayerSelector;
import ac.grim.grimac.platform.api.sender.Sender;
import ac.grim.grimac.platform.bukkit.sender.BukkitSenderFactory;
import lombok.RequiredArgsConstructor;
import org.incendo.cloud.bukkit.data.SinglePlayerSelector;
import java.util.Collection;
import java.util.Collections;
@RequiredArgsConstructor
public class BukkitPlayerSelectorAdapter implements PlayerSelector {
private final org.incendo.cloud.bukkit.data.SinglePlayerSelector bukkitSelector;
public BukkitPlayerSelectorAdapter(org.incendo.cloud.bukkit.data.SinglePlayerSelector bukkitSelector) {
this.bukkitSelector = bukkitSelector;
}
private final SinglePlayerSelector bukkitSelector;
@Override
public boolean isSingle() {
@@ -6,8 +6,10 @@ import ac.grim.grimac.platform.bukkit.utils.convert.BukkitConversionUtils;
import ac.grim.grimac.platform.bukkit.utils.reflection.PaperUtils;
import ac.grim.grimac.platform.bukkit.world.BukkitPlatformWorld;
import ac.grim.grimac.utils.math.Location;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import org.bukkit.entity.Entity;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.jetbrains.annotations.NotNull;
import java.util.Objects;
import java.util.UUID;
@@ -15,12 +17,13 @@ import java.util.concurrent.CompletableFuture;
public class BukkitGrimEntity implements GrimEntity {
protected static final boolean CAN_USE_DIRECT_GETTERS = PacketEvents.getAPI().getServerManager().getVersion().isNewerThan(ServerVersion.V_1_20_1) && PaperUtils.PAPER;
private final Entity entity;
private BukkitPlatformWorld bukkitPlatformWorld;
public BukkitGrimEntity(Entity entity) {
Objects.requireNonNull(entity);
this.entity = entity;
this.entity = Objects.requireNonNull(entity);
}
public Entity getBukkitEntity() {
@@ -44,7 +47,7 @@ public class BukkitGrimEntity implements GrimEntity {
}
@Override
@NonNull
@NotNull
public Entity getNative() {
return entity;
}
@@ -60,7 +63,6 @@ public class BukkitGrimEntity implements GrimEntity {
if (bukkitPlatformWorld == null || !bukkitPlatformWorld.getBukkitWorld().equals(entity.getWorld())) {
bukkitPlatformWorld = new BukkitPlatformWorld(entity.getWorld());
}
return bukkitPlatformWorld;
}
@@ -76,4 +78,26 @@ public class BukkitGrimEntity implements GrimEntity {
location.getPitch()
);
}
@Override
public double distanceSquared(double oX, double oY, double oZ) {
if (CAN_USE_DIRECT_GETTERS) {
double x = this.entity.getX();
double y = this.entity.getY();
double z = this.entity.getZ();
double distX = (x - oX) * (x - oX);
double distY = (y - oY) * (y - oY);
double distZ = (z - oZ) * (z - oZ);
return distX + distY + distZ;
} else {
org.bukkit.Location location = this.entity.getLocation();
double x = location.getX();
double y = location.getY();
double z = location.getZ();
double distX = (x - oX) * (x - oX);
double distY = (y - oY) * (y - oY);
double distZ = (z - oZ) * (z - oZ);
return distX + distY + distZ;
}
}
}
@@ -4,7 +4,7 @@ import ac.grim.grimac.GrimAPI;
import ac.grim.grimac.platform.bukkit.utils.convert.BukkitConversionUtils;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
import ac.grim.grimac.utils.data.PistonData;
import ac.grim.grimac.utils.data.PistonTemplate;
import com.github.retrooper.packetevents.protocol.world.BlockFace;
import org.bukkit.Material;
import org.bukkit.block.Block;
@@ -18,8 +18,19 @@ import java.util.ArrayList;
import java.util.List;
public class PistonEvent implements Listener {
Material SLIME_BLOCK = Material.getMaterial("SLIME_BLOCK");
Material HONEY_BLOCK = Material.getMaterial("HONEY_BLOCK");
private final Material SLIME_BLOCK = Material.getMaterial("SLIME_BLOCK");
private final Material HONEY_BLOCK = Material.getMaterial("HONEY_BLOCK");
private static final double MAX_HORIZONTAL_DISTANCE = 24.0;
private static final double MAX_VERTICAL_DISTANCE = 64.0;
// accuracy isn't that important, it's close enough and performant
private static boolean isCloseEnough(int x, int y, int z, double posX, double posY, double posZ) {
return Math.abs(x - posX) <= MAX_HORIZONTAL_DISTANCE
&& Math.abs(y - posY) <= MAX_VERTICAL_DISTANCE
&& Math.abs(z - posZ) <= MAX_HORIZONTAL_DISTANCE;
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onPistonPushEvent(BlockPistonExtendEvent event) {
@@ -55,17 +66,8 @@ public class PistonEvent implements Listener {
piston.getY() + event.getDirection().getModY(),
piston.getZ() + event.getDirection().getModZ()));
boolean finalHasSlimeBlock = hasSlimeBlock;
boolean finalHasHoneyBlock = hasHoneyBlock;
for (GrimPlayer player : GrimAPI.INSTANCE.getPlayerDataManager().getEntries()) {
int lastTrans = player.lastTransactionSent.get();
player.runSafely(() -> {
if (player.compensatedWorld.isChunkLoaded(event.getBlock().getX() >> 4, event.getBlock().getZ() >> 4)) {
PistonData data = new PistonData(BukkitConversionUtils.fromBukkitFace(event.getDirection()), boxes, lastTrans, true, finalHasSlimeBlock, finalHasHoneyBlock);
player.latencyUtils.addRealTimeTaskAsync(lastTrans, () -> player.compensatedWorld.activePistons.add(data));
}
});
}
PistonTemplate data = new PistonTemplate(BukkitConversionUtils.fromBukkitFace(event.getDirection()), boxes, true, hasSlimeBlock, hasHoneyBlock);
addPistonData(data, piston.getX(), piston.getY(), piston.getZ());
}
// For some unknown reason, bukkit handles this stupidly
@@ -87,9 +89,8 @@ public class PistonEvent implements Listener {
BlockFace face = BukkitConversionUtils.fromBukkitFace(event.getDirection());
// The event was called without blocks and is therefore in the right direction
Block piston = event.getBlock();
if (event.getBlocks().isEmpty()) {
Block piston = event.getBlock();
// Add bounding box of the actual piston head pushing
boxes.add(new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true)
.offset(piston.getX() + face.getModX(),
@@ -113,15 +114,18 @@ public class PistonEvent implements Listener {
}
}
boolean finalHasSlimeBlock = hasSlimeBlock;
boolean finalHasHoneyBlock = hasHoneyBlock;
PistonTemplate data = new PistonTemplate(face, boxes, false, hasSlimeBlock, hasHoneyBlock);
addPistonData(data, piston.getX(), piston.getY(), piston.getZ());
}
private void addPistonData(PistonTemplate pistonTemplate, int blockX, int blockY, int blockZ) {
for (GrimPlayer player : GrimAPI.INSTANCE.getPlayerDataManager().getEntries()) {
if (player.compensatedWorld.isChunkLoaded(blockX >> 4, blockZ >> 4) || !isCloseEnough(blockX, blockY, blockZ, player.x, player.y, player.z)) continue;
int lastTrans = player.lastTransactionSent.get();
player.runSafely(() -> {
if (player.compensatedWorld.isChunkLoaded(event.getBlock().getX() >> 4, event.getBlock().getZ() >> 4)) {
PistonData data = new PistonData(BukkitConversionUtils.fromBukkitFace(event.getDirection()), boxes, lastTrans, false, finalHasSlimeBlock, finalHasHoneyBlock);
player.latencyUtils.addRealTimeTaskAsync(lastTrans, () -> player.compensatedWorld.activePistons.add(data));
}
player.latencyUtils.addRealTimeTaskAsync(lastTrans, () -> {
player.compensatedWorld.addPiston(pistonTemplate, lastTrans);
});
}
}
@@ -2,15 +2,14 @@ package ac.grim.grimac.platform.bukkit.initables;
import ac.grim.grimac.manager.init.start.StartableInitable;
import ac.grim.grimac.platform.bukkit.GrimACBukkitLoaderPlugin;
import ac.grim.grimac.utils.anticheat.Constants;
import io.github.retrooper.packetevents.bstats.bukkit.Metrics;
public class BukkitBStats implements StartableInitable {
@Override
public void start() {
int pluginId = 12820; // <-- Replace with the id of your plugin!
try {
new Metrics(GrimACBukkitLoaderPlugin.LOADER, pluginId);
} catch (Exception ignored) {
}
new Metrics(GrimACBukkitLoaderPlugin.LOADER, Constants.BSTATS_PLUGIN_ID);
} catch (Exception ignored) {}
}
}
@@ -8,6 +8,7 @@ import ac.grim.grimac.platform.bukkit.utils.reflection.PaperUtils;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.LogUtil;
import ac.grim.grimac.utils.lists.HookedListWrapper;
import ac.grim.grimac.utils.reflection.ReflectionUtils;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.util.reflection.Reflection;
@@ -25,6 +26,20 @@ import java.util.List;
@SuppressWarnings(value = {"unchecked", "deprecated"})
public class BukkitTickEndEvent extends AbstractTickEndEvent implements Listener {
private Boolean getLateBindState() {
Class<?> spigotConfig = ReflectionUtils.getClass("org.spigotmc.SpigotConfig");
// ReflectionUtils.getField(class, name) handles the loop and setAccessible
Field field = ReflectionUtils.getField(spigotConfig, "lateBind");
if (field == null) return null;
try {
return (boolean) field.get(null);
} catch (Exception ignored) {
return null;
}
}
@Override
public void start() {
if (!super.shouldInjectEndTick()) {
@@ -38,9 +53,18 @@ public class BukkitTickEndEvent extends AbstractTickEndEvent implements Listener
PaperUtils.registerTickEndEvent(this, this::tickAllFoliaPlayers);
return;
}
// if it fails to inject, try to use paper's event
if (!injectWithReflection() && !PaperUtils.registerTickEndEvent(this, this::tickAllPlayers)) {
// if it fails to register Paper event, try to inject via reflection
if (!PaperUtils.registerTickEndEvent(this, this::tickAllPlayers) && !injectWithReflection()) {
LogUtil.error("Failed to inject into the end of tick event!");
if (PacketEvents.getAPI().getServerManager().getVersion().isOlderThan(ServerVersion.V_1_14_4)) {
Boolean lateBind = getLateBindState();
if (lateBind == null) {
LogUtil.error("Failed to determine the late-bind state. Perhaps you are using a custom server fork? Check the fork configuration for a late-bind option and disable it.");
} else if (lateBind) {
LogUtil.error("Injection failed because the late-bind option is enabled. Disable it in spigot.yml.");
}
}
}
}
@@ -65,6 +89,7 @@ public class BukkitTickEndEvent extends AbstractTickEndEvent implements Listener
// Inject so we can add the final transaction pre-flush event
try {
Object connection = SpigotReflectionUtil.getMinecraftServerConnectionInstance();
if (connection == null) return false;
Field connectionsList = Reflection.getField(connection.getClass(), List.class, 1);
List<Object> endOfTickObject = (List<Object>) connectionsList.get(connection);
@@ -86,7 +111,7 @@ public class BukkitTickEndEvent extends AbstractTickEndEvent implements Listener
Unsafe unsafe = (Unsafe) unsafeField.get(null);
unsafe.putObject(connection, unsafe.objectFieldOffset(connectionsList), wrapper);
} catch (NoSuchFieldException | IllegalAccessException e) {
LogUtil.exception("Failed to inject into the end of tick event via reflection", e);
LogUtil.error("Failed to inject into the end of tick event via reflection", e);
return false;
}
return true;
@@ -2,95 +2,212 @@ package ac.grim.grimac.platform.bukkit.manager;
import ac.grim.grimac.platform.api.manager.ItemResetHandler;
import ac.grim.grimac.platform.api.player.PlatformPlayer;
import ac.grim.grimac.platform.bukkit.player.BukkitPlatformPlayer;
import ac.grim.grimac.platform.bukkit.utils.reflection.PaperUtils;
import ac.grim.grimac.utils.reflection.ReflectionUtils;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import lombok.SneakyThrows;
import com.github.retrooper.packetevents.protocol.player.InteractionHand;
import org.bukkit.Bukkit;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.bukkit.inventory.EquipmentSlot;
import org.jetbrains.annotations.Nullable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
public class BukkitItemResetHandler implements ItemResetHandler {
// resets item usage, then returns whether the player was using an item
private final @NotNull ItemUsageReset resetItemUsage = createItemUsageResetFunction();
private static final Consumer<Player> resetItemUsage;
private static final Predicate<Player> isUsingItem;
private static final Function<Player, InteractionHand> getItemUsageHand;
@SneakyThrows
@Override
public void resetItemUsage(@Nullable PlatformPlayer player) {
if (player != null) {
resetItemUsage.accept(((BukkitPlatformPlayer) player).getNative());
}
if (player != null) resetItemUsage.accept((Player) player.getNative());
}
@SneakyThrows
private @NotNull ItemUsageReset createItemUsageResetFunction() {
ServerVersion version = PacketEvents.getAPI().getServerManager().getVersion();
if (version.isNewerThan(ServerVersion.V_1_17) && PaperUtils.PAPER) {
if (version.isOlderThan(ServerVersion.V_1_19)) {
return LivingEntity::clearActiveItem;
@Override
public @Nullable InteractionHand getItemUsageHand(@Nullable PlatformPlayer player) {
return player == null ? null : getItemUsageHand.apply((Player) player.getNative());
}
@Override
public boolean isUsingItem(@Nullable PlatformPlayer player) {
return player != null && isUsingItem.test((Player) player.getNative());
}
static {
final ServerVersion version = PacketEvents.getAPI().getServerManager().getVersion();
boolean legacy = version.isOlderThanOrEquals(ServerVersion.V_1_8_8);
try {
Method getHandle_ = null;
if (PaperUtils.PAPER && version.isNewerThanOrEquals(ServerVersion.V_1_20_5)) {
Class<?> cls = ReflectionUtils.getClass("org.bukkit.craftbukkit.entity.CraftLivingEntity");
if (cls != null) getHandle_ = cls.getMethod("getHandle");
}
Method setLivingEntityFlag = Class.forName(version.isOlderThan(ServerVersion.V_1_20_5) ? "net.minecraft.world.entity.EntityLiving" : "net.minecraft.world.entity.LivingEntity")
.getDeclaredMethod(version.isOlderThan(ServerVersion.V_1_20_5) ? "c" : "setLivingEntityFlag", int.class, boolean.class);
Method getHandle = (version.isOlderThan(ServerVersion.V_1_20_5)
? Class.forName("org.bukkit.craftbukkit." + Bukkit.getServer().getClass().getPackageName().split("\\.")[3] + ".entity.CraftPlayer")
: Class.forName("org.bukkit.craftbukkit.entity.CraftPlayer")
).getMethod("getHandle");
setLivingEntityFlag.setAccessible(true);
boolean obfuscated = getHandle_ == null;
String nmsPackage = obfuscated ? Bukkit.getServer().getClass().getPackageName().split("\\.")[3] : null;
return player -> {
// don't trigger gameevents
setLivingEntityFlag.invoke(getHandle.invoke(player), 1, false);
player.clearActiveItem();
};
}
if (obfuscated) {
String clazzName = legacy ? "CraftHumanEntity" : "CraftLivingEntity";
getHandle_ = Class.forName("org.bukkit.craftbukkit." + nmsPackage + ".entity." + clazzName).getMethod("getHandle");
}
if (version == ServerVersion.V_1_8_8) {
Class<?> EntityHuman = Class.forName("net.minecraft.server.v1_8_R3.EntityHuman");
Method getHandle = Class.forName("org.bukkit.craftbukkit.v1_8_R3.entity.CraftPlayer").getMethod("getHandle");
Method clearActiveItem = EntityHuman.getMethod("bV");
Method isUsingItem = EntityHuman.getMethod("bS");
final Method getHandle = getHandle_;
Class<?> clazz = getHandle_.getReturnType();
return player -> {
Object handle = getHandle.invoke(player);
clearActiveItem.invoke(handle);
if (version.isNewerThanOrEquals(ServerVersion.V_1_10)) {
isUsingItem = Player::isHandRaised;
} else {
Method method = clazz.getMethod(switch (Objects.requireNonNull(nmsPackage)) {
case "v1_8_R3" -> "bS";
case "v1_9_R1" -> "cs";
case "v1_9_R2" -> "ct";
default -> throw new IllegalStateException("You are using an unsupported server version! (" + version.getReleaseName() + ")");
});
isUsingItem = player -> {
try {
return (boolean) method.invoke(getHandle.invoke(player));
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
};
}
// in 1.8 we need to resync item usage manually,
// only do so if the player was using an item
if ((boolean) isUsingItem.invoke(handle)) {
player.updateInventory();
if (legacy) {
getItemUsageHand = player -> isUsingItem.test(player) ? InteractionHand.MAIN_HAND : null;
} else if (PaperUtils.PAPER && version.isNewerThanOrEquals(ServerVersion.V_1_16_5)) {
getItemUsageHand = player -> player.isHandRaised()
? player.getHandRaised() == EquipmentSlot.OFF_HAND
? InteractionHand.OFF_HAND
: InteractionHand.MAIN_HAND
: null;
} else {
Method method = clazz.getMethod(switch (Objects.requireNonNull(nmsPackage)) {
case "v1_9_R1" -> "ct";
case "v1_9_R2" -> "cu";
case "v1_10_R1" -> "cy";
case "v1_11_R1" -> "cz";
case "v1_12_R1" -> "cH";
case "v1_13_R1", "v1_13_R2", "v1_14_R1" -> "cU";
case "v1_15_R1", "v1_16_R1", "v1_16_R2", "v1_16_R3", "v1_17_R1" -> "getRaisedHand";
case "v1_18_R1" -> "eM";
case "v1_18_R2" -> "eN";
case "v1_19_R1" -> "eU";
case "v1_19_R2" -> "fa";
case "v1_19_R3" -> "ff";
case "v1_20_R1" -> "fj";
case "v1_20_R2" -> "fn";
case "v1_20_R3" -> "fo";
case "v1_20_R4" -> "fw";
case "v1_21_R1" -> "fs";
case "v1_21_R2", "v1_21_R3", "v1_21_R4" -> "fA";
case "v1_21_R5" -> "fH";
case "v1_21_R6" -> "fP";
default -> throw new IllegalStateException("You are using an unsupported server version! (" + version.getReleaseName() + ")");
});
getItemUsageHand = player -> {
try {
return isUsingItem.test(player)
? ((Enum<?>) method.invoke(getHandle.invoke(player))).ordinal() == 0
? InteractionHand.MAIN_HAND
: InteractionHand.OFF_HAND
: null;
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
};
}
Method setLivingEntityFlag;
if (version.isNewerThanOrEquals(ServerVersion.V_1_19)) {
String name = obfuscated ? "c" : "setLivingEntityFlag";
setLivingEntityFlag = clazz.getDeclaredMethod(name, int.class, boolean.class);
setLivingEntityFlag.setAccessible(true);
} else {
setLivingEntityFlag = null;
}
if (PaperUtils.PAPER && version.isNewerThan(ServerVersion.V_1_17)) {
resetItemUsage = setLivingEntityFlag == null ? LivingEntity::clearActiveItem : player -> {
try {
setLivingEntityFlag.invoke(getHandle.invoke(player), 1, false);
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
player.clearActiveItem();
};
} else {
Method method = clazz.getMethod(switch (Objects.requireNonNull(nmsPackage)) {
case "v1_8_R3" -> "bV";
case "v1_9_R1" -> "cz";
case "v1_9_R2" -> "cA";
case "v1_10_R1" -> "cE";
case "v1_11_R1" -> "cF";
case "v1_12_R1" -> "cN";
case "v1_13_R1", "v1_13_R2" -> "da";
case "v1_14_R1" -> "dp";
case "v1_15_R1" -> "dH";
case "v1_16_R1", "v1_16_R2", "v1_16_R3", "v1_17_R1" -> "clearActiveItem";
case "v1_18_R1" -> "eR";
case "v1_18_R2" -> "eS";
case "v1_19_R1" -> "eZ";
case "v1_19_R2" -> "ff";
case "v1_19_R3" -> "fk";
case "v1_20_R1" -> "fo";
case "v1_20_R2" -> "fs";
case "v1_20_R3" -> "ft";
case "v1_20_R4" -> "fB";
case "v1_21_R1" -> "fx";
case "v1_21_R2", "v1_21_R3", "v1_21_R4" -> "fF";
case "v1_21_R5" -> "fM";
case "v1_21_R6" -> "fU";
default -> throw new IllegalStateException("You are using an unsupported server version! (" + version.getReleaseName() + ")");
});
if (legacy) { // 1.8.8
resetItemUsage = player -> {
try {
method.invoke(getHandle.invoke(player));
// in 1.8 we need to resync item usage manually,
// only do so if the player is using an item
if (isUsingItem.test(player)) player.updateInventory();
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
};
} else if (setLivingEntityFlag == null) { // 1.9-1.18.2
resetItemUsage = player -> {
try {
method.invoke(getHandle.invoke(player));
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
};
} else { // 1.19+
resetItemUsage = player -> {
try {
Object handle = getHandle.invoke(player);
setLivingEntityFlag.invoke(handle, 1, false);
method.invoke(handle);
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
};
}
};
}
String nmsPackage = Bukkit.getServer().getClass().getPackageName().split("\\.")[3];
String livingEntityPackage = version.isNewerThan(ServerVersion.V_1_16_5) ? "net.minecraft.world.entity.EntityLiving" : "net.minecraft.server." + nmsPackage + ".EntityLiving";
Method getHandle = Class.forName("org.bukkit.craftbukkit." + nmsPackage + ".entity.CraftPlayer").getMethod("getHandle");
Method clearActiveItem = Class.forName(livingEntityPackage).getMethod(
switch (nmsPackage) {
case "v1_9_R1" -> "cz";
case "v1_9_R2" -> "cA";
case "v1_10_R1" -> "cE";
case "v1_11_R1" -> "cF";
case "v1_12_R1" -> "cN";
case "v1_13_R1", "v1_13_R2" -> "da";
case "v1_14_R1" -> "dp";
case "v1_15_R1" -> "dH";
case "v1_16_R1", "v1_16_R2", "v1_16_R3", "v1_17_R1" -> "clearActiveItem";
case "v1_18_R1" -> "eR";
case "v1_18_R2" -> "eS";
default -> throw new IllegalStateException("You are using an unsupported server version! (" + version.getReleaseName() + ")");
}
);
return player -> clearActiveItem.invoke(getHandle.invoke(player));
}
private interface ItemUsageReset {
void accept(@NotNull Player player) throws Throwable;
} catch (Throwable t) {
throw t instanceof RuntimeException e ? e : new RuntimeException(t);
}
}
}
@@ -5,14 +5,14 @@ import ac.grim.grimac.platform.api.player.PlatformPlayer;
import ac.grim.grimac.platform.bukkit.player.BukkitPlatformPlayer;
import com.github.retrooper.packetevents.util.reflection.Reflection;
import me.clip.placeholderapi.PlaceholderAPI;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class BukkitMessagePlaceHolderManager implements MessagePlaceHolderManager {
public static final boolean hasPlaceholderAPI = Reflection.getClassByNameWithoutException("me.clip.placeholderapi.PlaceholderAPI") != null;
@Override
public @NonNull String replacePlaceholders(@Nullable PlatformPlayer player, @NonNull String string) {
public @NotNull String replacePlaceholders(@Nullable PlatformPlayer player, @NotNull String string) {
if (!hasPlaceholderAPI) return string;
return PlaceholderAPI.setPlaceholders(player instanceof BukkitPlatformPlayer bukkitPlatformPlayer ? bukkitPlatformPlayer.getBukkitPlayer() : null, string);
}
@@ -1,17 +1,45 @@
package ac.grim.grimac.platform.bukkit.manager;
import ac.grim.grimac.platform.api.command.PlayerSelector;
import ac.grim.grimac.platform.api.manager.ParserDescriptorFactory;
import ac.grim.grimac.platform.api.manager.cloud.CloudCommandAdapter;
import ac.grim.grimac.platform.api.sender.Sender;
import ac.grim.grimac.platform.bukkit.command.BukkitPlayerSelectorParser;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.incendo.cloud.bukkit.BukkitCommandContextKeys;
import org.incendo.cloud.parser.ParserDescriptor;
import org.incendo.cloud.suggestion.Suggestion;
import org.incendo.cloud.suggestion.SuggestionProvider;
public class BukkitParserDescriptorFactory implements ParserDescriptorFactory {
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
BukkitPlayerSelectorParser<Sender> bukkitPlayerSelectorParser = new BukkitPlayerSelectorParser<>();
public class BukkitParserDescriptorFactory implements CloudCommandAdapter {
// Parser is kept as a field because its stateless
private final BukkitPlayerSelectorParser<Sender> bukkitPlayerSelectorParser = new BukkitPlayerSelectorParser<>();
@Override
public ParserDescriptor<Sender, PlayerSelector> getSinglePlayer() {
public ParserDescriptor<Sender, PlayerSelector> singlePlayerSelectorParser() {
return bukkitPlayerSelectorParser.descriptor();
}
// TODO (Cross-platform) better + brigadier suggestions
@Override
public SuggestionProvider<Sender> onlinePlayerSuggestions() {
return (context, input) -> {
List<Suggestion> suggestions = new ArrayList<>();
for(Player player : Bukkit.getOnlinePlayers()) {
CommandSender bukkit = context.get(BukkitCommandContextKeys.BUKKIT_COMMAND_SENDER);
if (!(bukkit instanceof Player) || ((Player)bukkit).canSee(player)) {
suggestions.add(Suggestion.suggestion(player.getName()));
}
}
return CompletableFuture.completedFuture(suggestions);
};
}
}
@@ -5,7 +5,7 @@ import ac.grim.grimac.platform.api.manager.PlatformPluginManager;
import ac.grim.grimac.platform.bukkit.BukkitPlatformPlugin;
import org.bukkit.Bukkit;
import org.bukkit.plugin.Plugin;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.jetbrains.annotations.Nullable;
public class BukkitPlatformPluginManager implements PlatformPluginManager {
@@ -0,0 +1,34 @@
package ac.grim.grimac.platform.bukkit.player;
import ac.grim.grimac.platform.api.player.OfflinePlatformPlayer;
import lombok.RequiredArgsConstructor;
import org.bukkit.OfflinePlayer;
import org.jetbrains.annotations.NotNull;
import java.util.Objects;
import java.util.UUID;
@RequiredArgsConstructor
public class BukkitOfflinePlatformPlayer implements OfflinePlatformPlayer {
private final OfflinePlayer offlinePlayer;
@Override
public boolean isOnline() {
return offlinePlayer.isOnline();
}
@Override
public @NotNull String getName() {
return Objects.requireNonNull(offlinePlayer.getName());
}
@Override
public @NotNull UUID getUniqueId() {
return offlinePlayer.getUniqueId();
}
@Override
public boolean equals(Object o) {
return o instanceof OfflinePlatformPlayer player && this.getUniqueId().equals(player.getUniqueId());
}
}
@@ -3,16 +3,14 @@ package ac.grim.grimac.platform.bukkit.player;
import ac.grim.grimac.platform.api.player.PlatformInventory;
import com.github.retrooper.packetevents.protocol.item.ItemStack;
import io.github.retrooper.packetevents.util.SpigotConversionUtil;
import lombok.RequiredArgsConstructor;
import org.bukkit.entity.Player;
@RequiredArgsConstructor
public class BukkitPlatformInventory implements PlatformInventory {
private final Player bukkitPlayer;
public BukkitPlatformInventory(Player bukkitPlayer) {
this.bukkitPlayer = bukkitPlayer;
}
@Override
public ItemStack getItemInHand() {
return SpigotConversionUtil.fromBukkitItemStack(bukkitPlayer.getInventory().getItemInHand());
@@ -1,6 +1,7 @@
package ac.grim.grimac.platform.bukkit.player;
import ac.grim.grimac.platform.api.entity.GrimEntity;
import ac.grim.grimac.platform.api.player.BlockTranslator;
import ac.grim.grimac.platform.api.player.PlatformInventory;
import ac.grim.grimac.platform.api.player.PlatformPlayer;
import ac.grim.grimac.platform.api.sender.Sender;
@@ -9,34 +10,44 @@ import ac.grim.grimac.platform.bukkit.entity.BukkitGrimEntity;
import ac.grim.grimac.platform.bukkit.utils.anticheat.MultiLibUtil;
import ac.grim.grimac.platform.bukkit.utils.convert.BukkitConversionUtils;
import ac.grim.grimac.platform.bukkit.utils.reflection.PaperUtils;
import ac.grim.grimac.utils.common.arguments.CommonGrimArguments;
import ac.grim.grimac.utils.math.Location;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.protocol.player.GameMode;
import com.github.retrooper.packetevents.protocol.player.User;
import com.github.retrooper.packetevents.util.Vector3d;
import io.github.retrooper.packetevents.util.SpigotConversionUtil;
import lombok.Getter;
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
import net.kyori.adventure.text.Component;
import org.bukkit.World;
import org.bukkit.entity.Player;
import org.bukkit.permissions.Permission;
import org.bukkit.permissions.PermissionDefault;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
public class BukkitPlatformPlayer extends BukkitGrimEntity implements PlatformPlayer {
private static final BukkitAudiences audiences = BukkitAudiences.create(GrimACBukkitLoaderPlugin.LOADER);
@Getter
private final Player bukkitPlayer;
@Getter
private final PlatformInventory inventory;
private final @Nullable User user;
public BukkitPlatformPlayer(Player bukkitPlayer) {
super(bukkitPlayer);
this.bukkitPlayer = bukkitPlayer;
this.inventory = new BukkitPlatformInventory(bukkitPlayer);
if (CommonGrimArguments.USE_CHAT_FAST_BYPASS.value()) {
Object channel = PacketEvents.getAPI().getProtocolManager().getChannel(bukkitPlayer.getUniqueId());
this.user = PacketEvents.getAPI().getProtocolManager().getUser(channel);
} else {
this.user = null;
}
}
@Override
@@ -66,12 +77,21 @@ public class BukkitPlatformPlayer extends BukkitGrimEntity implements PlatformPl
@Override
public void sendMessage(String message) {
bukkitPlayer.sendMessage(message);
if (CommonGrimArguments.USE_CHAT_FAST_BYPASS.value() && user != null) {
user.sendMessage(message);
} else {
bukkitPlayer.sendMessage(message);
}
}
@Override
public void sendMessage(Component message) {
audiences.player(bukkitPlayer).sendMessage(message);
if (CommonGrimArguments.USE_CHAT_FAST_BYPASS.value() && user != null) {
user.sendMessage(message);
} else {
// Paper/Purpur Player is natively an Adventure Audience, so no platform shim is needed
bukkitPlayer.sendMessage(message);
}
}
@Override
@@ -91,13 +111,12 @@ public class BukkitPlatformPlayer extends BukkitGrimEntity implements PlatformPl
@Override
public Vector3d getPosition() {
org.bukkit.Location location = this.bukkitPlayer.getLocation();
return new Vector3d(location.getX(), location.getY(), location.getZ());
}
@Override
public PlatformInventory getInventory() {
return inventory;
if (CAN_USE_DIRECT_GETTERS) {
return new Vector3d(this.bukkitPlayer.getX(), this.bukkitPlayer.getY(), this.bukkitPlayer.getZ());
} else {
org.bukkit.Location location = this.bukkitPlayer.getLocation();
return new Vector3d(location.getX(), location.getY(), location.getZ());
}
}
@Override
@@ -151,7 +170,12 @@ public class BukkitPlatformPlayer extends BukkitGrimEntity implements PlatformPl
}
@Override
@NonNull
public BlockTranslator getBlockTranslator() {
return BlockTranslator.IDENTITY;
}
@Override
@NotNull
public Player getNative() {
return this.bukkitPlayer;
}
@@ -1,41 +1,40 @@
package ac.grim.grimac.platform.bukkit.player;
import ac.grim.grimac.platform.api.player.AbstractPlatformPlayerFactory;
import ac.grim.grimac.platform.api.player.OfflinePlatformPlayer;
import ac.grim.grimac.platform.api.player.PlatformPlayer;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
import java.util.Collection;
import java.util.UUID;
public class BukkitPlatformPlayerFactory extends AbstractPlatformPlayerFactory<Player> {
@Override
protected Player getNativePlayer(@NotNull UUID uuid) {
return Bukkit.getPlayer(uuid);
}
@Override
protected Player getNativePlayer(@NotNull String name) {
return Bukkit.getPlayer(name);
}
@Override
protected PlatformPlayer createPlatformPlayer(@NotNull Player nativePlayer) {
return new BukkitPlatformPlayer(nativePlayer);
}
@Override
protected boolean isNativePlayerType(@NotNull Object playerObject) {
return playerObject instanceof Player;
}
@Override
protected UUID getPlayerUUID(@NotNull Player nativePlayer) {
return nativePlayer.getUniqueId();
}
@Override
protected Class<Player> getNativePlayerClass() {
return Player.class;
}
// The cast is safe because Bukkit.getOnlinePlayers() is guaranteed to contain Player or its subtypes,
// and we're only reading from it.
@Override
@@ -44,4 +43,26 @@ public class BukkitPlatformPlayerFactory extends AbstractPlatformPlayerFactory<P
// Cast Collection<? extends Player> to Collection<Player>
return (Collection<Player>) Bukkit.getOnlinePlayers();
}
@Override
public OfflinePlatformPlayer getOfflineFromUUID(@NotNull UUID uuid) {
OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(uuid);
return new BukkitOfflinePlatformPlayer(offlinePlayer);
}
@Override
public OfflinePlatformPlayer getOfflineFromName(@NotNull String name) {
OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(name);
return new BukkitOfflinePlatformPlayer(offlinePlayer);
}
@Override
public Collection<OfflinePlatformPlayer> getOfflinePlayers() {
OfflinePlayer[] offlinePlayers = Bukkit.getOfflinePlayers();
BukkitOfflinePlatformPlayer[] offlinePlatformPlayers = new BukkitOfflinePlatformPlayer[offlinePlayers.length];
for (int i = 0; i < offlinePlayers.length; i++) {
offlinePlatformPlayers[i] = new BukkitOfflinePlatformPlayer(offlinePlayers[i]);
}
return Arrays.asList(offlinePlatformPlayers);
}
}
@@ -22,29 +22,31 @@ public class BukkitAsyncScheduler implements AsyncScheduler {
@Override
public TaskHandle runDelayed(@NotNull GrimPlugin plugin, @NotNull Runnable task, long delay, @NotNull TimeUnit timeUnit) {
return new BukkitTaskHandle(bukkitScheduler.runTaskLaterAsynchronously(GrimACBukkitLoaderPlugin.LOADER,
return new BukkitTaskHandle(bukkitScheduler.runTaskLaterAsynchronously(
GrimACBukkitLoaderPlugin.LOADER,
task,
PlatformScheduler.convertTimeToTicks(delay, timeUnit))
);
PlatformScheduler.convertTimeToTicks(delay, timeUnit)
));
}
@Override
public TaskHandle runAtFixedRate(@NotNull GrimPlugin plugin, @NotNull Runnable task, long delay, long period, @NotNull TimeUnit timeUnit) {
return new BukkitTaskHandle(
bukkitScheduler.runTaskTimerAsynchronously(
GrimACBukkitLoaderPlugin.LOADER,
task,
PlatformScheduler.convertTimeToTicks(delay, timeUnit),
PlatformScheduler.convertTimeToTicks(period, timeUnit)
)
);
return new BukkitTaskHandle(bukkitScheduler.runTaskTimerAsynchronously(
GrimACBukkitLoaderPlugin.LOADER,
task,
PlatformScheduler.convertTimeToTicks(delay, timeUnit),
PlatformScheduler.convertTimeToTicks(period, timeUnit)
));
}
@Override
public TaskHandle runAtFixedRate(@NotNull GrimPlugin plugin, @NotNull Runnable task, long initialDelayTicks, long periodTicks) {
return new BukkitTaskHandle(
bukkitScheduler.runTaskTimerAsynchronously(GrimACBukkitLoaderPlugin.LOADER, task, initialDelayTicks, periodTicks)
);
return new BukkitTaskHandle(bukkitScheduler.runTaskTimerAsynchronously(
GrimACBukkitLoaderPlugin.LOADER,
task,
initialDelayTicks,
periodTicks
));
}
@Override
@@ -11,26 +11,25 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class BukkitEntityScheduler implements EntityScheduler {
private final BukkitScheduler bukkitScheduler = Bukkit.getScheduler();
private final BukkitScheduler scheduler = Bukkit.getScheduler();
@Override
public void execute(@NotNull GrimEntity entity, @NotNull GrimPlugin plugin, @NotNull Runnable run, @Nullable Runnable retired, long delay) {
bukkitScheduler.runTaskLater(GrimACBukkitLoaderPlugin.LOADER, run, delay);
scheduler.runTaskLater(GrimACBukkitLoaderPlugin.LOADER, run, delay);
}
@Override
public TaskHandle run(@NotNull GrimEntity entity, @NotNull GrimPlugin plugin, @NotNull Runnable task, @Nullable Runnable retired) {
return new BukkitTaskHandle(bukkitScheduler.runTask(GrimACBukkitLoaderPlugin.LOADER, task));
return new BukkitTaskHandle(scheduler.runTask(GrimACBukkitLoaderPlugin.LOADER, task));
}
@Override
public TaskHandle runDelayed(@NotNull GrimEntity entity, @NotNull GrimPlugin plugin, @NotNull Runnable task, @Nullable Runnable retired, long delayTicks) {
return new BukkitTaskHandle(bukkitScheduler.runTaskLater(GrimACBukkitLoaderPlugin.LOADER, task, delayTicks));
return new BukkitTaskHandle(scheduler.runTaskLater(GrimACBukkitLoaderPlugin.LOADER, task, delayTicks));
}
@Override
public TaskHandle runAtFixedRate(@NotNull GrimEntity entity, @NotNull GrimPlugin plugin, @NotNull Runnable task, @Nullable Runnable retired, long initialDelayTicks, long periodTicks) {
return new BukkitTaskHandle(bukkitScheduler.runTaskTimer(GrimACBukkitLoaderPlugin.LOADER, task, initialDelayTicks, periodTicks));
return new BukkitTaskHandle(scheduler.runTaskTimer(GrimACBukkitLoaderPlugin.LOADER, task, initialDelayTicks, periodTicks));
}
}
@@ -13,8 +13,8 @@ public class BukkitGlobalRegionScheduler implements GlobalRegionScheduler {
private final BukkitScheduler bukkitScheduler = Bukkit.getScheduler();
@Override
public void execute(@NotNull GrimPlugin plugin, @NotNull Runnable run) {
bukkitScheduler.runTask(GrimACBukkitLoaderPlugin.LOADER, run);
public void execute(@NotNull GrimPlugin plugin, @NotNull Runnable task) {
bukkitScheduler.runTask(GrimACBukkitLoaderPlugin.LOADER, task);
}
@Override
@@ -1,36 +1,13 @@
package ac.grim.grimac.platform.bukkit.scheduler.bukkit;
import ac.grim.grimac.platform.api.scheduler.AsyncScheduler;
import ac.grim.grimac.platform.api.scheduler.EntityScheduler;
import ac.grim.grimac.platform.api.scheduler.GlobalRegionScheduler;
import ac.grim.grimac.platform.api.scheduler.PlatformScheduler;
import ac.grim.grimac.platform.api.scheduler.RegionScheduler;
import org.checkerframework.checker.nullness.qual.NonNull;
import lombok.Getter;
import org.jetbrains.annotations.NotNull;
@Getter
public class BukkitPlatformScheduler implements PlatformScheduler {
private final BukkitAsyncScheduler bukkitAsyncScheduler = new BukkitAsyncScheduler();
private final BukkitGlobalRegionScheduler bukkitGlobalRegionScheduler = new BukkitGlobalRegionScheduler();
private final BukkitEntityScheduler bukkitEntityScheduler = new BukkitEntityScheduler();
private final BukkitRegionScheduler bukkitRegionScheduler = new BukkitRegionScheduler();
@Override
public @NonNull AsyncScheduler getAsyncScheduler() {
return bukkitAsyncScheduler;
}
@Override
public @NonNull GlobalRegionScheduler getGlobalRegionScheduler() {
return bukkitGlobalRegionScheduler;
}
@Override
public @NonNull EntityScheduler getEntityScheduler() {
return bukkitEntityScheduler;
}
@Override
public @NonNull RegionScheduler getRegionScheduler() {
return bukkitRegionScheduler;
}
private final @NotNull BukkitAsyncScheduler asyncScheduler = new BukkitAsyncScheduler();
private final @NotNull BukkitGlobalRegionScheduler globalRegionScheduler = new BukkitGlobalRegionScheduler();
private final @NotNull BukkitEntityScheduler entityScheduler = new BukkitEntityScheduler();
private final @NotNull BukkitRegionScheduler regionScheduler = new BukkitRegionScheduler();
}
@@ -15,13 +15,13 @@ public class BukkitRegionScheduler implements RegionScheduler {
private final BukkitScheduler bukkitScheduler = Bukkit.getScheduler();
@Override
public void execute(@NotNull GrimPlugin plugin, @NotNull PlatformWorld world, int chunkX, int chunkZ, @NotNull Runnable run) {
bukkitScheduler.runTask(GrimACBukkitLoaderPlugin.LOADER, run);
public void execute(@NotNull GrimPlugin plugin, @NotNull PlatformWorld world, int chunkX, int chunkZ, @NotNull Runnable task) {
bukkitScheduler.runTask(GrimACBukkitLoaderPlugin.LOADER, task);
}
@Override
public void execute(@NotNull GrimPlugin plugin, @NotNull Location location, @NotNull Runnable run) {
bukkitScheduler.runTask(GrimACBukkitLoaderPlugin.LOADER, run);
public void execute(@NotNull GrimPlugin plugin, @NotNull Location location, @NotNull Runnable task) {
bukkitScheduler.runTask(GrimACBukkitLoaderPlugin.LOADER, task);
}
@Override
@@ -2,27 +2,32 @@ package ac.grim.grimac.platform.bukkit.scheduler.bukkit;
import ac.grim.grimac.platform.api.scheduler.TaskHandle;
import org.bukkit.scheduler.BukkitTask;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import java.util.Objects;
public class BukkitTaskHandle implements TaskHandle {
private final BukkitTask bukkitTask;
private final @NotNull BukkitTask task;
public BukkitTaskHandle(BukkitTask bukkitTask) {
this.bukkitTask = bukkitTask;
@Contract(pure = true)
public BukkitTaskHandle(@NotNull BukkitTask task) {
this.task = Objects.requireNonNull(task);
}
@Override
public boolean isSync() {
return bukkitTask.isSync();
return task.isSync();
}
@Override
public boolean isCancelled() {
return bukkitTask.isCancelled();
return task.isCancelled();
}
@Override
public void cancel() {
bukkitTask.cancel();
task.cancel();
}
}
@@ -11,53 +11,47 @@ import java.util.concurrent.TimeUnit;
public class FoliaAsyncScheduler implements AsyncScheduler {
private final io.papermc.paper.threadedregions.scheduler.AsyncScheduler asyncScheduler = Bukkit.getAsyncScheduler();
private final io.papermc.paper.threadedregions.scheduler.AsyncScheduler scheduler = Bukkit.getAsyncScheduler();
@Override
public TaskHandle runNow(@NotNull GrimPlugin plugin, @NotNull Runnable task) {
return new FoliaTaskHandle(asyncScheduler.runNow(GrimACBukkitLoaderPlugin.LOADER, (ignored) -> task.run()));
return new FoliaTaskHandle(scheduler.runNow(GrimACBukkitLoaderPlugin.LOADER, ignored -> task.run()));
}
@Override
public TaskHandle runDelayed(@NotNull GrimPlugin plugin, @NotNull Runnable task, long delay, @NotNull TimeUnit timeUnit) {
return new FoliaTaskHandle(
asyncScheduler.runDelayed(
GrimACBukkitLoaderPlugin.LOADER,
(ignored) -> task.run(),
delay,
timeUnit
)
);
return new FoliaTaskHandle(scheduler.runDelayed(
GrimACBukkitLoaderPlugin.LOADER,
ignored -> task.run(),
delay,
timeUnit
));
}
@Override
public TaskHandle runAtFixedRate(@NotNull GrimPlugin plugin, @NotNull Runnable task, long delay, long period, @NotNull TimeUnit timeUnit) {
return new FoliaTaskHandle(
asyncScheduler.runAtFixedRate(
GrimACBukkitLoaderPlugin.LOADER,
(ignored) -> task.run(),
delay,
period,
timeUnit
)
);
return new FoliaTaskHandle(scheduler.runAtFixedRate(
GrimACBukkitLoaderPlugin.LOADER,
ignored -> task.run(),
delay,
period,
timeUnit
));
}
@Override
public TaskHandle runAtFixedRate(@NotNull GrimPlugin plugin, @NotNull Runnable task, long initialDelayTicks, long periodTicks) {
return new FoliaTaskHandle(
asyncScheduler.runAtFixedRate(
GrimACBukkitLoaderPlugin.LOADER,
(ignored) -> task.run(),
initialDelayTicks * 50,
periodTicks * 50,
TimeUnit.MILLISECONDS
)
);
return new FoliaTaskHandle(scheduler.runAtFixedRate(
GrimACBukkitLoaderPlugin.LOADER,
ignored -> task.run(),
initialDelayTicks * 50,
periodTicks * 50,
TimeUnit.MILLISECONDS
));
}
@Override
public void cancel(@NotNull GrimPlugin plugin) {
asyncScheduler.cancelTasks(GrimACBukkitLoaderPlugin.LOADER);
scheduler.cancelTasks(GrimACBukkitLoaderPlugin.LOADER);
}
}
@@ -6,30 +6,50 @@ import ac.grim.grimac.platform.api.scheduler.EntityScheduler;
import ac.grim.grimac.platform.api.scheduler.TaskHandle;
import ac.grim.grimac.platform.bukkit.GrimACBukkitLoaderPlugin;
import ac.grim.grimac.platform.bukkit.entity.BukkitGrimEntity;
import io.papermc.paper.threadedregions.scheduler.ScheduledTask;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class FoliaEntityScheduler implements EntityScheduler {
@Override
public void execute(@NotNull GrimEntity entity, @NotNull GrimPlugin plugin, @NotNull Runnable run, @Nullable Runnable retired, long delay) {
((BukkitGrimEntity) entity).getBukkitEntity().getScheduler().execute(GrimACBukkitLoaderPlugin.LOADER, run, retired, delay);
public void execute(@NotNull GrimEntity entity, @NotNull GrimPlugin plugin, @NotNull Runnable task, @Nullable Runnable retired, long delay) {
((BukkitGrimEntity) entity).getBukkitEntity().getScheduler().execute(GrimACBukkitLoaderPlugin.LOADER, task, retired, delay);
}
@Override
public TaskHandle run(@NotNull GrimEntity entity, @NotNull GrimPlugin plugin, @NotNull Runnable task, @Nullable Runnable retired) {
return new FoliaTaskHandle(((BukkitGrimEntity) entity).getBukkitEntity().getScheduler().run(GrimACBukkitLoaderPlugin.LOADER, (ignored) -> task.run(), retired));
ScheduledTask scheduled = ((BukkitGrimEntity) entity).getBukkitEntity().getScheduler().run(
GrimACBukkitLoaderPlugin.LOADER,
ignored -> task.run(),
retired
);
return scheduled == null ? null : new FoliaTaskHandle(scheduled);
}
@Override
public TaskHandle runDelayed(@NotNull GrimEntity entity, @NotNull GrimPlugin plugin, @NotNull Runnable task, @Nullable Runnable retired, long delayTicks) {
return new FoliaTaskHandle(
((BukkitGrimEntity) entity).getBukkitEntity().getScheduler().runDelayed(GrimACBukkitLoaderPlugin.LOADER, (ignored) -> task.run(), retired, delayTicks)
ScheduledTask scheduled = ((BukkitGrimEntity) entity).getBukkitEntity().getScheduler().runDelayed(
GrimACBukkitLoaderPlugin.LOADER,
ignored -> task.run(),
retired,
delayTicks
);
return scheduled == null ? null : new FoliaTaskHandle(scheduled);
}
@Override
public TaskHandle runAtFixedRate(@NotNull GrimEntity entity, @NotNull GrimPlugin plugin, @NotNull Runnable task, @Nullable Runnable retired, long initialDelayTicks, long periodTicks) {
return new FoliaTaskHandle(((BukkitGrimEntity) entity).getBukkitEntity().getScheduler().runAtFixedRate(GrimACBukkitLoaderPlugin.LOADER, (ignored) -> task.run(), retired, initialDelayTicks, periodTicks));
ScheduledTask scheduled = ((BukkitGrimEntity) entity).getBukkitEntity().getScheduler().runAtFixedRate(
GrimACBukkitLoaderPlugin.LOADER,
ignored -> task.run(),
retired,
initialDelayTicks,
periodTicks
);
return scheduled == null ? null : new FoliaTaskHandle(scheduled);
}
}
@@ -12,23 +12,23 @@ public class FoliaGlobalRegionScheduler implements GlobalRegionScheduler {
private final io.papermc.paper.threadedregions.scheduler.GlobalRegionScheduler globalRegionScheduler = Bukkit.getGlobalRegionScheduler();
@Override
public void execute(@NotNull GrimPlugin plugin, @NotNull Runnable run) {
globalRegionScheduler.execute(GrimACBukkitLoaderPlugin.LOADER, run);
public void execute(@NotNull GrimPlugin plugin, @NotNull Runnable task) {
globalRegionScheduler.execute(GrimACBukkitLoaderPlugin.LOADER, task);
}
@Override
public TaskHandle run(@NotNull GrimPlugin plugin, @NotNull Runnable task) {
return new FoliaTaskHandle(globalRegionScheduler.run(GrimACBukkitLoaderPlugin.LOADER, (ignored) -> task.run()));
return new FoliaTaskHandle(globalRegionScheduler.run(GrimACBukkitLoaderPlugin.LOADER, ignored -> task.run()));
}
@Override
public TaskHandle runDelayed(@NotNull GrimPlugin plugin, @NotNull Runnable task, long delay) {
return new FoliaTaskHandle(globalRegionScheduler.runDelayed(GrimACBukkitLoaderPlugin.LOADER, (ignored) -> task.run(), delay));
return new FoliaTaskHandle(globalRegionScheduler.runDelayed(GrimACBukkitLoaderPlugin.LOADER, ignored -> task.run(), delay));
}
@Override
public TaskHandle runAtFixedRate(@NotNull GrimPlugin plugin, @NotNull Runnable task, long initialDelayTicks, long periodTicks) {
return new FoliaTaskHandle(globalRegionScheduler.runAtFixedRate(GrimACBukkitLoaderPlugin.LOADER, (ignored) -> task.run(), initialDelayTicks, periodTicks));
return new FoliaTaskHandle(globalRegionScheduler.runAtFixedRate(GrimACBukkitLoaderPlugin.LOADER, ignored -> task.run(), initialDelayTicks, periodTicks));
}
@Override
@@ -1,36 +1,13 @@
package ac.grim.grimac.platform.bukkit.scheduler.folia;
import ac.grim.grimac.platform.api.scheduler.AsyncScheduler;
import ac.grim.grimac.platform.api.scheduler.EntityScheduler;
import ac.grim.grimac.platform.api.scheduler.GlobalRegionScheduler;
import ac.grim.grimac.platform.api.scheduler.PlatformScheduler;
import ac.grim.grimac.platform.api.scheduler.RegionScheduler;
import org.checkerframework.checker.nullness.qual.NonNull;
import lombok.Getter;
import org.jetbrains.annotations.NotNull;
@Getter
public class FoliaPlatformScheduler implements PlatformScheduler {
private final FoliaAsyncScheduler foliaAsyncScheduler = new FoliaAsyncScheduler();
private final FoliaGlobalRegionScheduler foliaGlobalRegionScheduler = new FoliaGlobalRegionScheduler();
private final FoliaEntityScheduler foliaEntityScheduler = new FoliaEntityScheduler();
private final FoliaRegionScheduler foliaRegionScheduler = new FoliaRegionScheduler();
@Override
public @NonNull AsyncScheduler getAsyncScheduler() {
return foliaAsyncScheduler;
}
@Override
public @NonNull GlobalRegionScheduler getGlobalRegionScheduler() {
return foliaGlobalRegionScheduler;
}
@Override
public @NonNull EntityScheduler getEntityScheduler() {
return foliaEntityScheduler;
}
@Override
public @NonNull RegionScheduler getRegionScheduler() {
return foliaRegionScheduler;
}
private final @NotNull FoliaAsyncScheduler asyncScheduler = new FoliaAsyncScheduler();
private final @NotNull FoliaGlobalRegionScheduler globalRegionScheduler = new FoliaGlobalRegionScheduler();
private final @NotNull FoliaEntityScheduler entityScheduler = new FoliaEntityScheduler();
private final @NotNull FoliaRegionScheduler regionScheduler = new FoliaRegionScheduler();
}
@@ -15,19 +15,24 @@ public class FoliaRegionScheduler implements RegionScheduler {
private final io.papermc.paper.threadedregions.scheduler.RegionScheduler regionScheduler = Bukkit.getRegionScheduler();
@Override
public void execute(@NotNull GrimPlugin plugin, @NotNull PlatformWorld world, int chunkX, int chunkZ, @NotNull Runnable run) {
regionScheduler.execute(GrimACBukkitLoaderPlugin.LOADER, ((BukkitPlatformWorld) world).getBukkitWorld(), chunkX, chunkZ, run);
public void execute(@NotNull GrimPlugin plugin, @NotNull PlatformWorld world, int chunkX, int chunkZ, @NotNull Runnable task) {
regionScheduler.execute(GrimACBukkitLoaderPlugin.LOADER, ((BukkitPlatformWorld) world).getBukkitWorld(), chunkX, chunkZ, task);
}
@Override
public void execute(@NotNull GrimPlugin plugin, @NotNull Location location, @NotNull Runnable run) {
execute(plugin, location.getWorld(), location.getBlockX() >> 4, location.getBlockZ() >> 4, run);
public void execute(@NotNull GrimPlugin plugin, @NotNull Location location, @NotNull Runnable task) {
execute(plugin, location.getWorld(), location.getBlockX() >> 4, location.getBlockZ() >> 4, task);
}
@Override
public TaskHandle run(@NotNull GrimPlugin plugin, @NotNull PlatformWorld world, int chunkX, int chunkZ, @NotNull Runnable task) {
return new FoliaTaskHandle(regionScheduler.run(GrimACBukkitLoaderPlugin.LOADER, ((BukkitPlatformWorld) world).getBukkitWorld(), chunkX, chunkZ, (ignored) -> task.run()));
return new FoliaTaskHandle(regionScheduler.run(
GrimACBukkitLoaderPlugin.LOADER,
((BukkitPlatformWorld) world).getBukkitWorld(),
chunkX,
chunkZ,
ignored -> task.run()
));
}
@Override
@@ -37,8 +42,14 @@ public class FoliaRegionScheduler implements RegionScheduler {
@Override
public TaskHandle runDelayed(@NotNull GrimPlugin plugin, @NotNull PlatformWorld world, int chunkX, int chunkZ, @NotNull Runnable task, long delayTicks) {
return new FoliaTaskHandle(regionScheduler.runDelayed(GrimACBukkitLoaderPlugin.LOADER,
((BukkitPlatformWorld) world).getBukkitWorld(), chunkX, chunkZ, (ignored) -> task.run(), delayTicks));
return new FoliaTaskHandle(regionScheduler.runDelayed(
GrimACBukkitLoaderPlugin.LOADER,
((BukkitPlatformWorld) world).getBukkitWorld(),
chunkX,
chunkZ,
ignored -> task.run(),
delayTicks
));
}
@Override
@@ -48,12 +59,27 @@ public class FoliaRegionScheduler implements RegionScheduler {
@Override
public TaskHandle runAtFixedRate(@NotNull GrimPlugin plugin, @NotNull PlatformWorld world, int chunkX, int chunkZ, @NotNull Runnable task, long initialDelayTicks, long periodTicks) {
return new FoliaTaskHandle(regionScheduler.runAtFixedRate(GrimACBukkitLoaderPlugin.LOADER,
((BukkitPlatformWorld) world).getBukkitWorld(), chunkX, chunkZ, (ignored) -> task.run(), initialDelayTicks, periodTicks));
return new FoliaTaskHandle(regionScheduler.runAtFixedRate(
GrimACBukkitLoaderPlugin.LOADER,
((BukkitPlatformWorld) world).getBukkitWorld(),
chunkX,
chunkZ,
ignored -> task.run(),
initialDelayTicks,
periodTicks
));
}
@Override
public TaskHandle runAtFixedRate(@NotNull GrimPlugin plugin, @NotNull Location location, @NotNull Runnable task, long initialDelayTicks, long periodTicks) {
return runAtFixedRate(plugin, location.getWorld(), location.getBlockX() >> 4, location.getBlockZ() >> 4, task, initialDelayTicks, periodTicks);
return runAtFixedRate(
plugin,
location.getWorld(),
location.getBlockX() >> 4,
location.getBlockZ() >> 4,
task,
initialDelayTicks,
periodTicks
);
}
}
@@ -2,13 +2,18 @@ package ac.grim.grimac.platform.bukkit.scheduler.folia;
import ac.grim.grimac.platform.api.scheduler.TaskHandle;
import io.papermc.paper.threadedregions.scheduler.ScheduledTask;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import java.util.Objects;
public class FoliaTaskHandle implements TaskHandle {
private final ScheduledTask task;
private final @NotNull ScheduledTask task;
public FoliaTaskHandle(ScheduledTask task) {
this.task = task;
@Contract(pure = true)
public FoliaTaskHandle(@NotNull ScheduledTask task) {
this.task = Objects.requireNonNull(task);
}
@Override
@@ -3,8 +3,6 @@ package ac.grim.grimac.platform.bukkit.sender;
import ac.grim.grimac.GrimAPI;
import ac.grim.grimac.platform.api.sender.Sender;
import ac.grim.grimac.platform.api.sender.SenderFactory;
import ac.grim.grimac.platform.bukkit.GrimACBukkitLoaderPlugin;
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
import net.kyori.adventure.text.Component;
import org.bukkit.command.CommandSender;
import org.bukkit.command.ConsoleCommandSender;
@@ -12,17 +10,12 @@ import org.bukkit.command.RemoteConsoleCommandSender;
import org.bukkit.entity.Player;
import org.bukkit.permissions.Permission;
import org.bukkit.permissions.PermissionDefault;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.incendo.cloud.SenderMapper;
import org.jetbrains.annotations.NotNull;
import java.util.UUID;
public class BukkitSenderFactory extends SenderFactory<CommandSender> implements SenderMapper<CommandSender, Sender> {
private final BukkitAudiences audiences;
public BukkitSenderFactory() {
this.audiences = BukkitAudiences.create(GrimACBukkitLoaderPlugin.LOADER);
}
@Override
protected String getName(CommandSender sender) {
@@ -31,7 +24,7 @@ public class BukkitSenderFactory extends SenderFactory<CommandSender> implements
@Override
protected UUID getUniqueId(CommandSender sender) {
return sender instanceof Player ? ((Player) sender).getUniqueId() : Sender.CONSOLE_UUID;
return sender instanceof Player player ? player.getUniqueId() : Sender.CONSOLE_UUID;
}
@Override
@@ -42,12 +35,13 @@ public class BukkitSenderFactory extends SenderFactory<CommandSender> implements
@Override
protected void sendMessage(CommandSender sender, Component message) {
// we can safely send async for players and the console - otherwise, send it sync
// Paper/Purpur CommandSender is natively an Adventure Audience, so no platform shim is needed
if (sender instanceof Player || sender instanceof ConsoleCommandSender || sender instanceof RemoteConsoleCommandSender) {
this.audiences.sender(sender).sendMessage(message);
sender.sendMessage(message);
} else {
GrimAPI.INSTANCE.getScheduler().getGlobalRegionScheduler().run(
GrimAPI.INSTANCE.getGrimPlugin(),
() -> this.audiences.sender(sender).sendMessage(message)
() -> sender.sendMessage(message)
);
}
}
@@ -78,12 +72,12 @@ public class BukkitSenderFactory extends SenderFactory<CommandSender> implements
}
@Override
public @NonNull Sender map(@NonNull CommandSender base) {
public @NotNull Sender map(@NotNull CommandSender base) {
return this.wrap(base);
}
@Override
public @NonNull CommandSender reverse(@NonNull Sender mapped) {
public @NotNull CommandSender reverse(@NotNull Sender mapped) {
return this.unwrap(mapped);
}
}
@@ -1,5 +1,7 @@
package ac.grim.grimac.platform.bukkit.utils.anticheat;
import ac.grim.grimac.utils.anticheat.LogUtil;
import ac.grim.grimac.utils.reflection.ReflectionUtils;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import org.bukkit.entity.Player;
@@ -8,27 +10,17 @@ import java.lang.reflect.Method;
public class MultiLibUtil {
public final static Method externalPlayerMethod = getMethod(Player.class, "isExternalPlayer");
public final static Method externalPlayerMethod = ReflectionUtils.getMethod(Player.class, "isExternalPlayer");
private static final boolean IS_PRE_1_18 = PacketEvents.getAPI().getServerManager().getVersion().isOlderThan(ServerVersion.V_1_18);
public static Method getMethod(Class<?> clazz, String methodName) {
try {
return clazz.getMethod(methodName);
} catch (NoSuchMethodException e) {
return null;
}
}
// TODO: cache external players for better performance, but this only matters for people using multi-lib
public static boolean isExternalPlayer(Player player) {
if (externalPlayerMethod == null || IS_PRE_1_18) return false;
try {
return (boolean) externalPlayerMethod.invoke(player);
} catch (Exception e) {
e.printStackTrace();
LogUtil.error("Failed to invoke external player method", e);
return false;
}
}
}
@@ -4,9 +4,14 @@ import ac.grim.grimac.platform.api.permissions.PermissionDefaultValue;
import ac.grim.grimac.platform.bukkit.world.BukkitPlatformWorld;
import ac.grim.grimac.utils.math.Location;
import com.github.retrooper.packetevents.protocol.world.BlockFace;
import org.bukkit.permissions.PermissionDefault;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.Nullable;
public class BukkitConversionUtils {
@Contract("null -> null; !null -> new")
public static org.bukkit.Location toBukkitLocation(Location location) {
if (location == null) return null;
return new org.bukkit.Location(((BukkitPlatformWorld) location.getWorld()).getBukkitWorld(), location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch());
}
@@ -15,20 +20,17 @@ public class BukkitConversionUtils {
*
* @return The corresponding Bukkit PermissionDefault.
*/
public static org.bukkit.permissions.PermissionDefault toBukkitPermissionDefault(PermissionDefaultValue permissionDefaultValue) {
@Contract(value = "null -> null; !null -> !null", pure = true)
public static @Nullable PermissionDefault toBukkitPermissionDefault(@Nullable PermissionDefaultValue permissionDefaultValue) {
if (permissionDefaultValue == null) return null;
return switch (permissionDefaultValue) {
case TRUE -> org.bukkit.permissions.PermissionDefault.TRUE;
case FALSE -> org.bukkit.permissions.PermissionDefault.FALSE;
case OP -> org.bukkit.permissions.PermissionDefault.OP;
case NOT_OP -> org.bukkit.permissions.PermissionDefault.NOT_OP;
case TRUE -> PermissionDefault.TRUE;
case FALSE -> PermissionDefault.FALSE;
case OP -> PermissionDefault.OP;
case NOT_OP -> PermissionDefault.NOT_OP;
};
}
/**
* For use with bukkit events only
* Grim is not meant to be restrained by bukkit!
*/
@Deprecated
public static BlockFace fromBukkitFace(org.bukkit.block.BlockFace face) {
return switch (face) {
case NORTH -> BlockFace.NORTH;
@@ -12,41 +12,29 @@ import org.bukkit.event.Listener;
import java.util.concurrent.CompletableFuture;
public class PaperUtils {
public static final boolean PAPER;
public static final boolean TICK_END_EVENT;
public static final boolean ASYNC_TELEPORT;
static {
PAPER = ReflectionUtils.hasClass("com.destroystokyo.paper.PaperConfig") || ReflectionUtils.hasClass("io.papermc.paper.configuration.Configuration");
TICK_END_EVENT = ReflectionUtils.hasClass("com.destroystokyo.paper.event.server.ServerTickEndEvent");
ASYNC_TELEPORT = ReflectionUtils.hasMethod(Entity.class, "teleportAsync", org.bukkit.Location.class);
}
public static final boolean PAPER = ReflectionUtils.hasClass("com.destroystokyo.paper.PaperConfig")
|| ReflectionUtils.hasClass("io.papermc.paper.configuration.Configuration");
public static CompletableFuture<Boolean> teleportAsync(final Entity entity, final Location location) {
if (PAPER) {
return entity.teleportAsync(location);
} else {
return CompletableFuture.completedFuture(entity.teleport(location));
}
return PAPER ? entity.teleportAsync(location) : CompletableFuture.completedFuture(entity.teleport(location));
}
@SuppressWarnings("unchecked")
public static boolean registerTickEndEvent(Listener listener, Runnable runnable) {
if (TICK_END_EVENT) {
try {
Class<?> clazz = ReflectionUtils.getClass("com.destroystokyo.paper.event.server.ServerTickEndEvent");
if (clazz == null) return false;
GrimACBukkitLoaderPlugin.LOADER.getServer().getPluginManager().registerEvent((Class<? extends Event>) clazz,
listener,
EventPriority.NORMAL,
(l, event) -> runnable.run(), GrimACBukkitLoaderPlugin.LOADER);
return true;
} catch (Exception e) {
LogUtil.exception("Failed to register tick end event", e);
}
try {
Class<?> clazz = ReflectionUtils.getClass("com.destroystokyo.paper.event.server.ServerTickEndEvent");
if (clazz == null) return false;
GrimACBukkitLoaderPlugin.LOADER.getServer().getPluginManager().registerEvent(
(Class<? extends Event>) clazz,
listener,
EventPriority.NORMAL,
(l, event) -> runnable.run(),
GrimACBukkitLoaderPlugin.LOADER
);
return true;
} catch (Exception e) {
LogUtil.error("Failed to register tick end event", e);
}
return false;
}
}
@@ -1,24 +1,26 @@
package ac.grim.grimac.platform.bukkit.world;
import ac.grim.grimac.GrimAPI;
import ac.grim.grimac.platform.api.Platform;
import ac.grim.grimac.platform.api.world.PlatformChunk;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState;
import lombok.RequiredArgsConstructor;
import org.bukkit.Chunk;
import org.bukkit.block.Block;
import org.bukkit.block.data.BlockData;
import org.jetbrains.annotations.NotNull;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@RequiredArgsConstructor
public class BukkitPlatformChunk implements PlatformChunk {
private static final HashMap<BlockData, Integer> blockDataToId = new HashMap<>();
private static final Map<BlockData, Integer> blockDataToId = GrimAPI.INSTANCE.getPlatform() == Platform.FOLIA ? new ConcurrentHashMap<>() : new HashMap<>();
private static final boolean isFlat = PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_13);
private final Chunk chunk;
public BukkitPlatformChunk(@NotNull Chunk chunkAt) {
this.chunk = chunkAt;
}
private final @NotNull Chunk chunk;
@Override
public int getBlockID(int x, int y, int z) {
@@ -26,6 +28,11 @@ public class BukkitPlatformChunk implements PlatformChunk {
return isFlat // Cache blockDataToID because Strings are expensive
? blockDataToId.computeIfAbsent(block.getBlockData(), data -> WrappedBlockState.getByString(PacketEvents.getAPI().getServerManager().getVersion().toClientVersion(), data.getAsString(false)).getGlobalId())
: (block.getType().getId() << 4) | block.getData();
: getLegacyBlockID(block);
}
@SuppressWarnings({ "deprecation", "UnstableApiUsage" })
private static int getLegacyBlockID(@NotNull Block block) {
return (block.getType().getId() << 4) | block.getData();
}
}
@@ -2,24 +2,26 @@ package ac.grim.grimac.platform.bukkit.world;
import ac.grim.grimac.platform.api.world.PlatformChunk;
import ac.grim.grimac.platform.api.world.PlatformWorld;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState;
import io.github.retrooper.packetevents.util.SpigotConversionUtil;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.UUID;
@Getter
@RequiredArgsConstructor
public class BukkitPlatformWorld implements PlatformWorld {
private final World bukkitWorld;
public BukkitPlatformWorld(@NotNull World world) {
this.bukkitWorld = world;
}
private static final boolean LEGACY_SERVER_VERSION = PacketEvents.getAPI().getServerManager().getVersion().isOlderThanOrEquals(ServerVersion.V_1_12_2);
private final @NotNull World bukkitWorld;
@Override
public boolean isChunkLoaded(int chunkX, int chunkZ) {
@@ -28,7 +30,14 @@ public class BukkitPlatformWorld implements PlatformWorld {
@Override
public WrappedBlockState getBlockAt(int x, int y, int z) {
return SpigotConversionUtil.fromBukkitBlockData(bukkitWorld.getBlockAt(x, y, z).getBlockData());
if (LEGACY_SERVER_VERSION) {
Block block = bukkitWorld.getBlockAt(x, y, z);
@SuppressWarnings({"deprecation", "UnstableApiUsage"})
int blockId = (block.getType().getId() << 4) | block.getData();
return WrappedBlockState.getByGlobalId(blockId);
} else {
return SpigotConversionUtil.fromBukkitBlockData(bukkitWorld.getBlockAt(x, y, z).getBlockData());
}
}
@Override
+47
View File
@@ -0,0 +1,47 @@
# cliff.toml - DEV BUILDS - Now with Author Info
[changelog]
body = """
## Changes in this Build (since previous push)
{%- for group, commits in commits | group_by(attribute="group") %}
### {{ group | striptags | upper_first }}
{%- for commit in commits %}
- {{ commit.message | upper_first }} - *by {{ commit.author.name }}* {% if commit.breaking %}**[BREAKING]**{% endif %} \
{%- if commit.remote and commit.remote.url -%}
([`{{ commit.id | truncate(length=7, end="") }}`]({{ commit.remote.url }}/commit/{{- commit.id -}}))
{%- else -%}
(`{{ commit.id | truncate(length=7, end="") }}`)
{%- endif -%}
{% endfor %}
{% endfor %}
"""
trim = true
[git]
# Your previous settings here are perfect, no changes needed
filter_unconventional = false
skip_unconventional = false
remote = "https://github.com/GrimAnticheat/Grim"
commit_parsers = [
{ message = "^feat", group = "🚀 Features" },
{ message = "^fix", group = "🐛 Fixes" },
{ message = "^perf", group = "⚡️ Performance" },
{ message = "^refactor", group = "♻️ Refactoring" },
{ message = "^docs", group = "📖 Documentation" },
{ message = "^test", group = "✅ Tests" },
{ message = "^chore", group = "🤖 Maintenance & CI" },
{ body = ".*BREAKING CHANGE.*", group = "🚨 Breaking Changes" },
{ message = ".*", group = "Miscellaneous" } # CHANGED THIS LINE
]
commit_preprocessors = [
{ pattern = 'src/main/java/ac/grim/grimac/checks/.*', group = "✔️ Checks" },
{ pattern = 'src/main/java/ac/grim/grimac/api/.*', group = "🔌 API" },
{ pattern = 'src/main/java/ac/grim/grimac/platform/.*', group = "🏗️ Platform" },
{ pattern = 'src/main/java/ac/grim/grimac/engine/.*', group = "⚙️ Core Engine" },
{ pattern = 'src/main/java/ac/grim/grimac/config/.*', group = "⚙️ Configuration" },
{ pattern = 'pom.xml', group = "🤖 Maintenance & CI" },
{ pattern = '.github/.*', group = "🤖 Maintenance & CI" },
{ pattern = 'build.gradle.kts', group = "🤖 Maintenance & CI" },
{ pattern = 'src/.*', group = "⚙️ Core Engine" },
]
sort_commits = "oldest"
+55 -24
View File
@@ -1,33 +1,52 @@
import versioning.BuildConfig
plugins {
eclipse
`maven-publish`
grim.`base-conventions`
}
repositories {
if (BuildConfig.mavenLocalOverride) {
mavenLocal()
// We still call mavenLocal() conditionally at the top for non-exclusive deps (general fallback)
if (BuildConfig.mavenLocalOverride) mavenLocal()
// Grim API
exclusive("https://repo.grim.ac/snapshots") {
includeGroup("ac.grim.grimac")
}
maven {
name = "papermc"
url = uri("https://repo.papermc.io/repository/maven-public/")
// PacketEvents
exclusive("https://repo.codemc.io/repository/maven-releases/", { mavenContent { releasesOnly() } }) {
includeGroup("com.github.retrooper")
}
maven("https://hub.spigotmc.org/nexus/content/repositories/snapshots/") // Spigot
maven("https://repo.grim.ac/snapshots") { // Grim API
content {
includeGroup("ac.grim.grimac")
includeGroup("com.github.retrooper")
}
// ViaVersion
exclusive("https://repo.viaversion.com", { mavenContent { releasesOnly() } }) {
includeGroup("com.viaversion")
}
maven("https://repo.viaversion.com") // ViaVersion
maven("https://nexus.scarsz.me/content/repositories/releases") // Configuralize
maven("https://repo.opencollab.dev/maven-snapshots/") // Floodgate
maven("https://repo.opencollab.dev/maven-releases/") // Cumulus (for Floodgate)
maven("https://s01.oss.sonatype.org/content/repositories/snapshots/")
mavenCentral() // FastUtil
// Configuralize
exclusive("https://nexus.scarsz.me/content/repositories/releases", { mavenContent { releasesOnly() } }) {
includeGroup("github.scarsz")
}
// Cumulus
exclusive("https://repo.opencollab.dev/maven-releases/", { mavenContent { releasesOnly() } }) {
includeGroup("org.geysermc.api")
}
// Floodgate
exclusive("https://repo.opencollab.dev/maven-snapshots/", { mavenContent { snapshotsOnly() } }) {
includeGroup("org.geysermc.floodgate")
includeGroup("org.geysermc.cumulus")
includeModule("org.geysermc", "common")
includeModule("org.geysermc", "geyser-parent")
}
mavenCentral()
}
dependencies {
if (BuildConfig.shadePE) {
api(libs.packetevents.api)
@@ -35,20 +54,32 @@ dependencies {
compileOnly(libs.packetevents.api)
}
api(libs.cloud.core)
api("org.incendo:cloud-processors-requirements:1.0.0-rc.1")
api("github.scarsz:configuralize:1.4.1:slim") {
api(libs.cloud.processors.requirements)
api(libs.configuralize) {
artifact {
classifier = "slim"
}
exclude(group = "org.yaml", module = "snakeyaml")
}
// Bump snakeyaml (transitive dep of configuralize) 1.29 -> 2.2 for geyser-fabric
api("org.yaml:snakeyaml:2.2")
// Bump snakeyaml (transitive dep of configuralize) 1.29 -> 2.2+ for geyser-fabric
api(libs.snakeyaml)
api(libs.fastutil)
api(libs.adventure.text.minimessage)
// Provided by the server at runtime; bundling it would shadow the platform's own Adventure
compileOnly(libs.adventure.text.minimessage)
api(libs.jetbrains.annotations)
api(libs.hikaricp)
api(libs.postgresql) // JDBC driver for /grim history postgresql backend
api("ac.grim.grimac:GrimAPI:1.1.0.0")
api(libs.grim.api)
api(libs.grim.internal)
compileOnly(libs.grim.internal.shims)
compileOnly(libs.geyser.base.api) {
isTransitive = false // messes with guava otherwise
}
compileOnly(libs.floodgate.api)
compileOnly(libs.via.version.api)
compileOnly(libs.viaversion)
compileOnly(libs.netty)
}
@@ -1,8 +1,9 @@
package ac.grim.grimac;
import ac.grim.grimac.api.event.EventBus;
import ac.grim.grimac.api.event.OptimizedEventBus;
import ac.grim.grimac.api.plugin.GrimPlugin;
import ac.grim.grimac.internal.plugin.resolver.GrimExtensionManager;
import ac.grim.grimac.internal.event.OptimizedEventBus;
import ac.grim.grimac.manager.AlertManagerImpl;
import ac.grim.grimac.manager.DiscordManager;
import ac.grim.grimac.manager.InitManager;
@@ -10,28 +11,24 @@ import ac.grim.grimac.manager.SpectateManager;
import ac.grim.grimac.manager.TickManager;
import ac.grim.grimac.manager.config.BaseConfigManager;
import ac.grim.grimac.manager.init.Initable;
import ac.grim.grimac.manager.violationdatabase.ViolationDatabaseManager;
import ac.grim.grimac.platform.api.Platform;
import ac.grim.grimac.platform.api.PlatformLoader;
import ac.grim.grimac.platform.api.PlatformServer;
import ac.grim.grimac.platform.api.command.CommandService;
import ac.grim.grimac.platform.api.manager.ItemResetHandler;
import ac.grim.grimac.platform.api.manager.MessagePlaceHolderManager;
import ac.grim.grimac.platform.api.manager.ParserDescriptorFactory;
import ac.grim.grimac.platform.api.manager.PermissionRegistrationManager;
import ac.grim.grimac.platform.api.manager.PlatformPluginManager;
import ac.grim.grimac.platform.api.player.PlatformPlayerFactory;
import ac.grim.grimac.platform.api.scheduler.PlatformScheduler;
import ac.grim.grimac.platform.api.sender.Sender;
import ac.grim.grimac.platform.api.sender.SenderFactory;
import ac.grim.grimac.utils.anticheat.PlayerDataManager;
import ac.grim.grimac.utils.common.arguments.CommonGrimArguments;
import ac.grim.grimac.utils.reflection.ReflectionUtils;
import lombok.Getter;
import org.incendo.cloud.CommandManager;
import org.jetbrains.annotations.NotNull;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@Getter
public final class GrimAPI {
public static final GrimAPI INSTANCE = new GrimAPI();
@@ -44,8 +41,10 @@ public final class GrimAPI {
private final DiscordManager discordManager;
private final PlayerDataManager playerDataManager;
private final TickManager tickManager;
private final GrimExtensionManager extensionManager;
private final EventBus eventBus;
private final GrimExternalAPI externalAPI;
private ViolationDatabaseManager violationDatabaseManager;
private PlatformLoader loader;
@Getter
private InitManager initManager;
@@ -58,27 +57,25 @@ public final class GrimAPI {
this.discordManager = new DiscordManager();
this.playerDataManager = new PlayerDataManager();
this.tickManager = new TickManager();
this.eventBus = new OptimizedEventBus();
this.extensionManager = new GrimExtensionManager();
this.eventBus = new OptimizedEventBus(extensionManager);
this.externalAPI = new GrimExternalAPI(this);
}
// the order matters
private static Platform detectPlatform() {
final Map<String, Platform> platforms = Map.of(
"io.papermc.paper.threadedregions.RegionizedServer", Platform.FOLIA,
"org.bukkit.Bukkit", Platform.BUKKIT,
"net.fabricmc.loader.api.FabricLoader", Platform.FABRIC
);
return platforms.entrySet().stream()
.filter(entry -> ReflectionUtils.hasClass(entry.getKey()))
.map(Map.Entry::getValue)
.findFirst()
.orElseThrow(() -> new IllegalStateException("Unknown platform!"));
Platform override = CommonGrimArguments.PLATFORM_OVERRIDE.value();
if (override != null) return override;
if (ReflectionUtils.hasClass("io.papermc.paper.threadedregions.RegionizedServer")) return Platform.FOLIA;
if (ReflectionUtils.hasClass("org.bukkit.Bukkit")) return Platform.BUKKIT;
if (ReflectionUtils.hasClass("net.fabricmc.loader.api.FabricLoader")) return Platform.FABRIC;
throw new IllegalStateException("Unknown platform!");
}
public void load(PlatformLoader platformLoader, Initable... platformSpecificInitables) {
this.loader = platformLoader;
this.initManager = new InitManager(loader.getPacketEvents(), loader::getCommandManager, platformSpecificInitables);
this.violationDatabaseManager = new ViolationDatabaseManager(getGrimPlugin());
this.initManager = new InitManager(loader.getPacketEvents(), platformSpecificInitables);
this.initManager.load();
this.initialized = true;
}
@@ -101,10 +98,6 @@ public final class GrimAPI {
return loader.getPlatformPlayerFactory();
}
public ParserDescriptorFactory getParserDescriptors() {
return loader.getParserDescriptorFactory();
}
public GrimPlugin getGrimPlugin() {
return loader.getPlugin();
}
@@ -129,8 +122,8 @@ public final class GrimAPI {
return loader.getMessagePlaceHolderManager();
}
public CommandManager<Sender> getCommandManager() {
return loader.getCommandManager();
public CommandService getCommandService() {
return loader.getCommandService();
}
private void checkInitialized() {
@@ -142,4 +135,8 @@ public final class GrimAPI {
public PermissionRegistrationManager getPermissionManager() {
return loader.getPermissionManager();
}
public GrimExtensionManager getExtensionManager() {
return extensionManager;
}
}
@@ -1,21 +1,21 @@
package ac.grim.grimac;
import ac.grim.grimac.api.GrimAbstractAPI;
import ac.grim.grimac.api.plugin.GrimPluginDescription;
import ac.grim.grimac.api.GrimUser;
import ac.grim.grimac.api.alerts.AlertManager;
import ac.grim.grimac.api.config.ConfigManager;
import ac.grim.grimac.api.event.EventBus;
import ac.grim.grimac.api.event.events.GrimReloadEvent;
import ac.grim.grimac.api.plugin.GrimPlugin;
import ac.grim.grimac.manager.config.ConfigManagerFileImpl;
import ac.grim.grimac.manager.init.start.StartableInitable;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.LogUtil;
import ac.grim.grimac.utils.chat.ChatUtil;
import ac.grim.grimac.utils.anticheat.MessageUtil;
import ac.grim.grimac.utils.common.ConfigReloadObserver;
import com.github.retrooper.packetevents.netty.channel.ChannelHelper;
import lombok.Getter;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Map;
@@ -43,10 +43,15 @@ public class GrimExternalAPI implements GrimAbstractAPI, ConfigReloadObserver, S
}
@Override
public @NonNull EventBus getEventBus() {
public @NotNull EventBus getEventBus() {
return api.getEventBus();
}
@Override
public @Nullable GrimUser getGrimUser(Player player) {
return getGrimUser(player.getUniqueId());
}
@Override
public @Nullable GrimUser getGrimUser(UUID uuid) {
return api.getPlayerDataManager().getPlayer(uuid);
@@ -72,8 +77,7 @@ public class GrimExternalAPI implements GrimAbstractAPI, ConfigReloadObserver, S
@Override
public String getGrimVersion() {
GrimPluginDescription description = GrimAPI.INSTANCE.getGrimPlugin().getDescription();
return description.getVersion();
return api.getGrimPlugin().getDescription().getVersion();
}
@Override
@@ -110,6 +114,11 @@ public class GrimExternalAPI implements GrimAbstractAPI, ConfigReloadObserver, S
return GrimAPI.INSTANCE.getTickManager().currentTick;
}
@Override
public @NotNull GrimPlugin getGrimPlugin(@NotNull Object o) {
return this.api.getExtensionManager().getPlugin(o);
}
// on load, load the config & register the service
public void load() {
reload(configManagerFile);
@@ -123,7 +132,7 @@ public class GrimExternalAPI implements GrimAbstractAPI, ConfigReloadObserver, S
try {
GrimAPI.INSTANCE.getConfigManager().start();
} catch (Exception e) {
e.printStackTrace();
LogUtil.error("Failed to start config manager.", e);
}
}
@@ -159,7 +168,7 @@ public class GrimExternalAPI implements GrimAbstractAPI, ConfigReloadObserver, S
() -> GrimAPI.INSTANCE.getEventBus().post(new GrimReloadEvent(true)));
return true;
} catch (Exception e) {
e.printStackTrace();
LogUtil.error("Failed to reload config", e);
}
if (started)
GrimAPI.INSTANCE.getScheduler().getAsyncScheduler().runNow(GrimAPI.INSTANCE.getGrimPlugin(),
@@ -181,14 +190,12 @@ public class GrimExternalAPI implements GrimAbstractAPI, ConfigReloadObserver, S
GrimAPI.INSTANCE.getAlertManager().reload(configManager);
GrimAPI.INSTANCE.getDiscordManager().reload();
GrimAPI.INSTANCE.getSpectateManager().reload();
GrimAPI.INSTANCE.getViolationDatabaseManager().reload();
// Don't reload players if the plugin hasn't started yet
if (!started) return;
// Reload checks for all players
for (GrimPlayer grimPlayer : GrimAPI.INSTANCE.getPlayerDataManager().getEntries()) {
ChannelHelper.runInEventLoop(grimPlayer.user.getChannel(), () -> {
grimPlayer.updatePermissions();
grimPlayer.reload(configManager);
});
for (GrimPlayer player : GrimAPI.INSTANCE.getPlayerDataManager().getEntries()) {
player.runSafely(() -> player.reload(configManager));
}
}
@@ -203,7 +210,7 @@ public class GrimExternalAPI implements GrimAbstractAPI, ConfigReloadObserver, S
variableReplacements.putIfAbsent("%tps%", user -> String.format("%.2f", GrimAPI.INSTANCE.getPlatformServer().getTPS()));
variableReplacements.putIfAbsent("%version%", GrimUser::getVersionName);
// static variables
staticReplacements.put("%prefix%", ChatUtil.translateAlternateColorCodes('&', GrimAPI.INSTANCE.getConfigManager().getPrefix()));
staticReplacements.put("%prefix%", MessageUtil.translateAlternateColorCodes('&', GrimAPI.INSTANCE.getConfigManager().getPrefix()));
staticReplacements.putIfAbsent("%grim_version%", getGrimVersion());
}
}
@@ -8,13 +8,15 @@ import ac.grim.grimac.player.GrimPlayer;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.protocol.packettype.PacketTypeCommon;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerFlying;
import com.github.retrooper.packetevents.protocol.player.DiggingAction;
import lombok.Getter;
import lombok.Setter;
import org.jetbrains.annotations.NotNull;
import java.util.Objects;
import static com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerFlying.isFlying;
// Class from https://github.com/Tecnio/AntiCheatBase/blob/master/src/main/java/me/tecnio/anticheat/check/Check.java
@Getter
public class Check extends GrimProcessor implements AbstractCheck {
@@ -59,22 +61,19 @@ public class Check extends GrimProcessor implements AbstractCheck {
}
public boolean shouldModifyPackets() {
return isEnabled && !player.disableGrim && !player.noModifyPacketPermission && !exemptPermission;
return isEnabled
&& !player.disableGrim
&& !player.noModifyPacketPermission
&& !noModifyPacketPermission
&& !exemptPermission;
}
public void updatePermissions() {
if (player.platformPlayer == null || configName == null) return;
GrimAPI.INSTANCE.getScheduler().getEntityScheduler().run(
player.platformPlayer,
GrimAPI.INSTANCE.getGrimPlugin(),
() -> {
final String id = configName.toLowerCase();
exemptPermission = player.platformPlayer.hasPermission("grim.exempt." + id);
noSetbackPermission = player.platformPlayer.hasPermission("grim.nosetback." + id);
noModifyPacketPermission = player.platformPlayer.hasPermission("grim.nomodifypacket." + id);
},
() -> {}
);
public final void updatePermissions() {
if (configName == null || player.platformPlayer == null) return;
final String id = configName.toLowerCase();
exemptPermission = player.platformPlayer.hasPermission("grim.exempt." + id);
noSetbackPermission = player.platformPlayer.hasPermission("grim.nosetback." + id);
noModifyPacketPermission = player.platformPlayer.hasPermission("grim.nomodifypacket." + id);
}
public final boolean flagAndAlert(String verbose) {
@@ -136,14 +135,13 @@ public class Check extends GrimProcessor implements AbstractCheck {
}
@Override
public void reload(ConfigManager configuration) {
public final void reload(ConfigManager configuration) {
decay = configuration.getDoubleElse(configName + ".decay", decay);
setbackVL = configuration.getDoubleElse(configName + ".setbackvl", setbackVL);
displayName = configuration.getStringElse(configName + ".displayname", checkName);
description = configuration.getStringElse(configName + ".description", description);
if (setbackVL == -1) setbackVL = Double.MAX_VALUE;
updatePermissions();
onReload(configuration);
}
@@ -171,13 +169,15 @@ public class Check extends GrimProcessor implements AbstractCheck {
return offset > 0.001 ? String.format("%.5f", offset) : String.format("%.2E", offset);
}
public boolean isTransaction(PacketTypeCommon packetType) {
public static boolean isTransaction(PacketTypeCommon packetType) {
return packetType == PacketType.Play.Client.PONG ||
packetType == PacketType.Play.Client.WINDOW_CONFIRMATION;
}
public boolean isFlying(PacketTypeCommon packetType) {
return WrapperPlayClientPlayerFlying.isFlying(packetType);
public static boolean isAsync(PacketTypeCommon packetType) {
return packetType == PacketType.Play.Client.KEEP_ALIVE
|| packetType == PacketType.Play.Client.CHUNK_BATCH_ACK
|| packetType == PacketType.Play.Client.RESOURCE_PACK_STATUS;
}
public boolean isUpdate(PacketTypeCommon packetType) {
@@ -209,4 +209,10 @@ public class Check extends GrimProcessor implements AbstractCheck {
return isFlying(packetType);
}
// prevent causing exploits with packet cancelling (ie noslow)
public boolean canCancel(DiggingAction action) {
return action != DiggingAction.RELEASE_USE_ITEM
// we check client version here because 1.8- doesn't predict dropping items, so we can cancel them. (see CompensatedInventory)
&& (action != DiggingAction.DROP_ITEM && action != DiggingAction.DROP_ITEM_STACK || player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8));
}
}
@@ -4,19 +4,11 @@ import ac.grim.grimac.checks.Check;
import ac.grim.grimac.player.GrimPlayer;
public abstract class AbstractDebugHandler extends Check {
protected final GrimPlayer grimPlayer;
public AbstractDebugHandler(GrimPlayer grimPlayer) {
super(grimPlayer);
this.grimPlayer = grimPlayer;
public AbstractDebugHandler(GrimPlayer player) {
super(player);
}
public GrimPlayer getPlayer() {
return grimPlayer;
}
public abstract void toggleListener(GrimPlayer player);
public abstract boolean toggleListener(GrimPlayer player);
public abstract boolean toggleConsoleOutput();
}
@@ -0,0 +1,209 @@
package ac.grim.grimac.checks.debug;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.collisions.datatypes.CollisionBox;
import ac.grim.grimac.utils.collisions.datatypes.NoCollisionBox;
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
import ac.grim.grimac.utils.data.Pair;
import ac.grim.grimac.utils.math.Vector3dm;
import com.github.retrooper.packetevents.netty.buffer.ByteBufHelper;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerPluginMessage;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
/**
* Handles debug visualization of hitboxes and reach calculations for GrimAC.
* Sends debug data to clients through plugin messages that can be visualized by compatible clients.
*/
public class HitboxDebugHandler extends AbstractDebugHandler {
/**
* Set of players currently listening to hitbox debug data
*/
private final Set<GrimPlayer> listeners = new CopyOnWriteArraySet<>(new HashSet<>());
/**
* Creates a new HitboxDebugHandler for the specified player
*
* @param grimPlayer The GrimAC player instance to debug
*/
public HitboxDebugHandler(GrimPlayer grimPlayer) {
super(grimPlayer);
}
/**
* Toggles whether a player receives hitbox debug visualization data.
* If the player is already listening, they will be removed. If not, they will be added.
*
* @param player The player to toggle debug visualization for
* @return {@code true} if the player is now listening (was added),
* {@code false} if the player is no longer listening (was removed).
*/
@Override
public boolean toggleListener(GrimPlayer player) {
boolean wasPresent = listeners.remove(player);
if (wasPresent) {
return false;
} else {
listeners.add(player);
return true;
}
}
@Override
public boolean toggleConsoleOutput() {
throw new UnsupportedOperationException();
}
/**
* Sends debug visualization data for reach calculations to all listening players.
* The data includes hitboxes, target entities, look vectors, and eye heights used in reach calculations.
*
* @param hitboxes Map of entity IDs to their collision boxes
* @param targetEntities Set of entity IDs that are considered targets
* @param lookVecsAndEyeHeights List of pairs containing look vectors and their corresponding eye heights
* @param baseX Base x coordinate before eye height adjustments
* @param baseY Base y coordinate before eye height adjustments
* @param baseZ Base z coordinate before eye height adjustments
* @param isPrediction Whether these hitboxes are from a prediction calculation
*
* Packet Format (Version 1):
* - Byte: Version (0)
* - Byte: Global flags
* - Bit 0: isPrediction
* - Bits 1-7: Reserved
* - Double: Player reach/interact distance
* - Vector3d: Base position (3 doubles)
* - VarInt: Number of ray traces
* - For each ray trace:
* - Double: Eye height delta
* - Vector3d: Look vector (3 doubles)
* - VarInt: Number of hitboxes
* - For each hitbox:
* - VarInt: Entity ID
* - Byte: Flags
* - Bit 0: Is target entity
* - Bit 1: Is no collision
* - Bits 2-7: Reserved
* - If not NoCollisionBox:
* - Double: minX
* - Double: minY
* - Double: minZ
* - Double: maxX
* - Double: maxY
* - Double: maxZ
*/
public void sendHitboxData(Map<Integer, CollisionBox> hitboxes, Set<Integer> targetEntities,
List<Pair<Vector3dm, Double>> lookVecsAndEyeHeights, double baseX, double baseY, double baseZ,
boolean isPrediction, double reachDistance) {
if (!isEnabled()) return;
ByteBuf buffer = Unpooled.buffer();
try {
// Version Header
buffer.writeByte(1);
// Global Flags Header
// Pack boolean flags into a single byte
byte global_flags = 0;
global_flags |= (isPrediction ? 1 : 0); // Bit 0: are the hitboxes from a prediction?
// Bits 2-7 reserved for future use
buffer.writeByte(global_flags);
// Write reach distance
buffer.writeDouble(reachDistance);
// Write base position
writeVector(buffer, baseX, baseY, baseZ);
// Write number of ray traces
ByteBufHelper.writeVarInt(buffer, lookVecsAndEyeHeights.size());
// Write all possible ray traces
for (Pair<Vector3dm, Double> pair : lookVecsAndEyeHeights) {
Vector3dm lookVec = pair.first();
double eyeHeight = pair.second();
// Write eye height delta and look vector
// we make them 3 meters shorter because all of the vectors passed into here are made 3 meters longer
// then they need to be so grim can report correct reach distance for too far away hits
buffer.writeDouble(eyeHeight);
writeVector(buffer, lookVec.getX(), lookVec.getY(), lookVec.getZ());
}
// Write number of hitboxes
ByteBufHelper.writeVarInt(buffer, hitboxes.size());
// Write hitbox data
for (Map.Entry<Integer, CollisionBox> entry : hitboxes.entrySet()) {
int entityId = entry.getKey();
CollisionBox box = entry.getValue();
// Write entity ID
ByteBufHelper.writeVarInt(buffer, entityId);
// Pack boolean flags into a single byte
byte flags = 0;
flags |= (targetEntities.contains(entityId) ? 1 : 0); // Bit 0: Is target
flags |= (box == NoCollisionBox.INSTANCE ? 2 : 0); // Bit 1: Is no collision
// Bits 2-7 reserved for future use
buffer.writeByte(flags);
// Write box coordinates if it's not a NoCollisionBox
if ((flags & 2) == 0) {
SimpleCollisionBox simpleCollisionBox = (SimpleCollisionBox) box;
buffer.writeDouble(simpleCollisionBox.minX);
buffer.writeDouble(simpleCollisionBox.minY);
buffer.writeDouble(simpleCollisionBox.minZ);
buffer.writeDouble(simpleCollisionBox.maxX);
buffer.writeDouble(simpleCollisionBox.maxY);
buffer.writeDouble(simpleCollisionBox.maxZ);
}
}
// Convert buffer to byte array
byte[] data = new byte[buffer.readableBytes()];
buffer.readBytes(data);
// Create and send packet
WrapperPlayServerPluginMessage packet = new WrapperPlayServerPluginMessage(
"grim:debug_hitbox",
data
);
// Send to all listeners
for (GrimPlayer listener : listeners) {
if (listener != null) {
listener.user.sendPacket(packet);
}
}
} finally {
// Release buffer to prevent memory leaks
buffer.release();
}
}
public boolean isEnabled() {
return !listeners.isEmpty();
}
/**
* Helper method to write vector coordinates to the ByteBuf
* @param buffer The buffer to write to
* @param x The x coordinate to write
* @param y The y coordinate to write
* @param z The z coordinate to write
*/
private void writeVector(ByteBuf buffer, double x, double y, double z) {
buffer.writeDouble(x);
buffer.writeDouble(y);
buffer.writeDouble(z);
}
}
@@ -8,7 +8,7 @@ import ac.grim.grimac.utils.anticheat.update.RotationUpdate;
@CheckData(name = "AimDuplicateLook")
public class AimDuplicateLook extends Check implements RotationCheck {
boolean exempt = false;
private boolean exempt;
public AimDuplicateLook(GrimPlayer playerData) {
super(playerData);
@@ -28,7 +28,7 @@ public class AimModulo360 extends Check implements RotationCheck {
return;
}
if (player.xRot < 360 && player.xRot > -360 && Math.abs(rotationUpdate.getDeltaXRot()) > 320 && Math.abs(lastDeltaYaw) < 30) {
if (player.yaw < 360 && player.yaw > -360 && Math.abs(rotationUpdate.getDeltaXRot()) > 320 && Math.abs(lastDeltaYaw) < 30) {
flagAndAlert();
} else {
reward();
@@ -8,7 +8,6 @@ import ac.grim.grimac.utils.data.Pair;
import ac.grim.grimac.utils.lists.RunningMode;
import ac.grim.grimac.utils.math.GrimMath;
public class AimProcessor extends Check implements RotationCheck {
private static final int SIGNIFICANT_SAMPLES_THRESHOLD = 15;
@@ -19,10 +18,10 @@ public class AimProcessor extends Check implements RotationCheck {
public double divisorY;
public double modeX, modeY;
public double deltaDotsX, deltaDotsY;
RunningMode xRotMode = new RunningMode(TOTAL_SAMPLES_THRESHOLD);
RunningMode yRotMode = new RunningMode(TOTAL_SAMPLES_THRESHOLD);
float lastXRot;
float lastYRot;
private final RunningMode xRotMode = new RunningMode(TOTAL_SAMPLES_THRESHOLD);
private final RunningMode yRotMode = new RunningMode(TOTAL_SAMPLES_THRESHOLD);
private float lastXRot;
private float lastYRot;
public AimProcessor(GrimPlayer playerData) {
super(playerData);
@@ -38,7 +37,6 @@ public class AimProcessor extends Check implements RotationCheck {
public void process(final RotationUpdate rotationUpdate) {
rotationUpdate.setProcessor(this);
float deltaXRot = rotationUpdate.getDeltaXRotABS();
this.divisorX = GrimMath.gcd(deltaXRot, lastXRot);
@@ -47,7 +45,6 @@ public class AimProcessor extends Check implements RotationCheck {
this.lastXRot = deltaXRot;
}
float deltaYRot = rotationUpdate.getDeltaYRotABS();
this.divisorY = GrimMath.gcd(deltaYRot, lastYRot);
@@ -10,7 +10,7 @@ import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientHe
@CheckData(name = "BadPacketsA", description = "Sent duplicate slot id")
public class BadPacketsA extends Check implements PacketCheck {
int lastSlot = -1;
private int lastSlot = -1;
public BadPacketsA(final GrimPlayer player) {
super(player);
@@ -6,26 +6,21 @@ 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.GameMode;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientInteractEntity;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientEntityAction;
import org.jetbrains.annotations.NotNull;
@CheckData(name = "BadPacketsC", description = "Interacted with self")
@CheckData(name = "BadPacketsC", description = "Tried to wake up while not sleeping", experimental = true)
public class BadPacketsC extends Check implements PacketCheck {
public BadPacketsC(GrimPlayer player) {
public BadPacketsC(@NotNull GrimPlayer player) {
super(player);
}
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (event.getPacketType() == PacketType.Play.Client.INTERACT_ENTITY) {
if (player.gamemode == GameMode.SPECTATOR) return;
if (new WrapperPlayClientInteractEntity(event).getEntityId() == player.entityID) {
// Instant ban
if (flagAndAlert() && shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
}
}
if (event.getPacketType() == PacketType.Play.Client.ENTITY_ACTION
&& new WrapperPlayClientEntityAction(event).getAction() == WrapperPlayClientEntityAction.Action.LEAVE_BED
&& !player.isInBed) {
flagAndAlert();
}
}
}
@@ -22,15 +22,13 @@ public class BadPacketsD extends Check implements PacketCheck {
final float pitch = new WrapperPlayClientPlayerFlying(event).getLocation().getPitch();
if (pitch > 90 || pitch < -90) {
// Ban.
if (flagAndAlert("pitch=" + pitch)) {
if (shouldModifyPackets()) {
// prevent other checks from using an invalid pitch
if (player.yRot > 90) player.yRot = 90;
if (player.yRot < -90) player.yRot = -90;
if (flagAndAlert("pitch=" + pitch) && shouldModifyPackets()) {
// prevent other checks from using an invalid pitch
if (player.pitch > 90) player.pitch = 90;
if (player.pitch < -90) player.pitch = -90;
event.setCancelled(true);
player.onPacketCancel();
}
event.setCancelled(true);
player.onPacketCancel();
}
}
}
@@ -14,6 +14,8 @@ import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPl
@CheckData(name = "BadPacketsE")
public class BadPacketsE extends Check implements PacketCheck {
private int noReminderTicks;
private final int maxNoReminderTicks = player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8) ? 20 : 19;
private final boolean isViaPleaseStopUsingProtocolHacksOnYourServer = player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_21_2) || PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_21_2);
public BadPacketsE(GrimPlayer player) {
super(player);
@@ -21,20 +23,17 @@ public class BadPacketsE extends Check implements PacketCheck {
@Override
public void onPacketReceive(PacketReceiveEvent event) {
final boolean isViaPleaseStopUsingProtocolHacksOnYourServer = player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_21_2) || PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_21_2);
if (event.getPacketType() == PacketType.Play.Client.PLAYER_POSITION_AND_ROTATION ||
event.getPacketType() == PacketType.Play.Client.PLAYER_POSITION) {
noReminderTicks = 0;
} else if (WrapperPlayClientPlayerFlying.isFlying(event.getPacketType()) && !player.packetStateData.lastPacketWasTeleport) {
noReminderTicks++;
if (++noReminderTicks > maxNoReminderTicks) {
flagAndAlert("ticks=" + noReminderTicks);
}
} else if (event.getPacketType() == PacketType.Play.Client.STEER_VEHICLE
|| (isViaPleaseStopUsingProtocolHacksOnYourServer && player.inVehicle())) {
noReminderTicks = 0; // Exempt vehicles
}
if (noReminderTicks > 20) {
flagAndAlert("ticks=" + noReminderTicks); // ban? I don't know how this would false
}
}
public void handleRespawn() {
@@ -54,15 +54,11 @@ public class BadPacketsH extends BlockPlaceCheck {
}
public boolean shouldCancel(int sequence) {
if (isSupportedVersion && sequence != lastSequence + 1) {
if (flagAndAlert("expected=" + (lastSequence + 1) + ", id=" + sequence) && shouldModifyPackets()) {
lastSequence = sequence;
return true;
}
}
int expected = lastSequence + 1;
lastSequence = sequence;
return false;
return isSupportedVersion && sequence != expected
&& flagAndAlert("expected=" + expected + ", id=" + sequence)
&& shouldModifyPackets();
}
public void onWorldChange() {
@@ -16,13 +16,11 @@ public class BadPacketsI extends Check implements PacketCheck {
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (event.getPacketType() == PacketType.Play.Client.PLAYER_ABILITIES) {
if (new WrapperPlayClientPlayerAbilities(event).isFlying() && !player.canFly) {
if (flagAndAlert() && shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
}
}
if (event.getPacketType() == PacketType.Play.Client.PLAYER_ABILITIES
&& new WrapperPlayClientPlayerAbilities(event).isFlying() && !player.canFly
&& flagAndAlert() && shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
}
}
}
@@ -2,7 +2,7 @@ package ac.grim.grimac.checks.impl.badpackets;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PostPredictionCheck;
import ac.grim.grimac.checks.type.PacketCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.data.HeadRotation;
import com.github.retrooper.packetevents.PacketEvents;
@@ -10,14 +10,13 @@ import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.player.GameMode;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientUseItem;
import java.util.ArrayList;
import java.util.List;
@CheckData(name = "BadPacketsJ", description = "Rotation in use item packet did not match tick rotation", experimental = true)
public class BadPacketsJ extends Check implements PostPredictionCheck {
@CheckData(name = "BadPacketsJ", description = "Rotation in use item packet did not match tick rotation")
public class BadPacketsJ extends Check implements PacketCheck {
private final List<HeadRotation> rotations = new ArrayList<>();
public BadPacketsJ(GrimPlayer player) {
@@ -26,7 +25,7 @@ public class BadPacketsJ extends Check implements PostPredictionCheck {
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (player.gamemode == GameMode.SPECTATOR) {
if (!player.cameraEntity.isSelf()) {
rotations.clear();
return;
}
@@ -41,12 +40,12 @@ public class BadPacketsJ extends Check implements PostPredictionCheck {
// due to tick skipping, the rotations sent could be last tick's
boolean allowLast = player.canSkipTicks() && (event.getPacketType() == PacketType.Play.Client.PLAYER_POSITION_AND_ROTATION || event.getPacketType() == PacketType.Play.Client.PLAYER_ROTATION);
for (HeadRotation rotation : rotations) {
if (rotation.getYaw() == player.xRot && rotation.getPitch() == player.yRot) {
if (rotation.yaw() == player.yaw && rotation.pitch() == player.pitch) {
allowLast = false;
continue;
}
if (rotation.getYaw() == player.lastXRot && rotation.getPitch() == player.lastYRot && allowLast) {
if (rotation.yaw() == player.lastYaw && rotation.pitch() == player.lastPitch && allowLast) {
continue;
}
@@ -16,13 +16,11 @@ public class BadPacketsK extends Check implements PacketCheck {
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (event.getPacketType() == PacketType.Play.Client.SPECTATE) {
if (player.gamemode != GameMode.SPECTATOR) {
if (flagAndAlert() && shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
}
}
if (event.getPacketType() == PacketType.Play.Client.SPECTATE
&& player.gamemode != GameMode.SPECTATOR
&& flagAndAlert() && shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
}
}
}
@@ -43,7 +43,7 @@ public class BadPacketsL extends Check implements PacketCheck {
+ ", face=" + packet.getBlockFace()
+ ", sequence=" + packet.getSequence()
+ ", action=" + packet.getAction().toString().toLowerCase(Locale.ROOT)
) && shouldModifyPackets() && packet.getAction() != DiggingAction.RELEASE_USE_ITEM) {
) && shouldModifyPackets() && canCancel(packet.getAction())) {
event.setCancelled(true);
player.onPacketCancel();
}
@@ -0,0 +1,64 @@
package ac.grim.grimac.checks.impl.badpackets;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PacketCheck;
import ac.grim.grimac.player.GrimPlayer;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.event.PacketSendEvent;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.player.Combat;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientClientStatus;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerChangeGameState;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerCombatEvent;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerDeathCombatEvent;
@CheckData(name = "BadPacketsM", description = "Tried to respawn while alive", experimental = true)
public class BadPacketsM extends Check implements PacketCheck {
public BadPacketsM(final GrimPlayer player) {
super(player);
}
// not a boolean because the server could send packets that cause
// the client to send a respawn packet before it receives the first
private int exempt;
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (event.getPacketType() == PacketType.Play.Client.CLIENT_STATUS && new WrapperPlayClientClientStatus(event).getAction() == WrapperPlayClientClientStatus.Action.PERFORM_RESPAWN) {
if (exempt > 0) {
exempt--;
return;
}
if (!player.compensatedEntities.self.isDead) {
if (flagAndAlert() && shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
}
}
}
}
@Override
public void onPacketSend(PacketSendEvent event) {
if (event.getPacketType() == PacketType.Play.Server.CHANGE_GAME_STATE && player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_9)
&& new WrapperPlayServerChangeGameState(event).getReason() == WrapperPlayServerChangeGameState.Reason.WIN_GAME) {
player.addRealTimeTaskNow(() -> exempt++);
}
if (event.getPacketType() == PacketType.Play.Server.DEATH_COMBAT_EVENT && player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_9)) {
if (new WrapperPlayServerDeathCombatEvent(event).getPlayerId() == player.entityID) {
player.addRealTimeTaskNow(() -> exempt++);
}
}
if (event.getPacketType() == PacketType.Play.Server.COMBAT_EVENT && player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_9)) {
WrapperPlayServerCombatEvent packet = new WrapperPlayServerCombatEvent(event);
if (packet.getCombat() == Combat.ENTITY_DEAD && packet.getPlayerId() == player.entityID) {
player.addRealTimeTaskNow(() -> exempt++);
}
}
}
}
@@ -2,11 +2,10 @@ package ac.grim.grimac.checks.impl.badpackets;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PacketCheck;
import ac.grim.grimac.player.GrimPlayer;
@CheckData(name = "BadPacketsN", setback = 0)
public class BadPacketsN extends Check implements PacketCheck {
public class BadPacketsN extends Check {
public BadPacketsN(final GrimPlayer player) {
super(player);
}
@@ -4,7 +4,6 @@ import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PacketCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.data.Pair;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.event.PacketSendEvent;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
@@ -12,11 +11,10 @@ import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientKe
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerKeepAlive;
import java.util.LinkedList;
import java.util.Queue;
@CheckData(name = "BadPacketsO")
public class BadPacketsO extends Check implements PacketCheck {
Queue<Pair<Long, Long>> keepaliveMap = new LinkedList<>();
private final LinkedList<Long> keepalives = new LinkedList<>();
public BadPacketsO(GrimPlayer player) {
super(player);
@@ -25,37 +23,30 @@ public class BadPacketsO extends Check implements PacketCheck {
@Override
public void onPacketSend(PacketSendEvent event) {
if (event.getPacketType() == PacketType.Play.Server.KEEP_ALIVE) {
WrapperPlayServerKeepAlive packet = new WrapperPlayServerKeepAlive(event);
keepaliveMap.add(new Pair<>(packet.getId(), System.nanoTime()));
keepalives.add(new WrapperPlayServerKeepAlive(event).getId());
}
}
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (event.getPacketType() == PacketType.Play.Client.KEEP_ALIVE) {
WrapperPlayClientKeepAlive packet = new WrapperPlayClientKeepAlive(event);
final long id = new WrapperPlayClientKeepAlive(event).getId();
long id = packet.getId();
boolean hasID = false;
for (long keepalive : keepalives) {
if (keepalive == id) {
// Found the ID, remove stuff until we get to it (to stop very slow memory leaks)
Long data;
do {
data = keepalives.poll();
} while (data != null && data != id);
for (Pair<Long, Long> iterator : keepaliveMap) {
if (iterator.first() == id) {
hasID = true;
break;
return;
}
}
if (!hasID) {
if (flagAndAlert("id=" + id) && shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
}
} else { // Found the ID, remove stuff until we get to it (to stop very slow memory leaks)
Pair<Long, Long> data;
do {
data = keepaliveMap.poll();
if (data == null) break;
} while (data.first() != id);
if (flagAndAlert("id=" + id) && shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
}
}
}
@@ -11,7 +11,7 @@ import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientCl
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientClickWindow.WindowClickType;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerOpenWindow;
@CheckData(name = "BadPacketsP", experimental = true)
@CheckData(name = "BadPacketsP", description = "Invalid window click packet", experimental = true)
public class BadPacketsP extends Check implements PacketCheck {
private int containerType = -1;
@@ -6,7 +6,6 @@ import ac.grim.grimac.checks.type.PacketCheck;
import ac.grim.grimac.player.GrimPlayer;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.protocol.player.GameMode;
@CheckData(name = "BadPacketsR", decay = 0.25, experimental = true)
public class BadPacketsR extends Check implements PacketCheck {
@@ -25,11 +24,12 @@ public class BadPacketsR extends Check implements PacketCheck {
long ms = (player.getPlayerClockAtLeast() - clock) / 1000000L;
long diff = (System.currentTimeMillis() - lastTransTime);
if (diff > 2000 && ms > 2000) {
if (positions == 0 && clock != 0 && player.gamemode != GameMode.SPECTATOR && !player.compensatedEntities.self.isDead) {
if (positions == 0 && clock != 0 && player.cameraEntity.isSelf() && !player.compensatedEntities.self.isDead) {
flag("time=" + ms + "ms, " + "lst=" + diff + "ms, positions=" + positions);
} else {
reward();
}
player.compensatedEntities.entitiesRemovedThisTick.clear();
player.compensatedWorld.removeInvalidPistonLikeStuff(oldTransId);
positions = 0;
clock = player.getPlayerClockAtLeast();
@@ -4,6 +4,9 @@ import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PacketCheck;
import ac.grim.grimac.player.GrimPlayer;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientWindowConfirmation;
@CheckData(name = "BadPacketsS")
public class BadPacketsS extends Check implements PacketCheck {
@@ -11,4 +14,13 @@ public class BadPacketsS extends Check implements PacketCheck {
super(player);
}
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (event.getPacketType() == PacketType.Play.Client.WINDOW_CONFIRMATION
&& !new WrapperPlayClientWindowConfirmation(event).isAccepted()
&& flagAndAlert() && shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
}
}
}
@@ -10,20 +10,25 @@ import com.github.retrooper.packetevents.protocol.attribute.Attributes;
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.util.Vector3f;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientInteractEntity;
@CheckData(name = "BadPacketsT")
public class BadPacketsT extends Check implements PacketCheck {
// 1.7 and 1.8 seem to have different hitbox "expansion" values than 1.9+
// https://github.com/GrimAnticheat/Grim/pull/1274#issuecomment-1872458702
// https://github.com/GrimAnticheat/Grim/pull/1274#issuecomment-1872533497
private final boolean hasLegacyExpansion = player.getClientVersion().isOlderThan(ClientVersion.V_1_9);
private final double maxHorizontalDisplacement = 0.3001 + (hasLegacyExpansion ? 0.1 : 0);
private final double minVerticalDisplacement = -0.0001 - (hasLegacyExpansion ? 0.1 : 0);
private final double maxVerticalDisplacement = 1.8001 + (hasLegacyExpansion ? 0.1 : 0);
private final double maxHorizontalDisplacement;
private final double minVerticalDisplacement;
private final double maxVerticalDisplacement;
public BadPacketsT(final GrimPlayer player) {
super(player);
// 1.7 and 1.8 seem to have different hitbox "expansion" values than 1.9+
// https://github.com/GrimAnticheat/Grim/pull/1274#issuecomment-1872458702
// https://github.com/GrimAnticheat/Grim/pull/1274#issuecomment-1872533497
double expansion = player.getClientVersion().isOlderThan(ClientVersion.V_1_9) ? 0.1 : 0;
maxHorizontalDisplacement = 0.3001 + expansion;
minVerticalDisplacement = -0.0001 - expansion;
maxVerticalDisplacement = 1.8001 + expansion;
}
@Override
@@ -31,37 +36,39 @@ public class BadPacketsT extends Check implements PacketCheck {
if (event.getPacketType().equals(PacketType.Play.Client.INTERACT_ENTITY)) {
final WrapperPlayClientInteractEntity wrapper = new WrapperPlayClientInteractEntity(event);
// Only INTERACT_AT actually has an interaction vector
wrapper.getTarget().ifPresent(targetVector -> {
final PacketEntity packetEntity = player.compensatedEntities.getEntity(wrapper.getEntityId());
// Don't continue if the compensated entity hasn't been resolved
if (packetEntity == null) {
return;
}
if (wrapper.getAction() != WrapperPlayClientInteractEntity.InteractAction.INTERACT_AT) return;
Vector3f targetVector = wrapper.getTarget().orElse(null);
if (targetVector == null) return; // shouldn't ever happen, but whatever
// Make sure our target entity is actually a player (Player NPCs work too)
if (!EntityTypes.PLAYER.equals(packetEntity.getType())) {
// We can't check for any entity that is not a player
return;
}
final PacketEntity packetEntity = player.compensatedEntities.getEntity(wrapper.getEntityId());
// Don't continue if the compensated entity hasn't been resolved
if (packetEntity == null) {
return;
}
// Perform the interaction vector check
// TODO:
// 27/12/2023 - Dynamic values for more than just one entity type?
// 28/12/2023 - Player-only is fine
// 30/12/2023 - Expansions differ in 1.9+
final float scale = (float) packetEntity.getAttributeValue(Attributes.SCALE);
if (targetVector.y > (minVerticalDisplacement * scale) && targetVector.y < (maxVerticalDisplacement * scale)
&& Math.abs(targetVector.x) < (maxHorizontalDisplacement * scale)
&& Math.abs(targetVector.z) < (maxHorizontalDisplacement * scale)) {
return;
}
// Make sure our target entity is actually a player (Player NPCs work too)
if (!EntityTypes.PLAYER.equals(packetEntity.type)) {
// We can't check for any entity that is not a player
return;
}
// Log the vector
final String verbose = String.format("%.5f/%.5f/%.5f",
targetVector.x, targetVector.y, targetVector.z);
// We could pretty much ban the player at this point
flagAndAlert(verbose);
});
// Perform the interaction vector check
// TODO:
// 27/12/2023 - Dynamic values for more than just one entity type?
// 28/12/2023 - Player-only is fine
// 30/12/2023 - Expansions differ in 1.9+
final float scale = (float) packetEntity.getAttributeValue(Attributes.SCALE);
if (targetVector.y > (minVerticalDisplacement * scale) && targetVector.y < (maxVerticalDisplacement * scale)
&& Math.abs(targetVector.x) < (maxHorizontalDisplacement * scale)
&& Math.abs(targetVector.z) < (maxHorizontalDisplacement * scale)) {
return;
}
// Log the vector
final String verbose = String.format("%.5f/%.5f/%.5f",
targetVector.x, targetVector.y, targetVector.z);
// We could pretty much ban the player at this point
flagAndAlert(verbose);
}
}
}
@@ -2,11 +2,10 @@ package ac.grim.grimac.checks.impl.badpackets;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PacketCheck;
import ac.grim.grimac.player.GrimPlayer;
@CheckData(name = "BadPacketsW", description = "Interacted with non-existent entity", experimental = true)
public class BadPacketsW extends Check implements PacketCheck {
public class BadPacketsW extends Check {
public BadPacketsW(GrimPlayer player) {
super(player);
}
@@ -7,7 +7,6 @@ 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.GameMode;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientEntityAction;
@CheckData(name = "BadPacketsX", experimental = true)
@@ -42,7 +41,7 @@ public class BadPacketsX extends Check implements PostPredictionCheck {
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (player.gamemode == GameMode.SPECTATOR || isTickPacket(event.getPacketType())) {
if (!player.cameraEntity.isSelf() || isTickPacket(event.getPacketType())) {
sprint = sneak = false;
return;
}
@@ -0,0 +1,32 @@
package ac.grim.grimac.checks.impl.badpackets;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PacketCheck;
import ac.grim.grimac.player.GrimPlayer;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
@CheckData(name = "BadPacketsZ", experimental = true)
public class BadPacketsZ extends Check implements PacketCheck {
private boolean sent;
public BadPacketsZ(GrimPlayer player) {
super(player);
}
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (event.getPacketType() == PacketType.Play.Client.CLIENT_TICK_END) {
sent = false;
}
if (event.getPacketType() == PacketType.Play.Client.PLAYER_INPUT) {
if (sent) {
flagAndAlert();
}
sent = true;
}
}
}
@@ -9,7 +9,7 @@ import ac.grim.grimac.utils.anticheat.update.RotationUpdate;
import ac.grim.grimac.utils.data.HeadRotation;
import ac.grim.grimac.utils.math.GrimMath;
// This check has been patched by Baritone for a long time and it also seems to false with cinematic camera now, so it is disabled.
// This check has been patched by Baritone for a long time, and it also seems to false with cinematic camera now, so it is disabled.
@CheckData(name = "Baritone")
public class Baritone extends Check implements RotationCheck {
private int verbose;
@@ -23,10 +23,10 @@ public class Baritone extends Check implements RotationCheck {
final HeadRotation from = rotationUpdate.getFrom();
final HeadRotation to = rotationUpdate.getTo();
final float deltaPitch = Math.abs(to.getPitch() - from.getPitch());
final float deltaPitch = Math.abs(to.pitch() - from.pitch());
// Baritone works with small degrees, limit to 1 degrees to pick up on baritone slightly moving aim to bypass anticheats
if (rotationUpdate.getDeltaXRot() == 0 && deltaPitch > 0 && deltaPitch < 1 && Math.abs(to.getPitch()) != 90.0f) {
// Baritone works with small degrees, limit to 1 degree to pick up on baritone slightly moving aim to bypass anticheats
if (rotationUpdate.getDeltaXRot() == 0 && deltaPitch > 0 && deltaPitch < 1 && Math.abs(to.pitch()) != 90.0f) {
if (rotationUpdate.getProcessor().divisorY < GrimMath.MINIMUM_DIVISOR) {
verbose++;
if (verbose > 8) {
@@ -12,7 +12,7 @@ import com.github.retrooper.packetevents.protocol.player.DiggingAction;
import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
import com.github.retrooper.packetevents.util.Vector3i;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.jetbrains.annotations.NotNull;
@CheckData(name = "AirLiquidBreak", description = "Breaking a block that cannot be broken")
public class AirLiquidBreak extends Check implements BlockBreakCheck {
@@ -20,8 +20,8 @@ public class AirLiquidBreak extends Check implements BlockBreakCheck {
private int lastTick;
private boolean didLastFlag;
// Initialize to non-null values to prevent NPE when checking for blockType properties and if position equals old position
private @NonNull Vector3i lastBreakLoc = new Vector3i();
private @NonNull StateType lastBlockType = StateTypes.AIR;
private @NotNull Vector3i lastBreakLoc = new Vector3i();
private @NotNull StateType lastBlockType = StateTypes.AIR;
public AirLiquidBreak(GrimPlayer player) {
super(player);
@@ -51,7 +51,7 @@ public class AirLiquidBreak extends Check implements BlockBreakCheck {
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)))
boolean invalid = (block == StateTypes.LIGHT && !(player.inventory.getHeldItem().is(ItemTypes.LIGHT) || player.inventory.getOffHand().is(ItemTypes.LIGHT)))
|| block.isAir()
|| block == StateTypes.WATER
|| block == StateTypes.LAVA
@@ -10,7 +10,6 @@ import ac.grim.grimac.utils.math.Vector3dm;
import ac.grim.grimac.utils.math.VectorUtils;
import com.github.retrooper.packetevents.protocol.attribute.Attributes;
import com.github.retrooper.packetevents.protocol.player.DiggingAction;
import com.github.retrooper.packetevents.protocol.player.GameMode;
@CheckData(name = "FarBreak", description = "Breaking blocks too far away", experimental = true)
public class FarBreak extends Check implements BlockBreakCheck {
@@ -20,15 +19,14 @@ public class FarBreak extends Check implements BlockBreakCheck {
@Override
public void onBlockBreak(BlockBreak blockBreak) {
if (player.gamemode == GameMode.SPECTATOR || player.inVehicle() || blockBreak.action == DiggingAction.CANCELLED_DIGGING)
if (!player.cameraEntity.isSelf() || player.inVehicle() || blockBreak.action == DiggingAction.CANCELLED_DIGGING)
return; // falses
double min = Double.MAX_VALUE;
for (double d : player.getPossibleEyeHeights()) {
SimpleCollisionBox box = new SimpleCollisionBox(blockBreak.position);
Vector3dm eyes = new Vector3dm(player.x, player.y + d, player.z);
Vector3dm best = VectorUtils.cutBoxToVector(eyes, box);
min = Math.min(min, eyes.distanceSquared(best));
Vector3dm best = VectorUtils.cutBoxToVector(player.x, player.y + d, player.z, box);
min = Math.min(min, best.distanceSquared(player.x, player.y + d, player.z));
}
// getPickRange() determines this?
@@ -7,7 +7,7 @@ import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.BlockBreak;
import ac.grim.grimac.utils.math.GrimMath;
import ac.grim.grimac.utils.nmsutil.BlockBreakSpeed;
import ac.grim.grimac.utils.reflection.ViaVersionUtil;
import ac.grim.grimac.utils.viaversion.ViaVersionUtil;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
@@ -53,7 +53,7 @@ public class FastBreak extends Check implements BlockBreakCheck {
@Override
public void onBlockBreak(BlockBreak blockBreak) {
if (blockBreak.action == DiggingAction.START_DIGGING) {
if (!ViaVersionUtil.isAvailable()) {
if (!ViaVersionUtil.isAvailable) {
// Exempt all blocks that do not exist in the player version
final WrappedBlockState defaultState = WrappedBlockState.getDefaultState(player.getClientVersion(), blockBreak.block.getType());
if (defaultState.getType() == StateTypes.AIR || EXEMPT_STATES.contains(defaultState.getType())) {
@@ -80,7 +80,8 @@ public class FastBreak extends Check implements BlockBreakCheck {
blockDelayBalance += 300 - breakDelay;
}
if (blockDelayBalance > 1000) { // If more than a second of advantage
// Skip flagging clients whose tick rate is unobservable, like MultiBreak does
if (blockDelayBalance > 1000 && player.isTickingReliablyFor(3)) { // If more than a second of advantage
if (flagAndAlert("delay=" + breakDelay + "ms, type=" + blockBreak.block.getType()) && shouldModifyPackets()) {
blockBreak.cancel();
}
@@ -102,7 +103,8 @@ public class FastBreak extends Check implements BlockBreakCheck {
blockBreakBalance += diff;
}
if (blockBreakBalance > 1000) { // If more than a second of advantage
// Skip flagging clients whose tick rate is unobservable, like MultiBreak does
if (blockBreakBalance > 1000 && player.isTickingReliablyFor(3)) { // If more than a second of advantage
if (flagAndAlert("diff=" + diff + "ms, balance=" + blockBreakBalance + "ms, type=" + blockBreak.block.getType()) && shouldModifyPackets()) {
blockBreak.cancel();
}
@@ -9,7 +9,6 @@ import ac.grim.grimac.utils.anticheat.update.BlockBreak;
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.protocol.player.DiggingAction;
import com.github.retrooper.packetevents.protocol.player.GameMode;
import com.github.retrooper.packetevents.protocol.world.BlockFace;
import com.github.retrooper.packetevents.util.Vector3i;
@@ -53,7 +52,7 @@ public class MultiBreak extends Check implements BlockBreakCheck {
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (player.gamemode == GameMode.SPECTATOR || isTickPacket(event.getPacketType())) {
if (!player.cameraEntity.isSelf() || isTickPacket(event.getPacketType())) {
hasBroken = false;
}
}
@@ -9,7 +9,7 @@ import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.player.DiggingAction;
import com.github.retrooper.packetevents.protocol.world.BlockFace;
@CheckData(name = "PositionBreakB", experimental = true)
@CheckData(name = "PositionBreakB")
public class PositionBreakB extends Check implements BlockBreakCheck {
private final int releaseFace = player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_8) ? 0 : 255;
private BlockFace lastFace;
@@ -13,7 +13,6 @@ 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.DiggingAction;
import com.github.retrooper.packetevents.protocol.player.GameMode;
import com.github.retrooper.packetevents.protocol.world.BlockFace;
import com.github.retrooper.packetevents.util.Vector3d;
import com.github.retrooper.packetevents.util.Vector3f;
@@ -34,7 +33,7 @@ public class RotationBreak extends Check implements BlockBreakCheck {
@Override
public void onBlockBreak(BlockBreak blockBreak) {
if (player.gamemode == GameMode.SPECTATOR)
if (!player.cameraEntity.isSelf())
return; // you don't send flying packets when spectating entities
if (player.inVehicle()) return; // falses
if (blockBreak.action == DiggingAction.CANCELLED_DIGGING) return; // falses
@@ -50,7 +49,7 @@ public class RotationBreak extends Check implements BlockBreakCheck {
@Override
public void onPostFlyingBlockBreak(BlockBreak blockBreak) {
if (player.gamemode == GameMode.SPECTATOR)
if (!player.cameraEntity.isSelf())
return; // you don't send flying packets when spectating entities
if (player.inVehicle()) return; // falses
if (blockBreak.action == DiggingAction.CANCELLED_DIGGING) return; // falses
@@ -92,18 +91,18 @@ public class RotationBreak extends Check implements BlockBreakCheck {
// End checking if the player is in the block
List<Vector3f> possibleLookDirs = new ArrayList<>(Arrays.asList(
new Vector3f(player.lastXRot, player.yRot, 0),
new Vector3f(player.xRot, player.yRot, 0)
new Vector3f(player.lastYaw, player.pitch, 0),
new Vector3f(player.yaw, player.pitch, 0)
));
// 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));
possibleLookDirs.add(new Vector3f(player.lastYaw, player.lastPitch, 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));
possibleLookDirs = Collections.singletonList(new Vector3f(player.yaw, player.pitch, 0));
}
final double distance = player.compensatedEntities.self.getAttributeValue(Attributes.BLOCK_INTERACTION_RANGE);
@@ -0,0 +1,33 @@
package ac.grim.grimac.checks.impl.chat;
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.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.wrapper.play.client.WrapperPlayClientTabComplete;
@CheckData(name = "ChatA", experimental = true)
public class ChatA extends Check implements PacketCheck {
public ChatA(GrimPlayer player) {
super(player);
}
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (event.getPacketType() == PacketType.Play.Client.TAB_COMPLETE && PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_13)) {
WrapperPlayClientTabComplete wrapper = new WrapperPlayClientTabComplete(event);
String text = wrapper.getText();
if (text.equals("/") || text.trim().isEmpty()) {
if (flagAndAlert("")) {
event.setCancelled(true);
player.onPacketCancel();
}
}
}
}
}
@@ -1,4 +1,4 @@
package ac.grim.grimac.checks.impl.exploit;
package ac.grim.grimac.checks.impl.chat;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
@@ -14,21 +14,18 @@ import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientCh
// this can false from click events, but I doubt this would actually
// happen unless they're trying to flag, or if the server is set up badly
@CheckData(name = "ExploitC", description = "Invalid chat message")
public class ExploitC extends Check implements PacketCheck {
public ExploitC(GrimPlayer playerData) {
super(playerData);
@CheckData(name = "ChatB", description = "Invalid chat message")
public class ChatB extends Check implements PacketCheck {
public ChatB(GrimPlayer player) {
super(player);
}
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (event.getPacketType() == PacketType.Play.Client.CHAT_MESSAGE) {
String message = new WrapperPlayClientChatMessage(event).getMessage();
if (message.isEmpty() || !message.trim().equals(message) || message.startsWith("/") && PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_19)) {
if (flagAndAlert("message=" + message)) {
event.setCancelled(true);
player.onPacketCancel();
}
if (checkChatMessage(message)) {
event.setCancelled(true);
}
}
@@ -43,6 +40,7 @@ public class ExploitC extends Check implements PacketCheck {
}
if (event.getPacketType() == PacketType.Play.Client.CHAT_COMMAND) {
// TODO make previa after making wrapper parse by client version instead of server version
String command = "/" + new WrapperPlayClientChatCommand(event).getCommand();
if (!command.trim().equals(command)) {
if (flagAndAlert("command=" + command)) {
@@ -52,4 +50,15 @@ public class ExploitC extends Check implements PacketCheck {
}
}
}
// returns whether the packet should be cancelled
public boolean checkChatMessage(String message) {
if (message.isEmpty() || !message.trim().equals(message) || message.startsWith("/") && PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_19)) {
if (flagAndAlert("message=" + message) && shouldModifyPackets()) {
player.onPacketCancel();
return true;
}
}
return false;
}
}
Loaded 100 of 612 files, more files were not shown because too many files have changed in this diff. Show more