#include "Chunk.h" #include #include #include #include "Constants.h" #include "Dimension.h" #include "Entity.h" #include "FluidBlock.h" #include "Game.h" #include "NoBlock.h" #include "WorldGenerator.h" Chunk::Chunk(Framework::Punkt location, int dimensionId) : ReferenceCounter(), dimensionId(dimensionId), location(location), added(0), worldUpdated(1), 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 : tickSourcesEachTick) zQueue->addToQueue(source); if (worldUpdated) { worldUpdated = 0; for (Block* source : tickSourcesAfterUpdate) { if (source->needsTick()) { zQueue->addToQueue(source); worldUpdated = 1; } } } } 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++) { Framework::Vec3 pos = Framework::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 && Game::INSTANCE->zBlockType(type) ->doesNeedClientInstance(); } else { if (x >= 0 && x < CHUNK_SIZE && y >= 0 && y < CHUNK_SIZE) { cs.lock(); if (i < 4 && zNeighbours[i]) { Framework::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 |= Game::INSTANCE->zBlockType(type) ->doesNeedClientInstance(); } cs.unlock(); } } 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; } cs.lock(); 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); } cs.unlock(); } } } } } int end = -2; zWriter->schreibe((char*)&end, 4); } bool Chunk::isVisible(int index) const { if (!blocks[index]) { unsigned short blockType = blocks[index] ? (unsigned short)blocks[index]->zBlockType()->getId() : blockIds[index]; if (blockType) { if (CONST_BLOCK(0, blockIds[index])->isTransparent() || CONST_BLOCK(0, blockIds[index])->isPassable()) return 1; else { Framework::Vec3 indexPos = {(index / WORLD_HEIGHT) / CHUNK_SIZE, (index / WORLD_HEIGHT) % CHUNK_SIZE, index % WORLD_HEIGHT}; for (int d = 0; d < 6; d++) { Framework::Either n = BlockTypeEnum::NO_BLOCK; Framework::Vec3 pos = getDirection((Directions)getDirectionFromIndex(d)) + indexPos; if (pos.x >= 0 && pos.x < CHUNK_SIZE && pos.y >= 0 && pos.y < CHUNK_SIZE && pos.z >= 0 && pos.z < WORLD_HEIGHT) { n = zBlockAt(pos); } else if (pos.z >= 0 && pos.z < WORLD_HEIGHT && d < 4 && zNeighbours[d]) { if (pos.x < 0) pos.x += CHUNK_SIZE; if (pos.x >= CHUNK_SIZE) pos.x -= CHUNK_SIZE; if (pos.y < 0) pos.y += CHUNK_SIZE; if (pos.y >= CHUNK_SIZE) pos.y -= CHUNK_SIZE; n = zNeighbours[d]->zBlockAt(pos); } else if (pos.z >= 0 && pos.z < WORLD_HEIGHT && d < 4 && !zNeighbours[d]) { return 1; } if (n.isA() && (((Block*)n)->isPassable() || ((Block*)n)->isTransparent())) return 1; if (n.isB() && (CONST_BLOCK(0, n)->isTransparent() || CONST_BLOCK(0, n)->isPassable())) return 1; } } } return 0; } else return blocks[index]->isVisible(); } 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, OUT Chunk** zNeighborChunk) { 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 (zNeighborChunk) { *zNeighborChunk = this; } 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, zNeighborChunk); return 0; } void Chunk::notifyObservers(NetworkMessage* msg) { Framework::Array remove; int index = 0; for (InformationObserver* observer : observers) { if (!observer->sendMessage( dynamic_cast(msg->getThis()))) { remove.add(index, 0); } index++; } for (int i : remove) observers.remove(i); msg->release(); } void Chunk::addObserver(Entity* zEntity, DoLaterHandler& laterHandler) { for (InformationObserver* observer : observers) { if (observer->getEntityId() == zEntity->getId()) return; } int id = zEntity->getId(); observers.add(new InformationObserver(id)); laterHandler.addTodo([this, id]() { Framework::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)); Framework::Logging::debug() << "chunk size: " << location.x << ", " << location.y << ": " << buffer.getSize() << "b"; 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 (InformationObserver* observer : observers) { if (observer->getEntityId() == 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; case 2: // observer ready for (InformationObserver* observer : observers) { if (observer->getEntityId() == zSource->getId()) { observer->setReady(); } } } } 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(Framework::Vec3(x, y, z)); unsigned char newLight[6] = {0, 0, 0, 0, 0, 0}; for (int i = 0; i < 6; i++) { unsigned char* neighborLeight; Framework::Vec3 neighborPos = Framework::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] : Game::INSTANCE->zBlockType(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 { if (location.z < 0 || location.z >= WORLD_HEIGHT) return 0; 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 Game::INSTANCE->zBlockType(b.getB())->zDefault(); } const Block* Chunk::zBlockConst(int index) const { if (blocks[index]) return blocks[index]; else return Game::INSTANCE->zBlockType(blockIds[index])->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, Game::INSTANCE->zBlockType(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() != TickSourceType::NONE) { // remove from tick sorces if (old->isTickSource() == TickSourceType::EACH_TICK) { for (Framework::ArrayIterator obj = tickSourcesEachTick.begin(); obj; obj++) { if (obj.val() == old) { obj.remove(); break; } } } else if (old->isTickSource() == TickSourceType::AFTER_WORLD_UPDATE) { for (Framework::ArrayIterator obj = tickSourcesAfterUpdate.begin(); obj; obj++) { if (obj.val() == old) { obj.remove(); break; } } } } bool change = 0; bool wasLightSource = old ? old->zBlockType()->isLightSource() : Game::INSTANCE->zBlockType(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); Chunk* zNeighborChunk = 0; Framework::Either neighbor = zBlockNeighbor(location + getDirection(d), &zNeighborChunk); 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 (zNeighborChunk && zNeighborChunk != this) { zNeighborChunk->worldUpdated = 1; } } if (old) old->release(); if (block && block->isTickSource() != TickSourceType::NONE) { // add to tick sources if (block->isTickSource() == TickSourceType::EACH_TICK) { tickSourcesEachTick.add(block); } else if (block->isTickSource() == TickSourceType::AFTER_WORLD_UPDATE) { tickSourcesAfterUpdate.add(block); } } worldUpdated = 1; if (change) { if (isLightSource != wasLightSource) { if (isLightSource) addLightSource(index); else removeLightSource(index); } if (added) { sendBlockInfo(location); if (block) { Game::INSTANCE->updateLightningWithoutWait(getDimensionId(), Framework::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 = Game::INSTANCE->zBlockType(blockIds[index])->isLightSource(); bool isLightSource = Game::INSTANCE->zBlockType(type)->isLightSource(); if (blockIds[index] != (unsigned short)type) { blockIds[index] = (unsigned short)type; for (int i = 0; i < 6; i++) { Direction d = getDirectionFromIndex(i); Chunk* zNeighborChunk = 0; Framework::Either neighbor = zBlockNeighbor(location + getDirection(d), &zNeighborChunk); if (neighbor.isA()) ((Block*)neighbor) ->setNeighbourType(getOppositeDirection(d), type); if (zNeighborChunk && zNeighborChunk != this) { zNeighborChunk->worldUpdated = 1; } } if (isLightSource != wasLightSource) { if (isLightSource) addLightSource(index); else removeLightSource(index); } if (added) { sendBlockInfo(location); Game::INSTANCE->updateLightningWithoutWait(getDimensionId(), Framework::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); } worldUpdated = 1; } } void Chunk::sendBlockInfo(Framework::Vec3 location) { int index = Chunk::index(location); char* msg = new char[14]; msg[0] = 0; // set block *(unsigned short*)(msg + 1) = blockIds[index]; *(int*)(msg + 3) = index; char state = 0; const BlockType* type = Game::INSTANCE->zBlockType(blockIds[index]); if (type->isFluid()) { state |= 1; } if ((blocks[index] && blocks[index]->isPassable()) || (type->zDefault()->isPassable())) { state |= 2; } msg[7] = state; if ((state | 1) == state) { FluidBlock* fluidBlock = dynamic_cast(blocks[index]); msg[8] = fluidBlock ? fluidBlock->getFlowOptions() : 0; msg[9] = fluidBlock ? fluidBlock->getDistanceToSource() : 0; } if ((state | 2) == state) { *(float*)(msg + 10) = blocks[index] ? blocks[index]->getSpeedModifier() : type->zDefault()->getSpeedModifier(); } NetworkMessage* message = new NetworkMessage(); message->addressChunck(this); message->setMessage(msg, 14); notifyObservers(message); if (blocks[index]) { NetworkMessage* message = new NetworkMessage(); blocks[index]->sendModelInfo(message); if (message->isEmpty()) { message->release(); } else { notifyObservers(message); } } cs.lock(); 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); } } cs.unlock(); } void Chunk::setNeighbor(Direction dir, Chunk* zChunk) { cs.lock(); int dirIndex = getDirectionIndex(dir); Chunk* old = zNeighbours[dirIndex]; zNeighbours[dirIndex] = 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; } bool needsTransmission = 0; if (blocks[index]) { bool visible = blocks[index]->isVisible(); 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 (!visible && blocks[index]->isVisible()) { needsTransmission = 1; } } else { zNeighbours[dirIndex] = old; bool visible = isVisible(index); zNeighbours[dirIndex] = zChunk; if (!visible && isVisible(index)) { needsTransmission = 1; } } if (zChunk) { if (!blocks[index]) { if (zChunk->zBlockConst(j)->isTransparent() && !blockIds[index]) { generateBlock(Framework::Vec3( (index / WORLD_HEIGHT) / CHUNK_SIZE, (index / WORLD_HEIGHT) % CHUNK_SIZE, index % WORLD_HEIGHT)); } } } if (needsTransmission && added) { sendBlockInfo( Framework::Vec3((index / WORLD_HEIGHT) / CHUNK_SIZE, (index / WORLD_HEIGHT) % CHUNK_SIZE, index % WORLD_HEIGHT)); } } } cs.unlock(); } 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, Game::INSTANCE->zBlockType(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); Game::INSTANCE->zBlockType(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}); const BlockType* type = Game::INSTANCE->zBlockType(blockIds[index]); if (isVisible(index) && type->doesNeedClientInstance()) { zWriter->schreibe((char*)&blockIds[index], 2); zWriter->schreibe((char*)&index, 4); char state = 0; if (type->isFluid()) { state |= 1; } if ((blocks[index] && blocks[index]->isPassable()) || (type->zDefault()->isPassable())) { state |= 2; } zWriter->schreibe((char*)&state, 1); if ((state | 1) == state) { FluidBlock* fluidBlock = dynamic_cast(blocks[index]); char data = fluidBlock ? fluidBlock->getFlowOptions() : 0; zWriter->schreibe(&data, 1); data = fluidBlock ? fluidBlock->getDistanceToSource() : 0; zWriter->schreibe(&data, 1); } if ((state | 2) == state) { float speedModifier = blocks[index] ? blocks[index]->getSpeedModifier() : type->zDefault()->getSpeedModifier(); zWriter->schreibe((char*)&speedModifier, 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), 0); 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 (Game::INSTANCE->zBlockType(blockIds[i])->doesNeedClientInstance()) count++; } Framework::Logging::debug() << "chunk " << location.x << ", " << location.y << " was generated with " << count << " blocks."; } 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; cs.lock(); for (int i = 0; i < 4; i++) { if (zNeighbours[i]) { zNeighbours[i]->setNeighbor( getOppositeDirection(getDirectionFromIndex(i)), 0); zNeighbours[i] = 0; } } cs.unlock(); } 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++) { Framework::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 |= Game::INSTANCE->zBlockType(type) ->doesNeedClientInstance(); if (needSend) break; } else { int type = Game::INSTANCE->getBlockType( pos + Framework::Vec3( this->location.x - CHUNK_SIZE / 2, this->location.y - CHUNK_SIZE / 2, 0), dimensionId); needSend |= Game::INSTANCE->zBlockType(type) ->doesNeedClientInstance(); if (needSend) break; } } } if (needSend) { broadcastLightData(index, foreground); } } int Chunk::getBlockTypeAt(Framework::Vec3 location) const { return blockIds[index(location)]; } void Chunk::onEntityEnters(Entity* zEntity, Chunk* lastChunk) { NetworkMessage* msg = 0; for (InformationObserver* observer : observers) { if (!lastChunk || !lastChunk->hasObserver(zEntity->getId())) { if (!msg) { msg = new NetworkMessage(); msg->addEntityMessage(zEntity); if (msg->isEmpty()) break; } observer->sendMessage(msg); } } if (msg) msg->release(); } void Chunk::onEntityLeaves(Entity* zEntity, Chunk* zNextChunk) { NetworkMessage* msg = 0; for (InformationObserver* observer : observers) { if (!zNextChunk || !zNextChunk->hasObserver(zEntity->getId())) { if (!msg) { msg = new NetworkMessage(); msg->removeEntityMessage(zEntity); if (msg->isEmpty()) break; } observer->sendMessage(msg); } } if (msg) msg->release(); } bool Chunk::hasObserver(int entityId) const { for (InformationObserver* observer : observers) { if (observer->getEntityId() == entityId) return 1; } return 0; }