#include #include "Chunk.h" #include "Constants.h" #include "Game.h" #include "NoBlock.h" Chunk::Chunk(Framework::Punkt location, int dimensionId) : ReferenceCounter(), dimensionId(dimensionId), location(location), added(0) { 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::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; } } } 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(&msg, zE); index++; } for (int i : remove) observers.remove(i); } void Chunk::addObserver(Entity* zEntity) { for (int id : observers) { if (id == zEntity->getId()) return; } observers.add(zEntity->getId()); InMemoryBuffer buffer; buffer.schreibe("\4", 1); buffer.schreibe((char*)&location.x, 4); buffer.schreibe((char*)&location.y, 4); sendToClient(&buffer); NetworkMessage msg; msg.addressDimension(); char* message = new char[buffer.getSize()]; buffer.lese(message, (int)buffer.getSize()); msg.setMessage(message, (int)buffer.getSize(), 1); msg.setUseBackground(); Game::INSTANCE->sendMessage(&msg, zEntity); } 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) { // TODO: answer api messages char type; zRequest->lese(&type, 1); switch (type) { case 0: // register observer addObserver(zSource); 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(Vec3(x, y, neighborPos.z)); } for (int j = 0; j < 3; j++) newLight[j] = (unsigned char)MAX(newLight[j], i == getDirectionIndex(TOP) ? neighborLeight[j] : neighborLeight[j] - 16); for (int j = 3; j < 6; j++) newLight[j] = (unsigned char)MAX(newLight[j], neighborLeight[j] - 16); } 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]) { memcpy(light, newLight, 6); } } } } } if (!changes) break; } } Framework::Either Chunk::zBlockAt(Framework::Vec3 location) const { int index = (location.x * CHUNK_SIZE + location.y) * WORLD_HEIGHT + location.z; 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; if (b.getB()) return StaticRegistry::INSTANCE.zElement(b.getB())->zDefault(); return 0; } 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 }, 0)); } void Chunk::generateBlock(Framework::Vec3 location) { int index = (location.x * CHUNK_SIZE + location.y) * WORLD_HEIGHT + location.z; 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 = (location.x * CHUNK_SIZE + location.y) * WORLD_HEIGHT + location.z; assert(index < CHUNK_SIZE* CHUNK_SIZE* WORLD_HEIGHT&& index >= 0); Block* old = blocks[index]; 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 { change = old != 0; } blocks[index] = block; Either neighbor = zBlockNeighbor(location + getDirection(NORTH)); if (neighbor.isA()) ((Block*)neighbor)->setNeighbour(SOUTH, block); if (block) block->setNeighbour(NORTH, neighbor); neighbor = zBlockNeighbor(location + getDirection(EAST)); if (neighbor.isA()) ((Block*)neighbor)->setNeighbour(WEST, block); if (block) block->setNeighbour(EAST, neighbor); neighbor = zBlockNeighbor(location + getDirection(SOUTH)); if (neighbor.isA()) ((Block*)neighbor)->setNeighbour(NORTH, block); if (block) block->setNeighbour(SOUTH, neighbor); neighbor = zBlockNeighbor(location + getDirection(WEST)); if (neighbor.isA()) ((Block*)neighbor)->setNeighbour(EAST, block); if (block) block->setNeighbour(WEST, neighbor); neighbor = zBlockNeighbor(location + getDirection(TOP)); if (neighbor.isA()) ((Block*)neighbor)->setNeighbour(BOTTOM, block); if (block) block->setNeighbour(TOP, neighbor); neighbor = zBlockNeighbor(location + getDirection(BOTTOM)); if (neighbor.isA()) ((Block*)neighbor)->setNeighbour(TOP, block); if (block) block->setNeighbour(BOTTOM, neighbor); if (old) old->release(); if (change) { if (isLightSource != wasLightSource) { if (isLightSource) addLightSource(index); else removeLightSource(index); } if (added) { Game::INSTANCE->updateLightning(getDimensionId(), Vec3(location.x + this->location.x - CHUNK_SIZE / 2, location.y + this->location.y - CHUNK_SIZE / 2, location.z)); } char msg[9]; msg[0] = 0; // set block *(int*)(msg + 1) = index; *(int*)(msg + 5) = block ? block->zBlockType()->getId() : NoBlockBlockType::ID; NetworkMessage message; message.addressChunck(this); message.setMessage(msg, 9, 0); notifyObservers(message); } } void Chunk::putBlockTypeAt(Framework::Vec3 location, int type) { int index = (location.x * CHUNK_SIZE + location.y) * WORLD_HEIGHT + location.z; 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; Either neighbor = zBlockNeighbor(location + getDirection(NORTH)); if (neighbor.isA()) ((Block*)neighbor)->setNeighbourType(SOUTH, type); neighbor = zBlockNeighbor(location + getDirection(EAST)); if (neighbor.isA()) ((Block*)neighbor)->setNeighbourType(WEST, type); neighbor = zBlockNeighbor(location + getDirection(SOUTH)); if (neighbor.isA()) ((Block*)neighbor)->setNeighbourType(NORTH, type); neighbor = zBlockNeighbor(location + getDirection(WEST)); if (neighbor.isA()) ((Block*)neighbor)->setNeighbourType(EAST, type); neighbor = zBlockNeighbor(location + getDirection(TOP)); if (neighbor.isA()) ((Block*)neighbor)->setNeighbourType(BOTTOM, type); neighbor = zBlockNeighbor(location + getDirection(BOTTOM)); if (neighbor.isA()) ((Block*)neighbor)->setNeighbourType(TOP, type); if (isLightSource != wasLightSource) { if (isLightSource) addLightSource(index); else removeLightSource(index); } if (added) { Game::INSTANCE->updateLightning(getDimensionId(), Vec3(location.x + this->location.x - CHUNK_SIZE / 2, location.y + this->location.y - CHUNK_SIZE / 2, location.z)); } char msg[9]; msg[0] = 0; // set block *(int*)(msg + 1) = index; *(int*)(msg + 5) = type; NetworkMessage message; message.addressChunck(this); message.setMessage(msg, 9, 0); notifyObservers(message); } } 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++) { if (dir == NORTH) { int index = i * CHUNK_SIZE * WORLD_HEIGHT + z; if (blocks[index]) { int j = (i * CHUNK_SIZE + CHUNK_SIZE - 1) * WORLD_HEIGHT + z; if (zChunk && zChunk->blocks[j]) blocks[index]->setNeighbour(NORTH, zChunk->blocks[j]); else { blocks[index]->setNeighbour(NORTH, 0); blocks[index]->setNeighbourType(NORTH, zChunk ? zChunk->blockIds[j] : 0); } } } else if (dir == EAST) { int index = ((CHUNK_SIZE - 1) * CHUNK_SIZE + i) * WORLD_HEIGHT + z; if (blocks[index]) { int j = i * WORLD_HEIGHT + z; if (zChunk && zChunk->blocks[j]) blocks[index]->setNeighbour(EAST, zChunk->blocks[j]); else { blocks[index]->setNeighbour(EAST, 0); blocks[index]->setNeighbourType(EAST, zChunk ? zChunk->blockIds[j] : 0); } } } else if (dir == SOUTH) { int index = (i * CHUNK_SIZE + CHUNK_SIZE - 1) * WORLD_HEIGHT + z; if (blocks[index]) { int j = i * CHUNK_SIZE * WORLD_HEIGHT + z; if (zChunk && zChunk->blocks[j]) blocks[index]->setNeighbour(SOUTH, zChunk->blocks[j]); else { blocks[index]->setNeighbour(SOUTH, 0); blocks[index]->setNeighbourType(SOUTH, zChunk ? zChunk->blockIds[j] : 0); } } } else if (dir == WEST) { int index = i * WORLD_HEIGHT + z; if (blocks[index]) { int j = ((CHUNK_SIZE - 1) * CHUNK_SIZE + i) * WORLD_HEIGHT + z; if (zChunk && zChunk->blocks[j]) blocks[index]->setNeighbour(WEST, zChunk->blocks[j]); else { blocks[index]->setNeighbour(WEST, 0); blocks[index]->setNeighbourType(WEST, zChunk ? zChunk->blockIds[j] : 0); } } } } } } void Chunk::load(Framework::StreamReader* zReader) { unsigned short id = 0; zReader->lese((char*)&id, 2); Framework::Vec3 pos; bool d = 0; while (id) { zReader->lese((char*)&pos.x, 4); zReader->lese((char*)&pos.y, 4); zReader->lese((char*)&pos.z, 4); zReader->lese((char*)&d, 1); if (d) putBlockAt(pos, StaticRegistry::INSTANCE.zElement(id)->loadBlock(Framework::Vec3(pos.x + location.x - CHUNK_SIZE / 2, pos.y + location.y - CHUNK_SIZE / 2, pos.z), zReader)); else putBlockTypeAt(pos, id); zReader->lese((char*)&id, 2); } initializeLightning(); } void Chunk::save(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 = (x * CHUNK_SIZE + y) * WORLD_HEIGHT + z; unsigned short blockType = blocks[index] ? (unsigned short)blocks[index]->zBlockType()->getId() : blockIds[index]; if (blockType) { zWriter->schreibe((char*)&blockType, 2); zWriter->schreibe((char*)&x, 4); zWriter->schreibe((char*)&y, 4); zWriter->schreibe((char*)&z, 4); 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); } } } } } unsigned short end = 0; zWriter->schreibe((char*)&end, 2); } 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 = (x * CHUNK_SIZE + y) * WORLD_HEIGHT + 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++) { 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; } } } else visible = blocks[index]->isVisible(); } if (visible && (blocks[index] || blockType != AirBlockBlockType::ID)) { zWriter->schreibe((char*)&blockType, 2); zWriter->schreibe((char*)&x, 4); zWriter->schreibe((char*)&y, 4); zWriter->schreibe((char*)&z, 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]) { if (!blocks[i]->isVisible()) { int x = (i / WORLD_HEIGHT) / CHUNK_SIZE; int y = (i / WORLD_HEIGHT) % CHUNK_SIZE; int z = i % WORLD_HEIGHT; putBlockAt({ x,y,z }, 0); putBlockTypeAt({ x, y, z }, NoBlockBlockType::ID); } } else if (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 }, NoBlockBlockType::ID); } } } int count = 0; for (int i = 0; i < CHUNK_SIZE * CHUNK_SIZE * WORLD_HEIGHT; i++) { if (blockIds[i] && blockIds[i] != AirBlockBlockType::ID) count++; } std::cout << "chunk " << location.x << ", " << location.y << " was generated with " << count << " blocks.\n"; } int Chunk::getDimensionId() const { return dimensionId; } 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; } unsigned char* Chunk::getLightData(Framework::Vec3 location) const { int index = ((location.x * CHUNK_SIZE + location.y) * WORLD_HEIGHT + location.z) * 6; assert(index < CHUNK_SIZE* CHUNK_SIZE* WORLD_HEIGHT); return lightData + index; }