Skip to content

Commit

Permalink
code cleanup / Litematic Entity NBT Save handling
Browse files Browse the repository at this point in the history
  • Loading branch information
sakura-ryoko committed Jul 3, 2024
1 parent ed8da90 commit 6870f1f
Show file tree
Hide file tree
Showing 5 changed files with 212 additions and 39 deletions.
131 changes: 123 additions & 8 deletions src/main/java/fi/dy/masa/litematica/data/EntitiesDataStorage.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,25 @@
import com.mojang.datafixers.util.Either;
import net.minecraft.block.BlockEntityProvider;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.block.entity.BlockEntityType;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.network.ClientPlayNetworkHandler;
import net.minecraft.entity.Entity;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.nbt.NbtList;
import net.minecraft.registry.Registries;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
import net.minecraft.world.chunk.Chunk;
import fi.dy.masa.malilib.interfaces.IClientTickHandler;
import fi.dy.masa.malilib.network.ClientPlayHandler;
import fi.dy.masa.malilib.network.IPluginClientPlayHandler;
import fi.dy.masa.malilib.util.Constants;
import fi.dy.masa.malilib.util.NBTUtils;
import fi.dy.masa.malilib.util.WorldUtils;
import fi.dy.masa.litematica.Litematica;
import fi.dy.masa.litematica.Reference;
import fi.dy.masa.litematica.config.Configs;
Expand Down Expand Up @@ -47,6 +56,8 @@ public static EntitiesDataStorage getInstance()
// Requests to be executed
private Set<BlockPos> pendingBlockEntitiesQueue = new LinkedHashSet<>();
private Set<Integer> pendingEntitiesQueue = new LinkedHashSet<>();
private Set<ChunkPos> pendingChunks = new LinkedHashSet<>();
private Set<ChunkPos> completedChunks = new LinkedHashSet<>();
// To save vanilla query packet transaction
private Map<Integer, Either<BlockPos, Integer>> transactionToBlockPosOrEntityId = new HashMap<>();

Expand Down Expand Up @@ -257,7 +268,7 @@ private void requestQueryBlockEntity(BlockPos pos)
{
handler.getDataQueryHandler().queryBlockNbt(pos, nbtCompound ->
{
handleBlockEntityData(pos, nbtCompound);
handleBlockEntityData(pos, nbtCompound, null);
});
transactionToBlockPosOrEntityId.put(((IMixinDataQueryHandler) handler.getDataQueryHandler()).currentTransactionId(), Either.left(pos));
}
Expand Down Expand Up @@ -302,24 +313,59 @@ private void requestServuxEntityData(int entityId)
HANDLER.encodeClientData(ServuxLitematicaPacket.EntityRequest(entityId));
}

// BlockEntity.createNbtWithIdentifyingData
// The minY, maxY should be calculated based on the Selection Box... But for now, we can just grab the entire chunk.
public void requestServuxBulkEntityData(ChunkPos chunkPos, int minY, int maxY)
{
if (this.hasServuxServer() == false)
{
return;
}

NbtCompound req = new NbtCompound();

if (this.completedChunks.contains(chunkPos))
{
this.completedChunks.remove(chunkPos);
}

this.pendingChunks.add(chunkPos);

if (minY < -60)
{
minY = -60;
}
if (maxY > 319)
{
maxY = 319;
}

req.putInt("minY", minY);
req.putInt("maxY", maxY);

Litematica.debugLog("EntitiesDataStorage#requestServuxBulkEntityData(): for chunkPos [{}] to Servux (minY [{}], maxY [{}])", chunkPos.toString(), minY, maxY);
HANDLER.encodeClientData(ServuxLitematicaPacket.BulkNbtRequest(chunkPos, req));
}

