package io.lampnet.travelerssuitcase.block.entity; import io.lampnet.travelerssuitcase.block.PocketPortalBlock; import net.minecraft.core.BlockPos; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.ListTag; import net.minecraft.nbt.Tag; import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; import org.jetbrains.annotations.Nullable; import java.util.*; public class SuitcaseBlockEntity extends BlockEntity { private String boundKeystoneName; private boolean isLocked = false; private boolean dimensionLocked = true; private final List enteredPlayers = new ArrayList<>(); public static class EnteredPlayerData { public final String uuid; public final double x, y, z; public final float pitch, yaw; public final BlockPos suitcasePos; public EnteredPlayerData(String uuid, double x, double y, double z, float pitch, float yaw, BlockPos suitcasePos) { this.uuid = uuid; this.x = x; this.y = y; this.z = z; this.pitch = pitch; this.yaw = yaw; this.suitcasePos = suitcasePos; } public CompoundTag toNbt() { CompoundTag nbt = new CompoundTag(); nbt.putString("UUID", uuid); nbt.putDouble("X", x); nbt.putDouble("Y", y); nbt.putDouble("Z", z); nbt.putFloat("Pitch", pitch); nbt.putFloat("Yaw", yaw); if (suitcasePos != null) { nbt.putInt("SuitcaseX", suitcasePos.getX()); nbt.putInt("SuitcaseY", suitcasePos.getY()); nbt.putInt("SuitcaseZ", suitcasePos.getZ()); } return nbt; } public static EnteredPlayerData fromNbt(CompoundTag nbt) { BlockPos sPos = null; if (nbt.contains("SuitcaseX") && nbt.contains("SuitcaseY") && nbt.contains("SuitcaseZ")) { sPos = new BlockPos(nbt.getInt("SuitcaseX"), nbt.getInt("SuitcaseY"), nbt.getInt("SuitcaseZ")); } return new EnteredPlayerData( nbt.getString("UUID"), nbt.getDouble("X"), nbt.getDouble("Y"), nbt.getDouble("Z"), nbt.getFloat("Pitch"), nbt.getFloat("Yaw"), sPos ); } } public SuitcaseBlockEntity(BlockPos pos, BlockState state) { super(ModBlockEntities.SUITCASE_BLOCK_ENTITY.get(), pos, state); } public boolean canOpenInDimension(Level level) { if (!dimensionLocked) { return true; } return level.dimension() == Level.OVERWORLD; } public boolean isDimensionLocked() { return dimensionLocked; } public void playerEntered(ServerPlayer player) { enteredPlayers.removeIf(data -> data.uuid.equals(player.getUUID().toString())); EnteredPlayerData data = new EnteredPlayerData( player.getUUID().toString(), player.getX(), player.getY(), player.getZ(), player.getXRot(), player.getYRot(), this.worldPosition ); enteredPlayers.add(data); if (boundKeystoneName != null) { Map suitcases = SUITCASE_REGISTRY.computeIfAbsent( boundKeystoneName, k -> new HashMap<>() ); suitcases.put(player.getUUID().toString(), this.worldPosition); } PocketPortalBlock.storePlayerPosition(player); setChangedAndNotify(); } public EnteredPlayerData getExitPosition(String playerUuid) { for (int i = 0; i < enteredPlayers.size(); i++) { EnteredPlayerData data = enteredPlayers.get(i); if (data.uuid.equals(playerUuid)) { EnteredPlayerData exitData = new EnteredPlayerData( data.uuid, this.worldPosition.getX() + 0.5, this.worldPosition.getY() + 1.0, this.worldPosition.getZ() + 0.5, data.pitch, data.yaw, this.worldPosition ); enteredPlayers.remove(i); setChangedAndNotify(); return exitData; } } return null; } public void bindKeystone(String keystoneName) { this.boundKeystoneName = keystoneName; setChangedAndNotify(); } public String getBoundKeystoneName() { return boundKeystoneName; } public void setLocked(boolean locked) { this.isLocked = locked; setChangedAndNotify(); } public boolean isLocked() { return isLocked; } @Override public void saveAdditional(CompoundTag nbt) { super.saveAdditional(nbt); if (boundKeystoneName != null) { nbt.putString("BoundKeystone", boundKeystoneName); } nbt.putBoolean("Locked", isLocked); nbt.putBoolean("DimensionLocked", dimensionLocked); ListTag playersListTag = new ListTag(); for (EnteredPlayerData data : enteredPlayers) { playersListTag.add(data.toNbt()); } if (!playersListTag.isEmpty()) { nbt.put("EnteredPlayers", playersListTag); } } @Override public void load(CompoundTag nbt) { super.load(nbt); if (nbt.contains("BoundKeystone", Tag.TAG_STRING)) { boundKeystoneName = nbt.getString("BoundKeystone"); } else { boundKeystoneName = null; } isLocked = nbt.getBoolean("Locked"); dimensionLocked = !nbt.contains("DimensionLocked") || nbt.getBoolean("DimensionLocked"); enteredPlayers.clear(); if (nbt.contains("EnteredPlayers", Tag.TAG_LIST)) { ListTag playersListTag = nbt.getList("EnteredPlayers", Tag.TAG_COMPOUND); for (int i = 0; i < playersListTag.size(); i++) { enteredPlayers.add(EnteredPlayerData.fromNbt(playersListTag.getCompound(i))); } } } @Nullable @Override public ClientboundBlockEntityDataPacket getUpdatePacket() { return ClientboundBlockEntityDataPacket.create(this); } @Override public CompoundTag getUpdateTag() { CompoundTag nbt = new CompoundTag(); this.saveAdditional(nbt); return nbt; } public static final Map> SUITCASE_REGISTRY = Collections.synchronizedMap(new HashMap<>()); public static BlockPos findSuitcasePosition(String keystoneName, String playerUuid) { Map suitcases = SUITCASE_REGISTRY.get(keystoneName); return (suitcases != null) ? suitcases.get(playerUuid) : null; } public static void removeSuitcaseEntry(String keystoneName, String playerUuid) { Map suitcases = SUITCASE_REGISTRY.get(keystoneName); if (suitcases != null) { suitcases.remove(playerUuid); if (suitcases.isEmpty()) { SUITCASE_REGISTRY.remove(keystoneName); } } } public List getEnteredPlayers() { return Collections.unmodifiableList(enteredPlayers); } public static void initializeSuitcaseRegistry(Map> savedRegistry) { SUITCASE_REGISTRY.clear(); if (savedRegistry != null) { savedRegistry.forEach((key, value) -> { Map players = SUITCASE_REGISTRY.computeIfAbsent(key, k -> Collections.synchronizedMap(new HashMap<>())); players.putAll(value); }); } } public static void saveSuitcaseRegistryTo(Map> destination) { destination.clear(); SUITCASE_REGISTRY.forEach((key, value) -> { Map players = destination.computeIfAbsent(key, k -> new HashMap<>()); players.putAll(value); }); } public void updatePlayerSuitcasePosition(String playerUuid, BlockPos newPos) { boolean updated = false; for (int i = 0; i < enteredPlayers.size(); i++) { EnteredPlayerData data = enteredPlayers.get(i); if (data.uuid.equals(playerUuid)) { enteredPlayers.set(i, new EnteredPlayerData(data.uuid, data.x, data.y, data.z, data.pitch, data.yaw, newPos)); updated = true; break; } } if (updated && boundKeystoneName != null) { Map suitcases = SUITCASE_REGISTRY.computeIfAbsent(boundKeystoneName, k -> new HashMap<>()); suitcases.put(playerUuid, newPos); } if(updated) setChangedAndNotify(); } private void setChangedAndNotify() { setChanged(); if (level != null && !level.isClientSide()) { level.sendBlockUpdated(worldPosition, getBlockState(), getBlockState(), Block.UPDATE_ALL); } } }