Skip to content

Commit

Permalink
implement vanilla structure parsing
Browse files Browse the repository at this point in the history
  • Loading branch information
LCLPYT committed Sep 10, 2024
1 parent 5bcf99b commit 1f7f2c7
Show file tree
Hide file tree
Showing 14 changed files with 321 additions and 24 deletions.
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ kibu-scheduler-api-version=0.9.0
kibu-hook-api-version=1.4.0
kibu-schematic-api-version=0.10.1
kibu-nbt-api-version=0.6.0
kibu-schematic-fabric-version=0.9.2
kibu-schematic-fabric-version=0.10.0
kibu-title-api-version=0.1.2
kibu-access-lib-version=0.15.0
kibu-inventory-api-version=0.5.2
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package work.lclpnet.kibu.schematic.mixin;

import net.minecraft.server.MinecraftServer;
import net.minecraft.structure.StructureTemplateManager;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import work.lclpnet.kibu.schematic.type.KibuServerView;
import work.lclpnet.kibu.schematic.vanilla.VanillaStructureFormat;

@Mixin(MinecraftServer.class)
public abstract class MinecraftServerMixin implements KibuServerView {

@Shadow public abstract StructureTemplateManager getStructureTemplateManager();

@Unique
private final Object vanillaStructureFormatLock = new Object();
@Unique @Nullable
private volatile VanillaStructureFormat vanillaStructureFormat = null;

@Override
public VanillaStructureFormat kibu$getVanillaStructureFormat() {
if (vanillaStructureFormat != null) {
return vanillaStructureFormat;
}

synchronized (vanillaStructureFormatLock) {
if (vanillaStructureFormat == null) {
var manager = getStructureTemplateManager();
vanillaStructureFormat = new VanillaStructureFormat(manager);
}
}

return vanillaStructureFormat;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package work.lclpnet.kibu.schematic.mixin;

import net.minecraft.structure.StructureTemplate;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;

import java.util.List;

@Mixin(StructureTemplate.class)
public interface StructureTemplateAccessor {

@Accessor
List<StructureTemplate.PalettedBlockInfoList> getBlockInfoLists();

@Accessor
List<StructureTemplate.StructureEntityInfo> getEntities();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package work.lclpnet.kibu.schematic.type;

import work.lclpnet.kibu.schematic.vanilla.VanillaStructureFormat;

public interface KibuServerView {

VanillaStructureFormat kibu$getVanillaStructureFormat();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package work.lclpnet.kibu.schematic.vanilla;

import net.minecraft.block.BlockState;
import net.minecraft.entity.EntityType;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.registry.Registries;
import net.minecraft.structure.StructureTemplate;
import net.minecraft.structure.StructureTemplateManager;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3i;
import work.lclpnet.kibu.jnbt.CompoundTag;
import work.lclpnet.kibu.jnbt.NBTConstants;
import work.lclpnet.kibu.mc.BlockStateAdapter;
import work.lclpnet.kibu.mc.KibuBlockPos;
import work.lclpnet.kibu.nbt.FabricNbtConversion;
import work.lclpnet.kibu.schematic.FabricBlockStateAdapter;
import work.lclpnet.kibu.schematic.FabricKibuBlockEntity;
import work.lclpnet.kibu.schematic.FabricKibuEntity;
import work.lclpnet.kibu.schematic.FabricStructureWrapper;
import work.lclpnet.kibu.schematic.api.BlockStructureFactory;
import work.lclpnet.kibu.schematic.api.SchematicDeserializer;
import work.lclpnet.kibu.schematic.mixin.StructureTemplateAccessor;
import work.lclpnet.kibu.structure.BlockStructure;

import java.util.List;

class Deserializer implements SchematicDeserializer {

private final StructureTemplateManager manager;

Deserializer(StructureTemplateManager manager) {
this.manager = manager;
}

@Override
public BlockStructure deserialize(CompoundTag tag, BlockStateAdapter _adapter, BlockStructureFactory factory) {
NbtCompound nbt = FabricNbtConversion.convert(tag, NbtCompound.class);
StructureTemplate template = manager.createTemplate(nbt);

Vec3i size = template.getSize();
var origin = new KibuBlockPos(0, 0, 0);
int dataVersion = FabricStructureWrapper.getDataVersion();

BlockStructure struct = factory.create(size.getX(), size.getY(), size.getZ(), origin, dataVersion);

var accessor = (StructureTemplateAccessor) template;
var blockInfoLists = accessor.getBlockInfoLists();

var adapter = FabricBlockStateAdapter.getInstance();

if (!blockInfoLists.isEmpty()) {
// blockInfoLists can contain multiple palettes (e.g. ship wreck structure files)
// this deserializer only chooses the first one
addBlocks(struct, blockInfoLists.getFirst().getAll(), adapter);
}

addEntities(struct, accessor.getEntities());

return struct;
}

private void addBlocks(BlockStructure struct, List<StructureTemplate.StructureBlockInfo> blocks, FabricBlockStateAdapter adapter) {
for (StructureTemplate.StructureBlockInfo block : blocks) {
BlockPos pos = block.pos();
BlockState state = block.state();

var kibuPos = adapter.adapt(pos);
var kibuState = adapter.adapt(state);

struct.setBlockState(kibuPos, kibuState);

NbtCompound nbt = block.nbt();

if (nbt == null) continue;

addBlockEntity(struct, kibuPos, pos, state, nbt);
}
}

private void addBlockEntity(BlockStructure struct, KibuBlockPos kibuPos, BlockPos pos, BlockState state, NbtCompound nbt) {
if (!state.hasBlockEntity()) return;

var type = Registries.BLOCK_ENTITY_TYPE.getOrEmpty(Identifier.of(nbt.getString("id")))
.orElse(null);

if (type == null) return;

var blockEntity = new FabricKibuBlockEntity(type, pos, nbt);

struct.setBlockEntity(kibuPos, blockEntity);
}

private void addEntities(BlockStructure struct, List<StructureTemplate.StructureEntityInfo> entities) {
for (StructureTemplate.StructureEntityInfo entity : entities) {
NbtCompound nbt = entity.nbt;

if (nbt.contains("TileX", NBTConstants.TYPE_INT)
&& nbt.contains("TileY", NBTConstants.TYPE_INT)
&& nbt.contains("TileZ", NBTConstants.TYPE_INT)) {
nbt.putInt("TileX", entity.blockPos.getX());
nbt.putInt("TileY", entity.blockPos.getY());
nbt.putInt("TileZ", entity.blockPos.getZ());
}

var type = EntityType.fromNbt(nbt).orElse(null);

if (type == null) continue;

var kibuEntity = new FabricKibuEntity(type, entity.pos, nbt);
struct.addEntity(kibuEntity);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package work.lclpnet.kibu.schematic.vanilla;

import work.lclpnet.kibu.jnbt.CompoundTag;
import work.lclpnet.kibu.jnbt.io.NbtIOHelper;
import work.lclpnet.kibu.mc.BlockStateAdapter;
import work.lclpnet.kibu.schematic.api.BlockStructureFactory;
import work.lclpnet.kibu.schematic.api.SchematicDeserializer;
import work.lclpnet.kibu.schematic.api.SchematicReader;
import work.lclpnet.kibu.structure.BlockStructure;

import java.io.IOException;
import java.io.InputStream;
import java.util.Objects;

class Reader implements SchematicReader {

private final SchematicDeserializer deserializer;

Reader(SchematicDeserializer deserializer) {
this.deserializer = Objects.requireNonNull(deserializer);
}

@Override
public BlockStructure read(InputStream in, BlockStateAdapter adapter, BlockStructureFactory factory) throws IOException {
var tag = NbtIOHelper.read(in);

if (!"".equals(tag.name())) throw new IOException("Invalid nbt");

var nbt = tag.tag();

if (!(nbt instanceof CompoundTag compoundNbt)) throw new IOException("Invalid nbt");

return deserializer.deserialize(compoundNbt, adapter, factory);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package work.lclpnet.kibu.schematic.vanilla;

import net.minecraft.server.MinecraftServer;
import net.minecraft.structure.StructureTemplateManager;
import work.lclpnet.kibu.schematic.api.*;
import work.lclpnet.kibu.schematic.type.KibuServerView;

public class VanillaStructureFormat implements SchematicFormat {

private final StructureTemplateManager manager;
private volatile SchematicSerializer serializer = null;
private volatile SchematicDeserializer deserializer = null;
private volatile SchematicWriter writer = null;
private volatile SchematicReader reader = null;

public VanillaStructureFormat(StructureTemplateManager manager) {
this.manager = manager;
}

@Override
public SchematicSerializer serializer() {
throw new UnsupportedOperationException("Not implemented");
}

@Override
public SchematicDeserializer deserializer() {
if (deserializer != null) return deserializer;

synchronized (this) {
if (deserializer == null) {
deserializer = new Deserializer(manager);
}
}

return deserializer;
}

@Override
public SchematicWriter writer() {
throw new UnsupportedOperationException("Not implemented");
}

@Override
public SchematicReader reader() {
if (reader != null) return reader;

synchronized (this) {
if (reader == null) {
reader = new Reader(deserializer());
}
}

return reader;
}

public static VanillaStructureFormat get(MinecraftServer server) {
return ((KibuServerView) server).kibu$getVanillaStructureFormat();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
"defaultRequire": 1
},
"mixins": [
"ItemStackMixin"
"ItemStackMixin",
"MinecraftServerMixin",
"StructureTemplateAccessor"
]
}
Loading

0 comments on commit 1f7f2c7

Please sign in to comment.