diff --git a/README.md b/README.md index f6ade15..fa76e29 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Requires Java 17+ to launch Gradle; the wrapper resolves a Java 25 toolchain for ./gradlew clean jar -PreleaseVersion=0.1.0 ``` -The runtime requires Java 25. New JARs are `build/libs/purpur-ignis-.jar`. Targets Purpur `26.2.build.2618-stable` and requires **ProtocolLib 5.4.0** for Mask helmet projection. Neither server API nor ProtocolLib is bundled. Source builds do not require the private wiki. +The runtime requires Java 25. New JARs are `build/libs/purpur-ignis-.jar`. Targets Purpur `26.3.build.2651-stable` and requires **ProtocolLib 5.4.0** for Mask helmet projection. Neither server API nor ProtocolLib is bundled. Source builds do not require the private wiki. ## Knowledge and delivery diff --git a/build.gradle.kts b/build.gradle.kts index 7bf1e23..7688e26 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -17,10 +17,10 @@ tasks.withType().configureEach { } dependencies { - compileOnly("org.purpurmc.purpur:purpur-api:26.2.build.2618-stable") + compileOnly("org.purpurmc.purpur:purpur-api:26.3.build.2651-stable") compileOnly("net.dmulloy2:ProtocolLib:5.4.0") testImplementation("net.dmulloy2:ProtocolLib:5.4.0") - testImplementation("org.purpurmc.purpur:purpur-api:26.2.build.2618-stable") + testImplementation("org.purpurmc.purpur:purpur-api:26.3.build.2651-stable") testImplementation(platform("org.junit:junit-bom:5.13.4")) testImplementation("org.junit.jupiter:junit-jupiter") testImplementation("org.mockito:mockito-core:5.18.0") @@ -36,24 +36,24 @@ tasks.test { // Native API/event/delivery regressions run without starting a listening server. val ignisRuntime = layout.buildDirectory.dir("ignis-runtime") val downloadIgnisRuntime = tasks.register("downloadIgnisRuntime") { - val launcher = ignisRuntime.map { it.file("purpur-26.2-2618.jar") } + val launcher = ignisRuntime.map { it.file("purpur-26.3-2651.jar") } outputs.file(launcher) doLast { val file = launcher.get().asFile file.parentFile.mkdirs() - val connection = URI("https://api.purpurmc.org/v2/purpur/26.2/2618/download").toURL().openConnection() + val connection = URI("https://api.purpurmc.org/v2/purpur/26.3/2651/download").toURL().openConnection() connection.connectTimeout = 30_000 connection.readTimeout = 120_000 val bytes = connection.getInputStream().use { it.readBytes() } val digest = MessageDigest.getInstance("SHA-256").digest(bytes).joinToString("") { "%02x".format(it) } - check(digest == "4a32d046a118804d89ca74ba89b798c98f6d8d1f310c18077ac573597049de31") { "Purpur 2618 checksum mismatch" } + check(digest == "0677c1febecfbc2e5b0a7f76abd647b70a1f38d3cbdf3dd6a82e401b4262a598") { "Purpur 2651 checksum mismatch" } file.writeBytes(bytes) } } val prepareIgnisRuntime = tasks.register("prepareIgnisRuntime") { dependsOn(downloadIgnisRuntime) javaLauncher = javaToolchains.launcherFor { languageVersion = JavaLanguageVersion.of(25) } - classpath = files(ignisRuntime.map { it.file("purpur-26.2-2618.jar") }) + classpath = files(ignisRuntime.map { it.file("purpur-26.3-2651.jar") }) mainClass = "io.papermc.paperclip.Main" jvmArgs("-Dpaperclip.patchonly=true") workingDir(ignisRuntime) diff --git a/src/nativeTest/java/games/dmg/spigotignis/AnnouncementPluginLifecycleTest.java b/src/nativeTest/java/games/dmg/spigotignis/AnnouncementPluginLifecycleTest.java index bd7bb20..d53975c 100644 --- a/src/nativeTest/java/games/dmg/spigotignis/AnnouncementPluginLifecycleTest.java +++ b/src/nativeTest/java/games/dmg/spigotignis/AnnouncementPluginLifecycleTest.java @@ -29,9 +29,9 @@ class AnnouncementPluginLifecycleTest { var platform = mockStatic(Bukkit.class, call -> switch (call.getMethod().getName()) { case "getServer" -> server; case "isPrimaryThread" -> true; - case "getVersion" -> "Purpur 2618 (MC: 26.2)"; - case "getMinecraftVersion" -> "26.2"; - case "getBukkitVersion" -> "26.2-R0.1-SNAPSHOT"; + case "getVersion" -> "Purpur 2651 (MC: 26.3)"; + case "getMinecraftVersion" -> "26.3"; + case "getBukkitVersion" -> "26.3-R0.1-SNAPSHOT"; default -> call.callRealMethod(); })) { var packets = new ArrayList(); diff --git a/src/nativeTest/java/games/dmg/spigotignis/NativeDaggerFixture.java b/src/nativeTest/java/games/dmg/spigotignis/NativeDaggerFixture.java index ae99f09..1e701eb 100644 --- a/src/nativeTest/java/games/dmg/spigotignis/NativeDaggerFixture.java +++ b/src/nativeTest/java/games/dmg/spigotignis/NativeDaggerFixture.java @@ -187,6 +187,8 @@ final class NativeDaggerFixture implements AutoCloseable { doCallRealMethod().when(nativePlayer).getBoundingBox(); when(nativePlayer.getDeltaMovement()).thenReturn(Vec3.ZERO); when(nativePlayer.getKnownMovement()).thenReturn(Vec3.ZERO); + when(nativePlayer.getClientPositionAndRotation()).thenAnswer(call -> + net.minecraft.core.PositionAndRotation.of(nativePlayer.position(), nativePlayer.getYRot(), nativePlayer.getXRot())); doCallRealMethod().when(nativePlayer).setPos(anyDouble(), anyDouble(), anyDouble()); doCallRealMethod().when(nativePlayer).setBoundingBox(any(net.minecraft.world.phys.AABB.class)); doCallRealMethod().when(nativePlayer).setPosRaw(anyDouble(), anyDouble(), anyDouble()); @@ -273,8 +275,10 @@ final class NativeDaggerFixture implements AutoCloseable { var key = net.minecraft.resources.ResourceKey.>create( net.minecraft.core.registries.Registries.RECIPE, net.minecraft.resources.Identifier.fromNamespaceAndPath("spigotignis", "dagger")); var holder = new net.minecraft.world.item.crafting.RecipeHolder(key, nativeRecipe); - var recipes = new net.minecraft.world.item.crafting.RecipeManager(org.bukkit.craftbukkit.CraftRegistry.getMinecraftRegistry()); - recipes.recipes = net.minecraft.world.item.crafting.RecipeMap.create(List.of(holder)); + var recipes = NativeRuntime.recipes(); + recipes.recipes = net.minecraft.world.item.crafting.RecipeMap.createEmpty(); + recipes.recipes.addRecipe(holder); + recipes.finalizeRecipeLoading(net.minecraft.world.flag.FeatureFlags.VANILLA_SET); when(level.recipeAccess()).thenReturn(recipes); menu.craftSlots.setCurrentRecipe(holder); menu.resultSlots.setRecipeUsed(holder); diff --git a/src/nativeTest/java/games/dmg/spigotignis/NativeEchoAnnouncementTest.java b/src/nativeTest/java/games/dmg/spigotignis/NativeEchoAnnouncementTest.java index f04fc98..55e3401 100644 --- a/src/nativeTest/java/games/dmg/spigotignis/NativeEchoAnnouncementTest.java +++ b/src/nativeTest/java/games/dmg/spigotignis/NativeEchoAnnouncementTest.java @@ -51,9 +51,9 @@ class NativeEchoAnnouncementTest { try (var platform = mockStatic(Bukkit.class, call -> switch (call.getMethod().getName()) { case "getServer" -> server; case "isPrimaryThread" -> true; - case "getVersion" -> "Purpur 2618 (MC: 26.2)"; - case "getMinecraftVersion" -> "26.2"; - case "getBukkitVersion" -> "26.2-R0.1-SNAPSHOT"; + case "getVersion" -> "Purpur 2651 (MC: 26.3)"; + case "getMinecraftVersion" -> "26.3"; + case "getBukkitVersion" -> "26.3-R0.1-SNAPSHOT"; default -> call.callRealMethod(); })) { UUID owner = UUID.randomUUID(), receipt = UUID.randomUUID(); diff --git a/src/nativeTest/java/games/dmg/spigotignis/NativeRuntime.java b/src/nativeTest/java/games/dmg/spigotignis/NativeRuntime.java index cefe775..dc8a087 100644 --- a/src/nativeTest/java/games/dmg/spigotignis/NativeRuntime.java +++ b/src/nativeTest/java/games/dmg/spigotignis/NativeRuntime.java @@ -21,6 +21,7 @@ import net.minecraft.server.permissions.LevelBasedPermissionSet; import net.minecraft.tags.TagLoader; import net.minecraft.util.Util; import net.minecraft.world.flag.FeatureFlags; +import net.minecraft.world.item.crafting.RecipeManager; import net.minecraft.world.level.DataPackConfig; import net.minecraft.world.level.WorldDataConfiguration; import org.bukkit.craftbukkit.CraftRegistry; @@ -29,6 +30,7 @@ import org.bukkit.craftbukkit.CraftRegistry; final class NativeRuntime { private NativeRuntime() { } private static boolean ready; + private static RecipeManager recipes; static synchronized void bootstrap() throws Exception { if (ready) { return; } SharedConstants.tryDetectVersion(); @@ -40,12 +42,12 @@ final class NativeRuntime { try (var resources = new MultiPackResourceManager(PackType.SERVER_DATA, packs.openAllSelected())) { LayeredRegistryAccess layers = RegistryLayer.createRegistryAccess(); List> tags = TagLoader.loadTagsForExistingRegistries(resources, layers.getLayer(RegistryLayer.STATIC)); - List> lookups = TagLoader.buildUpdatedLookups(layers.getAccessForLoading(RegistryLayer.WORLDGEN), tags); - RegistryAccess.Frozen worldgen = RegistryDataLoader.load(resources, lookups, - RegistryDataLoader.WORLDGEN_REGISTRIES, Util.backgroundExecutor()).join(); - layers = layers.replaceFrom(RegistryLayer.WORLDGEN, worldgen); + List> lookups = TagLoader.buildUpdatedLookups(layers.getAccessForLoading(RegistryLayer.WORLD), tags); + RegistryAccess.Frozen world = RegistryDataLoader.load(resources, lookups, + RegistryDataLoader.WORLD_REGISTRIES, Util.backgroundExecutor()).join(); + layers = layers.replaceFrom(RegistryLayer.WORLD, world); RegistryAccess.Frozen dimensions = RegistryDataLoader.load(resources, - Stream.concat(lookups.stream(), worldgen.listRegistries()).toList(), + Stream.concat(lookups.stream(), world.listRegistries()).toList(), RegistryDataLoader.DIMENSION_REGISTRIES, Util.backgroundExecutor()).join(); layers = layers.replaceFrom(RegistryLayer.DIMENSIONS, dimensions); Class.forName(org.bukkit.Registry.class.getName()); @@ -54,7 +56,16 @@ final class NativeRuntime { Util.backgroundExecutor(), Runnable::run).join(); datapack.updateComponentsAndStaticRegistryTags(); CraftRegistry.setMinecraftRegistry(layers.compositeAccess().freeze()); + recipes = datapack.getRecipeManager(); + recipes.finalizeRecipeLoading(flags); } ready = true; } + + static RecipeManager recipes() { + if (!ready) { + throw new IllegalStateException("Native runtime has not been bootstrapped"); + } + return recipes; + } } diff --git a/src/test/java/games/dmg/spigotignis/PurpurDistributionTest.java b/src/test/java/games/dmg/spigotignis/PurpurDistributionTest.java index 90617ba..5b1296e 100644 --- a/src/test/java/games/dmg/spigotignis/PurpurDistributionTest.java +++ b/src/test/java/games/dmg/spigotignis/PurpurDistributionTest.java @@ -21,6 +21,9 @@ class PurpurDistributionTest { assertEquals(version, metadata.get("version")); assertEquals("SpigotIgnis", metadata.get("name"), "Retain the data directory and persistent namespace"); assertEquals("games.dmg.spigotignis.SpigotIgnisPlugin", metadata.get("main")); + assertEquals("1.21", metadata.get("api-version")); + assertTrue(((Map) metadata.get("commands")).keySet().containsAll( + java.util.Set.of("ignis", "ignisadmin"))); assertTrue(((Map) metadata.get("permissions")).containsKey("ignis.admin")); assertEquals(java.util.List.of("ProtocolLib"), metadata.get("depend")); var descriptor = new org.bukkit.plugin.PluginDescriptionFile(jar.getInputStream(jar.getJarEntry("plugin.yml"))); @@ -42,10 +45,18 @@ class PurpurDistributionTest { } @Test - void releaseAndCiAgreeWithThePurpurDistributionNameAndToolchain() throws Exception { + void releaseAndCiAgreeWithThePurpur26_3DistributionAndToolchain() throws Exception { String build = Files.readString(Path.of("build.gradle.kts")); - assertTrue(build.contains("purpur-api:26.2.build.2618-stable")); + assertTrue(build.contains("purpur-api:26.3.build.2651-stable")); + assertTrue(build.contains("purpur-26.3-2651.jar")); + assertTrue(build.contains("https://api.purpurmc.org/v2/purpur/26.3/2651/download")); + assertTrue(build.contains("0677c1febecfbc2e5b0a7f76abd647b70a1f38d3cbdf3dd6a82e401b4262a598")); + assertTrue(build.contains("net.dmulloy2:ProtocolLib:5.4.0")); + assertFalse(build.contains("26.2") || build.contains("2618")); assertFalse(build.contains("org.spigotmc:spigot-api")); + String readme = Files.readString(Path.of("README.md")); + assertTrue(readme.contains("Purpur `26.3.build.2651-stable`")); + assertFalse(readme.contains("26.2") || readme.contains("2618")); for (String workflow : new String[] {"ci.yml", "release.yml"}) { String content = Files.readString(Path.of(".gitea/workflows", workflow)); assertTrue(content.contains("java-version: 25"));