Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix tag block related issues #704

Merged
merged 3 commits into from
Oct 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ minecraftVersion=1.21.1
#Comma seperated list of mc versions, which are marked as compatible on curseforge
additionalMinecraftVersions=1.21

blockUiVersion=1.0.188-1.21.1-snapshot
blockUiVersion=1.0.191-1.21.1-snapshot
domumOrnamentumVersion=1.0.203-1.21.1-snapshot

githubUrl=https://github.com/ldtteam/Structurize
Expand Down
33 changes: 33 additions & 0 deletions src/main/java/com/ldtteam/structurize/api/RotationMirror.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
package com.ldtteam.structurize.api;

import com.ldtteam.common.codec.Codecs;
import com.ldtteam.structurize.blueprints.FacingFixer;
import com.mojang.serialization.Codec;
import io.netty.buffer.ByteBuf;
import net.minecraft.core.BlockPos;
import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Mirror;
import net.minecraft.world.level.block.Rotation;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate;
import net.minecraft.world.phys.Vec3;

Expand Down Expand Up @@ -181,6 +184,36 @@ public Vec3 applyToPos(final Vec3 pos, final BlockPos pivot)
return StructureTemplate.transform(pos, mirror, rotation, pivot);
}

/**
* @param blockState blockState to transform
* @return transformed blockState using this rot+mir
* @deprecated use {@link #applyToBlockState(BlockState, Level, BlockPos)}, see vanilla methods for more info
*/
@Deprecated
public BlockState applyToBlockState(BlockState blockState)
{
if (isMirrored())
{
blockState = FacingFixer.fixMirroredFacing(blockState.mirror(mirror), blockState);
}
return blockState.rotate(rotation);
}

/**
* @param blockState blockState to transform
* @param level in which given blockState lives
* @param pos where the given blockState is in given level
* @return transformed blockState using this rot+mir
*/
public BlockState applyToBlockState(BlockState blockState, final Level level, final BlockPos pos)
{
if (isMirrored())
{
blockState = FacingFixer.fixMirroredFacing(blockState.mirror(mirror), blockState);
}
return blockState.rotate(level, pos, rotation);
}

