summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormadmaxoft@gmail.com <madmaxoft@gmail.com@0a769ca7-a7f5-676a-18bf-c427514a06d6>2013-06-13 09:36:43 +0200
committermadmaxoft@gmail.com <madmaxoft@gmail.com@0a769ca7-a7f5-676a-18bf-c427514a06d6>2013-06-13 09:36:43 +0200
commit03c6bb9f85b929a99df2c800b8ba7d8ef4a8ec43 (patch)
tree8027076867da60f62e52362059717c94084bcfd9
parentProtectionAreas: Added proper ignored files (diff)
downloadcuberite-03c6bb9f85b929a99df2c800b8ba7d8ef4a8ec43.tar
cuberite-03c6bb9f85b929a99df2c800b8ba7d8ef4a8ec43.tar.gz
cuberite-03c6bb9f85b929a99df2c800b8ba7d8ef4a8ec43.tar.bz2
cuberite-03c6bb9f85b929a99df2c800b8ba7d8ef4a8ec43.tar.lz
cuberite-03c6bb9f85b929a99df2c800b8ba7d8ef4a8ec43.tar.xz
cuberite-03c6bb9f85b929a99df2c800b8ba7d8ef4a8ec43.tar.zst
cuberite-03c6bb9f85b929a99df2c800b8ba7d8ef4a8ec43.zip
-rw-r--r--VC2008/MCServer.vcproj8
-rw-r--r--source/BlockEntities/HopperEntity.cpp291
-rw-r--r--source/BlockEntities/HopperEntity.h81
-rw-r--r--source/Chunk.cpp36
-rw-r--r--source/Chunk.h5
-rw-r--r--source/ItemGrid.cpp10
-rw-r--r--source/UI/SlotArea.cpp20
-rw-r--r--source/UI/SlotArea.h8
-rw-r--r--source/UI/Window.cpp53
-rw-r--r--source/UI/Window.h24
-rw-r--r--source/WorldStorage/NBTChunkSerializer.cpp34
-rw-r--r--source/WorldStorage/NBTChunkSerializer.h24
-rw-r--r--source/WorldStorage/WSSAnvil.cpp82
-rw-r--r--source/WorldStorage/WSSAnvil.h5
14 files changed, 615 insertions, 66 deletions
diff --git a/VC2008/MCServer.vcproj b/VC2008/MCServer.vcproj
index bbc3cb663..83e9137e7 100644
--- a/VC2008/MCServer.vcproj
+++ b/VC2008/MCServer.vcproj
@@ -2357,6 +2357,14 @@
>
</File>
<File
+ RelativePath="..\source\BlockEntities\HopperEntity.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\source\BlockEntities\HopperEntity.h"
+ >
+ </File>
+ <File
RelativePath="..\source\BlockEntities\JukeboxEntity.cpp"
>
</File>
diff --git a/source/BlockEntities/HopperEntity.cpp b/source/BlockEntities/HopperEntity.cpp
new file mode 100644
index 000000000..68974fdd3
--- /dev/null
+++ b/source/BlockEntities/HopperEntity.cpp
@@ -0,0 +1,291 @@
+
+// HopperEntity.cpp
+
+// Implements the cHopperEntity representing a hopper block entity
+
+#include "Globals.h"
+#include "HopperEntity.h"
+#include "../Chunk.h"
+#include "../Player.h"
+#include "ChestEntity.h"
+#include "DropSpenserEntity.h"
+
+
+
+
+
+cHopperEntity::cHopperEntity(int a_BlockX, int a_BlockY, int a_BlockZ) :
+ super(E_BLOCK_HOPPER, a_BlockX, a_BlockY, a_BlockZ, ContentsWidth, ContentsHeight, NULL)
+{
+}
+
+
+
+
+
+cHopperEntity::cHopperEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) :
+ super(E_BLOCK_HOPPER, a_BlockX, a_BlockY, a_BlockZ, ContentsWidth, ContentsHeight, a_World)
+{
+}
+
+
+
+
+
+bool cHopperEntity::Tick(float a_Dt, cChunk & a_Chunk)
+{
+ Int64 CurrentTick = a_Chunk.GetWorld()->GetWorldAge();
+
+ bool res = false;
+ res = MoveItemsIn (a_Chunk, CurrentTick) || res;
+ res = MovePickupsIn(a_Chunk, CurrentTick) || res;
+ res = MoveItemsOut (a_Chunk, CurrentTick) || res;
+ return res;
+}
+
+
+
+
+
+void cHopperEntity::SaveToJson(Json::Value & a_Value)
+{
+ // TODO
+ LOGWARNING("%s: Not implemented yet", __FUNCTION__);
+}
+
+
+
+
+
+void cHopperEntity::SendTo(cClientHandle & a_Client)
+{
+ // The hopper entity doesn't need anything sent to the client when it's created / gets in the viewdistance
+ // All the actual handling is in the cWindow UI code that gets called when the hopper is rclked
+
+ UNUSED(a_Client);
+}
+
+
+
+
+
+void cHopperEntity::UsedBy(cPlayer * a_Player)
+{
+ // If the window is not created, open it anew:
+ cWindow * Window = GetWindow();
+ if (Window == NULL)
+ {
+ OpenNewWindow();
+ Window = GetWindow();
+ }
+
+ // Open the window for the player:
+ if (Window != NULL)
+ {
+ if (a_Player->GetWindow() != Window)
+ {
+ a_Player->OpenWindow(Window);
+ }
+ }
+
+ // This is rather a hack
+ // Instead of marking the chunk as dirty upon chest contents change, we mark it dirty now
+ // We cannot properly detect contents change, but such a change doesn't happen without a player opening the chest first.
+ // The few false positives aren't much to worry about
+ int ChunkX, ChunkZ;
+ cChunkDef::BlockToChunk(m_PosX, m_PosY, m_PosZ, ChunkX, ChunkZ);
+ m_World->MarkChunkDirty(ChunkX, ChunkZ);
+}
+
+
+
+
+
+/// Opens a new window UI for this hopper
+void cHopperEntity::OpenNewWindow(void)
+{
+ OpenWindow(new cHopperWindow(m_PosX, m_PosY, m_PosZ, this));
+}
+
+
+
+
+
+/// Moves items from the container above it into this hopper. Returns true if the contents have changed.
+bool cHopperEntity::MoveItemsIn(cChunk & a_Chunk, Int64 a_CurrentTick)
+{
+ if (m_PosY >= cChunkDef::Height)
+ {
+ // This hopper is at the top of the world, no more blocks above
+ return false;
+ }
+
+ if (a_CurrentTick - m_LastMoveItemsInTick < TICKS_PER_TRANSFER)
+ {
+ // Too early after the previous transfer
+ return false;
+ }
+
+ // Try moving an item in:
+ bool res = false;
+ switch (a_Chunk.GetBlock(m_RelX, m_PosY + 1, m_RelZ))
+ {
+ case E_BLOCK_CHEST: res = MoveItemsFromChest(a_Chunk); break;
+ case E_BLOCK_FURNACE: res = MoveItemsFromFurnace(a_Chunk); break;
+ case E_BLOCK_DISPENSER:
+ case E_BLOCK_DROPPER: res = MoveItemsFromGrid(((cDropSpenserEntity *)a_Chunk.GetBlockEntity(m_PosX, m_PosY + 1, m_PosZ))->GetContents()); break;
+ case E_BLOCK_HOPPER: res = MoveItemsFromGrid(((cHopperEntity *) a_Chunk.GetBlockEntity(m_PosX, m_PosY + 1, m_PosZ))->GetContents()); break;
+ }
+
+ // If the item has been moved, reset the last tick:
+ if (res)
+ {
+ m_LastMoveItemsInTick = a_CurrentTick;
+ }
+
+ return res;
+}
+
+
+
+
+
+/// Moves pickups from above this hopper into it. Returns true if the contents have changed.
+bool cHopperEntity::MovePickupsIn(cChunk & a_Chunk, Int64 a_CurrentTick)
+{
+ // TODO
+ return false;
+}
+
+
+
+
+
+/// Moves items out from this hopper into the destination. Returns true if the contents have changed.
+bool cHopperEntity::MoveItemsOut(cChunk & a_Chunk, Int64 a_CurrentTick)
+{
+ if (a_CurrentTick - m_LastMoveItemsOutTick < TICKS_PER_TRANSFER)
+ {
+ // Too early after the previous transfer
+ return false;
+ }
+
+ // TODO
+ return false;
+}
+
+
+
+
+
+/// Moves items from a chest (dblchest) above the hopper into this hopper. Returns true if contents have changed.
+bool cHopperEntity::MoveItemsFromChest(cChunk & a_Chunk)
+{
+ if (MoveItemsFromGrid(((cChestEntity *)a_Chunk.GetBlockEntity(m_PosX, m_PosY + 1, m_PosZ))->GetContents()))
+ {
+ // Moved the item from the chest directly above the hopper
+ return true;
+ }
+
+ // Check if the chest is a double-chest, if so, try to move from there:
+ static const struct
+ {
+ int x, z;
+ }
+ Coords [] =
+ {
+ {1, 0},
+ {-1, 0},
+ {0, 1},
+ {0, -1},
+ } ;
+ for (int i = 0; i < ARRAYCOUNT(Coords); i++)
+ {
+ int x = m_RelX + Coords[i].x;
+ int z = m_RelZ + Coords[i].z;
+ cChunk * Neighbor = a_Chunk.GetRelNeighborChunkAdjustCoords(x, z);
+ if (
+ (Neighbor == NULL) ||
+ (Neighbor->GetBlock(x, m_PosY + 1, z) != E_BLOCK_CHEST)
+ )
+ {
+ continue;
+ }
+ if (MoveItemsFromGrid(((cChestEntity *)Neighbor->GetBlockEntity(x, m_PosY, z))->GetContents()))
+ {
+ return true;
+ }
+ return false;
+ }
+
+ // The chest was single and nothing could be moved
+ return false;
+}
+
+
+
+
+
+/// Moves items from a furnace above the hopper into this hopper. Returns true if contents have changed.
+bool cHopperEntity::MoveItemsFromFurnace(cChunk & a_Chunk)
+{
+ // TODO
+ return false;
+}
+
+
+
+
+
+/// Moves items from the specified ItemGrid into this hopper. Returns true if contents have changed.
+bool cHopperEntity::MoveItemsFromGrid(cItemGrid & a_Grid)
+{
+ int NumSlots = a_Grid.GetNumSlots();
+
+ // First try adding items of types already in the hopper:
+ for (int i = 0; i < NumSlots; i++)
+ {
+ if (a_Grid.IsSlotEmpty(i))
+ {
+ continue;
+ }
+ if (MoveItemsFromSlot(a_Grid.GetSlot(i), false))
+ {
+ a_Grid.ChangeSlotCount(i, -1);
+ return true;
+ }
+ }
+
+ // No already existing stack can be topped up, try again with allowing new stacks:
+ for (int i = 0; i < NumSlots; i++)
+ {
+ if (a_Grid.IsSlotEmpty(i))
+ {
+ continue;
+ }
+ if (MoveItemsFromSlot(a_Grid.GetSlot(i), true))
+ {
+ a_Grid.ChangeSlotCount(i, -1);
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+/// Moves one of the specified itemstack into this hopper. Returns true if contents have changed. Doesn't change the itemstack.
+bool cHopperEntity::MoveItemsFromSlot(const cItem & a_ItemStack, bool a_AllowNewStacks)
+{
+ if (m_Contents.AddItem(a_ItemStack.CopyOne(), a_AllowNewStacks) > 0)
+ {
+ return true;
+ }
+ return false;
+}
+
+
+
+
diff --git a/source/BlockEntities/HopperEntity.h b/source/BlockEntities/HopperEntity.h
new file mode 100644
index 000000000..73c2a536c
--- /dev/null
+++ b/source/BlockEntities/HopperEntity.h
@@ -0,0 +1,81 @@
+
+// HopperEntity.h
+
+// Declares the cHopperEntity representing a hopper block entity
+
+
+
+
+
+#pragma once
+
+#include "BlockEntityWithItems.h"
+#include "../UI/WindowOwner.h"
+
+
+
+
+
+class cHopperEntity : // tolua_export
+ public cBlockEntityWindowOwner,
+ // tolua_begin
+ public cBlockEntityWithItems
+{
+ typedef cBlockEntityWithItems super;
+
+public:
+ enum {
+ ContentsHeight = 1,
+ ContentsWidth = 5,
+ TICKS_PER_TRANSFER = 8, ///< How many ticks at minimum between two item transfers to or from the hopper
+ } ;
+
+ /// Constructor used while generating a chunk; sets m_World to NULL
+ cHopperEntity(int a_BlockX, int a_BlockY, int a_BlockZ);
+
+ // tolua_end
+
+ /// Constructor used for normal operation
+ cHopperEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World);
+
+ static const char * GetClassStatic(void) { return "cHopperEntity"; }
+
+protected:
+
+ Int64 m_LastMoveItemsInTick;
+ Int64 m_LastMoveItemsOutTick;
+
+ // cBlockEntity overrides:
+ virtual bool Tick(float a_Dt, cChunk & a_Chunk) override;
+ virtual void SaveToJson(Json::Value & a_Value) override;
+ virtual void SendTo(cClientHandle & a_Client) override;
+ virtual void UsedBy(cPlayer * a_Player) override;
+
+ /// Opens a new chest window for this chest. Scans for neighbors to open a double chest window, if appropriate.
+ void OpenNewWindow(void);
+
+ /// Moves items from the container above it into this hopper. Returns true if the contents have changed.
+ bool MoveItemsIn(cChunk & a_Chunk, Int64 a_CurrentTick);
+
+ /// Moves pickups from above this hopper into it. Returns true if the contents have changed.
+ bool MovePickupsIn(cChunk & a_Chunk, Int64 a_CurrentTick);
+
+ /// Moves items out from this hopper into the destination. Returns true if the contents have changed.
+ bool MoveItemsOut(cChunk & a_Chunk, Int64 a_CurrentTick);
+
+ /// Moves items from a chest (dblchest) above the hopper into this hopper. Returns true if contents have changed.
+ bool MoveItemsFromChest(cChunk & a_Chunk);
+
+ /// Moves items from a furnace above the hopper into this hopper. Returns true if contents have changed.
+ bool MoveItemsFromFurnace(cChunk & a_Chunk);
+
+ /// Moves items from the specified ItemGrid into this hopper. Returns true if contents have changed.
+ bool MoveItemsFromGrid(cItemGrid & a_Grid);
+
+ /// Moves one of the specified itemstack into this hopper. Returns true if contents have changed. Doesn't change the itemstack.
+ bool MoveItemsFromSlot(const cItem & a_ItemStack, bool a_AllowNewStacks);
+} ;
+
+
+
+
diff --git a/source/Chunk.cpp b/source/Chunk.cpp
index c2afb9d5c..78074a7af 100644
--- a/source/Chunk.cpp
+++ b/source/Chunk.cpp
@@ -16,6 +16,7 @@
#include "BlockEntities/DispenserEntity.h"
#include "BlockEntities/DropperEntity.h"
#include "BlockEntities/FurnaceEntity.h"
+#include "BlockEntities/HopperEntity.h"
#include "BlockEntities/JukeboxEntity.h"
#include "BlockEntities/NoteEntity.h"
#include "BlockEntities/SignEntity.h"
@@ -1222,7 +1223,7 @@ void cChunk::CreateBlockEntities(void)
{
if (!HasBlockEntityAt(x + m_PosX * Width, y + m_PosY * Height, z + m_PosZ * Width))
{
- m_BlockEntities.push_back( new cChestEntity( x + m_PosX * Width, y + m_PosY * Height, z + m_PosZ * Width, m_World) );
+ m_BlockEntities.push_back(new cChestEntity(x + m_PosX * Width, y + m_PosY * Height, z + m_PosZ * Width, m_World));
}
break;
}
@@ -1231,7 +1232,7 @@ void cChunk::CreateBlockEntities(void)
{
if (!HasBlockEntityAt(x + m_PosX * Width, y + m_PosY * Height, z + m_PosZ * Width))
{
- m_BlockEntities.push_back( new cDispenserEntity( x + m_PosX * Width, y + m_PosY * Height, z + m_PosZ * Width, m_World) );
+ m_BlockEntities.push_back(new cDispenserEntity(x + m_PosX * Width, y + m_PosY * Height, z + m_PosZ * Width, m_World));
}
break;
}
@@ -1249,17 +1250,25 @@ void cChunk::CreateBlockEntities(void)
{
if (!HasBlockEntityAt(x + m_PosX * Width, y + m_PosY * Height, z + m_PosZ * Width))
{
- m_BlockEntities.push_back( new cFurnaceEntity( x + m_PosX * Width, y + m_PosY * Height, z + m_PosZ * Width, m_World) );
+ m_BlockEntities.push_back(new cFurnaceEntity(x + m_PosX * Width, y + m_PosY * Height, z + m_PosZ * Width, m_World));
}
break;
}
+ case E_BLOCK_HOPPER:
+ {
+ if (!HasBlockEntityAt(x + m_PosX * Width, y + m_PosY * Height, z + m_PosZ * Width))
+ {
+ m_BlockEntities.push_back(new cHopperEntity(x + m_PosX * Width, y + m_PosY * Height, z + m_PosZ * Width, m_World));
+ }
+ }
+
case E_BLOCK_SIGN_POST:
case E_BLOCK_WALLSIGN:
{
if (!HasBlockEntityAt(x + m_PosX * Width, y + m_PosY * Height, z + m_PosZ * Width))
{
- m_BlockEntities.push_back( new cSignEntity( BlockType, x + m_PosX * Width, y + m_PosY * Height, z + m_PosZ * Width, m_World) );
+ m_BlockEntities.push_back( new cSignEntity(BlockType, x + m_PosX * Width, y + m_PosY * Height, z + m_PosZ * Width, m_World));
}
break;
}
@@ -1268,7 +1277,7 @@ void cChunk::CreateBlockEntities(void)
{
if (!HasBlockEntityAt(x + m_PosX * Width, y + m_PosY * Height, z + m_PosZ * Width))
{
- m_BlockEntities.push_back(new cNoteEntity(x + m_PosX * Width, y + m_PosY * Height, z + m_PosZ * Width, m_World) );
+ m_BlockEntities.push_back(new cNoteEntity(x + m_PosX * Width, y + m_PosY * Height, z + m_PosZ * Width, m_World));
}
break;
}
@@ -1277,7 +1286,7 @@ void cChunk::CreateBlockEntities(void)
{
if (!HasBlockEntityAt(x + m_PosX * Width, y + m_PosY * Height, z + m_PosZ * Width))
{
- m_BlockEntities.push_back(new cJukeboxEntity(x + m_PosX * Width, y + m_PosY * Height, z + m_PosZ * Width, m_World) );
+ m_BlockEntities.push_back(new cJukeboxEntity(x + m_PosX * Width, y + m_PosY * Height, z + m_PosZ * Width, m_World));
}
break;
}
@@ -1434,28 +1443,33 @@ void cChunk::SetBlock( int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType
{
case E_BLOCK_CHEST:
{
- AddBlockEntity( new cChestEntity( WorldPos.x, WorldPos.y, WorldPos.z, m_World) );
+ AddBlockEntity(new cChestEntity( WorldPos.x, WorldPos.y, WorldPos.z, m_World));
break;
}
case E_BLOCK_DISPENSER:
{
- AddBlockEntity( new cDispenserEntity( WorldPos.x, WorldPos.y, WorldPos.z, m_World) );
+ AddBlockEntity(new cDispenserEntity( WorldPos.x, WorldPos.y, WorldPos.z, m_World));
break;
}
case E_BLOCK_DROPPER:
{
- AddBlockEntity( new cDropperEntity( WorldPos.x, WorldPos.y, WorldPos.z, m_World) );
+ AddBlockEntity(new cDropperEntity( WorldPos.x, WorldPos.y, WorldPos.z, m_World));
break;
}
case E_BLOCK_FURNACE:
{
- AddBlockEntity( new cFurnaceEntity( WorldPos.x, WorldPos.y, WorldPos.z, m_World) );
+ AddBlockEntity(new cFurnaceEntity( WorldPos.x, WorldPos.y, WorldPos.z, m_World));
+ break;
+ }
+ case E_BLOCK_HOPPER:
+ {
+ AddBlockEntity(new cHopperEntity( WorldPos.x, WorldPos.y, WorldPos.z, m_World));
break;
}
case E_BLOCK_SIGN_POST:
case E_BLOCK_WALLSIGN:
{
- AddBlockEntity( new cSignEntity( (ENUM_BLOCK_ID)a_BlockType, WorldPos.x, WorldPos.y, WorldPos.z, m_World) );
+ AddBlockEntity(new cSignEntity(a_BlockType, WorldPos.x, WorldPos.y, WorldPos.z, m_World));
break;
}
case E_BLOCK_NOTE_BLOCK:
diff --git a/source/Chunk.h b/source/Chunk.h
index 748227b70..07062e71b 100644
--- a/source/Chunk.h
+++ b/source/Chunk.h
@@ -310,6 +310,9 @@ public:
cFluidSimulatorData * GetLavaSimulatorData (void) { return m_LavaSimulatorData; }
cSandSimulatorChunkData & GetSandSimulatorData (void) { return m_SandSimulatorData; }
+ cBlockEntity * GetBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ);
+ cBlockEntity * GetBlockEntity(const Vector3i & a_BlockPos) { return GetBlockEntity(a_BlockPos.x, a_BlockPos.y, a_BlockPos.z); }
+
private:
friend class cChunkMap;
@@ -362,8 +365,6 @@ private:
void RemoveBlockEntity(cBlockEntity * a_BlockEntity);
void AddBlockEntity (cBlockEntity * a_BlockEntity);
- cBlockEntity * GetBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ);
- cBlockEntity * GetBlockEntity(const Vector3i & a_BlockPos) { return GetBlockEntity(a_BlockPos.x, a_BlockPos.y, a_BlockPos.z); }
void SpreadLightOfBlock(NIBBLETYPE * a_LightBuffer, int a_X, int a_Y, int a_Z, char a_Falloff);
diff --git a/source/ItemGrid.cpp b/source/ItemGrid.cpp
index c62ea6bcc..1154250f1 100644
--- a/source/ItemGrid.cpp
+++ b/source/ItemGrid.cpp
@@ -616,9 +616,13 @@ void cItemGrid::RemoveListener(cListener & a_Listener)
void cItemGrid::TriggerListeners(int a_SlotNum)
{
- cCSLock Lock(m_CSListeners);
- m_IsInTriggerListeners = true;
- for (cListeners::iterator itr = m_Listeners.begin(), end = m_Listeners.end(); itr != end; ++itr)
+ cListeners Listeners;
+ {
+ cCSLock Lock(m_CSListeners);
+ m_IsInTriggerListeners = true;
+ Listeners = m_Listeners;
+ }
+ for (cListeners::iterator itr = Listeners.begin(), end = Listeners.end(); itr != end; ++itr)
{
(*itr)->OnSlotChanged(this, a_SlotNum);
} // for itr - m_Listeners[]
diff --git a/source/UI/SlotArea.cpp b/source/UI/SlotArea.cpp
index 3f480817c..feeeb4add 100644
--- a/source/UI/SlotArea.cpp
+++ b/source/UI/SlotArea.cpp
@@ -665,6 +665,16 @@ cSlotAreaItemGrid::cSlotAreaItemGrid(cItemGrid & a_ItemGrid, cWindow & a_ParentW
super(a_ItemGrid.GetNumSlots(), a_ParentWindow),
m_ItemGrid(a_ItemGrid)
{
+ m_ItemGrid.AddListener(*this);
+}
+
+
+
+
+
+cSlotAreaItemGrid::~cSlotAreaItemGrid()
+{
+ m_ItemGrid.RemoveListener(*this);
}
@@ -689,6 +699,16 @@ void cSlotAreaItemGrid::SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem &
+void cSlotAreaItemGrid::OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum)
+{
+ ASSERT(a_ItemGrid == &m_ItemGrid);
+ m_ParentWindow.BroadcastSlot(this, a_SlotNum);
+}
+
+
+
+
+
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cSlotAreaTemporary:
diff --git a/source/UI/SlotArea.h b/source/UI/SlotArea.h
index 0ad5296db..2666f5209 100644
--- a/source/UI/SlotArea.h
+++ b/source/UI/SlotArea.h
@@ -143,18 +143,24 @@ public:
/// Handles any slot area that is representing a cItemGrid; same items for all the players
class cSlotAreaItemGrid :
- public cSlotArea
+ public cSlotArea,
+ public cItemGrid::cListener
{
typedef cSlotArea super;
public:
cSlotAreaItemGrid(cItemGrid & a_ItemGrid, cWindow & a_ParentWindow);
+ virtual ~cSlotAreaItemGrid();
+
virtual const cItem * GetSlot(int a_SlotNum, cPlayer & a_Player) const override;
virtual void SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) override;
protected:
cItemGrid & m_ItemGrid;
+
+ // cItemGrid::cListener overrides:
+ virtual void OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) override;
} ;
diff --git a/source/UI/Window.cpp b/source/UI/Window.cpp
index 3c28f33b0..161145d50 100644
--- a/source/UI/Window.cpp
+++ b/source/UI/Window.cpp
@@ -11,6 +11,7 @@
#include "../Inventory.h"
#include "../Items/ItemHandler.h"
#include "../BlockEntities/ChestEntity.h"
+#include "../BlockEntities/HopperEntity.h"
@@ -607,6 +608,40 @@ int cWindow::DistributeItemToSlots(cPlayer & a_Player, const cItem & a_Item, int
+void cWindow::BroadcastSlot(cSlotArea * a_Area, int a_LocalSlotNum)
+{
+ // Translate local slot num into global slot num:
+ int SlotNum = 0;
+ bool HasFound = false;
+ for (cSlotAreas::const_iterator itr = m_SlotAreas.begin(), end = m_SlotAreas.end(); itr != end; ++itr)
+ {
+ if (a_Area == *itr)
+ {
+ SlotNum += a_LocalSlotNum;
+ HasFound = true;
+ break;
+ }
+ SlotNum += (*itr)->GetNumSlots();
+ } // for itr - m_SlotAreas[]
+ if (!HasFound)
+ {
+ LOGWARNING("%s: Invalid slot area parameter", __FUNCTION__);
+ ASSERT(!"Invalid slot area");
+ return;
+ }
+
+ // Broadcast the update packet:
+ cCSLock Lock(m_CS);
+ for (cPlayerList::iterator itr = m_OpenedBy.begin(); itr != m_OpenedBy.end(); ++itr)
+ {
+ (*itr)->GetClientHandle()->SendInventorySlot(m_WindowID, SlotNum, *a_Area->GetSlot(a_LocalSlotNum, **itr));
+ } // for itr - m_OpenedBy[]
+}
+
+
+
+
+
void cWindow::SendWholeWindow(cClientHandle & a_Client)
{
a_Client.SendWholeInventory(*this);
@@ -741,6 +776,7 @@ cChestWindow::~cChestWindow()
cDropSpenserWindow::cDropSpenserWindow(int a_BlockX, int a_BlockY, int a_BlockZ, cDropSpenserEntity * a_DropSpenser) :
cWindow(cWindow::DropSpenser, "MCS-DropSpenser")
{
+ m_ShouldDistributeToHotbarFirst = false;
m_SlotAreas.push_back(new cSlotAreaDropSpenser(a_DropSpenser, *this));
m_SlotAreas.push_back(new cSlotAreaInventory(*this));
m_SlotAreas.push_back(new cSlotAreaHotBar(*this));
@@ -751,11 +787,28 @@ cDropSpenserWindow::cDropSpenserWindow(int a_BlockX, int a_BlockY, int a_BlockZ,
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cHopperWindow:
+
+cHopperWindow::cHopperWindow(int a_BlockX, int a_BlockY, int a_BlockZ, cHopperEntity * a_Hopper) :
+ super(cWindow::Hopper, "MCS-Hopper")
+{
+ m_ShouldDistributeToHotbarFirst = false;
+ m_SlotAreas.push_back(new cSlotAreaItemGrid(a_Hopper->GetContents(), *this));
+ m_SlotAreas.push_back(new cSlotAreaInventory(*this));
+ m_SlotAreas.push_back(new cSlotAreaHotBar(*this));
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cFurnaceWindow:
cFurnaceWindow::cFurnaceWindow(int a_BlockX, int a_BlockY, int a_BlockZ, cFurnaceEntity * a_Furnace) :
cWindow(cWindow::Furnace, "MCS-Furnace")
{
+ m_ShouldDistributeToHotbarFirst = false;
m_SlotAreas.push_back(new cSlotAreaFurnace(a_Furnace, *this));
m_SlotAreas.push_back(new cSlotAreaInventory(*this));
m_SlotAreas.push_back(new cSlotAreaHotBar(*this));
diff --git a/source/UI/Window.h b/source/UI/Window.h
index 0ff87dc70..0be43e819 100644
--- a/source/UI/Window.h
+++ b/source/UI/Window.h
@@ -21,6 +21,7 @@ class cClientHandle;
class cChestEntity;
class cDropSpenserEntity;
class cFurnaceEntity;
+class cHopperEntity;
class cSlotArea;
class cWorld;
@@ -110,8 +111,16 @@ public:
/// Called when a player closes this window; notifies all slot areas. Returns true if close accepted
virtual bool ClosedByPlayer(cPlayer & a_Player, bool a_CanRefuse);
+ /// Sends the specified slot's contents to all clients of this window; the slot is specified as local in an area
+ void BroadcastSlot(cSlotArea * a_Area, int a_LocalSlotNum);
+
+ /// Sends the contents of the whole window to the specified client
void SendWholeWindow(cClientHandle & a_Client);
+
+ /// Sends the contents of the whole window to all clients of this window.
void BroadcastWholeWindow(void);
+
+ /// Sends the progressbar to all clients of this window
void BroadcastInventoryProgress(short a_Progressbar, short a_Value);
// tolua_begin
@@ -194,6 +203,7 @@ protected:
class cCraftingWindow :
public cWindow
{
+ typedef cWindow super;
public:
cCraftingWindow(int a_BlockX, int a_BlockY, int a_BlockZ);
} ;
@@ -205,6 +215,7 @@ public:
class cFurnaceWindow :
public cWindow
{
+ typedef cWindow super;
public:
cFurnaceWindow(int a_BlockX, int a_BlockY, int a_BlockZ, cFurnaceEntity * a_Furnace);
} ;
@@ -216,6 +227,7 @@ public:
class cDropSpenserWindow :
public cWindow
{
+ typedef cWindow super;
public:
cDropSpenserWindow(int a_BlockX, int a_BlockY, int a_BlockZ, cDropSpenserEntity * a_Dispenser);
} ;
@@ -224,6 +236,18 @@ public:
+class cHopperWindow :
+ public cWindow
+{
+ typedef cWindow super;
+public:
+ cHopperWindow(int a_BlockX, int a_BlockY, int a_BlockZ, cHopperEntity * a_Hopper);
+} ;
+
+
+
+
+
class cChestWindow :
public cWindow
{
diff --git a/source/WorldStorage/NBTChunkSerializer.cpp b/source/WorldStorage/NBTChunkSerializer.cpp
index 2cf34a24f..9c4f8a2ae 100644
--- a/source/WorldStorage/NBTChunkSerializer.cpp
+++ b/source/WorldStorage/NBTChunkSerializer.cpp
@@ -9,6 +9,7 @@
#include "../BlockEntities/DispenserEntity.h"
#include "../BlockEntities/DropperEntity.h"
#include "../BlockEntities/FurnaceEntity.h"
+#include "../BlockEntities/HopperEntity.h"
#include "../BlockEntities/JukeboxEntity.h"
#include "../BlockEntities/NoteEntity.h"
#include "../BlockEntities/SignEntity.h"
@@ -175,14 +176,25 @@ void cNBTChunkSerializer::AddFurnaceEntity(cFurnaceEntity * a_Furnace)
-void cNBTChunkSerializer::AddSignEntity(cSignEntity * a_Sign)
+void cNBTChunkSerializer::AddHopperEntity(cHopperEntity * a_Entity)
{
m_Writer.BeginCompound("");
- AddBasicTileEntity(a_Sign, "Sign");
- m_Writer.AddString("Text1", a_Sign->GetLine(0));
- m_Writer.AddString("Text2", a_Sign->GetLine(1));
- m_Writer.AddString("Text3", a_Sign->GetLine(2));
- m_Writer.AddString("Text4", a_Sign->GetLine(3));
+ AddBasicTileEntity(a_Entity, "Hopper");
+ m_Writer.BeginList("Items", TAG_Compound);
+ AddItemGrid(a_Entity->GetContents());
+ m_Writer.EndList();
+ m_Writer.EndCompound();
+}
+
+
+
+
+
+void cNBTChunkSerializer::AddJukeboxEntity(cJukeboxEntity * a_Jukebox)
+{
+ m_Writer.BeginCompound("");
+ AddBasicTileEntity(a_Jukebox, "RecordPlayer");
+ m_Writer.AddInt("Record", a_Jukebox->GetRecord());
m_Writer.EndCompound();
}
@@ -202,11 +214,14 @@ void cNBTChunkSerializer::AddNoteEntity(cNoteEntity * a_Note)
-void cNBTChunkSerializer::AddJukeboxEntity(cJukeboxEntity * a_Jukebox)
+void cNBTChunkSerializer::AddSignEntity(cSignEntity * a_Sign)
{
m_Writer.BeginCompound("");
- AddBasicTileEntity(a_Jukebox, "RecordPlayer");
- m_Writer.AddInt("Record", a_Jukebox->GetRecord());
+ AddBasicTileEntity(a_Sign, "Sign");
+ m_Writer.AddString("Text1", a_Sign->GetLine(0));
+ m_Writer.AddString("Text2", a_Sign->GetLine(1));
+ m_Writer.AddString("Text3", a_Sign->GetLine(2));
+ m_Writer.AddString("Text4", a_Sign->GetLine(3));
m_Writer.EndCompound();
}
@@ -428,6 +443,7 @@ void cNBTChunkSerializer::BlockEntity(cBlockEntity * a_Entity)
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;
diff --git a/source/WorldStorage/NBTChunkSerializer.h b/source/WorldStorage/NBTChunkSerializer.h
index 47389dfe8..14c31be01 100644
--- a/source/WorldStorage/NBTChunkSerializer.h
+++ b/source/WorldStorage/NBTChunkSerializer.h
@@ -20,12 +20,13 @@ class cFastNBTWriter;
class cEntity;
class cBlockEntity;
class cChestEntity;
-class cFurnaceEntity;
class cDispenserEntity;
class cDropperEntity;
-class cSignEntity;
-class cNoteEntity;
+class cFurnaceEntity;
+class cHopperEntity;
class cJukeboxEntity;
+class cNoteEntity;
+class cSignEntity;
class cFallingBlock;
class cMinecart;
class cMinecartWithChest;
@@ -78,17 +79,18 @@ protected:
void AddItemGrid(const cItemGrid & a_Grid, int a_BeginSlotNum = 0);
// Block entities:
- void AddBasicTileEntity(cBlockEntity * a_Entity, const char * a_EntityTypeID);
- void AddChestEntity(cChestEntity * a_Entity);
+ void AddBasicTileEntity(cBlockEntity * a_Entity, const char * a_EntityTypeID);
+ void AddChestEntity (cChestEntity * a_Entity);
void AddDispenserEntity(cDispenserEntity * a_Entity);
- void AddDropperEntity(cDropperEntity * a_Entity);
- void AddFurnaceEntity(cFurnaceEntity * a_Furnace);
- void AddSignEntity(cSignEntity * a_Sign);
- void AddNoteEntity(cNoteEntity * a_Note);
- void AddJukeboxEntity(cJukeboxEntity * a_Jukebox);
- void AddBasicEntity(cEntity * a_Entity, const AString & a_ClassName);
+ void AddDropperEntity (cDropperEntity * a_Entity);
+ void AddFurnaceEntity (cFurnaceEntity * a_Furnace);
+ void AddHopperEntity (cHopperEntity * a_Entity);
+ void AddJukeboxEntity (cJukeboxEntity * a_Jukebox);
+ void AddNoteEntity (cNoteEntity * a_Note);
+ void AddSignEntity (cSignEntity * a_Sign);
// Entities:
+ void AddBasicEntity (cEntity * a_Entity, const AString & a_ClassName);
void AddFallingBlockEntity(cFallingBlock * a_FallingBlock);
void AddMinecartEntity (cMinecart * a_Minecart);
void AddMonsterEntity (cMonster * a_Monster);
diff --git a/source/WorldStorage/WSSAnvil.cpp b/source/WorldStorage/WSSAnvil.cpp
index 1b70083c2..43e6dce05 100644
--- a/source/WorldStorage/WSSAnvil.cpp
+++ b/source/WorldStorage/WSSAnvil.cpp
@@ -13,6 +13,7 @@
#include "../BlockEntities/DispenserEntity.h"
#include "../BlockEntities/DropperEntity.h"
#include "../BlockEntities/FurnaceEntity.h"
+#include "../BlockEntities/HopperEntity.h"
#include "../BlockEntities/JukeboxEntity.h"
#include "../BlockEntities/NoteEntity.h"
#include "../BlockEntities/SignEntity.h"
@@ -568,6 +569,10 @@ void cWSSAnvil::LoadBlockEntitiesFromNBT(cBlockEntityList & a_BlockEntities, con
{
LoadFurnaceFromNBT(a_BlockEntities, a_NBT, Child);
}
+ else if (strncmp(a_NBT.GetData(sID), "Hopper", a_NBT.GetDataLength(sID)) == 0)
+ {
+ LoadHopperFromNBT(a_BlockEntities, a_NBT, Child);
+ }
else if (strncmp(a_NBT.GetData(sID), "Music", a_NBT.GetDataLength(sID)) == 0)
{
LoadNoteFromNBT(a_BlockEntities, a_NBT, Child);
@@ -721,7 +726,7 @@ void cWSSAnvil::LoadDropperFromNBT(cBlockEntityList & a_BlockEntities, const cPa
int Items = a_NBT.FindChildByName(a_TagIdx, "Items");
if ((Items < 0) || (a_NBT.GetType(Items) != TAG_List))
{
- return; // Make it an empty dispenser - the chunk loader will provide an empty cDispenserEntity for this
+ return; // Make it an empty dropper - the chunk loader will provide an empty cDropperEntity for this
}
std::auto_ptr<cDropperEntity> Dropper(new cDropperEntity(x, y, z, m_World));
LoadItemGridFromNBT(Dropper->GetContents(), a_NBT, Items);
@@ -781,7 +786,7 @@ void cWSSAnvil::LoadFurnaceFromNBT(cBlockEntityList & a_BlockEntities, const cPa
-void cWSSAnvil::LoadSignFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx)
+void cWSSAnvil::LoadHopperFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx)
{
ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound);
int x, y, z;
@@ -789,38 +794,41 @@ void cWSSAnvil::LoadSignFromNBT(cBlockEntityList & a_BlockEntities, const cParse
{
return;
}
- std::auto_ptr<cSignEntity> Sign(new cSignEntity(E_BLOCK_SIGN_POST, x, y, z, m_World));
-
- int currentLine = a_NBT.FindChildByName(a_TagIdx, "Text1");
- if (currentLine >= 0)
+ int Items = a_NBT.FindChildByName(a_TagIdx, "Items");
+ if ((Items < 0) || (a_NBT.GetType(Items) != TAG_List))
{
- Sign->SetLine(0, a_NBT.GetString(currentLine));
+ return; // Make it an empty hopper - the chunk loader will provide an empty cHopperEntity for this
}
+ std::auto_ptr<cHopperEntity> Hopper(new cHopperEntity(x, y, z, m_World));
+ LoadItemGridFromNBT(Hopper->GetContents(), a_NBT, Items);
+ a_BlockEntities.push_back(Hopper.release());
+}
- currentLine = a_NBT.FindChildByName(a_TagIdx, "Text2");
- if (currentLine >= 0)
- {
- Sign->SetLine(1, a_NBT.GetString(currentLine));
- }
- currentLine = a_NBT.FindChildByName(a_TagIdx, "Text3");
- if (currentLine >= 0)
+
+
+
+void cWSSAnvil::LoadJukeboxFromNBT(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))
{
- Sign->SetLine(2, a_NBT.GetString(currentLine));
+ return;
}
-
- currentLine = a_NBT.FindChildByName(a_TagIdx, "Text4");
- if (currentLine >= 0)
+ std::auto_ptr<cJukeboxEntity> Jukebox(new cJukeboxEntity(x, y, z, m_World));
+ int Record = a_NBT.FindChildByName(a_TagIdx, "Record");
+ if (Record >= 0)
{
- Sign->SetLine(3, a_NBT.GetString(currentLine));
+ Jukebox->SetRecord(a_NBT.GetInt(Record));
}
-
- a_BlockEntities.push_back(Sign.release());
+ a_BlockEntities.push_back(Jukebox.release());
}
+
void cWSSAnvil::LoadNoteFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx)
{
ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound);
@@ -842,7 +850,7 @@ void cWSSAnvil::LoadNoteFromNBT(cBlockEntityList & a_BlockEntities, const cParse
-void cWSSAnvil::LoadJukeboxFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx)
+void cWSSAnvil::LoadSignFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx)
{
ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound);
int x, y, z;
@@ -850,13 +858,33 @@ void cWSSAnvil::LoadJukeboxFromNBT(cBlockEntityList & a_BlockEntities, const cPa
{
return;
}
- std::auto_ptr<cJukeboxEntity> Jukebox(new cJukeboxEntity(x, y, z, m_World));
- int Record = a_NBT.FindChildByName(a_TagIdx, "Record");
- if (Record >= 0)
+ std::auto_ptr<cSignEntity> Sign(new cSignEntity(E_BLOCK_SIGN_POST, x, y, z, m_World));
+
+ int currentLine = a_NBT.FindChildByName(a_TagIdx, "Text1");
+ if (currentLine >= 0)
{
- Jukebox->SetRecord(a_NBT.GetInt(Record));
+ Sign->SetLine(0, a_NBT.GetString(currentLine));
}
- a_BlockEntities.push_back(Jukebox.release());
+
+ currentLine = a_NBT.FindChildByName(a_TagIdx, "Text2");
+ if (currentLine >= 0)
+ {
+ Sign->SetLine(1, a_NBT.GetString(currentLine));
+ }
+
+ currentLine = a_NBT.FindChildByName(a_TagIdx, "Text3");
+ if (currentLine >= 0)
+ {
+ Sign->SetLine(2, a_NBT.GetString(currentLine));
+ }
+
+ currentLine = a_NBT.FindChildByName(a_TagIdx, "Text4");
+ if (currentLine >= 0)
+ {
+ Sign->SetLine(3, a_NBT.GetString(currentLine));
+ }
+
+ a_BlockEntities.push_back(Sign.release());
}
diff --git a/source/WorldStorage/WSSAnvil.h b/source/WorldStorage/WSSAnvil.h
index 7281d687d..2dc17d2e9 100644
--- a/source/WorldStorage/WSSAnvil.h
+++ b/source/WorldStorage/WSSAnvil.h
@@ -131,9 +131,10 @@ protected:
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);
- void LoadSignFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
- void LoadNoteFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
+ 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 LoadEntityFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_EntityTagIdx, const char * a_IDTag, int a_IDTagLength);