#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**[WORLD_HEIGHT]; blockIds = new unsigned short*[WORLD_HEIGHT]; lightData = new unsigned char[CHUNK_SIZE * CHUNK_SIZE * WORLD_HEIGHT * 6]; memset(blocks, 0, WORLD_HEIGHT * sizeof(Block**)); memset(blockIds, 0, 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 h = 0; h < WORLD_HEIGHT; h++) { for (int i = 0; i < CHUNK_SIZE * CHUNK_SIZE; i++) { if (blocks[h] && blocks[h][i]) blocks[h][i]->release(); } delete[] blocks[h]; blocks[h] = 0; delete[] blockIds[h]; blockIds[h] = 0; } 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 z, int index) { for (int i : lightSources) { if (i == index * WORLD_HEIGHT + z) return; } lightSources.add(index * WORLD_HEIGHT + z); } void Chunk::removeLightSource(int z, int index) { for (auto i = lightSources.begin(); i; i++) { if (i.val() == index * WORLD_HEIGHT + z) { i.remove(); return; } } } void Chunk::sendToClient(Framework::StreamWriter* zWriter, bool* instanceMap) { 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); const BlockType* type = Game::INSTANCE->zBlockType( blockIds[z] ? blockIds[z][index] : 0); if (isVisible(z, index) && type->doesNeedClientInstance()) { int mI = index * WORLD_HEIGHT + z; if (z < WORLD_HEIGHT - 1) { instanceMap[mI + (CHUNK_SIZE + 1) * WORLD_HEIGHT + 1] = 1; } if (z > 0) { instanceMap[mI + (CHUNK_SIZE + 1) * WORLD_HEIGHT - 1] = 1; } instanceMap[mI + WORLD_HEIGHT] = 1; instanceMap[mI + (2 * CHUNK_SIZE + 1) * WORLD_HEIGHT] = 1; instanceMap[mI + CHUNK_SIZE * WORLD_HEIGHT] = 1; instanceMap[mI + (CHUNK_SIZE + 2) * WORLD_HEIGHT] = 1; assert(blockIds[z]); zWriter->schreibe((char*)&blockIds[z][index], 2); zWriter->schreibe((char*)&mI, 4); char state = 0; if (type->isFluid()) { state |= 1; } if ((blocks[z] && blocks[z][index] && blocks[z][index]->isPassable()) || (type->zDefault()->isPassable())) { state |= 2; } zWriter->schreibe((char*)&state, 1); if ((state | 1) == state) { FluidBlock* fluidBlock = blocks[z] ? dynamic_cast(blocks[z][index]) : 0; 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[z] && blocks[z][index] ? blocks[z][index]->getSpeedModifier() : type->zDefault()->getSpeedModifier(); zWriter->schreibe((char*)&speedModifier, 4); } } } } } unsigned short end = 0; zWriter->schreibe((char*)&end, 2); } void Chunk::sendLightToClient( Framework::StreamWriter* zWriter, bool* instanceMap) { cs.lock(); Chunk* zNeighbours[4]; for (int i = 0; i < 4; i++) { zNeighbours[i] = this->zNeighbours[i] ? dynamic_cast(this->zNeighbours[i]->getThis()) : 0; if (zNeighbours[i]) { Direction dir = getDirectionFromIndex(i); switch (dir) { case WEST: for (int z = 0; z < WORLD_HEIGHT; z++) { for (int j = 0; j < CHUNK_SIZE; j++) { int type = zNeighbours[i]->getBlockTypeAt( {CHUNK_SIZE - 1, j, z}); if (type && Game::INSTANCE->zBlockType(type) ->doesNeedClientInstance()) { instanceMap[(CHUNK_SIZE + j + 1) * WORLD_HEIGHT + z] = 1; } } } break; case NORTH: for (int z = 0; z < WORLD_HEIGHT; z++) { for (int j = 0; j < CHUNK_SIZE; j++) { int type = zNeighbours[i]->getBlockTypeAt( {j, CHUNK_SIZE - 1, z}); if (type && Game::INSTANCE->zBlockType(type) ->doesNeedClientInstance()) { instanceMap[((j + 1) * CHUNK_SIZE + 1) * WORLD_HEIGHT + z] = 1; } } } break; case EAST: for (int z = 0; z < WORLD_HEIGHT; z++) { for (int j = 0; j < CHUNK_SIZE; j++) { int type = zNeighbours[i]->getBlockTypeAt({0, j, z}); if (type && Game::INSTANCE->zBlockType(type) ->doesNeedClientInstance()) { instanceMap[((CHUNK_SIZE)*CHUNK_SIZE + j + 1) * WORLD_HEIGHT + z] = 1; } } } break; case SOUTH: for (int z = 0; z < WORLD_HEIGHT; z++) { for (int j = 0; j < CHUNK_SIZE; j++) { int type = zNeighbours[i]->getBlockTypeAt({j, 0, z}); if (type && Game::INSTANCE->zBlockType(type) ->doesNeedClientInstance()) { instanceMap[((j + 1) * CHUNK_SIZE + CHUNK_SIZE) * WORLD_HEIGHT + z] = 1; } } } break; } } } cs.unlock(); 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; } if (instanceMap[((x + 1) * CHUNK_SIZE + y + 1) * WORLD_HEIGHT + z]) { if (x >= 0 && x < CHUNK_SIZE && y >= 0 && y < CHUNK_SIZE) { int index = Chunk::index(x, 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); for (int i = 0; i < 4; i++) { if (zNeighbours[i]) { zNeighbours[i]->release(); } } } bool Chunk::isVisible(int z, int index) const { unsigned short blockType = blockIds[z] ? blockIds[z][index] : 0; if (blockType) { if (CONST_BLOCK(0, blockType)->isTransparent() || CONST_BLOCK(0, blockType)->isPassable()) return 1; else { Framework::Vec3 indexPos = {(index) / CHUNK_SIZE, (index) % CHUNK_SIZE, z}; 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; } void Chunk::broadcastLightData(int z, int index, bool foreground) { int x = index / CHUNK_SIZE; int y = index % CHUNK_SIZE; 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 * WORLD_HEIGHT + z) * 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 = Chunk::index(location.x, location.y); if (zNeighborChunk) { *zNeighborChunk = this; } if (blocks[location.z] && blocks[location.z][index]) return blocks[location.z][index]; else return blockIds[location.z] ? (int)blockIds[location.z][index] : 0; } if (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); bool instanceMap[(CHUNK_SIZE + 2) * (CHUNK_SIZE + 2) * WORLD_HEIGHT]; memset(instanceMap, 0, sizeof(instanceMap)); sendToClient(&buffer, instanceMap); sendLightToClient(&buffer, instanceMap); NetworkMessage* msg = new NetworkMessage(); msg->addressDimension(Game::INSTANCE->zDimension(dimensionId)); #ifdef _DEBUG Framework::Logging::debug() << "chunk size: " << location.x << ", " << location.y << ": " << buffer.getSize() << "b"; #endif 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[3] = {255, 255, 255}; unsigned char noLight[3] = {0, 0, 0}; unsigned short visited[CHUNK_SIZE][WORLD_HEIGHT]; memset(visited, 0, sizeof(visited)); int minZ = 0; int goUps = 0; for (int z = WORLD_HEIGHT - 1; z >= 0; z--) { minZ = z; unsigned char max[3] = {0, 0, 0}; bool allVisited = 1; for (int x = 0; x < CHUNK_SIZE; x++) { for (int y = 0; y < CHUNK_SIZE; y++) { if (visited[y][z] & (1 << x)) continue; unsigned char* lightAbove = z == WORLD_HEIGHT - 1 ? dayLight : getLightData(Framework::Vec3(x, y, z + 1)); if (lightAbove[0] | lightAbove[1] | lightAbove[2]) { visited[y][z] |= 1 << x; unsigned char* light = getLightData(Framework::Vec3(x, y, z)); int index = Chunk::index(x, y); const Block* current = (blocks[z] && blocks[z][index]) ? blocks[z][index] : Game::INSTANCE ->zBlockType( blockIds[z] ? blockIds[z][index] : 0) ->zDefault(); light[0] = lightAbove[0]; light[1] = lightAbove[1]; light[2] = lightAbove[2]; current->filterPassingLight(light); max[0] = MAX(max[0], lightAbove[0]); max[1] = MAX(max[1], lightAbove[1]); max[2] = MAX(max[2], lightAbove[2]); } else { allVisited = 0; } } } if (!(max[0] | max[1] | max[2])) break; if (!allVisited) { bool goUp = 1; while (goUp) { goUp = 0; bool changes = 1; while (changes) { changes = 0; for (int x = 0; x < CHUNK_SIZE; x++) { for (int y = 0; y < CHUNK_SIZE; y++) { int index = Chunk::index(x, y); unsigned char* light = getLightData(Framework::Vec3(x, y, z)); const Block* current = (blocks[z] && blocks[z][index]) ? blocks[z][index] : Game::INSTANCE ->zBlockType(blockIds[z] ? blockIds[z][index] : 0) ->zDefault(); unsigned char newLight[3] = {0, 0, 0}; for (int i = 0; i < 4; i++) { Framework::Vec3 neighborPos = Framework::Vec3(x, y, z) + getDirection(getDirectionFromIndex(i)); if (neighborPos.x < 0 || neighborPos.y < 0 || neighborPos.x >= CHUNK_SIZE || neighborPos.y >= CHUNK_SIZE) { continue; } unsigned char* neighborLeight = getLightData(neighborPos); for (int j = 0; j < 3; j++) { newLight[j] = (unsigned char)MAX( newLight[j], (unsigned char)((float)neighborLeight[j] * 0.8f)); } } current->filterPassingLight(newLight); if (newLight[0] > light[0] || newLight[1] > light[1] || newLight[2] > light[2]) { changes = 1; light[0] = MAX(light[0], newLight[0]); light[1] = MAX(light[1], newLight[1]); light[2] = MAX(light[2], newLight[2]); if (z < WORLD_HEIGHT - 1 && !(visited[y][z + 1] & (1 << x))) { unsigned char* lightAbove = getLightData( Framework::Vec3(x, y, z + 1)); newLight[0] = (unsigned char)(light[0] * 0.8f); newLight[1] = (unsigned char)(light[1] * 0.8f); newLight[2] = (unsigned char)(light[2] * 0.8f); const Block* above = blocks[z + 1] && blocks[z + 1][index] ? blocks[z + 1][index] : Game::INSTANCE ->zBlockType( blockIds[z + 1] ? blockIds[z + 1] [index] : 0) ->zDefault(); above->filterPassingLight(newLight); if (newLight[0] > lightAbove[0] || newLight[1] > lightAbove[1] || newLight[2] > lightAbove[2]) { lightAbove[0] = MAX(lightAbove[0], newLight[0]); lightAbove[1] = MAX(lightAbove[1], newLight[1]); lightAbove[2] = MAX(lightAbove[2], newLight[2]); visited[y][z + 1] |= 1 << x; goUp = 1; } } } } } } if (goUp) { z++; goUps++; } } } } #ifdef _DEBUG Framework::Logging::debug() << "goUps: " << goUps << " minZ: " << minZ; #endif } void Chunk::updateLightSources() { Dimension* zDim = Game::INSTANCE->zDimension(dimensionId); for (int i : lightSources) { int x = (i / WORLD_HEIGHT) / CHUNK_SIZE; int y = (i / WORLD_HEIGHT) % CHUNK_SIZE; int z = i % WORLD_HEIGHT; Framework::Vec3 pos = {x, y, z}; zDim->updateLightning( Framework::Vec3(x + this->location.x - CHUNK_SIZE / 2, y + this->location.y - CHUNK_SIZE / 2, z)); } } Framework::Either Chunk::zBlockAt( Framework::Vec3 location) const { if (location.z < 0 || location.z >= WORLD_HEIGHT) return 0; int index = Chunk::index(location.x, location.y); assert(index < CHUNK_SIZE * CHUNK_SIZE * WORLD_HEIGHT); if (blocks[location.z] && blocks[location.z][index]) return blocks[location.z][index]; else return blockIds[location.z] ? (int)blockIds[location.z][index] : 0; } 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 z, int index) const { if (blocks[z] && blocks[z][index]) return blocks[z][index]; else return Game::INSTANCE->zBlockType(blockIds[z] ? blockIds[z][index] : 0) ->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.x, location.y); if (blockIds[location.z] && blockIds[location.z][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.x, location.y); assert(index < CHUNK_SIZE * CHUNK_SIZE && index >= 0); Block* old = blocks[location.z] ? blocks[location.z][index] : 0; 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; } } } } if (!blockIds[location.z]) { blockIds[location.z] = new unsigned short[CHUNK_SIZE * CHUNK_SIZE]; memset(blockIds[location.z], 0, CHUNK_SIZE * CHUNK_SIZE * sizeof(unsigned short)); } if (!blocks[location.z]) { blocks[location.z] = new Block*[CHUNK_SIZE * CHUNK_SIZE]; memset(blocks[location.z], 0, CHUNK_SIZE * CHUNK_SIZE * sizeof(Block*)); } bool change = 0; bool wasLightSource = old ? old->zBlockType()->isLightSource() : Game::INSTANCE->zBlockType(blockIds[location.z][index]) ->isLightSource(); bool isLightSource = 0; if (block) { change = blockIds[location.z][index] != (unsigned short)block->zBlockType()->getId(); blockIds[location.z][index] = (unsigned short)block->zBlockType()->getId(); isLightSource = block->zBlockType()->isLightSource(); } else { if (old != 0) { blockIds[location.z][index] = BlockTypeEnum::NO_BLOCK; } change = old != 0; } blocks[location.z][index] = block; 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(location.z, index); else removeLightSource(location.z, 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.x, location.y); assert(index < CHUNK_SIZE * CHUNK_SIZE); int oldType = blockIds[location.z] ? blockIds[location.z][index] : 0; bool wasLightSource = Game::INSTANCE->zBlockType(oldType)->isLightSource(); bool isLightSource = Game::INSTANCE->zBlockType(type)->isLightSource(); if (oldType != (unsigned short)type) { if (!blockIds[location.z]) { blockIds[location.z] = new unsigned short[CHUNK_SIZE * CHUNK_SIZE]; memset(blockIds[location.z], 0, CHUNK_SIZE * CHUNK_SIZE * sizeof(unsigned short)); } blockIds[location.z][index] = (unsigned short)type; if (isLightSource != wasLightSource) { if (isLightSource) addLightSource(location.z, index); else removeLightSource(location.z, 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.x, location.y); char* msg = new char[14]; msg[0] = 0; // set block int typeId = blockIds[location.z] ? blockIds[location.z][index] : 0; *(unsigned short*)(msg + 1) = typeId; *(int*)(msg + 3) = index; char state = 0; const BlockType* type = Game::INSTANCE->zBlockType(typeId); if (type->isFluid()) { state |= 1; } if ((blocks[location.z] && blocks[location.z][index] && blocks[location.z][index]->isPassable()) || (type->zDefault()->isPassable())) { state |= 2; } msg[7] = state; if ((state | 1) == state) { FluidBlock* fluidBlock = dynamic_cast( blocks[location.z] ? blocks[location.z][index] : 0); msg[8] = fluidBlock ? fluidBlock->getFlowOptions() : 0; msg[9] = fluidBlock ? fluidBlock->getDistanceToSource() : 0; } if ((state | 2) == state) { *(float*)(msg + 10) = blocks[location.z] && blocks[location.z][index] ? blocks[location.z][index]->getSpeedModifier() : type->zDefault()->getSpeedModifier(); } NetworkMessage* message = new NetworkMessage(); message->addressChunck(this); message->setMessage(msg, 14); notifyObservers(message); if (blocks[location.z] && blocks[location.z][index]) { NetworkMessage* message = new NetworkMessage(); blocks[location.z][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(loc.z, Chunk::index(loc.x, loc.y), 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); zNeighbours[dirIndex] = zChunk; cs.unlock(); } Chunk* Chunk::zNeighbor(Direction dir) const { return zNeighbours[getDirectionIndex(dir)]; } 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 < CHUNK_SIZE * CHUNK_SIZE; index++) { for (int z = 0; z < WORLD_HEIGHT; z++) { unsigned short blockType = blockIds[z] ? blockIds[z][index] : 0; zWriter->schreibe((char*)&blockType, 2); if (blockType) { if (blocks[z] && blocks[z][index]) { bool d = 1; zWriter->schreibe((char*)&d, 1); Game::INSTANCE->zBlockType(blockType)->saveBlock( blocks[z][index], zWriter); } else { bool d = 0; zWriter->schreibe((char*)&d, 1); } } } } } void Chunk::removeUnusedBlocks() { // no longer needed becaus only used blocks are generated in the first place #ifdef _DEBUG int count = 0; for (int i = 0; i < CHUNK_SIZE * CHUNK_SIZE; i++) { for (int z = 0; z < WORLD_HEIGHT; z++) { if (Game::INSTANCE->zBlockType(blockIds[z] ? blockIds[z][i] : 0) ->doesNeedClientInstance()) count++; } } Framework::Logging::debug() << "chunk " << location.x << ", " << location.y << " was generated with " << count << " blocks."; #endif } int Chunk::getDimensionId() const { return dimensionId; } void Chunk::onLoaded() { for (int i = 0; i < CHUNK_SIZE * CHUNK_SIZE; i++) { for (int z = 0; z < WORLD_HEIGHT; z++) { if (blocks[z] && blocks[z][i]) blocks[z][i]->onLoaded(); } } currentlyLoading = 0; } void Chunk::onUnloaded() { for (int i = 0; i < CHUNK_SIZE * CHUNK_SIZE; i++) { for (int z = 0; z < WORLD_HEIGHT; z++) { if (blocks[z] && blocks[z][i]) blocks[z][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.x, location.y) * WORLD_HEIGHT + location.z) * 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.x, location.y); memcpy(lightData + (index * WORLD_HEIGHT + location.z) * 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); int type = blockIds[pos.z] ? blockIds[pos.z][bi] : 0; 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(location.z, index, foreground); } } int Chunk::getBlockTypeAt(Framework::Vec3 location) const { return blockIds[location.z] ? blockIds[location.z][index(location.x, location.y)] : 0; } int Chunk::getBlockTypeAtWC(int x, int y, int z) const { auto pos = Dimension::chunkCoordinates({x, y, z}); return blockIds[pos.z] ? blockIds[pos.z][index(pos.x, pos.y)] : 0; } 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( dynamic_cast(msg->getThis())); } } 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( dynamic_cast(msg->getThis())); } } if (msg) msg->release(); } bool Chunk::hasObserver(int entityId) const { for (InformationObserver* observer : observers) { if (observer->getEntityId() == entityId) return 1; } return 0; }