summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--MCServer/Plugins/APIDump/APIDesc.lua2
-rw-r--r--MCServer/Plugins/APIDump/Hooks/OnPlayerPlacedBlock.lua10
-rw-r--r--MCServer/Plugins/APIDump/Hooks/OnPlayerPlacingBlock.lua10
-rw-r--r--src/Bindings/Plugin.h4
-rw-r--r--src/Bindings/PluginLua.cpp18
-rw-r--r--src/Bindings/PluginLua.h4
-rw-r--r--src/Bindings/PluginManager.cpp8
-rw-r--r--src/Bindings/PluginManager.h6
-rw-r--r--src/Blocks/BlockBed.cpp20
-rw-r--r--src/Blocks/BlockBed.h1
-rw-r--r--src/Blocks/BlockBigFlower.h12
-rw-r--r--src/Blocks/BlockChest.h65
-rw-r--r--src/Blocks/BlockDoor.cpp40
-rw-r--r--src/Blocks/BlockDoor.h39
-rw-r--r--src/Blocks/BlockHandler.cpp2
-rw-r--r--src/Blocks/BlockHandler.h21
-rw-r--r--src/Blocks/BlockMobHead.h204
-rw-r--r--src/Blocks/BlockPumpkin.h64
-rw-r--r--src/Blocks/BlockSignPost.h11
-rw-r--r--src/Blocks/BlockWallSign.h11
-rw-r--r--src/Chunk.cpp16
-rw-r--r--src/ChunkDef.h49
-rw-r--r--src/ChunkMap.cpp103
-rw-r--r--src/ChunkMap.h23
-rw-r--r--src/ClientHandle.cpp158
-rw-r--r--src/ClientHandle.h3
-rw-r--r--src/Entities/Player.cpp96
-rw-r--r--src/Entities/Player.h19
-rw-r--r--src/Generating/StructGen.cpp14
-rw-r--r--src/Generating/Trees.cpp14
-rw-r--r--src/Items/CMakeLists.txt7
-rw-r--r--src/Items/ItemBed.h28
-rw-r--r--src/Items/ItemBigFlower.h56
-rw-r--r--src/Items/ItemChest.h167
-rw-r--r--src/Items/ItemDoor.h96
-rw-r--r--src/Items/ItemDye.h25
-rw-r--r--src/Items/ItemHandler.cpp199
-rw-r--r--src/Items/ItemHandler.h39
-rw-r--r--src/Items/ItemMobHead.h261
-rw-r--r--src/Items/ItemPumpkin.h156
-rw-r--r--src/Items/ItemRedstoneDust.h37
-rw-r--r--src/Items/ItemSign.h22
-rw-r--r--src/Items/ItemSlab.h93
-rw-r--r--src/Protocol/Protocol17x.cpp4
-rw-r--r--src/Protocol/Protocol18x.cpp4
-rw-r--r--src/Vector3.h16
-rw-r--r--src/World.cpp13
-rw-r--r--src/World.h5
48 files changed, 1489 insertions, 786 deletions
diff --git a/MCServer/Plugins/APIDump/APIDesc.lua b/MCServer/Plugins/APIDump/APIDesc.lua
index ba3763724..b2c7108e9 100644
--- a/MCServer/Plugins/APIDump/APIDesc.lua
+++ b/MCServer/Plugins/APIDump/APIDesc.lua
@@ -1840,7 +1840,9 @@ a_Player:OpenWindow(Window);
MoveToWorld = { Params = "WorldName", Return = "bool", Return = "Moves the player to the specified world. Returns true if successful." },
OpenWindow = { Params = "{{cWindow|Window}}", Return = "", Notes = "Opens the specified UI window for the player." },
PermissionMatches = { Params = "Permission, Template", Return = "bool", Notes = "(STATIC) Returns true if the specified permission matches the specified template. The template may contain wildcards." },
+ PlaceBlock = { Params = "BlockX, BlockY, BlockZ, BlockType, BlockMeta", Return = "bool", Notes = "Places a block while impersonating the player. The {{OnPlayerPlacingBlock|HOOK_PLAYER_PLACING_BLOCK}} hook is called before the placement, and if it succeeds, the block is placed and the {{OnPlayerPlacedBlock|HOOK_PLAYER_PLACED_BLOCK}} hook is called. Returns true iff the block is successfully placed. Assumes that the block is in a currently loaded chunk." },
Respawn = { Params = "", Return = "", Notes = "Restores the health, extinguishes fire, makes visible and sends the Respawn packet." },
+ SendBlocksAround = { Params = "BlockX, BlockY, BlockZ, [Range]", Return = "", Notes = "Sends all the world's blocks in Range from the specified coords to the player, as a BlockChange packet. Range defaults to 1 (only one block sent)." },
SendMessage = { Params = "Message", Return = "", Notes = "Sends the specified message to the player." },
SendMessageFailure = { Params = "Message", Return = "", Notes = "Prepends Rose [INFO] / colours entire text (depending on ShouldUseChatPrefixes()) and sends message to player. For a command that failed to run because of insufficient permissions, etc." },
SendMessageFatal = { Params = "Message", Return = "", Notes = "Prepends Red [FATAL] / colours entire text (depending on ShouldUseChatPrefixes()) and sends message to player. For something serious, such as a plugin crash, etc." },
diff --git a/MCServer/Plugins/APIDump/Hooks/OnPlayerPlacedBlock.lua b/MCServer/Plugins/APIDump/Hooks/OnPlayerPlacedBlock.lua
index 54888a6db..6445a76b4 100644
--- a/MCServer/Plugins/APIDump/Hooks/OnPlayerPlacedBlock.lua
+++ b/MCServer/Plugins/APIDump/Hooks/OnPlayerPlacedBlock.lua
@@ -12,7 +12,11 @@ return
Use the {{cPlayer}}:GetWorld() function to get the world to which the block belongs.</p>
<p>
See also the {{OnPlayerPlacingBlock|HOOK_PLAYER_PLACING_BLOCK}} hook for a similar hook called
- before the placement.
+ before the placement.</p>
+ <p>
+ If the client action results in multiple blocks being placed (such as a bed or a door), each separate
+ block is reported through this hook. All the blocks are already present in the world before the first
+ instance of this hook is called.
]],
Params =
{
@@ -20,10 +24,6 @@ return
{ Name = "BlockX", Type = "number", Notes = "X-coord of the block" },
{ Name = "BlockY", Type = "number", Notes = "Y-coord of the block" },
{ Name = "BlockZ", Type = "number", Notes = "Z-coord of the block" },
- { Name = "BlockFace", Type = "number", Notes = "Face of the existing block upon which the player interacted. One of the BLOCK_FACE_ constants" },
- { Name = "CursorX", Type = "number", Notes = "X-coord of the cursor within the block face (0 .. 15)" },
- { Name = "CursorY", Type = "number", Notes = "Y-coord of the cursor within the block face (0 .. 15)" },
- { Name = "CursorZ", Type = "number", Notes = "Z-coord of the cursor within the block face (0 .. 15)" },
{ Name = "BlockType", Type = "BLOCKTYPE", Notes = "The block type of the block" },
{ Name = "BlockMeta", Type = "NIBBLETYPE", Notes = "The block meta of the block" },
},
diff --git a/MCServer/Plugins/APIDump/Hooks/OnPlayerPlacingBlock.lua b/MCServer/Plugins/APIDump/Hooks/OnPlayerPlacingBlock.lua
index 2a928390b..4241a09aa 100644
--- a/MCServer/Plugins/APIDump/Hooks/OnPlayerPlacingBlock.lua
+++ b/MCServer/Plugins/APIDump/Hooks/OnPlayerPlacingBlock.lua
@@ -15,7 +15,11 @@ return
Use the {{cPlayer}}:GetWorld() function to get the world to which the block belongs.</p>
<p>
See also the {{OnPlayerPlacedBlock|HOOK_PLAYER_PLACED_BLOCK}} hook for a similar hook called after
- the placement.
+ the placement.</p>
+ <p>
+ If the client action results in multiple blocks being placed (such as a bed or a door), each separate
+ block is reported through this hook and only if all of them succeed, all the blocks are placed. If
+ any one of the calls are refused by the plugin, all the blocks are refused and reverted on the client.
]],
Params =
{
@@ -23,10 +27,6 @@ return
{ Name = "BlockX", Type = "number", Notes = "X-coord of the block" },
{ Name = "BlockY", Type = "number", Notes = "Y-coord of the block" },
{ Name = "BlockZ", Type = "number", Notes = "Z-coord of the block" },
- { Name = "BlockFace", Type = "number", Notes = "Face of the existing block upon which the player is interacting. One of the BLOCK_FACE_ constants" },
- { Name = "CursorX", Type = "number", Notes = "X-coord of the cursor within the block face (0 .. 15)" },
- { Name = "CursorY", Type = "number", Notes = "Y-coord of the cursor within the block face (0 .. 15)" },
- { Name = "CursorZ", Type = "number", Notes = "Z-coord of the cursor within the block face (0 .. 15)" },
{ Name = "BlockType", Type = "BLOCKTYPE", Notes = "The block type of the block" },
{ Name = "BlockMeta", Type = "NIBBLETYPE", Notes = "The block meta of the block" },
},
diff --git a/src/Bindings/Plugin.h b/src/Bindings/Plugin.h
index 08677553c..b5944e9cb 100644
--- a/src/Bindings/Plugin.h
+++ b/src/Bindings/Plugin.h
@@ -76,8 +76,8 @@ public:
virtual bool OnPlayerJoined (cPlayer & a_Player) = 0;
virtual bool OnPlayerLeftClick (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status) = 0;
virtual bool OnPlayerMoving (cPlayer & a_Player, const Vector3d & a_OldPosition, const Vector3d & a_NewPosition) = 0;
- virtual bool OnPlayerPlacedBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) = 0;
- virtual bool OnPlayerPlacingBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) = 0;
+ virtual bool OnPlayerPlacedBlock (cPlayer & a_Player, const sSetBlock & a_BlockChange) = 0;
+ 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 OnPlayerShooting (cPlayer & a_Player) = 0;
diff --git a/src/Bindings/PluginLua.cpp b/src/Bindings/PluginLua.cpp
index ea782ea3f..cc3146610 100644
--- a/src/Bindings/PluginLua.cpp
+++ b/src/Bindings/PluginLua.cpp
@@ -857,14 +857,19 @@ bool cPluginLua::OnPlayerMoving(cPlayer & a_Player, const Vector3d & a_OldPositi
-bool cPluginLua::OnPlayerPlacedBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
+bool cPluginLua::OnPlayerPlacedBlock(cPlayer & a_Player, const sSetBlock & a_BlockChange)
{
cCSLock Lock(m_CriticalSection);
bool res = false;
cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_PLACED_BLOCK];
for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
{
- m_LuaState.Call((int)(**itr), &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, a_BlockType, a_BlockMeta, cLuaState::Return, res);
+ m_LuaState.Call((int)(**itr), &a_Player,
+ a_BlockChange.GetX(), a_BlockChange.GetY(), a_BlockChange.GetZ(),
+ a_BlockChange.m_BlockType, a_BlockChange.m_BlockMeta,
+ cLuaState::Return,
+ res
+ );
if (res)
{
return true;
@@ -877,14 +882,19 @@ bool cPluginLua::OnPlayerPlacedBlock(cPlayer & a_Player, int a_BlockX, int a_Blo
-bool cPluginLua::OnPlayerPlacingBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
+bool cPluginLua::OnPlayerPlacingBlock(cPlayer & a_Player, const sSetBlock & a_BlockChange)
{
cCSLock Lock(m_CriticalSection);
bool res = false;
cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_PLACING_BLOCK];
for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
{
- m_LuaState.Call((int)(**itr), &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, a_BlockType, a_BlockMeta, cLuaState::Return, res);
+ m_LuaState.Call((int)(**itr), &a_Player,
+ a_BlockChange.GetX(), a_BlockChange.GetY(), a_BlockChange.GetZ(),
+ a_BlockChange.m_BlockType, a_BlockChange.m_BlockMeta,
+ cLuaState::Return,
+ res
+ );
if (res)
{
return true;
diff --git a/src/Bindings/PluginLua.h b/src/Bindings/PluginLua.h
index 7de5ffec4..ad3f82b42 100644
--- a/src/Bindings/PluginLua.h
+++ b/src/Bindings/PluginLua.h
@@ -100,8 +100,8 @@ public:
virtual bool OnPlayerJoined (cPlayer & a_Player) override;
virtual bool OnPlayerLeftClick (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status) override;
virtual bool OnPlayerMoving (cPlayer & a_Player, const Vector3d & a_OldPosition, const Vector3d & a_NewPosition) override;
- virtual bool OnPlayerPlacedBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override;
- virtual bool OnPlayerPlacingBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override;
+ virtual bool OnPlayerPlacedBlock (cPlayer & a_Player, const sSetBlock & a_BlockChange) override;
+ 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 OnPlayerShooting (cPlayer & a_Player) override;
diff --git a/src/Bindings/PluginManager.cpp b/src/Bindings/PluginManager.cpp
index 406a540f4..fad0a36d2 100644
--- a/src/Bindings/PluginManager.cpp
+++ b/src/Bindings/PluginManager.cpp
@@ -866,14 +866,14 @@ bool cPluginManager::CallHookPlayerMoving(cPlayer & a_Player, const Vector3d a_O
-bool cPluginManager::CallHookPlayerPlacedBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
+bool cPluginManager::CallHookPlayerPlacedBlock(cPlayer & a_Player, const sSetBlock & a_BlockChange)
{
FIND_HOOK(HOOK_PLAYER_PLACED_BLOCK);
VERIFY_HOOK;
for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
{
- if ((*itr)->OnPlayerPlacedBlock(a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, a_BlockType, a_BlockMeta))
+ if ((*itr)->OnPlayerPlacedBlock(a_Player, a_BlockChange))
{
return true;
}
@@ -885,14 +885,14 @@ bool cPluginManager::CallHookPlayerPlacedBlock(cPlayer & a_Player, int a_BlockX,
-bool cPluginManager::CallHookPlayerPlacingBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
+bool cPluginManager::CallHookPlayerPlacingBlock(cPlayer & a_Player, const sSetBlock & a_BlockChange)
{
FIND_HOOK(HOOK_PLAYER_PLACING_BLOCK);
VERIFY_HOOK;
for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
{
- if ((*itr)->OnPlayerPlacingBlock(a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, a_BlockType, a_BlockMeta))
+ if ((*itr)->OnPlayerPlacingBlock(a_Player, a_BlockChange))
{
return true;
}
diff --git a/src/Bindings/PluginManager.h b/src/Bindings/PluginManager.h
index 3a2aecc92..d4b82376a 100644
--- a/src/Bindings/PluginManager.h
+++ b/src/Bindings/PluginManager.h
@@ -207,10 +207,10 @@ public:
bool CallHookPlayerFishing (cPlayer & a_Player, cItems a_Reward);
bool CallHookPlayerFoodLevelChange (cPlayer & a_Player, int a_NewFoodLevel);
bool CallHookPlayerJoined (cPlayer & a_Player);
- bool CallHookPlayerMoving (cPlayer & a_Player, const Vector3d a_OldPosition, const Vector3d a_NewPosition);
bool CallHookPlayerLeftClick (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status);
- bool CallHookPlayerPlacedBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
- bool CallHookPlayerPlacingBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
+ bool CallHookPlayerMoving (cPlayer & a_Player, const Vector3d a_OldPosition, const Vector3d a_NewPosition);
+ bool CallHookPlayerPlacedBlock (cPlayer & a_Player, const sSetBlock & a_BlockChange);
+ 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 CallHookPlayerShooting (cPlayer & a_Player);
diff --git a/src/Blocks/BlockBed.cpp b/src/Blocks/BlockBed.cpp
index 3b6328b38..57b9855d0 100644
--- a/src/Blocks/BlockBed.cpp
+++ b/src/Blocks/BlockBed.cpp
@@ -14,24 +14,6 @@
-void cBlockBedHandler::OnPlacedByPlayer(
- cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player,
- int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
- int a_CursorX, int a_CursorY, int a_CursorZ,
- BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta
-)
-{
- if (a_BlockMeta < 8)
- {
- Vector3i Direction = MetaDataToDirection(a_BlockMeta);
- a_ChunkInterface.SetBlock(a_BlockX + Direction.x, a_BlockY, a_BlockZ + Direction.z, E_BLOCK_BED, a_BlockMeta | 0x8);
- }
-}
-
-
-
-
-
void cBlockBedHandler::OnDestroyed(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, int a_BlockX, int a_BlockY, int a_BlockZ)
{
NIBBLETYPE OldMeta = a_ChunkInterface.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
@@ -151,7 +133,7 @@ void cBlockBedHandler::OnUse(cChunkInterface & a_ChunkInterface, cWorldInterface
cPlayerBedStateUnsetter Unsetter(Vector3i(a_BlockX + PillowDirection.x, a_BlockY, a_BlockZ + PillowDirection.z), a_WorldInterface);
a_WorldInterface.ForEachPlayer(Unsetter);
a_WorldInterface.SetTimeOfDay(0);
- a_ChunkInterface.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta & 0xB); // Where 0xB = 1011, and zero is to make sure 'occupied' bit is always unset
+ a_ChunkInterface.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta & 0x0b); // Clear the "occupied" bit of the bed's block
}
}
}
diff --git a/src/Blocks/BlockBed.h b/src/Blocks/BlockBed.h
index a8b5be899..5b746110a 100644
--- a/src/Blocks/BlockBed.h
+++ b/src/Blocks/BlockBed.h
@@ -23,7 +23,6 @@ public:
}
- virtual void OnPlacedByPlayer(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override;
virtual void OnDestroyed(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, int a_BlockX, int a_BlockY, int a_BlockZ) override;
virtual void OnUse(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override;
diff --git a/src/Blocks/BlockBigFlower.h b/src/Blocks/BlockBigFlower.h
index 3577bdd40..5240ddf53 100644
--- a/src/Blocks/BlockBigFlower.h
+++ b/src/Blocks/BlockBigFlower.h
@@ -85,18 +85,6 @@ public:
}
- virtual void OnPlacedByPlayer(
- cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player,
- int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
- int a_CursorX, int a_CursorY, int a_CursorZ,
- BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta
- ) override
- {
- int Meta = (((int)floor(a_Player->GetYaw() * 4.0 / 360.0 + 0.5) & 0x3) + 2) % 4;
- a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY + 1, a_BlockZ, m_BlockType, 0x8 | Meta);
- }
-
-
virtual void OnDestroyed(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, int a_BlockX, int a_BlockY, int a_BlockZ) override
{
NIBBLETYPE OldMeta = a_ChunkInterface.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
diff --git a/src/Blocks/BlockChest.h b/src/Blocks/BlockChest.h
index 201f2309b..01fec7f8b 100644
--- a/src/Blocks/BlockChest.h
+++ b/src/Blocks/BlockChest.h
@@ -62,50 +62,11 @@ public:
}
// Single chest, get meta from rotation only
- a_BlockMeta = RotationToMetaData(yaw);
+ a_BlockMeta = PlayerYawToMetaData(yaw);
return true;
}
- virtual void OnPlacedByPlayer(
- cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player,
- int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
- int a_CursorX, int a_CursorY, int a_CursorZ,
- BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta
- ) override
- {
- // Check if this forms a doublechest, if so, need to adjust the meta:
- cBlockArea Area;
- if (!Area.Read(&a_ChunkInterface, a_BlockX - 1, a_BlockX + 1, a_BlockY, a_BlockY, a_BlockZ - 1, a_BlockZ + 1))
- {
- return;
- }
-
- double rot = a_Player->GetYaw(); // FIXME: Rename rot to yaw
- // Choose meta from player rotation, choose only between 2 or 3
- NIBBLETYPE NewMeta = ((rot >= -90) && (rot < 90)) ? 2 : 3;
- if (
- CheckAndAdjustNeighbor(a_ChunkInterface, Area, 0, 1, NewMeta) ||
- CheckAndAdjustNeighbor(a_ChunkInterface, Area, 2, 1, NewMeta)
- )
- {
- // Forming a double chest in the X direction
- return;
- }
- // Choose meta from player rotation, choose only between 4 or 5
- NewMeta = (rot < 0) ? 4 : 5;
- if (
- CheckAndAdjustNeighbor(a_ChunkInterface, Area, 1, 0, NewMeta) ||
- CheckAndAdjustNeighbor(a_ChunkInterface, Area, 2, 2, NewMeta)
- )
- {
- // Forming a double chest in the Z direction
- return;
- }
-
- // Single chest, no further processing needed
- }
-
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override
{
int BlockX = a_RelX + a_Chunk.GetPosX() * cChunkDef::Width;
@@ -180,30 +141,30 @@ public:
}
- /// Translates player rotation when placing a chest into the chest block metadata. Valid for single chests only
- static NIBBLETYPE RotationToMetaData(double a_Rotation)
+ /** Translates player yaw when placing a chest into the chest block metadata. Valid for single chests only */
+ static NIBBLETYPE PlayerYawToMetaData(double a_Yaw)
{
- a_Rotation += 90 + 45; // So its not aligned with axis
+ a_Yaw += 90 + 45; // So its not aligned with axis
- if (a_Rotation > 360.f)
+ if (a_Yaw > 360.f)
{
- a_Rotation -= 360.f;
+ a_Yaw -= 360.f;
}
- if ((a_Rotation >= 0.f) && (a_Rotation < 90.f))
+ if ((a_Yaw >= 0.f) && (a_Yaw < 90.f))
{
- return 0x4;
+ return 0x04;
}
- else if ((a_Rotation >= 180) && (a_Rotation < 270))
+ else if ((a_Yaw >= 180) && (a_Yaw < 270))
{
- return 0x5;
+ return 0x05;
}
- else if ((a_Rotation >= 90) && (a_Rotation < 180))
+ else if ((a_Yaw >= 90) && (a_Yaw < 180))
{
- return 0x2;
+ return 0x02;
}
else
{
- return 0x3;
+ return 0x03;
}
}
diff --git a/src/Blocks/BlockDoor.cpp b/src/Blocks/BlockDoor.cpp
index 90b7b15c2..d2bf180be 100644
--- a/src/Blocks/BlockDoor.cpp
+++ b/src/Blocks/BlockDoor.cpp
@@ -23,7 +23,7 @@ void cBlockDoorHandler::OnDestroyed(cChunkInterface & a_ChunkInterface, cWorldIn
if (OldMeta & 8)
{
// Was upper part of door
- if (IsDoor(a_ChunkInterface.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ)))
+ if (IsDoorBlockType(a_ChunkInterface.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ)))
{
a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0);
}
@@ -31,7 +31,7 @@ void cBlockDoorHandler::OnDestroyed(cChunkInterface & a_ChunkInterface, cWorldIn
else
{
// Was lower part
- if (IsDoor(a_ChunkInterface.GetBlock(a_BlockX, a_BlockY + 1, a_BlockZ)))
+ if (IsDoorBlockType(a_ChunkInterface.GetBlock(a_BlockX, a_BlockY + 1, a_BlockZ)))
{
a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY + 1, a_BlockZ, E_BLOCK_AIR, 0);
}
@@ -84,52 +84,34 @@ void cBlockDoorHandler::OnCancelRightClick(cChunkInterface & a_ChunkInterface, c
-void cBlockDoorHandler::OnPlacedByPlayer(
- cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player,
- int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
- int a_CursorX, int a_CursorY, int a_CursorZ,
- BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta
-)
-{
- NIBBLETYPE a_TopBlockMeta = 8;
- if (
- ((a_BlockMeta == 0) && (a_ChunkInterface.GetBlock(a_BlockX, a_BlockY, a_BlockZ - 1) == m_BlockType)) ||
- ((a_BlockMeta == 1) && (a_ChunkInterface.GetBlock(a_BlockX + 1, a_BlockY, a_BlockZ) == m_BlockType)) ||
- ((a_BlockMeta == 2) && (a_ChunkInterface.GetBlock(a_BlockX, a_BlockY, a_BlockZ + 1) == m_BlockType)) ||
- ((a_BlockMeta == 3) && (a_ChunkInterface.GetBlock(a_BlockX - 1, a_BlockY, a_BlockZ) == m_BlockType))
- )
- {
- a_TopBlockMeta = 9;
- }
- a_ChunkInterface.SetBlock(a_BlockX, a_BlockY + 1, a_BlockZ, m_BlockType, a_TopBlockMeta);
-}
-
-
-
-
-
NIBBLETYPE cBlockDoorHandler::MetaRotateCCW(NIBBLETYPE a_Meta)
{
if (a_Meta & 0x08)
{
+ // The meta doesn't change for the top block
return a_Meta;
}
else
{
+ // Rotate the bottom block
return super::MetaRotateCCW(a_Meta);
}
}
+
+
NIBBLETYPE cBlockDoorHandler::MetaRotateCW(NIBBLETYPE a_Meta)
{
if (a_Meta & 0x08)
{
+ // The meta doesn't change for the top block
return a_Meta;
}
else
{
+ // Rotate the bottom block
return super::MetaRotateCW(a_Meta);
}
}
@@ -138,8 +120,10 @@ NIBBLETYPE cBlockDoorHandler::MetaRotateCW(NIBBLETYPE a_Meta)
NIBBLETYPE cBlockDoorHandler::MetaMirrorXY(NIBBLETYPE a_Meta)
{
- // Top bit (0x08) contains door panel type (Top/Bottom panel) Only Bottom panels contain position data
- // Return a_Meta if panel is a top panel (0x08 bit is set to 1)
+ /*
+ Top bit (0x08) contains door block position (Top / Bottom). Only Bottom blocks contain position data
+ Return a_Meta if panel is a top panel (0x08 bit is set to 1)
+ */
// Note: Currently, you can not properly mirror the hinges on a double door. The orientation of the door is stored
// in only the bottom tile while the hinge position is in the top tile. This function only operates on one tile at a time,
diff --git a/src/Blocks/BlockDoor.h b/src/Blocks/BlockDoor.h
index 92ad8da12..334519077 100644
--- a/src/Blocks/BlockDoor.h
+++ b/src/Blocks/BlockDoor.h
@@ -101,14 +101,6 @@ public:
}
- virtual void OnPlacedByPlayer(
- cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player,
- int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
- int a_CursorX, int a_CursorY, int a_CursorZ,
- BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta
- ) override;
-
-
virtual bool IsUseable(void) override
{
return true;
@@ -117,11 +109,19 @@ public:
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override
{
- return ((a_RelY > 0) && (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ) != E_BLOCK_AIR));
+ return ((a_RelY > 0) && CanBeOn(a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ)));
+ }
+
+
+ /** Returns true if door can be placed on the specified block type. */
+ static bool CanBeOn(BLOCKTYPE a_BlockType)
+ {
+ // Vanilla refuses to place doors on transparent blocks
+ return !cBlockInfo::IsTransparent(a_BlockType);
}
- bool CanReplaceBlock(BLOCKTYPE a_BlockType)
+ static bool CanReplaceBlock(BLOCKTYPE a_BlockType)
{
switch (a_BlockType)
{
@@ -170,8 +170,21 @@ public:
}
+ /** Returns a vector pointing one block in the direction the door is facing (where the outside is). */
+ inline static Vector3i GetRelativeDirectionToOutside(NIBBLETYPE a_BlockMeta)
+ {
+ switch (a_BlockMeta & 0x03)
+ {
+ case 0: return Vector3i(-1, 0, 0); // Facing West / XM
+ case 1: return Vector3i( 0, 0, -1); // Facing North / ZM
+ case 2: return Vector3i( 1, 0, 0); // Facing East / XP
+ default: return Vector3i( 0, 0, 1); // Facing South / ZP
+ }
+ }
+
+
/** Returns true if the specified blocktype is any kind of door */
- inline static bool IsDoor(BLOCKTYPE a_Block)
+ inline static bool IsDoorBlockType(BLOCKTYPE a_Block)
{
switch (a_Block)
{
@@ -193,6 +206,8 @@ public:
}
+ /** Returns true iff the door at the specified coords is open.
+ The coords may point to either the top part or the bottom part of the door. */
static NIBBLETYPE IsOpen(cChunkInterface & a_ChunkInterface, int a_BlockX, int a_BlockY, int a_BlockZ)
{
NIBBLETYPE Meta = GetCompleteDoorMeta(a_ChunkInterface, a_BlockX, a_BlockY, a_BlockZ);
@@ -237,7 +252,7 @@ public:
static void SetOpen(cChunkInterface & a_ChunkInterface, int a_BlockX, int a_BlockY, int a_BlockZ, bool a_Open)
{
BLOCKTYPE Block = a_ChunkInterface.GetBlock(a_BlockX, a_BlockY, a_BlockZ);
- if (!IsDoor(Block))
+ if (!IsDoorBlockType(Block))
{
return;
}
diff --git a/src/Blocks/BlockHandler.cpp b/src/Blocks/BlockHandler.cpp
index d532aa1dc..2de4a3e4c 100644
--- a/src/Blocks/BlockHandler.cpp
+++ b/src/Blocks/BlockHandler.cpp
@@ -369,7 +369,7 @@ void cBlockHandler::OnUpdate(cChunkInterface & cChunkInterface, cWorldInterface
-void cBlockHandler::OnPlacedByPlayer(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
+void cBlockHandler::OnPlacedByPlayer(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, const sSetBlock & a_BlockChange)
{
}
diff --git a/src/Blocks/BlockHandler.h b/src/Blocks/BlockHandler.h
index f2298afb5..4dec0dc95 100644
--- a/src/Blocks/BlockHandler.h
+++ b/src/Blocks/BlockHandler.h
@@ -42,15 +42,12 @@ public:
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
);
- /// Called by cWorld::SetBlock() after the block has been set
+ /** Called by cWorld::SetBlock() after the block has been set */
virtual void OnPlaced(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
- /// Called by cClientHandle::HandlePlaceBlock() after the player has placed a new block. Called after OnPlaced().
+ /** Called by cPlayer::PlaceBlocks() for each block after it has been set to the world. Called after OnPlaced(). */
virtual void OnPlacedByPlayer(
- cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player,
- int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
- int a_CursorX, int a_CursorY, int a_CursorZ,
- BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta
+ cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, const sSetBlock & a_BlockChange
);
/// Called before the player has destroyed a block
@@ -96,7 +93,8 @@ public:
*/
// virtual bool CanBePlacedAt(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir);
- /// Called to check whether this block supports a rclk action. If it returns true, OnUse() is called
+ /** Called to check whether this block supports a rclk action.
+ If it returns true, OnUse() is called */
virtual bool IsUseable(void);
/** Indicates whether the client will click through this block.
@@ -109,20 +107,21 @@ public:
*/
virtual bool DoesIgnoreBuildCollision(void);
- /// <summary>Similar to DoesIgnoreBuildCollision(void), but is used for cases where block meta/player item-in-hand is needed to determine collision (thin snow)</summary>
+ /** Similar to DoesIgnoreBuildCollision(void), but is used for cases where block's meta or
+ player's item-in-hand is needed to determine collision (thin snow) */
virtual bool DoesIgnoreBuildCollision(cPlayer *, NIBBLETYPE a_Meta)
{
UNUSED(a_Meta);
return DoesIgnoreBuildCollision();
}
- /// <summary>Returns if this block drops if it gets destroyed by an unsuitable situation. Default: true</summary>
+ /** Returns if this block drops if it gets destroyed by an unsuitable situation.
+ Default: true */
virtual bool DoesDropOnUnsuitable(void);
/** Called when one of the neighbors gets set; equivalent to MC block update.
By default drops if position no more suitable (CanBeAt(), DoesDropOnUnsuitable(), Drop()),
- and wakes up all simulators on the block.
- */
+ and wakes up all simulators on the block. */
virtual void Check(cChunkInterface & ChunkInterface, cBlockPluginInterface & a_PluginInterface, int a_RelX, int a_RelY, int a_RelZ, cChunk & a_Chunk);
/// <summary>Rotates a given block meta counter-clockwise. Default: no change</summary>
diff --git a/src/Blocks/BlockMobHead.h b/src/Blocks/BlockMobHead.h
index e21e42334..cb8143749 100644
--- a/src/Blocks/BlockMobHead.h
+++ b/src/Blocks/BlockMobHead.h
@@ -12,16 +12,18 @@ class cBlockMobHeadHandler :
public cBlockEntityHandler
{
public:
- cBlockMobHeadHandler(BLOCKTYPE a_BlockType)
- : cBlockEntityHandler(a_BlockType)
+ cBlockMobHeadHandler(BLOCKTYPE a_BlockType):
+ cBlockEntityHandler(a_BlockType)
{
}
+
virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
{
- // The drop spawn is in OnDestroyed method
+ // The drop spawn is in the OnDestroyedByPlayer method
}
+
virtual void OnDestroyedByPlayer(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) override
{
if (a_Player->IsGameModeCreative())
@@ -61,202 +63,6 @@ public:
a_WorldInterface.DoWithBlockEntityAt(a_BlockX, a_BlockY, a_BlockZ, Callback);
}
-
- bool TrySpawnWither(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, int a_BlockX, int a_BlockY, int a_BlockZ)
- {
- if (a_BlockY < 2)
- {
- return false;
- }
-
- class cCallback : public cBlockEntityCallback
- {
- bool m_IsWither;
-
- virtual bool Item(cBlockEntity * a_BlockEntity)
- {
- if (a_BlockEntity->GetBlockType() != E_BLOCK_HEAD)
- {
- return false;
- }
- cMobHeadEntity * MobHeadEntity = static_cast<cMobHeadEntity*>(a_BlockEntity);
-
- m_IsWither = (MobHeadEntity->GetType() == SKULL_TYPE_WITHER);
- return false;
- }
-
- public:
- cCallback () : m_IsWither(false) {}
-
- bool IsWither(void) const { return m_IsWither; }
-
- void Reset(void) { m_IsWither = false; }
-
- } CallbackA, CallbackB;
-
- class cPlayerCallback : public cPlayerListCallback
- {
- Vector3f m_Pos;
-
- virtual bool Item(cPlayer * a_Player)
- {
- // TODO 2014-05-21 xdot: Vanilla minecraft uses an AABB check instead of a radius one
- double Dist = (a_Player->GetPosition() - m_Pos).Length();
- if (Dist < 50.0)
- {
- // If player is close, award achievement
- a_Player->AwardAchievement(achSpawnWither);
- }
- return false;
- }
-
- public:
- cPlayerCallback(const Vector3f & a_Pos) : m_Pos(a_Pos) {}
-
- } PlayerCallback(Vector3f((float)a_BlockX, (float)a_BlockY, (float)a_BlockZ));
-
- a_WorldInterface.DoWithBlockEntityAt(a_BlockX, a_BlockY, a_BlockZ, CallbackA);
-
- if (!CallbackA.IsWither())
- {
- return false;
- }
-
- CallbackA.Reset();
-
- BLOCKTYPE BlockY1 = a_ChunkInterface.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ);
- BLOCKTYPE BlockY2 = a_ChunkInterface.GetBlock(a_BlockX, a_BlockY - 2, a_BlockZ);
-
- if ((BlockY1 != E_BLOCK_SOULSAND) || (BlockY2 != E_BLOCK_SOULSAND))
- {
- return false;
- }
-
- a_WorldInterface.DoWithBlockEntityAt(a_BlockX - 1, a_BlockY, a_BlockZ, CallbackA);
- a_WorldInterface.DoWithBlockEntityAt(a_BlockX + 1, a_BlockY, a_BlockZ, CallbackB);
-
- BLOCKTYPE Block1 = a_ChunkInterface.GetBlock(a_BlockX - 1, a_BlockY - 1, a_BlockZ);
- BLOCKTYPE Block2 = a_ChunkInterface.GetBlock(a_BlockX + 1, a_BlockY - 1, a_BlockZ);
-
- if ((Block1 == E_BLOCK_SOULSAND) && (Block2 == E_BLOCK_SOULSAND) && CallbackA.IsWither() && CallbackB.IsWither())
- {
- a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0);
- a_ChunkInterface.FastSetBlock(a_BlockX + 1, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0);
- a_ChunkInterface.FastSetBlock(a_BlockX - 1, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0);
- a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY - 2, a_BlockZ, E_BLOCK_AIR, 0);
-
- // Block entities
- a_ChunkInterface.SetBlock(a_BlockX + 1, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0);
- a_ChunkInterface.SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0);
- a_ChunkInterface.SetBlock(a_BlockX - 1, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0);
-
- // Spawn the wither:
- a_WorldInterface.SpawnMob(a_BlockX + 0.5, a_BlockY - 2, a_BlockZ + 0.5, mtWither);
-
- // Award Achievement
- a_WorldInterface.ForEachPlayer(PlayerCallback);
-
- return true;
- }
-
- CallbackA.Reset();
- CallbackB.Reset();
-
- a_WorldInterface.DoWithBlockEntityAt(a_BlockX, a_BlockY, a_BlockZ - 1, CallbackA);
- a_WorldInterface.DoWithBlockEntityAt(a_BlockX, a_BlockY, a_BlockZ + 1, CallbackB);
-
- Block1 = a_ChunkInterface.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ - 1);
- Block2 = a_ChunkInterface.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ + 1);
-
- if ((Block1 == E_BLOCK_SOULSAND) && (Block2 == E_BLOCK_SOULSAND) && CallbackA.IsWither() && CallbackB.IsWither())
- {
- a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0);
- a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ + 1, E_BLOCK_AIR, 0);
- a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ - 1, E_BLOCK_AIR, 0);
- a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY - 2, a_BlockZ, E_BLOCK_AIR, 0);
-
- // Block entities
- a_ChunkInterface.SetBlock(a_BlockX, a_BlockY, a_BlockZ + 1, E_BLOCK_AIR, 0);
- a_ChunkInterface.SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0);
- a_ChunkInterface.SetBlock(a_BlockX, a_BlockY, a_BlockZ - 1, E_BLOCK_AIR, 0);
-
- // Spawn the wither:
- a_WorldInterface.SpawnMob(a_BlockX + 0.5, a_BlockY - 2, a_BlockZ + 0.5, mtWither);
-
- // Award Achievement
- a_WorldInterface.ForEachPlayer(PlayerCallback);
-
- return true;
- }
-
- return false;
- }
-
- virtual void OnPlacedByPlayer(
- cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player,
- int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
- int a_CursorX, int a_CursorY, int a_CursorZ,
- BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta
- ) override
- {
- class cCallback : public cBlockEntityCallback
- {
- cPlayer * m_Player;
- NIBBLETYPE m_OldBlockMeta;
- NIBBLETYPE m_NewBlockMeta;
-
- virtual bool Item(cBlockEntity * a_BlockEntity)
- {
- if (a_BlockEntity->GetBlockType() != E_BLOCK_HEAD)
- {
- return false;
- }
- cMobHeadEntity * MobHeadEntity = static_cast<cMobHeadEntity*>(a_BlockEntity);
-
- int Rotation = 0;
- if (m_NewBlockMeta == 1)
- {
- Rotation = (int) floor(m_Player->GetYaw() * 16.0F / 360.0F + 0.5) & 0xF;
- }
-
- MobHeadEntity->SetType(static_cast<eMobHeadType>(m_OldBlockMeta));
- MobHeadEntity->SetRotation(static_cast<eMobHeadRotation>(Rotation));
- MobHeadEntity->GetWorld()->BroadcastBlockEntity(MobHeadEntity->GetPosX(), MobHeadEntity->GetPosY(), MobHeadEntity->GetPosZ());
- return false;
- }
-
- public:
- cCallback (cPlayer * a_CBPlayer, NIBBLETYPE a_OldBlockMeta, NIBBLETYPE a_NewBlockMeta) :
- m_Player(a_CBPlayer),
- m_OldBlockMeta(a_OldBlockMeta),
- m_NewBlockMeta(a_NewBlockMeta)
- {}
- };
- cCallback Callback(a_Player, a_BlockMeta, static_cast<NIBBLETYPE>(a_BlockFace));
-
- a_BlockMeta = (NIBBLETYPE)a_BlockFace;
- a_WorldInterface.DoWithBlockEntityAt(a_BlockX, a_BlockY, a_BlockZ, Callback);
- a_ChunkInterface.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, a_BlockMeta);
-
- if (a_BlockMeta == SKULL_TYPE_WITHER)
- {
- static const Vector3i Coords[] =
- {
- Vector3i( 0, 0, 0),
- Vector3i( 1, 0, 0),
- Vector3i(-1, 0, 0),
- Vector3i( 0, 0, 1),
- Vector3i( 0, 0, -1),
- };
- for (size_t i = 0; i < ARRAYCOUNT(Coords); ++i)
- {
- if (TrySpawnWither(a_ChunkInterface, a_WorldInterface, a_BlockX + Coords[i].x, a_BlockY, a_BlockZ + Coords[i].z))
- {
- break;
- }
- } // for i - Coords[]
- }
- }
} ;
diff --git a/src/Blocks/BlockPumpkin.h b/src/Blocks/BlockPumpkin.h
index 275d1422a..af00fbe8e 100644
--- a/src/Blocks/BlockPumpkin.h
+++ b/src/Blocks/BlockPumpkin.h
@@ -17,70 +17,6 @@ public:
}
- virtual void OnPlacedByPlayer(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override
- {
- // Check whether the pumpkin is a part of a golem or a snowman
-
- if (a_BlockY < 2)
- {
- // The pumpkin is too low for a golem / snowman
- return;
- }
-
- BLOCKTYPE BlockY1 = a_ChunkInterface.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ);
- BLOCKTYPE BlockY2 = a_ChunkInterface.GetBlock(a_BlockX, a_BlockY - 2, a_BlockZ);
-
- // Check for a snow golem:
- if ((BlockY1 == E_BLOCK_SNOW_BLOCK) && (BlockY2 == E_BLOCK_SNOW_BLOCK))
- {
- a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0);
- a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0);
- a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY - 2, a_BlockZ, E_BLOCK_AIR, 0);
- a_WorldInterface.SpawnMob(a_BlockX + 0.5, a_BlockY - 2, a_BlockZ + 0.5, mtSnowGolem);
- return;
- }
-
- // Check for an iron golem. First check only the body and legs, since those are the same for both orientations:
- if ((BlockY1 != E_BLOCK_IRON_BLOCK) || (BlockY2 != E_BLOCK_IRON_BLOCK))
- {
- // One of the blocks is not an iron, no chance of a golem here
- return;
- }
-
- // Now check both orientations for hands:
- if (
- (a_ChunkInterface.GetBlock(a_BlockX + 1, a_BlockY - 1, a_BlockZ) == E_BLOCK_IRON_BLOCK) &&
- (a_ChunkInterface.GetBlock(a_BlockX - 1, a_BlockY - 1, a_BlockZ) == E_BLOCK_IRON_BLOCK)
- )
- {
- // Remove the iron blocks:
- a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0);
- a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0);
- a_ChunkInterface.FastSetBlock(a_BlockX + 1, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0);
- a_ChunkInterface.FastSetBlock(a_BlockX - 1, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0);
- a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY - 2, a_BlockZ, E_BLOCK_AIR, 0);
-
- // Spawn the golem:
- a_WorldInterface.SpawnMob(a_BlockX + 0.5, a_BlockY - 2, a_BlockZ + 0.5, mtIronGolem);
- }
- else if (
- (a_ChunkInterface.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ + 1) == E_BLOCK_IRON_BLOCK) &&
- (a_ChunkInterface.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ - 1) == E_BLOCK_IRON_BLOCK)
- )
- {
- // Remove the iron blocks:
- a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0);
- a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0);
- a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ + 1, E_BLOCK_AIR, 0);
- a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ - 1, E_BLOCK_AIR, 0);
- a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY - 2, a_BlockZ, E_BLOCK_AIR, 0);
-
- // Spawn the golem:
- a_WorldInterface.SpawnMob(a_BlockX + 0.5, a_BlockY - 2, a_BlockZ + 0.5, mtIronGolem);
- }
- }
-
-
virtual bool GetPlacementBlockTypeMeta(
cChunkInterface & a_ChunkInterface, cPlayer * a_Player,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
diff --git a/src/Blocks/BlockSignPost.h b/src/Blocks/BlockSignPost.h
index d97501651..99c000633 100644
--- a/src/Blocks/BlockSignPost.h
+++ b/src/Blocks/BlockSignPost.h
@@ -53,17 +53,6 @@ public:
}
- virtual void OnPlacedByPlayer(
- cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player,
- int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
- int a_CursorX, int a_CursorY, int a_CursorZ,
- BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta
- ) override
- {
- a_Player->GetClientHandle()->SendEditSign(a_BlockX, a_BlockY, a_BlockZ);
- }
-
-
virtual NIBBLETYPE MetaRotateCW(NIBBLETYPE a_Meta) override
{
return (a_Meta + 4) & 0x0f;
diff --git a/src/Blocks/BlockWallSign.h b/src/Blocks/BlockWallSign.h
index 0abe9c52c..b6599d033 100644
--- a/src/Blocks/BlockWallSign.h
+++ b/src/Blocks/BlockWallSign.h
@@ -27,17 +27,6 @@ public:
}
- virtual void OnPlacedByPlayer(
- cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player,
- int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
- int a_CursorX, int a_CursorY, int a_CursorZ,
- BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta
- ) override
- {
- a_Player->GetClientHandle()->SendEditSign(a_BlockX, a_BlockY, a_BlockZ);
- }
-
-
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override
{
int BlockX = (a_Chunk.GetPosX() * cChunkDef::Width) + a_RelX;
diff --git a/src/Chunk.cpp b/src/Chunk.cpp
index 555e85015..b06eed316 100644
--- a/src/Chunk.cpp
+++ b/src/Chunk.cpp
@@ -49,14 +49,14 @@
////////////////////////////////////////////////////////////////////////////////
// sSetBlock:
-sSetBlock::sSetBlock( int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) // absolute block position
- : x( a_BlockX)
- , y( a_BlockY)
- , z( a_BlockZ)
- , BlockType( a_BlockType)
- , BlockMeta( a_BlockMeta)
-{
- cChunkDef::AbsoluteToRelative(x, y, z, ChunkX, ChunkZ);
+sSetBlock::sSetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta):
+ m_RelX(a_BlockX),
+ m_RelY(a_BlockY),
+ m_RelZ(a_BlockZ),
+ m_BlockType(a_BlockType),
+ m_BlockMeta(a_BlockMeta)
+{
+ cChunkDef::AbsoluteToRelative(m_RelX, m_RelY, m_RelZ, m_ChunkX, m_ChunkZ);
}
diff --git a/src/ChunkDef.h b/src/ChunkDef.h
index 8f1d416ad..2bfa2949c 100644
--- a/src/ChunkDef.h
+++ b/src/ChunkDef.h
@@ -347,18 +347,32 @@ public:
struct sSetBlock
{
- int x, y, z;
- int ChunkX, ChunkZ;
- BLOCKTYPE BlockType;
- NIBBLETYPE BlockMeta;
-
- sSetBlock( int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); // absolute block position
- sSetBlock(int a_ChunkX, int a_ChunkZ, int a_X, int a_Y, int a_Z, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) :
- x(a_X), y(a_Y), z(a_Z),
- ChunkX(a_ChunkX), ChunkZ(a_ChunkZ),
- BlockType(a_BlockType),
- BlockMeta(a_BlockMeta)
- {}
+ int m_RelX, m_RelY, m_RelZ;
+ int m_ChunkX, m_ChunkZ;
+ BLOCKTYPE m_BlockType;
+ NIBBLETYPE m_BlockMeta;
+
+ sSetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
+
+ sSetBlock(int a_ChunkX, int a_ChunkZ, int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) :
+ m_RelX(a_RelX), m_RelY(a_RelY), m_RelZ(a_RelZ),
+ m_ChunkX(a_ChunkX), m_ChunkZ(a_ChunkZ),
+ m_BlockType(a_BlockType),
+ m_BlockMeta(a_BlockMeta)
+ {
+ ASSERT((a_RelX >= 0) && (a_RelX < cChunkDef::Width));
+ ASSERT((a_RelZ >= 0) && (a_RelZ < cChunkDef::Width));
+ }
+
+ /** Returns the absolute X coord of the stored block. */
+ int GetX(void) const { return m_RelX + cChunkDef::Width * m_ChunkX; }
+
+ /** Returns the absolute Y coord of the stored block.
+ Is the same as relative Y coords, because there's no Y relativization. */
+ int GetY(void) const { return m_RelY; }
+
+ /** Returns the absolute Z coord of the stored block. */
+ int GetZ(void) const { return m_RelZ + cChunkDef::Width * m_ChunkZ; }
};
typedef std::list<sSetBlock> sSetBlockList;
@@ -385,6 +399,17 @@ public:
typedef std::list<cChunkCoords> cChunkCoordsList;
typedef std::vector<cChunkCoords> cChunkCoordsVector;
+/** A simple hash function for chunk coords, we assume that chunk coords won't use more than 16 bits, so the hash is almost an identity.
+Used for std::unordered_map<cChunkCoords, ...> */
+class cChunkCoordsHash
+{
+public:
+ size_t operator () (const cChunkCoords & a_Coords) const
+ {
+ return (static_cast<size_t>(a_Coords.m_ChunkX) << 16) ^ static_cast<size_t>(a_Coords.m_ChunkZ);
+ }
+};
+
diff --git a/src/ChunkMap.cpp b/src/ChunkMap.cpp
index 6aff3a754..c8f5aa673 100644
--- a/src/ChunkMap.cpp
+++ b/src/ChunkMap.cpp
@@ -1101,17 +1101,17 @@ void cChunkMap::FastSetBlocks(sSetBlockList & a_BlockList)
// Process all items from a_BlockList, either successfully or by placing into Failed
while (!a_BlockList.empty())
{
- int ChunkX = a_BlockList.front().ChunkX;
- int ChunkZ = a_BlockList.front().ChunkZ;
+ int ChunkX = a_BlockList.front().m_ChunkX;
+ int ChunkZ = a_BlockList.front().m_ChunkZ;
cCSLock Lock(m_CSLayers);
cChunkPtr Chunk = GetChunkNoGen(ChunkX, ChunkZ);
if ((Chunk != nullptr) && Chunk->IsValid())
{
for (sSetBlockList::iterator itr = a_BlockList.begin(); itr != a_BlockList.end();)
{
- if ((itr->ChunkX == ChunkX) && (itr->ChunkZ == ChunkZ))
+ if ((itr->m_ChunkX == ChunkX) && (itr->m_ChunkZ == ChunkZ))
{
- Chunk->FastSetBlock(itr->x, itr->y, itr->z, itr->BlockType, itr->BlockMeta);
+ Chunk->FastSetBlock(itr->m_RelX, itr->m_RelY, itr->m_RelZ, itr->m_BlockType, itr->m_BlockMeta);
itr = a_BlockList.erase(itr);
}
else
@@ -1125,7 +1125,7 @@ void cChunkMap::FastSetBlocks(sSetBlockList & a_BlockList)
// The chunk is not valid, move all blocks within this chunk to Failed
for (sSetBlockList::iterator itr = a_BlockList.begin(); itr != a_BlockList.end();)
{
- if ((itr->ChunkX == ChunkX) && (itr->ChunkZ == ChunkZ))
+ if ((itr->m_ChunkX == ChunkX) && (itr->m_ChunkZ == ChunkZ))
{
Failed.push_back(*itr);
itr = a_BlockList.erase(itr);
@@ -1146,6 +1146,34 @@ void cChunkMap::FastSetBlocks(sSetBlockList & a_BlockList)
+void cChunkMap::SetBlocks(const sSetBlockVector & a_Blocks)
+{
+ cCSLock lock(m_CSLayers);
+ cChunkPtr chunk = nullptr;
+ int lastChunkX = 0x7fffffff; // Bogus coords so that chunk is updated on first pass
+ int lastChunkZ = 0x7fffffff;
+ for (auto block: a_Blocks)
+ {
+ // Update the chunk, if different from last time:
+ if ((block.m_ChunkX != lastChunkX) || (block.m_ChunkZ != lastChunkZ))
+ {
+ lastChunkX = block.m_ChunkX;
+ lastChunkZ = block.m_ChunkZ;
+ chunk = GetChunk(lastChunkX, lastChunkZ);
+ }
+
+ // If the chunk is valid, set the block:
+ if (chunk != nullptr)
+ {
+ chunk->SetBlock(block.m_RelX, block.m_RelY, block.m_RelZ, block.m_BlockType, block.m_BlockMeta);
+ }
+ } // for block - a_Blocks[]
+}
+
+
+
+
+
void cChunkMap::CollectPickupsByPlayer(cPlayer & a_Player)
{
int BlockX = (int)(a_Player.GetPosX()); // Truncating doesn't matter much; we're scanning entire chunks anyway
@@ -1175,28 +1203,28 @@ void cChunkMap::CollectPickupsByPlayer(cPlayer & a_Player)
BLOCKTYPE cChunkMap::GetBlock(int a_BlockX, int a_BlockY, int a_BlockZ)
{
+ int X = a_BlockX, Y = a_BlockY, Z = a_BlockZ;
+ int ChunkX, ChunkZ;
+ cChunkDef::AbsoluteToRelative(X, Y, Z, ChunkX, ChunkZ);
+
// First check if it isn't queued in the m_FastSetBlockQueue:
{
- int X = a_BlockX, Y = a_BlockY, Z = a_BlockZ;
- int ChunkX, ChunkZ;
- cChunkDef::AbsoluteToRelative(X, Y, Z, ChunkX, ChunkZ);
cCSLock Lock(m_CSFastSetBlock);
for (sSetBlockList::iterator itr = m_FastSetBlockQueue.begin(); itr != m_FastSetBlockQueue.end(); ++itr)
{
- if ((itr->x == X) && (itr->y == Y) && (itr->z == Z) && (itr->ChunkX == ChunkX) && (itr->ChunkZ == ChunkZ))
+ if ((itr->m_RelX == X) && (itr->m_RelY == Y) && (itr->m_RelZ == Z) && (itr->m_ChunkX == ChunkX) && (itr->m_ChunkZ == ChunkZ))
{
- return itr->BlockType;
+ return itr->m_BlockType;
}
} // for itr - m_FastSetBlockQueue[]
}
- int ChunkX, ChunkZ;
- cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ);
-
+
+ // Not in the queue, query the chunk, if loaded:
cCSLock Lock(m_CSLayers);
cChunkPtr Chunk = GetChunk(ChunkX, ChunkZ);
if ((Chunk != nullptr) && Chunk->IsValid())
{
- return Chunk->GetBlock(a_BlockX, a_BlockY, a_BlockZ);
+ return Chunk->GetBlock(X, Y, Z);
}
return 0;
}
@@ -1207,25 +1235,28 @@ BLOCKTYPE cChunkMap::GetBlock(int a_BlockX, int a_BlockY, int a_BlockZ)
NIBBLETYPE cChunkMap::GetBlockMeta(int a_BlockX, int a_BlockY, int a_BlockZ)
{
+ int X = a_BlockX, Y = a_BlockY, Z = a_BlockZ;
+ int ChunkX, ChunkZ;
+ cChunkDef::AbsoluteToRelative(X, Y, Z, ChunkX, ChunkZ);
+
// First check if it isn't queued in the m_FastSetBlockQueue:
{
cCSLock Lock(m_CSFastSetBlock);
for (sSetBlockList::iterator itr = m_FastSetBlockQueue.begin(); itr != m_FastSetBlockQueue.end(); ++itr)
{
- if ((itr->x == a_BlockX) && (itr->y == a_BlockY) && (itr->z == a_BlockZ))
+ if ((itr->m_RelX == X) && (itr->m_RelY == Y) && (itr->m_RelZ == Z) && (itr->m_ChunkX == ChunkX) && (itr->m_ChunkZ == ChunkZ))
{
- return itr->BlockMeta;
+ return itr->m_BlockMeta;
}
} // for itr - m_FastSetBlockQueue[]
}
- int ChunkX, ChunkZ;
- cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ);
-
+
+ // Not in the queue, query the chunk, if loaded:
cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk = GetChunk( ChunkX, ChunkZ);
+ cChunkPtr Chunk = GetChunk(ChunkX, ChunkZ);
if ((Chunk != nullptr) && Chunk->IsValid())
{
- return Chunk->GetMeta(a_BlockX, a_BlockY, a_BlockZ);
+ return Chunk->GetMeta(X, Y, Z);
}
return 0;
}
@@ -1373,14 +1404,14 @@ void cChunkMap::ReplaceBlocks(const sSetBlockVector & a_Blocks, BLOCKTYPE a_Filt
cCSLock Lock(m_CSLayers);
for (sSetBlockVector::const_iterator itr = a_Blocks.begin(); itr != a_Blocks.end(); ++itr)
{
- cChunkPtr Chunk = GetChunk(itr->ChunkX, itr->ChunkZ);
+ cChunkPtr Chunk = GetChunk(itr->m_ChunkX, itr->m_ChunkZ);
if ((Chunk == nullptr) || !Chunk->IsValid())
{
continue;
}
- if (Chunk->GetBlock(itr->x, itr->y, itr->z) == a_FilterBlockType)
+ if (Chunk->GetBlock(itr->m_RelX, itr->m_RelY, itr->m_RelZ) == a_FilterBlockType)
{
- Chunk->SetBlock(itr->x, itr->y, itr->z, itr->BlockType, itr->BlockMeta);
+ Chunk->SetBlock(itr->m_RelX, itr->m_RelY, itr->m_RelZ, itr->m_BlockType, itr->m_BlockMeta);
}
}
}
@@ -1394,24 +1425,24 @@ void cChunkMap::ReplaceTreeBlocks(const sSetBlockVector & a_Blocks)
cCSLock Lock(m_CSLayers);
for (sSetBlockVector::const_iterator itr = a_Blocks.begin(); itr != a_Blocks.end(); ++itr)
{
- cChunkPtr Chunk = GetChunk(itr->ChunkX, itr->ChunkZ);
+ cChunkPtr Chunk = GetChunk(itr->m_ChunkX, itr->m_ChunkZ);
if ((Chunk == nullptr) || !Chunk->IsValid())
{
continue;
}
- switch (Chunk->GetBlock(itr->x, itr->y, itr->z))
+ switch (Chunk->GetBlock(itr->m_RelX, itr->m_RelY, itr->m_RelZ))
{
CASE_TREE_OVERWRITTEN_BLOCKS:
{
- Chunk->SetBlock(itr->x, itr->y, itr->z, itr->BlockType, itr->BlockMeta);
+ Chunk->SetBlock(itr->m_RelX, itr->m_RelY, itr->m_RelZ, itr->m_BlockType, itr->m_BlockMeta);
break;
}
case E_BLOCK_LEAVES:
case E_BLOCK_NEW_LEAVES:
{
- if ((itr->BlockType == E_BLOCK_LOG) || (itr->BlockType == E_BLOCK_NEW_LOG))
+ if ((itr->m_BlockType == E_BLOCK_LOG) || (itr->m_BlockType == E_BLOCK_NEW_LOG))
{
- Chunk->SetBlock(itr->x, itr->y, itr->z, itr->BlockType, itr->BlockMeta);
+ Chunk->SetBlock(itr->m_RelX, itr->m_RelY, itr->m_RelZ, itr->m_BlockType, itr->m_BlockMeta);
}
break;
}
@@ -1507,7 +1538,7 @@ bool cChunkMap::GetBlocks(sSetBlockVector & a_Blocks, bool a_ContinueOnFailure)
cCSLock Lock(m_CSLayers);
for (sSetBlockVector::iterator itr = a_Blocks.begin(); itr != a_Blocks.end(); ++itr)
{
- cChunkPtr Chunk = GetChunk(itr->ChunkX, itr->ChunkZ);
+ cChunkPtr Chunk = GetChunk(itr->m_ChunkX, itr->m_ChunkZ);
if ((Chunk == nullptr) || !Chunk->IsValid())
{
if (!a_ContinueOnFailure)
@@ -1517,8 +1548,8 @@ bool cChunkMap::GetBlocks(sSetBlockVector & a_Blocks, bool a_ContinueOnFailure)
res = false;
continue;
}
- itr->BlockType = Chunk->GetBlock(itr->x, itr->y, itr->z);
- itr->BlockMeta = Chunk->GetMeta(itr->x, itr->y, itr->z);
+ itr->m_BlockType = Chunk->GetBlock(itr->m_RelX, itr->m_RelY, itr->m_RelZ);
+ itr->m_BlockMeta = Chunk->GetMeta(itr->m_RelX, itr->m_RelY, itr->m_RelZ);
}
return res;
}
@@ -1527,11 +1558,11 @@ bool cChunkMap::GetBlocks(sSetBlockVector & a_Blocks, bool a_ContinueOnFailure)
-bool cChunkMap::DigBlock(int a_X, int a_Y, int a_Z)
+bool cChunkMap::DigBlock(int a_BlockX, int a_BlockY, int a_BlockZ)
{
- int PosX = a_X, PosY = a_Y, PosZ = a_Z, ChunkX, ChunkZ;
+ int PosX = a_BlockX, PosY = a_BlockY, PosZ = a_BlockZ, ChunkX, ChunkZ;
- cChunkDef::AbsoluteToRelative( PosX, PosY, PosZ, ChunkX, ChunkZ);
+ cChunkDef::AbsoluteToRelative(PosX, PosY, PosZ, ChunkX, ChunkZ);
{
cCSLock Lock(m_CSLayers);
@@ -1542,7 +1573,7 @@ bool cChunkMap::DigBlock(int a_X, int a_Y, int a_Z)
}
DestChunk->SetBlock(PosX, PosY, PosZ, E_BLOCK_AIR, 0);
- m_World->GetSimulatorManager()->WakeUp(a_X, a_Y, a_Z, DestChunk);
+ m_World->GetSimulatorManager()->WakeUp(a_BlockX, a_BlockY, a_BlockZ, DestChunk);
}
return true;
diff --git a/src/ChunkMap.h b/src/ChunkMap.h
index 6a858b06d..fe0bb3733 100644
--- a/src/ChunkMap.h
+++ b/src/ChunkMap.h
@@ -143,7 +143,13 @@ public:
void FastSetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
void FastSetQueuedBlocks();
- void FastSetBlocks (sSetBlockList & a_BlockList);
+ void FastSetBlocks(sSetBlockList & a_BlockList);
+
+ /** Performs the specified single-block set operations simultaneously, as if SetBlock() was called for each item.
+ Is more efficient than calling SetBlock() multiple times.
+ If the chunk for any of the blocks is not loaded, the set operation is ignored silently. */
+ void SetBlocks(const sSetBlockVector & a_Blocks);
+
void CollectPickupsByPlayer(cPlayer & a_Player);
BLOCKTYPE GetBlock (int a_BlockX, int a_BlockY, int a_BlockZ);
@@ -173,11 +179,20 @@ public:
(Re)sends the chunks to their relevant clients if successful. */
bool SetAreaBiome(int a_MinX, int a_MaxX, int a_MinZ, int a_MaxZ, EMCSBiome a_Biome);
- /** Retrieves block types of the specified blocks. If a chunk is not loaded, doesn't modify the block. Returns true if all blocks were read. */
+ /** Retrieves block types and metas of the specified blocks.
+ If a chunk is not loaded, doesn't modify the block and consults a_ContinueOnFailure whether to process the rest of the array.
+ Returns true if all blocks were read, false if any one failed. */
bool GetBlocks(sSetBlockVector & a_Blocks, bool a_ContinueOnFailure);
- bool DigBlock (int a_X, int a_Y, int a_Z);
- void SendBlockTo(int a_X, int a_Y, int a_Z, cPlayer * a_Player);
+ /** Removes the block at the specified coords and wakes up simulators.
+ Returns false if the chunk is not loaded (and the block is not dug).
+ Returns true if successful. */
+ bool DigBlock(int a_BlockX, int a_BlockY, int a_BlockZ);
+
+ /** Sends the block at the specified coords to the specified player.
+ Uses a blockchange packet to send the block.
+ If the relevant chunk isn't loaded, doesn't do anything. */
+ void SendBlockTo(int a_BlockX, int a_BlockY, int a_BlockZ, cPlayer * a_Player);
/** Compares clients of two chunks, calls the callback accordingly */
void CompareChunkClients(int a_ChunkX1, int a_ChunkZ1, int a_ChunkX2, int a_ChunkZ2, cClientDiffCallback & a_Callback);
diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp
index cb9d34c84..6fe6e99fa 100644
--- a/src/ClientHandle.cpp
+++ b/src/ClientHandle.cpp
@@ -1346,12 +1346,19 @@ void cClientHandle::HandleRightClick(int a_BlockX, int a_BlockY, int a_BlockZ, e
if (ItemHandler->IsPlaceable() && (a_BlockFace != BLOCK_FACE_NONE))
{
- HandlePlaceBlock(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, *ItemHandler);
+ if (!ItemHandler->OnPlayerPlace(*World, *m_Player, Equipped, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ))
+ {
+ // Placement failed, bail out
+ return;
+ }
}
else if ((ItemHandler->IsFood() || ItemHandler->IsDrinkable(EquippedDamage)))
{
- if ((m_Player->IsSatiated() || m_Player->IsGameModeCreative()) &&
- ItemHandler->IsFood() && (Equipped.m_ItemType != E_ITEM_GOLDEN_APPLE))
+ if (
+ (m_Player->IsSatiated() || m_Player->IsGameModeCreative()) && // Only creative or hungry players can eat
+ ItemHandler->IsFood() &&
+ (Equipped.m_ItemType != E_ITEM_GOLDEN_APPLE) // Golden apple is a special case, it is used instead of eaten
+ )
{
// The player is satiated or in creative, and trying to eat
return;
@@ -1379,151 +1386,6 @@ void cClientHandle::HandleRightClick(int a_BlockX, int a_BlockY, int a_BlockZ, e
-void cClientHandle::HandlePlaceBlock(int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, cItemHandler & a_ItemHandler)
-{
- BLOCKTYPE EquippedBlock = (BLOCKTYPE)(m_Player->GetEquippedItem().m_ItemType);
- if (a_BlockFace < 0)
- {
- // Clicked in air
- return;
- }
-
- cWorld * World = m_Player->GetWorld();
-
- BLOCKTYPE ClickedBlock;
- NIBBLETYPE ClickedBlockMeta;
- NIBBLETYPE EquippedBlockDamage = (NIBBLETYPE)(m_Player->GetEquippedItem().m_ItemDamage);
-
- if ((a_BlockY < 0) || (a_BlockY >= cChunkDef::Height))
- {
- // The block is being placed outside the world, ignore this packet altogether (#128)
- return;
- }
-
- World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, ClickedBlock, ClickedBlockMeta);
-
- // Special slab handling - placing a slab onto another slab produces a dblslab instead:
- if (
- cBlockSlabHandler::IsAnySlabType(ClickedBlock) && // Is there a slab already?
- cBlockSlabHandler::IsAnySlabType(EquippedBlock) && // Is the player placing another slab?
- ((ClickedBlockMeta & 0x07) == EquippedBlockDamage) && // Is it the same slab type?
- (
- (a_BlockFace == BLOCK_FACE_TOP) || // Clicking the top of a bottom slab
- (a_BlockFace == BLOCK_FACE_BOTTOM) // Clicking the bottom of a top slab
- )
- )
- {
- // Coordinates at clicked block, which was an eligible slab, and either top or bottom faces were clicked
- // If clicked top face and slab occupies the top voxel, we want a slab to be placed above it (therefore increment Y)
- // Else if clicked bottom face and slab occupies the bottom voxel, decrement Y for the same reason
- // Don't touch coordinates if anything else because a dblslab opportunity is present
- if ((ClickedBlockMeta & 0x08) && (a_BlockFace == BLOCK_FACE_TOP))
- {
- ++a_BlockY;
- }
- else if (!(ClickedBlockMeta & 0x08) && (a_BlockFace == BLOCK_FACE_BOTTOM))
- {
- --a_BlockY;
- }
- World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, ClickedBlock, ClickedBlockMeta);
- }
- else
- {
- // Check if the block ignores build collision (water, grass etc.):
- if (
- BlockHandler(ClickedBlock)->DoesIgnoreBuildCollision() ||
- BlockHandler(ClickedBlock)->DoesIgnoreBuildCollision(m_Player, ClickedBlockMeta)
- )
- {
- cChunkInterface ChunkInterface(World->GetChunkMap());
- BlockHandler(ClickedBlock)->OnDestroyedByPlayer(ChunkInterface, *World, m_Player, a_BlockX, a_BlockY, a_BlockZ);
- }
- else
- {
- AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
-
- if ((a_BlockY < 0) || (a_BlockY >= cChunkDef::Height))
- {
- // The block is being placed outside the world, ignore this packet altogether (#128)
- return;
- }
-
- NIBBLETYPE PlaceMeta;
- BLOCKTYPE PlaceBlock;
- World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, PlaceBlock, PlaceMeta);
-
- // Clicked on side of block, make sure that placement won't be cancelled if there is a slab able to be double slabbed.
- // No need to do combinability (dblslab) checks, client will do that here.
- if (cBlockSlabHandler::IsAnySlabType(PlaceBlock))
- {
- // It's a slab, don't do checks and proceed to double-slabbing
- }
- else
- {
- if (
- !BlockHandler(PlaceBlock)->DoesIgnoreBuildCollision() &&
- !BlockHandler(PlaceBlock)->DoesIgnoreBuildCollision(m_Player, PlaceMeta)
- )
- {
- // Tried to place a block *into* another?
- // Happens when you place a block aiming at side of block with a torch on it or stem beside it
- return;
- }
- }
- }
- }
-
- BLOCKTYPE BlockType;
- NIBBLETYPE BlockMeta;
- if (!a_ItemHandler.GetPlacementBlockTypeMeta(World, m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, BlockType, BlockMeta))
- {
- // Handler refused the placement, send that information back to the client:
- World->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player);
- m_Player->GetInventory().SendEquippedSlot();
- return;
- }
-
- cBlockHandler * NewBlock = BlockHandler(BlockType);
-
- if (cRoot::Get()->GetPluginManager()->CallHookPlayerPlacingBlock(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, BlockType, BlockMeta))
- {
- // A plugin doesn't agree with placing the block, revert the block on the client:
- World->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player);
- m_Player->GetInventory().SendEquippedSlot();
- return;
- }
-
- // The actual block placement:
- World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta);
- if (!m_Player->IsGameModeCreative())
- {
- m_Player->GetInventory().RemoveOneEquippedItem();
- }
-
- cChunkInterface ChunkInterface(World->GetChunkMap());
- NewBlock->OnPlacedByPlayer(ChunkInterface, *World, m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, BlockType, BlockMeta);
-
- AString PlaceSound = cBlockInfo::GetPlaceSound(BlockType);
- float Volume = 1.0f, Pitch = 0.8f;
- if (PlaceSound == "dig.metal")
- {
- Pitch = 1.2f;
- PlaceSound = "dig.stone";
- }
- else if (PlaceSound == "random.anvil_land")
- {
- Volume = 0.65f;
- }
-
- World->BroadcastSoundEffect(PlaceSound, a_BlockX + 0.5, a_BlockY + 0.5, a_BlockZ + 0.5, Volume, Pitch);
-
- cRoot::Get()->GetPluginManager()->CallHookPlayerPlacedBlock(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, BlockType, BlockMeta);
-}
-
-
-
-
-
void cClientHandle::HandleChat(const AString & a_Message)
{
// We no longer need to postpone message processing, because the messages already arrive in the Tick thread
diff --git a/src/ClientHandle.h b/src/ClientHandle.h
index 25dd250d9..5e10bb52f 100644
--- a/src/ClientHandle.h
+++ b/src/ClientHandle.h
@@ -459,9 +459,6 @@ private:
UInt32 m_ProtocolVersion;
- /** Handles the block placing packet when it is a real block placement (not block-using, item-using or eating) */
- void HandlePlaceBlock(int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, cItemHandler & a_ItemHandler);
-
/** Returns true if the rate block interactions is within a reasonable limit (bot protection) */
bool CheckBlockInteractionsRate(void);
diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp
index 15920d6cf..1d5cc6554 100644
--- a/src/Entities/Player.cpp
+++ b/src/Entities/Player.cpp
@@ -2,6 +2,7 @@
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
#include "Player.h"
+#include <unordered_map>
#include "../ChatColor.h"
#include "../Server.h"
#include "../UI/Window.h"
@@ -19,6 +20,10 @@
#include "../WorldStorage/StatSerializer.h"
#include "../CompositeChat.h"
+#include "../Blocks/BlockHandler.h"
+#include "../Blocks/BlockSlab.h"
+#include "../Blocks/ChunkInterface.h"
+
#include "../IniFile.h"
#include "json/json.h"
@@ -2168,6 +2173,97 @@ void cPlayer::LoadRank(void)
+bool cPlayer::PlaceBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
+{
+ sSetBlockVector blk{{a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta}};
+ return PlaceBlocks(blk);
+}
+
+
+
+
+
+void cPlayer::SendBlocksAround(int a_BlockX, int a_BlockY, int a_BlockZ, int a_Range)
+{
+ // Collect the coords of all the blocks to send:
+ sSetBlockVector blks;
+ for (int y = a_BlockY - a_Range + 1; y < a_BlockY + a_Range; y++)
+ {
+ for (int z = a_BlockZ - a_Range + 1; z < a_BlockZ + a_Range; z++)
+ {
+ for (int x = a_BlockX - a_Range + 1; x < a_BlockX + a_Range; x++)
+ {
+ blks.emplace_back(x, y, z, E_BLOCK_AIR, 0); // Use fake blocktype, it will get set later on.
+ };
+ };
+ } // for y
+
+ // Get the values of all the blocks:
+ if (!m_World->GetBlocks(blks, false))
+ {
+ LOGD("%s: Cannot query all blocks, not sending an update", __FUNCTION__);
+ return;
+ }
+
+ // Divide the block changes by their respective chunks:
+ std::unordered_map<cChunkCoords, sSetBlockVector, cChunkCoordsHash> Changes;
+ for (const auto & blk: blks)
+ {
+ Changes[cChunkCoords(blk.m_ChunkX, blk.m_ChunkZ)].push_back(blk);
+ } // for blk - blks[]
+ blks.clear();
+
+ // Send the blocks for each affected chunk:
+ for (auto itr = Changes.cbegin(), end = Changes.cend(); itr != end; ++itr)
+ {
+ m_ClientHandle->SendBlockChanges(itr->first.m_ChunkX, itr->first.m_ChunkZ, itr->second);
+ }
+}
+
+
+
+
+
+bool cPlayer::PlaceBlocks(const sSetBlockVector & a_Blocks)
+{
+ // Call the "placing" hooks; if any fail, abort:
+ cPluginManager * pm = cPluginManager::Get();
+ for (auto blk: a_Blocks)
+ {
+ if (pm->CallHookPlayerPlacingBlock(*this, blk))
+ {
+ // Abort - re-send all the current blocks in the a_Blocks' coords to the client:
+ for (auto blk2: a_Blocks)
+ {
+ m_World->SendBlockTo(blk2.GetX(), blk2.GetY(), blk2.GetZ(), this);
+ }
+ return false;
+ }
+ } // for blk - a_Blocks[]
+
+ // Set the blocks:
+ m_World->SetBlocks(a_Blocks);
+
+ // Notify the blockhandlers:
+ cChunkInterface ChunkInterface(m_World->GetChunkMap());
+ for (auto blk: a_Blocks)
+ {
+ cBlockHandler * newBlock = BlockHandler(blk.m_BlockType);
+ newBlock->OnPlacedByPlayer(ChunkInterface, *m_World, this, blk);
+ }
+
+ // Call the "placed" hooks:
+ for (auto blk: a_Blocks)
+ {
+ pm->CallHookPlayerPlacedBlock(*this, blk);
+ }
+ return true;
+}
+
+
+
+
+
void cPlayer::Detach()
{
super::Detach();
diff --git a/src/Entities/Player.h b/src/Entities/Player.h
index c643aaa8e..b94d2659e 100644
--- a/src/Entities/Player.h
+++ b/src/Entities/Player.h
@@ -440,8 +440,27 @@ public:
Loads the m_Rank, m_Permissions, m_MsgPrefix, m_MsgSuffix and m_MsgNameColorCode members. */
void LoadRank(void);
+ /** Calls the block-placement hook and places the block in the world, unless refused by the hook.
+ If the hook prevents the placement, sends the current block at the specified coords back to the client.
+ Assumes that the block is in a currently loaded chunk.
+ Returns true if the block is successfully placed. */
+ bool PlaceBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
+
+ /** Sends the block in the specified range around the specified coord to the client
+ as a block change packet.
+ The blocks in range (a_BlockX - a_Range, a_BlockX + a_Range) are sent (NY-metric). */
+ void SendBlocksAround(int a_BlockX, int a_BlockY, int a_BlockZ, int a_Range = 1);
+
// tolua_end
+ /** Calls the block placement hooks and places the blocks in the world.
+ First the "placing" hooks for all the blocks are called, then the blocks are placed, and finally
+ the "placed" hooks are called.
+ If the any of the "placing" hooks aborts, none of the blocks are placed and the function returns false.
+ Returns true if all the blocks are placed.
+ Assumes that all the blocks are in currently loaded chunks. */
+ bool PlaceBlocks(const sSetBlockVector & a_Blocks);
+
// cEntity overrides:
virtual bool IsCrouched (void) const { return m_IsCrouched; }
virtual bool IsSprinting(void) const { return m_IsSprinting; }
diff --git a/src/Generating/StructGen.cpp b/src/Generating/StructGen.cpp
index 2f685c808..2d5a73739 100644
--- a/src/Generating/StructGen.cpp
+++ b/src/Generating/StructGen.cpp
@@ -123,18 +123,18 @@ void cStructGenTrees::GenerateSingleTree(
// Check if the generated image fits the terrain. Only the logs are checked:
for (sSetBlockVector::const_iterator itr = TreeLogs.begin(); itr != TreeLogs.end(); ++itr)
{
- if ((itr->ChunkX != a_ChunkX) || (itr->ChunkZ != a_ChunkZ))
+ if ((itr->m_ChunkX != a_ChunkX) || (itr->m_ChunkZ != a_ChunkZ))
{
// Outside the chunk
continue;
}
- if (itr->y >= cChunkDef::Height)
+ if (itr->m_RelY >= cChunkDef::Height)
{
// Above the chunk, cut off (this shouldn't happen too often, we're limiting trees to y < 230)
continue;
}
- BLOCKTYPE Block = a_ChunkDesc.GetBlockType(itr->x, itr->y, itr->z);
+ BLOCKTYPE Block = a_ChunkDesc.GetBlockType(itr->m_RelX, itr->m_RelY, itr->m_RelZ);
switch (Block)
{
CASE_TREE_ALLOWED_BLOCKS:
@@ -167,14 +167,14 @@ void cStructGenTrees::ApplyTreeImage(
// Put the generated image into a_BlockTypes, push things outside this chunk into a_Blocks
for (sSetBlockVector::const_iterator itr = a_Image.begin(), end = a_Image.end(); itr != end; ++itr)
{
- if ((itr->ChunkX == a_ChunkX) && (itr->ChunkZ == a_ChunkZ) && (itr->y < cChunkDef::Height))
+ if ((itr->m_ChunkX == a_ChunkX) && (itr->m_ChunkZ == a_ChunkZ) && (itr->m_RelY < cChunkDef::Height))
{
// Inside this chunk, integrate into a_ChunkDesc:
- switch (a_ChunkDesc.GetBlockType(itr->x, itr->y, itr->z))
+ switch (a_ChunkDesc.GetBlockType(itr->m_RelX, itr->m_RelY, itr->m_RelZ))
{
case E_BLOCK_LEAVES:
{
- if (itr->BlockType != E_BLOCK_LOG)
+ if (itr->m_BlockType != E_BLOCK_LOG)
{
break;
}
@@ -182,7 +182,7 @@ void cStructGenTrees::ApplyTreeImage(
}
CASE_TREE_OVERWRITTEN_BLOCKS:
{
- a_ChunkDesc.SetBlockTypeMeta(itr->x, itr->y, itr->z, itr->BlockType, itr->BlockMeta);
+ a_ChunkDesc.SetBlockTypeMeta(itr->m_RelX, itr->m_RelY, itr->m_RelZ, itr->m_BlockType, itr->m_BlockMeta);
break;
}
diff --git a/src/Generating/Trees.cpp b/src/Generating/Trees.cpp
index be8b0cd6b..a10e0f4f1 100644
--- a/src/Generating/Trees.cpp
+++ b/src/Generating/Trees.cpp
@@ -403,17 +403,17 @@ void GetLargeAppleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a
for (auto itr : a_LogBlocks)
{
// Get the log's X and Z coordinates
- int X = itr.ChunkX * 16 + itr.x;
- int Z = itr.ChunkZ * 16 + itr.z;
+ int X = itr.GetX();
+ int Z = itr.GetZ();
- a_OtherBlocks.push_back(sSetBlock(X, itr.y - 2, Z, E_BLOCK_LEAVES, E_META_LEAVES_APPLE));
- PushCoordBlocks(X, itr.y - 2, Z, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_APPLE);
+ a_OtherBlocks.push_back(sSetBlock(X, itr.m_RelY - 2, Z, E_BLOCK_LEAVES, E_META_LEAVES_APPLE));
+ PushCoordBlocks(X, itr.m_RelY - 2, Z, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_APPLE);
for (int y = -1; y <= 1; y++)
{
- PushCoordBlocks (X, itr.y + y, Z, a_OtherBlocks, BigO2, ARRAYCOUNT(BigO2), E_BLOCK_LEAVES, E_META_LEAVES_APPLE);
+ PushCoordBlocks (X, itr.m_RelY + y, Z, a_OtherBlocks, BigO2, ARRAYCOUNT(BigO2), E_BLOCK_LEAVES, E_META_LEAVES_APPLE);
}
- PushCoordBlocks(X, itr.y + 2, Z, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_APPLE);
- a_OtherBlocks.push_back(sSetBlock(X, itr.y + 2, Z, E_BLOCK_LEAVES, E_META_LEAVES_APPLE));
+ PushCoordBlocks(X, itr.m_RelY + 2, Z, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_APPLE);
+ a_OtherBlocks.push_back(sSetBlock(X, itr.m_RelY + 2, Z, E_BLOCK_LEAVES, E_META_LEAVES_APPLE));
}
// Trunk:
diff --git a/src/Items/CMakeLists.txt b/src/Items/CMakeLists.txt
index 12a467672..c50ddb372 100644
--- a/src/Items/CMakeLists.txt
+++ b/src/Items/CMakeLists.txt
@@ -10,12 +10,14 @@ SET (SRCS
SET (HDRS
ItemArmor.h
ItemBed.h
+ ItemBigFlower.h
ItemBoat.h
ItemBow.h
ItemBrewingStand.h
ItemBucket.h
ItemCake.h
ItemCauldron.h
+ ItemChest.h
ItemCloth.h
ItemComparator.h
ItemDoor.h
@@ -38,18 +40,21 @@ SET (HDRS
ItemPainting.h
ItemPickaxe.h
ItemPotion.h
+ ItemPumpkin.h
ItemRedstoneDust.h
ItemRedstoneRepeater.h
ItemSapling.h
ItemSeeds.h
ItemShears.h
ItemShovel.h
+ ItemSlab.h
ItemSign.h
ItemSpawnEgg.h
ItemString.h
ItemSugarcane.h
ItemSword.h
- ItemThrowable.h)
+ ItemThrowable.h
+)
if(NOT MSVC)
add_library(Items ${SRCS} ${HDRS})
diff --git a/src/Items/ItemBed.h b/src/Items/ItemBed.h
index 94a14cf16..77d51d744 100644
--- a/src/Items/ItemBed.h
+++ b/src/Items/ItemBed.h
@@ -24,30 +24,36 @@ public:
return true;
}
- virtual bool GetPlacementBlockTypeMeta(
- cWorld * a_World, cPlayer * a_Player,
+
+ virtual bool OnPlayerPlace(
+ cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
- int a_CursorX, int a_CursorY, int a_CursorZ,
- BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
+ int a_CursorX, int a_CursorY, int a_CursorZ
) override
{
+ // Can only be placed on the floor:
if (a_BlockFace != BLOCK_FACE_TOP)
{
- // Can only be placed on the floor
return false;
}
+ AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
- a_BlockMeta = cBlockBedHandler::RotationToMetaData(a_Player->GetYaw());
+ // The "foot" block:
+ sSetBlockVector blks;
+ NIBBLETYPE BlockMeta = cBlockBedHandler::RotationToMetaData(a_Player.GetYaw());
+ blks.emplace_back(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_BED, BlockMeta);
- // Check if there is empty space for the foot section:
- Vector3i Direction = cBlockBedHandler::MetaDataToDirection(a_BlockMeta);
- if (a_World->GetBlock(a_BlockX + Direction.x, a_BlockY, a_BlockZ + Direction.z) != E_BLOCK_AIR)
+ // Check if there is empty space for the "head" block:
+ // (Vanilla only allows beds to be placed into air)
+ Vector3i Direction = cBlockBedHandler::MetaDataToDirection(BlockMeta);
+ if (a_World.GetBlock(a_BlockX + Direction.x, a_BlockY, a_BlockZ + Direction.z) != E_BLOCK_AIR)
{
return false;
}
+ blks.emplace_back(a_BlockX + Direction.x, a_BlockY, a_BlockZ + Direction.z, E_BLOCK_BED, BlockMeta | 0x08);
- a_BlockType = E_BLOCK_BED;
- return true;
+ // Place both bed blocks:
+ return a_Player.PlaceBlocks(blks);
}
} ;
diff --git a/src/Items/ItemBigFlower.h b/src/Items/ItemBigFlower.h
new file mode 100644
index 000000000..4341a1a17
--- /dev/null
+++ b/src/Items/ItemBigFlower.h
@@ -0,0 +1,56 @@
+
+// ItemBigFlower.h
+
+// Declares the cItemBigFlower class representing the cItemHandler for big flowers
+
+
+
+
+
+#pragma once
+
+#include "ItemHandler.h"
+
+
+
+
+
+class cItemBigFlowerHandler:
+ public cItemHandler
+{
+ typedef cItemHandler super;
+
+public:
+ cItemBigFlowerHandler(void):
+ super(E_BLOCK_BIG_FLOWER)
+ {
+ }
+
+
+ virtual bool OnPlayerPlace(
+ cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem,
+ int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
+ int a_CursorX, int a_CursorY, int a_CursorZ
+ ) override
+ {
+ // Can only be placed on the floor:
+ if (a_BlockFace != BLOCK_FACE_TOP)
+ {
+ return false;
+ }
+ AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
+
+ // Place both blocks atomically:
+ sSetBlockVector blks;
+ blks.emplace_back(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_BIG_FLOWER, a_EquippedItem.m_ItemDamage & 0x07);
+ if (a_BlockY < cChunkDef::Height - 1)
+ {
+ blks.emplace_back(a_BlockX, a_BlockY + 1, a_BlockZ, E_BLOCK_BIG_FLOWER, (a_EquippedItem.m_ItemDamage & 0x07) | 0x08);
+ }
+ return a_Player.PlaceBlocks(blks);
+ }
+};
+
+
+
+
diff --git a/src/Items/ItemChest.h b/src/Items/ItemChest.h
new file mode 100644
index 000000000..b6579c423
--- /dev/null
+++ b/src/Items/ItemChest.h
@@ -0,0 +1,167 @@
+
+// ItemChest.h
+
+// Declares the cItemChestHandler class representing the cItemHandler descendant responsible for chests
+
+
+
+
+
+#pragma once
+
+#include "ItemHandler.h"
+#include "../Blocks/BlockChest.h"
+
+
+
+
+
+class cItemChestHandler:
+ public cItemHandler
+{
+ typedef cItemHandler super;
+public:
+ cItemChestHandler(int a_ItemType):
+ super(a_ItemType)
+ {
+ }
+
+
+ virtual bool OnPlayerPlace(
+ cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem,
+ int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
+ int a_CursorX, int a_CursorY, int a_CursorZ
+ ) override
+ {
+ if (a_BlockFace < 0)
+ {
+ // Clicked in air
+ return false;
+ }
+
+ if ((a_BlockY < 0) || (a_BlockY >= cChunkDef::Height))
+ {
+ // The clicked block is outside the world, ignore this call altogether (#128)
+ return false;
+ }
+
+ // Check if the block ignores build collision (water, grass etc.):
+ BLOCKTYPE ClickedBlock;
+ NIBBLETYPE ClickedBlockMeta;
+ a_World.GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, ClickedBlock, ClickedBlockMeta);
+ if (
+ BlockHandler(ClickedBlock)->DoesIgnoreBuildCollision() ||
+ BlockHandler(ClickedBlock)->DoesIgnoreBuildCollision(&a_Player, ClickedBlockMeta)
+ )
+ {
+ cChunkInterface ChunkInterface(a_World.GetChunkMap());
+ BlockHandler(ClickedBlock)->OnDestroyedByPlayer(ChunkInterface, a_World, &a_Player, a_BlockX, a_BlockY, a_BlockZ);
+ }
+ else
+ {
+ AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
+
+ if ((a_BlockY < 0) || (a_BlockY >= cChunkDef::Height))
+ {
+ // The block is being placed outside the world, ignore this packet altogether (#128)
+ return false;
+ }
+
+ NIBBLETYPE PlaceMeta;
+ BLOCKTYPE PlaceBlock;
+ a_World.GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, PlaceBlock, PlaceMeta);
+
+ // Clicked on side of block, make sure that placement won't be cancelled if there is a slab able to be double slabbed.
+ // No need to do combinability (dblslab) checks, client will do that here.
+ if (
+ !BlockHandler(PlaceBlock)->DoesIgnoreBuildCollision() &&
+ !BlockHandler(PlaceBlock)->DoesIgnoreBuildCollision(&a_Player, PlaceMeta)
+ )
+ {
+ // Tried to place a block *into* another?
+ // Happens when you place a block aiming at side of block with a torch on it or stem beside it
+ return false;
+ }
+ }
+
+ // Check that there is at most one single neighbor of the same chest type:
+ static const Vector3i CrossCoords[] =
+ {
+ {-1, 0, 0},
+ { 0, 0, -1},
+ { 1, 0, 0},
+ { 0, 0, 1},
+ };
+ int NeighborIdx = -1;
+ for (size_t i = 0; i < ARRAYCOUNT(CrossCoords); i++)
+ {
+ if (a_World.GetBlock(a_BlockX + CrossCoords[i].x, a_BlockY, a_BlockZ + CrossCoords[i].z) != m_ItemType)
+ {
+ continue;
+ }
+ if (NeighborIdx >= 0)
+ {
+ // Can't place here, there are already two neighbors, this would form a 3-block chest
+ return false;
+ }
+ NeighborIdx = static_cast<int>(i);
+
+ // Check that this neighbor is a single chest:
+ int bx = a_BlockX + CrossCoords[i].x;
+ int bz = a_BlockZ + CrossCoords[i].z;
+ for (size_t j = 0; j < ARRAYCOUNT(CrossCoords); j++)
+ {
+ if (a_World.GetBlock(bx + CrossCoords[j].x, a_BlockY, bz + CrossCoords[j].z) == m_ItemType)
+ {
+ return false;
+ }
+ } // for j
+ } // for i
+
+ // If there's no chest neighbor, place the single block chest and bail out:
+ BLOCKTYPE ChestBlockType = static_cast<BLOCKTYPE>(m_ItemType);
+ if (NeighborIdx < 0)
+ {
+ NIBBLETYPE Meta = cBlockChestHandler::PlayerYawToMetaData(a_Player.GetYaw());
+ return a_Player.PlaceBlock(a_BlockX, a_BlockY, a_BlockZ, ChestBlockType, Meta);
+ }
+
+ // There is a neighbor to which we need to adjust
+ double yaw = a_Player.GetYaw();
+ if ((NeighborIdx == 0) || (NeighborIdx == 2))
+ {
+ // The neighbor is in the X axis, form a X-axis-aligned dblchest:
+ NIBBLETYPE Meta = ((yaw >= -90) && (yaw < 90)) ? E_META_CHEST_FACING_ZM : E_META_CHEST_FACING_ZP;
+
+ // Place the new chest:
+ if (!a_Player.PlaceBlock(a_BlockX, a_BlockY, a_BlockZ, ChestBlockType, Meta))
+ {
+ return false;
+ }
+
+ // Adjust the existing chest:
+ a_World.FastSetBlock(a_BlockX + CrossCoords[NeighborIdx].x, a_BlockY, a_BlockZ + CrossCoords[NeighborIdx].z, ChestBlockType, Meta);
+ return true;
+ }
+
+ // The neighbor is in the Z axis, form a Z-axis-aligned dblchest:
+ NIBBLETYPE Meta = (yaw < 0) ? E_META_CHEST_FACING_XM : E_META_CHEST_FACING_XP;
+
+ // Place the new chest:
+ if (!a_Player.PlaceBlock(a_BlockX, a_BlockY, a_BlockZ, ChestBlockType, Meta))
+ {
+ return false;
+ }
+
+ // Adjust the existing chest:
+ a_World.FastSetBlock(a_BlockX + CrossCoords[NeighborIdx].x, a_BlockY, a_BlockZ + CrossCoords[NeighborIdx].z, ChestBlockType, Meta);
+ return true;
+ }
+
+private:
+ cItemChestHandler(const cItemChestHandler &) = delete;
+};
+
+
+
+
diff --git a/src/Items/ItemDoor.h b/src/Items/ItemDoor.h
index cd5baf44f..dbba26728 100644
--- a/src/Items/ItemDoor.h
+++ b/src/Items/ItemDoor.h
@@ -3,6 +3,7 @@
#include "ItemHandler.h"
#include "../World.h"
+#include "../Blocks/BlockDoor.h"
@@ -18,27 +19,43 @@ public:
}
- virtual bool IsPlaceable(void) override
- {
- return true;
- }
- virtual bool GetPlacementBlockTypeMeta(
- cWorld * a_World, cPlayer * a_Player,
+ virtual bool OnPlayerPlace(
+ cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
- int a_CursorX, int a_CursorY, int a_CursorZ,
- BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
+ int a_CursorX, int a_CursorY, int a_CursorZ
) override
{
+ // Vanilla only allows door placement while clicking on the top face of the block below the door:
+ if (a_BlockFace != BLOCK_FACE_NONE)
+ {
+ return false;
+ }
+ AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
+
+ // Door (bottom block) can be placed in Y range of [1, 254]:
+ if ((a_BlockY < 1) || (a_BlockY + 2 >= cChunkDef::Height))
+ {
+ return false;
+ }
+
+ // The door needs a compatible block below it:
+ if ((a_BlockY > 0) && cBlockDoorHandler::CanBeOn(a_World.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ)))
+ {
+ return false;
+ }
+
+ // Get the block type of the door to place:
+ BLOCKTYPE BlockType;
switch (m_ItemType)
{
- case E_ITEM_WOODEN_DOOR: a_BlockType = E_BLOCK_WOODEN_DOOR; break;
- case E_ITEM_IRON_DOOR: a_BlockType = E_BLOCK_IRON_DOOR; break;
- case E_ITEM_SPRUCE_DOOR: a_BlockType = E_BLOCK_SPRUCE_DOOR; break;
- case E_ITEM_BIRCH_DOOR: a_BlockType = E_BLOCK_BIRCH_DOOR; break;
- case E_ITEM_JUNGLE_DOOR: a_BlockType = E_BLOCK_JUNGLE_DOOR; break;
- case E_ITEM_DARK_OAK_DOOR: a_BlockType = E_BLOCK_DARK_OAK_DOOR; break;
- case E_ITEM_ACACIA_DOOR: a_BlockType = E_BLOCK_ACACIA_DOOR; break;
+ case E_ITEM_WOODEN_DOOR: BlockType = E_BLOCK_WOODEN_DOOR; break;
+ case E_ITEM_IRON_DOOR: BlockType = E_BLOCK_IRON_DOOR; break;
+ case E_ITEM_SPRUCE_DOOR: BlockType = E_BLOCK_SPRUCE_DOOR; break;
+ case E_ITEM_BIRCH_DOOR: BlockType = E_BLOCK_BIRCH_DOOR; break;
+ case E_ITEM_JUNGLE_DOOR: BlockType = E_BLOCK_JUNGLE_DOOR; break;
+ case E_ITEM_DARK_OAK_DOOR: BlockType = E_BLOCK_DARK_OAK_DOOR; break;
+ case E_ITEM_ACACIA_DOOR: BlockType = E_BLOCK_ACACIA_DOOR; break;
default:
{
ASSERT(!"Unhandled door type");
@@ -46,14 +63,47 @@ public:
}
}
- cChunkInterface ChunkInterface(a_World->GetChunkMap());
- bool Meta = BlockHandler(a_BlockType)->GetPlacementBlockTypeMeta(
- ChunkInterface, a_Player,
- a_BlockX, a_BlockY, a_BlockZ, a_BlockFace,
- a_CursorX, a_CursorY, a_CursorZ,
- a_BlockType, a_BlockMeta
- );
- return Meta;
+ // Check the two blocks that will get replaced by the door:
+ BLOCKTYPE LowerBlockType = a_World.GetBlock(a_BlockX, a_BlockY + 1, a_BlockZ);
+ BLOCKTYPE UpperBlockType = a_World.GetBlock(a_BlockX, a_BlockY + 2, a_BlockZ);
+ if (
+ !cBlockDoorHandler::CanReplaceBlock(LowerBlockType) ||
+ !cBlockDoorHandler::CanReplaceBlock(UpperBlockType))
+ {
+ return false;
+ }
+
+ // Get the coords of the neighboring blocks:
+ NIBBLETYPE LowerBlockMeta = cBlockDoorHandler::PlayerYawToMetaData(a_Player.GetYaw());
+ Vector3i RelDirToOutside = cBlockDoorHandler::GetRelativeDirectionToOutside(LowerBlockMeta);
+ Vector3i LeftNeighborPos = RelDirToOutside;
+ LeftNeighborPos.TurnCCW();
+ LeftNeighborPos.Move(a_BlockX, a_BlockY, a_BlockZ);
+ Vector3i RightNeighborPos = RelDirToOutside;
+ RightNeighborPos.TurnCW();
+ RightNeighborPos.Move(a_BlockX, a_BlockY, a_BlockZ);
+
+ // Decide whether the hinge is on the left (default) or on the right:
+ NIBBLETYPE UpperBlockMeta = 0x08;
+ if (
+ cBlockDoorHandler::IsDoorBlockType(a_World.GetBlock(LeftNeighborPos)) || // The block to the left is a door block
+ cBlockInfo::IsSolid(a_World.GetBlock(RightNeighborPos)) // The block to the right is solid
+ )
+ {
+ UpperBlockMeta = 0x09; // Upper block | hinge on right
+ }
+
+ // Set the blocks:
+ sSetBlockVector blks;
+ blks.emplace_back(a_BlockX, a_BlockY, a_BlockZ, BlockType, LowerBlockMeta);
+ blks.emplace_back(a_BlockX, a_BlockY + 1, a_BlockZ, BlockType, UpperBlockMeta);
+ return a_Player.PlaceBlocks(blks);
+ }
+
+
+ virtual bool IsPlaceable(void) override
+ {
+ return true;
}
} ;
diff --git a/src/Items/ItemDye.h b/src/Items/ItemDye.h
index da978040d..bfcd0bac4 100644
--- a/src/Items/ItemDye.h
+++ b/src/Items/ItemDye.h
@@ -55,25 +55,16 @@ public:
return false;
}
- // Check plugins
- if (cRoot::Get()->GetPluginManager()->CallHookPlayerPlacingBlock(*a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, 0, 0, 0, E_BLOCK_COCOA_POD, BlockMeta))
+ // Place the cocoa pod:
+ if (a_Player->PlaceBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_COCOA_POD, BlockMeta))
{
- a_World->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, a_Player);
- a_Player->GetInventory().SendEquippedSlot();
- return false;
- }
-
- // Set block and broadcast place sound
- a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_COCOA_POD, BlockMeta);
- a_World->BroadcastSoundEffect("dig.stone", a_BlockX + 0.5, a_BlockY + 0.5, a_BlockZ + 0.5, 1.0f, 0.8f);
-
- // Remove one cocoa pod from the inventory
- if (!a_Player->IsGameModeCreative())
- {
- a_Player->GetInventory().RemoveOneEquippedItem();
+ a_World->BroadcastSoundEffect("dig.stone", a_BlockX + 0.5, a_BlockY + 0.5, a_BlockZ + 0.5, 1.0f, 0.8f);
+ if (a_Player->IsGameModeSurvival())
+ {
+ a_Player->GetInventory().RemoveOneEquippedItem();
+ }
+ return true;
}
- cRoot::Get()->GetPluginManager()->CallHookPlayerPlacedBlock(*a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, 0, 0, 0, E_BLOCK_COCOA_POD, BlockMeta);
- return true;
}
return false;
}
diff --git a/src/Items/ItemHandler.cpp b/src/Items/ItemHandler.cpp
index 9272a723d..92c55ec62 100644
--- a/src/Items/ItemHandler.cpp
+++ b/src/Items/ItemHandler.cpp
@@ -6,39 +6,43 @@
#include "../Entities/Player.h"
#include "../FastRandom.h"
#include "../BlockInServerPluginInterface.h"
+#include "../Chunk.h"
// Handlers:
#include "ItemArmor.h"
#include "ItemBed.h"
+#include "ItemBigFlower.h"
#include "ItemBoat.h"
#include "ItemBow.h"
#include "ItemBrewingStand.h"
#include "ItemBucket.h"
#include "ItemCake.h"
#include "ItemCauldron.h"
+#include "ItemChest.h"
#include "ItemCloth.h"
#include "ItemComparator.h"
#include "ItemDoor.h"
-#include "ItemMilk.h"
#include "ItemDye.h"
#include "ItemEmptyMap.h"
#include "ItemFishingRod.h"
#include "ItemFlowerPot.h"
#include "ItemFood.h"
#include "ItemGoldenApple.h"
-#include "ItemItemFrame.h"
#include "ItemHoe.h"
+#include "ItemItemFrame.h"
#include "ItemLeaves.h"
#include "ItemLighter.h"
#include "ItemLilypad.h"
#include "ItemMap.h"
+#include "ItemMilk.h"
#include "ItemMinecart.h"
+#include "ItemMobHead.h"
#include "ItemMushroomSoup.h"
#include "ItemNetherWart.h"
#include "ItemPainting.h"
#include "ItemPickaxe.h"
#include "ItemPotion.h"
-#include "ItemThrowable.h"
+#include "ItemPumpkin.h"
#include "ItemRedstoneDust.h"
#include "ItemRedstoneRepeater.h"
#include "ItemSapling.h"
@@ -46,11 +50,12 @@
#include "ItemShears.h"
#include "ItemShovel.h"
#include "ItemSign.h"
-#include "ItemMobHead.h"
+#include "ItemSlab.h"
#include "ItemSpawnEgg.h"
#include "ItemString.h"
#include "ItemSugarcane.h"
#include "ItemSword.h"
+#include "ItemThrowable.h"
#include "../Blocks/BlockHandler.h"
@@ -94,52 +99,58 @@ cItemHandler * cItemHandler::GetItemHandler(int a_ItemType)
-cItemHandler *cItemHandler::CreateItemHandler(int a_ItemType)
+cItemHandler * cItemHandler::CreateItemHandler(int a_ItemType)
{
switch (a_ItemType)
{
default: return new cItemHandler(a_ItemType);
// Single item per handler, alphabetically sorted:
- case E_BLOCK_LEAVES: return new cItemLeavesHandler(a_ItemType);
- case E_BLOCK_NEW_LEAVES: return new cItemLeavesHandler(a_ItemType);
- case E_BLOCK_SAPLING: return new cItemSaplingHandler(a_ItemType);
- case E_BLOCK_WOOL: return new cItemClothHandler(a_ItemType);
- case E_ITEM_BED: return new cItemBedHandler(a_ItemType);
- case E_ITEM_BOAT: return new cItemBoatHandler(a_ItemType);
+ case E_BLOCK_CHEST: return new cItemChestHandler(a_ItemType);
+ case E_BLOCK_LEAVES: return new cItemLeavesHandler(a_ItemType);
+ case E_BLOCK_HEAD: return new cItemMobHeadHandler(a_ItemType);
+ case E_BLOCK_NEW_LEAVES: return new cItemLeavesHandler(a_ItemType);
+ case E_BLOCK_PUMPKIN: return new cItemPumpkinHandler;
+ case E_BLOCK_SAPLING: return new cItemSaplingHandler(a_ItemType);
+ case E_BLOCK_STONE_SLAB: return new cItemSlabHandler(E_BLOCK_STONE_SLAB, E_BLOCK_DOUBLE_STONE_SLAB);
+ case E_BLOCK_TRAPPED_CHEST: return new cItemChestHandler(a_ItemType);
+ case E_BLOCK_WOODEN_SLAB: return new cItemSlabHandler(E_BLOCK_WOODEN_SLAB, E_BLOCK_DOUBLE_WOODEN_SLAB);
+ case E_BLOCK_WOOL: return new cItemClothHandler(a_ItemType);
+ case E_ITEM_BED: return new cItemBedHandler(a_ItemType);
+ case E_ITEM_BOAT: return new cItemBoatHandler(a_ItemType);
case E_ITEM_BOTTLE_O_ENCHANTING: return new cItemBottleOEnchantingHandler();
- case E_ITEM_BOW: return new cItemBowHandler();
- case E_ITEM_BREWING_STAND: return new cItemBrewingStandHandler(a_ItemType);
- case E_ITEM_CAKE: return new cItemCakeHandler(a_ItemType);
- case E_ITEM_CAULDRON: return new cItemCauldronHandler(a_ItemType);
- case E_ITEM_COMPARATOR: return new cItemComparatorHandler(a_ItemType);
- case E_ITEM_DYE: return new cItemDyeHandler(a_ItemType);
- case E_ITEM_EGG: return new cItemEggHandler();
- case E_ITEM_EMPTY_MAP: return new cItemEmptyMapHandler();
- case E_ITEM_ENDER_PEARL: return new cItemEnderPearlHandler();
- case E_ITEM_FIRE_CHARGE: return new cItemLighterHandler(a_ItemType);
- case E_ITEM_FIREWORK_ROCKET: return new cItemFireworkHandler();
- case E_ITEM_FISHING_ROD: return new cItemFishingRodHandler(a_ItemType);
- case E_ITEM_FLINT_AND_STEEL: return new cItemLighterHandler(a_ItemType);
- case E_ITEM_FLOWER_POT: return new cItemFlowerPotHandler(a_ItemType);
- case E_ITEM_GOLDEN_APPLE: return new cItemGoldenAppleHandler();
- case E_BLOCK_LILY_PAD: return new cItemLilypadHandler(a_ItemType);
- case E_ITEM_MAP: return new cItemMapHandler();
- case E_ITEM_MILK: return new cItemMilkHandler();
- case E_ITEM_MUSHROOM_SOUP: return new cItemMushroomSoupHandler(a_ItemType);
- case E_ITEM_ITEM_FRAME: return new cItemItemFrameHandler(a_ItemType);
- case E_ITEM_NETHER_WART: return new cItemNetherWartHandler(a_ItemType);
- case E_ITEM_PAINTING: return new cItemPaintingHandler(a_ItemType);
- case E_ITEM_POTIONS: return new cItemPotionHandler();
- case E_ITEM_REDSTONE_DUST: return new cItemRedstoneDustHandler(a_ItemType);
- case E_ITEM_REDSTONE_REPEATER: return new cItemRedstoneRepeaterHandler(a_ItemType);
- case E_ITEM_SHEARS: return new cItemShearsHandler(a_ItemType);
- case E_ITEM_SIGN: return new cItemSignHandler(a_ItemType);
- case E_ITEM_HEAD: return new cItemMobHeadHandler(a_ItemType);
- case E_ITEM_SNOWBALL: return new cItemSnowballHandler();
- 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_BOW: return new cItemBowHandler();
+ case E_ITEM_BREWING_STAND: return new cItemBrewingStandHandler(a_ItemType);
+ case E_ITEM_CAKE: return new cItemCakeHandler(a_ItemType);
+ case E_ITEM_CAULDRON: return new cItemCauldronHandler(a_ItemType);
+ case E_ITEM_COMPARATOR: return new cItemComparatorHandler(a_ItemType);
+ case E_ITEM_DYE: return new cItemDyeHandler(a_ItemType);
+ case E_ITEM_EGG: return new cItemEggHandler();
+ case E_ITEM_EMPTY_MAP: return new cItemEmptyMapHandler();
+ case E_ITEM_ENDER_PEARL: return new cItemEnderPearlHandler();
+ case E_ITEM_FIRE_CHARGE: return new cItemLighterHandler(a_ItemType);
+ case E_ITEM_FIREWORK_ROCKET: return new cItemFireworkHandler();
+ case E_ITEM_FISHING_ROD: return new cItemFishingRodHandler(a_ItemType);
+ case E_ITEM_FLINT_AND_STEEL: return new cItemLighterHandler(a_ItemType);
+ case E_ITEM_FLOWER_POT: return new cItemFlowerPotHandler(a_ItemType);
+ case E_ITEM_GOLDEN_APPLE: return new cItemGoldenAppleHandler();
+ case E_BLOCK_LILY_PAD: return new cItemLilypadHandler(a_ItemType);
+ case E_ITEM_MAP: return new cItemMapHandler();
+ case E_ITEM_MILK: return new cItemMilkHandler();
+ case E_ITEM_MUSHROOM_SOUP: return new cItemMushroomSoupHandler(a_ItemType);
+ case E_ITEM_ITEM_FRAME: return new cItemItemFrameHandler(a_ItemType);
+ case E_ITEM_NETHER_WART: return new cItemNetherWartHandler(a_ItemType);
+ case E_ITEM_PAINTING: return new cItemPaintingHandler(a_ItemType);
+ case E_ITEM_POTIONS: return new cItemPotionHandler();
+ case E_ITEM_REDSTONE_DUST: return new cItemRedstoneDustHandler(a_ItemType);
+ case E_ITEM_REDSTONE_REPEATER: return new cItemRedstoneRepeaterHandler(a_ItemType);
+ case E_ITEM_SHEARS: return new cItemShearsHandler(a_ItemType);
+ case E_ITEM_SIGN: return new cItemSignHandler(a_ItemType);
+ case E_ITEM_HEAD: return new cItemMobHeadHandler(a_ItemType);
+ case E_ITEM_SNOWBALL: return new cItemSnowballHandler();
+ 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_WOODEN_HOE:
case E_ITEM_STONE_HOE:
@@ -297,6 +308,108 @@ cItemHandler::cItemHandler(int a_ItemType)
+bool cItemHandler::OnPlayerPlace(
+ cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem,
+ int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
+ int a_CursorX, int a_CursorY, int a_CursorZ
+)
+{
+ if (a_BlockFace < 0)
+ {
+ // Clicked in air
+ return false;
+ }
+
+ if ((a_BlockY < 0) || (a_BlockY >= cChunkDef::Height))
+ {
+ // The clicked block is outside the world, ignore this call altogether (#128)
+ return false;
+ }
+
+ BLOCKTYPE ClickedBlock;
+ NIBBLETYPE ClickedBlockMeta;
+
+ a_World.GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, ClickedBlock, ClickedBlockMeta);
+
+ // Check if the block ignores build collision (water, grass etc.):
+ if (
+ BlockHandler(ClickedBlock)->DoesIgnoreBuildCollision() ||
+ BlockHandler(ClickedBlock)->DoesIgnoreBuildCollision(&a_Player, ClickedBlockMeta)
+ )
+ {
+ cChunkInterface ChunkInterface(a_World.GetChunkMap());
+ BlockHandler(ClickedBlock)->OnDestroyedByPlayer(ChunkInterface, a_World, &a_Player, a_BlockX, a_BlockY, a_BlockZ);
+ }
+ else
+ {
+ AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
+
+ if ((a_BlockY < 0) || (a_BlockY >= cChunkDef::Height))
+ {
+ // The block is being placed outside the world, ignore this packet altogether (#128)
+ return false;
+ }
+
+ NIBBLETYPE PlaceMeta;
+ BLOCKTYPE PlaceBlock;
+ a_World.GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, PlaceBlock, PlaceMeta);
+
+ // Clicked on side of block, make sure that placement won't be cancelled if there is a slab able to be double slabbed.
+ // No need to do combinability (dblslab) checks, client will do that here.
+ if (
+ !BlockHandler(PlaceBlock)->DoesIgnoreBuildCollision() &&
+ !BlockHandler(PlaceBlock)->DoesIgnoreBuildCollision(&a_Player, PlaceMeta)
+ )
+ {
+ // Tried to place a block *into* another?
+ // Happens when you place a block aiming at side of block with a torch on it or stem beside it
+ return false;
+ }
+ }
+
+ BLOCKTYPE BlockType;
+ NIBBLETYPE BlockMeta;
+ if (!GetPlacementBlockTypeMeta(&a_World, &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, BlockType, BlockMeta))
+ {
+ // Handler refused the placement, send that information back to the client:
+ a_World.SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, &a_Player);
+ a_Player.GetInventory().SendEquippedSlot();
+ return false;
+ }
+
+ if (!a_Player.PlaceBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta))
+ {
+ // The placement failed, the block has already been re-sent, re-send inventory:
+ a_Player.GetInventory().SendEquippedSlot();
+ return false;
+ }
+
+ AString PlaceSound = cBlockInfo::GetPlaceSound(BlockType);
+ float Volume = 1.0f, Pitch = 0.8f;
+ if (PlaceSound == "dig.metal")
+ {
+ Pitch = 1.2f;
+ PlaceSound = "dig.stone";
+ }
+ else if (PlaceSound == "random.anvil_land")
+ {
+ Volume = 0.65f;
+ }
+
+ a_World.BroadcastSoundEffect(PlaceSound, a_BlockX + 0.5, a_BlockY + 0.5, a_BlockZ + 0.5, Volume, Pitch);
+
+ // Remove the "placed" item:
+ if (a_Player.IsGameModeSurvival())
+ {
+ a_Player.GetInventory().RemoveOneEquippedItem();
+ }
+ return true;
+}
+
+
+
+
+
bool cItemHandler::OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_Dir)
{
UNUSED(a_World);
diff --git a/src/Items/ItemHandler.h b/src/Items/ItemHandler.h
index 67c250a97..3ac664798 100644
--- a/src/Items/ItemHandler.h
+++ b/src/Items/ItemHandler.h
@@ -31,10 +31,35 @@ public:
/** Force virtual destructor */
virtual ~cItemHandler() {}
+
+
+ /** Called when the player tries to place the item (right mouse button, IsPlaceable() == true).
+ The default handler uses GetPlacementBlockTypeMeta and places the returned block.
+ Override this function for advanced behavior such as placing multiple blocks.
+ If the block placement is refused inside this call, it will automatically revert the client-side changes.
+ Returns true if the placement succeeded, false if the placement was aborted for any reason. */
+ virtual bool OnPlayerPlace(
+ cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem,
+ int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
+ int a_CursorX, int a_CursorY, int a_CursorZ
+ );
+
+ /** Called when the player right-clicks with this item and IsPlaceable() == true, and OnPlace() is not overridden.
+ This function should provide the block type and meta for the placed block, or refuse the placement.
+ Returns true to allow placement, false to refuse. */
+ virtual bool GetPlacementBlockTypeMeta(
+ cWorld * a_World, cPlayer * a_Player,
+ int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
+ int a_CursorX, int a_CursorY, int a_CursorZ,
+ BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
+ );
+
+
/** Called when the player tries to use the item (right mouse button). Return false to make the item unusable. DEFAULT: False */
virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_Dir);
+
/** Called when the client sends the SHOOT status in the lclk packet */
virtual void OnItemShoot(cPlayer *, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace)
{
@@ -106,18 +131,8 @@ public:
/** Can the anvil repair this item, when a_Item is the second input? */
virtual bool CanRepairWithRawMaterial(short a_ItemType);
- /** Called before a block is placed into a world.
- The handler should return true to allow placement, false to refuse.
- Also, the handler should set a_BlockType and a_BlockMeta to correct values for the newly placed block.
- */
- virtual bool GetPlacementBlockTypeMeta(
- cWorld * a_World, cPlayer * a_Player,
- int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
- int a_CursorX, int a_CursorY, int a_CursorZ,
- BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
- );
-
- /** Returns whether this tool/item can harvest a specific block (e.g. wooden pickaxe can harvest stone, but wood can't) DEFAULT: False */
+ /** Returns whether this tool / item can harvest a specific block (e.g. iron pickaxe can harvest diamond ore, but wooden one can't).
+ Defaults to false unless overridden. */
virtual bool CanHarvestBlock(BLOCKTYPE a_BlockType);
static cItemHandler * GetItemHandler(int a_ItemType);
diff --git a/src/Items/ItemMobHead.h b/src/Items/ItemMobHead.h
index 4c36fe8d8..d962dabae 100644
--- a/src/Items/ItemMobHead.h
+++ b/src/Items/ItemMobHead.h
@@ -3,6 +3,7 @@
#include "ItemHandler.h"
#include "../World.h"
+#include "../BlockEntities/MobHeadEntity.h"
@@ -18,6 +19,266 @@ public:
}
+ virtual bool OnPlayerPlace(
+ cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem,
+ int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
+ int a_CursorX, int a_CursorY, int a_CursorZ
+ ) override
+ {
+ // Cannot place a head at "no face" and from the bottom:
+ if ((a_BlockFace == BLOCK_FACE_NONE) || (a_BlockFace == BLOCK_FACE_BOTTOM))
+ {
+ return true;
+ }
+ AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
+
+ // If the placed head is a wither, try to spawn the wither first:
+ if (a_EquippedItem.m_ItemDamage == E_META_HEAD_WITHER)
+ {
+ if (TrySpawnWitherAround(a_World, a_Player, a_BlockX, a_BlockY, a_BlockZ))
+ {
+ return true;
+ }
+ // Wither not created, proceed with regular head placement
+ }
+
+ return PlaceRegularHead(a_World, a_Player, a_EquippedItem, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
+ }
+
+
+ /** Places a regular head block with no mob spawning checking. */
+ bool PlaceRegularHead(
+ cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem,
+ int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace
+ )
+ {
+ // Place the block:
+ if (!a_Player.PlaceBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_HEAD, static_cast<NIBBLETYPE>(a_EquippedItem.m_ItemType)))
+ {
+ return false;
+ }
+
+ // Use a callback to set the properties of the mob head block entity:
+ class cCallback : public cBlockEntityCallback
+ {
+ cPlayer & m_Player;
+ eMobHeadType m_HeadType;
+ NIBBLETYPE m_BlockMeta;
+
+ virtual bool Item(cBlockEntity * a_BlockEntity)
+ {
+ if (a_BlockEntity->GetBlockType() != E_BLOCK_HEAD)
+ {
+ return false;
+ }
+ cMobHeadEntity * MobHeadEntity = static_cast<cMobHeadEntity *>(a_BlockEntity);
+
+ int Rotation = 0;
+ if (m_BlockMeta == 1)
+ {
+ Rotation = FloorC(m_Player.GetYaw() * 16.0f / 360.0f + 0.5f) & 0x0f;
+ }
+
+ MobHeadEntity->SetType(m_HeadType);
+ MobHeadEntity->SetRotation(static_cast<eMobHeadRotation>(Rotation));
+ MobHeadEntity->GetWorld()->BroadcastBlockEntity(MobHeadEntity->GetPosX(), MobHeadEntity->GetPosY(), MobHeadEntity->GetPosZ());
+ return false;
+ }
+
+ public:
+ cCallback (cPlayer & a_CBPlayer, eMobHeadType a_HeadType, NIBBLETYPE a_BlockMeta) :
+ m_Player(a_CBPlayer),
+ m_HeadType(a_HeadType),
+ m_BlockMeta(a_BlockMeta)
+ {}
+ };
+ cCallback Callback(a_Player, static_cast<eMobHeadType>(a_EquippedItem.m_ItemType), static_cast<NIBBLETYPE>(a_BlockFace));
+ a_World.DoWithBlockEntityAt(a_BlockX, a_BlockY, a_BlockZ, Callback);
+ return true;
+ }
+
+
+ /** Spawns a wither if the wither skull placed at the specified coords completes wither's spawning formula.
+ Returns true if the wither was created. */
+ bool TrySpawnWitherAround(
+ cWorld & a_World, cPlayer & a_Player,
+ int a_BlockX, int a_BlockY, int a_BlockZ
+ )
+ {
+ // No wither can be created at Y < 2 - not enough space for the formula:
+ if (a_BlockY < 2)
+ {
+ return false;
+ }
+
+ // Check for all relevant wither locations:
+ static const Vector3i RelCoords[] =
+ {
+ { 0, 0, 0},
+ { 1, 0, 0},
+ {-1, 0, 0},
+ { 0, 0, 1},
+ { 0, 0, -1},
+ };
+ for (size_t i = 0; i < ARRAYCOUNT(RelCoords); ++i)
+ {
+ if (TrySpawnWitherAt(
+ a_World, a_Player,
+ a_BlockX, a_BlockY, a_BlockZ,
+ RelCoords[i].x, RelCoords[i].z
+ ))
+ {
+ return true;
+ }
+ } // for i - Coords[]
+
+ return false;
+ }
+
+
+ /** Tries to spawn a wither at the specified offset from the placed head block.
+ PlacedHead coords are used to override the block query - at those coords the block is not queried from the world,
+ but assumed to be a head instead.
+ Offset is used to shift the image around the X and Z axis.
+ Returns true iff the wither was created successfully. */
+ bool TrySpawnWitherAt(
+ cWorld & a_World, cPlayer & a_Player,
+ int a_PlacedHeadX, int a_PlacedHeadY, int a_PlacedHeadZ,
+ int a_OffsetX, int a_OffsetZ
+ )
+ {
+ // Image for the wither at the X axis:
+ static const sSetBlock ImageWitherX[] =
+ {
+ {-1, 0, 0, E_BLOCK_HEAD, E_META_HEAD_WITHER},
+ { 0, 0, 0, E_BLOCK_HEAD, E_META_HEAD_WITHER},
+ { 1, 0, 0, E_BLOCK_HEAD, E_META_HEAD_WITHER},
+ {-1, -1, 0, E_BLOCK_SOULSAND, 0},
+ { 0, -1, 0, E_BLOCK_SOULSAND, 0},
+ { 1, -1, 0, E_BLOCK_SOULSAND, 0},
+ {-1, -2, 0, E_BLOCK_AIR, 0},
+ { 0, -2, 0, E_BLOCK_SOULSAND, 0},
+ { 1, -2, 0, E_BLOCK_AIR, 0},
+ };
+
+ // Image for the wither at the Z axis:
+ static const sSetBlock ImageWitherZ[] =
+ {
+ { 0, 0, -1, E_BLOCK_HEAD, E_META_HEAD_WITHER},
+ { 0, 0, 0, E_BLOCK_HEAD, E_META_HEAD_WITHER},
+ { 0, 0, 1, E_BLOCK_HEAD, E_META_HEAD_WITHER},
+ { 0, -1, -1, E_BLOCK_SOULSAND, 0},
+ { 0, -1, 0, E_BLOCK_SOULSAND, 0},
+ { 0, -1, 1, E_BLOCK_SOULSAND, 0},
+ { 0, -2, -1, E_BLOCK_AIR, 0},
+ { 0, -2, 0, E_BLOCK_SOULSAND, 0},
+ { 0, -2, 1, E_BLOCK_AIR, 0},
+ };
+
+ // Try to spawn the wither from each image:
+ return (
+ TrySpawnWitherFromImage(
+ a_World, a_Player, ImageWitherX, ARRAYCOUNT(ImageWitherX),
+ a_PlacedHeadX, a_PlacedHeadY, a_PlacedHeadZ,
+ a_OffsetX, a_OffsetZ
+ ) ||
+ TrySpawnWitherFromImage(
+ a_World, a_Player, ImageWitherZ, ARRAYCOUNT(ImageWitherZ),
+ a_PlacedHeadX, a_PlacedHeadY, a_PlacedHeadZ,
+ a_OffsetX, a_OffsetZ
+ )
+ );
+ }
+
+
+ /** Tries to spawn a wither from the specified image at the specified offset from the placed head block.
+ PlacedHead coords are used to override the block query - at those coords the block is not queried from the world,
+ but assumed to be a head instead.
+ Offset is used to shift the image around the X and Z axis.
+ Returns true iff the wither was created successfully. */
+ bool TrySpawnWitherFromImage(
+ cWorld & a_World, cPlayer & a_Player, const sSetBlock * a_Image, size_t a_ImageCount,
+ int a_PlacedHeadX, int a_PlacedHeadY, int a_PlacedHeadZ,
+ int a_OffsetX, int a_OffsetZ
+ )
+ {
+ // Check each block individually; simultaneously build the SetBlockVector for clearing the blocks:
+ sSetBlockVector AirBlocks;
+ AirBlocks.reserve(a_ImageCount);
+ for (size_t i = 0; i < a_ImageCount; i++)
+ {
+ // Get the absolute coords of the image:
+ int BlockX = a_PlacedHeadX + a_OffsetX + a_Image[i].m_RelX;
+ int BlockY = a_PlacedHeadY + a_Image[i].m_RelY;
+ int BlockZ = a_PlacedHeadZ + a_OffsetZ + a_Image[i].m_RelZ;
+
+ // If the query is for the placed head, short-circuit-evaluate it:
+ if ((BlockX == a_PlacedHeadX) && (BlockY == a_PlacedHeadY) && (BlockZ == a_PlacedHeadZ))
+ {
+ if ((a_Image[i].m_BlockType != E_BLOCK_HEAD) || (a_Image[i].m_BlockMeta != E_META_HEAD_WITHER))
+ {
+ return false; // Didn't match
+ }
+ continue; // Matched, continue checking the rest of the image
+ }
+
+ // Query the world block:
+ BLOCKTYPE BlockType;
+ NIBBLETYPE BlockMeta;
+ if (!a_World.GetBlockTypeMeta(BlockX, BlockY, BlockZ, BlockType, BlockMeta))
+ {
+ // Cannot query block, assume unloaded chunk, fail to spawn the wither
+ return false;
+ }
+
+ // Compare the world block:
+ if ((BlockType != a_Image[i].m_BlockType) || (BlockMeta != a_Image[i].m_BlockMeta))
+ {
+ return false; // Didn't match
+ }
+ // Matched, continue checking
+ } // for i - a_Image
+
+ // All image blocks matched, try place the wither:
+ if (!a_Player.PlaceBlocks(AirBlocks))
+ {
+ return false;
+ }
+
+ // Spawn the wither:
+ int BlockX = a_PlacedHeadX + a_OffsetX;
+ int BlockZ = a_PlacedHeadZ + a_OffsetZ;
+ a_World.SpawnMob(static_cast<double>(BlockX) + 0.5, a_PlacedHeadY - 2, static_cast<double>(BlockZ) + 0.5, mtWither);
+ AwardSpawnWitherAchievement(a_World, BlockX, a_PlacedHeadY - 2, BlockZ);
+ return true;
+ }
+
+
+ /** Awards the achievement to all players close to the specified point. */
+ void AwardSpawnWitherAchievement(cWorld & a_World, int a_BlockX, int a_BlockY, int a_BlockZ)
+ {
+ class cPlayerCallback : public cPlayerListCallback
+ {
+ Vector3f m_Pos;
+
+ virtual bool Item(cPlayer * a_Player)
+ {
+ // If player is close, award achievement:
+ double Dist = (a_Player->GetPosition() - m_Pos).Length();
+ if (Dist < 50.0)
+ {
+ a_Player->AwardAchievement(achSpawnWither);
+ }
+ return false;
+ }
+
+ public:
+ cPlayerCallback(const Vector3f & a_Pos) : m_Pos(a_Pos) {}
+ } PlayerCallback(Vector3f(static_cast<float>(a_BlockX), static_cast<float>(a_BlockY), static_cast<float>(a_BlockZ)));
+ a_World.ForEachPlayer(PlayerCallback);
+ }
+
+
virtual bool IsPlaceable(void) override
{
return true;
diff --git a/src/Items/ItemPumpkin.h b/src/Items/ItemPumpkin.h
new file mode 100644
index 000000000..fa00179d3
--- /dev/null
+++ b/src/Items/ItemPumpkin.h
@@ -0,0 +1,156 @@
+
+// ItemPumpkin.h
+
+// Declares the cItemPumpkinHandler class representing the pumpkin block in its item form
+
+
+
+
+
+#pragma once
+
+#include "ItemHandler.h"
+
+
+
+
+
+class cItemPumpkinHandler:
+ public cItemHandler
+{
+ typedef cItemHandler super;
+
+public:
+ cItemPumpkinHandler(void):
+ super(E_BLOCK_PUMPKIN)
+ {
+ }
+
+
+ virtual bool OnPlayerPlace(
+ cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem,
+ int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
+ int a_CursorX, int a_CursorY, int a_CursorZ
+ ) override
+ {
+ // First try spawning a snow golem or an iron golem:
+ int PlacedBlockX = a_BlockX;
+ int PlacedBlockY = a_BlockY;
+ int PlacedBlockZ = a_BlockZ;
+ AddFaceDirection(PlacedBlockX, PlacedBlockY, PlacedBlockZ, a_BlockFace);
+ if (TrySpawnGolem(a_World, a_Player, PlacedBlockX, PlacedBlockY, PlacedBlockZ))
+ {
+ // The client thinks that they placed the pumpkin, let them know it's been replaced:
+ a_Player.SendBlocksAround(PlacedBlockX, PlacedBlockY, PlacedBlockZ);
+ return true;
+ }
+
+ // No golem at these coords, place the block normally:
+ return super::OnPlayerPlace(a_World, a_Player, a_EquippedItem, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ);
+ }
+
+
+ /** Spawns a snow / iron golem if the shape matches the recipe, supposing that the block placed at the specified coords is a pumpkin.
+ Returns true if the golem blocks are removed (for spawning), false if the recipe is not matched. */
+ bool TrySpawnGolem(cWorld & a_World, cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ)
+ {
+ // A golem can't form with a pumpkin below level 2 or above level 255
+ if ((a_BlockY < 2) || (a_BlockY >= cChunkDef::Height))
+ {
+ return false;
+ }
+
+ // Decide which golem to try spawning based on the block below the placed pumpkin:
+ switch (a_World.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ))
+ {
+ case E_BLOCK_SNOW_BLOCK: return TrySpawnSnowGolem(a_World, a_Player, a_BlockX, a_BlockY, a_BlockZ);
+ case E_BLOCK_IRON_BLOCK: return TrySpawnIronGolem(a_World, a_Player, a_BlockX, a_BlockY, a_BlockZ);
+ default:
+ {
+ // No golem here
+ return false;
+ }
+ }
+ }
+
+
+ /** Spawns a snow golem if the shape matches the recipe, supposing that the block placed at the specified coords is a pumpkin.
+ Returns true if the golem blocks are removed (for spawning), false if the recipe is not matched.
+ Assumes that the block below the specified block has already been checked and is a snow block. */
+ bool TrySpawnSnowGolem(cWorld & a_World, cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ)
+ {
+ // Need one more snow block 2 blocks below the pumpkin:
+ if (a_World.GetBlock(a_BlockX, a_BlockY - 2, a_BlockZ) != E_BLOCK_SNOW_BLOCK)
+ {
+ return false;
+ }
+
+ // Try to place air blocks where the original recipe blocks were:
+ sSetBlockVector AirBlocks;
+ AirBlocks.emplace_back(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); // Head
+ AirBlocks.emplace_back(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); // Torso
+ AirBlocks.emplace_back(a_BlockX, a_BlockY - 2, a_BlockZ, E_BLOCK_AIR, 0); // Legs
+ if (!a_Player.PlaceBlocks(AirBlocks))
+ {
+ return false;
+ }
+
+ // Spawn the golem:
+ a_World.SpawnMob(static_cast<double>(a_BlockX) + 0.5, a_BlockY - 2, static_cast<double>(a_BlockZ) + 0.5, mtSnowGolem);
+ return true;
+ }
+
+
+ /** Spawns an iron golem if the shape matches the recipe, supposing that the block placed at the specified coords is a pumpkin.
+ Returns true if the golem blocks are removed (for spawning), false if the recipe is not matched.
+ Assumes that the block below the specified block has already been checked and is an iron block. */
+ bool TrySpawnIronGolem(cWorld & a_World, cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ)
+ {
+ // Need one more iron block 2 blocks below the pumpkin:
+ if (a_World.GetBlock(a_BlockX, a_BlockY - 2, a_BlockZ) != E_BLOCK_IRON_BLOCK)
+ {
+ return false;
+ }
+
+ // Check the two arm directions (X, Z) using a loop over two sets of offset vectors:
+ static const Vector3i ArmOffsets[] =
+ {
+ {1, 0, 0},
+ {0, 0, 1},
+ };
+ for (size_t i = 0; i < ARRAYCOUNT(ArmOffsets); i++)
+ {
+ // If the arm blocks don't match, bail out of this loop repetition:
+ if (
+ (a_World.GetBlock(a_BlockX + ArmOffsets[i].x, a_BlockY - 1, a_BlockZ + ArmOffsets[i].z) != E_BLOCK_IRON_BLOCK) ||
+ (a_World.GetBlock(a_BlockX - ArmOffsets[i].x, a_BlockY - 1, a_BlockZ - ArmOffsets[i].z) != E_BLOCK_IRON_BLOCK)
+ )
+ {
+ continue;
+ }
+
+ // Try to place air blocks where the original recipe blocks were:
+ sSetBlockVector AirBlocks;
+ AirBlocks.emplace_back(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); // Head
+ AirBlocks.emplace_back(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); // Torso
+ AirBlocks.emplace_back(a_BlockX, a_BlockY - 2, a_BlockZ, E_BLOCK_AIR, 0); // Legs
+ AirBlocks.emplace_back(a_BlockX + ArmOffsets[i].x, a_BlockY - 1, a_BlockZ + ArmOffsets[i].z, E_BLOCK_AIR, 0); // Arm
+ AirBlocks.emplace_back(a_BlockX - ArmOffsets[i].x, a_BlockY - 1, a_BlockZ - ArmOffsets[i].z, E_BLOCK_AIR, 0); // Arm
+ if (!a_Player.PlaceBlocks(AirBlocks))
+ {
+ return false;
+ }
+
+ // Spawn the golem:
+ a_World.SpawnMob(static_cast<double>(a_BlockX) + 0.5, a_BlockY - 2, static_cast<double>(a_BlockZ) + 0.5, mtIronGolem);
+ return true;
+ } // for i - ArmOffsets[]
+
+ // Neither arm offset matched, this thing is not a complete golem
+ return false;
+ }
+};
+
+
+
+
diff --git a/src/Items/ItemRedstoneDust.h b/src/Items/ItemRedstoneDust.h
index a2289239c..6d5fb521f 100644
--- a/src/Items/ItemRedstoneDust.h
+++ b/src/Items/ItemRedstoneDust.h
@@ -27,7 +27,20 @@ public:
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) override
{
- if (!cBlockInfo::FullyOccupiesVoxel(a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ))) // Some solid blocks, such as cocoa beans, are not suitable for dust
+ // Check if coords are out of range:
+ if ((a_BlockY <= 0) || (a_BlockY >= cChunkDef::Height))
+ {
+ return false;
+ }
+
+ // Check the block below, if it supports dust on top of it:
+ BLOCKTYPE BlockType;
+ NIBBLETYPE BlockMeta;
+ if (!a_World->GetBlockTypeMeta(a_BlockX, a_BlockY - 1, a_BlockZ, BlockType, BlockMeta))
+ {
+ return false;
+ }
+ if (!IsBlockTypeUnderSuitable(BlockType, BlockMeta))
{
return false;
}
@@ -36,6 +49,28 @@ public:
a_BlockMeta = 0;
return true;
}
+
+
+ /** Returns true if the specified block type / meta is suitable to have redstone dust on top of it. */
+ static bool IsBlockTypeUnderSuitable(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
+ {
+ if (cBlockInfo::FullyOccupiesVoxel(a_BlockType))
+ {
+ return true;
+ }
+
+ switch (a_BlockType)
+ {
+ case E_BLOCK_NEW_STONE_SLAB:
+ case E_BLOCK_WOODEN_SLAB:
+ case E_BLOCK_STONE_SLAB:
+ {
+ // Slabs can support redstone if they're upside down:
+ return ((a_BlockMeta & 0x08) != 0);
+ }
+ }
+ return false;
+ }
} ;
diff --git a/src/Items/ItemSign.h b/src/Items/ItemSign.h
index 0fa0fa0be..dabbdbba1 100644
--- a/src/Items/ItemSign.h
+++ b/src/Items/ItemSign.h
@@ -13,13 +13,33 @@
class cItemSignHandler :
public cItemHandler
{
+ typedef cItemHandler super;
public:
cItemSignHandler(int a_ItemType) :
- cItemHandler(a_ItemType)
+ super(a_ItemType)
{
}
+ virtual bool OnPlayerPlace(
+ cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem,
+ int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
+ int a_CursorX, int a_CursorY, int a_CursorZ
+ )
+ {
+ // If the regular placement doesn't work, do no further processing:
+ if (!super::OnPlayerPlace(a_World, a_Player, a_EquippedItem, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ))
+ {
+ return false;
+ }
+
+ // After successfully placing the sign, open the sign editor for the player:
+ AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
+ a_Player.GetClientHandle()->SendEditSign(a_BlockX, a_BlockY, a_BlockZ);
+ return true;
+ }
+
+
virtual bool IsPlaceable(void) override
{
return true;
diff --git a/src/Items/ItemSlab.h b/src/Items/ItemSlab.h
new file mode 100644
index 000000000..1b68b9d0c
--- /dev/null
+++ b/src/Items/ItemSlab.h
@@ -0,0 +1,93 @@
+
+// ItemSlab.h
+
+// Declares the cItemSlabHandler responsible for handling slabs, when in their item form.
+
+
+
+
+
+#pragma once
+
+#include "ItemHandler.h"
+#include "../Blocks/BlockSlab.h"
+
+
+
+
+
+class cItemSlabHandler:
+ public cItemHandler
+{
+ typedef cItemHandler super;
+
+public:
+
+ /** Creates a new handler for the specified slab item type.
+ Sets the handler to use the specified doubleslab block type for combining self into doubleslabs. */
+ cItemSlabHandler(int a_ItemType, BLOCKTYPE a_DoubleSlabBlockType):
+ super(a_ItemType),
+ m_DoubleSlabBlockType(a_DoubleSlabBlockType)
+ {
+ }
+
+
+ // cItemHandler overrides:
+ virtual bool OnPlayerPlace(
+ cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem,
+ int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
+ int a_CursorX, int a_CursorY, int a_CursorZ
+ ) override
+ {
+ // Special slab handling - placing a slab onto another slab produces a dblslab instead:
+ BLOCKTYPE ClickedBlockType;
+ NIBBLETYPE ClickedBlockMeta;
+ a_World.GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, ClickedBlockType, ClickedBlockMeta);
+ if (
+ (ClickedBlockType == m_ItemType) && // Placing the same slab material
+ (ClickedBlockMeta == a_EquippedItem.m_ItemDamage) // Placing the same slab sub-kind (and existing slab is single)
+ )
+ {
+ // If clicking the top side of a bottom-half slab, combine into a doubleslab:
+ if (
+ (a_BlockFace == BLOCK_FACE_TOP) &&
+ ((ClickedBlockMeta & 0x08) == 0)
+ )
+ {
+ return a_Player.PlaceBlock(a_BlockX, a_BlockY, a_BlockZ, m_DoubleSlabBlockType, ClickedBlockMeta & 0x07);
+ }
+
+ // If clicking the bottom side of a top-half slab, combine into a doubleslab:
+ if (
+ (a_BlockFace == BLOCK_FACE_BOTTOM) &&
+ ((ClickedBlockMeta & 0x08) != 0)
+ )
+ {
+ return a_Player.PlaceBlock(a_BlockX, a_BlockY, a_BlockZ, m_DoubleSlabBlockType, ClickedBlockMeta & 0x07);
+ }
+ }
+
+ // The slabs didn't combine, use the default handler to place the slab:
+ bool res = super::OnPlayerPlace(a_World, a_Player, a_EquippedItem, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ);
+
+ /*
+ The client has a bug when a slab replaces snow and there's a slab above it.
+ The client then combines the slab above, rather than replacing the snow.
+ We send the block above the currently placed block back to the client to fix the bug.
+ Ref.: http://forum.mc-server.org/showthread.php?tid=434&pid=17388#pid17388
+ */
+ if ((a_BlockFace == BLOCK_FACE_TOP) && (a_BlockY < cChunkDef::Height - 1))
+ {
+ a_Player.SendBlocksAround(a_BlockX, a_BlockY + 1, a_BlockZ, 1);
+ }
+ return res;
+ }
+
+protected:
+ /** The block type to use when the slab combines into a doubleslab block. */
+ BLOCKTYPE m_DoubleSlabBlockType;
+};
+
+
+
+
diff --git a/src/Protocol/Protocol17x.cpp b/src/Protocol/Protocol17x.cpp
index 1e33ec433..34103ea5a 100644
--- a/src/Protocol/Protocol17x.cpp
+++ b/src/Protocol/Protocol17x.cpp
@@ -228,8 +228,8 @@ void cProtocol172::SendBlockChanges(int a_ChunkX, int a_ChunkZ, const sSetBlockV
Pkt.WriteInt((int)a_Changes.size() * 4);
for (sSetBlockVector::const_iterator itr = a_Changes.begin(), end = a_Changes.end(); itr != end; ++itr)
{
- unsigned int Coords = itr->y | (itr->z << 8) | (itr->x << 12);
- unsigned int Blocks = itr->BlockMeta | (itr->BlockType << 4);
+ unsigned int Coords = itr->m_RelY | (itr->m_RelZ << 8) | (itr->m_RelX << 12);
+ unsigned int Blocks = itr->m_BlockMeta | (itr->m_BlockType << 4);
Pkt.WriteInt((Coords << 16) | Blocks);
} // for itr - a_Changes[]
}
diff --git a/src/Protocol/Protocol18x.cpp b/src/Protocol/Protocol18x.cpp
index 1a13f4f7c..72827ac47 100644
--- a/src/Protocol/Protocol18x.cpp
+++ b/src/Protocol/Protocol18x.cpp
@@ -207,9 +207,9 @@ void cProtocol180::SendBlockChanges(int a_ChunkX, int a_ChunkZ, const sSetBlockV
Pkt.WriteVarInt((UInt32)a_Changes.size());
for (sSetBlockVector::const_iterator itr = a_Changes.begin(), end = a_Changes.end(); itr != end; ++itr)
{
- short Coords = (short) (itr->y | (itr->z << 8) | (itr->x << 12));
+ short Coords = (short) (itr->m_RelY | (itr->m_RelZ << 8) | (itr->m_RelX << 12));
Pkt.WriteShort(Coords);
- Pkt.WriteVarInt((itr->BlockType & 0xFFF) << 4 | (itr->BlockMeta & 0xF));
+ Pkt.WriteVarInt((itr->m_BlockType & 0xFFF) << 4 | (itr->m_BlockMeta & 0xF));
} // for itr - a_Changes[]
}
diff --git a/src/Vector3.h b/src/Vector3.h
index 1e4a1f5d9..1f3f6b955 100644
--- a/src/Vector3.h
+++ b/src/Vector3.h
@@ -307,6 +307,22 @@ public:
return (a_X - x) / (a_OtherEnd.x - x);
}
+ /** Rotates the vector 90 degrees clockwise around the vertical axis.
+ Note that this is specific to minecraft's axis ordering, which is X+ left, Z+ down. */
+ inline void TurnCW(void)
+ {
+ std::swap(x, z);
+ x = -x;
+ }
+
+ /** Rotates the vector 90 degrees counterclockwise around the vertical axis.
+ Note that this is specific to minecraft's axis ordering, which is X+ left, Z+ down. */
+ inline void TurnCCW(void)
+ {
+ std::swap(x, z);
+ z = -z;
+ }
+
/** The max difference between two coords for which the coords are assumed equal. */
static const double EPS;
diff --git a/src/World.cpp b/src/World.cpp
index ae739a2c3..c08b44f77 100644
--- a/src/World.cpp
+++ b/src/World.cpp
@@ -1463,7 +1463,7 @@ void cWorld::GrowTreeImage(const sSetBlockVector & a_Blocks)
sSetBlockVector b2;
for (sSetBlockVector::const_iterator itr = a_Blocks.begin(); itr != a_Blocks.end(); ++itr)
{
- if (itr->BlockType == E_BLOCK_LOG)
+ if (itr->m_BlockType == E_BLOCK_LOG)
{
b2.push_back(*itr);
}
@@ -1478,7 +1478,7 @@ void cWorld::GrowTreeImage(const sSetBlockVector & a_Blocks)
// Check that at each log's coord there's an block allowed to be overwritten:
for (sSetBlockVector::const_iterator itr = b2.begin(); itr != b2.end(); ++itr)
{
- switch (itr->BlockType)
+ switch (itr->m_BlockType)
{
CASE_TREE_ALLOWED_BLOCKS:
{
@@ -1926,6 +1926,15 @@ void cWorld::SpawnPrimedTNT(double a_X, double a_Y, double a_Z, int a_FuseTicks,
+void cWorld::SetBlocks(const sSetBlockVector & a_Blocks)
+{
+ m_ChunkMap->SetBlocks(a_Blocks);
+}
+
+
+
+
+
void cWorld::ReplaceBlocks(const sSetBlockVector & a_Blocks, BLOCKTYPE a_FilterBlockType)
{
m_ChunkMap->ReplaceBlocks(a_Blocks, a_FilterBlockType);
diff --git a/src/World.h b/src/World.h
index 4f24280a4..2ee33375f 100644
--- a/src/World.h
+++ b/src/World.h
@@ -491,6 +491,11 @@ public:
// tolua_end
+ /** Performs the specified single-block set operations simultaneously, as if SetBlock() was called for each item.
+ Is more efficient than calling SetBlock() multiple times.
+ If the chunk for any of the blocks is not loaded, the set operation is ignored silently. */
+ void SetBlocks(const sSetBlockVector & a_Blocks);
+
/** Replaces world blocks with a_Blocks, if they are of type a_FilterBlockType */
void ReplaceBlocks(const sSetBlockVector & a_Blocks, BLOCKTYPE a_FilterBlockType);