sync recipes from server to client, only shapeless right now

This commit is contained in:
DrPerkyLegit 2026-04-24 02:25:40 -04:00
parent 12429e1981
commit f2b304fad5
7 changed files with 256 additions and 29 deletions

View file

@ -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<LoginPacket> 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<EntityActionAtPos
void ClientConnection::handlePreLogin(shared_ptr<PreLoginPacket> 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<LevelSoundPacket> packet)
void ClientConnection::handleCustomPayload(shared_ptr<CustomPayloadPacket> 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);

View file

@ -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<LoginPacket> packet)
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
ServerRuntime::ServerLogManager::OnAcceptedPlayerLogin(GetPendingConnectionSmallId(connection), name);
#endif
connection->send(std::make_shared<CustomPayloadPacket>(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
}

View file

@ -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()
{
}

View file

@ -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;

View file

@ -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 *>(Tile::pistonBase), 1), //
addShapedRecipy(new ItemInstance(static_cast<Tile*>(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 *>(Tile::pistonStickyBase), 1), //
addShapedRecipy(new ItemInstance(static_cast<Tile*>(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;
}
}
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<CompoundTag>* 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<StringTag>* lore = (ListTag<StringTag> *) 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<CompoundTag>(L"ench"));
ListTag<CompoundTag>* list = static_cast<ListTag<CompoundTag> *>(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<short>(enchantmentId));
ench->putShort((wchar_t*)ItemInstance::TAG_ENCH_LEVEL, static_cast<byte>(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<StringTag>(L"Lore"));
ListTag<StringTag>* list = static_cast<ListTag<StringTag> *>(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<CustomPayloadPacket> 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<ItemInstance*>* ingredients = new vector<ItemInstance*>();
for (int j = 0; j < ingredientCount; j++) {
ingredients->emplace_back(parseItemInstance(&input));
}
ItemInstance* result = parseItemInstance(&input);
ShapelessRecipy* recipe = new ShapelessRecipy(result, ingredients, static_cast<Recipy::_eGroupType>(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<int>(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<ShapelessRecipy*>(recipe) != nullptr) {
ShapelessRecipy* shapeless = static_cast<ShapelessRecipy*>(recipe);
dos.writeByte(0); //0 for shapeless recipe
std::vector<ItemInstance*>* ingredients = shapeless->getIngredients();
dos.writeInt(static_cast<int>(ingredients->size()));
for (auto& ingredient : *ingredients) {
serializeItemInstance(&dos, ingredient);
}
} else if (dynamic_cast<ShapedRecipy*>(recipe) != nullptr) {
ShapedRecipy* shapedRecipe = static_cast<ShapedRecipy*>(recipe);
dos.writeByte(1); //1 for shaped recipe
continue;
}
serializeItemInstance(&dos, const_cast<ItemInstance*>(recipe->getResultItem()));
}
return baos.toByteArray();
}

View file

@ -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<ItemInstance> getItemForRecipe(Recipy *r);
Recipy::INGREDIENTS_REQUIRED *getRecipeIngredientsArray();
bool m_bPendingRecipeRebuild = false;
void rebuildRecipeArray();
void rebuildRecipeArray(std::shared_ptr<CustomPayloadPacket> packet);
byteArray buildSyncedRecipeArray();
private:
void buildRecipeIngredientsArray();
Recipy::INGREDIENTS_REQUIRED *m_pRecipeIngredientsRequired;

View file

@ -16,6 +16,8 @@ public:
virtual shared_ptr<ItemInstance> assemble(shared_ptr<CraftingContainer> craftSlots);
virtual int size();
vector<ItemInstance*>* 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);