the game where you go into mines and start crafting! but for consoles (forked directly from smartcmd's github)
at main 467 lines 13 kB view raw
1#include "stdafx.h" 2#include "net.minecraft.world.level.h" 3#include "ConsoleSaveFileIO.h" 4#include "LevelData.h" 5#include "McRegionChunkStorage.h" 6 7CRITICAL_SECTION McRegionChunkStorage::cs_memory; 8 9std::deque<DataOutputStream *> McRegionChunkStorage::s_chunkDataQueue; 10int McRegionChunkStorage::s_runningThreadCount = 0; 11C4JThread *McRegionChunkStorage::s_saveThreads[3]; 12 13 14McRegionChunkStorage::McRegionChunkStorage(ConsoleSaveFile *saveFile, const wstring &prefix) : m_prefix( prefix ) 15{ 16 m_saveFile = saveFile; 17 18 // Make sure that if there are any files for regions to be created, that they are created in the order that suits us for making the initial level save work fast 19 if( prefix == L"" ) 20 { 21 m_saveFile->createFile(ConsoleSavePath(L"DIM-1r.-1.-1.mcr")); 22 m_saveFile->createFile(ConsoleSavePath(L"DIM-1r.0.-1.mcr")); 23 m_saveFile->createFile(ConsoleSavePath(L"DIM-1r.0.0.mcr")); 24 m_saveFile->createFile(ConsoleSavePath(L"DIM-1r.-1.0.mcr")); 25 m_saveFile->createFile(ConsoleSavePath(L"DIM1/r.-1.-1.mcr")); 26 m_saveFile->createFile(ConsoleSavePath(L"DIM1/r.0.-1.mcr")); 27 m_saveFile->createFile(ConsoleSavePath(L"DIM1/r.0.0.mcr")); 28 m_saveFile->createFile(ConsoleSavePath(L"DIM1/r.-1.0.mcr")); 29 m_saveFile->createFile(ConsoleSavePath(L"r.-1.-1.mcr")); 30 m_saveFile->createFile(ConsoleSavePath(L"r.0.-1.mcr")); 31 m_saveFile->createFile(ConsoleSavePath(L"r.0.0.mcr")); 32 m_saveFile->createFile(ConsoleSavePath(L"r.-1.0.mcr")); 33 } 34 35 36#ifdef SPLIT_SAVES 37 ConsoleSavePath currentFile = ConsoleSavePath( m_prefix + wstring( L"entities.dat" ) ); 38 39 if(m_saveFile->doesFileExist(currentFile)) 40 { 41 ConsoleSaveFileInputStream fis = ConsoleSaveFileInputStream( m_saveFile, currentFile ); 42 DataInputStream dis(&fis); 43 44 int count = dis.readInt(); 45 46 for(int i = 0; i < count; ++i) 47 { 48 __int64 index = dis.readLong(); 49 CompoundTag *tag = NbtIo::read(&dis); 50 51 ByteArrayOutputStream bos; 52 DataOutputStream dos(&bos); 53 NbtIo::write(tag, &dos); 54 delete tag; 55 56 byteArray savedData(bos.size()); 57 memcpy(savedData.data, bos.buf.data, bos.size()); 58 59 m_entityData[index] = savedData; 60 } 61 } 62#endif 63} 64 65McRegionChunkStorage::~McRegionChunkStorage() 66{ 67 for(AUTO_VAR(it,m_entityData.begin()); it != m_entityData.end(); ++it) 68 { 69 delete it->second.data; 70 } 71} 72 73LevelChunk *McRegionChunkStorage::load(Level *level, int x, int z) 74{ 75 DataInputStream *regionChunkInputStream = RegionFileCache::getChunkDataInputStream(m_saveFile, m_prefix, x, z); 76 77#ifdef SPLIT_SAVES 78 // If we can't find the chunk in the save file, then we should remove any entities we might have for that chunk 79 if(regionChunkInputStream == NULL) 80 { 81 __int64 index = ((__int64)(x) << 32) | (((__int64)(z))&0x00000000FFFFFFFF); 82 83 AUTO_VAR(it, m_entityData.find(index)); 84 if(it != m_entityData.end()) 85 { 86 delete it->second.data; 87 m_entityData.erase(it); 88 } 89 } 90#endif 91 92 LevelChunk *levelChunk = NULL; 93 94 if(m_saveFile->getOriginalSaveVersion() >= SAVE_FILE_VERSION_COMPRESSED_CHUNK_STORAGE) 95 { 96 if (regionChunkInputStream != NULL) 97 { 98 MemSect(9); 99 levelChunk = OldChunkStorage::load(level, regionChunkInputStream); 100 loadEntities(level, levelChunk); 101 MemSect(0); 102 regionChunkInputStream->deleteChildStream(); 103 delete regionChunkInputStream; 104 } 105 } 106 else 107 { 108 CompoundTag *chunkData; 109 if (regionChunkInputStream != NULL) 110 { 111 MemSect(8); 112 chunkData = NbtIo::read((DataInput *)regionChunkInputStream); 113 MemSect(0); 114 } else 115 { 116 return NULL; 117 } 118 119 regionChunkInputStream->deleteChildStream(); 120 delete regionChunkInputStream; 121 122 if (!chunkData->contains(L"Level")) 123 { 124 char buf[256]; 125 sprintf(buf,"Chunk file at %d, %d is missing level data, skipping\n",x, z); 126 app.DebugPrintf(buf); 127 delete chunkData; 128 return NULL; 129 } 130 if (!chunkData->getCompound(L"Level")->contains(L"Blocks")) 131 { 132 char buf[256]; 133 sprintf(buf,"Chunk file at %d, %d is missing block data, skipping\n",x, z); 134 app.DebugPrintf(buf); 135 delete chunkData; 136 return NULL; 137 } 138 MemSect(9); 139 levelChunk = OldChunkStorage::load(level, chunkData->getCompound(L"Level")); 140 MemSect(0); 141 if (!levelChunk->isAt(x, z)) 142 { 143 char buf[256]; 144 sprintf(buf,"Chunk file at %d, %d is in the wrong location; relocating. Expected %d, %d, got %d, %d\n", 145 x, z, x, z, levelChunk->x, levelChunk->z); 146 app.DebugPrintf(buf); 147 delete levelChunk; 148 delete chunkData; 149 return NULL; 150 151 // 4J Stu - We delete the data within OldChunkStorage::load, so we can never reload from it 152 //chunkData->putInt(L"xPos", x); 153 //chunkData->putInt(L"zPos", z); 154 //MemSect(10); 155 //levelChunk = OldChunkStorage::load(level, chunkData->getCompound(L"Level")); 156 //MemSect(0); 157 } 158#ifdef SPLIT_SAVES 159 loadEntities(level, levelChunk); 160#endif 161 delete chunkData; 162 } 163#ifndef _CONTENT_PACKAGE 164 if(levelChunk && app.DebugSettingsOn() && app.GetGameSettingsDebugMask(ProfileManager.GetPrimaryPad())&(1L<<eDebugSetting_EnableBiomeOverride)) 165 { 166 // 4J Stu - This will force an update of the chunk's biome array 167 levelChunk->reloadBiomes(); 168 } 169#endif 170 return levelChunk; 171 172} 173 174void McRegionChunkStorage::save(Level *level, LevelChunk *levelChunk) 175{ 176 level->checkSession(); 177 178 // 4J - removed try/catch 179// try { 180 181 // Note - have added use of a critical section round sections of code that do a lot of memory alloc/free operations. This is because 182 // when we are running saves on multiple threads these sections have a lot of contention and thrash the memory system's critical sections 183 // Better to let each thread have its turn at a higher level of granularity. 184 MemSect(30); 185 PIXBeginNamedEvent(0,"Getting output stream\n"); 186 DataOutputStream *output = RegionFileCache::getChunkDataOutputStream(m_saveFile, m_prefix, levelChunk->x, levelChunk->z); 187 PIXEndNamedEvent(); 188 189 if(m_saveFile->getOriginalSaveVersion() >= SAVE_FILE_VERSION_COMPRESSED_CHUNK_STORAGE) 190 { 191 PIXBeginNamedEvent(0,"Writing chunk data"); 192 OldChunkStorage::save(levelChunk, level, output); 193 PIXEndNamedEvent(); 194 195 PIXBeginNamedEvent(0,"Updating chunk queue"); 196 EnterCriticalSection(&cs_memory); 197 s_chunkDataQueue.push_back(output); 198 LeaveCriticalSection(&cs_memory); 199 PIXEndNamedEvent(); 200 } 201 else 202 { 203 EnterCriticalSection(&cs_memory); 204 PIXBeginNamedEvent(0,"Creating tags\n"); 205 CompoundTag *tag = new CompoundTag(); 206 CompoundTag *levelData = new CompoundTag(); 207 tag->put(L"Level", levelData); 208 OldChunkStorage::save(levelChunk, level, levelData); 209 PIXEndNamedEvent(); 210 PIXBeginNamedEvent(0,"NbtIo writing\n"); 211 NbtIo::write(tag, output); 212 PIXEndNamedEvent(); 213 LeaveCriticalSection(&cs_memory); 214 PIXBeginNamedEvent(0,"Output closing\n"); 215 output->close(); 216 PIXEndNamedEvent(); 217 218 219 // 4J Stu - getChunkDataOutputStream makes a new DataOutputStream that points to a new ChunkBuffer( ByteArrayOutputStream ) 220 // We should clean these up when we are done 221 EnterCriticalSection(&cs_memory); 222 PIXBeginNamedEvent(0,"Cleaning up\n"); 223 output->deleteChildStream(); 224 delete output; 225 delete tag; 226 LeaveCriticalSection(&cs_memory); 227 PIXEndNamedEvent(); 228 } 229 MemSect(0); 230 231 LevelData *levelInfo = level->getLevelData(); 232 233 // 4J Stu - Override this with our save file size to stop all the RegionFileCache lookups 234 //levelInfo->setSizeOnDisk(levelInfo->getSizeOnDisk() + RegionFileCache::getSizeDelta(m_saveFile, m_prefix, levelChunk->x, levelChunk->z)); 235 levelInfo->setSizeOnDisk( this->m_saveFile->getSizeOnDisk() ); 236// } catch (Exception e) { 237// e.printStackTrace(); 238// } 239} 240 241void McRegionChunkStorage::saveEntities(Level *level, LevelChunk *levelChunk) 242{ 243#ifdef SPLIT_SAVES 244 PIXBeginNamedEvent(0,"Saving entities"); 245 __int64 index = ((__int64)(levelChunk->x) << 32) | (((__int64)(levelChunk->z))&0x00000000FFFFFFFF); 246 247 delete m_entityData[index].data; 248 249 CompoundTag *newTag = new CompoundTag(); 250 bool savedEntities = OldChunkStorage::saveEntities(levelChunk, level, newTag); 251 252 if(savedEntities) 253 { 254 ByteArrayOutputStream bos; 255 DataOutputStream dos(&bos); 256 NbtIo::write(newTag, &dos); 257 258 byteArray savedData(bos.size()); 259 memcpy(savedData.data, bos.buf.data, bos.size()); 260 261 m_entityData[index] = savedData; 262 } 263 else 264 { 265 AUTO_VAR(it, m_entityData.find(index)); 266 if(it != m_entityData.end()) 267 { 268 m_entityData.erase(it); 269 } 270 } 271 delete newTag; 272 PIXEndNamedEvent(); 273#endif 274} 275 276void McRegionChunkStorage::loadEntities(Level *level, LevelChunk *levelChunk) 277{ 278#ifdef SPLIT_SAVES 279 __int64 index = ((__int64)(levelChunk->x) << 32) | (((__int64)(levelChunk->z))&0x00000000FFFFFFFF); 280 281 AUTO_VAR(it, m_entityData.find(index)); 282 if(it != m_entityData.end()) 283 { 284 ByteArrayInputStream bais(it->second); 285 DataInputStream dis(&bais); 286 CompoundTag *tag = NbtIo::read(&dis); 287 OldChunkStorage::loadEntities(levelChunk, level, tag); 288 bais.reset(); 289 delete tag; 290 } 291#endif 292} 293 294void McRegionChunkStorage::tick() 295{ 296 m_saveFile->tick(); 297} 298 299void McRegionChunkStorage::flush() 300{ 301#ifdef SPLIT_SAVES 302 PIXBeginNamedEvent(0, "Flushing entity data"); 303 ConsoleSavePath currentFile = ConsoleSavePath( m_prefix + wstring( L"entities.dat" ) ); 304 ConsoleSaveFileOutputStream fos = ConsoleSaveFileOutputStream( m_saveFile, currentFile ); 305 BufferedOutputStream bos(&fos, 1024*1024); 306 DataOutputStream dos(&bos); 307 308 PIXBeginNamedEvent(0,"Writing to stream"); 309 dos.writeInt(m_entityData.size()); 310 311 for(AUTO_VAR(it,m_entityData.begin()); it != m_entityData.end(); ++it) 312 { 313 dos.writeLong(it->first); 314 dos.write(it->second,0,it->second.length); 315 } 316 bos.flush(); 317 PIXEndNamedEvent(); 318 PIXEndNamedEvent(); 319#endif 320} 321 322 323void McRegionChunkStorage::staticCtor() 324{ 325 InitializeCriticalSectionAndSpinCount(&cs_memory,5120); 326 327 for(unsigned int i = 0; i < 3; ++i) 328 { 329 char threadName[256]; 330 sprintf(threadName,"McRegion Save thread %d\n",i); 331 SetThreadName(0, threadName); 332 333 //saveThreads[j] = CreateThread(NULL,0,runSaveThreadProc,&threadData[j],CREATE_SUSPENDED,&threadId[j]); 334 s_saveThreads[i] = new C4JThread(runSaveThreadProc,NULL,threadName); 335 336 337 //app.DebugPrintf("Created new thread: %s\n",threadName); 338 339 // Threads 1,3 and 5 are generally idle so use them 340 if(i == 0) s_saveThreads[i]->SetProcessor(CPU_CORE_SAVE_THREAD_A); 341 else if(i == 1) 342 { 343 s_saveThreads[i]->SetProcessor(CPU_CORE_SAVE_THREAD_B); 344#ifdef __ORBIS__ 345 s_saveThreads[i]->SetPriority(THREAD_PRIORITY_BELOW_NORMAL); // On Orbis, this core is also used for Matching 2, and that priority of that seems to be always at default no matter what we set it to. Prioritise this below Matching 2. 346#endif 347 } 348 else if(i == 2) s_saveThreads[i]->SetProcessor(CPU_CORE_SAVE_THREAD_C); 349 350 //ResumeThread( saveThreads[j] ); 351 s_saveThreads[i]->Run(); 352 } 353} 354 355int McRegionChunkStorage::runSaveThreadProc(LPVOID lpParam) 356{ 357 Compression::CreateNewThreadStorage(); 358 359 bool running = true; 360 size_t lastQueueSize = 0; 361 362 DataOutputStream *dos = NULL; 363 while(running) 364 { 365 if( TryEnterCriticalSection(&cs_memory) ) 366 { 367 lastQueueSize = s_chunkDataQueue.size(); 368 if(lastQueueSize > 0) 369 { 370 dos = s_chunkDataQueue.front(); 371 s_chunkDataQueue.pop_front(); 372 } 373 s_runningThreadCount++; 374 LeaveCriticalSection(&cs_memory); 375 376 if(dos) 377 { 378 PIXBeginNamedEvent(0,"Saving chunk"); 379 //app.DebugPrintf("Compressing chunk data (%d left)\n", lastQueueSize - 1); 380 dos->close(); 381 dos->deleteChildStream(); 382 PIXEndNamedEvent(); 383 } 384 delete dos; 385 dos = NULL; 386 387 EnterCriticalSection(&cs_memory); 388 s_runningThreadCount--; 389 LeaveCriticalSection(&cs_memory); 390 } 391 392 // If there was more than one thing in the queue last time we checked, then we want to spin round again soon 393 // Otherwise wait a bit longer 394 if( (lastQueueSize -1) > 0) Sleep(1); // Sleep 1 to yield 395 else Sleep(100); 396 } 397 398 Compression::ReleaseThreadStorage(); 399 400 return 0; 401} 402 403void McRegionChunkStorage::WaitForAll() 404{ 405 WaitForAllSaves(); 406} 407 408void McRegionChunkStorage::WaitIfTooManyQueuedChunks() 409{ 410 WaitForSaves(); 411} 412 413// Static 414void McRegionChunkStorage::WaitForAllSaves() 415{ 416 // Wait for there to be no more tasks to be processed... 417 EnterCriticalSection(&cs_memory); 418 size_t queueSize = s_chunkDataQueue.size(); 419 LeaveCriticalSection(&cs_memory); 420 421 while(queueSize > 0) 422 { 423 Sleep(10); 424 425 EnterCriticalSection(&cs_memory); 426 queueSize = s_chunkDataQueue.size(); 427 LeaveCriticalSection(&cs_memory); 428 } 429 430 // And then wait for there to be no running threads that are processing these tasks 431 EnterCriticalSection(&cs_memory); 432 int runningThreadCount = s_runningThreadCount; 433 LeaveCriticalSection(&cs_memory); 434 435 while(runningThreadCount > 0) 436 { 437 Sleep(10); 438 439 EnterCriticalSection(&cs_memory); 440 runningThreadCount = s_runningThreadCount; 441 LeaveCriticalSection(&cs_memory); 442 } 443} 444 445// Static 446void McRegionChunkStorage::WaitForSaves() 447{ 448 static const int MAX_QUEUE_SIZE = 12; 449 static const int DESIRED_QUEUE_SIZE = 6; 450 451 // Wait for the queue to reduce to a level where we should add more elements 452 EnterCriticalSection(&cs_memory); 453 size_t queueSize = s_chunkDataQueue.size(); 454 LeaveCriticalSection(&cs_memory); 455 456 if( queueSize > MAX_QUEUE_SIZE ) 457 { 458 while( queueSize > DESIRED_QUEUE_SIZE ) 459 { 460 Sleep(10); 461 462 EnterCriticalSection(&cs_memory); 463 queueSize = s_chunkDataQueue.size(); 464 LeaveCriticalSection(&cs_memory); 465 } 466 } 467}