#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]; memset(blocks, 0, CHUNK_SIZE * CHUNK_SIZE * WORLD_HEIGHT * sizeof(Block*)); memset(blockIds, 0, CHUNK_SIZE * CHUNK_SIZE * WORLD_HEIGHT * sizeof(unsigned short)); 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; } 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::api(Framework::StreamReader* zRequest, NetworkResponse* zResponse) { // TODO: answer api messages } 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]; if (block) blockIds[index] = (unsigned short)block->zBlockType()->getId(); 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(); } 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); 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); } 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); } } 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; } void Chunk::addView(Entity* zEntity) { cs.lock(); bool found = 0; for (Entity* e : views) { if (e == zEntity) { found = 1; break; } } if (!found) views.add(zEntity); cs.unlock(); } void Chunk::removeView(Entity* zEntity) { cs.lock(); int i = 0; for (Entity* e : views) { if (e == zEntity) { views.remove(i); break; } i++; } cs.unlock(); } bool Chunk::hasView(Entity* zEntity) { cs.lock(); for (Entity* e : views) { if (e == zEntity) { cs.unlock(); return 1; } } cs.unlock(); return 0; } bool Chunk::hasViews() const { return views.getEintragAnzahl() > 0; }