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
ManInMyVan def21633e2 disable ExploitB "invalid page characters" check 2025-11-03 19:54:02 -07:00
ItzBloxxy 918e6575e8 New translation ro.yml (#2285)
Config Romanian translation for grim ac.
2025-11-03 17:02:42 +00:00
SamB440 ea1f1615b6 Disable PacketOrderP, fixes #2327 2025-11-03 16:59:53 +00:00
SamB440 c323cf06ce Bump cloud to beta.13, fixes #2318 2025-11-03 16:30:53 +00:00
SamB440 0ba331780e Don't simulate movement in unloaded chunks?
Who knows what other problems this could cause...
2025-11-03 16:26:30 +00:00
ManInMyVan fc47c5b663 refactor via 1.11->1.10 chat hook 2025-11-01 17:10:11 -06:00
ManInMyVan 17cf067d58 fix #2330 2025-11-01 16:05:03 -06:00
ManInMyVan 6280b28e16 show size for applicable reach/hitbox flags 2025-11-01 12:23:17 -06:00
Axionize bfd2bd0e22 Reorder publication of artifacts to Modrinth and update supported versions 2025-10-31 22:29:16 -04:00
ManInMyVan 0df77dd32a slight cleanup of fabric code 2025-10-31 14:59:26 -06:00
ManInMyVan b373e5f1c4 switch to mojmap for fabric
see https://fabricmc.net/2025/10/31/obfuscation.html
2025-10-31 14:38:23 -06:00
Szypkoo 115c32916f fix: useless player fetch (#2329) 2025-10-31 11:03:27 +00:00
ManInMyVan 5f8692deda refactor plugin message wrappers 2025-10-30 17:32:01 -06:00
135 changed files with 1545 additions and 764 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
+32 -31
View File
@@ -36,7 +36,7 @@ on:
jobs:
build:
runs-on: ubuntu-latest
runs-on: tenki-standard-autoscale
permissions:
contents: read
packages: write
@@ -85,7 +85,7 @@ jobs:
run: |
# If any command in a pipeline fails, treat the pipeline as failed.
set -o pipefail
BUILD_ARGS="build"
if [[ "${{ inputs.is-release-build }}" == "true" ]]; then
BUILD_ARGS="$BUILD_ARGS -Prelease=true"
@@ -94,23 +94,23 @@ jobs:
if [[ "${{ inputs.run-build-scan }}" == "true" ]]; then
BUILD_ARGS="$BUILD_ARGS --scan"
fi
echo "Running: ./gradlew printVersion $BUILD_ARGS"
# Use 'tee' to both display the output live and save it to a file for parsing
VERSION=$(./gradlew printVersion $BUILD_ARGS | tee build-output.log | grep '^VERSION=' | tail -n1 | cut -d'=' -f2)
echo "build_version=$VERSION" >> $GITHUB_OUTPUT
echo "BUILD_VERSION=$VERSION" >> $GITHUB_ENV
echo "Build version: $VERSION"
- name: Publish Build Scan URL
# Only run this step if a scan was requested
# Only run this step if a scan was requested
if: ${{ inputs.run-build-scan == true }}
run: |
# Search the log file for the build scan URL and extract it
SCAN_URL=$(grep -o 'https://gradle.com/s/[^ ]*' build-output.log | head -n 1 || true)
if [[ -n "$SCAN_URL" ]]; then
echo "Found build scan URL: $SCAN_URL"
# Post a highly visible link to the Job Summary page
@@ -151,7 +151,7 @@ jobs:
- name: Prepare Final Changelog
id: changelog
# Add this 'env' block to safely load the changelogs as environment variables
# Add this 'env' block to safely load the changelogs as environment variables
env:
RELEASE_NOTES: ${{ inputs.release-changelog }}
DEV_NOTES: ${{ steps.git-cliff.outputs.content }}
@@ -165,17 +165,36 @@ jobs:
# Use the environment variable for the dev changelog.
NOTES="$DEV_NOTES"
fi
if [ -z "$NOTES" ]; then
NOTES="* No new code changes in this build."
fi
# This is the correct way to set a multi-line output variable in Actions
{
echo "notes<<EOF"
echo "$NOTES"
echo "EOF"
} >> "$GITHUB_OUTPUT"
- name: Publish Fabric to Modrinth
if: ${{ inputs.publish-artifacts == true && env.MODRINTH_ID != '' && env.MODRINTH_TOKEN != '' }}
uses: Kir-Antipov/mc-publish@v3.3
with:
modrinth-id: ${{ secrets.MODRINTH_ID }}
modrinth-token: ${{ secrets.MODRINTH_TOKEN }}
modrinth-featured: ${{ inputs.is-release-build }} # Only feature release builds
files: fabric/build/libs/grimac-fabric-${{ env.BUILD_VERSION }}.jar
name: Grim Anticheat (Fabric) ${{ env.BUILD_VERSION }}
version: ${{ env.BUILD_VERSION }}
version-type: ${{ inputs.release-version-type }}
changelog: ${{ steps.changelog.outputs.notes }}
loaders: |
fabric
game-versions: '>=1.21.5 <=1.21.10'
dependencies: 'fabric-api'
#------------------------------------------------------------------
# Publish to Modrinth & API
#------------------------------------------------------------------
@@ -183,7 +202,7 @@ jobs:
if: ${{ inputs.publish-artifacts == true && env.MODRINTH_ID != '' && env.MODRINTH_TOKEN != '' }}
uses: Kir-Antipov/mc-publish@v3.3
with:
modrinth-id: ${{ secrets.MODRINTH_ID }}
modrinth-id: ${{ secrets.MODRINTH_ID }}
modrinth-token: ${{ secrets.MODRINTH_TOKEN }}
modrinth-featured: ${{ inputs.is-release-build }} # Only feature release builds
@@ -198,25 +217,7 @@ jobs:
paper
folia
purpur
game-versions: '>=1.7 <=1.21.8'
- name: Publish Fabric to Modrinth
if: ${{ inputs.publish-artifacts == true && env.MODRINTH_ID != '' && env.MODRINTH_TOKEN != '' }}
uses: Kir-Antipov/mc-publish@v3.3
with:
modrinth-id: ${{ secrets.MODRINTH_ID }}
modrinth-token: ${{ secrets.MODRINTH_TOKEN }}
modrinth-featured: ${{ inputs.is-release-build }} # Only feature release builds
files: fabric/build/libs/grimac-fabric-${{ env.BUILD_VERSION }}.jar
name: Grim Anticheat (Fabric) ${{ env.BUILD_VERSION }}
version: ${{ env.BUILD_VERSION }}
version-type: ${{ inputs.release-version-type }}
changelog: ${{ steps.changelog.outputs.notes }}
loaders: |
fabric
game-versions: '>=1.16.1 <=1.21.8'
dependencies: 'fabric-api'
game-versions: '>=1.7 <=1.21.10'
- name: Publish to API
if: ${{ inputs.publish-artifacts == true && env.BUILD_UPDATE_TOKEN != '' }}
@@ -226,4 +227,4 @@ jobs:
-H "User-Agent: GrimACBuilder/v1" \
-H "Authorization: Bearer ${{ secrets.BUILD_UPDATE_TOKEN }}" \
-d '{"org": "${{ github.repository_owner }}", "repository": "${{ github.event.repository.name }}", "branch": "${{ github.ref_name }}", "version": "${{ env.BUILD_VERSION }}"}' \
"${{ secrets.BUILD_UPDATE_URL }}"
"${{ secrets.BUILD_UPDATE_URL }}"
+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
+1 -1
View File
@@ -195,7 +195,7 @@ publishing.publications.create<MavenPublication>("maven") {
tasks {
runServer {
minecraftVersion("1.21.4")
minecraftVersion("1.21.10")
}
shadowJar {
@@ -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;
}
@@ -170,7 +170,8 @@ public class Check extends GrimProcessor implements AbstractCheck {
}
public static boolean isAsync(PacketTypeCommon packetType) {
return packetType == PacketType.Play.Client.KEEP_ALIVE;
return packetType == PacketType.Play.Client.KEEP_ALIVE
|| packetType == PacketType.Play.Client.CHUNK_BATCH_ACK;
}
public boolean isUpdate(PacketTypeCommon packetType) {
@@ -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();
}
}
@@ -7,7 +7,7 @@ import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.BlockBreak;
import ac.grim.grimac.utils.math.GrimMath;
import ac.grim.grimac.utils.nmsutil.BlockBreakSpeed;
import ac.grim.grimac.utils.reflection.ViaVersionUtil;
import ac.grim.grimac.utils.viaversion.ViaVersionUtil;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
@@ -67,12 +67,12 @@ public class FastBreak extends Check implements BlockBreakCheck {
// * can we translate back "up" to server version and run check against server version to avoid loading older registries?
WrappedBlockState block = clientOlderThanServer ? WrappedBlockState.getByGlobalId(player.getClientVersion(), player.getViaTranslatedClientBlockID(blockBreak.block.getGlobalId())) : blockBreak.block;
startBreak = System.currentTimeMillis() - (targetBlockPosition == null ? 50 : 0); // ???
startBreak = player.now() - (targetBlockPosition == null ? 50 : 0); // ???
targetBlockPosition = blockBreak.position;
maximumBlockDamage = BlockBreakSpeed.getBlockDamage(player, block);
double breakDelay = System.currentTimeMillis() - lastFinishBreak;
double breakDelay = player.now() - lastFinishBreak;
if (breakDelay >= 275) { // Reduce buffer if "close enough"
blockDelayBalance *= 0.9;
@@ -91,7 +91,7 @@ public class FastBreak extends Check implements BlockBreakCheck {
if (blockBreak.action == DiggingAction.FINISHED_DIGGING && targetBlockPosition != null) {
double predictedTime = Math.ceil(1 / maximumBlockDamage) * 50;
double realTime = System.currentTimeMillis() - startBreak;
double realTime = player.now() - startBreak;
double diff = predictedTime - realTime;
clampBalance();
@@ -109,7 +109,7 @@ public class FastBreak extends Check implements BlockBreakCheck {
}
// also set start time because the breaking netcode is fucked on 1.14.4+
lastFinishBreak = startBreak = System.currentTimeMillis();
lastFinishBreak = startBreak = player.now();
}
}
@@ -4,8 +4,6 @@ import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PacketCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.reflection.ViaVersionUtil;
import ac.grim.grimac.utils.via.ViaHooks;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
@@ -63,10 +61,4 @@ public class ChatB extends Check implements PacketCheck {
}
return false;
}
static {
if (ViaVersionUtil.isAvailable && PacketEvents.getAPI().getServerManager().getVersion().isOlderThan(ServerVersion.V_1_11)) {
ViaHooks.injectVia();
}
}
}
@@ -22,6 +22,7 @@ import ac.grim.grimac.checks.type.PacketCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
import ac.grim.grimac.utils.data.packetentity.PacketEntity;
import ac.grim.grimac.utils.data.packetentity.PacketEntitySizeable;
import ac.grim.grimac.utils.data.packetentity.dragon.PacketEntityEnderDragonPart;
import ac.grim.grimac.utils.math.Vector3dm;
import ac.grim.grimac.utils.nmsutil.ReachUtils;
@@ -94,7 +95,10 @@ public class Reach extends Check implements PacketCheck {
// TODO: Remove when in front of via
if (entity.type == EntityTypes.ARMOR_STAND && player.getClientVersion().isOlderThan(ClientVersion.V_1_8))
return;
//Prevents Happy Ghast Reach false on 1.21.6+ servers with ViaBackwards set up
if (entity.type == EntityTypes.HAPPY_GHAST && player.getClientVersion().isOlderThan(ClientVersion.V_1_21_6)) {
return;
}
if (player.gamemode == GameMode.CREATIVE || player.gamemode == GameMode.SPECTATOR)
return;
if (player.inVehicle()) return;
@@ -158,10 +162,16 @@ public class Reach extends Check implements PacketCheck {
switch (result.type()) {
case REACH -> {
String added = ", type=" + reachEntity.type.getName().getKey();
if (reachEntity instanceof PacketEntitySizeable sizeable) {
added += ", size=" + sizeable.size;
}
flagAndAlert(result.verbose() + added);
}
case HITBOX -> {
String added = "type=" + reachEntity.type.getName().getKey();
if (reachEntity instanceof PacketEntitySizeable sizeable) {
added += ", size=" + sizeable.size;
}
player.checkManager.getCheck(Hitboxes.class).flagAndAlert(result.verbose() + added);
}
}
@@ -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);
@@ -29,7 +29,7 @@ public class ExploitA extends Check implements PacketCheck {
if (event.getPacketType() == PacketType.Play.Client.PLUGIN_MESSAGE && PacketEvents.getAPI().getServerManager().getVersion().isOlderThan(ServerVersion.V_1_13)) {
WrapperPlayClientPluginMessage wrapper = new WrapperPlayClientPluginMessage(event);
if (wrapper.getChannelName().equals("MC|ItemName")) {
check(PayloadItemName.CODEC.read(wrapper.getData()).itemName(), event);
check(new PayloadItemName(wrapper.getData()).itemName(), event);
}
}
}
@@ -72,7 +72,7 @@ public class ExploitB extends Check implements PacketCheck {
}
}
ItemStack newBook = PayloadBookEdit.CODEC.read(wrapper.getData()).itemStack();
ItemStack newBook = new PayloadBookEdit(wrapper.getData()).itemStack();
checkItemStack(newBook, signing, event);
oldBook = null;
}
@@ -287,13 +287,14 @@ public class ExploitB extends Check implements PacketCheck {
return true;
}
if (containsInvalidCharacters(page, true)) {
if (flagAndAlert("invalid page characters") && shouldModifyPackets()) {
event.setCancelled(true);
player.onPacketCancel();
}
return true;
}
// commented out because existing characters are not considered
// if (containsInvalidCharacters(page, true)) {
// if (flagAndAlert("invalid page characters") && shouldModifyPackets()) {
// event.setCancelled(true);
// player.onPacketCancel();
// }
// return true;
// }
return false;
}
@@ -12,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);
}
@@ -1,6 +1,5 @@
package ac.grim.grimac.checks.impl.velocity;
import ac.grim.grimac.GrimAPI;
import ac.grim.grimac.api.config.ConfigManager;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
@@ -46,9 +45,6 @@ public class KnockbackHandler extends Check implements PostPredictionCheck {
WrapperPlayServerEntityVelocity velocity = new WrapperPlayServerEntityVelocity(event);
int entityId = velocity.getEntityId();
GrimPlayer player = GrimAPI.INSTANCE.getPlayerDataManager().getPlayer(event.getUser());
if (player == null) return;
// Detect whether this knockback packet affects the player or if it is useless
// Mojang sends extra useless knockback packets for no apparent reason
if (player.compensatedEntities.serverPlayerVehicle != null && entityId != player.compensatedEntities.serverPlayerVehicle) {
@@ -7,7 +7,7 @@ import ac.grim.grimac.platform.api.sender.Sender;
import ac.grim.grimac.utils.anticheat.MessageUtil;
import ac.grim.grimac.utils.common.PropertiesUtil;
import ac.grim.grimac.utils.reflection.ReflectionUtils;
import ac.grim.grimac.utils.reflection.ViaVersionUtil;
import ac.grim.grimac.utils.viaversion.ViaVersionUtil;
import com.github.retrooper.packetevents.PacketEvents;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
@@ -51,9 +51,9 @@ public class GrimDump implements BuildableCommand {
GrimLog.sendLogAsync(sender, generateDump(), string -> link = string, "text/yaml");
}
public static JsonObject getBasicInfo(String type) {
public static JsonObject getDumpInfo() {
JsonObject base = new JsonObject();
base.addProperty("type", type);
base.addProperty("type", "dump");
base.addProperty("timestamp", System.currentTimeMillis());
// versions
JsonObject versions = new JsonObject();
@@ -68,6 +68,11 @@ public class GrimDump implements BuildableCommand {
if (GrimAPI.INSTANCE.isInitialized()) states.addProperty("platform", GrimAPI.INSTANCE.getPlatform().toString());
if (ViaVersionUtil.isAvailable) states.addProperty("has_viaversion", true);
if (PAPER) states.addProperty("has_paper", true);
// include some relevant settings if not default
JsonObject settings = new JsonObject();
if (GrimAPI.INSTANCE.getAlertManager().hasConsoleVerboseEnabled()) settings.addProperty("console_verbose", true);
if (!GrimAPI.INSTANCE.getAlertManager().hasConsoleAlertsEnabled()) settings.addProperty("console_alerts", false);
if (settings.size() > 0) states.add("settings", settings);
// system
JsonObject system = new JsonObject();
base.add("system", system);
@@ -75,8 +80,17 @@ public class GrimDump implements BuildableCommand {
system.addProperty("java_version", System.getProperty("java.version"));
system.addProperty("user_language", System.getProperty("user.language"));
// build
JsonObject build = new JsonObject();
base.add("build", getBuildInfo());
// plugins
JsonArray plugins = new JsonArray();
base.add("plugins", plugins);
for (PlatformPlugin plugin : GrimAPI.INSTANCE.getPluginManager().getPlugins()) {
JsonObject pluginJson = new JsonObject();
pluginJson.addProperty("enabled", plugin.isEnabled());
pluginJson.addProperty("name", plugin.getName());
pluginJson.addProperty("version", plugin.getVersion());
plugins.add(pluginJson);
}
return base;
}
@@ -98,17 +112,7 @@ public class GrimDump implements BuildableCommand {
* @return A JSON-formatted string containing the diagnostic dump.
*/
private String generateDump() {
JsonObject base = getBasicInfo("dump");
// plugins
JsonArray plugins = new JsonArray();
base.add("plugins", plugins);
for (PlatformPlugin plugin : GrimAPI.INSTANCE.getPluginManager().getPlugins()) {
JsonObject pluginJson = new JsonObject();
pluginJson.addProperty("enabled", plugin.isEnabled());
pluginJson.addProperty("name", plugin.getName());
pluginJson.addProperty("version", plugin.getVersion());
plugins.add(pluginJson);
}
JsonObject base = getDumpInfo();
return gson.toJson(base);
}
}
@@ -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());
@@ -10,7 +10,7 @@ import ac.grim.grimac.utils.data.TrackerData;
import ac.grim.grimac.utils.data.packetentity.PacketEntity;
import ac.grim.grimac.utils.data.packetentity.PacketEntityHook;
import ac.grim.grimac.utils.data.packetentity.PacketEntityTrackXRot;
import ac.grim.grimac.utils.reflection.ViaVersionUtil;
import ac.grim.grimac.utils.viaversion.ViaVersionUtil;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.event.PacketSendEvent;
@@ -72,7 +72,7 @@ public class PacketPingListener extends PacketListenerAbstract {
if (id <= 0) {
if (player.didWeSendThatTrans.remove(id)) {
player.packetStateData.lastServerTransWasValid = true;
player.transactionsSent.add(new Pair<>(id, System.nanoTime()));
player.transactionsSent.add(new Pair<>(id, player.nano()));
player.lastTransactionSent.getAndIncrement();
}
}
@@ -91,7 +91,7 @@ public class PacketPingListener extends PacketListenerAbstract {
Short shortID = ((short) id);
if (player.didWeSendThatTrans.remove(shortID)) {
player.packetStateData.lastServerTransWasValid = true;
player.transactionsSent.add(new Pair<>(shortID, System.nanoTime()));
player.transactionsSent.add(new Pair<>(shortID, player.nano()));
player.lastTransactionSent.getAndIncrement();
}
}
@@ -228,7 +228,7 @@ public class PacketPlayerDigging extends PacketListenerAbstract {
// do we need to do this with block breaks too?
// Prevent issues if the player switches slots, while lagging, standing still, and is placing blocks
CheckManagerListener.handleQueuedPlaces(player, false, 0, 0, System.currentTimeMillis());
CheckManagerListener.handleQueuedPlaces(player, false, 0, 0, player.now());
if (player.packetStateData.lastSlotSelected != slot) {
if (player.isResetItemUsageOnSlotChange() && GrimAPI.INSTANCE.getItemResetHandler().getItemUsageHand(player.platformPlayer) == InteractionHand.MAIN_HAND) {
@@ -210,7 +210,9 @@ public class PacketPlayerRespawn extends PacketListenerAbstract {
}
player.pose = Pose.STANDING;
player.clientVelocity = new Vector3dm();
player.gamemode = respawn.getGameMode();
if (!GrimAPI.INSTANCE.getSpectateManager().isSpectating(player.uuid)) {
player.gamemode = respawn.getGameMode();
}
if (PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_17)) {
player.compensatedWorld.setDimension(respawn.getDimensionType(), event.getUser());
}
@@ -31,7 +31,7 @@ public class PacketWorldBorder extends Check implements PacketCheck {
}
public double getCurrentDiameter() {
double d0 = (double) (System.currentTimeMillis() - startTime) / ((double) endTime - startTime);
double d0 = (double) (player.now() - startTime) / ((double) endTime - startTime);
return d0 < 1.0D ? GrimMath.lerp(d0, oldDiameter, newDiameter) : newDiameter;
}
@@ -125,7 +125,7 @@ public class PacketWorldBorder extends Check implements PacketCheck {
private void setLerp(double oldDiameter, double newDiameter, long length) {
this.oldDiameter = oldDiameter;
this.newDiameter = newDiameter;
this.startTime = System.currentTimeMillis();
this.startTime = player.now();
this.endTime = this.startTime + length;
}
}
@@ -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))
@@ -125,7 +127,7 @@ public class CheckManager {
.put(PacketOrderC.class, new PacketOrderC(player))
.put(PacketOrderD.class, new PacketOrderD(player))
.put(PacketOrderO.class, new PacketOrderO(player))
.put(PacketOrderP.class, new PacketOrderP(player))
// .put(PacketOrderP.class, new PacketOrderP(player))
.put(SprintA.class, new SprintA(player))
.put(VehicleA.class, new VehicleA(player))
.put(VehicleB.class, new VehicleB(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,6 +100,13 @@ public class SetbackTeleportUtil extends Check implements PostPredictionCheck {
blockMovementsUntilResync(true, true);
}
public void executeNonSimulatingForceResync() {
if (player.gamemode == GameMode.SPECTATOR || player.disableGrim)
return; // We don't care about spectators, they don't flag
if (lastKnownGoodPosition == null) return; // Player hasn't spawned yet
blockMovementsUntilResync(false, true);
}
public void executeNonSimulatingSetback() {
if (player.gamemode == GameMode.SPECTATOR || player.disableGrim)
return; // We don't care about spectators, they don't flag
@@ -137,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))));
}
@@ -152,9 +161,9 @@ public class SetbackTeleportUtil extends Check implements PostPredictionCheck {
if (isPendingSetback()) return; // Don't spam setbacks
// Only let us full resync once every five seconds to prevent unneeded bukkit load
if (System.currentTimeMillis() - lastWorldResync > 5 * 1000) {
if (player.now() - lastWorldResync > 5 * 1000) {
player.resyncPositions(player.boundingBox.copy().expand(1));
lastWorldResync = System.currentTimeMillis();
lastWorldResync = player.now();
}
Vector3dm clientVel = lastKnownGoodPosition.vector.clone();
@@ -208,7 +217,7 @@ public class SetbackTeleportUtil extends Check implements PostPredictionCheck {
blockOffsets = true;
}
SetBackData data = new SetBackData(new TeleportData(position, new Vector3d(), new RelativeFlag(0b11000), player.lastTransactionSent.get(), 0), player.yaw, player.pitch, clientVel, player.inVehicle(), false);
SetBackData data = new SetBackData(new TeleportData(position, new Vector3d(), RelativeFlag.YAW.or(RelativeFlag.PITCH), player.lastTransactionSent.get(), 0), player.yaw, player.pitch, clientVel, player.inVehicle(), false);
sendSetback(data);
}
@@ -375,7 +384,7 @@ public class SetbackTeleportUtil extends Check implements PostPredictionCheck {
private boolean isPendingSetback() {
// Relative setbacks shouldn't count
if (requiredSetBack.getTeleportData().isRelativeX() || requiredSetBack.getTeleportData().isRelativeY() || requiredSetBack.getTeleportData().isRelativeZ()) {
if (requiredSetBack != null && (requiredSetBack.getTeleportData().isRelativeX() || requiredSetBack.getTeleportData().isRelativeY() || requiredSetBack.getTeleportData().isRelativeZ())) {
return false;
}
// The setback is not complete
@@ -2,7 +2,7 @@ package ac.grim.grimac.manager.init.start;
import ac.grim.grimac.GrimAPI;
import ac.grim.grimac.utils.anticheat.LogUtil;
import ac.grim.grimac.utils.reflection.ViaVersionUtil;
import ac.grim.grimac.utils.viaversion.ViaVersionUtil;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
@@ -2,7 +2,7 @@ package ac.grim.grimac.manager.init.start;
import ac.grim.grimac.GrimAPI;
import ac.grim.grimac.utils.anticheat.LogUtil;
import ac.grim.grimac.utils.reflection.ViaVersionUtil;
import ac.grim.grimac.utils.viaversion.ViaVersionUtil;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.viaversion.viaversion.api.Via;
@@ -12,6 +12,7 @@ public class ViaVersion implements StartableInitable {
@Override
public void start() {
if (!ViaVersionUtil.isAvailable) return;
ViaVersionUtil.injectHooks();
ServerVersion serverVersion = PacketEvents.getAPI().getServerManager().getVersion();
@@ -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());
}
}
@@ -39,7 +39,7 @@ import ac.grim.grimac.utils.nmsutil.BlockProperties;
import ac.grim.grimac.utils.nmsutil.Collisions;
import ac.grim.grimac.utils.nmsutil.GetBoundingBox;
import ac.grim.grimac.utils.nmsutil.Materials;
import ac.grim.grimac.utils.reflection.ViaVersionUtil;
import ac.grim.grimac.utils.viaversion.ViaVersionUtil;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.event.PacketSendEvent;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
@@ -120,13 +120,13 @@ public class GrimPlayer implements GrimUser {
public long lastTransSent = 0;
public long lastTransReceived = 0;
@Getter
private long playerClockAtLeast = System.nanoTime();
private long playerClockAtLeast;
public double lastWasClimbing = 0;
public boolean canSwimHop = false;
public int riptideSpinAttackTicks = 0;
public int powderSnowFrozenTicks = 0;
public boolean hasGravity = true;
public final long joinTime = System.currentTimeMillis();
public final long joinTime;
public boolean playerEntityHasGravity = true;
public VectorData predictedVelocity = new VectorData(new Vector3dm(), VectorData.VectorType.Normal);
public Vector3dm actualMovement = new Vector3dm();
@@ -272,6 +272,12 @@ public class GrimPlayer implements GrimUser {
public boolean lastJumping;
public GrimPlayer(@NotNull User user) {
this(user, System.currentTimeMillis(), System.nanoTime());
}
protected GrimPlayer(@NotNull User user, long joinTime, long clockStart) {
this.joinTime = joinTime;
this.playerClockAtLeast = clockStart;
this.user = user;
this.uuid = user.getUUID();
fireworks = new CompensatedFireworks(this); // Must be before checkmanager
@@ -397,7 +403,7 @@ public class GrimPlayer implements GrimUser {
// Transactions that we send don't count towards total limit
if (viaPacketTracker != null) viaPacketTracker.setIntervalPackets(viaPacketTracker.getIntervalPackets() - 1);
if (skipped > 0 && System.currentTimeMillis() - joinTime > 5000)
if (skipped > 0 && now() - joinTime > 5000)
checkManager.getCheck(TransactionOrder.class).flagAndAlert("skipped: " + skipped);
do {
@@ -406,14 +412,14 @@ public class GrimPlayer implements GrimUser {
break;
lastTransactionReceived.incrementAndGet();
lastTransReceived = System.currentTimeMillis();
transactionPing = (System.nanoTime() - data.second());
lastTransReceived = now();
transactionPing = (this.nano() - data.second());
playerClockAtLeast = data.second();
} while (data.first() != id);
// A transaction means a new tick, so handle any block interactions
CheckManagerListener.handleQueuedPlaces(this, false, 0, 0, System.currentTimeMillis());
CheckManagerListener.handleQueuedBreaks(this, false, 0, 0, System.currentTimeMillis());
CheckManagerListener.handleQueuedPlaces(this, false, 0, 0, now());
CheckManagerListener.handleQueuedBreaks(this, false, 0, 0, now());
latencyUtils.handleNettySyncTransaction(lastTransactionReceived.get());
}
@@ -461,11 +467,11 @@ public class GrimPlayer implements GrimUser {
if (user.getEncoderState() != ConnectionState.PLAY) return;
// Send a packet once every 15 seconds to avoid any memory leaks
if (disableGrim && (System.nanoTime() - getPlayerClockAtLeast()) > 15e9) {
if (disableGrim && (this.nano() - getPlayerClockAtLeast()) > 15e9) {
return;
}
lastTransSent = System.currentTimeMillis();
lastTransSent = now();
short transactionID = (short) (-1 * (transactionIDCounter.getAndIncrement() & 0x7FFF));
try {
@@ -524,15 +530,20 @@ public class GrimPlayer implements GrimUser {
}
public void pollData() {
// Reset the debug timer for the Scheduler Thread.
// In Production: This is a no-op (0 cost).
// In Debug: This tells the clock "I am starting a new tick, don't freeze time."
this.processingPacket();
// Send a transaction at least once a tick, for timer and post check purposes
// Don't be the first to send the transaction, or we will stack overflow
//
// This will only really activate if there's no entities around the player being tracked
// 80 is a magic value that is roughly every other tick, we don't want to spam too many packets.
if (lastTransSent != 0 && lastTransSent + 80 < System.currentTimeMillis()) {
if (lastTransSent != 0 && lastTransSent + 80 < now()) {
sendTransaction(true); // send on netty thread
}
if ((System.nanoTime() - getPlayerClockAtLeast()) > maxTransactionTime * 1e9) {
if ((this.nano() - getPlayerClockAtLeast()) > maxTransactionTime * 1e9) {
timedOut();
}
@@ -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() {}
}
@@ -70,13 +70,14 @@ public class MovementCheckRunner extends Check implements PositionCheck {
if (!invalidVehicle && !data.isTeleport()) {
// Teleport the player back to avoid players being able to simply ignore transactions
player.getSetbackTeleportUtil().executeForceResync();
// We shouldn't simulate movement in unloaded chunks
player.getSetbackTeleportUtil().executeNonSimulatingForceResync();
}
}
long start = System.nanoTime();
long start = player.nano();
check(data);
long length = System.nanoTime() - start;
long length = player.nano() - start;
if (!player.disableGrim) {
predictionNanos = (predictionNanos * 499 / 500d) + (length / 500d);
@@ -351,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) {
@@ -15,7 +15,7 @@ import ac.grim.grimac.utils.nmsutil.Collisions;
import ac.grim.grimac.utils.nmsutil.FluidFallingAdjustedMovement;
import ac.grim.grimac.utils.nmsutil.GetBoundingBox;
import ac.grim.grimac.utils.nmsutil.MainSupportingBlockPosFinder;
import ac.grim.grimac.utils.reflection.ViaVersionUtil;
import ac.grim.grimac.utils.viaversion.ViaVersionUtil;
import ac.grim.grimac.utils.team.EntityPredicates;
import ac.grim.grimac.utils.team.EntityTeam;
import ac.grim.grimac.utils.team.TeamHandler;
@@ -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);
}
@@ -22,7 +22,7 @@ import ac.grim.grimac.utils.collisions.datatypes.NoCollisionBox;
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
import ac.grim.grimac.utils.data.packetentity.PacketEntityStrider;
import ac.grim.grimac.utils.nmsutil.Materials;
import ac.grim.grimac.utils.reflection.ViaVersionUtil;
import ac.grim.grimac.utils.viaversion.ViaVersionUtil;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.item.ItemStack;
@@ -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));
}
@@ -287,9 +287,9 @@ public class CompensatedEntities {
if (sizeObject != null) {
Object value = sizeObject.getValue();
if (value instanceof Integer) {
sizeable.size = Math.max((int) value, 1);
sizeable.size = (int) value;
} else if (value instanceof Byte) {
sizeable.size = Math.max((byte) value, 1);
sizeable.size = (byte) value;
}
}
}
@@ -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;
@@ -51,7 +51,17 @@ public class GetBoundingBox {
double minZ = centerZ - (width / 2f);
double maxZ = centerZ + (width / 2f);
return new SimpleCollisionBox(minX, minY, minZ, maxX, maxY, maxZ, false);
// it's possible for width and/or height to be negative,
// correct the order of min and max
return new SimpleCollisionBox(
Math.min(minX, maxX),
Math.min(minY, maxY),
Math.min(minZ, maxZ),
Math.max(minX, maxX),
Math.max(minY, maxY),
Math.max(minZ, maxZ),
false
);
}
public static double @NotNull [] getEntityDimensions(GrimPlayer player, @NotNull PacketEntity entity) {
@@ -67,11 +77,20 @@ public class GetBoundingBox {
double height = dimensions[1];
double halfDepth = dimensions[2] / 2.0;
box.minX -= halfWidth;
box.minY -= 0; // No downward expansion
box.minZ -= halfDepth;
box.maxX += halfWidth;
box.maxY += height;
box.maxZ += halfDepth;
double minX = box.minX - halfWidth;
double minY = box.minY; // No downward expansion
double minZ = box.minZ - halfDepth;
double maxX = box.maxX + halfWidth;
double maxY = box.maxY + height;
double maxZ = box.maxZ + halfDepth;
// it's possible for width and/or height to be negative,
// correct the order of min and max
box.minX = Math.min(minX, maxX);
box.minY = Math.min(minY, maxY);
box.minZ = Math.min(minZ, maxZ);
box.maxX = Math.max(minX, maxX);
box.maxY = Math.max(minY, maxY);
box.maxZ = Math.max(minZ, maxZ);
}
}
@@ -0,0 +1,27 @@
package ac.grim.grimac.utils.payload;
import com.github.retrooper.packetevents.netty.buffer.ByteBufHelper;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import org.jetbrains.annotations.ApiStatus.NonExtendable;
import org.jetbrains.annotations.ApiStatus.OverrideOnly;
import org.jetbrains.annotations.NotNull;
import static com.github.retrooper.packetevents.netty.buffer.UnpooledByteBufAllocationHelper.buffer;
import static com.github.retrooper.packetevents.netty.buffer.UnpooledByteBufAllocationHelper.wrappedBuffer;
import static com.github.retrooper.packetevents.wrapper.PacketWrapper.createUniversalPacketWrapper;
public interface Payload {
@NonExtendable
default byte[] write() {
Object buffer = buffer();
write(createUniversalPacketWrapper(buffer));
return ByteBufHelper.array(buffer);
}
@OverrideOnly
void write(PacketWrapper<?> wrapper);
static @NotNull PacketWrapper<?> wrapper(byte[] data) {
return createUniversalPacketWrapper(wrappedBuffer(data));
}
}
@@ -1,13 +1,20 @@
package ac.grim.grimac.utils.payload;
import com.github.retrooper.packetevents.protocol.item.ItemStack;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientEditBook;
import org.jetbrains.annotations.NotNull;
/**
* Payload wrapper for {@code MC|BEdit} and {@code MC|BSign}
* Payload wrapper for serverbound {@code MC|BEdit} and {@code MC|BSign}, replaced by the {@link WrapperPlayClientEditBook EDIT_BOOK} packet in 1.13
*/
public record PayloadBookEdit(ItemStack itemStack) {
public static final PayloadCodec<PayloadBookEdit> CODEC = new PayloadCodec<>(
wrapper -> new PayloadBookEdit(wrapper.readItemStack()),
(wrapper, payload) -> wrapper.writeItemStack(payload.itemStack)
);
public record PayloadBookEdit(@NotNull ItemStack itemStack) implements Payload {
public PayloadBookEdit(byte[] data) {
this(Payload.wrapper(data).readItemStack());
}
@Override
public void write(PacketWrapper<?> wrapper) {
wrapper.writeItemStack(itemStack);
}
}
@@ -1,39 +0,0 @@
package ac.grim.grimac.utils.payload;
import com.github.retrooper.packetevents.netty.buffer.ByteBufHelper;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import java.util.function.BiConsumer;
import java.util.function.Function;
import static com.github.retrooper.packetevents.netty.buffer.UnpooledByteBufAllocationHelper.buffer;
import static com.github.retrooper.packetevents.netty.buffer.UnpooledByteBufAllocationHelper.copiedBuffer;
import static com.github.retrooper.packetevents.wrapper.PacketWrapper.createUniversalPacketWrapper;
public final class PayloadCodec<P> {
private final Function<PacketWrapper<?>, P> reader;
private final BiConsumer<PacketWrapper<?>, P> writer;
@Contract(pure = true)
public PayloadCodec(Function<PacketWrapper<?>, P> reader, BiConsumer<PacketWrapper<?>, P> writer) {
this.reader = reader;
this.writer = writer;
}
public P read(byte[] data) {
Object buffer = copiedBuffer(data);
P payload = reader.apply(createUniversalPacketWrapper(buffer));
ByteBufHelper.release(buffer);
return payload;
}
public byte @NotNull [] write(P payload) {
Object buffer = buffer();
writer.accept(createUniversalPacketWrapper(buffer), payload);
byte[] bytes = ByteBufHelper.array(buffer);
ByteBufHelper.release(buffer);
return bytes;
}
}
@@ -1,10 +1,19 @@
package ac.grim.grimac.utils.payload;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientNameItem;
import org.jetbrains.annotations.NotNull;
public record PayloadItemName(@NotNull String itemName) {
public static final PayloadCodec<PayloadItemName> CODEC = new PayloadCodec<>(
wrapper -> new PayloadItemName(wrapper.readString()),
(wrapper, payload) -> wrapper.writeString(payload.itemName)
);
/**
* Payload wrapper for serverbound {@code MC|ItemName}, replaced by the {@link WrapperPlayClientNameItem NAME_ITEM} packet in 1.13
*/
public record PayloadItemName(@NotNull String itemName) implements Payload {
public PayloadItemName(byte[] data) {
this(Payload.wrapper(data).readString());
}
@Override
public void write(PacketWrapper<?> wrapper) {
wrapper.writeString(itemName);
}
}
@@ -1,19 +1,34 @@
package ac.grim.grimac.utils.via;
package ac.grim.grimac.utils.viaversion;
import ac.grim.grimac.GrimAPI;
import ac.grim.grimac.checks.impl.chat.ChatB;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.LogUtil;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.protocols.v1_10to1_11.Protocol1_10To1_11;
import com.viaversion.viaversion.protocols.v1_9_1to1_9_3.packet.ServerboundPackets1_9_3;
import lombok.experimental.UtilityClass;
import java.util.UUID;
public class ViaHooks {
public static void injectVia() {
final var protocol = Via.getManager().getProtocolManager().getProtocol(Protocol1_10To1_11.class);
@UtilityClass
class ViaVersionHooks {
// this method's only purpose is getting <clinit> called
static void load() {}
static {
inject1_11ChatHook();
}
private static void inject1_11ChatHook() {
if (PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_11)) {
return; // not needed
}
final Protocol1_10To1_11 protocol = Via.getManager().getProtocolManager().getProtocol(Protocol1_10To1_11.class);
if (protocol == null) {
LogUtil.warn("Failed to inject ViaVersion message hook for 1.11+ clients: Protocol1_10To1_11 isn't registered!");
return;
@@ -1,6 +1,7 @@
package ac.grim.grimac.utils.reflection;
package ac.grim.grimac.utils.viaversion;
import ac.grim.grimac.utils.anticheat.LogUtil;
import ac.grim.grimac.utils.reflection.ReflectionUtils;
import lombok.experimental.UtilityClass;
@UtilityClass
@@ -12,4 +13,8 @@ public class ViaVersionUtil {
LogUtil.error("Using unsupported ViaVersion 4.0 API, update ViaVersion to 5.0");
}
}
public static void injectHooks() {
if (isAvailable) ViaVersionHooks.load();
}
}
+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
+229
View File
@@ -0,0 +1,229 @@
# Configurația principală GrimAC
# Cod sursă: https://github.com/MWHunter/Grim
# Drepturi de autor 2025 DefineOutside și colaboratorii, licențiat sub GPLv3.
# Binarele modificate sau pluginurile cu cod Grim copiat trebuie să fie private,
# sau cu întregul cod sursă disponibil cumpărătorilor fără costuri suplimentare.
alerts:
# Pe lângă transmiterea alertelor către jucători, ar trebui să fie trimise și în consolă?
print-to-console: true
# Aceasta controlează dacă/și cum sunt partajate alertele între serverele conectate la proxy.
# Trebuie să ai 'bungee-plugin-message-channel' activat în configurația Velocity dacă Velocity este folosit.
proxy:
# Ar trebui ca alertele să fie trimise către alte servere conectate la proxy?
send: false
# Ar trebui ca alertele primite de la alte servere să fie anunțate pe acest server?
receive: false
verbose:
print-to-console: false
check-for-updates: true
client-brand:
# Aceasta înseamnă că nu va transmite operatorilor brandul clientului dacă brandul se potrivește cu regex-urile de mai jos
ignored-clients:
- "^vanilla$"
- "^fabric$"
- "^lunarclient:v\\d+\\.\\d+\\.\\d+-\\d{4}$"
- "^Feather Fabric$"
- "^labymod$"
# Grim va bloca anumite versiuni Forge care includ hackuri Reach integrate (Forge 1.18.2 până la 1.19.3).
# Setarea acestei opțiuni la false va permite acestor clienți să se conecteze la server. Dezactivează pe propriul risc.
disconnect-blacklisted-forge-versions: true
spectators:
# Ascunde toți spectatorii cu permisiunea grim.spectator indiferent dacă spectează activ sau nu
hide-regardless: false
# Va face spectatorii invizibili doar în aceste lumi, lasă gol pentru a permite în toate lumile
allowed-worlds:
- ""
# Cât timp au jucătorii până să fie deconectați pentru timeout? Implicit = 60 secunde
max-transaction-time: 60
# Activează pentru a opri Grim din a anula pongs primite de la jucători.
# Poate îmbunătăți compatibilitatea cu alte anticheaturi. Poate cauza probleme cu limitatoarele de pachete.
# Nu activa dacă nu ești sigur ce faci.
disable-pong-cancelling: false
# Ar trebui să fie anulat pachetul de mișcare duplicat?
# Mojang a reparat această problemă în 1.21. A fost încercarea lor de a rezolva „bucket desync”. https://bugs.mojang.com/browse/MC-12363
# Această setare se aplică doar clienților 1.17-1.20.5 pe servere 1.8.
cancel-duplicate-packet: true
# Dacă să se ignore rotația în pachetele duplicate
ignore-duplicate-packet-rotation: false
Simulation:
# Cu cât ar trebui să înmulțim avantajul total atunci când jucătorul este legitim
# Așa arată configurația implicită (axa X = secunde, axa Y = 1/1000 bloc): https://www.desmos.com/calculator/d4ufgxrxer
setback-decay-multiplier: 0.999
# Cât de mare trebuie să fie abaterea de la mișcarea jucătorului pentru a crea o încălcare?
# Măsurat în blocuri față de mișcarea posibilă
# Luăm în considerare Optifine schimbând tabelele trig, dar scăderea la 0.001 va reduce FastMath
# și ar putea marca anticheatul dacă această compensație nu funcționează...
threshold: 0.001
# Cât de mare trebuie să fie încălcarea într-un tick pentru a da setback imediat?
# -1 pentru a dezactiva
immediate-setback-threshold: 0.1
# Cât de mare avantaj acumulat înainte să începem setback?
# -1 pentru a dezactiva
max-advantage: 1
# După 50 de secunde cu setările implicite, jucătorul va trece de la 4 blocuri -> 1 bloc avantaj
# Aceasta previne acumularea prea multor încălcări fără șansă de reset
# Grafic implicit avantaj (axa X = secunde, axa Y = 1/1000 bloc): https://www.desmos.com/calculator/4lovswdarj
max-ceiling: 4
# Pragul de încălcare pentru setback
# 1 pentru comportamentul vechi
setback-violation-threshold: 1
# Verifică dacă un jucător a intrat într-un bloc în timpul mișcării
Phase:
setbackvl: 1 # Glitch-ul în blocuri poate permite escaladarea pereților, iar acest check e relativ stabil
decay: 0.005
AirLiquidPlace:
cancelvl: 0
FabricatedPlace:
cancelvl: 5
FarPlace:
cancelvl: 5
PositionPlace:
cancelvl: 5
RotationPlace:
cancelvl: 5
# Verificare NoSlow bazată pe predicție
# Grim ține cont de netcode-ul buguit aici... greu de fals chiar și când spamezi click dreapta și butonul offhand
# Mult mai stabil decât alte anticheaturi, dar raportează orice falsuri... am reparat multe probleme de netcode aici.
NoSlow:
# Cât de mare abatere e considerată „cheating”
# Implicit este mai mică decât alte offs
# Marchează între 0.03-0.2 constant cu NoSlow activ
threshold: 0.001
# Setback rapid la primul obiect pentru a elimina orice avantaj dat de NoSlow
setbackvl: 5
# Scade atunci când jucătorul folosește un item ȘI este încetinit de acesta
decay: 0.05
Knockback:
# Cu cât ar trebui să înmulțim avantajul total atunci când jucătorul este legitim
setback-decay-multiplier: 0.999
# Cât de mare trebuie să fie abaterea față de viteza jucătorului pentru a crea o încălcare?
# Măsurat în blocuri față de viteza posibilă
threshold: 0.001
# Cât de mare trebuie să fie încălcarea într-un tick pentru setback imediat?
# -1 pentru a dezactiva
immediate-setback-threshold: 0.1
# Cât de mare avantaj acumulat înainte să începem setback?
# -1 pentru a dezactiva
max-advantage: 1
# Previne acumularea prea multor încălcări imposibil de resetat
max-ceiling: 4
Explosion:
threshold: 0.001
setbackvl: 3
TimerA:
setbackvl: 10
# Milisecunde pe care jucătorul le poate acumula pentru folosire ulterioară când rămâne în urmă
# Poate permite bypass-uri fast use/fast heal/fast bow pe 1.8 dacă e setat prea mare, 120 ms e un echilibru bun
drift: 120
# Acest check limitează abuzul TimerA prevenind mișcarea jucătorului să rămână prea mult în urmă față de timpul real
TimerLimit:
# Ping la care check-ul începe să limiteze balanța timer, pentru a preveni abuzul.
# Poate cauza setback pentru jucători legitimi doar dacă sunt peste acest prag de ping.
# -1 pentru a dezactiva
ping-abuse-limit-threshold: 1000
NegativeTimer:
# Număr de milisecunde pierdute în timp ce se mișcă înainte de a începe să marcheze
drift: 1200
# Aceeași metodă de verificare ca TimerA, dar pentru vehicule
VehicleTimer:
# Țintă timer 1.005
setbackvl: 10
PacketOrderI:
# activează dacă jucătorii sunt marcați folosind moduri de animații 1.7
exempt-placing-while-digging: false
Reach:
# Cu cât să mărim hitboxurile? 0.0005 ar trebui să detecteze reach 3.0005+
#
# Există o extindere forțată de 0.03 cu clienți 1.9-1.18.1 (nu 1.18.2), sau unele combinații client/server datorită
# schimbărilor și limitărilor protocolului. Acest check este cel mai puternic cu clienți 1.7/1.8 pe servere 1.8.
threshold: 0.0005
# Ar trebui să anulăm loviturile imposibile?
# Lovituri de 3.00-3.03 pot trece dar tot vor fi marcate, din cauza limitărilor de ordine a pachetelor
block-impossible-hits: true
# Va trimite un pachet suplimentar la sfârșitul fiecărui tick pentru a crește șansele de a prinde cheat-uri
# Injectează în lista de conexiuni a serverului pentru a trimite un pachet final chiar înainte ca serverul să facă flush
# Activarea va crește traficul pentru toți jucătorii
# Nu va scădea performanța serverului
# Activarea va prinde mai mulți cheateri.
# Lăsarea dezactivată va prinde totuși cheateri și nu va cauza false positive
# Dacă nu ești server PvP 1.8, acest pachet suplimentar nu este recomandat
enable-post-packet: false
exploit:
# Poți obține viteze mari sărind sprint cu elytra, aceasta previne exploitul dacă e setat pe false
# Mojang a stricat netcode-ul făcând startul elytra client-side și finalul server-side
# Elytra are fricțiune orizontală 0.99, așa că adăugând constant 0.2 viteză rezultă în viteze foarte mari.
allow-sprint-jumping-when-using-elytra: true
# Această opțiune reduce efectul ghostblocks prin resincronizarea jucătorului când se întâmplă
allow-building-on-ghostblocks: true
# Această setare, influențată de booleanul de mai sus, definește distanța verificată pentru ghostblocks
# Interval valid: 2 până la 4
distance-to-check-if-ghostblocks: 2
# Activează logarea pluginurilor care s-au injectat în netty la intrare pentru debugging de compatibilitate
debug-pipeline-on-join: false
# Activează verificări experimentale
experimental-checks: false
reset-item-usage-on-item-update: true
reset-item-usage-on-attack: true
reset-item-usage-on-slot-change: true
# Grim uneori anulează pachete ilegale precum timer, după X pachete anulate într-o secundă, când să
# deconectăm jucătorul? Este necesar deoarece unele limitatoare de pachete nu numără pachetele anulate de Grim.
packet-spam-threshold: 100
# Activează pentru a afișa stacktrace când un jucător e deconectat din cauza packet-spam-threshold
debug-packet-cancel: false
# Grim poate aplica ca un jucător scos din starea de zbor să nu aibă mai mult de X ms ping
# Deoarece Grim nu verifică în prezent jucătorii care zboară
# Pentru dezactivare, folosește -1
max-ping-out-of-flying: 1000
# Ping maxim când boost-ul cu artificii este eliminat de la jucător.
# Previne jucătorii cu latență mare să folosească un boost cu artificii la infinit cu elytra.
max-ping-firework-boost: 1000
history:
enabled: true
# Câte înregistrări să fie afișate pe pagină cu /grim history <player>
entries-per-page: 15
# 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:
# 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 de conectare la baza de date
host: localhost
port: 3306
database: grim
username: root
password: ""
config-version: 9
+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
+1 -1
View File
@@ -13,7 +13,7 @@ plugins {
dependencies {
minecraft("com.mojang:minecraft:$minecraft_version")
mappings("net.fabricmc:yarn:$yarn_mappings")
mappings(loom.officialMojangMappings())
modImplementation(fabricApi.module("fabric-lifecycle-events-v1", fabric_version))
modImplementation("me.lucko:fabric-permissions-api:0.3.1")
+1 -1
View File
@@ -1,6 +1,6 @@
dependencies {
minecraft("com.mojang:minecraft:1.16.1")
mappings("net.fabricmc:yarn:1.16.1+build.21:v2")
mappings(loom.officialMojangMappings())
modImplementation(fabricApi.module("fabric-lifecycle-events-v1", "0.42.0+1.16"))
modImplementation("me.lucko:fabric-permissions-api:0.1-SNAPSHOT")
@@ -3,19 +3,19 @@ package ac.grim.grimac.platform.fabric.mc1161;
import ac.grim.grimac.platform.api.sender.Sender;
import ac.grim.grimac.platform.fabric.AbstractFabricPlatformServer;
import ac.grim.grimac.platform.fabric.GrimACFabricLoaderPlugin;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.commands.CommandSourceStack;
public class Fabric1140PlatformServer extends AbstractFabricPlatformServer {
@Override
public void dispatchCommand(Sender sender, String command) {
ServerCommandSource commandSource = GrimACFabricLoaderPlugin.LOADER.getFabricSenderFactory().reverse(sender);
GrimACFabricLoaderPlugin.FABRIC_SERVER.getCommandManager().execute(commandSource, command);
CommandSourceStack commandSource = GrimACFabricLoaderPlugin.LOADER.getFabricSenderFactory().reverse(sender);
GrimACFabricLoaderPlugin.FABRIC_SERVER.getCommands().performCommand(commandSource, command);
}
// TODO (Cross-platform) implement proper bukkit equivalent for getting TPS over time
@Override
public double getTPS() {
return Math.min(1000.0 / GrimACFabricLoaderPlugin.FABRIC_SERVER.getTickTime(), 20.0);
return Math.min(1000.0 / GrimACFabricLoaderPlugin.FABRIC_SERVER.getAverageTickTime(), 20.0);
}
}
@@ -1,6 +1,5 @@
package ac.grim.grimac.platform.fabric.mc1161.command;
import ac.grim.grimac.GrimAPI;
import ac.grim.grimac.platform.api.command.PlayerSelector;
import ac.grim.grimac.platform.api.sender.Sender;
@@ -24,7 +23,7 @@ public class Fabric1161PlayerSelectorAdapter implements PlayerSelector {
@Override
public Sender getSinglePlayer() {
return ((FabricSenderFactory) GrimAPI.INSTANCE.getSenderFactory()).map(fabricSelector.single().getCommandSource());
return ((FabricSenderFactory) GrimAPI.INSTANCE.getSenderFactory()).map(fabricSelector.single().createCommandSourceStack());
}
@Override
@@ -3,11 +3,10 @@ package ac.grim.grimac.platform.fabric.mc1161.entity;
import ac.grim.grimac.platform.fabric.entity.AbstractFabricGrimEntity;
import ac.grim.grimac.platform.fabric.utils.thread.FabricFutureUtil;
import ac.grim.grimac.utils.math.Location;
import net.minecraft.entity.Entity;
import net.minecraft.entity.LivingEntity;
import net.minecraft.server.world.ServerWorld;
import java.util.concurrent.CompletableFuture;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
public class Fabric1161GrimEntity extends AbstractFabricGrimEntity {
@@ -18,8 +17,8 @@ public class Fabric1161GrimEntity extends AbstractFabricGrimEntity {
@Override
public CompletableFuture<Boolean> teleportAsync(Location location) {
return FabricFutureUtil.supplySync(() -> {
if (entity.getEntityWorld() instanceof ServerWorld) {
entity.teleport(
if (entity.getCommandSenderWorld() instanceof ServerLevel) {
entity.teleportToWithTicket(
location.getX(),
location.getY(),
location.getZ()
@@ -32,6 +31,6 @@ public class Fabric1161GrimEntity extends AbstractFabricGrimEntity {
@Override
public boolean isDead() {
return entity instanceof LivingEntity living ? living.isDead() : entity.removed;
return entity instanceof LivingEntity living ? living.isDeadOrDying() : entity.removed;
}
}
@@ -1,16 +1,16 @@
package ac.grim.grimac.platform.fabric.mc1161.player;
import ac.grim.grimac.platform.fabric.player.AbstractFabricPlatformInventory;
import net.minecraft.screen.PlayerScreenHandler;
import net.minecraft.screen.ScreenHandler;
import net.minecraft.screen.ScreenHandlerType;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.util.Identifier;
import net.minecraft.util.registry.Registry;
import net.minecraft.core.Registry;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.inventory.InventoryMenu;
import net.minecraft.world.inventory.MenuType;
import org.jetbrains.annotations.Nullable;
public class Fabric1161PlatformInventory extends AbstractFabricPlatformInventory {
public Fabric1161PlatformInventory(ServerPlayerEntity player) {
public Fabric1161PlatformInventory(ServerPlayer player) {
super(player);
}
@@ -23,13 +23,13 @@ public class Fabric1161PlatformInventory extends AbstractFabricPlatformInventory
// And is slated to be replaced by packet based behaviour, this should do for now
@Override
public String getOpenInventoryKey() {
ScreenHandler handler = fabricPlayer.currentScreenHandler;
ScreenHandlerType<?> type = getSafeType(handler);
AbstractContainerMenu handler = fabricPlayer.containerMenu;
MenuType<?> type = getSafeType(handler);
// Handle null types (player crafting and creative)
if (type == null) {
// 4x4 CRAFTING -> CRAFTING
if (handler instanceof PlayerScreenHandler) {
if (handler instanceof InventoryMenu) {
return "CRAFTING";
// Not sure if creative mode check here is correct
} else if (this.isPlayerCreative()) {
@@ -39,23 +39,23 @@ public class Fabric1161PlatformInventory extends AbstractFabricPlatformInventory
// should we handle crafters here also??
// CRAFTING -> CRAFTING
if (type == ScreenHandlerType.CRAFTING) {
if (type == MenuType.CRAFTING) {
return "CRAFTING";
// PLAYER -> PLAYER
} else if (type == ScreenHandlerType.GENERIC_9X4) {
} else if (type == MenuType.GENERIC_9x4) {
return "PLAYER";
// CHEST, ENDER_CHEST, or BARREL -> CHEST
} else if (type == ScreenHandlerType.GENERIC_9X3) {
} else if (type == MenuType.GENERIC_9x3) {
return "CHEST";
// DISPENSER, DROPPER -> DISPENSER
} else if (type == ScreenHandlerType.GENERIC_3X3) {
} else if (type == MenuType.GENERIC_3x3) {
return "DISPENSER";
} else {
// Registry handles:
// SHULKER_BOX -> SHULKER_BOX
// CRAFTIING -> CRAFTING
Identifier registryKey = this.getScreenID(type);
ResourceLocation registryKey = this.getScreenID(type);
if (registryKey != null) {
return registryKey.getPath();
}
@@ -64,15 +64,15 @@ public class Fabric1161PlatformInventory extends AbstractFabricPlatformInventory
}
}
protected Identifier getScreenID(ScreenHandlerType<?> type) {
return Registry.SCREEN_HANDLER.getId(type);
protected ResourceLocation getScreenID(MenuType<?> type) {
return Registry.MENU.getKey(type);
}
protected boolean isPlayerCreative() {
return fabricPlayer.isCreative();
}
protected @Nullable ScreenHandlerType<?> getSafeType(ScreenHandler handler) {
protected @Nullable MenuType<?> getSafeType(AbstractContainerMenu handler) {
try {
return handler.getType();
} catch (UnsupportedOperationException e) {
@@ -5,36 +5,25 @@ import ac.grim.grimac.platform.fabric.GrimACFabricLoaderPlugin;
import ac.grim.grimac.platform.fabric.player.AbstractFabricPlatformPlayer;
import ac.grim.grimac.platform.fabric.utils.thread.FabricFutureUtil;
import ac.grim.grimac.utils.math.Location;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.world.ServerWorld;
import java.util.concurrent.CompletableFuture;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
public class Fabric1161PlatformPlayer extends AbstractFabricPlatformPlayer {
public Fabric1161PlatformPlayer(ServerPlayerEntity player) {
public Fabric1161PlatformPlayer(ServerPlayer player) {
super(player);
}
@Override
public boolean hasPermission(String permission) {
return GrimACFabricLoaderPlugin.LOADER.getFabricSenderFactory().map(entity.getCommandSource()).hasPermission(permission);
}
@Override
public boolean hasPermission(String s, boolean defaultIfUnset) {
return GrimACFabricLoaderPlugin.LOADER.getFabricSenderFactory().map(entity.getCommandSource()).hasPermission(s, defaultIfUnset);
}
@Override
public Sender getSender() {
return GrimACFabricLoaderPlugin.LOADER.getFabricSenderFactory().map(entity.getCommandSource());
return GrimACFabricLoaderPlugin.LOADER.getFabricSenderFactory().map(entity.createCommandSourceStack());
}
@Override
public CompletableFuture<Boolean> teleportAsync(Location location) {
return FabricFutureUtil.supplySync(() -> {
fabricPlayer.teleport(
(ServerWorld) location.getWorld(),
fabricPlayer.teleportTo(
(ServerLevel) location.getWorld(),
location.getX(),
location.getY(),
location.getZ(),
@@ -9,19 +9,18 @@ import io.github.retrooper.packetevents.adventure.serializer.gson.GsonComponentS
import io.netty.buffer.ByteBuf;
import io.netty.buffer.PooledByteBufAllocator;
import net.kyori.adventure.text.Component;
import net.minecraft.network.PacketByteBuf;
import net.minecraft.text.Text;
import net.minecraft.network.FriendlyByteBuf;
public class Fabric1140ConversionUtil implements IFabricConversionUtil {
public ItemStack fromFabricItemStack(net.minecraft.item.ItemStack fabricStack) {
public ItemStack fromFabricItemStack(net.minecraft.world.item.ItemStack fabricStack) {
if (fabricStack.isEmpty()) {
return ItemStack.EMPTY;
}
ByteBuf buffer = PooledByteBufAllocator.DEFAULT.buffer();
try {
PacketByteBuf packetByteBuf = new PacketByteBuf(buffer);
packetByteBuf.writeItemStack(fabricStack);
FriendlyByteBuf packetByteBuf = new FriendlyByteBuf(buffer);
packetByteBuf.writeItem(fabricStack);
PacketWrapper<?> wrapper = PacketWrapper.createUniversalPacketWrapper(buffer);
return wrapper.readItemStack();
} catch (Exception e) {
@@ -32,7 +31,7 @@ public class Fabric1140ConversionUtil implements IFabricConversionUtil {
}
}
public Text toNativeText(Component component) {
return Text.Serializer.fromJson(GsonComponentSerializer.gson().serializeToTree(component));
public net.minecraft.network.chat.Component toNativeText(Component component) {
return net.minecraft.network.chat.Component.Serializer.fromJson(GsonComponentSerializer.gson().serializeToTree(component));
}
}
@@ -1,18 +1,18 @@
package ac.grim.grimac.platform.fabric.mc1161.util.convert;
import ac.grim.grimac.platform.fabric.utils.message.IFabricMessageUtil;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.text.LiteralText;
import net.minecraft.text.Text;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.TextComponent;
public class Fabric1161MessageUtil implements IFabricMessageUtil {
@Override
public Text textLiteral(String message) {
return new LiteralText(message);
public Component textLiteral(String message) {
return new TextComponent(message);
}
@Override
public void sendMessage(ServerCommandSource target, Text message, boolean overlay) {
target.sendFeedback(message, overlay);
public void sendMessage(CommandSourceStack target, Component message, boolean overlay) {
target.sendSuccess(message, overlay);
}
}
@@ -1,3 +1,3 @@
accessWidener v2 named
accessible field net/minecraft/server/world/ServerWorld worldProperties Lnet/minecraft/world/level/ServerWorldProperties;
accessible field net/minecraft/server/command/ServerCommandSource output Lnet/minecraft/server/command/CommandOutput;
accessible field net/minecraft/server/level/ServerLevel serverLevelData Lnet/minecraft/world/level/storage/ServerLevelData;
accessible field net/minecraft/commands/CommandSourceStack source Lnet/minecraft/commands/CommandSource;
+1 -1
View File
@@ -1,6 +1,6 @@
dependencies {
minecraft("com.mojang:minecraft:1.17.1")
mappings("net.fabricmc:yarn:1.17.1+build.65:v2")
mappings(loom.officialMojangMappings())
compileOnly(project(":fabric:mc1161", configuration = "namedElements"))
modImplementation(fabricApi.module("fabric-lifecycle-events-v1", "0.46.1+1.17"))
@@ -7,12 +7,10 @@ import org.jetbrains.annotations.Nullable;
import java.util.Optional;
public class Fabric1171PlatformServer extends Fabric1140PlatformServer {
@Override @Nullable
public GameProfile getProfileByName(String name) {
Optional<GameProfile> gameProfile = GrimACFabricLoaderPlugin.FABRIC_SERVER.getUserCache().findByName(name);
@Override
public @Nullable GameProfile getProfileByName(String name) {
Optional<GameProfile> gameProfile = GrimACFabricLoaderPlugin.FABRIC_SERVER.getProfileCache().get(name);
return gameProfile.orElse(null);
}
}
@@ -1,9 +1,8 @@
package ac.grim.grimac.platform.fabric.mc1171.entity;
import ac.grim.grimac.platform.fabric.mc1161.entity.Fabric1161GrimEntity;
import net.minecraft.entity.Entity;
import net.minecraft.entity.LivingEntity;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
public class Fabric1170GrimEntity extends Fabric1161GrimEntity {
@@ -13,8 +12,6 @@ public class Fabric1170GrimEntity extends Fabric1161GrimEntity {
@Override
public boolean isDead() {
if (this.entity instanceof LivingEntity)
return ((LivingEntity) entity).isDead();
return this.entity.isRemoved();
return this.entity instanceof LivingEntity living ? living.isDeadOrDying() : this.entity.isRemoved();
}
}
@@ -3,16 +3,15 @@ package ac.grim.grimac.platform.fabric.mc1171.player;
import ac.grim.grimac.platform.fabric.mc1161.player.Fabric1161PlatformPlayer;
import ac.grim.grimac.platform.fabric.utils.convert.FabricConversionUtil;
import com.github.retrooper.packetevents.protocol.player.GameMode;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.level.ServerPlayer;
public class Fabric1170PlatformPlayer extends Fabric1161PlatformPlayer {
public Fabric1170PlatformPlayer(ServerPlayerEntity player) {
public Fabric1170PlatformPlayer(ServerPlayer player) {
super(player);
}
@Override
public void setGameMode(GameMode gameMode) {
fabricPlayer.changeGameMode(FabricConversionUtil.toFabricGameMode(gameMode));
fabricPlayer.setGameMode(FabricConversionUtil.toFabricGameMode(gameMode));
}
}
@@ -1,3 +1,3 @@
accessWidener v2 named
accessible field net/minecraft/server/world/ServerWorld worldProperties Lnet/minecraft/world/level/ServerWorldProperties;
accessible field net/minecraft/server/command/ServerCommandSource output Lnet/minecraft/server/command/CommandOutput;
accessible field net/minecraft/server/level/ServerLevel serverLevelData Lnet/minecraft/world/level/storage/ServerLevelData;
accessible field net/minecraft/commands/CommandSourceStack source Lnet/minecraft/commands/CommandSource;
+1 -1
View File
@@ -1,6 +1,6 @@
dependencies {
minecraft("com.mojang:minecraft:1.19.4")
mappings("net.fabricmc:yarn:1.19.4+build.2:v2")
mappings(loom.officialMojangMappings())
compileOnly(project(":fabric:mc1161", configuration = "namedElements"))
compileOnly(project(":fabric:mc1171", configuration = "namedElements"))
@@ -3,12 +3,12 @@ package ac.grim.grimac.platform.fabric.mc1194;
import ac.grim.grimac.platform.api.sender.Sender;
import ac.grim.grimac.platform.fabric.GrimACFabricLoaderPlugin;
import ac.grim.grimac.platform.fabric.mc1171.Fabric1171PlatformServer;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.commands.CommandSourceStack;
public class Fabric1190PlatformServer extends Fabric1171PlatformServer {
@Override
public void dispatchCommand(Sender sender, String command) {
ServerCommandSource commandSource = GrimACFabricLoaderPlugin.LOADER.getFabricSenderFactory().reverse(sender);
GrimACFabricLoaderPlugin.FABRIC_SERVER.getCommandManager().executeWithPrefix(commandSource, command);
CommandSourceStack commandSource = GrimACFabricLoaderPlugin.LOADER.getFabricSenderFactory().reverse(sender);
GrimACFabricLoaderPlugin.FABRIC_SERVER.getCommands().performPrefixedCommand(commandSource, command);
}
}
@@ -1,11 +1,11 @@
package ac.grim.grimac.platform.fabric.mc1194.convert;
import ac.grim.grimac.platform.fabric.mc1161.util.convert.Fabric1161MessageUtil;
import net.minecraft.text.Text;
import net.minecraft.network.chat.Component;
public class Fabric1190MessageUtil extends Fabric1161MessageUtil {
@Override
public Text textLiteral(String message) {
return Text.literal(message);
public Component textLiteral(String message) {
return Component.literal(message);
}
}
@@ -3,12 +3,11 @@ package ac.grim.grimac.platform.fabric.mc1194.entity;
import ac.grim.grimac.platform.fabric.mc1171.entity.Fabric1170GrimEntity;
import ac.grim.grimac.platform.fabric.utils.thread.FabricFutureUtil;
import ac.grim.grimac.utils.math.Location;
import net.minecraft.entity.Entity;
import net.minecraft.network.packet.s2c.play.PositionFlag;
import net.minecraft.server.world.ServerWorld;
import java.util.EnumSet;
import java.util.concurrent.CompletableFuture;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.RelativeMovement;
public class Fabric1194GrimEntity extends Fabric1170GrimEntity {
@@ -19,13 +18,13 @@ public class Fabric1194GrimEntity extends Fabric1170GrimEntity {
@Override
public CompletableFuture<Boolean> teleportAsync(Location location) {
return FabricFutureUtil.supplySync(() -> {
if (entity.getWorld() instanceof ServerWorld) {
entity.teleport(
(ServerWorld) location.getWorld(),
if (entity.getLevel() instanceof ServerLevel) {
entity.teleportTo(
(ServerLevel) location.getWorld(),
location.getX(),
location.getY(),
location.getZ(),
EnumSet.noneOf(PositionFlag.class), // todo change to match paper? Do they do this?
EnumSet.noneOf(RelativeMovement.class), // todo change to match paper? Do they do this?
location.getYaw(),
location.getPitch()
@@ -1,18 +1,18 @@
package ac.grim.grimac.platform.fabric.mc1194.player;
import ac.grim.grimac.platform.fabric.mc1161.player.Fabric1161PlatformInventory;
import net.minecraft.registry.Registries;
import net.minecraft.screen.ScreenHandlerType;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.util.Identifier;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.inventory.MenuType;
public class Fabric1193PlatformInventory extends Fabric1161PlatformInventory {
public Fabric1193PlatformInventory(ServerPlayerEntity player) {
public Fabric1193PlatformInventory(ServerPlayer player) {
super(player);
}
@Override
protected Identifier getScreenID(ScreenHandlerType<?> type) {
return Registries.SCREEN_HANDLER.getId(type);
protected ResourceLocation getScreenID(MenuType<?> type) {
return BuiltInRegistries.MENU.getKey(type);
}
}
@@ -1,3 +1,3 @@
accessWidener v2 named
accessible field net/minecraft/server/world/ServerWorld worldProperties Lnet/minecraft/world/level/ServerWorldProperties;
accessible field net/minecraft/server/command/ServerCommandSource output Lnet/minecraft/server/command/CommandOutput;
accessible field net/minecraft/server/level/ServerLevel serverLevelData Lnet/minecraft/world/level/storage/ServerLevelData;
accessible field net/minecraft/commands/CommandSourceStack source Lnet/minecraft/commands/CommandSource;
+1 -1
View File
@@ -1,6 +1,6 @@
dependencies {
minecraft("com.mojang:minecraft:1.20.5")
mappings("net.fabricmc:yarn:1.20.5+build.1:v2")
mappings(loom.officialMojangMappings())
compileOnly(project(":fabric:mc1161", configuration = "namedElements"))
compileOnly(project(":fabric:mc1171", configuration = "namedElements"))
compileOnly(project(":fabric:mc1194", configuration = "namedElements"))
@@ -3,20 +3,20 @@ package ac.grim.grimac.platform.fabric.mc1205;
import ac.grim.grimac.platform.api.sender.Sender;
import ac.grim.grimac.platform.fabric.GrimACFabricLoaderPlugin;
import ac.grim.grimac.platform.fabric.mc1194.Fabric1190PlatformServer;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.commands.CommandSourceStack;
public class Fabric1203PlatformServer extends Fabric1190PlatformServer {
// TODO (Cross-platform) implement proper bukkit equivalent for getting TPS over time
@Override
public double getTPS() {
return Math.min(1000.0 / GrimACFabricLoaderPlugin.FABRIC_SERVER.getAverageTickTime(), GrimACFabricLoaderPlugin.FABRIC_SERVER.getTickManager().getTickRate());
return Math.min(1000.0 / GrimACFabricLoaderPlugin.FABRIC_SERVER.getCurrentSmoothedTickTime(), GrimACFabricLoaderPlugin.FABRIC_SERVER.tickRateManager().tickrate());
}
// Return type changed from int -> void in 1.20.3
@Override
public void dispatchCommand(Sender sender, String command) {
ServerCommandSource commandSource = GrimACFabricLoaderPlugin.LOADER.getFabricSenderFactory().reverse(sender);
GrimACFabricLoaderPlugin.FABRIC_SERVER.getCommandManager().executeWithPrefix(commandSource, command);
CommandSourceStack commandSource = GrimACFabricLoaderPlugin.LOADER.getFabricSenderFactory().reverse(sender);
GrimACFabricLoaderPlugin.FABRIC_SERVER.getCommands().performPrefixedCommand(commandSource, command);
}
}
@@ -1,12 +1,12 @@
package ac.grim.grimac.platform.fabric.mc1205.convert;
import ac.grim.grimac.platform.fabric.mc1194.convert.Fabric1190MessageUtil;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.text.Text;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.network.chat.Component;
public class Fabric1200MessageUtil extends Fabric1190MessageUtil {
@Override
public void sendMessage(ServerCommandSource target, Text message, boolean overlay) {
target.sendFeedback(() -> message, overlay);
public void sendMessage(CommandSourceStack target, Component message, boolean overlay) {
target.sendSuccess(() -> message, overlay);
}
}
@@ -10,12 +10,11 @@ import io.github.retrooper.packetevents.adventure.serializer.gson.GsonComponentS
import io.netty.buffer.ByteBuf;
import io.netty.buffer.PooledByteBufAllocator;
import net.kyori.adventure.text.Component;
import net.minecraft.network.RegistryByteBuf;
import net.minecraft.registry.DynamicRegistryManager;
import net.minecraft.text.Text;
import net.minecraft.core.RegistryAccess;
import net.minecraft.network.RegistryFriendlyByteBuf;
public class Fabric1205ConversionUtil implements IFabricConversionUtil {
public ItemStack fromFabricItemStack(net.minecraft.item.ItemStack fabricStack) {
public ItemStack fromFabricItemStack(net.minecraft.world.item.ItemStack fabricStack) {
if (fabricStack.isEmpty()) {
return ItemStack.EMPTY;
}
@@ -24,13 +23,13 @@ public class Fabric1205ConversionUtil implements IFabricConversionUtil {
ByteBuf buffer = PooledByteBufAllocator.DEFAULT.buffer();
try {
// Obtain the DynamicRegistryManager (you need to provide this from your context)
DynamicRegistryManager registryManager = GrimACFabricLoaderPlugin.FABRIC_SERVER.getRegistryManager(); // Replace with actual method to get registry manager
RegistryAccess registryManager = GrimACFabricLoaderPlugin.FABRIC_SERVER.registryAccess(); // Replace with actual method to get registry manager
// Create a RegistryByteBuf
RegistryByteBuf registryByteBuf = new RegistryByteBuf(buffer, registryManager);
RegistryFriendlyByteBuf registryByteBuf = new RegistryFriendlyByteBuf(buffer, registryManager);
// Encode the ItemStack using the appropriate PacketCodec
net.minecraft.item.ItemStack.PACKET_CODEC.encode(registryByteBuf, fabricStack);
net.minecraft.world.item.ItemStack.STREAM_CODEC.encode(registryByteBuf, fabricStack);
// Create a PacketWrapper to read the ItemStack back (if needed)
PacketWrapper<?> wrapper = PacketWrapper.createUniversalPacketWrapper(buffer);
@@ -46,7 +45,7 @@ public class Fabric1205ConversionUtil implements IFabricConversionUtil {
}
// TODO proper registry support?
public Text toNativeText(Component component) {
return Text.Serialization.fromJsonTree(GsonComponentSerializer.gson().serializeToTree(component), DynamicRegistryManager.EMPTY);
public net.minecraft.network.chat.Component toNativeText(Component component) {
return net.minecraft.network.chat.Component.Serializer.fromJson(GsonComponentSerializer.gson().serializeToTree(component), RegistryAccess.EMPTY);
}
}
@@ -2,15 +2,15 @@ package ac.grim.grimac.platform.fabric.mc1205.player;
import ac.grim.grimac.platform.fabric.GrimACFabricLoaderPlugin;
import ac.grim.grimac.platform.fabric.mc1171.player.Fabric1170PlatformPlayer;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.level.ServerPlayer;
public class Fabric1202PlatformPlayer extends Fabric1170PlatformPlayer {
public Fabric1202PlatformPlayer(ServerPlayerEntity player) {
public Fabric1202PlatformPlayer(ServerPlayer player) {
super(player);
}
@Override
public void kickPlayer(String textReason) {
fabricPlayer.networkHandler.disconnect(GrimACFabricLoaderPlugin.LOADER.getFabricMessageUtils().textLiteral(textReason));
fabricPlayer.connection.disconnect(GrimACFabricLoaderPlugin.LOADER.getFabricMessageUtils().textLiteral(textReason));
}
}
@@ -1,3 +1,3 @@
accessWidener v2 named
accessible field net/minecraft/server/world/ServerWorld worldProperties Lnet/minecraft/world/level/ServerWorldProperties;
accessible field net/minecraft/server/command/ServerCommandSource output Lnet/minecraft/server/command/CommandOutput;
accessible field net/minecraft/server/level/ServerLevel serverLevelData Lnet/minecraft/world/level/storage/ServerLevelData;
accessible field net/minecraft/commands/CommandSourceStack source Lnet/minecraft/commands/CommandSource;
Loaded 100 of 135 files, more files were not shown because too many files have changed in this diff. Show more