refactor: update structure handling and context management in dungeon generation

This commit is contained in:
Chipperfluff 2025-12-19 01:01:33 +01:00
parent dbaf8a6c0f
commit 8b2ebafe56
5 changed files with 176 additions and 87 deletions

View File

@ -16,26 +16,37 @@ import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.biome.v1.BiomeModifications; import net.fabricmc.fabric.api.biome.v1.BiomeModifications;
import net.fabricmc.fabric.api.biome.v1.BiomeSelectors; import net.fabricmc.fabric.api.biome.v1.BiomeSelectors;
import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback; import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerWorldEvents; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerWorldEvents;
import net.fabricmc.fabric.api.object.builder.v1.entity.FabricDefaultAttributeRegistry; import net.fabricmc.fabric.api.object.builder.v1.entity.FabricDefaultAttributeRegistry;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.registry.RegistryKey; import net.minecraft.registry.RegistryKey;
import net.minecraft.registry.RegistryKeys; import net.minecraft.registry.RegistryKeys;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.structure.StructurePlacementData; import net.minecraft.structure.StructurePlacementData;
import net.minecraft.structure.StructureTemplate; import net.minecraft.structure.StructureTemplate;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.world.GameRules; import net.minecraft.world.GameRules;
import net.minecraft.world.World;
import net.minecraft.world.gen.GenerationStep; import net.minecraft.world.gen.GenerationStep;
public class ChipiMod implements ModInitializer { public class ChipiMod implements ModInitializer {
public static final String MOD_ID = "chipi"; public static final String MOD_ID = "chipi";
private static final Identifier CHIPI_DIM = new Identifier("chipi", "chipi_dimension"); public static final RegistryKey<World> CHIPI_DIMENSION_KEY =
RegistryKey.of(
RegistryKeys.WORLD,
new Identifier("chipi", "chipi_dimension")
);
private static final Identifier SPAWN_STRUCTURE = new Identifier("chipi", "spawn"); private static final Identifier SPAWN_STRUCTURE =
new Identifier("chipi", "spawn");
private static MinecraftServer SERVER;
@Override @Override
public void onInitialize() { public void onInitialize() {
@ -49,72 +60,102 @@ public class ChipiMod implements ModInitializer {
ChipiBlessingEvents.register(); ChipiBlessingEvents.register();
ChipiHungerHandler.register(); ChipiHungerHandler.register();
FabricDefaultAttributeRegistry.register(ModEntities.MEP, MepEntity.createMepAttributes()); FabricDefaultAttributeRegistry.register(
ModEntities.MEP,
MepEntity.createMepAttributes()
);
BiomeModifications.addFeature( BiomeModifications.addFeature(
BiomeSelectors.foundInOverworld(), BiomeSelectors.foundInOverworld(),
GenerationStep.Feature.UNDERGROUND_ORES, GenerationStep.Feature.UNDERGROUND_ORES,
RegistryKey.of( RegistryKey.of(
RegistryKeys.PLACED_FEATURE, new Identifier("chipi", "chipper_ore"))); RegistryKeys.PLACED_FEATURE,
new Identifier("chipi", "chipper_ore")
)
);
CommandRegistrationCallback.EVENT.register( CommandRegistrationCallback.EVENT.register(
(dispatcher, registryAccess, environment) -> ChpCommand.register(dispatcher)); (dispatcher, registryAccess, environment) -> {
ChpCommand.register(dispatcher);
}
);
ServerTickEvents.END_WORLD_TICK.register( ServerLifecycleEvents.SERVER_STARTED.register(server -> {
world -> { SERVER = server;
if (!world.getRegistryKey().getValue().equals(CHIPI_DIM)) return; });
for (PlayerEntity player : world.getPlayers()) { ServerTickEvents.END_WORLD_TICK.register(world -> {
if (player.getBlockY() >= 50) continue; if (!world.getRegistryKey().equals(CHIPI_DIMENSION_KEY)) {
return;
}
ChipperPortalBlock.teleportToChipiSpawn(world, player); for (PlayerEntity player : world.getPlayers()) {
} if (player.getBlockY() >= 50) {
}); continue;
}
ServerWorldEvents.LOAD.register( ChipperPortalBlock.teleportToChipiSpawn(world, player);
(server, world) -> { }
if (!world.getRegistryKey().getValue().equals(CHIPI_DIM)) return; });
world.setTimeOfDay(18000); ServerWorldEvents.LOAD.register((server, world) -> {
world.getGameRules().get(GameRules.DO_DAYLIGHT_CYCLE).set(false, server); if (!world.getRegistryKey().equals(CHIPI_DIMENSION_KEY)) {
return;
}
SpawnPlacedState state = world.setTimeOfDay(18000);
world.getPersistentStateManager() world.getGameRules()
.getOrCreate( .get(GameRules.DO_DAYLIGHT_CYCLE)
SpawnPlacedState::fromNbt, .set(false, server);
SpawnPlacedState::new,
"chipi_spawn");
if (state.placed) return; SpawnPlacedState state =
world.getPersistentStateManager().getOrCreate(
SpawnPlacedState::fromNbt,
SpawnPlacedState::new,
"chipi_spawn"
);
StructureTemplate spawnTemplate = if (state.placed) {
world.getStructureTemplateManager() return;
.getTemplate(SPAWN_STRUCTURE) }
.orElse(null);
if (spawnTemplate == null) { StructureTemplate spawnTemplate =
System.err.println("[CHIPI] spawn.nbt not found!"); world.getStructureTemplateManager()
return; .getTemplate(SPAWN_STRUCTURE)
} .orElse(null);
BlockPos spawnCenter = new BlockPos(0, 80, 0); if (spawnTemplate == null) {
System.err.println("[CHIPI] spawn.nbt not found!");
return;
}
spawnTemplate.place( BlockPos spawnCenter = new BlockPos(0, 80, 0);
world,
spawnCenter,
spawnCenter,
new StructurePlacementData(),
world.getRandom(),
2);
world.setSpawnPos(spawnCenter.up(), 0.0f); spawnTemplate.place(
world,
spawnCenter,
spawnCenter,
new StructurePlacementData(),
world.getRandom(),
2
);
ChipiDungeonGenerator.generateInitialLayout(world, spawnCenter); world.setSpawnPos(spawnCenter.up(), 0.0f);
state.placed = true; ChipiDungeonGenerator.generateInitialLayout(world, spawnCenter);
state.markDirty();
System.out.println("[CHIPI] Spawn + initial dungeon generated"); state.placed = true;
}); state.markDirty();
System.out.println("[CHIPI] Spawn + initial dungeon generated");
});
}
public static ServerWorld getChipiWorld() {
if (SERVER == null) {
return null;
}
return SERVER.getWorld(CHIPI_DIMENSION_KEY);
} }
} }

View File

@ -71,9 +71,9 @@ public class ChipiDungeonGenerator {
ROOM_Y, ROOM_Y,
firstGeneratedRoomCenter.getZ() + (row * STEP_Z)); firstGeneratedRoomCenter.getZ() + (row * STEP_Z));
RoomBaseStructure room = WorldMaster.resolveRoom(ctx(gridX, gridY, center, null)); RoomBaseStructure room = WorldMaster.resolveRoom(ctx(world, gridX, gridY, center, null));
room.placeAt(world, center); room.placeAt(world, center);
WorldMaster.afterPlaceRoom(ctx(gridX, gridY, center, room)); WorldMaster.afterPlaceRoom(ctx(world, gridX, gridY, center, room));
} }
} }
@ -97,9 +97,9 @@ public class ChipiDungeonGenerator {
currentCenter.getZ() + ROOM_EXTENT_SOUTH); currentCenter.getZ() + ROOM_EXTENT_SOUTH);
CorridorNSStructure corridorNS = CorridorNSStructure corridorNS =
WorldMaster.resolveCorridorNS(ctx(gridX, gridY, anchorSouth, null)); WorldMaster.resolveCorridorNS(ctx(world, gridX, gridY, anchorSouth, null));
corridorNS.placeAt(world, anchorSouth); corridorNS.placeAt(world, anchorSouth);
WorldMaster.afterPlaceCorridorNS(ctx(gridX, gridY, anchorSouth, corridorNS)); WorldMaster.afterPlaceCorridorNS(ctx(world, gridX, gridY, anchorSouth, corridorNS));
} }
} }
@ -122,9 +122,9 @@ public class ChipiDungeonGenerator {
currentCenter.getZ()); currentCenter.getZ());
CorridorEWStructure corridorEW = CorridorEWStructure corridorEW =
WorldMaster.resolveCorridorEW(ctx(gridX, gridY, anchorEast, null)); WorldMaster.resolveCorridorEW(ctx(world, gridX, gridY, anchorEast, null));
corridorEW.placeAt(world, anchorEast); corridorEW.placeAt(world, anchorEast);
WorldMaster.afterPlaceCorridorEW(ctx(gridX, gridY, anchorEast, corridorEW)); WorldMaster.afterPlaceCorridorEW(ctx(world, gridX, gridY, anchorEast, corridorEW));
} }
} }
@ -188,7 +188,7 @@ public class ChipiDungeonGenerator {
} }
private static DungeonContext ctx( private static DungeonContext ctx(
int gridX, int gridY, BlockPos origin, ChipiStructure structure) { ServerWorld world, int gridX, int gridY, BlockPos origin, ChipiStructure structure) {
return DungeonContext.of(gridX, gridY, origin, structure); return DungeonContext.of(world, gridX, gridY, origin, structure);
} }
} }

