diff options
Diffstat (limited to 'src/Bindings')
-rw-r--r-- | src/Bindings/LuaChunkStay.cpp | 173 | ||||
-rw-r--r-- | src/Bindings/LuaChunkStay.h | 73 | ||||
-rw-r--r-- | src/Bindings/LuaState.cpp | 69 | ||||
-rw-r--r-- | src/Bindings/LuaState.h | 17 | ||||
-rw-r--r-- | src/Bindings/ManualBindings.cpp | 49 | ||||
-rw-r--r-- | src/Bindings/PluginLua.h | 28 |
6 files changed, 392 insertions, 17 deletions
diff --git a/src/Bindings/LuaChunkStay.cpp b/src/Bindings/LuaChunkStay.cpp new file mode 100644 index 000000000..0e982637f --- /dev/null +++ b/src/Bindings/LuaChunkStay.cpp @@ -0,0 +1,173 @@ + +// LuaChunkStay.cpp + +// Implements the cLuaChunkStay class representing a cChunkStay binding for plugins, used by cWorld:ChunkStay() Lua API + +#include "Globals.h" +#include "LuaChunkStay.h" +#include "PluginLua.h" +#include "../World.h" + + + + + +cLuaChunkStay::cLuaChunkStay(cPluginLua & a_Plugin) : + m_Plugin(a_Plugin), + m_LuaState(NULL) +{ +} + + + + + +bool cLuaChunkStay::AddChunks(int a_ChunkCoordTableStackPos) +{ + // This function is expected to be called just once, with all the coords in a table + ASSERT(m_Chunks.empty()); + + cPluginLua::cOperation Op(m_Plugin); + cLuaState & L = Op(); + + // Check that we got a table: + if (!lua_istable(L, a_ChunkCoordTableStackPos)) + { + LOGWARNING("%s: The parameter is not a table of coords (got %s). Ignoring the call.", + __FUNCTION__, lua_typename(L, lua_type(L, a_ChunkCoordTableStackPos)) + ); + L.LogStackTrace(); + return false; + } + + // Add each set of coords: + int NumChunks = luaL_getn(L, a_ChunkCoordTableStackPos); + m_Chunks.reserve(NumChunks); + for (int idx = 1; idx <= NumChunks; idx++) + { + // Push the idx-th element of the array onto stack top, check that it's a table: + lua_rawgeti(L, a_ChunkCoordTableStackPos, idx); + if (!lua_istable(L, -1)) + { + LOGWARNING("%s: Element #%d is not a table (got %s). Ignoring the element.", + __FUNCTION__, idx, lua_typename(L, -1) + ); + L.LogStackTrace(); + lua_pop(L, 1); + continue; + } + AddChunkCoord(L, idx); + lua_pop(L, 1); + } + + // If there are no chunks, log a warning and return failure: + if (m_Chunks.empty()) + { + LOGWARNING("%s: Zero chunks to stay.", __FUNCTION__); + L.LogStackTrace(); + return false; + } + + // All ok + return true; +} + + + + + +void cLuaChunkStay::AddChunkCoord(cLuaState & L, int a_Index) +{ + // Check that the element has 2 coords: + int NumCoords = luaL_getn(L, -1); + if (NumCoords != 2) + { + LOGWARNING("%s: Element #%d doesn't contain 2 coords (got %d). Ignoring the element.", + __FUNCTION__, a_Index, NumCoords + ); + return; + } + + // Read the two coords from the element: + lua_rawgeti(L, -1, 1); + lua_rawgeti(L, -2, 2); + int ChunkX = luaL_checkint(L, -2); + int ChunkZ = luaL_checkint(L, -1); + lua_pop(L, 2); + + // Check that a coord is not yet present: + for (cChunkCoordsVector::iterator itr = m_Chunks.begin(), end = m_Chunks.end(); itr != end; ++itr) + { + if ((itr->m_ChunkX == ChunkX) && (itr->m_ChunkZ == ChunkZ)) + { + LOGWARNING("%s: Element #%d is a duplicate, ignoring it.", + __FUNCTION__, a_Index + ); + return; + } + } // for itr - m_Chunks[] + + m_Chunks.push_back(cChunkCoords(ChunkX, ZERO_CHUNK_Y, ChunkZ)); +} + + + + + +void cLuaChunkStay::Enable(cChunkMap & a_ChunkMap, int a_OnChunkAvailableStackPos, int a_OnAllChunksAvailableStackPos) +{ + // Get the references to the callback functions: + m_LuaState = &m_Plugin.GetLuaState(); + m_OnChunkAvailable.RefStack(*m_LuaState, a_OnChunkAvailableStackPos); + m_OnAllChunksAvailable.RefStack(*m_LuaState, a_OnAllChunksAvailableStackPos); + + // Enable the ChunkStay: + super::Enable(a_ChunkMap); +} + + + + + +void cLuaChunkStay::OnChunkAvailable(int a_ChunkX, int a_ChunkZ) +{ + // DEBUG: + LOGD("LuaChunkStay: Chunk [%d, %d] is now available, calling the callback...", a_ChunkX, a_ChunkZ); + + cPluginLua::cOperation Op(m_Plugin); + Op().Call((int)m_OnChunkAvailable, a_ChunkX, a_ChunkZ); +} + + + + + +bool cLuaChunkStay::OnAllChunksAvailable(void) +{ + { + // Call the callback: + cPluginLua::cOperation Op(m_Plugin); + Op().Call((int)m_OnAllChunksAvailable); + + // Remove the callback references - they won't be needed anymore + m_OnChunkAvailable.UnRef(); + m_OnAllChunksAvailable.UnRef(); + } + + // Disable the ChunkStay by returning true + return true; +} + + + + + +void cLuaChunkStay::OnDisabled(void) +{ + // This object is no longer needed, delete it + delete this; +} + + + + diff --git a/src/Bindings/LuaChunkStay.h b/src/Bindings/LuaChunkStay.h new file mode 100644 index 000000000..49ab9a0ad --- /dev/null +++ b/src/Bindings/LuaChunkStay.h @@ -0,0 +1,73 @@ + +// LuaChunkStay.h + +// Declares the cLuaChunkStay class representing a cChunkStay binding for plugins, used by cWorld:ChunkStay() Lua API + + + + + +#pragma once + +#include "LuaState.h" +#include "../ChunkStay.h" + + + + + +// fwd: +class cPluginLua; + + + + + +class cLuaChunkStay + : public cChunkStay +{ + typedef cChunkStay super; + +public: + cLuaChunkStay(cPluginLua & a_Plugin); + + ~cLuaChunkStay() { } + + /** Adds chunks in the specified on-stack Lua table. + Returns true if any chunk added, false (plus log warning) if none. */ + bool AddChunks(int a_ChunkCoordTableStackPos); + + /** Enables the ChunkStay for the specified chunkmap, with the specified Lua callbacks. */ + void Enable(cChunkMap & a_ChunkMap, int a_OnChunkAvailableStackPos, int a_OnAllChunksAvailableStackPos); + +protected: + /** The plugin which has created the ChunkStay, via cWorld:ChunkStay() binding method. */ + cPluginLua & m_Plugin; + + /** The Lua state associated with the callbacks. Only valid when enabled. */ + cLuaState * m_LuaState; + + /** The Lua function to call in OnChunkAvailable. Only valid when enabled. */ + cLuaState::cRef m_OnChunkAvailable; + + /** The Lua function to call in OnAllChunksAvailable. Only valid when enabled. */ + cLuaState::cRef m_OnAllChunksAvailable; + + + // cChunkStay overrides: + virtual void OnChunkAvailable(int a_ChunkX, int a_ChunkZ) override; + virtual bool OnAllChunksAvailable(void) override; + virtual void OnDisabled(void) override; + + /** Adds a single chunk coord from the table at the top of the Lua stack. + Expects the top element to be a table, checks that it contains two numbers. + Uses those two numbers as chunk coords appended to m_Chunks. + If the coords are already present, gives a warning and ignores the pair. + The a_Index parameter is only for the error messages. */ + void AddChunkCoord(cLuaState & a_LuaState, int a_Index); +} ; + + + + + diff --git a/src/Bindings/LuaState.cpp b/src/Bindings/LuaState.cpp index ac5ce47e1..24e64d9b2 100644 --- a/src/Bindings/LuaState.cpp +++ b/src/Bindings/LuaState.cpp @@ -735,17 +735,20 @@ bool cLuaState::CallFunction(int a_NumResults) ASSERT(lua_isfunction(m_LuaState, -m_NumCurrentFunctionArgs - 1)); // The function to call ASSERT(lua_isfunction(m_LuaState, -m_NumCurrentFunctionArgs - 2)); // The error handler - int s = lua_pcall(m_LuaState, m_NumCurrentFunctionArgs, a_NumResults, -m_NumCurrentFunctionArgs - 2); + // Save the current "stack" state and reset, in case the callback calls another function: + AString CurrentFunctionName; + std::swap(m_CurrentFunctionName, CurrentFunctionName); + int NumArgs = m_NumCurrentFunctionArgs; + m_NumCurrentFunctionArgs = -1; + + // Call the function: + int s = lua_pcall(m_LuaState, NumArgs, a_NumResults, -NumArgs - 2); if (s != 0) { // The error has already been printed together with the stacktrace - LOGWARNING("Error in %s calling function %s()", m_SubsystemName.c_str(), m_CurrentFunctionName.c_str()); - m_NumCurrentFunctionArgs = -1; - m_CurrentFunctionName.clear(); + LOGWARNING("Error in %s calling function %s()", m_SubsystemName.c_str(), CurrentFunctionName.c_str()); return false; } - m_NumCurrentFunctionArgs = -1; - m_CurrentFunctionName.clear(); // Remove the error handler from the stack: lua_remove(m_LuaState, -a_NumResults - 1); @@ -1206,7 +1209,7 @@ void cLuaState::LogStack(const char * a_Header) void cLuaState::LogStack(lua_State * a_LuaState, const char * a_Header) { LOGD((a_Header != NULL) ? a_Header : "Lua C API Stack contents:"); - for (int i = lua_gettop(a_LuaState); i >= 0; i--) + for (int i = lua_gettop(a_LuaState); i > 0; i--) { AString Value; int Type = lua_type(a_LuaState, i); @@ -1240,13 +1243,21 @@ int cLuaState::ReportFnCallErrors(lua_State * a_LuaState) /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // cLuaState::cRef: +cLuaState::cRef::cRef(void) : + m_LuaState(NULL), + m_Ref(LUA_REFNIL) +{ +} + + + + + cLuaState::cRef::cRef(cLuaState & a_LuaState, int a_StackPos) : - m_LuaState(a_LuaState) + m_LuaState(NULL), + m_Ref(LUA_REFNIL) { - ASSERT(m_LuaState.IsValid()); - - lua_pushvalue(m_LuaState, a_StackPos); // Push a copy of the value at a_StackPos onto the stack - m_Ref = luaL_ref(m_LuaState, LUA_REGISTRYINDEX); + RefStack(a_LuaState, a_StackPos); } @@ -1255,12 +1266,42 @@ cLuaState::cRef::cRef(cLuaState & a_LuaState, int a_StackPos) : cLuaState::cRef::~cRef() { - ASSERT(m_LuaState.IsValid()); + if (m_LuaState != NULL) + { + UnRef(); + } +} + + + + + +void cLuaState::cRef::RefStack(cLuaState & a_LuaState, int a_StackPos) +{ + ASSERT(a_LuaState.IsValid()); + if (m_LuaState != NULL) + { + 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); +} + + + + + +void cLuaState::cRef::UnRef(void) +{ + ASSERT(m_LuaState->IsValid()); // The reference should be destroyed before destroying the LuaState if (IsValid()) { - luaL_unref(m_LuaState, LUA_REGISTRYINDEX, m_Ref); + luaL_unref(*m_LuaState, LUA_REGISTRYINDEX, m_Ref); } + m_LuaState = NULL; + m_Ref = LUA_REFNIL; } diff --git a/src/Bindings/LuaState.h b/src/Bindings/LuaState.h index dda45bb28..1c9c99e69 100644 --- a/src/Bindings/LuaState.h +++ b/src/Bindings/LuaState.h @@ -65,14 +65,27 @@ class cLuaState { public: - /** Used for storing references to object in the global registry */ + /** Used for storing references to object in the global registry. + Can be bound (contains a reference) or unbound (doesn't contain reference). + The reference can also be reset by calling RefStack(). */ class cRef { public: + /** Creates an unbound reference object. */ + cRef(void); + /** Creates a reference in the specified LuaState for object at the specified StackPos */ cRef(cLuaState & a_LuaState, int a_StackPos); + ~cRef(); + /** Creates a reference to Lua object at the specified stack pos, binds this object to it. + Calls UnRef() first if previously bound to another reference. */ + void RefStack(cLuaState & a_LuaState, int a_StackPos); + + /** Removes the bound reference, resets the object to Unbound state. */ + void UnRef(void); + /** Returns true if the reference is valid */ bool IsValid(void) const {return (m_Ref != LUA_REFNIL); } @@ -80,7 +93,7 @@ public: operator int(void) const { return m_Ref; } protected: - cLuaState & m_LuaState; + cLuaState * m_LuaState; int m_Ref; } ; diff --git a/src/Bindings/ManualBindings.cpp b/src/Bindings/ManualBindings.cpp index 841ec5cf2..f0bad92e8 100644 --- a/src/Bindings/ManualBindings.cpp +++ b/src/Bindings/ManualBindings.cpp @@ -8,6 +8,7 @@ #include "PluginLua.h" #include "PluginManager.h" #include "LuaWindow.h" +#include "LuaChunkStay.h" #include "../Root.h" #include "../World.h" #include "../Entities/Player.h" @@ -1638,6 +1639,53 @@ static int tolua_cPluginManager_CallPlugin(lua_State * tolua_S) +static int tolua_cWorld_ChunkStay(lua_State * tolua_S) +{ + /* Function signature: + World:ChunkStay(ChunkCoordTable, OnChunkAvailable, OnAllChunksAvailable) + ChunkCoordTable == { {Chunk1x, Chunk1z}, {Chunk2x, Chunk2z}, ... } + */ + + cLuaState L(tolua_S); + if ( + !L.CheckParamUserType(1, "cWorld") || + !L.CheckParamTable (2) || + !L.CheckParamFunction(3, 4) + ) + { + return 0; + } + + cPluginLua * Plugin = GetLuaPlugin(tolua_S); + if (Plugin == NULL) + { + return 0; + } + cLuaChunkStay * ChunkStay = new cLuaChunkStay(*Plugin); + + // Read the params: + cWorld * World = (cWorld *)tolua_tousertype(tolua_S, 1, NULL); + if (World == NULL) + { + LOGWARNING("World:ChunkStay(): invalid world parameter"); + L.LogStackTrace(); + return 0; + } + L.LogStack("Before AddChunks()"); + if (!ChunkStay->AddChunks(2)) + { + return 0; + } + L.LogStack("After params read"); + + ChunkStay->Enable(*World->GetChunkMap(), 3, 4); + return 0; +} + + + + + static int tolua_cPlayer_GetGroups(lua_State* tolua_S) { cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); @@ -2360,6 +2408,7 @@ void ManualBindings::Bind(lua_State * tolua_S) tolua_endmodule(tolua_S); tolua_beginmodule(tolua_S, "cWorld"); + tolua_function(tolua_S, "ChunkStay", tolua_cWorld_ChunkStay); tolua_function(tolua_S, "DoWithBlockEntityAt", tolua_DoWithXYZ<cWorld, cBlockEntity, &cWorld::DoWithBlockEntityAt>); tolua_function(tolua_S, "DoWithChestAt", tolua_DoWithXYZ<cWorld, cChestEntity, &cWorld::DoWithChestAt>); tolua_function(tolua_S, "DoWithDispenserAt", tolua_DoWithXYZ<cWorld, cDispenserEntity, &cWorld::DoWithDispenserAt>); diff --git a/src/Bindings/PluginLua.h b/src/Bindings/PluginLua.h index ad0cfbe5a..a177f5288 100644 --- a/src/Bindings/PluginLua.h +++ b/src/Bindings/PluginLua.h @@ -35,7 +35,33 @@ class cPluginLua : public: // tolua_end - cPluginLua( const AString & a_PluginDirectory ); + /** A RAII-style mutex lock for accessing the internal LuaState. + This will be the only way to retrieve the plugin's LuaState; + therefore it directly supports accessing the LuaState of the locked plugin. + Usage: + cPluginLua::cOperation Op(SomePlugin); + Op().Call(...) // Call a function in the plugin's LuaState + */ + class cOperation + { + public: + cOperation(cPluginLua & a_Plugin) : + m_Plugin(a_Plugin), + m_Lock(a_Plugin.m_CriticalSection) + { + } + + cLuaState & operator ()(void) { return m_Plugin.m_LuaState; } + + protected: + cPluginLua & m_Plugin; + + /** RAII lock for m_Plugin.m_CriticalSection */ + cCSLock m_Lock; + } ; + + + cPluginLua(const AString & a_PluginDirectory); ~cPluginLua(); virtual void OnDisable(void) override; |