summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Bindings/AllToLua.pkg3
-rw-r--r--src/Bindings/ManualBindings.cpp68
-rw-r--r--src/BlockArea.cpp12
-rw-r--r--src/BlockArea.h57
-rw-r--r--src/BlockID.cpp4
-rw-r--r--src/BlockID.h10
-rw-r--r--src/Blocks/BlockCauldron.h24
-rw-r--r--src/Blocks/BlockHandler.cpp4
-rw-r--r--src/Blocks/BlockLeaves.h2
-rw-r--r--src/Blocks/BlockNewLeaves.h42
-rw-r--r--src/Chunk.cpp7
-rw-r--r--src/Chunk.h1
-rw-r--r--src/ChunkDef.h1
-rw-r--r--src/ChunkMap.cpp4
-rw-r--r--src/ClientHandle.cpp124
-rw-r--r--src/ClientHandle.h76
-rw-r--r--src/Entities/Boat.cpp10
-rw-r--r--src/Entities/Player.cpp3
-rw-r--r--src/Generating/StructGen.cpp9
-rw-r--r--src/Generating/Trees.cpp18
-rw-r--r--src/Generating/Trees.h6
-rw-r--r--src/Globals.h10
-rw-r--r--src/Inventory.cpp25
-rw-r--r--src/Inventory.h3
-rw-r--r--src/Items/ItemEmptyMap.h62
-rw-r--r--src/Items/ItemHandler.cpp4
-rw-r--r--src/Items/ItemHandler.h8
-rw-r--r--src/Items/ItemMap.h43
-rw-r--r--src/LightingThread.h6
-rw-r--r--src/Map.cpp629
-rw-r--r--src/Map.h264
-rw-r--r--src/MapManager.cpp178
-rw-r--r--src/MapManager.h78
-rw-r--r--src/Mobs/Wolf.cpp44
-rw-r--r--src/OSSupport/Queue.h6
-rw-r--r--src/Protocol/Protocol.h4
-rw-r--r--src/Protocol/Protocol125.cpp52
-rw-r--r--src/Protocol/Protocol125.h3
-rw-r--r--src/Protocol/Protocol17x.cpp55
-rw-r--r--src/Protocol/Protocol17x.h3
-rw-r--r--src/Protocol/ProtocolRecognizer.cpp30
-rw-r--r--src/Protocol/ProtocolRecognizer.h3
-rw-r--r--src/Server.cpp4
-rw-r--r--src/Simulator/FireSimulator.cpp1
-rw-r--r--src/Simulator/IncrementalRedstoneSimulator.cpp35
-rw-r--r--src/Simulator/SandSimulator.cpp2
-rw-r--r--src/World.cpp18
-rw-r--r--src/World.h7
-rw-r--r--src/WorldStorage/MapSerializer.cpp276
-rw-r--r--src/WorldStorage/MapSerializer.h86
-rw-r--r--src/main.cpp4
51 files changed, 2297 insertions, 131 deletions
diff --git a/src/Bindings/AllToLua.pkg b/src/Bindings/AllToLua.pkg
index 1f08c66dc..4fd5a68b8 100644
--- a/src/Bindings/AllToLua.pkg
+++ b/src/Bindings/AllToLua.pkg
@@ -48,6 +48,7 @@ $cfile "../ItemGrid.h"
$cfile "../BlockEntities/BlockEntity.h"
$cfile "../BlockEntities/BlockEntityWithItems.h"
$cfile "../BlockEntities/ChestEntity.h"
+$cfile "../BlockEntities/CommandBlockEntity.h"
$cfile "../BlockEntities/DropSpenserEntity.h"
$cfile "../BlockEntities/DispenserEntity.h"
$cfile "../BlockEntities/DropperEntity.h"
@@ -72,6 +73,8 @@ $cfile "../CraftingRecipes.h"
$cfile "../UI/Window.h"
$cfile "../Mobs/Monster.h"
$cfile "../CompositeChat.h"
+$cfile "../Map.h"
+$cfile "../MapManager.h"
diff --git a/src/Bindings/ManualBindings.cpp b/src/Bindings/ManualBindings.cpp
index c220e5e0a..2f3f3ee91 100644
--- a/src/Bindings/ManualBindings.cpp
+++ b/src/Bindings/ManualBindings.cpp
@@ -2350,6 +2350,37 @@ static int tolua_cBlockArea_GetBlockTypeMeta(lua_State * tolua_S)
+static int tolua_cBlockArea_GetOrigin(lua_State * tolua_S)
+{
+ // function cBlockArea::GetOrigin()
+ // Returns all three coords of the origin point
+ // Exported manually because there's no direct C++ equivalent,
+ // plus tolua would generate extra input params for the outputs
+
+ cLuaState L(tolua_S);
+ if (!L.CheckParamUserType(1, "cBlockArea"))
+ {
+ return 0;
+ }
+
+ cBlockArea * self = (cBlockArea *)tolua_tousertype(tolua_S, 1, NULL);
+ if (self == NULL)
+ {
+ tolua_error(tolua_S, "invalid 'self' in function 'cBlockArea:GetOrigin'", NULL);
+ return 0;
+ }
+
+ // Push the three origin coords:
+ lua_pushnumber(tolua_S, self->GetOriginX());
+ lua_pushnumber(tolua_S, self->GetOriginY());
+ lua_pushnumber(tolua_S, self->GetOriginZ());
+ return 3;
+}
+
+
+
+
+
static int tolua_cBlockArea_GetRelBlockTypeMeta(lua_State * tolua_S)
{
// function cBlockArea::GetRelBlockTypeMeta()
@@ -2385,6 +2416,37 @@ static int tolua_cBlockArea_GetRelBlockTypeMeta(lua_State * tolua_S)
+static int tolua_cBlockArea_GetSize(lua_State * tolua_S)
+{
+ // function cBlockArea::GetSize()
+ // Returns all three sizes of the area
+ // Exported manually because there's no direct C++ equivalent,
+ // plus tolua would generate extra input params for the outputs
+
+ cLuaState L(tolua_S);
+ if (!L.CheckParamUserType(1, "cBlockArea"))
+ {
+ return 0;
+ }
+
+ cBlockArea * self = (cBlockArea *)tolua_tousertype(tolua_S, 1, NULL);
+ if (self == NULL)
+ {
+ tolua_error(tolua_S, "invalid 'self' in function 'cBlockArea:GetSize'", NULL);
+ return 0;
+ }
+
+ // Push the three origin coords:
+ lua_pushnumber(tolua_S, self->GetSizeX());
+ lua_pushnumber(tolua_S, self->GetSizeY());
+ lua_pushnumber(tolua_S, self->GetSizeZ());
+ return 3;
+}
+
+
+
+
+
static int tolua_cBlockArea_LoadFromSchematicFile(lua_State * tolua_S)
{
// function cBlockArea::LoadFromSchematicFile
@@ -2461,7 +2523,9 @@ void ManualBindings::Bind(lua_State * tolua_S)
tolua_beginmodule(tolua_S, "cBlockArea");
tolua_function(tolua_S, "GetBlockTypeMeta", tolua_cBlockArea_GetBlockTypeMeta);
+ tolua_function(tolua_S, "GetOrigin", tolua_cBlockArea_GetOrigin);
tolua_function(tolua_S, "GetRelBlockTypeMeta", tolua_cBlockArea_GetRelBlockTypeMeta);
+ tolua_function(tolua_S, "GetSize", tolua_cBlockArea_GetSize);
tolua_function(tolua_S, "LoadFromSchematicFile", tolua_cBlockArea_LoadFromSchematicFile);
tolua_function(tolua_S, "SaveToSchematicFile", tolua_cBlockArea_SaveToSchematicFile);
tolua_endmodule(tolua_S);
@@ -2511,6 +2575,10 @@ void ManualBindings::Bind(lua_State * tolua_S)
tolua_function(tolua_S, "UpdateSign", tolua_cWorld_SetSignLines);
tolua_endmodule(tolua_S);
+ tolua_beginmodule(tolua_S, "cMapManager");
+ tolua_function(tolua_S, "DoWithMap", tolua_DoWithID<cMapManager, cMap, &cMapManager::DoWithMap>);
+ tolua_endmodule(tolua_S);
+
tolua_beginmodule(tolua_S, "cPlugin");
tolua_function(tolua_S, "Call", tolua_cPlugin_Call);
tolua_endmodule(tolua_S);
diff --git a/src/BlockArea.cpp b/src/BlockArea.cpp
index 194e2d68a..df154d3af 100644
--- a/src/BlockArea.cpp
+++ b/src/BlockArea.cpp
@@ -878,7 +878,7 @@ void cBlockArea::RotateCCW(void)
int NewX = z;
for (int y = 0; y < m_SizeY; y++)
{
- int NewIdx = NewX + NewZ * m_SizeX + y * m_SizeX * m_SizeZ;
+ int NewIdx = NewX + NewZ * m_SizeZ + y * m_SizeX * m_SizeZ;
int OldIdx = MakeIndex(x, y, z);
NewTypes[NewIdx] = m_BlockTypes[OldIdx];
NewMetas[NewIdx] = BlockHandler(m_BlockTypes[OldIdx])->MetaRotateCCW(m_BlockMetas[OldIdx]);
@@ -923,7 +923,7 @@ void cBlockArea::RotateCW(void)
int NewX = m_SizeZ - z - 1;
for (int y = 0; y < m_SizeY; y++)
{
- int NewIdx = NewX + NewZ * m_SizeX + y * m_SizeX * m_SizeZ;
+ int NewIdx = NewX + NewZ * m_SizeZ + y * m_SizeX * m_SizeZ;
int OldIdx = MakeIndex(x, y, z);
NewTypes[NewIdx] = m_BlockTypes[OldIdx];
NewMetas[NewIdx] = BlockHandler(m_BlockTypes[OldIdx])->MetaRotateCW(m_BlockMetas[OldIdx]);
@@ -1075,7 +1075,7 @@ void cBlockArea::RotateCCWNoMeta(void)
int NewX = z;
for (int y = 0; y < m_SizeY; y++)
{
- NewTypes[NewX + NewZ * m_SizeX + y * m_SizeX * m_SizeZ] = m_BlockTypes[MakeIndex(x, y, z)];
+ NewTypes[NewX + NewZ * m_SizeZ + y * m_SizeX * m_SizeZ] = m_BlockTypes[MakeIndex(x, y, z)];
} // for y
} // for z
} // for x
@@ -1093,7 +1093,7 @@ void cBlockArea::RotateCCWNoMeta(void)
int NewX = z;
for (int y = 0; y < m_SizeY; y++)
{
- NewMetas[NewX + NewZ * m_SizeX + y * m_SizeX * m_SizeZ] = m_BlockMetas[MakeIndex(x, y, z)];
+ NewMetas[NewX + NewZ * m_SizeZ + y * m_SizeX * m_SizeZ] = m_BlockMetas[MakeIndex(x, y, z)];
} // for y
} // for z
} // for x
@@ -1120,7 +1120,7 @@ void cBlockArea::RotateCWNoMeta(void)
int NewZ = x;
for (int y = 0; y < m_SizeY; y++)
{
- NewTypes[NewX + NewZ * m_SizeX + y * m_SizeX * m_SizeZ] = m_BlockTypes[MakeIndex(x, y, z)];
+ NewTypes[NewX + NewZ * m_SizeZ + y * m_SizeX * m_SizeZ] = m_BlockTypes[MakeIndex(x, y, z)];
} // for y
} // for x
} // for z
@@ -1138,7 +1138,7 @@ void cBlockArea::RotateCWNoMeta(void)
int NewZ = x;
for (int y = 0; y < m_SizeY; y++)
{
- NewMetas[NewX + NewZ * m_SizeX + y * m_SizeX * m_SizeZ] = m_BlockMetas[MakeIndex(x, y, z)];
+ NewMetas[NewX + NewZ * m_SizeZ + y * m_SizeX * m_SizeZ] = m_BlockMetas[MakeIndex(x, y, z)];
} // for y
} // for x
} // for z
diff --git a/src/BlockArea.h b/src/BlockArea.h
index b4a161f32..5ef814d0e 100644
--- a/src/BlockArea.h
+++ b/src/BlockArea.h
@@ -24,7 +24,7 @@ class cBlockArea
public:
- /// What data is to be queried (bit-mask)
+ /** What data is to be queried (bit-mask) */
enum
{
baTypes = 1,
@@ -44,7 +44,7 @@ public:
cBlockArea(void);
~cBlockArea();
- /// Clears the data stored to reclaim memory
+ /** Clears the data stored to reclaim memory */
void Clear(void);
/** Creates a new area of the specified size and contents.
@@ -53,31 +53,31 @@ public:
*/
void Create(int a_SizeX, int a_SizeY, int a_SizeZ, int a_DataTypes = baTypes | baMetas);
- /// Resets the origin. No other changes are made, contents are untouched.
+ /** Resets the origin. No other changes are made, contents are untouched. */
void SetOrigin(int a_OriginX, int a_OriginY, int a_OriginZ);
- /// Reads an area of blocks specified. Returns true if successful. All coords are inclusive.
+ /** Reads an area of blocks specified. Returns true if successful. All coords are inclusive. */
bool Read(cForEachChunkProvider * a_ForEachChunkProvider, int a_MinBlockX, int a_MaxBlockX, int a_MinBlockY, int a_MaxBlockY, int a_MinBlockZ, int a_MaxBlockZ, int a_DataTypes = baTypes | baMetas);
// TODO: Write() is not too good an interface: if it fails, there's no way to repeat only for the parts that didn't write
// A better way may be to return a list of cBlockAreas for each part that didn't succeed writing, so that the caller may try again
- /// Writes the area back into cWorld at the coords specified. Returns true if successful in all chunks, false if only partially / not at all
+ /** Writes the area back into cWorld at the coords specified. Returns true if successful in all chunks, false if only partially / not at all */
bool Write(cForEachChunkProvider * a_ForEachChunkProvider, int a_MinBlockX, int a_MinBlockY, int a_MinBlockZ, int a_DataTypes = baTypes | baMetas);
- /// Copies this object's contents into the specified BlockArea.
+ /** Copies this object's contents into the specified BlockArea. */
void CopyTo(cBlockArea & a_Into) const;
- /// Copies the contents from the specified BlockArea into this object.
+ /** Copies the contents from the specified BlockArea into this object. */
void CopyFrom(const cBlockArea & a_From);
- /// For testing purposes only, dumps the area into a file.
+ /** For testing purposes only, dumps the area into a file. */
void DumpToRawFile(const AString & a_FileName);
- /// Crops the internal contents by the specified amount of blocks from each border.
+ /** Crops the internal contents by the specified amount of blocks from each border. */
void Crop(int a_AddMinX, int a_SubMaxX, int a_AddMinY, int a_SubMaxY, int a_AddMinZ, int a_SubMaxZ);
- /// Expands the internal contents by the specified amount of blocks from each border
+ /** Expands the internal contents by the specified amount of blocks from each border */
void Expand(int a_SubMinX, int a_AddMaxX, int a_SubMinY, int a_AddMaxY, int a_SubMinZ, int a_AddMaxZ);
/** Merges another block area into this one, using the specified block combinating strategy
@@ -117,49 +117,49 @@ public:
*/
void Merge(const cBlockArea & a_Src, int a_RelX, int a_RelY, int a_RelZ, eMergeStrategy a_Strategy);
- /// Fills the entire block area with the specified data
+ /** Fills the entire block area with the specified data */
void Fill(int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta = 0, NIBBLETYPE a_BlockLight = 0, NIBBLETYPE a_BlockSkyLight = 0x0f);
- /// Fills a cuboid inside the block area with the specified data
+ /** Fills a cuboid inside the block area with the specified data */
void FillRelCuboid(int a_MinRelX, int a_MaxRelX, int a_MinRelY, int a_MaxRelY, int a_MinRelZ, int a_MaxRelZ,
int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta = 0,
NIBBLETYPE a_BlockLight = 0, NIBBLETYPE a_BlockSkyLight = 0x0f
);
- /// Draws a line from between two points with the specified data
+ /** Draws a line from between two points with the specified data */
void RelLine(int a_RelX1, int a_RelY1, int a_RelZ1, int a_RelX2, int a_RelY2, int a_RelZ2,
int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta = 0,
NIBBLETYPE a_BlockLight = 0, NIBBLETYPE a_BlockSkyLight = 0x0f
);
- /// Rotates the entire area counter-clockwise around the Y axis
+ /** Rotates the entire area counter-clockwise around the Y axis */
void RotateCCW(void);
- /// Rotates the entire area clockwise around the Y axis
+ /** Rotates the entire area clockwise around the Y axis */
void RotateCW(void);
- /// Mirrors the entire area around the XY plane
+ /** Mirrors the entire area around the XY plane */
void MirrorXY(void);
- /// Mirrors the entire area around the XZ plane
+ /** Mirrors the entire area around the XZ plane */
void MirrorXZ(void);
- /// Mirrors the entire area around the YZ plane
+ /** Mirrors the entire area around the YZ plane */
void MirrorYZ(void);
- /// Rotates the entire area counter-clockwise around the Y axis, doesn't use blockhandlers for block meta
+ /** Rotates the entire area counter-clockwise around the Y axis, doesn't use blockhandlers for block meta */
void RotateCCWNoMeta(void);
- /// Rotates the entire area clockwise around the Y axis, doesn't use blockhandlers for block meta
+ /** Rotates the entire area clockwise around the Y axis, doesn't use blockhandlers for block meta */
void RotateCWNoMeta(void);
- /// Mirrors the entire area around the XY plane, doesn't use blockhandlers for block meta
+ /** Mirrors the entire area around the XY plane, doesn't use blockhandlers for block meta */
void MirrorXYNoMeta(void);
- /// Mirrors the entire area around the XZ plane, doesn't use blockhandlers for block meta
+ /** Mirrors the entire area around the XZ plane, doesn't use blockhandlers for block meta */
void MirrorXZNoMeta(void);
- /// Mirrors the entire area around the YZ plane, doesn't use blockhandlers for block meta
+ /** Mirrors the entire area around the YZ plane, doesn't use blockhandlers for block meta */
void MirrorYZNoMeta(void);
// Setters:
@@ -197,11 +197,14 @@ public:
int GetSizeY(void) const { return m_SizeY; }
int GetSizeZ(void) const { return m_SizeZ; }
+ /** Returns the volume of the area, as number of blocks */
+ int GetVolume(void) const { return m_SizeX * m_SizeY * m_SizeZ; }
+
int GetOriginX(void) const { return m_OriginX; }
int GetOriginY(void) const { return m_OriginY; }
int GetOriginZ(void) const { return m_OriginZ; }
- /// Returns the datatypes that are stored in the object (bitmask of baXXX values)
+ /** Returns the datatypes that are stored in the object (bitmask of baXXX values) */
int GetDataTypes(void) const;
bool HasBlockTypes (void) const { return (m_BlockTypes != NULL); }
@@ -212,7 +215,7 @@ public:
// tolua_end
// Clients can use these for faster access to all blocktypes. Be careful though!
- /// Returns the internal pointer to the block types
+ /** Returns the internal pointer to the block types */
BLOCKTYPE * GetBlockTypes (void) const { return m_BlockTypes; }
NIBBLETYPE * GetBlockMetas (void) const { return m_BlockMetas; } // NOTE: one byte per block!
NIBBLETYPE * GetBlockLight (void) const { return m_BlockLight; } // NOTE: one byte per block!
@@ -263,7 +266,7 @@ protected:
NIBBLETYPE * m_BlockLight; // Each light value is stored as a separate byte for faster access
NIBBLETYPE * m_BlockSkyLight; // Each light value is stored as a separate byte for faster access
- /// Clears the data stored and prepares a fresh new block area with the specified dimensions
+ /** Clears the data stored and prepares a fresh new block area with the specified dimensions */
bool SetSize(int a_SizeX, int a_SizeY, int a_SizeZ, int a_DataTypes);
// Basic Setters:
@@ -282,7 +285,7 @@ protected:
void ExpandBlockTypes(int a_SubMinX, int a_AddMaxX, int a_SubMinY, int a_AddMaxY, int a_SubMinZ, int a_AddMaxZ);
void ExpandNibbles (NIBBLEARRAY & a_Array, int a_SubMinX, int a_AddMaxX, int a_SubMinY, int a_AddMaxY, int a_SubMinZ, int a_AddMaxZ);
- /// Sets the specified datatypes at the specified location.
+ /** Sets the specified datatypes at the specified location. */
void RelSetData(
int a_RelX, int a_RelY, int a_RelZ,
int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta,
diff --git a/src/BlockID.cpp b/src/BlockID.cpp
index c38db0bfe..ff1c54e3f 100644
--- a/src/BlockID.cpp
+++ b/src/BlockID.cpp
@@ -644,9 +644,11 @@ public:
g_BlockPistonBreakable[E_BLOCK_DEAD_BUSH] = true;
g_BlockPistonBreakable[E_BLOCK_FIRE] = true;
g_BlockPistonBreakable[E_BLOCK_FLOWER] = true;
+ g_BlockPistonBreakable[E_BLOCK_HEAVY_WEIGHTED_PRESSURE_PLATE] = true;
g_BlockPistonBreakable[E_BLOCK_INACTIVE_COMPARATOR] = true;
g_BlockPistonBreakable[E_BLOCK_IRON_DOOR] = true;
g_BlockPistonBreakable[E_BLOCK_JACK_O_LANTERN] = true;
+ g_BlockPistonBreakable[E_BLOCK_LIGHT_WEIGHTED_PRESSURE_PLATE] = true;
g_BlockPistonBreakable[E_BLOCK_LADDER] = true;
g_BlockPistonBreakable[E_BLOCK_LAVA] = true;
g_BlockPistonBreakable[E_BLOCK_LEVER] = true;
@@ -727,10 +729,12 @@ public:
g_BlockRequiresSpecialTool[E_BLOCK_END_STONE] = true;
g_BlockRequiresSpecialTool[E_BLOCK_GOLD_BLOCK] = true;
g_BlockRequiresSpecialTool[E_BLOCK_GOLD_ORE] = true;
+ g_BlockRequiresSpecialTool[E_BLOCK_HEAVY_WEIGHTED_PRESSURE_PLATE] = true;
g_BlockRequiresSpecialTool[E_BLOCK_IRON_BLOCK] = true;
g_BlockRequiresSpecialTool[E_BLOCK_IRON_ORE] = true;
g_BlockRequiresSpecialTool[E_BLOCK_LAPIS_BLOCK] = true;
g_BlockRequiresSpecialTool[E_BLOCK_LAPIS_ORE] = true;
+ g_BlockRequiresSpecialTool[E_BLOCK_LIGHT_WEIGHTED_PRESSURE_PLATE] = true;
g_BlockRequiresSpecialTool[E_BLOCK_MOSSY_COBBLESTONE] = true;
g_BlockRequiresSpecialTool[E_BLOCK_NETHERRACK] = true;
g_BlockRequiresSpecialTool[E_BLOCK_NETHER_BRICK] = true;
diff --git a/src/BlockID.h b/src/BlockID.h
index 740c5fc90..3413555f4 100644
--- a/src/BlockID.h
+++ b/src/BlockID.h
@@ -527,10 +527,12 @@ enum
E_META_SANDSTONE_SMOOTH = 2,
// E_BLOCK_SAPLING metas (lowest 3 bits):
- E_META_SAPLING_APPLE = 0,
- E_META_SAPLING_CONIFER = 1,
- E_META_SAPLING_BIRCH = 2,
- E_META_SAPLING_JUNGLE = 3,
+ E_META_SAPLING_APPLE = 0,
+ E_META_SAPLING_CONIFER = 1,
+ E_META_SAPLING_BIRCH = 2,
+ E_META_SAPLING_JUNGLE = 3,
+ E_META_SAPLING_ACACIA = 4,
+ E_META_SAPLING_DARK_OAK = 5,
// E_BLOCK_SILVERFISH_EGG metas:
E_META_SILVERFISH_EGG_STONE = 0,
diff --git a/src/Blocks/BlockCauldron.h b/src/Blocks/BlockCauldron.h
index 09d5c3cbb..2e1032d2b 100644
--- a/src/Blocks/BlockCauldron.h
+++ b/src/Blocks/BlockCauldron.h
@@ -21,24 +21,30 @@ public:
a_Pickups.push_back(cItem(E_ITEM_CAULDRON, 1, 0));
}
- void OnUse(cChunkInterface * a_ChunkInterface, cWorldInterface * a_WorldInterface, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ)
+ 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
{
- char Meta = a_ChunkInterface->GetBlockMeta( a_BlockX, a_BlockY, a_BlockZ );
- switch( a_Player->GetEquippedItem().m_ItemType )
+ char Meta = a_ChunkInterface.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
+ switch (a_Player->GetEquippedItem().m_ItemType)
{
case E_ITEM_WATER_BUCKET:
{
- a_ChunkInterface->SetBlockMeta( a_BlockX, a_BlockY, a_BlockZ, 3 );
- a_Player->GetInventory().RemoveOneEquippedItem();
- cItem NewItem(E_ITEM_BUCKET, 1);
- a_Player->GetInventory().AddItem(NewItem);
+ if (Meta < 3)
+ {
+ a_ChunkInterface.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, 3);
+ if (!a_Player->IsGameModeCreative())
+ {
+ a_Player->GetInventory().RemoveOneEquippedItem();
+ cItem NewItem(E_ITEM_BUCKET, 1);
+ a_Player->GetInventory().AddItem(NewItem);
+ }
+ }
break;
}
case E_ITEM_GLASS_BOTTLE:
{
- if( Meta > 0 )
+ if (Meta > 0)
{
- a_ChunkInterface->SetBlockMeta( a_BlockX, a_BlockY, a_BlockZ, --Meta);
+ a_ChunkInterface.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, --Meta);
a_Player->GetInventory().RemoveOneEquippedItem();
cItem NewItem(E_ITEM_POTIONS, 1, 0);
a_Player->GetInventory().AddItem(NewItem);
diff --git a/src/Blocks/BlockHandler.cpp b/src/Blocks/BlockHandler.cpp
index 09a1244ea..834727c9a 100644
--- a/src/Blocks/BlockHandler.cpp
+++ b/src/Blocks/BlockHandler.cpp
@@ -39,6 +39,7 @@
#include "BlockIce.h"
#include "BlockLadder.h"
#include "BlockLeaves.h"
+#include "BlockNewLeaves.h"
#include "BlockLever.h"
#include "BlockMelon.h"
#include "BlockMushroom.h"
@@ -108,6 +109,7 @@ cBlockHandler * cBlockHandler::CreateBlockHandler(BLOCKTYPE a_BlockType)
switch(a_BlockType)
{
// Block handlers, alphabetically sorted:
+ case E_BLOCK_ACACIA_WOOD_STAIRS: return new cBlockStairsHandler (a_BlockType);
case E_BLOCK_ACTIVATOR_RAIL: return new cBlockRailHandler (a_BlockType);
case E_BLOCK_BED: return new cBlockBedHandler (a_BlockType);
case E_BLOCK_BIRCH_WOOD_STAIRS: return new cBlockStairsHandler (a_BlockType);
@@ -126,6 +128,7 @@ cBlockHandler * cBlockHandler::CreateBlockHandler(BLOCKTYPE a_BlockType)
case E_BLOCK_COBBLESTONE_STAIRS: return new cBlockStairsHandler (a_BlockType);
case E_BLOCK_COBWEB: return new cBlockCobWebHandler (a_BlockType);
case E_BLOCK_CROPS: return new cBlockCropsHandler (a_BlockType);
+ case E_BLOCK_DARK_OAK_WOOD_STAIRS: return new cBlockStairsHandler (a_BlockType);
case E_BLOCK_DEAD_BUSH: return new cBlockDeadBushHandler (a_BlockType);
case E_BLOCK_DETECTOR_RAIL: return new cBlockRailHandler (a_BlockType);
case E_BLOCK_DIAMOND_ORE: return new cBlockOreHandler (a_BlockType);
@@ -169,6 +172,7 @@ cBlockHandler * cBlockHandler::CreateBlockHandler(BLOCKTYPE a_BlockType)
case E_BLOCK_NETHER_BRICK_STAIRS: return new cBlockStairsHandler (a_BlockType);
case E_BLOCK_NETHER_PORTAL: return new cBlockPortalHandler (a_BlockType);
case E_BLOCK_NETHER_WART: return new cBlockNetherWartHandler (a_BlockType);
+ case E_BLOCK_NEW_LEAVES: return new cBlockNewLeavesHandler (a_BlockType);
case E_BLOCK_NEW_LOG: return new cBlockSidewaysHandler (a_BlockType);
case E_BLOCK_NOTE_BLOCK: return new cBlockNoteHandler (a_BlockType);
case E_BLOCK_PISTON: return new cBlockPistonHandler (a_BlockType);
diff --git a/src/Blocks/BlockLeaves.h b/src/Blocks/BlockLeaves.h
index ad6e440e2..7b8f0b378 100644
--- a/src/Blocks/BlockLeaves.h
+++ b/src/Blocks/BlockLeaves.h
@@ -139,6 +139,8 @@ bool HasNearLog(cBlockArea & a_Area, int a_BlockX, int a_BlockY, int a_BlockZ)
{
switch (Types[i])
{
+ case E_BLOCK_NEW_LEAVES:
+ case E_BLOCK_NEW_LOG:
case E_BLOCK_LEAVES:
case E_BLOCK_LOG:
{
diff --git a/src/Blocks/BlockNewLeaves.h b/src/Blocks/BlockNewLeaves.h
new file mode 100644
index 000000000..5a267e8c6
--- /dev/null
+++ b/src/Blocks/BlockNewLeaves.h
@@ -0,0 +1,42 @@
+#pragma once
+#include "BlockHandler.h"
+#include "BlockLeaves.h"
+#include "../World.h"
+
+
+
+
+
+
+class cBlockNewLeavesHandler :
+ public cBlockLeavesHandler
+{
+public:
+ cBlockNewLeavesHandler(BLOCKTYPE a_BlockType)
+ : cBlockLeavesHandler(a_BlockType)
+ {
+ }
+
+
+ virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
+ {
+ MTRand rand;
+
+ // Only the first 2 bits contain the display information, the others are for growing
+ if (rand.randInt(5) == 0)
+ {
+ a_Pickups.push_back(cItem(E_BLOCK_SAPLING, 1, (a_BlockMeta & 3) + 4));
+ }
+ }
+
+
+ void OnDestroyed(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, int a_BlockX, int a_BlockY, int a_BlockZ) override
+ {
+ cBlockHandler::OnDestroyed(a_ChunkInterface, a_WorldInterface, a_BlockX, a_BlockY, a_BlockZ);
+ }
+} ;
+
+
+
+
+
diff --git a/src/Chunk.cpp b/src/Chunk.cpp
index 4f301c209..8dfbbeef5 100644
--- a/src/Chunk.cpp
+++ b/src/Chunk.cpp
@@ -562,13 +562,6 @@ void cChunk::Tick(float a_Dt)
{
BroadcastPendingBlockChanges();
- // Unload the chunk from all clients that have queued unloading:
- for (cClientHandleList::iterator itr = m_UnloadQuery.begin(), end = m_UnloadQuery.end(); itr != end; ++itr)
- {
- (*itr)->SendUnloadChunk(m_PosX, m_PosZ);
- }
- m_UnloadQuery.clear();
-
// Set all blocks that have been queued for setting later:
ProcessQueuedSetBlocks();
diff --git a/src/Chunk.h b/src/Chunk.h
index 1b7a6fa07..c9e9697ca 100644
--- a/src/Chunk.h
+++ b/src/Chunk.h
@@ -405,7 +405,6 @@ private:
// A critical section is not needed, because all chunk access is protected by its parent ChunkMap's csLayers
cClientHandleList m_LoadedByClient;
- cClientHandleList m_UnloadQuery;
cEntityList m_Entities;
cBlockEntityList m_BlockEntities;
diff --git a/src/ChunkDef.h b/src/ChunkDef.h
index f48dc4fd5..7be2fa2df 100644
--- a/src/ChunkDef.h
+++ b/src/ChunkDef.h
@@ -92,6 +92,7 @@ public:
/// Converts absolute block coords into relative (chunk + block) coords:
inline static void AbsoluteToRelative(/* in-out */ int & a_X, int & a_Y, int & a_Z, /* out */ int & a_ChunkX, int & a_ChunkZ )
{
+ UNUSED(a_Y);
BlockToChunk(a_X, a_Z, a_ChunkX, a_ChunkZ);
a_X = a_X - a_ChunkX * Width;
diff --git a/src/ChunkMap.cpp b/src/ChunkMap.cpp
index fbb8706e0..b5795fbaf 100644
--- a/src/ChunkMap.cpp
+++ b/src/ChunkMap.cpp
@@ -805,6 +805,10 @@ void cChunkMap::WakeUpSimulators(int a_BlockX, int a_BlockY, int a_BlockZ)
/// Wakes up the simulators for the specified area of blocks
void cChunkMap::WakeUpSimulatorsInArea(int a_MinBlockX, int a_MaxBlockX, int a_MinBlockY, int a_MaxBlockY, int a_MinBlockZ, int a_MaxBlockZ)
{
+ // Limit the Y coords:
+ a_MinBlockY = std::max(a_MinBlockY, 0);
+ a_MaxBlockY = std::min(a_MaxBlockY, cChunkDef::Height - 1);
+
cSimulatorManager * SimMgr = m_World->GetSimulatorManager();
int MinChunkX, MinChunkZ, MaxChunkX, MaxChunkZ;
cChunkDef::BlockToChunk(a_MinBlockX, a_MinBlockZ, MinChunkX, MinChunkZ);
diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp
index 84286fc41..b08ceb5f6 100644
--- a/src/ClientHandle.cpp
+++ b/src/ClientHandle.cpp
@@ -32,6 +32,7 @@
#include "Protocol/ProtocolRecognizer.h"
#include "CompositeChat.h"
+#include "Items/ItemSword.h"
@@ -542,19 +543,23 @@ void cClientHandle::HandlePlayerPos(double a_PosX, double a_PosY, double a_PosZ,
void cClientHandle::HandlePluginMessage(const AString & a_Channel, const AString & a_Message)
{
- if (a_Channel == "MC|AdvCdm") // Command block, set text, Client -> Server
+ if (a_Channel == "MC|AdvCdm")
{
- const char* Data = a_Message.c_str();
- HandleCommandBlockMessage(Data, a_Message.size());
- return;
+ // Command block, set text, Client -> Server
+ HandleCommandBlockMessage(a_Message.c_str(), a_Message.size());
}
- else if (a_Channel == "MC|Brand") // Client <-> Server branding exchange
+ else if (a_Channel == "MC|Brand")
{
- // We are custom,
- // We are awesome,
- // We are MCServer.
+ // Client <-> Server branding exchange
SendPluginMessage("MC|Brand", "MCServer");
- return;
+ }
+ else if (a_Channel == "REGISTER")
+ {
+ RegisterPluginChannels(BreakApartPluginChannels(a_Message));
+ }
+ else if (a_Channel == "UNREGISTER")
+ {
+ UnregisterPluginChannels(BreakApartPluginChannels(a_Message));
}
cPluginManager::Get()->CallHookPluginMessage(*this, a_Channel, a_Message);
@@ -564,7 +569,61 @@ void cClientHandle::HandlePluginMessage(const AString & a_Channel, const AString
-void cClientHandle::HandleCommandBlockMessage(const char* a_Data, unsigned int a_Length)
+AStringVector cClientHandle::BreakApartPluginChannels(const AString & a_PluginChannels)
+{
+ // Break the string on each NUL character.
+ // Note that StringSplit() doesn't work on this because NUL is a special char - string terminator
+ size_t len = a_PluginChannels.size();
+ size_t first = 0;
+ AStringVector res;
+ for (size_t i = 0; i < len; i++)
+ {
+ if (a_PluginChannels[i] != 0)
+ {
+ continue;
+ }
+ if (i > first)
+ {
+ res.push_back(a_PluginChannels.substr(first, i - first));
+ }
+ first = i + 1;
+ } // for i - a_PluginChannels[]
+ if (first < len)
+ {
+ res.push_back(a_PluginChannels.substr(first, len - first));
+ }
+ return res;
+}
+
+
+
+
+
+void cClientHandle::RegisterPluginChannels(const AStringVector & a_ChannelList)
+{
+ for (AStringVector::const_iterator itr = a_ChannelList.begin(), end = a_ChannelList.end(); itr != end; ++itr)
+ {
+ m_PluginChannels.insert(*itr);
+ } // for itr - a_ChannelList[]
+}
+
+
+
+
+
+void cClientHandle::UnregisterPluginChannels(const AStringVector & a_ChannelList)
+{
+ for (AStringVector::const_iterator itr = a_ChannelList.begin(), end = a_ChannelList.end(); itr != end; ++itr)
+ {
+ m_PluginChannels.erase(*itr);
+ } // for itr - a_ChannelList[]
+}
+
+
+
+
+
+void cClientHandle::HandleCommandBlockMessage(const char * a_Data, unsigned int a_Length)
{
if (a_Length < 14)
{
@@ -736,6 +795,15 @@ void cClientHandle::HandleBlockDigStarted(int a_BlockX, int a_BlockY, int a_Bloc
return;
}
+ if (
+ m_Player->IsGameModeCreative() &&
+ ItemCategory::IsSword(m_Player->GetInventory().GetEquippedItem().m_ItemType)
+ )
+ {
+ // Players can't destroy blocks with a Sword in the hand.
+ return;
+ }
+
if (cRoot::Get()->GetPluginManager()->CallHookPlayerBreakingBlock(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_OldBlock, a_OldMeta))
{
// A plugin doesn't agree with the breaking. Bail out. Send the block back to the client, so that it knows:
@@ -2074,6 +2142,33 @@ void cClientHandle::SendInventorySlot(char a_WindowID, short a_SlotNum, const cI
+void cClientHandle::SendMapColumn(int a_ID, int a_X, int a_Y, const Byte * a_Colors, unsigned int a_Length)
+{
+ m_Protocol->SendMapColumn(a_ID, a_X, a_Y, a_Colors, a_Length);
+}
+
+
+
+
+
+void cClientHandle::SendMapDecorators(int a_ID, const cMapDecoratorList & a_Decorators)
+{
+ m_Protocol->SendMapDecorators(a_ID, a_Decorators);
+}
+
+
+
+
+
+void cClientHandle::SendMapInfo(int a_ID, unsigned int a_Scale)
+{
+ m_Protocol->SendMapInfo(a_ID, a_Scale);
+}
+
+
+
+
+
void cClientHandle::SendParticleEffect(const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmmount)
{
m_Protocol->SendParticleEffect(a_ParticleName, a_SrcX, a_SrcY, a_SrcZ, a_OffsetX, a_OffsetY, a_OffsetZ, a_ParticleData, a_ParticleAmmount);
@@ -2463,6 +2558,15 @@ void cClientHandle::SetViewDistance(int a_ViewDistance)
+bool cClientHandle::HasPluginChannel(const AString & a_PluginChannel)
+{
+ return (m_PluginChannels.find(a_PluginChannel) != m_PluginChannels.end());
+}
+
+
+
+
+
bool cClientHandle::WantsSendChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
{
if (m_State >= csDestroying)
diff --git a/src/ClientHandle.h b/src/ClientHandle.h
index aefca7233..194533402 100644
--- a/src/ClientHandle.h
+++ b/src/ClientHandle.h
@@ -17,6 +17,7 @@
#include "ChunkDef.h"
#include "ByteBuffer.h"
#include "Scoreboard.h"
+#include "Map.h"
@@ -72,10 +73,10 @@ public:
inline bool IsLoggedIn(void) const { return (m_State >= csAuthenticating); }
- /// Called while the client is being ticked from the world via its cPlayer object
+ /** Called while the client is being ticked from the world via its cPlayer object */
void Tick(float a_Dt);
- /// Called while the client is being ticked from the cServer object
+ /** Called while the client is being ticked from the cServer object */
void ServerTick(float a_Dt);
void Destroy(void);
@@ -112,6 +113,9 @@ public:
void SendGameMode (eGameMode a_GameMode);
void SendHealth (void);
void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item);
+ void SendMapColumn (int a_ID, int a_X, int a_Y, const Byte * a_Colors, unsigned int a_Length);
+ void SendMapDecorators (int a_ID, const cMapDecoratorList & a_Decorators);
+ void SendMapInfo (int a_ID, unsigned int a_Scale);
void SendPaintingSpawn (const cPainting & a_Painting);
void SendPickupSpawn (const cPickup & a_Pickup);
void SendEntityAnimation (const cEntity & a_Entity, char a_Animation);
@@ -150,23 +154,28 @@ public:
void SendWindowOpen (const cWindow & a_Window);
void SendWindowProperty (const cWindow & a_Window, int a_Property, int a_Value);
- const AString & GetUsername(void) const; // tolua_export
- void SetUsername( const AString & a_Username ); // tolua_export
+ // tolua_begin
+ const AString & GetUsername(void) const;
+ void SetUsername( const AString & a_Username );
- inline short GetPing(void) const { return m_Ping; } // tolua_export
+ inline short GetPing(void) const { return m_Ping; }
- void SetViewDistance(int a_ViewDistance); // tolua_export
- int GetViewDistance(void) const { return m_ViewDistance; } // tolua_export
+ void SetViewDistance(int a_ViewDistance);
+ int GetViewDistance(void) const { return m_ViewDistance; }
- void SetLocale(AString & a_Locale) { m_Locale = a_Locale; } // tolua_export
- AString GetLocale(void) const { return m_Locale; } // tolua_export
+ void SetLocale(AString & a_Locale) { m_Locale = a_Locale; }
+ AString GetLocale(void) const { return m_Locale; }
- int GetUniqueID() const { return m_UniqueID; } // tolua_export
+ int GetUniqueID(void) const { return m_UniqueID; }
- /// Returns true if the client wants the chunk specified to be sent (in m_ChunksToSend)
+ bool HasPluginChannel(const AString & a_PluginChannel);
+
+ // tolua_end
+
+ /** Returns true if the client wants the chunk specified to be sent (in m_ChunksToSend) */
bool WantsSendChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ);
- /// Adds the chunk specified to the list of chunks wanted for sending (m_ChunksToSend)
+ /** Adds the chunk specified to the list of chunks wanted for sending (m_ChunksToSend) */
void AddWantedChunk(int a_ChunkX, int a_ChunkZ);
// Calls that cProtocol descendants use to report state:
@@ -217,14 +226,17 @@ public:
void SendData(const char * a_Data, int a_Size);
- /// Called when the player moves into a different world; queues sreaming the new chunks
+ /** Called when the player moves into a different world; queues sreaming the new chunks */
void MoveToWorld(cWorld & a_World, bool a_SendRespawnPacket);
- /// Handles the block placing packet when it is a real block placement (not block-using, item-using or eating)
+ /** 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);
private:
+ /** The type used for storing the names of registered plugin channels. */
+ typedef std::set<AString> cChannels;
+
int m_ViewDistance; // Number of chunks the player can see in each direction; 4 is the minimum ( http://wiki.vg/Protocol_FAQ#.E2.80.A6all_connecting_clients_spasm_and_jerk_uncontrollably.21 )
static const int GENERATEDISTANCE = 2; // Server generates this many chunks AHEAD of player sight. 2 is the minimum, since foliage is generated 1 step behind chunk terrain generation
@@ -257,7 +269,7 @@ private:
int m_LastStreamedChunkX;
int m_LastStreamedChunkZ;
- /// Seconds since the last packet data was received (updated in Tick(), reset in DataReceived())
+ /** Seconds since the last packet data was received (updated in Tick(), reset in DataReceived()) */
float m_TimeSinceLastPacket;
short m_Ping;
@@ -279,7 +291,7 @@ private:
int m_LastDigBlockY;
int m_LastDigBlockZ;
- /// Used while csDestroyedWaiting for counting the ticks until the connection is closed
+ /** Used while csDestroyedWaiting for counting the ticks until the connection is closed */
int m_TicksSinceDestruction;
enum eState
@@ -299,10 +311,10 @@ private:
eState m_State;
- /// m_State needs to be locked in the Destroy() function so that the destruction code doesn't run twice on two different threads
+ /** m_State needs to be locked in the Destroy() function so that the destruction code doesn't run twice on two different threads */
cCriticalSection m_CSDestroyingState;
- /// If set to true during csDownloadingWorld, the tick thread calls CheckIfWorldDownloaded()
+ /** If set to true during csDownloadingWorld, the tick thread calls CheckIfWorldDownloaded() */
bool m_ShouldCheckDownloaded;
/** Number of explosions sent this tick */
@@ -311,27 +323,39 @@ private:
static int s_ClientCount;
int m_UniqueID;
- /// Set to true when the chunk where the player is is sent to the client. Used for spawning the player
+ /** Set to true when the chunk where the player is is sent to the client. Used for spawning the player */
bool m_HasSentPlayerChunk;
- /// Client Settings
+ /** Client Settings */
AString m_Locale;
+
+ /** The plugin channels that the client has registered. */
+ cChannels m_PluginChannels;
- /// Returns true if the rate block interactions is within a reasonable limit (bot protection)
+ /** Returns true if the rate block interactions is within a reasonable limit (bot protection) */
bool CheckBlockInteractionsRate(void);
- /// Adds a single chunk to be streamed to the client; used by StreamChunks()
+ /** Adds a single chunk to be streamed to the client; used by StreamChunks() */
void StreamChunk(int a_ChunkX, int a_ChunkZ);
- /// Handles the DIG_STARTED dig packet:
+ /** Handles the DIG_STARTED dig packet: */
void HandleBlockDigStarted (int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, BLOCKTYPE a_OldBlock, NIBBLETYPE a_OldMeta);
- /// Handles the DIG_FINISHED dig packet:
+ /** Handles the DIG_FINISHED dig packet: */
void HandleBlockDigFinished(int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, BLOCKTYPE a_OldBlock, NIBBLETYPE a_OldMeta);
- /// Handles the "MC|AdvCdm" plugin message
- void HandleCommandBlockMessage(const char* a_Data, unsigned int a_Length);
+ /** Converts the protocol-formatted channel list (NUL-separated) into a proper string vector. */
+ AStringVector BreakApartPluginChannels(const AString & a_PluginChannels);
+
+ /** Adds all of the channels to the list of current plugin channels. Handles duplicates gracefully. */
+ void RegisterPluginChannels(const AStringVector & a_ChannelList);
+
+ /** Removes all of the channels from the list of current plugin channels. Ignores channels that are not found. */
+ void UnregisterPluginChannels(const AStringVector & a_ChannelList);
+
+ /** Handles the "MC|AdvCdm" plugin message */
+ void HandleCommandBlockMessage(const char * a_Data, unsigned int a_Length);
// cSocketThreads::cCallback overrides:
virtual void DataReceived (const char * a_Data, int a_Size) override; // Data is received from the client
diff --git a/src/Entities/Boat.cpp b/src/Entities/Boat.cpp
index 67df201ce..94b24c5af 100644
--- a/src/Entities/Boat.cpp
+++ b/src/Entities/Boat.cpp
@@ -89,6 +89,7 @@ void cBoat::Tick(float a_Dt, cChunk & a_Chunk)
{
super::Tick(a_Dt, a_Chunk);
BroadcastMovementUpdate();
+
SetSpeed(GetSpeed() * 0.97); // Slowly decrease the speed
if ((POSY_TOINT < 0) || (POSY_TOINT > cChunkDef::Height))
@@ -98,7 +99,10 @@ void cBoat::Tick(float a_Dt, cChunk & a_Chunk)
if (IsBlockWater(m_World->GetBlock(POSX_TOINT, POSY_TOINT, POSZ_TOINT)))
{
- SetSpeedY(1);
+ if (GetSpeedY() < 2)
+ {
+ AddSpeedY(0.2);
+ }
}
}
@@ -108,12 +112,12 @@ void cBoat::Tick(float a_Dt, cChunk & a_Chunk)
void cBoat::HandleSpeedFromAttachee(float a_Forward, float a_Sideways)
{
- if (GetSpeed().Length() > 7)
+ if (GetSpeed().Length() > 7.5)
{
return;
}
- Vector3d ToAddSpeed(m_Attachee->GetLookVector() * (a_Sideways * 1.5));
+ Vector3d ToAddSpeed = m_Attachee->GetLookVector() * (a_Sideways * 0.4) ;
ToAddSpeed.y = 0;
AddSpeed(ToAddSpeed);
diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp
index 70ddb3c98..0152bfc5b 100644
--- a/src/Entities/Player.cpp
+++ b/src/Entities/Player.cpp
@@ -254,6 +254,9 @@ void cPlayer::Tick(float a_Dt, cChunk & a_Chunk)
HandleFloater();
}
+ // Update items (e.g. Maps)
+ m_Inventory.UpdateItems();
+
// Send Player List (Once per m_LastPlayerListTime/1000 ms)
cTimer t1;
if (m_LastPlayerListTime + cPlayer::PLAYER_LIST_TIME_MS <= t1.GetNowTime())
diff --git a/src/Generating/StructGen.cpp b/src/Generating/StructGen.cpp
index 4efcf92f0..47945cc2b 100644
--- a/src/Generating/StructGen.cpp
+++ b/src/Generating/StructGen.cpp
@@ -51,15 +51,6 @@ const int NEST_SIZE_GRAVEL = 32;
-template <typename T> T Clamp(T a_Value, T a_Min, T a_Max)
-{
- return (a_Value < a_Min) ? a_Min : ((a_Value > a_Max) ? a_Max : a_Value);
-}
-
-
-
-
-
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cStructGenTrees:
diff --git a/src/Generating/Trees.cpp b/src/Generating/Trees.cpp
index 7e8a3c75f..a660285d1 100644
--- a/src/Generating/Trees.cpp
+++ b/src/Generating/Trees.cpp
@@ -382,6 +382,24 @@ void GetBirchTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Nois
+void GetAcaciaTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks)
+{
+ // TODO
+}
+
+
+
+
+
+void GetDarkoakTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks)
+{
+ // TODO
+}
+
+
+
+
+
void GetTallBirchTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks)
{
int Height = 9 + (a_Noise.IntNoise3DInt(a_BlockX + 64 * a_Seq, a_BlockY, a_BlockZ) % 3);
diff --git a/src/Generating/Trees.h b/src/Generating/Trees.h
index 514158eb7..00f343a3d 100644
--- a/src/Generating/Trees.h
+++ b/src/Generating/Trees.h
@@ -63,6 +63,12 @@ void GetLargeAppleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a
/// Generates an image of a random birch tree
void GetBirchTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks);
+/// Generates an image of a random acacia tree
+void GetAcaciaTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks);
+
+/// Generates an image of a random darkoak tree
+void GetDarkoakTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks);
+
/// Generates an image of a random large birch tree
void GetTallBirchTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks,sSetBlockVector & a_OtherBlocks);
diff --git a/src/Globals.h b/src/Globals.h
index 1e90d83e9..e4737a98a 100644
--- a/src/Globals.h
+++ b/src/Globals.h
@@ -235,6 +235,16 @@ public:
+/** Clamp X to the specified range. */
+template <typename T>
+T Clamp(T a_Value, T a_Min, T a_Max)
+{
+ return (a_Value < a_Min) ? a_Min : ((a_Value > a_Max) ? a_Max : a_Value);
+}
+
+
+
+
// Common headers (part 2, with macros):
#include "ChunkDef.h"
diff --git a/src/Inventory.cpp b/src/Inventory.cpp
index 0e1cedc85..7f434adfd 100644
--- a/src/Inventory.cpp
+++ b/src/Inventory.cpp
@@ -515,6 +515,31 @@ bool cInventory::AddToBar( cItem & a_Item, const int a_Offset, const int a_Size,
+void cInventory::UpdateItems(void)
+{
+ const cItem & Slot = GetEquippedItem();
+
+ if (Slot.IsEmpty())
+ {
+ return;
+ }
+
+ switch (Slot.m_ItemType)
+ {
+ case E_ITEM_MAP:
+ {
+ ItemHandler(Slot.m_ItemType)->OnUpdate(m_Owner.GetWorld(), &m_Owner, Slot);
+ break;
+ }
+
+ default: break;
+ }
+}
+
+
+
+
+
void cInventory::SaveToJson(Json::Value & a_Value)
{
// The JSON originally included the 4 crafting slots and the result, so we have to put empty items there, too:
diff --git a/src/Inventory.h b/src/Inventory.h
index 3c6a19de8..fd2089a13 100644
--- a/src/Inventory.h
+++ b/src/Inventory.h
@@ -150,6 +150,9 @@ public:
/// Sends the slot contents to the owner
void SendSlot(int a_SlotNum);
+ /// Update items (e.g. Maps)
+ void UpdateItems(void);
+
/// Converts an armor slot number into the ID for the EntityEquipment packet
static int ArmorSlotNumToEntityEquipmentID(short a_ArmorSlotNum);
diff --git a/src/Items/ItemEmptyMap.h b/src/Items/ItemEmptyMap.h
new file mode 100644
index 000000000..6618bfce2
--- /dev/null
+++ b/src/Items/ItemEmptyMap.h
@@ -0,0 +1,62 @@
+
+// ItemEmptyMap.h
+
+
+
+
+
+#pragma once
+
+#include "../Entities/Entity.h"
+#include "../Item.h"
+
+
+
+
+
+class cItemEmptyMapHandler :
+ public cItemHandler
+{
+ typedef cItemHandler super;
+
+ static const unsigned int DEFAULT_SCALE = 0;
+
+public:
+ cItemEmptyMapHandler() :
+ super(E_ITEM_EMPTY_MAP)
+ {
+ }
+
+ 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) override
+ {
+ UNUSED(a_Item);
+ UNUSED(a_BlockX);
+ UNUSED(a_BlockZ);
+ UNUSED(a_Dir);
+
+ // The map center is fixed at the central point of the 8x8 block of chunks you are standing in when you right-click it.
+
+ const int RegionWidth = cChunkDef::Width * 8 * pow(2.0, (double) DEFAULT_SCALE);
+
+ int CenterX = floor(a_Player->GetPosX() / (float) RegionWidth) * RegionWidth;
+ int CenterZ = floor(a_Player->GetPosZ() / (float) RegionWidth) * RegionWidth;
+
+ cMap * NewMap = a_World->GetMapManager().CreateMap(CenterX, CenterZ, DEFAULT_SCALE);
+
+ // Remove empty map from inventory
+ if (!a_Player->GetInventory().RemoveOneEquippedItem())
+ {
+ ASSERT(!"Inventory mismatch");
+ return true;
+ }
+
+ if (NewMap == NULL)
+ {
+ return true;
+ }
+
+ a_Player->GetInventory().AddItem(cItem(E_ITEM_MAP, 1, NewMap->GetID()), true, true);
+
+ return true;
+ }
+} ;
diff --git a/src/Items/ItemHandler.cpp b/src/Items/ItemHandler.cpp
index e9bb616a6..c10d13edc 100644
--- a/src/Items/ItemHandler.cpp
+++ b/src/Items/ItemHandler.cpp
@@ -18,6 +18,7 @@
#include "ItemComparator.h"
#include "ItemDoor.h"
#include "ItemDye.h"
+#include "ItemEmptyMap.h"
#include "ItemFishingRod.h"
#include "ItemFlowerPot.h"
#include "ItemFood.h"
@@ -25,6 +26,7 @@
#include "ItemHoe.h"
#include "ItemLeaves.h"
#include "ItemLighter.h"
+#include "ItemMap.h"
#include "ItemMinecart.h"
#include "ItemNetherWart.h"
#include "ItemPainting.h"
@@ -103,11 +105,13 @@ cItemHandler *cItemHandler::CreateItemHandler(int 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_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_MAP: return new cItemMapHandler();
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);
diff --git a/src/Items/ItemHandler.h b/src/Items/ItemHandler.h
index 1a6bb044f..ef3f37a7a 100644
--- a/src/Items/ItemHandler.h
+++ b/src/Items/ItemHandler.h
@@ -32,6 +32,14 @@ public:
UNUSED(a_BlockZ);
UNUSED(a_BlockFace);
}
+
+ /// Called every tick while the item is on the player's inventory (Used by maps) - For now, called only for equipped items
+ virtual void OnUpdate(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item)
+ {
+ UNUSED(a_World);
+ UNUSED(a_Player);
+ UNUSED(a_Item);
+ }
/// Called while the player diggs a block using this item
virtual bool OnDiggingBlock(cWorld * a_World, cPlayer * a_Player, const cItem & a_HeldItem, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace);
diff --git a/src/Items/ItemMap.h b/src/Items/ItemMap.h
new file mode 100644
index 000000000..e8ff9da88
--- /dev/null
+++ b/src/Items/ItemMap.h
@@ -0,0 +1,43 @@
+
+// ItemMap.h
+
+
+
+
+
+#pragma once
+
+#include "../Entities/Entity.h"
+#include "../Item.h"
+
+
+
+
+
+class cItemMapHandler :
+ public cItemHandler
+{
+ typedef cItemHandler super;
+
+ static const unsigned int DEFAULT_RADIUS = 128;
+
+public:
+ cItemMapHandler() :
+ super(E_ITEM_MAP)
+ {
+ }
+
+ virtual void OnUpdate(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item)
+ {
+ cMap * Map = a_World->GetMapManager().GetMapData(a_Item.m_ItemDamage);
+
+ if (Map == NULL)
+ {
+ return;
+ }
+
+ Map->UpdateRadius(*a_Player, DEFAULT_RADIUS);
+
+ Map->UpdateClient(a_Player);
+ }
+} ;
diff --git a/src/LightingThread.h b/src/LightingThread.h
index 81dd9d61f..72d561348 100644
--- a/src/LightingThread.h
+++ b/src/LightingThread.h
@@ -82,7 +82,11 @@ protected:
cLightingChunkStay(cLightingThread & a_LightingThread, int a_ChunkX, int a_ChunkZ, cChunkCoordCallback * a_CallbackAfter);
protected:
- virtual void OnChunkAvailable(int a_ChunkX, int a_ChunkZ) override {}
+ virtual void OnChunkAvailable(int a_ChunkX, int a_ChunkZ) override
+ {
+ UNUSED(a_ChunkX);
+ UNUSED(a_ChunkZ);
+ }
virtual bool OnAllChunksAvailable(void) override;
virtual void OnDisabled(void) override;
} ;
diff --git a/src/Map.cpp b/src/Map.cpp
new file mode 100644
index 000000000..337c9cd31
--- /dev/null
+++ b/src/Map.cpp
@@ -0,0 +1,629 @@
+
+// Map.cpp
+
+#include "Globals.h"
+
+#include "Map.h"
+
+#include "ClientHandle.h"
+#include "World.h"
+#include "Chunk.h"
+#include "Entities/Player.h"
+#include "FastRandom.h"
+
+
+
+
+
+cMapDecorator::cMapDecorator(cMap * a_Map, eType a_Type, int a_X, int a_Z, int a_Rot)
+ : m_Map(a_Map)
+ , m_Type(a_Type)
+ , m_PixelX(a_X)
+ , m_PixelZ(a_Z)
+ , m_Rot(a_Rot)
+ , m_Player(NULL)
+{
+}
+
+
+
+
+
+cMapDecorator::cMapDecorator(cMap * a_Map, cPlayer * a_Player)
+ : m_Map(a_Map)
+ , m_Type(E_TYPE_PLAYER)
+ , m_Player(a_Player)
+{
+ Update();
+}
+
+
+
+
+
+void cMapDecorator::Update(void)
+{
+ if (m_Player != NULL)
+ {
+ ASSERT(m_Map != NULL);
+ unsigned int PixelWidth = m_Map->GetPixelWidth();
+
+ int InsideWidth = (m_Map->GetWidth() / 2) - 1;
+ int InsideHeight = (m_Map->GetHeight() / 2) - 1;
+
+ int PixelX = (int) (m_Player->GetPosX() - m_Map->GetCenterX()) / PixelWidth;
+ int PixelZ = (int) (m_Player->GetPosZ() - m_Map->GetCenterZ()) / PixelWidth;
+
+ // Center of pixel
+ m_PixelX = (2 * PixelX) + 1;
+ m_PixelZ = (2 * PixelZ) + 1;
+
+ if ((PixelX > -InsideWidth) && (PixelX <= InsideWidth) && (PixelZ > -InsideHeight) && (PixelZ <= InsideHeight))
+ {
+ double Yaw = m_Player->GetYaw();
+
+ if (m_Map->GetDimension() == dimNether)
+ {
+ cFastRandom Random;
+
+ Int64 WorldAge = m_Player->GetWorld()->GetWorldAge();
+
+ // TODO 2014-02-19 xdot: Refine
+ m_Rot = Random.NextInt(16, (int) WorldAge);
+ }
+ else
+ {
+ m_Rot = (int) (Yaw * 16) / 360;
+ }
+
+ m_Type = E_TYPE_PLAYER;
+ }
+ else
+ {
+ if ((abs(PixelX) > 320.0) || (abs(PixelZ) > 320.0))
+ {
+ // TODO 2014-02-18 xdot: Remove decorator
+ }
+
+ m_Rot = 0;
+
+ m_Type = E_TYPE_PLAYER_OUTSIDE;
+
+ // Move to border
+ if (PixelX <= -InsideWidth)
+ {
+ m_PixelX = (2 * -InsideWidth) + 1;
+ }
+ if (PixelZ <= -InsideHeight)
+ {
+ m_PixelZ = (2 * -InsideHeight) + 1;
+ }
+ if (PixelX > InsideWidth)
+ {
+ m_PixelX = (2 * InsideWidth) + 1;
+ }
+ if (PixelZ > InsideHeight)
+ {
+ m_PixelZ = (2 * InsideHeight) + 1;
+ }
+ }
+ }
+}
+
+
+
+
+
+cMap::cMap(unsigned int a_ID, cWorld * a_World)
+ : m_ID(a_ID)
+ , m_Width(cChunkDef::Width * 8)
+ , m_Height(cChunkDef::Width * 8)
+ , m_Scale(3)
+ , m_CenterX(0)
+ , m_CenterZ(0)
+ , m_World(a_World)
+{
+ m_Data.assign(m_Width * m_Height, E_BASE_COLOR_TRANSPARENT);
+
+ Printf(m_Name, "map_%i", m_ID);
+}
+
+
+
+
+
+cMap::cMap(unsigned int a_ID, int a_CenterX, int a_CenterZ, cWorld * a_World, unsigned int a_Scale)
+ : m_ID(a_ID)
+ , m_Width(cChunkDef::Width * 8)
+ , m_Height(cChunkDef::Width * 8)
+ , m_Scale(a_Scale)
+ , m_CenterX(a_CenterX)
+ , m_CenterZ(a_CenterZ)
+ , m_World(a_World)
+{
+ m_Data.assign(m_Width * m_Height, E_BASE_COLOR_TRANSPARENT);
+
+ Printf(m_Name, "map_%i", m_ID);
+}
+
+
+
+
+
+void cMap::UpdateRadius(int a_PixelX, int a_PixelZ, unsigned int a_Radius)
+{
+ int PixelRadius = a_Radius / GetPixelWidth();
+
+ unsigned int StartX = Clamp(a_PixelX - PixelRadius, 0, (int)m_Width);
+ unsigned int StartZ = Clamp(a_PixelZ - PixelRadius, 0, (int)m_Height);
+
+ unsigned int EndX = Clamp(a_PixelX + PixelRadius, 0, (int)m_Width);
+ unsigned int EndZ = Clamp(a_PixelZ + PixelRadius, 0, (int)m_Height);
+
+ for (unsigned int X = StartX; X < EndX; ++X)
+ {
+ for (unsigned int Z = StartZ; Z < EndZ; ++Z)
+ {
+ int dX = X - a_PixelX;
+ int dZ = Z - a_PixelZ;
+
+ if ((dX * dX) + (dZ * dZ) < (PixelRadius * PixelRadius))
+ {
+ UpdatePixel(X, Z);
+ }
+ }
+ }
+}
+
+
+
+
+
+void cMap::UpdateRadius(cPlayer & a_Player, unsigned int a_Radius)
+{
+ unsigned int PixelWidth = GetPixelWidth();
+
+ int PixelX = (int) (a_Player.GetPosX() - m_CenterX) / PixelWidth + (m_Width / 2);
+ int PixelZ = (int) (a_Player.GetPosZ() - m_CenterZ) / PixelWidth + (m_Height / 2);
+
+ UpdateRadius(PixelX, PixelZ, a_Radius);
+}
+
+
+
+
+
+bool cMap::UpdatePixel(unsigned int a_X, unsigned int a_Z)
+{
+ unsigned int PixelWidth = GetPixelWidth();
+
+ int BlockX = m_CenterX + ((a_X - (m_Width / 2)) * PixelWidth);
+ int BlockZ = m_CenterZ + ((a_Z - (m_Height / 2)) * PixelWidth);
+
+ int ChunkX, ChunkY, ChunkZ;
+ m_World->BlockToChunk(BlockX, 0, BlockZ, ChunkX, ChunkY, ChunkZ);
+
+ int RelX = BlockX - (ChunkX * cChunkDef::Width);
+ int RelZ = BlockZ - (ChunkZ * cChunkDef::Width);
+
+ class cCalculatePixelCb :
+ public cChunkCallback
+ {
+ cMap * m_Map;
+
+ int m_RelX, m_RelZ;
+
+ ColorID m_PixelData;
+
+ public:
+ cCalculatePixelCb(cMap * a_Map, int a_RelX, int a_RelZ)
+ : m_Map(a_Map), m_RelX(a_RelX), m_RelZ(a_RelZ), m_PixelData(E_BASE_COLOR_TRANSPARENT) {}
+
+ virtual bool Item(cChunk * a_Chunk) override
+ {
+ if (a_Chunk == NULL)
+ {
+ return false;
+ }
+
+ unsigned int PixelWidth = m_Map->GetPixelWidth();
+
+ if (m_Map->GetDimension() == dimNether)
+ {
+ // TODO 2014-02-22 xdot: Nether maps
+
+ return false;
+ }
+
+ typedef std::map<ColorID, unsigned int> ColorCountMap;
+ ColorCountMap ColorCounts;
+
+ // Count surface blocks
+ for (unsigned int X = m_RelX; X < m_RelX + PixelWidth; ++X)
+ {
+ for (unsigned int Z = m_RelZ; Z < m_RelZ + PixelWidth; ++Z)
+ {
+ unsigned int WaterDepth = 0;
+
+ BLOCKTYPE TargetBlock = E_BLOCK_AIR;
+ NIBBLETYPE TargetMeta = 0;
+
+ int Height = a_Chunk->GetHeight(X, Z);
+
+ while (Height > 0)
+ {
+ a_Chunk->GetBlockTypeMeta(X, Height, Z, TargetBlock, TargetMeta);
+
+ // TODO 2014-02-22 xdot: Check if block color is transparent
+ if (TargetBlock == E_BLOCK_AIR)
+ {
+ --Height;
+ continue;
+ }
+ // TODO 2014-02-22 xdot: Check if block is liquid
+ else if (false)
+ {
+ --Height;
+ ++WaterDepth;
+ continue;
+ }
+
+ break;
+ }
+
+ // TODO 2014-02-22 xdot: Query block color
+ ColorID Color = E_BASE_COLOR_BROWN;
+
+ // Debug - Temporary
+ switch (TargetBlock)
+ {
+ case E_BLOCK_GRASS:
+ {
+ Color = E_BASE_COLOR_LIGHT_GREEN; break;
+ }
+ case E_BLOCK_STATIONARY_WATER:
+ case E_BLOCK_WATER:
+ {
+ Color = E_BASE_COLOR_BLUE; break;
+ }
+ }
+
+ ++ColorCounts[Color];
+ }
+ }
+
+ // Find dominant color
+ ColorID PixelColor = E_BASE_COLOR_TRANSPARENT;
+
+ unsigned int MaxCount = 0;
+
+ for (ColorCountMap::iterator it = ColorCounts.begin(); it != ColorCounts.end(); ++it)
+ {
+ if (it->second > MaxCount)
+ {
+ PixelColor = it->first;
+ MaxCount = it->second;
+ }
+ }
+
+ // TODO 2014-02-22 xdot: Adjust brightness
+ unsigned int dColor = 1;
+
+ m_PixelData = PixelColor + dColor;
+
+ return false;
+ }
+
+ ColorID GetPixelData(void) const
+ {
+ return m_PixelData;
+ }
+ } CalculatePixelCb(this, RelX, RelZ);
+
+ ASSERT(m_World != NULL);
+ m_World->DoWithChunk(ChunkX, ChunkZ, CalculatePixelCb);
+
+ SetPixel(a_X, a_Z, CalculatePixelCb.GetPixelData());
+
+ return true;
+}
+
+
+
+
+
+void cMap::UpdateDecorators(void)
+{
+ for (cMapDecoratorList::iterator it = m_Decorators.begin(); it != m_Decorators.end(); ++it)
+ {
+ it->Update();
+ }
+}
+
+
+
+
+
+void cMap::AddPlayer(cPlayer * a_Player, Int64 a_WorldAge)
+{
+ cClientHandle * Handle = a_Player->GetClientHandle();
+ if (Handle == NULL)
+ {
+ return;
+ }
+
+ cMapClient MapClient;
+
+ MapClient.m_LastUpdate = a_WorldAge;
+ MapClient.m_SendInfo = true;
+ MapClient.m_Handle = Handle;
+
+ m_Clients.push_back(MapClient);
+
+ cMapDecorator PlayerDecorator(this, a_Player);
+
+ m_Decorators.push_back(PlayerDecorator);
+}
+
+
+
+
+
+void cMap::RemoveInactiveClients(Int64 a_WorldAge)
+{
+ for (cMapClientList::iterator it = m_Clients.begin(); it != m_Clients.end();)
+ {
+ if (it->m_LastUpdate < a_WorldAge)
+ {
+ // Remove associated decorators
+ for (cMapDecoratorList::iterator it2 = m_Decorators.begin(); it2 != m_Decorators.end();)
+ {
+ if (it2->GetPlayer()->GetClientHandle() == it->m_Handle)
+ {
+ // Erase decorator
+ cMapDecoratorList::iterator temp = it2;
+ ++it2;
+ m_Decorators.erase(temp);
+ }
+ else
+ {
+ ++it2;
+ }
+ }
+
+ // Erase client
+ cMapClientList::iterator temp = it;
+ ++it;
+ m_Clients.erase(temp);
+ }
+ else
+ {
+ ++it;
+ }
+ }
+}
+
+
+
+
+
+void cMap::StreamNext(cMapClient & a_Client)
+{
+ cClientHandle * Handle = a_Client.m_Handle;
+
+ if (a_Client.m_SendInfo)
+ {
+ Handle->SendMapInfo(m_ID, m_Scale);
+
+ a_Client.m_SendInfo = false;
+
+ return;
+ }
+
+ ++a_Client.m_NextDecoratorUpdate;
+
+ if (a_Client.m_NextDecoratorUpdate >= 4)
+ {
+ // TODO 2014-02-19 xdot
+ // This is dangerous as the player object may have been destroyed before the decorator is erased from the list
+ UpdateDecorators();
+
+ Handle->SendMapDecorators(m_ID, m_Decorators);
+
+ a_Client.m_NextDecoratorUpdate = 0;
+ }
+ else
+ {
+ ++a_Client.m_DataUpdate;
+
+ unsigned int Y = (a_Client.m_DataUpdate * 11) % m_Width;
+
+ const Byte * Colors = &m_Data[Y * m_Height];
+
+ Handle->SendMapColumn(m_ID, Y, 0, Colors, m_Height);
+ }
+}
+
+
+
+
+
+void cMap::UpdateClient(cPlayer * a_Player)
+{
+ ASSERT(a_Player != NULL);
+ cClientHandle * Handle = a_Player->GetClientHandle();
+
+ if (Handle == NULL)
+ {
+ return;
+ }
+
+ Int64 WorldAge = a_Player->GetWorld()->GetWorldAge();
+
+ RemoveInactiveClients(WorldAge - 5);
+
+ // Linear search for client state
+ for (cMapClientList::iterator it = m_Clients.begin(); it != m_Clients.end(); ++it)
+ {
+ if (it->m_Handle == Handle)
+ {
+ it->m_LastUpdate = WorldAge;
+
+ StreamNext(*it);
+
+ return;
+ }
+ }
+
+ // New player, construct a new client state
+ AddPlayer(a_Player, WorldAge);
+}
+
+
+
+
+
+void cMap::EraseData(void)
+{
+ m_Data.assign(m_Width * m_Height, 0);
+}
+
+
+
+
+
+eDimension cMap::GetDimension(void) const
+{
+ ASSERT(m_World != NULL);
+ return m_World->GetDimension();
+}
+
+
+
+
+
+
+void cMap::Resize(unsigned int a_Width, unsigned int a_Height)
+{
+ if ((m_Width == a_Width) && (m_Height == a_Height))
+ {
+ return;
+ }
+
+ m_Width = a_Width;
+ m_Height = a_Height;
+
+ m_Data.assign(m_Width * m_Height, 0);
+}
+
+
+
+
+
+void cMap::SetPosition(int a_CenterX, int a_CenterZ)
+{
+ m_CenterX = a_CenterX;
+ m_CenterZ = a_CenterZ;
+}
+
+
+
+
+
+void cMap::SetScale(unsigned int a_Scale)
+{
+ if (m_Scale == a_Scale)
+ {
+ return;
+ }
+
+ m_Scale = a_Scale;
+
+ for (cMapClientList::iterator it = m_Clients.begin(); it != m_Clients.end(); ++it)
+ {
+ it->m_SendInfo = true;
+ }
+}
+
+
+
+
+
+bool cMap::SetPixel(unsigned int a_X, unsigned int a_Z, cMap::ColorID a_Data)
+{
+ if ((a_X < m_Width) && (a_Z < m_Height))
+ {
+ m_Data[a_Z + (a_X * m_Height)] = a_Data;
+
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+
+
+
+
+cMap::ColorID cMap::GetPixel(unsigned int a_X, unsigned int a_Z)
+{
+ if ((a_X < m_Width) && (a_Z < m_Height))
+ {
+ return m_Data[a_Z + (a_X * m_Height)];
+ }
+ else
+ {
+ return E_BASE_COLOR_TRANSPARENT;
+ }
+}
+
+
+
+
+
+void cMap::SendTo(cClientHandle & a_Client)
+{
+ a_Client.SendMapInfo(m_ID, m_Scale);
+
+ for (unsigned int i = 0; i < m_Width; ++i)
+ {
+ const Byte* Colors = &m_Data[i * m_Height];
+
+ a_Client.SendMapColumn(m_ID, i, 0, Colors, m_Height);
+ }
+
+ a_Client.SendMapDecorators(m_ID, m_Decorators);
+}
+
+
+
+
+
+unsigned int cMap::GetNumPixels(void) const
+{
+ return m_Width * m_Height;
+}
+
+
+
+
+
+unsigned int cMap::GetNumDecorators(void) const
+{
+ return m_Decorators.size();
+}
+
+
+
+
+unsigned int cMap::GetPixelWidth(void) const
+{
+ return (int) pow(2.0, (double) m_Scale);
+}
+
+
+
+
+
diff --git a/src/Map.h b/src/Map.h
new file mode 100644
index 000000000..a313d5431
--- /dev/null
+++ b/src/Map.h
@@ -0,0 +1,264 @@
+
+// Map.h
+
+// Implementation of in-game coloured maps
+
+
+
+
+
+#pragma once
+
+
+
+
+
+#include "BlockID.h"
+
+
+
+
+
+class cClientHandle;
+class cWorld;
+class cPlayer;
+class cMap;
+
+
+
+
+
+/** Encapsulates a map decorator.
+ *
+ * A map decorator represents an object drawn on the map that can move freely.
+ * (e.g. player trackers and item frame pointers)
+ *
+ * Excluding manually placed decorators,
+ * decorators are automatically managed (allocated and freed) by their parent cMap instance.
+ */
+class cMapDecorator
+{
+public:
+
+ enum eType
+ {
+ E_TYPE_PLAYER = 0x00,
+ E_TYPE_ITEM_FRAME = 0x01,
+
+ /** Player outside of the boundaries of the map. */
+ E_TYPE_PLAYER_OUTSIDE = 0x06
+ };
+
+
+public:
+
+ /** Constructs a map decorator fixed at the specified pixel coordinates. (DEBUG) */
+ cMapDecorator(cMap * a_Map, eType a_Type, int a_X, int a_Z, int a_Rot);
+
+ /** Constructs a map decorator that tracks a player. */
+ cMapDecorator(cMap * a_Map, cPlayer * a_Player);
+
+ /** Updates the decorator. */
+ void Update(void);
+
+ unsigned int GetPixelX(void) const { return m_PixelX; }
+ unsigned int GetPixelZ(void) const { return m_PixelZ; }
+
+ int GetRot(void) const { return m_Rot; }
+
+ eType GetType(void) const { return m_Type; }
+
+ cPlayer * GetPlayer(void) { return m_Player; }
+
+
+protected:
+
+ cMap * m_Map;
+
+ eType m_Type;
+
+ unsigned int m_PixelX;
+ unsigned int m_PixelZ;
+
+ unsigned int m_Rot;
+
+ cPlayer * m_Player;
+
+};
+
+typedef std::list<cMapDecorator> cMapDecoratorList;
+
+
+
+
+
+// tolua_begin
+
+/** Encapsulates an in-game world map. */
+class cMap
+{
+public:
+
+ enum eBaseColor
+ {
+ E_BASE_COLOR_TRANSPARENT = 0, /* Air */
+ E_BASE_COLOR_LIGHT_GREEN = 4, /* Grass */
+ E_BASE_COLOR_LIGHT_BROWN = 8, /* Sand */
+ E_BASE_COLOR_GRAY_1 = 12, /* Cloth */
+ E_BASE_COLOR_RED = 16, /* TNT */
+ E_BASE_COLOR_PALE_BLUE = 20, /* Ice */
+ E_BASE_COLOR_GRAY_2 = 24, /* Iron */
+ E_BASE_COLOR_DARK_GREEN = 28, /* Foliage */
+ E_BASE_COLOR_WHITE = 32, /* Snow */
+ E_BASE_COLOR_LIGHT_GRAY = 36, /* Clay */
+ E_BASE_COLOR_BROWN = 40, /* Dirt */
+ E_BASE_COLOR_DARK_GRAY = 44, /* Stone */
+ E_BASE_COLOR_BLUE = 48, /* Water */
+ E_BASE_COLOR_DARK_BROWN = 52 /* Wood */
+ };
+
+ typedef Byte ColorID;
+
+ // tolua_end
+
+ typedef std::vector<ColorID> cColorList;
+
+
+public:
+
+ /** Construct an empty map. */
+ cMap(unsigned int a_ID, cWorld * a_World);
+
+ /** Construct an empty map at the specified coordinates. */
+ cMap(unsigned int a_ID, int a_CenterX, int a_CenterZ, cWorld * a_World, unsigned int a_Scale = 3);
+
+ /** Send this map to the specified client. WARNING: Slow */
+ void SendTo(cClientHandle & a_Client);
+
+ /** Update a circular region with the specified radius and center (in pixels). */
+ void UpdateRadius(int a_PixelX, int a_PixelZ, unsigned int a_Radius);
+
+ /** Update a circular region around the specified player. */
+ void UpdateRadius(cPlayer & a_Player, unsigned int a_Radius);
+
+ /** Send next update packet to the specified player and remove invalid decorators/clients. */
+ void UpdateClient(cPlayer * a_Player);
+
+ // tolua_begin
+
+ /** Erase pixel data */
+ void EraseData(void);
+
+ void Resize(unsigned int a_Width, unsigned int a_Height);
+
+ void SetPosition(int a_CenterX, int a_CenterZ);
+
+ void SetScale(unsigned int a_Scale);
+
+ bool SetPixel(unsigned int a_X, unsigned int a_Z, ColorID a_Data);
+
+ ColorID GetPixel(unsigned int a_X, unsigned int a_Z);
+
+ unsigned int GetWidth (void) const { return m_Width; }
+ unsigned int GetHeight(void) const { return m_Height; }
+
+ unsigned int GetScale(void) const { return m_Scale; }
+
+ int GetCenterX(void) const { return m_CenterX; }
+ int GetCenterZ(void) const { return m_CenterZ; }
+
+ unsigned int GetID(void) const { return m_ID; }
+
+ cWorld * GetWorld(void) { return m_World; }
+
+ AString GetName(void) { return m_Name; }
+
+ eDimension GetDimension(void) const;
+
+ unsigned int GetNumPixels(void) const;
+
+ unsigned int GetPixelWidth(void) const;
+
+ // tolua_end
+
+ unsigned int GetNumDecorators(void) const;
+
+ const cColorList & GetData(void) const { return m_Data; }
+
+ static const char * GetClassStatic(void) // Needed for ManualBindings's DoWith templates
+ {
+ return "cMap";
+ }
+
+
+protected:
+
+ /** Encapsulates the state of a map client.
+ *
+ * In order to enhance performace, maps are streamed column-by-column to each client.
+ * This structure stores the state of the stream.
+ */
+ struct cMapClient
+ {
+ cClientHandle * m_Handle;
+
+ /** Whether the map scale was modified and needs to be resent. */
+ bool m_SendInfo;
+
+ /** Ticks since last decorator update. */
+ unsigned int m_NextDecoratorUpdate;
+
+ /** Number of pixel data updates. */
+ Int64 m_DataUpdate;
+
+ Int64 m_LastUpdate;
+ };
+
+ typedef std::list<cMapClient> cMapClientList;
+
+
+private:
+
+ /** Update the associated decorators. */
+ void UpdateDecorators(void);
+
+ /** Update the specified pixel. */
+ bool UpdatePixel(unsigned int a_X, unsigned int a_Z);
+
+ /** Add a new map client. */
+ void AddPlayer(cPlayer * a_Player, Int64 a_WorldAge);
+
+ /** Remove inactive or invalid clients. */
+ void RemoveInactiveClients(Int64 a_WorldAge);
+
+ /** Send next update packet to the specified client. */
+ void StreamNext(cMapClient & a_Client);
+
+ unsigned int m_ID;
+
+ unsigned int m_Width;
+ unsigned int m_Height;
+
+ /** The zoom level, 2^scale square blocks per pixel */
+ unsigned int m_Scale;
+
+ int m_CenterX;
+ int m_CenterZ;
+
+ /** Column-major array of colours */
+ cColorList m_Data;
+
+ cWorld * m_World;
+
+ cMapDecoratorList m_Decorators;
+
+ cMapClientList m_Clients;
+
+ AString m_Name;
+
+ friend class cMapSerializer;
+
+}; // tolua_export
+
+
+
diff --git a/src/MapManager.cpp b/src/MapManager.cpp
new file mode 100644
index 000000000..9d02eafb4
--- /dev/null
+++ b/src/MapManager.cpp
@@ -0,0 +1,178 @@
+
+// MapManager.cpp
+
+#include "Globals.h"
+
+#include "MapManager.h"
+
+#include "World.h"
+#include "WorldStorage/MapSerializer.h"
+
+
+
+
+
+cMapManager::cMapManager(cWorld * a_World)
+ : m_World(a_World)
+{
+ ASSERT(m_World != NULL);
+}
+
+
+
+
+
+bool cMapManager::DoWithMap(int a_ID, cMapCallback & a_Callback)
+{
+ cCSLock Lock(m_CS);
+ cMap * Map = GetMapData(a_ID);
+
+ if (Map == NULL)
+ {
+ return false;
+ }
+ else
+ {
+ a_Callback.Item(Map);
+ return true;
+ }
+}
+
+
+
+
+
+bool cMapManager::ForEachMap(cMapCallback & a_Callback)
+{
+ cCSLock Lock(m_CS);
+ for (cMapList::iterator itr = m_MapData.begin(); itr != m_MapData.end(); ++itr)
+ {
+ cMap * Map = &(*itr);
+ if (a_Callback.Item(Map))
+ {
+ return false;
+ }
+ } // for itr - m_MapData[]
+ return true;
+}
+
+
+
+
+
+cMap * cMapManager::GetMapData(unsigned int a_ID)
+{
+ if (a_ID < m_MapData.size())
+ {
+ return &m_MapData[a_ID];
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+
+
+
+
+cMap * cMapManager::CreateMap(int a_CenterX, int a_CenterY, int a_Scale)
+{
+ cCSLock Lock(m_CS);
+
+ if (m_MapData.size() >= 65536)
+ {
+ LOGWARN("Could not craft map - Too many maps in use");
+ return NULL;
+ }
+
+ cMap Map(m_MapData.size(), a_CenterX, a_CenterY, m_World, a_Scale);
+
+ m_MapData.push_back(Map);
+
+ return &m_MapData[Map.GetID()];
+}
+
+
+
+
+
+unsigned int cMapManager::GetNumMaps(void) const
+{
+ return m_MapData.size();
+}
+
+
+
+
+
+void cMapManager::LoadMapData(void)
+{
+ cCSLock Lock(m_CS);
+
+ cIDCountSerializer IDSerializer(m_World->GetName());
+
+ if (!IDSerializer.Load())
+ {
+ return;
+ }
+
+ unsigned int MapCount = IDSerializer.GetMapCount();
+
+ m_MapData.clear();
+
+ for (unsigned int i = 0; i < MapCount; ++i)
+ {
+ cMap Map(i, m_World);
+
+ cMapSerializer Serializer(m_World->GetName(), &Map);
+
+ if (!Serializer.Load())
+ {
+ LOGWARN("Could not load map #%i", Map.GetID());
+ }
+
+ m_MapData.push_back(Map);
+ }
+}
+
+
+
+
+
+void cMapManager::SaveMapData(void)
+{
+ cCSLock Lock(m_CS);
+
+ if (m_MapData.empty())
+ {
+ return;
+ }
+
+ cIDCountSerializer IDSerializer(m_World->GetName());
+
+ IDSerializer.SetMapCount(m_MapData.size());
+
+ if (!IDSerializer.Save())
+ {
+ LOGERROR("Could not save idcounts.dat");
+ return;
+ }
+
+ for (cMapList::iterator it = m_MapData.begin(); it != m_MapData.end(); ++it)
+ {
+ cMap & Map = *it;
+
+ cMapSerializer Serializer(m_World->GetName(), &Map);
+
+ if (!Serializer.Save())
+ {
+ LOGWARN("Could not save map #%i", Map.GetID());
+ }
+ }
+}
+
+
+
+
+
diff --git a/src/MapManager.h b/src/MapManager.h
new file mode 100644
index 000000000..80e6d16d1
--- /dev/null
+++ b/src/MapManager.h
@@ -0,0 +1,78 @@
+
+// MapManager.h
+
+
+
+
+
+#pragma once
+
+
+
+
+
+#include "Map.h"
+
+
+
+
+typedef cItemCallback<cMap> cMapCallback;
+
+
+
+
+// tolua_begin
+
+/** Manages the in-game maps of a single world - Thread safe. */
+class cMapManager
+{
+public:
+ // tolua_end
+
+ cMapManager(cWorld * a_World);
+
+ /** Returns the map with the specified ID, NULL if out of range.
+ *
+ * WARNING: The returned map object is not thread safe.
+ */
+ cMap * GetMapData(unsigned int a_ID);
+
+ /** Creates a new map. Returns NULL on error */
+ cMap * CreateMap(int a_CenterX, int a_CenterY, int a_Scale = 3);
+
+ /** Calls the callback for the map with the specified ID.
+ *
+ * Returns true if the map was found and the callback called, false if map not found.
+ * Callback return ignored.
+ */
+ bool DoWithMap(int a_ID, cMapCallback & a_Callback); // Exported in ManualBindings.cpp
+
+ /** Calls the callback for each map.
+ *
+ * Returns true if all maps processed, false if the callback aborted by returning true.
+ */
+ bool ForEachMap(cMapCallback & a_Callback);
+
+ unsigned int GetNumMaps(void) const; // tolua_export
+
+ /** Loads the map data from the disk */
+ void LoadMapData(void);
+
+ /** Saves the map data to the disk */
+ void SaveMapData(void);
+
+
+private:
+
+ typedef std::vector<cMap> cMapList;
+
+ cCriticalSection m_CS;
+
+ cMapList m_MapData;
+
+ cWorld * m_World;
+
+}; // tolua_export
+
+
+
diff --git a/src/Mobs/Wolf.cpp b/src/Mobs/Wolf.cpp
index 2736c3dd1..0d3619166 100644
--- a/src/Mobs/Wolf.cpp
+++ b/src/Mobs/Wolf.cpp
@@ -4,6 +4,7 @@
#include "Wolf.h"
#include "../World.h"
#include "../Entities/Player.h"
+#include "../Items/ItemHandler.h"
@@ -86,23 +87,44 @@ void cWolf::OnRightClicked(cPlayer & a_Player)
}
else if (IsTame())
{
- if (a_Player.GetName() == m_OwnerName) // Is the player the owner of the dog?
+ switch (a_Player.GetEquippedItem().m_ItemType)
{
- if (a_Player.GetEquippedItem().m_ItemType == E_ITEM_DYE)
+ case E_ITEM_RAW_BEEF:
+ case E_ITEM_STEAK:
+ case E_ITEM_RAW_PORKCHOP:
+ case E_ITEM_COOKED_PORKCHOP:
+ case E_ITEM_RAW_CHICKEN:
+ case E_ITEM_COOKED_CHICKEN:
+ case E_ITEM_ROTTEN_FLESH:
{
- SetCollarColor(15 - a_Player.GetEquippedItem().m_ItemDamage);
- if (!a_Player.IsGameModeCreative())
+ if (m_Health < m_MaxHealth)
{
- a_Player.GetInventory().RemoveOneEquippedItem();
+ Heal(ItemHandler(a_Player.GetEquippedItem().m_ItemType)->GetFoodInfo().FoodLevel);
+ if (!a_Player.IsGameModeCreative())
+ {
+ a_Player.GetInventory().RemoveOneEquippedItem();
+ }
}
- }
- else if (IsSitting())
+ break;
+ }
+ case E_ITEM_DYE:
{
- SetIsSitting(false);
+ if (a_Player.GetName() == m_OwnerName) // Is the player the owner of the dog?
+ {
+ SetCollarColor(15 - a_Player.GetEquippedItem().m_ItemDamage);
+ if (!a_Player.IsGameModeCreative())
+ {
+ a_Player.GetInventory().RemoveOneEquippedItem();
+ }
+ }
+ break;
}
- else
+ default:
{
- SetIsSitting(true);
+ if (a_Player.GetName() == m_OwnerName) // Is the player the owner of the dog?
+ {
+ SetIsSitting(!IsSitting());
+ }
}
}
}
@@ -136,6 +158,8 @@ void cWolf::Tick(float a_Dt, cChunk & a_Chunk)
case E_ITEM_RAW_CHICKEN:
case E_ITEM_COOKED_CHICKEN:
case E_ITEM_ROTTEN_FLESH:
+ case E_ITEM_RAW_PORKCHOP:
+ case E_ITEM_COOKED_PORKCHOP:
{
if (!IsBegging())
{
diff --git a/src/OSSupport/Queue.h b/src/OSSupport/Queue.h
index 6c3d58295..beb6a63f1 100644
--- a/src/OSSupport/Queue.h
+++ b/src/OSSupport/Queue.h
@@ -29,7 +29,11 @@ public:
static void Delete(T) {};
/// Called when an Item is inserted with EnqueueItemIfNotPresent and there is another equal value already inserted
- static void Combine(T & a_existing, const T & a_new) {};
+ static void Combine(T & a_existing, const T & a_new)
+ {
+ UNUSED(a_existing);
+ UNUSED(a_new);
+ };
};
diff --git a/src/Protocol/Protocol.h b/src/Protocol/Protocol.h
index 46b627254..b5560f7c1 100644
--- a/src/Protocol/Protocol.h
+++ b/src/Protocol/Protocol.h
@@ -13,6 +13,7 @@
#include "../Defines.h"
#include "../Endianness.h"
#include "../Scoreboard.h"
+#include "../Map.h"
@@ -82,6 +83,9 @@ public:
virtual void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item) = 0;
virtual void SendKeepAlive (int a_PingID) = 0;
virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) = 0;
+ virtual void SendMapColumn (int a_ID, int a_X, int a_Y, const Byte * a_Colors, unsigned int a_Length) = 0;
+ virtual void SendMapDecorators (int a_ID, const cMapDecoratorList & a_Decorators) = 0;
+ virtual void SendMapInfo (int a_ID, unsigned int a_Scale) = 0;
virtual void SendPaintingSpawn (const cPainting & a_Painting) = 0;
virtual void SendPickupSpawn (const cPickup & a_Pickup) = 0;
virtual void SendPlayerAbilities (void) = 0;
diff --git a/src/Protocol/Protocol125.cpp b/src/Protocol/Protocol125.cpp
index 7020699d1..3980350f5 100644
--- a/src/Protocol/Protocol125.cpp
+++ b/src/Protocol/Protocol125.cpp
@@ -97,6 +97,7 @@ enum
PACKET_WINDOW_PROPERTY = 0x69,
PACKET_CREATIVE_INVENTORY_ACTION = 0x6B,
PACKET_UPDATE_SIGN = 0x82,
+ PACKET_ITEM_DATA = 0x83,
PACKET_PLAYER_LIST_ITEM = 0xC9,
PACKET_PLAYER_ABILITIES = 0xca,
PACKET_PLUGIN_MESSAGE = 0xfa,
@@ -614,6 +615,57 @@ void cProtocol125::SendLogin(const cPlayer & a_Player, const cWorld & a_World)
+void cProtocol125::SendMapColumn(int a_ID, int a_X, int a_Y, const Byte * a_Colors, unsigned int a_Length)
+{
+ cCSLock Lock(m_CSPacket);
+
+ WriteByte (PACKET_ITEM_DATA);
+ WriteShort(E_ITEM_MAP);
+ WriteShort(a_ID);
+ WriteShort(3 + a_Length);
+
+ WriteByte(0);
+ WriteByte(a_X);
+ WriteByte(a_Y);
+
+ for (unsigned int i = 0; i < a_Length; ++i)
+ {
+ WriteByte(a_Colors[i]);
+ }
+
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendMapDecorators(int a_ID, const cMapDecoratorList & a_Decorators)
+{
+ cCSLock Lock(m_CSPacket);
+
+ WriteByte (PACKET_ITEM_DATA);
+ WriteShort(E_ITEM_MAP);
+ WriteShort(a_ID);
+ WriteShort(1 + (3 * a_Decorators.size()));
+
+ WriteByte(1);
+
+ for (cMapDecoratorList::const_iterator it = a_Decorators.begin(); it != a_Decorators.end(); ++it)
+ {
+ WriteByte((it->GetType() << 4) | (it->GetRot() & 0xf));
+ WriteByte(it->GetPixelX());
+ WriteByte(it->GetPixelZ());
+ }
+
+ Flush();
+}
+
+
+
+
+
+
void cProtocol125::SendPickupSpawn(const cPickup & a_Pickup)
{
cCSLock Lock(m_CSPacket);
diff --git a/src/Protocol/Protocol125.h b/src/Protocol/Protocol125.h
index 54551ea5f..1d1484a60 100644
--- a/src/Protocol/Protocol125.h
+++ b/src/Protocol/Protocol125.h
@@ -55,6 +55,9 @@ public:
virtual void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item) override;
virtual void SendKeepAlive (int a_PingID) override;
virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) override;
+ virtual void SendMapColumn (int a_ID, int a_X, int a_Y, const Byte * a_Colors, unsigned int a_Length) override;
+ virtual void SendMapDecorators (int a_ID, const cMapDecoratorList & a_Decorators) override;
+ virtual void SendMapInfo (int a_ID, unsigned int a_Scale) override {} // This protocol doesn't support such message
virtual void SendParticleEffect (const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmmount) override;
virtual void SendPaintingSpawn (const cPainting & a_Painting) override {};
virtual void SendPickupSpawn (const cPickup & a_Pickup) override;
diff --git a/src/Protocol/Protocol17x.cpp b/src/Protocol/Protocol17x.cpp
index aaf8830cd..992023464 100644
--- a/src/Protocol/Protocol17x.cpp
+++ b/src/Protocol/Protocol17x.cpp
@@ -587,6 +587,61 @@ void cProtocol172::SendPaintingSpawn(const cPainting & a_Painting)
+void cProtocol172::SendMapColumn(int a_ID, int a_X, int a_Y, const Byte * a_Colors, unsigned int a_Length)
+{
+ cPacketizer Pkt(*this, 0x34);
+ Pkt.WriteVarInt(a_ID);
+ Pkt.WriteShort (3 + a_Length);
+
+ Pkt.WriteByte(0);
+ Pkt.WriteByte(a_X);
+ Pkt.WriteByte(a_Y);
+
+ for (unsigned int i = 0; i < a_Length; ++i)
+ {
+ Pkt.WriteByte(a_Colors[i]);
+ }
+}
+
+
+
+
+
+void cProtocol172::SendMapDecorators(int a_ID, const cMapDecoratorList & a_Decorators)
+{
+ cPacketizer Pkt(*this, 0x34);
+ Pkt.WriteVarInt(a_ID);
+ Pkt.WriteShort (1 + (3 * a_Decorators.size()));
+
+ Pkt.WriteByte(1);
+
+ for (cMapDecoratorList::const_iterator it = a_Decorators.begin(); it != a_Decorators.end(); ++it)
+ {
+ Pkt.WriteByte((it->GetType() << 4) | (it->GetRot() & 0xf));
+ Pkt.WriteByte(it->GetPixelX());
+ Pkt.WriteByte(it->GetPixelZ());
+ }
+}
+
+
+
+
+
+void cProtocol172::SendMapInfo(int a_ID, unsigned int a_Scale)
+{
+ cPacketizer Pkt(*this, 0x34);
+ Pkt.WriteVarInt(a_ID);
+ Pkt.WriteShort (2);
+
+ Pkt.WriteByte(2);
+ Pkt.WriteByte(a_Scale);
+}
+
+
+
+
+
+
void cProtocol172::SendPickupSpawn(const cPickup & a_Pickup)
{
{
diff --git a/src/Protocol/Protocol17x.h b/src/Protocol/Protocol17x.h
index ae3577867..113501568 100644
--- a/src/Protocol/Protocol17x.h
+++ b/src/Protocol/Protocol17x.h
@@ -87,6 +87,9 @@ public:
virtual void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item) override;
virtual void SendKeepAlive (int a_PingID) override;
virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) override;
+ virtual void SendMapColumn (int a_ID, int a_X, int a_Y, const Byte * a_Colors, unsigned int a_Length) override;
+ virtual void SendMapDecorators (int a_ID, const cMapDecoratorList & a_Decorators) override;
+ virtual void SendMapInfo (int a_ID, unsigned int a_Scale) override;
virtual void SendPaintingSpawn (const cPainting & a_Painting) override;
virtual void SendPickupSpawn (const cPickup & a_Pickup) override;
virtual void SendPlayerAbilities (void) override;
diff --git a/src/Protocol/ProtocolRecognizer.cpp b/src/Protocol/ProtocolRecognizer.cpp
index b658dc9db..84b052146 100644
--- a/src/Protocol/ProtocolRecognizer.cpp
+++ b/src/Protocol/ProtocolRecognizer.cpp
@@ -396,6 +396,36 @@ void cProtocolRecognizer::SendLogin(const cPlayer & a_Player, const cWorld & a_W
+void cProtocolRecognizer::SendMapColumn(int a_ID, int a_X, int a_Y, const Byte * a_Colors, unsigned int a_Length)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendMapColumn(a_ID, a_X, a_Y, a_Colors, a_Length);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendMapDecorators(int a_ID, const cMapDecoratorList & a_Decorators)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendMapDecorators(a_ID, a_Decorators);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendMapInfo(int a_ID, unsigned int a_Scale)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendMapInfo(a_ID, a_Scale);
+}
+
+
+
+
+
void cProtocolRecognizer::SendParticleEffect(const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmmount)
{
ASSERT(m_Protocol != NULL);
diff --git a/src/Protocol/ProtocolRecognizer.h b/src/Protocol/ProtocolRecognizer.h
index abbb22827..6aaafedeb 100644
--- a/src/Protocol/ProtocolRecognizer.h
+++ b/src/Protocol/ProtocolRecognizer.h
@@ -90,6 +90,9 @@ public:
virtual void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item) override;
virtual void SendKeepAlive (int a_PingID) override;
virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) override;
+ virtual void SendMapColumn (int a_ID, int a_X, int a_Y, const Byte * a_Colors, unsigned int a_Length) override;
+ virtual void SendMapDecorators (int a_ID, const cMapDecoratorList & a_Decorators) override;
+ virtual void SendMapInfo (int a_ID, unsigned int a_Scale) override;
virtual void SendParticleEffect (const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmmount) override;
virtual void SendPaintingSpawn (const cPainting & a_Painting) override;
virtual void SendPickupSpawn (const cPickup & a_Pickup) override;
diff --git a/src/Server.cpp b/src/Server.cpp
index ab1458da4..c60418b41 100644
--- a/src/Server.cpp
+++ b/src/Server.cpp
@@ -39,7 +39,9 @@ extern "C" {
// For the "dumpmem" server command:
/// Synchronize this with main.cpp - the leak finder needs initialization before it can be used to dump memory
-#define ENABLE_LEAK_FINDER
+// _X 2014_02_20: Disabled for canon repo, it makes the debug version too slow in MSVC2013
+// and we haven't had a memory leak for over a year anyway.
+// #define ENABLE_LEAK_FINDER
#if defined(_MSC_VER) && defined(_DEBUG) && defined(ENABLE_LEAK_FINDER)
#pragma warning(push)
diff --git a/src/Simulator/FireSimulator.cpp b/src/Simulator/FireSimulator.cpp
index d7c5ab3b4..b77fa1658 100644
--- a/src/Simulator/FireSimulator.cpp
+++ b/src/Simulator/FireSimulator.cpp
@@ -169,6 +169,7 @@ bool cFireSimulator::IsFuel(BLOCKTYPE a_BlockType)
case E_BLOCK_FENCE:
case E_BLOCK_TNT:
case E_BLOCK_VINES:
+ case E_BLOCK_HAY_BALE:
{
return true;
}
diff --git a/src/Simulator/IncrementalRedstoneSimulator.cpp b/src/Simulator/IncrementalRedstoneSimulator.cpp
index 60dabaf84..91de9e0cc 100644
--- a/src/Simulator/IncrementalRedstoneSimulator.cpp
+++ b/src/Simulator/IncrementalRedstoneSimulator.cpp
@@ -6,6 +6,7 @@
#include "../BlockEntities/NoteEntity.h"
#include "../BlockEntities/CommandBlockEntity.h"
#include "../Entities/TNTEntity.h"
+#include "../Entities/Pickup.h"
#include "../Blocks/BlockTorch.h"
#include "../Blocks/BlockDoor.h"
#include "../Piston.h"
@@ -87,7 +88,8 @@ void cIncrementalRedstoneSimulator::RedstoneAddBlock(int a_BlockX, int a_BlockY,
((Block == E_BLOCK_LEVER) && !IsLeverOn(Meta)) ||
((Block == E_BLOCK_DETECTOR_RAIL) && (Meta & 0x08) == 0) ||
(((Block == E_BLOCK_STONE_BUTTON) || (Block == E_BLOCK_WOODEN_BUTTON)) && (!IsButtonOn(Meta))) ||
- (((Block == E_BLOCK_STONE_PRESSURE_PLATE) || (Block == E_BLOCK_WOODEN_PRESSURE_PLATE)) && (Meta == 0))
+ (((Block == E_BLOCK_STONE_PRESSURE_PLATE) || (Block == E_BLOCK_WOODEN_PRESSURE_PLATE)) && (Meta == 0)) ||
+ (((Block == E_BLOCK_LIGHT_WEIGHTED_PRESSURE_PLATE) || (Block == E_BLOCK_HEAVY_WEIGHTED_PRESSURE_PLATE)) && (Meta == 0))
)
{
LOGD("cIncrementalRedstoneSimulator: Erased block @ {%i, %i, %i} from powered blocks list due to present/past metadata mismatch", itr->a_BlockPos.x, itr->a_BlockPos.y, itr->a_BlockPos.z);
@@ -313,6 +315,8 @@ void cIncrementalRedstoneSimulator::SimulateChunk(float a_Dt, int a_ChunkX, int
}
case E_BLOCK_WOODEN_PRESSURE_PLATE:
case E_BLOCK_STONE_PRESSURE_PLATE:
+ case E_BLOCK_LIGHT_WEIGHTED_PRESSURE_PLATE:
+ case E_BLOCK_HEAVY_WEIGHTED_PRESSURE_PLATE:
{
HandlePressurePlate(a_X, dataitr->y, a_Z, dataitr->Data);
break;
@@ -333,13 +337,13 @@ void cIncrementalRedstoneSimulator::WakeUp(int a_BlockX, int a_BlockY, int a_Blo
((a_BlockX % cChunkDef::Width) >= 14) ||
((a_BlockZ % cChunkDef::Width) <= 1) ||
((a_BlockZ % cChunkDef::Width) >= 14)
- ) // Are we on a chunk boundary? ± 2 because of LinkedPowered blocks
+ ) // Are we on a chunk boundary? +- 2 because of LinkedPowered blocks
{
// On a chunk boundary, alert all four sides (i.e. at least one neighbouring chunk)
AddBlock(a_BlockX, a_BlockY, a_BlockZ, a_Chunk);
// Pass the original coordinates, because when adding things to our simulator lists, we get the chunk that they are in, and therefore any updates need to preseve their position
- // RedstoneAddBlock to pass both the neighbouring chunk and the chunk which the coordiantes are in and ± 2 in GetNeighbour() to accomodate for LinkedPowered blocks being 2 away from chunk boundaries
+ // RedstoneAddBlock to pass both the neighbouring chunk and the chunk which the coordiantes are in and +- 2 in GetNeighbour() to accomodate for LinkedPowered blocks being 2 away from chunk boundaries
RedstoneAddBlock(a_BlockX, a_BlockY, a_BlockZ, a_Chunk->GetNeighborChunk(a_BlockX - 2, a_BlockZ), a_Chunk);
RedstoneAddBlock(a_BlockX, a_BlockY, a_BlockZ, a_Chunk->GetNeighborChunk(a_BlockX + 2, a_BlockZ), a_Chunk);
RedstoneAddBlock(a_BlockX, a_BlockY, a_BlockZ, a_Chunk->GetNeighborChunk(a_BlockX, a_BlockZ - 2), a_Chunk);
@@ -1039,13 +1043,15 @@ void cIncrementalRedstoneSimulator::HandlePressurePlate(int a_BlockX, int a_Bloc
}
break;
}
+ case E_BLOCK_LIGHT_WEIGHTED_PRESSURE_PLATE:
+ case E_BLOCK_HEAVY_WEIGHTED_PRESSURE_PLATE:
case E_BLOCK_WOODEN_PRESSURE_PLATE:
{
- class cWoodenPressurePlateCallback :
+ class cPressurePlateCallback :
public cEntityCallback
{
public:
- cWoodenPressurePlateCallback(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) :
+ cPressurePlateCallback(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) :
m_Entity(NULL),
m_World(a_World),
m_X(a_BlockX),
@@ -1063,7 +1069,7 @@ void cIncrementalRedstoneSimulator::HandlePressurePlate(int a_BlockX, int a_Bloc
if (Distance <= 0.7)
{
m_Entity = a_Entity;
- return true; // Break out, we only need to know for wooden plates that at least one entity is on top
+ return true; // Break out, we only need to know for plates that at least one entity is on top
}
return false;
}
@@ -1082,16 +1088,25 @@ void cIncrementalRedstoneSimulator::HandlePressurePlate(int a_BlockX, int a_Bloc
int m_Z;
} ;
- cWoodenPressurePlateCallback WoodenPressurePlateCallback(a_BlockX, a_BlockY, a_BlockZ, &m_World);
- m_World.ForEachEntity(WoodenPressurePlateCallback);
+ cPressurePlateCallback PressurePlateCallback(a_BlockX, a_BlockY, a_BlockZ, &m_World);
+ m_World.ForEachEntity(PressurePlateCallback);
- if (WoodenPressurePlateCallback.FoundEntity())
+ NIBBLETYPE Meta = m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
+ if (PressurePlateCallback.FoundEntity())
{
+ if (Meta == 0x0)
+ {
+ m_World.BroadcastSoundEffect("random.click", (int) ((a_BlockX + 0.5) * 8.0), (int) ((a_BlockY + 0.1) * 8.0), (int) ((a_BlockZ + 0.5) * 8.0), 0.3F, 0.5F);
+ }
m_World.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, 0x1);
- SetAllDirsAsPowered(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_WOODEN_PRESSURE_PLATE);
+ SetAllDirsAsPowered(a_BlockX, a_BlockY, a_BlockZ, a_MyType);
}
else
{
+ if (Meta == 0x1)
+ {
+ m_World.BroadcastSoundEffect("random.click", (int) ((a_BlockX + 0.5) * 8.0), (int) ((a_BlockY + 0.1) * 8.0), (int) ((a_BlockZ + 0.5) * 8.0), 0.3F, 0.6F);
+ }
m_World.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, 0x0);
m_World.WakeUpSimulators(a_BlockX, a_BlockY, a_BlockZ);
}
diff --git a/src/Simulator/SandSimulator.cpp b/src/Simulator/SandSimulator.cpp
index 87fb83357..f305ba61a 100644
--- a/src/Simulator/SandSimulator.cpp
+++ b/src/Simulator/SandSimulator.cpp
@@ -158,8 +158,10 @@ bool cSandSimulator::CanContinueFallThrough(BLOCKTYPE a_BlockType)
case E_BLOCK_DETECTOR_RAIL:
case E_BLOCK_FIRE:
case E_BLOCK_FLOWER_POT:
+ case E_BLOCK_HEAVY_WEIGHTED_PRESSURE_PLATE:
case E_BLOCK_LAVA:
case E_BLOCK_LEVER:
+ case E_BLOCK_LIGHT_WEIGHTED_PRESSURE_PLATE:
case E_BLOCK_MINECART_TRACKS:
case E_BLOCK_MELON_STEM:
case E_BLOCK_POWERED_RAIL:
diff --git a/src/World.cpp b/src/World.cpp
index 7d8bdd95f..ffdae2a37 100644
--- a/src/World.cpp
+++ b/src/World.cpp
@@ -11,6 +11,8 @@
#include "ChunkMap.h"
#include "Generating/ChunkDesc.h"
#include "OSSupport/Timer.h"
+
+// Serializers
#include "WorldStorage/ScoreboardSerializer.h"
// Entities (except mobs):
@@ -251,6 +253,7 @@ cWorld::cWorld(const AString & a_WorldName) :
m_bCommandBlocksEnabled(false),
m_bUseChatPrefixes(true),
m_Scoreboard(this),
+ m_MapManager(this),
m_GeneratorCallbacks(*this),
m_TickThread(*this)
{
@@ -261,6 +264,8 @@ cWorld::cWorld(const AString & a_WorldName) :
// Load the scoreboard
cScoreboardSerializer Serializer(m_WorldName, &m_Scoreboard);
Serializer.Load();
+
+ m_MapManager.LoadMapData();
}
@@ -284,6 +289,8 @@ cWorld::~cWorld()
cScoreboardSerializer Serializer(m_WorldName, &m_Scoreboard);
Serializer.Save();
+ m_MapManager.SaveMapData();
+
delete m_ChunkMap;
}
@@ -1216,10 +1223,12 @@ void cWorld::GrowTreeFromSapling(int a_X, int a_Y, int a_Z, NIBBLETYPE a_Sapling
sSetBlockVector Logs, Other;
switch (a_SaplingMeta & 0x07)
{
- case E_META_SAPLING_APPLE: GetAppleTreeImage (a_X, a_Y, a_Z, Noise, (int)(m_WorldAge & 0xffffffff), Logs, Other); break;
- case E_META_SAPLING_BIRCH: GetBirchTreeImage (a_X, a_Y, a_Z, Noise, (int)(m_WorldAge & 0xffffffff), Logs, Other); break;
- case E_META_SAPLING_CONIFER: GetConiferTreeImage(a_X, a_Y, a_Z, Noise, (int)(m_WorldAge & 0xffffffff), Logs, Other); break;
- case E_META_SAPLING_JUNGLE: GetJungleTreeImage (a_X, a_Y, a_Z, Noise, (int)(m_WorldAge & 0xffffffff), Logs, Other); break;
+ case E_META_SAPLING_APPLE: GetAppleTreeImage (a_X, a_Y, a_Z, Noise, (int)(m_WorldAge & 0xffffffff), Logs, Other); break;
+ case E_META_SAPLING_BIRCH: GetBirchTreeImage (a_X, a_Y, a_Z, Noise, (int)(m_WorldAge & 0xffffffff), Logs, Other); break;
+ case E_META_SAPLING_CONIFER: GetConiferTreeImage(a_X, a_Y, a_Z, Noise, (int)(m_WorldAge & 0xffffffff), Logs, Other); break;
+ case E_META_SAPLING_JUNGLE: GetJungleTreeImage (a_X, a_Y, a_Z, Noise, (int)(m_WorldAge & 0xffffffff), Logs, Other); break;
+ case E_META_SAPLING_ACACIA: GetAcaciaTreeImage (a_X, a_Y, a_Z, Noise, (int)(m_WorldAge & 0xffffffff), Logs, Other); break;
+ case E_META_SAPLING_DARK_OAK: GetDarkoakTreeImage(a_X, a_Y, a_Z, Noise, (int)(m_WorldAge & 0xffffffff), Logs, Other); break;
}
Other.insert(Other.begin(), Logs.begin(), Logs.end());
Logs.clear();
@@ -3023,6 +3032,7 @@ cFluidSimulator * cWorld::InitializeFluidSimulator(cIniFile & a_IniFile, const c
+
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cWorld::cTaskSaveAllChunks:
diff --git a/src/World.h b/src/World.h
index 5c18c5d23..4b74f7aba 100644
--- a/src/World.h
+++ b/src/World.h
@@ -24,6 +24,7 @@
#include "Entities/ProjectileEntity.h"
#include "ForEachChunkProvider.h"
#include "Scoreboard.h"
+#include "MapManager.h"
#include "Blocks/WorldInterface.h"
#include "Blocks/BroadcastInterface.h"
@@ -580,9 +581,12 @@ public:
/** Returns the name of the world.ini file used by this world */
const AString & GetIniFileName(void) const {return m_IniFileName; }
- /** Returns the associated scoreboard instance */
+ /** Returns the associated scoreboard instance. */
cScoreboard & GetScoreBoard(void) { return m_Scoreboard; }
+ /** Returns the associated map manager instance. */
+ cMapManager & GetMapManager(void) { return m_MapManager; }
+
bool AreCommandBlocksEnabled(void) const { return m_bCommandBlocksEnabled; }
void SetCommandBlocksEnabled(bool a_Flag) { m_bCommandBlocksEnabled = a_Flag; }
@@ -848,6 +852,7 @@ private:
cChunkGenerator m_Generator;
cScoreboard m_Scoreboard;
+ cMapManager m_MapManager;
/** The callbacks that the ChunkGenerator uses to store new chunks and interface to plugins */
cChunkGeneratorCallbacks m_GeneratorCallbacks;
diff --git a/src/WorldStorage/MapSerializer.cpp b/src/WorldStorage/MapSerializer.cpp
new file mode 100644
index 000000000..a4a0aab57
--- /dev/null
+++ b/src/WorldStorage/MapSerializer.cpp
@@ -0,0 +1,276 @@
+
+// MapSerializer.cpp
+
+
+#include "Globals.h"
+#include "MapSerializer.h"
+#include "../StringCompression.h"
+#include "zlib/zlib.h"
+#include "FastNBT.h"
+
+#include "../Map.h"
+#include "../World.h"
+
+
+
+
+
+cMapSerializer::cMapSerializer(const AString& a_WorldName, cMap * a_Map)
+ : m_Map(a_Map)
+{
+ AString DataPath;
+ Printf(DataPath, "%s/data", a_WorldName.c_str());
+
+ Printf(m_Path, "%s/map_%i.dat", DataPath.c_str(), a_Map->GetID());
+
+ cFile::CreateFolder(FILE_IO_PREFIX + DataPath);
+}
+
+
+
+
+
+bool cMapSerializer::Load(void)
+{
+ AString Data = cFile::ReadWholeFile(FILE_IO_PREFIX + m_Path);
+ if (Data.empty())
+ {
+ return false;
+ }
+
+ AString Uncompressed;
+ int res = UncompressStringGZIP(Data.data(), Data.size(), Uncompressed);
+
+ if (res != Z_OK)
+ {
+ return false;
+ }
+
+ // Parse the NBT data:
+ cParsedNBT NBT(Uncompressed.data(), Uncompressed.size());
+ if (!NBT.IsValid())
+ {
+ // NBT Parsing failed
+ return false;
+ }
+
+ return LoadMapFromNBT(NBT);
+}
+
+
+
+
+
+bool cMapSerializer::Save(void)
+{
+ cFastNBTWriter Writer;
+
+ SaveMapToNBT(Writer);
+
+ Writer.Finish();
+
+ #ifdef _DEBUG
+ cParsedNBT TestParse(Writer.GetResult().data(), Writer.GetResult().size());
+ ASSERT(TestParse.IsValid());
+ #endif // _DEBUG
+
+ cFile File;
+ if (!File.Open(FILE_IO_PREFIX + m_Path, cFile::fmWrite))
+ {
+ return false;
+ }
+
+ AString Compressed;
+ int res = CompressStringGZIP(Writer.GetResult().data(), Writer.GetResult().size(), Compressed);
+
+ if (res != Z_OK)
+ {
+ return false;
+ }
+
+ File.Write(Compressed.data(), Compressed.size());
+ File.Close();
+
+ return true;
+}
+
+
+
+
+
+void cMapSerializer::SaveMapToNBT(cFastNBTWriter & a_Writer)
+{
+ a_Writer.BeginCompound("data");
+
+ a_Writer.AddByte("scale", m_Map->GetScale());
+ a_Writer.AddByte("dimension", (int) m_Map->GetDimension());
+
+ a_Writer.AddShort("width", m_Map->GetWidth());
+ a_Writer.AddShort("height", m_Map->GetHeight());
+
+ a_Writer.AddInt("xCenter", m_Map->GetCenterX());
+ a_Writer.AddInt("zCenter", m_Map->GetCenterZ());
+
+ const cMap::cColorList & Data = m_Map->GetData();
+ a_Writer.AddByteArray("colors", (char *) &Data[0], Data.size());
+
+ a_Writer.EndCompound();
+}
+
+
+
+
+
+bool cMapSerializer::LoadMapFromNBT(const cParsedNBT & a_NBT)
+{
+ int Data = a_NBT.FindChildByName(0, "data");
+ if (Data < 0)
+ {
+ return false;
+ }
+
+ int CurrLine = a_NBT.FindChildByName(Data, "scale");
+ if (CurrLine >= 0)
+ {
+ unsigned int Scale = a_NBT.GetByte(CurrLine);
+ m_Map->SetScale(Scale);
+ }
+
+ CurrLine = a_NBT.FindChildByName(Data, "dimension");
+ if (CurrLine >= 0)
+ {
+ eDimension Dimension = (eDimension) a_NBT.GetByte(CurrLine);
+
+ ASSERT(Dimension == m_Map->m_World->GetDimension());
+ }
+
+ CurrLine = a_NBT.FindChildByName(Data, "width");
+ if (CurrLine >= 0)
+ {
+ unsigned int Width = a_NBT.GetShort(CurrLine);
+ m_Map->m_Width = Width;
+ }
+
+ CurrLine = a_NBT.FindChildByName(Data, "height");
+ if (CurrLine >= 0)
+ {
+ unsigned int Height = a_NBT.GetShort(CurrLine);
+ m_Map->m_Height = Height;
+ }
+
+ CurrLine = a_NBT.FindChildByName(Data, "xCenter");
+ if (CurrLine >= 0)
+ {
+ int CenterX = a_NBT.GetInt(CurrLine);
+ m_Map->m_CenterX = CenterX;
+ }
+
+ CurrLine = a_NBT.FindChildByName(Data, "zCenter");
+ if (CurrLine >= 0)
+ {
+ int CenterZ = a_NBT.GetInt(CurrLine);
+ m_Map->m_CenterZ = CenterZ;
+ }
+
+ unsigned int NumPixels = m_Map->GetNumPixels();
+ m_Map->m_Data.resize(NumPixels);
+
+ CurrLine = a_NBT.FindChildByName(Data, "colors");
+ if ((CurrLine >= 0) && (a_NBT.GetType(CurrLine) == TAG_ByteArray))
+ {
+ memcpy(&m_Map->m_Data[0], a_NBT.GetData(CurrLine), NumPixels);
+ }
+
+ return true;
+}
+
+
+
+
+
+cIDCountSerializer::cIDCountSerializer(const AString & a_WorldName) : m_MapCount(0)
+{
+ AString DataPath;
+ Printf(DataPath, "%s/data", a_WorldName.c_str());
+
+ Printf(m_Path, "%s/idcounts.dat", DataPath.c_str());
+
+ cFile::CreateFolder(FILE_IO_PREFIX + DataPath);
+}
+
+
+
+
+
+bool cIDCountSerializer::Load(void)
+{
+ AString Data = cFile::ReadWholeFile(FILE_IO_PREFIX + m_Path);
+ if (Data.empty())
+ {
+ return false;
+ }
+
+ // NOTE: idcounts.dat is not compressed (raw format)
+
+ // Parse the NBT data:
+ cParsedNBT NBT(Data.data(), Data.size());
+ if (!NBT.IsValid())
+ {
+ // NBT Parsing failed
+ return false;
+ }
+
+ int CurrLine = NBT.FindChildByName(0, "map");
+ if (CurrLine >= 0)
+ {
+ m_MapCount = (int)NBT.GetShort(CurrLine) + 1;
+ }
+ else
+ {
+ m_MapCount = 0;
+ }
+
+ return true;
+}
+
+
+
+
+
+bool cIDCountSerializer::Save(void)
+{
+ cFastNBTWriter Writer;
+
+ if (m_MapCount > 0)
+ {
+ Writer.AddShort("map", m_MapCount - 1);
+ }
+
+ Writer.Finish();
+
+ #ifdef _DEBUG
+ cParsedNBT TestParse(Writer.GetResult().data(), Writer.GetResult().size());
+ ASSERT(TestParse.IsValid());
+ #endif // _DEBUG
+
+ cFile File;
+ if (!File.Open(FILE_IO_PREFIX + m_Path, cFile::fmWrite))
+ {
+ return false;
+ }
+
+ // NOTE: idcounts.dat is not compressed (raw format)
+
+ File.Write(Writer.GetResult().data(), Writer.GetResult().size());
+ File.Close();
+
+ return true;
+}
+
+
+
+
+
+
+
+
diff --git a/src/WorldStorage/MapSerializer.h b/src/WorldStorage/MapSerializer.h
new file mode 100644
index 000000000..eb7678a08
--- /dev/null
+++ b/src/WorldStorage/MapSerializer.h
@@ -0,0 +1,86 @@
+
+// MapSerializer.h
+
+// Declares the cMapSerializer class that is used for saving maps into NBT format used by Anvil
+
+
+
+
+
+#pragma once
+
+
+
+
+
+// fwd:
+class cFastNBTWriter;
+class cParsedNBT;
+class cMap;
+
+
+
+
+/** Utility class used to serialize maps. */
+class cMapSerializer
+{
+public:
+
+ cMapSerializer(const AString& a_WorldName, cMap * a_Map);
+
+ /** Try to load the scoreboard */
+ bool Load(void);
+
+ /** Try to save the scoreboard */
+ bool Save(void);
+
+
+private:
+
+ void SaveMapToNBT(cFastNBTWriter & a_Writer);
+
+ bool LoadMapFromNBT(const cParsedNBT & a_NBT);
+
+ cMap * m_Map;
+
+ AString m_Path;
+
+
+} ;
+
+
+
+
+/** Utility class used to serialize item ID counts.
+ *
+ * In order to perform bounds checking (while loading),
+ * the last registered ID of each item is serialized to an NBT file.
+ */
+class cIDCountSerializer
+{
+public:
+
+ cIDCountSerializer(const AString & a_WorldName);
+
+ /** Try to load the ID counts */
+ bool Load(void);
+
+ /** Try to save the ID counts */
+ bool Save(void);
+
+ inline unsigned int GetMapCount(void) const { return m_MapCount; }
+
+ inline void SetMapCount(unsigned int a_MapCount) { m_MapCount = a_MapCount; }
+
+
+private:
+
+ AString m_Path;
+
+ unsigned int m_MapCount;
+
+};
+
+
+
+
diff --git a/src/main.cpp b/src/main.cpp
index c8cd2d4fe..4d2801926 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -30,7 +30,9 @@ bool g_ShouldLogCommOut;
/// If defined, a thorough leak finder will be used (debug MSVC only); leaks will be output to the Output window
-#define ENABLE_LEAK_FINDER
+// _X 2014_02_20: Disabled for canon repo, it makes the debug version too slow in MSVC2013
+// and we haven't had a memory leak for over a year anyway.
+// #define ENABLE_LEAK_FINDER