View File

@ -1,5 +1,7 @@
package net.Chipperfluff.chipi.world.gen; package net.Chipperfluff.chipi.world.gen;
import java.util.Optional;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks; import net.minecraft.block.Blocks;
import net.minecraft.block.entity.StructureBlockBlockEntity; import net.minecraft.block.entity.StructureBlockBlockEntity;
import net.minecraft.block.enums.StructureBlockMode; import net.minecraft.block.enums.StructureBlockMode;
@ -10,15 +12,10 @@ import net.minecraft.util.Identifier;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3i; import net.minecraft.util.math.Vec3i;
import java.util.Optional;
public abstract class ChipiStructure { public abstract class ChipiStructure {
protected final Identifier id; protected final Identifier id;
/** Offset from STRUCTURE BLOCK → actual structure origin */
protected final int deltaX; protected final int deltaX;
protected final int deltaY; protected final int deltaY;
protected final int deltaZ; protected final int deltaZ;
@ -29,41 +26,30 @@ public abstract class ChipiStructure {
this.deltaZ = deltaZ; this.deltaZ = deltaZ;
} }
/** REAL size from NBT */
public BlockPos getSize(ServerWorld world) { public BlockPos getSize(ServerWorld world) {
Optional<StructureTemplate> opt = world.getStructureTemplateManager().getTemplate(id); Optional<StructureTemplate> opt = world.getStructureTemplateManager().getTemplate(id);
if (opt.isEmpty()) return BlockPos.ORIGIN; if (opt.isEmpty()) return BlockPos.ORIGIN;
Vec3i size = opt.get().getSize(); Vec3i size = opt.get().getSize();
return new BlockPos(size.getX(), size.getY(), size.getZ()); return new BlockPos(size.getX(), size.getY(), size.getZ());
} }
/** Pure structure placement */
public void placeAt(ServerWorld world, BlockPos centerPos) { public void placeAt(ServerWorld world, BlockPos centerPos) {
Optional<StructureTemplate> opt = world.getStructureTemplateManager().getTemplate(id); Optional<StructureTemplate> opt = world.getStructureTemplateManager().getTemplate(id);
if (opt.isEmpty()) { if (opt.isEmpty()) {
System.out.println("[CHIPI] Missing structure: " + id); System.out.println("[CHIPI] Missing structure: " + id);
return; return;
} }
BlockPos origin = centerPos.add(new BlockPos(deltaX, deltaY, deltaZ)); BlockPos origin = centerPos.add(deltaX, deltaY, deltaZ);
opt.get().place(world, origin, origin, new StructurePlacementData(), world.getRandom(), 2);
StructureTemplate template = opt.get();
template.place(world, origin, origin, new StructurePlacementData(), world.getRandom(), 2);
} }
/** Command placement (structure + optional marker) */
public void placeCommand(ServerWorld world, BlockPos placePos, boolean marker) { public void placeCommand(ServerWorld world, BlockPos placePos, boolean marker) {
placeAt(world, placePos); placeAt(world, placePos);
BlockPos origin = placePos.add(new BlockPos(deltaX, deltaY, deltaZ));
if (!marker) return; if (!marker) return;
BlockPos origin = placePos.add(deltaX, deltaY, deltaZ);
world.setBlockState(origin, Blocks.STRUCTURE_BLOCK.getDefaultState(), 3); world.setBlockState(origin, Blocks.STRUCTURE_BLOCK.getDefaultState(), 3);
if (world.getBlockEntity(origin) instanceof StructureBlockBlockEntity be) { if (world.getBlockEntity(origin) instanceof StructureBlockBlockEntity be) {
@ -76,4 +62,48 @@ public abstract class ChipiStructure {
be.markDirty(); be.markDirty();
} }
} }
public BlockPos local(BlockPos centerPos, BlockPos localPos) {
return centerPos.add(deltaX + localPos.getX(), deltaY + localPos.getY(), deltaZ + localPos.getZ());
}
public void setBlock(
ServerWorld world,
BlockPos centerPos,
BlockPos localPos,
BlockState state,
Integer gridX,
Integer gridY) {
BlockPos worldPos = local(centerPos, localPos);
System.out.println(
"[CHIPI] setBlock grid=("
+ gridX
+ ","
+ gridY
+ ") worldPos="
+ worldPos);
world.setBlockState(worldPos, state, 3);
}
public void fillBlocks(
ServerWorld world,
BlockPos centerPos,
BlockPos from,
BlockPos to,
BlockState state,
Integer gridX,
Integer gridY) {
BlockPos start = local(centerPos, from);
BlockPos end = local(centerPos, to);
System.out.println(
"[CHIPI] fillBlocks grid=("
+ gridX
+ ","
+ gridY
+ ") from="
+ start
+ " to="
+ end);
BlockPos.stream(start, end).forEach(pos -> world.setBlockState(pos, state, 3));
}
} }

