Compare commits

...
Author SHA1 Message Date
Axionize cf115fd9c5 Disable requirements processor 2025-03-25 03:46:17 -04:00
Axionize 999f083363 I hate this 2025-03-25 03:24:34 -04:00
Axionize 96657b399f Fix build issues with downloading Conditional Mixin library for PE Fabric 2025-03-25 03:24:18 -04:00
Axionize 2cdca3ed72 Bump RAM allocation to Gradle in root gradle.properties 2025-03-24 14:35:25 -04:00
Axionize 196d4f592b Minor Build optimization 2025-03-24 08:48:19 -04:00
Axionize 588ba8def2 New API refactor with getEventBus() 2025-03-24 01:19:45 -04:00
Axionize 55cf328063 Only include fabricAPI modules we use, reuse dependency code for multiple projects 2025-03-23 08:10:26 -04:00
Axionize ddab7562fd Add support for Spigot 1.17 - 1.18.2 itemUsageReset()
- Also decrease allocated via gradle.properties
- Forces rebuild with new PE which will fix Geyser detection
2025-03-23 07:51:12 -04:00
Axionize 804e34f20d Make Grim rely on JiJed and loaded PE API; Transition PacketPlayerDigging to be cross-platform from upstream 2025-03-23 03:38:38 -04:00
Axionize fa65721116 Translation parity (and more/better) with pre-platform-independence port 2025-03-23 01:50:51 -04:00
Axionize 0c81517639 Fix updating native ServerPlayerEntity reference; Refactor @NonNull annotations 2025-03-23 01:50:50 -04:00
Axionize b71c44c2e1 Initable refactor; support multiple types of initables as one object; shutdown fabric bstats 2025-03-23 01:49:33 -04:00
Axionize d7437bce97 Fix Geyser-Fabric compatability 2025-03-23 01:49:33 -04:00
Axionize 888f209994 Build against slim configuralize for fabric support 2025-03-23 01:49:33 -04:00
Axionize f6397c978b Bump snakeyaml version for Fabric support 2025-03-23 01:49:33 -04:00
Axionize d612a522cc Fix library relocation typo 2025-03-23 01:49:33 -04:00
Axionize 661010805f Fix build issues with relocation on bukkit; force rebuild for fabric registry fix with PE 2025-03-23 01:49:33 -04:00
Axionize 6848c03e47 Code cleanup to address code review concerns 2025-03-23 01:49:33 -04:00
Axionize 0b168e2d60 Fix Build error from rebasing https://github.com/GrimAnticheat/Grim/commit/aab710dd8b55bc6fbe88f0f4c79aa214527a20dd 2025-03-23 01:49:33 -04:00
Axionize a1d0a6a73d Fix rare preconditions error with checking for null platformPlayer 2025-03-23 01:49:33 -04:00
Axionize a88c506518 Cleanup Fabric buildscript 2025-03-23 01:49:33 -04:00
Axionize 04f4bd7d21 Hopefully Fix inventory errors 2025-03-23 01:49:33 -04:00
Axionize cae28699a3 Fix running fabric in IDE and dev mode
Credit: @Bawnorton
2025-03-23 01:49:33 -04:00
Axionize ee689d1503 Fix 1.8 support using non-existent inventory method 2025-03-23 01:49:33 -04:00
Axionize 695e1ad5a3 Bump Fabric loom version; trigger rebuild on updated PE fork with Fabric fixes 2025-03-23 01:49:33 -04:00
Axionize bc766b51dd Minor code cleanup 2025-03-23 01:49:33 -04:00
Axionize fb1f3cda72 Code cleanup 2025-03-23 01:49:33 -04:00
Axionize 554d6d62db Configure builds to use shared lib versions files; push for rebuild with updated PE; (Working 1.20.1) 2025-03-23 01:49:33 -04:00
Axionize b9f71bce4e Fix project build issues and fix 1.20.1 support 2025-03-23 01:49:31 -04:00
Axionize f4f9092bd7 Fix Legacy (< 1.16.5) Big DripLeaf CollisionBox modeling 2025-03-23 01:49:19 -04:00
Axionize ff461cf041 Actuallly commit 1.20.4 support 2025-03-23 01:49:18 -04:00
Axionize 9b5264f0f3 Add 1.20.1 support (once PE is fixed) 2025-03-23 01:49:18 -04:00
Axionize 5cb5ce634d Refactor Fabric to support 1.21.4, 1.21.1, and (soon) 1.16.1 2025-03-23 01:49:18 -04:00
Axionize 0403565830 Fix endTick on Folia 2025-03-23 01:49:18 -04:00
Axionize 4d0a3696c9 PE datatype refactor; Fix Folia support 2025-03-23 01:49:18 -04:00
Axionize f1440bad93 Moderate refactoring and codebase cleanup
Fixed GitHub actions

Note that Fabric platform is missing:
- Pistons
- Providing an instance of GrimExternalAPI for other plugins/mods to get
- and more
2025-03-23 01:49:17 -04:00
Axionize a92e1e3892 Fix scheduler hierachy issue 2025-03-23 01:44:38 -04:00
Axionize c60355536e Temporarily build against my fork to fix PacketEvent's broken Fabric support 2025-03-23 01:44:38 -04:00
Axionize 2aeafe14cc Build against new Grim API instead of locally 2025-03-23 01:44:38 -04:00
Axionize e5d011b681 Fix Actions workflow 2025-03-23 01:43:34 -04:00
Axionize 5b59392817 Finish Fabric buildscript; now builds runnable jar 2025-03-23 01:43:34 -04:00
Axionize 656e73a07d Fix Fabric execution and shutdown; Initial Fabric build script 2025-03-23 01:43:34 -04:00
Axionize 7d9836ca50 Beta Fabric Support Complete + General Bug Fixes:
- Generate autocompletions for permissions
- Make OP, and enable-on-join permissions false by default even for ops

- Fixed usage of isWorldLoaded() in pre-migration codebase from breaking 1.8 support
- Numerous command loader bugs fixed
- A lot more
2025-03-23 01:43:34 -04:00
Axionize 7e91c739b2 Refactor resyncPosition() internally; may move to platformPlayer in future 2025-03-23 01:43:34 -04:00
Axionize 9aaf8b9491 Platform loading fixes 2025-03-23 01:43:34 -04:00
Axionize 3869a2077d Fabric Support MVP
- Registering permissions likely incomplete
- Building jar incomplete; only works in Debug mode
- Exempting on reload not done
- Plugin Channels not implemented
2025-03-23 01:43:34 -04:00
Axionize 525c380560 Complete multi-module project setup 2025-03-23 01:43:34 -04:00
Axionize ca7326cbd7 Multi-module project init setup 2025-03-23 01:43:32 -04:00
Axionize e9f2d29fd5 Platform Independence Achieved! 2025-03-23 01:43:26 -04:00
Axionize dfdab62763 Refactor Part 3:
Checkpoint
Need to implement:
- Fix PlaceHolderAPI and ManInMyVan's MessageUtil logic error
- Add BStats back
- Add platform independent Permissions
- Make TickEndEvent handling platform independent

Need to Test:
- ProxyAlertMessenger
- Commands
2025-03-23 01:43:26 -04:00
Axionize e339626ce1 Fix plugin compilation and exeution of commands 2025-03-23 01:43:26 -04:00
Axionize b0df122cb9 Almost there; just events and bukkit extensions left 2025-03-23 01:43:26 -04:00
Axionize ae284e2886 Stash code 2025-03-23 01:43:26 -04:00
Axionize 586f4bde69 Refactor Part 2:
- Replace Vectors
- Replace ChatColor.translateColorCodes()
2025-03-23 01:43:25 -04:00
Axionize c3d1b14dc1 Seperated/Abstracted:
- Thread Management
- GrimEntity/PlatformPlayer + PlatformInventory
- PlatformWorld, PlatformChunk, Location
- Removed many bukkit dependencies
- Migrated Bukkit Events to own code section

