summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CONTRIBUTORS1
-rw-r--r--src/Bindings/ManualBindings.cpp22
-rw-r--r--src/BlockEntities/BlockEntity.cpp24
-rw-r--r--src/BlockEntities/CommandBlockEntity.cpp227
-rw-r--r--src/BlockEntities/CommandBlockEntity.h94
-rw-r--r--src/Chunk.cpp34
-rw-r--r--src/Chunk.h16
-rw-r--r--src/ChunkMap.cpp17
-rw-r--r--src/ChunkMap.h23
-rw-r--r--src/ClientHandle.cpp64
-rw-r--r--src/ClientHandle.h3
-rw-r--r--src/Server.cpp2
-rw-r--r--src/Simulator/RedstoneSimulator.cpp27
-rw-r--r--src/Simulator/RedstoneSimulator.h4
-rw-r--r--src/World.cpp9
-rw-r--r--src/World.h16
-rw-r--r--src/WorldStorage/NBTChunkSerializer.cpp37
-rw-r--r--src/WorldStorage/NBTChunkSerializer.h2
-rw-r--r--src/WorldStorage/WSSAnvil.cpp42
-rw-r--r--src/WorldStorage/WSSAnvil.h17
20 files changed, 619 insertions, 62 deletions
diff --git a/CONTRIBUTORS b/CONTRIBUTORS
index 150bcf09e..b15a36a38 100644
--- a/CONTRIBUTORS
+++ b/CONTRIBUTORS
@@ -20,5 +20,6 @@ worktycho
Sxw1212
tonibm19
Diusrex
+structinf (xdot)
Please add yourself to this list if you contribute to MCServer.
diff --git a/src/Bindings/ManualBindings.cpp b/src/Bindings/ManualBindings.cpp
index ebee2d697..3ebe7b294 100644
--- a/src/Bindings/ManualBindings.cpp
+++ b/src/Bindings/ManualBindings.cpp
@@ -14,6 +14,7 @@
#include "../WebAdmin.h"
#include "../ClientHandle.h"
#include "../BlockEntities/ChestEntity.h"
+#include "../BlockEntities/CommandBlockEntity.h"
#include "../BlockEntities/DispenserEntity.h"
#include "../BlockEntities/DropperEntity.h"
#include "../BlockEntities/FurnaceEntity.h"
@@ -2265,16 +2266,17 @@ void ManualBindings::Bind(lua_State * tolua_S)
tolua_endmodule(tolua_S);
tolua_beginmodule(tolua_S, "cWorld");
- tolua_function(tolua_S, "DoWithBlockEntityAt", tolua_DoWithXYZ<cWorld, cBlockEntity, &cWorld::DoWithBlockEntityAt>);
- tolua_function(tolua_S, "DoWithChestAt", tolua_DoWithXYZ<cWorld, cChestEntity, &cWorld::DoWithChestAt>);
- tolua_function(tolua_S, "DoWithDispenserAt", tolua_DoWithXYZ<cWorld, cDispenserEntity, &cWorld::DoWithDispenserAt>);
- tolua_function(tolua_S, "DoWithDropSpenserAt", tolua_DoWithXYZ<cWorld, cDropSpenserEntity, &cWorld::DoWithDropSpenserAt>);
- tolua_function(tolua_S, "DoWithDropperAt", tolua_DoWithXYZ<cWorld, cDropperEntity, &cWorld::DoWithDropperAt>);
- tolua_function(tolua_S, "DoWithEntityByID", tolua_DoWithID< cWorld, cEntity, &cWorld::DoWithEntityByID>);
- tolua_function(tolua_S, "DoWithFurnaceAt", tolua_DoWithXYZ<cWorld, cFurnaceEntity, &cWorld::DoWithFurnaceAt>);
- tolua_function(tolua_S, "DoWithNoteBlockAt", tolua_DoWithXYZ<cWorld, cNoteEntity, &cWorld::DoWithNoteBlockAt>);
- tolua_function(tolua_S, "DoWithPlayer", tolua_DoWith< cWorld, cPlayer, &cWorld::DoWithPlayer>);
- tolua_function(tolua_S, "FindAndDoWithPlayer", tolua_DoWith< cWorld, cPlayer, &cWorld::FindAndDoWithPlayer>);
+ tolua_function(tolua_S, "DoWithBlockEntityAt", tolua_DoWithXYZ<cWorld, cBlockEntity, &cWorld::DoWithBlockEntityAt>);
+ tolua_function(tolua_S, "DoWithChestAt", tolua_DoWithXYZ<cWorld, cChestEntity, &cWorld::DoWithChestAt>);
+ tolua_function(tolua_S, "DoWithDispenserAt", tolua_DoWithXYZ<cWorld, cDispenserEntity, &cWorld::DoWithDispenserAt>);
+ tolua_function(tolua_S, "DoWithDropSpenserAt", tolua_DoWithXYZ<cWorld, cDropSpenserEntity, &cWorld::DoWithDropSpenserAt>);
+ tolua_function(tolua_S, "DoWithDropperAt", tolua_DoWithXYZ<cWorld, cDropperEntity, &cWorld::DoWithDropperAt>);
+ tolua_function(tolua_S, "DoWithEntityByID", tolua_DoWithID< cWorld, cEntity, &cWorld::DoWithEntityByID>);
+ tolua_function(tolua_S, "DoWithFurnaceAt", tolua_DoWithXYZ<cWorld, cFurnaceEntity, &cWorld::DoWithFurnaceAt>);
+ tolua_function(tolua_S, "DoWithNoteBlockAt", tolua_DoWithXYZ<cWorld, cNoteEntity, &cWorld::DoWithNoteBlockAt>);
+ tolua_function(tolua_S, "DoWithCommandBlockAt", tolua_DoWithXYZ<cWorld, cCommandBlockEntity, &cWorld::DoWithCommandBlockAt>);
+ tolua_function(tolua_S, "DoWithPlayer", tolua_DoWith< cWorld, cPlayer, &cWorld::DoWithPlayer>);
+ tolua_function(tolua_S, "FindAndDoWithPlayer", tolua_DoWith< cWorld, cPlayer, &cWorld::FindAndDoWithPlayer>);
tolua_function(tolua_S, "ForEachBlockEntityInChunk", tolua_ForEachInChunk<cWorld, cBlockEntity, &cWorld::ForEachBlockEntityInChunk>);
tolua_function(tolua_S, "ForEachChestInChunk", tolua_ForEachInChunk<cWorld, cChestEntity, &cWorld::ForEachChestInChunk>);
tolua_function(tolua_S, "ForEachEntity", tolua_ForEach< cWorld, cEntity, &cWorld::ForEachEntity>);
diff --git a/src/BlockEntities/BlockEntity.cpp b/src/BlockEntities/BlockEntity.cpp
index 5d7e4a37a..97b5c1a66 100644
--- a/src/BlockEntities/BlockEntity.cpp
+++ b/src/BlockEntities/BlockEntity.cpp
@@ -6,6 +6,7 @@
#include "Globals.h"
#include "BlockEntity.h"
#include "ChestEntity.h"
+#include "CommandBlockEntity.h"
#include "DispenserEntity.h"
#include "DropperEntity.h"
#include "EnderChestEntity.h"
@@ -23,17 +24,18 @@ cBlockEntity * cBlockEntity::CreateByBlockType(BLOCKTYPE a_BlockType, NIBBLETYPE
{
switch (a_BlockType)
{
- case E_BLOCK_CHEST: return new cChestEntity (a_BlockX, a_BlockY, a_BlockZ, a_World);
- case E_BLOCK_DISPENSER: return new cDispenserEntity (a_BlockX, a_BlockY, a_BlockZ, a_World);
- case E_BLOCK_DROPPER: return new cDropperEntity (a_BlockX, a_BlockY, a_BlockZ, a_World);
- case E_BLOCK_ENDER_CHEST: return new cEnderChestEntity (a_BlockX, a_BlockY, a_BlockZ, a_World);
- case E_BLOCK_LIT_FURNACE: return new cFurnaceEntity (a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta, a_World);
- case E_BLOCK_FURNACE: return new cFurnaceEntity (a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta, a_World);
- case E_BLOCK_HOPPER: return new cHopperEntity (a_BlockX, a_BlockY, a_BlockZ, a_World);
- case E_BLOCK_SIGN_POST: return new cSignEntity (a_BlockType, a_BlockX, a_BlockY, a_BlockZ, a_World);
- case E_BLOCK_WALLSIGN: return new cSignEntity (a_BlockType, a_BlockX, a_BlockY, a_BlockZ, a_World);
- case E_BLOCK_NOTE_BLOCK: return new cNoteEntity (a_BlockX, a_BlockY, a_BlockZ, a_World);
- case E_BLOCK_JUKEBOX: return new cJukeboxEntity (a_BlockX, a_BlockY, a_BlockZ, a_World);
+ case E_BLOCK_CHEST: return new cChestEntity (a_BlockX, a_BlockY, a_BlockZ, a_World);
+ case E_BLOCK_COMMAND_BLOCK: return new cCommandBlockEntity(a_BlockX, a_BlockY, a_BlockZ, a_World);
+ case E_BLOCK_DISPENSER: return new cDispenserEntity (a_BlockX, a_BlockY, a_BlockZ, a_World);
+ case E_BLOCK_DROPPER: return new cDropperEntity (a_BlockX, a_BlockY, a_BlockZ, a_World);
+ case E_BLOCK_ENDER_CHEST: return new cEnderChestEntity (a_BlockX, a_BlockY, a_BlockZ, a_World);
+ case E_BLOCK_LIT_FURNACE: return new cFurnaceEntity (a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta, a_World);
+ case E_BLOCK_FURNACE: return new cFurnaceEntity (a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta, a_World);
+ case E_BLOCK_HOPPER: return new cHopperEntity (a_BlockX, a_BlockY, a_BlockZ, a_World);
+ case E_BLOCK_SIGN_POST: return new cSignEntity (a_BlockType, a_BlockX, a_BlockY, a_BlockZ, a_World);
+ case E_BLOCK_WALLSIGN: return new cSignEntity (a_BlockType, a_BlockX, a_BlockY, a_BlockZ, a_World);
+ case E_BLOCK_NOTE_BLOCK: return new cNoteEntity (a_BlockX, a_BlockY, a_BlockZ, a_World);
+ case E_BLOCK_JUKEBOX: return new cJukeboxEntity (a_BlockX, a_BlockY, a_BlockZ, a_World);
}
LOGD("%s: Requesting creation of an unknown block entity - block type %d (%s)",
__FUNCTION__, a_BlockType, ItemTypeToString(a_BlockType).c_str()
diff --git a/src/BlockEntities/CommandBlockEntity.cpp b/src/BlockEntities/CommandBlockEntity.cpp
new file mode 100644
index 000000000..c29e3dff8
--- /dev/null
+++ b/src/BlockEntities/CommandBlockEntity.cpp
@@ -0,0 +1,227 @@
+
+// CommandBlockEntity.cpp
+
+// Implements the cCommandBlockEntity class representing a single command block in the world
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+#include "json/json.h"
+#include "CommandBlockEntity.h"
+#include "../Entities/Player.h"
+
+#include "CommandOutput.h"
+#include "Root.h"
+#include "Server.h" // ExecuteConsoleCommand()
+
+
+
+
+
+cCommandBlockEntity::cCommandBlockEntity(int a_X, int a_Y, int a_Z, cWorld * a_World) :
+ super(E_BLOCK_COMMAND_BLOCK, a_X, a_Y, a_Z, a_World),
+ m_ShouldExecute(false),
+ m_IsPowered(false)
+{
+ SetBlockEntity(this); // cBlockEntityWindowOwner
+}
+
+
+
+
+
+
+cCommandBlockEntity::~cCommandBlockEntity()
+{
+ // Tell window its owner is destroyed
+ cWindow * Window = GetWindow();
+ if (Window != NULL)
+ {
+ Window->OwnerDestroyed();
+ }
+}
+
+
+
+
+
+
+void cCommandBlockEntity::UsedBy(cPlayer * a_Player)
+{
+ cWindow * Window = GetWindow();
+ if (Window == NULL)
+ {
+ // TODO 2014-01-18 xdot: Open the appropriate window.
+ // OpenWindow(new cCommandBlockWindow(m_PosX, m_PosY, m_PosZ, this));
+ Window = GetWindow();
+ }
+
+ if (Window != NULL)
+ {
+ if (a_Player->GetWindow() != Window)
+ {
+ a_Player->OpenWindow(Window);
+ }
+ }
+}
+
+
+
+
+
+void cCommandBlockEntity::SetCommand(const AString & a_Cmd)
+{
+ m_Command = a_Cmd;
+}
+
+
+
+
+
+void cCommandBlockEntity::SetLastOutput(const AString & a_LastOut)
+{
+ m_LastOutput = a_LastOut;
+}
+
+
+
+
+
+void cCommandBlockEntity::SetResult(const NIBBLETYPE a_Result)
+{
+ m_Result = a_Result;
+}
+
+
+
+
+
+const AString & cCommandBlockEntity::GetCommand(void) const
+{
+ return m_Command;
+}
+
+
+
+
+
+const AString & cCommandBlockEntity::GetLastOutput(void) const
+{
+ return m_LastOutput;
+}
+
+
+
+
+
+NIBBLETYPE cCommandBlockEntity::GetResult(void) const
+{
+ return m_Result;
+}
+
+
+
+
+
+void cCommandBlockEntity::Activate(void)
+{
+ m_ShouldExecute = true;
+}
+
+
+
+
+
+void cCommandBlockEntity::SetRedstonePower(bool a_IsPowered)
+{
+ if (a_IsPowered && !m_IsPowered)
+ {
+ Activate();
+ }
+ m_IsPowered = a_IsPowered;
+}
+
+
+
+
+
+bool cCommandBlockEntity::Tick(float a_Dt, cChunk & a_Chunk)
+{
+ if (!m_ShouldExecute)
+ {
+ return false;
+ }
+
+ m_ShouldExecute = false;
+ Execute();
+ return true;
+}
+
+
+
+
+
+void cCommandBlockEntity::SendTo(cClientHandle & a_Client)
+{
+ // Nothing needs to be sent
+ UNUSED(a_Client);
+}
+
+
+
+
+
+bool cCommandBlockEntity::LoadFromJson(const Json::Value & a_Value)
+{
+ m_Command = a_Value.get("Command", "").asString();
+
+ m_LastOutput = a_Value.get("LastOutput", "").asString();
+
+ return true;
+}
+
+
+
+
+
+void cCommandBlockEntity::SaveToJson(Json::Value & a_Value)
+{
+ a_Value["Command"] = m_Command;
+
+ a_Value["LastOutput"] = m_LastOutput;
+}
+
+
+
+
+
+void cCommandBlockEntity::Execute()
+{
+ class CommandBlockOutCb :
+ public cCommandOutputCallback
+ {
+ cCommandBlockEntity* m_CmdBlock;
+
+ public:
+ CommandBlockOutCb(cCommandBlockEntity* a_CmdBlock) : m_CmdBlock(a_CmdBlock) {}
+
+ virtual void Out(const AString & a_Text)
+ {
+ ASSERT(m_CmdBlock != NULL);
+
+ // Overwrite field
+ m_CmdBlock->SetLastOutput(a_Text);
+ }
+ } CmdBlockOutCb(this);
+
+ LOGD("cCommandBlockEntity: Executing command %s", m_Command.c_str());
+
+ cServer* Server = cRoot::Get()->GetServer();
+
+ Server->ExecuteConsoleCommand(m_Command, CmdBlockOutCb);
+
+ // TODO 2014-01-18 xdot: Update the signal strength.
+ m_Result = 0;
+}
+
+
+
+
diff --git a/src/BlockEntities/CommandBlockEntity.h b/src/BlockEntities/CommandBlockEntity.h
new file mode 100644
index 000000000..c4529a1e6
--- /dev/null
+++ b/src/BlockEntities/CommandBlockEntity.h
@@ -0,0 +1,94 @@
+
+// CommandBlockEntity.h
+
+// Declares the cCommandBlockEntity class representing a single command block in the world
+
+
+
+
+
+#pragma once
+
+#include "BlockEntityWithItems.h"
+#include "../UI/WindowOwner.h"
+
+
+
+
+
+namespace Json
+{
+ class Value;
+}
+
+
+
+
+
+// tolua_begin
+
+class cCommandBlockEntity :
+ public cBlockEntity,
+ public cBlockEntityWindowOwner
+{
+ typedef cBlockEntity super;
+
+public:
+
+ // tolua_end
+
+ /// Creates a new empty command block entity
+ cCommandBlockEntity(int a_X, int a_Y, int a_Z, cWorld * a_World);
+ virtual ~cCommandBlockEntity();
+
+ bool LoadFromJson( const Json::Value& a_Value );
+ virtual void SaveToJson(Json::Value& a_Value ) override;
+
+ virtual bool Tick(float a_Dt, cChunk & a_Chunk) override;
+ virtual void SendTo(cClientHandle & a_Client) override;
+ virtual void UsedBy(cPlayer * a_Player) override;
+
+ void SetLastOutput(const AString & a_LastOut);
+
+ void SetResult(const NIBBLETYPE a_Result);
+
+ // tolua_begin
+
+ /// Sets the internal redstone power flag to "on" or "off", depending on the parameter. Calls Activate() if appropriate
+ void SetRedstonePower(bool a_IsPowered);
+
+ /// Sets the command block to execute a command in the next tick
+ void Activate(void);
+
+ /// Sets the command
+ void SetCommand(const AString & a_Cmd);
+
+ /// Retrieves stored command
+ const AString & GetCommand(void) const;
+
+ /// Retrieves the last line of output generated by the command block
+ const AString & GetLastOutput(void) const;
+
+ /// Retrieves the result (signal strength) of the last operation
+ NIBBLETYPE GetResult(void) const;
+
+ // tolua_end
+
+private:
+
+ /// Executes the associated command
+ void Execute();
+
+ bool m_ShouldExecute;
+ bool m_IsPowered;
+
+ AString m_Command;
+
+ AString m_LastOutput;
+
+ NIBBLETYPE m_Result;
+} ; // tolua_export
+
+
+
+
diff --git a/src/Chunk.cpp b/src/Chunk.cpp
index 0735c8144..a72464ec3 100644
--- a/src/Chunk.cpp
+++ b/src/Chunk.cpp
@@ -1298,6 +1298,7 @@ void cChunk::CreateBlockEntities(void)
switch (BlockType)
{
case E_BLOCK_CHEST:
+ case E_BLOCK_COMMAND_BLOCK:
case E_BLOCK_DISPENSER:
case E_BLOCK_DROPPER:
case E_BLOCK_ENDER_CHEST:
@@ -1425,6 +1426,7 @@ void cChunk::SetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType,
switch (a_BlockType)
{
case E_BLOCK_CHEST:
+ case E_BLOCK_COMMAND_BLOCK:
case E_BLOCK_DISPENSER:
case E_BLOCK_DROPPER:
case E_BLOCK_ENDER_CHEST:
@@ -2268,6 +2270,38 @@ bool cChunk::DoWithNoteBlockAt(int a_BlockX, int a_BlockY, int a_BlockZ, cNoteBl
+bool cChunk::DoWithCommandBlockAt(int a_BlockX, int a_BlockY, int a_BlockZ, cCommandBlockCallback & a_Callback)
+{
+ // The blockentity list is locked by the parent chunkmap's CS
+ for (cBlockEntityList::iterator itr = m_BlockEntities.begin(), itr2 = itr; itr != m_BlockEntities.end(); itr = itr2)
+ {
+ ++itr2;
+ if (((*itr)->GetPosX() != a_BlockX) || ((*itr)->GetPosY() != a_BlockY) || ((*itr)->GetPosZ() != a_BlockZ))
+ {
+ continue;
+ }
+ if ((*itr)->GetBlockType() != E_BLOCK_COMMAND_BLOCK)
+ {
+ // There is a block entity here, but of different type. No other block entity can be here, so we can safely bail out
+ return false;
+ }
+
+ // The correct block entity is here,
+ if (a_Callback.Item((cCommandBlockEntity *)*itr))
+ {
+ return false;
+ }
+ return true;
+ } // for itr - m_BlockEntitites[]
+
+ // Not found:
+ return false;
+}
+
+
+
+
+
bool cChunk::GetSignLines(int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4)
{
// The blockentity list is locked by the parent chunkmap's CS
diff --git a/src/Chunk.h b/src/Chunk.h
index f0a50c3c4..4ac38c46c 100644
--- a/src/Chunk.h
+++ b/src/Chunk.h
@@ -40,12 +40,13 @@ class cFluidSimulatorData;
class cMobCensus;
class cMobSpawner;
-typedef std::list<cClientHandle *> cClientHandleList;
-typedef cItemCallback<cEntity> cEntityCallback;
-typedef cItemCallback<cChestEntity> cChestCallback;
-typedef cItemCallback<cDispenserEntity> cDispenserCallback;
-typedef cItemCallback<cFurnaceEntity> cFurnaceCallback;
-typedef cItemCallback<cNoteEntity> cNoteBlockCallback;
+typedef std::list<cClientHandle *> cClientHandleList;
+typedef cItemCallback<cEntity> cEntityCallback;
+typedef cItemCallback<cChestEntity> cChestCallback;
+typedef cItemCallback<cDispenserEntity> cDispenserCallback;
+typedef cItemCallback<cFurnaceEntity> cFurnaceCallback;
+typedef cItemCallback<cNoteEntity> cNoteBlockCallback;
+typedef cItemCallback<cCommandBlockEntity> cCommandBlockCallback;
@@ -237,6 +238,9 @@ public:
/// Calls the callback for the noteblock at the specified coords; returns false if there's no noteblock at those coords or callback returns true, returns true if found
bool DoWithNoteBlockAt(int a_BlockX, int a_BlockY, int a_BlockZ, cNoteBlockCallback & a_Callback);
+ /// Calls the callback for the command block at the specified coords; returns false if there's no command block at those coords or callback returns true, returns true if found
+ bool DoWithCommandBlockAt(int a_BlockX, int a_BlockY, int a_BlockZ, cCommandBlockCallback & a_Callback);
+
/// Retrieves the test on the sign at the specified coords; returns false if there's no sign at those coords, true if found
bool GetSignLines (int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4); // Lua-accessible
diff --git a/src/ChunkMap.cpp b/src/ChunkMap.cpp
index e14090b18..5797eb453 100644
--- a/src/ChunkMap.cpp
+++ b/src/ChunkMap.cpp
@@ -1968,6 +1968,23 @@ bool cChunkMap::DoWithNoteBlockAt(int a_BlockX, int a_BlockY, int a_BlockZ, cNot
+bool cChunkMap::DoWithCommandBlockAt(int a_BlockX, int a_BlockY, int a_BlockZ, cCommandBlockCallback & a_Callback)
+{
+ int ChunkX, ChunkZ;
+ int BlockX = a_BlockX, BlockY = a_BlockY, BlockZ = a_BlockZ;
+ cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ);
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ);
+ if ((Chunk == NULL) && !Chunk->IsValid())
+ {
+ return false;
+ }
+ return Chunk->DoWithCommandBlockAt(a_BlockX, a_BlockY, a_BlockZ, a_Callback);
+}
+
+
+
+
bool cChunkMap::GetSignLines(int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4)
{
diff --git a/src/ChunkMap.h b/src/ChunkMap.h
index e688d1f93..e9d8ee30b 100644
--- a/src/ChunkMap.h
+++ b/src/ChunkMap.h
@@ -23,6 +23,7 @@ class cDropperEntity;
class cDropSpenserEntity;
class cFurnaceEntity;
class cNoteEntity;
+class cCommandBlockEntity;
class cPawn;
class cPickup;
class cChunkDataSerializer;
@@ -32,15 +33,16 @@ class cMobSpawner;
typedef std::list<cClientHandle *> cClientHandleList;
typedef cChunk * cChunkPtr;
-typedef cItemCallback<cEntity> cEntityCallback;
-typedef cItemCallback<cBlockEntity> cBlockEntityCallback;
-typedef cItemCallback<cChestEntity> cChestCallback;
-typedef cItemCallback<cDispenserEntity> cDispenserCallback;
-typedef cItemCallback<cDropperEntity> cDropperCallback;
-typedef cItemCallback<cDropSpenserEntity> cDropSpenserCallback;
-typedef cItemCallback<cFurnaceEntity> cFurnaceCallback;
-typedef cItemCallback<cNoteEntity> cNoteBlockCallback;
-typedef cItemCallback<cChunk> cChunkCallback;
+typedef cItemCallback<cEntity> cEntityCallback;
+typedef cItemCallback<cBlockEntity> cBlockEntityCallback;
+typedef cItemCallback<cChestEntity> cChestCallback;
+typedef cItemCallback<cDispenserEntity> cDispenserCallback;
+typedef cItemCallback<cDropperEntity> cDropperCallback;
+typedef cItemCallback<cDropSpenserEntity> cDropSpenserCallback;
+typedef cItemCallback<cFurnaceEntity> cFurnaceCallback;
+typedef cItemCallback<cNoteEntity> cNoteBlockCallback;
+typedef cItemCallback<cCommandBlockEntity> cCommandBlockCallback;
+typedef cItemCallback<cChunk> cChunkCallback;
@@ -236,6 +238,9 @@ public:
/// Calls the callback for the noteblock at the specified coords; returns false if there's no noteblock at those coords or callback returns true, returns true if found
bool DoWithNoteBlockAt(int a_BlockX, int a_BlockY, int a_BlockZ, cNoteBlockCallback & a_Callback); // Lua-accessible
+ /// Calls the callback for the command block at the specified coords; returns false if there's no command block at those coords or callback returns true, returns true if found
+ bool DoWithCommandBlockAt(int a_BlockX, int a_BlockY, int a_BlockZ, cCommandBlockCallback & a_Callback); // Lua-accessible
+
/// Retrieves the test on the sign at the specified coords; returns false if there's no sign at those coords, true if found
bool GetSignLines (int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4); // Lua-accessible
diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp
index c8513d516..0a97ba6c8 100644
--- a/src/ClientHandle.cpp
+++ b/src/ClientHandle.cpp
@@ -8,6 +8,7 @@
#include "Entities/Player.h"
#include "Inventory.h"
#include "BlockEntities/ChestEntity.h"
+#include "BlockEntities/CommandBlockEntity.h"
#include "BlockEntities/SignEntity.h"
#include "UI/Window.h"
#include "Item.h"
@@ -545,6 +546,15 @@ void cClientHandle::HandlePlayerPos(double a_PosX, double a_PosY, double a_PosZ,
void cClientHandle::HandlePluginMessage(const AString & a_Channel, const AString & a_Message)
{
+ if (a_Channel == "MC|AdvCdm") // Command block
+ {
+ const char* Data = a_Message.c_str();
+
+ HandleCommandBlockMessage(Data, a_Message.size());
+
+ return;
+ }
+
cPluginManager::Get()->CallHookPluginMessage(*this, a_Channel, a_Message);
}
@@ -552,6 +562,60 @@ void cClientHandle::HandlePluginMessage(const AString & a_Channel, const AString
+void cClientHandle::HandleCommandBlockMessage(const char* a_Data, unsigned int a_Length)
+{
+ if (a_Length < 14)
+ {
+ LOGD("Malformed MC|AdvCdm packet.");
+ return;
+ }
+
+ int BlockX, BlockY, BlockZ;
+
+ AString Command;
+
+ switch (a_Data[0])
+ {
+ case 0x00:
+ {
+ BlockX = GetBEInt(a_Data + 1);
+ BlockY = GetBEInt(a_Data + 5);
+ BlockZ = GetBEInt(a_Data + 9);
+
+ Command = AString(a_Data + 14, (int)a_Data[13]);
+ break;
+ }
+
+ default:
+ {
+ LOGD("Unhandled MC|AdvCdm packet mode.");
+ return;
+ }
+ }
+
+ class cUpdateCommandBlock :
+ public cCommandBlockCallback
+ {
+ AString m_Command;
+ public:
+ cUpdateCommandBlock(const AString & a_Command) : m_Command(a_Command) {}
+
+ virtual bool Item(cCommandBlockEntity * a_CommandBlock) override
+ {
+ a_CommandBlock->SetCommand(m_Command);
+ return false;
+ }
+ } CmdBlockCB (Command);
+
+ cWorld * World = m_Player->GetWorld();
+
+ World->DoWithCommandBlockAt(BlockX, BlockY, BlockZ, CmdBlockCB);
+}
+
+
+
+
+
void cClientHandle::HandleLeftClick(int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status)
{
LOGD("HandleLeftClick: {%i, %i, %i}; Face: %i; Stat: %i",
diff --git a/src/ClientHandle.h b/src/ClientHandle.h
index da2704b72..373ca9e2e 100644
--- a/src/ClientHandle.h
+++ b/src/ClientHandle.h
@@ -324,6 +324,9 @@ private:
/// Handles the DIG_FINISHED dig packet:
void HandleBlockDigFinished(int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_OldBlock, NIBBLETYPE a_OldMeta);
+
+ /// Handles the "MC|AdvCdm" plugin message
+ void HandleCommandBlockMessage(const char* a_Data, unsigned int a_Length);
// cSocketThreads::cCallback overrides:
virtual void DataReceived (const char * a_Data, int a_Size) override; // Data is received from the client
diff --git a/src/Server.cpp b/src/Server.cpp
index 49067c17f..5280270b9 100644
--- a/src/Server.cpp
+++ b/src/Server.cpp
@@ -459,7 +459,7 @@ void cServer::ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallbac
{
return;
}
-
+
// Special handling: "stop" and "restart" are built in
if ((split[0].compare("stop") == 0) || (split[0].compare("restart") == 0))
{
diff --git a/src/Simulator/RedstoneSimulator.cpp b/src/Simulator/RedstoneSimulator.cpp
index 469680098..e361cbf49 100644
--- a/src/Simulator/RedstoneSimulator.cpp
+++ b/src/Simulator/RedstoneSimulator.cpp
@@ -4,6 +4,7 @@
#include "RedstoneSimulator.h"
#include "../BlockEntities/DropSpenserEntity.h"
#include "../BlockEntities/NoteEntity.h"
+#include "../BlockEntities/CommandBlockEntity.h"
#include "../Entities/TNTEntity.h"
#include "../Blocks/BlockTorch.h"
#include "../Blocks/BlockDoor.h"
@@ -215,11 +216,12 @@ void cRedstoneSimulator::SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, c
{
case E_BLOCK_BLOCK_OF_REDSTONE: HandleRedstoneBlock(a_X, dataitr->y, a_Z); break;
case E_BLOCK_LEVER: HandleRedstoneLever(a_X, dataitr->y, a_Z); break;
- case E_BLOCK_TNT: HandleTNT(a_X, dataitr->y, a_Z); break;
+ case E_BLOCK_TNT: HandleTNT(a_X, dataitr->y, a_Z); break;
case E_BLOCK_TRAPDOOR: HandleTrapdoor(a_X, dataitr->y, a_Z); break;
case E_BLOCK_REDSTONE_WIRE: HandleRedstoneWire(a_X, dataitr->y, a_Z); break;
case E_BLOCK_NOTE_BLOCK: HandleNoteBlock(a_X, dataitr->y, a_Z); break;
case E_BLOCK_DAYLIGHT_SENSOR: HandleDaylightSensor(a_X, dataitr->y, a_Z); break;
+ case E_BLOCK_COMMAND_BLOCK: HandleCommandBlock(a_X, dataitr->y, a_Z); break;
case E_BLOCK_REDSTONE_TORCH_OFF:
case E_BLOCK_REDSTONE_TORCH_ON:
@@ -763,6 +765,29 @@ void cRedstoneSimulator::HandleDoor(int a_BlockX, int a_BlockY, int a_BlockZ)
+void cRedstoneSimulator::HandleCommandBlock(int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+ class cSetPowerToCommandBlock :
+ public cCommandBlockCallback
+ {
+ bool m_IsPowered;
+ public:
+ cSetPowerToCommandBlock(bool a_IsPowered) : m_IsPowered(a_IsPowered) {}
+
+ virtual bool Item(cCommandBlockEntity * a_CommandBlock) override
+ {
+ a_CommandBlock->SetRedstonePower(m_IsPowered);
+ return false;
+ }
+ } CmdBlockSP (AreCoordsPowered(a_BlockX, a_BlockY, a_BlockZ));
+
+ m_World.DoWithCommandBlockAt(a_BlockX, a_BlockY, a_BlockZ, CmdBlockSP);
+}
+
+
+
+
+
void cRedstoneSimulator::HandleRail(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_MyType)
{
switch (a_MyType)
diff --git a/src/Simulator/RedstoneSimulator.h b/src/Simulator/RedstoneSimulator.h
index 63a5be3d3..bb2efeb8a 100644
--- a/src/Simulator/RedstoneSimulator.h
+++ b/src/Simulator/RedstoneSimulator.h
@@ -110,6 +110,8 @@ private:
void HandleRedstoneLamp(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_MyState);
/** Handles doords */
void HandleDoor(int a_BlockX, int a_BlockY, int a_BlockZ);
+ /** Handles command blocks */
+ void HandleCommandBlock(int a_BlockX, int a_BlockY, int a_BlockZ);
/** Handles activator, detector, and powered rails */
void HandleRail(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_MyType);
/** Handles trapdoors */
@@ -166,6 +168,7 @@ private:
switch (Block)
{
case E_BLOCK_ACTIVATOR_RAIL:
+ case E_BLOCK_COMMAND_BLOCK:
case E_BLOCK_PISTON:
case E_BLOCK_STICKY_PISTON:
case E_BLOCK_DISPENSER:
@@ -220,6 +223,7 @@ private:
case E_BLOCK_ACTIVATOR_RAIL:
case E_BLOCK_ACTIVE_COMPARATOR:
case E_BLOCK_BLOCK_OF_REDSTONE:
+ case E_BLOCK_COMMAND_BLOCK:
case E_BLOCK_DETECTOR_RAIL:
case E_BLOCK_DISPENSER:
case E_BLOCK_DAYLIGHT_SENSOR:
diff --git a/src/World.cpp b/src/World.cpp
index 3e7c0ef74..7c56d2efb 100644
--- a/src/World.cpp
+++ b/src/World.cpp
@@ -1149,6 +1149,15 @@ bool cWorld::DoWithNoteBlockAt(int a_BlockX, int a_BlockY, int a_BlockZ, cNoteBl
+bool cWorld::DoWithCommandBlockAt(int a_BlockX, int a_BlockY, int a_BlockZ, cCommandBlockCallback & a_Callback)
+{
+ return m_ChunkMap->DoWithCommandBlockAt(a_BlockX, a_BlockY, a_BlockZ, a_Callback);
+}
+
+
+
+
+
bool cWorld::GetSignLines(int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4)
{
return m_ChunkMap->GetSignLines(a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4);
diff --git a/src/World.h b/src/World.h
index ef74a65c7..1a7ad0cb1 100644
--- a/src/World.h
+++ b/src/World.h
@@ -46,12 +46,13 @@ class cMobCensus;
typedef std::list< cPlayer * > cPlayerList;
-typedef cItemCallback<cPlayer> cPlayerListCallback;
-typedef cItemCallback<cEntity> cEntityCallback;
-typedef cItemCallback<cChestEntity> cChestCallback;
-typedef cItemCallback<cDispenserEntity> cDispenserCallback;
-typedef cItemCallback<cFurnaceEntity> cFurnaceCallback;
-typedef cItemCallback<cNoteEntity> cNoteBlockCallback;
+typedef cItemCallback<cPlayer> cPlayerListCallback;
+typedef cItemCallback<cEntity> cEntityCallback;
+typedef cItemCallback<cChestEntity> cChestCallback;
+typedef cItemCallback<cDispenserEntity> cDispenserCallback;
+typedef cItemCallback<cFurnaceEntity> cFurnaceCallback;
+typedef cItemCallback<cNoteEntity> cNoteBlockCallback;
+typedef cItemCallback<cCommandBlockEntity> cCommandBlockCallback;
@@ -466,6 +467,9 @@ public:
/// Calls the callback for the noteblock at the specified coords; returns false if there's no noteblock at those coords or callback returns true, returns true if found
bool DoWithNoteBlockAt(int a_BlockX, int a_BlockY, int a_BlockZ, cNoteBlockCallback & a_Callback); // Exported in ManualBindings.cpp
+
+ /// Calls the callback for the command block at the specified coords; returns false if there's no command block at those coords or callback returns true, returns true if found
+ bool DoWithCommandBlockAt(int a_BlockX, int a_BlockY, int a_BlockZ, cCommandBlockCallback & a_Callback); // Exported in ManualBindings.cpp
/// Retrieves the test on the sign at the specified coords; returns false if there's no sign at those coords, true if found
bool GetSignLines (int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4); // Exported in ManualBindings.cpp
diff --git a/src/WorldStorage/NBTChunkSerializer.cpp b/src/WorldStorage/NBTChunkSerializer.cpp
index e1205f2be..447296c7f 100644
--- a/src/WorldStorage/NBTChunkSerializer.cpp
+++ b/src/WorldStorage/NBTChunkSerializer.cpp
@@ -10,6 +10,7 @@
#include "FastNBT.h"
#include "../BlockEntities/ChestEntity.h"
+#include "../BlockEntities/CommandBlockEntity.h"
#include "../BlockEntities/DispenserEntity.h"
#include "../BlockEntities/DropperEntity.h"
#include "../BlockEntities/FurnaceEntity.h"
@@ -219,8 +220,23 @@ void cNBTChunkSerializer::AddJukeboxEntity(cJukeboxEntity * a_Jukebox)
void cNBTChunkSerializer::AddNoteEntity(cNoteEntity * a_Note)
{
m_Writer.BeginCompound("");
- AddBasicTileEntity(a_Note, "Music");
- m_Writer.AddByte("note", a_Note->GetPitch());
+ AddBasicTileEntity(a_Note, "Music");
+ m_Writer.AddByte("note", a_Note->GetPitch());
+ m_Writer.EndCompound();
+}
+
+
+
+
+
+void cNBTChunkSerializer::AddCommandBlockEntity(cCommandBlockEntity * a_CmdBlock)
+{
+ m_Writer.BeginCompound("");
+ AddBasicTileEntity(a_CmdBlock, "Control");
+ m_Writer.AddString("Command", a_CmdBlock->GetCommand());
+ m_Writer.AddInt ("SuccessCount", a_CmdBlock->GetResult());
+ m_Writer.AddString("LastOutput", a_CmdBlock->GetLastOutput());
+ m_Writer.AddByte ("TrackOutput", 1); // TODO 2014-01-18 xdot: Figure out what TrackOutput is and save it.
m_Writer.EndCompound();
}
@@ -639,15 +655,16 @@ void cNBTChunkSerializer::BlockEntity(cBlockEntity * a_Entity)
// Add tile-entity into NBT:
switch (a_Entity->GetBlockType())
{
- case E_BLOCK_CHEST: AddChestEntity ((cChestEntity *) a_Entity); break;
- case E_BLOCK_DISPENSER: AddDispenserEntity ((cDispenserEntity *) a_Entity); break;
- case E_BLOCK_DROPPER: AddDropperEntity ((cDropperEntity *) a_Entity); break;
- case E_BLOCK_FURNACE: AddFurnaceEntity ((cFurnaceEntity *) a_Entity); break;
- case E_BLOCK_HOPPER: AddHopperEntity ((cHopperEntity *) a_Entity); break;
+ case E_BLOCK_CHEST: AddChestEntity ((cChestEntity *) a_Entity); break;
+ case E_BLOCK_DISPENSER: AddDispenserEntity ((cDispenserEntity *) a_Entity); break;
+ case E_BLOCK_DROPPER: AddDropperEntity ((cDropperEntity *) a_Entity); break;
+ case E_BLOCK_FURNACE: AddFurnaceEntity ((cFurnaceEntity *) a_Entity); break;
+ case E_BLOCK_HOPPER: AddHopperEntity ((cHopperEntity *) a_Entity); break;
case E_BLOCK_SIGN_POST:
- case E_BLOCK_WALLSIGN: AddSignEntity ((cSignEntity *) a_Entity); break;
- case E_BLOCK_NOTE_BLOCK: AddNoteEntity ((cNoteEntity *) a_Entity); break;
- case E_BLOCK_JUKEBOX: AddJukeboxEntity ((cJukeboxEntity *) a_Entity); break;
+ case E_BLOCK_WALLSIGN: AddSignEntity ((cSignEntity *) a_Entity); break;
+ case E_BLOCK_NOTE_BLOCK: AddNoteEntity ((cNoteEntity *) a_Entity); break;
+ case E_BLOCK_JUKEBOX: AddJukeboxEntity ((cJukeboxEntity *) a_Entity); break;
+ case E_BLOCK_COMMAND_BLOCK: AddCommandBlockEntity((cCommandBlockEntity *) a_Entity); break;
default:
{
ASSERT(!"Unhandled block entity saved into Anvil");
diff --git a/src/WorldStorage/NBTChunkSerializer.h b/src/WorldStorage/NBTChunkSerializer.h
index 9d4ac208c..245b68063 100644
--- a/src/WorldStorage/NBTChunkSerializer.h
+++ b/src/WorldStorage/NBTChunkSerializer.h
@@ -21,6 +21,7 @@ class cEntity;
class cBlockEntity;
class cBoat;
class cChestEntity;
+class cCommandBlockEntity;
class cDispenserEntity;
class cDropperEntity;
class cFurnaceEntity;
@@ -92,6 +93,7 @@ protected:
void AddJukeboxEntity (cJukeboxEntity * a_Jukebox);
void AddNoteEntity (cNoteEntity * a_Note);
void AddSignEntity (cSignEntity * a_Sign);
+ void AddCommandBlockEntity(cCommandBlockEntity * a_CmdBlock);
// Entities:
void AddBasicEntity (cEntity * a_Entity, const AString & a_ClassName);
diff --git a/src/WorldStorage/WSSAnvil.cpp b/src/WorldStorage/WSSAnvil.cpp
index 16513cd1b..96a77152b 100644
--- a/src/WorldStorage/WSSAnvil.cpp
+++ b/src/WorldStorage/WSSAnvil.cpp
@@ -15,6 +15,7 @@
#include "../StringCompression.h"
#include "../BlockEntities/ChestEntity.h"
+#include "../BlockEntities/CommandBlockEntity.h"
#include "../BlockEntities/DispenserEntity.h"
#include "../BlockEntities/DropperEntity.h"
#include "../BlockEntities/FurnaceEntity.h"
@@ -567,6 +568,10 @@ void cWSSAnvil::LoadBlockEntitiesFromNBT(cBlockEntityList & a_BlockEntities, con
{
LoadChestFromNBT(a_BlockEntities, a_NBT, Child);
}
+ else if (strncmp(a_NBT.GetData(sID), "Control", a_NBT.GetDataLength(sID)) == 0)
+ {
+ LoadCommandBlockFromNBT(a_BlockEntities, a_NBT, Child);
+ }
else if (strncmp(a_NBT.GetData(sID), "Dropper", a_NBT.GetDataLength(sID)) == 0)
{
LoadDropperFromNBT(a_BlockEntities, a_NBT, Child);
@@ -915,6 +920,43 @@ void cWSSAnvil::LoadSignFromNBT(cBlockEntityList & a_BlockEntities, const cParse
+void cWSSAnvil::LoadCommandBlockFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx)
+{
+ ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound);
+ int x, y, z;
+ if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z))
+ {
+ return;
+ }
+ std::auto_ptr<cCommandBlockEntity> CmdBlock(new cCommandBlockEntity(x, y, z, m_World));
+
+ int currentLine = a_NBT.FindChildByName(a_TagIdx, "Command");
+ if (currentLine >= 0)
+ {
+ CmdBlock->SetCommand(a_NBT.GetString(currentLine));
+ }
+
+ currentLine = a_NBT.FindChildByName(a_TagIdx, "SuccessCount");
+ if (currentLine >= 0)
+ {
+ CmdBlock->SetResult(a_NBT.GetInt(currentLine));
+ }
+
+ currentLine = a_NBT.FindChildByName(a_TagIdx, "LastOutput");
+ if (currentLine >= 0)
+ {
+ CmdBlock->SetLastOutput(a_NBT.GetString(currentLine));
+ }
+
+ // TODO 2014-01-18 xdot: Figure out what TrackOutput is and parse it.
+
+ a_BlockEntities.push_back(CmdBlock.release());
+}
+
+
+
+
+
void cWSSAnvil::LoadEntityFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_EntityTagIdx, const char * a_IDTag, int a_IDTagLength)
{
if (strncmp(a_IDTag, "Boat", a_IDTagLength) == 0)
diff --git a/src/WorldStorage/WSSAnvil.h b/src/WorldStorage/WSSAnvil.h
index e66e63b08..5093ad083 100644
--- a/src/WorldStorage/WSSAnvil.h
+++ b/src/WorldStorage/WSSAnvil.h
@@ -131,14 +131,15 @@ protected:
*/
void LoadItemGridFromNBT(cItemGrid & a_ItemGrid, const cParsedNBT & a_NBT, int a_ItemsTagIdx, int s_SlotOffset = 0);
- void LoadChestFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
- void LoadDispenserFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
- void LoadDropperFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
- void LoadFurnaceFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx, BLOCKTYPE * a_BlockTypes, NIBBLETYPE * a_BlockMetas);
- void LoadHopperFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
- void LoadJukeboxFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
- void LoadNoteFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
- void LoadSignFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
+ void LoadChestFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
+ void LoadDispenserFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
+ void LoadDropperFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
+ void LoadFurnaceFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx, BLOCKTYPE * a_BlockTypes, NIBBLETYPE * a_BlockMetas);
+ void LoadHopperFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
+ void LoadJukeboxFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
+ void LoadNoteFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
+ void LoadSignFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
+ void LoadCommandBlockFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
void LoadEntityFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_EntityTagIdx, const char * a_IDTag, int a_IDTagLength);