@Nullable
public BlockEntity handleBlockEntityData(BlockPos pos, NbtCompound nbt)
public BlockEntity handleBlockEntityData(BlockPos pos, NbtCompound nbt, @Nullable Identifier type)
{
pendingBlockEntitiesQueue.remove(pos);
if (nbt == null || this.getWorld() == null) return null;

BlockEntity blockEntity = this.getWorld().getBlockEntity(pos);
if (blockEntity != null)
if (blockEntity != null && (type == null || type.equals(BlockEntityType.getId(blockEntity.getType()))))
{
blockEntity.read(nbt, this.getWorld().getRegistryManager());
return blockEntity;
}
else

BlockEntityType<?> beType = Registries.BLOCK_ENTITY_TYPE.get(type);
if (beType != null && beType.supports(this.getWorld().getBlockState(pos)))
{
BlockEntity blockEntity2 = BlockEntity.createFromNbt(pos, this.getWorld().getBlockState(pos), nbt, mc.world.getRegistryManager());
BlockEntity blockEntity2 = beType.instantiate(pos, this.getWorld().getBlockState(pos));
if (blockEntity2 != null)
{
blockEntity2.read(nbt, this.getWorld().getRegistryManager());
this.getWorld().addBlockEntity(blockEntity2);
return blockEntity2;
}
Expand All @@ -328,7 +374,6 @@ public BlockEntity handleBlockEntityData(BlockPos pos, NbtCompound nbt)
return null;
}

// Entity.saveSelfNbt
@Nullable
public Entity handleEntityData(int entityId, NbtCompound nbt)
{
Expand All @@ -342,16 +387,86 @@ public Entity handleEntityData(int entityId, NbtCompound nbt)
return entity;
}

public void handleBulkEntityData(int transactionId, @Nullable NbtCompound nbt)
{
if (nbt == null)
{
return;
}
NbtList tileList = nbt.contains("TileEntities") ? nbt.getList("TileEntities", Constants.NBT.TAG_COMPOUND) : new NbtList();
NbtList entityList = nbt.contains("Entities") ? nbt.getList("Entities", Constants.NBT.TAG_COMPOUND) : new NbtList();
ChunkPos chunkPos = new ChunkPos(nbt.getInt("chunkX"), nbt.getInt("chunkZ"));

for (int i = 0; i < tileList.size(); ++i)
{
NbtCompound te = tileList.getCompound(i);
BlockPos pos = NBTUtils.readBlockPos(te);
Identifier type = Identifier.of(te.getString("id"));

handleBlockEntityData(pos, te, type);
}

for (int i = 0; i < entityList.size(); ++i)
{
NbtCompound ent = entityList.getCompound(i);
Vec3d pos = NBTUtils.readEntityPositionFromTag(ent);
int entityId = ent.getInt("entityId");

handleEntityData(entityId, ent);
}

if (this.pendingChunks.contains(chunkPos))
{
this.pendingChunks.remove(chunkPos);
}

this.completedChunks.add(chunkPos);

Litematica.debugLog("EntitiesDataStorage#handleBulkEntityData(): [ChunkPos {}] received TE: [{}], and E: [{}] entiries from Servux", chunkPos.toString(), tileList.size(), entityList.size());
}

public void handleVanillaQueryNbt(int transactionId, NbtCompound nbt)
{
Either<BlockPos, Integer> either = transactionToBlockPosOrEntityId.remove(transactionId);
if (either != null)
{
either.ifLeft(pos -> handleBlockEntityData(pos, nbt))
either.ifLeft(pos -> handleBlockEntityData(pos, nbt, null))
.ifRight(entityId -> handleEntityData(entityId, nbt));
}
}

public boolean hasPendingChunk(ChunkPos pos)
{
if (this.hasServuxServer())
{
return this.pendingChunks.contains(pos);
}
else
{
return false;
}
}

public boolean hasCompletedChunk(ChunkPos pos)
{
if (this.hasServuxServer())
{
return this.completedChunks.contains(pos);
}
else
{
return true;
}
}

public void markCompletedChunkDirty(ChunkPos pos)
{
if (this.hasServuxServer())
{
this.completedChunks.remove(pos);
}
}

// TODO --> Only in case we need to save config settings in the future
public JsonObject toJson()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ public <P extends IClientPayloadData> void decodeClientData(Identifier channel,
this.servuxRegistered = true;
}
}
case PACKET_S2C_BLOCK_NBT_RESPONSE_SIMPLE -> EntitiesDataStorage.getInstance().handleBlockEntityData(packet.getPos(), packet.getCompound());
case PACKET_S2C_BLOCK_NBT_RESPONSE_SIMPLE -> EntitiesDataStorage.getInstance().handleBlockEntityData(packet.getPos(), packet.getCompound(), null);
case PACKET_S2C_ENTITY_NBT_RESPONSE_SIMPLE -> EntitiesDataStorage.getInstance().handleEntityData(packet.getEntityId(), packet.getCompound());
case PACKET_S2C_NBT_RESPONSE_DATA ->
{
Expand All @@ -88,16 +88,15 @@ public <P extends IClientPayloadData> void decodeClientData(Identifier channel,
this.readingSessionKey = Random.create(Util.getMeasuringTimeMs()).nextLong();
}

Litematica.debugLog("ServuxLitematicaHandler#decodeClientData(): received Entity Data Packet Slice of size {} (in bytes) // reading session key [{}]", packet.getTotalSize(), this.readingSessionKey);
//Litematica.debugLog("ServuxLitematicaHandler#decodeClientData(): received Entity Data Packet Slice of size {} (in bytes) // reading session key [{}]", packet.getTotalSize(), this.readingSessionKey);
PacketByteBuf fullPacket = PacketSplitter.receive(this, this.readingSessionKey, packet.getBuffer());

if (fullPacket != null)
{
try
{
this.readingSessionKey = -1;
EntitiesDataStorage.getInstance().handleEntityData(fullPacket.readVarInt(), fullPacket.readNbt());
// FIXME --> handleBulkData
EntitiesDataStorage.getInstance().handleBulkEntityData(fullPacket.readVarInt(), fullPacket.readNbt());
}
catch (Exception e)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import net.minecraft.network.codec.PacketCodec;
import net.minecraft.network.packet.CustomPayload;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
import fi.dy.masa.malilib.network.IClientPayloadData;
import fi.dy.masa.litematica.Litematica;

Expand All @@ -18,6 +19,7 @@ public class ServuxLitematicaPacket implements IClientPayloadData
private int entityId;
private BlockPos pos;
private NbtCompound nbt;
private ChunkPos chunkPos;
private PacketByteBuf buffer;
public static final int PROTOCOL_VERSION = 1;

Expand All @@ -27,6 +29,7 @@ private ServuxLitematicaPacket(Type type)
this.transactionId = -1;
this.entityId = -1;
this.pos = BlockPos.ORIGIN;
this.chunkPos = ChunkPos.ORIGIN;
this.nbt = new NbtCompound();
this.clearPacket();
}
Expand Down Expand Up @@ -88,6 +91,17 @@ public static ServuxLitematicaPacket EntityRequest(int entityId)
return packet;
}

