#include "Game.h" #include "Zeit.h" #include "Player.h" #include "OverworldDimension.h" #include "AddChunkUpdate.h" #include "NoBlock.h" using namespace Framework; GameClient::GameClient( Player* zPlayer, FCKlient* client ) : ReferenceCounter(), zPlayer( zPlayer ), client( client ), writer( client->zClient() ), viewDistance( DEFAULT_VIEW_DISTANCE ), first( 1 ), online( 1 ) {} GameClient::~GameClient() { client->release(); } void GameClient::sendWorldUpdate( WorldUpdate* zUpdate ) { if( zPlayer->getCurrentDimensionId() == zUpdate->getAffectedDimension() ) { auto pos = (Vec3)zPlayer->getPosition(); if( abs( pos.x - zUpdate->getMinAffectedPoint().x ) < viewDistance * CHUNK_SIZE || (abs( pos.x - zUpdate->getMaxAffectedPoint().x ) < viewDistance * CHUNK_SIZE) || abs( pos.y - zUpdate->getMinAffectedPoint().y ) < viewDistance * CHUNK_SIZE || (abs( pos.y - zUpdate->getMaxAffectedPoint().y ) < viewDistance * CHUNK_SIZE) ) { cs.Enter(); writer.schreibe( (char*)&Message::INGAME_MESSAGE, 1 ); writer.schreibe( (char*)&Message::WORLD_UPDATE, 1 ); zUpdate->write( &writer ); cs.Leave(); } } } void GameClient::reply( Game* zGame ) { cs.Enter(); for( auto req : requests ) zGame->api( req, this ); requests.leeren(); cs.Leave(); int x = (int)zPlayer->getPosition().x; int y = (int)zPlayer->getPosition().y; int d = zPlayer->getCurrentDimensionId(); cs.Enter(); writer.schreibe( (char*)&Message::INGAME_MESSAGE, 1 ); writer.schreibe( (char*)&Message::POSITION_UPDATE, 1 ); float f = zPlayer->getPosition().x; writer.schreibe( (char*)&f, 4 ); f = zPlayer->getPosition().y; writer.schreibe( (char*)&f, 4 ); f = zPlayer->getPosition().z; writer.schreibe( (char*)&f, 4 ); f = zPlayer->getFaceDir().x; writer.schreibe( (char*)&f, 4 ); f = zPlayer->getFaceDir().y; writer.schreibe( (char*)&f, 4 ); cs.Leave(); // send world to client if( first ) { first = 0; for( int xP = x - CHUNK_SIZE * viewDistance; x <= x + CHUNK_SIZE * viewDistance; x += CHUNK_SIZE ) { for( int yP = y - CHUNK_SIZE * viewDistance; y <= y + CHUNK_SIZE * viewDistance; y += CHUNK_SIZE ) { Chunk* chunk = zGame->zDimension( d )->zChunk( zGame->getChunkCenter( xP, yP ) ); if( chunk ) { AddChunkUpdate update( dynamic_cast(chunk->getThis()) ); sendWorldUpdate( &update ); } } } zGame->requestArea( { x - CHUNK_SIZE * viewDistance, y - CHUNK_SIZE * viewDistance, x + CHUNK_SIZE * viewDistance, y + CHUNK_SIZE * viewDistance, d } ); } else { Punkt lastMin = zGame->getChunkCenter( (int)lastPos.x - CHUNK_SIZE * viewDistance, (int)lastPos.y - CHUNK_SIZE * viewDistance ); Punkt curMin = zGame->getChunkCenter( x - CHUNK_SIZE * viewDistance, y - CHUNK_SIZE * viewDistance ); Punkt lastMax = zGame->getChunkCenter( (int)lastPos.x + CHUNK_SIZE * viewDistance, (int)lastPos.y + CHUNK_SIZE * viewDistance ); Punkt curMax = zGame->getChunkCenter( x + CHUNK_SIZE * viewDistance, y + CHUNK_SIZE * viewDistance ); for( int xP = curMin.x; x <= curMax.x; x += CHUNK_SIZE ) { for( int yP = curMin.y; y <= curMax.y; y += CHUNK_SIZE ) { if( xP < lastMin.x || xP > lastMax.x || yP < lastMin.y || yP > lastMax.y ) { Chunk* chunk = zGame->zDimension( d )->zChunk( zGame->getChunkCenter( x, y ) ); if( chunk ) { AddChunkUpdate update( dynamic_cast(chunk->getThis()) ); sendWorldUpdate( &update ); } else { zGame->requestArea( zGame->getChunckArea( Punkt( xP, yP ) ) ); } } } } } lastPos = zPlayer->getPosition(); } void GameClient::logout() { online = 0; } void GameClient::addMessage( StreamReader* reader ) { short len = 0; reader->lese( (char*)&len, 2 ); InMemoryBuffer* buffer = new InMemoryBuffer(); char* tmp = new char[ len ]; reader->lese( tmp, len ); buffer->schreibe( tmp, len ); delete[]tmp; cs.Enter(); requests.add( buffer ); cs.Leave(); } bool GameClient::isOnline() const { return online; } void GameClient::sendResponse( NetworkResponse* zResponse ) { if( zResponse->isAreaAffected( { lastPos.x - (float)CHUNK_SIZE * (float)viewDistance, lastPos.y - (float)CHUNK_SIZE * (float)viewDistance, 0.f }, { lastPos.x + (float)CHUNK_SIZE * (float)viewDistance, lastPos.y + (float)CHUNK_SIZE * (float)viewDistance, (float)WORLD_HEIGHT } ) ) { cs.Leave(); writer.schreibe( (char*)&Message::INGAME_MESSAGE, 1 ); writer.schreibe( (char*)&Message::API_MESSAGE, 1 ); zResponse->writeTo( client->zWriter() ); cs.Leave(); } } Player* GameClient::zEntity() const { return zPlayer; } Game::Game( Framework::Text name, Framework::Text worldsDir ) : Thread(), name( name ), dimensions( new RCArray() ), updates( new RCArray() ), clients( new RCArray() ), ticker( new TickOrganizer() ), path( (const char*)(worldsDir + "/" + name) ), stop( 0 ), tickId( 0 ), nextEntityId( 0 ), generator( 0 ), loader( 0 ) { if( !DateiExistiert( worldsDir + "/" + name ) ) DateiPfadErstellen( worldsDir + "/" + name + "/" ); Datei d; d.setDatei( path + "/eid" ); if( d.existiert() ) { d.open( Datei::Style::lesen ); d.lese( (char*)&nextEntityId, 4 ); d.close(); } int seed = 0; int index = 0; for( char* n = name; *n; n++ ) seed += (int)pow( (float)*n * 31, (float)++index ); generator = new WorldGenerator( seed, this ); loader = new WorldLoader( this ); start(); } Game::~Game() { dimensions->release(); updates->release(); clients->release(); generator->release(); loader->release(); } void Game::thread() { ZeitMesser m; while( !stop ) { m.messungStart(); ticker->nextTick(); Array removed; cs.Enter(); int index = 0; for( auto player : *clients ) { if( !player->isOnline() ) { Datei pFile; pFile.setDatei( path + "/player/" + player->zEntity()->getName() ); if( pFile.open( Datei::Style::schreiben ) ) PlayerEntityType::INSTANCE->saveEntity( player->zEntity(), &pFile ); removed.add( index, 0 ); } index++; } for( auto i : removed ) clients->remove( i ); cs.Leave(); for( auto dim : *dimensions ) dim->tickEntities( this ); cs.Enter(); while( updates->hat( 0 ) ) { WorldUpdate* update = updates->z( 0 ); cs.Leave(); for( auto client : *clients ) client->sendWorldUpdate( update ); if( !zDimension( update->getAffectedDimension() ) ) addDimension( new Dimension( update->getAffectedDimension() ) ); update->onUpdate( zDimension( update->getAffectedDimension() ) ); cs.Enter(); updates->remove( 0 ); } cs.Leave(); for( auto client : *clients ) client->reply( this ); m.messungEnde(); double sec = m.getSekunden(); if( sec < 0.05 ) Sleep( (int)((0.05 - sec) * 1000) ); } save(); } void Game::api( Framework::StreamReader* zRequest, GameClient* zOrigin ) { char type; zRequest->lese( &type, 1 ); NetworkResponse response; switch( type ) { case 1: // world { int dimensionId; zRequest->lese( (char*)&dimensionId, 4 ); Dimension* dim = zDimension( dimensionId ); if( !dim ) { dim = new Dimension( dimensionId ); addDimension( dim ); } dim->api( zRequest, &response ); break; } case 2: // player zOrigin->zEntity()->api( zRequest, &response ); break; default: std::cout << "received unknown api request in game with type " << (int)type << "\n"; } if( !response.isEmpty() ) { if( response.isBroadcast() ) distributeResponse( &response ); else zOrigin->sendResponse( &response ); } } void Game::distributeResponse( NetworkResponse* zResponse ) { for( auto client : *clients ) client->sendResponse( zResponse ); } void Game::requestWorldUpdate( WorldUpdate* update ) { cs.Enter(); updates->add( update ); cs.Leave(); } GameClient* Game::addPlayer( FCKlient* client, Framework::Text name ) { cs.Enter(); Datei pFile; pFile.setDatei( path + "/player/" + name ); Player* player; bool isNew = 0; if( !pFile.existiert() || !pFile.open( Datei::Style::lesen ) ) { player = (Player*)PlayerEntityType::INSTANCE->createEntityAt( Vec3( 0, 0, 0 ), OverworldDimension::ID, this ); player->setName( name ); isNew = 1; } else { player = (Player*)PlayerEntityType::INSTANCE->loadEntity( this, &pFile ); pFile.close(); } requestArea( { (int)player->getPosition().x - DEFAULT_VIEW_DISTANCE * CHUNK_SIZE, (int)player->getPosition().y - DEFAULT_VIEW_DISTANCE * CHUNK_SIZE, (int)player->getPosition().x + DEFAULT_VIEW_DISTANCE * CHUNK_SIZE, (int)player->getPosition().y + DEFAULT_VIEW_DISTANCE * CHUNK_SIZE, player->getCurrentDimensionId() } ); while( !zDimension( OverworldDimension::ID ) || (isNew && !zDimension( OverworldDimension::ID )->zChunk( getChunkCenter( (int)player->getPosition().x, (int)player->getPosition().y ) )) ) { cs.Leave(); Sleep( 1000 ); cs.Enter(); } if( isNew ) { Either b = NoBlockBlockType::ID; int h = WORLD_HEIGHT; while( ((b.isA() && (!(Block*)b || ((Block*)b)->isPassable())) || (b.isB() && StaticRegistry::INSTANCE.zElement( b )->zDefault()->isPassable())) && h > 0 ) b = zBlockAt( { (int)player->getPosition().x, (int)player->getPosition().y, --h }, player->getCurrentDimensionId() ); player->setPosition( { player->getPosition().x, player->getPosition().y, (float)h + 1.f } ); } zDimension( OverworldDimension::ID )->addEntity( player ); GameClient* gameClient = new GameClient( player, client ); clients->add( gameClient ); cs.Leave(); return dynamic_cast(gameClient->getThis()); } bool Game::isChunkLoaded( int x, int y, int dimension ) const { Dimension* dim = zDimension( dimension ); return (dim && dim->hasChunck( x, y )); } bool Game::doesChunkExist( int x, int y, int dimension ) const { return isChunkLoaded( x, y, dimension ) || loader->existsChunk( x, y, dimension ); } Framework::Either Game::zBlockAt( Framework::Vec3 location, int dimension ) const { Dimension* dim = zDimension( dimension ); if( dim ) return dim->zBlock( location, this ); return 0; } Dimension* Game::zDimension( int id ) const { for( auto dim : *dimensions ) { if( dim->getDimensionId() == id ) return dim; } return 0; } Framework::Punkt Game::getChunkCenter( int x, int y ) const { return Punkt( (x / CHUNK_SIZE) * CHUNK_SIZE + (x < 0 ? -CHUNK_SIZE : CHUNK_SIZE) / 2, (y / CHUNK_SIZE) * CHUNK_SIZE + (y < 0 ? -CHUNK_SIZE : CHUNK_SIZE) / 2 ); } Area Game::getChunckArea( Punkt center ) const { return { center.x - CHUNK_SIZE / 2, center.y - CHUNK_SIZE / 2, center.x + CHUNK_SIZE / 2, center.y + CHUNK_SIZE / 2, 0 }; } Framework::Text Game::getWorldDirectory() const { return path; } void Game::requestArea( Area area ) { generator->requestGeneration( area ); loader->requestLoading( area ); } void Game::save() const { Datei d; d.setDatei( path + "/eid" ); d.open( Datei::Style::schreiben ); d.schreibe( (char*)&nextEntityId, 4 ); d.close(); for( auto dim : *dimensions ) dim->save( path ); } void Game::requestStop() { stop = 1; warteAufThread( 1000000 ); } void Game::addDimension( Dimension* d ) { dimensions->add( d ); } int Game::getNextEntityId() { cs.Enter(); int result = nextEntityId++; cs.Leave(); return result; } WorldGenerator* Game::zGenerator() const { return generator; }