#include "cChunkMap.h" #include "cChunk.h" #include "cMCLogger.h" #include "cWorld.h" #include "cRoot.h" #include "cMakeDir.h" #ifndef _WIN32 #include // memcpy #include // abs #include // floorf #include // sprintf and stuff #define sprintf_s( dest, size, format, ... ) sprintf( dest, format, __VA_ARGS__ ) #endif #include "zlib.h" #include #define USE_MEMCPY #define LAYER_SIZE (32) cChunkMap::cChunkMap( int a_Width, int a_Height, cWorld* a_World ) : m_Nodes( new cChunkNode[ a_Width * a_Height ] ) , m_Width( a_Width ) , m_Height( a_Height ) , m_Layers( 0 ) , m_NumLayers( 0 ) , m_World( a_World ) { } cChunkMap::~cChunkMap() { delete [] m_Nodes; } cChunkMap::cChunkNode::cChunkNode() { m_Size = 0; m_Allocated = 0; m_Chunks = 0; } cChunkMap::cChunkNode::~cChunkNode() { if( m_Allocated > 0 ) { for( unsigned int i = 0; i < m_Size; ++i ) { delete m_Chunks[i]; } delete [] m_Chunks; } // m_Chunks = 0; // m_Allocated = 0; // m_Size = 0; } void cChunkMap::cChunkNode::push_back( cChunk* a_Chunk ) { if( m_Allocated == 0 ) { resize( 1 ); } if( m_Size >= m_Allocated ) { resize( m_Allocated*2 ); } m_Chunks[ m_Size ] = a_Chunk; m_Size++; } void cChunkMap::cChunkNode::resize( unsigned int a_NewSize ) { cChunk** TempChunks = new cChunk*[a_NewSize]; if( m_Allocated > 0 ) { #ifdef USE_MEMCPY memcpy( TempChunks, m_Chunks, sizeof( cChunk* ) * m_Size ); #else for( unsigned int i = 0; i < a_NewSize; ++i ) TempChunks[i] = m_Chunks[i]; #endif delete [] m_Chunks; } m_Chunks = TempChunks; m_Allocated = a_NewSize; } void cChunkMap::cChunkNode::erase( cChunk* a_Chunk ) { if( m_Size == 0 ) return; cChunk** TempChunks = new cChunk*[m_Size]; unsigned int TempIdx = 0; for( unsigned int i = 0; i < m_Size; ++i ) { if( m_Chunks[i] != a_Chunk ) { TempChunks[TempIdx] = m_Chunks[i]; TempIdx++; } } delete [] m_Chunks; m_Chunks = 0; if( TempIdx > 0 ) { m_Chunks = new cChunk*[ TempIdx ]; #ifdef USE_MEMCPY memcpy( m_Chunks, TempChunks, sizeof( cChunk* ) * TempIdx ); #else for( unsigned int i = 0; i < TempIdx; ++i ) m_Chunks[i] = TempChunks[i]; #endif } delete [] TempChunks; m_Allocated = TempIdx; m_Size = TempIdx; } cChunkMap::cChunkData* cChunkMap::cChunkLayer::GetChunk( int a_X, int a_Z ) { const int LocalX = a_X - m_X * LAYER_SIZE; const int LocalZ = a_Z - m_Z * LAYER_SIZE; //LOG("LocalX:%i LocalZ:%i", LocalX, LocalZ ); if( LocalX < LAYER_SIZE && LocalZ < LAYER_SIZE && LocalX > -1 && LocalZ > -1 ) return &m_Chunks[ LocalX + LocalZ * LAYER_SIZE ]; return 0; } bool cChunkMap::RemoveLayer( cChunkLayer* a_Layer ) { cChunkLayer* NewLayers = 0; if( m_NumLayers > 1 ) NewLayers = new cChunkLayer[m_NumLayers-1]; int idx = 0; bool bExcludedLayer = false; for( int i = 0; i < m_NumLayers; ++i ) { if( &m_Layers[i] != a_Layer ) { if( idx < m_NumLayers-1 ) { NewLayers[ idx ] = m_Layers[i]; idx++; } } else bExcludedLayer = true; } if( !bExcludedLayer ) { LOGWARN("Could not remove layer, because layer was not found %i %i", a_Layer->m_X, a_Layer->m_Z); delete [] NewLayers; return false; } if( m_Layers ) delete [] m_Layers; m_Layers = NewLayers; m_NumLayers--; return true; } cChunkMap::cChunkLayer* cChunkMap::AddLayer( const cChunkLayer & a_Layer ) { cChunkLayer* TempLayers = new cChunkLayer[m_NumLayers+1]; if( m_NumLayers > 0 ) { memcpy( TempLayers, m_Layers, sizeof( cChunkLayer ) * m_NumLayers ); delete [] m_Layers; } m_Layers = TempLayers; m_Layers[m_NumLayers] = a_Layer; cChunkLayer* NewLayer = &m_Layers[m_NumLayers]; m_NumLayers++; return NewLayer; } void cChunkMap::AddChunk( cChunk* a_Chunk ) { /* // OLD m_Nodes[ MakeHash( a_Chunk->GetPosX(), a_Chunk->GetPosZ() ) ].push_back( a_Chunk ); */ // NEW const int LayerX = (int)(floorf((float)a_Chunk->GetPosX() / (float)(LAYER_SIZE))); const int LayerZ = (int)(floorf((float)a_Chunk->GetPosZ() / (float)(LAYER_SIZE))); cChunkLayer* FoundLayer = GetLayer( LayerX, LayerZ ); if( !FoundLayer ) { cChunkLayer NewLayer( LAYER_SIZE*LAYER_SIZE ); NewLayer.m_X = LayerX; NewLayer.m_Z = LayerZ; FoundLayer = AddLayer( NewLayer ); LOGWARN("Created new layer %i %i (total layers %i)", LayerX, LayerZ, m_NumLayers ); } //Get local coordinates in layer const int LocalX = a_Chunk->GetPosX() - LayerX * LAYER_SIZE; const int LocalZ = a_Chunk->GetPosZ() - LayerZ * LAYER_SIZE; if( FoundLayer->m_Chunks[ LocalX + LocalZ * LAYER_SIZE ].m_LiveChunk ) LOGWARN("WARNING: Added chunk to layer while it was already loaded!"); if( FoundLayer->m_Chunks[ LocalX + LocalZ * LAYER_SIZE ].m_Compressed ) LOGWARN("WARNING: Added chunk to layer while a compressed version exists!"); FoundLayer->m_Chunks[ LocalX + LocalZ * LAYER_SIZE ].m_LiveChunk = a_Chunk; FoundLayer->m_NumChunksLoaded++; } void cChunkMap::RemoveChunk( cChunk* a_Chunk ) { /* // OLD m_Nodes[ MakeHash( a_Chunk->GetPosX(), a_Chunk->GetPosZ() ) ].erase( a_Chunk ); */ // NEW cChunkLayer* Layer = GetLayerForChunk( a_Chunk->GetPosX(), a_Chunk->GetPosZ() ); if( Layer ) { cChunkData* Data = Layer->GetChunk( a_Chunk->GetPosX(), a_Chunk->GetPosZ() ); if( Data->m_LiveChunk ) { CompressChunk( Data ); Data->m_LiveChunk = 0; // Set live chunk to 0 } Layer->m_NumChunksLoaded--; } } void cChunkMap::CompressChunk( cChunkData* a_ChunkData ) { if( a_ChunkData->m_LiveChunk ) { // Delete already present compressed data if( a_ChunkData->m_Compressed ) delete [] a_ChunkData->m_Compressed; // Get Json data Json::Value root; std::string JsonData = ""; a_ChunkData->m_LiveChunk->SaveToJson( root ); if( !root.empty() ) { Json::StyledWriter writer; // TODO FIXME: change to FastWriter ? :D JsonData = writer.write( root ); } unsigned int TotalSize = cChunk::c_BlockDataSize + JsonData.size(); uLongf CompressedSize = compressBound( TotalSize ); a_ChunkData->m_Compressed = new char[CompressedSize]; char* DataSource = a_ChunkData->m_LiveChunk->pGetBlockData(); if( JsonData.size() > 0 ) { // Move stuff around, so data is aligned in memory DataSource = new char[TotalSize]; memcpy( DataSource, a_ChunkData->m_LiveChunk->pGetBlockData(), cChunk::c_BlockDataSize ); memcpy( DataSource + cChunk::c_BlockDataSize, JsonData.c_str(), JsonData.size() ); } int errorcode = compress2( (Bytef*)a_ChunkData->m_Compressed, &CompressedSize, (const Bytef*)DataSource, TotalSize, Z_DEFAULT_COMPRESSION); if( errorcode != Z_OK ) { LOGERROR("Error compressing data (%i)", errorcode ); } a_ChunkData->m_CompressedSize = CompressedSize; a_ChunkData->m_UncompressedSize = TotalSize; if( DataSource != a_ChunkData->m_LiveChunk->pGetBlockData() ) delete [] DataSource; } } unsigned int cChunkMap::MakeHash( int a_X, int a_Z ) { const unsigned int HashX = abs( a_X ) % m_Width; const unsigned int HashZ = abs( a_Z ) % m_Height; return HashX + HashZ * m_Width; } cChunkMap::cChunkLayer* cChunkMap::GetLayerForChunk( int a_ChunkX, int a_ChunkZ ) { const int LayerX = (int)(floorf((float)a_ChunkX / (float)(LAYER_SIZE))); const int LayerZ = (int)(floorf((float)a_ChunkZ / (float)(LAYER_SIZE))); return GetLayer( LayerX, LayerZ ); } cChunkMap::cChunkLayer* cChunkMap::GetLayer( int a_LayerX, int a_LayerZ ) { // Find layer in memory for( int i = 0; i < m_NumLayers; ++i ) { if( m_Layers[i].m_X == a_LayerX && m_Layers[i].m_Z == a_LayerZ ) { return &m_Layers[i]; } } // Find layer on disk cChunkLayer* Layer = LoadLayer( a_LayerX, a_LayerZ ); if( !Layer ) return 0; cChunkLayer* NewLayer = AddLayer( *Layer ); delete Layer; return NewLayer; } cChunk* cChunkMap::GetChunk( int a_X, int a_Y, int a_Z ) { /* // OLD unsigned int Hash = MakeHash( a_X, a_Z ); cChunkNode & Node = m_Nodes[ Hash ]; cChunk** Chunks = Node.GetChunks(); for( unsigned int i = 0; i < Node.size(); ++i ) { if( Chunks[i]->GetPosX() == a_X && // Check if we found the right chunk Chunks[i]->GetPosY() == a_Y && Chunks[i]->GetPosZ() == a_Z ) { return Chunks[i]; } } */ // NEW cChunkLayer* Layer = GetLayerForChunk( a_X, a_Z ); if( Layer ) { cChunkData* Data = Layer->GetChunk( a_X, a_Z ); if( Data->m_LiveChunk ) return Data->m_LiveChunk; // Decompress cached chunk if( Data->m_Compressed ) { uLongf DestSize = Data->m_UncompressedSize; char* BlockData = new char[ DestSize ]; int errorcode = uncompress( (Bytef*)BlockData, &DestSize, (Bytef*)Data->m_Compressed, Data->m_CompressedSize ); if( Data->m_UncompressedSize != DestSize ) { LOGWARN("Lulwtf, expected uncompressed size differs!"); delete [] BlockData; } else if( errorcode != Z_OK ) { LOGERROR("ERROR: Decompressing chunk data! %i", errorcode ); switch( errorcode ) { case Z_MEM_ERROR: LOGERROR("Not enough memory"); break; case Z_BUF_ERROR: LOGERROR("Not enough room in output buffer"); break; case Z_DATA_ERROR: LOGERROR("Input data corrupted or incomplete"); break; default: break; }; delete [] BlockData; } else { cChunk* Chunk = new cChunk(a_X, a_Y, a_Z, m_World); memcpy( Chunk->m_BlockData, BlockData, cChunk::c_BlockDataSize ); Chunk->CalculateHeightmap(); Data->m_LiveChunk = Chunk; Layer->m_NumChunksLoaded++; if( DestSize > cChunk::c_BlockDataSize ) // We gots some extra data :D { LOGINFO("Parsing trailing JSON"); Json::Value root; // will contains the root value after parsing. Json::Reader reader; if( !reader.parse( BlockData + cChunk::c_BlockDataSize, root, false ) ) { LOGERROR("Failed to parse trailing JSON!"); } else { //Json::StyledWriter writer; //LOGINFO("Trailing Json:" ); //printf("%s", writer.write( root ).c_str() ); Chunk->LoadFromJson( root ); } } delete [] BlockData; delete [] Data->m_Compressed; Data->m_Compressed = 0; Data->m_CompressedSize = 0; return Chunk; } } } return 0; } void cChunkMap::Tick( float a_Dt ) { /* // OLD for( int i = 0; i < m_Width*m_Height; ++i ) { cChunkNode & Node = m_Nodes[ i ]; cChunk** Chunks = Node.GetChunks(); for( unsigned int i = 0; i < Node.size(); ++i ) { Chunks[i]->Tick( a_Dt ); } } */ // NEW for( int l = 0; l < m_NumLayers; ++l ) { for( int i = 0; i < LAYER_SIZE*LAYER_SIZE; ++i ) { cChunk* Chunk = m_Layers[l].m_Chunks[i].m_LiveChunk; if( Chunk ) Chunk->Tick( a_Dt ); } } } void cChunkMap::UnloadUnusedChunks() { cWorld* World = m_World; /* // OLD for( int i = 0; i < m_Width*m_Height; ++i ) { cChunkNode & Node = m_Nodes[ i ]; cChunk** Chunks = Node.GetChunks(); for( unsigned int i = 0; i < Node.size(); ++i ) { if( Chunks[i]->GetClients().size() == 0 ) { Chunks[i]->SaveToDisk(); LOG("Unloading %p", Chunks[i] ); World->RemoveSpread( Chunks[i] ); cChunk* TheChunk = Chunks[i]; RemoveChunk( TheChunk ); delete TheChunk; //Node.erase( Chunks[i] ); Chunks = Node.GetChunks(); // Chunks pointer is no longer valid, get a new one } } } */ // NEW for( int l = 0; l < m_NumLayers; ++l ) { cChunkLayer & Layer = m_Layers[l]; for( int i = 0; i < LAYER_SIZE*LAYER_SIZE; ++i ) { cChunk* Chunk = Layer.m_Chunks[i].m_LiveChunk; if( Chunk && Chunk->GetClients().size() == 0 ) { Chunk->SaveToDisk(); World->RemoveSpread( Chunk ); RemoveChunk( Chunk ); delete Chunk; } } // Unload layers if( Layer.m_NumChunksLoaded == 0 ) { SaveLayer( &Layer ); for( int i = 0; i < LAYER_SIZE*LAYER_SIZE; ++i ) // Free all chunk data for layer { if( Layer.m_Chunks[i].m_Compressed ) delete [] Layer.m_Chunks[i].m_Compressed; if( Layer.m_Chunks[i].m_LiveChunk ) delete Layer.m_Chunks[i].m_LiveChunk; } if( RemoveLayer( &Layer ) ) l--; } else if( Layer.m_NumChunksLoaded < 0 ) { LOGERROR("WTF! Chunks loaded in layer is %i !!", Layer.m_NumChunksLoaded ); } } } bool cChunkMap::RemoveEntityFromChunk( cEntity & a_Entity, cChunk* a_CalledFrom /* = 0 */ ) { /* // OLD for( int i = 0; i < m_Width*m_Height; ++i ) { cChunkNode & Node = m_Nodes[ i ]; cChunk** Chunks = Node.GetChunks(); for( unsigned int i = 0; i < Node.size(); ++i ) { if( Chunks[i] != a_CalledFrom ) { if( Chunks[i]->RemoveEntity( a_Entity, a_CalledFrom ) ) return true; } } } */ // NEW for( int i = 0; i < m_NumLayers; ++i ) { cChunkLayer & Layer = m_Layers[i]; for( int i = 0; i < LAYER_SIZE*LAYER_SIZE; ++i ) { cChunk* Chunk = Layer.m_Chunks[i].m_LiveChunk; if( Chunk != a_CalledFrom ) { if( Chunk && Chunk->RemoveEntity( a_Entity, a_CalledFrom ) ) return true; } } } LOG("WARNING: Entity was not found in any chunk!"); return false; } void cChunkMap::SaveAllChunks() { for( int i = 0; i < m_Width*m_Height; ++i ) { cChunkNode & Node = m_Nodes[ i ]; cChunk** Chunks = Node.GetChunks(); for( unsigned int i = 0; i < Node.size(); ++i ) { Chunks[i]->SaveToDisk(); } } for( int i = 0; i < m_NumLayers; ++i ) { SaveLayer( &m_Layers[i] ); } } /******************************** * Saving and loading **/ void cChunkMap::SaveLayer( cChunkLayer* a_Layer ) { std::string WorldName = m_World->GetName(); cMakeDir::MakeDir( WorldName.c_str() ); char SourceFile[128]; sprintf_s(SourceFile, 128, ( WorldName + "/X%i_Z%i.pak").c_str(), a_Layer->m_X, a_Layer->m_Z ); FILE* f = 0; #ifdef _WIN32 if( fopen_s(&f, SourceFile, "wb" ) == 0 ) // no error #else if( (f = fopen(SourceFile, "wb" )) != 0 ) // no error #endif { //--------------- // Header char PakVersion = 1; char ChunkVersion = 1; fwrite( &PakVersion, sizeof(PakVersion), 1, f ); // pak version fwrite( &ChunkVersion, sizeof(ChunkVersion), 1, f ); // chunk version // Count number of chunks in layer short NumChunks = 0; for( int i = 0; i < LAYER_SIZE*LAYER_SIZE; ++i ) { if( a_Layer->m_Chunks[i].m_Compressed || a_Layer->m_Chunks[i].m_LiveChunk ) NumChunks++; } fwrite( &NumChunks, sizeof( NumChunks ), 1, f ); LOG("Num Chunks in layer: %i", NumChunks ); //--------------- // Chunk headers for( int z = 0; z < LAYER_SIZE; ++z ) { for( int x = 0; x < LAYER_SIZE; ++x ) { cChunkData & Data = a_Layer->m_Chunks[x + z*LAYER_SIZE]; CompressChunk( &Data ); if( Data.m_Compressed || Data.m_LiveChunk ) { int ChunkX = a_Layer->m_X*LAYER_SIZE + x; int ChunkZ = a_Layer->m_Z*LAYER_SIZE + z; unsigned int Size = Data.m_CompressedSize; // Needs to be size of compressed data unsigned int USize = Data.m_UncompressedSize; // Uncompressed size fwrite( &ChunkX, sizeof( ChunkX ), 1, f ); fwrite( &ChunkZ, sizeof( ChunkZ ), 1, f ); fwrite( &Size, sizeof( Size ), 1, f ); fwrite( &USize, sizeof( USize ), 1, f ); } } } //---------------- // Chunk data for( int i = 0; i < LAYER_SIZE*LAYER_SIZE; ++i ) { char* Compressed = a_Layer->m_Chunks[i].m_Compressed; if( Compressed ) { fwrite( Compressed, a_Layer->m_Chunks[i].m_CompressedSize, 1, f ); if(a_Layer->m_Chunks[i].m_LiveChunk) // If there's a live chunk we have no need for compressed data { delete [] a_Layer->m_Chunks[i].m_Compressed; a_Layer->m_Chunks[i].m_Compressed = 0; a_Layer->m_Chunks[i].m_CompressedSize = 0; } } } fclose(f); } else { LOGERROR("ERROR: Could not write to file %s", SourceFile ); } } cChunkMap::cChunkLayer* cChunkMap::LoadLayer(int a_LayerX, int a_LayerZ ) { std::string WorldName = m_World->GetName(); char SourceFile[128]; sprintf_s(SourceFile, 128, (WorldName + "/X%i_Z%i.pak").c_str(), a_LayerX, a_LayerZ ); FILE* f = 0; #ifdef _WIN32 if( fopen_s(&f, SourceFile, "rb" ) == 0 ) // no error #else if( (f = fopen(SourceFile, "rb" )) != 0 ) // no error #endif { char PakVersion = 0; char ChunkVersion = 0; if( fread( &PakVersion, sizeof(PakVersion), 1, f) != 1 ) { LOGERROR("ERROR 1 READING FROM FILE %s", SourceFile); fclose(f); return false; } if( PakVersion != 1 ) { LOGERROR("WRONG PAK VERSION!"); fclose(f); return 0; } if( fread( &ChunkVersion, sizeof(ChunkVersion), 1, f) != 1 ) { LOGERROR("ERROR 2 READING FROM FILE %s", SourceFile); fclose(f); return false; } if( PakVersion != 1 ) { LOGERROR("WRONG CHUNK VERSION!"); fclose(f); return 0; } short NumChunks = 0; if( fread( &NumChunks, sizeof(NumChunks), 1, f) != 1 ) { LOGERROR("ERROR 3 READING FROM FILE %s", SourceFile); fclose(f); return false; } LOG("Num chunks: %i", NumChunks ); LOG("Source File: %s", SourceFile ); cChunkLayer* Layer = new cChunkLayer( LAYER_SIZE*LAYER_SIZE ); Layer->m_X = a_LayerX; Layer->m_Z = a_LayerZ; cChunkData** OrderedData = new cChunkData*[ NumChunks ]; // So we can loop over the chunks in the order they were loaded // Loop over all chunk headers for( short i = 0; i < NumChunks; ++i ) { int ChunkX = 0; int ChunkZ = 0; if( fread( &ChunkX, sizeof(ChunkX), 1, f) != 1 ) { LOGERROR("ERROR 4 READING FROM FILE %s", SourceFile); fclose(f); return false; } if( fread( &ChunkZ, sizeof(ChunkZ), 1, f) != 1 ) { LOGERROR("ERROR 5 READING FROM FILE %s", SourceFile); fclose(f); return false; } cChunkData* Data = Layer->GetChunk( ChunkX, ChunkZ ); if( Data ) { if( fread( &Data->m_CompressedSize, sizeof(Data->m_CompressedSize), 1, f) != 1 ) { LOGERROR("ERROR 6 READING FROM FILE %s", SourceFile); fclose(f); return false; } if( fread( &Data->m_UncompressedSize, sizeof(Data->m_UncompressedSize), 1, f) != 1 ) { LOGERROR("ERROR 7 READING FROM FILE %s", SourceFile); fclose(f); return false; } } else { LOGERROR("Chunk with wrong coordinates in pak file! %i %i", ChunkX, ChunkZ ); fclose(f); return 0; } OrderedData[i] = Data; } // Loop over chunks again, in the order they were loaded, and load their compressed data for( short i = 0; i < NumChunks; ++i ) { cChunkData* Data = OrderedData[i]; Data->m_Compressed = new char[ Data->m_CompressedSize ]; //printf("Compressed chunk size: %i\n",Data->m_CompressedSize); if( fread( Data->m_Compressed, Data->m_CompressedSize, 1, f) != 1 ) { LOGERROR("ERROR 8 READING FROM FILE %s", SourceFile); fclose(f); return false; } /* // Some testing... uLongf DestSize = Data->m_UncompressedSize; char* BlockData = new char[ DestSize ]; int errorcode = uncompress( (Bytef*)BlockData, &DestSize, (Bytef*)Data->m_Compressed, Data->m_CompressedSize ); if( errorcode != Z_OK ) { LOGERROR("lulwut"); } */ } delete [] OrderedData; fclose(f); return Layer; } else { //LOGWARN("Could not open file %s", SourceFile ); } return 0; }