mirror of
https://github.com/celisej567/mcpe.git
synced 2026-09-11 20:11:18 +03:00
Merge branch 'master' into master
This commit is contained in:
@@ -10,6 +10,7 @@
|
||||
#include "PauseScreen.hpp"
|
||||
#include "StartMenuScreen.hpp"
|
||||
#include "RenameMPLevelScreen.hpp"
|
||||
#include "SavingWorldScreen.hpp"
|
||||
#include "ServerSideNetworkHandler.hpp"
|
||||
#include "ClientSideNetworkHandler.hpp"
|
||||
|
||||
@@ -299,7 +300,7 @@ void Minecraft::tickInput()
|
||||
{
|
||||
if (!field_D14->field_10)
|
||||
{
|
||||
field_DB0 = 1;
|
||||
field_DB0 = true;
|
||||
field_D14->updateEvents();
|
||||
field_DB0 = false;
|
||||
if (field_DB1)
|
||||
@@ -850,6 +851,10 @@ void Minecraft::leaveGame(bool bCopyMap)
|
||||
delete m_pNetEventCallback;
|
||||
#endif
|
||||
|
||||
#ifdef ENH_IMPROVED_SAVING
|
||||
field_288 = true;
|
||||
setScreen(new SavingWorldScreen(bCopyMap));
|
||||
#else
|
||||
if (m_pLevel)
|
||||
{
|
||||
LevelStorage* pStorage = m_pLevel->getLevelStorage();
|
||||
@@ -858,6 +863,7 @@ void Minecraft::leaveGame(bool bCopyMap)
|
||||
|
||||
m_pLevel = nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ORIGINAL_CODE
|
||||
delete m_pNetEventCallback;
|
||||
@@ -866,10 +872,12 @@ void Minecraft::leaveGame(bool bCopyMap)
|
||||
m_pNetEventCallback = nullptr;
|
||||
field_D9C = 0;
|
||||
|
||||
#ifndef ENH_IMPROVED_SAVING
|
||||
if (bCopyMap)
|
||||
setScreen(new RenameMPLevelScreen("_LastJoinedServer"));
|
||||
else
|
||||
setScreen(new StartMenuScreen);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Minecraft::hostMultiplayer()
|
||||
|
||||
@@ -8,9 +8,14 @@
|
||||
|
||||
#include "NinecraftApp.hpp"
|
||||
#include "StartMenuScreen.hpp"
|
||||
#include "MemoryLevelStorageSource.hpp"
|
||||
#include "Item.hpp"
|
||||
|
||||
#ifdef DEMO
|
||||
#include "MemoryLevelStorageSource.hpp"
|
||||
#else
|
||||
#include "ExternalFileLevelStorageSource.hpp"
|
||||
#endif
|
||||
|
||||
bool NinecraftApp::_hasInitedStatics;
|
||||
|
||||
bool NinecraftApp::handleBack(bool b)
|
||||
@@ -66,7 +71,13 @@ void NinecraftApp::init()
|
||||
initGLStates();
|
||||
Tesselator::instance.init();
|
||||
Minecraft::init();
|
||||
|
||||
#ifdef DEMO
|
||||
m_pLevelStorageSource = new MemoryLevelStorageSource;
|
||||
#else
|
||||
m_pLevelStorageSource = new ExternalFileLevelStorageSource(m_externalStorageDir);
|
||||
#endif
|
||||
|
||||
field_D9C = 0;
|
||||
|
||||
setScreen(new StartMenuScreen);
|
||||
|
||||
@@ -11,17 +11,125 @@
|
||||
#include "Utils.hpp"
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#include <Windows.h>
|
||||
#include <io.h>
|
||||
#include <direct.h>
|
||||
|
||||
// Why are we not using GetTickCount64()? It's simple -- getTimeMs has the exact same problem as using regular old GetTickCount.
|
||||
#pragma warning(disable : 28159)
|
||||
|
||||
#else
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#define XPL_ACCESS access
|
||||
#define XPL_MKDIR(path, mode) mkdir(path, mode)
|
||||
#endif
|
||||
|
||||
#include "compat/GL.hpp"
|
||||
|
||||
int g_TimeSecondsOnInit = 0;
|
||||
|
||||
DIR* opendir(const char* name)
|
||||
{
|
||||
size_t len = strlen(name);
|
||||
if (len == 0)
|
||||
return NULL;
|
||||
|
||||
char buf[1024];
|
||||
if (len >= 1024 - 5)
|
||||
return NULL;
|
||||
|
||||
strcpy(buf, name);
|
||||
|
||||
if (name[len - 1] != '/')
|
||||
strcpy(&buf[len], "/*");
|
||||
else
|
||||
strcpy(&buf[len], "*");
|
||||
|
||||
DIR* pDir = (DIR*)malloc(sizeof(DIR));
|
||||
if (!pDir)
|
||||
return pDir;
|
||||
|
||||
memset(pDir, 0, sizeof * pDir);
|
||||
|
||||
pDir->current = FindFirstFile(buf, &pDir->findData);
|
||||
if (pDir->current == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
free(pDir);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return pDir;
|
||||
}
|
||||
|
||||
dirent* readdir(DIR* dir)
|
||||
{
|
||||
if (dir->current == INVALID_HANDLE_VALUE)
|
||||
return NULL;
|
||||
|
||||
static dirent de;
|
||||
|
||||
if (!dir->returnedFirstFileData)
|
||||
{
|
||||
dir->returnedFirstFileData = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!FindNextFile(dir->current, &dir->findData))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
strcpy(de.d_name, dir->findData.cFileName);
|
||||
de.d_type = (dir->findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? DT_DIR : DT_REG;
|
||||
|
||||
return &de;
|
||||
}
|
||||
|
||||
void closedir(DIR* dir)
|
||||
{
|
||||
if (dir->current != INVALID_HANDLE_VALUE)
|
||||
FindClose(dir->current);
|
||||
|
||||
free(dir);
|
||||
}
|
||||
|
||||
bool createFolderIfNotExists(const char* pDir)
|
||||
{
|
||||
if (!XPL_ACCESS(pDir, 0))
|
||||
return true;
|
||||
|
||||
return XPL_MKDIR(pDir, 0755) == 0;
|
||||
}
|
||||
|
||||
bool DeleteDirectory(const std::string& name, bool unused)
|
||||
{
|
||||
DIR* dir = opendir(name.c_str());
|
||||
if (!dir)
|
||||
return false;
|
||||
|
||||
char buffer[1024];
|
||||
|
||||
while (true)
|
||||
{
|
||||
dirent* de = readdir(dir);
|
||||
if (!de)
|
||||
break;
|
||||
|
||||
if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
|
||||
continue;
|
||||
|
||||
snprintf(buffer, sizeof buffer, "%s/%s", name.c_str(), de->d_name);
|
||||
remove(buffer);
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
return remove(name.c_str()) == 0;
|
||||
}
|
||||
|
||||
const char* GetTerrainName()
|
||||
{
|
||||
return "terrain.png";
|
||||
|
||||
@@ -15,16 +15,51 @@
|
||||
#include <cassert>
|
||||
#include <climits>
|
||||
|
||||
#include <string>
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
// @HACK: Include WinSock2.h also
|
||||
#include <WinSock2.h>
|
||||
#include <Windows.h>
|
||||
#include <WS2tcpip.h>
|
||||
#include <direct.h>
|
||||
#include <io.h>
|
||||
|
||||
// XPL means "Cross PLatform"
|
||||
#define XPL_ACCESS _access
|
||||
#define XPL_MKDIR(path, mode) _mkdir(path)
|
||||
|
||||
// Bare bones non conforming implementation, but good enough
|
||||
struct dirent
|
||||
{
|
||||
int d_type;
|
||||
char d_name[_MAX_PATH + 1];
|
||||
};
|
||||
|
||||
struct DIR
|
||||
{
|
||||
HANDLE current;
|
||||
WIN32_FIND_DATA findData;
|
||||
bool returnedFirstFileData;
|
||||
};
|
||||
|
||||
#define DT_UNKNOWN (0)
|
||||
#define DT_DIR (4)
|
||||
#define DT_REG (8)
|
||||
|
||||
DIR* opendir(const char* name);
|
||||
dirent* readdir(DIR* dir);
|
||||
void closedir(DIR* dir);
|
||||
|
||||
#else
|
||||
|
||||
#include <unistd.h>
|
||||
#include <dirent.h>
|
||||
|
||||
// XPL means "Cross PLatform"
|
||||
#define XPL_ACCESS access
|
||||
#define XPL_MKDIR(path, mode) mkdir(path, mode)
|
||||
|
||||
#endif
|
||||
|
||||
@@ -504,6 +539,8 @@ constexpr float Lerp(float a, float b, float progress)
|
||||
return a + progress * (b - a);
|
||||
}
|
||||
|
||||
bool createFolderIfNotExists(const char* pDir);
|
||||
bool DeleteDirectory(const std::string& name, bool unused);
|
||||
|
||||
// things that we added:
|
||||
#ifndef ORIGINAL_CODE
|
||||
@@ -537,3 +574,23 @@ void SetHWND(HWND hwnd);
|
||||
#define LogMsgNoCR(...)
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef MC_DEBUG
|
||||
|
||||
#ifdef PLATFORM_ANDROID
|
||||
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, "MinecraftPE", __VA_ARGS__)
|
||||
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, "MinecraftPE", __VA_ARGS__)
|
||||
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, "MinecraftPE", __VA_ARGS__)
|
||||
#else
|
||||
#define LOGI(...) printf("Info: " __VA_ARGS__)
|
||||
#define LOGW(...) printf("WARN: " __VA_ARGS__)
|
||||
#define LOGE(...) printf("ERROR: " __VA_ARGS__)
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
#define LOGI(...) printf(__VA_ARGS__)
|
||||
#define LOGW(...) printf(__VA_ARGS__)
|
||||
#define LOGE(...) printf(__VA_ARGS__)
|
||||
|
||||
#endif
|
||||
|
||||
63
source/GUI/Screen/SavingWorldScreen.cpp
Normal file
63
source/GUI/Screen/SavingWorldScreen.cpp
Normal file
@@ -0,0 +1,63 @@
|
||||
#include "SavingWorldScreen.hpp"
|
||||
#include "RenameMPLevelScreen.hpp"
|
||||
#include "StartMenuScreen.hpp"
|
||||
|
||||
#ifdef ENH_IMPROVED_SAVING
|
||||
|
||||
SavingWorldScreen::SavingWorldScreen(bool bCopyMap)
|
||||
{
|
||||
m_bCopyMapAtEnd = bCopyMap;
|
||||
m_timer = 0;
|
||||
}
|
||||
|
||||
void SavingWorldScreen::render(int mouseX, int mouseY, float f)
|
||||
{
|
||||
renderDirtBackground(0);
|
||||
|
||||
int x_width = int(Minecraft::width * Gui::InvGuiScale);
|
||||
int x_height = int(Minecraft::height * Gui::InvGuiScale);
|
||||
int yPos = x_height / 2;
|
||||
|
||||
int width = m_pFont->width("Saving chunks");
|
||||
m_pFont->drawShadow("Saving chunks", (x_width - width) / 2, yPos + 4, 0xFFFFFF);
|
||||
}
|
||||
|
||||
void SavingWorldScreen::tick()
|
||||
{
|
||||
if (m_timer < 0)
|
||||
return;
|
||||
|
||||
m_timer++;
|
||||
|
||||
if (m_timer >= 5)
|
||||
{
|
||||
m_timer = -1;
|
||||
|
||||
Level* pLevel = m_pMinecraft->m_pLevel;
|
||||
if (pLevel)
|
||||
{
|
||||
pLevel->saveAllChunks();
|
||||
pLevel->saveLevelData();
|
||||
pLevel->savePlayerData();
|
||||
|
||||
LevelStorage* pStorage = pLevel->getLevelStorage();
|
||||
SAFE_DELETE(pStorage);
|
||||
SAFE_DELETE(pLevel);
|
||||
|
||||
m_pMinecraft->m_pLevel = nullptr;
|
||||
}
|
||||
|
||||
m_pMinecraft->field_DB0 = true;
|
||||
|
||||
if (m_bCopyMapAtEnd)
|
||||
m_pMinecraft->setScreen(new RenameMPLevelScreen("_LastJoinedServer"));
|
||||
else
|
||||
m_pMinecraft->setScreen(new StartMenuScreen);
|
||||
|
||||
m_pMinecraft->field_DB0 = false;
|
||||
|
||||
m_pMinecraft->field_288 = false;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
20
source/GUI/Screen/SavingWorldScreen.hpp
Normal file
20
source/GUI/Screen/SavingWorldScreen.hpp
Normal file
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include "Screen.hpp"
|
||||
|
||||
#ifdef ENH_IMPROVED_SAVING
|
||||
|
||||
class SavingWorldScreen : public Screen
|
||||
{
|
||||
public:
|
||||
SavingWorldScreen(bool bCopyMap);
|
||||
|
||||
void render(int mouseX, int mouseY, float f) override;
|
||||
void tick() override;
|
||||
|
||||
public:
|
||||
bool m_bCopyMapAtEnd;
|
||||
int m_timer = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -126,7 +126,10 @@ void SelectWorldScreen::tick()
|
||||
m_pMinecraft->hostMultiplayer();
|
||||
m_pMinecraft->setScreen(new ProgressScreen);
|
||||
|
||||
// @BUG: Use of deallocated memory. SetScreen frees us
|
||||
#ifdef ORIGINAL_CODE
|
||||
field_130 = 0;
|
||||
#endif
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -48,16 +48,8 @@ void StartMenuScreen::buttonClicked(Button* pButton)
|
||||
{
|
||||
if (pButton->field_30 == m_startButton.field_30)
|
||||
{
|
||||
#if defined(DEMO) || !defined(ORIGINAL_CODE)
|
||||
|
||||
# ifdef DEMO
|
||||
# define DEMO_SEED int(getEpochTimeS())
|
||||
# else
|
||||
// 1942892620 = long(12345678901324)
|
||||
# define DEMO_SEED 123456
|
||||
# endif
|
||||
|
||||
m_pMinecraft->selectLevel("_DemoLevel", "_DemoLevel", DEMO_SEED);
|
||||
#if defined(DEMO)
|
||||
m_pMinecraft->selectLevel("_DemoLevel", "_DemoLevel", int(getEpochTimeS()));
|
||||
m_pMinecraft->hostMultiplayer();
|
||||
m_pMinecraft->setScreen(new ProgressScreen);
|
||||
#else
|
||||
|
||||
@@ -45,6 +45,7 @@ void Level::_init(const std::string& str, TLong seed, int x, Dimension* pDimens
|
||||
|
||||
field_B0C = pData == 0;
|
||||
|
||||
// @BUG: leaking a Dimension*?
|
||||
if (pDimension)
|
||||
m_pDimension = pDimension;
|
||||
else
|
||||
|
||||
@@ -19,6 +19,36 @@ LevelData::LevelData(TLong seed, const std::string& name, int x)
|
||||
field_78 = name;
|
||||
}
|
||||
|
||||
void LevelData::read(RakNet::BitStream& bs, int version)
|
||||
{
|
||||
field_20 = version;
|
||||
bs.Read(m_seed);
|
||||
bs.Read(m_spawnPos.x);
|
||||
bs.Read(m_spawnPos.y);
|
||||
bs.Read(m_spawnPos.z);
|
||||
bs.Read(field_10);
|
||||
bs.Read(field_18);
|
||||
bs.Read(field_14);
|
||||
|
||||
RakNet::RakString rs;
|
||||
bs.Read(rs);
|
||||
field_78 = std::string(rs.C_String());
|
||||
}
|
||||
|
||||
void LevelData::write(RakNet::BitStream& bs)
|
||||
{
|
||||
bs.Write(m_seed);
|
||||
bs.Write(m_spawnPos.x);
|
||||
bs.Write(m_spawnPos.y);
|
||||
bs.Write(m_spawnPos.z);
|
||||
bs.Write(field_10);
|
||||
bs.Write(field_18);
|
||||
bs.Write(int(getEpochTimeS()));
|
||||
|
||||
RakNet::RakString rs(field_78.c_str());
|
||||
bs.Write(rs);
|
||||
}
|
||||
|
||||
void PlayerData::loadPlayer(Player* player)
|
||||
{
|
||||
player->setPos(0.0f, 0.0f, 0.0f);
|
||||
@@ -43,3 +73,18 @@ void PlayerData::loadPlayer(Player* player)
|
||||
for (int i = 0; i < C_MAX_HOTBAR_ITEMS; i++)
|
||||
player->m_pInventory->setSelectionSlotItemId(i, m_hotbar[i]);
|
||||
}
|
||||
|
||||
void PlayerData::savePlayer(Player* player)
|
||||
{
|
||||
m_pos = player->m_pos;
|
||||
m_vel = player->m_vel;
|
||||
m_pitch = player->m_pitch;
|
||||
m_yaw = player->m_yaw;
|
||||
m_distanceFallen = player->m_distanceFallen;
|
||||
field_24 = player->field_C0;
|
||||
field_26 = player->field_BC;
|
||||
field_28 = player->field_7C;
|
||||
|
||||
for (int i = 0; i < C_MAX_HOTBAR_ITEMS; i++)
|
||||
m_hotbar[i] = player->m_pInventory->getSelectionSlotItemId(i);
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "Utils.hpp"
|
||||
#include "Vec3.hpp"
|
||||
#include "Inventory.hpp"
|
||||
#include "BitStream.h"
|
||||
|
||||
struct PlayerData
|
||||
{
|
||||
@@ -27,6 +28,7 @@ struct PlayerData
|
||||
int m_hotbar[C_MAX_HOTBAR_ITEMS];
|
||||
|
||||
void loadPlayer(Player* player);
|
||||
void savePlayer(Player* player);
|
||||
};
|
||||
|
||||
struct LevelData
|
||||
@@ -34,15 +36,29 @@ struct LevelData
|
||||
LevelData();
|
||||
LevelData(TLong seed, const std::string&, int);
|
||||
|
||||
void read(RakNet::BitStream& bs, int d);
|
||||
void write(RakNet::BitStream& bs);
|
||||
|
||||
TLong m_seed = 0;
|
||||
Pos m_spawnPos;
|
||||
TLong field_10 = 0;
|
||||
int field_14 = 0;
|
||||
int field_18 = 0;
|
||||
TLong field_18 = 0;
|
||||
int field_1C = 0;
|
||||
int field_20 = 0;
|
||||
PlayerData m_LocalPlayerData;
|
||||
int m_nPlayers = -1;
|
||||
std::string field_78;
|
||||
|
||||
// inlined in 0.1.0 demo
|
||||
int getVersion() const
|
||||
{
|
||||
return field_20;
|
||||
}
|
||||
|
||||
void setLevelName(const std::string& name)
|
||||
{
|
||||
field_78 = name;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
174
source/World/RegionFile.cpp
Normal file
174
source/World/RegionFile.cpp
Normal file
@@ -0,0 +1,174 @@
|
||||
#include "RegionFile.hpp"
|
||||
|
||||
#define SECTOR_BYTES (4096)
|
||||
|
||||
static void void_sub(int a, int b)
|
||||
{
|
||||
}
|
||||
|
||||
#define WRITE(data, elemsize, elemcnt, file) void_sub(int(fwrite(data, elemsize, elemcnt, file)), elemcnt)
|
||||
#define READ( data, elemsize, elemcnt, file) void_sub(int(fread (data, elemsize, elemcnt, file)), elemcnt)
|
||||
|
||||
RegionFile::RegionFile(const std::string fileName)
|
||||
{
|
||||
m_fileName = fileName + "/" + "chunks.dat";
|
||||
|
||||
field_20 = new int[1024];
|
||||
field_24 = new int[1024];
|
||||
memset(field_24, 0, 1024 * sizeof(int));
|
||||
}
|
||||
|
||||
RegionFile::~RegionFile()
|
||||
{
|
||||
close();
|
||||
if (field_20) delete[] field_20;
|
||||
if (field_24) delete[] field_24;
|
||||
}
|
||||
|
||||
void RegionFile::close()
|
||||
{
|
||||
if (m_pFile)
|
||||
{
|
||||
fclose(m_pFile);
|
||||
m_pFile = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool RegionFile::open()
|
||||
{
|
||||
close();
|
||||
memset(field_20, 0, 1024 * sizeof(int));
|
||||
|
||||
m_pFile = fopen(m_fileName.c_str(), "r+b");
|
||||
if (m_pFile)
|
||||
{
|
||||
READ(field_20, sizeof(int), 1024, m_pFile);
|
||||
|
||||
field_28[0] = false;
|
||||
|
||||
for (int i = 0; i < 1024; i++)
|
||||
{
|
||||
int v13 = this->field_20[i];
|
||||
if (v13)
|
||||
{
|
||||
int v12 = v13 >> 8;
|
||||
int v11 = uint8_t(v13);
|
||||
for (int j = 0; j < v11; ++j)
|
||||
{
|
||||
field_28[j + v12] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return m_pFile != nullptr;
|
||||
}
|
||||
|
||||
m_pFile = fopen(m_fileName.c_str(), "w+b");
|
||||
if (!m_pFile)
|
||||
return false;
|
||||
|
||||
WRITE(field_20, sizeof(int), 1024, m_pFile);
|
||||
field_28[0] = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RegionFile::readChunk(int x, int z, RakNet::BitStream** pBitStream)
|
||||
{
|
||||
int idx = field_20[32 * z + x];
|
||||
if (!idx)
|
||||
return false;
|
||||
|
||||
int thing = (idx >> 8);
|
||||
int offset = (idx & 0xFF);
|
||||
|
||||
int length = 0;
|
||||
fseek(m_pFile, thing * SECTOR_BYTES, SEEK_SET);
|
||||
fread(&length, sizeof(int), 1, m_pFile);
|
||||
|
||||
assert(length < ((offset & 0xff) * SECTOR_BYTES));
|
||||
|
||||
length -= 4;
|
||||
|
||||
uint8_t* data = new uint8_t[length];
|
||||
READ(data, 1, length, m_pFile);
|
||||
|
||||
*pBitStream = new RakNet::BitStream(data, length, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RegionFile::write(int index, RakNet::BitStream& bitStream)
|
||||
{
|
||||
fseek(m_pFile, index * SECTOR_BYTES, 0);
|
||||
int length = sizeof(int) + bitStream.GetNumberOfBytesUsed();
|
||||
|
||||
WRITE(&length, sizeof(length), 1, m_pFile);
|
||||
WRITE(bitStream.GetData(), 1, bitStream.GetNumberOfBytesUsed(), m_pFile);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RegionFile::writeChunk(int x, int z, RakNet::BitStream& bitStream)
|
||||
{
|
||||
int length = bitStream.GetNumberOfBytesUsed();
|
||||
int field20i = field_20[32 * z + x];
|
||||
int lowerIndex = (length + 4) / SECTOR_BYTES + 1;
|
||||
if (lowerIndex > 256)
|
||||
return false;
|
||||
|
||||
int field20iU = field20i >> 8, field20iL = field20i & 0xff;
|
||||
|
||||
if (field20iU && lowerIndex == field20iL)
|
||||
{
|
||||
write(field20iU, bitStream);
|
||||
return true;
|
||||
}
|
||||
|
||||
for (int i = 0; i < field20iL; i++)
|
||||
{
|
||||
field_28[i + field20iU] = true;
|
||||
}
|
||||
|
||||
bool bNeedWrite = false;
|
||||
int v22 = 0, i = 0;
|
||||
while (i < lowerIndex)
|
||||
{
|
||||
if (field_28.find(i + v22) == field_28.end())
|
||||
{
|
||||
bNeedWrite = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (field_28[i + v22])
|
||||
{
|
||||
i++;
|
||||
}
|
||||
else
|
||||
{
|
||||
v22 += i + 1;
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (bNeedWrite)
|
||||
{
|
||||
fseek(m_pFile, 0, SEEK_END);
|
||||
for (int j = 0; lowerIndex - i > j; j++)
|
||||
{
|
||||
fwrite(field_24, sizeof(int), 1024, m_pFile);
|
||||
field_28[j + v22] = true;
|
||||
}
|
||||
}
|
||||
|
||||
field_20[32 * z + x] = (v22 << 8) | lowerIndex;
|
||||
for (int k = 0; k < lowerIndex; k++)
|
||||
{
|
||||
field_28[k + v22] = false;
|
||||
}
|
||||
|
||||
write(v22, bitStream);
|
||||
fseek(m_pFile, sizeof(int) * (x + 32 * z), SEEK_SET);
|
||||
fwrite(&field_20[x + 32 * z], sizeof(int), 1, m_pFile);
|
||||
|
||||
return true;
|
||||
}
|
||||
27
source/World/RegionFile.hpp
Normal file
27
source/World/RegionFile.hpp
Normal file
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
#include <cassert>
|
||||
#include <map>
|
||||
#include "BitStream.h"
|
||||
|
||||
class RegionFile
|
||||
{
|
||||
public:
|
||||
RegionFile(const std::string fileName);
|
||||
~RegionFile();
|
||||
void close();
|
||||
bool open();
|
||||
bool readChunk(int x, int z, RakNet::BitStream**);
|
||||
bool write(int index, RakNet::BitStream&);
|
||||
bool writeChunk(int x, int z, RakNet::BitStream&);
|
||||
|
||||
public:
|
||||
FILE* m_pFile = nullptr;
|
||||
std::string m_fileName;
|
||||
int* field_20;
|
||||
int* field_24;
|
||||
std::map<int, bool> field_28;
|
||||
};
|
||||
|
||||
306
source/World/Storage/ExternalFileLevelStorage.cpp
Normal file
306
source/World/Storage/ExternalFileLevelStorage.cpp
Normal file
@@ -0,0 +1,306 @@
|
||||
/********************************************************************
|
||||
Minecraft: Pocket Edition - Decompilation Project
|
||||
Copyright (C) 2023 iProgramInCpp
|
||||
|
||||
The following code is licensed under the BSD 1 clause license.
|
||||
SPDX-License-Identifier: BSD-1-Clause
|
||||
********************************************************************/
|
||||
|
||||
#include "ExternalFileLevelStorage.hpp"
|
||||
#include "Level.hpp"
|
||||
#include "GetTime.h"
|
||||
|
||||
#define C_CHUNKS_TO_SAVE_PER_TICK (2)
|
||||
|
||||
ExternalFileLevelStorage::ExternalFileLevelStorage(const std::string& a, const std::string& path) :
|
||||
field_8(a),
|
||||
m_levelDirPath(path)
|
||||
{
|
||||
createFolderIfNotExists(m_levelDirPath.c_str());
|
||||
|
||||
std::string datLevel = m_levelDirPath + "/" + "level.dat";
|
||||
std::string datPlayer = m_levelDirPath + "/" + "player.dat";
|
||||
|
||||
m_pLevelData = new LevelData;
|
||||
if (!readLevelData(datLevel, m_pLevelData))
|
||||
{
|
||||
delete m_pLevelData;
|
||||
m_pLevelData = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
readPlayerData(datPlayer, m_pLevelData);
|
||||
}
|
||||
|
||||
LevelData* ExternalFileLevelStorage::prepareLevel(Level* level)
|
||||
{
|
||||
m_pLevel = level;
|
||||
return m_pLevelData;
|
||||
}
|
||||
|
||||
ChunkStorage* ExternalFileLevelStorage::createChunkStorage(Dimension* pDim)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
void ExternalFileLevelStorage::saveLevelData(LevelData* levelData, std::vector<Player*>& players)
|
||||
{
|
||||
writeLevelData(m_levelDirPath + "/" + "level.dat", levelData);
|
||||
savePlayerData(levelData, players);
|
||||
|
||||
SAFE_DELETE(m_pLevelData);
|
||||
|
||||
m_pLevelData = new LevelData(*levelData);
|
||||
}
|
||||
|
||||
void ExternalFileLevelStorage::savePlayerData(LevelData* levelData, std::vector<Player*>& players)
|
||||
{
|
||||
if (players.empty())
|
||||
return;
|
||||
|
||||
FILE* pFile = fopen((m_levelDirPath + "/" + "player.dat").c_str(), "wb");
|
||||
if (!pFile)
|
||||
{
|
||||
LogMsg("Not saving player data");
|
||||
return;
|
||||
}
|
||||
|
||||
levelData->m_LocalPlayerData.savePlayer(players[0]);
|
||||
|
||||
int nPlayers = 1;
|
||||
fwrite(&nPlayers, sizeof nPlayers, 1, pFile);
|
||||
|
||||
int nSizePD = 80;
|
||||
fwrite(&nSizePD, sizeof nSizePD, 1, pFile);
|
||||
|
||||
// @NOTE: No reason to swap elementCount and elementSize here. I understood it the
|
||||
// last time - to check whether the data loaded all the way. However, no checks are
|
||||
// done here.
|
||||
fwrite(&levelData->m_LocalPlayerData, 1, nSizePD, pFile);
|
||||
|
||||
fclose(pFile);
|
||||
}
|
||||
|
||||
void ExternalFileLevelStorage::closeAll()
|
||||
{
|
||||
}
|
||||
|
||||
void ExternalFileLevelStorage::tick()
|
||||
{
|
||||
m_timer++;
|
||||
if (m_timer % 50 != 0 || !m_pLevel)
|
||||
return;
|
||||
|
||||
for (int z = 0; z < C_MAX_CHUNKS_Z; z++)
|
||||
{
|
||||
for (int x = 0; x < C_MAX_CHUNKS_X; x++)
|
||||
{
|
||||
LevelChunk* pChunk = m_pLevel->getChunk(x, z);
|
||||
if (!pChunk || !pChunk->m_bUnsaved)
|
||||
continue;
|
||||
|
||||
int index = x + z * 16;
|
||||
|
||||
auto iter = m_unsavedLevelChunks.begin();
|
||||
for (; iter != m_unsavedLevelChunks.end(); ++iter)
|
||||
{
|
||||
if (iter->m_index == index)
|
||||
{
|
||||
iter->m_foundTime = RakNet::GetTimeMS();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (iter == m_unsavedLevelChunks.end())
|
||||
{
|
||||
UnsavedLevelChunk ulc = { index, RakNet::GetTimeMS(), pChunk };
|
||||
m_unsavedLevelChunks.push_back(ulc);
|
||||
}
|
||||
|
||||
pChunk->m_bUnsaved = false;
|
||||
}
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
while (count < C_CHUNKS_TO_SAVE_PER_TICK && !m_unsavedLevelChunks.empty())
|
||||
{
|
||||
count++;
|
||||
|
||||
auto iter = m_unsavedLevelChunks.begin();
|
||||
for (auto it2 = m_unsavedLevelChunks.begin(); it2 != m_unsavedLevelChunks.end(); ++it2)
|
||||
{
|
||||
if (iter->m_foundTime > it2->m_foundTime)
|
||||
iter = it2;
|
||||
}
|
||||
|
||||
LevelChunk* pChunk = iter->m_pChunk;
|
||||
m_unsavedLevelChunks.erase(iter);
|
||||
|
||||
save(m_pLevel, pChunk);
|
||||
}
|
||||
}
|
||||
|
||||
void ExternalFileLevelStorage::flush()
|
||||
{
|
||||
}
|
||||
|
||||
LevelChunk* ExternalFileLevelStorage::load(Level* level, int x, int z)
|
||||
{
|
||||
if (!m_pRegionFile)
|
||||
{
|
||||
m_pRegionFile = new RegionFile(m_levelDirPath);
|
||||
|
||||
if (!m_pRegionFile->open())
|
||||
{
|
||||
SAFE_DELETE(m_pRegionFile);
|
||||
m_pRegionFile = nullptr;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
RakNet::BitStream* pBitStream = nullptr;
|
||||
if (!m_pRegionFile->readChunk(x, z, &pBitStream))
|
||||
return nullptr;
|
||||
|
||||
pBitStream->ResetReadPointer();
|
||||
|
||||
TileID* pData = new TileID[16 * 16 * 128];
|
||||
pBitStream->Read((char*)pData, 16 * 16 * 128 * sizeof(TileID));
|
||||
|
||||
LevelChunk* pChunk = new LevelChunk(level, pData, x, z);
|
||||
pBitStream->Read((char*)pChunk->m_tileData, 16 * 16 * 128 / 2);
|
||||
|
||||
if (m_pLevelData->getVersion() == 1)
|
||||
{
|
||||
pBitStream->Read((char*)pChunk->m_lightSky, 16 * 16 * 128 / 2);
|
||||
pBitStream->Read((char*)pChunk->m_lightBlk, 16 * 16 * 128 / 2);
|
||||
}
|
||||
|
||||
pBitStream->Read((char*)pChunk->m_updateMap, sizeof pChunk->m_updateMap);
|
||||
|
||||
delete pBitStream->GetData();
|
||||
delete pBitStream;
|
||||
|
||||
pChunk->recalcHeightmap();
|
||||
pChunk->m_bUnsaved = false;
|
||||
pChunk->field_234 = true;
|
||||
pChunk->field_237 = true;
|
||||
|
||||
return pChunk;
|
||||
}
|
||||
|
||||
void ExternalFileLevelStorage::save(Level* level, LevelChunk* chunk)
|
||||
{
|
||||
if (!m_pRegionFile)
|
||||
m_pRegionFile = new RegionFile(m_levelDirPath);
|
||||
|
||||
if (!m_pRegionFile->open())
|
||||
{
|
||||
SAFE_DELETE(m_pRegionFile);
|
||||
m_pRegionFile = nullptr;
|
||||
|
||||
LogMsg("Not saving :( (x: %d z: %d)", chunk->m_chunkX, chunk->m_chunkZ);
|
||||
return;
|
||||
}
|
||||
|
||||
RakNet::BitStream bs;
|
||||
bs.Write((const char*)chunk->m_pBlockData, 16 * 16 * 128 * sizeof(TileID));
|
||||
bs.Write((const char*)chunk->m_tileData, 16 * 16 * 128 / 2);
|
||||
|
||||
if (m_pLevelData->field_20 == 1)
|
||||
{
|
||||
bs.Write((const char*)chunk->m_lightSky, 16 * 16 * 128 / 2);
|
||||
bs.Write((const char*)chunk->m_lightBlk, 16 * 16 * 128 / 2);
|
||||
}
|
||||
|
||||
bs.Write((const char*)chunk->m_updateMap, sizeof chunk->m_updateMap);
|
||||
|
||||
m_pRegionFile->writeChunk(chunk->m_chunkX, chunk->m_chunkZ, bs);
|
||||
}
|
||||
|
||||
void ExternalFileLevelStorage::saveEntities(Level* level, LevelChunk* chunk)
|
||||
{
|
||||
// no op
|
||||
}
|
||||
|
||||
bool ExternalFileLevelStorage::readLevelData(const std::string& path, LevelData* pLevelData)
|
||||
{
|
||||
FILE* pFile = fopen(path.c_str(), "rb");
|
||||
if (!pFile)
|
||||
return false;
|
||||
|
||||
int version = 0, length = 0;
|
||||
if (fread(&version, sizeof(int), 1, pFile) != 1)
|
||||
{
|
||||
_cleanup:
|
||||
fclose(pFile);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fread(&length, sizeof(int), 1, pFile) != 1)
|
||||
goto _cleanup;
|
||||
|
||||
uint8_t* data = new uint8_t[length];
|
||||
|
||||
if (fread(data, sizeof(uint8_t), length, pFile) != length)
|
||||
{
|
||||
SAFE_DELETE_ARRAY(data);
|
||||
goto _cleanup;
|
||||
}
|
||||
|
||||
RakNet::BitStream bs(data, length, false);
|
||||
pLevelData->read(bs, version);
|
||||
|
||||
SAFE_DELETE_ARRAY(data);
|
||||
fclose(pFile);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ExternalFileLevelStorage::readPlayerData(const std::string& path, LevelData* pLevelData)
|
||||
{
|
||||
FILE* pFile = fopen(path.c_str(), "rb");
|
||||
if (!pFile)
|
||||
return false;
|
||||
|
||||
// don't know if it's actually nPlayers or version
|
||||
int nPlayers = 0, size = 0;
|
||||
if (fread(&nPlayers, sizeof(int), 1, pFile) != 1)
|
||||
goto _cleanup;
|
||||
|
||||
if (fread(&size, sizeof(int), 1, pFile) != 1)
|
||||
goto _cleanup;
|
||||
|
||||
if (nPlayers != 1)
|
||||
goto _cleanup;
|
||||
|
||||
if (fread(&pLevelData->m_LocalPlayerData, 1, sizeof pLevelData->m_LocalPlayerData, pFile) == size)
|
||||
pLevelData->m_nPlayers = nPlayers;
|
||||
|
||||
fclose(pFile);
|
||||
return true;
|
||||
|
||||
_cleanup:
|
||||
fclose(pFile);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ExternalFileLevelStorage::writeLevelData(const std::string& path, LevelData* pLevelData)
|
||||
{
|
||||
FILE* pFile = fopen(path.c_str(), "wb");
|
||||
if (!pFile)
|
||||
return false;
|
||||
|
||||
RakNet::BitStream bs;
|
||||
pLevelData->write(bs);
|
||||
|
||||
fwrite(&pLevelData->field_20, sizeof(int), 1, pFile);
|
||||
|
||||
int length = bs.GetNumberOfBytesUsed();
|
||||
fwrite(&length, sizeof(int), 1, pFile);
|
||||
fwrite(bs.GetData(), 1, length, pFile);
|
||||
fclose(pFile);
|
||||
|
||||
return true;
|
||||
}
|
||||
58
source/World/Storage/ExternalFileLevelStorage.hpp
Normal file
58
source/World/Storage/ExternalFileLevelStorage.hpp
Normal file
@@ -0,0 +1,58 @@
|
||||
/********************************************************************
|
||||
Minecraft: Pocket Edition - Decompilation Project
|
||||
Copyright (C) 2023 iProgramInCpp
|
||||
|
||||
The following code is licensed under the BSD 1 clause license.
|
||||
SPDX-License-Identifier: BSD-1-Clause
|
||||
********************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <list>
|
||||
#include "LevelStorage.hpp"
|
||||
#include "ChunkStorage.hpp"
|
||||
#include "RegionFile.hpp"
|
||||
|
||||
#ifndef DEMO
|
||||
|
||||
struct UnsavedLevelChunk
|
||||
{
|
||||
int m_index;
|
||||
int m_foundTime;
|
||||
LevelChunk* m_pChunk;
|
||||
};
|
||||
|
||||
class ExternalFileLevelStorage : public LevelStorage, ChunkStorage
|
||||
{
|
||||
public:
|
||||
ExternalFileLevelStorage(const std::string& a, const std::string& path);
|
||||
|
||||
// LevelStorage
|
||||
LevelData* prepareLevel(Level* level) override;
|
||||
ChunkStorage* createChunkStorage(Dimension*) override;
|
||||
void saveLevelData(LevelData* levelData, std::vector<Player*>& players) override;
|
||||
void savePlayerData(LevelData* levelData, std::vector<Player*>& players) override;
|
||||
void closeAll() override;
|
||||
void tick() override;
|
||||
void flush() override;
|
||||
|
||||
// ChunkStorage
|
||||
LevelChunk* load(Level* level, int x, int z) override;
|
||||
void save(Level* level, LevelChunk* chunk) override;
|
||||
void saveEntities(Level* level, LevelChunk* chunk) override;
|
||||
|
||||
static bool readLevelData(const std::string& path, LevelData* pLevelData);
|
||||
static bool readPlayerData(const std::string& path, LevelData* pLevelData);
|
||||
static bool writeLevelData(const std::string& path, LevelData* pLevelData);
|
||||
|
||||
public:
|
||||
std::string field_8;
|
||||
std::string m_levelDirPath;
|
||||
LevelData* m_pLevelData = nullptr;
|
||||
RegionFile* m_pRegionFile = nullptr;
|
||||
Level* m_pLevel = nullptr;
|
||||
int m_timer = 0;
|
||||
std::list<UnsavedLevelChunk> m_unsavedLevelChunks;
|
||||
};
|
||||
|
||||
#endif
|
||||
164
source/World/Storage/ExternalFileLevelStorageSource.cpp
Normal file
164
source/World/Storage/ExternalFileLevelStorageSource.cpp
Normal file
@@ -0,0 +1,164 @@
|
||||
/********************************************************************
|
||||
Minecraft: Pocket Edition - Decompilation Project
|
||||
Copyright (C) 2023 iProgramInCpp
|
||||
|
||||
The following code is licensed under the BSD 1 clause license.
|
||||
SPDX-License-Identifier: BSD-1-Clause
|
||||
********************************************************************/
|
||||
|
||||
#include "ExternalFileLevelStorageSource.hpp"
|
||||
#include "ExternalFileLevelStorage.hpp"
|
||||
#include "Util.hpp"
|
||||
|
||||
ExternalFileLevelStorageSource::ExternalFileLevelStorageSource(const std::string& path)
|
||||
{
|
||||
m_worldsPath = path;
|
||||
|
||||
m_worldsPath += "/games";
|
||||
if (createFolderIfNotExists(m_worldsPath.c_str()))
|
||||
{
|
||||
m_worldsPath += "/com.mojang";
|
||||
if (createFolderIfNotExists(m_worldsPath.c_str()))
|
||||
{
|
||||
m_worldsPath += "/minecraftWorlds";
|
||||
if (createFolderIfNotExists(m_worldsPath.c_str()))
|
||||
{
|
||||
std::vector<LevelSummary> vls;
|
||||
getLevelList(vls);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_worldsPath = path + "/games" + "/com.mojang" + "/minecraftWorlds";
|
||||
}
|
||||
|
||||
std::string ExternalFileLevelStorageSource::getName()
|
||||
{
|
||||
return "External File Level Storage";
|
||||
}
|
||||
|
||||
LevelStorage* ExternalFileLevelStorageSource::selectLevel(const std::string& name, bool b)
|
||||
{
|
||||
return new ExternalFileLevelStorage(name, m_worldsPath + "/" + name);
|
||||
}
|
||||
|
||||
void ExternalFileLevelStorageSource::getLevelList(std::vector<LevelSummary>& vls)
|
||||
{
|
||||
DIR* dir = opendir(m_worldsPath.c_str());
|
||||
if (!dir)
|
||||
return;
|
||||
|
||||
while (true)
|
||||
{
|
||||
dirent* de = readdir(dir);
|
||||
if (!de)
|
||||
break;
|
||||
|
||||
LogMsg("Entry: %s", de->d_name);
|
||||
|
||||
if (de->d_type == DT_DIR)
|
||||
{
|
||||
addLevelSummaryIfExists(vls, de->d_name);
|
||||
}
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
}
|
||||
|
||||
void ExternalFileLevelStorageSource::clearAll()
|
||||
{
|
||||
}
|
||||
|
||||
int ExternalFileLevelStorageSource::getDataTagFor(const std::string& str)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool ExternalFileLevelStorageSource::isNewLevelIdAcceptable(const std::string& levelID)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static char g_EFLSSFilterArray[] = { '/','\n','\r','\x09','\0','\xC','`','?','*','\\','<','>','|','"',':' };
|
||||
|
||||
void ExternalFileLevelStorageSource::deleteLevel(const std::string& levelName)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << m_worldsPath << "/" << levelName;
|
||||
std::string path = ss.str();
|
||||
|
||||
if (DeleteDirectory(path, true))
|
||||
return;
|
||||
|
||||
remove((path + "/chunks.dat").c_str());
|
||||
remove((path + "/player.dat").c_str());
|
||||
remove((path + "/level.dat").c_str());
|
||||
}
|
||||
|
||||
void ExternalFileLevelStorageSource::renameLevel(const std::string& oldName, const std::string& newName)
|
||||
{
|
||||
int accessResult = XPL_ACCESS((m_worldsPath + "/" + oldName).c_str(), 0);
|
||||
if (accessResult)
|
||||
return;
|
||||
|
||||
std::string levelName = Util::stringTrim(newName);
|
||||
for (int i = 0; i < sizeof(g_EFLSSFilterArray); i++)
|
||||
{
|
||||
std::string str;
|
||||
str.push_back(g_EFLSSFilterArray[i]);
|
||||
Util::stringReplace(levelName, str, "");
|
||||
}
|
||||
|
||||
std::vector<LevelSummary> vls;
|
||||
getLevelList(vls);
|
||||
|
||||
std::set<std::string> maps;
|
||||
|
||||
for (const auto& ls : vls)
|
||||
maps.insert(ls.field_0);
|
||||
|
||||
std::string levelUniqueName = levelName;
|
||||
while (maps.find(levelUniqueName) != maps.end())
|
||||
levelUniqueName += "-";
|
||||
|
||||
int renameResult = rename((m_worldsPath + "/" + oldName).c_str(), (m_worldsPath + "/" + levelUniqueName).c_str());
|
||||
if (renameResult != 0)
|
||||
levelUniqueName = oldName;
|
||||
|
||||
LevelData ld;
|
||||
ExternalFileLevelStorage::readLevelData(m_worldsPath + "/" + levelUniqueName + "/" + "level.dat", &ld);
|
||||
ld.setLevelName(levelName);
|
||||
ExternalFileLevelStorage::writeLevelData(m_worldsPath + "/" + levelUniqueName + "/" + "level.dat", &ld);
|
||||
}
|
||||
|
||||
bool ExternalFileLevelStorageSource::isConvertible(const std::string&)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ExternalFileLevelStorageSource::requiresConversion(const std::string&)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int ExternalFileLevelStorageSource::convertLevel(const std::string&, ProgressListener*)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ExternalFileLevelStorageSource::addLevelSummaryIfExists(std::vector<LevelSummary>& vls, const char* name)
|
||||
{
|
||||
std::string levelDat = m_worldsPath + "/" + name + "/" + "level.dat";
|
||||
|
||||
LevelData ld;
|
||||
|
||||
if (!ExternalFileLevelStorage::readLevelData(levelDat, &ld))
|
||||
return;
|
||||
|
||||
LevelSummary ls;
|
||||
ls.field_0 = name;
|
||||
ls.field_18 = ld.field_78;
|
||||
ls.field_30 = ld.field_14;
|
||||
ls.field_34 = ld.field_18;
|
||||
vls.push_back(ls);
|
||||
}
|
||||
40
source/World/Storage/ExternalFileLevelStorageSource.hpp
Normal file
40
source/World/Storage/ExternalFileLevelStorageSource.hpp
Normal file
@@ -0,0 +1,40 @@
|
||||
/********************************************************************
|
||||
Minecraft: Pocket Edition - Decompilation Project
|
||||
Copyright (C) 2023 iProgramInCpp
|
||||
|
||||
The following code is licensed under the BSD 1 clause license.
|
||||
SPDX-License-Identifier: BSD-1-Clause
|
||||
********************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
#include "LevelStorageSource.hpp"
|
||||
|
||||
#ifndef DEMO
|
||||
|
||||
class ExternalFileLevelStorageSource : public LevelStorageSource
|
||||
{
|
||||
public:
|
||||
ExternalFileLevelStorageSource(const std::string& path);
|
||||
|
||||
std::string getName() override;
|
||||
LevelStorage* selectLevel(const std::string&, bool) override;
|
||||
void getLevelList(std::vector<LevelSummary>&);
|
||||
void clearAll() override;
|
||||
int getDataTagFor(const std::string&) override;
|
||||
bool isNewLevelIdAcceptable(const std::string&) override;
|
||||
void deleteLevel(const std::string&) override;
|
||||
void renameLevel(const std::string&, const std::string&) override;
|
||||
bool isConvertible(const std::string&) override;
|
||||
bool requiresConversion(const std::string&) override;
|
||||
int convertLevel(const std::string&, ProgressListener*) override;
|
||||
|
||||
void addLevelSummaryIfExists(std::vector<LevelSummary>& vls, const char* name);
|
||||
|
||||
public:
|
||||
std::string m_worldsPath;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -14,6 +14,8 @@ LevelStorage::~LevelStorage()
|
||||
|
||||
void LevelStorage::saveLevelData(LevelData* levelData)
|
||||
{
|
||||
std::vector<Player*> nothing;
|
||||
saveLevelData(levelData, nothing);
|
||||
}
|
||||
|
||||
void LevelStorage::savePlayerData(LevelData* levelData, std::vector<Player*>& players)
|
||||
|
||||
@@ -48,6 +48,6 @@ public:
|
||||
virtual void renameLevel(const std::string&, const std::string&) = 0;
|
||||
virtual bool isConvertible(const std::string&) = 0;
|
||||
virtual bool requiresConversion(const std::string&) = 0;
|
||||
virtual void convertLevel(const std::string&, ProgressListener*) = 0;
|
||||
virtual int convertLevel(const std::string&, ProgressListener*) = 0;
|
||||
};
|
||||
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
#include "MemoryLevelStorage.hpp"
|
||||
#include "MemoryLevelStorageSource.hpp"
|
||||
|
||||
#ifdef DEMO
|
||||
|
||||
std::string MemoryLevelStorageSource::getName()
|
||||
{
|
||||
return "Memory Storage";
|
||||
@@ -52,7 +54,9 @@ bool MemoryLevelStorageSource::requiresConversion(const std::string& x)
|
||||
return false;
|
||||
}
|
||||
|
||||
void MemoryLevelStorageSource::convertLevel(const std::string& x, ProgressListener* y)
|
||||
int MemoryLevelStorageSource::convertLevel(const std::string& x, ProgressListener* y)
|
||||
{
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
#include "LevelStorageSource.hpp"
|
||||
|
||||
#ifdef DEMO
|
||||
class MemoryLevelStorageSource : public LevelStorageSource
|
||||
{
|
||||
std::string getName() override;
|
||||
@@ -21,6 +22,6 @@ class MemoryLevelStorageSource : public LevelStorageSource
|
||||
void renameLevel(const std::string&, const std::string&) override;
|
||||
bool isConvertible(const std::string&) override;
|
||||
bool requiresConversion(const std::string&) override;
|
||||
void convertLevel(const std::string&, ProgressListener*) override;
|
||||
int convertLevel(const std::string&, ProgressListener*) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user