- Disabled BStats temporarily
2025-03-23 01:43:24 -04:00
Axionize b71c11882a Fix rare AirLiquidBreak NPE 2025-03-23 00:52:38 -04:00
ManInMyVan 990efdd6d2 fix a noslow 2025-03-22 15:31:21 -06:00
ManInMyVan 3c89193fff rename mitigate-autoblock and mitigate-desync-noslow 2025-03-22 15:25:49 -06:00
SamB440 aff9dc3c25 Merge pull request #2030 from Dream23322/patch-1
Update README.md
2025-03-22 18:43:15 +00:00
ManInMyVan 6b17d5fd0e fix spectate command 2025-03-19 16:06:07 -06:00
SamB440 319dca27ae Use single runnable for multi block changes 2025-03-19 14:24:27 +00:00
SamB440 5d20372645 Fix 1.21.2+ item cooldown
How to make simple feature more complex?
2025-03-14 18:26:39 +00:00
ManInMyVan 74108a2088 fix #1962 2025-03-14 11:56:23 -06:00
Cyramek aab710dd8b Fix Block Collisions [1.21.2+] (#2015) 2025-03-11 08:51:52 -04:00
SamB440 1fb1a73060 Fix teleport check 2025-03-02 20:37:41 +00:00
ManInMyVan 6811bc71fc Merge pull request #2033 from 483378/2.0
AimModulo360: fix horse false flag <= 1.13
2025-03-02 11:18:14 -07:00
SamB440 6205a23edd Merge pull request #2034 from 483378/Add-Hitboxes-to-punishments-config
Add Hitboxes to punishments config
2025-03-02 13:33:44 +00:00
483378 2f844bc56e Add Hitboxes to punishments config 2025-03-02 11:59:17 +01:00
483378 5ca4082675 AimModulo360: fix horse false flag <= 1.13 2025-03-02 10:59:51 +01:00
ManInMyVan 078bc139f7 revert changes from 68a04d05 and 45da02e7
it could be bypassed
2025-03-01 12:49:11 -07:00
Me. 37d07d3b03 Update README.md 2025-03-01 11:01:31 +10:00
SamB440 5bccfe831d Fix not reencoding stupidity packet 2025-02-28 14:31:41 +00:00
ManInMyVan 3fbaa0a0e6 why does OnlinePlayer exist 2025-02-23 10:42:20 -07:00
SamB440 931d26569c Don't need to throw 2025-02-23 12:42:05 +00:00
SamB440 b207fe233e Add option to debug packet cancelling 2025-02-22 20:40:23 +00:00
SamB440 0cc0910455 Merge pull request #2019 from Cyramek/water-push-fix
fix water flow push [1.21.4]
2025-02-22 19:25:09 +00:00
cyramek b5acee4911 fix water flow push 2025-02-22 20:04:57 +01:00
ManInMyVan ccada109b1 include "ms" in fastbreak verbose 2025-02-21 17:30:59 -07:00
ManInMyVan 16c0d37b54 include block type in fastbreak verbose 2025-02-21 17:30:16 -07:00
ManInMyVan f6c16ea567 make fastbreak verbose case consistent 2025-02-21 17:29:33 -07:00
ManInMyVan b9934f5130 same with tridents and debug sticks 2025-02-21 17:18:05 -07:00
ManInMyVan 3c48b6edab creative mode players cannot mine with a mace 2025-02-21 17:10:14 -07:00
ManInMyVan fdf8bc26a5 very minor changes 2025-02-21 16:07:00 -07:00
AoElite 900cbed5fa update api 2025-02-19 07:06:28 -05:00
SamB440 233cd2a7d7 Fix TAB server version check 2025-02-17 21:00:46 +00:00
AoElite 0c8b60018a build with Java 21 2025-02-14 03:14:54 -05:00
AoElite a54787152d bump deepsource runtime version to 17 2025-02-14 03:11:19 -05:00
AoElite 2932f0a40c handle end tick support for folia better & try to use paper's end tick event if reflection fails 2025-02-14 03:08:37 -05:00
AoElite 94d47391af correct wrong method name 2025-02-14 02:55:08 -05:00
AoElite 9964478ce1 added a better way to log exceptions 2025-02-14 02:33:38 -05:00
AoElite d3667e1add use teleportAsync when possible, this also should fix the spectating command not working on folia 2025-02-14 02:28:17 -05:00
ManInMyVan 04e1459d9e Merge pull request #2006 from ManInMyVan/fix/item-reset-exceptions
fix exceptions caused by resetting item usage async
2025-02-13 12:09:15 -07:00
ManInMyVan e99855bdc5 inline the class field 2025-02-12 15:54:59 -07:00
ManInMyVan 028a526c51 fix exceptions caused by resetting item usage async 2025-02-12 12:10:22 -07:00
SamB440 9650cdb4f2 Entity pushing fixes and performance improvement 2025-02-12 16:03:24 +00:00
GigaZelensky 5326845c7a Add Custom Setback Violation Threshold (#2001)
* Add Custom Setback Violation Threshold

This change introduces a configurable threshold for setbacks, allowing more flexibility in how violations are handled. It can be useful in multiple scenarios, such as integrating Grim with other anti-cheat systems like Vulcan. By delaying setbacks, more obvious cheats (e.g., flying) can be detected and handled by other systems first, preventing unnecessary interference that might limit their effectiveness.

Additionally, this feature helps reduce the impact of small false positives or minor violations on legitimate players, ensuring that occasional detection inaccuracies don’t result in unnecessary movement corrections.

The option is set to 1 by default to maintain the existing behavior, but it can be adjusted in the config for more customized handling.

* Update en.yml

* Update config to match custom Setback Violation Threshold

* Update config to match custom Setback Violation Threshold

* Update config to match custom Setback Violation Threshold

* Update config to match custom Setback Violation Threshold

* Update config to match custom Setback Violation Threshold

* Update config to match custom Setback Violation Threshold

* Update ja.yml

* Update config to match custom Setback Violation Threshold

* Update config to match custom Setback Violation Threshold

* Update tr.yml

* Update config to match custom Setback Violation Threshold

* Update config to match custom Setback Violation Threshold

* Update config to match custom Setback Violation Threshold
2025-02-12 15:09:01 +00:00
Axionize 7646df0563 Fix a rare CME in LatencyUtils (#1984) 2025-02-08 12:53:02 -05:00
Axionize 23369cf1d6 Fix modeling teleport packets for entities on clients < 1.16.2 (#1985)
* Fix modeling teleport packets for entities on clients < 1.16.2
* Fix new entity modeling to handle riding entities
* Fix merge issues from backporting
2025-02-08 12:52:24 -05:00
ManInMyVan 864e9de3e8 fix #1948 2025-02-05 16:57:46 -07:00
ManInMyVan 68b4fa7805 fix SprintE false 2025-02-05 16:43:38 -07:00
ManInMyVan c7e1fda7bb correct checking water for in sprint checks 2025-02-05 16:20:25 -07:00
ManInMyVan 6c42bdcd4e MC152728 was unfixed 2025-02-05 15:31:29 -07:00
Ty_Regotly 439cbc6db3 Correct some translation errors, and update the time (#1996)
* Update de.yml

* Update en.yml

* Update es.yml

* Update fr.yml

* Update it.yml

* Update ja.yml

* Update nl.yml

* Update pt.yml

* Update ru.yml

* Update tr.yml

* Update zh.yml
2025-02-05 11:35:21 +00:00
ManInMyVan 652b66202d Merge pull request #1997 from TrollsterCooleg/2.0
Make the placeholders persist
2025-02-04 21:03:11 -07:00
TrollsterCooleg 9f031e638a Make the placeholders persist 2025-02-04 21:54:30 -06:00
ManInMyVan 45da02e7c0 check slot 2025-02-04 20:21:12 -07:00
ManInMyVan 68a04d05f8 hopefuly fix errors with mitigate-desync-noslow 2025-02-04 17:29:08 -07:00
ManInMyVan e329171b11 add check-for-updates to all config files 2025-02-03 15:15:46 -07:00
SamB440 b8121543ee Add setting for checking for updates on startup (implements #1995) 2025-02-03 19:42:49 +00:00
SamB440 70cf56fad6 Stop paper taking 10 years to remap the plugin 2025-02-03 19:33:20 +00:00
SamB440 f88a114e0e Bump version 2025-02-01 15:17:30 +00:00
SamB440 ff888f8c73 Kick players if a transaction error occurs 2025-01-31 20:28:14 +00:00
SamB440 66a250eb9e Maybe fix flint and steel prediction (fixes #1991) 2025-01-31 20:06:57 +00:00
SamB440 3c56ba9880 Support creaking hitbox (fixes #1986) 2025-01-31 19:08:43 +00:00
SamB440 514a48e508 Update Gradle wrapper 2025-01-31 19:05:53 +00:00
SamB440 32b66cb4ff Fix checking for elytra on servers older than 1.21.2 2025-01-31 19:01:59 +00:00
SamB440 057de4d4e2 Update shadow plugin 2025-01-31 18:48:31 +00:00
ManInMyVan 081e2d3b2b fix resin hitbox
closes #1982
2025-01-30 19:17:28 -07:00
SamB440 856a51e574 Bump version 2025-01-30 21:35:29 +00:00
SamB440 a4786a16c0 Exempt TRIAL_SPAWNER on fastbreak (#1962) as temp-fix 2025-01-30 21:22:43 +00:00
SamB440 c904f14c7b Use Component instead of String 2025-01-30 21:19:54 +00:00
ManInMyVan 896cf12b71 patch civ 2025-01-29 16:02:04 -07:00
ManInMyVan 1506c97ffc fix comment 2025-01-29 07:56:20 -07:00
ManInMyVan 9107586fc1 assume cooldown is full on 1.8 servers 2025-01-28 19:44:24 -07:00
ManInMyVan bc96460eb3 RotationBreak
closes #1196
2025-01-28 17:12:53 -07:00
ManInMyVan 94705fcd09 why are we ticking velocity on duplicates? 2025-01-28 12:47:27 -07:00
Axionize 7ae935f5e3 Revert "More Accurate Position Reach Modeling for MC clients < 1.16.2 (#1964)" (#1980)
This reverts commit 6d06b9b559.
2025-01-28 05:55:36 -05:00
Axionize 6d06b9b559 More Accurate Position Reach Modeling for MC clients < 1.16.2 (#1964)
* Stop using server-side tracked position when attempting to account for teleports not always moving entity client side; expand from client area instead

* Seperate getting possible locations and possible hitbox to reduce how often expandNonRelative() needs to be called

* Add documention explaining getPossibleLocationCombined() and getPossibleHitboxCombined()

* Update documentation formatting
2025-01-28 05:47:59 -05:00
SamB440 bae421ff27 Merge pull request #1976 from Packsolite/project/add-editorconfig
Add .editorconfig
2025-01-27 18:38:46 +00:00
ManInMyVan f81743e07f Merge pull request #1975 from Packsolite/config/ignore-laby-mod-brand
Ignore labymod client brand by default
2025-01-27 09:17:54 -07:00
Packsolite b0e4693f2b Add .editorconfig 2025-01-27 16:55:54 +01:00
Packsolite e569483f63 Ignore labymod client brand 2025-01-27 16:18:18 +01:00
ManInMyVan 164d16dce9 fix warns from TrackerData 2025-01-26 15:50:56 -07:00
SamB440 a504b8f595 Fix viaversion respawn metadata handling (fixes #1965) 2025-01-26 19:45:22 +00:00
ManInMyVan 0a9ea97aa6 cleanup 2025-01-25 14:34:54 -07:00
ManInMyVan 91e281514b remove random newline 2025-01-25 14:22:45 -07:00
ManInMyVan a03c8bbdbd support placeholderapi placeholders in custom kick messages 2025-01-25 14:21:23 -07:00
ManInMyVan 22074dc228 revert renaming compensatedWorld and compensatedEntities 2025-01-25 14:17:15 -07:00
ManInMyVan 3a40ba8b29 "small" refactor 2025-01-25 12:34:22 -07:00
ManInMyVan 0d84b4e958 patch drop noslow 2025-01-24 19:16:06 -07:00
ManInMyVan 098399f754 don't allow negative kb on 1.9+ 2025-01-24 19:12:30 -07:00
ManInMyVan 334a356446 some keepsprint patch fixes 2025-01-24 17:59:05 -07:00
SamB440 acac69e750 Account for new 1.21.2 collision requirement 2025-01-24 19:53:13 +00:00
SamB440 f8aa54855d Update compatibility notice 2025-01-24 19:48:22 +00:00
ManInMyVan 7d928d581f patch keepsprint 2025-01-23 17:59:46 -07:00
SamB440 31394f472f Fix listening to brands on modern clients 2025-01-23 16:07:24 +00:00
SamB440 f721b1850c Fix the plugin not loading lol 2025-01-23 16:02:56 +00:00
ManInMyVan 4419646f11 customizable disconnect messages 2025-01-22 19:50:40 -07:00
Axionize 40bbd2eb2e Stop using server-side tracked position when attempting to account for teleports not always moving entity client side; expand from client area instead (#1902) 2025-01-22 01:27:16 -05:00
Axionize 6cfe71da9b Refactor HitboxData.getBlockHitbox() to include isTargetBlock as parameter (#1957)
Also fixes a rare NPE in LOSP
2025-01-22 00:45:27 -05:00
ManInMyVan 393357d9f6 revert recent BadPacketsN changes 2025-01-20 17:26:20 -07:00
ManInMyVan dcf0c73005 refactoring 2025-01-20 15:57:23 -07:00
ManInMyVan 856ec6c655 remove DebugHandler check name
I don't this is used
2025-01-20 15:42:08 -07:00
ManInMyVan 54a10f77db remove (Prediction) from NoSlow 2025-01-20 15:41:28 -07:00
ManInMyVan 0f50bde4db Merge pull request #1959 from spring-dependency-management/explosion-fix
Fix explosion false when dead
2025-01-20 15:25:33 -07:00
spring-dependency-management d7368ceba6 Fix explosion false when dead 2025-01-20 22:24:02 +00:00
ManInMyVan 1e8fc587af always poll 2025-01-20 12:27:52 -07:00
625 changed files with 17410 additions and 7084 deletions

No files matched your search

+1 -1
View File
@@ -5,4 +5,4 @@ name = "java"
enabled = true
[analyzers.meta]
runtime_version = "16"
runtime_version = "17"
+17
View File
@@ -0,0 +1,17 @@
root = true
[*]
charset = utf-8
indent_size = 4
indent_style = space
insert_final_newline = true
max_line_length = 100
tab_width = 4
# intellij specific configuration
ij_continuation_indent_size = 8
ij_formatter_off_tag = @formatter:off
ij_formatter_on_tag = @formatter:on
ij_formatter_tags_enabled = false
ij_smart_tabs = false
ij_wrap_on_typing = false
+36 -4
View File
@@ -20,19 +20,51 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Set up JDK 17
- name: Set up JDK 21
uses: actions/setup-java@v4
with:
java-version: '17'
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
# Temporary tasks so built jar works because PacketEvents won't merge my fixes for their broken fabric support
- name: Clone Axionize's PacketEvents Repository
run: |
git clone -b fix/fabric-events https://github.com/Axionize/packetevents.git packetevents
cd packetevents
echo "Cloned PacketEvents repository"
- name: Build and Publish PacketEvents to Maven Local
uses: gradle/actions/setup-gradle@v3
with:
build-root-directory: packetevents
arguments: publishToMavenLocal
- name: Extract Version from build.gradle.kts
id: extract-version
run: |
# Extract the version from build.gradle.kts using grep and sed
VERSION=$(grep "^version\s*=" build.gradle.kts | sed 's/version\s*=\s*"\(.*\)"/\1/')
echo "VERSION: $VERSION"
echo "VERSION=$VERSION" >> $GITHUB_ENV
echo "version=$VERSION" >> $GITHUB_OUTPUT
- name: Build with Gradle
uses: gradle/actions/setup-gradle@v3
with:
arguments: build
- name: Upload a Build Artifact
- name: Upload Bukkit Artifact
uses: actions/upload-artifact@v4
with:
path: /home/runner/work/Grim/Grim/build/libs/**.jar
name: grimac-bukkit
path: ${{ github.workspace }}/bukkit/build/libs/grimac-bukkit-${{ env.VERSION }}.jar
- name: Upload Fabric Artifact
uses: actions/upload-artifact@v4
with:
name: grimac-fabric
path: ${{ github.workspace }}/fabric/build/libs/grimac-fabric-${{ env.VERSION }}.jar
+7 -1
View File
@@ -65,4 +65,10 @@ nb-configuration.xml
##############################
## OS X
##############################
.DS_Store
.DS_Store
##############################
## Fabric
##############################
run/
fabric/run/
+2 -2
View File
@@ -41,8 +41,8 @@ What makes Grim stand out against other anticheats?
### Movement Simulation Engine
* We have a 1:1 replication of the player's possible movements
* This covers everything from basic walking, swimming, knockback, cobwebs, to bubble columns
* It even covers riding entities from boats to pigs to striders
* 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.
* The order of collisions depends on the client version and is correct
+9 -197
View File
@@ -1,206 +1,18 @@
import net.minecrell.pluginyml.bukkit.BukkitPluginDescription.Permission
plugins {
id("java")
id("maven-publish")
id("com.github.johnrengelman.shadow") version "8.1.1"
id("io.freefair.lombok") version "8.6"
id("net.minecrell.plugin-yml.bukkit") version "0.6.0"
id("com.diffplug.spotless") version "6.25.0"
}
spotless {
java {
endWithNewline()
indentWithSpaces(4)
removeUnusedImports()
trimTrailingWhitespace()
targetExclude("build/generated/**/*")
}
kotlinGradle {
endWithNewline()
indentWithSpaces(4)
trimTrailingWhitespace()
}
}
group = "ac.grim.grimac"
version = "2.3.69"
version = "2.3.71"
description = "Libre simulation anticheat designed for 1.21 with 1.8-1.21 support, powered by PacketEvents 2.0."
java {
toolchain.languageVersion.set(JavaLanguageVersion.of(17))
}
// Set to false for debug builds
// You cannot live reload classes if the jar relocates dependencies
// Checks Project properties -> environment variable -> defaults true
val relocate: Boolean = project.findProperty("relocate")?.toString()?.toBoolean()
extra["relocate"] = project.findProperty("relocate")?.toString()?.toBoolean()
?: System.getenv("RELOCATE_JAR")?.toBoolean()
?: true
?: true
repositories {
mavenLocal()
maven("https://hub.spigotmc.org/nexus/content/repositories/snapshots/") // Spigot
maven("https://jitpack.io/") { // Grim API
content {
includeGroup("com.github.grimanticheat")
}
}
maven("https://repo.viaversion.com") // ViaVersion
maven("https://repo.aikar.co/content/groups/aikar/") // ACF
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://repo.codemc.io/repository/maven-releases/") // PacketEvents
maven("https://repo.codemc.io/repository/maven-snapshots/")
maven("https://s01.oss.sonatype.org/content/repositories/snapshots/")
maven("https://repo.extendedclip.com/content/repositories/placeholderapi/") // placeholderapi
mavenCentral()
// FastUtil, Discord-Webhooks
}
dependencies {
implementation("com.github.retrooper:packetevents-spigot:2.7.1-SNAPSHOT")
implementation("co.aikar:acf-paper:0.5.1-SNAPSHOT")
implementation("club.minnced:discord-webhooks:0.8.0") // Newer versions include kotlin-stdlib, which leads to incompatibility with plugins that use Kotlin
implementation("it.unimi.dsi:fastutil:8.5.15")
implementation("github.scarsz:configuralize:1.4.0")
// Used for local testing:
//implementation("ac.grim.grimac:GrimAPI:1.0")
implementation("com.github.grimanticheat:grimapi:ed1ec7b4a1")
implementation("net.kyori:adventure-text-minimessage:4.17.0")
implementation("net.kyori:adventure-platform-bukkit:4.3.4")
implementation("org.jetbrains:annotations:24.1.0")
compileOnly("org.geysermc.floodgate:api:2.0-SNAPSHOT")
compileOnly("io.papermc.paper:paper-api:1.18.2-R0.1-SNAPSHOT")
compileOnly("com.viaversion:viaversion-api:5.0.4-SNAPSHOT")
compileOnly("me.clip:placeholderapi:2.11.6")
compileOnly("io.netty:netty-all:4.1.85.Final")
}
bukkit {
name = "GrimAC"
author = "GrimAC"
main = "ac.grim.grimac.GrimAC"
website = "https://grim.ac/"
apiVersion = "1.13"
foliaSupported = true
softDepend = listOf(
"ProtocolLib",
"ProtocolSupport",
"Essentials",
"ViaVersion",
"ViaBackwards",
"ViaRewind",
"Geyser-Spigot",
"floodgate",
"FastLogin",
"PlaceholderAPI",
)
permissions {
register("grim.alerts") {
description = "Receive alerts for violations"
default = Permission.Default.OP
}
register("grim.alerts.enable-on-join") {
description = "Enable alerts on join"
default = Permission.Default.OP
}
register("grim.performance") {
description = "Check performance metrics"
default = Permission.Default.OP
}
register("grim.profile") {
description = "Check user profile"
default = Permission.Default.OP
}
register("grim.brand") {
description = "Show client brands on join"
default = Permission.Default.OP
}
register("grim.brand.enable-on-join") {
description = "Enable showing client brands on join"
default = Permission.Default.OP
}
register("grim.sendalert") {
description = "Send cheater alert"
default = Permission.Default.OP
}
register("grim.nosetback") {
description = "Disable setback"
default = Permission.Default.FALSE
}
register("grim.nomodifypacket") {
description = "Disable modifying packets"
default = Permission.Default.FALSE
}
register("grim.exempt") {
description = "Exempt from all checks"
default = Permission.Default.FALSE
}
register("grim.verbose") {
description = "Receive verbose alerts for violations. Requires grim.alerts"
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"
default = Permission.Default.FALSE
}
}
}
tasks.build {
dependsOn(tasks.shadowJar)
dependsOn(tasks.spotlessApply)
}
tasks.withType<JavaCompile> {
options.encoding = "UTF-8"
}
publishing.publications.create<MavenPublication>("maven") {
artifact(tasks["shadowJar"])
}
tasks.shadowJar {
minimize()
archiveFileName.set("${project.name}-${project.version}.jar")
if (relocate) {
relocate("io.github.retrooper.packetevents", "ac.grim.grimac.shaded.io.github.retrooper.packetevents")
relocate("com.github.retrooper.packetevents", "ac.grim.grimac.shaded.com.github.retrooper.packetevents")
relocate("co.aikar.commands", "ac.grim.grimac.shaded.acf")
relocate("co.aikar.locale", "ac.grim.grimac.shaded.locale")
relocate("club.minnced", "ac.grim.grimac.shaded.discord-webhooks")
relocate("github.scarsz.configuralize", "ac.grim.grimac.shaded.configuralize")
relocate("com.github.puregero", "ac.grim.grimac.shaded.com.github.puregero")
relocate("com.google.code.gson", "ac.grim.grimac.shaded.gson")
relocate("alexh", "ac.grim.grimac.shaded.maps")
relocate("it.unimi.dsi.fastutil", "ac.grim.grimac.shaded.fastutil")
relocate("net.kyori", "ac.grim.grimac.shaded.kyori")
relocate("okhttp3", "ac.grim.grimac.shaded.okhttp3")
relocate("okio", "ac.grim.grimac.shaded.okio")
relocate("org.yaml.snakeyaml", "ac.grim.grimac.shaded.snakeyaml")
relocate("org.json", "ac.grim.grimac.shaded.json")
relocate("org.intellij", "ac.grim.grimac.shaded.intellij")
relocate("org.jetbrains", "ac.grim.grimac.shaded.jetbrains")
}
// Build Optimization:
// - Add parallel build support
// - Add build caching
tasks.withType<JavaCompile>().configureEach {
options.isFork = true
options.isIncremental = true
}
+16
View File
@@ -0,0 +1,16 @@
plugins {
`kotlin-dsl`
kotlin("plugin.serialization") version "1.9.0" // Use the same Kotlin version as your main project
}
repositories {
mavenCentral()
gradlePluginPortal()
}
dependencies {
implementation(libs.spotless)
implementation(libs.lombok)
implementation(libs.shadow)
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.0")
}
+7
View File
@@ -0,0 +1,7 @@
dependencyResolutionManagement {
versionCatalogs {
create("libs") {
from(files("../libs.versions.toml"))
}
}
}
@@ -0,0 +1,72 @@
plugins {
`java-library`
id("io.freefair.lombok")
id("com.diffplug.spotless")
}
group = rootProject.group
version = rootProject.version
description = rootProject.description
// Java compilation settings
java {
toolchain.languageVersion.set(JavaLanguageVersion.of(21))
disableAutoTargetJvm()
withSourcesJar()
withJavadocJar()
}
tasks.withType<JavaCompile>().configureEach {
options.encoding = "UTF-8"
options.release.set(17)
}
// Lombok configuration
//lombok {
// version.set("1.18.30") // Use the version from your version catalog if available
//}
// Spotless configuration
spotless {
java {
endWithNewline()
indentWithSpaces(4)
removeUnusedImports()
trimTrailingWhitespace()
targetExclude("build/generated/**/*")
}
kotlinGradle {
endWithNewline()
indentWithSpaces(4)
trimTrailingWhitespace()
}
}
// Ensure spotlessApply runs before build
tasks.build {
dependsOn(tasks.named("spotlessApply"))
}
// Process resources (e.g., for plugin metadata files)
//tasks.processResources {
// inputs.property("version", project.version)
// filesMatching(listOf("plugin.yml", "bungee.yml", "velocity-plugin.json", "fabric.mod.json")) {
// expand("version" to project.version)
// }
//}
// Javadoc configuration
tasks.javadoc {
title = "${rootProject.name}-${project.name} v${rootProject.version}"
options.encoding = "UTF-8"
options.overview =
rootProject.file("buildSrc/src/main/resources/javadoc-overview.html").toString()
setDestinationDir(file("${project.layout.buildDirectory.asFile.get()}/docs/javadoc"))
options {
(this as CoreJavadocOptions).addBooleanOption("Xdoclint:none", true)
}
}
// Default tasks
defaultTasks("build")
@@ -0,0 +1,391 @@
import kotlinx.serialization.json.*
import java.util.jar.JarFile
import java.util.zip.ZipEntry
plugins {
`java-library`
}
val jijDependencies: Configuration by project.configurations.creating {
isCanBeResolved = true
isCanBeConsumed = false
extendsFrom(project.configurations.getByName("implementation"))
project.configurations.findByName("modImplementation")?.let { extendsFrom(it) }
}
data class DependencyIdentifier(
val group: String,
val name: String,
val version: String,
val classifier: String = "" // Add classifier field, default to empty string
) {
override fun toString() = "$group:$name:$version${if (classifier.isNotEmpty()) ":$classifier" else ""}"
}
// Does not support extracting classifiers but while the fabric standard is ambiguous on whether they can be included
// It does not appear dependency classifiers are in use by any mod I am aware of or can find
fun parseFabricModJson(jarFile: File, project: Project): Set<DependencyIdentifier> {
val nestedJars = mutableSetOf<DependencyIdentifier>()
try {
JarFile(jarFile).use { jar ->
val fabricModJsonEntry: ZipEntry? = jar.getEntry("fabric.mod.json")
if (fabricModJsonEntry != null) {
jar.getInputStream(fabricModJsonEntry).bufferedReader().use { reader ->
val jsonContent = reader.readText()
val jsonElement = Json.parseToJsonElement(jsonContent)
val jsonObject = jsonElement.jsonObject
val jarsArray = jsonObject["jars"]?.jsonArray ?: return@use
jarsArray.forEach { jarEntry: JsonElement ->
val filePath =
jarEntry.jsonObject["file"]?.jsonPrimitive?.content ?: return@forEach
project.logger.debug(
"Found nested JAR in fabric.mod.json: {} in {}",
filePath,
jarFile
)
val jarName = filePath.substringAfterLast("/")
val parts = jarName.removeSuffix(".jar").split("-")
if (parts.size >= 2) {
val inferredName = parts.dropLast(1).joinToString("-")
val inferredVersion = parts.last()
nestedJars.add(DependencyIdentifier("", inferredName, inferredVersion))
project.logger.debug("Inferred nested dependency from fabric.mod.json: :$inferredName:$inferredVersion")
}
}
}
}
}
} catch (e: Exception) {
project.logger.error("Failed to parse fabric.mod.json in $jarFile: ${e.message}")
}
return nestedJars
}
fun extractEmbeddedJars(jarFile: File, project: Project): Set<DependencyIdentifier> {
val embeddedJars = mutableSetOf<DependencyIdentifier>()
embeddedJars.addAll(parseFabricModJson(jarFile, project))
try {
JarFile(jarFile).use { jar ->
val entries = jar.entries()
while (entries.hasMoreElements()) {
val entry = entries.nextElement()
val entryName = entry.name
if (entryName.endsWith(".jar") && (entryName.startsWith("META-INF/jarjar/") || entryName.startsWith(
"libs/"
) || entryName.startsWith("META-INF/jars/"))
) {
project.logger.debug("Found embedded JAR: {} in {}", entryName, jarFile)
val tempFile = File.createTempFile("embedded-", ".jar")
tempFile.deleteOnExit()
jar.getInputStream(entry).use { input ->
tempFile.outputStream().use { output ->
input.copyTo(output)
}
}
JarFile(tempFile).use { embeddedJar ->
val manifest = embeddedJar.manifest
if (manifest != null) {
val group =
manifest.mainAttributes.getValue("Implementation-Vendor") ?: ""
val name = manifest.mainAttributes.getValue("Implementation-Title")
?: embeddedJar.name
val version =
manifest.mainAttributes.getValue("Implementation-Version") ?: ""
if (group.isNotEmpty() && name.isNotEmpty() && version.isNotEmpty()) {
embeddedJars.add(DependencyIdentifier(group, name, version))
project.logger.debug("Identified embedded dependency: $group:$name:$version")
} else {
val jarName = entryName.substringAfterLast("/")
val parts = jarName.removeSuffix(".jar").split("-")
if (parts.size >= 2) {
val inferredName = parts.dropLast(1).joinToString("-")
val inferredVersion = parts.last()
embeddedJars.add(
DependencyIdentifier(
"",
inferredName,
inferredVersion
)
)
project.logger.debug("Inferred embedded dependency from filename: :$inferredName:$inferredVersion")
}
}
}
}
}
}
}
} catch (e: Exception) {
project.logger.error("Failed to analyze JAR file $jarFile for embedded JARs: ${e.message}")
}
return embeddedJars
}
fun shouldExcludeDependency(
dependency: ResolvedDependency,
allEmbeddedDependencies: Set<DependencyIdentifier>,
actuallyIncludedDependencies: Set<DependencyIdentifier>,
project: Project
): Boolean {
val group = dependency.moduleGroup
val name = dependency.moduleName
val version = dependency.moduleVersion
// Extract classifier from resolved dependency (if available)
val classifier = dependency.moduleArtifacts.firstOrNull()?.classifier ?: ""
val depId = DependencyIdentifier(group, name, version, classifier)
if (group.startsWith("net.minecraft") ||
group.startsWith("net.fabricmc") ||
name == "minecraft" ||
name == "fabric-loader" ||
name == "fastutil" ||
name.startsWith("fabric_") ||
name.startsWith("fabric-api")
) {
project.logger.debug("Excluding platform dependency: {}", depId)
return true
}
if (name.endsWith("-bom") || name.contains("_bom")) {
project.logger.debug("Excluding BOM file: {}", depId)
return true
}
if (allEmbeddedDependencies.any { it.name == name && it.version == version && it.classifier == classifier }) {
project.logger.debug("Excluding dependency already embedded in another JAR: {}", depId)
return true
}
if (actuallyIncludedDependencies.any { it.name == name && it.version == version && it.classifier == classifier }) {
project.logger.debug("Excluding duplicate dependency: {}", depId)
return true
}
return false
}
fun isJijTarget(
dependency: ResolvedDependency,
allEmbeddedDependencies: Set<DependencyIdentifier>,
actuallyIncludedDependencies: Set<DependencyIdentifier>,
project: Project
): Boolean {
val classifier = dependency.moduleArtifacts.firstOrNull()?.classifier ?: ""
project.logger.debug(
"Checking JIJ target: {}",
DependencyIdentifier(
dependency.moduleGroup,
dependency.moduleName,
dependency.moduleVersion,
classifier
)
)
return !shouldExcludeDependency(
dependency,
allEmbeddedDependencies,
actuallyIncludedDependencies,
project
)
}
fun includeDependencyWithExclusions(
depId: DependencyIdentifier,
project: Project,
actuallyIncludedDependencies: MutableSet<DependencyIdentifier>,
allEmbeddedDependencies: Set<DependencyIdentifier>
) {
project.logger.debug("Including external dependency as JIJ with dynamic exclusions: {}", depId)
project.dependencies {
// Include dependency with classifier (if present)
"include"("${depId.group}:${depId.name}:${depId.version}${if (depId.classifier.isNotEmpty()) ":${depId.classifier}" else ""}") {
allEmbeddedDependencies.forEach { embeddedDep ->
if (embeddedDep.group.isNotEmpty()) {
exclude(group = embeddedDep.group, module = embeddedDep.name)
} else {
exclude(module = embeddedDep.name)
}
project.logger.debug("Excluding embedded transitive dependency: ${embeddedDep.name}:${embeddedDep.version}${if (embeddedDep.classifier.isNotEmpty()) ":${embeddedDep.classifier}" else ""}")
}
}
}
actuallyIncludedDependencies.add(depId)
}
fun processDependencies(
dependencies: Set<ResolvedDependency>,
processed: MutableSet<String>,
allEmbeddedDependencies: MutableMap<DependencyIdentifier, File>,
actuallyIncludedDependencies: MutableSet<DependencyIdentifier>,
project: Project
) {
dependencies.forEach { dep: ResolvedDependency ->
val classifier = dep.moduleArtifacts.firstOrNull()?.classifier ?: ""
val depKey = "${dep.moduleGroup}:${dep.moduleName}:${dep.moduleVersion}${if (classifier.isNotEmpty()) ":$classifier" else ""}"
val depId = DependencyIdentifier(dep.moduleGroup, dep.moduleName, dep.moduleVersion, classifier)
if (!processed.contains(depKey)) {
processed.add(depKey)
project.logger.debug("Resolved dependency: {}", depId)
val jarFile = dep.moduleArtifacts.firstOrNull()?.file
if (jarFile != null && jarFile.exists()) {
val embeddedJars = extractEmbeddedJars(jarFile, project)
embeddedJars.forEach { embeddedDep ->
allEmbeddedDependencies[embeddedDep] = jarFile
}
}
if (isJijTarget(
dep,
allEmbeddedDependencies.keys,
actuallyIncludedDependencies,
project
)
) {
project.logger.debug("Processing JIJ dependency: {}", depId)
val isProjectDependency = project.rootProject.allprojects.any { subproject ->
subproject.name == dep.moduleName && (subproject.group == dep.moduleGroup || dep.moduleGroup.isEmpty())
}
if (isProjectDependency) {
val projectPath = project.rootProject.allprojects.find { subproject ->
subproject.name == dep.moduleName && (subproject.group == dep.moduleGroup || dep.moduleGroup.isEmpty())
}?.path
?: throw IllegalStateException("Project dependency not found: ${dep.moduleName}")
project.logger.debug("Including project dependency as JIJ: $projectPath")
project.dependencies {
"include"(project.project(projectPath))
}
actuallyIncludedDependencies.add(depId)
} else {
includeDependencyWithExclusions(
depId,
project,
actuallyIncludedDependencies,
allEmbeddedDependencies.keys
)
}
}
processDependencies(
dep.children,
processed,
allEmbeddedDependencies,
actuallyIncludedDependencies,
project
)
}
}
}
project.afterEvaluate {
project.logger.debug("Resolving jijDependencies configuration")
val resolvedDependencies = jijDependencies.resolvedConfiguration.firstLevelModuleDependencies
val processed = mutableSetOf<String>()
val allEmbeddedDependencies = mutableMapOf<DependencyIdentifier, File>()
val actuallyIncludedDependencies = mutableSetOf<DependencyIdentifier>()
fun collectAllEmbeddedDependencies(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 ""}"
if (!processed.contains(depKey)) {
processed.add(depKey)
val jarFile = dep.moduleArtifacts.firstOrNull()?.file
if (jarFile != null && jarFile.exists()) {
val embeddedJars = extractEmbeddedJars(jarFile, project)
embeddedJars.forEach { embeddedDep ->
allEmbeddedDependencies[embeddedDep] = jarFile
project.logger.debug(
"Collected embedded dependency: {} from {}",
embeddedDep,
jarFile
)
}
}
collectAllEmbeddedDependencies(dep.children)
}
}
}
processed.clear()
collectAllEmbeddedDependencies(resolvedDependencies)
processed.clear()
processDependencies(
resolvedDependencies,
processed,
allEmbeddedDependencies,
actuallyIncludedDependencies,
project
)
}
tasks.withType<Jar>().configureEach {
doFirst {
project.logger.debug("Resolving JIJ dependencies for JAR inclusion")
val resolvedDependencies =
jijDependencies.resolvedConfiguration.firstLevelModuleDependencies
val processed = mutableSetOf<String>()
val allEmbeddedDependencies = mutableMapOf<DependencyIdentifier, File>()
val actuallyIncludedDependencies = mutableSetOf<DependencyIdentifier>()
fun collectAllEmbeddedDependencies(dependencies: Set<ResolvedDependency>) {
dependencies.forEach { dep ->
val depKey = "${dep.moduleGroup}:${dep.moduleName}:${dep.moduleVersion}"
if (!processed.contains(depKey)) {
processed.add(depKey)
val jarFile = dep.moduleArtifacts.firstOrNull()?.file
if (jarFile != null && jarFile.exists()) {
val embeddedJars = extractEmbeddedJars(jarFile, project)
embeddedJars.forEach { embeddedDep ->
allEmbeddedDependencies[embeddedDep] = jarFile
project.logger.debug(
"Collected embedded dependency for JAR: {} from {}",
embeddedDep,
jarFile
)
}
}
collectAllEmbeddedDependencies(dep.children)
}
}
}
processed.clear()
collectAllEmbeddedDependencies(resolvedDependencies)
processed.clear()
fun logDependencies(dependencies: Set<ResolvedDependency>) {
dependencies.forEach { dep ->
val depKey = "${dep.moduleGroup}:${dep.moduleName}:${dep.moduleVersion}"
val depId = DependencyIdentifier(dep.moduleGroup, dep.moduleName, dep.moduleVersion)
if (!processed.contains(depKey)) {
processed.add(depKey)
project.logger.debug("Resolved JIJ dependency for JAR: {}", depId)
val jarFile = dep.moduleArtifacts.firstOrNull()?.file
if (jarFile != null && jarFile.exists()) {
val embeddedJars = extractEmbeddedJars(jarFile, project)
embeddedJars.forEach { embeddedDep ->
allEmbeddedDependencies[embeddedDep] = jarFile
}
}
if (isJijTarget(
dep,
allEmbeddedDependencies.keys,
actuallyIncludedDependencies,
project
)
) {
project.logger.debug("Including JIJ dependency in JAR: {}", depId)
actuallyIncludedDependencies.add(depId)
}
logDependencies(dep.children)
}
}
}
logDependencies(resolvedDependencies)
}
}
@@ -0,0 +1,52 @@
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
plugins {
id("com.gradleup.shadow")
}
val relocate: Boolean by rootProject.extra
// 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
if (relocate) {
relocate(
"io.github.retrooper.packetevents",
"ac.grim.grimac.shaded.io.github.retrooper.packetevents"
)
relocate(
"com.github.retrooper.packetevents",
"ac.grim.grimac.shaded.com.github.retrooper.packetevents"
)
relocate("club.minnced", "ac.grim.grimac.shaded.discord-webhooks")
relocate("org.slf4j", "ac.grim.grimac.shaded.slf4j") // Required by discord-webhooks
relocate("github.scarsz.configuralize", "ac.grim.grimac.shaded.configuralize")
relocate("com.github.puregero", "ac.grim.grimac.shaded.com.github.puregero")
relocate("com.google.code.gson", "ac.grim.grimac.shaded.gson")
relocate("alexh", "ac.grim.grimac.shaded.maps")
relocate("it.unimi.dsi.fastutil", "ac.grim.grimac.shaded.fastutil")
relocate("net.kyori", "ac.grim.grimac.shaded.kyori")
relocate("okhttp3", "ac.grim.grimac.shaded.okhttp3")
relocate("okio", "ac.grim.grimac.shaded.okio")
relocate("org.yaml.snakeyaml", "ac.grim.grimac.shaded.snakeyaml")
relocate("org.json", "ac.grim.grimac.shaded.json")
relocate("org.intellij", "ac.grim.grimac.shaded.intellij")
relocate("org.jetbrains", "ac.grim.grimac.shaded.jetbrains")
relocate("org.incendo", "ac.grim.grimac.shaded.incendo")
relocate("io.leangen.geantyref", "ac.grim.grimac.shaded.geantyref") // Required by cloud
}
mergeServiceFiles()
}
tasks.named("assemble") {
dependsOn(tasks.named("shadowJar"))
}
+139
View File
@@ -0,0 +1,139 @@
import net.minecrell.pluginyml.bukkit.BukkitPluginDescription.Permission
plugins {
`maven-publish`
grim.`base-conventions`
grim.`shadow-conventions`
id("net.minecrell.plugin-yml.bukkit") version "0.6.0"
}
repositories {
mavenLocal()
maven {
name = "papermc"
url = uri("https://repo.papermc.io/repository/maven-public/")
}
maven("https://hub.spigotmc.org/nexus/content/repositories/snapshots/") // Spigot
maven("https://jitpack.io/") { // Grim API
content {
includeGroup("com.github.grimanticheat")
}
}
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://repo.codemc.io/repository/maven-releases/") // PacketEvents
maven("https://repo.codemc.io/repository/maven-snapshots/")
maven("https://s01.oss.sonatype.org/content/repositories/snapshots/")
maven("https://repo.extendedclip.com/content/repositories/placeholderapi/") // placeholderapi
mavenCentral()
// FastUtil, Discord-Webhooks
}
dependencies {
compileOnly(libs.paper.api)
compileOnly(libs.placeholderapi)
implementation(libs.packetevents.spigot)
implementation(libs.cloud.paper)
implementation(libs.adventure.platform.bukkit)
implementation(project(":common"))
shadow(project(":common"))
}
bukkit {
name = "GrimAC"
author = "GrimAC"
main = "ac.grim.grimac.platform.bukkit.GrimACBukkitLoaderPlugin"
website = "https://grim.ac/"
apiVersion = "1.13"
foliaSupported = true
softDepend = listOf(
"ProtocolLib",
"ProtocolSupport",
"Essentials",
"ViaVersion",
"ViaBackwards",
"ViaRewind",
"Geyser-Spigot",
"floodgate",
"FastLogin",
"PlaceholderAPI",
)
permissions {
register("grim.alerts") {
description = "Receive alerts for violations"
default = Permission.Default.OP
}
register("grim.alerts.enable-on-join") {
description = "Enable alerts on join"
default = Permission.Default.OP
}
register("grim.performance") {
description = "Check performance metrics"
default = Permission.Default.OP
}
register("grim.profile") {
description = "Check user profile"
default = Permission.Default.OP
}
register("grim.brand") {
description = "Show client brands on join"
default = Permission.Default.OP
}
register("grim.brand.enable-on-join") {
description = "Enable showing client brands on join"
default = Permission.Default.OP
}
register("grim.sendalert") {
description = "Send cheater alert"
default = Permission.Default.OP
}
register("grim.nosetback") {
description = "Disable setback"
default = Permission.Default.FALSE
}
register("grim.nomodifypacket") {
description = "Disable modifying packets"
default = Permission.Default.FALSE
}
register("grim.exempt") {
description = "Exempt from all checks"
default = Permission.Default.FALSE
}
register("grim.verbose") {
description = "Receive verbose alerts for violations. Requires grim.alerts"
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"
default = Permission.Default.FALSE
}
}
}
publishing.publications.create<MavenPublication>("maven") {
artifact(tasks["shadowJar"])
}
tasks.shadowJar {
manifest {
attributes["paperweight-mappings-namespace"] = "mojang"
}
}
@@ -0,0 +1,27 @@
package ac.grim.grimac.platform.bukkit;
import ac.grim.grimac.platform.api.PlatformPlugin;
import org.bukkit.plugin.Plugin;
public class BukkitPlatformPlugin implements PlatformPlugin {
private final Plugin plugin;
public BukkitPlatformPlugin(Plugin plugin) {
this.plugin = plugin;
}
@Override
public boolean isEnabled() {
return plugin.isEnabled();
}
@Override
public String getName() {
return plugin.getName();
}
@Override
public String getVersion() {
return plugin.getDescription().getVersion();
}
}
@@ -0,0 +1,37 @@
package ac.grim.grimac.platform.bukkit;
import ac.grim.grimac.platform.api.PlatformServer;
import ac.grim.grimac.platform.api.sender.Sender;
import io.github.retrooper.packetevents.util.SpigotReflectionUtil;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
public class BukkitPlatformServer implements PlatformServer {
@Override
public String getPlatformImplementationString() {
return Bukkit.getVersion();
}
@Override
public void dispatchCommand(Sender sender, String command) {
CommandSender commandSender = GrimACBukkitLoaderPlugin.LOADER.getBukkitSenderFactory().reverse(sender);
Bukkit.dispatchCommand(commandSender, command);
}
@Override
public Sender getConsoleSender() {
return GrimACBukkitLoaderPlugin.LOADER.getBukkitSenderFactory().map(Bukkit.getConsoleSender());
}
@Override
public void registerOutgoingPluginChannel(String bungeeCord) {
GrimACBukkitLoaderPlugin.LOADER.getServer().getMessenger().registerOutgoingPluginChannel(GrimACBukkitLoaderPlugin.LOADER, "BungeeCord");
}
@Override
public double getTPS() {
return SpigotReflectionUtil.getTPS();
}
}
@@ -0,0 +1,195 @@
package ac.grim.grimac.platform.bukkit;
import ac.grim.grimac.api.plugin.BasicGrimPlugin;
import ac.grim.grimac.GrimAPI;
import ac.grim.grimac.api.GrimAPIProvider;
import ac.grim.grimac.api.GrimAbstractAPI;
import ac.grim.grimac.api.plugin.GrimPlugin;
import ac.grim.grimac.manager.init.Initable;
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.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.platform.bukkit.initables.BukkitBStats;
import ac.grim.grimac.platform.bukkit.initables.BukkitEventManager;
import ac.grim.grimac.platform.bukkit.initables.BukkitTickEndEvent;
import ac.grim.grimac.platform.bukkit.manager.BukkitItemResetHandler;
import ac.grim.grimac.platform.bukkit.manager.BukkitMessagePlaceHolderManager;
import ac.grim.grimac.platform.bukkit.manager.BukkitParserDescriptorFactory;
import ac.grim.grimac.platform.bukkit.manager.BukkitPermissionRegistrationManager;
import ac.grim.grimac.platform.bukkit.manager.BukkitPlatformPluginManager;
import ac.grim.grimac.platform.bukkit.player.BukkitPlatformPlayerFactory;
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.MessageUtil;
import ac.grim.grimac.utils.lazy.LazyHolder;
import com.github.retrooper.packetevents.PacketEventsAPI;
import io.github.retrooper.packetevents.factory.spigot.SpigotPacketEventsBuilder;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.plugin.ServicePriority;
import org.bukkit.plugin.java.JavaPlugin;
import org.incendo.cloud.CommandManager;
import org.incendo.cloud.bukkit.CloudBukkitCapabilities;
import org.incendo.cloud.execution.ExecutionCoordinator;
import org.incendo.cloud.paper.LegacyPaperCommandManager;
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<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 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();
public GrimACBukkitLoaderPlugin() {
this.plugin = new BasicGrimPlugin(
this.getLogger(),
this.getDataFolder(),
this.getDescription().getVersion(),
this.getDescription().getDescription(),
this.getDescription().getAuthors()
);
}
@Override
public void onLoad() {
LOADER = this;
GrimAPI.INSTANCE.load(this, this.getBukkitInitTasks());
}
private Initable[] getBukkitInitTasks() {
return new Initable[]{
new ExemptOnlinePlayersOnReload(),
new BukkitEventManager(),
new BukkitTickEndEvent(),
new BukkitBStats(),
(StartableInitable) () -> {
if (MessageUtil.hasPlaceholderAPI) {
new PlaceholderAPIExpansion().register();
}
}
};
}
@Override
public void onEnable() {
GrimAPI.INSTANCE.start();
}
@Override
public void onDisable() {
GrimAPI.INSTANCE.stop();
}
@Override
public PlatformScheduler getScheduler() {
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 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);
}
@Override
public @NotNull MessagePlaceHolderManager getMessagePlaceHolderManager() {
return messagePlaceHolderManager;
}
@Override
public PermissionRegistrationManager getPermissionManager() {
return bukkitPermissionRegistrationManager;
}
private PlatformScheduler createScheduler() {
return GrimAPI.INSTANCE.getPlatform() == Platform.FOLIA ? new FoliaPlatformScheduler() : new BukkitPlatformScheduler();
}
private CommandManager<Sender> createCommandManager() {
LegacyPaperCommandManager<Sender> manager = new LegacyPaperCommandManager<>(
this,
ExecutionCoordinator.simpleCoordinator(),
senderFactory.get()
);
if (manager.hasCapability(CloudBukkitCapabilities.NATIVE_BRIGADIER)) {
manager.registerBrigadier();
} else if (manager.hasCapability(CloudBukkitCapabilities.ASYNCHRONOUS_COMPLETION)) {
manager.registerAsynchronousCompletions();
}
return manager;
}
public BukkitSenderFactory getBukkitSenderFactory() {
return LOADER.senderFactory.get();
}
}
@@ -0,0 +1,37 @@
package ac.grim.grimac.platform.bukkit.command;
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 java.util.Collection;
import java.util.Collections;
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;
}
@Override
public boolean isSingle() {
return true;
}
@Override
public Sender getSinglePlayer() {
return ((BukkitSenderFactory) GrimAPI.INSTANCE.getSenderFactory()).map(bukkitSelector.single());
}
@Override
public Collection<Sender> getPlayers() {
return Collections.singletonList(((BukkitSenderFactory) GrimAPI.INSTANCE.getSenderFactory()).map(bukkitSelector.single()));
}
@Override
public String inputString() {
return bukkitSelector.inputString();
}
}
@@ -0,0 +1,28 @@
package ac.grim.grimac.platform.bukkit.command;
import ac.grim.grimac.platform.api.command.AbstractPlayerSelectorParser;
import ac.grim.grimac.platform.api.command.PlayerSelector;
import org.incendo.cloud.bukkit.parser.selector.SinglePlayerSelectorParser;
import org.incendo.cloud.context.CommandContext;
import org.incendo.cloud.parser.ParserDescriptor;
import java.util.concurrent.CompletableFuture;
public class BukkitPlayerSelectorParser<C> extends AbstractPlayerSelectorParser<C> {
@Override
public ParserDescriptor<C, PlayerSelector> descriptor() {
return super.createDescriptor();
}
@Override
protected ParserDescriptor<C, ?> getPlatformSpecificDescriptor() {
return SinglePlayerSelectorParser.singlePlayerSelectorParser();
}
@Override
protected CompletableFuture<PlayerSelector> adaptToCommonSelector(CommandContext<C> context, Object platformSpecificSelector) {
return CompletableFuture.completedFuture(new BukkitPlayerSelectorAdapter((org.incendo.cloud.bukkit.data.SinglePlayerSelector) platformSpecificSelector));
}
}
@@ -0,0 +1,79 @@
package ac.grim.grimac.platform.bukkit.entity;
import ac.grim.grimac.platform.api.entity.GrimEntity;
import ac.grim.grimac.platform.api.world.PlatformWorld;
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.google.common.base.Preconditions;
import org.bukkit.entity.Entity;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
public class BukkitGrimEntity implements GrimEntity {
private final Entity entity;
private BukkitPlatformWorld bukkitPlatformWorld;
public BukkitGrimEntity(Entity entity) {
Preconditions.checkArgument(entity != null);
this.entity = entity;
}
public Entity getBukkitEntity() {
return this.entity;
}
@Override
public UUID getUniqueId() {
return entity.getUniqueId();
}
@Override
public boolean eject() {
return entity.eject();
}
@Override
public CompletableFuture<Boolean> teleportAsync(Location location) {
org.bukkit.Location bLoc = BukkitConversionUtils.toBukkitLocation(location);
return PaperUtils.teleportAsync(this.entity, bLoc);
}
@Override
@NonNull
public Entity getNative() {
return entity;
}
@Override
public boolean isDead() {
return this.entity.isDead();
}
// TODO replace with PlayerWorldChangeEvent listener instead of checking for equality for better performance
@Override
public PlatformWorld getWorld() {
if (bukkitPlatformWorld == null || !bukkitPlatformWorld.getBukkitWorld().equals(entity.getWorld())) {
bukkitPlatformWorld = new BukkitPlatformWorld(entity.getWorld());
}
return bukkitPlatformWorld;
}
@Override
public Location getLocation() {
org.bukkit.Location location = this.entity.getLocation();
return new Location(
this.getWorld(),
location.getX(),
location.getY(),
location.getZ(),
location.getYaw(),
location.getPitch()
);
}
}
@@ -1,8 +1,8 @@
package ac.grim.grimac.events.bukkit;
package ac.grim.grimac.platform.bukkit.events;
import ac.grim.grimac.GrimAPI;
import ac.grim.grimac.platform.bukkit.utils.blockstate.BukkitBlockFaceHelper;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.blockstate.helper.BlockFaceHelper;
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
import ac.grim.grimac.utils.data.PistonData;
import com.github.retrooper.packetevents.protocol.world.BlockFace;
@@ -57,7 +57,7 @@ public class PistonEvent implements Listener {
for (GrimPlayer player : GrimAPI.INSTANCE.getPlayerDataManager().getEntries()) {
if (player.compensatedWorld.isChunkLoaded(event.getBlock().getX() >> 4, event.getBlock().getZ() >> 4)) {
PistonData data = new PistonData(BlockFaceHelper.fromBukkitFace(event.getDirection()), boxes, player.lastTransactionSent.get(), true, hasSlimeBlock, hasHoneyBlock);
PistonData data = new PistonData(BukkitBlockFaceHelper.fromBukkitFace(event.getDirection()), boxes, player.lastTransactionSent.get(), true, hasSlimeBlock, hasHoneyBlock);
player.latencyUtils.addRealTimeTaskAsync(player.lastTransactionSent.get(), () -> player.compensatedWorld.activePistons.add(data));
}
}
@@ -79,7 +79,7 @@ public class PistonEvent implements Listener {
boolean hasHoneyBlock = false;
List<SimpleCollisionBox> boxes = new ArrayList<>();
BlockFace face = BlockFaceHelper.fromBukkitFace(event.getDirection());
BlockFace face = BukkitBlockFaceHelper.fromBukkitFace(event.getDirection());
// The event was called without blocks and is therefore in the right direction
if (event.getBlocks().isEmpty()) {
@@ -110,7 +110,7 @@ public class PistonEvent implements Listener {
for (GrimPlayer player : GrimAPI.INSTANCE.getPlayerDataManager().getEntries()) {
if (player.compensatedWorld.isChunkLoaded(event.getBlock().getX() >> 4, event.getBlock().getZ() >> 4)) {
PistonData data = new PistonData(BlockFaceHelper.fromBukkitFace(event.getDirection()), boxes, player.lastTransactionSent.get(), false, hasSlimeBlock, hasHoneyBlock);
PistonData data = new PistonData(BukkitBlockFaceHelper.fromBukkitFace(event.getDirection()), boxes, player.lastTransactionSent.get(), false, hasSlimeBlock, hasHoneyBlock);
player.latencyUtils.addRealTimeTaskAsync(player.lastTransactionSent.get(), () -> player.compensatedWorld.activePistons.add(data));
}
}
@@ -0,0 +1,16 @@
package ac.grim.grimac.platform.bukkit.initables;
import ac.grim.grimac.manager.init.start.StartableInitable;
import ac.grim.grimac.platform.bukkit.GrimACBukkitLoaderPlugin;
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) {
}
}
}
@@ -0,0 +1,15 @@
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.platform.bukkit.events.PistonEvent;
import ac.grim.grimac.utils.anticheat.LogUtil;
import org.bukkit.Bukkit;
public class BukkitEventManager implements StartableInitable {
public void start() {
LogUtil.info("Registering singular bukkit event... (PistonEvent)");
Bukkit.getPluginManager().registerEvents(new PistonEvent(), GrimACBukkitLoaderPlugin.LOADER);
}
}
@@ -1,7 +1,10 @@
package ac.grim.grimac.manager.init.start;
package ac.grim.grimac.platform.bukkit.initables;
import ac.grim.grimac.GrimAPI;
import ac.grim.grimac.manager.init.Initable;
import ac.grim.grimac.manager.init.start.AbstractTickEndEvent;
import ac.grim.grimac.platform.api.Platform;
import ac.grim.grimac.platform.bukkit.player.BukkitPlatformPlayer;
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;
@@ -9,6 +12,9 @@ import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.util.reflection.Reflection;
import io.github.retrooper.packetevents.util.SpigotReflectionUtil;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.Listener;
import sun.misc.Unsafe;
import java.lang.reflect.Field;
@@ -17,26 +23,45 @@ import java.util.List;
// Copied from: https://github.com/ThomasOM/Pledge/blob/master/src/main/java/dev/thomazz/pledge/inject/ServerInjector.java
@SuppressWarnings(value = {"unchecked", "deprecated"})
public class TickEndEvent implements Initable {
boolean hasTicked = true;
private static void tickRelMove() {
for (GrimPlayer player : GrimAPI.INSTANCE.getPlayerDataManager().getEntries()) {
if (player.disableGrim) continue; // If we aren't active don't spam extra transactions
player.checkManager.getEntityReplication().onEndOfTickEvent();
}
}
public class BukkitTickEndEvent extends AbstractTickEndEvent implements Listener {
@Override
public void start() {
if (!GrimAPI.INSTANCE.getConfigManager().getConfig().getBooleanElse("Reach.enable-post-packet", false)) {
if (!super.shouldInjectEndTick()) {
return;
}
if (PacketEvents.getAPI().getServerManager().getVersion().isNewerThan(ServerVersion.V_1_11_2) && !Boolean.getBoolean("paper.explicit-flush")) {
LogUtil.warn("Reach.enable-post-packet=true but paper.explicit-flush=false, add \"-Dpaper.explicit-flush=true\" to your server's startup flags for fully functional extra reach accuracy.");
}
// this is necessary for folia
if (GrimAPI.INSTANCE.getPlatform() == Platform.FOLIA) {
PaperUtils.registerTickEndEvent(this, this::tickAllFoliaPlayers);
return;
}
// if it fails to inject, try to use paper's event
if (!injectWithReflection() && !PaperUtils.registerTickEndEvent(this, this::tickAllPlayers)) {
LogUtil.error("Failed to inject into the end of tick event!");
}
}
private void tickAllPlayers() {
for (GrimPlayer player : GrimAPI.INSTANCE.getPlayerDataManager().getEntries()) {
if (player.disableGrim) continue;
super.onEndOfTick(player);
}
}
private void tickAllFoliaPlayers() {
for (GrimPlayer player : GrimAPI.INSTANCE.getPlayerDataManager().getEntries()) {
if (player.disableGrim) continue;
if (player.platformPlayer == null) continue;
Player p = ((BukkitPlatformPlayer) player.platformPlayer).getNative();
if (!Bukkit.isOwnedByCurrentRegion(p)) continue;
super.onEndOfTick(player);
}
}
private boolean injectWithReflection() {
// Inject so we can add the final transaction pre-flush event
try {
Object connection = SpigotReflectionUtil.getMinecraftServerConnectionInstance();
@@ -52,8 +77,7 @@ public class TickEndEvent implements Initable {
List<?> wrapper = Collections.synchronizedList(new HookedListWrapper<>(endOfTickObject) {
@Override
public void onIterator() {
hasTicked = true;
tickRelMove();
tickAllPlayers();
}
});
@@ -62,7 +86,9 @@ public class TickEndEvent implements Initable {
Unsafe unsafe = (Unsafe) unsafeField.get(null);
unsafe.putObject(connection, unsafe.objectFieldOffset(connectionsList), wrapper);
} catch (NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
LogUtil.exception("Failed to inject into the end of tick event via reflection", e);
return false;
}
return true;
}
}
@@ -0,0 +1,100 @@
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 com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import lombok.SneakyThrows;
import org.bukkit.Bukkit;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.lang.reflect.Method;
public class BukkitItemResetHandler implements ItemResetHandler {
// resets item usage, then returns whether the player was using an item
private final @NotNull ItemUsageReset resetItemUsage = createItemUsageResetFunction();
@SneakyThrows
public void resetItemUsage(@Nullable PlatformPlayer player) {
if (player != null) {
resetItemUsage.accept(((BukkitPlatformPlayer) 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;
}
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);
return player -> {
// don't trigger gameevents
setLivingEntityFlag.invoke(getHandle.invoke(player), 1, false);
player.clearActiveItem();
};
}
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");
return player -> {
Object handle = getHandle.invoke(player);
clearActiveItem.invoke(handle);
// 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();
}
};
}
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;
}
}
@@ -0,0 +1,28 @@
package ac.grim.grimac.platform.bukkit.manager;
import ac.grim.grimac.platform.api.manager.MessagePlaceHolderManager;
import ac.grim.grimac.platform.api.player.PlatformPlayer;
import ac.grim.grimac.platform.api.sender.Sender;
import ac.grim.grimac.platform.bukkit.GrimACBukkitLoaderPlugin;
import ac.grim.grimac.platform.bukkit.player.BukkitPlatformPlayer;
import ac.grim.grimac.utils.anticheat.MessageUtil;
import me.clip.placeholderapi.PlaceholderAPI;
import org.bukkit.OfflinePlayer;
import org.bukkit.command.CommandSender;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
public class BukkitMessagePlaceHolderManager implements MessagePlaceHolderManager {
@Override
public @NonNull String replacePlaceholders(@Nullable Sender sender, @NonNull String string) {
if (!MessageUtil.hasPlaceholderAPI) return string;
CommandSender commandSender = sender == null ? null : GrimACBukkitLoaderPlugin.LOADER.getBukkitSenderFactory().unwrap(sender);
return PlaceholderAPI.setPlaceholders(sender instanceof OfflinePlayer ? (OfflinePlayer) commandSender : null, string);
}
@Override
public @NonNull String replacePlaceholders(@Nullable PlatformPlayer object, @NonNull String string) {
if (!MessageUtil.hasPlaceholderAPI) return string;
return PlaceholderAPI.setPlaceholders(((BukkitPlatformPlayer) object).getBukkitPlayer(), string);
}
}
@@ -0,0 +1,17 @@
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.sender.Sender;
import ac.grim.grimac.platform.bukkit.command.BukkitPlayerSelectorParser;
import org.incendo.cloud.parser.ParserDescriptor;
public class BukkitParserDescriptorFactory implements ParserDescriptorFactory {
BukkitPlayerSelectorParser<Sender> bukkitPlayerSelectorParser = new BukkitPlayerSelectorParser<>();
@Override
public ParserDescriptor<Sender, PlayerSelector> getSinglePlayer() {
return bukkitPlayerSelectorParser.descriptor();
}
}
@@ -0,0 +1,33 @@
package ac.grim.grimac.platform.bukkit.manager;
import ac.grim.grimac.platform.api.manager.PermissionRegistrationManager;
import ac.grim.grimac.platform.api.permissions.PermissionDefaultValue;
import ac.grim.grimac.platform.bukkit.utils.convert.BukkitConversionUtils;
import org.bukkit.Bukkit;
import org.bukkit.permissions.Permission;
public class BukkitPermissionRegistrationManager implements PermissionRegistrationManager {
/**
* Registers a permission with the specified default value on Bukkit.
* This method is only called for dynamic permissions (e.g., check-specific permissions
* like "grim.exempt.checkname") that are generated at runtime. Most other static permissions
* (e.g., "grim.exempt", "grim.alerts.enable-on-join") are registered with their defaults
* in the `plugin.yml` file, which is defined in the Bukkit Gradle build script.
*
* <p>Dynamic permissions are registered here to ensure they are available for autocomplete
* and permission checks immediately on startup. If the permission already exists, its default
* value is updated to match the specified value.</p>
*
* @param s The permission node to register (e.g., "grim.exempt.checkname").
* @param defaultValue The default value for the permission.
*/
@Override
public void registerPermission(String s, PermissionDefaultValue defaultValue) {
final Permission permission = Bukkit.getPluginManager().getPermission(s);
if (permission == null) {
Bukkit.getPluginManager().addPermission(new Permission(s, BukkitConversionUtils.toBukkitPermissionDefault(defaultValue)));
} else {
permission.setDefault(BukkitConversionUtils.toBukkitPermissionDefault(defaultValue));
}
}
}
@@ -0,0 +1,50 @@
package ac.grim.grimac.platform.bukkit.manager;
import ac.grim.grimac.api.event.EventBus;
import ac.grim.grimac.api.event.GrimEvent;
import ac.grim.grimac.api.event.OptimizedEventBus;
import ac.grim.grimac.platform.api.PlatformPlugin;
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;
public class BukkitPlatformPluginManager implements PlatformPluginManager {
private final EventBus eventBus = new OptimizedEventBus();
@Override
public void callEvent(GrimEvent event) {
eventBus.post(event);
}
@Override
public EventBus getEventBus() {
return this.eventBus;
}
@Override
public PlatformPlugin[] getPlugins() {
Plugin[] bukkitPlugins = Bukkit.getPluginManager().getPlugins();
PlatformPlugin[] plugins = new PlatformPlugin[bukkitPlugins.length];
for (int i = 0; i < bukkitPlugins.length; i++) {
plugins[i] = new BukkitPlatformPlugin(bukkitPlugins[i]);
}
return plugins;
}
@Override
public @Nullable PlatformPlugin getPlugin(String pluginName) {
Plugin bukkitPlugin = Bukkit.getPluginManager().getPlugin(pluginName);
return bukkitPlugin == null ? null : new BukkitPlatformPlugin(bukkitPlugin);
}
@Override
public boolean isPluginEnabled(String pluginName) {
PlatformPlugin plugin = getPlugin(pluginName);
return plugin != null && plugin.isEnabled();
}
}
@@ -0,0 +1,66 @@
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 org.bukkit.entity.Player;
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());
}
@Override
public ItemStack getItemInOffHand() {
return SpigotConversionUtil.fromBukkitItemStack(bukkitPlayer.getInventory().getItemInOffHand());
}
@Override
public ItemStack getStack(int bukkitSlot, int vanillaSlot) {
return SpigotConversionUtil.fromBukkitItemStack(bukkitPlayer.getInventory().getItem(bukkitSlot));
}
@Override
public ItemStack getHelmet() {
return SpigotConversionUtil.fromBukkitItemStack(bukkitPlayer.getInventory().getHelmet());
}
@Override
public ItemStack getChestplate() {
return SpigotConversionUtil.fromBukkitItemStack(bukkitPlayer.getInventory().getHelmet());
}
@Override
public ItemStack getLeggings() {
return SpigotConversionUtil.fromBukkitItemStack(bukkitPlayer.getInventory().getLeggings());
}
@Override
public ItemStack getBoots() {
return SpigotConversionUtil.fromBukkitItemStack(bukkitPlayer.getInventory().getBoots());
}
@Override
public ItemStack[] getContents() {
org.bukkit.inventory.ItemStack[] bukkitItems = bukkitPlayer.getInventory().getContents();
ItemStack[] items = new ItemStack[bukkitItems.length];
for (int i = 0; i < bukkitItems.length; i++) {
if (bukkitItems[i] == null) continue;
items[i] = SpigotConversionUtil.fromBukkitItemStack(bukkitItems[i]);
}
return items;
}
@Override
public String getOpenInventoryKey() {
return bukkitPlayer.getOpenInventory().getType().toString();
}
}
@@ -0,0 +1,156 @@
package ac.grim.grimac.platform.bukkit.player;
import ac.grim.grimac.platform.api.entity.GrimEntity;
import ac.grim.grimac.platform.api.player.PlatformInventory;
import ac.grim.grimac.platform.api.player.PlatformPlayer;
import ac.grim.grimac.platform.api.sender.Sender;
import ac.grim.grimac.platform.bukkit.GrimACBukkitLoaderPlugin;
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.math.Location;
import com.github.retrooper.packetevents.protocol.player.GameMode;
import com.github.retrooper.packetevents.util.Vector3d;
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 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;
private final PlatformInventory inventory;
public BukkitPlatformPlayer(Player bukkitPlayer) {
super(bukkitPlayer);
this.bukkitPlayer = bukkitPlayer;
this.inventory = new BukkitPlatformInventory(bukkitPlayer);
}
@Override
public void kickPlayer(String textReason) {
bukkitPlayer.kickPlayer(textReason);
}
@Override
public boolean hasPermission(String s) {
return bukkitPlayer.hasPermission(s);
}
@Override
public boolean hasPermission(String s, boolean defaultIfUnset) {
return this.bukkitPlayer.hasPermission(new Permission(s, defaultIfUnset ? PermissionDefault.TRUE : PermissionDefault.FALSE));
}
@Override
public boolean isSneaking() {
return bukkitPlayer.isSneaking();
}
@Override
public void setSneaking(boolean isSneaking) {
bukkitPlayer.setSneaking(isSneaking);
}
@Override
public void sendMessage(String message) {
bukkitPlayer.sendMessage(message);
}
@Override
public void sendMessage(Component message) {
audiences.player(bukkitPlayer).sendMessage(message);
}
@Override
public boolean isOnline() {
return bukkitPlayer.isOnline();
}
@Override
public String getName() {
return bukkitPlayer.getName();
}
@Override
public void updateInventory() {
bukkitPlayer.updateInventory();
}
@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;
}
@Override
public GrimEntity getVehicle() {
return new BukkitGrimEntity(bukkitPlayer.getVehicle());
}
@Override
public GameMode getGameMode() {
return BukkitConversionUtils.fromBukkitGameMode(bukkitPlayer.getGameMode());
}
@Override
public void setGameMode(GameMode gameMode) {
bukkitPlayer.setGameMode(BukkitConversionUtils.toBukkitGameMode(gameMode));
}
public World getBukkitWorld() {
return bukkitPlayer.getWorld();
}
@Override
public UUID getUniqueId() {
return bukkitPlayer.getUniqueId();
}
@Override
public boolean eject() {
return bukkitPlayer.eject();
}
@Override
public CompletableFuture<Boolean> teleportAsync(Location location) {
org.bukkit.Location bLoc = BukkitConversionUtils.toBukkitLocation(location);
return PaperUtils.teleportAsync(this.bukkitPlayer, bLoc);
}
@Override
public boolean isExternalPlayer() {
return MultiLibUtil.isExternalPlayer(this.bukkitPlayer);
}
@Override
public void sendPluginMessage(String channelName, byte[] byteArray) {
this.bukkitPlayer.sendPluginMessage(GrimACBukkitLoaderPlugin.LOADER, channelName, byteArray);
}
@Override
public Sender getSender() {
return GrimACBukkitLoaderPlugin.LOADER.getBukkitSenderFactory().map(this.bukkitPlayer);
}
@Override
@NonNull
public Player getNative() {
return this.bukkitPlayer;
}
}
@@ -0,0 +1,47 @@
package ac.grim.grimac.platform.bukkit.player;
import ac.grim.grimac.platform.api.player.AbstractPlatformPlayerFactory;
import ac.grim.grimac.platform.api.player.PlatformPlayer;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
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 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
@SuppressWarnings("unchecked")
protected Collection<Player> getNativeOnlinePlayers() {
// Cast Collection<? extends Player> to Collection<Player>
return (Collection<Player>) Bukkit.getOnlinePlayers();
}
}
@@ -0,0 +1,54 @@
package ac.grim.grimac.platform.bukkit.scheduler.bukkit;
import ac.grim.grimac.api.plugin.GrimPlugin;
import ac.grim.grimac.platform.api.scheduler.AsyncScheduler;
import ac.grim.grimac.platform.api.scheduler.PlatformScheduler;
import ac.grim.grimac.platform.api.scheduler.TaskHandle;
import ac.grim.grimac.platform.bukkit.GrimACBukkitLoaderPlugin;
import org.bukkit.Bukkit;
import org.bukkit.scheduler.BukkitScheduler;
import org.jetbrains.annotations.NotNull;
import java.util.concurrent.TimeUnit;
public class BukkitAsyncScheduler implements AsyncScheduler {
private final BukkitScheduler bukkitScheduler = Bukkit.getScheduler();
@Override
public TaskHandle runNow(@NotNull GrimPlugin plugin, @NotNull Runnable task) {
return new BukkitTaskHandle(bukkitScheduler.runTaskAsynchronously(GrimACBukkitLoaderPlugin.LOADER, task));
}
@Override
public TaskHandle runDelayed(@NotNull GrimPlugin plugin, @NotNull Runnable task, long delay, @NotNull TimeUnit timeUnit) {
return new BukkitTaskHandle(bukkitScheduler.runTaskLaterAsynchronously(GrimACBukkitLoaderPlugin.LOADER,
task,
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)
)
);
}
@Override
public TaskHandle runAtFixedRate(@NotNull GrimPlugin plugin, @NotNull Runnable task, long initialDelayTicks, long periodTicks) {
return new BukkitTaskHandle(
bukkitScheduler.runTaskTimerAsynchronously(GrimACBukkitLoaderPlugin.LOADER, task, initialDelayTicks, periodTicks)
);
}
@Override
public void cancel(@NotNull GrimPlugin plugin) {
bukkitScheduler.cancelTasks(GrimACBukkitLoaderPlugin.LOADER);
}
}
@@ -0,0 +1,36 @@
package ac.grim.grimac.platform.bukkit.scheduler.bukkit;
import ac.grim.grimac.api.plugin.GrimPlugin;
import ac.grim.grimac.platform.api.entity.GrimEntity;
import ac.grim.grimac.platform.api.scheduler.EntityScheduler;
import ac.grim.grimac.platform.api.scheduler.TaskHandle;
import ac.grim.grimac.platform.bukkit.GrimACBukkitLoaderPlugin;
import org.bukkit.Bukkit;
import org.bukkit.scheduler.BukkitScheduler;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class BukkitEntityScheduler implements EntityScheduler {
private final BukkitScheduler bukkitScheduler = 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);
}
@Override
public TaskHandle run(@NotNull GrimEntity entity, @NotNull GrimPlugin plugin, @NotNull Runnable task, @Nullable Runnable retired) {
return new BukkitTaskHandle(bukkitScheduler.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));
}
@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));
}
}
@@ -0,0 +1,39 @@
package ac.grim.grimac.platform.bukkit.scheduler.bukkit;
import ac.grim.grimac.api.plugin.GrimPlugin;
import ac.grim.grimac.platform.api.scheduler.GlobalRegionScheduler;
import ac.grim.grimac.platform.api.scheduler.TaskHandle;
import ac.grim.grimac.platform.bukkit.GrimACBukkitLoaderPlugin;
import org.bukkit.Bukkit;
import org.bukkit.scheduler.BukkitScheduler;
import org.jetbrains.annotations.NotNull;
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);
}
@Override
public TaskHandle run(@NotNull GrimPlugin plugin, @NotNull Runnable task) {
return new BukkitTaskHandle(bukkitScheduler.runTask(GrimACBukkitLoaderPlugin.LOADER, task));
}
@Override
public TaskHandle runDelayed(@NotNull GrimPlugin plugin, @NotNull Runnable task, long delay) {
return new BukkitTaskHandle(bukkitScheduler.runTaskLater(GrimACBukkitLoaderPlugin.LOADER, task, delay));
}
@Override
public TaskHandle runAtFixedRate(@NotNull GrimPlugin plugin, @NotNull Runnable task, long initialDelayTicks, long periodTicks) {
return new BukkitTaskHandle(bukkitScheduler.runTaskTimer(GrimACBukkitLoaderPlugin.LOADER, task, initialDelayTicks, periodTicks));
}
@Override
public void cancel(@NotNull GrimPlugin plugin) {
bukkitScheduler.cancelTasks(GrimACBukkitLoaderPlugin.LOADER);
}
}
@@ -0,0 +1,37 @@
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 org.jetbrains.annotations.NotNull;
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 @NotNull AsyncScheduler getAsyncScheduler() {
return bukkitAsyncScheduler;
}
@Override
public @NonNull @NotNull GlobalRegionScheduler getGlobalRegionScheduler() {
return bukkitGlobalRegionScheduler;
}
@Override
public @NonNull @NotNull EntityScheduler getEntityScheduler() {
return bukkitEntityScheduler;
}
@Override
public @NonNull @NotNull RegionScheduler getRegionScheduler() {
return bukkitRegionScheduler;
}
}
@@ -0,0 +1,56 @@
package ac.grim.grimac.platform.bukkit.scheduler.bukkit;
import ac.grim.grimac.api.plugin.GrimPlugin;
import ac.grim.grimac.platform.api.scheduler.RegionScheduler;
import ac.grim.grimac.platform.api.scheduler.TaskHandle;
import ac.grim.grimac.platform.api.world.PlatformWorld;
import ac.grim.grimac.platform.bukkit.GrimACBukkitLoaderPlugin;
import ac.grim.grimac.utils.math.Location;
import org.bukkit.Bukkit;
import org.bukkit.scheduler.BukkitScheduler;
import org.jetbrains.annotations.NotNull;
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);
}
@Override
public void execute(@NotNull GrimPlugin plugin, @NotNull Location location, @NotNull Runnable run) {
bukkitScheduler.runTask(GrimACBukkitLoaderPlugin.LOADER, run);
}
@Override
public TaskHandle run(@NotNull GrimPlugin plugin, @NotNull PlatformWorld world, int chunkX, int chunkZ, @NotNull Runnable task) {
return new BukkitTaskHandle(bukkitScheduler.runTask(GrimACBukkitLoaderPlugin.LOADER, task));
}
@Override
public TaskHandle run(@NotNull GrimPlugin plugin, @NotNull Location location, @NotNull Runnable task) {
return new BukkitTaskHandle(bukkitScheduler.runTask(GrimACBukkitLoaderPlugin.LOADER, task));
}
@Override
public TaskHandle runDelayed(@NotNull GrimPlugin plugin, @NotNull PlatformWorld world, int chunkX, int chunkZ, @NotNull Runnable task, long delayTicks) {
return new BukkitTaskHandle(bukkitScheduler.runTaskLater(GrimACBukkitLoaderPlugin.LOADER, task, delayTicks));
}
@Override
public TaskHandle runDelayed(@NotNull GrimPlugin plugin, @NotNull Location location, @NotNull Runnable task, long delayTicks) {
return new BukkitTaskHandle(bukkitScheduler.runTaskLater(GrimACBukkitLoaderPlugin.LOADER, task, delayTicks));
}
@Override
public TaskHandle runAtFixedRate(@NotNull GrimPlugin plugin, @NotNull PlatformWorld world, int chunkX, int chunkZ, @NotNull Runnable task, long initialDelayTicks, long periodTicks) {
return new BukkitTaskHandle(bukkitScheduler.runTaskTimer(GrimACBukkitLoaderPlugin.LOADER, task, initialDelayTicks, periodTicks));
}
@Override
public TaskHandle runAtFixedRate(@NotNull GrimPlugin plugin, @NotNull Location location, @NotNull Runnable task, long initialDelayTicks, long periodTicks) {
return new BukkitTaskHandle(bukkitScheduler.runTaskTimer(GrimACBukkitLoaderPlugin.LOADER, task, initialDelayTicks, periodTicks));
}
}
@@ -0,0 +1,28 @@
package ac.grim.grimac.platform.bukkit.scheduler.bukkit;
import ac.grim.grimac.platform.api.scheduler.TaskHandle;
import org.bukkit.scheduler.BukkitTask;
public class BukkitTaskHandle implements TaskHandle {
private final BukkitTask bukkitTask;
public BukkitTaskHandle(BukkitTask bukkitTask) {
this.bukkitTask = bukkitTask;
}
@Override
public boolean isSync() {
return bukkitTask.isSync();
}
@Override
public boolean getCancelled() {
return bukkitTask.isCancelled();
}
@Override
public void cancel() {
bukkitTask.cancel();
}
}
@@ -0,0 +1,63 @@
package ac.grim.grimac.platform.bukkit.scheduler.folia;
import ac.grim.grimac.api.plugin.GrimPlugin;
import ac.grim.grimac.platform.api.scheduler.AsyncScheduler;
import ac.grim.grimac.platform.api.scheduler.TaskHandle;
import ac.grim.grimac.platform.bukkit.GrimACBukkitLoaderPlugin;
import org.bukkit.Bukkit;
import org.jetbrains.annotations.NotNull;
import java.util.concurrent.TimeUnit;
public class FoliaAsyncScheduler implements AsyncScheduler {
private final io.papermc.paper.threadedregions.scheduler.AsyncScheduler asyncScheduler = Bukkit.getAsyncScheduler();
@Override
public TaskHandle runNow(@NotNull GrimPlugin plugin, @NotNull Runnable task) {
return new FoliaTaskHandle(asyncScheduler.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
)
);
}
@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
)
);
}
@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
)
);
}
@Override
public void cancel(@NotNull GrimPlugin plugin) {
asyncScheduler.cancelTasks(GrimACBukkitLoaderPlugin.LOADER);
}
}
@@ -0,0 +1,43 @@
package ac.grim.grimac.platform.bukkit.scheduler.folia;
import ac.grim.grimac.api.plugin.GrimPlugin;
import ac.grim.grimac.platform.api.entity.GrimEntity;
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 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, () -> {
}, 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(), () -> {
})
);
}
@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(), () -> {
}, delayTicks)
);
}
@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(), () -> {
}, initialDelayTicks, periodTicks)
);
}
}
@@ -0,0 +1,38 @@
package ac.grim.grimac.platform.bukkit.scheduler.folia;
import ac.grim.grimac.api.plugin.GrimPlugin;
import ac.grim.grimac.platform.api.scheduler.GlobalRegionScheduler;
import ac.grim.grimac.platform.api.scheduler.TaskHandle;
import ac.grim.grimac.platform.bukkit.GrimACBukkitLoaderPlugin;
import org.bukkit.Bukkit;
import org.jetbrains.annotations.NotNull;
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);
}
@Override
public TaskHandle run(@NotNull GrimPlugin plugin, @NotNull Runnable task) {
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));
}
@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));
}
@Override
public void cancel(@NotNull GrimPlugin plugin) {
globalRegionScheduler.cancelTasks(GrimACBukkitLoaderPlugin.LOADER);
}
}
@@ -0,0 +1,36 @@
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;
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;
}
}
@@ -0,0 +1,59 @@
package ac.grim.grimac.platform.bukkit.scheduler.folia;
import ac.grim.grimac.api.plugin.GrimPlugin;
import ac.grim.grimac.platform.api.scheduler.RegionScheduler;
import ac.grim.grimac.platform.api.scheduler.TaskHandle;
import ac.grim.grimac.platform.api.world.PlatformWorld;
import ac.grim.grimac.platform.bukkit.GrimACBukkitLoaderPlugin;
import ac.grim.grimac.platform.bukkit.world.BukkitPlatformWorld;
import ac.grim.grimac.utils.math.Location;
import org.bukkit.Bukkit;
import org.jetbrains.annotations.NotNull;
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);
}
@Override
public void execute(@NotNull GrimPlugin plugin, @NotNull Location location, @NotNull Runnable run) {
execute(plugin, location.getWorld(), location.getBlockX() >> 4, location.getBlockZ() >> 4, run);
}
@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()));
}
@Override
public TaskHandle run(@NotNull GrimPlugin plugin, @NotNull Location location, @NotNull Runnable task) {
return run(plugin, location.getWorld(), location.getBlockX() >> 4, location.getBlockZ() >> 4, task);
}
@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));
}
@Override
public TaskHandle runDelayed(@NotNull GrimPlugin plugin, @NotNull Location location, @NotNull Runnable task, long delayTicks) {
return runDelayed(plugin, location.getWorld(), location.getBlockX() >> 4, location.getBlockZ() >> 4, task, delayTicks);
}
@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));
}
@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);
}
}
@@ -0,0 +1,28 @@
package ac.grim.grimac.platform.bukkit.scheduler.folia;
import ac.grim.grimac.platform.api.scheduler.TaskHandle;
import io.papermc.paper.threadedregions.scheduler.ScheduledTask;
public class FoliaTaskHandle implements TaskHandle {
private final ScheduledTask task;
public FoliaTaskHandle(ScheduledTask task) {
this.task = task;
}
@Override
public boolean isSync() {
return false;
}
@Override
public boolean getCancelled() {
return task.isCancelled();
}
@Override
public void cancel() {
task.cancel();
}
}
@@ -0,0 +1,89 @@
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;
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 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) {
return sender instanceof Player ? sender.getName() : Sender.CONSOLE_NAME;
}
@Override
protected UUID getUniqueId(CommandSender sender) {
return sender instanceof Player ? ((Player) sender).getUniqueId() : Sender.CONSOLE_UUID;
}
@Override
protected void sendMessage(CommandSender sender, String message) {
sender.sendMessage(message);
}
@Override
protected void sendMessage(CommandSender sender, Component message) {
// we can safely send async for players and the console - otherwise, send it sync
if (sender instanceof Player || sender instanceof ConsoleCommandSender || sender instanceof RemoteConsoleCommandSender) {
this.audiences.sender(sender).sendMessage(message);
} else {
GrimAPI.INSTANCE.getScheduler().getGlobalRegionScheduler().run(
GrimAPI.INSTANCE.getGrimPlugin(),
() -> this.audiences.sender(sender).sendMessage(message)
);
}
}
@Override
protected boolean hasPermission(CommandSender sender, String node) {
return sender.hasPermission(node);
}
@Override
protected boolean hasPermission(CommandSender sender, String node, boolean defaultIfUnset) {
return sender.hasPermission(new Permission(node, defaultIfUnset ? PermissionDefault.TRUE : PermissionDefault.FALSE));
}
@Override
protected void performCommand(CommandSender sender, String command) {
throw new UnsupportedOperationException();
}
@Override
protected boolean isConsole(CommandSender sender) {
return sender instanceof ConsoleCommandSender || sender instanceof RemoteConsoleCommandSender;
}
@Override
protected boolean isPlayer(CommandSender sender) {
return sender instanceof Player;
}
@Override
public @NonNull Sender map(@NonNull CommandSender base) {
return this.wrap(base);
}
@Override
public @NonNull CommandSender reverse(@NonNull Sender mapped) {
return this.unwrap(mapped);
}
}
@@ -1,4 +1,4 @@
package ac.grim.grimac.utils.anticheat;
package ac.grim.grimac.platform.bukkit.utils.anticheat;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
@@ -9,6 +9,8 @@ import java.lang.reflect.Method;
public class MultiLibUtil {
public final static Method externalPlayerMethod = 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 {
@@ -20,7 +22,7 @@ public class MultiLibUtil {
// 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 || (PacketEvents.getAPI().getServerManager().getVersion().isOlderThan(ServerVersion.V_1_18))) return false;
if (externalPlayerMethod == null || IS_PRE_1_18) return false;
try {
return (boolean) externalPlayerMethod.invoke(player);
} catch (Exception e) {
@@ -0,0 +1,22 @@
package ac.grim.grimac.platform.bukkit.utils.blockstate;
import com.github.retrooper.packetevents.protocol.world.BlockFace;
public class BukkitBlockFaceHelper {
/**
* 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;
case SOUTH -> BlockFace.SOUTH;
case WEST -> BlockFace.WEST;
case EAST -> BlockFace.EAST;
case UP -> BlockFace.UP;
case DOWN -> BlockFace.DOWN;
default -> BlockFace.OTHER;
};
}
}
@@ -0,0 +1,62 @@
package ac.grim.grimac.platform.bukkit.utils.convert;
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.player.GameMode;
public class BukkitConversionUtils {
public static org.bukkit.Location toBukkitLocation(Location location) {
return new org.bukkit.Location(((BukkitPlatformWorld) location.getWorld()).getBukkitWorld(), location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch());
}
public static GameMode fromBukkitGameMode(org.bukkit.GameMode gameMode) {
switch (gameMode) {
case CREATIVE:
return GameMode.CREATIVE;
case SURVIVAL:
return GameMode.SURVIVAL;
case ADVENTURE:
return GameMode.ADVENTURE;
case SPECTATOR:
return GameMode.SPECTATOR;
default:
throw new IllegalStateException();
}
}
public static org.bukkit.GameMode toBukkitGameMode(GameMode gameMode) {
switch (gameMode) {
case CREATIVE:
return org.bukkit.GameMode.CREATIVE;
case SURVIVAL:
return org.bukkit.GameMode.SURVIVAL;
case ADVENTURE:
return org.bukkit.GameMode.ADVENTURE;
case SPECTATOR:
return org.bukkit.GameMode.SPECTATOR;
default:
throw new IllegalStateException();
}
}
/**
* Converts this enum to a Bukkit PermissionDefault.
*
* @return The corresponding Bukkit PermissionDefault.
*/
public static org.bukkit.permissions.PermissionDefault toBukkitPermissionDefault(PermissionDefaultValue permissionDefaultValue) {
switch (permissionDefaultValue) {
case TRUE:
return org.bukkit.permissions.PermissionDefault.TRUE;
case FALSE:
return org.bukkit.permissions.PermissionDefault.FALSE;
case OP:
return org.bukkit.permissions.PermissionDefault.OP;
case NOT_OP:
return org.bukkit.permissions.PermissionDefault.NOT_OP;
default:
throw new IllegalStateException();
}
}
}
@@ -1,4 +1,4 @@
package ac.grim.grimac.manager.init.start;
package ac.grim.grimac.platform.bukkit.utils.placeholder;
import ac.grim.grimac.GrimAPI;
import ac.grim.grimac.api.GrimUser;
@@ -14,13 +14,14 @@ import java.util.Set;
import java.util.function.Function;
public class PlaceholderAPIExpansion extends PlaceholderExpansion {
@Override
public @NotNull String getIdentifier() {
return "grim";
}
public @NotNull String getAuthor() {
return String.join(", ", GrimAPI.INSTANCE.getPlugin().getDescription().getAuthors());
return String.join(", ", GrimAPI.INSTANCE.getGrimPlugin().getDescription().getAuthors());
}
@Override
@@ -28,6 +29,11 @@ public class PlaceholderAPIExpansion extends PlaceholderExpansion {
return GrimAPI.INSTANCE.getExternalAPI().getGrimVersion();
}
@Override
public boolean persist() {
return true;
}
@Override
public @NotNull List<String> getPlaceholders() {
Set<String> placeholders = GrimAPI.INSTANCE.getExternalAPI().getStaticReplacements().keySet();
@@ -47,7 +53,7 @@ public class PlaceholderAPIExpansion extends PlaceholderExpansion {
}
if (offlinePlayer instanceof Player player) {
GrimPlayer grimPlayer = GrimAPI.INSTANCE.getPlayerDataManager().getPlayer(player);
GrimPlayer grimPlayer = GrimAPI.INSTANCE.getPlayerDataManager().getPlayer(player.getUniqueId());
if (grimPlayer == null) return null;
for (Map.Entry<String, Function<GrimUser, String>> entry : GrimAPI.INSTANCE.getExternalAPI().getVariableReplacements().entrySet()) {
@@ -0,0 +1,52 @@
package ac.grim.grimac.platform.bukkit.utils.reflection;
import ac.grim.grimac.platform.bukkit.GrimACBukkitLoaderPlugin;
import ac.grim.grimac.utils.anticheat.LogUtil;
import ac.grim.grimac.utils.reflection.ReflectionUtils;
import org.bukkit.Location;
import org.bukkit.entity.Entity;
import org.bukkit.event.Event;
import org.bukkit.event.EventPriority;
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 CompletableFuture<Boolean> teleportAsync(final Entity entity, final Location location) {
if (PAPER) {
return entity.teleportAsync(location);
} else {
return 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);
}
}
return false;
}
}
@@ -0,0 +1,31 @@
package ac.grim.grimac.platform.bukkit.world;
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 org.bukkit.Chunk;
import org.bukkit.block.Block;
import org.bukkit.block.data.BlockData;
import org.jetbrains.annotations.NotNull;
import java.util.HashMap;
public class BukkitPlatformChunk implements PlatformChunk {
private static final HashMap<BlockData, Integer> blockDataToId = 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;
}
@Override
public int getBlockID(int x, int y, int z) {
Block block = chunk.getBlock(x, y, z);
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();
}
}
@@ -0,0 +1,53 @@
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.protocol.world.states.WrappedBlockState;
import io.github.retrooper.packetevents.util.SpigotConversionUtil;
import lombok.Getter;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.UUID;
@Getter
public class BukkitPlatformWorld implements PlatformWorld {
private final World bukkitWorld;
public BukkitPlatformWorld(@NotNull World world) {
this.bukkitWorld = world;
}
@Override
public boolean isChunkLoaded(int chunkX, int chunkZ) {
return bukkitWorld.isChunkLoaded(chunkX, chunkZ);
}
@Override
public WrappedBlockState getBlockAt(int x, int y, int z) {
return SpigotConversionUtil.fromBukkitBlockData(bukkitWorld.getBlockAt(x, y, z).getBlockData());
}
@Override
public String getName() {
return bukkitWorld.getName();
}
@Override
public @Nullable UUID getUID() {
return this.bukkitWorld.getUID();
}
@Override
public PlatformChunk getChunkAt(int currChunkX, int currChunkZ) {
return new BukkitPlatformChunk(bukkitWorld.getChunkAt(currChunkX, currChunkZ));
}
@Override
public boolean isLoaded() {
return Bukkit.getWorld(bukkitWorld.getUID()) != null;
}
}
+54
View File
@@ -0,0 +1,54 @@
plugins {
`maven-publish`
grim.`base-conventions`
}
repositories {
mavenLocal()
maven {
name = "papermc"
url = uri("https://repo.papermc.io/repository/maven-public/")
}
maven("https://hub.spigotmc.org/nexus/content/repositories/snapshots/") // Spigot
maven("https://jitpack.io/") { // Grim API
content {
includeGroup("com.github.grimanticheat")
}
}
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://repo.codemc.io/repository/maven-releases/") // PacketEvents
maven("https://repo.codemc.io/repository/maven-snapshots/")
maven("https://s01.oss.sonatype.org/content/repositories/snapshots/")
mavenCentral()
// FastUtil, Discord-Webhooks
}
dependencies {
api(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") {
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")
api(libs.discord.webhooks)
api(libs.fastutil)
api(libs.adventure.text.minimessage)
api(libs.jetbrains.annotations)
// Used for local testing:
// api("ac.grim.grimac:GrimAPI:1.0")
api("com.github.grimanticheat:grimapi:8507e14a73")
compileOnly(libs.floodgate.api)
compileOnly(libs.via.version.api)
compileOnly(libs.netty)
}
publishing.publications.create<MavenPublication>("maven") {
from(components["java"])
}
@@ -0,0 +1,141 @@
package ac.grim.grimac;
import ac.grim.grimac.api.plugin.GrimPlugin;
import ac.grim.grimac.manager.AlertManagerImpl;
import ac.grim.grimac.manager.DiscordManager;
import ac.grim.grimac.manager.InitManager;
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.platform.api.Platform;
import ac.grim.grimac.platform.api.PlatformLoader;
import ac.grim.grimac.platform.api.PlatformServer;
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.SenderFactory;
import ac.grim.grimac.utils.anticheat.PlayerDataManager;
import ac.grim.grimac.utils.reflection.ReflectionUtils;
import lombok.Getter;
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();
private final Platform platform = detectPlatform();
private final BaseConfigManager configManager;
private final AlertManagerImpl alertManager;
private final SpectateManager spectateManager;
private final DiscordManager discordManager;
private final PlayerDataManager playerDataManager;
private final TickManager tickManager;
private final GrimExternalAPI externalAPI;
private PlatformLoader loader;
private InitManager initManager;
private boolean initialized = false;
private GrimAPI() {
this.configManager = new BaseConfigManager();
this.alertManager = new AlertManagerImpl();
this.spectateManager = new SpectateManager();
this.discordManager = new DiscordManager();
this.playerDataManager = new PlayerDataManager();
this.tickManager = new TickManager();
this.externalAPI = new GrimExternalAPI(this);
}
private static Platform detectPlatform() {
final Map<String, Platform> platforms = Collections.unmodifiableMap(new HashMap<>() {{
put("io.papermc.paper.threadedregions.RegionizedServer", Platform.FOLIA);
put("org.bukkit.Bukkit", Platform.BUKKIT);
put("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!"));
}
public void load(PlatformLoader platformLoader, Initable... platformSpecificInitables) {
this.loader = platformLoader;
this.initManager = new InitManager(loader.getPacketEvents(), loader::getCommandManager, platformSpecificInitables);
this.initManager.load();
this.initialized = true;
}
public void start() {
checkInitialized();
initManager.start();
}
public void stop() {
checkInitialized();
initManager.stop();
}
public PlatformScheduler getScheduler() {
return loader.getScheduler();
}
public PlatformPlayerFactory getPlatformPlayerFactory() {
return loader.getPlatformPlayerFactory();
}
public ParserDescriptorFactory getParserDescriptors() {
return loader.getParserDescriptorFactory();
}
public InitManager getInitManager() {
return initManager;
}
public GrimPlugin getGrimPlugin() {
return loader.getPlugin();
}
public SenderFactory<?> getSenderFactory() {
return loader.getSenderFactory();
}
public ItemResetHandler getItemResetHandler() {
return loader.getItemResetHandler();
}
public PlatformPluginManager getPluginManager() {
return loader.getPluginManager();
}
public PlatformServer getPlatformServer() {
return loader.getPlatformServer();
}
public @NotNull MessagePlaceHolderManager getMessagePlaceHolderManager() {
return loader.getMessagePlaceHolderManager();
}
private void checkInitialized() {
if (!initialized) {
throw new IllegalStateException("GrimAPI has not been initialized!");
}
}
public PermissionRegistrationManager getPermissionManager() {
return loader.getPermissionManager();
}
public Platform getPlatform() {
return platform;
}
}
@@ -1,24 +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.events.GrimReloadEvent;
import ac.grim.grimac.api.event.EventBus;
import ac.grim.grimac.api.event.events.GrimReloadEvent;
import ac.grim.grimac.manager.config.ConfigManagerFileImpl;
import ac.grim.grimac.manager.init.Initable;
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.common.ConfigReloadObserver;
import com.github.retrooper.packetevents.netty.channel.ChannelHelper;
import io.github.retrooper.packetevents.util.SpigotReflectionUtil;
import io.github.retrooper.packetevents.util.folia.FoliaScheduler;
import lombok.Getter;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.entity.Player;
import org.bukkit.plugin.PluginDescriptionFile;
import org.bukkit.plugin.ServicePriority;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.jetbrains.annotations.Nullable;
import java.util.Map;
@@ -29,33 +26,32 @@ import java.util.function.Function;
//This is used for grim's external API. It has its own class just for organization.
public class GrimExternalAPI implements GrimAbstractAPI, ConfigReloadObserver, Initable {
public class GrimExternalAPI implements GrimAbstractAPI, ConfigReloadObserver, StartableInitable {
private final GrimAPI api;
@Getter
private final Map<String, Function<GrimUser, String>> variableReplacements = new ConcurrentHashMap<>();
@Getter
private final Map<String, String> staticReplacements = new ConcurrentHashMap<>();
private final Map<String, Function<Object, Object>> functions = new ConcurrentHashMap<>();
private final ConfigManagerFileImpl configManagerFile = new ConfigManagerFileImpl();
private ConfigManager configManager = null;
private boolean started = false;
public GrimExternalAPI(GrimAPI api) {
this.api = api;
}
@Nullable
@Override
public GrimUser getGrimUser(Player player) {
return api.getPlayerDataManager().getPlayer(player);
public @NonNull EventBus getEventBus() {
return api.getPluginManager().getEventBus();
}
@Override
public @Nullable GrimUser getGrimUser(UUID uuid) {
Player player = Bukkit.getPlayer(uuid);
if (player == null) return null;
return getGrimUser(player);
return api.getPlayerDataManager().getPlayer(uuid);
}
@Getter
private final Map<String, Function<GrimUser, String>> variableReplacements = new ConcurrentHashMap<>();
@Getter
private final Map<String, String> staticReplacements = new ConcurrentHashMap<>();
public String replaceVariables(GrimUser user, String content) {
for (Map.Entry<String, String> entry : staticReplacements.entrySet()) {
content = content.replace(entry.getKey(), entry.getValue());
@@ -86,12 +82,10 @@ public class GrimExternalAPI implements GrimAbstractAPI, ConfigReloadObserver, I
@Override
public String getGrimVersion() {
PluginDescriptionFile description = GrimAPI.INSTANCE.getPlugin().getDescription();
GrimPluginDescription description = GrimAPI.INSTANCE.getGrimPlugin().getDescription();
return description.getVersion();
}
private final Map<String, Function<Object, Object>> functions = new ConcurrentHashMap<>();
@Override
public void registerFunction(String key, Function<Object, Object> function) {
if (function == null) {
@@ -126,14 +120,10 @@ public class GrimExternalAPI implements GrimAbstractAPI, ConfigReloadObserver, I
return GrimAPI.INSTANCE.getTickManager().currentTick;
}
private ConfigManager configManager = null;
private final ConfigManagerFileImpl configManagerFile = new ConfigManagerFileImpl();
private boolean started = false;
// on load, load the config & register the service
public void load() {
reload(configManagerFile);
Bukkit.getServicesManager().register(GrimAbstractAPI.class, this, GrimAPI.INSTANCE.getPlugin(), ServicePriority.Normal);
api.getLoader().registerAPIService();
}
// handles any config loading that's needed to be done after load
@@ -150,8 +140,8 @@ public class GrimExternalAPI implements GrimAbstractAPI, ConfigReloadObserver, I
@Override
public void reload(ConfigManager config) {
if (config.isLoadedAsync() && started) {
FoliaScheduler.getAsyncScheduler().runNow(GrimAPI.INSTANCE.getPlugin(),
o -> successfulReload(config));
GrimAPI.INSTANCE.getScheduler().getAsyncScheduler().runNow(GrimAPI.INSTANCE.getGrimPlugin(),
() -> successfulReload(config));
} else {
successfulReload(config);
}
@@ -161,8 +151,8 @@ public class GrimExternalAPI implements GrimAbstractAPI, ConfigReloadObserver, I
public CompletableFuture<Boolean> reloadAsync(ConfigManager config) {
if (config.isLoadedAsync() && started) {
CompletableFuture<Boolean> future = new CompletableFuture<>();
FoliaScheduler.getAsyncScheduler().runNow(GrimAPI.INSTANCE.getPlugin(),
o -> future.complete(successfulReload(config)));
GrimAPI.INSTANCE.getScheduler().getAsyncScheduler().runNow(GrimAPI.INSTANCE.getGrimPlugin(),
() -> future.complete(successfulReload(config)));
return future;
}
return CompletableFuture.completedFuture(successfulReload(config));
@@ -174,14 +164,16 @@ public class GrimExternalAPI implements GrimAbstractAPI, ConfigReloadObserver, I
GrimAPI.INSTANCE.getConfigManager().load(config);
if (started) GrimAPI.INSTANCE.getConfigManager().start();
onReload(config);
if (started) FoliaScheduler.getAsyncScheduler().runNow(GrimAPI.INSTANCE.getPlugin(),
o -> Bukkit.getPluginManager().callEvent(new GrimReloadEvent(true)));
if (started)
GrimAPI.INSTANCE.getScheduler().getAsyncScheduler().runNow(GrimAPI.INSTANCE.getGrimPlugin(),
() -> GrimAPI.INSTANCE.getPluginManager().callEvent(new GrimReloadEvent(true)));
return true;
} catch (Exception e) {
e.printStackTrace();
}
if (started) FoliaScheduler.getAsyncScheduler().runNow(GrimAPI.INSTANCE.getPlugin(),
o -> Bukkit.getPluginManager().callEvent(new GrimReloadEvent(false)));
if (started)
GrimAPI.INSTANCE.getScheduler().getAsyncScheduler().runNow(GrimAPI.INSTANCE.getGrimPlugin(),
() -> GrimAPI.INSTANCE.getPluginManager().callEvent(new GrimReloadEvent(false)));
return false;
}
@@ -196,8 +188,8 @@ public class GrimExternalAPI implements GrimAbstractAPI, ConfigReloadObserver, I
// Update variables
updateVariables();
// Restart
GrimAPI.INSTANCE.getDiscordManager().start();
GrimAPI.INSTANCE.getSpectateManager().start();
GrimAPI.INSTANCE.getDiscordManager().reload();
GrimAPI.INSTANCE.getSpectateManager().reload();
// Don't reload players if the plugin hasn't started yet
if (!started) return;
// Reload checks for all players
@@ -217,11 +209,10 @@ public class GrimExternalAPI implements GrimAbstractAPI, ConfigReloadObserver, I
variableReplacements.putIfAbsent("%h_sensitivity%", user -> ((int) Math.round(user.getHorizontalSensitivity() * 200)) + "");
variableReplacements.putIfAbsent("%v_sensitivity%", user -> ((int) Math.round(user.getVerticalSensitivity() * 200)) + "");
variableReplacements.putIfAbsent("%fast_math%", user -> !user.isVanillaMath() + "");
variableReplacements.putIfAbsent("%tps%", user -> String.format("%.2f", SpigotReflectionUtil.getTPS()));
variableReplacements.putIfAbsent("%tps%", user -> String.format("%.2f", GrimAPI.INSTANCE.getPlatformServer().getTPS()));
variableReplacements.putIfAbsent("%version%", GrimUser::getVersionName);
// static variables
staticReplacements.put("%prefix%", ChatColor.translateAlternateColorCodes('&', GrimAPI.INSTANCE.getConfigManager().getPrefix()));
staticReplacements.put("%prefix%", ChatUtil.translateAlternateColorCodes('&', GrimAPI.INSTANCE.getConfigManager().getPrefix()));
staticReplacements.putIfAbsent("%grim_version%", getGrimVersion());
}
}
@@ -3,21 +3,22 @@ package ac.grim.grimac.checks;
import ac.grim.grimac.GrimAPI;
import ac.grim.grimac.api.AbstractCheck;
import ac.grim.grimac.api.config.ConfigManager;
import ac.grim.grimac.api.events.FlagEvent;
import ac.grim.grimac.api.event.events.FlagEvent;
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 io.github.retrooper.packetevents.util.folia.FoliaScheduler;
import com.google.common.base.Preconditions;
import lombok.Getter;
import lombok.Setter;
import org.bukkit.Bukkit;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.jetbrains.annotations.NotNull;
// 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 {
protected final GrimPlayer player;
protected @NonNull final GrimPlayer player;
public double violations;
private double decay;
@@ -30,14 +31,15 @@ public class Check extends GrimProcessor implements AbstractCheck {
private String description;
private boolean experimental;
@Setter
private boolean isEnabled;
private @Setter boolean isEnabled;
private boolean exemptPermission;
private boolean noSetbackPermission;
private boolean noModifyPacketPermission;
private long lastViolationTime;
public Check(final GrimPlayer player) {
public Check(final @NotNull GrimPlayer player) {
Preconditions.checkArgument(player != null);
this.player = player;
final CheckData checkData = this.getClass().getAnnotation(CheckData.class);
@@ -62,15 +64,15 @@ public class Check extends GrimProcessor implements AbstractCheck {
}
public void updatePermissions() {
if (player.bukkitPlayer == null || checkName == null) return;
FoliaScheduler.getEntityScheduler().run(
player.bukkitPlayer,
GrimAPI.INSTANCE.getPlugin(),
t -> {
final String id = checkName.toLowerCase();
exemptPermission = player.bukkitPlayer.hasPermission("grim.exempt." + id);
noSetbackPermission = player.bukkitPlayer.hasPermission("grim.nosetback." + id);
noModifyPacketPermission = player.bukkitPlayer.hasPermission("grim.nomodifypacket." + id);
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);
},
() -> {}
);
@@ -92,14 +94,12 @@ public class Check extends GrimProcessor implements AbstractCheck {
return flag("");
}
private long lastViolationTime;
public final boolean flag(String verbose) {
if (player.disableGrim || (experimental && !player.isExperimentalChecks()) || exemptPermission)
return false; // Avoid calling event if disabled
FlagEvent event = new FlagEvent(player, this, verbose);
Bukkit.getPluginManager().callEvent(event);
GrimAPI.INSTANCE.getPluginManager().callEvent(event);
if (event.isCancelled()) return false;
player.punishmentManager.handleViolation(this);
@@ -116,6 +116,18 @@ public class Check extends GrimProcessor implements AbstractCheck {
return false;
}
public final boolean flagAndAlertWithSetback() {
return flagAndAlertWithSetback("");
}
public final boolean flagAndAlertWithSetback(String verbose) {
if (flagAndAlert(verbose)) {
setbackIfAboveSetbackVL();
return true;
}
return false;
}
public final void reward() {
violations = Math.max(0, violations - decay);
}
@@ -8,15 +8,15 @@ import ac.grim.grimac.utils.anticheat.update.RotationUpdate;
@CheckData(name = "AimDuplicateLook")
public class AimDuplicateLook extends Check implements RotationCheck {
boolean exempt = false;
public AimDuplicateLook(GrimPlayer playerData) {
super(playerData);
}
boolean exempt = false;
@Override
public void process(final RotationUpdate rotationUpdate) {
if (player.packetStateData.lastPacketWasTeleport || player.packetStateData.lastPacketWasOnePointSeventeenDuplicate || player.compensatedEntities.getSelf().getRiding() != null) {
if (player.packetStateData.lastPacketWasTeleport || player.packetStateData.lastPacketWasOnePointSeventeenDuplicate || player.compensatedEntities.self.getRiding() != null) {
exempt = true;
return;
}
@@ -20,8 +20,10 @@ public class AimModulo360 extends Check implements RotationCheck {
@Override
public void process(final RotationUpdate rotationUpdate) {
// Exempt for teleport or entering a vehicle due to rotation reset
if (player.packetStateData.lastPacketWasTeleport || player.vehicleData.wasVehicleSwitch) {
// Exempt for teleport, entering a vehicle due to rotation reset or
// after forced, client-sided rotation change after interacting with a horse (not necessarily mounting it)
if (player.packetStateData.lastPacketWasTeleport || player.vehicleData.wasVehicleSwitch
|| player.packetStateData.horseInteractCausedForcedRotation) {
lastDeltaYaw = rotationUpdate.getDeltaXRot();
return;
}
@@ -13,26 +13,26 @@ public class AimProcessor extends Check implements RotationCheck {
private static final int SIGNIFICANT_SAMPLES_THRESHOLD = 15;
private static final int TOTAL_SAMPLES_THRESHOLD = 80;
public double sensitivityX;
public double sensitivityY;
public double divisorX;
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;
public AimProcessor(GrimPlayer playerData) {
super(playerData);
}
RunningMode xRotMode = new RunningMode(TOTAL_SAMPLES_THRESHOLD);
RunningMode yRotMode = new RunningMode(TOTAL_SAMPLES_THRESHOLD);
float lastXRot;
float lastYRot;
public double sensitivityX;
public double sensitivityY;
public double divisorX;
public double divisorY;
public double modeX, modeY;
public double deltaDotsX, deltaDotsY;
public static double convertToSensitivity(double var13) {
double var11 = var13 / 0.15F / 8.0D;
double var9 = Math.cbrt(var11);
return (var9 - 0.2f) / 0.6f;
}
@Override
public void process(final RotationUpdate rotationUpdate) {
@@ -75,10 +75,4 @@ public class AimProcessor extends Check implements RotationCheck {
this.deltaDotsX = deltaXRot / modeX;
this.deltaDotsY = deltaYRot / modeY;
}
public static double convertToSensitivity(double var13) {
double var11 = var13 / 0.15F / 8.0D;
double var9 = Math.cbrt(var11);
return (var9 - 0.2f) / 0.6f;
}
}
@@ -5,8 +5,8 @@ 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.player.GameMode;
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;
@CheckData(name = "BadPacketsC", description = "Interacted with self")
@@ -28,7 +28,7 @@ public class BadPacketsE extends Check implements PacketCheck {
} else if (WrapperPlayClientPlayerFlying.isFlying(event.getPacketType())) {
noReminderTicks++;
} else if (event.getPacketType() == PacketType.Play.Client.STEER_VEHICLE
|| (isViaPleaseStopUsingProtocolHacksOnYourServer && player.compensatedEntities.getSelf().inVehicle())) {
|| (isViaPleaseStopUsingProtocolHacksOnYourServer && player.inVehicle())) {
noReminderTicks = 0; // Exempt vehicles
}
@@ -41,7 +41,8 @@ public class BadPacketsH extends Check implements PacketCheck {
// I will simply disable this check for 1.8- clients on 1.9+ servers as I can't be bothered to find a way around this.
// Stop supporting such old clients on modern servers!
if (player.getClientVersion().isOlderThan(ClientVersion.V_1_9)
&& PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_9)) return;
&& PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_9))
return;
if (!sentAnimation && flagAndAlert() && shouldModifyPackets()) {
event.setCancelled(true);
@@ -24,7 +24,8 @@ public class BadPacketsL extends Check implements PacketCheck {
if (event.getPacketType() == PacketType.Play.Client.PLAYER_DIGGING) {
final WrapperPlayClientPlayerDigging packet = new WrapperPlayClientPlayerDigging(event);
if (packet.getAction() == DiggingAction.START_DIGGING || packet.getAction() == DiggingAction.FINISHED_DIGGING || packet.getAction() == DiggingAction.CANCELLED_DIGGING) return;
if (packet.getAction() == DiggingAction.START_DIGGING || packet.getAction() == DiggingAction.FINISHED_DIGGING || packet.getAction() == DiggingAction.CANCELLED_DIGGING)
return;
// 1.8 and above clients always send digging packets that aren't used for digging at 0, 0, 0, facing DOWN
// 1.7 and below clients do the same, except use SOUTH for RELEASE_USE_ITEM
@@ -13,14 +13,14 @@ import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientIn
@CheckData(name = "BadPacketsM")
public class BadPacketsM extends Check implements PacketCheck {
public BadPacketsM(final GrimPlayer player) {
super(player);
}
// 1.7 players do not send INTERACT_AT, so we cannot check them
private final boolean exempt = player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_7_10);
private boolean sentInteractAt = false;
public BadPacketsM(final GrimPlayer player) {
super(player);
}
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (event.getPacketType() == PacketType.Play.Client.INTERACT_ENTITY && !exempt) {
@@ -14,13 +14,13 @@ import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerOp
@CheckData(name = "BadPacketsP", experimental = true)
public class BadPacketsP extends Check implements PacketCheck {
private int containerType = -1;
private int containerId = -1;
public BadPacketsP(GrimPlayer playerData) {
super(playerData);
}
private int containerType = -1;
private int containerId = -1;
@Override
public void onPacketSend(final PacketSendEvent event) {
if (event.getPacketType() == PacketType.Play.Server.OPEN_WINDOW) {
@@ -10,22 +10,22 @@ import com.github.retrooper.packetevents.protocol.player.GameMode;
@CheckData(name = "BadPacketsR", decay = 0.25, experimental = true)
public class BadPacketsR extends Check implements PacketCheck {
public BadPacketsR(final GrimPlayer player) {
super(player);
}
private int positions = 0;
private long clock = 0;
private long lastTransTime;
private int oldTransId = 0;
public BadPacketsR(final GrimPlayer player) {
super(player);
}
@Override
public void onPacketReceive(final PacketReceiveEvent event) {
if (isTransaction(event.getPacketType()) && player.packetStateData.lastTransactionPacketWasValid) {
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.getSelf().isDead) {
if (positions == 0 && clock != 0 && player.gamemode != GameMode.SPECTATOR && !player.compensatedEntities.self.isDead) {
flag("time=" + ms + "ms, " + "lst=" + diff + "ms, positions=" + positions);
} else {
reward();
@@ -39,7 +39,7 @@ public class BadPacketsR extends Check implements PacketCheck {
}
//
if ((event.getPacketType() == PacketType.Play.Client.PLAYER_POSITION_AND_ROTATION ||
event.getPacketType() == PacketType.Play.Client.PLAYER_POSITION) && !player.compensatedEntities.getSelf().inVehicle()) {
event.getPacketType() == PacketType.Play.Client.PLAYER_POSITION) && !player.inVehicle()) {
positions++;
} else if ((event.getPacketType() == PacketType.Play.Client.STEER_VEHICLE || event.getPacketType() == PacketType.Play.Client.VEHICLE_MOVE)
&& player.inVehicle()) {
@@ -14,10 +14,6 @@ import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientIn
@CheckData(name = "BadPacketsT")
public class BadPacketsT extends Check implements PacketCheck {
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
@@ -26,6 +22,10 @@ public class BadPacketsT extends Check implements PacketCheck {
private final double minVerticalDisplacement = -0.0001 - (hasLegacyExpansion ? 0.1 : 0);
private final double maxVerticalDisplacement = 1.8001 + (hasLegacyExpansion ? 0.1 : 0);
public BadPacketsT(final GrimPlayer player) {
super(player);
}
@Override
public void onPacketReceive(final PacketReceiveEvent event) {
if (event.getPacketType().equals(PacketType.Play.Client.INTERACT_ENTITY)) {
@@ -12,12 +12,12 @@ import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPl
@CheckData(name = "BadPacketsV", description = "Did not move far enough", experimental = true)
public class BadPacketsV extends Check implements PacketCheck {
private int noReminderTicks;
public BadPacketsV(GrimPlayer player) {
super(player);
}
private int noReminderTicks;
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (!player.canSkipTicks() && isTickPacket(event.getPacketType())) {
@@ -12,14 +12,14 @@ import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientEn
@CheckData(name = "BadPacketsX", experimental = true)
public class BadPacketsX extends Check implements PostPredictionCheck {
public BadPacketsX(GrimPlayer player) {
super(player);
}
private boolean sprint;
private boolean sneak;
private int flags;
public BadPacketsX(GrimPlayer player) {
super(player);
}
@Override
public void onPredictionComplete(final PredictionComplete predictionComplete) {
if (!player.canSkipTicks()) {
@@ -33,9 +33,7 @@ public class BadPacketsX extends Check implements PostPredictionCheck {
if (player.isTickingReliablyFor(3)) {
for (; flags > 0; flags--) {
if (flagAndAlert()) {
setbackIfAboveSetbackVL();
}
flagAndAlertWithSetback();
}
}
@@ -19,7 +19,7 @@ public class BadPacketsY extends Check implements PacketCheck {
if (event.getPacketType() == PacketType.Play.Client.HELD_ITEM_CHANGE) {
final int slot = new WrapperPlayClientHeldItemChange(event).getSlot();
if (slot > 8 || slot < 0) { // ban
if (flagAndAlert("slot="+slot) && shouldModifyPackets()) {
if (flagAndAlert("slot=" + slot) && shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
}
@@ -12,12 +12,12 @@ 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.
@CheckData(name = "Baritone")
public class Baritone extends Check implements RotationCheck {
private int verbose;
public Baritone(GrimPlayer playerData) {
super(playerData);
}
private int verbose;
@Override
public void process(final RotationUpdate rotationUpdate) {
final HeadRotation from = rotationUpdate.getFrom();
@@ -12,20 +12,21 @@ 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;
@CheckData(name = "AirLiquidBreak", description = "Breaking a block that cannot be broken")
public class AirLiquidBreak extends Check implements BlockBreakCheck {
public final boolean noFireHitbox = player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_15_2);
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;
public AirLiquidBreak(GrimPlayer player) {
super(player);
}
private int lastTick;
private boolean didLastFlag;
private Vector3i lastBreakLoc;
private StateType lastBlockType;
public final boolean noFireHitbox = player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_15_2);
@Override
public void onBlockBreak(BlockBreak blockBreak) {
if (blockBreak.action != DiggingAction.START_DIGGING && blockBreak.action != DiggingAction.FINISHED_DIGGING)
@@ -6,11 +6,11 @@ import ac.grim.grimac.checks.type.BlockBreakCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.BlockBreak;
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
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;
import org.bukkit.util.Vector;
@CheckData(name = "FarBreak", description = "Breaking blocks too far away", experimental = true)
public class FarBreak extends Check implements BlockBreakCheck {
@@ -20,19 +20,20 @@ public class FarBreak extends Check implements BlockBreakCheck {
@Override
public void onBlockBreak(BlockBreak blockBreak) {
if (player.gamemode == GameMode.SPECTATOR || player.compensatedEntities.getSelf().inVehicle() || blockBreak.action == DiggingAction.CANCELLED_DIGGING) return; // falses
if (player.gamemode == GameMode.SPECTATOR || 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);
Vector eyes = new Vector(player.x, player.y + d, player.z);
Vector best = VectorUtils.cutBoxToVector(eyes, box);
Vector3dm eyes = new Vector3dm(player.x, player.y + d, player.z);
Vector3dm best = VectorUtils.cutBoxToVector(eyes, box);
min = Math.min(min, eyes.distanceSquared(best));
}
// getPickRange() determines this?
// With 1.20.5+ the new attribute determines creative mode reach using a modifier
double maxReach = player.compensatedEntities.getSelf().getAttributeValue(Attributes.BLOCK_INTERACTION_RANGE);
double maxReach = player.compensatedEntities.self.getAttributeValue(Attributes.BLOCK_INTERACTION_RANGE);
if (player.packetStateData.didLastMovementIncludePosition || player.canSkipTicks()) {
double threshold = player.getMovementThreshold();
maxReach += Math.hypot(threshold, threshold);
@@ -12,15 +12,23 @@ import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.player.DiggingAction;
import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState;
import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
import com.github.retrooper.packetevents.util.Vector3i;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerFlying;
import java.util.Set;
// Based loosely off of Hawk BlockBreakSpeedSurvival
// Also based loosely off of NoCheatPlus FastBreak
// Also based off minecraft wiki: https://minecraft.wiki/w/Breaking#Instant_breaking
@CheckData(name = "FastBreak", description = "Breaking blocks too quickly")
public class FastBreak extends Check implements BlockBreakCheck {
// For some reason these states flag and I don't know why.
// Better to just exempt to not annoy legit players.
private static final Set<StateType> EXEMPT_STATES = Set.of();
public FastBreak(GrimPlayer playerData) {
super(playerData);
}
@@ -45,7 +53,8 @@ public class FastBreak extends Check implements BlockBreakCheck {
WrappedBlockState block = blockBreak.block;
// Exempt all blocks that do not exist in the player version
if (WrappedBlockState.getDefaultState(player.getClientVersion(), block.getType()).getType() == StateTypes.AIR) {
final WrappedBlockState defaultState = WrappedBlockState.getDefaultState(player.getClientVersion(), block.getType());
if (defaultState.getType() == StateTypes.AIR || EXEMPT_STATES.contains(defaultState.getType())) {
return;
}
@@ -63,7 +72,7 @@ public class FastBreak extends Check implements BlockBreakCheck {
}
if (blockDelayBalance > 1000) { // If more than a second of advantage
if (flagAndAlert("Delay=" + breakDelay) && shouldModifyPackets()) {
if (flagAndAlert("delay=" + breakDelay + "ms, type=" + blockBreak.block.getType()) && shouldModifyPackets()) {
blockBreak.cancel();
}
}
@@ -85,12 +94,13 @@ public class FastBreak extends Check implements BlockBreakCheck {
}
if (blockBreakBalance > 1000) { // If more than a second of advantage
if (flagAndAlert("Diff=" + diff + ",Balance=" + blockBreakBalance) && shouldModifyPackets()) {
if (flagAndAlert("diff=" + diff + "ms, balance=" + blockBreakBalance + "ms, type=" + blockBreak.block.getType()) && shouldModifyPackets()) {
blockBreak.cancel();
}
}
lastFinishBreak = System.currentTimeMillis();
// also set start time because the breaking netcode is fucked on 1.14.4+
lastFinishBreak = startBreak = System.currentTimeMillis();
}
}
@@ -18,16 +18,15 @@ import java.util.List;
@CheckData(name = "MultiBreak", experimental = true)
public class MultiBreak extends Check implements BlockBreakCheck {
public MultiBreak(GrimPlayer player) {
super(player);
}
private final List<String> flags = new ArrayList<>();
private boolean hasBroken;
private BlockFace lastFace;
private Vector3i lastPos;
public MultiBreak(GrimPlayer player) {
super(player);
}
@Override
public void onBlockBreak(BlockBreak blockBreak) {
if (blockBreak.action == DiggingAction.CANCELLED_DIGGING) {
@@ -11,13 +11,13 @@ import com.github.retrooper.packetevents.protocol.player.DiggingAction;
@CheckData(name = "NoSwingBreak", description = "Did not swing while breaking block", experimental = true)
public class NoSwingBreak extends Check implements BlockBreakCheck {
private boolean sentAnimation;
private boolean sentBreak;
public NoSwingBreak(GrimPlayer playerData) {
super(playerData);
}
private boolean sentAnimation;
private boolean sentBreak;
@Override
public void onBlockBreak(BlockBreak blockBreak) {
if (blockBreak.action != DiggingAction.CANCELLED_DIGGING) {
@@ -17,7 +17,7 @@ public class PositionBreakA extends Check implements BlockBreakCheck {
@Override
public void onBlockBreak(BlockBreak blockBreak) {
if (player.compensatedEntities.getSelf().inVehicle()
if (player.inVehicle()
|| blockBreak.action == DiggingAction.CANCELLED_DIGGING
|| blockBreak.block.getType() == StateTypes.REDSTONE_WIRE
) return;
@@ -11,13 +11,13 @@ import com.github.retrooper.packetevents.protocol.world.BlockFace;
@CheckData(name = "PositionBreakB", experimental = true)
public class PositionBreakB extends Check implements BlockBreakCheck {
private final int releaseFace = player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_8) ? 0 : 255;
private BlockFace lastFace;
public PositionBreakB(GrimPlayer player) {
super(player);
}
private BlockFace lastFace;
private final int releaseFace = player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_8) ? 0 : 255;
@Override
public void onBlockBreak(BlockBreak blockBreak) {
if (blockBreak.action == DiggingAction.START_DIGGING) {
@@ -0,0 +1,122 @@
package ac.grim.grimac.checks.impl.breaking;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.BlockBreakCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.BlockBreak;
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
import ac.grim.grimac.utils.data.Pair;
import ac.grim.grimac.utils.math.Vector3dm;
import ac.grim.grimac.utils.nmsutil.Ray;
import ac.grim.grimac.utils.nmsutil.ReachUtils;
import com.github.retrooper.packetevents.protocol.attribute.Attributes;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.player.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;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@CheckData(name = "RotationBreak", experimental = true)
public class RotationBreak extends Check implements BlockBreakCheck {
private double flagBuffer = 0; // If the player flags once, force them to play legit, or we will cancel the tick before.
private boolean ignorePost = false;
public RotationBreak(GrimPlayer player) {
super(player);
}
@Override
public void onBlockBreak(BlockBreak blockBreak) {
if (player.gamemode == GameMode.SPECTATOR)
return; // you don't send flying packets when spectating entities
if (player.inVehicle()) return; // falses
if (blockBreak.action == DiggingAction.CANCELLED_DIGGING) return; // falses
if (flagBuffer > 0 && !didRayTraceHit(blockBreak)) {
ignorePost = true;
// If the player hit and has flagged this check recently
if (flagAndAlert("pre-flying, action=" + blockBreak.action) && shouldModifyPackets()) {
blockBreak.cancel();
}
}
}
@Override
public void onPostFlyingBlockBreak(BlockBreak blockBreak) {
if (player.gamemode == GameMode.SPECTATOR)
return; // you don't send flying packets when spectating entities
if (player.inVehicle()) return; // falses
if (blockBreak.action == DiggingAction.CANCELLED_DIGGING) return; // falses
// Don't flag twice
if (ignorePost) {
ignorePost = false;
return;
}
if (didRayTraceHit(blockBreak)) {
flagBuffer = Math.max(0, flagBuffer - 0.1);
} else {
flagBuffer = 1;
flagAndAlert("post-flying, action=" + blockBreak.action);
}
}
private boolean didRayTraceHit(BlockBreak blockBreak) {
SimpleCollisionBox box = new SimpleCollisionBox(blockBreak.position);
final double[] possibleEyeHeights = player.getPossibleEyeHeights();
// Start checking if player is in the block
double minEyeHeight = Double.MAX_VALUE;
double maxEyeHeight = Double.MIN_VALUE;
for (double height : possibleEyeHeights) {
minEyeHeight = Math.min(minEyeHeight, height);
maxEyeHeight = Math.max(maxEyeHeight, height);
}
SimpleCollisionBox eyePositions = new SimpleCollisionBox(player.x, player.y + minEyeHeight, player.z, player.x, player.y + maxEyeHeight, player.z);
eyePositions.expand(player.getMovementThreshold());
// If the player is inside a block, then they can ray trace through the block and hit the other side of the block
if (eyePositions.isIntersected(box)) {
return true;
}
// End checking if the player is in the block
List<Vector3f> possibleLookDirs = new ArrayList<>(Arrays.asList(
new Vector3f(player.lastXRot, player.yRot, 0),
new Vector3f(player.xRot, player.yRot, 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));
}
// 1.7 players do not have any of these issues! They are always on the latest look vector
if (player.getClientVersion().isOlderThan(ClientVersion.V_1_8)) {
possibleLookDirs = Collections.singletonList(new Vector3f(player.xRot, player.yRot, 0));
}
final double distance = player.compensatedEntities.self.getAttributeValue(Attributes.BLOCK_INTERACTION_RANGE);
for (double d : possibleEyeHeights) {
for (Vector3f lookDir : possibleLookDirs) {
Vector3d starting = new Vector3d(player.x, player.y + d, player.z);
Ray trace = new Ray(player, starting.getX(), starting.getY(), starting.getZ(), lookDir.getX(), lookDir.getY());
Pair<Vector3dm, BlockFace> intercept = ReachUtils.calculateIntercept(box, trace.getOrigin(), trace.getPointAtDistance(distance));
if (intercept.first() != null) return true;
}
}
return false;
}
}
@@ -16,14 +16,14 @@ import static ac.grim.grimac.utils.nmsutil.BlockBreakSpeed.getBlockDamage;
@CheckData(name = "WrongBreak")
public class WrongBreak extends Check implements BlockBreakCheck {
private final int exemptedY = player.getClientVersion().isOlderThan(ClientVersion.V_1_8) ? 255 : (PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_14) ? -1 : 4095);
private boolean lastBlockWasInstantBreak = false;
private Vector3i lastBlock, lastCancelledBlock, lastLastBlock = null;
public WrongBreak(final GrimPlayer player) {
super(player);
}
private boolean lastBlockWasInstantBreak = false;
private Vector3i lastBlock, lastCancelledBlock, lastLastBlock = null;
private final int exemptedY = player.getClientVersion().isOlderThan(ClientVersion.V_1_8) ? 255 : (PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_14) ? -1 : 4095);
// The client sometimes sends a wierd cancel packet
private boolean shouldExempt(final Vector3i pos) {
// lastLastBlock is always null when this happens, and lastBlock isn't
@@ -14,15 +14,15 @@ import java.util.ArrayList;
@CheckData(name = "MultiInteractA", description = "Interacted with multiple entities in the same tick", experimental = true)
public class MultiInteractA extends Check implements PostPredictionCheck {
public MultiInteractA(final GrimPlayer player) {
super(player);
}
private final ArrayList<String> flags = new ArrayList<>();
private int lastEntity;
private boolean lastSneaking;
private boolean hasInteracted = false;
public MultiInteractA(final GrimPlayer player) {
super(player);
}
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (event.getPacketType() == PacketType.Play.Client.INTERACT_ENTITY) {
@@ -16,14 +16,14 @@ import java.util.ArrayList;
@CheckData(name = "MultiInteractB", experimental = true)
public class MultiInteractB extends Check implements PostPredictionCheck {
public MultiInteractB(final GrimPlayer player) {
super(player);
}
private final ArrayList<String> flags = new ArrayList<>();
private Vector3f lastPos;
private boolean hasInteracted = false;
public MultiInteractB(final GrimPlayer player) {
super(player);
}
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (event.getPacketType() == PacketType.Play.Client.INTERACT_ENTITY) {
@@ -23,6 +23,7 @@ import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
import ac.grim.grimac.utils.data.packetentity.PacketEntity;
import ac.grim.grimac.utils.data.packetentity.dragon.PacketEntityEnderDragonPart;
import ac.grim.grimac.utils.math.Vector3dm;
import ac.grim.grimac.utils.nmsutil.ReachUtils;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.protocol.attribute.Attributes;
@@ -35,23 +36,25 @@ import com.github.retrooper.packetevents.util.Vector3d;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientInteractEntity;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import org.bukkit.util.Vector;
import org.jetbrains.annotations.NotNull;
import java.util.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
// You may not copy the check unless you are licensed under GPL
@CheckData(name = "Reach", setback = 10)
public class Reach extends Check implements PacketCheck {
// Only one flag per reach attack, per entity, per tick.
// We store position because lastX isn't reliable on teleports.
private final Int2ObjectMap<Vector3d> playerAttackQueue = new Int2ObjectOpenHashMap<>();
private static final List<EntityType> blacklisted = Arrays.asList(
EntityTypes.BOAT,
EntityTypes.CHEST_BOAT,
EntityTypes.SHULKER);
private static final CheckResult NONE = new CheckResult(ResultType.NONE, "");
// Only one flag per reach attack, per entity, per tick.
// We store position because lastX isn't reliable on teleports.
private final Int2ObjectMap<Vector3d> playerAttackQueue = new Int2ObjectOpenHashMap<>();
private boolean cancelImpossibleHits;
private double threshold;
private double cancelBuffer; // For the next 4 hits after using reach, we aggressively cancel reach
@@ -89,10 +92,12 @@ public class Reach extends Check implements PacketCheck {
if (entity.isDead) return;
// TODO: Remove when in front of via
if (entity.getType() == EntityTypes.ARMOR_STAND && player.getClientVersion().isOlderThan(ClientVersion.V_1_8)) return;
if (entity.getType() == EntityTypes.ARMOR_STAND && player.getClientVersion().isOlderThan(ClientVersion.V_1_8))
return;
if (player.gamemode == GameMode.CREATIVE || player.gamemode == GameMode.SPECTATOR) return;
if (player.compensatedEntities.getSelf().inVehicle()) return;
if (player.gamemode == GameMode.CREATIVE || player.gamemode == GameMode.SPECTATOR)
return;
if (player.inVehicle()) return;
if (entity.riding != null) return;
boolean tooManyAttacks = playerAttackQueue.size() > 10;
@@ -127,8 +132,9 @@ public class Reach extends Check implements PacketCheck {
if ((blacklisted.contains(reachEntity.getType()) || !reachEntity.isLivingEntity()) && reachEntity.getType() != EntityTypes.END_CRYSTAL)
return false; // exempt
if (player.gamemode == GameMode.CREATIVE || player.gamemode == GameMode.SPECTATOR) return false;
if (player.compensatedEntities.getSelf().inVehicle()) return false;
if (player.gamemode == GameMode.CREATIVE || player.gamemode == GameMode.SPECTATOR)
return false;
if (player.inVehicle()) return false;
// Filter out what we assume to be cheats
if (cancelBuffer != 0) {
@@ -139,7 +145,7 @@ public class Reach extends Check implements PacketCheck {
if (reachEntity.getType() == EntityTypes.END_CRYSTAL) {
targetBox = new SimpleCollisionBox(reachEntity.trackedServerPosition.getPos().subtract(1, 0, 1), reachEntity.trackedServerPosition.getPos().add(1, 2, 1));
}
return ReachUtils.getMinReachToBox(player, targetBox) > player.compensatedEntities.getSelf().getAttributeValue(Attributes.ENTITY_INTERACTION_RANGE);
return ReachUtils.getMinReachToBox(player, targetBox) > player.compensatedEntities.self.getAttributeValue(Attributes.ENTITY_INTERACTION_RANGE);
}
}
@@ -191,7 +197,7 @@ public class Reach extends Check implements PacketCheck {
double minDistance = Double.MAX_VALUE;
// https://bugs.mojang.com/browse/MC-67665
List<Vector> possibleLookDirs = new ArrayList<>(Collections.singletonList(ReachUtils.getLook(player, player.xRot, player.yRot)));
List<Vector3dm> possibleLookDirs = new ArrayList<>(Collections.singletonList(ReachUtils.getLook(player, player.xRot, player.yRot)));
// If we are a tick behind, we don't know their next look so don't bother doing this
if (!isPrediction) {
@@ -208,17 +214,17 @@ public class Reach extends Check implements PacketCheck {
}
}
final double maxReach = player.compensatedEntities.getSelf().getAttributeValue(Attributes.ENTITY_INTERACTION_RANGE);
final double maxReach = player.compensatedEntities.self.getAttributeValue(Attributes.ENTITY_INTERACTION_RANGE);
// +3 would be 3 + 3 = 6, which is the pre-1.20.5 behaviour, preventing "Missed Hitbox"
final double distance = maxReach + 3;
final double[] possibleEyeHeights = player.getPossibleEyeHeights();
final Vector eyePos = new Vector(from.getX(), 0, from.getZ());
for (Vector lookVec : possibleLookDirs) {
final Vector3dm eyePos = new Vector3dm(from.getX(), 0, from.getZ());
for (Vector3dm lookVec : possibleLookDirs) {
for (double eye : possibleEyeHeights) {
eyePos.setY(from.getY() + eye);
Vector endReachPos = eyePos.clone().add(new Vector(lookVec.getX() * distance, lookVec.getY() * distance, lookVec.getZ() * distance));
Vector3dm endReachPos = eyePos.clone().add(new Vector3dm(lookVec.getX() * distance, lookVec.getY() * distance, lookVec.getZ() * distance));
Vector intercept = ReachUtils.calculateIntercept(targetBox, eyePos, endReachPos).first();
Vector3dm intercept = ReachUtils.calculateIntercept(targetBox, eyePos, endReachPos).first();
if (ReachUtils.isVecInside(targetBox, eyePos)) {
minDistance = 0;
@@ -247,21 +253,19 @@ public class Reach extends Check implements PacketCheck {
return NONE;
}
private static final CheckResult NONE = new CheckResult(ResultType.NONE, "");
private record CheckResult(ResultType type, String verbose) {
public boolean isFlag() {
return type != ResultType.NONE;
}
@Override
public void onReload(ConfigManager config) {
this.cancelImpossibleHits = config.getBooleanElse("Reach.block-impossible-hits", true);
this.threshold = config.getDoubleElse("Reach.threshold", 0.0005);
}
private enum ResultType {
REACH, HITBOX, NONE
}
@Override
public void onReload(ConfigManager config) {
this.cancelImpossibleHits = config.getBooleanElse("Reach.block-impossible-hits", true);
this.threshold = config.getDoubleElse("Reach.threshold", 0.0005);
private record CheckResult(ResultType type, String verbose) {
public boolean isFlag() {
return type != ResultType.NONE;
}
}
}
Loaded 100 of 625 files, more files were not shown because too many files have changed in this diff. Show more