#include "Chunk.h" #include #include #include "Constants.h" #include "Game.h" #include "NoBlock.h" Chunk::Chunk(Framework::Punkt location, int dimensionId) : ReferenceCounter(), dimensionId(dimensionId), location(location), added(0), currentlyLoading(1) { blocks = new Block*[CHUNK_SIZE * CHUNK_SIZE * WORLD_HEIGHT]; blockIds = new unsigned short[CHUNK_SIZE * CHUNK_SIZE * WORLD_HEIGHT]; lightData = new unsigned char[CHUNK_SIZE * CHUNK_SIZE * WORLD_HEIGHT * 6]; memset(blocks, 0, CHUNK_SIZE * CHUNK_SIZE * WORLD_HEIGHT * sizeof(Block*)); memset(blockIds, 0, CHUNK_SIZE * CHUNK_SIZE * WORLD_HEIGHT * sizeof(unsigned short)); memset(lightData, 0, CHUNK_SIZE * CHUNK_SIZE * WORLD_HEIGHT * 6); zNeighbours[0] = 0; zNeighbours[1] = 0; zNeighbours[2] = 0; zNeighbours[3] = 0; } Chunk::Chunk(Framework::Punkt location, int dimensionId, Framework::StreamReader* zReader) : Chunk(location, dimensionId) { load(zReader); } Chunk::~Chunk() { for (int i = 0; i < CHUNK_SIZE * CHUNK_SIZE * WORLD_HEIGHT; i++) { if (blocks[i]) blocks[i]->release(); } delete[] blocks; delete[] blockIds; delete[] lightData; } void Chunk::lock() { cs.lock(); } void Chunk::unlock() { cs.unlock(); } void Chunk::tick(TickQueue* zQueue) { for (Block* source : tickSources) zQueue->addToQueue(source); } void Chunk::postTick() {} void Chunk::addLightSource(int index) { for (int i : lightSources) { if (i == index) return; } lightSources.add(index); } void Chunk::removeLightSource(int index) { for (auto i = lightSources.begin(); i; i++) { if (i.val() == index) { i.remove(); return; } } } void Chunk::sendLightToClient(Framework::StreamWriter* zWriter) { for (int z = 0; z < WORLD_HEIGHT; z++) { for (int x = -1; x <= CHUNK_SIZE; x++) { for (int y = -1; y <= CHUNK_SIZE; y++) { if ((x < 0 || x == CHUNK_SIZE) && (y < 0 || y > CHUNK_SIZE)) { continue; } bool needSend = 0; for (int i = 0; i < 6; i++) { Vec3 pos = Vec3(x, y, z) + getDirection(getDirectionFromIndex(i)); if (pos.z >= 0 && pos.z < WORLD_HEIGHT) { if (pos.x >= 0 && pos.x < CHUNK_SIZE && pos.y >= 0 && pos.y < CHUNK_SIZE) { int bi = (pos.x * CHUNK_SIZE + pos.y) * WORLD_HEIGHT + pos.z; int type = blockIds[bi]; needSend |= type != BlockTypeEnum::NO_BLOCK && type != BlockTypeEnum::AIR; } else { if (x >= 0 && x < CHUNK_SIZE && y >= 0 && y < CHUNK_SIZE) { if (i < 4 && zNeighbours[i]) { Vec3 offset = getDirection(getDirectionFromIndex(i)) * 16; int bi = ((pos.x - offset.x) * CHUNK_SIZE + (pos.y - offset.y)) * WORLD_HEIGHT + (pos.z - offset.z); int type = zNeighbours[i]->blockIds[bi]; needSend |= type != BlockTypeEnum::NO_BLOCK && type != BlockTypeEnum::AIR; } } } if (needSend) break; } } if (needSend) { if (x >= 0 && x < CHUNK_SIZE && y >= 0 && y < CHUNK_SIZE) { int index = (x * CHUNK_SIZE + y) * WORLD_HEIGHT + z; zWriter->schreibe((char*)&index, 4); zWriter->schreibe((char*)(lightData + index * 6), 6); } else { int dir; int index = 0; if (x == -1) { dir = getDirectionIndex(WEST); index = ((CHUNK_SIZE - 1) * CHUNK_SIZE + y) * WORLD_HEIGHT + z; } else if (y == -1) { dir = getDirectionIndex(NORTH); index = (x * CHUNK_SIZE + CHUNK_SIZE - 1) * WORLD_HEIGHT + z; } else if (x == CHUNK_SIZE) { dir = getDirectionIndex(EAST); index = y * WORLD_HEIGHT + z; } else if (y == CHUNK_SIZE) { dir = getDirectionIndex(SOUTH); index = (x * CHUNK_SIZE) * WORLD_HEIGHT + z; } if (zNeighbours[dir]) { int i = -1; zWriter->schreibe((char*)&i, 4); zWriter->schreibe((char*)&x, 4); zWriter->schreibe((char*)&y, 4); zWriter->schreibe((char*)&z, 4); zWriter->schreibe( (char*)(zNeighbours[dir]->lightData + index * 6), 6); } } } } } } int end = -2; zWriter->schreibe((char*)&end, 4); } void Chunk::broadcastLightData(int index, bool foreground) { int x = (index / WORLD_HEIGHT) / CHUNK_SIZE; int y = (index / WORLD_HEIGHT) % CHUNK_SIZE; int z = index % WORLD_HEIGHT; NetworkMessage* msg = new NetworkMessage(); msg->addressDimension(Game::INSTANCE->zDimension(dimensionId)); char* message = new char[19]; message[0] = 5; *(int*)(message + 1) = x + this->location.x - CHUNK_SIZE / 2; *(int*)(message + 5) = y + this->location.y - CHUNK_SIZE / 2; *(int*)(message + 9) = z; memcpy(message + 13, lightData + index * 6, 6); msg->setMessage(message, 19); if (!foreground) msg->setUseBackground(); notifyObservers(msg); } Framework::Either Chunk::zBlockNeighbor( Framework::Vec3 location) { if (location.x >= 0 && location.x < CHUNK_SIZE && location.y >= 0 && location.y < CHUNK_SIZE && location.z >= 0 && location.z < WORLD_HEIGHT) { int index = (location.x * CHUNK_SIZE + location.y) * WORLD_HEIGHT + location.z; if (blocks[index]) return blocks[index]; else return (int)blockIds[index]; } if (added && location.z >= 0 && location.z < WORLD_HEIGHT) return Game::INSTANCE->zBlockAt( {location.x + this->location.x - CHUNK_SIZE / 2, location.y + this->location.y - CHUNK_SIZE / 2, location.z}, dimensionId); return 0; } void Chunk::notifyObservers(NetworkMessage* msg) { Array remove; int index = 0; for (int id : observers) { Entity* zE = Game::INSTANCE->zEntity(id); if (!zE) remove.add(index, 0); else Game::INSTANCE->sendMessage( dynamic_cast(msg->getThis()), zE); index++; } for (int i : remove) observers.remove(i); msg->release(); } void Chunk::addObserver(Entity* zEntity, DoLaterHandler& laterHandler) { for (int id : observers) { if (id == zEntity->getId()) return; } int id = zEntity->getId(); observers.add(id); laterHandler.addTodo([this, id]() { InMemoryBuffer buffer; buffer.schreibe("\4", 1); buffer.schreibe((char*)&location.x, 4); buffer.schreibe((char*)&location.y, 4); sendToClient(&buffer); sendLightToClient(&buffer); NetworkMessage* msg = new NetworkMessage(); msg->addressDimension(Game::INSTANCE->zDimension(dimensionId)); std::cout << "chunk size: " << buffer.getSize() << "b\n"; char* message = new char[buffer.getSize()]; buffer.lese(message, (int)buffer.getSize()); msg->setMessage(message, (int)buffer.getSize()); msg->setUseBackground(); Entity* e = Game::INSTANCE->zEntity(id); if (e) { Game::INSTANCE->sendMessage(msg, e); } else msg->release(); }); } void Chunk::removeObserver(Entity* zEntity) { int index = 0; for (int id : observers) { if (id == zEntity->getId()) { observers.remove(index); return; } index++; } } void Chunk::api(Framework::StreamReader* zRequest, Entity* zSource, DoLaterHandler& laterHandler) { char type; zRequest->lese(&type, 1); switch (type) { case 0: // register observer addObserver(zSource, laterHandler); break; case 1: // unsubscribe removeObserver(zSource); break; } } void Chunk::initializeLightning() { unsigned char dayLight[6] = {255, 255, 255, 0, 0, 0}; unsigned char noLight[6] = {0, 0, 0, 0, 0, 0}; while (true) { bool changes = false; for (int z = WORLD_HEIGHT - 1; z >= 0; z--) { for (int x = 0; x < CHUNK_SIZE; x++) { for (int y = 0; y < CHUNK_SIZE; y++) { int index = (x * CHUNK_SIZE + y) * WORLD_HEIGHT + z; unsigned char* light = getLightData(Vec3(x, y, z)); unsigned char newLight[6] = {0, 0, 0, 0, 0, 0}; for (int i = 0; i < 6; i++) { unsigned char* neighborLeight; Vec3 neighborPos = Vec3(x, y, z) + getDirection(getDirectionFromIndex(i)); if (neighborPos.z < 0 || neighborPos.x < 0 || neighborPos.y < 0 || neighborPos.x >= CHUNK_SIZE || neighborPos.y >= CHUNK_SIZE) { neighborLeight = noLight; } else if (neighborPos.z >= WORLD_HEIGHT) { neighborLeight = dayLight; } else { neighborLeight = getLightData(neighborPos); } for (int j = 0; j < 3; j++) newLight[j] = (unsigned char)MAX(newLight[j], i == getDirectionIndex(TOP) ? neighborLeight[j] : (unsigned char)((float)neighborLeight[j] * 0.8f)); for (int j = 3; j < 6; j++) newLight[j] = (unsigned char)MAX(newLight[j], (unsigned char)((float)neighborLeight[j] * 0.85f)); } const Block* current = blocks[index] ? blocks[index] : StaticRegistry::INSTANCE .zElement(blockIds[index]) ->zDefault(); // add own light emission for (int j = 3; j < 6; j++) newLight[j] = (unsigned char)MAX(newLight[j], current->getLightEmisionColor()[j - 3]); current->filterPassingLight(newLight); current->filterPassingLight(newLight + 3); for (int i = 0; i < 6; i++) { if (newLight[i] != light[i]) { changes = 1; memcpy(light, newLight, 6); break; } } } } } if (!changes) break; } } Framework::Either Chunk::zBlockAt( Framework::Vec3 location) const { int index = Chunk::index(location); assert(index < CHUNK_SIZE * CHUNK_SIZE * WORLD_HEIGHT); if (blocks[index]) return blocks[index]; else return (int)blockIds[index]; } const Block* Chunk::zBlockConst(Framework::Vec3 location) const { auto b = zBlockAt(location); if (b.isA()) return b; return StaticRegistry::INSTANCE.zElement(b.getB())->zDefault(); } void Chunk::instantiateBlock(Framework::Vec3 location) { auto b = zBlockAt(location); if (b.isA()) return; if (!b.getB()) generateBlock(location); b = zBlockAt(location); if (b.isB()) putBlockAt(location, StaticRegistry::INSTANCE.zElement(b.getB()) ->createBlockAt( {location.x + this->location.x - CHUNK_SIZE / 2, location.y + this->location.y - CHUNK_SIZE / 2, location.z}, dimensionId, 0)); } void Chunk::generateBlock(Framework::Vec3 location) { int index = Chunk::index(location); if (blockIds[index]) return; auto generated = Game::INSTANCE->zGenerator()->generateSingleBlock( {location.x + this->location.x - CHUNK_SIZE / 2, location.y + this->location.y - CHUNK_SIZE / 2, location.z}, dimensionId); if (generated.isA()) putBlockAt(location, generated); else putBlockTypeAt(location, generated); } void Chunk::putBlockAt(Framework::Vec3 location, Block* block) { int index = Chunk::index(location); assert(index < CHUNK_SIZE * CHUNK_SIZE * WORLD_HEIGHT && index >= 0); Block* old = blocks[index]; if (old && old->isTickSource()) { // remove from tick sorces for (Framework::Iterator obj = tickSources.begin(); obj; obj++) { if (obj.val() == old) { obj.remove(); break; } } } bool change = 0; bool wasLightSource = old ? old->zBlockType()->isLightSource() : StaticRegistry::INSTANCE.zElement(blockIds[index]) ->isLightSource(); bool isLightSource = 0; if (block) { change = blockIds[index] != (unsigned short)block->zBlockType()->getId(); blockIds[index] = (unsigned short)block->zBlockType()->getId(); isLightSource = block->zBlockType()->isLightSource(); } else { if (old != 0) { blockIds[index] = BlockTypeEnum::NO_BLOCK; } change = old != 0; } blocks[index] = block; for (int i = 0; i < 6; i++) { Direction d = getDirectionFromIndex(i); Either neighbor = zBlockNeighbor(location + getDirection(d)); if (neighbor.isA()) { if (block) { ((Block*)neighbor) ->setNeighbour(getOppositeDirection(d), block); } else { ((Block*)neighbor) ->setNeighbour(getOppositeDirection(d), blockIds[index]); } } if (block) block->setNeighbour(d, neighbor); } if (old) old->release(); if (block && block->isTickSource()) { // add to tick sources tickSources.add(block); } if (change) { if (isLightSource != wasLightSource) { if (isLightSource) addLightSource(index); else removeLightSource(index); } if (added) { sendBlockInfo(location); if (block) { Game::INSTANCE->updateLightningWithoutWait(getDimensionId(), Vec3(location.x + this->location.x - CHUNK_SIZE / 2, location.y + this->location.y - CHUNK_SIZE / 2, location.z)); } } } if (added) { Game::INSTANCE->zDimension(dimensionId) ->updateMap(location.x + this->location.x - CHUNK_SIZE / 2, location.y + this->location.y - CHUNK_SIZE / 2, location.z); } } void Chunk::putBlockTypeAt(Framework::Vec3 location, int type) { int index = Chunk::index(location); assert(index < CHUNK_SIZE * CHUNK_SIZE * WORLD_HEIGHT); bool wasLightSource = StaticRegistry::INSTANCE.zElement(blockIds[index]) ->isLightSource(); bool isLightSource = StaticRegistry::INSTANCE.zElement(type)->isLightSource(); if (blockIds[index] != (unsigned short)type) { blockIds[index] = (unsigned short)type; for (int i = 0; i < 6; i++) { Direction d = getDirectionFromIndex(i); Either neighbor = zBlockNeighbor(location + getDirection(d)); if (neighbor.isA()) ((Block*)neighbor) ->setNeighbourType(getOppositeDirection(d), type); } if (isLightSource != wasLightSource) { if (isLightSource) addLightSource(index); else removeLightSource(index); } if (added) { sendBlockInfo(location); Game::INSTANCE->updateLightningWithoutWait(getDimensionId(), Vec3(location.x + this->location.x - CHUNK_SIZE / 2, location.y + this->location.y - CHUNK_SIZE / 2, location.z)); Game::INSTANCE->zDimension(dimensionId) ->updateMap(location.x + this->location.x - CHUNK_SIZE / 2, location.y + this->location.y - CHUNK_SIZE / 2, location.z); } } } void Chunk::sendBlockInfo(Framework::Vec3 location) { int index = Chunk::index(location); char* msg = new char[9]; msg[0] = 0; // set block *(int*)(msg + 1) = index; *(int*)(msg + 5) = blockIds[index]; NetworkMessage* message = new NetworkMessage(); message->addressChunck(this); message->setMessage(msg, 9); notifyObservers(message); for (int i = 0; i < 6; i++) { Direction d = getDirectionFromIndex(i); Framework::Vec3 loc = location + getDirection(d); if (loc.x >= 0 && loc.x < CHUNK_SIZE && loc.y >= 0 && loc.y < CHUNK_SIZE && loc.z >= 0 && loc.z < WORLD_HEIGHT) { broadcastLightData(Chunk::index(loc), true); } else if (loc.z >= 0 && loc.z < WORLD_HEIGHT && i < 4 && zNeighbours[i]) { NetworkMessage* msg = new NetworkMessage(); msg->addressDimension(Game::INSTANCE->zDimension(dimensionId)); char* message = new char[19]; message[0] = 5; *(int*)(message + 1) = loc.x + this->location.x - CHUNK_SIZE / 2; *(int*)(message + 5) = loc.y + this->location.y - CHUNK_SIZE / 2; *(int*)(message + 9) = loc.z; loc -= getDirection(d) * CHUNK_SIZE; memcpy(message + 13, zNeighbours[i]->getLightData(loc), 6); msg->setMessage(message, 19); notifyObservers(msg); } } } void Chunk::setNeighbor(Direction dir, Chunk* zChunk) { zNeighbours[getDirectionIndex(dir)] = zChunk; for (int i = 0; i < CHUNK_SIZE; i++) { for (int z = 0; z < WORLD_HEIGHT; z++) { int index = 0; int j = 0; if (dir == NORTH) { index = i * CHUNK_SIZE * WORLD_HEIGHT + z; j = (i * CHUNK_SIZE + CHUNK_SIZE - 1) * WORLD_HEIGHT + z; } else if (dir == EAST) { index = ((CHUNK_SIZE - 1) * CHUNK_SIZE + i) * WORLD_HEIGHT + z; j = i * WORLD_HEIGHT + z; } else if (dir == SOUTH) { index = (i * CHUNK_SIZE + CHUNK_SIZE - 1) * WORLD_HEIGHT + z; j = i * CHUNK_SIZE * WORLD_HEIGHT + z; } else if (dir == WEST) { index = i * WORLD_HEIGHT + z; j = ((CHUNK_SIZE - 1) * CHUNK_SIZE + i) * WORLD_HEIGHT + z; } if (blocks[index]) { if (zChunk && zChunk->blocks[j]) blocks[index]->setNeighbour(dir, zChunk->blocks[j]); else { blocks[index]->setNeighbour(dir, 0); blocks[index]->setNeighbourType( dir, zChunk ? zChunk->blockIds[j] : 0); } } if (zChunk) { if (!blocks[index]) { if (zChunk->blockIds[j] == BlockTypeEnum::AIR && !blockIds[index]) { generateBlock( Vec3((index / WORLD_HEIGHT) / CHUNK_SIZE, (index / WORLD_HEIGHT) % CHUNK_SIZE, index % WORLD_HEIGHT)); } } if (blockIds[index] != BlockTypeEnum::AIR && blockIds[index] != BlockTypeEnum::NO_BLOCK) { zChunk->broadcastLightData(j, true); } } } } } void Chunk::load(Framework::StreamReader* zReader) { for (int index = 0; index < WORLD_HEIGHT * CHUNK_SIZE * CHUNK_SIZE; index++) { unsigned short blockType; zReader->lese((char*)&blockType, 2); if (blockType) { Framework::Vec3 pos = Framework::Vec3((index / WORLD_HEIGHT) / CHUNK_SIZE, (index / WORLD_HEIGHT) % CHUNK_SIZE, index % WORLD_HEIGHT); bool d; zReader->lese((char*)&d, 1); if (d) { putBlockAt(pos, StaticRegistry::INSTANCE.zElement(blockType) ->loadBlock(Framework::Vec3( pos.x + location.x - CHUNK_SIZE / 2, pos.y + location.y - CHUNK_SIZE / 2, pos.z), zReader, dimensionId)); } else { putBlockTypeAt(pos, blockType); } } } initializeLightning(); } void Chunk::save(Framework::StreamWriter* zWriter) { for (int index = 0; index < WORLD_HEIGHT * CHUNK_SIZE * CHUNK_SIZE; index++) { unsigned short blockType = blocks[index] ? (unsigned short)blocks[index]->zBlockType()->getId() : blockIds[index]; zWriter->schreibe((char*)&blockType, 2); if (blockType) { if (blocks[index]) { bool d = 1; zWriter->schreibe((char*)&d, 1); StaticRegistry::INSTANCE.zElement(blockType) ->saveBlock(blocks[index], zWriter); } else { bool d = 0; zWriter->schreibe((char*)&d, 1); } } } } void Chunk::sendToClient(Framework::StreamWriter* zWriter) { for (int x = 0; x < CHUNK_SIZE; x++) { for (int y = 0; y < CHUNK_SIZE; y++) { for (int z = 0; z < WORLD_HEIGHT; z++) { int index = Chunk::index({x, y, z}); unsigned short blockType = blocks[index] ? (unsigned short)blocks[index]->zBlockType()->getId() : blockIds[index]; if (blockType) { bool visible = 0; if (!visible) { if (!blocks[index]) { if (CONST_BLOCK(0, blockIds[index])->isTransparent() || CONST_BLOCK(0, blockIds[index]) ->isPassable()) visible = 1; else { for (int d = 0; d < 6 && !visible; d++) { Vec3 pos = getDirection( (Directions)getDirectionFromIndex( d)) + Framework::Vec3(x, y, z); auto n = zBlockNeighbor(pos); if (n.isA() && (((Block*)n)->isPassable() || ((Block*)n)->isTransparent())) visible = 1; if (n.isB() && (CONST_BLOCK(0, n)->isTransparent() || CONST_BLOCK(0, n)->isPassable())) visible = 1; if (pos.x < 0 || pos.y < 0 || pos.z < 0 || pos.x >= CHUNK_SIZE || pos.y >= CHUNK_SIZE || pos.z >= WORLD_HEIGHT) visible = 1; } } } else visible = blocks[index]->isVisible(); } if (visible && (blocks[index] || blockType != BlockTypeEnum::AIR)) { zWriter->schreibe((char*)&blockType, 2); zWriter->schreibe((char*)&index, 4); } } } } } unsigned short end = 0; zWriter->schreibe((char*)&end, 2); } void Chunk::removeUnusedBlocks() { for (int i = 0; i < CHUNK_SIZE * CHUNK_SIZE * WORLD_HEIGHT; i++) { if (!blocks[i] && blockIds[i]) { int x = (i / WORLD_HEIGHT) / CHUNK_SIZE; int y = (i / WORLD_HEIGHT) % CHUNK_SIZE; int z = i % WORLD_HEIGHT; bool visible = 0; if (CONST_BLOCK(0, blockIds[i])->isTransparent() || CONST_BLOCK(0, blockIds[i])->isPassable()) visible = 1; else { for (int d = 0; d < 6 && !visible; d++) { auto n = zBlockNeighbor( getDirection((Directions)getDirectionFromIndex(d)) + Framework::Vec3(x, y, z)); if (n.isA() && (((Block*)n)->isPassable() || ((Block*)n)->isTransparent())) visible = 1; if (n.isB() && (CONST_BLOCK(0, n)->isTransparent() || CONST_BLOCK(0, n)->isPassable())) visible = 1; } } if (!visible) { putBlockAt({x, y, z}, 0); putBlockTypeAt({x, y, z}, 0); } } } int count = 0; for (int i = 0; i < CHUNK_SIZE * CHUNK_SIZE * WORLD_HEIGHT; i++) { if (blockIds[i] && blockIds[i] != BlockTypeEnum::AIR) count++; } std::cout << "chunk " << location.x << ", " << location.y << " was generated with " << count << " blocks.\n"; } int Chunk::getDimensionId() const { return dimensionId; } void Chunk::onLoaded() { for (int i = 0; i < CHUNK_SIZE * CHUNK_SIZE * WORLD_HEIGHT; i++) { if (blocks[i]) blocks[i]->onLoaded(); } currentlyLoading = 0; } void Chunk::onUnloaded() { for (int i = 0; i < CHUNK_SIZE * CHUNK_SIZE * WORLD_HEIGHT; i++) { if (blocks[i]) blocks[i]->onUnloaded(); } } Framework::Punkt Chunk::getCenter() const { return location; } Framework::Vec3 Chunk::getMin() const { return {location.x - CHUNK_SIZE / 2, location.y - CHUNK_SIZE / 2, 0}; } Framework::Vec3 Chunk::getMax() const { return { location.x + CHUNK_SIZE / 2, location.y + CHUNK_SIZE / 2, WORLD_HEIGHT}; } void Chunk::prepareRemove() { added = 0; for (int i = 0; i < 4; i++) { if (zNeighbours[i]) { zNeighbours[i]->setNeighbor( getOppositeDirection(getDirectionFromIndex(i)), 0); zNeighbours[i] = 0; } } } void Chunk::setAdded() { added = 1; } bool Chunk::hasObservers() const { return observers.getEintragAnzahl() > 0 || currentlyLoading; } unsigned char* Chunk::getLightData(Framework::Vec3 location) const { int index = Chunk::index(location) * 6; assert(index < CHUNK_SIZE * CHUNK_SIZE * WORLD_HEIGHT * 6); return lightData + index; } void Chunk::setLightData( Framework::Vec3 location, unsigned char* data, bool foreground) { int index = Chunk::index(location); memcpy(lightData + index * 6, data, 6); // check if neighbor is a visible block and send update to clients bool needSend = 0; for (int i = 0; i < 6; i++) { Vec3 pos = location + getDirection(getDirectionFromIndex(i)); if (pos.z >= 0 && pos.z < WORLD_HEIGHT) { if (pos.x >= 0 && pos.x < CHUNK_SIZE && pos.y >= 0 && pos.y < CHUNK_SIZE) { int bi = (pos.x * CHUNK_SIZE + pos.y) * WORLD_HEIGHT + pos.z; int type = blockIds[bi]; needSend |= type != BlockTypeEnum::NO_BLOCK && type != BlockTypeEnum::AIR; if (needSend) break; } else { needSend = 1; // TODO: check if the block is visible } } } if (needSend) { broadcastLightData(index, foreground); } }