World Generation Crash/Freeze
ni-cky opened this issue ยท 1 comments
I have tried to code the example world generation mod from the following source:
https://github.com/TelepathicGrunt/StructureTutorialMod/tree/1.17.x-Fabric-Jigsaw
I just basically copy pasted the files for now and adjusted IDs and paths wherever it seemed necessary.
(I will include all my code at the end, but it is most equivalent)
Minecraft does start, but if I open a world the following Exception gets thrown and the progress freezes at 4%, dead locking the game.
Advice and help would be appreciated!
Exception:
[22:00:39] [IO-Worker-11/WARN] (FileUtil) Configuration conflict: there is more than one oshi.architecture.properties file on the classpath
[22:00:39] [IO-Worker-11/WARN] (Minecraft) Failed to read chunk [9, 3]
net.minecraft.util.crash.CrashException: Loading NBT data
at net.minecraft.nbt.NbtCompound.read(NbtCompound.java:407) ~[[email protected]+build.31-v2.jar:?]
at net.minecraft.nbt.NbtCompound$1.read(NbtCompound.java:459) ~[[email protected]+build.31-v2.jar:?]
at net.minecraft.nbt.NbtCompound$1.read(NbtCompound.java:476) ~[[email protected]+build.31-v2.jar:?]
at net.minecraft.nbt.NbtCompound.read(NbtCompound.java:401) ~[[email protected]+build.31-v2.jar:?]
at net.minecraft.nbt.NbtCompound$1.read(NbtCompound.java:459) ~[[email protected]+build.31-v2.jar:?]
at net.minecraft.nbt.NbtCompound$1.read(NbtCompound.java:476) ~[[email protected]+build.31-v2.jar:?]
at net.minecraft.nbt.NbtCompound.read(NbtCompound.java:401) ~[[email protected]+build.31-v2.jar:?]
at net.minecraft.nbt.NbtCompound$1.read(NbtCompound.java:459) ~[[email protected]+build.31-v2.jar:?]
at net.minecraft.nbt.NbtCompound$1.read(NbtCompound.java:476) ~[[email protected]+build.31-v2.jar:?]
at net.minecraft.nbt.NbtIo.read(NbtIo.java:203) ~[[email protected]+build.31-v2.jar:?]
at net.minecraft.nbt.NbtIo.read(NbtIo.java:175) ~[[email protected]+build.31-v2.jar:?]
at net.minecraft.nbt.NbtIo.read(NbtIo.java:171) ~[[email protected]+build.31-v2.jar:?]
at net.minecraft.world.storage.RegionBasedStorage.getTagAt(RegionBasedStorage.java:65) ~[[email protected]+build.31-v2.jar:?]
at net.minecraft.world.storage.StorageIoWorker.method_27943(StorageIoWorker.java:69) ~[[email protected]+build.31-v2.jar:?]
at net.minecraft.world.storage.StorageIoWorker.method_27939(StorageIoWorker.java:108) ~[[email protected]+build.31-v2.jar:?]
at net.minecraft.util.thread.TaskQueue$PrioritizedTask.run(TaskQueue.java:80) [[email protected]+build.31-v2.jar:?]
at net.minecraft.util.thread.TaskExecutor.runNext(TaskExecutor.java:84) [[email protected]+build.31-v2.jar:?]
at net.minecraft.util.thread.TaskExecutor.runWhile(TaskExecutor.java:139) [[email protected]+build.31-v2.jar:?]
at net.minecraft.util.thread.TaskExecutor.run(TaskExecutor.java:92) [[email protected]+build.31-v2.jar:?]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130) [?:?]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:630) [?:?]
at java.lang.Thread.run(Thread.java:831) [?:?]
Caused by: java.io.EOFException
at java.io.DataInputStream.readUnsignedShort(DataInputStream.java:346) ~[?:?]
at java.io.DataInputStream.readUTF(DataInputStream.java:595) ~[?:?]
at java.io.DataInputStream.readUTF(DataInputStream.java:570) ~[?:?]
at net.minecraft.nbt.NbtCompound.readString(NbtCompound.java:396) ~[[email protected]+build.31-v2.jar:?]
at net.minecraft.nbt.NbtCompound$1.read(NbtCompound.java:457) ~[[email protected]+build.31-v2.jar:?]
at net.minecraft.nbt.NbtCompound$1.read(NbtCompound.java:476) ~[[email protected]+build.31-v2.jar:?]
at net.minecraft.nbt.NbtCompound.read(NbtCompound.java:401) ~[[email protected]+build.31-v2.jar:?]
... 21 more
[22:00:39] [Server thread/ERROR] (Minecraft) Couldn't load chunk [9, 3]
java.util.concurrent.CompletionException: net.minecraft.util.crash.CrashException: Loading NBT data
at java.util.concurrent.CompletableFuture.reportJoin(CompletableFuture.java:412) ~[?:?]
at java.util.concurrent.CompletableFuture.join(CompletableFuture.java:2114) ~[?:?]
at net.minecraft.world.storage.StorageIoWorker.getNbt(StorageIoWorker.java:52) ~[[email protected]+build.31-v2.jar:?]
at net.minecraft.world.storage.VersionedChunkStorage.getNbt(VersionedChunkStorage.java:57) ~[[email protected]+build.31-v2.jar:?]
at net.minecraft.server.world.ThreadedAnvilChunkStorage.getUpdatedChunkNbt(ThreadedAnvilChunkStorage.java:814) ~[[email protected]+build.31-v2.jar:?]
at net.minecraft.server.world.ThreadedAnvilChunkStorage.method_17256(ThreadedAnvilChunkStorage.java:490) ~[[email protected]+build.31-v2.jar:?]
at java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1764) ~[?:?]
at net.minecraft.util.thread.ThreadExecutor.executeTask(ThreadExecutor.java:137) ~[[email protected]+build.31-v2.jar:?]
at net.minecraft.server.world.ServerChunkManager$MainThreadExecutor.executeTask(ServerChunkManager.java:519) ~[[email protected]+build.31-v2.jar:?]
at net.minecraft.util.thread.ThreadExecutor.runTask(ThreadExecutor.java:110) ~[[email protected]+build.31-v2.jar:?]
at net.minecraft.server.world.ServerChunkManager$MainThreadExecutor.runTask(ServerChunkManager.java:527) ~[[email protected]+build.31-v2.jar:?]
at net.minecraft.server.world.ServerChunkManager.executeQueuedTasks(ServerChunkManager.java:273) ~[[email protected]+build.31-v2.jar:?]
at net.minecraft.server.MinecraftServer.runOneTask(MinecraftServer.java:832) ~[[email protected]+build.31-v2.jar:?]
at net.minecraft.server.MinecraftServer.runTask(MinecraftServer.java:818) ~[[email protected]+build.31-v2.jar:?]
at net.minecraft.util.thread.ThreadExecutor.runTasks(ThreadExecutor.java:120) ~[[email protected]+build.31-v2.jar:?]
at net.minecraft.server.MinecraftServer.runTasksTillTickEnd(MinecraftServer.java:804) ~[[email protected]+build.31-v2.jar:?]
at net.minecraft.server.MinecraftServer.prepareStartRegion(MinecraftServer.java:544) ~[[email protected]+build.31-v2.jar:?]
at net.minecraft.server.MinecraftServer.loadWorld(MinecraftServer.java:379) ~[[email protected]+build.31-v2.jar:?]
at net.minecraft.server.integrated.IntegratedServer.setupServer(IntegratedServer.java:67) ~[[email protected]+build.31-v2.jar:?]
at net.minecraft.server.MinecraftServer.runServer(MinecraftServer.java:728) ~[[email protected]+build.31-v2.jar:?]
at net.minecraft.server.MinecraftServer.method_29739(MinecraftServer.java:272) ~[[email protected]+build.31-v2.jar:?]
at java.lang.Thread.run(Thread.java:831) [?:?]
Caused by: net.minecraft.util.crash.CrashException: Loading NBT data
at net.minecraft.nbt.NbtCompound.read(NbtCompound.java:407) ~[[email protected]+build.31-v2.jar:?]
at net.minecraft.nbt.NbtCompound$1.read(NbtCompound.java:459) ~[[email protected]+build.31-v2.jar:?]
at net.minecraft.nbt.NbtCompound$1.read(NbtCompound.java:476) ~[[email protected]+build.31-v2.jar:?]
at net.minecraft.nbt.NbtCompound.read(NbtCompound.java:401) ~[[email protected]+build.31-v2.jar:?]
at net.minecraft.nbt.NbtCompound$1.read(NbtCompound.java:459) ~[[email protected]+build.31-v2.jar:?]
at net.minecraft.nbt.NbtCompound$1.read(NbtCompound.java:476) ~[[email protected]+build.31-v2.jar:?]
at net.minecraft.nbt.NbtCompound.read(NbtCompound.java:401) ~[[email protected]+build.31-v2.jar:?]
at net.minecraft.nbt.NbtCompound$1.read(NbtCompound.java:459) ~[[email protected]+build.31-v2.jar:?]
at net.minecraft.nbt.NbtCompound$1.read(NbtCompound.java:476) ~[[email protected]+build.31-v2.jar:?]
at net.minecraft.nbt.NbtIo.read(NbtIo.java:203) ~[[email protected]+build.31-v2.jar:?]
at net.minecraft.nbt.NbtIo.read(NbtIo.java:175) ~[[email protected]+build.31-v2.jar:?]
at net.minecraft.nbt.NbtIo.read(NbtIo.java:171) ~[[email protected]+build.31-v2.jar:?]
at net.minecraft.world.storage.RegionBasedStorage.getTagAt(RegionBasedStorage.java:65) ~[[email protected]+build.31-v2.jar:?]
at net.minecraft.world.storage.StorageIoWorker.method_27943(StorageIoWorker.java:69) ~[[email protected]+build.31-v2.jar:?]
at net.minecraft.world.storage.StorageIoWorker.method_27939(StorageIoWorker.java:108) ~[[email protected]+build.31-v2.jar:?]
at net.minecraft.util.thread.TaskQueue$PrioritizedTask.run(TaskQueue.java:80) ~[[email protected]+build.31-v2.jar:?]
at net.minecraft.util.thread.TaskExecutor.runNext(TaskExecutor.java:84) ~[[email protected]+build.31-v2.jar:?]
at net.minecraft.util.thread.TaskExecutor.runWhile(TaskExecutor.java:139) ~[[email protected]+build.31-v2.jar:?]
at net.minecraft.util.thread.TaskExecutor.run(TaskExecutor.java:92) ~[[email protected]+build.31-v2.jar:?]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130) ~[?:?]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:630) ~[?:?]
... 1 more
Caused by: java.io.EOFException
at java.io.DataInputStream.readUnsignedShort(DataInputStream.java:346) ~[?:?]
at java.io.DataInputStream.readUTF(DataInputStream.java:595) ~[?:?]
at java.io.DataInputStream.readUTF(DataInputStream.java:570) ~[?:?]
at net.minecraft.nbt.NbtCompound.readString(NbtCompound.java:396) ~[[email protected]+build.31-v2.jar:?]
at net.minecraft.nbt.NbtCompound$1.read(NbtCompound.java:457) ~[[email protected]+build.31-v2.jar:?]
at net.minecraft.nbt.NbtCompound$1.read(NbtCompound.java:476) ~[[email protected]+build.31-v2.jar:?]
at net.minecraft.nbt.NbtCompound.read(NbtCompound.java:401) ~[[email protected]+build.31-v2.jar:?]
at net.minecraft.nbt.NbtCompound$1.read(NbtCompound.java:459) ~[[email protected]+build.31-v2.jar:?]
at net.minecraft.nbt.NbtCompound$1.read(NbtCompound.java:476) ~[[email protected]+build.31-v2.jar:?]
at net.minecraft.nbt.NbtCompound.read(NbtCompound.java:401) ~[[email protected]+build.31-v2.jar:?]
at net.minecraft.nbt.NbtCompound$1.read(NbtCompound.java:459) ~[[email protected]+build.31-v2.jar:?]
at net.minecraft.nbt.NbtCompound$1.read(NbtCompound.java:476) ~[[email protected]+build.31-v2.jar:?]
at net.minecraft.nbt.NbtCompound.read(NbtCompound.java:401) ~[[email protected]+build.31-v2.jar:?]
at net.minecraft.nbt.NbtCompound$1.read(NbtCompound.java:459) ~[[email protected]+build.31-v2.jar:?]
at net.minecraft.nbt.NbtCompound$1.read(NbtCompound.java:476) ~[[email protected]+build.31-v2.jar:?]
at net.minecraft.nbt.NbtIo.read(NbtIo.java:203) ~[[email protected]+build.31-v2.jar:?]
at net.minecraft.nbt.NbtIo.read(NbtIo.java:175) ~[[email protected]+build.31-v2.jar:?]
at net.minecraft.nbt.NbtIo.read(NbtIo.java:171) ~[[email protected]+build.31-v2.jar:?]
at net.minecraft.world.storage.RegionBasedStorage.getTagAt(RegionBasedStorage.java:65) ~[[email protected]+build.31-v2.jar:?]
at net.minecraft.world.storage.StorageIoWorker.method_27943(StorageIoWorker.java:69) ~[[email protected]+build.31-v2.jar:?]
at net.minecraft.world.storage.StorageIoWorker.method_27939(StorageIoWorker.java:108) ~[[email protected]+build.31-v2.jar:?]
at net.minecraft.util.thread.TaskQueue$PrioritizedTask.run(TaskQueue.java:80) ~[[email protected]+build.31-v2.jar:?]
at net.minecraft.util.thread.TaskExecutor.runNext(TaskExecutor.java:84) ~[[email protected]+build.31-v2.jar:?]
at net.minecraft.util.thread.TaskExecutor.runWhile(TaskExecutor.java:139) ~[[email protected]+build.31-v2.jar:?]
at net.minecraft.util.thread.TaskExecutor.run(TaskExecutor.java:92) ~[[email protected]+build.31-v2.jar:?]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130) ~[?:?]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:630) ~[?:?]
... 1 more
Central classes/files from the repository with minor changes:
package com.nicky.grisha.structure_tutorial;
import com.nicky.grisha.structure_tutorial.mixin.StructuresConfigAccessor;
import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.biome.v1.BiomeModifications;
import net.fabricmc.fabric.api.biome.v1.BiomeSelectors;
import net.fabricmc.fabric.api.biome.v1.ModificationPhase;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerWorldEvents;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.Identifier;
import net.minecraft.world.gen.chunk.StructureConfig;
import net.minecraft.world.gen.feature.StructureFeature;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.HashMap;
import java.util.Map;
public class StructureTutorialMain implements ModInitializer {
public static final Logger LOGGER = LogManager.getLogger();
public static final String MODID = "grisha";
@Override
@SuppressWarnings("deprecation")
public void onInitialize() {
/*
* We setup and register our structures here.
* You should always register your stuff to prevent mod compatibility issue down the line.
*/
STStructures.setupAndRegisterStructureFeatures();
STConfiguredStructures.registerConfiguredStructures();
/*
* This is the API you will use to add anything to any biome.
* This includes spawns, changing the biome's looks, messing with its surfacebuilders,
* adding carvers, spawning new features... etc
*
* Make sure you give this an identifier to make it clear later what mod did a change and why.
* It'll help people look to see if your mod was removing something from biomes.
* The biome modifier identifier might also be used by modpacks to disable mod's modifiers too for customization.
*/
BiomeModifications.create(new Identifier(MODID, "run_down_house_addition"))
.add( // Describes what we are doing. SInce we are adding a structure, we choose ADDITIONS.
ModificationPhase.ADDITIONS,
// Add our structure to all biomes including other modded biomes.
// You can filter to certain biomes based on stuff like temperature, scale, precipitation, mod id.
BiomeSelectors.all(),
// context is basically the biome itself. This is where you do the changes to the biome.
// Here, we will add our ConfiguredStructureFeature to the biome.
context -> {
context.getGenerationSettings().addBuiltInStructure(STConfiguredStructures.CONFIGURED_RUN_DOWN_HOUSE);
});
}
// This is optional and can be used for blacklisting the structure from dimensions.
// These two are for making sure our ServerWorldEvents.LOAD event always fires after Fabric API's usage of the same event.
// This is done so our changes don't get overwritten by Fabric API adding structure spacings to all dimensions.
// To activate these methods, make this class implement this:
// `implements ModInitializer, DedicatedServerModInitializer, ClientModInitializer {`
// And then go to fabric.mod.json and add this class to a "client" and "server" entry within "entrypoints" section.
// @Override
// public void onInitializeServer() {
// removeStructureSpawningFromSelectedDimension();
// }
//
// @Override
// public void onInitializeClient() {
// removeStructureSpawningFromSelectedDimension();
// }
/**
* || OPTIONAL ||
* This is optional as Fabric API already adds your structure to all dimension.
* But if you want to do dimension based blacklisting, you will need to both
* manually remove your structure from the chunkgenerator's structure spacing map.
* If the spacing or our structure is not added, the structure doesn't spawn in that dimension.
*/
public static void removeStructureSpawningFromSelectedDimension() {
// Controls the dimension blacklisting
ServerWorldEvents.LOAD.register((MinecraftServer minecraftServer, ServerWorld serverWorld)->{
// Need temp map as some mods use custom chunk generators with immutable maps in themselves.
Map<StructureFeature<?>, StructureConfig> tempMap = new HashMap<>(serverWorld.getChunkManager().getChunkGenerator().getStructuresConfig().getStructures());
// Make absolutely sure modded dimension cannot spawn our structures.
// New dimensions under the minecraft namespace will still get it (datapacks might do this)
if(!serverWorld.getRegistryKey().getValue().getNamespace().equals("minecraft")) {
tempMap.keySet().remove(STStructures.RUN_DOWN_HOUSE);
}
// Set the new modified map of structure spacing to the dimension's chunkgenerator.
((StructuresConfigAccessor)serverWorld.getChunkManager().getChunkGenerator().getStructuresConfig()).setStructures(tempMap);
});
}
}
package com.nicky.TestStructure.structures;
import com.mojang.serialization.Codec;
import com.nicky.grisha.structure_tutorial.StructureTutorialMain;
import net.minecraft.block.BlockState;
import net.minecraft.entity.EntityType;
import net.minecraft.structure.MarginedStructureStart;
import net.minecraft.structure.PoolStructurePiece;
import net.minecraft.structure.StructureManager;
import net.minecraft.structure.StructurePiece;
import net.minecraft.structure.pool.StructurePoolBasedGenerator;
import net.minecraft.util.Identifier;
import net.minecraft.util.collection.Pool;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.util.math.Vec3i;
import net.minecraft.util.registry.DynamicRegistryManager;
import net.minecraft.util.registry.Registry;
import net.minecraft.world.HeightLimitView;
import net.minecraft.world.Heightmap;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.biome.SpawnSettings;
import net.minecraft.world.biome.source.BiomeSource;
import net.minecraft.world.gen.ChunkRandom;
import net.minecraft.world.gen.chunk.ChunkGenerator;
import net.minecraft.world.gen.chunk.VerticalBlockSample;
import net.minecraft.world.gen.feature.DefaultFeatureConfig;
import net.minecraft.world.gen.feature.StructureFeature;
import net.minecraft.world.gen.feature.StructurePoolFeatureConfig;
import org.apache.logging.log4j.Level;
public class RunDownHouseStructure extends StructureFeature<DefaultFeatureConfig> {
public RunDownHouseStructure(Codec<DefaultFeatureConfig> codec) {
super(codec);
}
/**
* This is how the worldgen code knows what to call when it
* is time to create the pieces of the structure for generation.
*/
@Override
public StructureStartFactory<DefaultFeatureConfig> getStructureStartFactory() {
return RunDownHouseStructure.Start::new;
}
/**
* This method allows us to have mobs that spawn naturally over time in our structure.
* No other mobs will spawn in the structure of the same entity classification.
* The reason you want to match the classifications is so that your structure's mob
* will contribute to that classification's cap. Otherwise, it may cause a runaway
* spawning of the mob that will never stop.
*
* NOTE: getDefaultSpawnList is for monsters only and getDefaultCreatureSpawnList is
* for creatures only. If you want to add entities of another classification,
* use the StructureSpawnListGatherEvent to add water_creatures, water_ambient,
* ambient, or misc mobs. Use that event to add/remove mobs from structures
* that are not your own.
*/
private static final Pool<SpawnSettings.SpawnEntry> STRUCTURE_MONSTERS = Pool.of(
new SpawnSettings.SpawnEntry(EntityType.ILLUSIONER, 100, 4, 9),
new SpawnSettings.SpawnEntry(EntityType.VINDICATOR, 100, 4, 9)
);
@Override
public Pool<SpawnSettings.SpawnEntry> getMonsterSpawns() {
return STRUCTURE_MONSTERS;
}
private static final Pool<SpawnSettings.SpawnEntry> STRUCTURE_CREATURES = Pool.of(
new SpawnSettings.SpawnEntry(EntityType.SHEEP, 30, 10, 15),
new SpawnSettings.SpawnEntry(EntityType.RABBIT, 100, 1, 2)
);
@Override
public Pool<SpawnSettings.SpawnEntry> getCreatureSpawns() {
return STRUCTURE_CREATURES;
}
/*
* This is where extra checks can be done to determine if the structure can spawn here.
* This only needs to be overridden if you're adding additional spawn conditions.
*
* Fun fact, if you set your structure separation/spacing to be 0/1, you can use
* shouldStartAt to return true only if certain chunk coordinates are passed in
* which allows you to spawn structures only at certain coordinates in the world.
*
* Notice how the biome is also passed in. Though, you are not going to do any biome
* checking here as you should've added this structure to the biomes you
* wanted already with the biome load event.
*
* Basically, this method is used for determining if the land is at a suitable height,
* if certain other structures are too close or not, or some other restrictive condition.
*
* For example, Pillager Outposts added a check to make sure it cannot spawn within 10 chunk of a Village.
* (Bedrock Edition seems to not have the same check)
*
*
* Also, please for the love of god, do not do dimension checking here.
* If you do and another mod's dimension is trying to spawn your structure,
* the locate command will make minecraft hang forever and break the game.
*
* Instead, use the removeStructureSpawningFromSelectedDimension method in
* StructureTutorialMain class. If you check for the dimension there and do not add your
* structure's spacing into the chunk generator, the structure will not spawn in that dimension!
*/
@Override
protected boolean shouldStartAt(ChunkGenerator chunkGenerator, BiomeSource biomeSource, long seed, ChunkRandom chunkRandom, ChunkPos chunkPos, Biome biome, ChunkPos chunkPos2, DefaultFeatureConfig featureConfig, HeightLimitView heightLimitView) {
BlockPos centerOfChunk = new BlockPos(chunkPos.x * 16, 0, chunkPos.z * 16);
// Grab height of land. Will stop at first non-air block.
int landHeight = chunkGenerator.getHeightInGround(centerOfChunk.getX(), centerOfChunk.getZ(), Heightmap.Type.WORLD_SURFACE_WG, heightLimitView);
// Grabs column of blocks at given position. In overworld, this column will be made of stone, water, and air.
// In nether, it will be netherrack, lava, and air. End will only be endstone and air. It depends on what block
// the chunk generator will place for that dimension.
VerticalBlockSample columnOfBlocks = chunkGenerator.getColumnSample(centerOfChunk.getX(), centerOfChunk.getZ(), heightLimitView);
// Combine the column of blocks with land height and you get the top block itself which you can test.
BlockState topBlock = columnOfBlocks.getState(centerOfChunk.up(landHeight));
// Now we test to make sure our structure is not spawning on water or other fluids.
// You can do height check instead too to make it spawn at high elevations.
return topBlock.getFluidState().isEmpty(); //landHeight > 100;
}
/**
* Handles calling up the structure's pieces class and height that structure will spawn at.
*/
public static class Start extends MarginedStructureStart<DefaultFeatureConfig> {
public Start(StructureFeature<DefaultFeatureConfig> structureIn, ChunkPos chunkPos, int referenceIn, long seedIn) {
super(structureIn, chunkPos, referenceIn, seedIn);
}
@Override
public void init(DynamicRegistryManager dynamicRegistryManager, ChunkGenerator chunkGenerator, StructureManager structureManager, ChunkPos chunkPos, Biome biome, DefaultFeatureConfig defaultFeatureConfig, HeightLimitView heightLimitView) {
// Turns the chunk coordinates into actual coordinates we can use. (Gets center of that chunk)
int x = chunkPos.x * 16;
int z = chunkPos.z * 16;
/*
* We pass this into method_30419 to tell it where to generate the structure.
* If method_30419's last parameter is true, blockpos's Y value is ignored and the
* structure will spawn at terrain height instead. Set that parameter to false to
* force the structure to spawn at blockpos's Y value instead. You got options here!
*/
BlockPos.Mutable centerPos = new BlockPos.Mutable(x, 0, z);
/*
* If you are doing Nether structures, you'll probably want to spawn your structure on top of ledges.
* Best way to do that is to use getColumnSample to grab a column of blocks at the structure's x/z position.
* Then loop through it and look for land with air above it and set blockpos's Y value to it.
* Make sure to set the final boolean in StructurePoolBasedGenerator.method_30419 to false so
* that the structure spawns at blockpos's y value instead of placing the structure on the Bedrock roof!
*/
//VerticalBlockSample blockView = chunkGenerator.getColumnSample(blockpos.getX(), blockpos.getZ(), heightLimitView);
StructurePoolFeatureConfig structureSettingsAndStartPool = new StructurePoolFeatureConfig(() -> dynamicRegistryManager.get(Registry.STRUCTURE_POOL_KEY)
// The path to the starting Template Pool JSON file to read.
//
// Note, this is "structure_tutorial:run_down_house/start_pool" which means
// the game will automatically look into the following path for the template pool:
// "resources/data/structure_tutorial/worldgen/template_pool/run_down_house/start_pool.json"
// This is why your pool files must be in "data/<modid>/worldgen/template_pool/<the path to the pool here>"
// because the game automatically will check in worldgen/template_pool for the pools.
.get(new Identifier(StructureTutorialMain.MODID, "run_down_house/start_pool")),
// How many pieces outward from center can a recursive jigsaw structure spawn.
// Our structure is only 1 piece outward and isn't recursive so any value of 1 or more doesn't change anything.
// However, I recommend you keep this a decent value like 10 so people can use datapacks to add additional pieces to your structure easily.
// But don't make it too large for recursive structures like villages or you'll crash server due to hundreds of pieces attempting to generate!
10);
// All a structure has to do is call this method to turn it into a jigsaw based structure!
StructurePoolBasedGenerator.generate(
dynamicRegistryManager,
structureSettingsAndStartPool,
PoolStructurePiece::new,
chunkGenerator,
structureManager,
centerPos, // Position of the structure. Y value is ignored if last parameter is set to true.
this, // The class instance that holds the list that will be populated with the jigsaw pieces after this method.
this.random,
false, // Special boundary adjustments for villages. It's... hard to explain. Keep this false and make your pieces not be partially intersecting.
// Either not intersecting or fully contained will make children pieces spawn just fine. It's easier that way.
true, // Place at heightmap (top land). Set this to false for structure to be place at the passed in blockpos's Y value instead.
// Definitely keep this false when placing structures in the nether as otherwise, heightmap placing will put the structure on the Bedrock roof.
heightLimitView);
// **THE FOLLOWING TWO LINES ARE OPTIONAL**
//
// Right here, you can do interesting stuff with the pieces in this.children such as offset the
// center piece by 50 blocks up for no reason, remove repeats of a piece or add a new piece so
// only 1 of that piece exists, etc. But you do not have access to the piece's blocks as this list
// holds just the piece's size and positions. Blocks will be placed later in StructurePoolBasedGenerator.
//
// In this case, we do `piece.translate` to raise pieces up by 1 block so that the house is not right on
// the surface of water or sunken into land a bit.
//
// Then we move the bounding box down by 1 by doing `piece.getBoundingBox().move` which will cause the
// land formed around the structure to be lowered and not cover the doorstep. You can raise the bounding
// box to force the structure to be buried as well. This bounding box stuff with land is only for structures
// that you added to Structure.JIGSAW_STRUCTURES field handles adding land around the base of structures.
//
// By lifting the house up by 1 and lowering the bounding box, the land at bottom of house will now be
// flush with the surrounding terrain without blocking off the doorstep.
this.children.forEach(piece -> piece.translate(0, 1, 0));
this.children.forEach(piece -> piece.getBoundingBox().move(0, -1, 0));
// Since by default, the start piece of a structure spawns with it's corner at centerPos
// and will randomly rotate around that corner, we will center the piece on centerPos instead.
// This is so that our structure's start piece is now centered on the water check done in shouldStartAt.
// Whatever the offset done to center the start piece, that offset is applied to all other pieces
// so the entire structure is shifted properly to the new spot.
Vec3i structureCenter = this.children.get(0).getBoundingBox().getCenter();
int xOffset = centerPos.getX() - structureCenter.getX();
int zOffset = centerPos.getZ() - structureCenter.getZ();
for(StructurePiece structurePiece : this.children){
structurePiece.translate(xOffset, 0, zOffset);
}
// Sets the bounds of the structure once you are finished.
this.setBoundingBoxFromChildren();
// I use to debug and quickly find out if the structure is spawning or not and where it is.
// This is returning the coordinates of the center starting piece.
StructureTutorialMain.LOGGER.log(Level.DEBUG, "Rundown House at " +
this.children.get(0).getBoundingBox().getMinX() + " " +
this.children.get(0).getBoundingBox().getMinY() + " " +
this.children.get(0).getBoundingBox().getMinZ());
}
}
}
{
"required": true,
"minVersion": "0.8",
"package": "com.nicky.grisha.structure_tutorial.mixin",
"compatibilityLevel": "JAVA_16",
"mixins": [
"NoiseChunkGeneratorMixin",
"StructuresConfigAccessor"
],
"client": [
],
"injectors": {
"defaultRequire": 1
}
}
package com.nicky.grisha.structure_tutorial;
import com.nicky.TestStructure.structures.RunDownHouseStructure;
import net.fabricmc.fabric.api.structure.v1.FabricStructureBuilder;
import net.minecraft.util.Identifier;
import net.minecraft.world.gen.GenerationStep;
import net.minecraft.world.gen.chunk.StructureConfig;
import net.minecraft.world.gen.feature.DefaultFeatureConfig;
import net.minecraft.world.gen.feature.FeatureConfig;
import net.minecraft.world.gen.feature.StructureFeature;
public class STStructures {
/**
/**
* Registers the structure itself and sets what its path is. In this case, the
* structure will have the Identifier of structure_tutorial:run_down_house.
*
* It is always a good idea to register your Structures so that other mods and datapacks can
* use them too directly from the registries. It great for mod/datapacks compatibility.
*/
public static StructureFeature<DefaultFeatureConfig> RUN_DOWN_HOUSE = new RunDownHouseStructure(DefaultFeatureConfig.CODEC);
/**
* This is where we use Fabric API's structure API to setup the StructureFeature
* See the comments in below for more details.
*/
public static void setupAndRegisterStructureFeatures() {
// This is Fabric API's builder for structures.
// It has many options to make sure your structure will spawn and work properly.
// Give it your structure and the identifier you want for it.
FabricStructureBuilder.create(new Identifier(StructureTutorialMain.MODID, "run_down_house"), RUN_DOWN_HOUSE)
/* Generation stage for when to generate the structure. there are 10 stages you can pick from!
This surface structure stage places the structure before plants and ores are generated. */
.step(GenerationStep.Feature.SURFACE_STRUCTURES)
.defaultConfig(new StructureConfig(
10, /* average distance apart in chunks between spawn attempts */
5, /* minimum distance apart in chunks between spawn attempts. MUST BE LESS THAN ABOVE VALUE */
399117345 /* this modifies the seed of the structure so no two structures always spawn over each-other. Make this large and unique. */))
/* Always set this or else re-entering SuperFlat worldtype will crash.
Getting structures to spawn in Superflat is a bit buggy right now so don't focus too much on this. */
.superflatFeature(RUN_DOWN_HOUSE.configure(FeatureConfig.DEFAULT))
/*
* Whether surrounding land will be modified automatically to conform to the bottom of the structure.
* Basically, it adds land at the base of the structure like it does for Villages and Outposts.
* Doesn't work well on structure that have pieces stacked vertically or change in heights.
*
* Note: The air space this method will create will be filled with water if the structure is below sealevel.
* This means this is best for structure above sealevel so keep that in mind.
*/
.adjustsSurface()
/* Finally! Now we register our structure and everything above will take effect. */
.register();
// Add more structures here and so on
}
}
package com.nicky.grisha.structure_tutorial;
import net.minecraft.util.Identifier;
import net.minecraft.util.registry.BuiltinRegistries;
import net.minecraft.util.registry.Registry;
import net.minecraft.world.gen.feature.ConfiguredStructureFeature;
import net.minecraft.world.gen.feature.DefaultFeatureConfig;
public class STConfiguredStructures {
/**
* Static instance of our configured structure so we can reference it and add it to biomes easily.
*/
public static ConfiguredStructureFeature<?, ?> CONFIGURED_RUN_DOWN_HOUSE = STStructures.RUN_DOWN_HOUSE.configure(DefaultFeatureConfig.DEFAULT);
/**
* Registers the configured structure which is what gets added to the biomes.
* You can use the same identifier for the configured structure as the regular structure
* because the two fo them are registered to different registries.
*
* We can register configured structures at any time before a world is clicked on and made.
* But the best time to register configured features by code is honestly to do it in onInitialize.
*/
public static void registerConfiguredStructures() {
Registry<ConfiguredStructureFeature<?, ?>> registry = BuiltinRegistries.CONFIGURED_STRUCTURE_FEATURE;
Registry.register(registry, new Identifier(StructureTutorialMain.MODID, "configured_run_down_house"), CONFIGURED_RUN_DOWN_HOUSE);
}
The actual error is something to do with some nbt being truncated (i.e. the file ends too early).
This is not a fabric api problem.
I would suggest posting your question in the issues section of that tutorial if you want help with it:
https://github.com/TelepathicGrunt/StructureTutorialMod/issues