the game where you go into mines and start crafting! but for consoles (forked directly from smartcmd's github)
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}