#include "LevelGenerationOptions.h" #include #include #include #include "4J_Storage.h" #include "Minecraft.Client/Common/src/DLC/DLCPack.h" #include "Minecraft.Client/Common/src/GameRules/ConsoleGameRules.h" #include "Minecraft.Client/Common/src/Localisation/StringTable.h" #include "Minecraft.Client/include/NetTypes.h" #include "Minecraft.Client/include/SkinBox.h" #include "Minecraft.Client/include/XboxStubs.h" #include "Minecraft.Client/include/stdafx.h" #include "console_helpers/StringHelpers.h" #include "minecraft/Pos.h" #include "minecraft/world/level/chunk/net.minecraft.world.level.chunk.h" #include "minecraft/world/level/net.minecraft.world.level.h" #include "minecraft/world/phys/net.minecraft.world.phys.h" JustGrSource::JustGrSource() { m_displayName = L"Default_DisplayName"; m_worldName = L"Default_WorldName"; m_defaultSaveName = L"Default_DefaultSaveName"; m_bRequiresTexturePack = false; m_requiredTexturePackId = 0; m_grfPath = L"__NO_GRF_PATH__"; m_bRequiresBaseSave = false; } bool JustGrSource::requiresTexturePack() { return m_bRequiresTexturePack; } std::uint32_t JustGrSource::getRequiredTexturePackId() { return m_requiredTexturePackId; } std::wstring JustGrSource::getDefaultSaveName() { return m_defaultSaveName; } const wchar_t* JustGrSource::getWorldName() { return m_worldName.c_str(); } const wchar_t* JustGrSource::getDisplayName() { return m_displayName.c_str(); } std::wstring JustGrSource::getGrfPath() { return m_grfPath; } bool JustGrSource::requiresBaseSave() { return m_bRequiresBaseSave; }; std::wstring JustGrSource::getBaseSavePath() { return m_baseSavePath; }; void JustGrSource::setRequiresTexturePack(bool x) { m_bRequiresTexturePack = x; } void JustGrSource::setRequiredTexturePackId(std::uint32_t x) { m_requiredTexturePackId = x; } void JustGrSource::setDefaultSaveName(const std::wstring& x) { m_defaultSaveName = x; } void JustGrSource::setWorldName(const std::wstring& x) { m_worldName = x; } void JustGrSource::setDisplayName(const std::wstring& x) { m_displayName = x; } void JustGrSource::setGrfPath(const std::wstring& x) { m_grfPath = x; } void JustGrSource::setBaseSavePath(const std::wstring& x) { m_baseSavePath = x; m_bRequiresBaseSave = true; } bool JustGrSource::ready() { return true; } LevelGenerationOptions::LevelGenerationOptions(DLCPack* parentPack) { m_spawnPos = nullptr; m_stringTable = nullptr; m_hasLoadedData = false; m_seed = 0; m_bHasBeenInCreative = true; m_useFlatWorld = false; m_bHaveMinY = false; m_minY = INT_MAX; m_bRequiresGameRules = false; m_pbBaseSaveData = nullptr; m_baseSaveSize = 0; m_parentDLCPack = parentPack; m_bLoadingData = false; } LevelGenerationOptions::~LevelGenerationOptions() { clearSchematics(); if (m_spawnPos != nullptr) delete m_spawnPos; for (auto it = m_schematicRules.begin(); it != m_schematicRules.end(); ++it) { delete *it; } for (auto it = m_structureRules.begin(); it != m_structureRules.end(); ++it) { delete *it; } for (auto it = m_biomeOverrides.begin(); it != m_biomeOverrides.end(); ++it) { delete *it; } for (auto it = m_features.begin(); it != m_features.end(); ++it) { delete *it; } if (m_stringTable) if (!isTutorial()) delete m_stringTable; if (isFromSave()) delete m_pSrc; } ConsoleGameRules::EGameRuleType LevelGenerationOptions::getActionType() { return ConsoleGameRules::eGameRuleType_LevelGenerationOptions; } void LevelGenerationOptions::writeAttributes(DataOutputStream* dos, unsigned int numAttrs) { GameRuleDefinition::writeAttributes(dos, numAttrs + 5); ConsoleGameRules::write(dos, ConsoleGameRules::eGameRuleAttr_spawnX); dos->writeUTF(_toString(m_spawnPos->x)); ConsoleGameRules::write(dos, ConsoleGameRules::eGameRuleAttr_spawnY); dos->writeUTF(_toString(m_spawnPos->y)); ConsoleGameRules::write(dos, ConsoleGameRules::eGameRuleAttr_spawnZ); dos->writeUTF(_toString(m_spawnPos->z)); ConsoleGameRules::write(dos, ConsoleGameRules::eGameRuleAttr_seed); dos->writeUTF(_toString(m_seed)); ConsoleGameRules::write(dos, ConsoleGameRules::eGameRuleAttr_flatworld); dos->writeUTF(_toString(m_useFlatWorld)); } void LevelGenerationOptions::getChildren( std::vector* children) { GameRuleDefinition::getChildren(children); std::vector used_schematics; for (auto it = m_schematicRules.begin(); it != m_schematicRules.end(); it++) if (!(*it)->isComplete()) used_schematics.push_back(*it); for (auto it = m_structureRules.begin(); it != m_structureRules.end(); it++) children->push_back(*it); for (auto it = used_schematics.begin(); it != used_schematics.end(); it++) children->push_back(*it); for (auto it = m_biomeOverrides.begin(); it != m_biomeOverrides.end(); ++it) children->push_back(*it); for (auto it = m_features.begin(); it != m_features.end(); ++it) children->push_back(*it); } GameRuleDefinition* LevelGenerationOptions::addChild( ConsoleGameRules::EGameRuleType ruleType) { GameRuleDefinition* rule = nullptr; if (ruleType == ConsoleGameRules::eGameRuleType_ApplySchematic) { rule = new ApplySchematicRuleDefinition(this); m_schematicRules.push_back((ApplySchematicRuleDefinition*)rule); } else if (ruleType == ConsoleGameRules::eGameRuleType_GenerateStructure) { rule = new ConsoleGenerateStructure(); m_structureRules.push_back((ConsoleGenerateStructure*)rule); } else if (ruleType == ConsoleGameRules::eGameRuleType_BiomeOverride) { rule = new BiomeOverride(); m_biomeOverrides.push_back((BiomeOverride*)rule); } else if (ruleType == ConsoleGameRules::eGameRuleType_StartFeature) { rule = new StartFeature(); m_features.push_back((StartFeature*)rule); } else { #if !defined(_CONTENT_PACKAGE) wprintf( L"LevelGenerationOptions: Attempted to add invalid child rule - " L"%d\n", ruleType); #endif } return rule; } void LevelGenerationOptions::addAttribute(const std::wstring& attributeName, const std::wstring& attributeValue) { if (attributeName.compare(L"seed") == 0) { m_seed = _fromString(attributeValue); app.DebugPrintf( "LevelGenerationOptions: Adding parameter m_seed=%I64d\n", m_seed); } else if (attributeName.compare(L"spawnX") == 0) { if (m_spawnPos == nullptr) m_spawnPos = new Pos(); int value = _fromString(attributeValue); m_spawnPos->x = value; app.DebugPrintf("LevelGenerationOptions: Adding parameter spawnX=%d\n", value); } else if (attributeName.compare(L"spawnY") == 0) { if (m_spawnPos == nullptr) m_spawnPos = new Pos(); int value = _fromString(attributeValue); m_spawnPos->y = value; app.DebugPrintf("LevelGenerationOptions: Adding parameter spawnY=%d\n", value); } else if (attributeName.compare(L"spawnZ") == 0) { if (m_spawnPos == nullptr) m_spawnPos = new Pos(); int value = _fromString(attributeValue); m_spawnPos->z = value; app.DebugPrintf("LevelGenerationOptions: Adding parameter spawnZ=%d\n", value); } else if (attributeName.compare(L"flatworld") == 0) { if (attributeValue.compare(L"true") == 0) m_useFlatWorld = true; app.DebugPrintf( "LevelGenerationOptions: Adding parameter flatworld=%s\n", m_useFlatWorld ? "true" : "false"); } else if (attributeName.compare(L"saveName") == 0) { std::wstring string(getString(attributeValue)); if (!string.empty()) setDefaultSaveName(string); else setDefaultSaveName(attributeValue); app.DebugPrintf( "LevelGenerationOptions: Adding parameter saveName=%ls\n", getDefaultSaveName().c_str()); } else if (attributeName.compare(L"worldName") == 0) { std::wstring string(getString(attributeValue)); if (!string.empty()) setWorldName(string); else setWorldName(attributeValue); app.DebugPrintf( "LevelGenerationOptions: Adding parameter worldName=%ls\n", getWorldName()); } else if (attributeName.compare(L"displayName") == 0) { std::wstring string(getString(attributeValue)); if (!string.empty()) setDisplayName(string); else setDisplayName(attributeValue); app.DebugPrintf( "LevelGenerationOptions: Adding parameter displayName=%ls\n", getDisplayName()); } else if (attributeName.compare(L"texturePackId") == 0) { setRequiredTexturePackId(_fromString(attributeValue)); setRequiresTexturePack(true); app.DebugPrintf( "LevelGenerationOptions: Adding parameter texturePackId=%0x\n", getRequiredTexturePackId()); } else if (attributeName.compare(L"isTutorial") == 0) { if (attributeValue.compare(L"true") == 0) setSrc(eSrc_tutorial); app.DebugPrintf( "LevelGenerationOptions: Adding parameter isTutorial=%s\n", isTutorial() ? "true" : "false"); } else if (attributeName.compare(L"baseSaveName") == 0) { setBaseSavePath(attributeValue); app.DebugPrintf( "LevelGenerationOptions: Adding parameter baseSaveName=%ls\n", getBaseSavePath().c_str()); } else if (attributeName.compare(L"hasBeenInCreative") == 0) { bool value = _fromString(attributeValue); m_bHasBeenInCreative = value; app.DebugPrintf( "LevelGenerationOptions: Adding parameter gameMode=%d\n", m_bHasBeenInCreative); } else { GameRuleDefinition::addAttribute(attributeName, attributeValue); } } void LevelGenerationOptions::processSchematics(LevelChunk* chunk) { AABB chunkBox(chunk->x * 16, 0, chunk->z * 16, chunk->x * 16 + 16, Level::maxBuildHeight, chunk->z * 16 + 16); for (auto it = m_schematicRules.begin(); it != m_schematicRules.end(); ++it) { ApplySchematicRuleDefinition* rule = *it; rule->processSchematic(&chunkBox, chunk); } int cx = (chunk->x << 4); int cz = (chunk->z << 4); for (auto it = m_structureRules.begin(); it != m_structureRules.end(); it++) { ConsoleGenerateStructure* structureStart = *it; if (structureStart->getBoundingBox()->intersects(cx, cz, cx + 15, cz + 15)) { BoundingBox* bb = new BoundingBox(cx, cz, cx + 15, cz + 15); structureStart->postProcess(chunk->level, nullptr, bb); delete bb; } } } void LevelGenerationOptions::processSchematicsLighting(LevelChunk* chunk) { AABB chunkBox(chunk->x * 16, 0, chunk->z * 16, chunk->x * 16 + 16, Level::maxBuildHeight, chunk->z * 16 + 16); for (auto it = m_schematicRules.begin(); it != m_schematicRules.end(); ++it) { ApplySchematicRuleDefinition* rule = *it; rule->processSchematicLighting(&chunkBox, chunk); } } bool LevelGenerationOptions::checkIntersects(int x0, int y0, int z0, int x1, int y1, int z1) { // As an optimisation, we can quickly discard things below a certain y which // makes most ore checks faster due to a) ores generally being below // ground/sea level and b) tutorial world additions generally being above // ground/sea level if (!m_bHaveMinY) { for (auto it = m_schematicRules.begin(); it != m_schematicRules.end(); ++it) { ApplySchematicRuleDefinition* rule = *it; int minY = rule->getMinY(); if (minY < m_minY) m_minY = minY; } for (auto it = m_structureRules.begin(); it != m_structureRules.end(); it++) { ConsoleGenerateStructure* structureStart = *it; int minY = structureStart->getMinY(); if (minY < m_minY) m_minY = minY; } m_bHaveMinY = true; } // 4J Stu - We DO NOT intersect if our upper bound is below the lower bound // for all schematics if (y1 < m_minY) return false; bool intersects = false; for (auto it = m_schematicRules.begin(); it != m_schematicRules.end(); ++it) { ApplySchematicRuleDefinition* rule = *it; intersects = rule->checkIntersects(x0, y0, z0, x1, y1, z1); if (intersects) break; } if (!intersects) { for (auto it = m_structureRules.begin(); it != m_structureRules.end(); it++) { ConsoleGenerateStructure* structureStart = *it; intersects = structureStart->checkIntersects(x0, y0, z0, x1, y1, z1); if (intersects) break; } } return intersects; } void LevelGenerationOptions::clearSchematics() { for (auto it = m_schematics.begin(); it != m_schematics.end(); ++it) { delete it->second; } m_schematics.clear(); } ConsoleSchematicFile* LevelGenerationOptions::loadSchematicFile( const std::wstring& filename, std::uint8_t* pbData, unsigned int dataLength) { // If we have already loaded this, just return auto it = m_schematics.find(filename); if (it != m_schematics.end()) { #if !defined(_CONTENT_PACKAGE) wprintf(L"We have already loaded schematic file %ls\n", filename.c_str()); #endif it->second->incrementRefCount(); return it->second; } ConsoleSchematicFile* schematic = nullptr; std::vector data(pbData, pbData + dataLength); ByteArrayInputStream bais(data); DataInputStream dis(&bais); schematic = new ConsoleSchematicFile(); schematic->load(&dis); m_schematics[filename] = schematic; bais.reset(); return schematic; } ConsoleSchematicFile* LevelGenerationOptions::getSchematicFile( const std::wstring& filename) { ConsoleSchematicFile* schematic = nullptr; // If we have already loaded this, just return auto it = m_schematics.find(filename); if (it != m_schematics.end()) { schematic = it->second; } return schematic; } void LevelGenerationOptions::releaseSchematicFile( const std::wstring& filename) { // 4J Stu - We don't want to delete them when done, but probably want to // keep a set of active schematics for the current world // auto it = m_schematics.find(filename); // if(it != m_schematics.end()) //{ // ConsoleSchematicFile *schematic = it->second; // schematic->decrementRefCount(); // if(schematic->shouldDelete()) // { // delete schematic; // m_schematics.erase(it); // } //} } void LevelGenerationOptions::loadStringTable(StringTable* table) { m_stringTable = table; } const wchar_t* LevelGenerationOptions::getString(const std::wstring& key) { if (m_stringTable == nullptr) { return L""; } else { return m_stringTable->getString(key); } } void LevelGenerationOptions::getBiomeOverride(int biomeId, std::uint8_t& tile, std::uint8_t& topTile) { for (auto it = m_biomeOverrides.begin(); it != m_biomeOverrides.end(); ++it) { BiomeOverride* bo = *it; if (bo->isBiome(biomeId)) { bo->getTileValues(tile, topTile); break; } } } bool LevelGenerationOptions::isFeatureChunk( int chunkX, int chunkZ, StructureFeature::EFeatureTypes feature, int* orientation) { bool isFeature = false; for (auto it = m_features.begin(); it != m_features.end(); ++it) { StartFeature* sf = *it; if (sf->isFeatureChunk(chunkX, chunkZ, feature, orientation)) { isFeature = true; break; } } return isFeature; } std::unordered_map* LevelGenerationOptions::getUnfinishedSchematicFiles() { // Clean schematic rules. std::unordered_set usedFiles = std::unordered_set(); for (auto it = m_schematicRules.begin(); it != m_schematicRules.end(); it++) if (!(*it)->isComplete()) usedFiles.insert((*it)->getSchematicName()); // Clean schematic files. std::unordered_map* out = new std::unordered_map(); for (auto it = usedFiles.begin(); it != usedFiles.end(); it++) out->insert(std::pair( *it, getSchematicFile(*it))); return out; } void LevelGenerationOptions::loadBaseSaveData() { int mountIndex = -1; if (m_parentDLCPack != nullptr) mountIndex = m_parentDLCPack->GetDLCMountIndex(); if (mountIndex > -1) { if (StorageManager.MountInstalledDLC( ProfileManager.GetPrimaryPad(), mountIndex, &LevelGenerationOptions::packMounted, this, "WPACK") != ERROR_IO_PENDING) { // corrupt DLC setLoadedData(); app.DebugPrintf("Failed to mount LGO DLC %d for pad %d\n", mountIndex, ProfileManager.GetPrimaryPad()); } else { m_bLoadingData = true; app.DebugPrintf("Attempted to mount DLC data for LGO %d\n", mountIndex); } } else { setLoadedData(); app.SetAction(ProfileManager.GetPrimaryPad(), eAppAction_ReloadTexturePack); } } int LevelGenerationOptions::packMounted(void* pParam, int iPad, uint32_t dwErr, uint32_t dwLicenceMask) { LevelGenerationOptions* lgo = (LevelGenerationOptions*)pParam; lgo->m_bLoadingData = false; if (dwErr != ERROR_SUCCESS) { // corrupt DLC app.DebugPrintf("Failed to mount LGO DLC for pad %d: %d\n", iPad, dwErr); } else { app.DebugPrintf("Mounted DLC for LGO, attempting to load data\n"); uint32_t dwFilesProcessed = 0; int gameRulesCount = lgo->m_parentDLCPack->getDLCItemsCount( DLCManager::e_DLCType_GameRulesHeader); for (int i = 0; i < gameRulesCount; ++i) { DLCGameRulesHeader* dlcFile = (DLCGameRulesHeader*)lgo->m_parentDLCPack->getFile( DLCManager::e_DLCType_GameRulesHeader, i); if (!dlcFile->getGrfPath().empty()) { File grf(app.getFilePath(lgo->m_parentDLCPack->GetPackID(), dlcFile->getGrfPath(), true, L"WPACK:")); if (grf.exists()) { std::filesystem::path grfPath = grf.getPath(); std::ifstream fileHandle(grfPath, std::ios::binary); if (fileHandle) { uint32_t dwFileSize = grf.length(); uint8_t* pbData = (uint8_t*)new uint8_t[dwFileSize]; fileHandle.read( reinterpret_cast(pbData), static_cast(dwFileSize)); if (!fileHandle) { app.FatalLoadError(); } // 4J-PB - is it possible that we can get here after a // read fail and it's not an error? dlcFile->setGrfData(pbData, dwFileSize, lgo->m_stringTable); delete[] pbData; app.m_gameRules.setLevelGenerationOptions(dlcFile->lgo); } } } } if (lgo->requiresBaseSave() && !lgo->getBaseSavePath().empty()) { File save(app.getFilePath(lgo->m_parentDLCPack->GetPackID(), lgo->getBaseSavePath(), true, L"WPACK:")); if (save.exists()) { std::filesystem::path savePath = save.getPath(); std::ifstream saveHandle(savePath, std::ios::binary); if (saveHandle) { auto dwFileSize = std::filesystem::file_size(savePath); uint8_t* pbData = (uint8_t*)new uint8_t[dwFileSize]; saveHandle.read(reinterpret_cast(pbData), static_cast(dwFileSize)); if (!saveHandle) { app.FatalLoadError(); } // 4J-PB - is it possible that we can get here after a read // fail and it's not an error? lgo->setBaseSaveData(pbData, dwFileSize); } } } uint32_t result = StorageManager.UnmountInstalledDLC("WPACK"); } lgo->setLoadedData(); return 0; } void LevelGenerationOptions::reset_start() { for (auto it = m_schematicRules.begin(); it != m_schematicRules.end(); it++) { (*it)->reset(); } } void LevelGenerationOptions::reset_finish() { // if (m_spawnPos) { delete m_spawnPos; m_spawnPos // = nullptr; } if (m_stringTable) { delete m_stringTable; // m_stringTable = nullptr; } if (isFromDLC()) { m_hasLoadedData = false; } } GrSource* LevelGenerationOptions::info() { return m_pSrc; } void LevelGenerationOptions::setSrc(eSrc src) { m_src = src; } LevelGenerationOptions::eSrc LevelGenerationOptions::getSrc() { return m_src; } bool LevelGenerationOptions::isTutorial() { return getSrc() == eSrc_tutorial; } bool LevelGenerationOptions::isFromSave() { return getSrc() == eSrc_fromSave; } bool LevelGenerationOptions::isFromDLC() { return getSrc() == eSrc_fromDLC; } bool LevelGenerationOptions::requiresTexturePack() { return info()->requiresTexturePack(); } std::uint32_t LevelGenerationOptions::getRequiredTexturePackId() { return info()->getRequiredTexturePackId(); } std::wstring LevelGenerationOptions::getDefaultSaveName() { switch (getSrc()) { case eSrc_fromSave: return getString(info()->getDefaultSaveName()); case eSrc_fromDLC: return getString(info()->getDefaultSaveName()); case eSrc_tutorial: return app.GetString(IDS_TUTORIALSAVENAME); default: break; } return L""; } const wchar_t* LevelGenerationOptions::getWorldName() { switch (getSrc()) { case eSrc_fromSave: return getString(info()->getWorldName()); case eSrc_fromDLC: return getString(info()->getWorldName()); case eSrc_tutorial: return app.GetString(IDS_PLAY_TUTORIAL); default: break; } return L""; } const wchar_t* LevelGenerationOptions::getDisplayName() { switch (getSrc()) { case eSrc_fromSave: return getString(info()->getDisplayName()); case eSrc_fromDLC: return getString(info()->getDisplayName()); case eSrc_tutorial: return L""; default: break; } return L""; } std::wstring LevelGenerationOptions::getGrfPath() { return info()->getGrfPath(); } bool LevelGenerationOptions::requiresBaseSave() { return info()->requiresBaseSave(); } std::wstring LevelGenerationOptions::getBaseSavePath() { return info()->getBaseSavePath(); } void LevelGenerationOptions::setGrSource(GrSource* grs) { m_pSrc = grs; } void LevelGenerationOptions::setRequiresTexturePack(bool x) { info()->setRequiresTexturePack(x); } void LevelGenerationOptions::setRequiredTexturePackId(std::uint32_t x) { info()->setRequiredTexturePackId(x); } void LevelGenerationOptions::setDefaultSaveName(const std::wstring& x) { info()->setDefaultSaveName(x); } void LevelGenerationOptions::setWorldName(const std::wstring& x) { info()->setWorldName(x); } void LevelGenerationOptions::setDisplayName(const std::wstring& x) { info()->setDisplayName(x); } void LevelGenerationOptions::setGrfPath(const std::wstring& x) { info()->setGrfPath(x); } void LevelGenerationOptions::setBaseSavePath(const std::wstring& x) { info()->setBaseSavePath(x); } bool LevelGenerationOptions::ready() { return info()->ready(); } void LevelGenerationOptions::setBaseSaveData(std::uint8_t* pbData, unsigned int dataSize) { m_pbBaseSaveData = pbData; m_baseSaveSize = dataSize; } std::uint8_t* LevelGenerationOptions::getBaseSaveData(unsigned int& size) { size = m_baseSaveSize; return m_pbBaseSaveData; } bool LevelGenerationOptions::hasBaseSaveData() { return m_baseSaveSize > 0 && m_pbBaseSaveData != nullptr; } void LevelGenerationOptions::deleteBaseSaveData() { delete[] m_pbBaseSaveData; m_pbBaseSaveData = nullptr; m_baseSaveSize = 0; } bool LevelGenerationOptions::hasLoadedData() { return m_hasLoadedData; } void LevelGenerationOptions::setLoadedData() { m_hasLoadedData = true; } int64_t LevelGenerationOptions::getLevelSeed() { return m_seed; } int LevelGenerationOptions::getLevelHasBeenInCreative() { return m_bHasBeenInCreative; } Pos* LevelGenerationOptions::getSpawnPos() { return m_spawnPos; } bool LevelGenerationOptions::getuseFlatWorld() { return m_useFlatWorld; } bool LevelGenerationOptions::requiresGameRules() { return m_bRequiresGameRules; } void LevelGenerationOptions::setRequiredGameRules(LevelRuleset* rules) { m_requiredGameRules = rules; m_bRequiresGameRules = true; } LevelRuleset* LevelGenerationOptions::getRequiredGameRules() { return m_requiredGameRules; }