Compare commits

..
Author SHA1 Message Date
Axionize d0d6e8f0ac Fix logic error with FabricAsyncScheduler's runAtFixedRate() 2025-04-22 21:09:10 -04:00
Axionize 84767d2b7e Build 1.17 module against 1.17.1; Fix loader naming inconsistencies 2025-04-22 15:34:13 -04:00
Axionize a5e4660bd5 Fix executing commands in Fabric 1.20.3+ 2025-04-22 12:31:31 -04:00
Axionize e25764872f Fix Fabric PlatformEntity and PlatformInventory loader issue 2025-04-22 12:28:12 -04:00
Axionize bc345399a4 Fix error when kicking player on Fabric 1.20.2+ 2025-04-22 10:44:57 -04:00
Axionize 40cd824ec3 Add support for Fastbreak on blocks that don't exist in client version 2025-04-22 00:44:47 -04:00
Axionize 56d1cb9b7b Fix Entity Modeling
- Save data for what mobs can be hit for future hit-un-hittable-entity-check
- Fix bounding boxes and modeling sizes for some entities (firework rocket, falling block, guardians, armor stands)
- Fix modeling of numerous types of baby mobs
2025-04-21 05:06:45 -04:00
Axionize 4169d43fb2 Improve Reach and Hitboxes config 2025-04-20 13:38:07 -04:00
Axionize e4401a3db0 Complete translations 2025-04-20 12:56:33 -04:00
Axionize 66fd982861 Fix update checker by fixing version parsing for new version schema 2025-04-20 09:27:49 -04:00
Axionize 0fe401aec4 Fix Fabric Inventory from multi-version refactor 2025-04-20 08:45:08 -04:00
Axionize 5f70d8784c Startup time optimization 2025-04-20 00:37:57 -04:00
Axionize 3eec89f564 Rewrite Fabric multi-version support
- handles 1.16.1 - 1.21.5 (currently only 1.19.3+ works due to Cloud command library limitations)
 - Java 21 -> Java 17 requirement for Fabric on older versions that didn't require Java 21
2025-04-19 23:30:40 -04:00
Axionize 638f04c2ff Implement new versioning scheme: https://github.com/GrimAnticheat/Grim/wiki/Versions-and-Versioning 2025-04-19 00:05:30 -04:00
Axionize 02eb173a93 Fix rebase on top of more 1.21.5 and other movement fixes. 2025-04-18 18:56:14 -04:00
Axionize 2c99434e02 Fully replace underlying native inventory and player objects when reset on death.
- Fixes fastbreak issues after dying with keepInventory off
2025-04-18 18:47:55 -04:00
Axionize af31452dea Address code review styling concerns. Fix errors from rebasing on 2.0. 2025-04-18 18:47:55 -04:00
Axionize f56fef847a Fix PlaceholderAPI replacements 2025-04-18 18:47:55 -04:00
Axionize 4efbe35849 Improve performance of internal sendAlert command invocations 2025-04-18 18:47:55 -04:00
Axionize c875d60882 Fix 1.21.5 Inventory compatability issues 2025-04-18 18:47:55 -04:00
Axionize 7fae0285ec Fix working with cooldown groups 2025-04-18 18:47:55 -04:00
Axionize 41553b5720 Fix 1.21.5 support and entity metadata
- Fixed 1.21.5 Pig Variant entity metadata handling
- Read Strider isShaking value from entity metadata
- (Hopefully) Fix 1.8 horse saddle modeling
2025-04-18 18:47:55 -04:00
Axionize b8f7e176ee Execute piston updates from Bukkit Event in player netty channel.
- This fixes a rare race condition with pistons. https://github.com/GrimAnticheat/Grim/issues/2031
- Reduces load on main thread (we check for isChunkLoaded() and do processing in netty channel helper now)
2025-04-18 18:47:55 -04:00
Axionize 8321a9bdd2 Minor Annotation Style changes 2025-04-18 18:47:55 -04:00
Axionize bdd8377938 Update API; Add support for overriding player.resyncPosition(x, y, z, sequence) 2025-04-18 18:47:55 -04:00
Axionize 28a8a8cd1c Improve performance of underlying Collection used for exemptUsers 2025-04-18 18:47:55 -04:00
Axionize 5c5fa7f10a Fix 1.21.5 Support breaking with new Chunk format 2025-04-18 18:47:55 -04:00
Axionize 0ed1d5cd2c Significantly reduce Fabric jar size 2025-04-18 18:47:55 -04:00
Axionize 0d852ef4ec Initial 1.21.5 Support 2025-04-18 18:47:55 -04:00
Axionize 95854169ae Update Loom; Buildscript consolidation and improvements 2025-04-18 18:47:55 -04:00
Axionize 78b7b21b7c Fix error with teleporting between dimensions 2025-04-18 18:47:55 -04:00
Axionize 1735843fd5 Refactor to use new API with better EventBus and consolidate event-bus related code to be non-platform specific 2025-04-18 18:47:55 -04:00
Axionize f0c4dad700 Add explicit dependency for parent mod on packetevents 2025-04-18 18:47:55 -04:00
Axionize f9e35eef85 Fix build errors from rebasing 2025-04-18 18:47:55 -04:00
Axionize 8782e12a53 Partially fix lack of suggestions in cloud commands when incorrect command/typo is typed (only works when required arguments missing in a command on modern brigadier servers due to cloud bug, will fix later)
- Also force rebuild with new PE
2025-04-18 18:47:55 -04:00
Axionize 525133088e Fix build issues with downloading Conditional Mixin library for PE Fabric 2025-04-18 18:47:55 -04:00
Axionize c516fb5d7d Bump RAM allocation to Gradle in root gradle.properties 2025-04-18 18:47:55 -04:00
Axionize 40c9222aa7 Minor Build optimization 2025-04-18 18:47:55 -04:00
Axionize eb97c21feb New API refactor with getEventBus() 2025-04-18 18:47:55 -04:00
Axionize effffad5b0 Only include fabricAPI modules we use, reuse dependency code for multiple projects 2025-04-18 18:47:55 -04:00
Axionize 769e854ccf 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-04-18 18:47:55 -04:00
Axionize bffdd397d1 Make Grim rely on JiJed and loaded PE API; Transition PacketPlayerDigging to be cross-platform from upstream 2025-04-18 18:47:55 -04:00
Axionize 348730c0fd Translation parity (and more/better) with pre-platform-independence port 2025-04-18 18:47:55 -04:00
Axionize 734f189145 Fix updating native ServerPlayerEntity reference; Refactor @NonNull annotations 2025-04-18 18:47:55 -04:00
Axionize 39ab597a66 Initable refactor; support multiple types of initables as one object; shutdown fabric bstats 2025-04-18 18:47:55 -04:00
Axionize 0d3870f14a Fix Geyser-Fabric compatability 2025-04-18 18:47:55 -04:00
Axionize 699526d857 Build against slim configuralize for fabric support 2025-04-18 18:47:55 -04:00
Axionize a7f20794dd Bump snakeyaml version for Fabric support 2025-04-18 18:47:55 -04:00
Axionize bbda490c79 Fix library relocation typo 2025-04-18 18:47:55 -04:00
Axionize 3160a05fdc Fix build issues with relocation on bukkit; force rebuild for fabric registry fix with PE 2025-04-18 18:47:55 -04:00
Axionize 1803b6a608 Code cleanup to address code review concerns 2025-04-18 18:47:55 -04:00
Axionize fdb638927a Fix Build error from rebasing https://github.com/GrimAnticheat/Grim/commit/aab710dd8b55bc6fbe88f0f4c79aa214527a20dd 2025-04-18 18:47:55 -04:00
Axionize b7c348f71f Fix rare preconditions error with checking for null platformPlayer 2025-04-18 18:47:55 -04:00
Axionize 69e463c63c Cleanup Fabric buildscript 2025-04-18 18:47:55 -04:00
Axionize c2ad908fda Hopefully Fix inventory errors 2025-04-18 18:47:55 -04:00
Axionize e71141eefd Fix running fabric in IDE and dev mode
Credit: @Bawnorton
2025-04-18 18:47:55 -04:00
Axionize 0d84696cc3 Fix 1.8 support using non-existent inventory method 2025-04-18 18:47:55 -04:00
Axionize f0d686dce2 Bump Fabric loom version; trigger rebuild on updated PE fork with Fabric fixes 2025-04-18 18:47:55 -04:00
Axionize ab932e3dff Minor code cleanup 2025-04-18 18:47:55 -04:00
Axionize 647b98c0ed Code cleanup 2025-04-18 18:47:55 -04:00
Axionize e33d982266 Configure builds to use shared lib versions files; push for rebuild with updated PE; (Working 1.20.1) 2025-04-18 18:47:55 -04:00
Axionize 0ae35a3326 Fix project build issues and fix 1.20.1 support 2025-04-18 18:47:55 -04:00
Axionize 9eced9b1f3 Fix Legacy (< 1.16.5) Big DripLeaf CollisionBox modeling 2025-04-18 18:47:55 -04:00
Axionize 9e41f607ad Actuallly commit 1.20.4 support 2025-04-18 18:47:55 -04:00
Axionize 1d229afd60 Add 1.20.1 support (once PE is fixed) 2025-04-18 18:47:55 -04:00
Axionize 04fe0a9d5f Refactor Fabric to support 1.21.4, 1.21.1, and (soon) 1.16.1 2025-04-18 18:47:55 -04:00
Axionize 678c60adc8 Fix endTick on Folia 2025-04-18 18:47:55 -04:00
Axionize f9c096d7a5 PE datatype refactor; Fix Folia support 2025-04-18 18:47:55 -04:00
Axionize 3dcb418d75 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-04-18 18:47:53 -04:00
Axionize 5ac78bac75 Fix scheduler hierachy issue 2025-04-18 18:46:30 -04:00
Axionize e176210eb1 Temporarily build against my fork to fix PacketEvent's broken Fabric support 2025-04-18 18:46:30 -04:00
Axionize 95df8757cb Build against new Grim API instead of locally 2025-04-18 18:46:30 -04:00
Axionize 3db915ce5d Fix Actions workflow 2025-04-18 18:46:28 -04:00
Axionize ca27062413 Finish Fabric buildscript; now builds runnable jar 2025-04-18 18:44:21 -04:00
Axionize 6c2612d966 Fix Fabric execution and shutdown; Initial Fabric build script 2025-04-18 18:44:21 -04:00
Axionize 1b298480af 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-04-18 18:44:20 -04:00
Axionize 14c03b9a55 Refactor resyncPosition() internally; may move to platformPlayer in future 2025-04-18 18:44:13 -04:00
Axionize c15120f868 Platform loading fixes 2025-04-18 18:43:59 -04:00
Axionize c058413643 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-04-18 18:43:57 -04:00
Axionize 5d437f5c2f Complete multi-module project setup 2025-04-18 18:43:24 -04:00
Axionize 7519fad830 Multi-module project init setup 2025-04-18 18:43:09 -04:00
Axionize 7f9b316f1c Platform Independence Achieved! 2025-04-18 18:42:58 -04:00
Axionize 98c26bc397 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-04-18 18:42:58 -04:00
Axionize 514173e7f3 Fix plugin compilation and exeution of commands 2025-04-18 18:42:58 -04:00
Axionize aa5236ea7c Almost there; just events and bukkit extensions left 2025-04-18 18:42:58 -04:00
Axionize 0d7d3dbcd0 Stash code 2025-04-18 18:42:56 -04:00
Axionize 0da7aeca20 Refactor Part 2:
- Replace Vectors
- Replace ChatColor.translateColorCodes()
2025-04-18 18:42:49 -04:00
Axionize 402330b187 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-04-18 18:41:36 -04:00
cyramek fcc6f6aee6 support 1.18.2+ horizontal collision detection 2025-04-19 00:11:19 +02:00
cyramek cd158b0bf8 incorrect fix for powder snow issues (1.21.4) 2025-04-18 04:43:51 +02:00
cyramek 2f0df35dd4 initial block collisions support 2025-04-18 04:04:51 +02:00
cyramek 5349df4f9f java style 2025-04-18 01:39:59 +02:00
cyramek 7936c2be48 Merge remote-tracking branch 'origin/2.0' into 1.21.5
# Conflicts:
#	src/main/java/ac/grim/grimac/events/packets/worldreader/PacketWorldReaderEighteen.java
2025-04-18 01:27:35 +02:00
cyramek a8244cdcec note 2025-04-17 23:41:34 +02:00
cyramek 95e97849fe this is the right way 2025-04-16 15:54:56 +02:00
cyramek 4577d42241 not the case 2025-04-16 02:34:31 +02:00
cyramek f07a88864a i hate mutability 2025-04-16 02:33:37 +02:00
cyramek 80b1431e6b notes 2025-04-13 22:34:23 +02:00
Cyramek 8622bf2ca2 Merge branch 'GrimAnticheat:2.0' into 1.21.5 2025-04-13 22:32:12 +02:00
Cyramek c7bc30f7b5 Merge branch 'GrimAnticheat:2.0' into 1.21.5 2025-04-13 00:39:31 +02:00
cyramek 7550a4ad54 forgot to push that 2025-04-06 22:55:37 +02:00
cyramek ce083be707 1;1 input transformation 2025-04-06 22:52:05 +02:00
cyramek ba02d3fbd7 check if player is in vehicle 2025-04-06 17:32:10 +02:00
cyramek 9f6d55e63f cleanup 2025-04-06 15:41:25 +02:00
cyramek 05916274d5 leftover 2025-04-06 15:23:22 +02:00
cyramek bd46c7bbc6 1.21.5 partial changes 2025-04-06 15:20:39 +02:00
557 changed files with 6451 additions and 16003 deletions

No files matched your search

