Structures API ()

* Initial structures API implementation

* Improve generics + add a superflatFeature helper method

* Initialize StructuresConfig class early to prevent its assertion failing

* Add a testmod

* Documentation and null assertions

* Apply review suggestions

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

Co-authored-by: shartte <shartte@users.noreply.github.com>

* Apply review suggestions

* Update to 1.16.2

Co-authored-by: shartte <shartte@users.noreply.github.com>
This commit is contained in:
Joseph Burton 2020-08-21 17:22:57 +01:00 committed by GitHub
parent e2e6cdad60
commit 516ece7c6e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 743 additions and 0 deletions

View file

@ -0,0 +1,2 @@
archivesBaseName = "fabric-structure-api-v1"
version = getSubprojectVersion(project, "1.0.0")

View file

@ -0,0 +1,228 @@
/*
* 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 java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import net.minecraft.util.Identifier;
import net.minecraft.util.registry.BuiltinRegistries;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.gen.GenerationStep;
import net.minecraft.world.gen.chunk.StructureConfig;
import net.minecraft.world.gen.chunk.StructuresConfig;
import net.minecraft.world.gen.feature.ConfiguredStructureFeature;
import net.minecraft.world.gen.feature.FeatureConfig;
import net.minecraft.world.gen.feature.StructureFeature;
import net.fabricmc.fabric.impl.structure.FabricStructureUtil;
import net.fabricmc.fabric.impl.structure.StructuresConfigHooks;
import net.fabricmc.fabric.mixin.structure.BiomeAccessor;
import net.fabricmc.fabric.mixin.structure.FlatChunkGeneratorConfigAccessor;
import net.fabricmc.fabric.mixin.structure.StructureFeatureAccessor;
import net.fabricmc.fabric.mixin.structure.StructuresConfigAccessor;
/**
* A builder for registering custom structures.
*
* <p>Example usage:
* <pre>{@code
* StructureFeature structure = new MyStructure(DefaultFeatureConfig.CODEC);
* ConfiguredStructureFeature<DefaultFeatureConfig, ? extends StructureFeature<DefaultFeatureConfig>> configuredStructure
* = structure.configure(new DefaultFeatureConfig());
* FabricStructureBuilder.create(new Identifier("mymod:mystructure"), structure)
* .step(GenerationStep.Feature.SURFACE_STRUCTURES) // required
* .defaultConfig(32, 8, 12345) // required
* .superflatFeature(configuredStructure)
* .register();}
* </pre></p>
*
* <p>This class does <i>not</i> add structures to biomes for you, you have to do that yourself. You may also need to
* register custom structure pieces yourself.</p>
*/
public final class FabricStructureBuilder<FC extends FeatureConfig, S extends StructureFeature<FC>> {
private final Identifier id;
private final S structure;
private GenerationStep.Feature step;
private StructureConfig defaultConfig;
private ConfiguredStructureFeature<FC, ? extends StructureFeature<FC>> superflatFeature;
private boolean adjustsSurface = false;
private FabricStructureBuilder(Identifier id, S structure) {
this.id = id;
this.structure = structure;
}
/**
* Creates a new {@code FabricStructureBuilder} for registering a structure.
*
* @param id The structure ID.
* @param structure The {@linkplain StructureFeature} you want to register.
*/
public static <FC extends FeatureConfig, S extends StructureFeature<FC>> FabricStructureBuilder<FC, S> create(Identifier id, S structure) {
Objects.requireNonNull(id, "id must not be null");
Objects.requireNonNull(structure, "structure must not be null");
return new FabricStructureBuilder<>(id, structure);
}
/**
* Sets the generation step of this structure. The generation step specifies when the structure is generated, to
* ensure they are generated in the correct order to reduce the amount of floating blocks.
*
* <p>The most commonly used values for structures are {@linkplain GenerationStep.Feature#SURFACE_STRUCTURES} and
* {@linkplain GenerationStep.Feature#UNDERGROUND_STRUCTURES}, however technically any value in the
* {@linkplain GenerationStep.Feature} enum may be used.</p>
*
* <p>This is a required option.</p>
*/
public FabricStructureBuilder<FC, S> step(GenerationStep.Feature step) {
Objects.requireNonNull(step, "step must not be null");
this.step = step;
return this;
}
/**
* Sets the default {@linkplain StructureConfig} for this structure. See the alternative
* {@linkplain #defaultConfig(int, int, int)} for details.
*
* <p>This is a required option.</p>
*/
public FabricStructureBuilder<FC, S> defaultConfig(StructureConfig config) {
Objects.requireNonNull(config, "config must not be null");
this.defaultConfig = config;
return this;
}
/**
* Sets the default {@linkplain StructureConfig} for this structure. This sets the default configuration of where in
* the world to place structures.
*
* <p>Note: the {@code spacing} and {@code separation} options are subject to other checks for whether the structure
* can spawn, such as biome. If these checks always pass and the structure can spawn in every biome, then the
* description of these values below would be exactly correct.</p>
*
* <p>This is a required option. Vanilla needs it to function.</p>
*
* @param spacing The average distance between 2 structures of this type along the X and Z axes.
* @param separation The minimum distance between 2 structures of this type.
* @param salt The random salt of the structure. This does not affect how common the structure is, but every
* structure must have an unique {@code salt} in order to spawn in different places.
*
* @see #defaultConfig(StructureConfig)
*/
public FabricStructureBuilder<FC, S> defaultConfig(int spacing, int separation, int salt) {
return defaultConfig(new StructureConfig(spacing, separation, salt));
}
/**
* Sets the structure configuration which spawns in superflat worlds. If unset, this structure will not spawn in
* superflat worlds.
*
* @see #superflatFeature(FeatureConfig)
*/
public FabricStructureBuilder<FC, S> superflatFeature(ConfiguredStructureFeature<FC, ? extends StructureFeature<FC>> superflatFeature) {
Objects.requireNonNull(superflatFeature, "superflatFeature must not be null");
this.superflatFeature = superflatFeature;
return this;
}
/**
* Sets the structure configuration which spawns in superflat worlds. If unset, this structure will not spawn in
* superflat worlds.
*
* @see #superflatFeature(ConfiguredStructureFeature)
*/
public FabricStructureBuilder<FC, S> superflatFeature(FC config) {
return superflatFeature(structure.configure(config));
}
/**
* Causes structure pieces of this structure to adjust the surface of the world to fit them, so that they don't
* stick out of or into the ground.
*/
public FabricStructureBuilder<FC, S> adjustsSurface() {
this.adjustsSurface = true;
return this;
}
/**
* Registers this structure and applies the other changes from the {@linkplain FabricStructureBuilder}.
*/
public S register() {
Objects.requireNonNull(step, "Structure \"" + id + "\" is missing a generation step");
Objects.requireNonNull(defaultConfig, "Structure \"" + id + "\" is missing a default config");
// Ensure StructuresConfig class is initialized, so the assertion in its static {} block doesn't fail
StructuresConfig.DEFAULT_STRUCTURES.size();
StructureFeatureAccessor.callRegister(id.toString(), structure, step);
if (!id.toString().equals(structure.getName())) {
// mods should not be overriding getName, but if they do and it's incorrect, this gives an error
throw new IllegalStateException(String.format("Structure \"%s\" has mismatching name \"%s\". Structures should not override \"getName\".", id, structure.getName()));
}
StructuresConfigAccessor.setDefaultStructures(ImmutableMap.<StructureFeature<?>, StructureConfig>builder()
.putAll(StructuresConfig.DEFAULT_STRUCTURES)
.put(structure, defaultConfig)
.build());
if (superflatFeature != null) {
FlatChunkGeneratorConfigAccessor.getStructureToFeatures().put(structure, superflatFeature);
}
if (adjustsSurface) {
StructureFeatureAccessor.setSurfaceAdjustingStructures(ImmutableList.<StructureFeature<?>>builder()
.addAll(StructureFeature.field_24861)
.add(structure)
.build());
}
// update existing structures configs
for (StructuresConfig structuresConfig : FabricStructureUtil.DEFAULT_STRUCTURES_CONFIGS) {
((StructuresConfigHooks) structuresConfig).fabric_updateDefaultEntries();
}
// update builtin biomes, just to be safe
for (Biome biome : BuiltinRegistries.BIOME) {
BiomeAccessor biomeAccessor = (BiomeAccessor) (Object) biome;
Map<Integer, List<StructureFeature<?>>> structureLists = biomeAccessor.getStructureLists();
if (!(structureLists instanceof HashMap)) {
// not guaranteed by the standard to be a mutable map
((BiomeAccessor) (Object) biome).setStructureLists(structureLists = new HashMap<>(structureLists));
}
// not guaranteed by the standard to be mutable lists
structureLists.compute(step.ordinal(), (k, v) -> makeMutable(v)).add(structure);
}
return structure;
}
private static List<StructureFeature<?>> makeMutable(List<StructureFeature<?>> mapValue) {
if (mapValue == null) return new ArrayList<>();
if (!(mapValue instanceof ArrayList)) return new ArrayList<>(mapValue);
return mapValue;
}
}

