Entity SNBT Support and Cleanup

This commit is contained in:
Zoë Gidiere
2025-10-05 23:50:29 -06:00
parent 8d153998fa
commit 9ca7014344
14 changed files with 191 additions and 42 deletions

View File

@@ -1,8 +0,0 @@
package com.dfsek.terra.api.block;
import com.dfsek.terra.api.Handle;
public interface BlockData extends Handle {
String toString();
}

View File

@@ -12,12 +12,13 @@ import java.util.function.Consumer;
import com.dfsek.terra.api.Handle;
import com.dfsek.terra.api.block.BlockType;
import com.dfsek.terra.api.block.state.properties.Property;
import com.dfsek.terra.api.data.Extendable;
/**
* Contains basic data about a {@link BlockType} in the world
*/
public interface BlockState extends Handle {
public interface BlockState extends Handle, Extendable {
/**
* Whether this {@link BlockState} matches another.
@@ -115,12 +116,4 @@ public interface BlockState extends Handle {
* @return Whether this state is air
*/
boolean isAir();
/**
* Get whether this BlockState is an extended state.
* Extended states are states that contain extra data not normally present in a BlockState.
*
* @return Whether this state is extended.
*/
default boolean isExtended() { return false; }
}

View File

@@ -1,6 +1,6 @@
package com.dfsek.terra.api.block.state;
import com.dfsek.terra.api.block.BlockData;
import com.dfsek.terra.api.data.ExtendedData;
public interface BlockStateExtended extends BlockState {
@@ -9,7 +9,7 @@ public interface BlockStateExtended extends BlockState {
*
* @return BlockData of this BlockStateExtended
*/
BlockData getData();
ExtendedData getData();
/**
* Sets the BlockData.
@@ -18,7 +18,7 @@ public interface BlockStateExtended extends BlockState {
*
* @return New BlockStateExtended with the given BlockData
*/
BlockStateExtended setData(BlockData data);
BlockStateExtended setData(ExtendedData data);
/**
* Gets the BlockState.

View File

@@ -0,0 +1,11 @@
package com.dfsek.terra.api.data;
public interface Extendable {
/**
* Get whether this BlockState is an extended state.
* Extended states are states that contain extra data not normally present in a BlockState.
*
* @return Whether this state is extended.
*/
default boolean isExtended() { return false; }
}

View File

@@ -0,0 +1,8 @@
package com.dfsek.terra.api.data;
import com.dfsek.terra.api.Handle;
public interface ExtendedData extends Handle {
String toString();
}

View File

@@ -8,7 +8,8 @@
package com.dfsek.terra.api.entity;
import com.dfsek.terra.api.Handle;
import com.dfsek.terra.api.data.Extendable;
public interface EntityType extends Handle {
public interface EntityType extends Handle, Extendable {
}

View File

@@ -0,0 +1,32 @@
package com.dfsek.terra.api.entity;
import com.dfsek.terra.api.data.ExtendedData;
public interface EntityTypeExtended extends EntityType {
/**
* Gets the BlockData.
*
* @return BlockData of this EntityTypeExtended
*/
ExtendedData getData();
/**
* Sets the BlockData.
*
* @param data BlockData to set
*
* @return New EntityTypeExtended with the given BlockData
*/
EntityTypeExtended setData(ExtendedData data);
/**
* Gets the EntityType.
*
* @return Raw EntityType of this EntityTypeExtended
*/
EntityType getType();
@Override
default boolean isExtended() { return true; }
}

View File

@@ -17,6 +17,7 @@
package com.dfsek.terra.mod.handle;
import com.mojang.brigadier.StringReader;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import net.minecraft.block.BlockEntityProvider;
import net.minecraft.block.Blocks;
@@ -25,7 +26,11 @@ import net.minecraft.command.argument.BlockArgumentParser;
import net.minecraft.command.argument.BlockArgumentParser.BlockResult;
import net.minecraft.command.argument.BlockStateArgument;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.nbt.StringNbtReader;
import net.minecraft.registry.Registries;
import net.minecraft.registry.RegistryKey;
import net.minecraft.registry.RegistryKeys;
import net.minecraft.registry.entry.RegistryEntry.Reference;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.BlockPos;
import org.jetbrains.annotations.NotNull;
@@ -36,6 +41,9 @@ import com.dfsek.terra.api.block.state.BlockState;
import com.dfsek.terra.api.block.state.BlockStateExtended;
import com.dfsek.terra.api.entity.EntityType;
import com.dfsek.terra.api.handle.WorldHandle;
import com.dfsek.terra.mod.implmentation.FabricEntityTypeExtended;
import static net.minecraft.command.argument.BlockArgumentParser.INVALID_BLOCK_ID_EXCEPTION;
public class MinecraftWorldHandle implements WorldHandle {
@@ -45,6 +53,7 @@ public class MinecraftWorldHandle implements WorldHandle {
private static final Logger logger = LoggerFactory.getLogger(MinecraftWorldHandle.class);
@SuppressWarnings("DataFlowIssue")
@Override
public @NotNull BlockState createBlockState(@NotNull String data) {
try {
@@ -61,8 +70,12 @@ public class MinecraftWorldHandle implements WorldHandle {
nbtCompound.putInt("z", 0);
nbtCompound.put("id", BlockEntity.TYPE_CODEC, blockEntity.getType());
blockState = (BlockStateExtended) new BlockStateArgument(state, blockResult.properties().keySet(), nbtCompound);
} else {
blockState = (BlockState) state;
}
blockState = (BlockStateExtended) new BlockStateArgument(state, blockResult.properties().keySet(), nbtCompound);
} else {
blockState = (BlockState) blockResult.blockState();
}
@@ -80,10 +93,39 @@ public class MinecraftWorldHandle implements WorldHandle {
}
@Override
public @NotNull EntityType getEntity(@NotNull String id) {
if(!id.contains(":")) throw new IllegalArgumentException("Invalid entity identifier " + id);
Identifier identifier = Identifier.tryParse(id);
if(identifier == null) identifier = Identifier.tryParse(id);
return (EntityType) Registries.ENTITY_TYPE.getEntry(identifier).orElseThrow().value();
public @NotNull EntityType getEntity(@NotNull String data) {
try {
Identifier identifier;
NbtCompound nbtData = null;
StringReader reader = new StringReader(data);
int i = reader.getCursor();
identifier = Identifier.fromCommandInput(reader);
net.minecraft.entity.EntityType<?> entity =
(net.minecraft.entity.EntityType<?>) ((Reference<?>) Registries.ENTITY_TYPE.getOptional(
RegistryKey.of(RegistryKeys.ENTITY_TYPE, identifier)).orElseThrow(() -> {
reader.setCursor(i);
return INVALID_BLOCK_ID_EXCEPTION.createWithContext(reader, identifier.toString());
})).value();
if(reader.canRead() && reader.peek() == '{') {
nbtData = StringNbtReader.readCompoundAsArgument(reader);
nbtData.putString("id", entity.getRegistryEntry().registryKey().getValue().toString());
}
EntityType entityType;
if(nbtData != null) {
entityType = new FabricEntityTypeExtended(entity, nbtData);
} else {
entityType = (EntityType) entity;
}
if(identifier == null) throw new IllegalArgumentException("Invalid data: " + data);
return entityType;
} catch(CommandSyntaxException e) {
throw new RuntimeException(e);
}
}
}

View File

@@ -0,0 +1,33 @@
package com.dfsek.terra.mod.implmentation;
import net.minecraft.nbt.NbtCompound;
import com.dfsek.terra.api.data.ExtendedData;
import com.dfsek.terra.api.entity.EntityType;
import com.dfsek.terra.api.entity.EntityTypeExtended;
public record FabricEntityTypeExtended(net.minecraft.entity.EntityType<?> entityType, NbtCompound nbtCompound)
implements EntityTypeExtended {
@SuppressWarnings("DataFlowIssue")
@Override
public ExtendedData getData() {
return (ExtendedData) ((Object) nbtCompound);
}
@SuppressWarnings("DataFlowIssue")
@Override
public EntityTypeExtended setData(ExtendedData data) {
return new FabricEntityTypeExtended(entityType, data.getClass().equals(NbtCompound.class) ? ((NbtCompound) ((Object) data)) : null);
}
@Override
public EntityType getType() {
return (EntityType) entityType;
}
@Override
public Object getHandle() {
return entityType;
}
}

View File

@@ -36,6 +36,7 @@ import com.dfsek.terra.api.block.entity.MobSpawner;
import com.dfsek.terra.api.block.entity.SerialState;
import com.dfsek.terra.api.entity.EntityType;
import com.dfsek.terra.mod.CommonPlatform;
import com.dfsek.terra.mod.implmentation.FabricEntityTypeExtended;
import com.dfsek.terra.mod.mixin.access.MobSpawnerLogicAccessor;
@@ -66,7 +67,10 @@ public abstract class MobSpawnerBlockEntityMixin extends BlockEntity {
} else {
rand = Random.create();
}
setEntityType((net.minecraft.entity.EntityType<?>) creatureType, rand);
net.minecraft.entity.EntityType<?> entityType =
(((net.minecraft.entity.EntityType<?>) (creatureType.isExtended() && creatureType.getClass().equals(
FabricEntityTypeExtended.class) ? ((FabricEntityTypeExtended) creatureType).getType() : creatureType)));
setEntityType(entityType, rand);
}
public int terra$getDelay() {

View File

@@ -15,10 +15,10 @@ import org.spongepowered.asm.mixin.Shadow;
import java.util.Set;
import java.util.function.Predicate;
import com.dfsek.terra.api.block.BlockData;
import com.dfsek.terra.api.block.BlockType;
import com.dfsek.terra.api.block.state.BlockStateExtended;
import com.dfsek.terra.api.block.state.properties.Property;
import com.dfsek.terra.api.data.ExtendedData;
@Mixin(BlockStateArgument.class)
@@ -72,19 +72,23 @@ public abstract class BlockStateArgumentMixin implements Predicate<CachedBlockPo
@SuppressWarnings({ "ConstantValue", "DataFlowIssue", "EqualsBetweenInconvertibleTypes" })
@Intrinsic
public BlockStateExtended terra$setData(BlockData data) {
public BlockStateExtended terra$setData(ExtendedData data) {
return (BlockStateExtended) new BlockStateArgument(getBlockState(), getProperties(),
data.getClass().equals(NbtCompound.class) ? ((NbtCompound) ((Object) data)) : null);
}
@SuppressWarnings("DataFlowIssue")
@Intrinsic
public BlockData terra$getData() {
return ((BlockData) ((Object) data));
public ExtendedData terra$getData() {
return ((ExtendedData) ((Object) data));
}
@Intrinsic
public com.dfsek.terra.api.block.state.BlockState terra$getState() {
return (com.dfsek.terra.api.block.state.BlockState) getBlockState();
}
public Object terra$getHandle() {
return getBlockState();
}
}

View File

@@ -7,11 +7,11 @@ import org.spongepowered.asm.mixin.Interface;
import org.spongepowered.asm.mixin.Intrinsic;
import org.spongepowered.asm.mixin.Mixin;
import com.dfsek.terra.api.block.BlockData;
import com.dfsek.terra.api.data.ExtendedData;
@Mixin(NbtCompound.class)
@Implements(@Interface(iface = BlockData.class, prefix = "terra$"))
@Implements(@Interface(iface = ExtendedData.class, prefix = "terra$"))
public abstract class NbtCompoundMixin implements NbtElement {
@Intrinsic
public String terra$toString() {

View File

@@ -52,6 +52,7 @@ import com.dfsek.terra.api.world.biome.generation.BiomeProvider;
import com.dfsek.terra.api.world.chunk.generation.ChunkGenerator;
import com.dfsek.terra.api.world.chunk.generation.ProtoWorld;
import com.dfsek.terra.mod.generation.MinecraftChunkGeneratorWrapper;
import com.dfsek.terra.mod.implmentation.FabricEntityTypeExtended;
import com.dfsek.terra.mod.util.MinecraftUtil;
@@ -162,10 +163,24 @@ public abstract class ChunkRegionMixin implements StructureWorldAccess {
return terra$config.getBiomeProvider();
}
public Entity terraWorld$spawnEntity(double x, double y, double z, EntityType entityType) {
net.minecraft.entity.Entity entity = ((net.minecraft.entity.EntityType<?>) entityType).create(world, SpawnReason.CHUNK_GENERATION);
entity.setPos(x, y, z);
spawnEntity(entity);
@SuppressWarnings("DataFlowIssue")
public Entity terraWorld$spawnEntity(double x, double y, double z, EntityType data) {
boolean isExtended = data.isExtended() && data.getClass().equals(FabricEntityTypeExtended.class);
net.minecraft.entity.Entity entity;
if(isExtended) {
FabricEntityTypeExtended type = ((FabricEntityTypeExtended) data);
NbtCompound nbt = (NbtCompound) ((Object) type.getData());
entity = net.minecraft.entity.EntityType.loadEntityWithPassengers(nbt, world, SpawnReason.CHUNK_GENERATION, (entityx) -> {
entityx.refreshPositionAndAngles(x, y, z, entityx.getYaw(), entityx.getPitch());
return entityx;
});
spawnEntity(entity);
} else {
entity = ((net.minecraft.entity.EntityType<?>) data).create(world, SpawnReason.CHUNK_GENERATION);
entity.setPos(x, y, z);
spawnEntity(entity);
}
return (Entity) entity;
}

View File

@@ -50,6 +50,7 @@ import com.dfsek.terra.api.world.chunk.Chunk;
import com.dfsek.terra.api.world.chunk.generation.ChunkGenerator;
import com.dfsek.terra.mod.generation.MinecraftChunkGeneratorWrapper;
import com.dfsek.terra.mod.generation.TerraBiomeSource;
import com.dfsek.terra.mod.implmentation.FabricEntityTypeExtended;
import com.dfsek.terra.mod.util.MinecraftUtil;
@@ -69,10 +70,23 @@ public abstract class ServerWorldMixin extends World {
public abstract WorldTickScheduler<Fluid> getFluidTickScheduler();
public Entity terra$spawnEntity(double x, double y, double z, EntityType entityType) {
net.minecraft.entity.Entity entity = ((net.minecraft.entity.EntityType<?>) entityType).create(null, SpawnReason.CHUNK_GENERATION);
entity.setPos(x, y, z);
spawnEntity(entity);
public Entity terra$spawnEntity(double x, double y, double z, EntityType data) {
boolean isExtended = data.isExtended() && data.getClass().equals(FabricEntityTypeExtended.class);
net.minecraft.entity.Entity entity;
if(isExtended) {
FabricEntityTypeExtended type = ((FabricEntityTypeExtended) data);
NbtCompound nbt = (NbtCompound) ((Object) type.getData());
entity = net.minecraft.entity.EntityType.loadEntityWithPassengers(nbt, this, SpawnReason.CHUNK_GENERATION, (entityx) -> {
entityx.refreshPositionAndAngles(x, y, z, entityx.getYaw(), entityx.getPitch());
return entityx;
});
spawnEntity(entity);
} else {
entity = ((net.minecraft.entity.EntityType<?>) data).create(this, SpawnReason.CHUNK_GENERATION);
entity.setPos(x, y, z);
spawnEntity(entity);
}
return (Entity) entity;
}