|
12 | 12 | #include "..\Minecraft.Client\Common\GameRules\LevelGenerationOptions.h" |
13 | 13 | #include "..\Minecraft.World\net.minecraft.world.level.chunk.storage.h" |
14 | 14 |
|
| 15 | +#ifdef MINECRAFT_SERVER_BUILD |
| 16 | +#include <thread> |
| 17 | +#include <atomic> |
| 18 | +#include <mutex> |
| 19 | + |
| 20 | +static std::atomic<bool> s_bgSaveActive{false}; |
| 21 | +static std::mutex s_bgSaveMutex; |
| 22 | + |
| 23 | +struct BackgroundSaveResult |
| 24 | +{ |
| 25 | + ConsoleSaveFile *owner = nullptr; |
| 26 | + PBYTE thumbData = nullptr; |
| 27 | + DWORD thumbSize = 0; |
| 28 | + BYTE textMeta[88] = {}; |
| 29 | + int textMetaBytes = 0; |
| 30 | + bool pending = false; |
| 31 | +}; |
| 32 | +static BackgroundSaveResult s_bgResult; |
| 33 | +#endif |
| 34 | + |
15 | 35 |
|
16 | 36 | #ifdef _XBOX |
17 | 37 | #define RESERVE_ALLOCATION MEM_RESERVE | MEM_LARGE_PAGES |
@@ -673,6 +693,83 @@ void ConsoleSaveFileOriginal::Flush(bool autosave, bool updateThumbnail ) |
673 | 693 |
|
674 | 694 | unsigned int fileSize = header.GetFileSize(); |
675 | 695 |
|
| 696 | +#ifdef MINECRAFT_SERVER_BUILD |
| 697 | + // on the server we dont want to block the tick thread doing compression!!! |
| 698 | + // sna[pshot pvSaveMem while we still hold the lock then hand it off to a background thread |
| 699 | + byte *snap = new (std::nothrow) byte[fileSize]; |
| 700 | + if (snap) |
| 701 | + { |
| 702 | + // copy the save buffer while we still own the lock so nothing can write to it mid-copy |
| 703 | + QueryPerformanceCounter(&qwTime); |
| 704 | + memcpy(snap, pvSaveMem, fileSize); |
| 705 | + QueryPerformanceCounter(&qwNewTime); |
| 706 | + app.DebugPrintf("snapshot %u bytes in %.3f sec\n", fileSize, |
| 707 | + (qwNewTime.QuadPart - qwTime.QuadPart) * fSecsPerTick); |
| 708 | + |
| 709 | + PBYTE thumb = nullptr; |
| 710 | + DWORD thumbSz = 0; |
| 711 | + app.GetSaveThumbnail(&thumb, &thumbSz); |
| 712 | + |
| 713 | + BYTE meta[88]; |
| 714 | + ZeroMemory(meta, 88); |
| 715 | + int64_t seed = 0; |
| 716 | + bool hasSeed = false; |
| 717 | + if (MinecraftServer *sv = MinecraftServer::getInstance(); sv && sv->levels[0]) |
| 718 | + { |
| 719 | + seed = sv->levels[0]->getLevelData()->getSeed(); |
| 720 | + hasSeed = true; |
| 721 | + } |
| 722 | + int metaLen = app.CreateImageTextData(meta, seed, hasSeed, |
| 723 | + app.GetGameHostOption(eGameHostOption_All), Minecraft::GetInstance()->getCurrentTexturePackId()); |
| 724 | + |
| 725 | + // telemetry |
| 726 | + INT uid = 0; |
| 727 | + StorageManager.GetSaveUniqueNumber(&uid); |
| 728 | + TelemetryManager->RecordLevelSaveOrCheckpoint(ProfileManager.GetPrimaryPad(), uid, fileSize); |
| 729 | + |
| 730 | + ReleaseSaveAccess(); |
| 731 | + s_bgSaveActive.store(true, std::memory_order_release); |
| 732 | + |
| 733 | + std::thread([snap, fileSize, thumb, thumbSz, meta, metaLen, this]() { |
| 734 | + unsigned int compLen = fileSize + 8; |
| 735 | + byte *buf = static_cast<byte *>(StorageManager.AllocateSaveData(compLen)); |
| 736 | + if (!buf) |
| 737 | + { |
| 738 | + // FAIL!! attempt precalc |
| 739 | + compLen = 0; |
| 740 | + Compression::getCompression()->Compress(nullptr, &compLen, snap, fileSize); |
| 741 | + compLen += 8; |
| 742 | + buf = static_cast<byte *>(StorageManager.AllocateSaveData(compLen)); |
| 743 | + } |
| 744 | + if (buf) |
| 745 | + { |
| 746 | + // COM,PRESS |
| 747 | + Compression::getCompression()->Compress(buf + 8, &compLen, snap, fileSize); |
| 748 | + ZeroMemory(buf, 8); |
| 749 | + memcpy(buf + 4, &fileSize, sizeof(fileSize)); |
| 750 | + |
| 751 | + // store the result so flushPendingBackgroundSave() can pick it up on the main thread next tick |
| 752 | + // StorageManager isnt thread safe so we cant call SetSaveImages or SaveSaveData from here. Bwoomp |
| 753 | + std::lock_guard<std::mutex> lk(s_bgSaveMutex); |
| 754 | + s_bgResult.owner = this; |
| 755 | + s_bgResult.thumbData = thumb; |
| 756 | + s_bgResult.thumbSize = thumbSz; |
| 757 | + memcpy(s_bgResult.textMeta, meta, sizeof(meta)); |
| 758 | + s_bgResult.textMetaBytes = metaLen; |
| 759 | + s_bgResult.pending = true; |
| 760 | + } |
| 761 | + else |
| 762 | + { |
| 763 | + app.DebugPrintf("save buf alloc failed\n"); |
| 764 | + s_bgSaveActive.store(false, std::memory_order_release); |
| 765 | + } |
| 766 | + delete[] snap; |
| 767 | + }).detach(); |
| 768 | + return; |
| 769 | + } |
| 770 | + app.DebugPrintf("snapshot alloc failed (%u bytes)\n", fileSize); |
| 771 | +#endif |
| 772 | + |
676 | 773 | // Assume that the compression will make it smaller so initially attempt to allocate the current file size |
677 | 774 | // We add 4 bytes to the start so that we can signal compressed data |
678 | 775 | // And another 4 bytes to store the decompressed data size |
@@ -838,6 +935,10 @@ int ConsoleSaveFileOriginal::SaveSaveDataCallback(LPVOID lpParam,bool bRes) |
838 | 935 | { |
839 | 936 | ConsoleSaveFile *pClass=static_cast<ConsoleSaveFile *>(lpParam); |
840 | 937 |
|
| 938 | +#ifdef MINECRAFT_SERVER_BUILD |
| 939 | + s_bgSaveActive.store(false, std::memory_order_release); |
| 940 | +#endif |
| 941 | + |
841 | 942 | return 0; |
842 | 943 | } |
843 | 944 |
|
@@ -1085,3 +1186,25 @@ void *ConsoleSaveFileOriginal::getWritePointer(FileEntry *file) |
1085 | 1186 | { |
1086 | 1187 | return static_cast<char *>(pvSaveMem) + file->currentFilePointer;; |
1087 | 1188 | } |
| 1189 | + |
| 1190 | +#ifdef MINECRAFT_SERVER_BUILD |
| 1191 | +void ConsoleSaveFileOriginal::flushPendingBackgroundSave() |
| 1192 | +{ |
| 1193 | + std::lock_guard<std::mutex> lk(s_bgSaveMutex); |
| 1194 | + if (!s_bgResult.pending) |
| 1195 | + return; |
| 1196 | + |
| 1197 | + StorageManager.SetSaveImages( |
| 1198 | + s_bgResult.thumbData, s_bgResult.thumbSize, |
| 1199 | + nullptr, 0, s_bgResult.textMeta, s_bgResult.textMetaBytes); |
| 1200 | + StorageManager.SaveSaveData(&ConsoleSaveFileOriginal::SaveSaveDataCallback, s_bgResult.owner); |
| 1201 | + |
| 1202 | + s_bgResult.pending = false; |
| 1203 | + // the actual write isnt done until SaveSaveDataCallback fires |
| 1204 | +} |
| 1205 | + |
| 1206 | +bool ConsoleSaveFileOriginal::hasPendingBackgroundSave() |
| 1207 | +{ |
| 1208 | + return s_bgSaveActive.load(std::memory_order_acquire); |
| 1209 | +} |
| 1210 | +#endif |
0 commit comments