View file

@ -0,0 +1,31 @@
/*
* 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.Collections;
import java.util.Set;
import java.util.WeakHashMap;
import net.minecraft.world.gen.chunk.StructuresConfig;
public final class FabricStructureUtil {
private FabricStructureUtil() { }
// This tracks all StructuresConfig objects that have been created with the default set of structures
// in order to add mod-created structures that are registered later
public static final Set<StructuresConfig> DEFAULT_STRUCTURES_CONFIGS = Collections.newSetFromMap(new WeakHashMap<>());
}

View file

@ -0,0 +1,21 @@
/*
* 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;
public interface StructuresConfigHooks {
void fabric_updateDefaultEntries();
}

View file

@ -0,0 +1,37 @@
/*
* 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 java.util.Map;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Mutable;
import org.spongepowered.asm.mixin.gen.Accessor;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.gen.feature.StructureFeature;
@Mixin(Biome.class)
public interface BiomeAccessor {
@Accessor("field_26634")
Map<Integer, List<StructureFeature<?>>> getStructureLists();
@Mutable
@Accessor("field_26634")
void setStructureLists(Map<Integer, List<StructureFeature<?>>> field_26634);
}

View file

@ -0,0 +1,34 @@
/*
* 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.Map;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
import net.minecraft.world.gen.chunk.FlatChunkGeneratorConfig;
import net.minecraft.world.gen.feature.ConfiguredStructureFeature;
import net.minecraft.world.gen.feature.StructureFeature;
@Mixin(FlatChunkGeneratorConfig.class)
public interface FlatChunkGeneratorConfigAccessor {
@Accessor("STRUCTURE_TO_FEATURES")
static Map<StructureFeature<?>, ConfiguredStructureFeature<?, ?>> getStructureToFeatures() {
throw new AssertionError("Untransformed accessor");
}
}

View file

@ -0,0 +1,34 @@
/*
* 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.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import net.minecraft.world.gen.chunk.ChunkGeneratorSettings;
import net.fabricmc.fabric.impl.structure.FabricStructureUtil;
@Mixin(ChunkGeneratorSettings.class)
public class MixinChunkGeneratorSettings {
@Inject(method = "createUndergroundSettings", at = @At("RETURN"))
private static void onCreateCavesType(CallbackInfoReturnable<ChunkGeneratorSettings> cir) {
FabricStructureUtil.DEFAULT_STRUCTURES_CONFIGS.add(cir.getReturnValue().getStructuresConfig());
}
}

View file

@ -0,0 +1,53 @@
/*
* 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.Map;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import net.minecraft.world.gen.chunk.StructureConfig;
import net.minecraft.world.gen.chunk.StructuresConfig;
import net.minecraft.world.gen.feature.StructureFeature;
import net.fabricmc.fabric.impl.structure.FabricStructureUtil;
import net.fabricmc.fabric.impl.structure.StructuresConfigHooks;
@Mixin(StructuresConfig.class)
public class MixinStructuresConfig implements StructuresConfigHooks {
@Shadow
@Final
private Map<StructureFeature<?>, StructureConfig> structures;
// This constructor of StructuresConfig initializes it with the default set of structures.
// Since a mod can register its structures later, we need to keep track of the object created
// here, so that we can add new structures to it later.
@Inject(method = "<init>(Z)V", at = @At("RETURN"))
private void onDefaultInit(CallbackInfo ci) {
FabricStructureUtil.DEFAULT_STRUCTURES_CONFIGS.add((StructuresConfig) (Object) this);
}
@Override
public void fabric_updateDefaultEntries() {
StructuresConfig.DEFAULT_STRUCTURES.forEach(structures::putIfAbsent);
}
}

View file

@ -0,0 +1,41 @@
/*
* 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 org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Mutable;
import org.spongepowered.asm.mixin.gen.Accessor;
import org.spongepowered.asm.mixin.gen.Invoker;
import net.minecraft.world.gen.GenerationStep;
import net.minecraft.world.gen.feature.StructureFeature;
@Mixin(StructureFeature.class)
public interface StructureFeatureAccessor {
@Accessor("field_24861")
@Mutable
static void setSurfaceAdjustingStructures(List<StructureFeature<?>> surfaceAdjustingStructures) {
throw new AssertionError("Untransformed accessor");
}
@Invoker
static <F extends StructureFeature<?>> F callRegister(String name, F structureFeature, GenerationStep.Feature step) {
throw new AssertionError("Untransformed accessor");
}
}

View file

@ -0,0 +1,35 @@
/*
* 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 com.google.common.collect.ImmutableMap;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Mutable;
import org.spongepowered.asm.mixin.gen.Accessor;
import net.minecraft.world.gen.chunk.StructureConfig;
import net.minecraft.world.gen.chunk.StructuresConfig;
import net.minecraft.world.gen.feature.StructureFeature;
@Mixin(StructuresConfig.class)
public interface StructuresConfigAccessor {
@Mutable
@Accessor("DEFAULT_STRUCTURES")
static void setDefaultStructures(ImmutableMap<StructureFeature<?>, StructureConfig> defaultStructures) {
throw new AssertionError("Untransformed accessor");
}
}

View file

@ -0,0 +1,18 @@
{
"required": true,
"package": "net.fabricmc.fabric.mixin.structure",
"compatibilityLevel": "JAVA_8",
"mixins": [
"BiomeAccessor",
"FlatChunkGeneratorConfigAccessor",
"MixinChunkGeneratorSettings",
"MixinStructuresConfig",
"StructureFeatureAccessor",
"StructuresConfigAccessor"
],
"client": [
],
"injectors": {
"defaultRequire": 1
}
}

View file

@ -0,0 +1,26 @@
{
"schemaVersion": 1,
"id": "fabric-structure-api-v1",
"name": "Fabric Structure API (v1)",
"version": "${version}",
"environment": "*",
"license": "Apache-2.0",
"icon": "assets/fabric-structure-api-v1/icon.png",
"contact": {
"homepage": "https://fabricmc.net",
"irc": "irc://irc.esper.net:6667/fabric",
"issues": "https://github.com/FabricMC/fabric/issues",
"sources": "https://github.com/FabricMC/fabric"
},
"authors": [
"FabricMC"
],
"depends": {
"fabricloader": ">=0.8.0",
"fabric-api-base": "*"
},
"description": "Hooks for registering custom structures.",
"mixins": [
"fabric-structure-api-v1.mixins.json"
]
}

View file

@ -0,0 +1,117 @@
/*
* 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.test.structure;
import java.util.Random;
import com.mojang.serialization.Codec;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import net.minecraft.block.Blocks;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.structure.StructureManager;
import net.minecraft.structure.StructurePieceType;
import net.minecraft.structure.StructurePieceWithDimensions;
import net.minecraft.structure.StructureStart;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.BlockBox;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.util.registry.DynamicRegistryManager;
import net.minecraft.util.registry.Registry;
import net.minecraft.world.Heightmap;
import net.minecraft.world.StructureWorldAccess;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.gen.GenerationStep;
import net.minecraft.world.gen.StructureAccessor;
import net.minecraft.world.gen.chunk.ChunkGenerator;
import net.minecraft.world.gen.feature.ConfiguredStructureFeature;
import net.minecraft.world.gen.feature.DefaultFeatureConfig;
import net.minecraft.world.gen.feature.StructureFeature;
import net.fabricmc.fabric.api.structure.v1.FabricStructureBuilder;
public class StructureTest {
private static final Logger LOGGER = LogManager.getLogger();
public static final StructureFeature<DefaultFeatureConfig> STRUCTURE = new TestStructureFeature(DefaultFeatureConfig.CODEC);
public static final ConfiguredStructureFeature<DefaultFeatureConfig, ? extends StructureFeature<DefaultFeatureConfig>> CONFIGURED_STRUCTURE = STRUCTURE.configure(new DefaultFeatureConfig());
public static final StructurePieceType PIECE = TestStructureGenerator::new;
static {
LOGGER.info("Registering test structure");
FabricStructureBuilder.create(new Identifier("fabric", "test_structure"), STRUCTURE)
.step(GenerationStep.Feature.SURFACE_STRUCTURES)
.defaultConfig(32, 8, 12345)
.superflatFeature(CONFIGURED_STRUCTURE)
.adjustsSurface()
.register();
Registry.register(Registry.STRUCTURE_PIECE, new Identifier("fabric", "test_structure_piece"), PIECE);
}
public static class TestStructureFeature extends StructureFeature<DefaultFeatureConfig> {
public TestStructureFeature(Codec<DefaultFeatureConfig> codec) {
super(codec);
}
@Override
public StructureStartFactory<DefaultFeatureConfig> getStructureStartFactory() {
return Start::new;
}
public static class Start extends StructureStart<DefaultFeatureConfig> {
public Start(StructureFeature<DefaultFeatureConfig> feature, int chunkX, int chunkZ, BlockBox box, int references, long seed) {
super(feature, chunkX, chunkZ, box, references, seed);
}
@Override
public void init(DynamicRegistryManager dynamicRegistryManager, ChunkGenerator chunkGenerator, StructureManager structureManager, int chunkX, int chunkZ, Biome biome, DefaultFeatureConfig featureConfig) {
int blockX = chunkX * 16;
int blockZ = chunkZ * 16;
int blockY = chunkGenerator.getHeight(blockX, blockZ, Heightmap.Type.WORLD_SURFACE_WG);
TestStructureGenerator generator = new TestStructureGenerator(random, blockX, blockY, blockZ);
this.children.add(generator);
setBoundingBoxFromChildren();
}
}
}
public static class TestStructureGenerator extends StructurePieceWithDimensions {
protected TestStructureGenerator(Random random, int x, int y, int z) {
super(PIECE, random, x, y, z, 48, 16, 48);
}
protected TestStructureGenerator(StructureManager structureManager, CompoundTag compoundTag) {
super(PIECE, compoundTag);
}
@Override
public boolean generate(StructureWorldAccess structureWorldAccess, StructureAccessor structureAccessor, ChunkGenerator chunkGenerator, Random random, BlockBox boundingBox, ChunkPos chunkPos, BlockPos blockPos) {
for (int x = 0; x < 48; x++) {
for (int z = 0; z < 48; z++) {
for (int y = 0; y < 16; y++) {
this.addBlock(structureWorldAccess, Blocks.DIAMOND_BLOCK.getDefaultState(), x, y, z, boundingBox);
}
}
}
return true;
}
}
}

View file

@ -0,0 +1,38 @@
/*
* 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.test.structure.mixin;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.ModifyVariable;
import net.minecraft.world.biome.DefaultBiomeCreator;
import net.minecraft.world.biome.GenerationSettings;
import net.fabricmc.fabric.test.structure.StructureTest;
@Mixin(DefaultBiomeCreator.class)
public class MixinDefaultBiomeCreator {
@ModifyVariable(method = "createPlains", ordinal = 0, at = @At(value = "STORE", ordinal = 0))
private static GenerationSettings.Builder addCustomStructure(GenerationSettings.Builder builder, boolean sunflower) {
if (!sunflower) {
builder.structureFeature(StructureTest.CONFIGURED_STRUCTURE);
}
return builder;
}
}

View file

@ -0,0 +1,13 @@
{
"required": true,
"package": "net.fabricmc.fabric.test.structure.mixin",
"compatibilityLevel": "JAVA_8",
"mixins": [
"MixinDefaultBiomeCreator"
],
"client": [
],
"injectors": {
"defaultRequire": 1
}
}

View file

@ -0,0 +1,14 @@
{
"schemaVersion": 1,
"id": "fabric-structure-api-v1-testmod",
"name": "Fabric Structure API (v1) Test Mod",
"version": "1.0.0",
"environment": "*",
"license": "Apache-2.0",
"depends": {
"fabric-structure-api-v1": "*"
},
"mixins": [
"fabric-structure-api-v1-testmod.mixins.json"
]
}

View file

@ -48,6 +48,7 @@ include 'fabric-rendering-data-attachment-v1'
include 'fabric-rendering-fluids-v1'
include 'fabric-resource-loader-v0'
include 'fabric-screen-handler-api-v1'
include 'fabric-structure-api-v1'
include 'fabric-tag-extensions-v0'
include 'fabric-textures-v0'
include 'fabric-tool-attribute-api-v1'