diff --git a/Minecraft.Client/ClientConnection.cpp b/Minecraft.Client/ClientConnection.cpp index 41108ace0..289c50b91 100644 --- a/Minecraft.Client/ClientConnection.cpp +++ b/Minecraft.Client/ClientConnection.cpp @@ -60,6 +60,7 @@ #include "Common/Network/PlatformNetworkManagerStub.h" #endif +#include "../Minecraft.World/Recipes.h" #ifdef _DURANGO #include "../Minecraft.World/DurangoStats.h" @@ -244,6 +245,10 @@ void ClientConnection::handleLogin(shared_ptr packet) iUserID=m_userIndex; TelemetryManager->SetMultiplayerInstanceId(packet->m_multiplayerInstanceId); + + if (Recipes::getInstance()->m_bPendingRecipeRebuild) { + Recipes::getInstance()->rebuildRecipeArray(); + } } else { @@ -2013,6 +2018,7 @@ void ClientConnection::handleEntityActionAtPosition(shared_ptr packet) { // printf("Client: handlePreLogin\n"); + Recipes::getInstance()->m_bPendingRecipeRebuild = true; #if 1 // 4J - Check that we can play with all the players already in the game who have Friends-Only UGC set BOOL canPlay = TRUE; @@ -3742,7 +3748,12 @@ void ClientConnection::handleSoundEvent(shared_ptr packet) void ClientConnection::handleCustomPayload(shared_ptr customPayloadPacket) { - if (CustomPayloadPacket::TRADER_LIST_PACKET.compare(customPayloadPacket->identifier) == 0) + if (CustomPayloadPacket::UPDATE_CRAFTING_RECIPES_PACKET.compare(customPayloadPacket->identifier) == 0) + { + Recipes::getInstance()->rebuildRecipeArray(customPayloadPacket); + Recipes::getInstance()->m_bPendingRecipeRebuild = false; + } + else if (CustomPayloadPacket::TRADER_LIST_PACKET.compare(customPayloadPacket->identifier) == 0) { ByteArrayInputStream bais(customPayloadPacket->data); DataInputStream input(&bais); diff --git a/Minecraft.Client/PendingConnection.cpp b/Minecraft.Client/PendingConnection.cpp index 88608cb17..47e88f4ac 100644 --- a/Minecraft.Client/PendingConnection.cpp +++ b/Minecraft.Client/PendingConnection.cpp @@ -14,6 +14,7 @@ #include "../Minecraft.World/net.minecraft.world.item.h" #include "../Minecraft.World/SharedConstants.h" #include "Settings.h" +#include "../Minecraft.World/Recipes.h" #if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD) #include "../Minecraft.Server/ServerLogManager.h" #include "../Minecraft.Server/Access/Access.h" @@ -336,6 +337,7 @@ void PendingConnection::handleAcceptedLogin(shared_ptr packet) #if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD) ServerRuntime::ServerLogManager::OnAcceptedPlayerLogin(GetPendingConnectionSmallId(connection), name); #endif + connection->send(std::make_shared(CustomPayloadPacket::UPDATE_CRAFTING_RECIPES_PACKET, Recipes::getInstance()->buildSyncedRecipeArray())); server->getPlayers()->placeNewPlayer(connection, playerEntity, packet); connection = nullptr; // We've moved responsibility for this over to the new PlayerConnection, nullptr so we don't delete our reference to it here in our dtor } diff --git a/Minecraft.World/CustomPayloadPacket.cpp b/Minecraft.World/CustomPayloadPacket.cpp index 1bb9b7432..61f205c48 100644 --- a/Minecraft.World/CustomPayloadPacket.cpp +++ b/Minecraft.World/CustomPayloadPacket.cpp @@ -14,6 +14,9 @@ const wstring CustomPayloadPacket::SET_ADVENTURE_COMMAND_PACKET = L"MC|AdvCdm"; const wstring CustomPayloadPacket::SET_BEACON_PACKET = L"MC|Beacon"; const wstring CustomPayloadPacket::SET_ITEM_NAME_PACKET = L"MC|ItemName"; +// MinecraftConsoles-defined custom packets +const wstring CustomPayloadPacket::UPDATE_CRAFTING_RECIPES_PACKET = L"MC|Recipes"; + CustomPayloadPacket::CustomPayloadPacket() { } diff --git a/Minecraft.World/CustomPayloadPacket.h b/Minecraft.World/CustomPayloadPacket.h index 82a3f6e26..b60c3af74 100644 --- a/Minecraft.World/CustomPayloadPacket.h +++ b/Minecraft.World/CustomPayloadPacket.h @@ -17,6 +17,9 @@ public: static const wstring SET_BEACON_PACKET; static const wstring SET_ITEM_NAME_PACKET; + // MinecraftConsoles-defined custom packets + static const wstring UPDATE_CRAFTING_RECIPES_PACKET; + wstring identifier; int length; byteArray data; diff --git a/Minecraft.World/FurnaceRecipes.cpp b/Minecraft.World/FurnaceRecipes.cpp index f0a4c770e..0c08cb448 100644 --- a/Minecraft.World/FurnaceRecipes.cpp +++ b/Minecraft.World/FurnaceRecipes.cpp @@ -17,30 +17,7 @@ FurnaceRecipes *FurnaceRecipes::getInstance() FurnaceRecipes::FurnaceRecipes() { - addFurnaceRecipy(Tile::ironOre_Id, new ItemInstance(Item::ironIngot), .7f); - addFurnaceRecipy(Tile::goldOre_Id, new ItemInstance(Item::goldIngot), 1); - addFurnaceRecipy(Tile::diamondOre_Id, new ItemInstance(Item::diamond), 1); - addFurnaceRecipy(Tile::sand_Id, new ItemInstance(Tile::glass), .1f); - addFurnaceRecipy(Item::porkChop_raw_Id, new ItemInstance(Item::porkChop_cooked), .35f); - addFurnaceRecipy(Item::beef_raw_Id, new ItemInstance(Item::beef_cooked), .35f); - addFurnaceRecipy(Item::chicken_raw_Id, new ItemInstance(Item::chicken_cooked), .35f); - addFurnaceRecipy(Item::fish_raw_Id, new ItemInstance(Item::fish_cooked), .35f); - addFurnaceRecipy(Tile::cobblestone_Id, new ItemInstance(Tile::stone), .1f); - addFurnaceRecipy(Item::clay_Id, new ItemInstance(Item::brick), .3f); - addFurnaceRecipy(Tile::clay_Id, new ItemInstance(Tile::clayHardened), .35f); - addFurnaceRecipy(Tile::cactus_Id, new ItemInstance(Item::dye_powder, 1, DyePowderItem::GREEN), .2f); - addFurnaceRecipy(Tile::treeTrunk_Id, new ItemInstance(Item::coal, 1, CoalItem::CHAR_COAL), .15f); - addFurnaceRecipy(Tile::emeraldOre_Id, new ItemInstance(Item::emerald), 1); - addFurnaceRecipy(Item::potato_Id, new ItemInstance(Item::potatoBaked), .35f); - addFurnaceRecipy(Tile::netherRack_Id, new ItemInstance(Item::netherbrick), .1f); - - // special silk touch related recipes: - addFurnaceRecipy(Tile::coalOre_Id, new ItemInstance(Item::coal), .1f); - addFurnaceRecipy(Tile::redStoneOre_Id, new ItemInstance(Item::redStone), .7f); - addFurnaceRecipy(Tile::lapisOre_Id, new ItemInstance(Item::dye_powder, 1, DyePowderItem::BLUE), .2f); - addFurnaceRecipy(Tile::netherQuartz_Id, new ItemInstance(Item::netherQuartz), .2f); - - + this->rebuildRecipeArray(); } void FurnaceRecipes::addFurnaceRecipy(int itemId, ItemInstance *result, float value) @@ -79,4 +56,41 @@ float FurnaceRecipes::getRecipeValue(int itemId) return it->second; } return 0.0f; +} + +void FurnaceRecipes::rebuildRecipeArray() { + this->clearAllRecipies(); + + addFurnaceRecipy(Tile::ironOre_Id, new ItemInstance(Item::ironIngot), .7f); + addFurnaceRecipy(Tile::goldOre_Id, new ItemInstance(Item::goldIngot), 1); + addFurnaceRecipy(Tile::diamondOre_Id, new ItemInstance(Item::diamond), 1); + addFurnaceRecipy(Tile::sand_Id, new ItemInstance(Tile::glass), .1f); + addFurnaceRecipy(Item::porkChop_raw_Id, new ItemInstance(Item::porkChop_cooked), .35f); + addFurnaceRecipy(Item::beef_raw_Id, new ItemInstance(Item::beef_cooked), .35f); + addFurnaceRecipy(Item::chicken_raw_Id, new ItemInstance(Item::chicken_cooked), .35f); + addFurnaceRecipy(Item::fish_raw_Id, new ItemInstance(Item::fish_cooked), .35f); + addFurnaceRecipy(Tile::cobblestone_Id, new ItemInstance(Tile::stone), .1f); + addFurnaceRecipy(Item::clay_Id, new ItemInstance(Item::brick), .3f); + addFurnaceRecipy(Tile::clay_Id, new ItemInstance(Tile::clayHardened), .35f); + addFurnaceRecipy(Tile::cactus_Id, new ItemInstance(Item::dye_powder, 1, DyePowderItem::GREEN), .2f); + addFurnaceRecipy(Tile::treeTrunk_Id, new ItemInstance(Item::coal, 1, CoalItem::CHAR_COAL), .15f); + addFurnaceRecipy(Tile::emeraldOre_Id, new ItemInstance(Item::emerald), 1); + addFurnaceRecipy(Item::potato_Id, new ItemInstance(Item::potatoBaked), .35f); + addFurnaceRecipy(Tile::netherRack_Id, new ItemInstance(Item::netherbrick), .1f); + + // special silk touch related recipes: + addFurnaceRecipy(Tile::coalOre_Id, new ItemInstance(Item::coal), .1f); + addFurnaceRecipy(Tile::redStoneOre_Id, new ItemInstance(Item::redStone), .7f); + addFurnaceRecipy(Tile::lapisOre_Id, new ItemInstance(Item::dye_powder, 1, DyePowderItem::BLUE), .2f); + addFurnaceRecipy(Tile::netherQuartz_Id, new ItemInstance(Item::netherQuartz), .2f); +} + +void FurnaceRecipes::clearAllRecipies() +{ + for (auto &pair : recipies) { + delete pair.second; + } + + recipies.clear(); + recipeValue.clear(); } \ No newline at end of file diff --git a/Minecraft.World/FurnaceRecipes.h b/Minecraft.World/FurnaceRecipes.h index 168831c08..8a6c0a3cf 100644 --- a/Minecraft.World/FurnaceRecipes.h +++ b/Minecraft.World/FurnaceRecipes.h @@ -20,6 +20,9 @@ public: public: FurnaceRecipes(); + void rebuildRecipeArray(); + void clearAllRecipies(); + public: void addFurnaceRecipy(int itemId, ItemInstance *result, float value); bool isFurnaceItem(int itemId); diff --git a/Minecraft.World/ItemInstance.cpp b/Minecraft.World/ItemInstance.cpp index e4af60c67..f47c78a49 100644 --- a/Minecraft.World/ItemInstance.cpp +++ b/Minecraft.World/ItemInstance.cpp @@ -1,3 +1,4 @@ +#include "ItemInstance.h" #include "stdafx.h" #include "com.mojang.nbt.h" #include "net.minecraft.locale.h" @@ -220,6 +221,11 @@ void ItemInstance::setAuxValue(int value) } } +void ItemInstance::setRawAuxValue(int value) +{ + auxValue = value; +} + int ItemInstance::getMaxDamage() { return Item::items[id]->getMaxDamage(); diff --git a/Minecraft.World/ItemInstance.h b/Minecraft.World/ItemInstance.h index cff33a972..e2b0e0599 100644 --- a/Minecraft.World/ItemInstance.h +++ b/Minecraft.World/ItemInstance.h @@ -90,6 +90,7 @@ public: int getDamageValue(); int getAuxValue() const; void setAuxValue(int value); + void setRawAuxValue(int value); int getMaxDamage(); bool hurt(int dmg, Random *random); void hurtAndBreak(int dmg, shared_ptr owner); diff --git a/Minecraft.World/Packet.cpp b/Minecraft.World/Packet.cpp index dc6343448..472fe036b 100644 --- a/Minecraft.World/Packet.cpp +++ b/Minecraft.World/Packet.cpp @@ -126,7 +126,7 @@ void Packet::staticCtor() map(162, true, false, false, false, typeid(MoveEntityPacketSmall), MoveEntityPacketSmall::create); map(163, true, false, false, true, typeid(MoveEntityPacketSmall::Pos), MoveEntityPacketSmall::Pos::create); - map(164, true, false, false, true, typeid( MoveEntityPacketSmall::Rot), MoveEntityPacketSmall::Rot::create); + map(164, true, false, false, true, typeid(MoveEntityPacketSmall::Rot), MoveEntityPacketSmall::Rot::create); map(165, true, false, false, true, typeid(MoveEntityPacketSmall::PosRot), MoveEntityPacketSmall::PosRot::create); map(166, true, true, false, false, typeid(XZPacket), XZPacket::create); map(167, false, true, false, false, typeid(GameCommandPacket), GameCommandPacket::create); diff --git a/Minecraft.World/Recipes.cpp b/Minecraft.World/Recipes.cpp index 48a04e15a..afff7c7b8 100644 --- a/Minecraft.World/Recipes.cpp +++ b/Minecraft.World/Recipes.cpp @@ -1,3 +1,8 @@ +#include "FurnaceRecipes.h" +#include "Recipes.h" +#include "Recipes.h" +#include "Recipes.h" +#include "Recipes.h" #include "stdafx.h" #include "Container.h" #include "AbstractContainerMenu.h" @@ -8,6 +13,8 @@ #include "net.minecraft.world.level.tile.h" #include "net.minecraft.world.item.crafting.h" +#include "../Minecraft.World/CustomPayloadPacket.h" + Recipes *Recipes::instance = nullptr; ArmorRecipes *Recipes::pArmorRecipes=nullptr; ClothDyeRecipes *Recipes::pClothDyeRecipes=nullptr; @@ -30,25 +37,23 @@ void Recipes::_init() recipies = new RecipyList(); } -Recipes::Recipes() -{ - int iCount=0; +void Recipes::loadAllRecipes() { + int iCount = 0; _init(); - - pArmorRecipes = new ArmorRecipes; - pClothDyeRecipes = new ClothDyeRecipes; - pFoodRecipies = new FoodRecipies; - pOreRecipies = new OreRecipies; - pStructureRecipies = new StructureRecipies; - pToolRecipies = new ToolRecipies; - pWeaponRecipies = new WeaponRecipies; + if (pArmorRecipes == nullptr) pArmorRecipes = new ArmorRecipes; + if (pClothDyeRecipes == nullptr) pClothDyeRecipes = new ClothDyeRecipes; + if (pFoodRecipies == nullptr) pFoodRecipies = new FoodRecipies; + if (pOreRecipies == nullptr) pOreRecipies = new OreRecipies; + if (pStructureRecipies == nullptr) pStructureRecipies = new StructureRecipies; + if (pToolRecipies == nullptr) pToolRecipies = new ToolRecipies; + if (pWeaponRecipies == nullptr) pWeaponRecipies = new WeaponRecipies; // 4J Stu - These just don't work with our crafting menu //recipies->push_back(new ArmorDyeRecipe()); //recipies->add(new MapCloningRecipe()); //recipies->add(new MapExtendingRecipe()); //recipies->add(new FireworksRecipe()); - pFireworksRecipes = new FireworksRecipe(); + if (pFireworksRecipes == nullptr) pFireworksRecipes = new FireworksRecipe(); addShapedRecipy(new ItemInstance(Tile::wood, 4, 0), // @@ -542,7 +547,7 @@ Recipes::Recipes() L'#', Tile::wood, L'V'); - addShapedRecipy(new ItemInstance((Item *)Item::fishingRod, 1), // + addShapedRecipy(new ItemInstance((Item*)Item::fishingRod, 1), // L"ssscicig", L" #", // L" #X", // @@ -575,7 +580,7 @@ Recipes::Recipes() L'F'); // Moved bow and arrow in from weapons to avoid stacking on the group name display - addShapedRecipy(new ItemInstance((Item *)Item::bow, 1), // + addShapedRecipy(new ItemInstance((Item*)Item::bow, 1), // L"ssscicig", L" #X", // L"# X", // @@ -739,12 +744,12 @@ Recipes::Recipes() addShapelessRecipy(new ItemInstance(Item::fireball, 3), // L"iiig", - Item::gunpowder, Item::blazePowder,Item::coal, + Item::gunpowder, Item::blazePowder, Item::coal, L'T'); addShapelessRecipy(new ItemInstance(Item::fireball, 3), // L"iizg", - Item::gunpowder, Item::blazePowder,new ItemInstance(Item::coal, 1, CoalItem::CHAR_COAL), + Item::gunpowder, Item::blazePowder, new ItemInstance(Item::coal, 1, CoalItem::CHAR_COAL), L'T'); addShapedRecipy(new ItemInstance(Item::lead, 2), // @@ -951,21 +956,21 @@ Recipes::Recipes() L'D'); // 4J - TODO - put these new 1.7.3 items in required place within recipes - addShapedRecipy(new ItemInstance(static_cast(Tile::pistonBase), 1), // + addShapedRecipy(new ItemInstance(static_cast(Tile::pistonBase), 1), // L"sssctcicictg", - L"TTT", // - L"#X#", // - L"#R#", // + L"TTT", // + L"#X#", // + L"#R#", // - L'#', Tile::cobblestone, L'X', Item::ironIngot, L'R', Item::redStone, L'T', Tile::wood, + L'#', Tile::cobblestone, L'X', Item::ironIngot, L'R', Item::redStone, L'T', Tile::wood, L'M'); - addShapedRecipy(new ItemInstance(static_cast(Tile::pistonStickyBase), 1), // + addShapedRecipy(new ItemInstance(static_cast(Tile::pistonStickyBase), 1), // L"sscictg", - L"S", // - L"P", // + L"S", // + L"P", // - L'S', Item::slimeBall, L'P', Tile::pistonBase, + L'S', Item::slimeBall, L'P', Tile::pistonBase, L'M'); @@ -978,7 +983,7 @@ Recipes::Recipes() L'P', Item::paper, L'G', Item::gunpowder, L'D'); - addShapedRecipy(new ItemInstance(Item::fireworksCharge,1), // + addShapedRecipy(new ItemInstance(Item::fireworksCharge, 1), // L"sscicig", L" D ", // L" G ", // @@ -986,7 +991,7 @@ Recipes::Recipes() L'D', Item::dye_powder, L'G', Item::gunpowder, L'D'); - addShapedRecipy(new ItemInstance(Item::fireworksCharge,1), // + addShapedRecipy(new ItemInstance(Item::fireworksCharge, 1), // L"sscicig", L" D ", // L" C ", // @@ -1025,6 +1030,23 @@ Recipes::Recipes() buildRecipeIngredientsArray(); } +void Recipes::deleteAllRecipes() { + for (size_t i = 0; i < recipies->size(); i++) { + delete recipies->at(i); + } + + delete recipies; + recipies = nullptr; + + delete m_pRecipeIngredientsRequired; + m_pRecipeIngredientsRequired = nullptr; +} + +Recipes::Recipes() +{ + loadAllRecipes(); +} + // 4J-PB - this function has been substantially changed due to the differences with a va_list of classes in C++ and Java ShapedRecipy *Recipes::addShapedRecipy(ItemInstance *result, ...) { @@ -1322,4 +1344,233 @@ void Recipes::buildRecipeIngredientsArray(void) Recipy::INGREDIENTS_REQUIRED *Recipes::getRecipeIngredientsArray(void) { return m_pRecipeIngredientsRequired; -} \ No newline at end of file +} + +inline void Recipes::serializeItemInstance(DataOutputStream* dos, ItemInstance* result) { + dos->writeInt(result->id); + dos->writeByte(result->count); + dos->writeShort(result->getAuxValue()); + + unsigned char itemFlags = 0; + { + if (result->isEnchanted()) { + itemFlags |= 0x01; + } + + if (result->tag != nullptr) { + if (result->tag->contains(L"display")) { + CompoundTag* displayTag = result->tag->getCompound(L"display"); + if (displayTag->contains(L"Name")) + { + //title = displayTag->getString(L"Name"); + itemFlags |= 0x02; + } + if (displayTag->contains(L"Lore")) { + if (displayTag->getList(L"Lore")->size() > 0) { + itemFlags |= 0x03; + } + } + } + } + } + dos->writeByte(itemFlags); + + if (itemFlags & 0x01) { + ListTag* list = result->getEnchantmentTags(); + if (list != nullptr) { + dos->writeByte(list->size()); + for (int i = 0; i < list->size(); i++) { + dos->writeShort(list->get(i)->getShort((wchar_t*)ItemInstance::TAG_ENCH_ID)); + dos->writeShort(list->get(i)->getShort((wchar_t*)ItemInstance::TAG_ENCH_LEVEL)); + } + } + } + + if (itemFlags & 0x02) { + dos->writeUTF(result->tag->getCompound(L"display")->getString(L"Name")); + } + + if (itemFlags & 0x03) { + ListTag* lore = (ListTag *) result->tag->getCompound(L"display")->getList(L"Lore"); + dos->writeByte(lore->size()); + for (int i = 0; i < ((unsigned char)lore->size()); i++) + { + dos->writeUTF(lore->get(i)->data); + } + } +} + +inline ItemInstance* Recipes::parseItemInstance(DataInputStream* dis) { + int itemId = dis->readInt(); + int itemCount = dis->readByte(); + int itemAux = dis->readShort(); + + unsigned char itemFlags = dis->readByte(); + + ItemInstance* item = new ItemInstance(itemId, itemCount, 0); + item->setRawAuxValue(itemAux); //ctor limits aux to < 0 cause we are creating item by id and not tile or item instance + + if (itemFlags & 0x01) { + int enchantmentCount = dis->readByte(); + if (enchantmentCount > 0) { + if (item->tag == nullptr) item->setTag(new CompoundTag()); + if (!item->tag->contains(L"ench")) item->tag->put(L"ench", new ListTag(L"ench")); + + ListTag* list = static_cast *>(item->tag->get(L"ench")); + + for (int i = 0; i < enchantmentCount; i++) { + short enchantmentId = dis->readShort(); + short enchantmentLevel = dis->readShort(); + + CompoundTag* ench = new CompoundTag(); + ench->putShort((wchar_t*)ItemInstance::TAG_ENCH_ID, static_cast(enchantmentId)); + ench->putShort((wchar_t*)ItemInstance::TAG_ENCH_LEVEL, static_cast(enchantmentLevel)); + list->add(ench); + } + } + } + + if (itemFlags & 0x02) { + item->setHoverName(dis->readUTF()); + } + + if (itemFlags & 0x03) { + int loreCount = dis->readByte(); + + if (loreCount > 0) { + if (item->tag == nullptr) item->setTag(new CompoundTag()); + if (!item->tag->contains(L"display")) item->tag->putCompound(L"display", new CompoundTag()); + CompoundTag* displayTag = item->tag->getCompound(L"display"); + if (!displayTag->contains(L"Lore")) displayTag->put(L"Lore", new ListTag(L"Lore")); + + + ListTag* list = static_cast *>(displayTag->get(L"Lore")); + for (int i = 0; i < loreCount; i++) { + wstring loreLine = dis->readUTF(); + list->add(new StringTag(L"", loreLine)); + } + } + } + + return item; +} + +void Recipes::rebuildRecipeArray() { + deleteAllRecipes(); + loadAllRecipes(); + + FurnaceRecipes::getInstance()->rebuildRecipeArray(); +} + +void Recipes::rebuildRecipeArray(std::shared_ptr packet) { + deleteAllRecipes(); + FurnaceRecipes::getInstance()->clearAllRecipies(); + + ByteArrayInputStream bais(packet->data); + DataInputStream input(&bais); + _init(); + + int iCraftingRecipeC = input.readInt(); + for (int i = 0; i < iCraftingRecipeC; i++) { + unsigned char recipeHeader = input.readByte(); + int recipeType = recipeHeader & 0x0F; + + if (recipeType == 0) { // Shapeless recipe + int ingredientCount = input.readByte(); + vector* ingredients = new vector(); + for (int j = 0; j < ingredientCount; j++) { + ingredients->emplace_back(parseItemInstance(&input)); + } + + ItemInstance* result = parseItemInstance(&input); + ShapelessRecipy* recipe = new ShapelessRecipy(result, ingredients, static_cast((recipeHeader >> 4) & 0x0F)); + recipies->push_back(recipe); + } else if (recipeType == 1) { // Shaped recipe + unsigned char shapedRecipeHeader = input.readByte(); + + int width = (shapedRecipeHeader >> 4) & 0x0F; + int height = shapedRecipeHeader & 0x0F; + + ItemInstance** ids = new ItemInstance * [width * height]; + for (int j = 0; j < width * height; j++) { + byte isIngredientValid = input.readByte(); + + if (isIngredientValid) { + ids[j] = parseItemInstance(&input); + } else { + ids[j] = nullptr; + } + } + + ItemInstance* result = parseItemInstance(&input); + ShapedRecipy* recipe = new ShapedRecipy(width, height, ids, result, static_cast((recipeHeader >> 4) & 0x0F)); + recipies->push_back(recipe); + } + } + + int iFurnaceRecipesC = input.readInt(); + for (int i = 0; i < iFurnaceRecipesC; i++) { + int inputId = input.readInt(); + float recipeValue = input.readFloat(); + + ItemInstance* result = parseItemInstance(&input); + FurnaceRecipes::getInstance()->addFurnaceRecipy(inputId, result, recipeValue); + } + + buildRecipeIngredientsArray(); //we manually add recipes so we need to build the ingredients array +} + +byteArray Recipes::buildSyncedRecipeArray() { + + ByteArrayOutputStream baos; + DataOutputStream dos(&baos); + + int iCraftingRecipeC = static_cast(recipies->size()); + dos.writeInt(iCraftingRecipeC); + for (int i = 0; i < iCraftingRecipeC; i++) { + Recipy* recipe = (*recipies)[i]; + bool isShapeless = dynamic_cast(recipe) != nullptr; + dos.writeByte((recipe->getGroup() << 4) | ((isShapeless ? 0 : 1) & 0x0F)); + + if (isShapeless) { + ShapelessRecipy* shapeless = static_cast(recipe); + + std::vector* ingredients = shapeless->getIngredients(); + dos.writeByte(static_cast(ingredients->size())); + + for (auto& ingredient : *ingredients) { + serializeItemInstance(&dos, ingredient); + } + } else { + ShapedRecipy* shapedRecipe = static_cast(recipe); + + int width = shapedRecipe->getWidth(); + int height = shapedRecipe->getHeight(); + dos.writeByte((width << 4) | (height & 0x0F)); + + ItemInstance** ingredients = shapedRecipe->getRecipeItems(); + for (int j = 0; j < width * height; j++) { + ItemInstance* ingredient = ingredients[j]; + dos.writeByte((ingredient == nullptr ? 0 : 1)); + if (ingredient == nullptr) continue; + + serializeItemInstance(&dos, ingredient); + } + } + + serializeItemInstance(&dos, const_cast(recipe->getResultItem())); + } + + unordered_map* furnaceRecipes = FurnaceRecipes::getInstance()->getRecipies(); + int iFurnaceRecipesC = static_cast(furnaceRecipes->size()); + dos.writeInt(iFurnaceRecipesC); + + for (auto& pair : *furnaceRecipes) { + dos.writeInt(pair.first); + dos.writeFloat(FurnaceRecipes::getInstance()->getRecipeValue(pair.first)); + + serializeItemInstance(&dos, pair.second); + } + + return baos.toByteArray(); +} diff --git a/Minecraft.World/Recipes.h b/Minecraft.World/Recipes.h index d6e508a89..a1dd465c8 100644 --- a/Minecraft.World/Recipes.h +++ b/Minecraft.World/Recipes.h @@ -21,6 +21,7 @@ import net.minecraft.world.level.tile.Tile; using namespace std; class CraftingContainer; +class CustomPayloadPacket; class FireTile; class ArmorRecipes; @@ -84,6 +85,8 @@ public: private: void _init(); // 4J add + void loadAllRecipes(); + void deleteAllRecipes(); Recipes(); public: @@ -97,10 +100,20 @@ public: shared_ptr getItemForRecipe(Recipy *r); Recipy::INGREDIENTS_REQUIRED *getRecipeIngredientsArray(); + bool m_bPendingRecipeRebuild = false; + + void rebuildRecipeArray(); + void rebuildRecipeArray(std::shared_ptr packet); + + byteArray buildSyncedRecipeArray(); + private: void buildRecipeIngredientsArray(); Recipy::INGREDIENTS_REQUIRED *m_pRecipeIngredientsRequired; + void serializeItemInstance(DataOutputStream* dos, ItemInstance* result); + ItemInstance* parseItemInstance(DataInputStream* dis); + public: static ToolRecipies *pToolRecipies; static WeaponRecipies *pWeaponRecipies; diff --git a/Minecraft.World/Recipy.h b/Minecraft.World/Recipy.h index 9cbf5cacf..e78710726 100644 --- a/Minecraft.World/Recipy.h +++ b/Minecraft.World/Recipy.h @@ -41,7 +41,7 @@ public: unsigned short usBitmaskMissingGridIngredients[XUSER_MAX_COUNT]; // each bit set means we don't have that grid ingredient } INGREDIENTS_REQUIRED; - ~Recipy() {} + ~Recipy() = default; virtual bool matches(shared_ptr craftSlots, Level *level) = 0; virtual shared_ptr assemble(shared_ptr craftSlots) = 0; virtual int size() = 0; diff --git a/Minecraft.World/ShapedRecipy.cpp b/Minecraft.World/ShapedRecipy.cpp index 7a0e85b03..907666370 100644 --- a/Minecraft.World/ShapedRecipy.cpp +++ b/Minecraft.World/ShapedRecipy.cpp @@ -23,6 +23,17 @@ ShapedRecipy::ShapedRecipy(int width, int height, ItemInstance **recipeItems, It _keepTag = false; } +ShapedRecipy::~ShapedRecipy() +{ + for (int i = 0; i < width * height; i++) { + if (recipeItems[i] == nullptr) continue; + delete recipeItems[i]; + } + + delete[] recipeItems; + delete result; +} + const int ShapedRecipy::getGroup() { return group; diff --git a/Minecraft.World/ShapedRecipy.h b/Minecraft.World/ShapedRecipy.h index c111b7c77..b6d269503 100644 --- a/Minecraft.World/ShapedRecipy.h +++ b/Minecraft.World/ShapedRecipy.h @@ -12,11 +12,16 @@ public: public: ShapedRecipy(int width, int height, ItemInstance **recipeItems, ItemInstance *result, int iGroup=Recipy::eGroupType_Decoration); + ~ShapedRecipy(); virtual const ItemInstance *getResultItem(); virtual const int getGroup(); virtual bool matches(shared_ptr craftSlots, Level *level); + int getWidth() { return width; } + int getHeight() { return height; } + ItemInstance** getRecipeItems() { return recipeItems; } + private: bool matches(shared_ptr craftSlots, int xOffs, int yOffs, bool xFlip); diff --git a/Minecraft.World/ShapelessRecipy.cpp b/Minecraft.World/ShapelessRecipy.cpp index 8fab1b7e9..e10c3c672 100644 --- a/Minecraft.World/ShapelessRecipy.cpp +++ b/Minecraft.World/ShapelessRecipy.cpp @@ -19,6 +19,16 @@ ShapelessRecipy::ShapelessRecipy(ItemInstance *result, vector *i { } +ShapelessRecipy::~ShapelessRecipy() +{ + for (auto ingredient : *ingredients) { + delete ingredient; + } + + delete ingredients; + delete result; +} + const int ShapelessRecipy::getGroup() { return group; diff --git a/Minecraft.World/ShapelessRecipy.h b/Minecraft.World/ShapelessRecipy.h index bb1dfcf7d..105afafcb 100644 --- a/Minecraft.World/ShapelessRecipy.h +++ b/Minecraft.World/ShapelessRecipy.h @@ -9,6 +9,7 @@ private: public: ShapelessRecipy(ItemInstance *result, vector *ingredients, _eGroupType egroup=Recipy::eGroupType_Decoration); + ~ShapelessRecipy(); virtual const ItemInstance *getResultItem(); virtual const int getGroup(); @@ -16,6 +17,8 @@ public: virtual shared_ptr assemble(shared_ptr craftSlots); virtual int size(); + vector* getIngredients() { return ingredients; } + // 4J-PB - to return the items required to make a recipe virtual bool reqs(int iRecipe); virtual void reqs(INGREDIENTS_REQUIRED *pIngReq);