From 858315ce54241637f60bd31c2450ff03aba079fb Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Tue, 1 Aug 2023 16:15:13 +0300 Subject: [PATCH 1/8] * Add RegionFile class. --- source/World/RegionFile.cpp | 166 ++++++++++++++++++++++++ source/World/RegionFile.hpp | 27 ++++ windows_vs/minecraftcpp.vcxproj | 2 + windows_vs/minecraftcpp.vcxproj.filters | 6 + 4 files changed, 201 insertions(+) create mode 100644 source/World/RegionFile.cpp create mode 100644 source/World/RegionFile.hpp diff --git a/source/World/RegionFile.cpp b/source/World/RegionFile.cpp new file mode 100644 index 0000000..72c169d --- /dev/null +++ b/source/World/RegionFile.cpp @@ -0,0 +1,166 @@ +#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_20, 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++) + { + if (field_20[i]) + { + for (int j = 0; j < uint8_t(field_20[i]); j++) + { + field_28[j + (field_20[i] >> 8)] = 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; +} + +bool RegionFile::readChunk(int x, int z, RakNet::BitStream** pBitStream) +{ + int idx = field_20[32 * z + x]; + if (!idx) + return false; + + int length = 0; + fseek(m_pFile, (idx >> 8) * 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; + 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; +} diff --git a/source/World/RegionFile.hpp b/source/World/RegionFile.hpp new file mode 100644 index 0000000..0b873f2 --- /dev/null +++ b/source/World/RegionFile.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include +#include +#include +#include +#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 field_28; +}; + diff --git a/windows_vs/minecraftcpp.vcxproj b/windows_vs/minecraftcpp.vcxproj index 4d0638b..8acb945 100644 --- a/windows_vs/minecraftcpp.vcxproj +++ b/windows_vs/minecraftcpp.vcxproj @@ -137,6 +137,7 @@ + @@ -454,6 +455,7 @@ + diff --git a/windows_vs/minecraftcpp.vcxproj.filters b/windows_vs/minecraftcpp.vcxproj.filters index 1c93f6a..94636be 100644 --- a/windows_vs/minecraftcpp.vcxproj.filters +++ b/windows_vs/minecraftcpp.vcxproj.filters @@ -1059,6 +1059,9 @@ Source Files\App + + Source Files\World + @@ -1925,6 +1928,9 @@ Header Files + + Header Files\World + From 865d6882120bba28b9def9f85736720eb416edef Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Wed, 2 Aug 2023 09:07:20 +0300 Subject: [PATCH 2/8] * Start work on ExternalFileLevelStorage --- GameMods.hpp | 3 + source/Base/Utils.cpp | 23 ++ source/Base/Utils.hpp | 21 ++ source/World/LevelData.cpp | 45 ++++ source/World/LevelData.hpp | 13 +- .../Storage/ExternalFileLevelStorage.cpp | 225 ++++++++++++++++++ .../Storage/ExternalFileLevelStorage.hpp | 41 ++++ windows_vs/minecraftcpp.vcxproj | 2 + windows_vs/minecraftcpp.vcxproj.filters | 6 + 9 files changed, 378 insertions(+), 1 deletion(-) create mode 100644 source/World/Storage/ExternalFileLevelStorage.cpp create mode 100644 source/World/Storage/ExternalFileLevelStorage.hpp diff --git a/GameMods.hpp b/GameMods.hpp index 9c08afc..6f60124 100644 --- a/GameMods.hpp +++ b/GameMods.hpp @@ -41,4 +41,7 @@ // Toggle Demo Mode //#define DEMO +// Enable Debug Mode +#define MC_DEBUG + #endif diff --git a/source/Base/Utils.cpp b/source/Base/Utils.cpp index f7956c5..9abc732 100644 --- a/source/Base/Utils.cpp +++ b/source/Base/Utils.cpp @@ -11,17 +11,40 @@ #include "Utils.hpp" #ifdef _WIN32 + #include +#include +#include + +// 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 +#include +#include + +#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"; diff --git a/source/Base/Utils.hpp b/source/Base/Utils.hpp index dff6359..6b33a5c 100644 --- a/source/Base/Utils.hpp +++ b/source/Base/Utils.hpp @@ -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 diff --git a/source/World/LevelData.cpp b/source/World/LevelData.cpp index 1ce5f87..e23aa18 100644 --- a/source/World/LevelData.cpp +++ b/source/World/LevelData.cpp @@ -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); +} diff --git a/source/World/LevelData.hpp b/source/World/LevelData.hpp index f6d0f63..5e6d3d2 100644 --- a/source/World/LevelData.hpp +++ b/source/World/LevelData.hpp @@ -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; + } }; diff --git a/source/World/Storage/ExternalFileLevelStorage.cpp b/source/World/Storage/ExternalFileLevelStorage.cpp new file mode 100644 index 0000000..838df4d --- /dev/null +++ b/source/World/Storage/ExternalFileLevelStorage.cpp @@ -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& 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& 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; +} diff --git a/source/World/Storage/ExternalFileLevelStorage.hpp b/source/World/Storage/ExternalFileLevelStorage.hpp new file mode 100644 index 0000000..88736b7 --- /dev/null +++ b/source/World/Storage/ExternalFileLevelStorage.hpp @@ -0,0 +1,41 @@ +#pragma once + +#include +#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& players) override; + void savePlayerData(LevelData* levelData, std::vector& 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 m_unsavedLevelChunks; +}; + +#endif diff --git a/windows_vs/minecraftcpp.vcxproj b/windows_vs/minecraftcpp.vcxproj index 8acb945..48ee41e 100644 --- a/windows_vs/minecraftcpp.vcxproj +++ b/windows_vs/minecraftcpp.vcxproj @@ -154,6 +154,7 @@ + @@ -472,6 +473,7 @@ + diff --git a/windows_vs/minecraftcpp.vcxproj.filters b/windows_vs/minecraftcpp.vcxproj.filters index 94636be..5ad96c3 100644 --- a/windows_vs/minecraftcpp.vcxproj.filters +++ b/windows_vs/minecraftcpp.vcxproj.filters @@ -1062,6 +1062,9 @@ Source Files\World + + Source Files\World\Storage + @@ -1931,6 +1934,9 @@ Header Files\World + + Header Files + From 3eb8062d06ca039b2a7ae8225bccfe074c4e954b Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Wed, 2 Aug 2023 09:25:05 +0300 Subject: [PATCH 3/8] * Add some more file paths discovered in minecraftpe.apk from assets.minecraft.net --- TODO.md | 67 ++++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 55 insertions(+), 12 deletions(-) diff --git a/TODO.md b/TODO.md index a551aab..483544c 100644 --- a/TODO.md +++ b/TODO.md @@ -13,7 +13,7 @@ ### Save data * [DONE] `PlayerData` -* `RegionFile` +* [DONE] `RegionFile` ### Multiplayer * [DONE] `ServerSideNetworkHandler` @@ -77,24 +77,67 @@ * Attempt to recreate the project structure from Mojang. See the [Reconstructed project structure](#reconstructed-project-structure) ## Reconstructed project structure -(note: some info is present in v0.1.0demo, some in v0.1.1j. The latter will be marked as [J].) +Info extracted from: +* v0.1.0 demo - [D] +* v0.1.1j alpha - [J] +* v0.1.0 touch prototype - [T] Obviously, this is VERY incomplete. This is what we know: * Root: `C:/dev/subversion/mojang/minecraftcpp/trunk/handheld` ``` -project/ - android_java/ - jni/ +project/android/jni/Android.mk (possibly) [D] +project/android/jni/Application.mk (possibly) [D] +src/raknet/* [D] +src/client/gui/components/RolledSelectionList.cpp [T] +src/client/gui/components/ScrolledSelectionList.cpp [T] +src/client/gui/screens/IngameBlockSelectionScreen.cpp [T] +src/client/gui/screens/ProgressScreen.cpp +src/client/gui/Font.cpp +src/client/gui/Gui.cpp +src/client/gui/GuiComponent.cpp +src/client/gui/Screen.cpp +src/client/model/Cube.cpp +src/client/renderer/Chunk.cpp +src/client/renderer/GameRenderer.cpp +src/client/renderer/ItemInHandRenderer.cpp +src/client/renderer/LevelRenderer.cpp +src/client/renderer/RenderList.cpp +src/client/renderer/Tesselator.cpp +src/client/renderer/Textures.cpp +src/client/renderer/gles.cpp +src/client/renderer/entity/EntityRenderer.cpp +src/client/renderer/entity/HumanoidMobRenderer.cpp +src/client/renderer/entity/ItemRenderer.cpp +src/client/renderer/entity/ItemSpriteRenderer.cpp +src/client/renderer/entity/MobRenderer.cpp +src/client/renderer/entity/TntRenderer.cpp +src/client/renderer/entity/TripodCameraRenderer.cpp +src/player/input - maybe + +project/ [D] + android_java/ [D] + jni/ [D] Possibly: Android.mk, Application.mk -src/ - raknet/ - The RakNet source code resides here.[1] - world/ [J] - level/ [J] - storage/ [J] - RegionFile.cpp [J] +src/ [D] + raknet/ [D] + The RakNet source code resides here. [D] + client/ [T] + gui/ [T] + components/ [T] + RolledSelectionList.cpp [T] + ScrolledSelectionList.cpp [T] + screens/ + Font.cpp [T] + Gui.cpp [T] + GuiComponent.cpp [T] + Screen.cpp [T] + world/ [J] + level/ [J] + storage/ [J] + RegionFile.cpp [J] + NinecraftApp.cpp [T] ``` * [1] - In v0.1.1j, the RakNet source files are located at: `C:/dev/subversion/mojang/minecraftcpp/trunk/handheld/project/lib_projects//raknet/jni/RakNetSources/`. From 16e2b79165400ff28554fdbda59b4aaa96fb10b2 Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Wed, 2 Aug 2023 09:58:26 +0300 Subject: [PATCH 4/8] * Finish ExternalFileLevelStorage. --- source/World/LevelData.cpp | 2 +- source/World/LevelData.hpp | 2 +- .../Storage/ExternalFileLevelStorage.cpp | 75 ++++++++++++++++++- .../Storage/ExternalFileLevelStorage.hpp | 9 +++ 4 files changed, 85 insertions(+), 3 deletions(-) diff --git a/source/World/LevelData.cpp b/source/World/LevelData.cpp index e23aa18..7725d50 100644 --- a/source/World/LevelData.cpp +++ b/source/World/LevelData.cpp @@ -35,7 +35,7 @@ void LevelData::read(RakNet::BitStream& bs, int version) field_78 = std::string(rs.C_String()); } -void LevelData::write(RakNet::BitStream& bs, int d) +void LevelData::write(RakNet::BitStream& bs) { bs.Write(m_seed); bs.Write(m_spawnPos.x); diff --git a/source/World/LevelData.hpp b/source/World/LevelData.hpp index 5e6d3d2..4b382ad 100644 --- a/source/World/LevelData.hpp +++ b/source/World/LevelData.hpp @@ -37,7 +37,7 @@ struct LevelData LevelData(TLong seed, const std::string&, int); void read(RakNet::BitStream& bs, int d); - void write(RakNet::BitStream& bs, int d); + void write(RakNet::BitStream& bs); TLong m_seed = 0; Pos m_spawnPos; diff --git a/source/World/Storage/ExternalFileLevelStorage.cpp b/source/World/Storage/ExternalFileLevelStorage.cpp index 838df4d..abf0c6c 100644 --- a/source/World/Storage/ExternalFileLevelStorage.cpp +++ b/source/World/Storage/ExternalFileLevelStorage.cpp @@ -1,5 +1,8 @@ #include "ExternalFileLevelStorage.hpp" -#include "LevelChunk.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), @@ -76,6 +79,57 @@ 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() @@ -223,3 +277,22 @@ _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; +} diff --git a/source/World/Storage/ExternalFileLevelStorage.hpp b/source/World/Storage/ExternalFileLevelStorage.hpp index 88736b7..95664d6 100644 --- a/source/World/Storage/ExternalFileLevelStorage.hpp +++ b/source/World/Storage/ExternalFileLevelStorage.hpp @@ -7,6 +7,13 @@ #ifndef DEMO +struct UnsavedLevelChunk +{ + int m_index; + int m_foundTime; + LevelChunk* m_pChunk; +}; + class ExternalFileLevelStorage : public LevelStorage, ChunkStorage { public: @@ -28,6 +35,7 @@ public: 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; @@ -35,6 +43,7 @@ public: LevelData* m_pLevelData = nullptr; RegionFile* m_pRegionFile = nullptr; Level* m_pLevel = nullptr; + int m_timer = 0; std::list m_unsavedLevelChunks; }; From 450cb190b698c90bc3a8871354d8090da9227eba Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Wed, 2 Aug 2023 13:15:25 +0300 Subject: [PATCH 5/8] * Finish level saving. --- platforms/windows/AppPlatform_windows.cpp | 14 +- platforms/windows/AppPlatform_windows.hpp | 2 + platforms/windows/SoundSystem_windows.cpp | 1 + source/App/NinecraftApp.cpp | 13 +- source/Base/Utils.cpp | 93 ++- source/Base/Utils.hpp | 36 ++ source/GUI/Screen/SelectWorldScreen.cpp | 3 + source/GUI/Screen/StartMenuScreen.cpp | 12 +- source/World/Level.cpp | 1 + source/World/LevelData.hpp | 5 + source/World/RegionFile.cpp | 20 +- .../Storage/ExternalFileLevelStorage.cpp | 10 +- .../Storage/ExternalFileLevelStorage.hpp | 8 + .../ExternalFileLevelStorageSource.cpp | 164 +++++ .../ExternalFileLevelStorageSource.hpp | 40 ++ source/World/Storage/LevelStorage.cpp | 2 + source/World/Storage/LevelStorageSource.hpp | 2 +- .../Storage/MemoryLevelStorageSource.cpp | 8 +- .../Storage/MemoryLevelStorageSource.hpp | 5 +- thirdparty/direntm.h | 585 ++++++++++++++++++ windows_vs/minecraftcpp.vcxproj | 2 + windows_vs/minecraftcpp.vcxproj.filters | 8 +- 22 files changed, 1005 insertions(+), 29 deletions(-) create mode 100644 source/World/Storage/ExternalFileLevelStorageSource.cpp create mode 100644 source/World/Storage/ExternalFileLevelStorageSource.hpp create mode 100644 thirdparty/direntm.h diff --git a/platforms/windows/AppPlatform_windows.cpp b/platforms/windows/AppPlatform_windows.cpp index c61bc41..004fdff 100644 --- a/platforms/windows/AppPlatform_windows.cpp +++ b/platforms/windows/AppPlatform_windows.cpp @@ -103,11 +103,23 @@ void AppPlatform_windows::createUserInput() { m_UserInput.clear(); m_UserInputStatus = -1; + + switch (m_DialogType) + { + case DLG_CREATE_WORLD: + { + // some placeholder for now + m_UserInput.push_back("New World"); + m_UserInput.push_back("123456"); + m_UserInputStatus = 1; + break; + } + } } void AppPlatform_windows::showDialog(eDialogType type) { - // TODO + m_DialogType = type; } std::string AppPlatform_windows::getDateString(int time) diff --git a/platforms/windows/AppPlatform_windows.hpp b/platforms/windows/AppPlatform_windows.hpp index de55845..b4934e7 100644 --- a/platforms/windows/AppPlatform_windows.hpp +++ b/platforms/windows/AppPlatform_windows.hpp @@ -57,6 +57,8 @@ private: std::vector m_UserInput; int m_UserInputStatus = -1; + eDialogType m_DialogType; + bool m_bIsFocused = false; bool m_bGrabbedMouse = false; bool m_bActuallyGrabbedMouse = false; diff --git a/platforms/windows/SoundSystem_windows.cpp b/platforms/windows/SoundSystem_windows.cpp index f545dc5..809dabf 100644 --- a/platforms/windows/SoundSystem_windows.cpp +++ b/platforms/windows/SoundSystem_windows.cpp @@ -92,6 +92,7 @@ void SoundSystemWindows::stop(const std::string& sound) void SoundSystemWindows::playAt(const SoundDesc& sound, float x, float y, float z, float volume, float pitch) { + return; //Release sounds that finished playing for (size_t i = 0; i < m_buffers.size(); i++) diff --git a/source/App/NinecraftApp.cpp b/source/App/NinecraftApp.cpp index 0e8d2a1..d6c9329 100644 --- a/source/App/NinecraftApp.cpp +++ b/source/App/NinecraftApp.cpp @@ -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); diff --git a/source/Base/Utils.cpp b/source/Base/Utils.cpp index 9abc732..2567102 100644 --- a/source/Base/Utils.cpp +++ b/source/Base/Utils.cpp @@ -16,10 +16,6 @@ #include #include -// 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) @@ -37,6 +33,70 @@ 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)) @@ -45,6 +105,31 @@ bool createFolderIfNotExists(const char* pDir) 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"; diff --git a/source/Base/Utils.hpp b/source/Base/Utils.hpp index 6b33a5c..cae19d3 100644 --- a/source/Base/Utils.hpp +++ b/source/Base/Utils.hpp @@ -15,16 +15,51 @@ #include #include +#include + #ifdef _WIN32 // @HACK: Include WinSock2.h also #include #include #include +#include +#include + +// 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 +#include + + // XPL means "Cross PLatform" +#define XPL_ACCESS access +#define XPL_MKDIR(path, mode) mkdir(path, mode) #endif @@ -505,6 +540,7 @@ constexpr float Lerp(float a, float b, float progress) } bool createFolderIfNotExists(const char* pDir); +bool DeleteDirectory(const std::string& name, bool unused); // things that we added: #ifndef ORIGINAL_CODE diff --git a/source/GUI/Screen/SelectWorldScreen.cpp b/source/GUI/Screen/SelectWorldScreen.cpp index 21757f6..dca7304 100644 --- a/source/GUI/Screen/SelectWorldScreen.cpp +++ b/source/GUI/Screen/SelectWorldScreen.cpp @@ -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; } diff --git a/source/GUI/Screen/StartMenuScreen.cpp b/source/GUI/Screen/StartMenuScreen.cpp index e25c0a8..d95218e 100644 --- a/source/GUI/Screen/StartMenuScreen.cpp +++ b/source/GUI/Screen/StartMenuScreen.cpp @@ -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 diff --git a/source/World/Level.cpp b/source/World/Level.cpp index 7e8f91e..a17127d 100644 --- a/source/World/Level.cpp +++ b/source/World/Level.cpp @@ -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 diff --git a/source/World/LevelData.hpp b/source/World/LevelData.hpp index 4b382ad..295a9e5 100644 --- a/source/World/LevelData.hpp +++ b/source/World/LevelData.hpp @@ -55,5 +55,10 @@ struct LevelData { return field_20; } + + void setLevelName(const std::string& name) + { + field_78 = name; + } }; diff --git a/source/World/RegionFile.cpp b/source/World/RegionFile.cpp index 72c169d..dae9203 100644 --- a/source/World/RegionFile.cpp +++ b/source/World/RegionFile.cpp @@ -15,7 +15,7 @@ RegionFile::RegionFile(const std::string fileName) field_20 = new int[1024]; field_24 = new int[1024]; - memset(field_20, 0, 1024 * sizeof(int)); + memset(field_24, 0, 1024 * sizeof(int)); } RegionFile::~RegionFile() @@ -48,11 +48,14 @@ bool RegionFile::open() for (int i = 0; i < 1024; i++) { - if (field_20[i]) + int v13 = this->field_20[i]; + if (v13) { - for (int j = 0; j < uint8_t(field_20[i]); j++) + int v12 = v13 >> 8; + int v11 = uint8_t(v13); + for (int j = 0; j < v11; ++j) { - field_28[j + (field_20[i] >> 8)] = false; + field_28[j + v12] = false; } } } @@ -66,6 +69,8 @@ bool RegionFile::open() WRITE(field_20, sizeof(int), 1024, m_pFile); field_28[0] = false; + + return true; } bool RegionFile::readChunk(int x, int z, RakNet::BitStream** pBitStream) @@ -74,8 +79,11 @@ bool RegionFile::readChunk(int x, int z, RakNet::BitStream** pBitStream) if (!idx) return false; + int thing = (idx >> 8); + int offset = (idx & 0xFF); + int length = 0; - fseek(m_pFile, (idx >> 8) * SECTOR_BYTES, SEEK_SET); + fseek(m_pFile, thing * SECTOR_BYTES, SEEK_SET); fread(&length, sizeof(int), 1, m_pFile); assert(length < ((offset & 0xff) * SECTOR_BYTES)); @@ -104,7 +112,7 @@ 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; + int lowerIndex = (length + 4) / SECTOR_BYTES + 1; if (lowerIndex > 256) return false; diff --git a/source/World/Storage/ExternalFileLevelStorage.cpp b/source/World/Storage/ExternalFileLevelStorage.cpp index abf0c6c..bd1ecc3 100644 --- a/source/World/Storage/ExternalFileLevelStorage.cpp +++ b/source/World/Storage/ExternalFileLevelStorage.cpp @@ -1,3 +1,11 @@ +/******************************************************************** + 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" @@ -237,7 +245,7 @@ bool ExternalFileLevelStorage::readLevelData(const std::string& path, LevelData* if (fread(data, sizeof(uint8_t), length, pFile) != length) { - SAFE_DELETE(data); + SAFE_DELETE_ARRAY(data); goto _cleanup; } diff --git a/source/World/Storage/ExternalFileLevelStorage.hpp b/source/World/Storage/ExternalFileLevelStorage.hpp index 95664d6..1edaffd 100644 --- a/source/World/Storage/ExternalFileLevelStorage.hpp +++ b/source/World/Storage/ExternalFileLevelStorage.hpp @@ -1,3 +1,11 @@ +/******************************************************************** + 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 diff --git a/source/World/Storage/ExternalFileLevelStorageSource.cpp b/source/World/Storage/ExternalFileLevelStorageSource.cpp new file mode 100644 index 0000000..997c177 --- /dev/null +++ b/source/World/Storage/ExternalFileLevelStorageSource.cpp @@ -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 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& 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 vls; + getLevelList(vls); + + std::set 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& 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); +} diff --git a/source/World/Storage/ExternalFileLevelStorageSource.hpp b/source/World/Storage/ExternalFileLevelStorageSource.hpp new file mode 100644 index 0000000..e51f483 --- /dev/null +++ b/source/World/Storage/ExternalFileLevelStorageSource.hpp @@ -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 +#include +#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&); + 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& vls, const char* name); + +public: + std::string m_worldsPath; +}; + +#endif diff --git a/source/World/Storage/LevelStorage.cpp b/source/World/Storage/LevelStorage.cpp index 5f105ff..d3dc3b1 100644 --- a/source/World/Storage/LevelStorage.cpp +++ b/source/World/Storage/LevelStorage.cpp @@ -14,6 +14,8 @@ LevelStorage::~LevelStorage() void LevelStorage::saveLevelData(LevelData* levelData) { + std::vector nothing; + saveLevelData(levelData, nothing); } void LevelStorage::savePlayerData(LevelData* levelData, std::vector& players) diff --git a/source/World/Storage/LevelStorageSource.hpp b/source/World/Storage/LevelStorageSource.hpp index a327515..4e0f263 100644 --- a/source/World/Storage/LevelStorageSource.hpp +++ b/source/World/Storage/LevelStorageSource.hpp @@ -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; }; diff --git a/source/World/Storage/MemoryLevelStorageSource.cpp b/source/World/Storage/MemoryLevelStorageSource.cpp index 25021d5..9a9959b 100644 --- a/source/World/Storage/MemoryLevelStorageSource.cpp +++ b/source/World/Storage/MemoryLevelStorageSource.cpp @@ -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 diff --git a/source/World/Storage/MemoryLevelStorageSource.hpp b/source/World/Storage/MemoryLevelStorageSource.hpp index 4833358..ce45a61 100644 --- a/source/World/Storage/MemoryLevelStorageSource.hpp +++ b/source/World/Storage/MemoryLevelStorageSource.hpp @@ -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 diff --git a/thirdparty/direntm.h b/thirdparty/direntm.h new file mode 100644 index 0000000..2d61617 --- /dev/null +++ b/thirdparty/direntm.h @@ -0,0 +1,585 @@ +/* +MIT License +Copyright (c) 2019 win32ports +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#pragma once + +#ifndef __DIRENT_H_9DE6B42C_8D0C_4D31_A8EF_8E4C30E6C46A__ +#define __DIRENT_H_9DE6B42C_8D0C_4D31_A8EF_8E4C30E6C46A__ + +#ifndef _WIN32 + +//#pragma message("this dirent.h implementation is for Windows only!") -- it's going to just use the regular dirent +#include + +#else /* _WIN32 */ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#include +#include +#include + +#include + +#include + +#ifdef _MSC_VER +#pragma comment(lib, "Shlwapi.lib") +#endif + +#ifndef NAME_MAX +#define NAME_MAX 260 +#endif /* NAME_MAX */ + +#ifndef DT_UNKNOWN +#define DT_UNKNOWN 0 +#endif /* DT_UNKNOWN */ + +#ifndef DT_FIFO +#define DT_FIFO 1 +#endif /* DT_FIFO */ + +#ifndef DT_CHR +#define DT_CHR 2 +#endif /* DT_CHR */ + +#ifndef DT_DIR +#define DT_DIR 4 +#endif /* DT_DIR */ + +#ifndef DT_BLK +#define DT_BLK 6 +#endif /* DT_BLK */ + +#ifndef DT_REG +#define DT_REG 8 +#endif /* DT_REF */ + +#ifndef DT_LNK +#define DT_LNK 10 +#endif /* DT_LNK */ + +#ifndef DT_SOCK +#define DT_SOCK 12 +#endif /* DT_SOCK */ + +#ifndef DT_WHT +#define DT_WHT 14 +#endif /* DT_WHT */ + +#ifndef _DIRENT_HAVE_D_NAMLEN +#define _DIRENT_HAVE_D_NAMLEN 1 +#endif /* _DIRENT_HAVE_D_NAMLEN */ + +#ifndef _DIRENT_HAVE_D_RECLEN +#define _DIRENT_HAVE_D_RECLEN 1 +#endif /* _DIRENT_HAVE_D_RECLEN */ + +#ifndef _DIRENT_HAVE_D_OFF +#define _DIRENT_HAVE_D_OFF 1 +#endif /* _DIRENT_HAVE_D_OFF */ + +#ifndef _DIRENT_HAVE_D_TYPE +#define _DIRENT_HAVE_D_TYPE 1 +#endif /* _DIRENT_HAVE_D_TYPE */ + +#ifndef NTFS_MAX_PATH +#define NTFS_MAX_PATH 32768 +#endif /* NTFS_MAX_PATH */ + +#ifndef FSCTL_GET_REPARSE_POINT +#define FSCTL_GET_REPARSE_POINT 0x900a8 +#endif /* FSCTL_GET_REPARSE_POINT */ + +#ifndef FILE_NAME_NORMALIZED +#define FILE_NAME_NORMALIZED 0 +#endif /* FILE_NAME_NORMALIZED */ + +typedef void* DIR; + +typedef struct ino_t +{ + unsigned long long serial; + unsigned char fileid[16]; +} __ino_t; + +struct dirent +{ + __ino_t d_ino; + off_t d_off; + unsigned short d_reclen; + unsigned char d_namelen; + unsigned char d_type; + char d_name[NAME_MAX]; +}; + +struct __dir +{ + struct dirent* entries; + intptr_t fd; + long int count; + long int index; +}; + +static int closedir(DIR* dirp) +{ + struct __dir* data = NULL; + if (!dirp) { + errno = EBADF; + return -1; + } + data = (struct __dir*) dirp; + CloseHandle((HANDLE)data->fd); + free(data->entries); + free(data); + return 0; +} + +static void __seterrno(int value) +{ +#ifdef _MSC_VER + _set_errno(value); +#else /* _MSC_VER */ + errno = value; +#endif /* _MSC_VER */ +} + +static int __islink(const wchar_t * name, char * buffer) +{ + DWORD io_result = 0; + DWORD bytes_returned = 0; + HANDLE hFile = CreateFileW(name, 0, 0, NULL, OPEN_EXISTING, + FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, 0); + if (hFile == INVALID_HANDLE_VALUE) + return 0; + + io_result = DeviceIoControl(hFile, FSCTL_GET_REPARSE_POINT, NULL, 0, + buffer, MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &bytes_returned, NULL); + + CloseHandle(hFile); + + if (io_result == 0) + return 0; + + return ((REPARSE_GUID_DATA_BUFFER*)buffer)->ReparseTag == IO_REPARSE_TAG_SYMLINK; +} + +#pragma pack(push, 1) + +typedef struct dirent_FILE_ID_128 +{ + BYTE Identifier[16]; +} +dirent_FILE_ID_128; + +typedef struct _dirent_FILE_ID_INFO +{ + ULONGLONG VolumeSerialNumber; + dirent_FILE_ID_128 FileId; +} +dirent_FILE_ID_INFO; + +#pragma pack(pop) + +typedef enum dirent_FILE_INFO_BY_HANDLE_CLASS +{ dirent_FileIdInfo = 18 } +dirent_FILE_INFO_BY_HANDLE_CLASS; + +static __ino_t __inode(const wchar_t* name) +{ + __ino_t value = { 0 }; + BOOL result; + dirent_FILE_ID_INFO fileid; + BY_HANDLE_FILE_INFORMATION info; + typedef BOOL (__stdcall* pfnGetFileInformationByHandleEx)(HANDLE hFile, + dirent_FILE_INFO_BY_HANDLE_CLASS FileInformationClass, + LPVOID lpFileInformation, DWORD dwBufferSize); + + HANDLE hKernel32 = GetModuleHandleW(L"kernel32.dll"); + if (!hKernel32) + return value; + + pfnGetFileInformationByHandleEx fnGetFileInformationByHandleEx = (pfnGetFileInformationByHandleEx) GetProcAddress(hKernel32, "GetFileInformationByHandleEx"); + if (!fnGetFileInformationByHandleEx) + return value; + + HANDLE hFile = CreateFileW(name, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0); + if (hFile == INVALID_HANDLE_VALUE) + return value; + + result = fnGetFileInformationByHandleEx(hFile, dirent_FileIdInfo, &fileid, sizeof(fileid)); + if (result) + { + value.serial = fileid.VolumeSerialNumber; + memcpy(value.fileid, fileid.FileId.Identifier, 16); + } + else + { + result = GetFileInformationByHandle(hFile, &info); + if(result) + { + value.serial = info.dwVolumeSerialNumber; + memcpy(value.fileid + 8, &info.nFileIndexHigh, 4); + memcpy(value.fileid + 12, &info.nFileIndexLow, 4); + } + } + CloseHandle(hFile); + return value; +} + +static DIR* __internal_opendir(wchar_t* wname, int size) +{ + struct __dir* data = NULL; + struct dirent *tmp_entries = NULL; + static char default_char = '?'; + static wchar_t* prefix = L"\\\\?\\"; + static wchar_t* suffix = L"\\*.*"; + static int extra_prefix = 4; /* use prefix "\\?\" to handle long file names */ + static int extra_suffix = 4; /* use suffix "\*.*" to find everything */ + WIN32_FIND_DATAW w32fd = { 0 }; + HANDLE hFindFile = INVALID_HANDLE_VALUE; + static int grow_factor = 2; + char* buffer = NULL; + + BOOL relative = PathIsRelativeW(wname + extra_prefix); + + memcpy(wname + size - 1, suffix, sizeof(wchar_t) * extra_suffix); + wname[size + extra_suffix - 1] = 0; + + if (relative) { + wname += extra_prefix; + size -= extra_prefix; + } + hFindFile = FindFirstFileW(wname, &w32fd); + if (INVALID_HANDLE_VALUE == hFindFile) + { + __seterrno(ENOENT); + return NULL; + } + + data = (struct __dir*) malloc(sizeof(struct __dir)); + if (!data) + goto out_of_memory; + wname[size - 1] = 0; + data->fd = (intptr_t)CreateFileW(wname, 0, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0); + wname[size - 1] = L'\\'; + data->count = 16; + data->index = 0; + data->entries = (struct dirent*) malloc(sizeof(struct dirent) * data->count); + if (!data->entries) + goto out_of_memory; + buffer = malloc(MAXIMUM_REPARSE_DATA_BUFFER_SIZE); + if (!buffer) + goto out_of_memory; + do + { + WideCharToMultiByte(CP_UTF8, 0, w32fd.cFileName, -1, data->entries[data->index].d_name, NAME_MAX, &default_char, NULL); + + memcpy(wname + size, w32fd.cFileName, sizeof(wchar_t) * NAME_MAX); + + if (((w32fd.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) == FILE_ATTRIBUTE_REPARSE_POINT) && __islink(wname, buffer)) + data->entries[data->index].d_type = DT_LNK; + else if ((w32fd.dwFileAttributes & FILE_ATTRIBUTE_DEVICE) == FILE_ATTRIBUTE_DEVICE) + data->entries[data->index].d_type = DT_CHR; + else if ((w32fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY) + data->entries[data->index].d_type = DT_DIR; + else + data->entries[data->index].d_type = DT_REG; + + data->entries[data->index].d_ino = __inode(wname); + data->entries[data->index].d_reclen = sizeof(struct dirent); + data->entries[data->index].d_namelen = (unsigned char)wcslen(w32fd.cFileName); + data->entries[data->index].d_off = 0; + + if (++data->index == data->count) { + tmp_entries = (struct dirent*) realloc(data->entries, sizeof(struct dirent) * data->count * grow_factor); + if (!tmp_entries) + goto out_of_memory; + data->entries = tmp_entries; + data->count *= grow_factor; + } + } + while (FindNextFileW(hFindFile, &w32fd) != 0); + + free(buffer); + FindClose(hFindFile); + + data->count = data->index; + data->index = 0; + return (DIR*)data; +out_of_memory: + if (data) + { + if (INVALID_HANDLE_VALUE != (HANDLE)data->fd) + CloseHandle((HANDLE)data->fd); + free(data->entries); + } + free(buffer); + free(data); + if (INVALID_HANDLE_VALUE != hFindFile) + FindClose(hFindFile); + __seterrno(ENOMEM); + return NULL; +} + +static wchar_t* __get_buffer() +{ + wchar_t* name = malloc(sizeof(wchar_t) * (NTFS_MAX_PATH + NAME_MAX + 8)); + if (name) + memcpy(name, L"\\\\?\\", sizeof(wchar_t) * 4); + return name; +} + +static DIR* opendir(const char* name) +{ + DIR* dirp = NULL; + wchar_t* wname = __get_buffer(); + int size = 0; + if (!wname) + { + errno = ENOMEM; + return NULL; + } + size = MultiByteToWideChar(CP_UTF8, 0, name, -1, wname + 4, NTFS_MAX_PATH); + if (0 == size) + { + free(wname); + return NULL; + } + dirp = __internal_opendir(wname, size + 4); + free(wname); + return dirp; +} + +static DIR* _wopendir(const wchar_t* name) +{ + DIR* dirp = NULL; + wchar_t* wname = __get_buffer(); + int size = 0; + if (!wname) + { + errno = ENOMEM; + return NULL; + } + size = (int)wcslen(name); + if (size > NTFS_MAX_PATH) + { + free(wname); + return NULL; + } + memcpy(wname + 4, name, sizeof(wchar_t) * (size + 1)); + dirp = __internal_opendir(wname, size + 5); + free(wname); + return dirp; +} + +static DIR* fdopendir(intptr_t fd) +{ + DIR* dirp = NULL; + wchar_t* wname = __get_buffer(); + typedef DWORD (__stdcall * pfnGetFinalPathNameByHandleW)( + HANDLE hFile, LPWSTR lpszFilePath, DWORD cchFilePath, DWORD dwFlags); + + HANDLE hKernel32 = GetModuleHandleW(L"kernel32.dll"); + if (!hKernel32) + { + errno = EINVAL; + return NULL; + } + + pfnGetFinalPathNameByHandleW fnGetFinalPathNameByHandleW = (pfnGetFinalPathNameByHandleW) GetProcAddress(hKernel32, "GetFinalPathNameByHandleW"); + if (!fnGetFinalPathNameByHandleW) + { + errno = EINVAL; + return NULL; + } + + int size = 0; + if (!wname) + { + errno = ENOMEM; + return NULL; + } + size = fnGetFinalPathNameByHandleW((HANDLE) fd, wname + 4, NTFS_MAX_PATH, FILE_NAME_NORMALIZED); + if (0 == size) + { + free(wname); + errno = ENOTDIR; + return NULL; + } + dirp = __internal_opendir(wname, size + 5); + free(wname); + return dirp; +} + +static struct dirent* readdir(DIR* dirp) +{ + struct __dir* data = (struct __dir*) dirp; + if (!data) { + errno = EBADF; + return NULL; + } + if (data->index < data->count) + { + return &data->entries[data->index++]; + } + return NULL; +} + +static int readdir_r(DIR* dirp, struct dirent* entry, struct dirent**result) +{ + struct __dir* data = (struct __dir*) dirp; + if (!data) { + return EBADF; + } + if (data->index < data->count) + { + if (entry) + memcpy(entry, &data->entries[data->index++], sizeof(struct dirent)); + if (result) + *result = entry; + } + else if (result) + *result = NULL; + return 0; +} + +static void seekdir(DIR* dirp, long int offset) +{ + if (dirp) + { + struct __dir* data = (struct __dir*) dirp; + data->index = (offset < data->count) ? offset : data->index; + } +} + +static void rewinddir(DIR* dirp) +{ + seekdir(dirp, 0); +} + +static long int telldir(DIR* dirp) +{ + if (!dirp) { + errno = EBADF; + return -1; + } + return ((struct __dir*)dirp)->count; +} + +static intptr_t dirfd(DIR * dirp) +{ + if (!dirp) { + errno = EINVAL; + return -1; + } + return ((struct __dir*)dirp)->fd; +} + +static int scandir(const char* dirp, struct dirent*** namelist, + int (*filter)(const struct dirent*), + int (*compar)(const struct dirent**, const struct dirent**)) +{ + struct dirent ** entries = NULL, ** tmp_entries = NULL; + long int i = 0, index = 0, count = 16; + DIR * d = opendir(dirp); + struct __dir* data = (struct __dir*) d; + if (!data) { + closedir(d); + __seterrno(ENOENT); + return -1; + } + entries = (struct dirent**) malloc(sizeof(struct dirent*) * count); + if (!entries) + { + closedir(d); + __seterrno(ENOMEM); + return -1; + } + for (i = 0; i < data->count; ++i) + { + if (!filter || filter(&data->entries[i])) + { + entries[index] = (struct dirent*) malloc(sizeof(struct dirent)); + if (!entries[index]) + { + closedir(d); + for (i = 0; i < index; ++i) + free(entries[index]); + free(entries); + __seterrno(ENOMEM); + return -1; + } + memcpy(entries[index], &data->entries[i], sizeof(struct dirent)); + if (++index == count) + { + tmp_entries = (struct dirent**)realloc(entries, sizeof(struct dirent*) * count * 2); + if (!tmp_entries) + { + closedir(d); + for (i = 0; i < index; ++i) + free(entries[index - 1]); + free(entries); + __seterrno(ENOMEM); + return -1; + } + entries = tmp_entries; + count *= 2; + } + } + } + qsort(entries, index, sizeof(struct dirent*), compar); + entries[index] = NULL; + if (namelist) + *namelist = entries; + closedir(d); + return 0; +} + +int alphasort(const void* a, const void* b) +{ + struct dirent** dira = (struct dirent**)a, **dirb = (struct dirent**)b; + if (!dira || !dirb) + return 0; + return strcoll((*dira)->d_name, (*dirb)->d_name); +} + +static int __strverscmp(const char* s1, const char* s2) +{ + return alphasort(s1, s2); +} + +int versionsort(const void* a, const void* b) +{ + struct dirent** dira = (struct dirent**)a, ** dirb = (struct dirent**)b; + if (!dira || !dirb) + return 0; + return __strverscmp((*dira)->d_name, (*dirb)->d_name); +} + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* _WIN32 */ + +#endif /* __DIRENT_H_9DE6B42C_8D0C_4D31_A8EF_8E4C30E6C46A__ */ \ No newline at end of file diff --git a/windows_vs/minecraftcpp.vcxproj b/windows_vs/minecraftcpp.vcxproj index 48ee41e..e6d0a37 100644 --- a/windows_vs/minecraftcpp.vcxproj +++ b/windows_vs/minecraftcpp.vcxproj @@ -155,6 +155,7 @@ + @@ -474,6 +475,7 @@ + diff --git a/windows_vs/minecraftcpp.vcxproj.filters b/windows_vs/minecraftcpp.vcxproj.filters index 5ad96c3..e8d3fcf 100644 --- a/windows_vs/minecraftcpp.vcxproj.filters +++ b/windows_vs/minecraftcpp.vcxproj.filters @@ -1065,6 +1065,9 @@ Source Files\World\Storage + + Source Files\World\Storage + @@ -1934,9 +1937,12 @@ Header Files\World - + Header Files + + Header Files\World\Storage + From 6683422467b4948f5a446eece1f48f4c9911087e Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Wed, 2 Aug 2023 13:18:14 +0300 Subject: [PATCH 6/8] * Force save when leaving. Kind of sucks. I'll work on it :) --- source/App/Minecraft.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/source/App/Minecraft.cpp b/source/App/Minecraft.cpp index a5e1e7e..406e598 100644 --- a/source/App/Minecraft.cpp +++ b/source/App/Minecraft.cpp @@ -839,6 +839,14 @@ void Minecraft::leaveGame(bool bCopyMap) // @BUG: Deleting ServerSideNetworkHandler too late! This causes // access to invalid memory in the destructor seeing as we already deleted the level. delete m_pNetEventCallback; + + // @NOTE: Saving only happens once every 50 ticks. Force it to happen when quitting. + if (m_pLevel) + { + m_pLevel->saveAllChunks(); + m_pLevel->saveLevelData(); + m_pLevel->savePlayerData(); + } #endif if (m_pLevel) From d363d8d29824d9ee13d7c65bf07330cc18afac18 Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Wed, 2 Aug 2023 13:53:35 +0300 Subject: [PATCH 7/8] * Add improved saving. --- GameMods.hpp | 1 + source/App/Minecraft.cpp | 18 +++---- source/GUI/Screen/SavingWorldScreen.cpp | 63 +++++++++++++++++++++++++ source/GUI/Screen/SavingWorldScreen.hpp | 20 ++++++++ windows_vs/minecraftcpp.vcxproj | 2 + windows_vs/minecraftcpp.vcxproj.filters | 12 +++-- 6 files changed, 104 insertions(+), 12 deletions(-) create mode 100644 source/GUI/Screen/SavingWorldScreen.cpp create mode 100644 source/GUI/Screen/SavingWorldScreen.hpp diff --git a/GameMods.hpp b/GameMods.hpp index 6f60124..f3488dd 100644 --- a/GameMods.hpp +++ b/GameMods.hpp @@ -29,6 +29,7 @@ #define ENH_ALLOW_SCROLL_WHEEL // Allow use of the scroll wheel to change selected inventory slots #define ENH_DISABLE_TURN_ACCEL // Disable the turn acceleration mechanism. It should only be used on Xperia Play #define ENH_3D_INVENTORY_TILES // Uses 3D rendered inventory tiles, use with ENH_SHADE_HELD_TILES to render correctly. +#define ENH_IMPROVED_SAVING // Improve world saving. The original Minecraft doesn't always really save for some reason // Mods //#define MOD_USE_FLAT_WORLD // Use a flat world instead of the regular world generation diff --git a/source/App/Minecraft.cpp b/source/App/Minecraft.cpp index 406e598..cf44b12 100644 --- a/source/App/Minecraft.cpp +++ b/source/App/Minecraft.cpp @@ -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) @@ -839,16 +840,12 @@ void Minecraft::leaveGame(bool bCopyMap) // @BUG: Deleting ServerSideNetworkHandler too late! This causes // access to invalid memory in the destructor seeing as we already deleted the level. delete m_pNetEventCallback; - - // @NOTE: Saving only happens once every 50 ticks. Force it to happen when quitting. - if (m_pLevel) - { - m_pLevel->saveAllChunks(); - m_pLevel->saveLevelData(); - m_pLevel->savePlayerData(); - } #endif +#ifdef ENH_IMPROVED_SAVING + field_288 = true; + setScreen(new SavingWorldScreen(bCopyMap)); +#else if (m_pLevel) { LevelStorage* pStorage = m_pLevel->getLevelStorage(); @@ -857,6 +854,7 @@ void Minecraft::leaveGame(bool bCopyMap) m_pLevel = nullptr; } +#endif #ifdef ORIGINAL_CODE delete m_pNetEventCallback; @@ -865,10 +863,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() diff --git a/source/GUI/Screen/SavingWorldScreen.cpp b/source/GUI/Screen/SavingWorldScreen.cpp new file mode 100644 index 0000000..3044488 --- /dev/null +++ b/source/GUI/Screen/SavingWorldScreen.cpp @@ -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 diff --git a/source/GUI/Screen/SavingWorldScreen.hpp b/source/GUI/Screen/SavingWorldScreen.hpp new file mode 100644 index 0000000..9a3cd23 --- /dev/null +++ b/source/GUI/Screen/SavingWorldScreen.hpp @@ -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 diff --git a/windows_vs/minecraftcpp.vcxproj b/windows_vs/minecraftcpp.vcxproj index e6d0a37..39e0061 100644 --- a/windows_vs/minecraftcpp.vcxproj +++ b/windows_vs/minecraftcpp.vcxproj @@ -65,6 +65,7 @@ + @@ -358,6 +359,7 @@ + diff --git a/windows_vs/minecraftcpp.vcxproj.filters b/windows_vs/minecraftcpp.vcxproj.filters index e8d3fcf..90766ac 100644 --- a/windows_vs/minecraftcpp.vcxproj.filters +++ b/windows_vs/minecraftcpp.vcxproj.filters @@ -1068,6 +1068,9 @@ Source Files\World\Storage + + Source Files\GUI\Screen + @@ -1937,12 +1940,15 @@ Header Files\World - - Header Files - Header Files\World\Storage + + Header Files\World\Storage + + + Header Files\GUI\Screen + From b485071d002f578d7318fc0a3380727002a5b32f Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Wed, 2 Aug 2023 14:03:57 +0300 Subject: [PATCH 8/8] * Unblock sound playback --- platforms/windows/SoundSystem_windows.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/platforms/windows/SoundSystem_windows.cpp b/platforms/windows/SoundSystem_windows.cpp index 809dabf..cea2684 100644 --- a/platforms/windows/SoundSystem_windows.cpp +++ b/platforms/windows/SoundSystem_windows.cpp @@ -92,8 +92,6 @@ void SoundSystemWindows::stop(const std::string& sound) void SoundSystemWindows::playAt(const SoundDesc& sound, float x, float y, float z, float volume, float pitch) { - return; - //Release sounds that finished playing for (size_t i = 0; i < m_buffers.size(); i++) {