Compare commits

..
3 Commits
Author SHA1 Message Date
Axionize 187ef354e0 Deobfuscate and document purpose of misunderstood code in traverseBlocks() 2024-08-24 19:05:28 -04:00
Axionize b6cc8ce57c Merge reach wall hit check
(cherry picked from commit 335c33b109)
2024-08-24 19:05:26 -04:00
Axionize e07f02a05f Add .gradle.yml 2024-08-24 19:05:24 -04:00
241 changed files with 2129 additions and 7535 deletions

No files matched your search

-71
View File
@@ -1,71 +0,0 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches: [ master ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ master ]
schedule:
- cron: '30 15 * * 2'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'java' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
# Learn more:
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
steps:
- name: Checkout repository
uses: actions/checkout@v4
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v3
# ℹ️ Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
-38
View File
@@ -1,38 +0,0 @@
# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.
# This workflow will build a package using Gradle and then publish it to GitHub packages when a release is created
# For more information see: https://github.com/actions/setup-java/blob/main/docs/advanced-usage.md#Publishing-using-gradle
name: Gradle Package
on:
[push, workflow_dispatch]
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
settings-path: ${{ github.workspace }} # location for the settings.xml file
- name: Build with Gradle
uses: gradle/actions/setup-gradle@v3
with:
arguments: build
- name: Upload a Build Artifact
uses: actions/upload-artifact@v4
with:
path: /home/runner/work/Grim/Grim/build/libs/**.jar
+95
View File
@@ -0,0 +1,95 @@
# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.
# This workflow will build a Java project with Gradle and cache/restore any dependencies to improve the workflow execution time
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-gradle
name: Java CI with Gradle
on:
push:
branches: [ "2.0" ]
pull_request:
branches: [ "2.0" ]
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
- name: Set up JDK 21
uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'temurin'
# Configure Gradle for optimal use in GitHub Actions, including caching of downloaded dependencies.
# See: https://github.com/gradle/actions/blob/main/setup-gradle/README.md
- name: Setup Gradle
uses: gradle/actions/setup-gradle@dbbdc275be76ac10734476cc723d82dfe7ec6eda
- name: Build with Gradle Wrapper
run: ./gradlew build
- name: Get current datetime
id: datetime
run: echo "datetime=$(date +'%Y-%m-%d-%H%M%S')" >> $GITHUB_OUTPUT
- name: Delete previous dev release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
previous_release_id=$(curl -s -H "Authorization: token $GITHUB_TOKEN" \
"https://api.github.com/repos/${{ github.repository }}/releases/tags/dev" | jq -r .id)
if [ "$previous_release_id" != "null" ]; then
curl -X DELETE -H "Authorization: token $GITHUB_TOKEN" \
"https://api.github.com/repos/${{ github.repository }}/releases/$previous_release_id"
git push --delete origin dev || true
fi
- name: Create new dev release
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: dev
release_name: Dev Build ${{ steps.datetime.outputs.datetime }}
body: |
This is an automated dev build of the master branch.
Build date: ${{ steps.datetime.outputs.datetime }}
draft: false
prerelease: true
- name: Find JAR file
id: find_jar
run: |
JAR_PATH=$(find ./build/libs -name "*.jar" | head -n 1)
echo "jar_path=$JAR_PATH" >> $GITHUB_OUTPUT
echo "jar_name=$(basename $JAR_PATH)" >> $GITHUB_OUTPUT
- name: Upload artifact
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ${{ steps.find_jar.outputs.jar_path }}
asset_name: GrimAC-${{ steps.datetime.outputs.datetime }}-dev.jar
asset_content_type: application/java-archive
dependency-submission:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
- name: Set up JDK 21
uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'temurin'
+1 -1
View File
@@ -7,7 +7,7 @@ GrimAC is an open source Minecraft anticheat designed for 1.21 and supports 1.8-
### 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
3. gradlew build
4. The final jar is located in build/libs
### API Information
+26 -90
View File
@@ -6,35 +6,13 @@ plugins {
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"
}
spotless {
java {
endWithNewline()
indentWithSpaces(4)
removeUnusedImports()
trimTrailingWhitespace()
targetExclude("build/generated/**/*")
}
kotlinGradle {
endWithNewline()
indentWithSpaces(4)
trimTrailingWhitespace()
}
}
group = "ac.grim.grimac"
version = "2.3.69"
version = "2.3.67"
description = "Libre simulation anticheat designed for 1.21 with 1.8-1.21 support, powered by PacketEvents 2.0."
// Set to false for debug builds
// You cannot live reload classes if the jar relocates dependencies
// Checks Project properties -> environment variable -> defaults true
val relocate: Boolean = project.findProperty("relocate")?.toString()?.toBoolean()
?: System.getenv("RELOCATE_JAR")?.toBoolean()
?: true
java.sourceCompatibility = JavaVersion.VERSION_1_8
java.targetCompatibility = JavaVersion.VERSION_1_8
repositories {
mavenLocal()
@@ -56,20 +34,19 @@ repositories {
}
dependencies {
implementation("com.github.retrooper:packetevents-spigot:2.6.1-SNAPSHOT")
implementation("com.github.retrooper:packetevents-spigot:2.5.0-SNAPSHOT")
implementation("co.aikar:acf-paper:0.5.1-SNAPSHOT")
implementation("club.minnced:discord-webhooks:0.8.0") // Newer versions include kotlin-stdlib, which leads to incompatibility with plugins that use Kotlin
implementation("it.unimi.dsi:fastutil:8.5.15")
implementation("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("com.github.grimanticheat:grimapi:1193c4fa41")
// Used for local testing: implementation("ac.grim.grimac:grimapi:1.0")
implementation("org.jetbrains:annotations:24.1.0")
compileOnly("org.geysermc.floodgate:api:2.2.3-SNAPSHOT")
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.1.1-SNAPSHOT")
compileOnly("com.viaversion:viaversion-api:4.9.4-SNAPSHOT")
//
compileOnly("io.netty:netty-all:4.1.85.Final")
}
@@ -78,7 +55,6 @@ bukkit {
name = "GrimAC"
author = "GrimAC"
main = "ac.grim.grimac.GrimAC"
website = "https://grim.ac/"
apiVersion = "1.13"
foliaSupported = true
@@ -144,7 +120,6 @@ bukkit {
tasks.build {
dependsOn(tasks.shadowJar)
dependsOn(tasks.spotlessApply)
}
tasks.withType<JavaCompile> {
@@ -155,63 +130,24 @@ publishing.publications.create<MavenPublication>("maven") {
artifact(tasks["shadowJar"])
}
java {
sourceSets {
create("java18") {
java {
srcDirs("src/main/java18")
}
compileClasspath += main.get().output
runtimeClasspath += main.get().output
}
}
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
toolchain {
languageVersion.set(JavaLanguageVersion.of(21))
}
}
tasks.withType<JavaCompile> {
if (name == "compileJava18Java") {
options.compilerArgs.addAll(listOf("--add-modules", "jdk.incubator.vector", "-Xlint:unchecked"))
sourceCompatibility = "18"
targetCompatibility = "18"
}
}
tasks.withType<Jar> {
manifest {
attributes("Multi-Release" to "true")
}
duplicatesStrategy = DuplicatesStrategy.INCLUDE // Important for multi-release JARs
from(sourceSets.main.get().output)
from(sourceSets.getByName("java18").output) {
into("META-INF/versions/18")
}
}
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")
}
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")
}
Binary file not shown.
+1 -1
View File
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
@@ -1,149 +0,0 @@
package ac.grim.grimac.utils.vector;
import jdk.incubator.vector.*;
public final class SIMDVector3D implements Vector3D {
private static final VectorSpecies<Double> SPECIES = DoubleVector.SPECIES_PREFERRED;
private static final VectorMask<Double> LENGTH_3_ARRAY_MASK = SPECIES.indexInRange(0, 3);
private static final VectorShuffle<Double> CROSS_PRODUCT_SHUFFLE1 = VectorShuffle.fromArray(SPECIES, new int[]{1, 2, 0, 3}, 0);
private static final VectorShuffle<Double> CROSS_PRODUCT_SHUFFLE2 = VectorShuffle.fromArray(SPECIES, new int[]{2, 0, 1, 3}, 0);
private static final DoubleVector FOURTH_COMPONENT_ZERO = DoubleVector.zero(SPECIES).withLane(3, 1.0);
private DoubleVector vector;
public SIMDVector3D(double x, double y, double z) {
this.vector = DoubleVector.fromArray(SPECIES, new double[]{x, y, z, 0}, 0);
}
@Override
public double getX() {
return this.vector.lane(0);
}
@Override
public double getY() {
return this.vector.lane(1);
}
@Override
public double getZ() {
return this.vector.lane(2);
}
@Override
public Vector3D setX(double x) {
this.vector = this.vector.withLane(0, x);
return this;
}
@Override
public Vector3D setY(double y) {
this.vector = this.vector.withLane(1, y);
return this;
}
@Override
public Vector3D setZ(double z) {
this.vector = this.vector.withLane(2, z);
return this;
}
@Override
public double length() {
return Math.sqrt(lengthSquared());
}
@Override
public double lengthSquared() {
return this.vector.mul(this.vector).reduceLanes(VectorOperators.ADD);
}
@Override
public Vector3D multiply(double m) {
this.vector = this.vector.mul(m);
return this;
}
@Override
public Vector3D normalize() {
// DoubleVector squared = this.vector.mul(this.vector);
// double sum = squared.reduceLanes(VectorOperators.ADD);
// double length = Math.sqrt(sum);
// this.vector = this.vector.div(length);
// return this;
double length = length();
this.vector = this.vector.div(length).blend(FOURTH_COMPONENT_ZERO, LENGTH_3_ARRAY_MASK.not());
return this;
}
@Override
public Vector3D crossProduct(Vector3D other) {
SIMDVector3D o = (SIMDVector3D) other;
DoubleVector tmp0 = this.vector.rearrange(CROSS_PRODUCT_SHUFFLE1);
DoubleVector tmp1 = o.vector.rearrange(CROSS_PRODUCT_SHUFFLE2);
DoubleVector tmp2 = tmp0.mul(o.vector);
DoubleVector tmp3 = tmp0.mul(tmp1);
DoubleVector tmp4 = tmp2.rearrange(CROSS_PRODUCT_SHUFFLE1);
this.vector = tmp3.sub(tmp4).blend(FOURTH_COMPONENT_ZERO, LENGTH_3_ARRAY_MASK.not());
return this;
}
@Override
public Vector3D add(Vector3D o) {
this.vector = this.vector.add(((SIMDVector3D) o).vector);
return this;
}
@Override
public Vector3D subtract(Vector3D o) {
this.vector = this.vector.sub(((SIMDVector3D) o).vector);
return this;
}
@Override
public Vector3D multiply(Vector3D o) {
this.vector = this.vector.mul(((SIMDVector3D) o).vector);
return this;
}
@Override
public double distance(Vector3D o) {
return Math.sqrt(distanceSquared(o));
}
@Override
public double distanceSquared(Vector3D o) {
DoubleVector diff = this.vector.sub(((SIMDVector3D) o).vector);
return diff.mul(diff).reduceLanes(VectorOperators.ADD);
}
@Override
public double dot(Vector3D o) {
return this.vector.mul(((SIMDVector3D) o).vector).reduceLanes(VectorOperators.ADD);
}
@Override
public Vector3D clone() {
// I think this makes a shallow clone if I do it like this?
// try {
// return (Vector3D) super.clone();
// } catch (CloneNotSupportedException e) {
// throw new Error(e);
// }
try {
SIMDVector3D cloned = (SIMDVector3D) super.clone();
// Create a new DoubleVector with the same values
cloned.vector = DoubleVector.fromArray(SPECIES,
new double[]{this.getX(), this.getY(), this.getZ(), 0}, 0);
return cloned;
} catch (CloneNotSupportedException e) {
throw new Error(e);
}
}
}
@@ -1,13 +0,0 @@
package ac.grim.grimac.utils.vector;
import jdk.incubator.vector.VectorMask;
import jdk.incubator.vector.VectorSpecies;
import jdk.incubator.vector.DoubleVector;
public class VectorOperationsJava18 implements VectorOperations {
@Override
public Vector3D newVector(double x, double y, double z) {
return new SIMDVector3D(x, y, z);
}
}
+6 -2
View File
@@ -1,16 +1,17 @@
package ac.grim.grimac;
import ac.grim.grimac.api.GrimAbstractAPI;
import ac.grim.grimac.manager.*;
import ac.grim.grimac.manager.config.BaseConfigManager;
import ac.grim.grimac.utils.anticheat.PlayerDataManager;
import lombok.Getter;
import org.bukkit.Bukkit;
import org.bukkit.plugin.ServicePriority;
import org.bukkit.plugin.java.JavaPlugin;
@Getter
public enum GrimAPI {
INSTANCE;
private final BaseConfigManager configManager = new BaseConfigManager();
private final AlertManagerImpl alertManager = new AlertManagerImpl();
private final SpectateManager spectateManager = new SpectateManager();
private final DiscordManager discordManager = new DiscordManager();
@@ -18,10 +19,12 @@ public enum GrimAPI {
private final TickManager tickManager = new TickManager();
private final GrimExternalAPI externalAPI = new GrimExternalAPI(this);
private InitManager initManager;
private ConfigManager configManager;
private JavaPlugin plugin;
public void load(final JavaPlugin plugin) {
this.plugin = plugin;
this.configManager = new ConfigManager();
initManager = new InitManager();
initManager.load();
}
@@ -29,6 +32,7 @@ public enum GrimAPI {
public void start(final JavaPlugin plugin) {
this.plugin = plugin;
initManager.start();
Bukkit.getServicesManager().register(GrimAbstractAPI.class, externalAPI, plugin, ServicePriority.Normal);
}
public void stop(final JavaPlugin plugin) {
+37 -131
View File
@@ -1,34 +1,25 @@
package ac.grim.grimac;
import ac.grim.grimac.api.AbstractCheck;
import ac.grim.grimac.api.GrimAbstractAPI;
import ac.grim.grimac.api.GrimUser;
import ac.grim.grimac.api.alerts.AlertManager;
import ac.grim.grimac.api.config.ConfigManager;
import ac.grim.grimac.api.events.GrimReloadEvent;
import ac.grim.grimac.manager.config.ConfigManagerFileImpl;
import ac.grim.grimac.manager.init.Initable;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.LogUtil;
import ac.grim.grimac.utils.common.ConfigReloadObserver;
import com.github.retrooper.packetevents.netty.channel.ChannelHelper;
import io.github.retrooper.packetevents.util.SpigotReflectionUtil;
import io.github.retrooper.packetevents.util.folia.FoliaScheduler;
import lombok.Getter;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.entity.Player;
import org.bukkit.plugin.PluginDescriptionFile;
import org.bukkit.plugin.ServicePriority;
import org.jetbrains.annotations.Nullable;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
//This is used for grim's external API. It has its own class just for organization.
public class GrimExternalAPI implements GrimAbstractAPI, ConfigReloadObserver, Initable {
public class GrimExternalAPI implements GrimAbstractAPI, Initable {
private final GrimAPI api;
@@ -50,8 +41,7 @@ public class GrimExternalAPI implements GrimAbstractAPI, ConfigReloadObserver, I
@Getter
private final Map<String, Function<GrimUser, String>> variableReplacements = new ConcurrentHashMap<>();
@Getter
private final Map<String, String> staticReplacements = new ConcurrentHashMap<>();
@Getter private final Map<String, String> staticReplacements = new ConcurrentHashMap<>();
public String replaceVariables(GrimUser user, String content, boolean colors) {
if (colors) content = ChatColor.translateAlternateColorCodes('&', content);
@@ -66,20 +56,12 @@ public class GrimExternalAPI implements GrimAbstractAPI, ConfigReloadObserver, I
@Override
public void registerVariable(String string, Function<GrimUser, String> replacement) {
if (replacement == null) {
variableReplacements.remove(string);
} else {
variableReplacements.put(string, replacement);
}
variableReplacements.put(string, replacement);
}
@Override
public void registerVariable(String variable, String replacement) {
if (replacement == null) {
staticReplacements.remove(variable);
} else {
staticReplacements.put(variable, replacement);
}
staticReplacements.put(variable, replacement);
}
@Override
@@ -88,20 +70,34 @@ public class GrimExternalAPI implements GrimAbstractAPI, ConfigReloadObserver, I
return description.getVersion();
}
private final Map<String, Function<Object, Object>> functions = new ConcurrentHashMap<>();
@Override
public void registerFunction(String key, Function<Object, Object> function) {
if (function == null) {
functions.remove(key);
} else {
functions.put(key, function);
}
}
@Override
public Function<Object, Object> getFunction(String key) {
return functions.get(key);
return null;
}
@Override
public void reload() {
GrimAPI.INSTANCE.getConfigManager().reload();
//Reload checks for all players
for (GrimPlayer grimPlayer : GrimAPI.INSTANCE.getPlayerDataManager().getEntries()) {
ChannelHelper.runInEventLoop(grimPlayer.user.getChannel(), () -> {
grimPlayer.onReload();
grimPlayer.updatePermissions();
grimPlayer.punishmentManager.reload();
for (AbstractCheck value : grimPlayer.checkManager.allChecks.values()) {
value.reload();
}
});
}
//Restart
GrimAPI.INSTANCE.getDiscordManager().start();
GrimAPI.INSTANCE.getSpectateManager().start();
GrimAPI.INSTANCE.getExternalAPI().start();
}
@Override
@@ -109,107 +105,17 @@ public class GrimExternalAPI implements GrimAbstractAPI, ConfigReloadObserver, I
return GrimAPI.INSTANCE.getAlertManager();
}
@Override
public ConfigManager getConfigManager() {
return configManager;
}
private ConfigManager configManager = null;
private final ConfigManagerFileImpl configManagerFile = new ConfigManagerFileImpl();
private boolean started = false;
// on load, load the config & register the service
public void load() {
reload(configManagerFile);
Bukkit.getServicesManager().register(GrimAbstractAPI.class, this, GrimAPI.INSTANCE.getPlugin(), ServicePriority.Normal);
}
// handles any config loading that's needed to be done after load
@Override
public void start() {
started = true;
try {
GrimAPI.INSTANCE.getConfigManager().start();
} catch (Exception e) {
e.printStackTrace();
}
variableReplacements.put("%player%", GrimUser::getName);
variableReplacements.put("%uuid%", user -> user.getUniqueId().toString());
variableReplacements.put("%ping%", user -> user.getTransactionPing() + "");
variableReplacements.put("%brand%", GrimUser::getBrand);
variableReplacements.put("%h_sensitivity%", user -> ((int) Math.round(user.getHorizontalSensitivity() * 200)) + "");
variableReplacements.put("%v_sensitivity%", user -> ((int) Math.round(user.getVerticalSensitivity() * 200)) + "");
variableReplacements.put("%fast_math%", user -> !user.isVanillaMath() + "");
variableReplacements.put("%tps%", user -> String.format("%.2f", SpigotReflectionUtil.getTPS()));
variableReplacements.put("%version%", GrimUser::getVersionName);
variableReplacements.put("%prefix%", user -> ChatColor.translateAlternateColorCodes('&', GrimAPI.INSTANCE.getConfigManager().getConfig().getStringElse("prefix", "&bGrim &8»")));
}
@Override
public void reload(ConfigManager config) {
if (config.isLoadedAsync() && started) {
FoliaScheduler.getAsyncScheduler().runNow(GrimAPI.INSTANCE.getPlugin(),
o -> successfulReload(config));
} else {
successfulReload(config);
}
}
@Override
public CompletableFuture<Boolean> reloadAsync(ConfigManager config) {
if (config.isLoadedAsync() && started) {
CompletableFuture<Boolean> future = new CompletableFuture<>();
FoliaScheduler.getAsyncScheduler().runNow(GrimAPI.INSTANCE.getPlugin(),
o -> future.complete(successfulReload(config)));
return future;
}
return CompletableFuture.completedFuture(successfulReload(config));
}
private boolean successfulReload(ConfigManager config) {
try {
config.reload();
GrimAPI.INSTANCE.getConfigManager().load(config);
if (started) GrimAPI.INSTANCE.getConfigManager().start();
onReload(config);
if (started) FoliaScheduler.getAsyncScheduler().runNow(GrimAPI.INSTANCE.getPlugin(),
o -> Bukkit.getPluginManager().callEvent(new GrimReloadEvent(true)));
return true;
} catch (Exception e) {
e.printStackTrace();
}
if (started) FoliaScheduler.getAsyncScheduler().runNow(GrimAPI.INSTANCE.getPlugin(),
o -> Bukkit.getPluginManager().callEvent(new GrimReloadEvent(false)));
return false;
}
@Override
public void onReload(ConfigManager newConfig) {
if (newConfig == null) {
LogUtil.warn("ConfigManager not set. Using default config file manager.");
configManager = configManagerFile;
} else {
configManager = newConfig;
}
// Update variables
updateVariables();
// Restart
GrimAPI.INSTANCE.getDiscordManager().start();
GrimAPI.INSTANCE.getSpectateManager().start();
// Don't reload players if the plugin hasn't started yet
if (!started) return;
// Reload checks for all players
for (GrimPlayer grimPlayer : GrimAPI.INSTANCE.getPlayerDataManager().getEntries()) {
ChannelHelper.runInEventLoop(grimPlayer.user.getChannel(), () -> {
grimPlayer.updatePermissions();
grimPlayer.reload(configManager);
});
}
}
private void updateVariables() {
variableReplacements.putIfAbsent("%player%", GrimUser::getName);
variableReplacements.putIfAbsent("%uuid%", user -> user.getUniqueId().toString());
variableReplacements.putIfAbsent("%ping%", user -> user.getTransactionPing() + "");
variableReplacements.putIfAbsent("%brand%", GrimUser::getBrand);
variableReplacements.putIfAbsent("%h_sensitivity%", user -> ((int) Math.round(user.getHorizontalSensitivity() * 200)) + "");
variableReplacements.putIfAbsent("%v_sensitivity%", user -> ((int) Math.round(user.getVerticalSensitivity() * 200)) + "");
variableReplacements.putIfAbsent("%fast_math%", user -> !user.isVanillaMath() + "");
variableReplacements.putIfAbsent("%tps%", user -> String.format("%.2f", SpigotReflectionUtil.getTPS()));
variableReplacements.putIfAbsent("%version%", GrimUser::getVersionName);
// static variables
staticReplacements.putIfAbsent("%prefix%", ChatColor.translateAlternateColorCodes('&', GrimAPI.INSTANCE.getConfigManager().getPrefix()));
staticReplacements.putIfAbsent("%grim_version%", getGrimVersion());
}
}
+13 -34
View File
@@ -2,20 +2,18 @@ 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.events.FlagEvent;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.common.ConfigReloadObserver;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.protocol.packettype.PacketTypeCommon;
import io.github.retrooper.packetevents.util.folia.FoliaScheduler;
import github.scarsz.configuralize.DynamicConfig;
import lombok.Getter;
import lombok.Setter;
import org.bukkit.Bukkit;
// Class from https://github.com/Tecnio/AntiCheatBase/blob/master/src/main/java/me/tecnio/anticheat/check/Check.java
@Getter
public class Check implements AbstractCheck, ConfigReloadObserver {
public class Check implements AbstractCheck {
protected final GrimPlayer player;
public double violations;
@@ -25,13 +23,11 @@ public class Check implements AbstractCheck, ConfigReloadObserver {
private String checkName;
private String configName;
private String alternativeName;
private String displayName;
private String description;
private boolean experimental;
@Setter
private boolean isEnabled;
private boolean exempted;
@Override
public boolean isExperimental() {
@@ -54,21 +50,13 @@ public class Check implements AbstractCheck, ConfigReloadObserver {
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 && !exempted;
}
public void updateExempted() {
if (player.bukkitPlayer == null || checkName == null) return;
FoliaScheduler.getEntityScheduler().run(player.bukkitPlayer, GrimAPI.INSTANCE.getPlugin(),
t -> exempted = player.bukkitPlayer.hasPermission("grim.exempt." + checkName.toLowerCase()),
() -> {});
return isEnabled && !player.disableGrim && !player.noModifyPacketPermission;
}
public final boolean flagAndAlert(String verbose) {
@@ -84,7 +72,7 @@ public class Check implements AbstractCheck, ConfigReloadObserver {
}
public final boolean flag() {
if (player.disableGrim || (experimental && !player.isExperimentalChecks()) || exempted)
if (player.disableGrim || (experimental && !GrimAPI.INSTANCE.getConfigManager().isExperimentalChecks()))
return false; // Avoid calling event if disabled
FlagEvent event = new FlagEvent(player, this);
@@ -110,26 +98,21 @@ public class Check implements AbstractCheck, ConfigReloadObserver {
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);
public void reload() {
decay = getConfig().getDoubleElse(configName + ".decay", decay);
setbackVL = getConfig().getDoubleElse(configName + ".setbackvl", setbackVL);
if (setbackVL == -1) setbackVL = Double.MAX_VALUE;
updateExempted();
onReload(configuration);
}
@Override
public void onReload(ConfigManager config) {
}
public boolean alert(String verbose) {
return player.punishmentManager.handleAlert(player, verbose, this);
}
public DynamicConfig getConfig() {
return GrimAPI.INSTANCE.getConfigManager().getConfig();
}
public boolean setbackIfAboveSetbackVL() {
if (getViolations() > setbackVL) {
return player.getSetbackTeleportUtil().executeViolationSetback();
@@ -150,9 +133,5 @@ public class Check implements AbstractCheck, ConfigReloadObserver {
packetType == PacketType.Play.Client.WINDOW_CONFIRMATION;
}
@Override
public void reload() {
reload(GrimAPI.INSTANCE.getConfigManager().getConfig());
}
}
@@ -11,8 +11,7 @@ import ac.grim.grimac.utils.anticheat.update.RotationUpdate;
// It works on clients who % 360 their rotation.
@CheckData(name = "AimModulo360", decay = 0.005)
public class AimModulo360 extends Check implements RotationCheck {
private float lastDeltaYaw;
float lastDeltaYaw;
public AimModulo360(GrimPlayer playerData) {
super(playerData);
@@ -20,17 +19,12 @@ public class AimModulo360 extends Check implements RotationCheck {
@Override
public void process(final RotationUpdate rotationUpdate) {
if (player.packetStateData.lastPacketWasTeleport) {
lastDeltaYaw = rotationUpdate.getDeltaXRot();
return;
}
lastDeltaYaw = rotationUpdate.getDeltaXRot();
if (player.packetStateData.lastPacketWasTeleport) return;
if (player.xRot < 360 && player.xRot > -360 && Math.abs(rotationUpdate.getDeltaXRot()) > 320 && Math.abs(lastDeltaYaw) < 30) {
flagAndAlert();
} else {
reward();
}
lastDeltaYaw = rotationUpdate.getDeltaXRot();
}
}
@@ -81,4 +81,4 @@ public class AimProcessor extends Check implements RotationCheck {
double var9 = Math.cbrt(var11);
return (var9 - 0.2f) / 0.6f;
}
}
}
@@ -1,4 +1,4 @@
package ac.grim.grimac.checks.impl.packetorder;
package ac.grim.grimac.checks.impl.badpackets;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
@@ -10,16 +10,17 @@ import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientInteractEntity;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerFlying;
@CheckData(name = "PacketOrderB")
public class PacketOrderB extends Check implements PacketCheck {
@CheckData(name = "BadPacketsH")
public class BadPacketsH extends Check implements PacketCheck {
// 1.9 packet order: INTERACT -> ANIMATION
// 1.8 packet order: ANIMATION -> INTERACT
// I personally think 1.8 made much more sense. You swing and THEN you hit!
private boolean sentAnimation = player.getClientVersion().isNewerThan(ClientVersion.V_1_8);
public PacketOrderB(final GrimPlayer player) {
public BadPacketsH(final GrimPlayer player) {
super(player);
}
@@ -1,4 +1,4 @@
package ac.grim.grimac.checks.impl.packetorder;
package ac.grim.grimac.checks.impl.badpackets;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
@@ -9,33 +9,25 @@ import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.player.InteractionHand;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientInteractEntity;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerFlying;
@CheckData(name = "PacketOrderC", experimental = true)
public class PacketOrderC extends Check implements PacketCheck {
public PacketOrderC(final GrimPlayer player) {
@CheckData(name = "BadPacketsM", experimental = true)
public class BadPacketsM extends Check implements PacketCheck {
public BadPacketsM(final GrimPlayer player) {
super(player);
}
// 1.7 players do not send INTERACT_AT, so we cannot check them
private final boolean exempt = player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_7_10);
private boolean sentInteractAt = false;
private int requiredEntity;
private InteractionHand requiredHand;
private boolean requiredSneaking;
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (exempt) {
return;
}
if (event.getPacketType() == PacketType.Play.Client.INTERACT_ENTITY && !exempt) {
if (event.getPacketType() == PacketType.Play.Client.INTERACT_ENTITY) {
final WrapperPlayClientInteractEntity packet = new WrapperPlayClientInteractEntity(event);
final WrapperPlayClientInteractEntity wrapper = new WrapperPlayClientInteractEntity(event);
final PacketEntity entity = player.compensatedEntities.entityMap.get(packet.getEntityId());
final PacketEntity entity = player.compensatedEntities.entityMap.get(wrapper.getEntityId());
// For armor stands, vanilla clients send:
// - when renaming the armor stand or in spectator mode: INTERACT_AT + INTERACT
@@ -43,49 +35,27 @@ public class PacketOrderC extends Check implements PacketCheck {
// Just exempt armor stands to be safe
if (entity != null && entity.getType() == EntityTypes.ARMOR_STAND) return;
final boolean sneaking = packet.isSneaking().orElse(false);
switch (packet.getAction()) {
switch (wrapper.getAction()) {
// INTERACT_AT then INTERACT
case INTERACT:
if (!sentInteractAt) {
if (flagAndAlert("Skipped Interact-At") && shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
}
} else if (packet.getEntityId() != requiredEntity || packet.getHand() != requiredHand || sneaking != requiredSneaking) {
String verbose = "requiredEntity=" + requiredEntity + ", entity=" + packet.getEntityId()
+ ", requiredHand=" + requiredHand + ", hand=" + packet.getHand()
+ ", requiredSneaking=" + requiredSneaking + ", sneaking=" + sneaking;
if (flagAndAlert(verbose) && shouldModifyPackets()) {
if (flagAndAlert("Missed Interact-At") && shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
}
}
sentInteractAt = false;
break;
case INTERACT_AT:
if (sentInteractAt) {
if (flagAndAlert("Skipped Interact") && shouldModifyPackets()) {
if (flagAndAlert("Missed Interact") && shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
}
}
requiredHand = packet.getHand();
requiredEntity = packet.getEntityId();
requiredSneaking = sneaking;
sentInteractAt = true;
break;
}
}
if (WrapperPlayClientPlayerFlying.isFlying(event.getPacketType())) {
if (sentInteractAt) {
sentInteractAt = false;
flagAndAlert("Skipped Interact (Tick)");
}
}
}
}
@@ -6,6 +6,7 @@ import ac.grim.grimac.checks.type.PacketCheck;
import ac.grim.grimac.player.GrimPlayer;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.protocol.item.ItemStack;
import com.github.retrooper.packetevents.protocol.item.type.ItemType;
import com.github.retrooper.packetevents.protocol.item.type.ItemTypes;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
@@ -4,6 +4,7 @@ import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PacketCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.nmsutil.BlockBreakSpeed;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.protocol.item.type.ItemTypes;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
@@ -24,16 +25,17 @@ public class BadPacketsX extends Check implements PacketCheck {
if (dig.getAction() != DiggingAction.START_DIGGING && dig.getAction() != DiggingAction.FINISHED_DIGGING)
return;
// the block does not have a hitbox
boolean invalid = (block == StateTypes.LIGHT && !(player.getInventory().getHeldItem().is(ItemTypes.LIGHT) || player.getInventory().getOffHand().is(ItemTypes.LIGHT)))
final boolean invalid;
if (dig.getAction() == DiggingAction.FINISHED_DIGGING) {
invalid = BlockBreakSpeed.getBlockDamage(player, dig.getBlockPosition()) >= 1 || block.getHardness() == -1.0f;
} else invalid = (block == StateTypes.LIGHT && !(player.getInventory().getHeldItem().is(ItemTypes.LIGHT) || player.getInventory().getOffHand().is(ItemTypes.LIGHT)))
|| block.isAir()
|| block == StateTypes.WATER
|| block == StateTypes.LAVA
|| block == StateTypes.BUBBLE_COLUMN
|| block == StateTypes.MOVING_PISTON
|| (block == StateTypes.FIRE && noFireHitbox)
// or the client claims to have broken an unbreakable block
|| block.getHardness() == -1.0f && dig.getAction() == DiggingAction.FINISHED_DIGGING;
|| (block == StateTypes.FIRE && noFireHitbox);
if (invalid && flagAndAlert("block=" + block.getName() + ", type=" + dig.getAction()) && shouldModifyPackets()) {
event.setCancelled(true);
@@ -4,10 +4,7 @@ import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PacketCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.MessageUtil;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.player.DiggingAction;
import com.github.retrooper.packetevents.util.Vector3i;
@@ -24,7 +21,7 @@ public class BadPacketsZ extends Check implements PacketCheck {
private boolean lastBlockWasInstantBreak = false;
private Vector3i lastBlock, lastCancelledBlock, lastLastBlock = null;
private final int exemptedY = player.getClientVersion().isOlderThan(ClientVersion.V_1_8) ? 255 : (PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_14) ? -1 : 4095);
private final int exemptedY = player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_8) ? 4095 : 255;
// The client sometimes sends a wierd cancel packet
private boolean shouldExempt(final Vector3i pos) {
@@ -36,10 +33,18 @@ public class BadPacketsZ extends Check implements PacketCheck {
if (player.getClientVersion().isOlderThan(ClientVersion.V_1_14_4) && pos.y != exemptedY)
return false;
// the client only sends this packet if the last block was an instant break
if (getBlockDamage(player, lastBlock) < 1)
return false;
// and if this block is not an instant break
return player.getClientVersion().isOlderThan(ClientVersion.V_1_14_4) || getBlockDamage(player, pos) < 1;
}
private String formatted(Vector3i vec) {
return vec == null ? "null" : vec.x + ", " + vec.y + ", " + vec.z;
}
public void handle(PacketReceiveEvent event, WrapperPlayClientPlayerDigging dig) {
if (dig.getAction() == DiggingAction.START_DIGGING) {
final Vector3i pos = dig.getBlockPosition();
@@ -54,7 +59,7 @@ public class BadPacketsZ extends Check implements PacketCheck {
final Vector3i pos = dig.getBlockPosition();
if (shouldExempt(pos)) {
lastCancelledBlock = pos;
lastCancelledBlock = null;
lastLastBlock = null;
lastBlock = null;
return;
@@ -63,7 +68,7 @@ public class BadPacketsZ extends Check implements PacketCheck {
if (!pos.equals(lastBlock)) {
// https://github.com/GrimAnticheat/Grim/issues/1512
if (player.getClientVersion().isOlderThan(ClientVersion.V_1_14_4) || (!lastBlockWasInstantBreak && pos.equals(lastCancelledBlock))) {
if (flagAndAlert("action=CANCELLED_DIGGING" + ", last=" + MessageUtil.toUnlabledString(lastBlock) + ", pos=" + MessageUtil.toUnlabledString(pos))) {
if (flagAndAlert("action=CANCELLED_DIGGING" + ", last=" + formatted(lastBlock) + ", pos=" + formatted(pos))) {
if (shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
@@ -84,7 +89,7 @@ public class BadPacketsZ extends Check implements PacketCheck {
// 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(ClientVersion.V_1_14_4)) && !pos.equals(lastBlock)) {
if (flagAndAlert("action=FINISHED_DIGGING" + ", last=" + MessageUtil.toUnlabledString(lastBlock) + ", pos=" + MessageUtil.toUnlabledString(pos))) {
if (flagAndAlert("action=FINISHED_DIGGING" + ", last=" + formatted(lastBlock) + ", pos=" + formatted(pos))) {
if (shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
@@ -1,71 +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.PostPredictionCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientInteractEntity;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerFlying;
import java.util.ArrayList;
@CheckData(name = "MultiInteractA", experimental = true)
public class MultiInteractA extends Check implements PostPredictionCheck {
public MultiInteractA(final GrimPlayer player) {
super(player);
}
private final ArrayList<String> flags = new ArrayList<>();
private int lastEntity;
private boolean lastSneaking;
private boolean hasInteracted = false;
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (event.getPacketType() == PacketType.Play.Client.INTERACT_ENTITY) {
WrapperPlayClientInteractEntity packet = new WrapperPlayClientInteractEntity(event);
int entity = packet.getEntityId();
boolean sneaking = packet.isSneaking().orElse(false);
if (hasInteracted && entity != lastEntity) {
String verbose = "lastEntity=" + lastEntity + ", entity=" + entity
+ ", lastSneaking=" + lastSneaking + ", sneaking=" + sneaking;
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) {
if (flagAndAlert(verbose) && shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
}
} else {
flags.add(verbose);
}
}
lastEntity = entity;
lastSneaking = sneaking;
hasInteracted = true;
}
if (WrapperPlayClientPlayerFlying.isFlying(event.getPacketType()) && player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8) && !player.packetStateData.lastPacketWasTeleport) {
hasInteracted = false;
}
}
@Override
public void onPredictionComplete(PredictionComplete predictionComplete) {
// we don't need to check pre-1.9 players here (no tick skipping)
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) return;
if (player.isTickingReliablyFor(3)) {
for (String verbose : flags) {
flagAndAlert(verbose);
}
}
flags.clear();
hasInteracted = false;
}
}
@@ -1,72 +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.PostPredictionCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.MessageUtil;
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.util.Vector3f;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientInteractEntity;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerFlying;
import java.util.ArrayList;
@CheckData(name = "MultiInteractB", experimental = true)
public class MultiInteractB extends Check implements PostPredictionCheck {
public MultiInteractB(final GrimPlayer player) {
super(player);
}
private final ArrayList<String> flags = new ArrayList<>();
private Vector3f lastPos;
private boolean hasInteracted = false;
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (event.getPacketType() == PacketType.Play.Client.INTERACT_ENTITY) {
Vector3f pos = new WrapperPlayClientInteractEntity(event).getTarget().orElse(null);
if (pos == null) {
return;
}
if (hasInteracted && !pos.equals(lastPos)) {
String verbose = "pos=" + MessageUtil.toUnlabledString(pos) + ", lastPos=" + MessageUtil.toUnlabledString(lastPos);
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) {
if (flagAndAlert(verbose) && shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
}
} else {
flags.add(verbose);
}
}
lastPos = pos;
hasInteracted = true;
}
if (WrapperPlayClientPlayerFlying.isFlying(event.getPacketType()) && player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8) && !player.packetStateData.lastPacketWasTeleport) {
hasInteracted = false;
}
}
@Override
public void onPredictionComplete(PredictionComplete predictionComplete) {
// we don't need to check pre-1.9 players here (no tick skipping)
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) return;
if (player.isTickingReliablyFor(3)) {
for (String verbose : flags) {
flagAndAlert(verbose);
}
}
flags.clear();
hasInteracted = false;
}
}
@@ -15,7 +15,7 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package ac.grim.grimac.checks.impl.combat;
import ac.grim.grimac.api.config.ConfigManager;
import ac.grim.grimac.GrimAPI;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PacketCheck;
@@ -27,7 +27,6 @@ import ac.grim.grimac.utils.data.packetentity.PacketEntity;
import ac.grim.grimac.utils.nmsutil.BlockRayTrace;
import ac.grim.grimac.utils.data.packetentity.dragon.PacketEntityEnderDragonPart;
import ac.grim.grimac.utils.nmsutil.ReachUtils;
import ac.grim.grimac.utils.vector.Vector3D;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.protocol.attribute.Attributes;
import com.github.retrooper.packetevents.protocol.entity.type.EntityType;
@@ -40,26 +39,24 @@ import com.github.retrooper.packetevents.util.Vector3d;
import com.github.retrooper.packetevents.util.Vector3i;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientInteractEntity;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerFlying;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import ac.grim.grimac.utils.vector.Vector3D;
import org.bukkit.util.Vector;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
// You may not copy the check unless you are licensed under GPL
@CheckData(name = "Reach", configName = "Reach", setback = 10)
public class Reach extends Check implements PacketCheck {
// Only one flag per reach attack, per entity, per tick.
// We store position because lastX isn't reliable on teleports.
private final Int2ObjectMap<Vector3d> playerAttackQueue = new Int2ObjectOpenHashMap<>();
private final Map<Integer, Vector3d> playerAttackQueue = new HashMap<>();
// Used to prevent falses in the wall hit check
private final Set<Vector3i> blocksChangedThisTick = new HashSet<>();
@@ -68,7 +65,6 @@ public class Reach extends Check implements PacketCheck {
EntityTypes.CHEST_BOAT,
EntityTypes.SHULKER);
private boolean ignoreNonPlayerTargets;
private boolean cancelImpossibleHits;
private double threshold;
private double cancelBuffer; // For the next 4 hits after using reach, we aggressively cancel reach
@@ -101,11 +97,7 @@ public class Reach extends Check implements PacketCheck {
}
return;
}
if (ignoreNonPlayerTargets && !entity.getType().equals(EntityTypes.PLAYER)) {
return;
}
// Dead entities cause false flags (https://github.com/GrimAnticheat/Grim/issues/546)
if (entity.isDead) return;
@@ -167,8 +159,8 @@ public class Reach extends Check implements PacketCheck {
}
private void tickBetterReachCheckWithAngle(boolean isFlying) {
for (Int2ObjectMap.Entry<Vector3d> attack : playerAttackQueue.int2ObjectEntrySet()) {
PacketEntity reachEntity = player.compensatedEntities.entityMap.get(attack.getIntKey());
for (Map.Entry<Integer, Vector3d> attack : playerAttackQueue.entrySet()) {
PacketEntity reachEntity = player.compensatedEntities.entityMap.get(attack.getKey().intValue());
if (reachEntity != null) {
String result = checkReach(reachEntity, attack.getValue(), false);
if (result != null) {
@@ -211,7 +203,7 @@ public class Reach extends Check implements PacketCheck {
double minDistance = Double.MAX_VALUE;
// https://bugs.mojang.com/browse/MC-67665
List<Vector3D> possibleLookDirs = new ArrayList<>(Collections.singletonList(ReachUtils.getLook(player, player.xRot, player.yRot)));
List<Vector> possibleLookDirs = new ArrayList<>(Collections.singletonList(ReachUtils.getLook(player, player.xRot, player.yRot)));
// If we are a tick behind, we don't know their next look so don't bother doing this
if (!isPrediction) {
@@ -230,13 +222,12 @@ public class Reach extends Check implements PacketCheck {
// +3 would be 3 + 3 = 6, which is the pre-1.20.5 behaviour, preventing "Missed Hitbox"
final double distance = player.compensatedEntities.getSelf().getAttributeValue(Attributes.PLAYER_ENTITY_INTERACTION_RANGE) + 3;
final double[] possibleEyeHeights = player.getPossibleEyeHeights();
for (Vector3D lookVec : possibleLookDirs) {
for (double eye : possibleEyeHeights) {
ac.grim.grimac.utils.vector.Vector3D eyePos = newVector3D(from.getX(), from.getY() + eye, from.getZ());
ac.grim.grimac.utils.vector.Vector3D endReachPos = eyePos.clone().add(newVector3D(lookVec.getX() * distance, lookVec.getY() * distance, lookVec.getZ() * distance));
for (Vector lookVec : possibleLookDirs) {
for (double eye : player.getPossibleEyeHeights()) {
Vector eyePos = new Vector(from.getX(), from.getY() + eye, from.getZ());
Vector endReachPos = eyePos.clone().add(new Vector(lookVec.getX() * distance, lookVec.getY() * distance, lookVec.getZ() * distance));
Vector3D intercept = ReachUtils.calculateIntercept(targetBox, eyePos, endReachPos).getFirst();
Vector intercept = ReachUtils.calculateIntercept(targetBox, eyePos, endReachPos).getFirst();
if (ReachUtils.isVecInside(targetBox, eyePos)) {
minDistance = 0;
@@ -249,10 +240,11 @@ public class Reach extends Check implements PacketCheck {
}
}
final boolean experimentalChecks = true; //GrimAPI.INSTANCE.getConfigManager().isExperimentalChecks(); // TODO fix for undraft
HitData foundHitData = null;
// If the entity is within range of the player (we'll flag anyway if not, so no point checking blocks in this case)
// Ignore when could be hitting through a moving shulker, piston blocks. They are just too glitchy/uncertain to check.
if (minDistance <= 3 && !player.compensatedWorld.isNearHardEntity(player.boundingBox.copy().expand(4))) {
if (experimentalChecks && minDistance <= 3 && !player.compensatedWorld.isNearHardEntity(player.boundingBox.copy().expand(4))) {
final @Nullable Pair<Double, HitData> targetBlock = getTargetBlock(player, possibleLookDirs, from, minDistance);
// And if the target block is closer to the player than the entity box, they should hit the block instead
// So, this hit is invalid.
@@ -284,7 +276,7 @@ public class Reach extends Check implements PacketCheck {
public void handleBlockChange(Vector3i vector3i, WrappedBlockState state) {
if (blocksChangedThisTick.size() >= 40) return; // Don't let players freeze movement packets to grow this
// Only do this for nearby blocks
if (newVector3D(vector3i.x, vector3i.y, vector3i.z).distanceSquared(newVector3D(player.x, player.y, player.z)) > 6) return;
if (new Vector(vector3i.x, vector3i.y, vector3i.z).distanceSquared(new Vector(player.x, player.y, player.z)) > 6) return;
// Only do this if the state really had any world impact
if (state.equals(player.compensatedWorld.getWrappedBlockStateAt(vector3i))) return;
blocksChangedThisTick.add(vector3i);
@@ -292,14 +284,14 @@ public class Reach extends Check implements PacketCheck {
// Returns a pair so we can check the block type in the flag
@Nullable
private Pair<Double, HitData> getTargetBlock(GrimPlayer player, List<Vector3D> possibleLookDirs, Vector3d from, double minDistance) {
private Pair<Double, HitData> getTargetBlock(GrimPlayer player, List<Vector> possibleLookDirs, Vector3d from, double minDistance) {
// Check every possible look direction and every possible eye height
// IF *NONE* of them allow the player to hit the entity, this is an invalid hit
HitData bestHitData = null;
double min = Double.MAX_VALUE;
for (Vector3D lookVec : possibleLookDirs) {
for (Vector lookVec : possibleLookDirs) {
for (double eye : player.getPossibleEyeHeights()) {
Vector3D eyes = newVector3D(from.getX(), from.getY() + eye, from.getZ());
Vector eyes = new Vector(from.getX(), from.getY() + eye, from.getZ());
final double reach = player.compensatedEntities.getSelf().getAttributeValue(Attributes.PLAYER_BLOCK_INTERACTION_RANGE);
final HitData hitResult = BlockRayTrace.getNearestReachHitResult(player, eyes, lookVec, minDistance, reach);
if (hitResult == null) {
@@ -321,9 +313,9 @@ public class Reach extends Check implements PacketCheck {
}
@Override
public void onReload(ConfigManager config) {
this.ignoreNonPlayerTargets = config.getBooleanElse("Reach.ignore-non-player-targets", false);
this.cancelImpossibleHits = config.getBooleanElse("Reach.block-impossible-hits", true);
this.threshold = config.getDoubleElse("Reach.threshold", 0.0005);
public void reload() {
super.reload();
this.cancelImpossibleHits = getConfig().getBooleanElse("Reach.block-impossible-hits", true);
this.threshold = getConfig().getDoubleElse("Reach.threshold", 0.0005);
}
}
@@ -18,6 +18,7 @@ public class CrashB extends Check implements PacketCheck {
public void onPacketReceive(PacketReceiveEvent event) {
if (event.getPacketType() == PacketType.Play.Client.CREATIVE_INVENTORY_ACTION) {
if (player.gamemode != GameMode.CREATIVE) {
player.getSetbackTeleportUtil().executeViolationSetback();
event.setCancelled(true);
player.onPacketCancel();
flagAndAlert(); // Could be transaction split, no need to setback though
@@ -43,10 +43,7 @@ public class NoFallA extends Check implements PacketCheck {
if (!isNearGround(wrapper.isOnGround())) { // If player isn't near ground
// 1.8 boats have a mind on their own... only flag if they're not near a boat or are on 1.9+
if (!GhostBlockDetector.isGhostBlock(player) && flagWithSetback()) alert("");
if (shouldModifyPackets()) {
wrapper.setOnGround(false);
event.markForReEncode(true);
}
if (shouldModifyPackets()) wrapper.setOnGround(false);
}
}
}
@@ -62,16 +59,10 @@ public class NoFallA extends Check implements PacketCheck {
// Also flip teleports because I don't trust vanilla's handling of teleports and ground
if (flipPlayerGroundStatus) {
flipPlayerGroundStatus = false;
if (shouldModifyPackets()) {
wrapper.setOnGround(!wrapper.isOnGround());
event.markForReEncode(true);
}
if (shouldModifyPackets()) wrapper.setOnGround(!wrapper.isOnGround());
}
if (player.packetStateData.lastPacketWasTeleport) {
if (shouldModifyPackets()) {
wrapper.setOnGround(false);
event.markForReEncode(true);
}
if (shouldModifyPackets()) wrapper.setOnGround(false);
}
}
}
@@ -1,41 +0,0 @@
package ac.grim.grimac.checks.impl.inventory;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.InventoryCheck;
import ac.grim.grimac.player.GrimPlayer;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientInteractEntity;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientInteractEntity.InteractAction;
@CheckData(name = "InventoryA", setback = 3, description = "Attacked an entity while inventory is open")
public class InventoryA extends InventoryCheck {
public InventoryA(GrimPlayer player) {
super(player);
}
@Override
public void onPacketReceive(PacketReceiveEvent event) {
super.onPacketReceive(event);
if (event.getPacketType() == PacketType.Play.Client.INTERACT_ENTITY) {
WrapperPlayClientInteractEntity wrapper = new WrapperPlayClientInteractEntity(event);
if (wrapper.getAction() != InteractAction.ATTACK) return;
// Is not possible to attack while the inventory is open.
if (player.hasInventoryOpen) {
if (flagAndAlert()) {
// Cancel the packet
if (shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
}
closeInventory();
}
} else {
reward();
}
}
}
}
@@ -1,33 +0,0 @@
package ac.grim.grimac.checks.impl.inventory;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.InventoryCheck;
import ac.grim.grimac.player.GrimPlayer;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.protocol.player.DiggingAction;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerDigging;
@CheckData(name = "InventoryB", setback = 3, description = "Started digging blocks while inventory is open")
public class InventoryB extends InventoryCheck {
public InventoryB(GrimPlayer player) {
super(player);
}
public void handle(PacketReceiveEvent event, WrapperPlayClientPlayerDigging wrapper) {
if (wrapper.getAction() != DiggingAction.START_DIGGING) return;
// Is not possible to start digging a block while the inventory is open.
if (player.hasInventoryOpen) {
if (flagAndAlert()) {
// Cancel the packet
if (shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
}
closeInventory();
}
} else {
reward();
}
}
}
@@ -1,28 +0,0 @@
package ac.grim.grimac.checks.impl.inventory;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.InventoryCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.BlockPlace;
@CheckData(name = "InventoryC", setback = 3, description = "Placed a block while inventory is open")
public class InventoryC extends InventoryCheck {
public InventoryC(GrimPlayer player) {
super(player);
}
public void onBlockPlace(final BlockPlace place) {
// It is not possible to place a block while the inventory is open
if (player.hasInventoryOpen) {
if (flagAndAlert()) {
if (shouldModifyPackets()) {
place.resync();
}
closeInventory();
}
} else {
reward();
}
}
}
@@ -1,84 +0,0 @@
package ac.grim.grimac.checks.impl.inventory;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.InventoryCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
import ac.grim.grimac.utils.data.VectorData;
import ac.grim.grimac.utils.data.VectorData.MoveVectorData;
import ac.grim.grimac.utils.data.VehicleData;
import java.util.StringJoiner;
@CheckData(name = "InventoryD", setback = 1, decay = 0.25)
public class InventoryD extends InventoryCheck {
private int horseJumpVerbose;
public InventoryD(GrimPlayer player) {
super(player);
}
@Override
public void onPredictionComplete(final PredictionComplete predictionComplete) {
if (!predictionComplete.isChecked() ||
predictionComplete.getData().isTeleport() ||
player.getSetbackTeleportUtil().blockOffsets ||
player.packetStateData.lastPacketWasTeleport ||
player.packetStateData.isSlowedByUsingItem() ||
System.currentTimeMillis() - player.lastBlockPlaceUseItem < 50L) {
return;
}
if (player.hasInventoryOpen) {
boolean inVehicle = player.compensatedEntities.getSelf().inVehicle();
boolean isJumping, isMoving;
if (inVehicle) {
VehicleData vehicle = player.vehicleData;
// Will flag once if player open anything with pressed space bar
isJumping = vehicle.nextHorseJump > 0 && horseJumpVerbose++ >= 1;
isMoving = vehicle.nextVehicleForward != 0 || vehicle.nextVehicleHorizontal != 0;
} else {
MoveVectorData move = findMovement(player.predictedVelocity);
isJumping = player.predictedVelocity.isJump();
isMoving = move != null && (move.x != 0 || move.z != 0);
}
if (!isMoving && !isJumping) {
reward();
return;
}
if (flag()) {
closeInventory();
StringJoiner joiner = new StringJoiner(" ");
if (isMoving) joiner.add("moving");
if (isJumping) joiner.add("jumping");
if (inVehicle) joiner.add("inVehicle");
alert(joiner.toString());
}
} else {
horseJumpVerbose = 0;
}
}
private MoveVectorData findMovement(VectorData vectorData) {
if (vectorData instanceof MoveVectorData) {
return (MoveVectorData) vectorData;
}
while (vectorData != null) {
vectorData = vectorData.lastVector;
if (vectorData instanceof MoveVectorData) {
return (MoveVectorData) vectorData;
}
}
return null;
}
}
@@ -1,48 +0,0 @@
package ac.grim.grimac.checks.impl.inventory;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.InventoryCheck;
import ac.grim.grimac.player.GrimPlayer;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.event.PacketSendEvent;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
@CheckData(name = "InventoryE", setback = 3, description = "Sent a held item change packet while inventory is open")
public class InventoryE extends InventoryCheck {
private long lastTransaction = Long.MAX_VALUE; // Impossible transaction ID
public InventoryE(GrimPlayer player) {
super(player);
}
@Override
public void onPacketReceive(PacketReceiveEvent event) {
super.onPacketReceive(event);
if (event.getPacketType() == PacketType.Play.Client.HELD_ITEM_CHANGE) {
// It is not possible to change hotbar slots with held item change while the inventory is open
// A container click packet would be sent instead
if (player.hasInventoryOpen) {
if (this.lastTransaction < player.lastTransactionReceived.get()
&& flagAndAlert()) {
// Cancel the packet
if (shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
player.getInventory().needResend = true;
}
closeInventory();
}
} else {
reward();
}
}
}
@Override
public void onPacketSend(PacketSendEvent event) {
if (event.getPacketType() == PacketType.Play.Server.HELD_ITEM_CHANGE) {
this.lastTransaction = player.lastTransactionSent.get();
}
}
}
@@ -1,44 +0,0 @@
package ac.grim.grimac.checks.impl.inventory;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.InventoryCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.inventory.InventoryDesyncStatus;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
@CheckData(name = "InventoryF", setback = 3, description = "Sent a click window packet without a open inventory", experimental = true)
public class InventoryF extends InventoryCheck {
public InventoryF(GrimPlayer player) {
super(player);
}
@Override
public void onPacketReceive(PacketReceiveEvent event) {
// Exempt on 1.9+ server version due to the Via hack done in PacketPlayerWindow, the exemption can be deleted
// once we are ahead of ViaVersion
if (PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_9)
|| player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_9)) return;
super.onPacketReceive(event);
if (event.getPacketType() == PacketType.Play.Client.CLICK_WINDOW) {
if (!player.hasInventoryOpen && player.inventoryDesyncStatus == InventoryDesyncStatus.NOT_DESYNCED) {
if (flagAndAlert()) {
// Cancel the packet
if (shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
}
closeInventory();
}
} else {
reward();
}
}
}
}
@@ -1,40 +0,0 @@
package ac.grim.grimac.checks.impl.inventory;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.InventoryCheck;
import ac.grim.grimac.player.GrimPlayer;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientEntityAction;
@CheckData(name = "InventoryG", setback = 3, description = "Sent a entity action packet while inventory is open", experimental = true)
public class InventoryG extends InventoryCheck {
public InventoryG(GrimPlayer player) {
super(player);
}
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (player.packetStateData.lastPacketWasTeleport) return;
super.onPacketReceive(event);
if (event.getPacketType() == PacketType.Play.Client.ENTITY_ACTION) {
WrapperPlayClientEntityAction wrapper = new WrapperPlayClientEntityAction(event);
WrapperPlayClientEntityAction.Action action = wrapper.getAction();
if (action == WrapperPlayClientEntityAction.Action.STOP_SNEAKING
|| action == WrapperPlayClientEntityAction.Action.STOP_SPRINTING) {
return;
}
if (player.hasInventoryOpen) {
if (flagAndAlert()) {
closeInventory();
}
} else {
reward();
}
}
}
}
@@ -9,7 +9,6 @@ import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPluginMessage;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.entity.Player;
public class ClientBrand extends Check implements PacketCheck {
@@ -39,7 +38,6 @@ public class ClientBrand extends Check implements PacketCheck {
System.arraycopy(data, 1, minusLength, 0, minusLength.length);
brand = new String(minusLength).replace(" (Velocity)", ""); //removes velocity's brand suffix
brand = ChatColor.stripColor(brand); //strip color codes from client brand
if (player.checkManager.getPrePredictionCheck(ExploitA.class).checkString(brand)) brand = "sent log4j";
if (!GrimAPI.INSTANCE.getConfigManager().isIgnoredClient(brand)) {
String message = GrimAPI.INSTANCE.getConfigManager().getConfig().getStringElse("client-brand-format", "%prefix% &f%player% joined using %brand%");
@@ -63,15 +63,15 @@ public class FastBreak extends Check implements PacketCheck {
if (digging.getAction() == DiggingAction.START_DIGGING) {
WrappedBlockState block = player.compensatedWorld.getWrappedBlockStateAt(blockPosition);
// Exempt all blocks that do not exist in the player version
if (WrappedBlockState.getDefaultState(player.getClientVersion(), block.getType()).getType() == StateTypes.AIR) {
return;
}
startBreak = System.currentTimeMillis() - (targetBlock == null ? 50 : 0); // ???
targetBlock = blockPosition;
maximumBlockDamage = BlockBreakSpeed.getBlockDamage(player, targetBlock);
double breakDelay = System.currentTimeMillis() - lastFinishBreak;
@@ -1,6 +1,5 @@
package ac.grim.grimac.checks.impl.misc;
import ac.grim.grimac.api.config.ConfigManager;
import ac.grim.grimac.checks.type.BlockPlaceCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.BlockPlace;
@@ -64,9 +63,10 @@ public class GhostBlockMitigation extends BlockPlaceCheck {
}
@Override
public void onReload(ConfigManager config) {
allow = config.getBooleanElse("exploit.allow-building-on-ghostblocks", true);
distance = config.getIntElse("exploit.distance-to-check-for-ghostblocks", 2);
public void reload() {
super.reload();
allow = getConfig().getBooleanElse("exploit.allow-building-on-ghostblocks", true);
distance = getConfig().getIntElse("exploit.distance-to-check-for-ghostblocks", 2);
if (distance < 2 || distance > 4) distance = 2;
}
@@ -0,0 +1,17 @@
package ac.grim.grimac.checks.impl.misc;
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 java.util.ArrayList;
@CheckData(name = "TransactionOrder", experimental = false)
public class TransactionOrder extends Check implements PacketCheck {
public TransactionOrder(GrimPlayer player) {
super(player);
}
}
@@ -1,13 +1,12 @@
package ac.grim.grimac.checks.impl.movement;
import ac.grim.grimac.api.config.ConfigManager;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PostPredictionCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
@CheckData(name = "NegativeTimer", configName = "NegativeTimer", setback = -1, experimental = true)
@CheckData(name = "NegativeTimer", configName = "NegativeTimer", setback = 10, experimental = true)
public class NegativeTimerCheck extends TimerCheck implements PostPredictionCheck {
public NegativeTimerCheck(GrimPlayer player) {
@@ -24,7 +23,7 @@ public class NegativeTimerCheck extends TimerCheck implements PostPredictionChec
if (timerBalanceRealTime < lastMovementPlayerClock - clockDrift) {
int lostMS = (int) ((System.nanoTime() - timerBalanceRealTime) / 1e6);
if (flagAndAlert("-" + lostMS)) setbackIfAboveSetbackVL();
flagAndAlert("-" + lostMS);
timerBalanceRealTime += 50e6;
}
}
@@ -36,7 +35,8 @@ public class NegativeTimerCheck extends TimerCheck implements PostPredictionChec
}
@Override
public void onReload(ConfigManager config) {
clockDrift = (long) (config.getDoubleElse(getConfigName() + ".drift", 1200.0) * 1e6);
public void reload() {
super.reload();
clockDrift = (long) (getConfig().getDoubleElse(getConfigName() + ".drift", 1200.0) * 1e6);
}
}
@@ -1,6 +1,5 @@
package ac.grim.grimac.checks.impl.movement;
import ac.grim.grimac.api.config.ConfigManager;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PostPredictionCheck;
@@ -34,7 +33,8 @@ public class NoSlowA extends Check implements PostPredictionCheck {
}
if (bestOffset > offsetToFlag) {
if (flaggedLastTick && flagWithSetback()) {
if (flaggedLastTick) {
flagWithSetback();
alert("");
}
flaggedLastTick = true;
@@ -51,7 +51,8 @@ public class NoSlowA extends Check implements PostPredictionCheck {
}
@Override
public void onReload(ConfigManager config) {
offsetToFlag = config.getDoubleElse("NoSlowA.threshold", 0.001);
public void reload() {
super.reload();
offsetToFlag = getConfig().getDoubleElse("NoSlowA.threshold", 0.001);
}
}
@@ -35,4 +35,4 @@ public class NoSlowB extends Check implements PacketCheck {
}
}
}
}
}
@@ -2,17 +2,32 @@ package ac.grim.grimac.checks.impl.movement;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PacketCheck;
import ac.grim.grimac.checks.type.PostPredictionCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientEntityAction;
@CheckData(name = "NoSlowC", setback = 5, experimental = true)
public class NoSlowC extends Check implements PostPredictionCheck {
public class NoSlowC extends Check implements PostPredictionCheck, PacketCheck {
public NoSlowC(GrimPlayer player) {
super(player);
}
public boolean startedSprintingBeforeSlowMovement = false;
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (event.getPacketType() == PacketType.Play.Client.ENTITY_ACTION) {
if (new WrapperPlayClientEntityAction(event).getAction() == WrapperPlayClientEntityAction.Action.START_SPRINTING) {
startedSprintingBeforeSlowMovement = false;
}
}
}
@Override
public void onPredictionComplete(final PredictionComplete predictionComplete) {
if (!predictionComplete.isChecked()) return;
@@ -21,7 +36,8 @@ public class NoSlowC extends Check implements PostPredictionCheck {
ClientVersion client = player.getClientVersion();
// https://bugs.mojang.com/browse/MC-152728
if (client.isNewerThanOrEquals(ClientVersion.V_1_14_2)) {
if (startedSprintingBeforeSlowMovement && client.isNewerThanOrEquals(ClientVersion.V_1_14_2)) {
reward();
return;
}
@@ -2,26 +2,42 @@ package ac.grim.grimac.checks.impl.movement;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PacketCheck;
import ac.grim.grimac.checks.type.PostPredictionCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientEntityAction;
@CheckData(name = "NoSlowD", setback = 5, experimental = true)
public class NoSlowD extends Check implements PostPredictionCheck {
public class NoSlowD extends Check implements PostPredictionCheck, PacketCheck {
public NoSlowD(GrimPlayer player) {
super(player);
}
public boolean startedSprintingBeforeUse = false;
private boolean flaggedLastTick = false;
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (event.getPacketType() == PacketType.Play.Client.ENTITY_ACTION) {
if (new WrapperPlayClientEntityAction(event).getAction() == WrapperPlayClientEntityAction.Action.START_SPRINTING) {
startedSprintingBeforeUse = false;
}
}
}
@Override
public void onPredictionComplete(final PredictionComplete predictionComplete) {
if (!predictionComplete.isChecked()) return;
if (player.packetStateData.isSlowedByUsingItem()) {
// https://bugs.mojang.com/browse/MC-152728
if (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_14_2)) {
if (startedSprintingBeforeUse && player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_14_2)) {
reward();
flaggedLastTick = false;
return;
}
@@ -2,6 +2,7 @@ package ac.grim.grimac.checks.impl.movement;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PacketCheck;
import ac.grim.grimac.checks.type.PostPredictionCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
@@ -12,7 +13,7 @@ import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientEn
import static com.github.retrooper.packetevents.protocol.potion.PotionTypes.BLINDNESS;
@CheckData(name = "NoSlowE", setback = 5, experimental = true)
public class NoSlowE extends Check implements PostPredictionCheck {
public class NoSlowE extends Check implements PostPredictionCheck, PacketCheck {
public NoSlowE(GrimPlayer player) {
super(player);
}
@@ -6,6 +6,7 @@ import ac.grim.grimac.player.GrimPlayer;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.util.Vector3d;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientInteractEntity;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerFlying;
public class SetbackBlocker extends Check implements PacketCheck {
@@ -1,6 +1,5 @@
package ac.grim.grimac.checks.impl.movement;
import ac.grim.grimac.api.config.ConfigManager;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PacketCheck;
@@ -20,9 +19,9 @@ public class TimerCheck extends Check implements PacketCheck {
// How long should the player be able to fall back behind their ping?
// Default: 120 milliseconds
long clockDrift;
long clockDrift = (long) 120e6;
long limitAbuseOverPing;
long limitAbuseOverPing = 1000;
boolean hasGottenMovementAfterTransaction = false;
@@ -116,8 +115,9 @@ public class TimerCheck extends Check implements PacketCheck {
}
@Override
public void onReload(ConfigManager config) {
clockDrift = (long) (config.getDoubleElse(getConfigName() + ".drift", 120.0) * 1e6);
limitAbuseOverPing = (long) (config.getDoubleElse(getConfigName() + ".ping-abuse-limit-threshold", 1000));
public void reload() {
super.reload();
clockDrift = (long) (getConfig().getDoubleElse(getConfigName() + ".drift", 120.0) * 1e6);
limitAbuseOverPing = (long) (getConfig().getDoubleElse(getConfigName() + ".ping-abuse-limit-threshold", 1000));
}
}
@@ -1,54 +0,0 @@
package ac.grim.grimac.checks.impl.packetorder;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PostPredictionCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientClickWindow;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientClickWindow.WindowClickType;
@CheckData(name = "PacketOrderA", experimental = true)
public class PacketOrderA extends Check implements PostPredictionCheck {
public PacketOrderA(final GrimPlayer player) {
super(player);
}
private int invalid;
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (event.getPacketType() == PacketType.Play.Client.CLICK_WINDOW) {
final WindowClickType clickType = new WrapperPlayClientClickWindow(event).getWindowClickType();
if ((clickType == WindowClickType.PICKUP || clickType == WindowClickType.PICKUP_ALL) && player.packetOrderProcessor.isQuickMoveClicking()
|| clickType == WindowClickType.QUICK_MOVE && player.packetOrderProcessor.isPickUpClicking()) {
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) {
if (flagAndAlert() && shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
}
} else {
invalid++;
}
}
}
}
@Override
public void onPredictionComplete(PredictionComplete predictionComplete) {
// we don't need to check pre-1.9 players here (no tick skipping)
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) return;
if (player.isTickingReliablyFor(3)) {
for (; invalid >= 1; invalid--) {
flagAndAlert();
}
}
invalid = 0;
}
}
@@ -1,13 +0,0 @@
package ac.grim.grimac.checks.impl.packetorder;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PacketCheck;
import ac.grim.grimac.player.GrimPlayer;
@CheckData(name = "PacketOrderD")
public class PacketOrderD extends Check implements PacketCheck {
public PacketOrderD(GrimPlayer player) {
super(player);
}
}
@@ -1,66 +0,0 @@
package ac.grim.grimac.checks.impl.packetorder;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PostPredictionCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
@CheckData(name = "PacketOrderE", experimental = true)
public class PacketOrderE extends Check implements PostPredictionCheck {
public PacketOrderE(final GrimPlayer player) {
super(player);
}
private int invalid;
private boolean setback;
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (event.getPacketType() == PacketType.Play.Client.HELD_ITEM_CHANGE) {
if (player.packetOrderProcessor.isAttacking()
|| player.packetOrderProcessor.isRightClicking()
|| player.packetOrderProcessor.isOpeningInventory()
|| player.packetOrderProcessor.isReleasing()
|| player.packetOrderProcessor.isSneaking()
|| player.packetOrderProcessor.isSprinting()
|| player.packetOrderProcessor.isLeavingBed()
|| player.packetOrderProcessor.isStartingToGlide()
|| player.packetOrderProcessor.isJumpingWithMount()
) {
if (player.getClientVersion().isNewerThan(ClientVersion.V_1_8) || flagAndAlert()) {
invalid++;
if (player.packetOrderProcessor.isUsing()) {
setback = true;
}
}
}
}
}
@Override
public void onPredictionComplete(PredictionComplete predictionComplete) {
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) {
if (setback) {
setback = false;
setbackIfAboveSetbackVL();
}
return;
}
if (player.isTickingReliablyFor(3)) {
for (; invalid >= 1; invalid--) {
if (flagAndAlert() && setback) {
setback = false;
setbackIfAboveSetbackVL();
}
}
}
invalid = 0;
}
}
@@ -1,61 +0,0 @@
package ac.grim.grimac.checks.impl.packetorder;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PostPredictionCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.player.DiggingAction;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientClientStatus;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerDigging;
@CheckData(name = "PacketOrderF", experimental = true)
public class PacketOrderF extends Check implements PostPredictionCheck {
public PacketOrderF(GrimPlayer player) {
super(player);
}
private int invalid;
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (event.getPacketType() == PacketType.Play.Client.INTERACT_ENTITY
|| event.getPacketType() == PacketType.Play.Client.PLAYER_BLOCK_PLACEMENT
|| event.getPacketType() == PacketType.Play.Client.USE_ITEM
|| event.getPacketType() == PacketType.Play.Client.PICK_ITEM
|| event.getPacketType() == PacketType.Play.Client.PLAYER_DIGGING
|| (event.getPacketType() == PacketType.Play.Client.CLIENT_STATUS
&& new WrapperPlayClientClientStatus(event).getAction() == WrapperPlayClientClientStatus.Action.OPEN_INVENTORY_ACHIEVEMENT)
) if (player.packetOrderProcessor.isSprinting() || player.packetOrderProcessor.isSneaking()) {
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) {
if (flagAndAlert() && shouldModifyPackets()) {
if (event.getPacketType() == PacketType.Play.Client.PLAYER_DIGGING
&& new WrapperPlayClientPlayerDigging(event).getAction() == DiggingAction.RELEASE_USE_ITEM
) return; // don't cause a noslow
event.setCancelled(true);
player.onPacketCancel();
}
} else {
invalid++;
}
}
}
@Override
public void onPredictionComplete(PredictionComplete predictionComplete) {
// we don't need to check pre-1.9 players here (no tick skipping)
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) return;
if (player.isTickingReliablyFor(3)) {
for (; invalid >= 1; invalid--) {
flagAndAlert();
}
}
invalid = 0;
}
}
@@ -1,69 +0,0 @@
package ac.grim.grimac.checks.impl.packetorder;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PostPredictionCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.player.DiggingAction;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientClientStatus;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerDigging;
@CheckData(name = "PacketOrderG", experimental = true)
public class PacketOrderG extends Check implements PostPredictionCheck {
public PacketOrderG(GrimPlayer player) {
super(player);
}
private int invalid;
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (event.getPacketType() == PacketType.Play.Client.PLAYER_DIGGING
|| (event.getPacketType() == PacketType.Play.Client.CLIENT_STATUS
&& new WrapperPlayClientClientStatus(event).getAction() == WrapperPlayClientClientStatus.Action.OPEN_INVENTORY_ACHIEVEMENT)
) {
if (event.getPacketType() == PacketType.Play.Client.PLAYER_DIGGING) {
final DiggingAction action = new WrapperPlayClientPlayerDigging(event).getAction();
if (action == DiggingAction.RELEASE_USE_ITEM
|| action == DiggingAction.START_DIGGING
|| action == DiggingAction.CANCELLED_DIGGING
|| action == DiggingAction.FINISHED_DIGGING
) return;
}
if (player.packetOrderProcessor.isAttacking()
|| player.packetOrderProcessor.isReleasing()
|| player.packetOrderProcessor.isRightClicking()
|| player.packetOrderProcessor.isPicking()
|| player.packetOrderProcessor.isDigging()
) {
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) {
if (flagAndAlert() && shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
}
} else {
invalid++;
}
}
}
}
@Override
public void onPredictionComplete(PredictionComplete predictionComplete) {
// we don't need to check pre-1.9 players here (no tick skipping)
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) return;
if (player.isTickingReliablyFor(3)) {
for (; invalid >= 1; invalid--) {
flagAndAlert();
}
}
invalid = 0;
}
}
@@ -1,51 +0,0 @@
package ac.grim.grimac.checks.impl.packetorder;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PostPredictionCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientEntityAction;
@CheckData(name = "PacketOrderH", experimental = true)
public class PacketOrderH extends Check implements PostPredictionCheck {
public PacketOrderH(final GrimPlayer player) {
super(player);
}
private int invalid;
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (event.getPacketType() == PacketType.Play.Client.ENTITY_ACTION) {
switch (new WrapperPlayClientEntityAction(event).getAction()) {
case START_SPRINTING:
case STOP_SPRINTING:
if (player.packetOrderProcessor.isSneaking()) {
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) {
flagAndAlert();
} else {
invalid++;
}
}
}
}
}
@Override
public void onPredictionComplete(PredictionComplete predictionComplete) {
// we don't need to check pre-1.9 players here (no tick skipping)
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) return;
if (player.isTickingReliablyFor(3)) {
for (; invalid >= 1; invalid--) {
flagAndAlert();
}
}
invalid = 0;
}
}
@@ -1,130 +0,0 @@
package ac.grim.grimac.checks.impl.packetorder;
import ac.grim.grimac.api.config.ConfigManager;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PostPredictionCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
import ac.grim.grimac.utils.nmsutil.BlockBreakSpeed;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.player.GameMode;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientInteractEntity;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerDigging;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerFlying;
import java.util.ArrayDeque;
@CheckData(name = "PacketOrderI", experimental = true)
public class PacketOrderI extends Check implements PostPredictionCheck {
public PacketOrderI(final GrimPlayer player) {
super(player);
}
private boolean exemptPlacingWhileDigging;
private boolean setback;
private boolean digging; // for placing
private final ArrayDeque<String> flags = new ArrayDeque<>();
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (event.getPacketType() == PacketType.Play.Client.INTERACT_ENTITY) {
if (new WrapperPlayClientInteractEntity(event).getAction() == WrapperPlayClientInteractEntity.InteractAction.ATTACK) {
if (player.packetOrderProcessor.isRightClicking() || player.packetOrderProcessor.isPicking() || player.packetOrderProcessor.isReleasing() || player.packetOrderProcessor.isDigging()) {
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) {
if (flagAndAlert("attack") && shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
}
} else {
flags.add("attack");
}
}
} else if (player.packetOrderProcessor.isReleasing() || player.packetOrderProcessor.isDigging()) {
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) {
if (flagAndAlert("interact") && shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
}
} else {
flags.add("interact");
}
}
}
if (event.getPacketType() == PacketType.Play.Client.PLAYER_BLOCK_PLACEMENT || event.getPacketType() == PacketType.Play.Client.USE_ITEM) {
if (player.packetOrderProcessor.isReleasing() || digging) {
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) {
if (flagAndAlert("place/use") && shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
}
} else {
flags.add("place");
}
}
}
if (event.getPacketType() == PacketType.Play.Client.PLAYER_DIGGING) {
WrapperPlayClientPlayerDigging packet = new WrapperPlayClientPlayerDigging(event);
switch (packet.getAction()) {
case RELEASE_USE_ITEM:
if (player.packetOrderProcessor.isAttacking() || player.packetOrderProcessor.isRightClicking() || player.packetOrderProcessor.isPicking() || player.packetOrderProcessor.isDigging()) {
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) {
if (flagAndAlert("release")) {
setback = true;
}
} else {
flags.add("release");
setback = true;
}
}
break;
case START_DIGGING:
double damage = BlockBreakSpeed.getBlockDamage(player, packet.getBlockPosition());
if (damage >= 1 || damage <= 0 && player.gamemode == GameMode.CREATIVE) {
return;
}
case CANCELLED_DIGGING:
case FINISHED_DIGGING:
if (exemptPlacingWhileDigging || player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_7_10)) {
return;
}
digging = true;
}
}
if (WrapperPlayClientPlayerFlying.isFlying(event.getPacketType()) && player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8) && !player.packetStateData.lastPacketWasTeleport) {
digging = false;
}
}
@Override
public void onPredictionComplete(PredictionComplete predictionComplete) {
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) {
if (setback) {
setbackIfAboveSetbackVL();
setback = false;
}
} else if (player.isTickingReliablyFor(3)) {
for (String verbose : flags) {
if (flagAndAlert(verbose) && setback) {
setbackIfAboveSetbackVL();
setback = false;
}
}
}
flags.clear();
setback = digging = false;
}
@Override
public void onReload(ConfigManager config) {
exemptPlacingWhileDigging = config.getBooleanElse(getConfigName() + ".exempt-placing-while-digging", false);
}
}
@@ -1,49 +0,0 @@
package ac.grim.grimac.checks.impl.packetorder;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PostPredictionCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
@CheckData(name = "PacketOrderJ", experimental = true)
public class PacketOrderJ extends Check implements PostPredictionCheck {
public PacketOrderJ(final GrimPlayer player) {
super(player);
}
private int invalid;
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (event.getPacketType() == PacketType.Play.Client.PLAYER_BLOCK_PLACEMENT || event.getPacketType() == PacketType.Play.Client.USE_ITEM) {
if (player.packetOrderProcessor.isAttacking() && !player.packetOrderProcessor.isInteracting()) {
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) {
if (flagAndAlert() && shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
}
} else {
invalid++;
}
}
}
}
@Override
public void onPredictionComplete(PredictionComplete predictionComplete) {
// we don't need to check pre-1.9 players here (no tick skipping)
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) return;
if (player.isTickingReliablyFor(3)) {
for (; invalid >= 1; invalid--) {
flagAndAlert();
}
}
invalid = 0;
}
}
@@ -1,64 +0,0 @@
package ac.grim.grimac.checks.impl.packetorder;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PostPredictionCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientClientStatus;
import java.util.ArrayDeque;
@CheckData(name = "PacketOrderK", experimental = true)
public class PacketOrderK extends Check implements PostPredictionCheck {
public PacketOrderK(final GrimPlayer player) {
super(player);
}
private final ArrayDeque<String> flags = new ArrayDeque<>();
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (event.getPacketType() == PacketType.Play.Client.CLIENT_STATUS) {
if (new WrapperPlayClientClientStatus(event).getAction() == WrapperPlayClientClientStatus.Action.OPEN_INVENTORY_ACHIEVEMENT) {
if (player.packetOrderProcessor.isClickingInInventory() || player.packetOrderProcessor.isClosingInventory()) {
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) {
flagAndAlert("open");
} else {
flags.add("open");
}
}
}
}
if (event.getPacketType() == PacketType.Play.Client.CLICK_WINDOW || event.getPacketType() == PacketType.Play.Client.CLOSE_WINDOW) {
if (player.packetOrderProcessor.isOpeningInventory()) {
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) {
if (flagAndAlert(event.getPacketType() == PacketType.Play.Client.CLICK_WINDOW ? "click" : "close") && shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
}
} else {
flags.add(event.getPacketType() == PacketType.Play.Client.CLICK_WINDOW ? "click" : "close");
}
}
}
}
@Override
public void onPredictionComplete(PredictionComplete predictionComplete) {
// we don't need to check pre-1.9 players here (no tick skipping)
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) return;
if (player.isTickingReliablyFor(3)) {
for (String verbose : flags) {
flagAndAlert(verbose);
}
}
flags.clear();
}
}
@@ -1,64 +0,0 @@
package ac.grim.grimac.checks.impl.packetorder;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PostPredictionCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.player.DiggingAction;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientClientStatus;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerDigging;
import java.util.ArrayDeque;
@CheckData(name = "PacketOrderL", experimental = true)
public class PacketOrderL extends Check implements PostPredictionCheck {
public PacketOrderL(final GrimPlayer player) {
super(player);
}
private final ArrayDeque<String> flags = new ArrayDeque<>();
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (event.getPacketType() == PacketType.Play.Client.CLIENT_STATUS) {
if (new WrapperPlayClientClientStatus(event).getAction() == WrapperPlayClientClientStatus.Action.OPEN_INVENTORY_ACHIEVEMENT) {
if (player.packetOrderProcessor.isDropping()) {
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) {
if (flagAndAlert("inventory") && shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
}
} else {
flags.add("inventory");
}
}
}
}
if (event.getPacketType() == PacketType.Play.Client.PLAYER_DIGGING) {
if (new WrapperPlayClientPlayerDigging(event).getAction() == DiggingAction.SWAP_ITEM_WITH_OFFHAND) {
if (player.packetOrderProcessor.isDropping()) {
flags.add("swap");
}
}
}
}
@Override
public void onPredictionComplete(PredictionComplete predictionComplete) {
// we don't need to check pre-1.9 players here (no tick skipping)
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) return;
if (player.isTickingReliablyFor(3)) {
for (String verbose : flags) {
flagAndAlert(verbose);
}
}
flags.clear();
}
}
@@ -1,72 +0,0 @@
package ac.grim.grimac.checks.impl.packetorder;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PostPredictionCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.world.BlockFace;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientInteractEntity;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerBlockPlacement;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerFlying;
@CheckData(name = "PacketOrderM", experimental = true)
public class PacketOrderM extends Check implements PostPredictionCheck {
public PacketOrderM(final GrimPlayer player) {
super(player);
}
private int invalid;
private boolean usingWithoutInteract, interacting;
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (event.getPacketType() == PacketType.Play.Client.INTERACT_ENTITY) {
if (new WrapperPlayClientInteractEntity(event).getAction() != WrapperPlayClientInteractEntity.InteractAction.ATTACK) {
interacting = true;
if (usingWithoutInteract) {
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) {
if (flagAndAlert() && shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
}
} else {
invalid++;
}
}
}
}
if (event.getPacketType() == PacketType.Play.Client.USE_ITEM
|| event.getPacketType() == PacketType.Play.Client.PLAYER_BLOCK_PLACEMENT
&& new WrapperPlayClientPlayerBlockPlacement(event).getFace() == BlockFace.OTHER) {
if (!interacting) {
usingWithoutInteract = true;
}
interacting = false;
}
if (WrapperPlayClientPlayerFlying.isFlying(event.getPacketType()) && player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8) && !player.packetStateData.lastPacketWasTeleport) {
usingWithoutInteract = interacting = false;
}
}
@Override
public void onPredictionComplete(PredictionComplete predictionComplete) {
// we don't need to check pre-1.9 players here (no tick skipping)
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) return;
if (player.isTickingReliablyFor(3)) {
for (; invalid >= 1; invalid--) {
flagAndAlert();
}
}
invalid = 0;
usingWithoutInteract = interacting = false;
}
}
@@ -1,69 +0,0 @@
package ac.grim.grimac.checks.impl.packetorder;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.BlockPlaceCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.BlockPlace;
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.world.BlockFace;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerBlockPlacement;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerFlying;
@CheckData(name = "PacketOrderN", experimental = true)
public class PacketOrderN extends BlockPlaceCheck {
public PacketOrderN(final GrimPlayer player) {
super(player);
}
private int invalid;
private boolean usingWithoutPlacing, placing;
@Override
public void onBlockPlace(BlockPlace place) {
placing = true;
if (usingWithoutPlacing) {
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) {
if (flagAndAlert() && shouldModifyPackets() && shouldCancel()) {
place.resync();
}
} else {
invalid++;
}
}
}
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (event.getPacketType() == PacketType.Play.Client.USE_ITEM
|| event.getPacketType() == PacketType.Play.Client.PLAYER_BLOCK_PLACEMENT
&& new WrapperPlayClientPlayerBlockPlacement(event).getFace() == BlockFace.OTHER) {
if (!placing) {
usingWithoutPlacing = true;
}
placing = false;
}
if (WrapperPlayClientPlayerFlying.isFlying(event.getPacketType()) && player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8) && !player.packetStateData.lastPacketWasTeleport) {
usingWithoutPlacing = placing = false;
}
}
@Override
public void onPredictionComplete(PredictionComplete predictionComplete) {
// we don't need to check pre-1.9 players here (no tick skipping)
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) return;
if (player.isTickingReliablyFor(3)) {
for (; invalid >= 1; invalid--) {
flagAndAlert();
}
}
invalid = 0;
usingWithoutPlacing = placing = false;
}
}
@@ -1,60 +0,0 @@
package ac.grim.grimac.checks.impl.packetorder;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.BlockPlaceCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.BlockPlace;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.player.InteractionHand;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerFlying;
@CheckData(name = "PacketOrderO", experimental = true)
public class PacketOrderO extends BlockPlaceCheck {
public PacketOrderO(final GrimPlayer player) {
super(player);
}
private boolean sentMainhand;
@Override
public void onBlockPlace(BlockPlace place) {
if (!isSupported()) {
return;
}
if (place.getHand() == InteractionHand.OFF_HAND) {
if (!sentMainhand) {
if (flagAndAlert("Skipped Mainhand") && shouldModifyPackets() && shouldCancel()) {
place.resync();
}
}
sentMainhand = false;
} else {
if (sentMainhand) {
if (flagAndAlert("Skipped Offhand") && shouldModifyPackets() && shouldCancel()) {
place.resync();
}
}
sentMainhand = !place.isBlock();
}
}
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (WrapperPlayClientPlayerFlying.isFlying(event.getPacketType()) && !player.packetStateData.lastPacketWasTeleport && !player.packetStateData.lastPacketWasOnePointSeventeenDuplicate) {
if (sentMainhand) {
sentMainhand = false;
flagAndAlert("Skipped Offhand (Tick)");
}
}
}
private boolean isSupported() {
return player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_13) && PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_9);
}
}
@@ -1,63 +0,0 @@
package ac.grim.grimac.checks.impl.packetorder;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PacketCheck;
import ac.grim.grimac.player.GrimPlayer;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.player.InteractionHand;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientInteractEntity;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientInteractEntity.InteractAction;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerFlying;
@CheckData(name = "PacketOrderP", experimental = true)
public class PacketOrderP extends Check implements PacketCheck {
public PacketOrderP(final GrimPlayer player) {
super(player);
}
private boolean sentMainhand;
private int requiredEntity;
private boolean requiredSneaking;
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (event.getPacketType() == PacketType.Play.Client.INTERACT_ENTITY && player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_9)) {
final WrapperPlayClientInteractEntity packet = new WrapperPlayClientInteractEntity(event);
InteractAction action = packet.getAction();
if (action != InteractAction.ATTACK) {
final boolean sneaking = packet.isSneaking().orElse(false);
final int entity = packet.getEntityId();
if (packet.getHand() == InteractionHand.OFF_HAND) {
if (action == InteractAction.INTERACT) {
if (!sentMainhand) {
if (flagAndAlert("Skipped Mainhand") && shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
}
}
sentMainhand = false;
} else if (sneaking != requiredSneaking || entity != requiredEntity) {
String verbose = "requiredEntity=" + requiredEntity + ", entity=" + entity
+ ", requiredSneaking=" + requiredSneaking + ", sneaking=" + sneaking;
if (flagAndAlert(verbose) && shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
}
}
} else {
requiredEntity = entity;
requiredSneaking = sneaking;
sentMainhand = true;
}
}
}
if (WrapperPlayClientPlayerFlying.isFlying(event.getPacketType()) && !player.packetStateData.lastPacketWasTeleport && !player.packetStateData.lastPacketWasOnePointSeventeenDuplicate) {
sentMainhand = false;
}
}
}
@@ -1,178 +0,0 @@
package ac.grim.grimac.checks.impl.packetorder;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.type.PostPredictionCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.protocol.packettype.PacketTypeCommon;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.world.BlockFace;
import com.github.retrooper.packetevents.wrapper.play.client.*;
import lombok.Getter;
import org.jetbrains.annotations.Contract;
@Getter
public final class PacketOrderProcessor extends Check implements PostPredictionCheck {
public PacketOrderProcessor(final GrimPlayer player) {
super(player);
}
private boolean openingInventory; // only pre 1.9 clients on pre 1.9 servers
private boolean swapping;
private boolean dropping;
private boolean interacting;
private boolean attacking;
private boolean releasing;
private boolean digging;
private boolean sprinting;
private boolean sneaking;
private boolean placing;
private boolean using;
private boolean picking;
private boolean clickingInInventory;
private boolean closingInventory;
private boolean quickMoveClicking;
private boolean pickUpClicking;
private boolean leavingBed;
private boolean startingToGlide;
private boolean jumpingWithMount;
@Override
public void onPacketReceive(PacketReceiveEvent event) {
final PacketTypeCommon packetType = event.getPacketType();
if (packetType == PacketType.Play.Client.CLIENT_STATUS) {
if (new WrapperPlayClientClientStatus(event).getAction() == WrapperPlayClientClientStatus.Action.OPEN_INVENTORY_ACHIEVEMENT) {
openingInventory = true;
}
}
if (packetType == PacketType.Play.Client.INTERACT_ENTITY) {
if (new WrapperPlayClientInteractEntity(event).getAction() == WrapperPlayClientInteractEntity.InteractAction.ATTACK) {
attacking = true;
} else {
interacting = true;
}
}
if (packetType == PacketType.Play.Client.PLAYER_DIGGING) {
switch (new WrapperPlayClientPlayerDigging(event).getAction()) {
case SWAP_ITEM_WITH_OFFHAND:
swapping = true;
break;
case DROP_ITEM:
case DROP_ITEM_STACK:
dropping = true;
break;
case RELEASE_USE_ITEM:
releasing = true;
break;
case FINISHED_DIGGING:
case CANCELLED_DIGGING:
case START_DIGGING:
digging = true;
break;
}
}
if (packetType == PacketType.Play.Client.ENTITY_ACTION) {
switch (new WrapperPlayClientEntityAction(event).getAction()) {
case START_SPRINTING:
case STOP_SPRINTING:
sprinting = true;
break;
case STOP_SNEAKING:
case START_SNEAKING:
sneaking = true;
break;
case LEAVE_BED:
leavingBed = true;
break;
case START_FLYING_WITH_ELYTRA:
startingToGlide = true;
break;
case OPEN_HORSE_INVENTORY:
openingInventory = true;
break;
case START_JUMPING_WITH_HORSE:
case STOP_JUMPING_WITH_HORSE:
jumpingWithMount = true;
break;
}
}
if (packetType == PacketType.Play.Client.USE_ITEM) {
using = true;
}
if (packetType == PacketType.Play.Client.PLAYER_BLOCK_PLACEMENT) {
if (new WrapperPlayClientPlayerBlockPlacement(event).getFace() == BlockFace.OTHER) {
using = true;
} else {
placing = true;
}
}
if (packetType == PacketType.Play.Client.PICK_ITEM) {
picking = true;
}
if (packetType == PacketType.Play.Client.CLICK_WINDOW) {
clickingInInventory = true;
switch (new WrapperPlayClientClickWindow(event).getWindowClickType()) {
case QUICK_MOVE:
quickMoveClicking = true;
break;
case PICKUP:
case PICKUP_ALL:
pickUpClicking = true;
break;
}
}
if (packetType == PacketType.Play.Client.CLOSE_WINDOW) {
closingInventory = true;
}
if (WrapperPlayClientPlayerFlying.isFlying(event.getPacketType()) && player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8) && !player.packetStateData.lastPacketWasTeleport) {
onTick();
}
}
@Override
public void onPredictionComplete(PredictionComplete predictionComplete) {
if (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_9)) {
onTick();
}
}
private void onTick() {
openingInventory = false;
swapping = false;
dropping = false;
attacking = false;
interacting = false;
releasing = false;
digging = false;
placing = false;
using = false;
picking = false;
sprinting = false;
sneaking = false;
clickingInInventory = false;
closingInventory = false;
quickMoveClicking = false;
pickUpClicking = false;
leavingBed = false;
startingToGlide = false;
jumpingWithMount = false;
}
@Contract(pure = true)
public boolean isRightClicking() {
return placing || using || interacting;
}
}
@@ -2,6 +2,7 @@ package ac.grim.grimac.checks.impl.post;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PacketCheck;
import ac.grim.grimac.checks.type.PostPredictionCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
@@ -18,13 +19,14 @@ import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPl
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerEntityAnimation;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import static com.github.retrooper.packetevents.protocol.packettype.PacketType.Play.Client.*;
@CheckData(name = "Post")
public class Post extends Check implements PostPredictionCheck {
public class PostCheck extends Check implements PacketCheck, PostPredictionCheck {
private final ArrayDeque<PacketTypeCommon> post = new ArrayDeque<>();
// Due to 1.9+ missing the idle packet, we must queue flags
// 1.8 clients will have the same logic for simplicity, although it's not needed
@@ -32,7 +34,7 @@ public class Post extends Check implements PostPredictionCheck {
private boolean sentFlying = false;
private int isExemptFromSwingingCheck = Integer.MIN_VALUE;
public Post(GrimPlayer playerData) {
public PostCheck(GrimPlayer playerData) {
super(playerData);
}
@@ -85,7 +87,6 @@ public class Post extends Check implements PostPredictionCheck {
post.clear();
sentFlying = false;
} else if (PLAYER_ABILITIES.equals(packetType)
|| (HELD_ITEM_CHANGE.equals(packetType) && player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_8))
|| INTERACT_ENTITY.equals(packetType) || PLAYER_BLOCK_PLACEMENT.equals(packetType)
|| USE_ITEM.equals(packetType) || PLAYER_DIGGING.equals(packetType)) {
if (sentFlying) post.add(event.getPacketType());
@@ -9,7 +9,7 @@ import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
import ac.grim.grimac.utils.lists.EvictingQueue;
import org.bukkit.ChatColor;
import org.bukkit.entity.Player;
import ac.grim.grimac.utils.vector.Vector3D;
import org.bukkit.util.Vector;
import java.util.HashSet;
import java.util.Set;
@@ -45,8 +45,8 @@ public class DebugHandler extends Check implements PostPredictionCheck {
ChatColor color = pickColor(offset, offset);
Vector3D predicted = player.predictedVelocity.vector;
Vector3D actually = player.actualMovement;
Vector predicted = player.predictedVelocity.vector;
Vector actually = player.actualMovement;
ChatColor xColor = pickColor(Math.abs(predicted.getX() - actually.getX()), offset);
ChatColor yColor = pickColor(Math.abs(predicted.getY() - actually.getY()), offset);
@@ -1,6 +1,5 @@
package ac.grim.grimac.checks.impl.prediction;
import ac.grim.grimac.api.config.ConfigManager;
import ac.grim.grimac.api.events.CompletePredictionEvent;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
@@ -100,12 +99,14 @@ public class OffsetHandler extends Check implements PostPredictionCheck {
}
@Override
public void onReload(ConfigManager config) {
setbackDecayMultiplier = config.getDoubleElse("Simulation.setback-decay-multiplier", 0.999);
threshold = config.getDoubleElse("Simulation.threshold", 0.001);
immediateSetbackThreshold = config.getDoubleElse("Simulation.immediate-setback-threshold", 0.1);
maxAdvantage = config.getDoubleElse("Simulation.max-advantage", 1);
maxCeiling = config.getDoubleElse("Simulation.max-ceiling", 4);
public void reload() {
super.reload();
setbackDecayMultiplier = getConfig().getDoubleElse("Simulation.setback-decay-multiplier", 0.999);
threshold = getConfig().getDoubleElse("Simulation.threshold", 0.001);
immediateSetbackThreshold = getConfig().getDoubleElse("Simulation.immediate-setback-threshold", 0.1);
maxAdvantage = getConfig().getDoubleElse("Simulation.max-advantage", 1);
maxCeiling = getConfig().getDoubleElse("Simulation.max-ceiling", 4);
if (maxAdvantage == -1) maxAdvantage = Double.MAX_VALUE;
if (immediateSetbackThreshold == -1) immediateSetbackThreshold = Double.MAX_VALUE;
}
@@ -1,6 +1,5 @@
package ac.grim.grimac.checks.impl.scaffolding;
import ac.grim.grimac.api.config.ConfigManager;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.BlockPlaceCheck;
import ac.grim.grimac.player.GrimPlayer;
@@ -30,7 +29,8 @@ public class AirLiquidPlace extends BlockPlaceCheck {
}
@Override
public void onReload(ConfigManager config) {
this.cancelVL = config.getIntElse(getConfigName() + ".cancelVL", 0);
public void reload() {
super.reload();
this.cancelVL = getConfig().getIntElse(getConfigName() + ".cancelVL", 0);
}
}
@@ -7,11 +7,11 @@ import ac.grim.grimac.utils.anticheat.update.BlockPlace;
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
import ac.grim.grimac.utils.math.VectorUtils;
import com.github.retrooper.packetevents.protocol.attribute.Attributes;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.player.GameMode;
import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
import com.github.retrooper.packetevents.util.Vector3i;
import ac.grim.grimac.utils.vector.Vector3D;
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
import org.bukkit.util.Vector;
@CheckData(name = "FarPlace")
public class FarPlace extends BlockPlaceCheck {
@@ -26,11 +26,10 @@ public class FarPlace extends BlockPlaceCheck {
if (place.getMaterial() == StateTypes.SCAFFOLDING) return;
double min = Double.MAX_VALUE;
final double[] possibleEyeHeights = player.getPossibleEyeHeights();
for (double d : possibleEyeHeights) {
for (double d : player.getPossibleEyeHeights()) {
SimpleCollisionBox box = new SimpleCollisionBox(blockPos);
Vector3D eyes = newVector3D(player.x, player.y + d, player.z);
Vector3D best = VectorUtils.cutBoxToVector(eyes, box);
Vector eyes = new Vector(player.x, player.y + d, player.z);
Vector best = VectorUtils.cutBoxToVector(eyes, box);
min = Math.min(min, eyes.distanceSquared(best));
}
@@ -1,224 +0,0 @@
package ac.grim.grimac.checks.impl.scaffolding;
import ac.grim.grimac.api.config.ConfigManager;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.BlockPlaceCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.BlockPlace;
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
import ac.grim.grimac.utils.data.HitData;
import ac.grim.grimac.utils.nmsutil.BlockRayTrace;
import com.github.retrooper.packetevents.protocol.attribute.Attributes;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.player.GameMode;
import com.github.retrooper.packetevents.protocol.world.BlockFace;
import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState;
import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
import com.github.retrooper.packetevents.util.Vector3d;
import com.github.retrooper.packetevents.util.Vector3i;
import java.util.HashSet;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
@CheckData(name = "LineOfSightPlace")
public class LineOfSightPlace extends BlockPlaceCheck {
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;
private boolean useBlockWhitelist;
private HashSet<StateType> blockWhitelist;
// 15 is the maximum set of Collision boxes that will be used in the ray trace check
// it corresponds to the size of the collision boxes from the modern Cauldron
// Since this is used per-player we can avoid calling new[] by allocating it per-player in the check
private final SimpleCollisionBox[] collisionBoxBuffer = new SimpleCollisionBox[15];
public final ConcurrentHashMap<Vector3i, WrappedBlockState> blocksChangedList = new ConcurrentHashMap<>();
public LineOfSightPlace(GrimPlayer player) {
super(player);
}
@Override
public void onBlockPlace(final BlockPlace place) {
if (checkIfShouldSkip(place)) return;
// if (flagBuffer > 0 && !didRayTraceHit(place)) {
// ignorePost = true;
// // If the player hit and has flagged this check recently
// if (flagAndAlert("pre-flying: " + player.compensatedWorld.getWrappedBlockStateAt(place.getPlacedAgainstBlockLocation()).getType()) && shouldModifyPackets() && shouldCancel()) {
// place.resync(); // Deny the block placement.
// }
// }
// Only flag (which will only happen in post) if we miss pre
if (didRayTraceHit(place)) {
ignorePost = true;
} else if (flagBuffer > 0) {
if (flagAndAlert("pre-flying: " + player.compensatedWorld.getWrappedBlockStateAt(place.getPlacedAgainstBlockLocation()).getType()) && shouldModifyPackets() && shouldCancel()) {
place.resync(); // Deny the block placement.
}
}
}
// Use post flying because it has the correct rotation, and can't false easily.
@Override
public void onPostFlyingBlockPlace(BlockPlace place) {
if (checkIfShouldSkip(place)) return;
// Don't flag twice
if (ignorePost) {
ignorePost = false;
return;
}
blocksChangedList.clear();
// Ray trace to try and hit the target block.
boolean hit = didRayTraceHit(place);
// This can false with rapidly moving yaw in 1.8+ clients
if (!hit) {
flagBuffer = 1;
flagAndAlert("post-flying: " + player.compensatedWorld.getWrappedBlockStateAt(place.getPlacedAgainstBlockLocation()).getType());
}
else {
flagBuffer = Math.max(0, flagBuffer - 0.1);
}
}
private boolean checkIfShouldSkip(BlockPlace place) {
StateType targetBlockStateType = player.compensatedWorld.getWrappedBlockStateAt(place.getPlacedAgainstBlockLocation()).getType();
if (player.gamemode == GameMode.SPECTATOR) return true; // A waste to check creative mode players
if (targetBlockStateType == StateTypes.REDSTONE_WIRE) return true; // Redstone too buggy
if (player.compensatedWorld.isNearHardEntity(player.boundingBox.copy().expand(4))) return true; // Shulkers and Pistons are too buggy
if (useBlockWhitelist) {
return !isBlockTypeWhitelisted(targetBlockStateType);
}
return false;
}
public void handleBlockChange(Vector3i vector3i, WrappedBlockState state) {
if (blocksChangedList.size() >= 60) return; // Don't let players freeze movement packets to grow this
// Only do this for nearby blocks
if (new Vector3d(vector3i.x, vector3i.y, vector3i.z).distanceSquared(new Vector3d(player.x, player.y, player.z)) > 36) return;
// Only do this if the state really had any world impact
// this line always just returns the current world state in compensatedWorld
// if (state.equals(player.compensatedWorld.getWrappedBlockStateAt(vector3i))) return;
blocksChangedList.put(vector3i, state);
}
private boolean didRayTraceHit(BlockPlace place) {
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);
}
double movementThreshold = player.getMovementThreshold();
SimpleCollisionBox eyePositions = new SimpleCollisionBox(player.x, player.y + minEyeHeight, player.z, player.x, player.y + maxEyeHeight, player.z);
eyePositions.expand(movementThreshold);
int[] interactBlockVec = new int[]{place.blockPosition.x, place.blockPosition.y, place.blockPosition.z};
BlockFace expectedBlockFace = place.getDirection();
// If the player is inside a block, then they can ray trace through the block and hit the other side of the block
// This may potentially be exploitable as a minor bypass
if (eyePositions.isIntersected(new SimpleCollisionBox(interactBlockVec[0], interactBlockVec[1], interactBlockVec[2]))) {
return true;
}
// End checking if the player is in the block
double[][] possibleLookDirs;
// 1.9+ players could be a tick behind because we don't get skipped ticks
if (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_9)) {
possibleLookDirs = new double[][]{
{player.xRot, player.yRot},
{player.lastXRot, player.lastYRot},
{player.lastXRot, player.yRot}
};
} else if (player.getClientVersion().isOlderThan(ClientVersion.V_1_8)) {
// 1.7 players do not have any of these issues! They are always on the latest look vector
possibleLookDirs = new double[][]{{player.xRot, player.yRot}};
} else {
possibleLookDirs = new double[][]{
{player.xRot, player.yRot},
{player.lastXRot, player.yRot}
};
}
// We do not need to add 0.03/0.0002 to maxDistance to ensure our raytrace hits blocks
// Since we expand the hitboxes of the expectedTargetBlock by 0.03/0.002 already later
double maxDistance = player.compensatedEntities.getSelf().getAttributeValue(Attributes.PLAYER_BLOCK_INTERACTION_RANGE);
// Define possible offsets
// TODO, vectorize this with SIMD or AVX for performance
double[][] offsets = {
{0, 0, 0},
{movementThreshold, 0, 0},
{-movementThreshold, 0, 0},
{0, movementThreshold, 0},
{0, -movementThreshold, 0},
{0, 0, movementThreshold},
{0, 0, -movementThreshold}
};
double[] eyePosition = new double[3];
double[] eyeLookDir = new double[3];
for (double eyeHeight : possibleEyeHeights) {
for (double[] lookDir : possibleLookDirs) {
for (double[] offset : offsets) {
eyePosition[0] = player.x + offset[0];
eyePosition[1] = player.y + eyeHeight + offset[1];
eyePosition[2] = player.z + offset[2];
calculateDirection(eyeLookDir, lookDir[0], lookDir[1]);
if (didRayTraceHitTargetBlock(eyePosition, eyeLookDir, maxDistance, interactBlockVec, expectedBlockFace)) {
return true; // If any possible face matches the client-side placement, assume it's legitimate
}
}
}
}
return false; // No matching face found
}
// Helper method to calculate direction (replace the Ray class method)
private void calculateDirection(double[] result, double xRot, double yRot) {
float rotX = (float) Math.toRadians(xRot);
float rotY = (float) Math.toRadians(yRot);
result[1] = -player.trigHandler.sin(rotY);
double xz = player.trigHandler.cos(rotY);
result[0] = -xz * player.trigHandler.sin(rotX);
result[2] = xz * player.trigHandler.cos(rotX);
}
private boolean didRayTraceHitTargetBlock(double[] eyePos, double[] eyeDir, double maxDistance, int[] targetBlockVec, BlockFace expectedBlockFace) {
HitData hitData = BlockRayTrace.getNearestReachHitResult(player, eyePos, eyeDir, maxDistance, maxDistance, targetBlockVec, expectedBlockFace, collisionBoxBuffer, null, false);
if (hitData == null || !hitData.success)
hitData = BlockRayTrace.getNearestReachHitResult(player, eyePos, eyeDir, maxDistance, maxDistance, targetBlockVec,
expectedBlockFace, collisionBoxBuffer, player.checkManager.getBlockPlaceCheck(LineOfSightPlace.class).blocksChangedList, false);
// we check for hitdata != null because of being in expanded hitbox, or there was no result, do we still need this?
return hitData != null && hitData.success;
}
private boolean isBlockTypeWhitelisted(StateType type) {
return blockWhitelist.contains(type);
}
@Override
public void onReload(ConfigManager config) {
useBlockWhitelist = config.getBooleanElse("LineOfSightPlace.use-block-whitelist", false);
blockWhitelist = new HashSet<>();
List<String> blocks = config.getStringList("LineOfSightPlace.block-whitelist");
for (String block : blocks) {
blockWhitelist.add(StateTypes.getByName(block));
}
}
}
@@ -1,74 +0,0 @@
package ac.grim.grimac.checks.impl.scaffolding;
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.MessageUtil;
import ac.grim.grimac.utils.anticheat.update.BlockPlace;
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.world.BlockFace;
import com.github.retrooper.packetevents.util.Vector3f;
import com.github.retrooper.packetevents.util.Vector3i;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerFlying;
import java.util.ArrayList;
import java.util.List;
@CheckData(name = "MultiPlace", experimental = true)
public class MultiPlace extends BlockPlaceCheck {
public MultiPlace(GrimPlayer player) {
super(player);
}
private final List<String> flags = new ArrayList<>();
private boolean hasPlaced;
private BlockFace lastFace;
private Vector3f lastCursor;
private Vector3i lastPos;
@Override
public void onBlockPlace(final BlockPlace place) {
final BlockFace face = place.getDirection();
final Vector3f cursor = place.getCursor();
final Vector3i pos = place.getPlacedAgainstBlockLocation();
if (hasPlaced && (face != lastFace || !cursor.equals(lastCursor) || !pos.equals(lastPos))) {
final String verbose = "face=" + face + ", lastFace=" + lastFace
+ ", cursor=" + MessageUtil.toUnlabledString(cursor) + ", lastCursor=" + MessageUtil.toUnlabledString(lastCursor)
+ ", pos=" + MessageUtil.toUnlabledString(pos) + ", lastPos=" + MessageUtil.toUnlabledString(lastPos);
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) {
if (flagAndAlert(verbose) && shouldModifyPackets() && shouldCancel()) {
place.resync();
}
} else {
flags.add(verbose);
}
}
lastFace = face;
lastCursor = cursor;
lastPos = pos;
hasPlaced = true;
}
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (WrapperPlayClientPlayerFlying.isFlying(event.getPacketType()) && !player.packetStateData.lastPacketWasTeleport && !player.packetStateData.lastPacketWasOnePointSeventeenDuplicate) {
hasPlaced = false;
}
}
@Override
public void onPredictionComplete(PredictionComplete predictionComplete) {
if (player.getClientVersion().isNewerThan(ClientVersion.V_1_8) && player.isTickingReliablyFor(3)) {
for (String verbose : flags) {
flagAndAlert(verbose);
}
}
flags.clear();
}
}
@@ -8,6 +8,7 @@ import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
import java.util.Collections;
@CheckData(name = "PositionPlace")
public class PositionPlace extends BlockPlaceCheck {
@@ -27,13 +28,8 @@ public class PositionPlace extends BlockPlaceCheck {
// Each position represents the best case scenario to have clicked
//
// We will now calculate the most optimal position for the player's head to be in
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);
}
double minEyeHeight = Collections.min(player.getPossibleEyeHeights());
double maxEyeHeight = Collections.max(player.getPossibleEyeHeights());
// I love the idle packet, why did you remove it mojang :(
// Don't give 0.03 lenience if the player is a 1.8 player and we know they couldn't have 0.03'd because idle packet
double movementThreshold = !player.packetStateData.didLastMovementIncludePosition || player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_9) ? player.getMovementThreshold() : 0;
@@ -1 +1,116 @@
package ac.grim.grimac.checks.impl.scaffolding;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.BlockPlaceCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.BlockPlace;
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
import ac.grim.grimac.utils.data.Pair;
import ac.grim.grimac.utils.nmsutil.Ray;
import ac.grim.grimac.utils.nmsutil.ReachUtils;
import com.github.retrooper.packetevents.protocol.attribute.Attributes;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.player.GameMode;
import com.github.retrooper.packetevents.protocol.world.BlockFace;
import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
import com.github.retrooper.packetevents.util.Vector3d;
import com.github.retrooper.packetevents.util.Vector3f;
import org.bukkit.util.Vector;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@CheckData(name = "RotationPlace")
public class RotationPlace extends BlockPlaceCheck {
double flagBuffer = 0; // If the player flags once, force them to play legit, or we will cancel the tick before.
boolean ignorePost = false;
public RotationPlace(GrimPlayer player) {
super(player);
}
@Override
public void onBlockPlace(final BlockPlace place) {
if (place.getMaterial() == StateTypes.SCAFFOLDING) return;
if (player.gamemode == GameMode.SPECTATOR) return; // you don't send flying packets when spectating entities
if (flagBuffer > 0 && !didRayTraceHit(place)) {
ignorePost = true;
// If the player hit and has flagged this check recently
if (flagAndAlert("pre-flying") && shouldModifyPackets() && shouldCancel()) {
place.resync(); // Deny the block placement.
}
}
}
// Use post flying because it has the correct rotation, and can't false easily.
@Override
public void onPostFlyingBlockPlace(BlockPlace place) {
if (place.getMaterial() == StateTypes.SCAFFOLDING) return;
if (player.gamemode == GameMode.SPECTATOR) return; // you don't send flying packets when spectating entities
// Don't flag twice
if (ignorePost) {
ignorePost = false;
return;
}
// Ray trace to try and hit the target block.
boolean hit = didRayTraceHit(place);
// This can false with rapidly moving yaw in 1.8+ clients
if (!hit) {
flagBuffer = 1;
flagAndAlert("post-flying");
} else {
flagBuffer = Math.max(0, flagBuffer - 0.1);
}
}
private boolean didRayTraceHit(BlockPlace place) {
SimpleCollisionBox box = new SimpleCollisionBox(place.getPlacedAgainstBlockLocation());
List<Vector3f> possibleLookDirs = new ArrayList<>(Arrays.asList(
new Vector3f(player.lastXRot, player.yRot, 0),
new Vector3f(player.xRot, player.yRot, 0)
));
// Start checking if player is in the block
double minEyeHeight = Collections.min(player.getPossibleEyeHeights());
double maxEyeHeight = Collections.max(player.getPossibleEyeHeights());
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
// 1.9+ players could be a tick behind because we don't get skipped ticks
if (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_9)) {
possibleLookDirs.add(new Vector3f(player.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(ClientVersion.V_1_8)) {
possibleLookDirs = Collections.singletonList(new Vector3f(player.xRot, player.yRot, 0));
}
final double distance = player.compensatedEntities.getSelf().getAttributeValue(Attributes.PLAYER_BLOCK_INTERACTION_RANGE);
for (double d : player.getPossibleEyeHeights()) {
for (Vector3f lookDir : possibleLookDirs) {
// x, y, z are correct for the block placement even after post tick because of code elsewhere
Vector3d starting = new Vector3d(player.x, player.y + d, player.z);
// xRot and yRot are a tick behind
Ray trace = new Ray(player, starting.getX(), starting.getY(), starting.getZ(), lookDir.getX(), lookDir.getY());
Pair<Vector, BlockFace> intercept = ReachUtils.calculateIntercept(box, trace.getOrigin(), trace.getPointAtDistance(distance));
if (intercept.getFirst() != null) return true;
}
}
return false;
}
}
@@ -1,6 +1,5 @@
package ac.grim.grimac.checks.impl.velocity;
import ac.grim.grimac.api.config.ConfigManager;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PostPredictionCheck;
@@ -19,14 +18,13 @@ import com.github.retrooper.packetevents.util.Vector3f;
import com.github.retrooper.packetevents.util.Vector3i;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerExplosion;
import lombok.Getter;
import ac.grim.grimac.utils.vector.Vector3D;
import org.bukkit.Bukkit;
import org.bukkit.util.Vector;
import org.jetbrains.annotations.Nullable;
import java.util.Deque;
import java.util.LinkedList;
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
@CheckData(name = "AntiExplosion", configName = "Explosion", setback = 10)
public class ExplosionHandler extends Check implements PostPredictionCheck {
Deque<VelocityData> firstBreadMap = new LinkedList<>();
@@ -123,7 +121,7 @@ public class ExplosionHandler extends Check implements PostPredictionCheck {
}
public void addPlayerExplosion(int breadOne, Vector3f explosion) {
firstBreadMap.add(new VelocityData(-1, breadOne, player.getSetbackTeleportUtil().isSendingSetback, newVector3D(explosion.getX(), explosion.getY(), explosion.getZ())));
firstBreadMap.add(new VelocityData(-1, breadOne, player.getSetbackTeleportUtil().isSendingSetback, new Vector(explosion.getX(), explosion.getY(), explosion.getZ())));
}
public void setPointThree(boolean isPointThree) {
@@ -258,10 +256,12 @@ public class ExplosionHandler extends Check implements PostPredictionCheck {
}
@Override
public void onReload(ConfigManager config) {
offsetToFlag = config.getDoubleElse("Explosion.threshold", 0.00001);
setbackVL = config.getDoubleElse("Explosion.setbackvl", 10);
public void reload() {
super.reload();
offsetToFlag = getConfig().getDoubleElse("Explosion.threshold", 0.00001);
setbackVL = getConfig().getDoubleElse("Explosion.setbackvl", 10);
if (setbackVL == -1) setbackVL = Double.MAX_VALUE;
}
}
@@ -1,7 +1,6 @@
package ac.grim.grimac.checks.impl.velocity;
import ac.grim.grimac.GrimAPI;
import ac.grim.grimac.api.config.ConfigManager;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PostPredictionCheck;
@@ -15,14 +14,12 @@ import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.util.Vector3d;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerEntityVelocity;
import lombok.Getter;
import ac.grim.grimac.utils.vector.Vector3D;
import org.bukkit.util.Vector;
import org.jetbrains.annotations.NotNull;
import java.util.Deque;
import java.util.LinkedList;
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
// We are making a velocity sandwich between two pieces of transaction packets (bread)
@CheckData(name = "AntiKB", alternativeName = "AntiKnockback", configName = "Knockback", setback = 10, decay = 0.025)
public class KnockbackHandler extends Check implements PostPredictionCheck {
@@ -72,12 +69,12 @@ public class KnockbackHandler extends Check implements PostPredictionCheck {
// Wrap velocity between two transactions
player.sendTransaction();
addPlayerKnockback(entityId, player.lastTransactionSent.get(), newVector3D(playerVelocity.getX(), playerVelocity.getY(), playerVelocity.getZ()));
addPlayerKnockback(entityId, player.lastTransactionSent.get(), new Vector(playerVelocity.getX(), playerVelocity.getY(), playerVelocity.getZ()));
event.getTasksAfterSend().add(player::sendTransaction);
}
}
@NotNull public Pair<VelocityData, Vector3D> getFutureKnockback() {
@NotNull public Pair<VelocityData, Vector> getFutureKnockback() {
// Chronologically in the future
if (firstBreadMap.size() > 0) {
VelocityData data = firstBreadMap.peek();
@@ -99,7 +96,7 @@ public class KnockbackHandler extends Check implements PostPredictionCheck {
return new Pair<>(null, null);
}
private void addPlayerKnockback(int entityID, int breadOne, Vector3D knockback) {
private void addPlayerKnockback(int entityID, int breadOne, Vector knockback) {
firstBreadMap.add(new VelocityData(entityID, breadOne, player.getSetbackTeleportUtil().isSendingSetback, knockback));
}
@@ -243,14 +240,15 @@ public class KnockbackHandler extends Check implements PostPredictionCheck {
}
@Override
public void onReload(ConfigManager config) {
offsetToFlag = config.getDoubleElse("Knockback.threshold", 0.001);
maxAdv = config.getDoubleElse("Knockback.max-advantage", 1);
immediate = config.getDoubleElse("Knockback.immediate-setback-threshold", 0.1);
multiplier = config.getDoubleElse("Knockback.setback-decay-multiplier", 0.999);
ceiling = config.getDoubleElse("Knockback.max-ceiling", 4);
public void reload() {
super.reload();
offsetToFlag = getConfig().getDoubleElse("Knockback.threshold", 0.001);
maxAdv = getConfig().getDoubleElse("Knockback.max-advantage", 1);
immediate = getConfig().getDoubleElse("Knockback.immediate-setback-threshold", 0.1);
multiplier = getConfig().getDoubleElse("Knockback.setback-decay-multiplier", 0.999);
ceiling = getConfig().getDoubleElse("Knockback.max-ceiling", 4);
if (maxAdv < 0) maxAdv = Double.MAX_VALUE;
if (immediate < 0) immediate = Double.MAX_VALUE;
}
}
@@ -1,12 +1,10 @@
package ac.grim.grimac.checks.type;
import ac.grim.grimac.api.config.ConfigManager;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.BlockPlace;
import ac.grim.grimac.utils.collisions.HitboxData;
import ac.grim.grimac.utils.collisions.datatypes.CollisionBox;
import ac.grim.grimac.utils.collisions.datatypes.ComplexCollisionBox;
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
import com.github.retrooper.packetevents.protocol.world.states.defaulttags.BlockTags;
import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
@@ -16,10 +14,9 @@ import com.github.retrooper.packetevents.util.Vector3i;
import java.util.ArrayList;
import java.util.List;
public class BlockPlaceCheck extends Check implements RotationCheck, PostPredictionCheck {
public class BlockPlaceCheck extends Check implements RotationCheck {
private static final List<StateType> weirdBoxes = new ArrayList<>();
private static final List<StateType> buggyBoxes = new ArrayList<>();
private final SimpleCollisionBox[] boxes = new SimpleCollisionBox[ComplexCollisionBox.DEFAULT_MAX_COLLISION_BOX_SIZE];
protected int cancelVL;
@@ -36,8 +33,9 @@ public class BlockPlaceCheck extends Check implements RotationCheck, PostPredict
}
@Override
public void onReload(ConfigManager config) {
this.cancelVL = config.getIntElse(getConfigName() + ".cancelVL", 5);
public void reload() {
super.reload();
this.cancelVL = getConfig().getIntElse(getConfigName() + ".cancelVL", 5);
}
protected boolean shouldCancel() {
@@ -71,11 +69,11 @@ public class BlockPlaceCheck extends Check implements RotationCheck, PostPredict
Vector3i clicked = place.getPlacedAgainstBlockLocation();
CollisionBox placedOn = HitboxData.getBlockHitbox(player, place.getMaterial(), player.getClientVersion(), player.compensatedWorld.getWrappedBlockStateAt(clicked), clicked.getX(), clicked.getY(), clicked.getZ());
int size = placedOn.downCast(boxes);
List<SimpleCollisionBox> boxes = new ArrayList<>();
placedOn.downCast(boxes);
SimpleCollisionBox combined = new SimpleCollisionBox(clicked.getX(), clicked.getY(), clicked.getZ());
for (int i = 0; i < size; i++) {
SimpleCollisionBox box = boxes[i];
for (SimpleCollisionBox box : boxes) {
double minX = Math.max(box.minX, combined.minX);
double minY = Math.max(box.minY, combined.minY);
double minZ = Math.max(box.minZ, combined.minZ);
@@ -1,67 +0,0 @@
package ac.grim.grimac.checks.type;
import ac.grim.grimac.player.GrimPlayer;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientCloseWindow;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerCloseWindow;
import org.jetbrains.annotations.MustBeInvokedByOverriders;
public class InventoryCheck extends BlockPlaceCheck implements PacketCheck {
// Impossible transaction ID
protected static final long NONE = Long.MAX_VALUE;
protected long closeTransaction = NONE;
protected int closePacketsToSkip;
public InventoryCheck(GrimPlayer player) {
super(player);
}
@Override
@MustBeInvokedByOverriders
public void onPacketReceive(final PacketReceiveEvent event) {
if (event.getPacketType() == PacketType.Play.Client.CLICK_WINDOW) {
// Disallow any clicks if inventory is closing
if (closeTransaction != NONE && shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
player.getInventory().needResend = true;
}
} else if (event.getPacketType() == PacketType.Play.Client.CLOSE_WINDOW) {
// Players with high ping can close inventory faster than send transaction back
if (closeTransaction != NONE && closePacketsToSkip-- <= 0) {
closeTransaction = NONE;
}
}
}
public void closeInventory() {
if (closeTransaction != NONE) {
return;
}
int windowId = player.getInventory().openWindowID;
player.user.writePacket(new WrapperPlayServerCloseWindow(windowId));
// Force close inventory on server side
closePacketsToSkip = 1; // Sending close packet to itself, so skip it
PacketEvents.getAPI().getProtocolManager().receivePacket(
player.user.getChannel(), new WrapperPlayClientCloseWindow(windowId)
);
player.sendTransaction();
int transaction = player.lastTransactionSent.get();
closeTransaction = transaction;
player.latencyUtils.addRealTimeTask(transaction, () -> {
if (closeTransaction == transaction) {
closeTransaction = NONE;
}
});
player.user.flushPackets();
}
}
@@ -1,6 +1,8 @@
package ac.grim.grimac.checks.type;
import ac.grim.grimac.api.AbstractCheck;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.RotationUpdate;
public interface RotationCheck extends AbstractCheck {
@@ -1,6 +1,8 @@
package ac.grim.grimac.checks.type;
import ac.grim.grimac.api.AbstractCheck;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.VehiclePositionUpdate;
public interface VehicleCheck extends AbstractCheck {
@@ -70,4 +70,4 @@ public class GrimDebug extends BaseCommand {
sender.sendMessage("Console output for " + (grimPlayer.bukkitPlayer == null ? grimPlayer.user.getProfile().getName() : grimPlayer.bukkitPlayer.getName()) + " is now " + isOutput);
}
}
}
@@ -1,9 +1,12 @@
package ac.grim.grimac.commands;
import ac.grim.grimac.GrimAPI;
import ac.grim.grimac.checks.impl.aim.processor.AimProcessor;
import ac.grim.grimac.checks.impl.misc.ClientBrand;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.MessageUtil;
import ac.grim.grimac.utils.anticheat.MultiLibUtil;
import ac.grim.grimac.utils.math.GrimMath;
import co.aikar.commands.BaseCommand;
import co.aikar.commands.annotation.CommandAlias;
import co.aikar.commands.annotation.CommandCompletion;
@@ -1,11 +1,15 @@
package ac.grim.grimac.commands;
import ac.grim.grimac.GrimAPI;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.MessageUtil;
import co.aikar.commands.BaseCommand;
import co.aikar.commands.annotation.CommandAlias;
import co.aikar.commands.annotation.CommandPermission;
import co.aikar.commands.annotation.Subcommand;
import com.github.retrooper.packetevents.netty.channel.ChannelHelper;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
@CommandAlias("grim|grimac")
@@ -14,15 +18,14 @@ public class GrimReload extends BaseCommand {
@CommandPermission("grim.reload")
public void onReload(CommandSender sender) {
//reload config
sender.sendMessage(MessageUtil.format("%prefix% &7Reloading config..."));
GrimAPI.INSTANCE.getExternalAPI().reloadAsync().exceptionally(throwable -> false)
.thenAccept(bool -> {
if (bool) {
sender.sendMessage(MessageUtil.format("%prefix% &fConfig has been reloaded."));
} else {
sender.sendMessage(MessageUtil.format("%prefix% &cFailed to reload config."));
}
});
try {
GrimAPI.INSTANCE.getExternalAPI().reload();
} catch (RuntimeException e) {
sender.sendMessage(ChatColor.RED + e.getMessage());
return;
}
sender.sendMessage(MessageUtil.format("%prefix% &fConfig has been reloaded."));
}
}
@@ -20,7 +20,7 @@ public class GrimSendAlert extends BaseCommand {
bukkitPlayer.sendMessage(string);
}
if (GrimAPI.INSTANCE.getConfigManager().isPrintAlertsToConsole()) {
if (GrimAPI.INSTANCE.getConfigManager().getConfig().getBooleanElse("alerts.print-to-console", true)) {
LogUtil.console(string); // Print alert to console
}
}
@@ -28,3 +28,4 @@ public class GrimStopSpectating extends BaseCommand {
}
}
}
@@ -3,12 +3,14 @@ package ac.grim.grimac.events.packets;
import ac.grim.grimac.GrimAPI;
import ac.grim.grimac.checks.impl.badpackets.BadPacketsX;
import ac.grim.grimac.checks.impl.badpackets.BadPacketsZ;
import ac.grim.grimac.checks.impl.inventory.InventoryB;
import ac.grim.grimac.events.packets.patch.ResyncWorldUtil;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.*;
import ac.grim.grimac.utils.blockplace.BlockPlaceResult;
import ac.grim.grimac.utils.blockplace.ConsumesBlockPlace;
import ac.grim.grimac.utils.collisions.HitboxData;
import ac.grim.grimac.utils.collisions.datatypes.CollisionBox;
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
import ac.grim.grimac.utils.data.*;
import ac.grim.grimac.utils.inventory.Inventory;
import ac.grim.grimac.utils.latency.CompensatedWorld;
@@ -21,6 +23,7 @@ import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.event.PacketSendEvent;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.ConnectionState;
import com.github.retrooper.packetevents.protocol.attribute.Attributes;
import com.github.retrooper.packetevents.protocol.item.ItemStack;
import com.github.retrooper.packetevents.protocol.item.type.ItemType;
import com.github.retrooper.packetevents.protocol.item.type.ItemTypes;
@@ -43,8 +46,9 @@ import com.github.retrooper.packetevents.wrapper.play.client.*;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerAcknowledgeBlockChanges;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerSetSlot;
import io.github.retrooper.packetevents.util.SpigotConversionUtil;
import java.util.function.Function;
import java.util.ArrayList;
import java.util.List;
import org.bukkit.util.Vector;
public class CheckManagerListener extends PacketListenerAbstract {
@@ -146,7 +150,7 @@ public class CheckManagerListener extends PacketListenerAbstract {
}
}
private static void handleBlockPlaceOrUseItem(PacketWrapper<?> packet, GrimPlayer player) {
private static void handleBlockPlaceOrUseItem(PacketWrapper packet, GrimPlayer player) {
// Legacy "use item" packet
if (packet instanceof WrapperPlayClientPlayerBlockPlacement &&
PacketEvents.getAPI().getServerManager().getVersion().isOlderThan(ServerVersion.V_1_9)) {
@@ -270,7 +274,7 @@ public class CheckManagerListener extends PacketListenerAbstract {
// Additionally, only yaw/pitch matters: https://github.com/GrimAnticheat/Grim/issues/1275#issuecomment-1872444018
// 1.9+ isn't impacted by this packet as much.
if (PacketEvents.getAPI().getServerManager().getVersion().isOlderThanOrEquals(ServerVersion.V_1_9)) {
if (player.isCancelDuplicatePacket()) {
if (GrimAPI.INSTANCE.getConfigManager().getConfig().getBooleanElse("cancel-duplicate-packet", true)) {
player.packetStateData.cancelDuplicatePacket = true;
}
} else {
@@ -280,7 +284,7 @@ public class CheckManagerListener extends PacketListenerAbstract {
player.packetStateData.lastPacketWasOnePointSeventeenDuplicate = true;
if (!player.isIgnoreDuplicatePacketRotation()) {
if (!GrimAPI.INSTANCE.getConfigManager().isIgnoreDuplicatePacketRotation()) {
if (player.xRot != location.getYaw() || player.yRot != location.getPitch()) {
player.lastXRot = player.xRot;
player.lastYRot = player.yRot;
@@ -297,11 +301,6 @@ public class CheckManagerListener extends PacketListenerAbstract {
return false;
}
// Manual filter on FINISH_DIGGING to prevent clients setting non-breakable blocks to air
private static final Function<StateType, Boolean> BREAKABLE = type -> {
return !type.isAir() && type.getHardness() != -1.0f && type != StateTypes.WATER && type != StateTypes.LAVA;
};
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (event.getConnectionState() != ConnectionState.PLAY) return;
@@ -355,7 +354,7 @@ public class CheckManagerListener extends PacketListenerAbstract {
if (WrapperPlayClientPlayerFlying.isFlying(event.getPacketType())) {
WrapperPlayClientPlayerFlying flying = new WrapperPlayClientPlayerFlying(event);
Location pos = flying.getLocation();
boolean ignoreRotation = player.packetStateData.lastPacketWasOnePointSeventeenDuplicate && player.isIgnoreDuplicatePacketRotation();
boolean ignoreRotation = player.packetStateData.lastPacketWasOnePointSeventeenDuplicate && GrimAPI.INSTANCE.getConfigManager().isIgnoreDuplicatePacketRotation();
handleFlying(player, pos.getX(), pos.getY(), pos.getZ(), ignoreRotation ? player.xRot : pos.getYaw(), ignoreRotation ? player.yRot : pos.getPitch(), flying.hasPositionChanged(), flying.hasRotationChanged(), flying.isOnGround(), teleportData, event);
}
@@ -383,26 +382,22 @@ public class CheckManagerListener extends PacketListenerAbstract {
if (event.getPacketType() == PacketType.Play.Client.PLAYER_DIGGING) {
WrapperPlayClientPlayerDigging dig = new WrapperPlayClientPlayerDigging(event);
final Vector3i digPosition = dig.getBlockPosition();
WrappedBlockState block = player.compensatedWorld.getWrappedBlockStateAt(digPosition);
final StateType type = block.getType();
WrappedBlockState block = player.compensatedWorld.getWrappedBlockStateAt(dig.getBlockPosition());
player.checkManager.getPacketCheck(InventoryB.class).handle(event, dig);
player.checkManager.getPacketCheck(BadPacketsX.class).handle(event, dig, type);
player.checkManager.getPacketCheck(BadPacketsX.class).handle(event, dig, block.getType());
player.checkManager.getPacketCheck(BadPacketsZ.class).handle(event, dig);
if (dig.getAction() == DiggingAction.FINISHED_DIGGING) {
// Not unbreakable
if (BREAKABLE.apply(type) && !event.isCancelled()) {
if (!block.getType().isAir() && block.getType().getHardness() != -1.0f && !event.isCancelled()) {
player.compensatedWorld.startPredicting();
player.compensatedWorld.updateBlock(digPosition.getX(), digPosition.getY(), digPosition.getZ(), 0);
player.compensatedWorld.updateBlock(dig.getBlockPosition().getX(), dig.getBlockPosition().getY(), dig.getBlockPosition().getZ(), 0);
player.compensatedWorld.stopPredicting(dig);
}
}
if (dig.getAction() == DiggingAction.START_DIGGING && !event.isCancelled()) {
double damage = BlockBreakSpeed.getBlockDamage(player, digPosition);
double damage = BlockBreakSpeed.getBlockDamage(player, dig.getBlockPosition());
//Instant breaking, no damage means it is unbreakable by creative players (with swords)
if (damage >= 1) {
@@ -410,9 +405,9 @@ public class CheckManagerListener extends PacketListenerAbstract {
if (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_13) && Materials.isWaterSource(player.getClientVersion(), block)) {
// Vanilla uses a method to grab water flowing, but as you can't break flowing water
// We can simply treat all waterlogged blocks or source blocks as source blocks
player.compensatedWorld.updateBlock(digPosition, StateTypes.WATER.createBlockState(CompensatedWorld.blockVersion));
player.compensatedWorld.updateBlock(dig.getBlockPosition(), StateTypes.WATER.createBlockState(CompensatedWorld.blockVersion));
} else {
player.compensatedWorld.updateBlock(digPosition.getX(), digPosition.getY(), digPosition.getZ(), 0);
player.compensatedWorld.updateBlock(dig.getBlockPosition().getX(), dig.getBlockPosition().getY(), dig.getBlockPosition().getZ(), 0);
}
player.compensatedWorld.stopPredicting(dig);
}
@@ -1,6 +1,9 @@
package ac.grim.grimac.events.packets;
import ac.grim.grimac.GrimAPI;
import ac.grim.grimac.checks.impl.movement.NoSlowC;
import ac.grim.grimac.checks.impl.movement.NoSlowD;
import ac.grim.grimac.checks.impl.movement.NoSlowE;
import ac.grim.grimac.player.GrimPlayer;
import com.github.retrooper.packetevents.event.PacketListenerAbstract;
import com.github.retrooper.packetevents.event.PacketListenerPriority;
@@ -1,6 +1,6 @@
package ac.grim.grimac.events.packets;
import ac.grim.grimac.api.config.ConfigManager;
import ac.grim.grimac.GrimAPI;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.type.PacketCheck;
import ac.grim.grimac.player.GrimPlayer;
@@ -53,6 +53,7 @@ public class PacketEntityReplication extends Check implements PacketCheck {
private final List<Integer> despawnedEntitiesThisTransaction = new ArrayList<>();
// Maximum ping when a firework boost is removed from the player.
private final int maxFireworkBoostPing = GrimAPI.INSTANCE.getConfigManager().getConfig().getIntElse("max-ping-firework-boost", 1000);
public PacketEntityReplication(GrimPlayer player) {
super(player);
@@ -499,12 +500,4 @@ public class PacketEntityReplication extends Check implements PacketCheck {
public void tickStartTick() {
hasSentPreWavePacket = false;
}
private int maxFireworkBoostPing = 1000;
@Override
public void onReload(ConfigManager config) {
maxFireworkBoostPing = config.getIntElse("max-ping-firework-boost", 1000);
}
}
@@ -1,6 +1,6 @@
package ac.grim.grimac.events.packets;
import ac.grim.grimac.api.config.ConfigManager;
import ac.grim.grimac.GrimAPI;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.type.PacketCheck;
import ac.grim.grimac.player.GrimPlayer;
@@ -37,6 +37,7 @@ public class PacketPlayerAbilities extends Check implements PacketCheck {
if (lastSentPlayerCanFly && !abilities.isFlightAllowed()) {
int noFlying = player.lastTransactionSent.get();
int maxFlyingPing = GrimAPI.INSTANCE.getConfigManager().getConfig().getIntElse("max-ping-out-of-flying", 1000);
if (maxFlyingPing != -1) {
player.runNettyTaskInMs(() -> {
if (player.lastTransactionReceived.get() < noFlying) {
@@ -55,12 +56,4 @@ public class PacketPlayerAbilities extends Check implements PacketCheck {
}
}
int maxFlyingPing = 1000;
@Override
public void onReload(ConfigManager config) {
maxFlyingPing = config.getIntElse("max-ping-out-of-flying", 1000);
}
}
@@ -8,8 +8,6 @@ import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.event.PacketListenerAbstract;
import com.github.retrooper.packetevents.event.PacketListenerPriority;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.attribute.Attributes;
import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes;
import com.github.retrooper.packetevents.protocol.item.ItemStack;
import com.github.retrooper.packetevents.protocol.item.enchantment.type.EnchantmentTypes;
@@ -74,13 +72,6 @@ public class PacketPlayerAttack extends PacketListenerAbstract {
player.maxPlayerAttackSlow = 1;
}
} else if (!isLegacyPlayer && player.isSprinting) {
// 1.9+ players who have attack speed cannot slow themselves twice in one tick because their attack cooldown gets reset on swing.
if (player.maxPlayerAttackSlow > 0
&& PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_9)
&& player.compensatedEntities.getSelf().getAttributeValue(Attributes.GENERIC_ATTACK_SPEED) < 16) { // 16 is a reasonable limit
return;
}
// 1.9+ player who might have been slowed, but we can't be sure
player.maxPlayerAttackSlow += 1;
}
@@ -2,6 +2,7 @@ package ac.grim.grimac.events.packets;
import ac.grim.grimac.GrimAPI;
import ac.grim.grimac.checks.impl.movement.NoSlowA;
import ac.grim.grimac.checks.impl.movement.NoSlowD;
import ac.grim.grimac.player.GrimPlayer;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.event.PacketListenerAbstract;
@@ -22,6 +23,7 @@ import com.github.retrooper.packetevents.protocol.player.GameMode;
import com.github.retrooper.packetevents.protocol.player.InteractionHand;
import com.github.retrooper.packetevents.protocol.world.BlockFace;
import com.github.retrooper.packetevents.wrapper.play.client.*;
import org.bukkit.Bukkit;
public class PacketPlayerDigging extends PacketListenerAbstract {
@@ -170,12 +172,10 @@ public class PacketPlayerDigging extends PacketListenerAbstract {
if (WrapperPlayClientPlayerFlying.isFlying(event.getPacketType())) {
final GrimPlayer player = GrimAPI.INSTANCE.getPlayerDataManager().getPlayer(event.getUser());
if (player != null && player.packetStateData.isSlowedByUsingItem()
&& !player.packetStateData.lastPacketWasTeleport
&& !player.packetStateData.lastPacketWasOnePointSeventeenDuplicate) {
if (player.packetStateData.eatingHand != InteractionHand.OFF_HAND
&& player.packetStateData.getSlowedByUsingItemSlot() != player.packetStateData.lastSlotSelected
|| player.getInventory().getItemInHand(player.packetStateData.eatingHand).isEmpty()) {
if (player == null) return;
if (!player.packetStateData.lastPacketWasTeleport && !player.packetStateData.lastPacketWasOnePointSeventeenDuplicate) {
if (player.packetStateData.isSlowedByUsingItem() && player.packetStateData.getSlowedByUsingItemSlot() != player.packetStateData.lastSlotSelected) {
player.packetStateData.setSlowedByUsingItem(false);
player.checkManager.getPostPredictionCheck(NoSlowA.class).didSlotChangeLastTick = true;
}
@@ -196,7 +196,7 @@ public class PacketPlayerDigging extends PacketListenerAbstract {
if (player.packetStateData.lastSlotSelected != slot) {
// just assume they tick after this
if (!player.isTickingReliablyFor(3) && player.skippedTickInActualMovement && player.packetStateData.eatingHand != InteractionHand.OFF_HAND) {
if (!player.isTickingReliablyFor(3) && player.skippedTickInActualMovement) {
player.packetStateData.setSlowedByUsingItem(false);
}
}
@@ -220,7 +220,13 @@ public class PacketPlayerDigging extends PacketListenerAbstract {
final ItemStack item = hand == InteractionHand.MAIN_HAND ?
player.getInventory().getHeldItem() : player.getInventory().getOffHand();
final boolean wasSlow = player.packetStateData.isSlowedByUsingItem();
handleUseItem(player, item, hand);
if (!wasSlow) {
player.checkManager.getPostPredictionCheck(NoSlowD.class).startedSprintingBeforeUse = player.packetStateData.isSlowedByUsingItem() && player.isSprinting;
}
}
}
}
@@ -19,13 +19,11 @@ import com.github.retrooper.packetevents.util.Vector3d;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerJoinGame;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerRespawn;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerUpdateHealth;
import ac.grim.grimac.utils.vector.Vector3D;
import org.bukkit.util.Vector;
import java.util.List;
import java.util.Objects;
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
/**
* PlayerRespawnS2CPacket info (1.20.2+):
* If the world is different (check via registry key), world is recreated (all entities etc destroyed).
@@ -132,6 +130,7 @@ public class PacketPlayerRespawn extends PacketListenerAbstract {
player.compensatedEntities.serverPositionsMap.clear();
}
// TODO: What does keep all metadata do?
player.latencyUtils.addRealTimeTask(player.lastTransactionSent.get() + 1, () -> {
player.isSneaking = false;
player.lastOnGround = false;
@@ -142,16 +141,8 @@ public class PacketPlayerRespawn extends PacketListenerAbstract {
player.packetStateData.lastClaimedPosition = new Vector3d();
player.filterMojangStupidityOnMojangStupidity = new Vector3d();
final boolean keepTrackedData = this.hasFlag(respawn, KEEP_TRACKED_DATA);
if (!keepTrackedData) {
player.powderSnowFrozenTicks = 0;
player.compensatedEntities.getSelf().hasGravity = true;
player.playerEntityHasGravity = true;
}
if (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_19_4)) {
if (!keepTrackedData) {
if (!this.hasFlag(respawn, KEEP_TRACKED_DATA)) {
player.isSprinting = false;
}
} else {
@@ -187,12 +178,13 @@ public class PacketPlayerRespawn extends PacketListenerAbstract {
player.compensatedEntities.hasSprintingAttributeEnabled = false;
}
player.pose = Pose.STANDING;
player.clientVelocity = newVector3D();
player.clientVelocity = new Vector();
player.gamemode = respawn.getGameMode();
if (PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_17)) {
player.compensatedWorld.setDimension(respawn.getDimensionType(), event.getUser());
}
// TODO And there should probably be some attribute holder that we can just call reset() on.
if (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_16) && !this.hasFlag(respawn, KEEP_ATTRIBUTES)) {
// Reset attributes if not kept
player.compensatedEntities.getSelf().resetAttributes();
@@ -10,6 +10,7 @@ import com.github.retrooper.packetevents.event.PacketListenerAbstract;
import com.github.retrooper.packetevents.event.PacketListenerPriority;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientSteerVehicle;
@@ -1,165 +0,0 @@
package ac.grim.grimac.events.packets;
import ac.grim.grimac.GrimAPI;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.data.packetentity.PacketEntitySelf;
import ac.grim.grimac.utils.inventory.InventoryDesyncStatus;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.event.PacketListenerAbstract;
import com.github.retrooper.packetevents.event.PacketListenerPriority;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.event.PacketSendEvent;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientClientStatus;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientClientStatus.Action;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerFlying;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerOpenWindow;
public class PacketPlayerWindow extends PacketListenerAbstract {
public PacketPlayerWindow() {
super(PacketListenerPriority.LOWEST);
}
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (WrapperPlayClientPlayerFlying.isFlying(event.getPacketType()) && !event.isCancelled()) {
GrimPlayer player = GrimAPI.INSTANCE.getPlayerDataManager().getPlayer(event.getUser());
if (player == null) return;
if (player.hasInventoryOpen && isNearNetherPortal(player)) {
handleInventoryClose(player, InventoryDesyncStatus.NETHER_PORTAL);
}
}
// Client Status is sent in 1.7-1.8
if (event.getPacketType() == PacketType.Play.Client.CLIENT_STATUS) {
WrapperPlayClientClientStatus wrapper = new WrapperPlayClientClientStatus(event);
if (wrapper.getAction() == Action.OPEN_INVENTORY_ACHIEVEMENT) {
GrimPlayer player = GrimAPI.INSTANCE.getPlayerDataManager().getPlayer(event.getUser());
if (player == null) return;
handleInventoryOpen(player);
}
}
// We need to do this due to 1.9 not sending anymore the inventory action in the Client Status
if (event.getPacketType() == PacketType.Play.Client.CLICK_WINDOW) {
GrimPlayer player = GrimAPI.INSTANCE.getPlayerDataManager().getPlayer(event.getUser());
if (player == null) return;
// TODO: Remove this check after we finish the before ViaVersion injection
// Explanation: On 1.7 and 1.8 we have OPEN_INVENTORY_ACHIEVEMENT on CLIENT_STATUS packet
// but after Via translation this information gets lost
// This is a workaround to atleast make our inventory checks work "decently" in 1.8 clients for 1.9+ servers
if (PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_9)
&& player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) {
handleInventoryOpen(player);
}
if (player.getClientVersion().isNewerThan(ClientVersion.V_1_8)) {
handleInventoryOpen(player);
}
}
if (event.getPacketType() == PacketType.Play.Client.CLOSE_WINDOW) {
GrimPlayer player = GrimAPI.INSTANCE.getPlayerDataManager().getPlayer(event.getUser());
if (player == null) return;
handleInventoryClose(player, InventoryDesyncStatus.NOT_DESYNCED);
}
}
@Override
public void onPacketSend(PacketSendEvent event) {
if (event.getPacketType() == PacketType.Play.Server.RESPAWN) {
GrimPlayer player = GrimAPI.INSTANCE.getPlayerDataManager().getPlayer(event.getUser());
if (player == null) return;
player.sendTransaction();
player.latencyUtils.addRealTimeTask(player.lastTransactionSent.get(),
() -> handleInventoryClose(player, InventoryDesyncStatus.NOT_DESYNCED));
} else if (event.getPacketType() == PacketType.Play.Server.OPEN_WINDOW) {
WrapperPlayServerOpenWindow wrapper = new WrapperPlayServerOpenWindow(event);
GrimPlayer player = GrimAPI.INSTANCE.getPlayerDataManager().getPlayer(event.getUser());
if (player == null) return;
player.sendTransaction();
String legacyType = wrapper.getLegacyType();
int modernType = wrapper.getType();
InventoryDesyncStatus inventoryDesyncStatus = getContainerDesyncStatus(player, legacyType, modernType);
player.latencyUtils.addRealTimeTask(player.lastTransactionSent.get(),
() -> {
if (inventoryDesyncStatus == InventoryDesyncStatus.NOT_DESYNCED) {
handleInventoryOpen(player);
} else {
handleInventoryClose(player, inventoryDesyncStatus);
}
});
} else if (event.getPacketType() == PacketType.Play.Server.OPEN_HORSE_WINDOW) {
GrimPlayer player = GrimAPI.INSTANCE.getPlayerDataManager().getPlayer(event.getUser());
if (player == null) return;
player.sendTransaction();
player.latencyUtils.addRealTimeTask(player.lastTransactionSent.get(),
() -> handleInventoryOpen(player));
} else if (event.getPacketType() == PacketType.Play.Server.CLOSE_WINDOW) {
GrimPlayer player = GrimAPI.INSTANCE.getPlayerDataManager().getPlayer(event.getUser());
if (player == null) return;
player.sendTransaction();
player.latencyUtils.addRealTimeTask(player.lastTransactionSent.get(),
() -> handleInventoryClose(player, InventoryDesyncStatus.NOT_DESYNCED));
}
}
private void handleInventoryOpen(GrimPlayer player) {
if (!player.hasInventoryOpen) {
player.lastInventoryOpen = System.currentTimeMillis();
}
player.hasInventoryOpen = true;
}
private void handleInventoryClose(GrimPlayer player, InventoryDesyncStatus desyncStatus) {
player.hasInventoryOpen = false;
player.inventoryDesyncStatus = desyncStatus;
}
public InventoryDesyncStatus getContainerDesyncStatus(GrimPlayer player, String legacyType, int modernType) {
// Closing beacon with the cross button cause desync in 1.7-1.8
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8) &&
("minecraft:beacon".equals(legacyType) || modernType == 8)) {
return player.inventoryDesyncStatus = InventoryDesyncStatus.BEACON;
}
if (isNearNetherPortal(player)) {
return player.inventoryDesyncStatus = InventoryDesyncStatus.NETHER_PORTAL;
}
return player.inventoryDesyncStatus = InventoryDesyncStatus.NOT_DESYNCED;
}
public boolean isNearNetherPortal(GrimPlayer player) {
// Going inside nether portal with opened inventory cause desync, fixed in 1.12.2
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_12_1) &&
player.pointThreeEstimator.isNearNetherPortal) {
PacketEntitySelf playerEntity = player.compensatedEntities.getSelf();
// Client ignore nether portal if player has passengers or riding an entity
if (!playerEntity.inVehicle() && playerEntity.passengers.isEmpty()) {
return true;
}
}
return false;
}
}
@@ -1,6 +1,7 @@
package ac.grim.grimac.events.packets;
import ac.grim.grimac.GrimAPI;
import ac.grim.grimac.checks.impl.movement.NoSlowD;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.nmsutil.WatchableIndexUtil;
import com.github.retrooper.packetevents.PacketEvents;
@@ -23,7 +24,6 @@ import java.util.List;
import java.util.Optional;
public class PacketSelfMetadataListener extends PacketListenerAbstract {
public PacketSelfMetadataListener() {
super(PacketListenerPriority.HIGH);
}
@@ -68,7 +68,6 @@ public class PacketSelfMetadataListener extends PacketListenerAbstract {
// Remove the pose metadata from the list
metadataStuff.removeIf(element -> element.getIndex() == 6);
entityMetadata.setEntityMetadata(metadataStuff);
event.markForReEncode(true);
}
EntityData watchable = WatchableIndexUtil.getIndex(entityMetadata.getEntityMetadata(), 0);
@@ -208,6 +207,8 @@ public class PacketSelfMetadataListener extends PacketListenerAbstract {
// The above line is a hack to fake activate use item
player.packetStateData.setSlowedByUsingItem(isActive);
player.checkManager.getPostPredictionCheck(NoSlowD.class).startedSprintingBeforeUse = player.packetStateData.isSlowedByUsingItem() && player.isSprinting;
if (isActive) {
player.packetStateData.eatingHand = isOffhand ? InteractionHand.OFF_HAND : InteractionHand.MAIN_HAND;
}
@@ -6,12 +6,14 @@ import ac.grim.grimac.utils.data.Pair;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.event.PacketListenerAbstract;
import com.github.retrooper.packetevents.event.PacketListenerPriority;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.event.PacketSendEvent;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.teleport.RelativeFlag;
import com.github.retrooper.packetevents.util.Vector3d;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientTeleportConfirm;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerPlayerPositionAndLook;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerVehicleMove;
import org.bukkit.Location;
@@ -5,11 +5,14 @@ import ac.grim.grimac.player.GrimPlayer;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.event.PacketListenerAbstract;
import com.github.retrooper.packetevents.event.PacketListenerPriority;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.event.PacketSendEvent;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.protocol.packettype.PacketTypeCommon;
import com.github.retrooper.packetevents.protocol.player.GameMode;
import com.github.retrooper.packetevents.protocol.player.UserProfile;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerFlying;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerEntityMetadata;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerPlayerInfo;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerPlayerInfoUpdate;
@@ -17,15 +20,22 @@ import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
public class PacketHidePlayerInfo extends PacketListenerAbstract {
public PacketHidePlayerInfo() {
public class PacketSetWrapperNull extends PacketListenerAbstract {
// It's faster (and less buggy) to simply not re-encode the wrapper unless we changed something
// The two packets we change are clientbound entity metadata (to fix a netcode issue)
// and the serverbound player flying packets (to patch NoFall)
public PacketSetWrapperNull() {
super(PacketListenerPriority.HIGHEST);
}
@Override
public void onPacketSend(PacketSendEvent event) {
if (event.getPacketType() == PacketType.Play.Server.PLAYER_INFO) {
if (event.getPacketType() == PacketType.Play.Server.ENTITY_METADATA) {
WrapperPlayServerEntityMetadata wrapper = new WrapperPlayServerEntityMetadata(event);
if (wrapper.getEntityId() != event.getUser().getEntityId()) {
event.setLastUsedWrapper(null);
}
} else if (event.getPacketType() == PacketType.Play.Server.PLAYER_INFO) {
//iterate through players and fake their game mode if they are spectating via grim spectate
if (PacketEvents.getAPI().getServerManager().getVersion().isOlderThanOrEquals(ServerVersion.V_1_12_2))
return;
@@ -52,8 +62,8 @@ public class PacketHidePlayerInfo extends PacketListenerAbstract {
//if amount of hidden players is the amount of players updated & is an update game mode action just cancel it
if (hideCount == nmsPlayerInfoDataList.size() && info.getAction() == WrapperPlayServerPlayerInfo.Action.UPDATE_GAME_MODE) {
event.setCancelled(true);
} else if (hideCount > 0) {
event.markForReEncode(true);
} else if (hideCount <= 0) {
event.setLastUsedWrapper(null);
}
}
} else if (event.getPacketType() == PacketType.Play.Server.PLAYER_INFO_UPDATE) {
@@ -71,13 +81,12 @@ public class PacketHidePlayerInfo extends PacketListenerAbstract {
for (WrapperPlayServerPlayerInfoUpdate.PlayerInfo entry : wrapper.getEntries()) {
//check if the player should be hidden
WrapperPlayServerPlayerInfoUpdate.PlayerInfo modifiedPacket = null;
final UserProfile gameProfile = entry.getGameProfile();
if (GrimAPI.INSTANCE.getSpectateManager().shouldHidePlayer(receiver, gameProfile.getUUID())) {
if (GrimAPI.INSTANCE.getSpectateManager().shouldHidePlayer(receiver, entry.getProfileId())) {
hideCount++;
//modify & create a new packet from pre-existing one if they are a spectator
if (entry.getGameMode() == GameMode.SPECTATOR) {
modifiedPacket = new WrapperPlayServerPlayerInfoUpdate.PlayerInfo(
gameProfile,
entry.getGameProfile(),
entry.isListed(),
entry.getLatency(),
GameMode.SURVIVAL,
@@ -95,20 +104,30 @@ public class PacketHidePlayerInfo extends PacketListenerAbstract {
} //if only the game mode was updated and the packet was modified, don't send anything
}
// if the amount of hidden players & modified entries are the same
if (hideCount == modified.size()) {
//if no hidden players, don't modify packet
if (hideCount <= 0) {
event.setLastUsedWrapper(null);
} else if (hideCount == modified.size()) { //if the amount of hidden players & modified entries are the same
if (onlyGameMode) { // if only the game mode changed, cancel
event.setCancelled(true);
} else { //if more than the game mode changed, remove the action
wrapper.getActions().remove(WrapperPlayServerPlayerInfoUpdate.Action.UPDATE_GAME_MODE);
event.markForReEncode(true);
}
} else { //modify entries
wrapper.setEntries(modified);
event.markForReEncode(true);
}
}
} else if (event.getPacketType() != PacketType.Play.Server.PLAYER_POSITION_AND_LOOK) {
event.setLastUsedWrapper(null);
}
}
@Override
public void onPacketReceive(PacketReceiveEvent event) {
PacketTypeCommon packetType = event.getPacketType();
if (!WrapperPlayClientPlayerFlying.isFlying(packetType) && packetType != PacketType.Play.Client.CLIENT_SETTINGS && !event.isCancelled()) {
event.setLastUsedWrapper(null);
}
}
}
@@ -99,4 +99,4 @@ public class ProxyAlertMessenger extends PacketListenerAbstract {
if (!file.exists()) return false;
return YamlConfiguration.loadConfiguration(file).getBoolean(pathToValue);
}
}
}
@@ -37,8 +37,7 @@ public class ResyncWorldUtil {
// Takes 0.15ms or so to complete. Not bad IMO. Unsure how I could improve this other than sending packets async.
// But that's on PacketEvents.
FoliaScheduler.getRegionScheduler().execute(GrimAPI.INSTANCE.getPlugin(), player.bukkitPlayer.getWorld(),
minBlockX >> 4, minBlockZ >> 4, () -> {
FoliaScheduler.getEntityScheduler().execute(player.bukkitPlayer, GrimAPI.INSTANCE.getPlugin(), () -> {
boolean flat = PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_13);
if (player.bukkitPlayer == null) return;
@@ -112,6 +111,6 @@ public class ResyncWorldUtil {
}
}
}
});
}, null, 0);
}
}
@@ -174,4 +174,4 @@ public class BasePacketWorldReader extends PacketListenerAbstract {
player.latencyUtils.addRealTimeTask(player.lastTransactionSent.get(), () -> player.compensatedWorld.updateBlock(blockChange.getX(), blockChange.getY(), blockChange.getZ(), blockChange.getBlockId()));
}
}
}
}
@@ -1,24 +1,22 @@
package ac.grim.grimac.manager;
import ac.grim.grimac.api.AbstractCheck;
import ac.grim.grimac.checks.impl.aim.AimDuplicateLook;
import ac.grim.grimac.checks.impl.aim.AimModulo360;
import ac.grim.grimac.checks.impl.aim.processor.AimProcessor;
import ac.grim.grimac.checks.impl.badpackets.*;
import ac.grim.grimac.checks.impl.combat.MultiInteractA;
import ac.grim.grimac.checks.impl.combat.MultiInteractB;
import ac.grim.grimac.checks.impl.combat.Reach;
import ac.grim.grimac.checks.impl.crash.*;
import ac.grim.grimac.checks.impl.exploit.ExploitA;
import ac.grim.grimac.checks.impl.exploit.ExploitB;
import ac.grim.grimac.checks.impl.groundspoof.NoFallA;
import ac.grim.grimac.checks.impl.inventory.*;
import ac.grim.grimac.checks.impl.misc.ClientBrand;
import ac.grim.grimac.checks.impl.misc.FastBreak;
import ac.grim.grimac.checks.impl.misc.GhostBlockMitigation;
import ac.grim.grimac.checks.impl.misc.TransactionOrder;
import ac.grim.grimac.checks.impl.movement.*;
import ac.grim.grimac.checks.impl.packetorder.*;
import ac.grim.grimac.checks.impl.post.Post;
import ac.grim.grimac.checks.impl.post.PostCheck;
import ac.grim.grimac.checks.impl.prediction.DebugHandler;
import ac.grim.grimac.checks.impl.prediction.NoFallB;
import ac.grim.grimac.checks.impl.prediction.OffsetHandler;
@@ -44,16 +42,8 @@ import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.event.PacketSendEvent;
import com.google.common.collect.ClassToInstanceMap;
import com.google.common.collect.ImmutableClassToInstanceMap;
import org.bukkit.Bukkit;
import org.bukkit.permissions.Permission;
import org.bukkit.permissions.PermissionDefault;
import java.util.concurrent.atomic.AtomicBoolean;
public class CheckManager {
private static boolean inited;
private static final AtomicBoolean initedAtomic = new AtomicBoolean(false);
ClassToInstanceMap<PacketCheck> packetChecks;
ClassToInstanceMap<PositionCheck> positionCheck;
ClassToInstanceMap<RotationCheck> rotationCheck;
@@ -86,10 +76,12 @@ public class CheckManager {
.put(BadPacketsE.class, new BadPacketsE(player))
.put(BadPacketsF.class, new BadPacketsF(player))
.put(BadPacketsG.class, new BadPacketsG(player))
.put(BadPacketsH.class, new BadPacketsH(player))
.put(BadPacketsI.class, new BadPacketsI(player))
.put(BadPacketsJ.class, new BadPacketsJ(player))
.put(BadPacketsK.class, new BadPacketsK(player))
.put(BadPacketsL.class, new BadPacketsL(player))
.put(BadPacketsM.class, new BadPacketsM(player))
.put(BadPacketsN.class, new BadPacketsN(player))
.put(BadPacketsP.class, new BadPacketsP(player))
.put(BadPacketsQ.class, new BadPacketsQ(player))
@@ -102,16 +94,8 @@ public class CheckManager {
.put(BadPacketsX.class, new BadPacketsX(player))
.put(BadPacketsY.class, new BadPacketsY(player))
.put(BadPacketsZ.class, new BadPacketsZ(player))
.put(InventoryA.class, new InventoryA(player))
.put(InventoryB.class, new InventoryB(player))
.put(InventoryE.class, new InventoryE(player))
.put(InventoryF.class, new InventoryF(player))
.put(InventoryG.class, new InventoryG(player))
.put(FastBreak.class, new FastBreak(player))
.put(PacketOrderB.class, new PacketOrderB(player))
.put(PacketOrderC.class, new PacketOrderC(player))
.put(PacketOrderD.class, new PacketOrderD(player))
.put(PacketOrderP.class, new PacketOrderP(player))
.put(TransactionOrder.class, new TransactionOrder(player))
.put(NoSlowB.class, new NoSlowB(player))
.put(SetbackBlocker.class, new SetbackBlocker(player)) // Must be last class otherwise we can't check while blocking packets
.build();
@@ -134,20 +118,8 @@ public class CheckManager {
.put(ExplosionHandler.class, new ExplosionHandler(player))
.put(KnockbackHandler.class, new KnockbackHandler(player))
.put(GhostBlockDetector.class, new GhostBlockDetector(player))
.put(InventoryD.class, new InventoryD(player))
.put(Phase.class, new Phase(player))
.put(PacketOrderProcessor.class, player.packetOrderProcessor)
.put(Post.class, new Post(player))
.put(PacketOrderA.class, new PacketOrderA(player))
.put(PacketOrderE.class, new PacketOrderE(player))
.put(PacketOrderF.class, new PacketOrderF(player))
.put(PacketOrderG.class, new PacketOrderG(player))
.put(PacketOrderH.class, new PacketOrderH(player))
.put(PacketOrderI.class, new PacketOrderI(player))
.put(PacketOrderJ.class, new PacketOrderJ(player))
.put(PacketOrderK.class, new PacketOrderK(player))
.put(PacketOrderL.class, new PacketOrderL(player))
.put(PacketOrderM.class, new PacketOrderM(player))
.put(PostCheck.class, new PostCheck(player))
.put(NoFallB.class, new NoFallB(player))
.put(OffsetHandler.class, new OffsetHandler(player))
.put(SuperDebug.class, new SuperDebug(player))
@@ -157,8 +129,6 @@ public class CheckManager {
.put(NoSlowC.class, new NoSlowC(player))
.put(NoSlowD.class, new NoSlowD(player))
.put(NoSlowE.class, new NoSlowE(player))
.put(MultiInteractA.class, new MultiInteractA(player))
.put(MultiInteractB.class, new MultiInteractB(player))
.put(SetbackTeleportUtil.class, new SetbackTeleportUtil(player)) // Avoid teleporting to new position, update safe pos last
.put(CompensatedFireworks.class, player.compensatedFireworks)
.put(SneakingEstimator.class, new SneakingEstimator(player))
@@ -166,18 +136,14 @@ public class CheckManager {
.build();
blockPlaceCheck = new ImmutableClassToInstanceMap.Builder<BlockPlaceCheck>()
.put(InventoryC.class, new InventoryC(player))
.put(InvalidPlaceA.class, new InvalidPlaceA(player))
.put(InvalidPlaceB.class, new InvalidPlaceB(player))
.put(AirLiquidPlace.class, new AirLiquidPlace(player))
.put(MultiPlace.class, new MultiPlace(player))
.put(FarPlace.class, new FarPlace(player))
.put(FabricatedPlace.class, new FabricatedPlace(player))
.put(PositionPlace.class, new PositionPlace(player))
.put(PacketOrderN.class, new PacketOrderN(player))
.put(PacketOrderO.class, new PacketOrderO(player))
.put(RotationPlace.class, new RotationPlace(player))
.put(DuplicateRotPlace.class, new DuplicateRotPlace(player))
.put(LineOfSightPlace.class, new LineOfSightPlace(player))
.put(GhostBlockMitigation.class, new GhostBlockMitigation(player))
.build();
@@ -205,8 +171,6 @@ public class CheckManager {
.putAll(blockPlaceCheck)
.putAll(prePredictionChecks)
.build();
init();
}
@SuppressWarnings("unchecked")
@@ -237,9 +201,6 @@ public class CheckManager {
for (PostPredictionCheck check : postPredictionCheck.values()) {
check.onPacketReceive(packet);
}
for (BlockPlaceCheck check : blockPlaceCheck.values()) {
check.onPacketReceive(packet);
}
}
public void onPacketSend(final PacketSendEvent packet) {
@@ -252,9 +213,6 @@ public class CheckManager {
for (PostPredictionCheck check : postPredictionCheck.values()) {
check.onPacketSend(packet);
}
for (BlockPlaceCheck check : blockPlaceCheck.values()) {
check.onPacketSend(packet);
}
}
public void onPositionUpdate(final PositionUpdate position) {
@@ -282,9 +240,6 @@ public class CheckManager {
for (PostPredictionCheck check : postPredictionCheck.values()) {
check.onPredictionComplete(complete);
}
for (BlockPlaceCheck check : blockPlaceCheck.values()) {
check.onPredictionComplete(complete);
}
}
public void onBlockPlace(final BlockPlace place) {
@@ -308,17 +263,11 @@ public class CheckManager {
return (T) packetChecks.get(check);
}
@SuppressWarnings("unchecked")
public <T extends BlockPlaceCheck> T getBlockPlaceCheck(Class<T> check) {
return (T) blockPlaceCheck.get(check);
}
@SuppressWarnings("unchecked")
public <T extends PacketCheck> T getPrePredictionCheck(Class<T> check) {
return (T) prePredictionChecks.get(check);
}
private PacketEntityReplication packetEntityReplication = null;
public PacketEntityReplication getEntityReplication() {
@@ -365,25 +314,4 @@ public class CheckManager {
public <T extends PostPredictionCheck> T getPostPredictionCheck(Class<T> check) {
return (T) postPredictionCheck.get(check);
}
private void init() {
// Fast non-thread safe check
if (inited) return;
// Slow thread safe check
if (!initedAtomic.compareAndSet(false, true)) return;
inited = true;
for (AbstractCheck check : allChecks.values()) {
if (check.getCheckName() != null) {
String permissionName = "grim.exempt." + check.getCheckName().toLowerCase();
Permission permission = Bukkit.getPluginManager().getPermission(permissionName);
if (permission == null) {
Bukkit.getPluginManager().addPermission(new Permission(permissionName, PermissionDefault.FALSE));
} else {
permission.setDefault(PermissionDefault.FALSE);
}
}
}
}
}
@@ -1,44 +1,56 @@
package ac.grim.grimac.manager.config;
package ac.grim.grimac.manager;
import ac.grim.grimac.GrimAC;
import ac.grim.grimac.GrimAPI;
import ac.grim.grimac.api.common.BasicReloadable;
import ac.grim.grimac.api.config.ConfigManager;
import ac.grim.grimac.utils.anticheat.LogUtil;
import github.scarsz.configuralize.DynamicConfig;
import github.scarsz.configuralize.Language;
import lombok.Getter;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
public class ConfigManagerFileImpl implements ConfigManager, BasicReloadable {
public class ConfigManager {
@Getter
private final DynamicConfig config;
@Getter
private final File configFile = new File(GrimAPI.INSTANCE.getPlugin().getDataFolder(), "config.yml");
@Getter
private final File messagesFile = new File(GrimAPI.INSTANCE.getPlugin().getDataFolder(), "messages.yml");
@Getter
private final File discordFile = new File(GrimAPI.INSTANCE.getPlugin().getDataFolder(), "discord.yml");
@Getter
private final File punishFile = new File(GrimAPI.INSTANCE.getPlugin().getDataFolder(), "punishments.yml");
@Getter
private int maxPingTransaction = 60; // This is just a really hot variable so cache it.
@Getter
private boolean ignoreDuplicatePacketRotation = false;
private File getConfigFile(String path) {
return new File(GrimAPI.INSTANCE.getPlugin().getDataFolder(), path);
}
@Getter
private boolean experimentalChecks = false;
public ConfigManagerFileImpl() {
config = new DynamicConfig();
}
private final List<Pattern> ignoredClientPatterns = new ArrayList<>();
private boolean initialized = false;
public ConfigManager() {
upgrade();
@Override
public void reload() {
// load config
GrimAPI.INSTANCE.getPlugin().getDataFolder().mkdirs();
if (!initialized) {
initialized = true;
upgrade();
config.addSource(GrimAC.class, "config", getConfigFile("config.yml"));
config.addSource(GrimAC.class, "messages", getConfigFile("messages.yml"));
config.addSource(GrimAC.class, "discord", getConfigFile("discord.yml"));
config.addSource(GrimAC.class, "punishments", getConfigFile("punishments.yml"));
}
//
config = new DynamicConfig();
config.addSource(GrimAC.class, "config", getConfigFile());
config.addSource(GrimAC.class, "messages", getMessagesFile());
config.addSource(GrimAC.class, "discord", getDiscordFile());
config.addSource(GrimAC.class, "punishments", getPunishFile());
reload();
}
public void reload() {
String languageCode = System.getProperty("user.language").toUpperCase();
try {
@@ -65,6 +77,24 @@ public class ConfigManagerFileImpl implements ConfigManager, BasicReloadable {
} catch (Exception e) {
throw new RuntimeException("Failed to load config", e);
}
maxPingTransaction = config.getIntElse("max-transaction-time", 60);
ignoredClientPatterns.clear();
for (String string : config.getStringList("client-brand.ignored-clients")) {
try {
ignoredClientPatterns.add(Pattern.compile(string));
} catch (PatternSyntaxException e) {
throw new RuntimeException("Failed to compile client pattern", e);
}
}
experimentalChecks = config.getBooleanElse("experimental-checks", false);
ignoreDuplicatePacketRotation = config.getBooleanElse("ignore-duplicate-packet-rotation", false);
}
public boolean isIgnoredClient(String brand) {
for (Pattern pattern : ignoredClientPatterns) {
if (pattern.matcher(brand).find()) return true;
}
return false;
}
private void upgrade() {
@@ -82,7 +112,7 @@ public class ConfigManagerFileImpl implements ConfigManager, BasicReloadable {
configVersion = Integer.parseInt(configStringVersion);
// TODO: Do we have to hardcode this?
configString = configString.replaceAll("config-version: " + configStringVersion, "config-version: 10");
configString = configString.replaceAll("config-version: " + configStringVersion, "config-version: 9");
Files.write(config.toPath(), configString.getBytes());
upgradeModernConfig(config, configString, configVersion);
@@ -124,9 +154,6 @@ public class ConfigManagerFileImpl implements ConfigManager, BasicReloadable {
if (configVersion < 9) {
newOffsetHandlingAntiKB(config, configString);
}
if (configVersion < 10) {
addInventoryPunishments();
}
}
private void removeLegacyTwoPointOne(File config) throws IOException {
@@ -177,28 +204,6 @@ public class ConfigManagerFileImpl implements ConfigManager, BasicReloadable {
}
}
private void addInventoryPunishments() {
File config = new File(GrimAPI.INSTANCE.getPlugin().getDataFolder(), "punishments.yml");
String configString;
if (config.exists()) {
try {
configString = new String(Files.readAllBytes(config.toPath()));
// If it works, it isn't stupid. Only replace it if it exactly matches the default config.
String reachSection = " Reach:";
String inventorySection = " Inventory:\n remove-violations-after: 300\n checks:\n - \"Inventory\"\n commands:\n - \"10:10 [alert]\"\n - \"20:20 [webhook]\"\n - \"20:20 [proxy]\"\n";
int index = configString.indexOf(reachSection);
if (index != -1) {
configString = configString.substring(0, index) + inventorySection + configString.substring(index);
}
Files.write(config.toPath(), configString.getBytes());
} catch (IOException ignored) {
}
}
}
private void fixBadPacketsAndAdjustPingConfig(File config, String configString) {
try {
configString = configString.replaceAll("max-ping: \\d+", "max-transaction-time: 60");
@@ -312,45 +317,4 @@ public class ConfigManagerFileImpl implements ConfigManager, BasicReloadable {
);
Files.write(config.toPath(), configString.getBytes());
}
@Override
public String getStringElse(String key, String otherwise) {
return config.getStringElse(key, otherwise);
}
@Override
public List<String> getStringList(String key) {
return config.getStringList(key);
}
@Override
public List<String> getStringListElse(String key, List<String> otherwise) {
return config.getStringListElse(key, otherwise);
}
@Override
public int getIntElse(String key, int other) {
return config.getIntElse(key, other);
}
@Override
public long getLongElse(String key, long otherwise) {
return config.getLongElse(key, otherwise);
}
@Override
public double getDoubleElse(String key, double otherwise) {
return config.getDoubleElse(key, otherwise);
}
@Override
public boolean getBooleanElse(String key, boolean otherwise) {
return config.getBooleanElse(key, otherwise);
}
@Override
public <T> T get(String key) {
return config.get(key);
}
}
@@ -19,7 +19,6 @@ public class DiscordManager implements Initable {
private static WebhookClient client;
private int embedColor;
private String staticContent = "";
private String embedTitle = "";
public static final Pattern WEBHOOK_PATTERN = Pattern.compile("(?:https?://)?(?:\\w+\\.)?\\w+\\.\\w+/api(?:/v\\d+)?/webhooks/(\\d+)/([\\w-]+)(?:/(?:\\w+)?)?");
@@ -41,8 +40,6 @@ public class DiscordManager implements Initable {
client = WebhookClient.withId(Long.parseUnsignedLong(matcher.group(1)), matcher.group(2));
client.setTimeout(15000); // Requests expire after 15 seconds
embedTitle = GrimAPI.INSTANCE.getConfigManager().getConfig().getStringElse("embed-title", "**Grim Alert**");
try {
embedColor = Color.decode(GrimAPI.INSTANCE.getConfigManager().getConfig().getStringElse("embed-color", "#00FFFF")).getRGB();
} catch (NumberFormatException e) {
@@ -73,7 +70,7 @@ public class DiscordManager implements Initable {
public void sendAlert(GrimPlayer player, String verbose, String checkName, String violations) {
if (client != null) {
String content = staticContent;
String content = staticContent + "";
content = content.replace("%check%", checkName);
content = content.replace("%violations%", violations);
content = GrimAPI.INSTANCE.getExternalAPI().replaceVariables(player, content, false);
@@ -83,7 +80,7 @@ public class DiscordManager implements Initable {
.setImageUrl("https://i.stack.imgur.com/Fzh0w.png") // Constant width
.setThumbnailUrl("https://crafthead.net/helm/" + player.user.getProfile().getUUID())
.setColor(embedColor)
.setTitle(new WebhookEmbed.EmbedTitle(embedTitle, null))
.setTitle(new WebhookEmbed.EmbedTitle("**Grim Alert**", null))
.setDescription(content)
.setTimestamp(Instant.now())
.setFooter(new WebhookEmbed.EmbedFooter("", "https://grim.ac/images/grim.png"));
@@ -1,72 +1,60 @@
package ac.grim.grimac.manager;
import ac.grim.grimac.GrimAPI;
import ac.grim.grimac.GrimExternalAPI;
import ac.grim.grimac.manager.init.Initable;
import ac.grim.grimac.manager.init.load.PacketEventsInit;
import ac.grim.grimac.manager.init.start.*;
import ac.grim.grimac.manager.init.stop.TerminatePacketEvents;
import com.google.common.collect.ImmutableList;
import lombok.Getter;
import com.google.common.collect.ClassToInstanceMap;
import com.google.common.collect.ImmutableClassToInstanceMap;
public class InitManager {
private final ImmutableList<Initable> initializersOnLoad;
private final ImmutableList<Initable> initializersOnStart;
private final ImmutableList<Initable> initializersOnStop;
@Getter private boolean loaded = false;
@Getter private boolean started = false;
@Getter private boolean stopped = false;
ClassToInstanceMap<Initable> initializersOnLoad;
ClassToInstanceMap<Initable> initializersOnStart;
ClassToInstanceMap<Initable> initializersOnStop;
public InitManager() {
initializersOnLoad = ImmutableList.<Initable>builder()
.add(new PacketEventsInit())
.add(() -> GrimAPI.INSTANCE.getExternalAPI().load())
initializersOnLoad = new ImmutableClassToInstanceMap.Builder<Initable>()
.put(PacketEventsInit.class, new PacketEventsInit())
.build();
initializersOnStart = ImmutableList.<Initable>builder()
.add(GrimAPI.INSTANCE.getExternalAPI())
.add(new ExemptOnlinePlayers())
.add(new EventManager())
.add(new PacketManager())
.add(new ViaBackwardsManager())
.add(new TickRunner())
.add(new TickEndEvent())
.add(new CommandRegister())
.add(new BStats())
.add(new PacketLimiter())
.add(GrimAPI.INSTANCE.getDiscordManager())
.add(GrimAPI.INSTANCE.getSpectateManager())
.add(new JavaVersion())
.add(new ViaVersion())
initializersOnStart = new ImmutableClassToInstanceMap.Builder<Initable>()
.put(ExemptOnlinePlayers.class, new ExemptOnlinePlayers())
.put(EventManager.class, new EventManager())
.put(PacketManager.class, new PacketManager())
.put(ViaBackwardsManager.class, new ViaBackwardsManager())
.put(TickRunner.class, new TickRunner())
.put(TickEndEvent.class, new TickEndEvent())
.put(CommandRegister.class, new CommandRegister())
.put(BStats.class, new BStats())
.put(PacketLimiter.class, new PacketLimiter())
.put(DiscordManager.class, GrimAPI.INSTANCE.getDiscordManager())
.put(SpectateManager.class, GrimAPI.INSTANCE.getSpectateManager())
.put(GrimExternalAPI.class, GrimAPI.INSTANCE.getExternalAPI())
.put(JavaVersion.class, new JavaVersion())
.build();
initializersOnStop = ImmutableList.<Initable>builder()
.add(new TerminatePacketEvents())
initializersOnStop = new ImmutableClassToInstanceMap.Builder<Initable>()
.put(TerminatePacketEvents.class, new TerminatePacketEvents())
.build();
}
public void load() {
for (Initable initable : initializersOnLoad) handle(initable);
loaded = true;
}
public void start() {
for (Initable initable : initializersOnStart) handle(initable);
started = true;
}
public void stop() {
for (Initable initable : initializersOnStop) handle(initable);
stopped = true;
}
private void handle(Initable initable) {
try {
for (Initable initable : initializersOnLoad.values()) {
initable.start();
} catch (Exception e) {
e.printStackTrace();
}
}
}
public void start() {
for (Initable initable : initializersOnStart.values()) {
initable.start();
}
}
public void stop() {
for (Initable initable : initializersOnStop.values()) {
initable.start();
}
}
}
@@ -2,16 +2,14 @@ package ac.grim.grimac.manager;
import ac.grim.grimac.GrimAPI;
import ac.grim.grimac.api.AbstractCheck;
import ac.grim.grimac.api.config.ConfigManager;
import ac.grim.grimac.api.config.ConfigReloadable;
import ac.grim.grimac.api.events.CommandExecuteEvent;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.events.packets.ProxyAlertMessenger;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.LogUtil;
import ac.grim.grimac.utils.anticheat.MessageUtil;
import github.scarsz.configuralize.DynamicConfig;
import io.github.retrooper.packetevents.util.folia.FoliaScheduler;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import lombok.Getter;
import lombok.Setter;
import org.bukkit.Bukkit;
@@ -19,27 +17,21 @@ import org.bukkit.entity.Player;
import java.util.*;
public class PunishmentManager implements ConfigReloadable {
public class PunishmentManager {
GrimPlayer player;
List<PunishGroup> groups = new ArrayList<>();
String experimentalSymbol = "*";
private String alertString;
private boolean testMode;
private boolean printToConsole;
private String proxyAlertString = "";
public PunishmentManager(GrimPlayer player) {
this.player = player;
reload();
}
@Override
public void reload(ConfigManager config) {
public void reload() {
DynamicConfig config = GrimAPI.INSTANCE.getConfigManager().getConfig();
List<String> punish = config.getStringListElse("Punishments", new ArrayList<>());
experimentalSymbol = config.getStringElse("experimental-symbol", "*");
alertString = config.getStringElse("alerts-format", "%prefix% &f%player% &bfailed &f%check_name% &f(x&c%vl%&f) &7%verbose%");
testMode = config.getBooleanElse("test-mode", false);
printToConsole = config.getBooleanElse("verbose.print-to-console", false);
proxyAlertString = config.getStringElse("alerts-format-proxy", "%prefix% &f[&cproxy&f] &f%player% &bfailed &f%check_name% &f(x&c%vl%&f) &7%verbose%");
try {
groups.clear();
@@ -106,7 +98,7 @@ public class PunishmentManager implements ConfigReloadable {
original = MessageUtil.format(original
.replace("[alert]", alertString)
.replace("[proxy]", alertString)
.replace("%check_name%", check.getDisplayName())
.replace("%check_name%", check.getCheckName())
.replace("%experimental%", check.isExperimental() ? experimentalSymbol : "")
.replace("%vl%", vl)
.replace("%verbose%", verbose)
@@ -119,6 +111,8 @@ public class PunishmentManager implements ConfigReloadable {
}
public boolean handleAlert(GrimPlayer player, String verbose, Check check) {
String alertString = GrimAPI.INSTANCE.getConfigManager().getConfig().getStringElse("alerts-format", "%prefix% &f%player% &bfailed &f%check_name% &f(x&c%vl%&f) &7%verbose%");
boolean testMode = GrimAPI.INSTANCE.getConfigManager().getConfig().getBooleanElse("test-mode", false);
boolean sentDebug = false;
// Check commands
@@ -134,7 +128,7 @@ public class PunishmentManager implements ConfigReloadable {
for (Player bukkitPlayer : GrimAPI.INSTANCE.getAlertManager().getEnabledVerbose()) {
bukkitPlayer.sendMessage(cmd);
}
if (printToConsole) {
if (GrimAPI.INSTANCE.getConfigManager().getConfig().getBooleanElse("verbose.print-to-console", false)) {
LogUtil.console(cmd); // Print verbose to console
}
}
@@ -150,9 +144,11 @@ public class PunishmentManager implements ConfigReloadable {
if (command.command.equals("[webhook]")) {
String vl = group.violations.values().stream().filter((e) -> e == check).count() + "";
GrimAPI.INSTANCE.getDiscordManager().sendAlert(player, verbose, check.getDisplayName(), vl);
GrimAPI.INSTANCE.getDiscordManager().sendAlert(player, verbose, check.getCheckName(), vl);
} else if (command.command.equals("[proxy]")) {
ProxyAlertMessenger.sendPluginMessage(replaceAlertPlaceholders(command.getCommand(), group, check, proxyAlertString, verbose));
String proxyAlertString = GrimAPI.INSTANCE.getConfigManager().getConfig().getStringElse("alerts-format-proxy", "%prefix% &f[&cproxy&f] &f%player% &bfailed &f%check_name% &f(x&c%vl%&f) &7%verbose%");
proxyAlertString = replaceAlertPlaceholders(command.getCommand(), group, check, proxyAlertString, verbose);
ProxyAlertMessenger.sendPluginMessage(proxyAlertString);
} else {
if (command.command.equals("[alert]")) {
sentDebug = true;
@@ -164,8 +160,9 @@ public class PunishmentManager implements ConfigReloadable {
}
String finalCmd = cmd;
FoliaScheduler.getGlobalRegionScheduler().run(GrimAPI.INSTANCE.getPlugin(), (dummy) ->
Bukkit.dispatchCommand(Bukkit.getConsoleSender(), finalCmd));
FoliaScheduler.getGlobalRegionScheduler().run(GrimAPI.INSTANCE.getPlugin(), (dummy) -> {
Bukkit.dispatchCommand(Bukkit.getConsoleSender(), finalCmd);
});
}
}
@@ -184,7 +181,7 @@ public class PunishmentManager implements ConfigReloadable {
group.violations.put(currentTime, check);
// Remove violations older than the defined time in the config
group.violations.long2ObjectEntrySet().removeIf(time -> currentTime - time.getLongKey() > group.removeViolationsAfter);
group.violations.entrySet().removeIf(time -> currentTime - time.getKey() > group.removeViolationsAfter);
}
}
}
@@ -196,7 +193,7 @@ class PunishGroup {
@Getter
List<ParsedCommand> commands;
@Getter
Long2ObjectOpenHashMap<Check> violations = new Long2ObjectOpenHashMap<>();
HashMap<Long, Check> violations = new HashMap<>();
@Getter
int removeViolationsAfter;
@@ -19,7 +19,6 @@ import ac.grim.grimac.utils.math.VectorUtils;
import ac.grim.grimac.utils.nmsutil.Collisions;
import ac.grim.grimac.utils.nmsutil.GetBoundingBox;
import ac.grim.grimac.utils.nmsutil.ReachUtils;
import ac.grim.grimac.utils.vector.Vector3D;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.player.GameMode;
@@ -32,15 +31,13 @@ import lombok.Getter;
import lombok.Setter;
import org.bukkit.Location;
import org.bukkit.entity.Entity;
import ac.grim.grimac.utils.vector.Vector3D;
import org.bukkit.util.Vector;
import java.util.Collections;
import java.util.HashSet;
import java.util.Random;
import java.util.concurrent.ConcurrentLinkedQueue;
import static ac.grim.grimac.utils.vector.VectorFactory.newVector3D;
public class SetbackTeleportUtil extends Check implements PostPredictionCheck {
// Sync to netty
public final ConcurrentLinkedQueue<TeleportData> pendingTeleports = new ConcurrentLinkedQueue<>();
@@ -68,7 +65,7 @@ public class SetbackTeleportUtil extends Check implements PostPredictionCheck {
@Override
public void onPredictionComplete(final PredictionComplete predictionComplete) {
// Grab friction now when we know player on ground and other variables
Vector3D afterTickFriction = player.clientVelocity.clone();
Vector afterTickFriction = player.clientVelocity.clone();
// We must first check if the player has accepted their setback
// If the setback isn't complete, then this position is illegitimate
@@ -118,7 +115,7 @@ public class SetbackTeleportUtil extends Check implements PostPredictionCheck {
return false;
}
private void simulateFriction(Vector3D vector) {
private void simulateFriction(Vector vector) {
// We must always do this before simulating positions, as this is the last actual (safe) movement
// We must not do this for knockback or explosions, as they are at the start of the tick
if (player.wasTouchingWater) {
@@ -126,9 +123,9 @@ public class SetbackTeleportUtil extends Check implements PostPredictionCheck {
} else if (player.wasTouchingLava) {
vector.multiply(0.5D);
if (player.hasGravity)
vector.add(newVector3D(0.0D, -player.gravity / 4.0D, 0.0D));
vector.add(new Vector(0.0D, -player.gravity / 4.0D, 0.0D));
} else if (player.isGliding) {
PredictionEngineElytra.getElytraMovement(player, vector, ReachUtils.getLook(player, player.xRot, player.yRot)).multiply(player.stuckSpeedMultiplier).multiply(newVector3D(0.99F, 0.98F, 0.99F));
PredictionEngineElytra.getElytraMovement(player, vector, ReachUtils.getLook(player, player.xRot, player.yRot)).multiply(player.stuckSpeedMultiplier).multiply(new Vector(0.99F, 0.98F, 0.99F));
vector.setY(vector.getY() - 0.05); // Make the player fall a bit
} else { // Gliding doesn't have friction, we handle it differently
PredictionEngineNormal.staticVectorEndOfTick(player, vector); // Lava and normal movement
@@ -150,9 +147,9 @@ public class SetbackTeleportUtil extends Check implements PostPredictionCheck {
lastWorldResync = System.currentTimeMillis();
}
Vector3D clientVel = lastKnownGoodPosition.vector.clone();
Vector clientVel = lastKnownGoodPosition.vector.clone();
Pair<VelocityData, Vector3D> futureKb = player.checkManager.getKnockbackHandler().getFutureKnockback();
Pair<VelocityData, Vector> futureKb = player.checkManager.getKnockbackHandler().getFutureKnockback();
VelocityData futureExplosion = player.checkManager.getExplosionHandler().getFutureExplosion();
// Velocity sets
@@ -172,7 +169,7 @@ public class SetbackTeleportUtil extends Check implements PostPredictionCheck {
// Mini prediction engine - simulate collisions
if (simulateNextTickPosition) {
Vector3D collide = Collisions.collide(player, clientVel.getX(), clientVel.getY(), clientVel.getZ());
Vector collide = Collisions.collide(player, clientVel.getX(), clientVel.getY(), clientVel.getZ());
position = position.withX(position.getX() + collide.getX());
// 1.8 players need the collision epsilon to not phase into blocks when being setback
@@ -415,7 +412,7 @@ public class SetbackTeleportUtil extends Check implements PostPredictionCheck {
data = new TeleportData(safePosition, flags, transaction, teleportId);
requiredSetBack = new SetBackData(data, player.xRot, player.yRot, null, false, plugin);
this.lastKnownGoodPosition = new SetbackPosWithVector(safePosition, newVector3D());
this.lastKnownGoodPosition = new SetbackPosWithVector(safePosition, new Vector());
}
@AllArgsConstructor
@@ -423,6 +420,6 @@ public class SetbackTeleportUtil extends Check implements PostPredictionCheck {
@Setter
public static class SetbackPosWithVector {
private Vector3d pos;
private Vector3D vector;
private Vector vector;
}
}
@@ -4,7 +4,6 @@ import ac.grim.grimac.manager.tick.Tickable;
import ac.grim.grimac.manager.tick.impl.ClientVersionSetter;
import ac.grim.grimac.manager.tick.impl.ResetTick;
import ac.grim.grimac.manager.tick.impl.TickInventory;
import ac.grim.grimac.manager.tick.impl.UpdateChangedBlocksList;
import com.google.common.collect.ClassToInstanceMap;
import com.google.common.collect.ImmutableClassToInstanceMap;
@@ -23,7 +22,6 @@ public class TickManager {
asyncTick = new ImmutableClassToInstanceMap.Builder<Tickable>()
.put(ClientVersionSetter.class, new ClientVersionSetter()) // Async because permission lookups might take a while, depending on the plugin
.put(TickInventory.class, new TickInventory()) // Async because I've never gotten an exception from this. It's probably safe.
.put(UpdateChangedBlocksList.class, new UpdateChangedBlocksList())
.build();
}
Loaded 100 of 241 files, more files were not shown because too many files have changed in this diff. Show more