diff --git a/patches/server/Add-API-for-CanPlaceOn-and-CanDestroy-NBT-values.patch b/patches/server/Add-API-for-CanPlaceOn-and-CanDestroy-NBT-values.patch index 6f199aa34..e96358660 100644 --- a/patches/server/Add-API-for-CanPlaceOn-and-CanDestroy-NBT-values.patch +++ b/patches/server/Add-API-for-CanPlaceOn-and-CanDestroy-NBT-values.patch @@ -67,7 +67,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + 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 0000000000000000000000000000000000000000..00000000000000000000000000000000 + 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 0000000000000000000000000000000000000000..00000000000000000000000000000000 + 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 0000000000000000000000000000000000000000..00000000000000000000000000000000 + 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; + } @@ -347,8 +347,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + 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 0000000000000000000000000000000000000000..00000000000000000000000000000000 + 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); -+ } -+ } catch (IllegalArgumentException ex) { -+ org.bukkit.Bukkit.getLogger().warning("Namespaced resource does not validate: " + key.toString()); -+ ex.printStackTrace(); -+ } ++ return new NamespacedTag(key.getNamespace(), key.getPath()); + -+ return resource; ++ } ++ return CraftNamespacedKey.fromMinecraft(key); ++ } catch (IllegalArgumentException ignored) { ++ return null; ++ } + } + + private @Nonnull String serializeNamespaced(Namespaced resource) { diff --git a/patches/server/Do-not-allow-bees-to-load-chunks-for-beehives.patch b/patches/server/Do-not-allow-bees-to-load-chunks-for-beehives.patch index fe92df7e9..a48fa2a1e 100644 --- a/patches/server/Do-not-allow-bees-to-load-chunks-for-beehives.patch +++ b/patches/server/Do-not-allow-bees-to-load-chunks-for-beehives.patch @@ -40,3 +40,19 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 +--- a/src/main/java/net/minecraft/world/entity/monster/Vex.java ++++ b/src/main/java/net/minecraft/world/entity/monster/Vex.java +@@ -0,0 +0,0 @@ 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/Improve-tag-parser-handling.patch b/patches/server/Improve-tag-parser-handling.patch index 4d483370a..3b8988ef7 100644 --- a/patches/server/Improve-tag-parser-handling.patch +++ b/patches/server/Improve-tag-parser-handling.patch @@ -9,16 +9,48 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 --- a/src/main/java/com/mojang/brigadier/CommandDispatcher.java +++ b/src/main/java/com/mojang/brigadier/CommandDispatcher.java @@ -0,0 +0,0 @@ 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()); } +@@ -0,0 +0,0 @@ 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..0000000000000000000000000000000000000000 +--- /dev/null ++++ b/src/main/java/com/mojang/brigadier/exceptions/TagParseCommandSyntaxException.java +@@ -0,0 +0,0 @@ ++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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/net/minecraft/nbt/TagParser.java @@ -83,10 +115,10 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 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"); + } + } } @@ -122,22 +154,16 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 TAB_COMPLETE_EXECUTOR.execute(() -> this.handleCustomCommandSuggestions0(packet)); } @@ -0,0 +0,0 @@ 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 diff --git a/patches/server/LootTable-API-and-replenishable-lootables.patch b/patches/server/LootTable-API-and-replenishable-lootables.patch index 5ce986f62..5a992e7cd 100644 --- a/patches/server/LootTable-API-and-replenishable-lootables.patch +++ b/patches/server/LootTable-API-and-replenishable-lootables.patch @@ -675,7 +675,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + // 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/Validate-ResourceLocation-in-NBT-reading.patch b/patches/server/Validate-ResourceLocation-in-NBT-reading.patch index 1f303eb27..c156da55b 100644 --- a/patches/server/Validate-ResourceLocation-in-NBT-reading.patch +++ b/patches/server/Validate-ResourceLocation-in-NBT-reading.patch @@ -110,3 +110,34 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 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 +@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 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 +@@ -0,0 +0,0 @@ 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 {