#include "regions.h" #include #include #include #include "uox3.h" #include "classes.h" #include "StringUtility.hpp" #include "ObjectFactory.h" using namespace std::string_literals; #define DEBUG_REGIONS 0 CMapHandler *MapRegion; //o------------------------------------------------------------------------------------------------o //| Function - FileSize() //o------------------------------------------------------------------------------------------------o //| Purpose - Returns the filesize of a given file //o------------------------------------------------------------------------------------------------o SI32 FileSize( std::string filename ) { std::error_code ec; SI32 retVal = 0; std::uintmax_t size = std::filesystem::file_size( filename, ec ); if( ec ) { retVal = 0; } else { constexpr std::uintmax_t max_si32 = static_cast( std::numeric_limits::max() ); if( size > max_si32 ) { retVal = 0; } else { retVal = static_cast( size ); } } return retVal; } //o------------------------------------------------------------------------------------------------o //| Function - LoadChar() //o------------------------------------------------------------------------------------------------o //| Purpose - Creates a new character object based on data loaded from worldfiles //o------------------------------------------------------------------------------------------------o void LoadChar( std::istream& readDestination ) { CChar *x = static_cast( ObjectFactory::GetSingleton().CreateBlankObject( OT_CHAR )); if( x == nullptr ) return; if( !x->Load( readDestination )) { x->Cleanup(); ObjectFactory::GetSingleton().DestroyObject( x ); } } //o------------------------------------------------------------------------------------------------o //| Function - LoadItem() //o------------------------------------------------------------------------------------------------o //| Purpose - Creates a new item object based on data loaded from worldfiles //o------------------------------------------------------------------------------------------------o void LoadItem( std::istream& readDestination ) { CItem *x = static_cast( ObjectFactory::GetSingleton().CreateBlankObject( OT_ITEM )); if( x == nullptr ) return; if( !x->Load( readDestination )) { x->Cleanup(); ObjectFactory::GetSingleton().DestroyObject( x ); } } //o------------------------------------------------------------------------------------------------o //| Function - LoadMulti() //o------------------------------------------------------------------------------------------------o //| Purpose - Creates a new multi object, like a house, based on data loaded from worldfiles //o------------------------------------------------------------------------------------------------o void LoadMulti( std::istream& readDestination ) { CMultiObj *ourHouse = static_cast( ObjectFactory::GetSingleton().CreateBlankObject( OT_MULTI )); if( !ourHouse->Load( readDestination )) // if no load, DELETE { ourHouse->Cleanup(); ObjectFactory::GetSingleton().DestroyObject( ourHouse ); } } //o------------------------------------------------------------------------------------------------o //| Function - LoadBoat() //o------------------------------------------------------------------------------------------------o //| Purpose - Creates a new boat object based on data loaded from worldfiles //o------------------------------------------------------------------------------------------------o void LoadBoat( std::istream& readDestination ) { CBoatObj *ourBoat = static_cast( ObjectFactory::GetSingleton().CreateBlankObject( OT_BOAT )); if( !ourBoat->Load( readDestination )) // if no load, DELETE { ourBoat->Cleanup(); ObjectFactory::GetSingleton().DestroyObject( ourBoat ); } } //o------------------------------------------------------------------------------------------------o //| Function - LoadSpawnItem() //o------------------------------------------------------------------------------------------------o //| Purpose - Creates a new spawn object based on data loaded from worldfiles //o------------------------------------------------------------------------------------------------o void LoadSpawnItem( std::istream& readDestination ) { CSpawnItem *ourSpawner = static_cast( ObjectFactory::GetSingleton().CreateBlankObject( OT_SPAWNER )); if( !ourSpawner->Load( readDestination )) // if no load, DELETE { ourSpawner->Cleanup(); ObjectFactory::GetSingleton().DestroyObject( ourSpawner ); } } //o------------------------------------------------------------------------------------------------o //| Functio - CMapRegion::SaveToDisk() //o------------------------------------------------------------------------------------------------o //| Purpose - Save all items and characters inside a subregion reworked SaveChar from WorldMain //| to deal with pointer based stuff in region rather than index based stuff in array //| Also saves out all data regardless (in preparation for a simple binary save) //o------------------------------------------------------------------------------------------------o void CMapRegion::SaveToDisk( std::ostream& writeDestination ) { std::vector removeChar; for( const auto &charToWrite : charData.collection() ) { if( !ValidateObject( charToWrite )) { removeChar.push_back( charToWrite ); } else { #if defined( _MSC_VER ) #pragma todo( "PlayerHTML Dumping needs to be reimplemented" ) #endif if( charToWrite->ShouldSave() ) { charToWrite->Save( writeDestination ); } } } std::for_each( removeChar.begin(), removeChar.end(), [this]( CChar *character ) { charData.Remove( character ); }); std::vector removeItem; for( const auto &itemToWrite : itemData.collection() ) { if( !ValidateObject( itemToWrite )) { removeItem.push_back( itemToWrite ); } else { if( itemToWrite->ShouldSave() ) { if( itemToWrite->GetObjType() == OT_MULTI ) { CMultiObj *iMulti = static_cast( itemToWrite ); iMulti->Save( writeDestination ); } else if( itemToWrite->GetObjType() == OT_BOAT ) { CBoatObj *iBoat = static_cast( itemToWrite ); iBoat->Save( writeDestination ); } else { itemToWrite->Save( writeDestination ); } } } } std::for_each( removeItem.begin(), removeItem.end(), [this]( CItem *item ) { itemData.Remove( item ); }); } //o------------------------------------------------------------------------------------------------o //| Function - CMapRegion::GetItemList() //o------------------------------------------------------------------------------------------------o //| Purpose - Returns the Item DataList for iteration //o------------------------------------------------------------------------------------------------o GenericList * CMapRegion::GetItemList( void ) { return &itemData; } //o------------------------------------------------------------------------------------------------o //| Function - CMapRegion::GetCharList() //o------------------------------------------------------------------------------------------------o //| Purpose - Returns the Character DataList for iteration //o------------------------------------------------------------------------------------------------o GenericList * CMapRegion::GetCharList( void ) { return &charData; } //o------------------------------------------------------------------------------------------------o //| Function - CMapRegion::GetRegionSerialList() //o------------------------------------------------------------------------------------------------o //| Purpose - Returns the list of baseobject serials for iteration //o------------------------------------------------------------------------------------------------o RegionSerialList* CMapRegion::GetRegionSerialList() { return ®ionSerialData; } //o------------------------------------------------------------------------------------------------o //| Function - CMapRegion::HasRegionChanged() //o------------------------------------------------------------------------------------------------o //| Purpose - Returns a flag that says whether the region has seen any updates since last save //o------------------------------------------------------------------------------------------------o bool CMapRegion::HasRegionChanged( void ) { return hasRegionChanged; } //o------------------------------------------------------------------------------------------------o //| Function - CMapRegion::HasRegionChanged() //o------------------------------------------------------------------------------------------------o //| Purpose - Sets a flag that says whether the region has seen any updates since last save //o------------------------------------------------------------------------------------------------o void CMapRegion::HasRegionChanged( bool newVal ) { hasRegionChanged = newVal; } //o------------------------------------------------------------------------------------------------o //| Function - CMapWorld Constructor/Destructor //| Date - 17 October, 2005 //o------------------------------------------------------------------------------------------------o //| Purpose - Initializes & Clears the MapWorld data //o------------------------------------------------------------------------------------------------o CMapWorld::CMapWorld() : upperArrayX( 0 ), upperArrayY( 0 ), resourceX( 0 ), resourceY( 0 ) { mapResources.resize( 1 ); // ALWAYS initialize at least one resource region. mapRegions.resize( 0 ); } //o------------------------------------------------------------------------------------------------o //| Function - CMapWorld::CMapWorld() //o------------------------------------------------------------------------------------------------o //| Purpose - Initializes resource regions for the given world //o------------------------------------------------------------------------------------------------o CMapWorld::CMapWorld( UI08 worldNum ) { auto [mapWidth, mapHeight] = Map->SizeOfMap( worldNum ); upperArrayX = static_cast( mapWidth / MapColSize ); upperArrayY = static_cast( mapHeight/ MapRowSize ); resourceX = static_cast( mapWidth / cwmWorldState->ServerData()->ResourceAreaSize() ); resourceY = static_cast( mapHeight / cwmWorldState->ServerData()->ResourceAreaSize() ); size_t resourceSize = ( static_cast( resourceX ) * static_cast( resourceY )); if( resourceSize < 1 ) // ALWAYS initialize at least one resource region. { resourceSize = 1; } mapResources.resize( resourceSize ); mapRegions.resize( static_cast( upperArrayX ) * static_cast( upperArrayY )); } CMapWorld::~CMapWorld() { mapResources.resize( 0 ); mapRegions.resize( 0 ); } //o------------------------------------------------------------------------------------------------o //| Function - CMapWorld::GetMapRegion() //| Date - 9/13/2005 //o------------------------------------------------------------------------------------------------o //| Purpose - Returns the MapRegion based on its x and y offsets //o------------------------------------------------------------------------------------------------o CMapRegion * CMapWorld::GetMapRegion( SI16 xOffset, SI16 yOffset ) { CMapRegion *mRegion = nullptr; const size_t regionIndex = ( static_cast( xOffset ) * static_cast( upperArrayY ) + static_cast( yOffset )); if( xOffset >= 0 && xOffset < upperArrayX && yOffset >= 0 && yOffset < upperArrayY ) { if( regionIndex < mapRegions.size() ) { mRegion = &mapRegions[regionIndex]; } } return mRegion; } std::vector * CMapWorld::GetMapRegions() { return &mapRegions; } //o------------------------------------------------------------------------------------------------o //| Function - CMapWorld::GetResource() //| Date - 9/17/2005 //o------------------------------------------------------------------------------------------------o //| Purpose - Returns the Resource region x and y is. //o------------------------------------------------------------------------------------------------o MapResource_st& CMapWorld::GetResource( SI16 x, SI16 y ) { const UI16 gridX = ( x / cwmWorldState->ServerData()->ResourceAreaSize() ); const UI16 gridY = ( y / cwmWorldState->ServerData()->ResourceAreaSize() ); size_t resIndex = (( static_cast( gridX ) * static_cast( resourceY )) + static_cast( gridY )); if( gridX >= resourceX || gridY >= resourceY || resIndex > mapResources.size() ) { resIndex = 0; } return mapResources[resIndex]; } //o------------------------------------------------------------------------------------------------o //| Function - CMapWorld::SaveResources() //| Date - 9/17/2005 //o------------------------------------------------------------------------------------------------o //| Purpose - Saves the Resource data to disk //o------------------------------------------------------------------------------------------------o void CMapWorld::SaveResources( UI08 worldNum ) { auto resourceFile = std::filesystem::path( cwmWorldState->ServerData()->Directory( CSDDP_SHARED ) + "resource["s + std::to_string( worldNum ) + "].bin"s ); auto output = std::ofstream( resourceFile.string(), std::ios::binary ); auto buffer = std::vector( 3, 0 ); if( output.is_open() ) { for( auto iter = mapResources.begin(); iter != mapResources.end(); iter++ ) { buffer[0] = iter->oreAmt; std::reverse( reinterpret_cast( buffer.data()), reinterpret_cast( buffer.data()) + 2 ); // Make it big endian buffer[1] = iter->logAmt; std::reverse( reinterpret_cast( buffer.data()) + 2, reinterpret_cast( buffer.data()) + 4 ); buffer[2] = iter->fishAmt; std::reverse( reinterpret_cast( buffer.data()) + 4, reinterpret_cast( buffer.data()) + 6 ); // Now write it output.write( reinterpret_cast( buffer.data()), buffer.size() * 2 ); } } else { // Can't save resources Console.Error( "Failed to open resource.bin for writing" ); } } //o------------------------------------------------------------------------------------------------o //| Function - CMapWorld::LoadResources() //| Date - 9/17/2005 //o------------------------------------------------------------------------------------------------o //| Purpose - Loads the Resource data from the disk //o------------------------------------------------------------------------------------------------o void CMapWorld::LoadResources( UI08 worldNum ) { mapResources = std::vector( mapResources.size(), MapResource_st( cwmWorldState->ServerData()->ResOre(), cwmWorldState->ServerData()->ResLogs(), cwmWorldState->ServerData()->ResFish(), BuildTimeValue( static_cast( cwmWorldState->ServerData()->ResOreTime() )), BuildTimeValue( static_cast( cwmWorldState->ServerData()->ResLogTime() )), BuildTimeValue( static_cast( cwmWorldState->ServerData()->ResFishTime() )))); auto resourceFile = std::filesystem::path( cwmWorldState->ServerData()->Directory( CSDDP_SHARED ) + "resource["s + oldstrutil::number( worldNum ) + "].bin"s ); // The data is grouped as three shorts (for each resource), so we read in that format auto buffer = std::vector( 3, 0 ); auto input = std::ifstream( resourceFile.string(), std::ios::binary ); // We want to get the iteratro for the first mapResources ; auto iter = mapResources.begin(); if( input.is_open() ) { while( input.good() && !input.eof() && iter != mapResources.end() ) { input.read( reinterpret_cast( buffer.data() ), buffer.size() * 2 ); if( input.gcount() != buffer.size() * 2 ) { // We had issues reading the full amount, break out of this break; } // For whatever reason the resources are stored in big endidan, which on int/arm machines , we need little endian, so reverse them std::for_each( buffer.begin(), buffer.end(), []( SI16 &value ) { std::reverse( reinterpret_cast( &value ), reinterpret_cast( &value ) + 2 ); }); // Now set the values ( *iter ).oreAmt = buffer[0]; ( *iter ).logAmt = buffer[1]; ( *iter ).fishAmt = buffer[2]; iter++; } } } //o------------------------------------------------------------------------------------------------o //| Function - CMapHandler::Startup() //| Date - 23 July, 2000 //o------------------------------------------------------------------------------------------------o //| Purpose - Fills and clears the mapWorlds vector. //o------------------------------------------------------------------------------------------------o auto CMapHandler::Startup() -> void { UI08 numWorlds = Map->MapCount(); mapWorlds.reserve( numWorlds ); for( UI08 i = 0; i < numWorlds; ++i ) { mapWorlds.push_back( new CMapWorld( i )); } } CMapHandler::~CMapHandler() { for( WORLDLIST_ITERATOR mIter = mapWorlds.begin(); mIter != mapWorlds.end(); ++mIter ) { if(( *mIter ) != nullptr ) { delete ( *mIter ); ( *mIter ) = nullptr; } } mapWorlds.resize( 0 ); } //o------------------------------------------------------------------------------------------------o //| Function - CMapHandler::ChangeRegion() //| Date - February 1, 2006 //o------------------------------------------------------------------------------------------------o //| Purpose - Changes an items region ONLY if he has changed regions. //o------------------------------------------------------------------------------------------------o bool CMapHandler::ChangeRegion( CItem *nItem, SI16 x, SI16 y, UI08 worldNum ) { if( !ValidateObject( nItem )) return false; CMapRegion *curCell = GetMapRegion( nItem ); CMapRegion *newCell = GetMapRegion( GetGridX( x ), GetGridY( y ), worldNum ); if( curCell != newCell ) { if( !curCell->GetRegionSerialList()->Remove( nItem->GetSerial() ) || !curCell->GetItemList()->Remove( nItem )) { #if DEBUG_REGIONS Console.Warning( oldstrutil::format( "Item 0x%X does not exist in MapRegion, remove failed", nItem->GetSerial() )); #endif } else { if( nItem->ShouldSave() ) { curCell->HasRegionChanged( true ); } } if( newCell->GetRegionSerialList()->Add( nItem->GetSerial() )) { newCell->GetItemList()->Add( nItem, false ); if( nItem->ShouldSave() ) { newCell->HasRegionChanged( true ); } return true; } else { #if DEBUG_REGIONS Console.Warning( oldstrutil::format( "Item 0x%X already exists in MapRegion, add failed", nItem->GetSerial() )); #endif } } return false; } //o------------------------------------------------------------------------------------------------o //| Function - CMapHandler::ChangeRegion() //| Date - February 1, 2006 //o------------------------------------------------------------------------------------------------o //| Purpose - Changes a characters region ONLY if he has changed regions. //o------------------------------------------------------------------------------------------------o bool CMapHandler::ChangeRegion( CChar *nChar, SI16 x, SI16 y, UI08 worldNum ) { if( !ValidateObject( nChar )) return false; CMapRegion *curCell = GetMapRegion( nChar ); CMapRegion *newCell = GetMapRegion( GetGridX( x ), GetGridY( y ), worldNum ); if( curCell != newCell ) { if( !curCell->GetRegionSerialList()->Remove( nChar->GetSerial() ) || !curCell->GetCharList()->Remove( nChar )) { #if DEBUG_REGIONS Console.Warning( oldstrutil::format( "Character 0x%X does not exist in MapRegion, remove failed", nChar->GetSerial() )); #endif } else { if( nChar->ShouldSave() ) { curCell->HasRegionChanged( true ); } } if( newCell->GetRegionSerialList()->Add( nChar->GetSerial() )) { newCell->GetCharList()->Add( nChar, false ); if( nChar->ShouldSave() ) { newCell->HasRegionChanged( true ); } return true; } else { #if DEBUG_REGIONS Console.Warning( oldstrutil::format( "Character 0x%X already exists in MapRegion, add failed", nChar->GetSerial() )); #endif } } return false; } //o------------------------------------------------------------------------------------------------o //| Function - CMapHandler::AddItem() //| Date - 23 July, 2000 //o------------------------------------------------------------------------------------------------o //| Purpose - Adds nItem to the proper SubRegion //o------------------------------------------------------------------------------------------------o bool CMapHandler::AddItem( CItem *nItem ) { if( !ValidateObject( nItem )) return false; CMapRegion *cell = GetMapRegion( nItem ); if( cell->GetRegionSerialList()->Add( nItem->GetSerial() )) { cell->GetItemList()->Add( nItem, false ); if( nItem->ShouldSave() ) { cell->HasRegionChanged( true ); } return true; } else { #if DEBUG_REGIONS Console.Warning( oldstrutil::format( "Item 0x%X already exists in MapRegion, add failed", nItem->GetSerial() )); #endif } return false; } //o------------------------------------------------------------------------------------------------o //| Function - CMapHandler::RemoveItem() //| Date - 23 July, 2000 //o------------------------------------------------------------------------------------------------o //| Purpose - Removes nItem from it's CURRENT SubRegion. Do this before adjusting the location //o------------------------------------------------------------------------------------------------o bool CMapHandler::RemoveItem( CItem *nItem ) { if( !ValidateObject( nItem )) return false; CMapRegion *cell = GetMapRegion( nItem ); if( cell->GetRegionSerialList()->Remove( nItem->GetSerial() )) { cell->GetItemList()->Remove( nItem ); if( nItem->ShouldSave() ) { cell->HasRegionChanged( true ); } } else { #if DEBUG_REGIONS Console.Warning( oldstrutil::format( "Item 0x%X does not exist in MapRegion, remove failed", nItem->GetSerial() )); #endif return false; } return true; } //o------------------------------------------------------------------------------------------------o //| Function - CMapHandler::AddChar() //| Date - 23 July, 2000 //o------------------------------------------------------------------------------------------------o //| Purpose - Adds toAdd to the proper SubRegion //o------------------------------------------------------------------------------------------------o bool CMapHandler::AddChar( CChar *toAdd ) { if( !ValidateObject( toAdd )) return false; CMapRegion *cell = GetMapRegion( toAdd ); if( cell->GetRegionSerialList()->Add( toAdd->GetSerial() )) { cell->GetCharList()->Add( toAdd, false ); if( toAdd->ShouldSave() ) { cell->HasRegionChanged( true ); } return true; } else { #if DEBUG_REGIONS Console.Warning( oldstrutil::format( "Character 0x%X already exists in MapRegion, add failed", toAdd->GetSerial() )); #endif } return false; } //o------------------------------------------------------------------------------------------------o //| Function - CMapHandler::RemoveChar() //| Date - 23 July, 2000 //o------------------------------------------------------------------------------------------------o //| Purpose - Removes toRemove from it's CURRENT SubRegion. Do this before adjusting the location //o------------------------------------------------------------------------------------------------o bool CMapHandler::RemoveChar( CChar *toRemove ) { if( !ValidateObject( toRemove )) return false; CMapRegion *cell = GetMapRegion( toRemove ); if( cell->GetRegionSerialList()->Remove( toRemove->GetSerial() )) { cell->GetCharList()->Remove( toRemove ); if( toRemove->ShouldSave() ) { cell->HasRegionChanged( true ); } } else { #if DEBUG_REGIONS Console.Warning( oldstrutil::format( "Character 0x%X does not exist in MapRegion, remove failed", toRemove->GetSerial() )); #endif return false; } return true; } //o------------------------------------------------------------------------------------------------o //| Function - CMapHandler::GetGridX() //| Date - 23 July, 2000 //o------------------------------------------------------------------------------------------------o //| Purpose - Find the Column of SubRegion we want based on location //o------------------------------------------------------------------------------------------------o SI16 CMapHandler::GetGridX( SI16 x ) { return static_cast( x / MapColSize ); } //o------------------------------------------------------------------------------------------------o //| Function - CMapHandler::GetGridY() //| Date - 23 July, 2000 //o------------------------------------------------------------------------------------------------o //| Purpose - Finds the Row of SubRegion we want based on location //o------------------------------------------------------------------------------------------------o SI16 CMapHandler::GetGridY( SI16 y ) { return static_cast( y / MapRowSize ); } //o------------------------------------------------------------------------------------------------o //| Function - CMapHandler::GetMapRegion() //| Date - 23 July, 2000 //o------------------------------------------------------------------------------------------------o //| Purpose - Returns the MapRegion based on the offsets //o------------------------------------------------------------------------------------------------o CMapRegion *CMapHandler::GetMapRegion( SI16 xOffset, SI16 yOffset, UI08 worldNumber ) { CMapRegion * mRegion = mapWorlds[worldNumber]->GetMapRegion( xOffset, yOffset ); if( mRegion == nullptr ) { mRegion = &overFlow; } return mRegion; } //o------------------------------------------------------------------------------------------------o //| Function - CMapHandler::GetMapRegion() //| Date - 23 July, 2000 //o------------------------------------------------------------------------------------------------o //| Purpose - Returns the subregion that x,y is in //o------------------------------------------------------------------------------------------------o CMapRegion *CMapHandler::GetMapRegion( CBaseObject *mObj ) { return GetMapRegion( GetGridX( mObj->GetX() ), GetGridY( mObj->GetY() ), mObj->WorldNumber() ); } //o------------------------------------------------------------------------------------------------o //| Function - CMapHandler::GetMapWorld() //| Date - 9/13/2005 //o------------------------------------------------------------------------------------------------o //| Purpose - Returns the MapWorld object associated with the worldNumber //o------------------------------------------------------------------------------------------------o CMapWorld * CMapHandler::GetMapWorld( UI08 worldNum ) { CMapWorld *mWorld = nullptr; if( worldNum > mapWorlds.size() ) { mWorld = mapWorlds[worldNum]; } return mWorld; } //o------------------------------------------------------------------------------------------------o //| Function - CMapHandler::GetMapWorld() //| Date - 9/13/2005 //o------------------------------------------------------------------------------------------------o //| Purpose - Returns the MapWorld object associated with the worldNumber //o------------------------------------------------------------------------------------------------o MapResource_st * CMapHandler::GetResource( SI16 x, SI16 y, UI08 worldNum ) { MapResource_st *resData = nullptr; if( worldNum < mapWorlds.size() ) { resData = &mapWorlds[worldNum]->GetResource( x, y ); } return resData; } //o------------------------------------------------------------------------------------------------o //| Function - CMapHandler::PopulateList() //o------------------------------------------------------------------------------------------------o //| Purpose - Creates a list of nearby MapRegions based on the object provided //o------------------------------------------------------------------------------------------------o auto CMapHandler::PopulateList( CBaseObject *mObj ) -> std::vector { return PopulateList( mObj->GetX(), mObj->GetY(), mObj->WorldNumber() ); } //o------------------------------------------------------------------------------------------------o //| Function - CMapHandler::PopulateList() //o------------------------------------------------------------------------------------------------o //| Purpose - Creates a list of nearby MapRegions based on the coordinates provided //o------------------------------------------------------------------------------------------------o auto CMapHandler::PopulateList( SI16 x, SI16 y, UI08 worldNumber ) -> std::vector { std::vector nearbyRegions; const SI16 xOffset = MapRegion->GetGridX( x ); const SI16 yOffset = MapRegion->GetGridY( y ); for( SI08 counter1 = -1; counter1 <= 1; ++counter1 ) { for( SI08 counter2 = -1; counter2 <= 1; ++counter2 ) { CMapRegion *MapArea = GetMapRegion( xOffset + counter1, yOffset + counter2, worldNumber ); if( MapArea == nullptr ) continue; nearbyRegions.push_back( MapArea ); } } return nearbyRegions; } //o------------------------------------------------------------------------------------------------o //| Function - CMapHandler::Save() //| Date - 23 July, 2000 //o------------------------------------------------------------------------------------------------o //| Purpose - Saves out data from MapRegions to worldfiles //o------------------------------------------------------------------------------------------------o constexpr size_t BUFFERSIZE = 1024 * 1024 ; static auto streamBuffer = std::vector(BUFFERSIZE,0) ; void CMapHandler::Save( void ) { const SI16 AreaX = UpperX / 8; // we're storing 8x8 grid arrays together const SI16 AreaY = UpperY / 8; std::ofstream writeDestination, houseDestination; SI32 onePercent = 0; UI08 i = 0; for( i = 0; i < Map->MapCount(); ++i ) { auto [mapWidth, mapHeight] = Map->SizeOfMap( i ); onePercent += static_cast(( mapWidth / MapColSize ) * ( mapHeight / MapRowSize )); } onePercent /= 100.0f; const char blockDiscriminator[] = "\n\n---REGION---\n\n"; UI32 count = 0; const TIMERVAL s_t = GetClock(); Console << "Saving Character and Item Map Region data... "; Console.TurnYellow(); Console << "0%"; std::string basePath = cwmWorldState->ServerData()->Directory( CSDDP_SHARED ); // Legacy - deletes house.wsc on next world save, since house data is now saved in the regional // wsc files along with other objects, so we don't want this file loaded again on next startup std::filesystem::path houseFilePath = basePath + "house.wsc"; std::filesystem::remove( houseFilePath ); std::string filename = ""; for( SI16 counter1 = 0; counter1 < AreaX; ++counter1 ) // move left->right { const SI32 baseX = counter1 * 8; for( SI16 counter2 = 0; counter2 < AreaY; ++counter2 ) // move up->down { const SI32 baseY = counter2 * 8; // calculate x grid offset filename = basePath + oldstrutil::number( counter1 ) + std::string( "." ) + oldstrutil::number( counter2 ) + std::string( ".wsc" ); // let's name our file bool changesDetected = false; for( UI08 xCnt = 0; !changesDetected && xCnt < 8; ++xCnt ) // walk through each part of the 8x8 grid, left->right { for( UI08 yCnt = 0; !changesDetected && yCnt < 8; ++yCnt ) // walk the row { for( WORLDLIST_ITERATOR mIter = mapWorlds.begin(); mIter != mapWorlds.end(); ++mIter ) { CMapRegion * mRegion = ( *mIter )->GetMapRegion(( baseX + xCnt ), ( baseY + yCnt )); if( mRegion != nullptr ) { // Only save objects mapRegions marked as "changed" by objects updated in said regions if( mRegion->HasRegionChanged() ) { changesDetected = true; break; } } } } } if( !changesDetected ) continue; writeDestination.open( filename.c_str(), std::ios::binary ); if( !writeDestination ) { Console.Error( oldstrutil::format( "Failed to open %s for writing", filename.c_str() )); continue; } writeDestination.rdbuf()->pubsetbuf( streamBuffer.data(), BUFFERSIZE ); for( UI08 xCnt = 0; xCnt < 8; ++xCnt ) // walk through each part of the 8x8 grid, left->right { for( UI08 yCnt = 0; yCnt < 8; ++yCnt ) // walk the row { for( WORLDLIST_ITERATOR mIter = mapWorlds.begin(); mIter != mapWorlds.end(); ++mIter ) { ++count; if( count%onePercent == 0 ) { if( count/onePercent <= 10 ) { Console << "\b\b" << std::to_string( count / onePercent ) << "%"; } else if( count/onePercent <= 100 ) { Console << "\b\b\b" << std::to_string( count / onePercent ) << "%"; } } CMapRegion * mRegion = ( *mIter )->GetMapRegion(( baseX + xCnt ), ( baseY + yCnt )); if( mRegion != nullptr ) { mRegion->SaveToDisk( writeDestination ); // Remove "changed" flag from region, to avoid it saving again needlessly on next save mRegion->HasRegionChanged( false ); } writeDestination << blockDiscriminator; } } } writeDestination.close(); } } Console << "\b\b\b\b" << static_cast( 100 ) << "%"; filename = basePath + "overflow.wsc"; writeDestination.open( filename.c_str() ); if( writeDestination.is_open() ) { overFlow.SaveToDisk( writeDestination ); writeDestination.close(); } else { Console.Error( oldstrutil::format( "Failed to open %s for writing", filename.c_str() )); return; } Console << "\b\b\b\b"; Console.PrintDone(); const TIMERVAL e_t = GetClock(); Console.Print( oldstrutil::format( "World saved in %.02fsec\n", ( static_cast( e_t - s_t )) / 1000.0f )); i = 0; for( WORLDLIST_ITERATOR wIter = mapWorlds.begin(); wIter != mapWorlds.end(); ++wIter ) { ( *wIter )->SaveResources( i ); ++i; } } bool PostLoadFunctor( CBaseObject *a, [[maybe_unused]] UI32 &b, [[maybe_unused]] void *extraData ) { if( ValidateObject( a )) { if( !a->IsFree() ) { a->PostLoadProcessing(); } } return true; } //o------------------------------------------------------------------------------------------------o //| Function - CMapHandler::Load() //| Date - 23 July, 2000 //o------------------------------------------------------------------------------------------------o //| Purpose - Loads in worldfile data to MapRegions //o------------------------------------------------------------------------------------------------o void CMapHandler::Load( void ) { const SI16 AreaX = UpperX / 8; // we're storing 8x8 grid arrays together const SI16 AreaY = UpperY / 8; UI32 count = 0; std::ifstream readDestination; Console.TurnYellow(); Console << "0%"; TIMERVAL s_t = GetClock(); std::string basePath = cwmWorldState->ServerData()->Directory( CSDDP_SHARED ); std::string filename; UI32 runningCount = 0; SI32 fileSizes[AreaX][AreaY]; for( SI16 cx = 0; cx < AreaX; ++cx ) { for( SI16 cy = 0; cy < AreaY; ++cy ) { filename = basePath + oldstrutil::number( cx ) + std::string( "." ) + oldstrutil::number( cy ) + std::string( ".wsc" ); // let's name our file fileSizes[cx][cy] = FileSize( filename ); runningCount += fileSizes[cx][cy]; } } if( runningCount == 0 ) { runningCount = 1; } SI32 runningDone = 0; for( SI16 counter1 = 0; counter1 < AreaX; ++counter1 ) // move left->right { for( SI16 counter2 = 0; counter2 < AreaY; ++counter2 ) // move up->down { filename = basePath + oldstrutil::number( counter1 ) + std::string( "." ) + oldstrutil::number( counter2 ) + std::string( ".wsc" ); // let's name our file readDestination.open( filename.c_str() ); // let's open it readDestination.seekg( 0, std::ios::beg ); if( readDestination.eof() || readDestination.fail() ) { readDestination.close(); readDestination.clear(); continue; } ++count; LoadFromDisk( readDestination, runningDone, fileSizes[counter1][counter2], runningCount ); runningDone += fileSizes[counter1][counter2]; float tempVal = static_cast( runningDone ) / static_cast( runningCount ) * 100.0f; if( tempVal <= 10 ) { Console << "\b\b" << static_cast( tempVal ) << "%"; } else if( tempVal <= 100 ) { Console << "\b\b\b" << static_cast( tempVal ) << "%"; } readDestination.close(); readDestination.clear(); } } // If runningDone is still 0, there was nothing to load! 100% it if( runningDone == 0 ) { Console << "\b\b" << static_cast( 100 ) << "%"; } Console.TurnNormal(); Console << "\b\b\b"; Console.PrintDone(); filename = basePath + "overflow.wsc"; std::ifstream flowDestination( filename.c_str() ); LoadFromDisk( flowDestination, -1, -1, -1 ); flowDestination.close(); filename = basePath + "house.wsc"; std::ifstream houseDestination( filename.c_str() ); LoadFromDisk( houseDestination, -1, -1, -1 ); UI32 b = 0; ObjectFactory::GetSingleton().IterateOver( OT_MULTI, b, nullptr, &PostLoadFunctor ); ObjectFactory::GetSingleton().IterateOver( OT_ITEM, b, nullptr, &PostLoadFunctor ); ObjectFactory::GetSingleton().IterateOver( OT_CHAR, b, nullptr, &PostLoadFunctor ); houseDestination.close(); TIMERVAL e_t = GetClock(); Console.Print( oldstrutil::format( "ASCII world loaded in %.02fsec\n", ( static_cast( e_t - s_t )) / 1000.0f )); UI08 i = 0; for( WORLDLIST_ITERATOR wIter = mapWorlds.begin(); wIter != mapWorlds.end(); ++wIter ) { ( *wIter )->LoadResources( i ); ++i; } } //o------------------------------------------------------------------------------------------------o //| Function - CMapHandler::LoadFromDisk() //| Date - 23 July, 2000 //o------------------------------------------------------------------------------------------------o //| Purpose - Loads in objects from specified file //o------------------------------------------------------------------------------------------------o void CMapHandler::LoadFromDisk( std::istream& readDestination, SI32 baseValue, SI32 fileSize, UI32 maxSize ) { char line[1024]; R32 basePercent = static_cast( baseValue ) / static_cast( maxSize ) * 100.0f; R32 targPercent = static_cast( baseValue + fileSize ) / static_cast( maxSize ) * 100.0f; R32 diffValue = targPercent - basePercent; SI32 updateCount = 0; while( !readDestination.eof() && !readDestination.fail() ) { readDestination.getline( line, 1023 ); line[readDestination.gcount()] = 0; std::string sLine( line ); sLine = oldstrutil::trim( sLine ); if( sLine.substr( 0, 1 ) == "[" ) // in a section { sLine = sLine.substr( 1, sLine.size() - 2 ); sLine = oldstrutil::upper( oldstrutil::trim( sLine )); if( sLine == "CHARACTER" ) { LoadChar( readDestination ); } else if( sLine == "ITEM" ) { LoadItem( readDestination ); } else if( sLine == "HOUSE" ) { LoadMulti( readDestination ); } else if( sLine == "BOAT" ) { LoadBoat( readDestination ); } else if( sLine == "SPAWNITEM" ) { LoadSpawnItem( readDestination ); } if( fileSize != -1 && ( ++updateCount ) % 200 == 0 ) { R32 curPos = readDestination.tellg(); R32 tempVal = basePercent + ( curPos / fileSize * diffValue ); if( tempVal <= 10 ) { Console << "\b\b" << static_cast( tempVal ) << "%"; } else { Console << "\b\b\b" << static_cast( tempVal ) << "%"; } } } else if( sLine == "---REGION---" ) // end of region continue; } }