1
0

Introduce recipe book functionality (#4493)

* Introduce recipe book functionality

The recipe book helps especially new players. Missing it gives the
impression that cuberite is not as advanced as it is.

The handling of the recipe book uses the following functions:

- Unlock Recipes
(https://wiki.vg/index.php?title=Protocol&oldid=14204#Unlock_Recipes) to
make recipes available and show the notification for new recipes.
Initialization is done on player login for known ones, the update is done
when new items are discovered.
- Craft Recipe Request
(https://wiki.vg/index.php?title=Protocol&oldid=14204#Craft_Recipe_Request)
when the user selects a recipe from the recipe book to fill the slots.

Known recipes are initialized on player login via `Unlock Recipes` with
`Action` 0.
As soon as a new recipe is discovered this is added via `Unlock Recipes`
with `Action` 1.

To be able to know and recognize new recipes the player class is
extended with `KnownItems` and `KnownRecipes`. As soon as a player
touches an item this is compared to the list of `KnownItems`, if the
item is unknown the recipes are checked for this item and the other
ingredients are checked with the list of `KnownItems`. If a full match
is discovered the recipe is unlocked with the client and stored in the
`KnownRecipes`.

To unlock recipes the recipe ID is sent to the client. A mapping file
(for protocol 1.12.2) translated the minecraft recipe names to ids. The
crafting.txt is extended with and minecraft recipe names is possible.

Limitations:
Only a single recipe is added to the crafting area. Multiple clicks or
shift click does not increase the number of builds.

Co-authored-by: peterbell10 <peterbell10@live.co.uk>

* Address first issues mentioned by @peterbell10

- Some linting
- Extract loading of recipe specific protocol mapping into a function
- Build `RecipeNameMap` only once
- Use `std::optional`
- Extract `LoadRecipe` from `Window`

* Start to implement new suggestions

* Update with suggestions from @peterbell10

* Some minor cleanup

* Update protocol packet IDs
* Remove unused include
* Include header in cmake
* Change a vector to integer counter

* Change dromedaryCase method names to PascalCase

* Address suggestions from @madmaxoft

* Read Protocol subdirectories to load recipe books

To load all recipebooks iterate over the `Protocol` subdirectories
to find mapping files.

Co-authored-by: peterbell10 <peterbell10@live.co.uk>
This commit is contained in:
Tobias Wilken
2020-07-14 18:56:42 +02:00
committed by GitHub
parent 6e3f6d58a4
commit 36eab1b323
36 changed files with 1614 additions and 457 deletions

View File

@@ -25,6 +25,7 @@ Implements the 1.12 protocol classes:
#include "../Root.h"
#include "../Server.h"
#include "../ClientHandle.h"
#include "../CraftingRecipes.h"
#include "../Bindings/PluginManager.h"
#include "../JsonUtils.h"
@@ -1007,6 +1008,7 @@ UInt32 cProtocol_1_12::GetPacketID(cProtocol::ePacketType a_Packet)
case pktTeleportEntity: return 0x4b;
case pktTimeUpdate: return 0x46;
case pktTitle: return 0x47;
case pktUnlockRecipe: return 0x30;
case pktUpdateBlockEntity: return 0x09;
case pktUpdateHealth: return 0x40;
case pktUpdateScore: return 0x44;
@@ -1019,10 +1021,27 @@ UInt32 cProtocol_1_12::GetPacketID(cProtocol::ePacketType a_Packet)
void cProtocol_1_12::HandleCraftRecipe(cByteBuffer & a_ByteBuffer)
{
HANDLE_READ(a_ByteBuffer, ReadBEUInt8, UInt8, WindowID);
HANDLE_READ(a_ByteBuffer, ReadVarInt, UInt32, RecipeID);
HANDLE_READ(a_ByteBuffer, ReadBool, bool, MakeAll);
auto CuberiteRecipeId = cRoot::Get()->GetRecipeMapper()->GetCuberiteRecipeId(RecipeID, m_Client->GetProtocolVersion());
if (CuberiteRecipeId.has_value())
{
m_Client->HandleCraftRecipe(CuberiteRecipeId.value());
}
}
void cProtocol_1_12::HandlePacketCraftingBookData(cByteBuffer & a_ByteBuffer)
{
// TODO not yet used, not sure if it is needed
// https://wiki.vg/index.php?title=Protocol&oldid=14204#Crafting_Book_Data
a_ByteBuffer.SkipRead(a_ByteBuffer.GetReadableSpace() - 1);
m_Client->GetPlayer()->SendMessageInfo("The green crafting book feature is not implemented yet.");
}
@@ -1170,6 +1189,7 @@ UInt32 cProtocol_1_12_1::GetPacketID(ePacketType a_Packet)
case pktRespawn: return 0x35;
case pktScoreboardObjective: return 0x42;
case pktSpawnPosition: return 0x46;
case pktUnlockRecipe: return 0x31;
case pktUpdateHealth: return 0x41;
case pktUpdateScore: return 0x45;
case pktUseBed: return 0x30;
@@ -1277,7 +1297,7 @@ bool cProtocol_1_12_1::HandlePacket(cByteBuffer & a_ByteBuffer, UInt32 a_PacketT
case 0x0f: HandlePacketPlayerLook(a_ByteBuffer); return true;
case 0x10: HandlePacketVehicleMove(a_ByteBuffer); return true;
case 0x11: HandlePacketBoatSteer(a_ByteBuffer); return true;
case 0x12: break; // Craft Recipe Request - not yet implemented
case 0x12: HandleCraftRecipe(a_ByteBuffer); return true;
case 0x13: HandlePacketPlayerAbilities(a_ByteBuffer); return true;
case 0x14: HandlePacketBlockDig(a_ByteBuffer); return true;
case 0x15: HandlePacketEntityAction(a_ByteBuffer); return true;
@@ -1397,3 +1417,55 @@ void cProtocol_1_12_2::SendKeepAlive(UInt32 a_PingID)
cPacketizer Pkt(*this, pktKeepAlive);
Pkt.WriteBEInt64(a_PingID);
}
void cProtocol_1_12_2::SendUnlockRecipe(UInt32 a_RecipeID)
{
ASSERT(m_State == 3); // In game mode?
auto ProtocolRecipeId = cRoot::Get()->GetRecipeMapper()->GetProtocolRecipeId(a_RecipeID, m_Client->GetProtocolVersion());
if (ProtocolRecipeId.has_value())
{
cPacketizer Pkt(*this, pktUnlockRecipe);
Pkt.WriteVarInt32(1);
Pkt.WriteBool(true);
Pkt.WriteBool(false);
Pkt.WriteVarInt32(1);
Pkt.WriteVarInt32(ProtocolRecipeId.value());
}
}
void cProtocol_1_12_2::SendInitRecipes(UInt32 a_RecipeID)
{
ASSERT(m_State == 3); // In game mode?
auto ProtocolRecipeId = cRoot::Get()->GetRecipeMapper()->GetProtocolRecipeId(a_RecipeID, m_Client->GetProtocolVersion());
if (!ProtocolRecipeId.has_value())
{
return;
}
cPacketizer Pkt(*this, pktUnlockRecipe);
Pkt.WriteVarInt32(0);
Pkt.WriteBool(true);
Pkt.WriteBool(false);
if (a_RecipeID == 0)
{
Pkt.WriteVarInt32(0);
Pkt.WriteVarInt32(0);
}
else
{
Pkt.WriteVarInt32(1);
Pkt.WriteVarInt32(ProtocolRecipeId.value());
Pkt.WriteVarInt32(1);
Pkt.WriteVarInt32(ProtocolRecipeId.value());
}
}