aboutsummaryrefslogtreecommitdiffhomepage
path: root/patches/server/0557-Add-StructuresLocateEvent.patch
diff options
context:
space:
mode:
Diffstat (limited to 'patches/server/0557-Add-StructuresLocateEvent.patch')
-rw-r--r--patches/server/0557-Add-StructuresLocateEvent.patch214
1 files changed, 214 insertions, 0 deletions
diff --git a/patches/server/0557-Add-StructuresLocateEvent.patch b/patches/server/0557-Add-StructuresLocateEvent.patch
new file mode 100644
index 0000000000..42d31007ce
--- /dev/null
+++ b/patches/server/0557-Add-StructuresLocateEvent.patch
@@ -0,0 +1,214 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: dfsek <[email protected]>
+Date: Wed, 16 Sep 2020 01:12:29 -0700
+Subject: [PATCH] Add StructuresLocateEvent
+
+Co-authored-by: Jake Potrebic <[email protected]>
+
+diff --git a/src/main/java/io/papermc/paper/registry/RegistryKey.java b/src/main/java/io/papermc/paper/registry/RegistryKey.java
+index 6f39e343147803e15e7681c993b8797a629702e7..87154ae69788249960bca376aafd90bf64d5bfe7 100644
+--- a/src/main/java/io/papermc/paper/registry/RegistryKey.java
++++ b/src/main/java/io/papermc/paper/registry/RegistryKey.java
+@@ -1,8 +1,13 @@
+ package io.papermc.paper.registry;
+
++import io.papermc.paper.world.structure.ConfiguredStructure;
+ import net.minecraft.core.Registry;
+ import net.minecraft.resources.ResourceKey;
++import net.minecraft.world.level.levelgen.structure.Structure;
+ import org.bukkit.Keyed;
+
+ public record RegistryKey<API extends Keyed, MINECRAFT>(Class<API> apiClass, ResourceKey<? extends Registry<MINECRAFT>> resourceKey) {
++
++ public static final RegistryKey<ConfiguredStructure, Structure> CONFIGURED_STRUCTURE_REGISTRY = new RegistryKey<>(ConfiguredStructure.class, Registry.STRUCTURE_REGISTRY);
++
+ }
+diff --git a/src/main/java/io/papermc/paper/world/structure/PaperConfiguredStructure.java b/src/main/java/io/papermc/paper/world/structure/PaperConfiguredStructure.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..423bf87ebda7ea266dc7b48cbfadbc8551180721
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/world/structure/PaperConfiguredStructure.java
+@@ -0,0 +1,42 @@
++package io.papermc.paper.world.structure;
++
++import io.papermc.paper.registry.PaperRegistry;
++import io.papermc.paper.registry.RegistryKey;
++import net.minecraft.core.Registry;
++import net.minecraft.resources.ResourceLocation;
++import net.minecraft.world.level.levelgen.structure.Structure;
++import org.bukkit.NamespacedKey;
++import org.bukkit.StructureType;
++import org.checkerframework.checker.nullness.qual.NonNull;
++import org.checkerframework.checker.nullness.qual.Nullable;
++import org.checkerframework.framework.qual.DefaultQualifier;
++
++import java.util.Objects;
++import java.util.function.Supplier;
++
++@DefaultQualifier(NonNull.class)
++public final class PaperConfiguredStructure {
++
++ private PaperConfiguredStructure() {
++ }
++
++ public static void init() {
++ new ConfiguredStructureRegistry().register();
++ }
++
++ static final class ConfiguredStructureRegistry extends PaperRegistry<ConfiguredStructure, Structure> {
++
++ private static final Supplier<Registry<Structure>> STRUCTURE_FEATURE_REGISTRY = registryFor(Registry.STRUCTURE_REGISTRY);
++
++ public ConfiguredStructureRegistry() {
++ super(RegistryKey.CONFIGURED_STRUCTURE_REGISTRY);
++ }
++
++ @Override
++ public @Nullable ConfiguredStructure convertToApi(NamespacedKey key, Structure nms) {
++ final ResourceLocation structureTypeLoc = Objects.requireNonNull(Registry.STRUCTURE_TYPES.getKey(nms.type()), "unexpected structure type " + nms.type());
++ final @Nullable StructureType structureType = StructureType.getStructureTypes().get(structureTypeLoc.getPath());
++ return structureType == null ? null : new ConfiguredStructure(key, structureType);
++ }
++ }
++}
+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 6e2185bbaac889d51a54ec97907da3b1faa465c4..50b9c2fbe3a5c12a43b4711d678ed2398dbdee58 100644
+--- a/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java
++++ b/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java
+@@ -295,6 +295,26 @@ public abstract class ChunkGenerator {
+
+ @Nullable
+ public Pair<BlockPos, Holder<Structure>> findNearestMapStructure(ServerLevel world, HolderSet<Structure> structures, BlockPos center, int radius, boolean skipReferencedStructures) {
++ // Paper start - StructureLocateEvent
++ final org.bukkit.World bukkitWorld = world.getWorld();
++ final org.bukkit.Location origin = net.minecraft.server.MCUtil.toLocation(world, center);
++ final var paperRegistry = io.papermc.paper.registry.PaperRegistry.getRegistry(io.papermc.paper.registry.RegistryKey.CONFIGURED_STRUCTURE_REGISTRY);
++ final List<io.papermc.paper.world.structure.ConfiguredStructure> configuredStructures = new ArrayList<>();
++ paperRegistry.convertToApi(structures, configuredStructures::add, false); // gracefully handle missing api, use tests to check (or exclude)
++ if (!configuredStructures.isEmpty()) {
++ final io.papermc.paper.event.world.StructuresLocateEvent event = new io.papermc.paper.event.world.StructuresLocateEvent(bukkitWorld, origin, configuredStructures, radius, skipReferencedStructures);
++ if (!event.callEvent()) {
++ return null;
++ }
++ if (event.getResult() != null) {
++ return Pair.of(net.minecraft.server.MCUtil.toBlockPosition(event.getResult().position()), paperRegistry.getMinecraftHolder(event.getResult().configuredStructure()));
++ }
++ center = net.minecraft.server.MCUtil.toBlockPosition(event.getOrigin());
++ radius = event.getRadius();
++ skipReferencedStructures = event.shouldFindUnexplored();
++ structures = HolderSet.direct(paperRegistry::getMinecraftHolder, event.getConfiguredStructures());
++ }
++ // Paper end
+ Map<StructurePlacement, Set<Holder<Structure>>> map = new Object2ObjectArrayMap();
+ Iterator iterator = structures.iterator();
+
+diff --git a/src/main/java/net/minecraft/world/level/levelgen/structure/Structure.java b/src/main/java/net/minecraft/world/level/levelgen/structure/Structure.java
+index 737956b316c02e4ccdc6eef8de4a0a299d36b9ca..b8649eab719a1b71dc686386a8db756eefb9802e 100644
+--- a/src/main/java/net/minecraft/world/level/levelgen/structure/Structure.java
++++ b/src/main/java/net/minecraft/world/level/levelgen/structure/Structure.java
+@@ -41,6 +41,7 @@ public abstract class Structure {
+ public static final Codec<Structure> DIRECT_CODEC = Registry.STRUCTURE_TYPES.byNameCodec().dispatch(Structure::type, StructureType::codec);
+ public static final Codec<Holder<Structure>> CODEC = RegistryFileCodec.create(Registry.STRUCTURE_REGISTRY, DIRECT_CODEC);
+ protected final Structure.StructureSettings settings;
++ static { io.papermc.paper.world.structure.PaperConfiguredStructure.init(); } // Paper
+
+ public static <S extends Structure> RecordCodecBuilder<S, Structure.StructureSettings> settingsCodec(RecordCodecBuilder.Instance<S> instance) {
+ return Structure.StructureSettings.CODEC.forGetter((feature) -> {
+diff --git a/src/test/java/io/papermc/paper/world/structure/ConfiguredStructureTest.java b/src/test/java/io/papermc/paper/world/structure/ConfiguredStructureTest.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..61efebe1d363b34e2043ccc4c6e28bb714c2fa31
+--- /dev/null
++++ b/src/test/java/io/papermc/paper/world/structure/ConfiguredStructureTest.java
+@@ -0,0 +1,92 @@
++package io.papermc.paper.world.structure;
++
++import io.papermc.paper.registry.Reference;
++import net.minecraft.data.BuiltinRegistries;
++import net.minecraft.resources.ResourceKey;
++import net.minecraft.resources.ResourceLocation;
++import net.minecraft.server.Bootstrap;
++import net.minecraft.world.level.levelgen.structure.Structure;
++import net.minecraft.world.level.levelgen.structure.BuiltinStructures;
++import org.bukkit.NamespacedKey;
++import org.bukkit.craftbukkit.util.CraftNamespacedKey;
++import org.bukkit.support.AbstractTestingBase;
++import org.junit.AfterClass;
++import org.junit.BeforeClass;
++import org.junit.Test;
++
++import java.io.PrintStream;
++import java.lang.reflect.Field;
++import java.lang.reflect.Modifier;
++import java.util.LinkedHashMap;
++import java.util.Map;
++import java.util.StringJoiner;
++
++import static org.junit.Assert.assertEquals;
++import static org.junit.Assert.assertNotNull;
++import static org.junit.Assert.assertTrue;
++
++public class ConfiguredStructureTest extends AbstractTestingBase {
++
++ private static final Map<ResourceLocation, String> BUILT_IN_STRUCTURES = new LinkedHashMap<>();
++ private static final Map<NamespacedKey, Reference<?>> DEFAULT_CONFIGURED_STRUCTURES = new LinkedHashMap<>();
++
++ private static PrintStream out;
++
++ @BeforeClass
++ public static void collectStructures() throws ReflectiveOperationException {
++ out = System.out;
++ System.setOut(Bootstrap.STDOUT);
++ for (Field field : BuiltinStructures.class.getDeclaredFields()) {
++ if (field.getType().equals(ResourceKey.class) && Modifier.isStatic(field.getModifiers())) {
++ BUILT_IN_STRUCTURES.put(((ResourceKey<?>) field.get(null)).location(), field.getName());
++ }
++ }
++ for (Field field : ConfiguredStructure.class.getDeclaredFields()) {
++ if (field.getType().equals(Reference.class) && Modifier.isStatic(field.getModifiers())) {
++ final Reference<?> ref = (Reference<?>) field.get(null);
++ DEFAULT_CONFIGURED_STRUCTURES.put(ref.getKey(), ref);
++ }
++ }
++ }
++
++ @Test
++ public void testMinecraftToApi() {
++ assertEquals("configured structure maps should be the same size", BUILT_IN_STRUCTURES.size(), BuiltinRegistries.STRUCTURES.size());
++
++ Map<ResourceLocation, Structure> missing = new LinkedHashMap<>();
++ for (Structure feature : BuiltinRegistries.STRUCTURES) {
++ final ResourceLocation key = BuiltinRegistries.STRUCTURES.getKey(feature);
++ assertNotNull("Missing built-in registry key", key);
++ if (key.equals(BuiltinStructures.ANCIENT_CITY.location())) {
++ continue; // TODO remove when upstream adds "jigsaw" StructureType
++ }
++ if (DEFAULT_CONFIGURED_STRUCTURES.get(CraftNamespacedKey.fromMinecraft(key)) == null) {
++ missing.put(key, feature);
++ }
++ }
++
++ assertTrue(printMissing(missing), missing.isEmpty());
++ }
++
++ @Test
++ public void testApiToMinecraft() {
++ for (NamespacedKey apiKey : DEFAULT_CONFIGURED_STRUCTURES.keySet()) {
++ assertTrue(apiKey + " does not have a minecraft counterpart", BuiltinRegistries.STRUCTURES.containsKey(CraftNamespacedKey.toMinecraft(apiKey)));
++ }
++ }
++
++ private static String printMissing(Map<ResourceLocation, Structure> missing) {
++ final StringJoiner joiner = new StringJoiner("\n", "Missing: \n", "");
++
++ missing.forEach((key, configuredFeature) -> {
++ joiner.add("public static final Reference<ConfiguredStructure> " + BUILT_IN_STRUCTURES.get(key) + " = create(\"" + key.getPath() + "\");");
++ });
++
++ return joiner.toString();
++ }
++
++ @AfterClass
++ public static void after() {
++ System.setOut(out);
++ }
++}