summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Server/Plugins/APIDump/APIDesc.lua107
-rw-r--r--Server/Plugins/APIDump/Hooks/OnPlayerEditedBook.lua26
-rw-r--r--Server/Plugins/APIDump/Hooks/OnPlayerEditingBook.lua26
-rw-r--r--src/Bindings/AllToLua.pkg2
-rw-r--r--src/Bindings/CMakeLists.txt1
-rw-r--r--src/Bindings/ManualBindings.cpp61
-rw-r--r--src/Bindings/Plugin.h2
-rw-r--r--src/Bindings/PluginLua.cpp20
-rw-r--r--src/Bindings/PluginLua.h2
-rw-r--r--src/Bindings/PluginManager.cpp38
-rw-r--r--src/Bindings/PluginManager.h5
-rw-r--r--src/BookContent.cpp90
-rw-r--r--src/BookContent.h64
-rw-r--r--src/CMakeLists.txt2
-rw-r--r--src/ClientHandle.cpp9
-rw-r--r--src/ClientHandle.h1
-rw-r--r--src/Item.cpp38
-rw-r--r--src/Item.h6
-rw-r--r--src/Items/CMakeLists.txt1
-rw-r--r--src/Items/ItemHandler.cpp2
-rw-r--r--src/Items/ItemWrittenBook.h30
-rw-r--r--src/Protocol/Protocol.h1
-rw-r--r--src/Protocol/ProtocolRecognizer.cpp11
-rw-r--r--src/Protocol/ProtocolRecognizer.h1
-rw-r--r--src/Protocol/Protocol_1_8.h1
-rw-r--r--src/Protocol/Protocol_1_9.cpp101
-rw-r--r--src/Protocol/Protocol_1_9.h1
-rw-r--r--src/WorldStorage/NBTChunkSerializer.cpp6
-rwxr-xr-xsrc/WorldStorage/WSSAnvil.cpp4
29 files changed, 655 insertions, 4 deletions
diff --git a/Server/Plugins/APIDump/APIDesc.lua b/Server/Plugins/APIDump/APIDesc.lua
index d7dc83043..c7d47219a 100644
--- a/Server/Plugins/APIDump/APIDesc.lua
+++ b/Server/Plugins/APIDump/APIDesc.lua
@@ -6943,7 +6943,7 @@ These ItemGrids are available in the API and can be manipulated by the plugins,
},
m_Enchantments =
{
- Type = "{{cEnchantments|cEnchantments}}}",
+ Type = "{{cEnchantments|cEnchantments}}",
Notes = "The enchantments of the item.",
},
m_ItemCount =
@@ -6971,6 +6971,11 @@ These ItemGrids are available in the API and can be manipulated by the plugins,
Type = "number",
Notes = "The repair cost of the item. The anvil need this value",
},
+ m_BookContent =
+ {
+ Type = "{{cBookContent|cBookContent}}",
+ Notes = "If it's a written or a writeable book, it contains the information of the book: Author, title and pages",
+ },
},
AdditionalInfo =
{
@@ -7014,6 +7019,106 @@ local Item5 = cItem(E_ITEM_DIAMOND_CHESTPLATE, 1, 0, "thorns=1;unbreaking=3");
},
},
},
+ cBookContent =
+ {
+ Desc = [[
+This class contains the information for a signed or writeable book: The author, title and the pages. A page of the writeable book is a simple string. For a signed book it can be a json string. For the json string use {{cCompositeChat}} to create a page and then the function {{cCompositeChat#CreateJsonString_1|CreateJsonString}} to get the json string.
+]],
+ Functions =
+ {
+ constructor =
+ {
+ Notes = "Creates a empty book",
+ },
+ SetAuthor =
+ {
+ Params =
+ {
+ {
+ Name = "Author",
+ Type = "string",
+ },
+ },
+ Notes = "Set the author of the book",
+ },
+ GetAuthor =
+ {
+ Returns =
+ {
+ {
+ Type = "string",
+ },
+ },
+ Notes = "Get the author of the book",
+ },
+ SetTitle =
+ {
+ Params =
+ {
+ {
+ Name = "Title",
+ Type = "string",
+ },
+ },
+ Notes = "Set the title of the book",
+ },
+ GetTitle =
+ {
+ Returns =
+ {
+ {
+ Type = "string",
+ },
+ },
+ Notes = "Returns the title of the book",
+ },
+ AddPage =
+ {
+ Params =
+ {
+ {
+ Name = "Page",
+ Type = "string",
+ },
+ },
+ Notes = "Add a page to the end of the book. Note: A page can be a json string",
+ },
+ GetPages =
+ {
+ Returns =
+ {
+ {
+ Type = "table",
+ },
+ },
+ Notes = "Returns the pages of the book as a table",
+ },
+ SetPages =
+ {
+ Params =
+ {
+ {
+ Type = "table",
+ },
+ },
+ Notes = "Set the pages of the book",
+ },
+ Clear =
+ {
+ Notes = "Clears the whole book",
+ },
+ IsEmpty =
+ {
+ Returns =
+ {
+ {
+ Type = "boolean",
+ },
+ },
+ Notes = "Returns true if the book has no author, title and no pages",
+ },
+ },
+ },
cItemFrame =
{
Functions =
diff --git a/Server/Plugins/APIDump/Hooks/OnPlayerEditedBook.lua b/Server/Plugins/APIDump/Hooks/OnPlayerEditedBook.lua
new file mode 100644
index 000000000..275bd129d
--- /dev/null
+++ b/Server/Plugins/APIDump/Hooks/OnPlayerEditedBook.lua
@@ -0,0 +1,26 @@
+return
+{
+ HOOK_PLAYER_EDITED_BOOK =
+ {
+ CalledWhen = "A player has edited a book.",
+ DefaultFnName = "OnPlayerEditedBook", -- also used as pagename
+ Desc = [[
+ This hook is called whenever a {{cPlayer|player}} has edited a book.
+ See also the {{OnPlayerEditingBook|HOOK_PLAYER_EDITING_BOOK}} hook for a similar hook, is called when a
+ player is editing a book.
+ ]],
+ Params =
+ {
+ { Name = "Player", Type = "{{cPlayer}}", Notes = "The player that edited the book" },
+ { Name = "BookContent", Type = "{{cBookContent}}", Notes = "The class that contains the current info of the book" },
+ },
+ Returns = [[
+ If the function returns false or no value, Cuberite calls other plugins with this event. If the
+ function returns true, no other plugin is called for this event.</p>
+ ]],
+ }, -- HOOK_PLAYER_EDITED_BOOK
+}
+
+
+
+
diff --git a/Server/Plugins/APIDump/Hooks/OnPlayerEditingBook.lua b/Server/Plugins/APIDump/Hooks/OnPlayerEditingBook.lua
new file mode 100644
index 000000000..96521c361
--- /dev/null
+++ b/Server/Plugins/APIDump/Hooks/OnPlayerEditingBook.lua
@@ -0,0 +1,26 @@
+return
+{
+ HOOK_PLAYER_EDITING_BOOK =
+ {
+ CalledWhen = "A player is editing a book.",
+ DefaultFnName = "OnPlayerEditingBook", -- also used as pagename
+ Desc = [[
+ This hook is called whenever a {{cPlayer|player}} is editing a book.
+ See also the {{OnPlayerEditingBook|HOOK_PLAYER_EDITED_BOOK}} hook for a similar hook, is called when a
+ player has edited a book.
+ ]],
+ Params =
+ {
+ { Name = "Player", Type = "{{cPlayer}}", Notes = "The player that is editing the book" },
+ { Name = "BookContent", Type = "{{cBookContent}}", Notes = "The class that contains the current info of the book" },
+ },
+ Returns = [[
+ If the function returns false or no value, Cuberite calls other plugins with this event. If the function returns true,
+ no other plugin's callback is called and the editing of the book is denied.
+ ]],
+ }, -- HOOK_PLAYER_EDITING_BOOK
+}
+
+
+
+
diff --git a/src/Bindings/AllToLua.pkg b/src/Bindings/AllToLua.pkg
index 778cdf42c..0f775ccce 100644
--- a/src/Bindings/AllToLua.pkg
+++ b/src/Bindings/AllToLua.pkg
@@ -72,6 +72,8 @@ $cfile "../Scoreboard.h"
$cfile "../Statistics.h"
$cfile "../Protocol/MojangAPI.h"
$cfile "../UUID.h"
+$cfile "../BookContent.h"
+
// Entities:
$cfile "../Entities/Entity.h"
diff --git a/src/Bindings/CMakeLists.txt b/src/Bindings/CMakeLists.txt
index 0ab21467b..7b1635a4a 100644
--- a/src/Bindings/CMakeLists.txt
+++ b/src/Bindings/CMakeLists.txt
@@ -143,6 +143,7 @@ set(BINDING_DEPENDENCIES
../Vector3.h
../WebAdmin.h
../World.h
+ ../BookContent.h
)
if (NOT MSVC)
diff --git a/src/Bindings/ManualBindings.cpp b/src/Bindings/ManualBindings.cpp
index c87e9ed20..b6ef2b275 100644
--- a/src/Bindings/ManualBindings.cpp
+++ b/src/Bindings/ManualBindings.cpp
@@ -3967,6 +3967,62 @@ static int tolua_cEntity_GetSpeed(lua_State * tolua_S)
+static int tolua_cBookContent_GetPages(lua_State * tolua_S)
+{
+ // cBookContent::GetPages() -> table of strings
+
+ cLuaState L(tolua_S);
+ if (!L.CheckParamSelf("cBookContent"))
+ {
+ return 0;
+ }
+
+ cBookContent * BookContent = reinterpret_cast<cBookContent *>(tolua_tousertype(tolua_S, 1, nullptr));
+ L.Push(BookContent->GetPages());
+ return 1;
+}
+
+
+
+
+
+static int tolua_cBookContent_SetPages(lua_State * tolua_S)
+{
+ // cBookContent::SetPages(table)
+
+ cLuaState L(tolua_S);
+ if (
+ !L.CheckParamSelf("cBookContent") ||
+ !L.CheckParamTable(2))
+ {
+ return 0;
+ }
+
+ cBookContent * BookContent = reinterpret_cast<cBookContent *>(tolua_tousertype(tolua_S, 1, nullptr));
+
+ // Convert the input table into AStringVector:
+ AStringVector Pages;
+ int NumPages = luaL_getn(L, 2);
+ Pages.reserve(static_cast<size_t>(NumPages));
+ for (int i = 1; i <= NumPages; i++)
+ {
+ lua_rawgeti(L, 2, i);
+ AString Page;
+ L.GetStackValue(-1, Page);
+ if (!Page.empty())
+ {
+ Pages.push_back(Page);
+ }
+ lua_pop(L, 1);
+ }
+ BookContent->SetPages(Pages);
+ return 0;
+}
+
+
+
+
+
void cManualBindings::Bind(lua_State * tolua_S)
{
tolua_beginmodule(tolua_S, nullptr);
@@ -4194,6 +4250,11 @@ void cManualBindings::Bind(lua_State * tolua_S)
tolua_variable(tolua_S, "PostParams", tolua_get_HTTPRequest_PostParams, nullptr);
tolua_endmodule(tolua_S);
+ tolua_beginmodule(tolua_S, "cBookContent");
+ tolua_function(tolua_S, "GetPages", tolua_cBookContent_GetPages);
+ tolua_function(tolua_S, "SetPages", tolua_cBookContent_SetPages);
+ tolua_endmodule(tolua_S);
+
BindNetwork(tolua_S);
BindRankManager(tolua_S);
BindWorld(tolua_S);
diff --git a/src/Bindings/Plugin.h b/src/Bindings/Plugin.h
index 22e8f15e2..f7de4768f 100644
--- a/src/Bindings/Plugin.h
+++ b/src/Bindings/Plugin.h
@@ -85,6 +85,8 @@ public:
virtual bool OnPlayerPlacingBlock (cPlayer & a_Player, const sSetBlock & a_BlockChange) = 0;
virtual bool OnPlayerRightClick (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) = 0;
virtual bool OnPlayerRightClickingEntity(cPlayer & a_Player, cEntity & a_Entity) = 0;
+ virtual bool OnPlayerEditedBook (cPlayer & a_Player, cBookContent & a_BookContent, bool a_IsSigned) = 0;
+ virtual bool OnPlayerEditingBook (cPlayer & a_Player, cBookContent & a_BookContent, bool a_IsSigned) = 0;
virtual bool OnPlayerShooting (cPlayer & a_Player) = 0;
virtual bool OnPlayerSpawned (cPlayer & a_Player) = 0;
virtual bool OnPlayerTossingItem (cPlayer & a_Player) = 0;
diff --git a/src/Bindings/PluginLua.cpp b/src/Bindings/PluginLua.cpp
index 5af336a95..1d8024334 100644
--- a/src/Bindings/PluginLua.cpp
+++ b/src/Bindings/PluginLua.cpp
@@ -712,6 +712,24 @@ bool cPluginLua::OnPlayerRightClickingEntity(cPlayer & a_Player, cEntity & a_Ent
+bool cPluginLua::OnPlayerEditedBook(cPlayer & a_Player, cBookContent & a_BookContent, bool a_IsSigned)
+{
+ return CallSimpleHooks(cPluginManager::HOOK_PLAYER_EDITED_BOOK, &a_Player, &a_BookContent, a_IsSigned);
+}
+
+
+
+
+
+bool cPluginLua::OnPlayerEditingBook(cPlayer & a_Player, cBookContent & a_BookContent, bool a_IsSigned)
+{
+ return CallSimpleHooks(cPluginManager::HOOK_PLAYER_EDITING_BOOK, &a_Player, &a_BookContent, a_IsSigned);
+}
+
+
+
+
+
bool cPluginLua::OnPlayerShooting(cPlayer & a_Player)
{
return CallSimpleHooks(cPluginManager::HOOK_PLAYER_SHOOTING, &a_Player);
@@ -1070,6 +1088,8 @@ const char * cPluginLua::GetHookFnName(int a_HookType)
case cPluginManager::HOOK_PLAYER_PLACING_BLOCK: return "OnPlayerPlacingBlock";
case cPluginManager::HOOK_PLAYER_RIGHT_CLICK: return "OnPlayerRightClick";
case cPluginManager::HOOK_PLAYER_RIGHT_CLICKING_ENTITY: return "OnPlayerRightClickingEntity";
+ case cPluginManager::HOOK_PLAYER_EDITED_BOOK: return "OnPlayerEditedBook";
+ case cPluginManager::HOOK_PLAYER_EDITING_BOOK: return "OnPlayerSigningdBook";
case cPluginManager::HOOK_PLAYER_SHOOTING: return "OnPlayerShooting";
case cPluginManager::HOOK_PLAYER_SPAWNED: return "OnPlayerSpawned";
case cPluginManager::HOOK_PLAYER_TOSSING_ITEM: return "OnPlayerTossingItem";
diff --git a/src/Bindings/PluginLua.h b/src/Bindings/PluginLua.h
index 4de5751e7..82b78cbe0 100644
--- a/src/Bindings/PluginLua.h
+++ b/src/Bindings/PluginLua.h
@@ -106,6 +106,8 @@ public:
virtual bool OnPlayerPlacingBlock (cPlayer & a_Player, const sSetBlock & a_BlockChange) override;
virtual bool OnPlayerRightClick (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override;
virtual bool OnPlayerRightClickingEntity(cPlayer & a_Player, cEntity & a_Entity) override;
+ virtual bool OnPlayerEditedBook (cPlayer & a_Player, cBookContent & a_BookContent, bool a_IsSigned) override;
+ virtual bool OnPlayerEditingBook (cPlayer & a_Player, cBookContent & a_BookContent, bool a_IsSigned) override;
virtual bool OnPlayerShooting (cPlayer & a_Player) override;
virtual bool OnPlayerSpawned (cPlayer & a_Player) override;
virtual bool OnEntityTeleport (cEntity & a_Entity, const Vector3d & a_OldPosition, const Vector3d & a_NewPosition) override;
diff --git a/src/Bindings/PluginManager.cpp b/src/Bindings/PluginManager.cpp
index 1d977fcde..125a6bfc8 100644
--- a/src/Bindings/PluginManager.cpp
+++ b/src/Bindings/PluginManager.cpp
@@ -1094,6 +1094,44 @@ bool cPluginManager::CallHookPlayerRightClickingEntity(cPlayer & a_Player, cEnti
+bool cPluginManager::CallHookPlayerEditedBook(cPlayer & a_Player, cBookContent & a_BookContent, bool a_IsSigned)
+{
+ FIND_HOOK(HOOK_PLAYER_EDITED_BOOK);
+ VERIFY_HOOK;
+
+ for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
+ {
+ if ((*itr)->OnPlayerEditedBook(a_Player, a_BookContent, a_IsSigned))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginManager::CallHookPlayerEditingBook(cPlayer & a_Player, cBookContent & a_BookContent, bool a_IsSigned)
+{
+ FIND_HOOK(HOOK_PLAYER_EDITING_BOOK);
+ VERIFY_HOOK;
+
+ for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
+ {
+ if ((*itr)->OnPlayerEditingBook(a_Player, a_BookContent, a_IsSigned))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
bool cPluginManager::CallHookPlayerShooting(cPlayer & a_Player)
{
FIND_HOOK(HOOK_PLAYER_SHOOTING);
diff --git a/src/Bindings/PluginManager.h b/src/Bindings/PluginManager.h
index f3fc3551a..204a70e67 100644
--- a/src/Bindings/PluginManager.h
+++ b/src/Bindings/PluginManager.h
@@ -29,6 +29,7 @@ class cWorld;
class cSettingsRepositoryInterface;
class cDeadlockDetect;
struct TakeDamageInfo;
+class cBookContent;
typedef std::shared_ptr<cPlugin> cPluginPtr;
typedef std::vector<cPluginPtr> cPluginPtrs;
@@ -117,6 +118,8 @@ public:
HOOK_PLAYER_PLACING_BLOCK,
HOOK_PLAYER_RIGHT_CLICK,
HOOK_PLAYER_RIGHT_CLICKING_ENTITY,
+ HOOK_PLAYER_EDITING_BOOK,
+ HOOK_PLAYER_EDITED_BOOK,
HOOK_PLAYER_SHOOTING,
HOOK_PLAYER_SPAWNED,
HOOK_ENTITY_TELEPORT,
@@ -264,6 +267,8 @@ public:
bool CallHookPlayerPlacingBlock (cPlayer & a_Player, const sSetBlock & a_BlockChange);
bool CallHookPlayerRightClick (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ);
bool CallHookPlayerRightClickingEntity(cPlayer & a_Player, cEntity & a_Entity);
+ bool CallHookPlayerEditedBook (cPlayer & a_Player, cBookContent & a_BookContent, bool a_IsSigned);
+ bool CallHookPlayerEditingBook (cPlayer & a_Player, cBookContent & a_BookContent, bool a_IsSigned);
bool CallHookPlayerShooting (cPlayer & a_Player);
bool CallHookPlayerSpawned (cPlayer & a_Player);
bool CallHookPlayerTossingItem (cPlayer & a_Player);
diff --git a/src/BookContent.cpp b/src/BookContent.cpp
new file mode 100644
index 000000000..e30401c20
--- /dev/null
+++ b/src/BookContent.cpp
@@ -0,0 +1,90 @@
+
+// BookContent.cpp
+
+#include "Globals.h"
+#include "BookContent.h"
+
+
+
+
+
+void cBookContent::Clear()
+{
+ m_Author.clear();
+ m_Title.clear();
+ m_Pages.clear();
+}
+
+
+
+
+
+bool cBookContent::IsEmpty(void) const
+{
+ return (
+ m_Author.empty() &&
+ m_Title.empty() &&
+ m_Pages.empty()
+ );
+}
+
+
+
+
+
+void cBookContent::ParseFromNBT(int TagTag, cBookContent & a_BookContent, const cParsedNBT & a_NBT, bool a_SaveAsJson)
+{
+ int AuthorTag = a_NBT.FindChildByName(TagTag, "author");
+ if ((AuthorTag > 0) && (a_NBT.GetType(AuthorTag) == TAG_String))
+ {
+ a_BookContent.SetAuthor(a_NBT.GetString(AuthorTag));
+ }
+
+ int TitleTag = a_NBT.FindChildByName(TagTag, "title");
+ if ((TitleTag > 0) && (a_NBT.GetType(TitleTag) == TAG_String))
+ {
+ a_BookContent.SetTitle(a_NBT.GetString(TitleTag));
+ }
+
+ int PagesTag = a_NBT.FindChildByName(TagTag, "pages");
+ if ((PagesTag > 0) && (a_NBT.GetType(PagesTag) == TAG_List))
+ {
+ for (int PageTag = a_NBT.GetFirstChild(PagesTag); PageTag >= 0; PageTag = a_NBT.GetNextSibling(PageTag))
+ {
+ if (a_NBT.GetType(PageTag) == TAG_String)
+ {
+ if (a_SaveAsJson)
+ {
+ Json::Value Page;
+ Page["text"] = a_NBT.GetString(PageTag);
+ a_BookContent.AddPage(Page.toStyledString());
+ }
+ else
+ {
+ a_BookContent.AddPage(a_NBT.GetString(PageTag));
+ }
+ }
+ }
+ }
+}
+
+
+
+
+
+void cBookContent::WriteToNBTCompound(const cBookContent & a_BookContent, cFastNBTWriter & a_Writer)
+{
+ if (a_BookContent.IsEmpty())
+ {
+ return;
+ }
+
+ a_Writer.AddString("author", a_BookContent.GetAuthor());
+ a_Writer.AddString("title", a_BookContent.GetTitle());
+ a_Writer.BeginList("pages", TAG_String);
+ for (const auto & Page : a_BookContent.GetPages())
+ {
+ a_Writer.AddString("", Page);
+ }
+ a_Writer.EndList();
+}
diff --git a/src/BookContent.h b/src/BookContent.h
new file mode 100644
index 000000000..042ebff50
--- /dev/null
+++ b/src/BookContent.h
@@ -0,0 +1,64 @@
+
+// BookContent.h
+
+#pragma once
+
+#include "WorldStorage/FastNBT.h"
+#include "json/json.h"
+
+
+
+
+// tolua_begin
+class cBookContent
+{
+public:
+ /** Creates a empty book */
+ cBookContent() {}
+
+ /** Set the author of the book */
+ void SetAuthor(const AString & a_Author) { m_Author = a_Author; }
+
+ /** Returns the author of the book */
+ const AString GetAuthor(void) const { return m_Author; }
+
+ /** Set the title of the book */
+ void SetTitle(const AString & a_Title) { m_Title = a_Title; }
+
+ /** Returns the title of the book */
+ const AString GetTitle(void) const { return m_Title; }
+
+ /** Add a page to the end of the book */
+ void AddPage(const AString & a_Page) { m_Pages.emplace_back(a_Page); }
+
+ /** Clears the whole book */
+ void Clear();
+
+ /** Returns true if the book has no author, no title and no pages */
+ bool IsEmpty(void) const;
+
+ // tolua_end
+
+ /** Returns a AStringVector ref to the pages. Used in ManualBindings and for saving the book */
+ const AStringVector & GetPages(void) const { return m_Pages; }
+
+ /** Set the pages. Used in ManualBindings */
+ void SetPages(const AStringVector & a_Pages) { m_Pages = a_Pages; }
+
+ /** Read the book content from nbt. The boolean a_SaveAsJson is optional. If a player creates a book, the text should be in a json string */
+ static void ParseFromNBT(int TagTag, cBookContent & a_BookContent, const cParsedNBT & a_NBT, bool a_SaveAsJson = false);
+
+ /** Write book content to nbt */
+ static void WriteToNBTCompound(const cBookContent & a_BookContent, cFastNBTWriter & a_Writer);
+
+private:
+ /** Author of the book */
+ AString m_Author;
+
+ /** Title of the book */
+ AString m_Title;
+
+ /** Contains the pages */
+ AStringVector m_Pages;
+
+}; // tolua_export
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index d7eb4e903..a71da1363 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -73,6 +73,7 @@ SET (SRCS
VoronoiMap.cpp
WebAdmin.cpp
World.cpp
+ BookContent.cpp
main.cpp
)
@@ -153,6 +154,7 @@ SET (HDRS
VoronoiMap.h
WebAdmin.h
World.h
+ BookContent.h
XMLParser.h
)
diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp
index caa2d8fd8..9dbcc684b 100644
--- a/src/ClientHandle.cpp
+++ b/src/ClientHandle.cpp
@@ -2295,6 +2295,15 @@ void cClientHandle::SendBlockChanges(int a_ChunkX, int a_ChunkZ, const sSetBlock
+void cClientHandle::SendOpenBook(const short a_Hand)
+{
+ m_Protocol->SendOpenBook(a_Hand);
+}
+
+
+
+
+
void cClientHandle::SendCameraSetTo(const cEntity & a_Entity)
{
m_Protocol->SendCameraSetTo(a_Entity);
diff --git a/src/ClientHandle.h b/src/ClientHandle.h
index 09188f2ae..adf6bd683 100644
--- a/src/ClientHandle.h
+++ b/src/ClientHandle.h
@@ -144,6 +144,7 @@ public: // tolua_export
void SendBlockBreakAnim (UInt32 a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage);
void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); // tolua_export
void SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes);
+ void SendOpenBook (const short a_Hand);
void SendCameraSetTo (const cEntity & a_Entity);
void SendChat (const AString & a_Message, eMessageType a_ChatPrefix, const AString & a_AdditionalData = "");
void SendChat (const cCompositeChat & a_Message);
diff --git a/src/Item.cpp b/src/Item.cpp
index 9b17f1c37..412c66ec3 100644
--- a/src/Item.cpp
+++ b/src/Item.cpp
@@ -183,6 +183,31 @@ void cItem::GetJson(Json::Value & a_OutValue) const
a_OutValue["FadeColours"] = m_FireworkItem.FadeColoursToString(m_FireworkItem);
}
+ if (
+ !m_BookContent.IsEmpty() &&
+ (
+ (m_ItemType == E_ITEM_WRITTEN_BOOK) || (m_ItemType == E_ITEM_BOOK_AND_QUILL)
+ )
+ )
+ {
+ if (!m_BookContent.GetAuthor().empty())
+ {
+ a_OutValue["author"] = m_BookContent.GetAuthor();
+ }
+ if (!m_BookContent.GetTitle().empty())
+ {
+ a_OutValue["title"] = m_BookContent.GetTitle();
+ }
+ if (!m_BookContent.GetPages().empty())
+ {
+ a_OutValue["pages"] = Json::Value(Json::arrayValue);
+ for (const auto & Page : m_BookContent.GetPages())
+ {
+ a_OutValue["pages"].append(Page);
+ }
+ }
+ }
+
a_OutValue["RepairCost"] = m_RepairCost;
}
}
@@ -229,6 +254,19 @@ void cItem::FromJson(const Json::Value & a_Value)
m_FireworkItem.FadeColoursFromString(a_Value.get("FadeColours", "").asString(), m_FireworkItem);
}
+ if ((m_ItemType == E_ITEM_WRITTEN_BOOK) || (m_ItemType == E_ITEM_BOOK_AND_QUILL))
+ {
+ m_BookContent.SetAuthor(a_Value.get("author", "").asString());
+ m_BookContent.SetTitle(a_Value.get("title", "").asString());
+ if (a_Value.isMember("pages"))
+ {
+ for (Json::Value::ArrayIndex i = 0; i != a_Value["pages"].size(); i++)
+ {
+ m_BookContent.AddPage(a_Value["pages"][i].asString());
+ }
+ }
+ }
+
m_RepairCost = a_Value.get("RepairCost", 0).asInt();
}
}
diff --git a/src/Item.h b/src/Item.h
index 493061d93..664d63195 100644
--- a/src/Item.h
+++ b/src/Item.h
@@ -13,7 +13,7 @@
#include "Enchantments.h"
#include "WorldStorage/FireworksSerializer.h"
#include "Color.h"
-
+#include "BookContent.h"
@@ -109,6 +109,7 @@ public:
m_RepairCost = 0;
m_FireworkItem.EmptyData();
m_ItemColor.Clear();
+ m_BookContent.Clear();
}
@@ -119,6 +120,8 @@ public:
m_ItemDamage = 0;
m_RepairCost = 0;
m_ItemColor.Clear();
+ m_BookContent.Clear();
+ m_Enchantments.Clear();
}
@@ -230,6 +233,7 @@ public:
int m_RepairCost;
cFireworkItem m_FireworkItem;
cColor m_ItemColor;
+ cBookContent m_BookContent;
};
// tolua_end
diff --git a/src/Items/CMakeLists.txt b/src/Items/CMakeLists.txt
index 72858591a..5ab12feb2 100644
--- a/src/Items/CMakeLists.txt
+++ b/src/Items/CMakeLists.txt
@@ -56,6 +56,7 @@ SET (HDRS
ItemSword.h
ItemThrowable.h
ItemAxe.h
+ ItemWrittenBook.h
)
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
diff --git a/src/Items/ItemHandler.cpp b/src/Items/ItemHandler.cpp
index e0c5bb56c..234c0dbf6 100644
--- a/src/Items/ItemHandler.cpp
+++ b/src/Items/ItemHandler.cpp
@@ -56,6 +56,7 @@
#include "ItemSword.h"
#include "ItemThrowable.h"
#include "ItemAxe.h"
+#include "ItemWrittenBook.h"
#include "../Blocks/BlockHandler.h"
@@ -153,6 +154,7 @@ cItemHandler * cItemHandler::CreateItemHandler(int a_ItemType)
case E_ITEM_SPAWN_EGG: return new cItemSpawnEggHandler(a_ItemType);
case E_ITEM_STRING: return new cItemStringHandler(a_ItemType);
case E_ITEM_SUGARCANE: return new cItemSugarcaneHandler(a_ItemType);
+ case E_ITEM_WRITTEN_BOOK: return new cItemWrittenBookHandler(a_ItemType);
case E_ITEM_WOODEN_HOE:
case E_ITEM_STONE_HOE:
diff --git a/src/Items/ItemWrittenBook.h b/src/Items/ItemWrittenBook.h
new file mode 100644
index 000000000..37f1254c5
--- /dev/null
+++ b/src/Items/ItemWrittenBook.h
@@ -0,0 +1,30 @@
+
+// ItemWrittenBook.h
+
+#pragma once
+
+#include "ItemHandler.h"
+#include "../Entities/Player.h"
+#include "../Inventory.h"
+
+
+
+class cItemWrittenBookHandler :
+ public cItemHandler
+{
+public:
+ cItemWrittenBookHandler(int a_ItemType)
+ : cItemHandler(a_ItemType)
+ {
+ }
+
+ virtual bool OnItemUse(
+ cWorld * a_World, cPlayer * a_Player, cBlockPluginInterface & a_PluginInterface, const cItem & a_Item,
+ int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace
+ ) override
+ {
+ // TODO: Currently only main haind. If second hand is implemented, fix this
+ a_Player->GetClientHandle()->SendOpenBook(0);
+ return true;
+ }
+} ;
diff --git a/src/Protocol/Protocol.h b/src/Protocol/Protocol.h
index 54c5b7223..9d9497163 100644
--- a/src/Protocol/Protocol.h
+++ b/src/Protocol/Protocol.h
@@ -65,6 +65,7 @@ public:
virtual void SendBlockBreakAnim (UInt32 a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage) = 0;
virtual void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) = 0;
virtual void SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) = 0;
+ virtual void SendOpenBook (const short a_Hand) = 0;
virtual void SendCameraSetTo (const cEntity & a_Entity) = 0;
virtual void SendChat (const AString & a_Message, eChatType a_Type) = 0;
virtual void SendChat (const cCompositeChat & a_Message, eChatType a_Type, bool a_ShouldUseChatPrefixes) = 0;
diff --git a/src/Protocol/ProtocolRecognizer.cpp b/src/Protocol/ProtocolRecognizer.cpp
index cb2a4fc9d..06e942716 100644
--- a/src/Protocol/ProtocolRecognizer.cpp
+++ b/src/Protocol/ProtocolRecognizer.cpp
@@ -191,6 +191,17 @@ void cProtocolRecognizer::SendBlockChanges(int a_ChunkX, int a_ChunkZ, const sSe
+
+void cProtocolRecognizer::SendOpenBook(const short a_Hand)
+{
+ ASSERT(m_Protocol != nullptr);
+ m_Protocol->SendOpenBook(a_Hand);
+}
+
+
+
+
+
void cProtocolRecognizer::SendCameraSetTo(const cEntity & a_Entity)
{
ASSERT(m_Protocol != nullptr);
diff --git a/src/Protocol/ProtocolRecognizer.h b/src/Protocol/ProtocolRecognizer.h
index 295c6db16..544914cf3 100644
--- a/src/Protocol/ProtocolRecognizer.h
+++ b/src/Protocol/ProtocolRecognizer.h
@@ -61,6 +61,7 @@ public:
virtual void SendBlockBreakAnim (UInt32 a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage) override;
virtual void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override;
virtual void SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) override;
+ virtual void SendOpenBook (const short a_Hand) override;
virtual void SendCameraSetTo (const cEntity & a_Entity) override;
virtual void SendChat (const AString & a_Message, eChatType a_Type) override;
virtual void SendChat (const cCompositeChat & a_Message, eChatType a_Type, bool a_ShouldUseChatPrefixes) override;
diff --git a/src/Protocol/Protocol_1_8.h b/src/Protocol/Protocol_1_8.h
index b04e5c5f0..a76dbd8d8 100644
--- a/src/Protocol/Protocol_1_8.h
+++ b/src/Protocol/Protocol_1_8.h
@@ -54,6 +54,7 @@ public:
virtual void SendBlockBreakAnim (UInt32 a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage) override;
virtual void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override;
virtual void SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) override;
+ virtual void SendOpenBook (const short a_Hand) override {}
virtual void SendCameraSetTo (const cEntity & a_Entity) override;
virtual void SendChat (const AString & a_Message, eChatType a_Type) override;
virtual void SendChat (const cCompositeChat & a_Message, eChatType a_Type, bool a_ShouldUseChatPrefixes) override;
diff --git a/src/Protocol/Protocol_1_9.cpp b/src/Protocol/Protocol_1_9.cpp
index 7fc6cf5f1..f49a942b8 100644
--- a/src/Protocol/Protocol_1_9.cpp
+++ b/src/Protocol/Protocol_1_9.cpp
@@ -263,6 +263,17 @@ void cProtocol_1_9_0::SendBlockChanges(int a_ChunkX, int a_ChunkZ, const sSetBlo
+void cProtocol_1_9_0::SendOpenBook(const short a_Hand)
+{
+ cPacketizer Pkt(*this, 0x18); // Plugin Channel
+ Pkt.WriteString("MC|BOpen");
+ Pkt.WriteVarInt32(static_cast<UInt32>(a_Hand));
+}
+
+
+
+
+
void cProtocol_1_9_0::SendCameraSetTo(const cEntity & a_Entity)
{
cPacketizer Pkt(*this, 0x36); // Camera Packet (Attach the camera of a player at another entity in spectator mode)
@@ -2917,6 +2928,55 @@ void cProtocol_1_9_0::HandleVanillaPluginMessage(cByteBuffer & a_ByteBuffer, con
m_Client->HandleNPCTrade(SlotNum);
return;
}
+ else if ((a_Channel == "MC|BSign") || (a_Channel == "MC|BEdit"))
+ {
+ HANDLE_READ(a_ByteBuffer, ReadBEInt16, Int16, ItemID);
+ if (ItemID != E_ITEM_BOOK_AND_QUILL)
+ {
+ // Item is not a writeable book
+ return;
+ }
+
+ // Skip item count (1 byte) and item damage (2 bytes)
+ a_ByteBuffer.SkipRead(3);
+
+ // Read nbt content
+ AString BookData;
+ a_ByteBuffer.ReadString(BookData, a_ByteBuffer.GetReadableSpace() - 1);
+ cParsedNBT NBT(BookData.c_str(), BookData.size());
+
+ cItem BookItem;
+ if (a_Channel == "MC|BSign")
+ {
+ BookItem = cItem(E_ITEM_WRITTEN_BOOK);
+ // Add the text to a json string
+ cBookContent::ParseFromNBT(0, BookItem.m_BookContent, NBT, true);
+ }
+ else
+ {
+ BookItem = cItem(E_ITEM_BOOK_AND_QUILL);
+ cBookContent::ParseFromNBT(0, BookItem.m_BookContent, NBT);
+ }
+
+ cPlayer * Player = m_Client->GetPlayer();
+
+ // If true, player has clicked on the sign button
+ bool IsSigned = (BookItem.m_ItemType == E_ITEM_WRITTEN_BOOK) ? true : false;
+
+ if (cRoot::Get()->GetPluginManager()->CallHookPlayerEditingBook(*Player, BookItem.m_BookContent, IsSigned))
+ {
+ // Plugin denied the editing of the book
+ return;
+ }
+
+ // Book has been edited, inform plugins
+ cRoot::Get()->GetPluginManager()->CallHookPlayerEditedBook(*Player, BookItem.m_BookContent, IsSigned);
+
+ cInventory & inv = Player->GetInventory();
+ inv.SetHotbarSlot(inv.GetEquippedSlotNum(), BookItem);
+ SendWholeInventory(*Player->GetWindow()); // TODO: Use SendSlot
+ return;
+ }
LOG("Unhandled vanilla plugin channel: \"%s\".", a_Channel.c_str());
// Read the payload and send it through to the clienthandle:
@@ -3334,12 +3394,26 @@ void cProtocol_1_9_0::WriteItem(cPacketizer & a_Pkt, const cItem & a_Item)
a_Pkt.WriteBEInt16(a_Item.m_ItemDamage);
}
- if (a_Item.m_Enchantments.IsEmpty() && a_Item.IsBothNameAndLoreEmpty() && (ItemType != E_ITEM_FIREWORK_ROCKET) && (ItemType != E_ITEM_FIREWORK_STAR) && !a_Item.m_ItemColor.IsValid() && (ItemType != E_ITEM_POTION) && (ItemType != E_ITEM_SPAWN_EGG))
+ if (
+ a_Item.m_Enchantments.IsEmpty() &&
+ a_Item.IsBothNameAndLoreEmpty() &&
+ (ItemType != E_ITEM_FIREWORK_ROCKET) && (ItemType != E_ITEM_FIREWORK_STAR) &&
+ !a_Item.m_ItemColor.IsValid() &&
+ (ItemType != E_ITEM_POTION) &&
+ (ItemType != E_ITEM_SPAWN_EGG) &&
+ (ItemType != E_ITEM_WRITTEN_BOOK) && (ItemType != E_ITEM_BOOK_AND_QUILL))
{
a_Pkt.WriteBEInt8(0);
return;
}
+ if ((ItemType == E_ITEM_BOOK_AND_QUILL) && (a_Item.m_BookContent.GetPages().size() == 0))
+ {
+ // Don't send any nbt tag if the book is writeable and has no pages
+ // If a tag with a empty pages list is send, the player can't enter anything
+ a_Pkt.WriteBEInt8(0);
+ return;
+ }
// Send the enchantments and custom names:
cFastNBTWriter Writer;
@@ -3450,6 +3524,31 @@ void cProtocol_1_9_0::WriteItem(cPacketizer & a_Pkt, const cItem & a_Item)
Writer.EndCompound();
}
}
+ if ((a_Item.m_ItemType == E_ITEM_WRITTEN_BOOK) || (a_Item.m_ItemType == E_ITEM_BOOK_AND_QUILL))
+ {
+ if (a_Item.m_ItemType == E_ITEM_WRITTEN_BOOK)
+ {
+ // Only send author and title for a signed book
+ Writer.AddString("author", a_Item.m_BookContent.GetAuthor());
+ Writer.AddString("title", a_Item.m_BookContent.GetTitle());
+ }
+ if (a_Item.m_BookContent.GetPages().size() > 0)
+ {
+ Writer.BeginList("pages", TAG_String);
+ for (auto & Page : a_Item.m_BookContent.GetPages())
+ {
+ Writer.AddString("", Page);
+ }
+ Writer.EndList();
+ }
+ else
+ {
+ // A signed book, has a empty page
+ Writer.BeginList("pages", TAG_String);
+ Writer.AddString("", "");
+ Writer.EndList();
+ }
+ }
Writer.Finish();
diff --git a/src/Protocol/Protocol_1_9.h b/src/Protocol/Protocol_1_9.h
index b4fdc7f67..4b941726c 100644
--- a/src/Protocol/Protocol_1_9.h
+++ b/src/Protocol/Protocol_1_9.h
@@ -60,6 +60,7 @@ public:
virtual void SendBlockBreakAnim (UInt32 a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage) override;
virtual void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override;
virtual void SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) override;
+ virtual void SendOpenBook (const short a_Hand) override;
virtual void SendCameraSetTo (const cEntity & a_Entity) override;
virtual void SendChat (const AString & a_Message, eChatType a_Type) override;
virtual void SendChat (const cCompositeChat & a_Message, eChatType a_Type, bool a_ShouldUseChatPrefixes) override;
diff --git a/src/WorldStorage/NBTChunkSerializer.cpp b/src/WorldStorage/NBTChunkSerializer.cpp
index 1e8543648..d2356b71b 100644
--- a/src/WorldStorage/NBTChunkSerializer.cpp
+++ b/src/WorldStorage/NBTChunkSerializer.cpp
@@ -106,7 +106,7 @@ void cNBTChunkSerializer::AddItem(const cItem & a_Item, int a_Slot, const AStrin
// Write the tag compound (for enchantment, firework, custom name and repair cost):
if (
(!a_Item.m_Enchantments.IsEmpty()) ||
- ((a_Item.m_ItemType == E_ITEM_FIREWORK_ROCKET) || (a_Item.m_ItemType == E_ITEM_FIREWORK_STAR)) ||
+ ((a_Item.m_ItemType == E_ITEM_FIREWORK_ROCKET) || (a_Item.m_ItemType == E_ITEM_FIREWORK_STAR) || (a_Item.m_ItemType == E_ITEM_WRITTEN_BOOK)) || (a_Item.m_ItemType == E_ITEM_BOOK_AND_QUILL) ||
(a_Item.m_RepairCost > 0) ||
(a_Item.m_CustomName != "") ||
(!a_Item.m_LoreTable.empty())
@@ -149,6 +149,10 @@ void cNBTChunkSerializer::AddItem(const cItem & a_Item, int a_Slot, const AStrin
const char * TagName = (a_Item.m_ItemType == E_ITEM_BOOK) ? "StoredEnchantments" : "ench";
EnchantmentSerializer::WriteToNBTCompound(a_Item.m_Enchantments, m_Writer, TagName);
}
+ if ((a_Item.m_ItemType == E_ITEM_WRITTEN_BOOK) || (a_Item.m_ItemType == E_ITEM_BOOK_AND_QUILL))
+ {
+ cBookContent::WriteToNBTCompound(a_Item.m_BookContent, m_Writer);
+ }
m_Writer.EndCompound();
}
diff --git a/src/WorldStorage/WSSAnvil.cpp b/src/WorldStorage/WSSAnvil.cpp
index a3251481f..83f5a310d 100755
--- a/src/WorldStorage/WSSAnvil.cpp
+++ b/src/WorldStorage/WSSAnvil.cpp
@@ -837,6 +837,10 @@ bool cWSSAnvil::LoadItemFromNBT(cItem & a_Item, const cParsedNBT & a_NBT, int a_
cFireworkItem::ParseFromNBT(a_Item.m_FireworkItem, a_NBT, FireworksTag, static_cast<ENUM_ITEM_ID>(a_Item.m_ItemType));
}
+ if ((a_Item.m_ItemType == E_ITEM_WRITTEN_BOOK) || (a_Item.m_ItemType == E_ITEM_BOOK_AND_QUILL))
+ {
+ cBookContent::ParseFromNBT(TagTag, a_Item.m_BookContent, a_NBT);
+ }
return true;
}