Structure Pool Callback (#1540)

* Structure Pool Callback

* Update build.gradle

* Update fabric-structure-api-v1/src/main/java/net/fabricmc/fabric/api/structure/v1/FabricStructurePool.java

Co-authored-by: liach <7806504+liach@users.noreply.github.com>

* Update fabric-structure-api-v1/src/main/java/net/fabricmc/fabric/api/structure/v1/FabricStructurePool.java

Co-authored-by: liach <7806504+liach@users.noreply.github.com>

* Fix certain concerns

* Expose identifier for easier access

* Final test mod thing

* fix access widener

* Fix a few things

* Fix a few things

* Fix license

* haha lets see if this works

* checkstyle was working after I did checkstyleMain so u bettar work

* i take too much time to do checkstyle fixes

* checkstyle fix testmod ugh amirite

* Fix a few concerns brought up <3

* Oops, forgot to add a mutanle there

* Let's test this one more time?

* Remove Access Widener woop woop

* rename to poolAccessor

* Simplify the pool modification a bit

Co-authored-by: liach <7806504+liach@users.noreply.github.com>

(cherry picked from commit 48a9ad892d)
This commit is contained in:
frqnny 2022-01-14 15:13:16 +00:00 committed by modmuss50
parent 12540453db
commit 0a73a6c22b
8 changed files with 274 additions and 0 deletions

View file

@ -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();
}
}

View file

@ -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.
*
* <p><strong>Word of warning</strong>: Mods may be editing on the structure pool from user configured data packs
* instead of the builtin Minecraft or mod resources.
*
* <p>Example usage:
* <pre>{@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));
* }
* });}
* </pre>
*/
public interface StructurePoolAddCallback {
/**
* Called when structure pools are reloaded at data pack reload time.
*/
Event<StructurePoolAddCallback> EVENT = EventFactory.createArrayBacked(StructurePoolAddCallback.class,
listeners -> initialPool -> {
for (StructurePoolAddCallback listener : listeners) {
listener.onAdd(initialPool);
}
}
);
void onAdd(FabricStructurePool initialPool);
}

View file

@ -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<Pair<StructurePoolElement, Integer>> 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;
}
}

View file

@ -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 <E> void load(RegistryOps<?> ops, DynamicRegistryManager manager, @Coerce Object info, CallbackInfo ci, RegistryKey<? extends Registry<E>> 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));
}
}
}
}
}

View file

@ -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<StructurePoolElement> getElements();
@Accessor(value = "elementCounts")
List<Pair<StructurePoolElement, Integer>> getElementCounts();
@Mutable
@Accessor(value = "elementCounts")
void setElementCounts(List<Pair<StructurePoolElement, Integer>> list);
}

View file

@ -4,8 +4,10 @@
"compatibilityLevel": "JAVA_16",
"mixins": [
"ChunkSerializerMixin",
"DynamicRegistryManagerMixin",
"FlatChunkGeneratorConfigMixin",
"StructureFeatureAccessor",
"StructurePoolAccessor",
"StructuresConfigAccessor"
],
"client": [

View file

@ -29,6 +29,9 @@ import net.minecraft.structure.ShiftableStructurePiece;
import net.minecraft.structure.StructureGeneratorFactory;
import net.minecraft.structure.StructurePieceType;
import net.minecraft.structure.StructurePiecesGenerator;
import net.minecraft.structure.pool.StructurePool;
import net.minecraft.structure.pool.StructurePoolElement;
import net.minecraft.structure.processor.StructureProcessorLists;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.BlockBox;
import net.minecraft.util.math.BlockPos;
@ -44,6 +47,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();
@ -61,6 +65,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<DefaultFeatureConfig> {