/**
* @param end in which state we should end
* @return end - this = what it takes from this to end
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,24 @@
package com.ldtteam.structurize.blockentities;

import com.ldtteam.structurize.blockentities.interfaces.IBlueprintDataProviderBE;
import com.ldtteam.structurize.blueprints.v1.Blueprint;
import com.ldtteam.structurize.api.RotationMirror;
import com.ldtteam.structurize.api.Log;
import com.ldtteam.structurize.component.CapturedBlock;
import com.ldtteam.structurize.component.ModDataComponents;
import com.mojang.serialization.DynamicOps;
import net.minecraft.core.BlockPos;
import net.minecraft.core.HolderLookup;
import net.minecraft.core.component.DataComponentMap;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtOps;
import net.minecraft.nbt.NbtUtils;
import net.minecraft.nbt.Tag;
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket;
import net.minecraft.util.Tuple;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.*;

Expand All @@ -25,6 +27,12 @@
*/
public class BlockEntityTagSubstitution extends BlockEntity implements IBlueprintDataProviderBE
{
public static final String CAPTURED_BLOCK_TAG = "captured_block";
/**
* Up to 1.21.1
*/
public static final String CAPTURED_BLOCK_TAG_OLD = "replacement";

/**
* The schematic name of the block.
*/
Expand Down Expand Up @@ -54,7 +62,7 @@ public class BlockEntityTagSubstitution extends BlockEntity implements IBlueprin
/**
* Replacement block.
*/
private ReplacementBlock replacement = new ReplacementBlock();
private CapturedBlock replacement = CapturedBlock.EMPTY;

public BlockEntityTagSubstitution(final BlockPos pos, final BlockState state)
{
Expand Down Expand Up @@ -114,7 +122,7 @@ public BlockPos getTilePos()
* @return the replacement block details
*/
@NotNull
public ReplacementBlock getReplacement()
public CapturedBlock getReplacement()
{
return this.replacement;
}
Expand All @@ -123,16 +131,49 @@ public ReplacementBlock getReplacement()
public void loadAdditional( @NotNull final CompoundTag compound, final HolderLookup.Provider provider)
{
super.loadAdditional(compound, provider);
final DynamicOps<Tag> dynamicOps = provider.createSerializationContext(NbtOps.INSTANCE);

IBlueprintDataProviderBE.super.readSchematicDataFromNBT(compound);
this.replacement = new ReplacementBlock(compound, provider);
if (compound.contains(CAPTURED_BLOCK_TAG_OLD, Tag.TAG_COMPOUND))
{
final CompoundTag oldNbt = compound.getCompound(CAPTURED_BLOCK_TAG_OLD);
replacement = new CapturedBlock(NbtUtils.readBlockState(BuiltInRegistries.BLOCK.asLookup(), oldNbt.getCompound("b")),
Optional.of(oldNbt.getCompound("e")),
oldNbt.contains("i") ? ItemStack.parseOptional(provider, oldNbt.getCompound("i")) : ItemStack.EMPTY);
}
else
{
replacement = deserializeReplacement(compound, dynamicOps);
}
}

public static CapturedBlock deserializeReplacement(final CompoundTag compound, final DynamicOps<Tag> dynamicOps)
{
if (compound.getCompound(CAPTURED_BLOCK_TAG).isEmpty())
{
return CapturedBlock.EMPTY;
}
return CapturedBlock.CODEC.parse(dynamicOps, compound.get(CAPTURED_BLOCK_TAG)).resultOrPartial(error -> {
Log.getLogger()
.error("Parsing {} with data {}: {}", ModBlockEntities.TAG_SUBSTITUTION.getRegisteredName(), compound, error);
Log.getLogger().error("", new RuntimeException());
}).orElse(CapturedBlock.EMPTY);
}

@Override
public void saveAdditional(@NotNull final CompoundTag compound, final HolderLookup.Provider provider)
{
super.saveAdditional(compound, provider);
final DynamicOps<Tag> dynamicOps = provider.createSerializationContext(NbtOps.INSTANCE);
writeSchematicDataToNBT(compound);
this.replacement.write(compound, provider);

// this is still needed even with data components as of 1.21
serializeReplacement(compound, dynamicOps, replacement);
}

public static void serializeReplacement(final CompoundTag compound, final DynamicOps<Tag> dynamicOps, final CapturedBlock replacement)
{
compound.put(CAPTURED_BLOCK_TAG, CapturedBlock.CODEC.encodeStart(dynamicOps, replacement).getOrThrow());
}

@Override
Expand Down Expand Up @@ -169,199 +210,26 @@ public String getBlueprintPath()
@Override
public CompoundTag getUpdateTag(final HolderLookup.Provider provider)
{
final CompoundTag tag = new CompoundTag();
this.saveAdditional(tag, provider);
return tag;
return saveCustomOnly(provider);
}

/**
* Storage for information about the replacement block, if any.
*/
public static class ReplacementBlock
@Override
protected void applyImplicitComponents(final BlockEntity.DataComponentInput componentInput)
{
private static final String TAG_REPLACEMENT = "replacement";

private final BlockState blockstate;
private final CompoundTag blockentitytag;
private final ItemStack itemstack;

@Nullable private BlockEntity cachedBlockentity;

/**
* Construct
* @param blockstate the block state
* @param blockentity the block entity, if any
* @param itemstack the item stack
*/
@Deprecated(forRemoval = true, since = "1.21")
public ReplacementBlock(@NotNull final BlockState blockstate,
@Nullable final BlockEntity blockentity,
@NotNull final ItemStack itemstack)
{
throw new UnsupportedOperationException("Use compound tag ctor");
}

/**
* Construct
* @param blockstate the block state
* @param blockentity the block entity tag, if any
* @param itemstack the item stack
*/
public ReplacementBlock(@NotNull final BlockState blockstate,
@Nullable final CompoundTag blockentity,
@NotNull final ItemStack itemstack)
{
this.blockstate = blockstate;
this.blockentitytag = blockentity == null ? new CompoundTag() : blockentity.copy();
this.itemstack = itemstack;
}

/**
* Construct from tag
* @param tag the tag to load
*/
public ReplacementBlock(@NotNull CompoundTag tag, final HolderLookup.Provider provider)
{
final CompoundTag replacement = tag.getCompound(TAG_REPLACEMENT);
this.blockstate = NbtUtils.readBlockState(BuiltInRegistries.BLOCK.asLookup(), replacement.getCompound("b"));
this.blockentitytag = replacement.getCompound("e");
this.itemstack = replacement.contains("i") ? ItemStack.parseOptional(provider, replacement.getCompound("i")) : ItemStack.EMPTY;
}

/**
* Empty instance
*/
public ReplacementBlock()
{
this.blockstate = Blocks.AIR.defaultBlockState();
this.blockentitytag = new CompoundTag();
this.itemstack = ItemStack.EMPTY;
}

/**
* @return true if there is no replacement block set (assume air)
*/
public boolean isEmpty()
{
return this.blockstate.isAir();
}

/**
* @return the block state
*/
@NotNull
public BlockState getBlockState()
{
return this.blockstate;
}

/**
* @return the block entity tag
*/
@NotNull
public CompoundTag getBlockEntityTag()
{
return this.blockentitytag;
}

/**
* @return the item stack
*/
@NotNull
public ItemStack getItemStack()
{
return this.itemstack;
}

/**
* Creates and loads (once) the replacement block entity, or returns the preloaded one.
* @param pos the blockpos to use (ignored if already loaded)
* @return the new or cached entity, or null if there isn't one
*/
@Nullable
public BlockEntity getBlockEntity(final BlockPos pos, final HolderLookup.Provider provider)
{
if (this.cachedBlockentity == null)
{
this.cachedBlockentity = createBlockEntity(pos, provider);
}
return this.cachedBlockentity;
}

/**
* Always creates and loads a new replacement block entity, if needed.
* @param pos the blockpos to use
* @return the new entity, or null if there isn't one
*/
@Nullable
public BlockEntity createBlockEntity(final BlockPos pos, final HolderLookup.Provider provider)
{
return this.blockentitytag.isEmpty()
? null
: BlockEntity.loadStatic(pos, this.blockstate, this.blockentitytag, provider);
}

/**
* Serialisation
* @param tag the target tag
* @return the target tag, for convenience
*/
@NotNull
public CompoundTag write(@NotNull CompoundTag tag, final HolderLookup.Provider provider)
{
if (isEmpty())
{
tag.remove(TAG_REPLACEMENT);
}
else
{
final CompoundTag replacement = new CompoundTag();
replacement.put("b", NbtUtils.writeBlockState(this.blockstate));
if (this.blockentitytag.isEmpty())
{
replacement.remove("e");
}
else
{
replacement.put("e", this.blockentitytag);
}
replacement.put("i", this.itemstack.save(provider));

tag.put(TAG_REPLACEMENT, replacement);
}
return tag;
}

/**
* Creates a new single-block {@link Blueprint} for the replacement block.
* @return the blueprint
*/
@NotNull
public Blueprint createBlueprint(final HolderLookup.Provider provider)
{
final Blueprint blueprint = new Blueprint((short) 1, (short) 1, (short) 1, provider);
blueprint.addBlockState(BlockPos.ZERO, getBlockState());
blueprint.getTileEntities()[0][0][0] = getBlockEntityTag().isEmpty() ? null : getBlockEntityTag().copy();
return blueprint;
}
super.applyImplicitComponents(componentInput);
replacement = componentInput.getOrDefault(ModDataComponents.CAPTURED_BLOCK, CapturedBlock.EMPTY);
}

/**
* Rotates and mirrors the replacement data, in response to a blueprint containing this replacement block
* being rotated or mirrored.
*
* @param pos the world location for the replacement block
* @param rotationMirror the relative rotation/mirror
* @param level the (actual) world
* @return the new replacement data
*/
public ReplacementBlock rotateWithMirror(final BlockPos pos, final RotationMirror rotationMirror, final Level level)
{
final Blueprint blueprint = createBlueprint(level.registryAccess());
blueprint.setRotationMirrorRelative(rotationMirror, level);
@Override
protected void collectImplicitComponents(final DataComponentMap.Builder componentBuilder)
{
super.collectImplicitComponents(componentBuilder);
componentBuilder.set(ModDataComponents.CAPTURED_BLOCK, replacement);
}

final BlockState newBlockState = blueprint.getBlockState(BlockPos.ZERO);
final CompoundTag newBlockData = blueprint.getTileEntityData(pos, BlockPos.ZERO);
return new ReplacementBlock(newBlockState, newBlockData, this.getItemStack());
}
@Override
public void removeComponentsFromTag(final CompoundTag itemStackTag)
{
itemStackTag.remove(CAPTURED_BLOCK_TAG);
}
}
Loading