Compare commits

...
Author SHA1 Message Date
Axionize 4933fd9d4f feat: add breakpoint-tolerant clock system and refactor GrimPlayer time handling
This commit Implement zero-cost breakpoint-tolerant time tracking.

It introduces a new time architecture for GrimPlayer that allows developers to pause execution at breakpoints without triggering Timer, FastBreak, or Velocity violations upon resumption.

Changes:
- Refactored `GrimPlayer` to use `nano()`/`now()` overrides instead of direct System calls.
- Added `DebugGrimPlayer` subclass which uses `ThreadLocal` to track intra-packet execution time.
- Logic automatically absorbs execution pauses >200ms (breakpoints) while correctly preserving inter-packet delays (server lag/AFK).
- Added `processingPacket()` hooks to the Packet Dispatcher and Tick Manager to manage thread-local timers.
- Performance impact is 0 in production due to JIT monomorphic inlining and dead code elimination of the base class methods.
2025-11-27 20:23:11 -05:00
Nils Gereke c2c044f61b feat: support for postgresql (#2368)
* feat: support for postgresql
feat: fabric now includes jdbc driver

* fix: missing auto commit

* chore: remove database drivers

* feat: update de and en configs

* feat: support for other languages
2025-11-25 10:31:46 -05:00
Axionize 2d785fdc2c Bump PacketEvents version 2025-11-23 18:59:59 -05:00
Nils Gereke 403fd36830 fix(fabric): world isLoaded throws method not found (#2372)
fix(fabric): world name is always 'world'
2025-11-23 18:14:52 -05:00
Greened e386b936ff Fixed Reach false positive with Happy Ghast and clients under 1.21.6 (#2352) 2025-11-22 06:14:32 -05:00
GroupXyz 3fc985c8f3 Fix spectate command resetting gamemode when spectating someone in another dimension (#2365)
* Fix gamemode change on dimension change 

This issue happened when using the spectate command on a player in another dimension. the spectator was set to survival mode.

* Fix player gamemode reset bug

When spectating a player in another dimension, you would previously get set to survival mode, this is now fixed.
2025-11-22 06:11:15 -05:00
ManInMyVan 499bf7e2f9 more consistent issue templates 2025-11-19 17:02:06 -07:00
ManInMyVan ba91251307 improve bypass template 2025-11-19 16:39:03 -07:00
SamB440 8ddd7f18a5 Setback changes 2025-11-19 13:42:40 +00:00
cyramek 3f67272721 optimise bounding box calculations 2025-11-18 23:04:02 +01:00
AoElite 3380d7f9fd remove some unnecessary publishing paths 2025-11-18 11:14:20 -05:00
tenki-cloud[bot] e1bd2edc7c ci: migrate workflows to tenki (#2362)
* chore: migrate runner to tenki

* chore: migrate runner to tenki

---------

Co-authored-by: tenki-cloud[bot] <204909232+tenki-cloud[bot]@users.noreply.github.com>
2025-11-18 11:00:08 -05:00
Axionize 9b500a2617 Bump PacketEvents version 2025-11-13 03:39:44 -05:00
Weigurde 6df484bcc6 Update CrashC.java (#2350) 2025-11-11 17:10:24 -07:00
cyramek a46d29620f make sure that we use packet-based inventory state after closing the inventory 2025-11-09 23:28:48 +01:00
ManInMyVan b14628c578 hopefully make installation instructions more clear
also moves it higher on the page
2025-11-09 11:39:53 -07:00
AoElite 5c715315c9 tweak GrimDump output and include some non-default alert settings 2025-11-09 06:34:07 -05:00
AoElite ba1d1dbc16 small piston optimization 2025-11-09 05:39:44 -05:00
60 changed files with 851 additions and 313 deletions

No files matched your search

+3
View File
@@ -17,3 +17,6 @@ ij_formatter_on_tag = @formatter:on
ij_formatter_tags_enabled = false
ij_smart_tabs = false
ij_wrap_on_typing = false
[.github/ISSUE_TEMPLATE/*.yml]
indent_size = 2
+10 -10
View File
@@ -5,7 +5,7 @@ body:
- type: textarea
attributes:
label: Describe the bypass and how to replicate it
description: Provide a video, or detailed instructions to help replicate it.
description: Provide a video or detailed instructions to help replicate it.
placeholder: |
Step 1. Do this
Step 2. Then do this
@@ -17,8 +17,8 @@ body:
attributes:
label: Grim version
description: |
Provide the git commit or the version in the jar's name.
placeholder: A example would be b85c2d0 or grim version 2.3.5, etc
Provide the version of Grim running on the server. You can run `/grim version` to check.
placeholder: Examples include 2.3.73-8ddd7f1, 2.3.72, etc.
validations:
required: true
@@ -26,23 +26,23 @@ body:
attributes:
label: Server version
description: |
Run `/version` on your server to check. Provide both the server type (paper, airplane, etc) & the minecraft version.
Run `/version` on your server to check. Provide both the server type (ie Paper, Airplane, etc) & the Minecraft version.
validations:
required: true
- type: input
attributes:
label: Client version
description: |
Provide the version of your Minecraft client.
label: Client version
description: |
Provide the version of your Minecraft client. Also provide the protocol version if changed.
validations:
required: true
required: true
- type: textarea
attributes:
label: Plugins
label: Plugins/Mods
description: |
List all plugins or mods running on your server. To list plugins on bukkit, run `/plugins`.
List all plugins or mods installed on your server. To list plugins on Bukkit-based servers, run `/plugins`.
If your server is behind a proxy such as Bungeecord, you should list the proxy's plugins as well.
validations:
required: true
+14 -14
View File
@@ -6,7 +6,7 @@ body:
attributes:
label: Stack trace
description: |
Please include the entire stack trace with no attachments. If you would like, you can use a text pasting site such as https://paste.gg.
Provide the entire stack trace with no attachments. If you would like, you can use a text pasting site such as https://mclo.gs.
placeholder: Paste the stack trace or link to the stack trace here
validations:
required: true
@@ -14,7 +14,7 @@ body:
- type: textarea
attributes:
label: How to replicate it if known
description: A video, or detailed instructions to help replicate the issue would help.
description: A video or detailed instructions to help replicate the issue would help.
placeholder: |
Step 1. Do this
Step 2. Then do this
@@ -26,8 +26,8 @@ body:
attributes:
label: Grim version
description: |
Provide the git commit or the version in the jar's name.
placeholder: A example would be b85c2d0 or grim version 2.3.5, etc
Provide the version of Grim running on the server. You can run `/grim version` to check.
placeholder: Examples include 2.3.73-8ddd7f1, 2.3.72, etc.
validations:
required: true
@@ -35,27 +35,27 @@ body:
attributes:
label: Server version
description: |
Run `/version` on your server to check. Provide both the server type (paper, airplane, etc) & the minecraft version.
Run `/version` on your server to check. Provide both the server type (ie Paper, Airplane, etc) & the Minecraft version.
validations:
required: true
- type: dropdown
id: type
attributes:
label: Affected Platform(s)
description: Which platforms does this issue affect? If you aren't sure, select both.
options:
- "Bukkit"
- "Fabric"
multiple: true
label: Affected Platform(s)
description: Which platforms does this issue affect? If you aren't sure, select both.
options:
- "Bukkit"
- "Fabric"
multiple: true
validations:
required: true
required: true
- type: textarea
attributes:
label: Plugins/mods
label: Plugins/Mods
description: |
List all plugins or mods running on your server. To list plugins on bukkit, run `/plugins`.
List all plugins or mods installed on your server. To list plugins on Bukkit-based servers, run `/plugins`.
If your server is behind a proxy such as Bungeecord, you should list the proxy's plugins as well.
validations:
required: true
+7 -7
View File
@@ -20,8 +20,8 @@ body:
attributes:
label: Grim version
description: |
Provide the git commit or the version in the jar's name.
placeholder: A example would be b85c2d0 or grim version 2.3.5, etc
Provide the version of Grim running on the server. You can run `/grim version` to check.
placeholder: Examples include 2.3.73-8ddd7f1, 2.3.72, etc.
validations:
required: true
@@ -29,7 +29,7 @@ body:
attributes:
label: Server version
description: |
Run `/version` on your server to check. Provide both the server type (paper, airplane, etc) & the minecraft version.
Run `/version` on your server to check. Provide both the server type (ie Paper, Airplane, etc) & the Minecraft version.
validations:
required: true
@@ -37,16 +37,16 @@ body:
attributes:
label: Client version
description: |
Provide the version of your Minecraft client. You must be able to reproduce the false positive using the vanilla client.
Lunar Client, TLauncher etc. will result in your issue being closed.
Provide the version of your Minecraft client. You must be able to reproduce the false positive using a vanilla client.
Using Lunar Client, TLauncher, Fabric, etc, will result in your issue being closed.
validations:
required: true
- type: textarea
attributes:
label: Plugins
label: Plugins/Mods
description: |
List all plugins or mods running on your server. To list plugins on bukkit, run `/plugins`.
List all plugins or mods installed on your server. To list plugins on Bukkit-based servers, run `/plugins`.
If your server is behind a proxy such as Bungeecord, you should list the proxy's plugins as well.
validations:
required: true
+2 -2
View File
@@ -5,6 +5,6 @@ body:
- type: textarea
attributes:
label: Feature description
description: Describe the feature and why it should be added
description: Describe the feature and why it should be added.
validations:
required: true
required: true
+13 -14
View File
@@ -5,7 +5,7 @@ body:
- type: textarea
attributes:
label: Describe the bug and how to replicate it
description: A video, or detailed instructions to help replicate the issue would help.
description: A video or detailed instructions to help replicate the issue would help.
placeholder: |
Step 1. Do this
Step 2. Then do this
@@ -16,9 +16,8 @@ body:
- type: textarea
attributes:
label: Grim version
description: |
Provide the git commit or the version in the jar's name.
placeholder: A example would be b85c2d0 or grim version 2.3.5, etc
description: Provide the version of Grim running on the server. You can run `/grim version` to check.
placeholder: Examples include 2.3.73-8ddd7f1, 2.3.72, etc.
validations:
required: true
@@ -26,27 +25,27 @@ body:
attributes:
label: Server version
description: |
Run `/version` on your server to check. Provide both the server type (paper, airplane, etc) & the minecraft version.
Run `/version` on your server to check. Provide both the server type (ie Paper, Airplane, etc) & the Minecraft version.
validations:
required: true
- type: dropdown
id: type
attributes:
label: Affected Platform(s)
description: Which platforms does this issue affect? If you aren't sure, select both.
options:
- "Bukkit"
- "Fabric"
multiple: true
label: Affected Platform(s)
description: Which platforms does this issue affect? If you aren't sure, select both.
options:
- "Bukkit"
- "Fabric"
multiple: true
validations:
required: true
required: true
- type: textarea
attributes:
label: Plugins
label: Plugins/Mods
description: |
List all plugins or mods running on your server. To list plugins on bukkit, run `/plugins`.
List all plugins or mods installed on your server. To list plugins on Bukkit-based servers, run `/plugins`.
If your server is behind a proxy such as Bungeecord, you should list the proxy's plugins as well.
validations:
required: true
@@ -19,8 +19,15 @@ body:
- type: input
attributes:
label: Grim Log
description: |
Provide the generated /grim log link.
description: Provide the generated /grim log link.
validations:
required: true
- type: textarea
attributes:
label: Grim version
description: Provide the version of Grim running on the server. You can run `/grim version` to check.
placeholder: Examples include 2.3.73-8ddd7f1, 2.3.72, etc.
validations:
required: true
@@ -28,7 +35,7 @@ body:
attributes:
label: Server version
description: |
Run `/version` on your server to check. Provide both the server type (paper, airplane, etc) & the minecraft version.
Run `/version` on your server to check. Provide both the server type (ie Paper, Airplane, etc) & the Minecraft version.
validations:
required: true
@@ -36,16 +43,16 @@ body:
attributes:
label: Client version
description: |
Provide the version of your Minecraft client. You must be able to reproduce the false positive using the vanilla client.
Lunar Client, TLauncher etc. will result in your issue being closed.
Provide the version of your Minecraft client. You must be able to reproduce the false positive using a vanilla client.
Using Lunar Client, TLauncher, Fabric, etc, will result in your issue being closed.
validations:
required: true
- type: textarea
attributes:
label: Plugins
label: Plugins/Mods
description: |
List all plugins or mods running on your server. To list plugins on bukkit, run `/plugins`.
List all plugins or mods installed on your server. To list plugins on Bukkit-based servers, run `/plugins`.
If your server is behind a proxy such as Bungeecord, you should list the proxy's plugins as well.
validations:
required: true
required: true
+6 -6
View File
@@ -36,7 +36,7 @@ on:
jobs:
build:
runs-on: ubuntu-latest
runs-on: tenki-standard-autoscale
permissions:
contents: read
packages: write
@@ -105,7 +105,7 @@ jobs:
echo "Build version: $VERSION"
- name: Publish Build Scan URL
# Only run this step if a scan was requested
# Only run this step if a scan was requested
if: ${{ inputs.run-build-scan == true }}
run: |
# Search the log file for the build scan URL and extract it
@@ -151,7 +151,7 @@ jobs:
- name: Prepare Final Changelog
id: changelog
# Add this 'env' block to safely load the changelogs as environment variables
# Add this 'env' block to safely load the changelogs as environment variables
env:
RELEASE_NOTES: ${{ inputs.release-changelog }}
DEV_NOTES: ${{ steps.git-cliff.outputs.content }}
@@ -181,7 +181,7 @@ jobs:
if: ${{ inputs.publish-artifacts == true && env.MODRINTH_ID != '' && env.MODRINTH_TOKEN != '' }}
uses: Kir-Antipov/mc-publish@v3.3
with:
modrinth-id: ${{ secrets.MODRINTH_ID }}
modrinth-id: ${{ secrets.MODRINTH_ID }}
modrinth-token: ${{ secrets.MODRINTH_TOKEN }}
modrinth-featured: ${{ inputs.is-release-build }} # Only feature release builds
@@ -202,7 +202,7 @@ jobs:
if: ${{ inputs.publish-artifacts == true && env.MODRINTH_ID != '' && env.MODRINTH_TOKEN != '' }}
uses: Kir-Antipov/mc-publish@v3.3
with:
modrinth-id: ${{ secrets.MODRINTH_ID }}
modrinth-id: ${{ secrets.MODRINTH_ID }}
modrinth-token: ${{ secrets.MODRINTH_TOKEN }}
modrinth-featured: ${{ inputs.is-release-build }} # Only feature release builds
@@ -227,4 +227,4 @@ jobs:
-H "User-Agent: GrimACBuilder/v1" \
-H "Authorization: Bearer ${{ secrets.BUILD_UPDATE_TOKEN }}" \
-d '{"org": "${{ github.repository_owner }}", "repository": "${{ github.event.repository.name }}", "branch": "${{ github.ref_name }}", "version": "${{ env.BUILD_VERSION }}"}' \
"${{ secrets.BUILD_UPDATE_URL }}"
"${{ secrets.BUILD_UPDATE_URL }}"
+5 -5
View File
@@ -14,12 +14,12 @@ name: "CodeQL"
on:
workflow_dispatch:
push:
branches: [ dev ]
branches: [dev]
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
runs-on: tenki-standard-autoscale
permissions:
actions: read
contents: read
@@ -28,7 +28,7 @@ jobs:
strategy:
fail-fast: false
matrix:
language: [ 'java-kotlin' ]
language: ['java-kotlin']
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
# Learn more:
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
@@ -60,6 +60,7 @@ jobs:
- name: Autobuild
uses: github/codeql-action/autobuild@v3
# ℹ️ Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
@@ -70,6 +71,5 @@ jobs:
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
uses: github/codeql-action/analyze@v3
-2
View File
@@ -20,8 +20,6 @@ on:
- 'release'
- 'release/**'
paths:
- '.github/workflows/**'
- 'buildSrc/**'
- 'bukkit/**'
- 'common/**'
- 'fabric/**'
+10 -13
View File
@@ -33,30 +33,27 @@ A premium version is planned, which will offer additional subscription-based pai
- ~~[Hangar](https://hangar.papermc.io/GrimAnticheat/GrimAnticheat)~~
- ~~[SpigotMC](https://www.spigotmc.org/resources/grim-anticheat.99923/)~~
## Requirements & Installation
- Java 17 or higher. *For more details, see [Updating-to-Java-17](https://github.com/GrimAnticheat/Grim/wiki/Updating-to-Java-17).*
- A Spigot, Paper, Folia, or Fabric server environment. *For more details, see [Supported-environments](https://github.com/GrimAnticheat/Grim/wiki/Supported-environments).*
If you use a proxy such as Velocity or Bungeecord:
- If you use Geyser, Floodgate must be installed on the backend server (where Grim is) so Grim can access the Floodgate API.
- If you use ViaVersion, it must be installed on the backend server (where Grim is) ONLY.
Grim does not support having ViaVersion installed on the proxy, even if it is also installed on the backend.
## Resources
- For documentation and examples visit the [Wiki](https://github.com/GrimAnticheat/Grim/wiki).
- For answers to commonly asked questions visit the [FAQ](https://github.com/GrimAnticheat/Grim/wiki/FAQ).
- For community support and project discussion join our [Discord](https://discord.grim.ac).
## Pull Requests
See [Contributing](CONTRIBUTING.md) for more information about contributing and what our guidelines
are.
## Requirements & Installation
- Java 17 or higher. *For more details
see [Updating-to-Java-17](https://github.com/GrimAnticheat/Grim/wiki/Updating-to-Java-17).*
- A Spigot, Paper, Folia, or Fabric server environment. *For more details
see [Supported-environments](https://github.com/GrimAnticheat/Grim/wiki/Supported-environments).*
- If you use Geyser, place Floodgate on the backend server so grim can exempt bedrock players. Grim
cannot access the Floodgate API if it is on the proxy.
- If you use ViaVersion, it should be on the backend server as movement is highly dependent on
client version.
## Developer Plugin API
Grim's plugin API allows you to integrate Grim into your own plugins. Visit
@@ -74,7 +74,7 @@ public class PistonEvent implements Listener {
final Vector3i sourcePos = new Vector3i(piston.getX(), piston.getY(), piston.getZ());
for (GrimPlayer player : GrimAPI.INSTANCE.getPlayerDataManager().getEntries()) {
if (player.compensatedWorld.isChunkLoaded(chunkX, chunkZ) && isCloseEnough(sourcePos, player.compensatedEntities.self.trackedServerPosition.getPos())) {
if (isCloseEnough(sourcePos, player.compensatedEntities.self.trackedServerPosition.getPos()) && player.compensatedWorld.isChunkLoaded(chunkX, chunkZ)) {
final int lastTrans = player.lastTransactionSent.get();
PistonData data = new PistonData(blockFace, boxes, lastTrans, true, hasSlimeBlock, hasHoneyBlock);
player.latencyUtils.addRealTimeTaskAsync(lastTrans, () -> player.compensatedWorld.activePistons.add(data));
@@ -132,7 +132,7 @@ public class PistonEvent implements Listener {
Vector3i sourcePos = new Vector3i(event.getBlock().getX(), event.getBlock().getY(), event.getBlock().getZ());
for (GrimPlayer player : GrimAPI.INSTANCE.getPlayerDataManager().getEntries()) {
if (player.compensatedWorld.isChunkLoaded(chunkX, chunkZ) && isCloseEnough(sourcePos, player.compensatedEntities.self.trackedServerPosition.getPos())) {
if (isCloseEnough(sourcePos, player.compensatedEntities.self.trackedServerPosition.getPos()) && player.compensatedWorld.isChunkLoaded(chunkX, chunkZ)) {
int lastTrans = player.lastTransactionSent.get();
PistonData data = new PistonData(face, boxes, lastTrans, false, hasSlimeBlock, hasHoneyBlock);
player.latencyUtils.addRealTimeTaskAsync(lastTrans, () -> player.compensatedWorld.activePistons.add(data));
@@ -96,7 +96,7 @@ public class Check extends GrimProcessor implements AbstractCheck {
if (event.isCancelled()) return false;
player.punishmentManager.handleViolation(this);
lastViolationTime = System.currentTimeMillis();
lastViolationTime = player.now();
violations++;
return true;
}
@@ -0,0 +1,82 @@
package ac.grim.grimac.checks.debug;
import ac.grim.grimac.player.GrimPlayer;
import com.github.retrooper.packetevents.protocol.player.User;
import org.jetbrains.annotations.NotNull;
public class DebugGrimPlayer extends GrimPlayer {
private static final long THRESHOLD_NANO = 200_000_000L; // 200ms
private static final long THRESHOLD_MILLIS = 200;
// Global Offsets: Volatile so all threads see the pause adjustment immediately.
// When the Netty thread pauses for 60s, it updates this.
// The Scheduler thread sees the update and "stops" the timeout check.
private volatile long offsetNano = 0;
private volatile long offsetMillis = 0;
// ThreadLocal: Each thread tracks its OWN execution time.
// This prevents the Scheduler Thread (pollData) from resetting the timer
// while the Netty Thread is sitting at a breakpoint.
private final ThreadLocal<Long> packetStartNano = ThreadLocal.withInitial(System::nanoTime);
private final ThreadLocal<Long> packetStartMillis = ThreadLocal.withInitial(System::currentTimeMillis);
public DebugGrimPlayer(@NotNull User user) {
super(user, System.currentTimeMillis(), System.nanoTime());
}
@Override
public void processingPacket() {
// Reset the timer ONLY for the current thread (Netty or Scheduler)
this.packetStartNano.set(System.nanoTime());
this.packetStartMillis.set(System.currentTimeMillis());
}
@Override
public long nano() {
// SAFETY CHECK:
// If something in the parent constructor calls nano() BEFORE we are fully initialized,
// packetStartNano will be null. We must fallback to System time to prevent NPE.
if (packetStartNano == null) return System.nanoTime();
long sysTime = System.nanoTime();
// 1. Get start time for THIS thread
long start = packetStartNano.get();
long executionTime = sysTime - start;
// 2. Check if THIS thread was paused/stuck
if (executionTime > THRESHOLD_NANO) {
// Update the global offset so ALL threads know to skip this time
synchronized (this) {
offsetNano += executionTime;
}
// Reset this thread's timer so we don't loop
packetStartNano.set(sysTime);
} else if (executionTime < 0) {
// Self-healing for rare thread-local init edge cases or system clock jumps
packetStartNano.set(sysTime);
}
// 3. Return System Time minus the Global Pause Offset
return sysTime - offsetNano;
}
@Override
public long now() {
if (packetStartMillis == null) return System.currentTimeMillis();
long sysTime = System.currentTimeMillis();
long start = packetStartMillis.get();
long executionTime = sysTime - start;
if (executionTime > THRESHOLD_MILLIS) {
synchronized (this) {
offsetMillis += executionTime;
}
packetStartMillis.set(sysTime);
}
return sysTime - offsetMillis;
}
}
@@ -0,0 +1,35 @@
package ac.grim.grimac.checks.debug;
import ac.grim.grimac.utils.common.arguments.CommonGrimArguments;
import java.lang.management.ManagementFactory;
public class GrimDebugSettings {
public static boolean shouldEnable() {
// 1. If the argument is disabled, never enable debug mode.
if (!CommonGrimArguments.DEBUG_BREAKPOINT_MODE.value()) {
return false;
}
// 2. Check for Debugger (Startup Agent OR Dynamic Attach)
return isDebuggerAttached();
}
private static boolean isDebuggerAttached() {
// Fast Path: Check startup arguments for -agentlib:jdwp
if (ManagementFactory.getRuntimeMXBean().getInputArguments().toString().contains("jdwp")) {
return true;
}
// Slow Path (Dynamic Attach): Scan threads for JDWP/Signal Dispatcher
try {
for (Thread t : Thread.getAllStackTraces().keySet()) {
String name = t.getName();
if (name.contains("JDWP")) return true;
}
} catch (Throwable ignored) {}
return false;
}
}
@@ -23,7 +23,7 @@ public class BadPacketsR extends Check implements PacketCheck {
public void onPacketReceive(final PacketReceiveEvent event) {
if (isTransaction(event.getPacketType()) && player.packetStateData.lastTransactionPacketWasValid) {
long ms = (player.getPlayerClockAtLeast() - clock) / 1000000L;
long diff = (System.currentTimeMillis() - lastTransTime);
long diff = (player.now() - lastTransTime);
if (diff > 2000 && ms > 2000) {
if (positions == 0 && clock != 0 && player.gamemode != GameMode.SPECTATOR && !player.compensatedEntities.self.isDead) {
flag("time=" + ms + "ms, " + "lst=" + diff + "ms, positions=" + positions);
@@ -34,7 +34,7 @@ public class BadPacketsR extends Check implements PacketCheck {
player.compensatedWorld.removeInvalidPistonLikeStuff(oldTransId);
positions = 0;
clock = player.getPlayerClockAtLeast();
lastTransTime = System.currentTimeMillis();
lastTransTime = player.now();
oldTransId = player.lastTransactionSent.get();
}
}
@@ -67,12 +67,12 @@ public class FastBreak extends Check implements BlockBreakCheck {
// * can we translate back "up" to server version and run check against server version to avoid loading older registries?
WrappedBlockState block = clientOlderThanServer ? WrappedBlockState.getByGlobalId(player.getClientVersion(), player.getViaTranslatedClientBlockID(blockBreak.block.getGlobalId())) : blockBreak.block;
startBreak = System.currentTimeMillis() - (targetBlockPosition == null ? 50 : 0); // ???
startBreak = player.now() - (targetBlockPosition == null ? 50 : 0); // ???
targetBlockPosition = blockBreak.position;
maximumBlockDamage = BlockBreakSpeed.getBlockDamage(player, block);
double breakDelay = System.currentTimeMillis() - lastFinishBreak;
double breakDelay = player.now() - lastFinishBreak;
if (breakDelay >= 275) { // Reduce buffer if "close enough"
blockDelayBalance *= 0.9;
@@ -91,7 +91,7 @@ public class FastBreak extends Check implements BlockBreakCheck {
if (blockBreak.action == DiggingAction.FINISHED_DIGGING && targetBlockPosition != null) {
double predictedTime = Math.ceil(1 / maximumBlockDamage) * 50;
double realTime = System.currentTimeMillis() - startBreak;
double realTime = player.now() - startBreak;
double diff = predictedTime - realTime;
clampBalance();
@@ -109,7 +109,7 @@ public class FastBreak extends Check implements BlockBreakCheck {
}
// also set start time because the breaking netcode is fucked on 1.14.4+
lastFinishBreak = startBreak = System.currentTimeMillis();
lastFinishBreak = startBreak = player.now();
}
}
@@ -95,7 +95,10 @@ public class Reach extends Check implements PacketCheck {
// TODO: Remove when in front of via
if (entity.type == EntityTypes.ARMOR_STAND && player.getClientVersion().isOlderThan(ClientVersion.V_1_8))
return;
//Prevents Happy Ghast Reach false on 1.21.6+ servers with ViaBackwards set up
if (entity.type == EntityTypes.HAPPY_GHAST && player.getClientVersion().isOlderThan(ClientVersion.V_1_21_6)) {
return;
}
if (player.gamemode == GameMode.CREATIVE || player.gamemode == GameMode.SPECTATOR)
return;
if (player.inVehicle()) return;
@@ -20,10 +20,9 @@ public class CrashC extends Check implements PacketCheck {
WrapperPlayClientPlayerFlying flying = new WrapperPlayClientPlayerFlying(event);
if (flying.hasPositionChanged()) {
Location pos = flying.getLocation();
if (Double.isNaN(pos.getX()) || Double.isNaN(pos.getY()) || Double.isNaN(pos.getZ())
|| Double.isInfinite(pos.getX()) || Double.isInfinite(pos.getY()) || Double.isInfinite(pos.getZ()) ||
Float.isNaN(pos.getYaw()) || Float.isNaN(pos.getPitch()) ||
Float.isInfinite(pos.getYaw()) || Float.isInfinite(pos.getPitch())) {
if (!Double.isFinite(pos.getX()) || !Double.isFinite(pos.getY()) || !Double.isFinite(pos.getZ())
|| !Float.isFinite(pos.getYaw()) || !Float.isFinite(pos.getPitch())
) {
flagAndAlert("xyzYP: " + pos.getX() + ", " + pos.getY() + ", " + pos.getZ() + ", " + pos.getYaw() + ", " + pos.getPitch());
player.getSetbackTeleportUtil().executeViolationSetback();
event.setCancelled(true);
@@ -12,18 +12,18 @@ public class NegativeTimer extends Timer implements PostPredictionCheck {
public NegativeTimer(GrimPlayer player) {
super(player);
timerBalanceRealTime = System.nanoTime() + clockDrift;
timerBalanceRealTime = player.nano() + clockDrift;
}
@Override
public void onPredictionComplete(final PredictionComplete predictionComplete) {
// We can't negative timer check a 1.9+ player who is standing still.
if (player.uncertaintyHandler.lastPointThree.hasOccurredSince(2) || !predictionComplete.isChecked()) {
timerBalanceRealTime = System.nanoTime() + clockDrift;
timerBalanceRealTime = player.nano() + clockDrift;
}
if (timerBalanceRealTime < lastMovementPlayerClock - clockDrift) {
int lostMS = (int) ((System.nanoTime() - timerBalanceRealTime) / 1e6);
int lostMS = (int) ((player.nano() - timerBalanceRealTime) / 1e6);
flagAndAlertWithSetback("-" + lostMS);
timerBalanceRealTime += 50e6;
}
@@ -14,8 +14,8 @@ public class Timer extends Check implements PacketCheck {
protected long timerBalanceRealTime = 0;
// Default value is real time minus max keep-alive time
protected long knownPlayerClockTime = (long) (System.nanoTime() - 6e10);
protected long lastMovementPlayerClock = (long) (System.nanoTime() - 6e10);
protected long knownPlayerClockTime = (long) (player.nano() - 6e10);
protected long lastMovementPlayerClock = (long) (player.nano() - 6e10);
// How long should the player be able to fall back behind their ping? (nanos)
// Default: 120 milliseconds
@@ -71,7 +71,7 @@ public class Timer extends Check implements PacketCheck {
}
public void doCheck(final PacketReceiveEvent event) {
if (timerBalanceRealTime > System.nanoTime()) {
if (timerBalanceRealTime > player.nano()) {
if (flagAndAlert()) {
// Cancel the packet
if (shouldModifyPackets()) {
@@ -19,7 +19,7 @@ public class TimerLimit extends Timer {
@Override
public void doCheck(final PacketReceiveEvent event) {
// 1:1 with Timer minus cancelling the packet
if (timerBalanceRealTime > System.nanoTime()) {
if (timerBalanceRealTime > player.nano()) {
// If timer check already flagged, don't flag.
if (!event.isCancelled()) {
if (flagAndAlert() && shouldSetback()) {
@@ -38,8 +38,8 @@ public class TimerLimit extends Timer {
protected void limitFallBehind() {
// Limit using transaction ping if over 1000ms (default)
long playerClock = lastMovementPlayerClock;
if (limitAbuseOverPing != -1 && System.nanoTime() - playerClock > limitAbuseOverPing) {
playerClock = System.nanoTime() - limitAbuseOverPing;
if (limitAbuseOverPing != -1 && player.nano() - playerClock > limitAbuseOverPing) {
playerClock = player.nano() - limitAbuseOverPing;
}
timerBalanceRealTime = Math.max(timerBalanceRealTime, playerClock - clockDrift);
}
@@ -51,9 +51,9 @@ public class GrimDump implements BuildableCommand {
GrimLog.sendLogAsync(sender, generateDump(), string -> link = string, "text/yaml");
}
public static JsonObject getBasicInfo(String type) {
public static JsonObject getDumpInfo() {
JsonObject base = new JsonObject();
base.addProperty("type", type);
base.addProperty("type", "dump");
base.addProperty("timestamp", System.currentTimeMillis());
// versions
JsonObject versions = new JsonObject();
@@ -68,6 +68,11 @@ public class GrimDump implements BuildableCommand {
if (GrimAPI.INSTANCE.isInitialized()) states.addProperty("platform", GrimAPI.INSTANCE.getPlatform().toString());
if (ViaVersionUtil.isAvailable) states.addProperty("has_viaversion", true);
if (PAPER) states.addProperty("has_paper", true);
// include some relevant settings if not default
JsonObject settings = new JsonObject();
if (GrimAPI.INSTANCE.getAlertManager().hasConsoleVerboseEnabled()) settings.addProperty("console_verbose", true);
if (!GrimAPI.INSTANCE.getAlertManager().hasConsoleAlertsEnabled()) settings.addProperty("console_alerts", false);
if (settings.size() > 0) states.add("settings", settings);
// system
JsonObject system = new JsonObject();
base.add("system", system);
@@ -75,8 +80,17 @@ public class GrimDump implements BuildableCommand {
system.addProperty("java_version", System.getProperty("java.version"));
system.addProperty("user_language", System.getProperty("user.language"));
// build
JsonObject build = new JsonObject();
base.add("build", getBuildInfo());
// plugins
JsonArray plugins = new JsonArray();
base.add("plugins", plugins);
for (PlatformPlugin plugin : GrimAPI.INSTANCE.getPluginManager().getPlugins()) {
JsonObject pluginJson = new JsonObject();
pluginJson.addProperty("enabled", plugin.isEnabled());
pluginJson.addProperty("name", plugin.getName());
pluginJson.addProperty("version", plugin.getVersion());
plugins.add(pluginJson);
}
return base;
}
@@ -98,17 +112,7 @@ public class GrimDump implements BuildableCommand {
* @return A JSON-formatted string containing the diagnostic dump.
*/
private String generateDump() {
JsonObject base = getBasicInfo("dump");
// plugins
JsonArray plugins = new JsonArray();
base.add("plugins", plugins);
for (PlatformPlugin plugin : GrimAPI.INSTANCE.getPluginManager().getPlugins()) {
JsonObject pluginJson = new JsonObject();
pluginJson.addProperty("enabled", plugin.isEnabled());
pluginJson.addProperty("name", plugin.getName());
pluginJson.addProperty("version", plugin.getVersion());
plugins.add(pluginJson);
}
JsonObject base = getDumpInfo();
return gson.toJson(base);
}
}
@@ -495,7 +495,7 @@ public class CheckManagerListener extends PacketListenerAbstract {
if (event.getPacketType() == PacketType.Play.Client.PLAYER_BLOCK_PLACEMENT) {
WrapperPlayClientPlayerBlockPlacement packet = new WrapperPlayClientPlayerBlockPlacement(event);
player.lastBlockPlaceUseItem = System.currentTimeMillis();
player.lastBlockPlaceUseItem = player.now();
ItemStack placedWith = player.inventory.getHeldItem();
if (packet.getHand() == InteractionHand.OFF_HAND) {
@@ -562,7 +562,7 @@ public class CheckManagerListener extends PacketListenerAbstract {
if (event.getPacketType() == PacketType.Play.Client.USE_ITEM) {
WrapperPlayClientUseItem packet = new WrapperPlayClientUseItem(event);
player.placeUseItemPackets.add(new BlockPlaceSnapshot(packet, player.isSneaking));
player.lastBlockPlaceUseItem = System.currentTimeMillis();
player.lastBlockPlaceUseItem = player.now();
}
// Call the packet checks last as they can modify the contents of the packet
@@ -663,7 +663,7 @@ public class CheckManagerListener extends PacketListenerAbstract {
}
private static void handleFlying(GrimPlayer player, double x, double y, double z, float yaw, float pitch, boolean hasPosition, boolean hasLook, boolean onGround, TeleportAcceptData teleportData, PacketReceiveEvent event) {
long now = System.currentTimeMillis();
long now = player.now();
if (!hasPosition) {
// This may need to be secured later, although nothing that is very important relies on this
@@ -759,7 +759,7 @@ public class CheckManagerListener extends PacketListenerAbstract {
}
private static void handleDigging(GrimPlayer player, PacketReceiveEvent event) {
player.lastBlockBreak = System.currentTimeMillis();
player.lastBlockBreak = player.now();
final WrapperPlayClientPlayerDigging packet = new WrapperPlayClientPlayerDigging(event);
final DiggingAction action = packet.getAction();
@@ -44,7 +44,7 @@ public class PacketEntityAction extends PacketListenerAbstract {
break;
case START_FLYING_WITH_ELYTRA:
if (player.onGround || player.lastOnGround) {
player.getSetbackTeleportUtil().executeForceResync();
player.getSetbackTeleportUtil().executeNonSimulatingForceResync();
if (player.platformPlayer != null) {
// Client ignores sneaking, use it to resync
@@ -66,7 +66,7 @@ public class PacketEntityAction extends PacketListenerAbstract {
player.pointThreeEstimator.updatePlayerGliding();
} else {
// A client is flying with a ghost elytra, resync
player.getSetbackTeleportUtil().executeForceResync();
player.getSetbackTeleportUtil().executeNonSimulatingForceResync();
if (player.platformPlayer != null) {
// Client ignores sneaking, use it to resync
player.platformPlayer.setSneaking(!player.platformPlayer.isSneaking());
@@ -72,7 +72,7 @@ public class PacketPingListener extends PacketListenerAbstract {
if (id <= 0) {
if (player.didWeSendThatTrans.remove(id)) {
player.packetStateData.lastServerTransWasValid = true;
player.transactionsSent.add(new Pair<>(id, System.nanoTime()));
player.transactionsSent.add(new Pair<>(id, player.nano()));
player.lastTransactionSent.getAndIncrement();
}
}
@@ -91,7 +91,7 @@ public class PacketPingListener extends PacketListenerAbstract {
Short shortID = ((short) id);
if (player.didWeSendThatTrans.remove(shortID)) {
player.packetStateData.lastServerTransWasValid = true;
player.transactionsSent.add(new Pair<>(shortID, System.nanoTime()));
player.transactionsSent.add(new Pair<>(shortID, player.nano()));
player.lastTransactionSent.getAndIncrement();
}
}
@@ -228,7 +228,7 @@ public class PacketPlayerDigging extends PacketListenerAbstract {
// do we need to do this with block breaks too?
// Prevent issues if the player switches slots, while lagging, standing still, and is placing blocks
CheckManagerListener.handleQueuedPlaces(player, false, 0, 0, System.currentTimeMillis());
CheckManagerListener.handleQueuedPlaces(player, false, 0, 0, player.now());
if (player.packetStateData.lastSlotSelected != slot) {
if (player.isResetItemUsageOnSlotChange() && GrimAPI.INSTANCE.getItemResetHandler().getItemUsageHand(player.platformPlayer) == InteractionHand.MAIN_HAND) {
@@ -210,7 +210,9 @@ public class PacketPlayerRespawn extends PacketListenerAbstract {
}
player.pose = Pose.STANDING;
player.clientVelocity = new Vector3dm();
player.gamemode = respawn.getGameMode();
if (!GrimAPI.INSTANCE.getSpectateManager().isSpectating(player.uuid)) {
player.gamemode = respawn.getGameMode();
}
if (PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_17)) {
player.compensatedWorld.setDimension(respawn.getDimensionType(), event.getUser());
}
@@ -31,7 +31,7 @@ public class PacketWorldBorder extends Check implements PacketCheck {
}
public double getCurrentDiameter() {
double d0 = (double) (System.currentTimeMillis() - startTime) / ((double) endTime - startTime);
double d0 = (double) (player.now() - startTime) / ((double) endTime - startTime);
return d0 < 1.0D ? GrimMath.lerp(d0, oldDiameter, newDiameter) : newDiameter;
}
@@ -125,7 +125,7 @@ public class PacketWorldBorder extends Check implements PacketCheck {
private void setLerp(double oldDiameter, double newDiameter, long length) {
this.oldDiameter = oldDiameter;
this.newDiameter = newDiameter;
this.startTime = System.currentTimeMillis();
this.startTime = player.now();
this.endTime = this.startTime + length;
}
}
@@ -158,7 +158,7 @@ public class BasePacketWorldReader extends PacketListenerAbstract {
Vector3i blockPosition = blockChange.getBlockPosition();
// Don't spam transactions (block changes are sent in batches)
if (Math.abs(blockPosition.getX() - player.x) < range && Math.abs(blockPosition.getY() - player.y) < range && Math.abs(blockPosition.getZ() - player.z) < range &&
player.lastTransSent + 2 < System.currentTimeMillis())
player.lastTransSent + 2 < player.now())
player.sendTransaction();
player.latencyUtils.addRealTimeTask(player.lastTransactionSent.get(), () -> player.compensatedWorld.updateBlock(blockPosition.getX(), blockPosition.getY(), blockPosition.getZ(), blockChange.getBlockId()));
@@ -172,7 +172,7 @@ public class BasePacketWorldReader extends PacketListenerAbstract {
final var blocks = multiBlockChange.getBlocks();
for (WrapperPlayServerMultiBlockChange.EncodedBlock blockChange : blocks) {
// Don't send a transaction unless it's within 16 blocks of the player
if (Math.abs(blockChange.getX() - player.x) < range && Math.abs(blockChange.getY() - player.y) < range && Math.abs(blockChange.getZ() - player.z) < range && player.lastTransSent + 2 < System.currentTimeMillis()) {
if (Math.abs(blockChange.getX() - player.x) < range && Math.abs(blockChange.getY() - player.y) < range && Math.abs(blockChange.getZ() - player.z) < range && player.lastTransSent + 2 < player.now()) {
player.sendTransaction();
break;
}
@@ -24,7 +24,7 @@ public class ActionManager extends Check implements PacketCheck {
if (action.getAction() == WrapperPlayClientInteractEntity.InteractAction.ATTACK) {
player.totalFlyingPacketsSent = 0;
attacking = true;
lastAttack = System.currentTimeMillis();
lastAttack = player.now();
}
} else if (isTickPacketIncludingNonMovement(event.getPacketType())) {
player.totalFlyingPacketsSent++;
@@ -33,6 +33,6 @@ public class ActionManager extends Check implements PacketCheck {
}
public boolean hasAttackedSince(long time) {
return System.currentTimeMillis() - lastAttack < time;
return player.now() - lastAttack < time;
}
}
@@ -75,8 +75,10 @@ public class CheckManager {
private final ClassToInstanceMap<BlockPlaceCheck> blockPlaceChecks;
private final ClassToInstanceMap<PostPredictionCheck> postPredictionChecks;
private PacketEntityReplication packetEntityReplication = null;
private final GrimPlayer player;
public CheckManager(GrimPlayer player) {
this.player = player;
packetChecks = new ImmutableClassToInstanceMap.Builder<PacketCheck>()
.put(PacketOrderProcessor.class, player.packetOrderProcessor)
.put(Reach.class, new Reach(player))
@@ -294,6 +296,8 @@ public class CheckManager {
}
public void onPacketReceive(final PacketReceiveEvent packet) {
player.processingPacket();
for (PacketCheck check : packetChecks.values()) {
check.onPacketReceive(packet);
}
@@ -309,6 +313,8 @@ public class CheckManager {
}
public void onPacketSend(final PacketSendEvent packet) {
player.processingPacket();
for (PacketCheck check : prePredictionChecks.values()) {
check.onPacketSend(packet);
}
@@ -177,7 +177,7 @@ public class PunishmentManager implements ConfigReloadable {
public void handleViolation(Check check) {
for (PunishGroup group : groups) {
if (group.checks.contains(check)) {
long currentTime = System.currentTimeMillis();
long currentTime = player.now();
group.violations.put(currentTime, check);
// Remove violations older than the defined time in the config
@@ -100,7 +100,7 @@ public class SetbackTeleportUtil extends Check implements PostPredictionCheck {
blockMovementsUntilResync(true, true);
}
public void executeForceResyncNoSimulation() {
public void executeNonSimulatingForceResync() {
if (player.gamemode == GameMode.SPECTATOR || player.disableGrim)
return; // We don't care about spectators, they don't flag
if (lastKnownGoodPosition == null) return; // Player hasn't spawned yet
@@ -144,9 +144,11 @@ public class SetbackTeleportUtil extends Check implements PostPredictionCheck {
vector.setY(vector.getY() - 0.05); // Make the player fall a bit
} else { // Gliding doesn't have friction, we handle it differently
PredictionEngineNormal.staticVectorEndOfTick(player, vector); // Lava and normal movement
vector.multiply(player.stuckSpeedMultiplier); // Prevent abusing setbacks to move out of blocks like webs
}
// Prevent abusing setbacks to move out of blocks like webs
vector.multiply(player.stuckSpeedMultiplier);
// stop 1.8 players from stepping onto 1.25 high blocks, because why not?
new PredictionEngine().applyMovementThreshold(player, new HashSet<>(Collections.singletonList(new VectorData(vector, VectorData.VectorType.BestVelPicked))));
}
@@ -159,9 +161,9 @@ public class SetbackTeleportUtil extends Check implements PostPredictionCheck {
if (isPendingSetback()) return; // Don't spam setbacks
// Only let us full resync once every five seconds to prevent unneeded bukkit load
if (System.currentTimeMillis() - lastWorldResync > 5 * 1000) {
if (player.now() - lastWorldResync > 5 * 1000) {
player.resyncPositions(player.boundingBox.copy().expand(1));
lastWorldResync = System.currentTimeMillis();
lastWorldResync = player.now();
}
Vector3dm clientVel = lastKnownGoodPosition.vector.clone();
@@ -215,7 +217,7 @@ public class SetbackTeleportUtil extends Check implements PostPredictionCheck {
blockOffsets = true;
}
SetBackData data = new SetBackData(new TeleportData(position, new Vector3d(), new RelativeFlag(0b11000), player.lastTransactionSent.get(), 0), player.yaw, player.pitch, clientVel, player.inVehicle(), false);
SetBackData data = new SetBackData(new TeleportData(position, new Vector3d(), RelativeFlag.YAW.or(RelativeFlag.PITCH), player.lastTransactionSent.get(), 0), player.yaw, player.pitch, clientVel, player.inVehicle(), false);
sendSetback(data);
}
@@ -382,7 +384,7 @@ public class SetbackTeleportUtil extends Check implements PostPredictionCheck {
private boolean isPendingSetback() {
// Relative setbacks shouldn't count
if (requiredSetBack.getTeleportData().isRelativeX() || requiredSetBack.getTeleportData().isRelativeY() || requiredSetBack.getTeleportData().isRelativeZ()) {
if (requiredSetBack != null && (requiredSetBack.getTeleportData().isRelativeX() || requiredSetBack.getTeleportData().isRelativeY() || requiredSetBack.getTeleportData().isRelativeZ())) {
return false;
}
// The setback is not complete
@@ -6,6 +6,7 @@ import ac.grim.grimac.api.plugin.GrimPlugin;
import ac.grim.grimac.manager.init.ReloadableInitable;
import ac.grim.grimac.manager.init.start.StartableInitable;
import ac.grim.grimac.manager.violationdatabase.mysql.MySQLViolationDatabase;
import ac.grim.grimac.manager.violationdatabase.postgresql.PostgresqlViolationDatabase;
import ac.grim.grimac.manager.violationdatabase.sqlite.SQLiteViolationDatabase;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.LogUtil;
@@ -93,6 +94,28 @@ public class ViolationDatabaseManager implements StartableInitable, ReloadableIn
}
}
case "POSTGRESQL" -> {
String host = cfg.getStringElse("history.database.host", "localhost:3306");
String db = cfg.getStringElse("history.database.database", "grimac");
String user = cfg.getStringElse("history.database.username", "root");
String pwd = cfg.getStringElse("history.database.password", "password");
if (database instanceof PostgresqlViolationDatabase postgresql
&& postgresql.sameConfig(host, db, user, pwd)) {
break; // nothing changed → keep pool
}
database.disconnect();
database = new PostgresqlViolationDatabase(host, db, user, pwd);
try {
database.connect();
loaded = true;
} catch (SQLException e) {
LogUtil.error(e);
this.database = NoOpViolationDatabase.INSTANCE;
loaded = false;
}
}
default -> { // NOOP or invalid
if (!(database instanceof NoOpViolationDatabase)) {
database.disconnect();
@@ -0,0 +1,31 @@
package ac.grim.grimac.manager.violationdatabase.postgresql;
import ac.grim.grimac.manager.violationdatabase.DatabaseDialect;
public class PostgresqlDialect implements DatabaseDialect {
@Override
public String getUuidColumnType() {
return "UUID";
}
@Override
public String getAutoIncrementPrimaryKeySyntax() {
return "BIGSERIAL PRIMARY KEY";
}
@Override
public String getInsertOrIgnoreSyntax(String tableName, String columnNames) {
return "INSERT INTO " + tableName + " (" + columnNames + ") VALUES (?) ON CONFLICT DO NOTHING";
}
@Override
public String getUniqueConstraintViolationSQLState() {
return "23505"; // Postgresql duplicate key error
}
@Override
public int getUniqueConstraintViolationErrorCode() {
return 0; // Postgresql is not using numbers
}
}
@@ -0,0 +1,286 @@
package ac.grim.grimac.manager.violationdatabase.postgresql;
import ac.grim.grimac.GrimAPI;
import ac.grim.grimac.manager.violationdatabase.*;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.LogUtil;
import com.github.retrooper.packetevents.PacketEvents;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.UUID;
public class PostgresqlViolationDatabase implements ViolationDatabase {
private static String quoteVerboseColumn() {
return "\"" + DatabaseConstants.VIOLATIONS_VERBOSE_COLUMN + "\"";
}
private HikariDataSource dataSource;
private final DatabaseDialect dialect;
public PostgresqlViolationDatabase(String url, String database, String username, String password) {
this.dialect = new PostgresqlDialect();
setupDataSource(url, database, username, password);
}
private void setupDataSource(String url, String database, String username, String password) {
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:postgresql://" + url + "/" + database);
config.setUsername(username);
config.setPassword(password);
config.setMaximumPoolSize(10);
config.setAutoCommit(true);
dataSource = new HikariDataSource(config);
}
@Override
public void connect() throws SQLException {
try (Connection connection = dataSource.getConnection()) {
String pkSyntax = dialect.getAutoIncrementPrimaryKeySyntax();
String uuidType = dialect.getUuidColumnType();
// 1. Create Lookup Table for Server Names
connection.prepareStatement(
"CREATE TABLE IF NOT EXISTS " + DatabaseConstants.SERVERS_TABLE + "(" +
"id " + pkSyntax + ", " +
DatabaseConstants.SERVERS_STRING_COLUMN + " VARCHAR(255) NOT NULL UNIQUE" +
")"
).execute();
connection.prepareStatement(
"CREATE INDEX IF NOT EXISTS idx_" + DatabaseConstants.SERVERS_TABLE + "_name ON " + DatabaseConstants.SERVERS_TABLE + "(" + DatabaseConstants.SERVERS_STRING_COLUMN + ");"
).execute();
// 2. Create Lookup Table for Check Names
connection.prepareStatement(
"CREATE TABLE IF NOT EXISTS " + DatabaseConstants.CHECK_NAMES_TABLE + "(" +
"id " + pkSyntax + ", " +
DatabaseConstants.CHECK_NAMES_STRING_COLUMN + " VARCHAR(255) NOT NULL UNIQUE" +
")"
).execute();
connection.prepareStatement(
"CREATE INDEX IF NOT EXISTS idx_" + DatabaseConstants.CHECK_NAMES_TABLE + "_string ON " + DatabaseConstants.CHECK_NAMES_TABLE + "(" + DatabaseConstants.CHECK_NAMES_STRING_COLUMN + ");"
).execute();
// --- NEW LOOKUP TABLES ---
// 3. Create Lookup Table for Grim Versions
connection.prepareStatement(
"CREATE TABLE IF NOT EXISTS " + DatabaseConstants.GRIM_VERSIONS_TABLE + "(" +
"id " + pkSyntax + ", " +
DatabaseConstants.GRIM_VERSIONS_STRING_COLUMN + " VARCHAR(255) NOT NULL UNIQUE" +
")"
).execute();
connection.prepareStatement(
"CREATE INDEX IF NOT EXISTS idx_" + DatabaseConstants.GRIM_VERSIONS_TABLE + "_string ON " + DatabaseConstants.GRIM_VERSIONS_TABLE + "(" + DatabaseConstants.GRIM_VERSIONS_STRING_COLUMN + ");"
).execute();
// 4. Create Lookup Table for Client Brands
connection.prepareStatement(
"CREATE TABLE IF NOT EXISTS " + DatabaseConstants.CLIENT_BRANDS_TABLE + "(" +
"id " + pkSyntax + ", " +
DatabaseConstants.CLIENT_BRANDS_STRING_COLUMN + " VARCHAR(255) NOT NULL UNIQUE" +
")"
).execute();
connection.prepareStatement(
"CREATE INDEX IF NOT EXISTS idx_" + DatabaseConstants.CLIENT_BRANDS_TABLE + "_string ON " + DatabaseConstants.CLIENT_BRANDS_TABLE + "(" + DatabaseConstants.CLIENT_BRANDS_STRING_COLUMN + ");"
).execute();
// 5. Create Lookup Table for Client Versions
connection.prepareStatement(
"CREATE TABLE IF NOT EXISTS " + DatabaseConstants.CLIENT_VERSIONS_TABLE + "(" +
"id " + pkSyntax + ", " +
DatabaseConstants.CLIENT_VERSIONS_STRING_COLUMN + " VARCHAR(255) NOT NULL UNIQUE" +
")"
).execute();
connection.prepareStatement(
"CREATE INDEX IF NOT EXISTS idx_" + DatabaseConstants.CLIENT_VERSIONS_TABLE + "_string ON " + DatabaseConstants.CLIENT_VERSIONS_TABLE + "(" + DatabaseConstants.CLIENT_VERSIONS_STRING_COLUMN + ");"
).execute();
// 6. Create Lookup Table for Server Versions
connection.prepareStatement(
"CREATE TABLE IF NOT EXISTS " + DatabaseConstants.SERVER_VERSIONS_TABLE + "(" +
"id " + pkSyntax + ", " +
DatabaseConstants.SERVER_VERSIONS_STRING_COLUMN + " VARCHAR(255) NOT NULL UNIQUE" +
")"
).execute();
connection.prepareStatement(
"CREATE INDEX IF NOT EXISTS idx_" + DatabaseConstants.SERVER_VERSIONS_TABLE + "_string ON " + DatabaseConstants.SERVER_VERSIONS_TABLE + "(" + DatabaseConstants.SERVER_VERSIONS_STRING_COLUMN + ");"
).execute();
// --- END NEW LOOKUP TABLES ---
// 7. Create Main Violations Table with ALL Foreign Keys and optimized UUID
connection.prepareStatement(
"CREATE TABLE IF NOT EXISTS " + DatabaseConstants.VIOLATIONS_TABLE + "(" +
"id " + pkSyntax + ", " +
DatabaseConstants.VIOLATIONS_SERVER_ID_COLUMN + " BIGINT NOT NULL, " +
DatabaseConstants.VIOLATIONS_UUID_COLUMN + " " + uuidType + " NOT NULL, " +
DatabaseConstants.VIOLATIONS_CHECK_NAME_ID_COLUMN + " BIGINT NOT NULL, " +
quoteVerboseColumn() + " TEXT NOT NULL, " +
DatabaseConstants.VIOLATIONS_VL_COLUMN + " INT NOT NULL, " +
DatabaseConstants.VIOLATIONS_CREATED_AT_COLUMN + " BIGINT NOT NULL, " +
DatabaseConstants.VIOLATIONS_GRIM_VERSION_ID_COLUMN + " BIGINT NOT NULL, " + // NEW
DatabaseConstants.VIOLATIONS_CLIENT_BRAND_ID_COLUMN + " BIGINT NOT NULL, " + // NEW
DatabaseConstants.VIOLATIONS_CLIENT_VERSION_ID_COLUMN + " BIGINT NOT NULL, " + // NEW
DatabaseConstants.VIOLATIONS_SERVER_VERSION_ID_COLUMN + " BIGINT NOT NULL, " + // NEW
"FOREIGN KEY (" + DatabaseConstants.VIOLATIONS_SERVER_ID_COLUMN + ") REFERENCES " + DatabaseConstants.SERVERS_TABLE + "(id), " +
"FOREIGN KEY (" + DatabaseConstants.VIOLATIONS_CHECK_NAME_ID_COLUMN + ") REFERENCES " + DatabaseConstants.CHECK_NAMES_TABLE + "(id), " +
"FOREIGN KEY (" + DatabaseConstants.VIOLATIONS_GRIM_VERSION_ID_COLUMN + ") REFERENCES " + DatabaseConstants.GRIM_VERSIONS_TABLE + "(id), " + // NEW
"FOREIGN KEY (" + DatabaseConstants.VIOLATIONS_CLIENT_BRAND_ID_COLUMN + ") REFERENCES " + DatabaseConstants.CLIENT_BRANDS_TABLE + "(id), " + // NEW
"FOREIGN KEY (" + DatabaseConstants.VIOLATIONS_CLIENT_VERSION_ID_COLUMN + ") REFERENCES " + DatabaseConstants.CLIENT_VERSIONS_TABLE + "(id), " + // NEW
"FOREIGN KEY (" + DatabaseConstants.VIOLATIONS_SERVER_VERSION_ID_COLUMN + ") REFERENCES " + DatabaseConstants.SERVER_VERSIONS_TABLE + "(id)" + // NEW
")"
).execute();
// 8. Create Indexes for efficient querying on main table (includes new FKs)
connection.prepareStatement(
"CREATE INDEX IF NOT EXISTS idx_" + DatabaseConstants.VIOLATIONS_TABLE + "_uuid ON " + DatabaseConstants.VIOLATIONS_TABLE + "(" + DatabaseConstants.VIOLATIONS_UUID_COLUMN + ");"
).execute();
connection.prepareStatement(
"CREATE INDEX IF NOT EXISTS idx_" + DatabaseConstants.VIOLATIONS_TABLE + "_created_at ON " + DatabaseConstants.VIOLATIONS_TABLE + "(" + DatabaseConstants.VIOLATIONS_CREATED_AT_COLUMN + ");"
).execute();
connection.prepareStatement(
"CREATE INDEX IF NOT EXISTS idx_" + DatabaseConstants.VIOLATIONS_TABLE + "_server_id ON " + DatabaseConstants.VIOLATIONS_TABLE + "(" + DatabaseConstants.VIOLATIONS_SERVER_ID_COLUMN + ");"
).execute();
connection.prepareStatement(
"CREATE INDEX IF NOT EXISTS idx_" + DatabaseConstants.VIOLATIONS_TABLE + "_check_name_id ON " + DatabaseConstants.VIOLATIONS_TABLE + "(" + DatabaseConstants.VIOLATIONS_CHECK_NAME_ID_COLUMN + ");"
).execute();
connection.prepareStatement(
"CREATE INDEX IF NOT EXISTS idx_" + DatabaseConstants.VIOLATIONS_TABLE + "_grim_version_id ON " + DatabaseConstants.VIOLATIONS_TABLE + "(" + DatabaseConstants.VIOLATIONS_GRIM_VERSION_ID_COLUMN + ");" // NEW
).execute();
connection.prepareStatement(
"CREATE INDEX IF NOT EXISTS idx_" + DatabaseConstants.VIOLATIONS_TABLE + "_client_brand_id ON " + DatabaseConstants.VIOLATIONS_TABLE + "(" + DatabaseConstants.VIOLATIONS_CLIENT_BRAND_ID_COLUMN + ");" // NEW
).execute();
connection.prepareStatement(
"CREATE INDEX IF NOT EXISTS idx_" + DatabaseConstants.VIOLATIONS_TABLE + "_client_version_id ON " + DatabaseConstants.VIOLATIONS_TABLE + "(" + DatabaseConstants.VIOLATIONS_CLIENT_VERSION_ID_COLUMN + ");" // NEW
).execute();
connection.prepareStatement(
"CREATE INDEX IF NOT EXISTS idx_" + DatabaseConstants.VIOLATIONS_TABLE + "_server_version_id ON " + DatabaseConstants.VIOLATIONS_TABLE + "(" + DatabaseConstants.VIOLATIONS_SERVER_VERSION_ID_COLUMN + ");" // NEW
).execute();
} catch (SQLException ex) {
LogUtil.error("Failed to generate violations database:", ex);
throw ex;
}
}
@Override
// Updated method signature to accept all new parameters
public synchronized void logAlert(GrimPlayer player, String grimVersion, String verbose, String checkName, int vls) {
try (Connection connection = dataSource.getConnection();
// Updated INSERT statement with all new columns
PreparedStatement insertAlert = connection.prepareStatement(
"INSERT INTO " + DatabaseConstants.VIOLATIONS_TABLE + " (" +
DatabaseConstants.VIOLATIONS_SERVER_ID_COLUMN + ", " +
DatabaseConstants.VIOLATIONS_UUID_COLUMN + ", " +
DatabaseConstants.VIOLATIONS_CHECK_NAME_ID_COLUMN + ", " +
quoteVerboseColumn() + ", " +
DatabaseConstants.VIOLATIONS_VL_COLUMN + ", " +
DatabaseConstants.VIOLATIONS_CREATED_AT_COLUMN + ", " +
DatabaseConstants.VIOLATIONS_GRIM_VERSION_ID_COLUMN + ", " + // NEW
DatabaseConstants.VIOLATIONS_CLIENT_BRAND_ID_COLUMN + ", " + // NEW
DatabaseConstants.VIOLATIONS_CLIENT_VERSION_ID_COLUMN + ", " + // NEW
DatabaseConstants.VIOLATIONS_SERVER_VERSION_ID_COLUMN + // NEW
") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" // Total 10 parameters now
)
) {
// Get or create IDs for all deduplicated strings
String serverName = GrimAPI.INSTANCE.getConfigManager().getConfig().getStringElse("history.server-name", "Prison");
long serverId = DatabaseUtils.getOrCreateId(connection, dialect, DatabaseConstants.SERVERS_TABLE, DatabaseConstants.SERVERS_STRING_COLUMN, serverName);
long checkNameId = DatabaseUtils.getOrCreateId(connection, dialect, DatabaseConstants.CHECK_NAMES_TABLE, DatabaseConstants.CHECK_NAMES_STRING_COLUMN, checkName);
long grimVersionId = DatabaseUtils.getOrCreateId(connection, dialect, DatabaseConstants.GRIM_VERSIONS_TABLE, DatabaseConstants.GRIM_VERSIONS_STRING_COLUMN, grimVersion);
long clientBrandId = DatabaseUtils.getOrCreateId(connection, dialect, DatabaseConstants.CLIENT_BRANDS_TABLE, DatabaseConstants.CLIENT_BRANDS_STRING_COLUMN, player.getBrand());
long clientVersionId = DatabaseUtils.getOrCreateId(connection, dialect, DatabaseConstants.CLIENT_VERSIONS_TABLE, DatabaseConstants.CLIENT_VERSIONS_STRING_COLUMN, player.getClientVersion().getReleaseName());
long serverVersionId = DatabaseUtils.getOrCreateId(connection, dialect, DatabaseConstants.SERVER_VERSIONS_TABLE, DatabaseConstants.SERVER_VERSIONS_STRING_COLUMN, PacketEvents.getAPI().getServerManager().getVersion().toString());
// Set parameters for the PreparedStatement
insertAlert.setLong(1, serverId);
insertAlert.setObject(2, player.getUniqueId());
insertAlert.setLong(3, checkNameId);
insertAlert.setString(4, verbose);
insertAlert.setInt(5, vls);
insertAlert.setLong(6, System.currentTimeMillis());
insertAlert.setLong(7, grimVersionId); // NEW
insertAlert.setLong(8, clientBrandId); // NEW
insertAlert.setLong(9, clientVersionId); // NEW
insertAlert.setLong(10, serverVersionId); // NEW
insertAlert.execute();
} catch (SQLException ex) {
LogUtil.error("Failed to log alert", ex);
}
}
@Override
public synchronized int getLogCount(UUID uuid) {
try (Connection connection = dataSource.getConnection();
PreparedStatement countLogs = connection.prepareStatement(
"SELECT COUNT(*) FROM " + DatabaseConstants.VIOLATIONS_TABLE + " WHERE " + DatabaseConstants.VIOLATIONS_UUID_COLUMN + " = ?"
)
) {
countLogs.setObject(1, uuid);
ResultSet result = countLogs.executeQuery();
if (result.next()) {
return result.getInt(1);
}
} catch (SQLException ex) {
LogUtil.error("Failed to count logs", ex);
}
return 0;
}
@Override
public synchronized List<Violation> getViolations(UUID uuid, int page, int limit) {
try (Connection connection = dataSource.getConnection();
// Updated SELECT statement with all new joins and column selections
PreparedStatement fetchLogs = connection.prepareStatement(
"SELECT " +
"v." + DatabaseConstants.VIOLATIONS_ID_COLUMN + ", " +
"s." + DatabaseConstants.SERVERS_STRING_COLUMN + ", " +
"v." + DatabaseConstants.VIOLATIONS_UUID_COLUMN + ", " +
"cn." + DatabaseConstants.CHECK_NAMES_STRING_COLUMN + ", " +
"v." + quoteVerboseColumn() + ", " +
"v." + DatabaseConstants.VIOLATIONS_VL_COLUMN + ", " +
"v." + DatabaseConstants.VIOLATIONS_CREATED_AT_COLUMN + ", " +
"gv." + DatabaseConstants.GRIM_VERSIONS_STRING_COLUMN + ", " + // NEW
"cb." + DatabaseConstants.CLIENT_BRANDS_STRING_COLUMN + ", " + // NEW
"clv." + DatabaseConstants.CLIENT_VERSIONS_STRING_COLUMN + ", " + // NEW
"srv." + DatabaseConstants.SERVER_VERSIONS_STRING_COLUMN + " " + // NEW
"FROM " + DatabaseConstants.VIOLATIONS_TABLE + " v " +
"JOIN " + DatabaseConstants.SERVERS_TABLE + " s ON v." + DatabaseConstants.VIOLATIONS_SERVER_ID_COLUMN + " = s.id " +
"JOIN " + DatabaseConstants.CHECK_NAMES_TABLE + " cn ON v." + DatabaseConstants.VIOLATIONS_CHECK_NAME_ID_COLUMN + " = cn.id " +
"JOIN " + DatabaseConstants.GRIM_VERSIONS_TABLE + " gv ON v." + DatabaseConstants.VIOLATIONS_GRIM_VERSION_ID_COLUMN + " = gv.id " + // NEW
"JOIN " + DatabaseConstants.CLIENT_BRANDS_TABLE + " cb ON v." + DatabaseConstants.VIOLATIONS_CLIENT_BRAND_ID_COLUMN + " = cb.id " + // NEW
"JOIN " + DatabaseConstants.CLIENT_VERSIONS_TABLE + " clv ON v." + DatabaseConstants.VIOLATIONS_CLIENT_VERSION_ID_COLUMN + " = clv.id " + // NEW
"JOIN " + DatabaseConstants.SERVER_VERSIONS_TABLE + " srv ON v." + DatabaseConstants.VIOLATIONS_SERVER_VERSION_ID_COLUMN + " = srv.id " + // NEW
"WHERE v." + DatabaseConstants.VIOLATIONS_UUID_COLUMN + " = ? ORDER BY v." + DatabaseConstants.VIOLATIONS_CREATED_AT_COLUMN + " DESC LIMIT ? OFFSET ?"
)
) {
fetchLogs.setObject(1, uuid);
fetchLogs.setInt(2, limit);
fetchLogs.setInt(3, Math.max(0, (page - 1)) * limit); // postgresql is not allowing negative numbers
return Violation.fromResultSet(fetchLogs.executeQuery());
} catch (SQLException ex) {
LogUtil.error("Failed to fetch logs", ex);
return null;
}
}
@Override
public void disconnect() {
if (dataSource != null && !dataSource.isClosed()) {
dataSource.close();
}
}
public boolean sameConfig(String host, String db, String user, String pwd) {
String wantUrl = "jdbc:postgresql://" + host + "/" + db;
return wantUrl.equalsIgnoreCase(dataSource.getJdbcUrl())
&& user.equals(dataSource.getUsername())
&& pwd.equals(dataSource.getPassword());
}
}
@@ -120,13 +120,13 @@ public class GrimPlayer implements GrimUser {
public long lastTransSent = 0;
public long lastTransReceived = 0;
@Getter
private long playerClockAtLeast = System.nanoTime();
private long playerClockAtLeast;
public double lastWasClimbing = 0;
public boolean canSwimHop = false;
public int riptideSpinAttackTicks = 0;
public int powderSnowFrozenTicks = 0;
public boolean hasGravity = true;
public final long joinTime = System.currentTimeMillis();
public final long joinTime;
public boolean playerEntityHasGravity = true;
public VectorData predictedVelocity = new VectorData(new Vector3dm(), VectorData.VectorType.Normal);
public Vector3dm actualMovement = new Vector3dm();
@@ -272,6 +272,12 @@ public class GrimPlayer implements GrimUser {
public boolean lastJumping;
public GrimPlayer(@NotNull User user) {
this(user, System.currentTimeMillis(), System.nanoTime());
}
protected GrimPlayer(@NotNull User user, long joinTime, long clockStart) {
this.joinTime = joinTime;
this.playerClockAtLeast = clockStart;
this.user = user;
this.uuid = user.getUUID();
fireworks = new CompensatedFireworks(this); // Must be before checkmanager
@@ -397,7 +403,7 @@ public class GrimPlayer implements GrimUser {
// Transactions that we send don't count towards total limit
if (viaPacketTracker != null) viaPacketTracker.setIntervalPackets(viaPacketTracker.getIntervalPackets() - 1);
if (skipped > 0 && System.currentTimeMillis() - joinTime > 5000)
if (skipped > 0 && now() - joinTime > 5000)
checkManager.getCheck(TransactionOrder.class).flagAndAlert("skipped: " + skipped);
do {
@@ -406,14 +412,14 @@ public class GrimPlayer implements GrimUser {
break;
lastTransactionReceived.incrementAndGet();
lastTransReceived = System.currentTimeMillis();
transactionPing = (System.nanoTime() - data.second());
lastTransReceived = now();
transactionPing = (this.nano() - data.second());
playerClockAtLeast = data.second();
} while (data.first() != id);
// A transaction means a new tick, so handle any block interactions
CheckManagerListener.handleQueuedPlaces(this, false, 0, 0, System.currentTimeMillis());
CheckManagerListener.handleQueuedBreaks(this, false, 0, 0, System.currentTimeMillis());
CheckManagerListener.handleQueuedPlaces(this, false, 0, 0, now());
CheckManagerListener.handleQueuedBreaks(this, false, 0, 0, now());
latencyUtils.handleNettySyncTransaction(lastTransactionReceived.get());
}
@@ -461,11 +467,11 @@ public class GrimPlayer implements GrimUser {
if (user.getEncoderState() != ConnectionState.PLAY) return;
// Send a packet once every 15 seconds to avoid any memory leaks
if (disableGrim && (System.nanoTime() - getPlayerClockAtLeast()) > 15e9) {
if (disableGrim && (this.nano() - getPlayerClockAtLeast()) > 15e9) {
return;
}
lastTransSent = System.currentTimeMillis();
lastTransSent = now();
short transactionID = (short) (-1 * (transactionIDCounter.getAndIncrement() & 0x7FFF));
try {
@@ -524,15 +530,20 @@ public class GrimPlayer implements GrimUser {
}
public void pollData() {
// Reset the debug timer for the Scheduler Thread.
// In Production: This is a no-op (0 cost).
// In Debug: This tells the clock "I am starting a new tick, don't freeze time."
this.processingPacket();
// Send a transaction at least once a tick, for timer and post check purposes
// Don't be the first to send the transaction, or we will stack overflow
//
// This will only really activate if there's no entities around the player being tracked
// 80 is a magic value that is roughly every other tick, we don't want to spam too many packets.
if (lastTransSent != 0 && lastTransSent + 80 < System.currentTimeMillis()) {
if (lastTransSent != 0 && lastTransSent + 80 < now()) {
sendTransaction(true); // send on netty thread
}
if ((System.nanoTime() - getPlayerClockAtLeast()) > maxTransactionTime * 1e9) {
if ((this.nano() - getPlayerClockAtLeast()) > maxTransactionTime * 1e9) {
timedOut();
}
@@ -988,4 +999,27 @@ public class GrimPlayer implements GrimUser {
return blockStateId;
}
/**
* Returns the current high-resolution time in nanoseconds.
* In Debug Mode, this will subtract time spent paused at breakpoints.
*/
public long nano() {
return System.nanoTime();
}
/**
* Returns the current wall-clock time in milliseconds.
* In Debug Mode, this will subtract time spent paused at breakpoints.
*/
public long now() {
return System.currentTimeMillis();
}
/**
* Called immediately when a packet starts processing.
* Used by DebugGrimPlayer to differentiate "Lag" (Pre-Packet) from "Breakpoints" (Intra-Packet).
* Optimized away (Dead Code Elimination) in production.
*/
public void processingPacket() {}
}
@@ -71,13 +71,13 @@ public class MovementCheckRunner extends Check implements PositionCheck {
if (!invalidVehicle && !data.isTeleport()) {
// Teleport the player back to avoid players being able to simply ignore transactions
// We shouldn't simulate movement in unloaded chunks
player.getSetbackTeleportUtil().executeForceResyncNoSimulation();
player.getSetbackTeleportUtil().executeNonSimulatingForceResync();
}
}
long start = System.nanoTime();
long start = player.nano();
check(data);
long length = System.nanoTime() - start;
long length = player.nano() - start;
if (!player.disableGrim) {
predictionNanos = (predictionNanos * 499 / 500d) + (length / 500d);
@@ -352,7 +352,7 @@ public class MovementCheckRunner extends Check implements PositionCheck {
boolean clientClaimsRiptide = player.packetStateData.tryingToRiptide;
if (player.packetStateData.tryingToRiptide) {
long currentTime = System.currentTimeMillis();
long currentTime = player.now();
boolean isInWater = player.isInWaterOrRain();
if (currentTime - player.packetStateData.lastRiptide < 450 || !isInWater) {
@@ -3,6 +3,8 @@ package ac.grim.grimac.utils.anticheat;
import ac.grim.grimac.GrimAPI;
import ac.grim.grimac.api.event.events.GrimJoinEvent;
import ac.grim.grimac.api.event.events.GrimQuitEvent;
import ac.grim.grimac.checks.debug.DebugGrimPlayer;
import ac.grim.grimac.checks.debug.GrimDebugSettings;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.reflection.GeyserUtil;
import com.github.retrooper.packetevents.PacketEvents;
@@ -66,12 +68,21 @@ public class PlayerDataManager {
public void addUser(final @NotNull User user) {
if (shouldCheck(user)) {
GrimPlayer player = new GrimPlayer(user);
GrimPlayer player = this.createPlayer(user);
playerDataMap.put(user, player);
GrimAPI.INSTANCE.getEventBus().post(new GrimJoinEvent(player));
}
}
public GrimPlayer createPlayer(User user) {
// Simple Global Check
if (GrimDebugSettings.shouldEnable()) {
return new DebugGrimPlayer(user);
} else {
return new GrimPlayer(user);
}
}
public GrimPlayer remove(final @NotNull User user) {
return playerDataMap.remove(user);
}
@@ -29,4 +29,10 @@ public class CommonGrimArguments {
* This setting is opt-in (default: false) and requires a server restart to change.
*/
public final static SystemArgument<Boolean> USE_CHAT_FAST_BYPASS = FACTORY.create(string("ChatFastBypass", true));
/**
* If TRUE, and a Debugger is detected attached to the JVM, Grim will use the
* DebugGrimPlayer implementation which absorbs breakpoint pauses > 200ms.
* Default: false (Safety).
*/
public final static SystemArgument<Boolean> DEBUG_BREAKPOINT_MODE = FACTORY.create(string("DebugBreakpointMode", false));
}
@@ -501,6 +501,7 @@ public class CompensatedInventory extends Check implements PacketCheck {
* Closes the player's currently open inventory on the client by resetting to the player's inventory.
*/
private void closeActiveInventory() {
isPacketInventoryActive = true;
openWindowID = 0;
menu = inventory;
menu.setCarried(ItemStack.EMPTY); // Reset carried item
@@ -27,33 +27,33 @@ public final class BoundingBoxSize {
private static float getWidthMinusBaby(GrimPlayer player, PacketEntity packetEntity) {
final EntityType type = packetEntity.type;
if (EntityTypes.AXOLOTL.equals(type)) {
if (type == EntityTypes.AXOLOTL) {
return 0.75f;
} else if (EntityTypes.PANDA.equals(type)) {
} else if (type == EntityTypes.PANDA) {
return 1.3f;
} else if (EntityTypes.BAT.equals(type) || EntityTypes.PARROT.equals(type) || EntityTypes.COD.equals(type) || EntityTypes.EVOKER_FANGS.equals(type) || EntityTypes.TROPICAL_FISH.equals(type) || EntityTypes.FROG.equals(type) || EntityTypes.COPPER_GOLEM.equals(type)) {
} else if (type == EntityTypes.BAT || type == EntityTypes.PARROT || type == EntityTypes.COD || type == EntityTypes.EVOKER_FANGS || type == EntityTypes.TROPICAL_FISH || type == EntityTypes.FROG || type == EntityTypes.COPPER_GOLEM) {
return 0.5f;
} else if (EntityTypes.ARMADILLO.equals(type) || EntityTypes.BEE.equals(type) || EntityTypes.PUFFERFISH.equals(type) || EntityTypes.SALMON.equals(type) || EntityTypes.SNOW_GOLEM.equals(type) || EntityTypes.CAVE_SPIDER.equals(type)) {
} else if (type == EntityTypes.ARMADILLO || type == EntityTypes.BEE || type == EntityTypes.PUFFERFISH || type == EntityTypes.SALMON || type == EntityTypes.SNOW_GOLEM || type == EntityTypes.CAVE_SPIDER) {
return 0.7f;
} else if (EntityTypes.WITHER_SKELETON.equals(type)) {
} else if (type == EntityTypes.WITHER_SKELETON) {
return player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_9) ? 0.7f : 0.72f;
} else if (EntityTypes.WITHER_SKULL.equals(type) || EntityTypes.SHULKER_BULLET.equals(type)) {
} else if (type == EntityTypes.WITHER_SKULL || type == EntityTypes.SHULKER_BULLET) {
return 0.3125f;
} else if (EntityTypes.HOGLIN.equals(type) || EntityTypes.ZOGLIN.equals(type)) {
} else if (type == EntityTypes.HOGLIN || type == EntityTypes.ZOGLIN) {
return 1.3964844f;
} else if (EntityTypes.SKELETON_HORSE.equals(type) || EntityTypes.ZOMBIE_HORSE.equals(type) || EntityTypes.HORSE.equals(type) || EntityTypes.DONKEY.equals(type) || EntityTypes.MULE.equals(type)) {
} else if (type == EntityTypes.SKELETON_HORSE || type == EntityTypes.ZOMBIE_HORSE || type == EntityTypes.HORSE || type == EntityTypes.DONKEY || type == EntityTypes.MULE) {
return player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_9) ? 1.3964844f : 1.4f;
} else if (EntityTypes.isTypeInstanceOf(type, EntityTypes.BOAT)) {
return player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_9) ? 1.375f : 1.5f;
} else if (EntityTypes.HAPPY_GHAST.equals(type)) {
} else if (type == EntityTypes.HAPPY_GHAST) {
return 4.0f;
} else if (EntityTypes.CHICKEN.equals(type) || EntityTypes.ENDERMITE.equals(type) || EntityTypes.SILVERFISH.equals(type) || EntityTypes.VEX.equals(type) || EntityTypes.TADPOLE.equals(type)) {
} else if (type == EntityTypes.CHICKEN || type == EntityTypes.ENDERMITE || type == EntityTypes.SILVERFISH || type == EntityTypes.VEX || type == EntityTypes.TADPOLE) {
return 0.4f;
} else if (EntityTypes.RABBIT.equals(type)) {
} else if (type == EntityTypes.RABBIT) {
return player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_9) ? 0.4f : 0.6f;
} else if (EntityTypes.CREAKING.equals(type) || EntityTypes.STRIDER.equals(type) || EntityTypes.COW.equals(type) || EntityTypes.SHEEP.equals(type) || EntityTypes.MOOSHROOM.equals(type) || EntityTypes.PIG.equals(type) || EntityTypes.LLAMA.equals(type) || EntityTypes.DOLPHIN.equals(type) || EntityTypes.WITHER.equals(type) || EntityTypes.TRADER_LLAMA.equals(type) || EntityTypes.WARDEN.equals(type) || EntityTypes.GOAT.equals(type)) {
} else if (type == EntityTypes.CREAKING || type == EntityTypes.STRIDER || type == EntityTypes.COW || type == EntityTypes.SHEEP || type == EntityTypes.MOOSHROOM || type == EntityTypes.PIG || type == EntityTypes.LLAMA || type == EntityTypes.DOLPHIN || type == EntityTypes.WITHER || type == EntityTypes.TRADER_LLAMA || type == EntityTypes.WARDEN || type == EntityTypes.GOAT) {
return 0.9f;
} else if (EntityTypes.PHANTOM.equals(type)) {
} else if (type == EntityTypes.PHANTOM) {
if (packetEntity instanceof PacketEntitySizeable sizeable) {
return 0.9f + sizeable.size * 0.2f;
}
@@ -61,21 +61,21 @@ public final class BoundingBoxSize {
return 1.5f;
} else if (packetEntity instanceof PacketEntityGuardian packetEntityGuardian) { // TODO: 2.35 * guardian?
return packetEntityGuardian.isElder ? 1.9975f : 0.85f;
} else if (EntityTypes.END_CRYSTAL.equals(type)) {
} else if (type == EntityTypes.END_CRYSTAL) {
return 2f;
} else if (EntityTypes.ENDER_DRAGON.equals(type)) {
} else if (type == EntityTypes.ENDER_DRAGON) {
return 16f;
} else if (EntityTypes.FIREBALL.equals(type)) {
} else if (type == EntityTypes.FIREBALL) {
return 1f;
} else if (EntityTypes.GHAST.equals(type)) {
} else if (type == EntityTypes.GHAST) {
return 4f;
} else if (EntityTypes.GIANT.equals(type)) {
} else if (type == EntityTypes.GIANT) {
return 3.6f;
} else if (EntityTypes.GUARDIAN.equals(type)) {
} else if (type == EntityTypes.GUARDIAN) {
return 0.85f;
} else if (EntityTypes.IRON_GOLEM.equals(type)) {
} else if (type == EntityTypes.IRON_GOLEM) {
return 1.4f;
} else if (EntityTypes.MAGMA_CUBE.equals(type)) {
} else if (type == EntityTypes.MAGMA_CUBE) {
if (packetEntity instanceof PacketEntitySizeable sizeable) {
float size = sizeable.size;
return player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_20_5)
@@ -87,15 +87,15 @@ public final class BoundingBoxSize {
return 0.98f;
} else if (EntityTypes.isTypeInstanceOf(type, EntityTypes.MINECART_ABSTRACT)) {
return 0.98f;
} else if (EntityTypes.PLAYER.equals(type) || EntityTypes.MANNEQUIN.equals(type)) {
} else if (type == EntityTypes.PLAYER || type == EntityTypes.MANNEQUIN) {
return 0.6f;
} else if (EntityTypes.POLAR_BEAR.equals(type)) {
} else if (type == EntityTypes.POLAR_BEAR) {
return 1.4f;
} else if (EntityTypes.RAVAGER.equals(type)) {
} else if (type == EntityTypes.RAVAGER) {
return 1.95f;
} else if (EntityTypes.SHULKER.equals(type)) {
} else if (type == EntityTypes.SHULKER) {
return 1f;
} else if (EntityTypes.SLIME.equals(type)) {
} else if (type == EntityTypes.SLIME) {
if (packetEntity instanceof PacketEntitySizeable sizeable) {
float size = sizeable.size;
return player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_20_5)
@@ -104,27 +104,27 @@ public final class BoundingBoxSize {
}
return 0.3125f;
} else if (EntityTypes.SMALL_FIREBALL.equals(type)) {
} else if (type == EntityTypes.SMALL_FIREBALL) {
return 0.3125f;
} else if (EntityTypes.SPIDER.equals(type)) {
} else if (type == EntityTypes.SPIDER) {
return 1.4f;
} else if (EntityTypes.SQUID.equals(type)) {
} else if (type == EntityTypes.SQUID) {
return player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_9) ? 0.8f : 0.95f;
} else if (EntityTypes.TURTLE.equals(type)) {
} else if (type == EntityTypes.TURTLE) {
return 1.2f;
} else if (EntityTypes.ALLAY.equals(type)) {
} else if (type == EntityTypes.ALLAY) {
return 0.35f;
} else if (EntityTypes.SNIFFER.equals(type)) {
} else if (type == EntityTypes.SNIFFER) {
return 1.9f;
} else if (EntityTypes.CAMEL.equals(type)) {
} else if (type == EntityTypes.CAMEL) {
return 1.7f;
} else if (EntityTypes.WIND_CHARGE.equals(type)) {
} else if (type == EntityTypes.WIND_CHARGE) {
return 0.3125f;
} else if (EntityTypes.ARMOR_STAND.equals(type)) {
} else if (type == EntityTypes.ARMOR_STAND) {
return 0.5F;
} else if (EntityTypes.FALLING_BLOCK.equals(type)) {
} else if (type == EntityTypes.FALLING_BLOCK) {
return 0.98F;
} else if (EntityTypes.FIREWORK_ROCKET.equals(type)) {
} else if (type == EntityTypes.FIREWORK_ROCKET) {
return 0.25F;
}
return 0.6f;
@@ -191,15 +191,15 @@ public final class BoundingBoxSize {
public static double getMyRidingOffset(PacketEntity packetEntity) {
final EntityType type = packetEntity.type;
if (EntityTypes.PIGLIN.equals(type) || EntityTypes.ZOMBIFIED_PIGLIN.equals(type) || EntityTypes.ZOMBIE.equals(type)) {
if (type == EntityTypes.PIGLIN || type == EntityTypes.ZOMBIFIED_PIGLIN || type == EntityTypes.ZOMBIE) {
return packetEntity.isBaby ? -0.05 : -0.45;
} else if (EntityTypes.SKELETON.equals(type)) {
} else if (type == EntityTypes.SKELETON) {
return -0.6;
} else if (EntityTypes.ENDERMITE.equals(type) || EntityTypes.SILVERFISH.equals(type)) {
} else if (type == EntityTypes.ENDERMITE || type == EntityTypes.SILVERFISH) {
return 0.1;
} else if (EntityTypes.EVOKER.equals(type) || EntityTypes.ILLUSIONER.equals(type) || EntityTypes.PILLAGER.equals(type) || EntityTypes.RAVAGER.equals(type) || EntityTypes.VINDICATOR.equals(type) || EntityTypes.WITCH.equals(type)) {
} else if (type == EntityTypes.EVOKER || type == EntityTypes.ILLUSIONER || type == EntityTypes.PILLAGER || type == EntityTypes.RAVAGER || type == EntityTypes.VINDICATOR || type == EntityTypes.WITCH) {
return -0.45;
} else if (EntityTypes.PLAYER.equals(type) || EntityTypes.MANNEQUIN.equals(type)) {
} else if (type == EntityTypes.PLAYER || type == EntityTypes.MANNEQUIN) {
return -0.35;
}
@@ -219,21 +219,21 @@ public final class BoundingBoxSize {
return 0;
} else if (EntityTypes.isTypeInstanceOf(type, EntityTypes.BOAT)) {
return -0.1;
} else if (EntityTypes.HAPPY_GHAST.equals(type)) {
} else if (type == EntityTypes.HAPPY_GHAST) {
return 0.5;
} else if (EntityTypes.HOGLIN.equals(type) || EntityTypes.ZOGLIN.equals(type)) {
} else if (type == EntityTypes.HOGLIN || type == EntityTypes.ZOGLIN) {
return getHeight(player, packetEntity) - (packetEntity.isBaby ? 0.2 : 0.15);
} else if (EntityTypes.LLAMA.equals(type)) {
} else if (type == EntityTypes.LLAMA) {
return getHeight(player, packetEntity) * 0.67;
} else if (EntityTypes.PIGLIN.equals(type)) {
} else if (type == EntityTypes.PIGLIN) {
return getHeight(player, packetEntity) * 0.92;
} else if (EntityTypes.RAVAGER.equals(type)) {
} else if (type == EntityTypes.RAVAGER) {
return 2.1;
} else if (EntityTypes.SKELETON.equals(type)) {
} else if (type == EntityTypes.SKELETON) {
return (getHeight(player, packetEntity) * 0.75) - 0.1875;
} else if (EntityTypes.SPIDER.equals(type)) {
} else if (type == EntityTypes.SPIDER) {
return getHeight(player, packetEntity) * 0.5;
} else if (EntityTypes.STRIDER.equals(type)) {// depends on animation position, good luck getting it exactly, this is the best you can do though
} else if (type == EntityTypes.STRIDER) {// depends on animation position, good luck getting it exactly, this is the best you can do though
return getHeight(player, packetEntity) - 0.19;
}
return getHeight(player, packetEntity) * 0.75;
@@ -241,77 +241,77 @@ public final class BoundingBoxSize {
private static float getHeightMinusBaby(GrimPlayer player, PacketEntity packetEntity) {
final EntityType type = packetEntity.type;
if (EntityTypes.ARMADILLO.equals(type)) {
if (type == EntityTypes.ARMADILLO) {
return 0.65f;
} else if (EntityTypes.AXOLOTL.equals(type)) {
} else if (type == EntityTypes.AXOLOTL) {
return 0.42f;
} else if (EntityTypes.BEE.equals(type) || EntityTypes.DOLPHIN.equals(type) || EntityTypes.ALLAY.equals(type)) {
} else if (type == EntityTypes.BEE || type == EntityTypes.DOLPHIN || type == EntityTypes.ALLAY) {
return 0.6f;
} else if (EntityTypes.EVOKER_FANGS.equals(type) || EntityTypes.VEX.equals(type)) {
} else if (type == EntityTypes.EVOKER_FANGS || type == EntityTypes.VEX) {
return 0.8f;
} else if (EntityTypes.SQUID.equals(type)) {
} else if (type == EntityTypes.SQUID) {
return player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_9) ? 0.8f : 0.95f;
} else if (EntityTypes.PARROT.equals(type) || EntityTypes.BAT.equals(type) || EntityTypes.PIG.equals(type) || EntityTypes.SPIDER.equals(type)) {
} else if (type == EntityTypes.PARROT || type == EntityTypes.BAT || type == EntityTypes.PIG || type == EntityTypes.SPIDER) {
return 0.9f;
} else if (EntityTypes.WITHER_SKULL.equals(type) || EntityTypes.SHULKER_BULLET.equals(type)) {
} else if (type == EntityTypes.WITHER_SKULL || type == EntityTypes.SHULKER_BULLET) {
return 0.3125f;
} else if (EntityTypes.BLAZE.equals(type)) {
} else if (type == EntityTypes.BLAZE) {
return 1.8f;
} else if (EntityTypes.isTypeInstanceOf(type, EntityTypes.BOAT)) {
// WHY DOES VIAVERSION OFFSET BOATS? THIS MAKES IT HARD TO SUPPORT, EVEN IF WE INTERPOLATE RIGHT.
// I gave up and just exempted boats from the reach check and gave up with interpolation for collisions
return 0.5625f;
} else if (EntityTypes.HAPPY_GHAST.equals(type)) {
} else if (type == EntityTypes.HAPPY_GHAST) {
return 4.0f;
} else if (EntityTypes.CAT.equals(type)) {
} else if (type == EntityTypes.CAT) {
return 0.7f;
} else if (EntityTypes.CAVE_SPIDER.equals(type)) {
} else if (type == EntityTypes.CAVE_SPIDER) {
return 0.5f;
} else if (EntityTypes.FROG.equals(type)) {
} else if (type == EntityTypes.FROG) {
return 0.55f;
} else if (EntityTypes.CHICKEN.equals(type)) {
} else if (type == EntityTypes.CHICKEN) {
return 0.7f;
} else if (EntityTypes.HOGLIN.equals(type) || EntityTypes.ZOGLIN.equals(type)) {
} else if (type == EntityTypes.HOGLIN || type == EntityTypes.ZOGLIN) {
return 1.4f;
} else if (EntityTypes.COW.equals(type)) {
} else if (type == EntityTypes.COW) {
return player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_9) ? 1.4f : 1.3f;
} else if (EntityTypes.STRIDER.equals(type)) {
} else if (type == EntityTypes.STRIDER) {
return 1.7f;
} else if (EntityTypes.CREEPER.equals(type)) {
} else if (type == EntityTypes.CREEPER) {
return 1.7f;
} else if (EntityTypes.DONKEY.equals(type)) {
} else if (type == EntityTypes.DONKEY) {
return 1.5f;
} else if (packetEntity instanceof PacketEntityGuardian packetEntityGuardian) { // TODO: 2.35 * guardian?
return packetEntityGuardian.isElder ? 1.9975f : 0.85f;
} else if (EntityTypes.ENDERMAN.equals(type) || EntityTypes.WARDEN.equals(type)) {
} else if (type == EntityTypes.ENDERMAN || type == EntityTypes.WARDEN) {
return 2.9f;
} else if (EntityTypes.ENDERMITE.equals(type) || EntityTypes.COD.equals(type)) {
} else if (type == EntityTypes.ENDERMITE || type == EntityTypes.COD) {
return 0.3f;
} else if (EntityTypes.END_CRYSTAL.equals(type)) {
} else if (type == EntityTypes.END_CRYSTAL) {
return 2f;
} else if (EntityTypes.ENDER_DRAGON.equals(type)) {
} else if (type == EntityTypes.ENDER_DRAGON) {
return 8f;
} else if (EntityTypes.FIREBALL.equals(type)) {
} else if (type == EntityTypes.FIREBALL) {
return 1f;
} else if (EntityTypes.FOX.equals(type)) {
} else if (type == EntityTypes.FOX) {
return 0.7f;
} else if (EntityTypes.GHAST.equals(type)) {
} else if (type == EntityTypes.GHAST) {
return 4f;
} else if (EntityTypes.GIANT.equals(type)) {
} else if (type == EntityTypes.GIANT) {
return 12f;
} else if (EntityTypes.GUARDIAN.equals(type)) {
} else if (type == EntityTypes.GUARDIAN) {
return 0.85f;
} else if (EntityTypes.HORSE.equals(type)) {
} else if (type == EntityTypes.HORSE) {
return 1.6f;
} else if (EntityTypes.IRON_GOLEM.equals(type)) {
} else if (type == EntityTypes.IRON_GOLEM) {
return player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_9) ? 2.7f : 2.9f;
} else if (EntityTypes.CREAKING.equals(type)) {
} else if (type == EntityTypes.CREAKING) {
return 2.7f;
} else if (EntityTypes.LLAMA.equals(type) || EntityTypes.TRADER_LLAMA.equals(type)) {
} else if (type == EntityTypes.LLAMA || type == EntityTypes.TRADER_LLAMA) {
return 1.87f;
} else if (EntityTypes.TROPICAL_FISH.equals(type)) {
} else if (type == EntityTypes.TROPICAL_FISH) {
return 0.4f;
} else if (EntityTypes.MAGMA_CUBE.equals(type)) {
} else if (type == EntityTypes.MAGMA_CUBE) {
if (packetEntity instanceof PacketEntitySizeable sizeable) {
float size = sizeable.size;
return player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_20_5)
@@ -323,43 +323,43 @@ public final class BoundingBoxSize {
return 0.7f;
} else if (EntityTypes.isTypeInstanceOf(type, EntityTypes.MINECART_ABSTRACT)) {
return 0.7f;
} else if (EntityTypes.MULE.equals(type)) {
} else if (type == EntityTypes.MULE) {
return 1.6f;
} else if (EntityTypes.MOOSHROOM.equals(type)) {
} else if (type == EntityTypes.MOOSHROOM) {
return player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_9) ? 1.4f : 1.3f;
} else if (EntityTypes.OCELOT.equals(type)) {
} else if (type == EntityTypes.OCELOT) {
return 0.7f;
} else if (EntityTypes.PANDA.equals(type)) {
} else if (type == EntityTypes.PANDA) {
return 1.25f;
} else if (EntityTypes.PHANTOM.equals(type)) {
} else if (type == EntityTypes.PHANTOM) {
if (packetEntity instanceof PacketEntitySizeable sizeable) {
return 0.5f + sizeable.size * 0.1f;
}
return 1.8f;
} else if (EntityTypes.PLAYER.equals(type) || EntityTypes.MANNEQUIN.equals(type)) {
} else if (type == EntityTypes.PLAYER || type == EntityTypes.MANNEQUIN) {
return 1.8f;
} else if (EntityTypes.POLAR_BEAR.equals(type)) {
} else if (type == EntityTypes.POLAR_BEAR) {
return 1.4f;
} else if (EntityTypes.PUFFERFISH.equals(type)) {
} else if (type == EntityTypes.PUFFERFISH) {
return 0.7f;
} else if (EntityTypes.RABBIT.equals(type)) {
} else if (type == EntityTypes.RABBIT) {
return player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_9) ? 0.5f : 0.7f;
} else if (EntityTypes.RAVAGER.equals(type)) {
} else if (type == EntityTypes.RAVAGER) {
return 2.2f;
} else if (EntityTypes.SALMON.equals(type)) {
} else if (type == EntityTypes.SALMON) {
return 0.4f;
} else if (EntityTypes.SHEEP.equals(type) || EntityTypes.GOAT.equals(type)) {
} else if (type == EntityTypes.SHEEP || type == EntityTypes.GOAT) {
return 1.3f;
} else if (EntityTypes.SHULKER.equals(type)) { // Could maybe guess peek size, although seems useless
} else if (type == EntityTypes.SHULKER) { // Could maybe guess peek size, although seems useless
return 2f;
} else if (EntityTypes.SILVERFISH.equals(type)) {
} else if (type == EntityTypes.SILVERFISH) {
return 0.3f;
} else if (EntityTypes.SKELETON.equals(type)) {
} else if (type == EntityTypes.SKELETON) {
return player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_9) ? 1.99f : 1.95f;
} else if (EntityTypes.SKELETON_HORSE.equals(type)) {
} else if (type == EntityTypes.SKELETON_HORSE) {
return 1.6f;
} else if (EntityTypes.SLIME.equals(type)) {
} else if (type == EntityTypes.SLIME) {
if (packetEntity instanceof PacketEntitySizeable sizeable) {
float size = sizeable.size;
return player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_20_5)
@@ -369,43 +369,43 @@ public final class BoundingBoxSize {
}
return 0.3125f;
} else if (EntityTypes.SMALL_FIREBALL.equals(type)) {
} else if (type == EntityTypes.SMALL_FIREBALL) {
return 0.3125f;
} else if (EntityTypes.SNOW_GOLEM.equals(type)) {
} else if (type == EntityTypes.SNOW_GOLEM) {
return 1.9f;
} else if (EntityTypes.STRAY.equals(type)) {
} else if (type == EntityTypes.STRAY) {
return 1.99f;
} else if (EntityTypes.TURTLE.equals(type)) {
} else if (type == EntityTypes.TURTLE) {
return 0.4f;
} else if (EntityTypes.WITHER.equals(type)) {
} else if (type == EntityTypes.WITHER) {
return 3.5f;
} else if (EntityTypes.WITHER_SKELETON.equals(type)) {
} else if (type == EntityTypes.WITHER_SKELETON) {
return player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_9) ? 2.4f : 2.535f;
} else if (EntityTypes.WOLF.equals(type)) {
} else if (type == EntityTypes.WOLF) {
return player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_9) ? 0.85f : 0.8f;
} else if (EntityTypes.ZOMBIE_HORSE.equals(type)) {
} else if (type == EntityTypes.ZOMBIE_HORSE) {
return 1.6f;
} else if (EntityTypes.TADPOLE.equals(type)) {
} else if (type == EntityTypes.TADPOLE) {
return 0.3f;
} else if (EntityTypes.SNIFFER.equals(type)) {
} else if (type == EntityTypes.SNIFFER) {
return 1.75f;
} else if (EntityTypes.CAMEL.equals(type)) {
} else if (type == EntityTypes.CAMEL) {
return 2.375f;
} else if (EntityTypes.BREEZE.equals(type)) {
} else if (type == EntityTypes.BREEZE) {
return 1.77f;
} else if (EntityTypes.BOGGED.equals(type)) {
} else if (type == EntityTypes.BOGGED) {
return 1.99f;
} else if (EntityTypes.WIND_CHARGE.equals(type)) {
} else if (type == EntityTypes.WIND_CHARGE) {
return 0.3125f;
} else if (EntityTypes.ARMOR_STAND.equals(type)) {
} else if (type == EntityTypes.ARMOR_STAND) {
return 1.975F;
} else if (EntityTypes.FALLING_BLOCK.equals(type)) {
} else if (type == EntityTypes.FALLING_BLOCK) {
return 0.98F;
} else if (EntityTypes.VILLAGER.equals(type) && player.getClientVersion().isOlderThan(ClientVersion.V_1_9)) {
} else if (type == EntityTypes.VILLAGER && player.getClientVersion().isOlderThan(ClientVersion.V_1_9)) {
return 1.8F;
} else if (EntityTypes.FIREWORK_ROCKET.equals(type)) {
} else if (type == EntityTypes.FIREWORK_ROCKET) {
return 0.25F;
} else if (EntityTypes.COPPER_GOLEM.equals(type)) {
} else if (type == EntityTypes.COPPER_GOLEM) {
return 1.0F;
}
return 1.95f;
+2 -2
View File
@@ -218,9 +218,9 @@ history:
# Welcher Servername soll für den History‑Befehl eingetragen werden? Nützlich, wenn dieselbe Datenbank für mehrere Server verwendet wird
server-name: Prison
database:
# SQLITE für lokale Speicherung, MYSQL für eine externe MySQL‑Datenbank. Wird nur nach Serverneustart aktualisiert
# SQLITE für lokale Speicherung, MYSQL oder POSTGRESQL für eine externe Datenbank. Wird nur nach Serverneustart aktualisiert
type: SQLITE
# MySQL‑Verbindungsdetails
# Datenbank verbindungsdetails
host: localhost
port: 3306
database: grim
+2 -2
View File
@@ -218,9 +218,9 @@ history:
# What should the inserted server name be for the history command? This is useful if you use the same database for multiple servers
server-name: Prison
database:
# Use SQLITE for local storage, use MYSQL if you have an external MySQL database. This is only updated on server restart
# Use SQLITE for local storage, use MYSQL or POSTGRESQL if you have an external database. This is only updated on server restart
type: SQLITE
# MySQL connection details
# database connection details
host: localhost
port: 3306
database: grim
+2 -2
View File
@@ -220,9 +220,9 @@ history:
# ¿Qué nombre de servidor debe insertarse para el comando history? Útil si compartes la misma base de datos entre varios servidores
server-name: Prison
database:
# Usa SQLITE para almacenamiento local o MYSQL si tienes una base de datos MySQL externa. Solo se actualiza al reiniciar el servidor
# Use SQLITE para almacenamiento local, use MYSQL o POSTGRESQL si tiene una base de datos externa. Esto solo se actualiza al reiniciar el servidor
type: SQLITE
# Detalles de conexión MySQL
# Detalles de conexión de la base de datos
host: localhost
port: 3306
database: grim
+2 -2
View File
@@ -215,9 +215,9 @@ history:
# Quel nom de serveur doit être inséré pour la commande history ? Utile si vous utilisez la même base de données pour plusieurs serveurs
server-name: Prison
database:
# Utilisez SQLITE pour un stockage local, MYSQL si vous disposez d’une base MySQL externe. Mis à jour seulement au redémarrage du serveur
# Utilisez SQLITE pour le stockage local, utilisez MYSQL ou POSTGRESQL si vous avez une base de données externe. Ceci n'est mis à jour qu'au redémarrage du serveur
type: SQLITE
# Détails de connexion MySQL
# Détails de connexion à la base de données
host: localhost
port: 3306
database: grim
+2 -2
View File
@@ -196,9 +196,9 @@ history:
# Quale nome server deve essere inserito per il comando history? Utile se usi lo stesso database per più server
server-name: Prison
database:
# Usa SQLITE per l’archiviazione locale, MYSQL se hai un database MySQL esterno. Aggiornato solo al riavvio del server
# Usa SQLITE per l'archiviazione locale, usa MYSQL o POSTGRESQL se hai un database esterno. Questo viene aggiornato solo al riavvio del server
type: SQLITE
# Dettagli di connessione MySQL
# Dettagli di connessione al database
host: localhost
port: 3306
database: grim
+2 -2
View File
@@ -227,9 +227,9 @@ history:
# history コマンドで挿入されるサーバー名は? 複数サーバーで同じデータベースを使用する場合に便利
server-name: Prison
database:
# ローカル保存には SQLITE、外部 MySQL データベースには MYSQL を使用します。サーバー再起動時にのみ反映
# ローカルストレージにはSQLITEを使用し、外部データベースがある場合はMYSQLまたはPOSTGRESQLを使用してください。これはサーバー再起動時にのみ更新されます
type: SQLITE
# MySQL 接続情報
# データベース接続の詳細
host: localhost
port: 3306
database: grim
+2 -2
View File
@@ -218,9 +218,9 @@ history:
# Welke servernaam moet voor het history‑commando worden ingevoerd? Handig als je dezelfde database voor meerdere servers gebruikt
server-name: Prison
database:
# Gebruik SQLITE voor lokale opslag, MYSQL voor een externe MySQL‑database. Alleen bijgewerkt na een serverherstart
# Gebruik SQLITE voor lokale opslag, gebruik MYSQL of POSTGRESQL als u een externe database heeft. Dit wordt alleen bijgewerkt bij het herstarten van de server
type: SQLITE
# MySQL‑verbindingsgegevens
# Databaseverbindingsgegevens
host: localhost
port: 3306
database: grim
+2 -2
View File
@@ -223,9 +223,9 @@ history:
# Qual nome de servidor deve ser inserido no comando history? Útil se você usa o mesmo banco de dados para vários servidores
server-name: Prison
database:
# Use SQLITE para armazenamento local ou MYSQL para um banco MySQL externo. Atualizado somente ao reiniciar o servidor
# Use SQLITE para armazenamento local, use MYSQL ou POSTGRESQL se você tiver um banco de dados externo. Isso só é atualizado ao reiniciar o servidor
type: SQLITE
# Detalhes de conexão MySQL
# Detalhes de conexão do banco de dados
host: localhost
port: 3306
database: grim
+2 -2
View File
@@ -217,9 +217,9 @@ history:
# Ce nume de server să fie inserat pentru comanda history? Util dacă folosești aceeași bază de date pentru mai multe servere
server-name: Prison
database:
# Folosește SQLITE pentru stocare locală, MYSQL dacă ai bază de date externă. Se actualizează doar la restart server
# Folosiți SQLITE pentru stocare locală, folosiți MYSQL sau POSTGRESQL dacă aveți o bază de date externă. Aceasta este actualizată doar la repornirea serverului
type: SQLITE
# Detalii conexiune MySQL
# Detalii de conectare la baza de date
host: localhost
port: 3306
database: grim
+2 -2
View File
@@ -214,9 +214,9 @@ history:
# Какое имя сервера вставлять для команды history? Полезно при использовании одной базы для нескольких серверов
server-name: Prison
database:
# Используйте SQLITE для локального хранения или MYSQL для внешней MySQL‑БД. Изменения применяются после перезапуска сервера
# Используйте SQLITE для локального хранения, используйте MYSQL или POSTGRESQL, если у вас есть внешняя база данных. Это обновляется только при перезапуске сервера
type: SQLITE
# Параметры подключения MySQL
# анные для подключения к базе данных
host: localhost
port: 3306
database: grim
+2 -2
View File
@@ -218,9 +218,9 @@ history:
# History komutu için eklenecek sunucu adı nedir? Aynı veritabanını birden fazla sunucuda kullanıyorsanız faydalıdır
server-name: Prison
database:
# Yerel saklama için SQLITE, harici MySQL veritabanı için MYSQL kullanın. Sadece sunucu yeniden başlatıldığında güncellenir
# Yerel depolama için SQLITE kullanın, harici bir veritabanınız varsa MYSQL veya POSTGRESQL kullanın. Bu yalnızca sunucu yeniden başlatıldığında güncellenir
type: SQLITE
# MySQL bağlantı bilgileri
# veritabanı bağlantı detayları
host: localhost
port: 3306
database: grim
+2 -2
View File
@@ -221,9 +221,9 @@ history:
# history 指令中插入的服务器名称?如果多个服务器共用同一数据库则很有用
server-name: Prison
database:
# 本地存储使用 SQLITE,外部数据库使用 MYSQL。仅在重启服务器后生效
# 本地存储使用SQLITE,如有外部数据库请使用MYSQL或POSTGRESQL。此设置仅在服务器重启时更新
type: SQLITE
# MySQL 连接详情
# 数据库连接详情
host: localhost
port: 3306
database: grim
@@ -38,7 +38,7 @@ public abstract class AbstractFabricGrimEntity implements GrimEntity {
@Override
public PlatformWorld getWorld() {
return (PlatformWorld) entity.level;
return this.entity.level;
}
@Override
@@ -4,20 +4,22 @@ import ac.grim.grimac.platform.api.world.PlatformChunk;
import ac.grim.grimac.platform.api.world.PlatformWorld;
import ac.grim.grimac.platform.fabric.GrimACFabricLoaderPlugin;
import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState;
import net.minecraft.core.BlockPos;
import net.minecraft.resources.ResourceKey;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.block.Block;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.*;
import java.util.UUID;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.storage.ServerLevelData;
@Mixin(ServerLevel.class)
@Mixin(Level.class)
@Implements(@Interface(iface = PlatformWorld.class, prefix = "grimac$"))
abstract class ServerLevelMixin implements LevelAccessor {
@Shadow public @Final ServerLevelData serverLevelData;
abstract class LevelMixin implements LevelAccessor {
@Shadow
public abstract ResourceKey<Level> dimension();
public boolean grimac$isChunkLoaded(int chunkX, int chunkZ) {
return hasChunk(chunkX, chunkZ);
@@ -30,7 +32,7 @@ abstract class ServerLevelMixin implements LevelAccessor {
}
public String grimac$getName() {
return serverLevelData.getLevelName();
return this.dimension().location().toString();
}
public @Nullable UUID grimac$getUID() {
@@ -42,6 +44,6 @@ abstract class ServerLevelMixin implements LevelAccessor {
}
public boolean grimac$isLoaded() {
return GrimACFabricLoaderPlugin.FABRIC_SERVER.getLevel(getLevel().dimension()) != null;
return GrimACFabricLoaderPlugin.FABRIC_SERVER.getLevel(this.dimension()) != null;
}
}
@@ -20,6 +20,13 @@
"mixins": [
"grimac.mixins.json"
],
"custom": {
"loom:injected_interfaces": {
"net/minecraft/world/level/Level": [
"ac/grim/grimac/platform/api/world/PlatformWorld"
]
}
},
"accessWidener": "grimac.accesswidener",
"depends": {
"fabricloader": "\u003e\u003d0.16",
+1 -1
View File
@@ -5,7 +5,7 @@
"compatibilityLevel": "JAVA_17",
"mixins": [
"LevelChunkMixin",
"ServerLevelMixin",
"LevelMixin",
"ServerPlayerMixin"
],
"injectors": {
+1 -1
View File
@@ -12,7 +12,7 @@ cloud-processors-requirements = "1.0.0-rc.1"
floodgate-api = "2.0-SNAPSHOT"
geyser-base-api = "1.0.2"
grim-api = "1.2.0.0"
packetevents = "2.10.0+cca07b3-SNAPSHOT"
packetevents = "2.10.2+3684f01-SNAPSHOT"
placeholderapi = "2.11.6"
viaversion = "5.5.0"