Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ee36c2559a |
No files matched your search
+1
-1
@@ -5,4 +5,4 @@ name = "java"
|
||||
enabled = true
|
||||
|
||||
[analyzers.meta]
|
||||
runtime_version = "17"
|
||||
runtime_version = "16"
|
||||
@@ -1,19 +0,0 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_size = 4
|
||||
indent_style = space
|
||||
insert_final_newline = true
|
||||
max_line_length = 100
|
||||
tab_width = 4
|
||||
end_of_line = crlf
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
# intellij specific configuration
|
||||
ij_continuation_indent_size = 8
|
||||
ij_formatter_off_tag = @formatter:off
|
||||
ij_formatter_on_tag = @formatter:on
|
||||
ij_formatter_tags_enabled = false
|
||||
ij_smart_tabs = false
|
||||
ij_wrap_on_typing = false
|
||||
@@ -30,19 +30,11 @@ body:
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
attributes:
|
||||
label: Client version
|
||||
description: |
|
||||
Provide the version of your Minecraft client.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Plugins
|
||||
description: |
|
||||
List all plugins or mods running on your server. To list plugins on bukkit, run `/plugins`.
|
||||
List all plugins running on your server. To list plugins, run `/plugins`.
|
||||
If your server is behind a proxy such as Bungeecord, you should list the proxy's plugins as well.
|
||||
validations:
|
||||
required: true
|
||||
required: true
|
||||
@@ -1 +0,0 @@
|
||||
blank_issues_enabled: false
|
||||
@@ -39,23 +39,11 @@ body:
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: dropdown
|
||||
id: type
|
||||
attributes:
|
||||
label: Affected Platform(s)
|
||||
description: Which platforms does this issue affect? If you aren't sure, select both.
|
||||
options:
|
||||
- "Bukkit"
|
||||
- "Fabric"
|
||||
multiple: true
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Plugins/mods
|
||||
label: Plugins
|
||||
description: |
|
||||
List all plugins or mods running on your server. To list plugins on bukkit, run `/plugins`.
|
||||
List all plugins running on your server. To list plugins, run `/plugins`.
|
||||
If your server is behind a proxy such as Bungeecord, you should list the proxy's plugins as well.
|
||||
validations:
|
||||
required: true
|
||||
required: true
|
||||
@@ -46,7 +46,7 @@ body:
|
||||
attributes:
|
||||
label: Plugins
|
||||
description: |
|
||||
List all plugins or mods running on your server. To list plugins on bukkit, run `/plugins`.
|
||||
List all plugins running on your server. To list plugins, run `/plugins`.
|
||||
If your server is behind a proxy such as Bungeecord, you should list the proxy's plugins as well.
|
||||
validations:
|
||||
required: true
|
||||
required: true
|
||||
@@ -1,5 +1,5 @@
|
||||
name: Plugin/mod incompatibility or other bug
|
||||
description: Report a bug or plugin/mod incompatibility here
|
||||
name: Plugin incompatibility or other bug
|
||||
description: Report a bug or plugin incompatibility here
|
||||
labels: ["bug"]
|
||||
body:
|
||||
- type: textarea
|
||||
@@ -30,23 +30,11 @@ body:
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: dropdown
|
||||
id: type
|
||||
attributes:
|
||||
label: Affected Platform(s)
|
||||
description: Which platforms does this issue affect? If you aren't sure, select both.
|
||||
options:
|
||||
- "Bukkit"
|
||||
- "Fabric"
|
||||
multiple: true
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Plugins
|
||||
description: |
|
||||
List all plugins or mods running on your server. To list plugins on bukkit, run `/plugins`.
|
||||
List all plugins running on your server. To list plugins, run `/plugins`.
|
||||
If your server is behind a proxy such as Bungeecord, you should list the proxy's plugins as well.
|
||||
validations:
|
||||
required: true
|
||||
required: true
|
||||
@@ -45,7 +45,7 @@ body:
|
||||
attributes:
|
||||
label: Plugins
|
||||
description: |
|
||||
List all plugins or mods running on your server. To list plugins on bukkit, run `/plugins`.
|
||||
List all plugins running on your server. To list plugins, run `/plugins`.
|
||||
If your server is behind a proxy such as Bungeecord, you should list the proxy's plugins as well.
|
||||
validations:
|
||||
required: true
|
||||
required: true
|
||||
@@ -1,229 +0,0 @@
|
||||
name: Reusable Build and Publish
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
is-release-build:
|
||||
description: 'True if this is a release build, which affects versioning and publishing.'
|
||||
required: true
|
||||
type: boolean
|
||||
release-version-type:
|
||||
description: 'The version type for Modrinth (e.g., release, beta, alpha).'
|
||||
required: true
|
||||
type: string
|
||||
release-changelog:
|
||||
description: 'The changelog content for the release.'
|
||||
required: false
|
||||
type: string
|
||||
default: ''
|
||||
run-build-scan:
|
||||
description: 'Whether to enable Develocity build scan'
|
||||
required: true
|
||||
type: boolean
|
||||
publish-artifacts:
|
||||
description: 'If true, uploads artifacts to Modrinth and other distribution sites.'
|
||||
required: true
|
||||
type: boolean
|
||||
secrets:
|
||||
MODRINTH_ID:
|
||||
required: false
|
||||
MODRINTH_TOKEN:
|
||||
required: false
|
||||
BUILD_UPDATE_TOKEN:
|
||||
required: true
|
||||
BUILD_UPDATE_URL:
|
||||
required: true
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
env:
|
||||
MODRINTH_ID: ${{ secrets.MODRINTH_ID }}
|
||||
MODRINTH_TOKEN: ${{ secrets.MODRINTH_TOKEN }}
|
||||
GRIM_MAIN_BRANCH: ${{ github.event.repository.default_branch }}
|
||||
BUILD_UPDATE_TOKEN: ${{ secrets.BUILD_UPDATE_TOKEN }}
|
||||
|
||||
steps:
|
||||
- name: Checkout repository with full history
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
fetch-depth: 200
|
||||
|
||||
- name: Set up JDK 21
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: '21'
|
||||
distribution: 'temurin'
|
||||
server-id: github
|
||||
settings-path: ${{ github.workspace }}
|
||||
|
||||
- name: Setup Gradle
|
||||
uses: gradle/actions/setup-gradle@v3
|
||||
with:
|
||||
cache-read-only: ${{ github.ref_name != github.event.repository.default_branch && github.ref_name != 'dev' }}
|
||||
|
||||
- name: Cache Gradle Loom Cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: .gradle/loom-cache
|
||||
key: loom-cache-${{ runner.os }}-${{ hashFiles('**/libs.versions.toml', 'fabric/**/build.gradle.kts') }}
|
||||
restore-keys: |
|
||||
loom-cache-${{ runner.os }}
|
||||
|
||||
- name: Set Git Environment Variables
|
||||
run: |
|
||||
echo "GRIM_GIT_REPO=${{ github.event.repository.name }}" >> $GITHUB_ENV
|
||||
echo "GRIM_GIT_ORG=${{ github.repository_owner }}" >> $GITHUB_ENV
|
||||
|
||||
- name: Compute version and Build with Gradle
|
||||
id: build_with_version
|
||||
run: |
|
||||
# If any command in a pipeline fails, treat the pipeline as failed.
|
||||
set -o pipefail
|
||||
|
||||
BUILD_ARGS="build"
|
||||
if [[ "${{ inputs.is-release-build }}" == "true" ]]; then
|
||||
BUILD_ARGS="$BUILD_ARGS -Prelease=true"
|
||||
fi
|
||||
# Conditionally add the --scan flag
|
||||
if [[ "${{ inputs.run-build-scan }}" == "true" ]]; then
|
||||
BUILD_ARGS="$BUILD_ARGS --scan"
|
||||
fi
|
||||
|
||||
echo "Running: ./gradlew printVersion $BUILD_ARGS"
|
||||
|
||||
# Use 'tee' to both display the output live and save it to a file for parsing
|
||||
VERSION=$(./gradlew printVersion $BUILD_ARGS | tee build-output.log | grep '^VERSION=' | tail -n1 | cut -d'=' -f2)
|
||||
|
||||
echo "build_version=$VERSION" >> $GITHUB_OUTPUT
|
||||
echo "BUILD_VERSION=$VERSION" >> $GITHUB_ENV
|
||||
echo "Build version: $VERSION"
|
||||
|
||||
- name: Publish Build Scan URL
|
||||
# Only run this step if a scan was requested
|
||||
if: ${{ inputs.run-build-scan == true }}
|
||||
run: |
|
||||
# Search the log file for the build scan URL and extract it
|
||||
SCAN_URL=$(grep -o 'https://gradle.com/s/[^ ]*' build-output.log | head -n 1 || true)
|
||||
|
||||
if [[ -n "$SCAN_URL" ]]; then
|
||||
echo "Found build scan URL: $SCAN_URL"
|
||||
# Post a highly visible link to the Job Summary page
|
||||
echo "### Gradle Build Scan" >> $GITHUB_STEP_SUMMARY
|
||||
echo "A public build scan has been published and is available for inspection." >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "[**View Build Scan →**]($SCAN_URL)" >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
echo "Could not find build scan URL in build-output.log" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
- name: Upload Bukkit Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: grimac-bukkit
|
||||
path: ${{ github.workspace }}/bukkit/build/libs/grimac-bukkit-${{ env.BUILD_VERSION }}.jar
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Upload Fabric Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: grimac-fabric
|
||||
path: ${{ github.workspace }}/fabric/build/libs/grimac-fabric-${{ env.BUILD_VERSION }}.jar
|
||||
if-no-files-found: error
|
||||
|
||||
#------------------------------------------------------------------
|
||||
# Generate/Prepare Changelog
|
||||
#------------------------------------------------------------------
|
||||
- name: Generate Changelog with git-cliff (for Dev Builds)
|
||||
if: ${{ !inputs.is-release-build }}
|
||||
id: git-cliff
|
||||
uses: orhun/git-cliff-action@v4
|
||||
with:
|
||||
config: cliff.toml
|
||||
args: --verbose ${{ github.event.before }}..${{ github.event.after }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # git-cliff needs a token
|
||||
|
||||
- name: Prepare Final Changelog
|
||||
id: changelog
|
||||
# Add this 'env' block to safely load the changelogs as environment variables
|
||||
env:
|
||||
RELEASE_NOTES: ${{ inputs.release-changelog }}
|
||||
DEV_NOTES: ${{ steps.git-cliff.outputs.content }}
|
||||
run: |
|
||||
NOTES=""
|
||||
if [[ "${{ inputs.is-release-build }}" == "true" ]]; then
|
||||
# Use the environment variable for the release changelog.
|
||||
# The double quotes are critical to preserve newlines!
|
||||
NOTES="$RELEASE_NOTES"
|
||||
else
|
||||
# Use the environment variable for the dev changelog.
|
||||
NOTES="$DEV_NOTES"
|
||||
fi
|
||||
|
||||
if [ -z "$NOTES" ]; then
|
||||
NOTES="* No new code changes in this build."
|
||||
fi
|
||||
|
||||
# This is the correct way to set a multi-line output variable in Actions
|
||||
{
|
||||
echo "notes<<EOF"
|
||||
echo "$NOTES"
|
||||
echo "EOF"
|
||||
} >> "$GITHUB_OUTPUT"
|
||||
#------------------------------------------------------------------
|
||||
# Publish to Modrinth & API
|
||||
#------------------------------------------------------------------
|
||||
- name: Publish Bukkit to Modrinth
|
||||
if: ${{ inputs.publish-artifacts == true && env.MODRINTH_ID != '' && env.MODRINTH_TOKEN != '' }}
|
||||
uses: Kir-Antipov/mc-publish@v3.3
|
||||
with:
|
||||
modrinth-id: ${{ secrets.MODRINTH_ID }}
|
||||
modrinth-token: ${{ secrets.MODRINTH_TOKEN }}
|
||||
modrinth-featured: ${{ inputs.is-release-build }} # Only feature release builds
|
||||
|
||||
files: bukkit/build/libs/grimac-bukkit-${{ env.BUILD_VERSION }}.jar
|
||||
name: Grim Anticheat (Bukkit) ${{ env.BUILD_VERSION }}
|
||||
version: ${{ env.BUILD_VERSION }}
|
||||
version-type: ${{ inputs.release-version-type }}
|
||||
changelog: ${{ steps.changelog.outputs.notes }}
|
||||
loaders: |
|
||||
bukkit
|
||||
spigot
|
||||
paper
|
||||
folia
|
||||
purpur
|
||||
game-versions: '>=1.7 <=1.21.8'
|
||||
|
||||
- name: Publish Fabric to Modrinth
|
||||
if: ${{ inputs.publish-artifacts == true && env.MODRINTH_ID != '' && env.MODRINTH_TOKEN != '' }}
|
||||
uses: Kir-Antipov/mc-publish@v3.3
|
||||
with:
|
||||
modrinth-id: ${{ secrets.MODRINTH_ID }}
|
||||
modrinth-token: ${{ secrets.MODRINTH_TOKEN }}
|
||||
modrinth-featured: ${{ inputs.is-release-build }} # Only feature release builds
|
||||
|
||||
files: fabric/build/libs/grimac-fabric-${{ env.BUILD_VERSION }}.jar
|
||||
name: Grim Anticheat (Fabric) ${{ env.BUILD_VERSION }}
|
||||
version: ${{ env.BUILD_VERSION }}
|
||||
version-type: ${{ inputs.release-version-type }}
|
||||
changelog: ${{ steps.changelog.outputs.notes }}
|
||||
loaders: |
|
||||
fabric
|
||||
game-versions: '>=1.16.1 <=1.21.8'
|
||||
dependencies: 'fabric-api'
|
||||
|
||||
- name: Publish to API
|
||||
if: ${{ inputs.publish-artifacts == true && env.BUILD_UPDATE_TOKEN != '' }}
|
||||
run: |
|
||||
curl -X POST \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "User-Agent: GrimACBuilder/v1" \
|
||||
-H "Authorization: Bearer ${{ secrets.BUILD_UPDATE_TOKEN }}" \
|
||||
-d '{"org": "${{ github.repository_owner }}", "repository": "${{ github.event.repository.name }}", "branch": "${{ github.ref_name }}", "version": "${{ env.BUILD_VERSION }}"}' \
|
||||
"${{ secrets.BUILD_UPDATE_URL }}"
|
||||
@@ -12,9 +12,13 @@
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches: [ dev ]
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [ master ]
|
||||
schedule:
|
||||
- cron: '30 15 * * 2'
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
@@ -28,14 +32,14 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [ 'java-kotlin' ]
|
||||
language: [ 'java' ]
|
||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
|
||||
# Learn more:
|
||||
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v4
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
@@ -47,14 +51,6 @@ jobs:
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
# queries: ./path/to/local/query, your-org/your-repo/queries@main
|
||||
|
||||
- name: Set up JDK 21
|
||||
uses: actions/setup-java@v5
|
||||
with:
|
||||
java-version: '21'
|
||||
distribution: 'temurin'
|
||||
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
|
||||
settings-path: ${{ github.workspace }} # location for the settings.xml file
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
|
||||
@@ -1,44 +1,38 @@
|
||||
name: Gradle Package (Development)
|
||||
# This workflow uses actions that are not certified by GitHub.
|
||||
# They are provided by a third-party and are governed by
|
||||
# separate terms of service, privacy policy, and support
|
||||
# documentation.
|
||||
# This workflow will build a package using Gradle and then publish it to GitHub packages when a release is created
|
||||
# For more information see: https://github.com/actions/setup-java/blob/main/docs/advanced-usage.md#Publishing-using-gradle
|
||||
|
||||
name: Gradle Package
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
run-build-scan:
|
||||
description: 'Run with public build scan (adds --scan)'
|
||||
required: true
|
||||
type: boolean
|
||||
default: false
|
||||
publish-artifacts:
|
||||
description: 'Upload to Modrinth & API? (Uncheck for a dry run)'
|
||||
type: boolean
|
||||
default: true
|
||||
push:
|
||||
branches:
|
||||
- 'main'
|
||||
- '2.0'
|
||||
- 'dev'
|
||||
- 'release'
|
||||
- 'release/**'
|
||||
paths:
|
||||
- '.github/workflows/**'
|
||||
- 'buildSrc/**'
|
||||
- 'bukkit/**'
|
||||
- 'common/**'
|
||||
- 'fabric/**'
|
||||
- '**.java'
|
||||
- '**.kt'
|
||||
- '**.kts'
|
||||
- '**.toml'
|
||||
[push, workflow_dispatch]
|
||||
|
||||
jobs:
|
||||
call-build-workflow:
|
||||
uses: ./.github/workflows/build-and-publish.yml
|
||||
with:
|
||||
is-release-build: false
|
||||
release-version-type: alpha # Dev builds are always 'alpha'
|
||||
run-build-scan: ${{ github.event.inputs.run-build-scan == 'true' }}
|
||||
publish-artifacts: ${{ github.event.inputs.publish-artifacts != 'false' }}
|
||||
secrets:
|
||||
MODRINTH_ID: ${{ secrets.MODRINTH_ID }}
|
||||
MODRINTH_TOKEN: ${{ secrets.MODRINTH_TOKEN }}
|
||||
BUILD_UPDATE_TOKEN: ${{ secrets.BUILD_UPDATE_TOKEN }}
|
||||
BUILD_UPDATE_URL: ${{ secrets.BUILD_UPDATE_URL }}
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: '17'
|
||||
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: Build with Gradle
|
||||
uses: gradle/actions/setup-gradle@v3
|
||||
with:
|
||||
arguments: build
|
||||
- name: Upload a Build Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
path: /home/runner/work/Grim/Grim/build/libs/**.jar
|
||||
@@ -1,21 +0,0 @@
|
||||
name: Publish Release
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
jobs:
|
||||
call-release-workflow:
|
||||
uses: ./.github/workflows/build-and-publish.yml
|
||||
with:
|
||||
is-release-build: true
|
||||
release-version-type: release # Official releases are 'release'
|
||||
# Use the body of the GitHub Release as the changelog
|
||||
release-changelog: ${{ github.event.release.body }}
|
||||
run-build-scan: false
|
||||
publish-artifacts: true
|
||||
secrets:
|
||||
MODRINTH_ID: ${{ secrets.MODRINTH_ID }}
|
||||
MODRINTH_TOKEN: ${{ secrets.MODRINTH_TOKEN }}
|
||||
BUILD_UPDATE_TOKEN: ${{ secrets.BUILD_UPDATE_TOKEN }}
|
||||
BUILD_UPDATE_URL: ${{ secrets.BUILD_UPDATE_URL }}
|
||||
+1
-8
@@ -19,7 +19,6 @@ build/
|
||||
.gradletasknamecache
|
||||
gradle-app.setting
|
||||
!gradle-wrapper.jar
|
||||
buildSrc/.kotlin
|
||||
|
||||
##############################
|
||||
## IntelliJ
|
||||
@@ -66,10 +65,4 @@ nb-configuration.xml
|
||||
##############################
|
||||
## OS X
|
||||
##############################
|
||||
.DS_Store
|
||||
|
||||
##############################
|
||||
## Fabric
|
||||
##############################
|
||||
run/
|
||||
fabric/run/
|
||||
.DS_Store
|
||||
@@ -1,40 +0,0 @@
|
||||
# Contributing to GrimAC
|
||||
|
||||
Thank you for your interest in contributing to GrimAC. This document outlines the guidelines for
|
||||
making pull
|
||||
requests to the project. *We're usually pretty lenient with pull requests, but this guide will help
|
||||
make the process go more smoothly.*
|
||||
|
||||
### Pull Request Guidelines
|
||||
|
||||
- **Compatibility**
|
||||
- Any changes must be compatible with
|
||||
the [supported environments](https://github.com/GrimAnticheat/Grim/wiki/Supported-environments) (Spigot, Paper, Folia, Fabric, etc.)
|
||||
- The plugin must be compatible with minecraft versions 1.8 and higher. Exempting checks from specific versions is acceptable.
|
||||
- The plugin must be able to run on Java 17 or higher. Changes that don't support Java 17 at runtime will not be accepted.
|
||||
|
||||
- **Non-acceptable pull requests**
|
||||
- Heuristic-based checks will not be accepted; however, basic rate limiting such as blocking attacks based on CPS is acceptable.
|
||||
- Checks that can be easily circumvented that don't block packets or fix anything are likely to not be accepted.
|
||||
- Checks or features that are too specific to a single environment or minecraft version are likely to not be accepted.
|
||||
- Changes that require large or unnecessary dependencies will likely not be accepted.
|
||||
|
||||
- **Pull request formatting**
|
||||
- Create a new branch for your feature or fix when forking the repository.
|
||||
- Reference related issues in your pull request description if applicable.
|
||||
- Write clear and descriptive commit messages.
|
||||
|
||||
- **Code styling**
|
||||
- Add code comments for complex logic or significant changes.
|
||||
- Try to keep your code clean and avoid duplication.
|
||||
- Thoroughly test your changes before submitting your pull request.
|
||||
|
||||
### Development Notes
|
||||
|
||||
- GrimAC is built using [Gradle](https://gradle.org/) kotlin scripts.
|
||||
- Java 21 is currently required to build the project. A minimum of Java 17 is required to run it.
|
||||
|
||||
### Questions & Support
|
||||
|
||||
- Join our [Discord](https://discord.grim.ac) if you have questions or need assistance.
|
||||
- Refer to the [Wiki](https://github.com/GrimAnticheat/Grim/wiki) for project documentation.
|
||||
@@ -1,88 +1,50 @@
|
||||
<div align="center">
|
||||
<h1>GrimAC</h1>
|
||||
# GrimAC
|
||||
|
||||
<div>
|
||||
<a href="https://github.com/GrimAnticheat/Grim/actions/workflows/gradle-publish.yml">
|
||||
<img alt="Workflow" src="https://img.shields.io/github/actions/workflow/status/GrimAnticheat/Grim/gradle-publish.yml?style=flat&logo=github"/>
|
||||
</a>
|
||||
<a href="https://modrinth.com/plugin/grimac">
|
||||
<img alt="Modrinth" src="https://img.shields.io/modrinth/v/LJNGWSvH?style=flat&label=version&logo=modrinth">
|
||||
</a>
|
||||
<a href="https://modrinth.com/plugin/grimac#download">
|
||||
<img alt="Downloads" src="https://img.shields.io/modrinth/dt/LJNGWSvH?style=flat&logo=modrinth&label=downloads&link=https%3A%2F%2Fmodrinth.com%2Fplugin%2Fgrimac%23download">
|
||||
</a>
|
||||
<a href="https://discord.grim.ac">
|
||||
<img alt="Discord" src="https://img.shields.io/discord/811396969670901800?style=flat&label=discord&logo=discord">
|
||||
</a>
|
||||
</div>
|
||||
<br>
|
||||
</div>
|
||||
GrimAC is an open source Minecraft anticheat designed for 1.21 and supports 1.8-1.21. It is free while in beta. It will eventually become paid and/or will include offering additional subscription based paid checks. Geyser players are fully exempt.
|
||||
|
||||
GrimAC is an open source Minecraft anticheat designed to support the latest versions of Minecraft.
|
||||
It currently supports minecraft versions 1.8–1.21. Geyser players are fully exempt from the anticheat to prevent false positives.
|
||||
This project is considered feature-complete for the 2.0 (open-source) branch. If you would like a bug fix or enhancement and cannot sponsor the work, pull requests are welcome.
|
||||
A premium version is planned, which will offer additional subscription-based paid checks, such as heuristics.
|
||||
This project is considered feature complete for the 2.0 (open-source) branch of this project. If you would like a bugfix or enhancement and cannot sponsor the work, pull requests are welcome.
|
||||
|
||||
## Downloads
|
||||
- [Modrinth](https://modrinth.com/plugin/grimac)
|
||||
- [Hangar](https://hangar.papermc.io/GrimAnticheat/GrimAnticheat)
|
||||
- [SpigotMC](https://www.spigotmc.org/resources/grim-anticheat.99923/)
|
||||
- *For bleeding edge builds use* [Github artifacts](https://nightly.link/GrimAnticheat/Grim/workflows/gradle-publish/2.0/artifact.zip)
|
||||
|
||||
- Latest updates:
|
||||
- **[Modrinth](https://modrinth.com/plugin/grimac)** *(recommended)*
|
||||
- GitHub
|
||||
artifacts: [Bukkit](https://nightly.link/GrimAnticheat/Grim/workflows/gradle-publish/2.0/grimac-bukkit.zip), [Fabric](https://nightly.link/GrimAnticheat/Grim/workflows/gradle-publish/2.0/grimac-fabric.zip) *(bleeding edge)*
|
||||
- Major releases only:
|
||||
- ~~[Hangar](https://hangar.papermc.io/GrimAnticheat/GrimAnticheat)~~
|
||||
- ~~[SpigotMC](https://www.spigotmc.org/resources/grim-anticheat.99923/)~~
|
||||
## Installation notes
|
||||
> [!WARNING]
|
||||
> Java 17 is now required. More information [here](https://github.com/GrimAnticheat/Grim/wiki/Updating-to-Java-17).
|
||||
- Paper, Spigot, and Folia are currently supported.
|
||||
- If you use Geyser, place Floodgate on the backend server so grim can exempt bedrock players. Grim cannot access the Floodgate API if it is on the proxy.
|
||||
- If you use ViaVersion, it should be on the backend server as movement is highly dependent on client version.
|
||||
|
||||
## Resources
|
||||
## Support & wiki information
|
||||
- Support & discussion: [Discord](https://discord.com/invite/kqQAhTmkUF)
|
||||
- Report issues: [Issues](https://github.com/GrimAnticheat/Grim/issues/new/choose)
|
||||
- Wiki & examples: [Wiki](https://github.com/GrimAnticheat/Grim/wiki)
|
||||
|
||||
- For documentation and examples visit the [Wiki](https://github.com/GrimAnticheat/Grim/wiki).
|
||||
- For answers to commonly asked questions visit the [FAQ](https://github.com/GrimAnticheat/Grim/wiki/FAQ).
|
||||
- For community support and project discussion join our [Discord](https://discord.grim.ac).
|
||||
## Developer API
|
||||
Grim's API allows you to integrate Grim into your own plugins.
|
||||
- API repository: [GrimAPI](https://github.com/GrimAnticheat/GrimAPI)
|
||||
- Wiki info: [Wiki](https://github.com/GrimAnticheat/GrimAPI)
|
||||
|
||||
|
||||
## Pull Requests
|
||||
|
||||
See [Contributing](CONTRIBUTING.md) for more information about contributing and what our guidelines
|
||||
are.
|
||||
|
||||
|
||||
## Requirements & Installation
|
||||
|
||||
- Java 17 or higher. *For more details
|
||||
see [Updating-to-Java-17](https://github.com/GrimAnticheat/Grim/wiki/Updating-to-Java-17).*
|
||||
- A Spigot, Paper, Folia, or Fabric server environment. *For more details
|
||||
see [Supported-environments](https://github.com/GrimAnticheat/Grim/wiki/Supported-environments).*
|
||||
- If you use Geyser, place Floodgate on the backend server so grim can exempt bedrock players. Grim
|
||||
cannot access the Floodgate API if it is on the proxy.
|
||||
- If you use ViaVersion, it should be on the backend server as movement is highly dependent on
|
||||
client version.
|
||||
|
||||
## Developer Plugin API
|
||||
|
||||
Grim's plugin API allows you to integrate Grim into your own plugins. Visit
|
||||
the [plugin API repository](https://github.com/GrimAnticheat/GrimAPI) for the source code and more
|
||||
information.
|
||||
|
||||
## Compiling From Source
|
||||
## How to compile
|
||||
|
||||
1. `git clone https://github.com/GrimAnticheat/Grim.git`
|
||||
2. `cd Grim`
|
||||
3. `./gradlew build`
|
||||
4. The final jars will compile into the `<platform>/build/libs` folders
|
||||
4. The final jar will compile into the build/libs folder
|
||||
|
||||
## Grim Supremacy
|
||||
## Grim supremacy
|
||||
|
||||
What makes Grim stand out against other anticheats?
|
||||
|
||||
### Movement Simulation Engine
|
||||
|
||||
* We have a 1:1 replication of the player's possible movements
|
||||
* This covers everything from basic walking, swimming, knockback, cobwebs, to bubble columns
|
||||
* It even covers riding entities from boats to pigs to striders
|
||||
* This covers everything from basic walking, swimming, knockback, cobwebs, to bubble columns
|
||||
* It even covers riding entities from boats to pigs to striders
|
||||
* Built upon covering edge cases to confirm accuracy
|
||||
* 1.13+ clients on 1.13+ servers, 1.12- clients on 1.13+ servers, 1.13+ clients on 1.12- servers,
|
||||
and 1.12- clients on 1.12- servers are all supported regardless of the large technical changes
|
||||
between these versions.
|
||||
* 1.13+ clients on 1.13+ servers, 1.12- clients on 1.13+ servers, 1.13+ clients on 1.12- servers, and 1.12- clients on 1.12- servers are all supported regardless of the large technical changes between these versions.
|
||||
* The order of collisions depends on the client version and is correct
|
||||
* Accounts for minor bounding box differences between versions, for example:
|
||||
* Single glass panes will be a + shape for 1.7-1.8 players and * for 1.9+ players
|
||||
@@ -109,8 +71,7 @@ What makes Grim stand out against other anticheats?
|
||||
* Per player, the cache allows for multithreaded design
|
||||
* Sending players fake blocks with packets is safe and does not lead to falses
|
||||
* The world is recreated for each player to allow lag compensation
|
||||
* Client sided blocks cause no issues with packet based blocks. Block glitching does not false the
|
||||
anticheat.
|
||||
* Client sided blocks cause no issues with packet based blocks. Block glitching does not false the anticheat.
|
||||
|
||||
### Latency compensation
|
||||
|
||||
|
||||
+194
-37
@@ -1,49 +1,206 @@
|
||||
/**
|
||||
* GrimAC Build Configuration
|
||||
*
|
||||
* Build Flags:
|
||||
* -PshadePE=true - Enables 'lite' mode
|
||||
* -Prelocate=false - Adds 'no_relocate' modifier
|
||||
* -Prelease=true - Removes commit/modifiers for release build
|
||||
*
|
||||
* Logic in: buildSrc/versioning/BuildConfig.kt & VersionUtil.kt
|
||||
*/
|
||||
import net.minecrell.pluginyml.bukkit.BukkitPluginDescription.Permission
|
||||
|
||||
import versioning.BuildConfig
|
||||
import versioning.VersionUtil
|
||||
plugins {
|
||||
id("java")
|
||||
id("maven-publish")
|
||||
id("com.github.johnrengelman.shadow") version "8.1.1"
|
||||
id("io.freefair.lombok") version "8.6"
|
||||
id("net.minecrell.plugin-yml.bukkit") version "0.6.0"
|
||||
id("com.diffplug.spotless") version "6.25.0"
|
||||
}
|
||||
|
||||
BuildConfig.init(project)
|
||||
spotless {
|
||||
java {
|
||||
endWithNewline()
|
||||
indentWithSpaces(4)
|
||||
removeUnusedImports()
|
||||
trimTrailingWhitespace()
|
||||
targetExclude("build/generated/**/*")
|
||||
}
|
||||
|
||||
kotlinGradle {
|
||||
endWithNewline()
|
||||
indentWithSpaces(4)
|
||||
trimTrailingWhitespace()
|
||||
}
|
||||
}
|
||||
|
||||
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."
|
||||
version = "2.3.69"
|
||||
description = "Libre simulation anticheat designed for 1.21 with 1.8-1.21 support, powered by PacketEvents 2.0."
|
||||
|
||||
ext["timestamp"] = System.currentTimeMillis().toString()
|
||||
ext["git_branch"] = VersionUtil.getGitBranch(true)
|
||||
ext["git_commit"] = VersionUtil.getGitCommitHash(true)
|
||||
ext["git_org"] = System.getenv("GRIM_GIT_ORG") ?: VersionUtil.getGitUser()
|
||||
ext["git_repo"] = System.getenv("GRIM_GIT_REPO") ?: "Grim"
|
||||
java {
|
||||
toolchain.languageVersion.set(JavaLanguageVersion.of(17))
|
||||
}
|
||||
|
||||
println("Build configuration:")
|
||||
println(" shadePE = ${BuildConfig.shadePE}")
|
||||
println(" relocate = ${BuildConfig.relocate}")
|
||||
println(" mavenLocalOverride = ${BuildConfig.mavenLocalOverride}")
|
||||
println(" release = ${BuildConfig.release}")
|
||||
println(" version = $version")
|
||||
// Set to false for debug builds
|
||||
// You cannot live reload classes if the jar relocates dependencies
|
||||
// Checks Project properties -> environment variable -> defaults true
|
||||
val relocate: Boolean = project.findProperty("relocate")?.toString()?.toBoolean()
|
||||
?: System.getenv("RELOCATE_JAR")?.toBoolean()
|
||||
?: true
|
||||
|
||||
tasks.register("printVersion") {
|
||||
group = "versioning"
|
||||
description = "Prints the computed project version"
|
||||
doLast {
|
||||
println("VERSION=$version")
|
||||
repositories {
|
||||
mavenLocal()
|
||||
maven("https://hub.spigotmc.org/nexus/content/repositories/snapshots/") // Spigot
|
||||
maven("https://jitpack.io/") { // Grim API
|
||||
content {
|
||||
includeGroup("com.github.grimanticheat")
|
||||
}
|
||||
}
|
||||
maven("https://repo.viaversion.com") // ViaVersion
|
||||
maven("https://repo.aikar.co/content/groups/aikar/") // ACF
|
||||
maven("https://nexus.scarsz.me/content/repositories/releases") // Configuralize
|
||||
maven("https://repo.opencollab.dev/maven-snapshots/") // Floodgate
|
||||
maven("https://repo.opencollab.dev/maven-releases/") // Cumulus (for Floodgate)
|
||||
maven("https://repo.codemc.io/repository/maven-releases/") // PacketEvents
|
||||
maven("https://repo.codemc.io/repository/maven-snapshots/")
|
||||
maven("https://s01.oss.sonatype.org/content/repositories/snapshots/")
|
||||
maven("https://repo.extendedclip.com/content/repositories/placeholderapi/") // placeholderapi
|
||||
mavenCentral()
|
||||
// FastUtil, Discord-Webhooks
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("com.github.retrooper:packetevents-spigot:2.7.1-SNAPSHOT")
|
||||
implementation("co.aikar:acf-paper:0.5.1-SNAPSHOT")
|
||||
implementation("club.minnced:discord-webhooks:0.8.0") // Newer versions include kotlin-stdlib, which leads to incompatibility with plugins that use Kotlin
|
||||
implementation("it.unimi.dsi:fastutil:8.5.15")
|
||||
implementation("github.scarsz:configuralize:1.4.0")
|
||||
|
||||
|
||||
// Used for local testing:
|
||||
//implementation("ac.grim.grimac:GrimAPI:1.0")
|
||||
implementation("com.github.grimanticheat:grimapi:ed1ec7b4a1")
|
||||
|
||||
implementation("net.kyori:adventure-text-minimessage:4.17.0")
|
||||
implementation("net.kyori:adventure-platform-bukkit:4.3.4")
|
||||
|
||||
implementation("org.jetbrains:annotations:24.1.0")
|
||||
compileOnly("org.geysermc.floodgate:api:2.0-SNAPSHOT")
|
||||
compileOnly("io.papermc.paper:paper-api:1.18.2-R0.1-SNAPSHOT")
|
||||
compileOnly("com.viaversion:viaversion-api:5.0.4-SNAPSHOT")
|
||||
compileOnly("me.clip:placeholderapi:2.11.6")
|
||||
compileOnly("io.netty:netty-all:4.1.85.Final")
|
||||
}
|
||||
|
||||
bukkit {
|
||||
name = "GrimAC"
|
||||
author = "GrimAC"
|
||||
main = "ac.grim.grimac.GrimAC"
|
||||
website = "https://grim.ac/"
|
||||
apiVersion = "1.13"
|
||||
foliaSupported = true
|
||||
|
||||
softDepend = listOf(
|
||||
"ProtocolLib",
|
||||
"ProtocolSupport",
|
||||
"Essentials",
|
||||
"ViaVersion",
|
||||
"ViaBackwards",
|
||||
"ViaRewind",
|
||||
"Geyser-Spigot",
|
||||
"floodgate",
|
||||
"FastLogin",
|
||||
"PlaceholderAPI",
|
||||
)
|
||||
|
||||
permissions {
|
||||
register("grim.alerts") {
|
||||
description = "Receive alerts for violations"
|
||||
default = Permission.Default.OP
|
||||
}
|
||||
|
||||
register("grim.alerts.enable-on-join") {
|
||||
description = "Enable alerts on join"
|
||||
default = Permission.Default.OP
|
||||
}
|
||||
|
||||
register("grim.performance") {
|
||||
description = "Check performance metrics"
|
||||
default = Permission.Default.OP
|
||||
}
|
||||
|
||||
register("grim.profile") {
|
||||
description = "Check user profile"
|
||||
default = Permission.Default.OP
|
||||
}
|
||||
|
||||
register("grim.brand") {
|
||||
description = "Show client brands on join"
|
||||
default = Permission.Default.OP
|
||||
}
|
||||
|
||||
register("grim.brand.enable-on-join") {
|
||||
description = "Enable showing client brands on join"
|
||||
default = Permission.Default.OP
|
||||
}
|
||||
|
||||
register("grim.sendalert") {
|
||||
description = "Send cheater alert"
|
||||
default = Permission.Default.OP
|
||||
}
|
||||
|
||||
register("grim.nosetback") {
|
||||
description = "Disable setback"
|
||||
default = Permission.Default.FALSE
|
||||
}
|
||||
|
||||
register("grim.nomodifypacket") {
|
||||
description = "Disable modifying packets"
|
||||
default = Permission.Default.FALSE
|
||||
}
|
||||
|
||||
register("grim.exempt") {
|
||||
description = "Exempt from all checks"
|
||||
default = Permission.Default.FALSE
|
||||
}
|
||||
|
||||
register("grim.verbose") {
|
||||
description = "Receive verbose alerts for violations. Requires grim.alerts"
|
||||
default = Permission.Default.OP
|
||||
}
|
||||
|
||||
register("grim.verbose.enable-on-join") {
|
||||
description = "Enable verbose alerts on join. Requires grim.alerts and grim.alerts.enable-on-join"
|
||||
default = Permission.Default.FALSE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---------- Java Compile Optimization ----------
|
||||
subprojects {
|
||||
tasks.withType<JavaCompile>().configureEach {
|
||||
options.isFork = true
|
||||
options.isIncremental = true
|
||||
tasks.build {
|
||||
dependsOn(tasks.shadowJar)
|
||||
dependsOn(tasks.spotlessApply)
|
||||
}
|
||||
|
||||
tasks.withType<JavaCompile> {
|
||||
options.encoding = "UTF-8"
|
||||
}
|
||||
|
||||
publishing.publications.create<MavenPublication>("maven") {
|
||||
artifact(tasks["shadowJar"])
|
||||
}
|
||||
|
||||
tasks.shadowJar {
|
||||
minimize()
|
||||
archiveFileName.set("${project.name}-${project.version}.jar")
|
||||
if (relocate) {
|
||||
relocate("io.github.retrooper.packetevents", "ac.grim.grimac.shaded.io.github.retrooper.packetevents")
|
||||
relocate("com.github.retrooper.packetevents", "ac.grim.grimac.shaded.com.github.retrooper.packetevents")
|
||||
relocate("co.aikar.commands", "ac.grim.grimac.shaded.acf")
|
||||
relocate("co.aikar.locale", "ac.grim.grimac.shaded.locale")
|
||||
relocate("club.minnced", "ac.grim.grimac.shaded.discord-webhooks")
|
||||
relocate("github.scarsz.configuralize", "ac.grim.grimac.shaded.configuralize")
|
||||
relocate("com.github.puregero", "ac.grim.grimac.shaded.com.github.puregero")
|
||||
relocate("com.google.code.gson", "ac.grim.grimac.shaded.gson")
|
||||
relocate("alexh", "ac.grim.grimac.shaded.maps")
|
||||
relocate("it.unimi.dsi.fastutil", "ac.grim.grimac.shaded.fastutil")
|
||||
relocate("net.kyori", "ac.grim.grimac.shaded.kyori")
|
||||
relocate("okhttp3", "ac.grim.grimac.shaded.okhttp3")
|
||||
relocate("okio", "ac.grim.grimac.shaded.okio")
|
||||
relocate("org.yaml.snakeyaml", "ac.grim.grimac.shaded.snakeyaml")
|
||||
relocate("org.json", "ac.grim.grimac.shaded.json")
|
||||
relocate("org.intellij", "ac.grim.grimac.shaded.intellij")
|
||||
relocate("org.jetbrains", "ac.grim.grimac.shaded.jetbrains")
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
plugins {
|
||||
`kotlin-dsl`
|
||||
kotlin("plugin.serialization") version "1.9.0" // Use the same Kotlin version as your main project
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(libs.spotless)
|
||||
implementation(libs.lombok)
|
||||
implementation(libs.shadow)
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.0")
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
dependencyResolutionManagement {
|
||||
versionCatalogs {
|
||||
create("libs") {
|
||||
from(files("../libs.versions.toml"))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
import versioning.BuildConfig
|
||||
|
||||
plugins {
|
||||
`java-library`
|
||||
id("io.freefair.lombok")
|
||||
id("com.diffplug.spotless")
|
||||
}
|
||||
|
||||
group = rootProject.group
|
||||
version = rootProject.version
|
||||
description = rootProject.description
|
||||
|
||||
// Java compilation settings
|
||||
java {
|
||||
toolchain.languageVersion.set(JavaLanguageVersion.of(21))
|
||||
disableAutoTargetJvm()
|
||||
withSourcesJar()
|
||||
withJavadocJar()
|
||||
}
|
||||
|
||||
// Spotless configuration
|
||||
spotless {
|
||||
java {
|
||||
endWithNewline()
|
||||
indentWithSpaces(4)
|
||||
removeUnusedImports()
|
||||
trimTrailingWhitespace()
|
||||
targetExclude("build/generated/**/*")
|
||||
}
|
||||
|
||||
kotlinGradle {
|
||||
endWithNewline()
|
||||
indentWithSpaces(4)
|
||||
trimTrailingWhitespace()
|
||||
}
|
||||
}
|
||||
|
||||
tasks {
|
||||
withType<JavaCompile>().configureEach {
|
||||
options.encoding = "UTF-8"
|
||||
options.release.set(17)
|
||||
}
|
||||
|
||||
build {
|
||||
// Ensure spotlessApply runs before build
|
||||
dependsOn(tasks.named("spotlessApply"))
|
||||
}
|
||||
|
||||
// Process resources (e.g., for plugin metadata files)
|
||||
processResources {
|
||||
val properties = mapOf(
|
||||
"timestamp" to rootProject.ext["timestamp"],
|
||||
"version" to project.version.toString(),
|
||||
"git_commit" to rootProject.ext["git_commit"],
|
||||
"git_branch" to rootProject.ext["git_branch"],
|
||||
"git_repo" to rootProject.ext["git_repo"],
|
||||
"git_org" to rootProject.ext["git_org"],
|
||||
"build_shade_pe" to BuildConfig.shadePE,
|
||||
"build_relocate" to BuildConfig.relocate,
|
||||
"build_release" to BuildConfig.release,
|
||||
)
|
||||
|
||||
properties.forEach { (key, value) -> inputs.property(key, value) }
|
||||
|
||||
filesMatching(
|
||||
listOf(
|
||||
"bungee.yml",
|
||||
"velocity-plugin.json",
|
||||
"fabric.mod.json",
|
||||
"grimac.properties"
|
||||
)
|
||||
) {
|
||||
expand(properties)
|
||||
}
|
||||
}
|
||||
|
||||
javadoc {
|
||||
title = "${rootProject.name}-${project.name} v${rootProject.version}"
|
||||
options.encoding = "UTF-8"
|
||||
options.overview =
|
||||
rootProject.file("buildSrc/src/main/resources/javadoc-overview.html").toString()
|
||||
setDestinationDir(file("${project.layout.buildDirectory.asFile.get()}/docs/javadoc"))
|
||||
options {
|
||||
(this as CoreJavadocOptions).addBooleanOption("Xdoclint:none", true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Default tasks
|
||||
defaultTasks("build")
|
||||
@@ -1,407 +0,0 @@
|
||||
import kotlinx.serialization.json.*
|
||||
import java.util.jar.JarFile
|
||||
import java.util.zip.ZipEntry
|
||||
|
||||
plugins {
|
||||
`java-library`
|
||||
}
|
||||
|
||||
val jijDependencies: Configuration by project.configurations.creating {
|
||||
isCanBeResolved = true
|
||||
isCanBeConsumed = false
|
||||
extendsFrom(project.configurations.getByName("implementation"))
|
||||
project.configurations.findByName("modImplementation")?.let { extendsFrom(it) }
|
||||
}
|
||||
|
||||
data class DependencyIdentifier(
|
||||
val group: String,
|
||||
val name: String,
|
||||
val version: String,
|
||||
val classifier: String = "" // Add classifier field, default to empty string
|
||||
) {
|
||||
override fun toString() = "$group:$name:$version${if (classifier.isNotEmpty()) ":$classifier" else ""}"
|
||||
}
|
||||
|
||||
// Does not support extracting classifiers but while the fabric standard is ambiguous on whether they can be included
|
||||
// It does not appear dependency classifiers are in use by any mod I am aware of or can find
|
||||
fun parseFabricModJson(jarFile: File, project: Project): Set<DependencyIdentifier> {
|
||||
val nestedJars = mutableSetOf<DependencyIdentifier>()
|
||||
try {
|
||||
JarFile(jarFile).use { jar ->
|
||||
val fabricModJsonEntry: ZipEntry? = jar.getEntry("fabric.mod.json")
|
||||
if (fabricModJsonEntry != null) {
|
||||
jar.getInputStream(fabricModJsonEntry).bufferedReader().use { reader ->
|
||||
val jsonContent = reader.readText()
|
||||
val jsonElement = Json.parseToJsonElement(jsonContent)
|
||||
val jsonObject = jsonElement.jsonObject
|
||||
val jarsArray = jsonObject["jars"]?.jsonArray ?: return@use
|
||||
jarsArray.forEach { jarEntry: JsonElement ->
|
||||
val filePath =
|
||||
jarEntry.jsonObject["file"]?.jsonPrimitive?.content ?: return@forEach
|
||||
project.logger.debug(
|
||||
"Found nested JAR in fabric.mod.json: {} in {}",
|
||||
filePath,
|
||||
jarFile
|
||||
)
|
||||
val jarName = filePath.substringAfterLast("/")
|
||||
val parts = jarName.removeSuffix(".jar").split("-")
|
||||
if (parts.size >= 2) {
|
||||
val inferredName = parts.dropLast(1).joinToString("-")
|
||||
val inferredVersion = parts.last()
|
||||
nestedJars.add(DependencyIdentifier("", inferredName, inferredVersion))
|
||||
project.logger.debug("Inferred nested dependency from fabric.mod.json: :$inferredName:$inferredVersion")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
project.logger.error("Failed to parse fabric.mod.json in $jarFile: ${e.message}")
|
||||
}
|
||||
return nestedJars
|
||||
}
|
||||
|
||||
fun extractEmbeddedJars(jarFile: File, project: Project): Set<DependencyIdentifier> {
|
||||
val embeddedJars = mutableSetOf<DependencyIdentifier>()
|
||||
embeddedJars.addAll(parseFabricModJson(jarFile, project)) // Retain existing logic for top-level fabric.mod.json parsing
|
||||
try {
|
||||
JarFile(jarFile).use { jar ->
|
||||
val entries = jar.entries()
|
||||
while (entries.hasMoreElements()) {
|
||||
val entry = entries.nextElement()
|
||||
val entryName = entry.name
|
||||
if (entryName.endsWith(".jar") && (entryName.startsWith("META-INF/jarjar/") || entryName.startsWith(
|
||||
"libs/"
|
||||
) || entryName.startsWith("META-INF/jars/"))
|
||||
) {
|
||||
project.logger.debug("Found embedded JAR: {} in {}", entryName, jarFile)
|
||||
val tempFile = File.createTempFile("embedded-", ".jar")
|
||||
tempFile.deleteOnExit()
|
||||
jar.getInputStream(entry).use { input ->
|
||||
tempFile.outputStream().use { output ->
|
||||
input.copyTo(output)
|
||||
}
|
||||
}
|
||||
JarFile(tempFile).use { embeddedJar ->
|
||||
val fabricModJsonEntry: ZipEntry? = embeddedJar.getEntry("fabric.mod.json")
|
||||
if (fabricModJsonEntry != null) {
|
||||
embeddedJar.getInputStream(fabricModJsonEntry).bufferedReader().use { reader ->
|
||||
val jsonContent = reader.readText()
|
||||
val jsonElement = Json.parseToJsonElement(jsonContent)
|
||||
val jsonObject = jsonElement.jsonObject
|
||||
|
||||
// Extract id and parse into group
|
||||
val id = jsonObject["id"]?.jsonPrimitive?.content
|
||||
?: throw IllegalStateException("Missing 'id' in fabric.mod.json of embedded JAR: $entryName")
|
||||
val idWithDots = id.replace("_", ".")
|
||||
val lastDotIndex = idWithDots.lastIndexOf(".")
|
||||
val group = if (lastDotIndex != -1) idWithDots.take(lastDotIndex) else ""
|
||||
|
||||
// Extract name and version from the JAR filename
|
||||
val jarName = entryName.substringAfterLast("/").removeSuffix(".jar")
|
||||
val (name, version) = parseNameAndVersionFromJarName(jarName)
|
||||
?: throw IllegalStateException("Could not parse name and version from JAR filename: $jarName")
|
||||
|
||||
// Create DependencyIdentifier (classifier is empty for now)
|
||||
val depId = DependencyIdentifier(group, name, version)
|
||||
embeddedJars.add(depId)
|
||||
project.logger.debug("Identified embedded dependency from fabric.mod.json and filename: {}", depId)
|
||||
}
|
||||
} else {
|
||||
project.logger.error("No fabric.mod.json found in embedded JAR: {}. Skipping.", entryName)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
project.logger.error("Failed to analyze JAR file $jarFile for embedded JARs: ${e.message}")
|
||||
}
|
||||
return embeddedJars
|
||||
}
|
||||
|
||||
fun parseNameAndVersionFromJarName(jarName: String): Pair<String, String>? {
|
||||
val versionStartRegex = Regex("-(?=\\d)")
|
||||
// Use regex to find the position of the hyphen followed by a digit
|
||||
val versionStartMatch = versionStartRegex.find(jarName) ?: return null
|
||||
val versionStartIndex = versionStartMatch.range.first
|
||||
|
||||
// Split the JAR name into name and version at the version start index
|
||||
val name = jarName.take(versionStartIndex)
|
||||
val version = jarName.substring(versionStartIndex + 1) // Skip the hyphen
|
||||
|
||||
return if (name.isNotEmpty() && version.isNotEmpty()) {
|
||||
Pair(name, version)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
fun shouldExcludeDependency(
|
||||
dependency: ResolvedDependency,
|
||||
allEmbeddedDependencies: Set<DependencyIdentifier>,
|
||||
actuallyIncludedDependencies: Set<DependencyIdentifier>,
|
||||
project: Project
|
||||
): Boolean {
|
||||
val group = dependency.moduleGroup
|
||||
val name = dependency.moduleName
|
||||
val version = dependency.moduleVersion
|
||||
// Extract classifier from resolved dependency (if available)
|
||||
val classifier = dependency.moduleArtifacts.firstOrNull()?.classifier ?: ""
|
||||
val depId = DependencyIdentifier(group, name, version, classifier)
|
||||
|
||||
if (group.startsWith("net.minecraft") ||
|
||||
group.startsWith("net.fabricmc") ||
|
||||
name == "minecraft" ||
|
||||
name == "fabric-loader" ||
|
||||
name == "fastutil" ||
|
||||
name.startsWith("fabric_") ||
|
||||
name.startsWith("fabric-api")
|
||||
) {
|
||||
project.logger.debug("Excluding platform dependency: {}", depId)
|
||||
return true
|
||||
}
|
||||
|
||||
if (name.endsWith("-bom") || name.contains("_bom")) {
|
||||
project.logger.debug("Excluding BOM file: {}", depId)
|
||||
return true
|
||||
}
|
||||
|
||||
if (allEmbeddedDependencies.any { it.name == name && it.version == version && it.classifier == classifier }) {
|
||||
project.logger.debug("Excluding dependency already embedded in another JAR: {}", depId)
|
||||
return true
|
||||
}
|
||||
|
||||
if (actuallyIncludedDependencies.any { it.name == name && it.version == version && it.classifier == classifier }) {
|
||||
project.logger.debug("Excluding duplicate dependency: {}", depId)
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
fun isJijTarget(
|
||||
dependency: ResolvedDependency,
|
||||
allEmbeddedDependencies: Set<DependencyIdentifier>,
|
||||
actuallyIncludedDependencies: Set<DependencyIdentifier>,
|
||||
project: Project
|
||||
): Boolean {
|
||||
val classifier = dependency.moduleArtifacts.firstOrNull()?.classifier ?: ""
|
||||
project.logger.debug(
|
||||
"Checking JIJ target: {}",
|
||||
DependencyIdentifier(
|
||||
dependency.moduleGroup,
|
||||
dependency.moduleName,
|
||||
dependency.moduleVersion,
|
||||
classifier
|
||||
)
|
||||
)
|
||||
return !shouldExcludeDependency(
|
||||
dependency,
|
||||
allEmbeddedDependencies,
|
||||
actuallyIncludedDependencies,
|
||||
project
|
||||
)
|
||||
}
|
||||
|
||||
fun includeDependencyWithExclusions(
|
||||
depId: DependencyIdentifier,
|
||||
project: Project,
|
||||
actuallyIncludedDependencies: MutableSet<DependencyIdentifier>,
|
||||
allEmbeddedDependencies: Set<DependencyIdentifier>
|
||||
) {
|
||||
project.logger.debug("Including external dependency as JIJ with dynamic exclusions: {}", depId)
|
||||
project.dependencies {
|
||||
// Include dependency with classifier (if present)
|
||||
"include"("${depId.group}:${depId.name}:${depId.version}${if (depId.classifier.isNotEmpty()) ":${depId.classifier}" else ""}") {
|
||||
allEmbeddedDependencies.forEach { embeddedDep ->
|
||||
if (embeddedDep.group.isNotEmpty()) {
|
||||
exclude(group = embeddedDep.group, module = embeddedDep.name)
|
||||
} else {
|
||||
exclude(module = embeddedDep.name)
|
||||
}
|
||||
project.logger.debug("Excluding embedded transitive dependency: ${embeddedDep.name}:${embeddedDep.version}${if (embeddedDep.classifier.isNotEmpty()) ":${embeddedDep.classifier}" else ""}")
|
||||
}
|
||||
}
|
||||
}
|
||||
actuallyIncludedDependencies.add(depId)
|
||||
}
|
||||
|
||||
fun processDependencies(
|
||||
dependencies: Set<ResolvedDependency>,
|
||||
processed: MutableSet<String>,
|
||||
allEmbeddedDependencies: MutableMap<DependencyIdentifier, File>,
|
||||
actuallyIncludedDependencies: MutableSet<DependencyIdentifier>,
|
||||
project: Project
|
||||
) {
|
||||
dependencies.forEach { dep: ResolvedDependency ->
|
||||
val classifier = dep.moduleArtifacts.firstOrNull()?.classifier ?: ""
|
||||
val depKey = "${dep.moduleGroup}:${dep.moduleName}:${dep.moduleVersion}${if (classifier.isNotEmpty()) ":$classifier" else ""}"
|
||||
val depId = DependencyIdentifier(dep.moduleGroup, dep.moduleName, dep.moduleVersion, classifier)
|
||||
if (!processed.contains(depKey)) {
|
||||
processed.add(depKey)
|
||||
project.logger.debug("Resolved dependency: {}", depId)
|
||||
|
||||
val jarFile = dep.moduleArtifacts.firstOrNull()?.file
|
||||
if (jarFile != null && jarFile.exists()) {
|
||||
val embeddedJars = extractEmbeddedJars(jarFile, project)
|
||||
embeddedJars.forEach { embeddedDep ->
|
||||
allEmbeddedDependencies[embeddedDep] = jarFile
|
||||
}
|
||||
}
|
||||
|
||||
if (isJijTarget(
|
||||
dep,
|
||||
allEmbeddedDependencies.keys,
|
||||
actuallyIncludedDependencies,
|
||||
project
|
||||
)
|
||||
) {
|
||||
project.logger.debug("Processing JIJ dependency: {}", depId)
|
||||
val isProjectDependency = project.rootProject.allprojects.any { subproject ->
|
||||
subproject.name == dep.moduleName && (subproject.group == dep.moduleGroup || dep.moduleGroup.isEmpty())
|
||||
}
|
||||
if (isProjectDependency) {
|
||||
val projectPath = project.rootProject.allprojects.find { subproject ->
|
||||
subproject.name == dep.moduleName && (subproject.group == dep.moduleGroup || dep.moduleGroup.isEmpty())
|
||||
}?.path
|
||||
?: throw IllegalStateException("Project dependency not found: ${dep.moduleName}")
|
||||
project.logger.debug("Including project dependency as JIJ: $projectPath")
|
||||
project.dependencies {
|
||||
"include"(project.project(projectPath))
|
||||
}
|
||||
actuallyIncludedDependencies.add(depId)
|
||||
} else {
|
||||
includeDependencyWithExclusions(
|
||||
depId,
|
||||
project,
|
||||
actuallyIncludedDependencies,
|
||||
allEmbeddedDependencies.keys
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
processDependencies(
|
||||
dep.children,
|
||||
processed,
|
||||
allEmbeddedDependencies,
|
||||
actuallyIncludedDependencies,
|
||||
project
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
project.afterEvaluate {
|
||||
project.logger.debug("Resolving jijDependencies configuration")
|
||||
val resolvedDependencies = jijDependencies.resolvedConfiguration.firstLevelModuleDependencies
|
||||
|
||||
val processed = mutableSetOf<String>()
|
||||
val allEmbeddedDependencies = mutableMapOf<DependencyIdentifier, File>()
|
||||
val actuallyIncludedDependencies = mutableSetOf<DependencyIdentifier>()
|
||||
|
||||
fun collectAllEmbeddedDependenciesForAfterEvaluate(dependencies: Set<ResolvedDependency>) {
|
||||
dependencies.forEach { dep ->
|
||||
val classifier = dep.moduleArtifacts.firstOrNull()?.classifier ?: ""
|
||||
val depKey = "${dep.moduleGroup}:${dep.moduleName}:${dep.moduleVersion}${if (classifier.isNotEmpty()) ":$classifier" else ""}"
|
||||
if (!processed.contains(depKey)) {
|
||||
processed.add(depKey)
|
||||
val jarFile = dep.moduleArtifacts.firstOrNull()?.file
|
||||
if (jarFile != null && jarFile.exists()) {
|
||||
val embeddedJars = extractEmbeddedJars(jarFile, project)
|
||||
embeddedJars.forEach { embeddedDep ->
|
||||
allEmbeddedDependencies[embeddedDep] = jarFile
|
||||
project.logger.debug(
|
||||
"Collected embedded dependency: {} from {}",
|
||||
embeddedDep,
|
||||
jarFile
|
||||
)
|
||||
}
|
||||
}
|
||||
collectAllEmbeddedDependenciesForAfterEvaluate(dep.children)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
processed.clear()
|
||||
collectAllEmbeddedDependenciesForAfterEvaluate(resolvedDependencies)
|
||||
|
||||
processed.clear()
|
||||
processDependencies(
|
||||
resolvedDependencies,
|
||||
processed,
|
||||
allEmbeddedDependencies,
|
||||
actuallyIncludedDependencies,
|
||||
project
|
||||
)
|
||||
}
|
||||
|
||||
tasks.withType<Jar>().configureEach {
|
||||
doFirst {
|
||||
project.logger.debug("Resolving JIJ dependencies for JAR inclusion")
|
||||
val resolvedDependencies =
|
||||
jijDependencies.resolvedConfiguration.firstLevelModuleDependencies
|
||||
val processed = mutableSetOf<String>()
|
||||
val allEmbeddedDependencies = mutableMapOf<DependencyIdentifier, File>()
|
||||
val actuallyIncludedDependencies = mutableSetOf<DependencyIdentifier>()
|
||||
|
||||
fun collectAllEmbeddedDependenciesForJar(dependencies: Set<ResolvedDependency>) {
|
||||
dependencies.forEach { dep ->
|
||||
val depKey = "${dep.moduleGroup}:${dep.moduleName}:${dep.moduleVersion}"
|
||||
if (!processed.contains(depKey)) {
|
||||
processed.add(depKey)
|
||||
val jarFile = dep.moduleArtifacts.firstOrNull()?.file
|
||||
if (jarFile != null && jarFile.exists()) {
|
||||
val embeddedJars = extractEmbeddedJars(jarFile, project)
|
||||
embeddedJars.forEach { embeddedDep ->
|
||||
allEmbeddedDependencies[embeddedDep] = jarFile
|
||||
project.logger.debug(
|
||||
"Collected embedded dependency for JAR: {} from {}",
|
||||
embeddedDep,
|
||||
jarFile
|
||||
)
|
||||
}
|
||||
}
|
||||
collectAllEmbeddedDependenciesForJar(dep.children)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
processed.clear()
|
||||
collectAllEmbeddedDependenciesForJar(resolvedDependencies)
|
||||
|
||||
processed.clear()
|
||||
|
||||
fun logDependencies(dependencies: Set<ResolvedDependency>) {
|
||||
dependencies.forEach { dep ->
|
||||
val depKey = "${dep.moduleGroup}:${dep.moduleName}:${dep.moduleVersion}"
|
||||
val depId = DependencyIdentifier(dep.moduleGroup, dep.moduleName, dep.moduleVersion)
|
||||
if (!processed.contains(depKey)) {
|
||||
processed.add(depKey)
|
||||
project.logger.debug("Resolved JIJ dependency for JAR: {}", depId)
|
||||
|
||||
val jarFile = dep.moduleArtifacts.firstOrNull()?.file
|
||||
if (jarFile != null && jarFile.exists()) {
|
||||
val embeddedJars = extractEmbeddedJars(jarFile, project)
|
||||
embeddedJars.forEach { embeddedDep ->
|
||||
allEmbeddedDependencies[embeddedDep] = jarFile
|
||||
}
|
||||
}
|
||||
|
||||
if (isJijTarget(
|
||||
dep,
|
||||
allEmbeddedDependencies.keys,
|
||||
actuallyIncludedDependencies,
|
||||
project
|
||||
)
|
||||
) {
|
||||
project.logger.debug("Including JIJ dependency in JAR: {}", depId)
|
||||
actuallyIncludedDependencies.add(depId)
|
||||
}
|
||||
logDependencies(dep.children)
|
||||
}
|
||||
}
|
||||
}
|
||||
logDependencies(resolvedDependencies)
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
|
||||
import versioning.BuildConfig
|
||||
|
||||
plugins {
|
||||
id("com.gradleup.shadow")
|
||||
}
|
||||
|
||||
tasks.named<ShadowJar>("shadowJar") {
|
||||
minimize()
|
||||
archiveFileName = "${rootProject.name}-${project.name}-${rootProject.version}.jar"
|
||||
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
|
||||
|
||||
if (BuildConfig.relocate) {
|
||||
if (BuildConfig.shadePE) {
|
||||
relocate("io.github.retrooper.packetevents", "ac.grim.grimac.shaded.io.github.retrooper.packetevents")
|
||||
relocate("com.github.retrooper.packetevents", "ac.grim.grimac.shaded.com.github.retrooper.packetevents")
|
||||
relocate("net.kyori", "ac.grim.grimac.shaded.kyori") // use PE's built-in adventure instead when not shading PE
|
||||
}
|
||||
relocate("club.minnced", "ac.grim.grimac.shaded.discord-webhooks")
|
||||
relocate("org.slf4j", "ac.grim.grimac.shaded.slf4j") // Required by discord-webhooks
|
||||
relocate("github.scarsz.configuralize", "ac.grim.grimac.shaded.configuralize")
|
||||
relocate("com.github.puregero", "ac.grim.grimac.shaded.com.github.puregero")
|
||||
relocate("com.google.code.gson", "ac.grim.grimac.shaded.gson")
|
||||
relocate("alexh", "ac.grim.grimac.shaded.maps")
|
||||
relocate("it.unimi.dsi.fastutil", "ac.grim.grimac.shaded.fastutil")
|
||||
relocate("okhttp3", "ac.grim.grimac.shaded.okhttp3")
|
||||
relocate("okio", "ac.grim.grimac.shaded.okio")
|
||||
relocate("org.yaml.snakeyaml", "ac.grim.grimac.shaded.snakeyaml")
|
||||
relocate("org.json", "ac.grim.grimac.shaded.json")
|
||||
relocate("org.intellij", "ac.grim.grimac.shaded.intellij")
|
||||
relocate("org.jetbrains", "ac.grim.grimac.shaded.jetbrains")
|
||||
relocate("org.incendo", "ac.grim.grimac.shaded.incendo")
|
||||
relocate("io.leangen.geantyref", "ac.grim.grimac.shaded.geantyref") // Required by cloud
|
||||
relocate("com.zaxxer", "ac.grim.grimac.shaded.zaxxer") // Database history
|
||||
}
|
||||
mergeServiceFiles()
|
||||
}
|
||||
|
||||
tasks.named("assemble") {
|
||||
dependsOn(tasks.named("shadowJar"))
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
package versioning
|
||||
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.internal.extensions.stdlib.toDefaultLowerCase
|
||||
import versioning.BuildConfig.init
|
||||
import versioning.BuildConfig.mavenLocalOverride
|
||||
import versioning.BuildConfig.release
|
||||
import versioning.BuildConfig.relocate
|
||||
import versioning.BuildConfig.shadePE
|
||||
|
||||
/**
|
||||
* BuildConfig provides access to user-defined build flags that control how a Grim
|
||||
* build is assembled. These flags are resolved once at configuration time using
|
||||
* [init], and are then exposed as fast, memoized values.
|
||||
*
|
||||
* Flags can be defined in three ways:
|
||||
*
|
||||
* 1. JVM system properties (-Dflag=value)
|
||||
* 2. Gradle project properties (-Pflag=value or in gradle.properties)
|
||||
* 3. Environment variables (FLAG=value)
|
||||
*
|
||||
* You can use these to enable/disable features like shading, relocation, or release mode.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* Using Gradle -P properties:
|
||||
* ```
|
||||
* ./gradlew build -PshadePE=true -Prelocate=false -Prelease=true
|
||||
* ```
|
||||
*
|
||||
* Using environment variables:
|
||||
* ```
|
||||
* SHADE_PE=true RELOCATE_JAR=false RELEASE=true ./gradlew build
|
||||
* ```
|
||||
*
|
||||
* Using JVM system properties:
|
||||
* ```
|
||||
* ./gradlew build -DshadePE=true -Drelease=true
|
||||
* ```
|
||||
*
|
||||
* @property shadePE If true, shades PacketEvents into the jar. Default: true.
|
||||
* @property 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
|
||||
*/
|
||||
object BuildConfig {
|
||||
|
||||
/**
|
||||
* Must be called once from your root build script to initialize the flags.
|
||||
* Example (in build.gradle.kts):
|
||||
* ```
|
||||
* BuildConfig.init(project)
|
||||
* ```
|
||||
*/
|
||||
fun init(project: Project) {
|
||||
_shadePE = resolveBool(project, "shadePE", altKey = "SHADE_PE", default = true)
|
||||
_relocate = resolveBool(project, "relocate", altKey = "RELOCATE_JAR", default = true)
|
||||
_release = resolveBool(project, "release", default = false)
|
||||
_mavenLocalOverride = resolveBool(project, "mavenLocalOverride", default = false)
|
||||
}
|
||||
|
||||
// Unified resolution logic (System > Gradle > Env)
|
||||
private fun resolveRaw(project: Project, key: String): String? =
|
||||
System.getProperty(key) // 1. JVM (-Dkey=value)
|
||||
?: project.findProperty(key)?.toString() // 2. Gradle (-Pkey=value or gradle.properties)
|
||||
?: System.getenv(key.uppercase()) // 3. ENV (KEY=value)
|
||||
|
||||
private fun resolveBool(project: Project, key: String, altKey: String? = null, default: Boolean): Boolean {
|
||||
return resolveRaw(project, key)?.toDefaultLowerCase()?.toBooleanStrictOrNull()
|
||||
?: altKey?.let {
|
||||
resolveRaw(project, it)?.toDefaultLowerCase()?.toBooleanStrictOrNull()
|
||||
} ?: default
|
||||
}
|
||||
|
||||
// Private backing vars (nullable because we can't use lateinit with primitives)
|
||||
private var _shadePE: Boolean? = null
|
||||
private var _relocate: Boolean? = null
|
||||
private var _release: Boolean? = null
|
||||
private var _mavenLocalOverride: Boolean? = null
|
||||
|
||||
/** If true, shades PacketEvents into the jar. Default: true. */
|
||||
val shadePE: Boolean get() = _shadePE
|
||||
?: error("BuildConfig.shadePE accessed before init() was called")
|
||||
|
||||
/** If true, relocates shaded dependencies to avoid conflicts. Default: true. */
|
||||
val relocate: Boolean get() = _relocate
|
||||
?: error("BuildConfig.relocate accessed before init() was called")
|
||||
|
||||
/** If true, omits commit hash and modifiers from version string. Default: false. */
|
||||
val release: Boolean get() = _release
|
||||
?: error("BuildConfig.release accessed before init() was called")
|
||||
|
||||
val mavenLocalOverride: Boolean get() = _mavenLocalOverride
|
||||
?: error("BuildConfig.release accessed before init() was called")
|
||||
|
||||
}
|
||||
@@ -1,113 +0,0 @@
|
||||
package versioning
|
||||
|
||||
import java.io.ByteArrayOutputStream
|
||||
|
||||
/**
|
||||
* Utility for computing the version string of GrimAC artifacts.
|
||||
*
|
||||
* The version string is constructed based on:
|
||||
* - A base semantic version (e.g., "2.3.72")
|
||||
* - Git commit hash (unless in release mode)
|
||||
* - Git branch name (unless in release mode or main)
|
||||
* - Modifiers (e.g., lite, no_relocate) for non-default build configurations
|
||||
*
|
||||
* Example outputs:
|
||||
* - `2.3.72` (release build)
|
||||
* - `2.3.72-a4f8b21+lite` (preview build without PE shading)
|
||||
* - `2.3.72-feature_branch-a4f8b21+lite-no_relocate`
|
||||
*
|
||||
* @see BuildConfig for controlling the release/modifier behavior
|
||||
*/
|
||||
object VersionUtil {
|
||||
|
||||
/**
|
||||
* Computes the full version string for the build.
|
||||
*
|
||||
* @param baseVersion The base semantic version (e.g., "2.3.72")
|
||||
* @return Full version string including commit hash, branch, and modifiers if applicable
|
||||
*/
|
||||
fun computeVersion(baseVersion: String): String {
|
||||
if (BuildConfig.release) {
|
||||
return baseVersion
|
||||
}
|
||||
|
||||
val commitHash = getGitCommitHash()
|
||||
val branch = getGitBranch()
|
||||
|
||||
val modifiers = buildList {
|
||||
if (!BuildConfig.shadePE) add("lite")
|
||||
if (!BuildConfig.relocate) add("no_relocate")
|
||||
}.joinToString("-").takeIf { it.isNotEmpty() }
|
||||
|
||||
return buildString {
|
||||
append(baseVersion)
|
||||
append("-")
|
||||
branch?.let { append("$it-") }
|
||||
append(commitHash)
|
||||
modifiers?.let { append("+$it") }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the current Git commit
|
||||
*/
|
||||
fun getGitCommitHash(full: Boolean = false): String {
|
||||
val stdout = ByteArrayOutputStream()
|
||||
val command = listOfNotNull("git", "rev-parse", if (full) null else "--short", "HEAD")
|
||||
ProcessBuilder(command)
|
||||
.redirectErrorStream(true)
|
||||
.start()
|
||||
.apply { waitFor() }
|
||||
.inputStream
|
||||
.use { stdout.writeBytes(it.readAllBytes()) }
|
||||
val fullCommit = stdout.toString().trim()
|
||||
return fullCommit.take(minOf(fullCommit.length, 7))
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current Git branch, sanitised for use in file names.
|
||||
* If the branch is "main" or "2.0", returns null.
|
||||
*
|
||||
* Any slash (/) in the branch name is replaced with an underscore (_)
|
||||
* to avoid filesystem issues.
|
||||
*/
|
||||
fun getGitBranch(raw: Boolean = false): String? {
|
||||
val stdout = ByteArrayOutputStream()
|
||||
|
||||
ProcessBuilder("git", "rev-parse", "--abbrev-ref", "HEAD")
|
||||
.redirectErrorStream(true)
|
||||
.start()
|
||||
.apply { waitFor() }
|
||||
.inputStream.use { stdout.writeBytes(it.readAllBytes()) }
|
||||
|
||||
if (raw) return stdout.toString().trim()
|
||||
|
||||
val branch = stdout.toString().trim()
|
||||
.replace(Regex("[^a-zA-Z0-9_.-]+"), "_")
|
||||
.replace(Regex("_{2,}"), "_")
|
||||
.trim(' ', '.', '_', '-')
|
||||
.removePrefix("heads_")
|
||||
|
||||
val mainBranch = System.getenv("GRIM_MAIN_BRANCH") ?: "2.0"
|
||||
|
||||
return when (branch) {
|
||||
"main", mainBranch -> null // ← ignore these branches
|
||||
else -> branch
|
||||
}
|
||||
}
|
||||
|
||||
fun getGitUser(): String {
|
||||
try {
|
||||
val stdout = ByteArrayOutputStream()
|
||||
ProcessBuilder("git", "config", "user.name")
|
||||
.redirectErrorStream(true)
|
||||
.start()
|
||||
.apply { waitFor() }
|
||||
.inputStream.use { stdout.writeBytes(it.readAllBytes()) }
|
||||
return stdout.toString().trim()
|
||||
} catch (_: Exception) {
|
||||
return "unknown"
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,206 +0,0 @@
|
||||
import net.minecrell.pluginyml.bukkit.BukkitPluginDescription.Permission
|
||||
import versioning.BuildConfig
|
||||
|
||||
plugins {
|
||||
`maven-publish`
|
||||
grim.`base-conventions`
|
||||
grim.`shadow-conventions`
|
||||
id("de.eldoria.plugin-yml.bukkit") version "0.8.0"
|
||||
id("xyz.jpenilla.run-paper") version "3.0.0-beta.1"
|
||||
}
|
||||
|
||||
repositories {
|
||||
if (BuildConfig.mavenLocalOverride) {
|
||||
mavenLocal()
|
||||
}
|
||||
|
||||
// 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")
|
||||
}
|
||||
}
|
||||
|
||||
exclusiveContent {
|
||||
forRepository {
|
||||
maven("https://libraries.minecraft.net") { // Brigadier
|
||||
mavenContent { releasesOnly() }
|
||||
}
|
||||
}
|
||||
filter {
|
||||
includeModule("com.mojang", "brigadier")
|
||||
}
|
||||
}
|
||||
|
||||
// For placeholderapi
|
||||
exclusiveContent {
|
||||
forRepository {
|
||||
maven("https://repo.extendedclip.com/content/repositories/placeholderapi/")
|
||||
}
|
||||
filter {
|
||||
includeGroup("me.clip")
|
||||
}
|
||||
}
|
||||
|
||||
// For GrimAPI and PacketEvents (transitive from :common, direct in :bukkit)
|
||||
exclusiveContent {
|
||||
forRepository {
|
||||
maven("https://repo.grim.ac/snapshots")
|
||||
}
|
||||
filter {
|
||||
includeGroup("ac.grim.grimac")
|
||||
includeGroup("com.github.retrooper")
|
||||
}
|
||||
}
|
||||
|
||||
// For Configuralize (transitive from :common)
|
||||
exclusiveContent {
|
||||
forRepository {
|
||||
maven("https://nexus.scarsz.me/content/repositories/releases") {
|
||||
mavenContent { releasesOnly() }
|
||||
}
|
||||
}
|
||||
filter {
|
||||
includeGroup("github.scarsz")
|
||||
}
|
||||
}
|
||||
|
||||
// Maven Central Fallback
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
|
||||
dependencies {
|
||||
compileOnly(libs.paper.api)
|
||||
compileOnly(libs.placeholderapi)
|
||||
|
||||
if (BuildConfig.shadePE) {
|
||||
implementation(libs.packetevents.spigot)
|
||||
} else {
|
||||
compileOnly(libs.packetevents.spigot)
|
||||
}
|
||||
implementation(libs.cloud.paper)
|
||||
implementation(libs.adventure.platform.bukkit)
|
||||
|
||||
implementation(project(":common"))
|
||||
shadow(project(":common"))
|
||||
}
|
||||
|
||||
bukkit {
|
||||
name = "GrimAC"
|
||||
author = "GrimAC"
|
||||
main = "ac.grim.grimac.platform.bukkit.GrimACBukkitLoaderPlugin"
|
||||
website = "https://grim.ac/"
|
||||
apiVersion = "1.13"
|
||||
foliaSupported = true
|
||||
|
||||
if (!BuildConfig.shadePE) {
|
||||
depend = listOf("packetevents")
|
||||
}
|
||||
|
||||
softDepend = listOf(
|
||||
"ProtocolLib",
|
||||
"ProtocolSupport",
|
||||
"Essentials",
|
||||
"ViaVersion",
|
||||
"ViaBackwards",
|
||||
"ViaRewind",
|
||||
"Geyser-Spigot",
|
||||
"floodgate",
|
||||
"FastLogin",
|
||||
"PlaceholderAPI",
|
||||
)
|
||||
|
||||
permissions {
|
||||
register("grim.alerts") {
|
||||
description = "Receive alerts for violations"
|
||||
default = Permission.Default.OP
|
||||
}
|
||||
|
||||
register("grim.alerts.enable-on-join") {
|
||||
description = "Enable alerts on join"
|
||||
default = Permission.Default.OP
|
||||
}
|
||||
|
||||
register("grim.performance") {
|
||||
description = "Check performance metrics"
|
||||
default = Permission.Default.OP
|
||||
}
|
||||
|
||||
register("grim.profile") {
|
||||
description = "Check user profile"
|
||||
default = Permission.Default.OP
|
||||
}
|
||||
|
||||
register("grim.brand") {
|
||||
description = "Show client brands on join"
|
||||
default = Permission.Default.OP
|
||||
}
|
||||
|
||||
register("grim.brand.enable-on-join") {
|
||||
description = "Enable showing client brands on join"
|
||||
default = Permission.Default.OP
|
||||
}
|
||||
|
||||
register("grim.sendalert") {
|
||||
description = "Send cheater alert"
|
||||
default = Permission.Default.OP
|
||||
}
|
||||
|
||||
register("grim.nosetback") {
|
||||
description = "Disable setback"
|
||||
default = Permission.Default.FALSE
|
||||
}
|
||||
|
||||
register("grim.nomodifypacket") {
|
||||
description = "Disable modifying packets"
|
||||
default = Permission.Default.FALSE
|
||||
}
|
||||
|
||||
register("grim.exempt") {
|
||||
description = "Exempt from all checks"
|
||||
default = Permission.Default.FALSE
|
||||
}
|
||||
|
||||
register("grim.verbose") {
|
||||
description = "Receive verbose alerts for violations"
|
||||
default = Permission.Default.OP
|
||||
}
|
||||
|
||||
register("grim.verbose.enable-on-join") {
|
||||
description =
|
||||
"Enable verbose alerts on join"
|
||||
default = Permission.Default.FALSE
|
||||
}
|
||||
|
||||
register("grim.list") {
|
||||
description =
|
||||
"Shows lists of specific data"
|
||||
default = Permission.Default.FALSE
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
publishing.publications.create<MavenPublication>("maven") {
|
||||
artifact(tasks["shadowJar"])
|
||||
}
|
||||
|
||||
tasks {
|
||||
runServer {
|
||||
minecraftVersion("1.21.4")
|
||||
}
|
||||
|
||||
shadowJar {
|
||||
manifest {
|
||||
attributes["paperweight-mappings-namespace"] = "mojang"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
package ac.grim.grimac.platform.bukkit;
|
||||
|
||||
import ac.grim.grimac.platform.api.PlatformPlugin;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
public class BukkitPlatformPlugin implements PlatformPlugin {
|
||||
private final Plugin plugin;
|
||||
|
||||
public BukkitPlatformPlugin(Plugin plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return plugin.isEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return plugin.getName();
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public String getVersion() {
|
||||
return plugin.getDescription().getVersion();
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
package ac.grim.grimac.platform.bukkit;
|
||||
|
||||
import ac.grim.grimac.platform.api.PlatformServer;
|
||||
import ac.grim.grimac.platform.api.sender.Sender;
|
||||
import io.github.retrooper.packetevents.util.SpigotReflectionUtil;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
|
||||
public class BukkitPlatformServer implements PlatformServer {
|
||||
|
||||
@Override
|
||||
public String getPlatformImplementationString() {
|
||||
return Bukkit.getVersion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispatchCommand(Sender sender, String command) {
|
||||
CommandSender commandSender = GrimACBukkitLoaderPlugin.LOADER.getBukkitSenderFactory().reverse(sender);
|
||||
Bukkit.dispatchCommand(commandSender, command);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Sender getConsoleSender() {
|
||||
return GrimACBukkitLoaderPlugin.LOADER.getBukkitSenderFactory().map(Bukkit.getConsoleSender());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerOutgoingPluginChannel(String name) {
|
||||
GrimACBukkitLoaderPlugin.LOADER.getServer().getMessenger().registerOutgoingPluginChannel(GrimACBukkitLoaderPlugin.LOADER, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getTPS() {
|
||||
return SpigotReflectionUtil.getTPS();
|
||||
}
|
||||
}
|
||||
@@ -1,163 +0,0 @@
|
||||
package ac.grim.grimac.platform.bukkit;
|
||||
|
||||
import ac.grim.grimac.GrimAPI;
|
||||
import ac.grim.grimac.api.GrimAPIProvider;
|
||||
import ac.grim.grimac.api.GrimAbstractAPI;
|
||||
import ac.grim.grimac.api.plugin.GrimPlugin;
|
||||
import ac.grim.grimac.events.GrimExtensionManager;
|
||||
import ac.grim.grimac.manager.init.Initable;
|
||||
import ac.grim.grimac.manager.init.start.ExemptOnlinePlayersOnReload;
|
||||
import ac.grim.grimac.manager.init.start.StartableInitable;
|
||||
import ac.grim.grimac.platform.api.Platform;
|
||||
import ac.grim.grimac.platform.api.PlatformLoader;
|
||||
import ac.grim.grimac.platform.api.PlatformServer;
|
||||
import ac.grim.grimac.platform.api.manager.ItemResetHandler;
|
||||
import ac.grim.grimac.platform.api.manager.MessagePlaceHolderManager;
|
||||
import ac.grim.grimac.platform.api.manager.CommandAdapter;
|
||||
import ac.grim.grimac.platform.api.manager.PlatformPluginManager;
|
||||
import ac.grim.grimac.platform.api.player.PlatformPlayerFactory;
|
||||
import ac.grim.grimac.platform.api.scheduler.PlatformScheduler;
|
||||
import ac.grim.grimac.platform.api.sender.Sender;
|
||||
import ac.grim.grimac.platform.api.sender.SenderFactory;
|
||||
import ac.grim.grimac.platform.bukkit.initables.BukkitBStats;
|
||||
import ac.grim.grimac.platform.bukkit.initables.BukkitEventManager;
|
||||
import ac.grim.grimac.platform.bukkit.initables.BukkitTickEndEvent;
|
||||
import ac.grim.grimac.platform.bukkit.manager.BukkitItemResetHandler;
|
||||
import ac.grim.grimac.platform.bukkit.manager.BukkitMessagePlaceHolderManager;
|
||||
import ac.grim.grimac.platform.bukkit.manager.BukkitParserDescriptorFactory;
|
||||
import ac.grim.grimac.platform.bukkit.manager.BukkitPermissionRegistrationManager;
|
||||
import ac.grim.grimac.platform.bukkit.manager.BukkitPlatformPluginManager;
|
||||
import ac.grim.grimac.platform.bukkit.player.BukkitPlatformPlayerFactory;
|
||||
import ac.grim.grimac.platform.bukkit.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.lazy.LazyHolder;
|
||||
import com.github.retrooper.packetevents.PacketEventsAPI;
|
||||
import io.github.retrooper.packetevents.factory.spigot.SpigotPacketEventsBuilder;
|
||||
import lombok.Getter;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.plugin.ServicePriority;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.incendo.cloud.CommandManager;
|
||||
import org.incendo.cloud.brigadier.BrigadierSetting;
|
||||
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 {
|
||||
|
||||
public static GrimACBukkitLoaderPlugin LOADER;
|
||||
|
||||
private final LazyHolder<PlatformScheduler> scheduler = LazyHolder.simple(this::createScheduler);
|
||||
private final LazyHolder<PacketEventsAPI<?>> packetEvents = LazyHolder.simple(() -> SpigotPacketEventsBuilder.build(this));
|
||||
private final LazyHolder<BukkitSenderFactory> senderFactory = LazyHolder.simple(BukkitSenderFactory::new);
|
||||
private final LazyHolder<CommandManager<Sender>> commandManager = LazyHolder.simple(this::createCommandManager);
|
||||
private final LazyHolder<ItemResetHandler> itemResetHandler = LazyHolder.simple(BukkitItemResetHandler::new);
|
||||
|
||||
@Getter private final PlatformPlayerFactory platformPlayerFactory = new BukkitPlatformPlayerFactory();
|
||||
@Getter private final CommandAdapter commandAdapter = new BukkitParserDescriptorFactory();
|
||||
@Getter private final PlatformPluginManager pluginManager = new BukkitPlatformPluginManager();
|
||||
@Getter private final GrimPlugin plugin;
|
||||
@Getter private final PlatformServer platformServer = new BukkitPlatformServer();
|
||||
@Getter private final MessagePlaceHolderManager messagePlaceHolderManager = new BukkitMessagePlaceHolderManager();
|
||||
@Getter private final BukkitPermissionRegistrationManager permissionManager = new BukkitPermissionRegistrationManager();
|
||||
|
||||
public GrimACBukkitLoaderPlugin() {
|
||||
GrimExtensionManager extensionManager = GrimAPI.INSTANCE.getExtensionManager();
|
||||
BukkitResolverRegistrar registrar = new BukkitResolverRegistrar(extensionManager);
|
||||
registrar.registerAll();
|
||||
this.plugin = registrar.resolvePlugin(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
LOADER = this;
|
||||
GrimAPI.INSTANCE.load(this, this.getBukkitInitTasks());
|
||||
}
|
||||
|
||||
private Initable[] getBukkitInitTasks() {
|
||||
return new Initable[] {
|
||||
new ExemptOnlinePlayersOnReload(),
|
||||
new BukkitEventManager(),
|
||||
new BukkitTickEndEvent(),
|
||||
new BukkitBStats(),
|
||||
(StartableInitable) () -> {
|
||||
if (BukkitMessagePlaceHolderManager.hasPlaceholderAPI) {
|
||||
new PlaceholderAPIExpansion().register();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
GrimAPI.INSTANCE.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
GrimAPI.INSTANCE.stop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlatformScheduler getScheduler() {
|
||||
return scheduler.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PacketEventsAPI<?> getPacketEvents() {
|
||||
return packetEvents.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandManager<Sender> getCommandManager() {
|
||||
return commandManager.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemResetHandler getItemResetHandler() {
|
||||
return itemResetHandler.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SenderFactory<CommandSender> getSenderFactory() {
|
||||
return senderFactory.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerAPIService() {
|
||||
GrimAPIProvider.init(GrimAPI.INSTANCE.getExternalAPI());
|
||||
Bukkit.getServicesManager().register(GrimAbstractAPI.class, GrimAPI.INSTANCE.getExternalAPI(), GrimACBukkitLoaderPlugin.LOADER, ServicePriority.Normal);
|
||||
}
|
||||
|
||||
private PlatformScheduler createScheduler() {
|
||||
return GrimAPI.INSTANCE.getPlatform() == Platform.FOLIA ? new FoliaPlatformScheduler() : new BukkitPlatformScheduler();
|
||||
}
|
||||
|
||||
private CommandManager<Sender> createCommandManager() {
|
||||
LegacyPaperCommandManager<Sender> manager = new LegacyPaperCommandManager<>(
|
||||
this,
|
||||
ExecutionCoordinator.simpleCoordinator(),
|
||||
senderFactory.get()
|
||||
);
|
||||
if (manager.hasCapability(CloudBukkitCapabilities.NATIVE_BRIGADIER)) {
|
||||
manager.registerBrigadier();
|
||||
CloudBrigadierManager<Sender, ?> cbm = manager.brigadierManager();
|
||||
Configurable<BrigadierSetting> settings = cbm.settings();
|
||||
settings.set(BrigadierSetting.FORCE_EXECUTABLE, true);
|
||||
} else if (manager.hasCapability(CloudBukkitCapabilities.ASYNCHRONOUS_COMPLETION)) {
|
||||
manager.registerAsynchronousCompletions();
|
||||
}
|
||||
return manager;
|
||||
}
|
||||
|
||||
public BukkitSenderFactory getBukkitSenderFactory() {
|
||||
return LOADER.senderFactory.get();
|
||||
}
|
||||
}
|
||||
-36
@@ -1,36 +0,0 @@
|
||||
package ac.grim.grimac.platform.bukkit.command;
|
||||
|
||||
import ac.grim.grimac.GrimAPI;
|
||||
import ac.grim.grimac.platform.api.command.PlayerSelector;
|
||||
import ac.grim.grimac.platform.api.sender.Sender;
|
||||
import ac.grim.grimac.platform.bukkit.sender.BukkitSenderFactory;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.incendo.cloud.bukkit.data.SinglePlayerSelector;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class BukkitPlayerSelectorAdapter implements PlayerSelector {
|
||||
private final SinglePlayerSelector bukkitSelector;
|
||||
|
||||
@Override
|
||||
public boolean isSingle() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Sender getSinglePlayer() {
|
||||
return ((BukkitSenderFactory) GrimAPI.INSTANCE.getSenderFactory()).map(bukkitSelector.single());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Sender> getPlayers() {
|
||||
return Collections.singletonList(((BukkitSenderFactory) GrimAPI.INSTANCE.getSenderFactory()).map(bukkitSelector.single()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String inputString() {
|
||||
return bukkitSelector.inputString();
|
||||
}
|
||||
}
|
||||
-28
@@ -1,28 +0,0 @@
|
||||
package ac.grim.grimac.platform.bukkit.command;
|
||||
|
||||
import ac.grim.grimac.platform.api.command.AbstractPlayerSelectorParser;
|
||||
import ac.grim.grimac.platform.api.command.PlayerSelector;
|
||||
import org.incendo.cloud.bukkit.parser.selector.SinglePlayerSelectorParser;
|
||||
import org.incendo.cloud.context.CommandContext;
|
||||
import org.incendo.cloud.parser.ParserDescriptor;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class BukkitPlayerSelectorParser<C> extends AbstractPlayerSelectorParser<C> {
|
||||
|
||||
@Override
|
||||
public ParserDescriptor<C, PlayerSelector> descriptor() {
|
||||
return super.createDescriptor();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ParserDescriptor<C, ?> getPlatformSpecificDescriptor() {
|
||||
return SinglePlayerSelectorParser.singlePlayerSelectorParser();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CompletableFuture<PlayerSelector> adaptToCommonSelector(CommandContext<C> context, Object platformSpecificSelector) {
|
||||
return CompletableFuture.completedFuture(new BukkitPlayerSelectorAdapter((org.incendo.cloud.bukkit.data.SinglePlayerSelector) platformSpecificSelector));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
package ac.grim.grimac.platform.bukkit.entity;
|
||||
|
||||
import ac.grim.grimac.platform.api.entity.GrimEntity;
|
||||
import ac.grim.grimac.platform.api.world.PlatformWorld;
|
||||
import ac.grim.grimac.platform.bukkit.utils.convert.BukkitConversionUtils;
|
||||
import ac.grim.grimac.platform.bukkit.utils.reflection.PaperUtils;
|
||||
import ac.grim.grimac.platform.bukkit.world.BukkitPlatformWorld;
|
||||
import ac.grim.grimac.utils.math.Location;
|
||||
import com.github.retrooper.packetevents.PacketEvents;
|
||||
import com.github.retrooper.packetevents.manager.server.ServerVersion;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class BukkitGrimEntity implements GrimEntity {
|
||||
|
||||
protected static final boolean CAN_USE_DIRECT_GETTERS = PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_20_1) && PaperUtils.PAPER;
|
||||
|
||||
private final Entity entity;
|
||||
private BukkitPlatformWorld bukkitPlatformWorld;
|
||||
|
||||
public BukkitGrimEntity(Entity entity) {
|
||||
this.entity = Objects.requireNonNull(entity);
|
||||
}
|
||||
|
||||
public Entity getBukkitEntity() {
|
||||
return this.entity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getUniqueId() {
|
||||
return entity.getUniqueId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean eject() {
|
||||
return entity.eject();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Boolean> teleportAsync(Location location) {
|
||||
org.bukkit.Location bLoc = BukkitConversionUtils.toBukkitLocation(location);
|
||||
return PaperUtils.teleportAsync(this.entity, bLoc);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public Entity getNative() {
|
||||
return entity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDead() {
|
||||
return this.entity.isDead();
|
||||
}
|
||||
|
||||
// TODO replace with PlayerWorldChangeEvent listener instead of checking for equality for better performance
|
||||
@Override
|
||||
public PlatformWorld getWorld() {
|
||||
if (bukkitPlatformWorld == null || !bukkitPlatformWorld.getBukkitWorld().equals(entity.getWorld())) {
|
||||
bukkitPlatformWorld = new BukkitPlatformWorld(entity.getWorld());
|
||||
}
|
||||
|
||||
return bukkitPlatformWorld;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Location getLocation() {
|
||||
org.bukkit.Location location = this.entity.getLocation();
|
||||
return new Location(
|
||||
this.getWorld(),
|
||||
location.getX(),
|
||||
location.getY(),
|
||||
location.getZ(),
|
||||
location.getYaw(),
|
||||
location.getPitch()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double distanceSquared(double oX, double oY, double oZ) {
|
||||
if (CAN_USE_DIRECT_GETTERS) {
|
||||
double x = this.entity.getX();
|
||||
double y = this.entity.getY();
|
||||
double z = this.entity.getZ();
|
||||
double distX = (x - oX) * (x - oX);
|
||||
double distY = (y - oY) * (y - oY);
|
||||
double distZ = (z - oZ) * (z - oZ);
|
||||
return distX + distY + distZ;
|
||||
} else {
|
||||
org.bukkit.Location location = this.entity.getLocation();
|
||||
double x = location.getX();
|
||||
double y = location.getY();
|
||||
double z = location.getZ();
|
||||
double distX = (x - oX) * (x - oX);
|
||||
double distY = (y - oY) * (y - oY);
|
||||
double distZ = (z - oZ) * (z - oZ);
|
||||
return distX + distY + distZ;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
package ac.grim.grimac.platform.bukkit.initables;
|
||||
|
||||
import ac.grim.grimac.manager.init.start.StartableInitable;
|
||||
import ac.grim.grimac.platform.bukkit.GrimACBukkitLoaderPlugin;
|
||||
import ac.grim.grimac.utils.anticheat.Constants;
|
||||
import io.github.retrooper.packetevents.bstats.bukkit.Metrics;
|
||||
|
||||
public class BukkitBStats implements StartableInitable {
|
||||
@Override
|
||||
public void start() {
|
||||
try {
|
||||
new Metrics(GrimACBukkitLoaderPlugin.LOADER, Constants.BSTATS_PLUGIN_ID);
|
||||
} catch (Exception ignored) {}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
package ac.grim.grimac.platform.bukkit.initables;
|
||||
|
||||
import ac.grim.grimac.manager.init.start.StartableInitable;
|
||||
import ac.grim.grimac.platform.bukkit.GrimACBukkitLoaderPlugin;
|
||||
import ac.grim.grimac.platform.bukkit.events.PistonEvent;
|
||||
import ac.grim.grimac.utils.anticheat.LogUtil;
|
||||
import org.bukkit.Bukkit;
|
||||
|
||||
public class BukkitEventManager implements StartableInitable {
|
||||
public void start() {
|
||||
LogUtil.info("Registering singular bukkit event... (PistonEvent)");
|
||||
|
||||
Bukkit.getPluginManager().registerEvents(new PistonEvent(), GrimACBukkitLoaderPlugin.LOADER);
|
||||
}
|
||||
}
|
||||
-213
@@ -1,213 +0,0 @@
|
||||
package ac.grim.grimac.platform.bukkit.manager;
|
||||
|
||||
import ac.grim.grimac.platform.api.manager.ItemResetHandler;
|
||||
import ac.grim.grimac.platform.api.player.PlatformPlayer;
|
||||
import ac.grim.grimac.platform.bukkit.utils.reflection.PaperUtils;
|
||||
import ac.grim.grimac.utils.reflection.ReflectionUtils;
|
||||
import com.github.retrooper.packetevents.PacketEvents;
|
||||
import com.github.retrooper.packetevents.manager.server.ServerVersion;
|
||||
import com.github.retrooper.packetevents.protocol.player.InteractionHand;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.LivingEntity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.EquipmentSlot;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class BukkitItemResetHandler implements ItemResetHandler {
|
||||
private static final Consumer<Player> resetItemUsage;
|
||||
private static final Predicate<Player> isUsingItem;
|
||||
private static final Function<Player, InteractionHand> getItemUsageHand;
|
||||
|
||||
@Override
|
||||
public void resetItemUsage(@Nullable PlatformPlayer player) {
|
||||
if (player != null) resetItemUsage.accept((Player) player.getNative());
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable InteractionHand getItemUsageHand(@Nullable PlatformPlayer player) {
|
||||
return player == null ? null : getItemUsageHand.apply((Player) player.getNative());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUsingItem(@Nullable PlatformPlayer player) {
|
||||
return player != null && isUsingItem.test((Player) player.getNative());
|
||||
}
|
||||
|
||||
static {
|
||||
final ServerVersion version = PacketEvents.getAPI().getServerManager().getVersion();
|
||||
|
||||
boolean legacy = version.isOlderThanOrEquals(ServerVersion.V_1_8_8);
|
||||
|
||||
try {
|
||||
Method getHandle_ = null;
|
||||
|
||||
if (PaperUtils.PAPER && version.isNewerThanOrEquals(ServerVersion.V_1_20_5)) {
|
||||
Class<?> cls = ReflectionUtils.getClass("org.bukkit.craftbukkit.entity.CraftLivingEntity");
|
||||
if (cls != null) getHandle_ = cls.getMethod("getHandle");
|
||||
}
|
||||
|
||||
boolean obfuscated = getHandle_ == null;
|
||||
String nmsPackage = obfuscated ? Bukkit.getServer().getClass().getPackageName().split("\\.")[3] : null;
|
||||
|
||||
if (obfuscated) {
|
||||
String clazzName = legacy ? "CraftHumanEntity" : "CraftLivingEntity";
|
||||
getHandle_ = Class.forName("org.bukkit.craftbukkit." + nmsPackage + ".entity." + clazzName).getMethod("getHandle");
|
||||
}
|
||||
|
||||
final Method getHandle = getHandle_;
|
||||
Class<?> clazz = getHandle_.getReturnType();
|
||||
|
||||
if (version.isNewerThanOrEquals(ServerVersion.V_1_10)) {
|
||||
isUsingItem = Player::isHandRaised;
|
||||
} else {
|
||||
Method method = clazz.getMethod(switch (Objects.requireNonNull(nmsPackage)) {
|
||||
case "v1_8_R3" -> "bS";
|
||||
case "v1_9_R1" -> "cs";
|
||||
case "v1_9_R2" -> "ct";
|
||||
default -> throw new IllegalStateException("You are using an unsupported server version! (" + version.getReleaseName() + ")");
|
||||
});
|
||||
isUsingItem = player -> {
|
||||
try {
|
||||
return (boolean) method.invoke(getHandle.invoke(player));
|
||||
} catch (IllegalAccessException | InvocationTargetException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (legacy) {
|
||||
getItemUsageHand = player -> isUsingItem.test(player) ? InteractionHand.MAIN_HAND : null;
|
||||
} else if (PaperUtils.PAPER && version.isNewerThanOrEquals(ServerVersion.V_1_16_5)) {
|
||||
getItemUsageHand = player -> player.isHandRaised()
|
||||
? player.getHandRaised() == EquipmentSlot.OFF_HAND
|
||||
? InteractionHand.OFF_HAND
|
||||
: InteractionHand.MAIN_HAND
|
||||
: null;
|
||||
} else {
|
||||
Method method = clazz.getMethod(switch (Objects.requireNonNull(nmsPackage)) {
|
||||
case "v1_9_R1" -> "ct";
|
||||
case "v1_9_R2" -> "cu";
|
||||
case "v1_10_R1" -> "cy";
|
||||
case "v1_11_R1" -> "cz";
|
||||
case "v1_12_R1" -> "cH";
|
||||
case "v1_13_R1", "v1_13_R2", "v1_14_R1" -> "cU";
|
||||
case "v1_15_R1", "v1_16_R1", "v1_16_R2", "v1_16_R3", "v1_17_R1" -> "getRaisedHand";
|
||||
case "v1_18_R1" -> "eM";
|
||||
case "v1_18_R2" -> "eN";
|
||||
case "v1_19_R1" -> "eU";
|
||||
case "v1_19_R2" -> "fa";
|
||||
case "v1_19_R3" -> "ff";
|
||||
case "v1_20_R1" -> "fj";
|
||||
case "v1_20_R2" -> "fn";
|
||||
case "v1_20_R3" -> "fo";
|
||||
case "v1_20_R4" -> "fw";
|
||||
case "v1_21_R1" -> "fs";
|
||||
case "v1_21_R2", "v1_21_R3", "v1_21_R4" -> "fA";
|
||||
case "v1_21_R5" -> "fH";
|
||||
case "v1_21_R6" -> "fP";
|
||||
default -> throw new IllegalStateException("You are using an unsupported server version! (" + version.getReleaseName() + ")");
|
||||
});
|
||||
getItemUsageHand = player -> {
|
||||
try {
|
||||
return isUsingItem.test(player)
|
||||
? ((Enum<?>) method.invoke(getHandle.invoke(player))).ordinal() == 0
|
||||
? InteractionHand.MAIN_HAND
|
||||
: InteractionHand.OFF_HAND
|
||||
: null;
|
||||
} catch (IllegalAccessException | InvocationTargetException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Method setLivingEntityFlag;
|
||||
|
||||
if (version.isNewerThanOrEquals(ServerVersion.V_1_19)) {
|
||||
String name = obfuscated ? "c" : "setLivingEntityFlag";
|
||||
setLivingEntityFlag = clazz.getDeclaredMethod(name, int.class, boolean.class);
|
||||
setLivingEntityFlag.setAccessible(true);
|
||||
} else {
|
||||
setLivingEntityFlag = null;
|
||||
}
|
||||
|
||||
if (PaperUtils.PAPER && version.isNewerThan(ServerVersion.V_1_17)) {
|
||||
resetItemUsage = setLivingEntityFlag == null ? LivingEntity::clearActiveItem : player -> {
|
||||
try {
|
||||
setLivingEntityFlag.invoke(getHandle.invoke(player), 1, false);
|
||||
} catch (IllegalAccessException | InvocationTargetException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
player.clearActiveItem();
|
||||
};
|
||||
} else {
|
||||
Method method = clazz.getMethod(switch (Objects.requireNonNull(nmsPackage)) {
|
||||
case "v1_8_R3" -> "bV";
|
||||
case "v1_9_R1" -> "cz";
|
||||
case "v1_9_R2" -> "cA";
|
||||
case "v1_10_R1" -> "cE";
|
||||
case "v1_11_R1" -> "cF";
|
||||
case "v1_12_R1" -> "cN";
|
||||
case "v1_13_R1", "v1_13_R2" -> "da";
|
||||
case "v1_14_R1" -> "dp";
|
||||
case "v1_15_R1" -> "dH";
|
||||
case "v1_16_R1", "v1_16_R2", "v1_16_R3", "v1_17_R1" -> "clearActiveItem";
|
||||
case "v1_18_R1" -> "eR";
|
||||
case "v1_18_R2" -> "eS";
|
||||
case "v1_19_R1" -> "eZ";
|
||||
case "v1_19_R2" -> "ff";
|
||||
case "v1_19_R3" -> "fk";
|
||||
case "v1_20_R1" -> "fo";
|
||||
case "v1_20_R2" -> "fs";
|
||||
case "v1_20_R3" -> "ft";
|
||||
case "v1_20_R4" -> "fB";
|
||||
case "v1_21_R1" -> "fx";
|
||||
case "v1_21_R2", "v1_21_R3", "v1_21_R4" -> "fF";
|
||||
case "v1_21_R5" -> "fM";
|
||||
case "v1_21_R6" -> "fU";
|
||||
default -> throw new IllegalStateException("You are using an unsupported server version! (" + version.getReleaseName() + ")");
|
||||
});
|
||||
|
||||
if (legacy) { // 1.8.8
|
||||
resetItemUsage = player -> {
|
||||
try {
|
||||
method.invoke(getHandle.invoke(player));
|
||||
|
||||
// in 1.8 we need to resync item usage manually,
|
||||
// only do so if the player is using an item
|
||||
if (isUsingItem.test(player)) player.updateInventory();
|
||||
} catch (IllegalAccessException | InvocationTargetException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
};
|
||||
} else if (setLivingEntityFlag == null) { // 1.9-1.18.2
|
||||
resetItemUsage = player -> {
|
||||
try {
|
||||
method.invoke(getHandle.invoke(player));
|
||||
} catch (IllegalAccessException | InvocationTargetException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
};
|
||||
} else { // 1.19+
|
||||
resetItemUsage = player -> {
|
||||
try {
|
||||
Object handle = getHandle.invoke(player);
|
||||
setLivingEntityFlag.invoke(handle, 1, false);
|
||||
method.invoke(handle);
|
||||
} catch (IllegalAccessException | InvocationTargetException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
throw t instanceof RuntimeException e ? e : new RuntimeException(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
-19
@@ -1,19 +0,0 @@
|
||||
package ac.grim.grimac.platform.bukkit.manager;
|
||||
|
||||
import ac.grim.grimac.platform.api.manager.MessagePlaceHolderManager;
|
||||
import ac.grim.grimac.platform.api.player.PlatformPlayer;
|
||||
import ac.grim.grimac.platform.bukkit.player.BukkitPlatformPlayer;
|
||||
import com.github.retrooper.packetevents.util.reflection.Reflection;
|
||||
import me.clip.placeholderapi.PlaceholderAPI;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class BukkitMessagePlaceHolderManager implements MessagePlaceHolderManager {
|
||||
public static final boolean hasPlaceholderAPI = Reflection.getClassByNameWithoutException("me.clip.placeholderapi.PlaceholderAPI") != null;
|
||||
|
||||
@Override
|
||||
public @NotNull String replacePlaceholders(@Nullable PlatformPlayer player, @NotNull String string) {
|
||||
if (!hasPlaceholderAPI) return string;
|
||||
return PlaceholderAPI.setPlaceholders(player instanceof BukkitPlatformPlayer bukkitPlatformPlayer ? bukkitPlatformPlayer.getBukkitPlayer() : null, string);
|
||||
}
|
||||
}
|
||||
-45
@@ -1,45 +0,0 @@
|
||||
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.sender.Sender;
|
||||
import ac.grim.grimac.platform.bukkit.command.BukkitPlayerSelectorParser;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.incendo.cloud.bukkit.BukkitCommandContextKeys;
|
||||
import org.incendo.cloud.parser.ParserDescriptor;
|
||||
import org.incendo.cloud.suggestion.Suggestion;
|
||||
import org.incendo.cloud.suggestion.SuggestionProvider;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class BukkitParserDescriptorFactory implements CommandAdapter {
|
||||
|
||||
// Parser is kept as a field because its stateless
|
||||
private final BukkitPlayerSelectorParser<Sender> bukkitPlayerSelectorParser = new BukkitPlayerSelectorParser<>();
|
||||
|
||||
@Override
|
||||
public ParserDescriptor<Sender, PlayerSelector> singlePlayerSelectorParser() {
|
||||
return bukkitPlayerSelectorParser.descriptor();
|
||||
}
|
||||
|
||||
// TODO (Cross-platform) better + brigadier suggestions
|
||||
@Override
|
||||
public SuggestionProvider<Sender> onlinePlayerSuggestions() {
|
||||
return (context, input) -> {
|
||||
List<Suggestion> suggestions = new ArrayList<>();
|
||||
|
||||
for(Player player : Bukkit.getOnlinePlayers()) {
|
||||
CommandSender bukkit = context.get(BukkitCommandContextKeys.BUKKIT_COMMAND_SENDER);
|
||||
if (!(bukkit instanceof Player) || ((Player)bukkit).canSee(player)) {
|
||||
suggestions.add(Suggestion.suggestion(player.getName()));
|
||||
}
|
||||
}
|
||||
|
||||
return CompletableFuture.completedFuture(suggestions);
|
||||
};
|
||||
}
|
||||
}
|
||||
-33
@@ -1,33 +0,0 @@
|
||||
package ac.grim.grimac.platform.bukkit.manager;
|
||||
|
||||
import ac.grim.grimac.platform.api.manager.PermissionRegistrationManager;
|
||||
import ac.grim.grimac.platform.api.permissions.PermissionDefaultValue;
|
||||
import ac.grim.grimac.platform.bukkit.utils.convert.BukkitConversionUtils;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.permissions.Permission;
|
||||
|
||||
public class BukkitPermissionRegistrationManager implements PermissionRegistrationManager {
|
||||
/**
|
||||
* Registers a permission with the specified default value on Bukkit.
|
||||
* This method is only called for dynamic permissions (e.g., check-specific permissions
|
||||
* like "grim.exempt.checkname") that are generated at runtime. Most other static permissions
|
||||
* (e.g., "grim.exempt", "grim.alerts.enable-on-join") are registered with their defaults
|
||||
* in the `plugin.yml` file, which is defined in the Bukkit Gradle build script.
|
||||
*
|
||||
* <p>Dynamic permissions are registered here to ensure they are available for autocomplete
|
||||
* and permission checks immediately on startup. If the permission already exists, its default
|
||||
* value is updated to match the specified value.</p>
|
||||
*
|
||||
* @param name The permission node to register (e.g., "grim.exempt.checkname").
|
||||
* @param defaultValue The default value for the permission.
|
||||
*/
|
||||
@Override
|
||||
public void registerPermission(String name, PermissionDefaultValue defaultValue) {
|
||||
final Permission bukkitPermission = Bukkit.getPluginManager().getPermission(name);
|
||||
if (bukkitPermission == null) {
|
||||
Bukkit.getPluginManager().addPermission(new Permission(name, BukkitConversionUtils.toBukkitPermissionDefault(defaultValue)));
|
||||
} else {
|
||||
bukkitPermission.setDefault(BukkitConversionUtils.toBukkitPermissionDefault(defaultValue));
|
||||
}
|
||||
}
|
||||
}
|
||||
-29
@@ -1,29 +0,0 @@
|
||||
package ac.grim.grimac.platform.bukkit.manager;
|
||||
|
||||
import ac.grim.grimac.platform.api.PlatformPlugin;
|
||||
import ac.grim.grimac.platform.api.manager.PlatformPluginManager;
|
||||
import ac.grim.grimac.platform.bukkit.BukkitPlatformPlugin;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class BukkitPlatformPluginManager implements PlatformPluginManager {
|
||||
|
||||
@Override
|
||||
public PlatformPlugin[] getPlugins() {
|
||||
Plugin[] bukkitPlugins = Bukkit.getPluginManager().getPlugins();
|
||||
PlatformPlugin[] plugins = new PlatformPlugin[bukkitPlugins.length];
|
||||
|
||||
for (int i = 0; i < bukkitPlugins.length; i++) {
|
||||
plugins[i] = new BukkitPlatformPlugin(bukkitPlugins[i]);
|
||||
}
|
||||
|
||||
return plugins;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable PlatformPlugin getPlugin(String pluginName) {
|
||||
Plugin bukkitPlugin = Bukkit.getPluginManager().getPlugin(pluginName);
|
||||
return bukkitPlugin == null ? null : new BukkitPlatformPlugin(bukkitPlugin);
|
||||
}
|
||||
}
|
||||
-34
@@ -1,34 +0,0 @@
|
||||
package ac.grim.grimac.platform.bukkit.player;
|
||||
|
||||
import ac.grim.grimac.platform.api.player.OfflinePlatformPlayer;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class BukkitOfflinePlatformPlayer implements OfflinePlatformPlayer {
|
||||
private final OfflinePlayer offlinePlayer;
|
||||
|
||||
@Override
|
||||
public boolean isOnline() {
|
||||
return offlinePlayer.isOnline();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getName() {
|
||||
return Objects.requireNonNull(offlinePlayer.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull UUID getUniqueId() {
|
||||
return offlinePlayer.getUniqueId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
return o instanceof OfflinePlatformPlayer player && this.getUniqueId().equals(player.getUniqueId());
|
||||
}
|
||||
}
|
||||
-64
@@ -1,64 +0,0 @@
|
||||
package ac.grim.grimac.platform.bukkit.player;
|
||||
|
||||
import ac.grim.grimac.platform.api.player.PlatformInventory;
|
||||
import com.github.retrooper.packetevents.protocol.item.ItemStack;
|
||||
import io.github.retrooper.packetevents.util.SpigotConversionUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class BukkitPlatformInventory implements PlatformInventory {
|
||||
|
||||
private final Player bukkitPlayer;
|
||||
|
||||
@Override
|
||||
public ItemStack getItemInHand() {
|
||||
return SpigotConversionUtil.fromBukkitItemStack(bukkitPlayer.getInventory().getItemInHand());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getItemInOffHand() {
|
||||
return SpigotConversionUtil.fromBukkitItemStack(bukkitPlayer.getInventory().getItemInOffHand());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getStack(int bukkitSlot, int vanillaSlot) {
|
||||
return SpigotConversionUtil.fromBukkitItemStack(bukkitPlayer.getInventory().getItem(bukkitSlot));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getHelmet() {
|
||||
return SpigotConversionUtil.fromBukkitItemStack(bukkitPlayer.getInventory().getHelmet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getChestplate() {
|
||||
return SpigotConversionUtil.fromBukkitItemStack(bukkitPlayer.getInventory().getChestplate());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getLeggings() {
|
||||
return SpigotConversionUtil.fromBukkitItemStack(bukkitPlayer.getInventory().getLeggings());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getBoots() {
|
||||
return SpigotConversionUtil.fromBukkitItemStack(bukkitPlayer.getInventory().getBoots());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack[] getContents() {
|
||||
org.bukkit.inventory.ItemStack[] bukkitItems = bukkitPlayer.getInventory().getContents();
|
||||
ItemStack[] items = new ItemStack[bukkitItems.length];
|
||||
for (int i = 0; i < bukkitItems.length; i++) {
|
||||
if (bukkitItems[i] == null) continue;
|
||||
items[i] = SpigotConversionUtil.fromBukkitItemStack(bukkitItems[i]);
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getOpenInventoryKey() {
|
||||
return bukkitPlayer.getOpenInventory().getType().toString();
|
||||
}
|
||||
}
|
||||
@@ -1,178 +0,0 @@
|
||||
package ac.grim.grimac.platform.bukkit.player;
|
||||
|
||||
import ac.grim.grimac.platform.api.entity.GrimEntity;
|
||||
import ac.grim.grimac.platform.api.player.PlatformInventory;
|
||||
import ac.grim.grimac.platform.api.player.PlatformPlayer;
|
||||
import ac.grim.grimac.platform.api.sender.Sender;
|
||||
import ac.grim.grimac.platform.bukkit.GrimACBukkitLoaderPlugin;
|
||||
import ac.grim.grimac.platform.bukkit.entity.BukkitGrimEntity;
|
||||
import ac.grim.grimac.platform.bukkit.utils.anticheat.MultiLibUtil;
|
||||
import ac.grim.grimac.platform.bukkit.utils.convert.BukkitConversionUtils;
|
||||
import ac.grim.grimac.platform.bukkit.utils.reflection.PaperUtils;
|
||||
import ac.grim.grimac.utils.common.arguments.CommonGrimArguments;
|
||||
import ac.grim.grimac.utils.math.Location;
|
||||
import com.github.retrooper.packetevents.PacketEvents;
|
||||
import com.github.retrooper.packetevents.protocol.player.GameMode;
|
||||
import com.github.retrooper.packetevents.protocol.player.User;
|
||||
import com.github.retrooper.packetevents.util.Vector3d;
|
||||
import io.github.retrooper.packetevents.util.SpigotConversionUtil;
|
||||
import lombok.Getter;
|
||||
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.permissions.Permission;
|
||||
import org.bukkit.permissions.PermissionDefault;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class BukkitPlatformPlayer extends BukkitGrimEntity implements PlatformPlayer {
|
||||
|
||||
private static final BukkitAudiences audiences = BukkitAudiences.create(GrimACBukkitLoaderPlugin.LOADER);
|
||||
|
||||
@Getter
|
||||
private final Player bukkitPlayer;
|
||||
@Getter
|
||||
private final PlatformInventory inventory;
|
||||
|
||||
private final @Nullable User user;
|
||||
|
||||
public BukkitPlatformPlayer(Player bukkitPlayer) {
|
||||
super(bukkitPlayer);
|
||||
this.bukkitPlayer = bukkitPlayer;
|
||||
this.inventory = new BukkitPlatformInventory(bukkitPlayer);
|
||||
if (CommonGrimArguments.USE_CHAT_FAST_BYPASS.value()) {
|
||||
Object channel = PacketEvents.getAPI().getProtocolManager().getChannel(bukkitPlayer.getUniqueId());
|
||||
this.user = PacketEvents.getAPI().getProtocolManager().getUser(channel);
|
||||
} else {
|
||||
this.user = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void kickPlayer(String textReason) {
|
||||
bukkitPlayer.kickPlayer(textReason);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(String s) {
|
||||
return bukkitPlayer.hasPermission(s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(String s, boolean defaultIfUnset) {
|
||||
return this.bukkitPlayer.hasPermission(new Permission(s, defaultIfUnset ? PermissionDefault.TRUE : PermissionDefault.FALSE));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSneaking() {
|
||||
return bukkitPlayer.isSneaking();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSneaking(boolean isSneaking) {
|
||||
bukkitPlayer.setSneaking(isSneaking);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessage(String message) {
|
||||
if (CommonGrimArguments.USE_CHAT_FAST_BYPASS.value() && user != null) {
|
||||
user.sendMessage(message);
|
||||
} else {
|
||||
bukkitPlayer.sendMessage(message);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessage(Component message) {
|
||||
if (CommonGrimArguments.USE_CHAT_FAST_BYPASS.value() && user != null) {
|
||||
user.sendMessage(message);
|
||||
} else {
|
||||
audiences.player(bukkitPlayer).sendMessage(message);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOnline() {
|
||||
return bukkitPlayer.isOnline();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return bukkitPlayer.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateInventory() {
|
||||
bukkitPlayer.updateInventory();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector3d getPosition() {
|
||||
if (CAN_USE_DIRECT_GETTERS) {
|
||||
return new Vector3d(this.bukkitPlayer.getX(), this.bukkitPlayer.getY(), this.bukkitPlayer.getZ());
|
||||
} else {
|
||||
org.bukkit.Location location = this.bukkitPlayer.getLocation();
|
||||
return new Vector3d(location.getX(), location.getY(), location.getZ());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable GrimEntity getVehicle() {
|
||||
return bukkitPlayer.getVehicle() == null ? null : new BukkitGrimEntity(bukkitPlayer.getVehicle());
|
||||
}
|
||||
|
||||
@Override
|
||||
public GameMode getGameMode() {
|
||||
return SpigotConversionUtil.fromBukkitGameMode(bukkitPlayer.getGameMode());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setGameMode(GameMode gameMode) {
|
||||
bukkitPlayer.setGameMode(SpigotConversionUtil.toBukkitGameMode(gameMode));
|
||||
}
|
||||
|
||||
public World getBukkitWorld() {
|
||||
return bukkitPlayer.getWorld();
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getUniqueId() {
|
||||
return bukkitPlayer.getUniqueId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean eject() {
|
||||
return bukkitPlayer.eject();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Boolean> teleportAsync(Location location) {
|
||||
org.bukkit.Location bLoc = BukkitConversionUtils.toBukkitLocation(location);
|
||||
return PaperUtils.teleportAsync(this.bukkitPlayer, bLoc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isExternalPlayer() {
|
||||
return MultiLibUtil.isExternalPlayer(this.bukkitPlayer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendPluginMessage(String channelName, byte[] byteArray) {
|
||||
this.bukkitPlayer.sendPluginMessage(GrimACBukkitLoaderPlugin.LOADER, channelName, byteArray);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Sender getSender() {
|
||||
return GrimACBukkitLoaderPlugin.LOADER.getBukkitSenderFactory().map(this.bukkitPlayer);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public Player getNative() {
|
||||
return this.bukkitPlayer;
|
||||
}
|
||||
}
|
||||
-57
@@ -1,57 +0,0 @@
|
||||
package ac.grim.grimac.platform.bukkit.player;
|
||||
|
||||
import ac.grim.grimac.platform.api.player.AbstractPlatformPlayerFactory;
|
||||
import ac.grim.grimac.platform.api.player.OfflinePlatformPlayer;
|
||||
import ac.grim.grimac.platform.api.player.PlatformPlayer;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.UUID;
|
||||
|
||||
|
||||
public class BukkitPlatformPlayerFactory extends AbstractPlatformPlayerFactory<Player> {
|
||||
|
||||
@Override
|
||||
protected Player getNativePlayer(@NotNull UUID uuid) {
|
||||
return Bukkit.getPlayer(uuid);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Player getNativePlayer(@NotNull String name) {
|
||||
return Bukkit.getPlayer(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PlatformPlayer createPlatformPlayer(@NotNull Player nativePlayer) {
|
||||
return new BukkitPlatformPlayer(nativePlayer);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected UUID getPlayerUUID(@NotNull Player nativePlayer) {
|
||||
return nativePlayer.getUniqueId();
|
||||
}
|
||||
|
||||
// The cast is safe because Bukkit.getOnlinePlayers() is guaranteed to contain Player or its subtypes,
|
||||
// and we're only reading from it.
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
protected Collection<Player> getNativeOnlinePlayers() {
|
||||
// Cast Collection<? extends Player> to Collection<Player>
|
||||
return (Collection<Player>) Bukkit.getOnlinePlayers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public OfflinePlatformPlayer getOfflineFromUUID(@NotNull UUID uuid) {
|
||||
OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(uuid);
|
||||
return new BukkitOfflinePlatformPlayer(offlinePlayer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public OfflinePlatformPlayer getOfflineFromName(@NotNull String name) {
|
||||
OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(name);
|
||||
return new BukkitOfflinePlatformPlayer(offlinePlayer);
|
||||
}
|
||||
}
|
||||
-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);
|
||||
}
|
||||
}
|
||||
-56
@@ -1,56 +0,0 @@
|
||||
package ac.grim.grimac.platform.bukkit.scheduler.bukkit;
|
||||
|
||||
import ac.grim.grimac.api.plugin.GrimPlugin;
|
||||
import ac.grim.grimac.platform.api.scheduler.AsyncScheduler;
|
||||
import ac.grim.grimac.platform.api.scheduler.PlatformScheduler;
|
||||
import ac.grim.grimac.platform.api.scheduler.TaskHandle;
|
||||
import ac.grim.grimac.platform.bukkit.GrimACBukkitLoaderPlugin;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.scheduler.BukkitScheduler;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class BukkitAsyncScheduler implements AsyncScheduler {
|
||||
|
||||
private final BukkitScheduler bukkitScheduler = Bukkit.getScheduler();
|
||||
|
||||
@Override
|
||||
public TaskHandle runNow(@NotNull GrimPlugin plugin, @NotNull Runnable task) {
|
||||
return new BukkitTaskHandle(bukkitScheduler.runTaskAsynchronously(GrimACBukkitLoaderPlugin.LOADER, task));
|
||||
}
|
||||
|
||||
@Override
|
||||
public TaskHandle runDelayed(@NotNull GrimPlugin plugin, @NotNull Runnable task, long delay, @NotNull TimeUnit timeUnit) {
|
||||
return new BukkitTaskHandle(bukkitScheduler.runTaskLaterAsynchronously(
|
||||
GrimACBukkitLoaderPlugin.LOADER,
|
||||
task,
|
||||
PlatformScheduler.convertTimeToTicks(delay, timeUnit)
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
public TaskHandle runAtFixedRate(@NotNull GrimPlugin plugin, @NotNull Runnable task, long delay, long period, @NotNull TimeUnit timeUnit) {
|
||||
return new BukkitTaskHandle(bukkitScheduler.runTaskTimerAsynchronously(
|
||||
GrimACBukkitLoaderPlugin.LOADER,
|
||||
task,
|
||||
PlatformScheduler.convertTimeToTicks(delay, timeUnit),
|
||||
PlatformScheduler.convertTimeToTicks(period, timeUnit)
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
public TaskHandle runAtFixedRate(@NotNull GrimPlugin plugin, @NotNull Runnable task, long initialDelayTicks, long periodTicks) {
|
||||
return new BukkitTaskHandle(bukkitScheduler.runTaskTimerAsynchronously(
|
||||
GrimACBukkitLoaderPlugin.LOADER,
|
||||
task,
|
||||
initialDelayTicks,
|
||||
periodTicks
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel(@NotNull GrimPlugin plugin) {
|
||||
bukkitScheduler.cancelTasks(GrimACBukkitLoaderPlugin.LOADER);
|
||||
}
|
||||
}
|
||||
-35
@@ -1,35 +0,0 @@
|
||||
package ac.grim.grimac.platform.bukkit.scheduler.bukkit;
|
||||
|
||||
import ac.grim.grimac.api.plugin.GrimPlugin;
|
||||
import ac.grim.grimac.platform.api.entity.GrimEntity;
|
||||
import ac.grim.grimac.platform.api.scheduler.EntityScheduler;
|
||||
import ac.grim.grimac.platform.api.scheduler.TaskHandle;
|
||||
import ac.grim.grimac.platform.bukkit.GrimACBukkitLoaderPlugin;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.scheduler.BukkitScheduler;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class BukkitEntityScheduler implements EntityScheduler {
|
||||
private final BukkitScheduler scheduler = Bukkit.getScheduler();
|
||||
|
||||
@Override
|
||||
public void execute(@NotNull GrimEntity entity, @NotNull GrimPlugin plugin, @NotNull Runnable run, @Nullable Runnable retired, long delay) {
|
||||
scheduler.runTaskLater(GrimACBukkitLoaderPlugin.LOADER, run, delay);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TaskHandle run(@NotNull GrimEntity entity, @NotNull GrimPlugin plugin, @NotNull Runnable task, @Nullable Runnable retired) {
|
||||
return new BukkitTaskHandle(scheduler.runTask(GrimACBukkitLoaderPlugin.LOADER, task));
|
||||
}
|
||||
|
||||
@Override
|
||||
public TaskHandle runDelayed(@NotNull GrimEntity entity, @NotNull GrimPlugin plugin, @NotNull Runnable task, @Nullable Runnable retired, long delayTicks) {
|
||||
return new BukkitTaskHandle(scheduler.runTaskLater(GrimACBukkitLoaderPlugin.LOADER, task, delayTicks));
|
||||
}
|
||||
|
||||
@Override
|
||||
public TaskHandle runAtFixedRate(@NotNull GrimEntity entity, @NotNull GrimPlugin plugin, @NotNull Runnable task, @Nullable Runnable retired, long initialDelayTicks, long periodTicks) {
|
||||
return new BukkitTaskHandle(scheduler.runTaskTimer(GrimACBukkitLoaderPlugin.LOADER, task, initialDelayTicks, periodTicks));
|
||||
}
|
||||
}
|
||||
-39
@@ -1,39 +0,0 @@
|
||||
package ac.grim.grimac.platform.bukkit.scheduler.bukkit;
|
||||
|
||||
import ac.grim.grimac.api.plugin.GrimPlugin;
|
||||
import ac.grim.grimac.platform.api.scheduler.GlobalRegionScheduler;
|
||||
import ac.grim.grimac.platform.api.scheduler.TaskHandle;
|
||||
import ac.grim.grimac.platform.bukkit.GrimACBukkitLoaderPlugin;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.scheduler.BukkitScheduler;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class BukkitGlobalRegionScheduler implements GlobalRegionScheduler {
|
||||
|
||||
private final BukkitScheduler bukkitScheduler = Bukkit.getScheduler();
|
||||
|
||||
@Override
|
||||
public void execute(@NotNull GrimPlugin plugin, @NotNull Runnable task) {
|
||||
bukkitScheduler.runTask(GrimACBukkitLoaderPlugin.LOADER, task);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TaskHandle run(@NotNull GrimPlugin plugin, @NotNull Runnable task) {
|
||||
return new BukkitTaskHandle(bukkitScheduler.runTask(GrimACBukkitLoaderPlugin.LOADER, task));
|
||||
}
|
||||
|
||||
@Override
|
||||
public TaskHandle runDelayed(@NotNull GrimPlugin plugin, @NotNull Runnable task, long delay) {
|
||||
return new BukkitTaskHandle(bukkitScheduler.runTaskLater(GrimACBukkitLoaderPlugin.LOADER, task, delay));
|
||||
}
|
||||
|
||||
@Override
|
||||
public TaskHandle runAtFixedRate(@NotNull GrimPlugin plugin, @NotNull Runnable task, long initialDelayTicks, long periodTicks) {
|
||||
return new BukkitTaskHandle(bukkitScheduler.runTaskTimer(GrimACBukkitLoaderPlugin.LOADER, task, initialDelayTicks, periodTicks));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel(@NotNull GrimPlugin plugin) {
|
||||
bukkitScheduler.cancelTasks(GrimACBukkitLoaderPlugin.LOADER);
|
||||
}
|
||||
}
|
||||
-13
@@ -1,13 +0,0 @@
|
||||
package ac.grim.grimac.platform.bukkit.scheduler.bukkit;
|
||||
|
||||
import ac.grim.grimac.platform.api.scheduler.PlatformScheduler;
|
||||
import lombok.Getter;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@Getter
|
||||
public class BukkitPlatformScheduler implements PlatformScheduler {
|
||||
private final @NotNull BukkitAsyncScheduler asyncScheduler = new BukkitAsyncScheduler();
|
||||
private final @NotNull BukkitGlobalRegionScheduler globalRegionScheduler = new BukkitGlobalRegionScheduler();
|
||||
private final @NotNull BukkitEntityScheduler entityScheduler = new BukkitEntityScheduler();
|
||||
private final @NotNull BukkitRegionScheduler regionScheduler = new BukkitRegionScheduler();
|
||||
}
|
||||
-56
@@ -1,56 +0,0 @@
|
||||
package ac.grim.grimac.platform.bukkit.scheduler.bukkit;
|
||||
|
||||
import ac.grim.grimac.api.plugin.GrimPlugin;
|
||||
import ac.grim.grimac.platform.api.scheduler.RegionScheduler;
|
||||
import ac.grim.grimac.platform.api.scheduler.TaskHandle;
|
||||
import ac.grim.grimac.platform.api.world.PlatformWorld;
|
||||
import ac.grim.grimac.platform.bukkit.GrimACBukkitLoaderPlugin;
|
||||
import ac.grim.grimac.utils.math.Location;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.scheduler.BukkitScheduler;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class BukkitRegionScheduler implements RegionScheduler {
|
||||
|
||||
private final BukkitScheduler bukkitScheduler = Bukkit.getScheduler();
|
||||
|
||||
@Override
|
||||
public void execute(@NotNull GrimPlugin plugin, @NotNull PlatformWorld world, int chunkX, int chunkZ, @NotNull Runnable task) {
|
||||
bukkitScheduler.runTask(GrimACBukkitLoaderPlugin.LOADER, task);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(@NotNull GrimPlugin plugin, @NotNull Location location, @NotNull Runnable task) {
|
||||
bukkitScheduler.runTask(GrimACBukkitLoaderPlugin.LOADER, task);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TaskHandle run(@NotNull GrimPlugin plugin, @NotNull PlatformWorld world, int chunkX, int chunkZ, @NotNull Runnable task) {
|
||||
return new BukkitTaskHandle(bukkitScheduler.runTask(GrimACBukkitLoaderPlugin.LOADER, task));
|
||||
}
|
||||
|
||||
@Override
|
||||
public TaskHandle run(@NotNull GrimPlugin plugin, @NotNull Location location, @NotNull Runnable task) {
|
||||
return new BukkitTaskHandle(bukkitScheduler.runTask(GrimACBukkitLoaderPlugin.LOADER, task));
|
||||
}
|
||||
|
||||
@Override
|
||||
public TaskHandle runDelayed(@NotNull GrimPlugin plugin, @NotNull PlatformWorld world, int chunkX, int chunkZ, @NotNull Runnable task, long delayTicks) {
|
||||
return new BukkitTaskHandle(bukkitScheduler.runTaskLater(GrimACBukkitLoaderPlugin.LOADER, task, delayTicks));
|
||||
}
|
||||
|
||||
@Override
|
||||
public TaskHandle runDelayed(@NotNull GrimPlugin plugin, @NotNull Location location, @NotNull Runnable task, long delayTicks) {
|
||||
return new BukkitTaskHandle(bukkitScheduler.runTaskLater(GrimACBukkitLoaderPlugin.LOADER, task, delayTicks));
|
||||
}
|
||||
|
||||
@Override
|
||||
public TaskHandle runAtFixedRate(@NotNull GrimPlugin plugin, @NotNull PlatformWorld world, int chunkX, int chunkZ, @NotNull Runnable task, long initialDelayTicks, long periodTicks) {
|
||||
return new BukkitTaskHandle(bukkitScheduler.runTaskTimer(GrimACBukkitLoaderPlugin.LOADER, task, initialDelayTicks, periodTicks));
|
||||
}
|
||||
|
||||
@Override
|
||||
public TaskHandle runAtFixedRate(@NotNull GrimPlugin plugin, @NotNull Location location, @NotNull Runnable task, long initialDelayTicks, long periodTicks) {
|
||||
return new BukkitTaskHandle(bukkitScheduler.runTaskTimer(GrimACBukkitLoaderPlugin.LOADER, task, initialDelayTicks, periodTicks));
|
||||
}
|
||||
}
|
||||
-33
@@ -1,33 +0,0 @@
|
||||
package ac.grim.grimac.platform.bukkit.scheduler.bukkit;
|
||||
|
||||
import ac.grim.grimac.platform.api.scheduler.TaskHandle;
|
||||
import org.bukkit.scheduler.BukkitTask;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class BukkitTaskHandle implements TaskHandle {
|
||||
|
||||
private final @NotNull BukkitTask task;
|
||||
|
||||
@Contract(pure = true)
|
||||
public BukkitTaskHandle(@NotNull BukkitTask task) {
|
||||
this.task = Objects.requireNonNull(task);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSync() {
|
||||
return task.isSync();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
return task.isCancelled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
task.cancel();
|
||||
}
|
||||
}
|
||||
-57
@@ -1,57 +0,0 @@
|
||||
package ac.grim.grimac.platform.bukkit.scheduler.folia;
|
||||
|
||||
import ac.grim.grimac.api.plugin.GrimPlugin;
|
||||
import ac.grim.grimac.platform.api.scheduler.AsyncScheduler;
|
||||
import ac.grim.grimac.platform.api.scheduler.TaskHandle;
|
||||
import ac.grim.grimac.platform.bukkit.GrimACBukkitLoaderPlugin;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class FoliaAsyncScheduler implements AsyncScheduler {
|
||||
|
||||
private final io.papermc.paper.threadedregions.scheduler.AsyncScheduler scheduler = Bukkit.getAsyncScheduler();
|
||||
|
||||
@Override
|
||||
public TaskHandle runNow(@NotNull GrimPlugin plugin, @NotNull Runnable task) {
|
||||
return new FoliaTaskHandle(scheduler.runNow(GrimACBukkitLoaderPlugin.LOADER, ignored -> task.run()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public TaskHandle runDelayed(@NotNull GrimPlugin plugin, @NotNull Runnable task, long delay, @NotNull TimeUnit timeUnit) {
|
||||
return new FoliaTaskHandle(scheduler.runDelayed(
|
||||
GrimACBukkitLoaderPlugin.LOADER,
|
||||
ignored -> task.run(),
|
||||
delay,
|
||||
timeUnit
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
public TaskHandle runAtFixedRate(@NotNull GrimPlugin plugin, @NotNull Runnable task, long delay, long period, @NotNull TimeUnit timeUnit) {
|
||||
return new FoliaTaskHandle(scheduler.runAtFixedRate(
|
||||
GrimACBukkitLoaderPlugin.LOADER,
|
||||
ignored -> task.run(),
|
||||
delay,
|
||||
period,
|
||||
timeUnit
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
public TaskHandle runAtFixedRate(@NotNull GrimPlugin plugin, @NotNull Runnable task, long initialDelayTicks, long periodTicks) {
|
||||
return new FoliaTaskHandle(scheduler.runAtFixedRate(
|
||||
GrimACBukkitLoaderPlugin.LOADER,
|
||||
ignored -> task.run(),
|
||||
initialDelayTicks * 50,
|
||||
periodTicks * 50,
|
||||
TimeUnit.MILLISECONDS
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel(@NotNull GrimPlugin plugin) {
|
||||
scheduler.cancelTasks(GrimACBukkitLoaderPlugin.LOADER);
|
||||
}
|
||||
}
|
||||
-55
@@ -1,55 +0,0 @@
|
||||
package ac.grim.grimac.platform.bukkit.scheduler.folia;
|
||||
|
||||
import ac.grim.grimac.api.plugin.GrimPlugin;
|
||||
import ac.grim.grimac.platform.api.entity.GrimEntity;
|
||||
import ac.grim.grimac.platform.api.scheduler.EntityScheduler;
|
||||
import ac.grim.grimac.platform.api.scheduler.TaskHandle;
|
||||
import ac.grim.grimac.platform.bukkit.GrimACBukkitLoaderPlugin;
|
||||
import ac.grim.grimac.platform.bukkit.entity.BukkitGrimEntity;
|
||||
import io.papermc.paper.threadedregions.scheduler.ScheduledTask;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class FoliaEntityScheduler implements EntityScheduler {
|
||||
|
||||
@Override
|
||||
public void execute(@NotNull GrimEntity entity, @NotNull GrimPlugin plugin, @NotNull Runnable task, @Nullable Runnable retired, long delay) {
|
||||
((BukkitGrimEntity) entity).getBukkitEntity().getScheduler().execute(GrimACBukkitLoaderPlugin.LOADER, task, retired, delay);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TaskHandle run(@NotNull GrimEntity entity, @NotNull GrimPlugin plugin, @NotNull Runnable task, @Nullable Runnable retired) {
|
||||
ScheduledTask scheduled = ((BukkitGrimEntity) entity).getBukkitEntity().getScheduler().run(
|
||||
GrimACBukkitLoaderPlugin.LOADER,
|
||||
ignored -> task.run(),
|
||||
retired
|
||||
);
|
||||
|
||||
return scheduled == null ? null : new FoliaTaskHandle(scheduled);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TaskHandle runDelayed(@NotNull GrimEntity entity, @NotNull GrimPlugin plugin, @NotNull Runnable task, @Nullable Runnable retired, long delayTicks) {
|
||||
ScheduledTask scheduled = ((BukkitGrimEntity) entity).getBukkitEntity().getScheduler().runDelayed(
|
||||
GrimACBukkitLoaderPlugin.LOADER,
|
||||
ignored -> task.run(),
|
||||
retired,
|
||||
delayTicks
|
||||
);
|
||||
|
||||
return scheduled == null ? null : new FoliaTaskHandle(scheduled);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TaskHandle runAtFixedRate(@NotNull GrimEntity entity, @NotNull GrimPlugin plugin, @NotNull Runnable task, @Nullable Runnable retired, long initialDelayTicks, long periodTicks) {
|
||||
ScheduledTask scheduled = ((BukkitGrimEntity) entity).getBukkitEntity().getScheduler().runAtFixedRate(
|
||||
GrimACBukkitLoaderPlugin.LOADER,
|
||||
ignored -> task.run(),
|
||||
retired,
|
||||
initialDelayTicks,
|
||||
periodTicks
|
||||
);
|
||||
|
||||
return scheduled == null ? null : new FoliaTaskHandle(scheduled);
|
||||
}
|
||||
}
|
||||
-38
@@ -1,38 +0,0 @@
|
||||
package ac.grim.grimac.platform.bukkit.scheduler.folia;
|
||||
|
||||
import ac.grim.grimac.api.plugin.GrimPlugin;
|
||||
import ac.grim.grimac.platform.api.scheduler.GlobalRegionScheduler;
|
||||
import ac.grim.grimac.platform.api.scheduler.TaskHandle;
|
||||
import ac.grim.grimac.platform.bukkit.GrimACBukkitLoaderPlugin;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class FoliaGlobalRegionScheduler implements GlobalRegionScheduler {
|
||||
|
||||
private final io.papermc.paper.threadedregions.scheduler.GlobalRegionScheduler globalRegionScheduler = Bukkit.getGlobalRegionScheduler();
|
||||
|
||||
@Override
|
||||
public void execute(@NotNull GrimPlugin plugin, @NotNull Runnable task) {
|
||||
globalRegionScheduler.execute(GrimACBukkitLoaderPlugin.LOADER, task);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TaskHandle run(@NotNull GrimPlugin plugin, @NotNull Runnable task) {
|
||||
return new FoliaTaskHandle(globalRegionScheduler.run(GrimACBukkitLoaderPlugin.LOADER, ignored -> task.run()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public TaskHandle runDelayed(@NotNull GrimPlugin plugin, @NotNull Runnable task, long delay) {
|
||||
return new FoliaTaskHandle(globalRegionScheduler.runDelayed(GrimACBukkitLoaderPlugin.LOADER, ignored -> task.run(), delay));
|
||||
}
|
||||
|
||||
@Override
|
||||
public TaskHandle runAtFixedRate(@NotNull GrimPlugin plugin, @NotNull Runnable task, long initialDelayTicks, long periodTicks) {
|
||||
return new FoliaTaskHandle(globalRegionScheduler.runAtFixedRate(GrimACBukkitLoaderPlugin.LOADER, ignored -> task.run(), initialDelayTicks, periodTicks));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel(@NotNull GrimPlugin plugin) {
|
||||
globalRegionScheduler.cancelTasks(GrimACBukkitLoaderPlugin.LOADER);
|
||||
}
|
||||
}
|
||||
-13
@@ -1,13 +0,0 @@
|
||||
package ac.grim.grimac.platform.bukkit.scheduler.folia;
|
||||
|
||||
import ac.grim.grimac.platform.api.scheduler.PlatformScheduler;
|
||||
import lombok.Getter;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@Getter
|
||||
public class FoliaPlatformScheduler implements PlatformScheduler {
|
||||
private final @NotNull FoliaAsyncScheduler asyncScheduler = new FoliaAsyncScheduler();
|
||||
private final @NotNull FoliaGlobalRegionScheduler globalRegionScheduler = new FoliaGlobalRegionScheduler();
|
||||
private final @NotNull FoliaEntityScheduler entityScheduler = new FoliaEntityScheduler();
|
||||
private final @NotNull FoliaRegionScheduler regionScheduler = new FoliaRegionScheduler();
|
||||
}
|
||||
-85
@@ -1,85 +0,0 @@
|
||||
package ac.grim.grimac.platform.bukkit.scheduler.folia;
|
||||
|
||||
import ac.grim.grimac.api.plugin.GrimPlugin;
|
||||
import ac.grim.grimac.platform.api.scheduler.RegionScheduler;
|
||||
import ac.grim.grimac.platform.api.scheduler.TaskHandle;
|
||||
import ac.grim.grimac.platform.api.world.PlatformWorld;
|
||||
import ac.grim.grimac.platform.bukkit.GrimACBukkitLoaderPlugin;
|
||||
import ac.grim.grimac.platform.bukkit.world.BukkitPlatformWorld;
|
||||
import ac.grim.grimac.utils.math.Location;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class FoliaRegionScheduler implements RegionScheduler {
|
||||
|
||||
private final io.papermc.paper.threadedregions.scheduler.RegionScheduler regionScheduler = Bukkit.getRegionScheduler();
|
||||
|
||||
@Override
|
||||
public void execute(@NotNull GrimPlugin plugin, @NotNull PlatformWorld world, int chunkX, int chunkZ, @NotNull Runnable task) {
|
||||
regionScheduler.execute(GrimACBukkitLoaderPlugin.LOADER, ((BukkitPlatformWorld) world).getBukkitWorld(), chunkX, chunkZ, task);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(@NotNull GrimPlugin plugin, @NotNull Location location, @NotNull Runnable task) {
|
||||
execute(plugin, location.getWorld(), location.getBlockX() >> 4, location.getBlockZ() >> 4, task);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TaskHandle run(@NotNull GrimPlugin plugin, @NotNull PlatformWorld world, int chunkX, int chunkZ, @NotNull Runnable task) {
|
||||
return new FoliaTaskHandle(regionScheduler.run(
|
||||
GrimACBukkitLoaderPlugin.LOADER,
|
||||
((BukkitPlatformWorld) world).getBukkitWorld(),
|
||||
chunkX,
|
||||
chunkZ,
|
||||
ignored -> task.run()
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
public TaskHandle run(@NotNull GrimPlugin plugin, @NotNull Location location, @NotNull Runnable task) {
|
||||
return run(plugin, location.getWorld(), location.getBlockX() >> 4, location.getBlockZ() >> 4, task);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TaskHandle runDelayed(@NotNull GrimPlugin plugin, @NotNull PlatformWorld world, int chunkX, int chunkZ, @NotNull Runnable task, long delayTicks) {
|
||||
return new FoliaTaskHandle(regionScheduler.runDelayed(
|
||||
GrimACBukkitLoaderPlugin.LOADER,
|
||||
((BukkitPlatformWorld) world).getBukkitWorld(),
|
||||
chunkX,
|
||||
chunkZ,
|
||||
ignored -> task.run(),
|
||||
delayTicks
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
public TaskHandle runDelayed(@NotNull GrimPlugin plugin, @NotNull Location location, @NotNull Runnable task, long delayTicks) {
|
||||
return runDelayed(plugin, location.getWorld(), location.getBlockX() >> 4, location.getBlockZ() >> 4, task, delayTicks);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TaskHandle runAtFixedRate(@NotNull GrimPlugin plugin, @NotNull PlatformWorld world, int chunkX, int chunkZ, @NotNull Runnable task, long initialDelayTicks, long periodTicks) {
|
||||
return new FoliaTaskHandle(regionScheduler.runAtFixedRate(
|
||||
GrimACBukkitLoaderPlugin.LOADER,
|
||||
((BukkitPlatformWorld) world).getBukkitWorld(),
|
||||
chunkX,
|
||||
chunkZ,
|
||||
ignored -> task.run(),
|
||||
initialDelayTicks,
|
||||
periodTicks
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
public TaskHandle runAtFixedRate(@NotNull GrimPlugin plugin, @NotNull Location location, @NotNull Runnable task, long initialDelayTicks, long periodTicks) {
|
||||
return runAtFixedRate(
|
||||
plugin,
|
||||
location.getWorld(),
|
||||
location.getBlockX() >> 4,
|
||||
location.getBlockZ() >> 4,
|
||||
task,
|
||||
initialDelayTicks,
|
||||
periodTicks
|
||||
);
|
||||
}
|
||||
}
|
||||
-33
@@ -1,33 +0,0 @@
|
||||
package ac.grim.grimac.platform.bukkit.scheduler.folia;
|
||||
|
||||
import ac.grim.grimac.platform.api.scheduler.TaskHandle;
|
||||
import io.papermc.paper.threadedregions.scheduler.ScheduledTask;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class FoliaTaskHandle implements TaskHandle {
|
||||
|
||||
private final @NotNull ScheduledTask task;
|
||||
|
||||
@Contract(pure = true)
|
||||
public FoliaTaskHandle(@NotNull ScheduledTask task) {
|
||||
this.task = Objects.requireNonNull(task);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSync() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
return task.isCancelled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
task.cancel();
|
||||
}
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
package ac.grim.grimac.platform.bukkit.sender;
|
||||
|
||||
import ac.grim.grimac.GrimAPI;
|
||||
import ac.grim.grimac.platform.api.sender.Sender;
|
||||
import ac.grim.grimac.platform.api.sender.SenderFactory;
|
||||
import ac.grim.grimac.platform.bukkit.GrimACBukkitLoaderPlugin;
|
||||
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.ConsoleCommandSender;
|
||||
import org.bukkit.command.RemoteConsoleCommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.permissions.Permission;
|
||||
import org.bukkit.permissions.PermissionDefault;
|
||||
import org.incendo.cloud.SenderMapper;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class BukkitSenderFactory extends SenderFactory<CommandSender> implements SenderMapper<CommandSender, Sender> {
|
||||
private final BukkitAudiences audiences;
|
||||
|
||||
public BukkitSenderFactory() {
|
||||
this.audiences = BukkitAudiences.create(GrimACBukkitLoaderPlugin.LOADER);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getName(CommandSender sender) {
|
||||
return sender instanceof Player ? sender.getName() : Sender.CONSOLE_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected UUID getUniqueId(CommandSender sender) {
|
||||
return sender instanceof Player player ? player.getUniqueId() : Sender.CONSOLE_UUID;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void sendMessage(CommandSender sender, String message) {
|
||||
sender.sendMessage(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void sendMessage(CommandSender sender, Component message) {
|
||||
// we can safely send async for players and the console - otherwise, send it sync
|
||||
if (sender instanceof Player || sender instanceof ConsoleCommandSender || sender instanceof RemoteConsoleCommandSender) {
|
||||
this.audiences.sender(sender).sendMessage(message);
|
||||
} else {
|
||||
GrimAPI.INSTANCE.getScheduler().getGlobalRegionScheduler().run(
|
||||
GrimAPI.INSTANCE.getGrimPlugin(),
|
||||
() -> this.audiences.sender(sender).sendMessage(message)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean hasPermission(CommandSender sender, String node) {
|
||||
return sender.hasPermission(node);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean hasPermission(CommandSender sender, String node, boolean defaultIfUnset) {
|
||||
return sender.hasPermission(new Permission(node, defaultIfUnset ? PermissionDefault.TRUE : PermissionDefault.FALSE));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void performCommand(CommandSender sender, String command) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isConsole(CommandSender sender) {
|
||||
return sender instanceof ConsoleCommandSender || sender instanceof RemoteConsoleCommandSender;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isPlayer(CommandSender sender) {
|
||||
return sender instanceof Player;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Sender map(@NotNull CommandSender base) {
|
||||
return this.wrap(base);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull CommandSender reverse(@NotNull Sender mapped) {
|
||||
return this.unwrap(mapped);
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
package ac.grim.grimac.platform.bukkit.utils.anticheat;
|
||||
|
||||
import ac.grim.grimac.utils.anticheat.LogUtil;
|
||||
import ac.grim.grimac.utils.reflection.ReflectionUtils;
|
||||
import com.github.retrooper.packetevents.PacketEvents;
|
||||
import com.github.retrooper.packetevents.manager.server.ServerVersion;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public class MultiLibUtil {
|
||||
|
||||
public final static Method externalPlayerMethod = ReflectionUtils.getMethod(Player.class, "isExternalPlayer");
|
||||
private static final boolean IS_PRE_1_18 = PacketEvents.getAPI().getServerManager().getVersion().isOlderThan(ServerVersion.V_1_18);
|
||||
|
||||
// TODO: cache external players for better performance, but this only matters for people using multi-lib
|
||||
public static boolean isExternalPlayer(Player player) {
|
||||
if (externalPlayerMethod == null || IS_PRE_1_18) return false;
|
||||
try {
|
||||
return (boolean) externalPlayerMethod.invoke(player);
|
||||
} catch (Exception e) {
|
||||
LogUtil.error("Failed to invoke external player method", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
-45
@@ -1,45 +0,0 @@
|
||||
package ac.grim.grimac.platform.bukkit.utils.convert;
|
||||
|
||||
import ac.grim.grimac.platform.api.permissions.PermissionDefaultValue;
|
||||
import ac.grim.grimac.platform.bukkit.world.BukkitPlatformWorld;
|
||||
import ac.grim.grimac.utils.math.Location;
|
||||
import com.github.retrooper.packetevents.protocol.world.BlockFace;
|
||||
import org.bukkit.permissions.PermissionDefault;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class BukkitConversionUtils {
|
||||
@Contract("null -> null; !null -> new")
|
||||
public static org.bukkit.Location toBukkitLocation(Location location) {
|
||||
if (location == null) return null;
|
||||
return new org.bukkit.Location(((BukkitPlatformWorld) location.getWorld()).getBukkitWorld(), location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch());
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts this enum to a Bukkit PermissionDefault.
|
||||
*
|
||||
* @return The corresponding Bukkit PermissionDefault.
|
||||
*/
|
||||
@Contract(value = "null -> null; !null -> !null", pure = true)
|
||||
public static @Nullable PermissionDefault toBukkitPermissionDefault(@Nullable PermissionDefaultValue permissionDefaultValue) {
|
||||
if (permissionDefaultValue == null) return null;
|
||||
return switch (permissionDefaultValue) {
|
||||
case TRUE -> PermissionDefault.TRUE;
|
||||
case FALSE -> PermissionDefault.FALSE;
|
||||
case OP -> PermissionDefault.OP;
|
||||
case NOT_OP -> PermissionDefault.NOT_OP;
|
||||
};
|
||||
}
|
||||
|
||||
public static BlockFace fromBukkitFace(org.bukkit.block.BlockFace face) {
|
||||
return switch (face) {
|
||||
case NORTH -> BlockFace.NORTH;
|
||||
case SOUTH -> BlockFace.SOUTH;
|
||||
case WEST -> BlockFace.WEST;
|
||||
case EAST -> BlockFace.EAST;
|
||||
case UP -> BlockFace.UP;
|
||||
case DOWN -> BlockFace.DOWN;
|
||||
default -> BlockFace.OTHER;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
package ac.grim.grimac.platform.bukkit.utils.reflection;
|
||||
|
||||
import ac.grim.grimac.platform.bukkit.GrimACBukkitLoaderPlugin;
|
||||
import ac.grim.grimac.utils.anticheat.LogUtil;
|
||||
import ac.grim.grimac.utils.reflection.ReflectionUtils;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.event.Event;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class PaperUtils {
|
||||
public static final boolean PAPER = ReflectionUtils.hasClass("com.destroystokyo.paper.PaperConfig")
|
||||
|| ReflectionUtils.hasClass("io.papermc.paper.configuration.Configuration");
|
||||
|
||||
public static CompletableFuture<Boolean> teleportAsync(final Entity entity, final Location location) {
|
||||
return PAPER ? entity.teleportAsync(location) : CompletableFuture.completedFuture(entity.teleport(location));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static boolean registerTickEndEvent(Listener listener, Runnable runnable) {
|
||||
try {
|
||||
Class<?> clazz = ReflectionUtils.getClass("com.destroystokyo.paper.event.server.ServerTickEndEvent");
|
||||
if (clazz == null) return false;
|
||||
GrimACBukkitLoaderPlugin.LOADER.getServer().getPluginManager().registerEvent(
|
||||
(Class<? extends Event>) clazz,
|
||||
listener,
|
||||
EventPriority.NORMAL,
|
||||
(l, event) -> runnable.run(),
|
||||
GrimACBukkitLoaderPlugin.LOADER
|
||||
);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
LogUtil.error("Failed to register tick end event", e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
package ac.grim.grimac.platform.bukkit.world;
|
||||
|
||||
import ac.grim.grimac.GrimAPI;
|
||||
import ac.grim.grimac.platform.api.Platform;
|
||||
import ac.grim.grimac.platform.api.world.PlatformChunk;
|
||||
import com.github.retrooper.packetevents.PacketEvents;
|
||||
import com.github.retrooper.packetevents.manager.server.ServerVersion;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class BukkitPlatformChunk implements PlatformChunk {
|
||||
private static final Map<BlockData, Integer> blockDataToId = GrimAPI.INSTANCE.getPlatform() == Platform.FOLIA ? new ConcurrentHashMap<>() : new HashMap<>();
|
||||
private static final boolean isFlat = PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_13);
|
||||
private final @NotNull Chunk chunk;
|
||||
|
||||
@Override
|
||||
public int getBlockID(int x, int y, int z) {
|
||||
Block block = chunk.getBlock(x, y, z);
|
||||
|
||||
return isFlat // Cache blockDataToID because Strings are expensive
|
||||
? blockDataToId.computeIfAbsent(block.getBlockData(), data -> WrappedBlockState.getByString(PacketEvents.getAPI().getServerManager().getVersion().toClientVersion(), data.getAsString(false)).getGlobalId())
|
||||
: getLegacyBlockID(block);
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "deprecation", "UnstableApiUsage" })
|
||||
private static int getLegacyBlockID(@NotNull Block block) {
|
||||
return (block.getType().getId() << 4) | block.getData();
|
||||
}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
package ac.grim.grimac.platform.bukkit.world;
|
||||
|
||||
import ac.grim.grimac.platform.api.world.PlatformChunk;
|
||||
import ac.grim.grimac.platform.api.world.PlatformWorld;
|
||||
import com.github.retrooper.packetevents.PacketEvents;
|
||||
import com.github.retrooper.packetevents.manager.server.ServerVersion;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState;
|
||||
import io.github.retrooper.packetevents.util.SpigotConversionUtil;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Block;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
public class BukkitPlatformWorld implements PlatformWorld {
|
||||
|
||||
private static final boolean LEGACY_SERVER_VERSION = PacketEvents.getAPI().getServerManager().getVersion().isOlderThanOrEquals(ServerVersion.V_1_12_2);
|
||||
private final @NotNull World bukkitWorld;
|
||||
|
||||
@Override
|
||||
public boolean isChunkLoaded(int chunkX, int chunkZ) {
|
||||
return bukkitWorld.isChunkLoaded(chunkX, chunkZ);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WrappedBlockState getBlockAt(int x, int y, int z) {
|
||||
if (LEGACY_SERVER_VERSION) {
|
||||
Block block = bukkitWorld.getBlockAt(x, y, z);
|
||||
@SuppressWarnings({"deprecation", "UnstableApiUsage"})
|
||||
int blockId = (block.getType().getId() << 4) | block.getData();
|
||||
return WrappedBlockState.getByGlobalId(blockId);
|
||||
} else {
|
||||
return SpigotConversionUtil.fromBukkitBlockData(bukkitWorld.getBlockAt(x, y, z).getBlockData());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return bukkitWorld.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable UUID getUID() {
|
||||
return this.bukkitWorld.getUID();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlatformChunk getChunkAt(int currChunkX, int currChunkZ) {
|
||||
return new BukkitPlatformChunk(bukkitWorld.getChunkAt(currChunkX, currChunkZ));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLoaded() {
|
||||
return Bukkit.getWorld(bukkitWorld.getUID()) != null;
|
||||
}
|
||||
}
|
||||
-47
@@ -1,47 +0,0 @@
|
||||
# cliff.toml - DEV BUILDS - Now with Author Info
|
||||
|
||||
[changelog]
|
||||
body = """
|
||||
## Changes in this Build (since previous push)
|
||||
{%- for group, commits in commits | group_by(attribute="group") %}
|
||||
### {{ group | striptags | upper_first }}
|
||||
{%- for commit in commits %}
|
||||
- {{ commit.message | upper_first }} - *by {{ commit.author.name }}* {% if commit.breaking %}**[BREAKING]**{% endif %} \
|
||||
{%- if commit.remote and commit.remote.url -%}
|
||||
([`{{ commit.id | truncate(length=7, end="") }}`]({{ commit.remote.url }}/commit/{{- commit.id -}}))
|
||||
{%- else -%}
|
||||
(`{{ commit.id | truncate(length=7, end="") }}`)
|
||||
{%- endif -%}
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
"""
|
||||
trim = true
|
||||
|
||||
[git]
|
||||
# Your previous settings here are perfect, no changes needed
|
||||
filter_unconventional = false
|
||||
skip_unconventional = false
|
||||
remote = "https://github.com/GrimAnticheat/Grim"
|
||||
commit_parsers = [
|
||||
{ message = "^feat", group = "🚀 Features" },
|
||||
{ message = "^fix", group = "🐛 Fixes" },
|
||||
{ message = "^perf", group = "⚡️ Performance" },
|
||||
{ message = "^refactor", group = "♻️ Refactoring" },
|
||||
{ message = "^docs", group = "📖 Documentation" },
|
||||
{ message = "^test", group = "✅ Tests" },
|
||||
{ message = "^chore", group = "🤖 Maintenance & CI" },
|
||||
{ body = ".*BREAKING CHANGE.*", group = "🚨 Breaking Changes" },
|
||||
{ message = ".*", group = "Miscellaneous" } # CHANGED THIS LINE
|
||||
]
|
||||
commit_preprocessors = [
|
||||
{ pattern = 'src/main/java/ac/grim/grimac/checks/.*', group = "✔️ Checks" },
|
||||
{ pattern = 'src/main/java/ac/grim/grimac/api/.*', group = "🔌 API" },
|
||||
{ pattern = 'src/main/java/ac/grim/grimac/platform/.*', group = "🏗️ Platform" },
|
||||
{ pattern = 'src/main/java/ac/grim/grimac/engine/.*', group = "⚙️ Core Engine" },
|
||||
{ pattern = 'src/main/java/ac/grim/grimac/config/.*', group = "⚙️ Configuration" },
|
||||
{ pattern = 'pom.xml', group = "🤖 Maintenance & CI" },
|
||||
{ pattern = '.github/.*', group = "🤖 Maintenance & CI" },
|
||||
{ pattern = 'build.gradle.kts', group = "🤖 Maintenance & CI" },
|
||||
{ pattern = 'src/.*', group = "⚙️ Core Engine" },
|
||||
]
|
||||
sort_commits = "oldest"
|
||||
@@ -1,107 +0,0 @@
|
||||
import versioning.BuildConfig
|
||||
|
||||
plugins {
|
||||
`maven-publish`
|
||||
grim.`base-conventions`
|
||||
}
|
||||
|
||||
repositories {
|
||||
if (BuildConfig.mavenLocalOverride) {
|
||||
mavenLocal()
|
||||
}
|
||||
|
||||
exclusiveContent {
|
||||
forRepository {
|
||||
maven("https://repo.grim.ac/snapshots") // Grim API
|
||||
}
|
||||
filter {
|
||||
includeGroup("ac.grim.grimac")
|
||||
includeGroup("com.github.retrooper")
|
||||
}
|
||||
}
|
||||
|
||||
exclusiveContent {
|
||||
forRepository {
|
||||
maven("https://repo.viaversion.com") { // ViaVersion
|
||||
mavenContent { releasesOnly() }
|
||||
}
|
||||
}
|
||||
filter {
|
||||
includeGroup("com.viaversion")
|
||||
}
|
||||
}
|
||||
|
||||
exclusiveContent {
|
||||
forRepository {
|
||||
maven("https://nexus.scarsz.me/content/repositories/releases") { // Configuralize
|
||||
mavenContent { releasesOnly() }
|
||||
}
|
||||
}
|
||||
filter {
|
||||
includeGroup("github.scarsz")
|
||||
}
|
||||
}
|
||||
|
||||
exclusiveContent {
|
||||
forRepository {
|
||||
maven("https://repo.opencollab.dev/maven-releases/") { // Cumulus (for Floodgate)
|
||||
mavenContent { releasesOnly() }
|
||||
}
|
||||
}
|
||||
filter {
|
||||
includeGroup("org.geysermc.api")
|
||||
}
|
||||
}
|
||||
|
||||
exclusiveContent {
|
||||
forRepository {
|
||||
maven("https://repo.opencollab.dev/maven-snapshots/") { // Floodgate
|
||||
mavenContent { snapshotsOnly() }
|
||||
}
|
||||
}
|
||||
filter {
|
||||
includeGroup("org.geysermc.floodgate")
|
||||
includeGroup("org.geysermc.cumulus")
|
||||
includeModule("org.geysermc", "common")
|
||||
includeModule("org.geysermc", "geyser-parent")
|
||||
}
|
||||
}
|
||||
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
if (BuildConfig.shadePE) {
|
||||
api(libs.packetevents.api)
|
||||
} else {
|
||||
compileOnly(libs.packetevents.api)
|
||||
}
|
||||
api(libs.cloud.core)
|
||||
api(libs.cloud.processors.requirements)
|
||||
api(libs.configuralize) {
|
||||
artifact {
|
||||
classifier = "slim"
|
||||
}
|
||||
exclude(group = "org.yaml", module = "snakeyaml")
|
||||
}
|
||||
// Bump snakeyaml (transitive dep of configuralize) 1.29 -> 2.2+ for geyser-fabric
|
||||
api(libs.snakeyaml)
|
||||
api(libs.fastutil)
|
||||
api(libs.adventure.text.minimessage)
|
||||
api(libs.jetbrains.annotations)
|
||||
api(libs.hikaricp)
|
||||
|
||||
api(libs.grim.api)
|
||||
|
||||
compileOnly(libs.geyser.base.api) {
|
||||
isTransitive = false // messes with guava otherwise
|
||||
}
|
||||
|
||||
compileOnly(libs.floodgate.api)
|
||||
compileOnly(libs.viaversion)
|
||||
compileOnly(libs.netty)
|
||||
}
|
||||
|
||||
publishing.publications.create<MavenPublication>("maven") {
|
||||
from(components["java"])
|
||||
}
|
||||
@@ -1,149 +0,0 @@
|
||||
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.manager.AlertManagerImpl;
|
||||
import ac.grim.grimac.manager.DiscordManager;
|
||||
import ac.grim.grimac.manager.InitManager;
|
||||
import ac.grim.grimac.manager.SpectateManager;
|
||||
import ac.grim.grimac.manager.TickManager;
|
||||
import ac.grim.grimac.manager.config.BaseConfigManager;
|
||||
import ac.grim.grimac.manager.init.Initable;
|
||||
import ac.grim.grimac.manager.violationdatabase.ViolationDatabaseManager;
|
||||
import ac.grim.grimac.platform.api.Platform;
|
||||
import ac.grim.grimac.platform.api.PlatformLoader;
|
||||
import ac.grim.grimac.platform.api.PlatformServer;
|
||||
import ac.grim.grimac.platform.api.manager.ItemResetHandler;
|
||||
import ac.grim.grimac.platform.api.manager.MessagePlaceHolderManager;
|
||||
import ac.grim.grimac.platform.api.manager.CommandAdapter;
|
||||
import ac.grim.grimac.platform.api.manager.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();
|
||||
|
||||
@Getter
|
||||
private final Platform platform = detectPlatform();
|
||||
private final BaseConfigManager configManager;
|
||||
private final AlertManagerImpl alertManager;
|
||||
private final SpectateManager spectateManager;
|
||||
private final DiscordManager discordManager;
|
||||
private final PlayerDataManager playerDataManager;
|
||||
private final TickManager tickManager;
|
||||
private final GrimExtensionManager extensionManager;
|
||||
private final EventBus eventBus;
|
||||
private final GrimExternalAPI externalAPI;
|
||||
private ViolationDatabaseManager violationDatabaseManager;
|
||||
private PlatformLoader loader;
|
||||
@Getter
|
||||
private InitManager initManager;
|
||||
private boolean initialized = false;
|
||||
|
||||
private GrimAPI() {
|
||||
this.configManager = new BaseConfigManager();
|
||||
this.alertManager = new AlertManagerImpl();
|
||||
this.spectateManager = new SpectateManager();
|
||||
this.discordManager = new DiscordManager();
|
||||
this.playerDataManager = new PlayerDataManager();
|
||||
this.tickManager = new TickManager();
|
||||
this.extensionManager = new GrimExtensionManager();
|
||||
this.eventBus = new OptimizedEventBus(extensionManager);
|
||||
this.externalAPI = new GrimExternalAPI(this);
|
||||
}
|
||||
|
||||
// the order matters
|
||||
private static Platform detectPlatform() {
|
||||
Platform override = CommonGrimArguments.PLATFORM_OVERRIDE.value();
|
||||
if (override != null) return override;
|
||||
if (ReflectionUtils.hasClass("io.papermc.paper.threadedregions.RegionizedServer")) return Platform.FOLIA;
|
||||
if (ReflectionUtils.hasClass("org.bukkit.Bukkit")) return Platform.BUKKIT;
|
||||
if (ReflectionUtils.hasClass("net.fabricmc.loader.api.FabricLoader")) return Platform.FABRIC;
|
||||
throw new IllegalStateException("Unknown platform!");
|
||||
}
|
||||
|
||||
public void load(PlatformLoader platformLoader, Initable... platformSpecificInitables) {
|
||||
this.loader = platformLoader;
|
||||
this.violationDatabaseManager = new ViolationDatabaseManager(getGrimPlugin());
|
||||
this.initManager = new InitManager(loader.getPacketEvents(), loader::getCommandManager, platformSpecificInitables);
|
||||
this.initManager.load();
|
||||
this.initialized = true;
|
||||
}
|
||||
|
||||
public void start() {
|
||||
checkInitialized();
|
||||
initManager.start();
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
checkInitialized();
|
||||
initManager.stop();
|
||||
}
|
||||
|
||||
public PlatformScheduler getScheduler() {
|
||||
return loader.getScheduler();
|
||||
}
|
||||
|
||||
public PlatformPlayerFactory getPlatformPlayerFactory() {
|
||||
return loader.getPlatformPlayerFactory();
|
||||
}
|
||||
|
||||
public CommandAdapter getCommandAdapter() {
|
||||
return loader.getCommandAdapter();
|
||||
}
|
||||
|
||||
public GrimPlugin getGrimPlugin() {
|
||||
return loader.getPlugin();
|
||||
}
|
||||
|
||||
public SenderFactory<?> getSenderFactory() {
|
||||
return loader.getSenderFactory();
|
||||
}
|
||||
|
||||
public ItemResetHandler getItemResetHandler() {
|
||||
return loader.getItemResetHandler();
|
||||
}
|
||||
|
||||
public PlatformPluginManager getPluginManager() {
|
||||
return loader.getPluginManager();
|
||||
}
|
||||
|
||||
public PlatformServer getPlatformServer() {
|
||||
return loader.getPlatformServer();
|
||||
}
|
||||
|
||||
public @NotNull MessagePlaceHolderManager getMessagePlaceHolderManager() {
|
||||
return loader.getMessagePlaceHolderManager();
|
||||
}
|
||||
|
||||
public CommandManager<Sender> getCommandManager() {
|
||||
return loader.getCommandManager();
|
||||
}
|
||||
|
||||
private void checkInitialized() {
|
||||
if (!initialized) {
|
||||
throw new IllegalStateException("GrimAPI has not been initialized!");
|
||||
}
|
||||
}
|
||||
|
||||
public PermissionRegistrationManager getPermissionManager() {
|
||||
return loader.getPermissionManager();
|
||||
}
|
||||
|
||||
public GrimExtensionManager getExtensionManager() {
|
||||
return extensionManager;
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
package ac.grim.grimac.checks.debug;
|
||||
|
||||
import ac.grim.grimac.checks.Check;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
|
||||
public abstract class AbstractDebugHandler extends Check {
|
||||
public AbstractDebugHandler(GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
public abstract void toggleListener(GrimPlayer player);
|
||||
|
||||
public abstract boolean toggleConsoleOutput();
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
package ac.grim.grimac.checks.impl.badpackets;
|
||||
|
||||
import ac.grim.grimac.checks.Check;
|
||||
import ac.grim.grimac.checks.CheckData;
|
||||
import ac.grim.grimac.checks.type.PacketCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
|
||||
@CheckData(name = "BadPacketsB", description = "Ignored set rotation packet")
|
||||
public class BadPacketsB extends Check implements PacketCheck {
|
||||
|
||||
public BadPacketsB(final GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
if (isTransaction(event.getPacketType())) {
|
||||
player.pendingRotations.removeIf(data -> {
|
||||
if (player.getLastTransactionReceived() > data.getTransaction()) {
|
||||
if (!data.isAccepted()) {
|
||||
flagAndAlert();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
package ac.grim.grimac.checks.impl.badpackets;
|
||||
|
||||
import ac.grim.grimac.checks.CheckData;
|
||||
import ac.grim.grimac.checks.type.BlockPlaceCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.anticheat.update.BlockBreak;
|
||||
import ac.grim.grimac.utils.anticheat.update.BlockPlace;
|
||||
import com.github.retrooper.packetevents.PacketEvents;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.manager.server.ServerVersion;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientUseItem;
|
||||
|
||||
@CheckData(name = "BadPacketsH", description = "Sent unexpected sequence id", experimental = true)
|
||||
public class BadPacketsH extends BlockPlaceCheck {
|
||||
private int lastSequence;
|
||||
private final boolean isSupportedVersion = player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_19) && PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_19);
|
||||
|
||||
public BadPacketsH(final GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
if (event.getPacketType() == PacketType.Play.Client.USE_ITEM
|
||||
&& shouldCancel(new WrapperPlayClientUseItem(event).getSequence())) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBlockPlace(BlockPlace place) {
|
||||
if (shouldCancel(place.sequence) && shouldCancel()) {
|
||||
place.resync();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBlockBreak(BlockBreak blockBreak) {
|
||||
switch (blockBreak.action) {
|
||||
case START_DIGGING, FINISHED_DIGGING -> {
|
||||
if (shouldCancel(blockBreak.sequence)) {
|
||||
blockBreak.cancel();
|
||||
}
|
||||
}
|
||||
case CANCELLED_DIGGING -> { // other actions will be checked by BadPacketsL
|
||||
if (blockBreak.sequence != 0 && flagAndAlert("expected=0, id=" + blockBreak.sequence) && shouldModifyPackets()) {
|
||||
blockBreak.cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean shouldCancel(int sequence) {
|
||||
int expected = lastSequence + 1;
|
||||
lastSequence = sequence;
|
||||
return isSupportedVersion && sequence != expected
|
||||
&& flagAndAlert("expected=" + expected + ", id=" + sequence)
|
||||
&& shouldModifyPackets();
|
||||
}
|
||||
|
||||
public void onWorldChange() {
|
||||
lastSequence = 0;
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
package ac.grim.grimac.checks.impl.badpackets;
|
||||
|
||||
import ac.grim.grimac.checks.Check;
|
||||
import ac.grim.grimac.checks.CheckData;
|
||||
import ac.grim.grimac.checks.type.PacketCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.data.HeadRotation;
|
||||
import com.github.retrooper.packetevents.PacketEvents;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.manager.server.ServerVersion;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import com.github.retrooper.packetevents.protocol.player.GameMode;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientUseItem;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@CheckData(name = "BadPacketsJ", description = "Rotation in use item packet did not match tick rotation")
|
||||
public class BadPacketsJ extends Check implements PacketCheck {
|
||||
private final List<HeadRotation> rotations = new ArrayList<>();
|
||||
|
||||
public BadPacketsJ(GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
if (player.gamemode == GameMode.SPECTATOR) {
|
||||
rotations.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.getPacketType() == PacketType.Play.Client.USE_ITEM && player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_21)
|
||||
&& PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_21)) {
|
||||
WrapperPlayClientUseItem packet = new WrapperPlayClientUseItem(event);
|
||||
rotations.add(new HeadRotation(packet.getYaw(), packet.getPitch()));
|
||||
}
|
||||
|
||||
if (isTickPacket(event.getPacketType())) {
|
||||
// due to tick skipping, the rotations sent could be last tick's
|
||||
boolean allowLast = player.canSkipTicks() && (event.getPacketType() == PacketType.Play.Client.PLAYER_POSITION_AND_ROTATION || event.getPacketType() == PacketType.Play.Client.PLAYER_ROTATION);
|
||||
for (HeadRotation rotation : rotations) {
|
||||
if (rotation.yaw() == player.yaw && rotation.pitch() == player.pitch) {
|
||||
allowLast = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (rotation.yaw() == player.lastYaw && rotation.pitch() == player.lastPitch && allowLast) {
|
||||
continue;
|
||||
}
|
||||
|
||||
flagAndAlert();
|
||||
}
|
||||
|
||||
rotations.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
package ac.grim.grimac.checks.impl.badpackets;
|
||||
|
||||
import ac.grim.grimac.checks.Check;
|
||||
import ac.grim.grimac.checks.CheckData;
|
||||
import ac.grim.grimac.checks.type.PacketCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.event.PacketSendEvent;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import com.github.retrooper.packetevents.protocol.player.Combat;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientClientStatus;
|
||||
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerChangeGameState;
|
||||
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerCombatEvent;
|
||||
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerDeathCombatEvent;
|
||||
|
||||
@CheckData(name = "BadPacketsM", description = "Tried to respawn while alive", experimental = true)
|
||||
public class BadPacketsM extends Check implements PacketCheck {
|
||||
public BadPacketsM(final GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
// not a boolean because the server could send packets that cause
|
||||
// the client to send a respawn packet before it receives the first
|
||||
private int exempt;
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
if (event.getPacketType() == PacketType.Play.Client.CLIENT_STATUS && new WrapperPlayClientClientStatus(event).getAction() == WrapperPlayClientClientStatus.Action.PERFORM_RESPAWN) {
|
||||
if (exempt > 0) {
|
||||
exempt--;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!player.compensatedEntities.self.isDead) {
|
||||
if (flagAndAlert() && shouldModifyPackets()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPacketSend(PacketSendEvent event) {
|
||||
if (event.getPacketType() == PacketType.Play.Server.CHANGE_GAME_STATE && player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_9)
|
||||
&& new WrapperPlayServerChangeGameState(event).getReason() == WrapperPlayServerChangeGameState.Reason.WIN_GAME) {
|
||||
player.addRealTimeTaskNow(() -> exempt++);
|
||||
}
|
||||
|
||||
if (event.getPacketType() == PacketType.Play.Server.DEATH_COMBAT_EVENT && player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_9)) {
|
||||
if (new WrapperPlayServerDeathCombatEvent(event).getPlayerId() == player.entityID) {
|
||||
player.addRealTimeTaskNow(() -> exempt++);
|
||||
}
|
||||
}
|
||||
|
||||
if (event.getPacketType() == PacketType.Play.Server.COMBAT_EVENT && player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_9)) {
|
||||
WrapperPlayServerCombatEvent packet = new WrapperPlayServerCombatEvent(event);
|
||||
if (packet.getCombat() == Combat.ENTITY_DEAD && packet.getPlayerId() == player.entityID) {
|
||||
player.addRealTimeTaskNow(() -> exempt++);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
package ac.grim.grimac.checks.impl.badpackets;
|
||||
|
||||
import ac.grim.grimac.checks.Check;
|
||||
import ac.grim.grimac.checks.CheckData;
|
||||
import ac.grim.grimac.checks.type.PacketCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientWindowConfirmation;
|
||||
|
||||
@CheckData(name = "BadPacketsS")
|
||||
public class BadPacketsS extends Check implements PacketCheck {
|
||||
public BadPacketsS(GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
if (event.getPacketType() == PacketType.Play.Client.WINDOW_CONFIRMATION
|
||||
&& !new WrapperPlayClientWindowConfirmation(event).isAccepted()
|
||||
&& flagAndAlert() && shouldModifyPackets()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
package ac.grim.grimac.checks.impl.badpackets;
|
||||
|
||||
import ac.grim.grimac.checks.Check;
|
||||
import ac.grim.grimac.checks.CheckData;
|
||||
import ac.grim.grimac.checks.type.PacketCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
|
||||
@CheckData(name = "BadPacketsZ", experimental = true)
|
||||
public class BadPacketsZ extends Check implements PacketCheck {
|
||||
private boolean sent;
|
||||
|
||||
public BadPacketsZ(GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
if (event.getPacketType() == PacketType.Play.Client.CLIENT_TICK_END) {
|
||||
sent = false;
|
||||
}
|
||||
|
||||
if (event.getPacketType() == PacketType.Play.Client.PLAYER_INPUT) {
|
||||
if (sent) {
|
||||
flagAndAlert();
|
||||
}
|
||||
|
||||
sent = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
package ac.grim.grimac.checks.impl.breaking;
|
||||
|
||||
import ac.grim.grimac.checks.Check;
|
||||
import ac.grim.grimac.checks.CheckData;
|
||||
import ac.grim.grimac.checks.type.BlockBreakCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.anticheat.update.BlockBreak;
|
||||
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
|
||||
import ac.grim.grimac.utils.data.Pair;
|
||||
import ac.grim.grimac.utils.data.TeleportData;
|
||||
import ac.grim.grimac.utils.math.Vector3dm;
|
||||
import ac.grim.grimac.utils.nmsutil.Ray;
|
||||
import ac.grim.grimac.utils.nmsutil.ReachUtils;
|
||||
import com.github.retrooper.packetevents.protocol.attribute.Attributes;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import com.github.retrooper.packetevents.protocol.player.DiggingAction;
|
||||
import com.github.retrooper.packetevents.protocol.player.GameMode;
|
||||
import com.github.retrooper.packetevents.protocol.teleport.RelativeFlag;
|
||||
import com.github.retrooper.packetevents.protocol.world.BlockFace;
|
||||
import com.github.retrooper.packetevents.util.Vector3d;
|
||||
import com.github.retrooper.packetevents.util.Vector3f;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@CheckData(name = "RotationBreak", experimental = true)
|
||||
public class RotationBreak extends Check implements BlockBreakCheck {
|
||||
private double flagBuffer = 0; // If the player flags once, force them to play legit, or we will cancel the tick before.
|
||||
private boolean ignorePost = false;
|
||||
|
||||
public RotationBreak(GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBlockBreak(BlockBreak blockBreak) {
|
||||
if (player.gamemode == GameMode.SPECTATOR)
|
||||
return; // you don't send flying packets when spectating entities
|
||||
if (player.inVehicle()) return; // falses
|
||||
if (blockBreak.action == DiggingAction.CANCELLED_DIGGING) return; // falses
|
||||
|
||||
if (flagBuffer > 0 && !didRayTraceHit(blockBreak)) {
|
||||
ignorePost = true;
|
||||
// If the player hit and has flagged this check recently
|
||||
if (flagAndAlert("pre-flying, action=" + blockBreak.action) && shouldModifyPackets()) {
|
||||
blockBreak.cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPostFlyingBlockBreak(BlockBreak blockBreak) {
|
||||
if (player.gamemode == GameMode.SPECTATOR)
|
||||
return; // you don't send flying packets when spectating entities
|
||||
if (player.inVehicle()) return; // falses
|
||||
if (blockBreak.action == DiggingAction.CANCELLED_DIGGING) return; // falses
|
||||
|
||||
// Don't flag twice
|
||||
if (ignorePost) {
|
||||
ignorePost = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (didRayTraceHit(blockBreak)) {
|
||||
flagBuffer = Math.max(0, flagBuffer - 0.1);
|
||||
} else {
|
||||
flagBuffer = 1;
|
||||
flagAndAlert("post-flying, action=" + blockBreak.action);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean didRayTraceHit(BlockBreak blockBreak) {
|
||||
SimpleCollisionBox box = new SimpleCollisionBox(blockBreak.x, blockBreak.y, blockBreak.z);
|
||||
|
||||
final double[] possibleEyeHeights = player.getPossibleEyeHeights();
|
||||
|
||||
// Start checking if player is in the block
|
||||
double minEyeHeight = Double.MAX_VALUE;
|
||||
double maxEyeHeight = Double.MIN_VALUE;
|
||||
for (double height : possibleEyeHeights) {
|
||||
minEyeHeight = Math.min(minEyeHeight, height);
|
||||
maxEyeHeight = Math.max(maxEyeHeight, height);
|
||||
}
|
||||
|
||||
SimpleCollisionBox eyePositions = new SimpleCollisionBox(player.x, player.y + minEyeHeight, player.z, player.x, player.y + maxEyeHeight, player.z);
|
||||
eyePositions.expand(player.getMovementThreshold());
|
||||
|
||||
// If the player is inside a block, then they can ray trace through the block and hit the other side of the block
|
||||
if (eyePositions.isIntersected(box)) {
|
||||
return true;
|
||||
}
|
||||
// End checking if the player is in the block
|
||||
|
||||
List<Vector3f> possibleLookDirs = new ArrayList<>(Arrays.asList(
|
||||
new Vector3f(player.lastYaw, player.pitch, 0),
|
||||
new Vector3f(player.yaw, player.pitch, 0)
|
||||
));
|
||||
|
||||
// 1.9+ players could be a tick behind because we don't get skipped ticks
|
||||
if (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_9)) {
|
||||
possibleLookDirs.add(new Vector3f(player.lastYaw, player.lastPitch, 0));
|
||||
}
|
||||
|
||||
// 1.7 players do not have any of these issues! They are always on the latest look vector
|
||||
if (player.getClientVersion().isOlderThan(ClientVersion.V_1_8)) {
|
||||
possibleLookDirs = Collections.singletonList(new Vector3f(player.yaw, player.pitch, 0));
|
||||
}
|
||||
|
||||
final double distance = player.compensatedEntities.self.getAttributeValue(Attributes.BLOCK_INTERACTION_RANGE);
|
||||
for (double d : possibleEyeHeights) {
|
||||
for (Vector3f lookDir : possibleLookDirs) {
|
||||
Vector3d starting = new Vector3d(player.x, player.y + d, player.z);
|
||||
Ray trace = new Ray(player, starting.getX(), starting.getY(), starting.getZ(), lookDir.getX(), lookDir.getY());
|
||||
Vector3dm pointAtDistance = trace.getPointAtDistance(distance);
|
||||
Pair<Vector3dm, BlockFace> intercept = ReachUtils.calculateIntercept(box, trace.getOriginX(), trace.getOriginY(), trace.getOriginZ(), pointAtDistance.getX(), pointAtDistance.getY(), pointAtDistance.getZ());
|
||||
|
||||
if (intercept.first() != null) return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
package ac.grim.grimac.checks.impl.breaking;
|
||||
|
||||
import ac.grim.grimac.checks.Check;
|
||||
import ac.grim.grimac.checks.CheckData;
|
||||
import ac.grim.grimac.checks.type.BlockBreakCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.anticheat.MessageUtil;
|
||||
import ac.grim.grimac.utils.anticheat.update.BlockBreak;
|
||||
import com.github.retrooper.packetevents.PacketEvents;
|
||||
import com.github.retrooper.packetevents.manager.server.ServerVersion;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import com.github.retrooper.packetevents.protocol.player.DiggingAction;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState;
|
||||
import com.github.retrooper.packetevents.util.Vector3i;
|
||||
|
||||
import static ac.grim.grimac.utils.nmsutil.BlockBreakSpeed.getBlockDamage;
|
||||
|
||||
@CheckData(name = "WrongBreak")
|
||||
public class WrongBreak extends Check implements BlockBreakCheck {
|
||||
private final int exemptedY = player.getClientVersion().isOlderThan(ClientVersion.V_1_8) ? 255 : (PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_14) ? -1 : 4095);
|
||||
private boolean lastBlockWasInstantBreak = false;
|
||||
|
||||
private boolean lastBlockIsSet;
|
||||
private int lastBlockX, lastBlockY, lastBlockZ;
|
||||
|
||||
private boolean lastCancelledBlockIsSet;
|
||||
private int lastCancelledBlockX, lastCancelledBlockY, lastCancelledBlockZ;
|
||||
|
||||
private boolean lastLastBlockIsSet;
|
||||
|
||||
public WrongBreak(final GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
// The client sometimes sends a wierd cancel packet
|
||||
private boolean shouldExempt(final WrappedBlockState block, int yPos) {
|
||||
if (lastLastBlockIsSet || !lastBlockIsSet)
|
||||
return false;
|
||||
|
||||
// on pre 1.14.4 clients, the YPos of this packet is always the same
|
||||
if (player.getClientVersion().isOlderThan(ClientVersion.V_1_14_4) && yPos != exemptedY)
|
||||
return false;
|
||||
|
||||
// and if this block is not an instant break
|
||||
return player.getClientVersion().isOlderThan(ClientVersion.V_1_14_4) || getBlockDamage(player, block) < 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBlockBreak(BlockBreak blockBreak) {
|
||||
final int x = blockBreak.x;
|
||||
final int y = blockBreak.y;
|
||||
final int z = blockBreak.z;
|
||||
|
||||
if (blockBreak.action == DiggingAction.START_DIGGING) {
|
||||
lastBlockWasInstantBreak = getBlockDamage(player, blockBreak.block) >= 1;
|
||||
lastCancelledBlockIsSet = false;
|
||||
|
||||
lastLastBlockIsSet = lastBlockIsSet;
|
||||
|
||||
lastBlockIsSet = true;
|
||||
lastBlockX = x;
|
||||
lastBlockY = y;
|
||||
lastBlockZ = z;
|
||||
} else if (blockBreak.action == DiggingAction.CANCELLED_DIGGING) {
|
||||
boolean notLastBlock = !lastBlockIsSet || x != lastBlockX || y != lastBlockY || z != lastBlockZ;
|
||||
|
||||
if (!shouldExempt(blockBreak.block, y) && notLastBlock) {
|
||||
// https://github.com/GrimAnticheat/Grim/issues/1512
|
||||
boolean isLastCancelled = lastCancelledBlockIsSet && x == lastCancelledBlockX && y == lastCancelledBlockY && z == lastCancelledBlockZ;
|
||||
if (player.getClientVersion().isOlderThan(ClientVersion.V_1_14_4) || (!lastBlockWasInstantBreak && isLastCancelled)) {
|
||||
String lastBlockStr = lastBlockIsSet ? MessageUtil.toUnlabeledString(lastBlockX, lastBlockY, lastBlockZ) : "null";
|
||||
if (flagAndAlert("action=CANCELLED_DIGGING" + ", last=" + lastBlockStr + ", pos=" + MessageUtil.toUnlabeledString(x, y, z))) {
|
||||
if (shouldModifyPackets()) {
|
||||
blockBreak.cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lastCancelledBlockIsSet = true;
|
||||
lastCancelledBlockX = x;
|
||||
lastCancelledBlockY = y;
|
||||
lastCancelledBlockZ = z;
|
||||
|
||||
lastLastBlockIsSet = lastBlockIsSet = false;
|
||||
} else if (blockBreak.action == DiggingAction.FINISHED_DIGGING) {
|
||||
boolean notLastCancelled = !lastCancelledBlockIsSet || x != lastCancelledBlockX || y != lastCancelledBlockY || z != lastCancelledBlockZ;
|
||||
boolean notLastBlock = !lastBlockIsSet || x != lastBlockX || y != lastBlockY || z != lastBlockZ;
|
||||
|
||||
// when a player looks away from the mined block, they send a cancel, and if they look at it again, they don't send another start. (thanks mojang!)
|
||||
if (notLastCancelled && (!lastBlockWasInstantBreak || player.getClientVersion().isOlderThan(ClientVersion.V_1_14_4)) && notLastBlock) {
|
||||
String lastBlockStr = lastBlockIsSet ? MessageUtil.toUnlabeledString(lastBlockX, lastBlockY, lastBlockZ) : "null";
|
||||
if (flagAndAlert("action=FINISHED_DIGGING" + ", last=" + lastBlockStr + ", pos=" + MessageUtil.toUnlabeledString(x, y, z))) {
|
||||
if (shouldModifyPackets()) {
|
||||
blockBreak.cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 1.14.4+ clients don't send another start break in protected regions
|
||||
if (player.getClientVersion().isOlderThan(ClientVersion.V_1_14_4)) {
|
||||
lastCancelledBlockIsSet = lastLastBlockIsSet = lastBlockIsSet = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
package ac.grim.grimac.checks.impl.chat;
|
||||
|
||||
import ac.grim.grimac.api.config.ConfigManager;
|
||||
import ac.grim.grimac.checks.Check;
|
||||
import ac.grim.grimac.checks.CheckData;
|
||||
import ac.grim.grimac.checks.impl.multiactions.MultiActionsC;
|
||||
import ac.grim.grimac.checks.type.PacketCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientChatCommand;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientChatCommandUnsigned;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientChatMessage;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@CheckData(name = "ChatC", description = "Moving while chatting", experimental = true)
|
||||
public class ChatC extends Check implements PacketCheck {
|
||||
public ChatC(GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
// optionally allow cheats like autogg
|
||||
private @Nullable Predicate<String> exemptRegex;
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
if (event.getPacketType() == PacketType.Play.Client.CHAT_MESSAGE) {
|
||||
// TODO make previa after making wrapper parse by client version instead of server version
|
||||
check(new WrapperPlayClientChatMessage(event).getMessage(), event);
|
||||
}
|
||||
|
||||
if (event.getPacketType() == PacketType.Play.Client.CHAT_COMMAND_UNSIGNED) {
|
||||
check("/" + new WrapperPlayClientChatCommandUnsigned(event).getCommand(), event);
|
||||
}
|
||||
|
||||
if (event.getPacketType() == PacketType.Play.Client.CHAT_COMMAND) {
|
||||
// TODO make previa after making wrapper parse by client version instead of server version
|
||||
check("/" + new WrapperPlayClientChatCommand(event).getCommand(), event);
|
||||
}
|
||||
}
|
||||
|
||||
private void check(String message, PacketReceiveEvent event) {
|
||||
if (exemptRegex != null && exemptRegex.test(message)) {
|
||||
return;
|
||||
}
|
||||
|
||||
String verbose = MultiActionsC.getVerbose(player);
|
||||
if (!verbose.isEmpty() && flagAndAlert(verbose) && shouldModifyPackets()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReload(ConfigManager config) {
|
||||
String regexString = config.getStringElse(getConfigName() + ".exempt-regex", null);
|
||||
exemptRegex = regexString == null ? null : Pattern.compile(regexString).asMatchPredicate();
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
package ac.grim.grimac.checks.impl.chat;
|
||||
|
||||
import ac.grim.grimac.checks.Check;
|
||||
import ac.grim.grimac.checks.CheckData;
|
||||
import ac.grim.grimac.checks.type.PacketCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.wrapper.common.client.WrapperCommonClientSettings;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientSettings;
|
||||
|
||||
@CheckData(name = "ChatD", description = "Chatting while chat is hidden", experimental = true)
|
||||
public class ChatD extends Check implements PacketCheck {
|
||||
private boolean hidden;
|
||||
|
||||
public ChatD(GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
if (event.getPacketType() == PacketType.Play.Client.CHAT_MESSAGE
|
||||
|| event.getPacketType() == PacketType.Play.Client.CHAT_COMMAND_UNSIGNED
|
||||
|| event.getPacketType() == PacketType.Play.Client.CHAT_COMMAND) {
|
||||
if (hidden && flagAndAlert() && shouldModifyPackets()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
}
|
||||
|
||||
if (event.getPacketType() == PacketType.Play.Client.CLIENT_SETTINGS || event.getPacketType() == PacketType.Configuration.Client.CLIENT_SETTINGS) {
|
||||
hidden = new WrapperPlayClientSettings(event).getChatVisibility() == WrapperCommonClientSettings.ChatVisibility.HIDDEN;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
package ac.grim.grimac.checks.impl.crash;
|
||||
|
||||
import ac.grim.grimac.checks.Check;
|
||||
import ac.grim.grimac.checks.CheckData;
|
||||
import ac.grim.grimac.checks.type.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.WrapperPlayClientSelectBundleItem;
|
||||
|
||||
@CheckData(name = "CrashI")
|
||||
public class CrashI extends Check implements PacketCheck {
|
||||
public CrashI(GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
if (event.getPacketType() == PacketType.Play.Client.SELECT_BUNDLE_ITEM) {
|
||||
int selectedItemIndex;
|
||||
try {
|
||||
selectedItemIndex = new WrapperPlayClientSelectBundleItem(event).getSelectedItemIndex();
|
||||
} catch (IllegalArgumentException e) {
|
||||
// thanks packetevents!
|
||||
if (e.getMessage().startsWith("Invalid selectedItemIndex: ")) {
|
||||
selectedItemIndex = Integer.parseInt(e.getMessage().substring(27));
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
if (selectedItemIndex < -1) {
|
||||
flagAndAlert("selectedItemIndex=" + selectedItemIndex);
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
package ac.grim.grimac.checks.impl.exploit;
|
||||
|
||||
import ac.grim.grimac.checks.Check;
|
||||
import ac.grim.grimac.checks.CheckData;
|
||||
import ac.grim.grimac.checks.type.PacketCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.payload.PayloadItemName;
|
||||
import com.github.retrooper.packetevents.PacketEvents;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.manager.server.ServerVersion;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientNameItem;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPluginMessage;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@CheckData(name = "ExploitA", description = "Too long item name in anvil")
|
||||
public class ExploitA extends Check implements PacketCheck {
|
||||
public ExploitA(GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
if (event.getPacketType() == PacketType.Play.Client.NAME_ITEM) {
|
||||
check(new WrapperPlayClientNameItem(event).getItemName(), event);
|
||||
}
|
||||
|
||||
if (event.getPacketType() == PacketType.Play.Client.PLUGIN_MESSAGE && PacketEvents.getAPI().getServerManager().getVersion().isOlderThan(ServerVersion.V_1_13)) {
|
||||
WrapperPlayClientPluginMessage wrapper = new WrapperPlayClientPluginMessage(event);
|
||||
if (wrapper.getChannelName().equals("MC|ItemName")) {
|
||||
check(PayloadItemName.CODEC.read(wrapper.getData()).itemName(), event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void check(@NotNull String name, PacketReceiveEvent event) {
|
||||
int limit = player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_17) ? 50
|
||||
: player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_12) ? 35
|
||||
: player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_11_1) ? 31 : 30;
|
||||
|
||||
if (name.length() > limit) {
|
||||
if (flagAndAlert("name=" + name) && shouldModifyPackets()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,323 +0,0 @@
|
||||
package ac.grim.grimac.checks.impl.exploit;
|
||||
|
||||
import ac.grim.grimac.checks.Check;
|
||||
import ac.grim.grimac.checks.CheckData;
|
||||
import ac.grim.grimac.checks.type.PacketCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.payload.PayloadBookEdit;
|
||||
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.component.ComponentTypes;
|
||||
import com.github.retrooper.packetevents.protocol.item.ItemStack;
|
||||
import com.github.retrooper.packetevents.protocol.item.type.ItemType;
|
||||
import com.github.retrooper.packetevents.protocol.item.type.ItemTypes;
|
||||
import com.github.retrooper.packetevents.protocol.nbt.NBT;
|
||||
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
|
||||
import com.github.retrooper.packetevents.protocol.nbt.NBTList;
|
||||
import com.github.retrooper.packetevents.protocol.nbt.NBTString;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import com.github.retrooper.packetevents.protocol.player.InteractionHand;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientEditBook;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerBlockPlacement;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPluginMessage;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientUseItem;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonToken;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
@CheckData(name = "ExploitB", description = "Invalid book edit")
|
||||
public class ExploitB extends Check implements PacketCheck {
|
||||
private static final boolean IS_1_8 = PacketEvents.getAPI().getServerManager().getVersion().isOlderThanOrEquals(ServerVersion.V_1_8_8);
|
||||
private ItemStack oldBook;
|
||||
private InteractionHand hand;
|
||||
|
||||
public ExploitB(GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
if (IS_1_8 ? event.getPacketType() == PacketType.Play.Client.PLAYER_BLOCK_PLACEMENT
|
||||
&& new WrapperPlayClientPlayerBlockPlacement(event).getFaceId() == 255
|
||||
: event.getPacketType() == PacketType.Play.Client.USE_ITEM) {
|
||||
hand = IS_1_8 ? InteractionHand.MAIN_HAND : new WrapperPlayClientUseItem(event).getHand();
|
||||
oldBook = player.inventory.getItemInHand(hand).copy();
|
||||
}
|
||||
|
||||
if (event.getPacketType() == PacketType.Play.Client.EDIT_BOOK) {
|
||||
checkEditBook(new WrapperPlayClientEditBook(event), event);
|
||||
oldBook = null;
|
||||
}
|
||||
|
||||
// 1.13-
|
||||
if (event.getPacketType() == PacketType.Play.Client.PLUGIN_MESSAGE && PacketEvents.getAPI().getServerManager().getVersion().isOlderThan(ServerVersion.V_1_13)) {
|
||||
WrapperPlayClientPluginMessage wrapper = new WrapperPlayClientPluginMessage(event);
|
||||
|
||||
boolean signing;
|
||||
switch (wrapper.getChannelName()) {
|
||||
case "MC|BSign" -> signing = true;
|
||||
case "MC|BEdit" -> signing = false;
|
||||
default -> {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ItemStack newBook = PayloadBookEdit.CODEC.read(wrapper.getData()).itemStack();
|
||||
checkItemStack(newBook, signing, event);
|
||||
oldBook = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void checkEditBook(WrapperPlayClientEditBook wrapper, PacketReceiveEvent event) {
|
||||
if (checkEditingBook(event)) return;
|
||||
|
||||
int expectedSlot = hand == InteractionHand.OFF_HAND ? 40 : player.packetStateData.lastSlotSelected;
|
||||
if (wrapper.getSlot() != expectedSlot) {
|
||||
if (flagAndAlert("expectedSlot=" + expectedSlot + ", slot=" + wrapper.getSlot()) && shouldModifyPackets()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (wrapper.getSigning() == null) { // 1.17+
|
||||
if (wrapper.getTitle() != null && checkTitle(wrapper.getTitle(), event)) return;
|
||||
|
||||
List<String> pages = wrapper.getPages();
|
||||
if (!pages.isEmpty() && checkEmptyLastPage(pages.get(pages.size() - 1), event)) return;
|
||||
if (checkTooManyPages(pages.size(), event)) return;
|
||||
for (String page : pages) {
|
||||
if (checkPage(page, event)) return;
|
||||
}
|
||||
} else { // 1.13 - 1.16.5
|
||||
checkItemStack(wrapper.getItemStack(), wrapper.getSigning(), event);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkItemStack(ItemStack newBook, boolean signing, PacketReceiveEvent event) {
|
||||
if (checkEditingBook(event)) return;
|
||||
|
||||
ItemType expectedType = signing && IS_1_8 ? ItemTypes.WRITTEN_BOOK : ItemTypes.WRITABLE_BOOK;
|
||||
if (!newBook.is(expectedType)) {
|
||||
if (flagAndAlert("expectedType=" + expectedType.getName().getKey() + ", type=" + newBook.getType().getName().getKey()) && shouldModifyPackets()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (newBook.getAmount() != oldBook.getAmount()) {
|
||||
if (flagAndAlert("expectedAmount=" + oldBook.getAmount() + ", amount=" + newBook.getAmount()) && shouldModifyPackets()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (newBook.getLegacyData() != oldBook.getLegacyData()) {
|
||||
if (flagAndAlert("expectedMeta=" + oldBook.getLegacyData() + ", meta=" + newBook.getLegacyData()) && shouldModifyPackets()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
NBTCompound newBookNBT = newBook.getNBT();
|
||||
if (newBookNBT == null) {
|
||||
if (flagAndAlert("no nbt") && shouldModifyPackets()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
NBTList<@NotNull NBTString> newPagesNBT = newBookNBT.getStringListTagOrNull("pages");
|
||||
if (newPagesNBT == null) {
|
||||
if (flagAndAlert("no pages") && shouldModifyPackets()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (checkTooManyPages(newPagesNBT.size(), event)) return;
|
||||
|
||||
if (signing) {
|
||||
String title = newBookNBT.getStringTagValueOrNull("title");
|
||||
if (title == null) {
|
||||
if (flagAndAlert("no title") && shouldModifyPackets()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (checkTitle(title, event)) return;
|
||||
|
||||
String author = newBookNBT.getStringTagValueOrNull("author");
|
||||
if (!player.getName().equals(author)) {
|
||||
if (flagAndAlert("expectedAuthor=" + player.getName() + ", author=" + author) && shouldModifyPackets()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
String last = null;
|
||||
for (NBTString string : newPagesNBT.getTags()) {
|
||||
if (IS_1_8 && signing) {
|
||||
try {
|
||||
JsonReader reader = new JsonReader(new StringReader(string.getValue()));
|
||||
if (reader.peek() != JsonToken.BEGIN_OBJECT) throw new JsonSyntaxException("");
|
||||
reader.beginObject();
|
||||
if (reader.peek() != JsonToken.NAME || !reader.nextName().equals("text")
|
||||
|| reader.peek() != JsonToken.STRING) throw new JsonSyntaxException("");
|
||||
last = reader.nextString();
|
||||
if (reader.peek() != JsonToken.END_OBJECT) throw new JsonSyntaxException("");
|
||||
} catch (JsonSyntaxException e) {
|
||||
if (flagAndAlert("invalid page") && shouldModifyPackets()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
return;
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
} else {
|
||||
last = string.getValue();
|
||||
}
|
||||
|
||||
if (checkPage(last, event)) return;
|
||||
}
|
||||
|
||||
if (last != null && checkEmptyLastPage(last, event)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Set<Map.Entry<String, NBT>> newNBT = newBookNBT.getTags().entrySet();
|
||||
HashMap<String, NBT> oldNBT = oldBook.getNBT() == null ? new HashMap<>() : new HashMap<>(oldBook.getNBT().getTags());
|
||||
oldNBT.remove("pages");
|
||||
oldNBT.remove("title");
|
||||
oldNBT.remove("author");
|
||||
for (var entry : newNBT) {
|
||||
if (!entry.getKey().equals("pages") && !entry.getKey().equals("title") && !entry.getKey().equals("author")) {
|
||||
NBT nbt = oldNBT.remove(entry.getKey());
|
||||
if (nbt == null || !nbt.equals(entry.getValue())) {
|
||||
if (flagAndAlert("modified nbt") && shouldModifyPackets()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!oldNBT.isEmpty()) {
|
||||
if (flagAndAlert("modified nbt") && shouldModifyPackets()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean checkTitle(@NotNull String title, PacketReceiveEvent event) {
|
||||
if (title.length() > (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_16) ? 15 : 16)) {
|
||||
if (flagAndAlert("title too long") && shouldModifyPackets()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
return true;
|
||||
} else if (title.isEmpty() || !title.trim().equals(title) || containsInvalidCharacters(title, false)) {
|
||||
if (flagAndAlert("invalid title '" + title + "'") && shouldModifyPackets()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean checkEditingBook(PacketReceiveEvent event) {
|
||||
if (oldBook == null || !oldBook.is(ItemTypes.WRITABLE_BOOK)) {
|
||||
if (flagAndAlert("not editing book") && shouldModifyPackets()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean checkTooManyPages(int pages, PacketReceiveEvent event) {
|
||||
NBTList<@NotNull NBTString> oldPagesNBT;
|
||||
int oldPages = oldBook.getVersion().isNewerThanOrEquals(ClientVersion.V_1_20_5)
|
||||
? oldBook.getComponent(ComponentTypes.WRITABLE_BOOK_CONTENT).map(content -> content.getPages().size()).orElse(0)
|
||||
: oldBook.getNBT() != null && (oldPagesNBT = oldBook.getNBT().getStringListTagOrNull("pages")) != null
|
||||
? oldPagesNBT.size() : 0;
|
||||
|
||||
if (pages > oldPages && pages > (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_14) ? 100 : 50)) {
|
||||
if (flagAndAlert("too many pages") && shouldModifyPackets()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean checkPage(String page, PacketReceiveEvent event) {
|
||||
if (page.length() > 256) {
|
||||
if (flagAndAlert("page too long") && shouldModifyPackets()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (containsInvalidCharacters(page, true)) {
|
||||
if (flagAndAlert("invalid page characters") && shouldModifyPackets()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean checkEmptyLastPage(String page, PacketReceiveEvent event) {
|
||||
if (page.isEmpty()) {
|
||||
if (flagAndAlert("empty last page") && shouldModifyPackets()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean containsInvalidCharacters(@NotNull String string, boolean allowLineBreak) {
|
||||
for (int i = 0; i < string.length(); ++i) {
|
||||
char character = string.charAt(i);
|
||||
if (character == 167 || character < 32 && (!allowLineBreak || character != 10) || character == 127) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
package ac.grim.grimac.checks.impl.multiactions;
|
||||
|
||||
import ac.grim.grimac.checks.Check;
|
||||
import ac.grim.grimac.checks.CheckData;
|
||||
import ac.grim.grimac.checks.type.BlockBreakCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.anticheat.update.BlockBreak;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import com.github.retrooper.packetevents.protocol.player.InteractionHand;
|
||||
|
||||
@CheckData(name = "MultiActionsB", description = "Breaking blocks while using an item", experimental = true)
|
||||
public class MultiActionsB extends Check implements BlockBreakCheck {
|
||||
public MultiActionsB(GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBlockBreak(BlockBreak blockBreak) {
|
||||
if (player.packetStateData.isSlowedByUsingItem() && (player.packetStateData.lastSlotSelected == player.packetStateData.getSlowedByUsingItemSlot() || player.packetStateData.itemInUseHand == InteractionHand.OFF_HAND)) {
|
||||
// this is vanilla on 1.7
|
||||
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_7_10)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (flagAndAlert() && shouldModifyPackets()) {
|
||||
blockBreak.cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
package ac.grim.grimac.checks.impl.multiactions;
|
||||
|
||||
import ac.grim.grimac.checks.Check;
|
||||
import ac.grim.grimac.checks.CheckData;
|
||||
import ac.grim.grimac.checks.type.PacketCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.StringJoiner;
|
||||
|
||||
@CheckData(name = "MultiActionsC", description = "Clicked in inventory while moving")
|
||||
public class MultiActionsC extends Check implements PacketCheck {
|
||||
public MultiActionsC(GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
// TODO: move this to a bett spot? not sure where to put this
|
||||
@Contract(pure = true)
|
||||
public static String getVerbose(@NotNull GrimPlayer player) {
|
||||
StringJoiner verbose = new StringJoiner(", ");
|
||||
if (player.isSprinting && (!player.isSwimming || !player.clientClaimsLastOnGround)) {
|
||||
verbose.add("sprinting");
|
||||
}
|
||||
|
||||
if (player.isSneaking && player.getClientVersion().isOlderThan(ClientVersion.V_1_15)) {
|
||||
verbose.add("sneaking");
|
||||
}
|
||||
|
||||
if (player.supportsEndTick() && player.packetStateData.knownInput.moving()) {
|
||||
verbose.add("input");
|
||||
}
|
||||
|
||||
return verbose.toString();
|
||||
}
|
||||
|
||||
@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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
package ac.grim.grimac.checks.impl.multiactions;
|
||||
|
||||
import ac.grim.grimac.checks.CheckData;
|
||||
import ac.grim.grimac.checks.type.BlockPlaceCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.anticheat.update.BlockPlace;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import com.github.retrooper.packetevents.protocol.world.BlockFace;
|
||||
|
||||
@CheckData(name = "MultiActionsG", description = "Attacking or using items while rowing a boat", experimental = true)
|
||||
public class MultiActionsG extends BlockPlaceCheck {
|
||||
public MultiActionsG(GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
if (event.getPacketType() == PacketType.Play.Client.INTERACT_ENTITY && isCheckActive()
|
||||
&& flagAndAlert("interact") && shouldModifyPackets()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
|
||||
if (event.getPacketType() == PacketType.Play.Client.USE_ITEM && isCheckActive()
|
||||
&& flagAndAlert("use") && shouldModifyPackets()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBlockPlace(BlockPlace place) {
|
||||
if (isCheckActive() && flagAndAlert(place.getFace() == BlockFace.OTHER ? "use" : "place") && shouldModifyPackets() && shouldCancel()) {
|
||||
place.resync();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isCheckActive() {
|
||||
return player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_9) && !player.vehicleData.wasVehicleSwitch // one tick off?
|
||||
&& player.inVehicle() && player.compensatedEntities.self.getRiding().type.isInstanceOf(EntityTypes.BOAT)
|
||||
&& (player.vehicleData.nextVehicleForward != 0 || player.vehicleData.nextVehicleHorizontal != 0);
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
package ac.grim.grimac.checks.impl.packetorder;
|
||||
|
||||
import ac.grim.grimac.checks.Check;
|
||||
import ac.grim.grimac.checks.CheckData;
|
||||
import ac.grim.grimac.checks.type.PostPredictionCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientClickWindow;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientClickWindow.WindowClickType;
|
||||
|
||||
@CheckData(name = "PacketOrderA", experimental = true)
|
||||
public class PacketOrderA extends Check implements PostPredictionCheck {
|
||||
public PacketOrderA(final GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
private int invalid;
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
if (event.getPacketType() == PacketType.Play.Client.CLICK_WINDOW) {
|
||||
final WindowClickType clickType = new WrapperPlayClientClickWindow(event).getWindowClickType();
|
||||
|
||||
if ((clickType == WindowClickType.PICKUP || clickType == WindowClickType.PICKUP_ALL) && player.packetOrderProcessor.isQuickMoveClicking()
|
||||
|| clickType == WindowClickType.QUICK_MOVE && player.packetOrderProcessor.isPickUpClicking()) {
|
||||
if (!player.canSkipTicks()) {
|
||||
if (flagAndAlert() && shouldModifyPackets()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
} else {
|
||||
invalid++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPredictionComplete(PredictionComplete predictionComplete) {
|
||||
if (!player.canSkipTicks()) return;
|
||||
|
||||
if (player.isTickingReliablyFor(3)) {
|
||||
for (; invalid >= 1; invalid--) {
|
||||
flagAndAlert();
|
||||
}
|
||||
}
|
||||
|
||||
invalid = 0;
|
||||
}
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
package ac.grim.grimac.checks.impl.packetorder;
|
||||
|
||||
import ac.grim.grimac.checks.Check;
|
||||
import ac.grim.grimac.checks.CheckData;
|
||||
import ac.grim.grimac.checks.type.PacketCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import com.github.retrooper.packetevents.PacketEvents;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.manager.server.ServerVersion;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientInteractEntity;
|
||||
|
||||
@CheckData(name = "PacketOrderB", description = "Did not swing for attack")
|
||||
public class PacketOrderB extends Check implements PacketCheck {
|
||||
// 1.9 packet order: INTERACT -> ANIMATION
|
||||
// 1.8 packet order: ANIMATION -> INTERACT
|
||||
// I personally think 1.8 made much more sense. You swing and THEN you hit!
|
||||
private final boolean is1_9 = player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_9);
|
||||
|
||||
// There is a "bug" in ViaRewind
|
||||
// 1.8 packet order: ANIMATION -> INTERACT
|
||||
// 1.9 packet order: INTERACT -> ANIMATION
|
||||
// ViaRewind, on 1.9+ servers, delays a 1.8 client's ANIMATION to be after INTERACT (but before flying).
|
||||
// Which means we see 1.9 packet order for 1.8 clients
|
||||
// Due to ViaRewind also delaying the swings, we then see packet order above 20CPS like:
|
||||
// INTERACT -> INTERACT -> ANIMATION -> ANIMATION
|
||||
// I will simply disable this check for 1.8- clients on 1.9+ servers as I can't be bothered to find a way around this.
|
||||
// Stop supporting such old clients on modern servers!
|
||||
private final boolean exempt = player.getClientVersion().isOlderThan(ClientVersion.V_1_9)
|
||||
&& PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_9);
|
||||
|
||||
private boolean sentAnimationSinceLastAttack = player.getClientVersion().isNewerThan(ClientVersion.V_1_8);
|
||||
private boolean sentAttack, sentAnimation, sentSlotSwitch;
|
||||
|
||||
public PacketOrderB(final GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
if (exempt) return;
|
||||
|
||||
if (event.getPacketType() == PacketType.Play.Client.ANIMATION) {
|
||||
sentAnimationSinceLastAttack = sentAnimation = true;
|
||||
sentAttack = sentSlotSwitch = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.getPacketType() == PacketType.Play.Client.INTERACT_ENTITY) {
|
||||
WrapperPlayClientInteractEntity packet = new WrapperPlayClientInteractEntity(event);
|
||||
if (packet.getAction() == WrapperPlayClientInteractEntity.InteractAction.ATTACK) {
|
||||
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;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (event.getPacketType() == PacketType.Play.Client.HELD_ITEM_CHANGE && !is1_9 && !sentSlotSwitch) {
|
||||
sentSlotSwitch = true;
|
||||
return; // do not set sentAnimation to false
|
||||
}
|
||||
|
||||
if (!isAsync(event.getPacketType())) {
|
||||
if (sentAttack && is1_9) {
|
||||
flagAndAlert("post-attack");
|
||||
}
|
||||
|
||||
sentAttack = sentAnimation = sentSlotSwitch = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
package ac.grim.grimac.checks.impl.packetorder;
|
||||
|
||||
import ac.grim.grimac.checks.Check;
|
||||
import ac.grim.grimac.checks.CheckData;
|
||||
import ac.grim.grimac.checks.type.PacketCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.data.packetentity.PacketEntity;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import com.github.retrooper.packetevents.protocol.player.InteractionHand;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientInteractEntity;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerFlying;
|
||||
|
||||
@CheckData(name = "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 boolean sentInteractAt = false;
|
||||
private int requiredEntity;
|
||||
private InteractionHand requiredHand;
|
||||
private boolean requiredSneaking;
|
||||
|
||||
public PacketOrderC(final GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
if (exempt) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.getPacketType() == PacketType.Play.Client.INTERACT_ENTITY) {
|
||||
final WrapperPlayClientInteractEntity packet = new WrapperPlayClientInteractEntity(event);
|
||||
|
||||
final PacketEntity entity = player.compensatedEntities.entityMap.get(packet.getEntityId());
|
||||
|
||||
// For armor stands, vanilla clients send:
|
||||
// - when renaming the armor stand or in spectator mode: INTERACT_AT + INTERACT
|
||||
// - in all other cases: only INTERACT
|
||||
// Just exempt armor stands to be safe
|
||||
if (entity != null && entity.type == EntityTypes.ARMOR_STAND) return;
|
||||
|
||||
final boolean sneaking = packet.isSneaking().orElse(false);
|
||||
|
||||
switch (packet.getAction()) {
|
||||
// INTERACT_AT then INTERACT
|
||||
case INTERACT:
|
||||
if (!sentInteractAt) {
|
||||
if (flagAndAlert("Skipped Interact-At") && shouldModifyPackets()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
} else if (packet.getEntityId() != requiredEntity || packet.getHand() != requiredHand || sneaking != requiredSneaking) {
|
||||
String verbose = "requiredEntity=" + requiredEntity + ", entity=" + packet.getEntityId()
|
||||
+ ", requiredHand=" + requiredHand + ", hand=" + packet.getHand()
|
||||
+ ", requiredSneaking=" + requiredSneaking + ", sneaking=" + sneaking;
|
||||
if (flagAndAlert(verbose) && shouldModifyPackets()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
}
|
||||
|
||||
sentInteractAt = false;
|
||||
break;
|
||||
case INTERACT_AT:
|
||||
if (sentInteractAt) {
|
||||
if (flagAndAlert("Skipped Interact") && shouldModifyPackets()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
}
|
||||
|
||||
requiredHand = packet.getHand();
|
||||
requiredEntity = packet.getEntityId();
|
||||
requiredSneaking = sneaking;
|
||||
sentInteractAt = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (WrapperPlayClientPlayerFlying.isFlying(event.getPacketType())) {
|
||||
if (sentInteractAt) {
|
||||
sentInteractAt = false;
|
||||
flagAndAlert("Skipped Interact (Tick)");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
package ac.grim.grimac.checks.impl.packetorder;
|
||||
|
||||
import ac.grim.grimac.checks.Check;
|
||||
import ac.grim.grimac.checks.CheckData;
|
||||
import ac.grim.grimac.checks.type.PacketCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import com.github.retrooper.packetevents.protocol.player.InteractionHand;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientInteractEntity;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientInteractEntity.InteractAction;
|
||||
|
||||
@CheckData(name = "PacketOrderD", experimental = true)
|
||||
public class PacketOrderD extends Check implements PacketCheck {
|
||||
public PacketOrderD(final GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
private boolean sentMainhand;
|
||||
private int requiredEntity;
|
||||
private boolean requiredSneaking;
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
if (event.getPacketType() == PacketType.Play.Client.INTERACT_ENTITY && player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_9)) {
|
||||
final WrapperPlayClientInteractEntity packet = new WrapperPlayClientInteractEntity(event);
|
||||
InteractAction action = packet.getAction();
|
||||
if (action != InteractAction.ATTACK) {
|
||||
final boolean sneaking = packet.isSneaking().orElse(false);
|
||||
final int entity = packet.getEntityId();
|
||||
|
||||
if (packet.getHand() == InteractionHand.OFF_HAND) {
|
||||
if (action == InteractAction.INTERACT) {
|
||||
if (!sentMainhand) {
|
||||
if (flagAndAlert("Skipped Mainhand") && shouldModifyPackets()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
}
|
||||
sentMainhand = false;
|
||||
} else if (sneaking != requiredSneaking || entity != requiredEntity) {
|
||||
String verbose = "requiredEntity=" + requiredEntity + ", entity=" + entity
|
||||
+ ", requiredSneaking=" + requiredSneaking + ", sneaking=" + sneaking;
|
||||
if (flagAndAlert(verbose) && shouldModifyPackets()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
requiredEntity = entity;
|
||||
requiredSneaking = sneaking;
|
||||
sentMainhand = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isTickPacket(event.getPacketType())) {
|
||||
sentMainhand = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
package ac.grim.grimac.checks.impl.packetorder;
|
||||
|
||||
import ac.grim.grimac.checks.Check;
|
||||
import ac.grim.grimac.checks.CheckData;
|
||||
import ac.grim.grimac.checks.type.PostPredictionCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
|
||||
@CheckData(name = "PacketOrderE", experimental = true)
|
||||
public class PacketOrderE extends Check implements PostPredictionCheck {
|
||||
public PacketOrderE(final GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
private final ArrayDeque<String> flags = new ArrayDeque<>();
|
||||
private boolean setback;
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
if (event.getPacketType() == PacketType.Play.Client.HELD_ITEM_CHANGE) {
|
||||
if (player.packetOrderProcessor.isAttacking()
|
||||
|| player.packetOrderProcessor.isRightClicking()
|
||||
|| player.packetOrderProcessor.isOpeningInventory()
|
||||
|| player.packetOrderProcessor.isReleasing()
|
||||
|| player.packetOrderProcessor.isSneaking()
|
||||
|| player.packetOrderProcessor.isSprinting()
|
||||
|| player.packetOrderProcessor.isLeavingBed()
|
||||
|| player.packetOrderProcessor.isStartingToGlide()
|
||||
|| player.packetOrderProcessor.isJumpingWithMount()
|
||||
) {
|
||||
String verbose = "attacking=" + player.packetOrderProcessor.isAttacking()
|
||||
+ ", rightClicking=" + player.packetOrderProcessor.isRightClicking()
|
||||
+ ", openingInventory=" + player.packetOrderProcessor.isOpeningInventory()
|
||||
+ ", releasing=" + player.packetOrderProcessor.isReleasing()
|
||||
+ ", sneaking=" + player.packetOrderProcessor.isSneaking()
|
||||
+ ", sprinting=" + player.packetOrderProcessor.isSprinting()
|
||||
+ ", bed=" + player.packetOrderProcessor.isLeavingBed()
|
||||
+ ", sprinting=" + player.packetOrderProcessor.isSprinting()
|
||||
+ ", gliding=" + player.packetOrderProcessor.isStartingToGlide()
|
||||
+ ", mountJumping=" + player.packetOrderProcessor.isJumpingWithMount();
|
||||
if (player.canSkipTicks() && flags.add(verbose) || flagAndAlert(verbose)) {
|
||||
if (player.packetOrderProcessor.isUsing()) {
|
||||
setback = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPredictionComplete(PredictionComplete predictionComplete) {
|
||||
if (!player.canSkipTicks()) {
|
||||
if (setback) {
|
||||
setback = false;
|
||||
setbackIfAboveSetbackVL();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (player.isTickingReliablyFor(3)) {
|
||||
for (String verbose : flags) {
|
||||
if (flagAndAlert(verbose) && setback) {
|
||||
setback = false;
|
||||
setbackIfAboveSetbackVL();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setback = false;
|
||||
flags.clear();
|
||||
}
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
package ac.grim.grimac.checks.impl.packetorder;
|
||||
|
||||
import ac.grim.grimac.checks.Check;
|
||||
import ac.grim.grimac.checks.CheckData;
|
||||
import ac.grim.grimac.checks.type.PostPredictionCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.protocol.player.DiggingAction;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientClientStatus;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerDigging;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
|
||||
@CheckData(name = "PacketOrderF", experimental = true)
|
||||
public class PacketOrderF extends Check implements PostPredictionCheck {
|
||||
public PacketOrderF(GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
private final ArrayDeque<String> flags = new ArrayDeque<>();
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
if (event.getPacketType() == PacketType.Play.Client.INTERACT_ENTITY
|
||||
|| event.getPacketType() == PacketType.Play.Client.PLAYER_BLOCK_PLACEMENT
|
||||
|| event.getPacketType() == PacketType.Play.Client.USE_ITEM
|
||||
|| event.getPacketType() == PacketType.Play.Client.PICK_ITEM
|
||||
|| event.getPacketType() == PacketType.Play.Client.PLAYER_DIGGING
|
||||
|| (event.getPacketType() == PacketType.Play.Client.CLIENT_STATUS
|
||||
&& new WrapperPlayClientClientStatus(event).getAction() == WrapperPlayClientClientStatus.Action.OPEN_INVENTORY_ACHIEVEMENT)
|
||||
) if (player.packetOrderProcessor.isSprinting() || player.packetOrderProcessor.isSneaking()) {
|
||||
String verbose = "action=" + (event.getPacketType() == PacketType.Play.Client.INTERACT_ENTITY ? "interact"
|
||||
: event.getPacketType() == PacketType.Play.Client.PLAYER_BLOCK_PLACEMENT ? "place"
|
||||
: event.getPacketType() == PacketType.Play.Client.USE_ITEM ? "use"
|
||||
: event.getPacketType() == PacketType.Play.Client.PICK_ITEM ? "pick"
|
||||
: event.getPacketType() == PacketType.Play.Client.PLAYER_DIGGING ? "dig"
|
||||
: "openInventory")
|
||||
+ ", sprinting=" + player.packetOrderProcessor.isSprinting()
|
||||
+ ", sneaking=" + player.packetOrderProcessor.isSneaking();
|
||||
if (!player.canSkipTicks()) {
|
||||
if (flagAndAlert(verbose) && shouldModifyPackets()) {
|
||||
if (event.getPacketType() == PacketType.Play.Client.PLAYER_DIGGING
|
||||
&& new WrapperPlayClientPlayerDigging(event).getAction() == DiggingAction.RELEASE_USE_ITEM
|
||||
) return; // don't cause a noslow
|
||||
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
} else {
|
||||
flags.add(verbose);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPredictionComplete(PredictionComplete predictionComplete) {
|
||||
if (!player.canSkipTicks()) return;
|
||||
|
||||
if (player.isTickingReliablyFor(3)) {
|
||||
for (String verbose : flags) {
|
||||
flagAndAlert(verbose);
|
||||
}
|
||||
}
|
||||
|
||||
flags.clear();
|
||||
}
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
package ac.grim.grimac.checks.impl.packetorder;
|
||||
|
||||
import ac.grim.grimac.checks.Check;
|
||||
import ac.grim.grimac.checks.CheckData;
|
||||
import ac.grim.grimac.checks.type.PostPredictionCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.protocol.player.DiggingAction;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientClientStatus;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerDigging;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
|
||||
@CheckData(name = "PacketOrderG", experimental = true)
|
||||
public class PacketOrderG extends Check implements PostPredictionCheck {
|
||||
public PacketOrderG(GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
private final ArrayDeque<String> flags = new ArrayDeque<>();
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
if (event.getPacketType() == PacketType.Play.Client.PLAYER_DIGGING || (event.getPacketType() == PacketType.Play.Client.CLIENT_STATUS
|
||||
&& new WrapperPlayClientClientStatus(event).getAction() == WrapperPlayClientClientStatus.Action.OPEN_INVENTORY_ACHIEVEMENT)) {
|
||||
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
|
||||
) return;
|
||||
}
|
||||
|
||||
if (player.packetOrderProcessor.isAttacking()
|
||||
|| 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()
|
||||
+ ", releasing=" + player.packetOrderProcessor.isReleasing()
|
||||
+ ", rightClicking=" + player.packetOrderProcessor.isRightClicking()
|
||||
+ ", picking=" + player.packetOrderProcessor.isPicking()
|
||||
+ ", digging=" + player.packetOrderProcessor.isDigging();
|
||||
if (!player.canSkipTicks()) {
|
||||
if (flagAndAlert(verbose) && shouldModifyPackets()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
} else {
|
||||
flags.add(verbose);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPredictionComplete(PredictionComplete predictionComplete) {
|
||||
if (!player.canSkipTicks()) return;
|
||||
|
||||
if (player.isTickingReliablyFor(3)) {
|
||||
for (String verbose : flags) {
|
||||
flagAndAlert(verbose);
|
||||
}
|
||||
}
|
||||
|
||||
flags.clear();
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
package ac.grim.grimac.checks.impl.packetorder;
|
||||
|
||||
import ac.grim.grimac.checks.Check;
|
||||
import ac.grim.grimac.checks.CheckData;
|
||||
import ac.grim.grimac.checks.type.PostPredictionCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientEntityAction;
|
||||
|
||||
@CheckData(name = "PacketOrderH", experimental = true)
|
||||
public class PacketOrderH extends Check implements PostPredictionCheck {
|
||||
public PacketOrderH(final GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
private int invalid;
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
if (event.getPacketType() == PacketType.Play.Client.ENTITY_ACTION) {
|
||||
switch (new WrapperPlayClientEntityAction(event).getAction()) {
|
||||
case START_SPRINTING, STOP_SPRINTING -> {
|
||||
if (player.getClientVersion().isOlderThan(ClientVersion.V_1_21_2) && player.packetOrderProcessor.isSneaking()) {
|
||||
if (!player.canSkipTicks()) {
|
||||
flagAndAlert();
|
||||
} else {
|
||||
invalid++;
|
||||
}
|
||||
}
|
||||
}
|
||||
case START_SNEAKING, STOP_SNEAKING -> {
|
||||
if (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_21_2) && player.packetOrderProcessor.isSprinting()) {
|
||||
if (!player.canSkipTicks()) {
|
||||
flagAndAlert();
|
||||
} else {
|
||||
invalid++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPredictionComplete(PredictionComplete predictionComplete) {
|
||||
if (!player.canSkipTicks()) return;
|
||||
|
||||
if (player.isTickingReliablyFor(3)) {
|
||||
for (; invalid >= 1; invalid--) {
|
||||
flagAndAlert();
|
||||
}
|
||||
}
|
||||
|
||||
invalid = 0;
|
||||
}
|
||||
}
|
||||
@@ -1,141 +0,0 @@
|
||||
package ac.grim.grimac.checks.impl.packetorder;
|
||||
|
||||
import ac.grim.grimac.api.config.ConfigManager;
|
||||
import ac.grim.grimac.checks.Check;
|
||||
import ac.grim.grimac.checks.CheckData;
|
||||
import ac.grim.grimac.checks.type.PostPredictionCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
|
||||
import ac.grim.grimac.utils.nmsutil.BlockBreakSpeed;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import com.github.retrooper.packetevents.protocol.player.GameMode;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientInteractEntity;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerDigging;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
|
||||
@CheckData(name = "PacketOrderI", experimental = true)
|
||||
public class PacketOrderI extends Check implements PostPredictionCheck {
|
||||
public PacketOrderI(final GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
private boolean exemptPlacingWhileDigging;
|
||||
|
||||
private boolean setback;
|
||||
private boolean digging; // for placing
|
||||
private final ArrayDeque<String> flags = new ArrayDeque<>();
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
if (event.getPacketType() == PacketType.Play.Client.INTERACT_ENTITY) {
|
||||
if (new WrapperPlayClientInteractEntity(event).getAction() == WrapperPlayClientInteractEntity.InteractAction.ATTACK) {
|
||||
if (player.packetOrderProcessor.isRightClicking() || player.packetOrderProcessor.isPicking() || player.packetOrderProcessor.isReleasing() || player.packetOrderProcessor.isDigging()) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
} else if (player.packetOrderProcessor.isReleasing() || player.packetOrderProcessor.isDigging()) {
|
||||
String verbose = "type=interact, releasing=" + player.packetOrderProcessor.isReleasing() + ", digging=" + player.packetOrderProcessor.isDigging();
|
||||
if (!player.canSkipTicks()) {
|
||||
if (flagAndAlert(verbose) && shouldModifyPackets()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
} else {
|
||||
flags.add(verbose);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (event.getPacketType() == PacketType.Play.Client.PLAYER_BLOCK_PLACEMENT || event.getPacketType() == PacketType.Play.Client.USE_ITEM) {
|
||||
if (player.packetOrderProcessor.isReleasing() || digging) {
|
||||
String verbose = "type=place/use, releasing=" + player.packetOrderProcessor.isReleasing() + ", digging=" + digging;
|
||||
if (!player.canSkipTicks()) {
|
||||
if (flagAndAlert(verbose) && shouldModifyPackets()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
} else {
|
||||
flags.add(verbose);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (event.getPacketType() == PacketType.Play.Client.PLAYER_DIGGING) {
|
||||
WrapperPlayClientPlayerDigging packet = new WrapperPlayClientPlayerDigging(event);
|
||||
|
||||
switch (packet.getAction()) {
|
||||
case RELEASE_USE_ITEM:
|
||||
if (player.packetOrderProcessor.isAttacking() || player.packetOrderProcessor.isRightClicking() || player.packetOrderProcessor.isPicking() || player.packetOrderProcessor.isDigging()) {
|
||||
String verbose = "type=release, attacking=" + player.packetOrderProcessor.isAttacking()
|
||||
+ ", rightClicking=" + player.packetOrderProcessor.isRightClicking()
|
||||
+ ", picking=" + player.packetOrderProcessor.isPicking()
|
||||
+ ", digging=" + player.packetOrderProcessor.isDigging();
|
||||
if (!player.canSkipTicks()) {
|
||||
if (flagAndAlert(verbose)) {
|
||||
setback = true;
|
||||
}
|
||||
} else {
|
||||
flags.add(verbose);
|
||||
setback = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case START_DIGGING:
|
||||
double damage = BlockBreakSpeed.getBlockDamage(player, player.compensatedWorld.getBlock(packet.getBlockPosition()));
|
||||
if (damage >= 1 || damage <= 0 && player.gamemode == GameMode.CREATIVE) {
|
||||
return;
|
||||
}
|
||||
case CANCELLED_DIGGING, FINISHED_DIGGING:
|
||||
if (exemptPlacingWhileDigging || player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_7_10)) {
|
||||
return;
|
||||
}
|
||||
digging = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (player.gamemode == GameMode.SPECTATOR || isTickPacket(event.getPacketType())) {
|
||||
digging = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPredictionComplete(PredictionComplete predictionComplete) {
|
||||
if (!player.canSkipTicks()) {
|
||||
if (setback) {
|
||||
setbackIfAboveSetbackVL();
|
||||
setback = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (player.isTickingReliablyFor(3)) {
|
||||
for (String verbose : flags) {
|
||||
if (flagAndAlert(verbose) && setback) {
|
||||
setbackIfAboveSetbackVL();
|
||||
setback = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
flags.clear();
|
||||
setback = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReload(ConfigManager config) {
|
||||
exemptPlacingWhileDigging = config.getBooleanElse(getConfigName() + ".exempt-placing-while-digging", false);
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
package ac.grim.grimac.checks.impl.packetorder;
|
||||
|
||||
import ac.grim.grimac.checks.Check;
|
||||
import ac.grim.grimac.checks.CheckData;
|
||||
import ac.grim.grimac.checks.type.PostPredictionCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
|
||||
@CheckData(name = "PacketOrderJ", experimental = true)
|
||||
public class PacketOrderJ extends Check implements PostPredictionCheck {
|
||||
public PacketOrderJ(final GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
private int invalid;
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
if (event.getPacketType() == PacketType.Play.Client.PLAYER_BLOCK_PLACEMENT || event.getPacketType() == PacketType.Play.Client.USE_ITEM) {
|
||||
if (player.packetOrderProcessor.isAttacking() && !player.packetOrderProcessor.isInteracting()) {
|
||||
if (!player.canSkipTicks()) {
|
||||
if (flagAndAlert() && shouldModifyPackets()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
} else {
|
||||
invalid++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPredictionComplete(PredictionComplete predictionComplete) {
|
||||
if (!player.canSkipTicks()) return;
|
||||
|
||||
if (player.isTickingReliablyFor(3)) {
|
||||
for (; invalid >= 1; invalid--) {
|
||||
flagAndAlert();
|
||||
}
|
||||
}
|
||||
|
||||
invalid = 0;
|
||||
}
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
package ac.grim.grimac.checks.impl.packetorder;
|
||||
|
||||
import ac.grim.grimac.checks.Check;
|
||||
import ac.grim.grimac.checks.CheckData;
|
||||
import ac.grim.grimac.checks.type.PostPredictionCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientClientStatus;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
|
||||
@CheckData(name = "PacketOrderK", experimental = true)
|
||||
public class PacketOrderK extends Check implements PostPredictionCheck {
|
||||
public PacketOrderK(final GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
private final ArrayDeque<String> flags = new ArrayDeque<>();
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
if (event.getPacketType() == PacketType.Play.Client.CLIENT_STATUS) {
|
||||
if (new WrapperPlayClientClientStatus(event).getAction() == WrapperPlayClientClientStatus.Action.OPEN_INVENTORY_ACHIEVEMENT) {
|
||||
if (player.packetOrderProcessor.isClickingInInventory() || player.packetOrderProcessor.isClosingInventory()) {
|
||||
String verbose = "open, clicking=" + player.packetOrderProcessor.isClickingInInventory() + ", closing=" + player.packetOrderProcessor.isClosingInventory();
|
||||
if (!player.canSkipTicks()) {
|
||||
flagAndAlert(verbose);
|
||||
} else {
|
||||
flags.add(verbose);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (event.getPacketType() == PacketType.Play.Client.CLICK_WINDOW || event.getPacketType() == PacketType.Play.Client.CLOSE_WINDOW) {
|
||||
if (player.packetOrderProcessor.isOpeningInventory()) {
|
||||
String verbose = event.getPacketType() == PacketType.Play.Client.CLICK_WINDOW ? "click" : "close";
|
||||
if (!player.canSkipTicks()) {
|
||||
if (flagAndAlert(verbose) && shouldModifyPackets()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
} else {
|
||||
flags.add(verbose);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPredictionComplete(PredictionComplete predictionComplete) {
|
||||
if (!player.canSkipTicks()) return;
|
||||
|
||||
if (player.isTickingReliablyFor(3)) {
|
||||
for (String verbose : flags) {
|
||||
flagAndAlert(verbose);
|
||||
}
|
||||
}
|
||||
|
||||
flags.clear();
|
||||
}
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
package ac.grim.grimac.checks.impl.packetorder;
|
||||
|
||||
import ac.grim.grimac.checks.Check;
|
||||
import ac.grim.grimac.checks.CheckData;
|
||||
import ac.grim.grimac.checks.type.PostPredictionCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.protocol.player.DiggingAction;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientClientStatus;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerDigging;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
|
||||
@CheckData(name = "PacketOrderL", experimental = true)
|
||||
public class PacketOrderL extends Check implements PostPredictionCheck {
|
||||
public PacketOrderL(final GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
private final ArrayDeque<String> flags = new ArrayDeque<>();
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
if (event.getPacketType() == PacketType.Play.Client.CLIENT_STATUS) {
|
||||
if (new WrapperPlayClientClientStatus(event).getAction() == WrapperPlayClientClientStatus.Action.OPEN_INVENTORY_ACHIEVEMENT) {
|
||||
if (player.packetOrderProcessor.isDropping()) {
|
||||
if (!player.canSkipTicks()) {
|
||||
if (flagAndAlert("inventory") && shouldModifyPackets()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
} else {
|
||||
flags.add("inventory");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (event.getPacketType() == PacketType.Play.Client.PLAYER_DIGGING) {
|
||||
if (new WrapperPlayClientPlayerDigging(event).getAction() == DiggingAction.SWAP_ITEM_WITH_OFFHAND) {
|
||||
if (player.packetOrderProcessor.isDropping()) {
|
||||
if (!player.canSkipTicks()) {
|
||||
if (flagAndAlert("swap") && shouldModifyPackets()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
} else {
|
||||
flags.add("swap");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPredictionComplete(PredictionComplete predictionComplete) {
|
||||
if (!player.canSkipTicks()) return;
|
||||
|
||||
if (player.isTickingReliablyFor(3)) {
|
||||
for (String verbose : flags) {
|
||||
flagAndAlert(verbose);
|
||||
}
|
||||
}
|
||||
|
||||
flags.clear();
|
||||
}
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
package ac.grim.grimac.checks.impl.packetorder;
|
||||
|
||||
import ac.grim.grimac.checks.Check;
|
||||
import ac.grim.grimac.checks.CheckData;
|
||||
import ac.grim.grimac.checks.type.PostPredictionCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.protocol.player.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;
|
||||
|
||||
@CheckData(name = "PacketOrderM", experimental = true)
|
||||
public class PacketOrderM extends Check implements PostPredictionCheck {
|
||||
public PacketOrderM(final GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
private int invalid;
|
||||
private boolean usingWithoutInteract, interacting;
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
if (event.getPacketType() == PacketType.Play.Client.INTERACT_ENTITY) {
|
||||
if (new WrapperPlayClientInteractEntity(event).getAction() != WrapperPlayClientInteractEntity.InteractAction.ATTACK) {
|
||||
interacting = true;
|
||||
if (usingWithoutInteract) {
|
||||
if (!player.canSkipTicks()) {
|
||||
if (flagAndAlert() && shouldModifyPackets()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
} else {
|
||||
invalid++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (event.getPacketType() == PacketType.Play.Client.USE_ITEM
|
||||
|| event.getPacketType() == PacketType.Play.Client.PLAYER_BLOCK_PLACEMENT
|
||||
&& new WrapperPlayClientPlayerBlockPlacement(event).getFace() == BlockFace.OTHER) {
|
||||
if (!interacting) {
|
||||
usingWithoutInteract = true;
|
||||
}
|
||||
|
||||
interacting = false;
|
||||
}
|
||||
|
||||
if (player.gamemode == GameMode.SPECTATOR || isTickPacket(event.getPacketType())) {
|
||||
usingWithoutInteract = interacting = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPredictionComplete(PredictionComplete predictionComplete) {
|
||||
if (!player.canSkipTicks()) return;
|
||||
|
||||
if (player.isTickingReliablyFor(3)) {
|
||||
for (; invalid >= 1; invalid--) {
|
||||
flagAndAlert();
|
||||
}
|
||||
}
|
||||
|
||||
invalid = 0;
|
||||
}
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
package ac.grim.grimac.checks.impl.packetorder;
|
||||
|
||||
import ac.grim.grimac.checks.CheckData;
|
||||
import ac.grim.grimac.checks.type.BlockPlaceCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.anticheat.update.BlockPlace;
|
||||
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.protocol.player.GameMode;
|
||||
import com.github.retrooper.packetevents.protocol.world.BlockFace;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerBlockPlacement;
|
||||
|
||||
@CheckData(name = "PacketOrderN", experimental = true)
|
||||
public class PacketOrderN extends BlockPlaceCheck {
|
||||
public PacketOrderN(final GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
private int invalid;
|
||||
private boolean usingWithoutPlacing, placing;
|
||||
|
||||
@Override
|
||||
public void onBlockPlace(BlockPlace place) {
|
||||
placing = true;
|
||||
if (usingWithoutPlacing) {
|
||||
if (!player.canSkipTicks()) {
|
||||
if (flagAndAlert() && shouldModifyPackets() && shouldCancel()) {
|
||||
place.resync();
|
||||
}
|
||||
} else {
|
||||
invalid++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
if (event.getPacketType() == PacketType.Play.Client.USE_ITEM
|
||||
|| event.getPacketType() == PacketType.Play.Client.PLAYER_BLOCK_PLACEMENT
|
||||
&& new WrapperPlayClientPlayerBlockPlacement(event).getFace() == BlockFace.OTHER) {
|
||||
if (!placing) {
|
||||
usingWithoutPlacing = true;
|
||||
}
|
||||
|
||||
placing = false;
|
||||
}
|
||||
|
||||
if (player.gamemode == GameMode.SPECTATOR || isTickPacket(event.getPacketType())) {
|
||||
usingWithoutPlacing = placing = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPredictionComplete(PredictionComplete predictionComplete) {
|
||||
if (!player.canSkipTicks()) return;
|
||||
|
||||
if (player.isTickingReliablyFor(3)) {
|
||||
for (; invalid >= 1; invalid--) {
|
||||
flagAndAlert();
|
||||
}
|
||||
}
|
||||
|
||||
invalid = 0;
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
package ac.grim.grimac.checks.impl.packetorder;
|
||||
|
||||
import ac.grim.grimac.checks.Check;
|
||||
import ac.grim.grimac.checks.CheckData;
|
||||
import ac.grim.grimac.checks.type.PacketCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientEntityAction;
|
||||
|
||||
import static com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerFlying.isFlying;
|
||||
|
||||
@CheckData(name = "PacketOrderO", experimental = true)
|
||||
public class PacketOrderO extends Check implements PacketCheck {
|
||||
public PacketOrderO(final GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
private boolean flying;
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
if (event.getPacketType() == PacketType.Play.Client.CLIENT_TICK_END) {
|
||||
flying = false;
|
||||
}
|
||||
|
||||
if (isFlying(event.getPacketType()) && player.supportsEndTick() && !player.packetStateData.lastPacketWasTeleport) {
|
||||
flying = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (flying && !isAsync(event.getPacketType()) && event.getPacketType() != PacketType.Play.Client.VEHICLE_MOVE) {
|
||||
if (player.inVehicle() && event.getPacketType() == PacketType.Play.Client.ENTITY_ACTION) {
|
||||
WrapperPlayClientEntityAction.Action action = new WrapperPlayClientEntityAction(event).getAction();
|
||||
if (action == WrapperPlayClientEntityAction.Action.START_SPRINTING || action == WrapperPlayClientEntityAction.Action.STOP_SPRINTING) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
flagAndAlert("type=" + event.getPacketType());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
package ac.grim.grimac.checks.impl.packetorder;
|
||||
|
||||
import ac.grim.grimac.checks.Check;
|
||||
import ac.grim.grimac.checks.CheckData;
|
||||
import ac.grim.grimac.checks.type.PacketCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.event.PacketSendEvent;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerBundle;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
|
||||
@CheckData(name = "PacketOrderP", experimental = true)
|
||||
public class PacketOrderP extends Check implements PacketCheck {
|
||||
public PacketOrderP(final GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
private byte trimTimer; // let the list shrink eventually
|
||||
private final IntArrayList transactions = new IntArrayList(0);
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
if (event.getPacketType() == PacketType.Play.Client.CHUNK_BATCH_ACK) {
|
||||
if (!transactions.rem(player.getLastTransactionReceived())) {
|
||||
flagAndAlert("invalid response");
|
||||
}
|
||||
} else if (!isAsync(event.getPacketType()) && !isTransaction(event.getPacketType())) {
|
||||
if (transactions.rem(player.getLastTransactionReceived())) {
|
||||
flagAndAlert("skipped response, type=" + event.getPacketType());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void onPacketSend(PacketSendEvent event) {
|
||||
if (event.getPacketType() == PacketType.Play.Server.CHUNK_BATCH_END) {
|
||||
boolean sendingBundlePacket = player.packetStateData.sendingBundlePacket;
|
||||
if (!sendingBundlePacket) player.user.sendPacket(new WrapperPlayServerBundle());
|
||||
|
||||
player.sendTransaction();
|
||||
int transaction = player.getLastTransactionSent();
|
||||
transactions.add(transaction);
|
||||
if (++trimTimer == 0) transactions.trim();
|
||||
player.addRealTimeTaskNext(() -> {
|
||||
if (transactions.rem(transaction))
|
||||
flagAndAlert("skipped response, type=TRANSACTION");
|
||||
});
|
||||
|
||||
if (!sendingBundlePacket) {
|
||||
event.getTasksAfterSend().add(() -> player.user.sendPacket(new WrapperPlayServerBundle()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-143
@@ -1,143 +0,0 @@
|
||||
package ac.grim.grimac.checks.impl.packetorder;
|
||||
|
||||
import ac.grim.grimac.checks.Check;
|
||||
import ac.grim.grimac.checks.type.PacketCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.math.GrimMath;
|
||||
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;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
|
||||
@Getter
|
||||
public final class PacketOrderProcessor extends Check implements PacketCheck {
|
||||
public PacketOrderProcessor(final GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
private boolean openingInventory; // only pre 1.12 clients on pre 1.12 servers
|
||||
private boolean swapping;
|
||||
private boolean dropping;
|
||||
private boolean interacting;
|
||||
private boolean attacking;
|
||||
private boolean releasing;
|
||||
private boolean digging;
|
||||
private boolean sprinting;
|
||||
private boolean sneaking;
|
||||
private boolean placing;
|
||||
private boolean using;
|
||||
private boolean picking;
|
||||
private boolean clickingInInventory;
|
||||
private boolean closingInventory;
|
||||
private boolean quickMoveClicking;
|
||||
private boolean pickUpClicking;
|
||||
private boolean leavingBed;
|
||||
private boolean startingToGlide;
|
||||
private boolean jumpingWithMount;
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
final PacketTypeCommon packetType = event.getPacketType();
|
||||
|
||||
if (packetType == PacketType.Play.Client.CLIENT_STATUS) {
|
||||
if (new WrapperPlayClientClientStatus(event).getAction() == WrapperPlayClientClientStatus.Action.OPEN_INVENTORY_ACHIEVEMENT) {
|
||||
openingInventory = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (packetType == PacketType.Play.Client.INTERACT_ENTITY) {
|
||||
if (new WrapperPlayClientInteractEntity(event).getAction() == WrapperPlayClientInteractEntity.InteractAction.ATTACK) {
|
||||
attacking = true;
|
||||
} else {
|
||||
interacting = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (packetType == PacketType.Play.Client.PLAYER_DIGGING) {
|
||||
switch (new WrapperPlayClientPlayerDigging(event).getAction()) {
|
||||
case SWAP_ITEM_WITH_OFFHAND -> swapping = true;
|
||||
case DROP_ITEM, DROP_ITEM_STACK -> dropping = true;
|
||||
case RELEASE_USE_ITEM -> releasing = true;
|
||||
case FINISHED_DIGGING, CANCELLED_DIGGING, START_DIGGING -> digging = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (packetType == PacketType.Play.Client.ENTITY_ACTION) {
|
||||
switch (new WrapperPlayClientEntityAction(event).getAction()) {
|
||||
case START_SPRINTING, STOP_SPRINTING -> {
|
||||
if (!player.inVehicle()) {
|
||||
sprinting = true;
|
||||
}
|
||||
}
|
||||
case STOP_SNEAKING, START_SNEAKING -> sneaking = true;
|
||||
case LEAVE_BED -> leavingBed = true;
|
||||
case START_FLYING_WITH_ELYTRA -> startingToGlide = true;
|
||||
case OPEN_HORSE_INVENTORY -> openingInventory = true;
|
||||
case START_JUMPING_WITH_HORSE, STOP_JUMPING_WITH_HORSE -> jumpingWithMount = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (packetType == PacketType.Play.Client.USE_ITEM) {
|
||||
using = true;
|
||||
}
|
||||
|
||||
if (packetType == PacketType.Play.Client.PLAYER_BLOCK_PLACEMENT) {
|
||||
if (new WrapperPlayClientPlayerBlockPlacement(event).getFace() == BlockFace.OTHER) {
|
||||
using = true;
|
||||
} else {
|
||||
placing = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (packetType == PacketType.Play.Client.PICK_ITEM) {
|
||||
picking = true;
|
||||
}
|
||||
|
||||
if (packetType == PacketType.Play.Client.CLICK_WINDOW) {
|
||||
clickingInInventory = true;
|
||||
|
||||
switch (new WrapperPlayClientClickWindow(event).getWindowClickType()) {
|
||||
case QUICK_MOVE -> quickMoveClicking = true;
|
||||
case PICKUP, PICKUP_ALL -> pickUpClicking = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (packetType == PacketType.Play.Client.CLOSE_WINDOW) {
|
||||
closingInventory = true;
|
||||
}
|
||||
|
||||
if (player.gamemode == GameMode.SPECTATOR || 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;
|
||||
swapping = false;
|
||||
dropping = false;
|
||||
attacking = false;
|
||||
interacting = false;
|
||||
releasing = false;
|
||||
digging = false;
|
||||
placing = false;
|
||||
using = false;
|
||||
picking = false;
|
||||
sprinting = false;
|
||||
sneaking = false;
|
||||
clickingInInventory = false;
|
||||
closingInventory = false;
|
||||
quickMoveClicking = false;
|
||||
pickUpClicking = false;
|
||||
leavingBed = false;
|
||||
startingToGlide = false;
|
||||
jumpingWithMount = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
public boolean isRightClicking() {
|
||||
return placing || using || interacting;
|
||||
}
|
||||
}
|
||||
@@ -1,147 +0,0 @@
|
||||
package ac.grim.grimac.checks.impl.prediction;
|
||||
|
||||
import ac.grim.grimac.GrimAPI;
|
||||
import ac.grim.grimac.checks.debug.AbstractDebugHandler;
|
||||
import ac.grim.grimac.checks.type.PostPredictionCheck;
|
||||
import ac.grim.grimac.platform.api.sender.Sender;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
|
||||
import ac.grim.grimac.utils.lists.EvictingQueue;
|
||||
import ac.grim.grimac.utils.math.Vector3dm;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
|
||||
public class DebugHandler extends AbstractDebugHandler implements PostPredictionCheck {
|
||||
private static final Component GRAY_ARROW = MiniMessage.miniMessage().deserialize("<gray>→0.03→</gray>");
|
||||
private static final Component P_PREFIX = MiniMessage.miniMessage().deserialize("<reset>P: </reset>");
|
||||
private static final Component A_PREFIX = MiniMessage.miniMessage().deserialize("<reset>A: </reset>");
|
||||
private static final Component O_PREFIX = MiniMessage.miniMessage().deserialize("<reset>O: </reset>");
|
||||
|
||||
private final Set<GrimPlayer> listeners = new CopyOnWriteArraySet<>(new HashSet<>());
|
||||
private boolean outputToConsole = false;
|
||||
private boolean enabledFlags = false;
|
||||
private boolean lastMovementIsFlag = false;
|
||||
|
||||
private final EvictingQueue<Component> predicted = new EvictingQueue<>(5);
|
||||
private final EvictingQueue<Component> actually = new EvictingQueue<>(5);
|
||||
private final EvictingQueue<Component> offset = new EvictingQueue<>(5);
|
||||
|
||||
public DebugHandler(GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPredictionComplete(final PredictionComplete predictionComplete) {
|
||||
if (!predictionComplete.isChecked()) return;
|
||||
|
||||
double offset = predictionComplete.getOffset();
|
||||
|
||||
if (listeners.isEmpty() && !outputToConsole) return;
|
||||
|
||||
final double predictedX = player.predictedVelocity.vectorX;
|
||||
final double predictedY = player.predictedVelocity.vectorY;
|
||||
final double predictedZ = player.predictedVelocity.vectorZ;
|
||||
if (predictedX == 0.0 && predictedY == 0.0 && predictedZ == 0.0 && offset == 0) return;
|
||||
|
||||
String color = pickColor(offset, offset);
|
||||
Vector3dm actually = player.actualMovement;
|
||||
|
||||
String xColor = pickColor(Math.abs(predictedX - actually.getX()), offset);
|
||||
String yColor = pickColor(Math.abs(predictedY - actually.getY()), offset);
|
||||
String zColor = pickColor(Math.abs(predictedZ - actually.getZ()), offset);
|
||||
|
||||
Component p = Component.empty()
|
||||
.append(P_PREFIX.color(NamedTextColor.NAMES.value(color)))
|
||||
.append(Component.text(predictedX).color(NamedTextColor.NAMES.value(xColor)))
|
||||
.append(Component.space())
|
||||
.append(Component.text(predictedY).color(NamedTextColor.NAMES.value(yColor)))
|
||||
.append(Component.space())
|
||||
.append(Component.text(predictedZ).color(NamedTextColor.NAMES.value(zColor)));
|
||||
|
||||
Component a = Component.empty()
|
||||
.append(A_PREFIX.color(NamedTextColor.NAMES.value(color)))
|
||||
.append(Component.text(actually.getX()).color(NamedTextColor.NAMES.value(xColor)))
|
||||
.append(Component.space())
|
||||
.append(Component.text(actually.getY()).color(NamedTextColor.NAMES.value(yColor)))
|
||||
.append(Component.space())
|
||||
.append(Component.text(actually.getZ()).color(NamedTextColor.NAMES.value(zColor)));
|
||||
|
||||
String canSkipTick = (player.couldSkipTick + " ").substring(0, 1);
|
||||
String actualMovementSkip = (player.skippedTickInActualMovement + "").charAt(0) + " ";
|
||||
Component o = Component.empty()
|
||||
.append(Component.text(canSkipTick).color(NamedTextColor.GRAY))
|
||||
.append(GRAY_ARROW)
|
||||
.append(Component.text(actualMovementSkip).color(NamedTextColor.GRAY))
|
||||
.append(O_PREFIX.color(NamedTextColor.NAMES.value(color)))
|
||||
.append(Component.text(offset));
|
||||
|
||||
String prefix = player.platformPlayer == null ? "null" : player.platformPlayer.getName() + " ";
|
||||
Component prefixComponent = Component.text(prefix);
|
||||
|
||||
boolean thisFlag = !color.equals("gray") && !color.equals("green");
|
||||
if (enabledFlags) {
|
||||
if (lastMovementIsFlag) {
|
||||
this.predicted.clear();
|
||||
this.actually.clear();
|
||||
this.offset.clear();
|
||||
}
|
||||
this.predicted.add(p);
|
||||
this.actually.add(a);
|
||||
this.offset.add(o);
|
||||
lastMovementIsFlag = thisFlag;
|
||||
}
|
||||
|
||||
if (thisFlag) {
|
||||
for (int i = 0; i < this.predicted.size(); i++) {
|
||||
player.user.sendMessage(this.predicted.get(i));
|
||||
player.user.sendMessage(this.actually.get(i));
|
||||
player.user.sendMessage(this.offset.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
for (GrimPlayer listener : listeners) {
|
||||
Component listenerPrefix = listener == getPlayer() ? Component.empty() : prefixComponent;
|
||||
listener.sendMessage(listenerPrefix.append(p));
|
||||
listener.sendMessage(listenerPrefix.append(a));
|
||||
listener.sendMessage(listenerPrefix.append(o));
|
||||
}
|
||||
|
||||
listeners.removeIf(player -> player.platformPlayer != null && !player.platformPlayer.isOnline());
|
||||
|
||||
if (outputToConsole) {
|
||||
Sender consoleSender = GrimAPI.INSTANCE.getPlatformServer().getConsoleSender();
|
||||
consoleSender.sendMessage(p);
|
||||
consoleSender.sendMessage(a);
|
||||
consoleSender.sendMessage(o);
|
||||
}
|
||||
}
|
||||
|
||||
private String pickColor(double offset, double totalOffset) {
|
||||
if (player.getSetbackTeleportUtil().blockOffsets) return "gray";
|
||||
if (offset <= 0 || totalOffset <= 0) {
|
||||
return "gray";
|
||||
} else if (offset < 0.0001) {
|
||||
return "green";
|
||||
} else if (offset < 0.01) {
|
||||
return "yellow";
|
||||
} else {
|
||||
return "red";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toggleListener(GrimPlayer player) {
|
||||
if (!listeners.remove(player)) listeners.add(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean toggleConsoleOutput() {
|
||||
this.outputToConsole = !outputToConsole;
|
||||
return this.outputToConsole;
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
package ac.grim.grimac.checks.impl.sprint;
|
||||
|
||||
import ac.grim.grimac.checks.Check;
|
||||
import ac.grim.grimac.checks.CheckData;
|
||||
import ac.grim.grimac.checks.type.PostPredictionCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
|
||||
@CheckData(name = "SprintG", description = "Sprinting while in water", experimental = true)
|
||||
public class SprintG extends Check implements PostPredictionCheck {
|
||||
public SprintG(GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPredictionComplete(final PredictionComplete predictionComplete) {
|
||||
if (player.wasTouchingWater && (player.wasWasTouchingWater || player.getClientVersion() == ClientVersion.V_1_21_4)
|
||||
&& !player.wasEyeInWater && player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_13)
|
||||
&& player.wasLastPredictionCompleteChecked && predictionComplete.isChecked()) {
|
||||
if (player.isSprinting && !player.isSwimming) {
|
||||
flagAndAlertWithSetback();
|
||||
} else {
|
||||
reward();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
package ac.grim.grimac.checks.impl.timer;
|
||||
|
||||
import ac.grim.grimac.api.config.ConfigManager;
|
||||
import ac.grim.grimac.checks.CheckData;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
|
||||
// This works around 1.3 timer, to prevent too high abuse - maybe there's a better solution?
|
||||
@CheckData(name = "TimerLimit", setback = 10)
|
||||
public class TimerLimit extends Timer {
|
||||
|
||||
// At what ping should we start to limit the balance advantage? (nanos)
|
||||
private long limitAbuseOverPing;
|
||||
|
||||
public TimerLimit(GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doCheck(final PacketReceiveEvent event) {
|
||||
// 1:1 with Timer minus cancelling the packet
|
||||
if (timerBalanceRealTime > System.nanoTime()) {
|
||||
// If timer check already flagged, don't flag.
|
||||
if (!event.isCancelled()) {
|
||||
if (flagAndAlert() && shouldSetback()) {
|
||||
player.getSetbackTeleportUtil().executeNonSimulatingSetback();
|
||||
}
|
||||
}
|
||||
|
||||
// Reset the violation by 1 movement
|
||||
timerBalanceRealTime -= 50e6;
|
||||
}
|
||||
|
||||
limitFallBehind();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void limitFallBehind() {
|
||||
// Limit using transaction ping if over 1000ms (default)
|
||||
long playerClock = lastMovementPlayerClock;
|
||||
if (limitAbuseOverPing != -1 && System.nanoTime() - playerClock > limitAbuseOverPing) {
|
||||
playerClock = System.nanoTime() - limitAbuseOverPing;
|
||||
}
|
||||
timerBalanceRealTime = Math.max(timerBalanceRealTime, playerClock - clockDrift);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReload(ConfigManager config) {
|
||||
super.onReload(config);
|
||||
limitAbuseOverPing = config.getLongElse(getConfigName() + ".ping-abuse-limit-threshold", 1000L);
|
||||
if (limitAbuseOverPing != -1) {
|
||||
limitAbuseOverPing *= (long) 1e6;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loaded 100 of 815 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user