This commit is contained in:
Jason Penilla
2024-04-24 10:59:51 -07:00
parent c7f0919c43
commit 4a43daa012
10 changed files with 22 additions and 12 deletions

View File

@@ -1,170 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jason Penilla <11360596+jpenilla@users.noreply.github.com>
Date: Tue, 1 Feb 2022 15:51:55 -0700
Subject: [PATCH] API for creating command sender which forwards feedback
diff --git a/src/main/java/io/papermc/paper/commands/FeedbackForwardingSender.java b/src/main/java/io/papermc/paper/commands/FeedbackForwardingSender.java
new file mode 100644
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
--- /dev/null
+++ b/src/main/java/io/papermc/paper/commands/FeedbackForwardingSender.java
@@ -0,0 +0,0 @@
+package io.papermc.paper.commands;
+
+import io.papermc.paper.adventure.PaperAdventure;
+import java.util.UUID;
+import java.util.function.Consumer;
+import net.kyori.adventure.audience.MessageType;
+import net.kyori.adventure.identity.Identity;
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
+import net.minecraft.commands.CommandSource;
+import net.minecraft.commands.CommandSourceStack;
+import net.minecraft.server.level.ServerLevel;
+import net.minecraft.world.phys.Vec2;
+import net.minecraft.world.phys.Vec3;
+import org.bukkit.command.CommandSender;
+import org.bukkit.craftbukkit.CraftServer;
+import org.bukkit.craftbukkit.command.ServerCommandSender;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.checker.nullness.qual.Nullable;
+import org.checkerframework.framework.qual.DefaultQualifier;
+
+@DefaultQualifier(NonNull.class)
+public final class FeedbackForwardingSender extends ServerCommandSender {
+ private final Consumer<? super Component> feedback;
+ private final CraftServer server;
+
+ public FeedbackForwardingSender(final Consumer<? super Component> feedback, final CraftServer server) {
+ super(((ServerCommandSender) server.getConsoleSender()).perm);
+ this.server = server;
+ this.feedback = feedback;
+ }
+
+ @Override
+ public void sendMessage(final String message) {
+ this.sendMessage(LegacyComponentSerializer.legacySection().deserialize(message));
+ }
+
+ @Override
+ public void sendMessage(final String... messages) {
+ for (final String message : messages) {
+ this.sendMessage(message);
+ }
+ }
+
+ @Override
+ public void sendMessage(final Identity identity, final Component message, final MessageType type) {
+ this.feedback.accept(message);
+ }
+
+ @Override
+ public String getName() {
+ return "FeedbackForwardingSender";
+ }
+
+ @Override
+ public Component name() {
+ return Component.text(this.getName());
+ }
+
+ @Override
+ public boolean isOp() {
+ return true;
+ }
+
+ @Override
+ public void setOp(final boolean value) {
+ throw new UnsupportedOperationException("Cannot change operator status of " + this.getClass().getName());
+ }
+
+ public CommandSourceStack asVanilla() {
+ final @Nullable ServerLevel overworld = this.server.getServer().overworld();
+ return new CommandSourceStack(
+ new Source(this),
+ overworld == null ? Vec3.ZERO : Vec3.atLowerCornerOf(overworld.getSharedSpawnPos()),
+ Vec2.ZERO,
+ overworld,
+ 4,
+ this.getName(),
+ net.minecraft.network.chat.Component.literal(this.getName()),
+ this.server.getServer(),
+ null
+ );
+ }
+
+ private record Source(FeedbackForwardingSender sender) implements CommandSource {
+ @Override
+ public void sendSystemMessage(final net.minecraft.network.chat.Component message) {
+ this.sender.sendMessage(Identity.nil(), PaperAdventure.asAdventure(message));
+ }
+
+ @Override
+ public boolean acceptsSuccess() {
+ return true;
+ }
+
+ @Override
+ public boolean acceptsFailure() {
+ return true;
+ }
+
+ @Override
+ public boolean shouldInformAdmins() {
+ return false;
+ }
+
+ @Override
+ public CommandSender getBukkitSender(final CommandSourceStack stack) {
+ return this.sender;
+ }
+ }
+}
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -0,0 +0,0 @@ public final class CraftServer implements Server {
return this.console.console;
}
+ // Paper start
+ @Override
+ public CommandSender createCommandSender(final java.util.function.Consumer<? super net.kyori.adventure.text.Component> feedback) {
+ return new io.papermc.paper.commands.FeedbackForwardingSender(feedback, this);
+ }
+ // Paper end
+
public EntityMetadataStore getEntityMetadata() {
return this.entityMetadata;
}
diff --git a/src/main/java/org/bukkit/craftbukkit/command/ServerCommandSender.java b/src/main/java/org/bukkit/craftbukkit/command/ServerCommandSender.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/craftbukkit/command/ServerCommandSender.java
+++ b/src/main/java/org/bukkit/craftbukkit/command/ServerCommandSender.java
@@ -0,0 +0,0 @@ import org.bukkit.permissions.PermissionAttachmentInfo;
import org.bukkit.plugin.Plugin;
public abstract class ServerCommandSender implements CommandSender {
- private final PermissibleBase perm;
+ public final PermissibleBase perm; // Paper
private net.kyori.adventure.pointer.Pointers adventure$pointers; // Paper - implement pointers
protected ServerCommandSender() {
diff --git a/src/main/java/org/bukkit/craftbukkit/command/VanillaCommandWrapper.java b/src/main/java/org/bukkit/craftbukkit/command/VanillaCommandWrapper.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/craftbukkit/command/VanillaCommandWrapper.java
+++ b/src/main/java/org/bukkit/craftbukkit/command/VanillaCommandWrapper.java
@@ -0,0 +0,0 @@ public final class VanillaCommandWrapper extends BukkitCommand {
if (sender instanceof ProxiedCommandSender) {
return ((ProxiedNativeCommandSender) sender).getHandle();
}
+ // Paper start
+ if (sender instanceof io.papermc.paper.commands.FeedbackForwardingSender feedback) {
+ return feedback.asVanilla();
+ }
+ // Paper end
throw new IllegalArgumentException("Cannot make " + sender + " a vanilla command listener");
}

View File

@@ -1,35 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Doc <nachito94@msn.com>
Date: Mon, 2 Aug 2021 11:24:39 -0400
Subject: [PATCH] Add configurable height for slime spawn
diff --git a/src/main/java/net/minecraft/world/entity/monster/Slime.java b/src/main/java/net/minecraft/world/entity/monster/Slime.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/entity/monster/Slime.java
+++ b/src/main/java/net/minecraft/world/entity/monster/Slime.java
@@ -0,0 +0,0 @@ public class Slime extends Mob implements Enemy {
return checkMobSpawnRules(type, world, spawnReason, pos, random);
}
- if (world.getBiome(pos).is(BiomeTags.ALLOWS_SURFACE_SLIME_SPAWNS) && pos.getY() > 50 && pos.getY() < 70 && random.nextFloat() < 0.5F && random.nextFloat() < world.getMoonBrightness() && world.getMaxLocalRawBrightness(pos) <= random.nextInt(8)) {
+ // Paper start - Replace rules for Height in Swamp Biome
+ final double maxHeightSwamp = world.getMinecraftWorld().paperConfig().entities.spawning.slimeSpawnHeight.surfaceBiome.maximum;
+ final double minHeightSwamp = world.getMinecraftWorld().paperConfig().entities.spawning.slimeSpawnHeight.surfaceBiome.minimum;
+ if (world.getBiome(pos).is(BiomeTags.ALLOWS_SURFACE_SLIME_SPAWNS) && pos.getY() > minHeightSwamp && pos.getY() < maxHeightSwamp && random.nextFloat() < 0.5F && random.nextFloat() < world.getMoonBrightness() && world.getMaxLocalRawBrightness(pos) <= random.nextInt(8)) {
+ // Paper end - Replace rules for Height in Swamp Biome
return checkMobSpawnRules(type, world, spawnReason, pos, random);
}
@@ -0,0 +0,0 @@ public class Slime extends Mob implements Enemy {
ChunkPos chunkcoordintpair = new ChunkPos(pos);
boolean flag = world.getMinecraftWorld().paperConfig().entities.spawning.allChunksAreSlimeChunks || WorldgenRandom.seedSlimeChunk(chunkcoordintpair.x, chunkcoordintpair.z, ((WorldGenLevel) world).getSeed(), world.getMinecraftWorld().spigotConfig.slimeSeed).nextInt(10) == 0; // Spigot // Paper
- if (random.nextInt(10) == 0 && flag && pos.getY() < 40) {
+ // Paper start - Replace rules for Height in Slime Chunks
+ final double maxHeightSlimeChunk = world.getMinecraftWorld().paperConfig().entities.spawning.slimeSpawnHeight.slimeChunk.maximum;
+ if (random.nextInt(10) == 0 && flag && pos.getY() < maxHeightSlimeChunk) {
+ // Paper end - Replace rules for Height in Slime Chunks
return checkMobSpawnRules(type, world, spawnReason, pos, random);
}
}

View File

@@ -1,354 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jake Potrebic <jake.m.potrebic@gmail.com>
Date: Thu, 13 Jan 2022 23:05:53 -0800
Subject: [PATCH] Add missing structure set seed configs
The 4 missing structure set seed configs are strongholds, mineshafts,
buried treasure, and ancient cities.
Strongholds use a ring placement scheme which isn't random so they
utilize the world seed by default, this adds a config to override it
for just generating the ring positions.
Mineshafts and Buried Treasure structure sets are special cases
where the "salt" that can be defined for them via datapacks has 0
effect because the difference between the spacing and separation is 1
which is used as the upper bound in the random with salt. So the random
always returns the same int (0) so the salt has no effect. This adds
seeds/salts to the frequency reducer which has a similar effect.
Co-authored-by: William Blake Galbreath <blake.galbreath@gmail.com>
diff --git a/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java b/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java
+++ b/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java
@@ -0,0 +0,0 @@ public abstract class ChunkGenerator {
}
}
- if (structureplacement.isStructureChunk(placementCalculator, chunkcoordintpair.x, chunkcoordintpair.z)) {
+ if (structureplacement.isStructureChunk(placementCalculator, chunkcoordintpair.x, chunkcoordintpair.z, structureplacement instanceof net.minecraft.world.level.chunk.ChunkGeneratorStructureState.KeyedRandomSpreadStructurePlacement keyed ? keyed.key : null)) { // Paper - Add missing structure set seed configs
if (list.size() == 1) {
this.tryGenerateStructure((StructureSet.StructureSelectionEntry) list.get(0), structureAccessor, registryManager, randomstate, structureTemplateManager, placementCalculator.getLevelSeed(), chunk, chunkcoordintpair, sectionposition);
} else {
diff --git a/src/main/java/net/minecraft/world/level/chunk/ChunkGeneratorStructureState.java b/src/main/java/net/minecraft/world/level/chunk/ChunkGeneratorStructureState.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/chunk/ChunkGeneratorStructureState.java
+++ b/src/main/java/net/minecraft/world/level/chunk/ChunkGeneratorStructureState.java
@@ -0,0 +0,0 @@ public class ChunkGeneratorStructureState {
private final Map<ConcentricRingsStructurePlacement, CompletableFuture<List<ChunkPos>>> ringPositions = new Object2ObjectArrayMap();
private boolean hasGeneratedPositions;
private final List<Holder<StructureSet>> possibleStructureSets;
+ public final SpigotWorldConfig conf; // Paper - Add missing structure set seed configs
public static ChunkGeneratorStructureState createForFlat(RandomState randomstate, long i, BiomeSource worldchunkmanager, Stream<Holder<StructureSet>> stream, SpigotWorldConfig conf) { // Spigot
List<Holder<StructureSet>> list = stream.filter((holder) -> {
return ChunkGeneratorStructureState.hasBiomesForStructureSet((StructureSet) holder.value(), worldchunkmanager);
}).toList();
- return new ChunkGeneratorStructureState(randomstate, worldchunkmanager, i, 0L, ChunkGeneratorStructureState.injectSpigot(list, conf)); // Spigot
+ return new ChunkGeneratorStructureState(randomstate, worldchunkmanager, i, 0L, ChunkGeneratorStructureState.injectSpigot(list, conf), conf); // Spigot
}
public static ChunkGeneratorStructureState createForNormal(RandomState randomstate, long i, BiomeSource worldchunkmanager, HolderLookup<StructureSet> holderlookup, SpigotWorldConfig conf) { // Spigot
@@ -0,0 +0,0 @@ public class ChunkGeneratorStructureState {
return ChunkGeneratorStructureState.hasBiomesForStructureSet((StructureSet) holder_c.value(), worldchunkmanager);
}).collect(Collectors.toUnmodifiableList());
- return new ChunkGeneratorStructureState(randomstate, worldchunkmanager, i, i, ChunkGeneratorStructureState.injectSpigot(list, conf)); // Spigot
+ return new ChunkGeneratorStructureState(randomstate, worldchunkmanager, i, i, ChunkGeneratorStructureState.injectSpigot(list, conf), conf); // Spigot
}
+ // Paper start - Add missing structure set seed configs; horrible hack because spigot creates a ton of direct Holders which lose track of the identifying key
+ public static final class KeyedRandomSpreadStructurePlacement extends RandomSpreadStructurePlacement {
+ public final net.minecraft.resources.ResourceKey<StructureSet> key;
+ public KeyedRandomSpreadStructurePlacement(net.minecraft.resources.ResourceKey<StructureSet> key, net.minecraft.core.Vec3i locateOffset, FrequencyReductionMethod frequencyReductionMethod, float frequency, int salt, java.util.Optional<StructurePlacement.ExclusionZone> exclusionZone, int spacing, int separation, net.minecraft.world.level.levelgen.structure.placement.RandomSpreadType spreadType) {
+ super(locateOffset, frequencyReductionMethod, frequency, salt, exclusionZone, spacing, separation, spreadType);
+ this.key = key;
+ }
+ }
+ // Paper end - Add missing structure set seed configs
// Spigot start
private static List<Holder<StructureSet>> injectSpigot(List<Holder<StructureSet>> list, SpigotWorldConfig conf) {
return list.stream().map((holder) -> {
StructureSet structureset = holder.value();
- if (structureset.placement() instanceof RandomSpreadStructurePlacement randomConfig) {
+ final Holder<StructureSet> newHolder; // Paper - Add missing structure set seed configs
+ if (structureset.placement() instanceof RandomSpreadStructurePlacement randomConfig && holder.unwrapKey().orElseThrow().location().getNamespace().equals(net.minecraft.resources.ResourceLocation.DEFAULT_NAMESPACE)) { // Paper - Add missing structure set seed configs; check namespace cause datapacks could add structure sets with the same path
String name = holder.unwrapKey().orElseThrow().location().getPath();
int seed = randomConfig.salt;
@@ -0,0 +0,0 @@ public class ChunkGeneratorStructureState {
case "villages":
seed = conf.villageSeed;
break;
+ // Paper start - Add missing structure set seed configs
+ case "ancient_cities":
+ seed = conf.ancientCitySeed;
+ break;
+ case "trail_ruins":
+ seed = conf.trailRuinsSeed;
+ break;
+ case "trial_chambers":
+ seed = conf.trialChambersSeed;
+ break;
+ // Paper end - Add missing structure set seed configs
}
- structureset = new StructureSet(structureset.structures(), new RandomSpreadStructurePlacement(randomConfig.locateOffset, randomConfig.frequencyReductionMethod, randomConfig.frequency, seed, randomConfig.exclusionZone, randomConfig.spacing(), randomConfig.separation(), randomConfig.spreadType()));
+ // Paper start - Add missing structure set seed configs
+ structureset = new StructureSet(structureset.structures(), new KeyedRandomSpreadStructurePlacement(holder.unwrapKey().orElseThrow(), randomConfig.locateOffset, randomConfig.frequencyReductionMethod, randomConfig.frequency, seed, randomConfig.exclusionZone, randomConfig.spacing(), randomConfig.separation(), randomConfig.spreadType()));
+ newHolder = Holder.direct(structureset); // I really wish we didn't have to do this here
+ } else {
+ newHolder = holder;
}
- return Holder.direct(structureset);
+ return newHolder;
+ // Paper end - Add missing structure set seed configs
}).collect(Collectors.toUnmodifiableList());
}
// Spigot end
@@ -0,0 +0,0 @@ public class ChunkGeneratorStructureState {
return stream.anyMatch(set::contains);
}
- private ChunkGeneratorStructureState(RandomState noiseConfig, BiomeSource biomeSource, long structureSeed, long concentricRingSeed, List<Holder<StructureSet>> structureSets) {
+ private ChunkGeneratorStructureState(RandomState noiseConfig, BiomeSource biomeSource, long structureSeed, long concentricRingSeed, List<Holder<StructureSet>> structureSets, SpigotWorldConfig conf) { // Paper - Add missing structure set seed configs
this.randomState = noiseConfig;
this.levelSeed = structureSeed;
this.biomeSource = biomeSource;
this.concentricRingsSeed = concentricRingSeed;
this.possibleStructureSets = structureSets;
+ this.conf = conf; // Paper - Add missing structure set seed configs
}
public List<Holder<StructureSet>> possibleStructureSets() {
@@ -0,0 +0,0 @@ public class ChunkGeneratorStructureState {
HolderSet<Biome> holderset = placement.preferredBiomes();
RandomSource randomsource = RandomSource.create();
+ // Paper start - Add missing structure set seed configs
+ if (this.conf.strongholdSeed != null && structureSetEntry.is(net.minecraft.world.level.levelgen.structure.BuiltinStructureSets.STRONGHOLDS)) {
+ randomsource.setSeed(this.conf.strongholdSeed);
+ } else {
+ // Paper end - Add missing structure set seed configs
randomsource.setSeed(this.concentricRingsSeed);
+ } // Paper - Add missing structure set seed configs
double d0 = randomsource.nextDouble() * 3.141592653589793D * 2.0D;
int l = 0;
int i1 = 0;
@@ -0,0 +0,0 @@ public class ChunkGeneratorStructureState {
for (int l = centerChunkX - chunkCount; l <= centerChunkX + chunkCount; ++l) {
for (int i1 = centerChunkZ - chunkCount; i1 <= centerChunkZ + chunkCount; ++i1) {
- if (structureplacement.isStructureChunk(this, l, i1)) {
+ if (structureplacement.isStructureChunk(this, l, i1, structureplacement instanceof KeyedRandomSpreadStructurePlacement keyed ? keyed.key : null)) { // Paper - Add missing structure set seed configs
return true;
}
}
diff --git a/src/main/java/net/minecraft/world/level/levelgen/structure/placement/StructurePlacement.java b/src/main/java/net/minecraft/world/level/levelgen/structure/placement/StructurePlacement.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/levelgen/structure/placement/StructurePlacement.java
+++ b/src/main/java/net/minecraft/world/level/levelgen/structure/placement/StructurePlacement.java
@@ -0,0 +0,0 @@ public abstract class StructurePlacement {
return this.exclusionZone;
}
+ @Deprecated @io.papermc.paper.annotation.DoNotUse // Paper - Add missing structure set seed configs
public boolean isStructureChunk(ChunkGeneratorStructureState calculator, int chunkX, int chunkZ) {
+ // Paper start - Add missing structure set seed configs
+ return this.isStructureChunk(calculator, chunkX, chunkZ, null);
+ }
+ public boolean isStructureChunk(ChunkGeneratorStructureState calculator, int chunkX, int chunkZ, @org.jetbrains.annotations.Nullable net.minecraft.resources.ResourceKey<StructureSet> structureSetKey) {
+ Integer saltOverride = null;
+ if (structureSetKey != null) {
+ if (structureSetKey == net.minecraft.world.level.levelgen.structure.BuiltinStructureSets.MINESHAFTS) {
+ saltOverride = calculator.conf.mineshaftSeed;
+ } else if (structureSetKey == net.minecraft.world.level.levelgen.structure.BuiltinStructureSets.BURIED_TREASURES) {
+ saltOverride = calculator.conf.buriedTreasureSeed;
+ }
+ }
+ // Paper end - Add missing structure set seed configs
return this.isPlacementChunk(calculator, chunkX, chunkZ)
- && (!(this.frequency < 1.0F) || this.frequencyReductionMethod.shouldGenerate(calculator.getLevelSeed(), this.salt, chunkX, chunkZ, this.frequency))
+ && (!(this.frequency < 1.0F) || this.frequencyReductionMethod.shouldGenerate(calculator.getLevelSeed(), this.salt, chunkX, chunkZ, this.frequency, saltOverride)) // Paper - Add missing structure set seed configs
&& (!this.exclusionZone.isPresent() || !this.exclusionZone.get().isPlacementForbidden(calculator, chunkX, chunkZ));
}
@@ -0,0 +0,0 @@ public abstract class StructurePlacement {
public abstract StructurePlacementType<?> type();
- private static boolean probabilityReducer(long seed, int salt, int chunkX, int chunkZ, float frequency) {
+ private static boolean probabilityReducer(long seed, int salt, int chunkX, int chunkZ, float frequency, @org.jetbrains.annotations.Nullable Integer saltOverride) { // Paper - Add missing structure set seed configs; ignore here
WorldgenRandom worldgenRandom = new WorldgenRandom(new LegacyRandomSource(0L));
worldgenRandom.setLargeFeatureWithSalt(seed, salt, chunkX, chunkZ);
return worldgenRandom.nextFloat() < frequency;
}
- private static boolean legacyProbabilityReducerWithDouble(long seed, int salt, int chunkX, int chunkZ, float frequency) {
+ private static boolean legacyProbabilityReducerWithDouble(long seed, int salt, int chunkX, int chunkZ, float frequency, @org.jetbrains.annotations.Nullable Integer saltOverride) { // Paper - Add missing structure set seed configs
WorldgenRandom worldgenRandom = new WorldgenRandom(new LegacyRandomSource(0L));
+ if (saltOverride == null) { // Paper - Add missing structure set seed configs
worldgenRandom.setLargeFeatureSeed(seed, chunkX, chunkZ);
+ // Paper start - Add missing structure set seed configs
+ } else {
+ worldgenRandom.setLargeFeatureWithSalt(seed, chunkX, chunkZ, saltOverride);
+ }
+ // Paper end - Add missing structure set seed configs
return worldgenRandom.nextDouble() < (double)frequency;
}
- private static boolean legacyArbitrarySaltProbabilityReducer(long seed, int salt, int chunkX, int chunkZ, float frequency) {
+ private static boolean legacyArbitrarySaltProbabilityReducer(long seed, int salt, int chunkX, int chunkZ, float frequency, @org.jetbrains.annotations.Nullable Integer saltOverride) { // Paper - Add missing structure set seed configs
WorldgenRandom worldgenRandom = new WorldgenRandom(new LegacyRandomSource(0L));
- worldgenRandom.setLargeFeatureWithSalt(seed, chunkX, chunkZ, 10387320);
+ worldgenRandom.setLargeFeatureWithSalt(seed, chunkX, chunkZ, saltOverride != null ? saltOverride : HIGHLY_ARBITRARY_RANDOM_SALT); // Paper - Add missing structure set seed configs
return worldgenRandom.nextFloat() < frequency;
}
- private static boolean legacyPillagerOutpostReducer(long seed, int salt, int chunkX, int chunkZ, float frequency) {
+ private static boolean legacyPillagerOutpostReducer(long seed, int salt, int chunkX, int chunkZ, float frequency, @org.jetbrains.annotations.Nullable Integer saltOverride) { // Paper - Add missing structure set seed configs; ignore here
int i = chunkX >> 4;
int j = chunkZ >> 4;
WorldgenRandom worldgenRandom = new WorldgenRandom(new LegacyRandomSource(0L));
@@ -0,0 +0,0 @@ public abstract class StructurePlacement {
@FunctionalInterface
public interface FrequencyReducer {
- boolean shouldGenerate(long seed, int salt, int chunkX, int chunkZ, float chance);
+ boolean shouldGenerate(long seed, int salt, int chunkX, int chunkZ, float chance, @org.jetbrains.annotations.Nullable Integer saltOverride); // Paper - Add missing structure set seed configs
}
public static enum FrequencyReductionMethod implements StringRepresentable {
@@ -0,0 +0,0 @@ public abstract class StructurePlacement {
this.reducer = generationPredicate;
}
- public boolean shouldGenerate(long seed, int salt, int chunkX, int chunkZ, float chance) {
- return this.reducer.shouldGenerate(seed, salt, chunkX, chunkZ, chance);
+ public boolean shouldGenerate(long seed, int salt, int chunkX, int chunkZ, float chance, @org.jetbrains.annotations.Nullable Integer saltOverride) { // Paper - Add missing structure set seed configs
+ return this.reducer.shouldGenerate(seed, salt, chunkX, chunkZ, chance, saltOverride); // Paper - Add missing structure set seed configs
}
@Override
diff --git a/src/main/java/org/spigotmc/SpigotWorldConfig.java b/src/main/java/org/spigotmc/SpigotWorldConfig.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/spigotmc/SpigotWorldConfig.java
+++ b/src/main/java/org/spigotmc/SpigotWorldConfig.java
@@ -0,0 +0,0 @@ public class SpigotWorldConfig
public int mansionSeed;
public int fossilSeed;
public int portalSeed;
+ // Paper start - add missing structure set configs
+ public int ancientCitySeed;
+ public int trailRuinsSeed;
+ public int trialChambersSeed;
+ public int buriedTreasureSeed;
+ public Integer mineshaftSeed;
+ public Long strongholdSeed;
+ private <N extends Number> N getSeed(String path, java.util.function.Function<String, N> toNumberFunc) {
+ final String value = this.getString(path, "default");
+ return org.apache.commons.lang3.math.NumberUtils.isParsable(value) ? toNumberFunc.apply(value) : null;
+ }
+ // Paper end
private void initWorldGenSeeds()
{
this.villageSeed = this.getInt( "seed-village", 10387312 );
@@ -0,0 +0,0 @@ public class SpigotWorldConfig
this.mansionSeed = this.getInt( "seed-mansion", 10387319 );
this.fossilSeed = this.getInt( "seed-fossil", 14357921 );
this.portalSeed = this.getInt( "seed-portal", 34222645 );
+ // Paper start - add missing structure set configs
+ this.ancientCitySeed = this.getInt("seed-ancientcity", 20083232);
+ this.trailRuinsSeed = this.getInt("seed-trailruins", 83469867);
+ this.trialChambersSeed = this.getInt("seed-trialchambers", 94251327);
+ this.buriedTreasureSeed = this.getInt("seed-buriedtreasure", 10387320); // StructurePlacement#HIGHLY_ARBITRARY_RANDOM_SALT
+ this.mineshaftSeed = this.getSeed("seed-mineshaft", Integer::parseInt);
+ this.strongholdSeed = this.getSeed("seed-stronghold", Long::parseLong);
+ // Paper end
this.log( "Custom Map Seeds: Village: " + this.villageSeed + " Desert: " + this.desertSeed + " Igloo: " + this.iglooSeed + " Jungle: " + this.jungleSeed + " Swamp: " + this.swampSeed + " Monument: " + this.monumentSeed
+ " Ocean: " + this.oceanSeed + " Shipwreck: " + this.shipwreckSeed + " End City: " + this.endCitySeed + " Slime: " + this.slimeSeed + " Nether: " + this.netherSeed + " Mansion: " + this.mansionSeed + " Fossil: " + this.fossilSeed + " Portal: " + this.portalSeed );
}
diff --git a/src/test/java/io/papermc/paper/world/structure/StructureSeedConfigTest.java b/src/test/java/io/papermc/paper/world/structure/StructureSeedConfigTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
--- /dev/null
+++ b/src/test/java/io/papermc/paper/world/structure/StructureSeedConfigTest.java
@@ -0,0 +0,0 @@
+package io.papermc.paper.world.structure;
+
+import io.papermc.paper.configuration.PaperConfigurations;
+import java.io.File;
+import java.lang.reflect.Field;
+import net.minecraft.core.Registry;
+import net.minecraft.core.registries.Registries;
+import net.minecraft.resources.ResourceKey;
+import net.minecraft.resources.ResourceLocation;
+import net.minecraft.world.level.levelgen.structure.BuiltinStructureSets;
+import net.minecraft.world.level.levelgen.structure.StructureSet;
+import net.minecraft.world.level.levelgen.structure.placement.StructurePlacement;
+import org.bukkit.configuration.file.YamlConfiguration;
+import org.bukkit.support.AbstractTestingBase;
+import org.jetbrains.annotations.NotNull;
+import org.junit.jupiter.api.Test;
+import org.spigotmc.SpigotConfig;
+import org.spigotmc.SpigotWorldConfig;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class StructureSeedConfigTest extends AbstractTestingBase {
+
+ @Test
+ public void checkStructureSeedDefaults() throws ReflectiveOperationException {
+ SpigotConfig.config = new YamlConfiguration() {
+ @Override
+ public void save(final @NotNull File file) {
+ // no-op
+ }
+ };
+ final SpigotWorldConfig config = PaperConfigurations.SPIGOT_WORLD_DEFAULTS.get();
+
+
+ final Registry<StructureSet> structureSets = AbstractTestingBase.REGISTRY_CUSTOM.registryOrThrow(Registries.STRUCTURE_SET);
+ for (final ResourceKey<StructureSet> setKey : structureSets.registryKeySet()) {
+ assertEquals(ResourceLocation.DEFAULT_NAMESPACE, setKey.location().getNamespace());
+ final StructureSet set = structureSets.getOrThrow(setKey);
+ if (setKey == BuiltinStructureSets.STRONGHOLDS) { // special case due to seed matching world seed
+ assertEquals(0, set.placement().salt);
+ continue;
+ }
+ int salt = switch (setKey.location().getPath()) {
+ case "villages" -> config.villageSeed;
+ case "desert_pyramids" -> config.desertSeed;
+ case "igloos" -> config.iglooSeed;
+ case "jungle_temples" -> config.jungleSeed;
+ case "swamp_huts" -> config.swampSeed;
+ case "pillager_outposts" -> config.outpostSeed;
+ case "ocean_monuments" -> config.monumentSeed;
+ case "woodland_mansions" -> config.mansionSeed;
+ case "buried_treasures" -> config.buriedTreasureSeed;
+ case "mineshafts" -> config.mineshaftSeed == null ? 0 : config.mineshaftSeed; // mineshaft seed is set differently
+ case "ruined_portals" -> config.portalSeed;
+ case "shipwrecks" -> config.shipwreckSeed;
+ case "ocean_ruins" -> config.oceanSeed;
+ case "nether_complexes" -> config.netherSeed;
+ case "nether_fossils" -> config.fossilSeed;
+ case "end_cities" -> config.endCitySeed;
+ case "ancient_cities" -> config.ancientCitySeed;
+ case "trail_ruins" -> config.trailRuinsSeed;
+ case "trial_chambers" -> config.trialChambersSeed;
+ default -> throw new AssertionError("Missing structure set seed in SpigotWorldConfig for " + setKey);
+ };
+ if (setKey == BuiltinStructureSets.BURIED_TREASURES) {
+ final Field field = StructurePlacement.class.getDeclaredField("HIGHLY_ARBITRARY_RANDOM_SALT");
+ field.trySetAccessible();
+ assertEquals(0, set.placement().salt);
+ assertEquals(field.get(null), salt, "Mismatched default seed for " + setKey + ". Should be " + field.get(null));
+ continue;
+ }
+ assertEquals(set.placement().salt, salt, "Mismatched default seed for " + setKey + ". Should be " + set.placement().salt);
+ }
+ }
+}

View File

@@ -1,54 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Kieran Wallbanks <kieran.wallbanks@gmail.com>
Date: Mon, 21 Jun 2021 14:23:50 +0100
Subject: [PATCH] Fix NotePlayEvent
== AT ==
public org.bukkit.craftbukkit.block.data.CraftBlockData toNMS(Ljava/lang/Enum;Ljava/lang/Class;)Ljava/lang/Enum;
diff --git a/src/main/java/net/minecraft/world/level/block/NoteBlock.java b/src/main/java/net/minecraft/world/level/block/NoteBlock.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/block/NoteBlock.java
+++ b/src/main/java/net/minecraft/world/level/block/NoteBlock.java
@@ -0,0 +0,0 @@ public class NoteBlock extends Block {
private void playNote(@Nullable Entity entity, BlockState state, Level world, BlockPos pos) {
if (((NoteBlockInstrument) state.getValue(NoteBlock.INSTRUMENT)).worksAboveNoteBlock() || world.getBlockState(pos.above()).isAir()) {
// CraftBukkit start
- org.bukkit.event.block.NotePlayEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callNotePlayEvent(world, pos, state.getValue(NoteBlock.INSTRUMENT), state.getValue(NoteBlock.NOTE));
- if (event.isCancelled()) {
- return;
- }
+ // org.bukkit.event.block.NotePlayEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callNotePlayEvent(world, pos, state.getValue(NoteBlock.INSTRUMENT), state.getValue(NoteBlock.NOTE));
+ // if (event.isCancelled()) {
+ // return;
+ // }
// CraftBukkit end
+ // Paper - move NotePlayEvent call to fix instrument/note changes; TODO any way to cancel the game event?
world.blockEvent(pos, this, 0, 0);
world.gameEvent(entity, GameEvent.NOTE_BLOCK_PLAY, pos);
}
@@ -0,0 +0,0 @@ public class NoteBlock extends Block {
@Override
public boolean triggerEvent(BlockState state, Level world, BlockPos pos, int type, int data) {
NoteBlockInstrument blockpropertyinstrument = (NoteBlockInstrument) state.getValue(NoteBlock.INSTRUMENT);
+ // Paper start - move NotePlayEvent call to fix instrument/note changes
+ org.bukkit.event.block.NotePlayEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callNotePlayEvent(world, pos, blockpropertyinstrument, state.getValue(NOTE));
+ if (event.isCancelled()) return false;
+ // Paper end - move NotePlayEvent call to fix instrument/note changes
float f;
if (blockpropertyinstrument.isTunable()) {
- int k = (Integer) state.getValue(NoteBlock.NOTE);
+ int k = event.getNote().getId(); // Paper - move NotePlayEvent call to fix instrument/note changes
f = NoteBlock.getPitchFromNote(k);
world.addParticle(ParticleTypes.NOTE, (double) pos.getX() + 0.5D, (double) pos.getY() + 1.2D, (double) pos.getZ() + 0.5D, (double) k / 24.0D, 0.0D, 0.0D);
@@ -0,0 +0,0 @@ public class NoteBlock extends Block {
holder = Holder.direct(SoundEvent.createVariableRangeEvent(minecraftkey));
} else {
- holder = blockpropertyinstrument.getSoundEvent();
+ holder = org.bukkit.craftbukkit.block.data.CraftBlockData.toNMS(event.getInstrument(), NoteBlockInstrument.class).getSoundEvent(); // Paper - move NotePlayEvent call to fix instrument/note changes
}
world.playSeededSound((Player) null, (double) pos.getX() + 0.5D, (double) pos.getY() + 0.5D, (double) pos.getZ() + 0.5D, holder, SoundSource.RECORDS, 3.0F, f, world.random.nextLong());

View File

@@ -1,32 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jake Potrebic <jake.m.potrebic@gmail.com>
Date: Sun, 16 Jan 2022 10:34:02 -0800
Subject: [PATCH] Fix xp reward for baby zombies
The field that tracks the xpReward was not
getting reset if the death was cancelled
so this resets it after each call to
Zombie#getExperienceReward
diff --git a/src/main/java/net/minecraft/world/entity/monster/Zombie.java b/src/main/java/net/minecraft/world/entity/monster/Zombie.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/entity/monster/Zombie.java
+++ b/src/main/java/net/minecraft/world/entity/monster/Zombie.java
@@ -0,0 +0,0 @@ public class Zombie extends Monster {
@Override
public int getExperienceReward() {
+ final int previousReward = this.xpReward; // Paper - store previous value to reset after calculating XP reward
if (this.isBaby()) {
this.xpReward = (int) ((double) this.xpReward * 2.5D);
}
- return super.getExperienceReward();
+ // Paper start - store previous value to reset after calculating XP reward
+ int reward = super.getExperienceReward();
+ this.xpReward = previousReward;
+ return reward;
+ // Paper end - store previous value to reset after calculating XP reward
}
@Override

View File

@@ -1,82 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Owen1212055 <23108066+Owen1212055@users.noreply.github.com>
Date: Sun, 26 Dec 2021 20:27:43 -0500
Subject: [PATCH] Freeze Tick Lock API
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
@@ -0,0 +0,0 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, S
private org.bukkit.util.Vector origin;
@javax.annotation.Nullable
private UUID originWorld;
+ public boolean freezeLocked = false; // Paper - Freeze Tick Lock API
public void setOrigin(@javax.annotation.Nonnull Location location) {
this.origin = location.toVector();
@@ -0,0 +0,0 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, S
this.setRemainingFireTicks(this.remainingFireTicks - 1);
}
- if (this.getTicksFrozen() > 0) {
+ if (this.getTicksFrozen() > 0 && !freezeLocked) { // Paper - Freeze Tick Lock API
this.setTicksFrozen(0);
this.level().levelEvent((Player) null, 1009, this.blockPosition, 1);
}
@@ -0,0 +0,0 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, S
if (fromNetherPortal) {
nbttagcompound.putBoolean("Paper.FromNetherPortal", true);
}
+ if (freezeLocked) {
+ nbttagcompound.putBoolean("Paper.FreezeLock", true);
+ }
// Paper end
return nbttagcompound;
} catch (Throwable throwable) {
@@ -0,0 +0,0 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, S
if (spawnReason == null) {
spawnReason = org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.DEFAULT;
}
+ if (nbt.contains("Paper.FreezeLock")) {
+ freezeLocked = nbt.getBoolean("Paper.FreezeLock");
+ }
// Paper end
} catch (Throwable throwable) {
diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java
+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java
@@ -0,0 +0,0 @@ public abstract class LivingEntity extends Entity implements Attackable {
this.level().getProfiler().pop();
this.level().getProfiler().push("freezing");
- if (!this.level().isClientSide && !this.isDeadOrDying()) {
+ if (!this.level().isClientSide && !this.isDeadOrDying() && !this.freezeLocked) { // Paper - Freeze Tick Lock API
int i = this.getTicksFrozen();
if (this.isInPowderSnow && this.canFreeze()) {
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
@@ -0,0 +0,0 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity {
return this.getHandle().isFullyFrozen();
}
+ // Paper start - Freeze Tick Lock API
+ @Override
+ public boolean isFreezeTickingLocked() {
+ return this.entity.freezeLocked;
+ }
+
+ @Override
+ public void lockFreezeTicks(boolean locked) {
+ this.entity.freezeLocked = locked;
+ }
+ // Paper end - Freeze Tick Lock API
@Override
public void remove() {
this.entity.pluginRemoved = true;

View File

@@ -1,93 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jake Potrebic <jake.m.potrebic@gmail.com>
Date: Thu, 27 May 2021 21:58:24 -0700
Subject: [PATCH] More PotionEffectType API
diff --git a/src/main/java/org/bukkit/craftbukkit/potion/CraftPotionEffectType.java b/src/main/java/org/bukkit/craftbukkit/potion/CraftPotionEffectType.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/craftbukkit/potion/CraftPotionEffectType.java
+++ b/src/main/java/org/bukkit/craftbukkit/potion/CraftPotionEffectType.java
@@ -0,0 +0,0 @@ public class CraftPotionEffectType extends PotionEffectType implements Handleabl
return this.handle.getDescriptionId();
}
+ // Paper start
+ @Override
+ public java.util.Map<org.bukkit.attribute.Attribute, org.bukkit.attribute.AttributeModifier> getEffectAttributes() {
+ // re-create map each time because a nms MobEffect can have its attributes modified
+ final java.util.Map<org.bukkit.attribute.Attribute, org.bukkit.attribute.AttributeModifier> attributeMap = new java.util.HashMap<>();
+ this.handle.getAttributeModifiers().forEach((attribute, attributeModifier) -> {
+ attributeMap.put(
+ org.bukkit.craftbukkit.attribute.CraftAttribute.stringToBukkit(attribute.toString()),
+ // use zero as amplifier to get the base amount, as it is amount = base * (amplifier + 1)
+ org.bukkit.craftbukkit.attribute.CraftAttributeInstance.convert(attributeModifier.create(0))
+ );
+ });
+ return java.util.Map.copyOf(attributeMap);
+ }
+
+ @Override
+ public double getAttributeModifierAmount(org.bukkit.attribute.Attribute attribute, int effectAmplifier) {
+ com.google.common.base.Preconditions.checkArgument(effectAmplifier >= 0, "effectAmplifier must be greater than or equal to 0");
+ net.minecraft.world.entity.ai.attributes.Attribute nmsAttribute = org.bukkit.craftbukkit.attribute.CraftAttribute.bukkitToMinecraft(attribute);
+ com.google.common.base.Preconditions.checkArgument(this.handle.getAttributeModifiers().containsKey(nmsAttribute), attribute + " is not present on " + this.getKey());
+ return this.handle.getAttributeModifiers().get(nmsAttribute).create(effectAmplifier).getAmount();
+ }
+
+ @Override
+ public PotionEffectType.Category getEffectCategory() {
+ return fromNMS(handle.getCategory());
+ }
+
+ @Override
+ public String translationKey() {
+ return this.handle.getDescriptionId();
+ }
+
+ public static PotionEffectType.Category fromNMS(net.minecraft.world.effect.MobEffectCategory mobEffectInfo) {
+ return switch (mobEffectInfo) {
+ case BENEFICIAL -> PotionEffectType.Category.BENEFICIAL;
+ case HARMFUL -> PotionEffectType.Category.HARMFUL;
+ case NEUTRAL -> PotionEffectType.Category.NEUTRAL;
+ };
+ }
+ // Paper end
+
@Override
public boolean equals(Object other) {
if (this == other) {
diff --git a/src/test/java/io/papermc/paper/effects/EffectCategoryTest.java b/src/test/java/io/papermc/paper/effects/EffectCategoryTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
--- /dev/null
+++ b/src/test/java/io/papermc/paper/effects/EffectCategoryTest.java
@@ -0,0 +0,0 @@
+package io.papermc.paper.effects;
+
+import io.papermc.paper.adventure.PaperAdventure;
+import net.minecraft.world.effect.MobEffectCategory;
+import org.bukkit.craftbukkit.potion.CraftPotionEffectType;
+import org.bukkit.potion.PotionEffectType;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+public class EffectCategoryTest {
+
+ @Test
+ public void testEffectCategoriesExist() {
+ for (MobEffectCategory mobEffectInfo : MobEffectCategory.values()) {
+ assertNotNull(CraftPotionEffectType.fromNMS(mobEffectInfo), mobEffectInfo + " is missing a bukkit equivalent");
+ }
+ }
+
+ @Test
+ public void testCategoryHasEquivalentColors() {
+ for (MobEffectCategory mobEffectInfo : MobEffectCategory.values()) {
+ PotionEffectType.Category bukkitEffectCategory = CraftPotionEffectType.fromNMS(mobEffectInfo);
+ assertEquals(bukkitEffectCategory.getColor(), PaperAdventure.asAdventure(mobEffectInfo.getTooltipFormatting()), mobEffectInfo.getTooltipFormatting().name() + " doesn't equal " + bukkitEffectCategory.getColor());
+ }
+ }
+}

View File

@@ -1,62 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Brody Beckwith <brody@beckwith.dev>
Date: Fri, 14 Jan 2022 00:41:11 -0500
Subject: [PATCH] Multi Block Change API Implementation
diff --git a/src/main/java/net/minecraft/network/protocol/game/ClientboundSectionBlocksUpdatePacket.java b/src/main/java/net/minecraft/network/protocol/game/ClientboundSectionBlocksUpdatePacket.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/network/protocol/game/ClientboundSectionBlocksUpdatePacket.java
+++ b/src/main/java/net/minecraft/network/protocol/game/ClientboundSectionBlocksUpdatePacket.java
@@ -0,0 +0,0 @@ public class ClientboundSectionBlocksUpdatePacket implements Packet<ClientGamePa
}
+ // Paper start - Multi Block Change API
+ public ClientboundSectionBlocksUpdatePacket(SectionPos sectionPos, it.unimi.dsi.fastutil.shorts.Short2ObjectMap<BlockState> blockChanges) {
+ this.sectionPos = sectionPos;
+ this.positions = blockChanges.keySet().toShortArray();
+ this.states = blockChanges.values().toArray(new BlockState[0]);
+ }
+ // Paper end - Multi Block Change API
+
@Override
public void write(FriendlyByteBuf buf) {
buf.writeLong(this.sectionPos.asLong());
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
@@ -0,0 +0,0 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
this.getHandle().connection.send(packet);
}
+ // Paper start
+ @Override
+ public void sendMultiBlockChange(final Map<? extends io.papermc.paper.math.Position, BlockData> blockChanges) {
+ if (this.getHandle().connection == null) return;
+
+ Map<SectionPos, it.unimi.dsi.fastutil.shorts.Short2ObjectMap<net.minecraft.world.level.block.state.BlockState>> sectionMap = new HashMap<>();
+
+ for (Map.Entry<? extends io.papermc.paper.math.Position, BlockData> entry : blockChanges.entrySet()) {
+ BlockData blockData = entry.getValue();
+ BlockPos blockPos = io.papermc.paper.util.MCUtil.toBlockPos(entry.getKey());
+ SectionPos sectionPos = SectionPos.of(blockPos);
+
+ it.unimi.dsi.fastutil.shorts.Short2ObjectMap<net.minecraft.world.level.block.state.BlockState> sectionData = sectionMap.computeIfAbsent(sectionPos, key -> new it.unimi.dsi.fastutil.shorts.Short2ObjectArrayMap<>());
+ sectionData.put(SectionPos.sectionRelativePos(blockPos), ((CraftBlockData) blockData).getState());
+ }
+
+ for (Map.Entry<SectionPos, it.unimi.dsi.fastutil.shorts.Short2ObjectMap<net.minecraft.world.level.block.state.BlockState>> entry : sectionMap.entrySet()) {
+ SectionPos sectionPos = entry.getKey();
+ it.unimi.dsi.fastutil.shorts.Short2ObjectMap<net.minecraft.world.level.block.state.BlockState> blockData = entry.getValue();
+
+ net.minecraft.network.protocol.game.ClientboundSectionBlocksUpdatePacket packet = new net.minecraft.network.protocol.game.ClientboundSectionBlocksUpdatePacket(sectionPos, blockData);
+ this.getHandle().connection.send(packet);
+ }
+ }
+ // Paper end
+
@Override
public void sendBlockChanges(Collection<BlockState> blocks) {
Preconditions.checkArgument(blocks != null, "blocks must not be null");

View File

@@ -1,38 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Nassim Jahnke <nassim@njahnke.dev>
Date: Fri, 7 Jan 2022 11:45:15 +0100
Subject: [PATCH] Reset placed block on exception
diff --git a/src/main/java/net/minecraft/world/item/BlockItem.java b/src/main/java/net/minecraft/world/item/BlockItem.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/item/BlockItem.java
+++ b/src/main/java/net/minecraft/world/item/BlockItem.java
@@ -0,0 +0,0 @@ public class BlockItem extends Item {
if (this instanceof PlaceOnWaterBlockItem || this instanceof SolidBucketItem) {
blockstate = org.bukkit.craftbukkit.block.CraftBlockStates.getBlockState(blockactioncontext1.getLevel(), blockactioncontext1.getClickedPos());
}
+ final org.bukkit.block.BlockState oldBlockstate = blockstate != null ? blockstate : org.bukkit.craftbukkit.block.CraftBlockStates.getBlockState(blockactioncontext1.getLevel(), blockactioncontext1.getClickedPos()); // Paper - Reset placed block on exception
// CraftBukkit end
if (iblockdata == null) {
@@ -0,0 +0,0 @@ public class BlockItem extends Item {
if (iblockdata1.is(iblockdata.getBlock())) {
iblockdata1 = this.updateBlockStateFromTag(blockposition, world, itemstack, iblockdata1);
+ // Paper start - Reset placed block on exception
+ try {
this.updateCustomBlockEntityTag(blockposition, world, entityhuman, itemstack, iblockdata1);
+ } catch (Exception e) {
+ oldBlockstate.update(true, false);
+ if (entityhuman instanceof ServerPlayer player) {
+ org.apache.logging.log4j.LogManager.getLogger().error("Player {} tried placing invalid block", player.getScoreboardName(), e);
+ player.getBukkitEntity().kickPlayer("Packet processing error");
+ return InteractionResult.FAIL;
+ }
+ throw e; // Rethrow exception if not placed by a player
+ }
+ // Paper end - Reset placed block on exception
iblockdata1.getBlock().setPlacedBy(world, blockposition, iblockdata1, entityhuman, itemstack);
// CraftBukkit start
if (blockstate != null) {

View File

@@ -1,20 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Shane Freeder <theboyetronic@gmail.com>
Date: Mon, 12 Jul 2021 12:28:29 +0100
Subject: [PATCH] Use a CHM for StructureTemplate.Pallete cache
fixes a CME due to this collection being shared across threads
diff --git a/src/main/java/net/minecraft/world/level/levelgen/structure/templatesystem/StructureTemplate.java b/src/main/java/net/minecraft/world/level/levelgen/structure/templatesystem/StructureTemplate.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/levelgen/structure/templatesystem/StructureTemplate.java
+++ b/src/main/java/net/minecraft/world/level/levelgen/structure/templatesystem/StructureTemplate.java
@@ -0,0 +0,0 @@ public class StructureTemplate {
public static final class Palette {
private final List<StructureTemplate.StructureBlockInfo> blocks;
- private final Map<Block, List<StructureTemplate.StructureBlockInfo>> cache = Maps.newHashMap();
+ private final Map<Block, List<StructureTemplate.StructureBlockInfo>> cache = Maps.newConcurrentMap(); // Paper - Fix CME due to this collection being shared across threads
Palette(List<StructureTemplate.StructureBlockInfo> infos) {
this.blocks = infos;