Make NBT and in-memory representation of Templates cheaper (rel: #1)
This commit is contained in:
parent
309ebaa29d
commit
b20efb8479
@ -30,7 +30,7 @@ if(rootProject.file("private.gradle").exists()) { //Publishing details
|
|||||||
|
|
||||||
archivesBaseName = "templates"
|
archivesBaseName = "templates"
|
||||||
group = "io.github.cottonmc"
|
group = "io.github.cottonmc"
|
||||||
version = "2.0.4+1.20.1"
|
version = "2.0.5+1.20.1"
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
@ -6,7 +6,10 @@ import net.minecraft.block.BlockState;
|
|||||||
import net.minecraft.block.Blocks;
|
import net.minecraft.block.Blocks;
|
||||||
import net.minecraft.block.entity.BlockEntity;
|
import net.minecraft.block.entity.BlockEntity;
|
||||||
import net.minecraft.block.entity.BlockEntityType;
|
import net.minecraft.block.entity.BlockEntityType;
|
||||||
|
import net.minecraft.item.BlockItem;
|
||||||
|
import net.minecraft.item.ItemStack;
|
||||||
import net.minecraft.nbt.NbtCompound;
|
import net.minecraft.nbt.NbtCompound;
|
||||||
|
import net.minecraft.nbt.NbtElement;
|
||||||
import net.minecraft.nbt.NbtHelper;
|
import net.minecraft.nbt.NbtHelper;
|
||||||
import net.minecraft.network.listener.ClientPlayPacketListener;
|
import net.minecraft.network.listener.ClientPlayPacketListener;
|
||||||
import net.minecraft.network.packet.Packet;
|
import net.minecraft.network.packet.Packet;
|
||||||
@ -16,22 +19,25 @@ import net.minecraft.server.world.ServerWorld;
|
|||||||
import net.minecraft.util.math.BlockPos;
|
import net.minecraft.util.math.BlockPos;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
public class TemplateEntity extends BlockEntity implements ThemeableBlockEntity {
|
public class TemplateEntity extends BlockEntity implements ThemeableBlockEntity {
|
||||||
protected BlockState renderedState = Blocks.AIR.getDefaultState();
|
protected BlockState renderedState = Blocks.AIR.getDefaultState();
|
||||||
|
protected byte bitfield = DEFAULT_BITFIELD;
|
||||||
|
|
||||||
//Whether the player has manually spent a redstone/glowstone item to upgrade the template.
|
protected static final int SPENT_GLOWSTONE_DUST_MASK = 0b00000001;
|
||||||
//It's possible to get templates that, e.g. glow, without manually spending a glowstone on them
|
protected static final int SPENT_REDSTONE_TORCH_MASK = 0b00000010;
|
||||||
//(put a froglight in a template!) Same for redstone activation. We need to separately store
|
protected static final int SPENT_POPPED_CHORUS_MASK = 0b00000100;
|
||||||
//whether a redstone/glowstone should be refunded when the player breaks the template, and wasting a
|
protected static final int EMITS_REDSTONE_MASK = 0b00001000;
|
||||||
//blockstate for it is a little silly, so, here you go.
|
protected static final int IS_SOLID_MASK = 0b00010000;
|
||||||
protected boolean spentGlowstoneDust = false;
|
protected static final byte DEFAULT_BITFIELD = IS_SOLID_MASK; //brand-new templates shall be solid
|
||||||
protected boolean spentRedstoneTorch = false;
|
|
||||||
protected boolean spentPoppedChorus = false;
|
|
||||||
|
|
||||||
protected boolean emitsRedstone;
|
//Using one-character names is a little brash, like, what if there's a mod that adds crap to the NBT of ever
|
||||||
protected boolean isSolid = true;
|
//block entity, and uses short names for the same reason I am (there are lots and lots of block entities).
|
||||||
|
//Kinda doubt it?
|
||||||
|
protected static final String BLOCKSTATE_KEY = "s";
|
||||||
|
protected static final String BITFIELD_KEY = "b";
|
||||||
|
|
||||||
public TemplateEntity(BlockEntityType<?> type, BlockPos pos, BlockState state) {
|
public TemplateEntity(BlockEntityType<?> type, BlockPos pos, BlockState state) {
|
||||||
super(type, pos, state);
|
super(type, pos, state);
|
||||||
@ -43,13 +49,18 @@ public class TemplateEntity extends BlockEntity implements ThemeableBlockEntity
|
|||||||
|
|
||||||
BlockState lastRenderedState = renderedState;
|
BlockState lastRenderedState = renderedState;
|
||||||
|
|
||||||
|
if(tag.contains("BlockState")) { //2.0.4 and earlier
|
||||||
renderedState = NbtHelper.toBlockState(Registries.BLOCK.getReadOnlyWrapper(), tag.getCompound("BlockState"));
|
renderedState = NbtHelper.toBlockState(Registries.BLOCK.getReadOnlyWrapper(), tag.getCompound("BlockState"));
|
||||||
spentGlowstoneDust = tag.getBoolean("spentglow");
|
|
||||||
spentRedstoneTorch = tag.getBoolean("spentredst");
|
|
||||||
spentPoppedChorus = tag.getBoolean("spentchor");
|
|
||||||
|
|
||||||
emitsRedstone = tag.getBoolean("emitsredst");
|
if(tag.getBoolean("spentglow")) spentGlowstoneDust();
|
||||||
isSolid = !tag.contains("solid") || tag.getBoolean("solid"); //default to "true" if it's nonexistent
|
if(tag.getBoolean("spentredst")) spentRedstoneTorch();
|
||||||
|
if(tag.getBoolean("spentchor")) spentPoppedChorus();
|
||||||
|
setEmitsRedstone(tag.getBoolean("emitsredst"));
|
||||||
|
setSolidity(!tag.contains("solid") || tag.getBoolean("solid")); //default to "true" if it's nonexistent
|
||||||
|
} else {
|
||||||
|
renderedState = NbtHelper.toBlockState(Registries.BLOCK.getReadOnlyWrapper(), tag.getCompound(BLOCKSTATE_KEY));
|
||||||
|
bitfield = tag.contains(BITFIELD_KEY) ? tag.getByte(BITFIELD_KEY) : DEFAULT_BITFIELD;
|
||||||
|
}
|
||||||
|
|
||||||
//Force a chunk remesh on the client if the displayed blockstate has changed
|
//Force a chunk remesh on the client if the displayed blockstate has changed
|
||||||
//TODO: doors? (need remeshing when the *other* party changes)
|
//TODO: doors? (need remeshing when the *other* party changes)
|
||||||
@ -61,15 +72,97 @@ public class TemplateEntity extends BlockEntity implements ThemeableBlockEntity
|
|||||||
@Override
|
@Override
|
||||||
public void writeNbt(NbtCompound tag) {
|
public void writeNbt(NbtCompound tag) {
|
||||||
super.writeNbt(tag);
|
super.writeNbt(tag);
|
||||||
tag.put("BlockState", NbtHelper.fromBlockState(renderedState));
|
|
||||||
tag.putBoolean("spentglow", spentGlowstoneDust);
|
|
||||||
tag.putBoolean("spentredst", spentRedstoneTorch);
|
|
||||||
tag.putBoolean("spentchor", spentPoppedChorus);
|
|
||||||
|
|
||||||
tag.putBoolean("emitsredst", emitsRedstone);
|
if(renderedState != Blocks.AIR.getDefaultState()) tag.put(BLOCKSTATE_KEY, NbtHelper.fromBlockState(renderedState));
|
||||||
tag.putBoolean("solid", isSolid);
|
if(bitfield != DEFAULT_BITFIELD) tag.putByte(BITFIELD_KEY, bitfield);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static @Nonnull BlockState readStateFromItem(ItemStack stack) {
|
||||||
|
NbtCompound blockEntityTag = BlockItem.getBlockEntityNbt(stack);
|
||||||
|
if(blockEntityTag == null) return Blocks.AIR.getDefaultState();
|
||||||
|
|
||||||
|
//slightly paranoid NBT handling cause you never know what mysteries are afoot with items
|
||||||
|
NbtElement subElement;
|
||||||
|
if(blockEntityTag.contains(BLOCKSTATE_KEY)) subElement = blockEntityTag.get(BLOCKSTATE_KEY); //2.0.5
|
||||||
|
else if(blockEntityTag.contains("BlockState")) subElement = blockEntityTag.get("BlockState"); //old 2.0.4 items
|
||||||
|
else return Blocks.AIR.getDefaultState();
|
||||||
|
|
||||||
|
if(!(subElement instanceof NbtCompound subCompound)) return Blocks.AIR.getDefaultState();
|
||||||
|
|
||||||
|
else return NbtHelper.toBlockState(Registries.BLOCK.getReadOnlyWrapper(), subCompound);
|
||||||
|
}
|
||||||
|
|
||||||
|
//RenderAttachmentBlockEntity impl. Note that ThemeableBlockEntity depends on this returning a BlockState object.
|
||||||
|
@Override
|
||||||
|
public BlockState getRenderAttachmentData() {
|
||||||
|
return renderedState;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRenderedState(BlockState newState) {
|
||||||
|
if(!Objects.equals(renderedState, newState)) {
|
||||||
|
renderedState = newState;
|
||||||
|
markDirtyAndDispatch();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasSpentGlowstoneDust() {
|
||||||
|
return (bitfield & SPENT_GLOWSTONE_DUST_MASK) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void spentGlowstoneDust() {
|
||||||
|
bitfield |= SPENT_GLOWSTONE_DUST_MASK;
|
||||||
|
markDirtyAndDispatch();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasSpentRedstoneTorch() {
|
||||||
|
return (bitfield & SPENT_REDSTONE_TORCH_MASK) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void spentRedstoneTorch() {
|
||||||
|
bitfield |= SPENT_REDSTONE_TORCH_MASK;
|
||||||
|
markDirtyAndDispatch();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasSpentPoppedChorus() {
|
||||||
|
return (bitfield & SPENT_POPPED_CHORUS_MASK) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void spentPoppedChorus() {
|
||||||
|
bitfield |= SPENT_POPPED_CHORUS_MASK;
|
||||||
|
markDirtyAndDispatch();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean emitsRedstone() {
|
||||||
|
return (bitfield & EMITS_REDSTONE_MASK) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEmitsRedstone(boolean nextEmitsRedstone) {
|
||||||
|
boolean currentlyEmitsRedstone = emitsRedstone();
|
||||||
|
|
||||||
|
if(currentlyEmitsRedstone != nextEmitsRedstone) {
|
||||||
|
if(currentlyEmitsRedstone) bitfield &= ~EMITS_REDSTONE_MASK;
|
||||||
|
else bitfield |= EMITS_REDSTONE_MASK;
|
||||||
|
markDirtyAndDispatch();
|
||||||
|
if(world != null) world.updateNeighbors(pos, getCachedState().getBlock());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSolid() {
|
||||||
|
return (bitfield & IS_SOLID_MASK) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSolidity(boolean nextSolid) {
|
||||||
|
boolean currentlySolid = isSolid();
|
||||||
|
|
||||||
|
if(currentlySolid != nextSolid) {
|
||||||
|
if(currentlySolid) bitfield &= ~IS_SOLID_MASK;
|
||||||
|
else bitfield |= IS_SOLID_MASK;
|
||||||
|
markDirtyAndDispatch();
|
||||||
|
if(world != null) world.setBlockState(pos, getCachedState()); //do i need to invalidate any shape caches or something
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//<standard blockentity boilerplate>
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public Packet<ClientPlayPacketListener> toUpdatePacket() {
|
public Packet<ClientPlayPacketListener> toUpdatePacket() {
|
||||||
@ -83,76 +176,13 @@ public class TemplateEntity extends BlockEntity implements ThemeableBlockEntity
|
|||||||
return createNbt();
|
return createNbt();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public BlockState getRenderAttachmentData() {
|
|
||||||
return renderedState;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void dispatch() {
|
protected void dispatch() {
|
||||||
if(world instanceof ServerWorld sworld) sworld.getChunkManager().markForUpdate(pos);
|
if(world instanceof ServerWorld sworld) sworld.getChunkManager().markForUpdate(pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setRenderedState(BlockState newState) {
|
protected void markDirtyAndDispatch() {
|
||||||
if(!Objects.equals(renderedState, newState)) {
|
|
||||||
renderedState = newState;
|
|
||||||
markDirty();
|
markDirty();
|
||||||
dispatch();
|
dispatch();
|
||||||
}
|
}
|
||||||
}
|
//</standard blockentity boilerplate>
|
||||||
|
|
||||||
public boolean hasSpentGlowstoneDust() {
|
|
||||||
return spentGlowstoneDust;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void spentGlowstoneDust() {
|
|
||||||
spentGlowstoneDust = true;
|
|
||||||
markDirty();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasSpentRedstoneTorch() {
|
|
||||||
return spentRedstoneTorch;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void spentRedstoneTorch() {
|
|
||||||
spentRedstoneTorch = true;
|
|
||||||
markDirty();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasSpentPoppedChorus() {
|
|
||||||
return spentPoppedChorus;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void spentPoppedChorus() {
|
|
||||||
spentPoppedChorus = true;
|
|
||||||
markDirty();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean emitsRedstone() {
|
|
||||||
return emitsRedstone;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setEmitsRedstone(boolean emitsRedstone) {
|
|
||||||
if(this.emitsRedstone != emitsRedstone) {
|
|
||||||
this.emitsRedstone = emitsRedstone;
|
|
||||||
markDirty();
|
|
||||||
|
|
||||||
if(world != null) world.updateNeighbors(pos, getCachedState().getBlock());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isSolid() {
|
|
||||||
return isSolid;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSolidity(boolean isSolid) {
|
|
||||||
if(this.isSolid != isSolid) {
|
|
||||||
this.isSolid = isSolid;
|
|
||||||
markDirty();
|
|
||||||
|
|
||||||
//do i need to invalidate any shape caches or something
|
|
||||||
if(world != null) world.setBlockState(pos, getCachedState());
|
|
||||||
|
|
||||||
dispatch();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package io.github.cottonmc.templates.model;
|
package io.github.cottonmc.templates.model;
|
||||||
|
|
||||||
|
import io.github.cottonmc.templates.block.TemplateEntity;
|
||||||
import io.github.cottonmc.templates.mixin.MinecraftAccessor;
|
import io.github.cottonmc.templates.mixin.MinecraftAccessor;
|
||||||
import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh;
|
import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh;
|
||||||
import net.fabricmc.fabric.api.renderer.v1.mesh.MutableQuadView;
|
import net.fabricmc.fabric.api.renderer.v1.mesh.MutableQuadView;
|
||||||
@ -11,11 +12,7 @@ import net.minecraft.client.MinecraftClient;
|
|||||||
import net.minecraft.client.render.model.BakedModel;
|
import net.minecraft.client.render.model.BakedModel;
|
||||||
import net.minecraft.client.render.model.ModelBakeSettings;
|
import net.minecraft.client.render.model.ModelBakeSettings;
|
||||||
import net.minecraft.client.texture.Sprite;
|
import net.minecraft.client.texture.Sprite;
|
||||||
import net.minecraft.item.BlockItem;
|
|
||||||
import net.minecraft.item.ItemStack;
|
import net.minecraft.item.ItemStack;
|
||||||
import net.minecraft.nbt.NbtCompound;
|
|
||||||
import net.minecraft.nbt.NbtHelper;
|
|
||||||
import net.minecraft.registry.Registries;
|
|
||||||
import net.minecraft.util.math.BlockPos;
|
import net.minecraft.util.math.BlockPos;
|
||||||
import net.minecraft.util.math.Direction;
|
import net.minecraft.util.math.Direction;
|
||||||
import net.minecraft.util.math.random.Random;
|
import net.minecraft.util.math.random.Random;
|
||||||
@ -86,19 +83,17 @@ public abstract class RetexturingBakedModel extends ForwardingBakedModel {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void emitItemQuads(ItemStack stack, Supplier<Random> randomSupplier, RenderContext context) {
|
public void emitItemQuads(ItemStack stack, Supplier<Random> randomSupplier, RenderContext context) {
|
||||||
TemplateAppearance nbtAppearance = tam.getDefaultAppearance();
|
|
||||||
int tint = 0xFFFFFFFF;
|
|
||||||
|
|
||||||
//cheeky: if the item has NBT data, pluck out the blockstate from it & look up the item color provider
|
//cheeky: if the item has NBT data, pluck out the blockstate from it & look up the item color provider
|
||||||
//none of this is accessible unless you're in creative mode doing ctrl-pick btw
|
//none of this is accessible unless you're in creative mode doing ctrl-pick btw
|
||||||
NbtCompound tag = BlockItem.getBlockEntityNbt(stack);
|
TemplateAppearance nbtAppearance;
|
||||||
if(tag != null && tag.contains("BlockState")) {
|
int tint;
|
||||||
BlockState theme = NbtHelper.toBlockState(Registries.BLOCK.getReadOnlyWrapper(), tag.getCompound("BlockState"));
|
BlockState theme = TemplateEntity.readStateFromItem(stack);
|
||||||
if(!theme.isAir()) {
|
if(!theme.isAir()) {
|
||||||
nbtAppearance = tam.getAppearance(theme);
|
nbtAppearance = tam.getAppearance(theme);
|
||||||
|
|
||||||
tint = 0xFF000000 | ((MinecraftAccessor) MinecraftClient.getInstance()).templates$getItemColors().getColor(new ItemStack(theme.getBlock()), 0);
|
tint = 0xFF000000 | ((MinecraftAccessor) MinecraftClient.getInstance()).templates$getItemColors().getColor(new ItemStack(theme.getBlock()), 0);
|
||||||
}
|
} else {
|
||||||
|
nbtAppearance = tam.getDefaultAppearance();
|
||||||
|
tint = 0xFFFFFFFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
Mesh untintedMesh = getUntintedRetexturedMesh(new CacheKey(itemModelState, nbtAppearance));
|
Mesh untintedMesh = getUntintedRetexturedMesh(new CacheKey(itemModelState, nbtAppearance));
|
||||||
|
Loading…
Reference in New Issue
Block a user