Compare commits

..
Author SHA1 Message Date
Axionize 6b863655f1 Merge remote-tracking branch 'origin/2.0' into flatten-complexcollisionboxes
# Conflicts:
#	src/main/java/ac/grim/grimac/utils/collisions/HitboxData.java
2024-11-22 09:13:17 -05:00
Axionize 631be06ec2 Remove unused calls to makeShapes() 2024-11-22 09:11:04 -05:00
Axionize 1c4361f2c6 Make makeShapes() CCD extra maxIndex explicit; Fix DynamicCollisionWall error 2024-11-22 09:10:04 -05:00
Axionize 83e06bce77 Make CCD length private var 2024-11-21 18:32:33 -05:00
Axionize 42fd3e1cc7 Rename variable for clarity 2024-11-21 17:14:13 -05:00
Axionize 26a771e4da Fix copying CCBs in edge case 2024-11-21 17:12:38 -05:00
Axionize 1dc4361938 Remove debug print statement 2024-11-21 12:06:03 -05:00
Axionize 3d5491de4f Fix logic error again in CCB constructor 2024-11-21 12:04:04 -05:00
Axionize 75ff92c326 Optimize DynamicHitboxFence 2024-11-21 11:59:19 -05:00
Axionize c530dcfa17 Fix logic error in CCB constructor 2024-11-21 11:58:04 -05:00
Axionize d720b8864f Fix new downCast() for SimpleCollisionBox 2024-11-21 11:41:52 -05:00
Axionize 3e3dbf0f30 Manually size ComplexCollisionBoxes Pt. 2 2024-11-21 11:38:32 -05:00
Axionize 0bd154974b Manually size ComplexCollisionBoxes Pt. 1 2024-11-21 10:28:34 -05:00
Axionize 3f57026960 implement new downCast() API for Dynamic, Simple, and No Collision Boxes 2024-11-21 10:26:46 -05:00
Axionize be9841fb9f Port everything to new internal .downCast() API besides getCollisionBoxes 2024-11-20 12:37:52 -05:00
Axionize d8b529e837 Optimize ComplexCollisionBoxes by using primitives internally; Make new more efficient constructors available 2024-11-20 12:37:01 -05:00
Axionize 743a2d9cdb Ensure ComplexCollisionBoxes can only contain SimpleCollisionBoxes 2024-11-17 11:45:55 -05:00
714 changed files with 14730 additions and 28978 deletions

No files matched your search

