summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--VC2008/MCServer.vcproj8
-rw-r--r--source/Bindings.cpp98
-rw-r--r--source/Bindings.h2
-rw-r--r--source/Blocks/BlockBed.h8
-rw-r--r--source/Blocks/BlockChest.h6
-rw-r--r--source/Blocks/BlockDispenser.h2
-rw-r--r--source/Blocks/BlockFenceGate.h2
-rw-r--r--source/Blocks/BlockFurnace.h3
-rw-r--r--source/Chunk.cpp6
-rw-r--r--source/Defines.h24
-rw-r--r--source/Doors.h84
-rw-r--r--source/Entity.cpp29
-rw-r--r--source/Entity.h50
-rw-r--r--source/Matrix4f.h114
-rw-r--r--source/Minecart.cpp11
-rw-r--r--source/Minecart.h14
-rw-r--r--source/Mobs/Monster.cpp4
-rw-r--r--source/Pickup.cpp1
-rw-r--r--source/Pickup.h6
-rw-r--r--source/Piston.h21
-rw-r--r--source/Player.cpp2
-rw-r--r--source/Protocol/Protocol125.cpp4
-rw-r--r--source/Sign.h56
-rw-r--r--source/Simulator/RedstoneSimulator.cpp8
-rw-r--r--source/Simulator/RedstoneSimulator.h2
-rw-r--r--source/Stairs.h25
-rw-r--r--source/UI/SlotArea.cpp2
-rw-r--r--source/WorldStorage/FastNBT.cpp7
-rw-r--r--source/WorldStorage/FastNBT.h4
-rw-r--r--source/WorldStorage/NBTChunkSerializer.cpp417
-rw-r--r--source/WorldStorage/NBTChunkSerializer.h101
-rw-r--r--source/WorldStorage/WSSAnvil.cpp518
-rw-r--r--source/WorldStorage/WSSAnvil.h17
33 files changed, 1217 insertions, 439 deletions
diff --git a/VC2008/MCServer.vcproj b/VC2008/MCServer.vcproj
index 2a9d6ab25..6f6e8cb6e 100644
--- a/VC2008/MCServer.vcproj
+++ b/VC2008/MCServer.vcproj
@@ -1635,6 +1635,14 @@
>
</File>
<File
+ RelativePath="..\source\WorldStorage\NBTChunkSerializer.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\source\WorldStorage\NBTChunkSerializer.h"
+ >
+ </File>
+ <File
RelativePath="..\source\WorldStorage\WorldStorage.cpp"
>
</File>
diff --git a/source/Bindings.cpp b/source/Bindings.cpp
index a1d79ff04..c22032619 100644
--- a/source/Bindings.cpp
+++ b/source/Bindings.cpp
@@ -1,6 +1,6 @@
/*
** Lua binding: AllToLua
-** Generated automatically by tolua++-1.0.92 on 03/04/13 22:33:20.
+** Generated automatically by tolua++-1.0.92 on 03/09/13 15:33:58.
*/
#ifndef __cplusplus
@@ -257,7 +257,7 @@ static int tolua_AllToLua_cStairs_RotationToMetaData00(lua_State* tolua_S)
else
#endif
{
- float a_Rotation = ((float) tolua_tonumber(tolua_S,2,0));
+ double a_Rotation = ((double) tolua_tonumber(tolua_S,2,0));
{
unsigned char tolua_ret = (unsigned char) cStairs::RotationToMetaData(a_Rotation);
tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
@@ -4365,8 +4365,8 @@ static int tolua_AllToLua_cEntity_GetRot00(lua_State* tolua_S)
if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetRot'", NULL);
#endif
{
- const Vector3f& tolua_ret = (const Vector3f&) self->GetRot();
- tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const Vector3f");
+ const Vector3d& tolua_ret = (const Vector3d&) self->GetRot();
+ tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const Vector3d");
}
}
return 1;
@@ -4397,7 +4397,7 @@ static int tolua_AllToLua_cEntity_GetRotation00(lua_State* tolua_S)
if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetRotation'", NULL);
#endif
{
- float tolua_ret = (float) self->GetRotation();
+ double tolua_ret = (double) self->GetRotation();
tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
}
}
@@ -4429,7 +4429,7 @@ static int tolua_AllToLua_cEntity_GetPitch00(lua_State* tolua_S)
if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetPitch'", NULL);
#endif
{
- float tolua_ret = (float) self->GetPitch();
+ double tolua_ret = (double) self->GetPitch();
tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
}
}
@@ -4461,7 +4461,7 @@ static int tolua_AllToLua_cEntity_GetRoll00(lua_State* tolua_S)
if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetRoll'", NULL);
#endif
{
- float tolua_ret = (float) self->GetRoll();
+ double tolua_ret = (double) self->GetRoll();
tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
}
}
@@ -4493,15 +4493,15 @@ static int tolua_AllToLua_cEntity_GetLookVector00(lua_State* tolua_S)
if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetLookVector'", NULL);
#endif
{
- Vector3f tolua_ret = (Vector3f) self->GetLookVector();
+ Vector3d tolua_ret = (Vector3d) self->GetLookVector();
{
#ifdef __cplusplus
- void* tolua_obj = Mtolua_new((Vector3f)(tolua_ret));
- tolua_pushusertype(tolua_S,tolua_obj,"Vector3f");
+ void* tolua_obj = Mtolua_new((Vector3d)(tolua_ret));
+ tolua_pushusertype(tolua_S,tolua_obj,"Vector3d");
tolua_register_gc(tolua_S,lua_gettop(tolua_S));
#else
- void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3f));
- tolua_pushusertype(tolua_S,tolua_obj,"Vector3f");
+ void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3d));
+ tolua_pushusertype(tolua_S,tolua_obj,"Vector3d");
tolua_register_gc(tolua_S,lua_gettop(tolua_S));
#endif
}
@@ -4953,7 +4953,7 @@ static int tolua_AllToLua_cEntity_SetRotation00(lua_State* tolua_S)
#endif
{
cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0);
- float a_Rotation = ((float) tolua_tonumber(tolua_S,2,0));
+ double a_Rotation = ((double) tolua_tonumber(tolua_S,2,0));
#ifndef TOLUA_RELEASE
if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetRotation'", NULL);
#endif
@@ -4986,7 +4986,7 @@ static int tolua_AllToLua_cEntity_SetPitch00(lua_State* tolua_S)
#endif
{
cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0);
- float a_Pitch = ((float) tolua_tonumber(tolua_S,2,0));
+ double a_Pitch = ((double) tolua_tonumber(tolua_S,2,0));
#ifndef TOLUA_RELEASE
if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetPitch'", NULL);
#endif
@@ -5019,7 +5019,7 @@ static int tolua_AllToLua_cEntity_SetRoll00(lua_State* tolua_S)
#endif
{
cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0);
- float a_Roll = ((float) tolua_tonumber(tolua_S,2,0));
+ double a_Roll = ((double) tolua_tonumber(tolua_S,2,0));
#ifndef TOLUA_RELEASE
if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetRoll'", NULL);
#endif
@@ -5036,6 +5036,71 @@ static int tolua_AllToLua_cEntity_SetRoll00(lua_State* tolua_S)
}
#endif //#ifndef TOLUA_DISABLE
+/* method: SetSpeed of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetSpeed00
+static int tolua_AllToLua_cEntity_SetSpeed00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0);
+ double a_SpeedX = ((double) tolua_tonumber(tolua_S,2,0));
+ double a_SpeedY = ((double) tolua_tonumber(tolua_S,3,0));
+ double a_SpeedZ = ((double) tolua_tonumber(tolua_S,4,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetSpeed'", NULL);
+#endif
+ {
+ self->SetSpeed(a_SpeedX,a_SpeedY,a_SpeedZ);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetSpeed'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetSpeed of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetSpeed01
+static int tolua_AllToLua_cEntity_SetSpeed01(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0);
+ const Vector3d* a_Speed = ((const Vector3d*) tolua_tousertype(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetSpeed'", NULL);
+#endif
+ {
+ self->SetSpeed(*a_Speed);
+ }
+ }
+ return 0;
+tolua_lerror:
+ return tolua_AllToLua_cEntity_SetSpeed00(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
/* method: AddSpeed of class cEntity */
#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_AddSpeed00
static int tolua_AllToLua_cEntity_AddSpeed00(lua_State* tolua_S)
@@ -21731,6 +21796,7 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S)
tolua_constant(tolua_S,"etEntity",cEntity::etEntity);
tolua_constant(tolua_S,"etPlayer",cEntity::etPlayer);
tolua_constant(tolua_S,"etPickup",cEntity::etPickup);
+ tolua_constant(tolua_S,"etMonster",cEntity::etMonster);
tolua_constant(tolua_S,"etMob",cEntity::etMob);
tolua_constant(tolua_S,"etFallingBlock",cEntity::etFallingBlock);
tolua_constant(tolua_S,"etMinecart",cEntity::etMinecart);
@@ -21773,6 +21839,8 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S)
tolua_function(tolua_S,"SetRotation",tolua_AllToLua_cEntity_SetRotation00);
tolua_function(tolua_S,"SetPitch",tolua_AllToLua_cEntity_SetPitch00);
tolua_function(tolua_S,"SetRoll",tolua_AllToLua_cEntity_SetRoll00);
+ tolua_function(tolua_S,"SetSpeed",tolua_AllToLua_cEntity_SetSpeed00);
+ tolua_function(tolua_S,"SetSpeed",tolua_AllToLua_cEntity_SetSpeed01);
tolua_function(tolua_S,"AddSpeed",tolua_AllToLua_cEntity_AddSpeed00);
tolua_function(tolua_S,"GetUniqueID",tolua_AllToLua_cEntity_GetUniqueID00);
tolua_function(tolua_S,"IsDestroyed",tolua_AllToLua_cEntity_IsDestroyed00);
diff --git a/source/Bindings.h b/source/Bindings.h
index 1a56d4e60..a65a70022 100644
--- a/source/Bindings.h
+++ b/source/Bindings.h
@@ -1,6 +1,6 @@
/*
** Lua binding: AllToLua
-** Generated automatically by tolua++-1.0.92 on 03/04/13 22:33:20.
+** Generated automatically by tolua++-1.0.92 on 03/09/13 15:33:59.
*/
/* Exported function */
diff --git a/source/Blocks/BlockBed.h b/source/Blocks/BlockBed.h
index f4e48f195..ab55ffc92 100644
--- a/source/Blocks/BlockBed.h
+++ b/source/Blocks/BlockBed.h
@@ -47,14 +47,14 @@ public:
// Bed specific helper functions
- static NIBBLETYPE RotationToMetaData(float a_Rotation)
+ static NIBBLETYPE RotationToMetaData(double a_Rotation)
{
a_Rotation += 180 + (180/4); // So its not aligned with axis
- if( a_Rotation > 360.f ) a_Rotation -= 360.f;
+ if( a_Rotation > 360 ) a_Rotation -= 360;
- a_Rotation = (a_Rotation/360) * 4;
+ a_Rotation = (a_Rotation / 360) * 4;
- return ((char)a_Rotation+2) % 4;
+ return ((char)a_Rotation + 2) % 4;
}
diff --git a/source/Blocks/BlockChest.h b/source/Blocks/BlockChest.h
index 6de6a8e29..5a64a16bb 100644
--- a/source/Blocks/BlockChest.h
+++ b/source/Blocks/BlockChest.h
@@ -41,7 +41,7 @@ public:
{
return false;
}
- float rot = a_Player->GetRotation();
+ double rot = a_Player->GetRotation();
if (
(Area.GetRelBlockType(0, 0, 1) == E_BLOCK_CHEST) ||
(Area.GetRelBlockType(2, 0, 1) == E_BLOCK_CHEST)
@@ -79,7 +79,7 @@ public:
return;
}
- float rot = a_Player->GetRotation();
+ double rot = a_Player->GetRotation();
// Choose meta from player rotation, choose only between 2 or 3
NIBBLETYPE NewMeta = ((rot >= -90) && (rot < 90)) ? 2 : 3;
if (
@@ -178,7 +178,7 @@ public:
/// Translates player rotation when placing a chest into the chest block metadata. Valid for single chests only
- static NIBBLETYPE RotationToMetaData(float a_Rotation)
+ static NIBBLETYPE RotationToMetaData(double a_Rotation)
{
a_Rotation += 90 + 45; // So its not aligned with axis
diff --git a/source/Blocks/BlockDispenser.h b/source/Blocks/BlockDispenser.h
index a1aa45964..e5cde2fc5 100644
--- a/source/Blocks/BlockDispenser.h
+++ b/source/Blocks/BlockDispenser.h
@@ -28,6 +28,8 @@ public:
) override
{
a_BlockType = m_BlockType;
+
+ // FIXME: Do not use cPiston class for dispenser placement!
a_BlockMeta = cPiston::RotationPitchToMetaData(a_Player->GetRotation(), 0);
return true;
}
diff --git a/source/Blocks/BlockFenceGate.h b/source/Blocks/BlockFenceGate.h
index 0c17a0acc..a84ce8303 100644
--- a/source/Blocks/BlockFenceGate.h
+++ b/source/Blocks/BlockFenceGate.h
@@ -35,7 +35,7 @@ public:
{
NIBBLETYPE OldMetaData = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
NIBBLETYPE NewMetaData = cDoors::RotationToMetaData(a_Player->GetRotation() + 270);
- OldMetaData ^= 4; //Toggle the gate
+ OldMetaData ^= 4; // Toggle the gate
if ((OldMetaData & 1) == (NewMetaData & 1))
{
// Standing in front of the gate - apply new direction
diff --git a/source/Blocks/BlockFurnace.h b/source/Blocks/BlockFurnace.h
index a163c9483..7231c4d65 100644
--- a/source/Blocks/BlockFurnace.h
+++ b/source/Blocks/BlockFurnace.h
@@ -34,7 +34,10 @@ public:
) override
{
a_BlockType = m_BlockType;
+
+ // FIXME: Do not use cPiston class for furnace placement!
a_BlockMeta = cPiston::RotationPitchToMetaData(a_Player->GetRotation(), 0);
+
return true;
}
} ;
diff --git a/source/Chunk.cpp b/source/Chunk.cpp
index f809892f4..e87de292e 100644
--- a/source/Chunk.cpp
+++ b/source/Chunk.cpp
@@ -296,6 +296,12 @@ void cChunk::SetAllData(
CalculateHeightmap();
}
+ // Initialize incoming entities:
+ for (cEntityList::iterator itr = a_Entities.begin(), end = a_Entities.end(); itr != end; ++itr)
+ {
+ (*itr)->Initialize(m_World);
+ } // for itr - a_Entities[]
+
// Append entities to current entity list:
m_Entities.splice(m_Entities.end(), a_Entities);
diff --git a/source/Defines.h b/source/Defines.h
index ce0aa1594..c90416003 100644
--- a/source/Defines.h
+++ b/source/Defines.h
@@ -204,13 +204,13 @@ inline void AddFaceDirection(int & a_BlockX, unsigned char & a_BlockY, int & a_B
#define PI 3.14159265358979323846264338327950288419716939937510582097494459072381640628620899862803482534211706798f
#define MIN(a,b) (((a)>(b))?(b):(a))
#define MAX(a,b) (((a)>(b))?(a):(b))
-inline void EulerToVector( float a_Pan, float a_Pitch, float & a_X, float & a_Y, float & a_Z )
+inline void EulerToVector(double a_Pan, double a_Pitch, double & a_X, double & a_Y, double & a_Z)
{
// a_X = sinf ( a_Pan / 180 * PI ) * cosf ( a_Pitch / 180 * PI );
// a_Y = -sinf ( a_Pitch / 180 * PI );
// a_Z = -cosf ( a_Pan / 180 * PI ) * cosf ( a_Pitch / 180 * PI );
- a_X = cos(a_Pan / 180 * PI)*cos(a_Pitch / 180 * PI);
- a_Y = sin(a_Pan / 180 * PI)*cos(a_Pitch / 180 * PI);
+ a_X = cos(a_Pan / 180 * PI) * cos(a_Pitch / 180 * PI);
+ a_Y = sin(a_Pan / 180 * PI) * cos(a_Pitch / 180 * PI);
a_Z = sin(a_Pitch / 180 * PI);
}
@@ -218,22 +218,26 @@ inline void EulerToVector( float a_Pan, float a_Pitch, float & a_X, float & a_Y,
-inline void VectorToEuler( float a_X, float a_Y, float a_Z, float & a_Pan, float & a_Pitch )
+inline void VectorToEuler(double a_X, double a_Y, double a_Z, double & a_Pan, double & a_Pitch)
{
- if( a_X != 0 )
- a_Pan = atan2( a_Z, a_X ) * 180 / PI - 90;
+ if (a_X != 0)
+ {
+ a_Pan = atan2(a_Z, a_X) * 180 / PI - 90;
+ }
else
+ {
a_Pan = 0;
- a_Pitch = atan2(a_Y, sqrtf((a_X * a_X) + (a_Z * a_Z))) * 180 / PI;
+ }
+ a_Pitch = atan2(a_Y, sqrt((a_X * a_X) + (a_Z * a_Z))) * 180 / PI;
}
-inline float GetSignf( float a_Val )
+inline float GetSignf(float a_Val)
{
- return (a_Val < 0.f)?-1.f:1.f;
+ return (a_Val < 0.f) ? -1.f : 1.f;
}
@@ -242,7 +246,7 @@ inline float GetSignf( float a_Val )
inline float GetSpecialSignf( float a_Val )
{
- return (a_Val <= 0.f)?-1.f:1.f;
+ return (a_Val <= 0.f) ? -1.f : 1.f;
}
diff --git a/source/Doors.h b/source/Doors.h
index 4cf1be3dc..69784a3d7 100644
--- a/source/Doors.h
+++ b/source/Doors.h
@@ -1,61 +1,87 @@
+
#pragma once
-class cDoors // tolua_export
-{ // tolua_export
+
+
+
+// tolua_begin
+class cDoors
+{
public:
- static char RotationToMetaData( float a_Rotation ) // tolua_export
- { // tolua_export
+ static char RotationToMetaData(double a_Rotation)
+ {
a_Rotation += 90 + 45; // So its not aligned with axis
- if( a_Rotation > 360.f ) a_Rotation -= 360.f;
- if( a_Rotation >= 0.f && a_Rotation < 90.f )
+ if (a_Rotation > 360)
+ {
+ a_Rotation -= 360;
+ }
+ if (a_Rotation >= 0.f && a_Rotation < 90)
+ {
return 0x0;
- else if( a_Rotation >= 180 && a_Rotation < 270 )
+ }
+ else if ((a_Rotation >= 180) && (a_Rotation < 270))
+ {
return 0x2;
- else if( a_Rotation >= 90 && a_Rotation < 180 )
+ }
+ else if ((a_Rotation >= 90) && (a_Rotation < 180))
+ {
return 0x1;
+ }
else
+ {
return 0x3;
- } // tolua_export
+ }
+ }
+
- static char ChangeStateMetaData( char a_MetaData ) // tolua_export
- { // tolua_export
+ static NIBBLETYPE ChangeStateMetaData(NIBBLETYPE a_MetaData)
+ {
a_MetaData ^= 4; //XOR bit 2 aka 3. bit (Door open state)
return a_MetaData;
- } // tolua_export
+ }
+
- static void ChangeDoor(cWorld *a_World, int a_X, int a_Y, int a_Z) // tolua_export
- { // tolua_export
- char OldMetaData = a_World->GetBlockMeta(a_X, a_Y, a_Z);
+ static void ChangeDoor(cWorld * a_World, int a_X, int a_Y, int a_Z)
+ {
+ NIBBLETYPE OldMetaData = a_World->GetBlockMeta(a_X, a_Y, a_Z);
- a_World->SetBlockMeta(a_X, a_Y, a_Z, ChangeStateMetaData ( OldMetaData ) );
-
+ a_World->SetBlockMeta(a_X, a_Y, a_Z, ChangeStateMetaData(OldMetaData));
if (OldMetaData & 8)
- { //current block is top of the door
- char BottomBlock = a_World->GetBlock(a_X, a_Y - 1, a_Z);
- char BottomMeta = a_World->GetBlockMeta(a_X, a_Y - 1, a_Z);
+ {
+ // Current block is top of the door
+ BLOCKTYPE BottomBlock = a_World->GetBlock(a_X, a_Y - 1, a_Z);
+ NIBBLETYPE BottomMeta = a_World->GetBlockMeta(a_X, a_Y - 1, a_Z);
if (IsDoor(BottomBlock) && !(BottomMeta & 8))
{
- a_World->SetBlockMeta(a_X, a_Y - 1, a_Z, ChangeStateMetaData ( BottomMeta ) );
+ a_World->SetBlockMeta(a_X, a_Y - 1, a_Z, ChangeStateMetaData(BottomMeta));
}
- } else { //current block is bottom of the door
- char TopBlock = a_World->GetBlock(a_X, a_Y + 1, a_Z);
- char TopMeta = a_World->GetBlockMeta(a_X, a_Y + 1, a_Z);
+ }
+ else
+ {
+ // Current block is bottom of the door
+ BLOCKTYPE TopBlock = a_World->GetBlock(a_X, a_Y + 1, a_Z);
+ NIBBLETYPE TopMeta = a_World->GetBlockMeta(a_X, a_Y + 1, a_Z);
if (IsDoor(TopBlock) && (TopMeta & 8))
{
- a_World->SetBlockMeta(a_X, a_Y + 1, a_Z, ChangeStateMetaData ( TopMeta ) );
+ a_World->SetBlockMeta(a_X, a_Y + 1, a_Z, ChangeStateMetaData(TopMeta));
}
}
- } // tolua_export
+ }
+
- inline static bool IsDoor(char a_Block)
+ inline static bool IsDoor(BLOCKTYPE a_Block)
{
- return (a_Block == E_BLOCK_WOODEN_DOOR || a_Block == E_BLOCK_IRON_DOOR);
+ return (a_Block == E_BLOCK_WOODEN_DOOR) || (a_Block == E_BLOCK_IRON_DOOR);
}
+} ;
+// tolua_end
+
+
+
-}; // tolua_export
diff --git a/source/Entity.cpp b/source/Entity.cpp
index b95648f59..418543fb1 100644
--- a/source/Entity.cpp
+++ b/source/Entity.cpp
@@ -6,7 +6,6 @@
#include "Server.h"
#include "Root.h"
#include "Vector3d.h"
-#include "Vector3f.h"
#include "Matrix4f.h"
#include "ReferenceManager.h"
#include "ClientHandle.h"
@@ -134,8 +133,11 @@ void cEntity::WrapRotation()
void cEntity::MoveToCorrectChunk(bool a_bIgnoreOldChunk)
{
- ASSERT(m_World != NULL); // Entity needs a world to move to a chunk
- if (!m_World) return;
+ if (!m_World)
+ {
+ // This is normal for entities being currently loaded
+ return;
+ }
int ChunkX = 0, ChunkY = 0, ChunkZ = 0;
cWorld::BlockToChunk((int)m_Pos.x, (int)m_Pos.y, (int)m_Pos.z, ChunkX, ChunkY, ChunkZ);
@@ -299,7 +301,7 @@ void cEntity::SetRot(const Vector3f & a_Rot)
-void cEntity::SetRotation(float a_Rotation)
+void cEntity::SetRotation(double a_Rotation)
{
m_Rot.x = a_Rotation;
m_bDirtyOrientation = true;
@@ -309,7 +311,7 @@ void cEntity::SetRotation(float a_Rotation)
-void cEntity::SetPitch(float a_Pitch)
+void cEntity::SetPitch(double a_Pitch)
{
m_Rot.y = a_Pitch;
m_bDirtyOrientation = true;
@@ -319,7 +321,7 @@ void cEntity::SetPitch(float a_Pitch)
-void cEntity::SetRoll(float a_Roll)
+void cEntity::SetRoll(double a_Roll)
{
m_Rot.z = a_Roll;
m_bDirtyOrientation = true;
@@ -329,6 +331,15 @@ void cEntity::SetRoll(float a_Roll)
+void cEntity::SetSpeed(double a_SpeedX, double a_SpeedY, double a_SpeedZ)
+{
+ m_Speed.Set(a_SpeedX, a_SpeedY, a_SpeedZ);
+}
+
+
+
+
+
void cEntity::AddSpeed(const Vector3d & a_AddSpeed)
{
m_Speed += a_AddSpeed;
@@ -340,11 +351,11 @@ void cEntity::AddSpeed(const Vector3d & a_AddSpeed)
//////////////////////////////////////////////////////////////////////////
// Get look vector (this is NOT a rotation!)
-Vector3f cEntity::GetLookVector(void) const
+Vector3d cEntity::GetLookVector(void) const
{
- Matrix4f m;
+ Matrix4d m;
m.Init(Vector3f(), 0, m_Rot.x, -m_Rot.y);
- Vector3f Look = m.Transform(Vector3f(0, 0, 1));
+ Vector3d Look = m.Transform(Vector3d(0, 0, 1));
return Look;
}
diff --git a/source/Entity.h b/source/Entity.h
index e0c0bb191..cdcf57a98 100644
--- a/source/Entity.h
+++ b/source/Entity.h
@@ -63,11 +63,12 @@ public:
etEntity, // For all other types
etPlayer,
etPickup,
- etMob,
+ etMonster,
+ etMob = etMonster, // DEPRECATED, use etMonster instead!
etFallingBlock,
etMinecart,
- // Older constants, left over for compatibility reasons (plugins)
+ // DEPRECATED older constants, left over for compatibility reasons (plugins)
eEntityType_Entity = etEntity,
eEntityType_Player = etPlayer,
eEntityType_Pickup = etPickup,
@@ -108,11 +109,11 @@ public:
double GetPosX (void) const {return m_Pos.x; }
double GetPosY (void) const {return m_Pos.y; }
double GetPosZ (void) const {return m_Pos.z; }
- const Vector3f & GetRot (void) const {return m_Rot; }
- float GetRotation (void) const {return m_Rot.x; }
- float GetPitch (void) const {return m_Rot.y; }
- float GetRoll (void) const {return m_Rot.z; }
- Vector3f GetLookVector(void) const;
+ const Vector3d & GetRot (void) const {return m_Rot; }
+ double GetRotation (void) const {return m_Rot.x; }
+ double GetPitch (void) const {return m_Rot.y; }
+ double GetRoll (void) const {return m_Rot.z; }
+ Vector3d GetLookVector(void) const;
const Vector3d & GetSpeed (void) const { return m_Speed; }
double GetSpeedX (void) const { return m_Speed.x; }
double GetSpeedY (void) const { return m_Speed.y; }
@@ -128,11 +129,14 @@ public:
void SetPosition(double a_PosX, double a_PosY, double a_PosZ);
void SetPosition(const Vector3d & a_Pos);
void SetRot (const Vector3f & a_Rot);
- void SetRotation(float a_Rotation);
- void SetPitch (float a_Pitch);
- void SetRoll (float a_Roll);
+ void SetRotation(double a_Rotation);
+ void SetPitch (double a_Pitch);
+ void SetRoll (double a_Roll);
+ void SetSpeed (double a_SpeedX, double a_SpeedY, double a_SpeedZ);
+ void SetSpeed (const Vector3d & a_Speed) { m_Speed = a_Speed; }
void AddSpeed(const Vector3d & a_AddSpeed);
+
// tolua_end
inline int GetUniqueID(void) const { return m_UniqueID; } // tolua_export
@@ -173,16 +177,6 @@ public:
virtual void OnRightClicked(cPlayer & a_Player) {};
protected:
- virtual void Destroyed(void) {} // Called after the entity has been destroyed
-
- void SetWorld(cWorld * a_World) { m_World = a_World; }
- void MoveToCorrectChunk(bool a_bIgnoreOldChunk = false);
-
- friend class cReferenceManager;
- void AddReference( cEntity*& a_EntityPtr );
- void ReferencedBy( cEntity*& a_EntityPtr );
- void Dereference( cEntity*& a_EntityPtr );
-
static cCriticalSection m_CSCount;
static int m_EntityCount;
@@ -201,7 +195,7 @@ protected:
Vector3d m_Pos;
bool m_bDirtyPosition;
- Vector3f m_Rot;
+ Vector3d m_Rot;
bool m_bDirtyOrientation;
Vector3d m_Speed;
@@ -211,11 +205,21 @@ protected:
eEntityType m_EntityType;
- cWorld* m_World;
+ cWorld * m_World;
float m_FireDamageInterval;
float m_BurnPeriod;
-}; // tolua_export
+
+ virtual void Destroyed(void) {} // Called after the entity has been destroyed
+
+ void SetWorld(cWorld * a_World) { m_World = a_World; }
+ void MoveToCorrectChunk(bool a_bIgnoreOldChunk = false);
+
+ friend class cReferenceManager;
+ void AddReference( cEntity*& a_EntityPtr );
+ void ReferencedBy( cEntity*& a_EntityPtr );
+ void Dereference( cEntity*& a_EntityPtr );
+} ; // tolua_export
typedef std::list<cEntity *> cEntityList;
diff --git a/source/Matrix4f.h b/source/Matrix4f.h
index ab925da03..249c92f5f 100644
--- a/source/Matrix4f.h
+++ b/source/Matrix4f.h
@@ -109,3 +109,117 @@ public:
}
float cell[16];
};
+
+
+
+
+
+class Matrix4d
+{
+public:
+ enum
+ {
+ TX=3,
+ TY=7,
+ TZ=11,
+ D0=0, D1=5, D2=10, D3=15,
+ SX=D0, SY=D1, SZ=D2,
+ W=D3
+ };
+ Matrix4d() { Identity(); }
+ double& operator [] ( int a_N ) { return cell[a_N]; }
+ void Identity()
+ {
+ cell[1] = cell[2] = cell[TX] = cell[4] = cell[6] = cell[TY] =
+ cell[8] = cell[9] = cell[TZ] = cell[12] = cell[13] = cell[14] = 0;
+ cell[D0] = cell[D1] = cell[D2] = cell[W] = 1;
+ }
+ void Init( Vector3f a_Pos, double a_RX, double a_RY, double a_RZ )
+ {
+ Matrix4d t;
+ t.RotateX( a_RZ );
+ RotateY( a_RY );
+ Concatenate( t );
+ t.RotateZ( a_RX );
+ Concatenate( t );
+ Translate( a_Pos );
+ }
+ void RotateX( double a_RX )
+ {
+ double sx = (double)sin( a_RX * M_PI / 180 );
+ double cx = (double)cos( a_RX * M_PI / 180 );
+ Identity();
+ cell[5] = cx, cell[6] = sx, cell[9] = -sx, cell[10] = cx;
+ }
+ void RotateY( double a_RY )
+ {
+ double sy = (double)sin( a_RY * M_PI / 180 );
+ double cy = (double)cos( a_RY * M_PI / 180 );
+ Identity ();
+ cell[0] = cy, cell[2] = -sy, cell[8] = sy, cell[10] = cy;
+ }
+ void RotateZ( double a_RZ )
+ {
+ double sz = (double)sin( a_RZ * M_PI / 180 );
+ double cz = (double)cos( a_RZ * M_PI / 180 );
+ Identity ();
+ cell[0] = cz, cell[1] = sz, cell[4] = -sz, cell[5] = cz;
+ }
+ void Translate( Vector3d a_Pos ) { cell[TX] += a_Pos.x; cell[TY] += a_Pos.y; cell[TZ] += a_Pos.z; }
+ void SetTranslation( Vector3d a_Pos ) { cell[TX] = a_Pos.x; cell[TY] = a_Pos.y; cell[TZ] = a_Pos.z; }
+ void Concatenate( const Matrix4d & m2 )
+ {
+ Matrix4d res;
+ int c;
+ for ( c = 0; c < 4; c++ ) for ( int r = 0; r < 4; r++ )
+ res.cell[r * 4 + c] = cell[r * 4] * m2.cell[c] +
+ cell[r * 4 + 1] * m2.cell[c + 4] +
+ cell[r * 4 + 2] * m2.cell[c + 8] +
+ cell[r * 4 + 3] * m2.cell[c + 12];
+ for ( c = 0; c < 16; c++ ) cell[c] = res.cell[c];
+ }
+ Vector3d Transform( const Vector3d & v ) const
+ {
+ double x = cell[0] * v.x + cell[1] * v.y + cell[2] * v.z + cell[3];
+ double y = cell[4] * v.x + cell[5] * v.y + cell[6] * v.z + cell[7];
+ double z = cell[8] * v.x + cell[9] * v.y + cell[10] * v.z + cell[11];
+ return Vector3d( x, y, z );
+ }
+ void Invert()
+ {
+ Matrix4d t;
+ int h, i;
+ double tx = -cell[3], ty = -cell[7], tz = -cell[11];
+ for ( h = 0; h < 3; h++ ) for ( int v = 0; v < 3; v++ ) t.cell[h + v * 4] = cell[v + h * 4];
+ for ( i = 0; i < 11; i++ ) cell[i] = t.cell[i];
+ cell[3] = tx * cell[0] + ty * cell[1] + tz * cell[2];
+ cell[7] = tx * cell[4] + ty * cell[5] + tz * cell[6];
+ cell[11] = tx * cell[8] + ty * cell[9] + tz * cell[10];
+ }
+ Vector3d GetXColumn() { return Vector3d( cell[0], cell[1], cell[2] ); }
+ Vector3d GetYColumn() { return Vector3d( cell[4], cell[5], cell[6] ); }
+ Vector3d GetZColumn() { return Vector3d( cell[8], cell[9], cell[10] ); }
+ void SetXColumn( const Vector3d & a_X )
+ {
+ cell[0] = a_X.x;
+ cell[1] = a_X.y;
+ cell[2] = a_X.z;
+ }
+ void SetYColumn( const Vector3d & a_Y )
+ {
+ cell[4] = a_Y.x;
+ cell[5] = a_Y.y;
+ cell[6] = a_Y.z;
+ }
+ void SetZColumn( const Vector3d & a_Z )
+ {
+ cell[8] = a_Z.x;
+ cell[9] = a_Z.y;
+ cell[10] = a_Z.z;
+ }
+ double cell[16];
+} ;
+
+
+
+
diff --git a/source/Minecart.cpp b/source/Minecart.cpp
index 84ca57bcd..2606e7774 100644
--- a/source/Minecart.cpp
+++ b/source/Minecart.cpp
@@ -115,6 +115,17 @@ cMinecartWithChest::cMinecartWithChest(double a_X, double a_Y, double a_Z) :
+void cMinecartWithChest::SetItem(int a_Idx, const cItem & a_Item)
+{
+ ASSERT((a_Idx >= 0) && (a_Idx < ARRAYCOUNT(m_Items)));
+
+ m_Items[a_Idx] = a_Item;
+}
+
+
+
+
+
void cMinecartWithChest::OnRightClicked(cPlayer & a_Player)
{
// Show the chest UI window to the player
diff --git a/source/Minecart.h b/source/Minecart.h
index a074e2f97..17c766fb8 100644
--- a/source/Minecart.h
+++ b/source/Minecart.h
@@ -10,6 +10,7 @@
#pragma once
#include "Entity.h"
+#include "Item.h"
@@ -74,8 +75,21 @@ class cMinecartWithChest :
public:
CLASS_PROTODEF(cMinecartWithChest);
+ /// Number of item slots in the chest
+ static const int NumSlots = 9 * 3;
+
cMinecartWithChest(double a_X, double a_Y, double a_Z);
+ const cItem & GetItem(int a_Idx) const { return m_Items[a_Idx]; }
+ cItem & GetItem(int a_Idx) { return m_Items[a_Idx]; }
+
+ void SetItem(int a_Idx, const cItem & a_Item);
+
+protected:
+
+ /// The chest contents:
+ cItem m_Items[NumSlots];
+
// cEntity overrides:
virtual void OnRightClicked(cPlayer & a_Player) override;
} ;
diff --git a/source/Mobs/Monster.cpp b/source/Mobs/Monster.cpp
index 224a16782..a2819a0bd 100644
--- a/source/Mobs/Monster.cpp
+++ b/source/Mobs/Monster.cpp
@@ -145,10 +145,10 @@ void cMonster::Tick(float a_Dt, MTRand & a_TickRandom)
ReplicateMovement();
- Vector3f Distance = m_Destination - Vector3f( m_Pos );
+ Vector3d Distance = m_Destination - Vector3f( m_Pos );
if (Distance.SqrLength() > 0.1f)
{
- float Rotation, Pitch;
+ double Rotation, Pitch;
Distance.Normalize();
VectorToEuler( Distance.x, Distance.y, Distance.z, Rotation, Pitch );
SetRotation( Rotation );
diff --git a/source/Pickup.cpp b/source/Pickup.cpp
index 19a825cc1..d27a2aba7 100644
--- a/source/Pickup.cpp
+++ b/source/Pickup.cpp
@@ -26,6 +26,7 @@
cPickup::cPickup(int a_MicroPosX, int a_MicroPosY, int a_MicroPosZ, const cItem & a_Item, float a_SpeedX /* = 0.f */, float a_SpeedY /* = 0.f */, float a_SpeedZ /* = 0.f */)
: cEntity(etPickup, ((double)(a_MicroPosX)) / 32, ((double)(a_MicroPosY)) / 32, ((double)(a_MicroPosZ)) / 32)
+ , m_Health(5)
, m_bOnGround( false )
, m_bReplicated( false )
, m_Timer( 0.f )
diff --git a/source/Pickup.h b/source/Pickup.h
index d3be60794..b294b4e45 100644
--- a/source/Pickup.h
+++ b/source/Pickup.h
@@ -38,7 +38,13 @@ public:
virtual void Tick(float a_Dt, MTRand & a_TickRandom) override;
virtual void HandlePhysics(float a_Dt) override;
+ short GetHealth(void) const { return m_Health; }
+
+ /// Returns the number of ticks that this entity has existed
+ short GetAge(void) const { return (short)(m_Timer / 50); }
+
private:
+ short m_Health;
Vector3d m_ResultingSpeed; //Can be used to modify the resulting speed for the current tick ;)
diff --git a/source/Piston.h b/source/Piston.h
index ee0d32f5e..67aa80d67 100644
--- a/source/Piston.h
+++ b/source/Piston.h
@@ -5,7 +5,7 @@
-// fwd: "cWorld.h"
+// fwd: World.h
class cWorld;
@@ -16,15 +16,15 @@ class cPiston
{
public:
- cPiston( cWorld* a_World );
+ cPiston(cWorld * a_World);
- static NIBBLETYPE RotationPitchToMetaData(float a_Rotation, float a_Pitch)
+ static NIBBLETYPE RotationPitchToMetaData(double a_Rotation, double a_Pitch)
{
- if (a_Pitch >= 50.f)
+ if (a_Pitch >= 50)
{
return 0x1;
}
- else if (a_Pitch <= -50.f)
+ else if (a_Pitch <= -50)
{
return 0x0;
}
@@ -32,11 +32,11 @@ public:
{
a_Rotation += 90 + 45; // So its not aligned with axis
- if (a_Rotation > 360.f)
+ if (a_Rotation > 360)
{
- a_Rotation -= 360.f;
+ a_Rotation -= 360;
}
- if ((a_Rotation >= 0.f) && (a_Rotation < 90.f))
+ if ((a_Rotation >= 0) && (a_Rotation < 90))
{
return 0x4;
}
@@ -58,13 +58,12 @@ public:
void ExtendPiston( int, int, int );
void RetractPiston( int, int, int );
- cWorld* m_World;
+ cWorld * m_World;
private:
void ChainMove( int, int, int, char, unsigned short * );
unsigned short FirstPassthroughBlock( int, int, int, char );
-
-};
+} ;
diff --git a/source/Player.cpp b/source/Player.cpp
index 4942db10c..2e05fed26 100644
--- a/source/Player.cpp
+++ b/source/Player.cpp
@@ -843,7 +843,7 @@ void cPlayer::TossItem(
}
}
}
- float vX = 0, vY = 0, vZ = 0;
+ double vX = 0, vY = 0, vZ = 0;
EulerToVector(-GetRotation(), GetPitch(), vZ, vX, vY);
vY = -vY * 2 + 1.f;
m_World->SpawnItemPickups(Drops, GetPosX(), GetPosY() + 1.6f, GetPosZ(), vX * 2, vY * 2, vZ * 2);
diff --git a/source/Protocol/Protocol125.cpp b/source/Protocol/Protocol125.cpp
index 72a1d3b0c..0b4b0f942 100644
--- a/source/Protocol/Protocol125.cpp
+++ b/source/Protocol/Protocol125.cpp
@@ -556,8 +556,8 @@ void cProtocol125::SendPlayerMoveLook(void)
WriteDouble(Player->GetStance() + 0.03); // Add a small amount so that the player doesn't start inside a block
WriteDouble(Player->GetPosY() + 0.03); // Add a small amount so that the player doesn't start inside a block
WriteDouble(Player->GetPosZ());
- WriteFloat (Player->GetRotation());
- WriteFloat (Player->GetPitch());
+ WriteFloat ((float)(Player->GetRotation()));
+ WriteFloat ((float)(Player->GetPitch()));
WriteBool (Player->IsOnGround());
Flush();
}
diff --git a/source/Sign.h b/source/Sign.h
index d5d546c24..cc71666b5 100644
--- a/source/Sign.h
+++ b/source/Sign.h
@@ -1,32 +1,44 @@
+
#pragma once
-class cSign // tolua_export
-{ // tolua_export
+
+
+
+
+// tolua_begin
+class cSign
+{
public:
- static char RotationToMetaData( float a_Rotation ) // tolua_export
- { // tolua_export
- a_Rotation += 180 + (180/16); // So its not aligned with axis
- if( a_Rotation > 360.f ) a_Rotation -= 360.f;
+ static char RotationToMetaData(double a_Rotation)
+ {
+ a_Rotation += 180 + (180 / 16); // So it's not aligned with axis
+ if (a_Rotation > 360)
+ {
+ a_Rotation -= 360;
+ }
- a_Rotation = (a_Rotation/360) * 16;
+ a_Rotation = (a_Rotation / 360) * 16;
return ((char)a_Rotation) % 16;
- } // tolua_export
- static char DirectionToMetaData( char a_Direction ) // tolua_export
- { // tolua_export
- switch( a_Direction )
+ }
+
+
+ static char DirectionToMetaData(char a_Direction)
+ {
+ switch (a_Direction)
{
- case 0x2:
- return 0x2;
- case 0x3:
- return 0x3;
- case 0x4:
- return 0x4;
- case 0x5:
- return 0x5;
- default:
- break;
+ case 0x2: return 0x2;
+ case 0x3: return 0x3;
+ case 0x4: return 0x4;
+ case 0x5: return 0x5;
+ default:
+ break;
};
return 0x2;
}
-}; // tolua_export
+} ;
+// tolua_end
+
+
+
+
diff --git a/source/Simulator/RedstoneSimulator.cpp b/source/Simulator/RedstoneSimulator.cpp
index cfbc3fb1f..0b947b71b 100644
--- a/source/Simulator/RedstoneSimulator.cpp
+++ b/source/Simulator/RedstoneSimulator.cpp
@@ -1015,15 +1015,15 @@ bool cRedstoneSimulator::IsRepeaterPointingAway(const Vector3i & a_RepeaterPos,
-NIBBLETYPE cRedstoneSimulator::RepeaterRotationToMetaData(float a_Rotation)
+NIBBLETYPE cRedstoneSimulator::RepeaterRotationToMetaData(double a_Rotation)
{
a_Rotation += 90 + 45; // So its not aligned with axis
- if (a_Rotation > 360.f)
+ if (a_Rotation > 360)
{
- a_Rotation -= 360.f;
+ a_Rotation -= 360;
}
- if ((a_Rotation >= 0.f) && (a_Rotation < 90.f))
+ if ((a_Rotation >= 0) && (a_Rotation < 90))
{
return 0x1;
}
diff --git a/source/Simulator/RedstoneSimulator.h b/source/Simulator/RedstoneSimulator.h
index 7d869fae1..37c9aa92e 100644
--- a/source/Simulator/RedstoneSimulator.h
+++ b/source/Simulator/RedstoneSimulator.h
@@ -33,7 +33,7 @@ public:
static bool IsRepeaterPointingTo (const Vector3i & a_RepeaterPos, char a_MetaData, const Vector3i & a_BlockPos);
static bool IsRepeaterPointingAway(const Vector3i & a_RepeaterPos, char a_MetaData, const Vector3i & a_BlockPos);
- static NIBBLETYPE RepeaterRotationToMetaData(float a_Rotation);
+ static NIBBLETYPE RepeaterRotationToMetaData(double a_Rotation);
static Vector3i GetRepeaterDirection(NIBBLETYPE a_MetaData);
static NIBBLETYPE LeverDirectionToMetaData(char a_Dir);
static bool IsLeverOn(cWorld * a_World, const Vector3i & a_BlockPos);
diff --git a/source/Stairs.h b/source/Stairs.h
index 41e57a0fe..6d3a5a45c 100644
--- a/source/Stairs.h
+++ b/source/Stairs.h
@@ -5,19 +5,21 @@
-class cStairs // tolua_export
-{ // tolua_export
+// tolua_begin
+
+class cStairs
+{
public:
/// Converts player rotation to stair rotation metadata. To get upside-down stairs, OR with 0x4
- static NIBBLETYPE RotationToMetaData(float a_Rotation) // tolua_export
- { // tolua_export
- a_Rotation += 90 + 45; // So its not aligned with axis
- NIBBLETYPE result = 0x0;
- if (a_Rotation > 360.f)
+ static NIBBLETYPE RotationToMetaData(double a_Rotation)
+ {
+ a_Rotation += 90 + 45; // So its not aligned with axis
+ NIBBLETYPE result = 0x0;
+ if (a_Rotation > 360)
{
- a_Rotation -= 360.f;
+ a_Rotation -= 360;
}
- if ((a_Rotation >= 0.f) && (a_Rotation < 90.f))
+ if ((a_Rotation >= 0) && (a_Rotation < 90))
{
return 0x0;
}
@@ -33,9 +35,10 @@ public:
{
return 0x3;
}
- } // tolua_export
-} ; // tolua_export
+ }
+} ;
+// tolua_end
diff --git a/source/UI/SlotArea.cpp b/source/UI/SlotArea.cpp
index ac2bcbff5..881346c45 100644
--- a/source/UI/SlotArea.cpp
+++ b/source/UI/SlotArea.cpp
@@ -764,7 +764,7 @@ void cSlotAreaTemporary::TossItems(cPlayer & a_Player, int a_Begin, int a_End)
Item.Empty();
} // for i - itr->second[]
- float vX = 0, vY = 0, vZ = 0;
+ double vX = 0, vY = 0, vZ = 0;
EulerToVector(-a_Player.GetRotation(), a_Player.GetPitch(), vZ, vX, vY);
vY = -vY * 2 + 1.f;
a_Player.GetWorld()->SpawnItemPickups(Drops, a_Player.GetPosX(), a_Player.GetPosY() + 1.6f, a_Player.GetPosZ(), vX * 2, vY * 2, vZ * 2);
diff --git a/source/WorldStorage/FastNBT.cpp b/source/WorldStorage/FastNBT.cpp
index 4a9a9df9f..d5dc2b923 100644
--- a/source/WorldStorage/FastNBT.cpp
+++ b/source/WorldStorage/FastNBT.cpp
@@ -385,9 +385,10 @@ void cFastNBTWriter::BeginList(const AString & a_Name, eTagType a_ChildrenType)
m_Result.append(4, (char)0);
++m_CurrentStack;
- m_Stack[m_CurrentStack].m_Type = TAG_List;
- m_Stack[m_CurrentStack].m_Pos = m_Result.size() - 4;
- m_Stack[m_CurrentStack].m_Count = 0;
+ m_Stack[m_CurrentStack].m_Type = TAG_List;
+ m_Stack[m_CurrentStack].m_Pos = m_Result.size() - 4;
+ m_Stack[m_CurrentStack].m_Count = 0;
+ m_Stack[m_CurrentStack].m_ItemType = a_ChildrenType;
}
diff --git a/source/WorldStorage/FastNBT.h b/source/WorldStorage/FastNBT.h
index 5a4b69866..1cae4879f 100644
--- a/source/WorldStorage/FastNBT.h
+++ b/source/WorldStorage/FastNBT.h
@@ -247,6 +247,7 @@ protected:
int m_Type; // TAG_Compound or TAG_List
int m_Pos; // for TAG_List, the position of the list count
int m_Count; // for TAG_List, the element count
+ eTagType m_ItemType; // for TAG_List, the element type
} ;
static const int MAX_STACK = 50; // Highliy doubtful that an NBT would be constructed this many levels deep
@@ -263,6 +264,9 @@ protected:
inline void TagCommon(const AString & a_Name, eTagType a_Type)
{
+ // If we're directly inside a list, check that the list is of the correct type:
+ ASSERT((m_Stack[m_CurrentStack].m_Type != TAG_List) || (m_Stack[m_CurrentStack].m_ItemType == a_Type));
+
if (IsStackTopCompound())
{
// Compound: add the type and name:
diff --git a/source/WorldStorage/NBTChunkSerializer.cpp b/source/WorldStorage/NBTChunkSerializer.cpp
new file mode 100644
index 000000000..f41bd5e97
--- /dev/null
+++ b/source/WorldStorage/NBTChunkSerializer.cpp
@@ -0,0 +1,417 @@
+
+// NBTChunkSerializer.cpp
+
+
+#include "Globals.h"
+#include "NBTChunkSerializer.h"
+#include "../BlockID.h"
+#include "../ChestEntity.h"
+#include "../DispenserEntity.h"
+#include "../FurnaceEntity.h"
+#include "../SignEntity.h"
+#include "../NoteEntity.h"
+#include "../JukeboxEntity.h"
+#include "../Item.h"
+#include "../StringCompression.h"
+#include "../Entity.h"
+#include "../OSSupport/MakeDir.h"
+#include "FastNBT.h"
+#include "../FallingBlock.h"
+#include "../Minecart.h"
+#include "../Mobs/Monster.h"
+#include "../Pickup.h"
+
+
+
+
+cNBTChunkSerializer::cNBTChunkSerializer(cFastNBTWriter & a_Writer) :
+ m_BiomesAreValid(false),
+ m_Writer(a_Writer),
+ m_IsTagOpen(false),
+ m_HasHadEntity(false),
+ m_HasHadBlockEntity(false),
+ m_IsLightValid(false)
+{
+}
+
+
+
+
+
+void cNBTChunkSerializer::Finish(void)
+{
+ if (m_IsTagOpen)
+ {
+ m_Writer.EndList();
+ }
+
+ // If light not valid, reset it to all zeroes:
+ if (!m_IsLightValid)
+ {
+ memset(m_BlockLight, 0, sizeof(m_BlockLight));
+ memset(m_BlockSkyLight, 0, sizeof(m_BlockSkyLight));
+ }
+}
+
+
+
+
+
+void cNBTChunkSerializer::AddItem(const cItem & a_Item, int a_Slot, const AString & a_CompoundName)
+{
+ m_Writer.BeginCompound(a_CompoundName);
+ m_Writer.AddShort("id", (short)(a_Item.m_ItemType));
+ m_Writer.AddShort("Damage", a_Item.m_ItemDamage);
+ m_Writer.AddByte ("Count", a_Item.m_ItemCount);
+ if (a_Slot >= 0)
+ {
+ m_Writer.AddByte ("Slot", (unsigned char)a_Slot);
+ }
+ m_Writer.EndCompound();
+}
+
+
+
+
+
+void cNBTChunkSerializer::AddBasicTileEntity(cBlockEntity * a_Entity, const char * a_EntityTypeID)
+{
+ m_Writer.AddInt ("x", a_Entity->GetPosX());
+ m_Writer.AddInt ("y", a_Entity->GetPosY());
+ m_Writer.AddInt ("z", a_Entity->GetPosZ());
+ m_Writer.AddString("id", a_EntityTypeID);
+}
+
+
+
+
+
+void cNBTChunkSerializer::AddChestEntity(cChestEntity * a_Entity)
+{
+ m_Writer.BeginCompound("");
+ AddBasicTileEntity(a_Entity, "Chest");
+ m_Writer.BeginList("Items", TAG_Compound);
+ for (int i = 0; i < cChestEntity::c_ChestHeight * cChestEntity::c_ChestWidth; i++)
+ {
+ const cItem * Item = a_Entity->GetSlot(i);
+ if ((Item == NULL) || Item->IsEmpty())
+ {
+ continue;
+ }
+ AddItem(*Item, i);
+ } // for i - chest slots[]
+ m_Writer.EndList();
+ m_Writer.EndCompound();
+}
+
+
+
+
+
+void cNBTChunkSerializer::AddDispenserEntity(cDispenserEntity * a_Entity)
+{
+ m_Writer.BeginCompound("");
+ AddBasicTileEntity(a_Entity, "Trap");
+ m_Writer.BeginList("Items", TAG_Compound);
+ for (int i = 0; i < 9; i++)
+ {
+ const cItem * Item = a_Entity->GetSlot(i);
+ if ((Item == NULL) || Item->IsEmpty())
+ {
+ continue;
+ }
+ AddItem(*Item, i);
+ } // for i - contents[]
+ m_Writer.EndList();
+ m_Writer.EndCompound();
+}
+
+
+
+
+
+void cNBTChunkSerializer::AddFurnaceEntity(cFurnaceEntity * a_Furnace)
+{
+ m_Writer.BeginCompound("");
+ AddBasicTileEntity(a_Furnace, "Furnace");
+ m_Writer.BeginList("Items", TAG_Compound);
+ AddItem(*(a_Furnace->GetSlot(0)), 0);
+ AddItem(*(a_Furnace->GetSlot(1)), 1);
+ AddItem(*(a_Furnace->GetSlot(2)), 2);
+ m_Writer.EndList();
+ m_Writer.AddShort("BurnTime", (Int16)(a_Furnace->GetTimeToBurn() / 50.0));
+ m_Writer.AddShort("CookTime", (Int16)(a_Furnace->GetTimeCooked() / 50.0));
+ m_Writer.EndCompound();
+}
+
+
+
+
+
+void cNBTChunkSerializer::AddSignEntity(cSignEntity * a_Sign)
+{
+ m_Writer.BeginCompound("");
+ AddBasicTileEntity(a_Sign, "Sign");
+ m_Writer.AddString("Text1", a_Sign->GetLine(0));
+ m_Writer.AddString("Text2", a_Sign->GetLine(1));
+ m_Writer.AddString("Text3", a_Sign->GetLine(2));
+ m_Writer.AddString("Text4", a_Sign->GetLine(3));
+ m_Writer.EndCompound();
+}
+
+
+
+
+
+void cNBTChunkSerializer::AddNoteEntity(cNoteEntity * a_Note)
+{
+ m_Writer.BeginCompound("");
+ AddBasicTileEntity(a_Note, "Music");
+ m_Writer.AddByte("note", a_Note->GetPitch());
+ m_Writer.EndCompound();
+}
+
+
+
+
+
+void cNBTChunkSerializer::AddJukeboxEntity(cJukeboxEntity * a_Jukebox)
+{
+ m_Writer.BeginCompound("");
+ AddBasicTileEntity(a_Jukebox, "RecordPlayer");
+ m_Writer.AddInt("Record", a_Jukebox->GetRecord());
+ m_Writer.EndCompound();
+}
+
+
+
+
+
+void cNBTChunkSerializer::AddBasicEntity(cEntity * a_Entity, const AString & a_ClassName)
+{
+ m_Writer.AddString("id", a_ClassName);
+ m_Writer.BeginList("Pos", TAG_Double);
+ m_Writer.AddDouble("", a_Entity->GetPosX());
+ m_Writer.AddDouble("", a_Entity->GetPosY());
+ m_Writer.AddDouble("", a_Entity->GetPosZ());
+ m_Writer.EndList();
+ m_Writer.BeginList("Motion", TAG_Double);
+ m_Writer.AddDouble("", a_Entity->GetSpeedX());
+ m_Writer.AddDouble("", a_Entity->GetSpeedY());
+ m_Writer.AddDouble("", a_Entity->GetSpeedZ());
+ m_Writer.EndList();
+ m_Writer.BeginList("Rotation", TAG_Double);
+ m_Writer.AddDouble("", a_Entity->GetRotation());
+ m_Writer.AddDouble("", a_Entity->GetPitch());
+ m_Writer.EndList();
+}
+
+
+
+
+
+void cNBTChunkSerializer::AddFallingBlockEntity(cFallingBlock * a_FallingBlock)
+{
+ m_Writer.BeginCompound("");
+ AddBasicEntity(a_FallingBlock, "FallingSand");
+ m_Writer.AddInt("TileID", a_FallingBlock->GetBlockType());
+ m_Writer.AddByte("Data", a_FallingBlock->GetBlockMeta());
+ m_Writer.AddByte("Time", 1); // Unused in MCServer, Vanilla said to need nonzero
+ m_Writer.AddByte("DropItem", 1);
+ m_Writer.AddByte("HurtEntities", a_FallingBlock->GetBlockType() == E_BLOCK_ANVIL);
+ m_Writer.EndCompound();
+}
+
+
+
+
+
+void cNBTChunkSerializer::AddMinecartEntity(cMinecart * a_Minecart)
+{
+ const char * EntityClass = NULL;
+ switch (a_Minecart->GetPayload())
+ {
+ case cMinecart::mpNone: EntityClass = "MinecarRideable"; break;
+ case cMinecart::mpChest: EntityClass = "MinecartChest"; break;
+ case cMinecart::mpFurnace: EntityClass = "MinecartFurnace"; break;
+ default:
+ {
+ ASSERT(!"Unhandled minecart payload type");
+ return;
+ }
+ } // switch (payload)
+
+ m_Writer.BeginCompound("");
+ AddBasicEntity(a_Minecart, EntityClass);
+ switch (a_Minecart->GetPayload())
+ {
+ case cMinecart::mpChest:
+ {
+ // Add chest contents into the Items tag:
+ AddMinecartChestContents((cMinecartWithChest *)a_Minecart);
+ break;
+ }
+
+ case cMinecart::mpFurnace:
+ {
+ // TODO: Add "Push" and "Fuel" tags
+ break;
+ }
+ } // switch (Payload)
+ m_Writer.EndCompound();
+}
+
+
+
+
+
+void cNBTChunkSerializer::AddMonsterEntity(cMonster * a_Monster)
+{
+ // TODO
+}
+
+
+
+
+
+void cNBTChunkSerializer::AddPickupEntity(cPickup * a_Pickup)
+{
+ m_Writer.BeginCompound("");
+ AddBasicEntity(a_Pickup, "Item");
+ AddItem(a_Pickup->GetItem(), -1, "Item");
+ m_Writer.AddShort("Health", a_Pickup->GetHealth());
+ m_Writer.AddShort("Age", a_Pickup->GetAge());
+ m_Writer.EndCompound();
+}
+
+
+
+
+
+void cNBTChunkSerializer::AddMinecartChestContents(cMinecartWithChest * a_Minecart)
+{
+ m_Writer.BeginList("Items", TAG_Compound);
+ for (int i = 0; i < cMinecartWithChest::NumSlots; i++)
+ {
+ const cItem & Item = a_Minecart->GetItem(i);
+ if (Item.IsEmpty())
+ {
+ continue;
+ }
+ AddItem(Item, i);
+ }
+ m_Writer.EndList();
+}
+
+
+
+
+
+bool cNBTChunkSerializer::LightIsValid(bool a_IsLightValid)
+{
+ m_IsLightValid = a_IsLightValid;
+ return a_IsLightValid; // We want lighting only if it's valid, otherwise don't bother
+}
+
+
+
+
+
+void cNBTChunkSerializer::BiomeData(const cChunkDef::BiomeMap * a_BiomeMap)
+{
+ memcpy(m_Biomes, a_BiomeMap, sizeof(m_Biomes));
+ for (int i = 0; i < ARRAYCOUNT(m_Biomes); i++)
+ {
+ if ((*a_BiomeMap)[i] < 255)
+ {
+ // Normal MC biome, copy as-is:
+ m_VanillaBiomes[i] = (unsigned char)((*a_BiomeMap)[i]);
+ }
+ else
+ {
+ // TODO: MCS-specific biome, need to map to some basic MC biome:
+ ASSERT(!"Unimplemented MCS-specific biome");
+ return;
+ }
+ } // for i - m_BiomeMap[]
+ m_BiomesAreValid = true;
+}
+
+
+
+
+
+void cNBTChunkSerializer::Entity(cEntity * a_Entity)
+{
+ // Add entity into NBT:
+ if (m_IsTagOpen)
+ {
+ if (!m_HasHadEntity)
+ {
+ m_Writer.EndList();
+ m_Writer.BeginList("Entities", TAG_Compound);
+ }
+ }
+ else
+ {
+ m_Writer.BeginList("Entities", TAG_Compound);
+ }
+ m_IsTagOpen = true;
+ m_HasHadEntity = true;
+
+ switch (a_Entity->GetEntityType())
+ {
+ case cEntity::etFallingBlock: AddFallingBlockEntity((cFallingBlock *)a_Entity); break;
+ case cEntity::etMinecart: AddMinecartEntity ((cMinecart *) a_Entity); break;
+ case cEntity::etMonster: AddMonsterEntity ((cMonster *) a_Entity); break;
+ case cEntity::etPickup: AddPickupEntity ((cPickup *) a_Entity); break;
+ case cEntity::etPlayer: return; // Players aren't saved into the world
+ default:
+ {
+ ASSERT(!"Unhandled entity type is being saved");
+ break;
+ }
+ }
+}
+
+
+
+
+
+void cNBTChunkSerializer::BlockEntity(cBlockEntity * a_Entity)
+{
+ if (m_IsTagOpen)
+ {
+ if (!m_HasHadBlockEntity)
+ {
+ m_Writer.EndList();
+ m_Writer.BeginList("TileEntities", TAG_Compound);
+ }
+ }
+ else
+ {
+ m_Writer.BeginList("TileEntities", TAG_Compound);
+ }
+ m_IsTagOpen = true;
+
+ // Add tile-entity into NBT:
+ switch (a_Entity->GetBlockType())
+ {
+ case E_BLOCK_CHEST: AddChestEntity ((cChestEntity *) a_Entity); break;
+ case E_BLOCK_DISPENSER: AddDispenserEntity ((cDispenserEntity *) a_Entity); break;
+ case E_BLOCK_FURNACE: AddFurnaceEntity ((cFurnaceEntity *) a_Entity); break;
+ case E_BLOCK_SIGN_POST:
+ case E_BLOCK_WALLSIGN: AddSignEntity ((cSignEntity *) a_Entity); break;
+ case E_BLOCK_NOTE_BLOCK: AddNoteEntity ((cNoteEntity *) a_Entity); break;
+ case E_BLOCK_JUKEBOX: AddJukeboxEntity ((cJukeboxEntity *) a_Entity); break;
+ default:
+ {
+ ASSERT(!"Unhandled block entity saved into Anvil");
+ }
+ }
+ m_HasHadBlockEntity = true;
+}
+
+
+
+
diff --git a/source/WorldStorage/NBTChunkSerializer.h b/source/WorldStorage/NBTChunkSerializer.h
new file mode 100644
index 000000000..78847dbc0
--- /dev/null
+++ b/source/WorldStorage/NBTChunkSerializer.h
@@ -0,0 +1,101 @@
+
+// NBTChunkSerializer.h
+
+// Declares the cNBTChunkSerializer class that is used for saving individual chunks into NBT format used by Anvil
+
+
+
+
+
+#pragma once
+
+#include "../ChunkDef.h"
+
+
+
+
+
+// fwd:
+class cFastNBTWriter;
+class cEntity;
+class cBlockEntity;
+class cChestEntity;
+class cFurnaceEntity;
+class cDispenserEntity;
+class cSignEntity;
+class cNoteEntity;
+class cJukeboxEntity;
+class cFallingBlock;
+class cMinecart;
+class cMinecartWithChest;
+class cMinecartWithFurnace;
+class cMonster;
+class cPickup;
+
+
+
+
+class cNBTChunkSerializer :
+ public cChunkDataSeparateCollector
+{
+public:
+ cChunkDef::BiomeMap m_Biomes;
+ unsigned char m_VanillaBiomes[cChunkDef::Width * cChunkDef::Width];
+ bool m_BiomesAreValid;
+
+
+ cNBTChunkSerializer(cFastNBTWriter & a_Writer);
+
+ /// Close NBT tags that we've opened
+ void Finish(void);
+
+ bool IsLightValid(void) const {return m_IsLightValid; }
+
+protected:
+
+ /* From cChunkDataSeparateCollector we inherit:
+ - m_BlockTypes[]
+ - m_BlockMetas[]
+ - m_BlockLight[]
+ - m_BlockSkyLight[]
+ */
+
+ cFastNBTWriter & m_Writer;
+
+ bool m_IsTagOpen; // True if a tag has been opened in the callbacks and not yet closed.
+ bool m_HasHadEntity; // True if any Entity has already been received and processed
+ bool m_HasHadBlockEntity; // True if any BlockEntity has already been received and processed
+ bool m_IsLightValid; // True if the chunk lighting is valid
+
+
+ /// Writes an item into the writer, if slot >= 0, adds the Slot tag. The compound is named as requested.
+ void AddItem(const cItem & a_Item, int a_Slot, const AString & a_CompoundName = "");
+
+ // Block entities:
+ void AddBasicTileEntity(cBlockEntity * a_Entity, const char * a_EntityTypeID);
+ void AddChestEntity(cChestEntity * a_Entity);
+ void AddDispenserEntity(cDispenserEntity * a_Entity);
+ void AddFurnaceEntity(cFurnaceEntity * a_Furnace);
+ void AddSignEntity(cSignEntity * a_Sign);
+ void AddNoteEntity(cNoteEntity * a_Note);
+ void AddJukeboxEntity(cJukeboxEntity * a_Jukebox);
+ void AddBasicEntity(cEntity * a_Entity, const AString & a_ClassName);
+
+ // Entities:
+ void AddFallingBlockEntity(cFallingBlock * a_FallingBlock);
+ void AddMinecartEntity (cMinecart * a_Minecart);
+ void AddMonsterEntity (cMonster * a_Monster);
+ void AddPickupEntity (cPickup * a_Pickup);
+
+ void AddMinecartChestContents(cMinecartWithChest * a_Minecart);
+
+ // cChunkDataSeparateCollector overrides:
+ virtual bool LightIsValid(bool a_IsLightValid) override;
+ virtual void BiomeData(const cChunkDef::BiomeMap * a_BiomeMap) override;
+ virtual void Entity(cEntity * a_Entity) override;
+ virtual void BlockEntity(cBlockEntity * a_Entity) override;
+} ; // class cNBTChunkSerializer
+
+
+
+
diff --git a/source/WorldStorage/WSSAnvil.cpp b/source/WorldStorage/WSSAnvil.cpp
index 70e280a57..de9a3c3ae 100644
--- a/source/WorldStorage/WSSAnvil.cpp
+++ b/source/WorldStorage/WSSAnvil.cpp
@@ -5,6 +5,7 @@
#include "Globals.h"
#include "WSSAnvil.h"
+#include "NBTChunkSerializer.h"
#include "../World.h"
#include "zlib.h"
#include "../BlockID.h"
@@ -19,6 +20,10 @@
#include "../Entity.h"
#include "../OSSupport/MakeDir.h"
#include "FastNBT.h"
+#include "../FallingBlock.h"
+#include "../Minecart.h"
+#include "../Mobs/Monster.h"
+#include "../Pickup.h"
@@ -38,238 +43,6 @@ Since only the header is actually in the memory, this number can be high, but st
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// cNBTChunkSerializer
-
-class cNBTChunkSerializer :
- public cChunkDataSeparateCollector
-{
-public:
- cChunkDef::BiomeMap m_Biomes;
- unsigned char m_VanillaBiomes[cChunkDef::Width * cChunkDef::Width];
- bool m_BiomesAreValid;
-
-
- cNBTChunkSerializer(cFastNBTWriter & a_Writer) :
- m_BiomesAreValid(false),
- m_Writer(a_Writer),
- m_IsTagOpen(false),
- m_HasHadEntity(false),
- m_HasHadBlockEntity(false),
- m_IsLightValid(false)
- {
- }
-
-
- /// Close NBT tags that we've opened
- void Finish(void)
- {
- if (m_IsTagOpen)
- {
- m_Writer.EndList();
- }
-
- // If light not valid, reset it to all zeroes:
- if (!m_IsLightValid)
- {
- memset(m_BlockLight, 0, sizeof(m_BlockLight));
- memset(m_BlockSkyLight, 0, sizeof(m_BlockSkyLight));
- }
- }
-
-
- bool IsLightValid(void) const {return m_IsLightValid; }
-
-protected:
-
- /* From cChunkDataSeparateCollector we inherit:
- - m_BlockTypes[]
- - m_BlockMetas[]
- - m_BlockLight[]
- - m_BlockSkyLight[]
- */
-
- cFastNBTWriter & m_Writer;
-
- bool m_IsTagOpen; // True if a tag has been opened in the callbacks and not yet closed.
- bool m_HasHadEntity; // True if any Entity has already been received and processed
- bool m_HasHadBlockEntity; // True if any BlockEntity has already been received and processed
- bool m_IsLightValid; // True if the chunk lighting is valid
-
-
- void AddBasicTileEntity(cBlockEntity * a_Entity, const char * a_EntityTypeID)
- {
- m_Writer.AddInt ("x", a_Entity->GetPosX());
- m_Writer.AddInt ("y", a_Entity->GetPosY());
- m_Writer.AddInt ("z", a_Entity->GetPosZ());
- m_Writer.AddString("id", a_EntityTypeID);
- }
-
-
- void AddItem(const cItem * a_Item, int a_Slot)
- {
- m_Writer.BeginCompound("");
- m_Writer.AddShort("id", (short)(a_Item->m_ItemType));
- m_Writer.AddShort("Damage", a_Item->m_ItemDamage);
- m_Writer.AddByte ("Count", a_Item->m_ItemCount);
- m_Writer.AddByte ("Slot", (unsigned char)a_Slot);
- m_Writer.EndCompound();
- }
-
-
- void AddChestEntity(cChestEntity * a_Entity)
- {
- m_Writer.BeginCompound("");
- AddBasicTileEntity(a_Entity, "Chest");
- m_Writer.BeginList("Items", TAG_Compound);
- for (int i = 0; i < cChestEntity::c_ChestHeight * cChestEntity::c_ChestWidth; i++)
- {
- const cItem * Item = a_Entity->GetSlot(i);
- if ((Item == NULL) || Item->IsEmpty())
- {
- continue;
- }
- AddItem(Item, i);
- }
- m_Writer.EndList();
- m_Writer.EndCompound();
- }
-
-
- void AddDispenserEntity(cDispenserEntity * a_Entity)
- {
- m_Writer.BeginCompound("");
- AddBasicTileEntity(a_Entity, "Trap");
- m_Writer.BeginList("Items", TAG_Compound);
- for (int i = 0; i < 9; i++)
- {
- const cItem * Item = a_Entity->GetSlot(i);
- if ((Item == NULL) || Item->IsEmpty())
- {
- continue;
- }
- AddItem(Item, i);
- }
- m_Writer.EndList();
- m_Writer.EndCompound();
- }
-
-
- void AddFurnaceEntity(cFurnaceEntity * a_Furnace)
- {
- m_Writer.BeginCompound("");
- AddBasicTileEntity(a_Furnace, "Furnace");
- m_Writer.BeginList("Items", TAG_Compound);
- AddItem(a_Furnace->GetSlot(0), 0);
- AddItem(a_Furnace->GetSlot(1), 1);
- AddItem(a_Furnace->GetSlot(2), 2);
- m_Writer.EndList();
- m_Writer.AddShort("BurnTime", (Int16)(a_Furnace->GetTimeToBurn() / 50.0));
- m_Writer.AddShort("CookTime", (Int16)(a_Furnace->GetTimeCooked() / 50.0));
- m_Writer.EndCompound();
- }
-
-
- void AddSignEntity(cSignEntity * a_Sign)
- {
- m_Writer.BeginCompound("");
- AddBasicTileEntity(a_Sign, "Sign");
- m_Writer.AddString("Text1", a_Sign->GetLine(0));
- m_Writer.AddString("Text2", a_Sign->GetLine(1));
- m_Writer.AddString("Text3", a_Sign->GetLine(2));
- m_Writer.AddString("Text4", a_Sign->GetLine(3));
- m_Writer.EndCompound();
- }
-
- void AddNoteEntity(cNoteEntity * a_Note)
- {
- m_Writer.BeginCompound("");
- AddBasicTileEntity(a_Note, "Music");
- m_Writer.AddByte("note", a_Note->GetPitch());
- m_Writer.EndCompound();
- }
-
- void AddJukeboxEntity(cJukeboxEntity * a_Jukebox)
- {
- m_Writer.BeginCompound("");
- AddBasicTileEntity(a_Jukebox, "RecordPlayer");
- m_Writer.AddInt("Record", a_Jukebox->GetRecord());
- m_Writer.EndCompound();
- }
-
- virtual bool LightIsValid(bool a_IsLightValid) override
- {
- m_IsLightValid = a_IsLightValid;
- return a_IsLightValid; // We want lighting only if it's valid, otherwise don't bother
- }
-
-
- virtual void BiomeData(const cChunkDef::BiomeMap * a_BiomeMap) override
- {
- memcpy(m_Biomes, a_BiomeMap, sizeof(m_Biomes));
- for (int i = 0; i < ARRAYCOUNT(m_Biomes); i++)
- {
- if ((*a_BiomeMap)[i] < 255)
- {
- // Normal MC biome, copy as-is:
- m_VanillaBiomes[i] = (unsigned char)((*a_BiomeMap)[i]);
- }
- else
- {
- // TODO: MCS-specific biome, need to map to some basic MC biome:
- ASSERT(!"Unimplemented MCS-specific biome");
- return;
- }
- } // for i - m_BiomeMap[]
- m_BiomesAreValid = true;
- }
-
-
- virtual void Entity(cEntity * a_Entity) override
- {
- // TODO: Add entity into NBT:
- }
-
-
- virtual void BlockEntity(cBlockEntity * a_Entity)
- {
- if (m_IsTagOpen)
- {
- if (!m_HasHadBlockEntity)
- {
- m_Writer.EndList();
- m_Writer.BeginList("TileEntities", TAG_Compound);
- }
- }
- else
- {
- m_Writer.BeginList("TileEntities", TAG_Compound);
- }
- m_IsTagOpen = true;
-
- // Add tile-entity into NBT:
- switch (a_Entity->GetBlockType())
- {
- case E_BLOCK_CHEST: AddChestEntity ((cChestEntity *) a_Entity); break;
- case E_BLOCK_DISPENSER: AddDispenserEntity ((cDispenserEntity *) a_Entity); break;
- case E_BLOCK_FURNACE: AddFurnaceEntity ((cFurnaceEntity *) a_Entity); break;
- case E_BLOCK_SIGN_POST:
- case E_BLOCK_WALLSIGN: AddSignEntity ((cSignEntity *) a_Entity); break;
- case E_BLOCK_NOTE_BLOCK: AddNoteEntity ((cNoteEntity *) a_Entity); break;
- case E_BLOCK_JUKEBOX: AddJukeboxEntity ((cJukeboxEntity *) a_Entity); break;
- default:
- {
- ASSERT(!"Unhandled block entity saved into Anvil");
- }
- }
- m_HasHadBlockEntity = true;
- }
-} ; // class cNBTChunkSerializer
-
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cWSSAnvil:
cWSSAnvil::cWSSAnvil(cWorld * a_World) :
@@ -378,6 +151,9 @@ bool cWSSAnvil::SetChunkData(const cChunkCoords & a_Chunk, const AString & a_Dat
{
return false;
}
+ LOGD("Saving chunk [%d, %d] into region file %s",
+ a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, File->GetFileName().c_str()
+ );
return File->SetChunkData(a_Chunk, a_Data);
}
@@ -392,6 +168,10 @@ cWSSAnvil::cMCAFile * cWSSAnvil::LoadMCAFile(const cChunkCoords & a_Chunk)
const int RegionX = FAST_FLOOR_DIV(a_Chunk.m_ChunkX, 32);
const int RegionZ = FAST_FLOOR_DIV(a_Chunk.m_ChunkZ, 32);
+ ASSERT(a_Chunk.m_ChunkX - RegionX * 32 >= 0);
+ ASSERT(a_Chunk.m_ChunkZ - RegionZ * 32 >= 0);
+ ASSERT(a_Chunk.m_ChunkX - RegionX * 32 < 32);
+ ASSERT(a_Chunk.m_ChunkZ - RegionZ * 32 < 32);
// Is it already cached?
for (cMCAFiles::iterator itr = m_Files.begin(); itr != m_Files.end(); ++itr)
@@ -478,6 +258,7 @@ bool cWSSAnvil::SaveChunkToData(const cChunkCoords & a_Chunk, AString & a_Data)
return false;
}
Writer.Finish();
+
CompressString(Writer.GetResult().data(), Writer.GetResult().size(), a_Data);
return true;
}
@@ -719,9 +500,26 @@ cChunkDef::BiomeMap * cWSSAnvil::LoadBiomeMapFromNBT(cChunkDef::BiomeMap * a_Bio
-void cWSSAnvil::LoadEntitiesFromNBT(cEntityList & a_Entitites, const cParsedNBT & a_NBT, int a_TagIdx)
+void cWSSAnvil::LoadEntitiesFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
- // TODO: Load the entities
+ if ((a_TagIdx < 0) || (a_NBT.GetType(a_TagIdx) != TAG_List))
+ {
+ return;
+ }
+
+ for (int Child = a_NBT.GetFirstChild(a_TagIdx); Child != -1; Child = a_NBT.GetNextSibling(Child))
+ {
+ if (a_NBT.GetType(Child) != TAG_Compound)
+ {
+ continue;
+ }
+ int sID = a_NBT.FindChildByName(Child, "id");
+ if (sID < 0)
+ {
+ continue;
+ }
+ LoadEntityFromNBT(a_Entities, a_NBT, Child, a_NBT.GetData(sID), a_NBT.GetDataLength(sID));
+ } // for Child - a_NBT[]
}
@@ -778,6 +576,37 @@ void cWSSAnvil::LoadBlockEntitiesFromNBT(cBlockEntityList & a_BlockEntities, con
+bool cWSSAnvil::LoadItemFromNBT(cItem & a_Item, const cParsedNBT & a_NBT, int a_TagIdx)
+{
+ int ID = a_NBT.FindChildByName(a_TagIdx, "id");
+ if ((ID < 0) || (a_NBT.GetType(ID) != TAG_Short))
+ {
+ return false;
+ }
+ a_Item.m_ItemType = (ENUM_ITEM_ID)(a_NBT.GetShort(ID));
+
+ int Damage = a_NBT.FindChildByName(a_TagIdx, "Damage");
+ if ((Damage < 0) || (a_NBT.GetType(Damage) != TAG_Short))
+ {
+ return false;
+ }
+ a_Item.m_ItemDamage = a_NBT.GetShort(Damage);
+
+ int Count = a_NBT.FindChildByName(a_TagIdx, "Count");
+ if ((Count < 0) || (a_NBT.GetType(Count) != TAG_Byte))
+ {
+ return false;
+ }
+ a_Item.m_ItemCount = a_NBT.GetByte(Count);
+
+ // TODO: enchantments and other item properties
+ return true;
+}
+
+
+
+
+
void cWSSAnvil::LoadChestFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx)
{
ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound);
@@ -800,25 +629,10 @@ void cWSSAnvil::LoadChestFromNBT(cBlockEntityList & a_BlockEntities, const cPars
continue;
}
cItem Item;
- int ID = a_NBT.FindChildByName(Child, "id");
- if ((ID < 0) || (a_NBT.GetType(ID) != TAG_Short))
- {
- continue;
- }
- Item.m_ItemType = (ENUM_ITEM_ID)(a_NBT.GetShort(ID));
- int Damage = a_NBT.FindChildByName(Child, "Damage");
- if ((Damage < 0) || (a_NBT.GetType(Damage) != TAG_Short))
- {
- continue;
- }
- Item.m_ItemDamage = a_NBT.GetShort(Damage);
- int Count = a_NBT.FindChildByName(Child, "Count");
- if ((Count < 0) || (a_NBT.GetType(Count) != TAG_Byte))
+ if (LoadItemFromNBT(Item, a_NBT, Child))
{
- continue;
+ Chest->SetSlot(a_NBT.GetByte(Slot), Item);
}
- Item.m_ItemCount = a_NBT.GetByte(Count);
- Chest->SetSlot(a_NBT.GetByte(Slot), Item);
} // for itr - ItemDefs[]
a_BlockEntities.push_back(Chest.release());
}
@@ -849,25 +663,10 @@ void cWSSAnvil::LoadDispenserFromNBT(cBlockEntityList & a_BlockEntities, const c
continue;
}
cItem Item;
- int ID = a_NBT.FindChildByName(Child, "id");
- if ((ID < 0) || (a_NBT.GetType(ID) != TAG_Short))
- {
- continue;
- }
- Item.m_ItemType = (ENUM_ITEM_ID)(a_NBT.GetShort(ID));
- int Damage = a_NBT.FindChildByName(Child, "Damage");
- if ((Damage < 0) || (a_NBT.GetType(Damage) != TAG_Short))
+ if (LoadItemFromNBT(Item, a_NBT, Child))
{
- continue;
- }
- Item.m_ItemDamage = a_NBT.GetShort(Damage);
- int Count = a_NBT.FindChildByName(Child, "Count");
- if ((Count < 0) || (a_NBT.GetType(Count) != TAG_Byte))
- {
- continue;
+ Dispenser->SetSlot(a_NBT.GetByte(Slot), Item);
}
- Item.m_ItemCount = a_NBT.GetByte(Count);
- Dispenser->SetSlot(a_NBT.GetByte(Slot), Item);
} // for itr - ItemDefs[]
a_BlockEntities.push_back(Dispenser.release());
}
@@ -898,25 +697,10 @@ void cWSSAnvil::LoadFurnaceFromNBT(cBlockEntityList & a_BlockEntities, const cPa
continue;
}
cItem Item;
- int ID = a_NBT.FindChildByName(Child, "id");
- if ((ID < 0) || (a_NBT.GetType(ID) != TAG_Short))
- {
- continue;
- }
- Item.m_ItemType = (ENUM_ITEM_ID)(a_NBT.GetShort(ID));
- int Damage = a_NBT.FindChildByName(Child, "Damage");
- if ((Damage < 0) || (a_NBT.GetType(Damage) != TAG_Short))
- {
- continue;
- }
- Item.m_ItemDamage = a_NBT.GetShort(Damage);
- int Count = a_NBT.FindChildByName(Child, "Count");
- if ((Count < 0) || (a_NBT.GetType(Count) != TAG_Byte))
+ if (LoadItemFromNBT(Item, a_NBT, Child))
{
- continue;
+ Furnace->SetSlot(a_NBT.GetByte(Slot), Item);
}
- Item.m_ItemCount = a_NBT.GetByte(Count);
- Furnace->SetSlot(a_NBT.GetByte(Slot), Item);
} // for itr - ItemDefs[]
int BurnTime = a_NBT.FindChildByName(a_TagIdx, "BurnTime");
if (BurnTime >= 0)
@@ -1022,6 +806,159 @@ void cWSSAnvil::LoadJukeboxFromNBT(cBlockEntityList & a_BlockEntities, const cPa
+void cWSSAnvil::LoadEntityFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_EntityTagIdx, const char * a_IDTag, int a_IDTagLength)
+{
+ if (strncmp(a_IDTag, "FallingBlock", a_IDTagLength) == 0)
+ {
+ LoadFallingBlockFromNBT(a_Entities, a_NBT, a_EntityTagIdx);
+ }
+ else if (strncmp(a_IDTag, "Minecart", a_IDTagLength) == 0)
+ {
+ // It is a minecart, old style, find out the type:
+ int TypeTag = a_NBT.FindChildByName(a_EntityTagIdx, "Type");
+ if ((TypeTag < 0) || (a_NBT.GetType(TypeTag) != TAG_Int))
+ {
+ return;
+ }
+ switch (a_NBT.GetInt(TypeTag))
+ {
+ case 0: LoadMinecartRFromNBT(a_Entities, a_NBT, a_EntityTagIdx); break; // Rideable minecart
+ case 1: LoadMinecartCFromNBT(a_Entities, a_NBT, a_EntityTagIdx); break; // Minecart with chest
+ case 2: LoadMinecartFFromNBT(a_Entities, a_NBT, a_EntityTagIdx); break; // Minecart with furnace
+ }
+ }
+ else if (strncmp(a_IDTag, "MinecartRideable", a_IDTagLength) == 0)
+ {
+ LoadMinecartRFromNBT(a_Entities, a_NBT, a_EntityTagIdx);
+ }
+ else if (strncmp(a_IDTag, "MinecartChest", a_IDTagLength) == 0)
+ {
+ LoadMinecartCFromNBT(a_Entities, a_NBT, a_EntityTagIdx);
+ }
+ else if (strncmp(a_IDTag, "MinecartFurnace", a_IDTagLength) == 0)
+ {
+ LoadMinecartFFromNBT(a_Entities, a_NBT, a_EntityTagIdx);
+ }
+ if (strncmp(a_IDTag, "Item", a_IDTagLength) == 0)
+ {
+ LoadPickupFromNBT(a_Entities, a_NBT, a_EntityTagIdx);
+ }
+ // TODO: other entities
+}
+
+
+
+
+
+void cWSSAnvil::LoadFallingBlockFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
+{
+ // TODO
+}
+
+
+
+
+
+void cWSSAnvil::LoadMinecartRFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
+{
+ // TODO
+}
+
+
+
+
+
+void cWSSAnvil::LoadMinecartCFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
+{
+ // TODO
+}
+
+
+
+
+
+void cWSSAnvil::LoadMinecartFFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
+{
+ // TODO
+}
+
+
+
+
+
+void cWSSAnvil::LoadPickupFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
+{
+ int ItemTag = a_NBT.FindChildByName(a_TagIdx, "Item");
+ if ((ItemTag < 0) || (a_NBT.GetType(ItemTag) != TAG_Compound))
+ {
+ return;
+ }
+ cItem Item;
+ if (!LoadItemFromNBT(Item, a_NBT, ItemTag))
+ {
+ return;
+ }
+ std::auto_ptr<cPickup> Pickup(new cPickup(0, 0, 0, Item));
+ if (!LoadEntityBaseFromNBT(*Pickup.get(), a_NBT, a_TagIdx))
+ {
+ return;
+ }
+ a_Entities.push_back(Pickup.release());
+}
+
+
+
+
+
+bool cWSSAnvil::LoadEntityBaseFromNBT(cEntity & a_Entity, const cParsedNBT & a_NBT, int a_TagIdx)
+{
+ double Pos[3];
+ if (!LoadDoublesListFromNBT(Pos, 3, a_NBT, a_NBT.FindChildByName(a_TagIdx, "Pos")))
+ {
+ return false;
+ }
+ a_Entity.SetPosition(Pos[0], Pos[1], Pos[2]);
+
+ double Speed[3];
+ if (!LoadDoublesListFromNBT(Speed, 3, a_NBT, a_NBT.FindChildByName(a_TagIdx, "Motion")))
+ {
+ return false;
+ }
+ a_Entity.SetSpeed(Speed[0], Speed[1], Speed[2]);
+
+ double Rotation[3];
+ if (!LoadDoublesListFromNBT(Rotation, 2, a_NBT, a_NBT.FindChildByName(a_TagIdx, "Rotation")))
+ {
+ return false;
+ }
+ a_Entity.SetRotation(Rotation[0]);
+ a_Entity.SetRoll (Rotation[1]);
+
+ return true;
+}
+
+
+
+
+
+bool cWSSAnvil::LoadDoublesListFromNBT(double * a_Doubles, int a_NumDoubles, const cParsedNBT & a_NBT, int a_TagIdx)
+{
+ if ((a_TagIdx < 0) || (a_NBT.GetType(a_TagIdx) != TAG_List) || (a_NBT.GetChildrenType(a_TagIdx) != TAG_Double))
+ {
+ return false;
+ }
+ int idx = 0;
+ for (int Tag = a_NBT.GetFirstChild(a_TagIdx); (Tag > 0) && (idx < a_NumDoubles); Tag = a_NBT.GetNextSibling(Tag), ++idx)
+ {
+ a_Doubles[idx] = a_NBT.GetDouble(Tag);
+ } // for Tag - PosTag[]
+ return (idx == a_NumDoubles); // Did we read enough doubles?
+}
+
+
+
+
+
bool cWSSAnvil::GetBlockEntityNBTPos(const cParsedNBT & a_NBT, int a_TagIdx, int & a_X, int & a_Y, int & a_Z)
{
int x = a_NBT.FindChildByName(a_TagIdx, "x");
@@ -1162,6 +1099,7 @@ bool cWSSAnvil::cMCAFile::SetChunkData(const cChunkCoords & a_Chunk, const AStri
{
if (!OpenFile(false))
{
+ LOGWARNING("Cannot save chunk [%d, %d], opening file \"%s\" failed", a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, GetFileName().c_str());
return false;
}
@@ -1183,15 +1121,18 @@ bool cWSSAnvil::cMCAFile::SetChunkData(const cChunkCoords & a_Chunk, const AStri
unsigned ChunkSize = htonl(a_Data.size() + 1);
if (m_File.Write(&ChunkSize, 4) != 4)
{
+ LOGWARNING("Cannot save chunk [%d, %d], writing(1) data to file \"%s\" failed", a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, GetFileName().c_str());
return false;
}
char CompressionType = 2;
if (m_File.Write(&CompressionType, 1) != 1)
{
+ LOGWARNING("Cannot save chunk [%d, %d], writing(2) data to file \"%s\" failed", a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, GetFileName().c_str());
return false;
}
if (m_File.Write(a_Data.data(), a_Data.size()) != (int)(a_Data.size()))
{
+ LOGWARNING("Cannot save chunk [%d, %d], writing(3) data to file \"%s\" failed", a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, GetFileName().c_str());
return false;
}
@@ -1199,9 +1140,14 @@ bool cWSSAnvil::cMCAFile::SetChunkData(const cChunkCoords & a_Chunk, const AStri
ChunkSize = (a_Data.size() + MCA_CHUNK_HEADER_LENGTH + 4095) / 4096; // Round data size *up* to nearest 4KB sector, make it a sector number
ASSERT(ChunkSize < 256);
m_Header[LocalX + 32 * LocalZ] = htonl((ChunkSector << 8) | ChunkSize);
- m_File.Seek(0);
+ if (m_File.Seek(0) < 0)
+ {
+ LOGWARNING("Cannot save chunk [%d, %d], seeking in file \"%s\" failed", a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, GetFileName().c_str());
+ return false;
+ }
if (m_File.Write(m_Header, sizeof(m_Header)) != sizeof(m_Header))
{
+ LOGWARNING("Cannot save chunk [%d, %d], writing header to file \"%s\" failed", a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, GetFileName().c_str());
return false;
}
diff --git a/source/WorldStorage/WSSAnvil.h b/source/WorldStorage/WSSAnvil.h
index cdd225bd5..640edd210 100644
--- a/source/WorldStorage/WSSAnvil.h
+++ b/source/WorldStorage/WSSAnvil.h
@@ -111,6 +111,9 @@ protected:
/// Loads the chunk's BlockEntities from NBT data (a_Tag is the Level\\TileEntities list tag; may be -1)
void LoadBlockEntitiesFromNBT(cBlockEntityList & a_BlockEntitites, const cParsedNBT & a_NBT, int a_Tag);
+ /// Loads a cItem contents from the specified NBT tag; returns true if successful. Doesn't load the Slot tag
+ bool LoadItemFromNBT(cItem & a_Item, const cParsedNBT & a_NBT, int a_TagIdx);
+
void LoadChestFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
void LoadDispenserFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
void LoadFurnaceFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
@@ -118,6 +121,20 @@ protected:
void LoadNoteFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
void LoadJukeboxFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
+ void LoadEntityFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_EntityTagIdx, const char * a_IDTag, int a_IDTagLength);
+
+ void LoadFallingBlockFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
+ void LoadMinecartRFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
+ void LoadMinecartCFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
+ void LoadMinecartFFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
+ void LoadPickupFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
+
+ /// Loads entity common data from the NBT compound; returns true if successful
+ bool LoadEntityBaseFromNBT(cEntity & a_Entity, const cParsedNBT & a_NBT, int a_TagIdx);
+
+ /// Loads an array of doubles of the specified length from the specified NBT list tag a_TagIdx; returns true if successful
+ bool LoadDoublesListFromNBT(double * a_Doubles, int a_NumDoubles, const cParsedNBT & a_NBT, int a_TagIdx);
+
/// Helper function for extracting the X, Y, and Z int subtags of a NBT compound; returns true if successful
bool GetBlockEntityNBTPos(const cParsedNBT & a_NBT, int a_TagIdx, int & a_X, int & a_Y, int & a_Z);