diff --git a/fabric-structure-api-v1/src/main/java/net/fabricmc/fabric/api/structure/v1/FabricStructurePool.java b/fabric-structure-api-v1/src/main/java/net/fabricmc/fabric/api/structure/v1/FabricStructurePool.java
new file mode 100644
index 000000000..3a72304cc
--- /dev/null
+++ b/fabric-structure-api-v1/src/main/java/net/fabricmc/fabric/api/structure/v1/FabricStructurePool.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2016, 2017, 2018, 2019 FabricMC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.fabricmc.fabric.api.structure.v1;
+
+import net.minecraft.structure.pool.StructurePool;
+import net.minecraft.structure.pool.StructurePoolElement;
+import net.minecraft.util.Identifier;
+
+/**
+ * Represents a modifiable structure pool that has several helper methods for modders.
+ */
+public interface FabricStructurePool {
+ /**
+ * Adds a new {@link StructurePoolElement} to the {@link StructurePool}.
+ * See the alternative {@link #addStructurePoolElement(StructurePoolElement, int)} for details.
+ *
+ * @param element the element to add
+ */
+ void addStructurePoolElement(StructurePoolElement element);
+
+ /**
+ * Adds a new {@link StructurePoolElement} to the {@link StructurePool}.
+ * Its weight determines the amount of times an element is added to a list used for sampling during structure generation.
+ *
+ * @param element the element to add
+ * @param weight the weight of the element
+ */
+ void addStructurePoolElement(StructurePoolElement element, int weight);
+
+ /**
+ * Gets the underlying structure pool.
+ */
+ StructurePool getUnderlyingPool();
+
+ /**
+ * Gets the identifier for the pool.
+ */
+ default Identifier getId() {
+ return getUnderlyingPool().getId();
+ }
+}
diff --git a/fabric-structure-api-v1/src/main/java/net/fabricmc/fabric/api/structure/v1/StructurePoolAddCallback.java b/fabric-structure-api-v1/src/main/java/net/fabricmc/fabric/api/structure/v1/StructurePoolAddCallback.java
new file mode 100644
index 000000000..6db736d74
--- /dev/null
+++ b/fabric-structure-api-v1/src/main/java/net/fabricmc/fabric/api/structure/v1/StructurePoolAddCallback.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2016, 2017, 2018, 2019 FabricMC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.fabricmc.fabric.api.structure.v1;
+
+import net.fabricmc.fabric.api.event.Event;
+import net.fabricmc.fabric.api.event.EventFactory;
+
+/**
+ * A callback for newly added structure pools.
+ *
+ *
Word of warning: Mods may be editing on the structure pool from user configured data packs
+ * instead of the builtin Minecraft or mod resources.
+ *
+ *
Example usage:
+ *
{@code
+ * StructurePoolAddCallback.EVENT.register(structurePool -> {
+ * if (structurePool.getId().equals(new Identifier("minecraft:village/desert/houses"))) {
+ * structurePool.addStructurePoolElement(StructurePoolElement.ofProcessedLegacySingle("fabric:cactus_farm", StructureProcessorLists.FARM_PLAINS).apply(StructurePool.Projection.RIGID));
+ * }
+ * });}
+ *
+ */
+public interface StructurePoolAddCallback {
+ /**
+ * Called when structure pools are reloaded at data pack reload time.
+ */
+ Event EVENT = EventFactory.createArrayBacked(StructurePoolAddCallback.class,
+ listeners -> initialPool -> {
+ for (StructurePoolAddCallback listener : listeners) {
+ listener.onAdd(initialPool);
+ }
+ }
+ );
+
+ void onAdd(FabricStructurePool initialPool);
+}
diff --git a/fabric-structure-api-v1/src/main/java/net/fabricmc/fabric/impl/structure/FabricStructurePoolImpl.java b/fabric-structure-api-v1/src/main/java/net/fabricmc/fabric/impl/structure/FabricStructurePoolImpl.java
new file mode 100644
index 000000000..0bab8b484
--- /dev/null
+++ b/fabric-structure-api-v1/src/main/java/net/fabricmc/fabric/impl/structure/FabricStructurePoolImpl.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2016, 2017, 2018, 2019 FabricMC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.fabricmc.fabric.impl.structure;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.google.common.collect.ImmutableList;
+import com.mojang.datafixers.util.Pair;
+
+import net.minecraft.structure.pool.StructurePool;
+import net.minecraft.structure.pool.StructurePoolElement;
+
+import net.fabricmc.fabric.api.structure.v1.FabricStructurePool;
+import net.fabricmc.fabric.mixin.structure.StructurePoolAccessor;
+
+public class FabricStructurePoolImpl implements FabricStructurePool {
+ private final StructurePool pool;
+
+ public FabricStructurePoolImpl(StructurePool pool) {
+ this.pool = pool;
+ }
+
+ @Override
+ public void addStructurePoolElement(StructurePoolElement element) {
+ addStructurePoolElement(element, 1);
+ }
+
+ @Override
+ public void addStructurePoolElement(StructurePoolElement element, int weight) {
+ if (weight <= 0) {
+ throw new IllegalArgumentException("weight must be positive");
+ }
+
+ //adds to elementCounts list; minecraft makes these immutable lists, so we temporarily replace them with an array list
+ StructurePoolAccessor poolAccessor = (StructurePoolAccessor) getUnderlyingPool();
+
+ List> list = new ArrayList<>(poolAccessor.getElementCounts());
+ list.add(Pair.of(element, weight));
+ poolAccessor.setElementCounts(ImmutableList.copyOf(list));
+
+ //adds to elements list
+ for (int i = 0; i < weight; i++) {
+ poolAccessor.getElements().add(element);
+ }
+ }
+
+ @Override
+ public StructurePool getUnderlyingPool() {
+ return pool;
+ }
+}
diff --git a/fabric-structure-api-v1/src/main/java/net/fabricmc/fabric/mixin/structure/DynamicRegistryManagerMixin.java b/fabric-structure-api-v1/src/main/java/net/fabricmc/fabric/mixin/structure/DynamicRegistryManagerMixin.java
new file mode 100644
index 000000000..687f64a24
--- /dev/null
+++ b/fabric-structure-api-v1/src/main/java/net/fabricmc/fabric/mixin/structure/DynamicRegistryManagerMixin.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2016, 2017, 2018, 2019 FabricMC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.fabricmc.fabric.mixin.structure;
+
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Coerce;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
+import org.spongepowered.asm.mixin.Mixin;
+
+import net.minecraft.structure.pool.StructurePool;
+import net.minecraft.util.dynamic.RegistryOps;
+import net.minecraft.util.registry.DynamicRegistryManager;
+import net.minecraft.util.registry.Registry;
+import net.minecraft.util.registry.RegistryKey;
+
+import net.fabricmc.fabric.api.structure.v1.StructurePoolAddCallback;
+import net.fabricmc.fabric.impl.structure.FabricStructurePoolImpl;
+
+@Mixin(DynamicRegistryManager.class)
+public abstract class DynamicRegistryManagerMixin {
+ @Inject(method = "load(Lnet/minecraft/util/dynamic/RegistryOps;Lnet/minecraft/util/registry/DynamicRegistryManager;Lnet/minecraft/util/registry/DynamicRegistryManager$Info;)V", at = @At("TAIL"), locals = LocalCapture.CAPTURE_FAILHARD)
+ private static void load(RegistryOps> ops, DynamicRegistryManager manager, @Coerce Object info, CallbackInfo ci, RegistryKey extends Registry> registryKey) {
+ if (registryKey.equals(Registry.STRUCTURE_POOL_KEY)) {
+ for (E registryEntry : manager.get(registryKey)) {
+ if (registryEntry instanceof StructurePool pool) {
+ StructurePoolAddCallback.EVENT.invoker().onAdd(new FabricStructurePoolImpl(pool));
+ }
+ }
+ }
+ }
+}
diff --git a/fabric-structure-api-v1/src/main/java/net/fabricmc/fabric/mixin/structure/StructurePoolAccessor.java b/fabric-structure-api-v1/src/main/java/net/fabricmc/fabric/mixin/structure/StructurePoolAccessor.java
new file mode 100644
index 000000000..074b6cee9
--- /dev/null
+++ b/fabric-structure-api-v1/src/main/java/net/fabricmc/fabric/mixin/structure/StructurePoolAccessor.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2016, 2017, 2018, 2019 FabricMC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.fabricmc.fabric.mixin.structure;
+
+import java.util.List;
+
+import com.mojang.datafixers.util.Pair;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Mutable;
+import org.spongepowered.asm.mixin.gen.Accessor;
+
+import net.minecraft.structure.pool.StructurePool;
+import net.minecraft.structure.pool.StructurePoolElement;
+
+@Mixin(StructurePool.class)
+public interface StructurePoolAccessor {
+ @Accessor(value = "elements")
+ List getElements();
+
+ @Accessor(value = "elementCounts")
+ List> getElementCounts();
+
+ @Mutable
+ @Accessor(value = "elementCounts")
+ void setElementCounts(List> list);
+}
diff --git a/fabric-structure-api-v1/src/main/resources/fabric-structure-api-v1.mixins.json b/fabric-structure-api-v1/src/main/resources/fabric-structure-api-v1.mixins.json
index 501a06c9e..3c75ae32e 100644
--- a/fabric-structure-api-v1/src/main/resources/fabric-structure-api-v1.mixins.json
+++ b/fabric-structure-api-v1/src/main/resources/fabric-structure-api-v1.mixins.json
@@ -4,8 +4,10 @@
"compatibilityLevel": "JAVA_16",
"mixins": [
"ChunkSerializerMixin",
+ "DynamicRegistryManagerMixin",
"FlatChunkGeneratorConfigAccessor",
"StructureFeatureAccessor",
+ "StructurePoolAccessor",
"StructuresConfigAccessor"
],
"client": [
diff --git a/fabric-structure-api-v1/src/testmod/java/net/fabricmc/fabric/test/structure/StructureTest.java b/fabric-structure-api-v1/src/testmod/java/net/fabricmc/fabric/test/structure/StructureTest.java
index c6b957ea3..58962a13c 100644
--- a/fabric-structure-api-v1/src/testmod/java/net/fabricmc/fabric/test/structure/StructureTest.java
+++ b/fabric-structure-api-v1/src/testmod/java/net/fabricmc/fabric/test/structure/StructureTest.java
@@ -24,6 +24,9 @@ import org.apache.logging.log4j.Logger;
import net.minecraft.block.Blocks;
import net.minecraft.nbt.NbtCompound;
+import net.minecraft.structure.pool.StructurePool;
+import net.minecraft.structure.pool.StructurePoolElement;
+import net.minecraft.structure.processor.StructureProcessorLists;
import net.minecraft.structure.StructureManager;
import net.minecraft.structure.StructurePieceType;
import net.minecraft.structure.StructurePieceWithDimensions;
@@ -47,6 +50,7 @@ import net.minecraft.world.gen.feature.DefaultFeatureConfig;
import net.minecraft.world.gen.feature.StructureFeature;
import net.fabricmc.fabric.api.structure.v1.FabricStructureBuilder;
+import net.fabricmc.fabric.api.structure.v1.StructurePoolAddCallback;
public class StructureTest {
private static final Logger LOGGER = LogManager.getLogger();
@@ -64,6 +68,16 @@ public class StructureTest {
.adjustsSurface()
.register();
Registry.register(Registry.STRUCTURE_PIECE, new Identifier("fabric", "test_structure_piece"), PIECE);
+
+ //Basic Test of Callback
+ StructurePoolAddCallback.EVENT.register(structurePool -> LOGGER.info("Structure pool {} added", structurePool.getId()));
+
+ //The ideal usage of this callback is to add structures to a Village. Here, I constructed a Cactus Farm, which will be added to the house pool for deserts. For testing purposes, we will make it very common, and use a plains-style log outline so it is clear that it doesn't belong.
+ StructurePoolAddCallback.EVENT.register(structurePool -> {
+ if (structurePool.getId().equals(new Identifier("minecraft:village/desert/houses"))) {
+ structurePool.addStructurePoolElement(StructurePoolElement.ofProcessedLegacySingle("fabric:cactus_farm", StructureProcessorLists.FARM_PLAINS).apply(StructurePool.Projection.RIGID));
+ }
+ });
}
public static class TestStructureFeature extends StructureFeature {
diff --git a/fabric-structure-api-v1/src/testmod/resources/data/fabric/structures/cactus_farm.nbt b/fabric-structure-api-v1/src/testmod/resources/data/fabric/structures/cactus_farm.nbt
new file mode 100644
index 000000000..8a559dcb1
Binary files /dev/null and b/fabric-structure-api-v1/src/testmod/resources/data/fabric/structures/cactus_farm.nbt differ