mirror of
https://github.com/FabricMC/fabric.git
synced 2025-04-05 11:39:57 -04:00
Structures API (#917)
* 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:
parent
e2e6cdad60
commit
516ece7c6e
17 changed files with 743 additions and 0 deletions
fabric-structure-api-v1
build.gradle
settings.gradlesrc
main
java/net/fabricmc/fabric
api/structure/v1
impl/structure
mixin/structure
resources
testmod
java/net/fabricmc/fabric/test/structure
resources
2
fabric-structure-api-v1/build.gradle
Normal file
2
fabric-structure-api-v1/build.gradle
Normal file
|
@ -0,0 +1,2 @@
|
|||
archivesBaseName = "fabric-structure-api-v1"
|
||||
version = getSubprojectVersion(project, "1.0.0")
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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<>());
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
26
fabric-structure-api-v1/src/main/resources/fabric.mod.json
Normal file
26
fabric-structure-api-v1/src/main/resources/fabric.mod.json
Normal 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"
|
||||
]
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"required": true,
|
||||
"package": "net.fabricmc.fabric.test.structure.mixin",
|
||||
"compatibilityLevel": "JAVA_8",
|
||||
"mixins": [
|
||||
"MixinDefaultBiomeCreator"
|
||||
],
|
||||
"client": [
|
||||
],
|
||||
"injectors": {
|
||||
"defaultRequire": 1
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
]
|
||||
}
|
|
@ -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'
|
||||
|
|
Loading…
Add table
Reference in a new issue