@Override public Block getBlock(int x, int y, int z) { Vector3i pos = new Vector3i(x, y, z); Block block = blocks.get(pos); if (block != null) { return block; } // TODO block & biome manager Vector3i chunkPos = ChunkMath.calcChunkPos(pos); Chunk chunk = chunks.get(chunkPos); if (chunk == null && worldGenerator != null) { chunk = new ChunkImpl(chunkPos, blockManager, biomeManager, extraDataManager); worldGenerator.createChunk(chunk, entityBuffer); chunks.put(chunkPos, chunk); } if (chunk != null) { return chunk.getBlock(ChunkMath.calcBlockPos(pos.x, pos.y, pos.z)); } return null; }
private Chunk createChunk(int x, int y, int z) { return new ChunkImpl(new Vector3i(x, y, z), blockManager, biomeManager, extraDataManager); } }
public static Chunk decode(EntityData.ChunkStore message, BlockManager blockManager, BiomeManager biomeManager, ExtraBlockDataManager extraDataManager) { Preconditions.checkNotNull(message, "The parameter 'message' must not be null"); if (!message.hasX() || !message.hasY() || !message.hasZ()) { throw new IllegalArgumentException("Ill-formed protobuf message. Missing chunk position."); } Vector3i pos = new Vector3i(message.getX(), message.getY(), message.getZ()); if (!message.hasBlockData()) { throw new IllegalArgumentException("Ill-formed protobuf message. Missing block data."); } final TeraArray blockData = runLengthDecode(message.getBlockData()); final TeraArray biomeData = runLengthDecode(message.getBiomeData()); final TeraArray[] extraData = extraDataManager.makeDataArrays(ChunkConstants.SIZE_X, ChunkConstants.SIZE_Y, ChunkConstants.SIZE_Z); for (int i = 0; i < extraData.length; i++) { runLengthDecode(message.getExtraData(i), extraData[i]); } return new ChunkImpl(pos, blockData, biomeData, extraData, blockManager, biomeManager); }
@Override public void run() { ChunkStore chunkStore = storageManager.loadChunkStore(getPosition()); Chunk chunk; EntityBufferImpl buffer = new EntityBufferImpl(); if (chunkStore == null) { chunk = new ChunkImpl(getPosition(), blockManager, biomeManager, extraDataManager); generator.createChunk(chunk, buffer); } else { chunk = chunkStore.getChunk(); } InternalLightProcessor.generateInternalLighting(chunk); chunk.deflate(); TShortObjectMap<TIntList> mappings = createBatchBlockEventMappings(chunk); readyChunks.offer(new ReadyChunkInfo(chunk, mappings, chunkStore, buffer.getAll())); } });
@Test public void testUnblockedSunlightRegenPropagation() { Chunk chunk = new ChunkImpl(0, 0, 0, blockManager, biomeManager, extraDataManager); InternalLightProcessor.generateInternalLighting(chunk); for (Vector3i pos : Region3i.createFromMinAndSize(Vector3i.zero(), new Vector3i(ChunkConstants.SIZE_X, ChunkConstants.SIZE_Y, ChunkConstants.SIZE_Z))) { byte expectedRegen = (byte) Math.min(ChunkConstants.SIZE_Y - pos.y - 1, ChunkConstants.MAX_SUNLIGHT_REGEN); assertEquals(expectedRegen, chunk.getSunlightRegen(pos)); } }
@Test public void testUnblockedSunlightPropagation() { Chunk chunk = new ChunkImpl(0, 0, 0, blockManager, biomeManager, extraDataManager); InternalLightProcessor.generateInternalLighting(chunk); for (Vector3i pos : Region3i.createFromMinAndSize(new Vector3i(0, 0, 0), new Vector3i(ChunkConstants.SIZE_X, 15, ChunkConstants.SIZE_Z))) { assertEquals("Incorrect lighting at " + pos, 15 - pos.y, chunk.getSunlight(pos)); } }
@Test public void testStoreAndRestoreChunkStore() { Chunk chunk = new ChunkImpl(CHUNK_POS, blockManager, biomeManager, extraDataManager); chunk.setBlock(0, 0, 0, testBlock); chunk.markReady(); ChunkProvider chunkProvider = mock(ChunkProvider.class); when(chunkProvider.getAllChunks()).thenReturn(Arrays.asList(chunk)); CoreRegistry.put(ChunkProvider.class, chunkProvider); esm.waitForCompletionOfPreviousSaveAndStartSaving(); esm.finishSavingAndShutdown(); ChunkStore restored = esm.loadChunkStore(CHUNK_POS); assertNotNull(restored); assertEquals(CHUNK_POS, restored.getChunkPosition()); assertNotNull(restored.getChunk()); assertEquals(testBlock, restored.getChunk().getBlock(0, 0, 0)); }
@Test public void testBlockedAtTopSunlightRegenPropagationResets() { Chunk chunk = new ChunkImpl(0, 0, 0, blockManager, biomeManager, extraDataManager); for (Vector3i pos : Region3i.createFromMinAndSize(new Vector3i(0, 63, 0), new Vector3i(ChunkConstants.SIZE_X, 1, ChunkConstants.SIZE_Z))) { chunk.setBlock(pos, solidBlock); } InternalLightProcessor.generateInternalLighting(chunk); for (Vector3i pos : Region3i.createFromMinAndSize(Vector3i.zero(), new Vector3i(ChunkConstants.SIZE_X, ChunkConstants.SIZE_Y - 1, ChunkConstants.SIZE_Z))) { byte expectedRegen = (byte) Math.min(ChunkConstants.SIZE_Y - pos.y - 2, ChunkConstants.MAX_SUNLIGHT_REGEN); assertEquals(expectedRegen, chunk.getSunlightRegen(pos)); } }
@Test public void testUnblockedSunlightPropagationAfterHittingMaxRegen() { Chunk chunk = new ChunkImpl(0, 0, 0, blockManager, biomeManager, extraDataManager); InternalLightProcessor.generateInternalLighting(chunk); for (Vector3i pos : Region3i.createFromMinAndSize(new Vector3i(0, 15, 0), new Vector3i(ChunkConstants.SIZE_X, ChunkConstants.SIZE_Y - 15, ChunkConstants.SIZE_Z))) { assertEquals(0, chunk.getSunlight(pos)); } for (Vector3i pos : Region3i.createFromMinAndSize(Vector3i.zero(), new Vector3i(ChunkConstants.SIZE_X, ChunkConstants.SIZE_Y - ChunkConstants.MAX_SUNLIGHT_REGEN, ChunkConstants.SIZE_Z))) { byte expectedSunlight = (byte) Math.min(ChunkConstants.SIZE_Y - ChunkConstants.SUNLIGHT_REGEN_THRESHOLD - pos.y - 1, ChunkConstants.MAX_SUNLIGHT); assertEquals("Incorrect lighting at " + pos, expectedSunlight, chunk.getSunlight(pos)); } }
@Test public void testChunkSurvivesStorageSaveAndRestore() throws Exception { Chunk chunk = new ChunkImpl(CHUNK_POS, blockManager, biomeManager, extraDataManager); chunk.setBlock(0, 0, 0, testBlock); chunk.setBlock(0, 4, 2, testBlock2); chunk.markReady(); ChunkProvider chunkProvider = mock(ChunkProvider.class); when(chunkProvider.getAllChunks()).thenReturn(Arrays.asList(chunk)); when(chunkProvider.getChunk(Matchers.any(Vector3i.class))).thenReturn(chunk); CoreRegistry.put(ChunkProvider.class, chunkProvider); boolean storeChunkInZips = true; esm.setStoreChunksInZips(storeChunkInZips); esm.waitForCompletionOfPreviousSaveAndStartSaving(); esm.finishSavingAndShutdown(); EntitySystemSetupUtil.addReflectionBasedLibraries(context); EntitySystemSetupUtil.addEntityManagementRelatedClasses(context); EngineEntityManager newEntityManager = context.get(EngineEntityManager.class); StorageManager newSM = new ReadWriteStorageManager(savePath, moduleEnvironment, newEntityManager, blockManager, biomeManager, extraDataManager, storeChunkInZips, recordAndReplaySerializer, recordAndReplayUtils, recordAndReplayCurrentStatus); newSM.loadGlobalStore(); ChunkStore restored = newSM.loadChunkStore(CHUNK_POS); assertNotNull(restored); assertEquals(CHUNK_POS, restored.getChunkPosition()); assertNotNull(restored.getChunk()); assertEquals(testBlock, restored.getChunk().getBlock(0, 0, 0)); assertEquals(testBlock2, restored.getChunk().getBlock(0, 4, 2)); }
@Test public void testBetweenChunksSimpleSunlightRegenOnly() { Chunk topChunk = new ChunkImpl(new Vector3i(0, 1, 0), blockManager, biomeManager, extraDataManager); Chunk bottomChunk = new ChunkImpl(new Vector3i(0, 0, 0), blockManager, biomeManager, extraDataManager); provider.addChunk(topChunk); provider.addChunk(bottomChunk); for (Vector3i pos : Region3i.createFromMinAndSize(new Vector3i(0, 0, 0), new Vector3i(ChunkConstants.SIZE_X, 1, ChunkConstants.SIZE_Z))) { topChunk.setSunlight(pos, ChunkConstants.MAX_SUNLIGHT); topChunk.setSunlightRegen(pos, ChunkConstants.MAX_SUNLIGHT_REGEN); } InternalLightProcessor.generateInternalLighting(bottomChunk); propagator.propagateBetween(topChunk, bottomChunk, Side.BOTTOM, true); propagator.process(); for (Vector3i pos : ChunkConstants.CHUNK_REGION) { assertEquals("Incorrect at position " + pos, ChunkConstants.MAX_SUNLIGHT_REGEN, bottomChunk.getSunlightRegen(pos)); } }
@Test public void testBlockedSunlightPropagation() { Chunk chunk = new ChunkImpl(0, 0, 0, blockManager, biomeManager, extraDataManager); for (Vector3i pos : Region3i.createFromMinAndSize(new Vector3i(0, 4, 0), new Vector3i(ChunkConstants.SIZE_X, 1, ChunkConstants.SIZE_Z))) { chunk.setBlock(pos, solidBlock); } InternalLightProcessor.generateInternalLighting(chunk); for (Vector3i pos : Region3i.createFromMinAndSize(new Vector3i(0, 0, 0), new Vector3i(ChunkConstants.SIZE_X, 5, ChunkConstants.SIZE_Z))) { assertEquals("Incorrect lighting at " + pos, 0, chunk.getSunlight(pos)); } }
@Test public void testBetweenChunksSimple() { Chunk topChunk = new ChunkImpl(new Vector3i(0, 1, 0), blockManager, biomeManager, extraDataManager); Chunk bottomChunk = new ChunkImpl(new Vector3i(0, 0, 0), blockManager, biomeManager, extraDataManager); provider.addChunk(topChunk); provider.addChunk(bottomChunk); for (Vector3i pos : Region3i.createFromMinAndSize(new Vector3i(0, 0, 0), new Vector3i(ChunkConstants.SIZE_X, 1, ChunkConstants.SIZE_Z))) { topChunk.setSunlight(pos, ChunkConstants.MAX_SUNLIGHT); topChunk.setSunlightRegen(pos, ChunkConstants.MAX_SUNLIGHT_REGEN); } InternalLightProcessor.generateInternalLighting(bottomChunk); propagator.propagateBetween(topChunk, bottomChunk, Side.BOTTOM, true); propagator.process(); sunlightPropagator.process(); for (Vector3i pos : ChunkConstants.CHUNK_REGION) { assertEquals("Incorrect at position " + pos, ChunkConstants.MAX_SUNLIGHT, bottomChunk.getSunlight(pos)); assertEquals("Incorrect at position " + pos, ChunkConstants.MAX_SUNLIGHT_REGEN, bottomChunk.getSunlightRegen(pos)); } }
@Test public void testBlockedSunlightRegenPropagationResets() { Chunk chunk = new ChunkImpl(0, 0, 0, blockManager, biomeManager, extraDataManager); for (Vector3i pos : Region3i.createFromMinAndSize(new Vector3i(0, 60, 0), new Vector3i(ChunkConstants.SIZE_X, 1, ChunkConstants.SIZE_Z))) { chunk.setBlock(pos, solidBlock); } InternalLightProcessor.generateInternalLighting(chunk); for (Vector3i pos : Region3i.createFromMinAndSize(new Vector3i(0, 61, 0), new Vector3i(ChunkConstants.SIZE_X, 3, ChunkConstants.SIZE_Z))) { byte expectedRegen = (byte) Math.min(ChunkConstants.SIZE_Y - pos.y - 1, ChunkConstants.MAX_SUNLIGHT_REGEN); assertEquals(expectedRegen, chunk.getSunlightRegen(pos)); } for (Vector3i pos : Region3i.createFromMinAndSize(new Vector3i(0, 60, 0), new Vector3i(ChunkConstants.SIZE_X, 1, ChunkConstants.SIZE_Z))) { assertEquals(0, chunk.getSunlightRegen(pos)); } for (Vector3i pos : Region3i.createFromMinAndSize(new Vector3i(0, 0, 0), new Vector3i(ChunkConstants.SIZE_X, 59, ChunkConstants.SIZE_Z))) { byte expectedRegen = (byte) Math.min(60 - pos.y - 1, ChunkConstants.MAX_SUNLIGHT_REGEN); assertEquals(expectedRegen, chunk.getSunlightRegen(pos)); } }
@Test public void testEntitySurvivesStorageInChunkStore() throws Exception { Chunk chunk = new ChunkImpl(CHUNK_POS, blockManager, biomeManager, extraDataManager); chunk.setBlock(0, 0, 0, testBlock); chunk.markReady();
@Before public void setup() throws Exception { super.setup(); AssetManager assetManager = CoreRegistry.get(AssetManager.class); blockManager = new BlockManagerImpl(new NullWorldAtlas(), assetManager); CoreRegistry.put(BlockManager.class, blockManager); BiomeManager biomeManager = Mockito.mock(BiomeManager.class); ExtraBlockDataManager extraDataManager = new ExtraBlockDataManager(); chunk = new ChunkImpl(new Vector3i(0, 0, 0), blockManager, biomeManager, extraDataManager); BlockFamilyDefinitionData solidData = new BlockFamilyDefinitionData(); solidData.getBaseSection().setDisplayName("Stone"); solidData.getBaseSection().setShape(assetManager.getAsset("engine:cube", BlockShape.class).get()); solidData.getBaseSection().setTranslucent(false); solidData.setBlockFamily(SymmetricFamily.class); assetManager.loadAsset(new ResourceUrn("engine:stone"), solidData, BlockFamilyDefinition.class); solid = blockManager.getBlock(new BlockUri(new ResourceUrn("engine:stone"))); }
@Test public void testBetweenChunksWithOverhang() { Chunk topChunk = new ChunkImpl(new Vector3i(0, 1, 0), blockManager, biomeManager, extraDataManager); Chunk bottomChunk = new ChunkImpl(new Vector3i(0, 0, 0), blockManager, biomeManager, extraDataManager); provider.addChunk(topChunk); provider.addChunk(bottomChunk); for (Vector3i pos : Region3i.createFromMinAndSize(new Vector3i(0, 0, 0), new Vector3i(ChunkConstants.SIZE_X, 1, ChunkConstants.SIZE_Z))) { topChunk.setSunlight(pos, ChunkConstants.MAX_SUNLIGHT); topChunk.setSunlightRegen(pos, ChunkConstants.MAX_SUNLIGHT_REGEN); } for (Vector3i pos : Region3i.createFromMinMax(new Vector3i(16, 48, 0), new Vector3i(31, 48, 31))) { bottomChunk.setBlock(pos, solid); } InternalLightProcessor.generateInternalLighting(bottomChunk); propagator.propagateBetween(topChunk, bottomChunk, Side.BOTTOM, false); propagator.process(); sunlightPropagator.process(); for (int z = 0; z < ChunkConstants.SIZE_Z; ++z) { assertEquals(14, bottomChunk.getSunlight(16, 47, z)); } for (int z = 0; z < ChunkConstants.SIZE_Z; ++z) { assertEquals(13, bottomChunk.getSunlight(17, 47, z)); } }
@Test public void testLightPropagation() { Chunk chunk = new ChunkImpl(0, 0, 0, blockManager, biomeManager, extraDataManager); chunk.setBlock(16, 32, 16, fullLight); InternalLightProcessor.generateInternalLighting(chunk); assertEquals(fullLight.getLuminance(), chunk.getLight(16, 32, 16)); assertEquals(fullLight.getLuminance() - 1, chunk.getLight(new Vector3i(16, 33, 16))); for (int i = 1; i < fullLight.getLuminance(); ++i) { for (Vector3i pos : Diamond3iIterator.iterateAtDistance(new Vector3i(16, 32, 16), i)) { assertEquals(fullLight.getLuminance() - i, chunk.getLight(pos)); } } }
@Test public void testPropagateSunlightAppearingMidChunk() { Chunk topChunk = new ChunkImpl(new Vector3i(0, 1, 0), blockManager, biomeManager, extraDataManager); Chunk bottomChunk = new ChunkImpl(new Vector3i(0, 0, 0), blockManager, biomeManager, extraDataManager); provider.addChunk(topChunk); provider.addChunk(bottomChunk); for (Vector3i pos : Region3i.createFromMinAndSize(new Vector3i(0, 0, 0), new Vector3i(ChunkConstants.SIZE_X, 1, ChunkConstants.SIZE_Z))) { topChunk.setSunlight(pos, (byte) 0); topChunk.setSunlightRegen(pos, (byte) 0); } for (Vector3i pos : Region3i.createFromMinAndSize(new Vector3i(8, 0, 8), new Vector3i(ChunkConstants.SIZE_X - 16, 1, ChunkConstants.SIZE_Z - 16))) { topChunk.setSunlight(pos, (byte) 0); topChunk.setSunlightRegen(pos, (byte) 32); } InternalLightProcessor.generateInternalLighting(bottomChunk); propagator.propagateBetween(topChunk, bottomChunk, Side.BOTTOM, false); propagator.process(); sunlightPropagator.process(); for (int i = 0; i < 15; ++i) { assertEquals("Incorrect value at " + (33 + i), 14 - i, bottomChunk.getSunlight(7, 33 + i, 16)); } for (int i = 2; i < 33; ++i) { assertEquals("Incorrect value at " + i, 14, bottomChunk.getSunlight(7, i, 16)); } }
@Test public void testHorizontalSunlightPropagation() { Chunk chunk = new ChunkImpl(0, 0, 0, blockManager, biomeManager, extraDataManager); for (Vector3i pos : Region3i.createFromMinAndSize(new Vector3i(0, 4, 0), new Vector3i(ChunkConstants.SIZE_X, 1, ChunkConstants.SIZE_Z))) { chunk.setBlock(pos, solidBlock); } chunk.setBlock(new Vector3i(16, 4, 16), airBlock); InternalLightProcessor.generateInternalLighting(chunk); assertEquals(12, chunk.getSunlight(16, 3, 16)); assertEquals(11, chunk.getSunlight(15, 3, 16)); assertEquals(11, chunk.getSunlight(17, 3, 16)); assertEquals(11, chunk.getSunlight(16, 3, 15)); assertEquals(11, chunk.getSunlight(16, 3, 17)); assertEquals(12, chunk.getSunlight(15, 2, 16)); assertEquals(12, chunk.getSunlight(17, 2, 16)); assertEquals(12, chunk.getSunlight(16, 2, 15)); assertEquals(12, chunk.getSunlight(16, 2, 17)); }