* Start work on ExternalFileLevelStorage

This commit is contained in:
iProgramInCpp
2023-08-02 09:07:20 +03:00
parent 858315ce54
commit 865d688212
9 changed files with 378 additions and 1 deletions

View File

@@ -41,4 +41,7 @@
// Toggle Demo Mode
//#define DEMO
// Enable Debug Mode
#define MC_DEBUG
#endif

View File

@@ -11,17 +11,40 @@
#include "Utils.hpp"
#ifdef _WIN32
#include <Windows.h>
#include <io.h>
#include <direct.h>
// XPL means "Cross PLatform"
#define XPL_ACCESS _access
#define XPL_MKDIR(path, mode) _mkdir(path)
// 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;
bool createFolderIfNotExists(const char* pDir)
{
if (!XPL_ACCESS(pDir, 0))
return true;
return XPL_MKDIR(pDir, 0755) == 0;
}
const char* GetTerrainName()
{
return "terrain.png";

View File

@@ -504,6 +504,7 @@ constexpr float Lerp(float a, float b, float progress)
return a + progress * (b - a);
}
bool createFolderIfNotExists(const char* pDir);
// things that we added:
#ifndef ORIGINAL_CODE
@@ -537,3 +538,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

View File

@@ -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, int d)
{
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);
}

View File

@@ -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,24 @@ struct LevelData
LevelData();
LevelData(TLong seed, const std::string&, int);
void read(RakNet::BitStream& bs, int d);
void write(RakNet::BitStream& bs, int d);
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;
}
};

View File

@@ -0,0 +1,225 @@
#include "ExternalFileLevelStorage.hpp"
#include "LevelChunk.hpp"
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()
{
}
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(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;
}

View File

@@ -0,0 +1,41 @@
#pragma once
#include <list>
#include "LevelStorage.hpp"
#include "ChunkStorage.hpp"
#include "RegionFile.hpp"
#ifndef DEMO
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);
public:
std::string field_8;
std::string m_levelDirPath;
LevelData* m_pLevelData = nullptr;
RegionFile* m_pRegionFile = nullptr;
Level* m_pLevel = nullptr;
std::list<UnsavedLevelChunk> m_unsavedLevelChunks;
};
#endif

View File

@@ -154,6 +154,7 @@
<ClInclude Include="..\source\World\Renderer\TntRenderer.hpp" />
<ClInclude Include="..\source\World\Renderer\TripodCameraRenderer.hpp" />
<ClInclude Include="..\source\World\Storage\ChunkStorage.hpp" />
<ClInclude Include="..\source\World\Storage\ExternalFileLevelStorage.hpp" />
<ClInclude Include="..\source\World\Storage\LevelSource.hpp" />
<ClInclude Include="..\source\World\Storage\LevelStorage.hpp" />
<ClInclude Include="..\source\World\Storage\LevelStorageSource.hpp" />
@@ -472,6 +473,7 @@
<ClCompile Include="..\source\World\Renderer\TntRenderer.cpp" />
<ClCompile Include="..\source\World\Renderer\TripodCameraRenderer.cpp" />
<ClCompile Include="..\source\World\Storage\ChunkStorage.cpp" />
<ClCompile Include="..\source\World\Storage\ExternalFileLevelStorage.cpp" />
<ClCompile Include="..\source\World\Storage\LevelSource.cpp" />
<ClCompile Include="..\source\World\Storage\LevelStorage.cpp" />
<ClCompile Include="..\source\World\Storage\LevelStorageSource.cpp" />

View File

@@ -1062,6 +1062,9 @@
<ClCompile Include="..\source\World\RegionFile.cpp">
<Filter>Source Files\World</Filter>
</ClCompile>
<ClCompile Include="..\source\World\Storage\ExternalFileLevelStorage.cpp">
<Filter>Source Files\World\Storage</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\source\Options.hpp">
@@ -1931,6 +1934,9 @@
<ClInclude Include="..\source\World\RegionFile.hpp">
<Filter>Header Files\World</Filter>
</ClInclude>
<ClInclude Include="..\source\World\Storage\ExternalFileLevelStorage.hpp">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Text Include="..\thirdparty\raknet\CMakeLists.txt">