the whole game

This commit is contained in:
2026-03-02 22:04:18 +03:00
parent 816e9060b4
commit f0617a5d22
2069 changed files with 581500 additions and 0 deletions

View File

@@ -0,0 +1,632 @@
#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 "../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 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 )
{
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());
}
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 % 50) == 0 && level)
{
// 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*/

View File

@@ -0,0 +1,88 @@
#if !defined(DEMO_MODE) && !defined(APPLE_DEMO_PROMOTION)
#ifndef NET_MINECRAFT_WORLD_LEVEL_STORAGE__ExternalFileLevelStorage_H__
#define NET_MINECRAFT_WORLD_LEVEL_STORAGE__ExternalFileLevelStorage_H__
//package net.minecraft.world.level.storage;
#include <vector>
#include <list>
//#include "com/mojang/nbt/CompoundTag.h"
#include "LevelStorage.h"
#include "../chunk/storage/ChunkStorage.h"
class Player;
class Dimension;
class RegionFile;
typedef struct UnsavedLevelChunk
{
int pos;
RakNet::TimeMS addedToList;
LevelChunk* chunk;
} UnsavedLevelChunk;
typedef std::list<UnsavedLevelChunk> UnsavedChunkList;
/*public*/
class ExternalFileLevelStorage:
public LevelStorage,
public ChunkStorage
//public PlayerIO
{
public:
ExternalFileLevelStorage(const std::string& levelId, const std::string& fullPath);
virtual ~ExternalFileLevelStorage();
LevelData* prepareLevel(Level* level);
//throws LevelConflictException
void checkSession() {}
ChunkStorage* createChunkStorage(Dimension* dimension) { return this; }
void saveLevelData(LevelData& levelData, std::vector<Player*>* players);
// PlayerIO getPlayerIO() { return this; }
// CompoundTag loadPlayerDataTag(std::string playerName) { return NULL; }
void closeAll() {}
static bool readLevelData(const std::string& directory, LevelData& dest);
static bool readPlayerData(const std::string& filename, LevelData& dest);
static bool writeLevelData(const std::string& datFileName, LevelData& dest, const std::vector<Player*>* players);
static void saveLevelData(const std::string& directory, LevelData& levelData, std::vector<Player*>* players);
int savePendingUnsavedChunks(int maxCount);
//
// ChunkStorage methods
//
virtual LevelChunk* load(Level* level, int x, int z);
void save(Level* level, LevelChunk* levelChunk);
// @note, loadEntities and saveEntities dont use second parameter
void loadEntities(Level* level, LevelChunk* levelChunk);
void saveEntities(Level* level, LevelChunk* levelChunk);
void saveGame(Level* level);
void saveAll(Level* level, std::vector<LevelChunk*>& levelChunks);
virtual void tick();
virtual void flush() {}
private:
std::string levelId;
std::string levelPath;
LevelData* loadedLevelData;
RegionFile* regionFile;
RegionFile* entitiesFile;
Level* level;
int tickCount;
int loadedStorageVersion;
UnsavedChunkList unsavedChunkList;
int lastSavedEntitiesTick;
};
#endif /*NET_MINECRAFT_WORLD_LEVEL_STORAGE__ExternalFileLevelStorage_H__*/
#endif /*DEMO_MODE*/

View File

@@ -0,0 +1,193 @@
#if !defined(DEMO_MODE) && !defined(APPLE_DEMO_PROMOTION)
#include "LevelData.h"
#include "ExternalFileLevelStorageSource.h"
#include "ExternalFileLevelStorage.h"
#include "FolderMethods.h"
#include "../../../platform/file.h"
#include "../../../util/StringUtils.h"
#include <cstdio>
#include <sys/types.h>
#ifdef __APPLE__
#include "MoveFolder.h"
#endif
static const char ILLEGAL_FILE_CHARACTERS[] = {
'/', '\n', '\r', '\t', '\0', '\f', '`', '?', '*', '\\', '<', '>', '|', '\"', ':'
};
ExternalFileLevelStorageSource::ExternalFileLevelStorageSource(const std::string& externalPath, const std::string& temporaryFilesPath)
: _hasTempDirectory(temporaryFilesPath != externalPath)
{
#ifndef STANDALONE_SERVER
const char* p0 = "/games";
const char* p1 = "/com.mojang";
const char* p2 = "/minecraftWorlds";
const char* tree[] = {
p0, p1, p2
};
int treeLength = sizeof(tree) / sizeof(tree[0]);
createTree(externalPath.c_str(), tree, treeLength);
if (hasTempDirectory())
createTree(temporaryFilesPath.c_str(), tree, treeLength);
basePath = externalPath + p0 + p1 + p2;
tmpBasePath = temporaryFilesPath + p0 + p1 + p2;
#else
basePath = externalPath;
tmpBasePath = temporaryFilesPath;
#endif
}
void ExternalFileLevelStorageSource::addLevelSummaryIfExists(LevelSummaryList& dest, const char* dirName)
{
std::string directory = basePath;
directory += "/";
directory += dirName;
LevelData levelData;
if (ExternalFileLevelStorage::readLevelData(directory, levelData))
{
LevelSummary summary;
summary.id = dirName;
summary.name = levelData.levelName;
summary.lastPlayed = levelData.getLastPlayed();
summary.sizeOnDisk = (unsigned int)levelData.getSizeOnDisk();
summary.gameType = levelData.getGameType();
dest.push_back(summary);
}
}
void ExternalFileLevelStorageSource::getLevelList(LevelSummaryList& dest)
{
#ifdef WIN32
WIN32_FIND_DATAA fileData;
HANDLE hFind;
std::string searchString = basePath;
searchString += "/*";
hFind = FindFirstFileA(searchString.c_str(), &fileData);
if (hFind != INVALID_HANDLE_VALUE) {
do {
if (fileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
addLevelSummaryIfExists(dest, fileData.cFileName);
} while (FindNextFileA(hFind, &fileData));
FindClose(hFind);
}
#else
DIR *dp;
struct dirent *dirp;
if((dp = opendir(basePath.c_str())) == NULL) {
LOGI("Error listing base folder %s: %d", basePath.c_str(), _errno());
return;
}
while ((dirp = readdir(dp)) != NULL) {
if (dirp->d_type == DT_DIR)
{
addLevelSummaryIfExists(dest, dirp->d_name);
}
}
closedir(dp);
#endif
}
LevelStorage* ExternalFileLevelStorageSource::selectLevel(const std::string& levelId, bool createPlayerDir) {
return new ExternalFileLevelStorage(levelId, getFullPath(levelId));
}
void ExternalFileLevelStorageSource::deleteLevel( const std::string& levelId )
{
std::string path = getFullPath(levelId);
if (!DeleteDirectory(path)) { // If we couldn't delete whole folder, try to remove chunks..
remove((path + "/chunks.dat").c_str());
remove((path + "/player.dat").c_str());
remove((path + "/level.dat").c_str());
}
}
static std::string getUniqueLevelName(const LevelSummaryList& levels, const std::string& level )
{
std::set<std::string> Set;
for (unsigned int i = 0; i < levels.size(); ++i)
Set.insert(levels[i].id);
std::string s = level;
while ( Set.find(s) != Set.end() )
s += "-";
return s;
}
void ExternalFileLevelStorageSource::renameLevel( const std::string& oldLevelId_, const std::string& newLevelName_ )
{
#define _renFULLPATH(s) ((basePath + "/" + s).c_str())
bool isTempFile = (TempLevelId == oldLevelId_);
std::string oldFolder = getFullPath(oldLevelId_);
if (_access(oldFolder.c_str(), 0) != 0) {
LOGI("Couldn't access %s\n", oldFolder.c_str());
return;
}
std::string levelName = Util::stringTrim(newLevelName_);
std::string levelId = levelName;
for (int i = 0; i < sizeof(ILLEGAL_FILE_CHARACTERS) / sizeof(char); ++i)
levelId = Util::stringReplace(levelId, std::string(1, ILLEGAL_FILE_CHARACTERS[i]), "");
LevelSummaryList levels;
getLevelList(levels);
levelId = getUniqueLevelName(levels, levelId);
bool couldRename = false;
if (hasTempDirectory() && isTempFile) {
#ifdef __APPLE__
std::string newFolder = basePath + "/" + levelId;
moveFolder(oldFolder, newFolder);
couldRename = (_access(newFolder.c_str(), 0) == 0);
#endif
}
couldRename = couldRename || rename(_renFULLPATH(oldLevelId_), _renFULLPATH(levelId)) == 0;
if (!couldRename) // != 0: fail
levelId = oldLevelId_; // Try to rewrite the level name anyway
// Rename the level name and write back to file
LevelData levelData;
ExternalFileLevelStorage::readLevelData(_renFULLPATH(levelId), levelData);
levelData.setLevelName(newLevelName_);
ExternalFileLevelStorage::saveLevelData(_renFULLPATH(levelId), levelData, NULL);
}
std::string ExternalFileLevelStorageSource::getName()
{
return "External File Level Storage";
}
LevelData* ExternalFileLevelStorageSource::getDataTagFor( const std::string& levelId )
{
return NULL;
}
bool ExternalFileLevelStorageSource::isNewLevelIdAcceptable( const std::string& levelId )
{
return true;
}
std::string ExternalFileLevelStorageSource::getFullPath(const std::string& levelId) {
return ((TempLevelId == levelId)? tmpBasePath : basePath) + "/" + levelId;
}
#endif /*DEMO_MODE*/

View File

@@ -0,0 +1,45 @@
#if !defined(DEMO_MODE) && !defined(APPLE_DEMO_PROMOTION)
#ifndef NET_MINECRAFT_WORLD_LEVEL_STORAGE__ExternalFileLevelStorageSource_H__
#define NET_MINECRAFT_WORLD_LEVEL_STORAGE__ExternalFileLevelStorageSource_H__
//package net.minecraft.world.level.storage;
#include "LevelStorageSource.h"
#include "MemoryLevelStorage.h"
class ProgressListener;
class ExternalFileLevelStorageSource: public LevelStorageSource
{
public:
ExternalFileLevelStorageSource(const std::string& externalPath, const std::string& temporaryFilesPath);
std::string getName();
void getLevelList(LevelSummaryList& dest);
LevelStorage* selectLevel(const std::string& levelId, bool createPlayerDir);
LevelData* getDataTagFor(const std::string& levelId);
bool isNewLevelIdAcceptable(const std::string& levelId);
void clearAll() {}
void deleteLevel(const std::string& levelId);
void renameLevel(const std::string& levelId, const std::string& newLevelName);
bool isConvertible(const std::string& levelId) { return false; }
bool requiresConversion(const std::string& levelId) { return false; }
bool convertLevel(const std::string& levelId, ProgressListener* progress) { return false; }
private:
void addLevelSummaryIfExists(LevelSummaryList& dest, const char* dirName);
bool hasTempDirectory() { return _hasTempDirectory; }
std::string getFullPath(const std::string& levelId);
std::string basePath;
std::string tmpBasePath;
bool _hasTempDirectory;
};
#endif /*NET_MINECRAFT_WORLD_LEVEL_STORAGE__ExternalFileLevelStorageSource_H__*/
#endif /*DEMO_MODE*/

View File

@@ -0,0 +1,77 @@
#include "FolderMethods.h"
#include <string>
#ifndef WIN32
#include <unistd.h>
#include <errno.h>
#include <sys/stat.h>
int _mkdir( const char* name ) {
return mkdir(name, 0755);
}
int _access( const char* name, int mode ) {
return access(name, mode);
}
int _errno() {
return errno;
}
#else
#include <io.h>
#include <direct.h>
#include <windows.h>
#endif
bool exists(const char* name) {
return _access(name, 0) == 0;
}
bool createFolderIfNotExists( const char* name ) {
if (exists(name))
return true;
int errorCode = 0;
if ((errorCode = _mkdir(name)) != 0) {
LOGI("FAILED to create folder %s, %d! Escape plan?\n", name, _errno());
return false;
}
LOGI("Created folder %s\n", name);
return true;
}
int getRemainingFileSize( FILE* fp ) {
if (!fp) return 0;
int current = ftell(fp);
fseek(fp, 0, SEEK_END);
int end = ftell(fp);
fseek(fp, current, SEEK_SET);
return end - current;
}
int getFileSize( const char* filename ) {
FILE* fp = fopen(filename, "rb");
if (!fp)
return -1;
fseek(fp, 0, SEEK_END);
int size = ftell(fp);
fclose(fp);
return size;
}
bool createTree( const char* base, const char* tree[], int treeLength ) {
if (!createFolderIfNotExists(base))
return false;
std::string p(base);
for (int i = 0; i < treeLength; ++i && tree[i]) {
p += tree[i];
if (!createFolderIfNotExists(p.c_str()))
return false;
}
return true;
}

View File

@@ -0,0 +1,22 @@
#ifndef _MINECRAFT_FOLDERMETHODS_H_
#define _MINECRAFT_FOLDERMETHODS_H_
#include "../../../platform/log.h"
#ifdef WIN32
#include <io.h>
#else
int _mkdir(const char* name);
int _access(const char* name, int mode);
int _errno();
#endif
bool exists(const char* name);
bool createFolderIfNotExists(const char* name);
int getRemainingFileSize(FILE* fp);
int getFileSize(const char* filename);
bool createTree(const char* base, const char* tree[], int treeLength);
#endif

View File

@@ -0,0 +1,364 @@
#include "LevelData.h"
LevelData::LevelData()
: xSpawn(128),
ySpawn(64),
zSpawn(128),
seed(0),
lastPlayed(0),
generatorVersion(SharedConstants::GeneratorVersion),
time(0),
dimension(Dimension::NORMAL),
playerDataVersion(-1),
storageVersion(0),
gameType(GameType::Default),
loadedPlayerTag(NULL)
{
//LOGI("ctor 1: %p\n", this);
spawnMobs = (gameType == GameType::Survival);
}
LevelData::LevelData( const LevelSettings& settings, const std::string& levelName, int generatorVersion /*= -1*/ )
: seed(settings.getSeed()),
gameType(settings.getGameType()),
levelName(levelName),
xSpawn(128),
ySpawn(64),
zSpawn(128),
lastPlayed(0),
time(0),
dimension(Dimension::NORMAL),
playerDataVersion(-1),
loadedPlayerTag(NULL)
{
//LOGI("ctor 2: %p\n", this);
if (generatorVersion < 0)
generatorVersion = SharedConstants::GeneratorVersion;
this->generatorVersion = generatorVersion;
spawnMobs = (gameType == GameType::Survival);
}
LevelData::LevelData( CompoundTag* tag )
: loadedPlayerTag(NULL)
{
//LOGI("ctor 3: %p (%p)\n", this, tag);
getTagData(tag);
}
LevelData::LevelData( const LevelData& rhs )
: seed(rhs.seed),
gameType(rhs.gameType),
levelName(rhs.levelName),
xSpawn(rhs.xSpawn),
ySpawn(rhs.ySpawn),
zSpawn(rhs.zSpawn),
lastPlayed(rhs.lastPlayed),
time(rhs.time),
dimension(rhs.dimension),
storageVersion(rhs.storageVersion),
playerDataVersion(rhs.playerDataVersion),
generatorVersion(rhs.generatorVersion),
spawnMobs(rhs.spawnMobs),
loadedPlayerTag(NULL),
playerData(rhs.playerData)
{
//LOGI("c-ctor: %p (%p)\n", this, &rhs);
setPlayerTag(rhs.loadedPlayerTag);
//PlayerData playerData;
}
LevelData& LevelData::operator=( const LevelData& rhs )
{
//LOGI("as-op: %p (%p)\n", this, &rhs);
if (this != &rhs) {
seed = rhs.seed;
gameType = rhs.gameType;
levelName = rhs.levelName;
xSpawn = rhs.xSpawn;
ySpawn = rhs.ySpawn;
zSpawn = rhs.zSpawn;
lastPlayed = rhs.lastPlayed;
time = rhs.time;
dimension = rhs.dimension;
spawnMobs = rhs.spawnMobs;
playerData = rhs.playerData;
playerDataVersion = rhs.playerDataVersion;
generatorVersion = rhs.generatorVersion;
storageVersion = rhs.storageVersion;
setPlayerTag(rhs.loadedPlayerTag);
}
return *this;
}
LevelData::~LevelData()
{
//LOGI("dtor: %p\n", this);
setPlayerTag(NULL);
}
void LevelData::v1_write( RakNet::BitStream& bitStream )
{
bitStream.Write(seed);
bitStream.Write(xSpawn);
bitStream.Write(ySpawn);
bitStream.Write(zSpawn);
bitStream.Write(time);
bitStream.Write(sizeOnDisk);
bitStream.Write(getEpochTimeS());
RakNet::RakString rakName = levelName.c_str();
bitStream.Write(rakName);
//LOGI("WBS: %d, %d, %d, %d, %d, %d\n", seed, xSpawn, ySpawn, zSpawn, time, sizeOnDisk);
}
void LevelData::v1_read( RakNet::BitStream& bitStream, int storageVersion )
{
this->storageVersion = storageVersion;
bitStream.Read(seed);
bitStream.Read(xSpawn);
bitStream.Read(ySpawn);
bitStream.Read(zSpawn);
bitStream.Read(time);
bitStream.Read(sizeOnDisk);
bitStream.Read(lastPlayed);
RakNet::RakString rakName;
bitStream.Read(rakName);
levelName = rakName.C_String();
//LOGI("RBS: %d, %d, %d, %d, %d, %d\n", seed, xSpawn, ySpawn, zSpawn, time, sizeOnDisk);
}
// Caller's responsibility to destroy this Tag
CompoundTag* LevelData::createTag()
{
CompoundTag* tag = new CompoundTag();
CompoundTag* player = loadedPlayerTag? (CompoundTag*)loadedPlayerTag->copy() : NULL;
setTagData(tag, player);
return tag;
}
CompoundTag* LevelData::createTag( const std::vector<Player*>& players )
{
CompoundTag* tag = new CompoundTag();
Player* player = NULL;
CompoundTag* playerTag = NULL;
if (!players.empty()) player = players[0];
if (player != NULL) {
playerTag = new CompoundTag();
player->saveWithoutId(playerTag);
}
setTagData(tag, playerTag);
return tag;
}
void LevelData::setTagData( CompoundTag* tag, CompoundTag* playerTag )
{
if (!tag) return;
tag->putLong("RandomSeed", seed);
tag->putInt("GameType", gameType);
tag->putInt("SpawnX", xSpawn);
tag->putInt("SpawnY", ySpawn);
tag->putInt("SpawnZ", zSpawn);
tag->putLong("Time", time);
tag->putLong("SizeOnDisk", sizeOnDisk);
tag->putLong("LastPlayed", getEpochTimeS());
tag->putString("LevelName", levelName);
tag->putInt("StorageVersion", storageVersion);
tag->putInt("Platform", 2);
if (playerTag != NULL) {
tag->putCompound("Player", playerTag);
}
}
void LevelData::getTagData( const CompoundTag* tag )
{
if (!tag) return;
seed = (long)tag->getLong("RandomSeed");
gameType = tag->getInt("GameType");
xSpawn = tag->getInt("SpawnX");
ySpawn = tag->getInt("SpawnY");
zSpawn = tag->getInt("SpawnZ");
time = (long)tag->getLong("Time");
lastPlayed = (int)tag->getLong("LastPlayed");
sizeOnDisk = (int)tag->getLong("SizeOnDisk");
levelName = tag->getString("LevelName");
storageVersion = tag->getInt("StorageVersion");
spawnMobs = (gameType == GameType::Survival);
if (tag->contains("Player", Tag::TAG_Compound)) {
setPlayerTag(tag->getCompound("Player"));
//dimension = loadedPlayerTag.getInt("Dimension");
}
}
void LevelData::setPlayerTag( CompoundTag* tag )
{
if (loadedPlayerTag) {
loadedPlayerTag->deleteChildren();
delete loadedPlayerTag;
loadedPlayerTag = NULL;
}
if (tag)
loadedPlayerTag = (CompoundTag*)tag->copy();
}
long LevelData::getSeed() const
{
return seed;
}
int LevelData::getXSpawn() const
{
return xSpawn;
}
int LevelData::getYSpawn() const
{
return ySpawn;
}
int LevelData::getZSpawn() const
{
return zSpawn;
}
long LevelData::getTime() const
{
return time;
}
long LevelData::getSizeOnDisk() const
{
return sizeOnDisk;
}
CompoundTag* LevelData::getLoadedPlayerTag()
{
return loadedPlayerTag;
}
void LevelData::setLoadedPlayerTo( Player* p )
{
if (playerDataVersion == 1)
playerData.loadPlayer(p);
}
int LevelData::getDimension()
{
return dimension;
}
void LevelData::setSeed( long seed )
{
this->seed = seed;
}
void LevelData::setXSpawn( int xSpawn )
{
this->xSpawn = xSpawn;
}
void LevelData::setYSpawn( int ySpawn )
{
this->ySpawn = ySpawn;
}
void LevelData::setZSpawn( int zSpawn )
{
this->zSpawn = zSpawn;
}
void LevelData::setTime( long time )
{
this->time = time;
}
void LevelData::setSizeOnDisk( long sizeOnDisk )
{
this->sizeOnDisk = sizeOnDisk;
}
void LevelData::setLoadedPlayerTag( CompoundTag* playerTag )
{
LOGI("set-p: %p (%p <- %p)\n", this, loadedPlayerTag, playerTag);
if (loadedPlayerTag) {
loadedPlayerTag->deleteChildren();
delete loadedPlayerTag;
}
loadedPlayerTag = playerTag;
}
void LevelData::setDimension( int dimension )
{
this->dimension = dimension;
}
void LevelData::setSpawn( int xSpawn, int ySpawn, int zSpawn )
{
this->xSpawn = xSpawn;
this->ySpawn = ySpawn;
this->zSpawn = zSpawn;
}
std::string LevelData::getLevelName()
{
return levelName;
}
void LevelData::setLevelName( const std::string& levelName )
{
this->levelName = levelName;
}
int LevelData::getGeneratorVersion() const
{
return generatorVersion;
}
void LevelData::setGeneratorVersion( int version )
{
this->generatorVersion = version;
}
long LevelData::getLastPlayed() const
{
return lastPlayed;
}
int LevelData::getStorageVersion() const
{
return storageVersion;
}
void LevelData::setStorageVersion( int version )
{
storageVersion = version;
}
int LevelData::getGameType() const
{
return gameType;
}
void LevelData::setGameType( int type )
{
gameType = type;
}
bool LevelData::getSpawnMobs() const
{
return spawnMobs;
}
void LevelData::setSpawnMobs( bool doSpawn )
{
spawnMobs = doSpawn;
}

View File

@@ -0,0 +1,97 @@
#ifndef NET_MINECRAFT_WORLD_LEVEL_STORAGE__LevelData_H__
#define NET_MINECRAFT_WORLD_LEVEL_STORAGE__LevelData_H__
//package net.minecraft.world.level.storage;
#include <string>
#include "PlayerData.h"
#include "../LevelSettings.h"
#include "../dimension/Dimension.h"
// sorry for RakNet dependency, but I really like using BitStream
#include "../../../raknet/BitStream.h"
#include "../../../platform/time.h"
#include "../../../nbt/CompoundTag.h"
class LevelData
{
public:
LevelData();
LevelData(const LevelSettings& settings, const std::string& levelName, int generatorVersion = -1);
LevelData(CompoundTag* tag);
LevelData(const LevelData& rhs);
LevelData& operator=(const LevelData& rhs);
~LevelData();
void v1_write(RakNet::BitStream& bitStream);
void v1_read(RakNet::BitStream& bitStream, int storageVersion);
// Caller's responsibility to destroy this Tag
CompoundTag* createTag();
CompoundTag* createTag(const std::vector<Player*>& players);
void getTagData(const CompoundTag* tag);
void setTagData(CompoundTag* tag, CompoundTag* playerTag);
long getSeed() const;
int getXSpawn() const;
int getYSpawn() const;
int getZSpawn() const;
long getTime() const;
long getSizeOnDisk() const;
void setPlayerTag(CompoundTag* tag);
CompoundTag* getLoadedPlayerTag();
void setLoadedPlayerTo(Player* p);
int getDimension();
void setSeed(long seed);
void setXSpawn(int xSpawn);
void setYSpawn(int ySpawn);
void setZSpawn(int zSpawn);
void setSpawn(int xSpawn, int ySpawn, int zSpawn);
void setTime(long time);
void setSizeOnDisk(long sizeOnDisk);
void setLoadedPlayerTag(CompoundTag* playerTag);
void setDimension(int dimension);
std::string getLevelName();
void setLevelName(const std::string& levelName);
int getGeneratorVersion() const;
void setGeneratorVersion(int version);
long getLastPlayed() const;
int getStorageVersion() const;
void setStorageVersion(int version);
int getGameType() const;
void setGameType(int type);
bool getSpawnMobs() const;
void setSpawnMobs(bool doSpawn);
public:
PlayerData playerData;
int playerDataVersion;
std::string levelName;
private:
long seed;
int xSpawn;
int ySpawn;
int zSpawn;
long time;
int lastPlayed;
long sizeOnDisk;
CompoundTag* loadedPlayerTag;
int dimension;
int gameType;
int storageVersion;
bool spawnMobs;
//@note: This version is never written or loaded to disk. The only purpose
// is to use it in the level generator on server and clients.
int generatorVersion;
};
#endif /*NET_MINECRAFT_WORLD_LEVEL_STORAGE__LevelData_H__*/

View File

@@ -0,0 +1,39 @@
#ifndef NET_MINECRAFT_WORLD_LEVEL_STORAGE__LevelStorage_H__
#define NET_MINECRAFT_WORLD_LEVEL_STORAGE__LevelStorage_H__
//package net.minecraft.world.level.storage;
#include <vector>
#include <string>
class LevelData;
class ChunkStorage;
class Dimension;
class Player;
class Level;
class LevelChunk;
class LevelStorage
{
public:
virtual ~LevelStorage() {}
virtual LevelData* prepareLevel(Level* level) = 0;
virtual ChunkStorage* createChunkStorage(Dimension* dimension) = 0;
virtual void saveLevelData(LevelData& levelData, std::vector<Player*>* players) = 0;
virtual void saveLevelData(LevelData& levelData) {
saveLevelData(levelData, NULL);
}
virtual void closeAll() = 0;
virtual void saveGame(Level* level) {}
virtual void loadEntities(Level* level, LevelChunk* levelChunk) {}
//void checkSession() throws LevelConflictException;
//PlayerIO getPlayerIO();
};
#endif /*NET_MINECRAFT_WORLD_LEVEL_STORAGE__LevelStorage_H__*/

View File

@@ -0,0 +1,3 @@
#include "LevelStorageSource.h"
const std::string LevelStorageSource::TempLevelId = "_LastJoinedServer";

View File

@@ -0,0 +1,60 @@
#ifndef NET_MINECRAFT_WORLD_LEVEL_STORAGE__LevelStorageSource_H__
#define NET_MINECRAFT_WORLD_LEVEL_STORAGE__LevelStorageSource_H__
//package net.minecraft.world.level.storage;
#include <string>
#include <vector>
class ProgressListener;
class LevelData;
class LevelStorage;
struct LevelSummary
{
std::string id;
std::string name;
int lastPlayed;
int gameType;
unsigned int sizeOnDisk;
bool operator<(const LevelSummary& rhs) const {
return lastPlayed > rhs.lastPlayed;
}
};
typedef std::vector<LevelSummary> LevelSummaryList;
class LevelStorageSource
{
public:
static const std::string TempLevelId;
virtual ~LevelStorageSource() {}
virtual std::string getName() = 0;
virtual void getLevelList(LevelSummaryList& dest) {};
virtual LevelData* getDataTagFor(const std::string& levelId) = 0;
virtual LevelStorage* selectLevel(const std::string& levelId, bool createPlayerDir) = 0;
/**
* Tests if a levelId can be used to store a level. For example, a levelId
* can't be called COM1 on Windows systems, because that is a reserved file
* handle.
* <p>
* Also, a new levelId may not overwrite an existing one.
*
* @param levelId
* @return
*/
virtual bool isNewLevelIdAcceptable(const std::string& levelId) = 0;
virtual void clearAll() = 0;
virtual void deleteLevel(const std::string& levelId) = 0;
virtual void renameLevel(const std::string& levelId, const std::string& newLevelName) = 0;
virtual bool isConvertible(const std::string& levelId) = 0;
virtual bool requiresConversion(const std::string& levelId) = 0;
virtual bool convertLevel(const std::string& levelId, ProgressListener* progress) = 0;
};
#endif /*NET_MINECRAFT_WORLD_LEVEL_STORAGE__LevelStorageSource_H__*/

View File

@@ -0,0 +1,53 @@
#ifndef NET_MINECRAFT_WORLD_LEVEL_STORAGE__MemoryLevelStorage_H__
#define NET_MINECRAFT_WORLD_LEVEL_STORAGE__MemoryLevelStorage_H__
//package net.minecraft.world.level.storage;
#include <vector>
#include "LevelStorage.h"
#include "../chunk/storage/MemoryChunkStorage.h"
class Player;
class Dimension;
class MemoryLevelStorage: public LevelStorage //public PlayerIO
{
public:
MemoryLevelStorage()
: _storage(NULL)
{}
~MemoryLevelStorage() {
delete _storage;
}
LevelData* prepareLevel(Level* level) {
return NULL;
}
void checkSession() //throws LevelConflictException
{}
ChunkStorage* createChunkStorage(Dimension* dimension) {
if (_storage) {
LOGW(">WARNING< Creating a MemoryChunkStorage over another (#%p). A memory leak will occur.\n", _storage);
}
return _storage = new MemoryChunkStorage();
}
void saveLevelData(LevelData& levelData, std::vector<Player*>* players) {}
void closeAll() {}
void save(Player* player) {}
void load(Player* player) {}
/* CompoundTag loadPlayerDataTag(std::string playerName) {
return NULL;
} */
MemoryChunkStorage* _storage;
};
#endif /*NET_MINECRAFT_WORLD_LEVEL_STORAGE__MemoryLevelStorage_H__*/

View File

@@ -0,0 +1,58 @@
#ifndef NET_MINECRAFT_WORLD_LEVEL_STORAGE__MemoryLevelStorageSource_H__
#define NET_MINECRAFT_WORLD_LEVEL_STORAGE__MemoryLevelStorageSource_H__
//package net.minecraft.world.level.storage;
#include "LevelStorageSource.h"
#include "MemoryLevelStorage.h"
class ProgressListener;
class MemoryLevelStorageSource: public LevelStorageSource
{
public:
MemoryLevelStorageSource() {
}
std::string getName() {
return "Memory Storage";
}
LevelStorage* selectLevel(const std::string& levelId, bool createPlayerDir) {
return new MemoryLevelStorage();
}
//List<LevelSummary> getLevelList() {
// return /*new*/ ArrayList<LevelSummary>();
//}
void clearAll() {
}
LevelData* getDataTagFor(const std::string& levelId) {
return NULL;
}
bool isNewLevelIdAcceptable(const std::string& levelId) {
return true;
}
void deleteLevel(const std::string& levelId) {
}
void renameLevel(const std::string& levelId, const std::string& newLevelName) {
}
bool isConvertible(const std::string& levelId) {
return false;
}
bool requiresConversion(const std::string& levelId) {
return false;
}
bool convertLevel(const std::string& levelId, ProgressListener* progress) {
return false;
}
};
#endif /*NET_MINECRAFT_WORLD_LEVEL_STORAGE__MemoryLevelStorageSource_H__*/

View File

@@ -0,0 +1,8 @@
#ifndef MOVEFOLDER_H__
#define MOVEFOLDER_H__
#include <string>
void moveFolder(const std::string& src, const std::string& dst);
#endif /*MOVEFOLDER_H__*/

View File

@@ -0,0 +1,11 @@
#include "MoveFolder.h"
void moveFolder(const std::string& src, const std::string& dst) {
NSError* error = NULL;
[[NSFileManager defaultManager]
moveItemAtPath:[NSString stringWithUTF8String:src.c_str()]
toPath:[NSString stringWithUTF8String:dst.c_str()] error:&error];
if (error)
NSLog(@"Error: %@\n: %@\n", [error description], [error debugDescription]);
}

View File

@@ -0,0 +1,66 @@
#ifndef NET_MINECRAFT_WORLD_LEVEL_STORAGE__PlayerData_H__
#define NET_MINECRAFT_WORLD_LEVEL_STORAGE__PlayerData_H__
#include "../../entity/player/Player.h"
#include "../../entity/player/Inventory.h"
#include "../../phys/Vec3.h"
#include "../../../util/Mth.h"
static float clampRot(float r) {
return Mth::clamp(r, -100000.0f, 100000.0f);
}
static float clampXZ(float r) {
return Mth::clamp(r, 4.0f, 252.0f);
}
static float clampY(float r) {
return Mth::clamp(r, 4.0f, 126.0f);
}
class PlayerData {
public:
void loadPlayer(Player* p) const {
p->setPos(0, 0, 0);
p->x = p->xo = p->xOld = clampXZ(pos.x);
p->y = p->yo = p->yOld = clampY (pos.y);
p->z = p->zo = p->zOld = clampXZ(pos.z);
float motionX = motion.x;
float motionY = motion.y;
float motionZ = motion.z;
if (Mth::abs(motionX) > 10.0f) motionX = 0;
if (Mth::abs(motionY) > 10.0f) motionY = 0;
if (Mth::abs(motionZ) > 10.0f) motionZ = 0;
p->xd = motionX;
p->yd = motionY;
p->zd = motionZ;
p->xRot = p->xRotO = clampRot(xRot);
p->yRot = p->yRotO = clampRot(yRot);
p->fallDistance = fallDistance;
p->onFire = onFire;
p->airSupply = airSupply;
p->onGround = onGround;
p->setPos(pos.x, pos.y, pos.z);
//@deprecated
//for (int i = 0; i < Inventory::MAX_SELECTION_SIZE; ++i)
//p->inventory->setSelectionSlotItemId(i, inventorySlots[i]);
}
Vec3 pos;
Vec3 motion;
float xRot, yRot;
float fallDistance;
short onFire;
short airSupply;
bool onGround;
int inventorySlots[Inventory::MAX_SELECTION_SIZE];
};
#endif /*NET_MINECRAFT_WORLD_LEVEL_STORAGE__PlayerData_H__*/

View File

@@ -0,0 +1,216 @@
#include "RegionFile.h"
#include "../../../platform/log.h"
const int SECTOR_BYTES = 4096;
const int SECTOR_INTS = SECTOR_BYTES / 4;
const int SECTOR_COLS = 32;
static const char* const REGION_DAT_NAME = "chunks.dat";
static void logAssert(int actual, int expected) {
if (actual != expected) {
LOGI("ERROR: I/O operation failed (%d vs %d)\n", actual, expected);
}
}
RegionFile::RegionFile(const std::string& basePath)
: file(NULL)
{
filename = basePath;
filename += "/";
filename += REGION_DAT_NAME;
offsets = new int[SECTOR_INTS];
emptyChunk = new int[SECTOR_INTS];
memset(emptyChunk, 0, SECTOR_INTS * sizeof(int));
}
RegionFile::~RegionFile()
{
close();
delete [] offsets;
delete [] emptyChunk;
}
bool RegionFile::open()
{
close();
memset(offsets, 0, SECTOR_INTS * sizeof(int));
// attempt to open file
file = fopen(filename.c_str(), "r+b");
if (file)
{
// read offset table
logAssert(fread(offsets, sizeof(int), SECTOR_INTS, file), SECTOR_INTS);
// mark initial sector as blocked
sectorFree[0] = false;
// setup blocked sectors
for (int sector = 0; sector < SECTOR_INTS; sector++)
{
int offset = offsets[sector];
if (offset)
{
int base = offset >> 8;
int count = offset & 0xff;
for (int i = 0; i < count; i++)
{
sectorFree[base + i] = false;
}
}
}
}
else
{
// new world
file = fopen(filename.c_str(), "w+b");
if (!file)
{
LOGI("Failed to create chunk file %s\n", filename.c_str());
return false;
}
// write sector header (all zeroes)
logAssert(fwrite(offsets, sizeof(int), SECTOR_INTS, file), SECTOR_INTS);
// mark initial sector as blocked
sectorFree[0] = false;
}
return file != NULL;
}
void RegionFile::close()
{
if (file)
{
fclose(file);
file = NULL;
}
}
bool RegionFile::readChunk(int x, int z, RakNet::BitStream** destChunkData)
{
int offset = offsets[x + z * SECTOR_COLS];
if (offset == 0)
{
// not written to file yet
return false;
}
int sectorNum = offset >> 8;
fseek(file, sectorNum * SECTOR_BYTES, SEEK_SET);
int length = 0;
fread(&length, sizeof(int), 1, file);
assert(length < ((offset & 0xff) * SECTOR_BYTES));
length -= sizeof(int);
if (length <= 0) {
//*destChunkData = NULL;
//return false;
}
unsigned char* data = new unsigned char[length];
logAssert(fread(data, 1, length, file), length);
*destChunkData = new RakNet::BitStream(data, length, false);
//delete [] data;
return true;
}
bool RegionFile::writeChunk(int x, int z, RakNet::BitStream& chunkData)
{
int size = chunkData.GetNumberOfBytesUsed() + sizeof(int);
int offset = offsets[x + z * SECTOR_COLS];
int sectorNum = offset >> 8;
int sectorCount = offset & 0xff;
int sectorsNeeded = (size / SECTOR_BYTES) + 1;
if (sectorsNeeded > 256)
{
LOGI("ERROR: Chunk is too big to be saved to file\n");
return false;
}
if (sectorNum != 0 && sectorCount == sectorsNeeded) {
// the sector can be written on top of its old data
write(sectorNum, chunkData);
}
else
{
// we need a new location
// mark old sectors as free
for (int i = 0; i < sectorCount; i++)
{
sectorFree[sectorNum + i] = true;
}
// find an available slot with enough run length
int slot = 0;
int runLength = 0;
bool extendFile = false;
while (runLength < sectorsNeeded)
{
if (sectorFree.find(slot + runLength) == sectorFree.end())
{
extendFile = true;
break;
}
if (sectorFree[slot + runLength] == true)
{
runLength++;
}
else
{
slot = slot + runLength + 1;
runLength = 0;
}
}
if (extendFile)
{
fseek(file, 0, SEEK_END);
for (int i = 0; i < (sectorsNeeded - runLength); i++)
{
fwrite(emptyChunk, sizeof(int), SECTOR_INTS, file);
sectorFree[slot + i] = true;
}
}
offsets[x + z * SECTOR_COLS] = (slot << 8) | sectorsNeeded;
// mark slots as taken
for (int i = 0; i < sectorsNeeded; i++)
{
sectorFree[slot + i] = false;
}
// write!
write(slot, chunkData);
// write sector data
fseek(file, (x + z * SECTOR_COLS) * sizeof(int), SEEK_SET);
fwrite(&offsets[x + z * SECTOR_COLS], sizeof(int), 1, file);
}
return true;
}
bool RegionFile::write(int sector, RakNet::BitStream& chunkData)
{
fseek(file, sector * SECTOR_BYTES, SEEK_SET);
//LOGI("writing %d B to sector %d\n", chunkData.GetNumberOfBytesUsed(), sector);
int size = chunkData.GetNumberOfBytesUsed() + sizeof(int);
logAssert(fwrite(&size, sizeof(int), 1, file), 1);
logAssert(fwrite(chunkData.GetData(), 1, chunkData.GetNumberOfBytesUsed(), file), chunkData.GetNumberOfBytesUsed());
return true;
}

View File

@@ -0,0 +1,31 @@
#ifndef NET_MINECRAFT_WORLD_LEVEL_STORAGE__RegionFile_H__
#define NET_MINECRAFT_WORLD_LEVEL_STORAGE__RegionFile_H__
#include <map>
#include <string>
#include "../../../raknet/BitStream.h"
typedef std::map<int, bool> FreeSectorMap;
class RegionFile
{
public:
RegionFile(const std::string& basePath);
virtual ~RegionFile();
bool open();
bool readChunk(int x, int z, RakNet::BitStream** destChunkData);
bool writeChunk(int x, int z, RakNet::BitStream& chunkData);
private:
bool write(int sector, RakNet::BitStream& chunkData);
void close();
FILE* file;
std::string filename;
int* offsets;
int* emptyChunk;
FreeSectorMap sectorFree;
};
#endif /*NET_MINECRAFT_WORLD_LEVEL_STORAGE__RegionFile_H__*/