diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Bindings/BindingsProcessor.lua | 4 | ||||
-rw-r--r-- | src/Bindings/LuaState.cpp | 83 | ||||
-rw-r--r-- | src/Bindings/LuaState.h | 156 | ||||
-rw-r--r-- | src/Blocks/BlockHandler.cpp | 1 | ||||
-rw-r--r-- | src/Defines.h | 18 | ||||
-rw-r--r-- | src/Generating/CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/Generating/Prefab.cpp | 49 | ||||
-rw-r--r-- | src/Generating/Prefab.h | 25 | ||||
-rw-r--r-- | src/Generating/PrefabPiecePool.cpp | 414 | ||||
-rw-r--r-- | src/Generating/PrefabPiecePool.h | 79 | ||||
-rw-r--r-- | src/Globals.h | 16 | ||||
-rw-r--r-- | src/WorldStorage/CMakeLists.txt | 6 | ||||
-rw-r--r-- | src/WorldStorage/SchematicFileSerializer.cpp | 24 |
13 files changed, 806 insertions, 71 deletions
diff --git a/src/Bindings/BindingsProcessor.lua b/src/Bindings/BindingsProcessor.lua index fba992082..a398f5026 100644 --- a/src/Bindings/BindingsProcessor.lua +++ b/src/Bindings/BindingsProcessor.lua @@ -98,7 +98,9 @@ local function OutputLuaStateHelpers(a_Package) f:write("// This file expects to be included form inside the cLuaState class definition\n") f:write("\n\n\n\n\n") for _, item in ipairs(types) do - f:write("void Push(" .. item.name .. " * a_Value);\n") + if not(g_HasCustomPushImplementation[item.name]) then + f:write("void Push(" .. item.name .. " * a_Value);\n") + end end for _, item in ipairs(types) do f:write("bool GetStackValue(int a_StackPos, Ptr" .. item.lname .. " & a_ReturnedVal);\n") diff --git a/src/Bindings/LuaState.cpp b/src/Bindings/LuaState.cpp index 232432a99..9814d1c85 100644 --- a/src/Bindings/LuaState.cpp +++ b/src/Bindings/LuaState.cpp @@ -211,23 +211,31 @@ void cLuaState::AddPackagePath(const AString & a_PathVariable, const AString & a -bool cLuaState::LoadFile(const AString & a_FileName) +bool cLuaState::LoadFile(const AString & a_FileName, bool a_LogWarnings) { ASSERT(IsValid()); // Load the file: int s = luaL_loadfile(m_LuaState, a_FileName.c_str()); - if (ReportErrors(s)) + if (s != 0) { - LOGWARNING("Can't load %s because of an error in file %s", m_SubsystemName.c_str(), a_FileName.c_str()); + if (a_LogWarnings) + { + LOGWARNING("Can't load %s because of a load error in file %s: %d (%s)", m_SubsystemName.c_str(), a_FileName.c_str(), s, lua_tostring(m_LuaState, -1)); + } + lua_pop(m_LuaState, 1); return false; } // Execute the globals: s = lua_pcall(m_LuaState, 0, LUA_MULTRET, 0); - if (ReportErrors(s)) + if (s != 0) { - LOGWARNING("Error in %s in file %s", m_SubsystemName.c_str(), a_FileName.c_str()); + if (a_LogWarnings) + { + LOGWARNING("Can't load %s because of an initialization error in file %s: %d (%s)", m_SubsystemName.c_str(), a_FileName.c_str(), s, lua_tostring(m_LuaState, -1)); + } + lua_pop(m_LuaState, 1); return false; } @@ -446,6 +454,18 @@ void cLuaState::Push(const cPlayer * a_Player) +void cLuaState::Push(const cLuaState::cRef & a_Ref) +{ + ASSERT(IsValid()); + + lua_rawgeti(m_LuaState, LUA_REGISTRYINDEX, static_cast<int>(a_Ref)); + m_NumCurrentFunctionArgs += 1; +} + + + + + void cLuaState::Push(const HTTPRequest * a_Request) { ASSERT(IsValid()); @@ -765,14 +785,17 @@ bool cLuaState::GetStackValue(int a_StackPos, double & a_ReturnedVal) -bool cLuaState::GetStackValue(int a_StackPos, float & a_ReturnedVal) +bool cLuaState::GetStackValue(int a_StackPos, eBlockFace & a_ReturnedVal) { - if (lua_isnumber(m_LuaState, a_StackPos)) + if (!lua_isnumber(m_LuaState, a_StackPos)) { - a_ReturnedVal = static_cast<float>(tolua_tonumber(m_LuaState, a_StackPos, a_ReturnedVal)); - return true; + return false; } - return false; + a_ReturnedVal = static_cast<eBlockFace>(Clamp( + static_cast<int>(tolua_tonumber(m_LuaState, a_StackPos, a_ReturnedVal)), + static_cast<int>(BLOCK_FACE_MIN), static_cast<int>(BLOCK_FACE_MAX)) + ); + return true; } @@ -796,6 +819,46 @@ bool cLuaState::GetStackValue(int a_StackPos, eWeather & a_ReturnedVal) +bool cLuaState::GetStackValue(int a_StackPos, float & a_ReturnedVal) +{ + if (lua_isnumber(m_LuaState, a_StackPos)) + { + a_ReturnedVal = static_cast<float>(tolua_tonumber(m_LuaState, a_StackPos, a_ReturnedVal)); + return true; + } + return false; +} + + + + + +cLuaState::cStackValue cLuaState::WalkToValue(const AString & a_Name) +{ + auto path = StringSplit(a_Name, "."); + lua_pushvalue(m_LuaState, -1); // Copy the stack value into the "working area" + for (const auto & elem: path) + { + // If the value is not a table, bail out (error): + if (!lua_istable(m_LuaState, -1)) + { + lua_pop(m_LuaState, 1); + return cStackValue(); + } + + // Get the next part of the path: + lua_getfield(m_LuaState, -1, elem.c_str()); + + // Remove the previous value from the stack (keep only the new one): + lua_remove(m_LuaState, -2); + } // for elem - path[] + return std::move(cStackValue(*this)); +} + + + + + bool cLuaState::CallFunction(int a_NumResults) { ASSERT (m_NumCurrentFunctionArgs >= 0); // A function must be pushed to stack first diff --git a/src/Bindings/LuaState.h b/src/Bindings/LuaState.h index 8a3411d30..cc8214646 100644 --- a/src/Bindings/LuaState.h +++ b/src/Bindings/LuaState.h @@ -78,11 +78,14 @@ public: bool IsValid(void) const {return (m_Ref != LUA_REFNIL); } /** Allows to use this class wherever an int (i. e. ref) is to be used */ - operator int(void) const { return m_Ref; } - + explicit operator int(void) const { return m_Ref; } + protected: cLuaState * m_LuaState; int m_Ref; + + // Remove the copy-constructor: + cRef(const cRef &) = delete; } ; @@ -98,6 +101,12 @@ public: { } + cTableRef(const cRef & a_TableRef, const char * a_FnName) : + m_TableRef(static_cast<int>(a_TableRef)), + m_FnName(a_FnName) + { + } + int GetTableRef(void) const { return m_TableRef; } const char * GetFnName(void) const { return m_FnName; } } ; @@ -111,6 +120,61 @@ public: static const cRet Return; // Use this constant to delimit function args from return values for cLuaState::Call() + /** A RAII class for values pushed onto the Lua stack. + Will pop the value off the stack in the destructor. */ + class cStackValue + { + public: + cStackValue(void): + m_LuaState(nullptr) + { + } + + cStackValue(cLuaState & a_LuaState): + m_LuaState(a_LuaState) + { + m_StackLen = lua_gettop(a_LuaState); + } + + cStackValue(cStackValue && a_Src): + m_LuaState(nullptr), + m_StackLen(-1) + { + std::swap(m_LuaState, a_Src.m_LuaState); + std::swap(m_StackLen, a_Src.m_StackLen); + } + + ~cStackValue() + { + if (m_LuaState != nullptr) + { + auto top = lua_gettop(m_LuaState); + ASSERT(m_StackLen == top); + lua_pop(m_LuaState, 1); + } + } + + void Set(cLuaState & a_LuaState) + { + m_LuaState = a_LuaState; + m_StackLen = lua_gettop(a_LuaState); + } + + bool IsValid(void) const + { + return (m_LuaState != nullptr); + } + + protected: + lua_State * m_LuaState; + + int m_StackLen; + + // Remove the copy-constructor: + cStackValue(const cStackValue &) = delete; + }; + + /** Creates a new instance. The LuaState is not initialized. a_SubsystemName is used for reporting problems in the console, it is "plugin %s" for plugins, or "LuaScript" for the cLuaScript template @@ -151,10 +215,9 @@ public: void AddPackagePath(const AString & a_PathVariable, const AString & a_Path); /** Loads the specified file - Returns false and logs a warning to the console if not successful (but the LuaState is kept open). - m_SubsystemName is displayed in the warning log message. - */ - bool LoadFile(const AString & a_FileName); + Returns false and optionally logs a warning to the console if not successful (but the LuaState is kept open). + m_SubsystemName is displayed in the warning log message. */ + bool LoadFile(const AString & a_FileName, bool a_LogWarnings = true); /** Returns true if a_FunctionName is a valid Lua function that can be called */ bool HasFunction(const char * a_FunctionName); @@ -169,6 +232,7 @@ public: void Push(const char * a_Value); void Push(const cItems & a_Items); void Push(const cPlayer * a_Player); + void Push(const cRef & a_Ref); void Push(const HTTPRequest * a_Request); void Push(const HTTPTemplateRequest * a_Request); void Push(const Vector3d & a_Vector); @@ -178,22 +242,24 @@ public: // Push a simple value onto the stack (keep alpha-sorted): void Push(bool a_Value); + void Push(cEntity * a_Entity); + void Push(cLuaServerHandle * a_ServerHandle); + void Push(cLuaTCPLink * a_TCPLink); + void Push(cLuaUDPEndpoint * a_UDPEndpoint); void Push(double a_Value); void Push(int a_Value); void Push(void * a_Ptr); void Push(std::chrono::milliseconds a_time); - void Push(cLuaServerHandle * a_ServerHandle); - void Push(cLuaTCPLink * a_TCPLink); - void Push(cLuaUDPEndpoint * a_UDPEndpoint); // GetStackValue() retrieves the value at a_StackPos, if it is a valid type. If not, a_Value is unchanged. // Returns whether value was changed - // Enum values are clamped to their allowed range. + // Enum values are checked for their allowed values and fail if the value is not assigned. bool GetStackValue(int a_StackPos, AString & a_Value); bool GetStackValue(int a_StackPos, bool & a_Value); bool GetStackValue(int a_StackPos, cPluginManager::CommandResult & a_Result); bool GetStackValue(int a_StackPos, cRef & a_Ref); bool GetStackValue(int a_StackPos, double & a_Value); + bool GetStackValue(int a_StackPos, eBlockFace & a_Value); bool GetStackValue(int a_StackPos, eWeather & a_Value); bool GetStackValue(int a_StackPos, float & a_ReturnedVal); @@ -202,21 +268,53 @@ public: bool GetStackValue(int a_StackPos, T & a_ReturnedVal, typename std::enable_if<std::is_integral<T>::value>::type * unused = nullptr) { UNUSED(unused); - if (lua_isnumber(m_LuaState, a_StackPos)) + if (!lua_isnumber(m_LuaState, a_StackPos)) // Also accepts strings representing a number: http://pgl.yoyo.org/luai/i/lua_isnumber { - lua_Number Val = tolua_tonumber(m_LuaState, a_StackPos, a_ReturnedVal); - if (Val > std::numeric_limits<T>::max()) - { - return false; - } - if (Val < std::numeric_limits<T>::min()) - { - return false; - } - a_ReturnedVal = static_cast<T>(Val); - return true; + return false; + } + lua_Number Val = lua_tonumber(m_LuaState, a_StackPos); + if (Val > std::numeric_limits<T>::max()) + { + return false; + } + if (Val < std::numeric_limits<T>::min()) + { + return false; + } + a_ReturnedVal = static_cast<T>(Val); + return true; + } + + /** Pushes the named value in the table at the top of the stack. + a_Name may be a path containing multiple table levels, such as "_G.cChatColor.Blue". + If the value is found, it is pushed on top of the stack and the returned cStackValue is valid. + If the value is not found, the stack is unchanged and the returned cStackValue is invalid. */ + cStackValue WalkToValue(const AString & a_Name); + + /** Retrieves the named value in the table at the top of the Lua stack. + a_Name may be a path containing multiple table levels, such as "_G.cChatColor.Blue". + Returns true if the value was successfully retrieved, false on error. */ + template <typename T> bool GetNamedValue(const AString & a_Name, T & a_Value) + { + auto stk = WalkToValue(a_Name); + if (!stk.IsValid()) + { + // Name not found + return false; } - return false; + return GetStackValue(-1, a_Value); + } + + /** Retrieves the named global value. a_Name may be a path containing multiple table levels, such as "_G.cChatColor.Blue". + Returns true if the value was successfully retrieved, false on error. */ + template <typename T> bool GetNamedGlobal(const AString & a_Name, T & a_Value) + { + // Push the globals table onto the stack and make it RAII-removed: + lua_getglobal(m_LuaState, "_G"); + cStackValue stk(*this); + + // Get the named global: + return GetNamedValue(a_Name, a_Value); } // Include the auto-generated Push and GetStackValue() functions: @@ -229,12 +327,12 @@ public: template <typename FnT, typename... Args> bool Call(const FnT & a_Function, Args &&... args) { - if (!PushFunction(a_Function)) + if (!PushFunction(std::forward<const FnT &>(a_Function))) { // Pushing the function failed return false; } - return PushCallPop(args...); + return PushCallPop(std::forward<Args>(args)...); } /** Retrieves a list of values from the Lua stack, starting at the specified index. */ @@ -343,10 +441,10 @@ protected: /** Variadic template recursor: More params to push. Push them and recurse. */ template <typename T, typename... Args> - inline bool PushCallPop(T a_Param, Args &&... args) + inline bool PushCallPop(T && a_Param, Args &&... args) { - Push(a_Param); - return PushCallPop(args...); + Push(std::forward<T>(a_Param)); + return PushCallPop(std::forward<Args>(args)...); } /** Variadic template terminator: If there's nothing more to push, but return values to collect, call the function and collect the returns. */ @@ -363,7 +461,7 @@ protected: } // Collect the return values: - GetStackValues(-NumReturns, args...); + GetStackValues(-NumReturns, std::forward<Args>(args)...); lua_pop(m_LuaState, NumReturns); // All successful: diff --git a/src/Blocks/BlockHandler.cpp b/src/Blocks/BlockHandler.cpp index 452cc94a5..105765b5f 100644 --- a/src/Blocks/BlockHandler.cpp +++ b/src/Blocks/BlockHandler.cpp @@ -427,6 +427,7 @@ void cBlockHandler::NeighborChanged(cChunkInterface & a_ChunkInterface, int a_Bl + void cBlockHandler::ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) { // Setting the meta to a_BlockMeta keeps most textures. The few other blocks have to override this. diff --git a/src/Defines.h b/src/Defines.h index b167f69e3..f3b742e09 100644 --- a/src/Defines.h +++ b/src/Defines.h @@ -29,16 +29,16 @@ enum -/// Block face constants, used in PlayerDigging and PlayerBlockPlacement packets and bbox collision calc +/** Block face constants, used in PlayerDigging and PlayerBlockPlacement packets and bbox collision calc */ enum eBlockFace { BLOCK_FACE_NONE = -1, // Interacting with no block face - swinging the item in the air - BLOCK_FACE_XM = 4, // Interacting with the X- face of the block - BLOCK_FACE_XP = 5, // Interacting with the X+ face of the block - BLOCK_FACE_YM = 0, // Interacting with the Y- face of the block - BLOCK_FACE_YP = 1, // Interacting with the Y+ face of the block - BLOCK_FACE_ZM = 2, // Interacting with the Z- face of the block - BLOCK_FACE_ZP = 3, // Interacting with the Z+ face of the block + BLOCK_FACE_XM = 4, // Interacting with the X- face of the block + BLOCK_FACE_XP = 5, // Interacting with the X+ face of the block + BLOCK_FACE_YM = 0, // Interacting with the Y- face of the block + BLOCK_FACE_YP = 1, // Interacting with the Y+ face of the block + BLOCK_FACE_ZM = 2, // Interacting with the Z- face of the block + BLOCK_FACE_ZP = 3, // Interacting with the Z+ face of the block // Synonyms using the (deprecated) world directions: BLOCK_FACE_BOTTOM = BLOCK_FACE_YM, // Interacting with the bottom face of the block @@ -47,6 +47,10 @@ enum eBlockFace BLOCK_FACE_SOUTH = BLOCK_FACE_ZP, // Interacting with the southern face of the block BLOCK_FACE_WEST = BLOCK_FACE_XM, // Interacting with the western face of the block BLOCK_FACE_EAST = BLOCK_FACE_XP, // Interacting with the eastern face of the block + + // Bounds, used for range-checking: + BLOCK_FACE_MIN = -1, + BLOCK_FACE_MAX = 5, } ; diff --git a/src/Generating/CMakeLists.txt b/src/Generating/CMakeLists.txt index ebba4cce8..f3fe5c55c 100644 --- a/src/Generating/CMakeLists.txt +++ b/src/Generating/CMakeLists.txt @@ -101,5 +101,5 @@ endif() if(NOT MSVC) add_library(Generating ${SRCS} ${HDRS}) - target_link_libraries(Generating OSSupport Blocks) + target_link_libraries(Generating OSSupport Blocks Bindings) endif() diff --git a/src/Generating/Prefab.cpp b/src/Generating/Prefab.cpp index 1de0346bd..e5e6a1e06 100644 --- a/src/Generating/Prefab.cpp +++ b/src/Generating/Prefab.cpp @@ -166,6 +166,45 @@ cPrefab::cPrefab(const cBlockArea & a_Image, int a_AllowedRotations) : +cPrefab::cPrefab(const cBlockArea & a_Image) : + m_Size(a_Image.GetSize()), + m_AllowedRotations(0), + m_MergeStrategy(cBlockArea::msOverwrite), + m_ShouldExtendFloor(false), + m_DefaultWeight(1), + m_AddWeightIfSame(0), + m_MoveToGround(false) +{ + m_HitBox.p1.Set(0, 0, 0); + m_HitBox.p2.Set(m_Size.x - 1, m_Size.y - 1, m_Size.z - 1); + m_BlockArea[0].CopyFrom(a_Image); +} + + + + + +cPrefab::cPrefab(const AString & a_BlockDefinitions, const AString & a_BlockData, int a_SizeX, int a_SizeY, int a_SizeZ) : + m_Size(a_SizeX, a_SizeY, a_SizeZ), + m_AllowedRotations(0), + m_MergeStrategy(cBlockArea::msOverwrite), + m_ShouldExtendFloor(false), + m_DefaultWeight(1), + m_AddWeightIfSame(0), + m_MoveToGround(false) +{ + m_HitBox.p1.Set(0, 0, 0); + m_HitBox.p2.Set(m_Size.x - 1, m_Size.y - 1, m_Size.z - 1); + m_BlockArea[0].Create(m_Size); + CharMap cm; + ParseCharMap(cm, a_BlockDefinitions.c_str()); + ParseBlockImage(cm, a_BlockData.c_str()); +} + + + + + void cPrefab::AddRotatedBlockAreas(void) { // 1 CCW rotation: @@ -326,6 +365,16 @@ void cPrefab::AddConnector(int a_RelX, int a_RelY, int a_RelZ, eBlockFace a_Dire +void cPrefab::SetAllowedRotations(int a_AllowedRotations) +{ + m_AllowedRotations = a_AllowedRotations; + AddRotatedBlockAreas(); +} + + + + + void cPrefab::ParseCharMap(CharMap & a_CharMapOut, const char * a_CharMapDef) { ASSERT(a_CharMapDef != nullptr); diff --git a/src/Generating/Prefab.h b/src/Generating/Prefab.h index eb905e78e..45af4b282 100644 --- a/src/Generating/Prefab.h +++ b/src/Generating/Prefab.h @@ -95,6 +95,13 @@ public: /** Creates a prefab based on the given BlockArea and allowed rotations. */ cPrefab(const cBlockArea & a_Image, int a_AllowedRotations); + /** Creates a prefab based on the given BlockArea. Allowed rotations can be added later on using SetAllowedRotations(). */ + cPrefab(const cBlockArea & a_Image); + + /** Creates a prefab based on the specified block data, using the char-to-block map in a_BlockDefinitions. + Allowed rotations can be added later on using SetAllowedRotations(). */ + cPrefab(const AString & a_BlockDefinitions, const AString & a_BlockData, int a_SizeX, int a_SizeY, int a_SizeZ); + /** Draws the prefab into the specified chunk, according to the placement stored in the PlacedPiece. */ void Draw(cChunkDesc & a_Dest, const cPlacedPiece * a_Placement) const; @@ -124,6 +131,21 @@ public: at the coords governed by the connectors. */ bool ShouldMoveToGround(void) const { return m_MoveToGround; } + /** Sets the m_AllowedRotations bitmask and fills the m_BlockArea[] with rotated versions of m_BlockArea[0]. */ + void SetAllowedRotations(int a_AllowedRotations); + + /** Parses the per-depth weight into m_DepthWeight member. */ + void ParseDepthWeight(const char * a_DepthWeightDef); + + /** Sets the merge strategy to be used when drawing the piece. */ + void SetMergeStrategy(cBlockArea::eMergeStrategy a_MergeStrategy) { m_MergeStrategy = a_MergeStrategy; } + + /** Sets the flag whether the prefab should be moved to ground level before being drawn. */ + void SetMoveToGround(bool a_MoveToGround) { m_MoveToGround = a_MoveToGround; } + + /** Sets the flag whether the lowest layer of the prefab should be repeated downwards until it hits a solid block. */ + void SetExtendFloor(bool a_ShouldExtendFloor) { m_ShouldExtendFloor = a_ShouldExtendFloor; } + protected: /** Packs complete definition of a single block, for per-letter assignment. */ struct sBlockTypeDef @@ -201,9 +223,6 @@ protected: /** Parses the connectors definition text into m_Connectors member. */ void ParseConnectors(const char * a_ConnectorsDef); - - /** Parses the per-depth weight into m_DepthWeight member. */ - void ParseDepthWeight(const char * a_DepthWeightDef); }; diff --git a/src/Generating/PrefabPiecePool.cpp b/src/Generating/PrefabPiecePool.cpp index e4df8efa8..3f0ec8549 100644 --- a/src/Generating/PrefabPiecePool.cpp +++ b/src/Generating/PrefabPiecePool.cpp @@ -5,6 +5,54 @@ #include "Globals.h" #include "PrefabPiecePool.h" +#include "../Bindings/LuaState.h" +#include "SelfTests.h" +#include "WorldStorage/SchematicFileSerializer.h" + + + + + +// Conditionally log a warning +#define CONDWARNING(ShouldLog, ...) \ + if (ShouldLog) \ + { \ + LOGWARNING(__VA_ARGS__); \ + } + + + + + +/** Returns the map of string => eMergeStrategy used when translating cubeset file merge strategies. */ +static std::map<AString, cBlockArea::eMergeStrategy> & GetMergeStrategyMap(void) +{ + static std::map<AString, cBlockArea::eMergeStrategy> msmap; + if (msmap.empty()) + { + // This is the first use, initialize the map: + msmap["msOverwrite"] = cBlockArea::msOverwrite; + msmap["msFillAir"] = cBlockArea::msFillAir; + msmap["msImprint"] = cBlockArea::msImprint; + msmap["msLake"] = cBlockArea::msLake; + msmap["msSpongePrint"] = cBlockArea::msSpongePrint; + msmap["msDifference"] = cBlockArea::msDifference; + msmap["msSimpleCompare"] = cBlockArea::msSimpleCompare; + msmap["msMask"] = cBlockArea::msMask; + } + return msmap; +} + + + + + +//////////////////////////////////////////////////////////////////////////////// +// cPrefabPiecePool: + +cPrefabPiecePool::cPrefabPiecePool(void) +{ +} @@ -26,6 +74,15 @@ cPrefabPiecePool::cPrefabPiecePool( +cPrefabPiecePool::cPrefabPiecePool(const AString & a_FileName, bool a_LogWarnings) +{ + LoadFromFile(a_FileName, a_LogWarnings); +} + + + + + cPrefabPiecePool::~cPrefabPiecePool() { Clear(); @@ -83,6 +140,66 @@ void cPrefabPiecePool::AddStartingPieceDefs(const cPrefab::sDef * a_StartingPiec +bool cPrefabPiecePool::LoadFromFile(const AString & a_FileName, bool a_LogWarnings) +{ + // Read the first 4 KiB of the file in order to auto-detect format: + cFile f; + if (!f.Open(a_FileName, cFile::fmRead)) + { + CONDWARNING(a_LogWarnings, "Cannot open file %s for reading", a_FileName.c_str()); + return false; + } + char buf[4096]; + auto len = f.Read(buf, sizeof(buf)); + f.Close(); + AString Header(buf, static_cast<size_t>(len)); + + if (Header.find("CubesetFormatVersion =") != AString::npos) + { + return LoadFromCubesetFile(a_FileName, a_LogWarnings); + } + CONDWARNING(a_LogWarnings, "Cannot load prefabs from file %s, unknown file format", a_FileName.c_str()); + return false; +} + + + + + +bool cPrefabPiecePool::LoadFromCubesetFile(const AString & a_FileName, bool a_LogWarnings) +{ + // Load the file in the Lua interpreter: + cLuaState Lua(Printf("LoadablePiecePool %s", a_FileName.c_str())); + Lua.Create(); + if (!Lua.LoadFile(a_FileName, a_LogWarnings)) + { + // Reason for failure has already been logged in LoadFile() + return false; + } + + // Check the version: + int Version = 0; + if (!Lua.GetNamedGlobal("Cubeset.Metadata.CubesetFormatVersion", Version)) + { + CONDWARNING(a_LogWarnings, "Cannot load cubeset %s, it doesn't contain version information.", a_FileName.c_str()); + return false; + } + + // Load the data, using the correct version loader: + if (Version == 1) + { + return LoadFromCubesetFileVer1(a_FileName, Lua, a_LogWarnings); + } + + // Unknown version: + CONDWARNING(a_LogWarnings, "Cannot load cubeset %s, version (%d) not supported.", a_FileName.c_str(), Version); + return false; +} + + + + + void cPrefabPiecePool::AddToPerConnectorMap(cPrefab * a_Prefab) { cPiece::cConnectors Connectors = (static_cast<const cPiece *>(a_Prefab))->GetConnectors(); @@ -95,6 +212,303 @@ void cPrefabPiecePool::AddToPerConnectorMap(cPrefab * a_Prefab) + +bool cPrefabPiecePool::LoadFromCubesetFileVer1(const AString & a_FileName, cLuaState & a_LuaState, bool a_LogWarnings) +{ + // Push the Cubeset.Pieces global value on the stack: + lua_getglobal(a_LuaState, "_G"); + cLuaState::cStackValue stk(a_LuaState); + auto pieces = a_LuaState.WalkToValue("Cubeset.Pieces"); + if (!pieces.IsValid() || !lua_istable(a_LuaState, -1)) + { + CONDWARNING(a_LogWarnings, "The cubeset file %s doesn't contain any pieces", a_FileName.c_str()); + return false; + } + + // Iterate over all items in the Cubeset.Pieces value: + int idx = 1; + bool res = true; + while (true) + { + lua_pushinteger(a_LuaState, idx); // stk: [Pieces] [idx] + lua_gettable(a_LuaState, -2); // stk: [Pieces] [PieceItem] + if (!lua_istable(a_LuaState, -1)) + { + // The PieceItem is not present, we've iterated over all items + lua_pop(a_LuaState, 1); // stk: [Pieces] + break; + } + if (!LoadCubesetPieceVer1(a_FileName, a_LuaState, idx, a_LogWarnings)) + { + res = false; + } + lua_pop(a_LuaState, 1); // stk: [Pieces] + idx += 1; + } + return res; +} + + + + + +bool cPrefabPiecePool::LoadCubesetPieceVer1(const AString & a_FileName, cLuaState & a_LuaState, int a_PieceIndex, bool a_LogWarnings) +{ + ASSERT(lua_istable(a_LuaState, -1)); + + // The piece name is optional, but useful for debugging messages: + AString PieceName; + if (!a_LuaState.GetNamedValue("OriginData.ExportName", PieceName)) + { + Printf(PieceName, "Piece #%d", a_PieceIndex); + } + + // Read the hitbox dimensions: + cCuboid Hitbox; + if ( + !a_LuaState.GetNamedValue("Hitbox.MinX", Hitbox.p1.x) || + !a_LuaState.GetNamedValue("Hitbox.MinY", Hitbox.p1.y) || + !a_LuaState.GetNamedValue("Hitbox.MinZ", Hitbox.p1.z) || + !a_LuaState.GetNamedValue("Hitbox.MaxX", Hitbox.p2.x) || + !a_LuaState.GetNamedValue("Hitbox.MaxY", Hitbox.p2.y) || + !a_LuaState.GetNamedValue("Hitbox.MaxZ", Hitbox.p2.z) + ) + { + CONDWARNING(a_LogWarnings, "Cannot load piece %s from file %s, it's missing hitbox information", PieceName.c_str(), a_FileName.c_str()); + return false; + } + + // Load the prefab data: + auto prefab = LoadPrefabFromCubesetVer1(a_FileName, a_LuaState, PieceName, a_LogWarnings); + if (prefab == nullptr) + { + return false; + } + + // Read the connectors + if (!ReadConnectorsCubesetVer1(a_FileName, a_LuaState, PieceName, prefab.get(), a_LogWarnings)) + { + return false; + } + + // Read the allowed rotations. It is an optional metadata value, default to 0: + int AllowedRotations = 0; + a_LuaState.GetNamedValue("Metadata.AllowedRotations", AllowedRotations); + prefab->SetAllowedRotations(AllowedRotations); + + // Apply the relevant metadata: + if (!ApplyMetadataCubesetVer1(a_FileName, a_LuaState, PieceName, prefab.get(), a_LogWarnings)) + { + return false; + } + + // Add the prefab into the list of pieces: + int IsStartingPiece = 0; + a_LuaState.GetNamedValue("Metadata.IsStarting", IsStartingPiece); + if (IsStartingPiece != 0) + { + m_StartingPieces.push_back(prefab.release()); + } + else + { + auto p = prefab.release(); + m_AllPieces.push_back(p); + AddToPerConnectorMap(p); + } + return true; +} + + + + + +UniquePtr<cPrefab> cPrefabPiecePool::LoadPrefabFromCubesetVer1( + const AString & a_FileName, + cLuaState & a_LuaState, + const AString & a_PieceName, + bool a_LogWarnings +) +{ + // First try loading a referenced schematic file, if any: + AString SchematicFileName; + if (a_LuaState.GetNamedValue("SchematicFileName", SchematicFileName)) + { + auto PathEnd = a_FileName.find_last_of("/\\"); // Find the last path separator + if (PathEnd != AString::npos) + { + SchematicFileName = a_FileName.substr(0, PathEnd) + SchematicFileName; + } + cBlockArea area; + if (!cSchematicFileSerializer::LoadFromSchematicFile(area, SchematicFileName)) + { + CONDWARNING(a_LogWarnings, "Cannot load schematic file \"%s\" for piece %s in cubeset %s.", + SchematicFileName.c_str(), a_PieceName.c_str(), a_FileName.c_str() + ); + return nullptr; + } + return cpp14::make_unique<cPrefab>(area); + } // if (SchematicFileName) + + // There's no referenced schematic file, load from BlockDefinitions / BlockData. + // Get references to the data and the table.concat function: + cLuaState::cRef TableConcat, BlockDefinitions, BlockData; + if ( + !a_LuaState.GetNamedGlobal("table.concat", TableConcat) || + !a_LuaState.GetNamedValue("BlockDefinitions", BlockDefinitions) || + !a_LuaState.GetNamedValue("BlockData", BlockData) + ) + { + CONDWARNING(a_LogWarnings, "Cannot parse block data for piece %s in cubeset %s", a_PieceName.c_str(), a_FileName.c_str()); + return nullptr; + } + + // Call table.concat() on the BlockDefinitions: + AString BlockDefStr; + if (!a_LuaState.Call(TableConcat, BlockDefinitions, "\n", cLuaState::Return, BlockDefStr)) + { + CONDWARNING(a_LogWarnings, "Cannot concat block definitions for piece %s in cubeset %s", a_PieceName.c_str(), a_FileName.c_str()); + return nullptr; + } + + // Call table.concat() on the BlockData: + AString BlockDataStr; + if (!a_LuaState.Call(TableConcat, BlockData, "", cLuaState::Return, BlockDataStr)) + { + CONDWARNING(a_LogWarnings, "Cannot concat block data for piece %s in cubeset %s", a_PieceName.c_str(), a_FileName.c_str()); + return nullptr; + } + + // Read the size: + int SizeX = 0, SizeY = 0, SizeZ = 0; + if ( + !a_LuaState.GetNamedValue("Size.x", SizeX) || + !a_LuaState.GetNamedValue("Size.y", SizeY) || + !a_LuaState.GetNamedValue("Size.z", SizeZ) + ) + { + CONDWARNING(a_LogWarnings, "Cannot load piece %s from file %s, its size information is missing", a_PieceName.c_str(), a_FileName.c_str()); + return nullptr; + } + + // Check that the size matches the data length: + if (static_cast<size_t>(SizeX * SizeY * SizeZ) != BlockDataStr.size()) + { + CONDWARNING(a_LogWarnings, "Cannot create piece %s from file %s, its size (%d) doesn't match the blockdata length (%u)", + a_PieceName.c_str(), a_FileName.c_str(), + SizeX * SizeY * SizeZ, static_cast<unsigned>(BlockDataStr.size()) + ); + return nullptr; + } + + return cpp14::make_unique<cPrefab>(BlockDefStr, BlockDataStr, SizeX, SizeY, SizeZ); +} + + + + + +bool cPrefabPiecePool::ReadConnectorsCubesetVer1( + const AString & a_FileName, + cLuaState & a_LuaState, + const AString & a_PieceName, + cPrefab * a_Prefab, + bool a_LogWarnings +) +{ + // Get the Connectors subtable: + auto conns = a_LuaState.WalkToValue("Connectors"); + if (!conns.IsValid()) + { + CONDWARNING(a_LogWarnings, "Cannot load piece %s from file %s, it has no connectors definition.", a_PieceName.c_str(), a_FileName.c_str()); + return false; + } + + // Iterate over all items in the Connectors table: + int idx = 1; + bool res = true; + while (true) + { + lua_pushinteger(a_LuaState, idx); // stk: [Connectors] [idx] + lua_gettable(a_LuaState, -2); // stk: [Connectors] [conn] + if (!lua_istable(a_LuaState, -1)) + { + // The connector is not present, we've iterated over all items + lua_pop(a_LuaState, 1); // stk: [Connectors] + break; + } + int Type = 0, RelX = 0, RelY = 0, RelZ = 0; + eBlockFace Direction = BLOCK_FACE_NONE; + if ( + !a_LuaState.GetNamedValue("Type", Type) || + !a_LuaState.GetNamedValue("RelX", RelX) || + !a_LuaState.GetNamedValue("RelY", RelY) || + !a_LuaState.GetNamedValue("RelZ", RelZ) || + !a_LuaState.GetNamedValue("Direction", Direction) + ) + { + CONDWARNING(a_LogWarnings, "Piece %s in file %s has a malformed Connector at index %d. Skipping the connector.", a_PieceName.c_str(), a_FileName.c_str(), idx); + res = false; + continue; + } + a_Prefab->AddConnector(RelX, RelY, RelZ, Direction, Type); + lua_pop(a_LuaState, 1); // stk: [Connectors] + idx += 1; + } + return res; +} + + + + + +bool cPrefabPiecePool::ApplyMetadataCubesetVer1( + const AString & a_FileName, + cLuaState & a_LuaState, + const AString & a_PieceName, + cPrefab * a_Prefab, + bool a_LogWarnings +) +{ + // Push the Metadata table on top of the Lua stack: + auto md = a_LuaState.WalkToValue("Metadata"); + if (!md.IsValid()) + { + return false; + } + + // Get the values: + int AddWeightIfSame = 0, DefaultWeight = 100, MoveToGround = 0, ShouldExpandFloor = 0; + AString DepthWeight, MergeStrategy; + a_LuaState.GetNamedValue("AddWeightIfSame", AddWeightIfSame); + a_LuaState.GetNamedValue("DefaultWeight", DefaultWeight); + a_LuaState.GetNamedValue("DepthWeight", DepthWeight); + a_LuaState.GetNamedValue("MergeStrategy", MergeStrategy); + a_LuaState.GetNamedValue("MoveToGround", MoveToGround); + a_LuaState.GetNamedValue("ShouldExpandFloor", ShouldExpandFloor); + + // Apply the values: + a_Prefab->SetAddWeightIfSame(AddWeightIfSame); + a_Prefab->SetDefaultWeight(DefaultWeight); + a_Prefab->ParseDepthWeight(DepthWeight.c_str()); + auto msmap = GetMergeStrategyMap(); + auto strategy = msmap.find(MergeStrategy); + if (strategy == msmap.end()) + { + CONDWARNING(a_LogWarnings, "Unknown merge strategy (\"%s\") specified for piece %s in file %s. Using msSpongePrint instead.", + MergeStrategy.c_str(), a_PieceName.c_str(), a_FileName.c_str() + ); + a_Prefab->SetMergeStrategy(cBlockArea::msSpongePrint); + } + a_Prefab->SetMoveToGround(MoveToGround != 0); + a_Prefab->SetExtendFloor(ShouldExpandFloor != 0); + + return true; +} + + + + + cPieces cPrefabPiecePool::GetPiecesWithConnector(int a_ConnectorType) { return m_PiecesByConnector[a_ConnectorType]; diff --git a/src/Generating/PrefabPiecePool.h b/src/Generating/PrefabPiecePool.h index b9c1f0483..a22c6abd4 100644 --- a/src/Generating/PrefabPiecePool.h +++ b/src/Generating/PrefabPiecePool.h @@ -16,6 +16,13 @@ +// fwd: +class cLuaState; + + + + + class cPrefabPiecePool : public cPiecePool { @@ -34,6 +41,10 @@ public: const cPrefab::sDef * a_StartingPieceDefs, size_t a_NumStartingPieceDefs ); + /** Creates a pool and loads the contents of the specified file into it. + If a_LogWarnings is true, logs a warning to console when loading fails. */ + cPrefabPiecePool(const AString & a_FileName, bool a_LogWarnings); + /** Destroys the pool, freeing all pieces. */ ~cPrefabPiecePool(); @@ -50,6 +61,20 @@ public: May be called multiple times with different PieceDefs, will add all such pieces. */ void AddStartingPieceDefs(const cPrefab::sDef * a_StartingPieceDefs, size_t a_NumStartingPieceDefs); + /** Loads the pieces from the specified file. Returns true if successful, false on error. + If a_LogWarnings is true, logs a warning to console when loading fails. */ + bool LoadFromFile(const AString & a_FileName, bool a_LogWarnings); + + /** Loads the pieces from the specified Cubeset file. Returns true if successful, false on error. + If a_LogWarnings is true, logs a warning to console when loading fails. */ + bool LoadFromCubesetFile(const AString & a_FileName, bool a_LogWarnings); + + /** Returns the number of regular (non-starting) pieces. */ + size_t GetAllPiecesCount(void) const { return m_AllPieces.size(); } + + /** Returns the number of starting pieces. */ + size_t GetStartingPiecesCount(void) const { return m_StartingPieces.size(); } + protected: /** The type used to map a connector type to the list of pieces with that connector */ @@ -70,7 +95,61 @@ protected: /** Adds the prefab to the m_PiecesByConnector map for all its connectors. */ void AddToPerConnectorMap(cPrefab * a_Prefab); + + /** Loads the pieces from the cubeset file parsed into the specified Lua state. + Returns true on success, false on error. + If a_LogWarnings is true, logs a warning to console when loading fails. */ + bool LoadFromCubesetFileVer1(const AString & a_FileName, cLuaState & a_LuaState, bool a_LogWarnings); + + /** Loads a single piece from the cubeset file parsed into the specified Lua state. + The piece's definition table is expected to be at the top of the Lua stack. + Returns true on success, false on error. + a_PieceIndex is the index of the piece, in the Pieces table. It is used for logging only. + If a_LogWarnings is true, logs a warning to console when loading fails. */ + bool LoadCubesetPieceVer1(const AString & a_FileName, cLuaState & a_LuaState, int a_PieceIndex, bool a_LogWarnings); + + /** Loads a single piece's prefab from the cubeset file parsed into the specified Lua state. + The piece's definition table is expected to be at the top of the Lua stack. + Returns the prefab on success, nullptr on failure. + a_PieceName is the identification of the piece, used for logging only. + If a_LogWarnings is true, logs a warning to console when loading fails. */ + UniquePtr<cPrefab> LoadPrefabFromCubesetVer1( + const AString & a_FileName, + cLuaState & a_LuaState, + const AString & a_PieceName, + bool a_LogWarnings + ); + /** Reads a single piece's connectors from the cubeset file parsed into the specified Lua state. + The piece's definition table is expected to be at the top of the Lua stack. + Returns true on success, false on failure. + The connectors are added into the a_Prefab object. + No Connectors table is considered a failure, empty Connectors table is considered a success. + If any of the connectors are malformed, it is considered a failure, although the rest of the connectors will still load. + a_PieceName is the identification of the piece, used for logging only. + If a_LogWarnings is true, logs a warning to console when loading fails. */ + bool ReadConnectorsCubesetVer1( + const AString & a_FileName, + cLuaState & a_LuaState, + const AString & a_PieceName, + cPrefab * a_Prefab, + bool a_LogWarnings + ); + + /** Reads a single piece's metadata from the cubeset file parsed into the specified Lua state. + The piece's definition table is expected to be at the top of the Lua stack. + Returns true on success, false on failure. + The metadata is applied into the a_Prefab object. + a_PieceName is the identification of the piece, used for logging only. + If a_LogWarnings is true, logs a warning to console when loading fails. */ + bool ApplyMetadataCubesetVer1( + const AString & a_FileName, + cLuaState & a_LuaState, + const AString & a_PieceName, + cPrefab * a_Prefab, + bool a_LogWarnings + ); + // cPiecePool overrides: virtual cPieces GetPiecesWithConnector(int a_ConnectorType) override; virtual cPieces GetStartingPieces(void) override; diff --git a/src/Globals.h b/src/Globals.h index b787a94da..f0e370d2c 100644 --- a/src/Globals.h +++ b/src/Globals.h @@ -261,14 +261,15 @@ template class SizeChecker<UInt8, 1>; +// Common headers (part 1, without macros): +#include "StringUtils.h" +#include "OSSupport/CriticalSection.h" +#include "OSSupport/Event.h" +#include "OSSupport/File.h" +#include "OSSupport/StackTrace.h" + #ifndef TEST_GLOBALS - // Common headers (part 1, without macros): - #include "StringUtils.h" - #include "OSSupport/CriticalSection.h" - #include "OSSupport/Event.h" - #include "OSSupport/File.h" #include "Logger.h" - #include "OSSupport/StackTrace.h" #else // Logging functions void inline LOGERROR(const char * a_Format, ...) FORMATSTRING(1, 2); @@ -315,6 +316,9 @@ void inline LOG(const char * a_Format, ...) va_end(argList); } +#define LOGINFO LOG +#define LOGWARN LOGWARNING + #endif diff --git a/src/WorldStorage/CMakeLists.txt b/src/WorldStorage/CMakeLists.txt index 074958191..017d85f27 100644 --- a/src/WorldStorage/CMakeLists.txt +++ b/src/WorldStorage/CMakeLists.txt @@ -14,7 +14,8 @@ SET (SRCS ScoreboardSerializer.cpp StatSerializer.cpp WSSAnvil.cpp - WorldStorage.cpp) + WorldStorage.cpp +) SET (HDRS EnchantmentSerializer.h @@ -26,7 +27,8 @@ SET (HDRS ScoreboardSerializer.h StatSerializer.h WSSAnvil.h - WorldStorage.h) + WorldStorage.h +) if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") set_source_files_properties(EnchantmentSerializer.cpp PROPERTIES COMPILE_FLAGS "-Wno-error=old-style-cast") diff --git a/src/WorldStorage/SchematicFileSerializer.cpp b/src/WorldStorage/SchematicFileSerializer.cpp index 199873968..3e9ed5bef 100644 --- a/src/WorldStorage/SchematicFileSerializer.cpp +++ b/src/WorldStorage/SchematicFileSerializer.cpp @@ -238,27 +238,27 @@ bool cSchematicFileSerializer::LoadFromSchematicNBT(cBlockArea & a_BlockArea, cP } // Copy the block types and metas: - size_t NumBytes = a_BlockArea.GetBlockCount(); - if (a_NBT.GetDataLength(TBlockTypes) < NumBytes) + size_t NumTypeBytes = a_BlockArea.GetBlockCount(); + if (a_NBT.GetDataLength(TBlockTypes) < NumTypeBytes) { - LOG("BlockTypes truncated in the schematic file (exp %d, got %d bytes). Loading partial.", - (int)NumBytes, (int)a_NBT.GetDataLength(TBlockTypes) + LOG("BlockTypes truncated in the schematic file (exp %u, got %u bytes). Loading partial.", + static_cast<unsigned>(NumTypeBytes), static_cast<unsigned>(a_NBT.GetDataLength(TBlockTypes)) ); - NumBytes = a_NBT.GetDataLength(TBlockTypes); + NumTypeBytes = a_NBT.GetDataLength(TBlockTypes); } - memcpy(a_BlockArea.m_BlockTypes, a_NBT.GetData(TBlockTypes), NumBytes); + memcpy(a_BlockArea.m_BlockTypes, a_NBT.GetData(TBlockTypes), NumTypeBytes); if (AreMetasPresent) { - size_t NumBytes = a_BlockArea.GetBlockCount(); - if (a_NBT.GetDataLength(TBlockMetas) < NumBytes) + size_t NumMetaBytes = a_BlockArea.GetBlockCount(); + if (a_NBT.GetDataLength(TBlockMetas) < NumMetaBytes) { - LOG("BlockMetas truncated in the schematic file (exp %d, got %d bytes). Loading partial.", - (int)NumBytes, (int)a_NBT.GetDataLength(TBlockMetas) + LOG("BlockMetas truncated in the schematic file (exp %u, got %u bytes). Loading partial.", + static_cast<unsigned>(NumMetaBytes), static_cast<unsigned>(a_NBT.GetDataLength(TBlockMetas)) ); - NumBytes = a_NBT.GetDataLength(TBlockMetas); + NumMetaBytes = a_NBT.GetDataLength(TBlockMetas); } - memcpy(a_BlockArea.m_BlockMetas, a_NBT.GetData(TBlockMetas), NumBytes); + memcpy(a_BlockArea.m_BlockMetas, a_NBT.GetData(TBlockMetas), NumMetaBytes); } return true; |