Compare commits
192
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b195cb658f
|
||
|
|
8437088abf
|
||
|
|
6a72263909 | ||
|
|
ff04996ca9 | ||
|
|
46358b3079 | ||
|
|
d06bb80891 | ||
|
|
e29e8b6e9f | ||
|
|
9d1d5a4295 | ||
|
|
9ed8ae9dba | ||
|
|
bebcab70b0 | ||
|
|
db66c035c2 | ||
|
|
67da895f25 | ||
|
|
8909e710ae | ||
|
|
65815fc73b | ||
|
|
c169d9c682 | ||
|
|
99dce8c28d | ||
|
|
1b60cb82c5 | ||
|
|
11facb64c0 | ||
|
|
35fe1f0768 | ||
|
|
fc75e51a09 | ||
|
|
13ad8bd823 | ||
|
|
890799ddf9 | ||
|
|
3e07a0ee2d | ||
|
|
e8dc9ae997 | ||
|
|
b55a12c721 | ||
|
|
ece9f3cae4 | ||
|
|
efd796cdd6 | ||
|
|
b9da63c8e2 | ||
|
|
7fd7bc1d7b | ||
|
|
d1b2135191 | ||
|
|
10cb176992 | ||
|
|
77c48f4942 | ||
|
|
cd3576b196 | ||
|
|
89eedbeb85 | ||
|
|
7d9dc86ec7 | ||
|
|
11d572f8fd | ||
|
|
6a46c359c4 | ||
|
|
9b06c8670e | ||
|
|
f380fcd9af | ||
|
|
f0e15b0521 | ||
|
|
bf10e23e8e | ||
|
|
598a44ed56 | ||
|
|
8897fc87e6 | ||
|
|
252deb7fd7 | ||
|
|
5efefe59ec | ||
|
|
162a8d1b23 | ||
|
|
740f7f4aa7 | ||
|
|
5f9b9ddb65 | ||
|
|
e40ec015b9 | ||
|
|
e954124802 | ||
|
|
4a374d84be | ||
|
|
598aae0ec8 | ||
|
|
48115eb7aa | ||
|
|
68f34f2a31 | ||
|
|
3055cd10de | ||
|
|
177c00355c | ||
|
|
f6df1b768d | ||
|
|
539e822615 | ||
|
|
29c0757bbb | ||
|
|
13fb273501 | ||
|
|
574946391c | ||
|
|
0a1ce1d88b | ||
|
|
7e8b6b620e | ||
|
|
546abf07fb | ||
|
|
8a0d1e274a | ||
|
|
302bed1bc1 | ||
|
|
879d566c05 | ||
|
|
d4611f1b31 | ||
|
|
53cf3e8e99 | ||
|
|
c356f5fa4d | ||
|
|
d273229487 | ||
|
|
c7983b7e37 | ||
|
|
272a8892db | ||
|
|
439477eff1 | ||
|
|
1f55742d76 | ||
|
|
b281a2d158 | ||
|
|
7d487a4279 | ||
|
|
f0fbc9db8c | ||
|
|
0a6ac7a47e | ||
|
|
4ca65b1670 | ||
|
|
7be8f51267 | ||
|
|
1d0cbb7078 | ||
|
|
b65b7f0f58 | ||
|
|
327b947c08 | ||
|
|
2b621483e7 | ||
|
|
f118b5c781 | ||
|
|
f95d877025 | ||
|
|
6c49a01853 | ||
|
|
80afaae74f | ||
|
|
35174fdca9 | ||
|
|
88cfffc5d6 | ||
|
|
cd86c14709 | ||
|
|
b7a719dc2e | ||
|
|
c04335773b | ||
|
|
9d1682d397 | ||
|
|
fefe0d150f | ||
|
|
a54329ad45 | ||
|
|
36590605b1 | ||
|
|
075cec8c12 | ||
|
|
b9fa89ccb9 | ||
|
|
08770f0c6b | ||
|
|
50a25d9921 | ||
|
|
21f69886b8 | ||
|
|
8001f6cb1d | ||
|
|
c5855fd58d | ||
|
|
bf8586393a | ||
|
|
95e2774f59 | ||
|
|
5c17fba090 | ||
|
|
02f6a3b51b | ||
|
|
dbd75108c8 | ||
|
|
9cb45476ea | ||
|
|
be35b9b481 | ||
|
|
ca64aabaa2 | ||
|
|
801658600c | ||
|
|
a2074d0fe4 | ||
|
|
14573dace1 | ||
|
|
639490a825 | ||
|
|
07d201004e | ||
|
|
6186abc7e7 | ||
|
|
29d28c64fc | ||
|
|
98fd065ddb | ||
|
|
d0b30ded6c | ||
|
|
b79d5fdaaf | ||
|
|
7c68947731 | ||
|
|
80f90e658f | ||
|
|
101473b913 | ||
|
|
42b6630c55 | ||
|
|
daa3bfe186 | ||
|
|
4292e93327 | ||
|
|
60b70d1a11 | ||
|
|
ce0dad56ad | ||
|
|
90193b47d6 | ||
|
|
9e745fdba5 | ||
|
|
d374c2bc08 | ||
|
|
2d8def6eec | ||
|
|
83794571d5 | ||
|
|
8f0036e4e9 | ||
|
|
b6f9f86d11 | ||
|
|
55da3842f5 | ||
|
|
955b725884 | ||
|
|
96267128fc | ||
|
|
92d06e4222 | ||
|
|
5f02ccc4fe | ||
|
|
f772b745cc | ||
|
|
9bd12cc7a2 | ||
|
|
32f607b597 | ||
|
|
c2afc62241 | ||
|
|
97b875860f | ||
|
|
c019566255 | ||
|
|
12f6af3715 | ||
|
|
965008f7e1 | ||
|
|
4a104e287c | ||
|
|
0153ce3a4d | ||
|
|
d96d683b14 | ||
|
|
acf273fd1f | ||
|
|
679e869a00 | ||
|
|
324469a78a | ||
|
|
a1b4893d88 | ||
|
|
e4c4642984 | ||
|
|
4d28f85665 | ||
|
|
0d22e34c5a | ||
|
|
2d053d0923 | ||
|
|
3bf3226fa5 | ||
|
|
bf637f5fb3 | ||
|
|
3ae1e32813 | ||
|
|
e2e25206ec | ||
|
|
8f3b7db844 | ||
|
|
0d67045f66 | ||
|
|
49742a9ddc | ||
|
|
612e0f8f67 | ||
|
|
5d4af9365b | ||
|
|
5489f118c1 | ||
|
|
ded5c0f320 | ||
|
|
238b77c002 | ||
|
|
71d93c97cf | ||
|
|
43f4c7fcd7 | ||
|
|
03714c9b97 | ||
|
|
004804c09e | ||
|
|
853d02b9c8 | ||
|
|
2272d4df16 | ||
|
|
fa2dc7b00b | ||
|
|
c8347ed146 | ||
|
|
c94260bea6 | ||
|
|
bde2328511 | ||
|
|
b87eeaf490 | ||
|
|
3abc904590 | ||
|
|
1b416fc841 | ||
|
|
224f464eff | ||
|
|
ac35735fff | ||
|
|
f54ca0e231 | ||
|
|
75b5405160 | ||
|
|
c2acfae296 |
No files matched your search
@@ -0,0 +1 @@
|
||||
.gitea/CODEOWNERS linguist-detectable=false
|
||||
@@ -0,0 +1,3 @@
|
||||
# Pull requests request review from the eligible staff team(s).
|
||||
# One approval from either an Admin or Developer satisfies branch protection.
|
||||
* @trueog/Admins @trueog/Developers
|
||||
@@ -3,6 +3,11 @@ name: Reusable Build and Publish
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
runner:
|
||||
description: 'The runner to use for the build job.'
|
||||
required: false
|
||||
type: string
|
||||
default: 'ubuntu-latest'
|
||||
is-release-build:
|
||||
description: 'True if this is a release build, which affects versioning and publishing.'
|
||||
required: true
|
||||
@@ -36,9 +41,10 @@ on:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: tenki-standard-autoscale
|
||||
runs-on: ${{ inputs.runner }}
|
||||
permissions:
|
||||
contents: read
|
||||
# Grant permission to write contents (for creating tags/refs)
|
||||
contents: write
|
||||
packages: write
|
||||
|
||||
env:
|
||||
@@ -123,14 +129,14 @@ jobs:
|
||||
fi
|
||||
|
||||
- name: Upload Bukkit Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: grimac-bukkit
|
||||
path: ${{ github.workspace }}/bukkit/build/libs/grimac-bukkit-${{ env.BUILD_VERSION }}.jar
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Upload Fabric Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: grimac-fabric
|
||||
path: ${{ github.workspace }}/fabric/build/libs/grimac-fabric-${{ env.BUILD_VERSION }}.jar
|
||||
@@ -177,23 +183,12 @@ jobs:
|
||||
echo "EOF"
|
||||
} >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Publish Fabric to Modrinth
|
||||
if: ${{ inputs.publish-artifacts == true && env.MODRINTH_ID != '' && env.MODRINTH_TOKEN != '' }}
|
||||
uses: Kir-Antipov/mc-publish@v3.3
|
||||
- name: Generate build number
|
||||
if: contains(fromJSON('["merge","release","main","lightning"]'), github.ref_name) && inputs.publish-artifacts == true && env.MODRINTH_ID != '' && env.MODRINTH_TOKEN != ''
|
||||
id: buildnumber
|
||||
uses: onyxmueller/build-tag-number@v1
|
||||
with:
|
||||
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.21.5 <=1.21.10'
|
||||
dependencies: 'fabric-api'
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
#------------------------------------------------------------------
|
||||
# Publish to Modrinth & API
|
||||
@@ -207,24 +202,52 @@ jobs:
|
||||
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 }}
|
||||
name: LightningGrim Anticheat (Bukkit) ${{ env.BUILD_VERSION }}-b${{ steps.buildnumber.outputs.build_number }}
|
||||
version: ${{ env.BUILD_VERSION }}-b${{ steps.buildnumber.outputs.build_number }}
|
||||
version-type: ${{ inputs.release-version-type }}
|
||||
changelog: ${{ steps.changelog.outputs.notes }}
|
||||
loaders: |
|
||||
bukkit
|
||||
spigot
|
||||
paper
|
||||
folia
|
||||
purpur
|
||||
game-versions: '>=1.7 <=1.21.10'
|
||||
bukkit
|
||||
spigot
|
||||
paper
|
||||
folia
|
||||
purpur
|
||||
game-versions: '>=1.7 <=1.21.11'
|
||||
|
||||
- name: Publish Fabric to Modrinth
|
||||
if: ${{ inputs.publish-artifacts == true && env.MODRINTH_ID != '' && env.MODRINTH_TOKEN != '' }}
|
||||
uses: Kir-Antipov/mc-publish@v3.3
|
||||
with:
|
||||
modrinth-id: ${{ secrets.MODRINTH_ID }}
|
||||
modrinth-token: ${{ secrets.MODRINTH_TOKEN }}
|
||||
modrinth-featured: ${{ inputs.is-release-build }} # Only feature release builds
|
||||
|
||||
files: fabric/build/libs/grimac-fabric-${{ env.BUILD_VERSION }}.jar
|
||||
name: LightningGrim Anticheat (Fabric) ${{ env.BUILD_VERSION }}-b${{ steps.buildnumber.outputs.build_number }}
|
||||
version: ${{ env.BUILD_VERSION }}-b${{ steps.buildnumber.outputs.build_number }}
|
||||
version-type: ${{ inputs.release-version-type }}
|
||||
changelog: ${{ steps.changelog.outputs.notes }}
|
||||
loaders: |
|
||||
fabric
|
||||
game-versions: '>=1.16.1 <=1.21.10'
|
||||
dependencies: 'fabric-api'
|
||||
|
||||
- name: Publish to API
|
||||
if: ${{ inputs.publish-artifacts == true && env.BUILD_UPDATE_TOKEN != '' }}
|
||||
if: ${{ inputs.publish-artifacts == true && env.BUILD_UPDATE_TOKEN != '' && inputs.is-release-build == false }}
|
||||
run: |
|
||||
curl -X POST \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "User-Agent: GrimACBuilder/v1" \
|
||||
-H "Authorization: Bearer ${{ secrets.BUILD_UPDATE_TOKEN }}" \
|
||||
-d '{"org": "${{ github.repository_owner }}", "repository": "${{ github.event.repository.name }}", "branch": "${{ github.ref_name }}", "version": "${{ env.BUILD_VERSION }}"}' \
|
||||
"${{ secrets.BUILD_UPDATE_URL }}"
|
||||
"${{ secrets.BUILD_UPDATE_URL }}"
|
||||
|
||||
- name: Publish Release to API
|
||||
if: ${{ inputs.publish-artifacts == true && env.BUILD_UPDATE_TOKEN != '' && inputs.is-release-build == true }}
|
||||
run: |
|
||||
curl -X POST \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "User-Agent: GrimACBuilder/v1" \
|
||||
-H "Authorization: Bearer ${{ secrets.BUILD_UPDATE_TOKEN }}" \
|
||||
-d '{"org": "${{ github.repository_owner }}", "repository": "${{ github.event.repository.name }}", "branch": "release", "version": "${{ env.BUILD_VERSION }}"}' \
|
||||
"${{ secrets.BUILD_UPDATE_URL }}"
|
||||
@@ -12,64 +12,64 @@
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches: [dev]
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches: [dev]
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: tenki-standard-autoscale
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: tenki-standard-autoscale
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: ['java-kotlin']
|
||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
|
||||
# Learn more:
|
||||
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: ['java-kotlin']
|
||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
|
||||
# Learn more:
|
||||
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v5
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v5
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v3
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
# queries: ./path/to/local/query, your-org/your-repo/queries@main
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v3
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
# queries: ./path/to/local/query, your-org/your-repo/queries@main
|
||||
|
||||
- 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
|
||||
- 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
|
||||
uses: github/codeql-action/autobuild@v3
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v3
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
|
||||
# and modify them (or add more) to build your code if your project
|
||||
# uses a compiled language
|
||||
|
||||
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
|
||||
# and modify them (or add more) to build your code if your project
|
||||
# uses a compiled language
|
||||
#- run: |
|
||||
# make bootstrap
|
||||
# make release
|
||||
|
||||
#- run: |
|
||||
# make bootstrap
|
||||
# make release
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v3
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v3
|
||||
@@ -12,6 +12,11 @@ on:
|
||||
description: 'Upload to Modrinth & API? (Uncheck for a dry run)'
|
||||
type: boolean
|
||||
default: true
|
||||
runner:
|
||||
description: 'The runner to use for the build job.'
|
||||
required: false
|
||||
type: string
|
||||
default: 'tenki-standard-autoscale'
|
||||
push:
|
||||
branches:
|
||||
- 'main'
|
||||
@@ -19,6 +24,7 @@ on:
|
||||
- 'dev'
|
||||
- 'release'
|
||||
- 'release/**'
|
||||
- 'lightning'
|
||||
paths:
|
||||
- 'bukkit/**'
|
||||
- 'common/**'
|
||||
@@ -31,6 +37,7 @@ jobs:
|
||||
call-build-workflow:
|
||||
uses: ./.github/workflows/build-and-publish.yml
|
||||
with:
|
||||
runner: ${{ github.event.inputs.runner || 'tenki-standard-autoscale' }}
|
||||
is-release-build: false
|
||||
release-version-type: alpha # Dev builds are always 'alpha'
|
||||
run-build-scan: ${{ github.event.inputs.run-build-scan == 'true' }}
|
||||
|
||||
@@ -8,6 +8,7 @@ jobs:
|
||||
call-release-workflow:
|
||||
uses: ./.github/workflows/build-and-publish.yml
|
||||
with:
|
||||
runner: 'tenki-standard-autoscale'
|
||||
is-release-build: true
|
||||
release-version-type: release # Official releases are 'release'
|
||||
# Use the body of the GitHub Release as the changelog
|
||||
|
||||
@@ -73,3 +73,5 @@ nb-configuration.xml
|
||||
##############################
|
||||
run/
|
||||
fabric/run/
|
||||
|
||||
.ignored/
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
# Contributing to GrimAC
|
||||
# Contributing to LightningGrim
|
||||
|
||||
Thank you for your interest in contributing to GrimAC. This document outlines the guidelines for
|
||||
Thank you for your interest in contributing to LightningGrim. This document outlines the guidelines for
|
||||
making pull
|
||||
requests to the project. *We're usually pretty lenient with pull requests, but this guide will help
|
||||
make the process go more smoothly.*
|
||||
@@ -31,7 +31,7 @@ make the process go more smoothly.*
|
||||
|
||||
### Development Notes
|
||||
|
||||
- GrimAC is built using [Gradle](https://gradle.org/) kotlin scripts.
|
||||
- LightningGrim is built using [Gradle](https://gradle.org/) kotlin scripts.
|
||||
- Java 21 is currently required to build the project. A minimum of Java 17 is required to run it.
|
||||
|
||||
### Questions & Support
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
To honour the JVM settings for this build a single-use Daemon process will be forked. For more on this, please refer to https://docs.gradle.org/8.14.3/userguide/gradle_daemon.html#sec:disabling_the_daemon in the Gradle documentation.
|
||||
Daemon will be stopped at the end of the build
|
||||
|
||||
> Configure project :buildSrc
|
||||
WARNING: Unsupported Kotlin plugin version.
|
||||
The `embedded-kotlin` and `kotlin-dsl` plugins rely on features of Kotlin `2.0.21` that might work differently than in the requested version `2.2.20`.
|
||||
|
||||
> Task :buildSrc:checkKotlinGradlePluginConfigurationErrors SKIPPED
|
||||
> Task :buildSrc:generateExternalPluginSpecBuilders UP-TO-DATE
|
||||
> Task :buildSrc:extractPrecompiledScriptPluginPlugins UP-TO-DATE
|
||||
> Task :buildSrc:compilePluginsBlocks UP-TO-DATE
|
||||
> Task :buildSrc:generatePrecompiledScriptPluginAccessors UP-TO-DATE
|
||||
> Task :buildSrc:generateScriptPluginAdapters UP-TO-DATE
|
||||
> Task :buildSrc:pluginDescriptors UP-TO-DATE
|
||||
> Task :buildSrc:processResources UP-TO-DATE
|
||||
|
||||
> Task :buildSrc:compileKotlin
|
||||
w: Language version 1.8 is deprecated and its support will be removed in a future version of Kotlin
|
||||
|
||||
> Task :buildSrc:compileJava NO-SOURCE
|
||||
> Task :buildSrc:compileGroovy NO-SOURCE
|
||||
> Task :buildSrc:classes UP-TO-DATE
|
||||
> Task :buildSrc:jar
|
||||
|
||||
> Configure project :
|
||||
Build configuration:
|
||||
shadePE = false
|
||||
relocate = true
|
||||
mavenLocalOverride = true
|
||||
release = false
|
||||
version = 2.3.73-HEAD-bebcab7+lite
|
||||
|
||||
> Task :clean
|
||||
> Task :bukkit:clean
|
||||
> Task :common:clean
|
||||
> Task :assemble UP-TO-DATE
|
||||
> Task :check UP-TO-DATE
|
||||
> Task :bukkit:generateBukkitPluginDescription
|
||||
> Task :bukkit:processResources
|
||||
> Task :bukkit:generateTestEffectiveLombokConfig
|
||||
> Task :common:processResources
|
||||
> Task :bukkit:sourcesJar
|
||||
> Task :spotlessInternalRegisterDependencies
|
||||
> Task :bukkit:spotlessJava
|
||||
> Task :bukkit:spotlessJavaApply
|
||||
> Task :bukkit:spotlessJavaCheck
|
||||
> Task :bukkit:spotlessKotlinGradle
|
||||
> Task :bukkit:spotlessKotlinGradleApply
|
||||
> Task :bukkit:spotlessKotlinGradleCheck
|
||||
> Task :bukkit:spotlessCheck
|
||||
> Task :bukkit:processTestResources NO-SOURCE
|
||||
> Task :bukkit:spotlessApply
|
||||
> Task :common:sourcesJar
|
||||
> Task :common:spotlessJava
|
||||
> Task :common:spotlessJavaApply
|
||||
> Task :common:spotlessJavaCheck
|
||||
> Task :common:spotlessKotlinGradle
|
||||
> Task :common:spotlessKotlinGradleApply
|
||||
> Task :common:spotlessKotlinGradleCheck
|
||||
> Task :common:spotlessCheck
|
||||
> Task :common:generateTestEffectiveLombokConfig
|
||||
> Task :common:processTestResources NO-SOURCE
|
||||
> Task :common:spotlessApply
|
||||
|
||||
> Task :common:eclipseClasspath
|
||||
Could not resolve: com.github.retrooper:packetevents-api:2.11.2-SNAPSHOT
|
||||
|
||||
> Task :common:eclipseJdt
|
||||
> Task :common:eclipseProject
|
||||
> Task :common:eclipse
|
||||
> Task :bukkit:generateEffectiveLombokConfig
|
||||
> Task :common:generateEffectiveLombokConfig
|
||||
> Task :common:compileJava FAILED
|
||||
|
||||
FAILURE: Build failed with an exception.
|
||||
|
||||
* What went wrong:
|
||||
Execution failed for task ':common:compileJava'.
|
||||
> Could not resolve all files for configuration ':common:compileClasspath'.
|
||||
> Could not find com.github.retrooper:packetevents-api:2.11.2-SNAPSHOT.
|
||||
Searched in the following locations:
|
||||
- https://repo.grim.ac/snapshots/com/github/retrooper/packetevents-api/2.11.2-SNAPSHOT/maven-metadata.xml
|
||||
- https://repo.grim.ac/snapshots/com/github/retrooper/packetevents-api/2.11.2-SNAPSHOT/packetevents-api-2.11.2-SNAPSHOT.pom
|
||||
- file:/home/admin/.m2/repository/com/github/retrooper/packetevents-api/2.11.2-SNAPSHOT/maven-metadata.xml
|
||||
- file:/home/admin/.m2/repository/com/github/retrooper/packetevents-api/2.11.2-SNAPSHOT/packetevents-api-2.11.2-SNAPSHOT.pom
|
||||
Required by:
|
||||
project :common
|
||||
|
||||
* Try:
|
||||
> Run with --stacktrace option to get the stack trace.
|
||||
> Run with --info or --debug option to get more log output.
|
||||
> Run with --scan to get full insights.
|
||||
> Get more help at https://help.gradle.org.
|
||||
|
||||
BUILD FAILED in 17s
|
||||
38 actionable tasks: 31 executed, 7 up-to-date
|
||||
@@ -1,5 +1,5 @@
|
||||
<div align="center">
|
||||
<h1>GrimAC</h1>
|
||||
<h1>LightningGrim</h1>
|
||||
|
||||
<div>
|
||||
<a href="https://github.com/GrimAnticheat/Grim/actions/workflows/gradle-publish.yml">
|
||||
@@ -18,20 +18,20 @@
|
||||
<br>
|
||||
</div>
|
||||
|
||||
GrimAC is an open source Minecraft anticheat designed to support the latest versions of Minecraft.
|
||||
LightningGrim is an open source Minecraft anticheat fork designed to support the latest versions of Minecraft.
|
||||
It currently supports minecraft versions 1.8–1.21. Geyser players are fully exempt from the anticheat to prevent false positives.
|
||||
This project is considered feature-complete for the 2.0 (open-source) branch. If you would like a bug fix or enhancement and cannot sponsor the work, pull requests are welcome.
|
||||
A premium version is planned, which will offer additional subscription-based paid checks, such as heuristics.
|
||||
|
||||
## Downloads
|
||||
## Changes over GrimAC:
|
||||
|
||||
- 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/)~~
|
||||
- WallHit - checks for hitting through walls
|
||||
- EntityPierce - checks for hitting through other entities
|
||||
- Bukkit Event Piston Optimizations
|
||||
- Optimized Hitbox/Collision Boxes
|
||||
- Optimized Reach calculations
|
||||
- Checks for PacketOrder (patches lots of noslow, autoblocks, etc...)
|
||||
- Checks for Inventory interactions (prevents using crafting slots as extra storage, moving while having inventory open, etc...)
|
||||
|
||||
## Requirements & Installation
|
||||
|
||||
@@ -56,20 +56,20 @@ are.
|
||||
|
||||
## Developer Plugin API
|
||||
|
||||
Grim's plugin API allows you to integrate Grim into your own plugins. Visit
|
||||
LightningGrim's plugin API allows you to integrate LightningGrim into your own plugins. Visit
|
||||
the [plugin API repository](https://github.com/GrimAnticheat/GrimAPI) for the source code and more
|
||||
information.
|
||||
|
||||
## Compiling From Source
|
||||
|
||||
1. `git clone https://github.com/GrimAnticheat/Grim.git`
|
||||
2. `cd Grim`
|
||||
3. `./gradlew build`
|
||||
1. `git clone https://github.com/Axionize/LightningGrim`
|
||||
2. `cd LightningGrim`
|
||||
3. `./gradlew build clean eclipse --warning-mode all`
|
||||
4. The final jars will compile into the `<platform>/build/libs` folders
|
||||
|
||||
## Grim Supremacy
|
||||
## LightningGrim Supremacy
|
||||
|
||||
What makes Grim stand out against other anticheats?
|
||||
What makes LightningGrim stand out against other anticheats?
|
||||
|
||||
### Movement Simulation Engine
|
||||
|
||||
|
||||
+21
-2
@@ -2,13 +2,19 @@
|
||||
* GrimAC Build Configuration
|
||||
*
|
||||
* Build Flags:
|
||||
* -PshadePE=true - Enables 'lite' mode
|
||||
* -PshadePE=true - Bundles PacketEvents into the jar (default: external/lite).
|
||||
* -Prelocate=false - Adds 'no_relocate' modifier
|
||||
* -Prelease=true - Removes commit/modifiers for release build
|
||||
*
|
||||
* Logic in: buildSrc/versioning/BuildConfig.kt & VersionUtil.kt
|
||||
*/
|
||||
|
||||
plugins {
|
||||
base
|
||||
}
|
||||
|
||||
import org.gradle.api.tasks.Copy
|
||||
import org.gradle.api.tasks.compile.JavaCompile
|
||||
import versioning.BuildConfig
|
||||
import versioning.VersionUtil
|
||||
|
||||
@@ -17,7 +23,7 @@ BuildConfig.init(project)
|
||||
val baseVersion = "2.3.73"
|
||||
group = "ac.grim.grimac"
|
||||
version = VersionUtil.computeVersion(baseVersion)
|
||||
description = "Libre simulation anticheat designed for 1.21 with 1.8–1.21 support, powered by PacketEvents 2.0."
|
||||
description = "Libre simulation anticheat designed for 1.21 with 1.8–1.21 support, powered by PacketEvents 2.11.2."
|
||||
|
||||
ext["timestamp"] = System.currentTimeMillis().toString()
|
||||
ext["git_branch"] = VersionUtil.getGitBranch(true)
|
||||
@@ -47,3 +53,16 @@ subprojects {
|
||||
options.isIncremental = true
|
||||
}
|
||||
}
|
||||
|
||||
tasks.register<Copy>("copyBukkitJarToRoot") {
|
||||
dependsOn(":bukkit:shadowJar")
|
||||
from(project(":bukkit").layout.buildDirectory.dir("libs")) {
|
||||
include("LightningGrim-bukkit-$version.jar") // current build only; stale jars from prior commits would collide on rename
|
||||
rename("LightningGrim-bukkit-.*\\.jar", "LightningGrim-$baseVersion.jar")
|
||||
}
|
||||
into(layout.buildDirectory.dir("libs"))
|
||||
}
|
||||
|
||||
tasks.named("build") {
|
||||
dependsOn("copyBukkitJarToRoot")
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
plugins {
|
||||
`kotlin-dsl`
|
||||
kotlin("plugin.serialization") version "1.9.0" // Use the same Kotlin version as your main project
|
||||
kotlin("plugin.serialization") version "2.2.20"
|
||||
}
|
||||
|
||||
repositories {
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
import org.gradle.api.artifacts.dsl.RepositoryHandler
|
||||
import org.gradle.api.artifacts.repositories.MavenArtifactRepository
|
||||
import org.gradle.api.artifacts.repositories.InclusiveRepositoryContentDescriptor
|
||||
import org.gradle.kotlin.dsl.maven
|
||||
import versioning.BuildConfig
|
||||
|
||||
/**
|
||||
* Registers a repository with strict content exclusivity.
|
||||
*
|
||||
* Logic:
|
||||
* 1. If BuildConfig.mavenLocalOverride is true, checks Maven Local first.
|
||||
* 2. Checks the provided remote URL second.
|
||||
* 3. Enforces strict filtering so Gradle never checks this repo for other deps,
|
||||
* and never checks other repos (like Central) for these deps.
|
||||
*/
|
||||
fun RepositoryHandler.exclusive(
|
||||
url: String,
|
||||
repoConfig: MavenArtifactRepository.() -> Unit = {},
|
||||
filterConfig: InclusiveRepositoryContentDescriptor.() -> Unit
|
||||
) {
|
||||
// We define the repositories inside the function scope so they are added to the handler
|
||||
val remote = maven(url, repoConfig)
|
||||
|
||||
exclusive(remote, filterConfig)
|
||||
}
|
||||
|
||||
fun RepositoryHandler.exclusive(
|
||||
remote: MavenArtifactRepository,
|
||||
filterConfig: InclusiveRepositoryContentDescriptor.() -> Unit
|
||||
) {
|
||||
// We access BuildConfig here again to check for local override
|
||||
val local = if (BuildConfig.mavenLocalOverride) mavenLocal() else null
|
||||
|
||||
exclusiveContent {
|
||||
if (local != null) {
|
||||
forRepositories(local, remote)
|
||||
} else {
|
||||
forRepositories(remote)
|
||||
}
|
||||
filter(filterConfig)
|
||||
}
|
||||
}
|
||||
@@ -66,7 +66,6 @@ tasks {
|
||||
listOf(
|
||||
"bungee.yml",
|
||||
"velocity-plugin.json",
|
||||
"fabric.mod.json",
|
||||
"grimac.properties"
|
||||
)
|
||||
) {
|
||||
|
||||
@@ -6,7 +6,13 @@ plugins {
|
||||
}
|
||||
|
||||
tasks.named<ShadowJar>("shadowJar") {
|
||||
minimize()
|
||||
minimize {
|
||||
// The PostgreSQL JDBC driver is loaded reflectively via java.sql ServiceLoader,
|
||||
// so minimize() sees no direct references and would strip it. Keep it whole.
|
||||
exclude(dependency("org.postgresql:postgresql:.*"))
|
||||
// Same for the SLF4J NOP provider, loaded via SLF4JServiceProvider ServiceLoader.
|
||||
exclude(dependency("org.slf4j:slf4j-nop:.*"))
|
||||
}
|
||||
archiveFileName = "${rootProject.name}-${project.name}-${rootProject.version}.jar"
|
||||
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
|
||||
|
||||
@@ -32,6 +38,7 @@ tasks.named<ShadowJar>("shadowJar") {
|
||||
relocate("org.incendo", "ac.grim.grimac.shaded.incendo")
|
||||
relocate("io.leangen.geantyref", "ac.grim.grimac.shaded.geantyref") // Required by cloud
|
||||
relocate("com.zaxxer", "ac.grim.grimac.shaded.zaxxer") // Database history
|
||||
relocate("org.postgresql", "ac.grim.grimac.shaded.postgresql") // Database history (JDBC driver); service file rewritten by mergeServiceFiles
|
||||
}
|
||||
mergeServiceFiles()
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ import versioning.BuildConfig.shadePE
|
||||
* ./gradlew build -DshadePE=true -Drelease=true
|
||||
* ```
|
||||
*
|
||||
* @property shadePE If true, shades PacketEvents into the jar. Default: true.
|
||||
* @property shadePE Bundles PacketEvents into the jar when true; consumes an external provider when false (default; emits the `-lite` artifact).
|
||||
* @property relocate If true, relocates shaded dependencies to avoid conflicts. Default: true.
|
||||
* @property release If true, omits commit hash and modifiers from version string. Default: false.
|
||||
* @property mavenLocalOverride If true, will make artifacts in mavenLocal() will be used instead of their remote counterparts for this build. Default: false
|
||||
@@ -53,10 +53,10 @@ object BuildConfig {
|
||||
* ```
|
||||
*/
|
||||
fun init(project: Project) {
|
||||
_shadePE = resolveBool(project, "shadePE", altKey = "SHADE_PE", default = true)
|
||||
_shadePE = resolveBool(project, "shadePE", altKey = "SHADE_PE", default = false)
|
||||
_relocate = resolveBool(project, "relocate", altKey = "RELOCATE_JAR", default = true)
|
||||
_release = resolveBool(project, "release", default = false)
|
||||
_mavenLocalOverride = resolveBool(project, "mavenLocalOverride", default = false)
|
||||
_mavenLocalOverride = resolveBool(project, "mavenLocalOverride", altKey = "MAVEN_LOCAL_OVERRIDE", default = false)
|
||||
}
|
||||
|
||||
// Unified resolution logic (System > Gradle > Env)
|
||||
@@ -78,7 +78,7 @@ object BuildConfig {
|
||||
private var _release: Boolean? = null
|
||||
private var _mavenLocalOverride: Boolean? = null
|
||||
|
||||
/** If true, shades PacketEvents into the jar. Default: true. */
|
||||
/** If true, shades PacketEvents into the jar. Default: false (external provider). */
|
||||
val shadePE: Boolean get() = _shadePE
|
||||
?: error("BuildConfig.shadePE accessed before init() was called")
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ package versioning
|
||||
import java.io.ByteArrayOutputStream
|
||||
|
||||
/**
|
||||
* Utility for computing the version string of GrimAC artifacts.
|
||||
* Utility for computing the version string of LightningGrim artifacts.
|
||||
*
|
||||
* The version string is constructed based on:
|
||||
* - A base semantic version (e.g., "2.3.72")
|
||||
@@ -91,6 +91,7 @@ object VersionUtil {
|
||||
val mainBranch = System.getenv("GRIM_MAIN_BRANCH") ?: "2.0"
|
||||
|
||||
return when (branch) {
|
||||
"lightning" -> null
|
||||
"main", mainBranch -> null // ← ignore these branches
|
||||
else -> branch
|
||||
}
|
||||
|
||||
+40
-60
@@ -10,73 +10,38 @@ plugins {
|
||||
}
|
||||
|
||||
repositories {
|
||||
if (BuildConfig.mavenLocalOverride) {
|
||||
mavenLocal()
|
||||
// 1. Fallback for non-exclusive deps (e.g. Maven Central deps)
|
||||
if (BuildConfig.mavenLocalOverride) mavenLocal()
|
||||
|
||||
// 2. Exclusive Repositories (One HTTP request per dep)
|
||||
exclusive("https://repo.papermc.io/repository/maven-public/", { name = "papermc" }) {
|
||||
includeGroup("io.papermc.paper")
|
||||
includeGroup("net.md-5")
|
||||
}
|
||||
|
||||
// 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")
|
||||
}
|
||||
exclusive("https://libraries.minecraft.net", { mavenContent { releasesOnly() } }) {
|
||||
includeModule("com.mojang", "brigadier")
|
||||
}
|
||||
|
||||
exclusiveContent {
|
||||
forRepository {
|
||||
maven("https://libraries.minecraft.net") { // Brigadier
|
||||
mavenContent { releasesOnly() }
|
||||
}
|
||||
}
|
||||
filter {
|
||||
includeModule("com.mojang", "brigadier")
|
||||
}
|
||||
exclusive("https://repo.extendedclip.com/content/repositories/placeholderapi/") {
|
||||
includeGroup("me.clip")
|
||||
}
|
||||
|
||||
// For placeholderapi
|
||||
exclusiveContent {
|
||||
forRepository {
|
||||
maven("https://repo.extendedclip.com/content/repositories/placeholderapi/")
|
||||
}
|
||||
filter {
|
||||
includeGroup("me.clip")
|
||||
}
|
||||
exclusive("https://repo.codemc.io/repository/maven-releases/", { mavenContent { releasesOnly() } }) {
|
||||
includeGroup("com.github.retrooper")
|
||||
}
|
||||
|
||||
// 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")
|
||||
}
|
||||
exclusive("https://repo.grim.ac/snapshots") {
|
||||
includeGroup("ac.grim.grimac")
|
||||
}
|
||||
|
||||
// For Configuralize (transitive from :common)
|
||||
exclusiveContent {
|
||||
forRepository {
|
||||
maven("https://nexus.scarsz.me/content/repositories/releases") {
|
||||
mavenContent { releasesOnly() }
|
||||
}
|
||||
}
|
||||
filter {
|
||||
includeGroup("github.scarsz")
|
||||
}
|
||||
exclusive("https://nexus.scarsz.me/content/repositories/releases", { mavenContent { releasesOnly() } }) {
|
||||
includeGroup("github.scarsz")
|
||||
}
|
||||
|
||||
// Maven Central Fallback
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
|
||||
dependencies {
|
||||
compileOnly(libs.paper.api)
|
||||
compileOnly(libs.placeholderapi)
|
||||
@@ -87,22 +52,24 @@ dependencies {
|
||||
compileOnly(libs.packetevents.spigot)
|
||||
}
|
||||
implementation(libs.cloud.paper)
|
||||
implementation(libs.adventure.platform.bukkit)
|
||||
|
||||
implementation(libs.grim.bukkit.internal)
|
||||
implementation("org.slf4j:slf4j-nop:2.0.17") // Provide SLF4J NOP provider to suppress missing-provider warning.
|
||||
implementation("org.slf4j:slf4j-api:2.0.17") // Bundle a relocated SLF4J API for shaded dependencies.
|
||||
implementation(project(":common"))
|
||||
shadow(project(":common"))
|
||||
}
|
||||
|
||||
bukkit {
|
||||
name = "GrimAC"
|
||||
author = "GrimAC"
|
||||
name = "LightningGrim"
|
||||
author = "LightningGrim"
|
||||
main = "ac.grim.grimac.platform.bukkit.GrimACBukkitLoaderPlugin"
|
||||
website = "https://grim.ac/"
|
||||
apiVersion = "1.13"
|
||||
foliaSupported = true
|
||||
|
||||
if (!BuildConfig.shadePE) {
|
||||
depend = listOf("packetevents")
|
||||
// Bukkit `depend` is case-sensitive; canonical PE plugin name is PascalCase.
|
||||
depend = listOf("PacketEvents")
|
||||
}
|
||||
|
||||
softDepend = listOf(
|
||||
@@ -116,6 +83,7 @@ bukkit {
|
||||
"floodgate",
|
||||
"FastLogin",
|
||||
"PlaceholderAPI",
|
||||
"ZNPCsPlus",
|
||||
)
|
||||
|
||||
permissions {
|
||||
@@ -185,20 +153,32 @@ bukkit {
|
||||
"Shows lists of specific data"
|
||||
default = Permission.Default.FALSE
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
publishing.publications.create<MavenPublication>("maven") {
|
||||
artifact(tasks["shadowJar"])
|
||||
artifact(tasks.named("shadowJar"))
|
||||
}
|
||||
|
||||
apply(from = "$projectDir/gradle/packetevents-provider.gradle.kts")
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val registerStagePE = project.extra["registerStagePacketEventsTask"]
|
||||
as (org.gradle.api.Project, String, java.io.File) -> org.gradle.api.tasks.TaskProvider<Copy>
|
||||
|
||||
val stagePEJar = registerStagePE(project, "stageLocalPacketEvents", file("run"))
|
||||
|
||||
tasks {
|
||||
runServer {
|
||||
minecraftVersion("1.21.10")
|
||||
minecraftVersion("1.21.11")
|
||||
// External mode stages the locally built True-OG PacketEvents JAR.
|
||||
if (!BuildConfig.shadePE) {
|
||||
dependsOn(stagePEJar)
|
||||
}
|
||||
}
|
||||
|
||||
shadowJar {
|
||||
mergeServiceFiles()
|
||||
manifest {
|
||||
attributes["paperweight-mappings-namespace"] = "mojang"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
// True-OG PacketEvents JAR staging convention.
|
||||
// Resolution order (first hit wins): -PtruogPacketEventsJar, TRUEOG_PACKETEVENTS_JAR env, default glob under ~/eclipse-workspace/PacketEvents/spigot/build/libs (case-insensitive, latest by mtime). See context/refs/packetevents-provider.md.
|
||||
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
import java.util.Comparator
|
||||
|
||||
fun resolveTrueOgPacketEventsJar(project: Project): File {
|
||||
val home = System.getProperty("user.home")
|
||||
val defaultDir = Paths.get(home, "eclipse-workspace", "PacketEvents", "spigot", "build", "libs")
|
||||
|
||||
// Every resolution path is reported in the failure message regardless of which got tried.
|
||||
val propValue = (project.findProperty("truogPacketEventsJar") as? String)?.takeIf { it.isNotBlank() } ?: "<unset>"
|
||||
val envValue = System.getenv("TRUEOG_PACKETEVENTS_JAR")?.takeIf { it.isNotBlank() } ?: "<unset>"
|
||||
val attempts = listOf(
|
||||
"Gradle property truogPacketEventsJar=$propValue",
|
||||
"Environment variable TRUEOG_PACKETEVENTS_JAR=$envValue",
|
||||
"Default glob $defaultDir/PacketEvents-*.jar (case-insensitive; falls back to packetevents-spigot-*.jar; latest by mtime)",
|
||||
)
|
||||
|
||||
val gradleProp = (project.findProperty("truogPacketEventsJar") as? String)?.trim()
|
||||
if (!gradleProp.isNullOrEmpty()) {
|
||||
val f = File(gradleProp)
|
||||
if (f.isFile) return f
|
||||
}
|
||||
|
||||
val envVar = System.getenv("TRUEOG_PACKETEVENTS_JAR")?.trim()
|
||||
if (!envVar.isNullOrEmpty()) {
|
||||
val f = File(envVar)
|
||||
if (f.isFile) return f
|
||||
}
|
||||
|
||||
if (Files.isDirectory(defaultDir)) {
|
||||
val nameRegex = Regex("(?i)(PacketEvents|packetevents-spigot|packetevents)-.*\\.jar")
|
||||
val match = Files.list(defaultDir).use { stream ->
|
||||
stream
|
||||
.filter { nameRegex.matches(it.fileName.toString()) }
|
||||
.sorted(Comparator.comparingLong<Path> { Files.getLastModifiedTime(it).toMillis() }.reversed())
|
||||
.findFirst()
|
||||
.orElse(null)
|
||||
}
|
||||
if (match != null) return match.toFile()
|
||||
}
|
||||
|
||||
throw GradleException(
|
||||
buildString {
|
||||
appendLine("True-OG PacketEvents JAR not found. Tried:")
|
||||
attempts.forEach { appendLine(" - $it") }
|
||||
appendLine("Provide one of:")
|
||||
appendLine(" ./gradlew -PtruogPacketEventsJar=/abs/path/to/PacketEvents.jar ...")
|
||||
appendLine(" TRUEOG_PACKETEVENTS_JAR=/abs/path/to/PacketEvents.jar ./gradlew ...")
|
||||
appendLine(" Build the fork at \${user.home}/eclipse-workspace/PacketEvents (default location).")
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
// Registers a Copy task that stages the resolved JAR into runDir/plugins/PacketEvents.jar. See context/refs/packetevents-provider.md for the consumer integration snippet.
|
||||
val registerStagePacketEventsTask: (Project, String, File) -> TaskProvider<Copy> = { p, taskName, runDir ->
|
||||
p.tasks.register(taskName, Copy::class.java) {
|
||||
group = "packetevents"
|
||||
description = "Stage the locally built True-OG PacketEvents JAR into plugins/."
|
||||
// Eager filesystem resolution at configuration time is incompatible with the config cache.
|
||||
notCompatibleWithConfigurationCache(
|
||||
"Resolves the JAR via filesystem inspection during configuration.",
|
||||
)
|
||||
val jar = resolveTrueOgPacketEventsJar(p)
|
||||
from(jar)
|
||||
into(runDir.resolve("plugins"))
|
||||
rename { "PacketEvents.jar" }
|
||||
}
|
||||
}
|
||||
|
||||
extra["resolveTrueOgPacketEventsJar"] = { p: Project -> resolveTrueOgPacketEventsJar(p) }
|
||||
extra["registerStagePacketEventsTask"] = registerStagePacketEventsTask
|
||||
@@ -1,5 +1,7 @@
|
||||
package ac.grim.grimac.platform.bukkit;
|
||||
|
||||
import ac.grim.grimac.GrimAPI;
|
||||
import ac.grim.grimac.platform.api.Platform;
|
||||
import ac.grim.grimac.platform.api.PlatformServer;
|
||||
import ac.grim.grimac.platform.api.sender.Sender;
|
||||
import io.github.retrooper.packetevents.util.SpigotReflectionUtil;
|
||||
@@ -7,6 +9,7 @@ import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
|
||||
|
||||
public class BukkitPlatformServer implements PlatformServer {
|
||||
|
||||
@Override
|
||||
@@ -32,6 +35,10 @@ public class BukkitPlatformServer implements PlatformServer {
|
||||
|
||||
@Override
|
||||
public double getTPS() {
|
||||
// Folia throws UnsupportedOperationException on calling getTPS(), there is no API for getting TPS on Folia
|
||||
if (GrimAPI.INSTANCE.getPlatform() == Platform.FOLIA) {
|
||||
return Double.NaN;
|
||||
}
|
||||
return SpigotReflectionUtil.getTPS();
|
||||
}
|
||||
}
|
||||
+116
-21
@@ -1,20 +1,25 @@
|
||||
package ac.grim.grimac.platform.bukkit;
|
||||
|
||||
import ac.grim.grimac.GrimAPI;
|
||||
import ac.grim.grimac.GrimExternalAPI;
|
||||
import ac.grim.grimac.api.GrimAPIProvider;
|
||||
import ac.grim.grimac.api.GrimAbstractAPI;
|
||||
import ac.grim.grimac.api.event.EventBus;
|
||||
import ac.grim.grimac.api.plugin.GrimPlugin;
|
||||
import ac.grim.grimac.events.GrimExtensionManager;
|
||||
import ac.grim.grimac.command.CloudCommandService;
|
||||
import ac.grim.grimac.internal.platform.bukkit.resolver.BukkitResolverRegistrar;
|
||||
import ac.grim.grimac.manager.init.Initable;
|
||||
import ac.grim.grimac.manager.init.load.PacketEventsInit;
|
||||
import ac.grim.grimac.manager.init.start.ExemptOnlinePlayersOnReload;
|
||||
import ac.grim.grimac.manager.init.start.StartableInitable;
|
||||
import ac.grim.grimac.platform.api.Platform;
|
||||
import ac.grim.grimac.platform.api.PlatformLoader;
|
||||
import ac.grim.grimac.platform.api.PlatformServer;
|
||||
import ac.grim.grimac.platform.api.command.CommandService;
|
||||
import ac.grim.grimac.platform.api.manager.ItemResetHandler;
|
||||
import ac.grim.grimac.platform.api.manager.MessagePlaceHolderManager;
|
||||
import ac.grim.grimac.platform.api.manager.CommandAdapter;
|
||||
import ac.grim.grimac.platform.api.manager.PlatformPluginManager;
|
||||
import ac.grim.grimac.platform.api.manager.cloud.CloudCommandAdapter;
|
||||
import ac.grim.grimac.platform.api.player.PlatformPlayerFactory;
|
||||
import ac.grim.grimac.platform.api.scheduler.PlatformScheduler;
|
||||
import ac.grim.grimac.platform.api.sender.Sender;
|
||||
@@ -28,11 +33,11 @@ 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.LogUtil;
|
||||
import ac.grim.grimac.utils.lazy.LazyHolder;
|
||||
import com.github.retrooper.packetevents.PacketEventsAPI;
|
||||
import io.github.retrooper.packetevents.factory.spigot.SpigotPacketEventsBuilder;
|
||||
@@ -47,7 +52,6 @@ import org.incendo.cloud.brigadier.CloudBrigadierManager;
|
||||
import org.incendo.cloud.bukkit.CloudBukkitCapabilities;
|
||||
import org.incendo.cloud.execution.ExecutionCoordinator;
|
||||
import org.incendo.cloud.paper.LegacyPaperCommandManager;
|
||||
import org.incendo.cloud.setting.Configurable;
|
||||
|
||||
|
||||
public final class GrimACBukkitLoaderPlugin extends JavaPlugin implements PlatformLoader {
|
||||
@@ -55,13 +59,19 @@ public final class GrimACBukkitLoaderPlugin extends JavaPlugin implements Platfo
|
||||
public static GrimACBukkitLoaderPlugin LOADER;
|
||||
|
||||
private final LazyHolder<PlatformScheduler> scheduler = LazyHolder.simple(this::createScheduler);
|
||||
private final LazyHolder<PacketEventsAPI<?>> packetEvents = LazyHolder.simple(() -> SpigotPacketEventsBuilder.build(this));
|
||||
private final LazyHolder<PacketEventsAPI<?>> packetEvents = LazyHolder.simple(() -> {
|
||||
// External mode reuses the installed provider's API (plugin.yml depend orders load before this).
|
||||
if (PacketEventsInit.isShadePE()) {
|
||||
return SpigotPacketEventsBuilder.build(this);
|
||||
}
|
||||
return com.github.retrooper.packetevents.PacketEvents.getAPI();
|
||||
});
|
||||
private final LazyHolder<BukkitSenderFactory> senderFactory = LazyHolder.simple(BukkitSenderFactory::new);
|
||||
private final LazyHolder<CommandManager<Sender>> commandManager = LazyHolder.simple(this::createCommandManager);
|
||||
private final LazyHolder<ItemResetHandler> itemResetHandler = LazyHolder.simple(BukkitItemResetHandler::new);
|
||||
private final LazyHolder<CommandService> commandService = LazyHolder.simple(this::createCommandService);
|
||||
private final CloudCommandAdapter commandAdapter = new BukkitParserDescriptorFactory();
|
||||
|
||||
@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();
|
||||
@@ -69,9 +79,8 @@ public final class GrimACBukkitLoaderPlugin extends JavaPlugin implements Platfo
|
||||
@Getter private final BukkitPermissionRegistrationManager permissionManager = new BukkitPermissionRegistrationManager();
|
||||
|
||||
public GrimACBukkitLoaderPlugin() {
|
||||
GrimExtensionManager extensionManager = GrimAPI.INSTANCE.getExtensionManager();
|
||||
BukkitResolverRegistrar registrar = new BukkitResolverRegistrar(extensionManager);
|
||||
registrar.registerAll();
|
||||
BukkitResolverRegistrar registrar = new BukkitResolverRegistrar();
|
||||
registrar.registerAll(GrimAPI.INSTANCE.getExtensionManager());
|
||||
this.plugin = registrar.resolvePlugin(this);
|
||||
}
|
||||
|
||||
@@ -116,13 +125,13 @@ public final class GrimACBukkitLoaderPlugin extends JavaPlugin implements Platfo
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandManager<Sender> getCommandManager() {
|
||||
return commandManager.get();
|
||||
public ItemResetHandler getItemResetHandler() {
|
||||
return itemResetHandler.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemResetHandler getItemResetHandler() {
|
||||
return itemResetHandler.get();
|
||||
public CommandService getCommandService() {
|
||||
return commandService.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -132,25 +141,111 @@ public final class GrimACBukkitLoaderPlugin extends JavaPlugin implements Platfo
|
||||
|
||||
@Override
|
||||
public void registerAPIService() {
|
||||
GrimAPIProvider.init(GrimAPI.INSTANCE.getExternalAPI());
|
||||
Bukkit.getServicesManager().register(GrimAbstractAPI.class, GrimAPI.INSTANCE.getExternalAPI(), GrimACBukkitLoaderPlugin.LOADER, ServicePriority.Normal);
|
||||
final GrimExternalAPI externalAPI = GrimAPI.INSTANCE.getExternalAPI();
|
||||
final EventBus eventBus = externalAPI.getEventBus();
|
||||
final ac.grim.grimac.api.plugin.GrimPlugin context = GrimAPI.INSTANCE.getGrimPlugin();
|
||||
|
||||
eventBus.subscribe(context, ac.grim.grimac.api.event.events.GrimJoinEvent.class, (event) -> {
|
||||
ac.grim.grimac.api.events.GrimJoinEvent bukkitEvent =
|
||||
new ac.grim.grimac.api.events.GrimJoinEvent(event.getUser());
|
||||
|
||||
Bukkit.getPluginManager().callEvent(bukkitEvent);
|
||||
});
|
||||
|
||||
eventBus.subscribe(context, ac.grim.grimac.api.event.events.GrimQuitEvent.class, (event) -> {
|
||||
ac.grim.grimac.api.events.GrimQuitEvent bukkitEvent =
|
||||
new ac.grim.grimac.api.events.GrimQuitEvent(event.getUser());
|
||||
|
||||
Bukkit.getPluginManager().callEvent(bukkitEvent);
|
||||
});
|
||||
|
||||
eventBus.subscribe(context, ac.grim.grimac.api.event.events.GrimReloadEvent.class, (event) -> {
|
||||
ac.grim.grimac.api.events.GrimReloadEvent bukkitEvent =
|
||||
new ac.grim.grimac.api.events.GrimReloadEvent(event.isSuccess());
|
||||
|
||||
Bukkit.getPluginManager().callEvent(bukkitEvent);
|
||||
});
|
||||
|
||||
eventBus.subscribe(context, ac.grim.grimac.api.event.events.FlagEvent.class, (event) -> {
|
||||
ac.grim.grimac.api.events.FlagEvent bukkitEvent =
|
||||
new ac.grim.grimac.api.events.FlagEvent(
|
||||
event.getUser(),
|
||||
event.getCheck(),
|
||||
event.getVerbose()
|
||||
);
|
||||
|
||||
Bukkit.getPluginManager().callEvent(bukkitEvent);
|
||||
|
||||
if (bukkitEvent.isCancelled()) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
});
|
||||
|
||||
eventBus.subscribe(context, ac.grim.grimac.api.event.events.CommandExecuteEvent.class, (event) -> {
|
||||
ac.grim.grimac.api.events.CommandExecuteEvent bukkitEvent =
|
||||
new ac.grim.grimac.api.events.CommandExecuteEvent(
|
||||
event.getUser(),
|
||||
event.getCheck(),
|
||||
event.getVerbose(),
|
||||
event.getCommand()
|
||||
);
|
||||
|
||||
Bukkit.getPluginManager().callEvent(bukkitEvent);
|
||||
|
||||
if (bukkitEvent.isCancelled()) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
});
|
||||
|
||||
eventBus.subscribe(context, ac.grim.grimac.api.event.events.CompletePredictionEvent.class, (event) -> {
|
||||
// Note: New event doesn't have verbose, passing null or check name is standard fallback
|
||||
ac.grim.grimac.api.events.CompletePredictionEvent bukkitEvent =
|
||||
new ac.grim.grimac.api.events.CompletePredictionEvent(
|
||||
event.getUser(),
|
||||
event.getCheck(),
|
||||
"",
|
||||
event.getOffset()
|
||||
);
|
||||
|
||||
Bukkit.getPluginManager().callEvent(bukkitEvent);
|
||||
|
||||
if (bukkitEvent.isCancelled()) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
});
|
||||
|
||||
GrimAPIProvider.init(externalAPI);
|
||||
Bukkit.getServicesManager().register(GrimAbstractAPI.class, externalAPI, GrimACBukkitLoaderPlugin.LOADER, ServicePriority.Normal);
|
||||
}
|
||||
|
||||
private PlatformScheduler createScheduler() {
|
||||
return GrimAPI.INSTANCE.getPlatform() == Platform.FOLIA ? new FoliaPlatformScheduler() : new BukkitPlatformScheduler();
|
||||
}
|
||||
|
||||
private CommandManager<Sender> createCommandManager() {
|
||||
private CommandService createCommandService() {
|
||||
try {
|
||||
return new CloudCommandService(this::createCloudCommandManager, commandAdapter);
|
||||
} catch (Throwable t) {
|
||||
LogUtil.warn("CRITICAL: Failed to initialize Command Framework. " +
|
||||
"Grim will continue to run with no commands.", t);
|
||||
return () -> {};
|
||||
}
|
||||
}
|
||||
|
||||
private CommandManager<Sender> createCloudCommandManager() {
|
||||
LegacyPaperCommandManager<Sender> manager = new LegacyPaperCommandManager<>(
|
||||
this,
|
||||
ExecutionCoordinator.simpleCoordinator(),
|
||||
senderFactory.get()
|
||||
);
|
||||
if (manager.hasCapability(CloudBukkitCapabilities.NATIVE_BRIGADIER)) {
|
||||
manager.registerBrigadier();
|
||||
CloudBrigadierManager<Sender, ?> cbm = manager.brigadierManager();
|
||||
Configurable<BrigadierSetting> settings = cbm.settings();
|
||||
settings.set(BrigadierSetting.FORCE_EXECUTABLE, true);
|
||||
try {
|
||||
manager.registerBrigadier();
|
||||
CloudBrigadierManager<Sender, ?> cbm = manager.brigadierManager();
|
||||
cbm.settings().set(BrigadierSetting.FORCE_EXECUTABLE, true);
|
||||
} catch (Throwable t) {
|
||||
LogUtil.error("Failed to register Brigadier native completions. Falling back to standard completions.", t);
|
||||
}
|
||||
} else if (manager.hasCapability(CloudBukkitCapabilities.ASYNCHRONOUS_COMPLETION)) {
|
||||
manager.registerAsynchronousCompletions();
|
||||
}
|
||||
|
||||
@@ -63,7 +63,6 @@ public class BukkitGrimEntity implements GrimEntity {
|
||||
if (bukkitPlatformWorld == null || !bukkitPlatformWorld.getBukkitWorld().equals(entity.getWorld())) {
|
||||
bukkitPlatformWorld = new BukkitPlatformWorld(entity.getWorld());
|
||||
}
|
||||
|
||||
return bukkitPlatformWorld;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,10 +4,8 @@ import ac.grim.grimac.GrimAPI;
|
||||
import ac.grim.grimac.platform.bukkit.utils.convert.BukkitConversionUtils;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
|
||||
import ac.grim.grimac.utils.data.PistonData;
|
||||
import ac.grim.grimac.utils.data.PistonTemplate;
|
||||
import com.github.retrooper.packetevents.protocol.world.BlockFace;
|
||||
import com.github.retrooper.packetevents.util.Vector3d;
|
||||
import com.github.retrooper.packetevents.util.Vector3i;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.event.EventHandler;
|
||||
@@ -28,10 +26,10 @@ public class PistonEvent implements Listener {
|
||||
private static final double MAX_VERTICAL_DISTANCE = 64.0;
|
||||
|
||||
// accuracy isn't that important, it's close enough and performant
|
||||
private static boolean isCloseEnough(Vector3i vectorA, Vector3d vectorB) {
|
||||
return Math.abs(vectorA.getX() - vectorB.getX()) <= MAX_HORIZONTAL_DISTANCE
|
||||
&& Math.abs(vectorA.getY() - vectorB.getY()) <= MAX_VERTICAL_DISTANCE
|
||||
&& Math.abs(vectorA.getZ() - vectorB.getZ()) <= MAX_HORIZONTAL_DISTANCE;
|
||||
private static boolean isCloseEnough(int x, int y, int z, double posX, double posY, double posZ) {
|
||||
return Math.abs(x - posX) <= MAX_HORIZONTAL_DISTANCE
|
||||
&& Math.abs(y - posY) <= MAX_VERTICAL_DISTANCE
|
||||
&& Math.abs(z - posZ) <= MAX_HORIZONTAL_DISTANCE;
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
@@ -68,18 +66,8 @@ 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 Vector3i sourcePos = new Vector3i(piston.getX(), piston.getY(), piston.getZ());
|
||||
|
||||
for (GrimPlayer player : GrimAPI.INSTANCE.getPlayerDataManager().getEntries()) {
|
||||
if (isCloseEnough(sourcePos, player.compensatedEntities.self.trackedServerPosition.getPos()) && player.compensatedWorld.isChunkLoaded(chunkX, chunkZ)) {
|
||||
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));
|
||||
}
|
||||
}
|
||||
PistonTemplate data = new PistonTemplate(BukkitConversionUtils.fromBukkitFace(event.getDirection()), boxes, true, hasSlimeBlock, hasHoneyBlock);
|
||||
addPistonData(data, piston.getX(), piston.getY(), piston.getZ());
|
||||
}
|
||||
|
||||
// For some unknown reason, bukkit handles this stupidly
|
||||
@@ -101,9 +89,8 @@ public class PistonEvent implements Listener {
|
||||
BlockFace face = BukkitConversionUtils.fromBukkitFace(event.getDirection());
|
||||
|
||||
// The event was called without blocks and is therefore in the right direction
|
||||
Block piston = event.getBlock();
|
||||
if (event.getBlocks().isEmpty()) {
|
||||
Block piston = event.getBlock();
|
||||
|
||||
// Add bounding box of the actual piston head pushing
|
||||
boxes.add(new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true)
|
||||
.offset(piston.getX() + face.getModX(),
|
||||
@@ -127,16 +114,19 @@ public class PistonEvent implements Listener {
|
||||
}
|
||||
}
|
||||
|
||||
final int chunkX = event.getBlock().getX() >> 4;
|
||||
final int chunkZ = event.getBlock().getZ() >> 4;
|
||||
Vector3i sourcePos = new Vector3i(event.getBlock().getX(), event.getBlock().getY(), event.getBlock().getZ());
|
||||
PistonTemplate data = new PistonTemplate(face, boxes, false, hasSlimeBlock, hasHoneyBlock);
|
||||
addPistonData(data, piston.getX(), piston.getY(), piston.getZ());
|
||||
}
|
||||
|
||||
private void addPistonData(PistonTemplate pistonTemplate, int blockX, int blockY, int blockZ) {
|
||||
for (GrimPlayer player : GrimAPI.INSTANCE.getPlayerDataManager().getEntries()) {
|
||||
if (isCloseEnough(sourcePos, player.compensatedEntities.self.trackedServerPosition.getPos()) && player.compensatedWorld.isChunkLoaded(chunkX, chunkZ)) {
|
||||
int lastTrans = player.lastTransactionSent.get();
|
||||
PistonData data = new PistonData(face, boxes, lastTrans, false, hasSlimeBlock, hasHoneyBlock);
|
||||
player.latencyUtils.addRealTimeTaskAsync(lastTrans, () -> player.compensatedWorld.activePistons.add(data));
|
||||
}
|
||||
if (player.compensatedWorld.isChunkLoaded(blockX >> 4, blockZ >> 4) || !isCloseEnough(blockX, blockY, blockZ, player.x, player.y, player.z)) continue;
|
||||
|
||||
int lastTrans = player.lastTransactionSent.get();
|
||||
|
||||
player.latencyUtils.addRealTimeTaskAsync(lastTrans, () -> {
|
||||
player.compensatedWorld.addPiston(pistonTemplate, lastTrans);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
+27
-2
@@ -8,6 +8,7 @@ import ac.grim.grimac.platform.bukkit.utils.reflection.PaperUtils;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.anticheat.LogUtil;
|
||||
import ac.grim.grimac.utils.lists.HookedListWrapper;
|
||||
import ac.grim.grimac.utils.reflection.ReflectionUtils;
|
||||
import com.github.retrooper.packetevents.PacketEvents;
|
||||
import com.github.retrooper.packetevents.manager.server.ServerVersion;
|
||||
import com.github.retrooper.packetevents.util.reflection.Reflection;
|
||||
@@ -25,6 +26,20 @@ import java.util.List;
|
||||
@SuppressWarnings(value = {"unchecked", "deprecated"})
|
||||
public class BukkitTickEndEvent extends AbstractTickEndEvent implements Listener {
|
||||
|
||||
private Boolean getLateBindState() {
|
||||
Class<?> spigotConfig = ReflectionUtils.getClass("org.spigotmc.SpigotConfig");
|
||||
// ReflectionUtils.getField(class, name) handles the loop and setAccessible
|
||||
Field field = ReflectionUtils.getField(spigotConfig, "lateBind");
|
||||
|
||||
if (field == null) return null;
|
||||
|
||||
try {
|
||||
return (boolean) field.get(null);
|
||||
} catch (Exception ignored) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
if (!super.shouldInjectEndTick()) {
|
||||
@@ -38,9 +53,18 @@ public class BukkitTickEndEvent extends AbstractTickEndEvent implements Listener
|
||||
PaperUtils.registerTickEndEvent(this, this::tickAllFoliaPlayers);
|
||||
return;
|
||||
}
|
||||
// if it fails to inject, try to use paper's event
|
||||
if (!injectWithReflection() && !PaperUtils.registerTickEndEvent(this, this::tickAllPlayers)) {
|
||||
// if it fails to register Paper event, try to inject via reflection
|
||||
if (!PaperUtils.registerTickEndEvent(this, this::tickAllPlayers) && !injectWithReflection()) {
|
||||
LogUtil.error("Failed to inject into the end of tick event!");
|
||||
|
||||
if (PacketEvents.getAPI().getServerManager().getVersion().isOlderThan(ServerVersion.V_1_14_4)) {
|
||||
Boolean lateBind = getLateBindState();
|
||||
if (lateBind == null) {
|
||||
LogUtil.error("Failed to determine the late-bind state. Perhaps you are using a custom server fork? Check the fork configuration for a late-bind option and disable it.");
|
||||
} else if (lateBind) {
|
||||
LogUtil.error("Injection failed because the late-bind option is enabled. Disable it in spigot.yml.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,6 +89,7 @@ public class BukkitTickEndEvent extends AbstractTickEndEvent implements Listener
|
||||
// Inject so we can add the final transaction pre-flush event
|
||||
try {
|
||||
Object connection = SpigotReflectionUtil.getMinecraftServerConnectionInstance();
|
||||
if (connection == null) return false;
|
||||
|
||||
Field connectionsList = Reflection.getField(connection.getClass(), List.class, 1);
|
||||
List<Object> endOfTickObject = (List<Object>) connectionsList.get(connection);
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
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.cloud.CloudCommandAdapter;
|
||||
import ac.grim.grimac.platform.api.sender.Sender;
|
||||
import ac.grim.grimac.platform.bukkit.command.BukkitPlayerSelectorParser;
|
||||
import org.bukkit.Bukkit;
|
||||
@@ -16,7 +16,7 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class BukkitParserDescriptorFactory implements CommandAdapter {
|
||||
public class BukkitParserDescriptorFactory implements CloudCommandAdapter {
|
||||
|
||||
// Parser is kept as a field because its stateless
|
||||
private final BukkitPlayerSelectorParser<Sender> bukkitPlayerSelectorParser = new BukkitPlayerSelectorParser<>();
|
||||
|
||||
+8
-4
@@ -1,6 +1,7 @@
|
||||
package ac.grim.grimac.platform.bukkit.player;
|
||||
|
||||
import ac.grim.grimac.platform.api.entity.GrimEntity;
|
||||
import ac.grim.grimac.platform.api.player.BlockTranslator;
|
||||
import ac.grim.grimac.platform.api.player.PlatformInventory;
|
||||
import ac.grim.grimac.platform.api.player.PlatformPlayer;
|
||||
import ac.grim.grimac.platform.api.sender.Sender;
|
||||
@@ -17,7 +18,6 @@ import com.github.retrooper.packetevents.protocol.player.User;
|
||||
import com.github.retrooper.packetevents.util.Vector3d;
|
||||
import io.github.retrooper.packetevents.util.SpigotConversionUtil;
|
||||
import lombok.Getter;
|
||||
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.entity.Player;
|
||||
@@ -31,8 +31,6 @@ 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
|
||||
@@ -91,7 +89,8 @@ public class BukkitPlatformPlayer extends BukkitGrimEntity implements PlatformPl
|
||||
if (CommonGrimArguments.USE_CHAT_FAST_BYPASS.value() && user != null) {
|
||||
user.sendMessage(message);
|
||||
} else {
|
||||
audiences.player(bukkitPlayer).sendMessage(message);
|
||||
// Paper/Purpur Player is natively an Adventure Audience, so no platform shim is needed
|
||||
bukkitPlayer.sendMessage(message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,6 +169,11 @@ public class BukkitPlatformPlayer extends BukkitGrimEntity implements PlatformPl
|
||||
return GrimACBukkitLoaderPlugin.LOADER.getBukkitSenderFactory().map(this.bukkitPlayer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockTranslator getBlockTranslator() {
|
||||
return BlockTranslator.IDENTITY;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public Player getNative() {
|
||||
|
||||
+11
@@ -8,6 +8,7 @@ import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.UUID;
|
||||
|
||||
@@ -54,4 +55,14 @@ public class BukkitPlatformPlayerFactory extends AbstractPlatformPlayerFactory<P
|
||||
OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(name);
|
||||
return new BukkitOfflinePlatformPlayer(offlinePlayer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<OfflinePlatformPlayer> getOfflinePlayers() {
|
||||
OfflinePlayer[] offlinePlayers = Bukkit.getOfflinePlayers();
|
||||
BukkitOfflinePlatformPlayer[] offlinePlatformPlayers = new BukkitOfflinePlatformPlayer[offlinePlayers.length];
|
||||
for (int i = 0; i < offlinePlayers.length; i++) {
|
||||
offlinePlatformPlayers[i] = new BukkitOfflinePlatformPlayer(offlinePlayers[i]);
|
||||
}
|
||||
return Arrays.asList(offlinePlatformPlayers);
|
||||
}
|
||||
}
|
||||
-111
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -3,8 +3,6 @@ package ac.grim.grimac.platform.bukkit.sender;
|
||||
import ac.grim.grimac.GrimAPI;
|
||||
import ac.grim.grimac.platform.api.sender.Sender;
|
||||
import ac.grim.grimac.platform.api.sender.SenderFactory;
|
||||
import ac.grim.grimac.platform.bukkit.GrimACBukkitLoaderPlugin;
|
||||
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.ConsoleCommandSender;
|
||||
@@ -18,11 +16,6 @@ import org.jetbrains.annotations.NotNull;
|
||||
import java.util.UUID;
|
||||
|
||||
public class BukkitSenderFactory extends SenderFactory<CommandSender> implements SenderMapper<CommandSender, Sender> {
|
||||
private final BukkitAudiences audiences;
|
||||
|
||||
public BukkitSenderFactory() {
|
||||
this.audiences = BukkitAudiences.create(GrimACBukkitLoaderPlugin.LOADER);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getName(CommandSender sender) {
|
||||
@@ -42,12 +35,13 @@ public class BukkitSenderFactory extends SenderFactory<CommandSender> implements
|
||||
@Override
|
||||
protected void sendMessage(CommandSender sender, Component message) {
|
||||
// we can safely send async for players and the console - otherwise, send it sync
|
||||
// Paper/Purpur CommandSender is natively an Adventure Audience, so no platform shim is needed
|
||||
if (sender instanceof Player || sender instanceof ConsoleCommandSender || sender instanceof RemoteConsoleCommandSender) {
|
||||
this.audiences.sender(sender).sendMessage(message);
|
||||
sender.sendMessage(message);
|
||||
} else {
|
||||
GrimAPI.INSTANCE.getScheduler().getGlobalRegionScheduler().run(
|
||||
GrimAPI.INSTANCE.getGrimPlugin(),
|
||||
() -> this.audiences.sender(sender).sendMessage(message)
|
||||
() -> sender.sendMessage(message)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+31
-50
@@ -1,75 +1,52 @@
|
||||
import versioning.BuildConfig
|
||||
|
||||
plugins {
|
||||
eclipse
|
||||
`maven-publish`
|
||||
grim.`base-conventions`
|
||||
}
|
||||
|
||||
repositories {
|
||||
if (BuildConfig.mavenLocalOverride) {
|
||||
mavenLocal()
|
||||
// We still call mavenLocal() conditionally at the top for non-exclusive deps (general fallback)
|
||||
if (BuildConfig.mavenLocalOverride) mavenLocal()
|
||||
|
||||
// Grim API
|
||||
exclusive("https://repo.grim.ac/snapshots") {
|
||||
includeGroup("ac.grim.grimac")
|
||||
}
|
||||
|
||||
exclusiveContent {
|
||||
forRepository {
|
||||
maven("https://repo.grim.ac/snapshots") // Grim API
|
||||
}
|
||||
filter {
|
||||
includeGroup("ac.grim.grimac")
|
||||
includeGroup("com.github.retrooper")
|
||||
}
|
||||
// PacketEvents
|
||||
exclusive("https://repo.codemc.io/repository/maven-releases/", { mavenContent { releasesOnly() } }) {
|
||||
includeGroup("com.github.retrooper")
|
||||
}
|
||||
|
||||
exclusiveContent {
|
||||
forRepository {
|
||||
maven("https://repo.viaversion.com") { // ViaVersion
|
||||
mavenContent { releasesOnly() }
|
||||
}
|
||||
}
|
||||
filter {
|
||||
includeGroup("com.viaversion")
|
||||
}
|
||||
// ViaVersion
|
||||
exclusive("https://repo.viaversion.com", { mavenContent { releasesOnly() } }) {
|
||||
includeGroup("com.viaversion")
|
||||
}
|
||||
|
||||
exclusiveContent {
|
||||
forRepository {
|
||||
maven("https://nexus.scarsz.me/content/repositories/releases") { // Configuralize
|
||||
mavenContent { releasesOnly() }
|
||||
}
|
||||
}
|
||||
filter {
|
||||
includeGroup("github.scarsz")
|
||||
}
|
||||
// Configuralize
|
||||
exclusive("https://nexus.scarsz.me/content/repositories/releases", { mavenContent { releasesOnly() } }) {
|
||||
includeGroup("github.scarsz")
|
||||
}
|
||||
|
||||
exclusiveContent {
|
||||
forRepository {
|
||||
maven("https://repo.opencollab.dev/maven-releases/") { // Cumulus (for Floodgate)
|
||||
mavenContent { releasesOnly() }
|
||||
}
|
||||
}
|
||||
filter {
|
||||
includeGroup("org.geysermc.api")
|
||||
}
|
||||
// Cumulus
|
||||
exclusive("https://repo.opencollab.dev/maven-releases/", { mavenContent { releasesOnly() } }) {
|
||||
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")
|
||||
}
|
||||
// Floodgate
|
||||
exclusive("https://repo.opencollab.dev/maven-snapshots/", { mavenContent { snapshotsOnly() } }) {
|
||||
includeGroup("org.geysermc.floodgate")
|
||||
includeGroup("org.geysermc.cumulus")
|
||||
includeModule("org.geysermc", "common")
|
||||
includeModule("org.geysermc", "geyser-parent")
|
||||
}
|
||||
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
|
||||
dependencies {
|
||||
if (BuildConfig.shadePE) {
|
||||
api(libs.packetevents.api)
|
||||
@@ -87,11 +64,15 @@ dependencies {
|
||||
// Bump snakeyaml (transitive dep of configuralize) 1.29 -> 2.2+ for geyser-fabric
|
||||
api(libs.snakeyaml)
|
||||
api(libs.fastutil)
|
||||
api(libs.adventure.text.minimessage)
|
||||
// Provided by the server at runtime; bundling it would shadow the platform's own Adventure
|
||||
compileOnly(libs.adventure.text.minimessage)
|
||||
api(libs.jetbrains.annotations)
|
||||
api(libs.hikaricp)
|
||||
api(libs.postgresql) // JDBC driver for /grim history postgresql backend
|
||||
|
||||
api(libs.grim.api)
|
||||
api(libs.grim.internal)
|
||||
compileOnly(libs.grim.internal.shims)
|
||||
|
||||
compileOnly(libs.geyser.base.api) {
|
||||
isTransitive = false // messes with guava otherwise
|
||||
|
||||
@@ -2,8 +2,8 @@ package ac.grim.grimac;
|
||||
|
||||
import ac.grim.grimac.api.event.EventBus;
|
||||
import ac.grim.grimac.api.plugin.GrimPlugin;
|
||||
import ac.grim.grimac.events.GrimExtensionManager;
|
||||
import ac.grim.grimac.events.OptimizedEventBus;
|
||||
import ac.grim.grimac.internal.plugin.resolver.GrimExtensionManager;
|
||||
import ac.grim.grimac.internal.event.OptimizedEventBus;
|
||||
import ac.grim.grimac.manager.AlertManagerImpl;
|
||||
import ac.grim.grimac.manager.DiscordManager;
|
||||
import ac.grim.grimac.manager.InitManager;
|
||||
@@ -15,23 +15,20 @@ import ac.grim.grimac.manager.violationdatabase.ViolationDatabaseManager;
|
||||
import ac.grim.grimac.platform.api.Platform;
|
||||
import ac.grim.grimac.platform.api.PlatformLoader;
|
||||
import ac.grim.grimac.platform.api.PlatformServer;
|
||||
import ac.grim.grimac.platform.api.command.CommandService;
|
||||
import ac.grim.grimac.platform.api.manager.ItemResetHandler;
|
||||
import ac.grim.grimac.platform.api.manager.MessagePlaceHolderManager;
|
||||
import ac.grim.grimac.platform.api.manager.CommandAdapter;
|
||||
import ac.grim.grimac.platform.api.manager.PermissionRegistrationManager;
|
||||
import ac.grim.grimac.platform.api.manager.PlatformPluginManager;
|
||||
import ac.grim.grimac.platform.api.player.PlatformPlayerFactory;
|
||||
import ac.grim.grimac.platform.api.scheduler.PlatformScheduler;
|
||||
import ac.grim.grimac.platform.api.sender.Sender;
|
||||
import ac.grim.grimac.platform.api.sender.SenderFactory;
|
||||
import ac.grim.grimac.utils.anticheat.PlayerDataManager;
|
||||
import ac.grim.grimac.utils.common.arguments.CommonGrimArguments;
|
||||
import ac.grim.grimac.utils.reflection.ReflectionUtils;
|
||||
import lombok.Getter;
|
||||
import org.incendo.cloud.CommandManager;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
|
||||
@Getter
|
||||
public final class GrimAPI {
|
||||
public static final GrimAPI INSTANCE = new GrimAPI();
|
||||
@@ -78,7 +75,7 @@ public final class GrimAPI {
|
||||
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 = new InitManager(loader.getPacketEvents(), platformSpecificInitables);
|
||||
this.initManager.load();
|
||||
this.initialized = true;
|
||||
}
|
||||
@@ -101,10 +98,6 @@ public final class GrimAPI {
|
||||
return loader.getPlatformPlayerFactory();
|
||||
}
|
||||
|
||||
public CommandAdapter getCommandAdapter() {
|
||||
return loader.getCommandAdapter();
|
||||
}
|
||||
|
||||
public GrimPlugin getGrimPlugin() {
|
||||
return loader.getPlugin();
|
||||
}
|
||||
@@ -129,8 +122,8 @@ public final class GrimAPI {
|
||||
return loader.getMessagePlaceHolderManager();
|
||||
}
|
||||
|
||||
public CommandManager<Sender> getCommandManager() {
|
||||
return loader.getCommandManager();
|
||||
public CommandService getCommandService() {
|
||||
return loader.getCommandService();
|
||||
}
|
||||
|
||||
private void checkInitialized() {
|
||||
|
||||
@@ -6,6 +6,7 @@ import ac.grim.grimac.api.alerts.AlertManager;
|
||||
import ac.grim.grimac.api.config.ConfigManager;
|
||||
import ac.grim.grimac.api.event.EventBus;
|
||||
import ac.grim.grimac.api.event.events.GrimReloadEvent;
|
||||
import ac.grim.grimac.api.plugin.GrimPlugin;
|
||||
import ac.grim.grimac.manager.config.ConfigManagerFileImpl;
|
||||
import ac.grim.grimac.manager.init.start.StartableInitable;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
@@ -13,6 +14,7 @@ import ac.grim.grimac.utils.anticheat.LogUtil;
|
||||
import ac.grim.grimac.utils.anticheat.MessageUtil;
|
||||
import ac.grim.grimac.utils.common.ConfigReloadObserver;
|
||||
import lombok.Getter;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -45,6 +47,11 @@ public class GrimExternalAPI implements GrimAbstractAPI, ConfigReloadObserver, S
|
||||
return api.getEventBus();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable GrimUser getGrimUser(Player player) {
|
||||
return getGrimUser(player.getUniqueId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable GrimUser getGrimUser(UUID uuid) {
|
||||
return api.getPlayerDataManager().getPlayer(uuid);
|
||||
@@ -107,6 +114,11 @@ public class GrimExternalAPI implements GrimAbstractAPI, ConfigReloadObserver, S
|
||||
return GrimAPI.INSTANCE.getTickManager().currentTick;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull GrimPlugin getGrimPlugin(@NotNull Object o) {
|
||||
return this.api.getExtensionManager().getPlugin(o);
|
||||
}
|
||||
|
||||
// on load, load the config & register the service
|
||||
public void load() {
|
||||
reload(configManagerFile);
|
||||
|
||||
@@ -8,6 +8,7 @@ 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.protocol.player.DiggingAction;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -60,7 +61,11 @@ public class Check extends GrimProcessor implements AbstractCheck {
|
||||
}
|
||||
|
||||
public boolean shouldModifyPackets() {
|
||||
return isEnabled && !player.disableGrim && !player.noModifyPacketPermission && !exemptPermission;
|
||||
return isEnabled
|
||||
&& !player.disableGrim
|
||||
&& !player.noModifyPacketPermission
|
||||
&& !noModifyPacketPermission
|
||||
&& !exemptPermission;
|
||||
}
|
||||
|
||||
public final void updatePermissions() {
|
||||
@@ -171,7 +176,8 @@ public class Check extends GrimProcessor implements AbstractCheck {
|
||||
|
||||
public static boolean isAsync(PacketTypeCommon packetType) {
|
||||
return packetType == PacketType.Play.Client.KEEP_ALIVE
|
||||
|| packetType == PacketType.Play.Client.CHUNK_BATCH_ACK;
|
||||
|| packetType == PacketType.Play.Client.CHUNK_BATCH_ACK
|
||||
|| packetType == PacketType.Play.Client.RESOURCE_PACK_STATUS;
|
||||
}
|
||||
|
||||
public boolean isUpdate(PacketTypeCommon packetType) {
|
||||
@@ -203,4 +209,10 @@ public class Check extends GrimProcessor implements AbstractCheck {
|
||||
return isFlying(packetType);
|
||||
}
|
||||
|
||||
// prevent causing exploits with packet cancelling (ie noslow)
|
||||
public boolean canCancel(DiggingAction action) {
|
||||
return action != DiggingAction.RELEASE_USE_ITEM
|
||||
// we check client version here because 1.8- doesn't predict dropping items, so we can cancel them. (see CompensatedInventory)
|
||||
&& (action != DiggingAction.DROP_ITEM && action != DiggingAction.DROP_ITEM_STACK || player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8));
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@ public abstract class AbstractDebugHandler extends Check {
|
||||
super(player);
|
||||
}
|
||||
|
||||
public abstract void toggleListener(GrimPlayer player);
|
||||
public abstract boolean toggleListener(GrimPlayer player);
|
||||
|
||||
public abstract boolean toggleConsoleOutput();
|
||||
}
|
||||
@@ -0,0 +1,209 @@
|
||||
package ac.grim.grimac.checks.debug;
|
||||
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.collisions.datatypes.CollisionBox;
|
||||
import ac.grim.grimac.utils.collisions.datatypes.NoCollisionBox;
|
||||
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
|
||||
import ac.grim.grimac.utils.data.Pair;
|
||||
import ac.grim.grimac.utils.math.Vector3dm;
|
||||
import com.github.retrooper.packetevents.netty.buffer.ByteBufHelper;
|
||||
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerPluginMessage;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
|
||||
/**
|
||||
* Handles debug visualization of hitboxes and reach calculations for GrimAC.
|
||||
* Sends debug data to clients through plugin messages that can be visualized by compatible clients.
|
||||
*/
|
||||
public class HitboxDebugHandler extends AbstractDebugHandler {
|
||||
|
||||
/**
|
||||
* Set of players currently listening to hitbox debug data
|
||||
*/
|
||||
private final Set<GrimPlayer> listeners = new CopyOnWriteArraySet<>(new HashSet<>());
|
||||
|
||||
/**
|
||||
* Creates a new HitboxDebugHandler for the specified player
|
||||
*
|
||||
* @param grimPlayer The GrimAC player instance to debug
|
||||
*/
|
||||
public HitboxDebugHandler(GrimPlayer grimPlayer) {
|
||||
super(grimPlayer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles whether a player receives hitbox debug visualization data.
|
||||
* If the player is already listening, they will be removed. If not, they will be added.
|
||||
*
|
||||
* @param player The player to toggle debug visualization for
|
||||
* @return {@code true} if the player is now listening (was added),
|
||||
* {@code false} if the player is no longer listening (was removed).
|
||||
*/
|
||||
@Override
|
||||
public boolean toggleListener(GrimPlayer player) {
|
||||
boolean wasPresent = listeners.remove(player);
|
||||
|
||||
if (wasPresent) {
|
||||
return false;
|
||||
} else {
|
||||
listeners.add(player);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean toggleConsoleOutput() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends debug visualization data for reach calculations to all listening players.
|
||||
* The data includes hitboxes, target entities, look vectors, and eye heights used in reach calculations.
|
||||
*
|
||||
* @param hitboxes Map of entity IDs to their collision boxes
|
||||
* @param targetEntities Set of entity IDs that are considered targets
|
||||
* @param lookVecsAndEyeHeights List of pairs containing look vectors and their corresponding eye heights
|
||||
* @param baseX Base x coordinate before eye height adjustments
|
||||
* @param baseY Base y coordinate before eye height adjustments
|
||||
* @param baseZ Base z coordinate before eye height adjustments
|
||||
* @param isPrediction Whether these hitboxes are from a prediction calculation
|
||||
*
|
||||
* Packet Format (Version 1):
|
||||
* - Byte: Version (0)
|
||||
* - Byte: Global flags
|
||||
* - Bit 0: isPrediction
|
||||
* - Bits 1-7: Reserved
|
||||
* - Double: Player reach/interact distance
|
||||
* - Vector3d: Base position (3 doubles)
|
||||
* - VarInt: Number of ray traces
|
||||
* - For each ray trace:
|
||||
* - Double: Eye height delta
|
||||
* - Vector3d: Look vector (3 doubles)
|
||||
* - VarInt: Number of hitboxes
|
||||
* - For each hitbox:
|
||||
* - VarInt: Entity ID
|
||||
* - Byte: Flags
|
||||
* - Bit 0: Is target entity
|
||||
* - Bit 1: Is no collision
|
||||
* - Bits 2-7: Reserved
|
||||
* - If not NoCollisionBox:
|
||||
* - Double: minX
|
||||
* - Double: minY
|
||||
* - Double: minZ
|
||||
* - Double: maxX
|
||||
* - Double: maxY
|
||||
* - Double: maxZ
|
||||
*/
|
||||
public void sendHitboxData(Map<Integer, CollisionBox> hitboxes, Set<Integer> targetEntities,
|
||||
List<Pair<Vector3dm, Double>> lookVecsAndEyeHeights, double baseX, double baseY, double baseZ,
|
||||
boolean isPrediction, double reachDistance) {
|
||||
if (!isEnabled()) return;
|
||||
|
||||
ByteBuf buffer = Unpooled.buffer();
|
||||
try {
|
||||
// Version Header
|
||||
buffer.writeByte(1);
|
||||
|
||||
// Global Flags Header
|
||||
// Pack boolean flags into a single byte
|
||||
byte global_flags = 0;
|
||||
global_flags |= (isPrediction ? 1 : 0); // Bit 0: are the hitboxes from a prediction?
|
||||
// Bits 2-7 reserved for future use
|
||||
buffer.writeByte(global_flags);
|
||||
|
||||
// Write reach distance
|
||||
buffer.writeDouble(reachDistance);
|
||||
|
||||
// Write base position
|
||||
writeVector(buffer, baseX, baseY, baseZ);
|
||||
|
||||
// Write number of ray traces
|
||||
ByteBufHelper.writeVarInt(buffer, lookVecsAndEyeHeights.size());
|
||||
|
||||
// Write all possible ray traces
|
||||
for (Pair<Vector3dm, Double> pair : lookVecsAndEyeHeights) {
|
||||
Vector3dm lookVec = pair.first();
|
||||
double eyeHeight = pair.second();
|
||||
|
||||
// Write eye height delta and look vector
|
||||
// we make them 3 meters shorter because all of the vectors passed into here are made 3 meters longer
|
||||
// then they need to be so grim can report correct reach distance for too far away hits
|
||||
buffer.writeDouble(eyeHeight);
|
||||
writeVector(buffer, lookVec.getX(), lookVec.getY(), lookVec.getZ());
|
||||
}
|
||||
|
||||
// Write number of hitboxes
|
||||
ByteBufHelper.writeVarInt(buffer, hitboxes.size());
|
||||
|
||||
// Write hitbox data
|
||||
for (Map.Entry<Integer, CollisionBox> entry : hitboxes.entrySet()) {
|
||||
int entityId = entry.getKey();
|
||||
CollisionBox box = entry.getValue();
|
||||
|
||||
// Write entity ID
|
||||
ByteBufHelper.writeVarInt(buffer, entityId);
|
||||
|
||||
// Pack boolean flags into a single byte
|
||||
byte flags = 0;
|
||||
flags |= (targetEntities.contains(entityId) ? 1 : 0); // Bit 0: Is target
|
||||
flags |= (box == NoCollisionBox.INSTANCE ? 2 : 0); // Bit 1: Is no collision
|
||||
// Bits 2-7 reserved for future use
|
||||
buffer.writeByte(flags);
|
||||
|
||||
// Write box coordinates if it's not a NoCollisionBox
|
||||
if ((flags & 2) == 0) {
|
||||
SimpleCollisionBox simpleCollisionBox = (SimpleCollisionBox) box;
|
||||
buffer.writeDouble(simpleCollisionBox.minX);
|
||||
buffer.writeDouble(simpleCollisionBox.minY);
|
||||
buffer.writeDouble(simpleCollisionBox.minZ);
|
||||
buffer.writeDouble(simpleCollisionBox.maxX);
|
||||
buffer.writeDouble(simpleCollisionBox.maxY);
|
||||
buffer.writeDouble(simpleCollisionBox.maxZ);
|
||||
}
|
||||
}
|
||||
|
||||
// Convert buffer to byte array
|
||||
byte[] data = new byte[buffer.readableBytes()];
|
||||
buffer.readBytes(data);
|
||||
|
||||
// Create and send packet
|
||||
WrapperPlayServerPluginMessage packet = new WrapperPlayServerPluginMessage(
|
||||
"grim:debug_hitbox",
|
||||
data
|
||||
);
|
||||
|
||||
// Send to all listeners
|
||||
for (GrimPlayer listener : listeners) {
|
||||
if (listener != null) {
|
||||
listener.user.sendPacket(packet);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
// Release buffer to prevent memory leaks
|
||||
buffer.release();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return !listeners.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to write vector coordinates to the ByteBuf
|
||||
* @param buffer The buffer to write to
|
||||
* @param x The x coordinate to write
|
||||
* @param y The y coordinate to write
|
||||
* @param z The z coordinate to write
|
||||
*/
|
||||
private void writeVector(ByteBuf buffer, double x, double y, double z) {
|
||||
buffer.writeDouble(x);
|
||||
buffer.writeDouble(y);
|
||||
buffer.writeDouble(z);
|
||||
}
|
||||
}
|
||||
@@ -6,26 +6,21 @@ import ac.grim.grimac.checks.type.PacketCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.protocol.player.GameMode;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientInteractEntity;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientEntityAction;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@CheckData(name = "BadPacketsC", description = "Interacted with self")
|
||||
@CheckData(name = "BadPacketsC", description = "Tried to wake up while not sleeping", experimental = true)
|
||||
public class BadPacketsC extends Check implements PacketCheck {
|
||||
public BadPacketsC(GrimPlayer player) {
|
||||
public BadPacketsC(@NotNull GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
if (event.getPacketType() == PacketType.Play.Client.INTERACT_ENTITY) {
|
||||
if (player.gamemode == GameMode.SPECTATOR) return;
|
||||
if (new WrapperPlayClientInteractEntity(event).getEntityId() == player.entityID) {
|
||||
// Instant ban
|
||||
if (flagAndAlert() && shouldModifyPackets()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
}
|
||||
if (event.getPacketType() == PacketType.Play.Client.ENTITY_ACTION
|
||||
&& new WrapperPlayClientEntityAction(event).getAction() == WrapperPlayClientEntityAction.Action.LEAVE_BED
|
||||
&& !player.isInBed) {
|
||||
flagAndAlert();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,6 @@ 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;
|
||||
@@ -26,7 +25,7 @@ public class BadPacketsJ extends Check implements PacketCheck {
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
if (player.gamemode == GameMode.SPECTATOR) {
|
||||
if (!player.cameraEntity.isSelf()) {
|
||||
rotations.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ public class BadPacketsL extends Check implements PacketCheck {
|
||||
+ ", face=" + packet.getBlockFace()
|
||||
+ ", sequence=" + packet.getSequence()
|
||||
+ ", action=" + packet.getAction().toString().toLowerCase(Locale.ROOT)
|
||||
) && shouldModifyPackets() && packet.getAction() != DiggingAction.RELEASE_USE_ITEM) {
|
||||
) && shouldModifyPackets() && canCancel(packet.getAction())) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
|
||||
@@ -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", description = "Invalid window click packet", experimental = true)
|
||||
public class BadPacketsP extends Check implements PacketCheck {
|
||||
|
||||
private int containerType = -1;
|
||||
|
||||
@@ -6,7 +6,6 @@ import ac.grim.grimac.checks.type.PacketCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.protocol.player.GameMode;
|
||||
|
||||
@CheckData(name = "BadPacketsR", decay = 0.25, experimental = true)
|
||||
public class BadPacketsR extends Check implements PacketCheck {
|
||||
@@ -25,7 +24,7 @@ public class BadPacketsR extends Check implements PacketCheck {
|
||||
long ms = (player.getPlayerClockAtLeast() - clock) / 1000000L;
|
||||
long diff = (System.currentTimeMillis() - lastTransTime);
|
||||
if (diff > 2000 && ms > 2000) {
|
||||
if (positions == 0 && clock != 0 && player.gamemode != GameMode.SPECTATOR && !player.compensatedEntities.self.isDead) {
|
||||
if (positions == 0 && clock != 0 && player.cameraEntity.isSelf() && !player.compensatedEntities.self.isDead) {
|
||||
flag("time=" + ms + "ms, " + "lst=" + diff + "ms, positions=" + positions);
|
||||
} else {
|
||||
reward();
|
||||
|
||||
@@ -10,6 +10,7 @@ import com.github.retrooper.packetevents.protocol.attribute.Attributes;
|
||||
import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import com.github.retrooper.packetevents.util.Vector3f;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientInteractEntity;
|
||||
|
||||
@CheckData(name = "BadPacketsT")
|
||||
@@ -35,37 +36,39 @@ public class BadPacketsT extends Check implements PacketCheck {
|
||||
if (event.getPacketType().equals(PacketType.Play.Client.INTERACT_ENTITY)) {
|
||||
final WrapperPlayClientInteractEntity wrapper = new WrapperPlayClientInteractEntity(event);
|
||||
// Only INTERACT_AT actually has an interaction vector
|
||||
wrapper.getTarget().ifPresent(targetVector -> {
|
||||
final PacketEntity packetEntity = player.compensatedEntities.getEntity(wrapper.getEntityId());
|
||||
// Don't continue if the compensated entity hasn't been resolved
|
||||
if (packetEntity == null) {
|
||||
return;
|
||||
}
|
||||
if (wrapper.getAction() != WrapperPlayClientInteractEntity.InteractAction.INTERACT_AT) return;
|
||||
Vector3f targetVector = wrapper.getTarget().orElse(null);
|
||||
if (targetVector == null) return; // shouldn't ever happen, but whatever
|
||||
|
||||
// Make sure our target entity is actually a player (Player NPCs work too)
|
||||
if (!EntityTypes.PLAYER.equals(packetEntity.type)) {
|
||||
// We can't check for any entity that is not a player
|
||||
return;
|
||||
}
|
||||
final PacketEntity packetEntity = player.compensatedEntities.getEntity(wrapper.getEntityId());
|
||||
// Don't continue if the compensated entity hasn't been resolved
|
||||
if (packetEntity == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Perform the interaction vector check
|
||||
// TODO:
|
||||
// 27/12/2023 - Dynamic values for more than just one entity type?
|
||||
// 28/12/2023 - Player-only is fine
|
||||
// 30/12/2023 - Expansions differ in 1.9+
|
||||
final float scale = (float) packetEntity.getAttributeValue(Attributes.SCALE);
|
||||
if (targetVector.y > (minVerticalDisplacement * scale) && targetVector.y < (maxVerticalDisplacement * scale)
|
||||
&& Math.abs(targetVector.x) < (maxHorizontalDisplacement * scale)
|
||||
&& Math.abs(targetVector.z) < (maxHorizontalDisplacement * scale)) {
|
||||
return;
|
||||
}
|
||||
// Make sure our target entity is actually a player (Player NPCs work too)
|
||||
if (!EntityTypes.PLAYER.equals(packetEntity.type)) {
|
||||
// We can't check for any entity that is not a player
|
||||
return;
|
||||
}
|
||||
|
||||
// Log the vector
|
||||
final String verbose = String.format("%.5f/%.5f/%.5f",
|
||||
targetVector.x, targetVector.y, targetVector.z);
|
||||
// We could pretty much ban the player at this point
|
||||
flagAndAlert(verbose);
|
||||
});
|
||||
// Perform the interaction vector check
|
||||
// TODO:
|
||||
// 27/12/2023 - Dynamic values for more than just one entity type?
|
||||
// 28/12/2023 - Player-only is fine
|
||||
// 30/12/2023 - Expansions differ in 1.9+
|
||||
final float scale = (float) packetEntity.getAttributeValue(Attributes.SCALE);
|
||||
if (targetVector.y > (minVerticalDisplacement * scale) && targetVector.y < (maxVerticalDisplacement * scale)
|
||||
&& Math.abs(targetVector.x) < (maxHorizontalDisplacement * scale)
|
||||
&& Math.abs(targetVector.z) < (maxHorizontalDisplacement * scale)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Log the vector
|
||||
final String verbose = String.format("%.5f/%.5f/%.5f",
|
||||
targetVector.x, targetVector.y, targetVector.z);
|
||||
// We could pretty much ban the player at this point
|
||||
flagAndAlert(verbose);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,6 @@ import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.protocol.player.GameMode;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientEntityAction;
|
||||
|
||||
@CheckData(name = "BadPacketsX", experimental = true)
|
||||
@@ -42,7 +41,7 @@ public class BadPacketsX extends Check implements PostPredictionCheck {
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
if (player.gamemode == GameMode.SPECTATOR || isTickPacket(event.getPacketType())) {
|
||||
if (!player.cameraEntity.isSelf() || isTickPacket(event.getPacketType())) {
|
||||
sprint = sneak = false;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ import ac.grim.grimac.utils.math.Vector3dm;
|
||||
import ac.grim.grimac.utils.math.VectorUtils;
|
||||
import com.github.retrooper.packetevents.protocol.attribute.Attributes;
|
||||
import com.github.retrooper.packetevents.protocol.player.DiggingAction;
|
||||
import com.github.retrooper.packetevents.protocol.player.GameMode;
|
||||
|
||||
@CheckData(name = "FarBreak", description = "Breaking blocks too far away", experimental = true)
|
||||
public class FarBreak extends Check implements BlockBreakCheck {
|
||||
@@ -20,7 +19,7 @@ public class FarBreak extends Check implements BlockBreakCheck {
|
||||
|
||||
@Override
|
||||
public void onBlockBreak(BlockBreak blockBreak) {
|
||||
if (player.gamemode == GameMode.SPECTATOR || player.inVehicle() || blockBreak.action == DiggingAction.CANCELLED_DIGGING)
|
||||
if (!player.cameraEntity.isSelf() || player.inVehicle() || blockBreak.action == DiggingAction.CANCELLED_DIGGING)
|
||||
return; // falses
|
||||
|
||||
double min = Double.MAX_VALUE;
|
||||
|
||||
@@ -80,7 +80,8 @@ public class FastBreak extends Check implements BlockBreakCheck {
|
||||
blockDelayBalance += 300 - breakDelay;
|
||||
}
|
||||
|
||||
if (blockDelayBalance > 1000) { // If more than a second of advantage
|
||||
// Skip flagging clients whose tick rate is unobservable, like MultiBreak does
|
||||
if (blockDelayBalance > 1000 && player.isTickingReliablyFor(3)) { // If more than a second of advantage
|
||||
if (flagAndAlert("delay=" + breakDelay + "ms, type=" + blockBreak.block.getType()) && shouldModifyPackets()) {
|
||||
blockBreak.cancel();
|
||||
}
|
||||
@@ -102,7 +103,8 @@ public class FastBreak extends Check implements BlockBreakCheck {
|
||||
blockBreakBalance += diff;
|
||||
}
|
||||
|
||||
if (blockBreakBalance > 1000) { // If more than a second of advantage
|
||||
// Skip flagging clients whose tick rate is unobservable, like MultiBreak does
|
||||
if (blockBreakBalance > 1000 && player.isTickingReliablyFor(3)) { // If more than a second of advantage
|
||||
if (flagAndAlert("diff=" + diff + "ms, balance=" + blockBreakBalance + "ms, type=" + blockBreak.block.getType()) && shouldModifyPackets()) {
|
||||
blockBreak.cancel();
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ import ac.grim.grimac.utils.anticheat.update.BlockBreak;
|
||||
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.player.DiggingAction;
|
||||
import com.github.retrooper.packetevents.protocol.player.GameMode;
|
||||
import com.github.retrooper.packetevents.protocol.world.BlockFace;
|
||||
import com.github.retrooper.packetevents.util.Vector3i;
|
||||
|
||||
@@ -53,7 +52,7 @@ public class MultiBreak extends Check implements BlockBreakCheck {
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
if (player.gamemode == GameMode.SPECTATOR || isTickPacket(event.getPacketType())) {
|
||||
if (!player.cameraEntity.isSelf() || isTickPacket(event.getPacketType())) {
|
||||
hasBroken = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@ import ac.grim.grimac.utils.nmsutil.ReachUtils;
|
||||
import com.github.retrooper.packetevents.protocol.attribute.Attributes;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import com.github.retrooper.packetevents.protocol.player.DiggingAction;
|
||||
import com.github.retrooper.packetevents.protocol.player.GameMode;
|
||||
import com.github.retrooper.packetevents.protocol.world.BlockFace;
|
||||
import com.github.retrooper.packetevents.util.Vector3d;
|
||||
import com.github.retrooper.packetevents.util.Vector3f;
|
||||
@@ -34,7 +33,7 @@ public class RotationBreak extends Check implements BlockBreakCheck {
|
||||
|
||||
@Override
|
||||
public void onBlockBreak(BlockBreak blockBreak) {
|
||||
if (player.gamemode == GameMode.SPECTATOR)
|
||||
if (!player.cameraEntity.isSelf())
|
||||
return; // you don't send flying packets when spectating entities
|
||||
if (player.inVehicle()) return; // falses
|
||||
if (blockBreak.action == DiggingAction.CANCELLED_DIGGING) return; // falses
|
||||
@@ -50,7 +49,7 @@ public class RotationBreak extends Check implements BlockBreakCheck {
|
||||
|
||||
@Override
|
||||
public void onPostFlyingBlockBreak(BlockBreak blockBreak) {
|
||||
if (player.gamemode == GameMode.SPECTATOR)
|
||||
if (!player.cameraEntity.isSelf())
|
||||
return; // you don't send flying packets when spectating entities
|
||||
if (player.inVehicle()) return; // falses
|
||||
if (blockBreak.action == DiggingAction.CANCELLED_DIGGING) return; // falses
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
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 = "EntityPierce", configName = "EntityPierce", setback = 30)
|
||||
public class EntityPierce extends Check implements PacketCheck {
|
||||
public EntityPierce(GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,6 @@ import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.protocol.player.GameMode;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientInteractEntity;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -29,30 +28,33 @@ public class MultiInteractA extends Check implements PostPredictionCheck {
|
||||
WrapperPlayClientInteractEntity packet = new WrapperPlayClientInteractEntity(event);
|
||||
int entity = packet.getEntityId();
|
||||
boolean sneaking = packet.isSneaking().orElse(false);
|
||||
|
||||
if (hasInteracted && entity != lastEntity) {
|
||||
String verbose = "lastEntity=" + lastEntity + ", entity=" + entity
|
||||
+ ", lastSneaking=" + lastSneaking + ", sneaking=" + sneaking;
|
||||
if (!player.canSkipTicks()) {
|
||||
if (flagAndAlert(verbose) && shouldModifyPackets()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
} else {
|
||||
flags.add(verbose);
|
||||
}
|
||||
}
|
||||
|
||||
lastEntity = entity;
|
||||
lastSneaking = sneaking;
|
||||
hasInteracted = true;
|
||||
onInteract(event, entity, sneaking);
|
||||
}
|
||||
|
||||
if (player.gamemode == GameMode.SPECTATOR || isTickPacket(event.getPacketType())) {
|
||||
if (!player.cameraEntity.isSelf() || isTickPacket(event.getPacketType())) {
|
||||
hasInteracted = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void onInteract(PacketReceiveEvent event, int entity, boolean sneaking) {
|
||||
if (hasInteracted && (entity != lastEntity || sneaking != lastSneaking)) {
|
||||
String verbose = "lastEntity=" + lastEntity + ", entity=" + entity
|
||||
+ ", lastSneaking=" + lastSneaking + ", sneaking=" + sneaking;
|
||||
if (!player.canSkipTicks()) {
|
||||
if (flagAndAlert(verbose) && shouldModifyPackets()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
} else {
|
||||
flags.add(verbose);
|
||||
}
|
||||
}
|
||||
|
||||
lastEntity = entity;
|
||||
lastSneaking = sneaking;
|
||||
hasInteracted = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPredictionComplete(PredictionComplete predictionComplete) {
|
||||
if (!player.canSkipTicks()) return;
|
||||
|
||||
@@ -8,7 +8,6 @@ import ac.grim.grimac.utils.anticheat.MessageUtil;
|
||||
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.protocol.player.GameMode;
|
||||
import com.github.retrooper.packetevents.util.Vector3f;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientInteractEntity;
|
||||
|
||||
@@ -18,7 +17,7 @@ import java.util.ArrayList;
|
||||
public class MultiInteractB extends Check implements PostPredictionCheck {
|
||||
private final ArrayList<String> flags = new ArrayList<>();
|
||||
private Vector3f lastPos;
|
||||
private boolean hasInteracted = false;
|
||||
private boolean hasInteracted;
|
||||
|
||||
public MultiInteractB(final GrimPlayer player) {
|
||||
super(player);
|
||||
@@ -27,11 +26,11 @@ public class MultiInteractB extends Check implements PostPredictionCheck {
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
if (event.getPacketType() == PacketType.Play.Client.INTERACT_ENTITY) {
|
||||
Vector3f pos = new WrapperPlayClientInteractEntity(event).getTarget().orElse(null);
|
||||
WrapperPlayClientInteractEntity packet = new WrapperPlayClientInteractEntity(event);
|
||||
if (packet.getAction() != WrapperPlayClientInteractEntity.InteractAction.INTERACT_AT) return;
|
||||
|
||||
if (pos == null) {
|
||||
return;
|
||||
}
|
||||
Vector3f pos = packet.getTarget().orElse(null);
|
||||
if (pos == null) return; // shouldn't ever happen, but whatever
|
||||
|
||||
if (hasInteracted && !pos.equals(lastPos)) {
|
||||
String verbose = "pos=" + MessageUtil.toUnlabledString(pos) + ", lastPos=" + MessageUtil.toUnlabledString(lastPos);
|
||||
@@ -49,7 +48,7 @@ public class MultiInteractB extends Check implements PostPredictionCheck {
|
||||
hasInteracted = true;
|
||||
}
|
||||
|
||||
if (player.gamemode == GameMode.SPECTATOR || isTickPacket(event.getPacketType())) {
|
||||
if (!player.cameraEntity.isSelf() || isTickPacket(event.getPacketType())) {
|
||||
hasInteracted = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,31 +18,48 @@ package ac.grim.grimac.checks.impl.combat;
|
||||
import ac.grim.grimac.api.config.ConfigManager;
|
||||
import ac.grim.grimac.checks.Check;
|
||||
import ac.grim.grimac.checks.CheckData;
|
||||
import ac.grim.grimac.checks.debug.HitboxDebugHandler;
|
||||
import ac.grim.grimac.checks.type.PacketCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.collisions.datatypes.CollisionBox;
|
||||
import ac.grim.grimac.utils.collisions.datatypes.NoCollisionBox;
|
||||
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
|
||||
import ac.grim.grimac.utils.data.BlockHitData;
|
||||
import ac.grim.grimac.utils.data.EntityHitData;
|
||||
import ac.grim.grimac.utils.data.HitData;
|
||||
import ac.grim.grimac.utils.data.Pair;
|
||||
import ac.grim.grimac.utils.data.packetentity.PacketEntity;
|
||||
import ac.grim.grimac.utils.data.packetentity.PacketEntitySizeable;
|
||||
import ac.grim.grimac.utils.data.packetentity.dragon.PacketEntityEnderDragonPart;
|
||||
import ac.grim.grimac.utils.math.GrimMath;
|
||||
import ac.grim.grimac.utils.math.Vector3dm;
|
||||
import ac.grim.grimac.utils.nmsutil.ReachUtils;
|
||||
import ac.grim.grimac.utils.viaversion.ViaVersionUtil;
|
||||
import ac.grim.grimac.utils.nmsutil.WorldRayTrace;
|
||||
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.attribute.Attributes;
|
||||
import com.github.retrooper.packetevents.protocol.component.ComponentTypes;
|
||||
import com.github.retrooper.packetevents.protocol.component.builtin.item.ItemAttackRange;
|
||||
import com.github.retrooper.packetevents.protocol.entity.type.EntityType;
|
||||
import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes;
|
||||
import com.github.retrooper.packetevents.protocol.item.ItemStack;
|
||||
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.util.Vector3d;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState;
|
||||
import com.github.retrooper.packetevents.protocol.player.InteractionHand;
|
||||
import com.github.retrooper.packetevents.util.Vector3i;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientInteractEntity;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerFlying;
|
||||
import com.viaversion.viaversion.api.Via;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
|
||||
// You may not copy the check unless you are licensed under GPL
|
||||
@CheckData(name = "Reach", setback = 10)
|
||||
@@ -55,9 +72,15 @@ public class Reach extends Check implements PacketCheck {
|
||||
private static final CheckResult NONE = new CheckResult(ResultType.NONE, "");
|
||||
// Only one flag per reach attack, per entity, per tick.
|
||||
// We store position because lastX isn't reliable on teleports.
|
||||
private final Int2ObjectMap<Vector3d> playerAttackQueue = new Int2ObjectOpenHashMap<>();
|
||||
private final Int2ObjectMap<InteractionData> playerAttackQueue = new Int2ObjectOpenHashMap<>();
|
||||
// temporarily used to prevent falses in the wall hit check
|
||||
private final Set<Vector3i> blocksChangedThisTick = new HashSet<>();
|
||||
// extra distance to raytrace beyond player reach distance so we know how far beyond the legit distance a cheater hit
|
||||
public static final double extraSearchDistance = 3;
|
||||
|
||||
private boolean ignoreNonPlayerTargets;
|
||||
private boolean cancelImpossibleHits;
|
||||
private double threshold;
|
||||
public double threshold;
|
||||
private double cancelBuffer; // For the next 4 hits after using reach, we aggressively cancel reach
|
||||
|
||||
public Reach(GrimPlayer player) {
|
||||
@@ -67,59 +90,117 @@ public class Reach extends Check implements PacketCheck {
|
||||
@Override
|
||||
public void onPacketReceive(final PacketReceiveEvent event) {
|
||||
if (!player.disableGrim && event.getPacketType() == PacketType.Play.Client.INTERACT_ENTITY) {
|
||||
WrapperPlayClientInteractEntity action = new WrapperPlayClientInteractEntity(event);
|
||||
|
||||
// Don't let the player teleport to bypass reach
|
||||
if (player.getSetbackTeleportUtil().shouldBlockMovement()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
return;
|
||||
}
|
||||
|
||||
PacketEntity entity = player.compensatedEntities.entityMap.get(action.getEntityId());
|
||||
// Stop people from freezing transactions before an entity spawns to bypass reach
|
||||
// TODO: implement dragon parts?
|
||||
if (entity == null || entity instanceof PacketEntityEnderDragonPart) {
|
||||
// Only cancel if and only if we are tracking this entity
|
||||
// This is because we don't track paintings.
|
||||
if (shouldModifyPackets() && player.compensatedEntities.serverPositionsMap.containsKey(action.getEntityId())) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Dead entities cause false flags (https://github.com/GrimAnticheat/Grim/issues/546)
|
||||
if (entity.isDead) return;
|
||||
|
||||
// TODO: Remove when in front of via
|
||||
if (entity.type == EntityTypes.ARMOR_STAND && player.getClientVersion().isOlderThan(ClientVersion.V_1_8))
|
||||
return;
|
||||
//Prevents Happy Ghast Reach false on 1.21.6+ servers with ViaBackwards set up
|
||||
if (entity.type == EntityTypes.HAPPY_GHAST && player.getClientVersion().isOlderThan(ClientVersion.V_1_21_6)) {
|
||||
return;
|
||||
}
|
||||
if (player.gamemode == GameMode.CREATIVE || player.gamemode == GameMode.SPECTATOR)
|
||||
return;
|
||||
if (player.inVehicle()) return;
|
||||
if (entity.riding != null) return;
|
||||
|
||||
boolean tooManyAttacks = playerAttackQueue.size() > 10;
|
||||
if (!tooManyAttacks) {
|
||||
playerAttackQueue.put(action.getEntityId(), new Vector3d(player.x, player.y, player.z)); // Queue for next tick for very precise check
|
||||
}
|
||||
|
||||
boolean knownInvalid = isKnownInvalid(entity);
|
||||
|
||||
if ((shouldModifyPackets() && cancelImpossibleHits && knownInvalid) || tooManyAttacks) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
WrapperPlayClientInteractEntity packet = new WrapperPlayClientInteractEntity(event);
|
||||
onInteract(event, packet.getEntityId(), packet.getHand());
|
||||
}
|
||||
|
||||
// If the player set their look, or we know they have a new tick
|
||||
final boolean isFlying = WrapperPlayClientPlayerFlying.isFlying(event.getPacketType());
|
||||
if (isUpdate(event.getPacketType())) {
|
||||
tickBetterReachCheckWithAngle();
|
||||
tickBetterReachCheckWithAngle(isFlying);
|
||||
}
|
||||
}
|
||||
|
||||
private void onInteract(PacketReceiveEvent event, int entityId, InteractionHand hand) {
|
||||
// Don't let the player teleport to bypass reach
|
||||
if (player.getSetbackTeleportUtil().shouldBlockMovement()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
return;
|
||||
}
|
||||
|
||||
PacketEntity entity = player.compensatedEntities.entityMap.get(entityId);
|
||||
// Stop people from freezing transactions before an entity spawns to bypass reach
|
||||
// TODO: implement dragon parts?
|
||||
if (entity == null || entity instanceof PacketEntityEnderDragonPart) {
|
||||
// Only cancel if and only if we are tracking this entity
|
||||
// This is because we don't track paintings.
|
||||
if (shouldModifyPackets() && player.compensatedEntities.serverPositionsMap.containsKey(entityId)) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (ignoreNonPlayerTargets && !entity.type.equals(EntityTypes.PLAYER)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Dead entities cause false flags (https://github.com/GrimAnticheat/Grim/issues/546)
|
||||
if (entity.isDead) return;
|
||||
|
||||
// TODO: Remove when in front of via
|
||||
if (entity.type == EntityTypes.ARMOR_STAND && player.getClientVersion().isOlderThan(ClientVersion.V_1_8))
|
||||
return;
|
||||
// Prevents Happy Ghast Reach false on 1.21.6+ servers with ViaBackwards set up
|
||||
if (entity.type == EntityTypes.HAPPY_GHAST && player.getClientVersion().isOlderThan(ClientVersion.V_1_21_6))
|
||||
return;
|
||||
if (player.gamemode == GameMode.CREATIVE || player.gamemode == GameMode.SPECTATOR)
|
||||
return;
|
||||
if (player.inVehicle()) return;
|
||||
if (entity.riding != null) return;
|
||||
|
||||
ItemStack currentStack = player.inventory.getItemInHand(hand);
|
||||
ItemStack startStack = player.inventory.getStartOfTickStack();
|
||||
|
||||
boolean hasRange = false;
|
||||
float maxReach = 0f;
|
||||
float hitboxMargin = 0f;
|
||||
|
||||
boolean clientAttackRangeExists = player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_21_11);
|
||||
boolean clientAndServerAgrees = clientAttackRangeExists && ATTACK_RANGE_COMPONENT_EXISTS;
|
||||
|
||||
boolean viaVersionAvailable = false;
|
||||
if (USE_1_8_HITBOX_MARGIN && ViaVersionUtil.isAvailable) {
|
||||
viaVersionAvailable = Via.getConfig().getValues().containsKey("use-1_8-hitbox-margin") && Via.getConfig().use1_8HitboxMargin();
|
||||
}
|
||||
|
||||
boolean clientAndViaVersion = clientAttackRangeExists && viaVersionAvailable;
|
||||
if (clientAndServerAgrees || clientAndViaVersion) {
|
||||
ItemAttackRange startRange = startStack.getComponentOr(ComponentTypes.ATTACK_RANGE, null);
|
||||
ItemAttackRange currentRange = currentStack.getComponentOr(ComponentTypes.ATTACK_RANGE, null);
|
||||
|
||||
if (clientAndViaVersion) {
|
||||
if (startStack != ItemStack.EMPTY) {
|
||||
startRange = new ItemAttackRange(0F, 3F, 0F, 4F, 0.1F, 1F);
|
||||
}
|
||||
|
||||
if (currentStack != ItemStack.EMPTY) {
|
||||
currentRange = new ItemAttackRange(0F, 3F, 0F, 4F, 0.1F, 1F);
|
||||
}
|
||||
}
|
||||
|
||||
// If the start stack has no range component, the client defaults to vanilla reach behavior,
|
||||
// regardless of what the current stack is (No Range -> X = No Range used).
|
||||
if (startRange != null) {
|
||||
hasRange = true;
|
||||
if (currentRange == null) {
|
||||
// Range (Start) -> No Range (Current)
|
||||
// Client logic uses Start Range
|
||||
maxReach = startRange.getMaxRange();
|
||||
hitboxMargin = startRange.getHitboxMargin();
|
||||
} else {
|
||||
// Range (Start) -> Range (Current)
|
||||
// Client logic requires satisfying BOTH constraints
|
||||
maxReach = Math.min(startRange.getMaxRange(), currentRange.getMaxRange());
|
||||
hitboxMargin = Math.min(startRange.getHitboxMargin(), currentRange.getHitboxMargin());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
boolean tooManyAttacks = playerAttackQueue.size() > 10;
|
||||
if (!tooManyAttacks) {
|
||||
playerAttackQueue.put(entityId, new InteractionData(
|
||||
player.x, player.y, player.z,
|
||||
hasRange, maxReach, hitboxMargin
|
||||
)); // Queue for next tick for very precise check
|
||||
}
|
||||
|
||||
boolean knownInvalid = isKnownInvalid(entity, hasRange, maxReach, hitboxMargin);
|
||||
|
||||
if ((shouldModifyPackets() && cancelImpossibleHits && knownInvalid) || tooManyAttacks) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,7 +212,7 @@ public class Reach extends Check implements PacketCheck {
|
||||
// than this method. If this method flags, the other method WILL flag.
|
||||
//
|
||||
// Meaning that the other check should be the only one that flags.
|
||||
private boolean isKnownInvalid(PacketEntity reachEntity) {
|
||||
private boolean isKnownInvalid(PacketEntity reachEntity, boolean hasAttackRange, float itemMaxReach, float itemHitboxMargin) {
|
||||
// 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)
|
||||
return false; // exempt
|
||||
@@ -142,23 +223,23 @@ public class Reach extends Check implements PacketCheck {
|
||||
|
||||
// Filter out what we assume to be cheats
|
||||
if (cancelBuffer != 0) {
|
||||
CheckResult result = checkReach(reachEntity, new Vector3d(player.x, player.y, player.z), true);
|
||||
CheckResult result = checkReach(reachEntity, player.x, player.y, player.z, hasAttackRange, itemMaxReach, itemHitboxMargin, true);
|
||||
return result.isFlag(); // If they flagged
|
||||
} else {
|
||||
SimpleCollisionBox targetBox = reachEntity.getPossibleCollisionBoxes();
|
||||
if (reachEntity.type == 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);
|
||||
SimpleCollisionBox targetBox = getTargetBox(reachEntity);
|
||||
|
||||
double maxReach = applyReachModifiers(targetBox, hasAttackRange, itemMaxReach, itemHitboxMargin, !player.packetStateData.didLastMovementIncludePosition);
|
||||
return ReachUtils.getMinReachToBox(player, targetBox) > maxReach;
|
||||
}
|
||||
}
|
||||
|
||||
private void tickBetterReachCheckWithAngle() {
|
||||
for (Int2ObjectMap.Entry<Vector3d> attack : playerAttackQueue.int2ObjectEntrySet()) {
|
||||
private void tickBetterReachCheckWithAngle(boolean isFlying) {
|
||||
for (Int2ObjectMap.Entry<InteractionData> attack : playerAttackQueue.int2ObjectEntrySet()) {
|
||||
PacketEntity reachEntity = player.compensatedEntities.entityMap.get(attack.getIntKey());
|
||||
if (reachEntity == null) continue;
|
||||
|
||||
CheckResult result = checkReach(reachEntity, attack.getValue(), false);
|
||||
InteractionData interactionData = attack.getValue();
|
||||
CheckResult result = checkReach(reachEntity, interactionData.x, interactionData.y, interactionData.z, interactionData.hasAttackRange, interactionData.maxReach, interactionData.hitboxMargin, false);
|
||||
switch (result.type()) {
|
||||
case REACH -> {
|
||||
String added = ", type=" + reachEntity.type.getName().getKey();
|
||||
@@ -174,64 +255,43 @@ public class Reach extends Check implements PacketCheck {
|
||||
}
|
||||
player.checkManager.getCheck(Hitboxes.class).flagAndAlert(result.verbose() + added);
|
||||
}
|
||||
case WALL_HIT -> {
|
||||
String added = reachEntity.type == EntityTypes.PLAYER ? "" : "type=" + reachEntity.type.getName().getKey();
|
||||
player.checkManager.getCheck(WallHit.class).flagAndAlert(result.verbose() + added);
|
||||
}
|
||||
case ENTITY_PIERCE -> {
|
||||
String added = reachEntity.type == EntityTypes.PLAYER ? "" : "type=" + reachEntity.type.getName().getKey();
|
||||
player.checkManager.getCheck(EntityPierce.class).flagAndAlert(result.verbose() + added);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
playerAttackQueue.clear();
|
||||
// We can't use transactions for this because of this problem:
|
||||
// transaction -> block changed applied -> 2nd transaction -> list cleared -> attack packet -> flying -> reach block hit checked, falses
|
||||
if (isFlying) blocksChangedThisTick.clear();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private CheckResult checkReach(PacketEntity reachEntity, Vector3d from, boolean isPrediction) {
|
||||
SimpleCollisionBox targetBox = reachEntity.getPossibleCollisionBoxes();
|
||||
|
||||
if (reachEntity.type == 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)
|
||||
// Yes, this is vanilla and not uncertainty. All reach checks have this or they are wrong.
|
||||
if (player.getClientVersion().isOlderThan(ClientVersion.V_1_9)) {
|
||||
targetBox.expand(0.1f);
|
||||
}
|
||||
|
||||
targetBox.expand(threshold);
|
||||
|
||||
// This is better than adding to the reach, as 0.03 can cause a player to miss their target
|
||||
// Adds some more than 0.03 uncertainty in some cases, but a good trade off for simplicity
|
||||
//
|
||||
// Just give the uncertainty on 1.9+ clients as we have no way of knowing whether they had 0.03 movement
|
||||
// However, on 1.21.2+ we do know if they had 0.03 movement
|
||||
if (!player.packetStateData.didLastLastMovementIncludePosition || player.canSkipTicks())
|
||||
targetBox.expand(player.getMovementThreshold());
|
||||
private CheckResult checkReach(PacketEntity reachEntity, double x, double y, double z, boolean hasAttackRange, float itemMaxReach, float itemHitboxMargin, boolean isPrediction) {
|
||||
SimpleCollisionBox targetBox = getTargetBox(reachEntity);
|
||||
|
||||
double maxReach = applyReachModifiers(targetBox, hasAttackRange, itemMaxReach, itemHitboxMargin, !player.packetStateData.didLastLastMovementIncludePosition);
|
||||
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)));
|
||||
// will store all lookVecsAndEyeHeight pairs that landed a hit on the target entity
|
||||
// We only need to check for blocking intersections for these
|
||||
List<Pair<Vector3dm, Double>> lookVecsAndEyeHeights = new ArrayList<>();
|
||||
|
||||
// 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));
|
||||
|
||||
// 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));
|
||||
}
|
||||
|
||||
// 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));
|
||||
}
|
||||
}
|
||||
|
||||
final double maxReach = player.compensatedEntities.self.getAttributeValue(Attributes.ENTITY_INTERACTION_RANGE);
|
||||
// +3 would be 3 + 3 = 6, which is the pre-1.20.5 behaviour, preventing "Missed Hitbox"
|
||||
final double distance = maxReach + 3;
|
||||
|
||||
final double[] possibleEyeHeights = player.getPossibleEyeHeights();
|
||||
final Vector3dm eyePos = new Vector3dm(from.getX(), 0, from.getZ());
|
||||
final Vector3dm[] possibleLookDirs = player.getPossibleLookVectors(isPrediction);
|
||||
final Vector3dm eyePos = new Vector3dm(x, 0, z);
|
||||
for (Vector3dm lookVec : possibleLookDirs) {
|
||||
for (double eye : possibleEyeHeights) {
|
||||
eyePos.setY(from.getY() + eye);
|
||||
eyePos.setY(y + eye);
|
||||
Vector3dm endReachPos = eyePos.clone().add(lookVec.getX() * distance, lookVec.getY() * distance, lookVec.getZ() * distance);
|
||||
|
||||
Vector3dm intercept = ReachUtils.calculateIntercept(targetBox, eyePos, endReachPos).first();
|
||||
@@ -243,13 +303,44 @@ public class Reach extends Check implements PacketCheck {
|
||||
|
||||
if (intercept != null) {
|
||||
minDistance = Math.min(eyePos.distance(intercept), minDistance);
|
||||
lookVecsAndEyeHeights.add(new Pair<>(lookVec, eye));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hitboxDebuggingEnabled())
|
||||
sendHitboxDebugData(reachEntity, x, y, z, lookVecsAndEyeHeights, isPrediction);
|
||||
|
||||
HitData foundHitData = null;
|
||||
// If the entity is within range of the player (we'll flag anyway if not, so no point checking blocks in this case)
|
||||
// Ignore when could be hitting through a moving shulker, piston blocks. They are just too glitchy/uncertain to check.
|
||||
if (minDistance <= distance - extraSearchDistance && !player.compensatedWorld.isNearHardEntity(player.boundingBox.copy().expand(4))) {
|
||||
// we can optimize didRayTraceHit more to only rayTrace up to the maximize distance of all rays that hit to the target...
|
||||
// I'm too lazy to do that and we don't need to optimize that much yet so...
|
||||
final @Nullable Pair<Double, HitData> hitResult = WorldRayTrace.didRayTraceHit(player, reachEntity, lookVecsAndEyeHeights, x, y, z);
|
||||
HitData hitData = hitResult.second();
|
||||
// If the returned hit result was NOT the target entity we flag the check
|
||||
if (hitData instanceof EntityHitData &&
|
||||
player.compensatedEntities.getPacketEntityID(((EntityHitData) hitData).getEntity()) != player.compensatedEntities.getPacketEntityID(reachEntity)) {
|
||||
minDistance = Double.MIN_VALUE;
|
||||
foundHitData = hitData;
|
||||
// until I fix block modeling exempt any blocks changed this tick
|
||||
} else if (hitData instanceof BlockHitData && !blocksChangedThisTick.contains(((BlockHitData) hitData).position())) {
|
||||
minDistance = Double.MIN_VALUE;
|
||||
foundHitData = hitData;
|
||||
}
|
||||
}
|
||||
|
||||
// if the entity is not exempt and the entity is alive
|
||||
if ((!blacklisted.contains(reachEntity.type) && reachEntity.isLivingEntity) || reachEntity.type == EntityTypes.END_CRYSTAL) {
|
||||
if (minDistance == Double.MAX_VALUE) {
|
||||
if (minDistance == Double.MIN_VALUE && foundHitData != null) {
|
||||
cancelBuffer = 1;
|
||||
if (foundHitData instanceof BlockHitData) {
|
||||
return new CheckResult(ResultType.WALL_HIT, "Hit block=" + ((BlockHitData) foundHitData).state().getType().getName() + " ");
|
||||
} else { // entity hit data
|
||||
return new CheckResult(ResultType.ENTITY_PIERCE, "Hit entity=" + ((EntityHitData) foundHitData).getEntity().type.getName() + " ");
|
||||
}
|
||||
} else if (minDistance == Double.MAX_VALUE) {
|
||||
cancelBuffer = 1;
|
||||
return new CheckResult(ResultType.HITBOX, "");
|
||||
} else if (minDistance > maxReach) {
|
||||
@@ -263,14 +354,55 @@ public class Reach extends Check implements PacketCheck {
|
||||
return NONE;
|
||||
}
|
||||
|
||||
private SimpleCollisionBox getTargetBox(PacketEntity reachEntity) {
|
||||
if (reachEntity.type == EntityTypes.END_CRYSTAL) { // Hardcode end crystal box
|
||||
return new SimpleCollisionBox(reachEntity.trackedServerPosition.getPos().subtract(1, 0, 1), reachEntity.trackedServerPosition.getPos().add(1, 2, 1));
|
||||
}
|
||||
return reachEntity.getPossibleCollisionBoxes();
|
||||
}
|
||||
|
||||
private static final boolean ATTACK_RANGE_COMPONENT_EXISTS = PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_21_11);
|
||||
private static final boolean USE_1_8_HITBOX_MARGIN = PacketEvents.getAPI().getServerManager().getVersion().isOlderThanOrEquals(ServerVersion.V_1_8_8);
|
||||
|
||||
private double applyReachModifiers(SimpleCollisionBox targetBox, boolean hasAttackRange, float itemMaxReach, float itemHitboxMargin, boolean giveMovementThreshold) {
|
||||
double maxReach;
|
||||
double hitboxMargin = threshold;
|
||||
|
||||
if (hasAttackRange) {
|
||||
maxReach = itemMaxReach;
|
||||
hitboxMargin += itemHitboxMargin;
|
||||
} else {
|
||||
maxReach = player.compensatedEntities.self.getAttributeValue(Attributes.ENTITY_INTERACTION_RANGE);
|
||||
// 1.7 and 1.8 players get a bit of extra hitbox (this is why you should use 1.8 on cross version servers)
|
||||
// Yes, this is vanilla and not uncertainty. All reach checks have this or they are wrong.
|
||||
if (player.getClientVersion().isOlderThan(ClientVersion.V_1_9)) {
|
||||
hitboxMargin += 0.1f;
|
||||
}
|
||||
}
|
||||
|
||||
// This is better than adding to the reach, as 0.03 can cause a player to miss their target
|
||||
// Adds some more than 0.03 uncertainty in some cases, but a good trade off for simplicity
|
||||
//
|
||||
// Just give the uncertainty on 1.9+ clients as we have no way of knowing whether they had 0.03 movement
|
||||
// However, on 1.21.2+ we do know if they had 0.03 movement
|
||||
if (giveMovementThreshold || player.canSkipTicks()) {
|
||||
hitboxMargin += player.getMovementThreshold();
|
||||
}
|
||||
|
||||
targetBox.expand(hitboxMargin);
|
||||
|
||||
return maxReach;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReload(ConfigManager config) {
|
||||
this.ignoreNonPlayerTargets = config.getBooleanElse("Reach.ignore-non-player-targets", false);
|
||||
this.cancelImpossibleHits = config.getBooleanElse("Reach.block-impossible-hits", true);
|
||||
this.threshold = config.getDoubleElse("Reach.threshold", 0.0005);
|
||||
}
|
||||
|
||||
private enum ResultType {
|
||||
REACH, HITBOX, NONE
|
||||
REACH, HITBOX, WALL_HIT, ENTITY_PIERCE, NONE
|
||||
}
|
||||
|
||||
private record CheckResult(ResultType type, String verbose) {
|
||||
@@ -278,4 +410,71 @@ public class Reach extends Check implements PacketCheck {
|
||||
return type != ResultType.NONE;
|
||||
}
|
||||
}
|
||||
|
||||
public void handleBlockChange(Vector3i vector3i, WrappedBlockState state) {
|
||||
if (blocksChangedThisTick.size() >= 40) return; // Don't let players freeze movement packets to grow this
|
||||
// Only do this for nearby blocks
|
||||
if (GrimMath.distanceSquared(vector3i.x, vector3i.y, vector3i.z, player.x, player.y, player.z) > 36) return;
|
||||
// Only do this if the state really had any world impact
|
||||
if (state.equals(player.compensatedWorld.getBlock(vector3i))) return;
|
||||
blocksChangedThisTick.add(vector3i);
|
||||
}
|
||||
|
||||
private boolean hitboxDebuggingEnabled() {
|
||||
return player.checkManager.getCheck(HitboxDebugHandler.class).isEnabled();
|
||||
}
|
||||
|
||||
private void sendHitboxDebugData(PacketEntity reachEntity, double x, double y, double z, List<Pair<Vector3dm, Double>> lookVecsAndEyeHeights, boolean isPrediction) {
|
||||
Map<Integer, CollisionBox> hitboxes = new HashMap<>();
|
||||
for (Int2ObjectMap.Entry<PacketEntity> entry : player.compensatedEntities.entityMap.int2ObjectEntrySet()) {
|
||||
PacketEntity entity = entry.getValue();
|
||||
if (!entity.canHit()) continue;
|
||||
|
||||
CollisionBox box;
|
||||
|
||||
if (entity.equals(reachEntity)) {
|
||||
// Target entity gets expanded hitbox
|
||||
box = entity.getPossibleCollisionBoxes();
|
||||
SimpleCollisionBox sBox = (SimpleCollisionBox) box;
|
||||
sBox.expand(threshold);
|
||||
|
||||
// Add movement threshold uncertainty for 1.9+ or non-position updates
|
||||
if (!player.packetStateData.didLastLastMovementIncludePosition
|
||||
|| player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_9)) {
|
||||
sBox.expand(player.getMovementThreshold());
|
||||
}
|
||||
} else {
|
||||
// Non-target entities
|
||||
box = entity.getMinimumPossibleCollisionBoxes();
|
||||
if (box instanceof NoCollisionBox) {
|
||||
hitboxes.put(entry.getIntKey(), NoCollisionBox.INSTANCE);
|
||||
continue;
|
||||
} else if (box instanceof SimpleCollisionBox sBox) {
|
||||
sBox.expand(-threshold);
|
||||
// Shrink non-target entities by movement threshold when applicable
|
||||
if (!player.packetStateData.didLastLastMovementIncludePosition
|
||||
|| player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_9)) {
|
||||
sBox.expand(-player.getMovementThreshold());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add 1.8 and below extra hitbox size
|
||||
if (player.getClientVersion().isOlderThan(ClientVersion.V_1_9)
|
||||
&& box instanceof SimpleCollisionBox) {
|
||||
((SimpleCollisionBox) box).expand(0.1f);
|
||||
}
|
||||
|
||||
hitboxes.put(entry.getIntKey(), box);
|
||||
}
|
||||
|
||||
player.checkManager.getCheck(HitboxDebugHandler.class).sendHitboxData(hitboxes,
|
||||
Collections.singleton(player.compensatedEntities.getPacketEntityID(reachEntity)),
|
||||
lookVecsAndEyeHeights,
|
||||
x, y, z,
|
||||
isPrediction, player.compensatedEntities.self.getAttributeValue(Attributes.ENTITY_INTERACTION_RANGE));
|
||||
}
|
||||
|
||||
private record InteractionData(double x, double y, double z, boolean hasAttackRange, float maxReach, float hitboxMargin) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
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;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientInteractEntity;
|
||||
|
||||
@CheckData(name = "SelfInteract", description = "Interacted with self")
|
||||
public class SelfInteract extends Check implements PacketCheck {
|
||||
public SelfInteract(GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
if (event.getPacketType() == PacketType.Play.Client.INTERACT_ENTITY) {
|
||||
WrapperPlayClientInteractEntity packet = new WrapperPlayClientInteractEntity(event);
|
||||
onInteract(event, packet.getEntityId());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// TODO: should check for camera entity id instead of player entity id?
|
||||
private void onInteract(PacketReceiveEvent event, int entityId) {
|
||||
if (player.cameraEntity.isSelf() && entityId == player.entityID
|
||||
&& flagAndAlert() && shouldModifyPackets()) { // Instant ban
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
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 = "WallHit", configName = "WallHit", setback = 20)
|
||||
public class WallHit extends Check implements PacketCheck {
|
||||
public WallHit(GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,7 @@ public class CrashC extends Check implements PacketCheck {
|
||||
if (!Double.isFinite(pos.getX()) || !Double.isFinite(pos.getY()) || !Double.isFinite(pos.getZ())
|
||||
|| !Float.isFinite(pos.getYaw()) || !Float.isFinite(pos.getPitch())
|
||||
) {
|
||||
flagAndAlert("xyzYP: " + pos.getX() + ", " + pos.getY() + ", " + pos.getZ() + ", " + pos.getYaw() + ", " + pos.getPitch());
|
||||
flagAndAlert("xyzYP=" + pos.getX() + ", " + pos.getY() + ", " + pos.getZ() + ", " + pos.getYaw() + ", " + pos.getPitch());
|
||||
player.getSetbackTeleportUtil().executeViolationSetback();
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
|
||||
@@ -6,9 +6,7 @@ import ac.grim.grimac.checks.type.PostPredictionCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientEntityAction;
|
||||
|
||||
@CheckData(name = "ElytraA", description = "Started gliding while already gliding")
|
||||
public class ElytraA extends Check implements PostPredictionCheck {
|
||||
@@ -33,13 +31,6 @@ public class ElytraA extends Check implements PostPredictionCheck {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
if (player.getClientVersion().isOlderThan(ClientVersion.V_1_15) && event.getPacketType() == PacketType.Play.Client.ENTITY_ACTION
|
||||
&& new WrapperPlayClientEntityAction(event).getAction() == WrapperPlayClientEntityAction.Action.START_FLYING_WITH_ELYTRA
|
||||
) onStartGliding(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPredictionComplete(PredictionComplete predictionComplete) {
|
||||
if (setback) {
|
||||
|
||||
@@ -8,7 +8,6 @@ import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import com.github.retrooper.packetevents.protocol.player.GameMode;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientEntityAction;
|
||||
|
||||
@CheckData(name = "ElytraC", description = "Started gliding too frequently")
|
||||
@@ -27,7 +26,7 @@ public class ElytraC extends Check implements PostPredictionCheck {
|
||||
return;
|
||||
}
|
||||
|
||||
if (player.gamemode == GameMode.SPECTATOR) {
|
||||
if (!player.cameraEntity.isSelf()) {
|
||||
glideThisTick = glideLastTick = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
package ac.grim.grimac.checks.impl.inventory;
|
||||
|
||||
import ac.grim.grimac.checks.CheckData;
|
||||
import ac.grim.grimac.checks.type.InventoryCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientInteractEntity;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientInteractEntity.InteractAction;
|
||||
|
||||
@CheckData(name = "InventoryA", setback = 3, description = "Attacked an entity while inventory is open")
|
||||
public class InventoryA extends InventoryCheck {
|
||||
public InventoryA(GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
super.onPacketReceive(event);
|
||||
|
||||
if (event.getPacketType() == PacketType.Play.Client.INTERACT_ENTITY) {
|
||||
WrapperPlayClientInteractEntity wrapper = new WrapperPlayClientInteractEntity(event);
|
||||
|
||||
if (wrapper.getAction() != InteractAction.ATTACK) return;
|
||||
|
||||
// Is not possible to attack while the inventory is open.
|
||||
if (player.hasInventoryOpen) {
|
||||
if (flagAndAlert()) {
|
||||
// Cancel the packet
|
||||
if (shouldModifyPackets()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
if (!isNoSetbackPermission())
|
||||
closeInventory();
|
||||
}
|
||||
} else {
|
||||
reward();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package ac.grim.grimac.checks.impl.inventory;
|
||||
|
||||
import ac.grim.grimac.checks.CheckData;
|
||||
import ac.grim.grimac.checks.type.InventoryCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.player.DiggingAction;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerDigging;
|
||||
|
||||
@CheckData(name = "InventoryB", setback = 3, description = "Started digging blocks while inventory is open")
|
||||
public class InventoryB extends InventoryCheck {
|
||||
public InventoryB(GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
public void handle(PacketReceiveEvent event, WrapperPlayClientPlayerDigging wrapper) {
|
||||
if (wrapper.getAction() != DiggingAction.START_DIGGING) return;
|
||||
|
||||
// Is not possible to start digging a block while the inventory is open.
|
||||
if (player.hasInventoryOpen) {
|
||||
if (flagAndAlert()) {
|
||||
// Cancel the packet
|
||||
if (shouldModifyPackets()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
if (!isNoSetbackPermission()) {
|
||||
closeInventory();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
reward();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package ac.grim.grimac.checks.impl.inventory;
|
||||
|
||||
import ac.grim.grimac.checks.CheckData;
|
||||
import ac.grim.grimac.checks.type.InventoryCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.anticheat.update.BlockPlace;
|
||||
|
||||
@CheckData(name = "InventoryC", setback = 3, description = "Placed a block while inventory is open")
|
||||
public class InventoryC extends InventoryCheck {
|
||||
|
||||
public InventoryC(GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
public void onBlockPlace(final BlockPlace place) {
|
||||
// It is not possible to place a block while the inventory is open
|
||||
if (player.hasInventoryOpen) {
|
||||
if (flagAndAlert()) {
|
||||
if (shouldModifyPackets()) {
|
||||
place.resync();
|
||||
}
|
||||
if (!isNoSetbackPermission()) {
|
||||
closeInventory();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
reward();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package ac.grim.grimac.checks.impl.inventory;
|
||||
|
||||
import ac.grim.grimac.checks.CheckData;
|
||||
import ac.grim.grimac.checks.type.InventoryCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
|
||||
import ac.grim.grimac.utils.data.VectorData;
|
||||
import ac.grim.grimac.utils.data.VectorData.MoveVectorData;
|
||||
import ac.grim.grimac.utils.data.VehicleData;
|
||||
|
||||
import java.util.StringJoiner;
|
||||
|
||||
@CheckData(name = "InventoryD", setback = 1, decay = 0.25)
|
||||
public class InventoryD extends InventoryCheck {
|
||||
private int horseJumpVerbose;
|
||||
|
||||
public InventoryD(GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPredictionComplete(final PredictionComplete predictionComplete) {
|
||||
if (!predictionComplete.isChecked() ||
|
||||
predictionComplete.getData().isTeleport() ||
|
||||
player.getSetbackTeleportUtil().blockOffsets ||
|
||||
player.packetStateData.lastPacketWasTeleport ||
|
||||
player.packetStateData.isSlowedByUsingItem() ||
|
||||
System.currentTimeMillis() - player.lastBlockPlaceUseItem < 50L) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (player.hasInventoryOpen) {
|
||||
boolean inVehicle = player.inVehicle();
|
||||
boolean isJumping, isMoving;
|
||||
|
||||
if (inVehicle) {
|
||||
VehicleData vehicle = player.vehicleData;
|
||||
|
||||
// Will flag once if player open anything with pressed space bar
|
||||
isJumping = !vehicle.pendingJumps.isEmpty() && horseJumpVerbose++ >= 1;
|
||||
isMoving = vehicle.nextVehicleForward != 0 || vehicle.nextVehicleHorizontal != 0;
|
||||
} else {
|
||||
MoveVectorData move = findMovement(player.predictedVelocity);
|
||||
|
||||
isJumping = player.predictedVelocity.isJump();
|
||||
isMoving = move != null && (move.x != 0 || move.z != 0);
|
||||
}
|
||||
|
||||
if (!isMoving && !isJumping) {
|
||||
reward();
|
||||
return;
|
||||
}
|
||||
|
||||
if (flag()) {
|
||||
if (!isNoSetbackPermission())
|
||||
closeInventory();
|
||||
|
||||
StringJoiner joiner = new StringJoiner(" ");
|
||||
|
||||
if (isMoving) joiner.add("moving");
|
||||
if (isJumping) joiner.add("jumping");
|
||||
if (inVehicle) joiner.add("inVehicle");
|
||||
|
||||
alert(joiner.toString());
|
||||
}
|
||||
} else {
|
||||
horseJumpVerbose = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private MoveVectorData findMovement(VectorData vectorData) {
|
||||
if (vectorData instanceof MoveVectorData) {
|
||||
return (MoveVectorData) vectorData;
|
||||
}
|
||||
|
||||
while (vectorData != null) {
|
||||
vectorData = vectorData.lastVector;
|
||||
if (vectorData instanceof MoveVectorData) {
|
||||
return (MoveVectorData) vectorData;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package ac.grim.grimac.checks.impl.inventory;
|
||||
|
||||
import ac.grim.grimac.checks.CheckData;
|
||||
import ac.grim.grimac.checks.type.InventoryCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.event.PacketSendEvent;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
|
||||
@CheckData(name = "InventoryE", setback = 3, description = "Sent a held item change packet while inventory is open")
|
||||
public class InventoryE extends InventoryCheck {
|
||||
private long lastTransaction = Long.MAX_VALUE; // Impossible transaction ID
|
||||
|
||||
public InventoryE(GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
super.onPacketReceive(event);
|
||||
|
||||
if (event.getPacketType() == PacketType.Play.Client.HELD_ITEM_CHANGE) {
|
||||
// It is not possible to change hotbar slots with held item change while the inventory is open
|
||||
// A container click packet would be sent instead
|
||||
if (player.hasInventoryOpen) {
|
||||
if (this.lastTransaction < player.lastTransactionReceived.get()
|
||||
&& flagAndAlert()) {
|
||||
// Cancel the packet
|
||||
if (shouldModifyPackets()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
player.inventory.needResend = true;
|
||||
}
|
||||
if (!isNoSetbackPermission()) {
|
||||
closeInventory();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
reward();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPacketSend(PacketSendEvent event) {
|
||||
if (event.getPacketType() == PacketType.Play.Server.HELD_ITEM_CHANGE) {
|
||||
this.lastTransaction = player.lastTransactionSent.get();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package ac.grim.grimac.checks.impl.inventory;
|
||||
|
||||
import ac.grim.grimac.checks.CheckData;
|
||||
import ac.grim.grimac.checks.type.InventoryCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.inventory.InventoryDesyncStatus;
|
||||
import com.github.retrooper.packetevents.PacketEvents;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.manager.server.ServerVersion;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
|
||||
@CheckData(name = "InventoryF", setback = 3, description = "Sent a click window packet without a open inventory", experimental = true)
|
||||
public class InventoryF extends InventoryCheck {
|
||||
|
||||
public InventoryF(GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
// Exempt on 1.9+ server version due to the Via hack done in PacketPlayerWindow, the exemption can be deleted
|
||||
// once we are ahead of ViaVersion
|
||||
if (PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_9)
|
||||
|| player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_9)) return;
|
||||
|
||||
super.onPacketReceive(event);
|
||||
|
||||
if (event.getPacketType() == PacketType.Play.Client.CLICK_WINDOW) {
|
||||
if (!player.hasInventoryOpen && player.inventoryDesyncStatus == InventoryDesyncStatus.NOT_DESYNCED) {
|
||||
if (flagAndAlert()) {
|
||||
// Cancel the packet
|
||||
if (shouldModifyPackets()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
if (!isNoSetbackPermission()) {
|
||||
closeInventory();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
reward();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package ac.grim.grimac.checks.impl.inventory;
|
||||
|
||||
import ac.grim.grimac.checks.CheckData;
|
||||
import ac.grim.grimac.checks.type.InventoryCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientEntityAction;
|
||||
|
||||
@CheckData(name = "InventoryG", setback = 3, description = "Sent a entity action packet while inventory is open", experimental = true)
|
||||
public class InventoryG extends InventoryCheck {
|
||||
|
||||
public InventoryG(GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
if (player.packetStateData.lastPacketWasTeleport) return;
|
||||
super.onPacketReceive(event);
|
||||
|
||||
if (event.getPacketType() == PacketType.Play.Client.ENTITY_ACTION) {
|
||||
WrapperPlayClientEntityAction wrapper = new WrapperPlayClientEntityAction(event);
|
||||
WrapperPlayClientEntityAction.Action action = wrapper.getAction();
|
||||
|
||||
if (action == WrapperPlayClientEntityAction.Action.STOP_SNEAKING
|
||||
|| action == WrapperPlayClientEntityAction.Action.STOP_SPRINTING) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (player.hasInventoryOpen) {
|
||||
if (flagAndAlert() && !isNoSetbackPermission()) {
|
||||
closeInventory();
|
||||
}
|
||||
} else {
|
||||
reward();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -41,10 +41,6 @@ public class GhostBlockMitigation extends BlockPlaceCheck {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (i == xAgainst && j == yAgainst && k == zAgainst) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!world.isChunkLoaded(i >> 4, k >> 4)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -21,15 +21,7 @@ import java.util.ArrayDeque;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import static com.github.retrooper.packetevents.protocol.packettype.PacketType.Play.Client.ANIMATION;
|
||||
import static com.github.retrooper.packetevents.protocol.packettype.PacketType.Play.Client.CLICK_WINDOW;
|
||||
import static com.github.retrooper.packetevents.protocol.packettype.PacketType.Play.Client.ENTITY_ACTION;
|
||||
import static com.github.retrooper.packetevents.protocol.packettype.PacketType.Play.Client.HELD_ITEM_CHANGE;
|
||||
import static com.github.retrooper.packetevents.protocol.packettype.PacketType.Play.Client.INTERACT_ENTITY;
|
||||
import static com.github.retrooper.packetevents.protocol.packettype.PacketType.Play.Client.PLAYER_ABILITIES;
|
||||
import static com.github.retrooper.packetevents.protocol.packettype.PacketType.Play.Client.PLAYER_BLOCK_PLACEMENT;
|
||||
import static com.github.retrooper.packetevents.protocol.packettype.PacketType.Play.Client.PLAYER_DIGGING;
|
||||
import static com.github.retrooper.packetevents.protocol.packettype.PacketType.Play.Client.USE_ITEM;
|
||||
import static com.github.retrooper.packetevents.protocol.packettype.PacketType.Play.Client.*;
|
||||
|
||||
@CheckData(name = "Post")
|
||||
public class Post extends Check implements PacketCheck, PostPredictionCheck {
|
||||
@@ -107,6 +99,7 @@ public class Post extends Check implements PacketCheck, PostPredictionCheck {
|
||||
if (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_19_3) && player.inVehicle()) {
|
||||
return;
|
||||
}
|
||||
if (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_13) && new WrapperPlayClientEntityAction(event).getAction() == WrapperPlayClientEntityAction.Action.LEAVE_BED) return;
|
||||
if (sentFlying) post.add(event.getPacketType());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,8 +6,10 @@ import ac.grim.grimac.checks.type.PacketCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.protocol.player.DiggingAction;
|
||||
import com.github.retrooper.packetevents.protocol.player.InteractionHand;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientInteractEntity;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerDigging;
|
||||
|
||||
@CheckData(name = "MultiActionsA", description = "Attacked while using an item", experimental = true)
|
||||
public class MultiActionsA extends Check implements PacketCheck {
|
||||
@@ -17,8 +19,9 @@ public class MultiActionsA extends Check implements PacketCheck {
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
if (player.packetStateData.isSlowedByUsingItem() && (player.packetStateData.lastSlotSelected == player.packetStateData.getSlowedByUsingItemSlot() || player.packetStateData.itemInUseHand == InteractionHand.OFF_HAND) && event.getPacketType() == PacketType.Play.Client.INTERACT_ENTITY) {
|
||||
if (new WrapperPlayClientInteractEntity(event).getAction() == WrapperPlayClientInteractEntity.InteractAction.ATTACK) {
|
||||
if (player.packetStateData.isSlowedByUsingItem() && (player.packetStateData.lastSlotSelected == player.packetStateData.getSlowedByUsingItemSlot() || player.packetStateData.itemInUseHand == InteractionHand.OFF_HAND)) {
|
||||
if (event.getPacketType() == PacketType.Play.Client.INTERACT_ENTITY && new WrapperPlayClientInteractEntity(event).getAction() == WrapperPlayClientInteractEntity.InteractAction.ATTACK
|
||||
|| event.getPacketType() == PacketType.Play.Client.PLAYER_DIGGING && new WrapperPlayClientPlayerDigging(event).getAction() == DiggingAction.STAB) {
|
||||
if (flagAndAlert() && shouldModifyPackets()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
|
||||
@@ -39,12 +39,15 @@ public class MultiActionsC extends Check implements PacketCheck {
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
if (event.getPacketType() == PacketType.Play.Client.CLICK_WINDOW && !player.serverOpenedInventoryThisTick) {
|
||||
String verbose = getVerbose(player);
|
||||
if (!verbose.isEmpty() && flagAndAlert(verbose) && shouldModifyPackets()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
if (event.getPacketType() != PacketType.Play.Client.CLICK_WINDOW) return;
|
||||
if (player.serverOpenedInventoryThisTick) return;
|
||||
|
||||
String verbose = getVerbose(player);
|
||||
if (verbose.isEmpty()) return;
|
||||
|
||||
if (flagAndAlert(verbose) && shouldModifyPackets()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,12 +15,15 @@ public class MultiActionsD extends Check implements PacketCheck {
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
if (event.getPacketType() == PacketType.Play.Client.CLOSE_WINDOW) {
|
||||
String verbose = MultiActionsC.getVerbose(player);
|
||||
if (!verbose.isEmpty() && flagAndAlert(verbose) && shouldModifyPackets()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
}
|
||||
if (event.getPacketType() != PacketType.Play.Client.CLOSE_WINDOW) return;
|
||||
if (player.serverOpenedInventoryThisTick) return;
|
||||
|
||||
String verbose = MultiActionsC.getVerbose(player);
|
||||
if (verbose.isEmpty()) return;
|
||||
|
||||
// Don't cancel this packet, because it won't do anything except for making chests
|
||||
// look like they are still open (desynced),
|
||||
// and it can cause incompatibility issues with plugins
|
||||
flagAndAlert(verbose);
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,12 @@ 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.DiggingAction;
|
||||
import com.github.retrooper.packetevents.protocol.player.GameMode;
|
||||
import com.github.retrooper.packetevents.protocol.player.InteractionHand;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientAnimation;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientInteractEntity;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerDigging;
|
||||
|
||||
@CheckData(name = "PacketOrderB", description = "Did not swing for attack")
|
||||
public class PacketOrderB extends Check implements PacketCheck {
|
||||
@@ -41,7 +46,8 @@ public class PacketOrderB extends Check implements PacketCheck {
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
if (exempt) return;
|
||||
|
||||
if (event.getPacketType() == PacketType.Play.Client.ANIMATION) {
|
||||
if (event.getPacketType() == PacketType.Play.Client.ANIMATION
|
||||
&& new WrapperPlayClientAnimation(event).getHand() == InteractionHand.MAIN_HAND) {
|
||||
sentAnimationSinceLastAttack = sentAnimation = true;
|
||||
sentAttack = sentSlotSwitch = false;
|
||||
return;
|
||||
@@ -50,17 +56,15 @@ public class PacketOrderB extends Check implements PacketCheck {
|
||||
if (event.getPacketType() == PacketType.Play.Client.INTERACT_ENTITY) {
|
||||
WrapperPlayClientInteractEntity packet = new WrapperPlayClientInteractEntity(event);
|
||||
if (packet.getAction() == WrapperPlayClientInteractEntity.InteractAction.ATTACK) {
|
||||
sentAttack = true;
|
||||
onAttack(event);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (is1_9 ? !sentAnimationSinceLastAttack : !sentAnimation) {
|
||||
sentAttack = false; // don't flag twice
|
||||
if (flagAndAlert("pre-attack") && shouldModifyPackets()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
}
|
||||
|
||||
sentAnimationSinceLastAttack = sentAnimation = sentSlotSwitch = false;
|
||||
if (event.getPacketType() == PacketType.Play.Client.PLAYER_DIGGING) {
|
||||
WrapperPlayClientPlayerDigging packet = new WrapperPlayClientPlayerDigging(event);
|
||||
if (packet.getAction() == DiggingAction.STAB) {
|
||||
onAttack(event);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -78,4 +82,20 @@ public class PacketOrderB extends Check implements PacketCheck {
|
||||
sentAttack = sentAnimation = sentSlotSwitch = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void onAttack(PacketReceiveEvent event) {
|
||||
if (player.gamemode == GameMode.SPECTATOR && player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_21_11)) return;
|
||||
|
||||
sentAttack = true;
|
||||
|
||||
if (is1_9 ? !sentAnimationSinceLastAttack : !sentAnimation) {
|
||||
sentAttack = false; // don't flag twice
|
||||
if (flagAndAlert("pre-attack") && shouldModifyPackets()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
}
|
||||
|
||||
sentAnimationSinceLastAttack = sentAnimation = sentSlotSwitch = false;
|
||||
}
|
||||
}
|
||||
@@ -15,8 +15,7 @@ import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPl
|
||||
|
||||
@CheckData(name = "PacketOrderC")
|
||||
public class PacketOrderC 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 final boolean exempt = player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_7_10); // 1.7 players do not send INTERACT_AT
|
||||
private boolean sentInteractAt = false;
|
||||
private int requiredEntity;
|
||||
private InteractionHand requiredHand;
|
||||
|
||||
@@ -39,12 +39,16 @@ public class PacketOrderD extends Check implements PacketCheck {
|
||||
}
|
||||
}
|
||||
sentMainhand = false;
|
||||
} else if (sneaking != requiredSneaking || entity != requiredEntity) {
|
||||
String verbose = "requiredEntity=" + requiredEntity + ", entity=" + entity
|
||||
+ ", requiredSneaking=" + requiredSneaking + ", sneaking=" + sneaking;
|
||||
if (flagAndAlert(verbose) && shouldModifyPackets()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
|
||||
if (action == InteractAction.INTERACT_AT) {
|
||||
if (sneaking != requiredSneaking || entity != requiredEntity) {
|
||||
String verbose = "requiredEntity=" + requiredEntity + ", entity=" + entity
|
||||
+ ", requiredSneaking=" + requiredSneaking + ", sneaking=" + sneaking;
|
||||
if (flagAndAlert(verbose) && shouldModifyPackets()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -22,7 +22,7 @@ public class PacketOrderE extends Check implements PostPredictionCheck {
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
if (event.getPacketType() == PacketType.Play.Client.HELD_ITEM_CHANGE) {
|
||||
if (player.packetOrderProcessor.isAttacking()
|
||||
if (player.packetOrderProcessor.isAttackingOrStabbing()
|
||||
|| player.packetOrderProcessor.isRightClicking()
|
||||
|| player.packetOrderProcessor.isOpeningInventory()
|
||||
|| player.packetOrderProcessor.isReleasing()
|
||||
@@ -32,7 +32,7 @@ public class PacketOrderE extends Check implements PostPredictionCheck {
|
||||
|| player.packetOrderProcessor.isStartingToGlide()
|
||||
|| player.packetOrderProcessor.isJumpingWithMount()
|
||||
) {
|
||||
String verbose = "attacking=" + player.packetOrderProcessor.isAttacking()
|
||||
String verbose = "attacking=" + player.packetOrderProcessor.isAttackingOrStabbing()
|
||||
+ ", rightClicking=" + player.packetOrderProcessor.isRightClicking()
|
||||
+ ", openingInventory=" + player.packetOrderProcessor.isOpeningInventory()
|
||||
+ ", releasing=" + player.packetOrderProcessor.isReleasing()
|
||||
|
||||
@@ -7,7 +7,6 @@ import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.protocol.player.DiggingAction;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientClientStatus;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerDigging;
|
||||
|
||||
@@ -42,7 +41,7 @@ public class PacketOrderF extends Check implements PostPredictionCheck {
|
||||
if (!player.canSkipTicks()) {
|
||||
if (flagAndAlert(verbose) && shouldModifyPackets()) {
|
||||
if (event.getPacketType() == PacketType.Play.Client.PLAYER_DIGGING
|
||||
&& new WrapperPlayClientPlayerDigging(event).getAction() == DiggingAction.RELEASE_USE_ITEM
|
||||
&& !canCancel(new WrapperPlayClientPlayerDigging(event).getAction())
|
||||
) return; // don't cause a noslow
|
||||
|
||||
event.setCancelled(true);
|
||||
|
||||
@@ -28,27 +28,26 @@ public class PacketOrderG extends Check implements PostPredictionCheck {
|
||||
DiggingAction action = null;
|
||||
if (event.getPacketType() == PacketType.Play.Client.PLAYER_DIGGING) {
|
||||
action = new WrapperPlayClientPlayerDigging(event).getAction();
|
||||
if (action == DiggingAction.RELEASE_USE_ITEM
|
||||
|| action == DiggingAction.START_DIGGING
|
||||
|| action == DiggingAction.CANCELLED_DIGGING
|
||||
|| action == DiggingAction.FINISHED_DIGGING
|
||||
if (action != DiggingAction.SWAP_ITEM_WITH_OFFHAND
|
||||
&& action != DiggingAction.DROP_ITEM
|
||||
&& action != DiggingAction.DROP_ITEM_STACK
|
||||
) return;
|
||||
}
|
||||
|
||||
if (player.packetOrderProcessor.isAttacking()
|
||||
if (player.packetOrderProcessor.isAttackingOrStabbing()
|
||||
|| player.packetOrderProcessor.isReleasing()
|
||||
|| player.packetOrderProcessor.isRightClicking()
|
||||
|| player.packetOrderProcessor.isPicking()
|
||||
|| player.packetOrderProcessor.isDigging()
|
||||
) {
|
||||
String verbose = "action=" + (action == null ? "openInventory" : action == DiggingAction.SWAP_ITEM_WITH_OFFHAND ? "swap" : "drop")
|
||||
+ ", attacking=" + player.packetOrderProcessor.isAttacking()
|
||||
+ ", attacking=" + player.packetOrderProcessor.isAttackingOrStabbing()
|
||||
+ ", releasing=" + player.packetOrderProcessor.isReleasing()
|
||||
+ ", rightClicking=" + player.packetOrderProcessor.isRightClicking()
|
||||
+ ", picking=" + player.packetOrderProcessor.isPicking()
|
||||
+ ", digging=" + player.packetOrderProcessor.isDigging();
|
||||
if (!player.canSkipTicks()) {
|
||||
if (flagAndAlert(verbose) && shouldModifyPackets()) {
|
||||
if (flagAndAlert(verbose) && shouldModifyPackets() && canCancel(action)) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
|
||||
@@ -32,20 +32,7 @@ public class PacketOrderI extends Check implements PostPredictionCheck {
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
if (event.getPacketType() == PacketType.Play.Client.INTERACT_ENTITY) {
|
||||
if (new WrapperPlayClientInteractEntity(event).getAction() == WrapperPlayClientInteractEntity.InteractAction.ATTACK) {
|
||||
if (player.packetOrderProcessor.isRightClicking() || player.packetOrderProcessor.isPicking() || player.packetOrderProcessor.isReleasing() || player.packetOrderProcessor.isDigging()) {
|
||||
String verbose = "type=attack, rightClicking=" + player.packetOrderProcessor.isRightClicking()
|
||||
+ ", picking=" + player.packetOrderProcessor.isPicking()
|
||||
+ ", releasing=" + player.packetOrderProcessor.isReleasing()
|
||||
+ ", digging=" + player.packetOrderProcessor.isDigging();
|
||||
if (!player.canSkipTicks()) {
|
||||
if (flagAndAlert(verbose) && shouldModifyPackets()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
} else {
|
||||
flags.add(verbose);
|
||||
}
|
||||
}
|
||||
onAttack(event);
|
||||
} else if (player.packetOrderProcessor.isReleasing() || player.packetOrderProcessor.isDigging()) {
|
||||
String verbose = "type=interact, releasing=" + player.packetOrderProcessor.isReleasing() + ", digging=" + player.packetOrderProcessor.isDigging();
|
||||
if (!player.canSkipTicks()) {
|
||||
@@ -77,9 +64,12 @@ public class PacketOrderI extends Check implements PostPredictionCheck {
|
||||
WrapperPlayClientPlayerDigging packet = new WrapperPlayClientPlayerDigging(event);
|
||||
|
||||
switch (packet.getAction()) {
|
||||
case STAB:
|
||||
onAttack(event);
|
||||
break;
|
||||
case RELEASE_USE_ITEM:
|
||||
if (player.packetOrderProcessor.isAttacking() || player.packetOrderProcessor.isRightClicking() || player.packetOrderProcessor.isPicking() || player.packetOrderProcessor.isDigging()) {
|
||||
String verbose = "type=release, attacking=" + player.packetOrderProcessor.isAttacking()
|
||||
if (player.packetOrderProcessor.isAttackingOrStabbing() || player.packetOrderProcessor.isRightClicking() || player.packetOrderProcessor.isPicking() || player.packetOrderProcessor.isDigging()) {
|
||||
String verbose = "type=release, attacking=" + player.packetOrderProcessor.isAttackingOrStabbing()
|
||||
+ ", rightClicking=" + player.packetOrderProcessor.isRightClicking()
|
||||
+ ", picking=" + player.packetOrderProcessor.isPicking()
|
||||
+ ", digging=" + player.packetOrderProcessor.isDigging();
|
||||
@@ -106,7 +96,7 @@ public class PacketOrderI extends Check implements PostPredictionCheck {
|
||||
}
|
||||
}
|
||||
|
||||
if (player.gamemode == GameMode.SPECTATOR || isTickPacket(event.getPacketType())) {
|
||||
if (!player.cameraEntity.isSelf() || isTickPacket(event.getPacketType())) {
|
||||
digging = false;
|
||||
}
|
||||
}
|
||||
@@ -134,6 +124,23 @@ public class PacketOrderI extends Check implements PostPredictionCheck {
|
||||
setback = false;
|
||||
}
|
||||
|
||||
private void onAttack(PacketReceiveEvent event) {
|
||||
if (player.packetOrderProcessor.isRightClicking() || player.packetOrderProcessor.isPicking() || player.packetOrderProcessor.isReleasing() || player.packetOrderProcessor.isDigging()) {
|
||||
String verbose = "type=attack" + ", rightClicking=" + player.packetOrderProcessor.isRightClicking()
|
||||
+ ", picking=" + player.packetOrderProcessor.isPicking()
|
||||
+ ", releasing=" + player.packetOrderProcessor.isReleasing()
|
||||
+ ", digging=" + player.packetOrderProcessor.isDigging();
|
||||
if (!player.canSkipTicks()) {
|
||||
if (flagAndAlert(verbose) && shouldModifyPackets()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
} else {
|
||||
flags.add(verbose);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReload(ConfigManager config) {
|
||||
exemptPlacingWhileDigging = config.getBooleanElse(getConfigName() + ".exempt-placing-while-digging", false);
|
||||
|
||||
@@ -19,6 +19,7 @@ public class PacketOrderJ extends Check implements PostPredictionCheck {
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
if (event.getPacketType() == PacketType.Play.Client.PLAYER_BLOCK_PLACEMENT || event.getPacketType() == PacketType.Play.Client.USE_ITEM) {
|
||||
// we don't check stabbing here because you don't need to target an entity to stab
|
||||
if (player.packetOrderProcessor.isAttacking() && !player.packetOrderProcessor.isInteracting()) {
|
||||
if (!player.canSkipTicks()) {
|
||||
if (flagAndAlert() && shouldModifyPackets()) {
|
||||
|
||||
@@ -7,7 +7,6 @@ import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.protocol.player.GameMode;
|
||||
import com.github.retrooper.packetevents.protocol.world.BlockFace;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientInteractEntity;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerBlockPlacement;
|
||||
@@ -49,7 +48,7 @@ public class PacketOrderM extends Check implements PostPredictionCheck {
|
||||
interacting = false;
|
||||
}
|
||||
|
||||
if (player.gamemode == GameMode.SPECTATOR || isTickPacket(event.getPacketType())) {
|
||||
if (!player.cameraEntity.isSelf() || isTickPacket(event.getPacketType())) {
|
||||
usingWithoutInteract = interacting = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import ac.grim.grimac.utils.anticheat.update.BlockPlace;
|
||||
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.protocol.player.GameMode;
|
||||
import com.github.retrooper.packetevents.protocol.world.BlockFace;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerBlockPlacement;
|
||||
|
||||
@@ -46,7 +45,7 @@ public class PacketOrderN extends BlockPlaceCheck {
|
||||
placing = false;
|
||||
}
|
||||
|
||||
if (player.gamemode == GameMode.SPECTATOR || isTickPacket(event.getPacketType())) {
|
||||
if (!player.cameraEntity.isSelf() || isTickPacket(event.getPacketType())) {
|
||||
usingWithoutPlacing = placing = false;
|
||||
}
|
||||
}
|
||||
|
||||
+9
-2
@@ -8,7 +8,6 @@ import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketTypeCommon;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import com.github.retrooper.packetevents.protocol.player.GameMode;
|
||||
import com.github.retrooper.packetevents.protocol.world.BlockFace;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.*;
|
||||
import lombok.Getter;
|
||||
@@ -39,6 +38,7 @@ public final class PacketOrderProcessor extends Check implements PacketCheck {
|
||||
private boolean leavingBed;
|
||||
private boolean startingToGlide;
|
||||
private boolean jumpingWithMount;
|
||||
private boolean stabbing;
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
@@ -64,6 +64,7 @@ public final class PacketOrderProcessor extends Check implements PacketCheck {
|
||||
case DROP_ITEM, DROP_ITEM_STACK -> dropping = true;
|
||||
case RELEASE_USE_ITEM -> releasing = true;
|
||||
case FINISHED_DIGGING, CANCELLED_DIGGING, START_DIGGING -> digging = true;
|
||||
case STAB -> stabbing = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,7 +112,7 @@ public final class PacketOrderProcessor extends Check implements PacketCheck {
|
||||
closingInventory = true;
|
||||
}
|
||||
|
||||
if (player.gamemode == GameMode.SPECTATOR || isTickPacket(packetType)
|
||||
if (!player.cameraEntity.isSelf() || isTickPacket(packetType)
|
||||
|| player.getClientVersion().isOlderThan(ClientVersion.V_1_21_2)
|
||||
&& !player.compensatedWorld.isChunkLoaded(GrimMath.floor(player.x) >> 4, GrimMath.floor(player.z) >> 4)) {
|
||||
openingInventory = false;
|
||||
@@ -133,6 +134,7 @@ public final class PacketOrderProcessor extends Check implements PacketCheck {
|
||||
leavingBed = false;
|
||||
startingToGlide = false;
|
||||
jumpingWithMount = false;
|
||||
stabbing = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,4 +142,9 @@ public final class PacketOrderProcessor extends Check implements PacketCheck {
|
||||
public boolean isRightClicking() {
|
||||
return placing || using || interacting;
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
public boolean isAttackingOrStabbing() {
|
||||
return attacking || stabbing;
|
||||
}
|
||||
}
|
||||
@@ -133,8 +133,14 @@ public class DebugHandler extends AbstractDebugHandler implements PostPrediction
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toggleListener(GrimPlayer player) {
|
||||
if (!listeners.remove(player)) listeners.add(player);
|
||||
public boolean toggleListener(GrimPlayer player) {
|
||||
boolean wasPresent = listeners.remove(player);
|
||||
if (wasPresent) {
|
||||
return false;
|
||||
} else {
|
||||
listeners.add(player);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -10,6 +10,29 @@ import com.github.retrooper.packetevents.util.Vector3f;
|
||||
|
||||
@CheckData(name = "FabricatedPlace", description = "Sent out of bounds cursor position")
|
||||
public class FabricatedPlace extends BlockPlaceCheck {
|
||||
|
||||
/**
|
||||
* MAX_DOUBLE_ERROR:
|
||||
* Represents the maximum possible floating-point arithmetic error that can occur
|
||||
* when calculating vectors at the Minecraft World Border (30,000,000 blocks).
|
||||
*
|
||||
* Math.ulp(30,000,000.0) is approx 3.72E-9.
|
||||
* We multiply by 2.0 to account for subtraction compounding logic in raytracing.
|
||||
*
|
||||
* This constant is safe to use everywhere; 7.450580596923828E-9 is physically indistinguishable from zero.
|
||||
*/
|
||||
private static final double MAX_DOUBLE_ERROR = Math.ulp(30_000_000.0) * 2.0;
|
||||
|
||||
/**
|
||||
* FLOAT_STEP_AT_ONE:
|
||||
* Represents the resolution of a Float at the value 1.0 (and 1.5).
|
||||
* Math.ulp(1.0f) is approx 1.1920929E-7.
|
||||
*
|
||||
* When checking the upper bound (1.0), we must allow this variance because a Double
|
||||
* slightly larger than 1.0 might get cast/snapped to the next available Float step.
|
||||
*/
|
||||
private static final double FLOAT_STEP_AT_ONE = Math.ulp(1.0f);
|
||||
|
||||
public FabricatedPlace(GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
@@ -19,11 +42,50 @@ public class FabricatedPlace extends BlockPlaceCheck {
|
||||
Vector3f cursor = place.cursor;
|
||||
if (cursor == null) return;
|
||||
|
||||
double allowed = Materials.isShapeExceedsCube(place.getPlacedAgainstMaterial()) || place.getPlacedAgainstMaterial() == StateTypes.LECTERN ? 1.5 : 1;
|
||||
double minAllowed = 1 - allowed;
|
||||
// Determine if we allow up to 1.5 (Lecterns, Scaffolding, etc)
|
||||
boolean isExtended = Materials.isShapeExceedsCube(place.getPlacedAgainstMaterial())
|
||||
|| place.getPlacedAgainstMaterial() == StateTypes.LECTERN;
|
||||
|
||||
if (cursor.getX() < minAllowed || cursor.getY() < minAllowed || cursor.getZ() < minAllowed || cursor.getX() > allowed || cursor.getY() > allowed || cursor.getZ() > allowed) {
|
||||
if (flagAndAlert() && shouldModifyPackets() && shouldCancel()) {
|
||||
double maxBound = isExtended ? 1.5 : 1.0;
|
||||
double minBound = 1.0 - maxBound; // Usually 0.0
|
||||
|
||||
// ====================================================================================
|
||||
// LOWER BOUND CHECK (< 0.0)
|
||||
// ====================================================================================
|
||||
// Why only MAX_DOUBLE_ERROR?
|
||||
// Near 0.0, 'float' has extremely high resolution (down to E-45).
|
||||
// It acts like a double. When the client has a tiny calculation error (e.g. -4.44E-16),
|
||||
// the float cast preserves it exactly. We only need to account for the arithmetic noise.
|
||||
if (cursor.getX() < minBound - MAX_DOUBLE_ERROR ||
|
||||
cursor.getY() < minBound - MAX_DOUBLE_ERROR ||
|
||||
cursor.getZ() < minBound - MAX_DOUBLE_ERROR) {
|
||||
|
||||
// Alert logic
|
||||
String debug = String.format("cursor=%s limit=%.16f", cursor, minBound - MAX_DOUBLE_ERROR);
|
||||
if (flagAndAlert(debug) && shouldModifyPackets() && shouldCancel()) {
|
||||
place.resync();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// ====================================================================================
|
||||
// UPPER BOUND CHECK (> 1.0 or > 1.5)
|
||||
// ====================================================================================
|
||||
// Near 1.0, the "Float Step" is E-7. This is 16x larger than the Double error.
|
||||
// If the calculation is slightly off, it might snap to the NEXT float.
|
||||
// We tolerate exactly one "Float Step" of overflow.
|
||||
double upperTolerance = FLOAT_STEP_AT_ONE;
|
||||
|
||||
// Note: Effectively Math.max(MAX_DOUBLE_ERROR, FLOAT_STEP_AT_ONE)
|
||||
// but since FLOAT_STEP is always larger in MC coordinates, we just use it.
|
||||
|
||||
if (cursor.getX() > maxBound + upperTolerance ||
|
||||
cursor.getY() > maxBound + upperTolerance ||
|
||||
cursor.getZ() > maxBound + upperTolerance) {
|
||||
|
||||
// Alert logic
|
||||
String debug = String.format("cursor=%s limit=%.16f", cursor, maxBound + upperTolerance);
|
||||
if (flagAndAlert(debug) && shouldModifyPackets() && shouldCancel()) {
|
||||
place.resync();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
|
||||
import ac.grim.grimac.utils.math.Vector3dm;
|
||||
import ac.grim.grimac.utils.math.VectorUtils;
|
||||
import com.github.retrooper.packetevents.protocol.attribute.Attributes;
|
||||
import com.github.retrooper.packetevents.protocol.player.GameMode;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
|
||||
import com.github.retrooper.packetevents.util.Vector3i;
|
||||
|
||||
@@ -20,7 +19,7 @@ public class FarPlace extends BlockPlaceCheck {
|
||||
|
||||
@Override
|
||||
public void onBlockPlace(final BlockPlace place) {
|
||||
if (player.gamemode == GameMode.SPECTATOR || player.inVehicle()) return;
|
||||
if (!player.cameraEntity.isSelf() || player.inVehicle()) return;
|
||||
|
||||
Vector3i blockPos = place.position;
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ import ac.grim.grimac.utils.anticheat.MessageUtil;
|
||||
import ac.grim.grimac.utils.anticheat.update.BlockPlace;
|
||||
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.player.GameMode;
|
||||
import com.github.retrooper.packetevents.protocol.world.BlockFace;
|
||||
import com.github.retrooper.packetevents.util.Vector3f;
|
||||
import com.github.retrooper.packetevents.util.Vector3i;
|
||||
@@ -54,7 +53,7 @@ public class MultiPlace extends BlockPlaceCheck {
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
if (player.gamemode == GameMode.SPECTATOR || isTickPacket(event.getPacketType())) {
|
||||
if (!player.cameraEntity.isSelf() || isTickPacket(event.getPacketType())) {
|
||||
hasPlaced = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ import ac.grim.grimac.utils.nmsutil.Ray;
|
||||
import ac.grim.grimac.utils.nmsutil.ReachUtils;
|
||||
import com.github.retrooper.packetevents.protocol.attribute.Attributes;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import com.github.retrooper.packetevents.protocol.player.GameMode;
|
||||
import com.github.retrooper.packetevents.protocol.world.BlockFace;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
|
||||
import com.github.retrooper.packetevents.util.Vector3d;
|
||||
@@ -34,7 +33,7 @@ public class RotationPlace extends BlockPlaceCheck {
|
||||
@Override
|
||||
public void onBlockPlace(final BlockPlace place) {
|
||||
if (place.material == StateTypes.SCAFFOLDING) return;
|
||||
if (player.gamemode == GameMode.SPECTATOR)
|
||||
if (!player.cameraEntity.isSelf())
|
||||
return; // you don't send flying packets when spectating entities
|
||||
if (player.inVehicle()) return;
|
||||
if (flagBuffer > 0 && !didRayTraceHit(place)) {
|
||||
@@ -50,7 +49,7 @@ public class RotationPlace extends BlockPlaceCheck {
|
||||
@Override
|
||||
public void onPostFlyingBlockPlace(BlockPlace place) {
|
||||
if (place.material == StateTypes.SCAFFOLDING) return;
|
||||
if (player.gamemode == GameMode.SPECTATOR)
|
||||
if (!player.cameraEntity.isSelf())
|
||||
return; // you don't send flying packets when spectating entities
|
||||
if (player.inVehicle()) return;
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ public class VehicleD extends Check implements PacketCheck {
|
||||
if (event.getPacketType() == PacketType.Play.Client.ENTITY_ACTION && new WrapperPlayClientEntityAction(event).getAction() == WrapperPlayClientEntityAction.Action.START_JUMPING_WITH_HORSE) {
|
||||
final EntityType vehicle = player.getVehicleType();
|
||||
|
||||
if (!EntityTypes.isTypeInstanceOf(vehicle, EntityTypes.ABSTRACT_HORSE)) {
|
||||
if (!EntityTypes.isTypeInstanceOf(vehicle, EntityTypes.ABSTRACT_HORSE) && !EntityTypes.isTypeInstanceOf(vehicle, EntityTypes.ABSTRACT_NAUTILUS)) {
|
||||
if (flagAndAlert("vehicle=" + (vehicle == null ? "null" : vehicle.getName().getKey().toLowerCase())) && shouldModifyPackets()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
package ac.grim.grimac.checks.type;
|
||||
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import com.github.retrooper.packetevents.PacketEvents;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientCloseWindow;
|
||||
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerCloseWindow;
|
||||
import org.jetbrains.annotations.MustBeInvokedByOverriders;
|
||||
|
||||
public class InventoryCheck extends BlockPlaceCheck implements PacketCheck {
|
||||
// Impossible transaction ID
|
||||
protected static final long NONE = Long.MAX_VALUE;
|
||||
|
||||
protected long closeTransaction = NONE;
|
||||
protected int closePacketsToSkip;
|
||||
|
||||
public InventoryCheck(GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
@MustBeInvokedByOverriders
|
||||
public void onPacketReceive(final PacketReceiveEvent event) {
|
||||
if (event.getPacketType() == PacketType.Play.Client.CLICK_WINDOW) {
|
||||
// Disallow any clicks if inventory is closing
|
||||
if (closeTransaction != NONE && shouldModifyPackets()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
player.inventory.needResend = true;
|
||||
}
|
||||
} else if (event.getPacketType() == PacketType.Play.Client.CLOSE_WINDOW) {
|
||||
// Players with high ping can close inventory faster than send transaction back
|
||||
if (closeTransaction != NONE && closePacketsToSkip-- <= 0) {
|
||||
closeTransaction = NONE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void closeInventory() {
|
||||
if (closeTransaction != NONE) {
|
||||
return;
|
||||
}
|
||||
|
||||
int windowId = player.inventory.openWindowID;
|
||||
|
||||
player.user.writePacket(new WrapperPlayServerCloseWindow(windowId));
|
||||
|
||||
// Force close inventory on server side
|
||||
closePacketsToSkip = 1; // Sending close packet to itself, so skip it
|
||||
PacketEvents.getAPI().getProtocolManager().receivePacket(
|
||||
player.user.getChannel(), new WrapperPlayClientCloseWindow(windowId)
|
||||
);
|
||||
|
||||
player.sendTransaction();
|
||||
|
||||
int transaction = player.lastTransactionSent.get();
|
||||
closeTransaction = transaction;
|
||||
player.latencyUtils.addRealTimeTask(transaction, () -> {
|
||||
if (closeTransaction == transaction) {
|
||||
closeTransaction = NONE;
|
||||
}
|
||||
});
|
||||
|
||||
player.user.flushPackets();
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
package ac.grim.grimac.command;
|
||||
|
||||
import ac.grim.grimac.platform.api.manager.cloud.CloudCommandAdapter;
|
||||
import ac.grim.grimac.platform.api.sender.Sender;
|
||||
import org.incendo.cloud.CommandManager;
|
||||
|
||||
public interface BuildableCommand {
|
||||
void register(CommandManager<Sender> manager);
|
||||
void register(CommandManager<Sender> manager, CloudCommandAdapter adapter);
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package ac.grim.grimac.command;
|
||||
|
||||
import ac.grim.grimac.command.commands.*;
|
||||
import ac.grim.grimac.command.handler.GrimCommandFailureHandler;
|
||||
import ac.grim.grimac.platform.api.command.CommandService;
|
||||
import ac.grim.grimac.platform.api.manager.cloud.CloudCommandAdapter;
|
||||
import ac.grim.grimac.platform.api.sender.Sender;
|
||||
import ac.grim.grimac.utils.anticheat.MessageUtil;
|
||||
import io.leangen.geantyref.TypeToken;
|
||||
import net.kyori.adventure.text.ComponentLike;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import org.incendo.cloud.CommandManager;
|
||||
import org.incendo.cloud.exception.InvalidSyntaxException;
|
||||
import org.incendo.cloud.key.CloudKey;
|
||||
import org.incendo.cloud.processors.requirements.RequirementApplicable;
|
||||
import org.incendo.cloud.processors.requirements.RequirementApplicable.RequirementApplicableFactory;
|
||||
import org.incendo.cloud.processors.requirements.RequirementPostprocessor;
|
||||
import org.incendo.cloud.processors.requirements.Requirements;
|
||||
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class CloudCommandService implements CommandService {
|
||||
|
||||
public static final CloudKey<Requirements<Sender, SenderRequirement>> REQUIREMENT_KEY
|
||||
= CloudKey.of("requirements", new TypeToken<>() {});
|
||||
|
||||
public static final RequirementApplicableFactory<Sender, SenderRequirement> REQUIREMENT_FACTORY
|
||||
= RequirementApplicable.factory(REQUIREMENT_KEY);
|
||||
|
||||
private boolean commandsRegistered = false;
|
||||
|
||||
private final Supplier<CommandManager<Sender>> commandManagerSupplier;
|
||||
private final CloudCommandAdapter commandAdapter;
|
||||
|
||||
public CloudCommandService(Supplier<CommandManager<Sender>> commandManagerSupplier, CloudCommandAdapter commandAdapter) {
|
||||
this.commandManagerSupplier = commandManagerSupplier;
|
||||
this.commandAdapter = commandAdapter;
|
||||
}
|
||||
|
||||
public void registerCommands() {
|
||||
if (commandsRegistered) return;
|
||||
CommandManager<Sender> commandManager = commandManagerSupplier.get();
|
||||
new GrimPerf().register(commandManager, commandAdapter);
|
||||
new GrimDebug().register(commandManager, commandAdapter);
|
||||
new GrimAlerts().register(commandManager, commandAdapter);
|
||||
new GrimProfile().register(commandManager, commandAdapter);
|
||||
new GrimSendAlert().register(commandManager, commandAdapter);
|
||||
new GrimHelp().register(commandManager, commandAdapter);
|
||||
new GrimHistory().register(commandManager, commandAdapter);
|
||||
new GrimReload().register(commandManager, commandAdapter);
|
||||
new GrimSpectate().register(commandManager, commandAdapter);
|
||||
new GrimStopSpectating().register(commandManager, commandAdapter);
|
||||
new GrimLog().register(commandManager, commandAdapter);
|
||||
new GrimVerbose().register(commandManager, commandAdapter);
|
||||
new GrimVersion().register(commandManager, commandAdapter);
|
||||
new GrimDump().register(commandManager, commandAdapter);
|
||||
new GrimBrands().register(commandManager, commandAdapter);
|
||||
new GrimList().register(commandManager, commandAdapter);
|
||||
new GrimTestWebhook().register(commandManager, commandAdapter);
|
||||
|
||||
final RequirementPostprocessor<Sender, SenderRequirement>
|
||||
senderRequirementPostprocessor = RequirementPostprocessor.of(
|
||||
REQUIREMENT_KEY,
|
||||
new GrimCommandFailureHandler()
|
||||
);
|
||||
commandManager.registerCommandPostProcessor(senderRequirementPostprocessor);
|
||||
registerExceptionHandler(commandManager, InvalidSyntaxException.class, e -> MessageUtil.miniMessage(e.correctSyntax()));
|
||||
commandsRegistered = true;
|
||||
}
|
||||
|
||||
protected <E extends Exception> void registerExceptionHandler(CommandManager<Sender> commandManager, Class<E> ex, Function<E, ComponentLike> toComponent) {
|
||||
commandManager.exceptionController().registerHandler(ex,
|
||||
(c) -> c.context().sender().sendMessage(toComponent.apply(c.exception()).asComponent().colorIfAbsent(NamedTextColor.RED))
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package ac.grim.grimac.command.commands;
|
||||
|
||||
import ac.grim.grimac.GrimAPI;
|
||||
import ac.grim.grimac.command.BuildableCommand;
|
||||
import ac.grim.grimac.platform.api.manager.cloud.CloudCommandAdapter;
|
||||
import ac.grim.grimac.platform.api.sender.Sender;
|
||||
import org.incendo.cloud.CommandManager;
|
||||
import org.incendo.cloud.context.CommandContext;
|
||||
@@ -12,7 +13,7 @@ import java.util.Objects;
|
||||
|
||||
public class GrimAlerts implements BuildableCommand {
|
||||
@Override
|
||||
public void register(CommandManager<Sender> commandManager) {
|
||||
public void register(CommandManager<Sender> commandManager, CloudCommandAdapter adapter) {
|
||||
commandManager.command(
|
||||
commandManager.commandBuilder("grim", "grimac")
|
||||
.literal("alerts", Description.of("Toggle alerts for the sender"))
|
||||
|
||||
@@ -2,6 +2,7 @@ package ac.grim.grimac.command.commands;
|
||||
|
||||
import ac.grim.grimac.GrimAPI;
|
||||
import ac.grim.grimac.command.BuildableCommand;
|
||||
import ac.grim.grimac.platform.api.manager.cloud.CloudCommandAdapter;
|
||||
import ac.grim.grimac.platform.api.sender.Sender;
|
||||
import org.incendo.cloud.CommandManager;
|
||||
import org.incendo.cloud.context.CommandContext;
|
||||
@@ -12,7 +13,7 @@ import java.util.Objects;
|
||||
|
||||
public class GrimBrands implements BuildableCommand {
|
||||
@Override
|
||||
public void register(CommandManager<Sender> commandManager) {
|
||||
public void register(CommandManager<Sender> commandManager, CloudCommandAdapter adapter) {
|
||||
commandManager.command(
|
||||
commandManager.commandBuilder("grim", "grimac")
|
||||
.literal("brands", Description.of("Toggle brands for the sender"))
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package ac.grim.grimac.command.commands;
|
||||
|
||||
import ac.grim.grimac.GrimAPI;
|
||||
import ac.grim.grimac.checks.debug.HitboxDebugHandler;
|
||||
import ac.grim.grimac.command.BuildableCommand;
|
||||
import ac.grim.grimac.platform.api.command.PlayerSelector;
|
||||
import ac.grim.grimac.platform.api.manager.cloud.CloudCommandAdapter;
|
||||
import ac.grim.grimac.platform.api.sender.Sender;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.anticheat.MessageUtil;
|
||||
@@ -19,26 +21,33 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class GrimDebug implements BuildableCommand {
|
||||
|
||||
public void register(CommandManager<Sender> commandManager) {
|
||||
public void register(CommandManager<Sender> commandManager, CloudCommandAdapter adapter) {
|
||||
Command.Builder<Sender> grimCommand = commandManager.commandBuilder("grim", "grimac");
|
||||
|
||||
// Register "debug" subcommand
|
||||
Command.Builder<Sender> debugCommand = grimCommand
|
||||
.literal("debug", Description.of("Toggle debug output for a player"))
|
||||
.permission("grim.debug")
|
||||
.optional("target", GrimAPI.INSTANCE.getCommandAdapter().singlePlayerSelectorParser())
|
||||
.optional("target", adapter.singlePlayerSelectorParser())
|
||||
.handler(this::handleDebug);
|
||||
|
||||
// Register "consoledebug" subcommand
|
||||
Command.Builder<Sender> consoleDebugCommand = grimCommand
|
||||
.literal("consoledebug", Description.of("Toggle console debug output for a player"))
|
||||
.permission("grim.consoledebug")
|
||||
.required("target", GrimAPI.INSTANCE.getCommandAdapter().singlePlayerSelectorParser())
|
||||
.required("target", adapter.singlePlayerSelectorParser())
|
||||
.handler(this::handleConsoleDebug);
|
||||
|
||||
Command.Builder<Sender> hitboxDebugCommand = grimCommand
|
||||
.literal("hitboxdebug", Description.of("Toggle hitbox debug visualization"))
|
||||
.permission("grim.hitboxdebug")
|
||||
.optional("target", adapter.singlePlayerSelectorParser(), Description.of("Player to debug (defaults to self if sender is player)"))
|
||||
.handler(this::handleHitboxDebug);
|
||||
|
||||
// Register command
|
||||
commandManager.command(debugCommand);
|
||||
commandManager.command(consoleDebugCommand);
|
||||
commandManager.command(hitboxDebugCommand);
|
||||
}
|
||||
|
||||
private void handleDebug(@NotNull CommandContext<Sender> context) {
|
||||
@@ -88,6 +97,48 @@ public class GrimDebug implements BuildableCommand {
|
||||
sender.sendMessage(message);
|
||||
}
|
||||
|
||||
private void handleHitboxDebug(@NotNull CommandContext<Sender> context) {
|
||||
Sender sender = context.sender();
|
||||
PlayerSelector playerSelector = context.getOrDefault("target", null);
|
||||
|
||||
// Hitbox debug requires a *player* to be the listener (the one seeing the boxes)
|
||||
if (!sender.isPlayer()) {
|
||||
sender.sendMessage(MessageUtil.getParsedComponent(sender,
|
||||
"hitboxdebug-player-only",
|
||||
"%prefix% &cHitbox debug can only be toggled by players.")
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Determine the target player whose hitboxes are being debugged
|
||||
GrimPlayer targetGrimPlayer = parseTarget(sender, playerSelector == null ? sender : playerSelector.getSinglePlayer());
|
||||
if (targetGrimPlayer == null) return;
|
||||
|
||||
// Get the sender's GrimPlayer data, as they are the listener
|
||||
GrimPlayer senderGrimPlayer = GrimAPI.INSTANCE.getPlayerDataManager().getPlayer(sender.getUniqueId());
|
||||
if (senderGrimPlayer == null) {
|
||||
sender.sendMessage(Component.text("Could not find your player data to register as a listener.", NamedTextColor.RED));
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the HitboxDebugHandler check instance and toggle the listener
|
||||
HitboxDebugHandler hitboxHandler = targetGrimPlayer.checkManager.getCheck(HitboxDebugHandler.class);
|
||||
if (hitboxHandler == null) {
|
||||
sender.sendMessage(Component.text("HitboxDebugHandler check not found for target player.", NamedTextColor.RED));
|
||||
return;
|
||||
}
|
||||
|
||||
boolean enabled = hitboxHandler.toggleListener(senderGrimPlayer); // Pass the sender/listener
|
||||
|
||||
// Send feedback message
|
||||
Component message = Component.text()
|
||||
.append(Component.text("Hitbox debug listener for ", NamedTextColor.GRAY))
|
||||
.append(Component.text(targetGrimPlayer.getName(), NamedTextColor.WHITE))
|
||||
.append(Component.text(enabled ? " enabled." : " disabled.", NamedTextColor.GRAY))
|
||||
.build();
|
||||
sender.sendMessage(message);
|
||||
}
|
||||
|
||||
private @Nullable GrimPlayer parseTarget(@NotNull Sender sender, @Nullable Sender t) {
|
||||
if (sender.isConsole() && t == null) {
|
||||
sender.sendMessage(MessageUtil.getParsedComponent(sender, "console-specify-target", "%prefix% &cYou must specify a target as the console!"));
|
||||
|
||||
@@ -3,6 +3,7 @@ package ac.grim.grimac.command.commands;
|
||||
import ac.grim.grimac.GrimAPI;
|
||||
import ac.grim.grimac.command.BuildableCommand;
|
||||
import ac.grim.grimac.platform.api.PlatformPlugin;
|
||||
import ac.grim.grimac.platform.api.manager.cloud.CloudCommandAdapter;
|
||||
import ac.grim.grimac.platform.api.sender.Sender;
|
||||
import ac.grim.grimac.utils.anticheat.MessageUtil;
|
||||
import ac.grim.grimac.utils.common.PropertiesUtil;
|
||||
@@ -29,7 +30,7 @@ public class GrimDump implements BuildableCommand {
|
||||
private String link = null; // these links should not expire for a while
|
||||
|
||||
@Override
|
||||
public void register(CommandManager<Sender> commandManager) {
|
||||
public void register(CommandManager<Sender> commandManager, CloudCommandAdapter adapter) {
|
||||
commandManager.command(
|
||||
commandManager.commandBuilder("grim", "grimac")
|
||||
.literal("dump", Description.of("Generate a debug dump"))
|
||||
|
||||
@@ -2,6 +2,7 @@ package ac.grim.grimac.command.commands;
|
||||
|
||||
import ac.grim.grimac.GrimAPI;
|
||||
import ac.grim.grimac.command.BuildableCommand;
|
||||
import ac.grim.grimac.platform.api.manager.cloud.CloudCommandAdapter;
|
||||
import ac.grim.grimac.platform.api.sender.Sender;
|
||||
import ac.grim.grimac.utils.anticheat.MessageUtil;
|
||||
import org.incendo.cloud.CommandManager;
|
||||
@@ -11,7 +12,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class GrimHelp implements BuildableCommand {
|
||||
@Override
|
||||
public void register(CommandManager<Sender> commandManager) {
|
||||
public void register(CommandManager<Sender> commandManager, CloudCommandAdapter adapter) {
|
||||
commandManager.command(
|
||||
commandManager.commandBuilder("grim", "grimac")
|
||||
.literal("help", Description.of("Display help information"))
|
||||
|
||||
@@ -4,6 +4,7 @@ import ac.grim.grimac.GrimAPI;
|
||||
import ac.grim.grimac.command.BuildableCommand;
|
||||
import ac.grim.grimac.manager.violationdatabase.Violation;
|
||||
import ac.grim.grimac.manager.violationdatabase.ViolationDatabaseManager;
|
||||
import ac.grim.grimac.platform.api.manager.cloud.CloudCommandAdapter;
|
||||
import ac.grim.grimac.platform.api.player.OfflinePlatformPlayer;
|
||||
import ac.grim.grimac.platform.api.sender.Sender;
|
||||
import ac.grim.grimac.utils.anticheat.MessageUtil;
|
||||
@@ -18,12 +19,12 @@ import java.util.concurrent.TimeUnit;
|
||||
public class GrimHistory implements BuildableCommand {
|
||||
|
||||
@Override
|
||||
public void register(CommandManager<Sender> commandManager) {
|
||||
public void register(CommandManager<Sender> commandManager, CloudCommandAdapter adapter) {
|
||||
commandManager.command(
|
||||
commandManager.commandBuilder("grim", "grimac")
|
||||
.literal("history", "hist")
|
||||
.permission("grim.help")
|
||||
.required("target", StringParser.stringParser(), GrimAPI.INSTANCE.getCommandAdapter().onlinePlayerSuggestions())
|
||||
.required("target", StringParser.stringParser(), adapter.onlinePlayerSuggestions())
|
||||
.optional("page", IntegerParser.integerParser())
|
||||
.permission("grim.history")
|
||||
.handler(this::handleHistory)
|
||||
|
||||
@@ -4,6 +4,7 @@ import ac.grim.grimac.GrimAPI;
|
||||
import ac.grim.grimac.api.GrimIdentity;
|
||||
import ac.grim.grimac.command.BuildableCommand;
|
||||
import ac.grim.grimac.command.CommandUtils;
|
||||
import ac.grim.grimac.platform.api.manager.cloud.CloudCommandAdapter;
|
||||
import ac.grim.grimac.platform.api.player.PlatformPlayer;
|
||||
import ac.grim.grimac.platform.api.sender.Sender;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
@@ -28,7 +29,7 @@ public class GrimList implements BuildableCommand {
|
||||
// Mainly for debugging purposes. Useful for seeing which players are exempt or not.
|
||||
|
||||
@Override
|
||||
public void register(CommandManager<Sender> commandManager) {
|
||||
public void register(CommandManager<Sender> commandManager, CloudCommandAdapter adapter) {
|
||||
commandManager.command(commandManager.commandBuilder("grim", "grimac")
|
||||
.literal("list")
|
||||
.permission("grim.list")
|
||||
|
||||
@@ -3,6 +3,7 @@ package ac.grim.grimac.command.commands;
|
||||
import ac.grim.grimac.GrimAPI;
|
||||
import ac.grim.grimac.command.BuildableCommand;
|
||||
import ac.grim.grimac.manager.init.start.SuperDebug;
|
||||
import ac.grim.grimac.platform.api.manager.cloud.CloudCommandAdapter;
|
||||
import ac.grim.grimac.platform.api.sender.Sender;
|
||||
import ac.grim.grimac.utils.anticheat.LogUtil;
|
||||
import ac.grim.grimac.utils.anticheat.MessageUtil;
|
||||
@@ -44,7 +45,7 @@ public class GrimLog implements BuildableCommand {
|
||||
try {
|
||||
urlConn.setDoOutput(true);
|
||||
urlConn.setRequestMethod("POST");
|
||||
urlConn.addRequestProperty("User-Agent", "GrimAC/" + GrimAPI.INSTANCE.getExternalAPI().getGrimVersion());
|
||||
urlConn.addRequestProperty("User-Agent", "LightningGrim/" + GrimAPI.INSTANCE.getExternalAPI().getGrimVersion());
|
||||
urlConn.addRequestProperty("Content-Type", type); // Not really yaml, but looks nicer than plaintext
|
||||
urlConn.setRequestProperty("Content-Length", Integer.toString(log.length()));
|
||||
try (OutputStream stream = urlConn.getOutputStream()) {
|
||||
@@ -68,7 +69,7 @@ public class GrimLog implements BuildableCommand {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register(CommandManager<Sender> commandManager) {
|
||||
public void register(CommandManager<Sender> commandManager, CloudCommandAdapter adapter) {
|
||||
Command<Sender> command = commandManager.commandBuilder("grim", "grimac")
|
||||
.literal("log", "logs")
|
||||
.permission("grim.log")
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package ac.grim.grimac.command.commands;
|
||||
|
||||
import ac.grim.grimac.platform.api.manager.cloud.CloudCommandAdapter;
|
||||
import ac.grim.grimac.platform.api.sender.Sender;
|
||||
import ac.grim.grimac.predictionengine.MovementCheckRunner;
|
||||
import net.kyori.adventure.text.Component;
|
||||
@@ -11,7 +12,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class GrimPerf {
|
||||
|
||||
public void register(CommandManager<Sender> commandManager) {
|
||||
public void register(CommandManager<Sender> commandManager, CloudCommandAdapter adapter) {
|
||||
Command.Builder<Sender> grimCommand = commandManager.commandBuilder("grim", "grimac");
|
||||
|
||||
Command.Builder<Sender> configuredBuilder = grimCommand
|
||||
|
||||
@@ -3,6 +3,7 @@ package ac.grim.grimac.command.commands;
|
||||
import ac.grim.grimac.GrimAPI;
|
||||
import ac.grim.grimac.command.BuildableCommand;
|
||||
import ac.grim.grimac.platform.api.command.PlayerSelector;
|
||||
import ac.grim.grimac.platform.api.manager.cloud.CloudCommandAdapter;
|
||||
import ac.grim.grimac.platform.api.player.PlatformPlayer;
|
||||
import ac.grim.grimac.platform.api.sender.Sender;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
@@ -16,12 +17,12 @@ import java.util.Objects;
|
||||
|
||||
public class GrimProfile implements BuildableCommand {
|
||||
@Override
|
||||
public void register(CommandManager<Sender> commandManager) {
|
||||
public void register(CommandManager<Sender> commandManager, CloudCommandAdapter adapter) {
|
||||
commandManager.command(
|
||||
commandManager.commandBuilder("grim", "grimac")
|
||||
.literal("profile")
|
||||
.permission("grim.profile")
|
||||
.required("target", GrimAPI.INSTANCE.getCommandAdapter().singlePlayerSelectorParser())
|
||||
.required("target", adapter.singlePlayerSelectorParser())
|
||||
.handler(this::handleProfile)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package ac.grim.grimac.command.commands;
|
||||
|
||||
import ac.grim.grimac.GrimAPI;
|
||||
import ac.grim.grimac.command.BuildableCommand;
|
||||
import ac.grim.grimac.platform.api.manager.cloud.CloudCommandAdapter;
|
||||
import ac.grim.grimac.platform.api.sender.Sender;
|
||||
import ac.grim.grimac.utils.anticheat.MessageUtil;
|
||||
import net.kyori.adventure.text.Component;
|
||||
@@ -11,7 +12,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class GrimReload implements BuildableCommand {
|
||||
@Override
|
||||
public void register(CommandManager<Sender> commandManager) {
|
||||
public void register(CommandManager<Sender> commandManager, CloudCommandAdapter adapter) {
|
||||
commandManager.command(
|
||||
commandManager.commandBuilder("grim", "grimac")
|
||||
.literal("reload")
|
||||
|
||||
@@ -2,6 +2,7 @@ package ac.grim.grimac.command.commands;
|
||||
|
||||
import ac.grim.grimac.GrimAPI;
|
||||
import ac.grim.grimac.command.BuildableCommand;
|
||||
import ac.grim.grimac.platform.api.manager.cloud.CloudCommandAdapter;
|
||||
import ac.grim.grimac.platform.api.sender.Sender;
|
||||
import ac.grim.grimac.utils.anticheat.MessageUtil;
|
||||
import net.kyori.adventure.text.Component;
|
||||
@@ -12,7 +13,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class GrimSendAlert implements BuildableCommand {
|
||||
@Override
|
||||
public void register(CommandManager<Sender> commandManager) {
|
||||
public void register(CommandManager<Sender> commandManager, CloudCommandAdapter adapter) {
|
||||
commandManager.command(
|
||||
commandManager.commandBuilder("grim", "grimac")
|
||||
.literal("sendalert")
|
||||
|
||||
@@ -2,9 +2,10 @@ package ac.grim.grimac.command.commands;
|
||||
|
||||
import ac.grim.grimac.GrimAPI;
|
||||
import ac.grim.grimac.command.BuildableCommand;
|
||||
import ac.grim.grimac.command.CloudCommandService;
|
||||
import ac.grim.grimac.command.requirements.PlayerSenderRequirement;
|
||||
import ac.grim.grimac.manager.init.start.CommandRegister;
|
||||
import ac.grim.grimac.platform.api.command.PlayerSelector;
|
||||
import ac.grim.grimac.platform.api.manager.cloud.CloudCommandAdapter;
|
||||
import ac.grim.grimac.platform.api.player.PlatformPlayer;
|
||||
import ac.grim.grimac.platform.api.sender.Sender;
|
||||
import ac.grim.grimac.utils.anticheat.MessageUtil;
|
||||
@@ -17,14 +18,14 @@ import java.util.Objects;
|
||||
|
||||
public class GrimSpectate implements BuildableCommand {
|
||||
@Override
|
||||
public void register(CommandManager<Sender> commandManager) {
|
||||
public void register(CommandManager<Sender> commandManager, CloudCommandAdapter adapter) {
|
||||
commandManager.command(
|
||||
commandManager.commandBuilder("grim", "grimac")
|
||||
.literal("spectate")
|
||||
.permission("grim.spectate")
|
||||
.required("target", GrimAPI.INSTANCE.getCommandAdapter().singlePlayerSelectorParser())
|
||||
.required("target", adapter.singlePlayerSelectorParser())
|
||||
.handler(this::handleSpectate)
|
||||
.apply(CommandRegister.REQUIREMENT_FACTORY.create(PlayerSenderRequirement.PLAYER_SENDER_REQUIREMENT))
|
||||
.apply(CloudCommandService.REQUIREMENT_FACTORY.create(PlayerSenderRequirement.PLAYER_SENDER_REQUIREMENT))
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Loaded 100 of 378 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user