diff --git a/.github/workflows/build-and-publish.yml b/.github/workflows/build-and-publish.yml index c4881c3013..974bd741f7 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 }} @@ -266,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' #------------------------------------------------------------------ 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/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..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 @@ -385,6 +385,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 && 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 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; if (event.getConnectionState() != ConnectionState.PLAY) { 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..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 @@ -47,6 +47,14 @@ public void onUserConnect(UserConnectEvent event) { public void onUserLogin(UserLoginEvent event) { // fake channel (NPC / spoofer / EmbeddedChannel) — no PacketUser, nothing to track if (event.getUser() == null) return; + + // 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()); + } + Object nativePlayerObject = Objects.requireNonNull(event.getPlayer()); // This will never throw a NPE because code is run in OnUserConnect -> onPacketSend -> OnUserLogin order 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-common/build.gradle.kts b/fabric-common/build.gradle.kts new file mode 100644 index 0000000000..561a15552c --- /dev/null +++ b/fabric-common/build.gradle.kts @@ -0,0 +1,25 @@ +// Cross-variant Grim Fabric library. Currently sparse — most Fabric code is +// MC-typed and lives in fabric-intermediary/-official. + +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..9a56a0cf40 --- /dev/null +++ b/fabric-intermediary/build.gradle.kts @@ -0,0 +1,153 @@ +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) + // 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") +} + +// 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 = 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 + } + } +} + +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()) + } +} + +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 93% rename from fabric/mc1161/src/main/resources/fabric.mod.json rename to fabric-intermediary/mc1161/src/main/resources/fabric.mod.json index d13c081e23..08383c1027 100644 --- a/fabric/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/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 93% rename from fabric/mc1171/src/main/resources/fabric.mod.json rename to fabric-intermediary/mc1171/src/main/resources/fabric.mod.json index 753189fb25..9411c3ebae 100644 --- a/fabric/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/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 93% rename from fabric/mc1194/src/main/resources/fabric.mod.json rename to fabric-intermediary/mc1194/src/main/resources/fabric.mod.json index 713a2b7f1e..3fce5900ad 100644 --- a/fabric/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/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 93% rename from fabric/mc1205/src/main/resources/fabric.mod.json rename to fabric-intermediary/mc1205/src/main/resources/fabric.mod.json index 9ab853515a..1b1d0dca1c 100644 --- a/fabric/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/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 93% rename from fabric/mc12111/src/main/resources/fabric.mod.json rename to fabric-intermediary/mc12111/src/main/resources/fabric.mod.json index b03e4f81c6..efa6232cce 100644 --- a/fabric/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/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..f867c1c517 --- /dev/null +++ b/fabric-intermediary/src/main/resources/fabric.mod.json @@ -0,0 +1,42 @@ +{ + "schemaVersion": 1, + "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.", + "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" + ], + "accessWidener": "grimac.accesswidener", + "custom": { + "loom:injected_interfaces": { + "net/minecraft/world/level/Level": [ + "ac/grim/grimac/platform/api/world/PlatformWorld" + ] + } + }, + "depends": { + "minecraft": "\u003e\u003d1.16.1 \u003c26", + "java": "\u003e\u003d17", + "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..aabb24c1f3 --- /dev/null +++ b/fabric-official/build.gradle.kts @@ -0,0 +1,172 @@ +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) + grim.`base-conventions` + grim.`jij-conventions` +} + +java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(25)) + } +} + +loom { + accessWidenerPath = file("src/main/resources/grimac.accesswidener") +} + +dependencies { + minecraft("com.mojang:minecraft:$minecraft_version") + // 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: + // + // - 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. + // + // 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) + + 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") +} + +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 = if (project == project(":fabric-official")) { + "${rootProject.name}-fabric-official" + } else { + "${rootProject.name}-fabric-${project.name}" + } + archiveVersion = rootProject.version as String + } + } +} + +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()) + } +} + +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/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..502a32da64 --- /dev/null +++ b/fabric-official/mc261/src/main/java/ac/grim/grimac/platform/fabric/mc261/Fabric261ConversionUtil.java @@ -0,0 +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.wrapper.PacketWrapper; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.PooledByteBufAllocator; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.flattener.ComponentFlattener; +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) { + 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(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 net.minecraft.network.chat.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/resources/fabric.mod.json b/fabric-official/mc261/src/main/resources/fabric.mod.json new file mode 100644 index 0000000000..d1b1c029ff --- /dev/null +++ b/fabric-official/mc261/src/main/resources/fabric.mod.json @@ -0,0 +1,20 @@ +{ + "schemaVersion": 1, + "id": "grimac-fabric-mc261", + "version": "${version}", + "name": "GrimAC mc261", + "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": { + "grim26MainLoad": [ + "ac.grim.grimac.platform.fabric.mc261.Fabric261LoaderPlugin" + ] + }, + "depends": { + "minecraft": ">=26.1.2 <26.2", + "java": ">=25", + "fabricloader": ">=0.19", + "packetevents": "*" + } +} 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..1ef066cecd --- /dev/null +++ b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/GrimACFabricLoaderPlugin.java @@ -0,0 +1,130 @@ +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 — 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; + + 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/MinecraftServerMixin.java b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/mixins/MinecraftServerMixin.java new file mode 100644 index 0000000000..1287165a60 --- /dev/null +++ b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/mixins/MinecraftServerMixin.java @@ -0,0 +1,40 @@ +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 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) +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/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..a461e3732a --- /dev/null +++ b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/sender/NoopFabricSenderFactory.java @@ -0,0 +1,81 @@ +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; + +// 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 + 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) { + System.out.println(message); + } + + @Override + protected void sendMessage(CommandSourceStack source, Component message) { + // 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()); + } + + @Override + protected boolean hasPermission(CommandSourceStack source, String node) { + // 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))); + } + + @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..58b8232ece --- /dev/null +++ b/fabric-official/src/main/java/ac/grim/grimac/platform/fabric/utils/PolymerHook.java @@ -0,0 +1,94 @@ +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 utilsClass = Class.forName("eu.pb4.polymer.core.api.block.PolymerBlockUtils"); + + // 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)); + } + } + + 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) { + 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/fabric.mod.json b/fabric-official/src/main/resources/fabric.mod.json new file mode 100644 index 0000000000..9b52e9d8cd --- /dev/null +++ b/fabric-official/src/main/resources/fabric.mod.json @@ -0,0 +1,34 @@ +{ + "schemaVersion": 1, + "id": "grimac-fabric-official", + "version": "${version}", + "name": "GrimAC (Fabric, official mappings)", + "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.GrimACFabricEntryPoint" + ], + "main": [ + "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", + "fabricloader": ">=0.19", + "packetevents": "*" + } +} 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 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 + } +} diff --git a/fabric/build.gradle.kts b/fabric/build.gradle.kts index 4e585a0b02..8d575692cb 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,62 @@ plugins { grim.`jij-conventions` } +repositories { + if (BuildConfig.mavenLocalOverride) mavenLocal() + exclusive("https://maven.fabricmc.net/") { + 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() +} + 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 - } - } + // 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")) + + // 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) } -subprojects { - dependencies { - // configuration = "namedElements" required when depending on another loom project - implementation(project(":fabric", configuration = "namedElements")) - } -} - -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 + + // 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" + ) + ) } } 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..e35a2ed07e 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 <26", ">=26.1.2 <26.2"], + "java": ">=17", + "fabricloader": ">=0.16" } } diff --git a/libs.versions.toml b/libs.versions.toml index 86a055d297..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+c44d85c-SNAPSHOT" +packetevents = "2.12.2+b9bc23cbf-SNAPSHOT" placeholderapi = "2.11.6" viaversion = "5.9.0" 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")