/** Parses a string containing a range in which both values are optional ("<MinHeight>|<MaxHeight>") into Min, Range. Returns true if successful, false on failure. If a_LogWarnings is true, outputs failure reasons to console. The range is returned in a_Min and a_Range, they are left unchanged if the range value is not present in the string. */ static bool ParseRange(const AString & a_Params, int & a_Min, int & a_Range, bool a_LogWarnings) { auto params = StringSplitAndTrim(a_Params, "|"); if (params.size() == 0) { // No params, generate directly on top: return true; } if (!StringToInteger(params[0], a_Min)) { // Failed to parse the min rel height: CONDWARNING(a_LogWarnings, "Cannot parse minimum height from string \"%s\"!", params[0].c_str()); return false; } if (params.size() == 1) { // Only one param was given, there's no range return true; } int maxHeight = a_Min; if (!StringToInteger(params[1], maxHeight)) { CONDWARNING(a_LogWarnings, "Cannot parse maximum height from string \"%s\"!", params[1].c_str()); return false; } if (maxHeight < a_Min) { std::swap(maxHeight, a_Min); } a_Range = maxHeight - a_Min + 1; return true; }
bool cPrefabPiecePool::LoadFromString(const AString & a_Contents, const AString & a_FileName, bool a_LogWarnings) { // If the contents start with GZip signature, ungzip and retry: if (a_Contents.substr(0, 3) == "\x1f\x8b\x08") { AString Uncompressed; auto res = UncompressStringGZIP(a_Contents.data(), a_Contents.size(), Uncompressed); if (res == Z_OK) { return LoadFromString(Uncompressed, a_FileName, a_LogWarnings); } else { CONDWARNING(a_LogWarnings, "Failed to decompress Gzip data in file %s: %d", a_FileName.c_str(), res); return false; } } // Search the first 8 KiB of the file for the format auto-detection string: auto Header = a_Contents.substr(0, 8192); if (Header.find("CubesetFormatVersion =") != AString::npos) { return LoadFromCubeset(a_Contents, 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; }
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; AString DirectionStr; cPiece::cConnector::eDirection Direction = cPiece::cConnector::dirYM; 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", DirectionStr) || !cPiece::cConnector::StringToDirection(DirectionStr, Direction) ) { CONDWARNING(a_LogWarnings, "Piece %s in file %s has a malformed Connector at index %d ({%d, %d, %d}, type %d, direction %s). Skipping the connector.", a_PieceName.c_str(), a_FileName.c_str(), idx, RelX, RelY, RelZ, Type, DirectionStr.c_str() ); res = false; lua_pop(a_LuaState, 1); // stk: [Connectors] idx += 1; continue; } a_Prefab->AddConnector(RelX, RelY, RelZ, Direction, Type); lua_pop(a_LuaState, 1); // stk: [Connectors] idx += 1; } return res; }
virtual bool InitializeFromString(const AString & a_Params, bool a_LogWarnings) override { // Params: "<Height>", compulsory if (!StringToInteger(a_Params, m_Height)) { CONDWARNING(a_LogWarnings, "Cannot parse the fixed height from string \"%s\"!", a_Params.c_str()); return false; } return true; }
bool cPrefabPiecePool::LoadFromFile(const AString & a_FileName, bool a_LogWarnings) { // Read the file into a string buffer, load from string: auto contents = cFile::ReadWholeFile(a_FileName); if (contents.empty()) { CONDWARNING(a_LogWarnings, "Cannot read data from file %s", a_FileName.c_str()); return false; } return LoadFromString(contents, a_FileName, a_LogWarnings); }
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; }
void cPrefabPiecePool::ApplyBaseMetadataCubesetVer1( const AString & a_FileName, bool a_LogWarnings ) { // Set the metadata values to defaults: m_MinDensity = 100; m_MaxDensity = 100; m_VillageRoadBlockType = E_BLOCK_GRAVEL; m_VillageRoadBlockMeta = 0; m_VillageWaterRoadBlockType = E_BLOCK_PLANKS; m_VillageWaterRoadBlockMeta = 0; // Read the metadata values: m_IntendedUse = GetMetadata("IntendedUse"); GetStringMapInteger(m_Metadata, "MaxDensity", m_MaxDensity); GetStringMapInteger(m_Metadata, "MinDensity", m_MinDensity); GetStringMapInteger(m_Metadata, "VillageRoadBlockType", m_VillageRoadBlockType); GetStringMapInteger(m_Metadata, "VillageRoadBlockMeta", m_VillageRoadBlockMeta); GetStringMapInteger(m_Metadata, "VillageWaterRoadBlockType", m_VillageWaterRoadBlockType); GetStringMapInteger(m_Metadata, "VillageWaterRoadBlockMeta", m_VillageWaterRoadBlockMeta); // Read the allowed biomes: AString allowedBiomes = GetMetadata("AllowedBiomes"); if (allowedBiomes.empty()) { // All biomes are allowed: for (int b = biFirstBiome; b <= biMaxBiome; b++) { m_AllowedBiomes.insert(static_cast<EMCSBiome>(b)); } for (int b = biFirstVariantBiome; b <= biMaxVariantBiome; b++) { m_AllowedBiomes.insert(static_cast<EMCSBiome>(b)); } } else { auto biomes = StringSplitAndTrim(allowedBiomes, ","); for (const auto & biome: biomes) { EMCSBiome b = StringToBiome(biome); if (b == biInvalidBiome) { CONDWARNING(a_LogWarnings, "Invalid biome (\"%s\") specified in AllowedBiomes in cubeset file %s. Skipping the biome.", biome.c_str(), a_FileName.c_str() ); continue; } m_AllowedBiomes.insert(b); } } }
virtual bool InitializeFromString(const AString & a_Params, bool a_LogWarnings) override { // Params: "<MinHeight>|<MaxHeight>", all compulsory auto params = StringSplitAndTrim(a_Params, "|"); if (params.size() != 2) { CONDWARNING(a_LogWarnings, "Cannot parse the range parameters from string \"%s\"!", a_Params.c_str()); return false; } int Max = 0; if (!StringToInteger(params[0], m_Min) || !StringToInteger(params[1], Max)) { CONDWARNING(a_LogWarnings, "Cannot parse the minimum or maximum height from string \"%s\"!", a_Params.c_str()); return false; } if (m_Min > Max) { std::swap(m_Min, Max); } m_Range = Max - m_Min + 1; return true; }
bool cPrefabPiecePool::ApplyPieceMetadataCubesetVer1( 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); } else { a_Prefab->SetMergeStrategy(strategy->second); } a_Prefab->SetMoveToGround(MoveToGround != 0); a_Prefab->SetExtendFloor(ShouldExpandFloor != 0); return true; }
bool cPrefabPiecePool::LoadFromCubesetFileVer1(const AString & a_FileName, cLuaState & a_LuaState, bool a_LogWarnings) { // Load the metadata: ApplyPoolMetadataCubesetVer1(a_FileName, a_LuaState, 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::ApplyPoolMetadataCubesetVer1( const AString & a_FileName, cLuaState & a_LuaState, bool a_LogWarnings ) { // Push the Cubeset.Metadata table on top of the Lua stack: lua_getglobal(a_LuaState, "_G"); auto md = a_LuaState.WalkToValue("Cubeset.Metadata"); if (!md.IsValid()) { CONDWARNING(a_LogWarnings, "Cannot load cubeset from file %s: Cubeset.Metadata table is missing", a_FileName.c_str()); return false; } // Set the metadata values to defaults: m_MinDensity = 100; m_MaxDensity = 100; m_VillageRoadBlockType = E_BLOCK_GRAVEL; m_VillageRoadBlockMeta = 0; m_VillageWaterRoadBlockType = E_BLOCK_PLANKS; m_VillageWaterRoadBlockMeta = 0; // Read the metadata values: a_LuaState.GetNamedValue("IntendedUse", m_IntendedUse); a_LuaState.GetNamedValue("MaxDensity", m_MaxDensity); a_LuaState.GetNamedValue("MinDensity", m_MinDensity); a_LuaState.GetNamedValue("VillageRoadBlockType", m_VillageRoadBlockType); a_LuaState.GetNamedValue("VillageRoadBlockMeta", m_VillageRoadBlockMeta); a_LuaState.GetNamedValue("VillageWaterRoadBlockType", m_VillageWaterRoadBlockType); a_LuaState.GetNamedValue("VillageWaterRoadBlockMeta", m_VillageWaterRoadBlockMeta); AString allowedBiomes; if (a_LuaState.GetNamedValue("AllowedBiomes", allowedBiomes)) { auto biomes = StringSplitAndTrim(allowedBiomes, ","); for (const auto & biome: biomes) { EMCSBiome b = StringToBiome(biome); if (b == biInvalidBiome) { CONDWARNING(a_LogWarnings, "Invalid biome (\"%s\") specified in AllowedBiomes in cubeset file %s. Skipping the biome.", biome.c_str(), a_FileName.c_str() ); continue; } m_AllowedBiomes.insert(b); } } else { // All biomes are allowed: for (int b = biFirstBiome; b <= biMaxBiome; b++) { m_AllowedBiomes.insert(static_cast<EMCSBiome>(b)); } for (int b = biFirstVariantBiome; b <= biMaxVariantBiome; b++) { m_AllowedBiomes.insert(static_cast<EMCSBiome>(b)); } } 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::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; } prefab->SetHitBox(Hitbox); // 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 (!ApplyPieceMetadataCubesetVer1(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; }
bool cPrefabPiecePool::ReadPieceMetadataCubesetVer1( 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; AString DepthWeight, MergeStrategy, VerticalLimit, VerticalStrategy; 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("VerticalLimit", VerticalLimit); a_LuaState.GetNamedValue("VerticalStrategy", VerticalStrategy); // 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); } else { a_Prefab->SetMergeStrategy(strategy->second); } a_Prefab->SetMoveToGround(MoveToGround != 0); AString ExpandFloorStrategyStr; if (!a_LuaState.GetNamedValue("ExpandFloorStrategy", ExpandFloorStrategyStr)) { // Check the older variant for ExpandFloorStrategy, ShouldExpandFloor: int ShouldExpandFloor; if (a_LuaState.GetNamedValue("ShouldExpandFloor", ShouldExpandFloor)) { LOG("Piece \"%s\" in file \"%s\" is using the old \"ShouldExpandFloor\" attribute. Use the new \"ExpandFloorStrategy\" attribute instead for more options.", a_PieceName.c_str(), a_FileName.c_str() ); a_Prefab->SetExtendFloorStrategy((ShouldExpandFloor != 0) ? cPrefab::efsRepeatBottomTillNonAir : cPrefab::efsNone); } } else { auto lcExpandFloorStrategyStr = StrToLower(ExpandFloorStrategyStr); if (lcExpandFloorStrategyStr == "repeatbottomtillnonair") { a_Prefab->SetExtendFloorStrategy(cPrefab::efsRepeatBottomTillNonAir); } else if (lcExpandFloorStrategyStr == "repeatbottomtillsolid") { a_Prefab->SetExtendFloorStrategy(cPrefab::efsRepeatBottomTillSolid); } else { if (lcExpandFloorStrategyStr != "none") { LOGWARNING("Piece \"%s\" in file \"%s\" is using an unknown \"ExpandFloorStrategy\" attribute value: \"%s\"", a_PieceName.c_str(), a_FileName.c_str(), ExpandFloorStrategyStr.c_str() ); } a_Prefab->SetExtendFloorStrategy(cPrefab::efsNone); } } if (!VerticalLimit.empty()) { if (!a_Prefab->SetVerticalLimitFromString(VerticalLimit, a_LogWarnings)) { CONDWARNING(a_LogWarnings, "Unknown VerticalLimit (\"%s\") specified for piece %s in file %s. Using no limit instead.", VerticalLimit.c_str(), a_PieceName.c_str(), a_FileName.c_str() ); } } a_Prefab->SetVerticalStrategyFromString(VerticalStrategy, a_LogWarnings); return true; }