From a07d526698373844716c6f6a575cc77a687fda8f Mon Sep 17 00:00:00 2001 From: leguan <59799222+Leguan16@users.noreply.github.com> Date: Sat, 17 Feb 2024 20:15:15 +0100 Subject: [PATCH 1/8] [ci skip] Fix JavaDoc mistake in BlockPistonRetractEvent (#10250) --- patches/api/0056-Fix-upstream-javadocs.patch | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/patches/api/0056-Fix-upstream-javadocs.patch b/patches/api/0056-Fix-upstream-javadocs.patch index 4205834c36f1..e5282b07abd4 100644 --- a/patches/api/0056-Fix-upstream-javadocs.patch +++ b/patches/api/0056-Fix-upstream-javadocs.patch @@ -614,6 +614,19 @@ index 44f7f6939a27b9a0a796d91eac4b7c97ec90a643..641c71ab66bd2499b35cf3c1d533fd10 */ public class BlockExplodeEvent extends BlockEvent implements Cancellable { private static final HandlerList handlers = new HandlerList(); +diff --git a/src/main/java/org/bukkit/event/block/BlockPistonRetractEvent.java b/src/main/java/org/bukkit/event/block/BlockPistonRetractEvent.java +index 340fa397e68c024df380a28db21545a0c83d9fa6..79ac8a5db689cf9f8e2ff4cb7c06df6989128d10 100644 +--- a/src/main/java/org/bukkit/event/block/BlockPistonRetractEvent.java ++++ b/src/main/java/org/bukkit/event/block/BlockPistonRetractEvent.java +@@ -34,7 +34,7 @@ public class BlockPistonRetractEvent extends BlockPistonEvent { + + /** + * Get an immutable list of the blocks which will be moved by the +- * extending. ++ * retracting. + * + * @return Immutable list of the moved blocks. + */ diff --git a/src/main/java/org/bukkit/event/block/BlockPlaceEvent.java b/src/main/java/org/bukkit/event/block/BlockPlaceEvent.java index be0a2d1f234d8265d98e54e518a994957b1f3ab7..4e3c406ba883aae553e8d69b6b719b872cd6096c 100644 --- a/src/main/java/org/bukkit/event/block/BlockPlaceEvent.java From 9a66f2b2e1b4d888624fdde3a13273e7d3820d38 Mon Sep 17 00:00:00 2001 From: Emilia Kond Date: Sat, 17 Feb 2024 21:37:55 +0200 Subject: [PATCH 2/8] [ci skip] Fix javadoc mistake in PluginMeta (#10258) "Schematic versioning" is not a real thing. The intended versioning scheme is "semantic versioning". Introduced in #8108. --- patches/api/0009-Paper-Plugins.patch | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/patches/api/0009-Paper-Plugins.patch b/patches/api/0009-Paper-Plugins.patch index b4178cbec8b2..8a8150ca503c 100644 --- a/patches/api/0009-Paper-Plugins.patch +++ b/patches/api/0009-Paper-Plugins.patch @@ -329,7 +329,7 @@ index 0000000000000000000000000000000000000000..2c14693155de3654d5ca011c63e13e4a +} diff --git a/src/main/java/io/papermc/paper/plugin/configuration/PluginMeta.java b/src/main/java/io/papermc/paper/plugin/configuration/PluginMeta.java new file mode 100644 -index 0000000000000000000000000000000000000000..ef393f1f93ca48264fc1b6e3a27787f6a9152e1b +index 0000000000000000000000000000000000000000..bcf91d048d84144f6acf9bfd2095df9ada2e585f --- /dev/null +++ b/src/main/java/io/papermc/paper/plugin/configuration/PluginMeta.java @@ -0,0 +1,203 @@ @@ -403,7 +403,7 @@ index 0000000000000000000000000000000000000000..ef393f1f93ca48264fc1b6e3a27787f6 + /** + * Provides the version of this plugin as defined by the plugin. + * There is no inherit format defined/enforced for the version of a plugin, however a common approach -+ * might be schematic versioning. ++ * might be semantic versioning. + * + * @return the string representation of the plugin's version + */ From 1964b224398de7f51f56035420a33301d3ec027a Mon Sep 17 00:00:00 2001 From: David Mayr Date: Sat, 17 Feb 2024 21:22:00 +0100 Subject: [PATCH 3/8] Scoreboard objective number format api (#10036) * feat: number format api Signed-off-by: David Mayr * feat: make each individual score customizable Signed-off-by: David Mayr * docs: fix incorrect descriptions Signed-off-by: David Mayr * feat: use access transformers Signed-off-by: David Mayr * feat: use adventure codecs Signed-off-by: David Mayr * test: test for matching styles Signed-off-by: David Mayr * feat: convert number formats to interfaces Signed-off-by: David Mayr * feat: add style conversion to adventure patch Signed-off-by: David Mayr * feat: use paper adventure method in PaperScoreboardFormat Signed-off-by: David Mayr * chore: rename methods to avoid a method in records Signed-off-by: David Mayr * fix: check if objective is still registered Signed-off-by: David Mayr * feat: improve style conversion Signed-off-by: David Mayr * feat: modify how the getter behaves in score Signed-off-by: David Mayr * feat: use fluent naming Signed-off-by: David Mayr * docs: add spaces before the paper comments Signed-off-by: David Mayr * chore: styling changes Signed-off-by: David Mayr * chore: make constant final Signed-off-by: David Mayr * feat: add methods for styled format instead of constants Signed-off-by: David Mayr * fix: remove incorrect getTrackedPlayers check Signed-off-by: David Mayr * docs: add . at the end of sentences Signed-off-by: David Mayr * docs: explain null behaviour Signed-off-by: David Mayr * docs: mention score creation Signed-off-by: David Mayr * rebase and fix javadoc comments * remove server implementation defaults * fix format for PaperScoreboardFormat --------- Signed-off-by: David Mayr Co-authored-by: Jake Potrebic --- patches/api/0463-add-number-format-api.patch | 229 ++++++++++++++++++ patches/server/0010-Adventure.patch | 22 +- ...oleAppender-for-console-improvements.patch | 8 +- .../server/1046-add-number-format-api.patch | 138 +++++++++++ 4 files changed, 392 insertions(+), 5 deletions(-) create mode 100644 patches/api/0463-add-number-format-api.patch create mode 100644 patches/server/1046-add-number-format-api.patch diff --git a/patches/api/0463-add-number-format-api.patch b/patches/api/0463-add-number-format-api.patch new file mode 100644 index 000000000000..6d32302ad08c --- /dev/null +++ b/patches/api/0463-add-number-format-api.patch @@ -0,0 +1,229 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: David Mayr +Date: Sat, 16 Dec 2023 10:40:29 +0100 +Subject: [PATCH] add number format api + +Signed-off-by: David Mayr + +diff --git a/src/main/java/io/papermc/paper/scoreboard/numbers/BlankFormatImpl.java b/src/main/java/io/papermc/paper/scoreboard/numbers/BlankFormatImpl.java +new file mode 100644 +index 0000000000000000000000000000000000000000..486da6ebe0137bb3280e8b33c8e35e309507f118 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/scoreboard/numbers/BlankFormatImpl.java +@@ -0,0 +1,5 @@ ++package io.papermc.paper.scoreboard.numbers; ++ ++record BlankFormatImpl() implements NumberFormat { ++ public static final BlankFormatImpl INSTANCE = new BlankFormatImpl(); ++} +diff --git a/src/main/java/io/papermc/paper/scoreboard/numbers/FixedFormat.java b/src/main/java/io/papermc/paper/scoreboard/numbers/FixedFormat.java +new file mode 100644 +index 0000000000000000000000000000000000000000..66e0569789d523076cb571fb32be78ecff74305b +--- /dev/null ++++ b/src/main/java/io/papermc/paper/scoreboard/numbers/FixedFormat.java +@@ -0,0 +1,19 @@ ++package io.papermc.paper.scoreboard.numbers; ++ ++import net.kyori.adventure.text.Component; ++import net.kyori.adventure.text.ComponentLike; ++import org.jetbrains.annotations.NotNull; ++ ++/** ++ * A scoreboard number format that replaces the score number with a chat component. ++ */ ++public interface FixedFormat extends NumberFormat, ComponentLike { ++ ++ /** ++ * The component shown instead of the number for a score ++ * ++ * @return the chat component ++ */ ++ @NotNull Component component(); ++ ++} +diff --git a/src/main/java/io/papermc/paper/scoreboard/numbers/FixedFormatImpl.java b/src/main/java/io/papermc/paper/scoreboard/numbers/FixedFormatImpl.java +new file mode 100644 +index 0000000000000000000000000000000000000000..969bbfcdb68ffb5a207207e20e4d79621900c0f5 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/scoreboard/numbers/FixedFormatImpl.java +@@ -0,0 +1,12 @@ ++package io.papermc.paper.scoreboard.numbers; ++ ++import net.kyori.adventure.text.Component; ++import org.jetbrains.annotations.NotNull; ++ ++record FixedFormatImpl(@NotNull Component component) implements FixedFormat { ++ ++ @Override ++ public @NotNull Component asComponent() { ++ return this.component(); ++ } ++} +diff --git a/src/main/java/io/papermc/paper/scoreboard/numbers/NumberFormat.java b/src/main/java/io/papermc/paper/scoreboard/numbers/NumberFormat.java +new file mode 100644 +index 0000000000000000000000000000000000000000..eadf637f5fc582a2af5db71274ac1f01b2a28913 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/scoreboard/numbers/NumberFormat.java +@@ -0,0 +1,60 @@ ++package io.papermc.paper.scoreboard.numbers; ++ ++import net.kyori.adventure.text.ComponentLike; ++import net.kyori.adventure.text.format.Style; ++import net.kyori.adventure.text.format.StyleBuilderApplicable; ++import org.jetbrains.annotations.NotNull; ++ ++/** ++ * Describes a scoreboard number format that applies custom formatting to scoreboard scores. ++ */ ++public interface NumberFormat { ++ ++ /** ++ * Creates a blank scoreboard number format that removes the score number entirely. ++ * ++ * @return a blank number format ++ */ ++ static @NotNull NumberFormat blank() { ++ return BlankFormatImpl.INSTANCE; ++ } ++ ++ /** ++ * Gets an un-styled number format. ++ * ++ * @return an un-styled number format ++ */ ++ static @NotNull StyledFormat noStyle() { ++ return StyledFormatImpl.NO_STYLE; ++ } ++ ++ /** ++ * Creates a scoreboard number format that applies a custom formatting to the score number. ++ * ++ * @param style the style to apply on the number ++ * @return a styled number format ++ */ ++ static @NotNull StyledFormat styled(final @NotNull Style style) { ++ return new StyledFormatImpl(style); ++ } ++ ++ /** ++ * Creates a scoreboard number format that applies a custom formatting to the score number. ++ * ++ * @param styleBuilderApplicables the style to apply on the number ++ * @return a styled number format ++ */ ++ static @NotNull StyledFormat styled(final @NotNull StyleBuilderApplicable @NotNull... styleBuilderApplicables) { ++ return styled(Style.style(styleBuilderApplicables)); ++ } ++ ++ /** ++ * Creates a scoreboard number format that replaces the score number with a chat component. ++ * ++ * @param component the component to replace the number with ++ * @return a fixed number format ++ */ ++ static @NotNull FixedFormat fixed(final @NotNull ComponentLike component) { ++ return new FixedFormatImpl(component.asComponent()); ++ } ++} +diff --git a/src/main/java/io/papermc/paper/scoreboard/numbers/StyledFormat.java b/src/main/java/io/papermc/paper/scoreboard/numbers/StyledFormat.java +new file mode 100644 +index 0000000000000000000000000000000000000000..fe844677d689c3afe5ff2b706d562724e4121137 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/scoreboard/numbers/StyledFormat.java +@@ -0,0 +1,19 @@ ++package io.papermc.paper.scoreboard.numbers; ++ ++import net.kyori.adventure.text.format.Style; ++import net.kyori.adventure.text.format.StyleBuilderApplicable; ++import org.jetbrains.annotations.NotNull; ++ ++/** ++ * A scoreboard number format that applies a custom formatting to the score number. ++ */ ++public interface StyledFormat extends NumberFormat, StyleBuilderApplicable { ++ ++ /** ++ * The style that is being applied to the number in the score ++ * ++ * @return the style to apply ++ */ ++ @NotNull Style style(); ++ ++} +diff --git a/src/main/java/io/papermc/paper/scoreboard/numbers/StyledFormatImpl.java b/src/main/java/io/papermc/paper/scoreboard/numbers/StyledFormatImpl.java +new file mode 100644 +index 0000000000000000000000000000000000000000..0421e6c7cb32a912cf4aa281623c4311d5d1a34f +--- /dev/null ++++ b/src/main/java/io/papermc/paper/scoreboard/numbers/StyledFormatImpl.java +@@ -0,0 +1,13 @@ ++package io.papermc.paper.scoreboard.numbers; ++ ++import net.kyori.adventure.text.format.Style; ++import org.jetbrains.annotations.NotNull; ++ ++record StyledFormatImpl(@NotNull Style style) implements StyledFormat { ++ static final StyledFormat NO_STYLE = new StyledFormatImpl(Style.empty()); ++ ++ @Override ++ public void styleApply(final Style.@NotNull Builder style) { ++ style.merge(this.style); ++ } ++} +diff --git a/src/main/java/org/bukkit/scoreboard/Objective.java b/src/main/java/org/bukkit/scoreboard/Objective.java +index bd4d84cbf220ab02f09ece97873bbf0bdf7a45ba..1750f97d2122e6e597b9549df8f6fa74bf5e2e2d 100644 +--- a/src/main/java/org/bukkit/scoreboard/Objective.java ++++ b/src/main/java/org/bukkit/scoreboard/Objective.java +@@ -195,4 +195,22 @@ public interface Objective { + */ + void setAutoUpdateDisplay(boolean autoUpdateDisplay); + // Paper end - add more score API ++ ++ // Paper start - number format api ++ /** ++ * Gets the number format for this objective's scores or null if the client default is used. ++ * ++ * @return this objective's number format, or null if the client default is used ++ * @throws IllegalStateException if this objective has been unregistered ++ */ ++ @Nullable io.papermc.paper.scoreboard.numbers.NumberFormat numberFormat(); ++ ++ /** ++ * Sets the number format for this objective's scores. ++ * ++ * @param format the number format to set, pass null to reset format to default ++ * @throws IllegalStateException if this objective has been unregistered ++ */ ++ void numberFormat(@Nullable io.papermc.paper.scoreboard.numbers.NumberFormat format); ++ // Paper end - number format api + } +diff --git a/src/main/java/org/bukkit/scoreboard/Score.java b/src/main/java/org/bukkit/scoreboard/Score.java +index 5b6f243492d55d2db0d6944dc6daca9b181551d6..fba8e475c1f1a410c44a95fcc474cce19e0f515c 100644 +--- a/src/main/java/org/bukkit/scoreboard/Score.java ++++ b/src/main/java/org/bukkit/scoreboard/Score.java +@@ -129,4 +129,26 @@ public interface Score { + */ + void customName(net.kyori.adventure.text.@Nullable Component customName); + // Paper end - add more score API ++ ++ // Paper start - number format api ++ /** ++ * Gets the number format for this score or null if the score has not been set yet ++ * or the objective's default is being used. ++ * ++ * @return this score's number format, or null if the objective's default is used or the score doesn't exist ++ * @throws IllegalStateException if the associated objective has been ++ * unregistered ++ */ ++ @Nullable io.papermc.paper.scoreboard.numbers.NumberFormat numberFormat(); ++ ++ /** ++ * Sets the number format for this score. If this score has not been set yet {@link #isScoreSet()}, it will be created ++ * ++ * @param format the number format to set, pass null to reset format to default ++ * @throws IllegalStateException if the associated objective has been ++ * unregistered ++ */ ++ void numberFormat(@Nullable io.papermc.paper.scoreboard.numbers.NumberFormat format); ++ // Paper end - number format api ++ + } diff --git a/patches/server/0010-Adventure.patch b/patches/server/0010-Adventure.patch index 631f6c96d000..2ba9bdac09d0 100644 --- a/patches/server/0010-Adventure.patch +++ b/patches/server/0010-Adventure.patch @@ -1250,7 +1250,7 @@ new file mode 100644 index 0000000000000000000000000000000000000000..2e757cd9b01ac7eba1e4723a6e21dcea9d062483 --- /dev/null +++ b/src/main/java/io/papermc/paper/adventure/PaperAdventure.java -@@ -0,0 +1,401 @@ +@@ -0,0 +1,421 @@ +package io.papermc.paper.adventure; + +import com.mojang.brigadier.exceptions.CommandSyntaxException; @@ -1275,6 +1275,7 @@ index 0000000000000000000000000000000000000000..2e757cd9b01ac7eba1e4723a6e21dcea +import net.kyori.adventure.text.TranslatableComponent; +import net.kyori.adventure.text.TranslationArgument; +import net.kyori.adventure.text.flattener.ComponentFlattener; ++import net.kyori.adventure.text.format.Style; +import net.kyori.adventure.text.format.TextColor; +import net.kyori.adventure.text.serializer.ComponentSerializer; +import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; @@ -1285,6 +1286,7 @@ index 0000000000000000000000000000000000000000..2e757cd9b01ac7eba1e4723a6e21dcea +import net.kyori.adventure.translation.Translator; +import net.kyori.adventure.util.Codec; +import net.minecraft.ChatFormatting; ++import net.minecraft.Util; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.core.Holder; +import net.minecraft.core.registries.BuiltInRegistries; @@ -1651,6 +1653,24 @@ index 0000000000000000000000000000000000000000..2e757cd9b01ac7eba1e4723a6e21dcea + public static @Nullable ChatFormatting asVanilla(final TextColor color) { + return ChatFormatting.getByHexValue(color.value()); + } ++ ++ // Style ++ ++ public static net.minecraft.network.chat.Style asVanilla(Style style) { ++ Object encoded = Util.getOrThrow(AdventureCodecs.STYLE_MAP_CODEC.codec() ++ .encodeStart(net.minecraft.util.JavaOps.INSTANCE, style), IllegalStateException::new); ++ ++ return Util.getOrThrow(net.minecraft.network.chat.Style.Serializer.CODEC ++ .parse(net.minecraft.util.JavaOps.INSTANCE, encoded), IllegalStateException::new); ++ } ++ ++ public static Style asAdventure(net.minecraft.network.chat.Style style) { ++ Object encoded = Util.getOrThrow(net.minecraft.network.chat.Style.Serializer.CODEC ++ .encodeStart(net.minecraft.util.JavaOps.INSTANCE, style), IllegalStateException::new); ++ ++ return Util.getOrThrow(AdventureCodecs.STYLE_MAP_CODEC.codec() ++ .parse(net.minecraft.util.JavaOps.INSTANCE, encoded), IllegalStateException::new); ++ } +} 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 diff --git a/patches/server/0128-Use-TerminalConsoleAppender-for-console-improvements.patch b/patches/server/0128-Use-TerminalConsoleAppender-for-console-improvements.patch index be1dff00d079..125139cc3857 100644 --- a/patches/server/0128-Use-TerminalConsoleAppender-for-console-improvements.patch +++ b/patches/server/0128-Use-TerminalConsoleAppender-for-console-improvements.patch @@ -206,18 +206,18 @@ index 0000000000000000000000000000000000000000..8f07539a82f449ad217e316a7513a170 + +} diff --git a/src/main/java/io/papermc/paper/adventure/PaperAdventure.java b/src/main/java/io/papermc/paper/adventure/PaperAdventure.java -index 2e757cd9b01ac7eba1e4723a6e21dcea9d062483..ca80cbe422d766b3d044a5b53ce40bb7f92558e4 100644 +index 032d23ecda574ed1a3c740c16d13055f399bd6c4..cf5a4c142ed6d9ab4850373a9041bdc6af5a2a71 100644 --- a/src/main/java/io/papermc/paper/adventure/PaperAdventure.java +++ b/src/main/java/io/papermc/paper/adventure/PaperAdventure.java -@@ -24,6 +24,7 @@ import net.kyori.adventure.text.TranslationArgument; - import net.kyori.adventure.text.flattener.ComponentFlattener; +@@ -25,6 +25,7 @@ import net.kyori.adventure.text.flattener.ComponentFlattener; + import net.kyori.adventure.text.format.Style; import net.kyori.adventure.text.format.TextColor; import net.kyori.adventure.text.serializer.ComponentSerializer; +import net.kyori.adventure.text.serializer.ansi.ANSIComponentSerializer; import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; import net.kyori.adventure.text.serializer.plain.PlainComponentSerializer; import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; -@@ -111,6 +112,7 @@ public final class PaperAdventure { +@@ -113,6 +114,7 @@ public final class PaperAdventure { public static final AttributeKey LOCALE_ATTRIBUTE = AttributeKey.valueOf("adventure:locale"); // init after FLATTENER because classloading triggered here might create a logger @Deprecated public static final PlainComponentSerializer PLAIN = PlainComponentSerializer.builder().flattener(FLATTENER).build(); diff --git a/patches/server/1046-add-number-format-api.patch b/patches/server/1046-add-number-format-api.patch new file mode 100644 index 000000000000..d412b5f929a2 --- /dev/null +++ b/patches/server/1046-add-number-format-api.patch @@ -0,0 +1,138 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: David Mayr +Date: Sat, 16 Dec 2023 10:40:29 +0100 +Subject: [PATCH] add number format api + +== AT == +public net.minecraft.network.chat.numbers.FixedFormat value +public net.minecraft.network.chat.numbers.StyledFormat style + +diff --git a/src/main/java/io/papermc/paper/util/PaperScoreboardFormat.java b/src/main/java/io/papermc/paper/util/PaperScoreboardFormat.java +new file mode 100644 +index 0000000000000000000000000000000000000000..6064086cc76ef0df999c7057121d0ac22bd4df65 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/util/PaperScoreboardFormat.java +@@ -0,0 +1,38 @@ ++package io.papermc.paper.util; ++ ++import io.papermc.paper.adventure.PaperAdventure; ++import io.papermc.paper.scoreboard.numbers.NumberFormat; ++ ++public final class PaperScoreboardFormat { ++ ++ private PaperScoreboardFormat() { ++ } ++ ++ public static net.minecraft.network.chat.numbers.NumberFormat asVanilla(final NumberFormat format) { ++ final net.minecraft.network.chat.numbers.NumberFormat vanilla; ++ if (format instanceof final io.papermc.paper.scoreboard.numbers.StyledFormat styled) { ++ vanilla = new net.minecraft.network.chat.numbers.StyledFormat(PaperAdventure.asVanilla(styled.style())); ++ } else if (format instanceof final io.papermc.paper.scoreboard.numbers.FixedFormat fixed) { ++ vanilla = new net.minecraft.network.chat.numbers.FixedFormat(io.papermc.paper.adventure.PaperAdventure ++ .asVanilla(fixed.component())); ++ } else if (format.equals(NumberFormat.blank())) { ++ vanilla = net.minecraft.network.chat.numbers.BlankFormat.INSTANCE; ++ } else { ++ throw new IllegalArgumentException("Unknown format type " + format.getClass()); ++ } ++ ++ return vanilla; ++ } ++ ++ public static NumberFormat asPaper(final net.minecraft.network.chat.numbers.NumberFormat vanilla) { ++ if (vanilla instanceof final net.minecraft.network.chat.numbers.StyledFormat styled) { ++ return NumberFormat.styled(PaperAdventure.asAdventure(styled.style)); ++ } else if (vanilla instanceof final net.minecraft.network.chat.numbers.FixedFormat fixed) { ++ return NumberFormat.fixed(io.papermc.paper.adventure.PaperAdventure.asAdventure(fixed.value)); ++ } else if (vanilla instanceof net.minecraft.network.chat.numbers.BlankFormat) { ++ return NumberFormat.blank(); ++ } ++ ++ throw new IllegalArgumentException("Unknown format type " + vanilla.getClass()); ++ } ++} +diff --git a/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftObjective.java b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftObjective.java +index 2d3abf2a1da487ead74d698cc5ea4eb729c35c8d..1fec80c4f02aab3770c05bac8bfa2b622625e630 100644 +--- a/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftObjective.java ++++ b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftObjective.java +@@ -153,6 +153,34 @@ final class CraftObjective extends CraftScoreboardComponent implements Objective + } + // Paper end + ++ // Paper start - add number format ++ @Override ++ public io.papermc.paper.scoreboard.numbers.NumberFormat numberFormat() { ++ this.checkState(); ++ ++ net.minecraft.network.chat.numbers.NumberFormat vanilla = this.objective.numberFormat(); ++ ++ if (vanilla == null) { ++ return null; ++ } ++ ++ return io.papermc.paper.util.PaperScoreboardFormat.asPaper(vanilla); ++ } ++ ++ ++ @Override ++ public void numberFormat(io.papermc.paper.scoreboard.numbers.NumberFormat format) { ++ this.checkState(); ++ ++ if (format == null) { ++ this.objective.setNumberFormat(null); ++ return; ++ } ++ ++ this.objective.setNumberFormat(io.papermc.paper.util.PaperScoreboardFormat.asVanilla(format)); ++ } ++ // Paper end - add number format ++ + @Override + public void unregister() { + CraftScoreboard scoreboard = this.checkState(); +diff --git a/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScore.java b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScore.java +index 74d9c407e971804bed420370f7b684d8658eb5aa..e307e897d6e1ba4cb21883dfeaf334bfbf56cfc4 100644 +--- a/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScore.java ++++ b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScore.java +@@ -55,6 +55,41 @@ final class CraftScore implements Score { + this.objective.checkState().board.getOrCreatePlayerScore(this.entry, this.objective.getHandle()).set(score); + } + ++ ++ // Paper start - add number format ++ @Override ++ public io.papermc.paper.scoreboard.numbers.NumberFormat numberFormat() { ++ ReadOnlyScoreInfo scoreInfo = this.objective.checkState().board ++ .getPlayerScoreInfo(this.entry, this.objective.getHandle()); ++ ++ if (scoreInfo == null) { ++ return null; ++ } ++ ++ net.minecraft.network.chat.numbers.NumberFormat vanilla = scoreInfo.numberFormat(); ++ ++ if (vanilla == null) { ++ return null; ++ } ++ ++ return io.papermc.paper.util.PaperScoreboardFormat.asPaper(vanilla); ++ } ++ ++ ++ @Override ++ public void numberFormat(io.papermc.paper.scoreboard.numbers.NumberFormat format) { ++ final net.minecraft.world.scores.ScoreAccess access = this.objective.checkState() ++ .board.getOrCreatePlayerScore(this.entry, this.objective.getHandle()); ++ ++ if (format == null) { ++ access.numberFormatOverride(null); ++ return; ++ } ++ ++ access.numberFormatOverride(io.papermc.paper.util.PaperScoreboardFormat.asVanilla(format)); ++ } ++ // Paper end - add number format ++ + @Override + public boolean isScoreSet() { + Scoreboard board = this.objective.checkState().board; From d95341e44a8ba027af80547a44300617f314e548 Mon Sep 17 00:00:00 2001 From: viciscat <51047087+viciscat@users.noreply.github.com> Date: Sat, 17 Feb 2024 21:44:03 +0100 Subject: [PATCH 4/8] FluidState API (#9951) * Add new FluidState API functionality --------- Co-authored-by: Bjarne Koll Co-authored-by: Owen1212055 <23108066+Owen1212055@users.noreply.github.com> --- patches/api/0463-Add-FluidState-API.patch | 164 +++++++++++++++ patches/server/1046-Add-FluidState-API.patch | 208 +++++++++++++++++++ 2 files changed, 372 insertions(+) create mode 100644 patches/api/0463-Add-FluidState-API.patch create mode 100644 patches/server/1046-Add-FluidState-API.patch diff --git a/patches/api/0463-Add-FluidState-API.patch b/patches/api/0463-Add-FluidState-API.patch new file mode 100644 index 000000000000..3235c393f5f9 --- /dev/null +++ b/patches/api/0463-Add-FluidState-API.patch @@ -0,0 +1,164 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: vicisacat +Date: Fri, 17 Nov 2023 20:21:47 +0100 +Subject: [PATCH] Add FluidState API + + +diff --git a/src/main/java/io/papermc/paper/block/fluid/FluidData.java b/src/main/java/io/papermc/paper/block/fluid/FluidData.java +new file mode 100644 +index 0000000000000000000000000000000000000000..913acd58547d97cafc1466f6e2b3b4d22cf0b02d +--- /dev/null ++++ b/src/main/java/io/papermc/paper/block/fluid/FluidData.java +@@ -0,0 +1,68 @@ ++package io.papermc.paper.block.fluid; ++ ++import org.bukkit.Fluid; ++import org.bukkit.Location; ++import org.bukkit.util.Vector; ++import org.jetbrains.annotations.NotNull; ++import org.jetbrains.annotations.Range; ++ ++/** ++ * A representation of a fluid in a specific state of data. ++ * This type is not linked to a specific location and hence mostly resembles a {@link org.bukkit.block.data.BlockData}. ++ */ ++public interface FluidData extends Cloneable { ++ ++ /** ++ * Gets the fluid type of this fluid data. ++ * ++ * @return the fluid type ++ */ ++ @NotNull Fluid getFluidType(); ++ ++ /** ++ * Returns a copy of this FluidData. ++ * ++ * @return a copy of the fluid data ++ */ ++ @NotNull FluidData clone(); ++ ++ /** ++ * Computes the direction of the flow of the liquid at the given location as a vector. ++ *

++ * This method requires the passed location's chunk to be loaded. ++ * If said chunk is not loaded when this method is called, the chunk will first be loaded prior to the computation ++ * which leads to a potentially slow sync chunk load. ++ * ++ * @param location - the location to check the liquid flow ++ * @return the flow direction vector at the given location ++ */ ++ @NotNull Vector computeFlowDirection(@NotNull Location location); ++ ++ /** ++ * Returns the level of liquid this fluid data holds. ++ * ++ * @return the amount as an integer, between 0 and 8 ++ */ ++ @Range(from = 0, to = 8) ++ int getLevel(); ++ ++ /** ++ * Computes the height of the fluid in the world. ++ *

++ * This method requires the passed location's chunk to be loaded. ++ * If said chunk is not loaded when this method is called, the chunk will first be loaded prior to the computation ++ * which leads to a potentially slow sync chunk load. ++ * ++ * @param location the location at which to check the high of this fluid data. ++ * @return the height as a float value ++ */ ++ @Range(from = 0, to = 1) ++ float computeHeight(@NotNull Location location); ++ ++ /** ++ * Returns whether this fluid is a source block ++ * ++ * @return true if the fluid is a source block, false otherwise ++ */ ++ boolean isSource(); ++} +diff --git a/src/main/java/io/papermc/paper/block/fluid/type/FallingFluidData.java b/src/main/java/io/papermc/paper/block/fluid/type/FallingFluidData.java +new file mode 100644 +index 0000000000000000000000000000000000000000..7bd9f28ba646f09080b5c29b9d3be5af676c912e +--- /dev/null ++++ b/src/main/java/io/papermc/paper/block/fluid/type/FallingFluidData.java +@@ -0,0 +1,16 @@ ++package io.papermc.paper.block.fluid.type; ++ ++import io.papermc.paper.block.fluid.FluidData; ++ ++/** ++ * A specific subtype of {@link FluidData} that is returned by the API for fluid data of potentially falling fluids. ++ */ ++public interface FallingFluidData extends FluidData { ++ ++ /** ++ * Get if this liquid is falling. ++ * ++ * @return true if falling ++ */ ++ boolean isFalling(); ++} +diff --git a/src/main/java/io/papermc/paper/block/fluid/type/FlowingFluidData.java b/src/main/java/io/papermc/paper/block/fluid/type/FlowingFluidData.java +new file mode 100644 +index 0000000000000000000000000000000000000000..fbccdffe8d73e517204081c73bca9154f8c7d69f +--- /dev/null ++++ b/src/main/java/io/papermc/paper/block/fluid/type/FlowingFluidData.java +@@ -0,0 +1,10 @@ ++package io.papermc.paper.block.fluid.type; ++ ++import io.papermc.paper.block.fluid.FluidData; ++ ++/** ++ * A specific subtype of {@link FluidData} that is returned by the API for fluid data of potentially falling fluids. ++ */ ++public interface FlowingFluidData extends FallingFluidData { ++ ++} +diff --git a/src/main/java/org/bukkit/RegionAccessor.java b/src/main/java/org/bukkit/RegionAccessor.java +index 43dd6c59cceba12f27e6b265acc3ad97eea37abd..eb33e8e671972aa308ad75a7ce9aa9ac526f470f 100644 +--- a/src/main/java/org/bukkit/RegionAccessor.java ++++ b/src/main/java/org/bukkit/RegionAccessor.java +@@ -102,6 +102,41 @@ public interface RegionAccessor extends Keyed { // Paper + @NotNull + BlockState getBlockState(int x, int y, int z); + ++ // Paper start - FluidState API ++ /** ++ * Gets the {@link io.papermc.paper.block.fluid.FluidData} at the specified position. ++ * ++ * @param x The x-coordinate of the position ++ * @param y The y-coordinate of the position ++ * @param z The z-coordinate of the position ++ * @return The {@link io.papermc.paper.block.fluid.FluidData} at the specified position ++ */ ++ @NotNull ++ io.papermc.paper.block.fluid.FluidData getFluidData(int x, int y, int z); ++ ++ /** ++ * Gets the {@link io.papermc.paper.block.fluid.FluidData} at the given position ++ * ++ * @param position The position of the fluid ++ * @return The fluid data at the given position ++ */ ++ @NotNull ++ default io.papermc.paper.block.fluid.FluidData getFluidData(@NotNull io.papermc.paper.math.Position position) { ++ return getFluidData(position.blockX(), position.blockY(), position.blockZ()); ++ } ++ ++ /** ++ * Gets the {@link io.papermc.paper.block.fluid.FluidData} at the given position ++ * ++ * @param location The location of the fluid ++ * @return The fluid data at the given position ++ */ ++ @NotNull ++ default io.papermc.paper.block.fluid.FluidData getFluidData(@NotNull Location location) { ++ return getFluidData(location.blockX(), location.blockY(), location.blockZ()); ++ } ++ // Paper end ++ + /** + * Gets the {@link BlockData} at the given {@link Location}. + * diff --git a/patches/server/1046-Add-FluidState-API.patch b/patches/server/1046-Add-FluidState-API.patch new file mode 100644 index 000000000000..93e022238dbd --- /dev/null +++ b/patches/server/1046-Add-FluidState-API.patch @@ -0,0 +1,208 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: vicisacat +Date: Fri, 17 Nov 2023 20:22:43 +0100 +Subject: [PATCH] Add FluidState API + + +diff --git a/src/main/java/io/papermc/paper/block/fluid/PaperFluidData.java b/src/main/java/io/papermc/paper/block/fluid/PaperFluidData.java +new file mode 100644 +index 0000000000000000000000000000000000000000..479bc32241ebadf8bbc1080b601f61391ad37fa4 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/block/fluid/PaperFluidData.java +@@ -0,0 +1,110 @@ ++package io.papermc.paper.block.fluid; ++ ++import com.google.common.base.Preconditions; ++import io.papermc.paper.block.fluid.type.PaperFallingFluidData; ++import io.papermc.paper.block.fluid.type.PaperFlowingFluidData; ++import io.papermc.paper.util.MCUtil; ++import java.util.HashMap; ++import java.util.Map; ++import java.util.function.Function; ++import net.minecraft.world.level.material.FluidState; ++import net.minecraft.world.level.material.LavaFluid; ++import net.minecraft.world.level.material.WaterFluid; ++import org.bukkit.Fluid; ++import org.bukkit.Location; ++import org.bukkit.craftbukkit.CraftFluid; ++import org.bukkit.craftbukkit.CraftWorld; ++import org.bukkit.craftbukkit.util.CraftVector; ++import org.bukkit.util.Vector; ++import org.jetbrains.annotations.NotNull; ++ ++public class PaperFluidData implements FluidData { ++ ++ private final FluidState state; ++ ++ protected PaperFluidData(final FluidState state) { ++ this.state = state; ++ } ++ ++ /** ++ * Provides the internal server representation of this fluid data. ++ * @return the fluid state. ++ */ ++ public FluidState getState() { ++ return this.state; ++ } ++ ++ @Override ++ public final @NotNull Fluid getFluidType() { ++ return CraftFluid.minecraftToBukkit(this.state.getType()); ++ } ++ ++ @Override ++ public @NotNull PaperFluidData clone() { ++ try { ++ return (PaperFluidData) super.clone(); ++ } catch (final CloneNotSupportedException ex) { ++ throw new AssertionError("Clone not supported", ex); ++ } ++ } ++ ++ @Override ++ public @NotNull Vector computeFlowDirection(final Location location) { ++ Preconditions.checkArgument(location.getWorld() != null, "Cannot compute flow direction on world-less location"); ++ return CraftVector.toBukkit(this.state.getFlow( ++ ((CraftWorld) location.getWorld()).getHandle(), ++ MCUtil.toBlockPosition(location) ++ )); ++ } ++ ++ @Override ++ public int getLevel() { ++ return this.state.getAmount(); ++ } ++ ++ @Override ++ public float computeHeight(@NotNull final Location location) { ++ Preconditions.checkArgument(location.getWorld() != null, "Cannot compute height on world-less location"); ++ return this.state.getHeight(((CraftWorld) location.getWorld()).getHandle(), MCUtil.toBlockPos(location)); ++ } ++ ++ @Override ++ public boolean isSource() { ++ return this.state.isSource(); ++ } ++ ++ @Override ++ public int hashCode() { ++ return this.state.hashCode(); ++ } ++ ++ @Override ++ public boolean equals(final Object obj) { ++ return obj instanceof final PaperFluidData paperFluidData && this.state.equals(paperFluidData.state); ++ } ++ ++ @Override ++ public String toString() { ++ return "PaperFluidData{" + this.state + "}"; ++ } ++ ++ /* Registry */ ++ private static final Map, Function> MAP = new HashMap<>(); ++ static { ++ // ++ register(LavaFluid.Source.class, PaperFallingFluidData::new); ++ register(WaterFluid.Source.class, PaperFallingFluidData::new); ++ register(LavaFluid.Flowing.class, PaperFlowingFluidData::new); ++ register(WaterFluid.Flowing.class, PaperFlowingFluidData::new); ++ // ++ } ++ ++ static void register(final Class fluid, final Function creator) { ++ Preconditions.checkState(MAP.put(fluid, creator) == null, "Duplicate mapping %s->%s", fluid, creator); ++ MAP.put(fluid, creator); ++ } ++ ++ public static PaperFluidData createData(final FluidState state) { ++ return MAP.getOrDefault(state.getType().getClass(), PaperFluidData::new).apply(state); ++ } ++} +diff --git a/src/main/java/io/papermc/paper/block/fluid/package-info.java b/src/main/java/io/papermc/paper/block/fluid/package-info.java +new file mode 100644 +index 0000000000000000000000000000000000000000..cfabb814ebd281aab299c6c655266ff357e08806 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/block/fluid/package-info.java +@@ -0,0 +1,5 @@ ++@DefaultQualifier(NonNull.class) ++package io.papermc.paper.block.fluid; ++ ++import org.checkerframework.checker.nullness.qual.NonNull; ++import org.checkerframework.framework.qual.DefaultQualifier; +diff --git a/src/main/java/io/papermc/paper/block/fluid/type/PaperFallingFluidData.java b/src/main/java/io/papermc/paper/block/fluid/type/PaperFallingFluidData.java +new file mode 100644 +index 0000000000000000000000000000000000000000..655dbd83ff4e632f1168b75e9b402b05aa9d8edf +--- /dev/null ++++ b/src/main/java/io/papermc/paper/block/fluid/type/PaperFallingFluidData.java +@@ -0,0 +1,18 @@ ++ ++package io.papermc.paper.block.fluid.type; ++ ++import io.papermc.paper.block.fluid.PaperFluidData; ++import net.minecraft.world.level.material.FlowingFluid; ++import net.minecraft.world.level.material.FluidState; ++ ++public class PaperFallingFluidData extends PaperFluidData implements FallingFluidData { ++ ++ public PaperFallingFluidData(final FluidState state) { ++ super(state); ++ } ++ ++ @Override ++ public boolean isFalling() { ++ return this.getState().getValue(FlowingFluid.FALLING); ++ } ++} +diff --git a/src/main/java/io/papermc/paper/block/fluid/type/PaperFlowingFluidData.java b/src/main/java/io/papermc/paper/block/fluid/type/PaperFlowingFluidData.java +new file mode 100644 +index 0000000000000000000000000000000000000000..c0c2805cb045cdd835b402776a6923fe2ecc2a99 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/block/fluid/type/PaperFlowingFluidData.java +@@ -0,0 +1,11 @@ ++package io.papermc.paper.block.fluid.type; ++ ++import net.minecraft.world.level.material.FluidState; ++ ++public class PaperFlowingFluidData extends PaperFallingFluidData implements FlowingFluidData { ++ ++ public PaperFlowingFluidData(final FluidState state) { ++ super(state); ++ } ++ ++} +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftRegionAccessor.java b/src/main/java/org/bukkit/craftbukkit/CraftRegionAccessor.java +index 9dd6012556979514f9879f867138bc836c58ef3f..1df2202277ab58d7de844f7bebc07e494a0ecdf3 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftRegionAccessor.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftRegionAccessor.java +@@ -107,6 +107,13 @@ public abstract class CraftRegionAccessor implements RegionAccessor { + return CraftBlock.at(this.getHandle(), new BlockPos(x, y, z)).getState(); + } + ++ // Paper start - FluidState API ++ @Override ++ public io.papermc.paper.block.fluid.FluidData getFluidData(final int x, final int y, final int z) { ++ return io.papermc.paper.block.fluid.PaperFluidData.createData(getHandle().getFluidState(new BlockPos(x, y, z))); ++ } ++ // Paper end ++ + @Override + public BlockData getBlockData(Location location) { + return this.getBlockData(location.getBlockX(), location.getBlockY(), location.getBlockZ()); +diff --git a/src/main/java/org/bukkit/craftbukkit/generator/CraftLimitedRegion.java b/src/main/java/org/bukkit/craftbukkit/generator/CraftLimitedRegion.java +index ca9bb98ccfc3863c2ba538953470ab9e2b8a2f29..a2edef9739bad941f8e581da126bbfeac7cab5d8 100644 +--- a/src/main/java/org/bukkit/craftbukkit/generator/CraftLimitedRegion.java ++++ b/src/main/java/org/bukkit/craftbukkit/generator/CraftLimitedRegion.java +@@ -303,4 +303,11 @@ public class CraftLimitedRegion extends CraftRegionAccessor implements LimitedRe + return centerChunkZ; + } + // Paper end - Add more LimitedRegion API ++ // Paper start - Fluid API ++ @Override ++ public io.papermc.paper.block.fluid.FluidData getFluidData(int x, int y, int z) { ++ Preconditions.checkArgument(this.isInRegion(x, y, z), "Coordinates %s, %s, %s are not in the region", x, y, z); ++ return super.getFluidData(x, y, z); ++ } ++ // Paper end + } From 351923d17e3b5f2a2a3ed848c171d7d2b077a55a Mon Sep 17 00:00:00 2001 From: Jason Penilla <11360596+jpenilla@users.noreply.github.com> Date: Sat, 17 Feb 2024 14:58:56 -0700 Subject: [PATCH 5/8] Run round-trip adventure codec tests with JSON, NBT, and Java ops. Use JavaOps for conversions. (#10031) --- ...patch => 0464-add-number-format-api.patch} | 0 patches/server/0004-Test-changes.patch | 244 +++++++++++++++++- patches/server/0010-Adventure.patch | 127 +++++---- ...oleAppender-for-console-improvements.patch | 4 +- ...ktraces-in-log-messages-crash-report.patch | 4 +- ...patch => 1047-add-number-format-api.patch} | 0 6 files changed, 326 insertions(+), 53 deletions(-) rename patches/api/{0463-add-number-format-api.patch => 0464-add-number-format-api.patch} (100%) rename patches/server/{1046-add-number-format-api.patch => 1047-add-number-format-api.patch} (100%) diff --git a/patches/api/0463-add-number-format-api.patch b/patches/api/0464-add-number-format-api.patch similarity index 100% rename from patches/api/0463-add-number-format-api.patch rename to patches/api/0464-add-number-format-api.patch diff --git a/patches/server/0004-Test-changes.patch b/patches/server/0004-Test-changes.patch index 863fa7bf7ee3..63baf3d13847 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 e7fa464573909d4c3d649ebb5f40ef54055e09a8..2df1cae62cff433a7f3f55f561f70719bb6a745b 100644 +index e7fa464573909d4c3d649ebb5f40ef54055e09a8..8d2aa99b4bd0d1c46c66274907a1f11d605a75da 100644 --- a/build.gradle.kts +++ b/build.gradle.kts -@@ -57,6 +57,12 @@ tasks.compileJava { +@@ -22,6 +22,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 +@@ -57,6 +58,12 @@ tasks.compileJava { options.setIncremental(false) } @@ -97,6 +105,238 @@ 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..3f58ef36df34cd15fcb72189eeff057654adf0c6 +--- /dev/null ++++ b/src/test/java/io/papermc/paper/util/MethodParameterProvider.java +@@ -0,0 +1,206 @@ ++/* ++ * Copyright 2015-2023 the original author or authors of https://github.com/junit-team/junit5/blob/6593317c15fb556febbde11914fa7afe00abf8cd/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/MethodArgumentsProvider.java ++ * ++ * All rights reserved. This program and the accompanying materials are ++ * made available under the terms of the Eclipse Public License v2.0 which ++ * accompanies this distribution and is available at ++ * ++ * https://www.eclipse.org/legal/epl-v20.html ++ */ ++ ++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 b19d4f7d1fcb604b448a5084f6bfe56d47ab12b3..f3017525b0c2397fdc7ce0778add2e7b38e9e2ba 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 2ba9bdac09d0..4bf3f3be9918 100644 --- a/patches/server/0010-Adventure.patch +++ b/patches/server/0010-Adventure.patch @@ -1247,7 +1247,7 @@ index 0000000000000000000000000000000000000000..2fd6c3e65354071af71c7d8ebb97b559 +} diff --git a/src/main/java/io/papermc/paper/adventure/PaperAdventure.java b/src/main/java/io/papermc/paper/adventure/PaperAdventure.java new file mode 100644 -index 0000000000000000000000000000000000000000..2e757cd9b01ac7eba1e4723a6e21dcea9d062483 +index 0000000000000000000000000000000000000000..032d23ecda574ed1a3c740c16d13055f399bd6c4 --- /dev/null +++ b/src/main/java/io/papermc/paper/adventure/PaperAdventure.java @@ -0,0 +1,421 @@ @@ -1674,19 +1674,18 @@ index 0000000000000000000000000000000000000000..2e757cd9b01ac7eba1e4723a6e21dcea +} 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 @@ -1694,26 +1693,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(); + } @@ -5631,16 +5630,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..b97078552a86885bf77a2832d292f4062116c743 +index 0000000000000000000000000000000000000000..3ced2357b5604aac59a2aa57ded58898c7b78ed4 --- /dev/null +++ b/src/test/java/io/papermc/paper/adventure/AdventureCodecsTest.java -@@ -0,0 +1,369 @@ +@@ -0,0 +1,403 @@ +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; @@ -5663,6 +5669,7 @@ index 0000000000000000000000000000000000000000..b97078552a86885bf77a2832d292f406 +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; @@ -5671,6 +5678,7 @@ index 0000000000000000000000000000000000000000..b97078552a86885bf77a2832d292f406 +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; @@ -5704,6 +5712,8 @@ index 0000000000000000000000000000000000000000..b97078552a86885bf77a2832d292f406 + +class AdventureCodecsTest extends AbstractTestingBase { + ++ static final String PARAMETERIZED_NAME = "[{index}] {displayName}: {arguments}"; ++ + @Test + void testTextColor() { + final TextColor color = color(0x1d38df); @@ -5731,7 +5741,7 @@ index 0000000000000000000000000000000000000000..b97078552a86885bf77a2832d292f406 + 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)); @@ -5794,33 +5804,47 @@ index 0000000000000000000000000000000000000000..b97078552a86885bf77a2832d292f406 + 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); + } + -+ @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 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({"testTexts", "testTranslatables", "testKeybinds", "testScores", -+ "testSelectors", "testBlockNbts", "testEntityNbts", "testStorageNbts"}) -+ void testDirectRoundTripComponent(final Component component) { -+ testDirectRoundTrip(COMPONENT_CODEC, component); ++ @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 testMinecraftRoundTripComponent(final Component component) { -+ testMinecraftRoundTrip(COMPONENT_CODEC, ComponentSerialization.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 ++ static List> dynamicOps() { ++ return List.of( ++ NbtOps.INSTANCE, ++ JavaOps.INSTANCE, ++ JsonOps.INSTANCE ++ ); ++ } ++ ++ @ParameterizedTest(name = PARAMETERIZED_NAME) + @MethodSource({"invalidData"}) + void invalidThrows(final Tag input) { + assertThrows(RuntimeException.class, () -> { @@ -5831,33 +5855,33 @@ index 0000000000000000000000000000000000000000..b97078552a86885bf77a2832d292f406 + }); + } + -+ 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()); @@ -5902,6 +5926,15 @@ index 0000000000000000000000000000000000000000..b97078552a86885bf77a2832d292f406 + ); + } + ++ @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(), @@ -5938,7 +5971,7 @@ index 0000000000000000000000000000000000000000..b97078552a86885bf77a2832d292f406 + .key("thisIsA") + .fallback("This is a test.") + .build(), -+ translatable(key, numeric(5), text("HEY")), // boolean doesn't work in vanilla, can't test here ++ translatable(key, numeric(Integer.MAX_VALUE), text("HEY")), // boolean doesn't work in vanilla, can't test here + translatable( + key, + text().content(name) diff --git a/patches/server/0128-Use-TerminalConsoleAppender-for-console-improvements.patch b/patches/server/0128-Use-TerminalConsoleAppender-for-console-improvements.patch index 125139cc3857..6eb8e387d22d 100644 --- a/patches/server/0128-Use-TerminalConsoleAppender-for-console-improvements.patch +++ b/patches/server/0128-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 65e9d5918d46b123fb4f8122344a7d3863aec758..7a3c96318f95fcd6cf6fd94415958382d1193ec6 100644 +index e865c5ce514770f4fde9146b6e7138e88932c33b..d8f909f4f108343e3973e965af617596c91e3d0e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -6,9 +6,30 @@ plugins { @@ -60,7 +60,7 @@ index 65e9d5918d46b123fb4f8122344a7d3863aec758..7a3c96318f95fcd6cf6fd94415958382 implementation("org.apache.logging.log4j:log4j-iostreams:2.19.0") // Paper - remove exclusion implementation("org.ow2.asm:asm-commons:9.5") implementation("org.spongepowered:configurate-yaml:4.2.0-SNAPSHOT") // Paper - config files -@@ -78,7 +99,7 @@ relocation { +@@ -79,7 +100,7 @@ relocation { } tasks.shadowJar { diff --git a/patches/server/0366-Deobfuscate-stacktraces-in-log-messages-crash-report.patch b/patches/server/0366-Deobfuscate-stacktraces-in-log-messages-crash-report.patch index 8a44558d31c7..0ceb6d01a5e4 100644 --- a/patches/server/0366-Deobfuscate-stacktraces-in-log-messages-crash-report.patch +++ b/patches/server/0366-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 eaaf9a9779f57ee048245899750bf7a1599b716f..450f7c03bdcc109938ba9b66328bdbb2c96c03c9 100644 +index 1a0d26d4604b10fd85797a774a2d159cbd237d7e..76ae7fd188adc1c48d4a58253824e0ff75887339 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -35,6 +35,7 @@ dependencies { @@ -17,7 +17,7 @@ index eaaf9a9779f57ee048245899750bf7a1599b716f..450f7c03bdcc109938ba9b66328bdbb2 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 -@@ -124,6 +125,18 @@ tasks.check { +@@ -125,6 +126,18 @@ tasks.check { } // Paper end diff --git a/patches/server/1046-add-number-format-api.patch b/patches/server/1047-add-number-format-api.patch similarity index 100% rename from patches/server/1046-add-number-format-api.patch rename to patches/server/1047-add-number-format-api.patch From 4939f8711884901ddf1c56337f606de71cdae78d Mon Sep 17 00:00:00 2001 From: Nassim Jahnke Date: Sun, 18 Feb 2024 12:53:27 +0100 Subject: [PATCH 6/8] Add more ResourceLocation checks, some cleanup --- ...able-API-and-replenishable-lootables.patch | 4 +- ...CanPlaceOn-and-CanDestroy-NBT-values.patch | 31 ++++----- ...low-bees-to-load-chunks-for-beehives.patch | 16 +++++ ...date-ResourceLocation-in-NBT-reading.patch | 33 ++++++++- .../1039-Improve-tag-parser-handling.patch | 68 +++++++++++++------ 5 files changed, 110 insertions(+), 42 deletions(-) diff --git a/patches/server/0092-LootTable-API-and-replenishable-lootables.patch b/patches/server/0092-LootTable-API-and-replenishable-lootables.patch index 2f441da2cc34..2c728e93f80b 100644 --- a/patches/server/0092-LootTable-API-and-replenishable-lootables.patch +++ b/patches/server/0092-LootTable-API-and-replenishable-lootables.patch @@ -654,7 +654,7 @@ index e0fbacd574e0c83c2e1d164ded8e9ccf4af30480..7529751afa2932fd16bc4591189b0358 + // Paper end } diff --git a/src/main/java/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java -index aa4181e59f88be04a3605352fa5ceb3e04149dd3..7cbd403f9e96e7ce35475c8102cd9f9c04819c27 100644 +index aa4181e59f88be04a3605352fa5ceb3e04149dd3..e4e827a57c2913c719282cc0d5da33586607677b 100644 --- a/src/main/java/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java +++ b/src/main/java/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java @@ -17,6 +17,7 @@ public abstract class RandomizableContainerBlockEntity extends BaseContainerBloc @@ -675,7 +675,7 @@ index aa4181e59f88be04a3605352fa5ceb3e04149dd3..7cbd403f9e96e7ce35475c8102cd9f9c + // Copied from super with changes, always check the original method + this.lootableData.loadNbt(nbt); // Paper + if (nbt.contains("LootTable", 8)) { -+ this.setLootTable(new ResourceLocation(nbt.getString("LootTable"))); ++ this.setLootTable(ResourceLocation.tryParse(nbt.getString("LootTable"))); + try { org.bukkit.craftbukkit.util.CraftNamespacedKey.fromMinecraft(this.lootTable); } catch (IllegalArgumentException ex) { this.lootTable = null; } // Paper - validate + this.setLootTableSeed(nbt.getLong("LootTableSeed")); + return false; // Paper - always load the items, table may still remain diff --git a/patches/server/0248-Add-API-for-CanPlaceOn-and-CanDestroy-NBT-values.patch b/patches/server/0248-Add-API-for-CanPlaceOn-and-CanDestroy-NBT-values.patch index 61c1bd0e6e4b..f179b48bd74e 100644 --- a/patches/server/0248-Add-API-for-CanPlaceOn-and-CanDestroy-NBT-values.patch +++ b/patches/server/0248-Add-API-for-CanPlaceOn-and-CanDestroy-NBT-values.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Add API for CanPlaceOn and CanDestroy NBT values diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java -index 1920cf7ad846f57cd278cb9a72dce03f3d014fbb..629fa76e6c7c2ede36ab855bb3a7a65dfd601449 100644 +index 1920cf7ad846f57cd278cb9a72dce03f3d014fbb..7cf1153ae532a9d53ee85b05f77ed74b94cf5fbc 100644 --- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java +++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java @@ -85,6 +85,12 @@ import org.bukkit.persistence.PersistentDataContainer; @@ -67,7 +67,7 @@ index 1920cf7ad846f57cd278cb9a72dce03f3d014fbb..629fa76e6c7c2ede36ab855bb3a7a65d + if (tag.contains(CAN_DESTROY.NBT)) { + ListTag list = tag.getList(CAN_DESTROY.NBT, CraftMagicNumbers.NBT.TAG_STRING); + for (int i = 0; i < list.size(); i++) { -+ Namespaced namespaced = this.deserializeNamespaced(list.getString(i)); ++ Namespaced namespaced = this.blockKeyFromString(list.getString(i)); + if (namespaced == null) { + continue; + } @@ -79,7 +79,7 @@ index 1920cf7ad846f57cd278cb9a72dce03f3d014fbb..629fa76e6c7c2ede36ab855bb3a7a65d + if (tag.contains(CAN_PLACE_ON.NBT)) { + ListTag list = tag.getList(CAN_PLACE_ON.NBT, CraftMagicNumbers.NBT.TAG_STRING); + for (int i = 0; i < list.size(); i++) { -+ Namespaced namespaced = this.deserializeNamespaced(list.getString(i)); ++ Namespaced namespaced = this.blockKeyFromString(list.getString(i)); + if (namespaced == null) { + continue; + } @@ -100,7 +100,7 @@ index 1920cf7ad846f57cd278cb9a72dce03f3d014fbb..629fa76e6c7c2ede36ab855bb3a7a65d + if (canPlaceOnSerialized != null) { + for (Object canPlaceOnElement : canPlaceOnSerialized) { + String canPlaceOnRaw = (String) canPlaceOnElement; -+ Namespaced value = this.deserializeNamespaced(canPlaceOnRaw); ++ Namespaced value = this.blockKeyFromString(canPlaceOnRaw); + if (value == null) { + continue; + } @@ -113,7 +113,7 @@ index 1920cf7ad846f57cd278cb9a72dce03f3d014fbb..629fa76e6c7c2ede36ab855bb3a7a65d + if (canDestroySerialized != null) { + for (Object canDestroyElement : canDestroySerialized) { + String canDestroyRaw = (String) canDestroyElement; -+ Namespaced value = this.deserializeNamespaced(canDestroyRaw); ++ Namespaced value = this.blockKeyFromString(canDestroyRaw); + if (value == null) { + continue; + } @@ -253,7 +253,7 @@ index 1920cf7ad846f57cd278cb9a72dce03f3d014fbb..629fa76e6c7c2ede36ab855bb3a7a65d // Paper end CraftMetaCompass.LODESTONE_DIMENSION.NBT, CraftMetaCompass.LODESTONE_POS.NBT, -@@ -1545,4 +1688,146 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta { +@@ -1545,4 +1688,141 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta { } // Paper end @@ -347,8 +347,8 @@ index 1920cf7ad846f57cd278cb9a72dce03f3d014fbb..629fa76e6c7c2ede36ab855bb3a7a65d + return mats; + } + -+ private @Nullable Namespaced deserializeNamespaced(String raw) { -+ boolean isTag = raw.length() > 0 && raw.codePointAt(0) == '#'; ++ private @Nullable Namespaced blockKeyFromString(String raw) { ++ boolean isTag = !raw.isEmpty() && raw.codePointAt(0) == '#'; + com.mojang.datafixers.util.Either result; + try { + result = net.minecraft.commands.arguments.blocks.BlockStateParser.parseForTesting(net.minecraft.core.registries.BuiltInRegistries.BLOCK.asLookup(), raw, false); @@ -367,20 +367,15 @@ index 1920cf7ad846f57cd278cb9a72dce03f3d014fbb..629fa76e6c7c2ede36ab855bb3a7a65d + return null; + } + -+ // don't DC the player if something slips through somehow -+ Namespaced resource = null; + try { + if (isTag) { -+ resource = new NamespacedTag(key.getNamespace(), key.getPath()); -+ } else { -+ resource = CraftNamespacedKey.fromMinecraft(key); ++ return new NamespacedTag(key.getNamespace(), key.getPath()); ++ + } -+ } catch (IllegalArgumentException ex) { -+ org.bukkit.Bukkit.getLogger().warning("Namespaced resource does not validate: " + key.toString()); -+ ex.printStackTrace(); ++ return CraftNamespacedKey.fromMinecraft(key); ++ } catch (IllegalArgumentException ignored) { ++ return null; + } -+ -+ return resource; + } + + private @Nonnull String serializeNamespaced(Namespaced resource) { diff --git a/patches/server/0342-Do-not-allow-bees-to-load-chunks-for-beehives.patch b/patches/server/0342-Do-not-allow-bees-to-load-chunks-for-beehives.patch index 9c629a712d06..79bb156283d9 100644 --- a/patches/server/0342-Do-not-allow-bees-to-load-chunks-for-beehives.patch +++ b/patches/server/0342-Do-not-allow-bees-to-load-chunks-for-beehives.patch @@ -40,3 +40,19 @@ index 06e990b6957a4ef48c8778bbd175d3afddf52ca3..07ecc038a1000581335b8d18c094298f BlockEntity tileentity = Bee.this.level().getBlockEntity(Bee.this.hivePos); if (tileentity instanceof BeehiveBlockEntity) { +diff --git a/src/main/java/net/minecraft/world/entity/monster/Vex.java b/src/main/java/net/minecraft/world/entity/monster/Vex.java +index 30ea3f64234fd1fda8dada3c7fb12be0730322a8..f443006c1e32feee97b32312814e2447a50c45e2 100644 +--- a/src/main/java/net/minecraft/world/entity/monster/Vex.java ++++ b/src/main/java/net/minecraft/world/entity/monster/Vex.java +@@ -377,7 +377,10 @@ public class Vex extends Monster implements TraceableEntity { + for (int i = 0; i < 3; ++i) { + BlockPos blockposition1 = blockposition.offset(Vex.this.random.nextInt(15) - 7, Vex.this.random.nextInt(11) - 5, Vex.this.random.nextInt(15) - 7); + +- if (Vex.this.level().isEmptyBlock(blockposition1)) { ++ // Paper start - Don't load chunks ++ final net.minecraft.world.level.block.state.BlockState blockState = Vex.this.level().getBlockStateIfLoaded(blockposition1); ++ if (blockState != null && blockState.isAir()) { ++ // Paper end - Don't load chunks + Vex.this.moveControl.setWantedPosition((double) blockposition1.getX() + 0.5D, (double) blockposition1.getY() + 0.5D, (double) blockposition1.getZ() + 0.5D, 0.25D); + if (Vex.this.getTarget() == null) { + Vex.this.getLookControl().setLookAt((double) blockposition1.getX() + 0.5D, (double) blockposition1.getY() + 0.5D, (double) blockposition1.getZ() + 0.5D, 180.0F, 20.0F); diff --git a/patches/server/0978-Validate-ResourceLocation-in-NBT-reading.patch b/patches/server/0978-Validate-ResourceLocation-in-NBT-reading.patch index 2f94b7ad0323..eab18ce8864a 100644 --- a/patches/server/0978-Validate-ResourceLocation-in-NBT-reading.patch +++ b/patches/server/0978-Validate-ResourceLocation-in-NBT-reading.patch @@ -53,7 +53,7 @@ index 8ba573bb4099ee5b27b61f333e72d794c48d5f29..69bdf3f2ee731e59e8d454816a9ca72c @Nullable diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java -index 4591011a142f33f0c0ff84a2765cededde0e0c57..c0bfce7266bbdfe0c5a753367032eb333f56c182 100644 +index 6502dfa62fb0e5a0e3369c62c6ad97aecced4d2b..484056cf77334818aab98ad20e99685d1e240adc 100644 --- a/src/main/java/net/minecraft/world/entity/LivingEntity.java +++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java @@ -871,12 +871,13 @@ public abstract class LivingEntity extends Entity implements Attackable { @@ -110,3 +110,34 @@ index 7529751afa2932fd16bc4591189b0358268a7b14..e2e1c7a017e82dc7299e5cd1783818e4 this.setLootTableSeed(nbt.getLong("LootTableSeed")); } +diff --git a/src/main/java/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java +index d04fc84eef11adb5ea64077f48794b6ed7fb3ada..89d06253b00604114e543ebbe12a9993ae95dc41 100644 +--- a/src/main/java/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java ++++ b/src/main/java/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java +@@ -290,7 +290,12 @@ public abstract class AbstractFurnaceBlockEntity extends BaseContainerBlockEntit + while (iterator.hasNext()) { + String s = (String) iterator.next(); + +- this.recipesUsed.put(new ResourceLocation(s), nbttagcompound1.getInt(s)); ++ // Paper start - Validate ResourceLocation ++ final ResourceLocation resourceLocation = ResourceLocation.tryParse(s); ++ if (resourceLocation != null) { ++ this.recipesUsed.put(resourceLocation, nbttagcompound1.getInt(s)); ++ } ++ // Paper end - Validate ResourceLocation + } + + // Paper start - cook speed multiplier API +diff --git a/src/main/java/net/minecraft/world/level/block/entity/BrushableBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/BrushableBlockEntity.java +index b5b1831631e233a96b6fd55972a8862b0f420da8..3f62e823bf9b5aa696e3c240613a0fb50340875e 100644 +--- a/src/main/java/net/minecraft/world/level/block/entity/BrushableBlockEntity.java ++++ b/src/main/java/net/minecraft/world/level/block/entity/BrushableBlockEntity.java +@@ -199,7 +199,7 @@ public class BrushableBlockEntity extends BlockEntity { + + private boolean tryLoadLootTable(CompoundTag nbt) { + if (nbt.contains("LootTable", 8)) { +- this.lootTable = new ResourceLocation(nbt.getString("LootTable")); ++ this.lootTable = ResourceLocation.tryParse(nbt.getString("LootTable")); // Paper - Validate ResourceLocation + this.lootTableSeed = nbt.getLong("LootTableSeed"); + return true; + } else { diff --git a/patches/server/1039-Improve-tag-parser-handling.patch b/patches/server/1039-Improve-tag-parser-handling.patch index 2dabc4807825..e57cd51b7b4f 100644 --- a/patches/server/1039-Improve-tag-parser-handling.patch +++ b/patches/server/1039-Improve-tag-parser-handling.patch @@ -5,22 +5,54 @@ Subject: [PATCH] Improve tag parser handling diff --git a/src/main/java/com/mojang/brigadier/CommandDispatcher.java b/src/main/java/com/mojang/brigadier/CommandDispatcher.java -index 92848b64a78fce7a92e1657c2da6fc5ee53eea44..5d0e8f4f3ad61a27452675277380e27d3d28d133 100644 +index 92848b64a78fce7a92e1657c2da6fc5ee53eea44..5d5562676a77259b875e15b744b53258533851a7 100644 --- a/src/main/java/com/mojang/brigadier/CommandDispatcher.java +++ b/src/main/java/com/mojang/brigadier/CommandDispatcher.java -@@ -307,6 +307,10 @@ public class CommandDispatcher { +@@ -304,9 +304,15 @@ public class CommandDispatcher { + } + final CommandContextBuilder context = contextSoFar.copy(); + final StringReader reader = new StringReader(originalReader); ++ boolean stop = false; // Paper - Handle non-recoverable exceptions try { try { child.parse(reader, context); -+ // Paper start - Handle non-reoverable exceptions; Rethrow NbtAccounterException so it can be caught properly and immediately -+ } catch (final net.minecraft.nbt.NbtAccounterException e) { ++ // Paper start - Handle non-recoverable exceptions ++ } catch (final com.mojang.brigadier.exceptions.TagParseCommandSyntaxException e) { ++ stop = true; + throw e; -+ // Paper end - Handle non-reoverable exceptions ++ // Paper end - Handle non-recoverable exceptions } catch (final RuntimeException ex) { throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherParseException().createWithContext(reader, ex.getMessage()); } +@@ -321,6 +327,7 @@ public class CommandDispatcher { + } + errors.put(child, ex); + reader.setCursor(cursor); ++ if (stop) return new ParseResults<>(contextSoFar, originalReader, errors); // Paper - Handle non-recoverable exceptions + continue; + } + +diff --git a/src/main/java/com/mojang/brigadier/exceptions/TagParseCommandSyntaxException.java b/src/main/java/com/mojang/brigadier/exceptions/TagParseCommandSyntaxException.java +new file mode 100644 +index 0000000000000000000000000000000000000000..bf248a215dc69bb303c836112309471aab687e23 +--- /dev/null ++++ b/src/main/java/com/mojang/brigadier/exceptions/TagParseCommandSyntaxException.java +@@ -0,0 +1,13 @@ ++package com.mojang.brigadier.exceptions; ++ ++import com.mojang.brigadier.LiteralMessage; ++import net.minecraft.network.chat.Component; ++ ++public final class TagParseCommandSyntaxException extends CommandSyntaxException { ++ ++ private static final SimpleCommandExceptionType EXCEPTION_TYPE = new SimpleCommandExceptionType(new LiteralMessage("Error parsing NBT")); ++ ++ public TagParseCommandSyntaxException(final String message) { ++ super(EXCEPTION_TYPE, Component.literal(message)); ++ } ++} diff --git a/src/main/java/net/minecraft/nbt/TagParser.java b/src/main/java/net/minecraft/nbt/TagParser.java -index 5bec54239a2b185284c10d58854e5a13e33daae5..9ecd0b7ddaa8376f3c1448f810f7757c9ba1b90a 100644 +index 5bec54239a2b185284c10d58854e5a13e33daae5..94cb73e7f60171aa57bd1dbe7e91ef4db94e70b7 100644 --- a/src/main/java/net/minecraft/nbt/TagParser.java +++ b/src/main/java/net/minecraft/nbt/TagParser.java @@ -48,6 +48,7 @@ public class TagParser { @@ -83,10 +115,10 @@ index 5bec54239a2b185284c10d58854e5a13e33daae5..9ecd0b7ddaa8376f3c1448f810f7757c this.reader.expect(c); } + -+ private void increaseDepth() { ++ private void increaseDepth() throws CommandSyntaxException { + this.depth++; + if (this.depth > 512) { -+ throw new net.minecraft.nbt.NbtAccounterException("NBT tag is too complex, depth > 512"); ++ throw new com.mojang.brigadier.exceptions.TagParseCommandSyntaxException("NBT tag is too complex, depth > 512"); + } + } } @@ -104,7 +136,7 @@ index a5e438a834826161c52ca9db57d234d9ff80a591..4766994cce060564370b0d24836a7da8 @Override diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -index 2f9c62f2c4c4356a896f7004b77f12a595f9c6dc..6d943b5c38932de5fc3abb28618bf518c36edb47 100644 +index 2f9c62f2c4c4356a896f7004b77f12a595f9c6dc..ab5b259d8f72022c875cae73be25fe2da346c6b3 100644 --- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java @@ -777,6 +777,13 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl @@ -121,23 +153,17 @@ index 2f9c62f2c4c4356a896f7004b77f12a595f9c6dc..6d943b5c38932de5fc3abb28618bf518 // Paper start - AsyncTabCompleteEvent TAB_COMPLETE_EXECUTOR.execute(() -> this.handleCustomCommandSuggestions0(packet)); } -@@ -823,7 +830,18 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl - +@@ -824,6 +831,13 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl private void sendServerSuggestions(final ServerboundCommandSuggestionPacket packet, final StringReader stringreader) { // Paper end - AsyncTabCompleteEvent -- ParseResults parseresults = this.server.getCommands().getDispatcher().parse(stringreader, this.player.createCommandSourceStack()); -+ // Paper start - Handle non-reoverable exceptions -+ ParseResults parseresults; -+ try { -+ parseresults = this.server.getCommands().getDispatcher().parse(stringreader, this.player.createCommandSourceStack()); -+ } catch (final Throwable e) { // This is fine:tm: -+ if (LOGGER.isDebugEnabled()) { -+ LOGGER.error("Exception parsing command", e); -+ } + ParseResults parseresults = this.server.getCommands().getDispatcher().parse(stringreader, this.player.createCommandSourceStack()); ++ // Paper start - Handle non-recoverable exceptions ++ if (!parseresults.getExceptions().isEmpty() ++ && parseresults.getExceptions().values().stream().anyMatch(e -> e instanceof com.mojang.brigadier.exceptions.TagParseCommandSyntaxException)) { + this.disconnect(Component.translatable("disconnect.spam"), org.bukkit.event.player.PlayerKickEvent.Cause.SPAM); + return; + } -+ // Paper end - Handle non-reoverable exceptions ++ // Paper end - Handle non-recoverable exceptions this.server.getCommands().getDispatcher().getCompletionSuggestions(parseresults).thenAccept((suggestions) -> { // Paper start - Don't tab-complete namespaced commands if send-namespaced is false From ec67950bb984db5a152652201e68cc55274f1cde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20St=C3=A4ding?= Date: Tue, 20 Feb 2024 01:19:53 +0100 Subject: [PATCH 7/8] Add teamcity configuration --- .teamcity/pom.xml | 104 +++++++++++++++++++++++++++++++++++++++++ .teamcity/settings.kts | 100 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 204 insertions(+) create mode 100644 .teamcity/pom.xml create mode 100644 .teamcity/settings.kts diff --git a/.teamcity/pom.xml b/.teamcity/pom.xml new file mode 100644 index 000000000000..3afb790e77d0 --- /dev/null +++ b/.teamcity/pom.xml @@ -0,0 +1,104 @@ + + + 4.0.0 + Paper Config DSL Script + paper + paper_dsl + 1.0-SNAPSHOT + + + org.jetbrains.teamcity + configs-dsl-kotlin-parent + 1.0-SNAPSHOT + + + + + jetbrains-all + https://download.jetbrains.com/teamcity-repository + + true + + + + teamcity-server + https://ci.anvilpowered.org/app/dsl-plugins-repository + + true + + + + + + + JetBrains + https://download.jetbrains.com/teamcity-repository + + + + + ${basedir} + + + kotlin-maven-plugin + org.jetbrains.kotlin + ${kotlin.version} + + + + + compile + process-sources + + compile + + + + test-compile + process-test-sources + + test-compile + + + + + + org.jetbrains.teamcity + teamcity-configs-maven-plugin + ${teamcity.dsl.version} + + kotlin + target/generated-configs + + + + + + + + org.jetbrains.teamcity + configs-dsl-kotlin-latest + ${teamcity.dsl.version} + compile + + + org.jetbrains.teamcity + configs-dsl-kotlin-plugins-latest + 1.0-SNAPSHOT + pom + compile + + + org.jetbrains.kotlin + kotlin-stdlib-jdk8 + ${kotlin.version} + compile + + + org.jetbrains.kotlin + kotlin-script-runtime + ${kotlin.version} + compile + + + diff --git a/.teamcity/settings.kts b/.teamcity/settings.kts new file mode 100644 index 000000000000..387cd9faf14d --- /dev/null +++ b/.teamcity/settings.kts @@ -0,0 +1,100 @@ +import jetbrains.buildServer.configs.kotlin.BuildFeatures +import jetbrains.buildServer.configs.kotlin.BuildType +import jetbrains.buildServer.configs.kotlin.DslContext +import jetbrains.buildServer.configs.kotlin.FailureAction +import jetbrains.buildServer.configs.kotlin.buildFeatures.PullRequests +import jetbrains.buildServer.configs.kotlin.buildFeatures.commitStatusPublisher +import jetbrains.buildServer.configs.kotlin.buildFeatures.perfmon +import jetbrains.buildServer.configs.kotlin.buildFeatures.pullRequests +import jetbrains.buildServer.configs.kotlin.buildSteps.gradle +import jetbrains.buildServer.configs.kotlin.project +import jetbrains.buildServer.configs.kotlin.projectFeatures.githubIssues +import jetbrains.buildServer.configs.kotlin.triggers.vcs +import jetbrains.buildServer.configs.kotlin.version + +/* +The settings script is an entry point for defining a TeamCity +project hierarchy. The script should contain a single call to the +project() function with a Project instance or an init function as +an argument. + +VcsRoots, BuildTypes, Templates, and subprojects can be +registered inside the project using the vcsRoot(), buildType(), +template(), and subProject() methods respectively. + +To debug settings scripts in command-line, run the + + mvnDebug org.jetbrains.teamcity:teamcity-configs-maven-plugin:generate + +command and attach your debugger to the port 8000. + +To debug in IntelliJ Idea, open the 'Maven Projects' tool window (View +-> Tool Windows -> Maven Projects), find the generate task node +(Plugins -> teamcity-configs -> teamcity-configs:generate), the +'Debug' option is available in the context menu for the task. +*/ + +version = "2023.11" + +project { + + val publish = Publish() + + buildType(publish) + + features { + githubIssues { + id = "PROJECT_EXT_3" + displayName = "papermc/paper" + repositoryURL = "https://github.com/anvilpowered/paper" + authType = accessToken { + accessToken = "credentialsJSON:f57a4fdd-fb30-41c0-9983-620364336d03" + } + param("tokenId", "") + } + } +} + +fun BuildType.configureVcs() { + vcs { + root(DslContext.settingsRoot) + } +} + +fun BuildFeatures.configureBaseFeatures() { + perfmon {} + commitStatusPublisher { + vcsRootExtId = "${DslContext.settingsRoot.id}" + publisher = github { + githubUrl = "https://api.github.com" + authType = personalToken { + token = "credentialsJSON:f57a4fdd-fb30-41c0-9983-620364336d03" + } + } + } +} + +class Publish : BuildType() { + init { + name = "publish" + + configureVcs() + features { + configureBaseFeatures() + } + + requirements { + contains("env.AGENT_NAME", "publishing") + } + + steps { + gradle { + tasks = "applyPatches" + gradleParams = "--refresh-dependencies" + } + gradle { + tasks = "publish" + } + } + } +} From f587a19b1b298ed46c5ff82c7e115b3919f63719 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20St=C3=A4ding?= Date: Sat, 9 Dec 2023 21:48:46 +0100 Subject: [PATCH 8/8] Update publishing repo and version --- build.gradle.kts | 2 +- gradle.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index ed586bbaf954..c2149c4cae8d 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -132,7 +132,7 @@ publishing { allprojects { publishing { repositories { - maven("https://repo.papermc.io/repository/maven-snapshots/") { + maven("https://nexus.anvilpowered.org/repository/maven-snapshots/") { name = "paperSnapshots" credentials(PasswordCredentials::class) } diff --git a/gradle.properties b/gradle.properties index 794f7c154012..f49e96b90604 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ group=io.papermc.paper -version=1.20.4-R0.1-SNAPSHOT +version=1.20.4-brigadier-SNAPSHOT mcVersion=1.20.4 # Set to true while updating Minecraft version