@Override public void purgeWorld() { ChunkMonitor.fireChunkProviderDisposed(this); pipeline.shutdown(); unloadRequestTaskMaster.shutdown(new ChunkUnloadRequest(), true); lightMerger.shutdown(); for (Chunk chunk : nearCache.values()) { if (chunk.isReady()) { worldEntity.send(new BeforeChunkUnload(chunk.getPosition())); storageManager.deactivateChunk(chunk); chunk.dispose(); } } nearCache.clear(); readyChunks.clear(); sortedReadyChunks.clear(); storageManager.deleteWorld(); preparingChunks.clear(); worldEntity.send(new PurgeWorldEvent()); pipeline = new ChunkGenerationPipeline(new ChunkTaskRelevanceComparator()); unloadRequestTaskMaster = TaskMaster.createFIFOTaskMaster("Chunk-Unloader", 8); lightMerger = new LightMerger<>(this); lightMerger.restart(); ChunkMonitor.fireChunkProviderInitialized(this); for (ChunkRelevanceRegion chunkRelevanceRegion : regions.values()) { for (Vector3i pos : chunkRelevanceRegion.getCurrentRegion()) { createOrLoadChunk(pos); } chunkRelevanceRegion.setUpToDate(); } }
@Override public Block getBlockAt(Vector3i pos) { int index = chunkIndexOf(pos); Chunk chunk = chunks[index]; if (chunk != null) { return chunk.getBlock(ChunkMath.calcBlockPos(pos)); } return null; }
private boolean areAdjacentChunksReady(Chunk chunk) { Vector3i centerChunkPos = chunk.getPosition(); for (Side side : Side.values()) { Vector3i adjChunkPos = side.getAdjacentPos(centerChunkPos); Chunk adjChunk = nearCache.get(adjChunkPos); boolean adjChunkReady = (adjChunk != null && adjChunk.isReady()); if (!adjChunkReady) { return false; } } return true; }
private ChunkViewCore createWorldView(Region3i region, Vector3i offset) { Chunk[] chunks = new Chunk[region.sizeX() * region.sizeY() * region.sizeZ()]; for (Vector3i chunkPos : region) { Chunk chunk = nearCache.get(chunkPos); if (chunk == null || !chunk.isReady()) { return null; } chunkPos.sub(region.minX(), region.minY(), region.minZ()); int index = TeraMath.calculate3DArrayIndex(chunkPos, region.size()); chunks[index] = chunk; } return new ChunkViewCoreImpl( chunks, region, offset, blockManager.getBlock(BlockManager.AIR_ID)); }
@Override public void dispose() { shutdown(); for (Chunk chunk : nearCache.values()) { unloadChunkInternal(chunk.getPosition()); chunk.dispose(); } nearCache.clear(); /* * The chunk monitor needs to clear chunk references, so it's important * that no new chunk get created */ ChunkMonitor.fireChunkProviderDisposed(this); }
void gatherBlockPositionsForDeactivate(Chunk chunk) { try { deactivateBlocksQueue.put(createBatchBlockEventMappings(chunk)); } catch (InterruptedException e) { logger.error("Failed to queue deactivation of blocks for {}", chunk.getPosition()); } }
private void updateRelevance() { for (ChunkRelevanceRegion chunkRelevanceRegion : regions.values()) { chunkRelevanceRegion.update(); if (chunkRelevanceRegion.isDirty()) { for (Vector3i pos : chunkRelevanceRegion.getNeededChunks()) { Chunk chunk = nearCache.get(pos); if (chunk != null && chunk.isReady()) { chunkRelevanceRegion.chunkReady(chunk); } else if (chunk == null) { createOrLoadChunk(pos); } } chunkRelevanceRegion.setUpToDate(); } } }
// Generates all non-temporary block entities private void generateBlockEntities(Chunk chunk) { ChunkBlockIterator i = chunk.getBlockIterator(); while (i.next()) { if (i.getBlock().isKeepActive()) { registry.getBlockEntityAt(i.getBlockPos()); } } }
@Override public void deactivateChunk(Chunk chunk) { Collection<EntityRef> entitiesOfChunk = getEntitiesOfChunk(chunk); ChunkImpl chunkImpl = (ChunkImpl) chunk; // storage manager only works with ChunkImpl unloadedAndUnsavedChunkMap.put( chunk.getPosition(), new CompressedChunkBuilder(getEntityManager(), chunkImpl, entitiesOfChunk, true)); entitiesOfChunk.forEach(this::deactivateOrDestroyEntityRecursive); }
private void updateAdjacentChunksReadyFieldOfAdjChunks(Chunk chunkInCenter) { Vector3i centerChunkPos = chunkInCenter.getPosition(); for (Side side : Side.values()) { Vector3i adjChunkPos = side.getAdjacentPos(centerChunkPos); Chunk adjChunk = nearCache.get(adjChunkPos); if (adjChunk != null) { updateAdjacentChunksReadyFieldOf(adjChunk); } } }
private static synchronized ChunkMonitorEntry registerChunk(Chunk chunk) { Preconditions.checkNotNull(chunk, "The parameter 'chunk' must not be null"); final Vector3i pos = chunk.getPosition(); ChunkMonitorEntry entry = CHUNKS.get(pos); if (entry == null) { entry = new ChunkMonitorEntry(pos); CHUNKS.put(pos, entry); } entry.addChunk(chunk); return entry; }
private TShortObjectMap<TIntList> createBatchBlockEventMappings(Chunk chunk) { TShortObjectMap<TIntList> batchBlockMap = new TShortObjectHashMap<>(); for (Block block : blockManager.listRegisteredBlocks()) { if (block.isLifecycleEventsRequired()) { batchBlockMap.put(block.getId(), new TIntArrayList()); } } ChunkBlockIterator i = chunk.getBlockIterator(); while (i.next()) { if (i.getBlock().isLifecycleEventsRequired()) { TIntList positionList = batchBlockMap.get(i.getBlock().getId()); positionList.add(i.getBlockPos().x); positionList.add(i.getBlockPos().y); positionList.add(i.getBlockPos().z); } } return batchBlockMap; }
private boolean unloadChunkInternal(Vector3i pos) { Chunk chunk = nearCache.get(pos); if (chunk.isLocked()) { return false; } chunk.lock(); try { if (!chunk.isReady()) { // Chunk hasn't been finished or changed, so just drop it. Iterator<ReadyChunkInfo> infoIterator = sortedReadyChunks.iterator(); while (infoIterator.hasNext()) { ReadyChunkInfo next = infoIterator.next(); if (next.getPos().equals(chunk.getPosition())) { infoIterator.remove(); break; } } return true; } worldEntity.send(new BeforeChunkUnload(pos)); for (ChunkRelevanceRegion region : regions.values()) { region.chunkUnloaded(pos); } storageManager.deactivateChunk(chunk); chunk.dispose(); updateAdjacentChunksReadyFieldOfAdjChunks(chunk); try { unloadRequestTaskMaster.put(new ChunkUnloadRequest(chunk, this)); } catch (InterruptedException e) { logger.error("Failed to enqueue unload request for {}", chunk.getPosition(), e); } return true; } finally { chunk.unlock(); } }
private boolean isChunkReady(Chunk chunk) { return chunk != null && chunk.isReady(); }
@Override public void completeUpdate() { ReadyChunkInfo readyChunkInfo = lightMerger.completeMerge(); if (readyChunkInfo != null) { Chunk chunk = readyChunkInfo.getChunk(); chunk.lock(); try { chunk.markReady(); updateAdjacentChunksReadyFieldOf(chunk); updateAdjacentChunksReadyFieldOfAdjChunks(chunk); if (!readyChunkInfo.isNewChunk()) { PerformanceMonitor.startActivity("Generating Block Entities"); generateBlockEntities(chunk); PerformanceMonitor.endActivity(); } if (readyChunkInfo.getChunkStore() != null) { readyChunkInfo.getChunkStore().restoreEntities(); } if (!readyChunkInfo.isNewChunk()) { PerformanceMonitor.startActivity("Sending OnAddedBlocks"); readyChunkInfo .getBlockPositionMapppings() .forEachEntry( new TShortObjectProcedure<TIntList>() { @Override public boolean execute(short id, TIntList positions) { if (positions.size() > 0) { blockManager .getBlock(id) .getEntity() .send(new OnAddedBlocks(positions, registry)); } return true; } }); PerformanceMonitor.endActivity(); } PerformanceMonitor.startActivity("Sending OnActivateBlocks"); readyChunkInfo .getBlockPositionMapppings() .forEachEntry( new TShortObjectProcedure<TIntList>() { @Override public boolean execute(short id, TIntList positions) { if (positions.size() > 0) { blockManager .getBlock(id) .getEntity() .send(new OnActivatedBlocks(positions, registry)); } return true; } }); PerformanceMonitor.endActivity(); if (!readyChunkInfo.isNewChunk()) { worldEntity.send(new OnChunkGenerated(readyChunkInfo.getPos())); } worldEntity.send(new OnChunkLoaded(readyChunkInfo.getPos())); for (ChunkRelevanceRegion region : regions.values()) { region.chunkReady(chunk); } } finally { chunk.unlock(); } } }
private void updateAdjacentChunksReadyFieldOf(Chunk chunk) { chunk.setAdjacentChunksReady(areAdjacentChunksReady(chunk)); }
public static void fireChunkRevived(Chunk chunk) { Preconditions.checkNotNull(chunk, "The parameter 'chunk' must not be null"); post(new ChunkMonitorEvent.Revived(chunk.getPosition())); }
public static void fireChunkDeflated(Chunk chunk, int oldSize, int newSize) { Preconditions.checkNotNull(chunk, "The parameter 'chunk' must not be null"); post(new ChunkMonitorEvent.Deflated(chunk.getPosition(), oldSize, newSize)); }
/** * Generate the local contents of a chunk. This should be purely deterministic from the chunk * contents, chunk position and world seed - should not depend on external state or other data. * * @param c */ public void generateChunk(Chunk c) { int hm_x = (((c.getChunkWorldPosX() / Chunk.SIZE_X) % 512) + 512) % 512; int hm_z = (((c.getChunkWorldPosZ() / Chunk.SIZE_Z) % 512) + 512) % 512; double scaleFactor = 0.05 * Chunk.SIZE_Y; double p00 = heightmap[hm_x][hm_z] * scaleFactor; double p10 = heightmap[(hm_x - 1 + 512) % 512][(hm_z) % 512] * scaleFactor; double p11 = heightmap[(hm_x - 1 + 512) % 512][(hm_z + 1 + 512) % 512] * scaleFactor; double p01 = heightmap[(hm_x) % 512][(hm_z + 1 + 512) % 512] * scaleFactor; for (int x = 0; x < Chunk.SIZE_X; x++) { for (int z = 0; z < Chunk.SIZE_Z; z++) { WorldBiomeProvider.Biome type = biomeProvider.getBiomeAt(c.getBlockWorldPosX(x), c.getBlockWorldPosZ(z)); // calculate avg height double interpolatedHeight = lerp( x / (double) Chunk.SIZE_X, lerp(z / (double) Chunk.SIZE_Z, p10, p11), lerp(z / (double) Chunk.SIZE_Z, p00, p01)); // Scale the height to fit one chunk (suppose we have max height 20 on the Heigthmap // ToDo: Change this formula in later implementation of vertical chunks double threshold = Math.floor(interpolatedHeight); for (int y = Chunk.SIZE_Y - 1; y >= 0; y--) { if (y == 0) { // The very deepest layer of the world is an // indestructible mantle c.setBlock(x, y, z, mantle); break; } else if (y < threshold) { c.setBlock(x, y, z, stone); } else if (y == threshold) { if (y < Chunk.SIZE_Y * 0.05 + 1) { c.setBlock(x, y, z, sand); } else if (y < Chunk.SIZE_Y * 0.05 * 12) { c.setBlock(x, y, z, grass); } else { c.setBlock(x, y, z, snow); } } else { if (y <= Chunk.SIZE_Y / 20) { // Ocean c.setBlock(x, y, z, water); c.setLiquid(x, y, z, new LiquidData(LiquidType.WATER, Chunk.MAX_LIQUID_DEPTH)); } else { c.setBlock(x, y, z, air); } } } } } }