From c64dd8d5b8122838b06094800008462f645caf10 Mon Sep 17 00:00:00 2001 From: collin-b1 Date: Fri, 10 Apr 2026 14:19:02 -0400 Subject: [PATCH 1/3] Fabric 26.1.x and Mojang Mappings --- build.gradle | 29 +++---- gradle.properties | 12 +-- gradle/wrapper/gradle-wrapper.properties | 2 +- settings.gradle | 1 + .../modernbetapatches/ModernBetaPatches.java | 31 ++++---- .../manager/CapeManager.java | 79 ++++++++++++------- .../mixin/AvatarRendererMixin.java | 21 +++++ .../mixin/CapeFeatureRendererMixin.java | 23 ------ .../mixin/PlayerInfoMixin.java | 49 ++++++++++++ .../mixin/PlayerListEntryMixin.java | 47 ----------- src/main/resources/fabric.mod.json | 5 +- .../resources/modernbetapatches.mixins.json | 6 +- 12 files changed, 159 insertions(+), 146 deletions(-) create mode 100644 src/main/java/me/collinb/modernbetapatches/mixin/AvatarRendererMixin.java delete mode 100644 src/main/java/me/collinb/modernbetapatches/mixin/CapeFeatureRendererMixin.java create mode 100644 src/main/java/me/collinb/modernbetapatches/mixin/PlayerInfoMixin.java delete mode 100644 src/main/java/me/collinb/modernbetapatches/mixin/PlayerListEntryMixin.java diff --git a/build.gradle b/build.gradle index b2a78eb..5d9dff7 100644 --- a/build.gradle +++ b/build.gradle @@ -1,9 +1,9 @@ plugins { - id 'fabric-loom' version '1.10-SNAPSHOT' + id "net.fabricmc.fabric-loom" version "${loom_version}" id 'maven-publish' } -version = "${project.mod_version}-${project.minecraft_version}" +version = "${project.mod_version}+${project.minecraft_version}" group = project.maven_group base { @@ -22,10 +22,9 @@ repositories { dependencies { // To change the versions see the gradle.properties file minecraft "com.mojang:minecraft:${project.minecraft_version}" - mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2" - modImplementation "net.fabricmc:fabric-loader:${project.loader_version}" + implementation "net.fabricmc:fabric-loader:${project.loader_version}" - modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}" + implementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}" } processResources { @@ -41,31 +40,25 @@ processResources { } } -def targetJavaVersion = 21 tasks.withType(JavaCompile).configureEach { - // ensure that the encoding is set to UTF-8, no matter what the system default is - // this fixes some edge cases with special characters not displaying correctly - // see http://yodaconditions.net/blog/fix-for-java-file-encoding-problems-with-gradle.html - // If Javadoc is generated, this must be specified in that task too. - it.options.encoding = "UTF-8" - if (targetJavaVersion >= 10 || JavaVersion.current().isJava10Compatible()) { - it.options.release.set(targetJavaVersion) - } + it.options.release = 25 } java { - toolchain { - languageVersion = JavaLanguageVersion.of(21) - } // Loom will automatically attach sourcesJar to a RemapSourcesJar task and to the "build" task // if it is present. // If you remove this line, sources will not be generated. withSourcesJar() + + sourceCompatibility = JavaVersion.VERSION_25 + targetCompatibility = JavaVersion.VERSION_25 } jar { + inputs.property "projectName", project.name + from("LICENSE") { - rename { "${it}_${project.archivesBaseName}" } + rename { "${it}_${project.name}"} } } diff --git a/gradle.properties b/gradle.properties index 38a751e..15412ac 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,15 +1,15 @@ # Done to increase the memory available to gradle. org.gradle.jvmargs=-Xmx1G +org.gradle.parallel=true # Fabric Properties # check these on https://modmuss50.me/fabric.html -minecraft_version=1.21.8 -yarn_mappings=1.21.8+build.1 -loader_version=0.17.2 -loom_version=1.11-SNAPSHOT +minecraft_version=26.1 +loader_version=0.18.6 +loom_version=1.15-SNAPSHOT # Mod Properties -mod_version=1.1 +mod_version=1.2 maven_group=me.collinb archives_base_name=modernbetapatches # Dependencies # check this on https://modmuss50.me/fabric.html -fabric_version=0.133.4+1.21.8 \ No newline at end of file +fabric_version=0.145.1+26.1 \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index cea7a79..19a6bde 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.0-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/settings.gradle b/settings.gradle index f91a4fe..b02216b 100644 --- a/settings.gradle +++ b/settings.gradle @@ -4,6 +4,7 @@ pluginManagement { name = 'Fabric' url = 'https://maven.fabricmc.net/' } + mavenCentral() gradlePluginPortal() } } diff --git a/src/main/java/me/collinb/modernbetapatches/ModernBetaPatches.java b/src/main/java/me/collinb/modernbetapatches/ModernBetaPatches.java index 8ee088b..ce06a85 100644 --- a/src/main/java/me/collinb/modernbetapatches/ModernBetaPatches.java +++ b/src/main/java/me/collinb/modernbetapatches/ModernBetaPatches.java @@ -4,42 +4,41 @@ import net.fabricmc.api.ClientModInitializer; import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents; -import net.minecraft.entity.Entity; -import net.minecraft.entity.EquipmentSlot; -import net.minecraft.entity.decoration.ArmorStandEntity; -import net.minecraft.item.ItemStack; -import net.minecraft.item.Items; +import net.minecraft.world.entity.decoration.ArmorStand; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class ModernBetaPatches implements ClientModInitializer { + public static final Logger LOGGER = LoggerFactory.getLogger("modernbetapatches"); + public static String currentServer = null; @Override public void onInitializeClient() { - ClientPlayConnectionEvents.JOIN.register((clientPlayNetworkHandler, packetSender, minecraftClient) -> { - if (minecraftClient.getCurrentServerEntry() != null) { - currentServer = minecraftClient.getCurrentServerEntry().address; + ClientPlayConnectionEvents.JOIN.register((clientPlayNetworkHandler, _, minecraftClient) -> { + if (minecraftClient.getCurrentServer() != null) { + currentServer = minecraftClient.getCurrentServer().ip; if (isModernBeta()) { - CapeManager.fetchCape(clientPlayNetworkHandler.getProfile().getId()); + CapeManager.fetchCape(clientPlayNetworkHandler.getLocalGameProfile().id()); } } else { currentServer = null; } }); - ClientPlayConnectionEvents.DISCONNECT.register(((clientPlayNetworkHandler, minecraftClient) -> currentServer = null)); + ClientPlayConnectionEvents.DISCONNECT.register(((_, _) -> currentServer = null)); ClientTickEvents.END_CLIENT_TICK.register((client) -> { if (!isModernBeta()) return; - if (client.world == null) return; + if (client.level == null) return; - for (Entity entity : client.world.getEntities()) { - if (entity instanceof ArmorStandEntity armorStandEntity) { + for (net.minecraft.world.entity.Entity entity : client.level.entitiesForRendering()) { + if (entity instanceof ArmorStand armorStandEntity) { // Check if paper is on armor stand head - if (!armorStandEntity.hasStackEquipped(EquipmentSlot.HEAD)) continue; - ItemStack headItem = armorStandEntity.getEquippedStack(EquipmentSlot.HEAD); + var headItem = armorStandEntity.getItemBySlot(net.minecraft.world.entity.EquipmentSlot.HEAD); - if (headItem.isOf(Items.PAPER)) { + if (headItem.is(net.minecraft.world.item.Items.PAPER)) { armorStandEntity.discard(); } } diff --git a/src/main/java/me/collinb/modernbetapatches/manager/CapeManager.java b/src/main/java/me/collinb/modernbetapatches/manager/CapeManager.java index 375361c..565c574 100644 --- a/src/main/java/me/collinb/modernbetapatches/manager/CapeManager.java +++ b/src/main/java/me/collinb/modernbetapatches/manager/CapeManager.java @@ -1,57 +1,76 @@ package me.collinb.modernbetapatches.manager; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.texture.NativeImage; -import net.minecraft.client.texture.NativeImageBackedTexture; -import net.minecraft.util.Identifier; +import com.mojang.blaze3d.platform.NativeImage; +import me.collinb.modernbetapatches.ModernBetaPatches; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.texture.DynamicTexture; +import net.minecraft.core.ClientAsset; +import net.minecraft.resources.Identifier; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; -import java.io.IOException; import java.io.InputStream; import java.net.URI; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; +import java.util.*; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; public class CapeManager { - private static final Map capes = new HashMap<>(); + private static final Set capes = Collections.newSetFromMap(new ConcurrentHashMap<>()); - public static void fetchCape(UUID uuid) { + public static void fetchCape(@NonNull UUID uuid) { + String path = uuid.toString().toLowerCase(Locale.ROOT); + String formattedPath = "textures/" + path + ".png"; String url = "https://cape.modernbeta.org/cape/" + uuid; + Identifier id = Identifier.fromNamespaceAndPath("modernbetacapes", formattedPath); + CompletableFuture.runAsync(() -> { try (InputStream stream = URI.create(url).toURL().openStream()) { NativeImage image = NativeImage.read(stream); - NativeImage reformatted = new NativeImage(NativeImage.Format.RGBA, 64, 32, true); - reformatted.fillRect(0, 0, 64, 32, 0x00000000); - - for (int y = 0; y < image.getHeight(); ++y) { - for (int x = 0; x < image.getWidth(); ++x) { - int color = image.getColorArgb(x, y); - reformatted.setColorArgb(x, y, color); - } - } + NativeImage reformatted = getReformatted(image); - Identifier id = Identifier.of("modernbetacapes", uuid.toString()); + image.close(); - MinecraftClient.getInstance().execute(() -> { - MinecraftClient.getInstance().getTextureManager() - .registerTexture(id, new NativeImageBackedTexture(id::toString, reformatted)); - capes.put(uuid, id); + Minecraft.getInstance().execute(() -> { + Minecraft.getInstance().getTextureManager().register(id, new DynamicTexture(id::toString, reformatted)); + capes.add(uuid); + ModernBetaPatches.LOGGER.info("Registered cape {}", formattedPath); }); - } catch (IOException ignored) { - // Cape not found, remove identifier + } catch (Exception e) { capes.remove(uuid); } }); } - public static Identifier getCape(UUID uuid) { - return capes.get(uuid); + private static @NonNull NativeImage getReformatted(@NonNull NativeImage image) { + NativeImage reformatted = new NativeImage(NativeImage.Format.RGBA, 64, 32, true); + reformatted.fillRect(0, 0, 64, 32, 0x00000000); + + int[] sourcePixels = image.getPixelsABGR(); + int width = Math.min(image.getWidth(), 64); + int height = Math.min(image.getHeight(), 32); + + for (int y = 0; y < height; ++y) { + for (int x = 0; x < width; ++x) { + int color = sourcePixels[x + y * width]; + reformatted.setPixelABGR(x, y, color); + } + } + return reformatted; + } + + public static ClientAsset.@Nullable Texture getCape(UUID uuid) { + if (hasModernBetaCape(uuid)) { + String path = uuid.toString().toLowerCase(Locale.ROOT); + Identifier capeIdentifier = Identifier.fromNamespaceAndPath("modernbetacapes", path); + return new ClientAsset.ResourceTexture(capeIdentifier); + } + return null; } - public static boolean isModernBetaCape(Identifier id) { - return id != null && "modernbetacapes".equals(id.getNamespace()); + public static boolean hasModernBetaCape(UUID uuid) { + return capes.contains(uuid); } } diff --git a/src/main/java/me/collinb/modernbetapatches/mixin/AvatarRendererMixin.java b/src/main/java/me/collinb/modernbetapatches/mixin/AvatarRendererMixin.java new file mode 100644 index 0000000..d534a03 --- /dev/null +++ b/src/main/java/me/collinb/modernbetapatches/mixin/AvatarRendererMixin.java @@ -0,0 +1,21 @@ +package me.collinb.modernbetapatches.mixin; + +import me.collinb.modernbetapatches.manager.CapeManager; +import net.minecraft.client.renderer.entity.player.AvatarRenderer; +import net.minecraft.client.renderer.entity.state.AvatarRenderState; +import net.minecraft.world.entity.Avatar; +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(AvatarRenderer.class) +public abstract class AvatarRendererMixin { + + @Inject(method = "extractRenderState*", at = @At("TAIL")) + private void forceCapeVisibility(Avatar entity, AvatarRenderState state, float partialTicks, CallbackInfo ci) { + if (CapeManager.hasModernBetaCape(entity.getUUID())) { + state.showCape = true; + } + } +} diff --git a/src/main/java/me/collinb/modernbetapatches/mixin/CapeFeatureRendererMixin.java b/src/main/java/me/collinb/modernbetapatches/mixin/CapeFeatureRendererMixin.java deleted file mode 100644 index d42ab6d..0000000 --- a/src/main/java/me/collinb/modernbetapatches/mixin/CapeFeatureRendererMixin.java +++ /dev/null @@ -1,23 +0,0 @@ -package me.collinb.modernbetapatches.mixin; - -import me.collinb.modernbetapatches.manager.CapeManager; -import net.minecraft.client.render.VertexConsumerProvider; -import net.minecraft.client.render.entity.feature.CapeFeatureRenderer; -import net.minecraft.client.render.entity.state.PlayerEntityRenderState; -import net.minecraft.client.util.math.MatrixStack; -import net.minecraft.util.Identifier; -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(CapeFeatureRenderer.class) -public abstract class CapeFeatureRendererMixin { - @Inject(method = "render(Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;ILnet/minecraft/client/render/entity/state/PlayerEntityRenderState;FF)V", at = @At("HEAD")) - private void forceEnableCape(MatrixStack matrixStack, VertexConsumerProvider vertexConsumerProvider, int i, PlayerEntityRenderState playerEntityRenderState, float f, float g, CallbackInfo ci) { - Identifier cape = playerEntityRenderState.skinTextures.capeTexture(); - if (CapeManager.isModernBetaCape(cape)) { - playerEntityRenderState.capeVisible = true; - } - } -} diff --git a/src/main/java/me/collinb/modernbetapatches/mixin/PlayerInfoMixin.java b/src/main/java/me/collinb/modernbetapatches/mixin/PlayerInfoMixin.java new file mode 100644 index 0000000..177b616 --- /dev/null +++ b/src/main/java/me/collinb/modernbetapatches/mixin/PlayerInfoMixin.java @@ -0,0 +1,49 @@ +package me.collinb.modernbetapatches.mixin; + +import com.mojang.authlib.GameProfile; +import me.collinb.modernbetapatches.ModernBetaPatches; +import me.collinb.modernbetapatches.manager.CapeManager; +import net.minecraft.client.multiplayer.PlayerInfo; +import net.minecraft.core.ClientAsset; +import net.minecraft.world.entity.player.PlayerSkin; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import java.util.UUID; +import java.util.function.Supplier; + +@Mixin(PlayerInfo.class) +public abstract class PlayerInfoMixin { + + @Shadow + @Final + private GameProfile profile; + + @Inject(method = "createSkinLookup", at = @At("TAIL")) + private static void loadModernBetaCape(GameProfile profile, CallbackInfoReturnable> cir) { + if (ModernBetaPatches.isModernBeta()) { + UUID uuid = profile.id(); + CapeManager.fetchCape(uuid); + } + } + + @Inject(method = "getSkin", at = @At("TAIL"), cancellable = true) + private void overrideSkinTextures(CallbackInfoReturnable cir) { + if (ModernBetaPatches.isModernBeta()) { + PlayerSkin originalSkinTextures = cir.getReturnValue(); + ClientAsset.Texture modernBetaCape = CapeManager.getCape(profile.id()); + PlayerSkin newSkinTextures = new PlayerSkin( + originalSkinTextures.body(), + (modernBetaCape == null ? originalSkinTextures.cape() : modernBetaCape), + originalSkinTextures.elytra(), + originalSkinTextures.model(), + originalSkinTextures.secure() + ); + cir.setReturnValue(newSkinTextures); + } + } +} diff --git a/src/main/java/me/collinb/modernbetapatches/mixin/PlayerListEntryMixin.java b/src/main/java/me/collinb/modernbetapatches/mixin/PlayerListEntryMixin.java deleted file mode 100644 index 76aac5a..0000000 --- a/src/main/java/me/collinb/modernbetapatches/mixin/PlayerListEntryMixin.java +++ /dev/null @@ -1,47 +0,0 @@ -package me.collinb.modernbetapatches.mixin; - -import com.mojang.authlib.GameProfile; -import me.collinb.modernbetapatches.ModernBetaPatches; -import me.collinb.modernbetapatches.manager.CapeManager; -import net.minecraft.client.network.PlayerListEntry; -import net.minecraft.client.util.SkinTextures; -import net.minecraft.util.Identifier; -import org.spongepowered.asm.mixin.Final; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; - -import java.util.UUID; -import java.util.function.Supplier; - -@Mixin(PlayerListEntry.class) -public abstract class PlayerListEntryMixin { - - @Shadow - @Final - private GameProfile profile; - - @Inject(method = "texturesSupplier", at = @At("TAIL")) - private static void loadModernBetaCape(GameProfile profile, CallbackInfoReturnable> cir) { - if (ModernBetaPatches.isModernBeta()) { - UUID uuid = profile.getId(); - CapeManager.fetchCape(uuid); - } - } - - @Inject(method = "getSkinTextures", at = @At("TAIL"), cancellable = true) - private void overrideSkinTextures(CallbackInfoReturnable cir) { - if (ModernBetaPatches.isModernBeta()) { - SkinTextures originalSkinTextures = cir.getReturnValue(); - Identifier modernBetaCape = CapeManager.getCape(profile.getId()); - SkinTextures newSkinTextures = new SkinTextures( - originalSkinTextures.texture(), originalSkinTextures.textureUrl(), - (modernBetaCape == null ? originalSkinTextures.capeTexture() : modernBetaCape), - originalSkinTextures.elytraTexture(), - originalSkinTextures.model(), originalSkinTextures.secure()); - cir.setReturnValue(newSkinTextures); - } - } -} diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index 7f49d2d..52d8b76 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -21,7 +21,8 @@ ], "depends": { "fabricloader": ">=${loader_version}", - "fabric": "*", - "minecraft": ">=${minecraft_version}" + "fabric-api": "*", + "java": ">=25", + "minecraft": "~26.1" } } diff --git a/src/main/resources/modernbetapatches.mixins.json b/src/main/resources/modernbetapatches.mixins.json index 1a9bbd5..cf45f4e 100644 --- a/src/main/resources/modernbetapatches.mixins.json +++ b/src/main/resources/modernbetapatches.mixins.json @@ -2,11 +2,11 @@ "required": true, "minVersion": "0.8", "package": "me.collinb.modernbetapatches.mixin", - "compatibilityLevel": "JAVA_17", + "compatibilityLevel": "JAVA_25", "mixins": [], "client": [ - "CapeFeatureRendererMixin", - "PlayerListEntryMixin" + "AvatarRendererMixin", + "PlayerInfoMixin" ], "injectors": { "defaultRequire": 1 From eff1ddf6d7daccf8d4ff78acc5245786f5304bfc Mon Sep 17 00:00:00 2001 From: collin-b1 Date: Fri, 10 Apr 2026 14:27:13 -0400 Subject: [PATCH 2/3] Update Java version in build.yml --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5d5d4c6..396945b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,7 +16,7 @@ jobs: - name: setup jdk uses: actions/setup-java@v4 with: - java-version: '21' + java-version: '25' distribution: 'microsoft' - name: make gradle wrapper executable run: chmod +x ./gradlew From 7eeaaab5db906ffa1b9176a1f24ca2c9eb83d590 Mon Sep 17 00:00:00 2001 From: collin-b1 Date: Fri, 10 Apr 2026 14:28:51 -0400 Subject: [PATCH 3/3] Update build.yml action versions --- .github/workflows/build.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 396945b..c6e079a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -10,11 +10,11 @@ jobs: runs-on: ubuntu-24.04 steps: - name: checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: validate gradle wrapper - uses: gradle/actions/wrapper-validation@v4 + uses: gradle/actions/wrapper-validation@v6 - name: setup jdk - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: java-version: '25' distribution: 'microsoft' @@ -23,7 +23,7 @@ jobs: - name: build run: ./gradlew build - name: capture build artifacts - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: Artifacts path: build/libs/ \ No newline at end of file