From ae1668942059b68580c83adc43ee9fb06e1e13f2 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Wed, 17 Jun 2015 15:22:43 +0200 Subject: [PATCH] PrefabPiecePool: Added loading from cubeset file. --- src/Generating/Prefab.cpp | 49 ++++ src/Generating/Prefab.h | 25 +- src/Generating/PrefabPiecePool.cpp | 439 +++++++++++++++++++++++++++++ src/Generating/PrefabPiecePool.h | 79 ++++++ 4 files changed, 589 insertions(+), 3 deletions(-) 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..c98eaaa5e 100644 --- a/src/Generating/PrefabPiecePool.cpp +++ b/src/Generating/PrefabPiecePool.cpp @@ -5,6 +5,81 @@ #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__); \ + } + + + + + +#if SELF_TEST +static class cPrefabPiecePoolTest +{ +public: + cPrefabPiecePoolTest(void) + { + cSelfTests::Get().Register(cSelfTests::SelfTestFunction(cPrefabPiecePoolTest::TestLoading), "PrefabPiecePool loading test"); + } + + static void TestLoading(void) + { + cPrefabPiecePool test; + auto res = test.LoadFromFile("test.cubeset", true); + if (!res) + { + LOGWARNING("Loading from file \"test.cubeset\" failed."); + return; + } + LOG("Loaded %u pieces and %u starting pieces", static_cast(test.GetAllPiecesCount()), static_cast(test.GetStartingPiecesCount())); + } +} g_Test; +#endif + + + + + +/** Returns the map of string => eMergeStrategy used when translating cubeset file merge strategies. */ +static std::map & GetMergeStrategyMap(void) +{ + static std::map 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 +101,15 @@ cPrefabPiecePool::cPrefabPiecePool( +cPrefabPiecePool::cPrefabPiecePool(const AString & a_FileName, bool a_LogWarnings) +{ + LoadFromFile(a_FileName, a_LogWarnings); +} + + + + + cPrefabPiecePool::~cPrefabPiecePool() { Clear(); @@ -83,6 +167,64 @@ 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)) + { + return false; + } + char buf[4096]; + auto len = f.Read(buf, sizeof(buf)); + f.Close(); + AString Header(buf, static_cast(len)); + + if (Header.find("CubesetFormatVersion =") != AString::npos) + { + return LoadFromCubesetFile(a_FileName, a_LogWarnings); + } + 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(a_Prefab))->GetConnectors(); @@ -95,6 +237,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 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(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(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(BlockDataStr.size()) + ); + return nullptr; + } + + return cpp14::make_unique(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 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;