public static ServuxLitematicaPacket BulkNbtRequest(ChunkPos chunkPos, @Nullable NbtCompound nbt)
{
var packet = new ServuxLitematicaPacket(Type.PACKET_C2S_BULK_ENTITY_NBT_REQUEST);
packet.chunkPos = chunkPos;
if (nbt != null)
{
packet.nbt.copyFrom(nbt);
}
return packet;
}

// Nbt Packet, using Packet Splitter
public static ServuxLitematicaPacket ResponseS2CStart(@Nonnull NbtCompound nbt)
{
Expand Down Expand Up @@ -173,6 +187,8 @@ public NbtCompound getCompound()
return this.nbt;
}

public ChunkPos getChunkPos() { return this.chunkPos; }

public PacketByteBuf getBuffer()
{
return this.buffer;
Expand Down Expand Up @@ -245,6 +261,18 @@ public void toPacket(PacketByteBuf output)
Litematica.logger.error("ServuxEntitiesPacket#toPacket: error writing Entity Response to packet: [{}]", e.getLocalizedMessage());
}
}
case PACKET_C2S_BULK_ENTITY_NBT_REQUEST ->
{
try
{
output.writeChunkPos(this.chunkPos);
output.writeNbt(this.nbt);
}
catch (Exception e)
{
Litematica.logger.error("ServuxEntitiesPacket#toPacket: error writing Bulk Entity Request to packet: [{}]", e.getLocalizedMessage());
}
}
case PACKET_S2C_NBT_RESPONSE_DATA, PACKET_C2S_NBT_RESPONSE_DATA ->
{
// Write Packet Buffer (Slice)
Expand Down Expand Up @@ -335,6 +363,17 @@ public static ServuxLitematicaPacket fromPacket(PacketByteBuf input)
Litematica.logger.error("ServuxEntitiesPacket#fromPacket: error reading Entity Response from packet: [{}]", e.getLocalizedMessage());
}
}
case PACKET_C2S_BULK_ENTITY_NBT_REQUEST ->
{
try
{
return ServuxLitematicaPacket.BulkNbtRequest(input.readChunkPos(), input.readNbt());
}
catch (Exception e)
{
Litematica.logger.error("ServuxEntitiesPacket#fromPacket: error reading Bulk Entity Request from packet: [{}]", e.getLocalizedMessage());
}
}
case PACKET_S2C_NBT_RESPONSE_DATA ->
{
// Read Packet Buffer Slice
Expand Down Expand Up @@ -425,6 +464,7 @@ public enum Type
PACKET_C2S_ENTITY_REQUEST(4),
PACKET_S2C_BLOCK_NBT_RESPONSE_SIMPLE(5),
PACKET_S2C_ENTITY_NBT_RESPONSE_SIMPLE(6),
PACKET_C2S_BULK_ENTITY_NBT_REQUEST(7),
// For Packet Splitter (Oversize Packets, S2C)
PACKET_S2C_NBT_RESPONSE_START(10),
PACKET_S2C_NBT_RESPONSE_DATA(11),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
package fi.dy.masa.litematica.scheduler.tasks;

import java.io.File;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.*;
import javax.annotation.Nullable;
import com.google.common.collect.ImmutableMap;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.world.World;
import fi.dy.masa.litematica.data.EntitiesDataStorage;
import fi.dy.masa.litematica.data.SchematicHolder;
import fi.dy.masa.litematica.render.infohud.InfoHud;
import fi.dy.masa.litematica.schematic.LitematicaSchematic;
Expand Down Expand Up @@ -59,7 +58,35 @@ protected boolean canProcessChunk(ChunkPos pos)
{
return this.schematicWorld != null && this.schematicWorld.getChunkManager().isChunkLoaded(pos.x, pos.z);
}


// Request entity data from Servux, if the ClientWorld matches, and treat it as not yet loaded
if (EntitiesDataStorage.getInstance().hasServuxServer() &&
Objects.equals(EntitiesDataStorage.getInstance().getWorld(), this.clientWorld))
{
if (EntitiesDataStorage.getInstance().hasCompletedChunk(pos))
{
return this.areSurroundingChunksLoaded(pos, this.clientWorld, 0);
}
else if (EntitiesDataStorage.getInstance().hasPendingChunk(pos) == false)
{
ImmutableMap<String, IntBoundingBox> volumes = PositionUtils.getBoxesWithinChunk(pos.x, pos.z, this.subRegions);
int minY = 319; // Invert Values
int maxY = -60;

for (Map.Entry<String, IntBoundingBox> volumeEntry : volumes.entrySet())
{
IntBoundingBox bb = volumeEntry.getValue();

minY = Math.min(bb.minY, minY);
maxY = Math.max(bb.maxY, maxY);
}

EntitiesDataStorage.getInstance().requestServuxBulkEntityData(pos, minY, maxY);
}

return false;
}

return this.areSurroundingChunksLoaded(pos, this.clientWorld, 0);
}

Expand All @@ -75,6 +102,14 @@ protected boolean processChunk(ChunkPos pos)
this.schematic.takeEntitiesFromWorldWithinChunk(world, pos.x, pos.z, volumes, this.subRegions, this.existingEntities, this.origin);
}

if (EntitiesDataStorage.getInstance().hasServuxServer() &&
EntitiesDataStorage.getInstance().hasCompletedChunk(pos) &&
Objects.equals(EntitiesDataStorage.getInstance().getWorld(), this.clientWorld))
{
EntitiesDataStorage.getInstance().markCompletedChunkDirty(pos);
// Mark Dirty for refresh
}

return true;
}

Expand Down
Loading

0 comments on commit 6870f1f

Please sign in to comment.