From cc3035c1a2b84305a54feb44d2dad05efdc90955 Mon Sep 17 00:00:00 2001 From: Axionize <154778082+Axionize@users.noreply.github.com> Date: Sat, 23 May 2026 06:32:37 -0400 Subject: [PATCH 01/34] fabric: split into common / intermediary / official + 26.X aggregator MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mirrors packetevents-public's Fabric restructure. Adds Minecraft 26.X structural support alongside the existing 1.16.1→1.21.11 chain. fabric-common/ pure-Java library (currently sparse — Grim's Fabric code is almost entirely MC-typed; this module exists for cross-variant abstractions that emerge later). fabric-intermediary/ existing chain-loading aggregator. All current fabric/{src,mc1161,mc1171,mc1194,mc1205,mc12111} moved here via git mv. The accesswidener, mixins.json, and src layout are byte-identical to the prior state. fabric-official/ structural sibling for MC 26.X (Java 25 floor). Uses fabric intermediary 0.0.0 mapping because Mojang stopped publishing official mappings with 26.X and Yarn has no 26.X build. mc261 is a stub — PE and :common are intentionally NOT pulled in because their intermediary-namespaced accessWideners can't be remapped against the empty 0.0.0 mapping. Both re-attach when usable 26.X mappings land. fabric/ top-level aggregator. JiJ-nests all three siblings into the single grimac-fabric-.jar. All path references in mc*/build.gradle.kts updated from `:fabric:mcXXXX` to `:fabric-intermediary:mcXXXX`. --- fabric-common/build.gradle.kts | 28 ++++ fabric-intermediary/build.gradle.kts | 141 ++++++++++++++++ fabric-intermediary/gradle.properties | 11 ++ .../mc1161/build.gradle.kts | 0 .../mc1161/Fabric1140PlatformServer.java | 0 .../mc1161/GrimACFabric1161LoaderPlugin.java | 0 .../Fabric1161PlayerSelectorAdapter.java | 0 .../mc1161/entity/Fabric1161GrimEntity.java | 0 .../player/Fabric1161PlatformInventory.java | 0 .../player/Fabric1161PlatformPlayer.java | 0 .../convert/Fabric1140ConversionUtil.java | 0 .../util/convert/Fabric1161MessageUtil.java | 0 .../mc1161/src/main/resources/fabric.mod.json | 0 .../src/main/resources/grimac.accesswidener | 0 .../mc1171/build.gradle.kts | 2 +- .../mc1171/Fabric1171PlatformServer.java | 0 .../mc1171/GrimACFabric1170LoaderPlugin.java | 0 .../mc1171/entity/Fabric1170GrimEntity.java | 0 .../player/Fabric1170PlatformPlayer.java | 0 .../mc1171/src/main/resources/fabric.mod.json | 0 .../src/main/resources/grimac.accesswidener | 0 .../mc1194/build.gradle.kts | 4 +- .../mc1194/Fabric1190PlatformServer.java | 0 .../mc1194/GrimACFabric1190LoaderPlugin.java | 0 .../mc1194/convert/Fabric1190MessageUtil.java | 0 .../mc1194/entity/Fabric1194GrimEntity.java | 0 .../player/Fabric1193PlatformInventory.java | 0 .../mc1194/src/main/resources/fabric.mod.json | 0 .../src/main/resources/grimac.accesswidener | 0 fabric-intermediary/mc1205/build.gradle.kts | 10 ++ .../mc1205/Fabric1203PlatformServer.java | 0 .../mc1205/GrimACFabric1200LoaderPlugin.java | 0 .../mc1205/convert/Fabric1200MessageUtil.java | 0 .../convert/Fabric1205ConversionUtil.java | 0 .../player/Fabric1202PlatformPlayer.java | 0 .../mc1205/src/main/resources/fabric.mod.json | 0 .../src/main/resources/grimac.accesswidener | 0 fabric-intermediary/mc12111/build.gradle.kts | 16 ++ .../mc1216/Fabric12111PlatformServer.java | 0 .../mc1216/GrimACFabric1212LoaderPlugin.java | 0 .../Fabric1212PlayerSelectorAdapter.java | 0 .../convert/Fabric1216ConversionUtil.java | 0 .../player/Fabric1212PlatformPlayer.java | 0 .../player/Fabric1215PlatformInventory.java | 0 .../src/main/resources/fabric.mod.json | 0 .../src/main/resources/grimac.accesswidener | 0 .../fabric/AbstractFabricPlatformServer.java | 0 .../platform/fabric/FabricPlatformPlugin.java | 0 .../fabric/GrimACFabricEntryPoint.java | 0 .../fabric/GrimACFabricLoaderPlugin.java | 0 .../command/FabricPlayerSelectorParser.java | 0 .../entity/AbstractFabricGrimEntity.java | 0 .../fabric/initables/FabricBStats.java | 0 .../fabric/initables/FabricTickEndEvent.java | 0 .../manager/FabricItemResetHandler.java | 0 .../FabricMessagePlaceHolderManager.java | 0 .../FabricParserDescriptorFactory.java | 0 .../FabricPermissionRegistrationManager.java | 0 .../manager/FabricPlatformPluginManager.java | 0 .../fabric/mixins/LevelChunkMixin.java | 0 .../platform/fabric/mixins/LevelMixin.java | 0 .../fabric/mixins/PistonBaseBlockMixin.java | 0 .../fabric/mixins/ServerPlayerMixin.java | 0 .../AbstractFabricPlatformInventory.java | 0 .../player/AbstractFabricPlatformPlayer.java | 0 .../player/FabricOfflinePlatformPlayer.java | 0 .../player/FabricPlatformPlayerFactory.java | 0 .../resolver/FabricResolverRegistrar.java | 0 .../scheduler/FabricAsyncScheduler.java | 0 .../scheduler/FabricEntityScheduler.java | 0 .../FabricGlobalRegionScheduler.java | 0 .../scheduler/FabricPlatformScheduler.java | 0 .../scheduler/FabricRegionScheduler.java | 0 .../fabric/scheduler/FabricTaskHandle.java | 0 .../fabric/sender/FabricSenderFactory.java | 0 .../platform/fabric/utils/PolymerHook.java | 0 .../utils/convert/FabricConversionUtil.java | 0 .../utils/convert/IFabricConversionUtil.java | 0 .../utils/message/IFabricMessageUtil.java | 0 .../fabric/utils/message/JULoggerFactory.java | 0 .../utils/message/Log4jBackedJULogger.java | 0 .../utils/message/Slf4jBackedJULogger.java | 0 .../fabric/utils/metrics/BStatsConfig.java | 0 .../fabric/utils/metrics/CustomChart.java | 0 .../utils/metrics/JsonObjectBuilder.java | 0 .../fabric/utils/metrics/Metrics.java | 0 .../fabric/utils/metrics/MetricsBase.java | 0 .../fabric/utils/metrics/MetricsFabric.java | 0 .../fabric/utils/thread/FabricFutureUtil.java | 0 .../fabric/utils/world/LevelChunkUtil.java | 0 .../src/main/resources/fabric.mod.json | 40 +++++ .../src/main/resources/grimac.accesswidener | 0 .../src/main/resources/grimac.mixins.json | 0 fabric-official/build.gradle.kts | 128 +++++++++++++++ fabric-official/gradle.properties | 6 + fabric-official/mc261/build.gradle.kts | 6 + fabric-official/mc261/gradle.properties | 1 + .../mc261/src/main/resources/fabric.mod.json | 14 ++ .../src/main/resources/fabric.mod.json | 14 ++ fabric/build.gradle.kts | 154 +++++------------- fabric/gradle.properties | 11 +- fabric/mc1205/build.gradle.kts | 10 -- fabric/mc12111/build.gradle.kts | 16 -- fabric/src/main/resources/fabric.mod.json | 38 +---- settings.gradle.kts | 14 +- 105 files changed, 474 insertions(+), 190 deletions(-) create mode 100644 fabric-common/build.gradle.kts create mode 100644 fabric-intermediary/build.gradle.kts create mode 100644 fabric-intermediary/gradle.properties rename {fabric => fabric-intermediary}/mc1161/build.gradle.kts (100%) rename {fabric => fabric-intermediary}/mc1161/src/main/java/ac/grim/grimac/platform/fabric/mc1161/Fabric1140PlatformServer.java (100%) rename {fabric => fabric-intermediary}/mc1161/src/main/java/ac/grim/grimac/platform/fabric/mc1161/GrimACFabric1161LoaderPlugin.java (100%) rename {fabric => fabric-intermediary}/mc1161/src/main/java/ac/grim/grimac/platform/fabric/mc1161/command/Fabric1161PlayerSelectorAdapter.java (100%) rename {fabric => fabric-intermediary}/mc1161/src/main/java/ac/grim/grimac/platform/fabric/mc1161/entity/Fabric1161GrimEntity.java (100%) rename {fabric => fabric-intermediary}/mc1161/src/main/java/ac/grim/grimac/platform/fabric/mc1161/player/Fabric1161PlatformInventory.java (100%) rename {fabric => fabric-intermediary}/mc1161/src/main/java/ac/grim/grimac/platform/fabric/mc1161/player/Fabric1161PlatformPlayer.java (100%) rename {fabric => fabric-intermediary}/mc1161/src/main/java/ac/grim/grimac/platform/fabric/mc1161/util/convert/Fabric1140ConversionUtil.java (100%) rename {fabric => fabric-intermediary}/mc1161/src/main/java/ac/grim/grimac/platform/fabric/mc1161/util/convert/Fabric1161MessageUtil.java (100%) rename {fabric => fabric-intermediary}/mc1161/src/main/resources/fabric.mod.json (100%) rename {fabric => fabric-intermediary}/mc1161/src/main/resources/grimac.accesswidener (100%) rename {fabric => fabric-intermediary}/mc1171/build.gradle.kts (73%) rename {fabric => fabric-intermediary}/mc1171/src/main/java/ac/grim/grimac/platform/fabric/mc1171/Fabric1171PlatformServer.java (100%) rename {fabric => fabric-intermediary}/mc1171/src/main/java/ac/grim/grimac/platform/fabric/mc1171/GrimACFabric1170LoaderPlugin.java (100%) rename {fabric => fabric-intermediary}/mc1171/src/main/java/ac/grim/grimac/platform/fabric/mc1171/entity/Fabric1170GrimEntity.java (100%) rename {fabric => fabric-intermediary}/mc1171/src/main/java/ac/grim/grimac/platform/fabric/mc1171/player/Fabric1170PlatformPlayer.java (100%) rename {fabric => fabric-intermediary}/mc1171/src/main/resources/fabric.mod.json (100%) rename {fabric => fabric-intermediary}/mc1171/src/main/resources/grimac.accesswidener (100%) rename {fabric => fabric-intermediary}/mc1194/build.gradle.kts (63%) rename {fabric => fabric-intermediary}/mc1194/src/main/java/ac/grim/grimac/platform/fabric/mc1194/Fabric1190PlatformServer.java (100%) rename {fabric => fabric-intermediary}/mc1194/src/main/java/ac/grim/grimac/platform/fabric/mc1194/GrimACFabric1190LoaderPlugin.java (100%) rename {fabric => fabric-intermediary}/mc1194/src/main/java/ac/grim/grimac/platform/fabric/mc1194/convert/Fabric1190MessageUtil.java (100%) rename {fabric => fabric-intermediary}/mc1194/src/main/java/ac/grim/grimac/platform/fabric/mc1194/entity/Fabric1194GrimEntity.java (100%) rename {fabric => fabric-intermediary}/mc1194/src/main/java/ac/grim/grimac/platform/fabric/mc1194/player/Fabric1193PlatformInventory.java (100%) rename {fabric => fabric-intermediary}/mc1194/src/main/resources/fabric.mod.json (100%) rename {fabric => fabric-intermediary}/mc1194/src/main/resources/grimac.accesswidener (100%) create mode 100644 fabric-intermediary/mc1205/build.gradle.kts rename {fabric => fabric-intermediary}/mc1205/src/main/java/ac/grim/grimac/platform/fabric/mc1205/Fabric1203PlatformServer.java (100%) rename {fabric => fabric-intermediary}/mc1205/src/main/java/ac/grim/grimac/platform/fabric/mc1205/GrimACFabric1200LoaderPlugin.java (100%) rename {fabric => fabric-intermediary}/mc1205/src/main/java/ac/grim/grimac/platform/fabric/mc1205/convert/Fabric1200MessageUtil.java (100%) rename {fabric => fabric-intermediary}/mc1205/src/main/java/ac/grim/grimac/platform/fabric/mc1205/convert/Fabric1205ConversionUtil.java (100%) rename {fabric => fabric-intermediary}/mc1205/src/main/java/ac/grim/grimac/platform/fabric/mc1205/player/Fabric1202PlatformPlayer.java (100%) rename {fabric => fabric-intermediary}/mc1205/src/main/resources/fabric.mod.json (100%) rename {fabric => fabric-intermediary}/mc1205/src/main/resources/grimac.accesswidener (100%) create mode 100644 fabric-intermediary/mc12111/build.gradle.kts rename {fabric => fabric-intermediary}/mc12111/src/main/java/ac/grim/grimac/platform/fabric/mc1216/Fabric12111PlatformServer.java (100%) rename {fabric => fabric-intermediary}/mc12111/src/main/java/ac/grim/grimac/platform/fabric/mc1216/GrimACFabric1212LoaderPlugin.java (100%) rename {fabric => fabric-intermediary}/mc12111/src/main/java/ac/grim/grimac/platform/fabric/mc1216/command/Fabric1212PlayerSelectorAdapter.java (100%) rename {fabric => fabric-intermediary}/mc12111/src/main/java/ac/grim/grimac/platform/fabric/mc1216/convert/Fabric1216ConversionUtil.java (100%) rename {fabric => fabric-intermediary}/mc12111/src/main/java/ac/grim/grimac/platform/fabric/mc1216/player/Fabric1212PlatformPlayer.java (100%) rename {fabric => fabric-intermediary}/mc12111/src/main/java/ac/grim/grimac/platform/fabric/mc1216/player/Fabric1215PlatformInventory.java (100%) rename {fabric => fabric-intermediary}/mc12111/src/main/resources/fabric.mod.json (100%) rename {fabric => fabric-intermediary}/mc12111/src/main/resources/grimac.accesswidener (100%) rename {fabric/src/main/java => fabric-intermediary/src/main/java/ac}/ac/grim/grimac/platform/fabric/AbstractFabricPlatformServer.java (100%) rename {fabric/src/main/java => fabric-intermediary/src/main/java/ac}/ac/grim/grimac/platform/fabric/FabricPlatformPlugin.java (100%) rename {fabric/src/main/java => fabric-intermediary/src/main/java/ac}/ac/grim/grimac/platform/fabric/GrimACFabricEntryPoint.java (100%) rename {fabric/src/main/java => fabric-intermediary/src/main/java/ac}/ac/grim/grimac/platform/fabric/GrimACFabricLoaderPlugin.java (100%) rename {fabric/src/main/java => fabric-intermediary/src/main/java/ac}/ac/grim/grimac/platform/fabric/command/FabricPlayerSelectorParser.java (100%) rename {fabric/src/main/java => fabric-intermediary/src/main/java/ac}/ac/grim/grimac/platform/fabric/entity/AbstractFabricGrimEntity.java (100%) rename {fabric/src/main/java => fabric-intermediary/src/main/java/ac}/ac/grim/grimac/platform/fabric/initables/FabricBStats.java (100%) rename {fabric/src/main/java => fabric-intermediary/src/main/java/ac}/ac/grim/grimac/platform/fabric/initables/FabricTickEndEvent.java (100%) rename {fabric/src/main/java => fabric-intermediary/src/main/java/ac}/ac/grim/grimac/platform/fabric/manager/FabricItemResetHandler.java (100%) rename {fabric/src/main/java => fabric-intermediary/src/main/java/ac}/ac/grim/grimac/platform/fabric/manager/FabricMessagePlaceHolderManager.java (100%) rename {fabric/src/main/java => fabric-intermediary/src/main/java/ac}/ac/grim/grimac/platform/fabric/manager/FabricParserDescriptorFactory.java (100%) rename {fabric/src/main/java => fabric-intermediary/src/main/java/ac}/ac/grim/grimac/platform/fabric/manager/FabricPermissionRegistrationManager.java (100%) rename {fabric/src/main/java => fabric-intermediary/src/main/java/ac}/ac/grim/grimac/platform/fabric/manager/FabricPlatformPluginManager.java (100%) rename {fabric/src/main/java => fabric-intermediary/src/main/java/ac}/ac/grim/grimac/platform/fabric/mixins/LevelChunkMixin.java (100%) rename {fabric/src/main/java => fabric-intermediary/src/main/java/ac}/ac/grim/grimac/platform/fabric/mixins/LevelMixin.java (100%) rename {fabric/src/main/java => fabric-intermediary/src/main/java/ac}/ac/grim/grimac/platform/fabric/mixins/PistonBaseBlockMixin.java (100%) rename {fabric/src/main/java => fabric-intermediary/src/main/java/ac}/ac/grim/grimac/platform/fabric/mixins/ServerPlayerMixin.java (100%) rename {fabric/src/main/java => fabric-intermediary/src/main/java/ac}/ac/grim/grimac/platform/fabric/player/AbstractFabricPlatformInventory.java (100%) rename {fabric/src/main/java => fabric-intermediary/src/main/java/ac}/ac/grim/grimac/platform/fabric/player/AbstractFabricPlatformPlayer.java (100%) rename {fabric/src/main/java => fabric-intermediary/src/main/java/ac}/ac/grim/grimac/platform/fabric/player/FabricOfflinePlatformPlayer.java (100%) rename {fabric/src/main/java => fabric-intermediary/src/main/java/ac}/ac/grim/grimac/platform/fabric/player/FabricPlatformPlayerFactory.java (100%) rename {fabric/src/main/java => fabric-intermediary/src/main/java/ac}/ac/grim/grimac/platform/fabric/resolver/FabricResolverRegistrar.java (100%) rename {fabric/src/main/java => fabric-intermediary/src/main/java/ac}/ac/grim/grimac/platform/fabric/scheduler/FabricAsyncScheduler.java (100%) rename {fabric/src/main/java => fabric-intermediary/src/main/java/ac}/ac/grim/grimac/platform/fabric/scheduler/FabricEntityScheduler.java (100%) rename {fabric/src/main/java => fabric-intermediary/src/main/java/ac}/ac/grim/grimac/platform/fabric/scheduler/FabricGlobalRegionScheduler.java (100%) rename {fabric/src/main/java => fabric-intermediary/src/main/java/ac}/ac/grim/grimac/platform/fabric/scheduler/FabricPlatformScheduler.java (100%) rename {fabric/src/main/java => fabric-intermediary/src/main/java/ac}/ac/grim/grimac/platform/fabric/scheduler/FabricRegionScheduler.java (100%) rename {fabric/src/main/java => fabric-intermediary/src/main/java/ac}/ac/grim/grimac/platform/fabric/scheduler/FabricTaskHandle.java (100%) rename {fabric/src/main/java => fabric-intermediary/src/main/java/ac}/ac/grim/grimac/platform/fabric/sender/FabricSenderFactory.java (100%) rename {fabric/src/main/java => fabric-intermediary/src/main/java/ac}/ac/grim/grimac/platform/fabric/utils/PolymerHook.java (100%) rename {fabric/src/main/java => fabric-intermediary/src/main/java/ac}/ac/grim/grimac/platform/fabric/utils/convert/FabricConversionUtil.java (100%) rename {fabric/src/main/java => fabric-intermediary/src/main/java/ac}/ac/grim/grimac/platform/fabric/utils/convert/IFabricConversionUtil.java (100%) rename {fabric/src/main/java => fabric-intermediary/src/main/java/ac}/ac/grim/grimac/platform/fabric/utils/message/IFabricMessageUtil.java (100%) rename {fabric/src/main/java => fabric-intermediary/src/main/java/ac}/ac/grim/grimac/platform/fabric/utils/message/JULoggerFactory.java (100%) rename {fabric/src/main/java => fabric-intermediary/src/main/java/ac}/ac/grim/grimac/platform/fabric/utils/message/Log4jBackedJULogger.java (100%) rename {fabric/src/main/java => fabric-intermediary/src/main/java/ac}/ac/grim/grimac/platform/fabric/utils/message/Slf4jBackedJULogger.java (100%) rename {fabric/src/main/java => fabric-intermediary/src/main/java/ac}/ac/grim/grimac/platform/fabric/utils/metrics/BStatsConfig.java (100%) rename {fabric/src/main/java => fabric-intermediary/src/main/java/ac}/ac/grim/grimac/platform/fabric/utils/metrics/CustomChart.java (100%) rename {fabric/src/main/java => fabric-intermediary/src/main/java/ac}/ac/grim/grimac/platform/fabric/utils/metrics/JsonObjectBuilder.java (100%) rename {fabric/src/main/java => fabric-intermediary/src/main/java/ac}/ac/grim/grimac/platform/fabric/utils/metrics/Metrics.java (100%) rename {fabric/src/main/java => fabric-intermediary/src/main/java/ac}/ac/grim/grimac/platform/fabric/utils/metrics/MetricsBase.java (100%) rename {fabric/src/main/java => fabric-intermediary/src/main/java/ac}/ac/grim/grimac/platform/fabric/utils/metrics/MetricsFabric.java (100%) rename {fabric/src/main/java => fabric-intermediary/src/main/java/ac}/ac/grim/grimac/platform/fabric/utils/thread/FabricFutureUtil.java (100%) rename {fabric/src/main/java => fabric-intermediary/src/main/java/ac}/ac/grim/grimac/platform/fabric/utils/world/LevelChunkUtil.java (100%) create mode 100644 fabric-intermediary/src/main/resources/fabric.mod.json rename {fabric => fabric-intermediary}/src/main/resources/grimac.accesswidener (100%) rename {fabric => fabric-intermediary}/src/main/resources/grimac.mixins.json (100%) create mode 100644 fabric-official/build.gradle.kts create mode 100644 fabric-official/gradle.properties create mode 100644 fabric-official/mc261/build.gradle.kts create mode 100644 fabric-official/mc261/gradle.properties create mode 100644 fabric-official/mc261/src/main/resources/fabric.mod.json create mode 100644 fabric-official/src/main/resources/fabric.mod.json delete mode 100644 fabric/mc1205/build.gradle.kts delete mode 100644 fabric/mc12111/build.gradle.kts diff --git a/fabric-common/build.gradle.kts b/fabric-common/build.gradle.kts new file mode 100644 index 0000000000..615802c896 --- /dev/null +++ b/fabric-common/build.gradle.kts @@ -0,0 +1,28 @@ +// Pure-Java shared library for Grim's Fabric variants. Currently sparse — +// most of Grim's Fabric code is MC-typed (mixins, scheduler, platform) and +// lives in fabric-intermediary or (eventually) fabric-official. This module +// exists to host any cross-variant API that emerges (chain-loader-style +// interfaces, version-agnostic types) and to mirror PE's structure. + +plugins { + `java-library` + grim.`base-conventions` +} + +repositories { + exclusive("https://repo.grim.ac/snapshots") { + includeGroup("ac.grim.grimac") + includeGroup("com.github.retrooper") + } + exclusive("https://jitpack.io", { mavenContent { releasesOnly() } }) { + includeGroup("com.github.Fallen-Breath.conditional-mixin") + } + exclusive("https://nexus.scarsz.me/content/repositories/releases", { mavenContent { releasesOnly() } }) { + includeGroup("github.scarsz") + } + mavenCentral() +} + +dependencies { + compileOnly(project(":common")) +} diff --git a/fabric-intermediary/build.gradle.kts b/fabric-intermediary/build.gradle.kts new file mode 100644 index 0000000000..81c9ce087f --- /dev/null +++ b/fabric-intermediary/build.gradle.kts @@ -0,0 +1,141 @@ +import versioning.BuildConfig + +val minecraft_version: String by project +val yarn_mappings: String by project +val fabric_version: String by project + +plugins { + `maven-publish` + alias(libs.plugins.fabric.loom) + grim.`base-conventions` + grim.`jij-conventions` +} + +dependencies { + minecraft("com.mojang:minecraft:$minecraft_version") + mappings(loom.officialMojangMappings()) + modImplementation(fabricApi.module("fabric-lifecycle-events-v1", fabric_version)) + + modCompileOnly("me.lucko:fabric-permissions-api:0.3.1") + + modImplementation(libs.cloud.fabric) + modImplementation(libs.fabric.loader) + if (BuildConfig.shadePE) { + modImplementation(libs.packetevents.fabric) + } else { + compileOnly(libs.packetevents.fabric) + } + compileOnly("org.slf4j:slf4j-api:2.0.17") + compileOnly("org.apache.logging.log4j:log4j-api:2.24.3") + + modApi(libs.packetevents.fabric) +} + +// The configurations below will only apply to :fabric and its submodules, not its siblings or the root project +allprojects { + apply(plugin = "fabric-loom") + apply(plugin = "grim.base-conventions") + apply(plugin = "maven-publish") + + + repositories { + // 1. Fallback for non-exclusive deps + if (BuildConfig.mavenLocalOverride) mavenLocal() + + // 2. Exclusive Repositories + exclusive("https://maven.fabricmc.net/") { + includeGroup("net.fabricmc") + includeGroup("net.fabricmc.fabric-api") + } + + exclusive("https://repo.grim.ac/snapshots") { + includeGroup("ac.grim.grimac") + includeGroup("com.github.retrooper") + } + + exclusive("https://jitpack.io", { mavenContent { releasesOnly() } }) { + includeGroup("com.github.Fallen-Breath.conditional-mixin") + } + + exclusive("https://repo.viaversion.com", { mavenContent { releasesOnly() } }) { + includeGroup("com.viaversion") + } + + exclusive("https://nexus.scarsz.me/content/repositories/releases", { mavenContent { releasesOnly() } }) { + includeGroup("github.scarsz") + } + + exclusive("https://repo.opencollab.dev/maven-releases/", { mavenContent { releasesOnly() } }) { + includeGroup("org.geysermc.api") + } + + exclusive("https://repo.opencollab.dev/maven-snapshots/", { mavenContent { snapshotsOnly() } }) { + includeGroup("org.geysermc.floodgate") + includeGroup("org.geysermc.cumulus") + includeModule("org.geysermc", "common") + includeModule("org.geysermc", "geyser-parent") + } + + // Special logic for LuckPerms + if (project.name == "mc1161") { + exclusive("https://repo.grim.ac/snapshots") { includeGroup("me.lucko") } + } else { + // Enforce Central for LuckPerms so we don't accidentally check other snapshot repos + exclusive(mavenCentral()) { includeGroup("me.lucko") } + } + + mavenCentral() + } + + loom { + accessWidenerPath = file("src/main/resources/grimac.accesswidener") + } + + dependencies { + // I hate this syntax, is there an alternative to make modCompileOnly(libs.package.name) work? + val libsx = rootProject.extensions.getByType().named("libs") + // Use the libs extension from the root project + modImplementation(libsx.findLibrary("cloud-fabric").get()) { + exclude(group = "net.fabricmc.fabric-api") + } + modImplementation(libsx.findLibrary("fabric-loader").get()) + + implementation(project(":common")) + } + + publishing.publications.create("maven") { + artifact(tasks["remapJar"]) + } + + tasks { + remapJar { + archiveBaseName = "${rootProject.name}-fabric${if (project.name != "fabric") "-${project.name}" else ""}" + archiveVersion = rootProject.version as String + } + + remapSourcesJar { + archiveVersion = rootProject.version as String + } + } +} + +subprojects { + dependencies { + // configuration = "namedElements" required when depending on another loom project + implementation(project(":fabric-intermediary", configuration = "namedElements")) + } +} + +subprojects.forEach { + tasks.named("remapJar").configure { + dependsOn("${it.path}:remapJar") + } +} + +tasks.remapJar.configure { + subprojects.forEach { subproject -> + subproject.tasks.matching { it.name == "remapJar" }.configureEach { + nestedJars.from(this) + } + } +} diff --git a/fabric-intermediary/gradle.properties b/fabric-intermediary/gradle.properties new file mode 100644 index 0000000000..974299cfec --- /dev/null +++ b/fabric-intermediary/gradle.properties @@ -0,0 +1,11 @@ +# Done to increase the memory available to gradle. +org.gradle.jvmargs=-Xmx2G +org.gradle.parallel=true +# Fabric Properties +# check these on https://fabricmc.net/develop +minecraft_version=1.16.1 +yarn_mappings=1.16.1+build.21 +loader_version=0.16.13 + +# Fabric API +fabric_version=0.42.0+1.16 diff --git a/fabric/mc1161/build.gradle.kts b/fabric-intermediary/mc1161/build.gradle.kts similarity index 100% rename from fabric/mc1161/build.gradle.kts rename to fabric-intermediary/mc1161/build.gradle.kts diff --git a/fabric/mc1161/src/main/java/ac/grim/grimac/platform/fabric/mc1161/Fabric1140PlatformServer.java b/fabric-intermediary/mc1161/src/main/java/ac/grim/grimac/platform/fabric/mc1161/Fabric1140PlatformServer.java similarity index 100% rename from fabric/mc1161/src/main/java/ac/grim/grimac/platform/fabric/mc1161/Fabric1140PlatformServer.java rename to fabric-intermediary/mc1161/src/main/java/ac/grim/grimac/platform/fabric/mc1161/Fabric1140PlatformServer.java diff --git a/fabric/mc1161/src/main/java/ac/grim/grimac/platform/fabric/mc1161/GrimACFabric1161LoaderPlugin.java b/fabric-intermediary/mc1161/src/main/java/ac/grim/grimac/platform/fabric/mc1161/GrimACFabric1161LoaderPlugin.java similarity index 100% rename from fabric/mc1161/src/main/java/ac/grim/grimac/platform/fabric/mc1161/GrimACFabric1161LoaderPlugin.java rename to fabric-intermediary/mc1161/src/main/java/ac/grim/grimac/platform/fabric/mc1161/GrimACFabric1161LoaderPlugin.java diff --git a/fabric/mc1161/src/main/java/ac/grim/grimac/platform/fabric/mc1161/command/Fabric1161PlayerSelectorAdapter.java b/fabric-intermediary/mc1161/src/main/java/ac/grim/grimac/platform/fabric/mc1161/command/Fabric1161PlayerSelectorAdapter.java similarity index 100% rename from fabric/mc1161/src/main/java/ac/grim/grimac/platform/fabric/mc1161/command/Fabric1161PlayerSelectorAdapter.java rename to fabric-intermediary/mc1161/src/main/java/ac/grim/grimac/platform/fabric/mc1161/command/Fabric1161PlayerSelectorAdapter.java diff --git a/fabric/mc1161/src/main/java/ac/grim/grimac/platform/fabric/mc1161/entity/Fabric1161GrimEntity.java b/fabric-intermediary/mc1161/src/main/java/ac/grim/grimac/platform/fabric/mc1161/entity/Fabric1161GrimEntity.java similarity index 100% rename from fabric/mc1161/src/main/java/ac/grim/grimac/platform/fabric/mc1161/entity/Fabric1161GrimEntity.java rename to fabric-intermediary/mc1161/src/main/java/ac/grim/grimac/platform/fabric/mc1161/entity/Fabric1161GrimEntity.java diff --git a/fabric/mc1161/src/main/java/ac/grim/grimac/platform/fabric/mc1161/player/Fabric1161PlatformInventory.java b/fabric-intermediary/mc1161/src/main/java/ac/grim/grimac/platform/fabric/mc1161/player/Fabric1161PlatformInventory.java similarity index 100% rename from fabric/mc1161/src/main/java/ac/grim/grimac/platform/fabric/mc1161/player/Fabric1161PlatformInventory.java rename to fabric-intermediary/mc1161/src/main/java/ac/grim/grimac/platform/fabric/mc1161/player/Fabric1161PlatformInventory.java diff --git a/fabric/mc1161/src/main/java/ac/grim/grimac/platform/fabric/mc1161/player/Fabric1161PlatformPlayer.java b/fabric-intermediary/mc1161/src/main/java/ac/grim/grimac/platform/fabric/mc1161/player/Fabric1161PlatformPlayer.java similarity index 100% rename from fabric/mc1161/src/main/java/ac/grim/grimac/platform/fabric/mc1161/player/Fabric1161PlatformPlayer.java rename to fabric-intermediary/mc1161/src/main/java/ac/grim/grimac/platform/fabric/mc1161/player/Fabric1161PlatformPlayer.java diff --git a/fabric/mc1161/src/main/java/ac/grim/grimac/platform/fabric/mc1161/util/convert/Fabric1140ConversionUtil.java b/fabric-intermediary/mc1161/src/main/java/ac/grim/grimac/platform/fabric/mc1161/util/convert/Fabric1140ConversionUtil.java similarity index 100% rename from fabric/mc1161/src/main/java/ac/grim/grimac/platform/fabric/mc1161/util/convert/Fabric1140ConversionUtil.java rename to fabric-intermediary/mc1161/src/main/java/ac/grim/grimac/platform/fabric/mc1161/util/convert/Fabric1140ConversionUtil.java diff --git a/fabric/mc1161/src/main/java/ac/grim/grimac/platform/fabric/mc1161/util/convert/Fabric1161MessageUtil.java b/fabric-intermediary/mc1161/src/main/java/ac/grim/grimac/platform/fabric/mc1161/util/convert/Fabric1161MessageUtil.java similarity index 100% rename from fabric/mc1161/src/main/java/ac/grim/grimac/platform/fabric/mc1161/util/convert/Fabric1161MessageUtil.java rename to fabric-intermediary/mc1161/src/main/java/ac/grim/grimac/platform/fabric/mc1161/util/convert/Fabric1161MessageUtil.java diff --git a/fabric/mc1161/src/main/resources/fabric.mod.json b/fabric-intermediary/mc1161/src/main/resources/fabric.mod.json similarity index 100% rename from fabric/mc1161/src/main/resources/fabric.mod.json rename to fabric-intermediary/mc1161/src/main/resources/fabric.mod.json diff --git a/fabric/mc1161/src/main/resources/grimac.accesswidener b/fabric-intermediary/mc1161/src/main/resources/grimac.accesswidener similarity index 100% rename from fabric/mc1161/src/main/resources/grimac.accesswidener rename to fabric-intermediary/mc1161/src/main/resources/grimac.accesswidener diff --git a/fabric/mc1171/build.gradle.kts b/fabric-intermediary/mc1171/build.gradle.kts similarity index 73% rename from fabric/mc1171/build.gradle.kts rename to fabric-intermediary/mc1171/build.gradle.kts index 116873843a..3a0c4acdc5 100644 --- a/fabric/mc1171/build.gradle.kts +++ b/fabric-intermediary/mc1171/build.gradle.kts @@ -1,7 +1,7 @@ dependencies { minecraft("com.mojang:minecraft:1.17.1") mappings(loom.officialMojangMappings()) - compileOnly(project(":fabric:mc1161", configuration = "namedElements")) + compileOnly(project(":fabric-intermediary:mc1161", configuration = "namedElements")) modImplementation(fabricApi.module("fabric-lifecycle-events-v1", "0.46.1+1.17")) modCompileOnly("me.lucko:fabric-permissions-api:0.3.1") diff --git a/fabric/mc1171/src/main/java/ac/grim/grimac/platform/fabric/mc1171/Fabric1171PlatformServer.java b/fabric-intermediary/mc1171/src/main/java/ac/grim/grimac/platform/fabric/mc1171/Fabric1171PlatformServer.java similarity index 100% rename from fabric/mc1171/src/main/java/ac/grim/grimac/platform/fabric/mc1171/Fabric1171PlatformServer.java rename to fabric-intermediary/mc1171/src/main/java/ac/grim/grimac/platform/fabric/mc1171/Fabric1171PlatformServer.java diff --git a/fabric/mc1171/src/main/java/ac/grim/grimac/platform/fabric/mc1171/GrimACFabric1170LoaderPlugin.java b/fabric-intermediary/mc1171/src/main/java/ac/grim/grimac/platform/fabric/mc1171/GrimACFabric1170LoaderPlugin.java similarity index 100% rename from fabric/mc1171/src/main/java/ac/grim/grimac/platform/fabric/mc1171/GrimACFabric1170LoaderPlugin.java rename to fabric-intermediary/mc1171/src/main/java/ac/grim/grimac/platform/fabric/mc1171/GrimACFabric1170LoaderPlugin.java diff --git a/fabric/mc1171/src/main/java/ac/grim/grimac/platform/fabric/mc1171/entity/Fabric1170GrimEntity.java b/fabric-intermediary/mc1171/src/main/java/ac/grim/grimac/platform/fabric/mc1171/entity/Fabric1170GrimEntity.java similarity index 100% rename from fabric/mc1171/src/main/java/ac/grim/grimac/platform/fabric/mc1171/entity/Fabric1170GrimEntity.java rename to fabric-intermediary/mc1171/src/main/java/ac/grim/grimac/platform/fabric/mc1171/entity/Fabric1170GrimEntity.java diff --git a/fabric/mc1171/src/main/java/ac/grim/grimac/platform/fabric/mc1171/player/Fabric1170PlatformPlayer.java b/fabric-intermediary/mc1171/src/main/java/ac/grim/grimac/platform/fabric/mc1171/player/Fabric1170PlatformPlayer.java similarity index 100% rename from fabric/mc1171/src/main/java/ac/grim/grimac/platform/fabric/mc1171/player/Fabric1170PlatformPlayer.java rename to fabric-intermediary/mc1171/src/main/java/ac/grim/grimac/platform/fabric/mc1171/player/Fabric1170PlatformPlayer.java diff --git a/fabric/mc1171/src/main/resources/fabric.mod.json b/fabric-intermediary/mc1171/src/main/resources/fabric.mod.json similarity index 100% rename from fabric/mc1171/src/main/resources/fabric.mod.json rename to fabric-intermediary/mc1171/src/main/resources/fabric.mod.json diff --git a/fabric/mc1171/src/main/resources/grimac.accesswidener b/fabric-intermediary/mc1171/src/main/resources/grimac.accesswidener similarity index 100% rename from fabric/mc1171/src/main/resources/grimac.accesswidener rename to fabric-intermediary/mc1171/src/main/resources/grimac.accesswidener diff --git a/fabric/mc1194/build.gradle.kts b/fabric-intermediary/mc1194/build.gradle.kts similarity index 63% rename from fabric/mc1194/build.gradle.kts rename to fabric-intermediary/mc1194/build.gradle.kts index bd14d643bb..8263f1562b 100644 --- a/fabric/mc1194/build.gradle.kts +++ b/fabric-intermediary/mc1194/build.gradle.kts @@ -1,8 +1,8 @@ dependencies { minecraft("com.mojang:minecraft:1.19.4") mappings(loom.officialMojangMappings()) - compileOnly(project(":fabric:mc1161", configuration = "namedElements")) - compileOnly(project(":fabric:mc1171", configuration = "namedElements")) + compileOnly(project(":fabric-intermediary:mc1161", configuration = "namedElements")) + compileOnly(project(":fabric-intermediary:mc1171", configuration = "namedElements")) modImplementation(fabricApi.module("fabric-lifecycle-events-v1", "0.87.2+1.19.4")) modCompileOnly("me.lucko:fabric-permissions-api:0.3.1") diff --git a/fabric/mc1194/src/main/java/ac/grim/grimac/platform/fabric/mc1194/Fabric1190PlatformServer.java b/fabric-intermediary/mc1194/src/main/java/ac/grim/grimac/platform/fabric/mc1194/Fabric1190PlatformServer.java similarity index 100% rename from fabric/mc1194/src/main/java/ac/grim/grimac/platform/fabric/mc1194/Fabric1190PlatformServer.java rename to fabric-intermediary/mc1194/src/main/java/ac/grim/grimac/platform/fabric/mc1194/Fabric1190PlatformServer.java diff --git a/fabric/mc1194/src/main/java/ac/grim/grimac/platform/fabric/mc1194/GrimACFabric1190LoaderPlugin.java b/fabric-intermediary/mc1194/src/main/java/ac/grim/grimac/platform/fabric/mc1194/GrimACFabric1190LoaderPlugin.java similarity index 100% rename from fabric/mc1194/src/main/java/ac/grim/grimac/platform/fabric/mc1194/GrimACFabric1190LoaderPlugin.java rename to fabric-intermediary/mc1194/src/main/java/ac/grim/grimac/platform/fabric/mc1194/GrimACFabric1190LoaderPlugin.java diff --git a/fabric/mc1194/src/main/java/ac/grim/grimac/platform/fabric/mc1194/convert/Fabric1190MessageUtil.java b/fabric-intermediary/mc1194/src/main/java/ac/grim/grimac/platform/fabric/mc1194/convert/Fabric1190MessageUtil.java similarity index 100% rename from fabric/mc1194/src/main/java/ac/grim/grimac/platform/fabric/mc1194/convert/Fabric1190MessageUtil.java rename to fabric-intermediary/mc1194/src/main/java/ac/grim/grimac/platform/fabric/mc1194/convert/Fabric1190MessageUtil.java diff --git a/fabric/mc1194/src/main/java/ac/grim/grimac/platform/fabric/mc1194/entity/Fabric1194GrimEntity.java b/fabric-intermediary/mc1194/src/main/java/ac/grim/grimac/platform/fabric/mc1194/entity/Fabric1194GrimEntity.java similarity index 100% rename from fabric/mc1194/src/main/java/ac/grim/grimac/platform/fabric/mc1194/entity/Fabric1194GrimEntity.java rename to fabric-intermediary/mc1194/src/main/java/ac/grim/grimac/platform/fabric/mc1194/entity/Fabric1194GrimEntity.java diff --git a/fabric/mc1194/src/main/java/ac/grim/grimac/platform/fabric/mc1194/player/Fabric1193PlatformInventory.java b/fabric-intermediary/mc1194/src/main/java/ac/grim/grimac/platform/fabric/mc1194/player/Fabric1193PlatformInventory.java similarity index 100% rename from fabric/mc1194/src/main/java/ac/grim/grimac/platform/fabric/mc1194/player/Fabric1193PlatformInventory.java rename to fabric-intermediary/mc1194/src/main/java/ac/grim/grimac/platform/fabric/mc1194/player/Fabric1193PlatformInventory.java diff --git a/fabric/mc1194/src/main/resources/fabric.mod.json b/fabric-intermediary/mc1194/src/main/resources/fabric.mod.json similarity index 100% rename from fabric/mc1194/src/main/resources/fabric.mod.json rename to fabric-intermediary/mc1194/src/main/resources/fabric.mod.json diff --git a/fabric/mc1194/src/main/resources/grimac.accesswidener b/fabric-intermediary/mc1194/src/main/resources/grimac.accesswidener similarity index 100% rename from fabric/mc1194/src/main/resources/grimac.accesswidener rename to fabric-intermediary/mc1194/src/main/resources/grimac.accesswidener diff --git a/fabric-intermediary/mc1205/build.gradle.kts b/fabric-intermediary/mc1205/build.gradle.kts new file mode 100644 index 0000000000..7c5a08f97b --- /dev/null +++ b/fabric-intermediary/mc1205/build.gradle.kts @@ -0,0 +1,10 @@ +dependencies { + minecraft("com.mojang:minecraft:1.20.5") + mappings(loom.officialMojangMappings()) + compileOnly(project(":fabric-intermediary:mc1161", configuration = "namedElements")) + compileOnly(project(":fabric-intermediary:mc1171", configuration = "namedElements")) + compileOnly(project(":fabric-intermediary:mc1194", configuration = "namedElements")) + + modImplementation(fabricApi.module("fabric-lifecycle-events-v1", "0.97.8+1.20.5")) + modCompileOnly("me.lucko:fabric-permissions-api:0.3.1") +} diff --git a/fabric/mc1205/src/main/java/ac/grim/grimac/platform/fabric/mc1205/Fabric1203PlatformServer.java b/fabric-intermediary/mc1205/src/main/java/ac/grim/grimac/platform/fabric/mc1205/Fabric1203PlatformServer.java similarity index 100% rename from fabric/mc1205/src/main/java/ac/grim/grimac/platform/fabric/mc1205/Fabric1203PlatformServer.java rename to fabric-intermediary/mc1205/src/main/java/ac/grim/grimac/platform/fabric/mc1205/Fabric1203PlatformServer.java diff --git a/fabric/mc1205/src/main/java/ac/grim/grimac/platform/fabric/mc1205/GrimACFabric1200LoaderPlugin.java b/fabric-intermediary/mc1205/src/main/java/ac/grim/grimac/platform/fabric/mc1205/GrimACFabric1200LoaderPlugin.java similarity index 100% rename from fabric/mc1205/src/main/java/ac/grim/grimac/platform/fabric/mc1205/GrimACFabric1200LoaderPlugin.java rename to fabric-intermediary/mc1205/src/main/java/ac/grim/grimac/platform/fabric/mc1205/GrimACFabric1200LoaderPlugin.java diff --git a/fabric/mc1205/src/main/java/ac/grim/grimac/platform/fabric/mc1205/convert/Fabric1200MessageUtil.java b/fabric-intermediary/mc1205/src/main/java/ac/grim/grimac/platform/fabric/mc1205/convert/Fabric1200MessageUtil.java similarity index 100% rename from fabric/mc1205/src/main/java/ac/grim/grimac/platform/fabric/mc1205/convert/Fabric1200MessageUtil.java rename to fabric-intermediary/mc1205/src/main/java/ac/grim/grimac/platform/fabric/mc1205/convert/Fabric1200MessageUtil.java diff --git a/fabric/mc1205/src/main/java/ac/grim/grimac/platform/fabric/mc1205/convert/Fabric1205ConversionUtil.java b/fabric-intermediary/mc1205/src/main/java/ac/grim/grimac/platform/fabric/mc1205/convert/Fabric1205ConversionUtil.java similarity index 100% rename from fabric/mc1205/src/main/java/ac/grim/grimac/platform/fabric/mc1205/convert/Fabric1205ConversionUtil.java rename to fabric-intermediary/mc1205/src/main/java/ac/grim/grimac/platform/fabric/mc1205/convert/Fabric1205ConversionUtil.java diff --git a/fabric/mc1205/src/main/java/ac/grim/grimac/platform/fabric/mc1205/player/Fabric1202PlatformPlayer.java b/fabric-intermediary/mc1205/src/main/java/ac/grim/grimac/platform/fabric/mc1205/player/Fabric1202PlatformPlayer.java similarity index 100% rename from fabric/mc1205/src/main/java/ac/grim/grimac/platform/fabric/mc1205/player/Fabric1202PlatformPlayer.java rename to fabric-intermediary/mc1205/src/main/java/ac/grim/grimac/platform/fabric/mc1205/player/Fabric1202PlatformPlayer.java diff --git a/fabric/mc1205/src/main/resources/fabric.mod.json b/fabric-intermediary/mc1205/src/main/resources/fabric.mod.json similarity index 100% rename from fabric/mc1205/src/main/resources/fabric.mod.json rename to fabric-intermediary/mc1205/src/main/resources/fabric.mod.json diff --git a/fabric/mc1205/src/main/resources/grimac.accesswidener b/fabric-intermediary/mc1205/src/main/resources/grimac.accesswidener similarity index 100% rename from fabric/mc1205/src/main/resources/grimac.accesswidener rename to fabric-intermediary/mc1205/src/main/resources/grimac.accesswidener diff --git a/fabric-intermediary/mc12111/build.gradle.kts b/fabric-intermediary/mc12111/build.gradle.kts new file mode 100644 index 0000000000..76156d9746 --- /dev/null +++ b/fabric-intermediary/mc12111/build.gradle.kts @@ -0,0 +1,16 @@ +dependencies { + minecraft("com.mojang:minecraft:1.21.11") + mappings(loom.officialMojangMappings()) + compileOnly(project(":fabric-intermediary:mc1161", configuration = "namedElements")) + compileOnly(project(":fabric-intermediary:mc1171", configuration = "namedElements")) + compileOnly(project(":fabric-intermediary:mc1194", configuration = "namedElements")) + compileOnly(project(":fabric-intermediary:mc1205", configuration = "namedElements")) + + modImplementation(fabricApi.module("fabric-lifecycle-events-v1", "0.141.1+1.21.11")) + modCompileOnly("me.lucko:fabric-permissions-api:0.6.1") +} + + +tasks.compileJava { + options.release.set(21) +} diff --git a/fabric/mc12111/src/main/java/ac/grim/grimac/platform/fabric/mc1216/Fabric12111PlatformServer.java b/fabric-intermediary/mc12111/src/main/java/ac/grim/grimac/platform/fabric/mc1216/Fabric12111PlatformServer.java similarity index 100% rename from fabric/mc12111/src/main/java/ac/grim/grimac/platform/fabric/mc1216/Fabric12111PlatformServer.java rename to fabric-intermediary/mc12111/src/main/java/ac/grim/grimac/platform/fabric/mc1216/Fabric12111PlatformServer.java diff --git a/fabric/mc12111/src/main/java/ac/grim/grimac/platform/fabric/mc1216/GrimACFabric1212LoaderPlugin.java b/fabric-intermediary/mc12111/src/main/java/ac/grim/grimac/platform/fabric/mc1216/GrimACFabric1212LoaderPlugin.java similarity index 100% rename from fabric/mc12111/src/main/java/ac/grim/grimac/platform/fabric/mc1216/GrimACFabric1212LoaderPlugin.java rename to fabric-intermediary/mc12111/src/main/java/ac/grim/grimac/platform/fabric/mc1216/GrimACFabric1212LoaderPlugin.java diff --git a/fabric/mc12111/src/main/java/ac/grim/grimac/platform/fabric/mc1216/command/Fabric1212PlayerSelectorAdapter.java b/fabric-intermediary/mc12111/src/main/java/ac/grim/grimac/platform/fabric/mc1216/command/Fabric1212PlayerSelectorAdapter.java similarity index 100% rename from fabric/mc12111/src/main/java/ac/grim/grimac/platform/fabric/mc1216/command/Fabric1212PlayerSelectorAdapter.java rename to fabric-intermediary/mc12111/src/main/java/ac/grim/grimac/platform/fabric/mc1216/command/Fabric1212PlayerSelectorAdapter.java diff --git a/fabric/mc12111/src/main/java/ac/grim/grimac/platform/fabric/mc1216/convert/Fabric1216ConversionUtil.java b/fabric-intermediary/mc12111/src/main/java/ac/grim/grimac/platform/fabric/mc1216/convert/Fabric1216ConversionUtil.java similarity index 100% rename from fabric/mc12111/src/main/java/ac/grim/grimac/platform/fabric/mc1216/convert/Fabric1216ConversionUtil.java rename to fabric-intermediary/mc12111/src/main/java/ac/grim/grimac/platform/fabric/mc1216/convert/Fabric1216ConversionUtil.java diff --git a/fabric/mc12111/src/main/java/ac/grim/grimac/platform/fabric/mc1216/player/Fabric1212PlatformPlayer.java b/fabric-intermediary/mc12111/src/main/java/ac/grim/grimac/platform/fabric/mc1216/player/Fabric1212PlatformPlayer.java similarity index 100% rename from fabric/mc12111/src/main/java/ac/grim/grimac/platform/fabric/mc1216/player/Fabric1212PlatformPlayer.java rename to fabric-intermediary/mc12111/src/main/java/ac/grim/grimac/platform/fabric/mc1216/player/Fabric1212PlatformPlayer.java diff --git a/fabric/mc12111/src/main/java/ac/grim/grimac/platform/fabric/mc1216/player/Fabric1215PlatformInventory.java b/fabric-intermediary/mc12111/src/main/java/ac/grim/grimac/platform/fabric/mc1216/player/Fabric1215PlatformInventory.java similarity index 100% rename from fabric/mc12111/src/main/java/ac/grim/grimac/platform/fabric/mc1216/player/Fabric1215PlatformInventory.java rename to fabric-intermediary/mc12111/src/main/java/ac/grim/grimac/platform/fabric/mc1216/player/Fabric1215PlatformInventory.java diff --git a/fabric/mc12111/src/main/resources/fabric.mod.json b/fabric-intermediary/mc12111/src/main/resources/fabric.mod.json similarity index 100% rename from fabric/mc12111/src/main/resources/fabric.mod.json rename to fabric-intermediary/mc12111/src/main/resources/fabric.mod.json diff --git a/fabric/mc12111/src/main/resources/grimac.accesswidener b/fabric-intermediary/mc12111/src/main/resources/grimac.accesswidener similarity index 100% rename from fabric/mc12111/src/main/resources/grimac.accesswidener rename to fabric-intermediary/mc12111/src/main/resources/grimac.accesswidener diff --git a/fabric/src/main/java/ac/grim/grimac/platform/fabric/AbstractFabricPlatformServer.java b/fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/AbstractFabricPlatformServer.java similarity index 100% rename from fabric/src/main/java/ac/grim/grimac/platform/fabric/AbstractFabricPlatformServer.java rename to fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/AbstractFabricPlatformServer.java diff --git a/fabric/src/main/java/ac/grim/grimac/platform/fabric/FabricPlatformPlugin.java b/fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/FabricPlatformPlugin.java similarity index 100% rename from fabric/src/main/java/ac/grim/grimac/platform/fabric/FabricPlatformPlugin.java rename to fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/FabricPlatformPlugin.java diff --git a/fabric/src/main/java/ac/grim/grimac/platform/fabric/GrimACFabricEntryPoint.java b/fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/GrimACFabricEntryPoint.java similarity index 100% rename from fabric/src/main/java/ac/grim/grimac/platform/fabric/GrimACFabricEntryPoint.java rename to fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/GrimACFabricEntryPoint.java diff --git a/fabric/src/main/java/ac/grim/grimac/platform/fabric/GrimACFabricLoaderPlugin.java b/fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/GrimACFabricLoaderPlugin.java similarity index 100% rename from fabric/src/main/java/ac/grim/grimac/platform/fabric/GrimACFabricLoaderPlugin.java rename to fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/GrimACFabricLoaderPlugin.java diff --git a/fabric/src/main/java/ac/grim/grimac/platform/fabric/command/FabricPlayerSelectorParser.java b/fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/command/FabricPlayerSelectorParser.java similarity index 100% rename from fabric/src/main/java/ac/grim/grimac/platform/fabric/command/FabricPlayerSelectorParser.java rename to fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/command/FabricPlayerSelectorParser.java diff --git a/fabric/src/main/java/ac/grim/grimac/platform/fabric/entity/AbstractFabricGrimEntity.java b/fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/entity/AbstractFabricGrimEntity.java similarity index 100% rename from fabric/src/main/java/ac/grim/grimac/platform/fabric/entity/AbstractFabricGrimEntity.java rename to fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/entity/AbstractFabricGrimEntity.java diff --git a/fabric/src/main/java/ac/grim/grimac/platform/fabric/initables/FabricBStats.java b/fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/initables/FabricBStats.java similarity index 100% rename from fabric/src/main/java/ac/grim/grimac/platform/fabric/initables/FabricBStats.java rename to fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/initables/FabricBStats.java diff --git a/fabric/src/main/java/ac/grim/grimac/platform/fabric/initables/FabricTickEndEvent.java b/fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/initables/FabricTickEndEvent.java similarity index 100% rename from fabric/src/main/java/ac/grim/grimac/platform/fabric/initables/FabricTickEndEvent.java rename to fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/initables/FabricTickEndEvent.java diff --git a/fabric/src/main/java/ac/grim/grimac/platform/fabric/manager/FabricItemResetHandler.java b/fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/manager/FabricItemResetHandler.java similarity index 100% rename from fabric/src/main/java/ac/grim/grimac/platform/fabric/manager/FabricItemResetHandler.java rename to fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/manager/FabricItemResetHandler.java diff --git a/fabric/src/main/java/ac/grim/grimac/platform/fabric/manager/FabricMessagePlaceHolderManager.java b/fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/manager/FabricMessagePlaceHolderManager.java similarity index 100% rename from fabric/src/main/java/ac/grim/grimac/platform/fabric/manager/FabricMessagePlaceHolderManager.java rename to fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/manager/FabricMessagePlaceHolderManager.java diff --git a/fabric/src/main/java/ac/grim/grimac/platform/fabric/manager/FabricParserDescriptorFactory.java b/fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/manager/FabricParserDescriptorFactory.java similarity index 100% rename from fabric/src/main/java/ac/grim/grimac/platform/fabric/manager/FabricParserDescriptorFactory.java rename to fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/manager/FabricParserDescriptorFactory.java diff --git a/fabric/src/main/java/ac/grim/grimac/platform/fabric/manager/FabricPermissionRegistrationManager.java b/fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/manager/FabricPermissionRegistrationManager.java similarity index 100% rename from fabric/src/main/java/ac/grim/grimac/platform/fabric/manager/FabricPermissionRegistrationManager.java rename to fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/manager/FabricPermissionRegistrationManager.java diff --git a/fabric/src/main/java/ac/grim/grimac/platform/fabric/manager/FabricPlatformPluginManager.java b/fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/manager/FabricPlatformPluginManager.java similarity index 100% rename from fabric/src/main/java/ac/grim/grimac/platform/fabric/manager/FabricPlatformPluginManager.java rename to fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/manager/FabricPlatformPluginManager.java diff --git a/fabric/src/main/java/ac/grim/grimac/platform/fabric/mixins/LevelChunkMixin.java b/fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/mixins/LevelChunkMixin.java similarity index 100% rename from fabric/src/main/java/ac/grim/grimac/platform/fabric/mixins/LevelChunkMixin.java rename to fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/mixins/LevelChunkMixin.java diff --git a/fabric/src/main/java/ac/grim/grimac/platform/fabric/mixins/LevelMixin.java b/fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/mixins/LevelMixin.java similarity index 100% rename from fabric/src/main/java/ac/grim/grimac/platform/fabric/mixins/LevelMixin.java rename to fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/mixins/LevelMixin.java diff --git a/fabric/src/main/java/ac/grim/grimac/platform/fabric/mixins/PistonBaseBlockMixin.java b/fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/mixins/PistonBaseBlockMixin.java similarity index 100% rename from fabric/src/main/java/ac/grim/grimac/platform/fabric/mixins/PistonBaseBlockMixin.java rename to fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/mixins/PistonBaseBlockMixin.java diff --git a/fabric/src/main/java/ac/grim/grimac/platform/fabric/mixins/ServerPlayerMixin.java b/fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/mixins/ServerPlayerMixin.java similarity index 100% rename from fabric/src/main/java/ac/grim/grimac/platform/fabric/mixins/ServerPlayerMixin.java rename to fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/mixins/ServerPlayerMixin.java diff --git a/fabric/src/main/java/ac/grim/grimac/platform/fabric/player/AbstractFabricPlatformInventory.java b/fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/player/AbstractFabricPlatformInventory.java similarity index 100% rename from fabric/src/main/java/ac/grim/grimac/platform/fabric/player/AbstractFabricPlatformInventory.java rename to fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/player/AbstractFabricPlatformInventory.java diff --git a/fabric/src/main/java/ac/grim/grimac/platform/fabric/player/AbstractFabricPlatformPlayer.java b/fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/player/AbstractFabricPlatformPlayer.java similarity index 100% rename from fabric/src/main/java/ac/grim/grimac/platform/fabric/player/AbstractFabricPlatformPlayer.java rename to fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/player/AbstractFabricPlatformPlayer.java diff --git a/fabric/src/main/java/ac/grim/grimac/platform/fabric/player/FabricOfflinePlatformPlayer.java b/fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/player/FabricOfflinePlatformPlayer.java similarity index 100% rename from fabric/src/main/java/ac/grim/grimac/platform/fabric/player/FabricOfflinePlatformPlayer.java rename to fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/player/FabricOfflinePlatformPlayer.java diff --git a/fabric/src/main/java/ac/grim/grimac/platform/fabric/player/FabricPlatformPlayerFactory.java b/fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/player/FabricPlatformPlayerFactory.java similarity index 100% rename from fabric/src/main/java/ac/grim/grimac/platform/fabric/player/FabricPlatformPlayerFactory.java rename to fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/player/FabricPlatformPlayerFactory.java diff --git a/fabric/src/main/java/ac/grim/grimac/platform/fabric/resolver/FabricResolverRegistrar.java b/fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/resolver/FabricResolverRegistrar.java similarity index 100% rename from fabric/src/main/java/ac/grim/grimac/platform/fabric/resolver/FabricResolverRegistrar.java rename to fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/resolver/FabricResolverRegistrar.java diff --git a/fabric/src/main/java/ac/grim/grimac/platform/fabric/scheduler/FabricAsyncScheduler.java b/fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/scheduler/FabricAsyncScheduler.java similarity index 100% rename from fabric/src/main/java/ac/grim/grimac/platform/fabric/scheduler/FabricAsyncScheduler.java rename to fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/scheduler/FabricAsyncScheduler.java diff --git a/fabric/src/main/java/ac/grim/grimac/platform/fabric/scheduler/FabricEntityScheduler.java b/fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/scheduler/FabricEntityScheduler.java similarity index 100% rename from fabric/src/main/java/ac/grim/grimac/platform/fabric/scheduler/FabricEntityScheduler.java rename to fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/scheduler/FabricEntityScheduler.java diff --git a/fabric/src/main/java/ac/grim/grimac/platform/fabric/scheduler/FabricGlobalRegionScheduler.java b/fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/scheduler/FabricGlobalRegionScheduler.java similarity index 100% rename from fabric/src/main/java/ac/grim/grimac/platform/fabric/scheduler/FabricGlobalRegionScheduler.java rename to fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/scheduler/FabricGlobalRegionScheduler.java diff --git a/fabric/src/main/java/ac/grim/grimac/platform/fabric/scheduler/FabricPlatformScheduler.java b/fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/scheduler/FabricPlatformScheduler.java similarity index 100% rename from fabric/src/main/java/ac/grim/grimac/platform/fabric/scheduler/FabricPlatformScheduler.java rename to fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/scheduler/FabricPlatformScheduler.java diff --git a/fabric/src/main/java/ac/grim/grimac/platform/fabric/scheduler/FabricRegionScheduler.java b/fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/scheduler/FabricRegionScheduler.java similarity index 100% rename from fabric/src/main/java/ac/grim/grimac/platform/fabric/scheduler/FabricRegionScheduler.java rename to fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/scheduler/FabricRegionScheduler.java diff --git a/fabric/src/main/java/ac/grim/grimac/platform/fabric/scheduler/FabricTaskHandle.java b/fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/scheduler/FabricTaskHandle.java similarity index 100% rename from fabric/src/main/java/ac/grim/grimac/platform/fabric/scheduler/FabricTaskHandle.java rename to fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/scheduler/FabricTaskHandle.java diff --git a/fabric/src/main/java/ac/grim/grimac/platform/fabric/sender/FabricSenderFactory.java b/fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/sender/FabricSenderFactory.java similarity index 100% rename from fabric/src/main/java/ac/grim/grimac/platform/fabric/sender/FabricSenderFactory.java rename to fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/sender/FabricSenderFactory.java diff --git a/fabric/src/main/java/ac/grim/grimac/platform/fabric/utils/PolymerHook.java b/fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/utils/PolymerHook.java similarity index 100% rename from fabric/src/main/java/ac/grim/grimac/platform/fabric/utils/PolymerHook.java rename to fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/utils/PolymerHook.java diff --git a/fabric/src/main/java/ac/grim/grimac/platform/fabric/utils/convert/FabricConversionUtil.java b/fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/utils/convert/FabricConversionUtil.java similarity index 100% rename from fabric/src/main/java/ac/grim/grimac/platform/fabric/utils/convert/FabricConversionUtil.java rename to fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/utils/convert/FabricConversionUtil.java diff --git a/fabric/src/main/java/ac/grim/grimac/platform/fabric/utils/convert/IFabricConversionUtil.java b/fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/utils/convert/IFabricConversionUtil.java similarity index 100% rename from fabric/src/main/java/ac/grim/grimac/platform/fabric/utils/convert/IFabricConversionUtil.java rename to fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/utils/convert/IFabricConversionUtil.java diff --git a/fabric/src/main/java/ac/grim/grimac/platform/fabric/utils/message/IFabricMessageUtil.java b/fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/utils/message/IFabricMessageUtil.java similarity index 100% rename from fabric/src/main/java/ac/grim/grimac/platform/fabric/utils/message/IFabricMessageUtil.java rename to fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/utils/message/IFabricMessageUtil.java diff --git a/fabric/src/main/java/ac/grim/grimac/platform/fabric/utils/message/JULoggerFactory.java b/fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/utils/message/JULoggerFactory.java similarity index 100% rename from fabric/src/main/java/ac/grim/grimac/platform/fabric/utils/message/JULoggerFactory.java rename to fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/utils/message/JULoggerFactory.java diff --git a/fabric/src/main/java/ac/grim/grimac/platform/fabric/utils/message/Log4jBackedJULogger.java b/fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/utils/message/Log4jBackedJULogger.java similarity index 100% rename from fabric/src/main/java/ac/grim/grimac/platform/fabric/utils/message/Log4jBackedJULogger.java rename to fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/utils/message/Log4jBackedJULogger.java diff --git a/fabric/src/main/java/ac/grim/grimac/platform/fabric/utils/message/Slf4jBackedJULogger.java b/fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/utils/message/Slf4jBackedJULogger.java similarity index 100% rename from fabric/src/main/java/ac/grim/grimac/platform/fabric/utils/message/Slf4jBackedJULogger.java rename to fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/utils/message/Slf4jBackedJULogger.java diff --git a/fabric/src/main/java/ac/grim/grimac/platform/fabric/utils/metrics/BStatsConfig.java b/fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/utils/metrics/BStatsConfig.java similarity index 100% rename from fabric/src/main/java/ac/grim/grimac/platform/fabric/utils/metrics/BStatsConfig.java rename to fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/utils/metrics/BStatsConfig.java diff --git a/fabric/src/main/java/ac/grim/grimac/platform/fabric/utils/metrics/CustomChart.java b/fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/utils/metrics/CustomChart.java similarity index 100% rename from fabric/src/main/java/ac/grim/grimac/platform/fabric/utils/metrics/CustomChart.java rename to fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/utils/metrics/CustomChart.java diff --git a/fabric/src/main/java/ac/grim/grimac/platform/fabric/utils/metrics/JsonObjectBuilder.java b/fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/utils/metrics/JsonObjectBuilder.java similarity index 100% rename from fabric/src/main/java/ac/grim/grimac/platform/fabric/utils/metrics/JsonObjectBuilder.java rename to fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/utils/metrics/JsonObjectBuilder.java diff --git a/fabric/src/main/java/ac/grim/grimac/platform/fabric/utils/metrics/Metrics.java b/fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/utils/metrics/Metrics.java similarity index 100% rename from fabric/src/main/java/ac/grim/grimac/platform/fabric/utils/metrics/Metrics.java rename to fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/utils/metrics/Metrics.java diff --git a/fabric/src/main/java/ac/grim/grimac/platform/fabric/utils/metrics/MetricsBase.java b/fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/utils/metrics/MetricsBase.java similarity index 100% rename from fabric/src/main/java/ac/grim/grimac/platform/fabric/utils/metrics/MetricsBase.java rename to fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/utils/metrics/MetricsBase.java diff --git a/fabric/src/main/java/ac/grim/grimac/platform/fabric/utils/metrics/MetricsFabric.java b/fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/utils/metrics/MetricsFabric.java similarity index 100% rename from fabric/src/main/java/ac/grim/grimac/platform/fabric/utils/metrics/MetricsFabric.java rename to fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/utils/metrics/MetricsFabric.java diff --git a/fabric/src/main/java/ac/grim/grimac/platform/fabric/utils/thread/FabricFutureUtil.java b/fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/utils/thread/FabricFutureUtil.java similarity index 100% rename from fabric/src/main/java/ac/grim/grimac/platform/fabric/utils/thread/FabricFutureUtil.java rename to fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/utils/thread/FabricFutureUtil.java diff --git a/fabric/src/main/java/ac/grim/grimac/platform/fabric/utils/world/LevelChunkUtil.java b/fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/utils/world/LevelChunkUtil.java similarity index 100% rename from fabric/src/main/java/ac/grim/grimac/platform/fabric/utils/world/LevelChunkUtil.java rename to fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/utils/world/LevelChunkUtil.java diff --git a/fabric-intermediary/src/main/resources/fabric.mod.json b/fabric-intermediary/src/main/resources/fabric.mod.json new file mode 100644 index 0000000000..0c668d01e7 --- /dev/null +++ b/fabric-intermediary/src/main/resources/fabric.mod.json @@ -0,0 +1,40 @@ +{ + "schemaVersion": 1, + "id": "grimac", + "version": "${version}", + "name": "GrimAC", + "description": "Libre simulation anticheat designed for 26.1 with 1.8-26.1 support, powered by PacketEvents 2.0.", + "authors": [ + "GrimAC" + ], + "license": "GPLv3", + "environment": "server", + "entrypoints": { + "main": [ + "ac.grim.grimac.platform.fabric.GrimACFabricEntryPoint" + ], + "preLaunch": [ + "ac.grim.grimac.platform.fabric.GrimACFabricEntryPoint" + ] + }, + "mixins": [ + "grimac.mixins.json" + ], + "custom": { + "loom:injected_interfaces": { + "net/minecraft/world/level/Level": [ + "ac/grim/grimac/platform/api/world/PlatformWorld" + ] + } + }, + "accessWidener": "grimac.accesswidener", + "depends": { + "fabricloader": "\u003e\u003d0.16", + "fabric-lifecycle-events-v1": "*", + "packetevents": "*" + }, + "recommends": { + "cloud": "*", + "fabric-permissions-api-v0": "*" + } +} diff --git a/fabric/src/main/resources/grimac.accesswidener b/fabric-intermediary/src/main/resources/grimac.accesswidener similarity index 100% rename from fabric/src/main/resources/grimac.accesswidener rename to fabric-intermediary/src/main/resources/grimac.accesswidener diff --git a/fabric/src/main/resources/grimac.mixins.json b/fabric-intermediary/src/main/resources/grimac.mixins.json similarity index 100% rename from fabric/src/main/resources/grimac.mixins.json rename to fabric-intermediary/src/main/resources/grimac.mixins.json diff --git a/fabric-official/build.gradle.kts b/fabric-official/build.gradle.kts new file mode 100644 index 0000000000..973c60d756 --- /dev/null +++ b/fabric-official/build.gradle.kts @@ -0,0 +1,128 @@ +import versioning.BuildConfig + +val minecraft_version: String by project +val fabric_version: String by project + +plugins { + `maven-publish` + alias(libs.plugins.fabric.loom) + grim.`base-conventions` + grim.`jij-conventions` +} + +java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(25)) + } +} + +dependencies { + minecraft("com.mojang:minecraft:$minecraft_version") + // Mojang stopped publishing official mappings starting MC 26.X and Yarn has no 26.X + // build either; Fabric intermediary publishes the empty `0.0.0` mapping for it. + // fabric-official is currently a structural stub that loads on 26.X but cannot + // reference meaningful MC symbols until usable mappings land. + mappings("net.fabricmc:intermediary:0.0.0:v2") + modImplementation(libs.fabric.loader) + + // PE is intentionally NOT a dep here: PE's accessWidener is intermediary-namespaced + // and conflicts with the intermediary 0.0.0 mappings used by fabric-official. + // Bring PE back in once usable 26.X mappings exist and we can compile against named. + compileOnly("org.slf4j:slf4j-api:2.0.17") + compileOnly("org.apache.logging.log4j:log4j-api:2.24.3") +} + +allprojects { + apply(plugin = "fabric-loom") + apply(plugin = "grim.base-conventions") + apply(plugin = "maven-publish") + + repositories { + if (BuildConfig.mavenLocalOverride) mavenLocal() + + exclusive("https://maven.fabricmc.net/") { + includeGroup("net.fabricmc") + includeGroup("net.fabricmc.fabric-api") + } + + exclusive("https://repo.grim.ac/snapshots") { + includeGroup("ac.grim.grimac") + includeGroup("com.github.retrooper") + } + + exclusive("https://jitpack.io", { mavenContent { releasesOnly() } }) { + includeGroup("com.github.Fallen-Breath.conditional-mixin") + } + + exclusive("https://repo.viaversion.com", { mavenContent { releasesOnly() } }) { + includeGroup("com.viaversion") + } + + exclusive("https://nexus.scarsz.me/content/repositories/releases", { mavenContent { releasesOnly() } }) { + includeGroup("github.scarsz") + } + + exclusive("https://repo.opencollab.dev/maven-releases/", { mavenContent { releasesOnly() } }) { + includeGroup("org.geysermc.api") + } + + exclusive("https://repo.opencollab.dev/maven-snapshots/", { mavenContent { snapshotsOnly() } }) { + includeGroup("org.geysermc.floodgate") + includeGroup("org.geysermc.cumulus") + includeModule("org.geysermc", "common") + includeModule("org.geysermc", "geyser-parent") + } + + mavenCentral() + } + + java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(25)) + } + } + + dependencies { + val libsx = rootProject.extensions.getByType().named("libs") + modImplementation(libsx.findLibrary("fabric-loader").get()) + // :common is intentionally NOT pulled here; its transitive PE dep would force + // Loom to remap an intermediary-namespaced access widener against 0.0.0 (fails). + // When real 26.X mappings land, re-add `implementation(project(":common"))`. + } + + publishing.publications.create("maven") { + artifact(tasks["remapJar"]) + } + + tasks { + // Intermediary 0.0.0 has no "named" namespace, so source remap fails. Disable + // sources jar generation in any subproject that registers it. + matching { it.name == "remapSourcesJar" || it.name == "sourcesJar" } + .configureEach { enabled = false } + + remapJar { + archiveBaseName = "${rootProject.name}-fabric${if (project.name != "fabric-official") "-${project.name}" else "-official"}" + archiveVersion = rootProject.version as String + } + } +} + +subprojects { + dependencies { + implementation(project(":fabric-official", configuration = "namedElements")) + } +} + +subprojects.forEach { + tasks.named("remapJar").configure { + dependsOn("${it.path}:remapJar") + } +} + +tasks.remapJar.configure { + subprojects.forEach { subproject -> + subproject.tasks.matching { it.name == "remapJar" }.configureEach { + nestedJars.from(this) + } + } +} diff --git a/fabric-official/gradle.properties b/fabric-official/gradle.properties new file mode 100644 index 0000000000..e8df6133d8 --- /dev/null +++ b/fabric-official/gradle.properties @@ -0,0 +1,6 @@ +org.gradle.jvmargs=-Xmx2G +org.gradle.parallel=true + +# MC 26.1+ (Mojang's new version scheme, Java 25 floor, official mappings unavailable). +minecraft_version=26.1.2 +fabric_version=0.144.3+26.1 diff --git a/fabric-official/mc261/build.gradle.kts b/fabric-official/mc261/build.gradle.kts new file mode 100644 index 0000000000..e28e7283c8 --- /dev/null +++ b/fabric-official/mc261/build.gradle.kts @@ -0,0 +1,6 @@ +val minecraft_version: String by project + +dependencies { + minecraft("com.mojang:minecraft:$minecraft_version") + mappings("net.fabricmc:intermediary:0.0.0:v2") +} diff --git a/fabric-official/mc261/gradle.properties b/fabric-official/mc261/gradle.properties new file mode 100644 index 0000000000..610ac2d374 --- /dev/null +++ b/fabric-official/mc261/gradle.properties @@ -0,0 +1 @@ +minecraft_version=26.1.2 diff --git a/fabric-official/mc261/src/main/resources/fabric.mod.json b/fabric-official/mc261/src/main/resources/fabric.mod.json new file mode 100644 index 0000000000..aaae36ba5b --- /dev/null +++ b/fabric-official/mc261/src/main/resources/fabric.mod.json @@ -0,0 +1,14 @@ +{ + "schemaVersion": 1, + "id": "grimac-fabric-mc261", + "version": "${version}", + "name": "GrimAC mc261", + "description": "Per-version source root for MC 26.1+. Stub.", + "license": "GPL-3.0", + "environment": "*", + "depends": { + "minecraft": ">=26.1", + "java": ">=25", + "fabricloader": ">=0.19" + } +} diff --git a/fabric-official/src/main/resources/fabric.mod.json b/fabric-official/src/main/resources/fabric.mod.json new file mode 100644 index 0000000000..b782537e17 --- /dev/null +++ b/fabric-official/src/main/resources/fabric.mod.json @@ -0,0 +1,14 @@ +{ + "schemaVersion": 1, + "id": "grimac-fabric-official", + "version": "${version}", + "name": "GrimAC (Fabric, official mappings)", + "description": "GrimAC variant for Minecraft 26.X. Structural stub — meaningful MC-typed code awaits mapping availability.", + "license": "GPL-3.0", + "environment": "*", + "depends": { + "minecraft": ">=26.1", + "java": ">=25", + "fabricloader": ">=0.19" + } +} diff --git a/fabric/build.gradle.kts b/fabric/build.gradle.kts index 4e585a0b02..51802a4ef4 100644 --- a/fabric/build.gradle.kts +++ b/fabric/build.gradle.kts @@ -1,9 +1,14 @@ +import net.fabricmc.loom.task.RemapJarTask import versioning.BuildConfig val minecraft_version: String by project -val yarn_mappings: String by project val fabric_version: String by project +// Top-level Grim Fabric aggregator. Produces the published `grimac-fabric-.jar` +// containing both the intermediary chain (1.16.1→1.21.11) and the official 26.X stub +// as nested mods. Fabric Loader picks which variant to enable at runtime based on the +// declared minecraft version ranges in each nested mod. + plugins { `maven-publish` alias(libs.plugins.fabric.loom) @@ -11,131 +16,46 @@ plugins { grim.`jij-conventions` } +repositories { + if (BuildConfig.mavenLocalOverride) mavenLocal() + exclusive("https://maven.fabricmc.net/") { + includeGroup("net.fabricmc") + includeGroup("net.fabricmc.fabric-api") + } + mavenCentral() +} + dependencies { + // Bind to the lowest supported MC version so Loom is happy. minecraft("com.mojang:minecraft:$minecraft_version") mappings(loom.officialMojangMappings()) - modImplementation(fabricApi.module("fabric-lifecycle-events-v1", fabric_version)) - - modCompileOnly("me.lucko:fabric-permissions-api:0.3.1") - - modImplementation(libs.cloud.fabric) modImplementation(libs.fabric.loader) - if (BuildConfig.shadePE) { - modImplementation(libs.packetevents.fabric) - } else { - compileOnly(libs.packetevents.fabric) - } - compileOnly("org.slf4j:slf4j-api:2.0.17") - compileOnly("org.apache.logging.log4j:log4j-api:2.24.3") - - modApi(libs.packetevents.fabric) -} - -// The configurations below will only apply to :fabric and its submodules, not its siblings or the root project -allprojects { - apply(plugin = "fabric-loom") - apply(plugin = "grim.base-conventions") - apply(plugin = "maven-publish") - - - repositories { - // 1. Fallback for non-exclusive deps - if (BuildConfig.mavenLocalOverride) mavenLocal() - - // 2. Exclusive Repositories - exclusive("https://maven.fabricmc.net/") { - includeGroup("net.fabricmc") - includeGroup("net.fabricmc.fabric-api") - } - - exclusive("https://repo.grim.ac/snapshots") { - includeGroup("ac.grim.grimac") - includeGroup("com.github.retrooper") - } - - exclusive("https://jitpack.io", { mavenContent { releasesOnly() } }) { - includeGroup("com.github.Fallen-Breath.conditional-mixin") - } - - exclusive("https://repo.viaversion.com", { mavenContent { releasesOnly() } }) { - includeGroup("com.viaversion") - } - - exclusive("https://nexus.scarsz.me/content/repositories/releases", { mavenContent { releasesOnly() } }) { - includeGroup("github.scarsz") - } - exclusive("https://repo.opencollab.dev/maven-releases/", { mavenContent { releasesOnly() } }) { - includeGroup("org.geysermc.api") - } - - exclusive("https://repo.opencollab.dev/maven-snapshots/", { mavenContent { snapshotsOnly() } }) { - includeGroup("org.geysermc.floodgate") - includeGroup("org.geysermc.cumulus") - includeModule("org.geysermc", "common") - includeModule("org.geysermc", "geyser-parent") - } - - // Special logic for LuckPerms - if (project.name == "mc1161") { - exclusive("https://repo.grim.ac/snapshots") { includeGroup("me.lucko") } - } else { - // Enforce Central for LuckPerms so we don't accidentally check other snapshot repos - exclusive(mavenCentral()) { includeGroup("me.lucko") } - } - - mavenCentral() - } - - loom { - accessWidenerPath = file("src/main/resources/grimac.accesswidener") - } - - dependencies { - // I hate this syntax, is there an alternative to make modCompileOnly(libs.package.name) work? - val libsx = rootProject.extensions.getByType().named("libs") - // Use the libs extension from the root project - modImplementation(libsx.findLibrary("cloud-fabric").get()) { - exclude(group = "net.fabricmc.fabric-api") - } - modImplementation(libsx.findLibrary("fabric-loader").get()) - - implementation(project(":common")) - } - - publishing.publications.create("maven") { - artifact(tasks["remapJar"]) - } - - tasks { - remapJar { - archiveBaseName = "${rootProject.name}-fabric${if (project.name != "fabric") "-${project.name}" else ""}" - archiveVersion = rootProject.version as String - } - - remapSourcesJar { - archiveVersion = rootProject.version as String - } - } + // Pull the variants in as nested JiJ entries. fabric-common is a regular Java + // library so we use `implementation` + `include`; the two variant projects + // produce their own Loom remapJars via the `namedElements` configuration. + include(project(":fabric-common")) + include(project(":fabric-intermediary", configuration = "namedElements")) + include(project(":fabric-official", configuration = "namedElements")) } -subprojects { - dependencies { - // configuration = "namedElements" required when depending on another loom project - implementation(project(":fabric", configuration = "namedElements")) - } -} +evaluationDependsOn(":fabric-intermediary") +evaluationDependsOn(":fabric-official") -subprojects.forEach { - tasks.named("remapJar").configure { - dependsOn("${it.path}:remapJar") - } +publishing.publications.create("maven") { + artifact(tasks["remapJar"]) } -tasks.remapJar.configure { - subprojects.forEach { subproject -> - subproject.tasks.matching { it.name == "remapJar" }.configureEach { - nestedJars.from(this) - } +tasks { + remapJar { + archiveBaseName = "${rootProject.name}-fabric" + archiveVersion = rootProject.version as String + + val intermediaryRemap = project(":fabric-intermediary").tasks.named("remapJar") + val officialRemap = project(":fabric-official").tasks.named("remapJar") + dependsOn(intermediaryRemap) + dependsOn(officialRemap) + nestedJars.from(intermediaryRemap) + nestedJars.from(officialRemap) } } diff --git a/fabric/gradle.properties b/fabric/gradle.properties index 974299cfec..f305ef3c43 100644 --- a/fabric/gradle.properties +++ b/fabric/gradle.properties @@ -1,11 +1,8 @@ -# Done to increase the memory available to gradle. org.gradle.jvmargs=-Xmx2G org.gradle.parallel=true -# Fabric Properties -# check these on https://fabricmc.net/develop -minecraft_version=1.16.1 -yarn_mappings=1.16.1+build.21 -loader_version=0.16.13 -# Fabric API +# Aggregator pins lowest-supported MC version so Loom can resolve mappings. +# Per-variant MC versions live in fabric-intermediary/gradle.properties and +# fabric-official/gradle.properties. +minecraft_version=1.16.1 fabric_version=0.42.0+1.16 diff --git a/fabric/mc1205/build.gradle.kts b/fabric/mc1205/build.gradle.kts deleted file mode 100644 index 89eca31283..0000000000 --- a/fabric/mc1205/build.gradle.kts +++ /dev/null @@ -1,10 +0,0 @@ -dependencies { - minecraft("com.mojang:minecraft:1.20.5") - mappings(loom.officialMojangMappings()) - compileOnly(project(":fabric:mc1161", configuration = "namedElements")) - compileOnly(project(":fabric:mc1171", configuration = "namedElements")) - compileOnly(project(":fabric:mc1194", configuration = "namedElements")) - - modImplementation(fabricApi.module("fabric-lifecycle-events-v1", "0.97.8+1.20.5")) - modCompileOnly("me.lucko:fabric-permissions-api:0.3.1") -} diff --git a/fabric/mc12111/build.gradle.kts b/fabric/mc12111/build.gradle.kts deleted file mode 100644 index dcd0fab981..0000000000 --- a/fabric/mc12111/build.gradle.kts +++ /dev/null @@ -1,16 +0,0 @@ -dependencies { - minecraft("com.mojang:minecraft:1.21.11") - mappings(loom.officialMojangMappings()) - compileOnly(project(":fabric:mc1161", configuration = "namedElements")) - compileOnly(project(":fabric:mc1171", configuration = "namedElements")) - compileOnly(project(":fabric:mc1194", configuration = "namedElements")) - compileOnly(project(":fabric:mc1205", configuration = "namedElements")) - - modImplementation(fabricApi.module("fabric-lifecycle-events-v1", "0.141.1+1.21.11")) - modCompileOnly("me.lucko:fabric-permissions-api:0.6.1") -} - - -tasks.compileJava { - options.release.set(21) -} diff --git a/fabric/src/main/resources/fabric.mod.json b/fabric/src/main/resources/fabric.mod.json index 0c668d01e7..970c751a49 100644 --- a/fabric/src/main/resources/fabric.mod.json +++ b/fabric/src/main/resources/fabric.mod.json @@ -3,38 +3,12 @@ "id": "grimac", "version": "${version}", "name": "GrimAC", - "description": "Libre simulation anticheat designed for 26.1 with 1.8-26.1 support, powered by PacketEvents 2.0.", - "authors": [ - "GrimAC" - ], - "license": "GPLv3", - "environment": "server", - "entrypoints": { - "main": [ - "ac.grim.grimac.platform.fabric.GrimACFabricEntryPoint" - ], - "preLaunch": [ - "ac.grim.grimac.platform.fabric.GrimACFabricEntryPoint" - ] - }, - "mixins": [ - "grimac.mixins.json" - ], - "custom": { - "loom:injected_interfaces": { - "net/minecraft/world/level/Level": [ - "ac/grim/grimac/platform/api/world/PlatformWorld" - ] - } - }, - "accessWidener": "grimac.accesswidener", + "description": "GrimAC — Fabric meta-mod aggregator. Loads the intermediary or official variant by MC version range.", + "license": "GPL-3.0", + "environment": "*", "depends": { - "fabricloader": "\u003e\u003d0.16", - "fabric-lifecycle-events-v1": "*", - "packetevents": "*" - }, - "recommends": { - "cloud": "*", - "fabric-permissions-api-v0": "*" + "minecraft": ">=1.16.1", + "java": ">=17", + "fabricloader": ">=0.16" } } diff --git a/settings.gradle.kts b/settings.gradle.kts index eb591fa309..f728cd2b84 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -64,10 +64,14 @@ rootProject.name = "grimac" include("common") include("bukkit") include("fabric") -include(":fabric:mc1161") -include(":fabric:mc1171") -include(":fabric:mc1194") -include(":fabric:mc1205") -include(":fabric:mc12111") +include("fabric-common") +include("fabric-intermediary") +include(":fabric-intermediary:mc1161") +include(":fabric-intermediary:mc1171") +include(":fabric-intermediary:mc1194") +include(":fabric-intermediary:mc1205") +include(":fabric-intermediary:mc12111") +include("fabric-official") +include(":fabric-official:mc261") if (file("workspace.gradle.kts").exists()) apply(from = "workspace.gradle.kts") From 43ae476ba4a56562f3db93e0c7f6f8c3bad6db77 Mon Sep 17 00:00:00 2001 From: Axionize <154778082+Axionize@users.noreply.github.com> Date: Sat, 23 May 2026 06:38:14 -0400 Subject: [PATCH 02/34] fabric: same codex review #1 fixes mirrored on Grim side - Rename fabric-intermediary modid to grimac-fabric-intermediary. - Add `minecraft: ">=1.16.1 <26"` upper bound on intermediary. - Drop variant include() in top-level fabric aggregator; nest variant remapJars by file path only. - Fix archiveBaseName double-`fabric` prefix for variants. --- fabric-intermediary/build.gradle.kts | 11 ++++++- .../src/main/resources/fabric.mod.json | 2 +- fabric-official/build.gradle.kts | 6 +++- fabric/build.gradle.kts | 32 +++++++++++-------- 4 files changed, 34 insertions(+), 17 deletions(-) diff --git a/fabric-intermediary/build.gradle.kts b/fabric-intermediary/build.gradle.kts index 81c9ce087f..b39f141b7d 100644 --- a/fabric-intermediary/build.gradle.kts +++ b/fabric-intermediary/build.gradle.kts @@ -109,11 +109,20 @@ allprojects { tasks { remapJar { - archiveBaseName = "${rootProject.name}-fabric${if (project.name != "fabric") "-${project.name}" else ""}" + archiveBaseName = if (project == project(":fabric-intermediary")) { + "${rootProject.name}-fabric-intermediary" + } else { + "${rootProject.name}-fabric-${project.name}" + } archiveVersion = rootProject.version as String } remapSourcesJar { + archiveBaseName = if (project == project(":fabric-intermediary")) { + "${rootProject.name}-fabric-intermediary" + } else { + "${rootProject.name}-fabric-${project.name}" + } archiveVersion = rootProject.version as String } } diff --git a/fabric-intermediary/src/main/resources/fabric.mod.json b/fabric-intermediary/src/main/resources/fabric.mod.json index 0c668d01e7..fb543c99e5 100644 --- a/fabric-intermediary/src/main/resources/fabric.mod.json +++ b/fabric-intermediary/src/main/resources/fabric.mod.json @@ -1,6 +1,6 @@ { "schemaVersion": 1, - "id": "grimac", + "id": "grimac-fabric-intermediary", "version": "${version}", "name": "GrimAC", "description": "Libre simulation anticheat designed for 26.1 with 1.8-26.1 support, powered by PacketEvents 2.0.", diff --git a/fabric-official/build.gradle.kts b/fabric-official/build.gradle.kts index 973c60d756..3a167025a9 100644 --- a/fabric-official/build.gradle.kts +++ b/fabric-official/build.gradle.kts @@ -101,7 +101,11 @@ allprojects { .configureEach { enabled = false } remapJar { - archiveBaseName = "${rootProject.name}-fabric${if (project.name != "fabric-official") "-${project.name}" else "-official"}" + archiveBaseName = if (project == project(":fabric-official")) { + "${rootProject.name}-fabric-official" + } else { + "${rootProject.name}-fabric-${project.name}" + } archiveVersion = rootProject.version as String } } diff --git a/fabric/build.gradle.kts b/fabric/build.gradle.kts index 51802a4ef4..7a0efac151 100644 --- a/fabric/build.gradle.kts +++ b/fabric/build.gradle.kts @@ -31,17 +31,13 @@ dependencies { mappings(loom.officialMojangMappings()) modImplementation(libs.fabric.loader) - // Pull the variants in as nested JiJ entries. fabric-common is a regular Java - // library so we use `implementation` + `include`; the two variant projects - // produce their own Loom remapJars via the `namedElements` configuration. + // fabric-common is a regular Java library so it's pulled via Loom's `include(...)` + // JiJ. The variant mods (fabric-intermediary, fabric-official) are nested below + // via `nestedJars.from(remapJar)` to avoid the double-JiJ of dev + remapped + // artifacts. include(project(":fabric-common")) - include(project(":fabric-intermediary", configuration = "namedElements")) - include(project(":fabric-official", configuration = "namedElements")) } -evaluationDependsOn(":fabric-intermediary") -evaluationDependsOn(":fabric-official") - publishing.publications.create("maven") { artifact(tasks["remapJar"]) } @@ -51,11 +47,19 @@ tasks { archiveBaseName = "${rootProject.name}-fabric" archiveVersion = rootProject.version as String - val intermediaryRemap = project(":fabric-intermediary").tasks.named("remapJar") - val officialRemap = project(":fabric-official").tasks.named("remapJar") - dependsOn(intermediaryRemap) - dependsOn(officialRemap) - nestedJars.from(intermediaryRemap) - nestedJars.from(officialRemap) + // Nest variant remapJars by file path (not by task reference), so the aggregator + // doesn't trigger full project configuration of the variants — which would + // inject dev/namedElements jars into our include configuration. + dependsOn(":fabric-intermediary:remapJar", ":fabric-official:remapJar") + nestedJars.from( + project(":fabric-intermediary").layout.buildDirectory.file( + "libs/${rootProject.name}-fabric-intermediary-${rootProject.version}.jar" + ) + ) + nestedJars.from( + project(":fabric-official").layout.buildDirectory.file( + "libs/${rootProject.name}-fabric-official-${rootProject.version}.jar" + ) + ) } } From c05a8b43885f03ed9cf782974f6c7b558a4ac7f0 Mon Sep 17 00:00:00 2001 From: Axionize <154778082+Axionize@users.noreply.github.com> Date: Sat, 23 May 2026 06:39:14 -0400 Subject: [PATCH 03/34] fabric-intermediary: add minecraft <26 upper bound --- fabric-intermediary/src/main/resources/fabric.mod.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fabric-intermediary/src/main/resources/fabric.mod.json b/fabric-intermediary/src/main/resources/fabric.mod.json index fb543c99e5..4ca13bc577 100644 --- a/fabric-intermediary/src/main/resources/fabric.mod.json +++ b/fabric-intermediary/src/main/resources/fabric.mod.json @@ -29,6 +29,8 @@ }, "accessWidener": "grimac.accesswidener", "depends": { + "minecraft": "\u003e\u003d1.16.1 \u003c26", + "java": "\u003e\u003d17", "fabricloader": "\u003e\u003d0.16", "fabric-lifecycle-events-v1": "*", "packetevents": "*" From d9923836d4a4f92be002ed5adfd93a6757dbfe2f Mon Sep 17 00:00:00 2001 From: Axionize <154778082+Axionize@users.noreply.github.com> Date: Sat, 23 May 2026 17:13:24 -0400 Subject: [PATCH 04/34] fabric-intermediary mcXXXX: add minecraft <26 upper bounds MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Each mc11* subproject's fabric.mod.json declared an intermediary- namespaced accessWidener but no MC upper bound, so on a 26.X server Fabric Loader read the AW (loadClassTweakers runs before mod filtering) and crashed with: Namespace (intermediary) does not match current runtime namespace (official) Adding `<26` to each mcXXXX's minecraft constraint short-circuits the mod load before AW processing. Verified: fabric-26.1.2 boots clean with only fabric-official + mc261 loaded; fabric-1.21.11 still loads the full chain (mc1161 mc1171 mc1194 mc1205 mc12111) without regression. Note: mc12111's modid is `grimac-mc1215` (pre-existing typo from before this restructure — not touched here). --- fabric-intermediary/mc1161/src/main/resources/fabric.mod.json | 2 +- fabric-intermediary/mc1171/src/main/resources/fabric.mod.json | 2 +- fabric-intermediary/mc1194/src/main/resources/fabric.mod.json | 2 +- fabric-intermediary/mc1205/src/main/resources/fabric.mod.json | 2 +- fabric-intermediary/mc12111/src/main/resources/fabric.mod.json | 2 +- fabric-intermediary/src/main/resources/fabric.mod.json | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/fabric-intermediary/mc1161/src/main/resources/fabric.mod.json b/fabric-intermediary/mc1161/src/main/resources/fabric.mod.json index d13c081e23..08383c1027 100644 --- a/fabric-intermediary/mc1161/src/main/resources/fabric.mod.json +++ b/fabric-intermediary/mc1161/src/main/resources/fabric.mod.json @@ -17,7 +17,7 @@ "accessWidener": "grimac.accesswidener", "depends": { "fabricloader": "\u003e\u003d0.16", - "minecraft": ">=1.16.1", + "minecraft": ">=1.16.1 \u003c26", "fabric-resource-loader-v0": "*" } } diff --git a/fabric-intermediary/mc1171/src/main/resources/fabric.mod.json b/fabric-intermediary/mc1171/src/main/resources/fabric.mod.json index 753189fb25..9411c3ebae 100644 --- a/fabric-intermediary/mc1171/src/main/resources/fabric.mod.json +++ b/fabric-intermediary/mc1171/src/main/resources/fabric.mod.json @@ -16,7 +16,7 @@ }, "accessWidener": "grimac.accesswidener", "depends": { - "minecraft": ">=1.17", + "minecraft": ">=1.17 \u003c26", "fabric-lifecycle-events-v1": "*" } } diff --git a/fabric-intermediary/mc1194/src/main/resources/fabric.mod.json b/fabric-intermediary/mc1194/src/main/resources/fabric.mod.json index 713a2b7f1e..3fce5900ad 100644 --- a/fabric-intermediary/mc1194/src/main/resources/fabric.mod.json +++ b/fabric-intermediary/mc1194/src/main/resources/fabric.mod.json @@ -16,7 +16,7 @@ }, "accessWidener": "grimac.accesswidener", "depends": { - "minecraft": ">=1.19", + "minecraft": ">=1.19 \u003c26", "fabric-lifecycle-events-v1": "*" } } diff --git a/fabric-intermediary/mc1205/src/main/resources/fabric.mod.json b/fabric-intermediary/mc1205/src/main/resources/fabric.mod.json index 9ab853515a..1b1d0dca1c 100644 --- a/fabric-intermediary/mc1205/src/main/resources/fabric.mod.json +++ b/fabric-intermediary/mc1205/src/main/resources/fabric.mod.json @@ -16,7 +16,7 @@ }, "accessWidener": "grimac.accesswidener", "depends": { - "minecraft": ">=1.20", + "minecraft": ">=1.20 \u003c26", "fabric-lifecycle-events-v1": "*" } } diff --git a/fabric-intermediary/mc12111/src/main/resources/fabric.mod.json b/fabric-intermediary/mc12111/src/main/resources/fabric.mod.json index b03e4f81c6..efa6232cce 100644 --- a/fabric-intermediary/mc12111/src/main/resources/fabric.mod.json +++ b/fabric-intermediary/mc12111/src/main/resources/fabric.mod.json @@ -16,7 +16,7 @@ }, "accessWidener": "grimac.accesswidener", "depends": { - "minecraft": ">=1.21.2", + "minecraft": ">=1.21.2 \u003c26", "fabric-lifecycle-events-v1": "*" } } diff --git a/fabric-intermediary/src/main/resources/fabric.mod.json b/fabric-intermediary/src/main/resources/fabric.mod.json index 4ca13bc577..f867c1c517 100644 --- a/fabric-intermediary/src/main/resources/fabric.mod.json +++ b/fabric-intermediary/src/main/resources/fabric.mod.json @@ -20,6 +20,7 @@ "mixins": [ "grimac.mixins.json" ], + "accessWidener": "grimac.accesswidener", "custom": { "loom:injected_interfaces": { "net/minecraft/world/level/Level": [ @@ -27,7 +28,6 @@ ] } }, - "accessWidener": "grimac.accesswidener", "depends": { "minecraft": "\u003e\u003d1.16.1 \u003c26", "java": "\u003e\u003d17", From 7b86f34b8177920d16d7b20c00d1963f9bf37942 Mon Sep 17 00:00:00 2001 From: Axionize <154778082+Axionize@users.noreply.github.com> Date: Sat, 23 May 2026 22:19:52 -0400 Subject: [PATCH 05/34] deps: bump packetevents to 2.12.2+6dca3cc00 (fabric pre-Via listener fix) --- libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs.versions.toml b/libs.versions.toml index 86a055d297..3748cbb15b 100644 --- a/libs.versions.toml +++ b/libs.versions.toml @@ -12,7 +12,7 @@ cloud-processors-requirements = "1.0.0-rc.1" floodgate-api = "2.0-SNAPSHOT" geyser-base-api = "1.0.2" grim-api = "1.5.0.3-10fed42" -packetevents = "2.12.2+c44d85c-SNAPSHOT" +packetevents = "2.12.2+6dca3cc00-SNAPSHOT" placeholderapi = "2.11.6" viaversion = "5.9.0" From 7087a71a2d7d40b2d4bdbc27a0cb45be9007a6b7 Mon Sep 17 00:00:00 2001 From: Axionize <154778082+Axionize@users.noreply.github.com> Date: Sat, 23 May 2026 22:37:26 -0400 Subject: [PATCH 06/34] fabric-official: wire scaffold entrypoint + mc261 chain participant MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mirror of packetevents-public fad3d33aa. Previously fabric-official shipped no source code, so fabric-loader saw a mod with no entrypoints. This wires a minimal chain-loader scaffold: * GrimACFabricOfficialEntryPoint is registered as preLaunch + main. It discovers grim26ChainLoad entrypoints, sorts them by descending protocol version, and logs the participant count. Full anticheat startup stays deferred because GrimACFabricLoaderPlugin transitively references CommandSourceStack / MinecraftServer / ServerLifecycleEvents which need a 26.X-mapped build to compile. * mc261 declares Grim26ChainEntrypoint as grim26ChainLoad, reporting ServerVersion.V_26_1. Currently a thin marker — grows into a full GrimACFabricLoaderPlugin equivalent once 26.X Mojang-named sources can be compiled. * Grim26ChainEntryPoint interface lives in fabric-official rather than shared with fabric-intermediary because intermediary's GrimACFabricLoaderPlugin carries MC type refs we can't pull. Both branches load mutually exclusively (depends.minecraft ranges <26 vs >=26.1) so the parallel package layout doesn't collide at runtime. * Source stays free of net.minecraft.* references; PE's pure-Java api jar is the only library compileOnly'd into fabric-official + mc261. Loom configures cleanly against intermediary:0.0.0:v2. --- fabric-official/build.gradle.kts | 20 +++++++---- .../fabric/mc261/Grim26ChainEntrypoint.java | 12 +++++++ .../mc261/src/main/resources/fabric.mod.json | 11 ++++-- .../GrimACFabricOfficialEntryPoint.java | 35 +++++++++++++++++++ .../fabric/loader/Grim26ChainEntryPoint.java | 12 +++++++ .../src/main/resources/fabric.mod.json | 14 ++++++-- 6 files changed, 91 insertions(+), 13 deletions(-) create mode 100644 fabric-official/mc261/src/main/java/ac/grim/grimac/platform/fabric/mc261/Grim26ChainEntrypoint.java create mode 100644 fabric-official/src/main/java/ac/grim/grimac/platform/fabric/GrimACFabricOfficialEntryPoint.java create mode 100644 fabric-official/src/main/java/ac/grim/grimac/platform/fabric/loader/Grim26ChainEntryPoint.java diff --git a/fabric-official/build.gradle.kts b/fabric-official/build.gradle.kts index 3a167025a9..9a45851f0c 100644 --- a/fabric-official/build.gradle.kts +++ b/fabric-official/build.gradle.kts @@ -18,16 +18,20 @@ java { dependencies { minecraft("com.mojang:minecraft:$minecraft_version") - // Mojang stopped publishing official mappings starting MC 26.X and Yarn has no 26.X - // build either; Fabric intermediary publishes the empty `0.0.0` mapping for it. - // fabric-official is currently a structural stub that loads on 26.X but cannot - // reference meaningful MC symbols until usable mappings land. + // MC 26.X jars are pre-deobfuscated with Mojang's official names, but neither + // Mojang's manifest nor FabricMC publishes a tiny intermediary mapping for 26.X. + // The 0.0.0:v2 stub is the only mapping the maven currently serves; Loom configures + // against it cleanly as long as source code references no MC types. Per-version + // source compiled against Mojang names lands once a real 26.X intermediary mapping + // (or first-class no-mapping Loom support) is available. mappings("net.fabricmc:intermediary:0.0.0:v2") modImplementation(libs.fabric.loader) - // PE is intentionally NOT a dep here: PE's accessWidener is intermediary-namespaced - // and conflicts with the intermediary 0.0.0 mappings used by fabric-official. - // Bring PE back in once usable 26.X mappings exist and we can compile against named. + // PE's pure-Java api jar is safe to pull (no accessWidener inside). The fabric + // variant is intentionally excluded — it ships an intermediary-namespaced AW that + // Loom would try to remap against the 0.0.0 stub. Source must avoid net.minecraft.* + // references so Loom's source remap is a no-op. + compileOnly(libs.packetevents.api) compileOnly("org.slf4j:slf4j-api:2.0.17") compileOnly("org.apache.logging.log4j:log4j-api:2.24.3") } @@ -114,6 +118,8 @@ allprojects { subprojects { dependencies { implementation(project(":fabric-official", configuration = "namedElements")) + val libsx = rootProject.extensions.getByType().named("libs") + compileOnly(libsx.findLibrary("packetevents-api").get()) } } diff --git a/fabric-official/mc261/src/main/java/ac/grim/grimac/platform/fabric/mc261/Grim26ChainEntrypoint.java b/fabric-official/mc261/src/main/java/ac/grim/grimac/platform/fabric/mc261/Grim26ChainEntrypoint.java new file mode 100644 index 0000000000..eee2141a84 --- /dev/null +++ b/fabric-official/mc261/src/main/java/ac/grim/grimac/platform/fabric/mc261/Grim26ChainEntrypoint.java @@ -0,0 +1,12 @@ +package ac.grim.grimac.platform.fabric.mc261; + +import ac.grim.grimac.platform.fabric.loader.Grim26ChainEntryPoint; +import com.github.retrooper.packetevents.manager.server.ServerVersion; + +public class Grim26ChainEntrypoint implements Grim26ChainEntryPoint { + + @Override + public ServerVersion getNativeVersion() { + return ServerVersion.V_26_1; + } +} diff --git a/fabric-official/mc261/src/main/resources/fabric.mod.json b/fabric-official/mc261/src/main/resources/fabric.mod.json index aaae36ba5b..3e880bc52d 100644 --- a/fabric-official/mc261/src/main/resources/fabric.mod.json +++ b/fabric-official/mc261/src/main/resources/fabric.mod.json @@ -3,9 +3,14 @@ "id": "grimac-fabric-mc261", "version": "${version}", "name": "GrimAC mc261", - "description": "Per-version source root for MC 26.1+. Stub.", - "license": "GPL-3.0", - "environment": "*", + "description": "Per-version chain participant for Minecraft 26.1+. Registers a grim26ChainLoad entrypoint so fabric-official can dispatch into 26.X-specific code once compiled against Mojang-named sources.", + "license": "GPLv3", + "environment": "server", + "entrypoints": { + "grim26ChainLoad": [ + "ac.grim.grimac.platform.fabric.mc261.Grim26ChainEntrypoint" + ] + }, "depends": { "minecraft": ">=26.1", "java": ">=25", diff --git a/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/GrimACFabricOfficialEntryPoint.java b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/GrimACFabricOfficialEntryPoint.java new file mode 100644 index 0000000000..b029957a3c --- /dev/null +++ b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/GrimACFabricOfficialEntryPoint.java @@ -0,0 +1,35 @@ +package ac.grim.grimac.platform.fabric; + +import ac.grim.grimac.platform.fabric.loader.Grim26ChainEntryPoint; +import net.fabricmc.api.ModInitializer; +import net.fabricmc.loader.api.FabricLoader; +import net.fabricmc.loader.api.entrypoint.PreLaunchEntrypoint; + +import java.util.List; +import java.util.logging.Logger; + +// Scaffold entrypoint for the 26.X branch. Discovers grim26ChainLoad entrypoints so +// the chain-loader contract participates structurally, but does NOT bring up the +// full GrimAC platform — that requires MC-typed sources (CommandSourceStack, +// MinecraftServer, ServerLifecycleEvents) which need real 26.X mappings to compile. +public class GrimACFabricOfficialEntryPoint implements PreLaunchEntrypoint, ModInitializer { + + private static final Logger LOGGER = Logger.getLogger("grimac-fabric-official"); + + @Override + public void onPreLaunch() { + } + + @Override + public void onInitialize() { + FabricLoader loader = FabricLoader.getInstance(); + List entries = + loader.getEntrypoints("grim26ChainLoad", Grim26ChainEntryPoint.class); + entries.sort((a, b) -> + b.getNativeVersion().getProtocolVersion() - a.getNativeVersion().getProtocolVersion()); + + LOGGER.info("GrimAC (fabric-official) initialized in scaffold mode on " + + loader.getEnvironmentType() + "; chain participants registered: " + entries.size() + + ". Anticheat is inactive on 26.X until per-version sources land."); + } +} diff --git a/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/loader/Grim26ChainEntryPoint.java b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/loader/Grim26ChainEntryPoint.java new file mode 100644 index 0000000000..7c114ef930 --- /dev/null +++ b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/loader/Grim26ChainEntryPoint.java @@ -0,0 +1,12 @@ +package ac.grim.grimac.platform.fabric.loader; + +import com.github.retrooper.packetevents.manager.server.ServerVersion; + +// fabric-official's chain interface intentionally diverges from fabric-intermediary's +// GrimACFabricLoaderPlugin contract: that abstract carries MC type references +// (CommandSourceStack, MinecraftServer) which require Mojang-named compile-time +// sources. mc261 entrypoints register their native version here and grow into a +// full loader once 26.X-mapped sources are available. +public interface Grim26ChainEntryPoint { + ServerVersion getNativeVersion(); +} diff --git a/fabric-official/src/main/resources/fabric.mod.json b/fabric-official/src/main/resources/fabric.mod.json index b782537e17..e6f1a1fc71 100644 --- a/fabric-official/src/main/resources/fabric.mod.json +++ b/fabric-official/src/main/resources/fabric.mod.json @@ -3,9 +3,17 @@ "id": "grimac-fabric-official", "version": "${version}", "name": "GrimAC (Fabric, official mappings)", - "description": "GrimAC variant for Minecraft 26.X. Structural stub — meaningful MC-typed code awaits mapping availability.", - "license": "GPL-3.0", - "environment": "*", + "description": "GrimAC variant for Minecraft 26.X. Hosts the chain-loader scaffold; per-version subprojects contribute grim26ChainLoad entrypoints. Anticheat is inactive on this branch — see source for status.", + "license": "GPLv3", + "environment": "server", + "entrypoints": { + "preLaunch": [ + "ac.grim.grimac.platform.fabric.GrimACFabricOfficialEntryPoint" + ], + "main": [ + "ac.grim.grimac.platform.fabric.GrimACFabricOfficialEntryPoint" + ] + }, "depends": { "minecraft": ">=26.1", "java": ">=25", From bbe7c47c3cccace0aea067d33b155ed325269dfc Mon Sep 17 00:00:00 2001 From: Axionize <154778082+Axionize@users.noreply.github.com> Date: Sun, 24 May 2026 01:24:56 -0400 Subject: [PATCH 07/34] deps: bump PE to 2d8b75d21 (erased-bridge); restore grimac modid via provides MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PE 2d8b75d21 hoists the fabric bridge surface into fabric-common with MC types widened to Object — Grim's compile dep is unchanged (still uses the PE public API which lives in :api), but the published `packetevents- fabric` aggregator now also re-exports `packetevents-fabric-common` as a transitive maven dep so this build picks up the moved FQNs. GrimExtensionManager.getPlugin("GrimAC") looks the mod up by lowercased modId via FabricLoader.getModContainer("grimac"). The earlier codex-driven rename to `grimac-fabric-intermediary` (to differentiate from a future `grimac-fabric-official`) broke that lookup at runtime — every per-version GrimACFabricXXLoaderPlugin constructor failed in its supertype's getPlugin("GrimAC") call. Declaring `provides: ["grimac"]` keeps the explicit id distinct while restoring the "grimac" alias the extension manager expects. Verified by booting a fabric-1.21.11 server with these mods plus fabric-api 0.141.4+1.21.11 — `Done (0.303s)! For help, type "help"`, `You are using the latest version of GrimAC`. End-to-end login smoke (handshake → LoginStart → LoginSuccess → LoginAck → CONFIGURATION RegistryData reception) passes without PacketProcessException, the exact path PR #9 broke on fabric. --- fabric-intermediary/src/main/resources/fabric.mod.json | 1 + libs.versions.toml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/fabric-intermediary/src/main/resources/fabric.mod.json b/fabric-intermediary/src/main/resources/fabric.mod.json index f867c1c517..78eb1f6051 100644 --- a/fabric-intermediary/src/main/resources/fabric.mod.json +++ b/fabric-intermediary/src/main/resources/fabric.mod.json @@ -1,6 +1,7 @@ { "schemaVersion": 1, "id": "grimac-fabric-intermediary", + "provides": ["grimac"], "version": "${version}", "name": "GrimAC", "description": "Libre simulation anticheat designed for 26.1 with 1.8-26.1 support, powered by PacketEvents 2.0.", diff --git a/libs.versions.toml b/libs.versions.toml index 3748cbb15b..ec6cb64215 100644 --- a/libs.versions.toml +++ b/libs.versions.toml @@ -12,7 +12,7 @@ cloud-processors-requirements = "1.0.0-rc.1" floodgate-api = "2.0-SNAPSHOT" geyser-base-api = "1.0.2" grim-api = "1.5.0.3-10fed42" -packetevents = "2.12.2+6dca3cc00-SNAPSHOT" +packetevents = "2.12.2+2d8b75d21-SNAPSHOT" placeholderapi = "2.11.6" viaversion = "5.9.0" From 2de3ba4a27e7f48142eddf1d2a39932f4b189952 Mon Sep 17 00:00:00 2001 From: Axionize <154778082+Axionize@users.noreply.github.com> Date: Sun, 24 May 2026 02:17:21 -0400 Subject: [PATCH 08/34] deps: bump PE to 139086285 (fabric-official Mojang-named, 26.1.2 verified) --- libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs.versions.toml b/libs.versions.toml index ec6cb64215..0ca0157353 100644 --- a/libs.versions.toml +++ b/libs.versions.toml @@ -12,7 +12,7 @@ cloud-processors-requirements = "1.0.0-rc.1" floodgate-api = "2.0-SNAPSHOT" geyser-base-api = "1.0.2" grim-api = "1.5.0.3-10fed42" -packetevents = "2.12.2+2d8b75d21-SNAPSHOT" +packetevents = "2.12.2+139086285-SNAPSHOT" placeholderapi = "2.11.6" viaversion = "5.9.0" From cc89bee2a16280a9c1149203760469a90ee10f31 Mon Sep 17 00:00:00 2001 From: Axionize <154778082+Axionize@users.noreply.github.com> Date: Sun, 24 May 2026 03:05:55 -0400 Subject: [PATCH 09/34] =?UTF-8?q?fabric:=20lift=20PE=20JiJ=20to=20aggregat?= =?UTF-8?q?or=20level=20=E2=80=94=20single=20jar,=20no=20per-MC=20PE=20gat?= =?UTF-8?q?ing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Before: PE was JiJ'd inside grimac-fabric-intermediary via modImplementation. The intermediary nested mod has depends.minecraft<26, so on a 26.1.2 server fabric-loader skips loading it AND every PE jar nested inside it. The result was that on 26.1.2, PE was silently absent even though grimac-fabric-official loaded. Fix: move the PE JiJ up one level, to the top-level fabric/ aggregator's include() block. The aggregator's depends.minecraft is >=1.16.1 (no upper bound), so PE is active across the full MC range covered by either nested Grim variant. grimac-fabric-intermediary now only depends on PE at compile time (compileOnly + the per-mcXXXX subproject's compileOnly via the subprojects { ... } dependencies block). End-to-end verified on the live test-server profiles with a single distributable jar (no separate PE deploy): * fabric-1.21.11: bot joins, full PLAY state, no PacketProcessException. * fabric-1.19.4: bot joins, full PLAY state, no PacketProcessException. * fabric-26.1.2: server boots in 0.262s with `grimac` + `packetevents` mods loaded, chain participants mc261 (Grim) + mc261 (PE) active. Also picks up the PE-side dedup work (PE published at 2.12.2+b99c4b91a): shared deps JiJ'd once at PE aggregator level instead of inside each fabric-intermediary/fabric-official wrapper jar. Grim's aggregator jar size after both fixes: 12.5MB (was 16.6MB pre-cleanup). Known residual duplication: PE-api still ends up bundled inside both the PE aggregator JiJ and grimac-fabric-intermediary's nested JiJ, because Grim's :common module declares api(libs.packetevents.api) (consumed by spigot, fabric, etc.). Eliminating that final 4.2MB requires shifting :common's PE dep to compileOnly and adding it explicitly per platform — out of scope for this branch. --- fabric-intermediary/build.gradle.kts | 17 ++++++++++------- fabric/build.gradle.kts | 6 ++++++ libs.versions.toml | 2 +- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/fabric-intermediary/build.gradle.kts b/fabric-intermediary/build.gradle.kts index b39f141b7d..9a56a0cf40 100644 --- a/fabric-intermediary/build.gradle.kts +++ b/fabric-intermediary/build.gradle.kts @@ -20,15 +20,14 @@ dependencies { modImplementation(libs.cloud.fabric) modImplementation(libs.fabric.loader) - if (BuildConfig.shadePE) { - modImplementation(libs.packetevents.fabric) - } else { - compileOnly(libs.packetevents.fabric) - } + // PE is JiJ'd at the top-level fabric/ aggregator (so it loads on every MC range + // the aggregator covers, not just the intermediary range). Here PE is compile-only: + // declaring modImplementation/modApi would also nest the api/transitive jars inside + // grimac-fabric-intermediary's published jar, which on a 26.1.2 server would never + // load because the intermediary mod itself is gated <26. + compileOnly(libs.packetevents.fabric) compileOnly("org.slf4j:slf4j-api:2.0.17") compileOnly("org.apache.logging.log4j:log4j-api:2.24.3") - - modApi(libs.packetevents.fabric) } // The configurations below will only apply to :fabric and its submodules, not its siblings or the root project @@ -132,6 +131,10 @@ subprojects { dependencies { // configuration = "namedElements" required when depending on another loom project implementation(project(":fabric-intermediary", configuration = "namedElements")) + // PE is JiJ'd at fabric/ (aggregator); per-version submodules just need it on + // the compile classpath. compileOnly avoids re-nesting PE inside each mcXXXX jar. + val libsx = rootProject.extensions.getByType().named("libs") + compileOnly(libsx.findLibrary("packetevents-fabric").get()) } } diff --git a/fabric/build.gradle.kts b/fabric/build.gradle.kts index 7a0efac151..6054e46f20 100644 --- a/fabric/build.gradle.kts +++ b/fabric/build.gradle.kts @@ -36,6 +36,12 @@ dependencies { // via `nestedJars.from(remapJar)` to avoid the double-JiJ of dev + remapped // artifacts. include(project(":fabric-common")) + + // PE lives at the aggregator level so its depends.minecraft >=1.16.1 means it + // stays active across the full MC range (1.16.1 → 26.X), regardless of whether + // the user's MC matches grimac-fabric-intermediary's range (<26) or + // grimac-fabric-official's range (>=26.1). + include(libs.packetevents.fabric) } publishing.publications.create("maven") { diff --git a/libs.versions.toml b/libs.versions.toml index 0ca0157353..8e19a2a085 100644 --- a/libs.versions.toml +++ b/libs.versions.toml @@ -12,7 +12,7 @@ cloud-processors-requirements = "1.0.0-rc.1" floodgate-api = "2.0-SNAPSHOT" geyser-base-api = "1.0.2" grim-api = "1.5.0.3-10fed42" -packetevents = "2.12.2+139086285-SNAPSHOT" +packetevents = "2.12.2+b99c4b91a-SNAPSHOT" placeholderapi = "2.11.6" viaversion = "5.9.0" From c514fed2c3853d05a7feff5a338b19dc36081a37 Mon Sep 17 00:00:00 2001 From: Axionize <154778082+Axionize@users.noreply.github.com> Date: Sun, 24 May 2026 03:22:02 -0400 Subject: [PATCH 10/34] fabric-official: document the real 26.X blocker (intermediary-bound deps) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous comment claimed the blocker was 'empty 0.0.0 intermediary mapping → source must avoid net.minecraft.* refs'. That turned out to be half-true — switching to net.fabricmc.fabric-loom (LoomNoRemap) cleanly solves the source-compile-against-Mojang-names problem, as proven on the PE side where fabric-official now ships real Mojang-named source. The actual blocker for Grim is downstream: cloud-fabric, fabric-permissions-api, and the fabric-api event modules Grim depends on all ship intermediary-named bytecode. With LoomNoRemap there's no runtime intermediary remap, so 'net.minecraft.class_1297' refs inside those libraries fail to link at compile time: cannot access class_1297 class file for net.minecraft.class_1297 not found Three paths out, none in this branch's scope: 1. FabricMC publishes a real 26.X intermediary mapping (drops in officialMojangMappings() and Grim builds normally). 2. cloud-fabric / fabric-permissions-api ship 26.X-native builds with Mojang-named bytecode. 3. Grim writes Mojang-name shims for each missing dep — measured at ~6-8h of focused work the previous audit run. Keep grimac-fabric-official as a scaffold mod: loads on 26.X without crashing (verified end-to-end on the live fabric-26.1.2 profile), logs chain participation, but the GrimAC anticheat engine itself is gated until one of the three paths above resolves. --- fabric-official/build.gradle.kts | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/fabric-official/build.gradle.kts b/fabric-official/build.gradle.kts index 9a45851f0c..d4f4a95309 100644 --- a/fabric-official/build.gradle.kts +++ b/fabric-official/build.gradle.kts @@ -18,19 +18,22 @@ java { dependencies { minecraft("com.mojang:minecraft:$minecraft_version") - // MC 26.X jars are pre-deobfuscated with Mojang's official names, but neither - // Mojang's manifest nor FabricMC publishes a tiny intermediary mapping for 26.X. - // The 0.0.0:v2 stub is the only mapping the maven currently serves; Loom configures - // against it cleanly as long as source code references no MC types. Per-version - // source compiled against Mojang names lands once a real 26.X intermediary mapping - // (or first-class no-mapping Loom support) is available. + // 26.X status — KNOWN BLOCKERS preventing a functional Grim anticheat engine on + // this branch (tracked as scaffold until they resolve): + // 1. FabricMC has not published a tiny intermediary mapping for 26.X. The 0.0.0:v2 + // stub is the only mapping the maven currently serves. + // 2. Switching to net.fabricmc.fabric-loom (LoomNoRemap) lets source compile + // against the pre-deobfuscated 26.X jar's Mojang names — that works fine for + // PE because PE has no fabric-ecosystem deps. Grim has hard deps on + // cloud-fabric, fabric-permissions-api, and fabric-api event modules, ALL of + // which ship intermediary-named bytecode. With LoomNoRemap there's no + // runtime intermediary remap, so those refs are dead. + // 3. Re-enable in steps once any of: FabricMC publishes a 26.X intermediary; + // cloud-fabric / fabric-permissions-api publish 26.X-native builds; or we + // write Mojang-name shims for each missing dep. mappings("net.fabricmc:intermediary:0.0.0:v2") modImplementation(libs.fabric.loader) - // PE's pure-Java api jar is safe to pull (no accessWidener inside). The fabric - // variant is intentionally excluded — it ships an intermediary-namespaced AW that - // Loom would try to remap against the 0.0.0 stub. Source must avoid net.minecraft.* - // references so Loom's source remap is a no-op. compileOnly(libs.packetevents.api) compileOnly("org.slf4j:slf4j-api:2.0.17") compileOnly("org.apache.logging.log4j:log4j-api:2.24.3") From ca832575ea223b85b1f36196d1ea0d20b87f9567 Mon Sep 17 00:00:00 2001 From: Axionize <154778082+Axionize@users.noreply.github.com> Date: Sun, 24 May 2026 03:57:01 -0400 Subject: [PATCH 11/34] =?UTF-8?q?fabric:=20address=20Grim=20codex=20review?= =?UTF-8?q?=20=E2=80=94=20drop=20PE-api=20dup=20+=20add=20repo.grim.ac?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Codex review on cc89bee2a found 1 HIGH + 1 MED: * HIGH: common's api(libs.packetevents.api) propagated through fabric-intermediary's implementation(project(':common')) dep and got picked up by jij-conventions, nesting packetevents-api-*.jar inside grimac-fabric-intermediary AGAIN (a second copy beyond the one already inside the aggregator-level packetevents-fabric.jar). Switched common to compileOnly(libs.packetevents.api) — every platform now bundles PE through its own JiJ path, and the api-scope leak vanishes. * MED: fabric/build.gradle.kts's include(libs.packetevents.fabric) had no repo.grim.ac/snapshots in its repositories block, so the aggregator could only resolve PE when mavenLocalOverride=true. Added the repository so CI / fresh checkouts work too. Net size: grimac-fabric.jar 12.5MB -> 8.6MB after the dedup. Verified all three MC versions still boot + accept clients with the single 8.6MB Grim aggregator jar deployed (no separate PE). --- common/build.gradle.kts | 11 ++++++----- fabric/build.gradle.kts | 6 ++++++ 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/common/build.gradle.kts b/common/build.gradle.kts index 0af73a2562..c68a81dad7 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -43,11 +43,12 @@ repositories { dependencies { - if (BuildConfig.shadePE) { - api(libs.packetevents.api) - } else { - compileOnly(libs.packetevents.api) - } + // PE-api stays compileOnly even when shadePE=true: each platform module + // bundles PE through its own JiJ path (fabric/ aggregator JiJs the + // packetevents-fabric meta-jar; spigot/ shades PE-api separately). Declaring + // api() here would let Grim's jij-conventions plugin nest packetevents-api + // a second time inside grimac-fabric-intermediary, doubling ~4.2MB. + compileOnly(libs.packetevents.api) api(libs.cloud.core) api(libs.cloud.processors.requirements) api(libs.configuralize) { diff --git a/fabric/build.gradle.kts b/fabric/build.gradle.kts index 6054e46f20..8d575692cb 100644 --- a/fabric/build.gradle.kts +++ b/fabric/build.gradle.kts @@ -22,6 +22,12 @@ repositories { includeGroup("net.fabricmc") includeGroup("net.fabricmc.fabric-api") } + // PE snapshots live here; the aggregator-level include(libs.packetevents.fabric) + // needs this resolvable even when mavenLocalOverride is off (CI / fresh checkouts). + exclusive("https://repo.grim.ac/snapshots") { + includeGroup("ac.grim.grimac") + includeGroup("com.github.retrooper") + } mavenCentral() } From 00d6c2a02fbd3bfccb4018fe5dc5742f5fed3b84 Mon Sep 17 00:00:00 2001 From: Axionize <154778082+Axionize@users.noreply.github.com> Date: Sun, 24 May 2026 04:03:15 -0400 Subject: [PATCH 12/34] test: add fabric-1.21.11 flight smoke scenario to repo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Was living only at the agent harness's fallback path (/workspace/Grim/.test-scenarios/); pull it into the repo's preferred location so the flight-catch verification travels with the branch. Mirrors the existing flight-smoke-paper-1.19.4 shape — runs a wurst client through the standard chat-based Flight toggle and expects Grim to flag NoFall/Simulation/GroundSpoof/Vertical within 5s. --- .../flight-smoke-fabric-1.21.11.json | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 .test-scenarios/flight-smoke-fabric-1.21.11.json diff --git a/.test-scenarios/flight-smoke-fabric-1.21.11.json b/.test-scenarios/flight-smoke-fabric-1.21.11.json new file mode 100644 index 0000000000..bb8b50e146 --- /dev/null +++ b/.test-scenarios/flight-smoke-fabric-1.21.11.json @@ -0,0 +1,19 @@ +{ + "name": "flight-smoke-fabric-1.21.11", + "description": "Wurst Flight against Grim on fabric-1.21.11 with the erased-bridge PE build", + "client": "1.21.11-wurst", + "player": "Offline", + "steps": [ + {"type": "RCON", "command": "op {player}"}, + {"type": "RCON", "command": "difficulty peaceful"}, + {"type": "SEND", "target": "client", "text": "connect localhost {mc_port}"}, + {"type": "ENDS_WITH", "target": "server", "pattern": "{player} joined the game", "timeout": 30}, + {"type": "WAIT", "duration": 2}, + {"type": "ENSURE_VERBOSE"}, + {"type": "CLEAR_MATCH_WINDOW", "target": "server"}, + {"type": "WURST_HACK", "hack": "Flight", "state": "on"}, + {"type": "WAIT", "duration": 4}, + {"type": "WURST_HACK", "hack": "Flight", "state": "off"}, + {"type": "ENDS_WITH", "target": "server", "pattern": "Grim\\W+{player} failed (NoFall|Simulation|GroundSpoof|Vertical)[^\\n]*\\(x[0-9]+\\)", "timeout": 5} + ] +} From d4f7bb554ab5a183f5e6d60298a91d92dd43df3d Mon Sep 17 00:00:00 2001 From: Axionize <154778082+Axionize@users.noreply.github.com> Date: Sun, 24 May 2026 04:15:38 -0400 Subject: [PATCH 13/34] =?UTF-8?q?fabric:=20aggregator=20depends.minecraft?= =?UTF-8?q?=20=E2=86=92=20union=20to=20actually-supported=20ranges?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mirror of PE 1a09ba6c5. Codex r2 MED on PE flagged the same issue on Grim's aggregator: '>=1.16.1' unbounded would accept any post-1.21.11 MC where no nested variant activates. Declare the union of intermediary's '<26' and official's '>=26.1.2 <26.2' so fabric-loader rejects unsupported MC versions at range-check time. --- fabric/src/main/resources/fabric.mod.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fabric/src/main/resources/fabric.mod.json b/fabric/src/main/resources/fabric.mod.json index 970c751a49..e35a2ed07e 100644 --- a/fabric/src/main/resources/fabric.mod.json +++ b/fabric/src/main/resources/fabric.mod.json @@ -7,7 +7,7 @@ "license": "GPL-3.0", "environment": "*", "depends": { - "minecraft": ">=1.16.1", + "minecraft": [">=1.16.1 <26", ">=26.1.2 <26.2"], "java": ">=17", "fabricloader": ">=0.16" } From af7aa9d17837932454c8ac8c16c52636e69502eb Mon Sep 17 00:00:00 2001 From: Axionize <154778082+Axionize@users.noreply.github.com> Date: Sun, 24 May 2026 04:18:38 -0400 Subject: [PATCH 14/34] =?UTF-8?q?ci:=20bump=20build=20JDK=20from=2021=20?= =?UTF-8?q?=E2=86=92=2025=20for=20fabric-official=20LoomNoRemap?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Codex r2 MED. Even though Java 25 is the toolchain target, Gradle itself needs to run on Java 25 because fabric-official's LoomNoRemap plugin configures the MC 26.1.2 jar (Java 25 bytecode) during the Gradle configuration phase, before toolchain selection kicks in. On the previous Java 21 runner, configuring :fabric-official failed with: java.lang.IllegalStateException: Minecraft 26.1.2 requires Java 25 but Gradle is using 21 This unblocks :fabric:remapJar and :fabric:publishMods on CI. --- .github/workflows/build-and-publish.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-and-publish.yml b/.github/workflows/build-and-publish.yml index c4881c3013..0acb5551f3 100644 --- a/.github/workflows/build-and-publish.yml +++ b/.github/workflows/build-and-publish.yml @@ -64,10 +64,13 @@ jobs: persist-credentials: false fetch-depth: 0 - - name: Set up JDK 21 + - name: Set up JDK 25 uses: actions/setup-java@v4 with: - java-version: '21' + # fabric-official's LoomNoRemap setup needs the GRADLE JVM itself on + # Java 25 (toolchain alone isn't enough — Loom configures fabric-official + # during Gradle config phase, before toolchain selection kicks in). + java-version: '25' distribution: 'temurin' server-id: github settings-path: ${{ github.workspace }} From cb02f33eeb3895aeb22913ab1853b4ab69863d0c Mon Sep 17 00:00:00 2001 From: Axionize <154778082+Axionize@users.noreply.github.com> Date: Sun, 24 May 2026 05:09:33 -0400 Subject: [PATCH 15/34] fabric-official: tighten 26.X range + declare PE depend (codex r3) Two metadata fixes flagged by codex r3: * `depends.minecraft` was unbounded (`>=26.1`) on both the fabric-official wrapper and mc261's per-version chain participant, contradicting the aggregator's `>=26.1.2 <26.2` range. Tightened both to match so the nested metadata is self-consistent and won't claim support for hypothetical 26.2+ snapshots. * fabric-official's chain code references PacketEvents API types (ServerVersion, etc.) but the mod metadata didn't declare a `packetevents` dep. Loader could in principle init fabric-official before PE was on the classpath, surfacing as NoClassDefFoundError. Added `packetevents: "*"` to both depends blocks, matching the pattern fabric-intermediary already follows. --- fabric-official/mc261/src/main/resources/fabric.mod.json | 5 +++-- fabric-official/src/main/resources/fabric.mod.json | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/fabric-official/mc261/src/main/resources/fabric.mod.json b/fabric-official/mc261/src/main/resources/fabric.mod.json index 3e880bc52d..dbd161eb06 100644 --- a/fabric-official/mc261/src/main/resources/fabric.mod.json +++ b/fabric-official/mc261/src/main/resources/fabric.mod.json @@ -12,8 +12,9 @@ ] }, "depends": { - "minecraft": ">=26.1", + "minecraft": ">=26.1.2 <26.2", "java": ">=25", - "fabricloader": ">=0.19" + "fabricloader": ">=0.19", + "packetevents": "*" } } diff --git a/fabric-official/src/main/resources/fabric.mod.json b/fabric-official/src/main/resources/fabric.mod.json index e6f1a1fc71..bd1ed0624b 100644 --- a/fabric-official/src/main/resources/fabric.mod.json +++ b/fabric-official/src/main/resources/fabric.mod.json @@ -15,8 +15,9 @@ ] }, "depends": { - "minecraft": ">=26.1", + "minecraft": ">=26.1.2 <26.2", "java": ">=25", - "fabricloader": ">=0.19" + "fabricloader": ">=0.19", + "packetevents": "*" } } From 39cfd56fafa09c95e204a9f55bac34c63ef6017a Mon Sep 17 00:00:00 2001 From: Axionize <154778082+Axionize@users.noreply.github.com> Date: Sun, 24 May 2026 05:15:39 -0400 Subject: [PATCH 16/34] fabric-official: document the intentional empty-stub-mapping pattern MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Codex r3 flagged the `fabric-loom` + intermediary:0.0.0:v2 wiring as "not LoomNoRemap." Empirically true (the short plugin id resolves to LoomGradlePlugin, not LoomNoRemapGradlePlugin), but the effective behavior is identical here: the empty intermediary stub has zero entries, so the named→intermediary remap pass is a no-op. Mojang-named bytecode passes through remapJar untouched. Adding a comment to document that this is deliberate — not a mis-config. Switching to PE's actual LoomNoRemap pattern would require non-trivial plumbing (different jar task, no `namedElements` variant, different aggregator JiJ wiring) for no functional benefit while the scaffold has no MC-touching source. Worth revisiting if and when real 26.X anticheat code lands and uses intermediary-typed transitive deps (which is the real blocker, not the plugin choice). --- fabric-official/build.gradle.kts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/fabric-official/build.gradle.kts b/fabric-official/build.gradle.kts index d4f4a95309..3c8e05dedb 100644 --- a/fabric-official/build.gradle.kts +++ b/fabric-official/build.gradle.kts @@ -3,6 +3,18 @@ import versioning.BuildConfig val minecraft_version: String by project val fabric_version: String by project +// Plugin choice rationale: +// This module uses the short `fabric-loom` plugin (LoomGradlePlugin, the remap +// variant) with `mappings(intermediary:0.0.0:v2)` — a published empty intermediary +// stub. Because the stub has zero entries, the named→intermediary remap pass is +// effectively a no-op, leaving Mojang-named bytecode untouched in remapJar output. +// This is intentional and matches the practical effect of LoomNoRemap +// (LoomNoRemapGradlePlugin via the fully-qualified `net.fabricmc.fabric-loom` id) +// without requiring the different jar/task/configuration plumbing that PE's +// fabric-official uses. See PE's fabric-official build.gradle.kts for the +// alternative pattern. Both produce equivalent jars when source contains no +// intermediary refs, which is the case here (and will remain the case when real +// 26.X-mojmap anticheat code lands — see KNOWN BLOCKERS comment below). plugins { `maven-publish` alias(libs.plugins.fabric.loom) From 9b33fbf6e24f2ff530f58058aff98bfc69a0aacc Mon Sep 17 00:00:00 2001 From: Axionize <154778082+Axionize@users.noreply.github.com> Date: Sun, 24 May 2026 05:51:19 -0400 Subject: [PATCH 17/34] ci: tighten Modrinth game-versions to actual supported union MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The `>=1.16.1` predicate was over-claiming. fabric/build is a union of two ranges — intermediary variant `[1.16.1, 26)` and official variant `[26.1.2, 26.2)` — and the loader-side fabric.mod.json already encodes that union. mc-publish accepts multi-line YAML scalars as OR predicates (verified against Kir-Antipov/mc-publish@v3.3 README example), so the publish metadata can mirror the same shape. Filter behavior (game-version-filter default) is unchanged, so the published version list is a subset of what `>=1.16.1` produced plus 26.1.2 from the second predicate. Closes the last open codex r3 finding. --- .github/workflows/build-and-publish.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-and-publish.yml b/.github/workflows/build-and-publish.yml index 0acb5551f3..974bd741f7 100644 --- a/.github/workflows/build-and-publish.yml +++ b/.github/workflows/build-and-publish.yml @@ -269,7 +269,13 @@ jobs: changelog: ${{ steps.changelog.outputs.notes }} loaders: | fabric - game-versions: '>=1.16.1' + # Union of the two ranges actually shipped: intermediary + # variant covers [1.16.1, 26), official variant covers + # [26.1.2, 26.2). mc-publish reads multi-line YAML scalar + # as OR. Keep in sync with fabric.mod.json depends.minecraft. + game-versions: | + >=1.16.1 <26 + >=26.1.2 <26.2 dependencies: 'fabric-api' #------------------------------------------------------------------ From 3ebadd782dff478828c95854b4d482ac7723c1c1 Mon Sep 17 00:00:00 2001 From: Axionize <154778082+Axionize@users.noreply.github.com> Date: Sun, 24 May 2026 06:44:57 -0400 Subject: [PATCH 18/34] fabric-intermediary: drop provides: ["grimac"] (modid conflict with aggregator) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The nested fabric-intermediary jar was declaring `provides: ["grimac"]` while the top-level fabric aggregator already owns `id: "grimac"`. Fabric loader resolved the duplicate by keeping the aggregator and REJECTING the entire intermediary chain — so the JiJ tree showed grimac + fabric-common + packetevents loading, but grimac-fabric-intermediary / mc1161 / mc1171 / mc1194 / mc1205 / mc1215 were all silently dropped. Net effect on a fabric server: anticheat engine never initialized. Removing the provides alias lets fabric-intermediary register under its real modid. `FabricLoader.getModContainer("grimac")` still resolves to the aggregator, which has the same version, so the one consumer (MetricsFabric.java:100) continues to work. Verified: deploy to fabric-1.21.11 now responds to `/grim version` with the new build's version string instead of "Unknown command." --- fabric-intermediary/src/main/resources/fabric.mod.json | 1 - 1 file changed, 1 deletion(-) diff --git a/fabric-intermediary/src/main/resources/fabric.mod.json b/fabric-intermediary/src/main/resources/fabric.mod.json index 78eb1f6051..f867c1c517 100644 --- a/fabric-intermediary/src/main/resources/fabric.mod.json +++ b/fabric-intermediary/src/main/resources/fabric.mod.json @@ -1,7 +1,6 @@ { "schemaVersion": 1, "id": "grimac-fabric-intermediary", - "provides": ["grimac"], "version": "${version}", "name": "GrimAC", "description": "Libre simulation anticheat designed for 26.1 with 1.8-26.1 support, powered by PacketEvents 2.0.", From 5f0eaf4306b266b1bb4ecb0cc68f6ea8363ec0c9 Mon Sep 17 00:00:00 2001 From: Axionize <154778082+Axionize@users.noreply.github.com> Date: Sun, 24 May 2026 06:50:41 -0400 Subject: [PATCH 19/34] test: add fabric-1.19.4 + fabric-26.1.2 smoke scenarios MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - flight-smoke-fabric-1.19.4.json: mirror of the 1.21.11 Wurst Flight scenario but pointed at the 1.19.4-wurst client. Validates Grim catches movement cheats on the lower intermediary range. - connect-smoke-fabric-26.1.2.json: real 26.1.2 Wurst client connects to the fabric-official scaffold and the server reports the player via /list. Doesn't fire Wurst hacks (anticheat engine is inactive on the 26.X scaffold — documented in fabric-official source). The test verifies PE + scaffold load cleanly on Mojang-named MC. --- .../connect-smoke-fabric-26.1.2.json | 15 +++++++++++++++ .../flight-smoke-fabric-1.19.4.json | 19 +++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 .test-scenarios/connect-smoke-fabric-26.1.2.json create mode 100644 .test-scenarios/flight-smoke-fabric-1.19.4.json diff --git a/.test-scenarios/connect-smoke-fabric-26.1.2.json b/.test-scenarios/connect-smoke-fabric-26.1.2.json new file mode 100644 index 0000000000..5c49c70b40 --- /dev/null +++ b/.test-scenarios/connect-smoke-fabric-26.1.2.json @@ -0,0 +1,15 @@ +{ + "name": "connect-smoke-fabric-26.1.2", + "description": "Real Wurst 26.1.2 client connects to fabric-official scaffold. Verifies PE+scaffold load cleanly on Mojang-named MC; does not exercise anticheat (engine inactive on this branch — see fabric-official source comment).", + "client": "26.1.2-wurst", + "player": "Offline", + "steps": [ + {"type": "RCON", "command": "op {player}"}, + {"type": "RCON", "command": "difficulty peaceful"}, + {"type": "CLEAR_MATCH_WINDOW", "target": "both"}, + {"type": "SEND", "target": "client", "text": "connect localhost {mc_port}"}, + {"type": "ENDS_WITH", "target": "server", "pattern": "{player} joined the game", "timeout": 30}, + {"type": "WAIT", "duration": 4}, + {"type": "RCON_MATCHES", "command": "list", "pattern": "{player}", "assert": "contains:{player}"} + ] +} diff --git a/.test-scenarios/flight-smoke-fabric-1.19.4.json b/.test-scenarios/flight-smoke-fabric-1.19.4.json new file mode 100644 index 0000000000..bbefe502de --- /dev/null +++ b/.test-scenarios/flight-smoke-fabric-1.19.4.json @@ -0,0 +1,19 @@ +{ + "name": "flight-smoke-fabric-1.19.4", + "description": "Wurst Flight against Grim on fabric-1.19.4 with the erased-bridge PE build", + "client": "1.19.4-wurst", + "player": "Offline", + "steps": [ + {"type": "RCON", "command": "op {player}"}, + {"type": "RCON", "command": "difficulty peaceful"}, + {"type": "SEND", "target": "client", "text": "connect localhost {mc_port}"}, + {"type": "ENDS_WITH", "target": "server", "pattern": "{player} joined the game", "timeout": 30}, + {"type": "WAIT", "duration": 2}, + {"type": "ENSURE_VERBOSE"}, + {"type": "CLEAR_MATCH_WINDOW", "target": "server"}, + {"type": "WURST_HACK", "hack": "Flight", "state": "on"}, + {"type": "WAIT", "duration": 4}, + {"type": "WURST_HACK", "hack": "Flight", "state": "off"}, + {"type": "ENDS_WITH", "target": "server", "pattern": "Grim\\W+{player} failed (NoFall|Simulation|GroundSpoof|Vertical)[^\\n]*\\(x[0-9]+\\)", "timeout": 5} + ] +} From 2a24afb08a1a573c4e1f75dccdeafa118363990d Mon Sep 17 00:00:00 2001 From: Axionize <154778082+Axionize@users.noreply.github.com> Date: Sun, 24 May 2026 07:57:39 -0400 Subject: [PATCH 20/34] fabric-official: document the concrete 26.X port work needed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The prior comment hand-waved at "intermediary-bound deps" as the blocker. Replacing it with a per-category breakdown gathered by actually attempting the port (copy fabric-intermediary/src/main/java → fabric-official, pull :common, add compileOnly stubs for the missing deps, run :fabric-official:compileJava): 29 errors clustered into four buckets, each with a defined fix path. Keeping the file list in source so the next session that picks this up doesn't have to redo the same dig. State is unchanged — fabric-official still ships scaffold-only. The anticheat engine works on the intermediary chain (1.16.1-1.21.11); 26.X needs the four-category port described in the comment. --- fabric-official/build.gradle.kts | 50 +++++++++++++++++++++++--------- 1 file changed, 37 insertions(+), 13 deletions(-) diff --git a/fabric-official/build.gradle.kts b/fabric-official/build.gradle.kts index 3c8e05dedb..c1b0542c7e 100644 --- a/fabric-official/build.gradle.kts +++ b/fabric-official/build.gradle.kts @@ -30,19 +30,43 @@ java { dependencies { minecraft("com.mojang:minecraft:$minecraft_version") - // 26.X status — KNOWN BLOCKERS preventing a functional Grim anticheat engine on - // this branch (tracked as scaffold until they resolve): - // 1. FabricMC has not published a tiny intermediary mapping for 26.X. The 0.0.0:v2 - // stub is the only mapping the maven currently serves. - // 2. Switching to net.fabricmc.fabric-loom (LoomNoRemap) lets source compile - // against the pre-deobfuscated 26.X jar's Mojang names — that works fine for - // PE because PE has no fabric-ecosystem deps. Grim has hard deps on - // cloud-fabric, fabric-permissions-api, and fabric-api event modules, ALL of - // which ship intermediary-named bytecode. With LoomNoRemap there's no - // runtime intermediary remap, so those refs are dead. - // 3. Re-enable in steps once any of: FabricMC publishes a 26.X intermediary; - // cloud-fabric / fabric-permissions-api publish 26.X-native builds; or we - // write Mojang-name shims for each missing dep. + // 26.X anticheat port — concrete remaining work (audited via attempted + // source copy of fabric-intermediary, see commit history for the revert). + // Compile errors after pulling :common + compileOnly stubs fell into: + // + // A. Intermediary types in cloud-fabric + fabric-permissions-api public + // signatures (`Permissions.check(class_2168, String)`, etc.). javac + // can't resolve `class_NNNN` against Mojang-named MC, so any source + // file importing those APIs fails to compile. Fix: delete + // FabricPermissionRegistrationManager.java, FabricSenderFactory.java, + // command/FabricPlayerSelectorParser.java, manager/FabricParserDescriptorFactory.java + // and stub the loader plugin's getters to no-op equivalents. Grim's + // existing catch path (CloudHelper.create → NCDFE → silent fallback) + // lets the engine run without these. + // + // B. fabric-api event modules ship intermediary-named bytecode that + // doesn't link against 26.X Mojang names. ServerLifecycleEvents.SERVER_STARTING + // and ServerTickEvents.END_SERVER_TICK callers need to be replaced + // with direct mixins into MinecraftServer.runServer() and + // MinecraftServer.tickServer(). Affects GrimACFabricEntryPoint.java, + // initables/FabricTickEndEvent.java, scheduler/*. + // + // C. MC API drift 1.21.11 → 26.1.2. Sample symbols javac couldn't resolve: + // Entity.level (private — needs accessWidener applied at build), + // Player.inventory (private — accessWidener), CommandSourceStack.source + // (private — accessWidener), MinecraftServer.playerDataStorage + // (protected — accessWidener), PlayerDataStorage.playerDir (private — + // accessWidener), plus method renames in AbstractFabricPlatformServer + // (lines 13/17/39), FabricPlatformPlayerFactory (lines 113-114), + // AbstractFabricPlatformInventory (lines 20+ chains). Each needs the + // 26.1.2-mojmap call updated. + // + // D. The mc261 submodule needs concrete Fabric261PlatformServer + + // Fabric261PlatformPlayer + Fabric261LoaderPlugin analogous to mc12111 + // in fabric-intermediary. + // + // Order of operations for a future supervised session: A → B (mixin-driven + // events) → C (per-file API fixes) → D (mc261 wiring). Estimated 6-8h. mappings("net.fabricmc:intermediary:0.0.0:v2") modImplementation(libs.fabric.loader) From a822c02a05c7e047582c67322398795e9bb1dd71 Mon Sep 17 00:00:00 2001 From: Axionize <154778082+Axionize@users.noreply.github.com> Date: Sun, 24 May 2026 07:58:59 -0400 Subject: [PATCH 21/34] fabric-official: incorporate codex r5 review of 26.X port plan MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds category E (build/remap packaging — AW + mixin refmap + nested-jar viability under the empty intermediary stub) and re-sequences D first so compile targets exist before grinding source: D → E → A → B → C. --- fabric-official/build.gradle.kts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/fabric-official/build.gradle.kts b/fabric-official/build.gradle.kts index c1b0542c7e..b9e12aa2cf 100644 --- a/fabric-official/build.gradle.kts +++ b/fabric-official/build.gradle.kts @@ -65,8 +65,17 @@ dependencies { // Fabric261PlatformPlayer + Fabric261LoaderPlugin analogous to mc12111 // in fabric-intermediary. // - // Order of operations for a future supervised session: A → B (mixin-driven - // events) → C (per-file API fixes) → D (mc261 wiring). Estimated 6-8h. + // E. Build/remap packaging: prove access-widener application, + // mixin refmap generation, and nested-jar wiring all work under the + // empty intermediary:0.0.0:v2 stub before grinding through per-file + // API fixes. :common's existing AW assumptions may not apply cleanly + // to the no-op-remap pass — needs a smoke build before the real port. + // + // Order of operations (per codex r5 review): D first (write minimal mc261 + // platform/loader so compile targets exist) → E (verify the build pipeline + // mechanically) → A (strip cloud/perms surface) → B (mixin-driven events + // replacing fabric-api) → C (grind through Mojmap API drift last, once the + // architecture is proven). Estimated 6-8h supervised. mappings("net.fabricmc:intermediary:0.0.0:v2") modImplementation(libs.fabric.loader) From 22343a1afe37b78469fd165f5030bbb06993fcec Mon Sep 17 00:00:00 2001 From: Axionize <154778082+Axionize@users.noreply.github.com> Date: Sun, 24 May 2026 08:50:17 -0400 Subject: [PATCH 22/34] =?UTF-8?q?fabric-official:=20real=2026.X=20antichea?= =?UTF-8?q?t=20port=20=E2=80=94=20compile=20targets=20in=20place=20(Phase?= =?UTF-8?q?=20D)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Source code is a port of fabric-intermediary/src/main/java/ac/ac/grim/grimac/platform/fabric/* adapted to the 26.1.2 mojmap surface. Reuse-vs-duplication notes per the plan you signed off on: - :common is the engine + checks — pulled via `implementation(project(":common"))`, same as fabric-intermediary. Today this means common's transitive deps (adventure, cloud-core, GrimAPI, HikariCP, etc) get JiJ'd inside BOTH fabric-intermediary and fabric-official, costing ~4MB of duplication in the aggregator. Phase E follow-up: lift to aggregator-level JiJ. - :fabric-common — unchanged, JiJ'd at the aggregator once. Erased-bridge. - packetevents — already aggregator-JiJ'd, no change. PE's mc261 covers the 26.X packet pipeline; this port just consumes PE's platform-agnostic API. mc261 covers the full 26.1.X family. Verified empirically: 26.1 / 26.1.1 / 26.1.2 share an identical Java public/private signature surface (0 of 300 randomly sampled classes drift; 6 of 6 hand-picked critical classes — MS, ServerPlayer, ServerLevel, CommandSourceStack, Player, Entity — bit-equal javap -p -s). When 26.2 lands a real release a sibling breakpoint joins it. What this commit does: Phase A (strip intermediary-bound): - Delete FabricPermissionRegistrationManager, FabricSenderFactory, command/FabricPlayerSelectorParser, manager/FabricParserDescriptorFactory. - GrimACFabricLoaderPlugin replaced with a fabric-official variant whose getCommandService() returns no-op (matches the catch path the intermediary chain takes when cloud-fabric is unavailable — your design call, "Grim runs without commands"). getPermissionManager() no-op; vanilla op-level perm check via CommandSourceStack.permissions() .hasPermission(new Permission.HasCommandLevel(PermissionLevel.byId(2))). - NoopFabricSenderFactory implements just enough of SenderFactory to keep Sender wrapping alive. Phase B (fabric-api events → mixin-driven): - FabricServerEvents holds STARTING / STOPPING / END_TICK listener lists. Phase C will wire a MinecraftServer mixin to fire them. Until then the engine loads but doesn't actually tick. - GrimACFabricEntryPoint registers Grim's start/stop/tick on FabricServerEvents instead of ServerLifecycleEvents.SERVER_STARTING etc. Scheduler classes likewise. Entry point listens on `grim26MainLoad` (parallel to intermediary's `grimMainLoad`) for chain participants. Phase C (26.X mojmap drift fixes — landed inline): - ResourceKey.location() → .identifier() - CommandSourceStack.hasPermission(int) → .permissions().hasPermission(...) - MinecraftServer.getProfileCache().get(name) → .services().profileResolver().fetchByName(name) - MinecraftServer.getOperatorUserPermissionLevel() → .operatorUserPermissions().level().id() - Inventory.getSelected() → .getSelectedItem() - Player.displayClientMessage(...) → .sendSystemMessage(...) - GameProfile is now a Record: .getId() → .id(), .getName() → .name() Phase D (compile targets): - mc261 subproject: Fabric261LoaderPlugin (extends parent GrimACFabricLoaderPlugin, registered under grim26MainLoad), plus concrete Fabric261PlatformServer / Fabric261PlatformPlayer / Fabric261PlatformInventory / Fabric261GrimEntity / Fabric261MessageUtil / Fabric261ConversionUtil. Phase E (build wiring): - grimac.accesswidener copied from intermediary; loom.accessWidenerPath points at it so private MC fields used by :common resolve at compile. - :common added to fabric-official compile classpath; subprojects get it compileOnly + packetevents.api/fabric compileOnly. Compile clean: :fabric:remapJar BUILD SUCCESSFUL on JAVA_HOME=jbrsdk-25. Aggregator grows 8.6MB → 12.6MB; runtime hookup (the mixin) and JiJ dedup are the remaining Phase E/F items before the engine actually detects movement violations on a real 26.1.2 server. --- fabric-official/build.gradle.kts | 8 + .../fabric/mc261/Fabric261ConversionUtil.java | 29 ++ .../fabric/mc261/Fabric261GrimEntity.java | 40 +++ .../fabric/mc261/Fabric261LoaderPlugin.java | 31 ++ .../fabric/mc261/Fabric261MessageUtil.java | 17 ++ .../mc261/Fabric261PlatformInventory.java | 26 ++ .../fabric/mc261/Fabric261PlatformPlayer.java | 41 +++ .../fabric/mc261/Fabric261PlatformServer.java | 23 ++ .../fabric/mc261/Grim26ChainEntrypoint.java | 12 - .../mc261/src/main/resources/fabric.mod.json | 4 +- .../fabric/AbstractFabricPlatformServer.java | 46 +++ .../platform/fabric/FabricPlatformPlugin.java | 36 +++ .../platform/fabric/FabricServerEvents.java | 46 +++ .../fabric/GrimACFabricEntryPoint.java | 53 ++++ .../fabric/GrimACFabricLoaderPlugin.java | 138 +++++++++ .../entity/AbstractFabricGrimEntity.java | 68 +++++ .../fabric/initables/FabricBStats.java | 25 ++ .../fabric/initables/FabricTickEndEvent.java | 33 +++ .../manager/FabricItemResetHandler.java | 32 ++ .../FabricMessagePlaceHolderManager.java | 15 + .../manager/FabricPlatformPluginManager.java | 32 ++ .../fabric/mixins/LevelChunkMixin.java | 25 ++ .../platform/fabric/mixins/LevelMixin.java | 55 ++++ .../fabric/mixins/PistonBaseBlockMixin.java | 94 ++++++ .../fabric/mixins/ServerPlayerMixin.java | 17 ++ .../AbstractFabricPlatformInventory.java | 62 ++++ .../player/AbstractFabricPlatformPlayer.java | 164 +++++++++++ .../player/FabricOfflinePlatformPlayer.java | 26 ++ .../player/FabricPlatformPlayerFactory.java | 131 +++++++++ .../resolver/FabricResolverRegistrar.java | 183 ++++++++++++ .../scheduler/FabricAsyncScheduler.java | 118 ++++++++ .../scheduler/FabricEntityScheduler.java | 85 ++++++ .../FabricGlobalRegionScheduler.java | 76 +++++ .../scheduler/FabricPlatformScheduler.java | 133 +++++++++ .../scheduler/FabricRegionScheduler.java | 94 ++++++ .../fabric/scheduler/FabricTaskHandle.java | 28 ++ .../sender/NoopFabricSenderFactory.java | 87 ++++++ .../platform/fabric/utils/PolymerHook.java | 86 ++++++ .../utils/convert/FabricConversionUtil.java | 102 +++++++ .../utils/convert/IFabricConversionUtil.java | 9 + .../utils/message/IFabricMessageUtil.java | 9 + .../fabric/utils/message/JULoggerFactory.java | 18 ++ .../utils/message/Log4jBackedJULogger.java | 47 +++ .../utils/message/Slf4jBackedJULogger.java | 51 ++++ .../fabric/utils/metrics/BStatsConfig.java | 98 ++++++ .../fabric/utils/metrics/CustomChart.java | 37 +++ .../utils/metrics/JsonObjectBuilder.java | 210 +++++++++++++ .../fabric/utils/metrics/Metrics.java | 6 + .../fabric/utils/metrics/MetricsBase.java | 278 ++++++++++++++++++ .../fabric/utils/metrics/MetricsFabric.java | 110 +++++++ .../fabric/utils/thread/FabricFutureUtil.java | 15 + .../fabric/utils/world/LevelChunkUtil.java | 22 ++ .../src/main/resources/grimac.accesswidener | 7 + 53 files changed, 3224 insertions(+), 14 deletions(-) create mode 100644 fabric-official/mc261/src/main/java/ac/grim/grimac/platform/fabric/mc261/Fabric261ConversionUtil.java create mode 100644 fabric-official/mc261/src/main/java/ac/grim/grimac/platform/fabric/mc261/Fabric261GrimEntity.java create mode 100644 fabric-official/mc261/src/main/java/ac/grim/grimac/platform/fabric/mc261/Fabric261LoaderPlugin.java create mode 100644 fabric-official/mc261/src/main/java/ac/grim/grimac/platform/fabric/mc261/Fabric261MessageUtil.java create mode 100644 fabric-official/mc261/src/main/java/ac/grim/grimac/platform/fabric/mc261/Fabric261PlatformInventory.java create mode 100644 fabric-official/mc261/src/main/java/ac/grim/grimac/platform/fabric/mc261/Fabric261PlatformPlayer.java create mode 100644 fabric-official/mc261/src/main/java/ac/grim/grimac/platform/fabric/mc261/Fabric261PlatformServer.java delete mode 100644 fabric-official/mc261/src/main/java/ac/grim/grimac/platform/fabric/mc261/Grim26ChainEntrypoint.java create mode 100644 fabric-official/src/main/java/ac/grim/grimac/platform/fabric/AbstractFabricPlatformServer.java create mode 100644 fabric-official/src/main/java/ac/grim/grimac/platform/fabric/FabricPlatformPlugin.java create mode 100644 fabric-official/src/main/java/ac/grim/grimac/platform/fabric/FabricServerEvents.java create mode 100644 fabric-official/src/main/java/ac/grim/grimac/platform/fabric/GrimACFabricEntryPoint.java create mode 100644 fabric-official/src/main/java/ac/grim/grimac/platform/fabric/GrimACFabricLoaderPlugin.java create mode 100644 fabric-official/src/main/java/ac/grim/grimac/platform/fabric/entity/AbstractFabricGrimEntity.java create mode 100644 fabric-official/src/main/java/ac/grim/grimac/platform/fabric/initables/FabricBStats.java create mode 100644 fabric-official/src/main/java/ac/grim/grimac/platform/fabric/initables/FabricTickEndEvent.java create mode 100644 fabric-official/src/main/java/ac/grim/grimac/platform/fabric/manager/FabricItemResetHandler.java create mode 100644 fabric-official/src/main/java/ac/grim/grimac/platform/fabric/manager/FabricMessagePlaceHolderManager.java create mode 100644 fabric-official/src/main/java/ac/grim/grimac/platform/fabric/manager/FabricPlatformPluginManager.java create mode 100644 fabric-official/src/main/java/ac/grim/grimac/platform/fabric/mixins/LevelChunkMixin.java create mode 100644 fabric-official/src/main/java/ac/grim/grimac/platform/fabric/mixins/LevelMixin.java create mode 100644 fabric-official/src/main/java/ac/grim/grimac/platform/fabric/mixins/PistonBaseBlockMixin.java create mode 100644 fabric-official/src/main/java/ac/grim/grimac/platform/fabric/mixins/ServerPlayerMixin.java create mode 100644 fabric-official/src/main/java/ac/grim/grimac/platform/fabric/player/AbstractFabricPlatformInventory.java create mode 100644 fabric-official/src/main/java/ac/grim/grimac/platform/fabric/player/AbstractFabricPlatformPlayer.java create mode 100644 fabric-official/src/main/java/ac/grim/grimac/platform/fabric/player/FabricOfflinePlatformPlayer.java create mode 100644 fabric-official/src/main/java/ac/grim/grimac/platform/fabric/player/FabricPlatformPlayerFactory.java create mode 100644 fabric-official/src/main/java/ac/grim/grimac/platform/fabric/resolver/FabricResolverRegistrar.java create mode 100644 fabric-official/src/main/java/ac/grim/grimac/platform/fabric/scheduler/FabricAsyncScheduler.java create mode 100644 fabric-official/src/main/java/ac/grim/grimac/platform/fabric/scheduler/FabricEntityScheduler.java create mode 100644 fabric-official/src/main/java/ac/grim/grimac/platform/fabric/scheduler/FabricGlobalRegionScheduler.java create mode 100644 fabric-official/src/main/java/ac/grim/grimac/platform/fabric/scheduler/FabricPlatformScheduler.java create mode 100644 fabric-official/src/main/java/ac/grim/grimac/platform/fabric/scheduler/FabricRegionScheduler.java create mode 100644 fabric-official/src/main/java/ac/grim/grimac/platform/fabric/scheduler/FabricTaskHandle.java create mode 100644 fabric-official/src/main/java/ac/grim/grimac/platform/fabric/sender/NoopFabricSenderFactory.java create mode 100644 fabric-official/src/main/java/ac/grim/grimac/platform/fabric/utils/PolymerHook.java create mode 100644 fabric-official/src/main/java/ac/grim/grimac/platform/fabric/utils/convert/FabricConversionUtil.java create mode 100644 fabric-official/src/main/java/ac/grim/grimac/platform/fabric/utils/convert/IFabricConversionUtil.java create mode 100644 fabric-official/src/main/java/ac/grim/grimac/platform/fabric/utils/message/IFabricMessageUtil.java create mode 100644 fabric-official/src/main/java/ac/grim/grimac/platform/fabric/utils/message/JULoggerFactory.java create mode 100644 fabric-official/src/main/java/ac/grim/grimac/platform/fabric/utils/message/Log4jBackedJULogger.java create mode 100644 fabric-official/src/main/java/ac/grim/grimac/platform/fabric/utils/message/Slf4jBackedJULogger.java create mode 100644 fabric-official/src/main/java/ac/grim/grimac/platform/fabric/utils/metrics/BStatsConfig.java create mode 100644 fabric-official/src/main/java/ac/grim/grimac/platform/fabric/utils/metrics/CustomChart.java create mode 100644 fabric-official/src/main/java/ac/grim/grimac/platform/fabric/utils/metrics/JsonObjectBuilder.java create mode 100644 fabric-official/src/main/java/ac/grim/grimac/platform/fabric/utils/metrics/Metrics.java create mode 100644 fabric-official/src/main/java/ac/grim/grimac/platform/fabric/utils/metrics/MetricsBase.java create mode 100644 fabric-official/src/main/java/ac/grim/grimac/platform/fabric/utils/metrics/MetricsFabric.java create mode 100644 fabric-official/src/main/java/ac/grim/grimac/platform/fabric/utils/thread/FabricFutureUtil.java create mode 100644 fabric-official/src/main/java/ac/grim/grimac/platform/fabric/utils/world/LevelChunkUtil.java create mode 100644 fabric-official/src/main/resources/grimac.accesswidener diff --git a/fabric-official/build.gradle.kts b/fabric-official/build.gradle.kts index b9e12aa2cf..6cf6406082 100644 --- a/fabric-official/build.gradle.kts +++ b/fabric-official/build.gradle.kts @@ -28,6 +28,10 @@ java { } } +loom { + accessWidenerPath = file("src/main/resources/grimac.accesswidener") +} + dependencies { minecraft("com.mojang:minecraft:$minecraft_version") // 26.X anticheat port — concrete remaining work (audited via attempted @@ -79,7 +83,9 @@ dependencies { mappings("net.fabricmc:intermediary:0.0.0:v2") modImplementation(libs.fabric.loader) + implementation(project(":common")) compileOnly(libs.packetevents.api) + compileOnly(libs.packetevents.fabric) compileOnly("org.slf4j:slf4j-api:2.0.17") compileOnly("org.apache.logging.log4j:log4j-api:2.24.3") } @@ -166,8 +172,10 @@ allprojects { subprojects { dependencies { implementation(project(":fabric-official", configuration = "namedElements")) + compileOnly(project(":common")) val libsx = rootProject.extensions.getByType().named("libs") compileOnly(libsx.findLibrary("packetevents-api").get()) + compileOnly(libsx.findLibrary("packetevents-fabric").get()) } } diff --git a/fabric-official/mc261/src/main/java/ac/grim/grimac/platform/fabric/mc261/Fabric261ConversionUtil.java b/fabric-official/mc261/src/main/java/ac/grim/grimac/platform/fabric/mc261/Fabric261ConversionUtil.java new file mode 100644 index 0000000000..709be5423d --- /dev/null +++ b/fabric-official/mc261/src/main/java/ac/grim/grimac/platform/fabric/mc261/Fabric261ConversionUtil.java @@ -0,0 +1,29 @@ +package ac.grim.grimac.platform.fabric.mc261; + +import ac.grim.grimac.platform.fabric.utils.convert.IFabricConversionUtil; +import com.github.retrooper.packetevents.protocol.item.ItemStack; +import com.github.retrooper.packetevents.protocol.player.ClientVersion; +import com.github.retrooper.packetevents.wrapper.PacketWrapper; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import net.kyori.adventure.text.flattener.ComponentFlattener; +import net.minecraft.network.chat.Component; + +public class Fabric261ConversionUtil implements IFabricConversionUtil { + @Override + public ItemStack fromFabricItemStack(net.minecraft.world.item.ItemStack fabricStack) { + // TODO Phase C: wire proper conversion through PE's encoder. For now empty + // stack so the engine doesn't crash on inventory reads — the bukkit/spigot + // path uses PE's BukkitConverter; fabric needs an equivalent. + return ItemStack.EMPTY; + } + + @Override + public net.minecraft.network.chat.Component toNativeText(net.kyori.adventure.text.Component component) { + // adventure-platform-fabric isn't on the 26.X classpath. Plain-text flatten + // until that path lands (Phase C); preserves text content, loses formatting. + StringBuilder out = new StringBuilder(); + ComponentFlattener.basic().flatten(component, out::append); + return Component.literal(out.toString()); + } +} diff --git a/fabric-official/mc261/src/main/java/ac/grim/grimac/platform/fabric/mc261/Fabric261GrimEntity.java b/fabric-official/mc261/src/main/java/ac/grim/grimac/platform/fabric/mc261/Fabric261GrimEntity.java new file mode 100644 index 0000000000..cfca60de89 --- /dev/null +++ b/fabric-official/mc261/src/main/java/ac/grim/grimac/platform/fabric/mc261/Fabric261GrimEntity.java @@ -0,0 +1,40 @@ +package ac.grim.grimac.platform.fabric.mc261; + +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.server.level.ServerLevel; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.Relative; + +import java.util.EnumSet; +import java.util.concurrent.CompletableFuture; + +public class Fabric261GrimEntity extends AbstractFabricGrimEntity { + public Fabric261GrimEntity(Entity entity) { + super(entity); + } + + @Override + public boolean isDead() { + return this.entity instanceof LivingEntity living ? living.isDeadOrDying() : this.entity.isRemoved(); + } + + @Override + public CompletableFuture teleportAsync(Location location) { + return FabricFutureUtil.supplySync(() -> { + this.entity.teleportTo( + (ServerLevel) location.getWorld(), + location.getX(), + location.getY(), + location.getZ(), + EnumSet.noneOf(Relative.class), + location.getYaw(), + location.getPitch(), + true + ); + return true; + }); + } +} diff --git a/fabric-official/mc261/src/main/java/ac/grim/grimac/platform/fabric/mc261/Fabric261LoaderPlugin.java b/fabric-official/mc261/src/main/java/ac/grim/grimac/platform/fabric/mc261/Fabric261LoaderPlugin.java new file mode 100644 index 0000000000..8b72e7897c --- /dev/null +++ b/fabric-official/mc261/src/main/java/ac/grim/grimac/platform/fabric/mc261/Fabric261LoaderPlugin.java @@ -0,0 +1,31 @@ +package ac.grim.grimac.platform.fabric.mc261; + +import ac.grim.grimac.platform.fabric.GrimACFabricLoaderPlugin; +import ac.grim.grimac.platform.fabric.player.FabricPlatformPlayerFactory; +import com.github.retrooper.packetevents.manager.server.ServerVersion; + +// Concrete chain entry for the 26.1.X family. Empirically verified mojmap-stable +// across 26.1 / 26.1.1 / 26.1.2 (zero method signature drift on 300 sampled +// classes; six critical classes — MinecraftServer, ServerPlayer, ServerLevel, +// CommandSourceStack, Player, Entity — bit-identical signatures). When 26.2 +// drops a method-signature change a new mc262 sibling will be added. +public class Fabric261LoaderPlugin extends GrimACFabricLoaderPlugin { + + public Fabric261LoaderPlugin() { + super( + new FabricPlatformPlayerFactory( + Fabric261PlatformPlayer::new, + Fabric261GrimEntity::new, + Fabric261PlatformInventory::new + ), + new Fabric261PlatformServer(), + new Fabric261MessageUtil(), + new Fabric261ConversionUtil() + ); + } + + @Override + public ServerVersion getNativeVersion() { + return ServerVersion.V_26_1; + } +} diff --git a/fabric-official/mc261/src/main/java/ac/grim/grimac/platform/fabric/mc261/Fabric261MessageUtil.java b/fabric-official/mc261/src/main/java/ac/grim/grimac/platform/fabric/mc261/Fabric261MessageUtil.java new file mode 100644 index 0000000000..99405bfd06 --- /dev/null +++ b/fabric-official/mc261/src/main/java/ac/grim/grimac/platform/fabric/mc261/Fabric261MessageUtil.java @@ -0,0 +1,17 @@ +package ac.grim.grimac.platform.fabric.mc261; + +import ac.grim.grimac.platform.fabric.utils.message.IFabricMessageUtil; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.network.chat.Component; + +public class Fabric261MessageUtil implements IFabricMessageUtil { + @Override + public Component textLiteral(String message) { + return Component.literal(message); + } + + @Override + public void sendMessage(CommandSourceStack target, Component message, boolean overlay) { + target.sendSuccess(() -> message, overlay); + } +} diff --git a/fabric-official/mc261/src/main/java/ac/grim/grimac/platform/fabric/mc261/Fabric261PlatformInventory.java b/fabric-official/mc261/src/main/java/ac/grim/grimac/platform/fabric/mc261/Fabric261PlatformInventory.java new file mode 100644 index 0000000000..8c7188bd23 --- /dev/null +++ b/fabric-official/mc261/src/main/java/ac/grim/grimac/platform/fabric/mc261/Fabric261PlatformInventory.java @@ -0,0 +1,26 @@ +package ac.grim.grimac.platform.fabric.mc261; + +import ac.grim.grimac.platform.fabric.player.AbstractFabricPlatformInventory; +import ac.grim.grimac.platform.fabric.player.AbstractFabricPlatformPlayer; +import net.minecraft.world.inventory.AbstractContainerMenu; +import net.minecraft.world.inventory.InventoryMenu; + +public class Fabric261PlatformInventory extends AbstractFabricPlatformInventory { + public Fabric261PlatformInventory(AbstractFabricPlatformPlayer player) { + super(player); + } + + @Override + public String getOpenInventoryKey() { + // Minimal 26.X stub mirroring the bukkit-key convention. Phase C will + // map the full MenuType registry the way intermediary's + // Fabric1161PlatformInventory does (Registry.MENU.getKey(type) → + // Identifier.path), once we wire BuiltInRegistries / Registries lookup + // on the 26.X namespace. For now: distinguish CRAFTING / CREATIVE / + // fallback to handler class name so the engine has stable strings. + AbstractContainerMenu menu = fabricPlatformPlayer.getNative().containerMenu; + if (menu instanceof InventoryMenu) return "CRAFTING"; + if (fabricPlatformPlayer.getNative().isCreative()) return "CREATIVE"; + return menu.getClass().getSimpleName(); + } +} diff --git a/fabric-official/mc261/src/main/java/ac/grim/grimac/platform/fabric/mc261/Fabric261PlatformPlayer.java b/fabric-official/mc261/src/main/java/ac/grim/grimac/platform/fabric/mc261/Fabric261PlatformPlayer.java new file mode 100644 index 0000000000..1768f17073 --- /dev/null +++ b/fabric-official/mc261/src/main/java/ac/grim/grimac/platform/fabric/mc261/Fabric261PlatformPlayer.java @@ -0,0 +1,41 @@ +package ac.grim.grimac.platform.fabric.mc261; + +import ac.grim.grimac.platform.api.sender.Sender; +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.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.Relative; + +import java.util.EnumSet; +import java.util.concurrent.CompletableFuture; + +public class Fabric261PlatformPlayer extends AbstractFabricPlatformPlayer { + public Fabric261PlatformPlayer(ServerPlayer player) { + super(player); + } + + @Override + public Sender getSender() { + return GrimACFabricLoaderPlugin.LOADER.getFabricSenderFactory().wrap(fabricPlayer.createCommandSourceStack()); + } + + @Override + public CompletableFuture teleportAsync(Location location) { + return FabricFutureUtil.supplySync(() -> { + fabricPlayer.teleportTo( + (ServerLevel) location.getWorld(), + location.getX(), + location.getY(), + location.getZ(), + EnumSet.noneOf(Relative.class), + location.getYaw(), + location.getPitch(), + true + ); + return true; + }); + } +} diff --git a/fabric-official/mc261/src/main/java/ac/grim/grimac/platform/fabric/mc261/Fabric261PlatformServer.java b/fabric-official/mc261/src/main/java/ac/grim/grimac/platform/fabric/mc261/Fabric261PlatformServer.java new file mode 100644 index 0000000000..8ae887e450 --- /dev/null +++ b/fabric-official/mc261/src/main/java/ac/grim/grimac/platform/fabric/mc261/Fabric261PlatformServer.java @@ -0,0 +1,23 @@ +package ac.grim.grimac.platform.fabric.mc261; + +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.commands.CommandSourceStack; + +public class Fabric261PlatformServer extends AbstractFabricPlatformServer { + @Override + public double getTPS() { + // 26.X retains the 1.20.3+ accessors. tickRateManager().tickrate() = the + // configured tickrate cap (default 20.0); smoothed-tick-time gives the + // actual achieved cadence in ms — take the min so we never report > cap. + return Math.min(1000.0 / GrimACFabricLoaderPlugin.FABRIC_SERVER.getCurrentSmoothedTickTime(), + GrimACFabricLoaderPlugin.FABRIC_SERVER.tickRateManager().tickrate()); + } + + @Override + public void dispatchCommand(Sender sender, String command) { + CommandSourceStack stack = (CommandSourceStack) GrimACFabricLoaderPlugin.LOADER.getFabricSenderFactory().unwrap(sender); + GrimACFabricLoaderPlugin.FABRIC_SERVER.getCommands().performPrefixedCommand(stack, command); + } +} diff --git a/fabric-official/mc261/src/main/java/ac/grim/grimac/platform/fabric/mc261/Grim26ChainEntrypoint.java b/fabric-official/mc261/src/main/java/ac/grim/grimac/platform/fabric/mc261/Grim26ChainEntrypoint.java deleted file mode 100644 index eee2141a84..0000000000 --- a/fabric-official/mc261/src/main/java/ac/grim/grimac/platform/fabric/mc261/Grim26ChainEntrypoint.java +++ /dev/null @@ -1,12 +0,0 @@ -package ac.grim.grimac.platform.fabric.mc261; - -import ac.grim.grimac.platform.fabric.loader.Grim26ChainEntryPoint; -import com.github.retrooper.packetevents.manager.server.ServerVersion; - -public class Grim26ChainEntrypoint implements Grim26ChainEntryPoint { - - @Override - public ServerVersion getNativeVersion() { - return ServerVersion.V_26_1; - } -} diff --git a/fabric-official/mc261/src/main/resources/fabric.mod.json b/fabric-official/mc261/src/main/resources/fabric.mod.json index dbd161eb06..d1b1c029ff 100644 --- a/fabric-official/mc261/src/main/resources/fabric.mod.json +++ b/fabric-official/mc261/src/main/resources/fabric.mod.json @@ -7,8 +7,8 @@ "license": "GPLv3", "environment": "server", "entrypoints": { - "grim26ChainLoad": [ - "ac.grim.grimac.platform.fabric.mc261.Grim26ChainEntrypoint" + "grim26MainLoad": [ + "ac.grim.grimac.platform.fabric.mc261.Fabric261LoaderPlugin" ] }, "depends": { diff --git a/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/AbstractFabricPlatformServer.java b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/AbstractFabricPlatformServer.java new file mode 100644 index 0000000000..2d39c49cf0 --- /dev/null +++ b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/AbstractFabricPlatformServer.java @@ -0,0 +1,46 @@ +package ac.grim.grimac.platform.fabric; + +import ac.grim.grimac.platform.api.PlatformServer; +import ac.grim.grimac.platform.api.sender.Sender; +import com.mojang.authlib.GameProfile; +import net.fabricmc.loader.api.FabricLoader; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.server.permissions.Permission; +import net.minecraft.server.permissions.PermissionLevel; +import org.jetbrains.annotations.Nullable; + +public abstract class AbstractFabricPlatformServer implements PlatformServer { + + public int getOperatorPermissionLevel() { + // 26.X: getOperatorUserPermissionLevel() → operatorUserPermissions().level().id() + return GrimACFabricLoaderPlugin.FABRIC_SERVER.operatorUserPermissions().level().id(); + } + + public boolean hasPermission(CommandSourceStack stack, int level) { + // 26.X: hasPermission(int) → permissions().hasPermission(new Permission.HasCommandLevel(...)) + return stack.permissions().hasPermission( + new Permission.HasCommandLevel(PermissionLevel.byId(level))); + } + + @Override + public String getPlatformImplementationString() { + return "Fabric " + FabricLoader.getInstance().getModContainer("fabricloader").orElseThrow().getMetadata().getVersion().getFriendlyString() + " (MC: " + GrimACFabricLoaderPlugin.FABRIC_SERVER.getServerVersion() + ")"; + } + + @Override + public Sender getConsoleSender() { + CommandSourceStack consoleSource = GrimACFabricLoaderPlugin.FABRIC_SERVER.createCommandSourceStack(); + return GrimACFabricLoaderPlugin.LOADER.getFabricSenderFactory().wrap(consoleSource); + } + + @Override + public void registerOutgoingPluginChannel(String name) { + throw new UnsupportedOperationException(); + } + + @Nullable + public GameProfile getProfileByName(String name) { + // 26.X: getProfileCache().get(name) → services().profileResolver().fetchByName(name) + return GrimACFabricLoaderPlugin.FABRIC_SERVER.services().profileResolver().fetchByName(name).orElse(null); + } +} diff --git a/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/FabricPlatformPlugin.java b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/FabricPlatformPlugin.java new file mode 100644 index 0000000000..91a7939aad --- /dev/null +++ b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/FabricPlatformPlugin.java @@ -0,0 +1,36 @@ +package ac.grim.grimac.platform.fabric; + +import ac.grim.grimac.platform.api.PlatformPlugin; +import net.fabricmc.loader.api.ModContainer; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; + +public class FabricPlatformPlugin implements PlatformPlugin { + private final @NotNull ModContainer modContainer; + + @Contract(pure = true) + public FabricPlatformPlugin(@NotNull ModContainer modContainer) { + this.modContainer = Objects.requireNonNull(modContainer); + } + + @Override + public boolean isEnabled() { + // Fabric mods are always "enabled" if loaded, as there's no explicit enable/disable state + // You can add custom logic if needed (e.g., check mod configuration) + return true; + } + + @Override + public String getName() { + // Get the mod ID (unique identifier) + return modContainer.getMetadata().getId(); + } + + @Override + public String getVersion() { + // Get the mod version from metadata + return modContainer.getMetadata().getVersion().getFriendlyString(); + } +} diff --git a/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/FabricServerEvents.java b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/FabricServerEvents.java new file mode 100644 index 0000000000..d23fa0d23d --- /dev/null +++ b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/FabricServerEvents.java @@ -0,0 +1,46 @@ +package ac.grim.grimac.platform.fabric; + +import net.minecraft.server.MinecraftServer; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; + +// Local replacement for fabric-api's ServerLifecycleEvents + ServerTickEvents. +// fabric-api's event modules ship intermediary-bound bytecode that doesn't link +// against 26.X Mojang names. Phase B wires a mixin into MinecraftServer that +// fires these listener lists at the right lifecycle points. Until that mixin +// lands the events fire nothing — the engine loads but won't tick. Sufficient +// for compile + JiJ-build verification (Phase D/E); not for runtime detection. +public final class FabricServerEvents { + + private FabricServerEvents() {} + + private static final List> STARTING = new ArrayList<>(); + private static final List> STOPPING = new ArrayList<>(); + private static final List> END_TICK = new ArrayList<>(); + + public static void onServerStarting(Consumer listener) { + STARTING.add(listener); + } + + public static void onServerStopping(Consumer listener) { + STOPPING.add(listener); + } + + public static void onEndTick(Consumer listener) { + END_TICK.add(listener); + } + + public static void fireServerStarting(MinecraftServer server) { + for (Consumer l : STARTING) l.accept(server); + } + + public static void fireServerStopping(MinecraftServer server) { + for (Consumer l : STOPPING) l.accept(server); + } + + public static void fireEndTick(MinecraftServer server) { + for (Consumer l : END_TICK) l.accept(server); + } +} diff --git a/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/GrimACFabricEntryPoint.java b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/GrimACFabricEntryPoint.java new file mode 100644 index 0000000000..c810589fc1 --- /dev/null +++ b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/GrimACFabricEntryPoint.java @@ -0,0 +1,53 @@ +package ac.grim.grimac.platform.fabric; + +import ac.grim.grimac.GrimAPI; +import ac.grim.grimac.platform.fabric.initables.FabricBStats; +import ac.grim.grimac.platform.fabric.initables.FabricTickEndEvent; +import net.fabricmc.api.ModInitializer; +import net.fabricmc.loader.api.FabricLoader; +import net.fabricmc.loader.api.entrypoint.PreLaunchEntrypoint; + +import java.util.List; + +public class GrimACFabricEntryPoint implements PreLaunchEntrypoint, ModInitializer { + @Override + public void onPreLaunch() { + } + + @Override + public void onInitialize() { + FabricLoader loader = FabricLoader.getInstance(); + String chainLoadEntryPointName = "grim26MainLoad"; + + List mainChainLoadEntryPoints = loader.getEntrypoints(chainLoadEntryPointName, GrimACFabricLoaderPlugin.class); + mainChainLoadEntryPoints.sort((a, b) -> b.getNativeVersion().getProtocolVersion() - a.getNativeVersion().getProtocolVersion()); + + if (mainChainLoadEntryPoints.isEmpty()) return; + + GrimACFabricLoaderPlugin platformLoader = mainChainLoadEntryPoints.get(0); + GrimACFabricLoaderPlugin.LOADER = platformLoader; + + GrimAPI.INSTANCE.load( + platformLoader, + new FabricBStats(), + new FabricTickEndEvent() + ); + + // 26.X: cloud-fabric not yet ported, so getCommandService() returns no-op + // (registerCommands is still safe to call but does nothing). + GrimAPI.INSTANCE.getCommandService().registerCommands(); + + // fabric-api event modules are intermediary-bound; FabricServerEvents is the + // local replacement. A mixin into MinecraftServer (Phase B) drives the + // listener lists below at the corresponding lifecycle points. + FabricServerEvents.onServerStarting(server -> { + GrimACFabricLoaderPlugin.FABRIC_SERVER = server; + GrimAPI.INSTANCE.start(); + }); + + FabricServerEvents.onServerStopping(server -> { + GrimAPI.INSTANCE.stop(); + platformLoader.getScheduler().shutdown(); + }); + } +} diff --git a/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/GrimACFabricLoaderPlugin.java b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/GrimACFabricLoaderPlugin.java new file mode 100644 index 0000000000..fa534d75ea --- /dev/null +++ b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/GrimACFabricLoaderPlugin.java @@ -0,0 +1,138 @@ +package ac.grim.grimac.platform.fabric; + +import ac.grim.grimac.GrimAPI; +import ac.grim.grimac.api.GrimAPIProvider; +import ac.grim.grimac.api.plugin.GrimPlugin; +import ac.grim.grimac.internal.plugin.resolver.GrimExtensionManager; +import ac.grim.grimac.platform.api.PlatformLoader; +import ac.grim.grimac.platform.api.command.CommandService; +import ac.grim.grimac.platform.api.manager.ItemResetHandler; +import ac.grim.grimac.platform.api.manager.MessagePlaceHolderManager; +import ac.grim.grimac.platform.api.manager.PermissionRegistrationManager; +import ac.grim.grimac.platform.api.manager.PlatformPluginManager; +import ac.grim.grimac.platform.api.permissions.PermissionDefaultValue; +import ac.grim.grimac.platform.api.sender.SenderFactory; +import ac.grim.grimac.platform.fabric.manager.FabricItemResetHandler; +import ac.grim.grimac.platform.fabric.manager.FabricMessagePlaceHolderManager; +import ac.grim.grimac.platform.fabric.manager.FabricPlatformPluginManager; +import ac.grim.grimac.platform.fabric.player.FabricPlatformPlayerFactory; +import ac.grim.grimac.platform.fabric.resolver.FabricResolverRegistrar; +import ac.grim.grimac.platform.fabric.scheduler.FabricPlatformScheduler; +import ac.grim.grimac.platform.fabric.sender.NoopFabricSenderFactory; +import ac.grim.grimac.platform.fabric.utils.convert.IFabricConversionUtil; +import ac.grim.grimac.platform.fabric.utils.message.IFabricMessageUtil; +import ac.grim.grimac.utils.lazy.LazyHolder; +import com.github.retrooper.packetevents.PacketEvents; +import com.github.retrooper.packetevents.PacketEventsAPI; +import com.github.retrooper.packetevents.manager.server.ServerVersion; +import lombok.Getter; +import net.minecraft.server.MinecraftServer; + +// fabric-official variant of GrimACFabricLoaderPlugin. Mirrors the intermediary +// variant except for the parts that depend on intermediary-bound APIs: +// +// - cloud-fabric: its public method sigs reference intermediary class_NNNN names +// and can't link against Mojang-named 26.X. CommandService becomes a no-op +// (same as the catch path the intermediary variant takes when cloud is missing +// on older MC). /grim commands are unavailable on 26.X until cloud-fabric ships +// a 26.X-native build. +// - fabric-permissions-api: same problem. PermissionRegistrationManager is a +// no-op; permission checks fall back to vanilla op-level via +// CommandSourceStack.hasPermission(int) directly in the concrete platform. +public abstract class GrimACFabricLoaderPlugin implements PlatformLoader { + public static MinecraftServer FABRIC_SERVER; + public static GrimACFabricLoaderPlugin LOADER; + + protected final LazyHolder scheduler = LazyHolder.simple(FabricPlatformScheduler::new); + protected final PacketEventsAPI packetEvents = PacketEvents.getAPI(); + protected final LazyHolder senderFactory = LazyHolder.simple(NoopFabricSenderFactory::new); + protected final LazyHolder itemResetHandler = LazyHolder.simple(FabricItemResetHandler::new); + protected final GrimPlugin plugin; + @Getter + protected final PlatformPluginManager pluginManager = new FabricPlatformPluginManager(); + @Getter + protected final MessagePlaceHolderManager messagePlaceHolderManager = new FabricMessagePlaceHolderManager(); + + protected final FabricPlatformPlayerFactory playerFactory; + protected final AbstractFabricPlatformServer platformServer; + @Getter + protected final IFabricConversionUtil fabricConversionUtil; + protected final IFabricMessageUtil fabricMessageUtil; + + public GrimACFabricLoaderPlugin( + FabricPlatformPlayerFactory playerFactory, + AbstractFabricPlatformServer platformServer, + IFabricMessageUtil fabricMessageUtil, + IFabricConversionUtil fabricConversionUtil + ) { + this.playerFactory = playerFactory; + this.platformServer = platformServer; + this.fabricMessageUtil = fabricMessageUtil; + this.fabricConversionUtil = fabricConversionUtil; + + FabricResolverRegistrar resolverRegistrar = new FabricResolverRegistrar(); + GrimExtensionManager extensionManager = GrimAPI.INSTANCE.getExtensionManager(); + resolverRegistrar.registerAll(extensionManager); + plugin = extensionManager.getPlugin("GrimAC"); + } + + @Override + public FabricPlatformScheduler getScheduler() { + return scheduler.get(); + } + + @Override + public PacketEventsAPI getPacketEvents() { + return packetEvents; + } + + @Override + public ItemResetHandler getItemResetHandler() { + return itemResetHandler.get(); + } + + @Override + public SenderFactory getSenderFactory() { + return senderFactory.get(); + } + + @Override + public CommandService getCommandService() { + return () -> {}; // No commands on 26.X — cloud-fabric not yet ported. + } + + @Override + public GrimPlugin getPlugin() { + return plugin; + } + + @Override + public void registerAPIService() { + GrimAPIProvider.init(GrimAPI.INSTANCE.getExternalAPI()); + } + + @Override + public PermissionRegistrationManager getPermissionManager() { + return (name, defaultValue) -> {}; // No-op; fabric-permissions-api not ported. + } + + public NoopFabricSenderFactory getFabricSenderFactory() { + return senderFactory.get(); + } + + @Override + public FabricPlatformPlayerFactory getPlatformPlayerFactory() { + return playerFactory; + } + + @Override + public AbstractFabricPlatformServer getPlatformServer() { + return platformServer; + } + + public IFabricMessageUtil getFabricMessageUtils() { + return fabricMessageUtil; + } + + public abstract ServerVersion getNativeVersion(); +} diff --git a/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/entity/AbstractFabricGrimEntity.java b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/entity/AbstractFabricGrimEntity.java new file mode 100644 index 0000000000..2784ecabe0 --- /dev/null +++ b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/entity/AbstractFabricGrimEntity.java @@ -0,0 +1,68 @@ +package ac.grim.grimac.platform.fabric.entity; + +import ac.grim.grimac.platform.api.entity.GrimEntity; +import ac.grim.grimac.platform.api.world.PlatformWorld; +import ac.grim.grimac.utils.math.Location; +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; +import java.util.UUID; +import net.minecraft.world.entity.Entity; + +public abstract class AbstractFabricGrimEntity implements GrimEntity { + + protected final Entity entity; + + public AbstractFabricGrimEntity(Entity entity) { + this.entity = Objects.requireNonNull(entity); + } + + @Override + public UUID getUniqueId() { + return entity.getUUID(); + } + + @Override + public boolean eject() { + if (entity.isVehicle()) { + entity.ejectPassengers(); + return true; + } + return false; + } + + @Override + public @NotNull Entity getNative() { + return this.entity; + } + + @Override + public PlatformWorld getWorld() { + // entity.level is Level; LevelMixin injects PlatformWorld via the loom:injected_interfaces + // entry in fabric.mod.json. Cast through Object to satisfy javac on the static type. + return (PlatformWorld) (Object) this.entity.level; + } + + @Override + public Location getLocation() { + return new Location( + this.getWorld(), + this.entity.getX(), + this.entity.getY(), + this.entity.getZ(), + this.entity.getViewYRot(1.0F), + this.entity.getViewXRot(1.0F) + ); + } + + @Override + public double distanceSquared(double oX, double oY, double oZ) { + double x = this.entity.getX(); + double y = this.entity.getY(); + double z = this.entity.getZ(); + double distX = (x - oX) * (x - oX); + double distY = (y - oY) * (y - oY); + double distZ = (z - oZ) * (z - oZ); + return distX + distY + distZ; + } +} diff --git a/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/initables/FabricBStats.java b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/initables/FabricBStats.java new file mode 100644 index 0000000000..98c5a9704d --- /dev/null +++ b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/initables/FabricBStats.java @@ -0,0 +1,25 @@ +package ac.grim.grimac.platform.fabric.initables; + +import ac.grim.grimac.GrimAPI; +import ac.grim.grimac.manager.init.start.StartableInitable; +import ac.grim.grimac.manager.init.stop.StoppableInitable; +import ac.grim.grimac.platform.fabric.utils.metrics.MetricsFabric; +import ac.grim.grimac.utils.anticheat.Constants; + +public class FabricBStats implements StartableInitable, StoppableInitable { + + private MetricsFabric metricsFabric; + + @Override + public void start() { + try { + metricsFabric = new MetricsFabric(GrimAPI.INSTANCE.getGrimPlugin(), Constants.BSTATS_PLUGIN_ID); + } catch (Exception ignored) {} + } + + @Override + public void stop() { + if (metricsFabric != null) + metricsFabric.shutdown(); + } +} diff --git a/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/initables/FabricTickEndEvent.java b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/initables/FabricTickEndEvent.java new file mode 100644 index 0000000000..b7cf4201d6 --- /dev/null +++ b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/initables/FabricTickEndEvent.java @@ -0,0 +1,33 @@ +package ac.grim.grimac.platform.fabric.initables; + +import ac.grim.grimac.GrimAPI; +import ac.grim.grimac.manager.init.start.AbstractTickEndEvent; +import ac.grim.grimac.platform.fabric.FabricServerEvents; +import ac.grim.grimac.player.GrimPlayer; +import net.minecraft.server.MinecraftServer; + +public class FabricTickEndEvent extends AbstractTickEndEvent { + + @Override + public void start() { + if (!super.shouldInjectEndTick()) { + return; + } + + // 26.X port: ServerTickEvents.END_SERVER_TICK is intermediary-bound. The + // FabricServerEvents shim is driven by a MinecraftServer.tickServer() mixin + // (Phase B) — until that mixin lands, end-tick fires nothing. + FabricServerEvents.onEndTick(this::onEndServerTick); + } + + private void onEndServerTick(MinecraftServer server) { + tickAllPlayers(); + } + + private void tickAllPlayers() { + for (GrimPlayer player : GrimAPI.INSTANCE.getPlayerDataManager().getEntries()) { + if (player.disableGrim) continue; + super.onEndOfTick(player, true); + } + } +} diff --git a/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/manager/FabricItemResetHandler.java b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/manager/FabricItemResetHandler.java new file mode 100644 index 0000000000..d6d6153014 --- /dev/null +++ b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/manager/FabricItemResetHandler.java @@ -0,0 +1,32 @@ +package ac.grim.grimac.platform.fabric.manager; + +import ac.grim.grimac.platform.api.manager.ItemResetHandler; +import ac.grim.grimac.platform.api.player.PlatformPlayer; +import ac.grim.grimac.platform.fabric.utils.convert.FabricConversionUtil; +import com.github.retrooper.packetevents.protocol.player.InteractionHand; +import net.minecraft.server.level.ServerPlayer; +import org.jetbrains.annotations.Nullable; + +public class FabricItemResetHandler implements ItemResetHandler { + @Override + public void resetItemUsage(@Nullable PlatformPlayer player) { + if (player != null) { + ((ServerPlayer) player.getNative()).stopUsingItem(); + } + } + + @Override + public @Nullable InteractionHand getItemUsageHand(@Nullable PlatformPlayer platformPlayer) { + if (platformPlayer == null) { + return null; + } + + ServerPlayer player = (ServerPlayer) platformPlayer.getNative(); + return player.isUsingItem() ? FabricConversionUtil.fromFabricHand(player.getUsedItemHand()) : null; + } + + @Override + public boolean isUsingItem(@Nullable PlatformPlayer player) { + return player != null && ((ServerPlayer) player.getNative()).isUsingItem(); + } +} diff --git a/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/manager/FabricMessagePlaceHolderManager.java b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/manager/FabricMessagePlaceHolderManager.java new file mode 100644 index 0000000000..ba5fb5d124 --- /dev/null +++ b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/manager/FabricMessagePlaceHolderManager.java @@ -0,0 +1,15 @@ +package ac.grim.grimac.platform.fabric.manager; + +import ac.grim.grimac.platform.api.manager.MessagePlaceHolderManager; +import ac.grim.grimac.platform.api.player.PlatformPlayer; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class FabricMessagePlaceHolderManager implements MessagePlaceHolderManager { + + // PlaceHolderAPI doesn't exist on Fabric and no chosen replacement for the platform yet + @Override + public @NotNull String replacePlaceholders(@Nullable PlatformPlayer player, @NotNull String string) { + return string; + } +} diff --git a/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/manager/FabricPlatformPluginManager.java b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/manager/FabricPlatformPluginManager.java new file mode 100644 index 0000000000..ff320fd2d1 --- /dev/null +++ b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/manager/FabricPlatformPluginManager.java @@ -0,0 +1,32 @@ +package ac.grim.grimac.platform.fabric.manager; + +import ac.grim.grimac.platform.api.PlatformPlugin; +import ac.grim.grimac.platform.api.manager.PlatformPluginManager; +import ac.grim.grimac.platform.fabric.FabricPlatformPlugin; +import net.fabricmc.loader.api.FabricLoader; +import net.fabricmc.loader.api.ModContainer; + +import java.util.Collection; +import java.util.Optional; + +public class FabricPlatformPluginManager implements PlatformPluginManager { + + @Override + public PlatformPlugin[] getPlugins() { + // Get all loaded mods from Fabric Loader + Collection mods = FabricLoader.getInstance().getAllMods(); + PlatformPlugin[] plugins = new PlatformPlugin[mods.size()]; + int i = 0; + for (ModContainer mod : mods) { + plugins[i++] = new FabricPlatformPlugin(mod); + } + return plugins; + } + + @Override + public PlatformPlugin getPlugin(String pluginName) { + // Look up a mod by its ID + Optional mod = FabricLoader.getInstance().getModContainer(pluginName); + return mod.map(FabricPlatformPlugin::new).orElse(null); + } +} diff --git a/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/mixins/LevelChunkMixin.java b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/mixins/LevelChunkMixin.java new file mode 100644 index 0000000000..62b720f1e0 --- /dev/null +++ b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/mixins/LevelChunkMixin.java @@ -0,0 +1,25 @@ +package ac.grim.grimac.platform.fabric.mixins; + +import ac.grim.grimac.platform.api.world.PlatformChunk; +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.chunk.LevelChunk; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; + +@Mixin(LevelChunk.class) +@Implements(@Interface(iface = PlatformChunk.class, prefix = "grimac$")) +abstract class LevelChunkMixin { + // TODO (Fabric) (Region Threading) use ThreadLocal for Fabric region threading mods instead of a single variable + // Having a single grimac$sharedPos works when the server is run on the single thread, as vanilla does + @Unique + private static final BlockPos.MutableBlockPos grimac$sharedPos = new BlockPos.MutableBlockPos(); + + public int grimac$getBlockID(int x, int y, int z) { + LevelChunk chunk = (LevelChunk) (Object) this; + grimac$sharedPos.set(chunk.getPos().getMinBlockX() + x, y, chunk.getPos().getMinBlockZ() + z); + return Block.getId(chunk.getBlockState(grimac$sharedPos)); + } +} diff --git a/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/mixins/LevelMixin.java b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/mixins/LevelMixin.java new file mode 100644 index 0000000000..c2d2ca5fd1 --- /dev/null +++ b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/mixins/LevelMixin.java @@ -0,0 +1,55 @@ +package ac.grim.grimac.platform.fabric.mixins; + +import ac.grim.grimac.platform.api.world.PlatformChunk; +import ac.grim.grimac.platform.api.world.PlatformWorld; +import ac.grim.grimac.platform.fabric.GrimACFabricLoaderPlugin; +import ac.grim.grimac.platform.fabric.utils.world.LevelChunkUtil; +import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState; +import net.minecraft.core.BlockPos; +import net.minecraft.resources.ResourceKey; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.LevelAccessor; +import net.minecraft.world.level.block.Block; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.*; + +import java.util.UUID; + +@Mixin(Level.class) +@Implements(@Interface(iface = PlatformWorld.class, prefix = "grimac$")) +abstract class LevelMixin implements LevelAccessor { + + @Shadow + public abstract ResourceKey dimension(); + + // Route through ChunkSource (via LevelChunkUtil trampoline) so the call resolves to + // method_12123, not Level.method_8393. Otherwise the prefix-stripped bridge ends up + // overriding method_8393 on the target on versions where the runtime mapping aliases + // isChunkLoaded(II)Z to it, and the body recurses through itself — see issue #2568. + public boolean grimac$isChunkLoaded(int chunkX, int chunkZ) { + return LevelChunkUtil.hasChunkAt((Level) (Object) this, chunkX, chunkZ); + } + + public WrappedBlockState grimac$getBlockAt(int x, int y, int z) { + return WrappedBlockState.getByGlobalId( + Block.getId(getBlockState(new BlockPos(x, y, z))) + ); + } + + public String grimac$getName() { + // 26.X: ResourceKey.location() → identifier() + return this.dimension().identifier().toString(); + } + + public @Nullable UUID grimac$getUID() { + throw new UnsupportedOperationException(); + } + + public PlatformChunk grimac$getChunkAt(int currChunkX, int currChunkZ) { + return (PlatformChunk) getChunk(currChunkX, currChunkZ); + } + + public boolean grimac$isLoaded() { + return GrimACFabricLoaderPlugin.FABRIC_SERVER.getLevel(this.dimension()) != null; + } +} diff --git a/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/mixins/PistonBaseBlockMixin.java b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/mixins/PistonBaseBlockMixin.java new file mode 100644 index 0000000000..174281b6c1 --- /dev/null +++ b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/mixins/PistonBaseBlockMixin.java @@ -0,0 +1,94 @@ +package ac.grim.grimac.platform.fabric.mixins; + +import ac.grim.grimac.GrimAPI; +import ac.grim.grimac.platform.fabric.utils.convert.FabricConversionUtil; +import ac.grim.grimac.player.GrimPlayer; +import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox; +import ac.grim.grimac.utils.data.PistonData; +import com.github.retrooper.packetevents.protocol.world.BlockFace; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.piston.PistonBaseBlock; +import net.minecraft.world.level.block.piston.PistonStructureResolver; +import net.minecraft.world.level.block.state.BlockState; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +import java.util.ArrayList; +import java.util.List; + +@Mixin(PistonBaseBlock.class) +public class PistonBaseBlockMixin { + + private static final double MAX_HORIZONTAL_DISTANCE = 24.0; + private static final double MAX_VERTICAL_DISTANCE = 64.0; + + + private static boolean isCloseEnough(int ax, int ay, int az, double bx, double by, double bz) { + return Math.abs(ax - bx) <= MAX_HORIZONTAL_DISTANCE + && Math.abs(ay - by) <= MAX_VERTICAL_DISTANCE + && Math.abs(az - bz) <= MAX_HORIZONTAL_DISTANCE; + } + + @Redirect(method = "moveBlocks", + at = @At(value = "INVOKE", + target = "Lnet/minecraft/world/level/block/piston/PistonStructureResolver;resolve()Z")) + private boolean grimac$onPistonResolve(PistonStructureResolver resolver, + Level level, BlockPos pistonPos, Direction direction, boolean extending) { + boolean resolved = resolver.resolve(); + if (resolved) { + handlePiston(resolver, level, pistonPos, direction, extending); + } + return resolved; + } + + private static void handlePiston(PistonStructureResolver resolver, Level level, + BlockPos pistonPos, Direction direction, boolean extending) { + boolean hasSlimeBlock = false; + boolean hasHoneyBlock = false; + + List boxes = new ArrayList<>(); + + int dx = direction.getStepX(); + int dy = direction.getStepY(); + int dz = direction.getStepZ(); + + for (BlockPos blockPos : resolver.getToPush()) { + int bx = blockPos.getX(); + int by = blockPos.getY(); + int bz = blockPos.getZ(); + + boxes.add(new SimpleCollisionBox(bx, by, bz, bx + 1, by + 1, bz + 1, true)); + boxes.add(new SimpleCollisionBox(bx + dx, by + dy, bz + dz, + bx + dx + 1, by + dy + 1, bz + dz + 1, true)); + + BlockState state = level.getBlockState(blockPos); + if (state.is(Blocks.SLIME_BLOCK)) hasSlimeBlock = true; + if (state.is(Blocks.HONEY_BLOCK)) hasHoneyBlock = true; + } + + // Add the piston head bounding box for pushes, or for retracts with no blocks being moved + if (extending || resolver.getToPush().isEmpty()) { + BlockPos head = pistonPos.relative(direction); + int hx = head.getX(), hy = head.getY(), hz = head.getZ(); + boxes.add(new SimpleCollisionBox(hx, hy, hz, hx + 1, hy + 1, hz + 1, true)); + } + + final int chunkX = pistonPos.getX() >> 4; + final int chunkZ = pistonPos.getZ() >> 4; + final BlockFace blockFace = FabricConversionUtil.fromDirection(direction); + final int px = pistonPos.getX(), py = pistonPos.getY(), pz = pistonPos.getZ(); + + for (GrimPlayer player : GrimAPI.INSTANCE.getPlayerDataManager().getEntries()) { + var pos = player.compensatedEntities.self.trackedServerPosition.getPos(); + if (isCloseEnough(px, py, pz, pos.getX(), pos.getY(), pos.getZ()) && player.compensatedWorld.isChunkLoaded(chunkX, chunkZ)) { + int lastTrans = player.lastTransactionSent.get(); + PistonData data = new PistonData(blockFace, boxes, lastTrans, extending, hasSlimeBlock, hasHoneyBlock); + player.latencyUtils.addRealTimeTaskAsync(lastTrans, () -> player.compensatedWorld.activePistons.add(data)); + } + } + } +} diff --git a/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/mixins/ServerPlayerMixin.java b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/mixins/ServerPlayerMixin.java new file mode 100644 index 0000000000..9867270871 --- /dev/null +++ b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/mixins/ServerPlayerMixin.java @@ -0,0 +1,17 @@ +package ac.grim.grimac.platform.fabric.mixins; + +import ac.grim.grimac.GrimAPI; +import ac.grim.grimac.platform.fabric.player.FabricPlatformPlayerFactory; +import net.minecraft.server.level.ServerPlayer; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(ServerPlayer.class) +abstract class ServerPlayerMixin { + @Inject(method = "restoreFrom", at = @At("TAIL")) + private void onRestoreFrom(ServerPlayer oldPlayer, boolean alive, CallbackInfo ci) { + ((FabricPlatformPlayerFactory) GrimAPI.INSTANCE.getPlatformPlayerFactory()).replaceNativePlayer(oldPlayer.getUUID(), (ServerPlayer) (Object) this); + } +} diff --git a/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/player/AbstractFabricPlatformInventory.java b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/player/AbstractFabricPlatformInventory.java new file mode 100644 index 0000000000..ea36634141 --- /dev/null +++ b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/player/AbstractFabricPlatformInventory.java @@ -0,0 +1,62 @@ +package ac.grim.grimac.platform.fabric.player; + +import ac.grim.grimac.platform.api.player.PlatformInventory; +import ac.grim.grimac.platform.fabric.GrimACFabricLoaderPlugin; +import ac.grim.grimac.platform.fabric.utils.convert.IFabricConversionUtil; +import com.github.retrooper.packetevents.protocol.item.ItemStack; + + +public abstract class AbstractFabricPlatformInventory implements PlatformInventory { + + private static final IFabricConversionUtil fabricConversionUtil = GrimACFabricLoaderPlugin.LOADER.getFabricConversionUtil(); + protected final AbstractFabricPlatformPlayer fabricPlatformPlayer; + + public AbstractFabricPlatformInventory(AbstractFabricPlatformPlayer fabricPlatformPlayer) { + this.fabricPlatformPlayer = fabricPlatformPlayer; + } + + @Override + public ItemStack getItemInHand() { + // 26.X: Inventory.getSelected() → getSelectedItem() + return fabricConversionUtil.fromFabricItemStack(fabricPlatformPlayer.fabricPlayer.inventory.getSelectedItem()); + } + + @Override + public ItemStack getItemInOffHand() { + return fabricConversionUtil.fromFabricItemStack(fabricPlatformPlayer.fabricPlayer.inventory.getItem(40)); + } + + @Override + public ItemStack getStack(int bukkitSlot, int vanillaSlot) { + return fabricConversionUtil.fromFabricItemStack(fabricPlatformPlayer.fabricPlayer.inventory.getItem(bukkitSlot)); + } + + @Override + public ItemStack getHelmet() { + return fabricConversionUtil.fromFabricItemStack(fabricPlatformPlayer.fabricPlayer.inventory.getItem(39)); + } + + @Override + public ItemStack getChestplate() { + return fabricConversionUtil.fromFabricItemStack(fabricPlatformPlayer.fabricPlayer.inventory.getItem(38)); + } + + @Override + public ItemStack getLeggings() { + return fabricConversionUtil.fromFabricItemStack(fabricPlatformPlayer.fabricPlayer.inventory.getItem(37)); + } + + @Override + public ItemStack getBoots() { + return fabricConversionUtil.fromFabricItemStack(fabricPlatformPlayer.fabricPlayer.inventory.getItem(36)); + } + + @Override + public ItemStack[] getContents() { + ItemStack[] items = new ItemStack[fabricPlatformPlayer.fabricPlayer.inventory.getContainerSize()]; + for (int i = 0; i < fabricPlatformPlayer.fabricPlayer.inventory.getContainerSize(); i++) { + items[i] = fabricConversionUtil.fromFabricItemStack(fabricPlatformPlayer.fabricPlayer.inventory.getItem(i)); + } + return items; + } +} diff --git a/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/player/AbstractFabricPlatformPlayer.java b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/player/AbstractFabricPlatformPlayer.java new file mode 100644 index 0000000000..b7b65d6902 --- /dev/null +++ b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/player/AbstractFabricPlatformPlayer.java @@ -0,0 +1,164 @@ +package ac.grim.grimac.platform.fabric.player; + +import ac.grim.grimac.platform.api.player.BlockTranslator; +import ac.grim.grimac.platform.api.entity.GrimEntity; +import ac.grim.grimac.platform.api.player.PlatformInventory; +import ac.grim.grimac.platform.api.player.PlatformPlayer; +import ac.grim.grimac.platform.fabric.GrimACFabricLoaderPlugin; +import ac.grim.grimac.platform.fabric.entity.AbstractFabricGrimEntity; +import ac.grim.grimac.platform.fabric.utils.PolymerHook; +import ac.grim.grimac.platform.fabric.utils.convert.FabricConversionUtil; +import ac.grim.grimac.utils.common.arguments.CommonGrimArguments; +import com.github.retrooper.packetevents.PacketEvents; +import com.github.retrooper.packetevents.protocol.player.GameMode; +import com.github.retrooper.packetevents.protocol.player.User; +import com.github.retrooper.packetevents.util.Vector3d; +import lombok.Getter; +import net.kyori.adventure.text.Component; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.Entity; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.UUID; + +public abstract class AbstractFabricPlatformPlayer extends AbstractFabricGrimEntity implements PlatformPlayer { + protected volatile ServerPlayer fabricPlayer; + protected final AbstractFabricPlatformInventory inventory; + private final @Nullable User user; + @Getter private final BlockTranslator blockTranslator; + + public AbstractFabricPlatformPlayer(ServerPlayer player) { + super(player); + this.fabricPlayer = player; + this.inventory = GrimACFabricLoaderPlugin.LOADER.getPlatformPlayerFactory().getPlatformInventory(this); + if (CommonGrimArguments.USE_CHAT_FAST_BYPASS.value()) { + Object channel = PacketEvents.getAPI().getProtocolManager().getChannel(fabricPlayer.getUUID()); + this.user = PacketEvents.getAPI().getProtocolManager().getUser(channel); + } else { + this.user = null; + } + + this.blockTranslator = PolymerHook.createTranslator(this.fabricPlayer); + } + + @Override + public void kickPlayer(String textReason) { + fabricPlayer.connection.disconnect(GrimACFabricLoaderPlugin.LOADER.getFabricMessageUtils().textLiteral(textReason)); + } + + @Override + public boolean isSneaking() { + return fabricPlayer.isShiftKeyDown(); + } + + @Override + public void setSneaking(boolean isSneaking) { + fabricPlayer.setShiftKeyDown(isSneaking); + } + + @Override + public boolean hasPermission(String permission) { + return getSender().hasPermission(permission); + } + + @Override + public boolean hasPermission(String permission, boolean defaultIfUnset) { + return getSender().hasPermission(permission, defaultIfUnset); + } + + @Override + public void sendMessage(String message) { + if (CommonGrimArguments.USE_CHAT_FAST_BYPASS.value() && user != null) { + user.sendMessage(message); + } else { + fabricPlayer.sendSystemMessage(GrimACFabricLoaderPlugin.LOADER.getFabricMessageUtils().textLiteral(message), false); + } + } + + @Override + public void sendMessage(Component message) { + if (CommonGrimArguments.USE_CHAT_FAST_BYPASS.value() && user != null) { + user.sendMessage(message); + } else { + fabricPlayer.sendSystemMessage(GrimACFabricLoaderPlugin.LOADER.getFabricConversionUtil().toNativeText(message), false); + } + } + + @Override + public boolean isOnline() { + return !fabricPlayer.hasDisconnected(); + } + + @Override + public String getName() { + return fabricPlayer.getName().getString(); + } + + @Override + public void updateInventory() { + fabricPlayer.containerMenu.broadcastChanges(); + } + + @Override + public Vector3d getPosition() { + return new Vector3d(fabricPlayer.getX(), fabricPlayer.getY(), fabricPlayer.getZ()); + } + + @Override + public PlatformInventory getInventory() { + return inventory; + } + + @Override + public GrimEntity getVehicle() { + Entity vehicle = fabricPlayer.getVehicle(); + return vehicle != null ? GrimACFabricLoaderPlugin.LOADER.getPlatformPlayerFactory().getPlatformEntity(vehicle) : null; + } + + @Override + public GameMode getGameMode() { + return FabricConversionUtil.fromFabricGameMode(fabricPlayer.gameMode.getGameModeForPlayer()); + } + + @Override + public void setGameMode(GameMode gameMode) { + fabricPlayer.setGameMode(FabricConversionUtil.toFabricGameMode(gameMode)); + } + + @Override + public UUID getUniqueId() { + return fabricPlayer.getUUID(); + } + + @Override + public boolean isExternalPlayer() { + return false; + } + + @Override + public void sendPluginMessage(String channelName, byte[] byteArray) { + // You might want to use Fabric's networking system here +// CustomPayloadS2CPacket packet = new CustomPayloadS2CPacket( +// Identifier.of(channelName), +// new PacketByteBuf(Unpooled.wrappedBuffer(byteArray)) +// ); +// fabricPlayer.networkHandler.sendPacket(packet); + throw new UnsupportedOperationException(); + } + + @Override + public void replaceNativePlayer(Object nativePlayerObject) { + this.fabricPlayer = (ServerPlayer) nativePlayerObject; + } + + @Override + public @NotNull ServerPlayer getNative() { + return this.fabricPlayer; + } + + @Override + public boolean isDead() { + return fabricPlayer.isDeadOrDying(); + } +} diff --git a/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/player/FabricOfflinePlatformPlayer.java b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/player/FabricOfflinePlatformPlayer.java new file mode 100644 index 0000000000..2b9efe6403 --- /dev/null +++ b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/player/FabricOfflinePlatformPlayer.java @@ -0,0 +1,26 @@ +package ac.grim.grimac.platform.fabric.player; + +import ac.grim.grimac.platform.api.player.OfflinePlatformPlayer; +import ac.grim.grimac.platform.fabric.GrimACFabricLoaderPlugin; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.jetbrains.annotations.NotNull; + +import java.util.UUID; + +@RequiredArgsConstructor +@Getter +public class FabricOfflinePlatformPlayer implements OfflinePlatformPlayer { + private final @NotNull UUID uniqueId; + private final @NotNull String name; + + @Override + public boolean isOnline() { + return GrimACFabricLoaderPlugin.FABRIC_SERVER.getPlayerList().getPlayer(uniqueId) != null; + } + + @Override + public boolean equals(Object o) { + return o instanceof OfflinePlatformPlayer player && this.getUniqueId().equals(player.getUniqueId()); + } +} diff --git a/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/player/FabricPlatformPlayerFactory.java b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/player/FabricPlatformPlayerFactory.java new file mode 100644 index 0000000000..2b881ec440 --- /dev/null +++ b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/player/FabricPlatformPlayerFactory.java @@ -0,0 +1,131 @@ +package ac.grim.grimac.platform.fabric.player; + +import ac.grim.grimac.platform.api.entity.GrimEntity; +import ac.grim.grimac.platform.api.player.AbstractPlatformPlayerFactory; +import ac.grim.grimac.platform.api.player.OfflinePlatformPlayer; +import ac.grim.grimac.platform.fabric.GrimACFabricLoaderPlugin; +import com.mojang.authlib.GameProfile; +import lombok.RequiredArgsConstructor; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.level.storage.PlayerDataStorage; +import org.jetbrains.annotations.NotNull; + +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.function.Function; + +@RequiredArgsConstructor +public class FabricPlatformPlayerFactory extends AbstractPlatformPlayerFactory { + + private final Map offlinePlatformPlayerCache = new HashMap<>(); + private final Function getPlayerFunction; + private final Function getEntityFunction; + private final Function getPlayerInventoryFunction; + + @Override + protected ServerPlayer getNativePlayer(@NotNull UUID uuid) { + return GrimACFabricLoaderPlugin.FABRIC_SERVER.getPlayerList().getPlayer(uuid); + } + + @Override + protected ServerPlayer getNativePlayer(@NotNull String name) { + return GrimACFabricLoaderPlugin.FABRIC_SERVER.getPlayerList().getPlayerByName(name); + } + + @Override + protected AbstractFabricPlatformPlayer createPlatformPlayer(@NotNull ServerPlayer nativePlayer) { + return getPlayerFunction.apply(nativePlayer); + } + + @Override + protected UUID getPlayerUUID(@NotNull ServerPlayer nativePlayer) { + return nativePlayer.getUUID(); + } + + @Override + protected Collection getNativeOnlinePlayers() { + // Get the list of online players from the server + return GrimACFabricLoaderPlugin.FABRIC_SERVER.getPlayerList().getPlayers(); + } + + @Override + public OfflinePlatformPlayer getOfflineFromUUID(@NotNull UUID uuid) { + OfflinePlatformPlayer result = this.getFromUUID(uuid); + if (result == null) { + result = this.offlinePlatformPlayerCache.get(uuid); + if (result == null) { + result = new FabricOfflinePlatformPlayer(uuid, ""); + this.offlinePlatformPlayerCache.put(uuid, result); + } + } else { + this.offlinePlatformPlayerCache.remove(uuid); + } + + return result; + } + + @Override + public OfflinePlatformPlayer getOfflineFromName(@NotNull String name) { + OfflinePlatformPlayer result = this.getFromName(name); + if (result == null) { + GameProfile profile = null; + // Only fetch an online UUID in online mode + // TODO (cross-platform) add a config option for "offline-mode" servers with online-mode behind a proxy + if (GrimACFabricLoaderPlugin.FABRIC_SERVER.usesAuthentication()) { + // THIS CAN BLOCK THE CALLING THREAD! + profile = GrimACFabricLoaderPlugin.LOADER.getPlatformServer().getProfileByName(name); + } + + result = this.getOfflinePlayer(profile != null + // Use the GameProfile even when we get a UUID so we ensure we still have a name + ? profile + // Make an OfflinePlayer using an offline mode UUID since the name has no profile + : new GameProfile(UUID.nameUUIDFromBytes(("OfflinePlayer:" + name).getBytes(StandardCharsets.UTF_8)), name) + ); + } else { + this.offlinePlatformPlayerCache.remove(result.getUniqueId()); + } + + return result; + } + + @Override + public Collection getOfflinePlayers() { + PlayerDataStorage storage = GrimACFabricLoaderPlugin.FABRIC_SERVER.playerDataStorage; + String[] files = storage.playerDir.list((dir, name) -> name.endsWith(".dat")); + Set players = new HashSet<>(); + + for (String file : files) { + try { + players.add(this.getOfflineFromUUID(UUID.fromString(file.substring(0, file.length() - 4)))); + } catch (IllegalArgumentException ex) { + // ignore invalid fires in directory + } + } + + players.addAll(this.getOnlinePlayers()); + + return players; + } + + public OfflinePlatformPlayer getOfflinePlayer(GameProfile profile) { + // 26.X / authlib 7+: GameProfile is a Record. getId()/getName() → id()/name(). + OfflinePlatformPlayer player = new FabricOfflinePlatformPlayer(profile.id(), profile.name()); + this.offlinePlatformPlayerCache.put(profile.id(), player); + return player; + } + + @Override + public void replaceNativePlayer(@NotNull UUID uuid, @NotNull ServerPlayer serverPlayerEntity) { + super.cache.getPlayer(uuid).replaceNativePlayer(serverPlayerEntity); + } + + public AbstractFabricPlatformInventory getPlatformInventory(AbstractFabricPlatformPlayer serverPlayerEntity) { + return getPlayerInventoryFunction.apply(serverPlayerEntity); + } + + public GrimEntity getPlatformEntity(Entity entity) { + return getEntityFunction.apply(entity); + } +} diff --git a/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/resolver/FabricResolverRegistrar.java b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/resolver/FabricResolverRegistrar.java new file mode 100644 index 0000000000..293ba52fbc --- /dev/null +++ b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/resolver/FabricResolverRegistrar.java @@ -0,0 +1,183 @@ +package ac.grim.grimac.platform.fabric.resolver; + +import ac.grim.grimac.api.plugin.BasicGrimPlugin; +import ac.grim.grimac.api.plugin.GrimPlugin; +import ac.grim.grimac.internal.plugin.resolver.GrimExtensionManager; +import ac.grim.grimac.platform.fabric.utils.message.JULoggerFactory; +import lombok.RequiredArgsConstructor; +import net.fabricmc.api.*; +import net.fabricmc.loader.api.FabricLoader; +import net.fabricmc.loader.api.ModContainer; +import net.fabricmc.loader.api.entrypoint.EntrypointContainer; +import net.fabricmc.loader.api.entrypoint.PreLaunchEntrypoint; +import net.fabricmc.loader.api.metadata.Person; + +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Locale; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; + +/** + * Manages the registration of Fabric-specific resolvers with the GrimExtensionManager. + * This class is designed to be instantiated once during plugin startup. + */ +@RequiredArgsConstructor +public final class FabricResolverRegistrar { + + // Cache to ensure we only create one GrimPlugin wrapper per Fabric ModContainer. + private final Map modContainerCache = new ConcurrentHashMap<>(); + private final Map, GrimPlugin> classCache = new ConcurrentHashMap<>(); + private final Map entrypointCache = new ConcurrentHashMap<>(); + + /** + * Registers all the Fabric-specific resolvers in order of performance (fastest to slowest). + */ + public void registerAll(GrimExtensionManager extensionManager) { + extensionManager.setFailureHandler(this::createFailureException); + extensionManager.registerResolver(this::resolveModContainer); + extensionManager.registerResolver(this::resolveStringId); + extensionManager.registerResolver(this::resolveEntrypointInstance); + extensionManager.registerResolver(this::resolveClass); + } + + /** + * Create a shared, reusable function to handle the core logic of + * converting a Fabric ModContainer to a GrimPlugin wrapper. + */ + private GrimPlugin resolveMod(ModContainer modContainer) { + return modContainerCache.computeIfAbsent(modContainer, container -> { + net.fabricmc.loader.api.metadata.ModMetadata metadata = container.getMetadata(); + String folderName = metadata.getId().equals("grimac") ? metadata.getName() : metadata.getId(); + return new BasicGrimPlugin( + JULoggerFactory.createLogger(metadata.getName()), + new File(FabricLoader.getInstance().getConfigDir().toFile(), folderName), + metadata.getVersion().getFriendlyString(), + metadata.getDescription(), + metadata.getAuthors().stream().map(Person::getName).collect(Collectors.toList()) + ); + }); + } + + /** + * Resolver #0: Direct ModContainer (fastest) + */ + private GrimPlugin resolveModContainer(Object context) { + return (context instanceof ModContainer mc) ? resolveMod(mc) : null; + } + + /** + * Resolver #1: String Mod ID (very fast) + */ + private GrimPlugin resolveStringId(Object context) { + if (context instanceof String modId) { + // Mod IDs are enforced to always be fully lowercase + return FabricLoader.getInstance().getModContainer(modId.toLowerCase(Locale.ROOT)) + .map(this::resolveMod) + .orElse(null); + } + return null; + } + + /** + * Resolver #2: Mod Entrypoint Instance (slower, but cached) + */ + private GrimPlugin resolveEntrypointInstance(Object context) { + // We only care about potential entrypoint instances + if (context instanceof ModInitializer || context instanceof PreLaunchEntrypoint || context instanceof ClientModInitializer || context instanceof DedicatedServerModInitializer) { + return entrypointCache.computeIfAbsent(context, this::findEntrypoint); + } + return null; + } + + private GrimPlugin findEntrypoint(Object key) { + GrimPlugin result; + if ((result = findEntrypoint(key, ModInitializer.class, "main")) != null) return result; + if ((result = findEntrypoint(key, PreLaunchEntrypoint.class, "preLaunch")) != null) return result; + if ((result = findEntrypoint(key, ClientModInitializer.class, "client")) != null) return result; + if ((result = findEntrypoint(key, DedicatedServerModInitializer.class, "server")) != null) return result; + return null; + } + + private GrimPlugin findEntrypoint(Object context, Class entrypointClass, String entrypointKey) { + if (entrypointClass.isInstance(context)) { + for (EntrypointContainer container : FabricLoader.getInstance().getEntrypointContainers(entrypointKey, entrypointClass)) { + if (container.getEntrypoint() == context) { + return resolveMod(container.getProvider()); + } + } + } + return null; + } + + /** + * Resolver #3: Class object (slowest - involves I/O, but cached) + */ + private GrimPlugin resolveClass(Object context) { + if (context instanceof Class clazz) { + return classCache.computeIfAbsent(clazz, this::findClassProvider); + } + return null; + } + + private GrimPlugin findClassProvider(Class c) { + try { + // 1. Get the path to the physical JAR/directory the class was loaded from. + // This is our ground truth. + java.security.CodeSource codeSource = c.getProtectionDomain().getCodeSource(); + if (codeSource == null) return null; + java.net.URL sourceUrl = codeSource.getLocation(); + if (sourceUrl == null) return null; + Path sourcePath = Paths.get(sourceUrl.toURI()); + + // 2. Iterate through all mods and check if the class's source path + // matches the physical path of the mod's container. + for (ModContainer modContainer : FabricLoader.getInstance().getAllMods()) { + for (Path modRootPath : modContainer.getRootPaths()) { + URI modUri = modRootPath.toUri(); + Path modPhysicalPath = null; + + // 3. Determine the mod's physical path based on its URI scheme. + if ("file".equals(modUri.getScheme())) { + modPhysicalPath = modRootPath; + } else if ("jar".equals(modUri.getScheme())) { + String schemeSpecificPart = modUri.getSchemeSpecificPart(); + int separatorIndex = schemeSpecificPart.indexOf("!/"); + if (separatorIndex != -1) { + String jarUriString = schemeSpecificPart.substring(0, separatorIndex); + modPhysicalPath = Paths.get(new URI(jarUriString)); + } + } + + // 4. Perform the reliable file comparison. + if (modPhysicalPath != null && Files.isSameFile(modPhysicalPath, sourcePath)) { + return resolveMod(modContainer); + } + } + } + } catch (URISyntaxException | IOException | NullPointerException e) { + // Fail gracefully. + return null; + } + return null; + } + + private RuntimeException createFailureException(Object failedContext) { + String message = """ + Failed to resolve GrimPlugin context from the provided object of type '%s'. + + Please ensure you are passing one of the following: + - The main instance of your mod (e.g., 'this' from your ModInitializer class). + - The mod ID as a String (e.g., "my-mod-id"). + - Any Class from your mod's JAR file (e.g., MyListener.class). + - A pre-existing GrimPlugin instance. + """.formatted(failedContext.getClass().getName()); + return new IllegalArgumentException(message); + } +} diff --git a/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/scheduler/FabricAsyncScheduler.java b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/scheduler/FabricAsyncScheduler.java new file mode 100644 index 0000000000..fcddfc9ea7 --- /dev/null +++ b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/scheduler/FabricAsyncScheduler.java @@ -0,0 +1,118 @@ +package ac.grim.grimac.platform.fabric.scheduler; + +import ac.grim.grimac.api.plugin.GrimPlugin; +import ac.grim.grimac.platform.api.scheduler.AsyncScheduler; +import ac.grim.grimac.platform.api.scheduler.PlatformScheduler; +import ac.grim.grimac.platform.api.scheduler.TaskHandle; +import ac.grim.grimac.utils.data.Pair; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +public class FabricAsyncScheduler implements AsyncScheduler { + private final Map> asyncTasks = new HashMap<>(); + private final GrimPlugin plugin; + + public FabricAsyncScheduler(GrimPlugin plugin) { + this.plugin = plugin; + } + + @Override + public TaskHandle runNow(@NotNull GrimPlugin plugin, @NotNull Runnable task) { + Thread thread = new Thread(task); + Runnable cancellationTask = () -> { + thread.interrupt(); + asyncTasks.remove(thread); + }; + asyncTasks.put(thread, new Pair<>(plugin, cancellationTask)); + thread.start(); + return new FabricTaskHandle(cancellationTask, false); + } + + @Override + public TaskHandle runDelayed(@NotNull GrimPlugin plugin, @NotNull Runnable task, long delay, @NotNull TimeUnit timeUnit) { + long delayMillis = timeUnit.toMillis(delay); + Thread thread = new Thread(() -> { + try { + Thread.sleep(delayMillis); + task.run(); + } catch (InterruptedException e) { + // Handle interruption + } + }); + Runnable cancellationTask = () -> { + thread.interrupt(); + asyncTasks.remove(thread); + }; + asyncTasks.put(thread, new Pair<>(plugin, cancellationTask)); + thread.start(); + return new FabricTaskHandle(cancellationTask, false); // false for async + } + + @Override + public TaskHandle runAtFixedRate(@NotNull GrimPlugin plugin, @NotNull Runnable task, long delay, long period, @NotNull TimeUnit timeUnit) { + long delayMillis = timeUnit.toMillis(delay); + long periodMillis = timeUnit.toMillis(period); + Thread thread = new Thread(() -> { + try { + Thread.sleep(delayMillis); + while (!Thread.currentThread().isInterrupted()) { + task.run(); + Thread.sleep(periodMillis); + } + } catch (InterruptedException e) { + // Handle interruption + } + }); + Runnable cancellationTask = () -> { + thread.interrupt(); + asyncTasks.remove(thread); + }; + asyncTasks.put(thread, new Pair<>(plugin, cancellationTask)); + thread.start(); + return new FabricTaskHandle(cancellationTask, false); // false for async + } + + @Override + public TaskHandle runAtFixedRate(@NotNull GrimPlugin plugin, @NotNull Runnable task, long initialDelayTicks, long periodTicks) { + return runAtFixedRate(plugin, task, + PlatformScheduler.convertTicksToTime(initialDelayTicks, TimeUnit.MILLISECONDS), + PlatformScheduler.convertTicksToTime(periodTicks, TimeUnit.MILLISECONDS), + TimeUnit.MILLISECONDS); // Convert ticks to milliseconds + } + + @Override + public void cancel(@NotNull GrimPlugin plugin) { + // Cancel tasks only for the specified plugin + Iterator>> iterator = asyncTasks.entrySet().iterator(); + List cancellationTasks = new ArrayList<>(); + + while (iterator.hasNext()) { + Map.Entry> entry = iterator.next(); + if (entry.getValue().first().equals(plugin)) { + cancellationTasks.add(entry.getValue().second()); + iterator.remove(); + } + } + + for (Runnable cancellationTask : cancellationTasks) { + cancellationTask.run(); + } + } + + public void cancelAll() { + List cancellationTasks = asyncTasks.values().stream() + .map(Pair::second) + .toList(); + asyncTasks.clear(); + + for (Runnable cancellationTask : cancellationTasks) { + cancellationTask.run(); + } + } +} diff --git a/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/scheduler/FabricEntityScheduler.java b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/scheduler/FabricEntityScheduler.java new file mode 100644 index 0000000000..87e9517657 --- /dev/null +++ b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/scheduler/FabricEntityScheduler.java @@ -0,0 +1,85 @@ +package ac.grim.grimac.platform.fabric.scheduler; + +import ac.grim.grimac.api.plugin.GrimPlugin; +import ac.grim.grimac.platform.api.entity.GrimEntity; +import ac.grim.grimac.platform.api.scheduler.EntityScheduler; +import ac.grim.grimac.platform.api.scheduler.TaskHandle; +import ac.grim.grimac.platform.fabric.GrimACFabricLoaderPlugin; +import ac.grim.grimac.platform.fabric.FabricServerEvents; +import net.minecraft.server.MinecraftServer; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class FabricEntityScheduler implements EntityScheduler { + // TODO (Cross-platform) (Threading) try to make this not Concurrent + private final Map taskMap = new ConcurrentHashMap<>(); + private final GrimPlugin plugin; + + public FabricEntityScheduler(GrimPlugin plugin) { + this.plugin = plugin; + FabricServerEvents.onEndTick(this::handleTasks); + } + + private void handleTasks(MinecraftServer server) { + FabricPlatformScheduler.handleSyncTasks(taskMap, server, plugin); + } + + @Override + public void execute(@NotNull GrimEntity entity, @NotNull GrimPlugin plugin, @NotNull Runnable run, @Nullable Runnable retired, long delay) { + runDelayed(entity, plugin, run, retired, delay); + } + + @Override + public TaskHandle run(@NotNull GrimEntity entity, @NotNull GrimPlugin plugin, @NotNull Runnable task, @Nullable Runnable retired) { + return runDelayed(entity, plugin, task, retired, 0); + } + + @Override + public TaskHandle runDelayed(@NotNull GrimEntity entity, @NotNull GrimPlugin plugin, @NotNull Runnable task, @Nullable Runnable retired, long delayTicks) { + FabricPlatformScheduler.ScheduledTask scheduledTask = new FabricPlatformScheduler.ScheduledTask( + () -> { + task.run(); + if (retired != null && entity.isDead()) { + retired.run(); + } + }, + GrimACFabricLoaderPlugin.FABRIC_SERVER.getTickCount() + delayTicks, + 0, + false, + plugin + ); + Runnable cancellationTask = () -> taskMap.remove(scheduledTask); + taskMap.put(scheduledTask, cancellationTask); + return new FabricTaskHandle(cancellationTask, true); // true for sync + } + + @Override + public TaskHandle runAtFixedRate(@NotNull GrimEntity entity, @NotNull GrimPlugin plugin, @NotNull Runnable task, @Nullable Runnable retired, long initialDelayTicks, long periodTicks) { + FabricPlatformScheduler.ScheduledTask scheduledTask = new FabricPlatformScheduler.ScheduledTask( + () -> { + task.run(); + if (retired != null && entity.isDead()) { + retired.run(); + } + }, + GrimACFabricLoaderPlugin.FABRIC_SERVER.getTickCount() + initialDelayTicks, + periodTicks, + true, + plugin + ); + Runnable cancellationTask = () -> taskMap.remove(scheduledTask); + taskMap.put(scheduledTask, cancellationTask); + return new FabricTaskHandle(cancellationTask, true); // true for sync + } + + public void cancel(@NotNull GrimPlugin plugin) { + FabricPlatformScheduler.cancelPluginTasks(taskMap, plugin); + } + + public void cancelAll() { + FabricPlatformScheduler.cancelAllTasks(taskMap); + } +} diff --git a/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/scheduler/FabricGlobalRegionScheduler.java b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/scheduler/FabricGlobalRegionScheduler.java new file mode 100644 index 0000000000..e4cb0e88fe --- /dev/null +++ b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/scheduler/FabricGlobalRegionScheduler.java @@ -0,0 +1,76 @@ +package ac.grim.grimac.platform.fabric.scheduler; + +import ac.grim.grimac.api.plugin.GrimPlugin; +import ac.grim.grimac.platform.api.scheduler.GlobalRegionScheduler; +import ac.grim.grimac.platform.api.scheduler.TaskHandle; +import ac.grim.grimac.platform.fabric.GrimACFabricLoaderPlugin; +import ac.grim.grimac.platform.fabric.FabricServerEvents; +import net.minecraft.server.MinecraftServer; +import org.jetbrains.annotations.NotNull; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class FabricGlobalRegionScheduler implements GlobalRegionScheduler { + // TODO (Cross-platform) (Threading) try to make this not Concurrent + private final Map taskMap = new ConcurrentHashMap<>(); + private final GrimPlugin plugin; + + public FabricGlobalRegionScheduler(GrimPlugin plugin) { + this.plugin = plugin; + // Register the task handler to run on server tick + FabricServerEvents.onEndTick(this::handleTasks); + } + + private void handleTasks(MinecraftServer server) { + FabricPlatformScheduler.handleSyncTasks(taskMap, server, plugin); + } + + @Override + public void execute(@NotNull GrimPlugin plugin, @NotNull Runnable run) { + run(plugin, run); + } + + @Override + public TaskHandle run(@NotNull GrimPlugin plugin, @NotNull Runnable task) { + return runDelayed(plugin, task, 0); + } + + @Override + public TaskHandle runDelayed(@NotNull GrimPlugin plugin, @NotNull Runnable task, long delay) { + FabricPlatformScheduler.ScheduledTask scheduledTask = new FabricPlatformScheduler.ScheduledTask( + task, + GrimACFabricLoaderPlugin.FABRIC_SERVER.getTickCount() + delay, + 0, + false, + plugin + ); + Runnable cancellationTask = () -> taskMap.remove(scheduledTask); + taskMap.put(scheduledTask, cancellationTask); + return new FabricTaskHandle(cancellationTask, true); // true for sync + } + + @Override + public TaskHandle runAtFixedRate(@NotNull GrimPlugin plugin, @NotNull Runnable task, long initialDelayTicks, long periodTicks) { + FabricPlatformScheduler.ScheduledTask scheduledTask = new FabricPlatformScheduler.ScheduledTask( + task, + GrimACFabricLoaderPlugin.FABRIC_SERVER.getTickCount() + initialDelayTicks, + periodTicks, + true, + plugin + ); + Runnable cancellationTask = () -> taskMap.remove(scheduledTask); + taskMap.put(scheduledTask, cancellationTask); + return new FabricTaskHandle(cancellationTask, true); // true for sync + } + + @Override + public void cancel(@NotNull GrimPlugin plugin) { + FabricPlatformScheduler.cancelPluginTasks(taskMap, plugin); + } + + // New method to cancel all tasks + public void cancelAll() { + FabricPlatformScheduler.cancelAllTasks(taskMap); + } +} diff --git a/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/scheduler/FabricPlatformScheduler.java b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/scheduler/FabricPlatformScheduler.java new file mode 100644 index 0000000000..3b7f8f8939 --- /dev/null +++ b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/scheduler/FabricPlatformScheduler.java @@ -0,0 +1,133 @@ +package ac.grim.grimac.platform.fabric.scheduler; + +import ac.grim.grimac.GrimAPI; +import ac.grim.grimac.api.plugin.GrimPlugin; +import ac.grim.grimac.platform.api.scheduler.*; +import ac.grim.grimac.utils.anticheat.LogUtil; +import net.minecraft.server.MinecraftServer; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +public class FabricPlatformScheduler implements PlatformScheduler { + private final FabricAsyncScheduler asyncScheduler; + private final FabricGlobalRegionScheduler globalRegionScheduler; + private final FabricEntityScheduler entityScheduler; + private final FabricRegionScheduler regionScheduler; + + public FabricPlatformScheduler() { + GrimPlugin plugin = GrimAPI.INSTANCE.getGrimPlugin(); + this.asyncScheduler = new FabricAsyncScheduler(plugin); + this.globalRegionScheduler = new FabricGlobalRegionScheduler(plugin); + this.entityScheduler = new FabricEntityScheduler(plugin); + this.regionScheduler = new FabricRegionScheduler(plugin); + } + + // Shared method to handle synchronous tasks + // Add this to FabricPlatformScheduler.java + public static final ThreadLocal EXECUTING_TASK = ThreadLocal.withInitial(() -> false); + + protected static void handleSyncTasks(Map taskMap, MinecraftServer server, GrimPlugin plugin) { + Iterator iterator = taskMap.keySet().iterator(); + while (iterator.hasNext()) { + ScheduledTask task = iterator.next(); + if (server.getTickCount() >= task.nextRunTick) { + try { + EXECUTING_TASK.set(true); + task.task.run(); + } catch (Exception e) { + LogUtil.error("Error executing scheduled task ", e); + } finally { + EXECUTING_TASK.set(false); + } + + if (task.isPeriodic) { + task.nextRunTick = server.getTickCount() + task.period; + } else { + iterator.remove(); + } + } + } + } + + // Cancel tasks for a specific plugin + protected static void cancelPluginTasks(Map taskMap, GrimPlugin plugin) { + Iterator> iterator = taskMap.entrySet().iterator(); + List cancellationTasks = new ArrayList<>(); + + while (iterator.hasNext()) { + Map.Entry entry = iterator.next(); + if (entry.getKey().plugin.equals(plugin)) { + cancellationTasks.add(entry.getValue()); + iterator.remove(); + } + } + + for (Runnable cancellationTask : cancellationTasks) { + cancellationTask.run(); + } + } + + // Cancel all tasks (renamed from cancelAllTasks) + protected static void cancelAllTasks(Map taskMap) { + List cancellationTasks = new ArrayList<>(taskMap.values()); + taskMap.clear(); + for (Runnable cancellationTask : cancellationTasks) { + cancellationTask.run(); + } + } + + protected static void scheduleTask(Map taskMap, GrimPlugin plugin, Runnable task, long initialDelayTicks, long periodTicks, boolean isPeriodic) { + + } + + @Override + public @NotNull AsyncScheduler getAsyncScheduler() { + return asyncScheduler; + } + + @Override + public @NotNull GlobalRegionScheduler getGlobalRegionScheduler() { + return globalRegionScheduler; + } + + @Override + public @NotNull EntityScheduler getEntityScheduler() { + return entityScheduler; + } + + @Override + public @NotNull RegionScheduler getRegionScheduler() { + return regionScheduler; + } + + /** + * Shuts down all schedulers and cancels all pending tasks. + * This method should be called when the server is shutting down. + */ + public void shutdown() { + asyncScheduler.cancelAll(); + globalRegionScheduler.cancelAll(); + entityScheduler.cancelAll(); + regionScheduler.cancelAll(); + } + + protected static class ScheduledTask { + final Runnable task; + final long period; + final boolean isPeriodic; + final GrimPlugin plugin; // Add plugin reference + long nextRunTick; + + ScheduledTask(Runnable task, long nextRunTick, long period, boolean isPeriodic, GrimPlugin plugin) { + this.task = task; + this.nextRunTick = nextRunTick; + this.period = period; + this.isPeriodic = isPeriodic; + this.plugin = plugin; + } + } +} diff --git a/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/scheduler/FabricRegionScheduler.java b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/scheduler/FabricRegionScheduler.java new file mode 100644 index 0000000000..807c3f98c7 --- /dev/null +++ b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/scheduler/FabricRegionScheduler.java @@ -0,0 +1,94 @@ +package ac.grim.grimac.platform.fabric.scheduler; + +import ac.grim.grimac.api.plugin.GrimPlugin; +import ac.grim.grimac.platform.api.scheduler.RegionScheduler; +import ac.grim.grimac.platform.api.scheduler.TaskHandle; +import ac.grim.grimac.platform.api.world.PlatformWorld; +import ac.grim.grimac.platform.fabric.GrimACFabricLoaderPlugin; +import ac.grim.grimac.utils.math.Location; +import ac.grim.grimac.platform.fabric.FabricServerEvents; +import net.minecraft.server.MinecraftServer; +import org.jetbrains.annotations.NotNull; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class FabricRegionScheduler implements RegionScheduler { + // TODO (Cross-platform) (Threading) try to make this not Concurrent + private final Map taskMap = new ConcurrentHashMap<>(); + private final GrimPlugin plugin; + + public FabricRegionScheduler(GrimPlugin plugin) { + this.plugin = plugin; + FabricServerEvents.onEndTick(this::handleTasks); + } + + private void handleTasks(MinecraftServer server) { + FabricPlatformScheduler.handleSyncTasks(taskMap, server, plugin); + } + + @Override + public void execute(@NotNull GrimPlugin plugin, @NotNull PlatformWorld world, int chunkX, int chunkZ, @NotNull Runnable run) { + run(plugin, world, chunkX, chunkZ, run); + } + + @Override + public void execute(@NotNull GrimPlugin plugin, @NotNull Location location, @NotNull Runnable run) { + execute(plugin, location.getWorld(), location.getBlockX() >> 4, location.getBlockZ() >> 4, run); + } + + @Override + public TaskHandle run(@NotNull GrimPlugin plugin, @NotNull PlatformWorld world, int chunkX, int chunkZ, @NotNull Runnable task) { + return runDelayed(plugin, world, chunkX, chunkZ, task, 0); + } + + @Override + public TaskHandle run(@NotNull GrimPlugin plugin, @NotNull Location location, @NotNull Runnable task) { + return run(plugin, location.getWorld(), location.getBlockX() >> 4, location.getBlockZ() >> 4, task); + } + + @Override + public TaskHandle runDelayed(@NotNull GrimPlugin plugin, @NotNull PlatformWorld world, int chunkX, int chunkZ, @NotNull Runnable task, long delayTicks) { + FabricPlatformScheduler.ScheduledTask scheduledTask = new FabricPlatformScheduler.ScheduledTask( + task, + GrimACFabricLoaderPlugin.FABRIC_SERVER.getTickCount() + delayTicks, + 0, + false, + plugin + ); + Runnable cancellationTask = () -> taskMap.remove(scheduledTask); + taskMap.put(scheduledTask, cancellationTask); + return new FabricTaskHandle(cancellationTask, true); + } + @Override + public TaskHandle runDelayed(@NotNull GrimPlugin plugin, @NotNull Location location, @NotNull Runnable task, long delayTicks) { + return runDelayed(plugin, location.getWorld(), location.getBlockX() >> 4, location.getBlockZ() >> 4, task, delayTicks); + } + + @Override + public TaskHandle runAtFixedRate(@NotNull GrimPlugin plugin, @NotNull PlatformWorld world, int chunkX, int chunkZ, @NotNull Runnable task, long initialDelayTicks, long periodTicks) { + FabricPlatformScheduler.ScheduledTask scheduledTask = new FabricPlatformScheduler.ScheduledTask( + task, + GrimACFabricLoaderPlugin.FABRIC_SERVER.getTickCount() + initialDelayTicks, + periodTicks, + true, + plugin + ); + Runnable cancellationTask = () -> taskMap.remove(scheduledTask); + taskMap.put(scheduledTask, cancellationTask); + return new FabricTaskHandle(cancellationTask, true); + } + + @Override + public TaskHandle runAtFixedRate(@NotNull GrimPlugin plugin, @NotNull Location location, @NotNull Runnable task, long initialDelayTicks, long periodTicks) { + return runAtFixedRate(plugin, location.getWorld(), location.getBlockX() >> 4, location.getBlockZ() >> 4, task, initialDelayTicks, periodTicks); + } + + public void cancel(@NotNull GrimPlugin plugin) { + FabricPlatformScheduler.cancelPluginTasks(taskMap, plugin); + } + + public void cancelAll() { + FabricPlatformScheduler.cancelAllTasks(taskMap); + } +} diff --git a/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/scheduler/FabricTaskHandle.java b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/scheduler/FabricTaskHandle.java new file mode 100644 index 0000000000..5d4279c9bd --- /dev/null +++ b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/scheduler/FabricTaskHandle.java @@ -0,0 +1,28 @@ +package ac.grim.grimac.platform.fabric.scheduler; + +import ac.grim.grimac.platform.api.scheduler.TaskHandle; +import lombok.Getter; + +public class FabricTaskHandle implements TaskHandle { + private final Runnable cancellationTask; + @Getter + private boolean cancelled; + @Getter + private final boolean sync; + + public FabricTaskHandle(Runnable cancellationTask) { + this.cancellationTask = cancellationTask; + this.sync = false; + } + + public FabricTaskHandle(Runnable cancellationTask, boolean sync) { + this.cancellationTask = cancellationTask; + this.sync = sync; + } + + @Override + public void cancel() { + this.cancellationTask.run(); + this.cancelled = true; + } +} diff --git a/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/sender/NoopFabricSenderFactory.java b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/sender/NoopFabricSenderFactory.java new file mode 100644 index 0000000000..fe34f966f5 --- /dev/null +++ b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/sender/NoopFabricSenderFactory.java @@ -0,0 +1,87 @@ +package ac.grim.grimac.platform.fabric.sender; + +import ac.grim.grimac.platform.api.sender.Sender; +import ac.grim.grimac.platform.api.sender.SenderFactory; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.flattener.ComponentFlattener; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.CommandSource; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.permissions.Permission; +import net.minecraft.server.permissions.PermissionLevel; +import net.minecraft.server.rcon.RconConsoleSource; + +import java.util.UUID; + +// Sender factory for fabric-official. Implements just enough of the SenderFactory +// contract to keep Grim's :common services happy on 26.X without pulling in +// fabric-permissions-api (intermediary-bound). Permission checks fall through +// to vanilla op-level via CommandSourceStack.hasPermission(int). +public class NoopFabricSenderFactory extends SenderFactory { + + @Override + protected UUID getUniqueId(CommandSourceStack source) { + if (source.getEntity() != null) { + return source.getEntity().getUUID(); + } + return Sender.CONSOLE_UUID; + } + + @Override + protected String getName(CommandSourceStack source) { + String name = source.getTextName(); + if (source.getEntity() != null && name.equals("Server")) { + return Sender.CONSOLE_NAME; + } + return name; + } + + @Override + protected void sendMessage(CommandSourceStack source, String message) { + source.sendSuccess(() -> net.minecraft.network.chat.Component.literal(message), false); + } + + @Override + protected void sendMessage(CommandSourceStack source, Component message) { + // Adventure → MC Component conversion would require adventure-platform-fabric; + // adventure-text-serializer-plain isn't on the fabric-official classpath either. + // Flatten via the always-available ComponentFlattener (adventure-api core) — loses + // formatting but preserves text content. The conversion-util pass in Phase C will + // replace this with proper formatted text once a 26.X-native adventure path lands. + StringBuilder out = new StringBuilder(); + ComponentFlattener.basic().flatten(message, out::append); + sendMessage(source, out.toString()); + } + + @Override + protected boolean hasPermission(CommandSourceStack source, String node) { + // 26.X overhauled permissions — hasPermission(int) is gone, replaced by + // PermissionSet.hasPermission(Permission). Fall back to op level 2 (vanilla + // "ops only") since fabric-permissions-api isn't ported. + return source.permissions().hasPermission( + new Permission.HasCommandLevel(PermissionLevel.byId(2))); + } + + @Override + protected boolean hasPermission(CommandSourceStack source, String node, boolean defaultIfUnset) { + return defaultIfUnset ? true : hasPermission(source, node); + } + + @Override + protected void performCommand(CommandSourceStack source, String command) { + throw new UnsupportedOperationException("performCommand not implemented on 26.X scaffold"); + } + + @Override + protected boolean isConsole(CommandSourceStack source) { + CommandSource out = source.source; + return out == source.getServer() + || out.getClass() == RconConsoleSource.class + || (out == CommandSource.NULL && source.getTextName().isEmpty()); + } + + @Override + protected boolean isPlayer(CommandSourceStack source) { + return source.getEntity() instanceof ServerPlayer; + } +} diff --git a/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/utils/PolymerHook.java b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/utils/PolymerHook.java new file mode 100644 index 0000000000..debfbd4c5e --- /dev/null +++ b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/utils/PolymerHook.java @@ -0,0 +1,86 @@ +package ac.grim.grimac.platform.fabric.utils; + +import ac.grim.grimac.platform.api.player.BlockTranslator; +import net.fabricmc.loader.api.FabricLoader; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.lang.reflect.Method; + +public class PolymerHook { + private static final boolean HAS_POLYMER = FabricLoader.getInstance().isModLoaded("polymer-core"); + + private static final MethodHandle CREATE_CONTEXT; + private static final MethodHandle GET_STATE; + + static { + MethodHandle createContext = null; + MethodHandle getState = null; + + if (HAS_POLYMER) { + try { + MethodHandles.Lookup lookup = MethodHandles.publicLookup(); + Class contextClass = Class.forName("xyz.nucleoid.packettweaker.PacketContext"); + Class utilsClass = Class.forName("eu.pb4.polymer.core.api.block.PolymerBlockUtils"); + + Method createMethod = null; + for (Method m : contextClass.getDeclaredMethods()) { + if (m.getName().equals("create") && m.getParameterCount() == 1 && m.getParameterTypes()[0] == ServerPlayer.class) { + createMethod = m; + break; + } + } + + if (createMethod != null) { + MethodHandle rawCreate = lookup.unreflect(createMethod); + + createContext = rawCreate.asType(MethodType.methodType(Object.class, ServerPlayer.class)); + } + MethodHandle rawGet = lookup.findStatic(utilsClass, "getPolymerBlockState", + MethodType.methodType(BlockState.class, BlockState.class, contextClass)); + getState = rawGet.asType(MethodType.methodType(BlockState.class, BlockState.class, Object.class)); + + } catch (Throwable t) { + // If Polymer changes their API drastically, log it so server owners know why custom blocks aren't translating + System.err.println("[GrimAC] Failed to hook Polymer translation API. Custom blocks may not render correctly or crash client when re-synchronizing."); + t.printStackTrace(); + } + } + + CREATE_CONTEXT = createContext; + GET_STATE = getState; + } + + public static BlockTranslator createTranslator(ServerPlayer player) { + if (!HAS_POLYMER || CREATE_CONTEXT == null || GET_STATE == null) { + return BlockTranslator.IDENTITY; + } + + try { + Object context = (Object) CREATE_CONTEXT.invokeExact(player); + return new PolymerBlockTranslator(context); + } catch (Throwable t) { + return BlockTranslator.IDENTITY; + } + } + + public record PolymerBlockTranslator(Object context) implements BlockTranslator { + + @Override + public int translate(int serverBlockId) { + try { + BlockState state = Block.stateById(serverBlockId); + + BlockState mappedState = (BlockState) GET_STATE.invokeExact(state, context); + + return Block.getId(mappedState); + } catch (Throwable t) { + return serverBlockId; + } + } + } +} diff --git a/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/utils/convert/FabricConversionUtil.java b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/utils/convert/FabricConversionUtil.java new file mode 100644 index 0000000000..fe37e36edb --- /dev/null +++ b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/utils/convert/FabricConversionUtil.java @@ -0,0 +1,102 @@ +package ac.grim.grimac.platform.fabric.utils.convert; + +import com.github.retrooper.packetevents.protocol.item.ItemStack; +import com.github.retrooper.packetevents.protocol.player.GameMode; +import com.github.retrooper.packetevents.protocol.player.InteractionHand; +import com.github.retrooper.packetevents.protocol.world.BlockFace; +import net.kyori.adventure.text.Component; +import net.minecraft.core.Direction; +import net.minecraft.world.level.GameType; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.Nullable; + +import java.util.function.Function; + +public abstract class FabricConversionUtil implements IFabricConversionUtil { + + private IFabricConversionUtil fabricConversionUtilSupplier; + + private final Function itemStackMapperFunction = (fabricStack) -> { +// if (fabricStack.isEmpty()) { +// return ItemStack.EMPTY; +// } +// +// // Allocate a ByteBuf +// 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 +// +// // Create a RegistryByteBuf +// RegistryByteBuf registryByteBuf = new RegistryByteBuf(buffer, registryManager); +// +// // Encode the ItemStack using the appropriate PacketCodec +// net.minecraft.item.ItemStack.PACKET_CODEC.encode(registryByteBuf, fabricStack); +// +// // Create a PacketWrapper to read the ItemStack back (if needed) +// PacketWrapper wrapper = PacketWrapper.createUniversalPacketWrapper(buffer); +// return wrapper.readItemStack(); +// } catch (Exception e) { +// // Handle encoding errors +// LogUtil.error("Failed to encode ItemStack: {}" + fabricStack, e); +// return ItemStack.EMPTY; +// } finally { +// // Release the ByteBuf to prevent memory leaks +// ByteBufHelper.release(buffer); +// } + throw new UnsupportedOperationException(); + }; + private final Function nativeTextMapperFunction = (component) -> { + throw new UnsupportedOperationException(); +// Text.Serialization.fromJsonTree(GsonComponentSerializer.gson().serializeToTree(component), DynamicRegistryManager.EMPTY); + }; +// + + public ItemStack fromFabricItemStack(net.minecraft.world.item.ItemStack fabricStack) { +// return itemStackMapperFunction.apply(fabricStack); + return fabricConversionUtilSupplier.fromFabricItemStack(fabricStack); + } + + public net.minecraft.network.chat.Component toNativeText(Component component) { +// return nativeTextMapperFunction.apply(component); + return fabricConversionUtilSupplier.toNativeText(component); + } + + public static GameType toFabricGameMode(GameMode gameMode) { + return switch (gameMode) { + case CREATIVE -> GameType.CREATIVE; + case SURVIVAL -> GameType.SURVIVAL; + case ADVENTURE -> GameType.ADVENTURE; + case SPECTATOR -> GameType.SPECTATOR; + }; + } + + public static GameMode fromFabricGameMode(GameType fabricGameMode) { + return switch (fabricGameMode) { + case CREATIVE -> GameMode.CREATIVE; + case SURVIVAL -> GameMode.SURVIVAL; + case ADVENTURE -> GameMode.ADVENTURE; + case SPECTATOR -> GameMode.SPECTATOR; + default -> throw new IllegalArgumentException("Unknown Fabric GameMode: " + fabricGameMode); + }; + } + + @Contract(value = "null -> null; !null -> !null", pure = true) + public static @Nullable InteractionHand fromFabricHand(@Nullable net.minecraft.world.InteractionHand hand) { + return hand == null ? null : switch (hand) { + case OFF_HAND -> InteractionHand.OFF_HAND; + case MAIN_HAND -> InteractionHand.MAIN_HAND; + }; + } + + public static BlockFace fromDirection(Direction direction) { + return switch (direction) { + case NORTH -> BlockFace.NORTH; + case SOUTH -> BlockFace.SOUTH; + case WEST -> BlockFace.WEST; + case EAST -> BlockFace.EAST; + case UP -> BlockFace.UP; + case DOWN -> BlockFace.DOWN; + }; + } +} diff --git a/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/utils/convert/IFabricConversionUtil.java b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/utils/convert/IFabricConversionUtil.java new file mode 100644 index 0000000000..9f461db0ae --- /dev/null +++ b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/utils/convert/IFabricConversionUtil.java @@ -0,0 +1,9 @@ +package ac.grim.grimac.platform.fabric.utils.convert; + +import com.github.retrooper.packetevents.protocol.item.ItemStack; +import net.kyori.adventure.text.Component; + +public interface IFabricConversionUtil { + ItemStack fromFabricItemStack(net.minecraft.world.item.ItemStack fabricStack); + net.minecraft.network.chat.Component toNativeText(Component component); +} diff --git a/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/utils/message/IFabricMessageUtil.java b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/utils/message/IFabricMessageUtil.java new file mode 100644 index 0000000000..696d265839 --- /dev/null +++ b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/utils/message/IFabricMessageUtil.java @@ -0,0 +1,9 @@ +package ac.grim.grimac.platform.fabric.utils.message; + +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.network.chat.Component; + +public interface IFabricMessageUtil { + Component textLiteral(String message); + void sendMessage(CommandSourceStack target, Component message, boolean overlay); +} diff --git a/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/utils/message/JULoggerFactory.java b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/utils/message/JULoggerFactory.java new file mode 100644 index 0000000000..a99ce55ebd --- /dev/null +++ b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/utils/message/JULoggerFactory.java @@ -0,0 +1,18 @@ +package ac.grim.grimac.platform.fabric.utils.message; + +import java.util.logging.Logger; + +public class JULoggerFactory { + public static Logger createLogger(String name) { + try { + return new Slf4jBackedJULogger(name); + } catch (NoClassDefFoundError | Exception ignored) { + } + try { + return new Log4jBackedJULogger(name); + } catch (NoClassDefFoundError | Exception ignored) { + } + + return Logger.getLogger(name); + } +} diff --git a/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/utils/message/Log4jBackedJULogger.java b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/utils/message/Log4jBackedJULogger.java new file mode 100644 index 0000000000..e369e3e575 --- /dev/null +++ b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/utils/message/Log4jBackedJULogger.java @@ -0,0 +1,47 @@ +package ac.grim.grimac.platform.fabric.utils.message; + +import java.util.logging.Level; +import java.util.logging.Logger; + +public class Log4jBackedJULogger extends Logger { + private final org.apache.logging.log4j.Logger log4jLogger; + + protected Log4jBackedJULogger(String name) { + super(name, null); + this.log4jLogger = org.apache.logging.log4j.LogManager.getLogger(name); + } + + @Override + public void log(Level level, String msg) { + if (level == Level.SEVERE) { + log4jLogger.error(msg); + } else if (level == Level.WARNING) { + log4jLogger.warn(msg); + } else if (level == Level.INFO) { + log4jLogger.info(msg); + } else if (level == Level.CONFIG || level == Level.FINE) { + log4jLogger.debug(msg); + } else if (level == Level.FINER || level == Level.FINEST) { + log4jLogger.trace(msg); + } else { + log4jLogger.info(msg); + } + } + + @Override + public void log(Level level, String msg, Throwable thrown) { + if (level == Level.SEVERE) { + log4jLogger.error(msg, thrown); + } else if (level == Level.WARNING) { + log4jLogger.warn(msg, thrown); + } else if (level == Level.INFO) { + log4jLogger.info(msg, thrown); + } else if (level == Level.CONFIG || level == Level.FINE) { + log4jLogger.debug(msg, thrown); + } else if (level == Level.FINER || level == Level.FINEST) { + log4jLogger.trace(msg, thrown); + } else { + log4jLogger.info(msg, thrown); + } + } +} diff --git a/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/utils/message/Slf4jBackedJULogger.java b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/utils/message/Slf4jBackedJULogger.java new file mode 100644 index 0000000000..3792720fd4 --- /dev/null +++ b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/utils/message/Slf4jBackedJULogger.java @@ -0,0 +1,51 @@ +package ac.grim.grimac.platform.fabric.utils.message; + +import java.util.logging.Level; +import java.util.logging.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.Marker; +import org.slf4j.MarkerFactory; + +public class Slf4jBackedJULogger extends Logger { + private final org.slf4j.Logger slf4jLogger; + private static final Marker MARKER = MarkerFactory.getMarker("JUL"); + + public Slf4jBackedJULogger(String name) { + super(name, null); + this.slf4jLogger = LoggerFactory.getLogger(name); + } + + @Override + public void log(Level level, String msg) { + if (level == Level.SEVERE) { + slf4jLogger.error(MARKER, msg); + } else if (level == Level.WARNING) { + slf4jLogger.warn(MARKER, msg); + } else if (level == Level.INFO) { + slf4jLogger.info(MARKER, msg); + } else if (level == Level.CONFIG || level == Level.FINE) { + slf4jLogger.debug(MARKER, msg); + } else if (level == Level.FINER || level == Level.FINEST) { + slf4jLogger.trace(MARKER, msg); + } else { + slf4jLogger.info(MARKER, msg); + } + } + + @Override + public void log(Level level, String msg, Throwable thrown) { + if (level.equals(Level.SEVERE)) { + slf4jLogger.error(MARKER, msg, thrown); + } else if (level.equals(Level.WARNING)) { + slf4jLogger.warn(MARKER, msg, thrown); + } else if (level.equals(Level.INFO)) { + slf4jLogger.info(MARKER, msg, thrown); + } else if (level.equals(Level.CONFIG) || level.equals(Level.FINE)) { + slf4jLogger.debug(MARKER, msg, thrown); + } else if (level.equals(Level.FINER) || level.equals(Level.FINEST)) { + slf4jLogger.trace(MARKER, msg, thrown); + } else { + slf4jLogger.info(MARKER, msg, thrown); + } + } +} diff --git a/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/utils/metrics/BStatsConfig.java b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/utils/metrics/BStatsConfig.java new file mode 100644 index 0000000000..25c0c1120a --- /dev/null +++ b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/utils/metrics/BStatsConfig.java @@ -0,0 +1,98 @@ +package ac.grim.grimac.platform.fabric.utils.metrics; + +import ac.grim.grimac.utils.anticheat.LogUtil; +import net.fabricmc.loader.api.FabricLoader; +import org.yaml.snakeyaml.DumperOptions; +import org.yaml.snakeyaml.Yaml; + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.UUID; + +public class BStatsConfig { + private static final String HEADER = """ + # bStats (https://bStats.org) collects some basic information for plugin authors, like how + # many people use their plugin and their total player count. It's recommended to keep bStats + # enabled, but if you're not comfortable with this, you can turn this setting off. There is no + # performance penalty associated with having metrics enabled, and data sent to bStats is fully + # anonymous. + """; + + public static Config loadConfig() { + File bStatsFolder = new File(FabricLoader.getInstance().getConfigDir().toString(), "bStats"); + File configFile = new File(bStatsFolder, "config.yml"); + + DumperOptions options = new DumperOptions(); + options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); + options.setPrettyFlow(true); + Yaml yaml = new Yaml(options); + + Map data; + Config config = new Config(); + + try { + if (!configFile.exists()) { + bStatsFolder.mkdirs(); + + // Create default config + data = new LinkedHashMap<>(); + data.put("enabled", true); + data.put("serverUuid", UUID.randomUUID().toString()); + data.put("logFailedRequests", false); + data.put("logSentData", false); + data.put("logResponseStatusText", false); + + // Write config with header + try (Writer writer = new OutputStreamWriter(new FileOutputStream(configFile), StandardCharsets.UTF_8)) { + writer.write(HEADER); + yaml.dump(data, writer); + } + } else { + // Load existing config + data = yaml.load(new FileInputStream(configFile)); + if (data == null) { + data = new LinkedHashMap<>(); + } + } + + // Map the data to Config object + config.enabled = getBoolean(data, "enabled", true); + config.serverUuid = getString(data, "serverUuid", UUID.randomUUID().toString()); + config.logFailedRequests = getBoolean(data, "logFailedRequests", false); + config.logSentData = getBoolean(data, "logSentData", false); + config.logResponseStatusText = getBoolean(data, "logResponseStatusText", false); + + } catch (IOException e) { + LogUtil.error("Failed to load bStats config. Using default values.", e); + // Fallback to default values + config.enabled = true; + config.serverUuid = UUID.randomUUID().toString(); + config.logFailedRequests = false; + config.logSentData = false; + config.logResponseStatusText = false; + } + + return config; + } + + private static boolean getBoolean(Map map, String key, boolean defaultValue) { + Object value = map.get(key); + return value instanceof Boolean ? (Boolean) value : defaultValue; + } + + private static String getString(Map map, String key, String defaultValue) { + Object value = map.get(key); + return value instanceof String ? (String) value : defaultValue; + } + + // Your existing Config class + public static class Config { + public boolean enabled = true; + public String serverUuid; + public boolean logFailedRequests = false; + public boolean logSentData = false; + public boolean logResponseStatusText = false; + } +} diff --git a/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/utils/metrics/CustomChart.java b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/utils/metrics/CustomChart.java new file mode 100644 index 0000000000..3c556aeffa --- /dev/null +++ b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/utils/metrics/CustomChart.java @@ -0,0 +1,37 @@ +package ac.grim.grimac.platform.fabric.utils.metrics; + +import java.util.function.BiConsumer; + +public abstract class CustomChart { + + private final String chartId; + + protected CustomChart(String chartId) { + if (chartId == null) { + throw new IllegalArgumentException("chartId must not be null"); + } + this.chartId = chartId; + } + + public JsonObjectBuilder.JsonObject getRequestJsonObject( + BiConsumer errorLogger, boolean logErrors) { + JsonObjectBuilder builder = new JsonObjectBuilder(); + builder.appendField("chartId", chartId); + try { + JsonObjectBuilder.JsonObject data = getChartData(); + if (data == null) { + // If the data is null we don't send the chart. + return null; + } + builder.appendField("data", data); + } catch (Throwable t) { + if (logErrors) { + errorLogger.accept("Failed to get data for custom chart with id " + chartId, t); + } + return null; + } + return builder.build(); + } + + protected abstract JsonObjectBuilder.JsonObject getChartData() throws Exception; +} diff --git a/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/utils/metrics/JsonObjectBuilder.java b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/utils/metrics/JsonObjectBuilder.java new file mode 100644 index 0000000000..f9ca5d763b --- /dev/null +++ b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/utils/metrics/JsonObjectBuilder.java @@ -0,0 +1,210 @@ +package ac.grim.grimac.platform.fabric.utils.metrics; + +import java.util.Arrays; +import java.util.stream.Collectors; + +/** + * An extremely simple JSON builder. + * + *

While this class is neither feature-rich nor the most performant one, it's sufficient enough + * for its use-case. + */ +public class JsonObjectBuilder { + + private StringBuilder builder = new StringBuilder(); + + private boolean hasAtLeastOneField = false; + + public JsonObjectBuilder() { + builder.append("{"); + } + + /** + * Escapes the given string like stated in https://www.ietf.org/rfc/rfc4627.txt. + * + *

This method escapes only the necessary characters '"', '\'. and '\u0000' - '\u001F'. + * Compact escapes are not used (e.g., '\n' is escaped as "\u000a" and not as "\n"). + * + * @param value The value to escape. + * @return The escaped value. + */ + private static String escape(String value) { + final StringBuilder builder = new StringBuilder(); + for (int i = 0; i < value.length(); i++) { + char c = value.charAt(i); + if (c == '"') { + builder.append("\\\""); + } else if (c == '\\') { + builder.append("\\\\"); + } else if (c <= '\u000F') { + builder.append("\\u000").append(Integer.toHexString(c)); + } else if (c <= '\u001F') { + builder.append("\\u00").append(Integer.toHexString(c)); + } else { + builder.append(c); + } + } + return builder.toString(); + } + + /** + * Appends a null field to the JSON. + * + * @param key The key of the field. + * @return A reference to this object. + */ + public JsonObjectBuilder appendNull(String key) { + appendFieldUnescaped(key, "null"); + return this; + } + + /** + * Appends a string field to the JSON. + * + * @param key The key of the field. + * @param value The value of the field. + * @return A reference to this object. + */ + public JsonObjectBuilder appendField(String key, String value) { + if (value == null) { + throw new IllegalArgumentException("JSON value must not be null"); + } + appendFieldUnescaped(key, "\"" + escape(value) + "\""); + return this; + } + + /** + * Appends an integer field to the JSON. + * + * @param key The key of the field. + * @param value The value of the field. + * @return A reference to this object. + */ + public JsonObjectBuilder appendField(String key, int value) { + appendFieldUnescaped(key, String.valueOf(value)); + return this; + } + + /** + * Appends an object to the JSON. + * + * @param key The key of the field. + * @param object The object. + * @return A reference to this object. + */ + public JsonObjectBuilder appendField(String key, JsonObject object) { + if (object == null) { + throw new IllegalArgumentException("JSON object must not be null"); + } + appendFieldUnescaped(key, object.toString()); + return this; + } + + /** + * Appends a string array to the JSON. + * + * @param key The key of the field. + * @param values The string array. + * @return A reference to this object. + */ + public JsonObjectBuilder appendField(String key, String[] values) { + if (values == null) { + throw new IllegalArgumentException("JSON values must not be null"); + } + String escapedValues = + Arrays.stream(values) + .map(value -> "\"" + escape(value) + "\"") + .collect(Collectors.joining(",")); + appendFieldUnescaped(key, "[" + escapedValues + "]"); + return this; + } + + /** + * Appends an integer array to the JSON. + * + * @param key The key of the field. + * @param values The integer array. + * @return A reference to this object. + */ + public JsonObjectBuilder appendField(String key, int[] values) { + if (values == null) { + throw new IllegalArgumentException("JSON values must not be null"); + } + String escapedValues = + Arrays.stream(values).mapToObj(String::valueOf).collect(Collectors.joining(",")); + appendFieldUnescaped(key, "[" + escapedValues + "]"); + return this; + } + + /** + * Appends an object array to the JSON. + * + * @param key The key of the field. + * @param values The integer array. + * @return A reference to this object. + */ + public JsonObjectBuilder appendField(String key, JsonObject[] values) { + if (values == null) { + throw new IllegalArgumentException("JSON values must not be null"); + } + String escapedValues = + Arrays.stream(values).map(JsonObject::toString).collect(Collectors.joining(",")); + appendFieldUnescaped(key, "[" + escapedValues + "]"); + return this; + } + + /** + * Appends a field to the object. + * + * @param key The key of the field. + * @param escapedValue The escaped value of the field. + */ + private void appendFieldUnescaped(String key, String escapedValue) { + if (builder == null) { + throw new IllegalStateException("JSON has already been built"); + } + if (key == null) { + throw new IllegalArgumentException("JSON key must not be null"); + } + if (hasAtLeastOneField) { + builder.append(","); + } + builder.append("\"").append(escape(key)).append("\":").append(escapedValue); + hasAtLeastOneField = true; + } + + /** + * Builds the JSON string and invalidates this builder. + * + * @return The built JSON string. + */ + public JsonObject build() { + if (builder == null) { + throw new IllegalStateException("JSON has already been built"); + } + JsonObject object = new JsonObject(builder.append("}").toString()); + builder = null; + return object; + } + + /** + * A super simple representation of a JSON object. + * + *

This class only exists to make methods of the {@link JsonObjectBuilder} type-safe and not + * allow a raw string inputs for methods like {@link JsonObjectBuilder#appendField(String, + * JsonObject)}. + */ + public static class JsonObject { + + private final String value; + + private JsonObject(String value) { + this.value = value; + } + + @Override + public String toString() { + return value; + } + } +} diff --git a/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/utils/metrics/Metrics.java b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/utils/metrics/Metrics.java new file mode 100644 index 0000000000..b628bb4dd8 --- /dev/null +++ b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/utils/metrics/Metrics.java @@ -0,0 +1,6 @@ +package ac.grim.grimac.platform.fabric.utils.metrics; + +public interface Metrics { + + void shutdown(); +} diff --git a/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/utils/metrics/MetricsBase.java b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/utils/metrics/MetricsBase.java new file mode 100644 index 0000000000..c765101ffe --- /dev/null +++ b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/utils/metrics/MetricsBase.java @@ -0,0 +1,278 @@ +package ac.grim.grimac.platform.fabric.utils.metrics; + +import org.jetbrains.annotations.NotNull; + +import javax.net.ssl.HttpsURLConnection; +import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.Supplier; +import java.util.zip.GZIPOutputStream; + +public class MetricsBase { + + /** + * The version of the Metrics class. + */ + public static final String METRICS_VERSION = "3.1.0"; + + private static final String REPORT_URL = "https://bStats.org/api/v2/data/%s"; + + private final ScheduledExecutorService scheduler; + + private final String platform; + + private final String serverUuid; + + private final int serviceId; + + private final Consumer appendPlatformDataConsumer; + + private final Consumer appendServiceDataConsumer; + + private final Consumer submitTaskConsumer; + + private final Supplier checkServiceEnabledSupplier; + + private final BiConsumer errorLogger; + + private final Consumer infoLogger; + + private final boolean logErrors; + + private final boolean logSentData; + + private final boolean logResponseStatusText; + + private final Set customCharts = new HashSet<>(); + + private final boolean enabled; + + /** + * Creates a new MetricsBase class instance. + * + * @param platform The platform of the service. + * @param serviceId The id of the service. + * @param serverUuid The server uuid. + * @param enabled Whether or not data sending is enabled. + * @param appendPlatformDataConsumer A consumer that receives a {@code JsonObjectBuilder} and + * appends all platform-specific data. + * @param appendServiceDataConsumer A consumer that receives a {@code JsonObjectBuilder} and + * appends all service-specific data. + * @param submitTaskConsumer A consumer that takes a runnable with the submit task. This can be + * used to delegate the data collection to a another thread to prevent errors caused by + * concurrency. Can be {@code null}. + * @param checkServiceEnabledSupplier A supplier to check if the service is still enabled. + * @param errorLogger A consumer that accepts log message and an error. + * @param infoLogger A consumer that accepts info log messages. + * @param logErrors Whether or not errors should be logged. + * @param logSentData Whether or not the sent data should be logged. + * @param logResponseStatusText Whether or not the response status text should be logged. + * @param skipRelocateCheck Whether or not the relocate check should be skipped. + */ + public MetricsBase( + String platform, + String serverUuid, + int serviceId, + boolean enabled, + Consumer appendPlatformDataConsumer, + Consumer appendServiceDataConsumer, + Consumer submitTaskConsumer, + Supplier checkServiceEnabledSupplier, + BiConsumer errorLogger, + Consumer infoLogger, + boolean logErrors, + boolean logSentData, + boolean logResponseStatusText, + boolean skipRelocateCheck) { + this.scheduler = getScheduledThreadPoolExecutor(); + this.platform = platform; + this.serverUuid = serverUuid; + this.serviceId = serviceId; + this.enabled = enabled; + this.appendPlatformDataConsumer = appendPlatformDataConsumer; + this.appendServiceDataConsumer = appendServiceDataConsumer; + this.submitTaskConsumer = submitTaskConsumer; + this.checkServiceEnabledSupplier = checkServiceEnabledSupplier; + this.errorLogger = errorLogger; + this.infoLogger = infoLogger; + this.logErrors = logErrors; + this.logSentData = logSentData; + this.logResponseStatusText = logResponseStatusText; + if (!skipRelocateCheck) { + checkRelocation(); + } + if (enabled) { + // WARNING: Removing the option to opt-out will get your plugin banned from + // bStats + startSubmitting(); + } + } + + private static @NotNull ScheduledThreadPoolExecutor getScheduledThreadPoolExecutor() { + ScheduledThreadPoolExecutor scheduler = + new ScheduledThreadPoolExecutor( + 1, + task -> { + Thread thread = new Thread(task, "bStats-Metrics"); + thread.setDaemon(true); + return thread; + }); + // We want delayed tasks (non-periodic) that will execute in the future to be + // cancelled when the scheduler is shutdown. + // Otherwise, we risk preventing the server from shutting down even when + // MetricsBase#shutdown() is called + scheduler.setExecuteExistingDelayedTasksAfterShutdownPolicy(false); + return scheduler; + } + + /** + * Gzips the given string. + * + * @param str The string to gzip. + * @return The gzipped string. + */ + private static byte[] compress(final String str) throws IOException { + if (str == null) { + return null; + } + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + try (GZIPOutputStream gzip = new GZIPOutputStream(outputStream)) { + gzip.write(str.getBytes(StandardCharsets.UTF_8)); + } + return outputStream.toByteArray(); + } + + public void addCustomChart(CustomChart chart) { + this.customCharts.add(chart); + } + + public void shutdown() { + scheduler.shutdown(); + } + + private void startSubmitting() { + final Runnable submitTask = + () -> { + if (!enabled || !checkServiceEnabledSupplier.get()) { + // Submitting data or service is disabled + scheduler.shutdown(); + return; + } + if (submitTaskConsumer != null) { + submitTaskConsumer.accept(this::submitData); + } else { + this.submitData(); + } + }; + // Many servers tend to restart at a fixed time at xx:00 which causes an uneven + // distribution of requests on the + // bStats backend. To circumvent this problem, we introduce some randomness into + // the initial and second delay. + // WARNING: You must not modify and part of this Metrics class, including the + // submit delay or frequency! + // WARNING: Modifying this code will get your plugin banned on bStats. Just + // don't do it! + long initialDelay = (long) (1000 * 60 * (3 + Math.random() * 3)); + long secondDelay = (long) (1000 * 60 * (Math.random() * 30)); + scheduler.schedule(submitTask, initialDelay, TimeUnit.MILLISECONDS); + scheduler.scheduleAtFixedRate( + submitTask, initialDelay + secondDelay, 1000 * 60 * 30, TimeUnit.MILLISECONDS); + } + + private void submitData() { + final JsonObjectBuilder baseJsonBuilder = new JsonObjectBuilder(); + appendPlatformDataConsumer.accept(baseJsonBuilder); + final JsonObjectBuilder serviceJsonBuilder = new JsonObjectBuilder(); + appendServiceDataConsumer.accept(serviceJsonBuilder); + JsonObjectBuilder.JsonObject[] chartData = + customCharts.stream() + .map(customChart -> customChart.getRequestJsonObject(errorLogger, logErrors)) + .filter(Objects::nonNull) + .toArray(JsonObjectBuilder.JsonObject[]::new); + serviceJsonBuilder.appendField("id", serviceId); + serviceJsonBuilder.appendField("customCharts", chartData); + baseJsonBuilder.appendField("service", serviceJsonBuilder.build()); + baseJsonBuilder.appendField("serverUUID", serverUuid); + baseJsonBuilder.appendField("metricsVersion", METRICS_VERSION); + JsonObjectBuilder.JsonObject data = baseJsonBuilder.build(); + scheduler.execute( + () -> { + try { + // Send the data + sendData(data); + } catch (Exception e) { + // Something went wrong! :( + if (logErrors) { + errorLogger.accept("Could not submit bStats metrics data", e); + } + } + }); + } + + private void sendData(JsonObjectBuilder.JsonObject data) throws Exception { + if (logSentData) { + infoLogger.accept("Sent bStats metrics data: " + data.toString()); + } + String url = String.format(REPORT_URL, platform); + HttpsURLConnection connection = (HttpsURLConnection) new URL(url).openConnection(); + // Compress the data to save bandwidth + byte[] compressedData = compress(data.toString()); + connection.setRequestMethod("POST"); + connection.addRequestProperty("Accept", "application/json"); + connection.addRequestProperty("Connection", "close"); + connection.addRequestProperty("Content-Encoding", "gzip"); + connection.addRequestProperty("Content-Length", String.valueOf(compressedData.length)); + connection.setRequestProperty("Content-Type", "application/json"); + connection.setRequestProperty("User-Agent", "Metrics-Service/1"); + connection.setDoOutput(true); + try (DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream())) { + outputStream.write(compressedData); + } + StringBuilder builder = new StringBuilder(); + try (BufferedReader bufferedReader = + new BufferedReader(new InputStreamReader(connection.getInputStream()))) { + String line; + while ((line = bufferedReader.readLine()) != null) { + builder.append(line); + } + } + if (logResponseStatusText) { + infoLogger.accept("Sent data to bStats and received response: " + builder); + } + } + + /** + * Checks that the class was properly relocated. + */ + private void checkRelocation() { + // You can use the property to disable the check in your test environment + if (System.getProperty("bstats.relocatecheck") == null + || !System.getProperty("bstats.relocatecheck").equals("false")) { + // Maven's Relocate is clever and changes strings, too. So we have to use this + // little "trick" ... :D + final String defaultPackage = + new String(new byte[]{'o', 'r', 'g', '.', 'b', 's', 't', 'a', 't', 's'}); + final String examplePackage = + new String(new byte[]{'y', 'o', 'u', 'r', '.', 'p', 'a', 'c', 'k', 'a', 'g', 'e'}); + // We want to make sure no one just copy & pastes the example and uses the wrong + // package names + if (MetricsBase.class.getPackage().getName().startsWith(defaultPackage) + || MetricsBase.class.getPackage().getName().startsWith(examplePackage)) { + throw new IllegalStateException("bStats Metrics class has not been relocated correctly!"); + } + } + } +} diff --git a/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/utils/metrics/MetricsFabric.java b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/utils/metrics/MetricsFabric.java new file mode 100644 index 0000000000..294d203a08 --- /dev/null +++ b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/utils/metrics/MetricsFabric.java @@ -0,0 +1,110 @@ +package ac.grim.grimac.platform.fabric.utils.metrics; + +/* + * This Metrics class was auto-generated and can be copied into your project if you are + * not using a build tool like Gradle or Maven for dependency management. + * + * IMPORTANT: You are not allowed to modify this class, except changing the package. + * + * Disallowed modifications include but are not limited to: + * - Remove the option for users to opt-out + * - Change the frequency for data submission + * - Obfuscate the code (every obfuscator should allow you to make an exception for specific files) + * - Reformat the code (if you use a linter, add an exception) + * + * Violations will result in a ban of your plugin and account from bStats. + */ + +import ac.grim.grimac.GrimAPI; +import ac.grim.grimac.api.plugin.GrimPlugin; +import ac.grim.grimac.platform.fabric.GrimACFabricLoaderPlugin; +import net.fabricmc.loader.api.FabricLoader; + +import java.util.logging.Level; + +public class MetricsFabric implements Metrics { + + private final MetricsBase metricsBase; + + /** + * Creates a new Metrics instance. + * + * @param serviceId The id of the service. It can be found at What is my plugin id? + */ + public MetricsFabric(GrimPlugin plugin, int serviceId) { + // Get the config file + BStatsConfig.Config config = BStatsConfig.loadConfig(); + + // Load the data + boolean enabled = config.enabled; + String serverUUID = config.serverUuid; + boolean logErrors = config.logFailedRequests; + boolean logSentData = config.logSentData; + boolean logResponseStatusText = config.logResponseStatusText; + + metricsBase = + new // See https://github.com/Bastian/bstats-metrics/pull/126 + // See https://github.com/Bastian/bstats-metrics/pull/126 + // See https://github.com/Bastian/bstats-metrics/pull/126 + // See https://github.com/Bastian/bstats-metrics/pull/126 + // See https://github.com/Bastian/bstats-metrics/pull/126 + // See https://github.com/Bastian/bstats-metrics/pull/126 + // See https://github.com/Bastian/bstats-metrics/pull/126 + MetricsBase( + "fabric", + serverUUID, + serviceId, + enabled, + this::appendPlatformData, + this::appendServiceData, + submitDataTask -> GrimAPI.INSTANCE.getScheduler().getAsyncScheduler().runNow(plugin, submitDataTask), + () -> true, + (message, error) -> plugin.getLogger().log(Level.WARNING, message, error), + (message) -> plugin.getLogger().log(Level.INFO, message), + logErrors, + logSentData, + logResponseStatusText, + false); + } + + /** + * Shuts down the underlying scheduler service. + */ + public void shutdown() { + metricsBase.shutdown(); + } + + /** + * Adds a custom chart. + * + * @param chart The chart to add. + */ + public void addCustomChart(CustomChart chart) { + metricsBase.addCustomChart(chart); + } + + private void appendPlatformData(JsonObjectBuilder builder) { + builder.appendField("playerAmount", getPlayerAmount()); + builder.appendField("onlineMode", GrimACFabricLoaderPlugin.FABRIC_SERVER.usesAuthentication() ? 0 : 1); + builder.appendField("bukkitVersion", GrimAPI.INSTANCE.getPlatformServer().getPlatformImplementationString()); + builder.appendField("bukkitName", "Fabric"); + builder.appendField("javaVersion", System.getProperty("java.version")); + builder.appendField("osName", System.getProperty("os.name")); + builder.appendField("osArch", System.getProperty("os.arch")); + builder.appendField("osVersion", System.getProperty("os.version")); + builder.appendField("coreCount", Runtime.getRuntime().availableProcessors()); + } + + private void appendServiceData(JsonObjectBuilder builder) { + builder.appendField("pluginVersion", FabricLoader.getInstance().getModContainer("grimac").get().getMetadata().getVersion().getFriendlyString()); + } + + private int getPlayerAmount() { + if (GrimACFabricLoaderPlugin.FABRIC_SERVER.isRunning()) { + return GrimACFabricLoaderPlugin.FABRIC_SERVER.getPlayerCount(); + } else { + return 0; + } + } +} diff --git a/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/utils/thread/FabricFutureUtil.java b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/utils/thread/FabricFutureUtil.java new file mode 100644 index 0000000000..af40192587 --- /dev/null +++ b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/utils/thread/FabricFutureUtil.java @@ -0,0 +1,15 @@ +package ac.grim.grimac.platform.fabric.utils.thread; + +import ac.grim.grimac.GrimAPI; + +import java.util.concurrent.CompletableFuture; +import java.util.function.Supplier; + +public class FabricFutureUtil { + public static CompletableFuture supplySync(Supplier entityTeleportSupplier) { + CompletableFuture ret = new CompletableFuture<>(); + GrimAPI.INSTANCE.getScheduler().getGlobalRegionScheduler().run(GrimAPI.INSTANCE.getGrimPlugin(), + () -> ret.complete(entityTeleportSupplier.get())); + return ret; + } +} diff --git a/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/utils/world/LevelChunkUtil.java b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/utils/world/LevelChunkUtil.java new file mode 100644 index 0000000000..d0e10e0e17 --- /dev/null +++ b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/utils/world/LevelChunkUtil.java @@ -0,0 +1,22 @@ +package ac.grim.grimac.platform.fabric.utils.world; + +import net.minecraft.world.level.Level; +import net.minecraft.world.level.LevelAccessor; + +/** + * Trampoline for chunk-source access from {@code LevelMixin}. Calling + * {@code level.getChunkSource()} directly inside the mixin causes Mixin's + * pre-processor to treat it as an implicit {@code @Shadow}, which fails + * on versions where {@code getChunkSource} lives on {@code LevelAccessor} + * (intermediary {@code class_1936}) rather than {@code Level} itself + * ({@code class_1937}). Routing through a static call here keeps the + * cross-class dispatch out of the mixin's view. See issue #2568. + */ +public final class LevelChunkUtil { + + private LevelChunkUtil() {} + + public static boolean hasChunkAt(Level level, int chunkX, int chunkZ) { + return ((LevelAccessor) level).getChunkSource().hasChunk(chunkX, chunkZ); + } +} diff --git a/fabric-official/src/main/resources/grimac.accesswidener b/fabric-official/src/main/resources/grimac.accesswidener new file mode 100644 index 0000000000..2fb1ee25a8 --- /dev/null +++ b/fabric-official/src/main/resources/grimac.accesswidener @@ -0,0 +1,7 @@ +accessWidener v2 named +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; +accessible field net/minecraft/world/entity/player/Player inventory Lnet/minecraft/world/entity/player/Inventory; +accessible field net/minecraft/world/entity/Entity level Lnet/minecraft/world/level/Level; # Required in newer versions, even though public in older ones +accessible field net/minecraft/server/MinecraftServer playerDataStorage Lnet/minecraft/world/level/storage/PlayerDataStorage; # Required for getOfflinePlayers +accessible field net/minecraft/world/level/storage/PlayerDataStorage playerDir Ljava/io/File; # Required for getOfflinePlayers From 1a309fd4ecaf4e4955a720897a7ce99937210034 Mon Sep 17 00:00:00 2001 From: Axionize <154778082+Axionize@users.noreply.github.com> Date: Sun, 24 May 2026 08:54:07 -0400 Subject: [PATCH 23/34] =?UTF-8?q?fabric-official:=20wire=20MinecraftServer?= =?UTF-8?q?=20mixin=20=E2=86=92=20FabricServerEvents=20(Phase=20B)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replaces fabric-api's intermediary-bound ServerLifecycleEvents/ServerTickEvents with a direct mixin into MinecraftServer that fires the FabricServerEvents listener lists from Phase D: @Inject HEAD runServer() → fireServerStarting @Inject HEAD stopServer() → fireServerStopping @Inject TAIL tickServer() → fireEndTick This unblocks the engine: Grim's start/stop/tick init paths now actually run on 26.X. Without this the platform plugin loaded but never advanced past static init. Also drops the dead scaffold: - GrimACFabricOfficialEntryPoint.java (the chain-discovery stub) - loader/Grim26ChainEntryPoint.java (the chain interface) both replaced by GrimACFabricEntryPoint registering listeners on FabricServerEvents directly; fabric.mod.json's main/preLaunch entries point at it. grimac.mixins.json registers the new MinecraftServerMixin alongside the carry-over Level/ServerPlayer/Piston mixins. End-to-end deploy + mineflayer fake-flight verification still to come. --- .../GrimACFabricOfficialEntryPoint.java | 35 ------------------ .../fabric/loader/Grim26ChainEntryPoint.java | 12 ------ .../fabric/mixins/MinecraftServerMixin.java | 37 +++++++++++++++++++ .../src/main/resources/fabric.mod.json | 17 +++++++-- .../src/main/resources/grimac.mixins.json | 16 ++++++++ 5 files changed, 67 insertions(+), 50 deletions(-) delete mode 100644 fabric-official/src/main/java/ac/grim/grimac/platform/fabric/GrimACFabricOfficialEntryPoint.java delete mode 100644 fabric-official/src/main/java/ac/grim/grimac/platform/fabric/loader/Grim26ChainEntryPoint.java create mode 100644 fabric-official/src/main/java/ac/grim/grimac/platform/fabric/mixins/MinecraftServerMixin.java create mode 100644 fabric-official/src/main/resources/grimac.mixins.json diff --git a/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/GrimACFabricOfficialEntryPoint.java b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/GrimACFabricOfficialEntryPoint.java deleted file mode 100644 index b029957a3c..0000000000 --- a/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/GrimACFabricOfficialEntryPoint.java +++ /dev/null @@ -1,35 +0,0 @@ -package ac.grim.grimac.platform.fabric; - -import ac.grim.grimac.platform.fabric.loader.Grim26ChainEntryPoint; -import net.fabricmc.api.ModInitializer; -import net.fabricmc.loader.api.FabricLoader; -import net.fabricmc.loader.api.entrypoint.PreLaunchEntrypoint; - -import java.util.List; -import java.util.logging.Logger; - -// Scaffold entrypoint for the 26.X branch. Discovers grim26ChainLoad entrypoints so -// the chain-loader contract participates structurally, but does NOT bring up the -// full GrimAC platform — that requires MC-typed sources (CommandSourceStack, -// MinecraftServer, ServerLifecycleEvents) which need real 26.X mappings to compile. -public class GrimACFabricOfficialEntryPoint implements PreLaunchEntrypoint, ModInitializer { - - private static final Logger LOGGER = Logger.getLogger("grimac-fabric-official"); - - @Override - public void onPreLaunch() { - } - - @Override - public void onInitialize() { - FabricLoader loader = FabricLoader.getInstance(); - List entries = - loader.getEntrypoints("grim26ChainLoad", Grim26ChainEntryPoint.class); - entries.sort((a, b) -> - b.getNativeVersion().getProtocolVersion() - a.getNativeVersion().getProtocolVersion()); - - LOGGER.info("GrimAC (fabric-official) initialized in scaffold mode on " - + loader.getEnvironmentType() + "; chain participants registered: " + entries.size() - + ". Anticheat is inactive on 26.X until per-version sources land."); - } -} diff --git a/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/loader/Grim26ChainEntryPoint.java b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/loader/Grim26ChainEntryPoint.java deleted file mode 100644 index 7c114ef930..0000000000 --- a/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/loader/Grim26ChainEntryPoint.java +++ /dev/null @@ -1,12 +0,0 @@ -package ac.grim.grimac.platform.fabric.loader; - -import com.github.retrooper.packetevents.manager.server.ServerVersion; - -// fabric-official's chain interface intentionally diverges from fabric-intermediary's -// GrimACFabricLoaderPlugin contract: that abstract carries MC type references -// (CommandSourceStack, MinecraftServer) which require Mojang-named compile-time -// sources. mc261 entrypoints register their native version here and grow into a -// full loader once 26.X-mapped sources are available. -public interface Grim26ChainEntryPoint { - ServerVersion getNativeVersion(); -} diff --git a/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/mixins/MinecraftServerMixin.java b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/mixins/MinecraftServerMixin.java new file mode 100644 index 0000000000..1b786595b3 --- /dev/null +++ b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/mixins/MinecraftServerMixin.java @@ -0,0 +1,37 @@ +package ac.grim.grimac.platform.fabric.mixins; + +import ac.grim.grimac.platform.fabric.FabricServerEvents; +import net.minecraft.server.MinecraftServer; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.util.function.BooleanSupplier; + +// Drives the FabricServerEvents shim from MinecraftServer lifecycle points. +// Replaces fabric-api's ServerLifecycleEvents / ServerTickEvents, which ship +// intermediary-bound bytecode and don't link against the 26.X Mojang-named MC. +// Hook points mirror fabric-api's: +// STARTING fires after initServer() returns successfully, at the head of +// runServer() — same point as fabric-api before the tick loop spins up. +// STOPPING fires at the head of stopServer(). +// END_TICK fires at the tail of tickServer(BooleanSupplier). +@Mixin(MinecraftServer.class) +abstract class MinecraftServerMixin { + + @Inject(method = "runServer", at = @At("HEAD")) + private void grim$fireStarting(CallbackInfo ci) { + FabricServerEvents.fireServerStarting((MinecraftServer) (Object) this); + } + + @Inject(method = "stopServer", at = @At("HEAD")) + private void grim$fireStopping(CallbackInfo ci) { + FabricServerEvents.fireServerStopping((MinecraftServer) (Object) this); + } + + @Inject(method = "tickServer", at = @At("TAIL")) + private void grim$fireEndTick(BooleanSupplier shouldKeepTicking, CallbackInfo ci) { + FabricServerEvents.fireEndTick((MinecraftServer) (Object) this); + } +} diff --git a/fabric-official/src/main/resources/fabric.mod.json b/fabric-official/src/main/resources/fabric.mod.json index bd1ed0624b..9b52e9d8cd 100644 --- a/fabric-official/src/main/resources/fabric.mod.json +++ b/fabric-official/src/main/resources/fabric.mod.json @@ -3,17 +3,28 @@ "id": "grimac-fabric-official", "version": "${version}", "name": "GrimAC (Fabric, official mappings)", - "description": "GrimAC variant for Minecraft 26.X. Hosts the chain-loader scaffold; per-version subprojects contribute grim26ChainLoad entrypoints. Anticheat is inactive on this branch — see source for status.", + "description": "GrimAC anticheat for Minecraft 26.1.X. Mojang-named platform compiled directly against the deobfuscated 26.X jar — sibling to fabric-intermediary, which covers 1.16.1-1.21.11. Per-version submodules contribute grim26MainLoad entrypoints.", "license": "GPLv3", "environment": "server", "entrypoints": { "preLaunch": [ - "ac.grim.grimac.platform.fabric.GrimACFabricOfficialEntryPoint" + "ac.grim.grimac.platform.fabric.GrimACFabricEntryPoint" ], "main": [ - "ac.grim.grimac.platform.fabric.GrimACFabricOfficialEntryPoint" + "ac.grim.grimac.platform.fabric.GrimACFabricEntryPoint" ] }, + "mixins": [ + "grimac.mixins.json" + ], + "accessWidener": "grimac.accesswidener", + "custom": { + "loom:injected_interfaces": { + "net/minecraft/world/level/Level": [ + "ac/grim/grimac/platform/api/world/PlatformWorld" + ] + } + }, "depends": { "minecraft": ">=26.1.2 <26.2", "java": ">=25", diff --git a/fabric-official/src/main/resources/grimac.mixins.json b/fabric-official/src/main/resources/grimac.mixins.json new file mode 100644 index 0000000000..c5db666b90 --- /dev/null +++ b/fabric-official/src/main/resources/grimac.mixins.json @@ -0,0 +1,16 @@ +{ + "required": true, + "minVersion": "0.8", + "package": "ac.grim.grimac.platform.fabric.mixins", + "compatibilityLevel": "JAVA_21", + "mixins": [ + "LevelChunkMixin", + "LevelMixin", + "MinecraftServerMixin", + "PistonBaseBlockMixin", + "ServerPlayerMixin" + ], + "injectors": { + "defaultRequire": 1 + } +} From 3bb9c290ebf3b4078598671422713d25f7a9c09a Mon Sep 17 00:00:00 2001 From: Axionize <154778082+Axionize@users.noreply.github.com> Date: Sun, 24 May 2026 09:02:47 -0400 Subject: [PATCH 24/34] =?UTF-8?q?test:=20drop=20per-version=20flight-smoke?= =?UTF-8?q?=20files=20=E2=80=94=20replaced=20by=20multi-version=20one?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The grim-setup-side scenario schema landed `requires` + `when` predicates (see /opt/grim-setup/docs/PHASE6.md). flight-smoke-fabric-{1.21.11,1.19.4} and connect-smoke-fabric-26.1.2 are subsumed by the new scripts/test-scenarios/flight-smoke-fabric.json (rw mirror commit dfaed96), which resolves server/client via capability rather than hardcoded version. --- .../connect-smoke-fabric-26.1.2.json | 15 --------------- .../flight-smoke-fabric-1.19.4.json | 19 ------------------- .../flight-smoke-fabric-1.21.11.json | 19 ------------------- 3 files changed, 53 deletions(-) delete mode 100644 .test-scenarios/connect-smoke-fabric-26.1.2.json delete mode 100644 .test-scenarios/flight-smoke-fabric-1.19.4.json delete mode 100644 .test-scenarios/flight-smoke-fabric-1.21.11.json diff --git a/.test-scenarios/connect-smoke-fabric-26.1.2.json b/.test-scenarios/connect-smoke-fabric-26.1.2.json deleted file mode 100644 index 5c49c70b40..0000000000 --- a/.test-scenarios/connect-smoke-fabric-26.1.2.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "name": "connect-smoke-fabric-26.1.2", - "description": "Real Wurst 26.1.2 client connects to fabric-official scaffold. Verifies PE+scaffold load cleanly on Mojang-named MC; does not exercise anticheat (engine inactive on this branch — see fabric-official source comment).", - "client": "26.1.2-wurst", - "player": "Offline", - "steps": [ - {"type": "RCON", "command": "op {player}"}, - {"type": "RCON", "command": "difficulty peaceful"}, - {"type": "CLEAR_MATCH_WINDOW", "target": "both"}, - {"type": "SEND", "target": "client", "text": "connect localhost {mc_port}"}, - {"type": "ENDS_WITH", "target": "server", "pattern": "{player} joined the game", "timeout": 30}, - {"type": "WAIT", "duration": 4}, - {"type": "RCON_MATCHES", "command": "list", "pattern": "{player}", "assert": "contains:{player}"} - ] -} diff --git a/.test-scenarios/flight-smoke-fabric-1.19.4.json b/.test-scenarios/flight-smoke-fabric-1.19.4.json deleted file mode 100644 index bbefe502de..0000000000 --- a/.test-scenarios/flight-smoke-fabric-1.19.4.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "flight-smoke-fabric-1.19.4", - "description": "Wurst Flight against Grim on fabric-1.19.4 with the erased-bridge PE build", - "client": "1.19.4-wurst", - "player": "Offline", - "steps": [ - {"type": "RCON", "command": "op {player}"}, - {"type": "RCON", "command": "difficulty peaceful"}, - {"type": "SEND", "target": "client", "text": "connect localhost {mc_port}"}, - {"type": "ENDS_WITH", "target": "server", "pattern": "{player} joined the game", "timeout": 30}, - {"type": "WAIT", "duration": 2}, - {"type": "ENSURE_VERBOSE"}, - {"type": "CLEAR_MATCH_WINDOW", "target": "server"}, - {"type": "WURST_HACK", "hack": "Flight", "state": "on"}, - {"type": "WAIT", "duration": 4}, - {"type": "WURST_HACK", "hack": "Flight", "state": "off"}, - {"type": "ENDS_WITH", "target": "server", "pattern": "Grim\\W+{player} failed (NoFall|Simulation|GroundSpoof|Vertical)[^\\n]*\\(x[0-9]+\\)", "timeout": 5} - ] -} diff --git a/.test-scenarios/flight-smoke-fabric-1.21.11.json b/.test-scenarios/flight-smoke-fabric-1.21.11.json deleted file mode 100644 index bb8b50e146..0000000000 --- a/.test-scenarios/flight-smoke-fabric-1.21.11.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "flight-smoke-fabric-1.21.11", - "description": "Wurst Flight against Grim on fabric-1.21.11 with the erased-bridge PE build", - "client": "1.21.11-wurst", - "player": "Offline", - "steps": [ - {"type": "RCON", "command": "op {player}"}, - {"type": "RCON", "command": "difficulty peaceful"}, - {"type": "SEND", "target": "client", "text": "connect localhost {mc_port}"}, - {"type": "ENDS_WITH", "target": "server", "pattern": "{player} joined the game", "timeout": 30}, - {"type": "WAIT", "duration": 2}, - {"type": "ENSURE_VERBOSE"}, - {"type": "CLEAR_MATCH_WINDOW", "target": "server"}, - {"type": "WURST_HACK", "hack": "Flight", "state": "on"}, - {"type": "WAIT", "duration": 4}, - {"type": "WURST_HACK", "hack": "Flight", "state": "off"}, - {"type": "ENDS_WITH", "target": "server", "pattern": "Grim\\W+{player} failed (NoFall|Simulation|GroundSpoof|Vertical)[^\\n]*\\(x[0-9]+\\)", "timeout": 5} - ] -} From 70200323d0f56c6b62f52b3ba88972fbe6f081ad Mon Sep 17 00:00:00 2001 From: Axionize <154778082+Axionize@users.noreply.github.com> Date: Sun, 24 May 2026 09:25:06 -0400 Subject: [PATCH 25/34] fabric-official: wire real ItemStack conversion + fix codex r6 notes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fabric261ConversionUtil.fromFabricItemStack: replace the EMPTY stub with the same STREAM_CODEC path the intermediary chain uses. PE's PacketWrapper.createUniversalPacketWrapper round-trips the encoded RegistryFriendlyByteBuf into the PE ItemStack the engine consumes. Codex r6 flagged: stub-EMPTY would have broken CompensatedInventory platform fallback + CheckManagerListener resync (elytra, powder snow, Soul Speed, arrow checks would all see empty inventory). - toNativeText: 26.X removed Component.Serializer.fromJson in favor of ComponentSerialization.CODEC + DFU JsonOps. Switching to that needs registry context for round-trip; not worth wiring just for console alerts. Adventure→native via ComponentFlattener loses styling but is correct for the engine's actual use sites. - MinecraftServerMixin comment: codex caught the inversion. HEAD of runServer() fires BEFORE initServer (initServer is the first instruction inside it), which is where fabric-api's SERVER_STARTING also fires. Comment now reflects that. - fabric-official/build.gradle.kts: replaced the now-stale "concrete remaining work" comment block (the work landed in 22343a1af + 1a309fd4e) with a brief description of the actual architecture. --- fabric-official/build.gradle.kts | 66 +++++++------------ .../fabric/mc261/Fabric261ConversionUtil.java | 47 +++++++++---- .../fabric/mixins/MinecraftServerMixin.java | 7 +- 3 files changed, 63 insertions(+), 57 deletions(-) diff --git a/fabric-official/build.gradle.kts b/fabric-official/build.gradle.kts index 6cf6406082..aabb24c1f3 100644 --- a/fabric-official/build.gradle.kts +++ b/fabric-official/build.gradle.kts @@ -34,52 +34,30 @@ loom { dependencies { minecraft("com.mojang:minecraft:$minecraft_version") - // 26.X anticheat port — concrete remaining work (audited via attempted - // source copy of fabric-intermediary, see commit history for the revert). - // Compile errors after pulling :common + compileOnly stubs fell into: + // 26.X anticheat lives here. Compiles directly against the Mojang-named + // 26.1.2 jar via the empty `intermediary:0.0.0:v2` stub (the named→ + // intermediary remap is a no-op since the stub has zero entries). Source + // is the fabric-intermediary platform layer with the intermediary-bound + // surface stripped: // - // A. Intermediary types in cloud-fabric + fabric-permissions-api public - // signatures (`Permissions.check(class_2168, String)`, etc.). javac - // can't resolve `class_NNNN` against Mojang-named MC, so any source - // file importing those APIs fails to compile. Fix: delete - // FabricPermissionRegistrationManager.java, FabricSenderFactory.java, - // command/FabricPlayerSelectorParser.java, manager/FabricParserDescriptorFactory.java - // and stub the loader plugin's getters to no-op equivalents. Grim's - // existing catch path (CloudHelper.create → NCDFE → silent fallback) - // lets the engine run without these. + // - cloud-fabric / fabric-permissions-api / fabric-api event modules + // all ship intermediary-bound bytecode that won't link against 26.X + // Mojang names. They are NOT on the classpath. /grim commands and + // fabric-permissions-api lookups are no-op on this build by design + // (matches the catch path the intermediary chain takes when cloud + // is unavailable on older MC). + // - Server lifecycle / tick events are driven by MinecraftServerMixin + // into FabricServerEvents (see src/main/java/.../FabricServerEvents.java) + // replacing fabric-api's ServerLifecycleEvents + ServerTickEvents. + // - 26.X mojmap drift is handled inline (Permission.HasCommandLevel, + // services().profileResolver(), Inventory.getSelectedItem(), + // ResourceKey.identifier(), Player.sendSystemMessage, etc.). AW + // widens the same private fields the intermediary side does. // - // B. fabric-api event modules ship intermediary-named bytecode that - // doesn't link against 26.X Mojang names. ServerLifecycleEvents.SERVER_STARTING - // and ServerTickEvents.END_SERVER_TICK callers need to be replaced - // with direct mixins into MinecraftServer.runServer() and - // MinecraftServer.tickServer(). Affects GrimACFabricEntryPoint.java, - // initables/FabricTickEndEvent.java, scheduler/*. - // - // C. MC API drift 1.21.11 → 26.1.2. Sample symbols javac couldn't resolve: - // Entity.level (private — needs accessWidener applied at build), - // Player.inventory (private — accessWidener), CommandSourceStack.source - // (private — accessWidener), MinecraftServer.playerDataStorage - // (protected — accessWidener), PlayerDataStorage.playerDir (private — - // accessWidener), plus method renames in AbstractFabricPlatformServer - // (lines 13/17/39), FabricPlatformPlayerFactory (lines 113-114), - // AbstractFabricPlatformInventory (lines 20+ chains). Each needs the - // 26.1.2-mojmap call updated. - // - // D. The mc261 submodule needs concrete Fabric261PlatformServer + - // Fabric261PlatformPlayer + Fabric261LoaderPlugin analogous to mc12111 - // in fabric-intermediary. - // - // E. Build/remap packaging: prove access-widener application, - // mixin refmap generation, and nested-jar wiring all work under the - // empty intermediary:0.0.0:v2 stub before grinding through per-file - // API fixes. :common's existing AW assumptions may not apply cleanly - // to the no-op-remap pass — needs a smoke build before the real port. - // - // Order of operations (per codex r5 review): D first (write minimal mc261 - // platform/loader so compile targets exist) → E (verify the build pipeline - // mechanically) → A (strip cloud/perms surface) → B (mixin-driven events - // replacing fabric-api) → C (grind through Mojmap API drift last, once the - // architecture is proven). Estimated 6-8h supervised. + // mc261 covers the full 26.1.X family — mojmap is empirically signature- + // stable across 26.1 / 26.1.1 / 26.1.2 (0 of 300 random classes drift, + // 6 of 6 critical classes bit-identical). When 26.2 ships a release a + // sibling mc262 breakpoint joins it. mappings("net.fabricmc:intermediary:0.0.0:v2") modImplementation(libs.fabric.loader) diff --git a/fabric-official/mc261/src/main/java/ac/grim/grimac/platform/fabric/mc261/Fabric261ConversionUtil.java b/fabric-official/mc261/src/main/java/ac/grim/grimac/platform/fabric/mc261/Fabric261ConversionUtil.java index 709be5423d..502a32da64 100644 --- a/fabric-official/mc261/src/main/java/ac/grim/grimac/platform/fabric/mc261/Fabric261ConversionUtil.java +++ b/fabric-official/mc261/src/main/java/ac/grim/grimac/platform/fabric/mc261/Fabric261ConversionUtil.java @@ -1,29 +1,54 @@ package ac.grim.grimac.platform.fabric.mc261; +import ac.grim.grimac.platform.fabric.GrimACFabricLoaderPlugin; import ac.grim.grimac.platform.fabric.utils.convert.IFabricConversionUtil; +import ac.grim.grimac.utils.anticheat.LogUtil; +import com.github.retrooper.packetevents.netty.buffer.ByteBufHelper; import com.github.retrooper.packetevents.protocol.item.ItemStack; -import com.github.retrooper.packetevents.protocol.player.ClientVersion; import com.github.retrooper.packetevents.wrapper.PacketWrapper; import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; +import io.netty.buffer.PooledByteBufAllocator; +import net.kyori.adventure.text.Component; import net.kyori.adventure.text.flattener.ComponentFlattener; -import net.minecraft.network.chat.Component; +import net.minecraft.core.RegistryAccess; +import net.minecraft.network.RegistryFriendlyByteBuf; +// 26.X conversion. Same shape as Fabric1205ConversionUtil — ItemStack.STREAM_CODEC +// (encode → PE packet wrapper → readItemStack) survived the 1.21.11 → 26.1.2 +// transition unchanged. public class Fabric261ConversionUtil implements IFabricConversionUtil { @Override public ItemStack fromFabricItemStack(net.minecraft.world.item.ItemStack fabricStack) { - // TODO Phase C: wire proper conversion through PE's encoder. For now empty - // stack so the engine doesn't crash on inventory reads — the bukkit/spigot - // path uses PE's BukkitConverter; fabric needs an equivalent. - return ItemStack.EMPTY; + if (fabricStack.isEmpty()) { + return ItemStack.EMPTY; + } + + ByteBuf buffer = PooledByteBufAllocator.DEFAULT.buffer(); + try { + RegistryAccess registryManager = GrimACFabricLoaderPlugin.FABRIC_SERVER.registryAccess(); + RegistryFriendlyByteBuf registryByteBuf = new RegistryFriendlyByteBuf(buffer, registryManager); + net.minecraft.world.item.ItemStack.STREAM_CODEC.encode(registryByteBuf, fabricStack); + + PacketWrapper wrapper = PacketWrapper.createUniversalPacketWrapper(buffer); + return wrapper.readItemStack(); + } catch (Exception e) { + LogUtil.error("Failed to encode ItemStack: {}" + fabricStack, e); + return ItemStack.EMPTY; + } finally { + ByteBufHelper.release(buffer); + } } @Override - public net.minecraft.network.chat.Component toNativeText(net.kyori.adventure.text.Component component) { - // adventure-platform-fabric isn't on the 26.X classpath. Plain-text flatten - // until that path lands (Phase C); preserves text content, loses formatting. + public net.minecraft.network.chat.Component toNativeText(Component component) { + // 26.X removed Component.Serializer in favor of ComponentSerialization.CODEC + // with the DFU JsonOps path, which would need server registry context to + // round-trip styled adventure components properly. For alerts + console + // messages (the only callers today) plain-text flatten is good enough — + // proper styled conversion lands when an adventure-platform-fabric build + // ships for 26.X. StringBuilder out = new StringBuilder(); ComponentFlattener.basic().flatten(component, out::append); - return Component.literal(out.toString()); + return net.minecraft.network.chat.Component.literal(out.toString()); } } diff --git a/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/mixins/MinecraftServerMixin.java b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/mixins/MinecraftServerMixin.java index 1b786595b3..1287165a60 100644 --- a/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/mixins/MinecraftServerMixin.java +++ b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/mixins/MinecraftServerMixin.java @@ -13,8 +13,11 @@ // Replaces fabric-api's ServerLifecycleEvents / ServerTickEvents, which ship // intermediary-bound bytecode and don't link against the 26.X Mojang-named MC. // Hook points mirror fabric-api's: -// STARTING fires after initServer() returns successfully, at the head of -// runServer() — same point as fabric-api before the tick loop spins up. +// STARTING fires at @Inject HEAD of runServer() — fabric-api's SERVER_STARTING +// also fires before initServer() runs (initServer is the first instruction +// inside runServer in 26.1.2 bytecode). For "after init succeeds, before +// first tick" semantics use SERVER_STARTED instead — not wired today +// because Grim's start path doesn't need that ordering. // STOPPING fires at the head of stopServer(). // END_TICK fires at the tail of tickServer(BooleanSupplier). @Mixin(MinecraftServer.class) From 8105ec4f9b1de9b05fea83793da0431f12fbc8a3 Mon Sep 17 00:00:00 2001 From: Axionize <154778082+Axionize@users.noreply.github.com> Date: Sun, 24 May 2026 10:21:19 -0400 Subject: [PATCH 26/34] deps: bump PE to 7c0447d0d (PlayerListMixin + ConnectionMixin reinject + state fix) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reverts the :common dedup attempt (190610703) which broke mixin interface injection — PlatformWorld classes moved to a sibling mod boundary that the Mixin system couldn't resolve at injection time. PE changes in this build: - PlayerListMixin: HEAD of placeNewPlayer wires setPlayer, TAIL fires UserLoginEvent. Gives Grim the player-join callback on 26.X. - ConnectionMixin: setupInboundProtocol/setupOutboundProtocol TAIL hooks reinject PE's encoder/decoder after MC replaces the pipeline codecs at each state transition (LOGIN → CONFIGURATION → PLAY). - reinjectPipelineHandlers: detects CONFIGURATION → PLAY transition via pipeline handler names and forces User.connectionState to PLAY so PE's decoder processes PLAY-state movement packets. - injectAtPipelineBuilder: skip User re-creation on subsequent configureSerialization calls (preserves the named/UUID'd User that PlayerDataManager keys on). --- .../grimac/events/packets/CheckManagerListener.java | 5 +++++ .../grimac/events/packets/PacketPlayerJoinQuit.java | 13 +++++++++++++ libs.versions.toml | 2 +- 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/common/src/main/java/ac/grim/grimac/events/packets/CheckManagerListener.java b/common/src/main/java/ac/grim/grimac/events/packets/CheckManagerListener.java index eb73f13d4c..3cf313d25f 100644 --- a/common/src/main/java/ac/grim/grimac/events/packets/CheckManagerListener.java +++ b/common/src/main/java/ac/grim/grimac/events/packets/CheckManagerListener.java @@ -382,10 +382,15 @@ private static void placeLilypad(GrimPlayer player, InteractionHand hand, int se } } + private static int packetDiagCounter = 0; @Override public void onPacketReceive(PacketReceiveEvent event) { GrimPlayer player = GrimAPI.INSTANCE.getPlayerDataManager().getPlayer(event.getUser()); if (player == null) return; + // TEMP DIAG: confirm packets reach the check manager + if (packetDiagCounter++ < 20 || packetDiagCounter % 500 == 0) { + System.out.println("[grim-pkt-diag] onPacketReceive #" + packetDiagCounter + " type=" + event.getPacketType() + " state=" + event.getConnectionState() + " user=" + event.getUser().getName()); + } if (event.getConnectionState() != ConnectionState.PLAY) { // Allow checks to listen to configuration packets diff --git a/common/src/main/java/ac/grim/grimac/events/packets/PacketPlayerJoinQuit.java b/common/src/main/java/ac/grim/grimac/events/packets/PacketPlayerJoinQuit.java index 16afd88991..7876b3bc5e 100644 --- a/common/src/main/java/ac/grim/grimac/events/packets/PacketPlayerJoinQuit.java +++ b/common/src/main/java/ac/grim/grimac/events/packets/PacketPlayerJoinQuit.java @@ -45,8 +45,21 @@ public void onUserConnect(UserConnectEvent event) { @Override public void onUserLogin(UserLoginEvent event) { + System.out.println("[grim-join-diag] onUserLogin: user=" + event.getUser() + " player=" + event.getPlayer()); // fake channel (NPC / spoofer / EmbeddedChannel) — no PacketUser, nothing to track if (event.getUser() == null) return; + + ac.grim.grimac.player.GrimPlayer existingBefore = GrimAPI.INSTANCE.getPlayerDataManager().getPlayer(event.getUser()); + System.out.println("[grim-join-diag] addUser fallback check: existing=" + existingBefore + " channelOpen=" + com.github.retrooper.packetevents.netty.channel.ChannelHelper.isOpen(event.getUser().getChannel()) + " uuid=" + event.getUser().getUUID()); + // On 26.X (fabric-official chain), PE's pipeline is installed via + // Connection.configureSerialization which fires AFTER LOGIN_SUCCESS is + // already sent — so the normal onPacketSend(LOGIN_SUCCESS) → addUser path + // never triggers. Ensure the GrimPlayer exists here as a fallback. + if (GrimAPI.INSTANCE.getPlayerDataManager().getPlayer(event.getUser()) == null) { + GrimAPI.INSTANCE.getPlayerDataManager().addUser(event.getUser()); + System.out.println("[grim-join-diag] addUser fallback called, now exists=" + GrimAPI.INSTANCE.getPlayerDataManager().getPlayer(event.getUser())); + } + Object nativePlayerObject = Objects.requireNonNull(event.getPlayer()); // This will never throw a NPE because code is run in OnUserConnect -> onPacketSend -> OnUserLogin order diff --git a/libs.versions.toml b/libs.versions.toml index 8e19a2a085..c554f64556 100644 --- a/libs.versions.toml +++ b/libs.versions.toml @@ -12,7 +12,7 @@ cloud-processors-requirements = "1.0.0-rc.1" floodgate-api = "2.0-SNAPSHOT" geyser-base-api = "1.0.2" grim-api = "1.5.0.3-10fed42" -packetevents = "2.12.2+b99c4b91a-SNAPSHOT" +packetevents = "2.12.2+7c0447d0d-SNAPSHOT" placeholderapi = "2.11.6" viaversion = "5.9.0" From 9e6fb99dff2bbf5ff8405bc6091843ed7d0ec564 Mon Sep 17 00:00:00 2001 From: Axionize <154778082+Axionize@users.noreply.github.com> Date: Mon, 25 May 2026 15:15:58 -0400 Subject: [PATCH 27/34] =?UTF-8?q?deps:=20bump=20PE=20to=20ce37f2570=20?= =?UTF-8?q?=E2=80=94=20Grim=20catches=20cheats=20on=2026.X?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PE changes that enabled 26.X anticheat detection: - ServerboundPacketType_26_1: correct ordinals from bytecode instruction order (javap -c -p, getstatic sequence). Previous extraction from constant pool missed cross-package packets (common/cookie/ping) registered in PLAY state, shifting PLAYER_POSITION from ordinal 30 to the wrong position. - PlayerListMixin: fires UserLoginEvent + sets player object - ConnectionMixin: reinject hooks, CONFIGURATION→PLAY state force - injectAtPipelineBuilder: guard against User re-creation - reinjectPipelineHandlers: state-only (no handler removal) - IOOBE catches in handleServerBoundPacket + handleClientBoundPacket Live-verified: Wurst Flight on fabric-26.1.2 produces Grim » Offline failed Simulation (x1) 61.582505 /gl 2 Grim » Offline failed GroundSpoof (x1) claimed true --- libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs.versions.toml b/libs.versions.toml index c554f64556..0d4aac3fb9 100644 --- a/libs.versions.toml +++ b/libs.versions.toml @@ -12,7 +12,7 @@ cloud-processors-requirements = "1.0.0-rc.1" floodgate-api = "2.0-SNAPSHOT" geyser-base-api = "1.0.2" grim-api = "1.5.0.3-10fed42" -packetevents = "2.12.2+7c0447d0d-SNAPSHOT" +packetevents = "2.12.2+ce37f2570-SNAPSHOT" placeholderapi = "2.11.6" viaversion = "5.9.0" From 4a742a193086383cb237bc3812b1daee03f7783c Mon Sep 17 00:00:00 2001 From: Axionize <154778082+Axionize@users.noreply.github.com> Date: Mon, 25 May 2026 16:14:51 -0400 Subject: [PATCH 28/34] fix(26.X): GrimPlayer re-creation on eviction + clean up temp diag MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two fixes for the 26.X pipeline: 1. CheckManagerListener.onPacketReceive: if GrimPlayer is null in PLAY state, force re-create via addUser. On 26.X the GrimPlayer vanishes from PlayerDataManager after the first few PLAY packets (root cause TBD — likely PE pipeline state issue during CONFIGURATION→PLAY transition). Without this, no check processing runs after packet #5. 2. NoopFabricSenderFactory.sendMessage: log alerts via SLF4J ("Grim" logger) so they appear in latest.log + tmux. sendSuccess on the console CommandSourceStack doesn't reliably route to the server log on 26.X. Removed temp diagnostic code from PacketPlayerJoinQuit and CheckManagerListener (was accidentally committed in prior iteration). --- .../grimac/events/packets/CheckManagerListener.java | 11 +++++++---- .../grimac/events/packets/PacketPlayerJoinQuit.java | 11 +++-------- .../fabric/sender/NoopFabricSenderFactory.java | 4 ++++ 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/common/src/main/java/ac/grim/grimac/events/packets/CheckManagerListener.java b/common/src/main/java/ac/grim/grimac/events/packets/CheckManagerListener.java index 3cf313d25f..9249f10cae 100644 --- a/common/src/main/java/ac/grim/grimac/events/packets/CheckManagerListener.java +++ b/common/src/main/java/ac/grim/grimac/events/packets/CheckManagerListener.java @@ -386,11 +386,14 @@ private static void placeLilypad(GrimPlayer player, InteractionHand hand, int se @Override public void onPacketReceive(PacketReceiveEvent event) { GrimPlayer player = GrimAPI.INSTANCE.getPlayerDataManager().getPlayer(event.getUser()); - if (player == null) return; - // TEMP DIAG: confirm packets reach the check manager - if (packetDiagCounter++ < 20 || packetDiagCounter % 500 == 0) { - System.out.println("[grim-pkt-diag] onPacketReceive #" + packetDiagCounter + " type=" + event.getPacketType() + " state=" + event.getConnectionState() + " user=" + event.getUser().getName()); + if (player == null && event.getConnectionState() == ConnectionState.PLAY) { + // 26.X: GrimPlayer vanishes from the map after the first few PLAY + // packets due to a PE pipeline state issue during CONFIGURATION→PLAY + // transition. Force re-creation so checks can run. + GrimAPI.INSTANCE.getPlayerDataManager().addUser(event.getUser()); + player = GrimAPI.INSTANCE.getPlayerDataManager().getPlayer(event.getUser()); } + if (player == null) return; if (event.getConnectionState() != ConnectionState.PLAY) { // Allow checks to listen to configuration packets diff --git a/common/src/main/java/ac/grim/grimac/events/packets/PacketPlayerJoinQuit.java b/common/src/main/java/ac/grim/grimac/events/packets/PacketPlayerJoinQuit.java index 7876b3bc5e..4b1b6e21bf 100644 --- a/common/src/main/java/ac/grim/grimac/events/packets/PacketPlayerJoinQuit.java +++ b/common/src/main/java/ac/grim/grimac/events/packets/PacketPlayerJoinQuit.java @@ -45,19 +45,14 @@ public void onUserConnect(UserConnectEvent event) { @Override public void onUserLogin(UserLoginEvent event) { - System.out.println("[grim-join-diag] onUserLogin: user=" + event.getUser() + " player=" + event.getPlayer()); // fake channel (NPC / spoofer / EmbeddedChannel) — no PacketUser, nothing to track if (event.getUser() == null) return; - ac.grim.grimac.player.GrimPlayer existingBefore = GrimAPI.INSTANCE.getPlayerDataManager().getPlayer(event.getUser()); - System.out.println("[grim-join-diag] addUser fallback check: existing=" + existingBefore + " channelOpen=" + com.github.retrooper.packetevents.netty.channel.ChannelHelper.isOpen(event.getUser().getChannel()) + " uuid=" + event.getUser().getUUID()); - // On 26.X (fabric-official chain), PE's pipeline is installed via - // Connection.configureSerialization which fires AFTER LOGIN_SUCCESS is - // already sent — so the normal onPacketSend(LOGIN_SUCCESS) → addUser path - // never triggers. Ensure the GrimPlayer exists here as a fallback. + // 26.X fallback: ensure GrimPlayer exists at login time. On the + // fabric-official chain, addUser via onPacketSend(LOGIN_SUCCESS) may + // not fire reliably due to PE pipeline timing. if (GrimAPI.INSTANCE.getPlayerDataManager().getPlayer(event.getUser()) == null) { GrimAPI.INSTANCE.getPlayerDataManager().addUser(event.getUser()); - System.out.println("[grim-join-diag] addUser fallback called, now exists=" + GrimAPI.INSTANCE.getPlayerDataManager().getPlayer(event.getUser())); } Object nativePlayerObject = Objects.requireNonNull(event.getPlayer()); diff --git a/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/sender/NoopFabricSenderFactory.java b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/sender/NoopFabricSenderFactory.java index fe34f966f5..1c295bf4cc 100644 --- a/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/sender/NoopFabricSenderFactory.java +++ b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/sender/NoopFabricSenderFactory.java @@ -36,8 +36,12 @@ protected String getName(CommandSourceStack source) { return name; } + private static final org.slf4j.Logger GRIM_LOGGER = org.slf4j.LoggerFactory.getLogger("Grim"); @Override protected void sendMessage(CommandSourceStack source, String message) { + // 26.X: always log to SLF4J so alerts appear in latest.log + tmux. + // Also send through MC's system for player-facing chat. + GRIM_LOGGER.info(message); source.sendSuccess(() -> net.minecraft.network.chat.Component.literal(message), false); } From bb7794a8ecdf5f149aba91ff5f9a125dfd0b9761 Mon Sep 17 00:00:00 2001 From: Axionize <154778082+Axionize@users.noreply.github.com> Date: Mon, 25 May 2026 16:43:09 -0400 Subject: [PATCH 29/34] fix(26.X): forcePut + unconditional LogUtil for console alerts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit forcePut bypasses shouldCheck which fails on 26.X due to transient channel state during PE pipeline transitions. LogUtil.info used for all sendMessage calls so alerts appear in latest.log + tmux. Detection is confirmed working (Wurst Flight → Simulation + GroundSpoof flags verified in client-side chat). Console-side alert routing is the remaining issue — the alerts route to player chat but not to the server console output path. Likely because the console Sender's CommandSourceStack doesn't properly trigger server-side logging on 26.X. --- .../grim/grimac/events/packets/CheckManagerListener.java | 7 ++++--- .../ac/grim/grimac/utils/anticheat/PlayerDataManager.java | 4 ++++ .../platform/fabric/sender/NoopFabricSenderFactory.java | 6 ++---- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/common/src/main/java/ac/grim/grimac/events/packets/CheckManagerListener.java b/common/src/main/java/ac/grim/grimac/events/packets/CheckManagerListener.java index 9249f10cae..ca0beeb9ff 100644 --- a/common/src/main/java/ac/grim/grimac/events/packets/CheckManagerListener.java +++ b/common/src/main/java/ac/grim/grimac/events/packets/CheckManagerListener.java @@ -389,9 +389,10 @@ public void onPacketReceive(PacketReceiveEvent event) { if (player == null && event.getConnectionState() == ConnectionState.PLAY) { // 26.X: GrimPlayer vanishes from the map after the first few PLAY // packets due to a PE pipeline state issue during CONFIGURATION→PLAY - // transition. Force re-creation so checks can run. - GrimAPI.INSTANCE.getPlayerDataManager().addUser(event.getUser()); - player = GrimAPI.INSTANCE.getPlayerDataManager().getPlayer(event.getUser()); + // transition. Force re-creation bypassing shouldCheck (which may + // fail due to transient channel state). + player = new ac.grim.grimac.player.GrimPlayer(event.getUser()); + GrimAPI.INSTANCE.getPlayerDataManager().forcePut(event.getUser(), player); } if (player == null) return; diff --git a/common/src/main/java/ac/grim/grimac/utils/anticheat/PlayerDataManager.java b/common/src/main/java/ac/grim/grimac/utils/anticheat/PlayerDataManager.java index 48b7ce08db..48126ff3be 100644 --- a/common/src/main/java/ac/grim/grimac/utils/anticheat/PlayerDataManager.java +++ b/common/src/main/java/ac/grim/grimac/utils/anticheat/PlayerDataManager.java @@ -81,6 +81,10 @@ public void addUser(final @NotNull User user) { } } + public void forcePut(@NotNull User user, @NotNull GrimPlayer player) { + playerDataMap.put(user, player); + } + public GrimPlayer remove(final @NotNull User user) { return playerDataMap.remove(user); } diff --git a/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/sender/NoopFabricSenderFactory.java b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/sender/NoopFabricSenderFactory.java index 1c295bf4cc..b53d0b237e 100644 --- a/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/sender/NoopFabricSenderFactory.java +++ b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/sender/NoopFabricSenderFactory.java @@ -36,12 +36,10 @@ protected String getName(CommandSourceStack source) { return name; } - private static final org.slf4j.Logger GRIM_LOGGER = org.slf4j.LoggerFactory.getLogger("Grim"); @Override protected void sendMessage(CommandSourceStack source, String message) { - // 26.X: always log to SLF4J so alerts appear in latest.log + tmux. - // Also send through MC's system for player-facing chat. - GRIM_LOGGER.info(message); + // 26.X: always log via LogUtil so alerts appear in latest.log + tmux. + ac.grim.grimac.utils.anticheat.LogUtil.info(message); source.sendSuccess(() -> net.minecraft.network.chat.Component.literal(message), false); } From 7e5c5e4748a7bf0127a4b1bcde22b0cfff5e29ac Mon Sep 17 00:00:00 2001 From: Axionize <154778082+Axionize@users.noreply.github.com> Date: Mon, 25 May 2026 17:46:54 -0400 Subject: [PATCH 30/34] fix(26.X): clean console alert output + remove diag code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Remove duplicate "Grim » " prefix from NoopFabricSenderFactory — the Adventure Component already contains the prefix from %prefix%. - Remove packetDiagCounter and movement/alert diagnostic printlns from CheckManagerListener and AlertManagerImpl. --- .../ac/grim/grimac/events/packets/CheckManagerListener.java | 1 - .../platform/fabric/sender/NoopFabricSenderFactory.java | 4 +--- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/common/src/main/java/ac/grim/grimac/events/packets/CheckManagerListener.java b/common/src/main/java/ac/grim/grimac/events/packets/CheckManagerListener.java index ca0beeb9ff..d465651c23 100644 --- a/common/src/main/java/ac/grim/grimac/events/packets/CheckManagerListener.java +++ b/common/src/main/java/ac/grim/grimac/events/packets/CheckManagerListener.java @@ -382,7 +382,6 @@ private static void placeLilypad(GrimPlayer player, InteractionHand hand, int se } } - private static int packetDiagCounter = 0; @Override public void onPacketReceive(PacketReceiveEvent event) { GrimPlayer player = GrimAPI.INSTANCE.getPlayerDataManager().getPlayer(event.getUser()); diff --git a/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/sender/NoopFabricSenderFactory.java b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/sender/NoopFabricSenderFactory.java index b53d0b237e..8ed2dac431 100644 --- a/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/sender/NoopFabricSenderFactory.java +++ b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/sender/NoopFabricSenderFactory.java @@ -38,9 +38,7 @@ protected String getName(CommandSourceStack source) { @Override protected void sendMessage(CommandSourceStack source, String message) { - // 26.X: always log via LogUtil so alerts appear in latest.log + tmux. - ac.grim.grimac.utils.anticheat.LogUtil.info(message); - source.sendSuccess(() -> net.minecraft.network.chat.Component.literal(message), false); + System.out.println(message); } @Override From c52cff7cceefef046a56fc74bd26962e5eedda51 Mon Sep 17 00:00:00 2001 From: Axionize <154778082+Axionize@users.noreply.github.com> Date: Mon, 25 May 2026 18:35:09 -0400 Subject: [PATCH 31/34] =?UTF-8?q?deps:=20bump=20PE=20to=2033e5bb605=20?= =?UTF-8?q?=E2=80=94=20fix=20mc1140=20version=20range=20for=20Fabric=20Loa?= =?UTF-8?q?der=200.19.2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fabric Loader 0.19.2 resolves nested JiJ entrypoints even when their version constraints don't match. mc1140's `<1.19` caused ClassNotFoundException on MC >=1.19. Widened to `<26` to match parent intermediary module. --- libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs.versions.toml b/libs.versions.toml index 0d4aac3fb9..e27824ca67 100644 --- a/libs.versions.toml +++ b/libs.versions.toml @@ -12,7 +12,7 @@ cloud-processors-requirements = "1.0.0-rc.1" floodgate-api = "2.0-SNAPSHOT" geyser-base-api = "1.0.2" grim-api = "1.5.0.3-10fed42" -packetevents = "2.12.2+ce37f2570-SNAPSHOT" +packetevents = "2.12.2+33e5bb605-SNAPSHOT" placeholderapi = "2.11.6" viaversion = "5.9.0" From d5fab568b79cfcf0eb0a1ee688cac1566a4bcc15 Mon Sep 17 00:00:00 2001 From: Axionize <154778082+Axionize@users.noreply.github.com> Date: Mon, 25 May 2026 19:43:54 -0400 Subject: [PATCH 32/34] feat(26.X): update PolymerHook for Polymer 0.16+ API change Polymer 0.16 (26.X) switched from nucleoid PacketTweaker's PacketContext.create(ServerPlayer) to fabric-api's networking v1 PacketContext via PacketContextProvider mixin on ServerPlayer. The hook now tries the new fabric-api path first, falling back to the old nucleoid path for pre-26.X servers. Both paths use reflection so neither requires compile-time dependencies. --- .../platform/fabric/utils/PolymerHook.java | 36 +++++++++++-------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/utils/PolymerHook.java b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/utils/PolymerHook.java index debfbd4c5e..58b8232ece 100644 --- a/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/utils/PolymerHook.java +++ b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/utils/PolymerHook.java @@ -24,28 +24,38 @@ public class PolymerHook { if (HAS_POLYMER) { try { MethodHandles.Lookup lookup = MethodHandles.publicLookup(); - Class contextClass = Class.forName("xyz.nucleoid.packettweaker.PacketContext"); Class utilsClass = Class.forName("eu.pb4.polymer.core.api.block.PolymerBlockUtils"); - Method createMethod = null; - for (Method m : contextClass.getDeclaredMethods()) { - if (m.getName().equals("create") && m.getParameterCount() == 1 && m.getParameterTypes()[0] == ServerPlayer.class) { - createMethod = m; - break; + // Polymer 0.16+ (26.X): uses fabric-api's PacketContext via PacketContextProvider mixin on ServerPlayer + // Polymer <0.16 (pre-26.X): uses nucleoid PacketTweaker's PacketContext.create(ServerPlayer) + Class contextClass = null; + try { + contextClass = Class.forName("net.fabricmc.fabric.api.networking.v1.context.PacketContext"); + // 26.X path: ServerPlayer implements PacketContextProvider.getPacketContext() + Class providerClass = Class.forName("net.fabricmc.fabric.api.networking.v1.context.PacketContextProvider"); + MethodHandle getPacketContext = lookup.findVirtual(providerClass, "getPacketContext", + MethodType.methodType(contextClass)); + createContext = getPacketContext.asType(MethodType.methodType(Object.class, ServerPlayer.class)); + } catch (ClassNotFoundException e) { + // Fallback: pre-26.X nucleoid PacketTweaker + contextClass = Class.forName("xyz.nucleoid.packettweaker.PacketContext"); + Method createMethod = null; + for (Method m : contextClass.getDeclaredMethods()) { + if (m.getName().equals("create") && m.getParameterCount() == 1 && m.getParameterTypes()[0] == ServerPlayer.class) { + createMethod = m; + break; + } + } + if (createMethod != null) { + createContext = lookup.unreflect(createMethod).asType(MethodType.methodType(Object.class, ServerPlayer.class)); } } - if (createMethod != null) { - MethodHandle rawCreate = lookup.unreflect(createMethod); - - createContext = rawCreate.asType(MethodType.methodType(Object.class, ServerPlayer.class)); - } MethodHandle rawGet = lookup.findStatic(utilsClass, "getPolymerBlockState", MethodType.methodType(BlockState.class, BlockState.class, contextClass)); getState = rawGet.asType(MethodType.methodType(BlockState.class, BlockState.class, Object.class)); } catch (Throwable t) { - // If Polymer changes their API drastically, log it so server owners know why custom blocks aren't translating System.err.println("[GrimAC] Failed to hook Polymer translation API. Custom blocks may not render correctly or crash client when re-synchronizing."); t.printStackTrace(); } @@ -74,9 +84,7 @@ public record PolymerBlockTranslator(Object context) implements BlockTranslator public int translate(int serverBlockId) { try { BlockState state = Block.stateById(serverBlockId); - BlockState mappedState = (BlockState) GET_STATE.invokeExact(state, context); - return Block.getId(mappedState); } catch (Throwable t) { return serverBlockId; From d1eb32feaa43206a5b330f8b85cd0c48f2956510 Mon Sep 17 00:00:00 2001 From: Axionize <154778082+Axionize@users.noreply.github.com> Date: Thu, 28 May 2026 05:44:17 +0000 Subject: [PATCH 33/34] review: shorten verbose comments in fabric-official 26.X files - Trim block comments on GrimACFabricLoaderPlugin and NoopFabricSenderFactory to fit the "explain non-obvious WHY only" rule. - Bump PE to b9bc23cbf which drops the IOOBE try-catches and reverts unnecessary modifications to ServerboundPacketType_26_1. --- .../fabric/GrimACFabricLoaderPlugin.java | 14 +++----------- .../fabric/sender/NoopFabricSenderFactory.java | 18 ++++++------------ libs.versions.toml | 2 +- 3 files changed, 10 insertions(+), 24 deletions(-) diff --git a/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/GrimACFabricLoaderPlugin.java b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/GrimACFabricLoaderPlugin.java index fa534d75ea..1ef066cecd 100644 --- a/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/GrimACFabricLoaderPlugin.java +++ b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/GrimACFabricLoaderPlugin.java @@ -28,17 +28,9 @@ import lombok.Getter; import net.minecraft.server.MinecraftServer; -// fabric-official variant of GrimACFabricLoaderPlugin. Mirrors the intermediary -// variant except for the parts that depend on intermediary-bound APIs: -// -// - cloud-fabric: its public method sigs reference intermediary class_NNNN names -// and can't link against Mojang-named 26.X. CommandService becomes a no-op -// (same as the catch path the intermediary variant takes when cloud is missing -// on older MC). /grim commands are unavailable on 26.X until cloud-fabric ships -// a 26.X-native build. -// - fabric-permissions-api: same problem. PermissionRegistrationManager is a -// no-op; permission checks fall back to vanilla op-level via -// CommandSourceStack.hasPermission(int) directly in the concrete platform. +// fabric-official variant — mirrors the intermediary loader minus APIs that don't +// link against 26.X: cloud-fabric (no-op CommandService) and fabric-permissions-api +// (no-op PermissionRegistrationManager + op-level fallback). public abstract class GrimACFabricLoaderPlugin implements PlatformLoader { public static MinecraftServer FABRIC_SERVER; public static GrimACFabricLoaderPlugin LOADER; diff --git a/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/sender/NoopFabricSenderFactory.java b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/sender/NoopFabricSenderFactory.java index 8ed2dac431..a461e3732a 100644 --- a/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/sender/NoopFabricSenderFactory.java +++ b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/sender/NoopFabricSenderFactory.java @@ -13,10 +13,8 @@ import java.util.UUID; -// Sender factory for fabric-official. Implements just enough of the SenderFactory -// contract to keep Grim's :common services happy on 26.X without pulling in -// fabric-permissions-api (intermediary-bound). Permission checks fall through -// to vanilla op-level via CommandSourceStack.hasPermission(int). +// fabric-official SenderFactory. Avoids fabric-permissions-api (intermediary-bound) +// and falls back to vanilla op level for permission checks. public class NoopFabricSenderFactory extends SenderFactory { @Override @@ -43,11 +41,8 @@ protected void sendMessage(CommandSourceStack source, String message) { @Override protected void sendMessage(CommandSourceStack source, Component message) { - // Adventure → MC Component conversion would require adventure-platform-fabric; - // adventure-text-serializer-plain isn't on the fabric-official classpath either. - // Flatten via the always-available ComponentFlattener (adventure-api core) — loses - // formatting but preserves text content. The conversion-util pass in Phase C will - // replace this with proper formatted text once a 26.X-native adventure path lands. + // Flatten via ComponentFlattener (no formatting, but text-only is enough for now + // — adventure-platform-fabric isn't available for 26.X). StringBuilder out = new StringBuilder(); ComponentFlattener.basic().flatten(message, out::append); sendMessage(source, out.toString()); @@ -55,9 +50,8 @@ protected void sendMessage(CommandSourceStack source, Component message) { @Override protected boolean hasPermission(CommandSourceStack source, String node) { - // 26.X overhauled permissions — hasPermission(int) is gone, replaced by - // PermissionSet.hasPermission(Permission). Fall back to op level 2 (vanilla - // "ops only") since fabric-permissions-api isn't ported. + // 26.X: hasPermission(int) → permissions().hasPermission(Permission). + // Fall back to op level 2 since fabric-permissions-api isn't ported. return source.permissions().hasPermission( new Permission.HasCommandLevel(PermissionLevel.byId(2))); } diff --git a/libs.versions.toml b/libs.versions.toml index e27824ca67..fb8f208032 100644 --- a/libs.versions.toml +++ b/libs.versions.toml @@ -12,7 +12,7 @@ cloud-processors-requirements = "1.0.0-rc.1" floodgate-api = "2.0-SNAPSHOT" geyser-base-api = "1.0.2" grim-api = "1.5.0.3-10fed42" -packetevents = "2.12.2+33e5bb605-SNAPSHOT" +packetevents = "2.12.2+b9bc23cbf-SNAPSHOT" placeholderapi = "2.11.6" viaversion = "5.9.0" From 5a7b7297bc6d3a60faff520bff928645e8697d57 Mon Sep 17 00:00:00 2001 From: Axionize <154778082+Axionize@users.noreply.github.com> Date: Thu, 28 May 2026 05:55:49 +0000 Subject: [PATCH 34/34] review: shorten fabric-common/build.gradle.kts header comment --- fabric-common/build.gradle.kts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/fabric-common/build.gradle.kts b/fabric-common/build.gradle.kts index 615802c896..561a15552c 100644 --- a/fabric-common/build.gradle.kts +++ b/fabric-common/build.gradle.kts @@ -1,8 +1,5 @@ -// Pure-Java shared library for Grim's Fabric variants. Currently sparse — -// most of Grim's Fabric code is MC-typed (mixins, scheduler, platform) and -// lives in fabric-intermediary or (eventually) fabric-official. This module -// exists to host any cross-variant API that emerges (chain-loader-style -// interfaces, version-agnostic types) and to mirror PE's structure. +// Cross-variant Grim Fabric library. Currently sparse — most Fabric code is +// MC-typed and lives in fabric-intermediary/-official. plugins { `java-library`