int cLuaState::CallFunctionWithForeignParams( const AString & a_FunctionName, cLuaState & a_SrcLuaState, int a_SrcParamStart, int a_SrcParamEnd ) { ASSERT(IsValid()); ASSERT(a_SrcLuaState.IsValid()); // Store the stack position before any changes int OldTop = lua_gettop(m_LuaState); // Push the function to call, including the error handler: if (!PushFunction(a_FunctionName.c_str())) { LOGWARNING("Function '%s' not found", a_FunctionName.c_str()); lua_settop(m_LuaState, OldTop); return -1; } // Copy the function parameters to the target state if (CopyStackFrom(a_SrcLuaState, a_SrcParamStart, a_SrcParamEnd) < 0) { // Something went wrong, fix the stack and exit lua_settop(m_LuaState, OldTop); m_NumCurrentFunctionArgs = -1; m_CurrentFunctionName.clear(); return -1; } // Call the function, with an error handler: int s = lua_pcall(m_LuaState, a_SrcParamEnd - a_SrcParamStart + 1, LUA_MULTRET, OldTop + 1); if (ReportErrors(s)) { LOGWARN("Error while calling function '%s' in '%s'", a_FunctionName.c_str(), m_SubsystemName.c_str()); // Reset the stack: lua_settop(m_LuaState, OldTop); // Reset the internal checking mechanisms: m_NumCurrentFunctionArgs = -1; m_CurrentFunctionName.clear(); // Make Lua think everything is okay and return 0 values, so that plugins continue executing. // The failure is indicated by the zero return values. return 0; } // Reset the internal checking mechanisms: m_NumCurrentFunctionArgs = -1; m_CurrentFunctionName.clear(); // Remove the error handler from the stack: lua_remove(m_LuaState, OldTop + 1); // Return the number of return values: return lua_gettop(m_LuaState) - OldTop; }
void cLuaState::cRef::RefStack(cLuaState & a_LuaState, int a_StackPos) { ASSERT(a_LuaState.IsValid()); if (m_LuaState != nullptr) { UnRef(); } m_LuaState = &a_LuaState; lua_pushvalue(a_LuaState, a_StackPos); // Push a copy of the value at a_StackPos onto the stack m_Ref = luaL_ref(a_LuaState, LUA_REGISTRYINDEX); }
bool cLuaState::cCallback::IsSameLuaState(cLuaState & a_LuaState) { cCSLock lock(m_CS); if (!m_Ref.IsValid()) { return false; } auto canonState = a_LuaState.QueryCanonLuaState(); if (canonState == nullptr) { return false; } return (m_Ref.GetLuaState() == static_cast<lua_State *>(*canonState)); }
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 cLuaState::cCallback::RefStack(cLuaState & a_LuaState, int a_StackPos) { // Check if the stack contains a function: if (!lua_isfunction(a_LuaState, a_StackPos)) { return false; } // Clear any previous callback: Clear(); // Add self to LuaState's callback-tracking: a_LuaState.TrackCallback(*this); // Store the new callback: cCSLock Lock(m_CS); m_Ref.RefStack(a_LuaState, a_StackPos); 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; }
int cLuaState::CopyStackFrom(cLuaState & a_SrcLuaState, int a_SrcStart, int a_SrcEnd) { /* // DEBUG: LOGD("Copying stack values from %d to %d", a_SrcStart, a_SrcEnd); a_SrcLuaState.LogStack("Src stack before copying:"); LogStack("Dst stack before copying:"); */ for (int i = a_SrcStart; i <= a_SrcEnd; ++i) { int t = lua_type(a_SrcLuaState, i); switch (t) { case LUA_TNIL: { lua_pushnil(m_LuaState); break; } case LUA_TSTRING: { AString s; a_SrcLuaState.ToString(i, s); Push(s); break; } case LUA_TBOOLEAN: { bool b = (tolua_toboolean(a_SrcLuaState, i, false) != 0); Push(b); break; } case LUA_TNUMBER: { lua_Number d = tolua_tonumber(a_SrcLuaState, i, 0); Push(d); break; } case LUA_TUSERDATA: { // Get the class name: const char * type = nullptr; if (lua_getmetatable(a_SrcLuaState, i) == 0) { LOGWARNING("%s: Unknown class in pos %d, cannot copy.", __FUNCTION__, i); lua_pop(m_LuaState, i - a_SrcStart); return -1; } lua_rawget(a_SrcLuaState, LUA_REGISTRYINDEX); // Stack +1 type = lua_tostring(a_SrcLuaState, -1); lua_pop(a_SrcLuaState, 1); // Stack -1 // Copy the value: void * ud = tolua_touserdata(a_SrcLuaState, i, nullptr); tolua_pushusertype(m_LuaState, ud, type); break; } default: { LOGWARNING("%s: Unsupported value: '%s' at stack position %d. Can only copy numbers, strings, bools and classes!", __FUNCTION__, lua_typename(a_SrcLuaState, t), i ); a_SrcLuaState.LogStack("Stack where copying failed:"); lua_pop(m_LuaState, i - a_SrcStart); return -1; } } } return a_SrcEnd - a_SrcStart + 1; }
bool cLuaState::CopySingleValueFrom(cLuaState & a_SrcLuaState, int a_StackIdx, int a_NumAllowedNestingLevels) { int t = lua_type(a_SrcLuaState, a_StackIdx); switch (t) { case LUA_TNIL: { lua_pushnil(m_LuaState); return true; } case LUA_TSTRING: { AString s; a_SrcLuaState.ToString(a_StackIdx, s); Push(s); return true; } case LUA_TBOOLEAN: { bool b = (tolua_toboolean(a_SrcLuaState, a_StackIdx, false) != 0); Push(b); return true; } case LUA_TNUMBER: { lua_Number d = tolua_tonumber(a_SrcLuaState, a_StackIdx, 0); Push(d); return true; } case LUA_TUSERDATA: { // Get the class name: const char * type = nullptr; if (lua_getmetatable(a_SrcLuaState, a_StackIdx) == 0) { LOGWARNING("%s: Unknown class in pos %d, cannot copy.", __FUNCTION__, a_StackIdx); return false; } lua_rawget(a_SrcLuaState, LUA_REGISTRYINDEX); // Stack +1 type = lua_tostring(a_SrcLuaState, -1); lua_pop(a_SrcLuaState, 1); // Stack -1 // Copy the value: void * ud = tolua_touserdata(a_SrcLuaState, a_StackIdx, nullptr); tolua_pushusertype(m_LuaState, ud, type); return true; } case LUA_TTABLE: { if (!CopyTableFrom(a_SrcLuaState, a_StackIdx, a_NumAllowedNestingLevels - 1)) { LOGWARNING("%s: Failed to copy table in pos %d.", __FUNCTION__, a_StackIdx); return false; } return true; } default: { LOGWARNING("%s: Unsupported value: '%s' at stack position %d. Can only copy numbers, strings, bools, classes and simple tables!", __FUNCTION__, lua_typename(a_SrcLuaState, t), a_StackIdx ); return false; } } }
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; }