diff --git a/Minecraft.Client/AbstractTexturePack.cpp b/Minecraft.Client/AbstractTexturePack.cpp index 3adb09b0..04435970 100644 --- a/Minecraft.Client/AbstractTexturePack.cpp +++ b/Minecraft.Client/AbstractTexturePack.cpp @@ -223,7 +223,7 @@ wstring AbstractTexturePack::getAnimationString(const wstring &textureName, cons BufferedImage *AbstractTexturePack::getImageResource(const wstring& File, bool filenameHasExtension /*= false*/, bool bTitleUpdateTexture /*=false*/, const wstring &drive /*=L""*/) { const char *pchTexture=wstringtofilename(File); - app.DebugPrintf("AbstractTexturePack::getImageResource - %s, drive is %s\n",pchTexture, wstringtofilename(drive)); + // app.DebugPrintf("AbstractTexturePack::getImageResource - %s, drive is %s\n",pchTexture, wstringtofilename(drive)); return new BufferedImage(TexturePack::getResource(L"/" + File),filenameHasExtension,bTitleUpdateTexture,drive); } diff --git a/Minecraft.Client/BufferedImage.cpp b/Minecraft.Client/BufferedImage.cpp index 2dcf2b5e..844f4bd3 100644 --- a/Minecraft.Client/BufferedImage.cpp +++ b/Minecraft.Client/BufferedImage.cpp @@ -163,7 +163,7 @@ BufferedImage::BufferedImage(const wstring& File, bool filenameHasExtension /*=f const char *pchTextureName=wstringtofilename(name); #ifndef _CONTENT_PACKAGE - app.DebugPrintf("\n--- Loading TEXTURE - %s\n\n",pchTextureName); + // app.DebugPrintf("\n--- Loading TEXTURE - %s\n\n",pchTextureName); #endif D3DXIMAGE_INFO ImageInfo; diff --git a/Minecraft.Client/Chunk.cpp b/Minecraft.Client/Chunk.cpp index d64e6243..83ff3175 100644 --- a/Minecraft.Client/Chunk.cpp +++ b/Minecraft.Client/Chunk.cpp @@ -181,6 +181,7 @@ void Chunk::makeCopyForRebuild(Chunk *source) void Chunk::rebuild() { + if (this == nullptr) return; PIXBeginNamedEvent(0,"Rebuilding chunk %d, %d, %d", x, y, z); #if defined __PS3__ && !defined DISABLE_SPU_CODE rebuild_SPU(); @@ -404,6 +405,10 @@ void Chunk::rebuild() } Tile *tile = Tile::tiles[tileId]; + if (tile == nullptr) + { + continue; + } if (currentLayer == 0 && tile->isEntityTile()) { shared_ptr et = region->getTileEntity(x, y, z); diff --git a/Minecraft.Client/Common/Tutorial/ChoiceTask.cpp b/Minecraft.Client/Common/Tutorial/ChoiceTask.cpp index 8a45ee02..8e0b4bfa 100644 --- a/Minecraft.Client/Common/Tutorial/ChoiceTask.cpp +++ b/Minecraft.Client/Common/Tutorial/ChoiceTask.cpp @@ -40,6 +40,9 @@ bool ChoiceTask::isCompleted() return false; int xboxPad = pMinecraft->player->GetXboxPad(); + int tutorialPad = tutorial->getPad(); + bool hasValidPad = (tutorialPad >= 0 && tutorialPad < XUSER_MAX_COUNT); + bool menuDisplayed = hasValidPad && ui.GetMenuDisplayed(tutorialPad); if( m_bConfirmMappingComplete || m_bCancelMappingComplete ) { @@ -48,14 +51,10 @@ bool ChoiceTask::isCompleted() return true; } - if(ui.GetMenuDisplayed(tutorial->getPad())) - { - // If a menu is displayed, then we use the handleUIInput to complete the task - } - else + if(!menuDisplayed) { // If the player is under water then allow all keypresses so they can jump out - if (pMinecraft->localplayers[tutorial->getPad()]->isUnderLiquid(Material::water)) return false; + if (hasValidPad && pMinecraft->localplayers[tutorialPad] != nullptr && pMinecraft->localplayers[tutorialPad]->isUnderLiquid(Material::water)) return false; #ifdef _WINDOWS64 if (!m_bConfirmMappingComplete && (InputManager.GetValue(xboxPad, m_iConfirmMapping) > 0 @@ -85,8 +84,9 @@ bool ChoiceTask::isCompleted() sendTelemetry(); enableConstraints(false, true); } - return m_bConfirmMappingComplete || m_bCancelMappingComplete; } + + return m_bConfirmMappingComplete || m_bCancelMappingComplete; } eTutorial_CompletionAction ChoiceTask::getCompletionAction() diff --git a/Minecraft.Client/Common/Tutorial/InfoTask.cpp b/Minecraft.Client/Common/Tutorial/InfoTask.cpp index 1d148d60..c2279e9b 100644 --- a/Minecraft.Client/Common/Tutorial/InfoTask.cpp +++ b/Minecraft.Client/Common/Tutorial/InfoTask.cpp @@ -40,11 +40,13 @@ bool InfoTask::isCompleted() bool bAllComplete = true; Minecraft *pMinecraft = Minecraft::GetInstance(); + int tutorialPad = tutorial->getPad(); + bool hasValidPad = (tutorialPad >= 0 && tutorialPad < XUSER_MAX_COUNT); // If the player is under water then allow all keypresses so they can jump out - if( pMinecraft->localplayers[tutorial->getPad()]->isUnderLiquid(Material::water) ) return false; + if( hasValidPad && pMinecraft->localplayers[tutorialPad] != nullptr && pMinecraft->localplayers[tutorialPad]->isUnderLiquid(Material::water) ) return false; - if(ui.GetMenuDisplayed(tutorial->getPad())) + if(hasValidPad && ui.GetMenuDisplayed(tutorialPad)) { // If a menu is displayed, then we use the handleUIInput to complete the task bAllComplete = true; diff --git a/Minecraft.Client/Common/Tutorial/TutorialHint.cpp b/Minecraft.Client/Common/Tutorial/TutorialHint.cpp index 9f207a3e..86a09b15 100644 --- a/Minecraft.Client/Common/Tutorial/TutorialHint.cpp +++ b/Minecraft.Client/Common/Tutorial/TutorialHint.cpp @@ -118,10 +118,12 @@ bool TutorialHint::onLookAtEntity(eINSTANCEOF type) int TutorialHint::tick() { int returnVal = -1; + int tutorialPad = m_tutorial->getPad(); + bool hasValidPad = (tutorialPad >= 0 && tutorialPad < XUSER_MAX_COUNT); switch(m_type) { case e_Hint_SwimUp: - if( Minecraft::GetInstance()->localplayers[m_tutorial->getPad()]->isUnderLiquid(Material::water) ) returnVal = m_descriptionId; + if( hasValidPad && Minecraft::GetInstance()->localplayers[tutorialPad] != nullptr && Minecraft::GetInstance()->localplayers[tutorialPad]->isUnderLiquid(Material::water) ) returnVal = m_descriptionId; break; } return returnVal; diff --git a/Minecraft.Client/Common/UI/IUIScene_TradingMenu.cpp b/Minecraft.Client/Common/UI/IUIScene_TradingMenu.cpp index 7eda5803..82e8afa9 100644 --- a/Minecraft.Client/Common/UI/IUIScene_TradingMenu.cpp +++ b/Minecraft.Client/Common/UI/IUIScene_TradingMenu.cpp @@ -76,10 +76,11 @@ bool IUIScene_TradingMenu::handleKeyDown(int iPad, int iAction, bool bRepeat) // Do we have the ingredients? shared_ptr buyAItem = activeRecipe->getBuyAItem(); shared_ptr buyBItem = activeRecipe->getBuyBItem(); + shared_ptr sellItem = activeRecipe->getSellItem(); shared_ptr player = Minecraft::GetInstance()->localplayers[getPad()]; int buyAMatches = player->inventory->countMatches(buyAItem); int buyBMatches = player->inventory->countMatches(buyBItem); - if( (buyAItem != nullptr && buyAMatches >= buyAItem->count) && (buyBItem == nullptr || buyBMatches >= buyBItem->count) ) + if( sellItem != nullptr && (buyAItem != nullptr && buyAMatches >= buyAItem->count) && (buyBItem == nullptr || buyBMatches >= buyBItem->count) ) { // 4J-JEV: Fix for PS4 #7111: [PATCH 1.12] Trading Librarian villagers for multiple �Enchanted Books� will cause the title to crash. int actualShopItem = m_activeOffers.at(selectedShopItem).second; @@ -91,7 +92,7 @@ bool IUIScene_TradingMenu::handleKeyDown(int iPad, int iAction, bool bRepeat) player->inventory->removeResources(buyBItem); // Add the item we have purchased - shared_ptr result = activeRecipe->getSellItem()->copy(); + shared_ptr result = sellItem->copy(); if(!player->inventory->add( result ) ) { player->drop(result); @@ -238,6 +239,7 @@ void IUIScene_TradingMenu::updateDisplay() if( selectedShopItem < m_activeOffers.size() ) { MerchantRecipe *activeRecipe = m_activeOffers.at(selectedShopItem).first; + shared_ptr sellItem = activeRecipe ? activeRecipe->getSellItem() : nullptr; wstring wsTemp; @@ -245,11 +247,11 @@ void IUIScene_TradingMenu::updateDisplay() wsTemp = app.GetString(IDS_VILLAGER_OFFERS_ITEM); wsTemp = replaceAll(wsTemp,L"{*VILLAGER_TYPE*}",m_merchant->getDisplayName()); size_t iPos=wsTemp.find(L"%s"); - wsTemp.replace(iPos,2,activeRecipe->getSellItem()->getHoverName()); + wsTemp.replace(iPos,2,sellItem != nullptr ? sellItem->getHoverName() : L""); setTitle(wsTemp.c_str()); - vector *offerDescription = GetItemDescription(activeRecipe->getSellItem()); + vector *offerDescription = GetItemDescription(sellItem); setOfferDescription(offerDescription); shared_ptr buyAItem = activeRecipe->getBuyAItem(); @@ -270,8 +272,8 @@ void IUIScene_TradingMenu::updateDisplay() int buyAMatches = player->inventory->countMatches(buyAItem); if(buyAMatches > 0) { - setRequest1RedBox(buyAMatches < buyAItem->count); - canMake = buyAMatches > buyAItem->count; + setRequest1RedBox(buyAItem == nullptr || buyAMatches < buyAItem->count); + canMake = buyAItem != nullptr && buyAMatches > buyAItem->count; } else { @@ -282,8 +284,8 @@ void IUIScene_TradingMenu::updateDisplay() int buyBMatches = player->inventory->countMatches(buyBItem); if(buyBMatches > 0) { - setRequest2RedBox(buyBMatches < buyBItem->count); - canMake = canMake && buyBMatches > buyBItem->count; + setRequest2RedBox(buyBItem == nullptr || buyBMatches < buyBItem->count); + canMake = canMake && buyBItem != nullptr && buyBMatches > buyBItem->count; } else { @@ -369,6 +371,11 @@ void IUIScene_TradingMenu::setTradeItem(int index, shared_ptr item vector *IUIScene_TradingMenu::GetItemDescription(shared_ptr item) { + if (item == nullptr) + { + return new vector(); + } + bool advanced = false; if (const Minecraft* pMinecraft = Minecraft::GetInstance()) { diff --git a/Minecraft.Client/Gui.cpp b/Minecraft.Client/Gui.cpp index 98c7d691..2b828f2f 100644 --- a/Minecraft.Client/Gui.cpp +++ b/Minecraft.Client/Gui.cpp @@ -34,6 +34,7 @@ #include "../Minecraft.World/BlockStateDecoderRegistry.h" #include "../Minecraft.World/BlockStateDecoder.h" #include +#include #include ResourceLocation Gui::PUMPKIN_BLUR_LOCATION = ResourceLocation(TN__BLUR__MISC_PUMPKINBLUR); @@ -1190,6 +1191,14 @@ void Gui::render(float a, bool mouseFree, int xMouse, int yMouse) if (!decoded.empty()) { std::map props; + std::set shownProps; + auto appendProp = [&](const std::wstring &key) { + auto it = props.find(key); + if (it != props.end()) { + lines.push_back(key + L": " + it->second); + shownProps.insert(key); + } + }; size_t start = 0; while (start < decoded.size()) { size_t pos = decoded.find(L'\n', start); @@ -1217,31 +1226,35 @@ void Gui::render(float a, bool mouseFree, int xMouse, int yMouse) start = pos + 1; } lines.push_back(L"State:"); - if (props.find(L"age") != props.end()) lines.push_back(L"age: " + props[L"age"]); - if (props.find(L"facing") != props.end()) lines.push_back(L"facing: " + props[L"facing"]); - if (props.find(L"north") != props.end()) lines.push_back(L"north: " + props[L"north"]); - if (props.find(L"south") != props.end()) lines.push_back(L"south: " + props[L"south"]); - if (props.find(L"east") != props.end()) lines.push_back(L"east: " + props[L"east"]); - if (props.find(L"west") != props.end()) lines.push_back(L"west: " + props[L"west"]); - if (props.find(L"type") != props.end()) lines.push_back(L"type: " + props[L"type"]); - if (props.find(L"variant") != props.end()) lines.push_back(L"variant: " + props[L"variant"]); - if (props.find(L"axis") != props.end()) lines.push_back(L"axis: " + props[L"axis"]); - if (props.find(L"facing") != props.end()) lines.push_back(L"facing: " + props[L"facing"]); - if (props.find(L"hinge") != props.end()) lines.push_back(L"hinge: " + props[L"hinge"]); - if (props.find(L"half") != props.end()) lines.push_back(L"half: " + props[L"half"]); - if (props.find(L"shape") != props.end()) lines.push_back(L"shape: " + props[L"shape"]); - if (props.find(L"up") != props.end()) lines.push_back(L"up: " + props[L"up"]); - if (props.find(L"extended") != props.end()) lines.push_back(L"extended: " + props[L"extended"]); - if (props.find(L"open") != props.end()) lines.push_back(L"open: " + props[L"open"]); - if (props.find(L"attached") != props.end()) lines.push_back(L"attached: " + props[L"attached"]); - if (props.find(L"powered") != props.end()) lines.push_back(L"powered: " + props[L"powered"]); - if (props.find(L"delay") != props.end()) lines.push_back(L"delay: " + props[L"delay"]); - if (props.find(L"enabled") != props.end()) lines.push_back(L"enabled: " + props[L"enabled"]); - if (props.find(L"eye") != props.end()) lines.push_back(L"eye: " + props[L"eye"]); - if (props.find(L"bottle_0") != props.end()) lines.push_back(L"bottle_0: " + props[L"bottle_0"]); - if (props.find(L"bottle_1") != props.end()) lines.push_back(L"bottle_1: " + props[L"bottle_1"]); - if (props.find(L"bottle_2") != props.end()) lines.push_back(L"bottle_2: " + props[L"bottle_2"]); - if (props.find(L"has_record") != props.end()) lines.push_back(L"has_record: " + props[L"has_record"]); + appendProp(L"age"); + appendProp(L"facing"); + appendProp(L"north"); + appendProp(L"south"); + appendProp(L"east"); + appendProp(L"west"); + appendProp(L"type"); + appendProp(L"variant"); + appendProp(L"axis"); + appendProp(L"hinge"); + appendProp(L"half"); + appendProp(L"shape"); + appendProp(L"up"); + appendProp(L"extended"); + appendProp(L"open"); + appendProp(L"attached"); + appendProp(L"powered"); + appendProp(L"delay"); + appendProp(L"enabled"); + appendProp(L"eye"); + appendProp(L"bottle_0"); + appendProp(L"bottle_1"); + appendProp(L"bottle_2"); + appendProp(L"has_record"); + for (const auto &entry : props) { + if (shownProps.find(entry.first) == shownProps.end()) { + lines.push_back(entry.first + L": " + entry.second); + } + } } else { diff --git a/Minecraft.Client/Windows64Media/Tutorial/Tutorial.mcs b/Minecraft.Client/Windows64Media/Tutorial/Tutorial.mcs index 99f9f5ca..6193e15a 100644 Binary files a/Minecraft.Client/Windows64Media/Tutorial/Tutorial.mcs and b/Minecraft.Client/Windows64Media/Tutorial/Tutorial.mcs differ diff --git a/Minecraft.Client/Windows64Media/Tutorial/Tutorial.pck b/Minecraft.Client/Windows64Media/Tutorial/Tutorial.pck index f05788b3..f3eb2b97 100644 Binary files a/Minecraft.Client/Windows64Media/Tutorial/Tutorial.pck and b/Minecraft.Client/Windows64Media/Tutorial/Tutorial.pck differ diff --git a/Minecraft.World/BlockStateDecoder.cpp b/Minecraft.World/BlockStateDecoder.cpp index 986a655c..ff170980 100644 --- a/Minecraft.World/BlockStateDecoder.cpp +++ b/Minecraft.World/BlockStateDecoder.cpp @@ -186,17 +186,18 @@ static std::wstring jukeboxPropsToString(int composite) return ss.str(); } -static std::wstring daylightDetectorPropsToString(int composite) +static std::wstring daylightDetectorPropsToString(bool inverted) { std::wstringstream ss; - ss << L"power: " << (composite & 0xF); + ss << L"inverted: " << (inverted ? L"true" : L"false"); return ss.str(); } static std::wstring snowPropsToString(int composite) { std::wstringstream ss; - int layers = (composite & 0x7) + 1; + int layers = composite & 0x7; + if (layers == 0) layers = 8; ss << L"layers: " << layers; return ss.str(); } @@ -605,12 +606,6 @@ static bool registerPlantDecoders() registerDecoder(Tile::sapling_Id, [](int composite)->std::wstring { return saplingPropsToString(composite); }); registerDecoder(Tile::tallgrass_Id, [](int composite)->std::wstring { return tallGrassPropsToString(composite); }); registerDecoder(Tile::tallgrass2_Id, [](int composite)->std::wstring { return tallGrass2PropsToString(composite); }); - registerDecoder(Tile::fenceGate_Id, [](int composite)->std::wstring { return fenceGatePropsToString(composite); }); - registerDecoder(Tile::spruceGate_Id, [](int composite)->std::wstring { return fenceGatePropsToString(composite); }); - registerDecoder(Tile::birchGate_Id, [](int composite)->std::wstring { return fenceGatePropsToString(composite); }); - registerDecoder(Tile::jungleGate_Id, [](int composite)->std::wstring { return fenceGatePropsToString(composite); }); - registerDecoder(Tile::darkGate_Id, [](int composite)->std::wstring { return fenceGatePropsToString(composite); }); - registerDecoder(Tile::acaciaGate_Id, [](int composite)->std::wstring { return fenceGatePropsToString(composite); }); registerDecoder(Tile::fence_Id, [](int composite)->std::wstring { return fencePropsToString(composite); }); registerDecoder(Tile::netherFence_Id, [](int composite)->std::wstring { return fencePropsToString(composite); }); registerDecoder(Tile::spruceFence_Id, [](int composite)->std::wstring { return fencePropsToString(composite); }); @@ -642,8 +637,14 @@ static bool registerPlantDecoders() registerDecoder(Tile::hugeMushroom_red_Id, [](int composite)->std::wstring { return hugeMushroomPropsToString(composite); }); registerDecoder(Tile::hopper_Id, [](int composite)->std::wstring { return hopperPropsToString(composite); }); registerDecoder(Tile::jukebox_Id, [](int composite)->std::wstring { return jukeboxPropsToString(composite); }); - registerDecoder(Tile::daylightDetector_Id, [](int composite)->std::wstring { return daylightDetectorPropsToString(composite); }); - registerDecoder(Tile::invertedDaylightDetector_Id, [](int composite)->std::wstring { return daylightDetectorPropsToString(composite); }); + registerDecoder(Tile::fenceGate_Id, [](int composite)->std::wstring { return fenceGatePropsToString(composite); }); + registerDecoder(Tile::spruceGate_Id, [](int composite)->std::wstring { return fenceGatePropsToString(composite); }); + registerDecoder(Tile::birchGate_Id, [](int composite)->std::wstring { return fenceGatePropsToString(composite); }); + registerDecoder(Tile::jungleGate_Id, [](int composite)->std::wstring { return fenceGatePropsToString(composite); }); + registerDecoder(Tile::darkGate_Id, [](int composite)->std::wstring { return fenceGatePropsToString(composite); }); + registerDecoder(Tile::acaciaGate_Id, [](int composite)->std::wstring { return fenceGatePropsToString(composite); }); + registerDecoder(Tile::daylightDetector_Id, [](int composite)->std::wstring { (void)composite; return daylightDetectorPropsToString(false); }); + registerDecoder(Tile::invertedDaylightDetector_Id, [](int composite)->std::wstring { (void)composite; return daylightDetectorPropsToString(true); }); registerDecoder(Tile::snow_Id, [](int composite)->std::wstring { return snowPropsToString(composite); }); registerDecoder(Tile::topSnow_Id, [](int composite)->std::wstring { return snowPropsToString(composite); }); registerDecoder(Tile::cauldron_Id, [](int composite)->std::wstring { return cauldronPropsToString(composite); }); diff --git a/Minecraft.World/ChestTileEntity.cpp b/Minecraft.World/ChestTileEntity.cpp index ba277e24..d702be59 100644 --- a/Minecraft.World/ChestTileEntity.cpp +++ b/Minecraft.World/ChestTileEntity.cpp @@ -142,7 +142,15 @@ void ChestTileEntity::load(CompoundTag *base) { CompoundTag *tag = inventoryList->get(i); unsigned int slot = tag->getByte(L"Slot") & 0xff; - if (slot >= 0 && slot < items->length) (*items)[slot] = ItemInstance::fromTag(tag); + if (slot < items->length) + { + shared_ptr loadedItem = ItemInstance::fromTag(tag); + if (loadedItem == nullptr) + { + app.DebugPrintf("[ChestTileEntity] Missing chest item at %d,%d,%d slot=%u id=%d count=%d damage=%d\n", x, y, z, slot, tag->getShort(L"id"), tag->getByte(L"Count"), tag->getShort(L"Damage")); + } + (*items)[slot] = loadedItem; + } } isBonusChest = base->getBoolean(L"bonus"); } diff --git a/Minecraft.World/DaylightDetectorTile.cpp b/Minecraft.World/DaylightDetectorTile.cpp index 366db5ac..8e9bc2da 100644 --- a/Minecraft.World/DaylightDetectorTile.cpp +++ b/Minecraft.World/DaylightDetectorTile.cpp @@ -14,6 +14,32 @@ DaylightDetectorTile::DaylightDetectorTile(int id, bool inverted) : BaseEntityTi updateDefaultShape(); } +void DaylightDetectorTile::createBlockStateDefinition() +{ + if (!m_blockStateDefinition) + m_blockStateDefinition = new BlockStateDefinition(this); +} + +int DaylightDetectorTile::defaultBlockState() +{ + return 0; +} + +int DaylightDetectorTile::convertBlockStateToLegacyData(BlockState *state) +{ + return state ? (state->value & 0xF) : 0; +} + +Tile::BlockState DaylightDetectorTile::getBlockState(int data) +{ + return Tile::BlockState(data & 0xF); +} + +Tile::BlockState DaylightDetectorTile::getBlockState(LevelSource *level, int x, int y, int z) +{ + return Tile::BlockState(level->getData(x, y, z) & 0xF); +} + void DaylightDetectorTile::updateDefaultShape() { setShape(0, 0, 0, 1, 6.0f / 16.0f, 1); diff --git a/Minecraft.World/DaylightDetectorTile.h b/Minecraft.World/DaylightDetectorTile.h index 13b20c6d..d940eda0 100644 --- a/Minecraft.World/DaylightDetectorTile.h +++ b/Minecraft.World/DaylightDetectorTile.h @@ -12,6 +12,12 @@ private: public: DaylightDetectorTile(int id, bool inverted); + virtual void createBlockStateDefinition() override; + virtual int defaultBlockState() override; + virtual int convertBlockStateToLegacyData(BlockState *state) override; + virtual Tile::BlockState getBlockState(int data); + virtual Tile::BlockState getBlockState(LevelSource *level, int x, int y, int z) override; + virtual void updateDefaultShape(); // 4J Added override virtual void updateShape(LevelSource *level, int x, int y, int z, int forceData = -1, shared_ptr forceEntity = shared_ptr()); virtual int getSignal(LevelSource *level, int x, int y, int z, int dir); diff --git a/Minecraft.World/FileHeader.cpp b/Minecraft.World/FileHeader.cpp index b2ebe083..176bfedf 100644 --- a/Minecraft.World/FileHeader.cpp +++ b/Minecraft.World/FileHeader.cpp @@ -298,7 +298,7 @@ void FileHeader::ReadHeader( LPVOID saveMem, ESavePlatform plat /*= SAVE_FILE_PL default: #ifndef _CONTENT_PACKAGE app.DebugPrintf("********** Invalid save version %d\n",m_saveVersion); - DEBUG_BREAK(); + // DEBUG_BREAK(); #endif break; } diff --git a/Minecraft.World/HangingEntity.cpp b/Minecraft.World/HangingEntity.cpp index be26bd47..9aa57e86 100644 --- a/Minecraft.World/HangingEntity.cpp +++ b/Minecraft.World/HangingEntity.cpp @@ -299,9 +299,11 @@ void HangingEntity::readAdditionalSaveData(CompoundTag *tag) break; } } - xTile = tag->getInt(L"TileX"); - yTile = tag->getInt(L"TileY"); - zTile = tag->getInt(L"TileZ"); + + xTile = static_cast(floor(x + 0.5f)); + yTile = static_cast(floor(y + 0.5f)); + zTile = static_cast(floor(z + 0.5f)); + setDir(dir); } diff --git a/Minecraft.World/ItemInstance.cpp b/Minecraft.World/ItemInstance.cpp index e4af60c6..f315f3f0 100644 --- a/Minecraft.World/ItemInstance.cpp +++ b/Minecraft.World/ItemInstance.cpp @@ -13,6 +13,7 @@ #include "Item.h" #include "ItemInstance.h" #include "HtmlString.h" +#include "../Minecraft.Client/Common/Consoles_App.h" const wstring ItemInstance::ATTRIBUTE_MODIFIER_FORMAT = L"#.###"; @@ -79,9 +80,22 @@ ItemInstance::ItemInstance(int id, int count, int damage) shared_ptr ItemInstance::fromTag(CompoundTag *itemTag) { + if (!itemTag) + { + app.DebugPrintf("[ItemInstance] NULL itemTag\n"); + return nullptr; + } + shared_ptr itemInstance = shared_ptr(new ItemInstance()); itemInstance->load(itemTag); - return itemInstance->getItem() != nullptr ? itemInstance : nullptr; + + Item *item = itemInstance->getItem(); + if (item == nullptr) + { + app.DebugPrintf("[ItemInstance] Missing item while loading: id=%d count=%d damage=%d\n", itemInstance->id, itemInstance->count, itemInstance->auxValue); + } + + return item != nullptr ? itemInstance : nullptr; } ItemInstance::~ItemInstance() @@ -105,6 +119,11 @@ shared_ptr ItemInstance::remove(int count) Item *ItemInstance::getItem() const { + if (id < 0 || id >= Item::items.length) + { + return nullptr; + } + return Item::items[id]; } diff --git a/Minecraft.World/Level.cpp b/Minecraft.World/Level.cpp index 8478535b..5f9355be 100644 --- a/Minecraft.World/Level.cpp +++ b/Minecraft.World/Level.cpp @@ -963,7 +963,9 @@ Material *Level::getMaterial(int x, int y, int z) { int t = getTile(x, y, z); if (t == 0) return Material::air; - return Tile::tiles[t]->material; + Tile *tile = Tile::tiles[t]; + if (tile == nullptr) return Material::air; + return tile->material; } int Level::getData(int x, int y, int z) diff --git a/Minecraft.World/LevelChunk.cpp b/Minecraft.World/LevelChunk.cpp index 5800abd7..e61f7b58 100644 --- a/Minecraft.World/LevelChunk.cpp +++ b/Minecraft.World/LevelChunk.cpp @@ -2096,6 +2096,9 @@ void LevelChunk::setBiomes(byteArray biomes) // 4J - optimisation brought forward from 1.8.2 int LevelChunk::getTopRainBlock(int x, int z) { + // check if sent data is not malformed causing a crash + if (x < 0 || x >= 16 || z < 0 || z >= 16) return 0; + int slot = x | (z << 4); int h = rainHeights[slot]; @@ -2106,7 +2109,8 @@ int LevelChunk::getTopRainBlock(int x, int z) while (y > 0 && h == -1) { int t = getTile(x, y, z); - Material *m = t == 0 ? Material::air : Tile::tiles[t]->material; + Tile *tile = (t == 0) ? nullptr : Tile::tiles[t]; + Material *m = (tile == nullptr) ? Material::air : tile->material; if (!m->blocksMotion() && !m->isLiquid()) { y--; diff --git a/Minecraft.World/MerchantRecipe.cpp b/Minecraft.World/MerchantRecipe.cpp index b4eb1b54..ad190418 100644 --- a/Minecraft.World/MerchantRecipe.cpp +++ b/Minecraft.World/MerchantRecipe.cpp @@ -64,6 +64,10 @@ shared_ptr MerchantRecipe::getSellItem() bool MerchantRecipe::isSame(MerchantRecipe *other) { + if (!other || !buyA || !sell || !other->buyA || !other->sell) + { + return false; + } if (buyA->id != other->buyA->id || sell->id != other->sell->id) { return false; @@ -74,7 +78,11 @@ bool MerchantRecipe::isSame(MerchantRecipe *other) bool MerchantRecipe::isSameSameButBetter(MerchantRecipe *other) { // same deal, but cheaper - return isSame(other) && (buyA->count < other->buyA->count || (buyB != nullptr && buyB->count < other->buyB->count)); + if (!isSame(other) || !buyA || !other || !other->buyA) + { + return false; + } + return buyA->count < other->buyA->count || (buyB != nullptr && buyB->count < other->buyB->count); } int MerchantRecipe::getUses() @@ -110,9 +118,9 @@ void MerchantRecipe::enforceDeprecated() void MerchantRecipe::load(CompoundTag *tag) { CompoundTag *buyTag = tag->getCompound(L"buy"); - buyA = ItemInstance::fromTag(buyTag); + buyA = buyTag ? ItemInstance::fromTag(buyTag) : nullptr; CompoundTag *sellTag = tag->getCompound(L"sell"); - sell = ItemInstance::fromTag(sellTag); + sell = sellTag ? ItemInstance::fromTag(sellTag) : nullptr; if (tag->contains(L"buyB")) { buyB = ItemInstance::fromTag(tag->getCompound(L"buyB")); @@ -134,8 +142,14 @@ void MerchantRecipe::load(CompoundTag *tag) CompoundTag *MerchantRecipe::createTag() { CompoundTag *tag = new CompoundTag(); - tag->putCompound(L"buy", buyA->save(new CompoundTag(L"buy"))); - tag->putCompound(L"sell", sell->save(new CompoundTag(L"sell"))); + if (buyA != nullptr) + { + tag->putCompound(L"buy", buyA->save(new CompoundTag(L"buy"))); + } + if (sell != nullptr) + { + tag->putCompound(L"sell", sell->save(new CompoundTag(L"sell"))); + } if (buyB != nullptr) { tag->putCompound(L"buyB", buyB->save(new CompoundTag(L"buyB"))); diff --git a/Minecraft.World/Region.cpp b/Minecraft.World/Region.cpp index 1e8acaf3..7b778d4d 100644 --- a/Minecraft.World/Region.cpp +++ b/Minecraft.World/Region.cpp @@ -246,7 +246,9 @@ Material *Region::getMaterial(int x, int y, int z) { int t = getTile(x, y, z); if (t == 0) return Material::air; - return Tile::tiles[t]->material; + Tile *tile = Tile::tiles[t]; + if (tile == nullptr) return Material::air; + return tile->material; } diff --git a/Minecraft.World/TopSnowTile.cpp b/Minecraft.World/TopSnowTile.cpp index 7719f838..46b7536f 100644 --- a/Minecraft.World/TopSnowTile.cpp +++ b/Minecraft.World/TopSnowTile.cpp @@ -13,11 +13,38 @@ const int TopSnowTile::HEIGHT_MASK = 7; // max 8 steps TopSnowTile::TopSnowTile(int id) : Tile(id, Material::topSnow,isSolidRender()) { + m_defaultBlockState = 1; setShape(0, 0, 0, 1, 2 / 16.0f, 1); setTicking(true); updateShape(0); } +void TopSnowTile::createBlockStateDefinition() +{ + if (!m_blockStateDefinition) + m_blockStateDefinition = new BlockStateDefinition(this); +} + +int TopSnowTile::defaultBlockState() +{ + return 1; +} + +int TopSnowTile::convertBlockStateToLegacyData(BlockState *state) +{ + return state && state->value > 0 ? ((state->value - 1) & HEIGHT_MASK) : 0; +} + +Tile::BlockState TopSnowTile::getBlockState(int data) +{ + return Tile::BlockState((data & HEIGHT_MASK) + 1); +} + +Tile::BlockState TopSnowTile::getBlockState(LevelSource *level, int x, int y, int z) +{ + return Tile::BlockState((level->getData(x, y, z) & HEIGHT_MASK) + 1); +} + void TopSnowTile::registerIcons(IconRegister *iconRegister) { icon = iconRegister->registerIcon(L"snow"); diff --git a/Minecraft.World/TopSnowTile.h b/Minecraft.World/TopSnowTile.h index 059f0f42..e3628eb1 100644 --- a/Minecraft.World/TopSnowTile.h +++ b/Minecraft.World/TopSnowTile.h @@ -14,6 +14,13 @@ public: protected: TopSnowTile(int id); +public: + virtual void createBlockStateDefinition() override; + virtual int defaultBlockState() override; + virtual int convertBlockStateToLegacyData(BlockState *state) override; + virtual Tile::BlockState getBlockState(int data); + virtual Tile::BlockState getBlockState(LevelSource *level, int x, int y, int z) override; + public: void registerIcons(IconRegister *iconRegister); AABB *getAABB(Level *level, int x, int y, int z);