#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, StreamTarget target ) { 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 = target == StreamTarget::FULL; 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( target == StreamTarget::FULL || (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 ); 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::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; }