mirror of
https://github.com/4jcraft/4jcraft.git
synced 2026-04-24 16:24:11 +00:00
# Conflicts: # Minecraft.Client/Network/PlayerChunkMap.cpp # Minecraft.Client/Network/PlayerList.cpp # Minecraft.Client/Network/ServerChunkCache.cpp # Minecraft.Client/Platform/Common/Consoles_App.cpp # Minecraft.Client/Platform/Common/DLC/DLCManager.cpp # Minecraft.Client/Platform/Common/GameRules/LevelGenerationOptions.cpp # Minecraft.Client/Platform/Common/GameRules/LevelRuleset.cpp # Minecraft.Client/Platform/Common/Tutorial/Tutorial.cpp # Minecraft.Client/Platform/Common/Tutorial/TutorialTask.cpp # Minecraft.Client/Platform/Common/UI/IUIScene_CreativeMenu.cpp # Minecraft.Client/Platform/Common/UI/UIComponent_Panorama.cpp # Minecraft.Client/Platform/Common/UI/UIController.cpp # Minecraft.Client/Platform/Common/UI/UIController.h # Minecraft.Client/Platform/Extrax64Stubs.cpp # Minecraft.Client/Platform/Windows64/4JLibs/inc/4J_Input.h # Minecraft.Client/Platform/Windows64/4JLibs/inc/4J_Storage.h # Minecraft.Client/Player/EntityTracker.cpp # Minecraft.Client/Player/ServerPlayer.cpp # Minecraft.Client/Rendering/EntityRenderers/PlayerRenderer.cpp # Minecraft.Client/Textures/Packs/DLCTexturePack.cpp # Minecraft.Client/Textures/Stitching/StitchedTexture.cpp # Minecraft.Client/Textures/Stitching/TextureMap.cpp # Minecraft.Client/Textures/Textures.cpp # Minecraft.World/Blocks/NotGateTile.cpp # Minecraft.World/Blocks/PressurePlateTile.cpp # Minecraft.World/Blocks/TileEntities/PotionBrewing.cpp # Minecraft.World/Enchantments/EnchantmentHelper.cpp # Minecraft.World/Entities/HangingEntity.cpp # Minecraft.World/Entities/LeashFenceKnotEntity.cpp # Minecraft.World/Entities/LivingEntity.cpp # Minecraft.World/Entities/Mobs/Boat.cpp # Minecraft.World/Entities/Mobs/Minecart.cpp # Minecraft.World/Entities/Mobs/Witch.cpp # Minecraft.World/Entities/SyncedEntityData.cpp # Minecraft.World/Items/LeashItem.cpp # Minecraft.World/Items/PotionItem.cpp # Minecraft.World/Level/BaseMobSpawner.cpp # Minecraft.World/Level/CustomLevelSource.cpp # Minecraft.World/Level/Level.cpp # Minecraft.World/Level/Storage/DirectoryLevelStorage.cpp # Minecraft.World/Level/Storage/McRegionLevelStorage.cpp # Minecraft.World/Level/Storage/RegionFileCache.cpp # Minecraft.World/Player/Player.cpp # Minecraft.World/WorldGen/Biomes/BiomeCache.cpp # Minecraft.World/WorldGen/Features/RandomScatteredLargeFeature.cpp # Minecraft.World/WorldGen/Layers/BiomeOverrideLayer.cpp
750 lines
26 KiB
C++
750 lines
26 KiB
C++
#include "../../Platform/stdafx.h"
|
|
#include "../../Platform/System.h"
|
|
#include "../../Headers/net.minecraft.world.entity.player.h"
|
|
#include "../../Headers/net.minecraft.world.level.h"
|
|
#include "../../Headers/net.minecraft.world.level.chunk.storage.h"
|
|
#include "../../Headers/net.minecraft.world.level.dimension.h"
|
|
#include "../../Headers/com.mojang.nbt.h"
|
|
#include "../../IO/Files/File.h"
|
|
#include "../../IO/Streams/DataInputStream.h"
|
|
#include "../../IO/Files/FileInputStream.h"
|
|
#include "../LevelData.h"
|
|
#include "DirectoryLevelStorage.h"
|
|
#include "../../IO/Files/ConsoleSaveFileIO.h"
|
|
|
|
const std::wstring DirectoryLevelStorage::sc_szPlayerDir(L"players/");
|
|
|
|
_MapDataMappings::_MapDataMappings() {
|
|
ZeroMemory(xuids, sizeof(PlayerUID) * MAXIMUM_MAP_SAVE_DATA);
|
|
ZeroMemory(dimensions, sizeof(uint8_t) * (MAXIMUM_MAP_SAVE_DATA / 4));
|
|
}
|
|
|
|
int _MapDataMappings::getDimension(int id) {
|
|
int offset = (2 * (id % 4));
|
|
int val = (dimensions[id >> 2] & (3 << offset)) >> offset;
|
|
|
|
int returnVal = 0;
|
|
|
|
switch (val) {
|
|
case 0:
|
|
returnVal = 0; // Overworld
|
|
break;
|
|
case 1:
|
|
returnVal = -1; // Nether
|
|
break;
|
|
case 2:
|
|
returnVal = 1; // End
|
|
break;
|
|
default:
|
|
#if !defined(_CONTENT_PACKAGE)
|
|
printf("Read invalid dimension from MapDataMapping\n");
|
|
__debugbreak();
|
|
#endif
|
|
break;
|
|
}
|
|
return returnVal;
|
|
}
|
|
|
|
void _MapDataMappings::setMapping(int id, PlayerUID xuid, int dimension) {
|
|
xuids[id] = xuid;
|
|
|
|
int offset = (2 * (id % 4));
|
|
|
|
// Reset it first
|
|
dimensions[id >> 2] &= ~(2 << offset);
|
|
switch (dimension) {
|
|
case 0: // Overworld
|
|
// dimensions[id>>2] &= ~( 2 << offset );
|
|
break;
|
|
case -1: // Nether
|
|
dimensions[id >> 2] |= (1 << offset);
|
|
break;
|
|
case 1: // End
|
|
dimensions[id >> 2] |= (2 << offset);
|
|
break;
|
|
default:
|
|
#if !defined(_CONTENT_PACKAGE)
|
|
printf(
|
|
"Trinyg to set a MapDataMapping for an invalid dimension.\n");
|
|
__debugbreak();
|
|
#endif
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Old version the only used 1 bit for dimension indexing
|
|
_MapDataMappings_old::_MapDataMappings_old() {
|
|
ZeroMemory(xuids, sizeof(PlayerUID) * MAXIMUM_MAP_SAVE_DATA);
|
|
ZeroMemory(dimensions, sizeof(uint8_t) * (MAXIMUM_MAP_SAVE_DATA / 8));
|
|
}
|
|
|
|
int _MapDataMappings_old::getDimension(int id) {
|
|
return dimensions[id >> 3] & (128 >> (id % 8)) ? -1 : 0;
|
|
}
|
|
|
|
void _MapDataMappings_old::setMapping(int id, PlayerUID xuid, int dimension) {
|
|
xuids[id] = xuid;
|
|
if (dimension == 0) {
|
|
dimensions[id >> 3] &= ~(128 >> (id % 8));
|
|
} else {
|
|
dimensions[id >> 3] |= (128 >> (id % 8));
|
|
}
|
|
}
|
|
|
|
#if defined(_LARGE_WORLDS)
|
|
void DirectoryLevelStorage::PlayerMappings::addMapping(int id, int centreX,
|
|
int centreZ,
|
|
int dimension,
|
|
int scale) {
|
|
int64_t index = (((int64_t)(centreZ & 0x1FFFFFFF)) << 34) |
|
|
(((int64_t)(centreX & 0x1FFFFFFF)) << 5) |
|
|
((scale & 0x7) << 2) | (dimension & 0x3);
|
|
m_mappings[index] = id;
|
|
// app.DebugPrintf("Adding mapping: %d - (%d,%d)/%d/%d [%I64d -
|
|
// 0x%016llx]\n", id, centreX, centreZ, dimension, scale, index, index);
|
|
}
|
|
|
|
bool DirectoryLevelStorage::PlayerMappings::getMapping(int& id, int centreX,
|
|
int centreZ,
|
|
int dimension,
|
|
int scale) {
|
|
//int64_t zMasked = centreZ & 0x1FFFFFFF;
|
|
//int64_t xMasked = centreX & 0x1FFFFFFF;
|
|
//int64_t zShifted = zMasked << 34;
|
|
//int64_t xShifted = xMasked << 5;
|
|
// app.DebugPrintf("xShifted = %d (0x%016x), zShifted = %I64d
|
|
// (0x%016llx)\n", xShifted, xShifted, zShifted, zShifted);
|
|
int64_t index = (((int64_t)(centreZ & 0x1FFFFFFF)) << 34) |
|
|
(((int64_t)(centreX & 0x1FFFFFFF)) << 5) |
|
|
((scale & 0x7) << 2) | (dimension & 0x3);
|
|
auto it = m_mappings.find(index);
|
|
if (it != m_mappings.end()) {
|
|
id = it->second;
|
|
// app.DebugPrintf("Found mapping: %d - (%d,%d)/%d/%d [%I64d -
|
|
// 0x%016llx]\n", id, centreX, centreZ, dimension, scale, index, index);
|
|
return true;
|
|
} else {
|
|
// app.DebugPrintf("Failed to find mapping: (%d,%d)/%d/%d [%I64d -
|
|
// 0x%016llx]\n", centreX, centreZ, dimension, scale, index, index);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void DirectoryLevelStorage::PlayerMappings::writeMappings(
|
|
DataOutputStream* dos) {
|
|
dos->writeInt(m_mappings.size());
|
|
for (auto it = m_mappings.begin(); it != m_mappings.end(); ++it) {
|
|
app.DebugPrintf(" -- %lld (0x%016llx) = %d\n", it->first, it->first,
|
|
it->second);
|
|
dos->writeLong(it->first);
|
|
dos->writeInt(it->second);
|
|
}
|
|
}
|
|
|
|
void DirectoryLevelStorage::PlayerMappings::readMappings(DataInputStream* dis) {
|
|
int count = dis->readInt();
|
|
for (unsigned int i = 0; i < count; ++i) {
|
|
int64_t index = dis->readLong();
|
|
int id = dis->readInt();
|
|
m_mappings[index] = id;
|
|
app.DebugPrintf(" -- %lld (0x%016llx) = %d\n", index, index, id);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
DirectoryLevelStorage::DirectoryLevelStorage(ConsoleSaveFile* saveFile,
|
|
const File dir,
|
|
const std::wstring& levelId,
|
|
bool createPlayerDir)
|
|
: sessionId(System::currentTimeMillis()),
|
|
dir(L""),
|
|
playerDir(sc_szPlayerDir),
|
|
dataDir(std::wstring(L"data/")),
|
|
levelId(levelId) {
|
|
m_saveFile = saveFile;
|
|
m_bHasLoadedMapDataMappings = false;
|
|
|
|
#if defined(_LARGE_WORLDS)
|
|
m_usedMappings = byteArray(MAXIMUM_MAP_SAVE_DATA / 8);
|
|
#endif
|
|
}
|
|
|
|
DirectoryLevelStorage::~DirectoryLevelStorage() {
|
|
delete m_saveFile;
|
|
|
|
for (auto it = m_cachedSaveData.begin(); it != m_cachedSaveData.end();
|
|
++it) {
|
|
delete it->second;
|
|
}
|
|
|
|
#if defined(_LARGE_WORLDS)
|
|
delete m_usedMappings.data;
|
|
#endif
|
|
}
|
|
|
|
void DirectoryLevelStorage::initiateSession() {
|
|
// 4J Jev, removed try/catch.
|
|
|
|
File dataFile = File(dir, std::wstring(L"session.lock"));
|
|
FileOutputStream fos = FileOutputStream(dataFile);
|
|
DataOutputStream dos = DataOutputStream(&fos);
|
|
dos.writeLong(sessionId);
|
|
dos.close();
|
|
}
|
|
|
|
File DirectoryLevelStorage::getFolder() { return dir; }
|
|
|
|
void DirectoryLevelStorage::checkSession() {
|
|
// 4J-PB - Not in the Xbox game
|
|
|
|
/*
|
|
File dataFile = File( dir, wstring(L"session.lock"));
|
|
FileInputStream fis = FileInputStream(dataFile);
|
|
DataInputStream dis = DataInputStream(&fis);
|
|
dis.close();
|
|
*/
|
|
}
|
|
|
|
ChunkStorage* DirectoryLevelStorage::createChunkStorage(Dimension* dimension) {
|
|
// 4J Jev, removed try/catch.
|
|
|
|
if (dynamic_cast<HellDimension*>(dimension) != nullptr) {
|
|
File dir2 = File(dir, LevelStorage::NETHER_FOLDER);
|
|
// dir2.mkdirs(); // 4J Removed
|
|
return new OldChunkStorage(dir2, true);
|
|
}
|
|
if (dynamic_cast<TheEndDimension*>(dimension) != nullptr) {
|
|
File dir2 = File(dir, LevelStorage::ENDER_FOLDER);
|
|
// dir2.mkdirs(); // 4J Removed
|
|
return new OldChunkStorage(dir2, true);
|
|
}
|
|
|
|
return new OldChunkStorage(dir, true);
|
|
}
|
|
|
|
LevelData* DirectoryLevelStorage::prepareLevel() {
|
|
// 4J Stu Added
|
|
#if defined(_LARGE_WORLDS)
|
|
ConsoleSavePath mapFile = getDataFile(L"largeMapDataMappings");
|
|
#else
|
|
ConsoleSavePath mapFile = getDataFile(L"mapDataMappings");
|
|
#endif
|
|
if (!m_bHasLoadedMapDataMappings && !mapFile.getName().empty() &&
|
|
getSaveFile()->doesFileExist(mapFile)) {
|
|
unsigned int NumberOfBytesRead;
|
|
FileEntry* fileEntry = getSaveFile()->createFile(mapFile);
|
|
|
|
{
|
|
getSaveFile()->setFilePointer(fileEntry, 0,
|
|
SaveFileSeekOrigin::Begin);
|
|
|
|
#if defined(_LARGE_WORLDS)
|
|
byteArray data(fileEntry->getFileSize());
|
|
getSaveFile()->readFile(fileEntry, data.data,
|
|
fileEntry->getFileSize(),
|
|
&NumberOfBytesRead);
|
|
assert(NumberOfBytesRead == fileEntry->getFileSize());
|
|
|
|
ByteArrayInputStream bais(data);
|
|
DataInputStream dis(&bais);
|
|
int count = dis.readInt();
|
|
app.DebugPrintf("Loading %d mappings\n", count);
|
|
for (unsigned int i = 0; i < count; ++i) {
|
|
PlayerUID playerUid = dis.readPlayerUID();
|
|
#if defined(_WINDOWS64) || defined(__linux__)
|
|
app.DebugPrintf(" -- %d\n", playerUid);
|
|
#else
|
|
#if defined(__linux__)
|
|
app.DebugPrintf(" -- %d\n", playerUid);
|
|
#else
|
|
app.DebugPrintf(" -- %ls\n", playerUid.toString().c_str());
|
|
#endif
|
|
#endif
|
|
m_playerMappings[playerUid].readMappings(&dis);
|
|
}
|
|
dis.readFully(m_usedMappings);
|
|
#else
|
|
|
|
if (getSaveFile()->getSaveVersion() <
|
|
END_DIMENSION_MAP_MAPPINGS_SAVE_VERSION) {
|
|
MapDataMappings_old oldMapDataMappings;
|
|
getSaveFile()->readFile(
|
|
fileEntry,
|
|
&oldMapDataMappings, // data buffer
|
|
sizeof(MapDataMappings_old), // number of bytes to read
|
|
&NumberOfBytesRead // number of bytes read
|
|
);
|
|
assert(NumberOfBytesRead == sizeof(MapDataMappings_old));
|
|
|
|
for (unsigned int i = 0; i < MAXIMUM_MAP_SAVE_DATA; ++i) {
|
|
m_saveableMapDataMappings.setMapping(
|
|
i, oldMapDataMappings.xuids[i],
|
|
oldMapDataMappings.getDimension(i));
|
|
}
|
|
} else {
|
|
getSaveFile()->readFile(
|
|
fileEntry,
|
|
&m_saveableMapDataMappings, // data buffer
|
|
sizeof(MapDataMappings), // number of bytes to read
|
|
&NumberOfBytesRead // number of bytes read
|
|
);
|
|
assert(NumberOfBytesRead == sizeof(MapDataMappings));
|
|
}
|
|
|
|
memcpy(&m_mapDataMappings, &m_saveableMapDataMappings,
|
|
sizeof(MapDataMappings));
|
|
#endif
|
|
|
|
// Write out our changes now
|
|
if (getSaveFile()->getSaveVersion() <
|
|
END_DIMENSION_MAP_MAPPINGS_SAVE_VERSION)
|
|
saveMapIdLookup();
|
|
}
|
|
|
|
m_bHasLoadedMapDataMappings = true;
|
|
}
|
|
|
|
// 4J Jev, removed try/catch
|
|
|
|
ConsoleSavePath dataFile = ConsoleSavePath(std::wstring(L"level.dat"));
|
|
|
|
if (m_saveFile->doesFileExist(dataFile)) {
|
|
ConsoleSaveFileInputStream fis =
|
|
ConsoleSaveFileInputStream(m_saveFile, dataFile);
|
|
CompoundTag* root = NbtIo::readCompressed(&fis);
|
|
CompoundTag* tag = root->getCompound(L"Data");
|
|
LevelData* ret = new LevelData(tag);
|
|
delete root;
|
|
return ret;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
void DirectoryLevelStorage::saveLevelData(
|
|
LevelData* levelData, std::vector<std::shared_ptr<Player> >* players) {
|
|
// 4J Jev, removed try/catch
|
|
|
|
CompoundTag* dataTag = levelData->createTag(players);
|
|
|
|
CompoundTag* root = new CompoundTag();
|
|
root->put(L"Data", dataTag);
|
|
|
|
ConsoleSavePath currentFile = ConsoleSavePath(std::wstring(L"level.dat"));
|
|
|
|
ConsoleSaveFileOutputStream fos =
|
|
ConsoleSaveFileOutputStream(m_saveFile, currentFile);
|
|
NbtIo::writeCompressed(root, &fos);
|
|
|
|
delete root;
|
|
}
|
|
|
|
void DirectoryLevelStorage::saveLevelData(LevelData* levelData) {
|
|
// 4J Jev, removed try/catch
|
|
|
|
CompoundTag* dataTag = levelData->createTag();
|
|
|
|
CompoundTag* root = new CompoundTag();
|
|
root->put(L"Data", dataTag);
|
|
|
|
ConsoleSavePath currentFile = ConsoleSavePath(std::wstring(L"level.dat"));
|
|
|
|
ConsoleSaveFileOutputStream fos =
|
|
ConsoleSaveFileOutputStream(m_saveFile, currentFile);
|
|
NbtIo::writeCompressed(root, &fos);
|
|
|
|
delete root;
|
|
}
|
|
|
|
void DirectoryLevelStorage::save(std::shared_ptr<Player> player) {
|
|
// 4J Jev, removed try/catch.
|
|
PlayerUID playerXuid = player->getXuid();
|
|
if (playerXuid != INVALID_XUID && !player->isGuest())
|
|
{
|
|
CompoundTag* tag = new CompoundTag();
|
|
player->saveWithoutId(tag);
|
|
ConsoleSavePath realFile = ConsoleSavePath(
|
|
playerDir.getName() + _toString(player->getXuid()) + L".dat");
|
|
// If saves are disabled (e.g. because we are writing the save buffer to
|
|
// disk) then cache this player data
|
|
if (StorageManager.GetSaveDisabled()) {
|
|
ByteArrayOutputStream* bos = new ByteArrayOutputStream();
|
|
NbtIo::writeCompressed(tag, bos);
|
|
|
|
auto it = m_cachedSaveData.find(realFile.getName());
|
|
if (it != m_cachedSaveData.end()) {
|
|
delete it->second;
|
|
}
|
|
m_cachedSaveData[realFile.getName()] = bos;
|
|
app.DebugPrintf(
|
|
"Cached saving of file %ls due to saves being disabled\n",
|
|
realFile.getName().c_str());
|
|
} else {
|
|
ConsoleSaveFileOutputStream fos =
|
|
ConsoleSaveFileOutputStream(m_saveFile, realFile);
|
|
NbtIo::writeCompressed(tag, &fos);
|
|
}
|
|
delete tag;
|
|
} else if (playerXuid != INVALID_XUID) {
|
|
app.DebugPrintf("Not saving player as their XUID is a guest\n");
|
|
dontSaveMapMappingForPlayer(playerXuid);
|
|
}
|
|
}
|
|
|
|
// 4J Changed return val to bool to check if new player or loaded player
|
|
CompoundTag* DirectoryLevelStorage::load(std::shared_ptr<Player> player) {
|
|
CompoundTag* tag = loadPlayerDataTag(player->getXuid());
|
|
if (tag != nullptr) {
|
|
player->load(tag);
|
|
}
|
|
return tag;
|
|
}
|
|
|
|
CompoundTag* DirectoryLevelStorage::loadPlayerDataTag(PlayerUID xuid) {
|
|
// 4J Jev, removed try/catch.
|
|
ConsoleSavePath realFile =
|
|
ConsoleSavePath(playerDir.getName() + _toString(xuid) + L".dat");
|
|
auto it = m_cachedSaveData.find(realFile.getName());
|
|
if (it != m_cachedSaveData.end()) {
|
|
ByteArrayOutputStream* bos = it->second;
|
|
ByteArrayInputStream bis(bos->buf, 0, bos->size());
|
|
CompoundTag* tag = NbtIo::readCompressed(&bis);
|
|
bis.reset();
|
|
app.DebugPrintf("Loaded player data from cached file %ls\n",
|
|
realFile.getName().c_str());
|
|
return tag;
|
|
} else if (m_saveFile->doesFileExist(realFile)) {
|
|
ConsoleSaveFileInputStream fis =
|
|
ConsoleSaveFileInputStream(m_saveFile, realFile);
|
|
return NbtIo::readCompressed(&fis);
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
// 4J Added function
|
|
void DirectoryLevelStorage::clearOldPlayerFiles() {
|
|
if (StorageManager.GetSaveDisabled()) return;
|
|
|
|
std::vector<FileEntry*>* playerFiles =
|
|
m_saveFile->getFilesWithPrefix(playerDir.getName());
|
|
|
|
if (playerFiles != nullptr) {
|
|
#if !defined(_FINAL_BUILD)
|
|
if (app.DebugSettingsOn() &&
|
|
app.GetGameSettingsDebugMask(ProfileManager.GetPrimaryPad()) &
|
|
(1L << eDebugSetting_DistributableSave)) {
|
|
for (unsigned int i = 0; i < playerFiles->size(); ++i) {
|
|
FileEntry* file = playerFiles->at(i);
|
|
std::wstring xuidStr = replaceAll(
|
|
replaceAll(file->data.filename, playerDir.getName(), L""),
|
|
L".dat", L"");
|
|
PlayerUID xuid = _fromString<PlayerUID>(xuidStr);
|
|
deleteMapFilesForPlayer(xuid);
|
|
m_saveFile->deleteFile(playerFiles->at(i));
|
|
}
|
|
} else
|
|
#endif
|
|
if (playerFiles->size() > MAX_PLAYER_DATA_SAVES) {
|
|
sort(playerFiles->begin(), playerFiles->end(),
|
|
FileEntry::newestFirst);
|
|
|
|
for (unsigned int i = MAX_PLAYER_DATA_SAVES;
|
|
i < playerFiles->size(); ++i) {
|
|
FileEntry* file = playerFiles->at(i);
|
|
std::wstring xuidStr = replaceAll(
|
|
replaceAll(file->data.filename, playerDir.getName(), L""),
|
|
L".dat", L"");
|
|
PlayerUID xuid = _fromString<PlayerUID>(xuidStr);
|
|
deleteMapFilesForPlayer(xuid);
|
|
m_saveFile->deleteFile(playerFiles->at(i));
|
|
}
|
|
}
|
|
|
|
delete playerFiles;
|
|
}
|
|
}
|
|
|
|
PlayerIO* DirectoryLevelStorage::getPlayerIO() { return this; }
|
|
|
|
void DirectoryLevelStorage::closeAll() {}
|
|
|
|
ConsoleSavePath DirectoryLevelStorage::getDataFile(const std::wstring& id) {
|
|
return ConsoleSavePath(dataDir.getName() + id + L".dat");
|
|
}
|
|
|
|
std::wstring DirectoryLevelStorage::getLevelId() { return levelId; }
|
|
|
|
void DirectoryLevelStorage::flushSaveFile(bool autosave) {
|
|
#if !defined(_CONTENT_PACKAGE)
|
|
if (app.DebugSettingsOn() &&
|
|
app.GetGameSettingsDebugMask(ProfileManager.GetPrimaryPad()) &
|
|
(1L << eDebugSetting_DistributableSave)) {
|
|
// Delete gamerules files if it exists
|
|
ConsoleSavePath gameRulesFiles(GAME_RULE_SAVENAME);
|
|
if (m_saveFile->doesFileExist(gameRulesFiles)) {
|
|
FileEntry* fe = m_saveFile->createFile(gameRulesFiles);
|
|
m_saveFile->deleteFile(fe);
|
|
}
|
|
}
|
|
#endif
|
|
m_saveFile->Flush(autosave);
|
|
}
|
|
|
|
// 4J Added
|
|
void DirectoryLevelStorage::resetNetherPlayerPositions() {
|
|
if (app.GetResetNether()) {
|
|
std::vector<FileEntry*>* playerFiles =
|
|
m_saveFile->getFilesWithPrefix(playerDir.getName());
|
|
|
|
if (playerFiles != nullptr) {
|
|
for (auto it = playerFiles->begin(); it != playerFiles->end();
|
|
++it) {
|
|
FileEntry* realFile = *it;
|
|
ConsoleSaveFileInputStream fis =
|
|
ConsoleSaveFileInputStream(m_saveFile, realFile);
|
|
CompoundTag* tag = NbtIo::readCompressed(&fis);
|
|
if (tag != nullptr) {
|
|
// If the player is in the nether, set their y position
|
|
// above the top of the nether This will force the player to
|
|
// be spawned in a valid position in the overworld when they
|
|
// are loaded
|
|
if (tag->contains(L"Dimension") &&
|
|
tag->getInt(L"Dimension") ==
|
|
LevelData::DIMENSION_NETHER &&
|
|
tag->contains(L"Pos")) {
|
|
ListTag<DoubleTag>* pos =
|
|
(ListTag<DoubleTag>*)tag->getList(L"Pos");
|
|
pos->get(1)->data = DBL_MAX;
|
|
|
|
ConsoleSaveFileOutputStream fos =
|
|
ConsoleSaveFileOutputStream(m_saveFile, realFile);
|
|
NbtIo::writeCompressed(tag, &fos);
|
|
}
|
|
delete tag;
|
|
}
|
|
}
|
|
delete playerFiles;
|
|
}
|
|
}
|
|
}
|
|
|
|
int DirectoryLevelStorage::getAuxValueForMap(PlayerUID xuid, int dimension,
|
|
int centreXC, int centreZC,
|
|
int scale) {
|
|
int mapId = -1;
|
|
bool foundMapping = false;
|
|
|
|
#if defined(_LARGE_WORLDS)
|
|
auto it = m_playerMappings.find(xuid);
|
|
if (it != m_playerMappings.end()) {
|
|
foundMapping =
|
|
it->second.getMapping(mapId, centreXC, centreZC, dimension, scale);
|
|
}
|
|
|
|
if (!foundMapping) {
|
|
for (unsigned int i = 0; i < m_usedMappings.length; ++i) {
|
|
if (m_usedMappings[i] < 0xFF) {
|
|
unsigned int offset = 0;
|
|
for (; offset < 8; ++offset) {
|
|
if (!(m_usedMappings[i] & (1 << offset))) {
|
|
break;
|
|
}
|
|
}
|
|
mapId = (i * 8) + offset;
|
|
m_playerMappings[xuid].addMapping(mapId, centreXC, centreZC,
|
|
dimension, scale);
|
|
m_usedMappings[i] |= (1 << offset);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
#else
|
|
for (unsigned int i = 0; i < MAXIMUM_MAP_SAVE_DATA; ++i) {
|
|
if (m_mapDataMappings.xuids[i] == xuid &&
|
|
m_mapDataMappings.getDimension(i) == dimension) {
|
|
foundMapping = true;
|
|
mapId = i;
|
|
break;
|
|
}
|
|
if (mapId < 0 && m_mapDataMappings.xuids[i] == INVALID_XUID) {
|
|
mapId = i;
|
|
}
|
|
}
|
|
if (!foundMapping && mapId >= 0 && mapId < MAXIMUM_MAP_SAVE_DATA) {
|
|
m_mapDataMappings.setMapping(mapId, xuid, dimension);
|
|
m_saveableMapDataMappings.setMapping(mapId, xuid, dimension);
|
|
|
|
// If we had an old map file for a mapping that is no longer valid,
|
|
// delete it
|
|
std::wstring id = std::wstring(L"map_") + _toString(mapId);
|
|
ConsoleSavePath file = getDataFile(id);
|
|
|
|
if (m_saveFile->doesFileExist(file)) {
|
|
auto it = find(m_mapFilesToDelete.begin(),
|
|
m_mapFilesToDelete.end(), mapId);
|
|
if (it != m_mapFilesToDelete.end()) m_mapFilesToDelete.erase(it);
|
|
|
|
m_saveFile->deleteFile(m_saveFile->createFile(file));
|
|
}
|
|
}
|
|
#endif
|
|
return mapId;
|
|
}
|
|
|
|
void DirectoryLevelStorage::saveMapIdLookup() {
|
|
if (StorageManager.GetSaveDisabled()) return;
|
|
|
|
#if defined(_LARGE_WORLDS)
|
|
ConsoleSavePath file = getDataFile(L"largeMapDataMappings");
|
|
#else
|
|
ConsoleSavePath file = getDataFile(L"mapDataMappings");
|
|
#endif
|
|
|
|
if (!file.getName().empty()) {
|
|
unsigned int NumberOfBytesWritten;
|
|
FileEntry* fileEntry = m_saveFile->createFile(file);
|
|
m_saveFile->setFilePointer(fileEntry, 0, SaveFileSeekOrigin::Begin);
|
|
|
|
#if defined(_LARGE_WORLDS)
|
|
ByteArrayOutputStream baos;
|
|
DataOutputStream dos(&baos);
|
|
dos.writeInt(m_playerMappings.size());
|
|
app.DebugPrintf("Saving %d mappings\n", m_playerMappings.size());
|
|
for (auto it = m_playerMappings.begin();
|
|
it != m_playerMappings.end(); ++it) {
|
|
#if defined(_WINDOWS64) || defined(__linux__)
|
|
app.DebugPrintf(" -- %d\n", it->first);
|
|
#else
|
|
#if defined(__linux__)
|
|
app.DebugPrintf(" -- %d\n", it->first);
|
|
#else
|
|
app.DebugPrintf(" -- %ls\n", it->first.toString().c_str());
|
|
#endif
|
|
#endif
|
|
dos.writePlayerUID(it->first);
|
|
it->second.writeMappings(&dos);
|
|
}
|
|
dos.write(m_usedMappings);
|
|
m_saveFile->writeFile(fileEntry,
|
|
baos.buf.data, // data buffer
|
|
baos.size(), // number of bytes to write
|
|
&NumberOfBytesWritten // number of bytes written
|
|
);
|
|
#else
|
|
m_saveFile->writeFile(
|
|
fileEntry,
|
|
&m_saveableMapDataMappings, // data buffer
|
|
sizeof(MapDataMappings), // number of bytes to write
|
|
&NumberOfBytesWritten // number of bytes written
|
|
);
|
|
assert(NumberOfBytesWritten == sizeof(MapDataMappings));
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void DirectoryLevelStorage::dontSaveMapMappingForPlayer(PlayerUID xuid) {
|
|
#if defined(_LARGE_WORLDS)
|
|
auto it = m_playerMappings.find(xuid);
|
|
if (it != m_playerMappings.end()) {
|
|
for (auto itMap = it->second.m_mappings.begin();
|
|
itMap != it->second.m_mappings.end(); ++itMap) {
|
|
int index = itMap->second / 8;
|
|
int offset = itMap->second % 8;
|
|
m_usedMappings[index] &= ~(1 << offset);
|
|
}
|
|
m_playerMappings.erase(it);
|
|
}
|
|
#else
|
|
for (unsigned int i = 0; i < MAXIMUM_MAP_SAVE_DATA; ++i) {
|
|
if (m_saveableMapDataMappings.xuids[i] == xuid) {
|
|
m_saveableMapDataMappings.setMapping(i, INVALID_XUID, 0);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void DirectoryLevelStorage::deleteMapFilesForPlayer(
|
|
std::shared_ptr<Player> player) {
|
|
PlayerUID playerXuid = player->getXuid();
|
|
if (playerXuid != INVALID_XUID) deleteMapFilesForPlayer(playerXuid);
|
|
}
|
|
|
|
void DirectoryLevelStorage::deleteMapFilesForPlayer(PlayerUID xuid) {
|
|
#if defined(_LARGE_WORLDS)
|
|
auto it = m_playerMappings.find(xuid);
|
|
if (it != m_playerMappings.end()) {
|
|
for (auto itMap = it->second.m_mappings.begin();
|
|
itMap != it->second.m_mappings.end(); ++itMap) {
|
|
std::wstring id = std::wstring(L"map_") + _toString(itMap->second);
|
|
ConsoleSavePath file = getDataFile(id);
|
|
|
|
if (m_saveFile->doesFileExist(file)) {
|
|
// If we can't actually delete this file, store the name so we
|
|
// can delete it later
|
|
if (StorageManager.GetSaveDisabled())
|
|
m_mapFilesToDelete.push_back(itMap->second);
|
|
else
|
|
m_saveFile->deleteFile(m_saveFile->createFile(file));
|
|
}
|
|
|
|
int index = itMap->second / 8;
|
|
int offset = itMap->second % 8;
|
|
m_usedMappings[index] &= ~(1 << offset);
|
|
}
|
|
m_playerMappings.erase(it);
|
|
}
|
|
#else
|
|
bool changed = false;
|
|
for (unsigned int i = 0; i < MAXIMUM_MAP_SAVE_DATA; ++i) {
|
|
if (m_mapDataMappings.xuids[i] == xuid) {
|
|
changed = true;
|
|
|
|
std::wstring id = std::wstring(L"map_") + _toString(i);
|
|
ConsoleSavePath file = getDataFile(id);
|
|
|
|
if (m_saveFile->doesFileExist(file)) {
|
|
// If we can't actually delete this file, store the name so we
|
|
// can delete it later
|
|
if (StorageManager.GetSaveDisabled())
|
|
m_mapFilesToDelete.push_back(i);
|
|
else
|
|
m_saveFile->deleteFile(m_saveFile->createFile(file));
|
|
}
|
|
m_mapDataMappings.setMapping(i, INVALID_XUID, 0);
|
|
m_saveableMapDataMappings.setMapping(i, INVALID_XUID, 0);
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void DirectoryLevelStorage::saveAllCachedData() {
|
|
if (StorageManager.GetSaveDisabled()) return;
|
|
|
|
// Save any files that were saved while saving was disabled
|
|
for (auto it = m_cachedSaveData.begin(); it != m_cachedSaveData.end();
|
|
++it) {
|
|
ByteArrayOutputStream* bos = it->second;
|
|
|
|
ConsoleSavePath realFile = ConsoleSavePath(it->first);
|
|
ConsoleSaveFileOutputStream fos =
|
|
ConsoleSaveFileOutputStream(m_saveFile, realFile);
|
|
|
|
app.DebugPrintf("Actually writing cached file %ls\n",
|
|
it->first.c_str());
|
|
fos.write(bos->buf, 0, bos->size());
|
|
delete bos;
|
|
}
|
|
m_cachedSaveData.clear();
|
|
|
|
for (auto it = m_mapFilesToDelete.begin();
|
|
it != m_mapFilesToDelete.end(); ++it) {
|
|
std::wstring id = std::wstring(L"map_") + _toString(*it);
|
|
ConsoleSavePath file = getDataFile(id);
|
|
if (m_saveFile->doesFileExist(file)) {
|
|
m_saveFile->deleteFile(m_saveFile->createFile(file));
|
|
}
|
|
}
|
|
m_mapFilesToDelete.clear();
|
|
}
|