summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Bindings/CMakeLists.txt1
-rw-r--r--src/Bindings/LuaState.cpp24
-rw-r--r--src/Bindings/LuaState.h4
-rw-r--r--src/Bindings/ManualBindings.cpp373
-rw-r--r--src/Bindings/ManualBindings.h96
-rw-r--r--src/Bindings/ManualBindings_BlockArea.cpp998
-rw-r--r--src/BlockArea.cpp1155
-rw-r--r--src/BlockArea.h141
-rw-r--r--src/BlockEntities/BlockEntity.cpp51
-rw-r--r--src/BlockEntities/BlockEntity.h8
-rw-r--r--src/Chunk.cpp58
11 files changed, 2234 insertions, 675 deletions
diff --git a/src/Bindings/CMakeLists.txt b/src/Bindings/CMakeLists.txt
index 2614ef9b2..747f22157 100644
--- a/src/Bindings/CMakeLists.txt
+++ b/src/Bindings/CMakeLists.txt
@@ -16,6 +16,7 @@ SET (SRCS
LuaUDPEndpoint.cpp
LuaWindow.cpp
ManualBindings.cpp
+ ManualBindings_BlockArea.cpp
ManualBindings_Network.cpp
ManualBindings_RankManager.cpp
ManualBindings_World.cpp
diff --git a/src/Bindings/LuaState.cpp b/src/Bindings/LuaState.cpp
index 947e337fc..d18b6efcd 100644
--- a/src/Bindings/LuaState.cpp
+++ b/src/Bindings/LuaState.cpp
@@ -1762,6 +1762,30 @@ bool cLuaState::CheckParamEnd(int a_Param)
+bool cLuaState::CheckParamSelf(const char * a_SelfClassName)
+{
+ tolua_Error tolua_err;
+ if (tolua_isusertype(m_LuaState, 1, a_SelfClassName, 0, &tolua_err) && !lua_isnil(m_LuaState, 1))
+ {
+ return true;
+ }
+
+ // Not the correct parameter
+ lua_Debug entry;
+ VERIFY(lua_getstack(m_LuaState, 0, &entry));
+ VERIFY(lua_getinfo (m_LuaState, "n", &entry));
+ AString ErrMsg = Printf(
+ "Error in function '%s'. The 'self' parameter is not of the expected type, \"instance of %s\". Make sure you're using the correct calling convention (obj:fn() instead of obj.fn()).",
+ (entry.name != nullptr) ? entry.name : "<unknown>", a_SelfClassName
+ );
+ tolua_error(m_LuaState, ErrMsg.c_str(), &tolua_err);
+ return false;
+}
+
+
+
+
+
bool cLuaState::IsParamUserType(int a_Param, AString a_UserType)
{
ASSERT(IsValid());
diff --git a/src/Bindings/LuaState.h b/src/Bindings/LuaState.h
index 4320ce40e..674c7a240 100644
--- a/src/Bindings/LuaState.h
+++ b/src/Bindings/LuaState.h
@@ -788,6 +788,10 @@ public:
/** Returns true if the specified parameter on the stack is nil (indicating an end-of-parameters) */
bool CheckParamEnd(int a_Param);
+ /** Returns true if the first parameter is an instance of the expected class name.
+ Returns false and logs a special warning ("wrong calling convention") if not. */
+ bool CheckParamSelf(const char * a_SelfClassName);
+
bool IsParamUserType(int a_Param, AString a_UserType);
bool IsParamNumber(int a_Param);
diff --git a/src/Bindings/ManualBindings.cpp b/src/Bindings/ManualBindings.cpp
index d8bd8b023..2c2de6296 100644
--- a/src/Bindings/ManualBindings.cpp
+++ b/src/Bindings/ManualBindings.cpp
@@ -31,7 +31,6 @@
#include "../BlockEntities/FlowerPotEntity.h"
#include "../Generating/ChunkDesc.h"
#include "../LineBlockTracer.h"
-#include "../WorldStorage/SchematicFileSerializer.h"
#include "../CompositeChat.h"
#include "../StringCompression.h"
#include "../CommandOutput.h"
@@ -133,6 +132,46 @@ int cManualBindings::lua_do_error(lua_State * L, const char * a_pFormat, ...)
+int cManualBindings::ApiParamError(lua_State * a_LuaState, const char * a_MsgFormat, ...)
+{
+ // Retrieve current function name
+ lua_Debug entry;
+ VERIFY(lua_getstack(a_LuaState, 0, &entry));
+ VERIFY(lua_getinfo(a_LuaState, "n", &entry));
+
+ // Compose the error message:
+ va_list argp;
+ va_start(argp, a_MsgFormat);
+ AString msg;
+
+ #ifdef __clang__
+ #pragma clang diagnostic push
+ #pragma clang diagnostic ignored "-Wformat-nonliteral"
+ #endif
+
+ AppendVPrintf(msg, a_MsgFormat, argp);
+
+ #ifdef __clang__
+ #pragma clang diagnostic pop
+ #endif
+
+ va_end(argp);
+ AString errorMsg = Printf("%s: %s", (entry.name != nullptr) ? entry.name : "<unknown function>", msg.c_str());
+
+ // Log everything into the console:
+ LOGWARNING("%s", errorMsg.c_str());
+ // cLuaState::LogStackTrace(a_LuaState); // Do NOT log stack trace, it is already output as part of the Lua error handling
+ cLuaState::LogStackValues(a_LuaState, "Parameters on the stack");
+
+ // Raise Lua error:
+ lua_pushstring(a_LuaState, errorMsg.c_str());
+ return lua_error(a_LuaState);
+}
+
+
+
+
+
// Lua bound functions with special return types
static int tolua_Clamp(lua_State * tolua_S)
{
@@ -3199,324 +3238,6 @@ static int tolua_cHopperEntity_GetOutputBlockPos(lua_State * tolua_S)
-static int tolua_cBlockArea_GetBlockTypeMeta(lua_State * tolua_S)
-{
- // function cBlockArea::GetBlockTypeMeta()
- // Exported manually because tolua generates extra input params for the outputs
-
- cLuaState L(tolua_S);
- if (
- !L.CheckParamUserType(1, "cBlockArea") ||
- !L.CheckParamNumber (2, 4)
- )
- {
- return 0;
- }
-
- cBlockArea * self = reinterpret_cast<cBlockArea *>(tolua_tousertype(tolua_S, 1, nullptr));
- if (self == nullptr)
- {
- tolua_error(tolua_S, "invalid 'self' in function 'cBlockArea:GetRelBlockTypeMeta'", nullptr);
- return 0;
- }
- int BlockX = static_cast<int>(tolua_tonumber(tolua_S, 2, 0));
- int BlockY = static_cast<int>(tolua_tonumber(tolua_S, 3, 0));
- int BlockZ = static_cast<int>(tolua_tonumber(tolua_S, 4, 0));
- BLOCKTYPE BlockType;
- NIBBLETYPE BlockMeta;
- self->GetBlockTypeMeta(BlockX, BlockY, BlockZ, BlockType, BlockMeta);
- tolua_pushnumber(tolua_S, BlockType);
- tolua_pushnumber(tolua_S, BlockMeta);
- return 2;
-}
-
-
-
-
-
-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 = reinterpret_cast<cBlockArea *>(tolua_tousertype(tolua_S, 1, nullptr));
- if (self == nullptr)
- {
- tolua_error(tolua_S, "invalid 'self' in function 'cBlockArea:GetOrigin'", nullptr);
- 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_GetNonAirCropRelCoords(lua_State * tolua_S)
-{
- // function cBlockArea::GetNonAirCropRelCoords()
- // Exported manually because tolua would generate extra input params for the outputs
-
- cLuaState L(tolua_S);
- if (!L.CheckParamUserType(1, "cBlockArea"))
- {
- return 0;
- }
-
- cBlockArea * self = nullptr;
- BLOCKTYPE IgnoreBlockType = E_BLOCK_AIR;
- L.GetStackValues(1, self, IgnoreBlockType);
- if (self == nullptr)
- {
- tolua_error(tolua_S, "invalid 'self' in function 'cBlockArea:GetNonAirCropRelCoords'", nullptr);
- return 0;
- }
-
- // Calculate the crop coords:
- int MinRelX, MinRelY, MinRelZ, MaxRelX, MaxRelY, MaxRelZ;
- self->GetNonAirCropRelCoords(MinRelX, MinRelY, MinRelZ, MaxRelX, MaxRelY, MaxRelZ, IgnoreBlockType);
-
- // Push the six crop coords:
- L.Push(MinRelX, MinRelY, MinRelZ, MaxRelX, MaxRelY, MaxRelZ);
- return 6;
-}
-
-
-
-
-
-static int tolua_cBlockArea_GetRelBlockTypeMeta(lua_State * tolua_S)
-{
- // function cBlockArea::GetRelBlockTypeMeta()
- // Exported manually because tolua generates extra input params for the outputs
-
- cLuaState L(tolua_S);
- if (
- !L.CheckParamUserType(1, "cBlockArea") ||
- !L.CheckParamNumber (2, 4)
- )
- {
- return 0;
- }
-
- cBlockArea * self = reinterpret_cast<cBlockArea *>(tolua_tousertype(tolua_S, 1, nullptr));
- if (self == nullptr)
- {
- tolua_error(tolua_S, "invalid 'self' in function 'cBlockArea:GetRelBlockTypeMeta'", nullptr);
- return 0;
- }
- int BlockX = static_cast<int>(tolua_tonumber(tolua_S, 2, 0));
- int BlockY = static_cast<int>(tolua_tonumber(tolua_S, 3, 0));
- int BlockZ = static_cast<int>(tolua_tonumber(tolua_S, 4, 0));
- BLOCKTYPE BlockType;
- NIBBLETYPE BlockMeta;
- self->GetRelBlockTypeMeta(BlockX, BlockY, BlockZ, BlockType, BlockMeta);
- tolua_pushnumber(tolua_S, BlockType);
- tolua_pushnumber(tolua_S, BlockMeta);
- return 2;
-}
-
-
-
-
-
-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 = reinterpret_cast<cBlockArea *>(tolua_tousertype(tolua_S, 1, nullptr));
- if (self == nullptr)
- {
- tolua_error(tolua_S, "invalid 'self' in function 'cBlockArea:GetSize'", nullptr);
- 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_GetCoordRange(lua_State * tolua_S)
-{
- // function cBlockArea::GetCoordRange()
- // Returns all three sizes of the area, miuns one, so that they represent the maximum coord value
- // 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 = reinterpret_cast<cBlockArea *>(tolua_tousertype(tolua_S, 1, nullptr));
- if (self == nullptr)
- {
- tolua_error(tolua_S, "invalid 'self' in function 'cBlockArea:GetSize'", nullptr);
- return 0;
- }
-
- // Push the three origin coords:
- lua_pushnumber(tolua_S, self->GetSizeX() - 1);
- lua_pushnumber(tolua_S, self->GetSizeY() - 1);
- lua_pushnumber(tolua_S, self->GetSizeZ() - 1);
- return 3;
-}
-
-
-
-
-
-static int tolua_cBlockArea_LoadFromSchematicFile(lua_State * tolua_S)
-{
- // function cBlockArea::LoadFromSchematicFile
- // Exported manually because function has been moved to SchematicFileSerializer.cpp
- cLuaState L(tolua_S);
- if (
- !L.CheckParamUserType(1, "cBlockArea") ||
- !L.CheckParamString (2) ||
- !L.CheckParamEnd (3)
- )
- {
- return 0;
- }
- cBlockArea * self = reinterpret_cast<cBlockArea *>(tolua_tousertype(tolua_S, 1, nullptr));
- if (self == nullptr)
- {
- tolua_error(tolua_S, "invalid 'self' in function 'cBlockArea::LoadFromSchematicFile'", nullptr);
- return 0;
- }
-
- AString Filename = tolua_tostring(tolua_S, 2, 0);
- bool res = cSchematicFileSerializer::LoadFromSchematicFile(*self, Filename);
- tolua_pushboolean(tolua_S, res);
- return 1;
-}
-
-
-
-
-
-static int tolua_cBlockArea_LoadFromSchematicString(lua_State * tolua_S)
-{
- // function cBlockArea::LoadFromSchematicString
- // Exported manually because function has been moved to SchematicFileSerializer.cpp
- cLuaState L(tolua_S);
- if (
- !L.CheckParamUserType(1, "cBlockArea") ||
- !L.CheckParamString (2) ||
- !L.CheckParamEnd (3)
- )
- {
- return 0;
- }
- cBlockArea * self = reinterpret_cast<cBlockArea *>(tolua_tousertype(tolua_S, 1, nullptr));
- if (self == nullptr)
- {
- tolua_error(tolua_S, "invalid 'self' in function 'cBlockArea::LoadFromSchematicFile'", nullptr);
- return 0;
- }
-
- AString Data;
- L.GetStackValue(2, Data);
- bool res = cSchematicFileSerializer::LoadFromSchematicString(*self, Data);
- tolua_pushboolean(tolua_S, res);
- return 1;
-}
-
-
-
-
-
-static int tolua_cBlockArea_SaveToSchematicFile(lua_State * tolua_S)
-{
- // function cBlockArea::SaveToSchematicFile
- // Exported manually because function has been moved to SchematicFileSerializer.cpp
- cLuaState L(tolua_S);
- if (
- !L.CheckParamUserType(1, "cBlockArea") ||
- !L.CheckParamString (2) ||
- !L.CheckParamEnd (3)
- )
- {
- return 0;
- }
- cBlockArea * self = reinterpret_cast<cBlockArea *>(tolua_tousertype(tolua_S, 1, nullptr));
- if (self == nullptr)
- {
- tolua_error(tolua_S, "invalid 'self' in function 'cBlockArea::SaveToSchematicFile'", nullptr);
- return 0;
- }
- AString Filename = tolua_tostring(tolua_S, 2, 0);
- bool res = cSchematicFileSerializer::SaveToSchematicFile(*self, Filename);
- tolua_pushboolean(tolua_S, res);
- return 1;
-}
-
-
-
-
-
-static int tolua_cBlockArea_SaveToSchematicString(lua_State * tolua_S)
-{
- // function cBlockArea::SaveToSchematicString
- // Exported manually because function has been moved to SchematicFileSerializer.cpp
- cLuaState L(tolua_S);
- if (
- !L.CheckParamUserType(1, "cBlockArea") ||
- !L.CheckParamEnd (2)
- )
- {
- return 0;
- }
- cBlockArea * self = reinterpret_cast<cBlockArea *>(tolua_tousertype(tolua_S, 1, nullptr));
- if (self == nullptr)
- {
- tolua_error(tolua_S, "invalid 'self' in function 'cBlockArea::SaveToSchematicFile'", nullptr);
- return 0;
- }
-
- AString Data;
- if (cSchematicFileSerializer::SaveToSchematicString(*self, Data))
- {
- L.Push(Data);
- return 1;
- }
- return 0;
-}
-
-
-
-
-
static int tolua_cBoundingBox_CalcLineIntersection(lua_State * a_LuaState)
{
/* Function signatures:
@@ -4031,19 +3752,6 @@ void cManualBindings::Bind(lua_State * tolua_S)
tolua_function(tolua_S, "Base64Decode", tolua_Base64Decode);
tolua_function(tolua_S, "md5", tolua_md5_obsolete); // OBSOLETE, use cCryptoHash.md5() instead
- tolua_beginmodule(tolua_S, "cBlockArea");
- tolua_function(tolua_S, "GetBlockTypeMeta", tolua_cBlockArea_GetBlockTypeMeta);
- tolua_function(tolua_S, "GetCoordRange", tolua_cBlockArea_GetCoordRange);
- tolua_function(tolua_S, "GetOrigin", tolua_cBlockArea_GetOrigin);
- tolua_function(tolua_S, "GetNonAirCropRelCoords", tolua_cBlockArea_GetNonAirCropRelCoords);
- 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, "LoadFromSchematicString", tolua_cBlockArea_LoadFromSchematicString);
- tolua_function(tolua_S, "SaveToSchematicFile", tolua_cBlockArea_SaveToSchematicFile);
- tolua_function(tolua_S, "SaveToSchematicString", tolua_cBlockArea_SaveToSchematicString);
- tolua_endmodule(tolua_S);
-
tolua_beginmodule(tolua_S, "cBoundingBox");
tolua_function(tolua_S, "CalcLineIntersection", tolua_cBoundingBox_CalcLineIntersection);
tolua_function(tolua_S, "Intersect", tolua_cBoundingBox_Intersect);
@@ -4228,6 +3936,7 @@ void cManualBindings::Bind(lua_State * tolua_S)
BindNetwork(tolua_S);
BindRankManager(tolua_S);
BindWorld(tolua_S);
+ BindBlockArea(tolua_S);
tolua_endmodule(tolua_S);
}
diff --git a/src/Bindings/ManualBindings.h b/src/Bindings/ManualBindings.h
index 98e3e88ef..889e33664 100644
--- a/src/Bindings/ManualBindings.h
+++ b/src/Bindings/ManualBindings.h
@@ -41,6 +41,10 @@ protected:
Implemented in ManualBindings_World.cpp. */
static void BindWorld(lua_State * tolua_S);
+ /** Binds the manually implemented cBlockArea API functions to tlua_S.
+ Implemented in ManualBindings_BlockArea.cpp. */
+ static void BindBlockArea(lua_State * tolua_S);
+
public:
// Helper functions:
@@ -48,6 +52,11 @@ public:
static int tolua_do_error(lua_State * L, const char * a_pMsg, tolua_Error * a_pToLuaError);
static int lua_do_error(lua_State * L, const char * a_pFormat, ...);
+ /** Formats and prints the message, prefixed with the current function name, then logs the stack contents and raises a Lua error.
+ To be used for bindings when they detect bad parameters.
+ Doesn't return, but a dummy return type is provided so that Lua API functions may do "return ApiParamError(...)". */
+ static int ApiParamError(lua_State * a_LuaState, const char * a_MsgFormat, ...);
+
/** Binds the DoWith(ItemName) functions of regular classes. */
template <
@@ -243,10 +252,11 @@ public:
+ /** Template for the bindings for the DoWithXYZAt(X, Y, Z) functions that don't need to check their coords. */
template <
- class Ty1,
- class Ty2,
- bool (Ty1::*DoWithFn)(int, int, int, cItemCallback<Ty2> &)
+ class SELF,
+ class ITEM,
+ bool (SELF::*DoWithFn)(int, int, int, cItemCallback<ITEM> &)
>
static int DoWithXYZ(lua_State * tolua_S)
{
@@ -262,7 +272,7 @@ public:
}
// Get parameters:
- Ty1 * Self = nullptr;
+ SELF * Self = nullptr;
int BlockX = 0;
int BlockY = 0;
int BlockZ = 0;
@@ -277,7 +287,7 @@ public:
return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a valid callback function for parameter #5");
}
- class cLuaCallback : public cItemCallback<Ty2>
+ class cLuaCallback : public cItemCallback<ITEM>
{
public:
cLuaCallback(cLuaState & a_LuaState, cLuaState::cRef & a_FnRef):
@@ -287,7 +297,81 @@ public:
}
private:
- virtual bool Item(Ty2 * a_Item) override
+ virtual bool Item(ITEM * a_Item) override
+ {
+ bool ret = false;
+ m_LuaState.Call(m_FnRef, a_Item, cLuaState::Return, ret);
+ return ret;
+ }
+ cLuaState & m_LuaState;
+ cLuaState::cRef & m_FnRef;
+ } Callback(L, FnRef);
+
+ // Call the DoWith function:
+ bool res = (Self->*DoWithFn)(BlockX, BlockY, BlockZ, Callback);
+
+ // Push the result as the return value:
+ L.Push(res);
+ return 1;
+ }
+
+
+
+
+
+ /** Template for the bindings for the DoWithXYZAt(X, Y, Z) functions that need to check their coords. */
+ template <
+ class SELF,
+ class ITEM,
+ bool (SELF::*DoWithFn)(int, int, int, cItemCallback<ITEM> &),
+ bool (SELF::*CoordCheckFn)(int, int, int) const
+ >
+ static int DoWithXYZ(lua_State * tolua_S)
+ {
+ // Check params:
+ cLuaState L(tolua_S);
+ if (
+ !L.CheckParamNumber(2, 4) ||
+ !L.CheckParamFunction(5) ||
+ !L.CheckParamEnd(6)
+ )
+ {
+ return 0;
+ }
+
+ // Get parameters:
+ SELF * Self = nullptr;
+ int BlockX = 0;
+ int BlockY = 0;
+ int BlockZ = 0;
+ cLuaState::cRef FnRef;
+ L.GetStackValues(1, Self, BlockX, BlockY, BlockZ, FnRef);
+ if (Self == nullptr)
+ {
+ return lua_do_error(tolua_S, "Error in function call '#funcname#': Invalid 'self'");
+ }
+ if (!FnRef.IsValid())
+ {
+ return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a valid callback function for parameter #5");
+ }
+ if (!(Self->*CoordCheckFn)(BlockX, BlockY, BlockZ))
+ {
+ return lua_do_error(tolua_S, Printf("Error in function call '#funcname#': The provided coordinates ({%d, %d, %d}) are not valid",
+ BlockX, BlockY, BlockZ
+ ).c_str());
+ }
+
+ class cLuaCallback : public cItemCallback<ITEM>
+ {
+ public:
+ cLuaCallback(cLuaState & a_LuaState, cLuaState::cRef & a_FnRef):
+ m_LuaState(a_LuaState),
+ m_FnRef(a_FnRef)
+ {
+ }
+
+ private:
+ virtual bool Item(ITEM * a_Item) override
{
bool ret = false;
m_LuaState.Call(m_FnRef, a_Item, cLuaState::Return, ret);
diff --git a/src/Bindings/ManualBindings_BlockArea.cpp b/src/Bindings/ManualBindings_BlockArea.cpp
new file mode 100644
index 000000000..14462c6e9
--- /dev/null
+++ b/src/Bindings/ManualBindings_BlockArea.cpp
@@ -0,0 +1,998 @@
+// ManualBindings_BlockArea.cpp
+
+// Implements the manual bindings for functions in the cBlockArea class
+
+#include "Globals.h"
+#include "tolua++/include/tolua++.h"
+#include "../BlockArea.h"
+#include "../World.h"
+#include "ManualBindings.h"
+#include "LuaState.h"
+#include "PluginLua.h"
+#include "../WorldStorage/SchematicFileSerializer.h"
+
+
+
+
+
+
+/** Reads params that together form a Cuboid.
+These can be:
+ - 6 numbers (MinX, MaxX, MinY, MaxY, MinZ, MaxZ)
+ - 2 Vector3-s (Min, Max)
+ - cCuboid
+Returns the index of the first parameter following the Cuboid spec.
+Raises an Api error if the params don't specify a Cuboid.
+*/
+static int readCuboidOverloadParams(cLuaState & a_LuaState, int a_StartParam, cCuboid & a_Cuboid)
+{
+ if (a_LuaState.IsParamNumber(a_StartParam))
+ {
+ // Assume the 6-number version:
+ if (!a_LuaState.GetStackValues(a_StartParam, a_Cuboid.p1.x, a_Cuboid.p2.x, a_Cuboid.p1.y, a_Cuboid.p2.y, a_Cuboid.p1.z, a_Cuboid.p2.z))
+ {
+ return cManualBindings::ApiParamError(a_LuaState, "Cannot read the bounds parameters, expected 6 numbers.");
+ }
+ return a_StartParam + 6;
+ }
+ else if (a_LuaState.IsParamUserType(a_StartParam, "cCuboid"))
+ {
+ // Assume the cCuboid version:
+ cCuboid * c;
+ if (!a_LuaState.GetStackValues(a_StartParam, c))
+ {
+ return cManualBindings::ApiParamError(a_LuaState, "Cannot read the bounds parameter, expected a cCuboid instance.");
+ }
+ a_Cuboid = *c;
+ return a_StartParam + 1;
+ }
+ else
+ {
+ // Assume the 2-Vector3i version:
+ Vector3i * p1;
+ Vector3i * p2;
+ if (!a_LuaState.GetStackValues(a_StartParam, p1, p2))
+ {
+ return cManualBindings::ApiParamError(a_LuaState, "Cannot read the bounds parameter, expected two Vector3i instances.");
+ }
+ a_Cuboid.p1 = *p1;
+ a_Cuboid.p2 = *p2;
+ return a_StartParam + 2;
+ }
+}
+
+
+
+
+
+/** Reads params that together form a Vector3i.
+These can be:
+ - 3 numbers (x, y, z)
+ - Vector3i
+Returns the index of the first parameter following the Vector3i spec.
+Raises an Api error if the params don't specify a Vector3i.
+*/
+static int readVector3iOverloadParams(cLuaState & a_LuaState, int a_StartParam, Vector3i & a_Coords, const char * a_ParamName)
+{
+ if (a_LuaState.IsParamNumber(a_StartParam))
+ {
+ // Assume the 3-number version:
+ if (!a_LuaState.GetStackValues(a_StartParam, a_Coords.x, a_Coords.y, a_Coords.z))
+ {
+ return cManualBindings::ApiParamError(a_LuaState, "Cannot read the %s, expected 3 numbers.", a_ParamName);
+ }
+ return a_StartParam + 3;
+ }
+ else
+ {
+ // Assume the Vector3i version:
+ Vector3i * c;
+ if (!a_LuaState.GetStackValues(a_StartParam, c))
+ {
+ return cManualBindings::ApiParamError(a_LuaState, "Cannot read the %s, expected a Vector3i instance.", a_ParamName);
+ }
+ a_Coords = *c;
+ return a_StartParam + 1;
+ }
+}
+
+
+
+
+
+/** Binding for the cBlockArea::Create() functions. Supports two overloads and one default parameter. */
+static int tolua_cBlockArea_Create(lua_State * a_LuaState)
+{
+ cLuaState L(a_LuaState);
+ if (!L.CheckParamSelf("cBlockArea"))
+ {
+ return 0;
+ }
+ cBlockArea * self = nullptr;
+ if (!L.GetStackValues(1, self))
+ {
+ return cManualBindings::ApiParamError(a_LuaState, "Cannot read self.");
+ }
+ if (self == nullptr)
+ {
+ return cManualBindings::ApiParamError(a_LuaState, "Invalid 'self', must not be nil.");
+ }
+
+ int dataTypes = cBlockArea::baTypes | cBlockArea::baMetas | cBlockArea::baBlockEntities;
+ Vector3i size;
+ auto dataTypesIdx = readVector3iOverloadParams(L, 2, size, "size");
+ L.GetStackValue(dataTypesIdx, dataTypes);
+ if (!cBlockArea::IsValidDataTypeCombination(dataTypes))
+ {
+ return cManualBindings::ApiParamError(a_LuaState, "Invalid combination of baDataTypes specified (%d).", dataTypes);
+ }
+
+ // Create the area:
+ if ((size.x <= 0) || (size.y <= 0) || (size.z <= 0))
+ {
+ return cManualBindings::ApiParamError(a_LuaState, "Invalid sizes, must be greater than zero, got {%d, %d, %d}", size.x, size.y, size.z);
+ }
+ ASSERT(self != nullptr);
+ self->Create(size, dataTypes);
+ return 0;
+}
+
+
+
+
+
+/** Bindings for the cBlockArea:FillRelCuboid() functions. Supports coord overloads and one default parameter. */
+static int tolua_cBlockArea_FillRelCuboid(lua_State * a_LuaState)
+{
+ // Check the common params:
+ cLuaState L(a_LuaState);
+ if (!L.CheckParamSelf("cBlockArea"))
+ {
+ return 0;
+ }
+
+ // Get the common params:
+ cBlockArea * self = nullptr;
+ if (!L.GetStackValues(1, self))
+ {
+ return cManualBindings::ApiParamError(a_LuaState, "Cannot read self.");
+ }
+ if (self == nullptr)
+ {
+ return cManualBindings::ApiParamError(a_LuaState, "Invalid 'self', must not be nil.");
+ }
+
+ // Check and get the overloaded params:
+ cCuboid bounds;
+ auto nextIdx = readCuboidOverloadParams(L, 2, bounds);
+ int dataTypes = cBlockArea::baTypes | cBlockArea::baMetas | cBlockArea::baBlockEntities;
+ BLOCKTYPE blockType;
+ NIBBLETYPE blockMeta = 0, blockLight = 0, blockSkyLight = 0x0f;
+ if (!L.GetStackValues(nextIdx, dataTypes, blockType))
+ {
+ return cManualBindings::ApiParamError(a_LuaState, "Cannot read the datatypes or block type params");
+ }
+ L.GetStackValues(nextIdx + 2, blockMeta, blockLight, blockSkyLight); // These values are optional
+ if (!cBlockArea::IsValidDataTypeCombination(dataTypes))
+ {
+ return cManualBindings::ApiParamError(a_LuaState, "Invalid baDataTypes combination (%d).", dataTypes);
+ }
+
+ // Check the coords, shift if needed:
+ bounds.Sort();
+ bounds.ClampX(0, self->GetSizeX());
+ bounds.ClampY(0, self->GetSizeY());
+ bounds.ClampZ(0, self->GetSizeZ());
+
+ // Do the actual Fill:
+ self->FillRelCuboid(bounds, dataTypes, blockType, blockMeta, blockLight, blockSkyLight);
+ return 0;
+}
+
+
+
+
+
+static int tolua_cBlockArea_GetBlockTypeMeta(lua_State * a_LuaState)
+{
+ // function cBlockArea::GetBlockTypeMeta()
+
+ cLuaState L(a_LuaState);
+ if (!L.CheckParamSelf("cBlockArea"))
+ {
+ return 0;
+ }
+
+ cBlockArea * self;
+ if (!L.GetStackValues(1, self))
+ {
+ return cManualBindings::ApiParamError(a_LuaState, "Cannot read 'self'");
+ }
+ if (self == nullptr)
+ {
+ return cManualBindings::ApiParamError(a_LuaState, "Invalid 'self', must not be nil.");
+ }
+
+ Vector3i coords;
+ readVector3iOverloadParams(L, 2, coords, "coords");
+ if (!self->IsValidCoords(coords))
+ {
+ return cManualBindings::ApiParamError(a_LuaState, "Coords ({%d, %d, %d}) out of range ({%d, %d, %d} - {%d, %d, %d}).",
+ coords.x, coords.y, coords.z,
+ self->GetOriginX(), self->GetOriginY(), self->GetOriginZ(),
+ self->GetOriginX() + self->GetSizeX() - 1, self->GetOriginY() + self->GetSizeY() - 1, self->GetOriginZ() + self->GetSizeZ() - 1
+ );
+ }
+ BLOCKTYPE blockType;
+ NIBBLETYPE blockMeta;
+ self->GetBlockTypeMeta(coords.x, coords.y, coords.z, blockType, blockMeta);
+ L.Push(blockType, blockMeta);
+ return 2;
+}
+
+
+
+
+
+static int tolua_cBlockArea_GetCoordRange(lua_State * a_LuaState)
+{
+ // function cBlockArea::GetCoordRange()
+ // Returns all three sizes of the area, each minus one, so that they represent the maximum coord value
+ // Exported manually because there's no direct C++ equivalent,
+ // plus tolua would generate extra input params for the outputs
+
+ cLuaState L(a_LuaState);
+ if (!L.CheckParamSelf("cBlockArea"))
+ {
+ return 0;
+ }
+
+ cBlockArea * self;
+ if (!L.GetStackValues(1, self))
+ {
+ return cManualBindings::ApiParamError(a_LuaState, "Cannot read the 'self' parameter.");
+ }
+ if (self == nullptr)
+ {
+ return cManualBindings::ApiParamError(a_LuaState, "Invalid 'self', must not be nil.");
+ }
+
+ L.Push(self->GetSizeX() - 1, self->GetSizeY() - 1, self->GetSizeZ() - 1);
+ return 3;
+}
+
+
+
+
+
+static int tolua_cBlockArea_GetNonAirCropRelCoords(lua_State * a_LuaState)
+{
+ // function cBlockArea::GetNonAirCropRelCoords()
+ // Exported manually because tolua would generate extra input params for the outputs
+
+ cLuaState L(a_LuaState);
+ if (!L.CheckParamSelf("cBlockArea"))
+ {
+ return 0;
+ }
+
+ cBlockArea * self = nullptr;
+ BLOCKTYPE ignoreBlockType = E_BLOCK_AIR;
+ if (!L.GetStackValues(1, self, ignoreBlockType))
+ {
+ return cManualBindings::ApiParamError(a_LuaState, "Cannot read params");
+ }
+ if (self == nullptr)
+ {
+ return cManualBindings::ApiParamError(a_LuaState, "Invalid 'self', must not be nil");
+ }
+ if (!self->HasBlockTypes())
+ {
+ return cManualBindings::ApiParamError(a_LuaState, "The area doesn't contain baTypes datatype");
+ }
+
+ // Calculate the crop coords:
+ int minRelX, minRelY, minRelZ, maxRelX, maxRelY, maxRelZ;
+ self->GetNonAirCropRelCoords(minRelX, minRelY, minRelZ, maxRelX, maxRelY, maxRelZ, ignoreBlockType);
+
+ // Push the six crop coords:
+ L.Push(minRelX, minRelY, minRelZ, maxRelX, maxRelY, maxRelZ);
+ return 6;
+}
+
+
+
+
+
+static int tolua_cBlockArea_GetOrigin(lua_State * a_LuaState)
+{
+ // 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(a_LuaState);
+ if (!L.CheckParamSelf("cBlockArea"))
+ {
+ return 0;
+ }
+
+ cBlockArea * self;
+ if (!L.GetStackValues(1, self))
+ {
+ return cManualBindings::ApiParamError(a_LuaState, "Cannot read the 'self' parameter.");
+ }
+ if (self == nullptr)
+ {
+ return cManualBindings::ApiParamError(a_LuaState, "Invalid 'self', must not be nil.");
+ }
+
+ // Push the three origin coords:
+ L.Push(self->GetOriginX(), self->GetOriginY(), self->GetOriginZ());
+ return 3;
+}
+
+
+
+
+
+static int tolua_cBlockArea_GetRelBlockTypeMeta(lua_State * a_LuaState)
+{
+ // function cBlockArea::GetRelBlockTypeMeta()
+ // Exported manually because tolua generates extra input params for the outputs
+
+ cLuaState L(a_LuaState);
+ if (!L.CheckParamSelf("cBlockArea"))
+ {
+ return 0;
+ }
+
+ cBlockArea * self;
+ if (!L.GetStackValues(1, self))
+ {
+ return cManualBindings::ApiParamError(a_LuaState, "Cannot read the 'self' parameter.");
+ }
+ if (self == nullptr)
+ {
+ return cManualBindings::ApiParamError(a_LuaState, "Invalid 'self', must not be nil.");
+ }
+ if (!self->HasBlockTypes())
+ {
+ return cManualBindings::ApiParamError(a_LuaState, "The area doesn't contain baTypes datatype");
+ }
+ if (!self->HasBlockMetas())
+ {
+ return cManualBindings::ApiParamError(a_LuaState, "The area doesn't contain baMetas datatype");
+ }
+
+ Vector3i coords;
+ readVector3iOverloadParams(L, 2, coords, "coords");
+ if (!self->IsValidRelCoords(coords))
+ {
+ return cManualBindings::ApiParamError(a_LuaState, "The coords ({%d, %d, %d}) are out of range (max {%d, %d, %d}).",
+ coords.x, coords.y, coords.z,
+ self->GetSizeX() - 1, self->GetSizeY() - 1, self->GetSizeZ() - 1
+ );
+ }
+ BLOCKTYPE blockType;
+ NIBBLETYPE blockMeta;
+ self->GetRelBlockTypeMeta(coords.x, coords.y, coords.z, blockType, blockMeta);
+ L.Push(blockType, blockMeta);
+ return 2;
+}
+
+
+
+
+
+static int tolua_cBlockArea_GetSize(lua_State * a_LuaState)
+{
+ // 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(a_LuaState);
+ if (!L.CheckParamSelf("cBlockArea"))
+ {
+ return 0;
+ }
+
+ cBlockArea * self;
+ if (!L.GetStackValues(1, self))
+ {
+ return cManualBindings::ApiParamError(a_LuaState, "Cannot read the 'self' parameter.");
+ }
+ if (self == nullptr)
+ {
+ return cManualBindings::ApiParamError(a_LuaState, "Invalid 'self', must not be nil.");
+ }
+
+ L.Push(self->GetSizeX(), self->GetSizeY(), self->GetSizeZ());
+ return 3;
+}
+
+
+
+
+
+static int tolua_cBlockArea_LoadFromSchematicFile(lua_State * a_LuaState)
+{
+ // function cBlockArea::LoadFromSchematicFile
+ // Exported manually because function has been moved to SchematicFileSerializer.cpp
+ cLuaState L(a_LuaState);
+ if (
+ !L.CheckParamSelf("cBlockArea") ||
+ !L.CheckParamString(2) ||
+ !L.CheckParamEnd(3)
+ )
+ {
+ return 0;
+ }
+ cBlockArea * self;
+ AString fileName;
+ if (!L.GetStackValues(1, self, fileName))
+ {
+ return cManualBindings::ApiParamError(a_LuaState, "Cannot read the parameters.");
+ }
+ if (self == nullptr)
+ {
+ return cManualBindings::ApiParamError(a_LuaState, "Invalid 'self', must not be nil.");
+ }
+
+ L.Push(cSchematicFileSerializer::LoadFromSchematicFile(*self, fileName));
+ return 1;
+}
+
+
+
+
+
+static int tolua_cBlockArea_LoadFromSchematicString(lua_State * a_LuaState)
+{
+ // function cBlockArea::LoadFromSchematicString
+ // Exported manually because function has been moved to SchematicFileSerializer.cpp
+ cLuaState L(a_LuaState);
+ if (
+ !L.CheckParamSelf("cBlockArea") ||
+ !L.CheckParamString(2) ||
+ !L.CheckParamEnd(3)
+ )
+ {
+ return 0;
+ }
+ cBlockArea * self;
+ AString data;
+ if (!L.GetStackValues(1, self, data))
+ {
+ return cManualBindings::ApiParamError(a_LuaState, "Cannot read the parameters.");
+ }
+ if (self == nullptr)
+ {
+ return cManualBindings::ApiParamError(a_LuaState, "Invalid 'self', must not be nil.");
+ }
+
+ L.Push(cSchematicFileSerializer::LoadFromSchematicString(*self, data));
+ return 1;
+}
+
+
+
+
+
+/** Bindings for the cBlockArea:Read() functions. Supports three overloads and one default parameter. */
+static int tolua_cBlockArea_Read(lua_State * a_LuaState)
+{
+ // Check the common params:
+ cLuaState L(a_LuaState);
+ if (
+ !L.CheckParamSelf("cBlockArea") ||
+ !L.CheckParamUserType(2, "cWorld")
+ )
+ {
+ return 0;
+ }
+
+ // Get the common params:
+ cBlockArea * self = nullptr;
+ cWorld * world = nullptr;
+ if (!L.GetStackValues(1, self, world))
+ {
+ return cManualBindings::ApiParamError(a_LuaState, "Cannot read self or world.");
+ }
+ if (world == nullptr)
+ {
+ return cManualBindings::ApiParamError(a_LuaState, "Invalid world instance. The world must be not nil.");
+ }
+
+ // Check and get the overloaded params:
+ cCuboid bounds;
+ int dataTypes = cBlockArea::baTypes | cBlockArea::baMetas | cBlockArea::baBlockEntities;
+ auto dataTypesIdx = readCuboidOverloadParams(L, 3, bounds);
+ L.GetStackValues(dataTypesIdx, dataTypes);
+ if (!cBlockArea::IsValidDataTypeCombination(dataTypes))
+ {
+ return cManualBindings::ApiParamError(a_LuaState, "Invalid baDataTypes combination (%d).", dataTypes);
+ }
+
+ // Check the coords, shift if needed:
+ bounds.Sort();
+ if (bounds.p1.y < 0)
+ {
+ LOGWARNING("cBlockArea:Read(): MinBlockY less than zero, adjusting to zero. Coords: {%d, %d, %d} - {%d, %d, %d}",
+ bounds.p1.x, bounds.p1.y, bounds.p1.z, bounds.p2.x, bounds.p2.y, bounds.p2.z
+ );
+ L.LogStackTrace();
+ bounds.p1.y = 0;
+ }
+ else if (bounds.p1.y >= cChunkDef::Height)
+ {
+ LOGWARNING("cBlockArea:Read(): MinBlockY more than chunk height, adjusting to chunk height. Coords: {%d, %d, %d} - {%d, %d, %d}",
+ bounds.p1.x, bounds.p1.y, bounds.p1.z, bounds.p2.x, bounds.p2.y, bounds.p2.z
+ );
+ L.LogStackTrace();
+ bounds.p1.y = cChunkDef::Height - 1;
+ }
+ if (bounds.p2.y < 0)
+ {
+ LOGWARNING("cBlockArea:Read(): MaxBlockY less than zero, adjusting to zero. Coords: {%d, %d, %d} - {%d, %d, %d}",
+ bounds.p1.x, bounds.p1.y, bounds.p1.z, bounds.p2.x, bounds.p2.y, bounds.p2.z
+ );
+ L.LogStackTrace();
+ bounds.p2.y = 0;
+ }
+ else if (bounds.p2.y > cChunkDef::Height)
+ {
+ LOGWARNING("cBlockArea:Read(): MaxBlockY more than chunk height, adjusting to chunk height. Coords: {%d, %d, %d} - {%d, %d, %d}",
+ bounds.p1.x, bounds.p1.y, bounds.p1.z, bounds.p2.x, bounds.p2.y, bounds.p2.z
+ );
+ L.LogStackTrace();
+ bounds.p2.y = cChunkDef::Height;
+ }
+
+ // Do the actual read:
+ L.Push(self->Read(*world, bounds, dataTypes));
+ return 1;
+}
+
+
+
+
+
+/** Bindings for the cBlockArea:RelLine() functions. Supports two overloads and one default parameter.
+Also supports "bastard overloads" (Vector3i, x, y, z), but we don't advertise those. */
+static int tolua_cBlockArea_RelLine(lua_State * a_LuaState)
+{
+ // Check the common params:
+ cLuaState L(a_LuaState);
+ if (!L.CheckParamSelf("cBlockArea"))
+ {
+ return 0;
+ }
+
+ // Get the common params:
+ cBlockArea * self = nullptr;
+ if (!L.GetStackValues(1, self))
+ {
+ return cManualBindings::ApiParamError(a_LuaState, "Cannot read self.");
+ }
+
+ // Check and get the overloaded params:
+ Vector3i p1;
+ auto idx = readVector3iOverloadParams(L, 2, p1, "start coords");
+ Vector3i p2;
+ idx = readVector3iOverloadParams(L, idx, p2, "end coords");
+ int dataTypes = cBlockArea::baTypes | cBlockArea::baMetas | cBlockArea::baBlockEntities;
+ BLOCKTYPE blockType;
+ NIBBLETYPE blockMeta, blockLight, blockSkyLight;
+ L.GetStackValues(idx, dataTypes, blockType, blockMeta, blockLight, blockSkyLight);
+ if (!cBlockArea::IsValidDataTypeCombination(dataTypes))
+ {
+ return cManualBindings::ApiParamError(a_LuaState, "Invalid baDataTypes combination (%d).", dataTypes);
+ }
+
+ // Draw the line:
+ self->RelLine(p1, p2, dataTypes, blockType, blockMeta, blockLight, blockSkyLight);
+ return 0;
+}
+
+
+
+
+
+static int tolua_cBlockArea_SaveToSchematicFile(lua_State * a_LuaState)
+{
+ // function cBlockArea::SaveToSchematicFile
+ // Exported manually because function has been moved to SchematicFileSerializer.cpp
+ cLuaState L(a_LuaState);
+ if (
+ !L.CheckParamSelf("cBlockArea") ||
+ !L.CheckParamString(2) ||
+ !L.CheckParamEnd(3)
+ )
+ {
+ return 0;
+ }
+ cBlockArea * self;
+ AString fileName;
+ if (!L.GetStackValues(1, self, fileName))
+ {
+ return cManualBindings::ApiParamError(a_LuaState, "Cannot read the parameters.");
+ }
+ if (self == nullptr)
+ {
+ return cManualBindings::ApiParamError(a_LuaState, "Invalid 'self', must not be nil.");
+ }
+
+ L.Push(cSchematicFileSerializer::SaveToSchematicFile(*self, fileName));
+ return 1;
+}
+
+
+
+
+
+static int tolua_cBlockArea_SaveToSchematicString(lua_State * a_LuaState)
+{
+ // function cBlockArea::SaveToSchematicString
+ // Exported manually because function has been moved to SchematicFileSerializer.cpp
+ cLuaState L(a_LuaState);
+ if (
+ !L.CheckParamSelf("cBlockArea") ||
+ !L.CheckParamEnd(2)
+ )
+ {
+ return 0;
+ }
+ cBlockArea * self;
+ if (!L.GetStackValues(1, self))
+ {
+ return cManualBindings::ApiParamError(a_LuaState, "Cannot read the 'self' parameter.");
+ }
+ if (self == nullptr)
+ {
+ return cManualBindings::ApiParamError(a_LuaState, "Invalid 'self', must not be nil.");
+ }
+
+ AString data;
+ if (cSchematicFileSerializer::SaveToSchematicString(*self, data))
+ {
+ L.Push(data);
+ return 1;
+ }
+ return 0;
+}
+
+
+
+
+
+/** Bindings for the cBlockArea:Write() functions. Supports two overloads and one default parameter. */
+static int tolua_cBlockArea_Write(lua_State * a_LuaState)
+{
+ // Check the common params:
+ cLuaState L(a_LuaState);
+ if (
+ !L.CheckParamSelf("cBlockArea") ||
+ !L.CheckParamUserType(2, "cWorld")
+ )
+ {
+ return 0;
+ }
+
+ // Get the common params:
+ cBlockArea * self = nullptr;
+ cWorld * world = nullptr;
+ if (!L.GetStackValues(1, self, world))
+ {
+ return cManualBindings::ApiParamError(a_LuaState, "Cannot read self or world.");
+ }
+ if (world == nullptr)
+ {
+ return cManualBindings::ApiParamError(a_LuaState, "Invalid world instance. The world must be not nil.");
+ }
+
+ // Check and get the overloaded params:
+ Vector3i coords;
+ int dataTypes = cBlockArea::baTypes | cBlockArea::baMetas | cBlockArea::baBlockEntities;
+ auto dataTypesIdx = readVector3iOverloadParams(L, 3, coords, "coords");
+ L.GetStackValues(dataTypesIdx, dataTypes);
+
+ // Check the dataType parameter validity:
+ if (!cBlockArea::IsValidDataTypeCombination(dataTypes))
+ {
+ return cManualBindings::ApiParamError(a_LuaState, "Invalid datatype combination (%d).", dataTypes);
+ }
+ if ((self->GetDataTypes() & dataTypes) != dataTypes)
+ {
+ return cManualBindings::ApiParamError(a_LuaState, "Requesting datatypes not present in the cBlockArea. Got only 0x%02x, requested 0x%02x",
+ self->GetDataTypes(), dataTypes
+ );
+ }
+
+ // Check and adjust the coord params:
+ // TODO: Should we report this as a failure? Because the result is definitely not what the plugin assumed
+ // ... Or should we silently clone-crop-write the cBlockArea so that the API call does what would be naturally expected?
+ // ... Or should we change the cBlockArea::Write() to allow out-of-range Y coords and do the cropping there?
+ // ... NOTE: We already support auto-crop in cBlockArea::Merge() itself
+ if (coords.y < 0)
+ {
+ LOGWARNING("cBlockArea:Write(): MinBlockY less than zero, adjusting to zero");
+ L.LogStackTrace();
+ coords.y = 0;
+ }
+ else if (coords.y > cChunkDef::Height - self->GetSizeY())
+ {
+ LOGWARNING("cBlockArea:Write(): MinBlockY + m_SizeY more than chunk height, adjusting to chunk height");
+ L.LogStackTrace();
+ coords.y = cChunkDef::Height - self->GetSizeY();
+ }
+
+ // Do the actual write:
+ L.Push(self->Write(*world, coords, dataTypes));
+ return 1;
+}
+
+
+
+
+
+/** Templated bindings for the GetBlock___() functions.
+DataType is either BLOCKTYPE or NIBBLETYPE.
+DataTypeFlag is the ba___ constant used for the datatype being queried.
+Fn is the getter function.
+Also supports the Vector3i overloads (TODO: document these (?)). */
+template <
+ typename DataType,
+ int DataTypeFlag,
+ DataType (cBlockArea::*Fn)(int, int, int) const
+>
+static int GetBlock(lua_State * a_LuaState)
+{
+ // Check the common params:
+ cLuaState L(a_LuaState);
+ if (!L.CheckParamSelf("cBlockArea"))
+ {
+ return 0;
+ }
+
+ // Read the common params:
+ cBlockArea * self;
+ if (!L.GetStackValues(1, self))
+ {
+ return cManualBindings::ApiParamError(a_LuaState, "Cannot read the 'self' param.");
+ }
+
+ // Check the datatype's presence:
+ if ((self->GetDataTypes() & DataTypeFlag) == 0)
+ {
+ return cManualBindings::ApiParamError(a_LuaState, "The area doesn't contain the datatype (%d).", DataTypeFlag);
+ }
+
+ // Read the overloaded params:
+ Vector3i coords;
+ readVector3iOverloadParams(L, 2, coords, "coords");
+ if (!self->IsValidCoords(coords))
+ {
+ return cManualBindings::ApiParamError(a_LuaState, "The coords ({%d, %d, %d}) are out of range ({%d, %d, %d} - {%d, %d, %d}).",
+ coords.x, coords.y, coords.z,
+ self->GetOriginX(), self->GetOriginY(), self->GetOriginZ(),
+ self->GetOriginX() + self->GetSizeX() - 1, self->GetOriginY() + self->GetSizeY() - 1, self->GetOriginZ() + self->GetSizeZ() - 1
+ );
+ }
+
+ // Get the block info:
+ L.Push((self->*Fn)(coords.x, coords.y, coords.z));
+ return 1;
+}
+
+
+
+
+
+/** Templated bindings for the GetRelBlock___() functions.
+DataType is either BLOCKTYPE or NIBBLETYPE.
+DataTypeFlag is the ba___ constant used for the datatype being queried.
+Fn is the getter function.
+Also supports the Vector3i overloads (TODO: document these (?)). */
+template <
+ typename DataType,
+ int DataTypeFlag,
+ DataType (cBlockArea::*Fn)(int, int, int) const
+>
+static int GetRelBlock(lua_State * a_LuaState)
+{
+ // Check the common params:
+ cLuaState L(a_LuaState);
+ if (!L.CheckParamSelf("cBlockArea"))
+ {
+ return 0;
+ }
+
+ // Read the common params:
+ cBlockArea * self;
+ if (!L.GetStackValues(1, self))
+ {
+ return cManualBindings::ApiParamError(a_LuaState, "Cannot read the 'self' param.");
+ }
+
+ // Check the datatype's presence:
+ if ((self->GetDataTypes() & DataTypeFlag) == 0)
+ {
+ return cManualBindings::ApiParamError(a_LuaState, "The area doesn't contain the datatype (%d).", DataTypeFlag);
+ }
+
+ // Read the overloaded params:
+ Vector3i coords;
+ readVector3iOverloadParams(L, 2, coords, "coords");
+ if (!self->IsValidRelCoords(coords))
+ {
+ return cManualBindings::ApiParamError(a_LuaState, "The coords ({%d, %d, %d}) are out of range ({%d, %d, %d}).",
+ coords.x, coords.y, coords.z,
+ self->GetSizeX(), self->GetSizeY(), self->GetSizeZ()
+ );
+ }
+
+ // Get the block info:
+ L.Push((self->*Fn)(coords.x, coords.y, coords.z));
+ return 0;
+}
+
+
+
+
+
+/** Templated bindings for the SetBlock___() functions.
+DataType is either BLOCKTYPE or NIBBLETYPE.
+DataTypeFlag is the ba___ constant used for the datatypebeing manipulated.
+Fn is the setter function.
+Also supports the Vector3i overloads (TODO: document these (?)). */
+template <
+ typename DataType,
+ int DataTypeFlag,
+ void (cBlockArea::*Fn)(int, int, int, DataType)
+>
+static int SetBlock(lua_State * a_LuaState)
+{
+ // Check the common params:
+ cLuaState L(a_LuaState);
+ if (!L.CheckParamSelf("cBlockArea"))
+ {
+ return 0;
+ }
+
+ // Read the common params:
+ cBlockArea * self;
+ if (!L.GetStackValues(1, self))
+ {
+ return cManualBindings::ApiParamError(a_LuaState, "Cannot read the 'self' param.");
+ }
+
+ // Check the datatype's presence:
+ if ((self->GetDataTypes() & DataTypeFlag) == 0)
+ {
+ return cManualBindings::ApiParamError(a_LuaState, "The area doesn't contain the datatype (%d).", DataTypeFlag);
+ }
+
+ // Read the overloaded params:
+ Vector3i coords;
+ auto idx = readVector3iOverloadParams(L, 2, coords, "coords");
+ if (!self->IsValidCoords(coords))
+ {
+ return cManualBindings::ApiParamError(a_LuaState, "The coords ({%d, %d, %d}) are out of range ({%d, %d, %d} - {%d, %d, %d}).",
+ coords.x, coords.y, coords.z,
+ self->GetOriginX(), self->GetOriginY(), self->GetOriginZ(),
+ self->GetOriginX() + self->GetSizeX() - 1, self->GetOriginY() + self->GetSizeY() - 1, self->GetOriginZ() + self->GetSizeZ() - 1
+ );
+ }
+ DataType data;
+ L.GetStackValues(idx, data);
+
+ // Set the block info:
+ (self->*Fn)(coords.x, coords.y, coords.z, data);
+ return 0;
+}
+
+
+
+
+
+/** Templated bindings for the SetRelBlock___() functions.
+DataType is either BLOCKTYPE or NIBBLETYPE.
+DataTypeFlag is the ba___ constant used for the datatypebeing manipulated.
+Fn is the setter function.
+Also supports the Vector3i overloads (TODO: document these (?)). */
+template <
+ typename DataType,
+ int DataTypeFlag,
+ void (cBlockArea::*Fn)(int, int, int, DataType)
+>
+static int SetRelBlock(lua_State * a_LuaState)
+{
+ // Check the common params:
+ cLuaState L(a_LuaState);
+ if (!L.CheckParamSelf("cBlockArea"))
+ {
+ return 0;
+ }
+
+ // Read the common params:
+ cBlockArea * self;
+ if (!L.GetStackValues(1, self))
+ {
+ return cManualBindings::ApiParamError(a_LuaState, "Cannot read the 'self' param.");
+ }
+
+ // Check the datatype's presence:
+ if ((self->GetDataTypes() & DataTypeFlag) == 0)
+ {
+ return cManualBindings::ApiParamError(a_LuaState, "The area doesn't contain the datatype (%d).", DataTypeFlag);
+ }
+
+ // Read the overloaded params:
+ Vector3i coords;
+ auto idx = readVector3iOverloadParams(L, 2, coords, "coords");
+ if (!self->IsValidRelCoords(coords))
+ {
+ return cManualBindings::ApiParamError(a_LuaState, "The coords ({%d, %d, %d}) are out of range ({%d, %d, %d}).",
+ coords.x, coords.y, coords.z,
+ self->GetSizeX(), self->GetSizeY(), self->GetSizeZ()
+ );
+ }
+ DataType data;
+ L.GetStackValues(idx, data);
+
+ // Set the block info:
+ (self->*Fn)(coords.x, coords.y, coords.z, data);
+ return 0;
+}
+
+
+
+
+
+void cManualBindings::BindBlockArea(lua_State * a_LuaState)
+{
+ tolua_beginmodule(a_LuaState, nullptr);
+ tolua_beginmodule(a_LuaState, "cBlockArea");
+ tolua_function(a_LuaState, "Create", tolua_cBlockArea_Create);
+ tolua_function(a_LuaState, "DoWithBlockEntityAt", DoWithXYZ<cBlockArea, cBlockEntity, &cBlockArea::DoWithBlockEntityAt, &cBlockArea::IsValidCoords>);
+ tolua_function(a_LuaState, "DoWithBlockEntityRelAt", DoWithXYZ<cBlockArea, cBlockEntity, &cBlockArea::DoWithBlockEntityRelAt, &cBlockArea::IsValidRelCoords>);
+ tolua_function(a_LuaState, "FillRelCuboid", tolua_cBlockArea_FillRelCuboid);
+ tolua_function(a_LuaState, "ForEachBlockEntity", ForEach< cBlockArea, cBlockEntity, &cBlockArea::ForEachBlockEntity>);
+ tolua_function(a_LuaState, "GetBlockLight", GetBlock<NIBBLETYPE, cBlockArea::baLight, &cBlockArea::GetRelBlockLight>);
+ tolua_function(a_LuaState, "GetBlockMeta", GetBlock<NIBBLETYPE, cBlockArea::baMetas, &cBlockArea::GetRelBlockMeta>);
+ tolua_function(a_LuaState, "GetBlockSkyLight", GetBlock<NIBBLETYPE, cBlockArea::baSkyLight, &cBlockArea::GetRelBlockSkyLight>);
+ tolua_function(a_LuaState, "GetBlockType", GetBlock<BLOCKTYPE, cBlockArea::baTypes, &cBlockArea::GetRelBlockType>);
+ tolua_function(a_LuaState, "GetBlockTypeMeta", tolua_cBlockArea_GetBlockTypeMeta);
+ tolua_function(a_LuaState, "GetCoordRange", tolua_cBlockArea_GetCoordRange);
+ tolua_function(a_LuaState, "GetNonAirCropRelCoords", tolua_cBlockArea_GetNonAirCropRelCoords);
+ tolua_function(a_LuaState, "GetOrigin", tolua_cBlockArea_GetOrigin);
+ tolua_function(a_LuaState, "GetRelBlockLight", GetRelBlock<NIBBLETYPE, cBlockArea::baLight, &cBlockArea::GetRelBlockLight>);
+ tolua_function(a_LuaState, "GetRelBlockMeta", GetRelBlock<NIBBLETYPE, cBlockArea::baMetas, &cBlockArea::GetRelBlockMeta>);
+ tolua_function(a_LuaState, "GetRelBlockSkyLight", GetRelBlock<NIBBLETYPE, cBlockArea::baSkyLight, &cBlockArea::GetRelBlockSkyLight>);
+ tolua_function(a_LuaState, "GetRelBlockType", GetRelBlock<BLOCKTYPE, cBlockArea::baTypes, &cBlockArea::GetRelBlockType>);
+ tolua_function(a_LuaState, "GetRelBlockTypeMeta", tolua_cBlockArea_GetRelBlockTypeMeta);
+ tolua_function(a_LuaState, "GetSize", tolua_cBlockArea_GetSize);
+ tolua_function(a_LuaState, "LoadFromSchematicFile", tolua_cBlockArea_LoadFromSchematicFile);
+ tolua_function(a_LuaState, "LoadFromSchematicString", tolua_cBlockArea_LoadFromSchematicString);
+ tolua_function(a_LuaState, "Read", tolua_cBlockArea_Read);
+ tolua_function(a_LuaState, "RelLine", tolua_cBlockArea_RelLine);
+ tolua_function(a_LuaState, "SaveToSchematicFile", tolua_cBlockArea_SaveToSchematicFile);
+ tolua_function(a_LuaState, "SaveToSchematicString", tolua_cBlockArea_SaveToSchematicString);
+ tolua_function(a_LuaState, "SetBlockType", SetBlock<BLOCKTYPE, cBlockArea::baTypes, &cBlockArea::SetRelBlockType>);
+ tolua_function(a_LuaState, "SetBlockMeta", SetBlock<NIBBLETYPE, cBlockArea::baMetas, &cBlockArea::SetRelBlockMeta>);
+ tolua_function(a_LuaState, "SetBlockLight", SetBlock<NIBBLETYPE, cBlockArea::baLight, &cBlockArea::SetRelBlockLight>);
+ tolua_function(a_LuaState, "SetBlockSkyLight", SetBlock<NIBBLETYPE, cBlockArea::baSkyLight, &cBlockArea::SetRelBlockSkyLight>);
+ tolua_function(a_LuaState, "SetRelBlockType", SetRelBlock<BLOCKTYPE, cBlockArea::baTypes, &cBlockArea::SetRelBlockType>);
+ tolua_function(a_LuaState, "SetRelBlockMeta", SetRelBlock<NIBBLETYPE, cBlockArea::baMetas, &cBlockArea::SetRelBlockMeta>);
+ tolua_function(a_LuaState, "SetRelBlockLight", SetRelBlock<NIBBLETYPE, cBlockArea::baLight, &cBlockArea::SetRelBlockLight>);
+ tolua_function(a_LuaState, "SetRelBlockSkyLight", SetRelBlock<NIBBLETYPE, cBlockArea::baSkyLight, &cBlockArea::SetRelBlockSkyLight>);
+ tolua_function(a_LuaState, "Write", tolua_cBlockArea_Write);
+ tolua_endmodule(a_LuaState);
+ tolua_endmodule(a_LuaState);
+}
+
+
+
+
diff --git a/src/BlockArea.cpp b/src/BlockArea.cpp
index bca5544f9..a35c391fa 100644
--- a/src/BlockArea.cpp
+++ b/src/BlockArea.cpp
@@ -14,6 +14,7 @@
#include "Blocks/BlockHandler.h"
#include "Cuboid.h"
#include "ChunkData.h"
+#include "BlockEntities/BlockEntity.h"
@@ -317,12 +318,37 @@ cBlockArea::~cBlockArea()
+bool cBlockArea::IsValidDataTypeCombination(int a_DataTypes)
+{
+ // BlockEntities require that BlockTypes be present, too
+ if ((a_DataTypes & baBlockEntities) != 0)
+ {
+ if ((a_DataTypes & baTypes) == 0)
+ {
+ return false;
+ }
+ }
+
+ // All other combinations are considered valid
+ return true;
+}
+
+
+
+
+
void cBlockArea::Clear(void)
{
delete[] m_BlockTypes; m_BlockTypes = nullptr;
delete[] m_BlockMetas; m_BlockMetas = nullptr;
delete[] m_BlockLight; m_BlockLight = nullptr;
delete[] m_BlockSkyLight; m_BlockSkyLight = nullptr;
+ if (m_BlockEntities != nullptr)
+ {
+ ClearBlockEntities(*m_BlockEntities);
+ m_BlockEntities.reset();
+ }
+ m_BlockEntities.reset();
m_Origin.Set(0, 0, 0);
m_Size.Set(0, 0, 0);
}
@@ -333,13 +359,10 @@ void cBlockArea::Clear(void)
void cBlockArea::Create(int a_SizeX, int a_SizeY, int a_SizeZ, int a_DataTypes)
{
- if ((a_SizeX < 0) || (a_SizeY < 0) || (a_SizeZ < 0))
- {
- LOGWARNING("Creating a cBlockArea with a negative size! Call to Create ignored. (%d, %d, %d)",
- a_SizeX, a_SizeY, a_SizeZ
- );
- return;
- }
+ ASSERT(a_SizeX > 0);
+ ASSERT(a_SizeY > 0);
+ ASSERT(a_SizeZ > 0);
+ ASSERT(IsValidDataTypeCombination(a_DataTypes));
// Warn if the height is too much, but proceed with the creation:
if (a_SizeY > cChunkDef::Height)
@@ -381,6 +404,10 @@ void cBlockArea::Create(int a_SizeX, int a_SizeY, int a_SizeZ, int a_DataTypes)
m_BlockSkyLight[i] = 0x0f;
}
}
+ if ((a_DataTypes & baBlockEntities) != 0)
+ {
+ m_BlockEntities = cpp14::make_unique<cBlockEntities>();
+ }
m_Size.Set(a_SizeX, a_SizeY, a_SizeZ);
m_Origin.Set(0, 0, 0);
}
@@ -434,57 +461,60 @@ void cBlockArea::SetOrigin(const Vector3i & a_Origin)
+bool cBlockArea::IsValidRelCoords(int a_RelX, int a_RelY, int a_RelZ) const
+{
+ return (
+ (a_RelX >= 0) && (a_RelX < m_Size.x) &&
+ (a_RelY >= 0) && (a_RelY < m_Size.y) &&
+ (a_RelZ >= 0) && (a_RelZ < m_Size.z)
+ );
+}
+
+
+
+
+
+bool cBlockArea::IsValidRelCoords(const Vector3i & a_RelCoords) const
+{
+ return IsValidRelCoords(a_RelCoords.x, a_RelCoords.y, a_RelCoords.z);
+}
+
+
+
+
+
+bool cBlockArea::IsValidCoords(int a_BlockX, int a_BlockY, int a_BlockZ) const
+{
+ return IsValidRelCoords(a_BlockX - m_Origin.x, a_BlockY - m_Origin.y, a_BlockZ - m_Origin.z);
+}
+
+
+
+
+
+bool cBlockArea::IsValidCoords(const Vector3i & a_Coords) const
+{
+ return IsValidRelCoords(a_Coords - m_Origin);
+}
+
+
+
+
+
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:
- if (a_MinBlockX > a_MaxBlockX)
- {
- std::swap(a_MinBlockX, a_MaxBlockX);
- }
- if (a_MinBlockY > a_MaxBlockY)
- {
- std::swap(a_MinBlockY, a_MaxBlockY);
- }
- if (a_MinBlockZ > a_MaxBlockZ)
- {
- std::swap(a_MinBlockZ, a_MaxBlockZ);
- }
+ ASSERT(IsValidDataTypeCombination(a_DataTypes));
+ ASSERT(cChunkDef::IsValidHeight(a_MinBlockY));
+ ASSERT(cChunkDef::IsValidHeight(a_MaxBlockY));
+ ASSERT(a_MinBlockX <= a_MaxBlockX);
+ ASSERT(a_MinBlockY <= a_MaxBlockX);
+ ASSERT(a_MinBlockZ <= a_MaxBlockZ);
// Include the Max coords:
a_MaxBlockX += 1;
a_MaxBlockY += 1;
a_MaxBlockZ += 1;
- // Check coords validity:
- if (a_MinBlockY < 0)
- {
- LOGWARNING("%s: MinBlockY less than zero, adjusting to zero. Coords: {%d, %d, %d} - {%d, %d, %d}",
- __FUNCTION__, a_MinBlockX, a_MinBlockY, a_MinBlockZ, a_MaxBlockX, a_MaxBlockY, a_MaxBlockZ
- );
- a_MinBlockY = 0;
- }
- else if (a_MinBlockY >= cChunkDef::Height)
- {
- LOGWARNING("%s: MinBlockY more than chunk height, adjusting to chunk height. Coords: {%d, %d, %d} - {%d, %d, %d}",
- __FUNCTION__, a_MinBlockX, a_MinBlockY, a_MinBlockZ, a_MaxBlockX, a_MaxBlockY, a_MaxBlockZ
- );
- a_MinBlockY = cChunkDef::Height - 1;
- }
- if (a_MaxBlockY < 0)
- {
- LOGWARNING("%s: MaxBlockY less than zero, adjusting to zero. Coords: {%d, %d, %d} - {%d, %d, %d}",
- __FUNCTION__, a_MinBlockX, a_MinBlockY, a_MinBlockZ, a_MaxBlockX, a_MaxBlockY, a_MaxBlockZ
- );
- a_MaxBlockY = 0;
- }
- else if (a_MaxBlockY > cChunkDef::Height)
- {
- LOGWARNING("%s: MaxBlockY more than chunk height, adjusting to chunk height. Coords: {%d, %d, %d} - {%d, %d, %d}",
- __FUNCTION__, a_MinBlockX, a_MinBlockY, a_MinBlockZ, a_MaxBlockX, a_MaxBlockY, a_MaxBlockZ
- );
- a_MaxBlockY = cChunkDef::Height;
- }
-
// Allocate the needed memory:
Clear();
if (!SetSize(a_MaxBlockX - a_MinBlockX, a_MaxBlockY - a_MinBlockY, a_MaxBlockZ - a_MinBlockZ, a_DataTypes))
@@ -547,19 +577,8 @@ bool cBlockArea::Read(cForEachChunkProvider & a_ForEachChunkProvider, const Vect
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?
- a_DataTypes = a_DataTypes & GetDataTypes(); // For release builds, silently cut off the datatypes that I don't have
-
- // Check coords validity:
- if (a_MinBlockY < 0)
- {
- LOGWARNING("%s: MinBlockY less than zero, adjusting to zero", __FUNCTION__);
- a_MinBlockY = 0;
- }
- else if (a_MinBlockY > cChunkDef::Height - m_Size.y)
- {
- LOGWARNING("%s: MinBlockY + m_SizeY more than chunk height, adjusting to chunk height", __FUNCTION__);
- a_MinBlockY = std::max(cChunkDef::Height - m_Size.y, 0);
- }
+ ASSERT(cChunkDef::IsValidHeight(a_MinBlockY));
+ ASSERT(cChunkDef::IsValidHeight(a_MinBlockY + m_Size.y - 1));
return a_ForEachChunkProvider.WriteBlockArea(*this, a_MinBlockX, a_MinBlockY, a_MinBlockZ, a_DataTypes);
}
@@ -609,6 +628,15 @@ void cBlockArea::CopyTo(cBlockArea & a_Into) const
{
memcpy(a_Into.m_BlockSkyLight, m_BlockSkyLight, BlockCount * sizeof(NIBBLETYPE));
}
+ if (HasBlockEntities())
+ {
+ ClearBlockEntities(*(a_Into.m_BlockEntities));
+ for (const auto & keyPair: *m_BlockEntities)
+ {
+ const auto & pos = keyPair.second->GetPos();
+ a_Into.m_BlockEntities->insert({keyPair.first, keyPair.second->Clone(pos.x, pos.y, pos.z)});
+ }
+ }
}
@@ -705,6 +733,40 @@ void cBlockArea::Crop(int a_AddMinX, int a_SubMaxX, int a_AddMinY, int a_SubMaxY
{
CropNibbles(m_BlockSkyLight, a_AddMinX, a_SubMaxX, a_AddMinY, a_SubMaxY, a_AddMinZ, a_SubMaxZ);
}
+
+ auto maxX = m_Size.x - a_SubMaxX;
+ auto maxY = m_Size.y - a_SubMaxY;
+ auto maxZ = m_Size.z - a_SubMaxZ;
+
+ // Move and crop block Entities:
+ cBlockEntities oldBE;
+ std::swap(oldBE, *m_BlockEntities);
+ for (const auto & keyPair: oldBE)
+ {
+ auto & be = keyPair.second;
+ auto posX = be->GetPosX();
+ auto posY = be->GetPosY();
+ auto posZ = be->GetPosZ();
+ if (
+ (posX < a_AddMinX) || (posX >= maxX) ||
+ (posY < a_AddMinY) || (posY >= maxY) ||
+ (posZ < a_AddMinZ) || (posZ >= maxZ)
+ )
+ {
+ // The block entity is out of new coord range, remove it:
+ delete be;
+ }
+ else
+ {
+ // The block entity is within the new coords, recalculate its coords to match the new area:
+ posX -= a_AddMinX;
+ posY -= a_AddMinY;
+ posZ -= a_AddMinZ;
+ be->SetPos(posX, posY, posZ);
+ m_BlockEntities->insert({MakeIndex(posX, posY, posZ), std::move(be)});
+ }
+ }
+
m_Origin.Move(a_AddMinX, a_AddMinY, a_AddMinZ);
m_Size.x -= a_AddMinX + a_SubMaxX;
m_Size.y -= a_AddMinY + a_SubMaxY;
@@ -733,6 +795,20 @@ void cBlockArea::Expand(int a_SubMinX, int a_AddMaxX, int a_SubMinY, int a_AddMa
{
ExpandNibbles(m_BlockSkyLight, a_SubMinX, a_AddMaxX, a_SubMinY, a_AddMaxY, a_SubMinZ, a_AddMaxZ);
}
+
+ // Move block entities:
+ cBlockEntities oldBE;
+ std::swap(oldBE, *m_BlockEntities);
+ for (const auto & keyPair: oldBE)
+ {
+ auto & be = keyPair.second;
+ auto posX = be->GetPosX() + a_SubMinX;
+ auto posY = be->GetPosY() + a_SubMinY;
+ auto posZ = be->GetPosZ() + a_SubMinZ;
+ be->SetPos(posX, posY, posZ);
+ m_BlockEntities->insert({MakeIndex(posX, posY, posZ), std::move(be)});
+ }
+
m_Origin.Move(-a_SubMinX, -a_SubMinY, -a_SubMinZ);
m_Size.x += a_SubMinX + a_AddMaxX;
m_Size.y += a_SubMinY + a_AddMaxY;
@@ -813,6 +889,12 @@ void cBlockArea::Fill(int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_Block
m_BlockSkyLight[i] = a_BlockSkyLight;
}
}
+
+ // If the area contains block entities, remove those not matching and replace with whatever block entity block was filled
+ if (HasBlockEntities() && ((a_DataTypes & baTypes) != 0))
+ {
+ RescanBlockEntities();
+ }
}
@@ -860,6 +942,12 @@ void cBlockArea::FillRelCuboid(int a_MinRelX, int a_MaxRelX, int a_MinRelY, int
m_BlockSkyLight[MakeIndex(x, y, z)] = a_BlockSkyLight;
} // for x, z, y
}
+
+ // If the area contains block entities, remove those in the affected cuboid and replace with whatever block entity block was filled:
+ if (HasBlockEntities() && ((a_DataTypes & baTypes) != 0))
+ {
+ RescanBlockEntities();
+ }
}
@@ -1054,6 +1142,23 @@ void cBlockArea::RotateCCW(void)
delete[] NewTypes; NewTypes = nullptr;
delete[] NewMetas; NewMetas = nullptr;
+ // Rotate the BlockEntities:
+ if (HasBlockEntities())
+ {
+ cBlockEntities oldBE;
+ std::swap(oldBE, *m_BlockEntities);
+ for (const auto & keyPair: oldBE)
+ {
+ auto & be = keyPair.second;
+ auto newX = be->GetPosZ();
+ auto newY = be->GetPosY();
+ auto newZ = m_Size.x - be->GetPosX() - 1;
+ auto newIdx = newX + newZ * m_Size.z + newY * m_Size.x * m_Size.z;
+ be->SetPos(newX, newY, newZ);
+ m_BlockEntities->insert({newIdx, std::move(be)});
+ }
+ }
+
std::swap(m_Size.x, m_Size.z);
}
@@ -1099,6 +1204,23 @@ void cBlockArea::RotateCW(void)
delete[] NewTypes; NewTypes = nullptr;
delete[] NewMetas; NewMetas = nullptr;
+ // Rotate the BlockEntities:
+ if (HasBlockEntities())
+ {
+ cBlockEntities oldBE;
+ std::swap(oldBE, *m_BlockEntities);
+ for (const auto & keyPair: oldBE)
+ {
+ auto & be = keyPair.second;
+ auto newX = m_Size.z - be->GetPosZ() - 1;
+ auto newY = be->GetPosY();
+ auto newZ = be->GetPosX();
+ auto newIdx = newX + newZ * m_Size.z + newY * m_Size.x * m_Size.z;
+ be->SetPos(newX, newY, newZ);
+ m_BlockEntities->insert({newIdx, std::move(be)});
+ }
+ }
+
std::swap(m_Size.x, m_Size.z);
}
@@ -1140,6 +1262,23 @@ void cBlockArea::MirrorXY(void)
} // for x
} // for z
} // for y
+
+ // Mirror the BlockEntities:
+ if (HasBlockEntities())
+ {
+ cBlockEntities oldBE;
+ std::swap(oldBE, *m_BlockEntities);
+ for (const auto & keyPair: oldBE)
+ {
+ auto & be = keyPair.second;
+ auto newX = be->GetPosX();
+ auto newY = be->GetPosY();
+ auto newZ = MaxZ - be->GetPosZ();
+ auto newIdx = MakeIndex(newX, newY, newZ);
+ be->SetPos(newX, newY, newZ);
+ m_BlockEntities->insert({newIdx, std::move(be)});
+ }
+ }
}
@@ -1180,6 +1319,23 @@ void cBlockArea::MirrorXZ(void)
} // for x
} // for z
} // for y
+
+ // Mirror the BlockEntities:
+ if (HasBlockEntities())
+ {
+ cBlockEntities oldBE;
+ std::swap(oldBE, *m_BlockEntities);
+ for (const auto & keyPair: oldBE)
+ {
+ auto & be = keyPair.second;
+ auto newX = be->GetPosX();
+ auto newY = MaxY - be->GetPosY();
+ auto newZ = be->GetPosZ();
+ auto newIdx = MakeIndex(newX, newY, newZ);
+ be->SetPos(newX, newY, newZ);
+ m_BlockEntities->insert({newIdx, std::move(be)});
+ }
+ }
}
@@ -1220,6 +1376,23 @@ void cBlockArea::MirrorYZ(void)
} // for x
} // for z
} // for y
+
+ // Mirror the BlockEntities:
+ if (HasBlockEntities())
+ {
+ cBlockEntities oldBE;
+ std::swap(oldBE, *m_BlockEntities);
+ for (const auto & keyPair: oldBE)
+ {
+ auto & be = keyPair.second;
+ auto newX = MaxX - be->GetPosX();
+ auto newY = be->GetPosY();
+ auto newZ = be->GetPosZ();
+ auto newIdx = MakeIndex(newX, newY, newZ);
+ be->SetPos(newX, newY, newZ);
+ m_BlockEntities->insert({newIdx, std::move(be)});
+ }
+ }
}
@@ -1264,6 +1437,24 @@ void cBlockArea::RotateCCWNoMeta(void)
std::swap(m_BlockMetas, NewMetas);
delete[] NewMetas; NewMetas = nullptr;
}
+
+ // Rotate the BlockEntities:
+ if (HasBlockEntities())
+ {
+ cBlockEntities oldBE;
+ std::swap(oldBE, *m_BlockEntities);
+ for (const auto & keyPair: oldBE)
+ {
+ auto & be = keyPair.second;
+ auto newX = be->GetPosZ();
+ auto newY = be->GetPosY();
+ auto newZ = m_Size.x - be->GetPosX() - 1;
+ auto newIdx = newX + newZ * m_Size.z + newY * m_Size.x * m_Size.z;
+ be->SetPos(newX, newY, newZ);
+ m_BlockEntities->insert({newIdx, std::move(be)});
+ }
+ }
+
std::swap(m_Size.x, m_Size.z);
}
@@ -1309,6 +1500,24 @@ void cBlockArea::RotateCWNoMeta(void)
std::swap(m_BlockMetas, NewMetas);
delete[] NewMetas; NewMetas = nullptr;
}
+
+ // Rotate the BlockEntities:
+ if (HasBlockEntities())
+ {
+ cBlockEntities oldBE;
+ std::swap(oldBE, *m_BlockEntities);
+ for (const auto & keyPair: oldBE)
+ {
+ auto & be = keyPair.second;
+ auto newX = m_Size.z - be->GetPosZ() - 1;
+ auto newY = be->GetPosY();
+ auto newZ = be->GetPosX();
+ auto newIdx = newX + newZ * m_Size.z + newY * m_Size.x * m_Size.z;
+ be->SetPos(newX, newY, newZ);
+ m_BlockEntities->insert({newIdx, std::move(be)});
+ }
+ }
+
std::swap(m_Size.x, m_Size.z);
}
@@ -1347,6 +1556,23 @@ void cBlockArea::MirrorXYNoMeta(void)
} // for z
} // for y
} // if (HasBlockMetas)
+
+ // Mirror the BlockEntities:
+ if (HasBlockEntities())
+ {
+ cBlockEntities oldBE;
+ std::swap(oldBE, *m_BlockEntities);
+ for (const auto & keyPair: oldBE)
+ {
+ auto & be = keyPair.second;
+ auto newX = be->GetPosX();
+ auto newY = be->GetPosY();
+ auto newZ = MaxZ - be->GetPosZ();
+ auto newIdx = MakeIndex(newX, newY, newZ);
+ be->SetPos(newX, newY, newZ);
+ m_BlockEntities->insert({newIdx, std::move(be)});
+ }
+ }
}
@@ -1384,6 +1610,23 @@ void cBlockArea::MirrorXZNoMeta(void)
} // for z
} // for y
} // if (HasBlockMetas)
+
+ // Mirror the BlockEntities:
+ if (HasBlockEntities())
+ {
+ cBlockEntities oldBE;
+ std::swap(oldBE, *m_BlockEntities);
+ for (const auto & keyPair: oldBE)
+ {
+ auto & be = keyPair.second;
+ auto newX = be->GetPosX();
+ auto newY = MaxY - be->GetPosY();
+ auto newZ = be->GetPosZ();
+ auto newIdx = MakeIndex(newX, newY, newZ);
+ be->SetPos(newX, newY, newZ);
+ m_BlockEntities->insert({newIdx, std::move(be)});
+ }
+ }
}
@@ -1421,6 +1664,23 @@ void cBlockArea::MirrorYZNoMeta(void)
} // for z
} // for y
} // if (HasBlockMetas)
+
+ // Mirror the BlockEntities:
+ if (HasBlockEntities())
+ {
+ cBlockEntities oldBE;
+ std::swap(oldBE, *m_BlockEntities);
+ for (const auto & keyPair: oldBE)
+ {
+ auto & be = keyPair.second;
+ auto newX = MaxX - be->GetPosX();
+ auto newY = be->GetPosY();
+ auto newZ = be->GetPosZ();
+ auto newIdx = MakeIndex(newX, newY, newZ);
+ be->SetPos(newX, newY, newZ);
+ m_BlockEntities->insert({newIdx, std::move(be)});
+ }
+ }
}
@@ -1429,12 +1689,29 @@ void cBlockArea::MirrorYZNoMeta(void)
void cBlockArea::SetRelBlockType(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType)
{
- if (m_BlockTypes == nullptr)
+ ASSERT(m_BlockTypes != nullptr);
+ auto idx = MakeIndex(a_RelX, a_RelY, a_RelZ);
+ m_BlockTypes[idx] = a_BlockType;
+
+ // Update the block entities, if appropriate:
+ if (HasBlockEntities())
{
- LOGWARNING("cBlockArea: BlockTypes have not been read!");
- return;
+ auto itr = m_BlockEntities->find(idx);
+ if (itr != m_BlockEntities->end())
+ {
+ if (itr->second->GetBlockType() == a_BlockType)
+ {
+ // The block entity is for the same block type, keep the current one
+ return;
+ }
+ m_BlockEntities->erase(itr);
+ }
+ if (cBlockEntity::IsBlockEntityBlockType(a_BlockType))
+ {
+ NIBBLETYPE meta = HasBlockMetas() ? m_BlockMetas[idx] : 0;
+ m_BlockEntities->insert({idx, cBlockEntity::CreateByBlockType(a_BlockType, meta, a_RelX, a_RelY, a_RelZ)});
+ }
}
- m_BlockTypes[MakeIndex(a_RelX, a_RelY, a_RelZ)] = a_BlockType;
}
@@ -1609,6 +1886,25 @@ void cBlockArea::SetRelBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, B
{
m_BlockMetas[idx] = a_BlockMeta;
}
+
+ // Update the block entities, if appropriate:
+ if (HasBlockEntities())
+ {
+ auto itr = m_BlockEntities->find(idx);
+ if (itr != m_BlockEntities->end())
+ {
+ if (itr->second->GetBlockType() == a_BlockType)
+ {
+ // The block entity is for the same block type, keep the current one
+ return;
+ }
+ m_BlockEntities->erase(itr);
+ }
+ if (cBlockEntity::IsBlockEntityBlockType(a_BlockType))
+ {
+ m_BlockEntities->insert({idx, cBlockEntity::CreateByBlockType(a_BlockType, a_BlockMeta, a_RelX, a_RelY, a_RelZ)});
+ }
+ }
}
@@ -1828,6 +2124,10 @@ int cBlockArea::GetDataTypes(void) const
{
res |= baSkyLight;
}
+ if (m_BlockEntities != nullptr)
+ {
+ res |= baBlockEntities;
+ }
return res;
}
@@ -1838,8 +2138,9 @@ int cBlockArea::GetDataTypes(void) const
bool cBlockArea::SetSize(int a_SizeX, int a_SizeY, int a_SizeZ, int a_DataTypes)
{
ASSERT(m_BlockTypes == nullptr); // Has been cleared
+ ASSERT(IsValidDataTypeCombination(a_DataTypes));
- if (a_DataTypes & baTypes)
+ if ((a_DataTypes & baTypes) != 0)
{
m_BlockTypes = new BLOCKTYPE[a_SizeX * a_SizeY * a_SizeZ];
if (m_BlockTypes == nullptr)
@@ -1847,7 +2148,7 @@ bool cBlockArea::SetSize(int a_SizeX, int a_SizeY, int a_SizeZ, int a_DataTypes)
return false;
}
}
- if (a_DataTypes & baMetas)
+ if ((a_DataTypes & baMetas) != 0)
{
m_BlockMetas = new NIBBLETYPE[a_SizeX * a_SizeY * a_SizeZ];
if (m_BlockMetas == nullptr)
@@ -1857,7 +2158,7 @@ bool cBlockArea::SetSize(int a_SizeX, int a_SizeY, int a_SizeZ, int a_DataTypes)
return false;
}
}
- if (a_DataTypes & baLight)
+ if ((a_DataTypes & baLight) != 0)
{
m_BlockLight = new NIBBLETYPE[a_SizeX * a_SizeY * a_SizeZ];
if (m_BlockLight == nullptr)
@@ -1869,7 +2170,7 @@ bool cBlockArea::SetSize(int a_SizeX, int a_SizeY, int a_SizeZ, int a_DataTypes)
return false;
}
}
- if (a_DataTypes & baSkyLight)
+ if ((a_DataTypes & baSkyLight) != 0)
{
m_BlockSkyLight = new NIBBLETYPE[a_SizeX * a_SizeY * a_SizeZ];
if (m_BlockSkyLight == nullptr)
@@ -1883,6 +2184,22 @@ bool cBlockArea::SetSize(int a_SizeX, int a_SizeY, int a_SizeZ, int a_DataTypes)
return false;
}
}
+ if ((a_DataTypes & baBlockEntities) != 0)
+ {
+ m_BlockEntities = cpp14::make_unique<cBlockEntities>();
+ if (m_BlockEntities == nullptr)
+ {
+ delete[] m_BlockSkyLight;
+ m_BlockSkyLight = nullptr;
+ delete[] m_BlockLight;
+ m_BlockLight = nullptr;
+ delete[] m_BlockMetas;
+ m_BlockMetas = nullptr;
+ delete[] m_BlockTypes;
+ m_BlockTypes = nullptr;
+ return false;
+ }
+ }
m_Size.Set(a_SizeX, a_SizeY, a_SizeZ);
return true;
}
@@ -1907,276 +2224,101 @@ int cBlockArea::MakeIndex(int a_RelX, int a_RelY, int a_RelZ) const
-void cBlockArea::SetRelNibble(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_Value, NIBBLETYPE * a_Array)
+bool cBlockArea::DoWithBlockEntityRelAt(int a_RelX, int a_RelY, int a_RelZ, cItemCallback<cBlockEntity> & a_Callback)
{
- if (a_Array == nullptr)
+ ASSERT(IsValidRelCoords(a_RelX, a_RelY, a_RelZ));
+ if (!HasBlockEntities())
{
- LOGWARNING("cBlockArea: datatype has not been read!");
- return;
+ return false;
}
- a_Array[MakeIndex(a_RelX, a_RelY, a_RelZ)] = a_Value;
+ auto idx = MakeIndex(a_RelX, a_RelY, a_RelZ);
+ auto itr = m_BlockEntities->find(idx);
+ if (itr == m_BlockEntities->end())
+ {
+ return false;
+ }
+ return a_Callback.Item(itr->second);
}
-void cBlockArea::SetNibble(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_Value, NIBBLETYPE * a_Array)
+bool cBlockArea::DoWithBlockEntityAt(int a_BlockX, int a_BlockY, int a_BlockZ, cItemCallback<cBlockEntity> & a_Callback)
{
- SetRelNibble(a_BlockX - m_Origin.x, a_BlockY - m_Origin.y, a_BlockZ - m_Origin.z, a_Value, a_Array);
+ return DoWithBlockEntityRelAt(a_BlockX - m_Origin.x, a_BlockY - m_Origin.y, a_BlockZ - m_Origin.z, a_Callback);
}
-NIBBLETYPE cBlockArea::GetRelNibble(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE * a_Array) const
+bool cBlockArea::ForEachBlockEntity(cItemCallback<cBlockEntity> & a_Callback)
{
- if (a_Array == nullptr)
+ if (!HasBlockEntities())
{
- LOGWARNING("cBlockArea: datatype has not been read!");
- return 16;
+ return true;
}
- return a_Array[MakeIndex(a_RelX, a_RelY, a_RelZ)];
+ for (auto & keyPair: *m_BlockEntities)
+ {
+ if (a_Callback.Item(keyPair.second))
+ {
+ return false;
+ }
+ }
+ return true;
}
-NIBBLETYPE cBlockArea::GetNibble(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE * a_Array) const
+void cBlockArea::SetRelNibble(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_Value, NIBBLETYPE * a_Array)
{
- return GetRelNibble(a_BlockX - m_Origin.x, a_BlockY - m_Origin.y, a_BlockZ - m_Origin.z, a_Array);
+ if (a_Array == nullptr)
+ {
+ LOGWARNING("cBlockArea: datatype has not been read!");
+ return;
+ }
+ a_Array[MakeIndex(a_RelX, a_RelY, a_RelZ)] = a_Value;
}
-
-////////////////////////////////////////////////////////////////////////////////
-// cBlockArea::cChunkReader:
-
-cBlockArea::cChunkReader::cChunkReader(cBlockArea & a_Area) :
- m_Area(a_Area),
- m_Origin(a_Area.m_Origin.x, a_Area.m_Origin.y, a_Area.m_Origin.z),
- m_CurrentChunkX(0),
- m_CurrentChunkZ(0)
+void cBlockArea::SetNibble(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_Value, NIBBLETYPE * a_Array)
{
+ SetRelNibble(a_BlockX - m_Origin.x, a_BlockY - m_Origin.y, a_BlockZ - m_Origin.z, a_Value, a_Array);
}
-void cBlockArea::cChunkReader::CopyNibbles(NIBBLETYPE * a_AreaDst, const NIBBLETYPE * a_ChunkSrc)
+NIBBLETYPE cBlockArea::GetRelNibble(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE * a_Array) const
{
- int SizeY = m_Area.m_Size.y;
- int MinY = m_Origin.y;
-
- // SizeX, SizeZ are the dmensions of the block data to copy from the current chunk (size of the geometric union)
- // OffX, OffZ are the offsets of the current chunk data from the area origin
- // BaseX, BaseZ are the offsets of the area data within the current chunk from the chunk borders
- int SizeX = cChunkDef::Width;
- int SizeZ = cChunkDef::Width;
- int OffX, OffZ;
- int BaseX, BaseZ;
- OffX = m_CurrentChunkX * cChunkDef::Width - m_Origin.x;
- if (OffX < 0)
- {
- BaseX = -OffX;
- SizeX += OffX; // SizeX is decreased, OffX is negative
- OffX = 0;
- }
- else
- {
- BaseX = 0;
- }
- OffZ = m_CurrentChunkZ * cChunkDef::Width - m_Origin.z;
- if (OffZ < 0)
- {
- BaseZ = -OffZ;
- SizeZ += OffZ; // SizeZ is decreased, OffZ is negative
- OffZ = 0;
- }
- else
- {
- BaseZ = 0;
- }
- // If the chunk extends beyond the area in the X or Z axis, cut off the Size:
- if ((m_CurrentChunkX + 1) * cChunkDef::Width > m_Origin.x + m_Area.m_Size.x)
- {
- SizeX -= (m_CurrentChunkX + 1) * cChunkDef::Width - (m_Origin.x + m_Area.m_Size.x);
- }
- if ((m_CurrentChunkZ + 1) * cChunkDef::Width > m_Origin.z + m_Area.m_Size.z)
+ if (a_Array == nullptr)
{
- SizeZ -= (m_CurrentChunkZ + 1) * cChunkDef::Width - (m_Origin.z + m_Area.m_Size.z);
+ LOGWARNING("cBlockArea: datatype has not been read!");
+ return 16;
}
-
- for (int y = 0; y < SizeY; y++)
- {
- int ChunkY = MinY + y;
- int AreaY = y;
- for (int z = 0; z < SizeZ; z++)
- {
- int ChunkZ = BaseZ + z;
- int AreaZ = OffZ + z;
- for (int x = 0; x < SizeX; x++)
- {
- int ChunkX = BaseX + x;
- int AreaX = OffX + x;
- a_AreaDst[m_Area.MakeIndex(AreaX, AreaY, AreaZ)] = cChunkDef::GetNibble(a_ChunkSrc, ChunkX, ChunkY, ChunkZ);
- } // for x
- } // for z
- } // for y
+ return a_Array[MakeIndex(a_RelX, a_RelY, a_RelZ)];
}
-bool cBlockArea::cChunkReader::Coords(int a_ChunkX, int a_ChunkZ)
+NIBBLETYPE cBlockArea::GetNibble(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE * a_Array) const
{
- m_CurrentChunkX = a_ChunkX;
- m_CurrentChunkZ = a_ChunkZ;
- return true;
+ return GetRelNibble(a_BlockX - m_Origin.x, a_BlockY - m_Origin.y, a_BlockZ - m_Origin.z, a_Array);
}
-void cBlockArea::cChunkReader::ChunkData(const cChunkData & a_BlockBuffer)
-{
- int SizeY = m_Area.m_Size.y;
- int MinY = m_Origin.y;
-
- // SizeX, SizeZ are the dimensions of the block data to copy from the current chunk (size of the geometric union)
- // OffX, OffZ are the offsets of the current chunk data from the area origin
- // BaseX, BaseZ are the offsets of the area data within the current chunk from the chunk borders
- int SizeX = cChunkDef::Width;
- int SizeZ = cChunkDef::Width;
- int OffX, OffZ;
- int BaseX, BaseZ;
- OffX = m_CurrentChunkX * cChunkDef::Width - m_Origin.x;
- if (OffX < 0)
- {
- BaseX = -OffX;
- SizeX += OffX; // SizeX is decreased, OffX is negative
- OffX = 0;
- }
- else
- {
- BaseX = 0;
- }
- OffZ = m_CurrentChunkZ * cChunkDef::Width - m_Origin.z;
- if (OffZ < 0)
- {
- BaseZ = -OffZ;
- SizeZ += OffZ; // SizeZ is decreased, OffZ is negative
- OffZ = 0;
- }
- else
- {
- BaseZ = 0;
- }
- // If the chunk extends beyond the area in the X or Z axis, cut off the Size:
- if ((m_CurrentChunkX + 1) * cChunkDef::Width > m_Origin.x + m_Area.m_Size.x)
- {
- SizeX -= (m_CurrentChunkX + 1) * cChunkDef::Width - (m_Origin.x + m_Area.m_Size.x);
- }
- if ((m_CurrentChunkZ + 1) * cChunkDef::Width > m_Origin.z + m_Area.m_Size.z)
- {
- SizeZ -= (m_CurrentChunkZ + 1) * cChunkDef::Width - (m_Origin.z + m_Area.m_Size.z);
- }
-
- // Copy the blocktypes:
- if (m_Area.m_BlockTypes != nullptr)
- {
- for (int y = 0; y < SizeY; y++)
- {
- int InChunkY = MinY + y;
- int AreaY = y;
- for (int z = 0; z < SizeZ; z++)
- {
- int InChunkZ = BaseZ + z;
- int AreaZ = OffZ + z;
- for (int x = 0; x < SizeX; x++)
- {
- int InChunkX = BaseX + x;
- int AreaX = OffX + x;
- m_Area.m_BlockTypes[m_Area.MakeIndex(AreaX, AreaY, AreaZ)] = a_BlockBuffer.GetBlock(InChunkX, InChunkY, InChunkZ);
- } // for x
- } // for z
- } // for y
- }
-
- // Copy the block metas:
- if (m_Area.m_BlockMetas != nullptr)
- {
- for (int y = 0; y < SizeY; y++)
- {
- int InChunkY = MinY + y;
- int AreaY = y;
- for (int z = 0; z < SizeZ; z++)
- {
- int InChunkZ = BaseZ + z;
- int AreaZ = OffZ + z;
- for (int x = 0; x < SizeX; x++)
- {
- int InChunkX = BaseX + x;
- int AreaX = OffX + x;
- m_Area.m_BlockMetas[m_Area.MakeIndex(AreaX, AreaY, AreaZ)] = a_BlockBuffer.GetMeta(InChunkX, InChunkY, InChunkZ);
- } // for x
- } // for z
- } // for y
- }
-
- // Copy the blocklight:
- if (m_Area.m_BlockLight != nullptr)
- {
- for (int y = 0; y < SizeY; y++)
- {
- int InChunkY = MinY + y;
- int AreaY = y;
- for (int z = 0; z < SizeZ; z++)
- {
- int InChunkZ = BaseZ + z;
- int AreaZ = OffZ + z;
- for (int x = 0; x < SizeX; x++)
- {
- int InChunkX = BaseX + x;
- int AreaX = OffX + x;
- m_Area.m_BlockLight[m_Area.MakeIndex(AreaX, AreaY, AreaZ)] = a_BlockBuffer.GetBlockLight(InChunkX, InChunkY, InChunkZ);
- } // for x
- } // for z
- } // for y
- }
-
- // Copy the skylight:
- if (m_Area.m_BlockSkyLight != nullptr)
- {
- for (int y = 0; y < SizeY; y++)
- {
- int InChunkY = MinY + y;
- int AreaY = y;
- for (int z = 0; z < SizeZ; z++)
- {
- int InChunkZ = BaseZ + z;
- int AreaZ = OffZ + z;
- for (int x = 0; x < SizeX; x++)
- {
- int InChunkX = BaseX + x;
- int AreaX = OffX + x;
- m_Area.m_BlockSkyLight[m_Area.MakeIndex(AreaX, AreaY, AreaZ)] = a_BlockBuffer.GetSkyLight(InChunkX, InChunkY, InChunkZ);
- } // for x
- } // for z
- } // for y
- }
-}
-
-
-
void cBlockArea::CropBlockTypes(int a_AddMinX, int a_SubMaxX, int a_AddMinY, int a_SubMaxY, int a_AddMinZ, int a_SubMaxZ)
{
@@ -2295,6 +2437,11 @@ void cBlockArea::RelSetData(
NIBBLETYPE a_BlockLight, NIBBLETYPE a_BlockSkyLight
)
{
+ if (!IsValidCoords(a_RelX, a_RelY, a_RelZ))
+ {
+ return;
+ }
+
int Index = MakeIndex(a_RelX, a_RelY, a_RelZ);
if ((a_DataTypes & baTypes) != 0)
{
@@ -2312,6 +2459,27 @@ void cBlockArea::RelSetData(
{
m_BlockSkyLight[Index] = a_BlockSkyLight;
}
+
+ // Update the block entities, if appropriate:
+ if (HasBlockEntities())
+ {
+ auto itr = m_BlockEntities->find(Index);
+ if (itr != m_BlockEntities->end())
+ {
+ if (itr->second->GetBlockType() == a_BlockType)
+ {
+ // The block entity is for the same block type, keep the current one
+ return;
+ }
+ // The block entity is for a different block type, remove it:
+ m_BlockEntities->erase(itr);
+ }
+ if (cBlockEntity::IsBlockEntityBlockType(a_BlockType))
+ {
+ // The block type should have a block entity attached to it, create an empty one:
+ m_BlockEntities->insert({Index, cBlockEntity::CreateByBlockType(a_BlockType, a_BlockMeta, a_RelX, a_RelY, a_RelZ)});
+ }
+ }
}
@@ -2354,7 +2522,7 @@ void cBlockArea::MergeByStrategy(const cBlockArea & a_Src, int a_RelX, int a_Rel
a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(),
m_Size.x, m_Size.y, m_Size.z
);
- return;
+ break;
} // case msOverwrite
case cBlockArea::msFillAir:
@@ -2368,7 +2536,7 @@ void cBlockArea::MergeByStrategy(const cBlockArea & a_Src, int a_RelX, int a_Rel
a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(),
m_Size.x, m_Size.y, m_Size.z
);
- return;
+ break;
} // case msFillAir
case cBlockArea::msImprint:
@@ -2382,7 +2550,7 @@ void cBlockArea::MergeByStrategy(const cBlockArea & a_Src, int a_RelX, int a_Rel
a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(),
m_Size.x, m_Size.y, m_Size.z
);
- return;
+ break;
} // case msImprint
case cBlockArea::msLake:
@@ -2396,7 +2564,7 @@ void cBlockArea::MergeByStrategy(const cBlockArea & a_Src, int a_RelX, int a_Rel
a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(),
m_Size.x, m_Size.y, m_Size.z
);
- return;
+ break;
} // case msLake
case cBlockArea::msSpongePrint:
@@ -2410,7 +2578,7 @@ void cBlockArea::MergeByStrategy(const cBlockArea & a_Src, int a_RelX, int a_Rel
a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(),
m_Size.x, m_Size.y, m_Size.z
);
- return;
+ break;
} // case msSpongePrint
case cBlockArea::msDifference:
@@ -2424,7 +2592,7 @@ void cBlockArea::MergeByStrategy(const cBlockArea & a_Src, int a_RelX, int a_Rel
a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(),
m_Size.x, m_Size.y, m_Size.z
);
- return;
+ break;
} // case msDifference
case cBlockArea::msSimpleCompare:
@@ -2438,7 +2606,7 @@ void cBlockArea::MergeByStrategy(const cBlockArea & a_Src, int a_RelX, int a_Rel
a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(),
m_Size.x, m_Size.y, m_Size.z
);
- return;
+ break;
} // case msSimpleCompare
case cBlockArea::msMask:
@@ -2452,14 +2620,407 @@ void cBlockArea::MergeByStrategy(const cBlockArea & a_Src, int a_RelX, int a_Rel
a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(),
m_Size.x, m_Size.y, m_Size.z
);
- return;
+ break;
} // case msMask
+
+ #ifndef __clang__ // Clang complains about a default case in a switch with all cases covered
+ default:
+ {
+ LOGWARNING("Unknown block area merge strategy: %d", a_Strategy);
+ ASSERT(!"Unknown block area merge strategy");
+ return;
+ }
+ #endif
} // switch (a_Strategy)
- LOGWARNING("Unknown block area merge strategy: %d", a_Strategy);
- ASSERT(!"Unknown block area merge strategy");
- return;
+ if (HasBlockEntities())
+ {
+ if (a_Src.HasBlockEntities())
+ {
+ MergeBlockEntities(a_RelX, a_RelY, a_RelZ, a_Src);
+ }
+ else
+ {
+ RescanBlockEntities();
+ }
+ }
+}
+
+
+
+
+
+void cBlockArea::ClearBlockEntities(cBlockEntities & a_BlockEntities)
+{
+ for (auto & keyPair: a_BlockEntities)
+ {
+ delete keyPair.second;
+ }
+ a_BlockEntities.clear();
+}
+
+
+
+
+
+void cBlockArea::MergeBlockEntities(int a_RelX, int a_RelY, int a_RelZ, const cBlockArea & a_Src)
+{
+ // Only supported with both BlockEntities and BlockTypes (caller should check):
+ ASSERT(HasBlockTypes());
+ ASSERT(HasBlockEntities());
+ ASSERT(a_Src.HasBlockTypes());
+ ASSERT(a_Src.HasBlockEntities());
+
+ // Remove block entities that no longer match the block at their coords:
+ RemoveNonMatchingBlockEntities();
+
+ // Clone BEs from a_Src wherever a BE is missing:
+ for (int y = 0; y < m_Size.y; ++y) for (int z = 0; z < m_Size.z; ++z) for (int x = 0; x < m_Size.x; ++x)
+ {
+ auto idx = MakeIndex(x, y, z);
+ auto type = m_BlockTypes[idx];
+ if (!cBlockEntity::IsBlockEntityBlockType(type))
+ {
+ continue;
+ }
+
+ // This block should have a block entity, check that there is one:
+ auto itr = m_BlockEntities->find(idx);
+ if (itr != m_BlockEntities->end())
+ {
+ // There is one already
+ continue;
+ }
+
+ // Copy a BE from a_Src, if it exists there:
+ auto srcX = x + a_RelX;
+ auto srcY = y + a_RelY;
+ auto srcZ = z + a_RelZ;
+ if (a_Src.IsValidRelCoords(srcX, srcY, srcZ))
+ {
+ auto srcIdx = a_Src.MakeIndex(srcX, srcY, srcZ);
+ auto itrSrc = a_Src.m_BlockEntities->find(srcIdx);
+ if (itrSrc == a_Src.m_BlockEntities->end())
+ {
+ m_BlockEntities->insert({idx, itrSrc->second->Clone(x, y, z)});
+ continue;
+ }
+ }
+ // No BE found in a_Src, insert a new empty one:
+ NIBBLETYPE meta = HasBlockMetas() ? m_BlockMetas[idx] : 0;
+ m_BlockEntities->insert({idx, cBlockEntity::CreateByBlockType(type, meta, x, y, z)});
+ } // for x, z, y
+}
+
+
+
+
+
+void cBlockArea::RescanBlockEntities(void)
+{
+ // Only supported with both BlockEntities and BlockTypes
+ if (!HasBlockEntities() || !HasBlockTypes())
+ {
+ return;
+ }
+
+ // Remove block entities that no longer match the block at their coords:
+ RemoveNonMatchingBlockEntities();
+
+ // Add block entities for all block types that should have a BE assigned to them:
+ for (int y = 0; y < m_Size.y; ++y) for (int z = 0; z < m_Size.z; ++z) for (int x = 0; x < m_Size.x; ++x)
+ {
+ auto idx = MakeIndex(x, y, z);
+ auto type = m_BlockTypes[idx];
+ if (!cBlockEntity::IsBlockEntityBlockType(type))
+ {
+ continue;
+ }
+ // This block should have a block entity, check that there is one:
+ auto itr = m_BlockEntities->find(idx);
+ if (itr != m_BlockEntities->end())
+ {
+ continue;
+ }
+ // Create a new BE for this block:
+ NIBBLETYPE meta = HasBlockMetas() ? m_BlockMetas[idx] : 0;
+ m_BlockEntities->insert({idx, cBlockEntity::CreateByBlockType(type, meta, x, y, z)});
+ } // for x, z, y
+}
+
+
+
+
+
+void cBlockArea::RemoveNonMatchingBlockEntities(void)
+{
+ // Only supported with both BlockEntities and BlockTypes:
+ ASSERT(HasBlockTypes());
+ ASSERT(HasBlockEntities());
+
+ cBlockEntities oldBE;
+ std::swap(oldBE, *m_BlockEntities);
+ for (auto & keyPair: oldBE)
+ {
+ auto type = m_BlockTypes[keyPair.first];
+ if (type == keyPair.second->GetBlockType())
+ {
+ m_BlockEntities->insert({keyPair.first, std::move(keyPair.second)});
+ }
+ else
+ {
+ delete keyPair.second;
+ }
+ }
}
+
+
+////////////////////////////////////////////////////////////////////////////////
+// cBlockArea::cChunkReader:
+
+cBlockArea::cChunkReader::cChunkReader(cBlockArea & a_Area) :
+ m_Area(a_Area),
+ m_AreaBounds(cCuboid(a_Area.GetOrigin(), a_Area.GetOrigin() + a_Area.GetSize())),
+ m_Origin(a_Area.m_Origin.x, a_Area.m_Origin.y, a_Area.m_Origin.z),
+ m_CurrentChunkX(0),
+ m_CurrentChunkZ(0)
+{
+}
+
+
+
+
+
+void cBlockArea::cChunkReader::CopyNibbles(NIBBLETYPE * a_AreaDst, const NIBBLETYPE * a_ChunkSrc)
+{
+ int SizeY = m_Area.m_Size.y;
+ int MinY = m_Origin.y;
+
+ // SizeX, SizeZ are the dmensions of the block data to copy from the current chunk (size of the geometric union)
+ // OffX, OffZ are the offsets of the current chunk data from the area origin
+ // BaseX, BaseZ are the offsets of the area data within the current chunk from the chunk borders
+ int SizeX = cChunkDef::Width;
+ int SizeZ = cChunkDef::Width;
+ int OffX, OffZ;
+ int BaseX, BaseZ;
+ OffX = m_CurrentChunkX * cChunkDef::Width - m_Origin.x;
+ if (OffX < 0)
+ {
+ BaseX = -OffX;
+ SizeX += OffX; // SizeX is decreased, OffX is negative
+ OffX = 0;
+ }
+ else
+ {
+ BaseX = 0;
+ }
+ OffZ = m_CurrentChunkZ * cChunkDef::Width - m_Origin.z;
+ if (OffZ < 0)
+ {
+ BaseZ = -OffZ;
+ SizeZ += OffZ; // SizeZ is decreased, OffZ is negative
+ OffZ = 0;
+ }
+ else
+ {
+ BaseZ = 0;
+ }
+ // If the chunk extends beyond the area in the X or Z axis, cut off the Size:
+ if ((m_CurrentChunkX + 1) * cChunkDef::Width > m_Origin.x + m_Area.m_Size.x)
+ {
+ SizeX -= (m_CurrentChunkX + 1) * cChunkDef::Width - (m_Origin.x + m_Area.m_Size.x);
+ }
+ if ((m_CurrentChunkZ + 1) * cChunkDef::Width > m_Origin.z + m_Area.m_Size.z)
+ {
+ SizeZ -= (m_CurrentChunkZ + 1) * cChunkDef::Width - (m_Origin.z + m_Area.m_Size.z);
+ }
+
+ for (int y = 0; y < SizeY; y++)
+ {
+ int ChunkY = MinY + y;
+ int AreaY = y;
+ for (int z = 0; z < SizeZ; z++)
+ {
+ int ChunkZ = BaseZ + z;
+ int AreaZ = OffZ + z;
+ for (int x = 0; x < SizeX; x++)
+ {
+ int ChunkX = BaseX + x;
+ int AreaX = OffX + x;
+ a_AreaDst[m_Area.MakeIndex(AreaX, AreaY, AreaZ)] = cChunkDef::GetNibble(a_ChunkSrc, ChunkX, ChunkY, ChunkZ);
+ } // for x
+ } // for z
+ } // for y
+}
+
+
+
+
+
+bool cBlockArea::cChunkReader::Coords(int a_ChunkX, int a_ChunkZ)
+{
+ m_CurrentChunkX = a_ChunkX;
+ m_CurrentChunkZ = a_ChunkZ;
+ return true;
+}
+
+
+
+
+
+void cBlockArea::cChunkReader::ChunkData(const cChunkData & a_BlockBuffer)
+{
+ int SizeY = m_Area.m_Size.y;
+ int MinY = m_Origin.y;
+
+ // SizeX, SizeZ are the dimensions of the block data to copy from the current chunk (size of the geometric union)
+ // OffX, OffZ are the offsets of the current chunk data from the area origin
+ // BaseX, BaseZ are the offsets of the area data within the current chunk from the chunk borders
+ int SizeX = cChunkDef::Width;
+ int SizeZ = cChunkDef::Width;
+ int OffX, OffZ;
+ int BaseX, BaseZ;
+ OffX = m_CurrentChunkX * cChunkDef::Width - m_Origin.x;
+ if (OffX < 0)
+ {
+ BaseX = -OffX;
+ SizeX += OffX; // SizeX is decreased, OffX is negative
+ OffX = 0;
+ }
+ else
+ {
+ BaseX = 0;
+ }
+ OffZ = m_CurrentChunkZ * cChunkDef::Width - m_Origin.z;
+ if (OffZ < 0)
+ {
+ BaseZ = -OffZ;
+ SizeZ += OffZ; // SizeZ is decreased, OffZ is negative
+ OffZ = 0;
+ }
+ else
+ {
+ BaseZ = 0;
+ }
+ // If the chunk extends beyond the area in the X or Z axis, cut off the Size:
+ if ((m_CurrentChunkX + 1) * cChunkDef::Width > m_Origin.x + m_Area.m_Size.x)
+ {
+ SizeX -= (m_CurrentChunkX + 1) * cChunkDef::Width - (m_Origin.x + m_Area.m_Size.x);
+ }
+ if ((m_CurrentChunkZ + 1) * cChunkDef::Width > m_Origin.z + m_Area.m_Size.z)
+ {
+ SizeZ -= (m_CurrentChunkZ + 1) * cChunkDef::Width - (m_Origin.z + m_Area.m_Size.z);
+ }
+
+ // Copy the blocktypes:
+ if (m_Area.m_BlockTypes != nullptr)
+ {
+ for (int y = 0; y < SizeY; y++)
+ {
+ int InChunkY = MinY + y;
+ int AreaY = y;
+ for (int z = 0; z < SizeZ; z++)
+ {
+ int InChunkZ = BaseZ + z;
+ int AreaZ = OffZ + z;
+ for (int x = 0; x < SizeX; x++)
+ {
+ int InChunkX = BaseX + x;
+ int AreaX = OffX + x;
+ m_Area.m_BlockTypes[m_Area.MakeIndex(AreaX, AreaY, AreaZ)] = a_BlockBuffer.GetBlock(InChunkX, InChunkY, InChunkZ);
+ } // for x
+ } // for z
+ } // for y
+ }
+
+ // Copy the block metas:
+ if (m_Area.m_BlockMetas != nullptr)
+ {
+ for (int y = 0; y < SizeY; y++)
+ {
+ int InChunkY = MinY + y;
+ int AreaY = y;
+ for (int z = 0; z < SizeZ; z++)
+ {
+ int InChunkZ = BaseZ + z;
+ int AreaZ = OffZ + z;
+ for (int x = 0; x < SizeX; x++)
+ {
+ int InChunkX = BaseX + x;
+ int AreaX = OffX + x;
+ m_Area.m_BlockMetas[m_Area.MakeIndex(AreaX, AreaY, AreaZ)] = a_BlockBuffer.GetMeta(InChunkX, InChunkY, InChunkZ);
+ } // for x
+ } // for z
+ } // for y
+ }
+
+ // Copy the blocklight:
+ if (m_Area.m_BlockLight != nullptr)
+ {
+ for (int y = 0; y < SizeY; y++)
+ {
+ int InChunkY = MinY + y;
+ int AreaY = y;
+ for (int z = 0; z < SizeZ; z++)
+ {
+ int InChunkZ = BaseZ + z;
+ int AreaZ = OffZ + z;
+ for (int x = 0; x < SizeX; x++)
+ {
+ int InChunkX = BaseX + x;
+ int AreaX = OffX + x;
+ m_Area.m_BlockLight[m_Area.MakeIndex(AreaX, AreaY, AreaZ)] = a_BlockBuffer.GetBlockLight(InChunkX, InChunkY, InChunkZ);
+ } // for x
+ } // for z
+ } // for y
+ }
+
+ // Copy the skylight:
+ if (m_Area.m_BlockSkyLight != nullptr)
+ {
+ for (int y = 0; y < SizeY; y++)
+ {
+ int InChunkY = MinY + y;
+ int AreaY = y;
+ for (int z = 0; z < SizeZ; z++)
+ {
+ int InChunkZ = BaseZ + z;
+ int AreaZ = OffZ + z;
+ for (int x = 0; x < SizeX; x++)
+ {
+ int InChunkX = BaseX + x;
+ int AreaX = OffX + x;
+ m_Area.m_BlockSkyLight[m_Area.MakeIndex(AreaX, AreaY, AreaZ)] = a_BlockBuffer.GetSkyLight(InChunkX, InChunkY, InChunkZ);
+ } // for x
+ } // for z
+ } // for y
+ }
+}
+
+
+
+
+void cBlockArea::cChunkReader::BlockEntity(cBlockEntity * a_BlockEntity)
+{
+ if (!m_Area.HasBlockEntities())
+ {
+ return;
+ }
+ if (!m_AreaBounds.IsInside(a_BlockEntity->GetPos()))
+ {
+ return;
+ }
+ auto areaX = a_BlockEntity->GetPosX() - m_Area.m_Origin.x;
+ auto areaY = a_BlockEntity->GetPosY() - m_Area.m_Origin.y;
+ auto areaZ = a_BlockEntity->GetPosZ() - m_Area.m_Origin.z;
+ int Idx = cChunkDef::MakeIndex(areaX, areaY, areaZ);
+ m_Area.m_BlockEntities->insert({Idx, a_BlockEntity->Clone(areaX, areaY, areaZ)});
+}
+
+
+
+
+
diff --git a/src/BlockArea.h b/src/BlockArea.h
index 2100345e4..583b998c2 100644
--- a/src/BlockArea.h
+++ b/src/BlockArea.h
@@ -5,6 +5,8 @@
// The object also supports writing the blockdata back into cWorld, even into other coords
// NOTE: All Nibble values (meta, blocklight, skylight) are stored one-nibble-per-byte for faster access / editting!
+// NOTE: Lua bindings for this object explicitly check parameter values. C++ code is expected to pass in valid params, so the functions ASSERT on invalid params.
+// This includes the datatypes (must be present / valid combination), coords and sizes.
@@ -15,6 +17,7 @@
#include "ForEachChunkProvider.h"
#include "Vector3.h"
#include "ChunkDataCallback.h"
+#include "Cuboid.h"
@@ -38,10 +41,12 @@ public:
/** What data is to be queried (bit-mask) */
enum
{
- baTypes = 1,
- baMetas = 2,
- baLight = 4,
- baSkyLight = 8,
+ baTypes = 1,
+ baMetas = 2,
+ baLight = 4,
+ baSkyLight = 8,
+ // baEntities = 16, // Not supported yet
+ baBlockEntities = 32,
} ;
/** The per-block strategy to use when merging another block area into this object.
@@ -61,20 +66,26 @@ public:
cBlockArea(void);
~cBlockArea();
+ /** Returns true if the datatype combination is valid.
+ Invalid combinations include BlockEntities without BlockTypes. */
+ static bool IsValidDataTypeCombination(int a_DataTypes);
+
/** Clears the data stored to reclaim memory */
void Clear(void);
+ // tolua_end
+
/** Creates a new area of the specified size and contents.
Origin is set to all zeroes.
- BlockTypes are set to air, block metas to zero, blocklights to zero and skylights to full light.
- */
- void Create(int a_SizeX, int a_SizeY, int a_SizeZ, int a_DataTypes = baTypes | baMetas);
+ BlockTypes are set to air, block metas to zero, blocklights to zero and skylights to full light. */
+ void Create(int a_SizeX, int a_SizeY, int a_SizeZ, int a_DataTypes = baTypes | baMetas | baBlockEntities);
/** Creates a new area of the specified size and contents.
Origin is set to all zeroes.
- BlockTypes are set to air, block metas to zero, blocklights to zero and skylights to full light.
- */
- void Create(const Vector3i & a_Size, int a_DataTypes = baTypes | baMetas);
+ BlockTypes are set to air, block metas to zero, blocklights to zero and skylights to full light. */
+ void Create(const Vector3i & a_Size, int a_DataTypes = baTypes | baMetas | baBlockEntities);
+
+ // tolua_begin
/** Resets the origin. No other changes are made, contents are untouched. */
void SetOrigin(int a_OriginX, int a_OriginY, int a_OriginZ);
@@ -82,23 +93,39 @@ public:
/** Resets the origin. No other changes are made, contents are untouched. */
void SetOrigin(const Vector3i & a_Origin);
+ /** Returns true if the specified relative coords are within this area's coord range (0 - m_Size). */
+ bool IsValidRelCoords(int a_RelX, int a_RelY, int a_RelZ) const;
+
+ /** Returns true if the specified relative coords are within this area's coord range (0 - m_Size). */
+ bool IsValidRelCoords(const Vector3i & a_RelCoords) const;
+
+ /** Returns true if the specified coords are within this area's coord range (as indicated by m_Origin). */
+ bool IsValidCoords(int a_BlockX, int a_BlockY, int a_BlockZ) const;
+
+ /** Returns true if the specified coords are within this area's coord range (as indicated by m_Origin). */
+ bool IsValidCoords(const Vector3i & a_Coords) const;
+
+ // tolua_end
+
/** 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);
+ 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 | baBlockEntities);
/** 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);
+ bool Read(cForEachChunkProvider & a_ForEachChunkProvider, const cCuboid & a_Bounds, int a_DataTypes = baTypes | baMetas | baBlockEntities);
/** 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);
+ bool Read(cForEachChunkProvider & a_ForEachChunkProvider, const Vector3i & a_Point1, const Vector3i & a_Point2, int a_DataTypes = baTypes | baMetas | baBlockEntities);
// 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 */
- bool Write(cForEachChunkProvider & a_ForEachChunkProvider, int a_MinBlockX, int a_MinBlockY, int a_MinBlockZ, int a_DataTypes = baTypes | baMetas);
+ /** 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 | baBlockEntities);
- /** 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);
+ /** 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 | baBlockEntities);
+
+ // tolua_begin
/** Copies this object's contents into the specified BlockArea. */
void CopyTo(cBlockArea & a_Into) const;
@@ -117,6 +144,10 @@ public:
/** Merges another block area into this one, using the specified block combinating strategy
This function combines another BlockArea into the current object.
+ The a_RelX, a_RelY and a_RelZ parameters specify the coords of this BA where a_Src should be copied.
+ If both areas contain baBlockEntities, the BEs are merged (with preference of keeping this' ones) (MergeBlockEntities()).
+ If only this contains BEs, but a_Src doesn't, the BEs are checked after merge to remove the overwritten ones and create
+ the missing ones (UpdateBlockEntities()).
The strategy parameter specifies how individual blocks are combined together, using the table below.
| area block | result |
@@ -191,6 +222,8 @@ public:
/** 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);
+ // tolua_end
+
/** 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,
@@ -203,18 +236,20 @@ public:
NIBBLETYPE a_BlockLight = 0, NIBBLETYPE a_BlockSkyLight = 0x0f
);
- /** Draws a line from between two points with the specified data */
+ /** Draws a line between two points with the specified data. The line endpoints needn't be valid coords inside the area. */
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
);
- /** Draws a line from between two points with the specified data */
+ /** Draws a line between two points with the specified data. The line endpoints needn't be valid coords inside the area. */
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
);
+ // tolua_begin
+
/** Rotates the entire area counter-clockwise around the Y axis */
void RotateCCW(void);
@@ -245,6 +280,8 @@ public:
/** Mirrors the entire area around the YZ plane, doesn't use blockhandlers for block meta */
void MirrorYZNoMeta(void);
+ // tolua_end
+
// Setters:
void SetRelBlockType (int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType);
void SetBlockType (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType);
@@ -254,8 +291,14 @@ public:
void SetBlockLight (int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_BlockLight);
void SetRelBlockSkyLight(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_BlockSkyLight);
void SetBlockSkyLight (int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_BlockSkyLight);
- void SetWEOffset (int a_OffsetX, int a_OffsetY, int a_OffsetZ);
- void SetWEOffset (const Vector3i & a_Offset);
+
+ // tolua_begin
+
+ void SetWEOffset (int a_OffsetX, int a_OffsetY, int a_OffsetZ);
+ void SetWEOffset (const Vector3i & a_Offset);
+ const Vector3i & GetWEOffset (void) const {return m_WEOffset;}
+
+ // tolua_end
// Getters:
BLOCKTYPE GetRelBlockType (int a_RelX, int a_RelY, int a_RelZ) const;
@@ -266,21 +309,14 @@ public:
NIBBLETYPE GetBlockLight (int a_BlockX, int a_BlockY, int a_BlockZ) const;
NIBBLETYPE GetRelBlockSkyLight(int a_RelX, int a_RelY, int a_RelZ) const;
NIBBLETYPE GetBlockSkyLight (int a_BlockX, int a_BlockY, int a_BlockZ) const;
- const Vector3i & GetWEOffset (void) const {return m_WEOffset;}
void SetBlockTypeMeta (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
void SetRelBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
- // tolua_end
-
- // These need manual exporting, tolua generates the binding as requiring 2 extra input params
void GetBlockTypeMeta (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) const;
void GetRelBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) const;
- // GetSize() is already exported manually to return 3 numbers, can't auto-export
const Vector3i & GetSize(void) const { return m_Size; }
-
- // GetOrigin() is already exported manually to return 3 numbers, can't auto-export
const Vector3i & GetOrigin(void) const { return m_Origin; }
// tolua_begin
@@ -303,6 +339,7 @@ public:
bool HasBlockMetas (void) const { return (m_BlockMetas != nullptr); }
bool HasBlockLights (void) const { return (m_BlockLight != nullptr); }
bool HasBlockSkyLights(void) const { return (m_BlockSkyLight != nullptr); }
+ bool HasBlockEntities (void) const { return (m_BlockEntities != nullptr); }
/** Returns the count of blocks that are not air.
Returns 0 if blocktypes not available. Block metas are ignored (if present, air with any meta is still considered air). */
@@ -333,11 +370,32 @@ public:
size_t GetBlockCount(void) const { return static_cast<size_t>(m_Size.x * m_Size.y * m_Size.z); }
int MakeIndex(int a_RelX, int a_RelY, int a_RelZ) const;
+ /** Calls the callback for the block entity at the specified coords.
+ Returns false if there is no block entity at those coords, or the block area doesn't have baBlockEntities.
+ Returns the value that the callback has returned if there is a block entity. */
+ bool DoWithBlockEntityRelAt(int a_RelX, int a_RelY, int a_RelZ, cItemCallback<cBlockEntity> & a_Callback);
+
+ /** Calls the callback for the block entity at the specified coords.
+ Returns false if there is no block entity at those coords.
+ Returns the value that the callback has returned if there is a block entity. */
+ bool DoWithBlockEntityAt (int a_BlockX, int a_BlockY, int a_BlockZ, cItemCallback<cBlockEntity> & a_Callback);
+
+ /** Calls the callback for all the block entities.
+ If the callback returns true, aborts the enumeration and returns false.
+ If the callback returns true, continues with the next BE.
+ Returns true if all block entities have been enumerated (including the case when there is none or the area is without baBlockEntities). */
+ bool ForEachBlockEntity(cItemCallback<cBlockEntity> & a_Callback);
+
+ /** Direct read-only access to block entities. */
+ const cBlockEntities & GetBlockEntities(void) const { ASSERT(HasBlockEntities()); return *m_BlockEntities; }
+
+
protected:
+
friend class cChunkDesc;
friend class cSchematicFileSerializer;
- class cChunkReader :
+ class cChunkReader:
public cChunkDataCallback
{
public:
@@ -345,6 +403,7 @@ protected:
protected:
cBlockArea & m_Area;
+ cCuboid m_AreaBounds; ///< Bounds of the whole area being read, in world coords
Vector3i m_Origin;
int m_CurrentChunkX;
int m_CurrentChunkZ;
@@ -354,6 +413,7 @@ protected:
// cChunkDataCallback overrides:
virtual bool Coords(int a_ChunkX, int a_ChunkZ) override;
virtual void ChunkData(const cChunkData & a_BlockTypes) override;
+ virtual void BlockEntity(cBlockEntity * a_BlockEntity) override;
} ;
typedef NIBBLETYPE * NIBBLEARRAY;
@@ -371,6 +431,11 @@ 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
+ /** The block entities contained within the area.
+ Only valid if the area was created / read with the baBlockEntities flag.
+ The block entities are owned by this object. */
+ std::unique_ptr<cBlockEntities> m_BlockEntities;
+
/** 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);
@@ -390,7 +455,8 @@ 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.
+ If the coords are not valid, ignores the call (so that RelLine() can work simply). */
void RelSetData(
int a_RelX, int a_RelY, int a_RelZ,
int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta,
@@ -399,6 +465,21 @@ protected:
template <bool MetasValid>
void MergeByStrategy(const cBlockArea & a_Src, int a_RelX, int a_RelY, int a_RelZ, eMergeStrategy a_Strategy, const NIBBLETYPE * SrcMetas, NIBBLETYPE * DstMetas);
+
+ /** Clears the block entities from the specified container, freeing each blockentity. */
+ static void ClearBlockEntities(cBlockEntities & a_BlockEntities);
+
+ /** Updates m_BlockEntities to remove BEs that no longer match the blocktype at their coords, and clones from a_Src the BEs that are missing.
+ a_RelX, a_RelY and a_RelZ are relative coords that should be added to all BEs from a_Src before checking them.
+ If a block should have a BE but one cannot be found in either this or a_Src, a new one is created. */
+ void MergeBlockEntities(int a_RelX, int a_RelY, int a_RelZ, const cBlockArea & a_Src);
+
+ /** Updates m_BlockEntities to remove BEs that no longer match the blocktype at their coords, and add new BEs that are missing. */
+ void RescanBlockEntities(void);
+
+ /** Removes from m_BlockEntities those BEs that no longer match the blocktype at their coords. */
+ void RemoveNonMatchingBlockEntities(void);
+
// tolua_begin
} ;
// tolua_end
diff --git a/src/BlockEntities/BlockEntity.cpp b/src/BlockEntities/BlockEntity.cpp
index f0716dd08..f60eb5622 100644
--- a/src/BlockEntities/BlockEntity.cpp
+++ b/src/BlockEntities/BlockEntity.cpp
@@ -25,11 +25,57 @@
+void cBlockEntity::SetPos(int a_NewBlockX, int a_NewBlockY, int a_NewBlockZ)
+{
+ ASSERT(m_World == nullptr); // Cannot move block entities that represent world blocks (only use this for cBlockArea's BEs)
+ m_PosX = a_NewBlockX;
+ m_PosY = a_NewBlockY;
+ m_PosZ = a_NewBlockZ;
+}
+
+
+
+
+
+bool cBlockEntity::IsBlockEntityBlockType(BLOCKTYPE a_BlockType)
+{
+ switch (a_BlockType)
+ {
+ case E_BLOCK_BEACON:
+ case E_BLOCK_BREWING_STAND:
+ case E_BLOCK_CHEST:
+ case E_BLOCK_COMMAND_BLOCK:
+ case E_BLOCK_DISPENSER:
+ case E_BLOCK_DROPPER:
+ case E_BLOCK_ENDER_CHEST:
+ case E_BLOCK_FLOWER_POT:
+ case E_BLOCK_FURNACE:
+ case E_BLOCK_HEAD:
+ case E_BLOCK_HOPPER:
+ case E_BLOCK_JUKEBOX:
+ case E_BLOCK_LIT_FURNACE:
+ case E_BLOCK_MOB_SPAWNER:
+ case E_BLOCK_NOTE_BLOCK:
+ case E_BLOCK_SIGN_POST:
+ case E_BLOCK_TRAPPED_CHEST:
+ case E_BLOCK_WALLSIGN:
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
cBlockEntity * cBlockEntity::CreateByBlockType(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World)
{
switch (a_BlockType)
{
case E_BLOCK_BEACON: return new cBeaconEntity (a_BlockType, a_BlockMeta, a_BlockX, a_BlockY, a_BlockZ, a_World);
+ case E_BLOCK_BREWING_STAND: return new cBrewingstandEntity(a_BlockType, a_BlockMeta, a_BlockX, a_BlockY, a_BlockZ, a_World);
case E_BLOCK_CHEST: return new cChestEntity (a_BlockType, a_BlockMeta, a_BlockX, a_BlockY, a_BlockZ, a_World);
case E_BLOCK_COMMAND_BLOCK: return new cCommandBlockEntity(a_BlockType, a_BlockMeta, a_BlockX, a_BlockY, a_BlockZ, a_World);
case E_BLOCK_DISPENSER: return new cDispenserEntity (a_BlockType, a_BlockMeta, a_BlockX, a_BlockY, a_BlockZ, a_World);
@@ -37,16 +83,15 @@ cBlockEntity * cBlockEntity::CreateByBlockType(BLOCKTYPE a_BlockType, NIBBLETYPE
case E_BLOCK_ENDER_CHEST: return new cEnderChestEntity (a_BlockType, a_BlockMeta, a_BlockX, a_BlockY, a_BlockZ, a_World);
case E_BLOCK_FLOWER_POT: return new cFlowerPotEntity (a_BlockType, a_BlockMeta, a_BlockX, a_BlockY, a_BlockZ, a_World);
case E_BLOCK_FURNACE: return new cFurnaceEntity (a_BlockType, a_BlockMeta, a_BlockX, a_BlockY, a_BlockZ, a_World);
- case E_BLOCK_BREWING_STAND: return new cBrewingstandEntity(a_BlockType, a_BlockMeta, a_BlockX, a_BlockY, a_BlockZ, a_World);
case E_BLOCK_HEAD: return new cMobHeadEntity (a_BlockType, a_BlockMeta, a_BlockX, a_BlockY, a_BlockZ, a_World);
case E_BLOCK_HOPPER: return new cHopperEntity (a_BlockType, a_BlockMeta, a_BlockX, a_BlockY, a_BlockZ, a_World);
- case E_BLOCK_MOB_SPAWNER: return new cMobSpawnerEntity (a_BlockType, a_BlockMeta, a_BlockX, a_BlockY, a_BlockZ, a_World);
case E_BLOCK_JUKEBOX: return new cJukeboxEntity (a_BlockType, a_BlockMeta, a_BlockX, a_BlockY, a_BlockZ, a_World);
case E_BLOCK_LIT_FURNACE: return new cFurnaceEntity (a_BlockType, a_BlockMeta, a_BlockX, a_BlockY, a_BlockZ, a_World);
+ case E_BLOCK_MOB_SPAWNER: return new cMobSpawnerEntity (a_BlockType, a_BlockMeta, a_BlockX, a_BlockY, a_BlockZ, a_World);
+ case E_BLOCK_NOTE_BLOCK: return new cNoteEntity (a_BlockType, a_BlockMeta, a_BlockX, a_BlockY, a_BlockZ, a_World);
case E_BLOCK_SIGN_POST: return new cSignEntity (a_BlockType, a_BlockMeta, a_BlockX, a_BlockY, a_BlockZ, a_World);
case E_BLOCK_TRAPPED_CHEST: return new cChestEntity (a_BlockType, a_BlockMeta, a_BlockX, a_BlockY, a_BlockZ, a_World);
case E_BLOCK_WALLSIGN: return new cSignEntity (a_BlockType, a_BlockMeta, a_BlockX, a_BlockY, a_BlockZ, a_World);
- case E_BLOCK_NOTE_BLOCK: return new cNoteEntity (a_BlockType, a_BlockMeta, a_BlockX, a_BlockY, a_BlockZ, a_World);
}
LOGD("%s: Requesting creation of an unknown block entity - block type %d (%s)",
__FUNCTION__, a_BlockType, ItemTypeToString(a_BlockType).c_str()
diff --git a/src/BlockEntities/BlockEntity.h b/src/BlockEntities/BlockEntity.h
index 6c69e8260..5b7184775 100644
--- a/src/BlockEntities/BlockEntity.h
+++ b/src/BlockEntities/BlockEntity.h
@@ -64,6 +64,14 @@ public:
m_World = a_World;
}
+ /** Updates the internally stored position.
+ Note that this should not ever be used for world-contained block entities, it is meant only for when BEs in a cBlockArea are manipulated.
+ Asserts when the block entity is assigned to a world. */
+ void SetPos(int a_NewBlockX, int a_NewBlockY, int a_NewBlockZ);
+
+ /** Returns true if the specified blocktype is supposed to have an associated block entity. */
+ static bool IsBlockEntityBlockType(BLOCKTYPE a_BlockType);
+
/** Creates a new block entity for the specified block type
If a_World is valid, then the entity is created bound to that world
Returns nullptr for unknown block types. */
diff --git a/src/Chunk.cpp b/src/Chunk.cpp
index 849b0e2f5..99f643437 100644
--- a/src/Chunk.cpp
+++ b/src/Chunk.cpp
@@ -408,7 +408,7 @@ void cChunk::WriteBlockArea(cBlockArea & a_Area, int a_MinBlockX, int a_MinBlock
{
if ((a_DataTypes & (cBlockArea::baTypes | cBlockArea::baMetas)) != (cBlockArea::baTypes | cBlockArea::baMetas))
{
- LOGWARNING("cChunk::WriteBlockArea(): unsupported datatype request, can write only types + metas (0x%x), requested 0x%x. Ignoring.",
+ LOGWARNING("cChunk::WriteBlockArea(): unsupported datatype request, can write only types + metas together (0x%x), requested 0x%x. Ignoring.",
(cBlockArea::baTypes | cBlockArea::baMetas), a_DataTypes & (cBlockArea::baTypes | cBlockArea::baMetas)
);
return;
@@ -420,16 +420,16 @@ void cChunk::WriteBlockArea(cBlockArea & a_Area, int a_MinBlockX, int a_MinBlock
int BlockEndX = std::min(a_MinBlockX + a_Area.GetSizeX(), (m_PosX + 1) * cChunkDef::Width);
int BlockStartZ = std::max(a_MinBlockZ, m_PosZ * cChunkDef::Width);
int BlockEndZ = std::min(a_MinBlockZ + a_Area.GetSizeZ(), (m_PosZ + 1) * cChunkDef::Width);
- int SizeX = BlockEndX - BlockStartX;
+ int SizeX = BlockEndX - BlockStartX; // Size of the union
int SizeZ = BlockEndZ - BlockStartZ;
- int OffX = BlockStartX - m_PosX * cChunkDef::Width;
+ int SizeY = std::min(a_Area.GetSizeY(), cChunkDef::Height - a_MinBlockY);
+ int OffX = BlockStartX - m_PosX * cChunkDef::Width; // Offset within the chunk where the union starts
int OffZ = BlockStartZ - m_PosZ * cChunkDef::Width;
- int BaseX = BlockStartX - a_MinBlockX;
+ int BaseX = BlockStartX - a_MinBlockX; // Offset within the area where the union starts
int BaseZ = BlockStartZ - a_MinBlockZ;
- int SizeY = std::min(a_Area.GetSizeY(), cChunkDef::Height - a_MinBlockY);
- // TODO: Improve this by not calling FastSetBlock() and doing the processing here
- // so that the heightmap is touched only once for each column.
+ // Copy blocktype and blockmeta:
+ // TODO: Right now each changed block is transmitted to all clients as a separate packet. Optimize this for larger areas.
BLOCKTYPE * AreaBlockTypes = a_Area.GetBlockTypes();
NIBBLETYPE * AreaBlockMetas = a_Area.GetBlockMetas();
for (int y = 0; y < SizeY; y++)
@@ -451,6 +451,50 @@ void cChunk::WriteBlockArea(cBlockArea & a_Area, int a_MinBlockX, int a_MinBlock
} // for x
} // for z
} // for y
+
+ // Erase all affected block entities:
+ cCuboid affectedArea(OffX, a_MinBlockY, OffZ, OffX + SizeX - 1, a_MinBlockY + SizeY - 1, OffZ + SizeZ - 1);
+ for (auto itr = m_BlockEntities.begin(); itr != m_BlockEntities.end();)
+ {
+ if (affectedArea.IsInside(itr->second->GetPos()))
+ {
+ itr = m_BlockEntities.erase(itr);
+ }
+ else
+ {
+ ++itr;
+ }
+ }
+
+ // Clone block entities from a_Area into this chunk:
+ if ((a_DataTypes & cBlockArea::baBlockEntities) != 0)
+ {
+ for (const auto & keyPair: a_Area.GetBlockEntities())
+ {
+ auto & be = keyPair.second;
+ auto posX = be->GetPosX() + a_MinBlockX;
+ auto posY = be->GetPosY() + a_MinBlockY;
+ auto posZ = be->GetPosZ() + a_MinBlockZ;
+ if (
+ (posX < m_PosX * cChunkDef::Width) || (posX >= m_PosX * cChunkDef::Width + cChunkDef::Width) ||
+ (posZ < m_PosZ * cChunkDef::Width) || (posZ >= m_PosZ * cChunkDef::Width + cChunkDef::Width)
+ )
+ {
+ continue;
+ }
+ // This block entity is inside the chunk, clone it (and remove any that is there currently):
+ auto idx = MakeIndex(posX - m_PosX * cChunkDef::Width, posY, posZ - m_PosZ * cChunkDef::Width);
+ auto itr = m_BlockEntities.find(idx);
+ if (itr != m_BlockEntities.end())
+ {
+ m_BlockEntities.erase(itr);
+ }
+ auto clone = be->Clone(posX, posY, posZ);
+ clone->SetWorld(m_World);
+ AddBlockEntityClean(clone);
+ BroadcastBlockEntity(posX, posY, posZ);
+ }
+ }
}