Merge latest version from upstream
This commit was merged in pull request #2.
This commit is contained in:
commit
252deb7fd7
182 files changed
+3116
-1674
No files matched your search
@@ -17,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
|
||||
@@ -3,6 +3,11 @@ name: Reusable Build and Publish
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
runner:
|
||||
description: 'The runner to use for the build job.'
|
||||
required: false
|
||||
type: string
|
||||
default: 'ubuntu-latest'
|
||||
is-release-build:
|
||||
description: 'True if this is a release build, which affects versioning and publishing.'
|
||||
required: true
|
||||
@@ -36,7 +41,7 @@ on:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ${{ inputs.runner }}
|
||||
permissions:
|
||||
# Grant permission to write contents (for creating tags/refs)
|
||||
contents: write
|
||||
@@ -106,7 +111,7 @@ jobs:
|
||||
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
|
||||
@@ -124,14 +129,14 @@ jobs:
|
||||
fi
|
||||
|
||||
- name: Upload Bukkit Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: grimac-bukkit
|
||||
path: ${{ github.workspace }}/bukkit/build/libs/grimac-bukkit-${{ env.BUILD_VERSION }}.jar
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Upload Fabric Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: grimac-fabric
|
||||
path: ${{ github.workspace }}/fabric/build/libs/grimac-fabric-${{ env.BUILD_VERSION }}.jar
|
||||
@@ -152,7 +157,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 }}
|
||||
@@ -192,13 +197,13 @@ 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
|
||||
|
||||
files: bukkit/build/libs/grimac-bukkit-${{ env.BUILD_VERSION }}.jar
|
||||
name: LightningGrim Anticheat (Bukkit) ${{ env.BUILD_VERSION }}-b${{ steps.buildnumber.outputs.build_number }}
|
||||
version: ${{ env.BUILD_VERSION }}
|
||||
version: ${{ env.BUILD_VERSION }}-b${{ steps.buildnumber.outputs.build_number }}
|
||||
version-type: ${{ inputs.release-version-type }}
|
||||
changelog: ${{ steps.changelog.outputs.notes }}
|
||||
loaders: |
|
||||
@@ -207,7 +212,7 @@ jobs:
|
||||
paper
|
||||
folia
|
||||
purpur
|
||||
game-versions: '>=1.7 <=1.21.10'
|
||||
game-versions: '>=1.7 <=1.21.11'
|
||||
|
||||
- name: Publish Fabric to Modrinth
|
||||
if: ${{ inputs.publish-artifacts == true && env.MODRINTH_ID != '' && env.MODRINTH_TOKEN != '' }}
|
||||
@@ -219,7 +224,7 @@ jobs:
|
||||
|
||||
files: fabric/build/libs/grimac-fabric-${{ env.BUILD_VERSION }}.jar
|
||||
name: LightningGrim Anticheat (Fabric) ${{ env.BUILD_VERSION }}-b${{ steps.buildnumber.outputs.build_number }}
|
||||
version: ${{ env.BUILD_VERSION }}
|
||||
version: ${{ env.BUILD_VERSION }}-b${{ steps.buildnumber.outputs.build_number }}
|
||||
version-type: ${{ inputs.release-version-type }}
|
||||
changelog: ${{ steps.changelog.outputs.notes }}
|
||||
loaders: |
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -12,6 +12,11 @@ on:
|
||||
description: 'Upload to Modrinth & API? (Uncheck for a dry run)'
|
||||
type: boolean
|
||||
default: true
|
||||
runner:
|
||||
description: 'The runner to use for the build job.'
|
||||
required: false
|
||||
type: string
|
||||
default: 'tenki-standard-autoscale'
|
||||
push:
|
||||
branches:
|
||||
- 'main'
|
||||
@@ -21,8 +26,6 @@ on:
|
||||
- 'release/**'
|
||||
- 'lightning'
|
||||
paths:
|
||||
- '.github/workflows/**'
|
||||
- 'buildSrc/**'
|
||||
- 'bukkit/**'
|
||||
- 'common/**'
|
||||
- 'fabric/**'
|
||||
@@ -34,6 +37,7 @@ jobs:
|
||||
call-build-workflow:
|
||||
uses: ./.github/workflows/build-and-publish.yml
|
||||
with:
|
||||
runner: ${{ github.event.inputs.runner || 'tenki-standard-autoscale' }}
|
||||
is-release-build: false
|
||||
release-version-type: alpha # Dev builds are always 'alpha'
|
||||
run-build-scan: ${{ github.event.inputs.run-build-scan == 'true' }}
|
||||
|
||||
@@ -8,6 +8,7 @@ jobs:
|
||||
call-release-workflow:
|
||||
uses: ./.github/workflows/build-and-publish.yml
|
||||
with:
|
||||
runner: 'tenki-standard-autoscale'
|
||||
is-release-build: true
|
||||
release-version-type: release # Official releases are 'release'
|
||||
# Use the body of the GitHub Release as the changelog
|
||||
|
||||
@@ -73,3 +73,5 @@ nb-configuration.xml
|
||||
##############################
|
||||
run/
|
||||
fabric/run/
|
||||
|
||||
.ignored/
|
||||
@@ -33,23 +33,26 @@ A premium version is planned, which will offer additional subscription-based pai
|
||||
- Checks for PacketOrder (patches lots of noslow, autoblocks, etc...)
|
||||
- Checks for Inventory interactions (prevents using crafting slots as extra storage, moving while having inventory open, etc...)
|
||||
|
||||
## Requirements & Installation
|
||||
|
||||
- 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
|
||||
|
||||
## 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, or Folia, 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 LightningGrim can exempt bedrock players. LightningGrim
|
||||
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.
|
||||
See [Contributing](CONTRIBUTING.md) for more information about contributing and what our guidelines
|
||||
are.
|
||||
|
||||
## Developer Plugin API
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
plugins {
|
||||
`kotlin-dsl`
|
||||
kotlin("plugin.serialization") version "1.9.0" // Use the same Kotlin version as your main project
|
||||
kotlin("plugin.serialization") version "2.2.20"
|
||||
}
|
||||
|
||||
repositories {
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
import org.gradle.api.artifacts.dsl.RepositoryHandler
|
||||
import org.gradle.api.artifacts.repositories.MavenArtifactRepository
|
||||
import org.gradle.api.artifacts.repositories.InclusiveRepositoryContentDescriptor
|
||||
import org.gradle.kotlin.dsl.maven
|
||||
import versioning.BuildConfig
|
||||
|
||||
/**
|
||||
* Registers a repository with strict content exclusivity.
|
||||
*
|
||||
* Logic:
|
||||
* 1. If BuildConfig.mavenLocalOverride is true, checks Maven Local first.
|
||||
* 2. Checks the provided remote URL second.
|
||||
* 3. Enforces strict filtering so Gradle never checks this repo for other deps,
|
||||
* and never checks other repos (like Central) for these deps.
|
||||
*/
|
||||
fun RepositoryHandler.exclusive(
|
||||
url: String,
|
||||
repoConfig: MavenArtifactRepository.() -> Unit = {},
|
||||
filterConfig: InclusiveRepositoryContentDescriptor.() -> Unit
|
||||
) {
|
||||
// We define the repositories inside the function scope so they are added to the handler
|
||||
val remote = maven(url, repoConfig)
|
||||
|
||||
exclusive(remote, filterConfig)
|
||||
}
|
||||
|
||||
fun RepositoryHandler.exclusive(
|
||||
remote: MavenArtifactRepository,
|
||||
filterConfig: InclusiveRepositoryContentDescriptor.() -> Unit
|
||||
) {
|
||||
// We access BuildConfig here again to check for local override
|
||||
val local = if (BuildConfig.mavenLocalOverride) mavenLocal() else null
|
||||
|
||||
exclusiveContent {
|
||||
if (local != null) {
|
||||
forRepositories(local, remote)
|
||||
} else {
|
||||
forRepositories(remote)
|
||||
}
|
||||
filter(filterConfig)
|
||||
}
|
||||
}
|
||||
@@ -56,7 +56,7 @@ object BuildConfig {
|
||||
_shadePE = resolveBool(project, "shadePE", altKey = "SHADE_PE", default = true)
|
||||
_relocate = resolveBool(project, "relocate", altKey = "RELOCATE_JAR", default = true)
|
||||
_release = resolveBool(project, "release", default = false)
|
||||
_mavenLocalOverride = resolveBool(project, "mavenLocalOverride", default = false)
|
||||
_mavenLocalOverride = resolveBool(project, "mavenLocalOverride", altKey = "MAVEN_LOCAL_OVERRIDE", default = false)
|
||||
}
|
||||
|
||||
// Unified resolution logic (System > Gradle > Env)
|
||||
|
||||
+19
-54
@@ -10,69 +10,32 @@ plugins {
|
||||
}
|
||||
|
||||
repositories {
|
||||
if (BuildConfig.mavenLocalOverride) {
|
||||
mavenLocal()
|
||||
// 1. Fallback for non-exclusive deps (e.g. Maven Central deps)
|
||||
if (BuildConfig.mavenLocalOverride) mavenLocal()
|
||||
|
||||
// 2. Exclusive Repositories (One HTTP request per dep)
|
||||
exclusive("https://repo.papermc.io/repository/maven-public/", { name = "papermc" }) {
|
||||
includeGroup("io.papermc.paper")
|
||||
includeGroup("net.md-5")
|
||||
}
|
||||
|
||||
// For paper-api
|
||||
exclusiveContent {
|
||||
forRepository {
|
||||
maven {
|
||||
name = "papermc"
|
||||
url = uri("https://repo.papermc.io/repository/maven-public/")
|
||||
}
|
||||
}
|
||||
filter {
|
||||
includeGroup("io.papermc.paper")
|
||||
includeGroup("net.md-5")
|
||||
}
|
||||
exclusive("https://libraries.minecraft.net", { mavenContent { releasesOnly() } }) {
|
||||
includeModule("com.mojang", "brigadier")
|
||||
}
|
||||
|
||||
exclusiveContent {
|
||||
forRepository {
|
||||
maven("https://libraries.minecraft.net") { // Brigadier
|
||||
mavenContent { releasesOnly() }
|
||||
}
|
||||
}
|
||||
filter {
|
||||
includeModule("com.mojang", "brigadier")
|
||||
}
|
||||
exclusive("https://repo.extendedclip.com/content/repositories/placeholderapi/") {
|
||||
includeGroup("me.clip")
|
||||
}
|
||||
|
||||
// For placeholderapi
|
||||
exclusiveContent {
|
||||
forRepository {
|
||||
maven("https://repo.extendedclip.com/content/repositories/placeholderapi/")
|
||||
}
|
||||
filter {
|
||||
includeGroup("me.clip")
|
||||
}
|
||||
exclusive("https://repo.grim.ac/snapshots") {
|
||||
includeGroup("ac.grim.grimac")
|
||||
includeGroup("com.github.retrooper")
|
||||
}
|
||||
|
||||
// For GrimAPI and PacketEvents (transitive from :common, direct in :bukkit)
|
||||
exclusiveContent {
|
||||
forRepository {
|
||||
maven("https://repo.grim.ac/snapshots")
|
||||
}
|
||||
filter {
|
||||
includeGroup("ac.grim.grimac")
|
||||
includeGroup("com.github.retrooper")
|
||||
}
|
||||
exclusive("https://nexus.scarsz.me/content/repositories/releases", { mavenContent { releasesOnly() } }) {
|
||||
includeGroup("github.scarsz")
|
||||
}
|
||||
|
||||
// For Configuralize (transitive from :common)
|
||||
exclusiveContent {
|
||||
forRepository {
|
||||
maven("https://nexus.scarsz.me/content/repositories/releases") {
|
||||
mavenContent { releasesOnly() }
|
||||
}
|
||||
}
|
||||
filter {
|
||||
includeGroup("github.scarsz")
|
||||
}
|
||||
}
|
||||
|
||||
// Maven Central Fallback
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
@@ -87,6 +50,7 @@ dependencies {
|
||||
}
|
||||
implementation(libs.cloud.paper)
|
||||
implementation(libs.adventure.platform.bukkit)
|
||||
implementation(libs.grim.bukkit.internal)
|
||||
|
||||
implementation(project(":common"))
|
||||
shadow(project(":common"))
|
||||
@@ -115,6 +79,7 @@ bukkit {
|
||||
"floodgate",
|
||||
"FastLogin",
|
||||
"PlaceholderAPI",
|
||||
"ZNPCsPlus",
|
||||
)
|
||||
|
||||
permissions {
|
||||
@@ -193,7 +158,7 @@ publishing.publications.create<MavenPublication>("maven") {
|
||||
|
||||
tasks {
|
||||
runServer {
|
||||
minecraftVersion("1.21.10")
|
||||
minecraftVersion("1.21.11")
|
||||
}
|
||||
|
||||
shadowJar {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package ac.grim.grimac.platform.bukkit;
|
||||
|
||||
import ac.grim.grimac.GrimAPI;
|
||||
import ac.grim.grimac.platform.api.Platform;
|
||||
import ac.grim.grimac.platform.api.PlatformServer;
|
||||
import ac.grim.grimac.platform.api.sender.Sender;
|
||||
import io.github.retrooper.packetevents.util.SpigotReflectionUtil;
|
||||
@@ -7,6 +9,7 @@ import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
|
||||
|
||||
public class BukkitPlatformServer implements PlatformServer {
|
||||
|
||||
@Override
|
||||
@@ -32,6 +35,10 @@ public class BukkitPlatformServer implements PlatformServer {
|
||||
|
||||
@Override
|
||||
public double getTPS() {
|
||||
// Folia throws UnsupportedOperationException on calling getTPS(), there is no API for getting TPS on Folia
|
||||
if (GrimAPI.INSTANCE.getPlatform() == Platform.FOLIA) {
|
||||
return Double.NaN;
|
||||
}
|
||||
return SpigotReflectionUtil.getTPS();
|
||||
}
|
||||
}
|
||||
+108
-20
@@ -1,20 +1,24 @@
|
||||
package ac.grim.grimac.platform.bukkit;
|
||||
|
||||
import ac.grim.grimac.GrimAPI;
|
||||
import ac.grim.grimac.GrimExternalAPI;
|
||||
import ac.grim.grimac.api.GrimAPIProvider;
|
||||
import ac.grim.grimac.api.GrimAbstractAPI;
|
||||
import ac.grim.grimac.api.event.EventBus;
|
||||
import ac.grim.grimac.api.plugin.GrimPlugin;
|
||||
import ac.grim.grimac.events.GrimExtensionManager;
|
||||
import ac.grim.grimac.command.CloudCommandService;
|
||||
import ac.grim.grimac.internal.platform.bukkit.resolver.BukkitResolverRegistrar;
|
||||
import ac.grim.grimac.manager.init.Initable;
|
||||
import ac.grim.grimac.manager.init.start.ExemptOnlinePlayersOnReload;
|
||||
import ac.grim.grimac.manager.init.start.StartableInitable;
|
||||
import ac.grim.grimac.platform.api.Platform;
|
||||
import ac.grim.grimac.platform.api.PlatformLoader;
|
||||
import ac.grim.grimac.platform.api.PlatformServer;
|
||||
import ac.grim.grimac.platform.api.command.CommandService;
|
||||
import ac.grim.grimac.platform.api.manager.ItemResetHandler;
|
||||
import ac.grim.grimac.platform.api.manager.MessagePlaceHolderManager;
|
||||
import ac.grim.grimac.platform.api.manager.CommandAdapter;
|
||||
import ac.grim.grimac.platform.api.manager.PlatformPluginManager;
|
||||
import ac.grim.grimac.platform.api.manager.cloud.CloudCommandAdapter;
|
||||
import ac.grim.grimac.platform.api.player.PlatformPlayerFactory;
|
||||
import ac.grim.grimac.platform.api.scheduler.PlatformScheduler;
|
||||
import ac.grim.grimac.platform.api.sender.Sender;
|
||||
@@ -28,11 +32,11 @@ import ac.grim.grimac.platform.bukkit.manager.BukkitParserDescriptorFactory;
|
||||
import ac.grim.grimac.platform.bukkit.manager.BukkitPermissionRegistrationManager;
|
||||
import ac.grim.grimac.platform.bukkit.manager.BukkitPlatformPluginManager;
|
||||
import ac.grim.grimac.platform.bukkit.player.BukkitPlatformPlayerFactory;
|
||||
import ac.grim.grimac.platform.bukkit.resolver.BukkitResolverRegistrar;
|
||||
import ac.grim.grimac.platform.bukkit.scheduler.bukkit.BukkitPlatformScheduler;
|
||||
import ac.grim.grimac.platform.bukkit.scheduler.folia.FoliaPlatformScheduler;
|
||||
import ac.grim.grimac.platform.bukkit.sender.BukkitSenderFactory;
|
||||
import ac.grim.grimac.platform.bukkit.utils.placeholder.PlaceholderAPIExpansion;
|
||||
import ac.grim.grimac.utils.anticheat.LogUtil;
|
||||
import ac.grim.grimac.utils.lazy.LazyHolder;
|
||||
import com.github.retrooper.packetevents.PacketEventsAPI;
|
||||
import io.github.retrooper.packetevents.factory.spigot.SpigotPacketEventsBuilder;
|
||||
@@ -47,7 +51,6 @@ import org.incendo.cloud.brigadier.CloudBrigadierManager;
|
||||
import org.incendo.cloud.bukkit.CloudBukkitCapabilities;
|
||||
import org.incendo.cloud.execution.ExecutionCoordinator;
|
||||
import org.incendo.cloud.paper.LegacyPaperCommandManager;
|
||||
import org.incendo.cloud.setting.Configurable;
|
||||
|
||||
|
||||
public final class GrimACBukkitLoaderPlugin extends JavaPlugin implements PlatformLoader {
|
||||
@@ -57,11 +60,11 @@ public final class GrimACBukkitLoaderPlugin extends JavaPlugin implements Platfo
|
||||
private final LazyHolder<PlatformScheduler> scheduler = LazyHolder.simple(this::createScheduler);
|
||||
private final LazyHolder<PacketEventsAPI<?>> packetEvents = LazyHolder.simple(() -> SpigotPacketEventsBuilder.build(this));
|
||||
private final LazyHolder<BukkitSenderFactory> senderFactory = LazyHolder.simple(BukkitSenderFactory::new);
|
||||
private final LazyHolder<CommandManager<Sender>> commandManager = LazyHolder.simple(this::createCommandManager);
|
||||
private final LazyHolder<ItemResetHandler> itemResetHandler = LazyHolder.simple(BukkitItemResetHandler::new);
|
||||
private final LazyHolder<CommandService> commandService = LazyHolder.simple(this::createCommandService);
|
||||
private final CloudCommandAdapter commandAdapter = new BukkitParserDescriptorFactory();
|
||||
|
||||
@Getter private final PlatformPlayerFactory platformPlayerFactory = new BukkitPlatformPlayerFactory();
|
||||
@Getter private final CommandAdapter commandAdapter = new BukkitParserDescriptorFactory();
|
||||
@Getter private final PlatformPluginManager pluginManager = new BukkitPlatformPluginManager();
|
||||
@Getter private final GrimPlugin plugin;
|
||||
@Getter private final PlatformServer platformServer = new BukkitPlatformServer();
|
||||
@@ -69,9 +72,8 @@ public final class GrimACBukkitLoaderPlugin extends JavaPlugin implements Platfo
|
||||
@Getter private final BukkitPermissionRegistrationManager permissionManager = new BukkitPermissionRegistrationManager();
|
||||
|
||||
public GrimACBukkitLoaderPlugin() {
|
||||
GrimExtensionManager extensionManager = GrimAPI.INSTANCE.getExtensionManager();
|
||||
BukkitResolverRegistrar registrar = new BukkitResolverRegistrar(extensionManager);
|
||||
registrar.registerAll();
|
||||
BukkitResolverRegistrar registrar = new BukkitResolverRegistrar();
|
||||
registrar.registerAll(GrimAPI.INSTANCE.getExtensionManager());
|
||||
this.plugin = registrar.resolvePlugin(this);
|
||||
}
|
||||
|
||||
@@ -116,13 +118,13 @@ public final class GrimACBukkitLoaderPlugin extends JavaPlugin implements Platfo
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandManager<Sender> getCommandManager() {
|
||||
return commandManager.get();
|
||||
public ItemResetHandler getItemResetHandler() {
|
||||
return itemResetHandler.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemResetHandler getItemResetHandler() {
|
||||
return itemResetHandler.get();
|
||||
public CommandService getCommandService() {
|
||||
return commandService.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -132,25 +134,111 @@ public final class GrimACBukkitLoaderPlugin extends JavaPlugin implements Platfo
|
||||
|
||||
@Override
|
||||
public void registerAPIService() {
|
||||
GrimAPIProvider.init(GrimAPI.INSTANCE.getExternalAPI());
|
||||
Bukkit.getServicesManager().register(GrimAbstractAPI.class, GrimAPI.INSTANCE.getExternalAPI(), GrimACBukkitLoaderPlugin.LOADER, ServicePriority.Normal);
|
||||
final GrimExternalAPI externalAPI = GrimAPI.INSTANCE.getExternalAPI();
|
||||
final EventBus eventBus = externalAPI.getEventBus();
|
||||
final ac.grim.grimac.api.plugin.GrimPlugin context = GrimAPI.INSTANCE.getGrimPlugin();
|
||||
|
||||
eventBus.subscribe(context, ac.grim.grimac.api.event.events.GrimJoinEvent.class, (event) -> {
|
||||
ac.grim.grimac.api.events.GrimJoinEvent bukkitEvent =
|
||||
new ac.grim.grimac.api.events.GrimJoinEvent(event.getUser());
|
||||
|
||||
Bukkit.getPluginManager().callEvent(bukkitEvent);
|
||||
});
|
||||
|
||||
eventBus.subscribe(context, ac.grim.grimac.api.event.events.GrimQuitEvent.class, (event) -> {
|
||||
ac.grim.grimac.api.events.GrimQuitEvent bukkitEvent =
|
||||
new ac.grim.grimac.api.events.GrimQuitEvent(event.getUser());
|
||||
|
||||
Bukkit.getPluginManager().callEvent(bukkitEvent);
|
||||
});
|
||||
|
||||
eventBus.subscribe(context, ac.grim.grimac.api.event.events.GrimReloadEvent.class, (event) -> {
|
||||
ac.grim.grimac.api.events.GrimReloadEvent bukkitEvent =
|
||||
new ac.grim.grimac.api.events.GrimReloadEvent(event.isSuccess());
|
||||
|
||||
Bukkit.getPluginManager().callEvent(bukkitEvent);
|
||||
});
|
||||
|
||||
eventBus.subscribe(context, ac.grim.grimac.api.event.events.FlagEvent.class, (event) -> {
|
||||
ac.grim.grimac.api.events.FlagEvent bukkitEvent =
|
||||
new ac.grim.grimac.api.events.FlagEvent(
|
||||
event.getUser(),
|
||||
event.getCheck(),
|
||||
event.getVerbose()
|
||||
);
|
||||
|
||||
Bukkit.getPluginManager().callEvent(bukkitEvent);
|
||||
|
||||
if (bukkitEvent.isCancelled()) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
});
|
||||
|
||||
eventBus.subscribe(context, ac.grim.grimac.api.event.events.CommandExecuteEvent.class, (event) -> {
|
||||
ac.grim.grimac.api.events.CommandExecuteEvent bukkitEvent =
|
||||
new ac.grim.grimac.api.events.CommandExecuteEvent(
|
||||
event.getUser(),
|
||||
event.getCheck(),
|
||||
event.getVerbose(),
|
||||
event.getCommand()
|
||||
);
|
||||
|
||||
Bukkit.getPluginManager().callEvent(bukkitEvent);
|
||||
|
||||
if (bukkitEvent.isCancelled()) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
});
|
||||
|
||||
eventBus.subscribe(context, ac.grim.grimac.api.event.events.CompletePredictionEvent.class, (event) -> {
|
||||
// Note: New event doesn't have verbose, passing null or check name is standard fallback
|
||||
ac.grim.grimac.api.events.CompletePredictionEvent bukkitEvent =
|
||||
new ac.grim.grimac.api.events.CompletePredictionEvent(
|
||||
event.getUser(),
|
||||
event.getCheck(),
|
||||
"",
|
||||
event.getOffset()
|
||||
);
|
||||
|
||||
Bukkit.getPluginManager().callEvent(bukkitEvent);
|
||||
|
||||
if (bukkitEvent.isCancelled()) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
});
|
||||
|
||||
GrimAPIProvider.init(externalAPI);
|
||||
Bukkit.getServicesManager().register(GrimAbstractAPI.class, externalAPI, GrimACBukkitLoaderPlugin.LOADER, ServicePriority.Normal);
|
||||
}
|
||||
|
||||
private PlatformScheduler createScheduler() {
|
||||
return GrimAPI.INSTANCE.getPlatform() == Platform.FOLIA ? new FoliaPlatformScheduler() : new BukkitPlatformScheduler();
|
||||
}
|
||||
|
||||
private CommandManager<Sender> createCommandManager() {
|
||||
private CommandService createCommandService() {
|
||||
try {
|
||||
return new CloudCommandService(this::createCloudCommandManager, commandAdapter);
|
||||
} catch (Throwable t) {
|
||||
LogUtil.warn("CRITICAL: Failed to initialize Command Framework. " +
|
||||
"Grim will continue to run with no commands.", t);
|
||||
return () -> {};
|
||||
}
|
||||
}
|
||||
|
||||
private CommandManager<Sender> createCloudCommandManager() {
|
||||
LegacyPaperCommandManager<Sender> manager = new LegacyPaperCommandManager<>(
|
||||
this,
|
||||
ExecutionCoordinator.simpleCoordinator(),
|
||||
senderFactory.get()
|
||||
);
|
||||
if (manager.hasCapability(CloudBukkitCapabilities.NATIVE_BRIGADIER)) {
|
||||
manager.registerBrigadier();
|
||||
CloudBrigadierManager<Sender, ?> cbm = manager.brigadierManager();
|
||||
Configurable<BrigadierSetting> settings = cbm.settings();
|
||||
settings.set(BrigadierSetting.FORCE_EXECUTABLE, true);
|
||||
try {
|
||||
manager.registerBrigadier();
|
||||
CloudBrigadierManager<Sender, ?> cbm = manager.brigadierManager();
|
||||
cbm.settings().set(BrigadierSetting.FORCE_EXECUTABLE, true);
|
||||
} catch (Throwable t) {
|
||||
LogUtil.error("Failed to register Brigadier native completions. Falling back to standard completions.", t);
|
||||
}
|
||||
} else if (manager.hasCapability(CloudBukkitCapabilities.ASYNCHRONOUS_COMPLETION)) {
|
||||
manager.registerAsynchronousCompletions();
|
||||
}
|
||||
|
||||
+27
-2
@@ -8,6 +8,7 @@ import ac.grim.grimac.platform.bukkit.utils.reflection.PaperUtils;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.anticheat.LogUtil;
|
||||
import ac.grim.grimac.utils.lists.HookedListWrapper;
|
||||
import ac.grim.grimac.utils.reflection.ReflectionUtils;
|
||||
import com.github.retrooper.packetevents.PacketEvents;
|
||||
import com.github.retrooper.packetevents.manager.server.ServerVersion;
|
||||
import com.github.retrooper.packetevents.util.reflection.Reflection;
|
||||
@@ -25,6 +26,20 @@ import java.util.List;
|
||||
@SuppressWarnings(value = {"unchecked", "deprecated"})
|
||||
public class BukkitTickEndEvent extends AbstractTickEndEvent implements Listener {
|
||||
|
||||
private Boolean getLateBindState() {
|
||||
Class<?> spigotConfig = ReflectionUtils.getClass("org.spigotmc.SpigotConfig");
|
||||
// ReflectionUtils.getField(class, name) handles the loop and setAccessible
|
||||
Field field = ReflectionUtils.getField(spigotConfig, "lateBind");
|
||||
|
||||
if (field == null) return null;
|
||||
|
||||
try {
|
||||
return (boolean) field.get(null);
|
||||
} catch (Exception ignored) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
if (!super.shouldInjectEndTick()) {
|
||||
@@ -38,9 +53,18 @@ public class BukkitTickEndEvent extends AbstractTickEndEvent implements Listener
|
||||
PaperUtils.registerTickEndEvent(this, this::tickAllFoliaPlayers);
|
||||
return;
|
||||
}
|
||||
// if it fails to inject, try to use paper's event
|
||||
if (!injectWithReflection() && !PaperUtils.registerTickEndEvent(this, this::tickAllPlayers)) {
|
||||
// if it fails to register Paper event, try to inject via reflection
|
||||
if (!PaperUtils.registerTickEndEvent(this, this::tickAllPlayers) && !injectWithReflection()) {
|
||||
LogUtil.error("Failed to inject into the end of tick event!");
|
||||
|
||||
if (PacketEvents.getAPI().getServerManager().getVersion().isOlderThan(ServerVersion.V_1_14_4)) {
|
||||
Boolean lateBind = getLateBindState();
|
||||
if (lateBind == null) {
|
||||
LogUtil.error("Failed to determine the late-bind state. Perhaps you are using a custom server fork? Check the fork configuration for a late-bind option and disable it.");
|
||||
} else if (lateBind) {
|
||||
LogUtil.error("Injection failed because the late-bind option is enabled. Disable it in spigot.yml.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,6 +89,7 @@ public class BukkitTickEndEvent extends AbstractTickEndEvent implements Listener
|
||||
// Inject so we can add the final transaction pre-flush event
|
||||
try {
|
||||
Object connection = SpigotReflectionUtil.getMinecraftServerConnectionInstance();
|
||||
if (connection == null) return false;
|
||||
|
||||
Field connectionsList = Reflection.getField(connection.getClass(), List.class, 1);
|
||||
List<Object> endOfTickObject = (List<Object>) connectionsList.get(connection);
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
package ac.grim.grimac.platform.bukkit.manager;
|
||||
|
||||
import ac.grim.grimac.platform.api.command.PlayerSelector;
|
||||
import ac.grim.grimac.platform.api.manager.CommandAdapter;
|
||||
import ac.grim.grimac.platform.api.manager.cloud.CloudCommandAdapter;
|
||||
import ac.grim.grimac.platform.api.sender.Sender;
|
||||
import ac.grim.grimac.platform.bukkit.command.BukkitPlayerSelectorParser;
|
||||
import org.bukkit.Bukkit;
|
||||
@@ -16,7 +16,7 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class BukkitParserDescriptorFactory implements CommandAdapter {
|
||||
public class BukkitParserDescriptorFactory implements CloudCommandAdapter {
|
||||
|
||||
// Parser is kept as a field because its stateless
|
||||
private final BukkitPlayerSelectorParser<Sender> bukkitPlayerSelectorParser = new BukkitPlayerSelectorParser<>();
|
||||
|
||||
+11
@@ -8,6 +8,7 @@ import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.UUID;
|
||||
|
||||
@@ -54,4 +55,14 @@ public class BukkitPlatformPlayerFactory extends AbstractPlatformPlayerFactory<P
|
||||
OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(name);
|
||||
return new BukkitOfflinePlatformPlayer(offlinePlayer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<OfflinePlatformPlayer> getOfflinePlayers() {
|
||||
OfflinePlayer[] offlinePlayers = Bukkit.getOfflinePlayers();
|
||||
BukkitOfflinePlatformPlayer[] offlinePlatformPlayers = new BukkitOfflinePlatformPlayer[offlinePlayers.length];
|
||||
for (int i = 0; i < offlinePlayers.length; i++) {
|
||||
offlinePlatformPlayers[i] = new BukkitOfflinePlatformPlayer(offlinePlayers[i]);
|
||||
}
|
||||
return Arrays.asList(offlinePlatformPlayers);
|
||||
}
|
||||
}
|
||||
-111
@@ -1,111 +0,0 @@
|
||||
package ac.grim.grimac.platform.bukkit.resolver;
|
||||
|
||||
import ac.grim.grimac.api.plugin.BasicGrimPlugin;
|
||||
import ac.grim.grimac.api.plugin.GrimPlugin;
|
||||
import ac.grim.grimac.events.GrimExtensionManager;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.PluginDescriptionFile;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* Manages the registration of Bukkit-specific resolvers with the GrimExtensionManager.
|
||||
* This class is designed to be instantiated once during plugin startup.
|
||||
*/
|
||||
public final class BukkitResolverRegistrar {
|
||||
|
||||
private final GrimExtensionManager extensionManager;
|
||||
|
||||
// Cache to ensure we only create one GrimPlugin wrapper per Bukkit Plugin instance.
|
||||
private final Map<Plugin, GrimPlugin> pluginCache = new ConcurrentHashMap<>();
|
||||
|
||||
public BukkitResolverRegistrar(GrimExtensionManager extensionManager) {
|
||||
this.extensionManager = extensionManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers all the Bukkit-specific resolvers in order of performance (fastest to slowest).
|
||||
*/
|
||||
public void registerAll() {
|
||||
extensionManager.setFailureHandler(this::createFailureException);
|
||||
extensionManager.registerResolver(this::resolvePluginInstance);
|
||||
extensionManager.registerResolver(this::resolveStringName);
|
||||
extensionManager.registerResolver(this::resolveClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* The core logic for converting a Bukkit Plugin into a GrimPlugin, with caching.
|
||||
*/
|
||||
public GrimPlugin resolvePlugin(Plugin bukkitPlugin) {
|
||||
return pluginCache.computeIfAbsent(bukkitPlugin, plugin -> {
|
||||
PluginDescriptionFile desc = plugin.getDescription();
|
||||
return new BasicGrimPlugin(
|
||||
plugin.getLogger(),
|
||||
plugin.getDataFolder(),
|
||||
desc.getVersion(),
|
||||
desc.getDescription(),
|
||||
desc.getAuthors()
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Resolver for raw Plugin instances (the most common case).
|
||||
*/
|
||||
private GrimPlugin resolvePluginInstance(Object context) {
|
||||
if (context instanceof Plugin bukkitPlugin) {
|
||||
return resolvePlugin(bukkitPlugin);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 2. Resolver for String plugin names.
|
||||
*/
|
||||
private GrimPlugin resolveStringName(Object context) {
|
||||
if (context instanceof String pluginName) {
|
||||
// Use the Bukkit API to look up the plugin by name. This returns null if not found.
|
||||
Plugin bukkitPlugin = Bukkit.getPluginManager().getPlugin(pluginName);
|
||||
// We must check for null before applying the logic to avoid a NullPointerException.
|
||||
return (bukkitPlugin != null) ? resolvePlugin(bukkitPlugin) : null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 3. Resolver for Class objects.
|
||||
*/
|
||||
private GrimPlugin resolveClass(Object context) {
|
||||
if (context instanceof Class) {
|
||||
try {
|
||||
// First, resolve the Class to a Plugin instance.
|
||||
JavaPlugin providingPlugin = JavaPlugin.getProvidingPlugin((Class<?>) context);
|
||||
// Then, use our shared logic directly to get the GrimPlugin wrapper.
|
||||
return resolvePlugin(providingPlugin);
|
||||
} catch (IllegalArgumentException | IllegalStateException e) {
|
||||
// This is an expected failure if the class is not from a plugin
|
||||
// or is called from a static initializer. We simply indicate that
|
||||
// this resolver cannot handle this context.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
// This resolver only handles Classes.
|
||||
return null;
|
||||
}
|
||||
|
||||
private RuntimeException createFailureException(Object failedContext) {
|
||||
String message = """
|
||||
Failed to resolve GrimPlugin context from the provided object of type '%s'.
|
||||
|
||||
Please ensure you are passing one of the following:
|
||||
- The main instance of your plugin (e.g., 'this' from your class extending JavaPlugin).
|
||||
- The plugin name as a String (e.g., "MyPluginName").
|
||||
- Any Class from your plugin's JAR file (e.g., MyListener.class).
|
||||
- A pre-existing GrimPlugin instance.
|
||||
""".formatted(failedContext.getClass().getName());
|
||||
return new IllegalArgumentException(message);
|
||||
}
|
||||
}
|
||||
+25
-51
@@ -7,70 +7,42 @@ plugins {
|
||||
}
|
||||
|
||||
repositories {
|
||||
if (BuildConfig.mavenLocalOverride) {
|
||||
mavenLocal()
|
||||
// We still call mavenLocal() conditionally at the top for non-exclusive deps (general fallback)
|
||||
if (BuildConfig.mavenLocalOverride) mavenLocal()
|
||||
|
||||
// Grim API & PacketEvents
|
||||
exclusive("https://repo.grim.ac/snapshots") {
|
||||
includeGroup("ac.grim.grimac")
|
||||
includeGroup("com.github.retrooper")
|
||||
}
|
||||
|
||||
exclusiveContent {
|
||||
forRepository {
|
||||
maven("https://repo.grim.ac/snapshots") // Grim API
|
||||
}
|
||||
filter {
|
||||
includeGroup("ac.grim.grimac")
|
||||
includeGroup("com.github.retrooper")
|
||||
}
|
||||
// ViaVersion
|
||||
exclusive("https://repo.viaversion.com", { mavenContent { releasesOnly() } }) {
|
||||
includeGroup("com.viaversion")
|
||||
}
|
||||
|
||||
exclusiveContent {
|
||||
forRepository {
|
||||
maven("https://repo.viaversion.com") { // ViaVersion
|
||||
mavenContent { releasesOnly() }
|
||||
}
|
||||
}
|
||||
filter {
|
||||
includeGroup("com.viaversion")
|
||||
}
|
||||
// Configuralize
|
||||
exclusive("https://nexus.scarsz.me/content/repositories/releases", { mavenContent { releasesOnly() } }) {
|
||||
includeGroup("github.scarsz")
|
||||
}
|
||||
|
||||
exclusiveContent {
|
||||
forRepository {
|
||||
maven("https://nexus.scarsz.me/content/repositories/releases") { // Configuralize
|
||||
mavenContent { releasesOnly() }
|
||||
}
|
||||
}
|
||||
filter {
|
||||
includeGroup("github.scarsz")
|
||||
}
|
||||
// Cumulus
|
||||
exclusive("https://repo.opencollab.dev/maven-releases/", { mavenContent { releasesOnly() } }) {
|
||||
includeGroup("org.geysermc.api")
|
||||
}
|
||||
|
||||
exclusiveContent {
|
||||
forRepository {
|
||||
maven("https://repo.opencollab.dev/maven-releases/") { // Cumulus (for Floodgate)
|
||||
mavenContent { releasesOnly() }
|
||||
}
|
||||
}
|
||||
filter {
|
||||
includeGroup("org.geysermc.api")
|
||||
}
|
||||
}
|
||||
|
||||
exclusiveContent {
|
||||
forRepository {
|
||||
maven("https://repo.opencollab.dev/maven-snapshots/") { // Floodgate
|
||||
mavenContent { snapshotsOnly() }
|
||||
}
|
||||
}
|
||||
filter {
|
||||
includeGroup("org.geysermc.floodgate")
|
||||
includeGroup("org.geysermc.cumulus")
|
||||
includeModule("org.geysermc", "common")
|
||||
includeModule("org.geysermc", "geyser-parent")
|
||||
}
|
||||
// Floodgate
|
||||
exclusive("https://repo.opencollab.dev/maven-snapshots/", { mavenContent { snapshotsOnly() } }) {
|
||||
includeGroup("org.geysermc.floodgate")
|
||||
includeGroup("org.geysermc.cumulus")
|
||||
includeModule("org.geysermc", "common")
|
||||
includeModule("org.geysermc", "geyser-parent")
|
||||
}
|
||||
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
|
||||
dependencies {
|
||||
if (BuildConfig.shadePE) {
|
||||
api(libs.packetevents.api)
|
||||
@@ -93,6 +65,8 @@ dependencies {
|
||||
api(libs.hikaricp)
|
||||
|
||||
api(libs.grim.api)
|
||||
api(libs.grim.internal)
|
||||
compileOnly(libs.grim.internal.shims)
|
||||
|
||||
compileOnly(libs.geyser.base.api) {
|
||||
isTransitive = false // messes with guava otherwise
|
||||
|
||||
@@ -2,8 +2,8 @@ package ac.grim.grimac;
|
||||
|
||||
import ac.grim.grimac.api.event.EventBus;
|
||||
import ac.grim.grimac.api.plugin.GrimPlugin;
|
||||
import ac.grim.grimac.events.GrimExtensionManager;
|
||||
import ac.grim.grimac.events.OptimizedEventBus;
|
||||
import ac.grim.grimac.internal.plugin.resolver.GrimExtensionManager;
|
||||
import ac.grim.grimac.internal.event.OptimizedEventBus;
|
||||
import ac.grim.grimac.manager.AlertManagerImpl;
|
||||
import ac.grim.grimac.manager.DiscordManager;
|
||||
import ac.grim.grimac.manager.InitManager;
|
||||
@@ -15,20 +15,18 @@ import ac.grim.grimac.manager.violationdatabase.ViolationDatabaseManager;
|
||||
import ac.grim.grimac.platform.api.Platform;
|
||||
import ac.grim.grimac.platform.api.PlatformLoader;
|
||||
import ac.grim.grimac.platform.api.PlatformServer;
|
||||
import ac.grim.grimac.platform.api.command.CommandService;
|
||||
import ac.grim.grimac.platform.api.manager.ItemResetHandler;
|
||||
import ac.grim.grimac.platform.api.manager.MessagePlaceHolderManager;
|
||||
import ac.grim.grimac.platform.api.manager.CommandAdapter;
|
||||
import ac.grim.grimac.platform.api.manager.PermissionRegistrationManager;
|
||||
import ac.grim.grimac.platform.api.manager.PlatformPluginManager;
|
||||
import ac.grim.grimac.platform.api.player.PlatformPlayerFactory;
|
||||
import ac.grim.grimac.platform.api.scheduler.PlatformScheduler;
|
||||
import ac.grim.grimac.platform.api.sender.Sender;
|
||||
import ac.grim.grimac.platform.api.sender.SenderFactory;
|
||||
import ac.grim.grimac.utils.anticheat.PlayerDataManager;
|
||||
import ac.grim.grimac.utils.common.arguments.CommonGrimArguments;
|
||||
import ac.grim.grimac.utils.reflection.ReflectionUtils;
|
||||
import lombok.Getter;
|
||||
import org.incendo.cloud.CommandManager;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@Getter
|
||||
@@ -77,7 +75,7 @@ public final class GrimAPI {
|
||||
public void load(PlatformLoader platformLoader, Initable... platformSpecificInitables) {
|
||||
this.loader = platformLoader;
|
||||
this.violationDatabaseManager = new ViolationDatabaseManager(getGrimPlugin());
|
||||
this.initManager = new InitManager(loader.getPacketEvents(), loader::getCommandManager, platformSpecificInitables);
|
||||
this.initManager = new InitManager(loader.getPacketEvents(), platformSpecificInitables);
|
||||
this.initManager.load();
|
||||
this.initialized = true;
|
||||
}
|
||||
@@ -100,10 +98,6 @@ public final class GrimAPI {
|
||||
return loader.getPlatformPlayerFactory();
|
||||
}
|
||||
|
||||
public CommandAdapter getCommandAdapter() {
|
||||
return loader.getCommandAdapter();
|
||||
}
|
||||
|
||||
public GrimPlugin getGrimPlugin() {
|
||||
return loader.getPlugin();
|
||||
}
|
||||
@@ -128,8 +122,8 @@ public final class GrimAPI {
|
||||
return loader.getMessagePlaceHolderManager();
|
||||
}
|
||||
|
||||
public CommandManager<Sender> getCommandManager() {
|
||||
return loader.getCommandManager();
|
||||
public CommandService getCommandService() {
|
||||
return loader.getCommandService();
|
||||
}
|
||||
|
||||
private void checkInitialized() {
|
||||
|
||||
@@ -6,6 +6,7 @@ import ac.grim.grimac.api.alerts.AlertManager;
|
||||
import ac.grim.grimac.api.config.ConfigManager;
|
||||
import ac.grim.grimac.api.event.EventBus;
|
||||
import ac.grim.grimac.api.event.events.GrimReloadEvent;
|
||||
import ac.grim.grimac.api.plugin.GrimPlugin;
|
||||
import ac.grim.grimac.manager.config.ConfigManagerFileImpl;
|
||||
import ac.grim.grimac.manager.init.start.StartableInitable;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
@@ -13,6 +14,7 @@ import ac.grim.grimac.utils.anticheat.LogUtil;
|
||||
import ac.grim.grimac.utils.anticheat.MessageUtil;
|
||||
import ac.grim.grimac.utils.common.ConfigReloadObserver;
|
||||
import lombok.Getter;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -45,6 +47,11 @@ public class GrimExternalAPI implements GrimAbstractAPI, ConfigReloadObserver, S
|
||||
return api.getEventBus();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable GrimUser getGrimUser(Player player) {
|
||||
return getGrimUser(player.getUniqueId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable GrimUser getGrimUser(UUID uuid) {
|
||||
return api.getPlayerDataManager().getPlayer(uuid);
|
||||
@@ -107,6 +114,11 @@ public class GrimExternalAPI implements GrimAbstractAPI, ConfigReloadObserver, S
|
||||
return GrimAPI.INSTANCE.getTickManager().currentTick;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull GrimPlugin getGrimPlugin(@NotNull Object o) {
|
||||
return this.api.getExtensionManager().getPlugin(o);
|
||||
}
|
||||
|
||||
// on load, load the config & register the service
|
||||
public void load() {
|
||||
reload(configManagerFile);
|
||||
|
||||
@@ -60,7 +60,11 @@ public class Check extends GrimProcessor implements AbstractCheck {
|
||||
}
|
||||
|
||||
public boolean shouldModifyPackets() {
|
||||
return isEnabled && !player.disableGrim && !player.noModifyPacketPermission && !exemptPermission;
|
||||
return isEnabled
|
||||
&& !player.disableGrim
|
||||
&& !player.noModifyPacketPermission
|
||||
&& !noModifyPacketPermission
|
||||
&& !exemptPermission;
|
||||
}
|
||||
|
||||
public final void updatePermissions() {
|
||||
|
||||
@@ -6,7 +6,6 @@ import ac.grim.grimac.checks.type.PacketCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.protocol.player.GameMode;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientInteractEntity;
|
||||
|
||||
@CheckData(name = "BadPacketsC", description = "Interacted with self")
|
||||
@@ -17,15 +16,13 @@ public class BadPacketsC extends Check implements PacketCheck {
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
if (event.getPacketType() == PacketType.Play.Client.INTERACT_ENTITY) {
|
||||
if (player.gamemode == GameMode.SPECTATOR) return;
|
||||
if (new WrapperPlayClientInteractEntity(event).getEntityId() == player.entityID) {
|
||||
// Instant ban
|
||||
if (flagAndAlert() && shouldModifyPackets()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
}
|
||||
if (event.getPacketType() == PacketType.Play.Client.INTERACT_ENTITY
|
||||
&& player.cameraEntity.isSelf()
|
||||
&& new WrapperPlayClientInteractEntity(event).getEntityId() == player.entityID
|
||||
&& flagAndAlert() && shouldModifyPackets() // Instant ban
|
||||
) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,6 @@ import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.manager.server.ServerVersion;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import com.github.retrooper.packetevents.protocol.player.GameMode;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientUseItem;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -26,7 +25,7 @@ public class BadPacketsJ extends Check implements PacketCheck {
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
if (player.gamemode == GameMode.SPECTATOR) {
|
||||
if (!player.cameraEntity.isSelf()) {
|
||||
rotations.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import ac.grim.grimac.checks.type.PacketCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.protocol.player.GameMode;
|
||||
|
||||
@CheckData(name = "BadPacketsR", decay = 0.25, experimental = true)
|
||||
public class BadPacketsR extends Check implements PacketCheck {
|
||||
@@ -25,7 +24,7 @@ public class BadPacketsR extends Check implements PacketCheck {
|
||||
long ms = (player.getPlayerClockAtLeast() - clock) / 1000000L;
|
||||
long diff = (System.currentTimeMillis() - lastTransTime);
|
||||
if (diff > 2000 && ms > 2000) {
|
||||
if (positions == 0 && clock != 0 && player.gamemode != GameMode.SPECTATOR && !player.compensatedEntities.self.isDead) {
|
||||
if (positions == 0 && clock != 0 && player.cameraEntity.isSelf() && !player.compensatedEntities.self.isDead) {
|
||||
flag("time=" + ms + "ms, " + "lst=" + diff + "ms, positions=" + positions);
|
||||
} else {
|
||||
reward();
|
||||
|
||||
@@ -7,7 +7,6 @@ import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.protocol.player.GameMode;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientEntityAction;
|
||||
|
||||
@CheckData(name = "BadPacketsX", experimental = true)
|
||||
@@ -42,7 +41,7 @@ public class BadPacketsX extends Check implements PostPredictionCheck {
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
if (player.gamemode == GameMode.SPECTATOR || isTickPacket(event.getPacketType())) {
|
||||
if (!player.cameraEntity.isSelf() || isTickPacket(event.getPacketType())) {
|
||||
sprint = sneak = false;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ import ac.grim.grimac.utils.math.Vector3dm;
|
||||
import ac.grim.grimac.utils.math.VectorUtils;
|
||||
import com.github.retrooper.packetevents.protocol.attribute.Attributes;
|
||||
import com.github.retrooper.packetevents.protocol.player.DiggingAction;
|
||||
import com.github.retrooper.packetevents.protocol.player.GameMode;
|
||||
|
||||
@CheckData(name = "FarBreak", description = "Breaking blocks too far away", experimental = true)
|
||||
public class FarBreak extends Check implements BlockBreakCheck {
|
||||
@@ -20,7 +19,7 @@ public class FarBreak extends Check implements BlockBreakCheck {
|
||||
|
||||
@Override
|
||||
public void onBlockBreak(BlockBreak blockBreak) {
|
||||
if (player.gamemode == GameMode.SPECTATOR || player.inVehicle() || blockBreak.action == DiggingAction.CANCELLED_DIGGING)
|
||||
if (!player.cameraEntity.isSelf() || player.inVehicle() || blockBreak.action == DiggingAction.CANCELLED_DIGGING)
|
||||
return; // falses
|
||||
|
||||
double min = Double.MAX_VALUE;
|
||||
|
||||
@@ -9,7 +9,6 @@ import ac.grim.grimac.utils.anticheat.update.BlockBreak;
|
||||
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.player.DiggingAction;
|
||||
import com.github.retrooper.packetevents.protocol.player.GameMode;
|
||||
import com.github.retrooper.packetevents.protocol.world.BlockFace;
|
||||
import com.github.retrooper.packetevents.util.Vector3i;
|
||||
|
||||
@@ -53,7 +52,7 @@ public class MultiBreak extends Check implements BlockBreakCheck {
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
if (player.gamemode == GameMode.SPECTATOR || isTickPacket(event.getPacketType())) {
|
||||
if (!player.cameraEntity.isSelf() || isTickPacket(event.getPacketType())) {
|
||||
hasBroken = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@ import ac.grim.grimac.utils.nmsutil.ReachUtils;
|
||||
import com.github.retrooper.packetevents.protocol.attribute.Attributes;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import com.github.retrooper.packetevents.protocol.player.DiggingAction;
|
||||
import com.github.retrooper.packetevents.protocol.player.GameMode;
|
||||
import com.github.retrooper.packetevents.protocol.world.BlockFace;
|
||||
import com.github.retrooper.packetevents.util.Vector3d;
|
||||
import com.github.retrooper.packetevents.util.Vector3f;
|
||||
@@ -34,7 +33,7 @@ public class RotationBreak extends Check implements BlockBreakCheck {
|
||||
|
||||
@Override
|
||||
public void onBlockBreak(BlockBreak blockBreak) {
|
||||
if (player.gamemode == GameMode.SPECTATOR)
|
||||
if (!player.cameraEntity.isSelf())
|
||||
return; // you don't send flying packets when spectating entities
|
||||
if (player.inVehicle()) return; // falses
|
||||
if (blockBreak.action == DiggingAction.CANCELLED_DIGGING) return; // falses
|
||||
@@ -50,7 +49,7 @@ public class RotationBreak extends Check implements BlockBreakCheck {
|
||||
|
||||
@Override
|
||||
public void onPostFlyingBlockBreak(BlockBreak blockBreak) {
|
||||
if (player.gamemode == GameMode.SPECTATOR)
|
||||
if (!player.cameraEntity.isSelf())
|
||||
return; // you don't send flying packets when spectating entities
|
||||
if (player.inVehicle()) return; // falses
|
||||
if (blockBreak.action == DiggingAction.CANCELLED_DIGGING) return; // falses
|
||||
|
||||
@@ -7,7 +7,6 @@ import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.protocol.player.GameMode;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientInteractEntity;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -48,7 +47,7 @@ public class MultiInteractA extends Check implements PostPredictionCheck {
|
||||
hasInteracted = true;
|
||||
}
|
||||
|
||||
if (player.gamemode == GameMode.SPECTATOR || isTickPacket(event.getPacketType())) {
|
||||
if (!player.cameraEntity.isSelf() || isTickPacket(event.getPacketType())) {
|
||||
hasInteracted = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import ac.grim.grimac.utils.anticheat.MessageUtil;
|
||||
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.protocol.player.GameMode;
|
||||
import com.github.retrooper.packetevents.util.Vector3f;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientInteractEntity;
|
||||
|
||||
@@ -49,7 +48,7 @@ public class MultiInteractB extends Check implements PostPredictionCheck {
|
||||
hasInteracted = true;
|
||||
}
|
||||
|
||||
if (player.gamemode == GameMode.SPECTATOR || isTickPacket(event.getPacketType())) {
|
||||
if (!player.cameraEntity.isSelf() || isTickPacket(event.getPacketType())) {
|
||||
hasInteracted = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,14 +34,20 @@ import ac.grim.grimac.utils.data.packetentity.dragon.PacketEntityEnderDragonPart
|
||||
import ac.grim.grimac.utils.math.Vector3dm;
|
||||
import ac.grim.grimac.utils.nmsutil.ReachUtils;
|
||||
import ac.grim.grimac.utils.nmsutil.WorldRayTrace;
|
||||
import com.github.retrooper.packetevents.PacketEvents;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.manager.server.ServerVersion;
|
||||
import com.github.retrooper.packetevents.protocol.attribute.Attributes;
|
||||
import com.github.retrooper.packetevents.protocol.component.ComponentTypes;
|
||||
import com.github.retrooper.packetevents.protocol.component.builtin.item.ItemAttackRange;
|
||||
import com.github.retrooper.packetevents.protocol.entity.type.EntityType;
|
||||
import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes;
|
||||
import com.github.retrooper.packetevents.protocol.item.ItemStack;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import com.github.retrooper.packetevents.protocol.player.GameMode;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState;
|
||||
import com.github.retrooper.packetevents.protocol.player.InteractionHand;
|
||||
import com.github.retrooper.packetevents.util.Vector3d;
|
||||
import com.github.retrooper.packetevents.util.Vector3i;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientInteractEntity;
|
||||
@@ -64,7 +70,7 @@ public class Reach extends Check implements PacketCheck {
|
||||
private static final CheckResult NONE = new CheckResult(ResultType.NONE, "");
|
||||
// Only one flag per reach attack, per entity, per tick.
|
||||
// We store position because lastX isn't reliable on teleports.
|
||||
private final Int2ObjectMap<Vector3d> playerAttackQueue = new Int2ObjectOpenHashMap<>();
|
||||
private final Int2ObjectMap<InteractionData> playerAttackQueue = new Int2ObjectOpenHashMap<>();
|
||||
// temporarily used to prevent falses in the wall hit check
|
||||
private final Set<Vector3i> blocksChangedThisTick = new HashSet<>();
|
||||
// extra distance to raytrace beyond player reach distance so we know how far beyond the legit distance a cheater hit
|
||||
@@ -114,18 +120,25 @@ 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;
|
||||
if (entity.riding != null) return;
|
||||
|
||||
InteractionHand hand = action.getAction() == WrapperPlayClientInteractEntity.InteractAction.ATTACK ?
|
||||
InteractionHand.MAIN_HAND : action.getHand(); // attacks can be only performed with the main hand
|
||||
ItemStack itemInHand = player.inventory.getItemInHand(hand);
|
||||
|
||||
boolean tooManyAttacks = playerAttackQueue.size() > 10;
|
||||
if (!tooManyAttacks) {
|
||||
playerAttackQueue.put(action.getEntityId(), new Vector3d(player.x, player.y, player.z)); // Queue for next tick for very precise check
|
||||
playerAttackQueue.put(action.getEntityId(), new InteractionData(new Vector3d(player.x, player.y, player.z), itemInHand)); // Queue for next tick for very precise check
|
||||
}
|
||||
|
||||
boolean knownInvalid = isKnownInvalid(entity);
|
||||
boolean knownInvalid = isKnownInvalid(entity, itemInHand);
|
||||
|
||||
if ((shouldModifyPackets() && cancelImpossibleHits && knownInvalid) || tooManyAttacks) {
|
||||
event.setCancelled(true);
|
||||
@@ -148,7 +161,7 @@ public class Reach extends Check implements PacketCheck {
|
||||
// than this method. If this method flags, the other method WILL flag.
|
||||
//
|
||||
// Meaning that the other check should be the only one that flags.
|
||||
private boolean isKnownInvalid(PacketEntity reachEntity) {
|
||||
private boolean isKnownInvalid(PacketEntity reachEntity, ItemStack itemInHand) {
|
||||
// If the entity doesn't exist, or if it is exempt, or if it is dead
|
||||
if ((blacklisted.contains(reachEntity.type) || !reachEntity.isLivingEntity) && reachEntity.type != EntityTypes.END_CRYSTAL)
|
||||
return false; // exempt
|
||||
@@ -159,23 +172,24 @@ public class Reach extends Check implements PacketCheck {
|
||||
|
||||
// Filter out what we assume to be cheats
|
||||
if (cancelBuffer != 0) {
|
||||
CheckResult result = checkReach(reachEntity, new Vector3d(player.x, player.y, player.z), true);
|
||||
CheckResult result = checkReach(reachEntity, new Vector3d(player.x, player.y, player.z), itemInHand, true);
|
||||
return result.isFlag(); // If they flagged
|
||||
} else {
|
||||
SimpleCollisionBox targetBox = reachEntity.getPossibleCollisionBoxes();
|
||||
if (reachEntity.type == EntityTypes.END_CRYSTAL) {
|
||||
targetBox = new SimpleCollisionBox(reachEntity.trackedServerPosition.getPos().subtract(1, 0, 1), reachEntity.trackedServerPosition.getPos().add(1, 2, 1));
|
||||
}
|
||||
return ReachUtils.getMinReachToBox(player, targetBox) > player.compensatedEntities.self.getAttributeValue(Attributes.ENTITY_INTERACTION_RANGE);
|
||||
SimpleCollisionBox targetBox = getTargetBox(reachEntity);
|
||||
|
||||
double maxReach = applyReachModifiers(targetBox, itemInHand, !player.packetStateData.didLastMovementIncludePosition);
|
||||
|
||||
return ReachUtils.getMinReachToBox(player, targetBox) > maxReach;
|
||||
}
|
||||
}
|
||||
|
||||
private void tickBetterReachCheckWithAngle(boolean isFlying) {
|
||||
for (Int2ObjectMap.Entry<Vector3d> attack : playerAttackQueue.int2ObjectEntrySet()) {
|
||||
for (Int2ObjectMap.Entry<InteractionData> attack : playerAttackQueue.int2ObjectEntrySet()) {
|
||||
PacketEntity reachEntity = player.compensatedEntities.entityMap.get(attack.getIntKey());
|
||||
if (reachEntity == null) continue;
|
||||
|
||||
CheckResult result = checkReach(reachEntity, attack.getValue(), false);
|
||||
InteractionData interactionData = attack.getValue();
|
||||
CheckResult result = checkReach(reachEntity, interactionData.vector(), interactionData.itemInHand(), false);
|
||||
switch (result.type()) {
|
||||
case REACH -> {
|
||||
String added = ", type=" + reachEntity.type.getName().getKey();
|
||||
@@ -209,38 +223,20 @@ public class Reach extends Check implements PacketCheck {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private CheckResult checkReach(PacketEntity reachEntity, Vector3d from, boolean isPrediction) {
|
||||
SimpleCollisionBox targetBox = reachEntity.getPossibleCollisionBoxes();
|
||||
|
||||
if (reachEntity.type == EntityTypes.END_CRYSTAL) { // Hardcode end crystal box
|
||||
targetBox = new SimpleCollisionBox(reachEntity.trackedServerPosition.getPos().subtract(1, 0, 1), reachEntity.trackedServerPosition.getPos().add(1, 2, 1));
|
||||
}
|
||||
|
||||
// 1.7 and 1.8 players get a bit of extra hitbox (this is why you should use 1.8 on cross version servers)
|
||||
// Yes, this is vanilla and not uncertainty. All reach checks have this or they are wrong.
|
||||
if (player.getClientVersion().isOlderThan(ClientVersion.V_1_9)) {
|
||||
targetBox.expand(0.1f);
|
||||
}
|
||||
|
||||
targetBox.expand(threshold);
|
||||
|
||||
// This is better than adding to the reach, as 0.03 can cause a player to miss their target
|
||||
// Adds some more than 0.03 uncertainty in some cases, but a good trade off for simplicity
|
||||
//
|
||||
// Just give the uncertainty on 1.9+ clients as we have no way of knowing whether they had 0.03 movement
|
||||
// However, on 1.21.2+ we do know if they had 0.03 movement
|
||||
if (!player.packetStateData.didLastLastMovementIncludePosition || player.canSkipTicks())
|
||||
targetBox.expand(player.getMovementThreshold());
|
||||
private CheckResult checkReach(PacketEntity reachEntity, Vector3d from, ItemStack itemInHand, boolean isPrediction) {
|
||||
SimpleCollisionBox targetBox = getTargetBox(reachEntity);
|
||||
|
||||
double maxReach = applyReachModifiers(targetBox, itemInHand, !player.packetStateData.didLastLastMovementIncludePosition);
|
||||
double minDistance = Double.MAX_VALUE;
|
||||
|
||||
// will store all lookVecsAndEyeHeight pairs that landed a hit on the target entity
|
||||
// We only need to check for blocking intersections for these
|
||||
List<Pair<Vector3dm, Double>> lookVecsAndEyeHeights = new ArrayList<>();
|
||||
|
||||
final double maxReach = player.compensatedEntities.self.getAttributeValue(Attributes.ENTITY_INTERACTION_RANGE);
|
||||
// +3 would be 3 + 3 = 6, which is the pre-1.20.5 behaviour, preventing "Missed Hitbox"
|
||||
final double distance = maxReach + 3;
|
||||
|
||||
|
||||
final double[] possibleEyeHeights = player.getPossibleEyeHeights();
|
||||
final Vector3dm[] possibleLookDirs = player.getPossibleLookVectors(isPrediction);
|
||||
final Vector3dm eyePos = new Vector3dm(from.getX(), 0, from.getZ());
|
||||
@@ -309,6 +305,52 @@ public class Reach extends Check implements PacketCheck {
|
||||
return NONE;
|
||||
}
|
||||
|
||||
private SimpleCollisionBox getTargetBox(PacketEntity reachEntity) {
|
||||
if (reachEntity.type == EntityTypes.END_CRYSTAL) { // Hardcode end crystal box
|
||||
return new SimpleCollisionBox(reachEntity.trackedServerPosition.getPos().subtract(1, 0, 1), reachEntity.trackedServerPosition.getPos().add(1, 2, 1));
|
||||
}
|
||||
return reachEntity.getPossibleCollisionBoxes();
|
||||
}
|
||||
|
||||
private double applyReachModifiers(SimpleCollisionBox targetBox, ItemStack itemInHand, boolean giveMovementThreshold) {
|
||||
double maxReach;
|
||||
double hitboxMargin = threshold;
|
||||
|
||||
ItemAttackRange attackRange = null;
|
||||
|
||||
if (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_21_11) && ATTACK_RANGE_COMPONENT_EXISTS) {
|
||||
// TODO: ViaVersion support https://github.com/ViaVersion/ViaVersion/pull/4733
|
||||
attackRange = itemInHand.getComponentOr(ComponentTypes.ATTACK_RANGE, null);
|
||||
}
|
||||
|
||||
if (attackRange != null) {
|
||||
maxReach = attackRange.getMaxRange();
|
||||
hitboxMargin += attackRange.getHitboxMargin();
|
||||
} else {
|
||||
maxReach = player.compensatedEntities.self.getAttributeValue(Attributes.ENTITY_INTERACTION_RANGE);
|
||||
// 1.7 and 1.8 players get a bit of extra hitbox (this is why you should use 1.8 on cross version servers)
|
||||
// Yes, this is vanilla and not uncertainty. All reach checks have this or they are wrong.
|
||||
if (player.getClientVersion().isOlderThan(ClientVersion.V_1_9)) {
|
||||
hitboxMargin += 0.1f;
|
||||
}
|
||||
}
|
||||
|
||||
// This is better than adding to the reach, as 0.03 can cause a player to miss their target
|
||||
// Adds some more than 0.03 uncertainty in some cases, but a good trade off for simplicity
|
||||
//
|
||||
// Just give the uncertainty on 1.9+ clients as we have no way of knowing whether they had 0.03 movement
|
||||
// However, on 1.21.2+ we do know if they had 0.03 movement
|
||||
if (giveMovementThreshold || player.canSkipTicks()) {
|
||||
hitboxMargin += player.getMovementThreshold();
|
||||
}
|
||||
|
||||
targetBox.expand(hitboxMargin);
|
||||
|
||||
return maxReach;
|
||||
}
|
||||
|
||||
private static final boolean ATTACK_RANGE_COMPONENT_EXISTS = PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_21_11);
|
||||
|
||||
@Override
|
||||
public void onReload(ConfigManager config) {
|
||||
this.ignoreNonPlayerTargets = config.getBooleanElse("Reach.ignore-non-player-targets", false);
|
||||
@@ -389,4 +431,8 @@ public class Reach extends Check implements PacketCheck {
|
||||
new Vector3dm(from.getX(), from.getY(), from.getZ()),
|
||||
isPrediction, player.compensatedEntities.self.getAttributeValue(Attributes.ENTITY_INTERACTION_RANGE));
|
||||
}
|
||||
|
||||
private record InteractionData(Vector3d vector, ItemStack itemInHand) {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -20,11 +20,10 @@ 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())) {
|
||||
flagAndAlert("xyzYP: " + pos.getX() + ", " + pos.getY() + ", " + pos.getZ() + ", " + pos.getYaw() + ", " + 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);
|
||||
player.onPacketCancel();
|
||||
|
||||
@@ -8,7 +8,6 @@ import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import com.github.retrooper.packetevents.protocol.player.GameMode;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientEntityAction;
|
||||
|
||||
@CheckData(name = "ElytraC", description = "Started gliding too frequently")
|
||||
@@ -27,7 +26,7 @@ public class ElytraC extends Check implements PostPredictionCheck {
|
||||
return;
|
||||
}
|
||||
|
||||
if (player.gamemode == GameMode.SPECTATOR) {
|
||||
if (!player.cameraEntity.isSelf()) {
|
||||
glideThisTick = glideLastTick = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ public class InventoryD extends InventoryCheck {
|
||||
VehicleData vehicle = player.vehicleData;
|
||||
|
||||
// Will flag once if player open anything with pressed space bar
|
||||
isJumping = vehicle.nextHorseJump > 0 && horseJumpVerbose++ >= 1;
|
||||
isJumping = !vehicle.pendingJumps.isEmpty() && horseJumpVerbose++ >= 1;
|
||||
isMoving = vehicle.nextVehicleForward != 0 || vehicle.nextVehicleHorizontal != 0;
|
||||
} else {
|
||||
MoveVectorData move = findMovement(player.predictedVelocity);
|
||||
|
||||
@@ -106,7 +106,7 @@ public class PacketOrderI extends Check implements PostPredictionCheck {
|
||||
}
|
||||
}
|
||||
|
||||
if (player.gamemode == GameMode.SPECTATOR || isTickPacket(event.getPacketType())) {
|
||||
if (!player.cameraEntity.isSelf() || isTickPacket(event.getPacketType())) {
|
||||
digging = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.protocol.player.GameMode;
|
||||
import com.github.retrooper.packetevents.protocol.world.BlockFace;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientInteractEntity;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerBlockPlacement;
|
||||
@@ -49,7 +48,7 @@ public class PacketOrderM extends Check implements PostPredictionCheck {
|
||||
interacting = false;
|
||||
}
|
||||
|
||||
if (player.gamemode == GameMode.SPECTATOR || isTickPacket(event.getPacketType())) {
|
||||
if (!player.cameraEntity.isSelf() || isTickPacket(event.getPacketType())) {
|
||||
usingWithoutInteract = interacting = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import ac.grim.grimac.utils.anticheat.update.BlockPlace;
|
||||
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.protocol.player.GameMode;
|
||||
import com.github.retrooper.packetevents.protocol.world.BlockFace;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerBlockPlacement;
|
||||
|
||||
@@ -46,7 +45,7 @@ public class PacketOrderN extends BlockPlaceCheck {
|
||||
placing = false;
|
||||
}
|
||||
|
||||
if (player.gamemode == GameMode.SPECTATOR || isTickPacket(event.getPacketType())) {
|
||||
if (!player.cameraEntity.isSelf() || isTickPacket(event.getPacketType())) {
|
||||
usingWithoutPlacing = placing = false;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-2
@@ -8,7 +8,6 @@ import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketTypeCommon;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import com.github.retrooper.packetevents.protocol.player.GameMode;
|
||||
import com.github.retrooper.packetevents.protocol.world.BlockFace;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.*;
|
||||
import lombok.Getter;
|
||||
@@ -111,7 +110,7 @@ public final class PacketOrderProcessor extends Check implements PacketCheck {
|
||||
closingInventory = true;
|
||||
}
|
||||
|
||||
if (player.gamemode == GameMode.SPECTATOR || isTickPacket(packetType)
|
||||
if (!player.cameraEntity.isSelf() || isTickPacket(packetType)
|
||||
|| player.getClientVersion().isOlderThan(ClientVersion.V_1_21_2)
|
||||
&& !player.compensatedWorld.isChunkLoaded(GrimMath.floor(player.x) >> 4, GrimMath.floor(player.z) >> 4)) {
|
||||
openingInventory = false;
|
||||
|
||||
@@ -10,6 +10,29 @@ import com.github.retrooper.packetevents.util.Vector3f;
|
||||
|
||||
@CheckData(name = "FabricatedPlace", description = "Sent out of bounds cursor position")
|
||||
public class FabricatedPlace extends BlockPlaceCheck {
|
||||
|
||||
/**
|
||||
* MAX_DOUBLE_ERROR:
|
||||
* Represents the maximum possible floating-point arithmetic error that can occur
|
||||
* when calculating vectors at the Minecraft World Border (30,000,000 blocks).
|
||||
*
|
||||
* Math.ulp(30,000,000.0) is approx 3.72E-9.
|
||||
* We multiply by 2.0 to account for subtraction compounding logic in raytracing.
|
||||
*
|
||||
* This constant is safe to use everywhere; 7.450580596923828E-9 is physically indistinguishable from zero.
|
||||
*/
|
||||
private static final double MAX_DOUBLE_ERROR = Math.ulp(30_000_000.0) * 2.0;
|
||||
|
||||
/**
|
||||
* FLOAT_STEP_AT_ONE:
|
||||
* Represents the resolution of a Float at the value 1.0 (and 1.5).
|
||||
* Math.ulp(1.0f) is approx 1.1920929E-7.
|
||||
*
|
||||
* When checking the upper bound (1.0), we must allow this variance because a Double
|
||||
* slightly larger than 1.0 might get cast/snapped to the next available Float step.
|
||||
*/
|
||||
private static final double FLOAT_STEP_AT_ONE = Math.ulp(1.0f);
|
||||
|
||||
public FabricatedPlace(GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
@@ -19,11 +42,50 @@ public class FabricatedPlace extends BlockPlaceCheck {
|
||||
Vector3f cursor = place.cursor;
|
||||
if (cursor == null) return;
|
||||
|
||||
double allowed = Materials.isShapeExceedsCube(place.getPlacedAgainstMaterial()) || place.getPlacedAgainstMaterial() == StateTypes.LECTERN ? 1.5 : 1;
|
||||
double minAllowed = 1 - allowed;
|
||||
// Determine if we allow up to 1.5 (Lecterns, Scaffolding, etc)
|
||||
boolean isExtended = Materials.isShapeExceedsCube(place.getPlacedAgainstMaterial())
|
||||
|| place.getPlacedAgainstMaterial() == StateTypes.LECTERN;
|
||||
|
||||
if (cursor.getX() < minAllowed || cursor.getY() < minAllowed || cursor.getZ() < minAllowed || cursor.getX() > allowed || cursor.getY() > allowed || cursor.getZ() > allowed) {
|
||||
if (flagAndAlert() && shouldModifyPackets() && shouldCancel()) {
|
||||
double maxBound = isExtended ? 1.5 : 1.0;
|
||||
double minBound = 1.0 - maxBound; // Usually 0.0
|
||||
|
||||
// ====================================================================================
|
||||
// LOWER BOUND CHECK (< 0.0)
|
||||
// ====================================================================================
|
||||
// Why only MAX_DOUBLE_ERROR?
|
||||
// Near 0.0, 'float' has extremely high resolution (down to E-45).
|
||||
// It acts like a double. When the client has a tiny calculation error (e.g. -4.44E-16),
|
||||
// the float cast preserves it exactly. We only need to account for the arithmetic noise.
|
||||
if (cursor.getX() < minBound - MAX_DOUBLE_ERROR ||
|
||||
cursor.getY() < minBound - MAX_DOUBLE_ERROR ||
|
||||
cursor.getZ() < minBound - MAX_DOUBLE_ERROR) {
|
||||
|
||||
// Alert logic
|
||||
String debug = String.format("cursor=%s limit=%.16f", cursor, minBound - MAX_DOUBLE_ERROR);
|
||||
if (flagAndAlert(debug) && shouldModifyPackets() && shouldCancel()) {
|
||||
place.resync();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// ====================================================================================
|
||||
// UPPER BOUND CHECK (> 1.0 or > 1.5)
|
||||
// ====================================================================================
|
||||
// Near 1.0, the "Float Step" is E-7. This is 16x larger than the Double error.
|
||||
// If the calculation is slightly off, it might snap to the NEXT float.
|
||||
// We tolerate exactly one "Float Step" of overflow.
|
||||
double upperTolerance = FLOAT_STEP_AT_ONE;
|
||||
|
||||
// Note: Effectively Math.max(MAX_DOUBLE_ERROR, FLOAT_STEP_AT_ONE)
|
||||
// but since FLOAT_STEP is always larger in MC coordinates, we just use it.
|
||||
|
||||
if (cursor.getX() > maxBound + upperTolerance ||
|
||||
cursor.getY() > maxBound + upperTolerance ||
|
||||
cursor.getZ() > maxBound + upperTolerance) {
|
||||
|
||||
// Alert logic
|
||||
String debug = String.format("cursor=%s limit=%.16f", cursor, maxBound + upperTolerance);
|
||||
if (flagAndAlert(debug) && shouldModifyPackets() && shouldCancel()) {
|
||||
place.resync();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
|
||||
import ac.grim.grimac.utils.math.Vector3dm;
|
||||
import ac.grim.grimac.utils.math.VectorUtils;
|
||||
import com.github.retrooper.packetevents.protocol.attribute.Attributes;
|
||||
import com.github.retrooper.packetevents.protocol.player.GameMode;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
|
||||
import com.github.retrooper.packetevents.util.Vector3i;
|
||||
|
||||
@@ -20,7 +19,7 @@ public class FarPlace extends BlockPlaceCheck {
|
||||
|
||||
@Override
|
||||
public void onBlockPlace(final BlockPlace place) {
|
||||
if (player.gamemode == GameMode.SPECTATOR || player.inVehicle()) return;
|
||||
if (!player.cameraEntity.isSelf() || player.inVehicle()) return;
|
||||
|
||||
Vector3i blockPos = place.position;
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ import ac.grim.grimac.utils.anticheat.MessageUtil;
|
||||
import ac.grim.grimac.utils.anticheat.update.BlockPlace;
|
||||
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.player.GameMode;
|
||||
import com.github.retrooper.packetevents.protocol.world.BlockFace;
|
||||
import com.github.retrooper.packetevents.util.Vector3f;
|
||||
import com.github.retrooper.packetevents.util.Vector3i;
|
||||
@@ -54,7 +53,7 @@ public class MultiPlace extends BlockPlaceCheck {
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
if (player.gamemode == GameMode.SPECTATOR || isTickPacket(event.getPacketType())) {
|
||||
if (!player.cameraEntity.isSelf() || isTickPacket(event.getPacketType())) {
|
||||
hasPlaced = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ import ac.grim.grimac.utils.nmsutil.Ray;
|
||||
import ac.grim.grimac.utils.nmsutil.ReachUtils;
|
||||
import com.github.retrooper.packetevents.protocol.attribute.Attributes;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import com.github.retrooper.packetevents.protocol.player.GameMode;
|
||||
import com.github.retrooper.packetevents.protocol.world.BlockFace;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
|
||||
import com.github.retrooper.packetevents.util.Vector3d;
|
||||
@@ -34,7 +33,7 @@ public class RotationPlace extends BlockPlaceCheck {
|
||||
@Override
|
||||
public void onBlockPlace(final BlockPlace place) {
|
||||
if (place.material == StateTypes.SCAFFOLDING) return;
|
||||
if (player.gamemode == GameMode.SPECTATOR)
|
||||
if (!player.cameraEntity.isSelf())
|
||||
return; // you don't send flying packets when spectating entities
|
||||
if (player.inVehicle()) return;
|
||||
if (flagBuffer > 0 && !didRayTraceHit(place)) {
|
||||
@@ -50,7 +49,7 @@ public class RotationPlace extends BlockPlaceCheck {
|
||||
@Override
|
||||
public void onPostFlyingBlockPlace(BlockPlace place) {
|
||||
if (place.material == StateTypes.SCAFFOLDING) return;
|
||||
if (player.gamemode == GameMode.SPECTATOR)
|
||||
if (!player.cameraEntity.isSelf())
|
||||
return; // you don't send flying packets when spectating entities
|
||||
if (player.inVehicle()) return;
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ public class VehicleD extends Check implements PacketCheck {
|
||||
if (event.getPacketType() == PacketType.Play.Client.ENTITY_ACTION && new WrapperPlayClientEntityAction(event).getAction() == WrapperPlayClientEntityAction.Action.START_JUMPING_WITH_HORSE) {
|
||||
final EntityType vehicle = player.getVehicleType();
|
||||
|
||||
if (!EntityTypes.isTypeInstanceOf(vehicle, EntityTypes.ABSTRACT_HORSE)) {
|
||||
if (!EntityTypes.isTypeInstanceOf(vehicle, EntityTypes.ABSTRACT_HORSE) && !EntityTypes.isTypeInstanceOf(vehicle, EntityTypes.ABSTRACT_NAUTILUS)) {
|
||||
if (flagAndAlert("vehicle=" + (vehicle == null ? "null" : vehicle.getName().getKey().toLowerCase())) && shouldModifyPackets()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
package ac.grim.grimac.command;
|
||||
|
||||
import ac.grim.grimac.platform.api.manager.cloud.CloudCommandAdapter;
|
||||
import ac.grim.grimac.platform.api.sender.Sender;
|
||||
import org.incendo.cloud.CommandManager;
|
||||
|
||||
public interface BuildableCommand {
|
||||
void register(CommandManager<Sender> manager);
|
||||
void register(CommandManager<Sender> manager, CloudCommandAdapter adapter);
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package ac.grim.grimac.command;
|
||||
|
||||
import ac.grim.grimac.command.commands.*;
|
||||
import ac.grim.grimac.command.handler.GrimCommandFailureHandler;
|
||||
import ac.grim.grimac.platform.api.command.CommandService;
|
||||
import ac.grim.grimac.platform.api.manager.cloud.CloudCommandAdapter;
|
||||
import ac.grim.grimac.platform.api.sender.Sender;
|
||||
import ac.grim.grimac.utils.anticheat.MessageUtil;
|
||||
import io.leangen.geantyref.TypeToken;
|
||||
import net.kyori.adventure.text.ComponentLike;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import org.incendo.cloud.CommandManager;
|
||||
import org.incendo.cloud.exception.InvalidSyntaxException;
|
||||
import org.incendo.cloud.key.CloudKey;
|
||||
import org.incendo.cloud.processors.requirements.RequirementApplicable;
|
||||
import org.incendo.cloud.processors.requirements.RequirementApplicable.RequirementApplicableFactory;
|
||||
import org.incendo.cloud.processors.requirements.RequirementPostprocessor;
|
||||
import org.incendo.cloud.processors.requirements.Requirements;
|
||||
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class CloudCommandService implements CommandService {
|
||||
|
||||
public static final CloudKey<Requirements<Sender, SenderRequirement>> REQUIREMENT_KEY
|
||||
= CloudKey.of("requirements", new TypeToken<>() {});
|
||||
|
||||
public static final RequirementApplicableFactory<Sender, SenderRequirement> REQUIREMENT_FACTORY
|
||||
= RequirementApplicable.factory(REQUIREMENT_KEY);
|
||||
|
||||
private boolean commandsRegistered = false;
|
||||
|
||||
private final Supplier<CommandManager<Sender>> commandManagerSupplier;
|
||||
private final CloudCommandAdapter commandAdapter;
|
||||
|
||||
public CloudCommandService(Supplier<CommandManager<Sender>> commandManagerSupplier, CloudCommandAdapter commandAdapter) {
|
||||
this.commandManagerSupplier = commandManagerSupplier;
|
||||
this.commandAdapter = commandAdapter;
|
||||
}
|
||||
|
||||
public void registerCommands() {
|
||||
if (commandsRegistered) return;
|
||||
CommandManager<Sender> commandManager = commandManagerSupplier.get();
|
||||
new GrimPerf().register(commandManager, commandAdapter);
|
||||
new GrimDebug().register(commandManager, commandAdapter);
|
||||
new GrimAlerts().register(commandManager, commandAdapter);
|
||||
new GrimProfile().register(commandManager, commandAdapter);
|
||||
new GrimSendAlert().register(commandManager, commandAdapter);
|
||||
new GrimHelp().register(commandManager, commandAdapter);
|
||||
new GrimHistory().register(commandManager, commandAdapter);
|
||||
new GrimReload().register(commandManager, commandAdapter);
|
||||
new GrimSpectate().register(commandManager, commandAdapter);
|
||||
new GrimStopSpectating().register(commandManager, commandAdapter);
|
||||
new GrimLog().register(commandManager, commandAdapter);
|
||||
new GrimVerbose().register(commandManager, commandAdapter);
|
||||
new GrimVersion().register(commandManager, commandAdapter);
|
||||
new GrimDump().register(commandManager, commandAdapter);
|
||||
new GrimBrands().register(commandManager, commandAdapter);
|
||||
new GrimList().register(commandManager, commandAdapter);
|
||||
new GrimTestWebhook().register(commandManager, commandAdapter);
|
||||
|
||||
final RequirementPostprocessor<Sender, SenderRequirement>
|
||||
senderRequirementPostprocessor = RequirementPostprocessor.of(
|
||||
REQUIREMENT_KEY,
|
||||
new GrimCommandFailureHandler()
|
||||
);
|
||||
commandManager.registerCommandPostProcessor(senderRequirementPostprocessor);
|
||||
registerExceptionHandler(commandManager, InvalidSyntaxException.class, e -> MessageUtil.miniMessage(e.correctSyntax()));
|
||||
commandsRegistered = true;
|
||||
}
|
||||
|
||||
protected <E extends Exception> void registerExceptionHandler(CommandManager<Sender> commandManager, Class<E> ex, Function<E, ComponentLike> toComponent) {
|
||||
commandManager.exceptionController().registerHandler(ex,
|
||||
(c) -> c.context().sender().sendMessage(toComponent.apply(c.exception()).asComponent().colorIfAbsent(NamedTextColor.RED))
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package ac.grim.grimac.command.commands;
|
||||
|
||||
import ac.grim.grimac.GrimAPI;
|
||||
import ac.grim.grimac.command.BuildableCommand;
|
||||
import ac.grim.grimac.platform.api.manager.cloud.CloudCommandAdapter;
|
||||
import ac.grim.grimac.platform.api.sender.Sender;
|
||||
import org.incendo.cloud.CommandManager;
|
||||
import org.incendo.cloud.context.CommandContext;
|
||||
@@ -12,7 +13,7 @@ import java.util.Objects;
|
||||
|
||||
public class GrimAlerts implements BuildableCommand {
|
||||
@Override
|
||||
public void register(CommandManager<Sender> commandManager) {
|
||||
public void register(CommandManager<Sender> commandManager, CloudCommandAdapter adapter) {
|
||||
commandManager.command(
|
||||
commandManager.commandBuilder("grim", "grimac")
|
||||
.literal("alerts", Description.of("Toggle alerts for the sender"))
|
||||
|
||||
@@ -2,6 +2,7 @@ package ac.grim.grimac.command.commands;
|
||||
|
||||
import ac.grim.grimac.GrimAPI;
|
||||
import ac.grim.grimac.command.BuildableCommand;
|
||||
import ac.grim.grimac.platform.api.manager.cloud.CloudCommandAdapter;
|
||||
import ac.grim.grimac.platform.api.sender.Sender;
|
||||
import org.incendo.cloud.CommandManager;
|
||||
import org.incendo.cloud.context.CommandContext;
|
||||
@@ -12,7 +13,7 @@ import java.util.Objects;
|
||||
|
||||
public class GrimBrands implements BuildableCommand {
|
||||
@Override
|
||||
public void register(CommandManager<Sender> commandManager) {
|
||||
public void register(CommandManager<Sender> commandManager, CloudCommandAdapter adapter) {
|
||||
commandManager.command(
|
||||
commandManager.commandBuilder("grim", "grimac")
|
||||
.literal("brands", Description.of("Toggle brands for the sender"))
|
||||
|
||||
@@ -4,6 +4,7 @@ import ac.grim.grimac.GrimAPI;
|
||||
import ac.grim.grimac.checks.debug.HitboxDebugHandler;
|
||||
import ac.grim.grimac.command.BuildableCommand;
|
||||
import ac.grim.grimac.platform.api.command.PlayerSelector;
|
||||
import ac.grim.grimac.platform.api.manager.cloud.CloudCommandAdapter;
|
||||
import ac.grim.grimac.platform.api.sender.Sender;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.anticheat.MessageUtil;
|
||||
@@ -20,27 +21,27 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class GrimDebug implements BuildableCommand {
|
||||
|
||||
public void register(CommandManager<Sender> commandManager) {
|
||||
public void register(CommandManager<Sender> commandManager, CloudCommandAdapter adapter) {
|
||||
Command.Builder<Sender> grimCommand = commandManager.commandBuilder("grim", "grimac");
|
||||
|
||||
// Register "debug" subcommand
|
||||
Command.Builder<Sender> debugCommand = grimCommand
|
||||
.literal("debug", Description.of("Toggle debug output for a player"))
|
||||
.permission("grim.debug")
|
||||
.optional("target", GrimAPI.INSTANCE.getCommandAdapter().singlePlayerSelectorParser())
|
||||
.optional("target", adapter.singlePlayerSelectorParser())
|
||||
.handler(this::handleDebug);
|
||||
|
||||
// Register "consoledebug" subcommand
|
||||
Command.Builder<Sender> consoleDebugCommand = grimCommand
|
||||
.literal("consoledebug", Description.of("Toggle console debug output for a player"))
|
||||
.permission("grim.consoledebug")
|
||||
.required("target", GrimAPI.INSTANCE.getCommandAdapter().singlePlayerSelectorParser())
|
||||
.required("target", adapter.singlePlayerSelectorParser())
|
||||
.handler(this::handleConsoleDebug);
|
||||
|
||||
Command.Builder<Sender> hitboxDebugCommand = grimCommand
|
||||
.literal("hitboxdebug", Description.of("Toggle hitbox debug visualization"))
|
||||
.permission("grim.hitboxdebug")
|
||||
.optional("target", GrimAPI.INSTANCE.getCommandAdapter().singlePlayerSelectorParser(), Description.of("Player to debug (defaults to self if sender is player)"))
|
||||
.optional("target", adapter.singlePlayerSelectorParser(), Description.of("Player to debug (defaults to self if sender is player)"))
|
||||
.handler(this::handleHitboxDebug);
|
||||
|
||||
// Register command
|
||||
|
||||
@@ -3,6 +3,7 @@ package ac.grim.grimac.command.commands;
|
||||
import ac.grim.grimac.GrimAPI;
|
||||
import ac.grim.grimac.command.BuildableCommand;
|
||||
import ac.grim.grimac.platform.api.PlatformPlugin;
|
||||
import ac.grim.grimac.platform.api.manager.cloud.CloudCommandAdapter;
|
||||
import ac.grim.grimac.platform.api.sender.Sender;
|
||||
import ac.grim.grimac.utils.anticheat.MessageUtil;
|
||||
import ac.grim.grimac.utils.common.PropertiesUtil;
|
||||
@@ -29,7 +30,7 @@ public class GrimDump implements BuildableCommand {
|
||||
private String link = null; // these links should not expire for a while
|
||||
|
||||
@Override
|
||||
public void register(CommandManager<Sender> commandManager) {
|
||||
public void register(CommandManager<Sender> commandManager, CloudCommandAdapter adapter) {
|
||||
commandManager.command(
|
||||
commandManager.commandBuilder("grim", "grimac")
|
||||
.literal("dump", Description.of("Generate a debug dump"))
|
||||
@@ -51,9 +52,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 +69,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 +81,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 +113,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);
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package ac.grim.grimac.command.commands;
|
||||
|
||||
import ac.grim.grimac.GrimAPI;
|
||||
import ac.grim.grimac.command.BuildableCommand;
|
||||
import ac.grim.grimac.platform.api.manager.cloud.CloudCommandAdapter;
|
||||
import ac.grim.grimac.platform.api.sender.Sender;
|
||||
import ac.grim.grimac.utils.anticheat.MessageUtil;
|
||||
import org.incendo.cloud.CommandManager;
|
||||
@@ -11,7 +12,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class GrimHelp implements BuildableCommand {
|
||||
@Override
|
||||
public void register(CommandManager<Sender> commandManager) {
|
||||
public void register(CommandManager<Sender> commandManager, CloudCommandAdapter adapter) {
|
||||
commandManager.command(
|
||||
commandManager.commandBuilder("grim", "grimac")
|
||||
.literal("help", Description.of("Display help information"))
|
||||
|
||||
@@ -4,6 +4,7 @@ import ac.grim.grimac.GrimAPI;
|
||||
import ac.grim.grimac.command.BuildableCommand;
|
||||
import ac.grim.grimac.manager.violationdatabase.Violation;
|
||||
import ac.grim.grimac.manager.violationdatabase.ViolationDatabaseManager;
|
||||
import ac.grim.grimac.platform.api.manager.cloud.CloudCommandAdapter;
|
||||
import ac.grim.grimac.platform.api.player.OfflinePlatformPlayer;
|
||||
import ac.grim.grimac.platform.api.sender.Sender;
|
||||
import ac.grim.grimac.utils.anticheat.MessageUtil;
|
||||
@@ -18,12 +19,12 @@ import java.util.concurrent.TimeUnit;
|
||||
public class GrimHistory implements BuildableCommand {
|
||||
|
||||
@Override
|
||||
public void register(CommandManager<Sender> commandManager) {
|
||||
public void register(CommandManager<Sender> commandManager, CloudCommandAdapter adapter) {
|
||||
commandManager.command(
|
||||
commandManager.commandBuilder("grim", "grimac")
|
||||
.literal("history", "hist")
|
||||
.permission("grim.help")
|
||||
.required("target", StringParser.stringParser(), GrimAPI.INSTANCE.getCommandAdapter().onlinePlayerSuggestions())
|
||||
.required("target", StringParser.stringParser(), adapter.onlinePlayerSuggestions())
|
||||
.optional("page", IntegerParser.integerParser())
|
||||
.permission("grim.history")
|
||||
.handler(this::handleHistory)
|
||||
|
||||
@@ -4,6 +4,7 @@ import ac.grim.grimac.GrimAPI;
|
||||
import ac.grim.grimac.api.GrimIdentity;
|
||||
import ac.grim.grimac.command.BuildableCommand;
|
||||
import ac.grim.grimac.command.CommandUtils;
|
||||
import ac.grim.grimac.platform.api.manager.cloud.CloudCommandAdapter;
|
||||
import ac.grim.grimac.platform.api.player.PlatformPlayer;
|
||||
import ac.grim.grimac.platform.api.sender.Sender;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
@@ -28,7 +29,7 @@ public class GrimList implements BuildableCommand {
|
||||
// Mainly for debugging purposes. Useful for seeing which players are exempt or not.
|
||||
|
||||
@Override
|
||||
public void register(CommandManager<Sender> commandManager) {
|
||||
public void register(CommandManager<Sender> commandManager, CloudCommandAdapter adapter) {
|
||||
commandManager.command(commandManager.commandBuilder("grim", "grimac")
|
||||
.literal("list")
|
||||
.permission("grim.list")
|
||||
|
||||
@@ -3,6 +3,7 @@ package ac.grim.grimac.command.commands;
|
||||
import ac.grim.grimac.GrimAPI;
|
||||
import ac.grim.grimac.command.BuildableCommand;
|
||||
import ac.grim.grimac.manager.init.start.SuperDebug;
|
||||
import ac.grim.grimac.platform.api.manager.cloud.CloudCommandAdapter;
|
||||
import ac.grim.grimac.platform.api.sender.Sender;
|
||||
import ac.grim.grimac.utils.anticheat.LogUtil;
|
||||
import ac.grim.grimac.utils.anticheat.MessageUtil;
|
||||
@@ -68,7 +69,7 @@ public class GrimLog implements BuildableCommand {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register(CommandManager<Sender> commandManager) {
|
||||
public void register(CommandManager<Sender> commandManager, CloudCommandAdapter adapter) {
|
||||
Command<Sender> command = commandManager.commandBuilder("grim", "grimac")
|
||||
.literal("log", "logs")
|
||||
.permission("grim.log")
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package ac.grim.grimac.command.commands;
|
||||
|
||||
import ac.grim.grimac.platform.api.manager.cloud.CloudCommandAdapter;
|
||||
import ac.grim.grimac.platform.api.sender.Sender;
|
||||
import ac.grim.grimac.predictionengine.MovementCheckRunner;
|
||||
import net.kyori.adventure.text.Component;
|
||||
@@ -11,7 +12,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class GrimPerf {
|
||||
|
||||
public void register(CommandManager<Sender> commandManager) {
|
||||
public void register(CommandManager<Sender> commandManager, CloudCommandAdapter adapter) {
|
||||
Command.Builder<Sender> grimCommand = commandManager.commandBuilder("grim", "grimac");
|
||||
|
||||
Command.Builder<Sender> configuredBuilder = grimCommand
|
||||
|
||||
@@ -3,6 +3,7 @@ package ac.grim.grimac.command.commands;
|
||||
import ac.grim.grimac.GrimAPI;
|
||||
import ac.grim.grimac.command.BuildableCommand;
|
||||
import ac.grim.grimac.platform.api.command.PlayerSelector;
|
||||
import ac.grim.grimac.platform.api.manager.cloud.CloudCommandAdapter;
|
||||
import ac.grim.grimac.platform.api.player.PlatformPlayer;
|
||||
import ac.grim.grimac.platform.api.sender.Sender;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
@@ -16,12 +17,12 @@ import java.util.Objects;
|
||||
|
||||
public class GrimProfile implements BuildableCommand {
|
||||
@Override
|
||||
public void register(CommandManager<Sender> commandManager) {
|
||||
public void register(CommandManager<Sender> commandManager, CloudCommandAdapter adapter) {
|
||||
commandManager.command(
|
||||
commandManager.commandBuilder("grim", "grimac")
|
||||
.literal("profile")
|
||||
.permission("grim.profile")
|
||||
.required("target", GrimAPI.INSTANCE.getCommandAdapter().singlePlayerSelectorParser())
|
||||
.required("target", adapter.singlePlayerSelectorParser())
|
||||
.handler(this::handleProfile)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package ac.grim.grimac.command.commands;
|
||||
|
||||
import ac.grim.grimac.GrimAPI;
|
||||
import ac.grim.grimac.command.BuildableCommand;
|
||||
import ac.grim.grimac.platform.api.manager.cloud.CloudCommandAdapter;
|
||||
import ac.grim.grimac.platform.api.sender.Sender;
|
||||
import ac.grim.grimac.utils.anticheat.MessageUtil;
|
||||
import net.kyori.adventure.text.Component;
|
||||
@@ -11,7 +12,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class GrimReload implements BuildableCommand {
|
||||
@Override
|
||||
public void register(CommandManager<Sender> commandManager) {
|
||||
public void register(CommandManager<Sender> commandManager, CloudCommandAdapter adapter) {
|
||||
commandManager.command(
|
||||
commandManager.commandBuilder("grim", "grimac")
|
||||
.literal("reload")
|
||||
|
||||
@@ -2,6 +2,7 @@ package ac.grim.grimac.command.commands;
|
||||
|
||||
import ac.grim.grimac.GrimAPI;
|
||||
import ac.grim.grimac.command.BuildableCommand;
|
||||
import ac.grim.grimac.platform.api.manager.cloud.CloudCommandAdapter;
|
||||
import ac.grim.grimac.platform.api.sender.Sender;
|
||||
import ac.grim.grimac.utils.anticheat.MessageUtil;
|
||||
import net.kyori.adventure.text.Component;
|
||||
@@ -12,7 +13,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class GrimSendAlert implements BuildableCommand {
|
||||
@Override
|
||||
public void register(CommandManager<Sender> commandManager) {
|
||||
public void register(CommandManager<Sender> commandManager, CloudCommandAdapter adapter) {
|
||||
commandManager.command(
|
||||
commandManager.commandBuilder("grim", "grimac")
|
||||
.literal("sendalert")
|
||||
|
||||
@@ -2,9 +2,10 @@ package ac.grim.grimac.command.commands;
|
||||
|
||||
import ac.grim.grimac.GrimAPI;
|
||||
import ac.grim.grimac.command.BuildableCommand;
|
||||
import ac.grim.grimac.command.CloudCommandService;
|
||||
import ac.grim.grimac.command.requirements.PlayerSenderRequirement;
|
||||
import ac.grim.grimac.manager.init.start.CommandRegister;
|
||||
import ac.grim.grimac.platform.api.command.PlayerSelector;
|
||||
import ac.grim.grimac.platform.api.manager.cloud.CloudCommandAdapter;
|
||||
import ac.grim.grimac.platform.api.player.PlatformPlayer;
|
||||
import ac.grim.grimac.platform.api.sender.Sender;
|
||||
import ac.grim.grimac.utils.anticheat.MessageUtil;
|
||||
@@ -17,14 +18,14 @@ import java.util.Objects;
|
||||
|
||||
public class GrimSpectate implements BuildableCommand {
|
||||
@Override
|
||||
public void register(CommandManager<Sender> commandManager) {
|
||||
public void register(CommandManager<Sender> commandManager, CloudCommandAdapter adapter) {
|
||||
commandManager.command(
|
||||
commandManager.commandBuilder("grim", "grimac")
|
||||
.literal("spectate")
|
||||
.permission("grim.spectate")
|
||||
.required("target", GrimAPI.INSTANCE.getCommandAdapter().singlePlayerSelectorParser())
|
||||
.required("target", adapter.singlePlayerSelectorParser())
|
||||
.handler(this::handleSpectate)
|
||||
.apply(CommandRegister.REQUIREMENT_FACTORY.create(PlayerSenderRequirement.PLAYER_SENDER_REQUIREMENT))
|
||||
.apply(CloudCommandService.REQUIREMENT_FACTORY.create(PlayerSenderRequirement.PLAYER_SENDER_REQUIREMENT))
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,8 +2,9 @@ package ac.grim.grimac.command.commands;
|
||||
|
||||
import ac.grim.grimac.GrimAPI;
|
||||
import ac.grim.grimac.command.BuildableCommand;
|
||||
import ac.grim.grimac.command.CloudCommandService;
|
||||
import ac.grim.grimac.command.requirements.PlayerSenderRequirement;
|
||||
import ac.grim.grimac.manager.init.start.CommandRegister;
|
||||
import ac.grim.grimac.platform.api.manager.cloud.CloudCommandAdapter;
|
||||
import ac.grim.grimac.platform.api.sender.Sender;
|
||||
import ac.grim.grimac.utils.anticheat.MessageUtil;
|
||||
import org.incendo.cloud.CommandManager;
|
||||
@@ -18,7 +19,7 @@ import java.util.Objects;
|
||||
public class GrimStopSpectating implements BuildableCommand {
|
||||
|
||||
@Override
|
||||
public void register(CommandManager<Sender> commandManager) {
|
||||
public void register(CommandManager<Sender> commandManager, CloudCommandAdapter adapter) {
|
||||
commandManager.command(
|
||||
commandManager.commandBuilder("grim", "grimac")
|
||||
.literal("stopspectating")
|
||||
@@ -30,7 +31,7 @@ public class GrimStopSpectating implements BuildableCommand {
|
||||
return List.of(); // No suggestions if no permission
|
||||
}))
|
||||
.handler(this::onStopSpectate)
|
||||
.apply(CommandRegister.REQUIREMENT_FACTORY.create(PlayerSenderRequirement.PLAYER_SENDER_REQUIREMENT))
|
||||
.apply(CloudCommandService.REQUIREMENT_FACTORY.create(PlayerSenderRequirement.PLAYER_SENDER_REQUIREMENT))
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
package ac.grim.grimac.command.commands;
|
||||
|
||||
import ac.grim.grimac.GrimAPI;
|
||||
import ac.grim.grimac.command.BuildableCommand;
|
||||
import ac.grim.grimac.platform.api.manager.cloud.CloudCommandAdapter;
|
||||
import ac.grim.grimac.platform.api.sender.Sender;
|
||||
import ac.grim.grimac.utils.anticheat.LogUtil;
|
||||
import ac.grim.grimac.utils.anticheat.MessageUtil;
|
||||
import ac.grim.grimac.utils.webhook.WebhookMessage;
|
||||
import org.incendo.cloud.CommandManager;
|
||||
import org.incendo.cloud.context.CommandContext;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class GrimTestWebhook implements BuildableCommand {
|
||||
@Override
|
||||
public void register(CommandManager<Sender> commandManager, CloudCommandAdapter adapter) {
|
||||
commandManager.command(
|
||||
commandManager.commandBuilder("grim", "grimac")
|
||||
.literal("testwebhook")
|
||||
.permission("grim.testwebhook")
|
||||
.handler(this::handleTestWebhook)
|
||||
);
|
||||
}
|
||||
|
||||
private void handleTestWebhook(@NotNull CommandContext<Sender> context) {
|
||||
if (GrimAPI.INSTANCE.getDiscordManager().isDisabled()) {
|
||||
context.sender().sendMessage(MessageUtil.miniMessage(GrimAPI.INSTANCE.getConfigManager().getWebhookNotEnabled()));
|
||||
return;
|
||||
}
|
||||
|
||||
WebhookMessage webhookMessage = new WebhookMessage().content(GrimAPI.INSTANCE.getConfigManager().getWebhookTestMessage());
|
||||
GrimAPI.INSTANCE.getDiscordManager().sendWebhookMessage(webhookMessage).whenCompleteAsync(((successful, throwable) -> {
|
||||
if (successful == true) {
|
||||
context.sender().sendMessage(MessageUtil.miniMessage(GrimAPI.INSTANCE.getConfigManager().getWebhookTestSucceeded()));
|
||||
return;
|
||||
}
|
||||
|
||||
context.sender().sendMessage(MessageUtil.miniMessage(GrimAPI.INSTANCE.getConfigManager().getWebhookTestFailed()));
|
||||
|
||||
if (throwable != null) {
|
||||
LogUtil.error("Exception caught while sending a Discord webhook test alert", throwable);
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package ac.grim.grimac.command.commands;
|
||||
|
||||
import ac.grim.grimac.GrimAPI;
|
||||
import ac.grim.grimac.command.BuildableCommand;
|
||||
import ac.grim.grimac.platform.api.manager.cloud.CloudCommandAdapter;
|
||||
import ac.grim.grimac.platform.api.sender.Sender;
|
||||
import org.incendo.cloud.CommandManager;
|
||||
import org.incendo.cloud.context.CommandContext;
|
||||
@@ -9,7 +10,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class GrimVerbose implements BuildableCommand {
|
||||
@Override
|
||||
public void register(CommandManager<Sender> commandManager) {
|
||||
public void register(CommandManager<Sender> commandManager, CloudCommandAdapter adapter) {
|
||||
commandManager.command(
|
||||
commandManager.commandBuilder("grim", "grimac")
|
||||
.literal("verbose")
|
||||
|
||||
@@ -2,6 +2,7 @@ package ac.grim.grimac.command.commands;
|
||||
|
||||
import ac.grim.grimac.GrimAPI;
|
||||
import ac.grim.grimac.command.BuildableCommand;
|
||||
import ac.grim.grimac.platform.api.manager.cloud.CloudCommandAdapter;
|
||||
import ac.grim.grimac.platform.api.sender.Sender;
|
||||
import ac.grim.grimac.utils.anticheat.LogUtil;
|
||||
import ac.grim.grimac.utils.anticheat.MessageUtil;
|
||||
@@ -136,7 +137,7 @@ public class GrimVersion implements BuildableCommand {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register(CommandManager<Sender> commandManager) {
|
||||
public void register(CommandManager<Sender> commandManager, CloudCommandAdapter adapter) {
|
||||
commandManager.command(
|
||||
commandManager.commandBuilder("grim", "grimac")
|
||||
.literal("version")
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
package ac.grim.grimac.events;
|
||||
|
||||
import ac.grim.grimac.api.plugin.GrimPlugin;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
/**
|
||||
* Manages the resolution of platform-specific plugin contexts into the universal {@link GrimPlugin} interface.
|
||||
* <p>
|
||||
* This class is the bridge that allows the API to remain platform-agnostic while still supporting
|
||||
* platform-specific objects for lifecycle management.
|
||||
*/
|
||||
@ApiStatus.Internal
|
||||
public final class GrimExtensionManager {
|
||||
private final List<GrimExtensionResolver> resolvers = new CopyOnWriteArrayList<>();
|
||||
private ResolutionFailureHandler failureHandler = failedContext ->
|
||||
new IllegalArgumentException("Unable to resolve plugin context for type: " + failedContext.getClass().getName() +
|
||||
". Ensure you are passing a valid platform plugin instance or a pre-existing GrimPlugin.");
|
||||
|
||||
/**
|
||||
* Sets a custom handler for creating exceptions when plugin context resolution fails.
|
||||
* This allows platform-specific implementations to provide more descriptive error messages.
|
||||
*
|
||||
* @param failureHandler The handler to use.
|
||||
*/
|
||||
public void setFailureHandler(@NotNull ResolutionFailureHandler failureHandler) {
|
||||
this.failureHandler = Objects.requireNonNull(failureHandler, "failureHandler cannot be null");
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a new resolver. This is intended to be called by the core GrimAC implementation
|
||||
* during its startup phase.
|
||||
*
|
||||
* @param resolver The resolver to add.
|
||||
*/
|
||||
public void registerResolver(@NotNull GrimExtensionResolver resolver) {
|
||||
Objects.requireNonNull(resolver, "resolver cannot be null");
|
||||
this.resolvers.add(resolver);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves a context object into a GrimPlugin by trying all registered resolvers in order.
|
||||
* <p>
|
||||
* It also has built-in logic to handle being passed a {@link GrimPlugin} instance directly.
|
||||
*
|
||||
* @param context The context object to resolve.
|
||||
* @return The resolved GrimPlugin.
|
||||
* @throws IllegalArgumentException if no registered resolver can handle the provided context type.
|
||||
*/
|
||||
@NotNull
|
||||
public GrimPlugin getPlugin(@NotNull Object context) {
|
||||
Objects.requireNonNull(context, "context cannot be null");
|
||||
|
||||
// First, check for the universal case: was a GrimPlugin already passed?
|
||||
if (context instanceof GrimPlugin) {
|
||||
return (GrimPlugin) context;
|
||||
}
|
||||
|
||||
// Next, iterate through platform-specific resolvers.
|
||||
for (GrimExtensionResolver resolver : resolvers) {
|
||||
GrimPlugin resolved = resolver.resolve(context);
|
||||
if (resolved != null) {
|
||||
return resolved;
|
||||
}
|
||||
}
|
||||
|
||||
// If no resolver succeeded, throw a descriptive error.
|
||||
throw this.failureHandler.createExceptionFor(context);
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
package ac.grim.grimac.events;
|
||||
|
||||
import ac.grim.grimac.api.plugin.GrimPlugin;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
|
||||
/**
|
||||
* A functional interface responsible for attempting to resolve a generic context object
|
||||
* into a {@link GrimPlugin}.
|
||||
* <p>
|
||||
* Implementations of this are provided by the core GrimAC platform module (e.g., for Bukkit, Fabric)
|
||||
* and registered with the central {@link GrimPluginManager}.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface GrimExtensionResolver {
|
||||
|
||||
/**
|
||||
* Attempts to resolve the given context object into a GrimPlugin.
|
||||
*
|
||||
* @param context The context object to resolve (e.g., a Bukkit Plugin, a Plugin Class, a Fabric Mod).
|
||||
* @return An Optional containing the resolved GrimPlugin if this resolver supports the context type,
|
||||
* otherwise an empty Optional.
|
||||
*/
|
||||
@Nullable GrimPlugin resolve(@NotNull Object context);
|
||||
|
||||
}
|
||||
@@ -1,337 +0,0 @@
|
||||
package ac.grim.grimac.events;
|
||||
|
||||
import ac.grim.grimac.api.event.EventBus;
|
||||
import ac.grim.grimac.api.event.GrimEvent;
|
||||
import ac.grim.grimac.api.event.GrimEventHandler;
|
||||
import ac.grim.grimac.api.event.GrimEventListener;
|
||||
import ac.grim.grimac.api.event.events.*;
|
||||
import ac.grim.grimac.api.plugin.GrimPlugin;
|
||||
import ac.grim.grimac.utils.anticheat.LogUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class OptimizedEventBus implements EventBus {
|
||||
private final MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
private final GrimExtensionManager extensionManager;
|
||||
// Store all listeners together by event type
|
||||
private final Map<Class<? extends GrimEvent>, AtomicReference<OptimizedListener[]>> listenerMap = new ConcurrentHashMap<>();
|
||||
|
||||
public OptimizedEventBus(GrimExtensionManager extensionManager) {
|
||||
this.extensionManager = extensionManager;
|
||||
prefillKnownEventTypes(listenerMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Minor optimization to prefill the listenerMap for known existing events so that they don't have to be computed later
|
||||
* When registering an event of the type for the first time
|
||||
* @param map
|
||||
*/
|
||||
private void prefillKnownEventTypes(Map<Class<? extends GrimEvent>, AtomicReference<OptimizedListener[]>> map) {
|
||||
// Add all your known event types here
|
||||
map.put(GrimReloadEvent.class, new AtomicReference<>(new OptimizedListener[0]));
|
||||
map.put(GrimQuitEvent.class, new AtomicReference<>(new OptimizedListener[0]));
|
||||
map.put(GrimJoinEvent.class, new AtomicReference<>(new OptimizedListener[0]));
|
||||
|
||||
map.put(FlagEvent.class, new AtomicReference<>(new OptimizedListener[0]));
|
||||
map.put(CommandExecuteEvent.class, new AtomicReference<>(new OptimizedListener[0]));
|
||||
map.put(CompletePredictionEvent.class, new AtomicReference<>(new OptimizedListener[0]));
|
||||
|
||||
// Base event type
|
||||
map.put(GrimEvent.class, new AtomicReference<>(new OptimizedListener[0]));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void registerAnnotatedListeners(@NotNull Object pluginContext, @NotNull Object listener) {
|
||||
GrimPlugin plugin = extensionManager.getPlugin(pluginContext);
|
||||
registerAnnotatedListeners(plugin, listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerAnnotatedListeners(GrimPlugin plugin, @NotNull Object listener) {
|
||||
registerMethods(plugin, listener, listener.getClass());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerStaticAnnotatedListeners(@NotNull Object pluginContext, @NotNull Class<?> clazz) {
|
||||
GrimPlugin plugin = extensionManager.getPlugin(pluginContext);
|
||||
registerStaticAnnotatedListeners(plugin, clazz);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerStaticAnnotatedListeners(GrimPlugin plugin, @NotNull Class<?> clazz) {
|
||||
registerMethods(plugin, null, clazz);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterListeners(@NotNull Object pluginContext, @NotNull Object listener) {
|
||||
GrimPlugin plugin = extensionManager.getPlugin(pluginContext);
|
||||
unregisterListeners(plugin, listener);
|
||||
}
|
||||
|
||||
private void registerMethods(GrimPlugin plugin, @Nullable Object instance, @NotNull Class<?> clazz) {
|
||||
for (Method method : clazz.getDeclaredMethods()) {
|
||||
GrimEventHandler annotation = method.getAnnotation(GrimEventHandler.class);
|
||||
if (annotation != null && method.getParameterCount() == 1) {
|
||||
Class<?> eventType = method.getParameterTypes()[0];
|
||||
if (GrimEvent.class.isAssignableFrom(eventType)) {
|
||||
try {
|
||||
if (instance == null && !Modifier.isStatic(method.getModifiers())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
method.setAccessible(true);
|
||||
MethodHandle handle = lookup.unreflect(method);
|
||||
|
||||
final GrimEventListener<GrimEvent> listener = getGrimEventListener(instance, handle, eventType);
|
||||
|
||||
OptimizedListener optimizedListener = new OptimizedListener(
|
||||
plugin, listener, annotation.priority(),
|
||||
annotation.ignoreCancelled(), method.getDeclaringClass(), instance
|
||||
);
|
||||
|
||||
addListener((Class<? extends GrimEvent>) eventType, optimizedListener);
|
||||
} catch (IllegalAccessException e) {
|
||||
LogUtil.error("Failed to register listener for " + eventType.getName(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Decide which listener to create at registration time to avoid the runtime 'if' check.
|
||||
private @NotNull GrimEventListener<GrimEvent> getGrimEventListener(@Nullable Object instance, MethodHandle handle, Class<?> eventType) {
|
||||
final GrimEventListener<GrimEvent> listener;
|
||||
if (instance != null) {
|
||||
// For instance methods, create a listener that captures the instance.
|
||||
listener = event -> {
|
||||
try {
|
||||
handle.invoke(instance, event);
|
||||
} catch (Throwable throwable) {
|
||||
throw new RuntimeException("Failed to invoke listener for " + eventType.getName(), throwable);
|
||||
}
|
||||
};
|
||||
} else {
|
||||
// For static methods, create a listener that does NOT capture an instance.
|
||||
// This is slightly more memory efficient and avoids the null check.
|
||||
listener = event -> {
|
||||
try {
|
||||
handle.invoke(event);
|
||||
} catch (Throwable throwable) {
|
||||
throw new RuntimeException("Failed to invoke listener for " + eventType.getName(), throwable);
|
||||
}
|
||||
};
|
||||
}
|
||||
return listener;
|
||||
}
|
||||
|
||||
private void addListener(Class<? extends GrimEvent> eventType, OptimizedListener newListener) {
|
||||
AtomicReference<OptimizedListener[]> ref = listenerMap.computeIfAbsent(
|
||||
eventType, k -> new AtomicReference<>(new OptimizedListener[0])
|
||||
);
|
||||
|
||||
while (true) {
|
||||
OptimizedListener[] oldArray = ref.get();
|
||||
|
||||
// Find insertion point using Arrays.binarySearch
|
||||
// We use a comparator for descending order (higher priorities first)
|
||||
int insertionPoint = Arrays.binarySearch(oldArray, newListener,
|
||||
(a, b) -> Integer.compare(b.priority, a.priority));
|
||||
|
||||
// If negative, convert to insertion point
|
||||
if (insertionPoint < 0) {
|
||||
insertionPoint = -(insertionPoint + 1);
|
||||
} else {
|
||||
// If we found an exact match, insert after the last equal priority element
|
||||
// to maintain stable ordering
|
||||
while (insertionPoint < oldArray.length - 1 &&
|
||||
oldArray[insertionPoint + 1].priority == newListener.priority) {
|
||||
insertionPoint++;
|
||||
}
|
||||
insertionPoint++; // Insert after the last equal priority element
|
||||
}
|
||||
|
||||
OptimizedListener[] newArray = new OptimizedListener[oldArray.length + 1];
|
||||
|
||||
// Copy elements before insertion point
|
||||
System.arraycopy(oldArray, 0, newArray, 0, insertionPoint);
|
||||
|
||||
// Insert the new listener
|
||||
newArray[insertionPoint] = newListener;
|
||||
|
||||
// Copy remaining elements, shifted by one
|
||||
System.arraycopy(oldArray, insertionPoint, newArray, insertionPoint + 1,
|
||||
oldArray.length - insertionPoint);
|
||||
|
||||
if (ref.compareAndSet(oldArray, newArray)) {
|
||||
break;
|
||||
}
|
||||
// If CAS failed, loop and try again
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterListeners(GrimPlugin plugin, Object instance) {
|
||||
for (Map.Entry<Class<? extends GrimEvent>, AtomicReference<OptimizedListener[]>> entry : listenerMap.entrySet()) {
|
||||
removeListeners(entry.getKey(), entry.getValue(),
|
||||
listener -> listener.plugin.equals(plugin) &&
|
||||
listener.instance != null &&
|
||||
listener.instance.equals(instance));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterStaticListeners(@NotNull Object pluginContext, @NotNull Class<?> clazz) {
|
||||
GrimPlugin plugin = extensionManager.getPlugin(pluginContext);
|
||||
unregisterStaticListeners(plugin, clazz);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterStaticListeners(GrimPlugin plugin, Class<?> clazz) {
|
||||
for (Map.Entry<Class<? extends GrimEvent>, AtomicReference<OptimizedListener[]>> entry : listenerMap.entrySet()) {
|
||||
removeListeners(entry.getKey(), entry.getValue(),
|
||||
listener -> listener.plugin.equals(plugin) &&
|
||||
listener.instance == null &&
|
||||
listener.declaringClass.equals(clazz));
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void unregisterAllListeners(@NotNull Object pluginContext) {
|
||||
GrimPlugin plugin = extensionManager.getPlugin(pluginContext);
|
||||
unregisterAllListeners(plugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterAllListeners(GrimPlugin plugin) {
|
||||
for (Map.Entry<Class<? extends GrimEvent>, AtomicReference<OptimizedListener[]>> entry : listenerMap.entrySet()) {
|
||||
removeListeners(entry.getKey(), entry.getValue(),
|
||||
listener -> listener.plugin.equals(plugin));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterListener(@NotNull Object pluginContext, @NotNull GrimEventListener<?> listener) {
|
||||
GrimPlugin plugin = extensionManager.getPlugin(pluginContext);
|
||||
unregisterListener(plugin, listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterListener(GrimPlugin plugin, GrimEventListener<?> eventListener) {
|
||||
for (Map.Entry<Class<? extends GrimEvent>, AtomicReference<OptimizedListener[]>> entry : listenerMap.entrySet()) {
|
||||
removeListeners(entry.getKey(), entry.getValue(),
|
||||
listener -> listener.plugin.equals(plugin) &&
|
||||
listener.listener.equals(eventListener));
|
||||
}
|
||||
}
|
||||
|
||||
private void removeListeners(Class<? extends GrimEvent> eventType,
|
||||
AtomicReference<OptimizedListener[]> ref,
|
||||
Predicate<OptimizedListener> filter) {
|
||||
while (true) {
|
||||
OptimizedListener[] oldArray = ref.get();
|
||||
|
||||
// Count how many listeners will remain after filtering
|
||||
int remaining = 0;
|
||||
for (OptimizedListener listener : oldArray) {
|
||||
if (!filter.test(listener)) {
|
||||
remaining++;
|
||||
}
|
||||
}
|
||||
|
||||
// If no change, return early
|
||||
if (remaining == oldArray.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create new array with filtered listeners
|
||||
OptimizedListener[] newArray = new OptimizedListener[remaining];
|
||||
int index = 0;
|
||||
for (OptimizedListener listener : oldArray) {
|
||||
if (!filter.test(listener)) {
|
||||
newArray[index++] = listener;
|
||||
}
|
||||
}
|
||||
|
||||
// Try to update the array
|
||||
if (ref.compareAndSet(oldArray, newArray)) {
|
||||
// If array is empty, remove the event type entry
|
||||
if (newArray.length == 0) {
|
||||
listenerMap.remove(eventType);
|
||||
}
|
||||
break;
|
||||
}
|
||||
// If CAS failed, retry
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void post(@NotNull GrimEvent event) {
|
||||
Class<?> currentEventType = event.getClass();
|
||||
while (GrimEvent.class.isAssignableFrom(currentEventType)) {
|
||||
AtomicReference<OptimizedListener[]> ref = listenerMap.get(currentEventType);
|
||||
if (ref != null) {
|
||||
// Get current array - no locking needed for reads
|
||||
OptimizedListener[] listeners = ref.get();
|
||||
for (OptimizedListener listener : listeners) {
|
||||
try {
|
||||
if (event.isCancelled() && !listener.ignoreCancelled) {
|
||||
continue;
|
||||
}
|
||||
listener.listener.handle(event);
|
||||
} catch (Throwable throwable) {
|
||||
LogUtil.error("Error handling event " + event.getEventName(), throwable);
|
||||
}
|
||||
}
|
||||
}
|
||||
currentEventType = currentEventType.getSuperclass();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends GrimEvent> void subscribe(@NotNull Object pluginContext, @NotNull Class<T> eventType, @NotNull GrimEventListener<T> listener, int priority, boolean ignoreCancelled, @NotNull Class<?> declaringClass) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends GrimEvent> void subscribe(GrimPlugin plugin, @NotNull Class<T> eventType,
|
||||
@NotNull GrimEventListener<T> listener, int priority,
|
||||
boolean ignoreCancelled, @NotNull Class<?> declaringClass) {
|
||||
@SuppressWarnings("unchecked")
|
||||
OptimizedListener optimizedListener = new OptimizedListener(
|
||||
plugin, (GrimEventListener<GrimEvent>)listener, priority,
|
||||
ignoreCancelled, declaringClass, null
|
||||
);
|
||||
|
||||
addListener(eventType, optimizedListener);
|
||||
}
|
||||
|
||||
private static class OptimizedListener {
|
||||
final GrimPlugin plugin;
|
||||
final GrimEventListener<GrimEvent> listener;
|
||||
final int priority;
|
||||
final boolean ignoreCancelled;
|
||||
final Class<?> declaringClass;
|
||||
final Object instance;
|
||||
|
||||
OptimizedListener(GrimPlugin plugin, GrimEventListener<GrimEvent> listener,
|
||||
int priority, boolean ignoreCancelled,
|
||||
Class<?> declaringClass, Object instance) {
|
||||
this.plugin = plugin;
|
||||
this.listener = listener;
|
||||
this.priority = priority;
|
||||
this.ignoreCancelled = ignoreCancelled;
|
||||
this.declaringClass = declaringClass;
|
||||
this.instance = instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
package ac.grim.grimac.events;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface ResolutionFailureHandler {
|
||||
/**
|
||||
* Creates a runtime exception to be thrown when resolution fails.
|
||||
*
|
||||
* @param failedContext The object that could not be resolved.
|
||||
* @return The exception to be thrown.
|
||||
*/
|
||||
RuntimeException createExceptionFor(@NotNull Object failedContext);
|
||||
}
|
||||
@@ -485,7 +485,7 @@ public class CheckManagerListener extends PacketListenerAbstract {
|
||||
player.yaw = move.getYaw();
|
||||
player.pitch = move.getPitch();
|
||||
|
||||
final VehiclePositionUpdate update = new VehiclePositionUpdate(clamp, position, move.getYaw(), move.getPitch(), player.packetStateData.lastPacketWasTeleport);
|
||||
final VehiclePositionUpdate update = new VehiclePositionUpdate(clamp, position, move.getYaw(), move.getPitch(), move.isOnGround(), player.packetStateData.lastPacketWasTeleport);
|
||||
player.checkManager.onVehiclePositionUpdate(update);
|
||||
|
||||
player.packetStateData.receivedSteerVehicle = false;
|
||||
@@ -582,6 +582,11 @@ public class CheckManagerListener extends PacketListenerAbstract {
|
||||
// The player didn't send a movement packet, so we can predict this like we had idle tick on 1.8
|
||||
player.packetStateData.didLastLastMovementIncludePosition = player.packetStateData.didLastMovementIncludePosition;
|
||||
player.packetStateData.didLastMovementIncludePosition = false;
|
||||
|
||||
// Track dash cooldown
|
||||
if (!player.inVehicle()) {
|
||||
player.dashableEntities.tick();
|
||||
}
|
||||
}
|
||||
player.packetStateData.didSendMovementBeforeTickEnd = false;
|
||||
}
|
||||
@@ -717,6 +722,8 @@ public class CheckManagerListener extends PacketListenerAbstract {
|
||||
if (hasLook) {
|
||||
player.yaw = yaw;
|
||||
player.pitch = pitch;
|
||||
player.vehicleData.playerPitch = pitch;
|
||||
player.vehicleData.playerYaw = yaw;
|
||||
|
||||
float deltaXRot = player.yaw - player.lastYaw;
|
||||
float deltaYRot = player.pitch - player.lastPitch;
|
||||
|
||||
@@ -3,9 +3,14 @@ package ac.grim.grimac.events.packets;
|
||||
import ac.grim.grimac.GrimAPI;
|
||||
import ac.grim.grimac.checks.impl.elytra.ElytraA;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.data.Pair;
|
||||
import ac.grim.grimac.utils.data.packetentity.JumpableEntity;
|
||||
import ac.grim.grimac.utils.data.packetentity.PacketEntity;
|
||||
import com.github.retrooper.packetevents.PacketEvents;
|
||||
import com.github.retrooper.packetevents.event.PacketListenerAbstract;
|
||||
import com.github.retrooper.packetevents.event.PacketListenerPriority;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.manager.server.ServerVersion;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientEntityAction;
|
||||
@@ -43,8 +48,10 @@ public class PacketEntityAction extends PacketListenerAbstract {
|
||||
player.isSneaking = false;
|
||||
break;
|
||||
case START_FLYING_WITH_ELYTRA:
|
||||
if (PacketEvents.getAPI().getServerManager().getVersion().isOlderThan(ServerVersion.V_1_9)) return;
|
||||
|
||||
if (player.onGround || player.lastOnGround) {
|
||||
player.getSetbackTeleportUtil().executeForceResync();
|
||||
player.getSetbackTeleportUtil().executeNonSimulatingForceResync();
|
||||
|
||||
if (player.platformPlayer != null) {
|
||||
// Client ignores sneaking, use it to resync
|
||||
@@ -66,7 +73,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());
|
||||
@@ -76,12 +83,10 @@ public class PacketEntityAction extends PacketListenerAbstract {
|
||||
}
|
||||
break;
|
||||
case START_JUMPING_WITH_HORSE:
|
||||
int jumpBoost = action.getJumpBoost();
|
||||
if (jumpBoost < 0) jumpBoost = 0;
|
||||
if (jumpBoost >= 90) {
|
||||
player.vehicleData.nextHorseJump = 1;
|
||||
} else {
|
||||
player.vehicleData.nextHorseJump = 0.4F + 0.4F * jumpBoost / 90.0F;
|
||||
PacketEntity riding = player.compensatedEntities.self.getRiding();
|
||||
if (riding instanceof JumpableEntity jumpable) {
|
||||
if (player.vehicleData.pendingJumps.size() >= 20) return; // discard
|
||||
player.vehicleData.pendingJumps.add(new Pair<>(action.getJumpBoost(), jumpable));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import ac.grim.grimac.checks.type.PacketCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.anticheat.LogUtil;
|
||||
import ac.grim.grimac.utils.data.TrackerData;
|
||||
import ac.grim.grimac.utils.data.packetentity.DashableEntity;
|
||||
import ac.grim.grimac.utils.data.packetentity.PacketEntity;
|
||||
import ac.grim.grimac.utils.data.packetentity.PacketEntityHook;
|
||||
import ac.grim.grimac.utils.data.packetentity.PacketEntityTrackXRot;
|
||||
@@ -152,13 +153,15 @@ public class PacketEntityReplication extends Check implements PacketCheck {
|
||||
// 1.19.3+
|
||||
else if (event.getPacketType() == PacketType.Play.Server.PLAYER_INFO_UPDATE) {
|
||||
WrapperPlayServerPlayerInfoUpdate info = new WrapperPlayServerPlayerInfoUpdate(event);
|
||||
player.latencyUtils.addRealTimeTask(player.lastTransactionSent.get(), () -> {
|
||||
for (WrapperPlayServerPlayerInfoUpdate.PlayerInfo entry : info.getEntries()) {
|
||||
final UserProfile gameProfile = entry.getGameProfile();
|
||||
final UUID uuid = gameProfile.getUUID();
|
||||
player.compensatedEntities.profiles.put(uuid, gameProfile);
|
||||
}
|
||||
});
|
||||
if (info.getActions().contains(WrapperPlayServerPlayerInfoUpdate.Action.ADD_PLAYER)) {
|
||||
player.latencyUtils.addRealTimeTask(player.lastTransactionSent.get(), () -> {
|
||||
for (WrapperPlayServerPlayerInfoUpdate.PlayerInfo entry : info.getEntries()) {
|
||||
final UserProfile gameProfile = entry.getGameProfile();
|
||||
final UUID uuid = gameProfile.getUUID();
|
||||
player.compensatedEntities.profiles.put(uuid, gameProfile);
|
||||
}
|
||||
});
|
||||
}
|
||||
} else if (event.getPacketType() == PacketType.Play.Server.PLAYER_INFO_REMOVE) {
|
||||
WrapperPlayServerPlayerInfoRemove remove = new WrapperPlayServerPlayerInfoRemove(event);
|
||||
player.latencyUtils.addRealTimeTask(player.lastTransactionSent.get(), () -> remove.getProfileIds().forEach(player.compensatedEntities.profiles::remove));
|
||||
@@ -377,10 +380,11 @@ public class PacketEntityReplication extends Check implements PacketCheck {
|
||||
|
||||
final int destroyTransaction = player.lastTransactionSent.get() + 1;
|
||||
player.latencyUtils.addRealTimeTask(destroyTransaction, () -> {
|
||||
for (int integer : destroyEntityIds) {
|
||||
player.compensatedEntities.removeEntity(integer);
|
||||
player.fireworks.removeFirework(integer);
|
||||
player.compensatedEntities.entitiesRemovedThisTick.add(integer);
|
||||
for (int entityId : destroyEntityIds) {
|
||||
player.compensatedEntities.removeEntity(entityId);
|
||||
player.dashableEntities.removeEntity(entityId);
|
||||
player.fireworks.removeFirework(entityId);
|
||||
player.compensatedEntities.entitiesRemovedThisTick.add(entityId);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -522,7 +526,11 @@ public class PacketEntityReplication extends Check implements PacketCheck {
|
||||
player.compensatedEntities.serverPositionsMap.put(entityID, new TrackerData(position.getX(), position.getY(), position.getZ(), xRot, yRot, type, player.lastTransactionSent.get()));
|
||||
|
||||
player.latencyUtils.addRealTimeTask(player.lastTransactionSent.get(), () -> {
|
||||
player.compensatedEntities.addEntity(entityID, uuid, type, position, xRot, extraData);
|
||||
PacketEntity entity = player.compensatedEntities.addEntity(entityID, uuid, type, position, xRot, extraData);
|
||||
if (entity instanceof DashableEntity dashable) {
|
||||
player.dashableEntities.addEntity(entityID, dashable);
|
||||
}
|
||||
|
||||
if (entityMetadata != null) {
|
||||
player.compensatedEntities.updateEntityMetadata(entityID, entityMetadata);
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -3,11 +3,11 @@ package ac.grim.grimac.events.packets;
|
||||
import ac.grim.grimac.GrimAPI;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.predictionengine.predictions.PredictionEngine;
|
||||
import ac.grim.grimac.utils.math.Vec2;
|
||||
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
|
||||
import ac.grim.grimac.utils.data.KnownInput;
|
||||
import ac.grim.grimac.utils.data.packetentity.JumpableEntity;
|
||||
import ac.grim.grimac.utils.data.packetentity.PacketEntity;
|
||||
import ac.grim.grimac.utils.data.packetentity.PacketEntityHorse;
|
||||
import ac.grim.grimac.utils.math.Vec2;
|
||||
import com.github.retrooper.packetevents.PacketEvents;
|
||||
import com.github.retrooper.packetevents.event.PacketListenerAbstract;
|
||||
import com.github.retrooper.packetevents.event.PacketListenerPriority;
|
||||
@@ -98,7 +98,7 @@ public class PacketPlayerSteer extends PacketListenerAbstract {
|
||||
if (player.packetStateData.receivedSteerVehicle && riding != null) {
|
||||
// Horse and boat have first passenger in control
|
||||
// If the player is the first passenger, disregard this attempt to have the server control the entity
|
||||
if ((riding.isBoat || riding.isHappyGhast || (riding instanceof PacketEntityHorse horse && horse.hasSaddle())) &&
|
||||
if ((riding.isBoat || riding.isHappyGhast || (riding instanceof JumpableEntity jumpable && jumpable.hasSaddle())) &&
|
||||
riding.passengers.get(0) == player.compensatedEntities.self &&
|
||||
// Although if the player has server controlled entities
|
||||
player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_9) &&
|
||||
@@ -110,6 +110,7 @@ public class PacketPlayerSteer extends PacketListenerAbstract {
|
||||
// Tick update
|
||||
player.compensatedWorld.tickPlayerInPistonPushingArea();
|
||||
player.compensatedEntities.tick();
|
||||
player.dashableEntities.tick();
|
||||
|
||||
// Note for the movement check
|
||||
player.vehicleData.lastDummy = true;
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
package ac.grim.grimac.events.packets;
|
||||
|
||||
import ac.grim.grimac.GrimAPI;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import com.github.retrooper.packetevents.event.PacketListenerAbstract;
|
||||
import com.github.retrooper.packetevents.event.PacketListenerPriority;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerFlying;
|
||||
|
||||
public class PacketPlayerTick extends PacketListenerAbstract {
|
||||
|
||||
public PacketPlayerTick() {
|
||||
super(PacketListenerPriority.LOW);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPreVia() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
if (event.getPacketType() == PacketType.Play.Client.CLIENT_TICK_END) {
|
||||
GrimPlayer player = GrimAPI.INSTANCE.getPlayerDataManager().getPlayer(event.getUser());
|
||||
if (player == null || player.getClientVersion().isOlderThan(ClientVersion.V_1_21_2))
|
||||
return;
|
||||
|
||||
PacketWorldBorder border = player.checkManager.getPacketCheck(PacketWorldBorder.class);
|
||||
border.tickBorder();
|
||||
} else if (WrapperPlayClientPlayerFlying.isFlying(event.getPacketType())) {
|
||||
GrimPlayer player = GrimAPI.INSTANCE.getPlayerDataManager().getPlayer(event.getUser());
|
||||
if (player == null || player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_21_2))
|
||||
return;
|
||||
|
||||
PacketWorldBorder border = player.checkManager.getPacketCheck(PacketWorldBorder.class);
|
||||
border.tickBorder();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,9 +3,15 @@ package ac.grim.grimac.events.packets;
|
||||
import ac.grim.grimac.checks.Check;
|
||||
import ac.grim.grimac.checks.type.PacketCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.math.GrimMath;
|
||||
import ac.grim.grimac.utils.worldborder.BorderExtent;
|
||||
import ac.grim.grimac.utils.worldborder.RealTimeMovingBorderExtent;
|
||||
import ac.grim.grimac.utils.worldborder.StaticBorderExtent;
|
||||
import ac.grim.grimac.utils.worldborder.TickBasedMovingBorderExtent;
|
||||
import com.github.retrooper.packetevents.PacketEvents;
|
||||
import com.github.retrooper.packetevents.event.PacketSendEvent;
|
||||
import com.github.retrooper.packetevents.manager.server.ServerVersion;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerInitializeWorldBorder;
|
||||
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerWorldBorder;
|
||||
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerWorldBorderCenter;
|
||||
@@ -19,20 +25,38 @@ public class PacketWorldBorder extends Check implements PacketCheck {
|
||||
private double centerX;
|
||||
@Getter
|
||||
private double centerZ;
|
||||
private double oldDiameter;
|
||||
private double newDiameter;
|
||||
@Getter
|
||||
private double absoluteMaxSize;
|
||||
private long startTime = 1;
|
||||
private long endTime = 1;
|
||||
@Getter
|
||||
private BorderExtent extent;
|
||||
|
||||
public PacketWorldBorder(GrimPlayer playerData) {
|
||||
super(playerData);
|
||||
this.extent = new StaticBorderExtent(5.999997E7);
|
||||
}
|
||||
|
||||
public double getCurrentDiameter() {
|
||||
double d0 = (double) (System.currentTimeMillis() - startTime) / ((double) endTime - startTime);
|
||||
return d0 < 1.0D ? GrimMath.lerp(d0, oldDiameter, newDiameter) : newDiameter;
|
||||
return extent.size();
|
||||
}
|
||||
|
||||
public double getMinX() {
|
||||
return extent.getMinX(centerX, absoluteMaxSize);
|
||||
}
|
||||
|
||||
public double getMaxX() {
|
||||
return extent.getMaxX(centerX, absoluteMaxSize);
|
||||
}
|
||||
|
||||
public double getMinZ() {
|
||||
return extent.getMinZ(centerZ, absoluteMaxSize);
|
||||
}
|
||||
|
||||
public double getMaxZ() {
|
||||
return extent.getMaxZ(centerZ, absoluteMaxSize);
|
||||
}
|
||||
|
||||
public void tickBorder() {
|
||||
extent = extent.tick();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -117,15 +141,28 @@ public class PacketWorldBorder extends Check implements PacketCheck {
|
||||
|
||||
@Contract(mutates = "this")
|
||||
private void setSize(double size) {
|
||||
oldDiameter = size;
|
||||
newDiameter = size;
|
||||
this.extent = new StaticBorderExtent(size);
|
||||
}
|
||||
|
||||
@Contract(mutates = "this")
|
||||
private void setLerp(double oldDiameter, double newDiameter, long length) {
|
||||
this.oldDiameter = oldDiameter;
|
||||
this.newDiameter = newDiameter;
|
||||
this.startTime = System.currentTimeMillis();
|
||||
this.endTime = this.startTime + length;
|
||||
private void setLerp(double oldDiameter, double newDiameter, long speed) {
|
||||
if (speed <= 0 || oldDiameter == newDiameter) {
|
||||
this.extent = new StaticBorderExtent(newDiameter);
|
||||
} else {
|
||||
this.extent = createMovingExtent(oldDiameter, newDiameter, speed);
|
||||
}
|
||||
}
|
||||
|
||||
private static final boolean SERVER_TICK_BASED = PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_21_11);
|
||||
|
||||
private BorderExtent createMovingExtent(double from, double to, long speed) {
|
||||
if (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_21_11)) { // tick-based
|
||||
long durationTicks = SERVER_TICK_BASED ? speed : (speed / 50);
|
||||
return new TickBasedMovingBorderExtent(from, to, durationTicks);
|
||||
} else { // real-time based
|
||||
long durationMs = SERVER_TICK_BASED ? (speed * 50) : speed;
|
||||
return new RealTimeMovingBorderExtent(from, to, durationMs);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -11,6 +11,7 @@ import ac.grim.grimac.platform.api.player.PlatformPlayer;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.anticheat.MessageUtil;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -155,6 +156,30 @@ public final class AlertManagerImpl implements AlertManager, ConfigReloadable, S
|
||||
setPlayerStateAndNotify(requirePlatformPlayerFromUser(player), enabled, silent, AlertType.BRAND);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasAlertsEnabled(Player player) {
|
||||
if (player == null) return false;
|
||||
return hasAlertsEnabled(GrimAPI.INSTANCE.getPlatformPlayerFactory().getFromNativePlayerType(player));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toggleAlerts(Player player) {
|
||||
if (player == null) return;
|
||||
toggleAlerts(GrimAPI.INSTANCE.getPlatformPlayerFactory().getFromNativePlayerType(player));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasVerboseEnabled(Player player) {
|
||||
if (player == null) return false;
|
||||
return hasVerboseEnabled(GrimAPI.INSTANCE.getPlatformPlayerFactory().getFromNativePlayerType(player));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toggleVerbose(Player player) {
|
||||
if (player == null) return;
|
||||
toggleVerbose(GrimAPI.INSTANCE.getPlatformPlayerFactory().getFromNativePlayerType(player));
|
||||
}
|
||||
|
||||
public void handlePlayerQuit(@Nullable PlatformPlayer platformPlayer) {
|
||||
if (platformPlayer == null) return;
|
||||
|
||||
|
||||
@@ -50,6 +50,7 @@ import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.predictionengine.GhostBlockDetector;
|
||||
import ac.grim.grimac.predictionengine.SneakingEstimator;
|
||||
import ac.grim.grimac.utils.anticheat.update.*;
|
||||
import ac.grim.grimac.utils.latency.CompensatedCameraEntity;
|
||||
import ac.grim.grimac.utils.latency.CompensatedCooldown;
|
||||
import ac.grim.grimac.utils.latency.CompensatedFireworks;
|
||||
import ac.grim.grimac.utils.latency.CompensatedInventory;
|
||||
@@ -59,6 +60,8 @@ import com.github.retrooper.packetevents.event.PacketSendEvent;
|
||||
import com.google.common.collect.ClassToInstanceMap;
|
||||
import com.google.common.collect.ImmutableClassToInstanceMap;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
public class CheckManager {
|
||||
@@ -75,8 +78,18 @@ public class CheckManager {
|
||||
private final ClassToInstanceMap<PostPredictionCheck> postPredictionChecks;
|
||||
private PacketEntityReplication packetEntityReplication = null;
|
||||
|
||||
private final List<PacketCheck> packetChecksValues;
|
||||
private final List<PositionCheck> positionChecksValues;
|
||||
private final List<RotationCheck> rotationChecksValues;
|
||||
private final List<VehicleCheck> vehicleChecksValues;
|
||||
private final List<PacketCheck> prePredictionChecksValues;
|
||||
private final List<BlockBreakCheck> blockBreakChecksValues;
|
||||
private final List<BlockPlaceCheck> blockPlaceChecksValues;
|
||||
private final List<PostPredictionCheck> postPredictionChecksValues;
|
||||
|
||||
public CheckManager(GrimPlayer player) {
|
||||
packetChecks = new ImmutableClassToInstanceMap.Builder<PacketCheck>()
|
||||
.put(CompensatedCameraEntity.class, player.cameraEntity)
|
||||
.put(PacketOrderProcessor.class, player.packetOrderProcessor)
|
||||
.put(Reach.class, new Reach(player))
|
||||
.put(PacketEntityReplication.class, new PacketEntityReplication(player))
|
||||
@@ -163,6 +176,7 @@ public class CheckManager {
|
||||
.put(ExplosionHandler.class, new ExplosionHandler(player))
|
||||
.put(KnockbackHandler.class, new KnockbackHandler(player))
|
||||
.put(GhostBlockDetector.class, new GhostBlockDetector(player))
|
||||
.put(InventoryD.class, new InventoryD(player))
|
||||
.put(Phase.class, new Phase(player))
|
||||
.put(Post.class, new Post(player))
|
||||
.put(PacketOrderA.class, new PacketOrderA(player))
|
||||
@@ -272,6 +286,15 @@ public class CheckManager {
|
||||
.putAll(noneModules)
|
||||
.build();
|
||||
|
||||
packetChecksValues = new ArrayList<>(packetChecks.values());
|
||||
positionChecksValues = new ArrayList<>(positionChecks.values());
|
||||
rotationChecksValues = new ArrayList<>(rotationChecks.values());
|
||||
vehicleChecksValues = new ArrayList<>(vehicleChecks.values());
|
||||
prePredictionChecksValues = new ArrayList<>(prePredictionChecks.values());
|
||||
blockBreakChecksValues = new ArrayList<>(blockBreakChecks.values());
|
||||
blockPlaceChecksValues = new ArrayList<>(blockPlaceChecks.values());
|
||||
postPredictionChecksValues = new ArrayList<>(postPredictionChecks.values());
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
@@ -296,103 +319,103 @@ public class CheckManager {
|
||||
}
|
||||
|
||||
public void onPrePredictionReceivePacket(final PacketReceiveEvent packet) {
|
||||
for (PacketCheck check : prePredictionChecks.values()) {
|
||||
for (PacketCheck check : prePredictionChecksValues) {
|
||||
check.onPacketReceive(packet);
|
||||
}
|
||||
}
|
||||
|
||||
public void onPacketReceive(final PacketReceiveEvent packet) {
|
||||
for (PacketCheck check : packetChecks.values()) {
|
||||
for (PacketCheck check : packetChecksValues) {
|
||||
check.onPacketReceive(packet);
|
||||
}
|
||||
for (PostPredictionCheck check : postPredictionChecks.values()) {
|
||||
for (PostPredictionCheck check : postPredictionChecksValues) {
|
||||
check.onPacketReceive(packet);
|
||||
}
|
||||
for (BlockPlaceCheck check : blockPlaceChecks.values()) {
|
||||
for (BlockPlaceCheck check : blockPlaceChecksValues) {
|
||||
check.onPacketReceive(packet);
|
||||
}
|
||||
for (BlockBreakCheck check : blockBreakChecks.values()) {
|
||||
for (BlockBreakCheck check : blockBreakChecksValues) {
|
||||
check.onPacketReceive(packet);
|
||||
}
|
||||
}
|
||||
|
||||
public void onPacketSend(final PacketSendEvent packet) {
|
||||
for (PacketCheck check : prePredictionChecks.values()) {
|
||||
for (PacketCheck check : prePredictionChecksValues) {
|
||||
check.onPacketSend(packet);
|
||||
}
|
||||
for (PacketCheck check : packetChecks.values()) {
|
||||
for (PacketCheck check : packetChecksValues) {
|
||||
check.onPacketSend(packet);
|
||||
}
|
||||
for (PostPredictionCheck check : postPredictionChecks.values()) {
|
||||
for (PostPredictionCheck check : postPredictionChecksValues) {
|
||||
check.onPacketSend(packet);
|
||||
}
|
||||
for (BlockPlaceCheck check : blockPlaceChecks.values()) {
|
||||
for (BlockPlaceCheck check : blockPlaceChecksValues) {
|
||||
check.onPacketSend(packet);
|
||||
}
|
||||
for (BlockBreakCheck check : blockBreakChecks.values()) {
|
||||
for (BlockBreakCheck check : blockBreakChecksValues) {
|
||||
check.onPacketSend(packet);
|
||||
}
|
||||
}
|
||||
|
||||
public void onPositionUpdate(final PositionUpdate position) {
|
||||
for (PositionCheck check : positionChecks.values()) {
|
||||
for (PositionCheck check : positionChecksValues) {
|
||||
check.onPositionUpdate(position);
|
||||
}
|
||||
}
|
||||
|
||||
public void onRotationUpdate(final RotationUpdate rotation) {
|
||||
for (RotationCheck check : rotationChecks.values()) {
|
||||
for (RotationCheck check : rotationChecksValues) {
|
||||
check.process(rotation);
|
||||
}
|
||||
for (BlockPlaceCheck check : blockPlaceChecks.values()) {
|
||||
for (BlockPlaceCheck check : blockPlaceChecksValues) {
|
||||
check.process(rotation);
|
||||
}
|
||||
}
|
||||
|
||||
public void onVehiclePositionUpdate(final VehiclePositionUpdate update) {
|
||||
for (VehicleCheck check : vehicleChecks.values()) {
|
||||
for (VehicleCheck check : vehicleChecksValues) {
|
||||
check.process(update);
|
||||
}
|
||||
}
|
||||
|
||||
public void onPredictionFinish(final PredictionComplete complete) {
|
||||
for (PostPredictionCheck check : postPredictionChecks.values()) {
|
||||
for (PostPredictionCheck check : postPredictionChecksValues) {
|
||||
check.onPredictionComplete(complete);
|
||||
}
|
||||
for (BlockPlaceCheck check : blockPlaceChecks.values()) {
|
||||
for (BlockPlaceCheck check : blockPlaceChecksValues) {
|
||||
check.onPredictionComplete(complete);
|
||||
}
|
||||
for (BlockBreakCheck check : blockBreakChecks.values()) {
|
||||
for (BlockBreakCheck check : blockBreakChecksValues) {
|
||||
check.onPredictionComplete(complete);
|
||||
}
|
||||
}
|
||||
|
||||
public void onBlockPlace(final BlockPlace place) {
|
||||
for (BlockPlaceCheck check : blockPlaceChecks.values()) {
|
||||
for (BlockPlaceCheck check : blockPlaceChecksValues) {
|
||||
check.onBlockPlace(place);
|
||||
}
|
||||
}
|
||||
|
||||
public void onPostFlyingBlockPlace(final BlockPlace place) {
|
||||
for (BlockPlaceCheck check : blockPlaceChecks.values()) {
|
||||
for (BlockPlaceCheck check : blockPlaceChecksValues) {
|
||||
check.onPostFlyingBlockPlace(place);
|
||||
}
|
||||
}
|
||||
|
||||
public void onBlockBreak(final BlockBreak blockBreak) {
|
||||
for (BlockBreakCheck check : blockBreakChecks.values()) {
|
||||
for (BlockBreakCheck check : blockBreakChecksValues) {
|
||||
check.onBlockBreak(blockBreak);
|
||||
}
|
||||
for (BlockPlaceCheck check : blockPlaceChecks.values()) {
|
||||
for (BlockPlaceCheck check : blockPlaceChecksValues) {
|
||||
check.onBlockBreak(blockBreak);
|
||||
}
|
||||
}
|
||||
|
||||
public void onPostFlyingBlockBreak(final BlockBreak blockBreak) {
|
||||
for (BlockBreakCheck check : blockBreakChecks.values()) {
|
||||
for (BlockBreakCheck check : blockBreakChecksValues) {
|
||||
check.onPostFlyingBlockBreak(blockBreak);
|
||||
}
|
||||
for (BlockPlaceCheck check : blockPlaceChecks.values()) {
|
||||
for (BlockPlaceCheck check : blockPlaceChecksValues) {
|
||||
check.onPostFlyingBlockBreak(blockBreak);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ 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.data.Pair;
|
||||
import ac.grim.grimac.utils.webhook.*;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -21,16 +22,17 @@ import java.net.http.HttpResponse;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ConcurrentLinkedDeque;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class DiscordManager implements StartableInitable, ReloadableInitable {
|
||||
private static final Predicate<String> WEBHOOK_REGEX = Pattern.compile("https://discord.com/api/webhooks/\\d+/[\\w-]+").asMatchPredicate();
|
||||
private static final Predicate<String> WEBHOOK_REGEX = Pattern.compile("^https://discord\\.com/api(?:/v\\d+)?/webhooks/\\d+/[\\w-]+(\\?thread_id=\\d+)?$").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 ConcurrentLinkedDeque<Pair<HttpRequest, CompletableFuture<Boolean>>> requests = new ConcurrentLinkedDeque<>();
|
||||
private static final AtomicBoolean taskStarted = new AtomicBoolean();
|
||||
private static final AtomicBoolean sending = new AtomicBoolean();
|
||||
private static long rateLimitedUntil;
|
||||
@@ -120,7 +122,7 @@ public class DiscordManager implements StartableInitable, ReloadableInitable {
|
||||
}
|
||||
|
||||
public void sendAlert(GrimPlayer player, String verbose, String checkName, int violations) {
|
||||
if (url == null) {
|
||||
if (isDisabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -148,23 +150,36 @@ public class DiscordManager implements StartableInitable, ReloadableInitable {
|
||||
sendWebhookMessage(new WebhookMessage().addEmbeds(embed));
|
||||
}
|
||||
|
||||
public void sendWebhookMessage(WebhookMessage message) {
|
||||
requests.add(HttpRequest.newBuilder()
|
||||
public CompletableFuture<Boolean> sendWebhookMessage(WebhookMessage message) {
|
||||
if (isDisabled()) return CompletableFuture.completedFuture(false);
|
||||
|
||||
HttpRequest request = HttpRequest.newBuilder()
|
||||
.uri(url)
|
||||
.header("Content-Type", "application/json")
|
||||
.POST(HttpRequest.BodyPublishers.ofString(message.toJson().toString()))
|
||||
.timeout(timeout)
|
||||
.build());
|
||||
.build();
|
||||
|
||||
CompletableFuture<Boolean> future = new CompletableFuture<>();
|
||||
|
||||
requests.add(new Pair<>(request, future));
|
||||
|
||||
if (!taskStarted.getAndSet(true)) {
|
||||
// there's probably a better way to handle rate limits, but this works, so whatever.
|
||||
GrimAPI.INSTANCE.getScheduler().getAsyncScheduler().runAtFixedRate(GrimAPI.INSTANCE.getGrimPlugin(), DiscordManager::tick, 0, 1);
|
||||
}
|
||||
|
||||
return future;
|
||||
}
|
||||
|
||||
public boolean isDisabled() {
|
||||
return url == null;
|
||||
}
|
||||
|
||||
private static void tick() {
|
||||
HttpRequest request = requests.peek();
|
||||
if (request != null && rateLimitedUntil < System.currentTimeMillis() && !sending.getAndSet(true)) {
|
||||
Pair<HttpRequest, CompletableFuture<Boolean>> pair = requests.peek();
|
||||
if (pair != null && rateLimitedUntil < System.currentTimeMillis() && !sending.getAndSet(true)) {
|
||||
HttpRequest request = pair.first();
|
||||
client.sendAsync(request, HttpResponse.BodyHandlers.ofString()).whenComplete((response, throwable) -> {
|
||||
if (throwable != null) {
|
||||
sending.set(false);
|
||||
@@ -178,12 +193,15 @@ public class DiscordManager implements StartableInitable, ReloadableInitable {
|
||||
return;
|
||||
}
|
||||
|
||||
requests.remove(request);
|
||||
requests.remove(pair);
|
||||
sending.set(false);
|
||||
|
||||
// TODO: handle 503 (Service Unavailable)?
|
||||
if (response != null && response.statusCode() >= 400) {
|
||||
LogUtil.error("Encountered status code " + response.statusCode() + " with body " + response.body() + " and headers " + response.headers().map() + " while sending a Discord webhook alert.");
|
||||
pair.second().complete(false);
|
||||
} else {
|
||||
pair.second().complete(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -7,15 +7,12 @@ import ac.grim.grimac.manager.init.load.PacketEventsInit;
|
||||
import ac.grim.grimac.manager.init.start.*;
|
||||
import ac.grim.grimac.manager.init.stop.StoppableInitable;
|
||||
import ac.grim.grimac.manager.init.stop.TerminatePacketEvents;
|
||||
import ac.grim.grimac.platform.api.sender.Sender;
|
||||
import ac.grim.grimac.utils.anticheat.LogUtil;
|
||||
import com.github.retrooper.packetevents.PacketEventsAPI;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import lombok.Getter;
|
||||
import org.incendo.cloud.CommandManager;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class InitManager {
|
||||
|
||||
@@ -30,7 +27,7 @@ public class InitManager {
|
||||
@Getter
|
||||
private boolean stopped = false;
|
||||
|
||||
public InitManager(PacketEventsAPI<?> packetEventsAPI, Supplier<CommandManager<Sender>> commandManager, Initable... platformSpecificInitables) {
|
||||
public InitManager(PacketEventsAPI<?> packetEventsAPI, Initable... platformSpecificInitables) {
|
||||
ArrayList<LoadableInitable> extraLoadableInitables = new ArrayList<>();
|
||||
ArrayList<StartableInitable> extraStartableInitables = new ArrayList<>();
|
||||
ArrayList<StoppableInitable> extraStoppableInitables = new ArrayList<>();
|
||||
@@ -51,7 +48,8 @@ public class InitManager {
|
||||
.add(new PacketManager())
|
||||
.add(new ViaBackwardsManager())
|
||||
.add(new TickRunner())
|
||||
.add(new CommandRegister(commandManager))
|
||||
.add(new CommandRegister(GrimAPI.INSTANCE.getCommandService()))
|
||||
.add(new UpdateChecker())
|
||||
.add(new PacketLimiter())
|
||||
.add(GrimAPI.INSTANCE.getAlertManager())
|
||||
.add(GrimAPI.INSTANCE.getDiscordManager())
|
||||
|
||||
@@ -40,6 +40,7 @@ import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerSe
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
@@ -100,7 +101,7 @@ public class SetbackTeleportUtil extends Check implements PostPredictionCheck {
|
||||
blockMovementsUntilResync(true, true);
|
||||
}
|
||||
|
||||
public void executeForceResyncNoSimulation() {
|
||||
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
|
||||
@@ -144,9 +145,11 @@ public class SetbackTeleportUtil extends Check implements PostPredictionCheck {
|
||||
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))));
|
||||
}
|
||||
@@ -170,12 +173,14 @@ public class SetbackTeleportUtil extends Check implements PostPredictionCheck {
|
||||
VelocityData futureExplosion = player.checkManager.getExplosionHandler().getFutureExplosion();
|
||||
|
||||
// Velocity sets
|
||||
if (futureKb.first() != null) {
|
||||
// Don't let player reuse setback velocity
|
||||
if (futureKb.first() != null && !futureKb.first().isSetback) {
|
||||
clientVel = futureKb.second();
|
||||
}
|
||||
|
||||
// Explosion adds
|
||||
if (futureExplosion != null && (futureKb.first() == null || futureKb.first().transaction < futureExplosion.transaction)) {
|
||||
if (futureExplosion != null && (futureKb.first() == null
|
||||
|| (futureKb.first().transaction < futureExplosion.transaction && !futureKb.first().isSetback))) {
|
||||
clientVel.add(futureExplosion.vector);
|
||||
}
|
||||
|
||||
@@ -215,7 +220,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, null, RelativeFlag.YAW.or(RelativeFlag.PITCH), player.lastTransactionSent.get(), 0), player.yaw, player.pitch, clientVel, player.inVehicle(), false);
|
||||
sendSetback(data);
|
||||
}
|
||||
|
||||
@@ -267,7 +272,8 @@ public class SetbackTeleportUtil extends Check implements PostPredictionCheck {
|
||||
data.getTeleportData().setTransaction(player.lastTransactionSent.get());
|
||||
|
||||
// Use provided transaction ID to make sure it can never desync, although there's no reason to do this
|
||||
addSentTeleport(new Location(null, position.getX(), y, position.getZ(), player.yaw % 360, player.pitch % 360), new Vector3d(), data.getTeleportData().getTransaction(), new RelativeFlag(0b11000), false, teleportId);
|
||||
addSentTeleport(new Location(null, position.getX(), y, position.getZ(), player.yaw % 360, player.pitch % 360),
|
||||
null, data.getTeleportData().getTransaction(), RelativeFlag.YAW.or(RelativeFlag.PITCH), false, teleportId);
|
||||
// This must be done after setting the sent teleport, otherwise we lose velocity data
|
||||
requiredSetBack = data;
|
||||
// Send after tracking to fix race condition
|
||||
@@ -382,7 +388,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
|
||||
@@ -403,7 +409,12 @@ public class SetbackTeleportUtil extends Check implements PostPredictionCheck {
|
||||
!player.getSetbackTeleportUtil().hasAcceptedSpawnTeleport);
|
||||
}
|
||||
|
||||
public void addSentTeleport(Location position, Vector3d velocity, int transaction, RelativeFlag flags, boolean plugin, int teleportId) {
|
||||
public void addSentTeleport(Location position, @Nullable Vector3d velocity, int transaction, RelativeFlag flags, boolean plugin, int teleportId) {
|
||||
// Clients below 1.21.2 do not have this.
|
||||
if (player.getClientVersion().isOlderThan(ClientVersion.V_1_21_2)) {
|
||||
velocity = null;
|
||||
}
|
||||
|
||||
TeleportData data = new TeleportData(new Vector3d(position.getX(), position.getY(), position.getZ()), velocity, flags, transaction, teleportId);
|
||||
pendingTeleports.add(data);
|
||||
|
||||
@@ -422,7 +433,7 @@ public class SetbackTeleportUtil extends Check implements PostPredictionCheck {
|
||||
safePosition = safePosition.withZ(safePosition.getZ() + lastKnownGoodPosition.pos.getZ());
|
||||
}
|
||||
|
||||
data = new TeleportData(safePosition, velocity, new RelativeFlag(0b11000), transaction, teleportId);
|
||||
data = new TeleportData(safePosition, velocity, RelativeFlag.YAW.or(RelativeFlag.PITCH), transaction, teleportId);
|
||||
requiredSetBack = new SetBackData(data, player.yaw, player.pitch, null, false, plugin);
|
||||
|
||||
this.lastKnownGoodPosition = new SetbackPosWithVector(safePosition, new Vector3dm());
|
||||
|
||||
@@ -23,6 +23,14 @@ public class BaseConfigManager {
|
||||
@Getter
|
||||
private String prefix = "&bGrim &8»";
|
||||
@Getter
|
||||
private String webhookNotEnabled;
|
||||
@Getter
|
||||
private String webhookTestMessage;
|
||||
@Getter
|
||||
private String webhookTestSucceeded;
|
||||
@Getter
|
||||
private String webhookTestFailed;
|
||||
@Getter
|
||||
private String disconnectTimeout;
|
||||
@Getter
|
||||
private String disconnectClosed;
|
||||
@@ -57,6 +65,10 @@ public class BaseConfigManager {
|
||||
printAlertsToConsole = config.getBooleanElse("alerts.print-to-console", true);
|
||||
prefix = config.getStringElse("prefix", "&bGrim &8»");
|
||||
|
||||
webhookNotEnabled = config.getStringElse("webhook-not-enabled", "Discord webhooks are not enabled!");
|
||||
webhookTestMessage = config.getStringElse("webhook-test-message", "test message");
|
||||
webhookTestSucceeded = config.getStringElse("webhook-test-succeeded", "Discord webhook test succeeded!");
|
||||
webhookTestFailed = config.getStringElse("webhook-test-failed", "Discord webhook test failed!");
|
||||
disconnectTimeout = config.getStringElse("disconnect.timeout", "<lang:disconnect.timeout>");
|
||||
disconnectClosed = config.getStringElse("disconnect.closed", "<lang:disconnect.timeout>");
|
||||
disconnectPacketError = config.getStringElse("disconnect.error", "<red>An error occurred whilst processing packets. Please contact the administrators.");
|
||||
|
||||
@@ -1,82 +1,19 @@
|
||||
package ac.grim.grimac.manager.init.start;
|
||||
|
||||
import ac.grim.grimac.GrimAPI;
|
||||
import ac.grim.grimac.command.SenderRequirement;
|
||||
import ac.grim.grimac.command.commands.*;
|
||||
import ac.grim.grimac.command.handler.GrimCommandFailureHandler;
|
||||
import ac.grim.grimac.platform.api.sender.Sender;
|
||||
import ac.grim.grimac.utils.anticheat.MessageUtil;
|
||||
import io.leangen.geantyref.TypeToken;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import net.kyori.adventure.text.ComponentLike;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import org.incendo.cloud.CommandManager;
|
||||
import org.incendo.cloud.exception.InvalidSyntaxException;
|
||||
import org.incendo.cloud.key.CloudKey;
|
||||
import org.incendo.cloud.processors.requirements.RequirementApplicable;
|
||||
import org.incendo.cloud.processors.requirements.RequirementApplicable.RequirementApplicableFactory;
|
||||
import org.incendo.cloud.processors.requirements.RequirementPostprocessor;
|
||||
import org.incendo.cloud.processors.requirements.Requirements;
|
||||
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class CommandRegister implements StartableInitable {
|
||||
|
||||
public static final CloudKey<Requirements<Sender, SenderRequirement>> REQUIREMENT_KEY
|
||||
= CloudKey.of("requirements", new TypeToken<>() {});
|
||||
|
||||
public static final RequirementApplicableFactory<Sender, SenderRequirement> REQUIREMENT_FACTORY
|
||||
= RequirementApplicable.factory(REQUIREMENT_KEY);
|
||||
|
||||
private static boolean commandsRegistered = false;
|
||||
private final Supplier<CommandManager<Sender>> commandManagerSupplier;
|
||||
|
||||
// Public static method that can be called on platforms where command must be registered earlier than InitManager.load()
|
||||
public static void registerCommands(CommandManager<Sender> commandManager) {
|
||||
if (commandsRegistered) return;
|
||||
new GrimPerf().register(commandManager);
|
||||
new GrimDebug().register(commandManager);
|
||||
new GrimAlerts().register(commandManager);
|
||||
new GrimProfile().register(commandManager);
|
||||
new GrimSendAlert().register(commandManager);
|
||||
new GrimHelp().register(commandManager);
|
||||
new GrimHistory().register(commandManager);
|
||||
new GrimReload().register(commandManager);
|
||||
new GrimSpectate().register(commandManager);
|
||||
new GrimStopSpectating().register(commandManager);
|
||||
new GrimLog().register(commandManager);
|
||||
new GrimVerbose().register(commandManager);
|
||||
new GrimVersion().register(commandManager);
|
||||
new GrimDump().register(commandManager);
|
||||
new GrimBrands().register(commandManager);
|
||||
new GrimList().register(commandManager);
|
||||
|
||||
final RequirementPostprocessor<Sender, SenderRequirement>
|
||||
senderRequirementPostprocessor = RequirementPostprocessor.of(
|
||||
REQUIREMENT_KEY,
|
||||
new GrimCommandFailureHandler()
|
||||
);
|
||||
commandManager.registerCommandPostProcessor(senderRequirementPostprocessor);
|
||||
registerExceptionHandler(commandManager, InvalidSyntaxException.class, e -> MessageUtil.miniMessage(e.correctSyntax()));
|
||||
commandsRegistered = true;
|
||||
}
|
||||
|
||||
protected static <E extends Exception> void registerExceptionHandler(CommandManager<Sender> commandManager, Class<E> ex, Function<E, ComponentLike> toComponent) {
|
||||
commandManager.exceptionController().registerHandler(ex,
|
||||
(c) -> c.context().sender().sendMessage(toComponent.apply(c.exception()).asComponent().colorIfAbsent(NamedTextColor.RED))
|
||||
);
|
||||
}
|
||||
import ac.grim.grimac.platform.api.command.CommandService;
|
||||
import ac.grim.grimac.utils.anticheat.LogUtil;
|
||||
|
||||
public record CommandRegister(CommandService service) implements StartableInitable {
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
CommandManager<Sender> commandManager = commandManagerSupplier.get();
|
||||
registerCommands(commandManager);
|
||||
|
||||
if (GrimAPI.INSTANCE.getConfigManager().getConfig().getBooleanElse("check-for-updates", true)) {
|
||||
GrimVersion.checkForUpdatesAsync(GrimAPI.INSTANCE.getPlatformServer().getConsoleSender());
|
||||
try {
|
||||
if (service != null) {
|
||||
service.registerCommands();
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
// This is the ultimate safety net. If command registration fails, Grim keeps running.
|
||||
LogUtil.error("Failed to register commands! Grim will run without command support.", t);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,7 @@ public class PacketManager implements StartableInitable {
|
||||
PacketEvents.getAPI().getEventManager().registerListener(new PacketServerTeleport());
|
||||
PacketEvents.getAPI().getEventManager().registerListener(new PacketPlayerCooldown());
|
||||
PacketEvents.getAPI().getEventManager().registerListener(new PacketPlayerRespawn());
|
||||
PacketEvents.getAPI().getEventManager().registerListener(new PacketPlayerTick());
|
||||
PacketEvents.getAPI().getEventManager().registerListener(new CheckManagerListener());
|
||||
PacketEvents.getAPI().getEventManager().registerListener(new PacketPlayerSteer());
|
||||
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package ac.grim.grimac.manager.init.start;
|
||||
|
||||
import ac.grim.grimac.GrimAPI;
|
||||
import ac.grim.grimac.command.commands.GrimVersion;
|
||||
|
||||
public class UpdateChecker implements StartableInitable {
|
||||
@Override
|
||||
public void start() {
|
||||
if (GrimAPI.INSTANCE.getConfigManager().getConfig().getBooleanElse("check-for-updates", true)) {
|
||||
GrimVersion.checkForUpdatesAsync(GrimAPI.INSTANCE.getPlatformServer().getConsoleSender());
|
||||
}
|
||||
}
|
||||
}
|
||||
+7
-4
@@ -121,10 +121,13 @@ public class DefaultResyncHandler implements ResyncHandler {
|
||||
|
||||
final int blockId = world.getChunkAt(chunkX, chunkZ).getBlockID(x & 15, y, z & 15);
|
||||
|
||||
player.user.sendPacket(new WrapperPlayServerBlockChange(new Vector3i(x, y, z), blockId));
|
||||
if (PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_19)) { // Via will handle this for us pre-1.19
|
||||
player.user.sendPacket(new WrapperPlayServerAcknowledgeBlockChanges(sequence)); // Make 1.19 clients apply the changes
|
||||
}
|
||||
player.runSafely(() -> {
|
||||
player.user.sendPacket(new WrapperPlayServerBlockChange(new Vector3i(x, y, z), blockId));
|
||||
if (PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_19)) { // Via will handle this for us pre-1.19
|
||||
player.user.sendPacket(new WrapperPlayServerAcknowledgeBlockChanges(sequence)); // Make 1.19 clients apply the changes
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package ac.grim.grimac.manager.player.handlers;
|
||||
|
||||
import ac.grim.grimac.api.handler.ResyncHandler;
|
||||
|
||||
public class NoOpResyncHandler implements ResyncHandler {
|
||||
|
||||
public static final NoOpResyncHandler INSTANCE = new NoOpResyncHandler();
|
||||
|
||||
@Override
|
||||
public void resync(int minBlockX, int minBlockY, int minBlockZ, int maxBlockX, int maxBlockY, int maxBlockZ) {}
|
||||
|
||||
@Override public void resyncPosition(int x, int y, int z, int sequence) {}
|
||||
}
|
||||
+29
-5
@@ -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;
|
||||
@@ -57,7 +58,7 @@ public class ViolationDatabaseManager implements StartableInitable, ReloadableIn
|
||||
} catch (ClassNotFoundException e) {
|
||||
LogUtil.error(
|
||||
"""
|
||||
Could not load SQLite driver for /grim history database.
|
||||
IMPORTANT: Could not load SQLite driver for /grim history database.
|
||||
Download the minecraft-sqlite-jdbc mod/plugin for SQLite support, or change history.database.type
|
||||
Alternatively set history.enabled=false to remove this message if /grim history support is not desired"""
|
||||
);
|
||||
@@ -72,10 +73,11 @@ public class ViolationDatabaseManager implements StartableInitable, ReloadableIn
|
||||
}
|
||||
|
||||
case "MYSQL" -> {
|
||||
String host = cfg.getStringElse("history.database.host", "localhost:3306");
|
||||
String db = cfg.getStringElse("history.database.database", "grimac");
|
||||
int port = cfg.getIntElse("history.database.port", 3306);
|
||||
String host = cfg.getStringElse("history.database.host", "localhost") + ":" + port;
|
||||
String db = cfg.getStringElse("history.database.database", "grimac");
|
||||
String user = cfg.getStringElse("history.database.username", "root");
|
||||
String pwd = cfg.getStringElse("history.database.password", "password");
|
||||
String pwd = cfg.getStringElse("history.database.password", "password");
|
||||
|
||||
if (database instanceof MySQLViolationDatabase mysql
|
||||
&& mysql.sameConfig(host, db, user, pwd)) {
|
||||
@@ -93,6 +95,29 @@ public class ViolationDatabaseManager implements StartableInitable, ReloadableIn
|
||||
}
|
||||
}
|
||||
|
||||
case "POSTGRESQL" -> {
|
||||
int port = cfg.getIntElse("history.database.port", 3306);
|
||||
String host = cfg.getStringElse("history.database.host", "localhost") + ":" + port;
|
||||
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();
|
||||
@@ -115,5 +140,4 @@ public class ViolationDatabaseManager implements StartableInitable, ReloadableIn
|
||||
public List<Violation> getViolations(UUID player, int page, int limit) {
|
||||
return database.getViolations(player, page, limit);
|
||||
}
|
||||
|
||||
}
|
||||
+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());
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,15 @@
|
||||
package ac.grim.grimac.platform.api;
|
||||
|
||||
import ac.grim.grimac.api.plugin.GrimPlugin;
|
||||
import ac.grim.grimac.platform.api.command.CommandService;
|
||||
import ac.grim.grimac.platform.api.manager.ItemResetHandler;
|
||||
import ac.grim.grimac.platform.api.manager.MessagePlaceHolderManager;
|
||||
import ac.grim.grimac.platform.api.manager.CommandAdapter;
|
||||
import ac.grim.grimac.platform.api.manager.PermissionRegistrationManager;
|
||||
import ac.grim.grimac.platform.api.manager.PlatformPluginManager;
|
||||
import ac.grim.grimac.platform.api.player.PlatformPlayerFactory;
|
||||
import ac.grim.grimac.platform.api.scheduler.PlatformScheduler;
|
||||
import ac.grim.grimac.platform.api.sender.Sender;
|
||||
import ac.grim.grimac.platform.api.sender.SenderFactory;
|
||||
import com.github.retrooper.packetevents.PacketEventsAPI;
|
||||
import org.incendo.cloud.CommandManager;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public interface PlatformLoader {
|
||||
@@ -19,14 +17,12 @@ public interface PlatformLoader {
|
||||
|
||||
PlatformPlayerFactory getPlatformPlayerFactory();
|
||||
|
||||
CommandAdapter getCommandAdapter();
|
||||
|
||||
PacketEventsAPI<?> getPacketEvents();
|
||||
|
||||
CommandManager<Sender> getCommandManager();
|
||||
|
||||
ItemResetHandler getItemResetHandler();
|
||||
|
||||
CommandService getCommandService();
|
||||
|
||||
SenderFactory<?> getSenderFactory();
|
||||
|
||||
GrimPlugin getPlugin();
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
package ac.grim.grimac.platform.api.command;
|
||||
|
||||
public interface CommandService {
|
||||
void registerCommands();
|
||||
}
|
||||
@@ -1,30 +1,8 @@
|
||||
package ac.grim.grimac.platform.api.manager;
|
||||
|
||||
import ac.grim.grimac.platform.api.command.PlayerSelector;
|
||||
import ac.grim.grimac.platform.api.sender.Sender;
|
||||
import org.incendo.cloud.parser.ParserDescriptor;
|
||||
import org.incendo.cloud.suggestion.SuggestionProvider;
|
||||
|
||||
/**
|
||||
* Acts as a bridge between platform-agnostic command definitions
|
||||
* and platform-specific parsers, suggestion providers, etc.
|
||||
*/
|
||||
|
||||
public interface CommandAdapter {
|
||||
|
||||
/**
|
||||
* Provides a parser descriptor for a single player selector (@p, player name).
|
||||
*
|
||||
* @return The parser descriptor.
|
||||
*/
|
||||
ParserDescriptor<Sender, PlayerSelector> singlePlayerSelectorParser();
|
||||
|
||||
/**
|
||||
* Provides a suggestion provider that lists all online players.
|
||||
* <p>
|
||||
* Platform-specific implementations should handle things like
|
||||
* vanished players.
|
||||
*
|
||||
* @return The suggestion provider.
|
||||
*/
|
||||
SuggestionProvider<Sender> onlinePlayerSuggestions();
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package ac.grim.grimac.platform.api.manager.cloud;
|
||||
|
||||
import ac.grim.grimac.platform.api.command.PlayerSelector;
|
||||
import ac.grim.grimac.platform.api.manager.CommandAdapter;
|
||||
import ac.grim.grimac.platform.api.sender.Sender;
|
||||
import org.incendo.cloud.parser.ParserDescriptor;
|
||||
import org.incendo.cloud.suggestion.SuggestionProvider;
|
||||
|
||||
/**
|
||||
* Acts as a bridge between platform-agnostic command definitions
|
||||
* and platform-specific parsers, suggestion providers, etc.
|
||||
*/
|
||||
public interface CloudCommandAdapter extends CommandAdapter {
|
||||
/**
|
||||
* Provides a parser descriptor for a single player selector (@p, player name).
|
||||
*
|
||||
* @return The parser descriptor.
|
||||
*/
|
||||
ParserDescriptor<Sender, PlayerSelector> singlePlayerSelectorParser();
|
||||
|
||||
/**
|
||||
* Provides a suggestion provider that lists all online players.
|
||||
* <p>
|
||||
* Platform-specific implementations should handle things like
|
||||
* vanished players.
|
||||
*
|
||||
* @return The suggestion provider.
|
||||
*/
|
||||
SuggestionProvider<Sender> onlinePlayerSuggestions();
|
||||
}
|
||||
@@ -8,6 +8,8 @@ public interface PlatformPlayerFactory {
|
||||
|
||||
OfflinePlatformPlayer getOfflineFromName(String name);
|
||||
|
||||
Collection<OfflinePlatformPlayer> getOfflinePlayers();
|
||||
|
||||
PlatformPlayer getFromName(String name);
|
||||
|
||||
PlatformPlayer getFromUUID(UUID uuid);
|
||||
|
||||
@@ -3,6 +3,7 @@ package ac.grim.grimac.player;
|
||||
import ac.grim.grimac.GrimAPI;
|
||||
import ac.grim.grimac.api.AbstractCheck;
|
||||
import ac.grim.grimac.api.GrimUser;
|
||||
import ac.grim.grimac.api.PacketWorld;
|
||||
import ac.grim.grimac.api.config.ConfigManager;
|
||||
import ac.grim.grimac.api.handler.ResyncHandler;
|
||||
import ac.grim.grimac.checks.Check;
|
||||
@@ -14,6 +15,7 @@ import ac.grim.grimac.events.packets.CheckManagerListener;
|
||||
import ac.grim.grimac.manager.*;
|
||||
import ac.grim.grimac.manager.player.features.FeatureManagerImpl;
|
||||
import ac.grim.grimac.manager.player.handlers.DefaultResyncHandler;
|
||||
import ac.grim.grimac.manager.player.handlers.NoOpResyncHandler;
|
||||
import ac.grim.grimac.platform.api.player.PlatformPlayer;
|
||||
import ac.grim.grimac.predictionengine.MovementCheckRunner;
|
||||
import ac.grim.grimac.predictionengine.PointThreeEstimator;
|
||||
@@ -74,6 +76,8 @@ import io.github.retrooper.packetevents.adventure.serializer.legacy.LegacyCompon
|
||||
import io.netty.channel.Channel;
|
||||
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.longs.LongSet;
|
||||
import it.unimi.dsi.fastutil.objects.Object2DoubleArrayMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2DoubleMap;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
@@ -88,6 +92,7 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
// Everything in this class should be sync'd to the anticheat thread.
|
||||
@@ -210,6 +215,7 @@ public class GrimPlayer implements GrimUser {
|
||||
public final CompensatedFireworks fireworks;
|
||||
public final CompensatedWorld compensatedWorld;
|
||||
public final CompensatedEntities compensatedEntities;
|
||||
public final CompensatedDashableEntities dashableEntities;
|
||||
public final CompensatedInventory inventory;
|
||||
public final ILatencyUtils latencyUtils = new LatencyUtils(this);
|
||||
public final PointThreeEstimator pointThreeEstimator;
|
||||
@@ -235,6 +241,7 @@ public class GrimPlayer implements GrimUser {
|
||||
public long lastBlockBreak = 0;
|
||||
public final AtomicInteger cancelledPackets = new AtomicInteger(0);
|
||||
public MainSupportingBlockData mainSupportingBlockData = new MainSupportingBlockData(null, false);
|
||||
public final Object2DoubleMap<FluidTag> fluidHeight = new Object2DoubleArrayMap<>(2);
|
||||
// possibleEyeHeights[0] = Standing eye heights, [1] = Sneaking. [2] = Elytra, Swimming, and Riptide Trident which only exists in 1.9+
|
||||
public final double[][] possibleEyeHeights = new double[3][];
|
||||
public int totalFlyingPacketsSent;
|
||||
@@ -245,7 +252,8 @@ public class GrimPlayer implements GrimUser {
|
||||
public final Queue<BlockBreak> queuedBreaks = new LinkedBlockingQueue<>();
|
||||
public final PlayerBlockHistory blockHistory = new PlayerBlockHistory();
|
||||
public final ArrayDeque<RotationData> pendingRotations = new ArrayDeque<>();
|
||||
@Getter @Setter private ResyncHandler resyncHandler = new DefaultResyncHandler(this);
|
||||
public final CompensatedCameraEntity cameraEntity;
|
||||
@Getter @Setter private ResyncHandler resyncHandler = GrimAPI.INSTANCE.getConfigManager().getConfig().getBooleanElse("disable-default-resync-handler", false) ? NoOpResyncHandler.INSTANCE : new DefaultResyncHandler(this);
|
||||
@Getter private final FeatureManagerImpl featureManager = new FeatureManagerImpl(this);
|
||||
public boolean serverOpenedInventoryThisTick;
|
||||
// start config
|
||||
@@ -282,6 +290,11 @@ public class GrimPlayer implements GrimUser {
|
||||
fireworks = new CompensatedFireworks(this); // Must be before checkmanager
|
||||
inventory = new CompensatedInventory(this);
|
||||
|
||||
compensatedWorld = new CompensatedWorld(this);
|
||||
compensatedEntities = new CompensatedEntities(this);
|
||||
dashableEntities = new CompensatedDashableEntities();
|
||||
cameraEntity = new CompensatedCameraEntity(this);
|
||||
|
||||
lastInstanceManager = new LastInstanceManager(this);
|
||||
actionManager = new ActionManager(this);
|
||||
checkManager = new CheckManager(this);
|
||||
@@ -289,8 +302,6 @@ public class GrimPlayer implements GrimUser {
|
||||
this.tagManager = new SyncedTags(this); // must be after this.user = user
|
||||
movementCheckRunner = new MovementCheckRunner(this);
|
||||
|
||||
compensatedWorld = new CompensatedWorld(this);
|
||||
compensatedEntities = new CompensatedEntities(this);
|
||||
uncertaintyHandler = new UncertaintyHandler(this); // must be after checkmanager
|
||||
pointThreeEstimator = new PointThreeEstimator(this);
|
||||
|
||||
@@ -504,11 +515,17 @@ public class GrimPlayer implements GrimUser {
|
||||
: isSneaking ? 1.54f : 1.62f;
|
||||
}
|
||||
|
||||
private final AtomicBoolean hasDisconnected = new AtomicBoolean(false);
|
||||
|
||||
public void timedOut() {
|
||||
disconnect(MessageUtil.miniMessage(MessageUtil.replacePlaceholders(this, GrimAPI.INSTANCE.getConfigManager().getDisconnectTimeout())));
|
||||
}
|
||||
|
||||
public void disconnect(Component reason) {
|
||||
if (!hasDisconnected.compareAndSet(false, true)) {
|
||||
return;
|
||||
}
|
||||
|
||||
String textReason;
|
||||
if (reason instanceof TranslatableComponent translatableComponent) {
|
||||
textReason = translatableComponent.key();
|
||||
@@ -690,6 +707,11 @@ public class GrimPlayer implements GrimUser {
|
||||
return possibleLookDirs.toArray(new Vector3dm[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PacketWorld getPacketWorld() {
|
||||
return compensatedWorld;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTransactionPing() {
|
||||
return GrimMath.floor(transactionPing / 1e6);
|
||||
@@ -739,8 +761,9 @@ public class GrimPlayer implements GrimUser {
|
||||
EntityTypes.isTypeInstanceOf(data.getEntityType(), EntityTypes.ABSTRACT_HORSE) ||
|
||||
data.getEntityType() == EntityTypes.PIG ||
|
||||
data.getEntityType() == EntityTypes.STRIDER ||
|
||||
data.getEntityType() == EntityTypes.CAMEL ||
|
||||
data.getEntityType() == EntityTypes.HAPPY_GHAST) {
|
||||
EntityTypes.isTypeInstanceOf(data.getEntityType(), EntityTypes.CAMEL) ||
|
||||
data.getEntityType() == EntityTypes.HAPPY_GHAST ||
|
||||
EntityTypes.isTypeInstanceOf(data.getEntityType(), EntityTypes.ABSTRACT_NAUTILUS)) {
|
||||
// We need to set its velocity otherwise it will jump a bit on us, flagging the anticheat
|
||||
// The server does override this with some vehicles. This is intentional.
|
||||
user.writePacket(new WrapperPlayServerEntityVelocity(vehicleID, new Vector3d()));
|
||||
@@ -787,23 +810,25 @@ public class GrimPlayer implements GrimUser {
|
||||
}
|
||||
|
||||
public boolean canGlide() {
|
||||
// Servers older than 1.21.2 don't have this component
|
||||
// don't check the client/server version, this is relevant for all
|
||||
final ItemStack chestPlate = inventory.getChestplate();
|
||||
if (chestPlate.getType() == ItemTypes.ELYTRA && chestPlate.getDamageValue() < chestPlate.getMaxDamage() - 1)
|
||||
return true;
|
||||
|
||||
// if the server or client doesn't support glider components return false
|
||||
if (getClientVersion().isOlderThan(ClientVersion.V_1_21_2)
|
||||
|| PacketEvents.getAPI().getServerManager().getVersion().isOlderThan(ServerVersion.V_1_21_2)) {
|
||||
final ItemStack chestPlate = inventory.getChestplate();
|
||||
return chestPlate.getType() == ItemTypes.ELYTRA && chestPlate.getDamageValue() < chestPlate.getMaxDamage() - 1;
|
||||
}
|
||||
|| PacketEvents.getAPI().getServerManager().getVersion().isOlderThan(ServerVersion.V_1_21_2)) return false;
|
||||
|
||||
// PacketEvents mappings are wrong
|
||||
// TODO https://github.com/retrooper/packetevents/pull/1125
|
||||
return isGlider(inventory.getHelmet(), EquipmentSlot.CHEST_PLATE)
|
||||
|| isGlider(inventory.getChestplate(), EquipmentSlot.LEGGINGS)
|
||||
|| isGlider(inventory.getLeggings(), EquipmentSlot.BOOTS)
|
||||
|| isGlider(inventory.getBoots(), EquipmentSlot.OFF_HAND);
|
||||
|| isGlider(inventory.getBoots(), EquipmentSlot.OFF_HAND)
|
||||
|| isGlider(inventory.getOffHand(), EquipmentSlot.HELMET);
|
||||
}
|
||||
|
||||
private static boolean isGlider(ItemStack stack, EquipmentSlot slot) {
|
||||
if (!stack.hasComponent(ComponentTypes.GLIDER) || stack.getDamageValue() >= (stack.getMaxDamage() - 1)) {
|
||||
if (!stack.hasComponent(ComponentTypes.GLIDER) || (stack.canBeDepleted() && stack.getDamageValue() >= (stack.getMaxDamage() - 1))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -924,6 +949,30 @@ public class GrimPlayer implements GrimUser {
|
||||
maxTransactionTime = GrimMath.clamp(config.getIntElse("max-transaction-time", 60), 1, 180);
|
||||
ignoreDuplicatePacketRotation = config.getBooleanElse("ignore-duplicate-packet-rotation", false);
|
||||
cancelDuplicatePacket = config.getBooleanElse("cancel-duplicate-packet", true);
|
||||
|
||||
boolean shouldDisableResync = config.getBooleanElse("disable-default-resync-handler", false);
|
||||
Class<?> currentHandlerClass = this.resyncHandler.getClass();
|
||||
|
||||
// Check if the current handler is EXACTLY one of our internal types.
|
||||
// If someone extended DefaultResyncHandler, .getClass() will not match,
|
||||
// so we will skip this block and preserve their custom handler.
|
||||
boolean isInternalHandler = currentHandlerClass == DefaultResyncHandler.class
|
||||
|| currentHandlerClass == NoOpResyncHandler.class;
|
||||
|
||||
if (isInternalHandler) {
|
||||
if (shouldDisableResync) {
|
||||
// Config says disable, but we aren't using NoOp yet? Switch to NoOp.
|
||||
if (currentHandlerClass != NoOpResyncHandler.class) {
|
||||
this.resyncHandler = NoOpResyncHandler.INSTANCE;
|
||||
}
|
||||
} else {
|
||||
// Config says enable, but we are using NoOp? Switch to Default.
|
||||
if (currentHandlerClass != DefaultResyncHandler.class) {
|
||||
this.resyncHandler = new DefaultResyncHandler(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resetItemUsageOnAttack = config.getBooleanElse("reset-item-usage-on-attack", true);
|
||||
resetItemUsageOnItemUpdate = config.getBooleanElse("reset-item-usage-on-item-update", true);
|
||||
resetItemUsageOnSlotChange = config.getBooleanElse("reset-item-usage-on-slot-change", true);
|
||||
|
||||
@@ -10,19 +10,23 @@ import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.predictionengine.movementtick.MovementTickerCamel;
|
||||
import ac.grim.grimac.predictionengine.movementtick.MovementTickerHappyGhast;
|
||||
import ac.grim.grimac.predictionengine.movementtick.MovementTickerHorse;
|
||||
import ac.grim.grimac.predictionengine.movementtick.MovementTickerNautilus;
|
||||
import ac.grim.grimac.predictionengine.movementtick.MovementTickerPig;
|
||||
import ac.grim.grimac.predictionengine.movementtick.MovementTickerPlayer;
|
||||
import ac.grim.grimac.predictionengine.movementtick.MovementTickerStrider;
|
||||
import ac.grim.grimac.predictionengine.predictions.PredictionEngineNormal;
|
||||
import ac.grim.grimac.predictionengine.predictions.rideable.PredictionEngineBoat;
|
||||
import ac.grim.grimac.predictionengine.predictions.rideable.PredictionEngineRideableUtils;
|
||||
import ac.grim.grimac.utils.anticheat.update.PositionUpdate;
|
||||
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
|
||||
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
|
||||
import ac.grim.grimac.utils.data.SetBackData;
|
||||
import ac.grim.grimac.utils.data.VectorData;
|
||||
import ac.grim.grimac.utils.data.packetentity.PacketEntity;
|
||||
import ac.grim.grimac.utils.data.packetentity.PacketEntityCamel;
|
||||
import ac.grim.grimac.utils.data.packetentity.PacketEntityHappyGhast;
|
||||
import ac.grim.grimac.utils.data.packetentity.PacketEntityHorse;
|
||||
import ac.grim.grimac.utils.data.packetentity.PacketEntityNautilus;
|
||||
import ac.grim.grimac.utils.data.packetentity.PacketEntityRideable;
|
||||
import ac.grim.grimac.utils.data.packetentity.PacketEntityTrackXRot;
|
||||
import ac.grim.grimac.utils.enums.Pose;
|
||||
@@ -71,7 +75,7 @@ public class MovementCheckRunner extends Check implements PositionCheck {
|
||||
if (!invalidVehicle && !data.isTeleport()) {
|
||||
// Teleport the player back to avoid players being able to simply ignore transactions
|
||||
// We shouldn't simulate movement in unloaded chunks
|
||||
player.getSetbackTeleportUtil().executeForceResyncNoSimulation();
|
||||
player.getSetbackTeleportUtil().executeNonSimulatingForceResync();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,7 +111,15 @@ public class MovementCheckRunner extends Check implements PositionCheck {
|
||||
player.lastWasClimbing = 0; // Vertical movement reset
|
||||
player.canSwimHop = false; // Vertical movement reset
|
||||
} else {
|
||||
update.getTeleportData().modifyVector(player, player.clientVelocity);
|
||||
final SetBackData setback = update.getSetback();
|
||||
if (setback == null || setback.getVelocity() == null) {
|
||||
update.getTeleportData().modifyVector(player, player.clientVelocity);
|
||||
} else {
|
||||
// Enforce setback velocity?
|
||||
player.clientVelocity.setX(setback.getVelocity().getX());
|
||||
player.clientVelocity.setY(setback.getVelocity().getY());
|
||||
player.clientVelocity.setZ(setback.getVelocity().getZ());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -499,6 +511,9 @@ public class MovementCheckRunner extends Check implements PositionCheck {
|
||||
PlayerBaseTick.doBaseTick(player);
|
||||
// Speed doesn't affect anything with boat movement
|
||||
new PredictionEngineBoat(player).guessBestMovement(0.1f, player);
|
||||
} else if (riding instanceof PacketEntityNautilus) {
|
||||
PlayerBaseTick.doBaseTick(player);
|
||||
new MovementTickerNautilus(player).livingEntityAIStep();
|
||||
} else if (riding instanceof PacketEntityCamel) {
|
||||
PlayerBaseTick.doBaseTick(player);
|
||||
new MovementTickerCamel(player).livingEntityAIStep();
|
||||
@@ -599,7 +614,10 @@ public class MovementCheckRunner extends Check implements PositionCheck {
|
||||
player.riptideSpinAttackTicks = 20;
|
||||
|
||||
player.uncertaintyHandler.lastMovementWasZeroPointZeroThree = !player.inVehicle() && player.skippedTickInActualMovement;
|
||||
player.uncertaintyHandler.lastMovementWasUnknown003VectorReset = !player.inVehicle() && player.couldSkipTick && player.predictedVelocity.isKnockback();
|
||||
player.uncertaintyHandler.lastMovementWasUnknown003VectorReset = !player.inVehicle() && player.couldSkipTick
|
||||
&& player.predictedVelocity.isKnockback()
|
||||
// Don't let setbacks count
|
||||
&& !player.predictedVelocity.isSetbackKb(player);
|
||||
player.couldSkipTick = false;
|
||||
|
||||
// Logic is if the player was directly 0.03 and the player could control vertical movement in 0.03
|
||||
@@ -613,14 +631,12 @@ public class MovementCheckRunner extends Check implements PositionCheck {
|
||||
player.uncertaintyHandler.lastPacketWasGroundPacket = player.uncertaintyHandler.onGroundUncertain;
|
||||
player.uncertaintyHandler.onGroundUncertain = false;
|
||||
|
||||
PredictionEngineRideableUtils.applyPendingJumps(player);
|
||||
|
||||
player.vehicleData.vehicleForward = (float) Math.min(0.98, Math.max(-0.98, player.vehicleData.nextVehicleForward));
|
||||
player.vehicleData.vehicleHorizontal = (float) Math.min(0.98, Math.max(-0.98, player.vehicleData.nextVehicleHorizontal));
|
||||
if (player.onGround) { // if vehicle is on ground, this gets set
|
||||
player.vehicleData.horseJump = player.vehicleData.nextHorseJump;
|
||||
player.vehicleData.nextHorseJump = 0;
|
||||
}
|
||||
|
||||
player.vehicleData.camelDashCooldown = Math.max(0, player.vehicleData.camelDashCooldown - 1);
|
||||
player.dashableEntities.tick();
|
||||
|
||||
player.minAttackSlow = 0;
|
||||
player.maxAttackSlow = 0;
|
||||
|
||||
Loaded 100 of 182 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user