diff --git a/patches/server/0004-Test-changes.patch b/patches/server/0004-Test-changes.patch index 53e097733fb9..f57c00d9019b 100644 --- a/patches/server/0004-Test-changes.patch +++ b/patches/server/0004-Test-changes.patch @@ -5,10 +5,18 @@ Subject: [PATCH] Test changes diff --git a/build.gradle.kts b/build.gradle.kts -index f83cda5b820ebb3dcbc3a39059579ba9487586e8..ef91080dc7123677190839b057c3458c4d5b9f32 100644 +index 14eae4cf079a384691c72c2f70ba627bddb0b2b1..855418dd82c661d94278a5d284629e2823ad1bc5 100644 --- a/build.gradle.kts +++ b/build.gradle.kts -@@ -58,6 +58,12 @@ tasks.compileJava { +@@ -23,6 +23,7 @@ dependencies { + testImplementation("org.junit.jupiter:junit-jupiter:5.10.0") + testImplementation("org.hamcrest:hamcrest:2.2") + testImplementation("org.mockito:mockito-core:5.5.0") ++ testImplementation("org.junit-pioneer:junit-pioneer:2.2.0") // Paper - CartesianTest + } + + val craftbukkitPackageVersion = "1_20_R3" // Paper +@@ -58,6 +59,12 @@ tasks.compileJava { options.setIncremental(false) } @@ -97,6 +105,228 @@ index 0000000000000000000000000000000000000000..6eb95a5e2534974c0e52e2b78b04e7c2 + return Collections.emptySet(); + } +} +diff --git a/src/test/java/io/papermc/paper/util/MethodParameterProvider.java b/src/test/java/io/papermc/paper/util/MethodParameterProvider.java +new file mode 100644 +index 0000000000000000000000000000000000000000..c26640b1d603cff0971083d60947cb594f1e3fbe +--- /dev/null ++++ b/src/test/java/io/papermc/paper/util/MethodParameterProvider.java +@@ -0,0 +1,196 @@ ++package io.papermc.paper.util; ++ ++import java.lang.reflect.Method; ++import java.lang.reflect.Parameter; ++import java.util.List; ++import java.util.function.Predicate; ++import java.util.stream.Stream; ++import org.junit.jupiter.api.Test; ++import org.junit.jupiter.api.TestFactory; ++import org.junit.jupiter.api.TestTemplate; ++import org.junit.jupiter.api.extension.ExtensionContext; ++import org.junit.jupiter.params.support.AnnotationConsumer; ++import org.junit.platform.commons.JUnitException; ++import org.junit.platform.commons.PreconditionViolationException; ++import org.junit.platform.commons.util.ClassLoaderUtils; ++import org.junit.platform.commons.util.CollectionUtils; ++import org.junit.platform.commons.util.Preconditions; ++import org.junit.platform.commons.util.ReflectionUtils; ++import org.junit.platform.commons.util.StringUtils; ++import org.junitpioneer.jupiter.cartesian.CartesianParameterArgumentsProvider; ++ ++import static java.lang.String.format; ++import static java.util.Arrays.stream; ++import static java.util.stream.Collectors.toList; ++import static org.junit.platform.commons.util.AnnotationUtils.isAnnotated; ++import static org.junit.platform.commons.util.CollectionUtils.isConvertibleToStream; ++ ++public class MethodParameterProvider implements CartesianParameterArgumentsProvider, AnnotationConsumer { ++ private MethodParameterSource source; ++ ++ MethodParameterProvider() { ++ } ++ ++ @Override ++ public void accept(final MethodParameterSource source) { ++ this.source = source; ++ } ++ ++ @Override ++ public Stream provideArguments(ExtensionContext context, Parameter parameter) { ++ return this.provideArguments(context, this.source); ++ } ++ ++ // Below is mostly copied from MethodArgumentsProvider ++ ++ private static final Predicate isFactoryMethod = // ++ method -> isConvertibleToStream(method.getReturnType()) && !isTestMethod(method); ++ ++ protected Stream provideArguments(ExtensionContext context, MethodParameterSource methodSource) { ++ Class testClass = context.getRequiredTestClass(); ++ Method testMethod = context.getRequiredTestMethod(); ++ Object testInstance = context.getTestInstance().orElse(null); ++ String[] methodNames = methodSource.value(); ++ // @formatter:off ++ return stream(methodNames) ++ .map(factoryMethodName -> findFactoryMethod(testClass, testMethod, factoryMethodName)) ++ .map(factoryMethod -> validateFactoryMethod(factoryMethod, testInstance)) ++ .map(factoryMethod -> context.getExecutableInvoker().invoke(factoryMethod, testInstance)) ++ .flatMap(CollectionUtils::toStream); ++ // @formatter:on ++ } ++ ++ private static Method findFactoryMethod(Class testClass, Method testMethod, String factoryMethodName) { ++ String originalFactoryMethodName = factoryMethodName; ++ ++ // If the user did not provide a factory method name, find a "default" local ++ // factory method with the same name as the parameterized test method. ++ if (StringUtils.isBlank(factoryMethodName)) { ++ factoryMethodName = testMethod.getName(); ++ return findFactoryMethodBySimpleName(testClass, testMethod, factoryMethodName); ++ } ++ ++ // Convert local factory method name to fully-qualified method name. ++ if (!looksLikeAFullyQualifiedMethodName(factoryMethodName)) { ++ factoryMethodName = testClass.getName() + "#" + factoryMethodName; ++ } ++ ++ // Find factory method using fully-qualified name. ++ Method factoryMethod = findFactoryMethodByFullyQualifiedName(testClass, testMethod, factoryMethodName); ++ ++ // Ensure factory method has a valid return type and is not a test method. ++ Preconditions.condition(isFactoryMethod.test(factoryMethod), () -> format( ++ "Could not find valid factory method [%s] for test class [%s] but found the following invalid candidate: %s", ++ originalFactoryMethodName, testClass.getName(), factoryMethod)); ++ ++ return factoryMethod; ++ } ++ ++ private static boolean looksLikeAFullyQualifiedMethodName(String factoryMethodName) { ++ if (factoryMethodName.contains("#")) { ++ return true; ++ } ++ int indexOfFirstDot = factoryMethodName.indexOf('.'); ++ if (indexOfFirstDot == -1) { ++ return false; ++ } ++ int indexOfLastOpeningParenthesis = factoryMethodName.lastIndexOf('('); ++ if (indexOfLastOpeningParenthesis > 0) { ++ // Exclude simple/local method names with parameters ++ return indexOfFirstDot < indexOfLastOpeningParenthesis; ++ } ++ // If we get this far, we conclude the supplied factory method name "looks" ++ // like it was intended to be a fully-qualified method name, even if the ++ // syntax is invalid. We do this in order to provide better diagnostics for ++ // the user when a fully-qualified method name is in fact invalid. ++ return true; ++ } ++ ++ // package-private for testing ++ static Method findFactoryMethodByFullyQualifiedName( ++ Class testClass, Method testMethod, ++ String fullyQualifiedMethodName ++ ) { ++ String[] methodParts = ReflectionUtils.parseFullyQualifiedMethodName(fullyQualifiedMethodName); ++ String className = methodParts[0]; ++ String methodName = methodParts[1]; ++ String methodParameters = methodParts[2]; ++ ClassLoader classLoader = ClassLoaderUtils.getClassLoader(testClass); ++ Class clazz = loadRequiredClass(className, classLoader); ++ ++ // Attempt to find an exact match first. ++ Method factoryMethod = ReflectionUtils.findMethod(clazz, methodName, methodParameters).orElse(null); ++ if (factoryMethod != null) { ++ return factoryMethod; ++ } ++ ++ boolean explicitParameterListSpecified = // ++ StringUtils.isNotBlank(methodParameters) || fullyQualifiedMethodName.endsWith("()"); ++ ++ // If we didn't find an exact match but an explicit parameter list was specified, ++ // that's a user configuration error. ++ Preconditions.condition(!explicitParameterListSpecified, ++ () -> format("Could not find factory method [%s(%s)] in class [%s]", methodName, methodParameters, ++ className)); ++ ++ // Otherwise, fall back to the same lenient search semantics that are used ++ // to locate a "default" local factory method. ++ return findFactoryMethodBySimpleName(clazz, testMethod, methodName); ++ } ++ ++ /** ++ * Find the factory method by searching for all methods in the given {@code clazz} ++ * with the desired {@code factoryMethodName} which have return types that can be ++ * converted to a {@link Stream}, ignoring the {@code testMethod} itself as well ++ * as any {@code @Test}, {@code @TestTemplate}, or {@code @TestFactory} methods ++ * with the same name. ++ * ++ * @return the single factory method matching the search criteria ++ * @throws PreconditionViolationException if the factory method was not found or ++ * multiple competing factory methods with the same name were found ++ */ ++ private static Method findFactoryMethodBySimpleName(Class clazz, Method testMethod, String factoryMethodName) { ++ Predicate isCandidate = candidate -> factoryMethodName.equals(candidate.getName()) ++ && !testMethod.equals(candidate); ++ List candidates = ReflectionUtils.findMethods(clazz, isCandidate); ++ ++ List factoryMethods = candidates.stream().filter(isFactoryMethod).collect(toList()); ++ ++ Preconditions.condition(factoryMethods.size() > 0, () -> { ++ // If we didn't find the factory method using the isFactoryMethod Predicate, perhaps ++ // the specified factory method has an invalid return type or is a test method. ++ // In that case, we report the invalid candidates that were found. ++ if (candidates.size() > 0) { ++ return format( ++ "Could not find valid factory method [%s] in class [%s] but found the following invalid candidates: %s", ++ factoryMethodName, clazz.getName(), candidates); ++ } ++ // Otherwise, report that we didn't find anything. ++ return format("Could not find factory method [%s] in class [%s]", factoryMethodName, clazz.getName()); ++ }); ++ Preconditions.condition(factoryMethods.size() == 1, ++ () -> format("%d factory methods named [%s] were found in class [%s]: %s", factoryMethods.size(), ++ factoryMethodName, clazz.getName(), factoryMethods)); ++ return factoryMethods.get(0); ++ } ++ ++ private static boolean isTestMethod(Method candidate) { ++ return isAnnotated(candidate, Test.class) || isAnnotated(candidate, TestTemplate.class) ++ || isAnnotated(candidate, TestFactory.class); ++ } ++ ++ private static Class loadRequiredClass(String className, ClassLoader classLoader) { ++ return ReflectionUtils.tryToLoadClass(className, classLoader).getOrThrow( ++ cause -> new JUnitException(format("Could not load class [%s]", className), cause)); ++ } ++ ++ private static Method validateFactoryMethod(Method factoryMethod, Object testInstance) { ++ Preconditions.condition( ++ factoryMethod.getDeclaringClass().isInstance(testInstance) || ReflectionUtils.isStatic(factoryMethod), ++ () -> format("Method '%s' must be static: local factory methods must be static " ++ + "unless the PER_CLASS @TestInstance lifecycle mode is used; " ++ + "external factory methods must always be static.", ++ factoryMethod.toGenericString())); ++ return factoryMethod; ++ } ++} +diff --git a/src/test/java/io/papermc/paper/util/MethodParameterSource.java b/src/test/java/io/papermc/paper/util/MethodParameterSource.java +new file mode 100644 +index 0000000000000000000000000000000000000000..6cbf11c898439834cffb99ef84e5df1494356809 +--- /dev/null ++++ b/src/test/java/io/papermc/paper/util/MethodParameterSource.java +@@ -0,0 +1,14 @@ ++package io.papermc.paper.util; ++ ++import java.lang.annotation.ElementType; ++import java.lang.annotation.Retention; ++import java.lang.annotation.RetentionPolicy; ++import java.lang.annotation.Target; ++import org.junitpioneer.jupiter.cartesian.CartesianArgumentsSource; ++ ++@Retention(RetentionPolicy.RUNTIME) ++@Target({ElementType.PARAMETER, ElementType.ANNOTATION_TYPE}) ++@CartesianArgumentsSource(MethodParameterProvider.class) ++public @interface MethodParameterSource { ++ String[] value() default {}; ++} diff --git a/src/test/java/org/bukkit/support/DummyServer.java b/src/test/java/org/bukkit/support/DummyServer.java index d96efc9aa90debcca5f237c949ba11b10070223a..d1253a07a90aa4cc29d0140795203a71118c827a 100644 --- a/src/test/java/org/bukkit/support/DummyServer.java diff --git a/patches/server/0010-Adventure.patch b/patches/server/0010-Adventure.patch index 02c5efa45622..027f353d199c 100644 --- a/patches/server/0010-Adventure.patch +++ b/patches/server/0010-Adventure.patch @@ -1624,19 +1624,18 @@ index 0000000000000000000000000000000000000000..c10c0ffa29332d73328f088935a0b914 +} diff --git a/src/main/java/io/papermc/paper/adventure/WrapperAwareSerializer.java b/src/main/java/io/papermc/paper/adventure/WrapperAwareSerializer.java new file mode 100644 -index 0000000000000000000000000000000000000000..49809d4fa42d1054119cf38babba5a6161b5ee97 +index 0000000000000000000000000000000000000000..0a7764797b0f1f346fb35f09377d5639bc382bff --- /dev/null +++ b/src/main/java/io/papermc/paper/adventure/WrapperAwareSerializer.java -@@ -0,0 +1,40 @@ +@@ -0,0 +1,39 @@ +package io.papermc.paper.adventure; + +import com.mojang.datafixers.util.Pair; +import java.util.function.Function; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.serializer.ComponentSerializer; -+import net.minecraft.nbt.NbtOps; -+import net.minecraft.nbt.Tag; +import net.minecraft.network.chat.ComponentSerialization; ++import net.minecraft.util.JavaOps; + +final class WrapperAwareSerializer implements ComponentSerializer { + @Override @@ -1644,26 +1643,26 @@ index 0000000000000000000000000000000000000000..49809d4fa42d1054119cf38babba5a61 + if (input instanceof AdventureComponent) { + return ((AdventureComponent) input).adventure; + } -+ final Tag tag = ComponentSerialization.CODEC.encodeStart(NbtOps.INSTANCE, input) ++ final Object obj = ComponentSerialization.CODEC.encodeStart(JavaOps.INSTANCE, input) + .get().map(Function.identity(), partial -> { + throw new RuntimeException("Failed to encode Minecraft Component: " + input + "; " + partial.message()); + }); -+ final Pair converted = AdventureCodecs.COMPONENT_CODEC.decode(NbtOps.INSTANCE, tag) ++ final Pair converted = AdventureCodecs.COMPONENT_CODEC.decode(JavaOps.INSTANCE, obj) + .get().map(Function.identity(), partial -> { -+ throw new RuntimeException("Failed to decode to adventure Component: " + tag + "; " + partial.message()); ++ throw new RuntimeException("Failed to decode to adventure Component: " + obj + "; " + partial.message()); + }); + return converted.getFirst(); + } + + @Override + public net.minecraft.network.chat.Component serialize(final Component component) { -+ final Tag tag = AdventureCodecs.COMPONENT_CODEC.encodeStart(NbtOps.INSTANCE, component) ++ final Object obj = AdventureCodecs.COMPONENT_CODEC.encodeStart(JavaOps.INSTANCE, component) + .get().map(Function.identity(), partial -> { + throw new RuntimeException("Failed to encode adventure Component: " + component + "; " + partial.message()); + }); -+ final Pair converted = ComponentSerialization.CODEC.decode(NbtOps.INSTANCE, tag) ++ final Pair converted = ComponentSerialization.CODEC.decode(JavaOps.INSTANCE, obj) + .get().map(Function.identity(), partial -> { -+ throw new RuntimeException("Failed to decode to Minecraft Component: " + tag + "; " + partial.message()); ++ throw new RuntimeException("Failed to decode to Minecraft Component: " + obj + "; " + partial.message()); + }); + return converted.getFirst(); + } @@ -3198,7 +3197,7 @@ index 4c62df5a3781ec9df4a5c5f1b528649e6e8a62d1..affd1b8c7589ba59330dc0b6fc803cce } diff --git a/src/main/java/net/minecraft/world/level/block/entity/SignBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/SignBlockEntity.java -index fe31cd2698077c7d75fbd411a59947268100823c..4642113daf986c16d1390c53fbbb50e1e3067738 100644 +index 9026e380736d7298dd68d6aeb817c59f5daf552e..6003731da7be596baf1954df2e13ae54e111cd91 100644 --- a/src/main/java/net/minecraft/world/level/block/entity/SignBlockEntity.java +++ b/src/main/java/net/minecraft/world/level/block/entity/SignBlockEntity.java @@ -209,22 +209,22 @@ public class SignBlockEntity extends BlockEntity implements CommandSource { // C @@ -5468,16 +5467,23 @@ index 0000000000000000000000000000000000000000..3aedd0bbc97edacc1ebf71264b310e55 +} diff --git a/src/test/java/io/papermc/paper/adventure/AdventureCodecsTest.java b/src/test/java/io/papermc/paper/adventure/AdventureCodecsTest.java new file mode 100644 -index 0000000000000000000000000000000000000000..af4b7704ab4690138e3188a45d41b890369f6237 +index 0000000000000000000000000000000000000000..9f1f1ce3370df334232d4a1dfe072a4ea3656a52 --- /dev/null +++ b/src/test/java/io/papermc/paper/adventure/AdventureCodecsTest.java -@@ -0,0 +1,366 @@ +@@ -0,0 +1,399 @@ +package io.papermc.paper.adventure; + +import com.mojang.datafixers.util.Pair; +import com.mojang.serialization.Codec; +import com.mojang.serialization.DataResult; ++import com.mojang.serialization.DynamicOps; ++import com.mojang.serialization.JsonOps; ++import io.papermc.paper.util.MethodParameterSource; +import java.io.IOException; ++import java.lang.annotation.ElementType; ++import java.lang.annotation.Retention; ++import java.lang.annotation.RetentionPolicy; ++import java.lang.annotation.Target; +import java.util.List; +import java.util.UUID; +import java.util.function.Function; @@ -5500,6 +5506,7 @@ index 0000000000000000000000000000000000000000..af4b7704ab4690138e3188a45d41b890 +import net.minecraft.nbt.Tag; +import net.minecraft.network.chat.ComponentSerialization; +import net.minecraft.resources.ResourceLocation; ++import net.minecraft.util.JavaOps; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import org.apache.commons.lang3.RandomStringUtils; @@ -5508,6 +5515,7 @@ index 0000000000000000000000000000000000000000..af4b7704ab4690138e3188a45d41b890 +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; +import org.junit.jupiter.params.provider.MethodSource; ++import org.junitpioneer.jupiter.cartesian.CartesianTest; + +import static io.papermc.paper.adventure.AdventureCodecs.CLICK_EVENT_CODEC; +import static io.papermc.paper.adventure.AdventureCodecs.COMPONENT_CODEC; @@ -5539,6 +5547,8 @@ index 0000000000000000000000000000000000000000..af4b7704ab4690138e3188a45d41b890 + +class AdventureCodecsTest extends AbstractTestingBase { + ++ static final String PARAMETERIZED_NAME = "[{index}] {displayName}: {arguments}"; ++ + @Test + void testTextColor() { + final TextColor color = color(0x1d38df); @@ -5566,7 +5576,7 @@ index 0000000000000000000000000000000000000000..af4b7704ab4690138e3188a45d41b890 + assertEquals(key.asString(), location.toString()); + } + -+ @ParameterizedTest ++ @ParameterizedTest(name = PARAMETERIZED_NAME) + @EnumSource(value = ClickEvent.Action.class, mode = EnumSource.Mode.EXCLUDE, names = {"OPEN_FILE"}) + void testClickEvent(final ClickEvent.Action action) { + final ClickEvent event = ClickEvent.clickEvent(action, RandomStringUtils.randomAlphanumeric(20)); @@ -5629,33 +5639,47 @@ index 0000000000000000000000000000000000000000..af4b7704ab4690138e3188a45d41b890 + assertEquals(requireNonNull(style.color()).value(), requireNonNull(nms.getColor()).getValue()); + } + -+ @ParameterizedTest -+ @MethodSource({"testStyles"}) -+ void testDirectRoundTripStyle(final Style style) { -+ testDirectRoundTrip(STYLE_MAP_CODEC.codec(), style); ++ @CartesianTest(name = PARAMETERIZED_NAME) ++ void testDirectRoundTripStyle( ++ @MethodParameterSource("dynamicOps") final DynamicOps dynamicOps, ++ @MethodParameterSource("testStyles") final Style style ++ ) { ++ testDirectRoundTrip(dynamicOps, STYLE_MAP_CODEC.codec(), style); ++ } ++ ++ @CartesianTest(name = PARAMETERIZED_NAME) ++ void testMinecraftRoundTripStyle( ++ @MethodParameterSource("dynamicOps") final DynamicOps dynamicOps, ++ @MethodParameterSource("testStyles") final Style style ++ ) { ++ testMinecraftRoundTrip(dynamicOps, STYLE_MAP_CODEC.codec(), net.minecraft.network.chat.Style.Serializer.CODEC, style); + } + -+ @ParameterizedTest -+ @MethodSource({"testStyles"}) -+ void testMinecraftRoundTripStyle(final Style style) { -+ testMinecraftRoundTrip(STYLE_MAP_CODEC.codec(), net.minecraft.network.chat.Style.Serializer.CODEC, style); ++ @CartesianTest(name = PARAMETERIZED_NAME) ++ void testDirectRoundTripComponent( ++ @MethodParameterSource("dynamicOps") final DynamicOps dynamicOps, ++ @TestComponents final Component component ++ ) { ++ testDirectRoundTrip(dynamicOps, COMPONENT_CODEC, component); + } + -+ @ParameterizedTest -+ @MethodSource({"testTexts", "testTranslatables", "testKeybinds", "testScores", -+ "testSelectors", "testBlockNbts", "testEntityNbts", "testStorageNbts"}) -+ void testDirectRoundTripComponent(final Component component) { -+ testDirectRoundTrip(COMPONENT_CODEC, component); ++ @CartesianTest(name = PARAMETERIZED_NAME) ++ void testMinecraftRoundTripComponent( ++ @MethodParameterSource("dynamicOps") final DynamicOps dynamicOps, ++ @TestComponents final Component component ++ ) { ++ testMinecraftRoundTrip(dynamicOps, COMPONENT_CODEC, ComponentSerialization.CODEC, component); + } + -+ @ParameterizedTest -+ @MethodSource({"testTexts", "testTranslatables", "testKeybinds", "testScores", -+ "testSelectors", "testBlockNbts", "testEntityNbts", "testStorageNbts"}) -+ void testMinecraftRoundTripComponent(final Component component) { -+ testMinecraftRoundTrip(COMPONENT_CODEC, ComponentSerialization.CODEC, component); ++ static List> dynamicOps() { ++ return List.of( ++ NbtOps.INSTANCE, ++ JavaOps.INSTANCE, ++ JsonOps.INSTANCE ++ ); + } + -+ @ParameterizedTest ++ @ParameterizedTest(name = PARAMETERIZED_NAME) + @MethodSource({"invalidData"}) + void invalidThrows(final Tag input) { + assertThrows(RuntimeException.class, () -> { @@ -5666,33 +5690,33 @@ index 0000000000000000000000000000000000000000..af4b7704ab4690138e3188a45d41b890 + }); + } + -+ static void testDirectRoundTrip(final Codec codec, final A adventure) { -+ final Tag encoded = require( -+ codec.encodeStart(NbtOps.INSTANCE, adventure), ++ static void testDirectRoundTrip(final DynamicOps ops, final Codec codec, final A adventure) { ++ final O encoded = require( ++ codec.encodeStart(ops, adventure), + msg -> "Failed to encode " + adventure + ": " + msg + ); -+ final Pair roundTripResult = require( -+ codec.decode(NbtOps.INSTANCE, encoded), ++ final Pair roundTripResult = require( ++ codec.decode(ops, encoded), + msg -> "Failed to decode " + encoded + ": " + msg + ); + assertEquals(adventure, roundTripResult.getFirst()); + } + -+ static void testMinecraftRoundTrip(final Codec adventureCodec, final Codec minecraftCodec, final A adventure) { -+ final Tag encoded = require( -+ adventureCodec.encodeStart(NbtOps.INSTANCE, adventure), ++ static void testMinecraftRoundTrip(final DynamicOps ops, final Codec adventureCodec, final Codec minecraftCodec, final A adventure) { ++ final O encoded = require( ++ adventureCodec.encodeStart(ops, adventure), + msg -> "Failed to encode " + adventure + ": " + msg + ); + final M minecraftResult = require( -+ minecraftCodec.decode(NbtOps.INSTANCE, encoded), ++ minecraftCodec.decode(ops, encoded), + msg -> "Failed to decode to Minecraft: " + encoded + "; " + msg + ).getFirst(); -+ final Tag minecraftReEncoded = require( -+ minecraftCodec.encodeStart(NbtOps.INSTANCE, minecraftResult), ++ final O minecraftReEncoded = require( ++ minecraftCodec.encodeStart(ops, minecraftResult), + msg -> "Failed to re-encode Minecraft: " + minecraftResult + "; " + msg + ); -+ final Pair roundTripResult = require( -+ adventureCodec.decode(NbtOps.INSTANCE, minecraftReEncoded), ++ final Pair roundTripResult = require( ++ adventureCodec.decode(ops, minecraftReEncoded), + msg -> "Failed to decode " + minecraftReEncoded + ": " + msg + ); + assertEquals(adventure, roundTripResult.getFirst()); @@ -5737,6 +5761,14 @@ index 0000000000000000000000000000000000000000..af4b7704ab4690138e3188a45d41b890 + ); + } + ++ @Retention(RetentionPolicy.RUNTIME) ++ @Target({ElementType.PARAMETER, ElementType.ANNOTATION_TYPE}) ++ @MethodParameterSource({ ++ "testTexts", "testTranslatables", "testKeybinds", "testScores", ++ "testSelectors", "testBlockNbts", "testEntityNbts", "testStorageNbts" ++ }) ++ @interface TestComponents {} ++ + static List testTexts() { + return List.of( + Component.empty(), diff --git a/patches/server/0132-Use-TerminalConsoleAppender-for-console-improvements.patch b/patches/server/0132-Use-TerminalConsoleAppender-for-console-improvements.patch index a56f46962549..641cc1047d8f 100644 --- a/patches/server/0132-Use-TerminalConsoleAppender-for-console-improvements.patch +++ b/patches/server/0132-Use-TerminalConsoleAppender-for-console-improvements.patch @@ -25,7 +25,7 @@ Other changes: Co-Authored-By: Emilia Kond diff --git a/build.gradle.kts b/build.gradle.kts -index 89064b339ce27a09ad546c62a36459534d138c29..63ed761efdb11533905a8a44a7564d86c7e0bc90 100644 +index c3ea9bccd4ee0327b5752d502300f0bde4fca08c..52e1f25bfe0fd9e96cc3f847d0b2b4db6b50c12a 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -6,9 +6,30 @@ plugins { @@ -60,7 +60,7 @@ index 89064b339ce27a09ad546c62a36459534d138c29..63ed761efdb11533905a8a44a7564d86 implementation("org.apache.logging.log4j:log4j-iostreams:2.19.0") // Paper - remove exclusion implementation("org.ow2.asm:asm:9.5") implementation("org.ow2.asm:asm-commons:9.5") // Paper - ASM event executor generation -@@ -79,7 +100,7 @@ relocation { +@@ -80,7 +101,7 @@ relocation { } tasks.shadowJar { diff --git a/patches/server/0387-Deobfuscate-stacktraces-in-log-messages-crash-report.patch b/patches/server/0387-Deobfuscate-stacktraces-in-log-messages-crash-report.patch index 5081f05f86ba..f33dfe730e00 100644 --- a/patches/server/0387-Deobfuscate-stacktraces-in-log-messages-crash-report.patch +++ b/patches/server/0387-Deobfuscate-stacktraces-in-log-messages-crash-report.patch @@ -6,7 +6,7 @@ Subject: [PATCH] Deobfuscate stacktraces in log messages, crash reports, and diff --git a/build.gradle.kts b/build.gradle.kts -index 6acb1f37f4c5cb1addd835626041cd3c28eb842f..7398b673f416fa2c05231f90a59600517a21c908 100644 +index 3a801046ad63f90fda75b31fc2e26b07abfd8af9..95eaf83fb6bfac12aded7aff6a8f2012d59947dd 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -36,6 +36,7 @@ dependencies { @@ -17,7 +17,7 @@ index 6acb1f37f4c5cb1addd835626041cd3c28eb842f..7398b673f416fa2c05231f90a5960051 runtimeOnly("org.xerial:sqlite-jdbc:3.42.0.1") runtimeOnly("com.mysql:mysql-connector-j:8.2.0") runtimeOnly("com.lmax:disruptor:3.4.4") // Paper -@@ -125,6 +126,18 @@ tasks.check { +@@ -126,6 +127,18 @@ tasks.check { } // Paper end @@ -495,7 +495,7 @@ index 52eb3176437113f9a0ff85d10ce5c2415e1b5570..b54ddd0ba0b001fbcb1838a838ca4890 } } diff --git a/src/main/java/net/minecraft/network/Connection.java b/src/main/java/net/minecraft/network/Connection.java -index 1931db6936773657bd43b9b16de950cb3e7a2303..36a78cc103ddf1cc7ccddefc0b3fd6cef987f67d 100644 +index 93d3f29b08c79479c27d3f39e6c799c705e69902..38650109c1d2683a27da5788dc6013d8d0db26ad 100644 --- a/src/main/java/net/minecraft/network/Connection.java +++ b/src/main/java/net/minecraft/network/Connection.java @@ -75,13 +75,13 @@ public class Connection extends SimpleChannelInboundHandler> {