1
0
Fork 0

cBlockArea supports block entities. (#3795)

This commit is contained in:
Mattes D 2017-06-24 11:58:06 +02:00 committed by GitHub
parent cc3a67b9df
commit fe42538349
17 changed files with 4578 additions and 2668 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -532,7 +532,7 @@ function OnAllChunksAvailable()</pre> All return values from the callbacks are i
Type = "boolean",
},
},
Notes = "If there is a block entity at the specified coords, calls the CallbackFunction with the {{cBlockEntity}} parameter representing the block entity. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cBlockEntity|BlockEntity}})</pre> The function returns false if there is no block entity, or if there is, it returns the bool value that the callback has returned. Use {{tolua}}.cast() to cast the Callback's BlockEntity parameter to the correct {{cBlockEntity}} descendant.",
Notes = "If there is a block entity at the specified coords, calls the CallbackFunction with the {{cBlockEntity}} parameter representing the block entity. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cBlockEntity|BlockEntity}})</pre> The function returns false if there is no block entity, or if there is, it returns the bool value that the callback has returned.",
},
DoWithBrewingstandAt =
{
@ -978,7 +978,7 @@ function OnAllChunksAvailable()</pre> All return values from the callbacks are i
Type = "boolean",
},
},
Notes = "Calls the specified callback for each block entity in the chunk. Returns true if all block entities in the chunk have been processed (including when there are zero block entities), or false if the callback has aborted the enumeration by returning true. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cBlockEntity|BlockEntity}})</pre> The callback should return false or no value to continue with the next block entity, or true to abort the enumeration. Use {{tolua}}.cast() to cast the Callback's BlockEntity parameter to the correct {{cBlockEntity}} descendant.",
Notes = "Calls the specified callback for each block entity in the chunk. Returns true if all block entities in the chunk have been processed (including when there are zero block entities), or false if the callback has aborted the enumeration by returning true. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cBlockEntity|BlockEntity}})</pre> The callback should return false or no value to continue with the next block entity, or true to abort the enumeration.",
},
ForEachBrewingstandInChunk =
{
@ -3364,10 +3364,9 @@ World:ForEachEntity(
return;
end
-- Get the cMonster out of cEntity, now that we know the entity represents one.
local Monster = tolua.cast(a_Entity, "cMonster");
if (Monster:GetMobType() == mtSpider) then
Monster:TeleportToCoords(Monster:GetPosX(), Monster:GetPosY() + 100, Monster:GetPosZ());
-- Now that we know the entity represents a mob, we can use cMonster functions:
if (a_Entity:GetMobType() == mtSpider) then
a_Entity:TeleportToCoords(a_Entity:GetPosX(), a_Entity:GetPosY() + 100, a_Entity:GetPosZ());
end
end
);

View File

@ -16,6 +16,7 @@ SET (SRCS
LuaUDPEndpoint.cpp
LuaWindow.cpp
ManualBindings.cpp
ManualBindings_BlockArea.cpp
ManualBindings_Network.cpp
ManualBindings_RankManager.cpp
ManualBindings_World.cpp

View File

@ -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());

View File

@ -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);

View File

@ -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);
}

View File

@ -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);

View File

@ -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);
}

File diff suppressed because it is too large Load Diff

View File

@ -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);
/** 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);
// 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(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(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 | 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

View File

@ -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()

View File

@ -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. */

View File

@ -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 OffZ = BlockStartZ - m_PosZ * cChunkDef::Width;
int BaseX = BlockStartX - a_MinBlockX;
int BaseZ = BlockStartZ - a_MinBlockZ;
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; // Offset within the area where the union starts
int BaseZ = BlockStartZ - a_MinBlockZ;
// 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);
}
}
}

View File

@ -288,3 +288,29 @@ void cDeadlockDetect::UntrackCriticalSection(cCriticalSection & a_CS)
void cBlockEntity::SetPos(int a_BlockX, int a_BlockY, int a_BlockZ)
{
}
bool cBlockEntity::IsBlockEntityBlockType(BLOCKTYPE a_BlockType)
{
return false;
}
cBlockEntity * cBlockEntity::Clone(int a_BlockX, int a_BlockY, int a_BlockZ)
{
return nullptr;
}

View File

@ -288,3 +288,29 @@ void cDeadlockDetect::UntrackCriticalSection(cCriticalSection & a_CS)
void cBlockEntity::SetPos(int a_BlockX, int a_BlockY, int a_BlockZ)
{
}
bool cBlockEntity::IsBlockEntityBlockType(BLOCKTYPE a_BlockType)
{
return false;
}
cBlockEntity * cBlockEntity::Clone(int a_BlockX, int a_BlockY, int a_BlockZ)
{
return nullptr;
}

View File

@ -7,6 +7,7 @@
#include "Globals.h"
#include "BlockInfo.h"
#include "Blocks/BlockHandler.h"
#include "BlockEntities/BlockEntity.h"
@ -191,3 +192,37 @@ bool cBlockHandler::IsInsideBlock(const Vector3d & a_Position, const BLOCKTYPE a
bool cBlockEntity::IsBlockEntityBlockType(BLOCKTYPE a_BlockType)
{
return false;
}
void cBlockEntity::SetPos(int a_BlockX, int a_BlockY, int a_BlockZ)
{
}
cBlockEntity * cBlockEntity::Clone(int a_BlockX, int a_BlockY, int a_BlockZ)
{
return nullptr;
}
cBlockEntity * cBlockEntity::CreateByBlockType(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World)
{
return nullptr;
}