From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: dfsek Date: Wed, 16 Sep 2020 01:12:29 -0700 Subject: [PATCH] Add StructuresLocateEvent Co-authored-by: Jake Potrebic diff --git a/src/main/java/io/papermc/paper/registry/PaperRegistries.java b/src/main/java/io/papermc/paper/registry/PaperRegistries.java index 5b6d0c5c788bfd158494a88665a2b9b8c45a9ffe..51979b3c3f1f3a3c63e0559c70bed9193fd35dbb 100644 --- a/src/main/java/io/papermc/paper/registry/PaperRegistries.java +++ b/src/main/java/io/papermc/paper/registry/PaperRegistries.java @@ -46,6 +46,12 @@ import static io.papermc.paper.registry.entry.RegistryEntry.entry; @DefaultQualifier(NonNull.class) public final class PaperRegistries { + @Deprecated(forRemoval = true) + @org.jetbrains.annotations.VisibleForTesting + public static final RegistryKey CONFIGURED_STRUCTURE_REGISTRY_KEY = RegistryKeyImpl.createInternal("worldgen/structure"); + @Deprecated(forRemoval = true) + static final RegistryEntry CONFIGURED_STRUCTURE_REGISTRY_ENTRY = entry(Registries.STRUCTURE, CONFIGURED_STRUCTURE_REGISTRY_KEY, io.papermc.paper.world.structure.ConfiguredStructure.class, io.papermc.paper.world.structure.PaperConfiguredStructure::minecraftToBukkit).delayed(); + static final List> REGISTRY_ENTRIES; private static final Map, RegistryEntry> BY_REGISTRY_KEY; private static final Map, RegistryEntry> BY_RESOURCE_KEY; diff --git a/src/main/java/io/papermc/paper/registry/PaperRegistryAccess.java b/src/main/java/io/papermc/paper/registry/PaperRegistryAccess.java index 9f2bcfe0d9e479466a1e46e503071d1151310e6a..7aa1a29e93b787e1167169bc7d6e9563daf6241e 100644 --- a/src/main/java/io/papermc/paper/registry/PaperRegistryAccess.java +++ b/src/main/java/io/papermc/paper/registry/PaperRegistryAccess.java @@ -45,8 +45,13 @@ public class PaperRegistryAccess implements RegistryAccess { public @Nullable Registry getRegistry(final Class type) { final RegistryKey registryKey; final @Nullable RegistryEntry entry; - registryKey = requireNonNull(byType(type), () -> type + " is not a valid registry type"); - entry = PaperRegistries.getEntry(registryKey); + if (type == io.papermc.paper.world.structure.ConfiguredStructure.class) { // manually handle "duplicate" registries to avoid polluting maps in PaperRegistries + registryKey = (RegistryKey) PaperRegistries.CONFIGURED_STRUCTURE_REGISTRY_KEY; + entry = (RegistryEntry) PaperRegistries.CONFIGURED_STRUCTURE_REGISTRY_ENTRY; + } else { + registryKey = requireNonNull(byType(type), () -> type + " is not a valid registry type"); + entry = PaperRegistries.getEntry(registryKey); + } final @Nullable RegistryHolder registry = (RegistryHolder) this.registries.get(registryKey); if (registry != null) { // if the registry exists, return right away. Since this is the "legacy" method, we return DelayedRegistry 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..013d614a1cf1ab2b5a6ec190c2b4ba7753268731 --- /dev/null +++ b/src/main/java/io/papermc/paper/world/structure/PaperConfiguredStructure.java @@ -0,0 +1,27 @@ +package io.papermc.paper.world.structure; + +import java.util.Objects; +import net.minecraft.core.Registry; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.levelgen.structure.Structure; +import org.bukkit.NamespacedKey; +import org.bukkit.StructureType; +import org.bukkit.craftbukkit.CraftRegistry; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.framework.qual.DefaultQualifier; + +@DefaultQualifier(NonNull.class) +@Deprecated(forRemoval = true) +public final class PaperConfiguredStructure { + + private PaperConfiguredStructure() { + } + + public static @Nullable ConfiguredStructure minecraftToBukkit(NamespacedKey key, Structure nms) { + final ResourceLocation structureTypeLoc = Objects.requireNonNull(BuiltInRegistries.STRUCTURE_TYPE.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 7cdb59cd2f2ffe1195d21519ef97dae0e430285b..a8768f1925d5824ca985be1b53694ee233273758 100644 --- a/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java +++ b/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java @@ -127,6 +127,24 @@ public abstract class ChunkGenerator { @Nullable public Pair> findNearestMapStructure(ServerLevel world, HolderSet structures, BlockPos center, int radius, boolean skipReferencedStructures) { + // Paper start - StructuresLocateEvent + final org.bukkit.World bukkitWorld = world.getWorld(); + final org.bukkit.Location origin = io.papermc.paper.util.MCUtil.toLocation(world, center); + final List apiStructures = structures.stream().map(Holder::value).map(nms -> org.bukkit.craftbukkit.generator.structure.CraftStructure.minecraftToBukkit(nms)).toList(); + if (!apiStructures.isEmpty()) { + final io.papermc.paper.event.world.StructuresLocateEvent event = new io.papermc.paper.event.world.StructuresLocateEvent(bukkitWorld, origin, apiStructures, radius, skipReferencedStructures); + if (!event.callEvent()) { + return null; + } + if (event.getResult() != null) { + return Pair.of(io.papermc.paper.util.MCUtil.toBlockPos(event.getResult().pos()), world.registryAccess().registryOrThrow(Registries.STRUCTURE).wrapAsHolder(org.bukkit.craftbukkit.generator.structure.CraftStructure.bukkitToMinecraft(event.getResult().structure()))); + } + center = io.papermc.paper.util.MCUtil.toBlockPosition(event.getOrigin()); + radius = event.getRadius(); + skipReferencedStructures = event.shouldFindUnexplored(); + structures = HolderSet.direct(api -> world.registryAccess().registryOrThrow(Registries.STRUCTURE).wrapAsHolder(org.bukkit.craftbukkit.generator.structure.CraftStructure.bukkitToMinecraft(api)), event.getStructures()); + } + // Paper end ChunkGeneratorStructureState chunkgeneratorstructurestate = world.getChunkSource().getGeneratorState(); Map>> map = new Object2ObjectArrayMap(); Iterator iterator = structures.iterator(); 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..9178fe0d01b998ca1442bf2511f8fc00db9388ba --- /dev/null +++ b/src/test/java/io/papermc/paper/world/structure/ConfiguredStructureTest.java @@ -0,0 +1,96 @@ +package io.papermc.paper.world.structure; + +import io.papermc.paper.registry.Reference; +import net.minecraft.core.Registry; +import net.minecraft.core.registries.Registries; +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.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.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.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +@Deprecated(forRemoval = true) +public class ConfiguredStructureTest extends AbstractTestingBase { + + private static final Map BUILT_IN_STRUCTURES = new LinkedHashMap<>(); + private static final Map> DEFAULT_CONFIGURED_STRUCTURES = new LinkedHashMap<>(); + + private static PrintStream out; + + @BeforeAll + 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() { + Registry structureRegistry = AbstractTestingBase.REGISTRY_CUSTOM.registryOrThrow(Registries.STRUCTURE); + assertEquals(BUILT_IN_STRUCTURES.size(), structureRegistry.size(), "configured structure maps should be the same size"); + + Map missing = new LinkedHashMap<>(); + for (Structure feature : structureRegistry) { + final ResourceLocation key = structureRegistry.getKey(feature); + assertNotNull(key, "Missing built-in registry key"); + if (key.equals(BuiltinStructures.ANCIENT_CITY.location()) || key.equals(BuiltinStructures.TRAIL_RUINS.location()) || key.equals(BuiltinStructures.TRIAL_CHAMBERS.location())) { + continue; // TODO remove when upstream adds "jigsaw" StructureType + } + if (DEFAULT_CONFIGURED_STRUCTURES.get(CraftNamespacedKey.fromMinecraft(key)) == null) { + missing.put(key, feature); + } + } + + assertTrue(missing.isEmpty(), printMissing(missing)); + } + + @Test + public void testApiToMinecraft() { + Registry structureRegistry = AbstractTestingBase.REGISTRY_CUSTOM.registryOrThrow(Registries.STRUCTURE); + for (NamespacedKey apiKey : DEFAULT_CONFIGURED_STRUCTURES.keySet()) { + assertTrue(structureRegistry.containsKey(CraftNamespacedKey.toMinecraft(apiKey)), apiKey + " does not have a minecraft counterpart"); + } + } + + private static String printMissing(Map missing) { + final StringJoiner joiner = new StringJoiner("\n", "Missing: \n", ""); + + missing.forEach((key, configuredFeature) -> { + joiner.add("public static final Reference " + BUILT_IN_STRUCTURES.get(key) + " = create(\"" + key.getPath() + "\");"); + }); + + return joiner.toString(); + } + + @AfterAll + public static void after() { + System.setOut(out); + } +} diff --git a/src/test/java/org/bukkit/registry/PerRegistryTest.java b/src/test/java/org/bukkit/registry/PerRegistryTest.java index 4e4ea083063daf22f1bb785ef212958ea889c43b..523b4b208e05c6b70014440200e3196cc84f36cc 100644 --- a/src/test/java/org/bukkit/registry/PerRegistryTest.java +++ b/src/test/java/org/bukkit/registry/PerRegistryTest.java @@ -36,6 +36,7 @@ public class PerRegistryTest extends AbstractTestingBase { if (!(object instanceof CraftRegistry registry)) { continue; } + if (object == Registry.CONFIGURED_STRUCTURE) continue; // Paper - skip data.add(Arguments.of(registry)); } catch (ReflectiveOperationException e) { diff --git a/src/test/java/org/bukkit/registry/RegistryArgumentAddedTest.java b/src/test/java/org/bukkit/registry/RegistryArgumentAddedTest.java index 0dd775ad1bd0bf9ba7ea05255d543a9df8b5fcfd..c1e51d104c52dd3f3e48d651b0ff1f00ae9bf96d 100644 --- a/src/test/java/org/bukkit/registry/RegistryArgumentAddedTest.java +++ b/src/test/java/org/bukkit/registry/RegistryArgumentAddedTest.java @@ -26,6 +26,7 @@ public class RegistryArgumentAddedTest extends AbstractTestingBase { loadedRegistries.addAll(io.papermc.paper.registry.PaperRegistryAccess.instance().getLoadedServerBackedRegistries()); // Paper end Set> notFound = new HashSet<>(); // Paper + loadedRegistries.remove(io.papermc.paper.registry.PaperRegistries.CONFIGURED_STRUCTURE_REGISTRY_KEY); // Paper - ignore RegistriesArgumentProvider .getData()