Compare commits
76
Commits
2.0
...
2.0-debug-clock
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4933fd9d4f | ||
|
|
c2c044f61b | ||
|
|
2d785fdc2c | ||
|
|
403fd36830 | ||
|
|
e386b936ff | ||
|
|
3fc985c8f3 | ||
|
|
499bf7e2f9 | ||
|
|
ba91251307 | ||
|
|
8ddd7f18a5 | ||
|
|
3f67272721 | ||
|
|
3380d7f9fd | ||
|
|
e1bd2edc7c | ||
|
|
9b500a2617 | ||
|
|
6df484bcc6 | ||
|
|
a46d29620f | ||
|
|
b14628c578 | ||
|
|
5c715315c9 | ||
|
|
ba1d1dbc16 | ||
|
|
def21633e2 | ||
|
|
918e6575e8 | ||
|
|
ea1f1615b6 | ||
|
|
c323cf06ce | ||
|
|
0ba331780e | ||
|
|
fc47c5b663 | ||
|
|
17cf067d58 | ||
|
|
6280b28e16 | ||
|
|
bfd2bd0e22 | ||
|
|
0df77dd32a | ||
|
|
b373e5f1c4 | ||
|
|
115c32916f | ||
|
|
5f8692deda | ||
|
|
f8a5315fff | ||
|
|
102b5842e7 | ||
|
|
a76bf9ce09 | ||
|
|
4f3e715f94 | ||
|
|
1b47e31776 | ||
|
|
b69906f809 | ||
|
|
9ba4052c35 | ||
|
|
a86bf57a39 | ||
|
|
a82f2b888a | ||
|
|
89cf181772 | ||
|
|
6e5ec332dd | ||
|
|
79bf7e4fd1 | ||
|
|
d0aafdd9f7 | ||
|
|
3f19abc859 | ||
|
|
8a6863b9d3 | ||
|
|
26492fd8e3 | ||
|
|
15e809835b | ||
|
|
1eae5484c7 | ||
|
|
90036e2bf2 | ||
|
|
faf2fb3afb | ||
|
|
7461aca13f | ||
|
|
f381805cad | ||
|
|
2fa022eb00 | ||
|
|
44900e584b | ||
|
|
a2f4dc1979 | ||
|
|
0237f956ff | ||
|
|
e222df8e87 | ||
|
|
bbab7e222c | ||
|
|
41c7e41a0e | ||
|
|
5551ef68e1 | ||
|
|
ca6e83de4d | ||
|
|
4d6ac75c73 | ||
|
|
1317ae0bb4 | ||
|
|
d538c59a79 | ||
|
|
6a223a30c7 | ||
|
|
2e1c79b4e3 | ||
|
|
bb143d7993 | ||
|
|
322d2567c0 | ||
|
|
8d7a4cce87 | ||
|
|
832194a1e9 | ||
|
|
0321e0bceb | ||
|
|
63854605b6 | ||
|
|
5c4602188e | ||
|
|
163e120f00 | ||
|
|
ce6e1d3e9f |
No files matched your search
@@ -7,6 +7,8 @@ indent_style = space
|
||||
insert_final_newline = true
|
||||
max_line_length = 100
|
||||
tab_width = 4
|
||||
end_of_line = crlf
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
# intellij specific configuration
|
||||
ij_continuation_indent_size = 8
|
||||
@@ -15,3 +17,6 @@ ij_formatter_on_tag = @formatter:on
|
||||
ij_formatter_tags_enabled = false
|
||||
ij_smart_tabs = false
|
||||
ij_wrap_on_typing = false
|
||||
|
||||
[.github/ISSUE_TEMPLATE/*.yml]
|
||||
indent_size = 2
|
||||
@@ -5,7 +5,7 @@ body:
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Describe the bypass and how to replicate it
|
||||
description: Provide a video, or detailed instructions to help replicate it.
|
||||
description: Provide a video or detailed instructions to help replicate it.
|
||||
placeholder: |
|
||||
Step 1. Do this
|
||||
Step 2. Then do this
|
||||
@@ -17,8 +17,8 @@ body:
|
||||
attributes:
|
||||
label: Grim version
|
||||
description: |
|
||||
Provide the git commit or the version in the jar's name.
|
||||
placeholder: A example would be b85c2d0 or grim version 2.3.5, etc
|
||||
Provide the version of Grim running on the server. You can run `/grim version` to check.
|
||||
placeholder: Examples include 2.3.73-8ddd7f1, 2.3.72, etc.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
@@ -26,23 +26,23 @@ body:
|
||||
attributes:
|
||||
label: Server version
|
||||
description: |
|
||||
Run `/version` on your server to check. Provide both the server type (paper, airplane, etc) & the minecraft version.
|
||||
Run `/version` on your server to check. Provide both the server type (ie Paper, Airplane, etc) & the Minecraft version.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
attributes:
|
||||
label: Client version
|
||||
description: |
|
||||
Provide the version of your Minecraft client.
|
||||
label: Client version
|
||||
description: |
|
||||
Provide the version of your Minecraft client. Also provide the protocol version if changed.
|
||||
validations:
|
||||
required: true
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Plugins
|
||||
label: Plugins/Mods
|
||||
description: |
|
||||
List all plugins or mods running on your server. To list plugins on bukkit, run `/plugins`.
|
||||
List all plugins or mods installed on your server. To list plugins on Bukkit-based servers, run `/plugins`.
|
||||
If your server is behind a proxy such as Bungeecord, you should list the proxy's plugins as well.
|
||||
validations:
|
||||
required: true
|
||||
@@ -6,7 +6,7 @@ body:
|
||||
attributes:
|
||||
label: Stack trace
|
||||
description: |
|
||||
Please include the entire stack trace with no attachments. If you would like, you can use a text pasting site such as https://paste.gg.
|
||||
Provide the entire stack trace with no attachments. If you would like, you can use a text pasting site such as https://mclo.gs.
|
||||
placeholder: Paste the stack trace or link to the stack trace here
|
||||
validations:
|
||||
required: true
|
||||
@@ -14,7 +14,7 @@ body:
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: How to replicate it if known
|
||||
description: A video, or detailed instructions to help replicate the issue would help.
|
||||
description: A video or detailed instructions to help replicate the issue would help.
|
||||
placeholder: |
|
||||
Step 1. Do this
|
||||
Step 2. Then do this
|
||||
@@ -26,8 +26,8 @@ body:
|
||||
attributes:
|
||||
label: Grim version
|
||||
description: |
|
||||
Provide the git commit or the version in the jar's name.
|
||||
placeholder: A example would be b85c2d0 or grim version 2.3.5, etc
|
||||
Provide the version of Grim running on the server. You can run `/grim version` to check.
|
||||
placeholder: Examples include 2.3.73-8ddd7f1, 2.3.72, etc.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
@@ -35,27 +35,27 @@ body:
|
||||
attributes:
|
||||
label: Server version
|
||||
description: |
|
||||
Run `/version` on your server to check. Provide both the server type (paper, airplane, etc) & the minecraft version.
|
||||
Run `/version` on your server to check. Provide both the server type (ie Paper, Airplane, etc) & the Minecraft version.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: dropdown
|
||||
id: type
|
||||
attributes:
|
||||
label: Affected Platform(s)
|
||||
description: Which platforms does this issue affect? If you aren't sure, select both.
|
||||
options:
|
||||
- "Bukkit"
|
||||
- "Fabric"
|
||||
multiple: true
|
||||
label: Affected Platform(s)
|
||||
description: Which platforms does this issue affect? If you aren't sure, select both.
|
||||
options:
|
||||
- "Bukkit"
|
||||
- "Fabric"
|
||||
multiple: true
|
||||
validations:
|
||||
required: true
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Plugins/mods
|
||||
label: Plugins/Mods
|
||||
description: |
|
||||
List all plugins or mods running on your server. To list plugins on bukkit, run `/plugins`.
|
||||
List all plugins or mods installed on your server. To list plugins on Bukkit-based servers, run `/plugins`.
|
||||
If your server is behind a proxy such as Bungeecord, you should list the proxy's plugins as well.
|
||||
validations:
|
||||
required: true
|
||||
@@ -20,8 +20,8 @@ body:
|
||||
attributes:
|
||||
label: Grim version
|
||||
description: |
|
||||
Provide the git commit or the version in the jar's name.
|
||||
placeholder: A example would be b85c2d0 or grim version 2.3.5, etc
|
||||
Provide the version of Grim running on the server. You can run `/grim version` to check.
|
||||
placeholder: Examples include 2.3.73-8ddd7f1, 2.3.72, etc.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
@@ -29,7 +29,7 @@ body:
|
||||
attributes:
|
||||
label: Server version
|
||||
description: |
|
||||
Run `/version` on your server to check. Provide both the server type (paper, airplane, etc) & the minecraft version.
|
||||
Run `/version` on your server to check. Provide both the server type (ie Paper, Airplane, etc) & the Minecraft version.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
@@ -37,16 +37,16 @@ body:
|
||||
attributes:
|
||||
label: Client version
|
||||
description: |
|
||||
Provide the version of your Minecraft client. You must be able to reproduce the false positive using the vanilla client.
|
||||
Lunar Client, TLauncher etc. will result in your issue being closed.
|
||||
Provide the version of your Minecraft client. You must be able to reproduce the false positive using a vanilla client.
|
||||
Using Lunar Client, TLauncher, Fabric, etc, will result in your issue being closed.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Plugins
|
||||
label: Plugins/Mods
|
||||
description: |
|
||||
List all plugins or mods running on your server. To list plugins on bukkit, run `/plugins`.
|
||||
List all plugins or mods installed on your server. To list plugins on Bukkit-based servers, run `/plugins`.
|
||||
If your server is behind a proxy such as Bungeecord, you should list the proxy's plugins as well.
|
||||
validations:
|
||||
required: true
|
||||
@@ -5,6 +5,6 @@ body:
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Feature description
|
||||
description: Describe the feature and why it should be added
|
||||
description: Describe the feature and why it should be added.
|
||||
validations:
|
||||
required: true
|
||||
required: true
|
||||
@@ -5,7 +5,7 @@ body:
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Describe the bug and how to replicate it
|
||||
description: A video, or detailed instructions to help replicate the issue would help.
|
||||
description: A video or detailed instructions to help replicate the issue would help.
|
||||
placeholder: |
|
||||
Step 1. Do this
|
||||
Step 2. Then do this
|
||||
@@ -16,9 +16,8 @@ body:
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Grim version
|
||||
description: |
|
||||
Provide the git commit or the version in the jar's name.
|
||||
placeholder: A example would be b85c2d0 or grim version 2.3.5, etc
|
||||
description: Provide the version of Grim running on the server. You can run `/grim version` to check.
|
||||
placeholder: Examples include 2.3.73-8ddd7f1, 2.3.72, etc.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
@@ -26,27 +25,27 @@ body:
|
||||
attributes:
|
||||
label: Server version
|
||||
description: |
|
||||
Run `/version` on your server to check. Provide both the server type (paper, airplane, etc) & the minecraft version.
|
||||
Run `/version` on your server to check. Provide both the server type (ie Paper, Airplane, etc) & the Minecraft version.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: dropdown
|
||||
id: type
|
||||
attributes:
|
||||
label: Affected Platform(s)
|
||||
description: Which platforms does this issue affect? If you aren't sure, select both.
|
||||
options:
|
||||
- "Bukkit"
|
||||
- "Fabric"
|
||||
multiple: true
|
||||
label: Affected Platform(s)
|
||||
description: Which platforms does this issue affect? If you aren't sure, select both.
|
||||
options:
|
||||
- "Bukkit"
|
||||
- "Fabric"
|
||||
multiple: true
|
||||
validations:
|
||||
required: true
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Plugins
|
||||
label: Plugins/Mods
|
||||
description: |
|
||||
List all plugins or mods running on your server. To list plugins on bukkit, run `/plugins`.
|
||||
List all plugins or mods installed on your server. To list plugins on Bukkit-based servers, run `/plugins`.
|
||||
If your server is behind a proxy such as Bungeecord, you should list the proxy's plugins as well.
|
||||
validations:
|
||||
required: true
|
||||
@@ -19,8 +19,15 @@ body:
|
||||
- type: input
|
||||
attributes:
|
||||
label: Grim Log
|
||||
description: |
|
||||
Provide the generated /grim log link.
|
||||
description: Provide the generated /grim log link.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Grim version
|
||||
description: Provide the version of Grim running on the server. You can run `/grim version` to check.
|
||||
placeholder: Examples include 2.3.73-8ddd7f1, 2.3.72, etc.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
@@ -28,7 +35,7 @@ body:
|
||||
attributes:
|
||||
label: Server version
|
||||
description: |
|
||||
Run `/version` on your server to check. Provide both the server type (paper, airplane, etc) & the minecraft version.
|
||||
Run `/version` on your server to check. Provide both the server type (ie Paper, Airplane, etc) & the Minecraft version.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
@@ -36,16 +43,16 @@ body:
|
||||
attributes:
|
||||
label: Client version
|
||||
description: |
|
||||
Provide the version of your Minecraft client. You must be able to reproduce the false positive using the vanilla client.
|
||||
Lunar Client, TLauncher etc. will result in your issue being closed.
|
||||
Provide the version of your Minecraft client. You must be able to reproduce the false positive using a vanilla client.
|
||||
Using Lunar Client, TLauncher, Fabric, etc, will result in your issue being closed.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Plugins
|
||||
label: Plugins/Mods
|
||||
description: |
|
||||
List all plugins or mods running on your server. To list plugins on bukkit, run `/plugins`.
|
||||
List all plugins or mods installed on your server. To list plugins on Bukkit-based servers, run `/plugins`.
|
||||
If your server is behind a proxy such as Bungeecord, you should list the proxy's plugins as well.
|
||||
validations:
|
||||
required: true
|
||||
required: true
|
||||
@@ -36,7 +36,7 @@ on:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: tenki-standard-autoscale
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
@@ -85,7 +85,7 @@ jobs:
|
||||
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"
|
||||
@@ -94,23 +94,23 @@ jobs:
|
||||
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
|
||||
# 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
|
||||
@@ -151,7 +151,7 @@ jobs:
|
||||
|
||||
- name: Prepare Final Changelog
|
||||
id: changelog
|
||||
# Add this 'env' block to safely load the changelogs as environment variables
|
||||
# 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 }}
|
||||
@@ -165,17 +165,36 @@ jobs:
|
||||
# Use the environment variable for the dev changelog.
|
||||
NOTES="$DEV_NOTES"
|
||||
fi
|
||||
|
||||
|
||||
if [ -z "$NOTES" ]; then
|
||||
NOTES="* No new code changes in this build."
|
||||
fi
|
||||
|
||||
|
||||
# This is the correct way to set a multi-line output variable in Actions
|
||||
{
|
||||
echo "notes<<EOF"
|
||||
echo "$NOTES"
|
||||
echo "EOF"
|
||||
} >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: 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.21.5 <=1.21.10'
|
||||
dependencies: 'fabric-api'
|
||||
|
||||
#------------------------------------------------------------------
|
||||
# Publish to Modrinth & API
|
||||
#------------------------------------------------------------------
|
||||
@@ -183,7 +202,7 @@ jobs:
|
||||
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-id: ${{ secrets.MODRINTH_ID }}
|
||||
modrinth-token: ${{ secrets.MODRINTH_TOKEN }}
|
||||
modrinth-featured: ${{ inputs.is-release-build }} # Only feature release builds
|
||||
|
||||
@@ -198,25 +217,7 @@ jobs:
|
||||
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'
|
||||
game-versions: '>=1.7 <=1.21.10'
|
||||
|
||||
- name: Publish to API
|
||||
if: ${{ inputs.publish-artifacts == true && env.BUILD_UPDATE_TOKEN != '' }}
|
||||
@@ -226,4 +227,4 @@ jobs:
|
||||
-H "User-Agent: GrimACBuilder/v1" \
|
||||
-H "Authorization: Bearer ${{ secrets.BUILD_UPDATE_TOKEN }}" \
|
||||
-d '{"org": "${{ github.repository_owner }}", "repository": "${{ github.event.repository.name }}", "branch": "${{ github.ref_name }}", "version": "${{ env.BUILD_VERSION }}"}' \
|
||||
"${{ secrets.BUILD_UPDATE_URL }}"
|
||||
"${{ secrets.BUILD_UPDATE_URL }}"
|
||||
@@ -14,12 +14,12 @@ name: "CodeQL"
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches: [ dev ]
|
||||
branches: [dev]
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: tenki-standard-autoscale
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
@@ -28,7 +28,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [ 'java-kotlin' ]
|
||||
language: ['java-kotlin']
|
||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
|
||||
# Learn more:
|
||||
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
|
||||
@@ -60,6 +60,7 @@ jobs:
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v3
|
||||
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
|
||||
@@ -70,6 +71,5 @@ jobs:
|
||||
#- run: |
|
||||
# make bootstrap
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v3
|
||||
uses: github/codeql-action/analyze@v3
|
||||
@@ -20,8 +20,6 @@ on:
|
||||
- 'release'
|
||||
- 'release/**'
|
||||
paths:
|
||||
- '.github/workflows/**'
|
||||
- 'buildSrc/**'
|
||||
- 'bukkit/**'
|
||||
- 'common/**'
|
||||
- 'fabric/**'
|
||||
|
||||
@@ -33,30 +33,27 @@ A premium version is planned, which will offer additional subscription-based pai
|
||||
- ~~[Hangar](https://hangar.papermc.io/GrimAnticheat/GrimAnticheat)~~
|
||||
- ~~[SpigotMC](https://www.spigotmc.org/resources/grim-anticheat.99923/)~~
|
||||
|
||||
## 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 a proxy such as Velocity or Bungeecord:
|
||||
- If you use Geyser, Floodgate must be installed on the backend server (where Grim is) so Grim can access the Floodgate API.
|
||||
- If you use ViaVersion, it must be installed on the backend server (where Grim is) ONLY.
|
||||
Grim does not support having ViaVersion installed on the proxy, even if it is also installed on the backend.
|
||||
|
||||
## Resources
|
||||
|
||||
- For documentation and examples visit the [Wiki](https://github.com/GrimAnticheat/Grim/wiki).
|
||||
- For answers to commonly asked questions visit the [FAQ](https://github.com/GrimAnticheat/Grim/wiki/FAQ).
|
||||
- For community support and project discussion join our [Discord](https://discord.grim.ac).
|
||||
|
||||
|
||||
## Pull Requests
|
||||
|
||||
See [Contributing](CONTRIBUTING.md) for more information about contributing and what our guidelines
|
||||
are.
|
||||
|
||||
|
||||
## 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
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import kotlinx.serialization.json.*
|
||||
import java.util.jar.JarFile
|
||||
import java.util.zip.ZipEntry
|
||||
|
||||
plugins {
|
||||
`java-library`
|
||||
@@ -27,26 +28,35 @@ fun parseFabricModJson(jarFile: File, project: Project): Set<DependencyIdentifie
|
||||
val nestedJars = mutableSetOf<DependencyIdentifier>()
|
||||
try {
|
||||
JarFile(jarFile).use { jar ->
|
||||
val fabricModJsonEntry = jar.getEntry("fabric.mod.json") ?: return@use
|
||||
jar.getInputStream(fabricModJsonEntry).bufferedReader().use { reader ->
|
||||
val jsonObject = Json.parseToJsonElement(reader.readText()).jsonObject
|
||||
val jars = jsonObject["jars"]?.jsonArray ?: return@use
|
||||
jars.forEach { jarEntry ->
|
||||
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")
|
||||
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)
|
||||
project.logger.error("Failed to parse fabric.mod.json in $jarFile: ${e.message}")
|
||||
}
|
||||
return nestedJars
|
||||
}
|
||||
@@ -56,41 +66,50 @@ fun extractEmbeddedJars(jarFile: File, project: Project): Set<DependencyIdentifi
|
||||
embeddedJars.addAll(parseFabricModJson(jarFile, project)) // Retain existing logic for top-level fabric.mod.json parsing
|
||||
try {
|
||||
JarFile(jarFile).use { jar ->
|
||||
for (entry in jar.entries()) {
|
||||
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/")
|
||||
) continue
|
||||
project.logger.debug("Found embedded JAR: {} in {}", entryName, jarFile)
|
||||
val tempFile = File.createTempFile("embedded-", ".jar").also(File::deleteOnExit)
|
||||
jar.getInputStream(entry).use { input ->
|
||||
tempFile.outputStream().use { output ->
|
||||
input.copyTo(output)
|
||||
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 = embeddedJar.getEntry("fabric.mod.json") ?: run {
|
||||
project.logger.error("No fabric.mod.json found in embedded JAR: {}. Skipping.", entryName)
|
||||
return@use
|
||||
}
|
||||
embeddedJar.getInputStream(fabricModJsonEntry).bufferedReader().use { reader ->
|
||||
val jsonObject = Json.parseToJsonElement(reader.readText()).jsonObject
|
||||
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
|
||||
?: error("Missing 'id' in fabric.mod.json of embedded JAR: $entryName")
|
||||
// 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 ""
|
||||
|
||||
val group = id.replace('_', '.').dropLastWhile { it != '.' }.dropLast(1)
|
||||
// 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")
|
||||
|
||||
// Extract name and version from the JAR filename
|
||||
val jarName = entryName.substringAfterLast("/").removeSuffix(".jar")
|
||||
val (name, version) = parseNameAndVersionFromJarName(jarName)
|
||||
?: error("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)
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -107,13 +126,15 @@ fun parseNameAndVersionFromJarName(jarName: String): Pair<String, String>? {
|
||||
val versionStartMatch = versionStartRegex.find(jarName) ?: return null
|
||||
val versionStartIndex = versionStartMatch.range.first
|
||||
|
||||
if (versionStartIndex == 0 || versionStartIndex + 1 >= jarName.length) return null
|
||||
|
||||
// Split the JAR name into name and version at the version start index
|
||||
val name = jarName.take(versionStartIndex)
|
||||
val version = jarName.drop(versionStartIndex + 1) // Skip the hyphen
|
||||
val version = jarName.substring(versionStartIndex + 1) // Skip the hyphen
|
||||
|
||||
return Pair(name, version)
|
||||
return if (name.isNotEmpty() && version.isNotEmpty()) {
|
||||
Pair(name, version)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
fun shouldExcludeDependency(
|
||||
@@ -166,12 +187,15 @@ fun isJijTarget(
|
||||
project: Project
|
||||
): Boolean {
|
||||
val classifier = dependency.moduleArtifacts.firstOrNull()?.classifier ?: ""
|
||||
project.logger.debug("Checking JIJ target: {}", DependencyIdentifier(
|
||||
dependency.moduleGroup,
|
||||
dependency.moduleName,
|
||||
dependency.moduleVersion,
|
||||
classifier
|
||||
))
|
||||
project.logger.debug(
|
||||
"Checking JIJ target: {}",
|
||||
DependencyIdentifier(
|
||||
dependency.moduleGroup,
|
||||
dependency.moduleName,
|
||||
dependency.moduleVersion,
|
||||
classifier
|
||||
)
|
||||
)
|
||||
return !shouldExcludeDependency(
|
||||
dependency,
|
||||
allEmbeddedDependencies,
|
||||
@@ -189,14 +213,14 @@ fun includeDependencyWithExclusions(
|
||||
project.logger.debug("Including external dependency as JIJ with dynamic exclusions: {}", depId)
|
||||
project.dependencies {
|
||||
// Include dependency with classifier (if present)
|
||||
"include"(depId.toString()) {
|
||||
"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)
|
||||
project.logger.debug("Excluding embedded transitive dependency: ${embeddedDep.name}:${embeddedDep.version}${if (embeddedDep.classifier.isNotEmpty()) ":${embeddedDep.classifier}" else ""}")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -212,52 +236,59 @@ fun processDependencies(
|
||||
) {
|
||||
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)
|
||||
val depKey = depId.toString()
|
||||
if (!processed.contains(depKey)) {
|
||||
processed.add(depKey)
|
||||
project.logger.debug("Resolved dependency: {}", depId)
|
||||
|
||||
if (!processed.add(depKey)) return
|
||||
|
||||
project.logger.debug("Resolved dependency: {}", depKey)
|
||||
|
||||
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 ?: error("Project dependency not found: ${dep.moduleName}")
|
||||
project.logger.debug("Including project dependency as JIJ: {}", projectPath)
|
||||
project.dependencies {
|
||||
"include"(project.project(projectPath))
|
||||
val jarFile = dep.moduleArtifacts.firstOrNull()?.file
|
||||
if (jarFile != null && jarFile.exists()) {
|
||||
val embeddedJars = extractEmbeddedJars(jarFile, project)
|
||||
embeddedJars.forEach { embeddedDep ->
|
||||
allEmbeddedDependencies[embeddedDep] = jarFile
|
||||
}
|
||||
actuallyIncludedDependencies.add(depId)
|
||||
} else {
|
||||
includeDependencyWithExclusions(
|
||||
depId,
|
||||
project,
|
||||
actuallyIncludedDependencies,
|
||||
allEmbeddedDependencies.keys
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
processDependencies(
|
||||
dep.children,
|
||||
processed,
|
||||
allEmbeddedDependencies,
|
||||
actuallyIncludedDependencies,
|
||||
project
|
||||
)
|
||||
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
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -273,16 +304,22 @@ project.afterEvaluate {
|
||||
dependencies.forEach { dep ->
|
||||
val classifier = dep.moduleArtifacts.firstOrNull()?.classifier ?: ""
|
||||
val depKey = "${dep.moduleGroup}:${dep.moduleName}:${dep.moduleVersion}${if (classifier.isNotEmpty()) ":$classifier" else ""}"
|
||||
if (!processed.add(depKey)) return@forEach
|
||||
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)
|
||||
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)
|
||||
}
|
||||
collectAllEmbeddedDependenciesForAfterEvaluate(dep.children)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -302,7 +339,8 @@ project.afterEvaluate {
|
||||
tasks.withType<Jar>().configureEach {
|
||||
doFirst {
|
||||
project.logger.debug("Resolving JIJ dependencies for JAR inclusion")
|
||||
val resolvedDependencies = jijDependencies.resolvedConfiguration.firstLevelModuleDependencies
|
||||
val resolvedDependencies =
|
||||
jijDependencies.resolvedConfiguration.firstLevelModuleDependencies
|
||||
val processed = mutableSetOf<String>()
|
||||
val allEmbeddedDependencies = mutableMapOf<DependencyIdentifier, File>()
|
||||
val actuallyIncludedDependencies = mutableSetOf<DependencyIdentifier>()
|
||||
@@ -310,20 +348,22 @@ tasks.withType<Jar>().configureEach {
|
||||
fun collectAllEmbeddedDependenciesForJar(dependencies: Set<ResolvedDependency>) {
|
||||
dependencies.forEach { dep ->
|
||||
val depKey = "${dep.moduleGroup}:${dep.moduleName}:${dep.moduleVersion}"
|
||||
if (!processed.add(depKey)) return@forEach
|
||||
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
|
||||
)
|
||||
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)
|
||||
}
|
||||
collectAllEmbeddedDependenciesForJar(dep.children)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -336,27 +376,30 @@ tasks.withType<Jar>().configureEach {
|
||||
dependencies.forEach { dep ->
|
||||
val depKey = "${dep.moduleGroup}:${dep.moduleName}:${dep.moduleVersion}"
|
||||
val depId = DependencyIdentifier(dep.moduleGroup, dep.moduleName, dep.moduleVersion)
|
||||
if (!processed.add(depKey)) return@forEach
|
||||
project.logger.debug("Resolved JIJ dependency for JAR: {}", depId)
|
||||
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
|
||||
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)
|
||||
if (isJijTarget(
|
||||
dep,
|
||||
allEmbeddedDependencies.keys,
|
||||
actuallyIncludedDependencies,
|
||||
project
|
||||
)
|
||||
) {
|
||||
project.logger.debug("Including JIJ dependency in JAR: {}", depId)
|
||||
actuallyIncludedDependencies.add(depId)
|
||||
}
|
||||
logDependencies(dep.children)
|
||||
}
|
||||
logDependencies(dep.children)
|
||||
}
|
||||
}
|
||||
logDependencies(resolvedDependencies)
|
||||
|
||||
@@ -60,7 +60,8 @@ object VersionUtil {
|
||||
.apply { waitFor() }
|
||||
.inputStream
|
||||
.use { stdout.writeBytes(it.readAllBytes()) }
|
||||
return stdout.toString().trim()
|
||||
val fullCommit = stdout.toString().trim()
|
||||
return fullCommit.take(minOf(fullCommit.length, 7))
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -195,7 +195,7 @@ publishing.publications.create<MavenPublication>("maven") {
|
||||
|
||||
tasks {
|
||||
runServer {
|
||||
minecraftVersion("1.21.4")
|
||||
minecraftVersion("1.21.10")
|
||||
}
|
||||
|
||||
shadowJar {
|
||||
|
||||
@@ -6,6 +6,8 @@ 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;
|
||||
|
||||
@@ -15,6 +17,8 @@ import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class BukkitGrimEntity implements GrimEntity {
|
||||
|
||||
protected static final boolean CAN_USE_DIRECT_GETTERS = PacketEvents.getAPI().getServerManager().getVersion().isNewerThan(ServerVersion.V_1_20_1) && PaperUtils.PAPER;
|
||||
|
||||
private final Entity entity;
|
||||
private BukkitPlatformWorld bukkitPlatformWorld;
|
||||
|
||||
@@ -75,4 +79,26 @@ public class BukkitGrimEntity implements GrimEntity {
|
||||
location.getPitch()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double distanceSquared(double oX, double oY, double oZ) {
|
||||
if (CAN_USE_DIRECT_GETTERS) {
|
||||
double x = this.entity.getX();
|
||||
double y = this.entity.getY();
|
||||
double z = this.entity.getZ();
|
||||
double distX = (x - oX) * (x - oX);
|
||||
double distY = (y - oY) * (y - oY);
|
||||
double distZ = (z - oZ) * (z - oZ);
|
||||
return distX + distY + distZ;
|
||||
} else {
|
||||
org.bukkit.Location location = this.entity.getLocation();
|
||||
double x = location.getX();
|
||||
double y = location.getY();
|
||||
double z = location.getZ();
|
||||
double distX = (x - oX) * (x - oX);
|
||||
double distY = (y - oY) * (y - oY);
|
||||
double distZ = (z - oZ) * (z - oZ);
|
||||
return distX + distY + distZ;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -74,7 +74,7 @@ public class PistonEvent implements Listener {
|
||||
final Vector3i sourcePos = new Vector3i(piston.getX(), piston.getY(), piston.getZ());
|
||||
|
||||
for (GrimPlayer player : GrimAPI.INSTANCE.getPlayerDataManager().getEntries()) {
|
||||
if (player.compensatedWorld.isChunkLoaded(chunkX, chunkZ) && isCloseEnough(sourcePos, player.compensatedEntities.self.trackedServerPosition.getPos())) {
|
||||
if (isCloseEnough(sourcePos, player.compensatedEntities.self.trackedServerPosition.getPos()) && player.compensatedWorld.isChunkLoaded(chunkX, chunkZ)) {
|
||||
final int lastTrans = player.lastTransactionSent.get();
|
||||
PistonData data = new PistonData(blockFace, boxes, lastTrans, true, hasSlimeBlock, hasHoneyBlock);
|
||||
player.latencyUtils.addRealTimeTaskAsync(lastTrans, () -> player.compensatedWorld.activePistons.add(data));
|
||||
@@ -132,7 +132,7 @@ public class PistonEvent implements Listener {
|
||||
Vector3i sourcePos = new Vector3i(event.getBlock().getX(), event.getBlock().getY(), event.getBlock().getZ());
|
||||
|
||||
for (GrimPlayer player : GrimAPI.INSTANCE.getPlayerDataManager().getEntries()) {
|
||||
if (player.compensatedWorld.isChunkLoaded(chunkX, chunkZ) && isCloseEnough(sourcePos, player.compensatedEntities.self.trackedServerPosition.getPos())) {
|
||||
if (isCloseEnough(sourcePos, player.compensatedEntities.self.trackedServerPosition.getPos()) && player.compensatedWorld.isChunkLoaded(chunkX, chunkZ)) {
|
||||
int lastTrans = player.lastTransactionSent.get();
|
||||
PistonData data = new PistonData(face, boxes, lastTrans, false, hasSlimeBlock, hasHoneyBlock);
|
||||
player.latencyUtils.addRealTimeTaskAsync(lastTrans, () -> player.compensatedWorld.activePistons.add(data));
|
||||
|
||||
+2
-1
@@ -177,10 +177,11 @@ public class BukkitItemResetHandler implements ItemResetHandler {
|
||||
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();
|
||||
method.invoke(getHandle.invoke(player));
|
||||
} catch (IllegalAccessException | InvocationTargetException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
+28
-4
@@ -9,8 +9,11 @@ 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;
|
||||
@@ -27,6 +30,7 @@ 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
|
||||
@@ -34,10 +38,18 @@ public class BukkitPlatformPlayer extends BukkitGrimEntity implements PlatformPl
|
||||
@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
|
||||
@@ -67,12 +79,20 @@ public class BukkitPlatformPlayer extends BukkitGrimEntity implements PlatformPl
|
||||
|
||||
@Override
|
||||
public void sendMessage(String message) {
|
||||
bukkitPlayer.sendMessage(message);
|
||||
if (CommonGrimArguments.USE_CHAT_FAST_BYPASS.value() && user != null) {
|
||||
user.sendMessage(message);
|
||||
} else {
|
||||
bukkitPlayer.sendMessage(message);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessage(Component message) {
|
||||
audiences.player(bukkitPlayer).sendMessage(message);
|
||||
if (CommonGrimArguments.USE_CHAT_FAST_BYPASS.value() && user != null) {
|
||||
user.sendMessage(message);
|
||||
} else {
|
||||
audiences.player(bukkitPlayer).sendMessage(message);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -92,8 +112,12 @@ public class BukkitPlatformPlayer extends BukkitGrimEntity implements PlatformPl
|
||||
|
||||
@Override
|
||||
public Vector3d getPosition() {
|
||||
org.bukkit.Location location = this.bukkitPlayer.getLocation();
|
||||
return new Vector3d(location.getX(), location.getY(), location.getZ());
|
||||
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
|
||||
|
||||
@@ -98,7 +98,7 @@ dependencies {
|
||||
}
|
||||
|
||||
compileOnly(libs.floodgate.api)
|
||||
compileOnly(libs.via.version.api)
|
||||
compileOnly(libs.viaversion)
|
||||
compileOnly(libs.netty)
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ 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.GrimArguments;
|
||||
import ac.grim.grimac.utils.common.arguments.CommonGrimArguments;
|
||||
import ac.grim.grimac.utils.reflection.ReflectionUtils;
|
||||
import lombok.Getter;
|
||||
import org.incendo.cloud.CommandManager;
|
||||
@@ -67,7 +67,7 @@ public final class GrimAPI {
|
||||
|
||||
// the order matters
|
||||
private static Platform detectPlatform() {
|
||||
Platform override = Platform.getByName(GrimArguments.PLATFORM_OVERRIDE);
|
||||
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;
|
||||
|
||||
@@ -96,7 +96,7 @@ public class Check extends GrimProcessor implements AbstractCheck {
|
||||
if (event.isCancelled()) return false;
|
||||
|
||||
player.punishmentManager.handleViolation(this);
|
||||
lastViolationTime = System.currentTimeMillis();
|
||||
lastViolationTime = player.now();
|
||||
violations++;
|
||||
return true;
|
||||
}
|
||||
@@ -169,6 +169,11 @@ public class Check extends GrimProcessor implements AbstractCheck {
|
||||
packetType == PacketType.Play.Client.WINDOW_CONFIRMATION;
|
||||
}
|
||||
|
||||
public static boolean isAsync(PacketTypeCommon packetType) {
|
||||
return packetType == PacketType.Play.Client.KEEP_ALIVE
|
||||
|| packetType == PacketType.Play.Client.CHUNK_BATCH_ACK;
|
||||
}
|
||||
|
||||
public boolean isUpdate(PacketTypeCommon packetType) {
|
||||
return isFlying(packetType)
|
||||
|| packetType == PacketType.Play.Client.CLIENT_TICK_END
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
package ac.grim.grimac.checks.debug;
|
||||
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import com.github.retrooper.packetevents.protocol.player.User;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class DebugGrimPlayer extends GrimPlayer {
|
||||
|
||||
private static final long THRESHOLD_NANO = 200_000_000L; // 200ms
|
||||
private static final long THRESHOLD_MILLIS = 200;
|
||||
|
||||
// Global Offsets: Volatile so all threads see the pause adjustment immediately.
|
||||
// When the Netty thread pauses for 60s, it updates this.
|
||||
// The Scheduler thread sees the update and "stops" the timeout check.
|
||||
private volatile long offsetNano = 0;
|
||||
private volatile long offsetMillis = 0;
|
||||
|
||||
// ThreadLocal: Each thread tracks its OWN execution time.
|
||||
// This prevents the Scheduler Thread (pollData) from resetting the timer
|
||||
// while the Netty Thread is sitting at a breakpoint.
|
||||
private final ThreadLocal<Long> packetStartNano = ThreadLocal.withInitial(System::nanoTime);
|
||||
private final ThreadLocal<Long> packetStartMillis = ThreadLocal.withInitial(System::currentTimeMillis);
|
||||
|
||||
public DebugGrimPlayer(@NotNull User user) {
|
||||
super(user, System.currentTimeMillis(), System.nanoTime());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processingPacket() {
|
||||
// Reset the timer ONLY for the current thread (Netty or Scheduler)
|
||||
this.packetStartNano.set(System.nanoTime());
|
||||
this.packetStartMillis.set(System.currentTimeMillis());
|
||||
}
|
||||
|
||||
@Override
|
||||
public long nano() {
|
||||
// SAFETY CHECK:
|
||||
// If something in the parent constructor calls nano() BEFORE we are fully initialized,
|
||||
// packetStartNano will be null. We must fallback to System time to prevent NPE.
|
||||
if (packetStartNano == null) return System.nanoTime();
|
||||
|
||||
long sysTime = System.nanoTime();
|
||||
|
||||
// 1. Get start time for THIS thread
|
||||
long start = packetStartNano.get();
|
||||
long executionTime = sysTime - start;
|
||||
|
||||
// 2. Check if THIS thread was paused/stuck
|
||||
if (executionTime > THRESHOLD_NANO) {
|
||||
// Update the global offset so ALL threads know to skip this time
|
||||
synchronized (this) {
|
||||
offsetNano += executionTime;
|
||||
}
|
||||
// Reset this thread's timer so we don't loop
|
||||
packetStartNano.set(sysTime);
|
||||
} else if (executionTime < 0) {
|
||||
// Self-healing for rare thread-local init edge cases or system clock jumps
|
||||
packetStartNano.set(sysTime);
|
||||
}
|
||||
|
||||
// 3. Return System Time minus the Global Pause Offset
|
||||
return sysTime - offsetNano;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long now() {
|
||||
if (packetStartMillis == null) return System.currentTimeMillis();
|
||||
|
||||
long sysTime = System.currentTimeMillis();
|
||||
long start = packetStartMillis.get();
|
||||
long executionTime = sysTime - start;
|
||||
|
||||
if (executionTime > THRESHOLD_MILLIS) {
|
||||
synchronized (this) {
|
||||
offsetMillis += executionTime;
|
||||
}
|
||||
packetStartMillis.set(sysTime);
|
||||
}
|
||||
|
||||
return sysTime - offsetMillis;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package ac.grim.grimac.checks.debug;
|
||||
|
||||
import ac.grim.grimac.utils.common.arguments.CommonGrimArguments;
|
||||
|
||||
import java.lang.management.ManagementFactory;
|
||||
|
||||
public class GrimDebugSettings {
|
||||
|
||||
public static boolean shouldEnable() {
|
||||
// 1. If the argument is disabled, never enable debug mode.
|
||||
if (!CommonGrimArguments.DEBUG_BREAKPOINT_MODE.value()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 2. Check for Debugger (Startup Agent OR Dynamic Attach)
|
||||
return isDebuggerAttached();
|
||||
}
|
||||
|
||||
private static boolean isDebuggerAttached() {
|
||||
// Fast Path: Check startup arguments for -agentlib:jdwp
|
||||
if (ManagementFactory.getRuntimeMXBean().getInputArguments().toString().contains("jdwp")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Slow Path (Dynamic Attach): Scan threads for JDWP/Signal Dispatcher
|
||||
try {
|
||||
for (Thread t : Thread.getAllStackTraces().keySet()) {
|
||||
String name = t.getName();
|
||||
if (name.contains("JDWP")) return true;
|
||||
}
|
||||
} catch (Throwable ignored) {}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,7 @@ public class BadPacketsR extends Check implements PacketCheck {
|
||||
public void onPacketReceive(final PacketReceiveEvent event) {
|
||||
if (isTransaction(event.getPacketType()) && player.packetStateData.lastTransactionPacketWasValid) {
|
||||
long ms = (player.getPlayerClockAtLeast() - clock) / 1000000L;
|
||||
long diff = (System.currentTimeMillis() - lastTransTime);
|
||||
long diff = (player.now() - lastTransTime);
|
||||
if (diff > 2000 && ms > 2000) {
|
||||
if (positions == 0 && clock != 0 && player.gamemode != GameMode.SPECTATOR && !player.compensatedEntities.self.isDead) {
|
||||
flag("time=" + ms + "ms, " + "lst=" + diff + "ms, positions=" + positions);
|
||||
@@ -34,7 +34,7 @@ public class BadPacketsR extends Check implements PacketCheck {
|
||||
player.compensatedWorld.removeInvalidPistonLikeStuff(oldTransId);
|
||||
positions = 0;
|
||||
clock = player.getPlayerClockAtLeast();
|
||||
lastTransTime = System.currentTimeMillis();
|
||||
lastTransTime = player.now();
|
||||
oldTransId = player.lastTransactionSent.get();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package ac.grim.grimac.checks.impl.badpackets;
|
||||
|
||||
import ac.grim.grimac.checks.Check;
|
||||
import ac.grim.grimac.checks.CheckData;
|
||||
import ac.grim.grimac.checks.type.PacketCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
|
||||
@CheckData(name = "BadPacketsZ", experimental = true)
|
||||
public class BadPacketsZ extends Check implements PacketCheck {
|
||||
private boolean sent;
|
||||
|
||||
public BadPacketsZ(GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
if (event.getPacketType() == PacketType.Play.Client.CLIENT_TICK_END) {
|
||||
sent = false;
|
||||
}
|
||||
|
||||
if (event.getPacketType() == PacketType.Play.Client.PLAYER_INPUT) {
|
||||
if (sent) {
|
||||
flagAndAlert();
|
||||
}
|
||||
|
||||
sent = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,9 +26,8 @@ public class FarBreak extends Check implements BlockBreakCheck {
|
||||
double min = Double.MAX_VALUE;
|
||||
for (double d : player.getPossibleEyeHeights()) {
|
||||
SimpleCollisionBox box = new SimpleCollisionBox(blockBreak.position);
|
||||
Vector3dm eyes = new Vector3dm(player.x, player.y + d, player.z);
|
||||
Vector3dm best = VectorUtils.cutBoxToVector(eyes, box);
|
||||
min = Math.min(min, eyes.distanceSquared(best));
|
||||
Vector3dm best = VectorUtils.cutBoxToVector(player.x, player.y + d, player.z, box);
|
||||
min = Math.min(min, best.distanceSquared(player.x, player.y + d, player.z));
|
||||
}
|
||||
|
||||
// getPickRange() determines this?
|
||||
|
||||
@@ -7,7 +7,7 @@ import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.anticheat.update.BlockBreak;
|
||||
import ac.grim.grimac.utils.math.GrimMath;
|
||||
import ac.grim.grimac.utils.nmsutil.BlockBreakSpeed;
|
||||
import ac.grim.grimac.utils.reflection.ViaVersionUtil;
|
||||
import ac.grim.grimac.utils.viaversion.ViaVersionUtil;
|
||||
import com.github.retrooper.packetevents.PacketEvents;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
@@ -67,12 +67,12 @@ public class FastBreak extends Check implements BlockBreakCheck {
|
||||
// * can we translate back "up" to server version and run check against server version to avoid loading older registries?
|
||||
WrappedBlockState block = clientOlderThanServer ? WrappedBlockState.getByGlobalId(player.getClientVersion(), player.getViaTranslatedClientBlockID(blockBreak.block.getGlobalId())) : blockBreak.block;
|
||||
|
||||
startBreak = System.currentTimeMillis() - (targetBlockPosition == null ? 50 : 0); // ???
|
||||
startBreak = player.now() - (targetBlockPosition == null ? 50 : 0); // ???
|
||||
targetBlockPosition = blockBreak.position;
|
||||
|
||||
maximumBlockDamage = BlockBreakSpeed.getBlockDamage(player, block);
|
||||
|
||||
double breakDelay = System.currentTimeMillis() - lastFinishBreak;
|
||||
double breakDelay = player.now() - lastFinishBreak;
|
||||
|
||||
if (breakDelay >= 275) { // Reduce buffer if "close enough"
|
||||
blockDelayBalance *= 0.9;
|
||||
@@ -91,7 +91,7 @@ public class FastBreak extends Check implements BlockBreakCheck {
|
||||
|
||||
if (blockBreak.action == DiggingAction.FINISHED_DIGGING && targetBlockPosition != null) {
|
||||
double predictedTime = Math.ceil(1 / maximumBlockDamage) * 50;
|
||||
double realTime = System.currentTimeMillis() - startBreak;
|
||||
double realTime = player.now() - startBreak;
|
||||
double diff = predictedTime - realTime;
|
||||
|
||||
clampBalance();
|
||||
@@ -109,7 +109,7 @@ public class FastBreak extends Check implements BlockBreakCheck {
|
||||
}
|
||||
|
||||
// also set start time because the breaking netcode is fucked on 1.14.4+
|
||||
lastFinishBreak = startBreak = System.currentTimeMillis();
|
||||
lastFinishBreak = startBreak = player.now();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import com.github.retrooper.packetevents.protocol.player.DiggingAction;
|
||||
import com.github.retrooper.packetevents.protocol.world.BlockFace;
|
||||
|
||||
@CheckData(name = "PositionBreakB", experimental = true)
|
||||
@CheckData(name = "PositionBreakB")
|
||||
public class PositionBreakB extends Check implements BlockBreakCheck {
|
||||
private final int releaseFace = player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_8) ? 0 : 255;
|
||||
private BlockFace lastFace;
|
||||
|
||||
@@ -24,11 +24,8 @@ public class ChatB extends Check implements PacketCheck {
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
if (event.getPacketType() == PacketType.Play.Client.CHAT_MESSAGE) {
|
||||
String message = new WrapperPlayClientChatMessage(event).getMessage();
|
||||
if (message.isEmpty() || !message.trim().equals(message) || message.startsWith("/") && PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_19)) {
|
||||
if (flagAndAlert("message=" + message)) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
if (checkChatMessage(message)) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,4 +50,15 @@ public class ChatB extends Check implements PacketCheck {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// returns whether the packet should be cancelled
|
||||
public boolean checkChatMessage(String message) {
|
||||
if (message.isEmpty() || !message.trim().equals(message) || message.startsWith("/") && PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_19)) {
|
||||
if (flagAndAlert("message=" + message) && shouldModifyPackets()) {
|
||||
player.onPacketCancel();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,7 @@ import ac.grim.grimac.checks.type.PacketCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
|
||||
import ac.grim.grimac.utils.data.packetentity.PacketEntity;
|
||||
import ac.grim.grimac.utils.data.packetentity.PacketEntitySizeable;
|
||||
import ac.grim.grimac.utils.data.packetentity.dragon.PacketEntityEnderDragonPart;
|
||||
import ac.grim.grimac.utils.math.Vector3dm;
|
||||
import ac.grim.grimac.utils.nmsutil.ReachUtils;
|
||||
@@ -94,7 +95,10 @@ public class Reach extends Check implements PacketCheck {
|
||||
// TODO: Remove when in front of via
|
||||
if (entity.type == EntityTypes.ARMOR_STAND && player.getClientVersion().isOlderThan(ClientVersion.V_1_8))
|
||||
return;
|
||||
|
||||
//Prevents Happy Ghast Reach false on 1.21.6+ servers with ViaBackwards set up
|
||||
if (entity.type == EntityTypes.HAPPY_GHAST && player.getClientVersion().isOlderThan(ClientVersion.V_1_21_6)) {
|
||||
return;
|
||||
}
|
||||
if (player.gamemode == GameMode.CREATIVE || player.gamemode == GameMode.SPECTATOR)
|
||||
return;
|
||||
if (player.inVehicle()) return;
|
||||
@@ -158,10 +162,16 @@ public class Reach extends Check implements PacketCheck {
|
||||
switch (result.type()) {
|
||||
case REACH -> {
|
||||
String added = ", type=" + reachEntity.type.getName().getKey();
|
||||
if (reachEntity instanceof PacketEntitySizeable sizeable) {
|
||||
added += ", size=" + sizeable.size;
|
||||
}
|
||||
flagAndAlert(result.verbose() + added);
|
||||
}
|
||||
case HITBOX -> {
|
||||
String added = "type=" + reachEntity.type.getName().getKey();
|
||||
if (reachEntity instanceof PacketEntitySizeable sizeable) {
|
||||
added += ", size=" + sizeable.size;
|
||||
}
|
||||
player.checkManager.getCheck(Hitboxes.class).flagAndAlert(result.verbose() + added);
|
||||
}
|
||||
}
|
||||
@@ -222,7 +232,7 @@ public class Reach extends Check implements PacketCheck {
|
||||
for (Vector3dm lookVec : possibleLookDirs) {
|
||||
for (double eye : possibleEyeHeights) {
|
||||
eyePos.setY(from.getY() + eye);
|
||||
Vector3dm endReachPos = eyePos.clone().add(new Vector3dm(lookVec.getX() * distance, lookVec.getY() * distance, lookVec.getZ() * distance));
|
||||
Vector3dm endReachPos = eyePos.clone().add(lookVec.getX() * distance, lookVec.getY() * distance, lookVec.getZ() * distance);
|
||||
|
||||
Vector3dm intercept = ReachUtils.calculateIntercept(targetBox, eyePos, endReachPos).first();
|
||||
|
||||
|
||||
@@ -20,10 +20,9 @@ public class CrashC extends Check implements PacketCheck {
|
||||
WrapperPlayClientPlayerFlying flying = new WrapperPlayClientPlayerFlying(event);
|
||||
if (flying.hasPositionChanged()) {
|
||||
Location pos = flying.getLocation();
|
||||
if (Double.isNaN(pos.getX()) || Double.isNaN(pos.getY()) || Double.isNaN(pos.getZ())
|
||||
|| Double.isInfinite(pos.getX()) || Double.isInfinite(pos.getY()) || Double.isInfinite(pos.getZ()) ||
|
||||
Float.isNaN(pos.getYaw()) || Float.isNaN(pos.getPitch()) ||
|
||||
Float.isInfinite(pos.getYaw()) || Float.isInfinite(pos.getPitch())) {
|
||||
if (!Double.isFinite(pos.getX()) || !Double.isFinite(pos.getY()) || !Double.isFinite(pos.getZ())
|
||||
|| !Float.isFinite(pos.getYaw()) || !Float.isFinite(pos.getPitch())
|
||||
) {
|
||||
flagAndAlert("xyzYP: " + pos.getX() + ", " + pos.getY() + ", " + pos.getZ() + ", " + pos.getYaw() + ", " + pos.getPitch());
|
||||
player.getSetbackTeleportUtil().executeViolationSetback();
|
||||
event.setCancelled(true);
|
||||
|
||||
@@ -10,7 +10,7 @@ import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientEntityAction;
|
||||
|
||||
@CheckData(name = "ElytraA", description = "Started gliding while already gliding", experimental = true)
|
||||
@CheckData(name = "ElytraA", description = "Started gliding while already gliding")
|
||||
public class ElytraA extends Check implements PostPredictionCheck {
|
||||
private boolean setback;
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientEntityAction;
|
||||
|
||||
@CheckData(name = "ElytraB", description = "Started gliding without jumping", experimental = true)
|
||||
@CheckData(name = "ElytraB", description = "Started gliding without jumping")
|
||||
public class ElytraB extends Check implements PostPredictionCheck {
|
||||
private boolean glide;
|
||||
private boolean setback;
|
||||
|
||||
@@ -11,7 +11,7 @@ import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import com.github.retrooper.packetevents.protocol.player.GameMode;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientEntityAction;
|
||||
|
||||
@CheckData(name = "ElytraC", description = "Started gliding too frequently", experimental = true)
|
||||
@CheckData(name = "ElytraC", description = "Started gliding too frequently")
|
||||
public class ElytraC extends Check implements PostPredictionCheck {
|
||||
private boolean glideThisTick, glideLastTick, setback;
|
||||
private int flags;
|
||||
|
||||
@@ -10,7 +10,7 @@ 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 = "ElytraF", description = "Started gliding while on ground", experimental = true)
|
||||
@CheckData(name = "ElytraF", description = "Started gliding while on ground")
|
||||
public class ElytraF extends Check implements PostPredictionCheck {
|
||||
private boolean setback;
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ public class ExploitA extends Check implements PacketCheck {
|
||||
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);
|
||||
check(new PayloadItemName(wrapper.getData()).itemName(), event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ public class ExploitB extends Check implements PacketCheck {
|
||||
}
|
||||
}
|
||||
|
||||
ItemStack newBook = PayloadBookEdit.CODEC.read(wrapper.getData()).itemStack();
|
||||
ItemStack newBook = new PayloadBookEdit(wrapper.getData()).itemStack();
|
||||
checkItemStack(newBook, signing, event);
|
||||
oldBook = null;
|
||||
}
|
||||
@@ -287,13 +287,14 @@ public class ExploitB extends Check implements PacketCheck {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (containsInvalidCharacters(page, true)) {
|
||||
if (flagAndAlert("invalid page characters") && shouldModifyPackets()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// commented out because existing characters are not considered
|
||||
// if (containsInvalidCharacters(page, true)) {
|
||||
// if (flagAndAlert("invalid page characters") && shouldModifyPackets()) {
|
||||
// event.setCancelled(true);
|
||||
// player.onPacketCancel();
|
||||
// }
|
||||
// return true;
|
||||
// }
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -12,12 +12,13 @@ import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.StringJoiner;
|
||||
|
||||
@CheckData(name = "MultiActionsC", description = "Clicked in inventory while moving", experimental = true)
|
||||
@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(", ");
|
||||
|
||||
@@ -7,7 +7,7 @@ import ac.grim.grimac.player.GrimPlayer;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
|
||||
@CheckData(name = "MultiActionsD", description = "Closed inventory while moving", experimental = true)
|
||||
@CheckData(name = "MultiActionsD", description = "Closed inventory while moving")
|
||||
public class MultiActionsD extends Check implements PacketCheck {
|
||||
public MultiActionsD(GrimPlayer player) {
|
||||
super(player);
|
||||
|
||||
@@ -33,7 +33,7 @@ public class MultiActionsE extends Check implements PacketCheck {
|
||||
}
|
||||
}
|
||||
|
||||
if (event.getPacketType() != PacketType.Play.Client.KEEP_ALIVE) {
|
||||
if (!isAsync(event.getPacketType())) {
|
||||
dropping = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ public class PacketOrderB extends Check implements PacketCheck {
|
||||
return; // do not set sentAnimation to false
|
||||
}
|
||||
|
||||
if (event.getPacketType() != PacketType.Play.Client.KEEP_ALIVE) {
|
||||
if (!isAsync(event.getPacketType())) {
|
||||
if (sentAttack && is1_9) {
|
||||
flagAndAlert("post-attack");
|
||||
}
|
||||
|
||||
@@ -29,9 +29,7 @@ public class PacketOrderO extends Check implements PacketCheck {
|
||||
return;
|
||||
}
|
||||
|
||||
if (flying && event.getPacketType() != PacketType.Play.Client.KEEP_ALIVE
|
||||
&& event.getPacketType() != PacketType.Play.Client.VEHICLE_MOVE
|
||||
) {
|
||||
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) {
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -30,9 +30,8 @@ public class FarPlace extends BlockPlaceCheck {
|
||||
final double[] possibleEyeHeights = player.getPossibleEyeHeights();
|
||||
for (double d : possibleEyeHeights) {
|
||||
SimpleCollisionBox box = new SimpleCollisionBox(blockPos);
|
||||
Vector3dm eyes = new Vector3dm(player.x, player.y + d, player.z);
|
||||
Vector3dm best = VectorUtils.cutBoxToVector(eyes, box);
|
||||
min = Math.min(min, eyes.distanceSquared(best));
|
||||
Vector3dm best = VectorUtils.cutBoxToVector(player.x, player.y + d, player.z, box);
|
||||
min = Math.min(min, best.distanceSquared(player.x, player.y + d, player.z));
|
||||
}
|
||||
|
||||
// getPickRange() determines this?
|
||||
|
||||
@@ -12,18 +12,18 @@ public class NegativeTimer extends Timer implements PostPredictionCheck {
|
||||
|
||||
public NegativeTimer(GrimPlayer player) {
|
||||
super(player);
|
||||
timerBalanceRealTime = System.nanoTime() + clockDrift;
|
||||
timerBalanceRealTime = player.nano() + clockDrift;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPredictionComplete(final PredictionComplete predictionComplete) {
|
||||
// We can't negative timer check a 1.9+ player who is standing still.
|
||||
if (player.uncertaintyHandler.lastPointThree.hasOccurredSince(2) || !predictionComplete.isChecked()) {
|
||||
timerBalanceRealTime = System.nanoTime() + clockDrift;
|
||||
timerBalanceRealTime = player.nano() + clockDrift;
|
||||
}
|
||||
|
||||
if (timerBalanceRealTime < lastMovementPlayerClock - clockDrift) {
|
||||
int lostMS = (int) ((System.nanoTime() - timerBalanceRealTime) / 1e6);
|
||||
int lostMS = (int) ((player.nano() - timerBalanceRealTime) / 1e6);
|
||||
flagAndAlertWithSetback("-" + lostMS);
|
||||
timerBalanceRealTime += 50e6;
|
||||
}
|
||||
|
||||
@@ -14,8 +14,8 @@ public class Timer extends Check implements PacketCheck {
|
||||
protected long timerBalanceRealTime = 0;
|
||||
|
||||
// Default value is real time minus max keep-alive time
|
||||
protected long knownPlayerClockTime = (long) (System.nanoTime() - 6e10);
|
||||
protected long lastMovementPlayerClock = (long) (System.nanoTime() - 6e10);
|
||||
protected long knownPlayerClockTime = (long) (player.nano() - 6e10);
|
||||
protected long lastMovementPlayerClock = (long) (player.nano() - 6e10);
|
||||
|
||||
// How long should the player be able to fall back behind their ping? (nanos)
|
||||
// Default: 120 milliseconds
|
||||
@@ -71,7 +71,7 @@ public class Timer extends Check implements PacketCheck {
|
||||
}
|
||||
|
||||
public void doCheck(final PacketReceiveEvent event) {
|
||||
if (timerBalanceRealTime > System.nanoTime()) {
|
||||
if (timerBalanceRealTime > player.nano()) {
|
||||
if (flagAndAlert()) {
|
||||
// Cancel the packet
|
||||
if (shouldModifyPackets()) {
|
||||
|
||||
@@ -19,7 +19,7 @@ public class TimerLimit extends Timer {
|
||||
@Override
|
||||
public void doCheck(final PacketReceiveEvent event) {
|
||||
// 1:1 with Timer minus cancelling the packet
|
||||
if (timerBalanceRealTime > System.nanoTime()) {
|
||||
if (timerBalanceRealTime > player.nano()) {
|
||||
// If timer check already flagged, don't flag.
|
||||
if (!event.isCancelled()) {
|
||||
if (flagAndAlert() && shouldSetback()) {
|
||||
@@ -38,8 +38,8 @@ public class TimerLimit extends Timer {
|
||||
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;
|
||||
if (limitAbuseOverPing != -1 && player.nano() - playerClock > limitAbuseOverPing) {
|
||||
playerClock = player.nano() - limitAbuseOverPing;
|
||||
}
|
||||
timerBalanceRealTime = Math.max(timerBalanceRealTime, playerClock - clockDrift);
|
||||
}
|
||||
|
||||
@@ -91,8 +91,7 @@ public class ExplosionHandler extends Check implements PostPredictionCheck {
|
||||
}
|
||||
|
||||
// Otherwise try and flip/open it.
|
||||
final Object poweredValue = state.getInternalData().get(StateValue.POWERED);
|
||||
final boolean canFlip = (poweredValue != null && !(Boolean) poweredValue) || type == StateTypes.LEVER;
|
||||
final boolean canFlip = state.hasProperty(StateValue.POWERED) && !state.isPowered() || type == StateTypes.LEVER;
|
||||
if (canFlip) {
|
||||
player.compensatedWorld.tickOpenable(record.x, record.y, record.z);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package ac.grim.grimac.checks.impl.velocity;
|
||||
|
||||
import ac.grim.grimac.GrimAPI;
|
||||
import ac.grim.grimac.api.config.ConfigManager;
|
||||
import ac.grim.grimac.checks.Check;
|
||||
import ac.grim.grimac.checks.CheckData;
|
||||
@@ -46,9 +45,6 @@ public class KnockbackHandler extends Check implements PostPredictionCheck {
|
||||
WrapperPlayServerEntityVelocity velocity = new WrapperPlayServerEntityVelocity(event);
|
||||
int entityId = velocity.getEntityId();
|
||||
|
||||
GrimPlayer player = GrimAPI.INSTANCE.getPlayerDataManager().getPlayer(event.getUser());
|
||||
if (player == null) return;
|
||||
|
||||
// Detect whether this knockback packet affects the player or if it is useless
|
||||
// Mojang sends extra useless knockback packets for no apparent reason
|
||||
if (player.compensatedEntities.serverPlayerVehicle != null && entityId != player.compensatedEntities.serverPlayerVehicle) {
|
||||
@@ -69,6 +65,8 @@ public class KnockbackHandler extends Check implements PostPredictionCheck {
|
||||
event.markForReEncode(true);
|
||||
}
|
||||
|
||||
playerVelocity = VectorPrecisionConverter.convert(player.getClientVersion(), playerVelocity);
|
||||
|
||||
// Wrap velocity between two transactions
|
||||
player.sendTransaction();
|
||||
addPlayerKnockback(entityId, player.lastTransactionSent.get(), new Vector3dm(playerVelocity.getX(), playerVelocity.getY(), playerVelocity.getZ()));
|
||||
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
package ac.grim.grimac.checks.impl.velocity;
|
||||
|
||||
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.util.LpVector3d;
|
||||
import com.github.retrooper.packetevents.util.Vector3d;
|
||||
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import lombok.experimental.UtilityClass;
|
||||
|
||||
@UtilityClass
|
||||
public class VectorPrecisionConverter {
|
||||
|
||||
private static final ServerVersion SERVER_VERSION = PacketEvents.getAPI().getServerManager().getVersion();
|
||||
|
||||
public static Vector3d convert(ClientVersion version, Vector3d vector) {
|
||||
if (version.isNewerThanOrEquals(ClientVersion.V_1_21_9) && SERVER_VERSION.isOlderThanOrEquals(ServerVersion.V_1_21_8)) {
|
||||
return VectorPrecisionConverter.legacyToLp(vector);
|
||||
} else if (version.isOlderThanOrEquals(ClientVersion.V_1_21_7) && SERVER_VERSION.isNewerThanOrEquals(ServerVersion.V_1_21_9)) {
|
||||
return VectorPrecisionConverter.lpToLegacy(vector);
|
||||
}
|
||||
|
||||
return vector;
|
||||
}
|
||||
|
||||
public static Vector3d legacyToLp(Vector3d legacy) {
|
||||
PacketWrapper<?> wrapper = PacketWrapper.createUniversalPacketWrapper(Unpooled.buffer());
|
||||
LpVector3d.write(wrapper, legacy);
|
||||
return LpVector3d.read(wrapper);
|
||||
}
|
||||
|
||||
private static final double PRECISION_LOSS_FIX = 1e-11d;
|
||||
|
||||
public static Vector3d lpToLegacy(Vector3d lp) {
|
||||
int xi = (int) (lp.x * 8000d + Math.copySign(PRECISION_LOSS_FIX, lp.x));
|
||||
int yi = (int) (lp.y * 8000d + Math.copySign(PRECISION_LOSS_FIX, lp.y));
|
||||
int zi = (int) (lp.z * 8000d + Math.copySign(PRECISION_LOSS_FIX, lp.z));
|
||||
|
||||
short x = (short) xi;
|
||||
short y = (short) yi;
|
||||
short z = (short) zi;
|
||||
|
||||
return new Vector3d(
|
||||
x / 8000d,
|
||||
y / 8000d,
|
||||
z / 8000d
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -46,12 +46,19 @@ public class GrimDebug implements BuildableCommand {
|
||||
PlayerSelector playerSelector = context.getOrDefault("target", null);
|
||||
|
||||
GrimPlayer targetGrimPlayer = parseTarget(sender, playerSelector == null ? sender : playerSelector.getSinglePlayer());
|
||||
if (targetGrimPlayer == null) return;
|
||||
if (targetGrimPlayer == null) {
|
||||
sender.sendMessage(MessageUtil.getParsedComponent(sender, "player-not-found", "%prefix% &cPlayer is exempt or offline!"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (sender.isConsole()) {
|
||||
targetGrimPlayer.checkManager.getDebugHandler().toggleConsoleOutput();
|
||||
} else if (sender.isPlayer()) {
|
||||
GrimPlayer senderGrimPlayer = GrimAPI.INSTANCE.getPlayerDataManager().getPlayer(sender.getUniqueId());
|
||||
if (senderGrimPlayer == null) {
|
||||
sender.sendMessage(MessageUtil.getParsedComponent(sender, "sender-not-found", "%prefix% &cYou cannot be exempt to use this command!"));
|
||||
return;
|
||||
}
|
||||
targetGrimPlayer.checkManager.getDebugHandler().toggleListener(senderGrimPlayer);
|
||||
} else {
|
||||
sender.sendMessage(MessageUtil.getParsedComponent(sender,
|
||||
|
||||
@@ -7,7 +7,7 @@ import ac.grim.grimac.platform.api.sender.Sender;
|
||||
import ac.grim.grimac.utils.anticheat.MessageUtil;
|
||||
import ac.grim.grimac.utils.common.PropertiesUtil;
|
||||
import ac.grim.grimac.utils.reflection.ReflectionUtils;
|
||||
import ac.grim.grimac.utils.reflection.ViaVersionUtil;
|
||||
import ac.grim.grimac.utils.viaversion.ViaVersionUtil;
|
||||
import com.github.retrooper.packetevents.PacketEvents;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
@@ -51,9 +51,9 @@ public class GrimDump implements BuildableCommand {
|
||||
GrimLog.sendLogAsync(sender, generateDump(), string -> link = string, "text/yaml");
|
||||
}
|
||||
|
||||
public static JsonObject getBasicInfo(String type) {
|
||||
public static JsonObject getDumpInfo() {
|
||||
JsonObject base = new JsonObject();
|
||||
base.addProperty("type", type);
|
||||
base.addProperty("type", "dump");
|
||||
base.addProperty("timestamp", System.currentTimeMillis());
|
||||
// versions
|
||||
JsonObject versions = new JsonObject();
|
||||
@@ -68,6 +68,11 @@ public class GrimDump implements BuildableCommand {
|
||||
if (GrimAPI.INSTANCE.isInitialized()) states.addProperty("platform", GrimAPI.INSTANCE.getPlatform().toString());
|
||||
if (ViaVersionUtil.isAvailable) states.addProperty("has_viaversion", true);
|
||||
if (PAPER) states.addProperty("has_paper", true);
|
||||
// include some relevant settings if not default
|
||||
JsonObject settings = new JsonObject();
|
||||
if (GrimAPI.INSTANCE.getAlertManager().hasConsoleVerboseEnabled()) settings.addProperty("console_verbose", true);
|
||||
if (!GrimAPI.INSTANCE.getAlertManager().hasConsoleAlertsEnabled()) settings.addProperty("console_alerts", false);
|
||||
if (settings.size() > 0) states.add("settings", settings);
|
||||
// system
|
||||
JsonObject system = new JsonObject();
|
||||
base.add("system", system);
|
||||
@@ -75,8 +80,17 @@ public class GrimDump implements BuildableCommand {
|
||||
system.addProperty("java_version", System.getProperty("java.version"));
|
||||
system.addProperty("user_language", System.getProperty("user.language"));
|
||||
// build
|
||||
JsonObject build = new JsonObject();
|
||||
base.add("build", getBuildInfo());
|
||||
// plugins
|
||||
JsonArray plugins = new JsonArray();
|
||||
base.add("plugins", plugins);
|
||||
for (PlatformPlugin plugin : GrimAPI.INSTANCE.getPluginManager().getPlugins()) {
|
||||
JsonObject pluginJson = new JsonObject();
|
||||
pluginJson.addProperty("enabled", plugin.isEnabled());
|
||||
pluginJson.addProperty("name", plugin.getName());
|
||||
pluginJson.addProperty("version", plugin.getVersion());
|
||||
plugins.add(pluginJson);
|
||||
}
|
||||
return base;
|
||||
}
|
||||
|
||||
@@ -98,17 +112,7 @@ public class GrimDump implements BuildableCommand {
|
||||
* @return A JSON-formatted string containing the diagnostic dump.
|
||||
*/
|
||||
private String generateDump() {
|
||||
JsonObject base = getBasicInfo("dump");
|
||||
// plugins
|
||||
JsonArray plugins = new JsonArray();
|
||||
base.add("plugins", plugins);
|
||||
for (PlatformPlugin plugin : GrimAPI.INSTANCE.getPluginManager().getPlugins()) {
|
||||
JsonObject pluginJson = new JsonObject();
|
||||
pluginJson.addProperty("enabled", plugin.isEnabled());
|
||||
pluginJson.addProperty("name", plugin.getName());
|
||||
pluginJson.addProperty("version", plugin.getVersion());
|
||||
plugins.add(pluginJson);
|
||||
}
|
||||
JsonObject base = getDumpInfo();
|
||||
return gson.toJson(base);
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ import ac.grim.grimac.manager.init.start.SuperDebug;
|
||||
import ac.grim.grimac.platform.api.sender.Sender;
|
||||
import ac.grim.grimac.utils.anticheat.LogUtil;
|
||||
import ac.grim.grimac.utils.anticheat.MessageUtil;
|
||||
import ac.grim.grimac.utils.common.GrimArguments;
|
||||
import ac.grim.grimac.utils.common.arguments.CommonGrimArguments;
|
||||
import org.incendo.cloud.Command;
|
||||
import org.incendo.cloud.CommandManager;
|
||||
import org.incendo.cloud.context.CommandContext;
|
||||
@@ -39,7 +39,7 @@ public class GrimLog implements BuildableCommand {
|
||||
}
|
||||
|
||||
private static void sendLog(Sender sender, String log, String success, String failure, Consumer<String> consumer, String type) throws IOException {
|
||||
URL mUrl = new URL(GrimArguments.PASTE_URL + "data/post");
|
||||
URL mUrl = new URL(CommonGrimArguments.PASTE_URL.value() + "data/post");
|
||||
HttpURLConnection urlConn = (HttpURLConnection) mUrl.openConnection();
|
||||
try {
|
||||
urlConn.setDoOutput(true);
|
||||
@@ -53,7 +53,7 @@ public class GrimLog implements BuildableCommand {
|
||||
final int response = urlConn.getResponseCode();
|
||||
if (response == HttpURLConnection.HTTP_CREATED) {
|
||||
String responseURL = urlConn.getHeaderField("Location");
|
||||
String message = success.replace("%url%", GrimArguments.PASTE_URL + responseURL);
|
||||
String message = success.replace("%url%", CommonGrimArguments.PASTE_URL.value() + responseURL);
|
||||
consumer.accept(message);
|
||||
message = MessageUtil.replacePlaceholders(sender, message);
|
||||
sender.sendMessage(MessageUtil.miniMessage(message));
|
||||
|
||||
@@ -5,7 +5,7 @@ import ac.grim.grimac.command.BuildableCommand;
|
||||
import ac.grim.grimac.platform.api.sender.Sender;
|
||||
import ac.grim.grimac.utils.anticheat.LogUtil;
|
||||
import ac.grim.grimac.utils.anticheat.MessageUtil;
|
||||
import ac.grim.grimac.utils.common.GrimArguments;
|
||||
import ac.grim.grimac.utils.common.arguments.CommonGrimArguments;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import lombok.AllArgsConstructor;
|
||||
@@ -56,7 +56,7 @@ public class GrimVersion implements BuildableCommand {
|
||||
try {
|
||||
//
|
||||
HttpRequest request = HttpRequest.newBuilder()
|
||||
.uri(URI.create(GrimArguments.API_URL + "updates"))
|
||||
.uri(URI.create(CommonGrimArguments.API_URL.value() + "updates"))
|
||||
.GET()
|
||||
.header("User-Agent", "GrimAC/" + GrimAPI.INSTANCE.getExternalAPI().getGrimVersion())
|
||||
.header("Content-Type", "application/json")
|
||||
@@ -65,11 +65,11 @@ public class GrimVersion implements BuildableCommand {
|
||||
|
||||
HttpResponse<String> response = HTTP_CLIENT.send(request, HttpResponse.BodyHandlers.ofString());
|
||||
final int statusCode = response.statusCode();
|
||||
if (statusCode != 200) {
|
||||
if (statusCode < 200 || statusCode >= 300) {
|
||||
Component msg = updateMessage.get();
|
||||
sender.sendMessage(Objects.requireNonNullElseGet(msg, () -> Component.text()
|
||||
.append(MessageUtil.miniMessage("%prefix%"))
|
||||
.append(Component.text(" Failed to check latest GrimAC version. Update server responded with code: ")
|
||||
.append(Component.text(" Failed to check latest GrimAC version. Update server responded with status code: ")
|
||||
.color(NamedTextColor.YELLOW))
|
||||
.append(Component.text(statusCode)
|
||||
.color(getColorForStatusCode(statusCode))
|
||||
|
||||
@@ -68,7 +68,7 @@ public class CheckManagerListener extends PacketListenerAbstract {
|
||||
if (Materials.isPlaceableWaterBucket(blockPlace.itemStack.getType()) && PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_13)) {
|
||||
blockPlace.replaceClicked = true; // See what's in the existing place
|
||||
WrappedBlockState existing = blockPlace.getExistingBlockData();
|
||||
if (!(boolean) existing.getInternalData().getOrDefault(StateValue.WATERLOGGED, true)) {
|
||||
if (existing.hasProperty(StateValue.WATERLOGGED) && !existing.isWaterlogged()) {
|
||||
// Strangely, the client does not predict waterlogged placements
|
||||
didPlace = true;
|
||||
}
|
||||
@@ -308,7 +308,7 @@ public class CheckManagerListener extends PacketListenerAbstract {
|
||||
|
||||
if (PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_13)) {
|
||||
WrappedBlockState existing = blockPlace.getExistingBlockData();
|
||||
if (existing.getInternalData().containsKey(StateValue.WATERLOGGED)) { // waterloggable
|
||||
if (existing.hasProperty(StateValue.WATERLOGGED)) { // waterloggable
|
||||
existing.setWaterlogged(false);
|
||||
blockPlace.set(existing);
|
||||
placed = true;
|
||||
@@ -495,7 +495,7 @@ public class CheckManagerListener extends PacketListenerAbstract {
|
||||
|
||||
if (event.getPacketType() == PacketType.Play.Client.PLAYER_BLOCK_PLACEMENT) {
|
||||
WrapperPlayClientPlayerBlockPlacement packet = new WrapperPlayClientPlayerBlockPlacement(event);
|
||||
player.lastBlockPlaceUseItem = System.currentTimeMillis();
|
||||
player.lastBlockPlaceUseItem = player.now();
|
||||
|
||||
ItemStack placedWith = player.inventory.getHeldItem();
|
||||
if (packet.getHand() == InteractionHand.OFF_HAND) {
|
||||
@@ -562,7 +562,7 @@ public class CheckManagerListener extends PacketListenerAbstract {
|
||||
if (event.getPacketType() == PacketType.Play.Client.USE_ITEM) {
|
||||
WrapperPlayClientUseItem packet = new WrapperPlayClientUseItem(event);
|
||||
player.placeUseItemPackets.add(new BlockPlaceSnapshot(packet, player.isSneaking));
|
||||
player.lastBlockPlaceUseItem = System.currentTimeMillis();
|
||||
player.lastBlockPlaceUseItem = player.now();
|
||||
}
|
||||
|
||||
// Call the packet checks last as they can modify the contents of the packet
|
||||
@@ -599,6 +599,10 @@ public class CheckManagerListener extends PacketListenerAbstract {
|
||||
player.latencyUtils.addRealTimeTask(player.lastTransactionSent.get(), () -> player.serverOpenedInventoryThisTick = true);
|
||||
}
|
||||
|
||||
if (event.getPacketType() == PacketType.Play.Server.BUNDLE) {
|
||||
player.packetStateData.sendingBundlePacket = !player.packetStateData.sendingBundlePacket;
|
||||
}
|
||||
|
||||
player.checkManager.onPacketSend(event);
|
||||
}
|
||||
|
||||
@@ -659,7 +663,7 @@ public class CheckManagerListener extends PacketListenerAbstract {
|
||||
}
|
||||
|
||||
private static void handleFlying(GrimPlayer player, double x, double y, double z, float yaw, float pitch, boolean hasPosition, boolean hasLook, boolean onGround, TeleportAcceptData teleportData, PacketReceiveEvent event) {
|
||||
long now = System.currentTimeMillis();
|
||||
long now = player.now();
|
||||
|
||||
if (!hasPosition) {
|
||||
// This may need to be secured later, although nothing that is very important relies on this
|
||||
@@ -755,7 +759,7 @@ public class CheckManagerListener extends PacketListenerAbstract {
|
||||
}
|
||||
|
||||
private static void handleDigging(GrimPlayer player, PacketReceiveEvent event) {
|
||||
player.lastBlockBreak = System.currentTimeMillis();
|
||||
player.lastBlockBreak = player.now();
|
||||
|
||||
final WrapperPlayClientPlayerDigging packet = new WrapperPlayClientPlayerDigging(event);
|
||||
final DiggingAction action = packet.getAction();
|
||||
|
||||
@@ -44,7 +44,7 @@ public class PacketEntityAction extends PacketListenerAbstract {
|
||||
break;
|
||||
case START_FLYING_WITH_ELYTRA:
|
||||
if (player.onGround || player.lastOnGround) {
|
||||
player.getSetbackTeleportUtil().executeForceResync();
|
||||
player.getSetbackTeleportUtil().executeNonSimulatingForceResync();
|
||||
|
||||
if (player.platformPlayer != null) {
|
||||
// Client ignores sneaking, use it to resync
|
||||
@@ -66,7 +66,7 @@ public class PacketEntityAction extends PacketListenerAbstract {
|
||||
player.pointThreeEstimator.updatePlayerGliding();
|
||||
} else {
|
||||
// A client is flying with a ghost elytra, resync
|
||||
player.getSetbackTeleportUtil().executeForceResync();
|
||||
player.getSetbackTeleportUtil().executeNonSimulatingForceResync();
|
||||
if (player.platformPlayer != null) {
|
||||
// Client ignores sneaking, use it to resync
|
||||
player.platformPlayer.setSneaking(!player.platformPlayer.isSneaking());
|
||||
|
||||
@@ -10,7 +10,7 @@ import ac.grim.grimac.utils.data.TrackerData;
|
||||
import ac.grim.grimac.utils.data.packetentity.PacketEntity;
|
||||
import ac.grim.grimac.utils.data.packetentity.PacketEntityHook;
|
||||
import ac.grim.grimac.utils.data.packetentity.PacketEntityTrackXRot;
|
||||
import ac.grim.grimac.utils.reflection.ViaVersionUtil;
|
||||
import ac.grim.grimac.utils.viaversion.ViaVersionUtil;
|
||||
import com.github.retrooper.packetevents.PacketEvents;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.event.PacketSendEvent;
|
||||
|
||||
@@ -72,7 +72,7 @@ public class PacketPingListener extends PacketListenerAbstract {
|
||||
if (id <= 0) {
|
||||
if (player.didWeSendThatTrans.remove(id)) {
|
||||
player.packetStateData.lastServerTransWasValid = true;
|
||||
player.transactionsSent.add(new Pair<>(id, System.nanoTime()));
|
||||
player.transactionsSent.add(new Pair<>(id, player.nano()));
|
||||
player.lastTransactionSent.getAndIncrement();
|
||||
}
|
||||
}
|
||||
@@ -91,7 +91,7 @@ public class PacketPingListener extends PacketListenerAbstract {
|
||||
Short shortID = ((short) id);
|
||||
if (player.didWeSendThatTrans.remove(shortID)) {
|
||||
player.packetStateData.lastServerTransWasValid = true;
|
||||
player.transactionsSent.add(new Pair<>(shortID, System.nanoTime()));
|
||||
player.transactionsSent.add(new Pair<>(shortID, player.nano()));
|
||||
player.lastTransactionSent.getAndIncrement();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,7 +228,7 @@ public class PacketPlayerDigging extends PacketListenerAbstract {
|
||||
|
||||
// do we need to do this with block breaks too?
|
||||
// Prevent issues if the player switches slots, while lagging, standing still, and is placing blocks
|
||||
CheckManagerListener.handleQueuedPlaces(player, false, 0, 0, System.currentTimeMillis());
|
||||
CheckManagerListener.handleQueuedPlaces(player, false, 0, 0, player.now());
|
||||
|
||||
if (player.packetStateData.lastSlotSelected != slot) {
|
||||
if (player.isResetItemUsageOnSlotChange() && GrimAPI.INSTANCE.getItemResetHandler().getItemUsageHand(player.platformPlayer) == InteractionHand.MAIN_HAND) {
|
||||
|
||||
@@ -210,7 +210,9 @@ public class PacketPlayerRespawn extends PacketListenerAbstract {
|
||||
}
|
||||
player.pose = Pose.STANDING;
|
||||
player.clientVelocity = new Vector3dm();
|
||||
player.gamemode = respawn.getGameMode();
|
||||
if (!GrimAPI.INSTANCE.getSpectateManager().isSpectating(player.uuid)) {
|
||||
player.gamemode = respawn.getGameMode();
|
||||
}
|
||||
if (PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_17)) {
|
||||
player.compensatedWorld.setDimension(respawn.getDimensionType(), event.getUser());
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ public class PacketWorldBorder extends Check implements PacketCheck {
|
||||
}
|
||||
|
||||
public double getCurrentDiameter() {
|
||||
double d0 = (double) (System.currentTimeMillis() - startTime) / ((double) endTime - startTime);
|
||||
double d0 = (double) (player.now() - startTime) / ((double) endTime - startTime);
|
||||
return d0 < 1.0D ? GrimMath.lerp(d0, oldDiameter, newDiameter) : newDiameter;
|
||||
}
|
||||
|
||||
@@ -125,7 +125,7 @@ public class PacketWorldBorder extends Check implements PacketCheck {
|
||||
private void setLerp(double oldDiameter, double newDiameter, long length) {
|
||||
this.oldDiameter = oldDiameter;
|
||||
this.newDiameter = newDiameter;
|
||||
this.startTime = System.currentTimeMillis();
|
||||
this.startTime = player.now();
|
||||
this.endTime = this.startTime + length;
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -158,7 +158,7 @@ public class BasePacketWorldReader extends PacketListenerAbstract {
|
||||
Vector3i blockPosition = blockChange.getBlockPosition();
|
||||
// Don't spam transactions (block changes are sent in batches)
|
||||
if (Math.abs(blockPosition.getX() - player.x) < range && Math.abs(blockPosition.getY() - player.y) < range && Math.abs(blockPosition.getZ() - player.z) < range &&
|
||||
player.lastTransSent + 2 < System.currentTimeMillis())
|
||||
player.lastTransSent + 2 < player.now())
|
||||
player.sendTransaction();
|
||||
|
||||
player.latencyUtils.addRealTimeTask(player.lastTransactionSent.get(), () -> player.compensatedWorld.updateBlock(blockPosition.getX(), blockPosition.getY(), blockPosition.getZ(), blockChange.getBlockId()));
|
||||
@@ -172,7 +172,7 @@ public class BasePacketWorldReader extends PacketListenerAbstract {
|
||||
final var blocks = multiBlockChange.getBlocks();
|
||||
for (WrapperPlayServerMultiBlockChange.EncodedBlock blockChange : blocks) {
|
||||
// Don't send a transaction unless it's within 16 blocks of the player
|
||||
if (Math.abs(blockChange.getX() - player.x) < range && Math.abs(blockChange.getY() - player.y) < range && Math.abs(blockChange.getZ() - player.z) < range && player.lastTransSent + 2 < System.currentTimeMillis()) {
|
||||
if (Math.abs(blockChange.getX() - player.x) < range && Math.abs(blockChange.getY() - player.y) < range && Math.abs(blockChange.getZ() - player.z) < range && player.lastTransSent + 2 < player.now()) {
|
||||
player.sendTransaction();
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ public class ActionManager extends Check implements PacketCheck {
|
||||
if (action.getAction() == WrapperPlayClientInteractEntity.InteractAction.ATTACK) {
|
||||
player.totalFlyingPacketsSent = 0;
|
||||
attacking = true;
|
||||
lastAttack = System.currentTimeMillis();
|
||||
lastAttack = player.now();
|
||||
}
|
||||
} else if (isTickPacketIncludingNonMovement(event.getPacketType())) {
|
||||
player.totalFlyingPacketsSent++;
|
||||
@@ -33,6 +33,6 @@ public class ActionManager extends Check implements PacketCheck {
|
||||
}
|
||||
|
||||
public boolean hasAttackedSince(long time) {
|
||||
return System.currentTimeMillis() - lastAttack < time;
|
||||
return player.now() - lastAttack < time;
|
||||
}
|
||||
}
|
||||
@@ -75,8 +75,10 @@ public class CheckManager {
|
||||
private final ClassToInstanceMap<BlockPlaceCheck> blockPlaceChecks;
|
||||
private final ClassToInstanceMap<PostPredictionCheck> postPredictionChecks;
|
||||
private PacketEntityReplication packetEntityReplication = null;
|
||||
private final GrimPlayer player;
|
||||
|
||||
public CheckManager(GrimPlayer player) {
|
||||
this.player = player;
|
||||
packetChecks = new ImmutableClassToInstanceMap.Builder<PacketCheck>()
|
||||
.put(PacketOrderProcessor.class, player.packetOrderProcessor)
|
||||
.put(Reach.class, new Reach(player))
|
||||
@@ -116,6 +118,7 @@ public class CheckManager {
|
||||
.put(BadPacketsU.class, new BadPacketsU(player))
|
||||
.put(BadPacketsV.class, new BadPacketsV(player))
|
||||
.put(BadPacketsY.class, new BadPacketsY(player))
|
||||
.put(BadPacketsZ.class, new BadPacketsZ(player))
|
||||
.put(MultiActionsA.class, new MultiActionsA(player))
|
||||
.put(MultiActionsC.class, new MultiActionsC(player))
|
||||
.put(MultiActionsD.class, new MultiActionsD(player))
|
||||
@@ -124,6 +127,7 @@ public class CheckManager {
|
||||
.put(PacketOrderC.class, new PacketOrderC(player))
|
||||
.put(PacketOrderD.class, new PacketOrderD(player))
|
||||
.put(PacketOrderO.class, new PacketOrderO(player))
|
||||
// .put(PacketOrderP.class, new PacketOrderP(player))
|
||||
.put(SprintA.class, new SprintA(player))
|
||||
.put(VehicleA.class, new VehicleA(player))
|
||||
.put(VehicleB.class, new VehicleB(player))
|
||||
@@ -292,6 +296,8 @@ public class CheckManager {
|
||||
}
|
||||
|
||||
public void onPacketReceive(final PacketReceiveEvent packet) {
|
||||
player.processingPacket();
|
||||
|
||||
for (PacketCheck check : packetChecks.values()) {
|
||||
check.onPacketReceive(packet);
|
||||
}
|
||||
@@ -307,6 +313,8 @@ public class CheckManager {
|
||||
}
|
||||
|
||||
public void onPacketSend(final PacketSendEvent packet) {
|
||||
player.processingPacket();
|
||||
|
||||
for (PacketCheck check : prePredictionChecks.values()) {
|
||||
check.onPacketSend(packet);
|
||||
}
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
package ac.grim.grimac.manager;
|
||||
|
||||
import ac.grim.grimac.GrimAPI;
|
||||
import ac.grim.grimac.api.config.ConfigManager;
|
||||
import ac.grim.grimac.manager.init.ReloadableInitable;
|
||||
import ac.grim.grimac.manager.init.start.StartableInitable;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.anticheat.LogUtil;
|
||||
import ac.grim.grimac.utils.anticheat.MessageUtil;
|
||||
import ac.grim.grimac.utils.webhook.Embed;
|
||||
import ac.grim.grimac.utils.webhook.EmbedField;
|
||||
import ac.grim.grimac.utils.webhook.EmbedFooter;
|
||||
import ac.grim.grimac.utils.webhook.WebhookMessage;
|
||||
import ac.grim.grimac.utils.webhook.*;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.Unmodifiable;
|
||||
|
||||
import java.awt.*;
|
||||
import java.net.URI;
|
||||
@@ -18,7 +20,6 @@ import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentLinkedDeque;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
@@ -26,8 +27,8 @@ import java.util.function.Predicate;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class DiscordManager implements StartableInitable, ReloadableInitable {
|
||||
public static final Predicate<String> WEBHOOK_REGEX = Pattern.compile("https://discord.com/api/webhooks/\\d+/[\\w-]+").asMatchPredicate();
|
||||
public static final Duration timeout = Duration.ofSeconds(15);
|
||||
private static final Predicate<String> WEBHOOK_REGEX = Pattern.compile("https://discord.com/api/webhooks/\\d+/[\\w-]+").asMatchPredicate();
|
||||
private static final Duration timeout = Duration.ofSeconds(15);
|
||||
private static final HttpClient client = HttpClient.newBuilder().connectTimeout(timeout).build();
|
||||
private static final ConcurrentLinkedDeque<HttpRequest> requests = new ConcurrentLinkedDeque<>();
|
||||
private static final AtomicBoolean taskStarted = new AtomicBoolean();
|
||||
@@ -38,6 +39,24 @@ public class DiscordManager implements StartableInitable, ReloadableInitable {
|
||||
private String staticContent = "";
|
||||
private String embedTitle = "";
|
||||
private boolean includeTimestamp;
|
||||
private boolean includeVerbose;
|
||||
private @Nullable String embedImageUrl;
|
||||
private @Nullable String embedThumbnailUrl;
|
||||
private @Nullable String embedFooterUrl;
|
||||
private String embedFooterText = "";
|
||||
|
||||
private static final Pattern URL_PATTERN = Pattern.compile("^https?://(?:www\\.)?[-a-z0-9@:%._+~#=]{1,256}\\.[a-z0-9()]{1,6}\\b[-a-z0-9()@:%_+.~#?&/=]*$", Pattern.CASE_INSENSITIVE);
|
||||
|
||||
private static String validatedConfigURL(String configPath, String defaultURL) {
|
||||
String url = GrimAPI.INSTANCE.getConfigManager().getConfig().getStringElse("embed-image-url", defaultURL);
|
||||
if (url == null || url.isBlank()) return null;
|
||||
if (URL_PATTERN.matcher(url).matches()) {
|
||||
return url;
|
||||
} else {
|
||||
LogUtil.warn("Invalid embed url for config path " + configPath + ": " + configPath);
|
||||
return defaultURL;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
@@ -47,12 +66,13 @@ public class DiscordManager implements StartableInitable, ReloadableInitable {
|
||||
@Override
|
||||
public void reload() {
|
||||
try {
|
||||
if (!GrimAPI.INSTANCE.getConfigManager().getConfig().getBooleanElse("enabled", false)) {
|
||||
ConfigManager config = GrimAPI.INSTANCE.getConfigManager().getConfig();
|
||||
if (!config.getBooleanElse("enabled", false)) {
|
||||
url = null;
|
||||
return;
|
||||
}
|
||||
|
||||
String webhook = GrimAPI.INSTANCE.getConfigManager().getConfig().getStringElse("webhook", "");
|
||||
String webhook = config.getStringElse("webhook", "");
|
||||
|
||||
if (!WEBHOOK_REGEX.test(webhook)) {
|
||||
LogUtil.error("Discord webhook url does not follow expected format (https://discord.com/api/webhooks/<id>/<token>): " + webhook);
|
||||
@@ -60,36 +80,43 @@ public class DiscordManager implements StartableInitable, ReloadableInitable {
|
||||
} else {
|
||||
url = new URI(webhook);
|
||||
}
|
||||
|
||||
embedTitle = GrimAPI.INSTANCE.getConfigManager().getConfig().getStringElse("embed-title", "**Grim Alert**");
|
||||
// not adding these to the config since they may change in the future
|
||||
// mainly for just for allowing more customization
|
||||
embedImageUrl = validatedConfigURL("embed-image-url", null);
|
||||
embedThumbnailUrl = validatedConfigURL("embed-thumbnail-url", "https://crafthead.net/helm/%uuid%");
|
||||
embedFooterUrl = validatedConfigURL("embed-footer-url", "https://grim.ac/images/grim.png");
|
||||
embedFooterText = config.getStringElse("embed-footer-text", "v%grim_version%");
|
||||
embedTitle = config.getStringElse("embed-title", "**Grim Alert**");
|
||||
|
||||
try {
|
||||
embedColor = Color.decode(GrimAPI.INSTANCE.getConfigManager().getConfig().getStringElse("embed-color", "#00FFFF")).getRGB();
|
||||
embedColor = Color.decode(config.getStringElse("embed-color", "#00FFFF")).getRGB();
|
||||
} catch (NumberFormatException e) {
|
||||
LogUtil.warn("Discord embed color is invalid");
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (String string : GrimAPI.INSTANCE.getConfigManager().getConfig().getStringListElse("violation-content", getDefaultContents())) {
|
||||
for (String string : config.getStringListElse("violation-content", getDefaultContents())) {
|
||||
sb.append(string).append("\n");
|
||||
}
|
||||
staticContent = sb.toString();
|
||||
includeTimestamp = GrimAPI.INSTANCE.getConfigManager().getConfig().getBooleanElse("include-timestamp", true);
|
||||
includeTimestamp = config.getBooleanElse("include-timestamp", true);
|
||||
includeVerbose = config.getBooleanElse("include-verbose", true);
|
||||
} catch (Exception e) {
|
||||
LogUtil.error("Failed to load Discord webhook configuration", e);
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> getDefaultContents() {
|
||||
List<String> list = new ArrayList<>();
|
||||
list.add("**Player**: %player%");
|
||||
list.add("**Check**: %check%");
|
||||
list.add("**Violations**: %violations%");
|
||||
list.add("**Client Version**: %version%");
|
||||
list.add("**Brand**: %brand%");
|
||||
list.add("**Ping**: %ping%");
|
||||
list.add("**TPS**: %tps%");
|
||||
return list;
|
||||
@Contract(value = " -> new", pure = true)
|
||||
private @NotNull @Unmodifiable List<@NotNull String> getDefaultContents() {
|
||||
return List.of(
|
||||
"**Player**: %player%",
|
||||
"**Check**: %check%",
|
||||
"**Violations**: %violations%",
|
||||
"**Client Version**: %version%",
|
||||
"**Brand**: %brand%",
|
||||
"**Ping**: %ping%",
|
||||
"**TPS**: %tps%"
|
||||
);
|
||||
}
|
||||
|
||||
public void sendAlert(GrimPlayer player, String verbose, String checkName, int violations) {
|
||||
@@ -103,17 +130,18 @@ public class DiscordManager implements StartableInitable, ReloadableInitable {
|
||||
content = MessageUtil.replacePlaceholders(player, content, true);
|
||||
|
||||
Embed embed = new Embed(content)
|
||||
.imageURL("https://i.stack.imgur.com/Fzh0w.png")
|
||||
.thumbnailURL("https://crafthead.net/helm/" + player.user.getProfile().getUUID())
|
||||
.color(embedColor)
|
||||
.title(embedTitle)
|
||||
.footer(new EmbedFooter("", "https://grim.ac/images/grim.png"));
|
||||
.imageURL(MessageUtil.replacePlaceholders(player, embedImageUrl, false))
|
||||
.thumbnailURL(MessageUtil.replacePlaceholders(player, embedThumbnailUrl, false))
|
||||
.footer(new EmbedFooter(
|
||||
MessageUtil.replacePlaceholders(player, embedFooterText, true),
|
||||
MessageUtil.replacePlaceholders(player, embedFooterUrl, false)
|
||||
));
|
||||
|
||||
if (includeTimestamp) {
|
||||
embed.timestamp(Instant.now());
|
||||
}
|
||||
if (includeTimestamp) embed.timestamp(Instant.now());
|
||||
|
||||
if (!verbose.isEmpty()) {
|
||||
if (!verbose.isEmpty() && includeVerbose) {
|
||||
embed.addFields(new EmbedField("Verbose", MessageUtil.filterDiscordText(verbose), true));
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,8 @@ import ac.grim.grimac.platform.api.player.PlatformPlayer;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.anticheat.LogUtil;
|
||||
import ac.grim.grimac.utils.anticheat.MessageUtil;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||
@@ -175,11 +177,11 @@ public class PunishmentManager implements ConfigReloadable {
|
||||
public void handleViolation(Check check) {
|
||||
for (PunishGroup group : groups) {
|
||||
if (group.checks.contains(check)) {
|
||||
long currentTime = System.currentTimeMillis();
|
||||
long currentTime = player.now();
|
||||
|
||||
group.violations.put(currentTime, check);
|
||||
// Remove violations older than the defined time in the config
|
||||
group.violations.entrySet().removeIf(time -> currentTime - time.getKey() > group.removeViolationsAfter);
|
||||
group.violations.long2ObjectEntrySet().removeIf(time -> currentTime - time.getLongKey() > group.removeViolationsAfter);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -197,7 +199,7 @@ public class PunishmentManager implements ConfigReloadable {
|
||||
class PunishGroup {
|
||||
public final List<AbstractCheck> checks;
|
||||
public final List<ParsedCommand> commands;
|
||||
public final Map<Long, Check> violations = new HashMap<>();
|
||||
public final Long2ObjectMap<Check> violations = new Long2ObjectOpenHashMap<>();
|
||||
public final int removeViolationsAfter; // time to remove violations after in milliseconds
|
||||
}
|
||||
|
||||
|
||||
@@ -100,6 +100,13 @@ public class SetbackTeleportUtil extends Check implements PostPredictionCheck {
|
||||
blockMovementsUntilResync(true, true);
|
||||
}
|
||||
|
||||
public void executeNonSimulatingForceResync() {
|
||||
if (player.gamemode == GameMode.SPECTATOR || player.disableGrim)
|
||||
return; // We don't care about spectators, they don't flag
|
||||
if (lastKnownGoodPosition == null) return; // Player hasn't spawned yet
|
||||
blockMovementsUntilResync(false, true);
|
||||
}
|
||||
|
||||
public void executeNonSimulatingSetback() {
|
||||
if (player.gamemode == GameMode.SPECTATOR || player.disableGrim)
|
||||
return; // We don't care about spectators, they don't flag
|
||||
@@ -131,15 +138,17 @@ public class SetbackTeleportUtil extends Check implements PostPredictionCheck {
|
||||
} else if (player.wasTouchingLava) {
|
||||
vector.multiply(0.5D);
|
||||
if (player.hasGravity)
|
||||
vector.add(new Vector3dm(0.0D, -player.gravity / 4.0D, 0.0D));
|
||||
vector.add(0.0D, -player.gravity / 4.0D, 0.0D);
|
||||
} else if (player.isGliding) {
|
||||
PredictionEngineElytra.getElytraMovement(player, vector, ReachUtils.getLook(player, player.yaw, player.pitch)).multiply(player.stuckSpeedMultiplier).multiply(new Vector3dm(0.99F, 0.98F, 0.99F));
|
||||
PredictionEngineElytra.getElytraMovement(player, vector, ReachUtils.getLook(player, player.yaw, player.pitch)).multiply(player.stuckSpeedMultiplier).multiply(0.99F, 0.98F, 0.99F);
|
||||
vector.setY(vector.getY() - 0.05); // Make the player fall a bit
|
||||
} else { // Gliding doesn't have friction, we handle it differently
|
||||
PredictionEngineNormal.staticVectorEndOfTick(player, vector); // Lava and normal movement
|
||||
vector.multiply(player.stuckSpeedMultiplier); // Prevent abusing setbacks to move out of blocks like webs
|
||||
}
|
||||
|
||||
// Prevent abusing setbacks to move out of blocks like webs
|
||||
vector.multiply(player.stuckSpeedMultiplier);
|
||||
|
||||
// stop 1.8 players from stepping onto 1.25 high blocks, because why not?
|
||||
new PredictionEngine().applyMovementThreshold(player, new HashSet<>(Collections.singletonList(new VectorData(vector, VectorData.VectorType.BestVelPicked))));
|
||||
}
|
||||
@@ -152,9 +161,9 @@ public class SetbackTeleportUtil extends Check implements PostPredictionCheck {
|
||||
if (isPendingSetback()) return; // Don't spam setbacks
|
||||
|
||||
// Only let us full resync once every five seconds to prevent unneeded bukkit load
|
||||
if (System.currentTimeMillis() - lastWorldResync > 5 * 1000) {
|
||||
if (player.now() - lastWorldResync > 5 * 1000) {
|
||||
player.resyncPositions(player.boundingBox.copy().expand(1));
|
||||
lastWorldResync = System.currentTimeMillis();
|
||||
lastWorldResync = player.now();
|
||||
}
|
||||
|
||||
Vector3dm clientVel = lastKnownGoodPosition.vector.clone();
|
||||
@@ -208,7 +217,7 @@ public class SetbackTeleportUtil extends Check implements PostPredictionCheck {
|
||||
blockOffsets = true;
|
||||
}
|
||||
|
||||
SetBackData data = new SetBackData(new TeleportData(position, new Vector3d(), new RelativeFlag(0b11000), player.lastTransactionSent.get(), 0), player.yaw, player.pitch, clientVel, player.inVehicle(), false);
|
||||
SetBackData data = new SetBackData(new TeleportData(position, new Vector3d(), RelativeFlag.YAW.or(RelativeFlag.PITCH), player.lastTransactionSent.get(), 0), player.yaw, player.pitch, clientVel, player.inVehicle(), false);
|
||||
sendSetback(data);
|
||||
}
|
||||
|
||||
@@ -375,7 +384,7 @@ public class SetbackTeleportUtil extends Check implements PostPredictionCheck {
|
||||
|
||||
private boolean isPendingSetback() {
|
||||
// Relative setbacks shouldn't count
|
||||
if (requiredSetBack.getTeleportData().isRelativeX() || requiredSetBack.getTeleportData().isRelativeY() || requiredSetBack.getTeleportData().isRelativeZ()) {
|
||||
if (requiredSetBack != null && (requiredSetBack.getTeleportData().isRelativeX() || requiredSetBack.getTeleportData().isRelativeY() || requiredSetBack.getTeleportData().isRelativeZ())) {
|
||||
return false;
|
||||
}
|
||||
// The setback is not complete
|
||||
|
||||
@@ -2,7 +2,7 @@ package ac.grim.grimac.manager.init.start;
|
||||
|
||||
import ac.grim.grimac.GrimAPI;
|
||||
import ac.grim.grimac.utils.anticheat.LogUtil;
|
||||
import ac.grim.grimac.utils.reflection.ViaVersionUtil;
|
||||
import ac.grim.grimac.utils.viaversion.ViaVersionUtil;
|
||||
import com.github.retrooper.packetevents.PacketEvents;
|
||||
import com.github.retrooper.packetevents.manager.server.ServerVersion;
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ package ac.grim.grimac.manager.init.start;
|
||||
|
||||
import ac.grim.grimac.GrimAPI;
|
||||
import ac.grim.grimac.utils.anticheat.LogUtil;
|
||||
import ac.grim.grimac.utils.reflection.ViaVersionUtil;
|
||||
import ac.grim.grimac.utils.viaversion.ViaVersionUtil;
|
||||
import com.github.retrooper.packetevents.PacketEvents;
|
||||
import com.github.retrooper.packetevents.manager.server.ServerVersion;
|
||||
import com.viaversion.viaversion.api.Via;
|
||||
@@ -12,6 +12,7 @@ public class ViaVersion implements StartableInitable {
|
||||
@Override
|
||||
public void start() {
|
||||
if (!ViaVersionUtil.isAvailable) return;
|
||||
ViaVersionUtil.injectHooks();
|
||||
|
||||
ServerVersion serverVersion = PacketEvents.getAPI().getServerManager().getVersion();
|
||||
|
||||
|
||||
+1
-2
@@ -7,7 +7,6 @@ import ac.grim.grimac.platform.api.world.PlatformWorld;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import com.github.retrooper.packetevents.PacketEvents;
|
||||
import com.github.retrooper.packetevents.manager.server.ServerVersion;
|
||||
import com.github.retrooper.packetevents.util.Vector3d;
|
||||
import com.github.retrooper.packetevents.util.Vector3i;
|
||||
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerAcknowledgeBlockChanges;
|
||||
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerBlockChange;
|
||||
@@ -116,7 +115,7 @@ public class DefaultResyncHandler implements ResyncHandler {
|
||||
GrimAPI.INSTANCE.getScheduler().getRegionScheduler().execute(GrimAPI.INSTANCE.getGrimPlugin(), world, chunkX, chunkZ, () -> {
|
||||
if (!player.platformPlayer.isOnline() || !player.getSetbackTeleportUtil().hasAcceptedSpawnTeleport)
|
||||
return;
|
||||
if (player.platformPlayer.getPosition().distance(new Vector3d(x, y, z)) >= 64)
|
||||
if (player.platformPlayer.distanceSquared(x, y, z) >= 64 * 64)
|
||||
return;
|
||||
if (!world.isChunkLoaded(chunkX, chunkZ)) return; // Don't load chunks sync
|
||||
|
||||
|
||||
+23
@@ -6,6 +6,7 @@ import ac.grim.grimac.api.plugin.GrimPlugin;
|
||||
import ac.grim.grimac.manager.init.ReloadableInitable;
|
||||
import ac.grim.grimac.manager.init.start.StartableInitable;
|
||||
import ac.grim.grimac.manager.violationdatabase.mysql.MySQLViolationDatabase;
|
||||
import ac.grim.grimac.manager.violationdatabase.postgresql.PostgresqlViolationDatabase;
|
||||
import ac.grim.grimac.manager.violationdatabase.sqlite.SQLiteViolationDatabase;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.anticheat.LogUtil;
|
||||
@@ -93,6 +94,28 @@ public class ViolationDatabaseManager implements StartableInitable, ReloadableIn
|
||||
}
|
||||
}
|
||||
|
||||
case "POSTGRESQL" -> {
|
||||
String host = cfg.getStringElse("history.database.host", "localhost:3306");
|
||||
String db = cfg.getStringElse("history.database.database", "grimac");
|
||||
String user = cfg.getStringElse("history.database.username", "root");
|
||||
String pwd = cfg.getStringElse("history.database.password", "password");
|
||||
|
||||
if (database instanceof PostgresqlViolationDatabase postgresql
|
||||
&& postgresql.sameConfig(host, db, user, pwd)) {
|
||||
break; // nothing changed → keep pool
|
||||
}
|
||||
database.disconnect();
|
||||
database = new PostgresqlViolationDatabase(host, db, user, pwd);
|
||||
try {
|
||||
database.connect();
|
||||
loaded = true;
|
||||
} catch (SQLException e) {
|
||||
LogUtil.error(e);
|
||||
this.database = NoOpViolationDatabase.INSTANCE;
|
||||
loaded = false;
|
||||
}
|
||||
}
|
||||
|
||||
default -> { // NOOP or invalid
|
||||
if (!(database instanceof NoOpViolationDatabase)) {
|
||||
database.disconnect();
|
||||
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
package ac.grim.grimac.manager.violationdatabase.postgresql;
|
||||
|
||||
import ac.grim.grimac.manager.violationdatabase.DatabaseDialect;
|
||||
|
||||
public class PostgresqlDialect implements DatabaseDialect {
|
||||
|
||||
@Override
|
||||
public String getUuidColumnType() {
|
||||
return "UUID";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAutoIncrementPrimaryKeySyntax() {
|
||||
return "BIGSERIAL PRIMARY KEY";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getInsertOrIgnoreSyntax(String tableName, String columnNames) {
|
||||
return "INSERT INTO " + tableName + " (" + columnNames + ") VALUES (?) ON CONFLICT DO NOTHING";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUniqueConstraintViolationSQLState() {
|
||||
return "23505"; // Postgresql duplicate key error
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getUniqueConstraintViolationErrorCode() {
|
||||
return 0; // Postgresql is not using numbers
|
||||
}
|
||||
}
|
||||
+286
@@ -0,0 +1,286 @@
|
||||
package ac.grim.grimac.manager.violationdatabase.postgresql;
|
||||
|
||||
import ac.grim.grimac.GrimAPI;
|
||||
import ac.grim.grimac.manager.violationdatabase.*;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.anticheat.LogUtil;
|
||||
import com.github.retrooper.packetevents.PacketEvents;
|
||||
import com.zaxxer.hikari.HikariConfig;
|
||||
import com.zaxxer.hikari.HikariDataSource;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
public class PostgresqlViolationDatabase implements ViolationDatabase {
|
||||
|
||||
private static String quoteVerboseColumn() {
|
||||
return "\"" + DatabaseConstants.VIOLATIONS_VERBOSE_COLUMN + "\"";
|
||||
}
|
||||
|
||||
private HikariDataSource dataSource;
|
||||
private final DatabaseDialect dialect;
|
||||
|
||||
public PostgresqlViolationDatabase(String url, String database, String username, String password) {
|
||||
this.dialect = new PostgresqlDialect();
|
||||
setupDataSource(url, database, username, password);
|
||||
}
|
||||
|
||||
private void setupDataSource(String url, String database, String username, String password) {
|
||||
HikariConfig config = new HikariConfig();
|
||||
config.setJdbcUrl("jdbc:postgresql://" + url + "/" + database);
|
||||
config.setUsername(username);
|
||||
config.setPassword(password);
|
||||
config.setMaximumPoolSize(10);
|
||||
config.setAutoCommit(true);
|
||||
dataSource = new HikariDataSource(config);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connect() throws SQLException {
|
||||
try (Connection connection = dataSource.getConnection()) {
|
||||
String pkSyntax = dialect.getAutoIncrementPrimaryKeySyntax();
|
||||
String uuidType = dialect.getUuidColumnType();
|
||||
|
||||
// 1. Create Lookup Table for Server Names
|
||||
connection.prepareStatement(
|
||||
"CREATE TABLE IF NOT EXISTS " + DatabaseConstants.SERVERS_TABLE + "(" +
|
||||
"id " + pkSyntax + ", " +
|
||||
DatabaseConstants.SERVERS_STRING_COLUMN + " VARCHAR(255) NOT NULL UNIQUE" +
|
||||
")"
|
||||
).execute();
|
||||
connection.prepareStatement(
|
||||
"CREATE INDEX IF NOT EXISTS idx_" + DatabaseConstants.SERVERS_TABLE + "_name ON " + DatabaseConstants.SERVERS_TABLE + "(" + DatabaseConstants.SERVERS_STRING_COLUMN + ");"
|
||||
).execute();
|
||||
|
||||
// 2. Create Lookup Table for Check Names
|
||||
connection.prepareStatement(
|
||||
"CREATE TABLE IF NOT EXISTS " + DatabaseConstants.CHECK_NAMES_TABLE + "(" +
|
||||
"id " + pkSyntax + ", " +
|
||||
DatabaseConstants.CHECK_NAMES_STRING_COLUMN + " VARCHAR(255) NOT NULL UNIQUE" +
|
||||
")"
|
||||
).execute();
|
||||
connection.prepareStatement(
|
||||
"CREATE INDEX IF NOT EXISTS idx_" + DatabaseConstants.CHECK_NAMES_TABLE + "_string ON " + DatabaseConstants.CHECK_NAMES_TABLE + "(" + DatabaseConstants.CHECK_NAMES_STRING_COLUMN + ");"
|
||||
).execute();
|
||||
|
||||
// --- NEW LOOKUP TABLES ---
|
||||
// 3. Create Lookup Table for Grim Versions
|
||||
connection.prepareStatement(
|
||||
"CREATE TABLE IF NOT EXISTS " + DatabaseConstants.GRIM_VERSIONS_TABLE + "(" +
|
||||
"id " + pkSyntax + ", " +
|
||||
DatabaseConstants.GRIM_VERSIONS_STRING_COLUMN + " VARCHAR(255) NOT NULL UNIQUE" +
|
||||
")"
|
||||
).execute();
|
||||
connection.prepareStatement(
|
||||
"CREATE INDEX IF NOT EXISTS idx_" + DatabaseConstants.GRIM_VERSIONS_TABLE + "_string ON " + DatabaseConstants.GRIM_VERSIONS_TABLE + "(" + DatabaseConstants.GRIM_VERSIONS_STRING_COLUMN + ");"
|
||||
).execute();
|
||||
|
||||
// 4. Create Lookup Table for Client Brands
|
||||
connection.prepareStatement(
|
||||
"CREATE TABLE IF NOT EXISTS " + DatabaseConstants.CLIENT_BRANDS_TABLE + "(" +
|
||||
"id " + pkSyntax + ", " +
|
||||
DatabaseConstants.CLIENT_BRANDS_STRING_COLUMN + " VARCHAR(255) NOT NULL UNIQUE" +
|
||||
")"
|
||||
).execute();
|
||||
connection.prepareStatement(
|
||||
"CREATE INDEX IF NOT EXISTS idx_" + DatabaseConstants.CLIENT_BRANDS_TABLE + "_string ON " + DatabaseConstants.CLIENT_BRANDS_TABLE + "(" + DatabaseConstants.CLIENT_BRANDS_STRING_COLUMN + ");"
|
||||
).execute();
|
||||
|
||||
// 5. Create Lookup Table for Client Versions
|
||||
connection.prepareStatement(
|
||||
"CREATE TABLE IF NOT EXISTS " + DatabaseConstants.CLIENT_VERSIONS_TABLE + "(" +
|
||||
"id " + pkSyntax + ", " +
|
||||
DatabaseConstants.CLIENT_VERSIONS_STRING_COLUMN + " VARCHAR(255) NOT NULL UNIQUE" +
|
||||
")"
|
||||
).execute();
|
||||
connection.prepareStatement(
|
||||
"CREATE INDEX IF NOT EXISTS idx_" + DatabaseConstants.CLIENT_VERSIONS_TABLE + "_string ON " + DatabaseConstants.CLIENT_VERSIONS_TABLE + "(" + DatabaseConstants.CLIENT_VERSIONS_STRING_COLUMN + ");"
|
||||
).execute();
|
||||
|
||||
// 6. Create Lookup Table for Server Versions
|
||||
connection.prepareStatement(
|
||||
"CREATE TABLE IF NOT EXISTS " + DatabaseConstants.SERVER_VERSIONS_TABLE + "(" +
|
||||
"id " + pkSyntax + ", " +
|
||||
DatabaseConstants.SERVER_VERSIONS_STRING_COLUMN + " VARCHAR(255) NOT NULL UNIQUE" +
|
||||
")"
|
||||
).execute();
|
||||
connection.prepareStatement(
|
||||
"CREATE INDEX IF NOT EXISTS idx_" + DatabaseConstants.SERVER_VERSIONS_TABLE + "_string ON " + DatabaseConstants.SERVER_VERSIONS_TABLE + "(" + DatabaseConstants.SERVER_VERSIONS_STRING_COLUMN + ");"
|
||||
).execute();
|
||||
// --- END NEW LOOKUP TABLES ---
|
||||
|
||||
// 7. Create Main Violations Table with ALL Foreign Keys and optimized UUID
|
||||
connection.prepareStatement(
|
||||
"CREATE TABLE IF NOT EXISTS " + DatabaseConstants.VIOLATIONS_TABLE + "(" +
|
||||
"id " + pkSyntax + ", " +
|
||||
DatabaseConstants.VIOLATIONS_SERVER_ID_COLUMN + " BIGINT NOT NULL, " +
|
||||
DatabaseConstants.VIOLATIONS_UUID_COLUMN + " " + uuidType + " NOT NULL, " +
|
||||
DatabaseConstants.VIOLATIONS_CHECK_NAME_ID_COLUMN + " BIGINT NOT NULL, " +
|
||||
quoteVerboseColumn() + " TEXT NOT NULL, " +
|
||||
DatabaseConstants.VIOLATIONS_VL_COLUMN + " INT NOT NULL, " +
|
||||
DatabaseConstants.VIOLATIONS_CREATED_AT_COLUMN + " BIGINT NOT NULL, " +
|
||||
DatabaseConstants.VIOLATIONS_GRIM_VERSION_ID_COLUMN + " BIGINT NOT NULL, " + // NEW
|
||||
DatabaseConstants.VIOLATIONS_CLIENT_BRAND_ID_COLUMN + " BIGINT NOT NULL, " + // NEW
|
||||
DatabaseConstants.VIOLATIONS_CLIENT_VERSION_ID_COLUMN + " BIGINT NOT NULL, " + // NEW
|
||||
DatabaseConstants.VIOLATIONS_SERVER_VERSION_ID_COLUMN + " BIGINT NOT NULL, " + // NEW
|
||||
"FOREIGN KEY (" + DatabaseConstants.VIOLATIONS_SERVER_ID_COLUMN + ") REFERENCES " + DatabaseConstants.SERVERS_TABLE + "(id), " +
|
||||
"FOREIGN KEY (" + DatabaseConstants.VIOLATIONS_CHECK_NAME_ID_COLUMN + ") REFERENCES " + DatabaseConstants.CHECK_NAMES_TABLE + "(id), " +
|
||||
"FOREIGN KEY (" + DatabaseConstants.VIOLATIONS_GRIM_VERSION_ID_COLUMN + ") REFERENCES " + DatabaseConstants.GRIM_VERSIONS_TABLE + "(id), " + // NEW
|
||||
"FOREIGN KEY (" + DatabaseConstants.VIOLATIONS_CLIENT_BRAND_ID_COLUMN + ") REFERENCES " + DatabaseConstants.CLIENT_BRANDS_TABLE + "(id), " + // NEW
|
||||
"FOREIGN KEY (" + DatabaseConstants.VIOLATIONS_CLIENT_VERSION_ID_COLUMN + ") REFERENCES " + DatabaseConstants.CLIENT_VERSIONS_TABLE + "(id), " + // NEW
|
||||
"FOREIGN KEY (" + DatabaseConstants.VIOLATIONS_SERVER_VERSION_ID_COLUMN + ") REFERENCES " + DatabaseConstants.SERVER_VERSIONS_TABLE + "(id)" + // NEW
|
||||
")"
|
||||
).execute();
|
||||
|
||||
// 8. Create Indexes for efficient querying on main table (includes new FKs)
|
||||
connection.prepareStatement(
|
||||
"CREATE INDEX IF NOT EXISTS idx_" + DatabaseConstants.VIOLATIONS_TABLE + "_uuid ON " + DatabaseConstants.VIOLATIONS_TABLE + "(" + DatabaseConstants.VIOLATIONS_UUID_COLUMN + ");"
|
||||
).execute();
|
||||
connection.prepareStatement(
|
||||
"CREATE INDEX IF NOT EXISTS idx_" + DatabaseConstants.VIOLATIONS_TABLE + "_created_at ON " + DatabaseConstants.VIOLATIONS_TABLE + "(" + DatabaseConstants.VIOLATIONS_CREATED_AT_COLUMN + ");"
|
||||
).execute();
|
||||
connection.prepareStatement(
|
||||
"CREATE INDEX IF NOT EXISTS idx_" + DatabaseConstants.VIOLATIONS_TABLE + "_server_id ON " + DatabaseConstants.VIOLATIONS_TABLE + "(" + DatabaseConstants.VIOLATIONS_SERVER_ID_COLUMN + ");"
|
||||
).execute();
|
||||
connection.prepareStatement(
|
||||
"CREATE INDEX IF NOT EXISTS idx_" + DatabaseConstants.VIOLATIONS_TABLE + "_check_name_id ON " + DatabaseConstants.VIOLATIONS_TABLE + "(" + DatabaseConstants.VIOLATIONS_CHECK_NAME_ID_COLUMN + ");"
|
||||
).execute();
|
||||
connection.prepareStatement(
|
||||
"CREATE INDEX IF NOT EXISTS idx_" + DatabaseConstants.VIOLATIONS_TABLE + "_grim_version_id ON " + DatabaseConstants.VIOLATIONS_TABLE + "(" + DatabaseConstants.VIOLATIONS_GRIM_VERSION_ID_COLUMN + ");" // NEW
|
||||
).execute();
|
||||
connection.prepareStatement(
|
||||
"CREATE INDEX IF NOT EXISTS idx_" + DatabaseConstants.VIOLATIONS_TABLE + "_client_brand_id ON " + DatabaseConstants.VIOLATIONS_TABLE + "(" + DatabaseConstants.VIOLATIONS_CLIENT_BRAND_ID_COLUMN + ");" // NEW
|
||||
).execute();
|
||||
connection.prepareStatement(
|
||||
"CREATE INDEX IF NOT EXISTS idx_" + DatabaseConstants.VIOLATIONS_TABLE + "_client_version_id ON " + DatabaseConstants.VIOLATIONS_TABLE + "(" + DatabaseConstants.VIOLATIONS_CLIENT_VERSION_ID_COLUMN + ");" // NEW
|
||||
).execute();
|
||||
connection.prepareStatement(
|
||||
"CREATE INDEX IF NOT EXISTS idx_" + DatabaseConstants.VIOLATIONS_TABLE + "_server_version_id ON " + DatabaseConstants.VIOLATIONS_TABLE + "(" + DatabaseConstants.VIOLATIONS_SERVER_VERSION_ID_COLUMN + ");" // NEW
|
||||
).execute();
|
||||
|
||||
} catch (SQLException ex) {
|
||||
LogUtil.error("Failed to generate violations database:", ex);
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
// Updated method signature to accept all new parameters
|
||||
public synchronized void logAlert(GrimPlayer player, String grimVersion, String verbose, String checkName, int vls) {
|
||||
try (Connection connection = dataSource.getConnection();
|
||||
// Updated INSERT statement with all new columns
|
||||
PreparedStatement insertAlert = connection.prepareStatement(
|
||||
"INSERT INTO " + DatabaseConstants.VIOLATIONS_TABLE + " (" +
|
||||
DatabaseConstants.VIOLATIONS_SERVER_ID_COLUMN + ", " +
|
||||
DatabaseConstants.VIOLATIONS_UUID_COLUMN + ", " +
|
||||
DatabaseConstants.VIOLATIONS_CHECK_NAME_ID_COLUMN + ", " +
|
||||
quoteVerboseColumn() + ", " +
|
||||
DatabaseConstants.VIOLATIONS_VL_COLUMN + ", " +
|
||||
DatabaseConstants.VIOLATIONS_CREATED_AT_COLUMN + ", " +
|
||||
DatabaseConstants.VIOLATIONS_GRIM_VERSION_ID_COLUMN + ", " + // NEW
|
||||
DatabaseConstants.VIOLATIONS_CLIENT_BRAND_ID_COLUMN + ", " + // NEW
|
||||
DatabaseConstants.VIOLATIONS_CLIENT_VERSION_ID_COLUMN + ", " + // NEW
|
||||
DatabaseConstants.VIOLATIONS_SERVER_VERSION_ID_COLUMN + // NEW
|
||||
") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" // Total 10 parameters now
|
||||
)
|
||||
) {
|
||||
// Get or create IDs for all deduplicated strings
|
||||
String serverName = GrimAPI.INSTANCE.getConfigManager().getConfig().getStringElse("history.server-name", "Prison");
|
||||
long serverId = DatabaseUtils.getOrCreateId(connection, dialect, DatabaseConstants.SERVERS_TABLE, DatabaseConstants.SERVERS_STRING_COLUMN, serverName);
|
||||
long checkNameId = DatabaseUtils.getOrCreateId(connection, dialect, DatabaseConstants.CHECK_NAMES_TABLE, DatabaseConstants.CHECK_NAMES_STRING_COLUMN, checkName);
|
||||
long grimVersionId = DatabaseUtils.getOrCreateId(connection, dialect, DatabaseConstants.GRIM_VERSIONS_TABLE, DatabaseConstants.GRIM_VERSIONS_STRING_COLUMN, grimVersion);
|
||||
long clientBrandId = DatabaseUtils.getOrCreateId(connection, dialect, DatabaseConstants.CLIENT_BRANDS_TABLE, DatabaseConstants.CLIENT_BRANDS_STRING_COLUMN, player.getBrand());
|
||||
long clientVersionId = DatabaseUtils.getOrCreateId(connection, dialect, DatabaseConstants.CLIENT_VERSIONS_TABLE, DatabaseConstants.CLIENT_VERSIONS_STRING_COLUMN, player.getClientVersion().getReleaseName());
|
||||
long serverVersionId = DatabaseUtils.getOrCreateId(connection, dialect, DatabaseConstants.SERVER_VERSIONS_TABLE, DatabaseConstants.SERVER_VERSIONS_STRING_COLUMN, PacketEvents.getAPI().getServerManager().getVersion().toString());
|
||||
|
||||
// Set parameters for the PreparedStatement
|
||||
insertAlert.setLong(1, serverId);
|
||||
insertAlert.setObject(2, player.getUniqueId());
|
||||
insertAlert.setLong(3, checkNameId);
|
||||
insertAlert.setString(4, verbose);
|
||||
insertAlert.setInt(5, vls);
|
||||
insertAlert.setLong(6, System.currentTimeMillis());
|
||||
insertAlert.setLong(7, grimVersionId); // NEW
|
||||
insertAlert.setLong(8, clientBrandId); // NEW
|
||||
insertAlert.setLong(9, clientVersionId); // NEW
|
||||
insertAlert.setLong(10, serverVersionId); // NEW
|
||||
|
||||
insertAlert.execute();
|
||||
} catch (SQLException ex) {
|
||||
LogUtil.error("Failed to log alert", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized int getLogCount(UUID uuid) {
|
||||
try (Connection connection = dataSource.getConnection();
|
||||
PreparedStatement countLogs = connection.prepareStatement(
|
||||
"SELECT COUNT(*) FROM " + DatabaseConstants.VIOLATIONS_TABLE + " WHERE " + DatabaseConstants.VIOLATIONS_UUID_COLUMN + " = ?"
|
||||
)
|
||||
) {
|
||||
countLogs.setObject(1, uuid);
|
||||
ResultSet result = countLogs.executeQuery();
|
||||
if (result.next()) {
|
||||
return result.getInt(1);
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
LogUtil.error("Failed to count logs", ex);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized List<Violation> getViolations(UUID uuid, int page, int limit) {
|
||||
try (Connection connection = dataSource.getConnection();
|
||||
// Updated SELECT statement with all new joins and column selections
|
||||
PreparedStatement fetchLogs = connection.prepareStatement(
|
||||
"SELECT " +
|
||||
"v." + DatabaseConstants.VIOLATIONS_ID_COLUMN + ", " +
|
||||
"s." + DatabaseConstants.SERVERS_STRING_COLUMN + ", " +
|
||||
"v." + DatabaseConstants.VIOLATIONS_UUID_COLUMN + ", " +
|
||||
"cn." + DatabaseConstants.CHECK_NAMES_STRING_COLUMN + ", " +
|
||||
"v." + quoteVerboseColumn() + ", " +
|
||||
"v." + DatabaseConstants.VIOLATIONS_VL_COLUMN + ", " +
|
||||
"v." + DatabaseConstants.VIOLATIONS_CREATED_AT_COLUMN + ", " +
|
||||
"gv." + DatabaseConstants.GRIM_VERSIONS_STRING_COLUMN + ", " + // NEW
|
||||
"cb." + DatabaseConstants.CLIENT_BRANDS_STRING_COLUMN + ", " + // NEW
|
||||
"clv." + DatabaseConstants.CLIENT_VERSIONS_STRING_COLUMN + ", " + // NEW
|
||||
"srv." + DatabaseConstants.SERVER_VERSIONS_STRING_COLUMN + " " + // NEW
|
||||
"FROM " + DatabaseConstants.VIOLATIONS_TABLE + " v " +
|
||||
"JOIN " + DatabaseConstants.SERVERS_TABLE + " s ON v." + DatabaseConstants.VIOLATIONS_SERVER_ID_COLUMN + " = s.id " +
|
||||
"JOIN " + DatabaseConstants.CHECK_NAMES_TABLE + " cn ON v." + DatabaseConstants.VIOLATIONS_CHECK_NAME_ID_COLUMN + " = cn.id " +
|
||||
"JOIN " + DatabaseConstants.GRIM_VERSIONS_TABLE + " gv ON v." + DatabaseConstants.VIOLATIONS_GRIM_VERSION_ID_COLUMN + " = gv.id " + // NEW
|
||||
"JOIN " + DatabaseConstants.CLIENT_BRANDS_TABLE + " cb ON v." + DatabaseConstants.VIOLATIONS_CLIENT_BRAND_ID_COLUMN + " = cb.id " + // NEW
|
||||
"JOIN " + DatabaseConstants.CLIENT_VERSIONS_TABLE + " clv ON v." + DatabaseConstants.VIOLATIONS_CLIENT_VERSION_ID_COLUMN + " = clv.id " + // NEW
|
||||
"JOIN " + DatabaseConstants.SERVER_VERSIONS_TABLE + " srv ON v." + DatabaseConstants.VIOLATIONS_SERVER_VERSION_ID_COLUMN + " = srv.id " + // NEW
|
||||
"WHERE v." + DatabaseConstants.VIOLATIONS_UUID_COLUMN + " = ? ORDER BY v." + DatabaseConstants.VIOLATIONS_CREATED_AT_COLUMN + " DESC LIMIT ? OFFSET ?"
|
||||
)
|
||||
) {
|
||||
fetchLogs.setObject(1, uuid);
|
||||
fetchLogs.setInt(2, limit);
|
||||
fetchLogs.setInt(3, Math.max(0, (page - 1)) * limit); // postgresql is not allowing negative numbers
|
||||
return Violation.fromResultSet(fetchLogs.executeQuery());
|
||||
} catch (SQLException ex) {
|
||||
LogUtil.error("Failed to fetch logs", ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disconnect() {
|
||||
if (dataSource != null && !dataSource.isClosed()) {
|
||||
dataSource.close();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean sameConfig(String host, String db, String user, String pwd) {
|
||||
String wantUrl = "jdbc:postgresql://" + host + "/" + db;
|
||||
return wantUrl.equalsIgnoreCase(dataSource.getJdbcUrl())
|
||||
&& user.equals(dataSource.getUsername())
|
||||
&& pwd.equals(dataSource.getPassword());
|
||||
}
|
||||
}
|
||||
@@ -25,4 +25,6 @@ public interface GrimEntity extends GrimIdentity {
|
||||
PlatformWorld getWorld();
|
||||
|
||||
Location getLocation();
|
||||
|
||||
double distanceSquared(double x, double y, double z);
|
||||
}
|
||||
@@ -39,7 +39,7 @@ import ac.grim.grimac.utils.nmsutil.BlockProperties;
|
||||
import ac.grim.grimac.utils.nmsutil.Collisions;
|
||||
import ac.grim.grimac.utils.nmsutil.GetBoundingBox;
|
||||
import ac.grim.grimac.utils.nmsutil.Materials;
|
||||
import ac.grim.grimac.utils.reflection.ViaVersionUtil;
|
||||
import ac.grim.grimac.utils.viaversion.ViaVersionUtil;
|
||||
import com.github.retrooper.packetevents.PacketEvents;
|
||||
import com.github.retrooper.packetevents.event.PacketSendEvent;
|
||||
import com.github.retrooper.packetevents.manager.server.ServerVersion;
|
||||
@@ -120,13 +120,13 @@ public class GrimPlayer implements GrimUser {
|
||||
public long lastTransSent = 0;
|
||||
public long lastTransReceived = 0;
|
||||
@Getter
|
||||
private long playerClockAtLeast = System.nanoTime();
|
||||
private long playerClockAtLeast;
|
||||
public double lastWasClimbing = 0;
|
||||
public boolean canSwimHop = false;
|
||||
public int riptideSpinAttackTicks = 0;
|
||||
public int powderSnowFrozenTicks = 0;
|
||||
public boolean hasGravity = true;
|
||||
public final long joinTime = System.currentTimeMillis();
|
||||
public final long joinTime;
|
||||
public boolean playerEntityHasGravity = true;
|
||||
public VectorData predictedVelocity = new VectorData(new Vector3dm(), VectorData.VectorType.Normal);
|
||||
public Vector3dm actualMovement = new Vector3dm();
|
||||
@@ -272,6 +272,12 @@ public class GrimPlayer implements GrimUser {
|
||||
public boolean lastJumping;
|
||||
|
||||
public GrimPlayer(@NotNull User user) {
|
||||
this(user, System.currentTimeMillis(), System.nanoTime());
|
||||
}
|
||||
|
||||
protected GrimPlayer(@NotNull User user, long joinTime, long clockStart) {
|
||||
this.joinTime = joinTime;
|
||||
this.playerClockAtLeast = clockStart;
|
||||
this.user = user;
|
||||
this.uuid = user.getUUID();
|
||||
fireworks = new CompensatedFireworks(this); // Must be before checkmanager
|
||||
@@ -397,7 +403,7 @@ public class GrimPlayer implements GrimUser {
|
||||
// Transactions that we send don't count towards total limit
|
||||
if (viaPacketTracker != null) viaPacketTracker.setIntervalPackets(viaPacketTracker.getIntervalPackets() - 1);
|
||||
|
||||
if (skipped > 0 && System.currentTimeMillis() - joinTime > 5000)
|
||||
if (skipped > 0 && now() - joinTime > 5000)
|
||||
checkManager.getCheck(TransactionOrder.class).flagAndAlert("skipped: " + skipped);
|
||||
|
||||
do {
|
||||
@@ -406,14 +412,14 @@ public class GrimPlayer implements GrimUser {
|
||||
break;
|
||||
|
||||
lastTransactionReceived.incrementAndGet();
|
||||
lastTransReceived = System.currentTimeMillis();
|
||||
transactionPing = (System.nanoTime() - data.second());
|
||||
lastTransReceived = now();
|
||||
transactionPing = (this.nano() - data.second());
|
||||
playerClockAtLeast = data.second();
|
||||
} while (data.first() != id);
|
||||
|
||||
// A transaction means a new tick, so handle any block interactions
|
||||
CheckManagerListener.handleQueuedPlaces(this, false, 0, 0, System.currentTimeMillis());
|
||||
CheckManagerListener.handleQueuedBreaks(this, false, 0, 0, System.currentTimeMillis());
|
||||
CheckManagerListener.handleQueuedPlaces(this, false, 0, 0, now());
|
||||
CheckManagerListener.handleQueuedBreaks(this, false, 0, 0, now());
|
||||
latencyUtils.handleNettySyncTransaction(lastTransactionReceived.get());
|
||||
}
|
||||
|
||||
@@ -461,11 +467,11 @@ public class GrimPlayer implements GrimUser {
|
||||
if (user.getEncoderState() != ConnectionState.PLAY) return;
|
||||
|
||||
// Send a packet once every 15 seconds to avoid any memory leaks
|
||||
if (disableGrim && (System.nanoTime() - getPlayerClockAtLeast()) > 15e9) {
|
||||
if (disableGrim && (this.nano() - getPlayerClockAtLeast()) > 15e9) {
|
||||
return;
|
||||
}
|
||||
|
||||
lastTransSent = System.currentTimeMillis();
|
||||
lastTransSent = now();
|
||||
short transactionID = (short) (-1 * (transactionIDCounter.getAndIncrement() & 0x7FFF));
|
||||
try {
|
||||
|
||||
@@ -524,15 +530,20 @@ public class GrimPlayer implements GrimUser {
|
||||
}
|
||||
|
||||
public void pollData() {
|
||||
// Reset the debug timer for the Scheduler Thread.
|
||||
// In Production: This is a no-op (0 cost).
|
||||
// In Debug: This tells the clock "I am starting a new tick, don't freeze time."
|
||||
this.processingPacket();
|
||||
|
||||
// Send a transaction at least once a tick, for timer and post check purposes
|
||||
// Don't be the first to send the transaction, or we will stack overflow
|
||||
//
|
||||
// This will only really activate if there's no entities around the player being tracked
|
||||
// 80 is a magic value that is roughly every other tick, we don't want to spam too many packets.
|
||||
if (lastTransSent != 0 && lastTransSent + 80 < System.currentTimeMillis()) {
|
||||
if (lastTransSent != 0 && lastTransSent + 80 < now()) {
|
||||
sendTransaction(true); // send on netty thread
|
||||
}
|
||||
if ((System.nanoTime() - getPlayerClockAtLeast()) > maxTransactionTime * 1e9) {
|
||||
if ((this.nano() - getPlayerClockAtLeast()) > maxTransactionTime * 1e9) {
|
||||
timedOut();
|
||||
}
|
||||
|
||||
@@ -942,14 +953,24 @@ public class GrimPlayer implements GrimUser {
|
||||
if (this.movementThisTick.size() >= 100) {
|
||||
GrimPlayer.Movement movement1 = this.movementThisTick.removeFirst();
|
||||
GrimPlayer.Movement movement2 = this.movementThisTick.removeFirst();
|
||||
GrimPlayer.Movement movement3 = new GrimPlayer.Movement(movement1.from(), movement2.to(), false);
|
||||
GrimPlayer.Movement movement3 = new GrimPlayer.Movement(movement1.from(), movement2.to());
|
||||
this.movementThisTick.addFirst(movement3);
|
||||
}
|
||||
|
||||
this.movementThisTick.add(movement);
|
||||
}
|
||||
|
||||
public record Movement(Vector3d from, Vector3d to, boolean axisIndependant) {}
|
||||
public record Movement(Vector3d from, Vector3d to, Vector3d axisDependentOriginalMovement) {
|
||||
|
||||
public Movement(Vector3d from, Vector3d to) {
|
||||
this(from, to, null);
|
||||
}
|
||||
|
||||
public boolean axisIndependant() {
|
||||
return axisDependentOriginalMovement != null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// TODO (Cross-platform) keep track of world at packet level; do not rely on potentially non-lag-compensated platformPlayer.getWorld()
|
||||
public Location getLocation() {
|
||||
@@ -978,4 +999,27 @@ public class GrimPlayer implements GrimUser {
|
||||
|
||||
return blockStateId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current high-resolution time in nanoseconds.
|
||||
* In Debug Mode, this will subtract time spent paused at breakpoints.
|
||||
*/
|
||||
public long nano() {
|
||||
return System.nanoTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current wall-clock time in milliseconds.
|
||||
* In Debug Mode, this will subtract time spent paused at breakpoints.
|
||||
*/
|
||||
public long now() {
|
||||
return System.currentTimeMillis();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called immediately when a packet starts processing.
|
||||
* Used by DebugGrimPlayer to differentiate "Lag" (Pre-Packet) from "Breakpoints" (Intra-Packet).
|
||||
* Optimized away (Dead Code Elimination) in production.
|
||||
*/
|
||||
public void processingPacket() {}
|
||||
}
|
||||
@@ -70,13 +70,14 @@ public class MovementCheckRunner extends Check implements PositionCheck {
|
||||
|
||||
if (!invalidVehicle && !data.isTeleport()) {
|
||||
// Teleport the player back to avoid players being able to simply ignore transactions
|
||||
player.getSetbackTeleportUtil().executeForceResync();
|
||||
// We shouldn't simulate movement in unloaded chunks
|
||||
player.getSetbackTeleportUtil().executeNonSimulatingForceResync();
|
||||
}
|
||||
}
|
||||
|
||||
long start = System.nanoTime();
|
||||
long start = player.nano();
|
||||
check(data);
|
||||
long length = System.nanoTime() - start;
|
||||
long length = player.nano() - start;
|
||||
|
||||
if (!player.disableGrim) {
|
||||
predictionNanos = (predictionNanos * 499 / 500d) + (length / 500d);
|
||||
@@ -196,7 +197,6 @@ public class MovementCheckRunner extends Check implements PositionCheck {
|
||||
player.vehicleData.wasVehicleSwitch = false;
|
||||
|
||||
if (riding != null) {
|
||||
Vector3dm pos = new Vector3dm(player.x, player.y, player.z);
|
||||
SimpleCollisionBox interTruePositions = riding.getPossibleCollisionBoxes();
|
||||
|
||||
// We shrink the expanded bounding box to what the packet positions can be, for a smaller box
|
||||
@@ -206,7 +206,7 @@ public class MovementCheckRunner extends Check implements PositionCheck {
|
||||
interTruePositions.expand(-width, 0, -width);
|
||||
interTruePositions.expandMax(0, -height, 0);
|
||||
|
||||
Vector3dm cutTo = VectorUtils.cutBoxToVector(pos, interTruePositions);
|
||||
Vector3dm cutTo = VectorUtils.cutBoxToVector(player.x, player.y, player.z, interTruePositions);
|
||||
|
||||
// Now we need to simulate a tick starting at the most optimal position
|
||||
// The start position is never sent, so we assume the most optimal start position
|
||||
@@ -352,7 +352,7 @@ public class MovementCheckRunner extends Check implements PositionCheck {
|
||||
|
||||
boolean clientClaimsRiptide = player.packetStateData.tryingToRiptide;
|
||||
if (player.packetStateData.tryingToRiptide) {
|
||||
long currentTime = System.currentTimeMillis();
|
||||
long currentTime = player.now();
|
||||
boolean isInWater = player.isInWaterOrRain();
|
||||
|
||||
if (currentTime - player.packetStateData.lastRiptide < 450 || !isInWater) {
|
||||
@@ -567,7 +567,7 @@ public class MovementCheckRunner extends Check implements PositionCheck {
|
||||
// Patch sprint jumping with elytra exploit
|
||||
if (player.platformPlayer != null && player.isGliding && player.predictedVelocity.isJump() && player.isSprinting && !allowSprintJumpingWithElytra) {
|
||||
SetbackTeleportUtil.SetbackPosWithVector lastKnownGoodPosition = player.getSetbackTeleportUtil().lastKnownGoodPosition;
|
||||
lastKnownGoodPosition.setVector(lastKnownGoodPosition.getVector().multiply(new Vector3dm(0.6 * 0.91, 1, 0.6 * 0.91)));
|
||||
lastKnownGoodPosition.setVector(lastKnownGoodPosition.getVector().multiply(0.6 * 0.91, 1, 0.6 * 0.91));
|
||||
player.getSetbackTeleportUtil().executeNonSimulatingSetback();
|
||||
}
|
||||
|
||||
|
||||
@@ -157,12 +157,12 @@ public class UncertaintyHandler {
|
||||
Vector3dm maxLocation = new Vector3dm(entityBox.maxX, entityBox.maxY, entityBox.maxZ);
|
||||
Vector3dm minLocation = new Vector3dm(entityBox.minX, entityBox.minY, entityBox.minZ);
|
||||
|
||||
Vector3dm diff = minLocation.subtract(new Vector3dm(player.lastX, player.lastY + 0.8 * 1.8, player.lastZ)).multiply(0.1);
|
||||
Vector3dm diff = minLocation.subtract(player.lastX, player.lastY + 0.8 * 1.8, player.lastZ).multiply(0.1);
|
||||
fishingRodPullBox.minX = Math.min(0, diff.getX());
|
||||
fishingRodPullBox.minY = Math.min(0, diff.getY());
|
||||
fishingRodPullBox.minZ = Math.min(0, diff.getZ());
|
||||
|
||||
diff = maxLocation.subtract(new Vector3dm(player.lastX, player.lastY + 0.8 * 1.8, player.lastZ)).multiply(0.1);
|
||||
diff = maxLocation.subtract(player.lastX, player.lastY + 0.8 * 1.8, player.lastZ).multiply(0.1);
|
||||
fishingRodPullBox.maxX = Math.max(0, diff.getX());
|
||||
fishingRodPullBox.maxY = Math.max(0, diff.getY());
|
||||
fishingRodPullBox.maxZ = Math.max(0, diff.getZ());
|
||||
|
||||
+127
@@ -0,0 +1,127 @@
|
||||
package ac.grim.grimac.predictionengine.blockeffects;
|
||||
|
||||
import ac.grim.grimac.utils.nmsutil.Collisions;
|
||||
import com.github.retrooper.packetevents.protocol.world.Direction;
|
||||
import com.github.retrooper.packetevents.util.Vector3d;
|
||||
import com.github.retrooper.packetevents.util.Vector3i;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import lombok.experimental.UtilityClass;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@UtilityClass
|
||||
public class BlockCollisions {
|
||||
|
||||
public static final Vector3d X_AXIS = new Vector3d(1.0, 0.0, 0.0);
|
||||
public static final Vector3d Y_AXIS = new Vector3d(0.0, 1.0, 0.0);
|
||||
public static final Vector3d Z_AXIS = new Vector3d(0.0, 0.0, 1.0);
|
||||
|
||||
public static Vector3i getFurthestCorner(Vector3d vector) {
|
||||
double xDot = Math.abs(X_AXIS.dot(vector));
|
||||
double yDot = Math.abs(Y_AXIS.dot(vector));
|
||||
double zDot = Math.abs(Z_AXIS.dot(vector));
|
||||
int xSign = vector.x >= 0.0 ? 1 : -1;
|
||||
int ySign = vector.y >= 0.0 ? 1 : -1;
|
||||
int zSign = vector.z >= 0.0 ? 1 : -1;
|
||||
if (xDot <= yDot && xDot <= zDot) {
|
||||
return new Vector3i(-xSign, -zSign, ySign);
|
||||
} else {
|
||||
return yDot <= zDot ? new Vector3i(zSign, -ySign, -xSign) : new Vector3i(-ySign, xSign, -zSign);
|
||||
}
|
||||
}
|
||||
|
||||
public static Optional<Vector3d> clip(double minX, double minY, double minZ, double maxX, double maxY, double maxZ, Vector3d start, Vector3d end) {
|
||||
double[] minDistance = new double[]{1.0};
|
||||
double deltaX = end.x - start.x;
|
||||
double deltaY = end.y - start.y;
|
||||
double deltaZ = end.z - start.z;
|
||||
Direction direction = getDirection(minX, minY, minZ, maxX, maxY, maxZ, start, minDistance, null, deltaX, deltaY, deltaZ);
|
||||
if (direction == null) {
|
||||
return Optional.empty();
|
||||
} else {
|
||||
double distance = minDistance[0];
|
||||
return Optional.of(start.add(distance * deltaX, distance * deltaY, distance * deltaZ));
|
||||
}
|
||||
}
|
||||
|
||||
private static Direction getDirection(
|
||||
double minX,
|
||||
double minY,
|
||||
double minZ,
|
||||
double maxX,
|
||||
double maxY,
|
||||
double maxZ,
|
||||
Vector3d start,
|
||||
double[] minDistance,
|
||||
Direction facing,
|
||||
double deltaX,
|
||||
double deltaY,
|
||||
double deltaZ
|
||||
) {
|
||||
if (deltaX > Collisions.COLLISION_EPSILON) {
|
||||
facing = clipPoint(minDistance, facing, deltaX, deltaY, deltaZ, minX, minY, maxY, minZ, maxZ, Direction.WEST, start.x, start.y, start.z);
|
||||
} else if (deltaX < -Collisions.COLLISION_EPSILON) {
|
||||
facing = clipPoint(minDistance, facing, deltaX, deltaY, deltaZ, maxX, minY, maxY, minZ, maxZ, Direction.EAST, start.x, start.y, start.z);
|
||||
}
|
||||
|
||||
if (deltaY > Collisions.COLLISION_EPSILON) {
|
||||
facing = clipPoint(minDistance, facing, deltaY, deltaZ, deltaX, minY, minZ, maxZ, minX, maxX, Direction.DOWN, start.y, start.z, start.x);
|
||||
} else if (deltaY < -Collisions.COLLISION_EPSILON) {
|
||||
facing = clipPoint(minDistance, facing, deltaY, deltaZ, deltaX, maxY, minZ, maxZ, minX, maxX, Direction.UP, start.y, start.z, start.x);
|
||||
}
|
||||
|
||||
if (deltaZ > Collisions.COLLISION_EPSILON) {
|
||||
facing = clipPoint(minDistance, facing, deltaZ, deltaX, deltaY, minZ, minX, maxX, minY, maxY, Direction.NORTH, start.z, start.x, start.y);
|
||||
} else if (deltaZ < -Collisions.COLLISION_EPSILON) {
|
||||
facing = clipPoint(minDistance, facing, deltaZ, deltaX, deltaY, maxZ, minX, maxX, minY, maxY, Direction.SOUTH, start.z, start.x, start.y);
|
||||
}
|
||||
|
||||
return facing;
|
||||
}
|
||||
|
||||
public static Direction clipPoint(
|
||||
double[] minDistance,
|
||||
Direction prevDirection,
|
||||
double distanceSide,
|
||||
double distanceOtherA,
|
||||
double distanceOtherB,
|
||||
double minSide,
|
||||
double minOtherA,
|
||||
double maxOtherA,
|
||||
double minOtherB,
|
||||
double maxOtherB,
|
||||
Direction hitSide,
|
||||
double startSide,
|
||||
double startOtherA,
|
||||
double startOtherB
|
||||
) {
|
||||
double sideDistance = (minSide - startSide) / distanceSide;
|
||||
double otherDistanceA = startOtherA + sideDistance * distanceOtherA;
|
||||
double otherDistanceB = startOtherB + sideDistance * distanceOtherB;
|
||||
if (sideDistance > 0.0 && sideDistance < minDistance[0]
|
||||
&& minOtherA - Collisions.COLLISION_EPSILON < otherDistanceA
|
||||
&& otherDistanceA < maxOtherA + Collisions.COLLISION_EPSILON
|
||||
&& minOtherB - Collisions.COLLISION_EPSILON < otherDistanceB
|
||||
&& otherDistanceB < maxOtherB + Collisions.COLLISION_EPSILON) {
|
||||
minDistance[0] = sideDistance;
|
||||
return hitSide;
|
||||
} else {
|
||||
return prevDirection;
|
||||
}
|
||||
}
|
||||
|
||||
public static final ImmutableList<Collisions.Axis> YXZ_AXIS_ORDER = ImmutableList.of(Collisions.Axis.Y, Collisions.Axis.X, Collisions.Axis.Z);
|
||||
public static final ImmutableList<Collisions.Axis> YZX_AXIS_ORDER = ImmutableList.of(Collisions.Axis.Y, Collisions.Axis.Z, Collisions.Axis.X);
|
||||
|
||||
public static ImmutableList<Collisions.Axis> axisStepOrder(Vector3d vector) {
|
||||
return Math.abs(vector.getX()) < Math.abs(vector.getZ()) ? YZX_AXIS_ORDER : YXZ_AXIS_ORDER;
|
||||
}
|
||||
|
||||
public static Vector3d relative(Vector3d curr, Direction direction, double value) {
|
||||
Vector3i vec = direction.getVector();
|
||||
return new Vector3d(
|
||||
curr.x + value * vec.getX(), curr.y + value * vec.getY(), curr.z + value * vec.getZ()
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
package ac.grim.grimac.predictionengine.blockeffects;
|
||||
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
|
||||
public interface BlockEffectsResolver {
|
||||
|
||||
void applyEffectsFromBlocks(GrimPlayer player);
|
||||
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
package ac.grim.grimac.predictionengine.blockeffects;
|
||||
|
||||
import com.github.retrooper.packetevents.util.Vector3i;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface BlockStepVisitor {
|
||||
|
||||
boolean visit(Vector3i blockPos, int index);
|
||||
|
||||
}
|
||||
+191
@@ -0,0 +1,191 @@
|
||||
package ac.grim.grimac.predictionengine.blockeffects.impl;
|
||||
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.predictionengine.blockeffects.BlockCollisions;
|
||||
import ac.grim.grimac.predictionengine.blockeffects.BlockEffectsResolver;
|
||||
import ac.grim.grimac.predictionengine.blockeffects.BlockStepVisitor;
|
||||
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
|
||||
import ac.grim.grimac.utils.math.GrimMath;
|
||||
import ac.grim.grimac.utils.nmsutil.Collisions;
|
||||
import ac.grim.grimac.utils.nmsutil.GetBoundingBox;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
|
||||
import com.github.retrooper.packetevents.util.Vector3d;
|
||||
import com.github.retrooper.packetevents.util.Vector3i;
|
||||
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.longs.LongSet;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
// 1.21.10 ONLY - 1.21.9 is impossible to implement due to Mojang awesomeness
|
||||
public class BlockEffectsResolverV1_21_10 implements BlockEffectsResolver {
|
||||
|
||||
public static final BlockEffectsResolver INSTANCE = new BlockEffectsResolverV1_21_10();
|
||||
|
||||
@Override
|
||||
public void applyEffectsFromBlocks(GrimPlayer player) {
|
||||
LongSet visitedBlocks = player.visitedBlocks;
|
||||
|
||||
for (GrimPlayer.Movement movement : player.finalMovementsThisTick) {
|
||||
Vector3d from = movement.from();
|
||||
Vector3d to = movement.to().subtract(movement.from());
|
||||
|
||||
int iterationCount = 16;
|
||||
if (movement.axisIndependant() && to.lengthSquared() > 0.0) {
|
||||
for (Collisions.Axis axis : BlockCollisions.axisStepOrder(movement.axisDependentOriginalMovement())) {
|
||||
double value = axis.get(to);
|
||||
if (value != 0.0) {
|
||||
Vector3d vector = BlockCollisions.relative(from, axis.getPositive(), value);
|
||||
iterationCount -= checkInsideBlocks(player, from, vector, visitedBlocks, iterationCount);
|
||||
from = vector;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
iterationCount -= checkInsideBlocks(player, movement.from(), movement.to(), visitedBlocks, 16);
|
||||
}
|
||||
|
||||
if (iterationCount <= 0) {
|
||||
checkInsideBlocks(player, movement.to(), movement.to(), visitedBlocks, 1);
|
||||
}
|
||||
}
|
||||
|
||||
visitedBlocks.clear();
|
||||
}
|
||||
|
||||
public static int checkInsideBlocks(GrimPlayer player, Vector3d from, Vector3d to, LongSet visitedBlocks, int count) {
|
||||
SimpleCollisionBox boundingBox = GetBoundingBox.getCollisionBoxForPlayer(player, to.x, to.y, to.z).expand(-1.0E-5F);
|
||||
boolean isFarEnough = from.distanceSquared(to) > GrimMath.square(0.9999900000002526);
|
||||
|
||||
AtomicInteger blockCount = new AtomicInteger();
|
||||
forEachBlockIntersectedBetween(from, to, boundingBox, (blockPos, localCount) -> {
|
||||
if (localCount >= count) {
|
||||
return false;
|
||||
}
|
||||
|
||||
blockCount.set(localCount);
|
||||
|
||||
WrappedBlockState blockState = player.compensatedWorld.getBlock(blockPos);
|
||||
StateType blockType = blockState.getType();
|
||||
|
||||
if (blockType.isAir()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (visitedBlocks.add(GrimMath.asLong(blockPos))) {
|
||||
boolean shouldApply = isFarEnough || boundingBox.intersects(blockPos);
|
||||
Collisions.onInsideBlock(player, blockType, blockState, blockPos.x, blockPos.y, blockPos.z, shouldApply);
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
return blockCount.get() + 1;
|
||||
}
|
||||
|
||||
public static boolean forEachBlockIntersectedBetween(Vector3d start, Vector3d end, SimpleCollisionBox boundingBox, BlockStepVisitor visitor) {
|
||||
Vector3d direction = end.subtract(start);
|
||||
if (direction.lengthSquared() < (double) GrimMath.square(1.0E-5F)) {
|
||||
for (Vector3i blockPos : SimpleCollisionBox.betweenClosed(boundingBox)) {
|
||||
if (!visitor.visit(blockPos, 0)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
LongSet alreadyVisited = new LongOpenHashSet();
|
||||
|
||||
for (Vector3i blockPos : SimpleCollisionBox.betweenCornersInDirection(boundingBox.move(direction.multiply(-1.0)), direction)) {
|
||||
if (!visitor.visit(blockPos, 0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
alreadyVisited.add(GrimMath.asLong(blockPos));
|
||||
}
|
||||
|
||||
int iterationCount = addCollisionsAlongTravel(alreadyVisited, direction, boundingBox, visitor);
|
||||
if (iterationCount < 0) {
|
||||
return false;
|
||||
} else {
|
||||
for (Vector3i blockPos : SimpleCollisionBox.betweenCornersInDirection(boundingBox, direction)) {
|
||||
if (alreadyVisited.add(GrimMath.asLong(blockPos)) && !visitor.visit(blockPos, iterationCount + 1)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static int addCollisionsAlongTravel(LongSet alreadyVisited, Vector3d direction, SimpleCollisionBox boundingBox, BlockStepVisitor visitor) {
|
||||
double sizeX = boundingBox.getXSize();
|
||||
double sizeY = boundingBox.getYSize();
|
||||
double sizeZ = boundingBox.getZSize();
|
||||
|
||||
Vector3i furthestCorner = BlockCollisions.getFurthestCorner(direction);
|
||||
|
||||
Vector3d center = boundingBox.getCenter();
|
||||
Vector3d end = new Vector3d(
|
||||
center.x + sizeX * 0.5 * furthestCorner.getX(),
|
||||
center.y + sizeY * 0.5 * furthestCorner.getY(),
|
||||
center.z + sizeZ * 0.5 * furthestCorner.getZ()
|
||||
);
|
||||
Vector3d start = end.subtract(direction);
|
||||
|
||||
int currentX = GrimMath.floor(start.x);
|
||||
int currentY = GrimMath.floor(start.y);
|
||||
int currentZ = GrimMath.floor(start.z);
|
||||
int stepX = GrimMath.sign(direction.x);
|
||||
int stepY = GrimMath.sign(direction.y);
|
||||
int stepZ = GrimMath.sign(direction.z);
|
||||
double tMaxX = stepX == 0 ? Double.MAX_VALUE : stepX / direction.x;
|
||||
double tMaxY = stepY == 0 ? Double.MAX_VALUE : stepY / direction.y;
|
||||
double tMaxZ = stepZ == 0 ? Double.MAX_VALUE : stepZ / direction.z;
|
||||
double tDeltaX = tMaxX * (stepX > 0 ? 1.0 - GrimMath.frac(start.x) : GrimMath.frac(start.x));
|
||||
double tDeltaY = tMaxY * (stepY > 0 ? 1.0 - GrimMath.frac(start.y) : GrimMath.frac(start.y));
|
||||
double tDeltaZ = tMaxZ * (stepZ > 0 ? 1.0 - GrimMath.frac(start.z) : GrimMath.frac(start.z));
|
||||
int iterationCount = 0;
|
||||
|
||||
while (tDeltaX <= 1.0 || tDeltaY <= 1.0 || tDeltaZ <= 1.0) {
|
||||
if (tDeltaX < tDeltaY) {
|
||||
if (tDeltaX < tDeltaZ) {
|
||||
currentX += stepX;
|
||||
tDeltaX += tMaxX;
|
||||
} else {
|
||||
currentZ += stepZ;
|
||||
tDeltaZ += tMaxZ;
|
||||
}
|
||||
} else if (tDeltaY < tDeltaZ) {
|
||||
currentY += stepY;
|
||||
tDeltaY += tMaxY;
|
||||
} else {
|
||||
currentZ += stepZ;
|
||||
tDeltaZ += tMaxZ;
|
||||
}
|
||||
|
||||
Optional<Vector3d> collisionPoint = BlockCollisions.clip(currentX, currentY, currentZ, currentX + 1, currentY + 1, currentZ + 1, start, end);
|
||||
if (!collisionPoint.isEmpty()) {
|
||||
iterationCount++;
|
||||
Vector3d collisionVec = collisionPoint.get();
|
||||
double clampedX = GrimMath.clamp(collisionVec.x, currentX + 1.0E-5F, currentX + 1.0 - 1.0E-5F);
|
||||
double clampedY = GrimMath.clamp(collisionVec.y, currentY + 1.0E-5F, currentY + 1.0 - 1.0E-5F);
|
||||
double clampedZ = GrimMath.clamp(collisionVec.z, currentZ + 1.0E-5F, currentZ + 1.0 - 1.0E-5F);
|
||||
int endX = GrimMath.floor(clampedX - sizeX * furthestCorner.getX());
|
||||
int endY = GrimMath.floor(clampedY - sizeY * furthestCorner.getY());
|
||||
int endZ = GrimMath.floor(clampedZ - sizeZ * furthestCorner.getZ());
|
||||
int copyIterationCount = iterationCount;
|
||||
|
||||
for (Vector3i blockPos : SimpleCollisionBox.betweenCornersInDirection(currentX, currentY, currentZ, endX, endY, endZ, direction)) {
|
||||
if (alreadyVisited.add(GrimMath.asLong(blockPos)) && !visitor.visit(blockPos, copyIterationCount)) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return iterationCount;
|
||||
}
|
||||
|
||||
}
|
||||
+131
@@ -0,0 +1,131 @@
|
||||
package ac.grim.grimac.predictionengine.blockeffects.impl;
|
||||
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.predictionengine.blockeffects.BlockCollisions;
|
||||
import ac.grim.grimac.predictionengine.blockeffects.BlockEffectsResolver;
|
||||
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
|
||||
import ac.grim.grimac.utils.math.GrimMath;
|
||||
import ac.grim.grimac.utils.nmsutil.Collisions;
|
||||
import ac.grim.grimac.utils.nmsutil.GetBoundingBox;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
|
||||
import com.github.retrooper.packetevents.util.Vector3d;
|
||||
import com.github.retrooper.packetevents.util.Vector3i;
|
||||
import it.unimi.dsi.fastutil.longs.LongSet;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectLinkedOpenHashSet;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
// 1.21.2-1.21.3
|
||||
public class BlockEffectsResolverV1_21_2 implements BlockEffectsResolver {
|
||||
|
||||
public static BlockEffectsResolver INSTANCE = new BlockEffectsResolverV1_21_2();
|
||||
|
||||
@Override
|
||||
public void applyEffectsFromBlocks(GrimPlayer player) {
|
||||
LongSet visitedBlocks = player.visitedBlocks;
|
||||
SimpleCollisionBox boundingBox = (player.inVehicle()
|
||||
? GetBoundingBox.getCollisionBoxForPlayer(player, player.x, player.y, player.z)
|
||||
: player.boundingBox.copy()).expand(-1.0E-5F);
|
||||
|
||||
for (GrimPlayer.Movement movement : player.finalMovementsThisTick) {
|
||||
Vector3d from = movement.from();
|
||||
Vector3d to = movement.to();
|
||||
|
||||
for (Vector3i blockPos : boxTraverseBlocks(from, to, boundingBox)) {
|
||||
WrappedBlockState blockState = player.compensatedWorld.getBlock(blockPos);
|
||||
StateType blockType = blockState.getType();
|
||||
|
||||
if (blockType.isAir()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (visitedBlocks.add(GrimMath.asLong(blockPos))) {
|
||||
Collisions.onInsideBlock(player, blockType, blockState, blockPos.x, blockPos.y, blockPos.z, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
visitedBlocks.clear();
|
||||
}
|
||||
|
||||
private static Iterable<Vector3i> boxTraverseBlocks(Vector3d start, Vector3d end, SimpleCollisionBox boundingBox) {
|
||||
Vector3d direction = end.subtract(start);
|
||||
Iterable<Vector3i> initialBlocks = SimpleCollisionBox.betweenClosed(boundingBox);
|
||||
if (direction.lengthSquared() < GrimMath.square(0.99999F)) {
|
||||
return initialBlocks;
|
||||
} else {
|
||||
Set<Vector3i> traversedBlocks = new ObjectLinkedOpenHashSet<>();
|
||||
Vector3d normalizedDirection = direction.normalize().multiply(Collisions.COLLISION_EPSILON);
|
||||
Vector3d boxMinPosition = boundingBox.min().toVector3d().add(normalizedDirection);
|
||||
Vector3d subtractedMinPosition = boundingBox.min().toVector3d().subtract(direction).subtract(normalizedDirection);
|
||||
addCollisionsAlongTravel(traversedBlocks, subtractedMinPosition, boxMinPosition, boundingBox);
|
||||
|
||||
for (Vector3i blockPos : initialBlocks) {
|
||||
traversedBlocks.add(blockPos);
|
||||
}
|
||||
|
||||
return traversedBlocks;
|
||||
}
|
||||
}
|
||||
|
||||
public static void addCollisionsAlongTravel(Set<Vector3i> output, Vector3d start, Vector3d end, SimpleCollisionBox boundingBox) {
|
||||
Vector3d direction = end.subtract(start);
|
||||
int currentX = GrimMath.floor(start.x);
|
||||
int currentY = GrimMath.floor(start.y);
|
||||
int currentZ = GrimMath.floor(start.z);
|
||||
int stepX = GrimMath.sign(direction.x);
|
||||
int stepY = GrimMath.sign(direction.y);
|
||||
int stepZ = GrimMath.sign(direction.z);
|
||||
double tMaxX = stepX == 0 ? Double.MAX_VALUE : stepX / direction.x;
|
||||
double tMaxY = stepY == 0 ? Double.MAX_VALUE : stepY / direction.y;
|
||||
double tMaxZ = stepZ == 0 ? Double.MAX_VALUE : stepZ / direction.z;
|
||||
double tDeltaX = tMaxX * (stepX > 0 ? 1.0 - GrimMath.frac(start.x) : GrimMath.frac(start.x));
|
||||
double tDeltaY = tMaxY * (stepY > 0 ? 1.0 - GrimMath.frac(start.y) : GrimMath.frac(start.y));
|
||||
double tDeltaZ = tMaxZ * (stepZ > 0 ? 1.0 - GrimMath.frac(start.z) : GrimMath.frac(start.z));
|
||||
int iterationCount = 0;
|
||||
|
||||
while (tDeltaX <= 1.0 || tDeltaY <= 1.0 || tDeltaZ <= 1.0) {
|
||||
if (tDeltaX < tDeltaY) {
|
||||
if (tDeltaX < tDeltaZ) {
|
||||
currentX += stepX;
|
||||
tDeltaX += tMaxX;
|
||||
} else {
|
||||
currentZ += stepZ;
|
||||
tDeltaZ += tMaxZ;
|
||||
}
|
||||
} else if (tDeltaY < tDeltaZ) {
|
||||
currentY += stepY;
|
||||
tDeltaY += tMaxY;
|
||||
} else {
|
||||
currentZ += stepZ;
|
||||
tDeltaZ += tMaxZ;
|
||||
}
|
||||
|
||||
if (iterationCount++ > 16) {
|
||||
break;
|
||||
}
|
||||
|
||||
Optional<Vector3d> collisionPoint = BlockCollisions.clip(currentX, currentY, currentZ, currentX + 1, currentY + 1, currentZ + 1, start, end);
|
||||
if (!collisionPoint.isEmpty()) {
|
||||
Vector3d collisionVec = collisionPoint.get();
|
||||
double clampedX = GrimMath.clamp(collisionVec.x, currentX + 1.0E-5F, currentX + 1.0 - 1.0E-5F);
|
||||
double clampedY = GrimMath.clamp(collisionVec.y, currentY + 1.0E-5F, currentY + 1.0 - 1.0E-5F);
|
||||
double clampedZ = GrimMath.clamp(collisionVec.z, currentZ + 1.0E-5F, currentZ + 1.0 - 1.0E-5F);
|
||||
int endX = GrimMath.floor(clampedX + boundingBox.getXSize());
|
||||
int endY = GrimMath.floor(clampedY + boundingBox.getYSize());
|
||||
int endZ = GrimMath.floor(clampedZ + boundingBox.getZSize());
|
||||
|
||||
for (int x = currentX; x <= endX; x++) {
|
||||
for (int y = currentY; y <= endY; y++) {
|
||||
for (int z = currentZ; z <= endZ; z++) {
|
||||
output.add(new Vector3i(x, y, z));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
package ac.grim.grimac.predictionengine.blockeffects.impl;
|
||||
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.predictionengine.blockeffects.BlockEffectsResolver;
|
||||
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
|
||||
import ac.grim.grimac.utils.math.GrimMath;
|
||||
import ac.grim.grimac.utils.nmsutil.Collisions;
|
||||
import ac.grim.grimac.utils.nmsutil.GetBoundingBox;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
|
||||
import com.github.retrooper.packetevents.util.Vector3d;
|
||||
import com.github.retrooper.packetevents.util.Vector3i;
|
||||
import it.unimi.dsi.fastutil.longs.LongSet;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectLinkedOpenHashSet;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
// 1.21.4
|
||||
public class BlockEffectsResolverV1_21_4 implements BlockEffectsResolver {
|
||||
|
||||
public static final BlockEffectsResolver INSTANCE = new BlockEffectsResolverV1_21_4();
|
||||
|
||||
@Override
|
||||
public void applyEffectsFromBlocks(GrimPlayer player) {
|
||||
LongSet visitedBlocks = player.visitedBlocks;
|
||||
|
||||
for (GrimPlayer.Movement movement : player.finalMovementsThisTick) {
|
||||
Vector3d from = movement.from();
|
||||
Vector3d to = movement.to();
|
||||
|
||||
SimpleCollisionBox boundingBox = GetBoundingBox.getCollisionBoxForPlayer(player, to.x, to.y, to.z).expand(-1.0E-5F);
|
||||
for (Vector3i blockPos : boxTraverseBlocks(from, to, boundingBox)) {
|
||||
WrappedBlockState blockState = player.compensatedWorld.getBlock(blockPos);
|
||||
StateType blockType = blockState.getType();
|
||||
|
||||
if (blockType.isAir()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (visitedBlocks.add(GrimMath.asLong(blockPos))) {
|
||||
Collisions.onInsideBlock(player, blockType, blockState, blockPos.x, blockPos.y, blockPos.z, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
visitedBlocks.clear();
|
||||
}
|
||||
|
||||
private static Iterable<Vector3i> boxTraverseBlocks(Vector3d start, Vector3d end, SimpleCollisionBox boundingBox) {
|
||||
Vector3d direction = end.subtract(start);
|
||||
Iterable<Vector3i> initialBlocks = SimpleCollisionBox.betweenClosed(boundingBox);
|
||||
if (direction.lengthSquared() < GrimMath.square(0.99999F)) {
|
||||
return initialBlocks;
|
||||
} else {
|
||||
Set<Vector3i> traversedBlocks = new ObjectLinkedOpenHashSet<>();
|
||||
Vector3d boxMinPosition = boundingBox.min().toVector3d();
|
||||
Vector3d subtractedMinPosition = boxMinPosition.subtract(direction);
|
||||
BlockEffectsResolverV1_21_2.addCollisionsAlongTravel(traversedBlocks, subtractedMinPosition, boxMinPosition, boundingBox);
|
||||
|
||||
for (Vector3i blockPos : initialBlocks) {
|
||||
traversedBlocks.add(blockPos);
|
||||
}
|
||||
|
||||
return traversedBlocks;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+149
@@ -0,0 +1,149 @@
|
||||
package ac.grim.grimac.predictionengine.blockeffects.impl;
|
||||
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.predictionengine.blockeffects.BlockCollisions;
|
||||
import ac.grim.grimac.predictionengine.blockeffects.BlockEffectsResolver;
|
||||
import ac.grim.grimac.predictionengine.blockeffects.BlockStepVisitor;
|
||||
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
|
||||
import ac.grim.grimac.utils.math.GrimMath;
|
||||
import ac.grim.grimac.utils.nmsutil.Collisions;
|
||||
import ac.grim.grimac.utils.nmsutil.GetBoundingBox;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
|
||||
import com.github.retrooper.packetevents.util.Vector3d;
|
||||
import com.github.retrooper.packetevents.util.Vector3i;
|
||||
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.longs.LongSet;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
// 1.21.5
|
||||
public class BlockEffectsResolverV1_21_5 implements BlockEffectsResolver {
|
||||
|
||||
public static final BlockEffectsResolver INSTANCE = new BlockEffectsResolverV1_21_5();
|
||||
|
||||
@Override
|
||||
public void applyEffectsFromBlocks(GrimPlayer player) {
|
||||
LongSet visitedBlocks = player.visitedBlocks;
|
||||
|
||||
for (GrimPlayer.Movement movement : player.finalMovementsThisTick) {
|
||||
Vector3d from = movement.from();
|
||||
Vector3d to = movement.to().subtract(movement.from());
|
||||
if (movement.axisIndependant() && to.lengthSquared() > 0.0) {
|
||||
for (Collisions.Axis axis : BlockCollisions.axisStepOrder(to)) {
|
||||
double value = axis.get(to);
|
||||
if (value != 0.0) {
|
||||
Vector3d vector = BlockCollisions.relative(from, axis.getPositive(), value);
|
||||
checkInsideBlocks(player, from, vector, visitedBlocks);
|
||||
from = vector;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
checkInsideBlocks(player, movement.from(), movement.to(), visitedBlocks);
|
||||
}
|
||||
}
|
||||
|
||||
visitedBlocks.clear();
|
||||
}
|
||||
|
||||
private static void checkInsideBlocks(GrimPlayer player, Vector3d from, Vector3d to, LongSet visitedBlocks) {
|
||||
SimpleCollisionBox boundingBox = GetBoundingBox.getCollisionBoxForPlayer(player, to.x, to.y, to.z).expand(-1.0E-5F);
|
||||
forEachBlockIntersectedBetween(from, to, boundingBox, (blockPos, i) -> {
|
||||
WrappedBlockState blockState = player.compensatedWorld.getBlock(blockPos);
|
||||
StateType blockType = blockState.getType();
|
||||
|
||||
if (blockType.isAir()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (visitedBlocks.add(GrimMath.asLong(blockPos))) {
|
||||
Collisions.onInsideBlock(player, blockType, blockState, blockPos.x, blockPos.y, blockPos.z, true);
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
private static void forEachBlockIntersectedBetween(Vector3d start, Vector3d end, SimpleCollisionBox boundingBox, BlockStepVisitor blockStepVisitor) {
|
||||
Vector3d direction = end.subtract(start);
|
||||
if (!(direction.lengthSquared() < GrimMath.square(0.99999F))) {
|
||||
LongSet alreadyVisited = new LongOpenHashSet();
|
||||
Vector3d boxMinPosition = boundingBox.min().toVector3d();
|
||||
Vector3d subtractedMinPosition = boxMinPosition.subtract(direction);
|
||||
int iterationCount = addCollisionsAlongTravel(alreadyVisited, subtractedMinPosition, boxMinPosition, boundingBox, blockStepVisitor);
|
||||
|
||||
for (Vector3i blockPos : SimpleCollisionBox.betweenClosed(boundingBox)) {
|
||||
if (!alreadyVisited.contains(GrimMath.asLong(blockPos))) {
|
||||
blockStepVisitor.visit(blockPos, iterationCount + 1);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (Vector3i blockPos : SimpleCollisionBox.betweenClosed(boundingBox)) {
|
||||
blockStepVisitor.visit(blockPos, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static int addCollisionsAlongTravel(LongSet alreadyVisited, Vector3d start, Vector3d end, SimpleCollisionBox boundingBox, BlockStepVisitor blockStepVisitor) {
|
||||
Vector3d direction = end.subtract(start);
|
||||
int currentX = GrimMath.floor(start.x);
|
||||
int currentY = GrimMath.floor(start.y);
|
||||
int currentZ = GrimMath.floor(start.z);
|
||||
int stepX = GrimMath.sign(direction.x);
|
||||
int stepY = GrimMath.sign(direction.y);
|
||||
int stepZ = GrimMath.sign(direction.z);
|
||||
double tMaxX = stepX == 0 ? Double.MAX_VALUE : stepX / direction.x;
|
||||
double tMaxY = stepY == 0 ? Double.MAX_VALUE : stepY / direction.y;
|
||||
double tMaxZ = stepZ == 0 ? Double.MAX_VALUE : stepZ / direction.z;
|
||||
double tDeltaX = tMaxX * (stepX > 0 ? 1.0 - GrimMath.frac(start.x) : GrimMath.frac(start.x));
|
||||
double tDeltaY = tMaxY * (stepY > 0 ? 1.0 - GrimMath.frac(start.y) : GrimMath.frac(start.y));
|
||||
double tDeltaZ = tMaxZ * (stepZ > 0 ? 1.0 - GrimMath.frac(start.z) : GrimMath.frac(start.z));
|
||||
int iterationCount = 0;
|
||||
|
||||
while (tDeltaX <= 1.0 || tDeltaY <= 1.0 || tDeltaZ <= 1.0) {
|
||||
if (tDeltaX < tDeltaY) {
|
||||
if (tDeltaX < tDeltaZ) {
|
||||
currentX += stepX;
|
||||
tDeltaX += tMaxX;
|
||||
} else {
|
||||
currentZ += stepZ;
|
||||
tDeltaZ += tMaxZ;
|
||||
}
|
||||
} else if (tDeltaY < tDeltaZ) {
|
||||
currentY += stepY;
|
||||
tDeltaY += tMaxY;
|
||||
} else {
|
||||
currentZ += stepZ;
|
||||
tDeltaZ += tMaxZ;
|
||||
}
|
||||
|
||||
if (iterationCount++ > 16) {
|
||||
break;
|
||||
}
|
||||
|
||||
Optional<Vector3d> collisionPoint = BlockCollisions.clip(currentX, currentY, currentZ, currentX + 1, currentY + 1, currentZ + 1, start, end);
|
||||
if (!collisionPoint.isEmpty()) {
|
||||
Vector3d collisionVec = collisionPoint.get();
|
||||
double clampedX = GrimMath.clamp(collisionVec.x, currentX + 1.0E-5F, currentX + 1.0 - 1.0E-5F);
|
||||
double clampedY = GrimMath.clamp(collisionVec.y, currentY + 1.0E-5F, currentY + 1.0 - 1.0E-5F);
|
||||
double clampedZ = GrimMath.clamp(collisionVec.z, currentZ + 1.0E-5F, currentZ + 1.0 - 1.0E-5F);
|
||||
int endX = GrimMath.floor(clampedX + boundingBox.getXSize());
|
||||
int endY = GrimMath.floor(clampedY + boundingBox.getYSize());
|
||||
int endZ = GrimMath.floor(clampedZ + boundingBox.getZSize());
|
||||
|
||||
for (int x = currentX; x <= endX; x++) {
|
||||
for (int y = currentY; y <= endY; y++) {
|
||||
for (int z = currentZ; z <= endZ; z++) {
|
||||
if (alreadyVisited.add(GrimMath.asLong(x, y, z))) {
|
||||
blockStepVisitor.visit(new Vector3i(x, y, z), iterationCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return iterationCount;
|
||||
}
|
||||
|
||||
}
|
||||
+159
@@ -0,0 +1,159 @@
|
||||
package ac.grim.grimac.predictionengine.blockeffects.impl;
|
||||
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.predictionengine.blockeffects.BlockCollisions;
|
||||
import ac.grim.grimac.predictionengine.blockeffects.BlockEffectsResolver;
|
||||
import ac.grim.grimac.predictionengine.blockeffects.BlockStepVisitor;
|
||||
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
|
||||
import ac.grim.grimac.utils.math.GrimMath;
|
||||
import ac.grim.grimac.utils.nmsutil.Collisions;
|
||||
import ac.grim.grimac.utils.nmsutil.GetBoundingBox;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
|
||||
import com.github.retrooper.packetevents.util.Vector3d;
|
||||
import com.github.retrooper.packetevents.util.Vector3i;
|
||||
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.longs.LongSet;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
// 1.21.6-1.21.8
|
||||
public class BlockEffectsResolverV1_21_6 implements BlockEffectsResolver {
|
||||
|
||||
public static final BlockEffectsResolver INSTANCE = new BlockEffectsResolverV1_21_6();
|
||||
|
||||
@Override
|
||||
public void applyEffectsFromBlocks(GrimPlayer player) {
|
||||
LongSet visitedBlocks = player.visitedBlocks;
|
||||
|
||||
for (GrimPlayer.Movement movement : player.finalMovementsThisTick) {
|
||||
Vector3d from = movement.from();
|
||||
Vector3d to = movement.to().subtract(movement.from());
|
||||
if (movement.axisIndependant() && to.lengthSquared() > 0.0) {
|
||||
for (Collisions.Axis axis : BlockCollisions.axisStepOrder(to)) {
|
||||
double value = axis.get(to);
|
||||
if (value != 0.0) {
|
||||
Vector3d vector = BlockCollisions.relative(from, axis.getPositive(), value);
|
||||
checkInsideBlocks(player, from, vector, visitedBlocks);
|
||||
from = vector;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
checkInsideBlocks(player, movement.from(), movement.to(), visitedBlocks);
|
||||
}
|
||||
}
|
||||
|
||||
visitedBlocks.clear();
|
||||
}
|
||||
|
||||
private static void checkInsideBlocks(GrimPlayer player, Vector3d from, Vector3d to, LongSet visitedBlocks) {
|
||||
SimpleCollisionBox boundingBox = GetBoundingBox.getCollisionBoxForPlayer(player, to.x, to.y, to.z).expand(-1.0E-5F);
|
||||
forEachBlockIntersectedBetween(from, to, boundingBox, (blockPos, i) -> {
|
||||
WrappedBlockState blockState = player.compensatedWorld.getBlock(blockPos);
|
||||
StateType blockType = blockState.getType();
|
||||
|
||||
if (blockType.isAir()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (visitedBlocks.add(GrimMath.asLong(blockPos))) {
|
||||
Collisions.onInsideBlock(player, blockType, blockState, blockPos.x, blockPos.y, blockPos.z, true);
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
private static boolean forEachBlockIntersectedBetween(Vector3d start, Vector3d end, SimpleCollisionBox boundingBox, BlockStepVisitor blockStepVisitor) {
|
||||
Vector3d direction = end.subtract(start);
|
||||
if (direction.lengthSquared() < GrimMath.square(0.99999F)) {
|
||||
for (Vector3i blockPos : SimpleCollisionBox.betweenClosed(boundingBox)) {
|
||||
if (!blockStepVisitor.visit(blockPos, 0)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
LongSet alreadyVisited = new LongOpenHashSet();
|
||||
Vector3d boxMinPosition = boundingBox.min().toVector3d();
|
||||
Vector3d subtractedMinPosition = boxMinPosition.subtract(direction);
|
||||
|
||||
int iterationCount = addCollisionsAlongTravel(alreadyVisited, subtractedMinPosition, boxMinPosition, boundingBox, blockStepVisitor);
|
||||
if (iterationCount < 0) {
|
||||
return false;
|
||||
} else {
|
||||
for (Vector3i blockPos : SimpleCollisionBox.betweenClosed(boundingBox)) {
|
||||
if (!alreadyVisited.contains(GrimMath.asLong(blockPos)) && !blockStepVisitor.visit(blockPos, iterationCount + 1)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static int addCollisionsAlongTravel(LongSet alreadyVisited, Vector3d start, Vector3d end, SimpleCollisionBox boundingBox, BlockStepVisitor blockStepVisitor) {
|
||||
Vector3d direction = end.subtract(start);
|
||||
int currentX = GrimMath.floor(start.x);
|
||||
int currentY = GrimMath.floor(start.y);
|
||||
int currentZ = GrimMath.floor(start.z);
|
||||
int stepX = GrimMath.sign(direction.x);
|
||||
int stepY = GrimMath.sign(direction.y);
|
||||
int stepZ = GrimMath.sign(direction.z);
|
||||
double tMaxX = stepX == 0 ? Double.MAX_VALUE : stepX / direction.x;
|
||||
double tMaxY = stepY == 0 ? Double.MAX_VALUE : stepY / direction.y;
|
||||
double tMaxZ = stepZ == 0 ? Double.MAX_VALUE : stepZ / direction.z;
|
||||
double tDeltaX = tMaxX * (stepX > 0 ? 1.0 - GrimMath.frac(start.x) : GrimMath.frac(start.x));
|
||||
double tDeltaY = tMaxY * (stepY > 0 ? 1.0 - GrimMath.frac(start.y) : GrimMath.frac(start.y));
|
||||
double tDeltaZ = tMaxZ * (stepZ > 0 ? 1.0 - GrimMath.frac(start.z) : GrimMath.frac(start.z));
|
||||
int iterationCount = 0;
|
||||
|
||||
while (tDeltaX <= 1.0 || tDeltaY <= 1.0 || tDeltaZ <= 1.0) {
|
||||
if (tDeltaX < tDeltaY) {
|
||||
if (tDeltaX < tDeltaZ) {
|
||||
currentX += stepX;
|
||||
tDeltaX += tMaxX;
|
||||
} else {
|
||||
currentZ += stepZ;
|
||||
tDeltaZ += tMaxZ;
|
||||
}
|
||||
} else if (tDeltaY < tDeltaZ) {
|
||||
currentY += stepY;
|
||||
tDeltaY += tMaxY;
|
||||
} else {
|
||||
currentZ += stepZ;
|
||||
tDeltaZ += tMaxZ;
|
||||
}
|
||||
|
||||
if (iterationCount++ > 16) {
|
||||
break;
|
||||
}
|
||||
|
||||
Optional<Vector3d> collisionPoint = BlockCollisions.clip(currentX, currentY, currentZ, currentX + 1, currentY + 1, currentZ + 1, start, end);
|
||||
if (!collisionPoint.isEmpty()) {
|
||||
Vector3d collisionVec = collisionPoint.get();
|
||||
double clampedX = GrimMath.clamp(collisionVec.x, currentX + 1.0E-5F, currentX + 1.0 - 1.0E-5F);
|
||||
double clampedY = GrimMath.clamp(collisionVec.y, currentY + 1.0E-5F, currentY + 1.0 - 1.0E-5F);
|
||||
double clampedZ = GrimMath.clamp(collisionVec.z, currentZ + 1.0E-5F, currentZ + 1.0 - 1.0E-5F);
|
||||
int endX = GrimMath.floor(clampedX + boundingBox.getXSize());
|
||||
int endY = GrimMath.floor(clampedY + boundingBox.getYSize());
|
||||
int endZ = GrimMath.floor(clampedZ + boundingBox.getZSize());
|
||||
|
||||
for (int x = currentX; x <= endX; x++) {
|
||||
for (int y = currentY; y <= endY; y++) {
|
||||
for (int z = currentZ; z <= endZ; z++) {
|
||||
if (alreadyVisited.add(GrimMath.asLong(x, y, z)) && !blockStepVisitor.visit(new Vector3i(x, y, z), iterationCount)) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return iterationCount;
|
||||
}
|
||||
|
||||
}
|
||||
+6
-43
@@ -15,7 +15,7 @@ import ac.grim.grimac.utils.nmsutil.Collisions;
|
||||
import ac.grim.grimac.utils.nmsutil.FluidFallingAdjustedMovement;
|
||||
import ac.grim.grimac.utils.nmsutil.GetBoundingBox;
|
||||
import ac.grim.grimac.utils.nmsutil.MainSupportingBlockPosFinder;
|
||||
import ac.grim.grimac.utils.reflection.ViaVersionUtil;
|
||||
import ac.grim.grimac.utils.viaversion.ViaVersionUtil;
|
||||
import ac.grim.grimac.utils.team.EntityPredicates;
|
||||
import ac.grim.grimac.utils.team.EntityTeam;
|
||||
import ac.grim.grimac.utils.team.TeamHandler;
|
||||
@@ -226,14 +226,14 @@ public class MovementTicker {
|
||||
Vector3d from = new Vector3d(player.lastX, player.lastY, player.lastZ);
|
||||
Vector3d to = new Vector3d(player.x, player.y, player.z);
|
||||
|
||||
player.addMovementThisTick(new GrimPlayer.Movement(from, to, true));
|
||||
player.addMovementThisTick(new GrimPlayer.Movement(from, to, new Vector3d(inputVel.getX(), inputVel.getY(), inputVel.getZ())));
|
||||
}
|
||||
|
||||
// This is where vanilla moves the bounding box and sets it
|
||||
player.predictedVelocity = new VectorData(collide.clone(), player.predictedVelocity.lastVector, player.predictedVelocity.vectorType);
|
||||
|
||||
float f = BlockProperties.getBlockSpeedFactor(player, player.mainSupportingBlockData, new Vector3d(player.x, player.y, player.z));
|
||||
player.clientVelocity.multiply(new Vector3dm(f, 1, f));
|
||||
player.clientVelocity.multiply(f, 1, f);
|
||||
|
||||
if (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_21_2)) {
|
||||
return;
|
||||
@@ -446,14 +446,14 @@ public class MovementTicker {
|
||||
|
||||
// Lava movement changed in 1.16
|
||||
if (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_16) && player.slightlyTouchingLava) {
|
||||
player.clientVelocity = player.clientVelocity.multiply(new Vector3dm(0.5D, 0.800000011920929D, 0.5D));
|
||||
player.clientVelocity = player.clientVelocity.multiply(0.5D, 0.800000011920929D, 0.5D);
|
||||
player.clientVelocity = FluidFallingAdjustedMovement.getFluidFallingAdjustedMovement(player, playerGravity, isFalling, player.clientVelocity);
|
||||
} else {
|
||||
player.clientVelocity.multiply(0.5D);
|
||||
}
|
||||
|
||||
if (player.hasGravity)
|
||||
player.clientVelocity.add(new Vector3dm(0.0D, -playerGravity / 4.0D, 0.0D));
|
||||
player.clientVelocity.add(0.0D, -playerGravity / 4.0D, 0.0D);
|
||||
|
||||
} else if (player.isGliding) {
|
||||
if (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_21_5) && Collisions.onClimbable(player, player.lastX, player.lastY, player.lastZ)) {
|
||||
@@ -481,44 +481,7 @@ public class MovementTicker {
|
||||
}
|
||||
}
|
||||
|
||||
if (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_21_2)) {
|
||||
// Reset stuck speed so it can update
|
||||
if (player.stuckSpeedMultiplier.getX() < 0.99) {
|
||||
player.uncertaintyHandler.lastStuckSpeedMultiplier.reset();
|
||||
}
|
||||
|
||||
player.stuckSpeedMultiplier = new Vector3dm(1, 1, 1);
|
||||
player.finalMovementsThisTick.clear();
|
||||
|
||||
Vector3d from = new Vector3d(player.lastX, player.lastY, player.lastZ);
|
||||
Vector3d to = new Vector3d(player.x, player.y, player.z);
|
||||
|
||||
ClientVersion clientVersion = player.getClientVersion();
|
||||
if (clientVersion.isOlderThan(ClientVersion.V_1_21_5)) {
|
||||
player.finalMovementsThisTick.add(new GrimPlayer.Movement(from, to, false));
|
||||
} else if (clientVersion.isNewerThanOrEquals(ClientVersion.V_1_21_5)) {
|
||||
player.finalMovementsThisTick.addAll(player.movementThisTick);
|
||||
player.movementThisTick.clear();
|
||||
|
||||
if (player.finalMovementsThisTick.isEmpty()) {
|
||||
player.finalMovementsThisTick.add(new GrimPlayer.Movement(from, to, false));
|
||||
} else if (player.finalMovementsThisTick.get(player.finalMovementsThisTick.size() - 1).to().distanceSquared(to) > 9.9999994E-11F) {
|
||||
player.finalMovementsThisTick.add(new GrimPlayer.Movement(player.finalMovementsThisTick.get(player.finalMovementsThisTick.size() - 1).to(), to, false));
|
||||
}
|
||||
}
|
||||
|
||||
Collisions.applyEffectsFromBlocks(player);
|
||||
|
||||
if (player.stuckSpeedMultiplier.getX() < 0.9) {
|
||||
// Reset fall distance if stuck in block
|
||||
player.fallDistance = 0;
|
||||
}
|
||||
|
||||
// Flying players are not affected by cobwebs/sweet berry bushes
|
||||
if (player.isFlying) {
|
||||
player.stuckSpeedMultiplier = new Vector3dm(1, 1, 1);
|
||||
}
|
||||
}
|
||||
Collisions.applyEffectsFromBlocks(player);
|
||||
}
|
||||
|
||||
public boolean canStandOnLava() {
|
||||
|
||||
+1
-1
@@ -30,7 +30,7 @@ public class MovementTickerStrider extends MovementTickerRideable {
|
||||
if (isAbove(player) && player.compensatedWorld.getLavaFluidLevelAt((int) Math.floor(player.x), (int) Math.floor(player.y + 1), (int) Math.floor(player.z)) == 0) {
|
||||
player.onGround = true;
|
||||
} else {
|
||||
player.clientVelocity.multiply(0.5).add(new Vector3dm(0, 0.05, 0));
|
||||
player.clientVelocity.multiply(0.5).add(0, 0.05, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+16
-3
@@ -417,7 +417,7 @@ public class PredictionEngine {
|
||||
data.vector.setZ(data.vector.getZ() * 0.6);
|
||||
data.addVectorType(VectorData.VectorType.AttackSlow);
|
||||
} else {
|
||||
velocities.add(data.returnNewModified(data.vector.clone().multiply(new Vector3dm(0.6, 1, 0.6)), VectorData.VectorType.AttackSlow));
|
||||
velocities.add(data.returnNewModified(data.vector.clone().multiply(0.6, 1, 0.6), VectorData.VectorType.AttackSlow));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -437,9 +437,21 @@ public class PredictionEngine {
|
||||
minimumMovement = 0.005D;
|
||||
}
|
||||
|
||||
for (VectorData vector : velocities) {
|
||||
if (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_21_5) && !player.inVehicle()) {
|
||||
boolean stupidVectors = player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_21_5) && !player.inVehicle();
|
||||
boolean stuckOnEdge = player.uncertaintyHandler.stuckOnEdge.hasOccurredSince(2);
|
||||
Set<VectorData> vectors = stupidVectors && stuckOnEdge ? new HashSet<>(velocities) : velocities;
|
||||
for (VectorData vector : vectors) {
|
||||
if (stupidVectors) {
|
||||
if (Collisions.getHorizontalDistanceSqr(vector.vector) < 9.0E-6) {
|
||||
if (stuckOnEdge) {
|
||||
VectorData edgeVector = vector.returnNewModified(vector.vector.clone(), vector.vectorType);
|
||||
if (Math.abs(edgeVector.vector.getY()) < minimumMovement) {
|
||||
edgeVector.vector.setY(0D);
|
||||
}
|
||||
|
||||
velocities.add(edgeVector);
|
||||
}
|
||||
|
||||
vector.vector.setX(0D);
|
||||
vector.vector.setZ(0D);
|
||||
}
|
||||
@@ -460,6 +472,7 @@ public class PredictionEngine {
|
||||
}
|
||||
|
||||
public void addExplosionToPossibilities(GrimPlayer player, Set<VectorData> existingVelocities) {
|
||||
if (player.likelyExplosions == null && player.firstBreadExplosion == null) return;
|
||||
for (VectorData vector : new HashSet<>(existingVelocities)) {
|
||||
if (player.likelyExplosions != null) {
|
||||
existingVelocities.add(new VectorData(vector.vector.clone().add(player.likelyExplosions.vector), vector, VectorData.VectorType.Explosion));
|
||||
|
||||
+5
-5
@@ -31,24 +31,24 @@ public class PredictionEngineElytra extends PredictionEngine {
|
||||
recalculatedGravity = player.getClientVersion().isOlderThan(ClientVersion.V_1_20_5) ? 0.01 : Math.min(recalculatedGravity, 0.01);
|
||||
}
|
||||
|
||||
vector.add(new Vector3dm(0.0D, recalculatedGravity * (-1.0D + vertCosRotation * 0.75D), 0.0D));
|
||||
vector.add(0.0D, recalculatedGravity * (-1.0D + vertCosRotation * 0.75D), 0.0D);
|
||||
double d5;
|
||||
|
||||
// Handle slowing the player down when falling
|
||||
if (vector.getY() < 0.0D && horizontalSqrt > 0.0D) {
|
||||
d5 = vector.getY() * -0.1D * vertCosRotation;
|
||||
vector.add(new Vector3dm(lookVector.getX() * d5 / horizontalSqrt, d5, lookVector.getZ() * d5 / horizontalSqrt));
|
||||
vector.add(lookVector.getX() * d5 / horizontalSqrt, d5, lookVector.getZ() * d5 / horizontalSqrt);
|
||||
}
|
||||
|
||||
// Handle accelerating the player when they are looking down
|
||||
if (pitchRadians < 0.0F && horizontalSqrt > 0.0D) {
|
||||
d5 = horizontalLength * (double) (-player.trigHandler.sin(pitchRadians)) * 0.04D;
|
||||
vector.add(new Vector3dm(-lookVector.getX() * d5 / horizontalSqrt, d5 * 3.2D, -lookVector.getZ() * d5 / horizontalSqrt));
|
||||
vector.add(-lookVector.getX() * d5 / horizontalSqrt, d5 * 3.2D, -lookVector.getZ() * d5 / horizontalSqrt);
|
||||
}
|
||||
|
||||
// Handle accelerating the player sideways
|
||||
if (horizontalSqrt > 0) {
|
||||
vector.add(new Vector3dm((lookVector.getX() / horizontalSqrt * horizontalLength - vector.getX()) * 0.1D, 0.0D, (lookVector.getZ() / horizontalSqrt * horizontalLength - vector.getZ()) * 0.1D));
|
||||
vector.add((lookVector.getX() / horizontalSqrt * horizontalLength - vector.getX()) * 0.1D, 0.0D, (lookVector.getZ() / horizontalSqrt * horizontalLength - vector.getZ()) * 0.1D);
|
||||
}
|
||||
|
||||
return vector;
|
||||
@@ -67,7 +67,7 @@ public class PredictionEngineElytra extends PredictionEngine {
|
||||
for (VectorData data : possibleVectors) {
|
||||
Vector3dm elytraResult = getElytraMovement(player, data.vector.clone(), currentLook);
|
||||
if (applyStuckSpeed != 0) elytraResult.multiply(player.stuckSpeedMultiplier);
|
||||
elytraResult.multiply(new Vector3dm(0.99F, 0.98F, 0.99F));
|
||||
elytraResult.multiply(0.99F, 0.98F, 0.99F);
|
||||
VectorData modified = data.returnNewModified(elytraResult, VectorData.VectorType.InputResult);
|
||||
modified.input = new Vector3dm(0, 0, 0);
|
||||
results.add(modified);
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@ public class PredictionEngineLava extends PredictionEngine {
|
||||
double extraVelFromVertTickSkipUpwards = GrimMath.clamp(player.actualMovement.getY(), vector.vector.clone().getY(), vector.vector.clone().getY() + 0.05f);
|
||||
existingVelocities.add(new VectorData(vector.vector.clone().setY(extraVelFromVertTickSkipUpwards), vector, VectorData.VectorType.Jump));
|
||||
} else {
|
||||
existingVelocities.add(new VectorData(vector.vector.clone().add(new Vector3dm(0, 0.04f, 0)), vector, VectorData.VectorType.Jump));
|
||||
existingVelocities.add(new VectorData(vector.vector.clone().add(0, 0.04f, 0), vector, VectorData.VectorType.Jump));
|
||||
}
|
||||
|
||||
if (player.slightlyTouchingLava && player.lastOnGround && !player.onGround) {
|
||||
|
||||
+1
-1
@@ -56,7 +56,7 @@ public class PredictionEngineNormal extends PredictionEngine {
|
||||
|
||||
JumpPower.jumpFromGround(player, jump);
|
||||
} else {
|
||||
jump.add(new Vector3dm(0, player.flySpeed * 3, 0));
|
||||
jump.add(0, player.flySpeed * 3, 0);
|
||||
if (!player.wasFlying) {
|
||||
Vector3dm edgeCaseJump = jump.clone();
|
||||
JumpPower.jumpFromGround(player, edgeCaseJump);
|
||||
|
||||
+2
-2
@@ -20,7 +20,7 @@ public class PredictionEngineWater extends PredictionEngine {
|
||||
private float swimmingFriction;
|
||||
|
||||
public static void staticVectorEndOfTick(GrimPlayer player, Vector3dm vector, float swimmingFriction, double playerGravity, boolean isFalling) {
|
||||
vector.multiply(new Vector3dm(swimmingFriction, 0.8F, swimmingFriction));
|
||||
vector.multiply(swimmingFriction, 0.8F, swimmingFriction);
|
||||
Vector3dm fluidVector = FluidFallingAdjustedMovement.getFluidFallingAdjustedMovement(player, playerGravity, isFalling, vector);
|
||||
vector.setX(fluidVector.getX());
|
||||
vector.setY(fluidVector.getY());
|
||||
@@ -76,7 +76,7 @@ public class PredictionEngineWater extends PredictionEngine {
|
||||
double extraVelFromVertTickSkipUpwards = GrimMath.clamp(player.actualMovement.getY(), vector.vector.clone().getY(), vector.vector.clone().getY() + 0.05f);
|
||||
existingVelocities.add(new VectorData(vector.vector.clone().setY(extraVelFromVertTickSkipUpwards), vector, VectorData.VectorType.Jump));
|
||||
} else {
|
||||
existingVelocities.add(new VectorData(vector.vector.clone().add(new Vector3dm(0, 0.04f, 0)), vector, VectorData.VectorType.Jump));
|
||||
existingVelocities.add(new VectorData(vector.vector.clone().add(0, 0.04f, 0), vector, VectorData.VectorType.Jump));
|
||||
}
|
||||
|
||||
if (player.slightlyTouchingWater && player.lastOnGround && !player.onGround) {
|
||||
|
||||
+3
-3
@@ -47,10 +47,10 @@ public class PredictionEngineWaterLegacy extends PredictionEngine {
|
||||
@Override
|
||||
public void addJumpsToPossibilities(GrimPlayer player, Set<VectorData> existingVelocities) {
|
||||
for (VectorData vector : new HashSet<>(existingVelocities)) {
|
||||
existingVelocities.add(new VectorData(vector.vector.clone().add(new Vector3dm(0, 0.04f, 0)), vector, VectorData.VectorType.Jump));
|
||||
existingVelocities.add(new VectorData(vector.vector.clone().add(0, 0.04f, 0), vector, VectorData.VectorType.Jump));
|
||||
|
||||
if (player.skippedTickInActualMovement) {
|
||||
existingVelocities.add(new VectorData(vector.vector.clone().add(new Vector3dm(0, 0.02f, 0)), vector, VectorData.VectorType.Jump));
|
||||
existingVelocities.add(new VectorData(vector.vector.clone().add(0, 0.02f, 0), vector, VectorData.VectorType.Jump));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -60,7 +60,7 @@ public class PredictionEngineWaterLegacy extends PredictionEngine {
|
||||
super.endOfTick(player, playerGravity);
|
||||
|
||||
for (VectorData vector : player.getPossibleVelocitiesMinusKnockback()) {
|
||||
vector.vector.multiply(new Vector3dm(swimmingFriction, 0.8F, swimmingFriction));
|
||||
vector.vector.multiply(swimmingFriction, 0.8F, swimmingFriction);
|
||||
|
||||
// Gravity
|
||||
vector.vector.setY(vector.vector.getY() - 0.02D);
|
||||
|
||||
+4
@@ -186,6 +186,10 @@ public class PredictionEngineBoat extends PredictionEngine {
|
||||
public void endOfTick(GrimPlayer player, double d) {
|
||||
super.endOfTick(player, d);
|
||||
Collisions.handleInsideBlocks(player);
|
||||
|
||||
// yes, this is how mojank does it
|
||||
Collisions.applyEffectsFromBlocks(player);
|
||||
Collisions.applyEffectsFromBlocks(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+1
-1
@@ -59,7 +59,7 @@ public final class PredictionEngineRideableUtils {
|
||||
}
|
||||
|
||||
final double multiplier = (double) (22.2222F * player.vehicleData.horseJump) * camel.getAttributeValue(Attributes.MOVEMENT_SPEED) * (double) BlockProperties.getBlockSpeedFactor(player, player.mainSupportingBlockData, new Vector3d(player.lastX, player.lastY, player.lastZ));
|
||||
Vector3dm jumpVelocity = ReachUtils.getLook(player, player.yaw, player.pitch).multiply(new Vector3dm(1.0, 0.0, 1.0)).normalize().multiply(multiplier).add(new Vector3dm(0, (double) (1.4285F * player.vehicleData.horseJump) * jumpYVelocity, 0));
|
||||
Vector3dm jumpVelocity = ReachUtils.getLook(player, player.yaw, player.pitch).multiply(1.0, 0.0, 1.0).normalize().multiply(multiplier).add(0, (double) (1.4285F * player.vehicleData.horseJump) * jumpYVelocity, 0);
|
||||
|
||||
for (VectorData vectorData : possibleVectors) {
|
||||
vectorData.vector.add(jumpVelocity);
|
||||
|
||||
@@ -34,36 +34,105 @@ public class MessageUtil {
|
||||
return vec == null ? "null" : vec.x + ", " + vec.y + ", " + vec.z;
|
||||
}
|
||||
|
||||
public @NotNull String replacePlaceholders(@Nullable GrimPlayer player, @NotNull String string, boolean removeFormatting) {
|
||||
@Contract("_, null, _ -> null; _, !null, _ -> !null")
|
||||
public @Nullable String replacePlaceholders(@Nullable GrimPlayer player, @Nullable String string, boolean removeFormatting) {
|
||||
return replacePlaceholders(player, player == null ? null : player.platformPlayer, string, removeFormatting);
|
||||
}
|
||||
|
||||
public @NotNull String replacePlaceholders(@Nullable GrimPlayer player, @NotNull String string) {
|
||||
@Contract("_, null -> null; _, !null -> !null")
|
||||
public @Nullable String replacePlaceholders(@Nullable GrimPlayer player, @Nullable String string) {
|
||||
return replacePlaceholders(player, player == null ? null : player.platformPlayer, string, false);
|
||||
}
|
||||
|
||||
public @NotNull String replacePlaceholders(@Nullable Sender sender, @NotNull String string) {
|
||||
@Contract("_, null -> null; _, !null -> !null")
|
||||
public @Nullable String replacePlaceholders(@Nullable Sender sender, @Nullable String string) {
|
||||
return replacePlaceholders(sender != null ? sender.getPlatformPlayer() : null, string);
|
||||
}
|
||||
|
||||
public @NotNull String replacePlaceholders(@Nullable PlatformPlayer player, @NotNull String string) {
|
||||
@Contract("_, null -> null; _, !null -> !null")
|
||||
public @Nullable String replacePlaceholders(@Nullable PlatformPlayer player, @Nullable String string) {
|
||||
return replacePlaceholders(player == null ? null : GrimAPI.INSTANCE.getPlayerDataManager().getPlayer(player.getUniqueId()), player, string, false);
|
||||
}
|
||||
|
||||
private @NotNull String replacePlaceholders(@Nullable GrimPlayer grimPlayer, @Nullable PlatformPlayer platformPlayer, @NotNull String string, boolean removeFormatting) {
|
||||
for (Map.Entry<String, String> entry : GrimAPI.INSTANCE.getExternalAPI().getStaticReplacements().entrySet()) {
|
||||
string = string.replace(entry.getKey(), entry.getValue());
|
||||
private static final Pattern UNIFIED_PLACEHOLDER_PATTERN = Pattern.compile("%([a-zA-Z0-9_]+)%");
|
||||
|
||||
@Contract("_, _, null, _ -> null; _, _, !null, _ -> !null")
|
||||
private @Nullable String replacePlaceholders(@Nullable GrimPlayer grimPlayer, @Nullable PlatformPlayer platformPlayer, @Nullable String string, boolean removeFormatting) {
|
||||
if (string == null) return null;
|
||||
|
||||
// --- OPTIMIZATION 1: THE FAST PATH ---
|
||||
// If the string contains no '%' characters, it's impossible for it to have placeholders.
|
||||
// indexOf() is a JVM intrinsic and is magnitudes faster than even creating a Matcher.
|
||||
if (string.indexOf('%') == -1) {
|
||||
// Since there are no % signs we can skip calling papi or our own replacement code
|
||||
return string;
|
||||
}
|
||||
|
||||
if (grimPlayer != null) {
|
||||
for (Map.Entry<String, Function<GrimUser, String>> entry : GrimAPI.INSTANCE.getExternalAPI().getVariableReplacements().entrySet()) {
|
||||
String value = entry.getValue().apply(grimPlayer).replace('%', PLACEHOLDER_ESCAPE_CHAR);
|
||||
if (removeFormatting) value = filterDiscordText(value);
|
||||
string = string.replace(entry.getKey(), value);
|
||||
final Matcher matcher = UNIFIED_PLACEHOLDER_PATTERN.matcher(string);
|
||||
|
||||
// If matcher.find() is false, it means '%' existed but not in a valid %...% pattern.
|
||||
// This avoids allocating a StringBuilder unless absolutely necessary.
|
||||
if (!matcher.find()) {
|
||||
return GrimAPI.INSTANCE.getMessagePlaceHolderManager().replacePlaceholders(platformPlayer, string);
|
||||
}
|
||||
|
||||
// Get references to the maps once, outside the loop.
|
||||
final Map<String, String> staticReplacements = GrimAPI.INSTANCE.getExternalAPI().getStaticReplacements();
|
||||
final Map<String, Function<GrimUser, String>> variableReplacements = GrimAPI.INSTANCE.getExternalAPI().getVariableReplacements();
|
||||
final StringBuilder sb = new StringBuilder(string.length() + 32); // Pre-size with a little extra room
|
||||
|
||||
// --- OPTIMIZATION 2: THE UNIFIED SINGLE-PASS LOOP ---
|
||||
// We use a do-while loop because we already performed the first matcher.find().
|
||||
do {
|
||||
// The full placeholder, e.g., "%tps%" or "%prefix%"
|
||||
final String keyWithPercent = matcher.group(0);
|
||||
String value = null;
|
||||
|
||||
// --- OPTIMIZATION 3: UNIFIED LOOKUP ---
|
||||
// We check the static map first. This is a single, O(1) hash map lookup.
|
||||
String staticValue = staticReplacements.get(keyWithPercent);
|
||||
|
||||
if (staticValue != null) {
|
||||
value = staticValue;
|
||||
} else if (grimPlayer != null) {
|
||||
// If it's not a static placeholder, check if it's a dynamic one.
|
||||
// This is a second, O(1) hash map lookup.
|
||||
final Function<GrimUser, String> func = variableReplacements.get(keyWithPercent);
|
||||
if (func != null) {
|
||||
// LAZY EVALUATION: We only call the expensive function (like getTPS)
|
||||
// if we actually found its placeholder in the string.
|
||||
value = func.apply(grimPlayer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return GrimAPI.INSTANCE.getMessagePlaceHolderManager().replacePlaceholders(platformPlayer, string).replace(PLACEHOLDER_ESCAPE_CHAR, '%');
|
||||
// If we found no replacement, `value` will be null.
|
||||
// In that case, we treat the placeholder as literal text by appending the original key.
|
||||
if (value == null) {
|
||||
value = keyWithPercent;
|
||||
}
|
||||
|
||||
// --- OPTIMIZATION 4: CONDITIONAL FORMATTING ---
|
||||
// The check for `removeFormatting` is inside the loop, but it's a simple boolean
|
||||
// check and the cost is negligible compared to the string operations.
|
||||
if (removeFormatting) {
|
||||
// Note: This assumes `filterDiscordText` is reasonably fast.
|
||||
// If it's slow, there are further micro-optimizations, but this is the right place for it.
|
||||
value = filterDiscordText(value);
|
||||
}
|
||||
|
||||
// `appendReplacement` efficiently appends the text between matches and our replacement value.
|
||||
// `Matcher.quoteReplacement` should handle any '$' or '\' in the replacement value.
|
||||
matcher.appendReplacement(sb, Matcher.quoteReplacement(value));
|
||||
|
||||
} while (matcher.find());
|
||||
|
||||
// Append the final part of the string after the last found placeholder.
|
||||
matcher.appendTail(sb);
|
||||
|
||||
// Create the final string from our builder.
|
||||
String grimReplaced = sb.toString();
|
||||
|
||||
return GrimAPI.INSTANCE.getMessagePlaceHolderManager().replacePlaceholders(platformPlayer, grimReplaced).replace(PLACEHOLDER_ESCAPE_CHAR, '%');
|
||||
}
|
||||
|
||||
public static String filterDiscordText(String message) {
|
||||
|
||||
@@ -3,6 +3,8 @@ package ac.grim.grimac.utils.anticheat;
|
||||
import ac.grim.grimac.GrimAPI;
|
||||
import ac.grim.grimac.api.event.events.GrimJoinEvent;
|
||||
import ac.grim.grimac.api.event.events.GrimQuitEvent;
|
||||
import ac.grim.grimac.checks.debug.DebugGrimPlayer;
|
||||
import ac.grim.grimac.checks.debug.GrimDebugSettings;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.reflection.GeyserUtil;
|
||||
import com.github.retrooper.packetevents.PacketEvents;
|
||||
@@ -66,12 +68,21 @@ public class PlayerDataManager {
|
||||
|
||||
public void addUser(final @NotNull User user) {
|
||||
if (shouldCheck(user)) {
|
||||
GrimPlayer player = new GrimPlayer(user);
|
||||
GrimPlayer player = this.createPlayer(user);
|
||||
playerDataMap.put(user, player);
|
||||
GrimAPI.INSTANCE.getEventBus().post(new GrimJoinEvent(player));
|
||||
}
|
||||
}
|
||||
|
||||
public GrimPlayer createPlayer(User user) {
|
||||
// Simple Global Check
|
||||
if (GrimDebugSettings.shouldEnable()) {
|
||||
return new DebugGrimPlayer(user);
|
||||
} else {
|
||||
return new GrimPlayer(user);
|
||||
}
|
||||
}
|
||||
|
||||
public GrimPlayer remove(final @NotNull User user) {
|
||||
return playerDataMap.remove(user);
|
||||
}
|
||||
|
||||
@@ -555,6 +555,7 @@ public class BlockPlace {
|
||||
// 1.8 clients will simply not send the place when it fails, thanks mojang.
|
||||
if (player.getClientVersion().isNewerThan(ClientVersion.V_1_8)) {
|
||||
for (PacketEntity entity : player.compensatedEntities.entityMap.values()) {
|
||||
if (!entity.canHit()) continue;
|
||||
SimpleCollisionBox interpBox = entity.getPossibleCollisionBoxes();
|
||||
|
||||
final double scale = entity.getAttributeValue(Attributes.SCALE);
|
||||
@@ -591,7 +592,7 @@ public class BlockPlace {
|
||||
|
||||
// Check for waterlogged
|
||||
if (PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_13)) {
|
||||
if (state.getInternalData().containsKey(StateValue.WATERLOGGED)) { // waterloggable
|
||||
if (state.hasProperty(StateValue.WATERLOGGED)) { // waterloggable
|
||||
state.setWaterlogged(existingState.getType() == StateTypes.WATER && existingState.getLevel() == 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -318,7 +318,7 @@ public enum BlockPlaceResult {
|
||||
WrappedBlockState typePlacingOn = place.getDirectionalState(primaryDir.getOppositeFace()); // Block we are placing on
|
||||
|
||||
// Check to see if we can place on the block or there is dripstone on the block that we are placing on also pointing upwards
|
||||
boolean primarySameType = typePlacingOn.getInternalData().containsKey(StateValue.VERTICAL_DIRECTION) && typePlacingOn.getVerticalDirection().name().equals(primaryDir.name());
|
||||
boolean primarySameType = typePlacingOn.hasProperty(StateValue.VERTICAL_DIRECTION) && typePlacingOn.getVerticalDirection().name().equals(primaryDir.name());
|
||||
boolean primaryValid = place.isFullFace(primaryDir.getOppositeFace()) || primarySameType;
|
||||
|
||||
// Try to use the opposite direction, just to see if switching directions makes it valid.
|
||||
@@ -326,7 +326,7 @@ public enum BlockPlaceResult {
|
||||
BlockFace secondaryDirection = primaryDir.getOppositeFace(); // See if placing it DOWNWARDS is valid
|
||||
WrappedBlockState secondaryType = place.getDirectionalState(secondaryDirection.getOppositeFace()); // Get the block above us
|
||||
// Check if the dripstone above us is also facing downwards
|
||||
boolean secondarySameType = secondaryType.getInternalData().containsKey(StateValue.VERTICAL_DIRECTION) && secondaryType.getVerticalDirection().name().equals(primaryDir.name());
|
||||
boolean secondarySameType = secondaryType.hasProperty(StateValue.VERTICAL_DIRECTION) && secondaryType.getVerticalDirection().name().equals(primaryDir.name());
|
||||
|
||||
primaryDir = secondaryDirection;
|
||||
// Update block survivability
|
||||
|
||||
@@ -22,7 +22,7 @@ import ac.grim.grimac.utils.collisions.datatypes.NoCollisionBox;
|
||||
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
|
||||
import ac.grim.grimac.utils.data.packetentity.PacketEntityStrider;
|
||||
import ac.grim.grimac.utils.nmsutil.Materials;
|
||||
import ac.grim.grimac.utils.reflection.ViaVersionUtil;
|
||||
import ac.grim.grimac.utils.viaversion.ViaVersionUtil;
|
||||
import com.github.retrooper.packetevents.PacketEvents;
|
||||
import com.github.retrooper.packetevents.manager.server.ServerVersion;
|
||||
import com.github.retrooper.packetevents.protocol.item.ItemStack;
|
||||
@@ -378,7 +378,7 @@ public enum CollisionData implements CollisionFactory {
|
||||
new HexCollisionBox(5.0D, 0.0D, 5.0D, 11.0D, 7.0D, 11.0D),
|
||||
new HexCollisionBox(6.0D, 7.0D, 6.0D, 10.0D, 9.0D, 10.0D));
|
||||
|
||||
}, StateTypes.LANTERN, StateTypes.SOUL_LANTERN),
|
||||
}, BlockTags.LANTERNS.getStates().toArray(new StateType[0])),
|
||||
|
||||
|
||||
LECTERN((player, version, data, x, y, z) -> {
|
||||
@@ -516,7 +516,7 @@ public enum CollisionData implements CollisionFactory {
|
||||
}
|
||||
|
||||
return new HexCollisionBox(6.5D, 6.5D, 0.0D, 9.5D, 9.5D, 16.0D);
|
||||
}, StateTypes.CHAIN),
|
||||
}, Materials.getChains().toArray(new StateType[0])),
|
||||
|
||||
CHORUS_PLANT(new DynamicChorusPlant(), StateTypes.CHORUS_PLANT),
|
||||
|
||||
@@ -552,7 +552,7 @@ public enum CollisionData implements CollisionFactory {
|
||||
|
||||
STAIR(new DynamicStair(), BlockTags.STAIRS.getStates().toArray(new StateType[0])),
|
||||
|
||||
CHEST(new DynamicChest(), StateTypes.CHEST, StateTypes.TRAPPED_CHEST),
|
||||
CHEST(new DynamicChest(), Materials.getChests().toArray(new StateType[0])),
|
||||
|
||||
ENDER_CHEST(new SimpleCollisionBox(0.0625F, 0.0F, 0.0625F,
|
||||
0.9375F, 0.875F, 0.9375F, false),
|
||||
@@ -675,7 +675,7 @@ public enum CollisionData implements CollisionFactory {
|
||||
0.625, 0.625, 0.625, false),
|
||||
StateTypes.STRUCTURE_VOID),
|
||||
|
||||
END_ROD((player, version, data, x, y, z) -> getEndRod(version, data.getFacing()), StateTypes.END_ROD, StateTypes.LIGHTNING_ROD),
|
||||
END_ROD((player, version, data, x, y, z) -> getEndRod(version, data.getFacing()), Materials.getRods().toArray(new StateType[0])),
|
||||
|
||||
CAULDRON((player, version, data, x, y, z) -> {
|
||||
if (version.isNewerThan(ClientVersion.V_1_13_2)) { // changed in 19w13a, 1.14 Snapshot
|
||||
@@ -813,7 +813,7 @@ public enum CollisionData implements CollisionFactory {
|
||||
}, StateTypes.TRIPWIRE_HOOK),
|
||||
|
||||
TORCH(new HexCollisionBox(6.0D, 0.0D, 6.0D, 10.0D, 10.0D, 10.0D),
|
||||
StateTypes.TORCH, StateTypes.REDSTONE_TORCH),
|
||||
StateTypes.TORCH, StateTypes.REDSTONE_TORCH, StateTypes.COPPER_TORCH),
|
||||
|
||||
WALL_TORCH((player, version, data, x, y, z) -> switch (data.getFacing()) {
|
||||
case NORTH -> new HexCollisionBox(5.5D, 3.0D, 11.0D, 10.5D, 13.0D, 16.0D);
|
||||
@@ -822,7 +822,7 @@ public enum CollisionData implements CollisionFactory {
|
||||
case EAST -> new HexCollisionBox(0.0D, 3.0D, 5.5D, 5.0D, 13.0D, 10.5D);
|
||||
// 1.13 separates wall and normal torches, 1.12 does not
|
||||
default -> new HexCollisionBox(6.0D, 0.0D, 6.0D, 10.0D, 10.0D, 10.0D);
|
||||
}, StateTypes.WALL_TORCH, StateTypes.REDSTONE_WALL_TORCH),
|
||||
}, StateTypes.WALL_TORCH, StateTypes.REDSTONE_WALL_TORCH, StateTypes.COPPER_WALL_TORCH),
|
||||
|
||||
// 1.17 blocks
|
||||
CANDLE((player, version, data, x, y, z) -> {
|
||||
@@ -1018,6 +1018,30 @@ public enum CollisionData implements CollisionFactory {
|
||||
}
|
||||
}, StateTypes.DRIED_GHAST),
|
||||
|
||||
SHELF((player, version, data, x, y, z) -> {
|
||||
if (version.isOlderThan(ClientVersion.V_1_21_9)) {
|
||||
// ViaVersion replacement block (planks)
|
||||
return new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true);
|
||||
}
|
||||
|
||||
return switch (data.getFacing()) {
|
||||
case NORTH -> new ComplexCollisionBox(3, new HexCollisionBox(0, 12, 11, 16, 16, 13), new HexCollisionBox(0, 0, 13, 16, 16, 16), new HexCollisionBox(0, 0, 11, 16, 4, 13));
|
||||
case SOUTH -> new ComplexCollisionBox(3, new HexCollisionBox(0, 12, 3, 16, 16, 5), new HexCollisionBox(0, 0, 0, 16, 16, 3), new HexCollisionBox(0, 0, 3, 16, 4, 5));
|
||||
case WEST -> new ComplexCollisionBox(3, new HexCollisionBox(11, 12, 0, 13, 16, 16), new HexCollisionBox(13, 0, 0, 16, 16, 16), new HexCollisionBox(11, 0, 0, 13, 4, 16));
|
||||
case EAST -> new ComplexCollisionBox(3, new HexCollisionBox(3, 12, 0, 5, 16, 16), new HexCollisionBox(0, 0, 0, 3, 16, 16), new HexCollisionBox(3, 0, 0, 5, 4, 16));
|
||||
default -> throw new IllegalStateException("Unexpected value: " + data.getFacing());
|
||||
};
|
||||
}, BlockTags.WOODEN_SHELVES.getStates().toArray(new StateType[0])),
|
||||
|
||||
COPPER_GOLEM_STATUE((player, version, data, x, y, z) -> {
|
||||
if (version.isOlderThan(ClientVersion.V_1_21_9)) {
|
||||
// ViaVersion replacement block (copper block)
|
||||
return new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true);
|
||||
}
|
||||
|
||||
return new HexCollisionBox(3, 0, 3, 13, 14, 13);
|
||||
}, BlockTags.COPPER_GOLEM_STATUES.getStates().toArray(new StateType[0])),
|
||||
|
||||
DEFAULT(new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true), StateTypes.STONE);
|
||||
|
||||
// This should be an array... but a hashmap will do for now...
|
||||
@@ -1049,7 +1073,7 @@ public enum CollisionData implements CollisionFactory {
|
||||
this.materials = mList.toArray(new StateType[0]);
|
||||
}
|
||||
|
||||
private static CollisionBox getAmethystBox(ClientVersion version, com.github.retrooper.packetevents.protocol.world.BlockFace facing, int param_0, int param_1) {
|
||||
private static CollisionBox getAmethystBox(ClientVersion version, BlockFace facing, int param_0, int param_1) {
|
||||
if (version.isOlderThanOrEquals(ClientVersion.V_1_16_4))
|
||||
return NoCollisionBox.INSTANCE;
|
||||
|
||||
@@ -1146,7 +1170,11 @@ public enum CollisionData implements CollisionFactory {
|
||||
// Would pre-computing all states be worth the memory cost? I doubt it
|
||||
public static CollisionData getData(StateType state) { // TODO: Find a better hack for lava and scaffolding
|
||||
// What the fuck mojang, why put noCollision() and then give PITCHER_CROP collision?
|
||||
return state.isSolid() || state == StateTypes.LAVA || state == StateTypes.SCAFFOLDING || state == StateTypes.PITCHER_CROP || state == StateTypes.HEAVY_CORE || state == StateTypes.PALE_MOSS_CARPET || BlockTags.WALL_HANGING_SIGNS.contains(state) ? rawLookupMap.getOrDefault(state, DEFAULT) : NO_COLLISION;
|
||||
return state.isSolid() || state == StateTypes.LAVA || state == StateTypes.SCAFFOLDING
|
||||
|| state == StateTypes.PITCHER_CROP || state == StateTypes.HEAVY_CORE
|
||||
|| state == StateTypes.PALE_MOSS_CARPET || BlockTags.WALL_HANGING_SIGNS.contains(state)
|
||||
|| BlockTags.COPPER_GOLEM_STATUES.contains(state)
|
||||
? rawLookupMap.getOrDefault(state, DEFAULT) : NO_COLLISION;
|
||||
}
|
||||
|
||||
// TODO: This is wrong if a block doesn't have any hitbox and isn't specified, light block?
|
||||
|
||||
Loaded 100 of 222 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user