From b67cbb85f393dabb860b86926963c04dd5b949a5 Mon Sep 17 00:00:00 2001 From: DrPerkyLegit <116128211+DrPerkyLegit@users.noreply.github.com> Date: Fri, 22 May 2026 11:39:27 -0400 Subject: [PATCH] feat: multiplayer feat/less hardcoded lists (#89) * CustomPayloadPacket Changes commented all code for old equip packets cleaned up custom payload key creation added 2 new payload packet definitions for upcoming changes * server sided recipe list * code cleanup for recipes * add dtor for recipe classes to fix memory leak * forgot this part to the dtor commit * dtor changes tested and fixed * server side creative menu list --- Minecraft.Client/ClientConnection.cpp | 23 ++ Minecraft.Client/ClientConnection.h | 2 + .../Common/UI/IUIScene_CreativeMenu.cpp | 126 +++++++---- .../Common/UI/IUIScene_CreativeMenu.h | 7 + Minecraft.Client/PlayerConnection.cpp | 4 +- Minecraft.Client/PlayerList.cpp | 6 + Minecraft.World/CustomPayloadPacket.cpp | 47 ++-- Minecraft.World/CustomPayloadPacket.h | 11 +- Minecraft.World/FireworksRecipe.h | 4 + Minecraft.World/ItemInstance.cpp | 5 + Minecraft.World/ItemInstance.h | 1 + Minecraft.World/Packet.h | 2 - Minecraft.World/Recipes.cpp | 203 ++++++++++-------- Minecraft.World/Recipes.h | 8 + Minecraft.World/Recipy.h | 30 +-- Minecraft.World/ShapedRecipy.cpp | 79 +++++++ Minecraft.World/ShapedRecipy.h | 21 +- Minecraft.World/ShapelessRecipy.cpp | 72 ++++++- Minecraft.World/ShapelessRecipy.h | 3 + 19 files changed, 484 insertions(+), 170 deletions(-) diff --git a/Minecraft.Client/ClientConnection.cpp b/Minecraft.Client/ClientConnection.cpp index d13356f9..361c2974 100644 --- a/Minecraft.Client/ClientConnection.cpp +++ b/Minecraft.Client/ClientConnection.cpp @@ -61,6 +61,8 @@ #include "Windows64/Network/WinsockNetLayer.h" #endif +#include "../Minecraft.World/Recipes.h" + #ifdef _DURANGO #include "../Minecraft.World/DurangoStats.h" @@ -137,6 +139,9 @@ ClientConnection::ClientConnection(Minecraft *minecraft, Socket *socket, int iUs maxPlayers = 20; m_isForkServer = false; + m_recivedRecipeRegistyUpdate = false; + m_recivedCreativeRegistyUpdate = false; + this->minecraft = minecraft; if( iUserIndex < 0 ) @@ -246,6 +251,14 @@ void ClientConnection::handleLogin(shared_ptr packet) { if (done) return; + if (!m_recivedRecipeRegistyUpdate) { + Recipes::getInstance()->loadFromLocal(); + } + + if (!m_recivedCreativeRegistyUpdate) { + IUIScene_CreativeMenu::loadFromLocal(); + } + PlayerUID OnlineXuid; ProfileManager.GetXUID(m_userIndex,&OnlineXuid,true); // online xuid MOJANG_DATA *pMojangData = nullptr; @@ -4025,6 +4038,16 @@ void ClientConnection::handleCustomPayload(shared_ptr custo } } } + else if (CustomPayloadPacket::UPDATE_RECIPE_REGISTRY.compare(customPayloadPacket->identifier) == 0) { + this->m_recivedRecipeRegistyUpdate = true; + + Recipes::getInstance()->loadFromPacket(customPayloadPacket->data); + } + else if (CustomPayloadPacket::UPDATE_CREATIVE_REGISTRY.compare(customPayloadPacket->identifier) == 0) { + this->m_recivedCreativeRegistyUpdate = true; + + IUIScene_CreativeMenu::loadFromPacket(customPayloadPacket->data); + } } Connection *ClientConnection::getConnection() diff --git a/Minecraft.Client/ClientConnection.h b/Minecraft.Client/ClientConnection.h index ad78cac9..4e60b7d4 100644 --- a/Minecraft.Client/ClientConnection.h +++ b/Minecraft.Client/ClientConnection.h @@ -49,6 +49,8 @@ private: std::unordered_set m_trackedEntityIds; std::unordered_set m_visibleChunks; bool m_isForkServer; // true when connected to a fork server (received MC|ForkHello) + bool m_recivedRecipeRegistyUpdate; + bool m_recivedCreativeRegistyUpdate; static int64_t chunkKey(int x, int z) { return ((int64_t)x << 32) | ((int64_t)z & 0xFFFFFFFF); } diff --git a/Minecraft.Client/Common/UI/IUIScene_CreativeMenu.cpp b/Minecraft.Client/Common/UI/IUIScene_CreativeMenu.cpp index 4ddbd620..128df572 100644 --- a/Minecraft.Client/Common/UI/IUIScene_CreativeMenu.cpp +++ b/Minecraft.Client/Common/UI/IUIScene_CreativeMenu.cpp @@ -1,3 +1,4 @@ +#include "IUIScene_CreativeMenu.h" #include "stdafx.h" #include "IUIScene_CreativeMenu.h" @@ -22,6 +23,56 @@ vector< shared_ptr > IUIScene_CreativeMenu::categoryGroups[eCreati #define ITEM_AUX(id, aux) list->push_back( shared_ptr(new ItemInstance(id, 1, aux)) ); #define DEF(index) list = &categoryGroups[index]; +void IUIScene_CreativeMenu::_wipeCreativeItems() { + for (int i = 0; i < eCreativeInventoryGroupsCount; i++) { + IUIScene_CreativeMenu::categoryGroups[i].clear(); + } +} + +void IUIScene_CreativeMenu::loadFromLocal() { + IUIScene_CreativeMenu::_wipeCreativeItems(); + IUIScene_CreativeMenu::staticCtor(); +} + +void IUIScene_CreativeMenu::loadFromPacket(byteArray packetData) { + ByteArrayInputStream bais(packetData); + DataInputStream input(&bais); + + IUIScene_CreativeMenu::_wipeCreativeItems(); + + for (int i = 0; i < eCreativeInventoryGroupsCount; i++) { + int itemCount = input.readShort(); + + for (int j = 0; j < itemCount; j++) { + int itemId = input.readShort(); + int itemAux = input.readShort(); + + shared_ptr item = std::make_shared(itemId, 1, 0); + item->setRawAuxValue(itemAux); + item->tag = Packet::readNbt(&input); + + IUIScene_CreativeMenu::categoryGroups[i].emplace_back(item); + } + } + +} + +std::shared_ptr IUIScene_CreativeMenu::createUpdatePacket() { + ByteArrayOutputStream baos; + DataOutputStream dos(&baos); + + for (int i = 0; i < eCreativeInventoryGroupsCount; i++) { + dos.writeShort(IUIScene_CreativeMenu::categoryGroups[i].size()); + for (shared_ptr& item : IUIScene_CreativeMenu::categoryGroups[i]) { + dos.writeShort(item->id); + dos.writeShort(item->getAuxValue()); + Packet::writeNbt(item->tag, &dos); + } + } + + return std::make_shared(CustomPayloadPacket::UPDATE_CREATIVE_REGISTRY, baos.toByteArray()); +} + void IUIScene_CreativeMenu::staticCtor() { vector< shared_ptr > *list; @@ -801,55 +852,56 @@ void IUIScene_CreativeMenu::staticCtor() ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(MASK_SPLASH, MASK_LEVEL2EXTENDED, MASK_JUMPBOOST)) // end of tu31 potions + if (specs == nullptr) { + specs = new TabSpec * [eCreativeInventoryTab_COUNT]; - specs = new TabSpec*[eCreativeInventoryTab_COUNT]; - - // Top Row - ECreative_Inventory_Groups blocksGroup[] = {eCreativeInventory_BuildingBlocks}; - specs[eCreativeInventoryTab_BuildingBlocks] = new TabSpec(L"Structures", IDS_GROUPNAME_BUILDING_BLOCKS, 1, blocksGroup); + // Top Row + ECreative_Inventory_Groups blocksGroup[] = { eCreativeInventory_BuildingBlocks }; + specs[eCreativeInventoryTab_BuildingBlocks] = new TabSpec(L"Structures", IDS_GROUPNAME_BUILDING_BLOCKS, 1, blocksGroup); #ifndef _CONTENT_PACKAGE - ECreative_Inventory_Groups decorationsGroup[] = {eCreativeInventory_Decoration}; - ECreative_Inventory_Groups debugDecorationsGroup[] = {eCreativeInventory_ArtToolsDecorations}; - specs[eCreativeInventoryTab_Decorations] = new TabSpec(L"Decoration", IDS_GROUPNAME_DECORATIONS, 1, decorationsGroup, 0, nullptr, 1, debugDecorationsGroup); + ECreative_Inventory_Groups decorationsGroup[] = { eCreativeInventory_Decoration }; + ECreative_Inventory_Groups debugDecorationsGroup[] = { eCreativeInventory_ArtToolsDecorations }; + specs[eCreativeInventoryTab_Decorations] = new TabSpec(L"Decoration", IDS_GROUPNAME_DECORATIONS, 1, decorationsGroup, 0, nullptr, 1, debugDecorationsGroup); #else - ECreative_Inventory_Groups decorationsGroup[] = {eCreativeInventory_Decoration}; - specs[eCreativeInventoryTab_Decorations] = new TabSpec(L"Decoration", IDS_GROUPNAME_DECORATIONS, 1, decorationsGroup); + ECreative_Inventory_Groups decorationsGroup[] = { eCreativeInventory_Decoration }; + specs[eCreativeInventoryTab_Decorations] = new TabSpec(L"Decoration", IDS_GROUPNAME_DECORATIONS, 1, decorationsGroup); #endif - ECreative_Inventory_Groups redAndTranGroup[] = {eCreativeInventory_Transport, eCreativeInventory_Redstone}; - specs[eCreativeInventoryTab_RedstoneAndTransport] = new TabSpec(L"RedstoneAndTransport", IDS_GROUPNAME_REDSTONE_AND_TRANSPORT, 2, redAndTranGroup); + ECreative_Inventory_Groups redAndTranGroup[] = { eCreativeInventory_Transport, eCreativeInventory_Redstone }; + specs[eCreativeInventoryTab_RedstoneAndTransport] = new TabSpec(L"RedstoneAndTransport", IDS_GROUPNAME_REDSTONE_AND_TRANSPORT, 2, redAndTranGroup); - ECreative_Inventory_Groups materialsGroup[] = {eCreativeInventory_Materials}; - specs[eCreativeInventoryTab_Materials] = new TabSpec(L"Materials", IDS_GROUPNAME_MATERIALS, 1, materialsGroup); + ECreative_Inventory_Groups materialsGroup[] = { eCreativeInventory_Materials }; + specs[eCreativeInventoryTab_Materials] = new TabSpec(L"Materials", IDS_GROUPNAME_MATERIALS, 1, materialsGroup); - ECreative_Inventory_Groups foodGroup[] = {eCreativeInventory_Food}; - specs[eCreativeInventoryTab_Food] = new TabSpec(L"Food", IDS_GROUPNAME_FOOD, 1, foodGroup); + ECreative_Inventory_Groups foodGroup[] = { eCreativeInventory_Food }; + specs[eCreativeInventoryTab_Food] = new TabSpec(L"Food", IDS_GROUPNAME_FOOD, 1, foodGroup); - ECreative_Inventory_Groups toolsGroup[] = {eCreativeInventory_ToolsArmourWeapons}; - specs[eCreativeInventoryTab_ToolsWeaponsArmor] = new TabSpec(L"Tools", IDS_GROUPNAME_TOOLS_WEAPONS_ARMOR, 1, toolsGroup); + ECreative_Inventory_Groups toolsGroup[] = { eCreativeInventory_ToolsArmourWeapons }; + specs[eCreativeInventoryTab_ToolsWeaponsArmor] = new TabSpec(L"Tools", IDS_GROUPNAME_TOOLS_WEAPONS_ARMOR, 1, toolsGroup); - ECreative_Inventory_Groups brewingGroup[] = {eCreativeInventory_Brewing, eCreativeInventory_Potions_Level2_Extended, eCreativeInventory_Potions_Extended, eCreativeInventory_Potions_Level2, eCreativeInventory_Potions_Basic}; + ECreative_Inventory_Groups brewingGroup[] = { eCreativeInventory_Brewing, eCreativeInventory_Potions_Level2_Extended, eCreativeInventory_Potions_Extended, eCreativeInventory_Potions_Level2, eCreativeInventory_Potions_Basic }; - // Just use the text LT - the graphic doesn't fit in splitscreen either - // In 480p there's not enough room for the LT button, so use text instead - //if(!RenderManager.IsHiDef() && !RenderManager.IsWidescreen()) - { - specs[eCreativeInventoryTab_Brewing] = new TabSpec(L"Brewing", IDS_GROUPNAME_POTIONS_480, 5, brewingGroup); + // Just use the text LT - the graphic doesn't fit in splitscreen either + // In 480p there's not enough room for the LT button, so use text instead + //if(!RenderManager.IsHiDef() && !RenderManager.IsWidescreen()) + { + specs[eCreativeInventoryTab_Brewing] = new TabSpec(L"Brewing", IDS_GROUPNAME_POTIONS_480, 5, brewingGroup); + } + // else + // { + // specs[eCreativeInventoryTab_Brewing] = new TabSpec(L"icon_brewing.png", IDS_GROUPNAME_POTIONS, 1, brewingGroup, 4, potionsGroup); + // } + +#ifndef _CONTENT_PACKAGE + ECreative_Inventory_Groups miscGroup[] = { eCreativeInventory_Misc }; + ECreative_Inventory_Groups debugMiscGroup[] = { eCreativeInventory_ArtToolsMisc }; + specs[eCreativeInventoryTab_Misc] = new TabSpec(L"Misc", IDS_GROUPNAME_MISCELLANEOUS, 1, miscGroup, 0, nullptr, 1, debugMiscGroup); +#else + ECreative_Inventory_Groups miscGroup[] = { eCreativeInventory_Misc }; + specs[eCreativeInventoryTab_Misc] = new TabSpec(L"Misc", IDS_GROUPNAME_MISCELLANEOUS, 1, miscGroup); +#endif } - // else - // { - // specs[eCreativeInventoryTab_Brewing] = new TabSpec(L"icon_brewing.png", IDS_GROUPNAME_POTIONS, 1, brewingGroup, 4, potionsGroup); - // } - -#ifndef _CONTENT_PACKAGE - ECreative_Inventory_Groups miscGroup[] = {eCreativeInventory_Misc}; - ECreative_Inventory_Groups debugMiscGroup[] = {eCreativeInventory_ArtToolsMisc}; - specs[eCreativeInventoryTab_Misc] = new TabSpec(L"Misc", IDS_GROUPNAME_MISCELLANEOUS, 1, miscGroup, 0, nullptr, 1, debugMiscGroup); -#else - ECreative_Inventory_Groups miscGroup[] = {eCreativeInventory_Misc}; - specs[eCreativeInventoryTab_Misc] = new TabSpec(L"Misc", IDS_GROUPNAME_MISCELLANEOUS, 1, miscGroup); -#endif } IUIScene_CreativeMenu::IUIScene_CreativeMenu() diff --git a/Minecraft.Client/Common/UI/IUIScene_CreativeMenu.h b/Minecraft.Client/Common/UI/IUIScene_CreativeMenu.h index 02625233..855e5198 100644 --- a/Minecraft.Client/Common/UI/IUIScene_CreativeMenu.h +++ b/Minecraft.Client/Common/UI/IUIScene_CreativeMenu.h @@ -1,6 +1,7 @@ #pragma once #include "IUIScene_AbstractContainerMenu.h" #include "../../../Minecraft.World/AbstractContainerMenu.h" +#include "../../../Minecraft.World/CustomPayloadPacket.h" // 4J Stu - This class is for code that is common between XUI and Iggy class SimpleContainer; @@ -100,10 +101,16 @@ protected: bool m_bCarryingCreativeItem; int m_creativeSlotX, m_creativeSlotY, m_inventorySlotX, m_inventorySlotY; + static void _wipeCreativeItems(); + public: static void staticCtor(); IUIScene_CreativeMenu(); + static void loadFromLocal(); + static void loadFromPacket(byteArray packetData); + + static std::shared_ptr createUpdatePacket(); protected: ECreativeInventoryTabs m_curTab; int m_tabDynamicPos[eCreativeInventoryTab_COUNT]; diff --git a/Minecraft.Client/PlayerConnection.cpp b/Minecraft.Client/PlayerConnection.cpp index 166e03dc..845d9ee3 100644 --- a/Minecraft.Client/PlayerConnection.cpp +++ b/Minecraft.Client/PlayerConnection.cpp @@ -2417,7 +2417,7 @@ void PlayerConnection::handleCustomPayload(shared_ptr custo player->inventory->setItem(player->inventory->selected, sentItem); } } - else if (CustomPayloadPacket::QUICK_EQUIP_PACKET.compare(customPayloadPacket->identifier) == 0) { + /*else if (CustomPayloadPacket::QUICK_EQUIP_PACKET.compare(customPayloadPacket->identifier) == 0) { //ByteArrayInputStream bais(customPayloadPacket->data); //DataInputStream input(&bais); //shared_ptr sentItem = Packet::readItem(&input); @@ -2448,7 +2448,7 @@ void PlayerConnection::handleCustomPayload(shared_ptr custo //PlayerList* playerList = MinecraftServer::getInstance()->getPlayers(); //playerList->broadcastAll(std::make_shared(player->entityId, slot, sentItem)); - } + }*/ else if (CustomPayloadPacket::TRADER_SELECTION_PACKET.compare(customPayloadPacket->identifier) == 0) { ByteArrayInputStream bais(customPayloadPacket->data); diff --git a/Minecraft.Client/PlayerList.cpp b/Minecraft.Client/PlayerList.cpp index 8814401d..04da1990 100644 --- a/Minecraft.Client/PlayerList.cpp +++ b/Minecraft.Client/PlayerList.cpp @@ -37,6 +37,8 @@ #include "Common/Network/Sony/NetworkPlayerSony.h" #endif +#include "../Minecraft.World/Recipes.h" + #if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD) #include "../Minecraft.Server/Access/Access.h" #include "../Minecraft.Server/Common/StringUtils.h" @@ -51,6 +53,7 @@ extern bool g_Win64DedicatedServer; static unsigned int s_playerListTickCount = 0; static const int kIdentityResponseGraceTicks = 200; // 10 seconds at 20 TPS #endif +#include "../Minecraft.Client/Common/UI/IUIScene_CreativeMenu.h" // 4J - this class is fairly substantially altered as there didn't seem any point in porting code for banning, whitelisting, ops etc. @@ -300,6 +303,9 @@ bool PlayerList::placeNewPlayer(Connection *connection, shared_ptr app.DebugPrintf("RECONNECT: placeNewPlayer smallId=%d entityId=%d dim=%d\n", newSmallId, player->entityId, level->dimension->id); + playerConnection->send(Recipes::getInstance()->createUpdatePacket()); + playerConnection->send(IUIScene_CreativeMenu::createUpdatePacket()); + playerConnection->send(std::make_shared(L"", player->entityId, level->getLevelData()->getGenerator(), level->getSeed(), player->gameMode->getGameModeForPlayer()->getId(), diff --git a/Minecraft.World/CustomPayloadPacket.cpp b/Minecraft.World/CustomPayloadPacket.cpp index e6dacfeb..6b706d6f 100644 --- a/Minecraft.World/CustomPayloadPacket.cpp +++ b/Minecraft.World/CustomPayloadPacket.cpp @@ -5,30 +5,39 @@ #include "CustomPayloadPacket.h" // Mojang-defined custom packets -const wstring CustomPayloadPacket::CUSTOM_BOOK_PACKET = L"MC|BEdit"; -const wstring CustomPayloadPacket::CUSTOM_BOOK_SIGN_PACKET = L"MC|BSign"; -const wstring CustomPayloadPacket::TEXTURE_PACK_PACKET = L"MC|TPack"; -const wstring CustomPayloadPacket::TRADER_LIST_PACKET = L"MC|TrList"; -const wstring CustomPayloadPacket::TRADER_SELECTION_PACKET = L"MC|TrSel"; -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"; +const wstring CustomPayloadPacket::CUSTOM_BOOK_PACKET = CreateVanillaPayloadKey(L"BEdit"); +const wstring CustomPayloadPacket::CUSTOM_BOOK_SIGN_PACKET = CreateVanillaPayloadKey(L"BSign"); +const wstring CustomPayloadPacket::TEXTURE_PACK_PACKET = CreateVanillaPayloadKey(L"TPack"); +const wstring CustomPayloadPacket::TRADER_LIST_PACKET = CreateVanillaPayloadKey(L"TrList"); +const wstring CustomPayloadPacket::TRADER_SELECTION_PACKET = CreateVanillaPayloadKey(L"TrSel"); -const wstring CustomPayloadPacket::CIPHER_KEY_CHANNEL = L"MC|CKey"; -const wstring CustomPayloadPacket::CIPHER_ACK_CHANNEL = L"MC|CAck"; -const wstring CustomPayloadPacket::CIPHER_ON_CHANNEL = L"MC|COn"; +// neoLegacy-defined custom packets +const wstring CustomPayloadPacket::UPDATE_RECIPE_REGISTRY = CreatePayloadKey(L"neo", L"UpdRReg"); +const wstring CustomPayloadPacket::UPDATE_CREATIVE_REGISTRY = CreatePayloadKey(L"neo", L"UpdCReg"); -const wstring CustomPayloadPacket::IDENTITY_TOKEN_ISSUE = L"MC|CTIssue"; -const wstring CustomPayloadPacket::IDENTITY_TOKEN_CHALLENGE = L"MC|CTChallenge"; -const wstring CustomPayloadPacket::IDENTITY_TOKEN_RESPONSE = L"MC|CTResponse"; +//todo: figure out if we should replace the packets in the comment section with a custom payload identifier +//comment section start +const wstring CustomPayloadPacket::SET_ADVENTURE_COMMAND_PACKET = CreateVanillaPayloadKey(L"AdvCdm"); +const wstring CustomPayloadPacket::SET_BEACON_PACKET = CreateVanillaPayloadKey(L"Beacon"); +const wstring CustomPayloadPacket::SET_ITEM_NAME_PACKET = CreateVanillaPayloadKey(L"ItemName"); -const wstring CustomPayloadPacket::FORK_HELLO_CHANNEL = L"MC|ForkHello"; -const wstring CustomPayloadPacket::FORK_PLAYER_LEAVE_CHANNEL = L"MC|ForkPLeave"; +const wstring CustomPayloadPacket::CIPHER_KEY_CHANNEL = CreateVanillaPayloadKey(L"CKey"); +const wstring CustomPayloadPacket::CIPHER_ACK_CHANNEL = CreateVanillaPayloadKey(L"CAck"); +const wstring CustomPayloadPacket::CIPHER_ON_CHANNEL = CreateVanillaPayloadKey(L"COn"); -const wstring CustomPayloadPacket::QUICK_EQUIP_PACKET = L"MC|QEquip"; -const wstring CustomPayloadPacket::QUICK_EQUIP_SERVER_PACKET = L"MC|QEquipServer"; +const wstring CustomPayloadPacket::IDENTITY_TOKEN_ISSUE = CreateVanillaPayloadKey(L"CTIssue"); +const wstring CustomPayloadPacket::IDENTITY_TOKEN_CHALLENGE = CreateVanillaPayloadKey(L"CTChallenge"); +const wstring CustomPayloadPacket::IDENTITY_TOKEN_RESPONSE = CreateVanillaPayloadKey(L"CTResponse"); -const wstring CustomPayloadPacket::ENCHANTMENT_LIST_PACKET = L"MC|EnchList"; +const wstring CustomPayloadPacket::FORK_HELLO_CHANNEL = CreateVanillaPayloadKey(L"ForkHello"); +const wstring CustomPayloadPacket::FORK_PLAYER_LEAVE_CHANNEL = CreateVanillaPayloadKey(L"ForkPLeave"); + +const wstring CustomPayloadPacket::ENCHANTMENT_LIST_PACKET = CreateVanillaPayloadKey(L"EnchList"); +//comment section end + +//removed cause its now handled on the server side +// const wstring CustomPayloadPacket::QUICK_EQUIP_PACKET = CreateVanillaPayloadKey(L"QEquip"); +// const wstring CustomPayloadPacket::QUICK_EQUIP_SERVER_PACKET = CreateVanillaPayloadKey(L"QEquipServer"); CustomPayloadPacket::CustomPayloadPacket() : length(0) diff --git a/Minecraft.World/CustomPayloadPacket.h b/Minecraft.World/CustomPayloadPacket.h index ae7163bf..4fd30f05 100644 --- a/Minecraft.World/CustomPayloadPacket.h +++ b/Minecraft.World/CustomPayloadPacket.h @@ -3,6 +3,9 @@ using namespace std; #include "Packet.h" +#define CreatePayloadKey(identifier, action) identifier L"|" action +#define CreateVanillaPayloadKey(action) CreatePayloadKey(L"MC", action) + class CustomPayloadPacket : public Packet, public enable_shared_from_this { public: @@ -17,6 +20,10 @@ public: static const wstring SET_BEACON_PACKET; static const wstring SET_ITEM_NAME_PACKET; + // neoLegacy-defined custom packets + static const wstring UPDATE_RECIPE_REGISTRY; + static const wstring UPDATE_CREATIVE_REGISTRY; + // Security: stream cipher handshake channels static const wstring CIPHER_KEY_CHANNEL; // server->client: carries 32-byte key (16 AES key + 16 IV) static const wstring CIPHER_ACK_CHANNEL; // client->server: ack (empty payload) @@ -32,8 +39,8 @@ public: static const wstring FORK_PLAYER_LEAVE_CHANNEL; // server->client: player disconnected (payload: UTF gamertag) // Fixes for MP related crashes - static const wstring QUICK_EQUIP_PACKET; - static const wstring QUICK_EQUIP_SERVER_PACKET; + //static const wstring QUICK_EQUIP_PACKET; + //static const wstring QUICK_EQUIP_SERVER_PACKET; static const wstring ENCHANTMENT_LIST_PACKET; diff --git a/Minecraft.World/FireworksRecipe.h b/Minecraft.World/FireworksRecipe.h index b0c7c236..015a2478 100644 --- a/Minecraft.World/FireworksRecipe.h +++ b/Minecraft.World/FireworksRecipe.h @@ -43,4 +43,8 @@ public: // 4J Added static void updatePossibleRecipes(shared_ptr craftSlots, bool *firework, bool *charge, bool *fade); static bool isValidIngredient(shared_ptr item, bool firework, bool charge, bool fade); + + virtual void writeToStream(DataOutputStream* dos) { + dos->writeByte(99); + } }; \ No newline at end of file diff --git a/Minecraft.World/ItemInstance.cpp b/Minecraft.World/ItemInstance.cpp index e4af60c6..dcc2200f 100644 --- a/Minecraft.World/ItemInstance.cpp +++ b/Minecraft.World/ItemInstance.cpp @@ -220,6 +220,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 cff33a97..e2b0e059 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.h b/Minecraft.World/Packet.h index c639c8f1..9938afa6 100644 --- a/Minecraft.World/Packet.h +++ b/Minecraft.World/Packet.h @@ -103,7 +103,5 @@ public: static shared_ptr readItem(DataInputStream *dis); static void writeItem(shared_ptr item, DataOutputStream *dos); static CompoundTag *readNbt(DataInputStream *dis); - -protected: static void writeNbt(CompoundTag *tag, DataOutputStream *dos); }; \ No newline at end of file diff --git a/Minecraft.World/Recipes.cpp b/Minecraft.World/Recipes.cpp index 2eedf59f..f8c1ef41 100644 --- a/Minecraft.World/Recipes.cpp +++ b/Minecraft.World/Recipes.cpp @@ -29,20 +29,14 @@ void Recipes::_init() { // 4J Jev: instance = new Recipes(); recipies = new RecipyList(); -} -Recipes::Recipes() -{ - 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; + pArmorRecipes = new ArmorRecipes; + pClothDyeRecipes = new ClothDyeRecipes; + pFoodRecipies = new FoodRecipies; + pOreRecipies = new OreRecipies; + pStructureRecipies = new StructureRecipies; + pToolRecipies = new ToolRecipies; + pWeaponRecipies = new WeaponRecipies; // 4J Stu - These just don't work with our crafting menu //recipies->push_back(new ArmorDyeRecipe()); @@ -50,8 +44,10 @@ Recipes::Recipes() //recipies->add(new MapExtendingRecipe()); //recipies->add(new FireworksRecipe()); pFireworksRecipes = new FireworksRecipe(); +} - +void Recipes::_compileRecipes() +{ addShapedRecipy(new ItemInstance(Tile::wood, 4, 0), // L"sczg", L"#", // @@ -186,7 +182,7 @@ Recipes::Recipes() L"W#W", // L"W#W", // - L'#', Item::stick, + L'#', Item::stick, L'W', new ItemInstance(Tile::wood, 1, TreeTile::ACACIA_TRUNK), L'S'); @@ -195,7 +191,7 @@ Recipes::Recipes() L"W#W", // L"W#W", // - L'#', Item::stick, + L'#', Item::stick, L'W', new ItemInstance(Tile::wood, 1, TreeTile::DARK_TRUNK), L'S'); @@ -475,12 +471,10 @@ Recipes::Recipes() L'S'); pArmorRecipes->addRecipes(this); - //iCount=getRecipies()->size(); + pClothDyeRecipes->addRecipes(this); - - addShapedRecipy(new ItemInstance(Tile::snow, 1), // L"sscig", L"##", // @@ -497,7 +491,7 @@ Recipes::Recipes() L'#', Item::prismarine_shard, L'S'); - addShapedRecipy(new ItemInstance(Tile::prismarine, 1,PrismarineTile::TYPE_BRICKS), // + addShapedRecipy(new ItemInstance(Tile::prismarine, 1, PrismarineTile::TYPE_BRICKS), // L"ssscig", L"###", // L"###", // @@ -507,7 +501,7 @@ Recipes::Recipes() L'S'); - addShapedRecipy(new ItemInstance(Tile::prismarine, 1,PrismarineTile::TYPE_DARK), // + addShapedRecipy(new ItemInstance(Tile::prismarine, 1, PrismarineTile::TYPE_DARK), // L"ssscicig", L"###", // L"#X#", // @@ -657,10 +651,6 @@ Recipes::Recipes() - - - //iCount=getRecipies()->size(); - addShapedRecipy(new ItemInstance(Item::cake, 1), // L"ssscicicicig", L"AAA", // @@ -770,7 +760,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", // @@ -803,7 +793,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", // @@ -850,7 +840,7 @@ Recipes::Recipes() L'#', Tile::glass, L'T'); - + // torch made of charcoal - moved to be the default due to the tutorial using it addShapedRecipy(new ItemInstance(Tile::torch, 4), // @@ -961,12 +951,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), // @@ -1096,24 +1086,24 @@ Recipes::Recipes() L'#', Tile::wood, L'X', Item::diamond, 'D'); - - addShapedRecipy(new ItemInstance(Item::leather, 1), - L"sscig", - L"##", - L"##", - L'#', Item::rabbit_hide, - L'D'); + addShapedRecipy(new ItemInstance(Item::leather, 1), + L"sscig", + L"##", + L"##", + + L'#', Item::rabbit_hide, + L'D'); addShapedRecipy(new ItemInstance(Item::armor_stand, 1), - L"ssscictg", - L"SSS", - L" S ", - L"SXS", - L'S', Item::stick, - L'X', Tile::stoneSlabHalf, + L"ssscictg", + L"SSS", + L" S ", + L"SXS", + L'S', Item::stick, + L'X', Tile::stoneSlabHalf, L'D'); - + addShapedRecipy(new ItemInstance(Item::paper, 3), // @@ -1206,21 +1196,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'); @@ -1233,7 +1223,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 ", // @@ -1241,7 +1231,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 ", // @@ -1249,37 +1239,38 @@ Recipes::Recipes() L'D', Item::dye_powder, L'C', Item::fireworksCharge, L'D'); - - // Sort so the largest recipes get checked first! - /* 4J-PB - TODO - Collections.sort(recipies, new Comparator() - { - public: int compare(Recipy r0, Recipy r1) - { - - // shapeless recipes are put in the back of the list - if (r0 instanceof ShapelessRecipy && r1 instanceof ShapedRecipy) - { - return 1; - } - if (r1 instanceof ShapelessRecipy && r0 instanceof ShapedRecipy) - { - return -1; - } - - if (r1.size() < r0.size()) return -1; - if (r1.size() > r0.size()) return 1; - return 0; - } - }); - */ - - // 4J-PB removed System.out.println(recipies->size() + L" recipes"); - - // 4J-PB - build the array of ingredients required per recipe buildRecipeIngredientsArray(); } +void Recipes::_wipeRecipes() +{ + int iCount = recipies->size(); + + for (int i = 0; i < iCount; i++) { + Recipy::INGREDIENTS_REQUIRED& req = m_pRecipeIngredientsRequired[i]; + + delete[] req.iIngIDA; + delete[] req.iIngValA; + delete[] req.iIngAuxValA; + delete[] req.uiGridA; + } + + for (int i = 0; i < iCount; i++) { + delete (*recipies)[i]; + } + + recipies->clear(); + + delete[] m_pRecipeIngredientsRequired; + m_pRecipeIngredientsRequired = nullptr; +} + +Recipes::Recipes() +{ + _init(); + _compileRecipes(); +} + // 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, ...) { @@ -1563,7 +1554,7 @@ void Recipes::buildRecipeIngredientsArray(void) int iRecipeC=static_cast(recipies->size()); - m_pRecipeIngredientsRequired= new Recipy::INGREDIENTS_REQUIRED [iRecipeC]; + m_pRecipeIngredientsRequired = new Recipy::INGREDIENTS_REQUIRED [iRecipeC]; int iCount=0; for (auto& recipe : *recipies) @@ -1577,4 +1568,46 @@ void Recipes::buildRecipeIngredientsArray(void) Recipy::INGREDIENTS_REQUIRED *Recipes::getRecipeIngredientsArray(void) { return m_pRecipeIngredientsRequired; -} \ No newline at end of file +} + +void Recipes::loadFromLocal() +{ + this->_wipeRecipes(); + this->_compileRecipes(); +} + +void Recipes::loadFromPacket(byteArray packetData) +{ + ByteArrayInputStream bais(packetData); + DataInputStream input(&bais); + + this->_wipeRecipes(); + { + int iCount = input.readInt(); + for (int i = 0; i < iCount; i++) { + int recipeType = input.readByte(); + + if (recipeType == 1) { + recipies->push_back(ShapelessRecipy::readFromStream(&input)); + } else if (recipeType == 2) { + recipies->push_back(ShapedRecipy::readFromStream(&input)); + } + } + } + this->buildRecipeIngredientsArray(); +} + +std::shared_ptr Recipes::createUpdatePacket() +{ + ByteArrayOutputStream baos; + DataOutputStream dos(&baos); + + int iCount = recipies->size(); + dos.writeInt(iCount); + + for (int i = 0; i < iCount; i++) { + (*recipies)[i]->writeToStream(&dos); + } + + return std::make_shared(CustomPayloadPacket::UPDATE_RECIPE_REGISTRY, baos.toByteArray()); +} diff --git a/Minecraft.World/Recipes.h b/Minecraft.World/Recipes.h index d6e508a8..856e3a64 100644 --- a/Minecraft.World/Recipes.h +++ b/Minecraft.World/Recipes.h @@ -16,6 +16,7 @@ import net.minecraft.world.level.tile.Tile; */ #include "Recipy.h" +#include "../Minecraft.World/CustomPayloadPacket.h" #pragma once using namespace std; @@ -84,6 +85,8 @@ public: private: void _init(); // 4J add + void _compileRecipes(); + void _wipeRecipes(); Recipes(); public: @@ -97,6 +100,11 @@ public: shared_ptr getItemForRecipe(Recipy *r); Recipy::INGREDIENTS_REQUIRED *getRecipeIngredientsArray(); + void loadFromLocal(); + void loadFromPacket(byteArray packetData); + + std::shared_ptr createUpdatePacket(); + private: void buildRecipeIngredientsArray(); Recipy::INGREDIENTS_REQUIRED *m_pRecipeIngredientsRequired; diff --git a/Minecraft.World/Recipy.h b/Minecraft.World/Recipy.h index 9cbf5cac..b2ad0127 100644 --- a/Minecraft.World/Recipy.h +++ b/Minecraft.World/Recipy.h @@ -10,13 +10,13 @@ #define RECIPE_TYPE_2x2 0 #define RECIPE_TYPE_3x3 1 -class Recipy +class Recipy { public: enum _eGroupType { - eGroupType_First=0, - eGroupType_Structure=0, + eGroupType_First = 0, + eGroupType_Structure = 0, eGroupType_Tool, eGroupType_Food, eGroupType_Armour, @@ -28,28 +28,30 @@ public: eGroupType; // to class the item produced by the recipe // 4J-PB - we'll classing an ingredient ID with a different aux value as a different IngID AuxVal pair - typedef struct + typedef struct { int iIngC; int iType; // Can be a 2x2 or a 3x3. Inventory crafting can only make a 2x2. - int *iIngIDA; - int *iIngValA; - int *iIngAuxValA; - Recipy *pRecipy; + int* iIngIDA; + int* iIngValA; + int* iIngAuxValA; + Recipy* pRecipy; bool bCanMake[XUSER_MAX_COUNT]; - unsigned int *uiGridA; // hold the layout of the recipe (id | auxval<<24) + unsigned int* uiGridA; // hold the layout of the recipe (id | auxval<<24) unsigned short usBitmaskMissingGridIngredients[XUSER_MAX_COUNT]; // each bit set means we don't have that grid ingredient } INGREDIENTS_REQUIRED; - ~Recipy() {} - virtual bool matches(shared_ptr craftSlots, Level *level) = 0; + virtual ~Recipy() = default; + virtual bool matches(shared_ptr craftSlots, Level* level) = 0; virtual shared_ptr assemble(shared_ptr craftSlots) = 0; virtual int size() = 0; - virtual const ItemInstance *getResultItem() = 0; - virtual const int getGroup() = 0; + virtual const ItemInstance* getResultItem() = 0; + virtual const int getGroup() = 0; // 4J-PB virtual bool reqs(int iRecipe) = 0; - virtual void reqs(INGREDIENTS_REQUIRED *pIngReq) = 0; + virtual void reqs(INGREDIENTS_REQUIRED* pIngReq) = 0; + + virtual void writeToStream(DataOutputStream* dos) = 0; }; diff --git a/Minecraft.World/ShapedRecipy.cpp b/Minecraft.World/ShapedRecipy.cpp index 7a0e85b0..d73fe6ee 100644 --- a/Minecraft.World/ShapedRecipy.cpp +++ b/Minecraft.World/ShapedRecipy.cpp @@ -23,6 +23,24 @@ ShapedRecipy::ShapedRecipy(int width, int height, ItemInstance **recipeItems, It _keepTag = false; } +ShapedRecipy::~ShapedRecipy() { + // todo: why does this cause a error when clearing out these specifically? + // might be leaking memory here but im not sure cause it crashes when you clear them, so we dont clear them + /*for (int x = 0; x < 3; x++) { + for (int y = 0; y < 3; y++) { + if (x < width && y < height) { + delete recipeItems[x + y * width]; + } + } + }*/ + + delete[] recipeItems; + delete result; + + recipeItems = nullptr; + result = nullptr; +} + const int ShapedRecipy::getGroup() { return group; @@ -227,4 +245,65 @@ ShapedRecipy *ShapedRecipy::keepTag() { _keepTag = true; return this; +} + +void ShapedRecipy::writeToStream(DataOutputStream* dos) { + dos->writeByte(2); + dos->writeByte(this->group); + + //write result item, it should always be valid + { + dos->writeShort(this->result->id); + dos->writeByte(this->result->count); + dos->writeShort(this->result->getAuxValue()); + + Packet::writeNbt(this->result->tag, dos); + } + + dos->writeByte((this->width << 2) | this->height); + + for (int i = 0; i < (this->width * this->height); i++) { + ItemInstance* ingredients_item = this->recipeItems[i]; + dos->writeBoolean(ingredients_item == nullptr); + if (ingredients_item == nullptr) continue; + + dos->writeShort(ingredients_item->id); + dos->writeShort(ingredients_item->getAuxValue()); + Packet::writeNbt(ingredients_item->tag, dos); + } +} + +ShapedRecipy* ShapedRecipy::readFromStream(DataInputStream* dis) { + int groupType = dis->readByte(); + + int resultItemID = dis->readShort(); + int resultItemCount = dis->readByte(); + int resultItemAux = dis->readShort(); + + ItemInstance* resultItem = new ItemInstance(resultItemID, resultItemCount, 0); + resultItem->setRawAuxValue(resultItemAux); + resultItem->tag = Packet::readNbt(dis); + + unsigned char packedSize = dis->readByte(); + + int width = (packedSize >> 2) & 0x3; + int height = packedSize & 0x3; + ItemInstance** ids = new ItemInstance*[width * height]; + + for (int i = 0; i < width * height; i++) { + ItemInstance* ingredients_item = nullptr; + bool isNull = dis->readBoolean(); + if (!isNull) { + int itemId = dis->readShort(); + int itemAux = dis->readShort(); + + ingredients_item = new ItemInstance(itemId, 1, 0); + ingredients_item->setRawAuxValue(itemAux); + ingredients_item->tag = Packet::readNbt(dis); + } + + ids[i] = ingredients_item; + } + + return new ShapedRecipy(width, height, ids, resultItem, groupType); } \ No newline at end of file diff --git a/Minecraft.World/ShapedRecipy.h b/Minecraft.World/ShapedRecipy.h index c111b7c7..25b6cc29 100644 --- a/Minecraft.World/ShapedRecipy.h +++ b/Minecraft.World/ShapedRecipy.h @@ -1,21 +1,22 @@ #pragma once -class ShapedRecipy : public Recipy +class ShapedRecipy : public Recipy { private: int width, height, group; - ItemInstance **recipeItems; - ItemInstance *result; + ItemInstance** recipeItems; + ItemInstance* result; bool _keepTag; public: const int resultId; public: - ShapedRecipy(int width, int height, ItemInstance **recipeItems, ItemInstance *result, int iGroup=Recipy::eGroupType_Decoration); + ShapedRecipy(int width, int height, ItemInstance** recipeItems, ItemInstance* result, int iGroup = Recipy::eGroupType_Decoration); + virtual ~ShapedRecipy() override; - virtual const ItemInstance *getResultItem(); + virtual const ItemInstance* getResultItem(); virtual const int getGroup(); - virtual bool matches(shared_ptr craftSlots, Level *level); + virtual bool matches(shared_ptr craftSlots, Level* level); private: bool matches(shared_ptr craftSlots, int xOffs, int yOffs, bool xFlip); @@ -23,10 +24,14 @@ private: public: virtual shared_ptr assemble(shared_ptr craftSlots); virtual int size(); - ShapedRecipy *keepTag(); + ShapedRecipy* keepTag(); // 4J-PB - to return the items required to make a recipe virtual bool reqs(int iRecipe); - virtual void reqs(INGREDIENTS_REQUIRED *pIngReq); + virtual void reqs(INGREDIENTS_REQUIRED* pIngReq); + + virtual void writeToStream(DataOutputStream* dos); + static ShapedRecipy* readFromStream(DataInputStream* dis); + }; diff --git a/Minecraft.World/ShapelessRecipy.cpp b/Minecraft.World/ShapelessRecipy.cpp index 8fab1b7e..3bf24ad7 100644 --- a/Minecraft.World/ShapelessRecipy.cpp +++ b/Minecraft.World/ShapelessRecipy.cpp @@ -19,6 +19,19 @@ ShapelessRecipy::ShapelessRecipy(ItemInstance *result, vector *i { } +ShapelessRecipy::~ShapelessRecipy() { + for (int i = 0; i < ingredients->size(); i++) { + delete (*ingredients)[i]; + } + + delete ingredients; + delete result; + + ingredients = nullptr; + result = nullptr; +} + + const int ShapelessRecipy::getGroup() { return group; @@ -173,4 +186,61 @@ void ShapelessRecipy::reqs(INGREDIENTS_REQUIRED *pIngReq) delete [] TempIngReq.iIngValA; delete [] TempIngReq.iIngAuxValA; delete [] TempIngReq.uiGridA; -} \ No newline at end of file +} + +void ShapelessRecipy::writeToStream(DataOutputStream* dos) { + dos->writeByte(1); + dos->writeByte(this->group); + + //write result item, it should always be valid + { + dos->writeShort(this->result->id); + dos->writeByte(this->result->count); + dos->writeShort(this->result->getAuxValue()); + + Packet::writeNbt(this->result->tag, dos); + } + + byte iCount = ingredients->size(); + dos->writeByte(iCount); + + for (int i = 0; i < iCount; i++) { + ItemInstance* item = (*ingredients)[i]; + dos->writeBoolean(item == nullptr); + if (item == nullptr) continue; + + dos->writeShort(item->id); + dos->writeShort(item->getAuxValue()); + Packet::writeNbt(item->tag, dos); + } + +} + +ShapelessRecipy* ShapelessRecipy::readFromStream(DataInputStream* dis) { + unsigned char groupType = dis->readByte(); + + int resultItemID = dis->readShort(); + int resultItemCount = dis->readByte(); + int resultItemAux = dis->readShort(); + + ItemInstance* resultItem = new ItemInstance(resultItemID, resultItemCount, 0); + resultItem->setRawAuxValue(resultItemAux); + resultItem->tag = Packet::readNbt(dis); + + vector* ingredients = new vector(); + int iCount = dis->readByte(); + for (int i = 0; i < iCount; i++) { + if (dis->readBoolean() == true) continue; //item is null or something weird + + int itemID = dis->readShort(); + int itemAux = dis->readShort(); + + ItemInstance* ingredients_item = new ItemInstance(itemID, 1, 0); + ingredients_item->setRawAuxValue(itemAux); + ingredients_item->tag = Packet::readNbt(dis); + + ingredients->push_back(ingredients_item); + } + + return new ShapelessRecipy(resultItem, ingredients, (Recipy::_eGroupType)groupType); +} diff --git a/Minecraft.World/ShapelessRecipy.h b/Minecraft.World/ShapelessRecipy.h index bb1dfcf7..8e2c9e3e 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); + virtual ~ShapelessRecipy() override; virtual const ItemInstance *getResultItem(); virtual const int getGroup(); @@ -20,4 +21,6 @@ public: virtual bool reqs(int iRecipe); virtual void reqs(INGREDIENTS_REQUIRED *pIngReq); + virtual void writeToStream(DataOutputStream* dos); + static ShapelessRecipy* readFromStream(DataInputStream* dos); };