public class CustomBlockTracker { private final GlobalBlockContainer globalContainer = GlobalBlockContainer.of(); public CustomBlockTracker() { Bukkit.getScheduler().runTaskTimer(CorePlugin.getInstance(), this::tickBlocks, 1, 1); } public TableBlock getBlockAt(Block block) { return globalContainer.getBlock(block.getLocation()); } public void registerBlock(TableBlock block) { globalContainer.setBlock(block.getPosition(), block); } public void removeBlock(TableBlock block) { globalContainer.removeBlock(block.getPosition()); } public void handleChunkLoad(Chunk chunk) { PDCContainer container = PDCContainer.wrap(chunk); PDCContainer tableDataContainer = container.getContainer("table-data"); if (tableDataContainer == null) { return; } List dataContainers = tableDataContainer.get("entries", PersistentDataType.LIST.dataContainers()); if (dataContainers == null) { return; } for (PersistentDataContainer dataContainer : dataContainers) { PDCContainer data = PDCContainer.wrap(dataContainer); BlockEntityType type = TableTypes.get(data.getString("type")); if (type == null) { continue; } TableBlock block = createBlock(type, data); registerBlock(block); } } public void handleChunkUnload(Chunk chunk) { saveChunk(chunk, holder -> { TableBlock block = (TableBlock) holder.getData(); block.dispose(TableDisposalReason.CHUNK_UNLOAD); }); WorldBlockContainer container = globalContainer.getWorldContainer(chunk.getWorld()); if (container == null) { return; } container.removeChunkContainer(chunk); } public void saveChunk(Chunk chunk) { saveChunk(chunk, holder -> {}); } public void saveChunk(Chunk chunk, Consumer> callback) { World world = chunk.getWorld(); ChunkKey key = ChunkKey.fromChunk(chunk); WorldBlockContainer container = globalContainer.getWorldContainer(world); if (container == null) { return; } ChunkBlockContainer chunkContainer = container.getChunkContainer(key); if (chunkContainer == null) { return; } PDCContainer data = PDCContainer.wrap(chunk).getOrCreateContainer("table-data"); List dataContainers = new ArrayList<>(); for (BlockHolder holder : chunkContainer.getAllBlocks()) { TableBlock block = (TableBlock) holder.getData(); PDCContainer blockData = data.newContainer(); saveBlock(block, blockData); dataContainers.add(blockData); callback.accept(holder); } if (!dataContainers.isEmpty()) { List unwrapped = dataContainers.stream().map(PDCContainer::unwrap).toList(); data.set("entries", PersistentDataType.LIST.dataContainers(), unwrapped); } } private TableBlock createBlock(BlockEntityType type, PDCContainer data) { Location location = data.getLocation("position"); return type.createBlock(type.getCodec().get(data, "data"), location); } private void saveBlock(TableBlock block, PDCContainer data) { data.setLocation("position", block.getPosition()); block.getType().getCodec().set(data, "data", block.getData()); } private void tickBlocks() { for (BlockHolder holder : globalContainer.getAllBlocks()) { if (holder.getData() instanceof TickableBlock tickable) { tickable.tick(); } } } }