+1 -1
View File
@@ -5,4 +5,4 @@ name = "java"
enabled = true
[analyzers.meta]
runtime_version = "17"
runtime_version = "16"
-17
View File
@@ -1,17 +0,0 @@
root = true
[*]
charset = utf-8
indent_size = 4
indent_style = space
insert_final_newline = true
max_line_length = 100
tab_width = 4
# intellij specific configuration
ij_continuation_indent_size = 8
ij_formatter_off_tag = @formatter:off
ij_formatter_on_tag = @formatter:on
ij_formatter_tags_enabled = false
ij_smart_tabs = false
ij_wrap_on_typing = false
+2 -2
View File
@@ -34,7 +34,7 @@ body:
attributes:
label: Plugins
description: |
List all plugins or mods running on your server. To list plugins on bukkit, run `/plugins`.
List all plugins running on your server. To list plugins, run `/plugins`.
If your server is behind a proxy such as Bungeecord, you should list the proxy's plugins as well.
validations:
required: true
required: true
+3 -15
View File
@@ -39,23 +39,11 @@ body:
validations:
required: true
- type: dropdown
id: type
attributes:
label: Affected Platform(s)
description: Which platforms does this issue affect? If you aren't sure, select both.
options:
- "Bukkit"
- "Fabric"
multiple: true
validations:
required: true
- type: textarea
attributes:
label: Plugins/mods
label: Plugins
description: |
List all plugins or mods running on your server. To list plugins on bukkit, run `/plugins`.
List all plugins running on your server. To list plugins, run `/plugins`.
If your server is behind a proxy such as Bungeecord, you should list the proxy's plugins as well.
validations:
required: true
required: true
+2 -2
View File
@@ -46,7 +46,7 @@ body:
attributes:
label: Plugins
description: |
List all plugins or mods running on your server. To list plugins on bukkit, run `/plugins`.
List all plugins running on your server. To list plugins, run `/plugins`.
If your server is behind a proxy such as Bungeecord, you should list the proxy's plugins as well.
validations:
required: true
required: true
+4 -16
View File
@@ -1,5 +1,5 @@
name: Plugin/mod incompatibility or other bug
description: Report a bug or plugin/mod incompatibility here
name: Plugin incompatibility or other bug
description: Report a bug or plugin incompatibility here
labels: ["bug"]
body:
- type: textarea
@@ -30,23 +30,11 @@ body:
validations:
required: true
- type: dropdown
id: type
attributes:
label: Affected Platform(s)
description: Which platforms does this issue affect? If you aren't sure, select both.
options:
- "Bukkit"
- "Fabric"
multiple: true
validations:
required: true
- type: textarea
attributes:
label: Plugins
description: |
List all plugins or mods running on your server. To list plugins on bukkit, run `/plugins`.
List all plugins running on your server. To list plugins, run `/plugins`.
If your server is behind a proxy such as Bungeecord, you should list the proxy's plugins as well.
validations:
required: true
required: true
@@ -45,7 +45,7 @@ body:
attributes:
label: Plugins
description: |
List all plugins or mods running on your server. To list plugins on bukkit, run `/plugins`.
List all plugins running on your server. To list plugins, run `/plugins`.
If your server is behind a proxy such as Bungeecord, you should list the proxy's plugins as well.
validations:
required: true
required: true
+10 -31
View File
@@ -7,10 +7,12 @@
name: Gradle Package
on: [push, workflow_dispatch]
on:
[push, workflow_dispatch]
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
@@ -18,42 +20,19 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Set up JDK 21
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: '21'
java-version: '17'
distribution: 'temurin'
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
settings-path: ${{ github.workspace }} # location for the settings.xml file
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4 # Handles Gradle wrapper validation and basic caching
- name: Cache Gradle Loom Cache
uses: actions/cache@v4
- name: Build with Gradle
uses: gradle/actions/setup-gradle@v3
with:
path: .gradle/loom-cache # Path to Loom cache relative to workspace root
key: loom-cache-${{ runner.os }}-${{ hashFiles('**/libs.versions.toml', 'fabric/**/build.gradle.kts') }}
restore-keys: |
loom-cache-${{ runner.os }}
- name: Build with Gradle (Actual Build for Artifacts)
run: ./gradlew build
- name: Upload Bukkit Artifact
arguments: build
- name: Upload a Build Artifact
uses: actions/upload-artifact@v4
with:
name: grimac-bukkit
# Adding if-no-files-found for robustness
path: ${{ github.workspace }}/bukkit/build/libs/grimac-bukkit-*.jar
if-no-files-found: error
- name: Upload Fabric Artifact
uses: actions/upload-artifact@v4
with:
name: grimac-fabric
# Adding if-no-files-found for robustness
path: ${{ github.workspace }}/fabric/build/libs/grimac-fabric-*.jar
if-no-files-found: error
path: /home/runner/work/Grim/Grim/build/libs/**.jar
+1 -8
View File
@@ -19,7 +19,6 @@ build/
.gradletasknamecache
gradle-app.setting
!gradle-wrapper.jar
buildSrc/.kotlin
##############################
## IntelliJ
@@ -66,10 +65,4 @@ nb-configuration.xml
##############################
## OS X
##############################
.DS_Store
##############################
## Fabric
##############################
run/
fabric/run/
.DS_Store
-6
View File
@@ -1,6 +0,0 @@
[submodule "GrimAPI"]
path = GrimAPI
url = https://github.com/GrimAnticheat/GrimAPI
[submodule "api"]
path = api
url = https://github.com/GrimAnticheat/GrimAPI
+12 -33
View File
@@ -1,48 +1,27 @@
# GrimAC
This project is considered feature complete for the 2.0 (open-source) branch of this project. If you would like a bugfix or enhancement and cannot sponsor the work, pull requests are welcome. You can join the [discord](https://discord.com/invite/kqQAhTmkUF) for jar releases & changelogs.
GrimAC is an open source Minecraft anticheat designed for 1.21 and supports 1.8-1.21. It is free while in beta. It will eventually become paid and/or will include offering additional subscription based paid checks. Geyser players are fully exempt.
This project is considered feature complete for the 2.0 (open-source) branch of this project. If you would like a bugfix or enhancement and cannot sponsor the work, pull requests are welcome.
### Compiling through terminal/command prompt
1. git clone https://github.com/GrimAnticheat/Grim.git (or click the green code button, download ZIP, then unzip it.)
2. cd Grim
3. ./gradlew build
4. The final jar is located in build/libs
## Downloads
- [Modrinth](https://modrinth.com/plugin/grimac)
- [Hangar](https://hangar.papermc.io/GrimAnticheat/GrimAnticheat)
- [SpigotMC](https://www.spigotmc.org/resources/grim-anticheat.99923/)
- *For bleeding edge builds use GitHub artifacts*: [Bukkit](https://nightly.link/GrimAnticheat/Grim/workflows/gradle-publish/2.0/grimac-bukkit.zip), [Fabric](https://nightly.link/GrimAnticheat/Grim/workflows/gradle-publish/2.0/grimac-fabric.zip)
## Installation notes
> [!WARNING]
> Java 17 is now required. More information [here](https://github.com/GrimAnticheat/Grim/wiki/Updating-to-Java-17).
- Paper, Spigot, Folia, and Fabric are currently supported.
- If you use Geyser, place Floodgate on the backend server so grim can exempt bedrock players. Grim cannot access the Floodgate API if it is on the proxy.
- If you use ViaVersion, it should be on the backend server as movement is highly dependent on client version.
## Support & wiki information
- Support & discussion: [Discord](https://discord.com/invite/kqQAhTmkUF)
- Report issues: [Issues](https://github.com/GrimAnticheat/Grim/issues/new/choose)
- Wiki & examples: [Wiki](https://github.com/GrimAnticheat/Grim/wiki)
## Developer API
Grim's API allows you to integrate Grim into your own plugins.
- API repository: [GrimAPI](https://github.com/GrimAnticheat/GrimAPI)
- Wiki info: [Wiki](https://github.com/GrimAnticheat/GrimAPI)
## How to compile
1. `git clone https://github.com/GrimAnticheat/Grim.git`
2. `cd Grim`
3. `./gradlew build`
4. The final jars will compile into the `<platform>/build/libs` folders
### API Information
Grim's API allows you to integrate Grim into your own plugins. For more information, check out the API's GitHub repository [here](https://github.com/GrimAnticheat/GrimAPI).
## Grim supremacy
What makes Grim stand out against other anticheats?
Here are the main cores that make Grim stand out against other anticheats
### Movement Simulation Engine
* We have a 1:1 replication of the player's possible movements
* This covers everything from basic walking, swimming, knockback, cobwebs, to bubble columns
* It even covers riding entities from boats to pigs to striders
* This covers everything from basic walking, swimming, knockback, cobwebs, to bubble columns
* It even covers riding entities from boats to pigs to striders
* Built upon covering edge cases to confirm accuracy
* 1.13+ clients on 1.13+ servers, 1.12- clients on 1.13+ servers, 1.13+ clients on 1.12- servers, and 1.12- clients on 1.12- servers are all supported regardless of the large technical changes between these versions.
* The order of collisions depends on the client version and is correct
Submodule api deleted from 6061ddc684.
+174 -38
View File
@@ -1,45 +1,181 @@
/**
* ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
* ┃ GrimAC Build Configuration ┃
* ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
*
* 🔧 Build Flags:
* -PshadePE=true → Enables 'lite' mode
* -Prelocate=false → Adds 'no_relocate' modifier
* -Prelease=true → Removes commit/modifiers for release build
*
* Logic in: buildSrc/versioning/BuildConfig.kt & VersionUtil.kt
*/
import net.minecrell.pluginyml.bukkit.BukkitPluginDescription.Permission
import versioning.BuildConfig
import versioning.VersionUtil
plugins {
id("java")
id("maven-publish")
id("com.github.johnrengelman.shadow") version "8.1.1"
id("io.freefair.lombok") version "8.6"
id("net.minecrell.plugin-yml.bukkit") version "0.6.0"
id("com.diffplug.spotless") version "6.25.0"
}
BuildConfig.init(project)
spotless {
java {
endWithNewline()
indentWithSpaces(4)
removeUnusedImports()
trimTrailingWhitespace()
targetExclude("build/generated/**/*")
}
val baseVersion = "2.3.72"
group = "ac.grim.grimac"
version = VersionUtil.computeVersion(baseVersion)
description = "Libre simulation anticheat designed for 1.21 with 1.8–1.21 support, powered by PacketEvents 2.0."
println("⚙️ Build configuration:")
println(" shadePE = ${BuildConfig.shadePE}")
println(" relocate = ${BuildConfig.relocate}")
println(" mavenLocalOverride = ${BuildConfig.mavenLocalOverride}")
println(" release = ${BuildConfig.release}")
println(" version = $version")
tasks.register("printVersion") {
group = "versioning"
description = "Prints the computed project version"
doLast {
println("VERSION=$version")
kotlinGradle {
endWithNewline()
indentWithSpaces(4)
trimTrailingWhitespace()
}
}
// ---------- Java Compile Optimization ----------
subprojects {
tasks.withType<JavaCompile>().configureEach {
options.isFork = true
options.isIncremental = true
group = "ac.grim.grimac"
version = "2.3.68"
description = "Libre simulation anticheat designed for 1.21 with 1.8-1.21 support, powered by PacketEvents 2.0."
java {
toolchain.languageVersion.set(JavaLanguageVersion.of(17))
}
// Set to false for debug builds
// You cannot live reload classes if the jar relocates dependencies
var relocate = true;
repositories {
mavenLocal()
maven("https://hub.spigotmc.org/nexus/content/repositories/snapshots/") // Spigot
maven("https://jitpack.io/") { // Grim API
content {
includeGroup("com.github.grimanticheat")
}
}
}
maven("https://repo.viaversion.com") // ViaVersion
maven("https://repo.aikar.co/content/groups/aikar/") // ACF
maven("https://nexus.scarsz.me/content/repositories/releases") // Configuralize
maven("https://repo.opencollab.dev/maven-snapshots/") // Floodgate
maven("https://repo.opencollab.dev/maven-releases/") // Cumulus (for Floodgate)
maven("https://repo.codemc.io/repository/maven-releases/") // PacketEvents
maven("https://repo.codemc.io/repository/maven-snapshots/")
mavenCentral()
// FastUtil, Discord-Webhooks
}
dependencies {
implementation("com.github.retrooper:packetevents-spigot:2.5.1-SNAPSHOT")
implementation("co.aikar:acf-paper:0.5.1-SNAPSHOT")
implementation("club.minnced:discord-webhooks:0.8.0") // Newer versions include kotlin-stdlib, which leads to incompatibility with plugins that use Kotlin
implementation("it.unimi.dsi:fastutil:8.5.13")
implementation("github.scarsz:configuralize:1.4.0")
//implementation("com.github.grimanticheat:grimapi:1193c4fa41")
// Used for local testing: implementation("ac.grim.grimac:GRIMAPI:1.0")
implementation("com.github.grimanticheat:grimapi:fc5634e444")
implementation("org.jetbrains:annotations:24.1.0")
compileOnly("org.geysermc.floodgate:api:2.0-SNAPSHOT")
compileOnly("org.spigotmc:spigot-api:1.18.2-R0.1-SNAPSHOT")
compileOnly("com.viaversion:viaversion-api:5.0.4-SNAPSHOT")
//
compileOnly("io.netty:netty-all:4.1.85.Final")
}
bukkit {
name = "GrimAC"
author = "GrimAC"
main = "ac.grim.grimac.GrimAC"
website = "https://grim.ac/"
apiVersion = "1.13"
foliaSupported = true
softDepend = listOf(
"ProtocolLib",
"ProtocolSupport",
"Essentials",
"ViaVersion",
"ViaBackwards",
"ViaRewind",
"Geyser-Spigot",
"floodgate",
"FastLogin"
)
permissions {
register("grim.alerts") {
description = "Receive alerts for violations"
default = Permission.Default.OP
}
register("grim.alerts.enable-on-join") {
description = "Enable alerts on join"
default = Permission.Default.OP
}
register("grim.performance") {
description = "Check performance metrics"
default = Permission.Default.OP
}
register("grim.profile") {
description = "Check user profile"
default = Permission.Default.OP
}
register("grim.brand") {
description = "Show client brands on join"
default = Permission.Default.OP
}
register("grim.sendalert") {
description = "Send cheater alert"
default = Permission.Default.OP
}
register("grim.nosetback") {
description = "Disable setback"
default = Permission.Default.FALSE
}
register("grim.nomodifypacket") {
description = "Disable modifying packets"
default = Permission.Default.FALSE
}
register("grim.exempt") {
description = "Exempt from all checks"
default = Permission.Default.FALSE
}
}
}
tasks.build {
dependsOn(tasks.shadowJar)
dependsOn(tasks.spotlessApply)
}
tasks.withType<JavaCompile> {
options.encoding = "UTF-8"
}
publishing.publications.create<MavenPublication>("maven") {
artifact(tasks["shadowJar"])
}
tasks.shadowJar {
minimize()
archiveFileName.set("${project.name}-${project.version}.jar")
if (relocate) {
relocate("io.github.retrooper.packetevents", "ac.grim.grimac.shaded.io.github.retrooper.packetevents")
relocate("com.github.retrooper.packetevents", "ac.grim.grimac.shaded.com.github.retrooper.packetevents")
relocate("co.aikar.commands", "ac.grim.grimac.shaded.acf")
relocate("co.aikar.locale", "ac.grim.grimac.shaded.locale")
relocate("club.minnced", "ac.grim.grimac.shaded.discord-webhooks")
relocate("github.scarsz.configuralize", "ac.grim.grimac.shaded.configuralize")
relocate("com.github.puregero", "ac.grim.grimac.shaded.com.github.puregero")
relocate("com.google.code.gson", "ac.grim.grimac.shaded.gson")
relocate("alexh", "ac.grim.grimac.shaded.maps")
relocate("it.unimi.dsi.fastutil", "ac.grim.grimac.shaded.fastutil")
relocate("net.kyori", "ac.grim.grimac.shaded.kyori")
relocate("okhttp3", "ac.grim.grimac.shaded.okhttp3")
relocate("okio", "ac.grim.grimac.shaded.okio")
relocate("org.yaml.snakeyaml", "ac.grim.grimac.shaded.snakeyaml")
relocate("org.json", "ac.grim.grimac.shaded.json")
relocate("org.intellij", "ac.grim.grimac.shaded.intellij")
relocate("org.jetbrains", "ac.grim.grimac.shaded.jetbrains")
}
}
-16
View File
@@ -1,16 +0,0 @@
plugins {
`kotlin-dsl`
kotlin("plugin.serialization") version "1.9.0" // Use the same Kotlin version as your main project
}
repositories {
mavenCentral()
gradlePluginPortal()
}
dependencies {
implementation(libs.spotless)
implementation(libs.lombok)
implementation(libs.shadow)
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.0")
}
-7
View File
@@ -1,7 +0,0 @@
dependencyResolutionManagement {
versionCatalogs {
create("libs") {
from(files("../libs.versions.toml"))
}
}
}
@@ -1,76 +0,0 @@
plugins {
`java-library`
id("io.freefair.lombok")
id("com.diffplug.spotless")
}
group = rootProject.group
version = rootProject.version
description = rootProject.description
// Java compilation settings
java {
toolchain.languageVersion.set(JavaLanguageVersion.of(21))
disableAutoTargetJvm()
withSourcesJar()
withJavadocJar()
}
// Lombok configuration
//lombok {
// version.set("1.18.30") // Use the version from your version catalog if available
//}
// Spotless configuration
spotless {
java {
endWithNewline()
indentWithSpaces(4)
removeUnusedImports()
trimTrailingWhitespace()
targetExclude("build/generated/**/*")
}
kotlinGradle {
endWithNewline()
indentWithSpaces(4)
trimTrailingWhitespace()
}
}
tasks {
withType<JavaCompile>().configureEach {
options.encoding = "UTF-8"
options.release.set(17)
}
build {
// Ensure spotlessApply runs before build
dependsOn(tasks.named("spotlessApply"))
}
// Process resources (e.g., for plugin metadata files)
processResources {
inputs.property("version", project.version)
filesMatching(listOf( // "plugin.yml", Bukkit variable expansion taken care of by net.minecrell.plugin-yml.bukkit
"bungee.yml", "velocity-plugin.json", "fabric.mod.json")
) {
expand("version" to project.version)
}
}
javadoc {
title = "${rootProject.name}-${project.name} v${rootProject.version}"
options.encoding = "UTF-8"
options.overview =
rootProject.file("buildSrc/src/main/resources/javadoc-overview.html").toString()
setDestinationDir(file("${project.layout.buildDirectory.asFile.get()}/docs/javadoc"))
options {
(this as CoreJavadocOptions).addBooleanOption("Xdoclint:none", true)
}
}
}
// Default tasks
defaultTasks("build")
@@ -1,407 +0,0 @@
import kotlinx.serialization.json.*
import java.util.jar.JarFile
import java.util.zip.ZipEntry
plugins {
`java-library`
}
val jijDependencies: Configuration by project.configurations.creating {
isCanBeResolved = true
isCanBeConsumed = false
extendsFrom(project.configurations.getByName("implementation"))
project.configurations.findByName("modImplementation")?.let { extendsFrom(it) }
}
data class DependencyIdentifier(
val group: String,
val name: String,
val version: String,
val classifier: String = "" // Add classifier field, default to empty string
) {
override fun toString() = "$group:$name:$version${if (classifier.isNotEmpty()) ":$classifier" else ""}"
}
// Does not support extracting classifiers but while the fabric standard is ambiguous on whether they can be included
// It does not appear dependency classifiers are in use by any mod I am aware of or can find
fun parseFabricModJson(jarFile: File, project: Project): Set<DependencyIdentifier> {
val nestedJars = mutableSetOf<DependencyIdentifier>()
try {
JarFile(jarFile).use { jar ->
val fabricModJsonEntry: ZipEntry? = jar.getEntry("fabric.mod.json")
if (fabricModJsonEntry != null) {
jar.getInputStream(fabricModJsonEntry).bufferedReader().use { reader ->
val jsonContent = reader.readText()
val jsonElement = Json.parseToJsonElement(jsonContent)
val jsonObject = jsonElement.jsonObject
val jarsArray = jsonObject["jars"]?.jsonArray ?: return@use
jarsArray.forEach { jarEntry: JsonElement ->
val filePath =
jarEntry.jsonObject["file"]?.jsonPrimitive?.content ?: return@forEach
project.logger.debug(
"Found nested JAR in fabric.mod.json: {} in {}",
filePath,
jarFile
)
val jarName = filePath.substringAfterLast("/")
val parts = jarName.removeSuffix(".jar").split("-")
if (parts.size >= 2) {
val inferredName = parts.dropLast(1).joinToString("-")
val inferredVersion = parts.last()
nestedJars.add(DependencyIdentifier("", inferredName, inferredVersion))
project.logger.debug("Inferred nested dependency from fabric.mod.json: :$inferredName:$inferredVersion")
}
}
}
}
}
} catch (e: Exception) {
project.logger.error("Failed to parse fabric.mod.json in $jarFile: ${e.message}")
}
return nestedJars
}
fun extractEmbeddedJars(jarFile: File, project: Project): Set<DependencyIdentifier> {
val embeddedJars = mutableSetOf<DependencyIdentifier>()
embeddedJars.addAll(parseFabricModJson(jarFile, project)) // Retain existing logic for top-level fabric.mod.json parsing
try {
JarFile(jarFile).use { jar ->
val entries = jar.entries()
while (entries.hasMoreElements()) {
val entry = entries.nextElement()
val entryName = entry.name
if (entryName.endsWith(".jar") && (entryName.startsWith("META-INF/jarjar/") || entryName.startsWith(
"libs/"
) || entryName.startsWith("META-INF/jars/"))
) {
project.logger.debug("Found embedded JAR: {} in {}", entryName, jarFile)
val tempFile = File.createTempFile("embedded-", ".jar")
tempFile.deleteOnExit()
jar.getInputStream(entry).use { input ->
tempFile.outputStream().use { output ->
input.copyTo(output)
}
}
JarFile(tempFile).use { embeddedJar ->
val fabricModJsonEntry: ZipEntry? = embeddedJar.getEntry("fabric.mod.json")
if (fabricModJsonEntry != null) {
embeddedJar.getInputStream(fabricModJsonEntry).bufferedReader().use { reader ->
val jsonContent = reader.readText()
val jsonElement = Json.parseToJsonElement(jsonContent)
val jsonObject = jsonElement.jsonObject
// Extract id and parse into group
val id = jsonObject["id"]?.jsonPrimitive?.content
?: throw IllegalStateException("Missing 'id' in fabric.mod.json of embedded JAR: $entryName")
val idWithDots = id.replace("_", ".")
val lastDotIndex = idWithDots.lastIndexOf(".")
val group = if (lastDotIndex != -1) idWithDots.substring(0, lastDotIndex) else ""
// Extract name and version from the JAR filename
val jarName = entryName.substringAfterLast("/").removeSuffix(".jar")
val (name, version) = parseNameAndVersionFromJarName(jarName)
?: throw IllegalStateException("Could not parse name and version from JAR filename: $jarName")
// Create DependencyIdentifier (classifier is empty for now)
val depId = DependencyIdentifier(group, name, version)
embeddedJars.add(depId)
project.logger.debug("Identified embedded dependency from fabric.mod.json and filename: {}", depId)
}
} else {
project.logger.error("No fabric.mod.json found in embedded JAR: {}. Skipping.", entryName)
}
}
}
}
}
} catch (e: Exception) {
project.logger.error("Failed to analyze JAR file $jarFile for embedded JARs: ${e.message}")
}
return embeddedJars
}
fun parseNameAndVersionFromJarName(jarName: String): Pair<String, String>? {
val versionStartRegex = Regex("-(?=\\d)")
// Use regex to find the position of the hyphen followed by a digit
val versionStartMatch = versionStartRegex.find(jarName) ?: return null
val versionStartIndex = versionStartMatch.range.first
// Split the JAR name into name and version at the version start index
val name = jarName.substring(0, versionStartIndex)
val version = jarName.substring(versionStartIndex + 1) // Skip the hyphen
return if (name.isNotEmpty() && version.isNotEmpty()) {
Pair(name, version)
} else {
null
}
}
fun shouldExcludeDependency(
dependency: ResolvedDependency,
allEmbeddedDependencies: Set<DependencyIdentifier>,
actuallyIncludedDependencies: Set<DependencyIdentifier>,
project: Project
): Boolean {
val group = dependency.moduleGroup
val name = dependency.moduleName
val version = dependency.moduleVersion
// Extract classifier from resolved dependency (if available)
val classifier = dependency.moduleArtifacts.firstOrNull()?.classifier ?: ""
val depId = DependencyIdentifier(group, name, version, classifier)
if (group.startsWith("net.minecraft") ||
group.startsWith("net.fabricmc") ||
name == "minecraft" ||
name == "fabric-loader" ||
name == "fastutil" ||
name.startsWith("fabric_") ||
name.startsWith("fabric-api")
) {
project.logger.debug("Excluding platform dependency: {}", depId)
return true
}
if (name.endsWith("-bom") || name.contains("_bom")) {
project.logger.debug("Excluding BOM file: {}", depId)
return true
}
if (allEmbeddedDependencies.any { it.name == name && it.version == version && it.classifier == classifier }) {
project.logger.debug("Excluding dependency already embedded in another JAR: {}", depId)
return true
}
if (actuallyIncludedDependencies.any { it.name == name && it.version == version && it.classifier == classifier }) {
project.logger.debug("Excluding duplicate dependency: {}", depId)
return true
}
return false
}
fun isJijTarget(
dependency: ResolvedDependency,
allEmbeddedDependencies: Set<DependencyIdentifier>,
actuallyIncludedDependencies: Set<DependencyIdentifier>,
project: Project
): Boolean {
val classifier = dependency.moduleArtifacts.firstOrNull()?.classifier ?: ""
project.logger.debug(
"Checking JIJ target: {}",
DependencyIdentifier(
dependency.moduleGroup,
dependency.moduleName,
dependency.moduleVersion,
classifier
)
)
return !shouldExcludeDependency(
dependency,
allEmbeddedDependencies,
actuallyIncludedDependencies,
project
)
}
fun includeDependencyWithExclusions(
depId: DependencyIdentifier,
project: Project,
actuallyIncludedDependencies: MutableSet<DependencyIdentifier>,
allEmbeddedDependencies: Set<DependencyIdentifier>
) {
project.logger.debug("Including external dependency as JIJ with dynamic exclusions: {}", depId)
project.dependencies {
// Include dependency with classifier (if present)
"include"("${depId.group}:${depId.name}:${depId.version}${if (depId.classifier.isNotEmpty()) ":${depId.classifier}" else ""}") {
allEmbeddedDependencies.forEach { embeddedDep ->
if (embeddedDep.group.isNotEmpty()) {
exclude(group = embeddedDep.group, module = embeddedDep.name)
} else {
exclude(module = embeddedDep.name)
}
project.logger.debug("Excluding embedded transitive dependency: ${embeddedDep.name}:${embeddedDep.version}${if (embeddedDep.classifier.isNotEmpty()) ":${embeddedDep.classifier}" else ""}")
}
}
}
actuallyIncludedDependencies.add(depId)
}
fun processDependencies(
dependencies: Set<ResolvedDependency>,
processed: MutableSet<String>,
allEmbeddedDependencies: MutableMap<DependencyIdentifier, File>,
actuallyIncludedDependencies: MutableSet<DependencyIdentifier>,
project: Project
) {
dependencies.forEach { dep: ResolvedDependency ->
val classifier = dep.moduleArtifacts.firstOrNull()?.classifier ?: ""
val depKey = "${dep.moduleGroup}:${dep.moduleName}:${dep.moduleVersion}${if (classifier.isNotEmpty()) ":$classifier" else ""}"
val depId = DependencyIdentifier(dep.moduleGroup, dep.moduleName, dep.moduleVersion, classifier)
if (!processed.contains(depKey)) {
processed.add(depKey)
project.logger.debug("Resolved dependency: {}", depId)
val jarFile = dep.moduleArtifacts.firstOrNull()?.file
if (jarFile != null && jarFile.exists()) {
val embeddedJars = extractEmbeddedJars(jarFile, project)
embeddedJars.forEach { embeddedDep ->
allEmbeddedDependencies[embeddedDep] = jarFile
}
}
if (isJijTarget(
dep,
allEmbeddedDependencies.keys,
actuallyIncludedDependencies,
project
)
) {
project.logger.debug("Processing JIJ dependency: {}", depId)
val isProjectDependency = project.rootProject.allprojects.any { subproject ->
subproject.name == dep.moduleName && (subproject.group == dep.moduleGroup || dep.moduleGroup.isEmpty())
}
if (isProjectDependency) {
val projectPath = project.rootProject.allprojects.find { subproject ->
subproject.name == dep.moduleName && (subproject.group == dep.moduleGroup || dep.moduleGroup.isEmpty())
}?.path
?: throw IllegalStateException("Project dependency not found: ${dep.moduleName}")
project.logger.debug("Including project dependency as JIJ: $projectPath")
project.dependencies {
"include"(project.project(projectPath))
}
actuallyIncludedDependencies.add(depId)
} else {
includeDependencyWithExclusions(
depId,
project,
actuallyIncludedDependencies,
allEmbeddedDependencies.keys
)
}
}
processDependencies(
dep.children,
processed,
allEmbeddedDependencies,
actuallyIncludedDependencies,
project
)
}
}
}
project.afterEvaluate {
project.logger.debug("Resolving jijDependencies configuration")
val resolvedDependencies = jijDependencies.resolvedConfiguration.firstLevelModuleDependencies
val processed = mutableSetOf<String>()
val allEmbeddedDependencies = mutableMapOf<DependencyIdentifier, File>()
val actuallyIncludedDependencies = mutableSetOf<DependencyIdentifier>()
fun collectAllEmbeddedDependencies(dependencies: Set<ResolvedDependency>) {
dependencies.forEach { dep ->
val classifier = dep.moduleArtifacts.firstOrNull()?.classifier ?: ""
val depKey = "${dep.moduleGroup}:${dep.moduleName}:${dep.moduleVersion}${if (classifier.isNotEmpty()) ":$classifier" else ""}"
if (!processed.contains(depKey)) {
processed.add(depKey)
val jarFile = dep.moduleArtifacts.firstOrNull()?.file
if (jarFile != null && jarFile.exists()) {
val embeddedJars = extractEmbeddedJars(jarFile, project)
embeddedJars.forEach { embeddedDep ->
allEmbeddedDependencies[embeddedDep] = jarFile
project.logger.debug(
"Collected embedded dependency: {} from {}",
embeddedDep,
jarFile
)
}
}
collectAllEmbeddedDependencies(dep.children)
}
}
}
processed.clear()
collectAllEmbeddedDependencies(resolvedDependencies)
processed.clear()
processDependencies(
resolvedDependencies,
processed,
allEmbeddedDependencies,
actuallyIncludedDependencies,
project
)
}
tasks.withType<Jar>().configureEach {
doFirst {
project.logger.debug("Resolving JIJ dependencies for JAR inclusion")
val resolvedDependencies =
jijDependencies.resolvedConfiguration.firstLevelModuleDependencies
val processed = mutableSetOf<String>()
val allEmbeddedDependencies = mutableMapOf<DependencyIdentifier, File>()
val actuallyIncludedDependencies = mutableSetOf<DependencyIdentifier>()
fun collectAllEmbeddedDependencies(dependencies: Set<ResolvedDependency>) {
dependencies.forEach { dep ->
val depKey = "${dep.moduleGroup}:${dep.moduleName}:${dep.moduleVersion}"
if (!processed.contains(depKey)) {
processed.add(depKey)
val jarFile = dep.moduleArtifacts.firstOrNull()?.file
if (jarFile != null && jarFile.exists()) {
val embeddedJars = extractEmbeddedJars(jarFile, project)
embeddedJars.forEach { embeddedDep ->
allEmbeddedDependencies[embeddedDep] = jarFile
project.logger.debug(
"Collected embedded dependency for JAR: {} from {}",
embeddedDep,
jarFile
)
}
}
collectAllEmbeddedDependencies(dep.children)
}
}
}
processed.clear()
collectAllEmbeddedDependencies(resolvedDependencies)
processed.clear()
fun logDependencies(dependencies: Set<ResolvedDependency>) {
dependencies.forEach { dep ->
val depKey = "${dep.moduleGroup}:${dep.moduleName}:${dep.moduleVersion}"
val depId = DependencyIdentifier(dep.moduleGroup, dep.moduleName, dep.moduleVersion)
if (!processed.contains(depKey)) {
processed.add(depKey)
project.logger.debug("Resolved JIJ dependency for JAR: {}", depId)
val jarFile = dep.moduleArtifacts.firstOrNull()?.file
if (jarFile != null && jarFile.exists()) {
val embeddedJars = extractEmbeddedJars(jarFile, project)
embeddedJars.forEach { embeddedDep ->
allEmbeddedDependencies[embeddedDep] = jarFile
}
}
if (isJijTarget(
dep,
allEmbeddedDependencies.keys,
actuallyIncludedDependencies,
project
)
) {
project.logger.debug("Including JIJ dependency in JAR: {}", depId)
actuallyIncludedDependencies.add(depId)
}
logDependencies(dep.children)
}
}
}
logDependencies(resolvedDependencies)
}
}
@@ -1,48 +0,0 @@
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
import versioning.BuildConfig
plugins {
id("com.gradleup.shadow")
}
// Create a configuration for shading dependencies from common
val shadowCommon: Configuration by project.configurations.creating {
isCanBeResolved = true
isCanBeConsumed = false
// Extend from the "implementation" configuration of the current project
extendsFrom(project.configurations.getByName("implementation"))
}
tasks.named<ShadowJar>("shadowJar") {
archiveFileName.set("${rootProject.name}-${project.name}-${rootProject.version}.jar")
from(shadowCommon) // Use from() instead of direct assignment
if (BuildConfig.relocate) {
if (BuildConfig.shadePE) {
relocate("io.github.retrooper.packetevents", "ac.grim.grimac.shaded.io.github.retrooper.packetevents")
relocate("com.github.retrooper.packetevents", "ac.grim.grimac.shaded.com.github.retrooper.packetevents")
relocate("net.kyori", "ac.grim.grimac.shaded.kyori") // use PE's built-in adventure instead when not shading PE
}
relocate("club.minnced", "ac.grim.grimac.shaded.discord-webhooks")
relocate("org.slf4j", "ac.grim.grimac.shaded.slf4j") // Required by discord-webhooks
relocate("github.scarsz.configuralize", "ac.grim.grimac.shaded.configuralize")
relocate("com.github.puregero", "ac.grim.grimac.shaded.com.github.puregero")
relocate("com.google.code.gson", "ac.grim.grimac.shaded.gson")
relocate("alexh", "ac.grim.grimac.shaded.maps")
relocate("it.unimi.dsi.fastutil", "ac.grim.grimac.shaded.fastutil")
relocate("okhttp3", "ac.grim.grimac.shaded.okhttp3")
relocate("okio", "ac.grim.grimac.shaded.okio")
relocate("org.yaml.snakeyaml", "ac.grim.grimac.shaded.snakeyaml")
relocate("org.json", "ac.grim.grimac.shaded.json")
relocate("org.intellij", "ac.grim.grimac.shaded.intellij")
relocate("org.jetbrains", "ac.grim.grimac.shaded.jetbrains")
relocate("org.incendo", "ac.grim.grimac.shaded.incendo")
relocate("io.leangen.geantyref", "ac.grim.grimac.shaded.geantyref") // Required by cloud
}
mergeServiceFiles()
minimize()
}
tasks.named("assemble") {
dependsOn(tasks.named("shadowJar"))
}
@@ -1,99 +0,0 @@
package versioning
import org.gradle.api.Project
import org.gradle.internal.extensions.stdlib.toDefaultLowerCase
/**
* BuildConfig provides access to user-defined build flags that control how a Grim
* build is assembled. These flags are resolved once at configuration time using
* [init], and are then exposed as fast, memoized values.
*
* Flags can be defined in three ways:
*
* ① JVM system properties (-Dflag=value)
* ② Gradle project properties (-Pflag=value or in gradle.properties)
* ③ Environment variables (FLAG=value)
*
* You can use these to enable/disable features like shading, relocation, or release mode.
*
* Examples:
*
* Using Gradle -P properties:
* ```
* ./gradlew build -PshadePE=true -Prelocate=false -Prelease=true
* ```
*
* Using environment variables:
* ```
* SHADE_PE=true RELOCATE_JAR=false RELEASE=true ./gradlew build
* ```
*
* Using JVM system properties:
* ```
* ./gradlew build -DshadePE=true -Drelease=true
* ```
*
* @property shadePE If true, shades PacketEvents into the jar. Default: true.
* @property relocate If true, relocates shaded dependencies to avoid conflicts. Default: true.
* @property release If true, omits commit hash and modifiers from version string. Default: false.
* @property mavenLocalOverride If true, will make artifacts in mavenLocal() will be used instead of their remote counterparts for this build. Default: false
*/
object BuildConfig {
/**
* Must be called once from your root build script to initialize the flags.
* Example (in build.gradle.kts):
* ```
* BuildConfig.init(project)
* ```
*/
fun init(project: Project) {
_shadePE = resolveBool(project, "shadePE", altKey = "SHADE_PE", default = true)
_relocate = resolveBool(project, "relocate", altKey = "RELOCATE_JAR", default = true)
_release = resolveBool(project, "release", default = false)
_mavenLocalOverride = resolveBool(project, "mavenLocalOverride", altKey = "MAVEN_LOCAL_OVERRIDE", default = false)
}
// Unified resolution logic (System > Gradle > Env)
private fun resolveRaw(project: Project, key: String): String? =
System.getProperty(key) // ① JVM (-Dkey=value)
?: project.findProperty(key)?.toString() // ② Gradle (-Pkey=value or gradle.properties)
?: System.getenv(key.uppercase()) // ③ ENV (KEY=value)
private fun resolveBool(project: Project, key: String, altKey: String? = null, default: Boolean): Boolean {
val primaryValue = resolveRaw(project, key)?.toDefaultLowerCase()?.toBooleanStrictOrNull()
if (primaryValue != null) {
return primaryValue
}
if (altKey != null) {
val altValue = resolveRaw(project, altKey)?.toDefaultLowerCase()?.toBooleanStrictOrNull()
if (altValue != null) {
return altValue
}
}
return default
}
// Private backing vars (nullable because we can't use lateinit with primitives)
private var _shadePE: Boolean? = null
private var _relocate: Boolean? = null
private var _release: Boolean? = null
private var _mavenLocalOverride: Boolean? = null
/** If true, shades PacketEvents into the jar. Default: true. */
val shadePE: Boolean get() = _shadePE
?: error("BuildConfig.shadePE accessed before init() was called")
/** If true, relocates shaded dependencies to avoid conflicts. Default: true. */
val relocate: Boolean get() = _relocate
?: error("BuildConfig.relocate accessed before init() was called")
/** If true, omits commit hash and modifiers from version string. Default: false. */
val release: Boolean get() = _release
?: error("BuildConfig.release accessed before init() was called")
val mavenLocalOverride: Boolean get() = _mavenLocalOverride
?: error("BuildConfig.release accessed before init() was called")
}
@@ -1,88 +0,0 @@
package versioning
import java.io.ByteArrayOutputStream
/**
* Utility for computing the version string of GrimAC artifacts.
*
* The version string is constructed based on:
* - A base semantic version (e.g., "2.3.72")
* - Git commit hash (unless in release mode)
* - Git branch name (unless in release mode or main)
* - Modifiers (e.g., lite, no_relocate) for non-default build configurations
*
* Example outputs:
* - `2.3.72` (release build)
* - `2.3.72-a4f8b21+lite` (preview build without PE shading)
* - `2.3.72-feature_branch-a4f8b21+lite-no_relocate`
*
* @see BuildConfig for controlling the release/modifier behavior
*/
object VersionUtil {
/**
* Computes the full version string for the build.
*
* @param baseVersion The base semantic version (e.g., "2.3.72")
* @return Full version string including commit hash, branch, and modifiers if applicable
*/
fun computeVersion(baseVersion: String): String {
if (BuildConfig.release) {
return baseVersion
}
val commitHash = getGitCommitHash()
val branch = getGitBranch()
val modifiers = buildList {
if (!BuildConfig.shadePE) add("lite")
if (!BuildConfig.relocate) add("no_relocate")
}.takeIf { it.isNotEmpty() }?.joinToString("-")
return buildString {
append(baseVersion)
append("-")
branch?.let { append("$it-") }
append(commitHash)
modifiers?.let { append("+$modifiers") }
}
}
/**
* Retrieves the current Git commit as a short hash.
*/
private fun getGitCommitHash(): String {
val stdout = ByteArrayOutputStream()
ProcessBuilder("git", "rev-parse", "--short", "HEAD")
.redirectErrorStream(true)
.start()
.apply { waitFor() }
.inputStream
.use { stdout.writeBytes(it.readAllBytes()) }
return stdout.toString().trim()
}
/**
* Returns the current Git branch, sanitised for use in file names.
* If the branch is "main" or "2.0", returns null.
*
* Any slash (/) in the branch name is replaced with an underscore (_)
* to avoid filesystem issues.
*/
private fun getGitBranch(): String? {
val stdout = ByteArrayOutputStream()
ProcessBuilder("git", "rev-parse", "--abbrev-ref", "HEAD")
.redirectErrorStream(true)
.start()
.apply { waitFor() }
.inputStream.use { stdout.writeBytes(it.readAllBytes()) }
val branch = stdout.toString().trim()
return when (branch) {
"main", "2.0" -> null // ← ignore these branches
else -> branch.replace("/", "_")
}
}
}
-149
View File
@@ -1,149 +0,0 @@
import net.minecrell.pluginyml.bukkit.BukkitPluginDescription.Permission
import versioning.BuildConfig
plugins {
`maven-publish`
grim.`base-conventions`
grim.`shadow-conventions`
id("net.minecrell.plugin-yml.bukkit") version "0.6.0"
}
repositories {
if (BuildConfig.mavenLocalOverride) {
mavenLocal()
}
maven {
name = "papermc"
url = uri("https://repo.papermc.io/repository/maven-public/")
}
maven("https://hub.spigotmc.org/nexus/content/repositories/snapshots/") // Spigot
maven("https://repo.grim.ac/snapshots") { // Grim API
content {
includeGroup("ac.grim.grimac")
includeGroup("com.github.retrooper")
}
}
maven("https://repo.viaversion.com") // ViaVersion
maven("https://nexus.scarsz.me/content/repositories/releases") // Configuralize
maven("https://repo.opencollab.dev/maven-snapshots/") // Floodgate
maven("https://repo.opencollab.dev/maven-releases/") // Cumulus (for Floodgate)
maven("https://s01.oss.sonatype.org/content/repositories/snapshots/")
maven("https://repo.extendedclip.com/content/repositories/placeholderapi/") // placeholderapi
mavenCentral() // FastUtil
}
dependencies {
compileOnly(libs.paper.api)
compileOnly(libs.placeholderapi)
if (BuildConfig.shadePE) {
implementation(libs.packetevents.spigot)
} else {
compileOnly(libs.packetevents.spigot)
}
implementation(libs.cloud.paper)
implementation(libs.adventure.platform.bukkit)
shadowCommon(project(":common", "default"))
implementation(project(":api"))
implementation(project(":api:packetevents"))
}
bukkit {
name = "GrimAC"
author = "GrimAC"
main = "ac.grim.grimac.platform.bukkit.GrimACBukkitLoaderPlugin"
website = "https://grim.ac/"
apiVersion = "1.13"
foliaSupported = true
if (!BuildConfig.shadePE) {
depend = listOf("packetevents")
}
softDepend = listOf(
"ProtocolLib",
"ProtocolSupport",
"Essentials",
"ViaVersion",
"ViaBackwards",
"ViaRewind",
"Geyser-Spigot",
"floodgate",
"FastLogin",
"PlaceholderAPI",
)
permissions {
register("grim.alerts") {
description = "Receive alerts for violations"
default = Permission.Default.OP
}
register("grim.alerts.enable-on-join") {
description = "Enable alerts on join"
default = Permission.Default.OP
}
register("grim.performance") {
description = "Check performance metrics"
default = Permission.Default.OP
}
register("grim.profile") {
description = "Check user profile"
default = Permission.Default.OP
}
register("grim.brand") {
description = "Show client brands on join"
default = Permission.Default.OP
}
register("grim.brand.enable-on-join") {
description = "Enable showing client brands on join"
default = Permission.Default.OP
}
register("grim.sendalert") {
description = "Send cheater alert"
default = Permission.Default.OP
}
register("grim.nosetback") {
description = "Disable setback"
default = Permission.Default.FALSE
}
register("grim.nomodifypacket") {
description = "Disable modifying packets"
default = Permission.Default.FALSE
}
register("grim.exempt") {
description = "Exempt from all checks"
default = Permission.Default.FALSE
}
register("grim.verbose") {
description = "Receive verbose alerts for violations. Requires grim.alerts"
default = Permission.Default.OP
}
register("grim.verbose.enable-on-join") {
description =
"Enable verbose alerts on join. Requires grim.alerts and grim.alerts.enable-on-join"
default = Permission.Default.FALSE
}
}
}
publishing.publications.create<MavenPublication>("maven") {
artifact(tasks["shadowJar"])
}
tasks.shadowJar {
manifest {
attributes["paperweight-mappings-namespace"] = "mojang"
}
}
@@ -1,27 +0,0 @@
package ac.grim.grimac.platform.bukkit;
import ac.grim.grimac.api.platform.PlatformPlugin;
import org.bukkit.plugin.Plugin;
public class BukkitPlatformPlugin implements PlatformPlugin {
private final Plugin plugin;
public BukkitPlatformPlugin(Plugin plugin) {
this.plugin = plugin;
}
@Override
public boolean isEnabled() {
return plugin.isEnabled();
}
@Override
public String getName() {
return plugin.getName();
}
@Override
public String getVersion() {
return plugin.getDescription().getVersion();
}
}
@@ -1,37 +0,0 @@
package ac.grim.grimac.platform.bukkit;
import ac.grim.grimac.api.platform.PlatformServer;
import ac.grim.grimac.api.platform.sender.Sender;
import io.github.retrooper.packetevents.util.SpigotReflectionUtil;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
public class BukkitPlatformServer implements PlatformServer {
@Override
public String getPlatformImplementationString() {
return Bukkit.getVersion();
}
@Override
public void dispatchCommand(Sender sender, String command) {
CommandSender commandSender = GrimACBukkitLoaderPlugin.LOADER.getBukkitSenderFactory().reverse(sender);
Bukkit.dispatchCommand(commandSender, command);
}
@Override
public Sender getConsoleSender() {
return GrimACBukkitLoaderPlugin.LOADER.getBukkitSenderFactory().map(Bukkit.getConsoleSender());
}
@Override
public void registerOutgoingPluginChannel(String name) {
GrimACBukkitLoaderPlugin.LOADER.getServer().getMessenger().registerOutgoingPluginChannel(GrimACBukkitLoaderPlugin.LOADER, name);
}
@Override
public double getTPS() {
return SpigotReflectionUtil.getTPS();
}
}
@@ -1,206 +0,0 @@
package ac.grim.grimac.platform.bukkit;
import ac.grim.grimac.api.lazy.LazyHolder;
import ac.grim.grimac.api.packet.MCPacketAPI;
import ac.grim.grimac.api.packet.pe.PEPacketAPI;
import ac.grim.grimac.api.platform.CoreLoader;
import ac.grim.grimac.api.platform.init.Initable;
import ac.grim.grimac.api.plugin.BasicGrimPlugin;
import ac.grim.grimac.api.GrimAPIProvider;
import ac.grim.grimac.api.GrimAbstractAPI;
import ac.grim.grimac.api.plugin.GrimPlugin;
import ac.grim.grimac.api.platform.init.StartableInitable;
import ac.grim.grimac.api.platform.Platform;
import ac.grim.grimac.api.platform.PlatformLoader;
import ac.grim.grimac.api.platform.PlatformServer;
import ac.grim.grimac.api.platform.manager.ItemResetHandler;
import ac.grim.grimac.api.platform.manager.MessagePlaceHolderManager;
import ac.grim.grimac.api.platform.manager.ParserDescriptorFactory;
import ac.grim.grimac.api.platform.manager.PermissionRegistrationManager;
import ac.grim.grimac.api.platform.manager.PlatformPluginManager;
import ac.grim.grimac.api.platform.player.PlatformPlayerFactory;
import ac.grim.grimac.api.platform.scheduler.PlatformScheduler;
import ac.grim.grimac.api.platform.sender.Sender;
import ac.grim.grimac.api.platform.sender.SenderFactory;
import ac.grim.grimac.platform.bukkit.initables.BukkitBStats;
import ac.grim.grimac.platform.bukkit.initables.BukkitEventManager;
import ac.grim.grimac.platform.bukkit.initables.BukkitTickEndEvent;
import ac.grim.grimac.platform.bukkit.manager.BukkitItemResetHandler;
import ac.grim.grimac.platform.bukkit.manager.BukkitMessagePlaceHolderManager;
import ac.grim.grimac.platform.bukkit.manager.BukkitParserDescriptorFactory;
import ac.grim.grimac.platform.bukkit.manager.BukkitPermissionRegistrationManager;
import ac.grim.grimac.platform.bukkit.manager.BukkitPlatformPluginManager;
import ac.grim.grimac.platform.bukkit.player.BukkitPlatformPlayerFactory;
import ac.grim.grimac.platform.bukkit.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 com.github.retrooper.packetevents.PacketEventsAPI;
import io.github.retrooper.packetevents.factory.spigot.SpigotPacketEventsBuilder;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.plugin.ServicePriority;
import org.bukkit.plugin.java.JavaPlugin;
import org.incendo.cloud.CommandManager;
import org.incendo.cloud.brigadier.BrigadierSetting;
import org.incendo.cloud.brigadier.CloudBrigadierManager;
import org.incendo.cloud.bukkit.CloudBukkitCapabilities;
import org.incendo.cloud.execution.ExecutionCoordinator;
import org.incendo.cloud.paper.LegacyPaperCommandManager;
import org.incendo.cloud.setting.Configurable;
import org.jetbrains.annotations.NotNull;
public final class GrimACBukkitLoaderPlugin extends JavaPlugin implements PlatformLoader {
public static GrimACBukkitLoaderPlugin LOADER;
private final LazyHolder<PlatformScheduler> scheduler = LazyHolder.simple(this::createScheduler);
private final LazyHolder<PacketEventsAPI<?>> packetEvents = LazyHolder.simple(() -> SpigotPacketEventsBuilder.build(this));
private final LazyHolder<BukkitSenderFactory> senderFactory = LazyHolder.simple(BukkitSenderFactory::new);
private final LazyHolder<CommandManager<Sender>> commandManager = LazyHolder.simple(this::createCommandManager);
private final LazyHolder<ItemResetHandler> itemResetHandler = LazyHolder.simple(BukkitItemResetHandler::new);
private final PlatformPlayerFactory playerFactory = new BukkitPlatformPlayerFactory();
private final ParserDescriptorFactory parserFactory = new BukkitParserDescriptorFactory();
private final PlatformPluginManager platformPluginManager = new BukkitPlatformPluginManager();
private final GrimPlugin plugin;
private final PlatformServer platformServer = new BukkitPlatformServer();
private final MessagePlaceHolderManager messagePlaceHolderManager = new BukkitMessagePlaceHolderManager();
private final BukkitPermissionRegistrationManager bukkitPermissionRegistrationManager = new BukkitPermissionRegistrationManager();
private final MCPacketAPI mcPacketAPI = new PEPacketAPI();
public GrimACBukkitLoaderPlugin() {
this.plugin = new BasicGrimPlugin(
this.getLogger(),
this.getDataFolder(),
this.getDescription().getVersion(),
this.getDescription().getDescription(),
this.getDescription().getAuthors()
);
}
@Override
public void onLoad() {
LOADER = this;
CoreLoader.Manager.loadAll(this, this.getBukkitInitTasks());
}
private Initable[] getBukkitInitTasks() {
return new Initable[] {
new BukkitEventManager(),
new BukkitTickEndEvent(),
new BukkitBStats(),
(StartableInitable) () -> {
if (BukkitMessagePlaceHolderManager.hasPlaceholderAPI) {
new PlaceholderAPIExpansion().register();
}
}
};
}
@Override
public void onEnable() {
CoreLoader.Manager.startAll();
}
@Override
public void onDisable() {
CoreLoader.Manager.stopAll();
}
@Override
public PlatformScheduler getScheduler() {
return scheduler.get();
}
@Override
public PlatformPlayerFactory getPlatformPlayerFactory() {
return playerFactory;
}
@Override
public ParserDescriptorFactory getParserDescriptorFactory() {
return parserFactory;
}
@Override
public PacketEventsAPI<?> getPacketAPI() {
return packetEvents.get();
}
@Override
public CommandManager<Sender> getCommandManager() {
return commandManager.get();
}
@Override
public ItemResetHandler getItemResetHandler() {
return itemResetHandler.get();
}
@Override
public SenderFactory<CommandSender> getSenderFactory() {
return senderFactory.get();
}
@Override
public GrimPlugin getPlugin() {
return plugin;
}
@Override
public PlatformPluginManager getPluginManager() {
return platformPluginManager;
}
@Override
public PlatformServer getPlatformServer() {
return platformServer;
}
@Override
public void registerAPIService(GrimAbstractAPI api) {
GrimAPIProvider.init(api);
Bukkit.getServicesManager().register(GrimAbstractAPI.class, api, GrimACBukkitLoaderPlugin.LOADER, ServicePriority.Normal);
}
@Override
public @NotNull MessagePlaceHolderManager getMessagePlaceHolderManager() {
return messagePlaceHolderManager;
}
@Override
public PermissionRegistrationManager getPermissionManager() {
return bukkitPermissionRegistrationManager;
}
@Override
public MCPacketAPI getMCPacketAPI() {
return mcPacketAPI;
}
private PlatformScheduler createScheduler() {
return GrimAPIProvider.getDirect().getPlatform() == Platform.FOLIA ? new FoliaPlatformScheduler() : new BukkitPlatformScheduler();
}
private CommandManager<Sender> createCommandManager() {
LegacyPaperCommandManager<Sender> manager = new LegacyPaperCommandManager<>(
this,
ExecutionCoordinator.simpleCoordinator(),
senderFactory.get()
);
if (manager.hasCapability(CloudBukkitCapabilities.NATIVE_BRIGADIER)) {
manager.registerBrigadier();
CloudBrigadierManager<Sender, ?> cbm = manager.brigadierManager();
Configurable<BrigadierSetting> settings = cbm.settings();
settings.set(BrigadierSetting.FORCE_EXECUTABLE, true);
} else if (manager.hasCapability(CloudBukkitCapabilities.ASYNCHRONOUS_COMPLETION)) {
manager.registerAsynchronousCompletions();
}
return manager;
}
public BukkitSenderFactory getBukkitSenderFactory() {
return LOADER.senderFactory.get();
}
}
@@ -1,37 +0,0 @@
package ac.grim.grimac.platform.bukkit.command;
import ac.grim.grimac.api.GrimAPIProvider;
import ac.grim.grimac.api.platform.command.PlayerSelector;
import ac.grim.grimac.api.platform.sender.Sender;
import ac.grim.grimac.platform.bukkit.sender.BukkitSenderFactory;
import java.util.Collection;
import java.util.Collections;
public class BukkitPlayerSelectorAdapter implements PlayerSelector {
private final org.incendo.cloud.bukkit.data.SinglePlayerSelector bukkitSelector;
public BukkitPlayerSelectorAdapter(org.incendo.cloud.bukkit.data.SinglePlayerSelector bukkitSelector) {
this.bukkitSelector = bukkitSelector;
}
@Override
public boolean isSingle() {
return true;
}
@Override
public Sender getSinglePlayer() {
return ((BukkitSenderFactory) GrimAPIProvider.getDirect().getPlatformLoader().getSenderFactory()).map(bukkitSelector.single());
}
@Override
public Collection<Sender> getPlayers() {
return Collections.singletonList(((BukkitSenderFactory) GrimAPIProvider.getDirect().getPlatformLoader().getSenderFactory()).map(bukkitSelector.single()));
}
@Override
public String inputString() {
return bukkitSelector.inputString();
}
}
@@ -1,28 +0,0 @@
package ac.grim.grimac.platform.bukkit.command;
import ac.grim.grimac.api.platform.command.AbstractPlayerSelectorParser;
import ac.grim.grimac.api.platform.command.PlayerSelector;
import org.incendo.cloud.bukkit.parser.selector.SinglePlayerSelectorParser;
import org.incendo.cloud.context.CommandContext;
import org.incendo.cloud.parser.ParserDescriptor;
import java.util.concurrent.CompletableFuture;
public class BukkitPlayerSelectorParser<C> extends AbstractPlayerSelectorParser<C> {
@Override
public ParserDescriptor<C, PlayerSelector> descriptor() {
return super.createDescriptor();
}
@Override
protected ParserDescriptor<C, ?> getPlatformSpecificDescriptor() {
return SinglePlayerSelectorParser.singlePlayerSelectorParser();
}
@Override
protected CompletableFuture<PlayerSelector> adaptToCommonSelector(CommandContext<C> context, Object platformSpecificSelector) {
return CompletableFuture.completedFuture(new BukkitPlayerSelectorAdapter((org.incendo.cloud.bukkit.data.SinglePlayerSelector) platformSpecificSelector));
}
}
@@ -1,79 +0,0 @@
package ac.grim.grimac.platform.bukkit.entity;
import ac.grim.grimac.api.platform.entity.GrimEntity;
import ac.grim.grimac.api.platform.world.PlatformWorld;
import ac.grim.grimac.platform.bukkit.utils.convert.BukkitConversionUtils;
import ac.grim.grimac.platform.bukkit.utils.reflection.PaperUtils;
import ac.grim.grimac.platform.bukkit.world.BukkitPlatformWorld;
import ac.grim.grimac.api.math.Location;
import org.bukkit.entity.Entity;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
public class BukkitGrimEntity implements GrimEntity {
private final Entity entity;
private BukkitPlatformWorld bukkitPlatformWorld;
public BukkitGrimEntity(Entity entity) {
Objects.requireNonNull(entity);
this.entity = entity;
}
public Entity getBukkitEntity() {
return this.entity;
}
@Override
public UUID getUniqueId() {
return entity.getUniqueId();
}
@Override
public boolean eject() {
return entity.eject();
}
@Override
public CompletableFuture<Boolean> teleportAsync(Location location) {
org.bukkit.Location bLoc = BukkitConversionUtils.toBukkitLocation(location);
return PaperUtils.teleportAsync(this.entity, bLoc);
}
@Override
@NonNull
public Entity getNative() {
return entity;
}
@Override
public boolean isDead() {
return this.entity.isDead();
}
// TODO replace with PlayerWorldChangeEvent listener instead of checking for equality for better performance
@Override
public PlatformWorld getWorld() {
if (bukkitPlatformWorld == null || !bukkitPlatformWorld.getBukkitWorld().equals(entity.getWorld())) {
bukkitPlatformWorld = new BukkitPlatformWorld(entity.getWorld());
}
return bukkitPlatformWorld;
}
@Override
public Location getLocation() {
org.bukkit.Location location = this.entity.getLocation();
return new Location(
this.getWorld(),
location.getX(),
location.getY(),
location.getZ(),
location.getYaw(),
location.getPitch()
);
}
}
@@ -1,16 +0,0 @@
package ac.grim.grimac.platform.bukkit.initables;
import ac.grim.grimac.api.platform.init.StartableInitable;
import ac.grim.grimac.platform.bukkit.GrimACBukkitLoaderPlugin;
import io.github.retrooper.packetevents.bstats.bukkit.Metrics;
public class BukkitBStats implements StartableInitable {
@Override
public void start() {
int pluginId = 12820; // <-- Replace with the id of your plugin!
try {
new Metrics(GrimACBukkitLoaderPlugin.LOADER, pluginId);
} catch (Exception ignored) {
}
}
}
@@ -1,15 +0,0 @@
package ac.grim.grimac.platform.bukkit.initables;
import ac.grim.grimac.api.platform.init.StartableInitable;
import ac.grim.grimac.api.util.LogUtil;
import ac.grim.grimac.platform.bukkit.GrimACBukkitLoaderPlugin;
import ac.grim.grimac.platform.bukkit.events.PistonEvent;
import org.bukkit.Bukkit;
public class BukkitEventManager implements StartableInitable {
public void start() {
LogUtil.info("Registering singular bukkit event... (PistonEvent)");
Bukkit.getPluginManager().registerEvents(new PistonEvent(), GrimACBukkitLoaderPlugin.LOADER);
}
}
@@ -1,93 +0,0 @@
package ac.grim.grimac.platform.bukkit.initables;
import ac.grim.grimac.api.GrimAPIProvider;
import ac.grim.grimac.api.GrimUser;
import ac.grim.grimac.api.packet.protocol.version.server.ServerVersions;
import ac.grim.grimac.api.platform.Platform;
import ac.grim.grimac.api.platform.init.AbstractTickEndEvent;
import ac.grim.grimac.api.util.LogUtil;
import ac.grim.grimac.platform.bukkit.player.BukkitPlatformPlayer;
import ac.grim.grimac.platform.bukkit.utils.list.HookedListWrapper;
import ac.grim.grimac.platform.bukkit.utils.reflection.PaperUtils;
import com.github.retrooper.packetevents.util.reflection.Reflection;
import io.github.retrooper.packetevents.util.SpigotReflectionUtil;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.Listener;
import sun.misc.Unsafe;
import java.lang.reflect.Field;
import java.util.Collections;
import java.util.List;
// Copied from: https://github.com/ThomasOM/Pledge/blob/master/src/main/java/dev/thomazz/pledge/inject/ServerInjector.java
@SuppressWarnings(value = {"unchecked", "deprecated"})
public class BukkitTickEndEvent extends AbstractTickEndEvent implements Listener {
@Override
public void start() {
if (!super.shouldInjectEndTick()) {
return;
}
if (ServerVersions.getServerVersion().isNewerThan(ServerVersions.V_1_11_2) && !Boolean.getBoolean("paper.explicit-flush")) {
LogUtil.warn("Reach.enable-post-packet=true but paper.explicit-flush=false, add \"-Dpaper.explicit-flush=true\" to your server's startup flags for fully functional extra reach accuracy.");
}
// this is necessary for folia
if (GrimAPIProvider.getDirect().getPlatform() == Platform.FOLIA) {
PaperUtils.registerTickEndEvent(this, this::tickAllFoliaPlayers);
return;
}
// if it fails to inject, try to use paper's event
if (!injectWithReflection() && !PaperUtils.registerTickEndEvent(this, this::tickAllPlayers)) {
LogUtil.error("Failed to inject into the end of tick event!");
}
}
private void tickAllPlayers() {
for (GrimUser player : GrimAPIProvider.getDirect().getGrimUsers()) {
if (player.isGrimDisabled()) continue;
player.onEndOfTick();
}
}
private void tickAllFoliaPlayers() {
for (GrimUser player : GrimAPIProvider.getDirect().getGrimUsers()) {
if (player.isGrimDisabled()) continue;
if (player.getPlatformPlayer() == null) continue;
Player p = ((BukkitPlatformPlayer) player.getPlatformPlayer()).getNative();
if (!Bukkit.isOwnedByCurrentRegion(p)) continue;
player.onEndOfTick();
}
}
private boolean injectWithReflection() {
// Inject so we can add the final transaction pre-flush event
try {
Object connection = SpigotReflectionUtil.getMinecraftServerConnectionInstance();
Field connectionsList = Reflection.getField(connection.getClass(), List.class, 1);
List<Object> endOfTickObject = (List<Object>) connectionsList.get(connection);
// Use a list wrapper to check when the size method is called
// Unsure why synchronized is needed because the object itself gets synchronized
// but whatever. At least plugins can't break it, I guess.
//
// Pledge injects into another list, so we should be safe injecting into this one
List<?> wrapper = Collections.synchronizedList(new HookedListWrapper<>(endOfTickObject) {
@Override
public void onIterator() {
tickAllPlayers();
}
});
Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
unsafeField.setAccessible(true);
Unsafe unsafe = (Unsafe) unsafeField.get(null);
unsafe.putObject(connection, unsafe.objectFieldOffset(connectionsList), wrapper);
} catch (NoSuchFieldException | IllegalAccessException e) {
LogUtil.exception("Failed to inject into the end of tick event via reflection", e);
return false;
}
return true;
}
}
@@ -1,96 +0,0 @@
package ac.grim.grimac.platform.bukkit.manager;
import ac.grim.grimac.api.packet.protocol.version.server.PacketServerVersion;
import ac.grim.grimac.api.packet.protocol.version.server.ServerVersions;
import ac.grim.grimac.api.platform.manager.ItemResetHandler;
import ac.grim.grimac.api.platform.player.PlatformPlayer;
import ac.grim.grimac.platform.bukkit.player.BukkitPlatformPlayer;
import ac.grim.grimac.platform.bukkit.utils.reflection.PaperUtils;
import lombok.SneakyThrows;
import org.bukkit.Bukkit;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.lang.reflect.Method;
public class BukkitItemResetHandler implements ItemResetHandler {
// resets item usage, then returns whether the player was using an item
private final @NotNull ItemUsageReset resetItemUsage = createItemUsageResetFunction();
@SneakyThrows
public void resetItemUsage(@Nullable PlatformPlayer player) {
if (player != null) {
resetItemUsage.accept(((BukkitPlatformPlayer) player).getNative());
}
}
@SneakyThrows
private @NotNull ItemUsageReset createItemUsageResetFunction() {
PacketServerVersion version = ServerVersions.getServerVersion();
if (version.isNewerThan(ServerVersions.V_1_17) && PaperUtils.PAPER) {
if (version.isOlderThan(ServerVersions.V_1_19)) {
return LivingEntity::clearActiveItem;
}
Method setLivingEntityFlag = Class.forName(version.isOlderThan(ServerVersions.V_1_20_5) ? "net.minecraft.world.entity.EntityLiving" : "net.minecraft.world.entity.LivingEntity")
.getDeclaredMethod(version.isOlderThan(ServerVersions.V_1_20_5) ? "c" : "setLivingEntityFlag", int.class, boolean.class);
Method getHandle = (version.isOlderThan(ServerVersions.V_1_20_5)
? Class.forName("org.bukkit.craftbukkit." + Bukkit.getServer().getClass().getPackageName().split("\\.")[3] + ".entity.CraftPlayer")
: Class.forName("org.bukkit.craftbukkit.entity.CraftPlayer")
).getMethod("getHandle");
setLivingEntityFlag.setAccessible(true);
return player -> {
// don't trigger gameevents
setLivingEntityFlag.invoke(getHandle.invoke(player), 1, false);
player.clearActiveItem();
};
}
if (version == ServerVersions.V_1_8_8) {
Class<?> EntityHuman = Class.forName("net.minecraft.server.v1_8_R3.EntityHuman");
Method getHandle = Class.forName("org.bukkit.craftbukkit.v1_8_R3.entity.CraftPlayer").getMethod("getHandle");
Method clearActiveItem = EntityHuman.getMethod("bV");
Method isUsingItem = EntityHuman.getMethod("bS");
return player -> {
Object handle = getHandle.invoke(player);
clearActiveItem.invoke(handle);
// in 1.8 we need to resync item usage manually,
// only do so if the player was using an item
if ((boolean) isUsingItem.invoke(handle)) {
player.updateInventory();
}
};
}
String nmsPackage = Bukkit.getServer().getClass().getPackageName().split("\\.")[3];
String livingEntityPackage = version.isNewerThan(ServerVersions.V_1_16_5) ? "net.minecraft.world.entity.EntityLiving" : "net.minecraft.server." + nmsPackage + ".EntityLiving";
Method getHandle = Class.forName("org.bukkit.craftbukkit." + nmsPackage + ".entity.CraftPlayer").getMethod("getHandle");
Method clearActiveItem = Class.forName(livingEntityPackage).getMethod(
switch (nmsPackage) {
case "v1_9_R1" -> "cz";
case "v1_9_R2" -> "cA";
case "v1_10_R1" -> "cE";
case "v1_11_R1" -> "cF";
case "v1_12_R1" -> "cN";
case "v1_13_R1", "v1_13_R2" -> "da";
case "v1_14_R1" -> "dp";
case "v1_15_R1" -> "dH";
case "v1_16_R1", "v1_16_R2", "v1_16_R3", "v1_17_R1" -> "clearActiveItem";
case "v1_18_R1" -> "eR";
case "v1_18_R2" -> "eS";
default -> throw new IllegalStateException("You are using an unsupported server version! (" + version.getReleaseName() + ")");
}
);
return player -> clearActiveItem.invoke(getHandle.invoke(player));
}
private interface ItemUsageReset {
void accept(@NotNull Player player) throws Throwable;
}
}
@@ -1,19 +0,0 @@
package ac.grim.grimac.platform.bukkit.manager;
import ac.grim.grimac.api.platform.manager.MessagePlaceHolderManager;
import ac.grim.grimac.api.platform.player.PlatformPlayer;
import ac.grim.grimac.platform.bukkit.player.BukkitPlatformPlayer;
import com.github.retrooper.packetevents.util.reflection.Reflection;
import me.clip.placeholderapi.PlaceholderAPI;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
public class BukkitMessagePlaceHolderManager implements MessagePlaceHolderManager {
public static final boolean hasPlaceholderAPI = Reflection.getClassByNameWithoutException("me.clip.placeholderapi.PlaceholderAPI") != null;
@Override
public @NonNull String replacePlaceholders(@Nullable PlatformPlayer player, @NonNull String string) {
if (!hasPlaceholderAPI) return string;
return PlaceholderAPI.setPlaceholders(player instanceof BukkitPlatformPlayer bukkitPlatformPlayer ? bukkitPlatformPlayer.getBukkitPlayer() : null, string);
}
}
@@ -1,17 +0,0 @@
package ac.grim.grimac.platform.bukkit.manager;
import ac.grim.grimac.api.platform.command.PlayerSelector;
import ac.grim.grimac.api.platform.manager.ParserDescriptorFactory;
import ac.grim.grimac.api.platform.sender.Sender;
import ac.grim.grimac.platform.bukkit.command.BukkitPlayerSelectorParser;
import org.incendo.cloud.parser.ParserDescriptor;
public class BukkitParserDescriptorFactory implements ParserDescriptorFactory {
BukkitPlayerSelectorParser<Sender> bukkitPlayerSelectorParser = new BukkitPlayerSelectorParser<>();
@Override
public ParserDescriptor<Sender, PlayerSelector> getSinglePlayer() {
return bukkitPlayerSelectorParser.descriptor();
}
}
@@ -1,33 +0,0 @@
package ac.grim.grimac.platform.bukkit.manager;
import ac.grim.grimac.api.platform.manager.PermissionRegistrationManager;
import ac.grim.grimac.api.platform.permissions.PermissionDefaultValue;
import ac.grim.grimac.platform.bukkit.utils.convert.BukkitConversionUtils;
import org.bukkit.Bukkit;
import org.bukkit.permissions.Permission;
public class BukkitPermissionRegistrationManager implements PermissionRegistrationManager {
/**
* Registers a permission with the specified default value on Bukkit.
* This method is only called for dynamic permissions (e.g., check-specific permissions
* like "grim.exempt.checkname") that are generated at runtime. Most other static permissions
* (e.g., "grim.exempt", "grim.alerts.enable-on-join") are registered with their defaults
* in the `plugin.yml` file, which is defined in the Bukkit Gradle build script.
*
* <p>Dynamic permissions are registered here to ensure they are available for autocomplete
* and permission checks immediately on startup. If the permission already exists, its default
* value is updated to match the specified value.</p>
*
* @param name The permission node to register (e.g., "grim.exempt.checkname").
* @param defaultValue The default value for the permission.
*/
@Override
public void registerPermission(String name, PermissionDefaultValue defaultValue) {
final Permission bukkitPermission = Bukkit.getPluginManager().getPermission(name);
if (bukkitPermission == null) {
Bukkit.getPluginManager().addPermission(new Permission(name, BukkitConversionUtils.toBukkitPermissionDefault(defaultValue)));
} else {
bukkitPermission.setDefault(BukkitConversionUtils.toBukkitPermissionDefault(defaultValue));
}
}
}
@@ -1,29 +0,0 @@
package ac.grim.grimac.platform.bukkit.manager;
import ac.grim.grimac.api.platform.PlatformPlugin;
import ac.grim.grimac.api.platform.manager.PlatformPluginManager;
import ac.grim.grimac.platform.bukkit.BukkitPlatformPlugin;
import org.bukkit.Bukkit;
import org.bukkit.plugin.Plugin;
import org.checkerframework.checker.nullness.qual.Nullable;
public class BukkitPlatformPluginManager implements PlatformPluginManager {
@Override
public PlatformPlugin[] getPlugins() {
Plugin[] bukkitPlugins = Bukkit.getPluginManager().getPlugins();
PlatformPlugin[] plugins = new PlatformPlugin[bukkitPlugins.length];
for (int i = 0; i < bukkitPlugins.length; i++) {
plugins[i] = new BukkitPlatformPlugin(bukkitPlugins[i]);
}
return plugins;
}
@Override
public @Nullable PlatformPlugin getPlugin(String pluginName) {
Plugin bukkitPlugin = Bukkit.getPluginManager().getPlugin(pluginName);
return bukkitPlugin == null ? null : new BukkitPlatformPlugin(bukkitPlugin);
}
}
@@ -1,66 +0,0 @@
package ac.grim.grimac.platform.bukkit.player;
import ac.grim.grimac.api.packet.item.PacketItemStack;
import ac.grim.grimac.api.platform.player.PlatformPlayerInventory;
import io.github.retrooper.packetevents.util.SpigotConversionUtil;
import org.bukkit.entity.Player;
public class BukkitPlatformInventory implements PlatformPlayerInventory {
private final Player bukkitPlayer;
public BukkitPlatformInventory(Player bukkitPlayer) {
this.bukkitPlayer = bukkitPlayer;
}
@Override
public PacketItemStack getItemInHand() {
return SpigotConversionUtil.fromBukkitItemStack(bukkitPlayer.getInventory().getItemInHand());
}
@Override
public PacketItemStack getItemInOffHand() {
return SpigotConversionUtil.fromBukkitItemStack(bukkitPlayer.getInventory().getItemInOffHand());
}
@Override
public PacketItemStack getStack(int bukkitSlot, int vanillaSlot) {
return SpigotConversionUtil.fromBukkitItemStack(bukkitPlayer.getInventory().getItem(bukkitSlot));
}
@Override
public PacketItemStack getHelmet() {
return SpigotConversionUtil.fromBukkitItemStack(bukkitPlayer.getInventory().getHelmet());
}
@Override
public PacketItemStack getChestplate() {
return SpigotConversionUtil.fromBukkitItemStack(bukkitPlayer.getInventory().getChestplate());
}
@Override
public PacketItemStack getLeggings() {
return SpigotConversionUtil.fromBukkitItemStack(bukkitPlayer.getInventory().getLeggings());
}
@Override
public PacketItemStack getBoots() {
return SpigotConversionUtil.fromBukkitItemStack(bukkitPlayer.getInventory().getBoots());
}
@Override
public PacketItemStack[] getContents() {
org.bukkit.inventory.ItemStack[] bukkitItems = bukkitPlayer.getInventory().getContents();
PacketItemStack[] items = new PacketItemStack[bukkitItems.length];
for (int i = 0; i < bukkitItems.length; i++) {
if (bukkitItems[i] == null) continue;
items[i] = SpigotConversionUtil.fromBukkitItemStack(bukkitItems[i]);
}
return items;
}
@Override
public String getOpenInventoryKey() {
return bukkitPlayer.getOpenInventory().getType().toString();
}
}
@@ -1,157 +0,0 @@
package ac.grim.grimac.platform.bukkit.player;
import ac.grim.grimac.api.math.Vector3dm;
import ac.grim.grimac.api.platform.entity.GrimEntity;
import ac.grim.grimac.api.platform.player.PlatformPlayer;
import ac.grim.grimac.api.platform.player.PlatformPlayerInventory;
import ac.grim.grimac.api.platform.sender.Sender;
import ac.grim.grimac.platform.bukkit.GrimACBukkitLoaderPlugin;
import ac.grim.grimac.platform.bukkit.entity.BukkitGrimEntity;
import ac.grim.grimac.platform.bukkit.utils.anticheat.MultiLibUtil;
import ac.grim.grimac.platform.bukkit.utils.convert.BukkitConversionUtils;
import ac.grim.grimac.platform.bukkit.utils.reflection.PaperUtils;
import ac.grim.grimac.api.math.Location;
import lombok.Getter;
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
import net.kyori.adventure.text.Component;
import org.bukkit.GameMode;
import org.bukkit.World;
import org.bukkit.entity.Player;
import org.bukkit.permissions.Permission;
import org.bukkit.permissions.PermissionDefault;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
public class BukkitPlatformPlayer extends BukkitGrimEntity implements PlatformPlayer {
private static final BukkitAudiences audiences = BukkitAudiences.create(GrimACBukkitLoaderPlugin.LOADER);
@Getter
private final Player bukkitPlayer;
private final PlatformPlayerInventory inventory;
public BukkitPlatformPlayer(Player bukkitPlayer) {
super(bukkitPlayer);
this.bukkitPlayer = bukkitPlayer;
this.inventory = new BukkitPlatformInventory(bukkitPlayer);
}
@Override
public void kickPlayer(String textReason) {
bukkitPlayer.kickPlayer(textReason);
}
@Override
public boolean hasPermission(String s) {
return bukkitPlayer.hasPermission(s);
}
@Override
public boolean hasPermission(String s, boolean defaultIfUnset) {
return this.bukkitPlayer.hasPermission(new Permission(s, defaultIfUnset ? PermissionDefault.TRUE : PermissionDefault.FALSE));
}
@Override
public boolean isSneaking() {
return bukkitPlayer.isSneaking();
}
@Override
public void setSneaking(boolean isSneaking) {
bukkitPlayer.setSneaking(isSneaking);
}
@Override
public void sendMessage(String message) {
bukkitPlayer.sendMessage(message);
}
@Override
public void sendMessage(Component message) {
audiences.player(bukkitPlayer).sendMessage(message);
}
@Override
public boolean isOnline() {
return bukkitPlayer.isOnline();
}
@Override
public String getName() {
return bukkitPlayer.getName();
}
@Override
public void updateInventory() {
bukkitPlayer.updateInventory();
}
@Override
public Vector3dm getPosition() {
org.bukkit.Location location = this.bukkitPlayer.getLocation();
return new Vector3dm(location.getX(), location.getY(), location.getZ());
}
@Override
public PlatformPlayerInventory getInventory() {
return inventory;
}
@Override
public @Nullable GrimEntity getVehicle() {
return bukkitPlayer.getVehicle() == null ? null : new BukkitGrimEntity(bukkitPlayer.getVehicle());
}
@Override
public int getGameModeID() {
return bukkitPlayer.getGameMode().getValue();
}
@Override
public void setGameMode(int gameModeID) {
bukkitPlayer.setGameMode(GameMode.getByValue(gameModeID));
}
public World getBukkitWorld() {
return bukkitPlayer.getWorld();
}
@Override
public UUID getUniqueId() {
return bukkitPlayer.getUniqueId();
}
@Override
public boolean eject() {
return bukkitPlayer.eject();
}
@Override
public CompletableFuture<Boolean> teleportAsync(Location location) {
org.bukkit.Location bLoc = BukkitConversionUtils.toBukkitLocation(location);
return PaperUtils.teleportAsync(this.bukkitPlayer, bLoc);
}
@Override
public boolean isExternalPlayer() {
return MultiLibUtil.isExternalPlayer(this.bukkitPlayer);
}
@Override
public void sendPluginMessage(String channelName, byte[] byteArray) {
this.bukkitPlayer.sendPluginMessage(GrimACBukkitLoaderPlugin.LOADER, channelName, byteArray);
}
@Override
public Sender getSender() {
return GrimACBukkitLoaderPlugin.LOADER.getBukkitSenderFactory().map(this.bukkitPlayer);
}
@Override
@NonNull
public Player getNative() {
return this.bukkitPlayer;
}
}
@@ -1,47 +0,0 @@
package ac.grim.grimac.platform.bukkit.player;
import ac.grim.grimac.api.platform.player.AbstractPlatformPlayerFactory;
import ac.grim.grimac.api.platform.player.PlatformPlayer;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
import java.util.UUID;
public class BukkitPlatformPlayerFactory extends AbstractPlatformPlayerFactory<Player> {
@Override
protected Player getNativePlayer(@NotNull UUID uuid) {
return Bukkit.getPlayer(uuid);
}
@Override
protected PlatformPlayer createPlatformPlayer(@NotNull Player nativePlayer) {
return new BukkitPlatformPlayer(nativePlayer);
}
@Override
protected boolean isNativePlayerType(@NotNull Object playerObject) {
return playerObject instanceof Player;
}
@Override
protected UUID getPlayerUUID(@NotNull Player nativePlayer) {
return nativePlayer.getUniqueId();
}
@Override
protected Class<Player> getNativePlayerClass() {
return Player.class;
}
// The cast is safe because Bukkit.getOnlinePlayers() is guaranteed to contain Player or its subtypes,
// and we're only reading from it.
@Override
@SuppressWarnings("unchecked")
protected Collection<Player> getNativeOnlinePlayers() {
// Cast Collection<? extends Player> to Collection<Player>
return (Collection<Player>) Bukkit.getOnlinePlayers();
}
}
@@ -1,54 +0,0 @@
package ac.grim.grimac.platform.bukkit.scheduler.bukkit;
import ac.grim.grimac.api.plugin.GrimPlugin;
import ac.grim.grimac.api.platform.scheduler.AsyncScheduler;
import ac.grim.grimac.api.platform.scheduler.PlatformScheduler;
import ac.grim.grimac.api.platform.scheduler.TaskHandle;
import ac.grim.grimac.platform.bukkit.GrimACBukkitLoaderPlugin;
import org.bukkit.Bukkit;
import org.bukkit.scheduler.BukkitScheduler;
import org.jetbrains.annotations.NotNull;
import java.util.concurrent.TimeUnit;
public class BukkitAsyncScheduler implements AsyncScheduler {
private final BukkitScheduler bukkitScheduler = Bukkit.getScheduler();
@Override
public TaskHandle runNow(@NotNull GrimPlugin plugin, @NotNull Runnable task) {
return new BukkitTaskHandle(bukkitScheduler.runTaskAsynchronously(GrimACBukkitLoaderPlugin.LOADER, task));
}
@Override
public TaskHandle runDelayed(@NotNull GrimPlugin plugin, @NotNull Runnable task, long delay, @NotNull TimeUnit timeUnit) {
return new BukkitTaskHandle(bukkitScheduler.runTaskLaterAsynchronously(GrimACBukkitLoaderPlugin.LOADER,
task,
PlatformScheduler.convertTimeToTicks(delay, timeUnit))
);
}
@Override
public TaskHandle runAtFixedRate(@NotNull GrimPlugin plugin, @NotNull Runnable task, long delay, long period, @NotNull TimeUnit timeUnit) {
return new BukkitTaskHandle(
bukkitScheduler.runTaskTimerAsynchronously(
GrimACBukkitLoaderPlugin.LOADER,
task,
PlatformScheduler.convertTimeToTicks(delay, timeUnit),
PlatformScheduler.convertTimeToTicks(period, timeUnit)
)
);
}
@Override
public TaskHandle runAtFixedRate(@NotNull GrimPlugin plugin, @NotNull Runnable task, long initialDelayTicks, long periodTicks) {
return new BukkitTaskHandle(
bukkitScheduler.runTaskTimerAsynchronously(GrimACBukkitLoaderPlugin.LOADER, task, initialDelayTicks, periodTicks)
);
}
@Override
public void cancel(@NotNull GrimPlugin plugin) {
bukkitScheduler.cancelTasks(GrimACBukkitLoaderPlugin.LOADER);
}
}
@@ -1,36 +0,0 @@
package ac.grim.grimac.platform.bukkit.scheduler.bukkit;
import ac.grim.grimac.api.plugin.GrimPlugin;
import ac.grim.grimac.api.platform.entity.GrimEntity;
import ac.grim.grimac.api.platform.scheduler.EntityScheduler;
import ac.grim.grimac.api.platform.scheduler.TaskHandle;
import ac.grim.grimac.platform.bukkit.GrimACBukkitLoaderPlugin;
import org.bukkit.Bukkit;
import org.bukkit.scheduler.BukkitScheduler;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class BukkitEntityScheduler implements EntityScheduler {
private final BukkitScheduler bukkitScheduler = Bukkit.getScheduler();
@Override
public void execute(@NotNull GrimEntity entity, @NotNull GrimPlugin plugin, @NotNull Runnable run, @Nullable Runnable retired, long delay) {
bukkitScheduler.runTaskLater(GrimACBukkitLoaderPlugin.LOADER, run, delay);
}
@Override
public TaskHandle run(@NotNull GrimEntity entity, @NotNull GrimPlugin plugin, @NotNull Runnable task, @Nullable Runnable retired) {
return new BukkitTaskHandle(bukkitScheduler.runTask(GrimACBukkitLoaderPlugin.LOADER, task));
}
@Override
public TaskHandle runDelayed(@NotNull GrimEntity entity, @NotNull GrimPlugin plugin, @NotNull Runnable task, @Nullable Runnable retired, long delayTicks) {
return new BukkitTaskHandle(bukkitScheduler.runTaskLater(GrimACBukkitLoaderPlugin.LOADER, task, delayTicks));
}
@Override
public TaskHandle runAtFixedRate(@NotNull GrimEntity entity, @NotNull GrimPlugin plugin, @NotNull Runnable task, @Nullable Runnable retired, long initialDelayTicks, long periodTicks) {
return new BukkitTaskHandle(bukkitScheduler.runTaskTimer(GrimACBukkitLoaderPlugin.LOADER, task, initialDelayTicks, periodTicks));
}
}
@@ -1,39 +0,0 @@
package ac.grim.grimac.platform.bukkit.scheduler.bukkit;
import ac.grim.grimac.api.plugin.GrimPlugin;
import ac.grim.grimac.api.platform.scheduler.GlobalRegionScheduler;
import ac.grim.grimac.api.platform.scheduler.TaskHandle;
import ac.grim.grimac.platform.bukkit.GrimACBukkitLoaderPlugin;
import org.bukkit.Bukkit;
import org.bukkit.scheduler.BukkitScheduler;
import org.jetbrains.annotations.NotNull;
public class BukkitGlobalRegionScheduler implements GlobalRegionScheduler {
private final BukkitScheduler bukkitScheduler = Bukkit.getScheduler();
@Override
public void execute(@NotNull GrimPlugin plugin, @NotNull Runnable run) {
bukkitScheduler.runTask(GrimACBukkitLoaderPlugin.LOADER, run);
}
@Override
public TaskHandle run(@NotNull GrimPlugin plugin, @NotNull Runnable task) {
return new BukkitTaskHandle(bukkitScheduler.runTask(GrimACBukkitLoaderPlugin.LOADER, task));
}
@Override
public TaskHandle runDelayed(@NotNull GrimPlugin plugin, @NotNull Runnable task, long delay) {
return new BukkitTaskHandle(bukkitScheduler.runTaskLater(GrimACBukkitLoaderPlugin.LOADER, task, delay));
}
@Override
public TaskHandle runAtFixedRate(@NotNull GrimPlugin plugin, @NotNull Runnable task, long initialDelayTicks, long periodTicks) {
return new BukkitTaskHandle(bukkitScheduler.runTaskTimer(GrimACBukkitLoaderPlugin.LOADER, task, initialDelayTicks, periodTicks));
}
@Override
public void cancel(@NotNull GrimPlugin plugin) {
bukkitScheduler.cancelTasks(GrimACBukkitLoaderPlugin.LOADER);
}
}
@@ -1,36 +0,0 @@
package ac.grim.grimac.platform.bukkit.scheduler.bukkit;
import ac.grim.grimac.api.platform.scheduler.AsyncScheduler;
import ac.grim.grimac.api.platform.scheduler.EntityScheduler;
import ac.grim.grimac.api.platform.scheduler.GlobalRegionScheduler;
import ac.grim.grimac.api.platform.scheduler.PlatformScheduler;
import ac.grim.grimac.api.platform.scheduler.RegionScheduler;
import org.checkerframework.checker.nullness.qual.NonNull;
public class BukkitPlatformScheduler implements PlatformScheduler {
private final BukkitAsyncScheduler bukkitAsyncScheduler = new BukkitAsyncScheduler();
private final BukkitGlobalRegionScheduler bukkitGlobalRegionScheduler = new BukkitGlobalRegionScheduler();
private final BukkitEntityScheduler bukkitEntityScheduler = new BukkitEntityScheduler();
private final BukkitRegionScheduler bukkitRegionScheduler = new BukkitRegionScheduler();
@Override
public @NonNull AsyncScheduler getAsyncScheduler() {
return bukkitAsyncScheduler;
}
@Override
public @NonNull GlobalRegionScheduler getGlobalRegionScheduler() {
return bukkitGlobalRegionScheduler;
}
@Override
public @NonNull EntityScheduler getEntityScheduler() {
return bukkitEntityScheduler;
}
@Override
public @NonNull RegionScheduler getRegionScheduler() {
return bukkitRegionScheduler;
}
}
@@ -1,56 +0,0 @@
package ac.grim.grimac.platform.bukkit.scheduler.bukkit;
import ac.grim.grimac.api.plugin.GrimPlugin;
import ac.grim.grimac.api.platform.scheduler.RegionScheduler;
import ac.grim.grimac.api.platform.scheduler.TaskHandle;
import ac.grim.grimac.api.platform.world.PlatformWorld;
import ac.grim.grimac.platform.bukkit.GrimACBukkitLoaderPlugin;
import ac.grim.grimac.api.math.Location;
import org.bukkit.Bukkit;
import org.bukkit.scheduler.BukkitScheduler;
import org.jetbrains.annotations.NotNull;
public class BukkitRegionScheduler implements RegionScheduler {
private final BukkitScheduler bukkitScheduler = Bukkit.getScheduler();
@Override
public void execute(@NotNull GrimPlugin plugin, @NotNull PlatformWorld world, int chunkX, int chunkZ, @NotNull Runnable run) {
bukkitScheduler.runTask(GrimACBukkitLoaderPlugin.LOADER, run);
}
@Override
public void execute(@NotNull GrimPlugin plugin, @NotNull Location location, @NotNull Runnable run) {
bukkitScheduler.runTask(GrimACBukkitLoaderPlugin.LOADER, run);
}
@Override
public TaskHandle run(@NotNull GrimPlugin plugin, @NotNull PlatformWorld world, int chunkX, int chunkZ, @NotNull Runnable task) {
return new BukkitTaskHandle(bukkitScheduler.runTask(GrimACBukkitLoaderPlugin.LOADER, task));
}
@Override
public TaskHandle run(@NotNull GrimPlugin plugin, @NotNull Location location, @NotNull Runnable task) {
return new BukkitTaskHandle(bukkitScheduler.runTask(GrimACBukkitLoaderPlugin.LOADER, task));
}
@Override
public TaskHandle runDelayed(@NotNull GrimPlugin plugin, @NotNull PlatformWorld world, int chunkX, int chunkZ, @NotNull Runnable task, long delayTicks) {
return new BukkitTaskHandle(bukkitScheduler.runTaskLater(GrimACBukkitLoaderPlugin.LOADER, task, delayTicks));
}
@Override
public TaskHandle runDelayed(@NotNull GrimPlugin plugin, @NotNull Location location, @NotNull Runnable task, long delayTicks) {
return new BukkitTaskHandle(bukkitScheduler.runTaskLater(GrimACBukkitLoaderPlugin.LOADER, task, delayTicks));
}
@Override
public TaskHandle runAtFixedRate(@NotNull GrimPlugin plugin, @NotNull PlatformWorld world, int chunkX, int chunkZ, @NotNull Runnable task, long initialDelayTicks, long periodTicks) {
return new BukkitTaskHandle(bukkitScheduler.runTaskTimer(GrimACBukkitLoaderPlugin.LOADER, task, initialDelayTicks, periodTicks));
}
@Override
public TaskHandle runAtFixedRate(@NotNull GrimPlugin plugin, @NotNull Location location, @NotNull Runnable task, long initialDelayTicks, long periodTicks) {
return new BukkitTaskHandle(bukkitScheduler.runTaskTimer(GrimACBukkitLoaderPlugin.LOADER, task, initialDelayTicks, periodTicks));
}
}
@@ -1,28 +0,0 @@
package ac.grim.grimac.platform.bukkit.scheduler.bukkit;
import ac.grim.grimac.api.platform.scheduler.TaskHandle;
import org.bukkit.scheduler.BukkitTask;
public class BukkitTaskHandle implements TaskHandle {
private final BukkitTask bukkitTask;
public BukkitTaskHandle(BukkitTask bukkitTask) {
this.bukkitTask = bukkitTask;
}
@Override
public boolean isSync() {
return bukkitTask.isSync();
}
@Override
public boolean isCancelled() {
return bukkitTask.isCancelled();
}
@Override
public void cancel() {
bukkitTask.cancel();
}
}
@@ -1,63 +0,0 @@
package ac.grim.grimac.platform.bukkit.scheduler.folia;
import ac.grim.grimac.api.plugin.GrimPlugin;
import ac.grim.grimac.api.platform.scheduler.AsyncScheduler;
import ac.grim.grimac.api.platform.scheduler.TaskHandle;
import ac.grim.grimac.platform.bukkit.GrimACBukkitLoaderPlugin;
import org.bukkit.Bukkit;
import org.jetbrains.annotations.NotNull;
import java.util.concurrent.TimeUnit;
public class FoliaAsyncScheduler implements AsyncScheduler {
private final io.papermc.paper.threadedregions.scheduler.AsyncScheduler asyncScheduler = Bukkit.getAsyncScheduler();
@Override
public TaskHandle runNow(@NotNull GrimPlugin plugin, @NotNull Runnable task) {
return new FoliaTaskHandle(asyncScheduler.runNow(GrimACBukkitLoaderPlugin.LOADER, (ignored) -> task.run()));
}
@Override
public TaskHandle runDelayed(@NotNull GrimPlugin plugin, @NotNull Runnable task, long delay, @NotNull TimeUnit timeUnit) {
return new FoliaTaskHandle(
asyncScheduler.runDelayed(
GrimACBukkitLoaderPlugin.LOADER,
(ignored) -> task.run(),
delay,
timeUnit
)
);
}
@Override
public TaskHandle runAtFixedRate(@NotNull GrimPlugin plugin, @NotNull Runnable task, long delay, long period, @NotNull TimeUnit timeUnit) {
return new FoliaTaskHandle(
asyncScheduler.runAtFixedRate(
GrimACBukkitLoaderPlugin.LOADER,
(ignored) -> task.run(),
delay,
period,
timeUnit
)
);
}
@Override
public TaskHandle runAtFixedRate(@NotNull GrimPlugin plugin, @NotNull Runnable task, long initialDelayTicks, long periodTicks) {
return new FoliaTaskHandle(
asyncScheduler.runAtFixedRate(
GrimACBukkitLoaderPlugin.LOADER,
(ignored) -> task.run(),
initialDelayTicks * 50,
periodTicks * 50,
TimeUnit.MILLISECONDS
)
);
}
@Override
public void cancel(@NotNull GrimPlugin plugin) {
asyncScheduler.cancelTasks(GrimACBukkitLoaderPlugin.LOADER);
}
}
@@ -1,35 +0,0 @@
package ac.grim.grimac.platform.bukkit.scheduler.folia;
import ac.grim.grimac.api.plugin.GrimPlugin;
import ac.grim.grimac.api.platform.entity.GrimEntity;
import ac.grim.grimac.api.platform.scheduler.EntityScheduler;
import ac.grim.grimac.api.platform.scheduler.TaskHandle;
import ac.grim.grimac.platform.bukkit.GrimACBukkitLoaderPlugin;
import ac.grim.grimac.platform.bukkit.entity.BukkitGrimEntity;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class FoliaEntityScheduler implements EntityScheduler {
@Override
public void execute(@NotNull GrimEntity entity, @NotNull GrimPlugin plugin, @NotNull Runnable run, @Nullable Runnable retired, long delay) {
((BukkitGrimEntity) entity).getBukkitEntity().getScheduler().execute(GrimACBukkitLoaderPlugin.LOADER, run, retired, delay);
}
@Override
public TaskHandle run(@NotNull GrimEntity entity, @NotNull GrimPlugin plugin, @NotNull Runnable task, @Nullable Runnable retired) {
return new FoliaTaskHandle(((BukkitGrimEntity) entity).getBukkitEntity().getScheduler().run(GrimACBukkitLoaderPlugin.LOADER, (ignored) -> task.run(), retired));
}
@Override
public TaskHandle runDelayed(@NotNull GrimEntity entity, @NotNull GrimPlugin plugin, @NotNull Runnable task, @Nullable Runnable retired, long delayTicks) {
return new FoliaTaskHandle(
((BukkitGrimEntity) entity).getBukkitEntity().getScheduler().runDelayed(GrimACBukkitLoaderPlugin.LOADER, (ignored) -> task.run(), retired, delayTicks)
);
}
@Override
public TaskHandle runAtFixedRate(@NotNull GrimEntity entity, @NotNull GrimPlugin plugin, @NotNull Runnable task, @Nullable Runnable retired, long initialDelayTicks, long periodTicks) {
return new FoliaTaskHandle(((BukkitGrimEntity) entity).getBukkitEntity().getScheduler().runAtFixedRate(GrimACBukkitLoaderPlugin.LOADER, (ignored) -> task.run(), retired, initialDelayTicks, periodTicks));
}
}
@@ -1,38 +0,0 @@
package ac.grim.grimac.platform.bukkit.scheduler.folia;
import ac.grim.grimac.api.plugin.GrimPlugin;
import ac.grim.grimac.api.platform.scheduler.GlobalRegionScheduler;
import ac.grim.grimac.api.platform.scheduler.TaskHandle;
import ac.grim.grimac.platform.bukkit.GrimACBukkitLoaderPlugin;
import org.bukkit.Bukkit;
import org.jetbrains.annotations.NotNull;
public class FoliaGlobalRegionScheduler implements GlobalRegionScheduler {
private final io.papermc.paper.threadedregions.scheduler.GlobalRegionScheduler globalRegionScheduler = Bukkit.getGlobalRegionScheduler();
@Override
public void execute(@NotNull GrimPlugin plugin, @NotNull Runnable run) {
globalRegionScheduler.execute(GrimACBukkitLoaderPlugin.LOADER, run);
}
@Override
public TaskHandle run(@NotNull GrimPlugin plugin, @NotNull Runnable task) {
return new FoliaTaskHandle(globalRegionScheduler.run(GrimACBukkitLoaderPlugin.LOADER, (ignored) -> task.run()));
}
@Override
public TaskHandle runDelayed(@NotNull GrimPlugin plugin, @NotNull Runnable task, long delay) {
return new FoliaTaskHandle(globalRegionScheduler.runDelayed(GrimACBukkitLoaderPlugin.LOADER, (ignored) -> task.run(), delay));
}
@Override
public TaskHandle runAtFixedRate(@NotNull GrimPlugin plugin, @NotNull Runnable task, long initialDelayTicks, long periodTicks) {
return new FoliaTaskHandle(globalRegionScheduler.runAtFixedRate(GrimACBukkitLoaderPlugin.LOADER, (ignored) -> task.run(), initialDelayTicks, periodTicks));
}
@Override
public void cancel(@NotNull GrimPlugin plugin) {
globalRegionScheduler.cancelTasks(GrimACBukkitLoaderPlugin.LOADER);
}
}
@@ -1,36 +0,0 @@
package ac.grim.grimac.platform.bukkit.scheduler.folia;
import ac.grim.grimac.api.platform.scheduler.AsyncScheduler;
import ac.grim.grimac.api.platform.scheduler.EntityScheduler;
import ac.grim.grimac.api.platform.scheduler.GlobalRegionScheduler;
import ac.grim.grimac.api.platform.scheduler.PlatformScheduler;
import ac.grim.grimac.api.platform.scheduler.RegionScheduler;
import org.checkerframework.checker.nullness.qual.NonNull;
public class FoliaPlatformScheduler implements PlatformScheduler {
private final FoliaAsyncScheduler foliaAsyncScheduler = new FoliaAsyncScheduler();
private final FoliaGlobalRegionScheduler foliaGlobalRegionScheduler = new FoliaGlobalRegionScheduler();
private final FoliaEntityScheduler foliaEntityScheduler = new FoliaEntityScheduler();
private final FoliaRegionScheduler foliaRegionScheduler = new FoliaRegionScheduler();
@Override
public @NonNull AsyncScheduler getAsyncScheduler() {
return foliaAsyncScheduler;
}
@Override
public @NonNull GlobalRegionScheduler getGlobalRegionScheduler() {
return foliaGlobalRegionScheduler;
}
@Override
public @NonNull EntityScheduler getEntityScheduler() {
return foliaEntityScheduler;
}
@Override
public @NonNull RegionScheduler getRegionScheduler() {
return foliaRegionScheduler;
}
}
@@ -1,59 +0,0 @@
package ac.grim.grimac.platform.bukkit.scheduler.folia;
import ac.grim.grimac.api.plugin.GrimPlugin;
import ac.grim.grimac.api.platform.scheduler.RegionScheduler;
import ac.grim.grimac.api.platform.scheduler.TaskHandle;
import ac.grim.grimac.api.platform.world.PlatformWorld;
import ac.grim.grimac.platform.bukkit.GrimACBukkitLoaderPlugin;
import ac.grim.grimac.platform.bukkit.world.BukkitPlatformWorld;
import ac.grim.grimac.api.math.Location;
import org.bukkit.Bukkit;
import org.jetbrains.annotations.NotNull;
public class FoliaRegionScheduler implements RegionScheduler {
private final io.papermc.paper.threadedregions.scheduler.RegionScheduler regionScheduler = Bukkit.getRegionScheduler();
@Override
public void execute(@NotNull GrimPlugin plugin, @NotNull PlatformWorld world, int chunkX, int chunkZ, @NotNull Runnable run) {
regionScheduler.execute(GrimACBukkitLoaderPlugin.LOADER, ((BukkitPlatformWorld) world).getBukkitWorld(), chunkX, chunkZ, run);
}
@Override
public void execute(@NotNull GrimPlugin plugin, @NotNull Location location, @NotNull Runnable run) {
execute(plugin, location.getWorld(), location.getBlockX() >> 4, location.getBlockZ() >> 4, run);
}
@Override
public TaskHandle run(@NotNull GrimPlugin plugin, @NotNull PlatformWorld world, int chunkX, int chunkZ, @NotNull Runnable task) {
return new FoliaTaskHandle(regionScheduler.run(GrimACBukkitLoaderPlugin.LOADER, ((BukkitPlatformWorld) world).getBukkitWorld(), chunkX, chunkZ, (ignored) -> task.run()));
}
@Override
public TaskHandle run(@NotNull GrimPlugin plugin, @NotNull Location location, @NotNull Runnable task) {
return run(plugin, location.getWorld(), location.getBlockX() >> 4, location.getBlockZ() >> 4, task);
}
@Override
public TaskHandle runDelayed(@NotNull GrimPlugin plugin, @NotNull PlatformWorld world, int chunkX, int chunkZ, @NotNull Runnable task, long delayTicks) {
return new FoliaTaskHandle(regionScheduler.runDelayed(GrimACBukkitLoaderPlugin.LOADER,
((BukkitPlatformWorld) world).getBukkitWorld(), chunkX, chunkZ, (ignored) -> task.run(), delayTicks));
}
@Override
public TaskHandle runDelayed(@NotNull GrimPlugin plugin, @NotNull Location location, @NotNull Runnable task, long delayTicks) {
return runDelayed(plugin, location.getWorld(), location.getBlockX() >> 4, location.getBlockZ() >> 4, task, delayTicks);
}
@Override
public TaskHandle runAtFixedRate(@NotNull GrimPlugin plugin, @NotNull PlatformWorld world, int chunkX, int chunkZ, @NotNull Runnable task, long initialDelayTicks, long periodTicks) {
return new FoliaTaskHandle(regionScheduler.runAtFixedRate(GrimACBukkitLoaderPlugin.LOADER,
((BukkitPlatformWorld) world).getBukkitWorld(), chunkX, chunkZ, (ignored) -> task.run(), initialDelayTicks, periodTicks));
}
@Override
public TaskHandle runAtFixedRate(@NotNull GrimPlugin plugin, @NotNull Location location, @NotNull Runnable task, long initialDelayTicks, long periodTicks) {
return runAtFixedRate(plugin, location.getWorld(), location.getBlockX() >> 4, location.getBlockZ() >> 4, task, initialDelayTicks, periodTicks);
}
}
@@ -1,28 +0,0 @@
package ac.grim.grimac.platform.bukkit.scheduler.folia;
import ac.grim.grimac.api.platform.scheduler.TaskHandle;
import io.papermc.paper.threadedregions.scheduler.ScheduledTask;
public class FoliaTaskHandle implements TaskHandle {
private final ScheduledTask task;
public FoliaTaskHandle(ScheduledTask task) {
this.task = task;
}
@Override
public boolean isSync() {
return false;
}
@Override
public boolean isCancelled() {
return task.isCancelled();
}
@Override
public void cancel() {
task.cancel();
}
}
@@ -1,89 +0,0 @@
package ac.grim.grimac.platform.bukkit.sender;
import ac.grim.grimac.api.GrimAPIProvider;
import ac.grim.grimac.api.platform.sender.Sender;
import ac.grim.grimac.api.platform.sender.SenderFactory;
import ac.grim.grimac.platform.bukkit.GrimACBukkitLoaderPlugin;
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
import net.kyori.adventure.text.Component;
import org.bukkit.command.CommandSender;
import org.bukkit.command.ConsoleCommandSender;
import org.bukkit.command.RemoteConsoleCommandSender;
import org.bukkit.entity.Player;
import org.bukkit.permissions.Permission;
import org.bukkit.permissions.PermissionDefault;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.incendo.cloud.SenderMapper;
import java.util.UUID;
public class BukkitSenderFactory extends SenderFactory<CommandSender> implements SenderMapper<CommandSender, Sender> {
private final BukkitAudiences audiences;
public BukkitSenderFactory() {
this.audiences = BukkitAudiences.create(GrimACBukkitLoaderPlugin.LOADER);
}
@Override
protected String getName(CommandSender sender) {
return sender instanceof Player ? sender.getName() : Sender.CONSOLE_NAME;
}
@Override
protected UUID getUniqueId(CommandSender sender) {
return sender instanceof Player ? ((Player) sender).getUniqueId() : Sender.CONSOLE_UUID;
}
@Override
protected void sendMessage(CommandSender sender, String message) {
sender.sendMessage(message);
}
@Override
protected void sendMessage(CommandSender sender, Component message) {
// we can safely send async for players and the console - otherwise, send it sync
if (sender instanceof Player || sender instanceof ConsoleCommandSender || sender instanceof RemoteConsoleCommandSender) {
this.audiences.sender(sender).sendMessage(message);
} else {
GrimAPIProvider.getDirect().getPlatformLoader().getScheduler().getGlobalRegionScheduler().run(
GrimAPIProvider.getDirect().getPlatformLoader().getPlugin(),
() -> this.audiences.sender(sender).sendMessage(message)
);
}
}
@Override
protected boolean hasPermission(CommandSender sender, String node) {
return sender.hasPermission(node);
}
@Override
protected boolean hasPermission(CommandSender sender, String node, boolean defaultIfUnset) {
return sender.hasPermission(new Permission(node, defaultIfUnset ? PermissionDefault.TRUE : PermissionDefault.FALSE));
}
@Override
protected void performCommand(CommandSender sender, String command) {
throw new UnsupportedOperationException();
}
@Override
protected boolean isConsole(CommandSender sender) {
return sender instanceof ConsoleCommandSender || sender instanceof RemoteConsoleCommandSender;
}
@Override
protected boolean isPlayer(CommandSender sender) {
return sender instanceof Player;
}
@Override
public @NonNull Sender map(@NonNull CommandSender base) {
return this.wrap(base);
}
@Override
public @NonNull CommandSender reverse(@NonNull Sender mapped) {
return this.unwrap(mapped);
}
}
@@ -1,43 +0,0 @@
package ac.grim.grimac.platform.bukkit.utils.convert;
import ac.grim.grimac.api.platform.permissions.PermissionDefaultValue;
import ac.grim.grimac.platform.bukkit.world.BukkitPlatformWorld;
import ac.grim.grimac.api.math.Location;
import ac.grim.grimac.api.packet.world.enums.BlockFace;
public class BukkitConversionUtils {
public static org.bukkit.Location toBukkitLocation(Location location) {
return new org.bukkit.Location(((BukkitPlatformWorld) location.getWorld()).getBukkitWorld(), location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch());
}
/**
* Converts this enum to a Bukkit PermissionDefault.
*
* @return The corresponding Bukkit PermissionDefault.
*/
public static org.bukkit.permissions.PermissionDefault toBukkitPermissionDefault(PermissionDefaultValue permissionDefaultValue) {
return switch (permissionDefaultValue) {
case TRUE -> org.bukkit.permissions.PermissionDefault.TRUE;
case FALSE -> org.bukkit.permissions.PermissionDefault.FALSE;
case OP -> org.bukkit.permissions.PermissionDefault.OP;
case NOT_OP -> org.bukkit.permissions.PermissionDefault.NOT_OP;
};
}
/**
* For use with bukkit events only
* Grim is not meant to be restrained by bukkit!
*/
@Deprecated
public static BlockFace fromBukkitFace(org.bukkit.block.BlockFace face) {
return switch (face) {
case NORTH -> BlockFace.NORTH;
case SOUTH -> BlockFace.SOUTH;
case WEST -> BlockFace.WEST;
case EAST -> BlockFace.EAST;
case UP -> BlockFace.UP;
case DOWN -> BlockFace.DOWN;
default -> BlockFace.OTHER;
};
}
}
@@ -1,78 +0,0 @@
package ac.grim.grimac.platform.bukkit.utils.placeholder;
import ac.grim.grimac.api.GrimAPIProvider;
import ac.grim.grimac.api.GrimUser;
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
public class PlaceholderAPIExpansion extends PlaceholderExpansion {
@Override
public @NotNull String getIdentifier() {
return "grim";
}
public @NotNull String getAuthor() {
return String.join(", ", GrimAPIProvider.getDirect().getPlatformLoader().getPlugin().getDescription().getAuthors());
}
@Override
public @NotNull String getVersion() {
return GrimAPIProvider.getDirect().getGrimVersion();
}
@Override
public boolean persist() {
return true;
}
@Override
public @NotNull List<String> getPlaceholders() {
Set<String> staticReplacements = GrimAPIProvider.getDirect().getStaticReplacements().keySet();
Set<String> variableReplacements = GrimAPIProvider.getDirect().getVariableReplacements().keySet();
ArrayList<String> placeholders = new ArrayList<>(staticReplacements.size() + variableReplacements.size());
for (String s : staticReplacements) {
placeholders.add(s.equals("%grim_version%") ? s : "%grim_" + s.replaceAll("%", "") + "%");
}
for (String s : variableReplacements) {
placeholders.add(s.equals("%player%") ? "%grim_player%" : "%grim_player_" + s.replaceAll("%", "") + "%");
}
return placeholders;
}
@Override
public String onRequest(OfflinePlayer offlinePlayer, @NotNull String params) {
for (Map.Entry<String, String> entry : GrimAPIProvider.getDirect().getStaticReplacements().entrySet()) {
String key = entry.getKey().equals("%grim_version%")
? "version"
: entry.getKey().replaceAll("%", "");
if (params.equalsIgnoreCase(key)) {
return entry.getValue();
}
}
if (offlinePlayer instanceof Player player) {
GrimUser grimPlayer = GrimAPIProvider.getDirect().getGrimUser(player.getUniqueId());
if (grimPlayer == null) return null;
for (Map.Entry<String, Function<GrimUser, String>> entry : GrimAPIProvider.getDirect().getVariableReplacements().entrySet()) {
String key = entry.getKey().equals("%player%")
? "player"
: "player_" + entry.getKey().replaceAll("%", "");
if (params.equalsIgnoreCase(key)) {
return entry.getValue().apply(grimPlayer);
}
}
}
return null;
}
}
@@ -1,52 +0,0 @@
package ac.grim.grimac.platform.bukkit.utils.reflection;
import ac.grim.grimac.api.reflection.ReflectionUtils;
import ac.grim.grimac.api.util.LogUtil;
import ac.grim.grimac.platform.bukkit.GrimACBukkitLoaderPlugin;
import org.bukkit.Location;
import org.bukkit.entity.Entity;
import org.bukkit.event.Event;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import java.util.concurrent.CompletableFuture;
public class PaperUtils {
public static final boolean PAPER;
public static final boolean TICK_END_EVENT;
public static final boolean ASYNC_TELEPORT;
static {
PAPER = ReflectionUtils.hasClass("com.destroystokyo.paper.PaperConfig") || ReflectionUtils.hasClass("io.papermc.paper.configuration.Configuration");
TICK_END_EVENT = ReflectionUtils.hasClass("com.destroystokyo.paper.event.server.ServerTickEndEvent");
ASYNC_TELEPORT = ReflectionUtils.hasMethod(Entity.class, "teleportAsync", org.bukkit.Location.class);
}
public static CompletableFuture<Boolean> teleportAsync(final Entity entity, final Location location) {
if (PAPER) {
return entity.teleportAsync(location);
} else {
return CompletableFuture.completedFuture(entity.teleport(location));
}
}
@SuppressWarnings("unchecked")
public static boolean registerTickEndEvent(Listener listener, Runnable runnable) {
if (TICK_END_EVENT) {
try {
Class<?> clazz = ReflectionUtils.getClass("com.destroystokyo.paper.event.server.ServerTickEndEvent");
if (clazz == null) return false;
GrimACBukkitLoaderPlugin.LOADER.getServer().getPluginManager().registerEvent((Class<? extends Event>) clazz,
listener,
EventPriority.NORMAL,
(l, event) -> runnable.run(), GrimACBukkitLoaderPlugin.LOADER);
return true;
} catch (Exception e) {
LogUtil.exception("Failed to register tick end event", e);
}
}
return false;
}
}
@@ -1,31 +0,0 @@
package ac.grim.grimac.platform.bukkit.world;
import ac.grim.grimac.api.packet.block.PacketBlockState;
import ac.grim.grimac.api.platform.world.PlatformChunk;
import ac.grim.grimac.api.packet.protocol.version.server.ServerVersions;
import org.bukkit.Chunk;
import org.bukkit.block.Block;
import org.bukkit.block.data.BlockData;
import org.jetbrains.annotations.NotNull;
import java.util.HashMap;
import java.util.Map;
public class BukkitPlatformChunk implements PlatformChunk {
private static final Map<BlockData, Integer> blockDataToId = new HashMap<>();
private static final boolean isFlat = ServerVersions.getServerVersion().isNewerThanOrEquals(ServerVersions.V_1_13);
private final Chunk chunk;
public BukkitPlatformChunk(@NotNull Chunk chunkAt) {
this.chunk = chunkAt;
}
@Override
public int getBlockID(int x, int y, int z) {
Block block = chunk.getBlock(x, y, z);
return isFlat // Cache blockDataToID because Strings are expensive
? blockDataToId.computeIfAbsent(block.getBlockData(), data -> PacketBlockState.getByString(ServerVersions.getServerVersion().toClientVersion(), data.getAsString(false)).getGlobalId())
: (block.getType().getId() << 4) | block.getData();
}
}
@@ -1,59 +0,0 @@
package ac.grim.grimac.platform.bukkit.world;
import ac.grim.grimac.api.platform.world.PlatformChunk;
import ac.grim.grimac.api.platform.world.PlatformWorld;
import ac.grim.grimac.api.packet.block.PacketBlockState;
import io.github.retrooper.packetevents.util.SpigotConversionUtil;
import lombok.Getter;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.UUID;
@Getter
public class BukkitPlatformWorld implements PlatformWorld {
private final World bukkitWorld;
public BukkitPlatformWorld(@NotNull World world) {
this.bukkitWorld = world;
}
@Override
public boolean isChunkLoaded(int chunkX, int chunkZ) {
return bukkitWorld.isChunkLoaded(chunkX, chunkZ);
}
// @Override
// public getBlockAt(int x, int y, int z) {
// return fromBukkitBlockData(bukkitWorld.getBlockAt(x, y, z).getBlockData());
// }
@Override
public boolean isAirAt(int x, int y, int z) {
PacketBlockState wrappedBlockState = SpigotConversionUtil.fromBukkitBlockData(bukkitWorld.getBlockData(x, y , z));
return wrappedBlockState.getType().isAir();
}
@Override
public String getName() {
return bukkitWorld.getName();
}
@Override
public @Nullable UUID getUID() {
return this.bukkitWorld.getUID();
}
@Override
public PlatformChunk getChunkAt(int currChunkX, int currChunkZ) {
return new BukkitPlatformChunk(bukkitWorld.getChunkAt(currChunkX, currChunkZ));
}
@Override
public boolean isLoaded() {
return Bukkit.getWorld(bukkitWorld.getUID()) != null;
}
}
-58
View File
@@ -1,58 +0,0 @@
import versioning.BuildConfig
plugins {
`maven-publish`
grim.`base-conventions`
}
repositories {
if (BuildConfig.mavenLocalOverride) {
mavenLocal()
}
maven {
name = "papermc"
url = uri("https://repo.papermc.io/repository/maven-public/")
}
maven("https://hub.spigotmc.org/nexus/content/repositories/snapshots/") // Spigot
maven("https://repo.grim.ac/snapshots") { // Grim API
content {
includeGroup("ac.grim.grimac")
includeGroup("com.github.retrooper")
}
}
maven("https://repo.viaversion.com") // ViaVersion
maven("https://nexus.scarsz.me/content/repositories/releases") // Configuralize
maven("https://repo.opencollab.dev/maven-snapshots/") // Floodgate
maven("https://repo.opencollab.dev/maven-releases/") // Cumulus (for Floodgate)
maven("https://s01.oss.sonatype.org/content/repositories/snapshots/")
mavenCentral() // FastUtil
}
dependencies {
if (BuildConfig.shadePE) {
api(libs.packetevents.api)
} else {
compileOnly(libs.packetevents.api)
}
api(libs.cloud.core)
api("org.incendo:cloud-processors-requirements:1.0.0-rc.1")
api("github.scarsz:configuralize:1.4.1:slim") {
exclude(group = "org.yaml", module = "snakeyaml")
}
// Bump snakeyaml (transitive dep of configuralize) 1.29 -> 2.2 for geyser-fabric
api(libs.snakeyaml)
api(libs.fastutil)
api(libs.adventure.text.minimessage)
api(libs.jetbrains.annotations)
implementation(project(":api"))
implementation(project(":api:packetevents"))
compileOnly(libs.floodgate.api)
compileOnly(libs.via.version.api)
compileOnly(libs.netty)
}
publishing.publications.create<MavenPublication>("maven") {
from(components["java"])
}
@@ -1,147 +0,0 @@
package ac.grim.grimac;
import ac.grim.grimac.api.event.EventBus;
import ac.grim.grimac.api.event.OptimizedEventBus;
import ac.grim.grimac.api.plugin.GrimPlugin;
import ac.grim.grimac.manager.AlertManagerImpl;
import ac.grim.grimac.manager.DiscordManager;
import ac.grim.grimac.manager.InitManager;
import ac.grim.grimac.manager.SpectateManager;
import ac.grim.grimac.manager.TickManager;
import ac.grim.grimac.manager.config.BaseConfigManager;
import ac.grim.grimac.api.platform.init.Initable;
import ac.grim.grimac.api.platform.Platform;
import ac.grim.grimac.api.platform.PlatformLoader;
import ac.grim.grimac.api.platform.PlatformServer;
import ac.grim.grimac.api.platform.manager.ItemResetHandler;
import ac.grim.grimac.api.platform.manager.MessagePlaceHolderManager;
import ac.grim.grimac.api.platform.manager.ParserDescriptorFactory;
import ac.grim.grimac.api.platform.manager.PermissionRegistrationManager;
import ac.grim.grimac.api.platform.manager.PlatformPluginManager;
import ac.grim.grimac.api.platform.player.PlatformPlayerFactory;
import ac.grim.grimac.api.platform.scheduler.PlatformScheduler;
import ac.grim.grimac.api.platform.sender.Sender;
import ac.grim.grimac.api.platform.sender.SenderFactory;
import ac.grim.grimac.utils.anticheat.PlayerDataManager;
import ac.grim.grimac.api.reflection.ReflectionUtils;
import com.github.retrooper.packetevents.PacketEventsAPI;
import lombok.Getter;
import org.incendo.cloud.CommandManager;
import org.jetbrains.annotations.NotNull;
import java.util.Map;
@Getter
public final class GrimAPI {
public final static GrimAPI INSTANCE = new GrimAPI();
@Getter
private final Platform platform = detectPlatform();
private final BaseConfigManager configManager;
private final AlertManagerImpl alertManager;
private final SpectateManager spectateManager;
private final DiscordManager discordManager;
private final PlayerDataManager playerDataManager;
private final TickManager tickManager;
private final EventBus eventBus;
private final GrimExternalAPI externalAPI;
private PlatformLoader loader;
@Getter
private InitManager initManager;
private boolean initialized = false;
public GrimAPI() {
this.configManager = new BaseConfigManager();
this.alertManager = new AlertManagerImpl(this);
this.spectateManager = new SpectateManager(this);
this.discordManager = new DiscordManager(this);
this.playerDataManager = new PlayerDataManager(this);
this.tickManager = new TickManager();
this.eventBus = new OptimizedEventBus();
this.externalAPI = new GrimExternalAPI(this);
System.out.println("GrimAPI classloader: " + GrimAPI.class.getClassLoader());
System.out.println("GrimAPI instance: " + this);
}
private static Platform detectPlatform() {
final Map<String, Platform> platforms = Map.of(
"io.papermc.paper.threadedregions.RegionizedServer", Platform.FOLIA,
"org.bukkit.Bukkit", Platform.BUKKIT,
"net.fabricmc.loader.api.FabricLoader", Platform.FABRIC
);
return platforms.entrySet().stream()
.filter(entry -> ReflectionUtils.hasClass(entry.getKey()))
.map(Map.Entry::getValue)
.findFirst()
.orElseThrow(() -> new IllegalStateException("Unknown platform!"));
}
public void bootstrap(@NotNull PlatformLoader platformLoader, Initable... platformSpecificInitables) {
this.loader = platformLoader;
this.initManager = new InitManager((PacketEventsAPI<?>) loader.getPacketAPI(), loader::getCommandManager, platformSpecificInitables);
this.initManager.load();
this.initialized = true;
}
public void start() {
checkInitialized();
initManager.start();
}
public void stop() {
checkInitialized();
initManager.stop();
}
public PlatformScheduler getScheduler() {
return loader.getScheduler();
}
public PlatformPlayerFactory getPlatformPlayerFactory() {
return loader.getPlatformPlayerFactory();
}
public ParserDescriptorFactory getParserDescriptors() {
return loader.getParserDescriptorFactory();
}
public GrimPlugin getGrimPlugin() {
return loader.getPlugin();
}
public SenderFactory<?> getSenderFactory() {
return loader.getSenderFactory();
}
public ItemResetHandler getItemResetHandler() {
return loader.getItemResetHandler();
}
public PlatformPluginManager getPluginManager() {
return loader.getPluginManager();
}
public PlatformServer getPlatformServer() {
return loader.getPlatformServer();
}
public @NotNull MessagePlaceHolderManager getMessagePlaceHolderManager() {
return loader.getMessagePlaceHolderManager();
}
public CommandManager<Sender> getCommandManager() {
return loader.getCommandManager();
}
private void checkInitialized() {
if (!initialized) {
throw new IllegalStateException("GrimAPI has not been initialized!");
}
}
public PermissionRegistrationManager getPermissionManager() {
return loader.getPermissionManager();
}
}
@@ -1,28 +0,0 @@
package ac.grim.grimac;
import ac.grim.grimac.api.platform.CoreLoader;
import ac.grim.grimac.api.platform.PlatformLoader;
import ac.grim.grimac.api.platform.init.Initable;
/**
* Service-loader entry that just forwards every call to the
* canonical singleton GrimAPI.INSTANCE.
* Ensures that ServiceLoader call to clazz.getDeclaredConstructor().newInstance(); will not create two instances
*/
public final class GrimAPICoreLoader implements CoreLoader {
@Override
public void bootstrap(PlatformLoader platform, Initable... initables) {
GrimAPI.INSTANCE.bootstrap(platform, initables);
}
@Override
public void start() {
GrimAPI.INSTANCE.start();
}
@Override
public void stop() {
GrimAPI.INSTANCE.stop();
}
}
@@ -1,218 +0,0 @@
package ac.grim.grimac.checks;
import ac.grim.grimac.GrimAPI;
import ac.grim.grimac.api.AbstractCheck;
import ac.grim.grimac.api.config.ConfigManager;
import ac.grim.grimac.api.event.events.FlagEvent;
import ac.grim.grimac.api.packet.MCPacket;
import ac.grim.grimac.api.packet.protocol.PacketClientVersions;
import ac.grim.grimac.api.packet.types.PacketType;
import ac.grim.grimac.api.packet.types.PacketTypes;
import ac.grim.grimac.api.packet.types.PacketWrapperFactory;
import ac.grim.grimac.api.packet.types.client.play.ClientPlayerFlyingMetaPacket;
import ac.grim.grimac.player.GrimPlayer;
import lombok.Getter;
import lombok.Setter;
import org.jetbrains.annotations.NotNull;
import java.util.Objects;
// Class from https://github.com/Tecnio/AntiCheatBase/blob/master/src/main/java/me/tecnio/anticheat/check/Check.java
@Getter
public class Check extends GrimProcessor implements AbstractCheck {
protected @NotNull final GrimPlayer player;
public double violations;
private double decay;
private double setbackVL;
private String checkName;
private String configName;
private String alternativeName;
private String displayName;
private String description;
private boolean experimental;
private @Setter boolean isEnabled;
private boolean exemptPermission;
private boolean noSetbackPermission;
private boolean noModifyPacketPermission;
private long lastViolationTime;
protected PacketWrapperFactory packetFactory = MCPacket.getAPI().packetFactory();
public Check(final @NotNull GrimPlayer player) {
this.player = Objects.requireNonNull(player);
final CheckData checkData = this.getClass().getAnnotation(CheckData.class);
if (checkData != null) {
this.checkName = checkData.name();
this.configName = checkData.configName();
// Fall back to check name
if (this.configName.equals("DEFAULT")) this.configName = this.checkName;
this.decay = checkData.decay();
this.setbackVL = checkData.setback();
this.alternativeName = checkData.alternativeName();
this.experimental = checkData.experimental();
this.description = checkData.description();
this.displayName = this.checkName;
}
reload();
}
public boolean shouldModifyPackets() {
return isEnabled && !player.disableGrim && !player.noModifyPacketPermission && !exemptPermission;
}
public void updatePermissions() {
if (player.platformPlayer == null || configName == null) return;
GrimAPI.INSTANCE.getScheduler().getEntityScheduler().run(
player.platformPlayer,
GrimAPI.INSTANCE.getGrimPlugin(),
() -> {
final String id = configName.toLowerCase();
exemptPermission = player.platformPlayer.hasPermission("grim.exempt." + id);
noSetbackPermission = player.platformPlayer.hasPermission("grim.nosetback." + id);
noModifyPacketPermission = player.platformPlayer.hasPermission("grim.nomodifypacket." + id);
},
() -> {}
);
}
public final boolean flagAndAlert(String verbose) {
if (flag(verbose)) {
alert(verbose);
return true;
}
return false;
}
public final boolean flagAndAlert() {
return flagAndAlert("");
}
public final boolean flag() {
return flag("");
}
public final boolean flag(String verbose) {
if (player.disableGrim || (experimental && !player.isExperimentalChecks()) || exemptPermission)
return false; // Avoid calling event if disabled
FlagEvent event = new FlagEvent(player, this, verbose);
GrimAPI.INSTANCE.getEventBus().post(event);
if (event.isCancelled()) return false;
player.punishmentManager.handleViolation(this);
lastViolationTime = System.currentTimeMillis();
violations++;
return true;
}
public final boolean flagWithSetback() {
return flagWithSetback("");
}
public final boolean flagWithSetback(String verbose) {
if (flag(verbose)) {
setbackIfAboveSetbackVL();
return true;
}
return false;
}
public final boolean flagAndAlertWithSetback() {
return flagAndAlertWithSetback("");
}
public final boolean flagAndAlertWithSetback(String verbose) {
if (flagAndAlert(verbose)) {
setbackIfAboveSetbackVL();
return true;
}
return false;
}
public final void reward() {
violations = Math.max(0, violations - decay);
}
@Override
public void reload(ConfigManager configuration) {
decay = configuration.getDoubleElse(configName + ".decay", decay);
setbackVL = configuration.getDoubleElse(configName + ".setbackvl", setbackVL);
displayName = configuration.getStringElse(configName + ".displayname", checkName);
description = configuration.getStringElse(configName + ".description", description);
if (setbackVL == -1) setbackVL = Double.MAX_VALUE;
updatePermissions();
onReload(configuration);
}
@Override
public void onReload(ConfigManager config) {
}
public boolean alert(String verbose) {
return player.punishmentManager.handleAlert(player, verbose, this);
}
public boolean setbackIfAboveSetbackVL() {
if (shouldSetback()) {
return player.getSetbackTeleportUtil().executeViolationSetback();
}
return false;
}
public boolean shouldSetback() {
return !noSetbackPermission && violations > setbackVL;
}
public String formatOffset(double offset) {
return offset > 0.001 ? String.format("%.5f", offset) : String.format("%.2E", offset);
}
public boolean isTransaction(PacketType packetType) {
return packetType == PacketTypes.Play.Client.PONG ||
packetType == PacketTypes.Play.Client.WINDOW_CONFIRMATION;
}
public boolean isFlying(PacketType type) {
return ClientPlayerFlyingMetaPacket.isFlying(type);
}
public boolean isUpdate(PacketType packetType) {
return isFlying(packetType)
|| packetType == PacketTypes.Play.Client.CLIENT_TICK_END
|| isTransaction(packetType);
}
public boolean isTickPacket(PacketType packetType) {
if (isTickPacketIncludingNonMovement(packetType)) {
if (isFlying(packetType)) {
return !player.packetStateData.lastPacketWasTeleport && !player.packetStateData.lastPacketWasOnePointSeventeenDuplicate;
}
return true;
}
return false;
}
public boolean isTickPacketIncludingNonMovement(PacketType packetType) {
// On 1.21.2+ fall back to the TICK_END packet IF the player did not send a movement packet for their tick
// TickTimer checks to see if player did not send a tick end packet before new flying packet is sent
if (player.getClientVersion().isNewerThanOrEquals(PacketClientVersions.V_1_21_2)
&& !player.packetStateData.didSendMovementBeforeTickEnd) {
if (packetType == PacketTypes.Play.Client.CLIENT_TICK_END) {
return true;
}
}
return isFlying(packetType);
}
public static final PacketType[] POSSIBLE_TICK_PACKET_TYPES = new PacketType[]{PacketTypes.Play.Client.CLIENT_TICK_END, PacketTypes.Play.Client.PLAYER_FLYING, PacketTypes.Play.Client.PLAYER_POSITION, PacketTypes.Play.Client.PLAYER_ROTATION, PacketTypes.Play.Client.PLAYER_POSITION_AND_ROTATION};
}
@@ -1,17 +0,0 @@
package ac.grim.grimac.checks;
import ac.grim.grimac.GrimAPI;
import ac.grim.grimac.api.AbstractProcessor;
import ac.grim.grimac.api.config.ConfigReloadable;
import ac.grim.grimac.utils.common.ConfigReloadObserver;
public abstract class GrimProcessor implements AbstractProcessor, ConfigReloadable, ConfigReloadObserver {
// Not everything has to be a check for it to process packets & be configurable
@Override
public void reload() {
reload(GrimAPI.INSTANCE.getConfigManager().getConfig());
}
}
@@ -1,22 +0,0 @@
package ac.grim.grimac.checks.debug;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.player.GrimPlayer;
public abstract class AbstractDebugHandler extends Check {
protected final GrimPlayer grimPlayer;
public AbstractDebugHandler(GrimPlayer grimPlayer) {
super(grimPlayer);
this.grimPlayer = grimPlayer;
}
public GrimPlayer getPlayer() {
return grimPlayer;
}
public abstract void toggleListener(GrimPlayer player);
public abstract boolean toggleConsoleOutput();
}
@@ -1,32 +0,0 @@
package ac.grim.grimac.checks.impl.badpackets;
import ac.grim.grimac.api.packet.types.event.PacketReceiveEvent;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PacketCheck;
import ac.grim.grimac.player.GrimPlayer;
@CheckData(name = "BadPacketsB", description = "Ignored set rotation packet")
public class BadPacketsB extends Check implements PacketCheck {
public BadPacketsB(final GrimPlayer player) {
super(player);
}
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (isTransaction(event.getPacketType())) {
player.pendingRotations.removeIf(data -> {
if (player.getLastTransactionReceived() > data.getTransaction()) {
if (!data.isAccepted()) {
flagAndAlert();
}
return true;
}
return false;
});
}
}
}
@@ -1,41 +0,0 @@
package ac.grim.grimac.checks.impl.badpackets;
import ac.grim.grimac.api.packet.protocol.PacketClientVersions;
import ac.grim.grimac.api.packet.protocol.version.server.ServerVersions;
import ac.grim.grimac.api.packet.types.PacketTypes;
import ac.grim.grimac.api.packet.types.event.PacketReceiveEvent;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PacketCheck;
import ac.grim.grimac.player.GrimPlayer;
@CheckData(name = "BadPacketsE")
public class BadPacketsE extends Check implements PacketCheck {
private int noReminderTicks;
public BadPacketsE(GrimPlayer player) {
super(player);
}
@Override
public void onPacketReceive(PacketReceiveEvent event) {
final boolean isViaPleaseStopUsingProtocolHacksOnYourServer = player.getClientVersion().isNewerThanOrEquals(PacketClientVersions.V_1_21_2) || ServerVersions.getServerVersion().isNewerThanOrEquals(ServerVersions.V_1_21_2);
if (event.getPacketType() == PacketTypes.Play.Client.PLAYER_POSITION_AND_ROTATION ||
event.getPacketType() == PacketTypes.Play.Client.PLAYER_POSITION) {
noReminderTicks = 0;
} else if (isFlying(event.getPacketType()) && !player.packetStateData.lastPacketWasTeleport) {
noReminderTicks++;
} else if (event.getPacketType() == PacketTypes.Play.Client.STEER_VEHICLE
|| (isViaPleaseStopUsingProtocolHacksOnYourServer && player.inVehicle())) {
noReminderTicks = 0; // Exempt vehicles
}
if (noReminderTicks > 20) {
flagAndAlert("ticks=" + noReminderTicks); // ban? I don't know how this would false
}
}
public void handleRespawn() {
noReminderTicks = 0;
}
}
@@ -1,53 +0,0 @@
package ac.grim.grimac.checks.impl.badpackets;
import ac.grim.grimac.api.packet.types.PacketTypes;
import ac.grim.grimac.api.packet.types.event.PacketReceiveEvent;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PacketCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.api.packet.types.client.play.ClientEntityActionPacket;
@CheckData(name = "BadPacketsG", description = "Sent duplicate sneaking status")
public class BadPacketsG extends Check implements PacketCheck {
private boolean lastSneaking, respawn;
public BadPacketsG(GrimPlayer player) {
super(player);
}
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (event.getPacketType() == PacketTypes.Play.Client.ENTITY_ACTION) {
ClientEntityActionPacket packet = packetFactory.clientEntityAction(event);
if (packet.getAction() == ClientEntityActionPacket.Action.START_SNEAKING) {
// The player may send two START_SNEAKING packets if they respawned
if (lastSneaking && !respawn) {
if (flagAndAlert("state=true") && shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
}
} else {
lastSneaking = true;
}
respawn = false;
} else if (packet.getAction() == ClientEntityActionPacket.Action.STOP_SNEAKING) {
if (!lastSneaking && !respawn) {
if (flagAndAlert("state=false") && shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
}
} else {
lastSneaking = false;
}
respawn = false;
}
}
}
public void handleRespawn() {
// Clients could potentially not send a STOP_SNEAKING packet when they die, so we need to track it
respawn = true;
}
}
@@ -1,69 +0,0 @@
package ac.grim.grimac.checks.impl.badpackets;
import ac.grim.grimac.api.packet.protocol.PacketClientVersions;
import ac.grim.grimac.api.packet.types.event.PacketReceiveEvent;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.BlockPlaceCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.BlockBreak;
import ac.grim.grimac.utils.anticheat.update.BlockPlace;
import ac.grim.grimac.api.packet.protocol.version.server.ServerVersions;
import ac.grim.grimac.api.packet.types.PacketTypes;
@CheckData(name = "BadPacketsH", description = "Sent unexpected sequence id", experimental = true)
public class BadPacketsH extends BlockPlaceCheck {
private int lastSequence;
private final boolean isSupportedVersion = player.getClientVersion().isNewerThanOrEquals(PacketClientVersions.V_1_19) && ServerVersions.getServerVersion().isNewerThanOrEquals(ServerVersions.V_1_19);
public BadPacketsH(final GrimPlayer player) {
super(player);
}
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (event.getPacketType() == PacketTypes.Play.Client.USE_ITEM
&& shouldCancel(packetFactory.clientPlayerUseItem(event).getSequence())) {
event.setCancelled(true);
player.onPacketCancel();
}
}
@Override
public void onBlockPlace(BlockPlace place) {
if (shouldCancel(place.sequence) && shouldCancel()) {
place.resync();
}
}
@Override
public void onBlockBreak(BlockBreak blockBreak) {
switch (blockBreak.action) {
case START_DIGGING, FINISHED_DIGGING -> {
if (shouldCancel(blockBreak.sequence)) {
blockBreak.cancel();
}
}
case CANCELLED_DIGGING -> { // other actions will be checked by BadPacketsL
if (blockBreak.sequence != 0 && flagAndAlert("expected=0, id=" + blockBreak.sequence) && shouldModifyPackets()) {
blockBreak.cancel();
}
}
}
}
public boolean shouldCancel(int sequence) {
if (isSupportedVersion && sequence != lastSequence + 1) {
if (flagAndAlert("expected=" + (lastSequence + 1) + ", id=" + sequence) && shouldModifyPackets()) {
lastSequence = sequence;
return true;
}
}
lastSequence = sequence;
return false;
}
public void onWorldChange() {
lastSequence = 0;
}
}
@@ -1,58 +0,0 @@
package ac.grim.grimac.checks.impl.badpackets;
import ac.grim.grimac.api.packet.player.enums.GameMode;
import ac.grim.grimac.api.packet.protocol.PacketClientVersions;
import ac.grim.grimac.api.packet.protocol.version.server.ServerVersions;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PostPredictionCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.data.HeadRotation;
import ac.grim.grimac.api.packet.types.event.PacketReceiveEvent;
import ac.grim.grimac.api.packet.types.PacketTypes;
import ac.grim.grimac.api.packet.types.client.play.ClientPlayerUseItemPacket;
import java.util.ArrayList;
import java.util.List;
@CheckData(name = "BadPacketsJ", description = "Rotation in use item packet did not match tick rotation", experimental = true)
public class BadPacketsJ extends Check implements PostPredictionCheck {
private final List<HeadRotation> rotations = new ArrayList<>();
public BadPacketsJ(GrimPlayer player) {
super(player);
}
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (player.gamemode == GameMode.SPECTATOR) {
rotations.clear();
return;
}
if (event.getPacketType() == PacketTypes.Play.Client.USE_ITEM && player.getClientVersion().isNewerThanOrEquals(PacketClientVersions.V_1_21)
&& ServerVersions.getServerVersion().isNewerThanOrEquals(ServerVersions.V_1_21)) {
ClientPlayerUseItemPacket packet = packetFactory.clientPlayerUseItem(event);
rotations.add(new HeadRotation(packet.getYaw(), packet.getPitch()));
}
if (isTickPacket(event.getPacketType())) {
// due to tick skipping, the rotations sent could be last tick's
boolean allowLast = player.canSkipTicks() && (event.getPacketType() == PacketTypes.Play.Client.PLAYER_POSITION_AND_ROTATION || event.getPacketType() == PacketTypes.Play.Client.PLAYER_ROTATION);
for (HeadRotation rotation : rotations) {
if (rotation.getYaw() == player.xRot && rotation.getPitch() == player.yRot) {
allowLast = false;
continue;
}
if (rotation.getYaw() == player.lastXRot && rotation.getPitch() == player.lastYRot && allowLast) {
continue;
}
flagAndAlert();
}
rotations.clear();
}
}
}
@@ -1,53 +0,0 @@
package ac.grim.grimac.checks.impl.badpackets;
import ac.grim.grimac.api.packet.player.enums.DiggingAction;
import ac.grim.grimac.api.packet.protocol.PacketClientVersions;
import ac.grim.grimac.api.packet.types.client.play.ClientPlayerDiggingPacket;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PacketCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.api.packet.types.event.PacketReceiveEvent;
import ac.grim.grimac.api.packet.types.PacketTypes;
import java.util.Locale;
@CheckData(name = "BadPacketsL", description = "Sent impossible dig packet")
public class BadPacketsL extends Check implements PacketCheck {
public BadPacketsL(GrimPlayer player) {
super(player);
}
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (event.getPacketType() == PacketTypes.Play.Client.PLAYER_DIGGING) {
final ClientPlayerDiggingPacket packet = packetFactory.clientPlayerDigging(event);
if (packet.getDiggingAction() == DiggingAction.START_DIGGING || packet.getDiggingAction() == DiggingAction.FINISHED_DIGGING || packet.getDiggingAction() == DiggingAction.CANCELLED_DIGGING)
return;
// 1.8 and above clients always send digging packets that aren't used for digging at 0, 0, 0, facing DOWN
// 1.7 and below clients do the same, except use SOUTH for RELEASE_USE_ITEM
final int expectedFace = player.getClientVersion().isOlderThanOrEquals(PacketClientVersions.V_1_7_10) && packet.getDiggingAction() == DiggingAction.RELEASE_USE_ITEM
? 255 : 0;
if (packet.getBlockFaceId() != expectedFace
|| packet.getBlockPosition().getX() != 0
|| packet.getBlockPosition().getY() != 0
|| packet.getBlockPosition().getZ() != 0
|| packet.getSequence() != 0
) {
if (flagAndAlert("pos="
+ packet.getBlockPosition().getX() + ", " + packet.getBlockPosition().getY() + ", " + packet.getBlockPosition().getZ()
+ ", face=" + packet.getBlockFace()
+ ", sequence=" + packet.getSequence()
+ ", action=" + packet.getDiggingAction().toString().toLowerCase(Locale.ROOT)
) && shouldModifyPackets() && packet.getDiggingAction() != DiggingAction.RELEASE_USE_ITEM) {
event.setCancelled(true);
player.onPacketCancel();
}
}
}
}
}
@@ -1,59 +0,0 @@
package ac.grim.grimac.checks.impl.badpackets;
import ac.grim.grimac.api.packet.types.client.play.ClientClickWindowPacket;
import ac.grim.grimac.api.packet.types.event.PacketReceiveEvent;
import ac.grim.grimac.api.packet.types.event.PacketSendEvent;
import ac.grim.grimac.api.packet.types.server.play.ServerOpenWindowPacket;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PacketCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.api.packet.types.PacketTypes;
import ac.grim.grimac.api.packet.types.client.play.ClientClickWindowPacket.WindowClickType;
@CheckData(name = "BadPacketsP", experimental = true)
public class BadPacketsP extends Check implements PacketCheck {
private int containerType = -1;
private int containerId = -1;
public BadPacketsP(GrimPlayer playerData) {
super(playerData);
}
@Override
public void onPacketSend(final PacketSendEvent event) {
if (event.getPacketType() == PacketTypes.Play.Server.OPEN_WINDOW) {
ServerOpenWindowPacket window = packetFactory.serverOpenWindow(event);
this.containerType = window.getType();
this.containerId = window.getContainerId();
}
}
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (event.getPacketType() == PacketTypes.Play.Client.CLICK_WINDOW) {
ClientClickWindowPacket wrapper = packetFactory.clientClickWindow(event);
WindowClickType clickType = wrapper.getWindowClickType();
int button = wrapper.getButton();
// TODO: Adjust for containers
boolean flag = switch (clickType) {
case PICKUP, QUICK_MOVE, CLONE -> button > 2 || button < 0;
case SWAP -> (button > 8 || button < 0) && button != 40;
case THROW -> button != 0 && button != 1;
case QUICK_CRAFT -> button == 3 || button == 7 || button > 10 || button < 0;
case PICKUP_ALL -> button != 0;
case UNKNOWN -> true;
};
// Allowing this to false flag to debug and find issues faster
if (flag) {
if (flagAndAlert("clickType=" + clickType.toString().toLowerCase() + ", button=" + button + (wrapper.getWindowId() == containerId ? ", container=" + containerType : "")) && shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
}
}
}
}
}
@@ -1,41 +0,0 @@
package ac.grim.grimac.checks.impl.badpackets;
import ac.grim.grimac.api.packet.MCPacket;
import ac.grim.grimac.api.packet.protocol.PacketClientVersions;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PacketCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.api.packet.types.event.PacketReceiveEvent;
import ac.grim.grimac.api.packet.types.PacketTypes;
import ac.grim.grimac.api.packet.types.client.play.ClientPlayerFlyingMetaPacket;
@CheckData(name = "BadPacketsV", description = "Did not move far enough", experimental = true)
public class BadPacketsV extends Check implements PacketCheck {
private int noReminderTicks;
public BadPacketsV(GrimPlayer player) {
super(player);
}
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (!player.canSkipTicks() && isTickPacket(event.getPacketType())) {
if (event.getPacketType() == PacketTypes.Play.Client.PLAYER_POSITION || event.getPacketType() == PacketTypes.Play.Client.PLAYER_POSITION_AND_ROTATION) {
int positionAtLeastEveryNTicks = player.getClientVersion().isOlderThanOrEquals(PacketClientVersions.V_1_8) ? 20 : 19;
if (noReminderTicks < positionAtLeastEveryNTicks && !player.uncertaintyHandler.lastTeleportTicks.hasOccurredSince(1)) {
final double deltaSq = ClientPlayerFlyingMetaPacket.from(event).getLocation().getPosition()
.distanceSquared(MCPacket.getAPI().getVectorFactory().getImmutableVec3d(player.lastX, player.lastY, player.lastZ));
if (deltaSq <= player.getMovementThreshold() * player.getMovementThreshold()) {
flagAndAlert("delta=" + Math.sqrt(deltaSq));
}
}
noReminderTicks = 0;
} else {
noReminderTicks++;
}
}
}
}
@@ -1,76 +0,0 @@
package ac.grim.grimac.checks.impl.badpackets;
import ac.grim.grimac.api.packet.types.event.PacketReceiveEvent;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PostPredictionCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
import ac.grim.grimac.api.packet.types.PacketTypes;
import ac.grim.grimac.api.packet.player.enums.GameMode;
@CheckData(name = "BadPacketsX", experimental = true)
public class BadPacketsX extends Check implements PostPredictionCheck {
private boolean sprint;
private boolean sneak;
private int flags;
public BadPacketsX(GrimPlayer player) {
super(player);
}
@Override
public void onPredictionComplete(final PredictionComplete predictionComplete) {
if (!player.canSkipTicks()) {
if (flags > 0) {
setbackIfAboveSetbackVL();
}
flags = 0;
return;
}
if (player.isTickingReliablyFor(3)) {
for (; flags > 0; flags--) {
flagAndAlertWithSetback();
}
}
flags = 0;
}
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (player.gamemode == GameMode.SPECTATOR || isTickPacket(event.getPacketType())) {
sprint = sneak = false;
return;
}
if (event.getPacketType() == PacketTypes.Play.Client.ENTITY_ACTION) {
switch (packetFactory.clientEntityAction(event).getAction()) {
case START_SNEAKING, STOP_SNEAKING -> {
if (sneak) {
if (player.canSkipTicks() || flagAndAlert()) {
flags++;
}
}
sneak = true;
}
case START_SPRINTING, STOP_SPRINTING -> {
if (player.inVehicle()) {
return;
}
if (sprint) {
if (player.canSkipTicks() || flagAndAlert()) {
flags++;
}
}
sprint = true;
}
}
}
}
}
@@ -1,72 +0,0 @@
package ac.grim.grimac.checks.impl.breaking;
import ac.grim.grimac.GrimAPI;
import ac.grim.grimac.api.packet.MCPacket;
import ac.grim.grimac.api.packet.item.PacketItemTypes;
import ac.grim.grimac.api.packet.item.PacketStateType;
import ac.grim.grimac.api.packet.protocol.PacketClientVersions;
import ac.grim.grimac.api.packet.util.vec.ImmutableVector3i;
import ac.grim.grimac.api.packet.world.PacketStateTypes;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.BlockBreakCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.BlockBreak;
import ac.grim.grimac.api.packet.player.enums.DiggingAction;
import org.checkerframework.checker.nullness.qual.NonNull;
@CheckData(name = "AirLiquidBreak", description = "Breaking a block that cannot be broken")
public class AirLiquidBreak extends Check implements BlockBreakCheck {
public final boolean noFireHitbox = player.getClientVersion().isOlderThanOrEquals(PacketClientVersions.V_1_15_2);
private int lastTick;
private boolean didLastFlag;
// Initialize to non-null values to prevent NPE when checking for blockType properties and if position equals old position
private @NonNull ImmutableVector3i lastBreakLoc = MCPacket.getAPI().getVectorFactory().getImmutableVec3i();
private @NonNull PacketStateType lastBlockType = PacketStateTypes.AIR;
public AirLiquidBreak(GrimPlayer player) {
super(player);
}
@Override
public void onBlockBreak(BlockBreak blockBreak) {
if (blockBreak.action != DiggingAction.START_DIGGING && blockBreak.action != DiggingAction.FINISHED_DIGGING)
return;
final PacketStateType block = blockBreak.block.getType();
// Fixes false from breaking kelp underwater
// The client sends two start digging packets to the server both in the same tick. AirLiquidBreak gets called twice, doesn't false the first time, but falses the second
// One ends up breaking the kelp, the other ends up doing nothing besides falsing this check because we think they're trying to mine water
// I am explicitly making this patch as narrow and specific as possible to potentially discover other blocks that exhibit similar behaviour
int newTick = GrimAPI.INSTANCE.getTickManager().currentTick;
if (lastTick == newTick
&& lastBreakLoc.equals(blockBreak.position)
&& !didLastFlag
&& lastBlockType.getHardness() == 0.0F
&& lastBlockType.getBlastResistance() == 0.0F
&& block == PacketStateTypes.WATER
) return;
lastTick = newTick;
lastBreakLoc = blockBreak.position;
lastBlockType = block;
// the block does not have a hitbox
boolean invalid = (block == PacketStateTypes.LIGHT && !(player.getInventory().getHeldItem() == PacketItemTypes.LIGHT || player.getInventory().getOffHand() == PacketItemTypes.LIGHT))
|| block.isAir()
|| block == PacketStateTypes.WATER
|| block == PacketStateTypes.LAVA
|| block == PacketStateTypes.BUBBLE_COLUMN
|| block == PacketStateTypes.MOVING_PISTON
|| block == PacketStateTypes.FIRE && noFireHitbox
// or the client claims to have broken an unbreakable block
|| block.getHardness() == -1.0f && blockBreak.action == DiggingAction.FINISHED_DIGGING;
if (invalid && flagAndAlert("block=" + block.getName() + ", type=" + blockBreak.action) && shouldModifyPackets()) {
didLastFlag = true;
blockBreak.cancel();
} else {
didLastFlag = false;
}
}
}
@@ -1,46 +0,0 @@
package ac.grim.grimac.checks.impl.breaking;
import ac.grim.grimac.api.packet.player.enums.GameMode;
import ac.grim.grimac.api.packet.protocol.attribute.Attributes;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.BlockBreakCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.BlockBreak;
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
import ac.grim.grimac.api.math.Vector3dm;
import ac.grim.grimac.utils.math.VectorUtils;
import ac.grim.grimac.api.packet.player.enums.DiggingAction;
@CheckData(name = "FarBreak", description = "Breaking blocks too far away", experimental = true)
public class FarBreak extends Check implements BlockBreakCheck {
public FarBreak(GrimPlayer player) {
super(player);
}
@Override
public void onBlockBreak(BlockBreak blockBreak) {
if (player.gamemode == GameMode.SPECTATOR || player.inVehicle() || blockBreak.action == DiggingAction.CANCELLED_DIGGING)
return; // falses
double min = Double.MAX_VALUE;
for (double d : player.getPossibleEyeHeights()) {
SimpleCollisionBox box = new SimpleCollisionBox(blockBreak.position);
Vector3dm eyes = new Vector3dm(player.x, player.y + d, player.z);
Vector3dm best = VectorUtils.cutBoxToVector(eyes, box);
min = Math.min(min, eyes.distanceSquared(best));
}
// getPickRange() determines this?
// With 1.20.5+ the new attribute determines creative mode reach using a modifier
double maxReach = player.compensatedEntities.self.getAttributeValue(Attributes.BLOCK_INTERACTION_RANGE);
if (player.packetStateData.didLastMovementIncludePosition || player.canSkipTicks()) {
double threshold = player.getMovementThreshold();
maxReach += Math.hypot(threshold, threshold);
}
if (min > maxReach * maxReach && flagAndAlert(String.format("distance=%.2f", Math.sqrt(min))) && shouldModifyPackets()) {
blockBreak.cancel();
}
}
}
@@ -1,129 +0,0 @@
package ac.grim.grimac.checks.impl.breaking;
import ac.grim.grimac.api.packet.block.PacketBlockState;
import ac.grim.grimac.api.packet.item.PacketStateType;
import ac.grim.grimac.api.packet.player.enums.DiggingAction;
import ac.grim.grimac.api.packet.protocol.PacketClientVersions;
import ac.grim.grimac.api.packet.protocol.version.server.ServerVersions;
import ac.grim.grimac.api.packet.types.event.PacketReceiveEvent;
import ac.grim.grimac.api.packet.util.vec.ImmutableVector3i;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.BlockBreakCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.BlockBreak;
import ac.grim.grimac.utils.math.GrimMath;
import ac.grim.grimac.utils.nmsutil.BlockBreakSpeed;
import ac.grim.grimac.utils.reflection.ViaVersionUtil;
import ac.grim.grimac.api.packet.types.PacketTypes;
import ac.grim.grimac.api.packet.world.PacketStateTypes;
import java.util.Set;
// Based loosely off of Hawk BlockBreakSpeedSurvival
// Also based loosely off of NoCheatPlus FastBreak
// Also based off minecraft wiki: https://minecraft.wiki/w/Breaking#Instant_breaking
@CheckData(name = "FastBreak", description = "Breaking blocks too quickly")
public class FastBreak extends Check implements BlockBreakCheck {
// For some reason these states flag and I don't know why.
// Better to just exempt to not annoy legit players.
private static final Set<PacketStateType> EXEMPT_STATES = Set.of();
private final boolean clientOlderThanServer = ServerVersions.getServerVersion().getProtocolVersion() > player.getClientVersion().getProtocolVersion();
public FastBreak(GrimPlayer playerData) {
super(playerData);
}
// The block the player is currently breaking
ImmutableVector3i targetBlockPosition = null;
// The maximum amount of damage the player deals to the block
//
double maximumBlockDamage = 0;
// The last time a finish digging packet was sent, to enforce 0.3-second delay after non-instabreak
long lastFinishBreak = 0;
// The time the player started to break the block, to know how long the player waited until they finished breaking the block
long startBreak = 0;
// The buffer to this check
double blockBreakBalance = 0;
double blockDelayBalance = 0;
@Override
public void onBlockBreak(BlockBreak blockBreak) {
if (blockBreak.action == DiggingAction.START_DIGGING) {
if (!ViaVersionUtil.isAvailable()) {
// Exempt all blocks that do not exist in the player version
final PacketBlockState defaultState = PacketBlockState.getDefaultState(player.getClientVersion(), blockBreak.block.getType());
if (defaultState.getType() == PacketStateTypes.AIR || EXEMPT_STATES.contains(defaultState.getType())) {
return;
}
}
// If client is older than the server, fetch block client actually sees from via
// otherwise just return the server-side block (since if client is >= server version the block is guaranteed to exist in client version)
// TODO this lazy loads PacketEvents mappings for older versions for clients on versions older than the servers, increasing memory usage
// * its the only thing we use non-native mappings for behind ViaVersion
// * can we translate back "up" to server version and run check against server version to avoid loading older registries?
PacketBlockState block = clientOlderThanServer ? PacketBlockState.getByGlobalId(player.getClientVersion(), player.getViaTranslatedClientBlockID(blockBreak.block.getGlobalId())) : blockBreak.block;
startBreak = System.currentTimeMillis() - (targetBlockPosition == null ? 50 : 0); // ???
targetBlockPosition = blockBreak.position;
maximumBlockDamage = BlockBreakSpeed.getBlockDamage(player, block);
double breakDelay = System.currentTimeMillis() - lastFinishBreak;
if (breakDelay >= 275) { // Reduce buffer if "close enough"
blockDelayBalance *= 0.9;
} else { // Otherwise, increase buffer
blockDelayBalance += 300 - breakDelay;
}
if (blockDelayBalance > 1000) { // If more than a second of advantage
if (flagAndAlert("delay=" + breakDelay + "ms, type=" + blockBreak.block.getType()) && shouldModifyPackets()) {
blockBreak.cancel();
}
}
clampBalance();
}
if (blockBreak.action == DiggingAction.FINISHED_DIGGING && targetBlockPosition != null) {
double predictedTime = Math.ceil(1 / maximumBlockDamage) * 50;
double realTime = System.currentTimeMillis() - startBreak;
double diff = predictedTime - realTime;
clampBalance();
if (diff < 25) { // Reduce buffer if "close enough"
blockBreakBalance *= 0.9;
} else { // Otherwise, increase buffer
blockBreakBalance += diff;
}
if (blockBreakBalance > 1000) { // If more than a second of advantage
if (flagAndAlert("diff=" + diff + "ms, balance=" + blockBreakBalance + "ms, type=" + blockBreak.block.getType()) && shouldModifyPackets()) {
blockBreak.cancel();
}
}
// also set start time because the breaking netcode is fucked on 1.14.4+
lastFinishBreak = startBreak = System.currentTimeMillis();
}
}
@Override
public void onPacketReceive(PacketReceiveEvent event) {
// Find the most optimal block damage using the animation packet, which is sent at least once a tick when breaking blocks
// On 1.8 clients, via screws with this packet meaning we must fall back to the 1.8 idle flying packet
if ((player.getClientVersion().isNewerThanOrEquals(PacketClientVersions.V_1_9) ? event.getPacketType() == PacketTypes.Play.Client.ANIMATION : isFlying(event.getPacketType())) && targetBlockPosition != null) {
maximumBlockDamage = Math.max(maximumBlockDamage, BlockBreakSpeed.getBlockDamage(player, player.compensatedWorld.getBlock(targetBlockPosition)));
}
}
private void clampBalance() {
double balance = Math.max(1000, (player.getTransactionPing()));
blockBreakBalance = GrimMath.clamp(blockBreakBalance, -balance, balance); // Clamp not Math.max in case other logic changes
blockDelayBalance = GrimMath.clamp(blockDelayBalance, -balance, balance);
}
}
@@ -1,30 +0,0 @@
package ac.grim.grimac.checks.impl.breaking;
import ac.grim.grimac.api.packet.player.enums.DiggingAction;
import ac.grim.grimac.api.packet.protocol.PacketClientVersions;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.BlockBreakCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.BlockBreak;
@CheckData(name = "InvalidBreak", description = "Sent impossible block face id")
public class InvalidBreak extends Check implements BlockBreakCheck {
public InvalidBreak(GrimPlayer player) {
super(player);
}
@Override
public void onBlockBreak(BlockBreak blockBreak) {
if (blockBreak.faceId == 255 && blockBreak.action == DiggingAction.CANCELLED_DIGGING && player.getClientVersion().isOlderThanOrEquals(PacketClientVersions.V_1_7_10)) {
return;
}
if (blockBreak.faceId < 0 || blockBreak.faceId > 5) {
// ban
if (flagAndAlert("face=" + blockBreak.faceId + ", action=" + blockBreak.action) && shouldModifyPackets()) {
blockBreak.cancel();
}
}
}
}
@@ -1,73 +0,0 @@
package ac.grim.grimac.checks.impl.breaking;
import ac.grim.grimac.api.packet.player.enums.DiggingAction;
import ac.grim.grimac.api.packet.player.enums.GameMode;
import ac.grim.grimac.api.packet.world.enums.BlockFace;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.BlockBreakCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.MessageUtil;
import ac.grim.grimac.utils.anticheat.update.BlockBreak;
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
import ac.grim.grimac.api.packet.types.event.PacketReceiveEvent;
import ac.grim.grimac.api.packet.util.vec.ImmutableVector3i;
import java.util.ArrayList;
import java.util.List;
@CheckData(name = "MultiBreak", experimental = true)
public class MultiBreak extends Check implements BlockBreakCheck {
private final List<String> flags = new ArrayList<>();
private boolean hasBroken;
private BlockFace lastFace;
private ImmutableVector3i lastPos;
public MultiBreak(GrimPlayer player) {
super(player);
}
@Override
public void onBlockBreak(BlockBreak blockBreak) {
if (blockBreak.action == DiggingAction.CANCELLED_DIGGING) {
return;
}
if (hasBroken && (blockBreak.face != lastFace || !blockBreak.position.equals(lastPos))) {
final String verbose = "face=" + blockBreak.face + ", lastFace=" + lastFace
+ ", pos=" + MessageUtil.toUnlabledString(blockBreak.position)
+ ", lastPos=" + MessageUtil.toUnlabledString(lastPos);
if (!player.canSkipTicks()) {
if (flagAndAlert(verbose) && shouldModifyPackets()) {
blockBreak.cancel();
}
} else {
flags.add(verbose);
}
}
lastFace = blockBreak.face;
lastPos = blockBreak.position;
hasBroken = true;
}
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (player.gamemode == GameMode.SPECTATOR || isTickPacket(event.getPacketType())) {
hasBroken = false;
}
}
@Override
public void onPredictionComplete(PredictionComplete predictionComplete) {
if (!player.canSkipTicks()) return;
if (player.isTickingReliablyFor(3)) {
for (String verbose : flags) {
flagAndAlert(verbose);
}
}
flags.clear();
}
}
@@ -1,42 +0,0 @@
package ac.grim.grimac.checks.impl.breaking;
import ac.grim.grimac.api.packet.types.PacketTypes;
import ac.grim.grimac.api.packet.types.event.PacketReceiveEvent;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.BlockBreakCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.BlockBreak;
import ac.grim.grimac.api.packet.player.enums.DiggingAction;
@CheckData(name = "NoSwingBreak", description = "Did not swing while breaking block", experimental = true)
public class NoSwingBreak extends Check implements BlockBreakCheck {
private boolean sentAnimation;
private boolean sentBreak;
public NoSwingBreak(GrimPlayer playerData) {
super(playerData);
}
@Override
public void onBlockBreak(BlockBreak blockBreak) {
if (blockBreak.action != DiggingAction.CANCELLED_DIGGING) {
sentBreak = true;
}
}
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (event.getPacketType() == PacketTypes.Play.Client.ANIMATION) {
sentAnimation = true;
}
if (isTickPacket(event.getPacketType())) {
if (sentBreak && !sentAnimation) {
flagAndAlert();
}
sentAnimation = sentBreak = false;
}
}
}
@@ -1,61 +0,0 @@
package ac.grim.grimac.checks.impl.breaking;
import ac.grim.grimac.api.packet.player.enums.DiggingAction;
import ac.grim.grimac.api.packet.world.PacketStateTypes;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.BlockBreakCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.BlockBreak;
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
@CheckData(name = "PositionBreakA")
public class PositionBreakA extends Check implements BlockBreakCheck {
public PositionBreakA(GrimPlayer player) {
super(player);
}
@Override
public void onBlockBreak(BlockBreak blockBreak) {
if (player.inVehicle()
|| blockBreak.action == DiggingAction.CANCELLED_DIGGING
|| blockBreak.block.getType() == PacketStateTypes.REDSTONE_WIRE
) return;
SimpleCollisionBox combined = blockBreak.getCombinedBox();
final double[] possibleEyeHeights = player.getPossibleEyeHeights();
double minEyeHeight = Double.MAX_VALUE;
double maxEyeHeight = Double.MIN_VALUE;
for (double height : possibleEyeHeights) {
minEyeHeight = Math.min(minEyeHeight, height);
maxEyeHeight = Math.max(maxEyeHeight, height);
}
SimpleCollisionBox eyePositions = new SimpleCollisionBox(player.x, player.y + minEyeHeight, player.z, player.x, player.y + maxEyeHeight, player.z);
if (!player.packetStateData.didLastMovementIncludePosition || player.canSkipTicks()) {
eyePositions.expand(player.getMovementThreshold());
}
// If the player is inside a block, then they can ray trace through the block and hit the other side of the block
if (eyePositions.isIntersected(combined)) {
return;
}
// So now we have the player's possible eye positions
// So then look at the face that the player has clicked
boolean flag = switch (blockBreak.face) {
case NORTH -> eyePositions.minZ > combined.minZ; // Z- face
case SOUTH -> eyePositions.maxZ < combined.maxZ; // Z+ face
case EAST -> eyePositions.maxX < combined.maxX; // X+ face
case WEST -> eyePositions.minX > combined.minX; // X- face
case UP -> eyePositions.maxY < combined.maxY; // Y+ face
case DOWN -> eyePositions.minY > combined.minY; // Y- face
default -> false;
};
if (flag && flagAndAlert("action=" + blockBreak.action + ", face=" + blockBreak.face) && shouldModifyPackets()) {
blockBreak.cancel();
}
}
}
@@ -1,37 +0,0 @@
package ac.grim.grimac.checks.impl.breaking;
import ac.grim.grimac.api.packet.player.enums.DiggingAction;
import ac.grim.grimac.api.packet.protocol.PacketClientVersions;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.BlockBreakCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.BlockBreak;
import ac.grim.grimac.api.packet.world.enums.BlockFace;
@CheckData(name = "PositionBreakB", experimental = true)
public class PositionBreakB extends Check implements BlockBreakCheck {
private final int releaseFace = player.getClientVersion().isNewerThanOrEquals(PacketClientVersions.V_1_8) ? 0 : 255;
private BlockFace lastFace;
public PositionBreakB(GrimPlayer player) {
super(player);
}
@Override
public void onBlockBreak(BlockBreak blockBreak) {
if (blockBreak.action == DiggingAction.START_DIGGING) {
if (blockBreak.face == lastFace) {
lastFace = null;
}
}
if (lastFace != null) {
flagAndAlert("lastFace=" + lastFace + ", action=" + blockBreak.action);
}
if (blockBreak.action == DiggingAction.CANCELLED_DIGGING) {
lastFace = blockBreak.faceId == releaseFace ? null : blockBreak.face;
}
}
}
@@ -1,123 +0,0 @@
package ac.grim.grimac.checks.impl.breaking;
import ac.grim.grimac.api.packet.MCPacket;
import ac.grim.grimac.api.packet.player.enums.DiggingAction;
import ac.grim.grimac.api.packet.protocol.PacketClientVersions;
import ac.grim.grimac.api.packet.util.vec.ImmutableVector3d;
import ac.grim.grimac.api.packet.util.vec.ImmutableVector3f;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.BlockBreakCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.BlockBreak;
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
import ac.grim.grimac.utils.data.Pair;
import ac.grim.grimac.api.math.Vector3dm;
import ac.grim.grimac.utils.nmsutil.Ray;
import ac.grim.grimac.utils.nmsutil.ReachUtils;
import ac.grim.grimac.api.packet.protocol.attribute.Attributes;
import ac.grim.grimac.api.packet.player.enums.GameMode;
import ac.grim.grimac.api.packet.world.enums.BlockFace;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@CheckData(name = "RotationBreak", experimental = true)
public class RotationBreak extends Check implements BlockBreakCheck {
private double flagBuffer = 0; // If the player flags once, force them to play legit, or we will cancel the tick before.
private boolean ignorePost = false;
public RotationBreak(GrimPlayer player) {
super(player);
}
@Override
public void onBlockBreak(BlockBreak blockBreak) {
if (player.gamemode == GameMode.SPECTATOR)
return; // you don't send flying packets when spectating entities
if (player.inVehicle()) return; // falses
if (blockBreak.action == DiggingAction.CANCELLED_DIGGING) return; // falses
if (flagBuffer > 0 && !didRayTraceHit(blockBreak)) {
ignorePost = true;
// If the player hit and has flagged this check recently
if (flagAndAlert("pre-flying, action=" + blockBreak.action) && shouldModifyPackets()) {
blockBreak.cancel();
}
}
}
@Override
public void onPostFlyingBlockBreak(BlockBreak blockBreak) {
if (player.gamemode == GameMode.SPECTATOR)
return; // you don't send flying packets when spectating entities
if (player.inVehicle()) return; // falses
if (blockBreak.action == DiggingAction.CANCELLED_DIGGING) return; // falses
// Don't flag twice
if (ignorePost) {
ignorePost = false;
return;
}
if (didRayTraceHit(blockBreak)) {
flagBuffer = Math.max(0, flagBuffer - 0.1);
} else {
flagBuffer = 1;
flagAndAlert("post-flying, action=" + blockBreak.action);
}
}
private boolean didRayTraceHit(BlockBreak blockBreak) {
SimpleCollisionBox box = new SimpleCollisionBox(blockBreak.position);
final double[] possibleEyeHeights = player.getPossibleEyeHeights();
// Start checking if player is in the block
double minEyeHeight = Double.MAX_VALUE;
double maxEyeHeight = Double.MIN_VALUE;
for (double height : possibleEyeHeights) {
minEyeHeight = Math.min(minEyeHeight, height);
maxEyeHeight = Math.max(maxEyeHeight, height);
}
SimpleCollisionBox eyePositions = new SimpleCollisionBox(player.x, player.y + minEyeHeight, player.z, player.x, player.y + maxEyeHeight, player.z);
eyePositions.expand(player.getMovementThreshold());
// If the player is inside a block, then they can ray trace through the block and hit the other side of the block
if (eyePositions.isIntersected(box)) {
return true;
}
// End checking if the player is in the block
List<ImmutableVector3f> possibleLookDirs = new ArrayList<>(Arrays.asList(
MCPacket.getAPI().getVectorFactory().getImmutableVector3f(player.lastXRot, player.yRot, 0),
MCPacket.getAPI().getVectorFactory().getImmutableVector3f(player.xRot, player.yRot, 0)
));
// 1.9+ players could be a tick behind because we don't get skipped ticks
if (player.getClientVersion().isNewerThanOrEquals(PacketClientVersions.V_1_9)) {
possibleLookDirs.add(MCPacket.getAPI().getVectorFactory().getImmutableVector3f(player.lastXRot, player.lastYRot, 0));
}
// 1.7 players do not have any of these issues! They are always on the latest look vector
if (player.getClientVersion().isOlderThan(PacketClientVersions.V_1_8)) {
possibleLookDirs = Collections.singletonList(MCPacket.getAPI().getVectorFactory().getImmutableVector3f(player.xRot, player.yRot, 0));
}
final double distance = player.compensatedEntities.self.getAttributeValue(Attributes.BLOCK_INTERACTION_RANGE);
for (double d : possibleEyeHeights) {
for (ImmutableVector3f lookDir : possibleLookDirs) {
ImmutableVector3d starting = MCPacket.getAPI().getVectorFactory().getImmutableVec3d(player.x, player.y + d, player.z);
Ray trace = new Ray(player, starting.getX(), starting.getY(), starting.getZ(), lookDir.getX(), lookDir.getY());
Pair<Vector3dm, BlockFace> intercept = ReachUtils.calculateIntercept(box, trace.getOrigin(), trace.getPointAtDistance(distance));
if (intercept.first() != null) return true;
}
}
return false;
}
}
@@ -1,92 +0,0 @@
package ac.grim.grimac.checks.impl.breaking;
import ac.grim.grimac.api.packet.protocol.PacketClientVersions;
import ac.grim.grimac.api.packet.util.vec.ImmutableVector3i;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.BlockBreakCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.MessageUtil;
import ac.grim.grimac.utils.anticheat.update.BlockBreak;
import ac.grim.grimac.api.packet.protocol.version.server.ServerVersions;
import ac.grim.grimac.api.packet.player.enums.DiggingAction;
import ac.grim.grimac.api.packet.block.PacketBlockState;
import static ac.grim.grimac.utils.nmsutil.BlockBreakSpeed.getBlockDamage;
@CheckData(name = "WrongBreak")
public class WrongBreak extends Check implements BlockBreakCheck {
private final int exemptedY = player.getClientVersion().isOlderThan(PacketClientVersions.V_1_8) ? 255 : (ServerVersions.getServerVersion().isNewerThanOrEquals(ServerVersions.V_1_14) ? -1 : 4095);
private boolean lastBlockWasInstantBreak = false;
private ImmutableVector3i lastBlock, lastCancelledBlock, lastLastBlock = null;
public WrongBreak(final GrimPlayer player) {
super(player);
}
// The client sometimes sends a wierd cancel packet
private boolean shouldExempt(final PacketBlockState block, int yPos) {
// lastLastBlock is always null when this happens, and lastBlock isn't
if (lastLastBlock != null || lastBlock == null)
return false;
// on pre 1.14.4 clients, the YPos of this packet is always the same
if (player.getClientVersion().isOlderThan(PacketClientVersions.V_1_14_4) && yPos != exemptedY)
return false;
// and if this block is not an instant break
return player.getClientVersion().isOlderThan(PacketClientVersions.V_1_14_4) || getBlockDamage(player, block) < 1;
}
@Override
public void onBlockBreak(BlockBreak blockBreak) {
if (blockBreak.action == DiggingAction.START_DIGGING) {
final ImmutableVector3i pos = blockBreak.position;
lastBlockWasInstantBreak = getBlockDamage(player, blockBreak.block) >= 1;
lastCancelledBlock = null;
lastLastBlock = lastBlock;
lastBlock = pos;
}
if (blockBreak.action == DiggingAction.CANCELLED_DIGGING) {
final ImmutableVector3i pos = blockBreak.position;
if (!shouldExempt(blockBreak.block, pos.getY()) && !pos.equals(lastBlock)) {
// https://github.com/GrimAnticheat/Grim/issues/1512
if (player.getClientVersion().isOlderThan(PacketClientVersions.V_1_14_4) || (!lastBlockWasInstantBreak && pos.equals(lastCancelledBlock))) {
if (flagAndAlert("action=CANCELLED_DIGGING" + ", last=" + MessageUtil.toUnlabledString(lastBlock) + ", pos=" + MessageUtil.toUnlabledString(pos))) {
if (shouldModifyPackets()) {
blockBreak.cancel();
}
}
}
}
lastCancelledBlock = pos;
lastLastBlock = null;
lastBlock = null;
return;
}
if (blockBreak.action == DiggingAction.FINISHED_DIGGING) {
final ImmutableVector3i pos = blockBreak.position;
// when a player looks away from the mined block, they send a cancel, and if they look at it again, they don't send another start. (thanks mojang!)
if (!pos.equals(lastCancelledBlock) && (!lastBlockWasInstantBreak || player.getClientVersion().isOlderThan(PacketClientVersions.V_1_14_4)) && !pos.equals(lastBlock)) {
if (flagAndAlert("action=FINISHED_DIGGING" + ", last=" + MessageUtil.toUnlabledString(lastBlock) + ", pos=" + MessageUtil.toUnlabledString(pos))) {
if (shouldModifyPackets()) {
blockBreak.cancel();
}
}
}
// 1.14.4+ clients don't send another start break in protected regions
if (player.getClientVersion().isOlderThan(PacketClientVersions.V_1_14_4)) {
lastCancelledBlock = null;
lastLastBlock = null;
lastBlock = null;
}
}
}
}
@@ -1,13 +0,0 @@
package ac.grim.grimac.checks.impl.combat;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PacketCheck;
import ac.grim.grimac.player.GrimPlayer;
@CheckData(name = "Hitboxes", setback = 10)
public class Hitboxes extends Check implements PacketCheck {
public Hitboxes(GrimPlayer player) {
super(player);
}
}
@@ -1,30 +0,0 @@
package ac.grim.grimac.checks.impl.crash;
import ac.grim.grimac.api.packet.types.PacketTypes;
import ac.grim.grimac.api.packet.types.client.play.ClientSettingsPacket;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PacketCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.api.packet.types.event.PacketReceiveEvent;
@CheckData(name = "CrashE")
public class CrashE extends Check implements PacketCheck {
public CrashE(GrimPlayer playerData) {
super(playerData);
}
@Override
public void onPacketReceive(final PacketReceiveEvent event) {
if (event.getPacketType() == PacketTypes.Play.Client.CLIENT_SETTINGS) {
ClientSettingsPacket wrapper = ClientSettingsPacket.from(event);
int viewDistance = wrapper.getViewDistance();
if (viewDistance < 2) {
flagAndAlert("distance=" + viewDistance);
wrapper.setViewDistance(2);
}
}
}
}
@@ -1,53 +0,0 @@
package ac.grim.grimac.checks.impl.crash;
import ac.grim.grimac.api.packet.protocol.PacketClientVersions;
import ac.grim.grimac.api.packet.protocol.version.server.ServerVersions;
import ac.grim.grimac.api.packet.types.client.play.ClientPlayerUseItemPacket;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.BlockPlaceCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.BlockBreak;
import ac.grim.grimac.utils.anticheat.update.BlockPlace;
import ac.grim.grimac.api.packet.types.event.PacketReceiveEvent;
import ac.grim.grimac.api.packet.types.PacketTypes;
@CheckData(name = "CrashG", description = "Sent negative sequence id")
public class CrashG extends BlockPlaceCheck {
public CrashG(GrimPlayer player) {
super(player);
}
@Override
public void onPacketReceive(final PacketReceiveEvent event) {
if (event.getPacketType() == PacketTypes.Play.Client.USE_ITEM && isSupportedVersion()) {
ClientPlayerUseItemPacket use = packetFactory.clientPlayerUseItem(event);
if (use.getSequence() < 0) {
flagAndAlert();
event.setCancelled(true);
player.onPacketCancel();
}
}
}
@Override
public void onBlockBreak(BlockBreak blockBreak) {
if (blockBreak.sequence < 0 && isSupportedVersion()) {
flagAndAlert();
blockBreak.cancel();
}
}
@Override
public void onBlockPlace(BlockPlace place) {
if (place.sequence < 0 && isSupportedVersion()) {
flagAndAlert();
place.resync();
}
}
private boolean isSupportedVersion() {
return player.getClientVersion().isNewerThanOrEquals(PacketClientVersions.V_1_19) && ServerVersions.getServerVersion().isNewerThanOrEquals(ServerVersions.V_1_19);
}
}
@@ -1,39 +0,0 @@
package ac.grim.grimac.checks.impl.crash;
import ac.grim.grimac.api.packet.types.client.play.ClientSelectBundleItemPacket;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PacketCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.api.packet.types.event.PacketReceiveEvent;
import ac.grim.grimac.api.packet.types.PacketTypes;
@CheckData(name = "CrashI")
public class CrashI extends Check implements PacketCheck {
public CrashI(GrimPlayer player) {
super(player);
}
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (event.getPacketType() == PacketTypes.Play.Client.SELECT_BUNDLE_ITEM) {
int selectedItemIndex;
try {
selectedItemIndex = ClientSelectBundleItemPacket.from(event).getSelectedItemIndex();
} catch (IllegalArgumentException e) {
// thanks packetevents!
if (e.getMessage().startsWith("Invalid selectedItemIndex: ")) {
selectedItemIndex = Integer.parseInt(e.getMessage().substring(27));
} else {
throw e;
}
}
if (selectedItemIndex < -1) {
flagAndAlert("selectedItemIndex=" + selectedItemIndex);
event.setCancelled(true);
player.onPacketCancel();
}
}
}
}
@@ -1,50 +0,0 @@
package ac.grim.grimac.checks.impl.elytra;
import ac.grim.grimac.api.packet.protocol.PacketClientVersions;
import ac.grim.grimac.api.packet.types.PacketTypes;
import ac.grim.grimac.api.packet.types.client.play.ClientEntityActionPacket;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PostPredictionCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
import ac.grim.grimac.api.packet.types.event.PacketReceiveEvent;
@CheckData(name = "ElytraA", description = "Started gliding while already gliding", experimental = true)
public class ElytraA extends Check implements PostPredictionCheck {
private boolean setback;
public ElytraA(GrimPlayer player) {
super(player);
}
public void onStartGliding(PacketReceiveEvent event) {
if (player.getClientVersion().isOlderThanOrEquals(PacketClientVersions.V_1_8)) {
return;
}
if (player.isGliding && flagAndAlert()) {
setback = true;
if (shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
player.resyncPose();
}
}
}
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (player.getClientVersion().isOlderThan(PacketClientVersions.V_1_15) && event.getPacketType() == PacketTypes.Play.Client.ENTITY_ACTION
&& packetFactory.clientEntityAction(event).getAction() == ClientEntityActionPacket.Action.START_FLYING_WITH_ELYTRA
) onStartGliding(event);
}
@Override
public void onPredictionComplete(PredictionComplete predictionComplete) {
if (setback) {
setbackIfAboveSetbackVL();
setback = false;
}
}
}
@@ -1,57 +0,0 @@
package ac.grim.grimac.checks.impl.elytra;
import ac.grim.grimac.api.packet.types.client.play.ClientEntityActionPacket;
import ac.grim.grimac.api.packet.types.event.PacketReceiveEvent;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PostPredictionCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
import ac.grim.grimac.api.packet.types.PacketTypes;
@CheckData(name = "ElytraB", description = "Started gliding without jumping", experimental = true)
public class ElytraB extends Check implements PostPredictionCheck {
private boolean glide;
private boolean setback;
public ElytraB(GrimPlayer player) {
super(player);
}
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (event.getPacketType() == PacketTypes.Play.Client.ENTITY_ACTION
&& packetFactory.clientEntityAction(event).getAction() == ClientEntityActionPacket.Action.START_FLYING_WITH_ELYTRA
&& player.supportsEndTick()
) {
if (player.packetStateData.knownInput.jump()) {
if (flagAndAlert("no release")) {
setback = true;
if (shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
player.resyncPose();
}
}
} else {
glide = true;
}
}
if (isUpdate(event.getPacketType())) {
if (glide && !player.packetStateData.knownInput.jump() && flagAndAlert("no jump")) {
setback = true;
}
glide = false;
}
}
@Override
public void onPredictionComplete(PredictionComplete predictionComplete) {
if (setback) {
setback = false;
setbackIfAboveSetbackVL();
}
}
}
@@ -1,76 +0,0 @@
package ac.grim.grimac.checks.impl.elytra;
import ac.grim.grimac.api.packet.protocol.PacketClientVersions;
import ac.grim.grimac.api.packet.types.PacketTypes;
import ac.grim.grimac.api.packet.types.client.play.ClientEntityActionPacket;
import ac.grim.grimac.api.packet.types.event.PacketReceiveEvent;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PostPredictionCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
import ac.grim.grimac.api.packet.player.enums.GameMode;
@CheckData(name = "ElytraC", description = "Started gliding too frequently", experimental = true)
public class ElytraC extends Check implements PostPredictionCheck {
private boolean glideThisTick, glideLastTick, setback;
private int flags;
public ElytraC(GrimPlayer player) {
super(player);
}
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (player.getClientVersion().isOlderThanOrEquals(PacketClientVersions.V_1_8)) {
return;
}
if (player.gamemode == GameMode.SPECTATOR) {
glideThisTick = glideLastTick = false;
}
if (event.getPacketType() == PacketTypes.Play.Client.ENTITY_ACTION && packetFactory.clientEntityAction(event).getAction() == ClientEntityActionPacket.Action.START_FLYING_WITH_ELYTRA) {
if (glideThisTick || glideLastTick) {
if (player.canSkipTicks()) {
flags++;
} else {
if (flagAndAlert()) {
setback = true;
if (shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
player.resyncPose();
}
}
}
}
glideThisTick = true;
}
if (isTickPacket(event.getPacketType())) {
glideLastTick = glideThisTick;
glideThisTick = false;
}
}
@Override
public void onPredictionComplete(PredictionComplete predictionComplete) {
if (player.canSkipTicks()) {
if (player.isTickingReliablyFor(3)) {
for (; flags > 0; flags--) {
flagAndAlert();
}
}
flags = 0;
setback = false;
}
if (setback) {
setback = false;
setbackIfAboveSetbackVL();
}
}
}
@@ -1,48 +0,0 @@
package ac.grim.grimac.checks.impl.elytra;
import ac.grim.grimac.api.packet.protocol.PacketClientVersions;
import ac.grim.grimac.api.packet.types.PacketTypes;
import ac.grim.grimac.api.packet.types.event.PacketReceiveEvent;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PostPredictionCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
import ac.grim.grimac.api.packet.types.client.play.ClientEntityActionPacket;
@CheckData(name = "ElytraD", description = "Started gliding without an elytra", experimental = true)
public class ElytraD extends Check implements PostPredictionCheck {
private boolean setback;
public ElytraD(GrimPlayer player) {
super(player);
}
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (player.getClientVersion().isOlderThanOrEquals(PacketClientVersions.V_1_8)) {
return;
}
if (event.getPacketType() == PacketTypes.Play.Client.ENTITY_ACTION
&& packetFactory.clientEntityAction(event).getAction() == ClientEntityActionPacket.Action.START_FLYING_WITH_ELYTRA
&& !player.canGlide()
&& flagAndAlert()
) {
setback = true;
if (shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
player.resyncPose();
}
}
}
@Override
public void onPredictionComplete(PredictionComplete predictionComplete) {
if (setback) {
setbackIfAboveSetbackVL();
setback = false;
}
}
}
@@ -1,48 +0,0 @@
package ac.grim.grimac.checks.impl.elytra;
import ac.grim.grimac.api.packet.protocol.PacketClientVersions;
import ac.grim.grimac.api.packet.types.PacketTypes;
import ac.grim.grimac.api.packet.types.client.play.ClientEntityActionPacket;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PostPredictionCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
import ac.grim.grimac.api.packet.types.event.PacketReceiveEvent;
@CheckData(name = "ElytraE", description = "Started gliding while flying", experimental = true)
public class ElytraE extends Check implements PostPredictionCheck {
private boolean setback;
public ElytraE(GrimPlayer player) {
super(player);
}
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (player.getClientVersion().isOlderThanOrEquals(PacketClientVersions.V_1_8)) {
return;
}
if (event.getPacketType() == PacketTypes.Play.Client.ENTITY_ACTION
&& packetFactory.clientEntityAction(event).getAction() == ClientEntityActionPacket.Action.START_FLYING_WITH_ELYTRA
&& player.isFlying
&& flagAndAlert()
) {
setback = true;
if (shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
player.resyncPose();
}
}
}
@Override
public void onPredictionComplete(PredictionComplete predictionComplete) {
if (setback) {
setbackIfAboveSetbackVL();
setback = false;
}
}
}
@@ -1,48 +0,0 @@
package ac.grim.grimac.checks.impl.elytra;
import ac.grim.grimac.api.packet.protocol.PacketClientVersions;
import ac.grim.grimac.api.packet.types.PacketTypes;
import ac.grim.grimac.api.packet.types.client.play.ClientEntityActionPacket;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PostPredictionCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
import ac.grim.grimac.api.packet.types.event.PacketReceiveEvent;
@CheckData(name = "ElytraF", description = "Started gliding while on ground", experimental = true)
public class ElytraF extends Check implements PostPredictionCheck {
private boolean setback;
public ElytraF(GrimPlayer player) {
super(player);
}
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (player.getClientVersion().isOlderThanOrEquals(PacketClientVersions.V_1_8)) {
return;
}
if (event.getPacketType() == PacketTypes.Play.Client.ENTITY_ACTION
&& packetFactory.clientEntityAction(event).getAction() == ClientEntityActionPacket.Action.START_FLYING_WITH_ELYTRA
&& player.clientClaimsLastOnGround
&& flagAndAlert()
) {
setback = true;
if (shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
player.resyncPose();
}
}
}
@Override
public void onPredictionComplete(PredictionComplete predictionComplete) {
if (setback) {
setbackIfAboveSetbackVL();
setback = false;
}
}
}
@@ -1,46 +0,0 @@
package ac.grim.grimac.checks.impl.elytra;
import ac.grim.grimac.api.packet.protocol.PacketClientVersions;
import ac.grim.grimac.api.packet.protocol.potion.PotionTypes;
import ac.grim.grimac.api.packet.types.PacketTypes;
import ac.grim.grimac.api.packet.types.client.play.ClientEntityActionPacket;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PostPredictionCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
import ac.grim.grimac.api.packet.types.event.PacketReceiveEvent;
@CheckData(name = "ElytraG", description = "Started gliding with levitation", experimental = true)
public class ElytraG extends Check implements PostPredictionCheck {
private boolean setback;
public ElytraG(GrimPlayer player) {
super(player);
}
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (event.getPacketType() == PacketTypes.Play.Client.ENTITY_ACTION
&& packetFactory.clientEntityAction(event).getAction() == ClientEntityActionPacket.Action.START_FLYING_WITH_ELYTRA
&& player.getClientVersion().isNewerThanOrEquals(PacketClientVersions.V_1_16)
&& player.compensatedEntities.self.hasPotionEffect(PotionTypes.LEVITATION)
&& flagAndAlert()
) {
setback = true;
if (shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
player.resyncPose();
}
}
}
@Override
public void onPredictionComplete(PredictionComplete predictionComplete) {
if (setback) {
setbackIfAboveSetbackVL();
setback = false;
}
}
}
@@ -1,48 +0,0 @@
package ac.grim.grimac.checks.impl.elytra;
import ac.grim.grimac.api.packet.protocol.PacketClientVersions;
import ac.grim.grimac.api.packet.types.client.play.ClientEntityActionPacket;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PostPredictionCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
import ac.grim.grimac.api.packet.types.event.PacketReceiveEvent;
import ac.grim.grimac.api.packet.types.PacketTypes;
@CheckData(name = "ElytraH", description = "Started gliding in vehicle", experimental = true)
public class ElytraH extends Check implements PostPredictionCheck {
private boolean setback;
public ElytraH(GrimPlayer player) {
super(player);
}
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (player.getClientVersion().isOlderThanOrEquals(PacketClientVersions.V_1_8)) {
return;
}
if (event.getPacketType() == PacketTypes.Play.Client.ENTITY_ACTION
&& packetFactory.clientEntityAction(event).getAction() == ClientEntityActionPacket.Action.START_FLYING_WITH_ELYTRA
&& player.inVehicle()
&& flagAndAlert()
) {
setback = true;
if (shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
player.resyncPose();
}
}
}
@Override
public void onPredictionComplete(PredictionComplete predictionComplete) {
if (setback) {
setbackIfAboveSetbackVL();
setback = false;
}
}
}
@@ -1,45 +0,0 @@
package ac.grim.grimac.checks.impl.elytra;
import ac.grim.grimac.api.packet.protocol.PacketClientVersions;
import ac.grim.grimac.api.packet.types.client.play.ClientEntityActionPacket;
import ac.grim.grimac.api.packet.types.event.PacketReceiveEvent;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PostPredictionCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
import ac.grim.grimac.api.packet.types.PacketTypes;
@CheckData(name = "ElytraI", description = "Started gliding in water", experimental = true)
public class ElytraI extends Check implements PostPredictionCheck {
private boolean setback;
public ElytraI(GrimPlayer player) {
super(player);
}
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (event.getPacketType() == PacketTypes.Play.Client.ENTITY_ACTION
&& packetFactory.clientEntityAction(event).getAction() == ClientEntityActionPacket.Action.START_FLYING_WITH_ELYTRA
&& player.wasTouchingWater
&& player.getClientVersion().isNewerThanOrEquals(PacketClientVersions.V_1_15)
&& flagAndAlert()
) {
setback = true;
if (shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
player.resyncPose();
}
}
}
@Override
public void onPredictionComplete(PredictionComplete predictionComplete) {
if (setback) {
setbackIfAboveSetbackVL();
setback = false;
}
}
}
@@ -1,33 +0,0 @@
package ac.grim.grimac.checks.impl.exploit;
import ac.grim.grimac.api.packet.protocol.version.server.ServerVersions;
import ac.grim.grimac.api.packet.types.PacketTypes;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PacketCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.api.packet.types.event.PacketReceiveEvent;
import ac.grim.grimac.api.packet.types.client.play.ClientTabCompletePacket;
@CheckData(name = "ExploitA", experimental = true)
public class ExploitA extends Check implements PacketCheck {
public ExploitA(GrimPlayer playerData) {
super(playerData);
}
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (event.getPacketType() == PacketTypes.Play.Client.TAB_COMPLETE && ServerVersions.getServerVersion().isNewerThanOrEquals(ServerVersions.V_1_13)) {
ClientTabCompletePacket wrapper = ClientTabCompletePacket.from(event);
String text = wrapper.getText();
if (text.equals("/") || text.trim().isEmpty()) {
if (flagAndAlert("")) {
event.setCancelled(true);
player.onPacketCancel();
}
}
}
}
}
@@ -1,32 +0,0 @@
package ac.grim.grimac.checks.impl.exploit;
import ac.grim.grimac.api.packet.types.PacketTypes;
import ac.grim.grimac.api.packet.types.client.play.ClientEditBookPacket;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PacketCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.api.packet.types.event.PacketReceiveEvent;
@CheckData(name = "ExploitB", description = "Too long book title")
public class ExploitB extends Check implements PacketCheck {
public ExploitB(GrimPlayer playerData) {
super(playerData);
}
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (event.getPacketType() == PacketTypes.Play.Client.EDIT_BOOK) {
final String title = ClientEditBookPacket.from(event).getTitle();
if (title == null) return; // book was not signed
if (title.length() > 15) {
if (flagAndAlert("title=" + title) && shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
}
}
}
}
}
@@ -1,54 +0,0 @@
package ac.grim.grimac.checks.impl.exploit;
import ac.grim.grimac.api.packet.protocol.version.server.ServerVersions;
import ac.grim.grimac.api.packet.types.PacketTypes;
import ac.grim.grimac.api.packet.types.client.play.ClientChatCommandPacket;
import ac.grim.grimac.api.packet.types.client.play.ClientChatCommandUnsignedPacket;
import ac.grim.grimac.api.packet.types.client.play.ClientChatMessagePacket;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PacketCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.api.packet.types.event.PacketReceiveEvent;
// this can false from click events, but I doubt this would actually
// happen unless they're trying to flag, or if the server is set up badly
@CheckData(name = "ExploitC", description = "Invalid chat message")
public class ExploitC extends Check implements PacketCheck {
public ExploitC(GrimPlayer playerData) {
super(playerData);
}
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (event.getPacketType() == PacketTypes.Play.Client.CHAT_MESSAGE) {
String message = ClientChatMessagePacket.from(event).getMessage();
if (message.isEmpty() || !message.trim().equals(message) || message.startsWith("/") && ServerVersions.getServerVersion().isNewerThanOrEquals(ServerVersions.V_1_19)) {
if (flagAndAlert("message=" + message)) {
event.setCancelled(true);
player.onPacketCancel();
}
}
}
if (event.getPacketType() == PacketTypes.Play.Client.CHAT_COMMAND_UNSIGNED) {
String command = "/" + ClientChatCommandUnsignedPacket.from(event).getCommand();
if (!command.stripTrailing().equals(command)) {
if (flagAndAlert("command=" + command)) {
event.setCancelled(true);
player.onPacketCancel();
}
}
}
if (event.getPacketType() == PacketTypes.Play.Client.CHAT_COMMAND) {
String command = "/" + ClientChatCommandPacket.from(event).getCommand();
if (!command.trim().equals(command)) {
if (flagAndAlert("command=" + command)) {
event.setCancelled(true);
player.onPacketCancel();
}
}
}
}
}
@@ -1,60 +0,0 @@
package ac.grim.grimac.checks.impl.misc;
import ac.grim.grimac.GrimAPI;
import ac.grim.grimac.api.packet.types.client.play.ClientPluginMessagePacket;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.type.PacketCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.MessageUtil;
import ac.grim.grimac.api.util.ChatUtil;
import ac.grim.grimac.api.packet.types.event.PacketReceiveEvent;
import ac.grim.grimac.api.packet.protocol.version.server.ServerVersions;
import ac.grim.grimac.api.packet.types.PacketTypes;
import lombok.Getter;
import net.kyori.adventure.text.Component;
public class ClientBrand extends Check implements PacketCheck {
public static final String channel = ServerVersions.getServerVersion().isNewerThanOrEquals(ServerVersions.V_1_13) ? "minecraft:brand" : "MC|Brand";
@Getter
private String brand = "vanilla";
private boolean hasBrand = false;
public ClientBrand(GrimPlayer player) {
super(player);
}
@Override
public void onPacketReceive(final PacketReceiveEvent event) {
if (event.getPacketType() == PacketTypes.Play.Client.PLUGIN_MESSAGE) {
ClientPluginMessagePacket packet = ClientPluginMessagePacket.from(event);
handle(packet.getChannelName(), packet.getData());
} else if (event.getPacketType() == PacketTypes.Configuration.Client.PLUGIN_MESSAGE) {
ac.grim.grimac.api.packet.types.client.config.ClientPluginMessagePacket packet = ac.grim.grimac.api.packet.types.client.config.ClientPluginMessagePacket.from(event);
handle(packet.getChannelName(), packet.getData());
}
}
private void handle(String channel, byte[] data) {
if (!channel.equals(ClientBrand.channel)) return;
if (data.length > 64 || data.length == 0) {
brand = "sent " + data.length + " bytes as brand";
} else if (!hasBrand) {
byte[] minusLength = new byte[data.length - 1];
System.arraycopy(data, 1, minusLength, 0, minusLength.length);
brand = new String(minusLength).replace(" (Velocity)", ""); // removes velocity's brand suffix
brand = ChatUtil.stripColor(brand); // strip color codes from client brand
if (!GrimAPI.INSTANCE.getConfigManager().isIgnoredClient(brand)) {
String message = GrimAPI.INSTANCE.getConfigManager().getConfig().getStringElse("client-brand-format", "%prefix% &f%player% joined using %brand%");
Component component = MessageUtil.replacePlaceholders(player, MessageUtil.miniMessage(message));
GrimAPI.INSTANCE.getAlertManager().sendBrand(component, null);
}
}
hasBrand = true;
}
}
@@ -1,29 +0,0 @@
package ac.grim.grimac.checks.impl.multiactions;
import ac.grim.grimac.api.packet.player.enums.InteractionHand;
import ac.grim.grimac.api.packet.types.client.play.ClientInteractEntityPacket;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PacketCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.api.packet.types.event.PacketReceiveEvent;
import ac.grim.grimac.api.packet.types.PacketTypes;
@CheckData(name = "MultiActionsA", description = "Attacked while using an item", experimental = true)
public class MultiActionsA extends Check implements PacketCheck {
public MultiActionsA(GrimPlayer player) {
super(player);
}
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (player.packetStateData.isSlowedByUsingItem() && (player.packetStateData.lastSlotSelected == player.packetStateData.getSlowedByUsingItemSlot() || player.packetStateData.eatingHand == InteractionHand.OFF_HAND) && event.getPacketType() == PacketTypes.Play.Client.INTERACT_ENTITY) {
if (packetFactory.clientInteractEntity(event).getInteractAction() == ClientInteractEntityPacket.InteractAction.ATTACK) {
if (flagAndAlert() && shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
}
}
}
}
}
@@ -1,39 +0,0 @@
package ac.grim.grimac.checks.impl.multiactions;
import ac.grim.grimac.api.packet.player.enums.DiggingAction;
import ac.grim.grimac.api.packet.player.enums.InteractionHand;
import ac.grim.grimac.api.packet.protocol.PacketClientVersions;
import ac.grim.grimac.api.packet.types.event.PacketReceiveEvent;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PacketCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.api.packet.types.PacketTypes;
import ac.grim.grimac.api.packet.types.client.play.ClientPlayerDiggingPacket;
@CheckData(name = "MultiActionsB", description = "Breaking blocks while using an item", experimental = true)
public class MultiActionsB extends Check implements PacketCheck {
public MultiActionsB(GrimPlayer player) {
super(player);
}
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (player.packetStateData.isSlowedByUsingItem() && (player.packetStateData.lastSlotSelected == player.packetStateData.getSlowedByUsingItemSlot() || player.packetStateData.eatingHand == InteractionHand.OFF_HAND) && event.getPacketType() == PacketTypes.Play.Client.PLAYER_DIGGING) {
// this is vanilla on 1.7
if (player.getClientVersion().isOlderThanOrEquals(PacketClientVersions.V_1_7_10)) {
return;
}
final ClientPlayerDiggingPacket packet = packetFactory.clientPlayerDigging(event);
if (packet.getDiggingAction() == DiggingAction.START_DIGGING || packet.getDiggingAction() == DiggingAction.CANCELLED_DIGGING || packet.getDiggingAction() == DiggingAction.FINISHED_DIGGING) {
if (flagAndAlert() && shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
player.resyncPosition(packet.getBlockPosition());
}
}
}
}
}
Loaded 100 of 714 files, more files were not shown because too many files have changed in this diff. Show more