diff options
Diffstat (limited to 'src')
81 files changed, 2976 insertions, 167 deletions
diff --git a/src/Bindings/AllToLua.pkg b/src/Bindings/AllToLua.pkg index 1f08c66dc..6537437cd 100644 --- a/src/Bindings/AllToLua.pkg +++ b/src/Bindings/AllToLua.pkg @@ -48,6 +48,7 @@ $cfile "../ItemGrid.h" $cfile "../BlockEntities/BlockEntity.h" $cfile "../BlockEntities/BlockEntityWithItems.h" $cfile "../BlockEntities/ChestEntity.h" +$cfile "../BlockEntities/CommandBlockEntity.h" $cfile "../BlockEntities/DropSpenserEntity.h" $cfile "../BlockEntities/DispenserEntity.h" $cfile "../BlockEntities/DropperEntity.h" @@ -72,6 +73,8 @@ $cfile "../CraftingRecipes.h" $cfile "../UI/Window.h" $cfile "../Mobs/Monster.h" $cfile "../CompositeChat.h" +$cfile "../Map.h" +$cfile "../MapManager.h" @@ -84,3 +87,10 @@ class cLineBlockTracer; + +// To avoid tolua treating Byte as a class, and avoid the need to $cfile entire Globals.h: +typedef unsigned char Byte; + + + + diff --git a/src/Bindings/LuaState.cpp b/src/Bindings/LuaState.cpp index ca7f6b255..45a066efe 100644 --- a/src/Bindings/LuaState.cpp +++ b/src/Bindings/LuaState.cpp @@ -677,6 +677,11 @@ void cLuaState::Push(void * a_Ptr) { ASSERT(IsValid()); + // Investigate the cause of this - what is the callstack? + LOGWARNING("Lua engine encountered an error - attempting to push a plain pointer"); + LogStackTrace(); + ASSERT(!"A plain pointer should never be pushed on Lua stack"); + lua_pushnil(m_LuaState); m_NumCurrentFunctionArgs += 1; } @@ -1265,6 +1270,8 @@ void cLuaState::LogStack(const char * a_Header) void cLuaState::LogStack(lua_State * a_LuaState, const char * a_Header) { + UNUSED(a_Header); // The param seems unused when compiling for release, so the compiler warns + LOGD((a_Header != NULL) ? a_Header : "Lua C API Stack contents:"); for (int i = lua_gettop(a_LuaState); i > 0; i--) { diff --git a/src/Bindings/ManualBindings.cpp b/src/Bindings/ManualBindings.cpp index c220e5e0a..461186d3b 100644 --- a/src/Bindings/ManualBindings.cpp +++ b/src/Bindings/ManualBindings.cpp @@ -1321,7 +1321,9 @@ static int tolua_cPluginManager_ForEachCommand(lua_State * tolua_S) private: virtual bool Command(const AString & a_Command, const cPlugin * a_Plugin, const AString & a_Permission, const AString & a_HelpString) override { - lua_rawgeti( LuaState, LUA_REGISTRYINDEX, FuncRef); /* Push function reference */ + UNUSED(a_Plugin); + + lua_rawgeti(LuaState, LUA_REGISTRYINDEX, FuncRef); /* Push function reference */ tolua_pushcppstring(LuaState, a_Command); tolua_pushcppstring(LuaState, a_Permission); tolua_pushcppstring(LuaState, a_HelpString); @@ -1396,7 +1398,10 @@ static int tolua_cPluginManager_ForEachConsoleCommand(lua_State * tolua_S) private: virtual bool Command(const AString & a_Command, const cPlugin * a_Plugin, const AString & a_Permission, const AString & a_HelpString) override { - lua_rawgeti( LuaState, LUA_REGISTRYINDEX, FuncRef); /* Push function reference */ + UNUSED(a_Plugin); + UNUSED(a_Permission); + + lua_rawgeti(LuaState, LUA_REGISTRYINDEX, FuncRef); /* Push function reference */ tolua_pushcppstring(LuaState, a_Command); tolua_pushcppstring(LuaState, a_HelpString); @@ -2350,6 +2355,37 @@ static int tolua_cBlockArea_GetBlockTypeMeta(lua_State * tolua_S) +static int tolua_cBlockArea_GetOrigin(lua_State * tolua_S) +{ + // function cBlockArea::GetOrigin() + // Returns all three coords of the origin point + // Exported manually because there's no direct C++ equivalent, + // plus tolua would generate extra input params for the outputs + + cLuaState L(tolua_S); + if (!L.CheckParamUserType(1, "cBlockArea")) + { + return 0; + } + + cBlockArea * self = (cBlockArea *)tolua_tousertype(tolua_S, 1, NULL); + if (self == NULL) + { + tolua_error(tolua_S, "invalid 'self' in function 'cBlockArea:GetOrigin'", NULL); + return 0; + } + + // Push the three origin coords: + lua_pushnumber(tolua_S, self->GetOriginX()); + lua_pushnumber(tolua_S, self->GetOriginY()); + lua_pushnumber(tolua_S, self->GetOriginZ()); + return 3; +} + + + + + static int tolua_cBlockArea_GetRelBlockTypeMeta(lua_State * tolua_S) { // function cBlockArea::GetRelBlockTypeMeta() @@ -2385,6 +2421,37 @@ static int tolua_cBlockArea_GetRelBlockTypeMeta(lua_State * tolua_S) +static int tolua_cBlockArea_GetSize(lua_State * tolua_S) +{ + // function cBlockArea::GetSize() + // Returns all three sizes of the area + // Exported manually because there's no direct C++ equivalent, + // plus tolua would generate extra input params for the outputs + + cLuaState L(tolua_S); + if (!L.CheckParamUserType(1, "cBlockArea")) + { + return 0; + } + + cBlockArea * self = (cBlockArea *)tolua_tousertype(tolua_S, 1, NULL); + if (self == NULL) + { + tolua_error(tolua_S, "invalid 'self' in function 'cBlockArea:GetSize'", NULL); + return 0; + } + + // Push the three origin coords: + lua_pushnumber(tolua_S, self->GetSizeX()); + lua_pushnumber(tolua_S, self->GetSizeY()); + lua_pushnumber(tolua_S, self->GetSizeZ()); + return 3; +} + + + + + static int tolua_cBlockArea_LoadFromSchematicFile(lua_State * tolua_S) { // function cBlockArea::LoadFromSchematicFile @@ -2461,7 +2528,9 @@ void ManualBindings::Bind(lua_State * tolua_S) tolua_beginmodule(tolua_S, "cBlockArea"); tolua_function(tolua_S, "GetBlockTypeMeta", tolua_cBlockArea_GetBlockTypeMeta); + tolua_function(tolua_S, "GetOrigin", tolua_cBlockArea_GetOrigin); tolua_function(tolua_S, "GetRelBlockTypeMeta", tolua_cBlockArea_GetRelBlockTypeMeta); + tolua_function(tolua_S, "GetSize", tolua_cBlockArea_GetSize); tolua_function(tolua_S, "LoadFromSchematicFile", tolua_cBlockArea_LoadFromSchematicFile); tolua_function(tolua_S, "SaveToSchematicFile", tolua_cBlockArea_SaveToSchematicFile); tolua_endmodule(tolua_S); @@ -2511,6 +2580,10 @@ void ManualBindings::Bind(lua_State * tolua_S) tolua_function(tolua_S, "UpdateSign", tolua_cWorld_SetSignLines); tolua_endmodule(tolua_S); + tolua_beginmodule(tolua_S, "cMapManager"); + tolua_function(tolua_S, "DoWithMap", tolua_DoWithID<cMapManager, cMap, &cMapManager::DoWithMap>); + tolua_endmodule(tolua_S); + tolua_beginmodule(tolua_S, "cPlugin"); tolua_function(tolua_S, "Call", tolua_cPlugin_Call); tolua_endmodule(tolua_S); diff --git a/src/BlockArea.cpp b/src/BlockArea.cpp index 194e2d68a..d07ef747a 100644 --- a/src/BlockArea.cpp +++ b/src/BlockArea.cpp @@ -8,6 +8,7 @@ #include "BlockArea.h" #include "OSSupport/GZipFile.h" #include "Blocks/BlockHandler.h" +#include "Cuboid.h" @@ -264,6 +265,15 @@ void cBlockArea::SetOrigin(int a_OriginX, int a_OriginY, int a_OriginZ) +void cBlockArea::SetOrigin(const Vector3i & a_Origin) +{ + SetOrigin(a_Origin.x, a_Origin.y, a_Origin.z); +} + + + + + bool cBlockArea::Read(cForEachChunkProvider * a_ForEachChunkProvider, int a_MinBlockX, int a_MaxBlockX, int a_MinBlockY, int a_MaxBlockY, int a_MinBlockZ, int a_MaxBlockZ, int a_DataTypes) { // Normalize the coords: @@ -338,6 +348,36 @@ bool cBlockArea::Read(cForEachChunkProvider * a_ForEachChunkProvider, int a_MinB +bool cBlockArea::Read(cForEachChunkProvider * a_ForEachChunkProvider, const cCuboid & a_Bounds, int a_DataTypes) +{ + return Read( + a_ForEachChunkProvider, + a_Bounds.p1.x, a_Bounds.p2.x, + a_Bounds.p1.y, a_Bounds.p2.y, + a_Bounds.p1.z, a_Bounds.p2.z, + a_DataTypes + ); +} + + + + + +bool cBlockArea::Read(cForEachChunkProvider * a_ForEachChunkProvider, const Vector3i & a_Point1, const Vector3i & a_Point2, int a_DataTypes) +{ + return Read( + a_ForEachChunkProvider, + a_Point1.x, a_Point2.x, + a_Point1.y, a_Point2.y, + a_Point1.z, a_Point2.z, + a_DataTypes + ); +} + + + + + bool cBlockArea::Write(cForEachChunkProvider * a_ForEachChunkProvider, int a_MinBlockX, int a_MinBlockY, int a_MinBlockZ, int a_DataTypes) { ASSERT((a_DataTypes & GetDataTypes()) == a_DataTypes); // Are you requesting only the data that I have? @@ -362,6 +402,19 @@ bool cBlockArea::Write(cForEachChunkProvider * a_ForEachChunkProvider, int a_Min +bool cBlockArea::Write(cForEachChunkProvider * a_ForEachChunkProvider, const Vector3i & a_MinCoords, int a_DataTypes) +{ + return Write( + a_ForEachChunkProvider, + a_MinCoords.x, a_MinCoords.y, a_MinCoords.z, + a_DataTypes + ); +} + + + + + void cBlockArea::CopyTo(cBlockArea & a_Into) const { if (&a_Into == this) @@ -643,6 +696,15 @@ void cBlockArea::Merge(const cBlockArea & a_Src, int a_RelX, int a_RelY, int a_R +void cBlockArea::Merge(const cBlockArea & a_Src, const Vector3i & a_RelMinCoords, eMergeStrategy a_Strategy) +{ + Merge(a_Src, a_RelMinCoords.x, a_RelMinCoords.y, a_RelMinCoords.z, a_Strategy); +} + + + + + void cBlockArea::Fill(int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, NIBBLETYPE a_BlockLight, NIBBLETYPE a_BlockSkyLight) { if ((a_DataTypes & GetDataTypes()) != a_DataTypes) @@ -735,6 +797,23 @@ void cBlockArea::FillRelCuboid(int a_MinRelX, int a_MaxRelX, int a_MinRelY, int +void cBlockArea::FillRelCuboid(const cCuboid & a_RelCuboid, + int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, + NIBBLETYPE a_BlockLight, NIBBLETYPE a_BlockSkyLight +) +{ + FillRelCuboid( + a_RelCuboid.p1.x, a_RelCuboid.p2.x, + a_RelCuboid.p1.y, a_RelCuboid.p2.y, + a_RelCuboid.p1.z, a_RelCuboid.p2.z, + a_DataTypes, a_BlockType, a_BlockMeta, a_BlockLight, a_BlockSkyLight + ); +} + + + + + void cBlockArea::RelLine(int a_RelX1, int a_RelY1, int a_RelZ1, int a_RelX2, int a_RelY2, int a_RelZ2, int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, NIBBLETYPE a_BlockLight, NIBBLETYPE a_BlockSkyLight @@ -852,6 +931,22 @@ void cBlockArea::RelLine(int a_RelX1, int a_RelY1, int a_RelZ1, int a_RelX2, int +void cBlockArea::RelLine(const Vector3i & a_Point1, const Vector3i & a_Point2, + int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, + NIBBLETYPE a_BlockLight, NIBBLETYPE a_BlockSkyLight +) +{ + RelLine( + a_Point1.x, a_Point1.y, a_Point1.z, + a_Point2.x, a_Point2.y, a_Point2.z, + a_DataTypes, a_BlockType, a_BlockMeta, a_BlockLight, a_BlockSkyLight + ); +} + + + + + void cBlockArea::RotateCCW(void) { if (!HasBlockTypes()) @@ -878,7 +973,7 @@ void cBlockArea::RotateCCW(void) int NewX = z; for (int y = 0; y < m_SizeY; y++) { - int NewIdx = NewX + NewZ * m_SizeX + y * m_SizeX * m_SizeZ; + int NewIdx = NewX + NewZ * m_SizeZ + y * m_SizeX * m_SizeZ; int OldIdx = MakeIndex(x, y, z); NewTypes[NewIdx] = m_BlockTypes[OldIdx]; NewMetas[NewIdx] = BlockHandler(m_BlockTypes[OldIdx])->MetaRotateCCW(m_BlockMetas[OldIdx]); @@ -923,7 +1018,7 @@ void cBlockArea::RotateCW(void) int NewX = m_SizeZ - z - 1; for (int y = 0; y < m_SizeY; y++) { - int NewIdx = NewX + NewZ * m_SizeX + y * m_SizeX * m_SizeZ; + int NewIdx = NewX + NewZ * m_SizeZ + y * m_SizeX * m_SizeZ; int OldIdx = MakeIndex(x, y, z); NewTypes[NewIdx] = m_BlockTypes[OldIdx]; NewMetas[NewIdx] = BlockHandler(m_BlockTypes[OldIdx])->MetaRotateCW(m_BlockMetas[OldIdx]); @@ -1075,7 +1170,7 @@ void cBlockArea::RotateCCWNoMeta(void) int NewX = z; for (int y = 0; y < m_SizeY; y++) { - NewTypes[NewX + NewZ * m_SizeX + y * m_SizeX * m_SizeZ] = m_BlockTypes[MakeIndex(x, y, z)]; + NewTypes[NewX + NewZ * m_SizeZ + y * m_SizeX * m_SizeZ] = m_BlockTypes[MakeIndex(x, y, z)]; } // for y } // for z } // for x @@ -1093,7 +1188,7 @@ void cBlockArea::RotateCCWNoMeta(void) int NewX = z; for (int y = 0; y < m_SizeY; y++) { - NewMetas[NewX + NewZ * m_SizeX + y * m_SizeX * m_SizeZ] = m_BlockMetas[MakeIndex(x, y, z)]; + NewMetas[NewX + NewZ * m_SizeZ + y * m_SizeX * m_SizeZ] = m_BlockMetas[MakeIndex(x, y, z)]; } // for y } // for z } // for x @@ -1120,7 +1215,7 @@ void cBlockArea::RotateCWNoMeta(void) int NewZ = x; for (int y = 0; y < m_SizeY; y++) { - NewTypes[NewX + NewZ * m_SizeX + y * m_SizeX * m_SizeZ] = m_BlockTypes[MakeIndex(x, y, z)]; + NewTypes[NewX + NewZ * m_SizeZ + y * m_SizeX * m_SizeZ] = m_BlockTypes[MakeIndex(x, y, z)]; } // for y } // for x } // for z @@ -1138,7 +1233,7 @@ void cBlockArea::RotateCWNoMeta(void) int NewZ = x; for (int y = 0; y < m_SizeY; y++) { - NewMetas[NewX + NewZ * m_SizeX + y * m_SizeX * m_SizeZ] = m_BlockMetas[MakeIndex(x, y, z)]; + NewMetas[NewX + NewZ * m_SizeZ + y * m_SizeX * m_SizeZ] = m_BlockMetas[MakeIndex(x, y, z)]; } // for y } // for x } // for z diff --git a/src/BlockArea.h b/src/BlockArea.h index b4a161f32..0703f195e 100644 --- a/src/BlockArea.h +++ b/src/BlockArea.h @@ -15,6 +15,16 @@ #include "ForEachChunkProvider.h" + + +// fwd: +class cCuboid; +class Vector3i; + + + + + // tolua_begin class cBlockArea { @@ -24,7 +34,7 @@ class cBlockArea public: - /// What data is to be queried (bit-mask) + /** What data is to be queried (bit-mask) */ enum { baTypes = 1, @@ -44,7 +54,7 @@ public: cBlockArea(void); ~cBlockArea(); - /// Clears the data stored to reclaim memory + /** Clears the data stored to reclaim memory */ void Clear(void); /** Creates a new area of the specified size and contents. @@ -53,31 +63,43 @@ public: */ void Create(int a_SizeX, int a_SizeY, int a_SizeZ, int a_DataTypes = baTypes | baMetas); - /// Resets the origin. No other changes are made, contents are untouched. + /** Resets the origin. No other changes are made, contents are untouched. */ void SetOrigin(int a_OriginX, int a_OriginY, int a_OriginZ); - /// Reads an area of blocks specified. Returns true if successful. All coords are inclusive. + /** Resets the origin. No other changes are made, contents are untouched. */ + void SetOrigin(const Vector3i & a_Origin); + + /** Reads an area of blocks specified. Returns true if successful. All coords are inclusive. */ bool Read(cForEachChunkProvider * a_ForEachChunkProvider, int a_MinBlockX, int a_MaxBlockX, int a_MinBlockY, int a_MaxBlockY, int a_MinBlockZ, int a_MaxBlockZ, int a_DataTypes = baTypes | baMetas); + /** Reads an area of blocks specified. Returns true if successful. The bounds are included in the read area. */ + bool Read(cForEachChunkProvider * a_ForEachChunkProvider, const cCuboid & a_Bounds, int a_DataTypes = baTypes | baMetas); + + /** Reads an area of blocks specified. Returns true if successful. The bounds are included in the read area. */ + bool Read(cForEachChunkProvider * a_ForEachChunkProvider, const Vector3i & a_Point1, const Vector3i & a_Point2, int a_DataTypes = baTypes | baMetas); + // TODO: Write() is not too good an interface: if it fails, there's no way to repeat only for the parts that didn't write // A better way may be to return a list of cBlockAreas for each part that didn't succeed writing, so that the caller may try again - /// Writes the area back into cWorld at the coords specified. Returns true if successful in all chunks, false if only partially / not at all + /** Writes the area back into cWorld at the coords specified. Returns true if successful in all chunks, false if only partially / not at all */ bool Write(cForEachChunkProvider * a_ForEachChunkProvider, int a_MinBlockX, int a_MinBlockY, int a_MinBlockZ, int a_DataTypes = baTypes | baMetas); - /// Copies this object's contents into the specified BlockArea. + /** Writes the area back into cWorld at the coords specified. Returns true if successful in all chunks, false if only partially / not at all */ + bool Write(cForEachChunkProvider * a_ForEachChunkProvider, const Vector3i & a_MinCoords, int a_DataTypes = baTypes | baMetas); + + /** Copies this object's contents into the specified BlockArea. */ void CopyTo(cBlockArea & a_Into) const; - /// Copies the contents from the specified BlockArea into this object. + /** Copies the contents from the specified BlockArea into this object. */ void CopyFrom(const cBlockArea & a_From); - /// For testing purposes only, dumps the area into a file. + /** For testing purposes only, dumps the area into a file. */ void DumpToRawFile(const AString & a_FileName); - /// Crops the internal contents by the specified amount of blocks from each border. + /** Crops the internal contents by the specified amount of blocks from each border. */ void Crop(int a_AddMinX, int a_SubMaxX, int a_AddMinY, int a_SubMaxY, int a_AddMinZ, int a_SubMaxZ); - /// Expands the internal contents by the specified amount of blocks from each border + /** Expands the internal contents by the specified amount of blocks from each border */ void Expand(int a_SubMinX, int a_AddMaxX, int a_SubMinY, int a_AddMaxY, int a_SubMinZ, int a_AddMaxZ); /** Merges another block area into this one, using the specified block combinating strategy @@ -117,49 +139,65 @@ public: */ void Merge(const cBlockArea & a_Src, int a_RelX, int a_RelY, int a_RelZ, eMergeStrategy a_Strategy); - /// Fills the entire block area with the specified data + /** Merges another block area into this one, using the specified block combinating strategy. + See Merge() above for details. */ + void Merge(const cBlockArea & a_Src, const Vector3i & a_RelMinCoords, eMergeStrategy a_Strategy); + + /** Fills the entire block area with the specified data */ void Fill(int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta = 0, NIBBLETYPE a_BlockLight = 0, NIBBLETYPE a_BlockSkyLight = 0x0f); - /// Fills a cuboid inside the block area with the specified data + /** Fills a cuboid inside the block area with the specified data */ void FillRelCuboid(int a_MinRelX, int a_MaxRelX, int a_MinRelY, int a_MaxRelY, int a_MinRelZ, int a_MaxRelZ, int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta = 0, NIBBLETYPE a_BlockLight = 0, NIBBLETYPE a_BlockSkyLight = 0x0f ); - /// Draws a line from between two points with the specified data + /** Fills a cuboid inside the block area with the specified data. a_Cuboid must be sorted. */ + void FillRelCuboid(const cCuboid & a_RelCuboid, + int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta = 0, + NIBBLETYPE a_BlockLight = 0, NIBBLETYPE a_BlockSkyLight = 0x0f + ); + + /** Draws a line from between two points with the specified data */ void RelLine(int a_RelX1, int a_RelY1, int a_RelZ1, int a_RelX2, int a_RelY2, int a_RelZ2, int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta = 0, NIBBLETYPE a_BlockLight = 0, NIBBLETYPE a_BlockSkyLight = 0x0f ); - /// Rotates the entire area counter-clockwise around the Y axis + /** Draws a line from between two points with the specified data */ + void RelLine(const Vector3i & a_Point1, const Vector3i & a_Point2, + int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta = 0, + NIBBLETYPE a_BlockLight = 0, NIBBLETYPE a_BlockSkyLight = 0x0f + ); + + /** Rotates the entire area counter-clockwise around the Y axis */ void RotateCCW(void); - /// Rotates the entire area clockwise around the Y axis + /** Rotates the entire area clockwise around the Y axis */ void RotateCW(void); - /// Mirrors the entire area around the XY plane + /** Mirrors the entire area around the XY plane */ void MirrorXY(void); - /// Mirrors the entire area around the XZ plane + /** Mirrors the entire area around the XZ plane */ void MirrorXZ(void); - /// Mirrors the entire area around the YZ plane + /** Mirrors the entire area around the YZ plane */ void MirrorYZ(void); - /// Rotates the entire area counter-clockwise around the Y axis, doesn't use blockhandlers for block meta + /** Rotates the entire area counter-clockwise around the Y axis, doesn't use blockhandlers for block meta */ void RotateCCWNoMeta(void); - /// Rotates the entire area clockwise around the Y axis, doesn't use blockhandlers for block meta + /** Rotates the entire area clockwise around the Y axis, doesn't use blockhandlers for block meta */ void RotateCWNoMeta(void); - /// Mirrors the entire area around the XY plane, doesn't use blockhandlers for block meta + /** Mirrors the entire area around the XY plane, doesn't use blockhandlers for block meta */ void MirrorXYNoMeta(void); - /// Mirrors the entire area around the XZ plane, doesn't use blockhandlers for block meta + /** Mirrors the entire area around the XZ plane, doesn't use blockhandlers for block meta */ void MirrorXZNoMeta(void); - /// Mirrors the entire area around the YZ plane, doesn't use blockhandlers for block meta + /** Mirrors the entire area around the YZ plane, doesn't use blockhandlers for block meta */ void MirrorYZNoMeta(void); // Setters: @@ -197,11 +235,14 @@ public: int GetSizeY(void) const { return m_SizeY; } int GetSizeZ(void) const { return m_SizeZ; } + /** Returns the volume of the area, as number of blocks */ + int GetVolume(void) const { return m_SizeX * m_SizeY * m_SizeZ; } + int GetOriginX(void) const { return m_OriginX; } int GetOriginY(void) const { return m_OriginY; } int GetOriginZ(void) const { return m_OriginZ; } - /// Returns the datatypes that are stored in the object (bitmask of baXXX values) + /** Returns the datatypes that are stored in the object (bitmask of baXXX values) */ int GetDataTypes(void) const; bool HasBlockTypes (void) const { return (m_BlockTypes != NULL); } @@ -212,7 +253,7 @@ public: // tolua_end // Clients can use these for faster access to all blocktypes. Be careful though! - /// Returns the internal pointer to the block types + /** Returns the internal pointer to the block types */ BLOCKTYPE * GetBlockTypes (void) const { return m_BlockTypes; } NIBBLETYPE * GetBlockMetas (void) const { return m_BlockMetas; } // NOTE: one byte per block! NIBBLETYPE * GetBlockLight (void) const { return m_BlockLight; } // NOTE: one byte per block! @@ -263,7 +304,7 @@ protected: NIBBLETYPE * m_BlockLight; // Each light value is stored as a separate byte for faster access NIBBLETYPE * m_BlockSkyLight; // Each light value is stored as a separate byte for faster access - /// Clears the data stored and prepares a fresh new block area with the specified dimensions + /** Clears the data stored and prepares a fresh new block area with the specified dimensions */ bool SetSize(int a_SizeX, int a_SizeY, int a_SizeZ, int a_DataTypes); // Basic Setters: @@ -282,7 +323,7 @@ protected: void ExpandBlockTypes(int a_SubMinX, int a_AddMaxX, int a_SubMinY, int a_AddMaxY, int a_SubMinZ, int a_AddMaxZ); void ExpandNibbles (NIBBLEARRAY & a_Array, int a_SubMinX, int a_AddMaxX, int a_SubMinY, int a_AddMaxY, int a_SubMinZ, int a_AddMaxZ); - /// Sets the specified datatypes at the specified location. + /** Sets the specified datatypes at the specified location. */ void RelSetData( int a_RelX, int a_RelY, int a_RelZ, int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, diff --git a/src/BlockEntities/CommandBlockEntity.cpp b/src/BlockEntities/CommandBlockEntity.cpp index 0bc6ca359..d395997a6 100644 --- a/src/BlockEntities/CommandBlockEntity.cpp +++ b/src/BlockEntities/CommandBlockEntity.cpp @@ -12,6 +12,7 @@ #include "../CommandOutput.h" #include "../Root.h" #include "../Server.h" // ExecuteConsoleCommand() +#include "../Chunk.h" @@ -126,6 +127,8 @@ void cCommandBlockEntity::SetRedstonePower(bool a_IsPowered) bool cCommandBlockEntity::Tick(float a_Dt, cChunk & a_Chunk) { + UNUSED(a_Dt); + UNUSED(a_Chunk); if (!m_ShouldExecute) { return false; diff --git a/src/BlockEntities/DropSpenserEntity.cpp b/src/BlockEntities/DropSpenserEntity.cpp index 81df0fc8c..0012742fb 100644 --- a/src/BlockEntities/DropSpenserEntity.cpp +++ b/src/BlockEntities/DropSpenserEntity.cpp @@ -129,6 +129,7 @@ void cDropSpenserEntity::SetRedstonePower(bool a_IsPowered) bool cDropSpenserEntity::Tick(float a_Dt, cChunk & a_Chunk) { + UNUSED(a_Dt); if (!m_ShouldDropSpense) { return false; diff --git a/src/BlockEntities/FurnaceEntity.cpp b/src/BlockEntities/FurnaceEntity.cpp index c6643bcff..7d6d1f89e 100644 --- a/src/BlockEntities/FurnaceEntity.cpp +++ b/src/BlockEntities/FurnaceEntity.cpp @@ -94,6 +94,8 @@ bool cFurnaceEntity::ContinueCooking(void) bool cFurnaceEntity::Tick(float a_Dt, cChunk & a_Chunk) { + UNUSED(a_Dt); + UNUSED(a_Chunk); if (m_FuelBurnTime <= 0) { // No fuel is burning, reset progressbars and bail out @@ -110,7 +112,7 @@ bool cFurnaceEntity::Tick(float a_Dt, cChunk & a_Chunk) if (m_TimeCooked >= m_NeedCookTime) { // Finished smelting one item - FinishOne(a_Chunk); + FinishOne(); } } @@ -208,7 +210,7 @@ void cFurnaceEntity::BroadcastProgress(int a_ProgressbarID, short a_Value) /// One item finished cooking -void cFurnaceEntity::FinishOne(cChunk & a_Chunk) +void cFurnaceEntity::FinishOne() { m_TimeCooked = 0; diff --git a/src/BlockEntities/FurnaceEntity.h b/src/BlockEntities/FurnaceEntity.h index 5e08ae37a..4f935a74b 100644 --- a/src/BlockEntities/FurnaceEntity.h +++ b/src/BlockEntities/FurnaceEntity.h @@ -126,7 +126,7 @@ protected: void BroadcastProgress(int a_ProgressbarID, short a_Value); /// One item finished cooking - void FinishOne(cChunk & a_Chunk); + void FinishOne(); /// Starts burning a new fuel, if possible void BurnNewFuel(void); diff --git a/src/BlockEntities/HopperEntity.cpp b/src/BlockEntities/HopperEntity.cpp index 31b23ac99..af7043767 100644 --- a/src/BlockEntities/HopperEntity.cpp +++ b/src/BlockEntities/HopperEntity.cpp @@ -13,6 +13,7 @@ #include "DropSpenserEntity.h" #include "FurnaceEntity.h" #include "../BoundingBox.h" +#include "json/json.h" @@ -58,6 +59,7 @@ bool cHopperEntity::GetOutputBlockPos(NIBBLETYPE a_BlockMeta, int & a_OutputX, i bool cHopperEntity::Tick(float a_Dt, cChunk & a_Chunk) { + UNUSED(a_Dt); Int64 CurrentTick = a_Chunk.GetWorld()->GetWorldAge(); bool res = false; @@ -73,6 +75,7 @@ bool cHopperEntity::Tick(float a_Dt, cChunk & a_Chunk) void cHopperEntity::SaveToJson(Json::Value & a_Value) { + UNUSED(a_Value); // TODO LOGWARNING("%s: Not implemented yet", __FUNCTION__); } diff --git a/src/BlockEntities/NoteEntity.cpp b/src/BlockEntities/NoteEntity.cpp index 9a33ead62..58b05a324 100644 --- a/src/BlockEntities/NoteEntity.cpp +++ b/src/BlockEntities/NoteEntity.cpp @@ -21,6 +21,7 @@ cNoteEntity::cNoteEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_Wo void cNoteEntity::UsedBy(cPlayer * a_Player) { + UNUSED(a_Player); IncrementPitch(); MakeSound(); } diff --git a/src/Blocks/BlockBed.cpp b/src/Blocks/BlockBed.cpp index a6f3c36b6..3dad4feba 100644 --- a/src/Blocks/BlockBed.cpp +++ b/src/Blocks/BlockBed.cpp @@ -63,20 +63,29 @@ void cBlockBedHandler::OnUse(cChunkInterface & a_ChunkInterface, cWorldInterface if (a_WorldInterface.GetTimeOfDay() > 13000) { NIBBLETYPE Meta = a_ChunkInterface.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); - if (Meta & 0x8) + if (Meta & 0x4) { - // Is pillow - a_WorldInterface.GetBroadcastManager().BroadcastUseBed(*a_Player, a_BlockX, a_BlockY, a_BlockZ); + a_Player->SendMessageFailure("This bed is occupied."); } else { - // Is foot end - Vector3i Direction = MetaDataToDirection( Meta & 0x7 ); - if (a_ChunkInterface.GetBlock(a_BlockX + Direction.x, a_BlockY, a_BlockZ + Direction.z) == E_BLOCK_BED) // Must always use pillow location for sleeping + if (Meta & 0x8) { - a_WorldInterface.GetBroadcastManager().BroadcastUseBed(*a_Player, a_BlockX + Direction.x, a_BlockY, a_BlockZ + Direction.z); + // Is pillow + a_WorldInterface.GetBroadcastManager().BroadcastUseBed(*a_Player, a_BlockX, a_BlockY, a_BlockZ); } + else + { + // Is foot end + Vector3i Direction = MetaDataToDirection( Meta & 0x7 ); + if (a_ChunkInterface.GetBlock(a_BlockX + Direction.x, a_BlockY, a_BlockZ + Direction.z) == E_BLOCK_BED) // Must always use pillow location for sleeping + { + a_WorldInterface.GetBroadcastManager().BroadcastUseBed(*a_Player, a_BlockX + Direction.x, a_BlockY, a_BlockZ + Direction.z); + } + } + a_ChunkInterface.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, (Meta | (1 << 2))); } + } else { a_Player->SendMessageFailure("You can only sleep at night"); } @@ -86,3 +95,5 @@ void cBlockBedHandler::OnUse(cChunkInterface & a_ChunkInterface, cWorldInterface + + diff --git a/src/Chunk.cpp b/src/Chunk.cpp index 4f301c209..8dfbbeef5 100644 --- a/src/Chunk.cpp +++ b/src/Chunk.cpp @@ -562,13 +562,6 @@ void cChunk::Tick(float a_Dt) { BroadcastPendingBlockChanges(); - // Unload the chunk from all clients that have queued unloading: - for (cClientHandleList::iterator itr = m_UnloadQuery.begin(), end = m_UnloadQuery.end(); itr != end; ++itr) - { - (*itr)->SendUnloadChunk(m_PosX, m_PosZ); - } - m_UnloadQuery.clear(); - // Set all blocks that have been queued for setting later: ProcessQueuedSetBlocks(); diff --git a/src/Chunk.h b/src/Chunk.h index 1b7a6fa07..c9e9697ca 100644 --- a/src/Chunk.h +++ b/src/Chunk.h @@ -405,7 +405,6 @@ private: // A critical section is not needed, because all chunk access is protected by its parent ChunkMap's csLayers cClientHandleList m_LoadedByClient; - cClientHandleList m_UnloadQuery; cEntityList m_Entities; cBlockEntityList m_BlockEntities; diff --git a/src/ChunkDef.h b/src/ChunkDef.h index f48dc4fd5..7be2fa2df 100644 --- a/src/ChunkDef.h +++ b/src/ChunkDef.h @@ -92,6 +92,7 @@ public: /// Converts absolute block coords into relative (chunk + block) coords: inline static void AbsoluteToRelative(/* in-out */ int & a_X, int & a_Y, int & a_Z, /* out */ int & a_ChunkX, int & a_ChunkZ ) { + UNUSED(a_Y); BlockToChunk(a_X, a_Z, a_ChunkX, a_ChunkZ); a_X = a_X - a_ChunkX * Width; diff --git a/src/ChunkMap.cpp b/src/ChunkMap.cpp index fbb8706e0..b5795fbaf 100644 --- a/src/ChunkMap.cpp +++ b/src/ChunkMap.cpp @@ -805,6 +805,10 @@ void cChunkMap::WakeUpSimulators(int a_BlockX, int a_BlockY, int a_BlockZ) /// Wakes up the simulators for the specified area of blocks void cChunkMap::WakeUpSimulatorsInArea(int a_MinBlockX, int a_MaxBlockX, int a_MinBlockY, int a_MaxBlockY, int a_MinBlockZ, int a_MaxBlockZ) { + // Limit the Y coords: + a_MinBlockY = std::max(a_MinBlockY, 0); + a_MaxBlockY = std::min(a_MaxBlockY, cChunkDef::Height - 1); + cSimulatorManager * SimMgr = m_World->GetSimulatorManager(); int MinChunkX, MinChunkZ, MaxChunkX, MaxChunkZ; cChunkDef::BlockToChunk(a_MinBlockX, a_MinBlockZ, MinChunkX, MinChunkZ); diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp index 84286fc41..b08ceb5f6 100644 --- a/src/ClientHandle.cpp +++ b/src/ClientHandle.cpp @@ -32,6 +32,7 @@ #include "Protocol/ProtocolRecognizer.h" #include "CompositeChat.h" +#include "Items/ItemSword.h" @@ -542,19 +543,23 @@ void cClientHandle::HandlePlayerPos(double a_PosX, double a_PosY, double a_PosZ, void cClientHandle::HandlePluginMessage(const AString & a_Channel, const AString & a_Message) { - if (a_Channel == "MC|AdvCdm") // Command block, set text, Client -> Server + if (a_Channel == "MC|AdvCdm") { - const char* Data = a_Message.c_str(); - HandleCommandBlockMessage(Data, a_Message.size()); - return; + // Command block, set text, Client -> Server + HandleCommandBlockMessage(a_Message.c_str(), a_Message.size()); } - else if (a_Channel == "MC|Brand") // Client <-> Server branding exchange + else if (a_Channel == "MC|Brand") { - // We are custom, - // We are awesome, - // We are MCServer. + // Client <-> Server branding exchange SendPluginMessage("MC|Brand", "MCServer"); - return; + } + else if (a_Channel == "REGISTER") + { + RegisterPluginChannels(BreakApartPluginChannels(a_Message)); + } + else if (a_Channel == "UNREGISTER") + { + UnregisterPluginChannels(BreakApartPluginChannels(a_Message)); } cPluginManager::Get()->CallHookPluginMessage(*this, a_Channel, a_Message); @@ -564,7 +569,61 @@ void cClientHandle::HandlePluginMessage(const AString & a_Channel, const AString -void cClientHandle::HandleCommandBlockMessage(const char* a_Data, unsigned int a_Length) +AStringVector cClientHandle::BreakApartPluginChannels(const AString & a_PluginChannels) +{ + // Break the string on each NUL character. + // Note that StringSplit() doesn't work on this because NUL is a special char - string terminator + size_t len = a_PluginChannels.size(); + size_t first = 0; + AStringVector res; + for (size_t i = 0; i < len; i++) + { + if (a_PluginChannels[i] != 0) + { + continue; + } + if (i > first) + { + res.push_back(a_PluginChannels.substr(first, i - first)); + } + first = i + 1; + } // for i - a_PluginChannels[] + if (first < len) + { + res.push_back(a_PluginChannels.substr(first, len - first)); + } + return res; +} + + + + + +void cClientHandle::RegisterPluginChannels(const AStringVector & a_ChannelList) +{ + for (AStringVector::const_iterator itr = a_ChannelList.begin(), end = a_ChannelList.end(); itr != end; ++itr) + { + m_PluginChannels.insert(*itr); + } // for itr - a_ChannelList[] +} + + + + + +void cClientHandle::UnregisterPluginChannels(const AStringVector & a_ChannelList) +{ + for (AStringVector::const_iterator itr = a_ChannelList.begin(), end = a_ChannelList.end(); itr != end; ++itr) + { + m_PluginChannels.erase(*itr); + } // for itr - a_ChannelList[] +} + + + + + +void cClientHandle::HandleCommandBlockMessage(const char * a_Data, unsigned int a_Length) { if (a_Length < 14) { @@ -736,6 +795,15 @@ void cClientHandle::HandleBlockDigStarted(int a_BlockX, int a_BlockY, int a_Bloc return; } + if ( + m_Player->IsGameModeCreative() && + ItemCategory::IsSword(m_Player->GetInventory().GetEquippedItem().m_ItemType) + ) + { + // Players can't destroy blocks with a Sword in the hand. + return; + } + if (cRoot::Get()->GetPluginManager()->CallHookPlayerBreakingBlock(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_OldBlock, a_OldMeta)) { // A plugin doesn't agree with the breaking. Bail out. Send the block back to the client, so that it knows: @@ -2074,6 +2142,33 @@ void cClientHandle::SendInventorySlot(char a_WindowID, short a_SlotNum, const cI +void cClientHandle::SendMapColumn(int a_ID, int a_X, int a_Y, const Byte * a_Colors, unsigned int a_Length) +{ + m_Protocol->SendMapColumn(a_ID, a_X, a_Y, a_Colors, a_Length); +} + + + + + +void cClientHandle::SendMapDecorators(int a_ID, const cMapDecoratorList & a_Decorators) +{ + m_Protocol->SendMapDecorators(a_ID, a_Decorators); +} + + + + + +void cClientHandle::SendMapInfo(int a_ID, unsigned int a_Scale) +{ + m_Protocol->SendMapInfo(a_ID, a_Scale); +} + + + + + void cClientHandle::SendParticleEffect(const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmmount) { m_Protocol->SendParticleEffect(a_ParticleName, a_SrcX, a_SrcY, a_SrcZ, a_OffsetX, a_OffsetY, a_OffsetZ, a_ParticleData, a_ParticleAmmount); @@ -2463,6 +2558,15 @@ void cClientHandle::SetViewDistance(int a_ViewDistance) +bool cClientHandle::HasPluginChannel(const AString & a_PluginChannel) +{ + return (m_PluginChannels.find(a_PluginChannel) != m_PluginChannels.end()); +} + + + + + bool cClientHandle::WantsSendChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ) { if (m_State >= csDestroying) diff --git a/src/ClientHandle.h b/src/ClientHandle.h index aefca7233..194533402 100644 --- a/src/ClientHandle.h +++ b/src/ClientHandle.h @@ -17,6 +17,7 @@ #include "ChunkDef.h" #include "ByteBuffer.h" #include "Scoreboard.h" +#include "Map.h" @@ -72,10 +73,10 @@ public: inline bool IsLoggedIn(void) const { return (m_State >= csAuthenticating); } - /// Called while the client is being ticked from the world via its cPlayer object + /** Called while the client is being ticked from the world via its cPlayer object */ void Tick(float a_Dt); - /// Called while the client is being ticked from the cServer object + /** Called while the client is being ticked from the cServer object */ void ServerTick(float a_Dt); void Destroy(void); @@ -112,6 +113,9 @@ public: void SendGameMode (eGameMode a_GameMode); void SendHealth (void); void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item); + void SendMapColumn (int a_ID, int a_X, int a_Y, const Byte * a_Colors, unsigned int a_Length); + void SendMapDecorators (int a_ID, const cMapDecoratorList & a_Decorators); + void SendMapInfo (int a_ID, unsigned int a_Scale); void SendPaintingSpawn (const cPainting & a_Painting); void SendPickupSpawn (const cPickup & a_Pickup); void SendEntityAnimation (const cEntity & a_Entity, char a_Animation); @@ -150,23 +154,28 @@ public: void SendWindowOpen (const cWindow & a_Window); void SendWindowProperty (const cWindow & a_Window, int a_Property, int a_Value); - const AString & GetUsername(void) const; // tolua_export - void SetUsername( const AString & a_Username ); // tolua_export + // tolua_begin + const AString & GetUsername(void) const; + void SetUsername( const AString & a_Username ); - inline short GetPing(void) const { return m_Ping; } // tolua_export + inline short GetPing(void) const { return m_Ping; } - void SetViewDistance(int a_ViewDistance); // tolua_export - int GetViewDistance(void) const { return m_ViewDistance; } // tolua_export + void SetViewDistance(int a_ViewDistance); + int GetViewDistance(void) const { return m_ViewDistance; } - void SetLocale(AString & a_Locale) { m_Locale = a_Locale; } // tolua_export - AString GetLocale(void) const { return m_Locale; } // tolua_export + void SetLocale(AString & a_Locale) { m_Locale = a_Locale; } + AString GetLocale(void) const { return m_Locale; } - int GetUniqueID() const { return m_UniqueID; } // tolua_export + int GetUniqueID(void) const { return m_UniqueID; } - /// Returns true if the client wants the chunk specified to be sent (in m_ChunksToSend) + bool HasPluginChannel(const AString & a_PluginChannel); + + // tolua_end + + /** Returns true if the client wants the chunk specified to be sent (in m_ChunksToSend) */ bool WantsSendChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ); - /// Adds the chunk specified to the list of chunks wanted for sending (m_ChunksToSend) + /** Adds the chunk specified to the list of chunks wanted for sending (m_ChunksToSend) */ void AddWantedChunk(int a_ChunkX, int a_ChunkZ); // Calls that cProtocol descendants use to report state: @@ -217,14 +226,17 @@ public: void SendData(const char * a_Data, int a_Size); - /// Called when the player moves into a different world; queues sreaming the new chunks + /** Called when the player moves into a different world; queues sreaming the new chunks */ void MoveToWorld(cWorld & a_World, bool a_SendRespawnPacket); - /// Handles the block placing packet when it is a real block placement (not block-using, item-using or eating) + /** Handles the block placing packet when it is a real block placement (not block-using, item-using or eating) */ void HandlePlaceBlock(int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, cItemHandler & a_ItemHandler); private: + /** The type used for storing the names of registered plugin channels. */ + typedef std::set<AString> cChannels; + int m_ViewDistance; // Number of chunks the player can see in each direction; 4 is the minimum ( http://wiki.vg/Protocol_FAQ#.E2.80.A6all_connecting_clients_spasm_and_jerk_uncontrollably.21 ) static const int GENERATEDISTANCE = 2; // Server generates this many chunks AHEAD of player sight. 2 is the minimum, since foliage is generated 1 step behind chunk terrain generation @@ -257,7 +269,7 @@ private: int m_LastStreamedChunkX; int m_LastStreamedChunkZ; - /// Seconds since the last packet data was received (updated in Tick(), reset in DataReceived()) + /** Seconds since the last packet data was received (updated in Tick(), reset in DataReceived()) */ float m_TimeSinceLastPacket; short m_Ping; @@ -279,7 +291,7 @@ private: int m_LastDigBlockY; int m_LastDigBlockZ; - /// Used while csDestroyedWaiting for counting the ticks until the connection is closed + /** Used while csDestroyedWaiting for counting the ticks until the connection is closed */ int m_TicksSinceDestruction; enum eState @@ -299,10 +311,10 @@ private: eState m_State; - /// m_State needs to be locked in the Destroy() function so that the destruction code doesn't run twice on two different threads + /** m_State needs to be locked in the Destroy() function so that the destruction code doesn't run twice on two different threads */ cCriticalSection m_CSDestroyingState; - /// If set to true during csDownloadingWorld, the tick thread calls CheckIfWorldDownloaded() + /** If set to true during csDownloadingWorld, the tick thread calls CheckIfWorldDownloaded() */ bool m_ShouldCheckDownloaded; /** Number of explosions sent this tick */ @@ -311,27 +323,39 @@ private: static int s_ClientCount; int m_UniqueID; - /// Set to true when the chunk where the player is is sent to the client. Used for spawning the player + /** Set to true when the chunk where the player is is sent to the client. Used for spawning the player */ bool m_HasSentPlayerChunk; - /// Client Settings + /** Client Settings */ AString m_Locale; + + /** The plugin channels that the client has registered. */ + cChannels m_PluginChannels; - /// Returns true if the rate block interactions is within a reasonable limit (bot protection) + /** Returns true if the rate block interactions is within a reasonable limit (bot protection) */ bool CheckBlockInteractionsRate(void); - /// Adds a single chunk to be streamed to the client; used by StreamChunks() + /** Adds a single chunk to be streamed to the client; used by StreamChunks() */ void StreamChunk(int a_ChunkX, int a_ChunkZ); - /// Handles the DIG_STARTED dig packet: + /** Handles the DIG_STARTED dig packet: */ void HandleBlockDigStarted (int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, BLOCKTYPE a_OldBlock, NIBBLETYPE a_OldMeta); - /// Handles the DIG_FINISHED dig packet: + /** Handles the DIG_FINISHED dig packet: */ void HandleBlockDigFinished(int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, BLOCKTYPE a_OldBlock, NIBBLETYPE a_OldMeta); - /// Handles the "MC|AdvCdm" plugin message - void HandleCommandBlockMessage(const char* a_Data, unsigned int a_Length); + /** Converts the protocol-formatted channel list (NUL-separated) into a proper string vector. */ + AStringVector BreakApartPluginChannels(const AString & a_PluginChannels); + + /** Adds all of the channels to the list of current plugin channels. Handles duplicates gracefully. */ + void RegisterPluginChannels(const AStringVector & a_ChannelList); + + /** Removes all of the channels from the list of current plugin channels. Ignores channels that are not found. */ + void UnregisterPluginChannels(const AStringVector & a_ChannelList); + + /** Handles the "MC|AdvCdm" plugin message */ + void HandleCommandBlockMessage(const char * a_Data, unsigned int a_Length); // cSocketThreads::cCallback overrides: virtual void DataReceived (const char * a_Data, int a_Size) override; // Data is received from the client diff --git a/src/Cuboid.cpp b/src/Cuboid.cpp index ea6f7c453..782837b23 100644 --- a/src/Cuboid.cpp +++ b/src/Cuboid.cpp @@ -58,6 +58,18 @@ void cCuboid::Sort(void) +int cCuboid::GetVolume(void) const +{ + int DifX = abs(p2.x - p1.x) + 1; + int DifY = abs(p2.y - p1.y) + 1; + int DifZ = abs(p2.z - p1.z) + 1; + return DifX * DifY * DifZ; +} + + + + + bool cCuboid::DoesIntersect(const cCuboid & a_Other) const { // In order for cuboids to intersect, each of their coord intervals need to intersect @@ -103,6 +115,76 @@ void cCuboid::Move(int a_OfsX, int a_OfsY, int a_OfsZ) +void cCuboid::Expand(int a_SubMinX, int a_AddMaxX, int a_SubMinY, int a_AddMaxY, int a_SubMinZ, int a_AddMaxZ) +{ + if (p1.x < p2.x) + { + p1.x -= a_SubMinX; + p2.x += a_AddMaxX; + } + else + { + p2.x -= a_SubMinX; + p1.x += a_AddMaxX; + } + + if (p1.y < p2.y) + { + p1.y -= a_SubMinY; + p2.y += a_AddMaxY; + } + else + { + p2.y -= a_SubMinY; + p1.y += a_AddMaxY; + } + + if (p1.z < p2.z) + { + p1.z -= a_SubMinZ; + p2.z += a_AddMaxZ; + } + else + { + p2.z -= a_SubMinZ; + p1.z += a_AddMaxZ; + } +} + + + + + +void cCuboid::ClampX(int a_MinX, int a_MaxX) +{ + p1.x = Clamp(p1.x, a_MinX, a_MaxX); + p2.x = Clamp(p2.x, a_MinX, a_MaxX); +} + + + + + +void cCuboid::ClampY(int a_MinY, int a_MaxY) +{ + p1.y = Clamp(p1.y, a_MinY, a_MaxY); + p2.y = Clamp(p2.y, a_MinY, a_MaxY); +} + + + + + +void cCuboid::ClampZ(int a_MinZ, int a_MaxZ) +{ + p1.z = Clamp(p1.z, a_MinZ, a_MaxZ); + p2.z = Clamp(p2.z, a_MinZ, a_MaxZ); +} + + + + + bool cCuboid::IsSorted(void) const { return ( diff --git a/src/Cuboid.h b/src/Cuboid.h index 44db7b98e..51ccf799b 100644 --- a/src/Cuboid.h +++ b/src/Cuboid.h @@ -29,7 +29,12 @@ public: int DifY(void) const { return p2.y - p1.y; } int DifZ(void) const { return p2.z - p1.z; } - /// Returns true if the cuboids have at least one voxel in common. Both coords are considered inclusive. + /** Returns the volume of the cuboid, in blocks. + Note that the volume considers both coords inclusive. + Works on unsorted cuboids, too. */ + int GetVolume(void) const; + + /** Returns true if the cuboids have at least one voxel in common. Both coords are considered inclusive. */ bool DoesIntersect(const cCuboid & a_Other) const; bool IsInside(const Vector3i & v) const @@ -59,13 +64,27 @@ public: ); } - /// Returns true if this cuboid is completely inside the specifie cuboid (in all 6 coords) + /** Returns true if this cuboid is completely inside the specifie cuboid (in all 6 coords) */ bool IsCompletelyInside(const cCuboid & a_Outer) const; - /// Moves the cuboid by the specified offsets in each direction + /** Moves the cuboid by the specified offsets in each direction */ void Move(int a_OfsX, int a_OfsY, int a_OfsZ); + + /** Expands the cuboid by the specified amount in each direction. + Works on unsorted cuboids as well. + Note that this function doesn't check for underflows. */ + void Expand(int a_SubMinX, int a_AddMaxX, int a_SubMinY, int a_AddMaxY, int a_SubMinZ, int a_AddMaxZ); + + /** Clamps both X coords to the specified range. Works on unsorted cuboids, too. */ + void ClampX(int a_MinX, int a_MaxX); + + /** Clamps both Y coords to the specified range. Works on unsorted cuboids, too. */ + void ClampY(int a_MinY, int a_MaxY); + + /** Clamps both Z coords to the specified range. Works on unsorted cuboids, too. */ + void ClampZ(int a_MinZ, int a_MaxZ); - /// Returns true if the coords are properly sorted (lesser in p1, greater in p2) + /** Returns true if the coords are properly sorted (lesser in p1, greater in p2) */ bool IsSorted(void) const; } ; // tolua_end diff --git a/src/Enchantments.h b/src/Enchantments.h index e984df92e..f77b535d8 100644 --- a/src/Enchantments.h +++ b/src/Enchantments.h @@ -21,7 +21,6 @@ class cParsedNBT; - /** Class that stores item enchantments or stored-enchantments The enchantments may be serialized to a stringspec and read back from such stringspec. The format for the stringspec is "id=lvl;id=lvl;id=lvl...", with an optional semicolon at the end, diff --git a/src/Entities/Boat.cpp b/src/Entities/Boat.cpp index 67df201ce..94b24c5af 100644 --- a/src/Entities/Boat.cpp +++ b/src/Entities/Boat.cpp @@ -89,6 +89,7 @@ void cBoat::Tick(float a_Dt, cChunk & a_Chunk) { super::Tick(a_Dt, a_Chunk); BroadcastMovementUpdate(); + SetSpeed(GetSpeed() * 0.97); // Slowly decrease the speed if ((POSY_TOINT < 0) || (POSY_TOINT > cChunkDef::Height)) @@ -98,7 +99,10 @@ void cBoat::Tick(float a_Dt, cChunk & a_Chunk) if (IsBlockWater(m_World->GetBlock(POSX_TOINT, POSY_TOINT, POSZ_TOINT))) { - SetSpeedY(1); + if (GetSpeedY() < 2) + { + AddSpeedY(0.2); + } } } @@ -108,12 +112,12 @@ void cBoat::Tick(float a_Dt, cChunk & a_Chunk) void cBoat::HandleSpeedFromAttachee(float a_Forward, float a_Sideways) { - if (GetSpeed().Length() > 7) + if (GetSpeed().Length() > 7.5) { return; } - Vector3d ToAddSpeed(m_Attachee->GetLookVector() * (a_Sideways * 1.5)); + Vector3d ToAddSpeed = m_Attachee->GetLookVector() * (a_Sideways * 0.4) ; ToAddSpeed.y = 0; AddSpeed(ToAddSpeed); diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index 19173592e..e0f0b9222 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -254,6 +254,9 @@ void cPlayer::Tick(float a_Dt, cChunk & a_Chunk) HandleFloater(); } + // Update items (e.g. Maps) + m_Inventory.UpdateItems(); + // Send Player List (Once per m_LastPlayerListTime/1000 ms) cTimer t1; if (m_LastPlayerListTime + cPlayer::PLAYER_LIST_TIME_MS <= t1.GetNowTime()) diff --git a/src/Generating/StructGen.cpp b/src/Generating/StructGen.cpp index 4efcf92f0..47945cc2b 100644 --- a/src/Generating/StructGen.cpp +++ b/src/Generating/StructGen.cpp @@ -51,15 +51,6 @@ const int NEST_SIZE_GRAVEL = 32; -template <typename T> T Clamp(T a_Value, T a_Min, T a_Max) -{ - return (a_Value < a_Min) ? a_Min : ((a_Value > a_Max) ? a_Max : a_Value); -} - - - - - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // cStructGenTrees: diff --git a/src/Globals.h b/src/Globals.h index 1e90d83e9..e4737a98a 100644 --- a/src/Globals.h +++ b/src/Globals.h @@ -235,6 +235,16 @@ public: +/** Clamp X to the specified range. */ +template <typename T> +T Clamp(T a_Value, T a_Min, T a_Max) +{ + return (a_Value < a_Min) ? a_Min : ((a_Value > a_Max) ? a_Max : a_Value); +} + + + + // Common headers (part 2, with macros): #include "ChunkDef.h" diff --git a/src/Inventory.cpp b/src/Inventory.cpp index 0e1cedc85..7f434adfd 100644 --- a/src/Inventory.cpp +++ b/src/Inventory.cpp @@ -515,6 +515,31 @@ bool cInventory::AddToBar( cItem & a_Item, const int a_Offset, const int a_Size, +void cInventory::UpdateItems(void) +{ + const cItem & Slot = GetEquippedItem(); + + if (Slot.IsEmpty()) + { + return; + } + + switch (Slot.m_ItemType) + { + case E_ITEM_MAP: + { + ItemHandler(Slot.m_ItemType)->OnUpdate(m_Owner.GetWorld(), &m_Owner, Slot); + break; + } + + default: break; + } +} + + + + + void cInventory::SaveToJson(Json::Value & a_Value) { // The JSON originally included the 4 crafting slots and the result, so we have to put empty items there, too: diff --git a/src/Inventory.h b/src/Inventory.h index 3c6a19de8..fd2089a13 100644 --- a/src/Inventory.h +++ b/src/Inventory.h @@ -150,6 +150,9 @@ public: /// Sends the slot contents to the owner void SendSlot(int a_SlotNum); + /// Update items (e.g. Maps) + void UpdateItems(void); + /// Converts an armor slot number into the ID for the EntityEquipment packet static int ArmorSlotNumToEntityEquipmentID(short a_ArmorSlotNum); diff --git a/src/Items/ItemEmptyMap.h b/src/Items/ItemEmptyMap.h new file mode 100644 index 000000000..f0b1e1424 --- /dev/null +++ b/src/Items/ItemEmptyMap.h @@ -0,0 +1,62 @@ + +// ItemEmptyMap.h + + + + + +#pragma once + +#include "../Entities/Entity.h" +#include "../Item.h" + + + + + +class cItemEmptyMapHandler : + public cItemHandler +{ + typedef cItemHandler super; + + static const unsigned int DEFAULT_SCALE = 0; + +public: + cItemEmptyMapHandler() : + super(E_ITEM_EMPTY_MAP) + { + } + + virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_Dir) override + { + UNUSED(a_Item); + UNUSED(a_BlockX); + UNUSED(a_BlockZ); + UNUSED(a_Dir); + + // The map center is fixed at the central point of the 8x8 block of chunks you are standing in when you right-click it. + + const int RegionWidth = cChunkDef::Width * 8; + + int CenterX = (int)(floor(a_Player->GetPosX() / (float) RegionWidth) * RegionWidth); + int CenterZ = (int)(floor(a_Player->GetPosZ() / (float) RegionWidth) * RegionWidth); + + cMap * NewMap = a_World->GetMapManager().CreateMap(CenterX, CenterZ, DEFAULT_SCALE); + + // Remove empty map from inventory + if (!a_Player->GetInventory().RemoveOneEquippedItem()) + { + ASSERT(!"Inventory mismatch"); + return true; + } + + if (NewMap == NULL) + { + return true; + } + + a_Player->GetInventory().AddItem(cItem(E_ITEM_MAP, 1, NewMap->GetID()), true, true); + + return true; + } +} ; diff --git a/src/Items/ItemHandler.cpp b/src/Items/ItemHandler.cpp index e9bb616a6..c10d13edc 100644 --- a/src/Items/ItemHandler.cpp +++ b/src/Items/ItemHandler.cpp @@ -18,6 +18,7 @@ #include "ItemComparator.h" #include "ItemDoor.h" #include "ItemDye.h" +#include "ItemEmptyMap.h" #include "ItemFishingRod.h" #include "ItemFlowerPot.h" #include "ItemFood.h" @@ -25,6 +26,7 @@ #include "ItemHoe.h" #include "ItemLeaves.h" #include "ItemLighter.h" +#include "ItemMap.h" #include "ItemMinecart.h" #include "ItemNetherWart.h" #include "ItemPainting.h" @@ -103,11 +105,13 @@ cItemHandler *cItemHandler::CreateItemHandler(int a_ItemType) case E_ITEM_COMPARATOR: return new cItemComparatorHandler(a_ItemType); case E_ITEM_DYE: return new cItemDyeHandler(a_ItemType); case E_ITEM_EGG: return new cItemEggHandler(); + case E_ITEM_EMPTY_MAP: return new cItemEmptyMapHandler(); case E_ITEM_ENDER_PEARL: return new cItemEnderPearlHandler(); case E_ITEM_FIREWORK_ROCKET: return new cItemFireworkHandler(); case E_ITEM_FISHING_ROD: return new cItemFishingRodHandler(a_ItemType); case E_ITEM_FLINT_AND_STEEL: return new cItemLighterHandler(a_ItemType); case E_ITEM_FLOWER_POT: return new cItemFlowerPotHandler(a_ItemType); + case E_ITEM_MAP: return new cItemMapHandler(); case E_ITEM_ITEM_FRAME: return new cItemItemFrameHandler(a_ItemType); case E_ITEM_NETHER_WART: return new cItemNetherWartHandler(a_ItemType); case E_ITEM_PAINTING: return new cItemPaintingHandler(a_ItemType); diff --git a/src/Items/ItemHandler.h b/src/Items/ItemHandler.h index 1a6bb044f..ef3f37a7a 100644 --- a/src/Items/ItemHandler.h +++ b/src/Items/ItemHandler.h @@ -32,6 +32,14 @@ public: UNUSED(a_BlockZ); UNUSED(a_BlockFace); } + + /// Called every tick while the item is on the player's inventory (Used by maps) - For now, called only for equipped items + virtual void OnUpdate(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item) + { + UNUSED(a_World); + UNUSED(a_Player); + UNUSED(a_Item); + } /// Called while the player diggs a block using this item virtual bool OnDiggingBlock(cWorld * a_World, cPlayer * a_Player, const cItem & a_HeldItem, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace); diff --git a/src/Items/ItemMap.h b/src/Items/ItemMap.h new file mode 100644 index 000000000..e8ff9da88 --- /dev/null +++ b/src/Items/ItemMap.h @@ -0,0 +1,43 @@ + +// ItemMap.h + + + + + +#pragma once + +#include "../Entities/Entity.h" +#include "../Item.h" + + + + + +class cItemMapHandler : + public cItemHandler +{ + typedef cItemHandler super; + + static const unsigned int DEFAULT_RADIUS = 128; + +public: + cItemMapHandler() : + super(E_ITEM_MAP) + { + } + + virtual void OnUpdate(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item) + { + cMap * Map = a_World->GetMapManager().GetMapData(a_Item.m_ItemDamage); + + if (Map == NULL) + { + return; + } + + Map->UpdateRadius(*a_Player, DEFAULT_RADIUS); + + Map->UpdateClient(a_Player); + } +} ; diff --git a/src/LightingThread.h b/src/LightingThread.h index 81dd9d61f..72d561348 100644 --- a/src/LightingThread.h +++ b/src/LightingThread.h @@ -82,7 +82,11 @@ protected: cLightingChunkStay(cLightingThread & a_LightingThread, int a_ChunkX, int a_ChunkZ, cChunkCoordCallback * a_CallbackAfter); protected: - virtual void OnChunkAvailable(int a_ChunkX, int a_ChunkZ) override {} + virtual void OnChunkAvailable(int a_ChunkX, int a_ChunkZ) override + { + UNUSED(a_ChunkX); + UNUSED(a_ChunkZ); + } virtual bool OnAllChunksAvailable(void) override; virtual void OnDisabled(void) override; } ; diff --git a/src/Log.cpp b/src/Log.cpp index 2d6be0f59..1ea327d5d 100644 --- a/src/Log.cpp +++ b/src/Log.cpp @@ -42,7 +42,7 @@ cLog::~cLog() -cLog* cLog::GetInstance() +cLog * cLog::GetInstance() { if (s_Log != NULL) { @@ -92,7 +92,7 @@ void cLog::ClearLog() if( m_File ) fclose (m_File); #endif - m_File = 0; + m_File = NULL; } diff --git a/src/Map.cpp b/src/Map.cpp new file mode 100644 index 000000000..2d8f57168 --- /dev/null +++ b/src/Map.cpp @@ -0,0 +1,631 @@ + +// Map.cpp + +#include "Globals.h" + +#include "Map.h" + +#include "ClientHandle.h" +#include "World.h" +#include "Chunk.h" +#include "Entities/Player.h" +#include "FastRandom.h" + + + + + +cMapDecorator::cMapDecorator(cMap * a_Map, eType a_Type, int a_X, int a_Z, int a_Rot) + : m_Map(a_Map) + , m_Type(a_Type) + , m_PixelX(a_X) + , m_PixelZ(a_Z) + , m_Rot(a_Rot) + , m_Player(NULL) +{ +} + + + + + +cMapDecorator::cMapDecorator(cMap * a_Map, cPlayer * a_Player) + : m_Map(a_Map) + , m_Type(E_TYPE_PLAYER) + , m_Player(a_Player) +{ + Update(); +} + + + + + +void cMapDecorator::Update(void) +{ + if (m_Player != NULL) + { + ASSERT(m_Map != NULL); + unsigned int PixelWidth = m_Map->GetPixelWidth(); + + int InsideWidth = (m_Map->GetWidth() / 2) - 1; + int InsideHeight = (m_Map->GetHeight() / 2) - 1; + + int PixelX = (int) (m_Player->GetPosX() - m_Map->GetCenterX()) / PixelWidth; + int PixelZ = (int) (m_Player->GetPosZ() - m_Map->GetCenterZ()) / PixelWidth; + + // Center of pixel + m_PixelX = (2 * PixelX) + 1; + m_PixelZ = (2 * PixelZ) + 1; + + if ((PixelX > -InsideWidth) && (PixelX <= InsideWidth) && (PixelZ > -InsideHeight) && (PixelZ <= InsideHeight)) + { + double Yaw = m_Player->GetYaw(); + + if (m_Map->GetDimension() == dimNether) + { + cFastRandom Random; + + Int64 WorldAge = m_Player->GetWorld()->GetWorldAge(); + + // TODO 2014-02-19 xdot: Refine + m_Rot = Random.NextInt(16, (int) WorldAge); + } + else + { + m_Rot = (int) (Yaw * 16) / 360; + } + + m_Type = E_TYPE_PLAYER; + } + else + { + if ((abs(PixelX) > 320.0) || (abs(PixelZ) > 320.0)) + { + // TODO 2014-02-18 xdot: Remove decorator + } + + m_Rot = 0; + + m_Type = E_TYPE_PLAYER_OUTSIDE; + + // Move to border + if (PixelX <= -InsideWidth) + { + m_PixelX = (2 * -InsideWidth) + 1; + } + if (PixelZ <= -InsideHeight) + { + m_PixelZ = (2 * -InsideHeight) + 1; + } + if (PixelX > InsideWidth) + { + m_PixelX = (2 * InsideWidth) + 1; + } + if (PixelZ > InsideHeight) + { + m_PixelZ = (2 * InsideHeight) + 1; + } + } + } +} + + + + + +cMap::cMap(unsigned int a_ID, cWorld * a_World) + : m_ID(a_ID) + , m_Width(cChunkDef::Width * 8) + , m_Height(cChunkDef::Width * 8) + , m_Scale(3) + , m_CenterX(0) + , m_CenterZ(0) + , m_World(a_World) +{ + m_Data.assign(m_Width * m_Height, E_BASE_COLOR_TRANSPARENT); + + Printf(m_Name, "map_%i", m_ID); +} + + + + + +cMap::cMap(unsigned int a_ID, int a_CenterX, int a_CenterZ, cWorld * a_World, unsigned int a_Scale) + : m_ID(a_ID) + , m_Width(cChunkDef::Width * 8) + , m_Height(cChunkDef::Width * 8) + , m_Scale(a_Scale) + , m_CenterX(a_CenterX) + , m_CenterZ(a_CenterZ) + , m_World(a_World) +{ + m_Data.assign(m_Width * m_Height, E_BASE_COLOR_TRANSPARENT); + + Printf(m_Name, "map_%i", m_ID); +} + + + + + +void cMap::UpdateRadius(int a_PixelX, int a_PixelZ, unsigned int a_Radius) +{ + int PixelRadius = a_Radius / GetPixelWidth(); + + unsigned int StartX = Clamp(a_PixelX - PixelRadius, 0, (int)m_Width); + unsigned int StartZ = Clamp(a_PixelZ - PixelRadius, 0, (int)m_Height); + + unsigned int EndX = Clamp(a_PixelX + PixelRadius, 0, (int)m_Width); + unsigned int EndZ = Clamp(a_PixelZ + PixelRadius, 0, (int)m_Height); + + for (unsigned int X = StartX; X < EndX; ++X) + { + for (unsigned int Z = StartZ; Z < EndZ; ++Z) + { + int dX = X - a_PixelX; + int dZ = Z - a_PixelZ; + + if ((dX * dX) + (dZ * dZ) < (PixelRadius * PixelRadius)) + { + UpdatePixel(X, Z); + } + } + } +} + + + + + +void cMap::UpdateRadius(cPlayer & a_Player, unsigned int a_Radius) +{ + unsigned int PixelWidth = GetPixelWidth(); + + int PixelX = (int) (a_Player.GetPosX() - m_CenterX) / PixelWidth + (m_Width / 2); + int PixelZ = (int) (a_Player.GetPosZ() - m_CenterZ) / PixelWidth + (m_Height / 2); + + UpdateRadius(PixelX, PixelZ, a_Radius); +} + + + + + +bool cMap::UpdatePixel(unsigned int a_X, unsigned int a_Z) +{ + unsigned int PixelWidth = GetPixelWidth(); + + int BlockX = m_CenterX + ((a_X - (m_Width / 2)) * PixelWidth); + int BlockZ = m_CenterZ + ((a_Z - (m_Height / 2)) * PixelWidth); + + int ChunkX, ChunkY, ChunkZ; + m_World->BlockToChunk(BlockX, 0, BlockZ, ChunkX, ChunkY, ChunkZ); + + int RelX = BlockX - (ChunkX * cChunkDef::Width); + int RelZ = BlockZ - (ChunkZ * cChunkDef::Width); + + class cCalculatePixelCb : + public cChunkCallback + { + cMap * m_Map; + + int m_RelX, m_RelZ; + + ColorID m_PixelData; + + public: + cCalculatePixelCb(cMap * a_Map, int a_RelX, int a_RelZ) + : m_Map(a_Map), m_RelX(a_RelX), m_RelZ(a_RelZ), m_PixelData(E_BASE_COLOR_TRANSPARENT) {} + + virtual bool Item(cChunk * a_Chunk) override + { + if (a_Chunk == NULL) + { + return false; + } + + unsigned int PixelWidth = m_Map->GetPixelWidth(); + + if (m_Map->GetDimension() == dimNether) + { + // TODO 2014-02-22 xdot: Nether maps + + return false; + } + + typedef std::map<ColorID, unsigned int> ColorCountMap; + ColorCountMap ColorCounts; + + // Count surface blocks + for (unsigned int X = m_RelX; X < m_RelX + PixelWidth; ++X) + { + for (unsigned int Z = m_RelZ; Z < m_RelZ + PixelWidth; ++Z) + { + unsigned int WaterDepth = 0; + + BLOCKTYPE TargetBlock = E_BLOCK_AIR; + NIBBLETYPE TargetMeta = 0; + + int Height = a_Chunk->GetHeight(X, Z); + + while (Height > 0) + { + a_Chunk->GetBlockTypeMeta(X, Height, Z, TargetBlock, TargetMeta); + + // TODO 2014-02-22 xdot: Check if block color is transparent + if (TargetBlock == E_BLOCK_AIR) + { + --Height; + continue; + } + // TODO 2014-02-22 xdot: Check if block is liquid + else if (false) + { + --Height; + ++WaterDepth; + continue; + } + + break; + } + + // TODO 2014-02-22 xdot: Query block color + ColorID Color = E_BASE_COLOR_BROWN; + + // Debug - Temporary + switch (TargetBlock) + { + case E_BLOCK_GRASS: + { + Color = E_BASE_COLOR_LIGHT_GREEN; break; + } + case E_BLOCK_STATIONARY_WATER: + case E_BLOCK_WATER: + { + Color = E_BASE_COLOR_BLUE; break; + } + } + + ++ColorCounts[Color]; + } + } + + // Find dominant color + ColorID PixelColor = E_BASE_COLOR_TRANSPARENT; + + unsigned int MaxCount = 0; + + for (ColorCountMap::iterator it = ColorCounts.begin(); it != ColorCounts.end(); ++it) + { + if (it->second > MaxCount) + { + PixelColor = it->first; + MaxCount = it->second; + } + } + + // TODO 2014-02-22 xdot: Adjust brightness + unsigned int dColor = 1; + + m_PixelData = PixelColor + dColor; + + return false; + } + + ColorID GetPixelData(void) const + { + return m_PixelData; + } + } CalculatePixelCb(this, RelX, RelZ); + + ASSERT(m_World != NULL); + m_World->DoWithChunk(ChunkX, ChunkZ, CalculatePixelCb); + + SetPixel(a_X, a_Z, CalculatePixelCb.GetPixelData()); + + return true; +} + + + + + +void cMap::UpdateDecorators(void) +{ + for (cMapDecoratorList::iterator it = m_Decorators.begin(); it != m_Decorators.end(); ++it) + { + it->Update(); + } +} + + + + + +void cMap::AddPlayer(cPlayer * a_Player, Int64 a_WorldAge) +{ + cClientHandle * Handle = a_Player->GetClientHandle(); + if (Handle == NULL) + { + return; + } + + cMapClient MapClient; + + MapClient.m_LastUpdate = a_WorldAge; + MapClient.m_SendInfo = true; + MapClient.m_Handle = Handle; + MapClient.m_DataUpdate = 0; + MapClient.m_NextDecoratorUpdate = 0; + + m_Clients.push_back(MapClient); + + cMapDecorator PlayerDecorator(this, a_Player); + + m_Decorators.push_back(PlayerDecorator); +} + + + + + +void cMap::RemoveInactiveClients(Int64 a_WorldAge) +{ + for (cMapClientList::iterator it = m_Clients.begin(); it != m_Clients.end();) + { + if (it->m_LastUpdate < a_WorldAge) + { + // Remove associated decorators + for (cMapDecoratorList::iterator it2 = m_Decorators.begin(); it2 != m_Decorators.end();) + { + if (it2->GetPlayer()->GetClientHandle() == it->m_Handle) + { + // Erase decorator + cMapDecoratorList::iterator temp = it2; + ++it2; + m_Decorators.erase(temp); + } + else + { + ++it2; + } + } + + // Erase client + cMapClientList::iterator temp = it; + ++it; + m_Clients.erase(temp); + } + else + { + ++it; + } + } +} + + + + + +void cMap::StreamNext(cMapClient & a_Client) +{ + cClientHandle * Handle = a_Client.m_Handle; + + if (a_Client.m_SendInfo) + { + Handle->SendMapInfo(m_ID, m_Scale); + + a_Client.m_SendInfo = false; + + return; + } + + ++a_Client.m_NextDecoratorUpdate; + + if (a_Client.m_NextDecoratorUpdate >= 4) + { + // TODO 2014-02-19 xdot + // This is dangerous as the player object may have been destroyed before the decorator is erased from the list + UpdateDecorators(); + + Handle->SendMapDecorators(m_ID, m_Decorators); + + a_Client.m_NextDecoratorUpdate = 0; + } + else + { + ++a_Client.m_DataUpdate; + + unsigned int Y = (a_Client.m_DataUpdate * 11) % m_Width; + + const Byte * Colors = &m_Data[Y * m_Height]; + + Handle->SendMapColumn(m_ID, Y, 0, Colors, m_Height); + } +} + + + + + +void cMap::UpdateClient(cPlayer * a_Player) +{ + ASSERT(a_Player != NULL); + cClientHandle * Handle = a_Player->GetClientHandle(); + + if (Handle == NULL) + { + return; + } + + Int64 WorldAge = a_Player->GetWorld()->GetWorldAge(); + + RemoveInactiveClients(WorldAge - 5); + + // Linear search for client state + for (cMapClientList::iterator it = m_Clients.begin(); it != m_Clients.end(); ++it) + { + if (it->m_Handle == Handle) + { + it->m_LastUpdate = WorldAge; + + StreamNext(*it); + + return; + } + } + + // New player, construct a new client state + AddPlayer(a_Player, WorldAge); +} + + + + + +void cMap::EraseData(void) +{ + m_Data.assign(m_Width * m_Height, 0); +} + + + + + +eDimension cMap::GetDimension(void) const +{ + ASSERT(m_World != NULL); + return m_World->GetDimension(); +} + + + + + + +void cMap::Resize(unsigned int a_Width, unsigned int a_Height) +{ + if ((m_Width == a_Width) && (m_Height == a_Height)) + { + return; + } + + m_Width = a_Width; + m_Height = a_Height; + + m_Data.assign(m_Width * m_Height, 0); +} + + + + + +void cMap::SetPosition(int a_CenterX, int a_CenterZ) +{ + m_CenterX = a_CenterX; + m_CenterZ = a_CenterZ; +} + + + + + +void cMap::SetScale(unsigned int a_Scale) +{ + if (m_Scale == a_Scale) + { + return; + } + + m_Scale = a_Scale; + + for (cMapClientList::iterator it = m_Clients.begin(); it != m_Clients.end(); ++it) + { + it->m_SendInfo = true; + } +} + + + + + +bool cMap::SetPixel(unsigned int a_X, unsigned int a_Z, cMap::ColorID a_Data) +{ + if ((a_X < m_Width) && (a_Z < m_Height)) + { + m_Data[a_Z + (a_X * m_Height)] = a_Data; + + return true; + } + else + { + return false; + } +} + + + + + +cMap::ColorID cMap::GetPixel(unsigned int a_X, unsigned int a_Z) +{ + if ((a_X < m_Width) && (a_Z < m_Height)) + { + return m_Data[a_Z + (a_X * m_Height)]; + } + else + { + return E_BASE_COLOR_TRANSPARENT; + } +} + + + + + +void cMap::SendTo(cClientHandle & a_Client) +{ + a_Client.SendMapInfo(m_ID, m_Scale); + + for (unsigned int i = 0; i < m_Width; ++i) + { + const Byte* Colors = &m_Data[i * m_Height]; + + a_Client.SendMapColumn(m_ID, i, 0, Colors, m_Height); + } + + a_Client.SendMapDecorators(m_ID, m_Decorators); +} + + + + + +unsigned int cMap::GetNumPixels(void) const +{ + return m_Width * m_Height; +} + + + + + +unsigned int cMap::GetNumDecorators(void) const +{ + return m_Decorators.size(); +} + + + + +unsigned int cMap::GetPixelWidth(void) const +{ + return (int) pow(2.0, (double) m_Scale); +} + + + + + diff --git a/src/Map.h b/src/Map.h new file mode 100644 index 000000000..a313d5431 --- /dev/null +++ b/src/Map.h @@ -0,0 +1,264 @@ + +// Map.h + +// Implementation of in-game coloured maps + + + + + +#pragma once + + + + + +#include "BlockID.h" + + + + + +class cClientHandle; +class cWorld; +class cPlayer; +class cMap; + + + + + +/** Encapsulates a map decorator. + * + * A map decorator represents an object drawn on the map that can move freely. + * (e.g. player trackers and item frame pointers) + * + * Excluding manually placed decorators, + * decorators are automatically managed (allocated and freed) by their parent cMap instance. + */ +class cMapDecorator +{ +public: + + enum eType + { + E_TYPE_PLAYER = 0x00, + E_TYPE_ITEM_FRAME = 0x01, + + /** Player outside of the boundaries of the map. */ + E_TYPE_PLAYER_OUTSIDE = 0x06 + }; + + +public: + + /** Constructs a map decorator fixed at the specified pixel coordinates. (DEBUG) */ + cMapDecorator(cMap * a_Map, eType a_Type, int a_X, int a_Z, int a_Rot); + + /** Constructs a map decorator that tracks a player. */ + cMapDecorator(cMap * a_Map, cPlayer * a_Player); + + /** Updates the decorator. */ + void Update(void); + + unsigned int GetPixelX(void) const { return m_PixelX; } + unsigned int GetPixelZ(void) const { return m_PixelZ; } + + int GetRot(void) const { return m_Rot; } + + eType GetType(void) const { return m_Type; } + + cPlayer * GetPlayer(void) { return m_Player; } + + +protected: + + cMap * m_Map; + + eType m_Type; + + unsigned int m_PixelX; + unsigned int m_PixelZ; + + unsigned int m_Rot; + + cPlayer * m_Player; + +}; + +typedef std::list<cMapDecorator> cMapDecoratorList; + + + + + +// tolua_begin + +/** Encapsulates an in-game world map. */ +class cMap +{ +public: + + enum eBaseColor + { + E_BASE_COLOR_TRANSPARENT = 0, /* Air */ + E_BASE_COLOR_LIGHT_GREEN = 4, /* Grass */ + E_BASE_COLOR_LIGHT_BROWN = 8, /* Sand */ + E_BASE_COLOR_GRAY_1 = 12, /* Cloth */ + E_BASE_COLOR_RED = 16, /* TNT */ + E_BASE_COLOR_PALE_BLUE = 20, /* Ice */ + E_BASE_COLOR_GRAY_2 = 24, /* Iron */ + E_BASE_COLOR_DARK_GREEN = 28, /* Foliage */ + E_BASE_COLOR_WHITE = 32, /* Snow */ + E_BASE_COLOR_LIGHT_GRAY = 36, /* Clay */ + E_BASE_COLOR_BROWN = 40, /* Dirt */ + E_BASE_COLOR_DARK_GRAY = 44, /* Stone */ + E_BASE_COLOR_BLUE = 48, /* Water */ + E_BASE_COLOR_DARK_BROWN = 52 /* Wood */ + }; + + typedef Byte ColorID; + + // tolua_end + + typedef std::vector<ColorID> cColorList; + + +public: + + /** Construct an empty map. */ + cMap(unsigned int a_ID, cWorld * a_World); + + /** Construct an empty map at the specified coordinates. */ + cMap(unsigned int a_ID, int a_CenterX, int a_CenterZ, cWorld * a_World, unsigned int a_Scale = 3); + + /** Send this map to the specified client. WARNING: Slow */ + void SendTo(cClientHandle & a_Client); + + /** Update a circular region with the specified radius and center (in pixels). */ + void UpdateRadius(int a_PixelX, int a_PixelZ, unsigned int a_Radius); + + /** Update a circular region around the specified player. */ + void UpdateRadius(cPlayer & a_Player, unsigned int a_Radius); + + /** Send next update packet to the specified player and remove invalid decorators/clients. */ + void UpdateClient(cPlayer * a_Player); + + // tolua_begin + + /** Erase pixel data */ + void EraseData(void); + + void Resize(unsigned int a_Width, unsigned int a_Height); + + void SetPosition(int a_CenterX, int a_CenterZ); + + void SetScale(unsigned int a_Scale); + + bool SetPixel(unsigned int a_X, unsigned int a_Z, ColorID a_Data); + + ColorID GetPixel(unsigned int a_X, unsigned int a_Z); + + unsigned int GetWidth (void) const { return m_Width; } + unsigned int GetHeight(void) const { return m_Height; } + + unsigned int GetScale(void) const { return m_Scale; } + + int GetCenterX(void) const { return m_CenterX; } + int GetCenterZ(void) const { return m_CenterZ; } + + unsigned int GetID(void) const { return m_ID; } + + cWorld * GetWorld(void) { return m_World; } + + AString GetName(void) { return m_Name; } + + eDimension GetDimension(void) const; + + unsigned int GetNumPixels(void) const; + + unsigned int GetPixelWidth(void) const; + + // tolua_end + + unsigned int GetNumDecorators(void) const; + + const cColorList & GetData(void) const { return m_Data; } + + static const char * GetClassStatic(void) // Needed for ManualBindings's DoWith templates + { + return "cMap"; + } + + +protected: + + /** Encapsulates the state of a map client. + * + * In order to enhance performace, maps are streamed column-by-column to each client. + * This structure stores the state of the stream. + */ + struct cMapClient + { + cClientHandle * m_Handle; + + /** Whether the map scale was modified and needs to be resent. */ + bool m_SendInfo; + + /** Ticks since last decorator update. */ + unsigned int m_NextDecoratorUpdate; + + /** Number of pixel data updates. */ + Int64 m_DataUpdate; + + Int64 m_LastUpdate; + }; + + typedef std::list<cMapClient> cMapClientList; + + +private: + + /** Update the associated decorators. */ + void UpdateDecorators(void); + + /** Update the specified pixel. */ + bool UpdatePixel(unsigned int a_X, unsigned int a_Z); + + /** Add a new map client. */ + void AddPlayer(cPlayer * a_Player, Int64 a_WorldAge); + + /** Remove inactive or invalid clients. */ + void RemoveInactiveClients(Int64 a_WorldAge); + + /** Send next update packet to the specified client. */ + void StreamNext(cMapClient & a_Client); + + unsigned int m_ID; + + unsigned int m_Width; + unsigned int m_Height; + + /** The zoom level, 2^scale square blocks per pixel */ + unsigned int m_Scale; + + int m_CenterX; + int m_CenterZ; + + /** Column-major array of colours */ + cColorList m_Data; + + cWorld * m_World; + + cMapDecoratorList m_Decorators; + + cMapClientList m_Clients; + + AString m_Name; + + friend class cMapSerializer; + +}; // tolua_export + + + diff --git a/src/MapManager.cpp b/src/MapManager.cpp new file mode 100644 index 000000000..9d02eafb4 --- /dev/null +++ b/src/MapManager.cpp @@ -0,0 +1,178 @@ + +// MapManager.cpp + +#include "Globals.h" + +#include "MapManager.h" + +#include "World.h" +#include "WorldStorage/MapSerializer.h" + + + + + +cMapManager::cMapManager(cWorld * a_World) + : m_World(a_World) +{ + ASSERT(m_World != NULL); +} + + + + + +bool cMapManager::DoWithMap(int a_ID, cMapCallback & a_Callback) +{ + cCSLock Lock(m_CS); + cMap * Map = GetMapData(a_ID); + + if (Map == NULL) + { + return false; + } + else + { + a_Callback.Item(Map); + return true; + } +} + + + + + +bool cMapManager::ForEachMap(cMapCallback & a_Callback) +{ + cCSLock Lock(m_CS); + for (cMapList::iterator itr = m_MapData.begin(); itr != m_MapData.end(); ++itr) + { + cMap * Map = &(*itr); + if (a_Callback.Item(Map)) + { + return false; + } + } // for itr - m_MapData[] + return true; +} + + + + + +cMap * cMapManager::GetMapData(unsigned int a_ID) +{ + if (a_ID < m_MapData.size()) + { + return &m_MapData[a_ID]; + } + else + { + return NULL; + } +} + + + + + +cMap * cMapManager::CreateMap(int a_CenterX, int a_CenterY, int a_Scale) +{ + cCSLock Lock(m_CS); + + if (m_MapData.size() >= 65536) + { + LOGWARN("Could not craft map - Too many maps in use"); + return NULL; + } + + cMap Map(m_MapData.size(), a_CenterX, a_CenterY, m_World, a_Scale); + + m_MapData.push_back(Map); + + return &m_MapData[Map.GetID()]; +} + + + + + +unsigned int cMapManager::GetNumMaps(void) const +{ + return m_MapData.size(); +} + + + + + +void cMapManager::LoadMapData(void) +{ + cCSLock Lock(m_CS); + + cIDCountSerializer IDSerializer(m_World->GetName()); + + if (!IDSerializer.Load()) + { + return; + } + + unsigned int MapCount = IDSerializer.GetMapCount(); + + m_MapData.clear(); + + for (unsigned int i = 0; i < MapCount; ++i) + { + cMap Map(i, m_World); + + cMapSerializer Serializer(m_World->GetName(), &Map); + + if (!Serializer.Load()) + { + LOGWARN("Could not load map #%i", Map.GetID()); + } + + m_MapData.push_back(Map); + } +} + + + + + +void cMapManager::SaveMapData(void) +{ + cCSLock Lock(m_CS); + + if (m_MapData.empty()) + { + return; + } + + cIDCountSerializer IDSerializer(m_World->GetName()); + + IDSerializer.SetMapCount(m_MapData.size()); + + if (!IDSerializer.Save()) + { + LOGERROR("Could not save idcounts.dat"); + return; + } + + for (cMapList::iterator it = m_MapData.begin(); it != m_MapData.end(); ++it) + { + cMap & Map = *it; + + cMapSerializer Serializer(m_World->GetName(), &Map); + + if (!Serializer.Save()) + { + LOGWARN("Could not save map #%i", Map.GetID()); + } + } +} + + + + + diff --git a/src/MapManager.h b/src/MapManager.h new file mode 100644 index 000000000..80e6d16d1 --- /dev/null +++ b/src/MapManager.h @@ -0,0 +1,78 @@ + +// MapManager.h + + + + + +#pragma once + + + + + +#include "Map.h" + + + + +typedef cItemCallback<cMap> cMapCallback; + + + + +// tolua_begin + +/** Manages the in-game maps of a single world - Thread safe. */ +class cMapManager +{ +public: + // tolua_end + + cMapManager(cWorld * a_World); + + /** Returns the map with the specified ID, NULL if out of range. + * + * WARNING: The returned map object is not thread safe. + */ + cMap * GetMapData(unsigned int a_ID); + + /** Creates a new map. Returns NULL on error */ + cMap * CreateMap(int a_CenterX, int a_CenterY, int a_Scale = 3); + + /** Calls the callback for the map with the specified ID. + * + * Returns true if the map was found and the callback called, false if map not found. + * Callback return ignored. + */ + bool DoWithMap(int a_ID, cMapCallback & a_Callback); // Exported in ManualBindings.cpp + + /** Calls the callback for each map. + * + * Returns true if all maps processed, false if the callback aborted by returning true. + */ + bool ForEachMap(cMapCallback & a_Callback); + + unsigned int GetNumMaps(void) const; // tolua_export + + /** Loads the map data from the disk */ + void LoadMapData(void); + + /** Saves the map data to the disk */ + void SaveMapData(void); + + +private: + + typedef std::vector<cMap> cMapList; + + cCriticalSection m_CS; + + cMapList m_MapData; + + cWorld * m_World; + +}; // tolua_export + + + diff --git a/src/MersenneTwister.h b/src/MersenneTwister.h index 784ac605d..f4c7b0699 100644 --- a/src/MersenneTwister.h +++ b/src/MersenneTwister.h @@ -207,7 +207,7 @@ inline void MTRand::seed( uint32 *const bigSeed, const uint32 seedLength ) initialize(19650218UL); int i = 1; uint32 j = 0; - int k = ( N > seedLength ? N : seedLength ); + int k = ( (uint32)N > seedLength ? (uint32)N : seedLength ); for( ; k; --k ) { state[i] = diff --git a/src/Mobs/Blaze.cpp b/src/Mobs/Blaze.cpp index f9c05b17a..ac42cf40b 100644 --- a/src/Mobs/Blaze.cpp +++ b/src/Mobs/Blaze.cpp @@ -19,7 +19,11 @@ cBlaze::cBlaze(void) : void cBlaze::GetDrops(cItems & a_Drops, cEntity * a_Killer) { - AddRandomDropItem(a_Drops, 0, 1, E_ITEM_BLAZE_ROD); + if ((a_Killer != NULL) && (a_Killer->IsPlayer() || a_Killer->IsA("cWolf"))) + { + int LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting); + AddRandomDropItem(a_Drops, 0, 1 + LootingLevel, E_ITEM_BLAZE_ROD); + } } diff --git a/src/Mobs/Cavespider.cpp b/src/Mobs/Cavespider.cpp index aba1ff9f5..94e93283d 100644 --- a/src/Mobs/Cavespider.cpp +++ b/src/Mobs/Cavespider.cpp @@ -31,8 +31,16 @@ void cCavespider::Tick(float a_Dt, cChunk & a_Chunk) void cCavespider::GetDrops(cItems & a_Drops, cEntity * a_Killer) { - AddRandomDropItem(a_Drops, 0, 2, E_ITEM_STRING); - AddRandomDropItem(a_Drops, 0, 1, E_ITEM_SPIDER_EYE); + int LootingLevel = 0; + if (a_Killer != NULL) + { + LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting); + } + AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_STRING); + if ((a_Killer != NULL) && (a_Killer->IsPlayer() || a_Killer->IsA("cWolf"))) + { + AddRandomUncommonDropItem(a_Drops, 33.0f, E_ITEM_SPIDER_EYE); + } } diff --git a/src/Mobs/Chicken.cpp b/src/Mobs/Chicken.cpp index fab92ce49..f7e44238f 100644 --- a/src/Mobs/Chicken.cpp +++ b/src/Mobs/Chicken.cpp @@ -48,8 +48,13 @@ void cChicken::Tick(float a_Dt, cChunk & a_Chunk) void cChicken::GetDrops(cItems & a_Drops, cEntity * a_Killer) { - AddRandomDropItem(a_Drops, 0, 2, E_ITEM_FEATHER); - a_Drops.push_back(cItem(IsOnFire() ? E_ITEM_COOKED_CHICKEN : E_ITEM_RAW_CHICKEN, 1)); + int LootingLevel = 0; + if (a_Killer != NULL) + { + LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting); + } + AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_FEATHER); + AddRandomDropItem(a_Drops, 1, 1, IsOnFire() ? E_ITEM_COOKED_CHICKEN : E_ITEM_RAW_CHICKEN); } diff --git a/src/Mobs/Cow.cpp b/src/Mobs/Cow.cpp index d8e905217..9914df6b5 100644 --- a/src/Mobs/Cow.cpp +++ b/src/Mobs/Cow.cpp @@ -21,8 +21,13 @@ cCow::cCow(void) : void cCow::GetDrops(cItems & a_Drops, cEntity * a_Killer) { - AddRandomDropItem(a_Drops, 0, 2, E_ITEM_LEATHER); - AddRandomDropItem(a_Drops, 1, 3, IsOnFire() ? E_ITEM_STEAK : E_ITEM_RAW_BEEF); + int LootingLevel = 0; + if (a_Killer != NULL) + { + LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting); + } + AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_LEATHER); + AddRandomDropItem(a_Drops, 1, 3 + LootingLevel, IsOnFire() ? E_ITEM_STEAK : E_ITEM_RAW_BEEF); } diff --git a/src/Mobs/Creeper.cpp b/src/Mobs/Creeper.cpp index ff0abfdca..40ee20e44 100644 --- a/src/Mobs/Creeper.cpp +++ b/src/Mobs/Creeper.cpp @@ -39,7 +39,12 @@ void cCreeper::Tick(float a_Dt, cChunk & a_Chunk) void cCreeper::GetDrops(cItems & a_Drops, cEntity * a_Killer) { - AddRandomDropItem(a_Drops, 0, 2, E_ITEM_GUNPOWDER); + int LootingLevel = 0; + if (a_Killer != NULL) + { + LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting); + } + AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_GUNPOWDER); if ((a_Killer != NULL) && (a_Killer->IsProjectile())) { diff --git a/src/Mobs/Enderman.cpp b/src/Mobs/Enderman.cpp index a784131e4..becc99a86 100644 --- a/src/Mobs/Enderman.cpp +++ b/src/Mobs/Enderman.cpp @@ -21,7 +21,12 @@ cEnderman::cEnderman(void) : void cEnderman::GetDrops(cItems & a_Drops, cEntity * a_Killer) { - AddRandomDropItem(a_Drops, 0, 1, E_ITEM_ENDER_PEARL); + int LootingLevel = 0; + if (a_Killer != NULL) + { + LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting); + } + AddRandomDropItem(a_Drops, 0, 1 + LootingLevel, E_ITEM_ENDER_PEARL); } diff --git a/src/Mobs/Ghast.cpp b/src/Mobs/Ghast.cpp index 96a29b2d8..fe18f5e76 100644 --- a/src/Mobs/Ghast.cpp +++ b/src/Mobs/Ghast.cpp @@ -18,8 +18,13 @@ cGhast::cGhast(void) : void cGhast::GetDrops(cItems & a_Drops, cEntity * a_Killer) { - AddRandomDropItem(a_Drops, 0, 2, E_ITEM_GUNPOWDER); - AddRandomDropItem(a_Drops, 0, 1, E_ITEM_GHAST_TEAR); + int LootingLevel = 0; + if (a_Killer != NULL) + { + LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting); + } + AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_GUNPOWDER); + AddRandomDropItem(a_Drops, 0, 1 + LootingLevel, E_ITEM_GHAST_TEAR); } diff --git a/src/Mobs/Horse.cpp b/src/Mobs/Horse.cpp index bb9a4e3f6..9d130301f 100644 --- a/src/Mobs/Horse.cpp +++ b/src/Mobs/Horse.cpp @@ -140,7 +140,12 @@ void cHorse::OnRightClicked(cPlayer & a_Player) void cHorse::GetDrops(cItems & a_Drops, cEntity * a_Killer) { - AddRandomDropItem(a_Drops, 0, 2, E_ITEM_LEATHER); + int LootingLevel = 0; + if (a_Killer != NULL) + { + LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting); + } + AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_LEATHER); if (m_bIsSaddled) { a_Drops.push_back(cItem(E_ITEM_SADDLE, 1)); diff --git a/src/Mobs/IronGolem.cpp b/src/Mobs/IronGolem.cpp index 47c961098..dae4615e4 100644 --- a/src/Mobs/IronGolem.cpp +++ b/src/Mobs/IronGolem.cpp @@ -18,7 +18,9 @@ cIronGolem::cIronGolem(void) : void cIronGolem::GetDrops(cItems & a_Drops, cEntity * a_Killer) { + UNUSED(a_Killer); AddRandomDropItem(a_Drops, 0, 5, E_ITEM_IRON); + AddRandomDropItem(a_Drops, 0, 2, E_BLOCK_FLOWER); } diff --git a/src/Mobs/Magmacube.cpp b/src/Mobs/Magmacube.cpp index 86447ff6b..05405f082 100644 --- a/src/Mobs/Magmacube.cpp +++ b/src/Mobs/Magmacube.cpp @@ -19,7 +19,11 @@ cMagmaCube::cMagmaCube(int a_Size) : void cMagmaCube::GetDrops(cItems & a_Drops, cEntity * a_Killer) { - AddRandomDropItem(a_Drops, 0, 1, E_ITEM_MAGMA_CREAM); + UNUSED(a_Killer); + if (GetSize() > 1) + { + AddRandomUncommonDropItem(a_Drops, 25.0f, E_ITEM_MAGMA_CREAM); + } } diff --git a/src/Mobs/Monster.cpp b/src/Mobs/Monster.cpp index b5cf693cb..ac9137ccd 100644 --- a/src/Mobs/Monster.cpp +++ b/src/Mobs/Monster.cpp @@ -82,6 +82,12 @@ cMonster::cMonster(const AString & a_ConfigName, eType a_MobType, const AString , m_AttackRange(2) , m_AttackInterval(0) , m_SightDistance(25) + , m_DropChanceWeapon(0.085) + , m_DropChanceHelmet(0.085) + , m_DropChanceChestplate(0.085) + , m_DropChanceLeggings(0.085) + , m_DropChanceBoots(0.085) + , m_CanPickUpLoot(true) , m_BurnsInDaylight(false) { if (!a_ConfigName.empty()) @@ -880,6 +886,76 @@ void cMonster::AddRandomDropItem(cItems & a_Drops, unsigned int a_Min, unsigned +void cMonster::AddRandomUncommonDropItem(cItems & a_Drops, float a_Chance, short a_Item, short a_ItemHealth) +{ + MTRand r1; + int Count = r1.randInt() % 1000; + if (Count < (a_Chance * 10)) + { + a_Drops.push_back(cItem(a_Item, 1, a_ItemHealth)); + } +} + + + + + +void cMonster::AddRandomRareDropItem(cItems & a_Drops, cItems & a_Items, short a_LootingLevel) +{ + MTRand r1; + int Count = r1.randInt() % 200; + if (Count < (5 + a_LootingLevel)) + { + int Rare = r1.randInt() % a_Items.Size(); + a_Drops.push_back(a_Items.at(Rare)); + } +} + + + + + +void cMonster::AddRandomArmorDropItem(cItems & a_Drops, short a_LootingLevel) +{ + MTRand r1; + if (r1.randInt() % 200 < ((m_DropChanceHelmet * 200) + (a_LootingLevel * 2))) + { + if (!GetEquippedHelmet().IsEmpty()) a_Drops.push_back(GetEquippedHelmet()); + } + + if (r1.randInt() % 200 < ((m_DropChanceChestplate * 200) + (a_LootingLevel * 2))) + { + if (!GetEquippedChestplate().IsEmpty()) a_Drops.push_back(GetEquippedChestplate()); + } + + if (r1.randInt() % 200 < ((m_DropChanceLeggings * 200) + (a_LootingLevel * 2))) + { + if (!GetEquippedLeggings().IsEmpty()) a_Drops.push_back(GetEquippedLeggings()); + } + + if (r1.randInt() % 200 < ((m_DropChanceBoots * 200) + (a_LootingLevel * 2))) + { + if (!GetEquippedBoots().IsEmpty()) a_Drops.push_back(GetEquippedBoots()); + } +} + + + + + +void cMonster::AddRandomWeaponDropItem(cItems & a_Drops, short a_LootingLevel) +{ + MTRand r1; + if (r1.randInt() % 200 < ((m_DropChanceWeapon * 200) + (a_LootingLevel * 2))) + { + if (!GetEquippedWeapon().IsEmpty()) a_Drops.push_back(GetEquippedWeapon()); + } +} + + + + + void cMonster::HandleDaylightBurning(cChunk & a_Chunk) { if (!m_BurnsInDaylight) diff --git a/src/Mobs/Monster.h b/src/Mobs/Monster.h index 4d2e099c5..776426a0d 100644 --- a/src/Mobs/Monster.h +++ b/src/Mobs/Monster.h @@ -5,6 +5,7 @@ #include "../Defines.h" #include "../BlockID.h" #include "../Item.h" +#include "../Enchantments.h" @@ -118,6 +119,19 @@ public: void SetAttackDamage(int a_AttackDamage) { m_AttackDamage = a_AttackDamage; } void SetSightDistance(int a_SightDistance) { m_SightDistance = a_SightDistance; } + float GetDropChanceWeapon() { return m_DropChanceWeapon; } + float GetDropChanceHelmet() { return m_DropChanceHelmet; } + float GetDropChanceChestplate() { return m_DropChanceChestplate; } + float GetDropChanceLeggings() { return m_DropChanceLeggings; } + float GetDropChanceBoots() { return m_DropChanceBoots; } + bool CanPickUpLoot() { return m_CanPickUpLoot; } + void SetDropChanceWeapon(float a_DropChanceWeapon) { m_DropChanceWeapon = a_DropChanceWeapon; } + void SetDropChanceHelmet(float a_DropChanceHelmet) { m_DropChanceHelmet = a_DropChanceHelmet; } + void SetDropChanceChestplate(float a_DropChanceChestplate) { m_DropChanceChestplate = a_DropChanceChestplate; } + void SetDropChanceLeggings(float a_DropChanceLeggings) { m_DropChanceLeggings = a_DropChanceLeggings; } + void SetDropChanceBoots(float a_DropChanceBoots) { m_DropChanceBoots = a_DropChanceBoots; } + void SetCanPickUpLoot(bool a_CanPickUpLoot) { m_CanPickUpLoot = a_CanPickUpLoot; } + /// Sets whether the mob burns in daylight. Only evaluated at next burn-decision tick void SetBurnsInDaylight(bool a_BurnsInDaylight) { m_BurnsInDaylight = a_BurnsInDaylight; } @@ -220,10 +234,31 @@ protected: float m_AttackInterval; int m_SightDistance; + float m_DropChanceWeapon; + float m_DropChanceHelmet; + float m_DropChanceChestplate; + float m_DropChanceLeggings; + float m_DropChanceBoots; + bool m_CanPickUpLoot; + void HandleDaylightBurning(cChunk & a_Chunk); bool m_BurnsInDaylight; + /** Adds a random number of a_Item between a_Min and a_Max to itemdrops a_Drops*/ void AddRandomDropItem(cItems & a_Drops, unsigned int a_Min, unsigned int a_Max, short a_Item, short a_ItemHealth = 0); + + /** Adds a item a_Item with the chance of a_Chance (in percent) to itemdrops a_Drops*/ + void AddRandomUncommonDropItem(cItems & a_Drops, float a_Chance, short a_Item, short a_ItemHealth = 0); + + /** Adds one rare item out of the list of rare items a_Items modified by the looting level a_LootingLevel(I-III or custom) to the itemdrop a_Drops*/ + void AddRandomRareDropItem(cItems & a_Drops, cItems & a_Items, short a_LootingLevel); + + /** Adds armor that is equipped with the chance saved in m_DropChance[...] (this will be greter than 1 if piccked up or 0.085 + (0.01 per LootingLevel) if born with) to the drop*/ + void AddRandomArmorDropItem(cItems & a_Drops, short a_LootingLevel); + + /** Adds weapon that is equipped with the chance saved in m_DropChance[...] (this will be greter than 1 if piccked up or 0.085 + (0.01 per LootingLevel) if born with) to the drop*/ + void AddRandomWeaponDropItem(cItems & a_Drops, short a_LootingLevel); + } ; // tolua_export diff --git a/src/Mobs/Mooshroom.cpp b/src/Mobs/Mooshroom.cpp index 88101cd83..81bd3e3b4 100644 --- a/src/Mobs/Mooshroom.cpp +++ b/src/Mobs/Mooshroom.cpp @@ -2,11 +2,12 @@ #include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules #include "Mooshroom.h" +#include "../Entities/Player.h" + -// TODO: Milk Cow @@ -23,9 +24,52 @@ cMooshroom::cMooshroom(void) : void cMooshroom::GetDrops(cItems & a_Drops, cEntity * a_Killer) { - AddRandomDropItem(a_Drops, 0, 2, E_ITEM_LEATHER); - AddRandomDropItem(a_Drops, 1, 3, IsOnFire() ? E_ITEM_STEAK : E_ITEM_RAW_BEEF); + int LootingLevel = 0; + if (a_Killer != NULL) + { + LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting); + } + AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_LEATHER); + AddRandomDropItem(a_Drops, 1, 3 + LootingLevel, IsOnFire() ? E_ITEM_STEAK : E_ITEM_RAW_BEEF); } + + +void cMooshroom::OnRightClicked(cPlayer & a_Player) +{ + switch (a_Player.GetEquippedItem().m_ItemType) + { + case E_ITEM_BUCKET: + { + if (!a_Player.IsGameModeCreative()) + { + a_Player.GetInventory().RemoveOneEquippedItem(); + a_Player.GetInventory().AddItem(E_ITEM_MILK); + } + } break; + case E_ITEM_BOWL: + { + if (!a_Player.IsGameModeCreative()) + { + a_Player.GetInventory().RemoveOneEquippedItem(); + a_Player.GetInventory().AddItem(E_ITEM_MUSHROOM_SOUP); + } + } break; + case E_ITEM_SHEARS: + { + if (!a_Player.IsGameModeCreative()) + { + a_Player.UseEquippedItem(); + } + + cItems Drops; + Drops.push_back(cItem(E_BLOCK_RED_MUSHROOM, 5, 0)); + m_World->SpawnItemPickups(Drops, GetPosX(), GetPosY(), GetPosZ(), 10); + m_World->SpawnMob(GetPosX(), GetPosY(), GetPosZ(), cMonster::mtCow); + Destroy(); + } break; + } +} + diff --git a/src/Mobs/Mooshroom.h b/src/Mobs/Mooshroom.h index c94301098..16f6c8248 100644 --- a/src/Mobs/Mooshroom.h +++ b/src/Mobs/Mooshroom.h @@ -18,6 +18,7 @@ public: CLASS_PROTODEF(cMooshroom); virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; + virtual void OnRightClicked(cPlayer & a_Player) override; virtual const cItem GetFollowedItem(void) const override { return cItem(E_ITEM_WHEAT); } } ; diff --git a/src/Mobs/Pig.cpp b/src/Mobs/Pig.cpp index d8f3dda37..e862f5aaa 100644 --- a/src/Mobs/Pig.cpp +++ b/src/Mobs/Pig.cpp @@ -21,7 +21,12 @@ cPig::cPig(void) : void cPig::GetDrops(cItems & a_Drops, cEntity * a_Killer) { - AddRandomDropItem(a_Drops, 1, 3, IsOnFire() ? E_ITEM_COOKED_PORKCHOP : E_ITEM_RAW_PORKCHOP); + int LootingLevel = 0; + if (a_Killer != NULL) + { + LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting); + } + AddRandomDropItem(a_Drops, 1, 3 + LootingLevel, IsOnFire() ? E_ITEM_COOKED_PORKCHOP : E_ITEM_RAW_PORKCHOP); if (m_bIsSaddled) { a_Drops.push_back(cItem(E_ITEM_SADDLE, 1)); diff --git a/src/Mobs/Skeleton.cpp b/src/Mobs/Skeleton.cpp index 4c8e78988..47fcdbb26 100644 --- a/src/Mobs/Skeleton.cpp +++ b/src/Mobs/Skeleton.cpp @@ -20,8 +20,26 @@ cSkeleton::cSkeleton(bool IsWither) : void cSkeleton::GetDrops(cItems & a_Drops, cEntity * a_Killer) { - AddRandomDropItem(a_Drops, 0, 2, E_ITEM_ARROW); - AddRandomDropItem(a_Drops, 0, 2, E_ITEM_BONE); + int LootingLevel = 0; + if (a_Killer != NULL) + { + LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting); + } + if (IsWither()) + { + AddRandomUncommonDropItem(a_Drops, 33.0f, E_ITEM_COAL); + cItems RareDrops; + RareDrops.Add(cItem(E_ITEM_HEAD, 1, 1)); + AddRandomRareDropItem(a_Drops, RareDrops, LootingLevel); + } + else + { + AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_ARROW); + + } + AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_BONE); + AddRandomArmorDropItem(a_Drops, LootingLevel); + AddRandomWeaponDropItem(a_Drops, LootingLevel); } diff --git a/src/Mobs/Slime.cpp b/src/Mobs/Slime.cpp index 19f376c21..52a52bb39 100644 --- a/src/Mobs/Slime.cpp +++ b/src/Mobs/Slime.cpp @@ -20,8 +20,15 @@ cSlime::cSlime(int a_Size) : void cSlime::GetDrops(cItems & a_Drops, cEntity * a_Killer) { - // TODO: only when tiny - AddRandomDropItem(a_Drops, 0, 2, E_ITEM_SLIMEBALL); + int LootingLevel = 0; + if (a_Killer != NULL) + { + LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting); + } + if (GetSize() == 1) + { + AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_SLIMEBALL); + } } diff --git a/src/Mobs/SnowGolem.cpp b/src/Mobs/SnowGolem.cpp index c60103055..67e3a3bb8 100644 --- a/src/Mobs/SnowGolem.cpp +++ b/src/Mobs/SnowGolem.cpp @@ -19,7 +19,8 @@ cSnowGolem::cSnowGolem(void) : void cSnowGolem::GetDrops(cItems & a_Drops, cEntity * a_Killer) { - AddRandomDropItem(a_Drops, 0, 5, E_ITEM_SNOWBALL); + UNUSED(a_Killer); + AddRandomDropItem(a_Drops, 0, 15, E_ITEM_SNOWBALL); } diff --git a/src/Mobs/Spider.cpp b/src/Mobs/Spider.cpp index b19a5dcef..8b978ff6b 100644 --- a/src/Mobs/Spider.cpp +++ b/src/Mobs/Spider.cpp @@ -18,8 +18,16 @@ cSpider::cSpider(void) : void cSpider::GetDrops(cItems & a_Drops, cEntity * a_Killer) { - AddRandomDropItem(a_Drops, 0, 2, E_ITEM_STRING); - AddRandomDropItem(a_Drops, 0, 1, E_ITEM_SPIDER_EYE); + int LootingLevel = 0; + if (a_Killer != NULL) + { + LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting); + } + AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_STRING); + if ((a_Killer != NULL) && (a_Killer->IsPlayer() || a_Killer->IsA("cWolf"))) + { + AddRandomUncommonDropItem(a_Drops, 33.0f, E_ITEM_SPIDER_EYE); + } } diff --git a/src/Mobs/Squid.cpp b/src/Mobs/Squid.cpp index 5a27762ff..ba9171b39 100644 --- a/src/Mobs/Squid.cpp +++ b/src/Mobs/Squid.cpp @@ -21,7 +21,12 @@ cSquid::cSquid(void) : void cSquid::GetDrops(cItems & a_Drops, cEntity * a_Killer) { // Drops 0-3 Ink Sacs - AddRandomDropItem(a_Drops, 0, 3, E_ITEM_DYE, E_META_DYE_BLACK); + int LootingLevel = 0; + if (a_Killer != NULL) + { + LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting); + } + AddRandomDropItem(a_Drops, 0, 3 + LootingLevel, E_ITEM_DYE, E_META_DYE_BLACK); } diff --git a/src/Mobs/Witch.cpp b/src/Mobs/Witch.cpp index 25d27041f..6956f7b7a 100644 --- a/src/Mobs/Witch.cpp +++ b/src/Mobs/Witch.cpp @@ -18,13 +18,28 @@ cWitch::cWitch(void) : void cWitch::GetDrops(cItems & a_Drops, cEntity * a_Killer) { - AddRandomDropItem(a_Drops, 0, 6, E_ITEM_GLASS_BOTTLE); - AddRandomDropItem(a_Drops, 0, 6, E_ITEM_GLOWSTONE_DUST); - AddRandomDropItem(a_Drops, 0, 6, E_ITEM_GUNPOWDER); - AddRandomDropItem(a_Drops, 0, 6, E_ITEM_REDSTONE_DUST); - AddRandomDropItem(a_Drops, 0, 6, E_ITEM_SPIDER_EYE); - AddRandomDropItem(a_Drops, 0, 6, E_ITEM_STICK); - AddRandomDropItem(a_Drops, 0, 6, E_ITEM_SUGAR); + int LootingLevel = 0; + if (a_Killer != NULL) + { + LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting); + } + MTRand r1; + int DropTypeCount = (r1.randInt() % 3) + 1; + for (int i = 0; i < DropTypeCount; i++) + { + int DropType = r1.randInt() % 7; + switch (DropType) + { + case 0: AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_GLASS_BOTTLE); break; + case 1: AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_GLOWSTONE_DUST); break; + case 2: AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_GUNPOWDER); break; + case 3: AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_REDSTONE_DUST); break; + case 4: AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_SPIDER_EYE); break; + case 5: AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_STICK); break; + case 6: AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_SUGAR); break; + } + } + AddRandomWeaponDropItem(a_Drops, LootingLevel); } diff --git a/src/Mobs/Witch.h b/src/Mobs/Witch.h index 4e637beea..51c63322a 100644 --- a/src/Mobs/Witch.h +++ b/src/Mobs/Witch.h @@ -2,6 +2,7 @@ #pragma once #include "AggressiveMonster.h" +#include "../MersenneTwister.h" diff --git a/src/Mobs/Zombie.cpp b/src/Mobs/Zombie.cpp index 27e8ed5fb..f19e096ee 100644 --- a/src/Mobs/Zombie.cpp +++ b/src/Mobs/Zombie.cpp @@ -23,9 +23,19 @@ cZombie::cZombie(bool a_IsVillagerZombie) : void cZombie::GetDrops(cItems & a_Drops, cEntity * a_Killer) { - AddRandomDropItem(a_Drops, 0, 2, E_ITEM_ROTTEN_FLESH); - - // TODO: Rare drops + int LootingLevel = 0; + if (a_Killer != NULL) + { + LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting); + } + AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_ROTTEN_FLESH); + cItems RareDrops; + RareDrops.Add(cItem(E_ITEM_IRON)); + RareDrops.Add(cItem(E_ITEM_CARROT)); + RareDrops.Add(cItem(E_ITEM_POTATO)); + AddRandomRareDropItem(a_Drops, RareDrops, LootingLevel); + AddRandomArmorDropItem(a_Drops, LootingLevel); + AddRandomWeaponDropItem(a_Drops, LootingLevel); } diff --git a/src/Mobs/Zombiepigman.cpp b/src/Mobs/Zombiepigman.cpp index 6ac89ed4c..a0142b566 100644 --- a/src/Mobs/Zombiepigman.cpp +++ b/src/Mobs/Zombiepigman.cpp @@ -19,10 +19,19 @@ cZombiePigman::cZombiePigman(void) : void cZombiePigman::GetDrops(cItems & a_Drops, cEntity * a_Killer) { - AddRandomDropItem(a_Drops, 0, 1, E_ITEM_ROTTEN_FLESH); - AddRandomDropItem(a_Drops, 0, 1, E_ITEM_GOLD_NUGGET); + int LootingLevel = 0; + if (a_Killer != NULL) + { + LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting); + } + AddRandomDropItem(a_Drops, 0, 1 + LootingLevel, E_ITEM_ROTTEN_FLESH); + AddRandomDropItem(a_Drops, 0, 1 + LootingLevel, E_ITEM_GOLD_NUGGET); - // TODO: Rare drops + cItems RareDrops; + RareDrops.Add(cItem(E_ITEM_GOLD)); + AddRandomRareDropItem(a_Drops, RareDrops, LootingLevel); + AddRandomArmorDropItem(a_Drops, LootingLevel); + AddRandomWeaponDropItem(a_Drops, LootingLevel); } diff --git a/src/OSSupport/Queue.h b/src/OSSupport/Queue.h index 6c3d58295..beb6a63f1 100644 --- a/src/OSSupport/Queue.h +++ b/src/OSSupport/Queue.h @@ -29,7 +29,11 @@ public: static void Delete(T) {}; /// Called when an Item is inserted with EnqueueItemIfNotPresent and there is another equal value already inserted - static void Combine(T & a_existing, const T & a_new) {}; + static void Combine(T & a_existing, const T & a_new) + { + UNUSED(a_existing); + UNUSED(a_new); + }; }; diff --git a/src/Protocol/Protocol.h b/src/Protocol/Protocol.h index 46b627254..b5560f7c1 100644 --- a/src/Protocol/Protocol.h +++ b/src/Protocol/Protocol.h @@ -13,6 +13,7 @@ #include "../Defines.h" #include "../Endianness.h" #include "../Scoreboard.h" +#include "../Map.h" @@ -82,6 +83,9 @@ public: virtual void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item) = 0; virtual void SendKeepAlive (int a_PingID) = 0; virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) = 0; + virtual void SendMapColumn (int a_ID, int a_X, int a_Y, const Byte * a_Colors, unsigned int a_Length) = 0; + virtual void SendMapDecorators (int a_ID, const cMapDecoratorList & a_Decorators) = 0; + virtual void SendMapInfo (int a_ID, unsigned int a_Scale) = 0; virtual void SendPaintingSpawn (const cPainting & a_Painting) = 0; virtual void SendPickupSpawn (const cPickup & a_Pickup) = 0; virtual void SendPlayerAbilities (void) = 0; diff --git a/src/Protocol/Protocol125.cpp b/src/Protocol/Protocol125.cpp index 7020699d1..3980350f5 100644 --- a/src/Protocol/Protocol125.cpp +++ b/src/Protocol/Protocol125.cpp @@ -97,6 +97,7 @@ enum PACKET_WINDOW_PROPERTY = 0x69, PACKET_CREATIVE_INVENTORY_ACTION = 0x6B, PACKET_UPDATE_SIGN = 0x82, + PACKET_ITEM_DATA = 0x83, PACKET_PLAYER_LIST_ITEM = 0xC9, PACKET_PLAYER_ABILITIES = 0xca, PACKET_PLUGIN_MESSAGE = 0xfa, @@ -614,6 +615,57 @@ void cProtocol125::SendLogin(const cPlayer & a_Player, const cWorld & a_World) +void cProtocol125::SendMapColumn(int a_ID, int a_X, int a_Y, const Byte * a_Colors, unsigned int a_Length) +{ + cCSLock Lock(m_CSPacket); + + WriteByte (PACKET_ITEM_DATA); + WriteShort(E_ITEM_MAP); + WriteShort(a_ID); + WriteShort(3 + a_Length); + + WriteByte(0); + WriteByte(a_X); + WriteByte(a_Y); + + for (unsigned int i = 0; i < a_Length; ++i) + { + WriteByte(a_Colors[i]); + } + + Flush(); +} + + + + + +void cProtocol125::SendMapDecorators(int a_ID, const cMapDecoratorList & a_Decorators) +{ + cCSLock Lock(m_CSPacket); + + WriteByte (PACKET_ITEM_DATA); + WriteShort(E_ITEM_MAP); + WriteShort(a_ID); + WriteShort(1 + (3 * a_Decorators.size())); + + WriteByte(1); + + for (cMapDecoratorList::const_iterator it = a_Decorators.begin(); it != a_Decorators.end(); ++it) + { + WriteByte((it->GetType() << 4) | (it->GetRot() & 0xf)); + WriteByte(it->GetPixelX()); + WriteByte(it->GetPixelZ()); + } + + Flush(); +} + + + + + + void cProtocol125::SendPickupSpawn(const cPickup & a_Pickup) { cCSLock Lock(m_CSPacket); diff --git a/src/Protocol/Protocol125.h b/src/Protocol/Protocol125.h index 54551ea5f..1d1484a60 100644 --- a/src/Protocol/Protocol125.h +++ b/src/Protocol/Protocol125.h @@ -55,6 +55,9 @@ public: virtual void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item) override; virtual void SendKeepAlive (int a_PingID) override; virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) override; + virtual void SendMapColumn (int a_ID, int a_X, int a_Y, const Byte * a_Colors, unsigned int a_Length) override; + virtual void SendMapDecorators (int a_ID, const cMapDecoratorList & a_Decorators) override; + virtual void SendMapInfo (int a_ID, unsigned int a_Scale) override {} // This protocol doesn't support such message virtual void SendParticleEffect (const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmmount) override; virtual void SendPaintingSpawn (const cPainting & a_Painting) override {}; virtual void SendPickupSpawn (const cPickup & a_Pickup) override; diff --git a/src/Protocol/Protocol17x.cpp b/src/Protocol/Protocol17x.cpp index aaf8830cd..992023464 100644 --- a/src/Protocol/Protocol17x.cpp +++ b/src/Protocol/Protocol17x.cpp @@ -587,6 +587,61 @@ void cProtocol172::SendPaintingSpawn(const cPainting & a_Painting) +void cProtocol172::SendMapColumn(int a_ID, int a_X, int a_Y, const Byte * a_Colors, unsigned int a_Length) +{ + cPacketizer Pkt(*this, 0x34); + Pkt.WriteVarInt(a_ID); + Pkt.WriteShort (3 + a_Length); + + Pkt.WriteByte(0); + Pkt.WriteByte(a_X); + Pkt.WriteByte(a_Y); + + for (unsigned int i = 0; i < a_Length; ++i) + { + Pkt.WriteByte(a_Colors[i]); + } +} + + + + + +void cProtocol172::SendMapDecorators(int a_ID, const cMapDecoratorList & a_Decorators) +{ + cPacketizer Pkt(*this, 0x34); + Pkt.WriteVarInt(a_ID); + Pkt.WriteShort (1 + (3 * a_Decorators.size())); + + Pkt.WriteByte(1); + + for (cMapDecoratorList::const_iterator it = a_Decorators.begin(); it != a_Decorators.end(); ++it) + { + Pkt.WriteByte((it->GetType() << 4) | (it->GetRot() & 0xf)); + Pkt.WriteByte(it->GetPixelX()); + Pkt.WriteByte(it->GetPixelZ()); + } +} + + + + + +void cProtocol172::SendMapInfo(int a_ID, unsigned int a_Scale) +{ + cPacketizer Pkt(*this, 0x34); + Pkt.WriteVarInt(a_ID); + Pkt.WriteShort (2); + + Pkt.WriteByte(2); + Pkt.WriteByte(a_Scale); +} + + + + + + void cProtocol172::SendPickupSpawn(const cPickup & a_Pickup) { { diff --git a/src/Protocol/Protocol17x.h b/src/Protocol/Protocol17x.h index ae3577867..113501568 100644 --- a/src/Protocol/Protocol17x.h +++ b/src/Protocol/Protocol17x.h @@ -87,6 +87,9 @@ public: virtual void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item) override; virtual void SendKeepAlive (int a_PingID) override; virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) override; + virtual void SendMapColumn (int a_ID, int a_X, int a_Y, const Byte * a_Colors, unsigned int a_Length) override; + virtual void SendMapDecorators (int a_ID, const cMapDecoratorList & a_Decorators) override; + virtual void SendMapInfo (int a_ID, unsigned int a_Scale) override; virtual void SendPaintingSpawn (const cPainting & a_Painting) override; virtual void SendPickupSpawn (const cPickup & a_Pickup) override; virtual void SendPlayerAbilities (void) override; diff --git a/src/Protocol/ProtocolRecognizer.cpp b/src/Protocol/ProtocolRecognizer.cpp index b658dc9db..84b052146 100644 --- a/src/Protocol/ProtocolRecognizer.cpp +++ b/src/Protocol/ProtocolRecognizer.cpp @@ -396,6 +396,36 @@ void cProtocolRecognizer::SendLogin(const cPlayer & a_Player, const cWorld & a_W +void cProtocolRecognizer::SendMapColumn(int a_ID, int a_X, int a_Y, const Byte * a_Colors, unsigned int a_Length) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendMapColumn(a_ID, a_X, a_Y, a_Colors, a_Length); +} + + + + + +void cProtocolRecognizer::SendMapDecorators(int a_ID, const cMapDecoratorList & a_Decorators) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendMapDecorators(a_ID, a_Decorators); +} + + + + + +void cProtocolRecognizer::SendMapInfo(int a_ID, unsigned int a_Scale) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendMapInfo(a_ID, a_Scale); +} + + + + + void cProtocolRecognizer::SendParticleEffect(const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmmount) { ASSERT(m_Protocol != NULL); diff --git a/src/Protocol/ProtocolRecognizer.h b/src/Protocol/ProtocolRecognizer.h index abbb22827..6aaafedeb 100644 --- a/src/Protocol/ProtocolRecognizer.h +++ b/src/Protocol/ProtocolRecognizer.h @@ -90,6 +90,9 @@ public: virtual void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item) override; virtual void SendKeepAlive (int a_PingID) override; virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) override; + virtual void SendMapColumn (int a_ID, int a_X, int a_Y, const Byte * a_Colors, unsigned int a_Length) override; + virtual void SendMapDecorators (int a_ID, const cMapDecoratorList & a_Decorators) override; + virtual void SendMapInfo (int a_ID, unsigned int a_Scale) override; virtual void SendParticleEffect (const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmmount) override; virtual void SendPaintingSpawn (const cPainting & a_Painting) override; virtual void SendPickupSpawn (const cPickup & a_Pickup) override; diff --git a/src/Server.cpp b/src/Server.cpp index c80348872..fcbcaa919 100644 --- a/src/Server.cpp +++ b/src/Server.cpp @@ -39,7 +39,9 @@ extern "C" { // For the "dumpmem" server command: /// Synchronize this with main.cpp - the leak finder needs initialization before it can be used to dump memory -#define ENABLE_LEAK_FINDER +// _X 2014_02_20: Disabled for canon repo, it makes the debug version too slow in MSVC2013 +// and we haven't had a memory leak for over a year anyway. +// #define ENABLE_LEAK_FINDER #if defined(_MSC_VER) && defined(_DEBUG) && defined(ENABLE_LEAK_FINDER) #pragma warning(push) diff --git a/src/Simulator/IncrementalRedstoneSimulator.cpp b/src/Simulator/IncrementalRedstoneSimulator.cpp index 1637ac02a..91de9e0cc 100644 --- a/src/Simulator/IncrementalRedstoneSimulator.cpp +++ b/src/Simulator/IncrementalRedstoneSimulator.cpp @@ -337,13 +337,13 @@ void cIncrementalRedstoneSimulator::WakeUp(int a_BlockX, int a_BlockY, int a_Blo ((a_BlockX % cChunkDef::Width) >= 14) || ((a_BlockZ % cChunkDef::Width) <= 1) || ((a_BlockZ % cChunkDef::Width) >= 14) - ) // Are we on a chunk boundary? ± 2 because of LinkedPowered blocks + ) // Are we on a chunk boundary? +- 2 because of LinkedPowered blocks { // On a chunk boundary, alert all four sides (i.e. at least one neighbouring chunk) AddBlock(a_BlockX, a_BlockY, a_BlockZ, a_Chunk); // Pass the original coordinates, because when adding things to our simulator lists, we get the chunk that they are in, and therefore any updates need to preseve their position - // RedstoneAddBlock to pass both the neighbouring chunk and the chunk which the coordiantes are in and ± 2 in GetNeighbour() to accomodate for LinkedPowered blocks being 2 away from chunk boundaries + // RedstoneAddBlock to pass both the neighbouring chunk and the chunk which the coordiantes are in and +- 2 in GetNeighbour() to accomodate for LinkedPowered blocks being 2 away from chunk boundaries RedstoneAddBlock(a_BlockX, a_BlockY, a_BlockZ, a_Chunk->GetNeighborChunk(a_BlockX - 2, a_BlockZ), a_Chunk); RedstoneAddBlock(a_BlockX, a_BlockY, a_BlockZ, a_Chunk->GetNeighborChunk(a_BlockX + 2, a_BlockZ), a_Chunk); RedstoneAddBlock(a_BlockX, a_BlockY, a_BlockZ, a_Chunk->GetNeighborChunk(a_BlockX, a_BlockZ - 2), a_Chunk); diff --git a/src/World.cpp b/src/World.cpp index 42c286c46..ffdae2a37 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -11,6 +11,8 @@ #include "ChunkMap.h" #include "Generating/ChunkDesc.h" #include "OSSupport/Timer.h" + +// Serializers #include "WorldStorage/ScoreboardSerializer.h" // Entities (except mobs): @@ -251,6 +253,7 @@ cWorld::cWorld(const AString & a_WorldName) : m_bCommandBlocksEnabled(false), m_bUseChatPrefixes(true), m_Scoreboard(this), + m_MapManager(this), m_GeneratorCallbacks(*this), m_TickThread(*this) { @@ -261,6 +264,8 @@ cWorld::cWorld(const AString & a_WorldName) : // Load the scoreboard cScoreboardSerializer Serializer(m_WorldName, &m_Scoreboard); Serializer.Load(); + + m_MapManager.LoadMapData(); } @@ -284,6 +289,8 @@ cWorld::~cWorld() cScoreboardSerializer Serializer(m_WorldName, &m_Scoreboard); Serializer.Save(); + m_MapManager.SaveMapData(); + delete m_ChunkMap; } @@ -3025,6 +3032,7 @@ cFluidSimulator * cWorld::InitializeFluidSimulator(cIniFile & a_IniFile, const c + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // cWorld::cTaskSaveAllChunks: diff --git a/src/World.h b/src/World.h index 5c18c5d23..4b74f7aba 100644 --- a/src/World.h +++ b/src/World.h @@ -24,6 +24,7 @@ #include "Entities/ProjectileEntity.h" #include "ForEachChunkProvider.h" #include "Scoreboard.h" +#include "MapManager.h" #include "Blocks/WorldInterface.h" #include "Blocks/BroadcastInterface.h" @@ -580,9 +581,12 @@ public: /** Returns the name of the world.ini file used by this world */ const AString & GetIniFileName(void) const {return m_IniFileName; } - /** Returns the associated scoreboard instance */ + /** Returns the associated scoreboard instance. */ cScoreboard & GetScoreBoard(void) { return m_Scoreboard; } + /** Returns the associated map manager instance. */ + cMapManager & GetMapManager(void) { return m_MapManager; } + bool AreCommandBlocksEnabled(void) const { return m_bCommandBlocksEnabled; } void SetCommandBlocksEnabled(bool a_Flag) { m_bCommandBlocksEnabled = a_Flag; } @@ -848,6 +852,7 @@ private: cChunkGenerator m_Generator; cScoreboard m_Scoreboard; + cMapManager m_MapManager; /** The callbacks that the ChunkGenerator uses to store new chunks and interface to plugins */ cChunkGeneratorCallbacks m_GeneratorCallbacks; diff --git a/src/WorldStorage/FastNBT.h b/src/WorldStorage/FastNBT.h index b84eda1a1..49f97c458 100644 --- a/src/WorldStorage/FastNBT.h +++ b/src/WorldStorage/FastNBT.h @@ -172,8 +172,18 @@ public: inline float GetFloat(int a_Tag) const { ASSERT(m_Tags[a_Tag].m_Type == TAG_Float); - Int32 tmp = GetBEInt(m_Data + m_Tags[a_Tag].m_DataStart); - return *((float *)&tmp); + + // Cause a compile-time error if sizeof(float) != 4 + // If your platform produces a compiler error here, you'll need to add code that manually decodes 32-bit floats + char Check1[5 - sizeof(float)]; // sizeof(float) <= 4 + char Check2[sizeof(float) - 3]; // sizeof(float) >= 4 + UNUSED(Check1); + UNUSED(Check2); + + Int32 i = GetBEInt(m_Data + m_Tags[a_Tag].m_DataStart); + float f; + memcpy(&f, &i, sizeof(f)); + return f; } inline double GetDouble(int a_Tag) const diff --git a/src/WorldStorage/MapSerializer.cpp b/src/WorldStorage/MapSerializer.cpp new file mode 100644 index 000000000..a4a0aab57 --- /dev/null +++ b/src/WorldStorage/MapSerializer.cpp @@ -0,0 +1,276 @@ + +// MapSerializer.cpp + + +#include "Globals.h" +#include "MapSerializer.h" +#include "../StringCompression.h" +#include "zlib/zlib.h" +#include "FastNBT.h" + +#include "../Map.h" +#include "../World.h" + + + + + +cMapSerializer::cMapSerializer(const AString& a_WorldName, cMap * a_Map) + : m_Map(a_Map) +{ + AString DataPath; + Printf(DataPath, "%s/data", a_WorldName.c_str()); + + Printf(m_Path, "%s/map_%i.dat", DataPath.c_str(), a_Map->GetID()); + + cFile::CreateFolder(FILE_IO_PREFIX + DataPath); +} + + + + + +bool cMapSerializer::Load(void) +{ + AString Data = cFile::ReadWholeFile(FILE_IO_PREFIX + m_Path); + if (Data.empty()) + { + return false; + } + + AString Uncompressed; + int res = UncompressStringGZIP(Data.data(), Data.size(), Uncompressed); + + if (res != Z_OK) + { + return false; + } + + // Parse the NBT data: + cParsedNBT NBT(Uncompressed.data(), Uncompressed.size()); + if (!NBT.IsValid()) + { + // NBT Parsing failed + return false; + } + + return LoadMapFromNBT(NBT); +} + + + + + +bool cMapSerializer::Save(void) +{ + cFastNBTWriter Writer; + + SaveMapToNBT(Writer); + + Writer.Finish(); + + #ifdef _DEBUG + cParsedNBT TestParse(Writer.GetResult().data(), Writer.GetResult().size()); + ASSERT(TestParse.IsValid()); + #endif // _DEBUG + + cFile File; + if (!File.Open(FILE_IO_PREFIX + m_Path, cFile::fmWrite)) + { + return false; + } + + AString Compressed; + int res = CompressStringGZIP(Writer.GetResult().data(), Writer.GetResult().size(), Compressed); + + if (res != Z_OK) + { + return false; + } + + File.Write(Compressed.data(), Compressed.size()); + File.Close(); + + return true; +} + + + + + +void cMapSerializer::SaveMapToNBT(cFastNBTWriter & a_Writer) +{ + a_Writer.BeginCompound("data"); + + a_Writer.AddByte("scale", m_Map->GetScale()); + a_Writer.AddByte("dimension", (int) m_Map->GetDimension()); + + a_Writer.AddShort("width", m_Map->GetWidth()); + a_Writer.AddShort("height", m_Map->GetHeight()); + + a_Writer.AddInt("xCenter", m_Map->GetCenterX()); + a_Writer.AddInt("zCenter", m_Map->GetCenterZ()); + + const cMap::cColorList & Data = m_Map->GetData(); + a_Writer.AddByteArray("colors", (char *) &Data[0], Data.size()); + + a_Writer.EndCompound(); +} + + + + + +bool cMapSerializer::LoadMapFromNBT(const cParsedNBT & a_NBT) +{ + int Data = a_NBT.FindChildByName(0, "data"); + if (Data < 0) + { + return false; + } + + int CurrLine = a_NBT.FindChildByName(Data, "scale"); + if (CurrLine >= 0) + { + unsigned int Scale = a_NBT.GetByte(CurrLine); + m_Map->SetScale(Scale); + } + + CurrLine = a_NBT.FindChildByName(Data, "dimension"); + if (CurrLine >= 0) + { + eDimension Dimension = (eDimension) a_NBT.GetByte(CurrLine); + + ASSERT(Dimension == m_Map->m_World->GetDimension()); + } + + CurrLine = a_NBT.FindChildByName(Data, "width"); + if (CurrLine >= 0) + { + unsigned int Width = a_NBT.GetShort(CurrLine); + m_Map->m_Width = Width; + } + + CurrLine = a_NBT.FindChildByName(Data, "height"); + if (CurrLine >= 0) + { + unsigned int Height = a_NBT.GetShort(CurrLine); + m_Map->m_Height = Height; + } + + CurrLine = a_NBT.FindChildByName(Data, "xCenter"); + if (CurrLine >= 0) + { + int CenterX = a_NBT.GetInt(CurrLine); + m_Map->m_CenterX = CenterX; + } + + CurrLine = a_NBT.FindChildByName(Data, "zCenter"); + if (CurrLine >= 0) + { + int CenterZ = a_NBT.GetInt(CurrLine); + m_Map->m_CenterZ = CenterZ; + } + + unsigned int NumPixels = m_Map->GetNumPixels(); + m_Map->m_Data.resize(NumPixels); + + CurrLine = a_NBT.FindChildByName(Data, "colors"); + if ((CurrLine >= 0) && (a_NBT.GetType(CurrLine) == TAG_ByteArray)) + { + memcpy(&m_Map->m_Data[0], a_NBT.GetData(CurrLine), NumPixels); + } + + return true; +} + + + + + +cIDCountSerializer::cIDCountSerializer(const AString & a_WorldName) : m_MapCount(0) +{ + AString DataPath; + Printf(DataPath, "%s/data", a_WorldName.c_str()); + + Printf(m_Path, "%s/idcounts.dat", DataPath.c_str()); + + cFile::CreateFolder(FILE_IO_PREFIX + DataPath); +} + + + + + +bool cIDCountSerializer::Load(void) +{ + AString Data = cFile::ReadWholeFile(FILE_IO_PREFIX + m_Path); + if (Data.empty()) + { + return false; + } + + // NOTE: idcounts.dat is not compressed (raw format) + + // Parse the NBT data: + cParsedNBT NBT(Data.data(), Data.size()); + if (!NBT.IsValid()) + { + // NBT Parsing failed + return false; + } + + int CurrLine = NBT.FindChildByName(0, "map"); + if (CurrLine >= 0) + { + m_MapCount = (int)NBT.GetShort(CurrLine) + 1; + } + else + { + m_MapCount = 0; + } + + return true; +} + + + + + +bool cIDCountSerializer::Save(void) +{ + cFastNBTWriter Writer; + + if (m_MapCount > 0) + { + Writer.AddShort("map", m_MapCount - 1); + } + + Writer.Finish(); + + #ifdef _DEBUG + cParsedNBT TestParse(Writer.GetResult().data(), Writer.GetResult().size()); + ASSERT(TestParse.IsValid()); + #endif // _DEBUG + + cFile File; + if (!File.Open(FILE_IO_PREFIX + m_Path, cFile::fmWrite)) + { + return false; + } + + // NOTE: idcounts.dat is not compressed (raw format) + + File.Write(Writer.GetResult().data(), Writer.GetResult().size()); + File.Close(); + + return true; +} + + + + + + + + diff --git a/src/WorldStorage/MapSerializer.h b/src/WorldStorage/MapSerializer.h new file mode 100644 index 000000000..eb7678a08 --- /dev/null +++ b/src/WorldStorage/MapSerializer.h @@ -0,0 +1,86 @@ + +// MapSerializer.h + +// Declares the cMapSerializer class that is used for saving maps into NBT format used by Anvil + + + + + +#pragma once + + + + + +// fwd: +class cFastNBTWriter; +class cParsedNBT; +class cMap; + + + + +/** Utility class used to serialize maps. */ +class cMapSerializer +{ +public: + + cMapSerializer(const AString& a_WorldName, cMap * a_Map); + + /** Try to load the scoreboard */ + bool Load(void); + + /** Try to save the scoreboard */ + bool Save(void); + + +private: + + void SaveMapToNBT(cFastNBTWriter & a_Writer); + + bool LoadMapFromNBT(const cParsedNBT & a_NBT); + + cMap * m_Map; + + AString m_Path; + + +} ; + + + + +/** Utility class used to serialize item ID counts. + * + * In order to perform bounds checking (while loading), + * the last registered ID of each item is serialized to an NBT file. + */ +class cIDCountSerializer +{ +public: + + cIDCountSerializer(const AString & a_WorldName); + + /** Try to load the ID counts */ + bool Load(void); + + /** Try to save the ID counts */ + bool Save(void); + + inline unsigned int GetMapCount(void) const { return m_MapCount; } + + inline void SetMapCount(unsigned int a_MapCount) { m_MapCount = a_MapCount; } + + +private: + + AString m_Path; + + unsigned int m_MapCount; + +}; + + + + diff --git a/src/WorldStorage/NBTChunkSerializer.cpp b/src/WorldStorage/NBTChunkSerializer.cpp index 2a1eda523..c1c659b36 100644 --- a/src/WorldStorage/NBTChunkSerializer.cpp +++ b/src/WorldStorage/NBTChunkSerializer.cpp @@ -409,6 +409,14 @@ void cNBTChunkSerializer::AddMonsterEntity(cMonster * a_Monster) m_Writer.BeginCompound(""); AddBasicEntity(a_Monster, EntityClass); + m_Writer.BeginList("DropChances", TAG_Float); + m_Writer.AddFloat("", a_Monster->GetDropChanceWeapon()); + m_Writer.AddFloat("", a_Monster->GetDropChanceHelmet()); + m_Writer.AddFloat("", a_Monster->GetDropChanceChestplate()); + m_Writer.AddFloat("", a_Monster->GetDropChanceLeggings()); + m_Writer.AddFloat("", a_Monster->GetDropChanceBoots()); + m_Writer.EndList(); + m_Writer.AddByte("CanPickUpLoot", (char)a_Monster->CanPickUpLoot()); switch (a_Monster->GetMobType()) { case cMonster::mtBat: diff --git a/src/WorldStorage/WSSAnvil.cpp b/src/WorldStorage/WSSAnvil.cpp index d4490c7fe..05332d23d 100644 --- a/src/WorldStorage/WSSAnvil.cpp +++ b/src/WorldStorage/WSSAnvil.cpp @@ -1485,6 +1485,11 @@ void cWSSAnvil::LoadBatFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NB { return; } + + if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) + { + return; + } a_Entities.push_back(Monster.release()); } @@ -1500,6 +1505,11 @@ void cWSSAnvil::LoadBlazeFromNBT(cEntityList & a_Entities, const cParsedNBT & a_ { return; } + + if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) + { + return; + } a_Entities.push_back(Monster.release()); } @@ -1515,6 +1525,11 @@ void cWSSAnvil::LoadCaveSpiderFromNBT(cEntityList & a_Entities, const cParsedNBT { return; } + + if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) + { + return; + } a_Entities.push_back(Monster.release()); } @@ -1530,6 +1545,11 @@ void cWSSAnvil::LoadChickenFromNBT(cEntityList & a_Entities, const cParsedNBT & { return; } + + if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) + { + return; + } a_Entities.push_back(Monster.release()); } @@ -1545,6 +1565,11 @@ void cWSSAnvil::LoadCowFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NB { return; } + + if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) + { + return; + } a_Entities.push_back(Monster.release()); } @@ -1560,6 +1585,11 @@ void cWSSAnvil::LoadCreeperFromNBT(cEntityList & a_Entities, const cParsedNBT & { return; } + + if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) + { + return; + } a_Entities.push_back(Monster.release()); } @@ -1575,6 +1605,11 @@ void cWSSAnvil::LoadEnderDragonFromNBT(cEntityList & a_Entities, const cParsedNB { return; } + + if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) + { + return; + } a_Entities.push_back(Monster.release()); } @@ -1591,6 +1626,11 @@ void cWSSAnvil::LoadEndermanFromNBT(cEntityList & a_Entities, const cParsedNBT & return; } + if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) + { + return; + } + a_Entities.push_back(Monster.release()); } @@ -1606,6 +1646,11 @@ void cWSSAnvil::LoadGhastFromNBT(cEntityList & a_Entities, const cParsedNBT & a_ return; } + if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) + { + return; + } + a_Entities.push_back(Monster.release()); } @@ -1620,6 +1665,11 @@ void cWSSAnvil::LoadGiantFromNBT(cEntityList & a_Entities, const cParsedNBT & a_ { return; } + + if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) + { + return; + } a_Entities.push_back(Monster.release()); } @@ -1646,6 +1696,11 @@ void cWSSAnvil::LoadHorseFromNBT(cEntityList & a_Entities, const cParsedNBT & a_ { return; } + + if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) + { + return; + } a_Entities.push_back(Monster.release()); } @@ -1661,6 +1716,11 @@ void cWSSAnvil::LoadIronGolemFromNBT(cEntityList & a_Entities, const cParsedNBT { return; } + + if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) + { + return; + } a_Entities.push_back(Monster.release()); } @@ -1682,6 +1742,11 @@ void cWSSAnvil::LoadMagmaCubeFromNBT(cEntityList & a_Entities, const cParsedNBT { return; } + + if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) + { + return; + } a_Entities.push_back(Monster.release()); } @@ -1697,6 +1762,11 @@ void cWSSAnvil::LoadMooshroomFromNBT(cEntityList & a_Entities, const cParsedNBT { return; } + + if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) + { + return; + } a_Entities.push_back(Monster.release()); } @@ -1712,6 +1782,11 @@ void cWSSAnvil::LoadOcelotFromNBT(cEntityList & a_Entities, const cParsedNBT & a { return; } + + if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) + { + return; + } a_Entities.push_back(Monster.release()); } @@ -1727,6 +1802,11 @@ void cWSSAnvil::LoadPigFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NB { return; } + + if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) + { + return; + } a_Entities.push_back(Monster.release()); } @@ -1748,6 +1828,11 @@ void cWSSAnvil::LoadSheepFromNBT(cEntityList & a_Entities, const cParsedNBT & a_ { return; } + + if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) + { + return; + } a_Entities.push_back(Monster.release()); } @@ -1763,6 +1848,11 @@ void cWSSAnvil::LoadSilverfishFromNBT(cEntityList & a_Entities, const cParsedNBT { return; } + + if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) + { + return; + } a_Entities.push_back(Monster.release()); } @@ -1784,6 +1874,11 @@ void cWSSAnvil::LoadSkeletonFromNBT(cEntityList & a_Entities, const cParsedNBT & { return; } + + if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) + { + return; + } a_Entities.push_back(Monster.release()); } @@ -1805,6 +1900,11 @@ void cWSSAnvil::LoadSlimeFromNBT(cEntityList & a_Entities, const cParsedNBT & a_ { return; } + + if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) + { + return; + } a_Entities.push_back(Monster.release()); } @@ -1820,6 +1920,11 @@ void cWSSAnvil::LoadSnowGolemFromNBT(cEntityList & a_Entities, const cParsedNBT { return; } + + if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) + { + return; + } a_Entities.push_back(Monster.release()); } @@ -1835,6 +1940,11 @@ void cWSSAnvil::LoadSpiderFromNBT(cEntityList & a_Entities, const cParsedNBT & a { return; } + + if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) + { + return; + } a_Entities.push_back(Monster.release()); } @@ -1850,6 +1960,11 @@ void cWSSAnvil::LoadSquidFromNBT(cEntityList & a_Entities, const cParsedNBT & a_ { return; } + + if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) + { + return; + } a_Entities.push_back(Monster.release()); } @@ -1871,6 +1986,11 @@ void cWSSAnvil::LoadVillagerFromNBT(cEntityList & a_Entities, const cParsedNBT & { return; } + + if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) + { + return; + } a_Entities.push_back(Monster.release()); } @@ -1886,6 +2006,11 @@ void cWSSAnvil::LoadWitchFromNBT(cEntityList & a_Entities, const cParsedNBT & a_ { return; } + + if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) + { + return; + } a_Entities.push_back(Monster.release()); } @@ -1901,6 +2026,11 @@ void cWSSAnvil::LoadWitherFromNBT(cEntityList & a_Entities, const cParsedNBT & a { return; } + + if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) + { + return; + } a_Entities.push_back(Monster.release()); } @@ -1916,6 +2046,10 @@ void cWSSAnvil::LoadWolfFromNBT(cEntityList & a_Entities, const cParsedNBT & a_N { return; } + if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) + { + return; + } int OwnerIdx = a_NBT.FindChildByName(a_TagIdx, "Owner"); if (OwnerIdx > 0) { @@ -1964,6 +2098,11 @@ void cWSSAnvil::LoadZombieFromNBT(cEntityList & a_Entities, const cParsedNBT & a { return; } + + if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) + { + return; + } a_Entities.push_back(Monster.release()); } @@ -1980,6 +2119,11 @@ void cWSSAnvil::LoadPigZombieFromNBT(cEntityList & a_Entities, const cParsedNBT return; } + if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) + { + return; + } + a_Entities.push_back(Monster.release()); } @@ -2023,6 +2167,27 @@ bool cWSSAnvil::LoadEntityBaseFromNBT(cEntity & a_Entity, const cParsedNBT & a_N +bool cWSSAnvil::LoadMonsterBaseFromNBT(cMonster & a_Monster, const cParsedNBT & a_NBT, int a_TagIdx) +{ + float DropChance[5]; + if (!LoadFloatsListFromNBT(DropChance, 5, a_NBT, a_NBT.FindChildByName(a_TagIdx, "DropChance"))) + { + return false; + } + a_Monster.SetDropChanceWeapon(DropChance[0]); + a_Monster.SetDropChanceHelmet(DropChance[1]); + a_Monster.SetDropChanceChestplate(DropChance[2]); + a_Monster.SetDropChanceLeggings(DropChance[3]); + a_Monster.SetDropChanceBoots(DropChance[4]); + bool CanPickUpLoot = (a_NBT.GetByte(a_NBT.FindChildByName(a_TagIdx, "CanPickUpLoot")) == 1); + a_Monster.SetCanPickUpLoot(CanPickUpLoot); + return true; +} + + + + + bool cWSSAnvil::LoadProjectileBaseFromNBT(cProjectileEntity & a_Entity, const cParsedNBT & a_NBT, int a_TagIdx) { if (!LoadEntityBaseFromNBT(a_Entity, a_NBT, a_TagIdx)) @@ -2065,6 +2230,24 @@ bool cWSSAnvil::LoadDoublesListFromNBT(double * a_Doubles, int a_NumDoubles, con +bool cWSSAnvil::LoadFloatsListFromNBT(float * a_Floats, int a_NumFloats, const cParsedNBT & a_NBT, int a_TagIdx) +{ + if ((a_TagIdx < 0) || (a_NBT.GetType(a_TagIdx) != TAG_List) || (a_NBT.GetChildrenType(a_TagIdx) != TAG_Float)) + { + return false; + } + int idx = 0; + for (int Tag = a_NBT.GetFirstChild(a_TagIdx); (Tag > 0) && (idx < a_NumFloats); Tag = a_NBT.GetNextSibling(Tag), ++idx) + { + a_Floats[idx] = a_NBT.GetFloat(Tag); + } // for Tag - PosTag[] + return (idx == a_NumFloats); // 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"); diff --git a/src/WorldStorage/WSSAnvil.h b/src/WorldStorage/WSSAnvil.h index 541371560..4acf3f2a1 100644 --- a/src/WorldStorage/WSSAnvil.h +++ b/src/WorldStorage/WSSAnvil.h @@ -10,6 +10,7 @@ #include "WorldStorage.h" #include "FastNBT.h" +#include "../Mobs/Monster.h" @@ -194,12 +195,18 @@ protected: /// 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 monster common data from the NBT compound; returns true if successful + bool LoadMonsterBaseFromNBT(cMonster & a_Monster, const cParsedNBT & a_NBT, int a_TagIdx); + /// Loads projectile common data from the NBT compound; returns true if successful bool LoadProjectileBaseFromNBT(cProjectileEntity & a_Entity, const cParsedNBT & a_NBT, int a_TagIx); /// 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); + /// Loads an array of floats of the specified length from the specified NBT list tag a_TagIdx; returns true if successful + bool LoadFloatsListFromNBT(float * a_Floats, int a_NumFloats, 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); diff --git a/src/main.cpp b/src/main.cpp index c8cd2d4fe..2ae8a413b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -30,7 +30,9 @@ bool g_ShouldLogCommOut; /// If defined, a thorough leak finder will be used (debug MSVC only); leaks will be output to the Output window -#define ENABLE_LEAK_FINDER +// _X 2014_02_20: Disabled for canon repo, it makes the debug version too slow in MSVC2013 +// and we haven't had a memory leak for over a year anyway. +// #define ENABLE_LEAK_FINDER @@ -241,31 +243,36 @@ int main( int argc, char **argv ) // Check if comm logging is to be enabled: for (int i = 0; i < argc; i++) { + AString Arg(argv[i]); if ( - (NoCaseCompare(argv[i], "/commlog") == 0) || - (NoCaseCompare(argv[i], "/logcomm") == 0) + (NoCaseCompare(Arg, "/commlog") == 0) || + (NoCaseCompare(Arg, "/logcomm") == 0) ) { g_ShouldLogCommIn = true; g_ShouldLogCommOut = true; } - if ( - (NoCaseCompare(argv[i], "/commlogin") == 0) || - (NoCaseCompare(argv[i], "/comminlog") == 0) || - (NoCaseCompare(argv[i], "/logcommin") == 0) + else if ( + (NoCaseCompare(Arg, "/commlogin") == 0) || + (NoCaseCompare(Arg, "/comminlog") == 0) || + (NoCaseCompare(Arg, "/logcommin") == 0) ) { g_ShouldLogCommIn = true; } - if ( - (NoCaseCompare(argv[i], "/commlogout") == 0) || - (NoCaseCompare(argv[i], "/commoutlog") == 0) || - (NoCaseCompare(argv[i], "/logcommout") == 0) + else if ( + (NoCaseCompare(Arg, "/commlogout") == 0) || + (NoCaseCompare(Arg, "/commoutlog") == 0) || + (NoCaseCompare(Arg, "/logcommout") == 0) ) { g_ShouldLogCommOut = true; } - } + else if (NoCaseCompare(Arg, "nooutbuf") == 0) + { + setvbuf(stdout, NULL, _IONBF, 0); + } + } // for i - argv[] #if !defined(ANDROID_NDK) try |