-2
View File
@@ -7,8 +7,6 @@ indent_style = space
insert_final_newline = true
max_line_length = 100
tab_width = 4
end_of_line = crlf
trim_trailing_whitespace = true
# intellij specific configuration
ij_continuation_indent_size = 8
+2 -10
View File
@@ -30,19 +30,11 @@ body:
validations:
required: true
- type: input
attributes:
label: Client version
description: |
Provide the version of your Minecraft client.
validations:
required: true
- type: textarea
attributes:
label: Plugins
description: |
List all plugins or mods running on your server. To list plugins on bukkit, run `/plugins`.
List all plugins running on your server. To list plugins, run `/plugins`.
If your server is behind a proxy such as Bungeecord, you should list the proxy's plugins as well.
validations:
required: true
required: true
-1
View File
@@ -1 +0,0 @@
blank_issues_enabled: false
+3 -15
View File
@@ -39,23 +39,11 @@ body:
validations:
required: true
- type: dropdown
id: type
attributes:
label: Affected Platform(s)
description: Which platforms does this issue affect? If you aren't sure, select both.
options:
- "Bukkit"
- "Fabric"
multiple: true
validations:
required: true
- type: textarea
attributes:
label: Plugins/mods
label: Plugins
description: |
List all plugins or mods running on your server. To list plugins on bukkit, run `/plugins`.
List all plugins running on your server. To list plugins, run `/plugins`.
If your server is behind a proxy such as Bungeecord, you should list the proxy's plugins as well.
validations:
required: true
required: true
+2 -2
View File
@@ -46,7 +46,7 @@ body:
attributes:
label: Plugins
description: |
List all plugins or mods running on your server. To list plugins on bukkit, run `/plugins`.
List all plugins running on your server. To list plugins, run `/plugins`.
If your server is behind a proxy such as Bungeecord, you should list the proxy's plugins as well.
validations:
required: true
required: true
+4 -16
View File
@@ -1,5 +1,5 @@
name: Plugin/mod incompatibility or other bug
description: Report a bug or plugin/mod incompatibility here
name: Plugin incompatibility or other bug
description: Report a bug or plugin incompatibility here
labels: ["bug"]
body:
- type: textarea
@@ -30,23 +30,11 @@ body:
validations:
required: true
- type: dropdown
id: type
attributes:
label: Affected Platform(s)
description: Which platforms does this issue affect? If you aren't sure, select both.
options:
- "Bukkit"
- "Fabric"
multiple: true
validations:
required: true
- type: textarea
attributes:
label: Plugins
description: |
List all plugins or mods running on your server. To list plugins on bukkit, run `/plugins`.
List all plugins running on your server. To list plugins, run `/plugins`.
If your server is behind a proxy such as Bungeecord, you should list the proxy's plugins as well.
validations:
required: true
required: true
@@ -45,7 +45,7 @@ body:
attributes:
label: Plugins
description: |
List all plugins or mods running on your server. To list plugins on bukkit, run `/plugins`.
List all plugins running on your server. To list plugins, run `/plugins`.
If your server is behind a proxy such as Bungeecord, you should list the proxy's plugins as well.
validations:
required: true
required: true
-229
View File
@@ -1,229 +0,0 @@
name: Reusable Build and Publish
on:
workflow_call:
inputs:
is-release-build:
description: 'True if this is a release build, which affects versioning and publishing.'
required: true
type: boolean
release-version-type:
description: 'The version type for Modrinth (e.g., release, beta, alpha).'
required: true
type: string
release-changelog:
description: 'The changelog content for the release.'
required: false
type: string
default: ''
run-build-scan:
description: 'Whether to enable Develocity build scan'
required: true
type: boolean
publish-artifacts:
description: 'If true, uploads artifacts to Modrinth and other distribution sites.'
required: true
type: boolean
secrets:
MODRINTH_ID:
required: false
MODRINTH_TOKEN:
required: false
BUILD_UPDATE_TOKEN:
required: true
BUILD_UPDATE_URL:
required: true
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
env:
MODRINTH_ID: ${{ secrets.MODRINTH_ID }}
MODRINTH_TOKEN: ${{ secrets.MODRINTH_TOKEN }}
GRIM_MAIN_BRANCH: ${{ github.event.repository.default_branch }}
BUILD_UPDATE_TOKEN: ${{ secrets.BUILD_UPDATE_TOKEN }}
steps:
- name: Checkout repository with full history
uses: actions/checkout@v4
with:
persist-credentials: false
fetch-depth: 200
- name: Set up JDK 21
uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'temurin'
server-id: github
settings-path: ${{ github.workspace }}
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v3
with:
cache-read-only: ${{ github.ref_name != github.event.repository.default_branch && github.ref_name != 'dev' }}
- name: Cache Gradle Loom Cache
uses: actions/cache@v4
with:
path: .gradle/loom-cache
key: loom-cache-${{ runner.os }}-${{ hashFiles('**/libs.versions.toml', 'fabric/**/build.gradle.kts') }}
restore-keys: |
loom-cache-${{ runner.os }}
- name: Set Git Environment Variables
run: |
echo "GRIM_GIT_REPO=${{ github.event.repository.name }}" >> $GITHUB_ENV
echo "GRIM_GIT_ORG=${{ github.repository_owner }}" >> $GITHUB_ENV
- name: Compute version and Build with Gradle
id: build_with_version
run: |
# If any command in a pipeline fails, treat the pipeline as failed.
set -o pipefail
BUILD_ARGS="build"
if [[ "${{ inputs.is-release-build }}" == "true" ]]; then
BUILD_ARGS="$BUILD_ARGS -Prelease=true"
fi
# Conditionally add the --scan flag
if [[ "${{ inputs.run-build-scan }}" == "true" ]]; then
BUILD_ARGS="$BUILD_ARGS --scan"
fi
echo "Running: ./gradlew printVersion $BUILD_ARGS"
# Use 'tee' to both display the output live and save it to a file for parsing
VERSION=$(./gradlew printVersion $BUILD_ARGS | tee build-output.log | grep '^VERSION=' | tail -n1 | cut -d'=' -f2)
echo "build_version=$VERSION" >> $GITHUB_OUTPUT
echo "BUILD_VERSION=$VERSION" >> $GITHUB_ENV
echo "Build version: $VERSION"
- name: Publish Build Scan URL
# Only run this step if a scan was requested
if: ${{ inputs.run-build-scan == true }}
run: |
# Search the log file for the build scan URL and extract it
SCAN_URL=$(grep -o 'https://gradle.com/s/[^ ]*' build-output.log | head -n 1 || true)
if [[ -n "$SCAN_URL" ]]; then
echo "Found build scan URL: $SCAN_URL"
# Post a highly visible link to the Job Summary page
echo "### Gradle Build Scan" >> $GITHUB_STEP_SUMMARY
echo "A public build scan has been published and is available for inspection." >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "[**View Build Scan →**]($SCAN_URL)" >> $GITHUB_STEP_SUMMARY
else
echo "Could not find build scan URL in build-output.log" >> $GITHUB_STEP_SUMMARY
fi
- name: Upload Bukkit Artifact
uses: actions/upload-artifact@v4
with:
name: grimac-bukkit
path: ${{ github.workspace }}/bukkit/build/libs/grimac-bukkit-${{ env.BUILD_VERSION }}.jar
if-no-files-found: error
- name: Upload Fabric Artifact
uses: actions/upload-artifact@v4
with:
name: grimac-fabric
path: ${{ github.workspace }}/fabric/build/libs/grimac-fabric-${{ env.BUILD_VERSION }}.jar
if-no-files-found: error
#------------------------------------------------------------------
# Generate/Prepare Changelog
#------------------------------------------------------------------
- name: Generate Changelog with git-cliff (for Dev Builds)
if: ${{ !inputs.is-release-build }}
id: git-cliff
uses: orhun/git-cliff-action@v4
with:
config: cliff.toml
args: --verbose ${{ github.event.before }}..${{ github.event.after }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # git-cliff needs a token
- name: Prepare Final Changelog
id: changelog
# Add this 'env' block to safely load the changelogs as environment variables
env:
RELEASE_NOTES: ${{ inputs.release-changelog }}
DEV_NOTES: ${{ steps.git-cliff.outputs.content }}
run: |
NOTES=""
if [[ "${{ inputs.is-release-build }}" == "true" ]]; then
# Use the environment variable for the release changelog.
# The double quotes are critical to preserve newlines!
NOTES="$RELEASE_NOTES"
else
# Use the environment variable for the dev changelog.
NOTES="$DEV_NOTES"
fi
if [ -z "$NOTES" ]; then
NOTES="* No new code changes in this build."
fi
# This is the correct way to set a multi-line output variable in Actions
{
echo "notes<<EOF"
echo "$NOTES"
echo "EOF"
} >> "$GITHUB_OUTPUT"
#------------------------------------------------------------------
# Publish to Modrinth & API
#------------------------------------------------------------------
- name: Publish Bukkit to Modrinth
if: ${{ inputs.publish-artifacts == true && env.MODRINTH_ID != '' && env.MODRINTH_TOKEN != '' }}
uses: Kir-Antipov/mc-publish@v3.3
with:
modrinth-id: ${{ secrets.MODRINTH_ID }}
modrinth-token: ${{ secrets.MODRINTH_TOKEN }}
modrinth-featured: ${{ inputs.is-release-build }} # Only feature release builds
files: bukkit/build/libs/grimac-bukkit-${{ env.BUILD_VERSION }}.jar
name: Grim Anticheat (Bukkit) ${{ env.BUILD_VERSION }}
version: ${{ env.BUILD_VERSION }}
version-type: ${{ inputs.release-version-type }}
changelog: ${{ steps.changelog.outputs.notes }}
loaders: |
bukkit
spigot
paper
folia
purpur
game-versions: '>=1.7 <=1.21.8'
- name: Publish Fabric to Modrinth
if: ${{ inputs.publish-artifacts == true && env.MODRINTH_ID != '' && env.MODRINTH_TOKEN != '' }}
uses: Kir-Antipov/mc-publish@v3.3
with:
modrinth-id: ${{ secrets.MODRINTH_ID }}
modrinth-token: ${{ secrets.MODRINTH_TOKEN }}
modrinth-featured: ${{ inputs.is-release-build }} # Only feature release builds
files: fabric/build/libs/grimac-fabric-${{ env.BUILD_VERSION }}.jar
name: Grim Anticheat (Fabric) ${{ env.BUILD_VERSION }}
version: ${{ env.BUILD_VERSION }}
version-type: ${{ inputs.release-version-type }}
changelog: ${{ steps.changelog.outputs.notes }}
loaders: |
fabric
game-versions: '>=1.16.1 <=1.21.8'
dependencies: 'fabric-api'
- name: Publish to API
if: ${{ inputs.publish-artifacts == true && env.BUILD_UPDATE_TOKEN != '' }}
run: |
curl -X POST \
-H "Content-Type: application/json" \
-H "User-Agent: GrimACBuilder/v1" \
-H "Authorization: Bearer ${{ secrets.BUILD_UPDATE_TOKEN }}" \
-d '{"org": "${{ github.repository_owner }}", "repository": "${{ github.event.repository.name }}", "branch": "${{ github.ref_name }}", "version": "${{ env.BUILD_VERSION }}"}' \
"${{ secrets.BUILD_UPDATE_URL }}"
+8 -12
View File
@@ -12,9 +12,13 @@
name: "CodeQL"
on:
workflow_dispatch:
push:
branches: [ dev ]
branches: [ master ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ master ]
schedule:
- cron: '30 15 * * 2'
jobs:
analyze:
@@ -28,14 +32,14 @@ jobs:
strategy:
fail-fast: false
matrix:
language: [ 'java-kotlin' ]
language: [ 'java' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
# Learn more:
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
steps:
- name: Checkout repository
uses: actions/checkout@v5
uses: actions/checkout@v4
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
@@ -47,14 +51,6 @@ jobs:
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
- name: Set up JDK 21
uses: actions/setup-java@v5
with:
java-version: '21'
distribution: 'temurin'
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
settings-path: ${{ github.workspace }} # location for the settings.xml file
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
+67 -42
View File
@@ -1,44 +1,69 @@
name: Gradle Package (Development)
# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.
# This workflow will build a package using Gradle and then publish it to GitHub packages when a release is created
# For more information see: https://github.com/actions/setup-java/blob/main/docs/advanced-usage.md#Publishing-using-gradle
name: Gradle Package
on: [push, workflow_dispatch]
on:
workflow_dispatch:
inputs:
run-build-scan:
description: 'Run with public build scan (adds --scan)'
required: true
type: boolean
default: false
publish-artifacts:
description: 'Upload to Modrinth & API? (Uncheck for a dry run)'
type: boolean
default: true
push:
branches:
- 'main'
- '2.0'
- 'dev'
- 'release'
- 'release/**'
paths:
- '.github/workflows/**'
- 'buildSrc/**'
- 'bukkit/**'
- 'common/**'
- 'fabric/**'
- '**.java'
- '**.kt'
- '**.kts'
- '**.toml'
jobs:
call-build-workflow:
uses: ./.github/workflows/build-and-publish.yml
with:
is-release-build: false
release-version-type: alpha # Dev builds are always 'alpha'
run-build-scan: ${{ github.event.inputs.run-build-scan == 'true' }}
publish-artifacts: ${{ github.event.inputs.publish-artifacts != 'false' }}
secrets:
MODRINTH_ID: ${{ secrets.MODRINTH_ID }}
MODRINTH_TOKEN: ${{ secrets.MODRINTH_TOKEN }}
BUILD_UPDATE_TOKEN: ${{ secrets.BUILD_UPDATE_TOKEN }}
BUILD_UPDATE_URL: ${{ secrets.BUILD_UPDATE_URL }}
build:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- name: Set up JDK 21
uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'temurin'
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
settings-path: ${{ github.workspace }} # location for the settings.xml file
# 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 Computed Version from Gradle
id: extract-version
run: |
VERSION=$(./gradlew -q printVersion | grep VERSION= | cut -d '=' -f2)
echo "VERSION: $VERSION"
echo "VERSION=$VERSION" >> $GITHUB_ENV
echo "version=$VERSION" >> $GITHUB_OUTPUT
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4
- name: Build with Gradle
run: ./gradlew build
- name: Upload Bukkit Artifact
uses: actions/upload-artifact@v4
with:
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
-21
View File
@@ -1,21 +0,0 @@
name: Publish Release
on:
release:
types: [published]
jobs:
call-release-workflow:
uses: ./.github/workflows/build-and-publish.yml
with:
is-release-build: true
release-version-type: release # Official releases are 'release'
# Use the body of the GitHub Release as the changelog
release-changelog: ${{ github.event.release.body }}
run-build-scan: false
publish-artifacts: true
secrets:
MODRINTH_ID: ${{ secrets.MODRINTH_ID }}
MODRINTH_TOKEN: ${{ secrets.MODRINTH_TOKEN }}
BUILD_UPDATE_TOKEN: ${{ secrets.BUILD_UPDATE_TOKEN }}
BUILD_UPDATE_URL: ${{ secrets.BUILD_UPDATE_URL }}
-1
View File
@@ -19,7 +19,6 @@ build/
.gradletasknamecache
gradle-app.setting
!gradle-wrapper.jar
buildSrc/.kotlin
##############################
## IntelliJ
-40
View File
@@ -1,40 +0,0 @@
# Contributing to GrimAC
Thank you for your interest in contributing to GrimAC. This document outlines the guidelines for
making pull
requests to the project. *We're usually pretty lenient with pull requests, but this guide will help
make the process go more smoothly.*
### Pull Request Guidelines
- **Compatibility**
- Any changes must be compatible with
the [supported environments](https://github.com/GrimAnticheat/Grim/wiki/Supported-environments) (Spigot, Paper, Folia, Fabric, etc.)
- The plugin must be compatible with minecraft versions 1.8 and higher. Exempting checks from specific versions is acceptable.
- The plugin must be able to run on Java 17 or higher. Changes that don't support Java 17 at runtime will not be accepted.
- **Non-acceptable pull requests**
- Heuristic-based checks will not be accepted; however, basic rate limiting such as blocking attacks based on CPS is acceptable.
- Checks that can be easily circumvented that don't block packets or fix anything are likely to not be accepted.
- Checks or features that are too specific to a single environment or minecraft version are likely to not be accepted.
- Changes that require large or unnecessary dependencies will likely not be accepted.
- **Pull request formatting**
- Create a new branch for your feature or fix when forking the repository.
- Reference related issues in your pull request description if applicable.
- Write clear and descriptive commit messages.
- **Code styling**
- Add code comments for complex logic or significant changes.
- Try to keep your code clean and avoid duplication.
- Thoroughly test your changes before submitting your pull request.
### Development Notes
- GrimAC is built using [Gradle](https://gradle.org/) kotlin scripts.
- Java 21 is currently required to build the project. A minimum of Java 17 is required to run it.
### Questions & Support
- Join our [Discord](https://discord.grim.ac) if you have questions or need assistance.
- Refer to the [Wiki](https://github.com/GrimAnticheat/Grim/wiki) for project documentation.
+26 -65
View File
@@ -1,76 +1,40 @@
<div align="center">
<h1>GrimAC</h1>
# GrimAC
<div>
<a href="https://github.com/GrimAnticheat/Grim/actions/workflows/gradle-publish.yml">
<img alt="Workflow" src="https://img.shields.io/github/actions/workflow/status/GrimAnticheat/Grim/gradle-publish.yml?style=flat&logo=github"/>
</a>&nbsp;&nbsp;
<a href="https://modrinth.com/plugin/grimac">
<img alt="Modrinth" src="https://img.shields.io/modrinth/v/LJNGWSvH?style=flat&label=version&logo=modrinth">
</a>&nbsp;&nbsp;
<a href="https://modrinth.com/plugin/grimac#download">
<img alt="Downloads" src="https://img.shields.io/modrinth/dt/LJNGWSvH?style=flat&logo=modrinth&label=downloads&link=https%3A%2F%2Fmodrinth.com%2Fplugin%2Fgrimac%23download">
</a>&nbsp;&nbsp;
<a href="https://discord.grim.ac">
<img alt="Discord" src="https://img.shields.io/discord/811396969670901800?style=flat&label=discord&logo=discord">
</a>
</div>
<br>
</div>
GrimAC is an open source Minecraft anticheat designed for 1.21 and supports 1.8-1.21. It is free while in beta. It will eventually become paid and/or will include offering additional subscription based paid checks. Geyser players are fully exempt.
GrimAC is an open source Minecraft anticheat designed to support the latest versions of Minecraft.
It currently supports minecraft versions 1.8–1.21. Geyser players are fully exempt from the anticheat to prevent false positives.
This project is considered feature-complete for the 2.0 (open-source) branch. If you would like a bug fix or enhancement and cannot sponsor the work, pull requests are welcome.
A premium version is planned, which will offer additional subscription-based paid checks, such as heuristics.
This project is considered feature complete for the 2.0 (open-source) branch of this project. If you would like a bugfix or enhancement and cannot sponsor the work, pull requests are welcome.
## Downloads
- [Modrinth](https://modrinth.com/plugin/grimac)
- [Hangar](https://hangar.papermc.io/GrimAnticheat/GrimAnticheat)
- [SpigotMC](https://www.spigotmc.org/resources/grim-anticheat.99923/)
- *For bleeding edge builds use* [Github artifacts](https://nightly.link/GrimAnticheat/Grim/workflows/gradle-publish/2.0/artifact.zip)
- Latest updates:
- **[Modrinth](https://modrinth.com/plugin/grimac)** *(recommended)*
- GitHub
artifacts: [Bukkit](https://nightly.link/GrimAnticheat/Grim/workflows/gradle-publish/2.0/grimac-bukkit.zip), [Fabric](https://nightly.link/GrimAnticheat/Grim/workflows/gradle-publish/2.0/grimac-fabric.zip) *(bleeding edge)*
- Major releases only:
- ~~[Hangar](https://hangar.papermc.io/GrimAnticheat/GrimAnticheat)~~
- ~~[SpigotMC](https://www.spigotmc.org/resources/grim-anticheat.99923/)~~
## Installation notes
> [!WARNING]
> Java 17 is now required. More information [here](https://github.com/GrimAnticheat/Grim/wiki/Updating-to-Java-17).
- Paper, Spigot, and Folia are currently supported.
- If you use Geyser, place Floodgate on the backend server so grim can exempt bedrock players. Grim cannot access the Floodgate API if it is on the proxy.
- If you use ViaVersion, it should be on the backend server as movement is highly dependent on client version.
## Resources
## Support & wiki information
- Support & discussion: [Discord](https://discord.com/invite/kqQAhTmkUF)
- Report issues: [Issues](https://github.com/GrimAnticheat/Grim/issues/new/choose)
- Wiki & examples: [Wiki](https://github.com/GrimAnticheat/Grim/wiki)
- For documentation and examples visit the [Wiki](https://github.com/GrimAnticheat/Grim/wiki).
- For answers to commonly asked questions visit the [FAQ](https://github.com/GrimAnticheat/Grim/wiki/FAQ).
- For community support and project discussion join our [Discord](https://discord.grim.ac).
## Developer API
Grim's API allows you to integrate Grim into your own plugins.
- API repository: [GrimAPI](https://github.com/GrimAnticheat/GrimAPI)
- Wiki info: [Wiki](https://github.com/GrimAnticheat/GrimAPI)
## Pull Requests
See [Contributing](CONTRIBUTING.md) for more information about contributing and what our guidelines
are.
## Requirements & Installation
- Java 17 or higher. *For more details
see [Updating-to-Java-17](https://github.com/GrimAnticheat/Grim/wiki/Updating-to-Java-17).*
- A Spigot, Paper, Folia, or Fabric server environment. *For more details
see [Supported-environments](https://github.com/GrimAnticheat/Grim/wiki/Supported-environments).*
- If you use Geyser, place Floodgate on the backend server so grim can exempt bedrock players. Grim
cannot access the Floodgate API if it is on the proxy.
- If you use ViaVersion, it should be on the backend server as movement is highly dependent on
client version.
## Developer Plugin API
Grim's plugin API allows you to integrate Grim into your own plugins. Visit
the [plugin API repository](https://github.com/GrimAnticheat/GrimAPI) for the source code and more
information.
## Compiling From Source
## How to compile
1. `git clone https://github.com/GrimAnticheat/Grim.git`
2. `cd Grim`
3. `./gradlew build`
4. The final jars will compile into the `<platform>/build/libs` folders
4. The final jar will compile into the build/libs folder
## Grim Supremacy
## Grim supremacy
What makes Grim stand out against other anticheats?
@@ -80,9 +44,7 @@ What makes Grim stand out against other anticheats?
* This covers everything from basic walking, swimming, knockback, cobwebs, to bubble columns
* It even covers riding entities from boats to pigs to striders
* Built upon covering edge cases to confirm accuracy
* 1.13+ clients on 1.13+ servers, 1.12- clients on 1.13+ servers, 1.13+ clients on 1.12- servers,
and 1.12- clients on 1.12- servers are all supported regardless of the large technical changes
between these versions.
* 1.13+ clients on 1.13+ servers, 1.12- clients on 1.13+ servers, 1.13+ clients on 1.12- servers, and 1.12- clients on 1.12- servers are all supported regardless of the large technical changes between these versions.
* The order of collisions depends on the client version and is correct
* Accounts for minor bounding box differences between versions, for example:
* Single glass panes will be a + shape for 1.7-1.8 players and * for 1.9+ players
@@ -109,8 +71,7 @@ What makes Grim stand out against other anticheats?
* Per player, the cache allows for multithreaded design
* Sending players fake blocks with packets is safe and does not lead to falses
* The world is recreated for each player to allow lag compensation
* Client sided blocks cause no issues with packet based blocks. Block glitching does not false the
anticheat.
* Client sided blocks cause no issues with packet based blocks. Block glitching does not false the anticheat.
### Latency compensation
+17 -26
View File
@@ -1,10 +1,12 @@
/**
* GrimAC Build Configuration
* ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
* ┃ GrimAC Build Configuration ┃
* ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
*
* Build Flags:
* -PshadePE=true - Enables 'lite' mode
* -Prelocate=false - Adds 'no_relocate' modifier
* -Prelease=true - Removes commit/modifiers for release build
* 🔧 Build Flags:
* -PshadePE=true → Enables 'lite' mode
* -Prelocate=false → Adds 'no_relocate' modifier
* -Prelease=true → Removes commit/modifiers for release build
*
* Logic in: buildSrc/versioning/BuildConfig.kt & VersionUtil.kt
*/
@@ -12,25 +14,16 @@
import versioning.BuildConfig
import versioning.VersionUtil
BuildConfig.init(project)
val baseVersion = "2.3.73"
val baseVersion = "2.3.72"
group = "ac.grim.grimac"
version = VersionUtil.computeVersion(baseVersion)
description = "Libre simulation anticheat designed for 1.21 with 1.8–1.21 support, powered by PacketEvents 2.0."
ext["timestamp"] = System.currentTimeMillis().toString()
ext["git_branch"] = VersionUtil.getGitBranch(true)
ext["git_commit"] = VersionUtil.getGitCommitHash(true)
ext["git_org"] = System.getenv("GRIM_GIT_ORG") ?: VersionUtil.getGitUser()
ext["git_repo"] = System.getenv("GRIM_GIT_REPO") ?: "Grim"
println("Build configuration:")
println(" shadePE = ${BuildConfig.shadePE}")
println(" relocate = ${BuildConfig.relocate}")
println(" mavenLocalOverride = ${BuildConfig.mavenLocalOverride}")
println(" release = ${BuildConfig.release}")
println(" version = $version")
println("⚙️ Build configuration:")
println(" shadePE = ${BuildConfig.shadePE}")
println(" relocate = ${BuildConfig.relocate}")
println(" release = ${BuildConfig.release}")
println(" version = $version")
tasks.register("printVersion") {
group = "versioning"
@@ -41,9 +34,7 @@ tasks.register("printVersion") {
}
// ---------- Java Compile Optimization ----------
subprojects {
tasks.withType<JavaCompile>().configureEach {
options.isFork = true
options.isIncremental = true
}
}
tasks.withType<JavaCompile>().configureEach {
options.isFork = true
options.isIncremental = true
}
+1
View File
@@ -5,6 +5,7 @@ plugins {
repositories {
mavenCentral()
gradlePluginPortal()
}
dependencies {
@@ -1,5 +1,3 @@
import versioning.BuildConfig
plugins {
`java-library`
id("io.freefair.lombok")
@@ -18,6 +16,11 @@ java {
withJavadocJar()
}
// Lombok configuration
//lombok {
// version.set("1.18.30") // Use the version from your version catalog if available
//}
// Spotless configuration
spotless {
java {
@@ -35,6 +38,7 @@ spotless {
}
}
tasks {
withType<JavaCompile>().configureEach {
options.encoding = "UTF-8"
@@ -48,29 +52,11 @@ tasks {
// Process resources (e.g., for plugin metadata files)
processResources {
val properties = mapOf(
"timestamp" to rootProject.ext["timestamp"],
"version" to project.version.toString(),
"git_commit" to rootProject.ext["git_commit"],
"git_branch" to rootProject.ext["git_branch"],
"git_repo" to rootProject.ext["git_repo"],
"git_org" to rootProject.ext["git_org"],
"build_shade_pe" to BuildConfig.shadePE,
"build_relocate" to BuildConfig.relocate,
"build_release" to BuildConfig.release,
)
properties.forEach { (key, value) -> inputs.property(key, value) }
filesMatching(
listOf(
"bungee.yml",
"velocity-plugin.json",
"fabric.mod.json",
"grimac.properties"
)
inputs.property("version", project.version)
filesMatching(listOf( // "plugin.yml", Bukkit variable expansion taken care of by net.minecrell.plugin-yml.bukkit
"bungee.yml", "velocity-plugin.json", "fabric.mod.json")
) {
expand(properties)
expand("version" to project.version)
}
}
@@ -95,7 +95,7 @@ fun extractEmbeddedJars(jarFile: File, project: Project): Set<DependencyIdentifi
?: throw IllegalStateException("Missing 'id' in fabric.mod.json of embedded JAR: $entryName")
val idWithDots = id.replace("_", ".")
val lastDotIndex = idWithDots.lastIndexOf(".")
val group = if (lastDotIndex != -1) idWithDots.take(lastDotIndex) else ""
val group = if (lastDotIndex != -1) idWithDots.substring(0, lastDotIndex) else ""
// Extract name and version from the JAR filename
val jarName = entryName.substringAfterLast("/").removeSuffix(".jar")
@@ -127,7 +127,7 @@ fun parseNameAndVersionFromJarName(jarName: String): Pair<String, String>? {
val versionStartIndex = versionStartMatch.range.first
// Split the JAR name into name and version at the version start index
val name = jarName.take(versionStartIndex)
val name = jarName.substring(0, versionStartIndex)
val version = jarName.substring(versionStartIndex + 1) // Skip the hyphen
return if (name.isNotEmpty() && version.isNotEmpty()) {
@@ -300,7 +300,7 @@ project.afterEvaluate {
val allEmbeddedDependencies = mutableMapOf<DependencyIdentifier, File>()
val actuallyIncludedDependencies = mutableSetOf<DependencyIdentifier>()
fun collectAllEmbeddedDependenciesForAfterEvaluate(dependencies: Set<ResolvedDependency>) {
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 ""}"
@@ -318,13 +318,13 @@ project.afterEvaluate {
)
}
}
collectAllEmbeddedDependenciesForAfterEvaluate(dep.children)
collectAllEmbeddedDependencies(dep.children)
}
}
}
processed.clear()
collectAllEmbeddedDependenciesForAfterEvaluate(resolvedDependencies)
collectAllEmbeddedDependencies(resolvedDependencies)
processed.clear()
processDependencies(
@@ -345,7 +345,7 @@ tasks.withType<Jar>().configureEach {
val allEmbeddedDependencies = mutableMapOf<DependencyIdentifier, File>()
val actuallyIncludedDependencies = mutableSetOf<DependencyIdentifier>()
fun collectAllEmbeddedDependenciesForJar(dependencies: Set<ResolvedDependency>) {
fun collectAllEmbeddedDependencies(dependencies: Set<ResolvedDependency>) {
dependencies.forEach { dep ->
val depKey = "${dep.moduleGroup}:${dep.moduleName}:${dep.moduleVersion}"
if (!processed.contains(depKey)) {
@@ -362,13 +362,13 @@ tasks.withType<Jar>().configureEach {
)
}
}
collectAllEmbeddedDependenciesForJar(dep.children)
collectAllEmbeddedDependencies(dep.children)
}
}
}
processed.clear()
collectAllEmbeddedDependenciesForJar(resolvedDependencies)
collectAllEmbeddedDependencies(resolvedDependencies)
processed.clear()
@@ -5,10 +5,17 @@ plugins {
id("com.gradleup.shadow")
}
// Create a configuration for shading dependencies from common
val shadowCommon: Configuration by project.configurations.creating {
isCanBeResolved = true
isCanBeConsumed = false
extendsFrom(project.configurations.getByName("implementation"))
}
tasks.named<ShadowJar>("shadowJar") {
minimize()
archiveFileName = "${rootProject.name}-${project.name}-${rootProject.version}.jar"
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
archiveFileName.set("${rootProject.name}-${project.name}-${rootProject.version}.jar")
from(shadowCommon) // Use from() instead of direct assignment
if (BuildConfig.relocate) {
if (BuildConfig.shadePE) {
@@ -31,7 +38,6 @@ tasks.named<ShadowJar>("shadowJar") {
relocate("org.jetbrains", "ac.grim.grimac.shaded.jetbrains")
relocate("org.incendo", "ac.grim.grimac.shaded.incendo")
relocate("io.leangen.geantyref", "ac.grim.grimac.shaded.geantyref") // Required by cloud
relocate("com.zaxxer", "ac.grim.grimac.shaded.zaxxer") // Database history
}
mergeServiceFiles()
}
@@ -1,96 +1,38 @@
package versioning
import org.gradle.api.Project
import org.gradle.internal.extensions.stdlib.toDefaultLowerCase
import versioning.BuildConfig.init
import versioning.BuildConfig.mavenLocalOverride
import versioning.BuildConfig.release
import versioning.BuildConfig.relocate
import versioning.BuildConfig.shadePE
/**
* BuildConfig provides access to user-defined build flags that control how a Grim
* build is assembled. These flags are resolved once at configuration time using
* [init], and are then exposed as fast, memoized values.
* BuildConfig provides access to user-defined build flags that control how a GrimAC
* build is assembled. These flags are set via Gradle project properties or environment variables.
*
* Flags can be defined in three ways:
* You can use these flags to enable/disable features like shading, relocation, or release mode.
*
* 1. JVM system properties (-Dflag=value)
* 2. Gradle project properties (-Pflag=value or in gradle.properties)
* 3. Environment variables (FLAG=value)
*
* You can use these to enable/disable features like shading, relocation, or release mode.
*
* Examples:
*
* Using Gradle -P properties:
* Usage (command line):
* ```
* ./gradlew build -PshadePE=true -Prelocate=false -Prelease=true
* ```
*
* Using environment variables:
* Or using environment variables:
* ```
* SHADE_PE=true RELOCATE_JAR=false RELEASE=true ./gradlew build
* ```
*
* Using JVM system properties:
* ```
* ./gradlew build -DshadePE=true -Drelease=true
* ```
*
* @property shadePE If true, shades PacketEvents into the jar. Default: true.
* @property shadePE If true, shades PacketEvents into the jar. Default: true.
* @property relocate If true, relocates shaded dependencies to avoid conflicts. Default: true.
* @property release If true, omits commit hash and modifiers from version string. Default: false.
* @property mavenLocalOverride If true, will make artifacts in mavenLocal() will be used instead of their remote counterparts for this build. Default: false
* @property release If true, omits commit hash and modifiers from version string. Default: false.
*/
object BuildConfig {
val shadePE: Boolean
get() = System.getProperty("shadePE")?.toBoolean()
?: System.getenv("SHADE_PE")?.toBoolean()
?: true
/**
* Must be called once from your root build script to initialize the flags.
* Example (in build.gradle.kts):
* ```
* BuildConfig.init(project)
* ```
*/
fun init(project: Project) {
_shadePE = resolveBool(project, "shadePE", altKey = "SHADE_PE", default = true)
_relocate = resolveBool(project, "relocate", altKey = "RELOCATE_JAR", default = true)
_release = resolveBool(project, "release", default = false)
_mavenLocalOverride = resolveBool(project, "mavenLocalOverride", default = false)
}
val relocate: Boolean
get() = System.getProperty("relocate")?.toBoolean()
?: System.getenv("RELOCATE_JAR")?.toBoolean()
?: true
// Unified resolution logic (System > Gradle > Env)
private fun resolveRaw(project: Project, key: String): String? =
System.getProperty(key) // 1. JVM (-Dkey=value)
?: project.findProperty(key)?.toString() // 2. Gradle (-Pkey=value or gradle.properties)
?: System.getenv(key.uppercase()) // 3. ENV (KEY=value)
private fun resolveBool(project: Project, key: String, altKey: String? = null, default: Boolean): Boolean {
return resolveRaw(project, key)?.toDefaultLowerCase()?.toBooleanStrictOrNull()
?: altKey?.let {
resolveRaw(project, it)?.toDefaultLowerCase()?.toBooleanStrictOrNull()
} ?: default
}
// Private backing vars (nullable because we can't use lateinit with primitives)
private var _shadePE: Boolean? = null
private var _relocate: Boolean? = null
private var _release: Boolean? = null
private var _mavenLocalOverride: Boolean? = null
/** If true, shades PacketEvents into the jar. Default: true. */
val shadePE: Boolean get() = _shadePE
?: error("BuildConfig.shadePE accessed before init() was called")
/** If true, relocates shaded dependencies to avoid conflicts. Default: true. */
val relocate: Boolean get() = _relocate
?: error("BuildConfig.relocate accessed before init() was called")
/** If true, omits commit hash and modifiers from version string. Default: false. */
val release: Boolean get() = _release
?: error("BuildConfig.release accessed before init() was called")
val mavenLocalOverride: Boolean get() = _mavenLocalOverride
?: error("BuildConfig.release accessed before init() was called")
}
val release: Boolean
get() = System.getProperty("release")?.toBoolean()
?: System.getenv("RELEASE")?.toBoolean()
?: false
}
@@ -37,31 +37,29 @@ object VersionUtil {
val modifiers = buildList {
if (!BuildConfig.shadePE) add("lite")
if (!BuildConfig.relocate) add("no_relocate")
}.joinToString("-").takeIf { it.isNotEmpty() }
}.takeIf { it.isNotEmpty() }?.joinToString("-")
return buildString {
append(baseVersion)
append("-")
branch?.let { append("$it-") }
append(commitHash)
modifiers?.let { append("+$it") }
modifiers?.let { append("+$modifiers") }
}
}
/**
* Retrieves the current Git commit
* Retrieves the current Git commit as a short hash.
*/
fun getGitCommitHash(full: Boolean = false): String {
private fun getGitCommitHash(): String {
val stdout = ByteArrayOutputStream()
val command = listOfNotNull("git", "rev-parse", if (full) null else "--short", "HEAD")
ProcessBuilder(command)
ProcessBuilder("git", "rev-parse", "--short", "HEAD")
.redirectErrorStream(true)
.start()
.apply { waitFor() }
.inputStream
.use { stdout.writeBytes(it.readAllBytes()) }
val fullCommit = stdout.toString().trim()
return fullCommit.take(minOf(fullCommit.length, 7))
return stdout.toString().trim()
}
/**
@@ -71,7 +69,7 @@ object VersionUtil {
* Any slash (/) in the branch name is replaced with an underscore (_)
* to avoid filesystem issues.
*/
fun getGitBranch(raw: Boolean = false): String? {
private fun getGitBranch(): String? {
val stdout = ByteArrayOutputStream()
ProcessBuilder("git", "rev-parse", "--abbrev-ref", "HEAD")
@@ -80,34 +78,11 @@ object VersionUtil {
.apply { waitFor() }
.inputStream.use { stdout.writeBytes(it.readAllBytes()) }
if (raw) return stdout.toString().trim()
val branch = stdout.toString().trim()
.replace(Regex("[^a-zA-Z0-9_.-]+"), "_")
.replace(Regex("_{2,}"), "_")
.trim(' ', '.', '_', '-')
.removePrefix("heads_")
val mainBranch = System.getenv("GRIM_MAIN_BRANCH") ?: "2.0"
return when (branch) {
"main", mainBranch -> null // ← ignore these branches
else -> branch
"main", "2.0" -> null // ← ignore these branches
else -> branch.replace("/", "_")
}
}
fun getGitUser(): String {
try {
val stdout = ByteArrayOutputStream()
ProcessBuilder("git", "config", "user.name")
.redirectErrorStream(true)
.start()
.apply { waitFor() }
.inputStream.use { stdout.writeBytes(it.readAllBytes()) }
return stdout.toString().trim()
} catch (_: Exception) {
return "unknown"
}
}
}
}
+24 -91
View File
@@ -1,91 +1,41 @@
import net.minecrell.pluginyml.bukkit.BukkitPluginDescription.Permission
import versioning.BuildConfig
plugins {
`maven-publish`
grim.`base-conventions`
grim.`shadow-conventions`
id("de.eldoria.plugin-yml.bukkit") version "0.8.0"
id("xyz.jpenilla.run-paper") version "3.0.0-beta.1"
id("net.minecrell.plugin-yml.bukkit") version "0.6.0"
}
repositories {
if (BuildConfig.mavenLocalOverride) {
mavenLocal()
mavenLocal()
maven {
name = "papermc"
url = uri("https://repo.papermc.io/repository/maven-public/")
}
// For paper-api
exclusiveContent {
forRepository {
maven {
name = "papermc"
url = uri("https://repo.papermc.io/repository/maven-public/")
}
}
filter {
includeGroup("io.papermc.paper")
includeGroup("net.md-5")
maven("https://hub.spigotmc.org/nexus/content/repositories/snapshots/") // Spigot
maven("https://jitpack.io/") { // Grim API
content {
includeGroup("com.github.grimanticheat")
}
}
exclusiveContent {
forRepository {
maven("https://libraries.minecraft.net") { // Brigadier
mavenContent { releasesOnly() }
}
}
filter {
includeModule("com.mojang", "brigadier")
}
}
// For placeholderapi
exclusiveContent {
forRepository {
maven("https://repo.extendedclip.com/content/repositories/placeholderapi/")
}
filter {
includeGroup("me.clip")
}
}
// For GrimAPI and PacketEvents (transitive from :common, direct in :bukkit)
exclusiveContent {
forRepository {
maven("https://repo.grim.ac/snapshots")
}
filter {
includeGroup("ac.grim.grimac")
includeGroup("com.github.retrooper")
}
}
// For Configuralize (transitive from :common)
exclusiveContent {
forRepository {
maven("https://nexus.scarsz.me/content/repositories/releases") {
mavenContent { releasesOnly() }
}
}
filter {
includeGroup("github.scarsz")
}
}
// Maven Central Fallback
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)
if (BuildConfig.shadePE) {
implementation(libs.packetevents.spigot)
} else {
compileOnly(libs.packetevents.spigot)
}
implementation(libs.packetevents.spigot)
implementation(libs.cloud.paper)
implementation(libs.adventure.platform.bukkit)
@@ -101,10 +51,6 @@ bukkit {
apiVersion = "1.13"
foliaSupported = true
if (!BuildConfig.shadePE) {
depend = listOf("packetevents")
}
softDepend = listOf(
"ProtocolLib",
"ProtocolSupport",
@@ -170,22 +116,15 @@ bukkit {
}
register("grim.verbose") {
description = "Receive verbose alerts for violations"
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"
"Enable verbose alerts on join. Requires grim.alerts and grim.alerts.enable-on-join"
default = Permission.Default.FALSE
}
register("grim.list") {
description =
"Shows lists of specific data"
default = Permission.Default.FALSE
}
}
}
@@ -193,14 +132,8 @@ publishing.publications.create<MavenPublication>("maven") {
artifact(tasks["shadowJar"])
}
tasks {
runServer {
minecraftVersion("1.21.4")
}
shadowJar {
manifest {
attributes["paperweight-mappings-namespace"] = "mojang"
}
tasks.shadowJar {
manifest {
attributes["paperweight-mappings-namespace"] = "mojang"
}
}
@@ -20,7 +20,6 @@ public class BukkitPlatformPlugin implements PlatformPlugin {
return plugin.getName();
}
@SuppressWarnings("deprecation")
@Override
public String getVersion() {
return plugin.getDescription().getVersion();
@@ -26,8 +26,8 @@ public class BukkitPlatformServer implements PlatformServer {
}
@Override
public void registerOutgoingPluginChannel(String name) {
GrimACBukkitLoaderPlugin.LOADER.getServer().getMessenger().registerOutgoingPluginChannel(GrimACBukkitLoaderPlugin.LOADER, name);
public void registerOutgoingPluginChannel(String bungeeCord) {
GrimACBukkitLoaderPlugin.LOADER.getServer().getMessenger().registerOutgoingPluginChannel(GrimACBukkitLoaderPlugin.LOADER, "BungeeCord");
}
@Override
@@ -1,10 +1,10 @@
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.events.GrimExtensionManager;
import ac.grim.grimac.manager.init.Initable;
import ac.grim.grimac.manager.init.start.ExemptOnlinePlayersOnReload;
import ac.grim.grimac.manager.init.start.StartableInitable;
@@ -13,7 +13,8 @@ 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.CommandAdapter;
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;
@@ -28,15 +29,14 @@ 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.resolver.BukkitResolverRegistrar;
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 lombok.Getter;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.plugin.ServicePriority;
@@ -48,7 +48,7 @@ import org.incendo.cloud.bukkit.CloudBukkitCapabilities;
import org.incendo.cloud.execution.ExecutionCoordinator;
import org.incendo.cloud.paper.LegacyPaperCommandManager;
import org.incendo.cloud.setting.Configurable;
import org.jetbrains.annotations.NotNull;
public final class GrimACBukkitLoaderPlugin extends JavaPlugin implements PlatformLoader {
@@ -60,19 +60,22 @@ public final class GrimACBukkitLoaderPlugin extends JavaPlugin implements Platfo
private final LazyHolder<CommandManager<Sender>> commandManager = LazyHolder.simple(this::createCommandManager);
private final LazyHolder<ItemResetHandler> itemResetHandler = LazyHolder.simple(BukkitItemResetHandler::new);
@Getter private final PlatformPlayerFactory platformPlayerFactory = new BukkitPlatformPlayerFactory();
@Getter private final CommandAdapter commandAdapter = new BukkitParserDescriptorFactory();
@Getter private final PlatformPluginManager pluginManager = new BukkitPlatformPluginManager();
@Getter private final GrimPlugin plugin;
@Getter private final PlatformServer platformServer = new BukkitPlatformServer();
@Getter private final MessagePlaceHolderManager messagePlaceHolderManager = new BukkitMessagePlaceHolderManager();
@Getter private final BukkitPermissionRegistrationManager permissionManager = new BukkitPermissionRegistrationManager();
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() {
GrimExtensionManager extensionManager = GrimAPI.INSTANCE.getExtensionManager();
BukkitResolverRegistrar registrar = new BukkitResolverRegistrar(extensionManager);
registrar.registerAll();
this.plugin = registrar.resolvePlugin(this);
this.plugin = new BasicGrimPlugin(
this.getLogger(),
this.getDataFolder(),
this.getDescription().getVersion(),
this.getDescription().getDescription(),
this.getDescription().getAuthors()
);
}
@Override
@@ -82,13 +85,13 @@ public final class GrimACBukkitLoaderPlugin extends JavaPlugin implements Platfo
}
private Initable[] getBukkitInitTasks() {
return new Initable[] {
return new Initable[]{
new ExemptOnlinePlayersOnReload(),
new BukkitEventManager(),
new BukkitTickEndEvent(),
new BukkitBStats(),
(StartableInitable) () -> {
if (BukkitMessagePlaceHolderManager.hasPlaceholderAPI) {
if (MessageUtil.hasPlaceholderAPI) {
new PlaceholderAPIExpansion().register();
}
}
@@ -110,6 +113,16 @@ public final class GrimACBukkitLoaderPlugin extends JavaPlugin implements Platfo
return scheduler.get();
}
@Override
public PlatformPlayerFactory getPlatformPlayerFactory() {
return playerFactory;
}
@Override
public ParserDescriptorFactory getParserDescriptorFactory() {
return parserFactory;
}
@Override
public PacketEventsAPI<?> getPacketEvents() {
return packetEvents.get();
@@ -130,12 +143,37 @@ public final class GrimACBukkitLoaderPlugin extends JavaPlugin implements Platfo
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();
}
@@ -4,15 +4,16 @@ import ac.grim.grimac.GrimAPI;
import ac.grim.grimac.platform.api.command.PlayerSelector;
import ac.grim.grimac.platform.api.sender.Sender;
import ac.grim.grimac.platform.bukkit.sender.BukkitSenderFactory;
import lombok.RequiredArgsConstructor;
import org.incendo.cloud.bukkit.data.SinglePlayerSelector;
import java.util.Collection;
import java.util.Collections;
@RequiredArgsConstructor
public class BukkitPlayerSelectorAdapter implements PlayerSelector {
private final SinglePlayerSelector bukkitSelector;
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() {
@@ -6,24 +6,21 @@ import ac.grim.grimac.platform.bukkit.utils.convert.BukkitConversionUtils;
import ac.grim.grimac.platform.bukkit.utils.reflection.PaperUtils;
import ac.grim.grimac.platform.bukkit.world.BukkitPlatformWorld;
import ac.grim.grimac.utils.math.Location;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.google.common.base.Preconditions;
import org.bukkit.entity.Entity;
import org.jetbrains.annotations.NotNull;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
public class BukkitGrimEntity implements GrimEntity {
protected static final boolean CAN_USE_DIRECT_GETTERS = PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_20_1) && PaperUtils.PAPER;
private final Entity entity;
private BukkitPlatformWorld bukkitPlatformWorld;
public BukkitGrimEntity(Entity entity) {
this.entity = Objects.requireNonNull(entity);
Preconditions.checkArgument(entity != null);
this.entity = entity;
}
public Entity getBukkitEntity() {
@@ -47,7 +44,7 @@ public class BukkitGrimEntity implements GrimEntity {
}
@Override
@NotNull
@NonNull
public Entity getNative() {
return entity;
}
@@ -79,26 +76,4 @@ public class BukkitGrimEntity implements GrimEntity {
location.getPitch()
);
}
@Override
public double distanceSquared(double oX, double oY, double oZ) {
if (CAN_USE_DIRECT_GETTERS) {
double x = this.entity.getX();
double y = this.entity.getY();
double z = this.entity.getZ();
double distX = (x - oX) * (x - oX);
double distY = (y - oY) * (y - oY);
double distZ = (z - oZ) * (z - oZ);
return distX + distY + distZ;
} else {
org.bukkit.Location location = this.entity.getLocation();
double x = location.getX();
double y = location.getY();
double z = location.getZ();
double distX = (x - oX) * (x - oX);
double distY = (y - oY) * (y - oY);
double distZ = (z - oZ) * (z - oZ);
return distX + distY + distZ;
}
}
}
@@ -5,7 +5,6 @@ import ac.grim.grimac.platform.bukkit.utils.convert.BukkitConversionUtils;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
import ac.grim.grimac.utils.data.PistonData;
import ac.grim.grimac.utils.data.TrackedPosition;
import com.github.retrooper.packetevents.protocol.world.BlockFace;
import org.bukkit.Material;
import org.bukkit.block.Block;
@@ -19,20 +18,8 @@ import java.util.ArrayList;
import java.util.List;
public class PistonEvent implements Listener {
private final Material SLIME_BLOCK = Material.getMaterial("SLIME_BLOCK");
private final Material HONEY_BLOCK = Material.getMaterial("HONEY_BLOCK");
private static final double MAX_HORIZONTAL_DISTANCE = 24.0;
private static final double MAX_VERTICAL_DISTANCE = 64.0;
// accuracy isn't that important, it's close enough and performant
private static boolean isCloseEnough(final int aX, final int aY, final int aZ,
final double bX, final double bY, final double bZ) {
return Math.abs(aX - bX) <= MAX_HORIZONTAL_DISTANCE
&& Math.abs(aY - bY) <= MAX_VERTICAL_DISTANCE
&& Math.abs(aZ - bZ) <= MAX_HORIZONTAL_DISTANCE;
}
Material SLIME_BLOCK = Material.getMaterial("SLIME_BLOCK");
Material HONEY_BLOCK = Material.getMaterial("HONEY_BLOCK");
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onPistonPushEvent(BlockPistonExtendEvent event) {
@@ -68,20 +55,16 @@ public class PistonEvent implements Listener {
piston.getY() + event.getDirection().getModY(),
piston.getZ() + event.getDirection().getModZ()));
final int chunkX = event.getBlock().getX() >> 4;
final int chunkZ = event.getBlock().getZ() >> 4;
final BlockFace blockFace = BukkitConversionUtils.fromBukkitFace(event.getDirection());
final int sourceX = piston.getX();
final int sourceY = piston.getY();
final int sourceZ = piston.getZ();
boolean finalHasSlimeBlock = hasSlimeBlock;
boolean finalHasHoneyBlock = hasHoneyBlock;
for (GrimPlayer player : GrimAPI.INSTANCE.getPlayerDataManager().getEntries()) {
TrackedPosition position = player.compensatedEntities.self.trackedServerPosition;
if (player.compensatedWorld.isChunkLoaded(chunkX, chunkZ) && isCloseEnough(sourceX, sourceY, sourceZ, position.getX(), position.getY(), position.getZ())) {
final int lastTrans = player.lastTransactionSent.get();
PistonData data = new PistonData(blockFace, boxes, lastTrans, true, hasSlimeBlock, hasHoneyBlock);
player.latencyUtils.addRealTimeTaskAsync(lastTrans, () -> player.compensatedWorld.activePistons.add(data));
}
int lastTrans = player.lastTransactionSent.get();
player.runSafely(() -> {
if (player.compensatedWorld.isChunkLoaded(event.getBlock().getX() >> 4, event.getBlock().getZ() >> 4)) {
PistonData data = new PistonData(BukkitConversionUtils.fromBukkitFace(event.getDirection()), boxes, lastTrans, true, finalHasSlimeBlock, finalHasHoneyBlock);
player.latencyUtils.addRealTimeTaskAsync(lastTrans, () -> player.compensatedWorld.activePistons.add(data));
}
});
}
}
@@ -130,20 +113,16 @@ public class PistonEvent implements Listener {
}
}
final Block block = event.getBlock();
final int chunkX = block.getX() >> 4;
final int chunkZ = block.getZ() >> 4;
final int sourceX = block.getX();
final int sourceY = block.getY();
final int sourceZ = block.getZ();
boolean finalHasSlimeBlock = hasSlimeBlock;
boolean finalHasHoneyBlock = hasHoneyBlock;
for (GrimPlayer player : GrimAPI.INSTANCE.getPlayerDataManager().getEntries()) {
TrackedPosition position = player.compensatedEntities.self.trackedServerPosition;
if (player.compensatedWorld.isChunkLoaded(chunkX, chunkZ) && isCloseEnough(sourceX, sourceY, sourceZ, position.getX(), position.getY(), position.getZ())) {
int lastTrans = player.lastTransactionSent.get();
PistonData data = new PistonData(face, boxes, lastTrans, false, hasSlimeBlock, hasHoneyBlock);
player.latencyUtils.addRealTimeTaskAsync(lastTrans, () -> player.compensatedWorld.activePistons.add(data));
}
int lastTrans = player.lastTransactionSent.get();
player.runSafely(() -> {
if (player.compensatedWorld.isChunkLoaded(event.getBlock().getX() >> 4, event.getBlock().getZ() >> 4)) {
PistonData data = new PistonData(BukkitConversionUtils.fromBukkitFace(event.getDirection()), boxes, lastTrans, false, finalHasSlimeBlock, finalHasHoneyBlock);
player.latencyUtils.addRealTimeTaskAsync(lastTrans, () -> player.compensatedWorld.activePistons.add(data));
}
});
}
}
}
@@ -2,14 +2,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.utils.anticheat.Constants;
import io.github.retrooper.packetevents.bstats.bukkit.Metrics;
public class BukkitBStats implements StartableInitable {
@Override
public void start() {
int pluginId = 12820; // <-- Replace with the id of your plugin!
try {
new Metrics(GrimACBukkitLoaderPlugin.LOADER, Constants.BSTATS_PLUGIN_ID);
} catch (Exception ignored) {}
new Metrics(GrimACBukkitLoaderPlugin.LOADER, pluginId);
} catch (Exception ignored) {
}
}
}
@@ -86,7 +86,7 @@ public class BukkitTickEndEvent extends AbstractTickEndEvent implements Listener
Unsafe unsafe = (Unsafe) unsafeField.get(null);
unsafe.putObject(connection, unsafe.objectFieldOffset(connectionsList), wrapper);
} catch (NoSuchFieldException | IllegalAccessException e) {
LogUtil.error("Failed to inject into the end of tick event via reflection", e);
LogUtil.exception("Failed to inject into the end of tick event via reflection", e);
return false;
}
return true;
@@ -2,212 +2,95 @@ package ac.grim.grimac.platform.bukkit.manager;
import ac.grim.grimac.platform.api.manager.ItemResetHandler;
import ac.grim.grimac.platform.api.player.PlatformPlayer;
import ac.grim.grimac.platform.bukkit.player.BukkitPlatformPlayer;
import ac.grim.grimac.platform.bukkit.utils.reflection.PaperUtils;
import ac.grim.grimac.utils.reflection.ReflectionUtils;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.player.InteractionHand;
import lombok.SneakyThrows;
import org.bukkit.Bukkit;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.inventory.EquipmentSlot;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
public class BukkitItemResetHandler implements ItemResetHandler {
private static final Consumer<Player> resetItemUsage;
private static final Predicate<Player> isUsingItem;
private static final Function<Player, InteractionHand> getItemUsageHand;
// resets item usage, then returns whether the player was using an item
private final @NotNull ItemUsageReset resetItemUsage = createItemUsageResetFunction();
@Override
@SneakyThrows
public void resetItemUsage(@Nullable PlatformPlayer player) {
if (player != null) resetItemUsage.accept((Player) player.getNative());
}
@Override
public @Nullable InteractionHand getItemUsageHand(@Nullable PlatformPlayer player) {
return player == null ? null : getItemUsageHand.apply((Player) player.getNative());
}
@Override
public boolean isUsingItem(@Nullable PlatformPlayer player) {
return player != null && isUsingItem.test((Player) player.getNative());
}
static {
final ServerVersion version = PacketEvents.getAPI().getServerManager().getVersion();
boolean legacy = version.isOlderThanOrEquals(ServerVersion.V_1_8_8);
try {
Method getHandle_ = null;
if (PaperUtils.PAPER && version.isNewerThanOrEquals(ServerVersion.V_1_20_5)) {
Class<?> cls = ReflectionUtils.getClass("org.bukkit.craftbukkit.entity.CraftLivingEntity");
if (cls != null) getHandle_ = cls.getMethod("getHandle");
}
boolean obfuscated = getHandle_ == null;
String nmsPackage = obfuscated ? Bukkit.getServer().getClass().getPackageName().split("\\.")[3] : null;
if (obfuscated) {
String clazzName = legacy ? "CraftHumanEntity" : "CraftLivingEntity";
getHandle_ = Class.forName("org.bukkit.craftbukkit." + nmsPackage + ".entity." + clazzName).getMethod("getHandle");
}
final Method getHandle = getHandle_;
Class<?> clazz = getHandle_.getReturnType();
if (version.isNewerThanOrEquals(ServerVersion.V_1_10)) {
isUsingItem = Player::isHandRaised;
} else {
Method method = clazz.getMethod(switch (Objects.requireNonNull(nmsPackage)) {
case "v1_8_R3" -> "bS";
case "v1_9_R1" -> "cs";
case "v1_9_R2" -> "ct";
default -> throw new IllegalStateException("You are using an unsupported server version! (" + version.getReleaseName() + ")");
});
isUsingItem = player -> {
try {
return (boolean) method.invoke(getHandle.invoke(player));
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
};
}
if (legacy) {
getItemUsageHand = player -> isUsingItem.test(player) ? InteractionHand.MAIN_HAND : null;
} else if (PaperUtils.PAPER && version.isNewerThanOrEquals(ServerVersion.V_1_16_5)) {
getItemUsageHand = player -> player.isHandRaised()
? player.getHandRaised() == EquipmentSlot.OFF_HAND
? InteractionHand.OFF_HAND
: InteractionHand.MAIN_HAND
: null;
} else {
Method method = clazz.getMethod(switch (Objects.requireNonNull(nmsPackage)) {
case "v1_9_R1" -> "ct";
case "v1_9_R2" -> "cu";
case "v1_10_R1" -> "cy";
case "v1_11_R1" -> "cz";
case "v1_12_R1" -> "cH";
case "v1_13_R1", "v1_13_R2", "v1_14_R1" -> "cU";
case "v1_15_R1", "v1_16_R1", "v1_16_R2", "v1_16_R3", "v1_17_R1" -> "getRaisedHand";
case "v1_18_R1" -> "eM";
case "v1_18_R2" -> "eN";
case "v1_19_R1" -> "eU";
case "v1_19_R2" -> "fa";
case "v1_19_R3" -> "ff";
case "v1_20_R1" -> "fj";
case "v1_20_R2" -> "fn";
case "v1_20_R3" -> "fo";
case "v1_20_R4" -> "fw";
case "v1_21_R1" -> "fs";
case "v1_21_R2", "v1_21_R3", "v1_21_R4" -> "fA";
case "v1_21_R5" -> "fH";
case "v1_21_R6" -> "fP";
default -> throw new IllegalStateException("You are using an unsupported server version! (" + version.getReleaseName() + ")");
});
getItemUsageHand = player -> {
try {
return isUsingItem.test(player)
? ((Enum<?>) method.invoke(getHandle.invoke(player))).ordinal() == 0
? InteractionHand.MAIN_HAND
: InteractionHand.OFF_HAND
: null;
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
};
}
Method setLivingEntityFlag;
if (version.isNewerThanOrEquals(ServerVersion.V_1_19)) {
String name = obfuscated ? "c" : "setLivingEntityFlag";
setLivingEntityFlag = clazz.getDeclaredMethod(name, int.class, boolean.class);
setLivingEntityFlag.setAccessible(true);
} else {
setLivingEntityFlag = null;
}
if (PaperUtils.PAPER && version.isNewerThan(ServerVersion.V_1_17)) {
resetItemUsage = setLivingEntityFlag == null ? LivingEntity::clearActiveItem : player -> {
try {
setLivingEntityFlag.invoke(getHandle.invoke(player), 1, false);
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
player.clearActiveItem();
};
} else {
Method method = clazz.getMethod(switch (Objects.requireNonNull(nmsPackage)) {
case "v1_8_R3" -> "bV";
case "v1_9_R1" -> "cz";
case "v1_9_R2" -> "cA";
case "v1_10_R1" -> "cE";
case "v1_11_R1" -> "cF";
case "v1_12_R1" -> "cN";
case "v1_13_R1", "v1_13_R2" -> "da";
case "v1_14_R1" -> "dp";
case "v1_15_R1" -> "dH";
case "v1_16_R1", "v1_16_R2", "v1_16_R3", "v1_17_R1" -> "clearActiveItem";
case "v1_18_R1" -> "eR";
case "v1_18_R2" -> "eS";
case "v1_19_R1" -> "eZ";
case "v1_19_R2" -> "ff";
case "v1_19_R3" -> "fk";
case "v1_20_R1" -> "fo";
case "v1_20_R2" -> "fs";
case "v1_20_R3" -> "ft";
case "v1_20_R4" -> "fB";
case "v1_21_R1" -> "fx";
case "v1_21_R2", "v1_21_R3", "v1_21_R4" -> "fF";
case "v1_21_R5" -> "fM";
case "v1_21_R6" -> "fU";
default -> throw new IllegalStateException("You are using an unsupported server version! (" + version.getReleaseName() + ")");
});
if (legacy) { // 1.8.8
resetItemUsage = player -> {
try {
method.invoke(getHandle.invoke(player));
// in 1.8 we need to resync item usage manually,
// only do so if the player is using an item
if (isUsingItem.test(player)) player.updateInventory();
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
};
} else if (setLivingEntityFlag == null) { // 1.9-1.18.2
resetItemUsage = player -> {
try {
method.invoke(getHandle.invoke(player));
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
};
} else { // 1.19+
resetItemUsage = player -> {
try {
Object handle = getHandle.invoke(player);
setLivingEntityFlag.invoke(handle, 1, false);
method.invoke(handle);
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
};
}
}
} catch (Throwable t) {
throw t instanceof RuntimeException e ? e : new RuntimeException(t);
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;
}
}
@@ -2,18 +2,27 @@ 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 com.github.retrooper.packetevents.util.reflection.Reflection;
import ac.grim.grimac.utils.anticheat.MessageUtil;
import me.clip.placeholderapi.PlaceholderAPI;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
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 {
public static final boolean hasPlaceholderAPI = Reflection.getClassByNameWithoutException("me.clip.placeholderapi.PlaceholderAPI") != null;
@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(commandSender instanceof OfflinePlayer offlinePlayer ? offlinePlayer : null, string);
}
@Override
public @NotNull String replacePlaceholders(@Nullable PlatformPlayer player, @NotNull String string) {
if (!hasPlaceholderAPI) return string;
return PlaceholderAPI.setPlaceholders(player instanceof BukkitPlatformPlayer bukkitPlatformPlayer ? bukkitPlatformPlayer.getBukkitPlayer() : null, string);
public @NonNull String replacePlaceholders(@Nullable PlatformPlayer object, @NonNull String string) {
if (!MessageUtil.hasPlaceholderAPI) return string;
return PlaceholderAPI.setPlaceholders(object instanceof BukkitPlatformPlayer bukkitPlatformPlayer ? bukkitPlatformPlayer.getBukkitPlayer() : null, string);
}
}
@@ -1,45 +1,17 @@
package ac.grim.grimac.platform.bukkit.manager;
import ac.grim.grimac.platform.api.command.PlayerSelector;
import ac.grim.grimac.platform.api.manager.CommandAdapter;
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.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.incendo.cloud.bukkit.BukkitCommandContextKeys;
import org.incendo.cloud.parser.ParserDescriptor;
import org.incendo.cloud.suggestion.Suggestion;
import org.incendo.cloud.suggestion.SuggestionProvider;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
public class BukkitParserDescriptorFactory implements ParserDescriptorFactory {
public class BukkitParserDescriptorFactory implements CommandAdapter {
// Parser is kept as a field because its stateless
private final BukkitPlayerSelectorParser<Sender> bukkitPlayerSelectorParser = new BukkitPlayerSelectorParser<>();
BukkitPlayerSelectorParser<Sender> bukkitPlayerSelectorParser = new BukkitPlayerSelectorParser<>();
@Override
public ParserDescriptor<Sender, PlayerSelector> singlePlayerSelectorParser() {
public ParserDescriptor<Sender, PlayerSelector> getSinglePlayer() {
return bukkitPlayerSelectorParser.descriptor();
}
// TODO (Cross-platform) better + brigadier suggestions
@Override
public SuggestionProvider<Sender> onlinePlayerSuggestions() {
return (context, input) -> {
List<Suggestion> suggestions = new ArrayList<>();
for(Player player : Bukkit.getOnlinePlayers()) {
CommandSender bukkit = context.get(BukkitCommandContextKeys.BUKKIT_COMMAND_SENDER);
if (!(bukkit instanceof Player) || ((Player)bukkit).canSee(player)) {
suggestions.add(Suggestion.suggestion(player.getName()));
}
}
return CompletableFuture.completedFuture(suggestions);
};
}
}
@@ -5,7 +5,7 @@ import ac.grim.grimac.platform.api.manager.PlatformPluginManager;
import ac.grim.grimac.platform.bukkit.BukkitPlatformPlugin;
import org.bukkit.Bukkit;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.Nullable;
import org.checkerframework.checker.nullness.qual.Nullable;
public class BukkitPlatformPluginManager implements PlatformPluginManager {
@@ -1,34 +0,0 @@
package ac.grim.grimac.platform.bukkit.player;
import ac.grim.grimac.platform.api.player.OfflinePlatformPlayer;
import lombok.RequiredArgsConstructor;
import org.bukkit.OfflinePlayer;
import org.jetbrains.annotations.NotNull;
import java.util.Objects;
import java.util.UUID;
@RequiredArgsConstructor
public class BukkitOfflinePlatformPlayer implements OfflinePlatformPlayer {
private final OfflinePlayer offlinePlayer;
@Override
public boolean isOnline() {
return offlinePlayer.isOnline();
}
@Override
public @NotNull String getName() {
return Objects.requireNonNull(offlinePlayer.getName());
}
@Override
public @NotNull UUID getUniqueId() {
return offlinePlayer.getUniqueId();
}
@Override
public boolean equals(Object o) {
return o instanceof OfflinePlatformPlayer player && this.getUniqueId().equals(player.getUniqueId());
}
}
@@ -3,14 +3,16 @@ package ac.grim.grimac.platform.bukkit.player;
import ac.grim.grimac.platform.api.player.PlatformInventory;
import com.github.retrooper.packetevents.protocol.item.ItemStack;
import io.github.retrooper.packetevents.util.SpigotConversionUtil;
import lombok.RequiredArgsConstructor;
import org.bukkit.entity.Player;
@RequiredArgsConstructor
public class BukkitPlatformInventory implements PlatformInventory {
private final Player bukkitPlayer;
public BukkitPlatformInventory(Player bukkitPlayer) {
this.bukkitPlayer = bukkitPlayer;
}
@Override
public ItemStack getItemInHand() {
return SpigotConversionUtil.fromBukkitItemStack(bukkitPlayer.getInventory().getItemInHand());
@@ -33,7 +35,7 @@ public class BukkitPlatformInventory implements PlatformInventory {
@Override
public ItemStack getChestplate() {
return SpigotConversionUtil.fromBukkitItemStack(bukkitPlayer.getInventory().getChestplate());
return SpigotConversionUtil.fromBukkitItemStack(bukkitPlayer.getInventory().getHelmet());
}
@Override
@@ -9,11 +9,8 @@ import ac.grim.grimac.platform.bukkit.entity.BukkitGrimEntity;
import ac.grim.grimac.platform.bukkit.utils.anticheat.MultiLibUtil;
import ac.grim.grimac.platform.bukkit.utils.convert.BukkitConversionUtils;
import ac.grim.grimac.platform.bukkit.utils.reflection.PaperUtils;
import ac.grim.grimac.utils.common.arguments.CommonGrimArguments;
import ac.grim.grimac.utils.math.Location;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.protocol.player.GameMode;
import com.github.retrooper.packetevents.protocol.player.User;
import com.github.retrooper.packetevents.util.Vector3d;
import io.github.retrooper.packetevents.util.SpigotConversionUtil;
import lombok.Getter;
@@ -23,33 +20,22 @@ import org.bukkit.World;
import org.bukkit.entity.Player;
import org.bukkit.permissions.Permission;
import org.bukkit.permissions.PermissionDefault;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
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;
@Getter
private final PlatformInventory inventory;
private final @Nullable User user;
public BukkitPlatformPlayer(Player bukkitPlayer) {
super(bukkitPlayer);
this.bukkitPlayer = bukkitPlayer;
this.inventory = new BukkitPlatformInventory(bukkitPlayer);
if (CommonGrimArguments.USE_CHAT_FAST_BYPASS.value()) {
Object channel = PacketEvents.getAPI().getProtocolManager().getChannel(bukkitPlayer.getUniqueId());
this.user = PacketEvents.getAPI().getProtocolManager().getUser(channel);
} else {
this.user = null;
}
}
@Override
@@ -79,20 +65,12 @@ public class BukkitPlatformPlayer extends BukkitGrimEntity implements PlatformPl
@Override
public void sendMessage(String message) {
if (CommonGrimArguments.USE_CHAT_FAST_BYPASS.value() && user != null) {
user.sendMessage(message);
} else {
bukkitPlayer.sendMessage(message);
}
bukkitPlayer.sendMessage(message);
}
@Override
public void sendMessage(Component message) {
if (CommonGrimArguments.USE_CHAT_FAST_BYPASS.value() && user != null) {
user.sendMessage(message);
} else {
audiences.player(bukkitPlayer).sendMessage(message);
}
audiences.player(bukkitPlayer).sendMessage(message);
}
@Override
@@ -112,17 +90,18 @@ public class BukkitPlatformPlayer extends BukkitGrimEntity implements PlatformPl
@Override
public Vector3d getPosition() {
if (CAN_USE_DIRECT_GETTERS) {
return new Vector3d(this.bukkitPlayer.getX(), this.bukkitPlayer.getY(), this.bukkitPlayer.getZ());
} else {
org.bukkit.Location location = this.bukkitPlayer.getLocation();
return new Vector3d(location.getX(), location.getY(), location.getZ());
}
org.bukkit.Location location = this.bukkitPlayer.getLocation();
return new Vector3d(location.getX(), location.getY(), location.getZ());
}
@Override
public @Nullable GrimEntity getVehicle() {
return bukkitPlayer.getVehicle() == null ? null : new BukkitGrimEntity(bukkitPlayer.getVehicle());
public PlatformInventory getInventory() {
return inventory;
}
@Override
public GrimEntity getVehicle() {
return new BukkitGrimEntity(bukkitPlayer.getVehicle());
}
@Override
@@ -171,7 +150,7 @@ public class BukkitPlatformPlayer extends BukkitGrimEntity implements PlatformPl
}
@Override
@NotNull
@NonNull
public Player getNative() {
return this.bukkitPlayer;
}
@@ -1,10 +1,8 @@
package ac.grim.grimac.platform.bukkit.player;
import ac.grim.grimac.platform.api.player.AbstractPlatformPlayerFactory;
import ac.grim.grimac.platform.api.player.OfflinePlatformPlayer;
import ac.grim.grimac.platform.api.player.PlatformPlayer;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
@@ -13,27 +11,31 @@ import java.util.UUID;
public class BukkitPlatformPlayerFactory extends AbstractPlatformPlayerFactory<Player> {
@Override
protected Player getNativePlayer(@NotNull UUID uuid) {
return Bukkit.getPlayer(uuid);
}
@Override
protected Player getNativePlayer(@NotNull String name) {
return Bukkit.getPlayer(name);
}
@Override
protected PlatformPlayer createPlatformPlayer(@NotNull Player nativePlayer) {
return new BukkitPlatformPlayer(nativePlayer);
}
@Override
protected boolean isNativePlayerType(@NotNull Object playerObject) {
return playerObject instanceof Player;
}
@Override
protected UUID getPlayerUUID(@NotNull Player nativePlayer) {
return nativePlayer.getUniqueId();
}
@Override
protected Class<Player> getNativePlayerClass() {
return Player.class;
}
// The cast is safe because Bukkit.getOnlinePlayers() is guaranteed to contain Player or its subtypes,
// and we're only reading from it.
@Override
@@ -42,16 +44,4 @@ public class BukkitPlatformPlayerFactory extends AbstractPlatformPlayerFactory<P
// Cast Collection<? extends Player> to Collection<Player>
return (Collection<Player>) Bukkit.getOnlinePlayers();
}
@Override
public OfflinePlatformPlayer getOfflineFromUUID(@NotNull UUID uuid) {
OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(uuid);
return new BukkitOfflinePlatformPlayer(offlinePlayer);
}
@Override
public OfflinePlatformPlayer getOfflineFromName(@NotNull String name) {
OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(name);
return new BukkitOfflinePlatformPlayer(offlinePlayer);
}
}
@@ -1,111 +0,0 @@
package ac.grim.grimac.platform.bukkit.resolver;
import ac.grim.grimac.api.plugin.BasicGrimPlugin;
import ac.grim.grimac.api.plugin.GrimPlugin;
import ac.grim.grimac.events.GrimExtensionManager;
import org.bukkit.Bukkit;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginDescriptionFile;
import org.bukkit.plugin.java.JavaPlugin;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* Manages the registration of Bukkit-specific resolvers with the GrimExtensionManager.
* This class is designed to be instantiated once during plugin startup.
*/
public final class BukkitResolverRegistrar {
private final GrimExtensionManager extensionManager;
// Cache to ensure we only create one GrimPlugin wrapper per Bukkit Plugin instance.
private final Map<Plugin, GrimPlugin> pluginCache = new ConcurrentHashMap<>();
public BukkitResolverRegistrar(GrimExtensionManager extensionManager) {
this.extensionManager = extensionManager;
}
/**
* Registers all the Bukkit-specific resolvers in order of performance (fastest to slowest).
*/
public void registerAll() {
extensionManager.setFailureHandler(this::createFailureException);
extensionManager.registerResolver(this::resolvePluginInstance);
extensionManager.registerResolver(this::resolveStringName);
extensionManager.registerResolver(this::resolveClass);
}
/**
* The core logic for converting a Bukkit Plugin into a GrimPlugin, with caching.
*/
public GrimPlugin resolvePlugin(Plugin bukkitPlugin) {
return pluginCache.computeIfAbsent(bukkitPlugin, plugin -> {
PluginDescriptionFile desc = plugin.getDescription();
return new BasicGrimPlugin(
plugin.getLogger(),
plugin.getDataFolder(),
desc.getVersion(),
desc.getDescription(),
desc.getAuthors()
);
});
}
/**
* 1. Resolver for raw Plugin instances (the most common case).
*/
private GrimPlugin resolvePluginInstance(Object context) {
if (context instanceof Plugin bukkitPlugin) {
return resolvePlugin(bukkitPlugin);
}
return null;
}
/**
* 2. Resolver for String plugin names.
*/
private GrimPlugin resolveStringName(Object context) {
if (context instanceof String pluginName) {
// Use the Bukkit API to look up the plugin by name. This returns null if not found.
Plugin bukkitPlugin = Bukkit.getPluginManager().getPlugin(pluginName);
// We must check for null before applying the logic to avoid a NullPointerException.
return (bukkitPlugin != null) ? resolvePlugin(bukkitPlugin) : null;
}
return null;
}
/**
* 3. Resolver for Class objects.
*/
private GrimPlugin resolveClass(Object context) {
if (context instanceof Class) {
try {
// First, resolve the Class to a Plugin instance.
JavaPlugin providingPlugin = JavaPlugin.getProvidingPlugin((Class<?>) context);
// Then, use our shared logic directly to get the GrimPlugin wrapper.
return resolvePlugin(providingPlugin);
} catch (IllegalArgumentException | IllegalStateException e) {
// This is an expected failure if the class is not from a plugin
// or is called from a static initializer. We simply indicate that
// this resolver cannot handle this context.
return null;
}
}
// This resolver only handles Classes.
return null;
}
private RuntimeException createFailureException(Object failedContext) {
String message = """
Failed to resolve GrimPlugin context from the provided object of type '%s'.
Please ensure you are passing one of the following:
- The main instance of your plugin (e.g., 'this' from your class extending JavaPlugin).
- The plugin name as a String (e.g., "MyPluginName").
- Any Class from your plugin's JAR file (e.g., MyListener.class).
- A pre-existing GrimPlugin instance.
""".formatted(failedContext.getClass().getName());
return new IllegalArgumentException(message);
}
}
@@ -22,31 +22,29 @@ public class BukkitAsyncScheduler implements AsyncScheduler {
@Override
public TaskHandle runDelayed(@NotNull GrimPlugin plugin, @NotNull Runnable task, long delay, @NotNull TimeUnit timeUnit) {
return new BukkitTaskHandle(bukkitScheduler.runTaskLaterAsynchronously(
GrimACBukkitLoaderPlugin.LOADER,
return new BukkitTaskHandle(bukkitScheduler.runTaskLaterAsynchronously(GrimACBukkitLoaderPlugin.LOADER,
task,
PlatformScheduler.convertTimeToTicks(delay, timeUnit)
));
PlatformScheduler.convertTimeToTicks(delay, timeUnit))
);
}
@Override
public TaskHandle runAtFixedRate(@NotNull GrimPlugin plugin, @NotNull Runnable task, long delay, long period, @NotNull TimeUnit timeUnit) {
return new BukkitTaskHandle(bukkitScheduler.runTaskTimerAsynchronously(
GrimACBukkitLoaderPlugin.LOADER,
task,
PlatformScheduler.convertTimeToTicks(delay, timeUnit),
PlatformScheduler.convertTimeToTicks(period, timeUnit)
));
return new BukkitTaskHandle(
bukkitScheduler.runTaskTimerAsynchronously(
GrimACBukkitLoaderPlugin.LOADER,
task,
PlatformScheduler.convertTimeToTicks(delay, timeUnit),
PlatformScheduler.convertTimeToTicks(period, timeUnit)
)
);
}
@Override
public TaskHandle runAtFixedRate(@NotNull GrimPlugin plugin, @NotNull Runnable task, long initialDelayTicks, long periodTicks) {
return new BukkitTaskHandle(bukkitScheduler.runTaskTimerAsynchronously(
GrimACBukkitLoaderPlugin.LOADER,
task,
initialDelayTicks,
periodTicks
));
return new BukkitTaskHandle(
bukkitScheduler.runTaskTimerAsynchronously(GrimACBukkitLoaderPlugin.LOADER, task, initialDelayTicks, periodTicks)
);
}
@Override
@@ -11,25 +11,26 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class BukkitEntityScheduler implements EntityScheduler {
private final BukkitScheduler scheduler = Bukkit.getScheduler();
private final BukkitScheduler bukkitScheduler = Bukkit.getScheduler();
@Override
public void execute(@NotNull GrimEntity entity, @NotNull GrimPlugin plugin, @NotNull Runnable run, @Nullable Runnable retired, long delay) {
scheduler.runTaskLater(GrimACBukkitLoaderPlugin.LOADER, run, 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(scheduler.runTask(GrimACBukkitLoaderPlugin.LOADER, task));
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(scheduler.runTaskLater(GrimACBukkitLoaderPlugin.LOADER, task, 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(scheduler.runTaskTimer(GrimACBukkitLoaderPlugin.LOADER, task, initialDelayTicks, periodTicks));
return new BukkitTaskHandle(bukkitScheduler.runTaskTimer(GrimACBukkitLoaderPlugin.LOADER, task, initialDelayTicks, periodTicks));
}
}
@@ -13,8 +13,8 @@ public class BukkitGlobalRegionScheduler implements GlobalRegionScheduler {
private final BukkitScheduler bukkitScheduler = Bukkit.getScheduler();
@Override
public void execute(@NotNull GrimPlugin plugin, @NotNull Runnable task) {
bukkitScheduler.runTask(GrimACBukkitLoaderPlugin.LOADER, task);
public void execute(@NotNull GrimPlugin plugin, @NotNull Runnable run) {
bukkitScheduler.runTask(GrimACBukkitLoaderPlugin.LOADER, run);
}
@Override
@@ -1,13 +1,36 @@
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 lombok.Getter;
import org.jetbrains.annotations.NotNull;
import ac.grim.grimac.platform.api.scheduler.RegionScheduler;
import org.checkerframework.checker.nullness.qual.NonNull;
@Getter
public class BukkitPlatformScheduler implements PlatformScheduler {
private final @NotNull BukkitAsyncScheduler asyncScheduler = new BukkitAsyncScheduler();
private final @NotNull BukkitGlobalRegionScheduler globalRegionScheduler = new BukkitGlobalRegionScheduler();
private final @NotNull BukkitEntityScheduler entityScheduler = new BukkitEntityScheduler();
private final @NotNull BukkitRegionScheduler regionScheduler = new BukkitRegionScheduler();
private final BukkitAsyncScheduler bukkitAsyncScheduler = new BukkitAsyncScheduler();
private final BukkitGlobalRegionScheduler bukkitGlobalRegionScheduler = new BukkitGlobalRegionScheduler();
private final BukkitEntityScheduler bukkitEntityScheduler = new BukkitEntityScheduler();
private final BukkitRegionScheduler bukkitRegionScheduler = new BukkitRegionScheduler();
@Override
public @NonNull AsyncScheduler getAsyncScheduler() {
return bukkitAsyncScheduler;
}
@Override
public @NonNull GlobalRegionScheduler getGlobalRegionScheduler() {
return bukkitGlobalRegionScheduler;
}
@Override
public @NonNull EntityScheduler getEntityScheduler() {
return bukkitEntityScheduler;
}
@Override
public @NonNull RegionScheduler getRegionScheduler() {
return bukkitRegionScheduler;
}
}
@@ -15,13 +15,13 @@ public class BukkitRegionScheduler implements RegionScheduler {
private final BukkitScheduler bukkitScheduler = Bukkit.getScheduler();
@Override
public void execute(@NotNull GrimPlugin plugin, @NotNull PlatformWorld world, int chunkX, int chunkZ, @NotNull Runnable task) {
bukkitScheduler.runTask(GrimACBukkitLoaderPlugin.LOADER, task);
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 task) {
bukkitScheduler.runTask(GrimACBukkitLoaderPlugin.LOADER, task);
public void execute(@NotNull GrimPlugin plugin, @NotNull Location location, @NotNull Runnable run) {
bukkitScheduler.runTask(GrimACBukkitLoaderPlugin.LOADER, run);
}
@Override
@@ -2,32 +2,27 @@ package ac.grim.grimac.platform.bukkit.scheduler.bukkit;
import ac.grim.grimac.platform.api.scheduler.TaskHandle;
import org.bukkit.scheduler.BukkitTask;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import java.util.Objects;
public class BukkitTaskHandle implements TaskHandle {
private final @NotNull BukkitTask task;
private final BukkitTask bukkitTask;
@Contract(pure = true)
public BukkitTaskHandle(@NotNull BukkitTask task) {
this.task = Objects.requireNonNull(task);
public BukkitTaskHandle(BukkitTask bukkitTask) {
this.bukkitTask = bukkitTask;
}
@Override
public boolean isSync() {
return task.isSync();
return bukkitTask.isSync();
}
@Override
public boolean isCancelled() {
return task.isCancelled();
public boolean getCancelled() {
return bukkitTask.isCancelled();
}
@Override
public void cancel() {
task.cancel();
bukkitTask.cancel();
}
}
@@ -11,47 +11,53 @@ import java.util.concurrent.TimeUnit;
public class FoliaAsyncScheduler implements AsyncScheduler {
private final io.papermc.paper.threadedregions.scheduler.AsyncScheduler scheduler = Bukkit.getAsyncScheduler();
private final io.papermc.paper.threadedregions.scheduler.AsyncScheduler asyncScheduler = Bukkit.getAsyncScheduler();
@Override
public TaskHandle runNow(@NotNull GrimPlugin plugin, @NotNull Runnable task) {
return new FoliaTaskHandle(scheduler.runNow(GrimACBukkitLoaderPlugin.LOADER, ignored -> task.run()));
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(scheduler.runDelayed(
GrimACBukkitLoaderPlugin.LOADER,
ignored -> task.run(),
delay,
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(scheduler.runAtFixedRate(
GrimACBukkitLoaderPlugin.LOADER,
ignored -> task.run(),
delay,
period,
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(scheduler.runAtFixedRate(
GrimACBukkitLoaderPlugin.LOADER,
ignored -> task.run(),
initialDelayTicks * 50,
periodTicks * 50,
TimeUnit.MILLISECONDS
));
return new FoliaTaskHandle(
asyncScheduler.runAtFixedRate(
GrimACBukkitLoaderPlugin.LOADER,
(ignored) -> task.run(),
initialDelayTicks * 50,
periodTicks * 50,
TimeUnit.MILLISECONDS
)
);
}
@Override
public void cancel(@NotNull GrimPlugin plugin) {
scheduler.cancelTasks(GrimACBukkitLoaderPlugin.LOADER);
asyncScheduler.cancelTasks(GrimACBukkitLoaderPlugin.LOADER);
}
}
@@ -6,50 +6,38 @@ import ac.grim.grimac.platform.api.scheduler.EntityScheduler;
import ac.grim.grimac.platform.api.scheduler.TaskHandle;
import ac.grim.grimac.platform.bukkit.GrimACBukkitLoaderPlugin;
import ac.grim.grimac.platform.bukkit.entity.BukkitGrimEntity;
import io.papermc.paper.threadedregions.scheduler.ScheduledTask;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class FoliaEntityScheduler implements EntityScheduler {
@Override
public void execute(@NotNull GrimEntity entity, @NotNull GrimPlugin plugin, @NotNull Runnable task, @Nullable Runnable retired, long delay) {
((BukkitGrimEntity) entity).getBukkitEntity().getScheduler().execute(GrimACBukkitLoaderPlugin.LOADER, task, retired, delay);
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) {
ScheduledTask scheduled = ((BukkitGrimEntity) entity).getBukkitEntity().getScheduler().run(
GrimACBukkitLoaderPlugin.LOADER,
ignored -> task.run(),
retired
return new FoliaTaskHandle(
((BukkitGrimEntity) entity).getBukkitEntity().getScheduler().run(GrimACBukkitLoaderPlugin.LOADER, (ignored) -> task.run(), () -> {
})
);
return scheduled == null ? null : new FoliaTaskHandle(scheduled);
}
@Override
public TaskHandle runDelayed(@NotNull GrimEntity entity, @NotNull GrimPlugin plugin, @NotNull Runnable task, @Nullable Runnable retired, long delayTicks) {
ScheduledTask scheduled = ((BukkitGrimEntity) entity).getBukkitEntity().getScheduler().runDelayed(
GrimACBukkitLoaderPlugin.LOADER,
ignored -> task.run(),
retired,
delayTicks
return new FoliaTaskHandle(
((BukkitGrimEntity) entity).getBukkitEntity().getScheduler().runDelayed(GrimACBukkitLoaderPlugin.LOADER, (ignored) -> task.run(), () -> {
}, delayTicks)
);
return scheduled == null ? null : new FoliaTaskHandle(scheduled);
}
@Override
public TaskHandle runAtFixedRate(@NotNull GrimEntity entity, @NotNull GrimPlugin plugin, @NotNull Runnable task, @Nullable Runnable retired, long initialDelayTicks, long periodTicks) {
ScheduledTask scheduled = ((BukkitGrimEntity) entity).getBukkitEntity().getScheduler().runAtFixedRate(
GrimACBukkitLoaderPlugin.LOADER,
ignored -> task.run(),
retired,
initialDelayTicks,
periodTicks
return new FoliaTaskHandle(
((BukkitGrimEntity) entity).getBukkitEntity().getScheduler().runAtFixedRate(GrimACBukkitLoaderPlugin.LOADER, (ignored) -> task.run(), () -> {
}, initialDelayTicks, periodTicks)
);
return scheduled == null ? null : new FoliaTaskHandle(scheduled);
}
}
@@ -12,23 +12,23 @@ public class FoliaGlobalRegionScheduler implements GlobalRegionScheduler {
private final io.papermc.paper.threadedregions.scheduler.GlobalRegionScheduler globalRegionScheduler = Bukkit.getGlobalRegionScheduler();
@Override
public void execute(@NotNull GrimPlugin plugin, @NotNull Runnable task) {
globalRegionScheduler.execute(GrimACBukkitLoaderPlugin.LOADER, task);
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()));
return new FoliaTaskHandle(globalRegionScheduler.run(GrimACBukkitLoaderPlugin.LOADER, (ignored) -> task.run()));
}
@Override
public TaskHandle runDelayed(@NotNull GrimPlugin plugin, @NotNull Runnable task, long delay) {
return new FoliaTaskHandle(globalRegionScheduler.runDelayed(GrimACBukkitLoaderPlugin.LOADER, ignored -> task.run(), delay));
return new FoliaTaskHandle(globalRegionScheduler.runDelayed(GrimACBukkitLoaderPlugin.LOADER, (ignored) -> task.run(), delay));
}
@Override
public TaskHandle runAtFixedRate(@NotNull GrimPlugin plugin, @NotNull Runnable task, long initialDelayTicks, long periodTicks) {
return new FoliaTaskHandle(globalRegionScheduler.runAtFixedRate(GrimACBukkitLoaderPlugin.LOADER, ignored -> task.run(), initialDelayTicks, periodTicks));
return new FoliaTaskHandle(globalRegionScheduler.runAtFixedRate(GrimACBukkitLoaderPlugin.LOADER, (ignored) -> task.run(), initialDelayTicks, periodTicks));
}
@Override
@@ -1,13 +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 lombok.Getter;
import org.jetbrains.annotations.NotNull;
import ac.grim.grimac.platform.api.scheduler.RegionScheduler;
import org.checkerframework.checker.nullness.qual.NonNull;
@Getter
public class FoliaPlatformScheduler implements PlatformScheduler {
private final @NotNull FoliaAsyncScheduler asyncScheduler = new FoliaAsyncScheduler();
private final @NotNull FoliaGlobalRegionScheduler globalRegionScheduler = new FoliaGlobalRegionScheduler();
private final @NotNull FoliaEntityScheduler entityScheduler = new FoliaEntityScheduler();
private final @NotNull FoliaRegionScheduler regionScheduler = new FoliaRegionScheduler();
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;
}
}
@@ -15,24 +15,19 @@ 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 task) {
regionScheduler.execute(GrimACBukkitLoaderPlugin.LOADER, ((BukkitPlatformWorld) world).getBukkitWorld(), chunkX, chunkZ, task);
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 task) {
execute(plugin, location.getWorld(), location.getBlockX() >> 4, location.getBlockZ() >> 4, task);
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()
));
return new FoliaTaskHandle(regionScheduler.run(GrimACBukkitLoaderPlugin.LOADER, ((BukkitPlatformWorld) world).getBukkitWorld(), chunkX, chunkZ, (ignored) -> task.run()));
}
@Override
@@ -42,14 +37,8 @@ public class FoliaRegionScheduler implements RegionScheduler {
@Override
public TaskHandle runDelayed(@NotNull GrimPlugin plugin, @NotNull PlatformWorld world, int chunkX, int chunkZ, @NotNull Runnable task, long delayTicks) {
return new FoliaTaskHandle(regionScheduler.runDelayed(
GrimACBukkitLoaderPlugin.LOADER,
((BukkitPlatformWorld) world).getBukkitWorld(),
chunkX,
chunkZ,
ignored -> task.run(),
delayTicks
));
return new FoliaTaskHandle(regionScheduler.runDelayed(GrimACBukkitLoaderPlugin.LOADER,
((BukkitPlatformWorld) world).getBukkitWorld(), chunkX, chunkZ, (ignored) -> task.run(), delayTicks));
}
@Override
@@ -59,27 +48,12 @@ public class FoliaRegionScheduler implements RegionScheduler {
@Override
public TaskHandle runAtFixedRate(@NotNull GrimPlugin plugin, @NotNull PlatformWorld world, int chunkX, int chunkZ, @NotNull Runnable task, long initialDelayTicks, long periodTicks) {
return new FoliaTaskHandle(regionScheduler.runAtFixedRate(
GrimACBukkitLoaderPlugin.LOADER,
((BukkitPlatformWorld) world).getBukkitWorld(),
chunkX,
chunkZ,
ignored -> task.run(),
initialDelayTicks,
periodTicks
));
return new FoliaTaskHandle(regionScheduler.runAtFixedRate(GrimACBukkitLoaderPlugin.LOADER,
((BukkitPlatformWorld) world).getBukkitWorld(), chunkX, chunkZ, (ignored) -> task.run(), initialDelayTicks, periodTicks));
}
@Override
public TaskHandle runAtFixedRate(@NotNull GrimPlugin plugin, @NotNull Location location, @NotNull Runnable task, long initialDelayTicks, long periodTicks) {
return runAtFixedRate(
plugin,
location.getWorld(),
location.getBlockX() >> 4,
location.getBlockZ() >> 4,
task,
initialDelayTicks,
periodTicks
);
return runAtFixedRate(plugin, location.getWorld(), location.getBlockX() >> 4, location.getBlockZ() >> 4, task, initialDelayTicks, periodTicks);
}
}
@@ -2,18 +2,13 @@ package ac.grim.grimac.platform.bukkit.scheduler.folia;
import ac.grim.grimac.platform.api.scheduler.TaskHandle;
import io.papermc.paper.threadedregions.scheduler.ScheduledTask;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import java.util.Objects;
public class FoliaTaskHandle implements TaskHandle {
private final @NotNull ScheduledTask task;
private final ScheduledTask task;
@Contract(pure = true)
public FoliaTaskHandle(@NotNull ScheduledTask task) {
this.task = Objects.requireNonNull(task);
public FoliaTaskHandle(ScheduledTask task) {
this.task = task;
}
@Override
@@ -22,7 +17,7 @@ public class FoliaTaskHandle implements TaskHandle {
}
@Override
public boolean isCancelled() {
public boolean getCancelled() {
return task.isCancelled();
}
@@ -12,8 +12,8 @@ import org.bukkit.command.RemoteConsoleCommandSender;
import org.bukkit.entity.Player;
import org.bukkit.permissions.Permission;
import org.bukkit.permissions.PermissionDefault;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.incendo.cloud.SenderMapper;
import org.jetbrains.annotations.NotNull;
import java.util.UUID;
@@ -31,7 +31,7 @@ public class BukkitSenderFactory extends SenderFactory<CommandSender> implements
@Override
protected UUID getUniqueId(CommandSender sender) {
return sender instanceof Player player ? player.getUniqueId() : Sender.CONSOLE_UUID;
return sender instanceof Player ? ((Player) sender).getUniqueId() : Sender.CONSOLE_UUID;
}
@Override
@@ -78,12 +78,12 @@ public class BukkitSenderFactory extends SenderFactory<CommandSender> implements
}
@Override
public @NotNull Sender map(@NotNull CommandSender base) {
public @NonNull Sender map(@NonNull CommandSender base) {
return this.wrap(base);
}
@Override
public @NotNull CommandSender reverse(@NotNull Sender mapped) {
public @NonNull CommandSender reverse(@NonNull Sender mapped) {
return this.unwrap(mapped);
}
}
@@ -1,7 +1,5 @@
package ac.grim.grimac.platform.bukkit.utils.anticheat;
import ac.grim.grimac.utils.anticheat.LogUtil;
import ac.grim.grimac.utils.reflection.ReflectionUtils;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import org.bukkit.entity.Player;
@@ -10,17 +8,27 @@ import java.lang.reflect.Method;
public class MultiLibUtil {
public final static Method externalPlayerMethod = ReflectionUtils.getMethod(Player.class, "isExternalPlayer");
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 {
return clazz.getMethod(methodName);
} catch (NoSuchMethodException e) {
return null;
}
}
// TODO: cache external players for better performance, but this only matters for people using multi-lib
public static boolean isExternalPlayer(Player player) {
if (externalPlayerMethod == null || IS_PRE_1_18) return false;
try {
return (boolean) externalPlayerMethod.invoke(player);
} catch (Exception e) {
LogUtil.error("Failed to invoke external player method", e);
e.printStackTrace();
return false;
}
}
}
@@ -4,14 +4,9 @@ import ac.grim.grimac.platform.api.permissions.PermissionDefaultValue;
import ac.grim.grimac.platform.bukkit.world.BukkitPlatformWorld;
import ac.grim.grimac.utils.math.Location;
import com.github.retrooper.packetevents.protocol.world.BlockFace;
import org.bukkit.permissions.PermissionDefault;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.Nullable;
public class BukkitConversionUtils {
@Contract("null -> null; !null -> new")
public static org.bukkit.Location toBukkitLocation(Location location) {
if (location == null) return null;
return new org.bukkit.Location(((BukkitPlatformWorld) location.getWorld()).getBukkitWorld(), location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch());
}
@@ -20,17 +15,20 @@ public class BukkitConversionUtils {
*
* @return The corresponding Bukkit PermissionDefault.
*/
@Contract(value = "null -> null; !null -> !null", pure = true)
public static @Nullable PermissionDefault toBukkitPermissionDefault(@Nullable PermissionDefaultValue permissionDefaultValue) {
if (permissionDefaultValue == null) return null;
public static org.bukkit.permissions.PermissionDefault toBukkitPermissionDefault(PermissionDefaultValue permissionDefaultValue) {
return switch (permissionDefaultValue) {
case TRUE -> PermissionDefault.TRUE;
case FALSE -> PermissionDefault.FALSE;
case OP -> PermissionDefault.OP;
case NOT_OP -> PermissionDefault.NOT_OP;
case TRUE -> org.bukkit.permissions.PermissionDefault.TRUE;
case FALSE -> org.bukkit.permissions.PermissionDefault.FALSE;
case OP -> org.bukkit.permissions.PermissionDefault.OP;
case NOT_OP -> org.bukkit.permissions.PermissionDefault.NOT_OP;
};
}
/**
* 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;
@@ -8,7 +8,6 @@ import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -37,16 +36,9 @@ public class PlaceholderAPIExpansion extends PlaceholderExpansion {
@Override
public @NotNull List<String> getPlaceholders() {
Set<String> staticReplacements = GrimAPI.INSTANCE.getExternalAPI().getStaticReplacements().keySet();
Set<String> variableReplacements = GrimAPI.INSTANCE.getExternalAPI().getVariableReplacements().keySet();
ArrayList<String> placeholders = new ArrayList<>(staticReplacements.size() + variableReplacements.size());
for (String s : staticReplacements) {
placeholders.add(s.equals("%grim_version%") ? s : "%grim_" + s.replaceAll("%", "") + "%");
}
for (String s : variableReplacements) {
placeholders.add(s.equals("%player%") ? "%grim_player%" : "%grim_player_" + s.replaceAll("%", "") + "%");
}
return placeholders;
Set<String> placeholders = GrimAPI.INSTANCE.getExternalAPI().getStaticReplacements().keySet();
placeholders.addAll(GrimAPI.INSTANCE.getExternalAPI().getVariableReplacements().keySet());
return List.copyOf(placeholders);
}
@Override
@@ -12,29 +12,41 @@ import org.bukkit.event.Listener;
import java.util.concurrent.CompletableFuture;
public class PaperUtils {
public static final boolean PAPER = ReflectionUtils.hasClass("com.destroystokyo.paper.PaperConfig")
|| ReflectionUtils.hasClass("io.papermc.paper.configuration.Configuration");
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) {
return PAPER ? entity.teleportAsync(location) : CompletableFuture.completedFuture(entity.teleport(location));
if (PAPER) {
return entity.teleportAsync(location);
} else {
return CompletableFuture.completedFuture(entity.teleport(location));
}
}
@SuppressWarnings("unchecked")
public static boolean registerTickEndEvent(Listener listener, Runnable runnable) {
try {
Class<?> clazz = ReflectionUtils.getClass("com.destroystokyo.paper.event.server.ServerTickEndEvent");
if (clazz == null) return false;
GrimACBukkitLoaderPlugin.LOADER.getServer().getPluginManager().registerEvent(
(Class<? extends Event>) clazz,
listener,
EventPriority.NORMAL,
(l, event) -> runnable.run(),
GrimACBukkitLoaderPlugin.LOADER
);
return true;
} catch (Exception e) {
LogUtil.error("Failed to register tick end event", e);
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;
}
}
@@ -1,26 +1,24 @@
package ac.grim.grimac.platform.bukkit.world;
import ac.grim.grimac.GrimAPI;
import ac.grim.grimac.platform.api.Platform;
import ac.grim.grimac.platform.api.world.PlatformChunk;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState;
import lombok.RequiredArgsConstructor;
import org.bukkit.Chunk;
import org.bukkit.block.Block;
import org.bukkit.block.data.BlockData;
import org.jetbrains.annotations.NotNull;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@RequiredArgsConstructor
public class BukkitPlatformChunk implements PlatformChunk {
private static final Map<BlockData, Integer> blockDataToId = GrimAPI.INSTANCE.getPlatform() == Platform.FOLIA ? new ConcurrentHashMap<>() : new HashMap<>();
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 @NotNull Chunk chunk;
private final Chunk chunk;
public BukkitPlatformChunk(@NotNull Chunk chunkAt) {
this.chunk = chunkAt;
}
@Override
public int getBlockID(int x, int y, int z) {
@@ -28,11 +26,6 @@ public class BukkitPlatformChunk implements PlatformChunk {
return isFlat // Cache blockDataToID because Strings are expensive
? blockDataToId.computeIfAbsent(block.getBlockData(), data -> WrappedBlockState.getByString(PacketEvents.getAPI().getServerManager().getVersion().toClientVersion(), data.getAsString(false)).getGlobalId())
: getLegacyBlockID(block);
}
@SuppressWarnings({ "deprecation", "UnstableApiUsage" })
private static int getLegacyBlockID(@NotNull Block block) {
return (block.getType().getId() << 4) | block.getData();
: (block.getType().getId() << 4) | block.getData();
}
}
@@ -2,26 +2,24 @@ package ac.grim.grimac.platform.bukkit.world;
import ac.grim.grimac.platform.api.world.PlatformChunk;
import ac.grim.grimac.platform.api.world.PlatformWorld;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState;
import io.github.retrooper.packetevents.util.SpigotConversionUtil;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.UUID;
@Getter
@RequiredArgsConstructor
public class BukkitPlatformWorld implements PlatformWorld {
private static final boolean LEGACY_SERVER_VERSION = PacketEvents.getAPI().getServerManager().getVersion().isOlderThanOrEquals(ServerVersion.V_1_12_2);
private final @NotNull World bukkitWorld;
private final World bukkitWorld;
public BukkitPlatformWorld(@NotNull World world) {
this.bukkitWorld = world;
}
@Override
public boolean isChunkLoaded(int chunkX, int chunkZ) {
@@ -30,14 +28,7 @@ public class BukkitPlatformWorld implements PlatformWorld {
@Override
public WrappedBlockState getBlockAt(int x, int y, int z) {
if (LEGACY_SERVER_VERSION) {
Block block = bukkitWorld.getBlockAt(x, y, z);
@SuppressWarnings({"deprecation", "UnstableApiUsage"})
int blockId = (block.getType().getId() << 4) | block.getData();
return WrappedBlockState.getByGlobalId(blockId);
} else {
return SpigotConversionUtil.fromBukkitBlockData(bukkitWorld.getBlockAt(x, y, z).getBlockData());
}
return SpigotConversionUtil.fromBukkitBlockData(bukkitWorld.getBlockAt(x, y, z).getBlockData());
}
@Override
-47
View File
@@ -1,47 +0,0 @@
# cliff.toml - DEV BUILDS - Now with Author Info
[changelog]
body = """
## Changes in this Build (since previous push)
{%- for group, commits in commits | group_by(attribute="group") %}
### {{ group | striptags | upper_first }}
{%- for commit in commits %}
- {{ commit.message | upper_first }} - *by {{ commit.author.name }}* {% if commit.breaking %}**[BREAKING]**{% endif %} \
{%- if commit.remote and commit.remote.url -%}
([`{{ commit.id | truncate(length=7, end="") }}`]({{ commit.remote.url }}/commit/{{- commit.id -}}))
{%- else -%}
(`{{ commit.id | truncate(length=7, end="") }}`)
{%- endif -%}
{% endfor %}
{% endfor %}
"""
trim = true
[git]
# Your previous settings here are perfect, no changes needed
filter_unconventional = false
skip_unconventional = false
remote = "https://github.com/GrimAnticheat/Grim"
commit_parsers = [
{ message = "^feat", group = "🚀 Features" },
{ message = "^fix", group = "🐛 Fixes" },
{ message = "^perf", group = "⚡️ Performance" },
{ message = "^refactor", group = "♻️ Refactoring" },
{ message = "^docs", group = "📖 Documentation" },
{ message = "^test", group = "✅ Tests" },
{ message = "^chore", group = "🤖 Maintenance & CI" },
{ body = ".*BREAKING CHANGE.*", group = "🚨 Breaking Changes" },
{ message = ".*", group = "Miscellaneous" } # CHANGED THIS LINE
]
commit_preprocessors = [
{ pattern = 'src/main/java/ac/grim/grimac/checks/.*', group = "✔️ Checks" },
{ pattern = 'src/main/java/ac/grim/grimac/api/.*', group = "🔌 API" },
{ pattern = 'src/main/java/ac/grim/grimac/platform/.*', group = "🏗️ Platform" },
{ pattern = 'src/main/java/ac/grim/grimac/engine/.*', group = "⚙️ Core Engine" },
{ pattern = 'src/main/java/ac/grim/grimac/config/.*', group = "⚙️ Configuration" },
{ pattern = 'pom.xml', group = "🤖 Maintenance & CI" },
{ pattern = '.github/.*', group = "🤖 Maintenance & CI" },
{ pattern = 'build.gradle.kts', group = "🤖 Maintenance & CI" },
{ pattern = 'src/.*', group = "⚙️ Core Engine" },
]
sort_commits = "oldest"
+26 -79
View File
@@ -1,104 +1,51 @@
import versioning.BuildConfig
plugins {
`maven-publish`
grim.`base-conventions`
}
repositories {
if (BuildConfig.mavenLocalOverride) {
mavenLocal()
mavenLocal()
maven {
name = "papermc"
url = uri("https://repo.papermc.io/repository/maven-public/")
}
exclusiveContent {
forRepository {
maven("https://repo.grim.ac/snapshots") // Grim API
}
filter {
includeGroup("ac.grim.grimac")
includeGroup("com.github.retrooper")
maven("https://hub.spigotmc.org/nexus/content/repositories/snapshots/") // Spigot
maven("https://jitpack.io/") { // Grim API
content {
includeGroup("com.github.grimanticheat")
}
}
exclusiveContent {
forRepository {
maven("https://repo.viaversion.com") { // ViaVersion
mavenContent { releasesOnly() }
}
}
filter {
includeGroup("com.viaversion")
}
}
exclusiveContent {
forRepository {
maven("https://nexus.scarsz.me/content/repositories/releases") { // Configuralize
mavenContent { releasesOnly() }
}
}
filter {
includeGroup("github.scarsz")
}
}
exclusiveContent {
forRepository {
maven("https://repo.opencollab.dev/maven-releases/") { // Cumulus (for Floodgate)
mavenContent { releasesOnly() }
}
}
filter {
includeGroup("org.geysermc.api")
}
}
exclusiveContent {
forRepository {
maven("https://repo.opencollab.dev/maven-snapshots/") { // Floodgate
mavenContent { snapshotsOnly() }
}
}
filter {
includeGroup("org.geysermc.floodgate")
includeGroup("org.geysermc.cumulus")
includeModule("org.geysermc", "common")
includeModule("org.geysermc", "geyser-parent")
}
}
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 {
if (BuildConfig.shadePE) {
api(libs.packetevents.api)
} else {
compileOnly(libs.packetevents.api)
}
api(libs.packetevents.api)
api(libs.cloud.core)
api(libs.cloud.processors.requirements)
api(libs.configuralize) {
artifact {
classifier = "slim"
}
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(libs.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)
api(libs.hikaricp)
api(libs.grim.api)
compileOnly(libs.geyser.base.api) {
isTransitive = false // messes with guava otherwise
}
// Used for local testing:
// api("ac.grim.grimac:GrimAPI:1.0")
api("com.github.grimanticheat:grimapi:9d6a0e6b7f")
compileOnly(libs.floodgate.api)
compileOnly(libs.viaversion)
compileOnly(libs.via.version.api)
compileOnly(libs.netty)
}
@@ -1,9 +1,8 @@
package ac.grim.grimac;
import ac.grim.grimac.api.event.EventBus;
import ac.grim.grimac.api.event.OptimizedEventBus;
import ac.grim.grimac.api.plugin.GrimPlugin;
import ac.grim.grimac.events.GrimExtensionManager;
import ac.grim.grimac.events.OptimizedEventBus;
import ac.grim.grimac.manager.AlertManagerImpl;
import ac.grim.grimac.manager.DiscordManager;
import ac.grim.grimac.manager.InitManager;
@@ -11,13 +10,12 @@ import ac.grim.grimac.manager.SpectateManager;
import ac.grim.grimac.manager.TickManager;
import ac.grim.grimac.manager.config.BaseConfigManager;
import ac.grim.grimac.manager.init.Initable;
import ac.grim.grimac.manager.violationdatabase.ViolationDatabaseManager;
import ac.grim.grimac.platform.api.Platform;
import ac.grim.grimac.platform.api.PlatformLoader;
import ac.grim.grimac.platform.api.PlatformServer;
import ac.grim.grimac.platform.api.manager.ItemResetHandler;
import ac.grim.grimac.platform.api.manager.MessagePlaceHolderManager;
import ac.grim.grimac.platform.api.manager.CommandAdapter;
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;
@@ -25,18 +23,19 @@ import ac.grim.grimac.platform.api.scheduler.PlatformScheduler;
import ac.grim.grimac.platform.api.sender.Sender;
import ac.grim.grimac.platform.api.sender.SenderFactory;
import ac.grim.grimac.utils.anticheat.PlayerDataManager;
import ac.grim.grimac.utils.common.arguments.CommonGrimArguments;
import ac.grim.grimac.utils.reflection.ReflectionUtils;
import lombok.Getter;
import org.incendo.cloud.CommandManager;
import org.jetbrains.annotations.NotNull;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@Getter
public final class GrimAPI {
public static final GrimAPI INSTANCE = new GrimAPI();
@Getter
private final Platform platform = detectPlatform();
private final BaseConfigManager configManager;
private final AlertManagerImpl alertManager;
@@ -44,12 +43,9 @@ public final class GrimAPI {
private final DiscordManager discordManager;
private final PlayerDataManager playerDataManager;
private final TickManager tickManager;
private final GrimExtensionManager extensionManager;
private final EventBus eventBus;
private final GrimExternalAPI externalAPI;
private ViolationDatabaseManager violationDatabaseManager;
private PlatformLoader loader;
@Getter
private InitManager initManager;
private boolean initialized = false;
@@ -60,24 +56,26 @@ public final class GrimAPI {
this.discordManager = new DiscordManager();
this.playerDataManager = new PlayerDataManager();
this.tickManager = new TickManager();
this.extensionManager = new GrimExtensionManager();
this.eventBus = new OptimizedEventBus(extensionManager);
this.eventBus = new OptimizedEventBus();
this.externalAPI = new GrimExternalAPI(this);
}
// the order matters
private static Platform detectPlatform() {
Platform override = CommonGrimArguments.PLATFORM_OVERRIDE.value();
if (override != null) return override;
if (ReflectionUtils.hasClass("io.papermc.paper.threadedregions.RegionizedServer")) return Platform.FOLIA;
if (ReflectionUtils.hasClass("org.bukkit.Bukkit")) return Platform.BUKKIT;
if (ReflectionUtils.hasClass("net.fabricmc.loader.api.FabricLoader")) return Platform.FABRIC;
throw new IllegalStateException("Unknown platform!");
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.violationDatabaseManager = new ViolationDatabaseManager(getGrimPlugin());
this.initManager = new InitManager(loader.getPacketEvents(), loader::getCommandManager, platformSpecificInitables);
this.initManager.load();
this.initialized = true;
@@ -101,8 +99,12 @@ public final class GrimAPI {
return loader.getPlatformPlayerFactory();
}
public CommandAdapter getCommandAdapter() {
return loader.getCommandAdapter();
public ParserDescriptorFactory getParserDescriptors() {
return loader.getParserDescriptorFactory();
}
public InitManager getInitManager() {
return initManager;
}
public GrimPlugin getGrimPlugin() {
@@ -143,7 +145,7 @@ public final class GrimAPI {
return loader.getPermissionManager();
}
public GrimExtensionManager getExtensionManager() {
return extensionManager;
public Platform getPlatform() {
return platform;
}
}
@@ -1,6 +1,7 @@
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;
@@ -10,10 +11,11 @@ import ac.grim.grimac.manager.config.ConfigManagerFileImpl;
import ac.grim.grimac.manager.init.start.StartableInitable;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.LogUtil;
import ac.grim.grimac.utils.anticheat.MessageUtil;
import ac.grim.grimac.utils.chat.ChatUtil;
import ac.grim.grimac.utils.common.ConfigReloadObserver;
import com.github.retrooper.packetevents.netty.channel.ChannelHelper;
import lombok.Getter;
import org.jetbrains.annotations.NotNull;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.jetbrains.annotations.Nullable;
import java.util.Map;
@@ -41,7 +43,7 @@ public class GrimExternalAPI implements GrimAbstractAPI, ConfigReloadObserver, S
}
@Override
public @NotNull EventBus getEventBus() {
public @NonNull EventBus getEventBus() {
return api.getEventBus();
}
@@ -50,6 +52,16 @@ public class GrimExternalAPI implements GrimAbstractAPI, ConfigReloadObserver, S
return api.getPlayerDataManager().getPlayer(uuid);
}
public String replaceVariables(GrimUser user, String content) {
for (Map.Entry<String, String> entry : staticReplacements.entrySet()) {
content = content.replace(entry.getKey(), entry.getValue());
}
for (Map.Entry<String, Function<GrimUser, String>> entry : variableReplacements.entrySet()) {
content = content.replace(entry.getKey(), entry.getValue().apply(user));
}
return content;
}
@Override
public void registerVariable(String string, Function<GrimUser, String> replacement) {
if (replacement == null) {
@@ -70,7 +82,8 @@ public class GrimExternalAPI implements GrimAbstractAPI, ConfigReloadObserver, S
@Override
public String getGrimVersion() {
return api.getGrimPlugin().getDescription().getVersion();
GrimPluginDescription description = GrimAPI.INSTANCE.getGrimPlugin().getDescription();
return description.getVersion();
}
@Override
@@ -120,7 +133,7 @@ public class GrimExternalAPI implements GrimAbstractAPI, ConfigReloadObserver, S
try {
GrimAPI.INSTANCE.getConfigManager().start();
} catch (Exception e) {
LogUtil.error("Failed to start config manager.", e);
e.printStackTrace();
}
}
@@ -156,7 +169,7 @@ public class GrimExternalAPI implements GrimAbstractAPI, ConfigReloadObserver, S
() -> GrimAPI.INSTANCE.getEventBus().post(new GrimReloadEvent(true)));
return true;
} catch (Exception e) {
LogUtil.error("Failed to reload config", e);
e.printStackTrace();
}
if (started)
GrimAPI.INSTANCE.getScheduler().getAsyncScheduler().runNow(GrimAPI.INSTANCE.getGrimPlugin(),
@@ -175,15 +188,16 @@ public class GrimExternalAPI implements GrimAbstractAPI, ConfigReloadObserver, S
// Update variables
updateVariables();
// Restart
GrimAPI.INSTANCE.getAlertManager().reload(configManager);
GrimAPI.INSTANCE.getDiscordManager().reload();
GrimAPI.INSTANCE.getSpectateManager().reload();
GrimAPI.INSTANCE.getViolationDatabaseManager().reload();
// Don't reload players if the plugin hasn't started yet
if (!started) return;
// Reload checks for all players
for (GrimPlayer player : GrimAPI.INSTANCE.getPlayerDataManager().getEntries()) {
player.runSafely(() -> player.reload(configManager));
for (GrimPlayer grimPlayer : GrimAPI.INSTANCE.getPlayerDataManager().getEntries()) {
ChannelHelper.runInEventLoop(grimPlayer.user.getChannel(), () -> {
grimPlayer.updatePermissions();
grimPlayer.reload(configManager);
});
}
}
@@ -198,7 +212,7 @@ public class GrimExternalAPI implements GrimAbstractAPI, ConfigReloadObserver, S
variableReplacements.putIfAbsent("%tps%", user -> String.format("%.2f", GrimAPI.INSTANCE.getPlatformServer().getTPS()));
variableReplacements.putIfAbsent("%version%", GrimUser::getVersionName);
// static variables
staticReplacements.put("%prefix%", MessageUtil.translateAlternateColorCodes('&', GrimAPI.INSTANCE.getConfigManager().getPrefix()));
staticReplacements.put("%prefix%", ChatUtil.translateAlternateColorCodes('&', GrimAPI.INSTANCE.getConfigManager().getPrefix()));
staticReplacements.putIfAbsent("%grim_version%", getGrimVersion());
}
}
@@ -8,18 +8,17 @@ import ac.grim.grimac.player.GrimPlayer;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.protocol.packettype.PacketTypeCommon;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerFlying;
import com.google.common.base.Preconditions;
import lombok.Getter;
import lombok.Setter;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.jetbrains.annotations.NotNull;
import java.util.Objects;
import static com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerFlying.isFlying;
// Class from https://github.com/Tecnio/AntiCheatBase/blob/master/src/main/java/me/tecnio/anticheat/check/Check.java
@Getter
public class Check extends GrimProcessor implements AbstractCheck {
protected @NotNull final GrimPlayer player;
protected @NonNull final GrimPlayer player;
public double violations;
private double decay;
@@ -40,7 +39,8 @@ public class Check extends GrimProcessor implements AbstractCheck {
private long lastViolationTime;
public Check(final @NotNull GrimPlayer player) {
this.player = Objects.requireNonNull(player);
Preconditions.checkArgument(player != null);
this.player = player;
final CheckData checkData = this.getClass().getAnnotation(CheckData.class);
if (checkData != null) {
@@ -63,12 +63,19 @@ public class Check extends GrimProcessor implements AbstractCheck {
return isEnabled && !player.disableGrim && !player.noModifyPacketPermission && !exemptPermission;
}
public final void updatePermissions() {
if (configName == null || player.platformPlayer == null) return;
final String id = configName.toLowerCase();
exemptPermission = player.platformPlayer.hasPermission("grim.exempt." + id);
noSetbackPermission = player.platformPlayer.hasPermission("grim.nosetback." + id);
noModifyPacketPermission = player.platformPlayer.hasPermission("grim.nomodifypacket." + id);
public void updatePermissions() {
if (player.platformPlayer == null || configName == null) return;
GrimAPI.INSTANCE.getScheduler().getEntityScheduler().run(
player.platformPlayer,
GrimAPI.INSTANCE.getGrimPlugin(),
() -> {
final String id = configName.toLowerCase();
exemptPermission = player.platformPlayer.hasPermission("grim.exempt." + id);
noSetbackPermission = player.platformPlayer.hasPermission("grim.nosetback." + id);
noModifyPacketPermission = player.platformPlayer.hasPermission("grim.nomodifypacket." + id);
},
() -> {}
);
}
public final boolean flagAndAlert(String verbose) {
@@ -102,11 +109,7 @@ public class Check extends GrimProcessor implements AbstractCheck {
}
public final boolean flagWithSetback() {
return flagWithSetback("");
}
public final boolean flagWithSetback(String verbose) {
if (flag(verbose)) {
if (flag()) {
setbackIfAboveSetbackVL();
return true;
}
@@ -130,13 +133,14 @@ public class Check extends GrimProcessor implements AbstractCheck {
}
@Override
public final void reload(ConfigManager configuration) {
public void reload(ConfigManager configuration) {
decay = configuration.getDoubleElse(configName + ".decay", decay);
setbackVL = configuration.getDoubleElse(configName + ".setbackvl", setbackVL);
displayName = configuration.getStringElse(configName + ".displayname", checkName);
description = configuration.getStringElse(configName + ".description", description);
if (setbackVL == -1) setbackVL = Double.MAX_VALUE;
updatePermissions();
onReload(configuration);
}
@@ -164,13 +168,13 @@ public class Check extends GrimProcessor implements AbstractCheck {
return offset > 0.001 ? String.format("%.5f", offset) : String.format("%.2E", offset);
}
public static boolean isTransaction(PacketTypeCommon packetType) {
public boolean isTransaction(PacketTypeCommon packetType) {
return packetType == PacketType.Play.Client.PONG ||
packetType == PacketType.Play.Client.WINDOW_CONFIRMATION;
}
public static boolean isAsync(PacketTypeCommon packetType) {
return packetType == PacketType.Play.Client.KEEP_ALIVE;
public boolean isFlying(PacketTypeCommon packetType) {
return WrapperPlayClientPlayerFlying.isFlying(packetType);
}
public boolean isUpdate(PacketTypeCommon packetType) {
@@ -1,14 +0,0 @@
package ac.grim.grimac.checks.debug;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.player.GrimPlayer;
public abstract class AbstractDebugHandler extends Check {
public AbstractDebugHandler(GrimPlayer player) {
super(player);
}
public abstract void toggleListener(GrimPlayer player);
public abstract boolean toggleConsoleOutput();
}
@@ -8,7 +8,7 @@ import ac.grim.grimac.utils.anticheat.update.RotationUpdate;
@CheckData(name = "AimDuplicateLook")
public class AimDuplicateLook extends Check implements RotationCheck {
private boolean exempt;
boolean exempt = false;
public AimDuplicateLook(GrimPlayer playerData) {
super(playerData);
@@ -28,7 +28,7 @@ public class AimModulo360 extends Check implements RotationCheck {
return;
}
if (player.yaw < 360 && player.yaw > -360 && Math.abs(rotationUpdate.getDeltaXRot()) > 320 && Math.abs(lastDeltaYaw) < 30) {
if (player.xRot < 360 && player.xRot > -360 && Math.abs(rotationUpdate.getDeltaXRot()) > 320 && Math.abs(lastDeltaYaw) < 30) {
flagAndAlert();
} else {
reward();
@@ -8,6 +8,7 @@ import ac.grim.grimac.utils.data.Pair;
import ac.grim.grimac.utils.lists.RunningMode;
import ac.grim.grimac.utils.math.GrimMath;
public class AimProcessor extends Check implements RotationCheck {
private static final int SIGNIFICANT_SAMPLES_THRESHOLD = 15;
@@ -18,10 +19,10 @@ public class AimProcessor extends Check implements RotationCheck {
public double divisorY;
public double modeX, modeY;
public double deltaDotsX, deltaDotsY;
private final RunningMode xRotMode = new RunningMode(TOTAL_SAMPLES_THRESHOLD);
private final RunningMode yRotMode = new RunningMode(TOTAL_SAMPLES_THRESHOLD);
private float lastXRot;
private float lastYRot;
RunningMode xRotMode = new RunningMode(TOTAL_SAMPLES_THRESHOLD);
RunningMode yRotMode = new RunningMode(TOTAL_SAMPLES_THRESHOLD);
float lastXRot;
float lastYRot;
public AimProcessor(GrimPlayer playerData) {
super(playerData);
@@ -37,6 +38,7 @@ public class AimProcessor extends Check implements RotationCheck {
public void process(final RotationUpdate rotationUpdate) {
rotationUpdate.setProcessor(this);
float deltaXRot = rotationUpdate.getDeltaXRotABS();
this.divisorX = GrimMath.gcd(deltaXRot, lastXRot);
@@ -45,6 +47,7 @@ public class AimProcessor extends Check implements RotationCheck {
this.lastXRot = deltaXRot;
}
float deltaYRot = rotationUpdate.getDeltaYRotABS();
this.divisorY = GrimMath.gcd(deltaYRot, lastYRot);
@@ -10,7 +10,7 @@ import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientHe
@CheckData(name = "BadPacketsA", description = "Sent duplicate slot id")
public class BadPacketsA extends Check implements PacketCheck {
private int lastSlot = -1;
int lastSlot = -1;
public BadPacketsA(final GrimPlayer player) {
super(player);
@@ -1,32 +0,0 @@
package ac.grim.grimac.checks.impl.badpackets;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PacketCheck;
import ac.grim.grimac.player.GrimPlayer;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
@CheckData(name = "BadPacketsB", description = "Ignored set rotation packet")
public class BadPacketsB extends Check implements PacketCheck {
public BadPacketsB(final GrimPlayer player) {
super(player);
}
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (isTransaction(event.getPacketType())) {
player.pendingRotations.removeIf(data -> {
if (player.getLastTransactionReceived() > data.getTransaction()) {
if (!data.isAccepted()) {
flagAndAlert();
}
return true;
}
return false;
});
}
}
}
@@ -22,13 +22,15 @@ public class BadPacketsD extends Check implements PacketCheck {
final float pitch = new WrapperPlayClientPlayerFlying(event).getLocation().getPitch();
if (pitch > 90 || pitch < -90) {
// Ban.
if (flagAndAlert("pitch=" + pitch) && shouldModifyPackets()) {
// prevent other checks from using an invalid pitch
if (player.pitch > 90) player.pitch = 90;
if (player.pitch < -90) player.pitch = -90;
if (flagAndAlert("pitch=" + pitch)) {
if (shouldModifyPackets()) {
// prevent other checks from using an invalid pitch
if (player.yRot > 90) player.yRot = 90;
if (player.yRot < -90) player.yRot = -90;
event.setCancelled(true);
player.onPacketCancel();
event.setCancelled(true);
player.onPacketCancel();
}
}
}
}
@@ -14,8 +14,6 @@ import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPl
@CheckData(name = "BadPacketsE")
public class BadPacketsE extends Check implements PacketCheck {
private int noReminderTicks;
private final int maxNoReminderTicks = player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8) ? 20 : 19;
private final boolean isViaPleaseStopUsingProtocolHacksOnYourServer = player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_21_2) || PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_21_2);
public BadPacketsE(GrimPlayer player) {
super(player);
@@ -23,17 +21,20 @@ public class BadPacketsE extends Check implements PacketCheck {
@Override
public void onPacketReceive(PacketReceiveEvent event) {
final boolean isViaPleaseStopUsingProtocolHacksOnYourServer = player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_21_2) || PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_21_2);
if (event.getPacketType() == PacketType.Play.Client.PLAYER_POSITION_AND_ROTATION ||
event.getPacketType() == PacketType.Play.Client.PLAYER_POSITION) {
noReminderTicks = 0;
} else if (WrapperPlayClientPlayerFlying.isFlying(event.getPacketType()) && !player.packetStateData.lastPacketWasTeleport) {
if (++noReminderTicks > maxNoReminderTicks) {
flagAndAlert("ticks=" + noReminderTicks);
}
} else if (WrapperPlayClientPlayerFlying.isFlying(event.getPacketType())) {
noReminderTicks++;
} else if (event.getPacketType() == PacketType.Play.Client.STEER_VEHICLE
|| (isViaPleaseStopUsingProtocolHacksOnYourServer && player.inVehicle())) {
noReminderTicks = 0; // Exempt vehicles
}
if (noReminderTicks > 20) {
flagAndAlert("ticks=" + noReminderTicks); // ban? I don't know how this would false
}
}
public void handleRespawn() {
@@ -1,21 +1,23 @@
package ac.grim.grimac.checks.impl.badpackets;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.BlockPlaceCheck;
import ac.grim.grimac.checks.type.PacketCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.BlockBreak;
import ac.grim.grimac.utils.anticheat.update.BlockPlace;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientUseItem;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientInteractEntity;
@CheckData(name = "BadPacketsH", description = "Sent unexpected sequence id", experimental = true)
public class BadPacketsH extends BlockPlaceCheck {
private int lastSequence;
private final boolean isSupportedVersion = player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_19) && PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_19);
@CheckData(name = "BadPacketsH", description = "Did not swing for attack")
public class BadPacketsH extends Check implements PacketCheck {
// 1.9 packet order: INTERACT -> ANIMATION
// 1.8 packet order: ANIMATION -> INTERACT
// I personally think 1.8 made much more sense. You swing and THEN you hit!
private boolean sentAnimation = player.getClientVersion().isNewerThan(ClientVersion.V_1_8);
public BadPacketsH(final GrimPlayer player) {
super(player);
@@ -23,45 +25,31 @@ public class BadPacketsH extends BlockPlaceCheck {
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (event.getPacketType() == PacketType.Play.Client.USE_ITEM
&& shouldCancel(new WrapperPlayClientUseItem(event).getSequence())) {
event.setCancelled(true);
player.onPacketCancel();
}
}
if (event.getPacketType() == PacketType.Play.Client.ANIMATION) {
sentAnimation = true;
} else if (event.getPacketType() == PacketType.Play.Client.INTERACT_ENTITY) {
WrapperPlayClientInteractEntity packet = new WrapperPlayClientInteractEntity(event);
if (packet.getAction() != WrapperPlayClientInteractEntity.InteractAction.ATTACK) return;
@Override
public void onBlockPlace(BlockPlace place) {
if (shouldCancel(place.sequence) && shouldCancel()) {
place.resync();
}
}
// There is a "bug" in ViaRewind
// 1.8 packet order: ANIMATION -> INTERACT
// 1.9 packet order: INTERACT -> ANIMATION
// ViaRewind, on 1.9+ servers, delays a 1.8 client's ANIMATION to be after INTERACT (but before flying).
// Which means we see 1.9 packet order for 1.8 clients
// Due to ViaRewind also delaying the swings, we then see packet order above 20CPS like:
// INTERACT -> INTERACT -> ANIMATION -> ANIMATION
// 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;
@Override
public void onBlockBreak(BlockBreak blockBreak) {
switch (blockBreak.action) {
case START_DIGGING, FINISHED_DIGGING -> {
if (shouldCancel(blockBreak.sequence)) {
blockBreak.cancel();
}
}
case CANCELLED_DIGGING -> { // other actions will be checked by BadPacketsL
if (blockBreak.sequence != 0 && flagAndAlert("expected=0, id=" + blockBreak.sequence) && shouldModifyPackets()) {
blockBreak.cancel();
}
if (!sentAnimation && flagAndAlert() && shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
}
sentAnimation = false;
}
}
public boolean shouldCancel(int sequence) {
int expected = lastSequence + 1;
lastSequence = sequence;
return isSupportedVersion && sequence != expected
&& flagAndAlert("expected=" + expected + ", id=" + sequence)
&& shouldModifyPackets();
}
public void onWorldChange() {
lastSequence = 0;
}
}
@@ -16,11 +16,13 @@ public class BadPacketsI extends Check implements PacketCheck {
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (event.getPacketType() == PacketType.Play.Client.PLAYER_ABILITIES
&& new WrapperPlayClientPlayerAbilities(event).isFlying() && !player.canFly
&& flagAndAlert() && shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
if (event.getPacketType() == PacketType.Play.Client.PLAYER_ABILITIES) {
if (new WrapperPlayClientPlayerAbilities(event).isFlying() && !player.canFly) {
if (flagAndAlert() && shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
}
}
}
}
}
@@ -1,59 +0,0 @@
package ac.grim.grimac.checks.impl.badpackets;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PacketCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.data.HeadRotation;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.player.GameMode;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientUseItem;
import java.util.ArrayList;
import java.util.List;
@CheckData(name = "BadPacketsJ", description = "Rotation in use item packet did not match tick rotation")
public class BadPacketsJ extends Check implements PacketCheck {
private final List<HeadRotation> rotations = new ArrayList<>();
public BadPacketsJ(GrimPlayer player) {
super(player);
}
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (player.gamemode == GameMode.SPECTATOR) {
rotations.clear();
return;
}
if (event.getPacketType() == PacketType.Play.Client.USE_ITEM && player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_21)
&& PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_21)) {
WrapperPlayClientUseItem packet = new WrapperPlayClientUseItem(event);
rotations.add(new HeadRotation(packet.getYaw(), packet.getPitch()));
}
if (isTickPacket(event.getPacketType())) {
// due to tick skipping, the rotations sent could be last tick's
boolean allowLast = player.canSkipTicks() && (event.getPacketType() == PacketType.Play.Client.PLAYER_POSITION_AND_ROTATION || event.getPacketType() == PacketType.Play.Client.PLAYER_ROTATION);
for (HeadRotation rotation : rotations) {
if (rotation.yaw() == player.yaw && rotation.pitch() == player.pitch) {
allowLast = false;
continue;
}
if (rotation.yaw() == player.lastYaw && rotation.pitch() == player.lastPitch && allowLast) {
continue;
}
flagAndAlert();
}
rotations.clear();
}
}
}
@@ -16,11 +16,13 @@ public class BadPacketsK extends Check implements PacketCheck {
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (event.getPacketType() == PacketType.Play.Client.SPECTATE
&& player.gamemode != GameMode.SPECTATOR
&& flagAndAlert() && shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
if (event.getPacketType() == PacketType.Play.Client.SPECTATE) {
if (player.gamemode != GameMode.SPECTATOR) {
if (flagAndAlert() && shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
}
}
}
}
}
@@ -4,60 +4,57 @@ import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PacketCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.data.packetentity.PacketEntity;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.event.PacketSendEvent;
import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.player.Combat;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientClientStatus;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerChangeGameState;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerCombatEvent;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerDeathCombatEvent;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientInteractEntity;
@CheckData(name = "BadPacketsM", description = "Tried to respawn while alive", experimental = true)
@CheckData(name = "BadPacketsM")
public class BadPacketsM extends Check implements PacketCheck {
// 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);
}
// not a boolean because the server could send packets that cause
// the client to send a respawn packet before it receives the first
private int exempt;
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (event.getPacketType() == PacketType.Play.Client.CLIENT_STATUS && new WrapperPlayClientClientStatus(event).getAction() == WrapperPlayClientClientStatus.Action.PERFORM_RESPAWN) {
if (exempt > 0) {
exempt--;
return;
}
if (event.getPacketType() == PacketType.Play.Client.INTERACT_ENTITY && !exempt) {
if (!player.compensatedEntities.self.isDead) {
if (flagAndAlert() && shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
}
}
}
}
final WrapperPlayClientInteractEntity wrapper = new WrapperPlayClientInteractEntity(event);
@Override
public void onPacketSend(PacketSendEvent event) {
if (event.getPacketType() == PacketType.Play.Server.CHANGE_GAME_STATE && player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_9)
&& new WrapperPlayServerChangeGameState(event).getReason() == WrapperPlayServerChangeGameState.Reason.WIN_GAME) {
player.addRealTimeTaskNow(() -> exempt++);
}
final PacketEntity entity = player.compensatedEntities.entityMap.get(wrapper.getEntityId());
if (event.getPacketType() == PacketType.Play.Server.DEATH_COMBAT_EVENT && player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_9)) {
if (new WrapperPlayServerDeathCombatEvent(event).getPlayerId() == player.entityID) {
player.addRealTimeTaskNow(() -> exempt++);
}
}
// For armor stands, vanilla clients send:
// - when renaming the armor stand or in spectator mode: INTERACT_AT + INTERACT
// - in all other cases: only INTERACT
// Just exempt armor stands to be safe
if (entity != null && entity.getType() == EntityTypes.ARMOR_STAND) return;
if (event.getPacketType() == PacketType.Play.Server.COMBAT_EVENT && player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_9)) {
WrapperPlayServerCombatEvent packet = new WrapperPlayServerCombatEvent(event);
if (packet.getCombat() == Combat.ENTITY_DEAD && packet.getPlayerId() == player.entityID) {
player.addRealTimeTaskNow(() -> exempt++);
switch (wrapper.getAction()) {
// INTERACT_AT then INTERACT
case INTERACT:
if (!sentInteractAt) {
if (flagAndAlert("Missed Interact-At") && shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
}
}
sentInteractAt = false;
break;
case INTERACT_AT:
if (sentInteractAt) {
if (flagAndAlert("Missed Interact") && shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
}
}
sentInteractAt = true;
break;
}
}
}
@@ -2,10 +2,11 @@ package ac.grim.grimac.checks.impl.badpackets;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PacketCheck;
import ac.grim.grimac.player.GrimPlayer;
@CheckData(name = "BadPacketsN", setback = 0)
public class BadPacketsN extends Check {
public class BadPacketsN extends Check implements PacketCheck {
public BadPacketsN(final GrimPlayer player) {
super(player);
}
@@ -4,6 +4,7 @@ import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PacketCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.data.Pair;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.event.PacketSendEvent;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
@@ -11,10 +12,11 @@ import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientKe
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerKeepAlive;
import java.util.LinkedList;
import java.util.Queue;
@CheckData(name = "BadPacketsO")
public class BadPacketsO extends Check implements PacketCheck {
private final LinkedList<Long> keepalives = new LinkedList<>();
Queue<Pair<Long, Long>> keepaliveMap = new LinkedList<>();
public BadPacketsO(GrimPlayer player) {
super(player);
@@ -23,30 +25,37 @@ public class BadPacketsO extends Check implements PacketCheck {
@Override
public void onPacketSend(PacketSendEvent event) {
if (event.getPacketType() == PacketType.Play.Server.KEEP_ALIVE) {
keepalives.add(new WrapperPlayServerKeepAlive(event).getId());
WrapperPlayServerKeepAlive packet = new WrapperPlayServerKeepAlive(event);
keepaliveMap.add(new Pair<>(packet.getId(), System.nanoTime()));
}
}
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (event.getPacketType() == PacketType.Play.Client.KEEP_ALIVE) {
final long id = new WrapperPlayClientKeepAlive(event).getId();
WrapperPlayClientKeepAlive packet = new WrapperPlayClientKeepAlive(event);
for (long keepalive : keepalives) {
if (keepalive == id) {
// Found the ID, remove stuff until we get to it (to stop very slow memory leaks)
Long data;
do {
data = keepalives.poll();
} while (data != null && data != id);
long id = packet.getId();
boolean hasID = false;
return;
for (Pair<Long, Long> iterator : keepaliveMap) {
if (iterator.first() == id) {
hasID = true;
break;
}
}
if (flagAndAlert("id=" + id) && shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
if (!hasID) {
if (flagAndAlert("id=" + id) && shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
}
} else { // Found the ID, remove stuff until we get to it (to stop very slow memory leaks)
Pair<Long, Long> data;
do {
data = keepaliveMap.poll();
if (data == null) break;
} while (data.first() != id);
}
}
}
@@ -11,7 +11,7 @@ import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientCl
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientClickWindow.WindowClickType;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerOpenWindow;
@CheckData(name = "BadPacketsP", description = "Invalid click packets", experimental = true)
@CheckData(name = "BadPacketsP", experimental = true)
public class BadPacketsP extends Check implements PacketCheck {
private int containerType = -1;
@@ -30,7 +30,6 @@ public class BadPacketsR extends Check implements PacketCheck {
} else {
reward();
}
player.compensatedEntities.entitiesRemovedThisTick.clear();
player.compensatedWorld.removeInvalidPistonLikeStuff(oldTransId);
positions = 0;
clock = player.getPlayerClockAtLeast();
@@ -4,9 +4,6 @@ import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PacketCheck;
import ac.grim.grimac.player.GrimPlayer;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientWindowConfirmation;
@CheckData(name = "BadPacketsS")
public class BadPacketsS extends Check implements PacketCheck {
@@ -14,13 +11,4 @@ public class BadPacketsS extends Check implements PacketCheck {
super(player);
}
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (event.getPacketType() == PacketType.Play.Client.WINDOW_CONFIRMATION
&& !new WrapperPlayClientWindowConfirmation(event).isAccepted()
&& flagAndAlert() && shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
}
}
}
@@ -14,20 +14,16 @@ import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientIn
@CheckData(name = "BadPacketsT")
public class BadPacketsT extends Check implements PacketCheck {
private final double maxHorizontalDisplacement;
private final double minVerticalDisplacement;
private final double maxVerticalDisplacement;
// 1.7 and 1.8 seem to have different hitbox "expansion" values than 1.9+
// https://github.com/GrimAnticheat/Grim/pull/1274#issuecomment-1872458702
// https://github.com/GrimAnticheat/Grim/pull/1274#issuecomment-1872533497
private final boolean hasLegacyExpansion = player.getClientVersion().isOlderThan(ClientVersion.V_1_9);
private final double maxHorizontalDisplacement = 0.3001 + (hasLegacyExpansion ? 0.1 : 0);
private final double minVerticalDisplacement = -0.0001 - (hasLegacyExpansion ? 0.1 : 0);
private final double maxVerticalDisplacement = 1.8001 + (hasLegacyExpansion ? 0.1 : 0);
public BadPacketsT(final GrimPlayer player) {
super(player);
// 1.7 and 1.8 seem to have different hitbox "expansion" values than 1.9+
// https://github.com/GrimAnticheat/Grim/pull/1274#issuecomment-1872458702
// https://github.com/GrimAnticheat/Grim/pull/1274#issuecomment-1872533497
double expansion = player.getClientVersion().isOlderThan(ClientVersion.V_1_9) ? 0.1 : 0;
maxHorizontalDisplacement = 0.3001 + expansion;
minVerticalDisplacement = -0.0001 - expansion;
maxVerticalDisplacement = 1.8001 + expansion;
}
@Override
@@ -43,7 +39,7 @@ public class BadPacketsT extends Check implements PacketCheck {
}
// Make sure our target entity is actually a player (Player NPCs work too)
if (!EntityTypes.PLAYER.equals(packetEntity.type)) {
if (!EntityTypes.PLAYER.equals(packetEntity.getType())) {
// We can't check for any entity that is not a player
return;
}
@@ -2,10 +2,11 @@ package ac.grim.grimac.checks.impl.badpackets;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PacketCheck;
import ac.grim.grimac.player.GrimPlayer;
@CheckData(name = "BadPacketsW", description = "Interacted with non-existent entity", experimental = true)
public class BadPacketsW extends Check {
public class BadPacketsW extends Check implements PacketCheck {
public BadPacketsW(GrimPlayer player) {
super(player);
}
@@ -1,32 +0,0 @@
package ac.grim.grimac.checks.impl.badpackets;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PacketCheck;
import ac.grim.grimac.player.GrimPlayer;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
@CheckData(name = "BadPacketsZ", experimental = true)
public class BadPacketsZ extends Check implements PacketCheck {
private boolean sent;
public BadPacketsZ(GrimPlayer player) {
super(player);
}
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (event.getPacketType() == PacketType.Play.Client.CLIENT_TICK_END) {
sent = false;
}
if (event.getPacketType() == PacketType.Play.Client.PLAYER_INPUT) {
if (sent) {
flagAndAlert();
}
sent = true;
}
}
}
@@ -9,7 +9,7 @@ import ac.grim.grimac.utils.anticheat.update.RotationUpdate;
import ac.grim.grimac.utils.data.HeadRotation;
import ac.grim.grimac.utils.math.GrimMath;
// This check has been patched by Baritone for a long time, and it also seems to false with cinematic camera now, so it is disabled.
// This check has been patched by Baritone for a long time and it also seems to false with cinematic camera now, so it is disabled.
@CheckData(name = "Baritone")
public class Baritone extends Check implements RotationCheck {
private int verbose;
@@ -23,10 +23,10 @@ public class Baritone extends Check implements RotationCheck {
final HeadRotation from = rotationUpdate.getFrom();
final HeadRotation to = rotationUpdate.getTo();
final float deltaPitch = Math.abs(to.pitch() - from.pitch());
final float deltaPitch = Math.abs(to.getPitch() - from.getPitch());
// Baritone works with small degrees, limit to 1 degree to pick up on baritone slightly moving aim to bypass anticheats
if (rotationUpdate.getDeltaXRot() == 0 && deltaPitch > 0 && deltaPitch < 1 && Math.abs(to.pitch()) != 90.0f) {
// Baritone works with small degrees, limit to 1 degrees to pick up on baritone slightly moving aim to bypass anticheats
if (rotationUpdate.getDeltaXRot() == 0 && deltaPitch > 0 && deltaPitch < 1 && Math.abs(to.getPitch()) != 90.0f) {
if (rotationUpdate.getProcessor().divisorY < GrimMath.MINIMUM_DIVISOR) {
verbose++;
if (verbose > 8) {
@@ -12,7 +12,7 @@ import com.github.retrooper.packetevents.protocol.player.DiggingAction;
import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
import com.github.retrooper.packetevents.util.Vector3i;
import org.jetbrains.annotations.NotNull;
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 {
@@ -20,8 +20,8 @@ public class AirLiquidBreak extends Check implements BlockBreakCheck {
private int lastTick;
private boolean didLastFlag;
// Initialize to non-null values to prevent NPE when checking for blockType properties and if position equals old position
private int lastX, lastY, lastZ;
private float lastHardness, lastBlastResistance;
private @NonNull Vector3i lastBreakLoc = new Vector3i();
private @NonNull StateType lastBlockType = StateTypes.AIR;
public AirLiquidBreak(GrimPlayer player) {
super(player);
@@ -40,23 +40,18 @@ public class AirLiquidBreak extends Check implements BlockBreakCheck {
// I am explicitly making this patch as narrow and specific as possible to potentially discover other blocks that exhibit similar behaviour
int newTick = GrimAPI.INSTANCE.getTickManager().currentTick;
if (lastTick == newTick
&& lastX == blockBreak.x
&& lastY == blockBreak.y
&& lastZ == blockBreak.z
&& lastBreakLoc.equals(blockBreak.position)
&& !didLastFlag
&& lastHardness == 0.0F
&& lastBlastResistance == 0.0F
&& lastBlockType.getHardness() == 0.0F
&& lastBlockType.getBlastResistance() == 0.0F
&& block == StateTypes.WATER
) return;
lastTick = newTick;
lastX = blockBreak.x;
lastY = blockBreak.y;
lastZ = blockBreak.z;
lastHardness = block.getHardness();
lastBlastResistance = block.getBlastResistance();
lastBreakLoc = blockBreak.position;
lastBlockType = block;
// the block does not have a hitbox
boolean invalid = (block == StateTypes.LIGHT && !(player.inventory.getHeldItem().is(ItemTypes.LIGHT) || player.inventory.getOffHand().is(ItemTypes.LIGHT)))
boolean invalid = (block == StateTypes.LIGHT && !(player.getInventory().getHeldItem().is(ItemTypes.LIGHT) || player.getInventory().getOffHand().is(ItemTypes.LIGHT)))
|| block.isAir()
|| block == StateTypes.WATER
|| block == StateTypes.LAVA
@@ -25,9 +25,10 @@ public class FarBreak extends Check implements BlockBreakCheck {
double min = Double.MAX_VALUE;
for (double d : player.getPossibleEyeHeights()) {
SimpleCollisionBox box = new SimpleCollisionBox(blockBreak.x, blockBreak.y, blockBreak.z);
Vector3dm best = VectorUtils.cutBoxToVector(player.x, player.y + d, player.z, box);
min = Math.min(min, best.distanceSquared(player.x, player.y + d, player.z));
SimpleCollisionBox box = new SimpleCollisionBox(blockBreak.position);
Vector3dm eyes = new Vector3dm(player.x, player.y + d, player.z);
Vector3dm best = VectorUtils.cutBoxToVector(eyes, box);
min = Math.min(min, eyes.distanceSquared(best));
}
// getPickRange() determines this?
@@ -36,11 +36,8 @@ public class FastBreak extends Check implements BlockBreakCheck {
super(playerData);
}
// The block the player is currently breaking, represented by primitives for performance.
// `targetBlockIsSet` tracks nullness of the position, since primitives can't be null
private boolean targetBlockIsSet;
private int targetBlockX, targetBlockY, targetBlockZ;
// The block the player is currently breaking
Vector3i targetBlockPosition = null;
// The maximum amount of damage the player deals to the block
//
double maximumBlockDamage = 0;
@@ -56,7 +53,7 @@ public class FastBreak extends Check implements BlockBreakCheck {
@Override
public void onBlockBreak(BlockBreak blockBreak) {
if (blockBreak.action == DiggingAction.START_DIGGING) {
if (!ViaVersionUtil.isAvailable) {
if (!ViaVersionUtil.isAvailable()) {
// Exempt all blocks that do not exist in the player version
final WrappedBlockState defaultState = WrappedBlockState.getDefaultState(player.getClientVersion(), blockBreak.block.getType());
if (defaultState.getType() == StateTypes.AIR || EXEMPT_STATES.contains(defaultState.getType())) {
@@ -65,16 +62,10 @@ public class FastBreak extends Check implements BlockBreakCheck {
}
// If client is older than the server, fetch block client actually sees from via
// otherwise just return the server-side block (since if client is >= server version the block is guaranteed to exist in client version)
// TODO this lazy loads PacketEvents mappings for older versions for clients on versions older than the servers, increasing memory usage
// * its the only thing we use non-native mappings for behind ViaVersion
// * can we translate back "up" to server version and run check against server version to avoid loading older registries?
WrappedBlockState block = clientOlderThanServer ? WrappedBlockState.getByGlobalId(player.getClientVersion(), player.getViaTranslatedClientBlockID(blockBreak.block.getGlobalId())) : blockBreak.block;
WrappedBlockState block = clientOlderThanServer ? WrappedBlockState.getByGlobalId(player.getClientVersion(), player.getViaTranslatedBlockID(blockBreak.block.getGlobalId())) : blockBreak.block;
startBreak = System.currentTimeMillis() - (!targetBlockIsSet ? 50 : 0); // ???
targetBlockIsSet = true;
targetBlockX = blockBreak.x;
targetBlockY = blockBreak.y;
targetBlockZ = blockBreak.z;
startBreak = System.currentTimeMillis() - (targetBlockPosition == null ? 50 : 0); // ???
targetBlockPosition = blockBreak.position;
maximumBlockDamage = BlockBreakSpeed.getBlockDamage(player, block);
@@ -93,7 +84,9 @@ public class FastBreak extends Check implements BlockBreakCheck {
}
clampBalance();
} else if (blockBreak.action == DiggingAction.FINISHED_DIGGING && targetBlockIsSet) {
}
if (blockBreak.action == DiggingAction.FINISHED_DIGGING && targetBlockPosition != null) {
double predictedTime = Math.ceil(1 / maximumBlockDamage) * 50;
double realTime = System.currentTimeMillis() - startBreak;
double diff = predictedTime - realTime;
@@ -121,8 +114,8 @@ public class FastBreak extends Check implements BlockBreakCheck {
public void onPacketReceive(PacketReceiveEvent event) {
// Find the most optimal block damage using the animation packet, which is sent at least once a tick when breaking blocks
// On 1.8 clients, via screws with this packet meaning we must fall back to the 1.8 idle flying packet
if ((player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_9) ? event.getPacketType() == PacketType.Play.Client.ANIMATION : WrapperPlayClientPlayerFlying.isFlying(event.getPacketType())) && targetBlockIsSet) {
maximumBlockDamage = Math.max(maximumBlockDamage, BlockBreakSpeed.getBlockDamage(player, player.compensatedWorld.getBlock(targetBlockX, targetBlockY, targetBlockZ)));
if ((player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_9) ? event.getPacketType() == PacketType.Play.Client.ANIMATION : WrapperPlayClientPlayerFlying.isFlying(event.getPacketType())) && targetBlockPosition != null) {
maximumBlockDamage = Math.max(maximumBlockDamage, BlockBreakSpeed.getBlockDamage(player, player.compensatedWorld.getBlock(targetBlockPosition)));
}
}
@@ -21,9 +21,7 @@ public class MultiBreak extends Check implements BlockBreakCheck {
private final List<String> flags = new ArrayList<>();
private boolean hasBroken;
private BlockFace lastFace;
private int lastX;
private int lastY;
private int lastZ;
private Vector3i lastPos;
public MultiBreak(GrimPlayer player) {
super(player);
@@ -35,22 +33,10 @@ public class MultiBreak extends Check implements BlockBreakCheck {
return;
}
final int currentX = blockBreak.x;
final int currentY = blockBreak.y;
final int currentZ = blockBreak.z;
if (hasBroken && (blockBreak.face != lastFace ||
currentX != lastX ||
currentY != lastY ||
currentZ != lastZ)) {
final String currentPosString = MessageUtil.toUnlabeledString(currentX, currentY, currentZ);
final String lastPosString = MessageUtil.toUnlabeledString(lastX, lastY, lastZ);
if (hasBroken && (blockBreak.face != lastFace || !blockBreak.position.equals(lastPos))) {
final String verbose = "face=" + blockBreak.face + ", lastFace=" + lastFace
+ ", pos=" + currentPosString
+ ", lastPos=" + lastPosString;
+ ", pos=" + MessageUtil.toUnlabledString(blockBreak.position)
+ ", lastPos=" + MessageUtil.toUnlabledString(lastPos);
if (!player.canSkipTicks()) {
if (flagAndAlert(verbose) && shouldModifyPackets()) {
blockBreak.cancel();
@@ -61,9 +47,7 @@ public class MultiBreak extends Check implements BlockBreakCheck {
}
lastFace = blockBreak.face;
lastX = currentX;
lastY = currentY;
lastZ = currentZ;
lastPos = blockBreak.position;
hasBroken = true;
}
@@ -9,7 +9,7 @@ import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.player.DiggingAction;
import com.github.retrooper.packetevents.protocol.world.BlockFace;
@CheckData(name = "PositionBreakB")
@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;
@@ -7,7 +7,6 @@ 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.data.TeleportData;
import ac.grim.grimac.utils.math.Vector3dm;
import ac.grim.grimac.utils.nmsutil.Ray;
import ac.grim.grimac.utils.nmsutil.ReachUtils;
@@ -15,7 +14,6 @@ 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.teleport.RelativeFlag;
import com.github.retrooper.packetevents.protocol.world.BlockFace;
import com.github.retrooper.packetevents.util.Vector3d;
import com.github.retrooper.packetevents.util.Vector3f;
@@ -72,7 +70,7 @@ public class RotationBreak extends Check implements BlockBreakCheck {
}
private boolean didRayTraceHit(BlockBreak blockBreak) {
SimpleCollisionBox box = new SimpleCollisionBox(blockBreak.x, blockBreak.y, blockBreak.z);
SimpleCollisionBox box = new SimpleCollisionBox(blockBreak.position);
final double[] possibleEyeHeights = player.getPossibleEyeHeights();
@@ -94,18 +92,18 @@ public class RotationBreak extends Check implements BlockBreakCheck {
// End checking if the player is in the block
List<Vector3f> possibleLookDirs = new ArrayList<>(Arrays.asList(
new Vector3f(player.lastYaw, player.pitch, 0),
new Vector3f(player.yaw, player.pitch, 0)
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.lastYaw, player.lastPitch, 0));
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.yaw, player.pitch, 0));
possibleLookDirs = Collections.singletonList(new Vector3f(player.xRot, player.yRot, 0));
}
final double distance = player.compensatedEntities.self.getAttributeValue(Attributes.BLOCK_INTERACTION_RANGE);
@@ -113,8 +111,7 @@ public class RotationBreak extends Check implements BlockBreakCheck {
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());
Vector3dm pointAtDistance = trace.getPointAtDistance(distance);
Pair<Vector3dm, BlockFace> intercept = ReachUtils.calculateIntercept(box, trace.getOriginX(), trace.getOriginY(), trace.getOriginZ(), pointAtDistance.getX(), pointAtDistance.getY(), pointAtDistance.getZ());
Pair<Vector3dm, BlockFace> intercept = ReachUtils.calculateIntercept(box, trace.getOrigin(), trace.getPointAtDistance(distance));
if (intercept.first() != null) return true;
}
@@ -19,14 +19,7 @@ import static ac.grim.grimac.utils.nmsutil.BlockBreakSpeed.getBlockDamage;
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 boolean lastBlockIsSet;
private int lastBlockX, lastBlockY, lastBlockZ;
private boolean lastCancelledBlockIsSet;
private int lastCancelledBlockX, lastCancelledBlockY, lastCancelledBlockZ;
private boolean lastLastBlockIsSet;
private Vector3i lastBlock, lastCancelledBlock, lastLastBlock = null;
public WrongBreak(final GrimPlayer player) {
super(player);
@@ -34,7 +27,8 @@ public class WrongBreak extends Check implements BlockBreakCheck {
// The client sometimes sends a wierd cancel packet
private boolean shouldExempt(final WrappedBlockState block, int yPos) {
if (lastLastBlockIsSet || !lastBlockIsSet)
// lastLastBlock is always null when this happens, and lastBlock isn't
if (lastLastBlock != null || lastBlock == null)
return false;
// on pre 1.14.4 clients, the YPos of this packet is always the same
@@ -47,29 +41,22 @@ public class WrongBreak extends Check implements BlockBreakCheck {
@Override
public void onBlockBreak(BlockBreak blockBreak) {
final int x = blockBreak.x;
final int y = blockBreak.y;
final int z = blockBreak.z;
if (blockBreak.action == DiggingAction.START_DIGGING) {
final Vector3i pos = blockBreak.position;
lastBlockWasInstantBreak = getBlockDamage(player, blockBreak.block) >= 1;
lastCancelledBlockIsSet = false;
lastCancelledBlock = null;
lastLastBlock = lastBlock;
lastBlock = pos;
}
lastLastBlockIsSet = lastBlockIsSet;
if (blockBreak.action == DiggingAction.CANCELLED_DIGGING) {
final Vector3i pos = blockBreak.position;
lastBlockIsSet = true;
lastBlockX = x;
lastBlockY = y;
lastBlockZ = z;
} else if (blockBreak.action == DiggingAction.CANCELLED_DIGGING) {
boolean notLastBlock = !lastBlockIsSet || x != lastBlockX || y != lastBlockY || z != lastBlockZ;
if (!shouldExempt(blockBreak.block, y) && notLastBlock) {
if (!shouldExempt(blockBreak.block, pos.y) && !pos.equals(lastBlock)) {
// https://github.com/GrimAnticheat/Grim/issues/1512
boolean isLastCancelled = lastCancelledBlockIsSet && x == lastCancelledBlockX && y == lastCancelledBlockY && z == lastCancelledBlockZ;
if (player.getClientVersion().isOlderThan(ClientVersion.V_1_14_4) || (!lastBlockWasInstantBreak && isLastCancelled)) {
String lastBlockStr = lastBlockIsSet ? MessageUtil.toUnlabeledString(lastBlockX, lastBlockY, lastBlockZ) : "null";
if (flagAndAlert("action=CANCELLED_DIGGING" + ", last=" + lastBlockStr + ", pos=" + MessageUtil.toUnlabeledString(x, y, z))) {
if (player.getClientVersion().isOlderThan(ClientVersion.V_1_14_4) || (!lastBlockWasInstantBreak && pos.equals(lastCancelledBlock))) {
if (flagAndAlert("action=CANCELLED_DIGGING" + ", last=" + MessageUtil.toUnlabledString(lastBlock) + ", pos=" + MessageUtil.toUnlabledString(pos))) {
if (shouldModifyPackets()) {
blockBreak.cancel();
}
@@ -77,20 +64,18 @@ public class WrongBreak extends Check implements BlockBreakCheck {
}
}
lastCancelledBlockIsSet = true;
lastCancelledBlockX = x;
lastCancelledBlockY = y;
lastCancelledBlockZ = z;
lastCancelledBlock = pos;
lastLastBlock = null;
lastBlock = null;
return;
}
lastLastBlockIsSet = lastBlockIsSet = false;
} else if (blockBreak.action == DiggingAction.FINISHED_DIGGING) {
boolean notLastCancelled = !lastCancelledBlockIsSet || x != lastCancelledBlockX || y != lastCancelledBlockY || z != lastCancelledBlockZ;
boolean notLastBlock = !lastBlockIsSet || x != lastBlockX || y != lastBlockY || z != lastBlockZ;
if (blockBreak.action == DiggingAction.FINISHED_DIGGING) {
final Vector3i pos = blockBreak.position;
// when a player looks away from the mined block, they send a cancel, and if they look at it again, they don't send another start. (thanks mojang!)
if (notLastCancelled && (!lastBlockWasInstantBreak || player.getClientVersion().isOlderThan(ClientVersion.V_1_14_4)) && notLastBlock) {
String lastBlockStr = lastBlockIsSet ? MessageUtil.toUnlabeledString(lastBlockX, lastBlockY, lastBlockZ) : "null";
if (flagAndAlert("action=FINISHED_DIGGING" + ", last=" + lastBlockStr + ", pos=" + MessageUtil.toUnlabeledString(x, y, z))) {
if (!pos.equals(lastCancelledBlock) && (!lastBlockWasInstantBreak || player.getClientVersion().isOlderThan(ClientVersion.V_1_14_4)) && !pos.equals(lastBlock)) {
if (flagAndAlert("action=FINISHED_DIGGING" + ", last=" + MessageUtil.toUnlabledString(lastBlock) + ", pos=" + MessageUtil.toUnlabledString(pos))) {
if (shouldModifyPackets()) {
blockBreak.cancel();
}
@@ -99,7 +84,9 @@ public class WrongBreak extends Check implements BlockBreakCheck {
// 1.14.4+ clients don't send another start break in protected regions
if (player.getClientVersion().isOlderThan(ClientVersion.V_1_14_4)) {
lastCancelledBlockIsSet = lastLastBlockIsSet = lastBlockIsSet = false;
lastCancelledBlock = null;
lastLastBlock = null;
lastBlock = null;
}
}
}
@@ -1,33 +0,0 @@
package ac.grim.grimac.checks.impl.chat;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PacketCheck;
import ac.grim.grimac.player.GrimPlayer;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientTabComplete;
@CheckData(name = "ChatA", experimental = true)
public class ChatA extends Check implements PacketCheck {
public ChatA(GrimPlayer player) {
super(player);
}
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (event.getPacketType() == PacketType.Play.Client.TAB_COMPLETE && PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_13)) {
WrapperPlayClientTabComplete wrapper = new WrapperPlayClientTabComplete(event);
String text = wrapper.getText();
if (text.equals("/") || text.trim().isEmpty()) {
if (flagAndAlert("")) {
event.setCancelled(true);
player.onPacketCancel();
}
}
}
}
}
@@ -1,62 +0,0 @@
package ac.grim.grimac.checks.impl.chat;
import ac.grim.grimac.api.config.ConfigManager;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.impl.multiactions.MultiActionsC;
import ac.grim.grimac.checks.type.PacketCheck;
import ac.grim.grimac.player.GrimPlayer;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientChatCommand;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientChatCommandUnsigned;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientChatMessage;
import org.jetbrains.annotations.Nullable;
import java.util.function.Predicate;
import java.util.regex.Pattern;
@CheckData(name = "ChatC", description = "Moving while chatting", experimental = true)
public class ChatC extends Check implements PacketCheck {
public ChatC(GrimPlayer player) {
super(player);
}
// optionally allow cheats like autogg
private @Nullable Predicate<String> exemptRegex;
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (event.getPacketType() == PacketType.Play.Client.CHAT_MESSAGE) {
// TODO make previa after making wrapper parse by client version instead of server version
check(new WrapperPlayClientChatMessage(event).getMessage(), event);
}
if (event.getPacketType() == PacketType.Play.Client.CHAT_COMMAND_UNSIGNED) {
check("/" + new WrapperPlayClientChatCommandUnsigned(event).getCommand(), event);
}
if (event.getPacketType() == PacketType.Play.Client.CHAT_COMMAND) {
// TODO make previa after making wrapper parse by client version instead of server version
check("/" + new WrapperPlayClientChatCommand(event).getCommand(), event);
}
}
private void check(String message, PacketReceiveEvent event) {
if (exemptRegex != null && exemptRegex.test(message)) {
return;
}
String verbose = MultiActionsC.getVerbose(player);
if (!verbose.isEmpty() && flagAndAlert(verbose) && shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
}
}
@Override
public void onReload(ConfigManager config) {
String regexString = config.getStringElse(getConfigName() + ".exempt-regex", null);
exemptRegex = regexString == null ? null : Pattern.compile(regexString).asMatchPredicate();
}
}
@@ -1,35 +0,0 @@
package ac.grim.grimac.checks.impl.chat;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PacketCheck;
import ac.grim.grimac.player.GrimPlayer;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.wrapper.common.client.WrapperCommonClientSettings;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientSettings;
@CheckData(name = "ChatD", description = "Chatting while chat is hidden", experimental = true)
public class ChatD extends Check implements PacketCheck {
private boolean hidden;
public ChatD(GrimPlayer player) {
super(player);
}
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (event.getPacketType() == PacketType.Play.Client.CHAT_MESSAGE
|| event.getPacketType() == PacketType.Play.Client.CHAT_COMMAND_UNSIGNED
|| event.getPacketType() == PacketType.Play.Client.CHAT_COMMAND) {
if (hidden && flagAndAlert() && shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
}
}
if (event.getPacketType() == PacketType.Play.Client.CLIENT_SETTINGS || event.getPacketType() == PacketType.Configuration.Client.CLIENT_SETTINGS) {
hidden = new WrapperPlayClientSettings(event).getChatVisibility() == WrapperCommonClientSettings.ChatVisibility.HIDDEN;
}
}
}
@@ -2,10 +2,11 @@ package ac.grim.grimac.checks.impl.combat;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PacketCheck;
import ac.grim.grimac.player.GrimPlayer;
@CheckData(name = "Hitboxes", setback = 10)
public class Hitboxes extends Check {
public class Hitboxes extends Check implements PacketCheck {
public Hitboxes(GrimPlayer player) {
super(player);
}
@@ -92,7 +92,7 @@ public class Reach extends Check implements PacketCheck {
if (entity.isDead) return;
// TODO: Remove when in front of via
if (entity.type == EntityTypes.ARMOR_STAND && player.getClientVersion().isOlderThan(ClientVersion.V_1_8))
if (entity.getType() == EntityTypes.ARMOR_STAND && player.getClientVersion().isOlderThan(ClientVersion.V_1_8))
return;
if (player.gamemode == GameMode.CREATIVE || player.gamemode == GameMode.SPECTATOR)
@@ -129,7 +129,7 @@ public class Reach extends Check implements PacketCheck {
// Meaning that the other check should be the only one that flags.
private boolean isKnownInvalid(PacketEntity reachEntity) {
// If the entity doesn't exist, or if it is exempt, or if it is dead
if ((blacklisted.contains(reachEntity.type) || !reachEntity.isLivingEntity) && reachEntity.type != EntityTypes.END_CRYSTAL)
if ((blacklisted.contains(reachEntity.getType()) || !reachEntity.isLivingEntity()) && reachEntity.getType() != EntityTypes.END_CRYSTAL)
return false; // exempt
if (player.gamemode == GameMode.CREATIVE || player.gamemode == GameMode.SPECTATOR)
@@ -142,11 +142,8 @@ public class Reach extends Check implements PacketCheck {
return result.isFlag(); // If they flagged
} else {
SimpleCollisionBox targetBox = reachEntity.getPossibleCollisionBoxes();
if (reachEntity.type == EntityTypes.END_CRYSTAL) {
final double x = reachEntity.trackedServerPosition.getX();
final double y = reachEntity.trackedServerPosition.getX();
final double z = reachEntity.trackedServerPosition.getX();
targetBox = new SimpleCollisionBox(x - 1, y, z - 1, x + 1, y + 2, z + 1);
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.self.getAttributeValue(Attributes.ENTITY_INTERACTION_RANGE);
}
@@ -160,12 +157,12 @@ public class Reach extends Check implements PacketCheck {
CheckResult result = checkReach(reachEntity, attack.getValue(), false);
switch (result.type()) {
case REACH -> {
String added = ", type=" + reachEntity.type.getName().getKey();
String added = reachEntity.getType() == EntityTypes.PLAYER ? "" : ", type=" + reachEntity.getType().getName().getKey();
flagAndAlert(result.verbose() + added);
}
case HITBOX -> {
String added = "type=" + reachEntity.type.getName().getKey();
player.checkManager.getCheck(Hitboxes.class).flagAndAlert(result.verbose() + added);
String added = reachEntity.getType() == EntityTypes.PLAYER ? "" : "type=" + reachEntity.getType().getName().getKey();
player.checkManager.getPacketCheck(Hitboxes.class).flagAndAlert(result.verbose() + added);
}
}
}
@@ -177,11 +174,8 @@ public class Reach extends Check implements PacketCheck {
private CheckResult checkReach(PacketEntity reachEntity, Vector3d from, boolean isPrediction) {
SimpleCollisionBox targetBox = reachEntity.getPossibleCollisionBoxes();
if (reachEntity.type == EntityTypes.END_CRYSTAL) { // Hardcode end crystal box
final double x = reachEntity.trackedServerPosition.getX();
final double y = reachEntity.trackedServerPosition.getX();
final double z = reachEntity.trackedServerPosition.getX();
targetBox = new SimpleCollisionBox(x - 1, y, z - 1, x + 1, y + 2, z + 1);
if (reachEntity.getType() == EntityTypes.END_CRYSTAL) { // Hardcode end crystal box
targetBox = new SimpleCollisionBox(reachEntity.trackedServerPosition.getPos().subtract(1, 0, 1), reachEntity.trackedServerPosition.getPos().add(1, 2, 1));
}
// 1.7 and 1.8 players get a bit of extra hitbox (this is why you should use 1.8 on cross version servers)
@@ -203,20 +197,20 @@ public class Reach extends Check implements PacketCheck {
double minDistance = Double.MAX_VALUE;
// https://bugs.mojang.com/browse/MC-67665
List<Vector3dm> possibleLookDirs = new ArrayList<>(Collections.singletonList(ReachUtils.getLook(player, player.yaw, player.pitch)));
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) {
possibleLookDirs.add(ReachUtils.getLook(player, player.lastYaw, player.pitch));
possibleLookDirs.add(ReachUtils.getLook(player, player.lastXRot, player.yRot));
// 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(ReachUtils.getLook(player, player.lastYaw, player.lastPitch));
possibleLookDirs.add(ReachUtils.getLook(player, player.lastXRot, player.lastYRot));
}
// 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(ReachUtils.getLook(player, player.yaw, player.pitch));
possibleLookDirs = Collections.singletonList(ReachUtils.getLook(player, player.xRot, player.yRot));
}
}
@@ -224,33 +218,27 @@ public class Reach extends Check implements PacketCheck {
// +3 would be 3 + 3 = 6, which is the pre-1.20.5 behaviour, preventing "Missed Hitbox"
final double distance = maxReach + 3;
final double[] possibleEyeHeights = player.getPossibleEyeHeights();
final double eyePosX = from.getX();
final double eyePosZ = from.getZ();
final Vector3dm eyePos = new Vector3dm(from.getX(), 0, from.getZ());
for (Vector3dm lookVec : possibleLookDirs) {
for (double eye : possibleEyeHeights) {
final double eyePosY = from.getY() + eye;
eyePos.setY(from.getY() + eye);
Vector3dm endReachPos = eyePos.clone().add(new Vector3dm(lookVec.getX() * distance, lookVec.getY() * distance, lookVec.getZ() * distance));
final double endX = eyePosX + lookVec.getX() * distance;
final double endY = eyePosY + lookVec.getY() * distance;
final double endZ = eyePosZ + lookVec.getZ() * distance;
Vector3dm intercept = ReachUtils.calculateIntercept(targetBox, eyePos, endReachPos).first();
Vector3dm intercept = ReachUtils.calculateIntercept(targetBox, eyePosX, eyePosY, eyePosZ, endX, endY, endZ).first();
if (ReachUtils.isVecInside(targetBox, eyePosX, eyePosY, eyePosZ)) {
if (ReachUtils.isVecInside(targetBox, eyePos)) {
minDistance = 0;
break;
}
if (intercept != null) {
minDistance = Math.min(intercept.distance(eyePosX, eyePosY, eyePosZ), minDistance);
minDistance = Math.min(eyePos.distance(intercept), minDistance);
}
}
}
// if the entity is not exempt and the entity is alive
if ((!blacklisted.contains(reachEntity.type) && reachEntity.isLivingEntity) || reachEntity.type == EntityTypes.END_CRYSTAL) {
if ((!blacklisted.contains(reachEntity.getType()) && reachEntity.isLivingEntity()) || reachEntity.getType() == EntityTypes.END_CRYSTAL) {
if (minDistance == Double.MAX_VALUE) {
cancelBuffer = 1;
return new CheckResult(ResultType.HITBOX, "");
@@ -7,7 +7,6 @@ import ac.grim.grimac.player.GrimPlayer;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientClickWindow;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientClickWindow.WindowClickType;
@CheckData(name = "CrashF")
public class CrashF extends Check implements PacketCheck {
@@ -20,22 +19,24 @@ public class CrashF extends Check implements PacketCheck {
public void onPacketReceive(final PacketReceiveEvent event) {
if (event.getPacketType() == PacketType.Play.Client.CLICK_WINDOW) {
WrapperPlayClientClickWindow click = new WrapperPlayClientClickWindow(event);
WindowClickType clickType = click.getWindowClickType();
int clickType = click.getWindowClickType().ordinal();
int button = click.getButton();
int windowId = click.getWindowId();
int slot = click.getSlot();
if ((clickType == WindowClickType.QUICK_MOVE || clickType == WindowClickType.SWAP) && windowId >= 0 && button < 0) {
if ((clickType == 1 || clickType == 2) && windowId >= 0 && button < 0) {
if (flagAndAlert("clickType=" + clickType + " button=" + button)) {
event.setCancelled(true);
player.onPacketCancel();
}
} else if (windowId >= 0 && clickType == WindowClickType.SWAP && slot < 0) {
} else if (windowId >= 0 && clickType == 2 && slot < 0) {
if (flagAndAlert("clickType=" + clickType + " button=" + button + " slot=" + slot)) {
event.setCancelled(true);
player.onPacketCancel();
}
}
}
}
}
@@ -1,19 +1,20 @@
package ac.grim.grimac.checks.impl.crash;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.BlockPlaceCheck;
import ac.grim.grimac.checks.type.PacketCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.BlockBreak;
import ac.grim.grimac.utils.anticheat.update.BlockPlace;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerBlockPlacement;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerDigging;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientUseItem;
@CheckData(name = "CrashG", description = "Sent negative sequence id")
public class CrashG extends BlockPlaceCheck {
public class CrashG extends Check implements PacketCheck {
public CrashG(GrimPlayer player) {
super(player);
@@ -21,7 +22,27 @@ public class CrashG extends BlockPlaceCheck {
@Override
public void onPacketReceive(final PacketReceiveEvent event) {
if (event.getPacketType() == PacketType.Play.Client.USE_ITEM && isSupportedVersion()) {
if (!isSupportedVersion()) return;
if (event.getPacketType() == PacketType.Play.Client.PLAYER_BLOCK_PLACEMENT) {
WrapperPlayClientPlayerBlockPlacement place = new WrapperPlayClientPlayerBlockPlacement(event);
if (place.getSequence() < 0) {
flagAndAlert();
event.setCancelled(true);
player.onPacketCancel();
}
}
if (event.getPacketType() == PacketType.Play.Client.PLAYER_DIGGING) {
WrapperPlayClientPlayerDigging dig = new WrapperPlayClientPlayerDigging(event);
if (dig.getSequence() < 0) {
flagAndAlert();
event.setCancelled(true);
player.onPacketCancel();
}
}
if (event.getPacketType() == PacketType.Play.Client.USE_ITEM) {
WrapperPlayClientUseItem use = new WrapperPlayClientUseItem(event);
if (use.getSequence() < 0) {
flagAndAlert();
@@ -29,22 +50,7 @@ public class CrashG extends BlockPlaceCheck {
player.onPacketCancel();
}
}
}
@Override
public void onBlockBreak(BlockBreak blockBreak) {
if (blockBreak.sequence < 0 && isSupportedVersion()) {
flagAndAlert();
blockBreak.cancel();
}
}
@Override
public void onBlockPlace(BlockPlace place) {
if (place.sequence < 0 && isSupportedVersion()) {
flagAndAlert();
place.resync();
}
}
private boolean isSupportedVersion() {
@@ -22,7 +22,7 @@ public class CrashH extends Check implements PacketCheck {
String text = wrapper.getText();
final int length = text.length();
// general length limit
if (length > (!player.canUseGameMasterBlocks() ? 256 : 32500)) {
if (length > 256) {
if (shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
@@ -41,4 +41,6 @@ public class CrashH extends Check implements PacketCheck {
}
}
}
}
Loaded 100 of 557 files, more files were not shown because too many files have changed in this diff. Show more