From 12429e19815fdf1af4f30973741b9b7ca2b97b17 Mon Sep 17 00:00:00 2001 From: DrPerkyLegit Date: Fri, 24 Apr 2026 02:24:31 -0400 Subject: [PATCH 1/8] remove random space --- Minecraft.World/Packet.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Minecraft.World/Packet.cpp b/Minecraft.World/Packet.cpp index 1769c2acb..b3a0b754e 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); From f2b304fad5c87daf9ff8eaf071634cc6171d9226 Mon Sep 17 00:00:00 2001 From: DrPerkyLegit Date: Fri, 24 Apr 2026 02:25:40 -0400 Subject: [PATCH 2/8] sync recipes from server to client, only shapeless right now --- Minecraft.Client/ClientConnection.cpp | 13 +- Minecraft.Client/PendingConnection.cpp | 2 + Minecraft.World/CustomPayloadPacket.cpp | 3 + Minecraft.World/CustomPayloadPacket.h | 3 + Minecraft.World/Recipes.cpp | 252 +++++++++++++++++++++--- Minecraft.World/Recipes.h | 10 + Minecraft.World/ShapelessRecipy.h | 2 + 7 files changed, 256 insertions(+), 29 deletions(-) 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 e86f01de0..8d6149eb3 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/Recipes.cpp b/Minecraft.World/Recipes.cpp index 48a04e15a..94e5faac5 100644 --- a/Minecraft.World/Recipes.cpp +++ b/Minecraft.World/Recipes.cpp @@ -1,3 +1,5 @@ +#include "Recipes.h" +#include "Recipes.h" #include "stdafx.h" #include "Container.h" #include "AbstractContainerMenu.h" @@ -8,6 +10,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 +34,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 +544,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 +577,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 +741,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 +953,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 +980,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 +988,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 +1027,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 +1341,181 @@ void Recipes::buildRecipeIngredientsArray(void) Recipy::INGREDIENTS_REQUIRED *Recipes::getRecipeIngredientsArray(void) { return m_pRecipeIngredientsRequired; -} \ No newline at end of file +} + +inline void serializeItemInstance(DataOutputStream* dos, ItemInstance* result) { + dos->writeInt(result->id); + dos->writeInt(result->count); + dos->writeInt(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->writeInt(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->writeInt(lore->size()); + for (int i = 0; i < lore->size(); i++) + { + dos->writeUTF(lore->get(i)->data); + } + } +} + +inline ItemInstance* parseItemInstance(DataInputStream* dis) { + int itemId = dis->readInt(); + int itemCount = dis->readInt(); + int itemAux = dis->readInt(); + + unsigned char itemFlags = dis->readByte(); + + ItemInstance* item = new ItemInstance(itemId, itemCount, itemAux); + + if (itemFlags & 0x01) { + int enchantmentCount = dis->readInt(); + 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->readInt(); + + 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 *>(item->tag->get(L"Lore")); + for (int i = 0; i < loreCount; i++) { + wstring loreLine = dis->readUTF(); + list->add(new StringTag(loreLine)); + } + } + } + + return item; +} + +void Recipes::rebuildRecipeArray() { + deleteAllRecipes(); + loadAllRecipes(); +} + +void Recipes::rebuildRecipeArray(std::shared_ptr packet) { + deleteAllRecipes(); + + ByteArrayInputStream bais(packet->data); + DataInputStream input(&bais); + _init(); + + int recipeCount = input.readInt(); + for (int i = 0; i < recipeCount; i++) { + unsigned char group = input.readByte(); + unsigned char recipeType = input.readByte(); + + if (recipeType == 0) { // Shapeless recipe + int ingredientCount = input.readInt(); + 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(group)); + recipies->push_back(recipe); + } + } + + buildRecipeIngredientsArray(); //we manually add recipes so we need to build the ingredients array +} + +byteArray Recipes::buildSyncedRecipeArray() { + int iRecipeC = static_cast(recipies->size()); + + ByteArrayOutputStream baos; + DataOutputStream dos(&baos); + + dos.writeInt(iRecipeC); + for (int i = 0; i < iRecipeC; i++) { + Recipy* recipe = (*recipies)[i]; + dos.writeByte(recipe->getGroup()); + + if (dynamic_cast(recipe) != nullptr) { + ShapelessRecipy* shapeless = static_cast(recipe); + dos.writeByte(0); //0 for shapeless recipe + + std::vector* ingredients = shapeless->getIngredients(); + dos.writeInt(static_cast(ingredients->size())); + + for (auto& ingredient : *ingredients) { + serializeItemInstance(&dos, ingredient); + } + } else if (dynamic_cast(recipe) != nullptr) { + ShapedRecipy* shapedRecipe = static_cast(recipe); + dos.writeByte(1); //1 for shaped recipe + + continue; + } + + serializeItemInstance(&dos, const_cast(recipe->getResultItem())); + } + + return baos.toByteArray(); +} diff --git a/Minecraft.World/Recipes.h b/Minecraft.World/Recipes.h index d6e508a89..2e2799593 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,6 +100,13 @@ 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; diff --git a/Minecraft.World/ShapelessRecipy.h b/Minecraft.World/ShapelessRecipy.h index bb1dfcf7d..713a41faa 100644 --- a/Minecraft.World/ShapelessRecipy.h +++ b/Minecraft.World/ShapelessRecipy.h @@ -16,6 +16,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); From 12d11a8b1a8b65762ab855c44a94cbe0539cb13b Mon Sep 17 00:00:00 2001 From: DrPerkyLegit Date: Fri, 24 Apr 2026 02:57:00 -0400 Subject: [PATCH 3/8] lighten packet size --- Minecraft.World/Recipes.cpp | 49 +++++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 21 deletions(-) diff --git a/Minecraft.World/Recipes.cpp b/Minecraft.World/Recipes.cpp index 94e5faac5..ca34b379b 100644 --- a/Minecraft.World/Recipes.cpp +++ b/Minecraft.World/Recipes.cpp @@ -1345,8 +1345,8 @@ Recipy::INGREDIENTS_REQUIRED *Recipes::getRecipeIngredientsArray(void) inline void serializeItemInstance(DataOutputStream* dos, ItemInstance* result) { dos->writeInt(result->id); - dos->writeInt(result->count); - dos->writeInt(result->getAuxValue()); + dos->writeByte(result->count); + dos->writeShort(result->getAuxValue()); unsigned char itemFlags = 0; { @@ -1370,14 +1370,12 @@ inline void serializeItemInstance(DataOutputStream* dos, ItemInstance* result) { } } } - - dos->writeByte(itemFlags); if (itemFlags & 0x01) { ListTag* list = result->getEnchantmentTags(); if (list != nullptr) { - dos->writeInt(list->size()); + 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)); @@ -1391,8 +1389,8 @@ inline void serializeItemInstance(DataOutputStream* dos, ItemInstance* result) { if (itemFlags & 0x03) { ListTag* lore = (ListTag *) result->tag->getCompound(L"display")->getList(L"Lore"); - dos->writeInt(lore->size()); - for (int i = 0; i < lore->size(); i++) + dos->writeByte(lore->size()); + for (int i = 0; i < ((unsigned char)lore->size()); i++) { dos->writeUTF(lore->get(i)->data); } @@ -1401,15 +1399,15 @@ inline void serializeItemInstance(DataOutputStream* dos, ItemInstance* result) { inline ItemInstance* parseItemInstance(DataInputStream* dis) { int itemId = dis->readInt(); - int itemCount = dis->readInt(); - int itemAux = dis->readInt(); + int itemCount = dis->readByte(); + int itemAux = dis->readShort(); unsigned char itemFlags = dis->readByte(); ItemInstance* item = new ItemInstance(itemId, itemCount, itemAux); if (itemFlags & 0x01) { - int enchantmentCount = dis->readInt(); + 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")); @@ -1433,7 +1431,7 @@ inline ItemInstance* parseItemInstance(DataInputStream* dis) { } if (itemFlags & 0x03) { - int loreCount = dis->readInt(); + int loreCount = dis->readByte(); if (loreCount > 0) { if (item->tag == nullptr) item->setTag(new CompoundTag()); @@ -1467,18 +1465,18 @@ void Recipes::rebuildRecipeArray(std::shared_ptr packet) { int recipeCount = input.readInt(); for (int i = 0; i < recipeCount; i++) { - unsigned char group = input.readByte(); - unsigned char recipeType = input.readByte(); + unsigned char recipeHeader = input.readByte(); + int recipeType = recipeHeader & 0x0F; if (recipeType == 0) { // Shapeless recipe - int ingredientCount = input.readInt(); + 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(group)); + ShapelessRecipy* recipe = new ShapelessRecipy(result, ingredients, static_cast((recipeHeader >> 4) & 0x0F)); recipies->push_back(recipe); } } @@ -1495,22 +1493,31 @@ byteArray Recipes::buildSyncedRecipeArray() { dos.writeInt(iRecipeC); for (int i = 0; i < iRecipeC; i++) { Recipy* recipe = (*recipies)[i]; - dos.writeByte(recipe->getGroup()); + bool isShapeless = dynamic_cast(recipe) != nullptr; + dos.writeByte((recipe->getGroup() << 4) | ((isShapeless ? 0 : 1) & 0x0F)); - if (dynamic_cast(recipe) != nullptr) { + if (isShapeless) { ShapelessRecipy* shapeless = static_cast(recipe); - dos.writeByte(0); //0 for shapeless recipe std::vector* ingredients = shapeless->getIngredients(); - dos.writeInt(static_cast(ingredients->size())); + dos.writeByte(static_cast(ingredients->size())); for (auto& ingredient : *ingredients) { serializeItemInstance(&dos, ingredient); } - } else if (dynamic_cast(recipe) != nullptr) { + } else { ShapedRecipy* shapedRecipe = static_cast(recipe); - dos.writeByte(1); //1 for shaped recipe + /*int width = shapedRecipe->getWidth(); + int height = shapedRecipe->getHeight(); + dos.writeByte((width << 4) | (height & 0x0F)); + + for (int h = 0; h < height; h++) { + for (int w = 0; w < width; w++) { + + } + } + */ continue; } From 4cacbee4aa909913a71b97938142a0f633a3f231 Mon Sep 17 00:00:00 2001 From: DrPerkyLegit Date: Fri, 24 Apr 2026 03:25:05 -0400 Subject: [PATCH 4/8] shaped recipes --- Minecraft.World/Recipes.cpp | 39 ++++++++++++++++++++++++++-------- Minecraft.World/ShapedRecipy.h | 4 ++++ 2 files changed, 34 insertions(+), 9 deletions(-) diff --git a/Minecraft.World/Recipes.cpp b/Minecraft.World/Recipes.cpp index ca34b379b..848f4b73d 100644 --- a/Minecraft.World/Recipes.cpp +++ b/Minecraft.World/Recipes.cpp @@ -1440,10 +1440,10 @@ inline ItemInstance* parseItemInstance(DataInputStream* dis) { if (!displayTag->contains(L"Lore")) displayTag->put(L"Lore", new ListTag(L"Lore")); - ListTag* list = static_cast *>(item->tag->get(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(loreLine)); + list->add(new StringTag(L"", loreLine)); } } } @@ -1478,6 +1478,26 @@ void Recipes::rebuildRecipeArray(std::shared_ptr packet) { 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); } } @@ -1508,17 +1528,18 @@ byteArray Recipes::buildSyncedRecipeArray() { } else { ShapedRecipy* shapedRecipe = static_cast(recipe); - /*int width = shapedRecipe->getWidth(); + int width = shapedRecipe->getWidth(); int height = shapedRecipe->getHeight(); dos.writeByte((width << 4) | (height & 0x0F)); - for (int h = 0; h < height; h++) { - for (int w = 0; w < width; w++) { - - } + 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); } - */ - continue; } serializeItemInstance(&dos, const_cast(recipe->getResultItem())); diff --git a/Minecraft.World/ShapedRecipy.h b/Minecraft.World/ShapedRecipy.h index c111b7c77..33087ab69 100644 --- a/Minecraft.World/ShapedRecipy.h +++ b/Minecraft.World/ShapedRecipy.h @@ -17,6 +17,10 @@ public: 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); From 9af38d7a9d9131ccaeca39298d58e5ade9d88828 Mon Sep 17 00:00:00 2001 From: DrPerkyLegit Date: Fri, 24 Apr 2026 04:03:14 -0400 Subject: [PATCH 5/8] add proper recipe clean up on class delete --- Minecraft.World/Recipy.h | 2 +- Minecraft.World/ShapedRecipy.cpp | 11 +++++++++++ Minecraft.World/ShapedRecipy.h | 1 + Minecraft.World/ShapelessRecipy.cpp | 10 ++++++++++ Minecraft.World/ShapelessRecipy.h | 1 + 5 files changed, 24 insertions(+), 1 deletion(-) 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 33087ab69..b6d269503 100644 --- a/Minecraft.World/ShapedRecipy.h +++ b/Minecraft.World/ShapedRecipy.h @@ -12,6 +12,7 @@ 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(); 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 713a41faa..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(); From dd6bc32821fbf94474e0abf1e4f2475485228272 Mon Sep 17 00:00:00 2001 From: DrPerkyLegit Date: Fri, 24 Apr 2026 04:03:29 -0400 Subject: [PATCH 6/8] fix aux bug --- Minecraft.World/ItemInstance.cpp | 6 ++++++ Minecraft.World/ItemInstance.h | 1 + Minecraft.World/Recipes.cpp | 9 ++++++--- Minecraft.World/Recipes.h | 3 +++ 4 files changed, 16 insertions(+), 3 deletions(-) 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/Recipes.cpp b/Minecraft.World/Recipes.cpp index 848f4b73d..6347cbbf2 100644 --- a/Minecraft.World/Recipes.cpp +++ b/Minecraft.World/Recipes.cpp @@ -1,5 +1,7 @@ #include "Recipes.h" #include "Recipes.h" +#include "Recipes.h" +#include "Recipes.h" #include "stdafx.h" #include "Container.h" #include "AbstractContainerMenu.h" @@ -1343,7 +1345,7 @@ Recipy::INGREDIENTS_REQUIRED *Recipes::getRecipeIngredientsArray(void) return m_pRecipeIngredientsRequired; } -inline void serializeItemInstance(DataOutputStream* dos, ItemInstance* result) { +inline void Recipes::serializeItemInstance(DataOutputStream* dos, ItemInstance* result) { dos->writeInt(result->id); dos->writeByte(result->count); dos->writeShort(result->getAuxValue()); @@ -1397,14 +1399,15 @@ inline void serializeItemInstance(DataOutputStream* dos, ItemInstance* result) { } } -inline ItemInstance* parseItemInstance(DataInputStream* dis) { +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, itemAux); + 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(); diff --git a/Minecraft.World/Recipes.h b/Minecraft.World/Recipes.h index 2e2799593..a1dd465c8 100644 --- a/Minecraft.World/Recipes.h +++ b/Minecraft.World/Recipes.h @@ -111,6 +111,9 @@ 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; From 02b01e4e7506a2c25e253d57a9b9cb3bc85ca5d8 Mon Sep 17 00:00:00 2001 From: DrPerkyLegit Date: Fri, 24 Apr 2026 12:01:14 -0400 Subject: [PATCH 7/8] sync furnace recipes --- Minecraft.World/FurnaceRecipes.cpp | 62 ++++++++++++++++++------------ Minecraft.World/Recipes.cpp | 34 +++++++++++++--- 2 files changed, 67 insertions(+), 29 deletions(-) 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/Recipes.cpp b/Minecraft.World/Recipes.cpp index 6347cbbf2..afff7c7b8 100644 --- a/Minecraft.World/Recipes.cpp +++ b/Minecraft.World/Recipes.cpp @@ -1,3 +1,4 @@ +#include "FurnaceRecipes.h" #include "Recipes.h" #include "Recipes.h" #include "Recipes.h" @@ -1457,17 +1458,20 @@ inline ItemInstance* Recipes::parseItemInstance(DataInputStream* dis) { 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 recipeCount = input.readInt(); - for (int i = 0; i < recipeCount; i++) { + int iCraftingRecipeC = input.readInt(); + for (int i = 0; i < iCraftingRecipeC; i++) { unsigned char recipeHeader = input.readByte(); int recipeType = recipeHeader & 0x0F; @@ -1504,17 +1508,26 @@ void Recipes::rebuildRecipeArray(std::shared_ptr packet) { } } + 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() { - int iRecipeC = static_cast(recipies->size()); ByteArrayOutputStream baos; DataOutputStream dos(&baos); - dos.writeInt(iRecipeC); - for (int i = 0; i < iRecipeC; i++) { + 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)); @@ -1548,5 +1561,16 @@ byteArray Recipes::buildSyncedRecipeArray() { 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(); } From 5f76c365141129f9920aa405a2ce184814af55ef Mon Sep 17 00:00:00 2001 From: DrPerkyLegit Date: Fri, 24 Apr 2026 12:25:54 -0400 Subject: [PATCH 8/8] oops, forgot headers --- Minecraft.World/FurnaceRecipes.h | 3 +++ 1 file changed, 3 insertions(+) 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);