diff --git a/VC2008/MCServer.vcproj b/VC2008/MCServer.vcproj index 2a9d6ab25..6f6e8cb6e 100644 --- a/VC2008/MCServer.vcproj +++ b/VC2008/MCServer.vcproj @@ -1634,6 +1634,14 @@ RelativePath="..\source\WorldStorage\FastNBT.h" > + + + + 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 -public: - static char RotationToMetaData( float a_Rotation ) // tolua_export - { // tolua_export - 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 ) - return 0x0; - else if( a_Rotation >= 180 && a_Rotation < 270 ) - return 0x2; - 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 + + +// tolua_begin +class cDoors +{ +public: + static char RotationToMetaData(double a_Rotation) + { + a_Rotation += 90 + 45; // So its not aligned with axis + 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)) + { + return 0x2; + } + else if ((a_Rotation >= 90) && (a_Rotation < 180)) + { + return 0x1; + } + else + { + return 0x3; + } + } + + + 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) - { - return (a_Block == E_BLOCK_WOODEN_DOOR || a_Block == E_BLOCK_IRON_DOOR); } + + + inline static bool IsDoor(BLOCKTYPE a_Block) + { + 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 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" @@ -37,238 +42,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: @@ -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)) + if (LoadItemFromNBT(Item, a_NBT, Child)) { - continue; + Chest->SetSlot(a_NBT.GetByte(Slot), Item); } - 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)) - { - continue; - } - 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)) + if (LoadItemFromNBT(Item, a_NBT, Child)) { - continue; + Dispenser->SetSlot(a_NBT.GetByte(Slot), Item); } - 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)) - { - continue; - } - 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)) + if (LoadItemFromNBT(Item, a_NBT, Child)) { - continue; + Furnace->SetSlot(a_NBT.GetByte(Slot), Item); } - 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)) - { - continue; - } - 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 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);