mirror of
https://gitea.sffempire.ru/Kolyah35/minecraft-pe-0.6.1.git
synced 2026-03-30 20:13:31 +00:00
703 lines
20 KiB
C++
Executable File
703 lines
20 KiB
C++
Executable File
#include <cstddef>
|
|
#include <fstream>
|
|
#include <ios>
|
|
#if !defined(DEMO_MODE) && !defined(APPLE_DEMO_PROMOTION)
|
|
|
|
#include "LevelData.h"
|
|
#include "RegionFile.h"
|
|
#include "ExternalFileLevelStorage.h"
|
|
#include "FolderMethods.h"
|
|
#include "../chunk/LevelChunk.h"
|
|
#include "../Level.h"
|
|
#include "../LevelConstants.h"
|
|
#include "platform/log.h"
|
|
#include "../tile/TreeTile.h"
|
|
#include "../../entity/EntityFactory.h"
|
|
#include "../../../nbt/NbtIo.h"
|
|
#include "../../../util/RakDataIO.h"
|
|
#include "../../../raknet/GetTime.h"
|
|
#include "../tile/entity/TileEntity.h"
|
|
|
|
static const int ChunkVersion_Light = 1;
|
|
static const int ChunkVersion_Entity = 2;
|
|
|
|
const char* const fnLevelDatOld = "level.dat_old";
|
|
const char* const fnLevelDatNew = "level.dat_new";
|
|
const char* const fnLevelDat = "level.dat";
|
|
const char* const fnPlayerDat = "player.dat";
|
|
|
|
//
|
|
// Helpers for converting old levels to newer
|
|
//
|
|
class LevelConverters
|
|
{
|
|
public:
|
|
// Replacing old Cloth (id based) with new Cloth (data based)
|
|
static bool v1_ClothIdToClothData(LevelChunk* c) {
|
|
bool changed = false;
|
|
unsigned char* blocks = c->getBlockData();
|
|
unsigned char newTile = Tile::cloth->id;
|
|
|
|
for (int i = 0; i < 16*16*128; ++i) {
|
|
unsigned char oldTile = blocks[i];
|
|
//Tile::cloth_00 to Tile::cloth_61
|
|
if (oldTile >= 101 && oldTile <= 115) {
|
|
int color = 0xf - (oldTile - 101);
|
|
blocks[i] = newTile;
|
|
c->data.set(i, color);
|
|
changed = true;
|
|
}
|
|
}
|
|
return changed;
|
|
}
|
|
|
|
// Replacing unavailable blocks with "Update!" blocks
|
|
static bool ReplaceUnavailableBlocks(LevelChunk* c) {
|
|
//int st = getTimeMs();
|
|
|
|
bool changed = false;
|
|
unsigned char* blocks = c->getBlockData();
|
|
|
|
for (int i = 0; i < 16*16*128; ++i) {
|
|
unsigned char oldTile = blocks[i];
|
|
unsigned char newTile = Tile::transformToValidBlockId(oldTile);
|
|
if (oldTile != newTile) {
|
|
blocks[i] = newTile;
|
|
changed = true;
|
|
}
|
|
}
|
|
|
|
//int et = getTimeMs();
|
|
//LOGI("time: %d\n", et - st);
|
|
return changed;
|
|
}
|
|
|
|
//static bool ConvertPlayerDatToLevelDat() {
|
|
// return false;
|
|
//}
|
|
};
|
|
|
|
|
|
ExternalFileLevelStorage::ExternalFileLevelStorage(const std::string& levelId, const std::string& fullPath)
|
|
: levelId(levelId),
|
|
levelPath(fullPath),
|
|
loadedLevelData(NULL),
|
|
regionFile(NULL),
|
|
entitiesFile(NULL),
|
|
tickCount(0),
|
|
lastSavedEntitiesTick(-999999),
|
|
level(NULL),
|
|
loadedStorageVersion(SharedConstants::StorageVersion)
|
|
{
|
|
createFolderIfNotExists(levelPath.c_str());
|
|
|
|
std::string playerFolder = levelPath + "/players";
|
|
createFolderIfNotExists(playerFolder.c_str());
|
|
|
|
std::string datFileName = levelPath + "/" + fnLevelDat;
|
|
std::string levelFileName = levelPath + "/" + fnPlayerDat;
|
|
loadedLevelData = new LevelData();
|
|
if (readLevelData(levelPath, *loadedLevelData))
|
|
{
|
|
loadedStorageVersion = loadedLevelData->getStorageVersion();
|
|
readPlayerData(levelFileName, *loadedLevelData);
|
|
} else {
|
|
delete loadedLevelData;
|
|
loadedLevelData = NULL;
|
|
}
|
|
}
|
|
|
|
ExternalFileLevelStorage::~ExternalFileLevelStorage()
|
|
{
|
|
delete regionFile;
|
|
delete loadedLevelData;
|
|
}
|
|
|
|
void ExternalFileLevelStorage::saveLevelData(LevelData& levelData, std::vector<Player*>* players) {
|
|
ExternalFileLevelStorage::saveLevelData(levelPath, levelData, players);
|
|
}
|
|
|
|
void ExternalFileLevelStorage::saveLevelData( const std::string& levelPath, LevelData& levelData, std::vector<Player*>* players )
|
|
{
|
|
// @todo: completely rewrite
|
|
std::string directory = levelPath + "/";
|
|
std::string tmpFile = directory + fnLevelDatNew;
|
|
std::string datFile = directory + fnLevelDat;
|
|
std::string oldFile = directory + fnLevelDatOld;
|
|
|
|
levelData.setStorageVersion(SharedConstants::StorageVersion);
|
|
if (!writeLevelData(tmpFile, levelData, players))
|
|
return;
|
|
|
|
// Remove old backup
|
|
remove(oldFile.c_str());
|
|
|
|
// If it exists, move the previous save to backup (and possibly delete it)
|
|
if (exists(datFile.c_str())) {
|
|
if (rename(datFile.c_str(), oldFile.c_str())) {
|
|
LOGE("Error@saveLevelData: Couldn't move savefile to level.dat_old\n");
|
|
return;
|
|
}
|
|
remove(datFile.c_str());
|
|
}
|
|
// Move the new save to level.dat
|
|
if (rename(tmpFile.c_str(), datFile.c_str())) {
|
|
LOGE("Error@saveLevelData: Couldn't move new file to level.dat\n");
|
|
return;
|
|
}
|
|
|
|
// Remove the temporary save, if the rename didn't do it
|
|
remove(tmpFile.c_str());
|
|
|
|
// Save players
|
|
// fuck mojang for that
|
|
if (!players || players->empty()) {
|
|
return;
|
|
}
|
|
|
|
for (auto& player : *players) {
|
|
if (player != NULL) {
|
|
savePlayer(*player, directory);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ExternalFileLevelStorage::savePlayer(Player& player, const std::string& worldDir) {
|
|
std::string playerPath = worldDir + "/players/" + player.name + ".dat";
|
|
|
|
LOGI("Saving player %s to %s...\n", player.name.c_str(), playerPath.c_str());
|
|
|
|
RakNet::BitStream data;
|
|
RakDataOutput buf(data);
|
|
CompoundTag playerTag;
|
|
player.saveWithoutId(&playerTag);
|
|
|
|
NbtIo::write(&playerTag, &buf);
|
|
|
|
std::ofstream file(playerPath, std::ios::out | std::ios::binary);
|
|
file.write((const char*)data.GetData(), (size_t)data.GetNumberOfBytesUsed());
|
|
}
|
|
|
|
bool ExternalFileLevelStorage::loadPlayer(Player& player, const std::string& worldDir) {
|
|
std::string playerPath = worldDir + "/players/" + player.name + ".dat";
|
|
|
|
LOGI("Loading player %s from %s...\n", player.name.c_str(), playerPath.c_str());
|
|
|
|
std::ifstream file(playerPath, std::ios::in | std::ios::binary);
|
|
if (!file.is_open()) {
|
|
return false;
|
|
}
|
|
|
|
std::vector<uint8_t> data((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
|
|
|
|
RakNet::BitStream bitStream(data.data(), data.size(), false);
|
|
RakDataInput stream(bitStream);
|
|
|
|
CompoundTag* tag = NbtIo::read(&stream);
|
|
if (tag) {
|
|
player.load(tag);
|
|
tag->deleteChildren();
|
|
delete tag;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void ExternalFileLevelStorage::savePlayer(Player& player) {
|
|
ExternalFileLevelStorage::savePlayer(player, levelPath);
|
|
}
|
|
|
|
bool ExternalFileLevelStorage::loadPlayer(Player& player) {
|
|
return ExternalFileLevelStorage::loadPlayer(player, levelPath);
|
|
}
|
|
|
|
LevelData* ExternalFileLevelStorage::prepareLevel(Level* _level)
|
|
{
|
|
level = _level;
|
|
return loadedLevelData;
|
|
}
|
|
|
|
bool ExternalFileLevelStorage::readLevelData(const std::string& directory, LevelData& levelData)
|
|
{
|
|
// Try to load level.dat
|
|
std::string datFilename = directory + "/" + fnLevelDat;
|
|
FILE* file = fopen(datFilename.c_str(), "rb");
|
|
|
|
// If that fails, try to load level.dat_old
|
|
if (!file) {
|
|
datFilename = directory + "/" + fnLevelDatOld;
|
|
file = fopen(datFilename.c_str(), "rb");
|
|
}
|
|
|
|
if (!file)
|
|
return false;
|
|
|
|
int version = 0;
|
|
int size = 0;
|
|
unsigned char* data = NULL;
|
|
|
|
do {
|
|
if (fread(&version, sizeof(version), 1, file) != 1)
|
|
{
|
|
break;
|
|
}
|
|
if (fread(&size, sizeof(size), 1, file) != 1)
|
|
{
|
|
break;
|
|
}
|
|
|
|
int left = getRemainingFileSize(file);
|
|
if (size > left || size <= 0)
|
|
break;
|
|
|
|
data = new unsigned char[size];
|
|
if (fread(data, 1, size, file) != size)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (version == 1) {
|
|
RakNet::BitStream bitStream(data, size, false);
|
|
levelData.v1_read(bitStream, version);
|
|
} else if (version >= 2) {
|
|
//LOGI("---> Trying to load level with version %d\n", version);
|
|
RakNet::BitStream bitStream(data, size, false);
|
|
RakDataInput stream(bitStream);
|
|
//LOGI("dat: %s\n", datFileName.c_str());
|
|
CompoundTag* tag = NbtIo::read(&stream);
|
|
if (tag) {
|
|
levelData.getTagData(tag);
|
|
tag->deleteChildren();
|
|
delete tag;
|
|
}
|
|
//LOGI("<--- Finished reading level tag: %p\n", tag);
|
|
}
|
|
} while (false);
|
|
|
|
fclose(file);
|
|
delete [] data;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ExternalFileLevelStorage::writeLevelData(const std::string& datFileName, LevelData& levelData, const std::vector<Player*>* players)
|
|
{
|
|
LOGI("Writing down level seed as: %ld\n", levelData.getSeed());
|
|
//return true;
|
|
|
|
// Write level info
|
|
FILE* file = fopen(datFileName.c_str(), "wb");
|
|
if (!file)
|
|
return false;
|
|
|
|
//if (levelData.getStorageVersion() == 1) {
|
|
RakNet::BitStream data;
|
|
if (levelData.getStorageVersion() == 1)
|
|
levelData.v1_write(data);
|
|
else {
|
|
RakDataOutput buf(data);
|
|
//LOGI("---> Trying to write level with version %d\n", version);
|
|
CompoundTag* tag = NULL;
|
|
if (players && !players->empty())
|
|
tag = levelData.createTag(*players);
|
|
else
|
|
tag = levelData.createTag();
|
|
|
|
NbtIo::write(tag, &buf);
|
|
tag->deleteChildren();
|
|
delete tag;
|
|
//LOGI("<--- Finished writing level data. Size: %d\n", fdout.bytesWritten);
|
|
}
|
|
|
|
int version = levelData.getStorageVersion(); // 1
|
|
fwrite(&version, sizeof(version), 1, file);
|
|
int size = data.GetNumberOfBytesUsed();
|
|
fwrite(&size, sizeof(size), 1, file);
|
|
|
|
fwrite(data.GetData(), 1, size, file);
|
|
fclose(file);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ExternalFileLevelStorage::readPlayerData(const std::string& filename, LevelData& dest)
|
|
{
|
|
FILE* fp = fopen(filename.c_str(), "rb");
|
|
if (!fp)
|
|
return false;
|
|
|
|
do {
|
|
int version;
|
|
if (fread(&version, 4, 1, fp) != 1)
|
|
break;
|
|
|
|
int size;
|
|
if (fread(&size, 4, 1, fp) != 1)
|
|
break;
|
|
|
|
if (version == 1) {
|
|
if (fread(&dest.playerData, 1, sizeof(dest.playerData), fp) != size)
|
|
break;
|
|
|
|
// Fix coordinates
|
|
Vec3& pos = dest.playerData.pos;
|
|
if (pos.x < 0.5f) pos.x = 0.5f;
|
|
if (pos.z < 0.5f) pos.z = 0.5f;
|
|
if (pos.x > (LEVEL_WIDTH - 0.5f)) pos.x = LEVEL_WIDTH - 0.5f;
|
|
if (pos.z > (LEVEL_DEPTH - 0.5f)) pos.z = LEVEL_DEPTH - 0.5f;
|
|
if (pos.y < 0) pos.y = 64;
|
|
|
|
dest.playerDataVersion = version;
|
|
}
|
|
} while (false);
|
|
|
|
fclose(fp);
|
|
return true;
|
|
}
|
|
|
|
void ExternalFileLevelStorage::tick()
|
|
{
|
|
tickCount++;
|
|
if ((tickCount % 1000) == 0 && level) {
|
|
LOGI("Saving level...\n");
|
|
|
|
// look for chunks that needs to be saved
|
|
for (int z = 0; z < CHUNK_CACHE_WIDTH; z++)
|
|
{
|
|
for (int x = 0; x < CHUNK_CACHE_WIDTH; x++)
|
|
{
|
|
LevelChunk* chunk = level->getChunk(x, z);
|
|
if (chunk && chunk->unsaved)
|
|
{
|
|
int pos = x + z * CHUNK_CACHE_WIDTH;
|
|
UnsavedChunkList::iterator prev = unsavedChunkList.begin();
|
|
for ( ; prev != unsavedChunkList.end(); ++prev)
|
|
{
|
|
if ((*prev).pos == pos)
|
|
{
|
|
// the chunk has been modified again, so update its time
|
|
(*prev).addedToList = RakNet::GetTimeMS();
|
|
break;
|
|
}
|
|
}
|
|
if (prev == unsavedChunkList.end())
|
|
{
|
|
UnsavedLevelChunk unsaved;
|
|
unsaved.pos = pos;
|
|
unsaved.addedToList = RakNet::GetTimeMS();
|
|
unsaved.chunk = chunk;
|
|
unsavedChunkList.push_back(unsaved);
|
|
}
|
|
chunk->unsaved = false; // not actually saved, but in our working list at least
|
|
}
|
|
}
|
|
}
|
|
|
|
savePendingUnsavedChunks(2);
|
|
}
|
|
if (tickCount - lastSavedEntitiesTick > (60 * SharedConstants::TicksPerSecond)) {
|
|
saveEntities(level, NULL);
|
|
}
|
|
}
|
|
|
|
void ExternalFileLevelStorage::save(Level* level, LevelChunk* levelChunk)
|
|
{
|
|
if (!regionFile)
|
|
{
|
|
regionFile = new RegionFile(levelPath);
|
|
if (!regionFile->open())
|
|
{
|
|
delete regionFile;
|
|
regionFile = NULL;
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Write chunk
|
|
RakNet::BitStream chunkData;
|
|
chunkData.Write((const char*)levelChunk->getBlockData(), CHUNK_BLOCK_COUNT);
|
|
chunkData.Write((const char*)levelChunk->data.data, CHUNK_BLOCK_COUNT / 2);
|
|
|
|
chunkData.Write((const char*)levelChunk->skyLight.data, CHUNK_BLOCK_COUNT / 2);
|
|
chunkData.Write((const char*)levelChunk->blockLight.data, CHUNK_BLOCK_COUNT / 2);
|
|
|
|
chunkData.Write((const char*)levelChunk->updateMap, CHUNK_COLUMNS);
|
|
|
|
regionFile->writeChunk(levelChunk->x, levelChunk->z, chunkData);
|
|
|
|
// Write entities
|
|
|
|
//LOGI("Saved chunk (%d, %d)\n", levelChunk->x, levelChunk->z);
|
|
|
|
}
|
|
|
|
LevelChunk* ExternalFileLevelStorage::load(Level* level, int x, int z)
|
|
{
|
|
if (!regionFile)
|
|
{
|
|
regionFile = new RegionFile(levelPath);
|
|
if (!regionFile->open())
|
|
{
|
|
delete regionFile;
|
|
regionFile = NULL;
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
RakNet::BitStream* chunkData = NULL;
|
|
if (!regionFile->readChunk(x, z, &chunkData))
|
|
{
|
|
//LOGI("Failed to read data for %d, %d\n", x, z);
|
|
return NULL;
|
|
}
|
|
|
|
chunkData->ResetReadPointer();
|
|
|
|
unsigned char* blockIds = new unsigned char[CHUNK_BLOCK_COUNT];
|
|
chunkData->Read((char*)blockIds, CHUNK_BLOCK_COUNT);
|
|
|
|
LevelChunk* levelChunk = new LevelChunk(level, blockIds, x, z);
|
|
chunkData->Read((char*)levelChunk->data.data, CHUNK_BLOCK_COUNT / 2);
|
|
if (loadedStorageVersion >= ChunkVersion_Light) {
|
|
chunkData->Read((char*)levelChunk->skyLight.data, CHUNK_BLOCK_COUNT / 2);
|
|
chunkData->Read((char*)levelChunk->blockLight.data, CHUNK_BLOCK_COUNT / 2);
|
|
}
|
|
chunkData->Read((char*)levelChunk->updateMap, CHUNK_COLUMNS);
|
|
// This will be difficult to maintain.. Storage version could be per chunk
|
|
// too (but probably better to just read all -> write all, so that all
|
|
// chunks got same version anyway)
|
|
//if (loadedStorageVersion >= ChunkVersion_Entity) {
|
|
// int dictSize;
|
|
// chunkData->Read(dictSize);
|
|
|
|
// RakDataInput dis(*chunkData);
|
|
// Tag* tmp = Tag::readNamedTag(&dis);
|
|
// if (tmp && tmp->getId() == Tag::TAG_Compound) {
|
|
// CompoundTag* tag = (CompoundTag*) tmp;
|
|
|
|
// delete tmp;
|
|
// }
|
|
//}
|
|
|
|
delete [] chunkData->GetData();
|
|
delete chunkData;
|
|
|
|
//bool dbg = (x == 7 && z == 9);
|
|
|
|
//int t = 0;
|
|
//for (int i = 0; i < CHUNK_COLUMNS; ++i) {
|
|
// char bits = levelChunk->updateMap[i];
|
|
// t += (bits != 0);
|
|
// int xx = x * 16 + i%16;
|
|
// int zz = z * 16 + i/16;
|
|
// if (dbg && xx == 125 && zz == 152) {
|
|
// LOGI("xz: %d, %d: %d\n", xx, zz, bits);
|
|
// for (int j = 0; j < 8; ++j) {
|
|
// if (bits & (1 << j)) {
|
|
// LOGI("%d - %d\n", j << 4, ((j+1) << 4) - 1);
|
|
// }
|
|
// }
|
|
// }
|
|
//}
|
|
|
|
//
|
|
// Convert LevelChunks here if necessary
|
|
//
|
|
//LOGI("level version: %d: upd: %d - (%d, %d)\n", loadedStorageVersion, t, x, z);
|
|
bool changed = false;
|
|
|
|
// Loaded level has old Cloth types (one Tile* per color)
|
|
if (loadedStorageVersion == 1)
|
|
changed |= LevelConverters::v1_ClothIdToClothData(levelChunk);
|
|
|
|
// Loaded level is newer than our level - replace all unavailable block types
|
|
//if (loadedStorageVersion > SharedConstants::StorageVersion)
|
|
changed |= LevelConverters::ReplaceUnavailableBlocks(levelChunk);
|
|
|
|
levelChunk->recalcHeightmap();
|
|
levelChunk->unsaved = changed;
|
|
levelChunk->terrainPopulated = true;
|
|
levelChunk->createdFromSave = true;
|
|
|
|
return levelChunk;
|
|
}
|
|
|
|
void ExternalFileLevelStorage::saveEntities( Level* level, LevelChunk* levelChunk )
|
|
{
|
|
lastSavedEntitiesTick = tickCount;
|
|
int count = 0;
|
|
float st = getTimeS();
|
|
|
|
// Version 1: Save ALL Entities for all chunks in one structure
|
|
EntityList& entities = level->entities;
|
|
|
|
ListTag* entityTags = new ListTag();
|
|
for (unsigned int i = 0; i < entities.size(); ++i) {
|
|
Entity* e = entities[i];
|
|
|
|
CompoundTag* tag = new CompoundTag();
|
|
if (e->save(tag)) {
|
|
count++;
|
|
entityTags->add(tag);
|
|
} else
|
|
delete tag;
|
|
}
|
|
|
|
// Version 1: Save ALL TileEntities for all chunks in one structure
|
|
TileEntityList& tileEntities = level->tileEntities;
|
|
//TileEntityList keep, dontKeep;
|
|
//partitionTileEntities
|
|
|
|
ListTag* tileEntityTags = new ListTag();
|
|
for (unsigned int i = 0; i < tileEntities.size(); ++i) {
|
|
TileEntity* e = tileEntities[i];
|
|
if (!e->shouldSave()) continue;
|
|
|
|
CompoundTag* tag = new CompoundTag();
|
|
if (e->save(tag)) {
|
|
count++;
|
|
tileEntityTags->add(tag);
|
|
} else
|
|
delete tag;
|
|
}
|
|
|
|
CompoundTag base;
|
|
base.put("Entities", entityTags);
|
|
base.put("TileEntities", tileEntityTags);
|
|
|
|
RakNet::BitStream stream;
|
|
RakDataOutput dos(stream);
|
|
NbtIo::write(&base, &dos);
|
|
int numBytes = stream.GetNumberOfBytesUsed();
|
|
|
|
FILE* fp = fopen((levelPath + "/entities.dat").c_str(), "wb");
|
|
if (fp) {
|
|
int version = 1;
|
|
fwrite("ENT\0", 1, 4, fp);
|
|
fwrite(&version, sizeof(int), 1, fp);
|
|
fwrite(&numBytes, sizeof(int), 1, fp);
|
|
fwrite(stream.GetData(), 1, numBytes, fp);
|
|
fclose(fp);
|
|
}
|
|
|
|
base.deleteChildren();
|
|
|
|
float tt = getTimeS() - st;
|
|
LOGI("Time to save %d entities: %f s. Size: %d bytes\n", count, tt, numBytes);
|
|
}
|
|
|
|
void ExternalFileLevelStorage::loadEntities(Level* level, LevelChunk* chunk) {
|
|
lastSavedEntitiesTick = tickCount;
|
|
FILE* fp = fopen((levelPath + "/entities.dat").c_str(), "rb");
|
|
if (fp) {
|
|
char header[5];
|
|
int version, numBytes;
|
|
fread(header, 1, 4, fp);
|
|
fread(&version, sizeof(int), 1, fp);
|
|
fread(&numBytes, sizeof(int), 1, fp);
|
|
|
|
int left = getRemainingFileSize(fp);
|
|
if (numBytes <= left && numBytes > 0) {
|
|
unsigned char* buf = new unsigned char[numBytes];
|
|
|
|
fread(buf, 1, numBytes, fp);
|
|
|
|
RakNet::BitStream stream(buf, numBytes, false);
|
|
RakDataInput dis(stream);
|
|
|
|
CompoundTag* tag = NbtIo::read(&dis);
|
|
//
|
|
// Read Entity:es
|
|
//
|
|
if (tag->contains("Entities", Tag::TAG_List)) {
|
|
ListTag* entityTags = tag->getList("Entities");
|
|
for (int i = 0; i < entityTags->size(); ++i) {
|
|
Tag* _et = entityTags->get(i);
|
|
if (!_et || _et->getId() != Tag::TAG_Compound) {
|
|
LOGE("Entity tag is either NULL or not a compoundTag: %p : %d!\n", _et, _et?_et->getId() : -1);
|
|
continue;
|
|
}
|
|
CompoundTag* et = (CompoundTag*)_et;
|
|
if (Entity* e = EntityFactory::loadEntity(et, level)) {
|
|
level->addEntity(e);
|
|
}
|
|
}
|
|
}
|
|
//
|
|
// Read TileEntity:s
|
|
//
|
|
if (tag->contains("TileEntities", Tag::TAG_List)) {
|
|
ListTag* tileEntityTags = tag->getList("TileEntities");
|
|
for (int i = 0; i < tileEntityTags->size(); ++i) {
|
|
Tag* _et = tileEntityTags->get(i);
|
|
if (!_et || _et->getId() != Tag::TAG_Compound) {
|
|
LOGE("TileEntity tag is either NULL or not a compoundTag: %p : %d!\n", _et, _et?_et->getId() : -1);
|
|
continue;
|
|
}
|
|
CompoundTag* et = (CompoundTag*)_et;
|
|
if (TileEntity* e = TileEntity::loadStatic(et)) {
|
|
|
|
LevelChunk* chunk = level->getChunkAt(e->x, e->z);
|
|
if (chunk && !chunk->hasTileEntityAt(e)) {
|
|
LOGI("Adding TileEntity %d to %d, %d, %d\n", e->type, e->x, e->y, e->z);
|
|
chunk->addTileEntity(e);
|
|
} else {
|
|
if (!chunk)
|
|
LOGE("Couldn't find chunk at %d, %d to add %d\n", e->x, e->z, e->type);
|
|
else
|
|
LOGE("Already have TileEntity at %d, %d to add %d\n", e->x, e->z, e->type);
|
|
delete e;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
tag->deleteChildren();
|
|
delete tag;
|
|
|
|
delete[] buf;
|
|
}
|
|
LOGI("header: %s, version: %d, bytes: %d (remaining: %d)\n", header, version, numBytes, left);
|
|
|
|
//fread(stream.GetData(), 1, numBytes, fp);
|
|
fclose(fp);
|
|
}
|
|
}
|
|
|
|
void ExternalFileLevelStorage::saveGame(Level* level) {
|
|
saveEntities(level, NULL);
|
|
}
|
|
|
|
int ExternalFileLevelStorage::savePendingUnsavedChunks( int maxCount ) {
|
|
if (maxCount < 0)
|
|
maxCount = unsavedChunkList.size();
|
|
|
|
int count = 0;
|
|
while (++count <= maxCount && !unsavedChunkList.empty()) {
|
|
|
|
UnsavedChunkList::iterator it = unsavedChunkList.begin();
|
|
UnsavedChunkList::iterator remove = unsavedChunkList.begin();
|
|
UnsavedLevelChunk* oldest = &(*it);
|
|
|
|
for ( ; it != unsavedChunkList.end(); ++it) {
|
|
if ((*it).addedToList < oldest->addedToList) {
|
|
oldest = &(*it);
|
|
remove = it;
|
|
}
|
|
}
|
|
LevelChunk* chunk = oldest->chunk;
|
|
unsavedChunkList.erase(remove);
|
|
|
|
save(level, chunk);
|
|
}
|
|
return count;
|
|
}
|
|
|
|
void ExternalFileLevelStorage::saveAll( Level* level, std::vector<LevelChunk*>& levelChunks ) {
|
|
ChunkStorage::saveAll(level, levelChunks);
|
|
int numChunks = savePendingUnsavedChunks(-1);
|
|
LOGI("Saving %d additional chunks.\n", numChunks);
|
|
}
|
|
|
|
#endif /*DEMO_MODE*/
|