Paper/patches/server/0556-Add-StructuresLocateEvent.patch

215 lines
11 KiB
Diff
Raw Normal View History

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: dfsek <dfsek@protonmail.com>
Date: Wed, 16 Sep 2020 01:12:29 -0700
Subject: [PATCH] Add StructuresLocateEvent
Co-authored-by: Jake Potrebic <jake.m.potrebic@gmail.com>
diff --git a/src/main/java/io/papermc/paper/registry/RegistryKey.java b/src/main/java/io/papermc/paper/registry/RegistryKey.java
2022-06-08 09:30:41 +02:00
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;
2022-06-08 09:30:41 +02:00
+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) {
+
2022-06-08 09:30:41 +02:00
+ 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
2022-06-08 21:00:19 +02:00
index 0000000000000000000000000000000000000000..423bf87ebda7ea266dc7b48cbfadbc8551180721
--- /dev/null
+++ b/src/main/java/io/papermc/paper/world/structure/PaperConfiguredStructure.java
2022-06-08 21:00:19 +02:00
@@ -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;
2022-06-08 09:30:41 +02:00
+import net.minecraft.world.level.levelgen.structure.Structure;
+import org.bukkit.NamespacedKey;
+import org.bukkit.StructureType;
+import org.checkerframework.checker.nullness.qual.NonNull;
2022-06-08 21:00:19 +02:00
+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();
+ }
+
2022-06-08 09:30:41 +02:00
+ static final class ConfiguredStructureRegistry extends PaperRegistry<ConfiguredStructure, Structure> {
+
2022-06-08 09:30:41 +02:00
+ private static final Supplier<Registry<Structure>> STRUCTURE_FEATURE_REGISTRY = registryFor(Registry.STRUCTURE_REGISTRY);
+
+ public ConfiguredStructureRegistry() {
+ super(RegistryKey.CONFIGURED_STRUCTURE_REGISTRY);
+ }
+
+ @Override
2022-06-08 21:00:19 +02:00
+ public @Nullable ConfiguredStructure convertToApi(NamespacedKey key, Structure nms) {
2022-06-08 09:30:41 +02:00
+ final ResourceLocation structureTypeLoc = Objects.requireNonNull(Registry.STRUCTURE_TYPES.getKey(nms.type()), "unexpected structure type " + nms.type());
2022-06-08 21:00:19 +02:00
+ 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
Rewrite chunk system (#8177) Patch documentation to come Issues with the old system that are fixed now: - World generation does not scale with cpu cores effectively. - Relies on the main thread for scheduling and maintaining chunk state, dropping chunk load/generate rates at lower tps. - Unreliable prioritisation of chunk gen/load calls that block the main thread. - Shutdown logic is utterly unreliable, as it has to wait for all chunks to unload - is it guaranteed that the chunk system is in a state on shutdown that it can reliably do this? Watchdog shutdown also typically failed due to thread checks, which is now resolved. - Saving of data is not unified (i.e can save chunk data without saving entity data, poses problems for desync if shutdown is really abnormal. - Entities are not loaded with chunks. This caused quite a bit of headache for Chunk#getEntities API, but now the new chunk system loads entities with chunks so that they are ready whenever the chunk loads in. Effectively brings the behavior back to 1.16 era, but still storing entities in their own separate regionfiles. The above list is not complete. The patch documentation will complete it. New chunk system hard relies on starlight and dataconverter, and most importantly the new concurrent utilities in ConcurrentUtil. Some of the old async chunk i/o interface (i.e the old file io thread reroutes _some_ calls to the new file io thread) is kept for plugin compat reasons. It will be removed in the next major version of minecraft. The old legacy chunk system patches have been moved to the removed folder in case we need them again.
2022-09-26 10:02:51 +02:00
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
2022-06-08 09:30:41 +02:00
public Pair<BlockPos, Holder<Structure>> findNearestMapStructure(ServerLevel world, HolderSet<Structure> structures, BlockPos center, int radius, boolean skipReferencedStructures) {
+ // Paper start - StructureLocateEvent
2022-06-08 09:30:41 +02:00
+ 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<>();
2022-06-08 21:00:19 +02:00
+ 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
2022-06-08 09:30:41 +02:00
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
2022-06-08 09:30:41 +02:00
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
2022-06-08 21:00:19 +02:00
index 0000000000000000000000000000000000000000..61efebe1d363b34e2043ccc4c6e28bb714c2fa31
--- /dev/null
+++ b/src/test/java/io/papermc/paper/world/structure/ConfiguredStructureTest.java
2022-06-08 21:00:19 +02:00
@@ -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;
2022-06-08 09:30:41 +02:00
+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() {
2022-06-08 09:30:41 +02:00
+ assertEquals("configured structure maps should be the same size", BUILT_IN_STRUCTURES.size(), BuiltinRegistries.STRUCTURES.size());
+
2022-06-08 09:30:41 +02:00
+ 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);
2022-06-08 21:00:19 +02:00
+ 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()) {
2022-06-08 09:30:41 +02:00
+ assertTrue(apiKey + " does not have a minecraft counterpart", BuiltinRegistries.STRUCTURES.containsKey(CraftNamespacedKey.toMinecraft(apiKey)));
+ }
+ }
+
2022-06-08 09:30:41 +02:00
+ 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);
+ }
+}