View File

@ -1,28 +1,34 @@
package net.Chipperfluff.chipi.world.gen; package net.Chipperfluff.chipi.world.gen;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
public final class DungeonContext { public final class DungeonContext {
public static final DungeonContext EMPTY = new DungeonContext(); public static final DungeonContext EMPTY = new DungeonContext();
private Integer grid_x = null; private Integer grid_x;
private Integer grid_y = null; private Integer grid_y;
private BlockPos structure_origin = null; private BlockPos structure_origin;
private ChipiStructure structure = null; private ChipiStructure structure;
private ServerWorld world;
private DungeonContext() {} private DungeonContext() {}
private DungeonContext( private DungeonContext(ServerWorld world, Integer grid_x, Integer grid_y, BlockPos structure_origin, ChipiStructure structure) {
Integer grid_x, Integer grid_y, BlockPos structure_origin, ChipiStructure structure) { this.world = world;
this.grid_x = grid_x; this.grid_x = grid_x;
this.grid_y = grid_y; this.grid_y = grid_y;
this.structure_origin = structure_origin; this.structure_origin = structure_origin;
this.structure = structure; this.structure = structure;
} }
public static DungeonContext of( public static DungeonContext of(ServerWorld world, Integer grid_x, Integer grid_y, BlockPos structure_origin, ChipiStructure structure) {
Integer grid_x, Integer grid_y, BlockPos structure_origin, ChipiStructure structure) { return new DungeonContext(world, grid_x, grid_y, structure_origin, structure);
return new DungeonContext(grid_x, grid_y, structure_origin, structure); }
public ServerWorld getWorld() {
return world;
} }
public Integer getGridX() { public Integer getGridX() {

View File

@ -3,6 +3,8 @@ package net.Chipperfluff.chipi.world.gen;
import net.Chipperfluff.chipi.world.gen.struct.CorridorEWStructure; import net.Chipperfluff.chipi.world.gen.struct.CorridorEWStructure;
import net.Chipperfluff.chipi.world.gen.struct.CorridorNSStructure; import net.Chipperfluff.chipi.world.gen.struct.CorridorNSStructure;
import net.Chipperfluff.chipi.world.gen.struct.RoomBaseStructure; import net.Chipperfluff.chipi.world.gen.struct.RoomBaseStructure;
import net.minecraft.block.Blocks;
import net.minecraft.util.math.BlockPos;
public final class WorldMaster { public final class WorldMaster {
@ -21,7 +23,6 @@ public final class WorldMaster {
} }
public static RoomBaseStructure resolveRoom(DungeonContext ctx) { public static RoomBaseStructure resolveRoom(DungeonContext ctx) {
return getDefaultRoom(); return getDefaultRoom();
} }
@ -35,6 +36,17 @@ public final class WorldMaster {
public static RoomBaseStructure afterPlaceRoom(DungeonContext ctx) { public static RoomBaseStructure afterPlaceRoom(DungeonContext ctx) {
if (ctx.getGridX() == 1 && ctx.getGridY() == 1) {
ctx.getStructure().setBlock(
ctx.getWorld(),
ctx.getStructureOrigin(),
new BlockPos(0, 0, 0),
Blocks.REDSTONE_BLOCK.getDefaultState(),
ctx.getGridX(),
ctx.getGridY()
);
}
return getDefaultRoom(); return getDefaultRoom();
} }