diff options
author | Mattes D <github@xoft.cz> | 2016-06-30 10:42:58 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2016-06-30 10:42:58 +0200 |
commit | 9af664dc739405726dd52f4fe7dce7b42f5ce18e (patch) | |
tree | 59bae2de637b5f3c629db8e55fb44a3106266709 /src | |
parent | Self tests (#3242) (diff) | |
parent | Converted cLuaState::cCallbackPtr into a UniquePtr. (diff) | |
download | cuberite-9af664dc739405726dd52f4fe7dce7b42f5ce18e.tar cuberite-9af664dc739405726dd52f4fe7dce7b42f5ce18e.tar.gz cuberite-9af664dc739405726dd52f4fe7dce7b42f5ce18e.tar.bz2 cuberite-9af664dc739405726dd52f4fe7dce7b42f5ce18e.tar.lz cuberite-9af664dc739405726dd52f4fe7dce7b42f5ce18e.tar.xz cuberite-9af664dc739405726dd52f4fe7dce7b42f5ce18e.tar.zst cuberite-9af664dc739405726dd52f4fe7dce7b42f5ce18e.zip |
Diffstat (limited to '')
-rw-r--r-- | src/Bindings/AllToLua.pkg | 1 | ||||
-rw-r--r-- | src/Bindings/CMakeLists.txt | 3 | ||||
-rw-r--r-- | src/Bindings/LuaChunkStay.cpp | 22 | ||||
-rw-r--r-- | src/Bindings/LuaChunkStay.h | 6 | ||||
-rw-r--r-- | src/Bindings/LuaNameLookup.cpp | 2 | ||||
-rw-r--r-- | src/Bindings/LuaServerHandle.cpp | 2 | ||||
-rw-r--r-- | src/Bindings/LuaState.cpp | 273 | ||||
-rw-r--r-- | src/Bindings/LuaState.h | 164 | ||||
-rw-r--r-- | src/Bindings/LuaTCPLink.cpp | 2 | ||||
-rw-r--r-- | src/Bindings/LuaUDPEndpoint.cpp | 2 | ||||
-rw-r--r-- | src/Bindings/LuaWindow.cpp | 95 | ||||
-rw-r--r-- | src/Bindings/LuaWindow.h | 72 | ||||
-rw-r--r-- | src/Bindings/ManualBindings.cpp | 559 | ||||
-rw-r--r-- | src/Bindings/ManualBindings_World.cpp | 109 | ||||
-rw-r--r-- | src/Bindings/Plugin.h | 15 | ||||
-rw-r--r-- | src/Bindings/PluginLua.cpp | 1392 | ||||
-rw-r--r-- | src/Bindings/PluginLua.h | 132 | ||||
-rw-r--r-- | src/Bindings/PluginManager.cpp | 64 | ||||
-rw-r--r-- | src/Bindings/PluginManager.h | 52 | ||||
-rw-r--r-- | src/Bindings/WebPlugin.cpp | 152 | ||||
-rw-r--r-- | src/Bindings/WebPlugin.h | 80 | ||||
-rw-r--r-- | src/Entities/Player.h | 6 | ||||
-rw-r--r-- | src/Server.cpp | 35 | ||||
-rw-r--r-- | src/UI/Window.cpp | 2 | ||||
-rw-r--r-- | src/WebAdmin.cpp | 415 | ||||
-rw-r--r-- | src/WebAdmin.h | 158 |
26 files changed, 1559 insertions, 2256 deletions
diff --git a/src/Bindings/AllToLua.pkg b/src/Bindings/AllToLua.pkg index 991ed0ddd..6ca9c8658 100644 --- a/src/Bindings/AllToLua.pkg +++ b/src/Bindings/AllToLua.pkg @@ -38,7 +38,6 @@ $cfile "LuaFunctions.h" $cfile "PluginManager.h" $cfile "Plugin.h" $cfile "PluginLua.h" -$cfile "WebPlugin.h" $cfile "LuaWindow.h" $cfile "../BlockID.h" diff --git a/src/Bindings/CMakeLists.txt b/src/Bindings/CMakeLists.txt index 10cda1efb..4f25f2cf4 100644 --- a/src/Bindings/CMakeLists.txt +++ b/src/Bindings/CMakeLists.txt @@ -23,7 +23,6 @@ SET (SRCS Plugin.cpp PluginLua.cpp PluginManager.cpp - WebPlugin.cpp ) SET (HDRS @@ -44,7 +43,6 @@ SET (HDRS Plugin.h PluginLua.h PluginManager.h - WebPlugin.h tolua++.h ) @@ -66,7 +64,6 @@ set(BINDING_DEPENDENCIES ../Bindings/Plugin.h ../Bindings/PluginLua.h ../Bindings/PluginManager.h - ../Bindings/WebPlugin.h ../BiomeDef.h ../BlockArea.h ../BlockEntities/BeaconEntity.h diff --git a/src/Bindings/LuaChunkStay.cpp b/src/Bindings/LuaChunkStay.cpp index d8d7da5d0..145a9ee1b 100644 --- a/src/Bindings/LuaChunkStay.cpp +++ b/src/Bindings/LuaChunkStay.cpp @@ -113,12 +113,10 @@ void cLuaChunkStay::AddChunkCoord(cLuaState & L, int a_Index) -void cLuaChunkStay::Enable(cChunkMap & a_ChunkMap, int a_OnChunkAvailableStackPos, int a_OnAllChunksAvailableStackPos) +void cLuaChunkStay::Enable(cChunkMap & a_ChunkMap, cLuaState::cCallbackPtr a_OnChunkAvailable, cLuaState::cCallbackPtr a_OnAllChunksAvailable) { - // 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); + m_OnChunkAvailable = std::move(a_OnChunkAvailable); + m_OnAllChunksAvailable = std::move(a_OnAllChunksAvailable); // Enable the ChunkStay: super::Enable(a_ChunkMap); @@ -130,10 +128,9 @@ void cLuaChunkStay::Enable(cChunkMap & a_ChunkMap, int a_OnChunkAvailableStackPo void cLuaChunkStay::OnChunkAvailable(int a_ChunkX, int a_ChunkZ) { - if (m_OnChunkAvailable.IsValid()) + if (m_OnChunkAvailable != nullptr) { - cPluginLua::cOperation Op(m_Plugin); - Op().Call(static_cast<int>(m_OnChunkAvailable), a_ChunkX, a_ChunkZ); + m_OnChunkAvailable->Call(a_ChunkX, a_ChunkZ); } } @@ -143,15 +140,14 @@ void cLuaChunkStay::OnChunkAvailable(int a_ChunkX, int a_ChunkZ) bool cLuaChunkStay::OnAllChunksAvailable(void) { - if (m_OnAllChunksAvailable.IsValid()) + if (m_OnAllChunksAvailable != nullptr) { // Call the callback: - cPluginLua::cOperation Op(m_Plugin); - Op().Call(static_cast<int>(m_OnAllChunksAvailable)); + m_OnAllChunksAvailable->Call(); // Remove the callback references - they won't be needed anymore - m_OnChunkAvailable.UnRef(); - m_OnAllChunksAvailable.UnRef(); + m_OnChunkAvailable.reset(); + m_OnAllChunksAvailable.reset(); } // Disable the ChunkStay by returning true diff --git a/src/Bindings/LuaChunkStay.h b/src/Bindings/LuaChunkStay.h index a455d2502..51356d5b7 100644 --- a/src/Bindings/LuaChunkStay.h +++ b/src/Bindings/LuaChunkStay.h @@ -39,7 +39,7 @@ public: 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); + void Enable(cChunkMap & a_ChunkMap, cLuaState::cCallbackPtr a_OnChunkAvailable, cLuaState::cCallbackPtr a_OnAllChunksAvailable); protected: /** The plugin which has created the ChunkStay, via cWorld:ChunkStay() binding method. */ @@ -49,10 +49,10 @@ protected: cLuaState * m_LuaState; /** The Lua function to call in OnChunkAvailable. Only valid when enabled. */ - cLuaState::cRef m_OnChunkAvailable; + cLuaState::cCallbackPtr m_OnChunkAvailable; /** The Lua function to call in OnAllChunksAvailable. Only valid when enabled. */ - cLuaState::cRef m_OnAllChunksAvailable; + cLuaState::cCallbackPtr m_OnAllChunksAvailable; // cChunkStay overrides: diff --git a/src/Bindings/LuaNameLookup.cpp b/src/Bindings/LuaNameLookup.cpp index e52d8dbdc..3cbdbb5cf 100644 --- a/src/Bindings/LuaNameLookup.cpp +++ b/src/Bindings/LuaNameLookup.cpp @@ -12,7 +12,7 @@ cLuaNameLookup::cLuaNameLookup(const AString & a_Query, cPluginLua & a_Plugin, int a_CallbacksTableStackPos): m_Plugin(a_Plugin), - m_Callbacks(a_Plugin.GetLuaState(), a_CallbacksTableStackPos), + m_Callbacks(cPluginLua::cOperation(a_Plugin)(), a_CallbacksTableStackPos), m_Query(a_Query) { } diff --git a/src/Bindings/LuaServerHandle.cpp b/src/Bindings/LuaServerHandle.cpp index a84f894b5..9cc8ad350 100644 --- a/src/Bindings/LuaServerHandle.cpp +++ b/src/Bindings/LuaServerHandle.cpp @@ -14,7 +14,7 @@ cLuaServerHandle::cLuaServerHandle(UInt16 a_Port, cPluginLua & a_Plugin, int a_CallbacksTableStackPos): m_Plugin(a_Plugin), - m_Callbacks(a_Plugin.GetLuaState(), a_CallbacksTableStackPos), + m_Callbacks(cPluginLua::cOperation(a_Plugin)(), a_CallbacksTableStackPos), m_Port(a_Port) { } diff --git a/src/Bindings/LuaState.cpp b/src/Bindings/LuaState.cpp index f11e74e75..5e6c24365 100644 --- a/src/Bindings/LuaState.cpp +++ b/src/Bindings/LuaState.cpp @@ -20,6 +20,10 @@ extern "C" #include "../Entities/Entity.h" #include "../BlockEntities/BlockEntity.h" + + + + // fwd: "SQLite/lsqlite3.c" extern "C" { @@ -39,6 +43,10 @@ extern "C" const cLuaState::cRet cLuaState::Return = {}; +/** Each Lua state stores a pointer to its creating cLuaState in Lua globals, under this name. +This way any cLuaState can reference the main cLuaState's TrackedCallbacks, mutex etc. */ +static const char * g_CanonLuaStateGlobalName = "_CuberiteInternal_CanonLuaState"; + @@ -114,6 +122,135 @@ cLuaStateTracker & cLuaStateTracker::Get(void) //////////////////////////////////////////////////////////////////////////////// +// cLuaState::cCallback: + +cLuaState::cCallback::cCallback(void): + m_CS(nullptr) +{ +} + + + + + +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: + auto canonState = a_LuaState.QueryCanonLuaState(); + canonState->TrackCallback(*this); + + // Store the new callback: + m_CS = &(canonState->m_CS); + m_Ref.RefStack(*canonState, a_StackPos); + return true; +} + + + + + +void cLuaState::cCallback::Clear(void) +{ + // Free the callback reference: + lua_State * luaState = nullptr; + { + auto cs = m_CS; + if (cs != nullptr) + { + cCSLock Lock(*cs); + if (!m_Ref.IsValid()) + { + return; + } + luaState = m_Ref.GetLuaState(); + m_Ref.UnRef(); + } + } + + // Remove from LuaState's callback-tracking: + if (luaState == nullptr) + { + return; + } + cLuaState(luaState).UntrackCallback(*this); +} + + + + + +bool cLuaState::cCallback::IsValid(void) +{ + auto cs = m_CS; + if (cs == nullptr) + { + return false; + } + cCSLock lock(*cs); + return m_Ref.IsValid(); +} + + + + + +bool cLuaState::cCallback::IsSameLuaState(cLuaState & a_LuaState) +{ + auto cs = m_CS; + if (cs == nullptr) + { + return false; + } + cCSLock lock(*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)); +} + + + + + +void cLuaState::cCallback::Invalidate(void) +{ + auto cs = m_CS; + if (cs == nullptr) + { + // Already invalid + return; + } + cCSLock Lock(*cs); + if (!m_Ref.IsValid()) + { + LOGD("%s: Inconsistent callback at %p, has a CS but an invalid Ref. This should not happen", + __FUNCTION__, reinterpret_cast<void *>(this) + ); + return; + } + m_Ref.UnRef(); +} + + + + + +//////////////////////////////////////////////////////////////////////////////// // cLuaState: cLuaState::cLuaState(const AString & a_SubsystemName) : @@ -170,6 +307,10 @@ void cLuaState::Create(void) luaL_openlibs(m_LuaState); m_IsOwned = true; cLuaStateTracker::Add(*this); + + // Add the CanonLuaState value into the Lua state, so that we can get it from anywhere: + lua_pushlightuserdata(m_LuaState, reinterpret_cast<void *>(this)); + lua_setglobal(m_LuaState, g_CanonLuaStateGlobalName); } @@ -206,6 +347,16 @@ void cLuaState::Close(void) Detach(); return; } + + // Invalidate all callbacks: + { + cCSLock Lock(m_CSTrackedCallbacks); + for (auto & c: m_TrackedCallbacks) + { + c->Invalidate(); + } + } + cLuaStateTracker::Del(*this); lua_close(m_LuaState); m_LuaState = nullptr; @@ -402,7 +553,7 @@ bool cLuaState::PushFunction(const char * a_FunctionName) -bool cLuaState::PushFunction(int a_FnRef) +bool cLuaState::PushFunction(const cRef & a_FnRef) { ASSERT(IsValid()); ASSERT(m_NumCurrentFunctionArgs == -1); // If not, there's already something pushed onto the stack @@ -410,7 +561,7 @@ bool cLuaState::PushFunction(int a_FnRef) // Push the error handler for lua_pcall() lua_pushcfunction(m_LuaState, &ReportFnCallErrors); - lua_rawgeti(m_LuaState, LUA_REGISTRYINDEX, a_FnRef); // same as lua_getref() + lua_rawgeti(m_LuaState, LUA_REGISTRYINDEX, static_cast<int>(a_FnRef)); // same as lua_getref() if (!lua_isfunction(m_LuaState, -1)) { lua_pop(m_LuaState, 2); @@ -821,6 +972,18 @@ void cLuaState::Push(std::chrono::milliseconds a_Value) +void cLuaState::Pop(int a_NumValuesToPop) +{ + ASSERT(IsValid()); + + lua_pop(m_LuaState, a_NumValuesToPop); + m_NumCurrentFunctionArgs -= a_NumValuesToPop; +} + + + + + bool cLuaState::GetStackValue(int a_StackPos, AString & a_Value) { size_t len = 0; @@ -847,6 +1010,41 @@ bool cLuaState::GetStackValue(int a_StackPos, bool & a_ReturnedVal) +bool cLuaState::GetStackValue(int a_StackPos, cCallback & a_Callback) +{ + return a_Callback.RefStack(*this, a_StackPos); +} + + + + + +bool cLuaState::GetStackValue(int a_StackPos, cCallbackPtr & a_Callback) +{ + if (a_Callback == nullptr) + { + a_Callback = cpp14::make_unique<cCallback>(); + } + return a_Callback->RefStack(*this, a_StackPos); +} + + + + + +bool cLuaState::GetStackValue(int a_StackPos, cCallbackSharedPtr & a_Callback) +{ + if (a_Callback == nullptr) + { + a_Callback = std::make_shared<cCallback>(); + } + return a_Callback->RefStack(*this, a_StackPos); +} + + + + + bool cLuaState::GetStackValue(int a_StackPos, cPluginManager::CommandResult & a_Result) { if (lua_isnumber(m_LuaState, a_StackPos)) @@ -1632,16 +1830,16 @@ void cLuaState::ToString(int a_StackPos, AString & a_String) -void cLuaState::LogStack(const char * a_Header) +void cLuaState::LogStackValues(const char * a_Header) { - LogStack(m_LuaState, a_Header); + LogStackValues(m_LuaState, a_Header); } -void cLuaState::LogStack(lua_State * a_LuaState, const char * a_Header) +void cLuaState::LogStackValues(lua_State * a_LuaState, const char * a_Header) { // Format string consisting only of %s is used to appease the compiler LOG("%s", (a_Header != nullptr) ? a_Header : "Lua C API Stack contents:"); @@ -1667,6 +1865,21 @@ void cLuaState::LogStack(lua_State * a_LuaState, const char * a_Header) +cLuaState * cLuaState::QueryCanonLuaState(void) +{ + // Get the CanonLuaState global from Lua: + auto cb = WalkToNamedGlobal(g_CanonLuaStateGlobalName); + if (!cb.IsValid()) + { + return nullptr; + } + return reinterpret_cast<cLuaState *>(lua_touserdata(m_LuaState, -1)); +} + + + + + int cLuaState::ReportFnCallErrors(lua_State * a_LuaState) { LOGWARNING("LUA: %s", lua_tostring(a_LuaState, -1)); @@ -1701,6 +1914,50 @@ int cLuaState::BreakIntoDebugger(lua_State * a_LuaState) +void cLuaState::TrackCallback(cCallback & a_Callback) +{ + // Get the CanonLuaState global from Lua: + auto canonState = QueryCanonLuaState(); + if (canonState == nullptr) + { + LOGWARNING("%s: Lua state %p has invalid CanonLuaState!", __FUNCTION__, reinterpret_cast<void *>(m_LuaState)); + return; + } + + // Add the callback: + cCSLock Lock(canonState->m_CSTrackedCallbacks); + canonState->m_TrackedCallbacks.push_back(&a_Callback); +} + + + + + +void cLuaState::UntrackCallback(cCallback & a_Callback) +{ + // Get the CanonLuaState global from Lua: + auto canonState = QueryCanonLuaState(); + if (canonState == nullptr) + { + LOGWARNING("%s: Lua state %p has invalid CanonLuaState!", __FUNCTION__, reinterpret_cast<void *>(m_LuaState)); + return; + } + + // Remove the callback: + cCSLock Lock(canonState->m_CSTrackedCallbacks); + auto & trackedCallbacks = canonState->m_TrackedCallbacks; + trackedCallbacks.erase(std::remove_if(trackedCallbacks.begin(), trackedCallbacks.end(), + [&a_Callback](cCallback * a_StoredCallback) + { + return (a_StoredCallback == &a_Callback); + } + )); +} + + + + + //////////////////////////////////////////////////////////////////////////////// // cLuaState::cRef: @@ -1756,7 +2013,7 @@ void cLuaState::cRef::RefStack(cLuaState & a_LuaState, int a_StackPos) { UnRef(); } - m_LuaState = &a_LuaState; + 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); } @@ -1767,11 +2024,9 @@ void cLuaState::cRef::RefStack(cLuaState & a_LuaState, int a_StackPos) 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 = nullptr; m_Ref = LUA_REFNIL; diff --git a/src/Bindings/LuaState.h b/src/Bindings/LuaState.h index b795a80d4..106d8a783 100644 --- a/src/Bindings/LuaState.h +++ b/src/Bindings/LuaState.h @@ -7,7 +7,7 @@ The contained lua_State can be either owned or attached. Owned lua_State is created by calling Create() and the cLuaState automatically closes the state Or, lua_State can be attached by calling Attach(), the cLuaState doesn't close such a state -Attaching a state will automatically close an owned state. +If owning a state, trying to attach a state will automatically close the previously owned state. Calling a Lua function is done internally by pushing the function using PushFunction(), then pushing the arguments and finally executing CallFunction(). cLuaState automatically keeps track of the number of @@ -16,8 +16,12 @@ the stack using GetStackValue(). All of this is wrapped in a templated function which is generated automatically by gen_LuaState_Call.lua script file into the LuaState_Call.inc file. Reference management is provided by the cLuaState::cRef class. This is used when you need to hold a reference to -any Lua object across several function calls; usually this is used for callbacks. The class is RAII-like, with -automatic resource management. +any Lua object across several function calls. The class is RAII-like, with automatic resource management. + +Callbacks management is provided by the cLuaState::cCallback class. Use a GetStackValue() with cCallbackPtr +parameter to store the callback, and then at any time you can use the cCallback's Call() templated function +to call the callback. The callback management takes care of cLuaState being destroyed - the cCallback object +stays valid but doesn't call into Lua code anymore, returning false for "failure" instead. */ @@ -39,6 +43,7 @@ extern "C" class cLuaServerHandle; class cLuaTCPLink; class cLuaUDPEndpoint; +class cPluginLua; @@ -49,6 +54,20 @@ class cLuaState { public: + /** Provides a RAII-style locking for the LuaState. + Used mainly by the cPluginLua internals to provide the actual locking for interface operations, such as callbacks. */ + class cLock + { + public: + cLock(cLuaState & a_LuaState): + m_Lock(a_LuaState.m_CS) + { + } + protected: + cCSLock m_Lock; + }; + + /** 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(). */ @@ -80,8 +99,20 @@ public: /** Allows to use this class wherever an int (i. e. ref) is to be used */ explicit operator int(void) const { return m_Ref; } + /** Returns the Lua state associated with the value. */ + lua_State * GetLuaState(void) { return m_LuaState; } + + /** Creates a Lua reference to the specified object instance in the specified Lua state. + This is useful to make anti-GC references for objects that were created by Lua and need to stay alive longer than Lua GC would normally guarantee. */ + template <typename T> void CreateFromObject(cLuaState & a_LuaState, T && a_Object) + { + a_LuaState.Push(std::forward<T>(a_Object)); + RefStack(a_LuaState, -1); + a_LuaState.Pop(); + } + protected: - cLuaState * m_LuaState; + lua_State * m_LuaState; int m_Ref; // Remove the copy-constructor: @@ -112,6 +143,83 @@ public: } ; + /** Represents a callback to Lua that C++ code can call. + Is thread-safe and unload-safe. + When the Lua state is unloaded, the callback returns an error instead of calling into non-existent code. + To receive the callback instance from the Lua side, use RefStack() or (better) cLuaState::GetStackValue() + with a cCallbackPtr. Note that instances of this class are tracked in the canon LuaState instance, so that + they can be invalidated when the LuaState is unloaded; due to multithreading issues they can only be tracked + by-ptr, which has an unfortunate effect of disabling the copy and move constructors. */ + class cCallback + { + public: + /** Creates an unbound callback instance. */ + cCallback(void); + + ~cCallback() + { + Clear(); + } + + /** Calls the Lua callback, if still available. + Returns true if callback has been called. + Returns false if the Lua state isn't valid anymore. */ + template <typename... Args> + bool Call(Args &&... args) + { + auto cs = m_CS; + if (cs == nullptr) + { + return false; + } + cCSLock Lock(*cs); + if (!m_Ref.IsValid()) + { + return false; + } + return cLuaState(m_Ref.GetLuaState()).Call(m_Ref, std::forward<Args>(args)...); + } + + /** Set the contained callback to the function in the specified Lua state's stack position. + If a callback has been previously contained, it is freed first. */ + bool RefStack(cLuaState & a_LuaState, int a_StackPos); + + /** Frees the contained callback, if any. */ + void Clear(void); + + /** Returns true if the contained callback is valid. */ + bool IsValid(void); + + /** Returns true if the callback resides in the specified Lua state. + Internally, compares the callback's canon Lua state. */ + bool IsSameLuaState(cLuaState & a_LuaState); + + protected: + friend class cLuaState; + + /** The mutex protecting m_Ref against multithreaded access */ + cCriticalSection * m_CS; + + /** Reference to the Lua callback */ + cRef m_Ref; + + + /** Invalidates the callback, without untracking it from the cLuaState. + Called only from cLuaState when closing the Lua state. */ + void Invalidate(void); + + /** This class cannot be copied, because it is tracked in the LuaState by-ptr. + Use cCallbackPtr for a copyable object. */ + cCallback(const cCallback &) = delete; + + /** This class cannot be moved, because it is tracked in the LuaState by-ptr. + Use cCallbackPtr for a copyable object. */ + cCallback(cCallback &&) = delete; + }; + typedef UniquePtr<cCallback> cCallbackPtr; + typedef SharedPtr<cCallback> cCallbackSharedPtr; + + /** A dummy class that's used only to delimit function args from return values for cLuaState::Call() */ class cRet { @@ -168,6 +276,8 @@ public: protected: lua_State * m_LuaState; + /** Used for debugging, Lua state's stack size is checked against this number to make sure + it is the same as when the value was pushed. */ int m_StackLen; // Remove the copy-constructor: @@ -261,11 +371,17 @@ public: void Push(const UInt32 a_Value); void Push(std::chrono::milliseconds a_time); + /** Pops the specified number of values off the top of the Lua stack. */ + void Pop(int a_NumValuesToPop = 1); + // 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 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, cCallback & a_Callback); + bool GetStackValue(int a_StackPos, cCallbackPtr & a_Callback); + bool GetStackValue(int a_StackPos, cCallbackSharedPtr & a_Callback); 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); @@ -440,21 +556,26 @@ public: void ToString(int a_StackPos, AString & a_String); /** Logs all the elements' types on the API stack, with an optional header for the listing. */ - void LogStack(const char * a_Header = nullptr); + void LogStackValues(const char * a_Header = nullptr); /** Logs all the elements' types on the API stack, with an optional header for the listing. */ - static void LogStack(lua_State * a_LuaState, const char * a_Header = nullptr); + static void LogStackValues(lua_State * a_LuaState, const char * a_Header = nullptr); + + /** Returns the canon Lua state (the primary cLuaState instance that was used to create, rather than attach, the lua_State structure). + Returns nullptr if the canon Lua state cannot be queried. */ + cLuaState * QueryCanonLuaState(void); protected: + cCriticalSection m_CS; + lua_State * m_LuaState; /** If true, the state is owned by this object and will be auto-Closed. False => attached state */ bool m_IsOwned; /** The subsystem name is used for reporting errors to the console, it is either "plugin %s" or "LuaScript" - whatever is given to the constructor - */ + whatever is given to the constructor. */ AString m_SubsystemName; /** Name of the currently pushed function (for the Push / Call chain) */ @@ -463,6 +584,15 @@ protected: /** Number of arguments currently pushed (for the Push / Call chain) */ int m_NumCurrentFunctionArgs; + /** The tracked callbacks. + This object will invalidate all of these when it is about to be closed. + Protected against multithreaded access by m_CSTrackedCallbacks. */ + std::vector<cCallback *> m_TrackedCallbacks; + + /** Protects m_TrackedTallbacks against multithreaded access. */ + cCriticalSection m_CSTrackedCallbacks; + + /** Variadic template terminator: If there's nothing more to push / pop, just call the function. Note that there are no return values either, because those are prefixed by a cRet value, so the arg list is never empty. */ bool PushCallPop(void) @@ -512,18 +642,10 @@ protected: */ bool PushFunction(const char * a_FunctionName); - /** Pushes a function that has been saved into the global registry, identified by a_FnRef. - Returns true if successful. Logs a warning on failure - */ - bool PushFunction(int a_FnRef); - /** Pushes a function that has been saved as a reference. Returns true if successful. Logs a warning on failure */ - bool PushFunction(const cRef & a_FnRef) - { - return PushFunction(static_cast<int>(a_FnRef)); - } + bool PushFunction(const cRef & a_FnRef); /** Pushes a function that is stored in a referenced table by name Returns true if successful. Logs a warning on failure @@ -545,6 +667,14 @@ protected: /** Tries to break into the MobDebug debugger, if it is installed. */ static int BreakIntoDebugger(lua_State * a_LuaState); + + /** Adds the specified callback to tracking. + The callback will be invalidated when this Lua state is about to be closed. */ + void TrackCallback(cCallback & a_Callback); + + /** Removes the specified callback from tracking. + The callback will no longer be invalidated when this Lua state is about to be closed. */ + void UntrackCallback(cCallback & a_Callback); } ; diff --git a/src/Bindings/LuaTCPLink.cpp b/src/Bindings/LuaTCPLink.cpp index deaba9d86..4b04a1c02 100644 --- a/src/Bindings/LuaTCPLink.cpp +++ b/src/Bindings/LuaTCPLink.cpp @@ -13,7 +13,7 @@ cLuaTCPLink::cLuaTCPLink(cPluginLua & a_Plugin, int a_CallbacksTableStackPos): m_Plugin(a_Plugin), - m_Callbacks(a_Plugin.GetLuaState(), a_CallbacksTableStackPos) + m_Callbacks(cPluginLua::cOperation(a_Plugin)(), a_CallbacksTableStackPos) { // Warn if the callbacks aren't valid: if (!m_Callbacks.IsValid()) diff --git a/src/Bindings/LuaUDPEndpoint.cpp b/src/Bindings/LuaUDPEndpoint.cpp index 8637eeb4e..ed8f4e87f 100644 --- a/src/Bindings/LuaUDPEndpoint.cpp +++ b/src/Bindings/LuaUDPEndpoint.cpp @@ -12,7 +12,7 @@ cLuaUDPEndpoint::cLuaUDPEndpoint(cPluginLua & a_Plugin, int a_CallbacksTableStackPos): m_Plugin(a_Plugin), - m_Callbacks(a_Plugin.GetLuaState(), a_CallbacksTableStackPos) + m_Callbacks(cPluginLua::cOperation(a_Plugin)(), a_CallbacksTableStackPos) { // Warn if the callbacks aren't valid: if (!m_Callbacks.IsValid()) diff --git a/src/Bindings/LuaWindow.cpp b/src/Bindings/LuaWindow.cpp index 706397a27..6fffa7aac 100644 --- a/src/Bindings/LuaWindow.cpp +++ b/src/Bindings/LuaWindow.cpp @@ -15,14 +15,13 @@ //////////////////////////////////////////////////////////////////////////////// // cLuaWindow: -cLuaWindow::cLuaWindow(cWindow::WindowType a_WindowType, int a_SlotsX, int a_SlotsY, const AString & a_Title) : - super(a_WindowType, a_Title), +cLuaWindow::cLuaWindow(cLuaState & a_LuaState, cWindow::WindowType a_WindowType, int a_SlotsX, int a_SlotsY, const AString & a_Title) : + Super(a_WindowType, a_Title), m_Contents(a_SlotsX, a_SlotsY), - m_Plugin(nullptr), - m_LuaRef(LUA_REFNIL), - m_OnClosingFnRef(LUA_REFNIL), - m_OnSlotChangedFnRef(LUA_REFNIL) + m_LuaState(a_LuaState.QueryCanonLuaState()) { + ASSERT(m_LuaState != nullptr); // We must have a valid Lua state; this assert fails only if there was no Canon Lua state + m_Contents.AddListener(*this); m_SlotAreas.push_back(new cSlotAreaItemGrid(m_Contents, *this)); @@ -67,62 +66,42 @@ cLuaWindow::~cLuaWindow() -void cLuaWindow::SetLuaRef(cPluginLua * a_Plugin, int a_LuaRef) +void cLuaWindow::SetOnClosing(cLuaState::cCallbackPtr && a_OnClosing) { - // Either m_Plugin is not set or equal to the passed plugin; only one plugin can use one cLuaWindow object - ASSERT((m_Plugin == nullptr) || (m_Plugin == a_Plugin)); - ASSERT(m_LuaRef == LUA_REFNIL); - m_Plugin = a_Plugin; - m_LuaRef = a_LuaRef; -} - - - + // Only one Lua state can be a cLuaWindow object callback: + ASSERT(a_OnClosing->IsSameLuaState(*m_LuaState)); - -bool cLuaWindow::IsLuaReferenced(void) const -{ - return ((m_Plugin != nullptr) && (m_LuaRef != LUA_REFNIL)); + // Store the new reference, releasing the old one if appropriate: + m_OnClosing = std::move(a_OnClosing); } -void cLuaWindow::SetOnClosing(cPluginLua * a_Plugin, int a_FnRef) +void cLuaWindow::SetOnSlotChanged(cLuaState::cCallbackPtr && a_OnSlotChanged) { - // Either m_Plugin is not set or equal to the passed plugin; only one plugin can use one cLuaWindow object - ASSERT((m_Plugin == nullptr) || (m_Plugin == a_Plugin)); + // Only one Lua state can be a cLuaWindow object callback: + ASSERT(a_OnSlotChanged->IsSameLuaState(*m_LuaState)); - // If there already was a function, unreference it first - if (m_OnClosingFnRef != LUA_REFNIL) - { - m_Plugin->Unreference(m_OnClosingFnRef); - } - - // Store the new reference - m_Plugin = a_Plugin; - m_OnClosingFnRef = a_FnRef; + // Store the new reference, releasing the old one if appropriate: + m_OnSlotChanged = std::move(a_OnSlotChanged); } -void cLuaWindow::SetOnSlotChanged(cPluginLua * a_Plugin, int a_FnRef) +void cLuaWindow::OpenedByPlayer(cPlayer & a_Player) { - // Either m_Plugin is not set or equal to the passed plugin; only one plugin can use one cLuaWindow object - ASSERT((m_Plugin == nullptr) || (m_Plugin == a_Plugin)); - - // If there already was a function, unreference it first - if (m_OnSlotChangedFnRef != LUA_REFNIL) + // If the first player is opening the window, create a Lua Reference to the window object so that Lua will not GC it until the last player closes the window: + if (m_PlayerCount == 0) { - m_Plugin->Unreference(m_OnSlotChangedFnRef); + m_LuaRef.CreateFromObject(*m_LuaState, this); } - // Store the new reference - m_Plugin = a_Plugin; - m_OnSlotChangedFnRef = a_FnRef; + ++m_PlayerCount; + Super::OpenedByPlayer(a_Player); } @@ -132,17 +111,27 @@ void cLuaWindow::SetOnSlotChanged(cPluginLua * a_Plugin, int a_FnRef) bool cLuaWindow::ClosedByPlayer(cPlayer & a_Player, bool a_CanRefuse) { // First notify the plugin through the registered callback: - if (m_OnClosingFnRef != LUA_REFNIL) + if (m_OnClosing != nullptr) { - ASSERT(m_Plugin != nullptr); - if (m_Plugin->CallbackWindowClosing(m_OnClosingFnRef, *this, a_Player, a_CanRefuse)) + bool res; + if ( + m_OnClosing->Call(this, &a_Player, a_CanRefuse, cLuaState::Return, res) && // The callback succeeded + res // The callback says not to close the window + ) { // The callback disagrees (the higher levels check the CanRefuse flag compliance) return false; } } - return super::ClosedByPlayer(a_Player, a_CanRefuse); + // If the last player has closed the window, release the Lua reference, so that Lua may GC the object: + --m_PlayerCount; + if (m_PlayerCount == 0) + { + m_LuaRef.UnRef(); + } + + return Super::ClosedByPlayer(a_Player, a_CanRefuse); } @@ -151,13 +140,7 @@ bool cLuaWindow::ClosedByPlayer(cPlayer & a_Player, bool a_CanRefuse) void cLuaWindow::Destroy(void) { - super::Destroy(); - - if ((m_LuaRef != LUA_REFNIL) && (m_Plugin != nullptr)) - { - // The object is referenced by Lua, un-reference it - m_Plugin->Unreference(m_LuaRef); - } + Super::Destroy(); // Lua will take care of this object, it will garbage-collect it, so we must not delete it! m_IsDestroyed = false; @@ -178,7 +161,7 @@ void cLuaWindow::DistributeStack(cItem & a_ItemStack, int a_Slot, cPlayer & a_Pl } } - super::DistributeStackToAreas(a_ItemStack, a_Player, Areas, a_ShouldApply, false); + Super::DistributeStackToAreas(a_ItemStack, a_Player, Areas, a_ShouldApply, false); } @@ -194,9 +177,9 @@ void cLuaWindow::OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) } // If an OnSlotChanged callback has been registered, call it: - if (m_OnSlotChangedFnRef != LUA_REFNIL) + if (m_OnSlotChanged != nullptr) { - m_Plugin->CallbackWindowSlotChanged(m_OnSlotChangedFnRef, *this, a_SlotNum); + m_OnSlotChanged->Call(this, a_SlotNum); } } diff --git a/src/Bindings/LuaWindow.h b/src/Bindings/LuaWindow.h index f292a5154..5f987d4c6 100644 --- a/src/Bindings/LuaWindow.h +++ b/src/Bindings/LuaWindow.h @@ -9,6 +9,8 @@ #pragma once +#include <atomic> +#include "LuaState.h" #include "../UI/Window.h" #include "../ItemGrid.h" @@ -16,35 +18,30 @@ -// fwd: PluginLua.h -class cPluginLua; - - - - - /** A window that has been created by a Lua plugin and is handled entirely by that plugin This object needs extra care with its lifetime management: - It is created by Lua, so Lua expects to garbage-collect it later -- normal cWindow objects are deleted in their ClosedByPlayer() function if the last player closes them -To overcome this, this object overloads the Destroy functions, which doesn't let the ClosedByPlayer() -delete the window, but rather leaves it dangling, with only Lua having the reference to it. -Additionally, to forbid Lua from deleting this object while it is used by players, the manual bindings for -cPlayer:OpenWindow check if the window is of this class, and if so, make a global Lua reference for this object. -This reference needs to be unreferenced in the Destroy() function. */ +- Normal cWindow objects are deleted in their ClosedByPlayer() function if the last player closes them + To overcome this, this object overloads the Destroy functions, which doesn't let the ClosedByPlayer() + delete the window, but rather leaves it dangling, with only Lua having the reference to it. +- Lua could GC the window while a player is still using it + The object creates a Lua reference to itself when opened by a player and + removes the reference when the last player closes the window. +*/ // tolua_begin class cLuaWindow : public cWindow // tolua_end , public cItemGrid::cListener - // tolua_begin -{ - typedef cWindow super; +{ // tolua_export + typedef cWindow Super; public: - /** Create a window of the specified type, with a slot grid of a_SlotsX * a_SlotsY size */ - cLuaWindow(cWindow::WindowType a_WindowType, int a_SlotsX, int a_SlotsY, const AString & a_Title); + /** Create a window of the specified type, with a slot grid of a_SlotsX * a_SlotsY size. + Exported in ManualBindings.cpp */ + cLuaWindow(cLuaState & a_LuaState, cWindow::WindowType a_WindowType, int a_SlotsX, int a_SlotsY, const AString & a_Title); + // tolua_begin virtual ~cLuaWindow(); /** Returns the internal representation of the contents that are manipulated by Lua */ @@ -52,36 +49,37 @@ public: // tolua_end - /** Sets the plugin reference and the internal Lua object reference index - used for preventing Lua's GC to collect this class while the window is open. */ - void SetLuaRef(cPluginLua * a_Plugin, int a_LuaRef); - - /** Returns true if SetLuaRef() has been called */ - bool IsLuaReferenced(void) const; - - /** Sets the callback function (Lua reference) to call when the window is about to close */ - void SetOnClosing(cPluginLua * a_Plugin, int a_FnRef); + /** Sets the Lua callback function to call when the window is about to close */ + void SetOnClosing(cLuaState::cCallbackPtr && a_OnClosing); - /** Sets the callback function (Lua reference) to call when a slot is changed */ - void SetOnSlotChanged(cPluginLua * a_Plugin, int a_FnRef); + /** Sets the Lua callback function to call when a slot is changed */ + void SetOnSlotChanged(cLuaState::cCallbackPtr && a_OnSlotChanged); protected: + /** Contents of the non-inventory part */ cItemGrid m_Contents; - /** The plugin that has opened the window and owns the m_LuaRef */ - cPluginLua * m_Plugin; + /** The Lua state that has opened the window and owns the m_LuaRef */ + cLuaState * m_LuaState; + + /** The Lua callback to call when the window is closing for any player */ + cLuaState::cCallbackPtr m_OnClosing; + + /** The Lua callback to call when a slot has changed */ + cLuaState::cCallbackPtr m_OnSlotChanged; - /** The Lua object reference, used for keeping the object alive as long as any player has the window open */ - int m_LuaRef; + /** Number of players that are currently using the window. + Used to manager the m_LuaRef lifetime. */ + std::atomic<int> m_PlayerCount; - /** The Lua reference for the callback to call when the window is closing for any player */ - int m_OnClosingFnRef; + /** Reference to self, to keep Lua from GCing the object while a player is still using it. + Created when the first player opens the window, destroyed when the last player closes the window. */ + cLuaState::cRef m_LuaRef; - /** The Lua reference for the callback to call when a slot has changed */ - int m_OnSlotChangedFnRef; // cWindow overrides: + virtual void OpenedByPlayer(cPlayer & a_Player) override; virtual bool ClosedByPlayer(cPlayer & a_Player, bool a_CanRefuse) override; virtual void Destroy(void) override; virtual void DistributeStack(cItem & a_ItemStack, int a_Slot, cPlayer & a_Player, cSlotArea * a_ClickedArea, bool a_ShouldApply) override; diff --git a/src/Bindings/ManualBindings.cpp b/src/Bindings/ManualBindings.cpp index 9945929d0..1d735ac83 100644 --- a/src/Bindings/ManualBindings.cpp +++ b/src/Bindings/ManualBindings.cpp @@ -43,6 +43,50 @@ +//////////////////////////////////////////////////////////////////////////////// +// LuaCommandHandler: + +/** Defines a bridge between cPluginManager::cCommandHandler and cLuaState::cCallback. */ +class LuaCommandHandler: + public cPluginManager::cCommandHandler +{ +public: + LuaCommandHandler(cLuaState::cCallbackPtr && a_Callback): + m_Callback(std::move(a_Callback)) + { + } + + virtual bool ExecuteCommand( + const AStringVector & a_Split, + cPlayer * a_Player, + const AString & a_Command, + cCommandOutputCallback * a_Output + ) override + { + bool res = false; + AString s; + if (!m_Callback->Call(a_Split, a_Player, a_Command, cLuaState::Return, res, s)) + { + return false; + } + if (res && (a_Output != nullptr) && !s.empty()) + { + a_Output->Out(s); + } + return res; + } + +protected: + cLuaState::cCallbackPtr m_Callback; +}; + + + + + +//////////////////////////////////////////////////////////////////////////////// +// cManualBindings: + // Better error reporting for Lua int cManualBindings::tolua_do_error(lua_State * L, const char * a_pMsg, tolua_Error * a_pToLuaError) { @@ -993,7 +1037,13 @@ static int tolua_cPluginManager_AddHook_FnRef(cPluginManager * a_PluginManager, } // Retrieve and check the hook type - int HookType = static_cast<int>(tolua_tonumber(S, a_ParamIdx, -1)); + int HookType; + if (!S.GetStackValue(a_ParamIdx, HookType)) + { + LOGWARNING("cPluginManager.AddHook(): Cannot read the hook type."); + S.LogStackTrace(); + return 0; + } if (!a_PluginManager->IsValidHookType(HookType)) { LOGWARNING("cPluginManager.AddHook(): Invalid HOOK_TYPE parameter: %d", HookType); @@ -1002,7 +1052,14 @@ static int tolua_cPluginManager_AddHook_FnRef(cPluginManager * a_PluginManager, } // Add the hook to the plugin - if (!Plugin->AddHookRef(HookType, a_ParamIdx + 1)) + cLuaState::cCallbackPtr callback; + if (!S.GetStackValue(a_ParamIdx + 1, callback)) + { + LOGWARNING("cPluginManager.AddHook(): Cannot read the callback parameter"); + S.LogStackTrace(); + return 0; + } + if (!Plugin->AddHookCallback(HookType, std::move(callback))) { LOGWARNING("cPluginManager.AddHook(): Cannot add hook %d, unknown error.", HookType); S.LogStackTrace(); @@ -1059,10 +1116,11 @@ static int tolua_cPluginManager_AddHook_DefFn(cPluginManager * a_PluginManager, } // Retrieve the function to call and add it to the plugin: - lua_pushstring(S, FnName); - bool res = Plugin->AddHookRef(HookType, 1); - lua_pop(S, 1); // Pop the function off the stack - if (!res) + cLuaState::cCallbackPtr callback; + lua_getglobal(S, FnName); + bool res = S.GetStackValue(-1, callback); + lua_pop(S, 1); + if (!res || !callback->IsValid()) { LOGWARNING("cPluginManager.AddHook(): Function %s not found. Hook not added.", FnName); S.LogStackTrace(); @@ -1254,12 +1312,13 @@ static int tolua_cPluginManager_ForEachConsoleCommand(lua_State * tolua_S) -static int tolua_cPluginManager_BindCommand(lua_State * L) +static int tolua_cPluginManager_BindCommand(lua_State * a_LuaState) { /* Function signatures: cPluginManager:BindCommand(Command, Permission, Function, HelpString) cPluginManager.BindCommand(Command, Permission, Function, HelpString) -- without the "self" param */ + cLuaState L(a_LuaState); cPluginLua * Plugin = cManualBindings::GetLuaPlugin(L); if (Plugin == nullptr) { @@ -1292,29 +1351,24 @@ static int tolua_cPluginManager_BindCommand(lua_State * L) return 0; } cPluginManager * self = cPluginManager::Get(); - AString Command (tolua_tocppstring(L, idx, "")); - AString Permission(tolua_tocppstring(L, idx + 1, "")); - AString HelpString(tolua_tocppstring(L, idx + 3, "")); - - // Store the function reference: - lua_pop(L, 1); // Pop the help string off the stack - int FnRef = luaL_ref(L, LUA_REGISTRYINDEX); // Store function reference - if (FnRef == LUA_REFNIL) + AString Command, Permission, HelpString; + cLuaState::cCallbackPtr Handler; + L.GetStackValues(idx, Command, Permission, Handler, HelpString); + if (!Handler->IsValid()) { LOGERROR("\"BindCommand\": Cannot create a function reference. Command \"%s\" not bound.", Command.c_str()); return 0; } - if (!self->BindCommand(Command, Plugin, Permission, HelpString)) + auto CommandHandler = std::make_shared<LuaCommandHandler>(std::move(Handler)); + if (!self->BindCommand(Command, Plugin, CommandHandler, Permission, HelpString)) { // Refused. Possibly already bound. Error message has been given, display the callstack: - cLuaState LS(L); - LS.LogStackTrace(); + L.LogStackTrace(); return 0; } - Plugin->BindCommand(Command, FnRef); - lua_pushboolean(L, true); + L.Push(true); return 1; } @@ -1322,7 +1376,7 @@ static int tolua_cPluginManager_BindCommand(lua_State * L) -static int tolua_cPluginManager_BindConsoleCommand(lua_State * L) +static int tolua_cPluginManager_BindConsoleCommand(lua_State * a_LuaState) { /* Function signatures: cPluginManager:BindConsoleCommand(Command, Function, HelpString) @@ -1330,6 +1384,7 @@ static int tolua_cPluginManager_BindConsoleCommand(lua_State * L) */ // Get the plugin identification out of LuaState: + cLuaState L(a_LuaState); cPluginLua * Plugin = cManualBindings::GetLuaPlugin(L); if (Plugin == nullptr) { @@ -1361,28 +1416,23 @@ static int tolua_cPluginManager_BindConsoleCommand(lua_State * L) return 0; } cPluginManager * self = cPluginManager::Get(); - AString Command (tolua_tocppstring(L, idx, "")); - AString HelpString(tolua_tocppstring(L, idx + 2, "")); - - // Store the function reference: - lua_pop(L, 1); // Pop the help string off the stack - int FnRef = luaL_ref(L, LUA_REGISTRYINDEX); // Store function reference - if (FnRef == LUA_REFNIL) + AString Command, HelpString; + cLuaState::cCallbackPtr Handler; + L.GetStackValues(idx, Command, Handler, HelpString); + if (!Handler->IsValid()) { - LOGERROR("\"BindConsoleCommand\": Cannot create a function reference. Console Command \"%s\" not bound.", Command.c_str()); + LOGERROR("\"BindConsoleCommand\": Cannot create a function reference. Console command \"%s\" not bound.", Command.c_str()); return 0; } - if (!self->BindConsoleCommand(Command, Plugin, HelpString)) + auto CommandHandler = std::make_shared<LuaCommandHandler>(std::move(Handler)); + if (!self->BindConsoleCommand(Command, Plugin, CommandHandler, HelpString)) { // Refused. Possibly already bound. Error message has been given, display the callstack: - cLuaState LS(L); - LS.LogStackTrace(); + L.LogStackTrace(); return 0; } - - Plugin->BindConsoleCommand(Command, FnRef); - lua_pushboolean(L, true); + L.Push(true); return 1; } @@ -1586,55 +1636,6 @@ static int tolua_cPlayer_GetRestrictions(lua_State * tolua_S) -static int tolua_cPlayer_OpenWindow(lua_State * tolua_S) -{ - // Function signature: cPlayer:OpenWindow(Window) - - // Retrieve the plugin instance from the Lua state - cPluginLua * Plugin = cManualBindings::GetLuaPlugin(tolua_S); - if (Plugin == nullptr) - { - return 0; - } - - // Get the parameters: - cPlayer * self = reinterpret_cast<cPlayer *>(tolua_tousertype(tolua_S, 1, nullptr)); - cWindow * wnd = reinterpret_cast<cWindow *>(tolua_tousertype(tolua_S, 2, nullptr)); - if ((self == nullptr) || (wnd == nullptr)) - { - LOGWARNING("%s: invalid self (%p) or wnd (%p)", __FUNCTION__, static_cast<void *>(self), static_cast<void *>(wnd)); - return 0; - } - - // If cLuaWindow, add a reference, so that Lua won't delete the cLuaWindow object mid-processing - tolua_Error err; - if (tolua_isusertype(tolua_S, 2, "cLuaWindow", 0, &err)) - { - cLuaWindow * LuaWnd = reinterpret_cast<cLuaWindow *>(wnd); - // Only if not already referenced - if (!LuaWnd->IsLuaReferenced()) - { - int LuaRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); - if (LuaRef == LUA_REFNIL) - { - LOGWARNING("%s: Cannot create a window reference. Cannot open window \"%s\".", - __FUNCTION__, wnd->GetWindowTitle().c_str() - ); - return 0; - } - LuaWnd->SetLuaRef(Plugin, LuaRef); - } - } - - // Open the window - self->OpenWindow(wnd); - return 0; -} - - - - - static int tolua_cPlayer_PermissionMatches(lua_State * tolua_S) { // Function signature: cPlayer:PermissionMatches(PermissionStr, TemplateStr) -> bool @@ -1665,36 +1666,25 @@ static int tolua_cPlayer_PermissionMatches(lua_State * tolua_S) template < class OBJTYPE, - void (OBJTYPE::*SetCallback)(cPluginLua * a_Plugin, int a_FnRef) + void (OBJTYPE::*SetCallback)(cLuaState::cCallbackPtr && a_CallbackFn) > static int tolua_SetObjectCallback(lua_State * tolua_S) { // Function signature: OBJTYPE:SetWhateverCallback(CallbackFunction) - // Retrieve the plugin instance from the Lua state - cPluginLua * Plugin = cManualBindings::GetLuaPlugin(tolua_S); - if (Plugin == nullptr) - { - // Warning message has already been printed by GetLuaPlugin(), bail out silently - return 0; - } - // Get the parameters - self and the function reference: - OBJTYPE * self = reinterpret_cast<OBJTYPE *>(tolua_tousertype(tolua_S, 1, nullptr)); - if (self == nullptr) - { - LOGWARNING("%s: invalid self (%p)", __FUNCTION__, static_cast<void *>(self)); - return 0; - } - int FnRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); // Store function reference for later retrieval - if (FnRef == LUA_REFNIL) + cLuaState L(tolua_S); + OBJTYPE * self; + cLuaState::cCallbackPtr callback; + if (!L.GetStackValues(1, self, callback)) { - LOGERROR("%s: Cannot create a function reference. Callback not set.", __FUNCTION__); + LOGWARNING("%s: Cannot get parameters", __FUNCTION__); + L.LogStackTrace(); return 0; } // Set the callback - (self->*SetCallback)(Plugin, FnRef); + (self->*SetCallback)(std::move(callback)); return 0; } @@ -1702,47 +1692,71 @@ static int tolua_SetObjectCallback(lua_State * tolua_S) -static int tolua_cPluginLua_AddWebTab(lua_State * tolua_S) +// Callback class used for the WebTab: +class cWebTabCallback: + public cWebAdmin::cWebTabCallback { - cLuaState LuaState(tolua_S); - cPluginLua * self = nullptr; +public: + /** The Lua callback to call to generate the page contents. */ + cLuaState::cCallback m_Callback; - if (!LuaState.GetStackValue(1, self)) + virtual bool Call( + const HTTPRequest & a_Request, + const AString & a_UrlPath, + AString & a_Content, + AString & a_ContentType + ) override { - LOGWARNING("cPluginLua:AddWebTab: invalid self as first argument"); - return 0; + AString content, contentType; + return m_Callback.Call(&a_Request, a_UrlPath, cLuaState::Return, a_Content, a_ContentType); } +}; + + + - tolua_Error tolua_err; - tolua_err.array = 0; - tolua_err.index = 3; - tolua_err.type = "function"; - std::string Title; - int Reference = LUA_REFNIL; +static int tolua_cPluginLua_AddWebTab(lua_State * tolua_S) +{ + // OBSOLETE, use cWebAdmin:AddWebTab() instead! + // Function signature: + // cPluginLua:AddWebTab(Title, CallbackFn, [UrlPath]) - if (LuaState.CheckParamString(2) && LuaState.CheckParamFunction(3)) + // TODO: Warn about obsolete API usage + // Only implement after merging the new API change and letting some time for changes in the plugins + + // Check params: + cLuaState LuaState(tolua_S); + cPluginLua * self = cManualBindings::GetLuaPlugin(tolua_S); + if (self == nullptr) { - Reference = luaL_ref(tolua_S, LUA_REGISTRYINDEX); - LuaState.GetStackValue(2, Title); + return 0; } - else + if ( + !LuaState.CheckParamString(2) || + !LuaState.CheckParamFunction(3) || + // Optional string as param 4 + !LuaState.CheckParamEnd(5) + ) { - return cManualBindings::tolua_do_error(tolua_S, "#ferror calling function '#funcname#'", &tolua_err); + return 0; } - if (Reference != LUA_REFNIL) + // Read the params: + AString title, urlPath; + auto callback = std::make_shared<cWebTabCallback>(); + if (!LuaState.GetStackValues(2, title, callback->m_Callback)) { - if (!self->AddWebTab(Title.c_str(), tolua_S, Reference)) - { - luaL_unref(tolua_S, LUA_REGISTRYINDEX, Reference); - } + LOGWARNING("cPlugin:AddWebTab(): Cannot read required parameters"); + return 0; } - else + if (!LuaState.GetStackValue(4, urlPath)) { - LOGWARNING("cPluginLua:AddWebTab: invalid function reference in 2nd argument (Title: \"%s\")", Title.c_str()); + urlPath = cWebAdmin::GetURLEncodedString(title); } + cRoot::Get()->GetWebAdmin()->AddWebTab(title, urlPath, self->GetName(), callback); + return 0; } @@ -2107,22 +2121,68 @@ static int tolua_cUrlParser_ParseAuthorityPart(lua_State * a_LuaState) -static int tolua_cWebAdmin_GetPlugins(lua_State * tolua_S) +static int tolua_cWebAdmin_AddWebTab(lua_State * tolua_S) +{ + // Function signatures: + // cWebAdmin:AddWebTab(Title, UrlPath, CallbackFn) + + // Check params: + cLuaState LuaState(tolua_S); + cPluginLua * self = cManualBindings::GetLuaPlugin(tolua_S); + if (self == nullptr) + { + return 0; + } + if ( + // Don't care whether the first param is a cWebAdmin instance or class + !LuaState.CheckParamString(2, 3) || + !LuaState.CheckParamFunction(4) || + !LuaState.CheckParamEnd(5) + ) + { + return 0; + } + + // Read the params: + AString title, urlPath; + auto callback = std::make_shared<cWebTabCallback>(); + if (!LuaState.GetStackValues(2, title, urlPath, callback->m_Callback)) + { + LOGWARNING("cWebAdmin:AddWebTab(): Cannot read required parameters"); + return 0; + } + + cRoot::Get()->GetWebAdmin()->AddWebTab(title, urlPath, self->GetName(), callback); + + return 0; +} + + + + + +static int tolua_cWebAdmin_GetAllWebTabs(lua_State * tolua_S) { - cWebAdmin * self = reinterpret_cast<cWebAdmin *>(tolua_tousertype(tolua_S, 1, nullptr)); + // Function signature: + // cWebAdmin:GetAllWebTabs() -> { {"PluginName", "UrlPath", "Title"}, {"PluginName", "UrlPath", "Title"}, ...} - const cWebAdmin::PluginList & AllPlugins = self->GetPlugins(); + // Don't care about params at all - lua_createtable(tolua_S, static_cast<int>(AllPlugins.size()), 0); + auto webTabs = cRoot::Get()->GetWebAdmin()->GetAllWebTabs(); + lua_createtable(tolua_S, static_cast<int>(webTabs.size()), 0); int newTable = lua_gettop(tolua_S); int index = 1; - cWebAdmin::PluginList::const_iterator iter = AllPlugins.begin(); - while (iter != AllPlugins.end()) - { - const cWebPlugin * Plugin = *iter; - tolua_pushusertype(tolua_S, reinterpret_cast<void *>(const_cast<cWebPlugin*>(Plugin)), "const cWebPlugin"); + cLuaState L(tolua_S); + for (const auto & wt: webTabs) + { + lua_createtable(tolua_S, 0, 3); + L.Push(wt->m_PluginName); + lua_setfield(tolua_S, -2, "PluginName"); + L.Push(wt->m_UrlPath); + lua_setfield(tolua_S, -2, "UrlPath"); + L.Push(wt->m_Title); + lua_setfield(tolua_S, -2, "Title"); lua_rawseti(tolua_S, newTable, index); - ++iter; ++index; } return 1; @@ -2132,14 +2192,14 @@ static int tolua_cWebAdmin_GetPlugins(lua_State * tolua_S) -/** Binding for cWebAdmin::GetHTMLEscapedString. +/** Binding for cWebAdmin::GetBaseURL. Manual code required because ToLua generates an extra return value */ -static int tolua_AllToLua_cWebAdmin_GetHTMLEscapedString(lua_State * tolua_S) +static int tolua_cWebAdmin_GetBaseURL(lua_State * tolua_S) { // Check the param types: cLuaState S(tolua_S); if ( - !S.CheckParamUserTable(1, "cWebAdmin") || + // Don't care whether the first param is a cWebAdmin instance or class !S.CheckParamString(2) || !S.CheckParamEnd(3) ) @@ -2152,7 +2212,7 @@ static int tolua_AllToLua_cWebAdmin_GetHTMLEscapedString(lua_State * tolua_S) S.GetStackValue(2, Input); // Convert and return: - S.Push(cWebAdmin::GetHTMLEscapedString(Input)); + S.Push(cWebAdmin::GetBaseURL(Input)); return 1; } @@ -2160,14 +2220,14 @@ static int tolua_AllToLua_cWebAdmin_GetHTMLEscapedString(lua_State * tolua_S) -/** Binding for cWebAdmin::GetURLEncodedString. +/** Binding for cWebAdmin::GetContentTypeFromFileExt. Manual code required because ToLua generates an extra return value */ -static int tolua_AllToLua_cWebAdmin_GetURLEncodedString(lua_State * tolua_S) +static int tolua_cWebAdmin_GetContentTypeFromFileExt(lua_State * tolua_S) { // Check the param types: cLuaState S(tolua_S); if ( - !S.CheckParamUserTable(1, "cWebAdmin") || + // Don't care whether the first param is a cWebAdmin instance or class !S.CheckParamString(2) || !S.CheckParamEnd(3) ) @@ -2180,7 +2240,7 @@ static int tolua_AllToLua_cWebAdmin_GetURLEncodedString(lua_State * tolua_S) S.GetStackValue(2, Input); // Convert and return: - S.Push(cWebAdmin::GetURLEncodedString(Input)); + S.Push(cWebAdmin::GetContentTypeFromFileExt(Input)); return 1; } @@ -2188,20 +2248,112 @@ static int tolua_AllToLua_cWebAdmin_GetURLEncodedString(lua_State * tolua_S) -static int tolua_cWebPlugin_GetTabNames(lua_State * tolua_S) +/** Binding for cWebAdmin::GetHTMLEscapedString. +Manual code required because ToLua generates an extra return value */ +static int tolua_cWebAdmin_GetHTMLEscapedString(lua_State * tolua_S) { - // Returns a map of (SafeTitle -> Title) for the plugin's web tabs. - auto self = reinterpret_cast<cWebPlugin *>(tolua_tousertype(tolua_S, 1, nullptr)); - auto TabNames = self->GetTabNames(); - lua_newtable(tolua_S); - int index = 1; - for (auto itr = TabNames.cbegin(), end = TabNames.cend(); itr != end; ++itr) + // Check the param types: + cLuaState S(tolua_S); + if ( + // Don't care whether the first param is a cWebAdmin instance or class + !S.CheckParamString(2) || + !S.CheckParamEnd(3) + ) { - tolua_pushstring(tolua_S, itr->second.c_str()); // Because the SafeTitle is supposed to be unique, use it as key - tolua_pushstring(tolua_S, itr->first.c_str()); - lua_rawset(tolua_S, -3); - ++index; + return 0; + } + + // Get the parameters: + AString Input; + S.GetStackValue(2, Input); + + // Convert and return: + S.Push(cWebAdmin::GetHTMLEscapedString(Input)); + return 1; +} + + + + + +/** Binding for cWebAdmin::GetPage. */ +static int tolua_cWebAdmin_GetPage(lua_State * tolua_S) +{ + /* + Function signature: + cWebAdmin:GetPage(a_HTTPRequest) -> + { + Content = "", // Content generated by the plugin + ContentType = "", // Content type generated by the plugin (default: "text/html") + UrlPath = "", // URL path of the tab + TabTitle = "", // Tab's title, as register via cWebAdmin:AddWebTab() + PluginName = "", // Plugin's API name + PluginFolder = "", // Plugin's folder name (display name) + } + */ + + // Check the param types: + cLuaState S(tolua_S); + if ( + // Don't care about first param, whether it's cWebAdmin instance or class + !S.CheckParamUserType(2, "HTTPRequest") || + !S.CheckParamEnd(3) + ) + { + return 0; + } + + // Get the parameters: + HTTPRequest * request = nullptr; + if (!S.GetStackValue(2, request)) + { + LOGWARNING("cWebAdmin:GetPage(): Cannot read the HTTPRequest parameter."); + return 0; + } + + // Generate the page and push the results as a dictionary-table: + auto page = cRoot::Get()->GetWebAdmin()->GetPage(*request); + lua_createtable(S, 0, 6); + S.Push(page.Content); + lua_setfield(S, -2, "Content"); + S.Push(page.ContentType); + lua_setfield(S, -2, "ContentType"); + S.Push(page.TabUrlPath); + lua_setfield(S, -2, "UrlPath"); + S.Push(page.TabTitle); + lua_setfield(S, -2, "TabTitle"); + S.Push(page.PluginName); + lua_setfield(S, -2, "PluginName"); + S.Push(cPluginManager::Get()->GetPluginFolderName(page.PluginName)); + lua_setfield(S, -2, "PluginFolder"); + return 1; +} + + + + + +/** Binding for cWebAdmin::GetURLEncodedString. +Manual code required because ToLua generates an extra return value */ +static int tolua_cWebAdmin_GetURLEncodedString(lua_State * tolua_S) +{ + // Check the param types: + cLuaState S(tolua_S); + if ( + // Don't care whether the first param is a cWebAdmin instance or class + !S.CheckParamString(2) || + !S.CheckParamEnd(3) + ) + { + return 0; } + + // Get the parameters: + AString Input; + S.GetStackValue(2, Input); + + // Convert and return: + S.Push(cWebAdmin::GetURLEncodedString(Input)); return 1; } @@ -2624,6 +2776,79 @@ static int tolua_cLineBlockTracer_Trace(lua_State * tolua_S) +static int tolua_cLuaWindow_new(lua_State * tolua_S) +{ + // Function signature: + // cLuaWindow:new(type, slotsX, slotsY, title) + + // Check params: + cLuaState L(tolua_S); + if ( + !L.CheckParamUserTable(1, "cLuaWindow") || + !L.CheckParamNumber(2, 4) || + !L.CheckParamString(5) || + !L.CheckParamEnd(6) + ) + { + return 0; + } + + // Read params: + int windowType, slotsX, slotsY; + AString title; + if (!L.GetStackValues(2, windowType, slotsX, slotsY, title)) + { + LOGWARNING("%s: Cannot read Lua parameters", __FUNCTION__); + L.LogStackValues(); + L.LogStackTrace(); + } + + // Create the window and return it: + L.Push(new cLuaWindow(L, static_cast<cLuaWindow::WindowType>(windowType), slotsX, slotsY, title)); + return 1; +} + + + + + +static int tolua_cLuaWindow_new_local(lua_State * tolua_S) +{ + // Function signature: + // cLuaWindow:new(type, slotsX, slotsY, title) + + // Check params: + cLuaState L(tolua_S); + if ( + !L.CheckParamUserTable(1, "cLuaWindow") || + !L.CheckParamNumber(2, 4) || + !L.CheckParamString(5) || + !L.CheckParamEnd(6) + ) + { + return 0; + } + + // Read params: + int windowType, slotsX, slotsY; + AString title; + if (!L.GetStackValues(2, windowType, slotsX, slotsY, title)) + { + LOGWARNING("%s: Cannot read Lua parameters", __FUNCTION__); + L.LogStackValues(); + L.LogStackTrace(); + } + + // Create the window, register it for GC and return it: + L.Push(new cLuaWindow(L, static_cast<cLuaWindow::WindowType>(windowType), slotsX, slotsY, title)); + tolua_register_gc(tolua_S, lua_gettop(tolua_S)); + return 1; +} + + + + + static int tolua_cRoot_GetBuildCommitID(lua_State * tolua_S) { cLuaState L(tolua_S); @@ -3167,7 +3392,7 @@ static int tolua_cBoundingBox_CalcLineIntersection(lua_State * a_LuaState) const cBoundingBox * bbox; if (!L.GetStackValues(1, bbox, pt1, pt2)) // Try the regular signature { - L.LogStack(); + L.LogStackValues(); tolua_error(a_LuaState, "Invalid function params. Expected either bbox:CalcLineIntersection(pt1, pt2) or cBoundingBox:CalcLineIntersection(min, max, pt1, pt2).", nullptr); return 0; } @@ -3197,7 +3422,7 @@ static int tolua_cBoundingBox_Intersect(lua_State * a_LuaState) const cBoundingBox * other; if (!L.GetStackValues(1, self, other)) { - L.LogStack(); + L.LogStackValues(); tolua_error(a_LuaState, "Invalid function params. Expected bbox:Intersect(otherBbox).", nullptr); return 0; } @@ -3570,6 +3795,9 @@ void cManualBindings::Bind(lua_State * tolua_S) tolua_endmodule(tolua_S); tolua_beginmodule(tolua_S, "cLuaWindow"); + tolua_function(tolua_S, "new", tolua_cLuaWindow_new); + tolua_function(tolua_S, "new_local", tolua_cLuaWindow_new_local); + tolua_function(tolua_S, ".call", tolua_cLuaWindow_new_local); tolua_function(tolua_S, "SetOnClosing", tolua_SetObjectCallback<cLuaWindow, &cLuaWindow::SetOnClosing>); tolua_function(tolua_S, "SetOnSlotChanged", tolua_SetObjectCallback<cLuaWindow, &cLuaWindow::SetOnSlotChanged>); tolua_endmodule(tolua_S); @@ -3590,7 +3818,6 @@ void cManualBindings::Bind(lua_State * tolua_S) tolua_beginmodule(tolua_S, "cPlayer"); tolua_function(tolua_S, "GetPermissions", tolua_cPlayer_GetPermissions); tolua_function(tolua_S, "GetRestrictions", tolua_cPlayer_GetRestrictions); - tolua_function(tolua_S, "OpenWindow", tolua_cPlayer_OpenWindow); tolua_function(tolua_S, "PermissionMatches", tolua_cPlayer_PermissionMatches); tolua_endmodule(tolua_S); @@ -3655,13 +3882,13 @@ void cManualBindings::Bind(lua_State * tolua_S) tolua_endmodule(tolua_S); tolua_beginmodule(tolua_S, "cWebAdmin"); - tolua_function(tolua_S, "GetHTMLEscapedString", tolua_AllToLua_cWebAdmin_GetHTMLEscapedString); - tolua_function(tolua_S, "GetPlugins", tolua_cWebAdmin_GetPlugins); - tolua_function(tolua_S, "GetURLEncodedString", tolua_AllToLua_cWebAdmin_GetURLEncodedString); - tolua_endmodule(tolua_S); - - tolua_beginmodule(tolua_S, "cWebPlugin"); - tolua_function(tolua_S, "GetTabNames", tolua_cWebPlugin_GetTabNames); + tolua_function(tolua_S, "AddWebTab", tolua_cWebAdmin_AddWebTab); + tolua_function(tolua_S, "GetAllWebTabs", tolua_cWebAdmin_GetAllWebTabs); + tolua_function(tolua_S, "GetBaseURL", tolua_cWebAdmin_GetBaseURL); + tolua_function(tolua_S, "GetContentTypeFromFileExt", tolua_cWebAdmin_GetContentTypeFromFileExt); + tolua_function(tolua_S, "GetHTMLEscapedString", tolua_cWebAdmin_GetHTMLEscapedString); + tolua_function(tolua_S, "GetPage", tolua_cWebAdmin_GetPage); + tolua_function(tolua_S, "GetURLEncodedString", tolua_cWebAdmin_GetURLEncodedString); tolua_endmodule(tolua_S); tolua_beginmodule(tolua_S, "HTTPRequest"); diff --git a/src/Bindings/ManualBindings_World.cpp b/src/Bindings/ManualBindings_World.cpp index 00d2169d8..b3170a636 100644 --- a/src/Bindings/ManualBindings_World.cpp +++ b/src/Bindings/ManualBindings_World.cpp @@ -103,7 +103,9 @@ static int tolua_cWorld_ChunkStay(lua_State * tolua_S) return 0; } - ChunkStay->Enable(*World->GetChunkMap(), 3, 4); + cLuaState::cCallbackPtr onChunkAvailable, onAllChunksAvailable; + L.GetStackValues(3, onChunkAvailable, onAllChunksAvailable); // Callbacks may be unassigned at all - as a request to load / generate chunks + ChunkStay->Enable(*World->GetChunkMap(), std::move(onChunkAvailable), std::move(onAllChunksAvailable)); return 0; } @@ -466,67 +468,41 @@ static int tolua_cWorld_PrepareChunk(lua_State * tolua_S) -class cLuaWorldTask : - public cPluginLua::cResettable -{ -public: - cLuaWorldTask(cPluginLua & a_Plugin, int a_FnRef) : - cPluginLua::cResettable(a_Plugin), - m_FnRef(a_FnRef) - { - } - - void Run(cWorld & a_World) - { - cCSLock Lock(m_CSPlugin); - if (m_Plugin != nullptr) - { - m_Plugin->Call(m_FnRef, &a_World); - } - } - -protected: - int m_FnRef; -}; - - - - - static int tolua_cWorld_QueueTask(lua_State * tolua_S) { - // Binding for cWorld::QueueTask - // Params: function + // Function signature: + // World:QueueTask(Callback) - // Retrieve the cPlugin from the LuaState: - cPluginLua * Plugin = cManualBindings::GetLuaPlugin(tolua_S); - if (Plugin == nullptr) + // Retrieve the args: + cLuaState L(tolua_S); + if ( + !L.CheckParamUserType(1, "cWorld") || + !L.CheckParamNumber (2) || + !L.CheckParamFunction(3) + ) { - // An error message has been already printed in GetLuaPlugin() return 0; } - - // Retrieve the args: - cWorld * self = reinterpret_cast<cWorld *>(tolua_tousertype(tolua_S, 1, nullptr)); - if (self == nullptr) + cWorld * World; + cLuaState::cCallbackSharedPtr Task; + if (!L.GetStackValues(1, World, Task)) { - return cManualBindings::lua_do_error(tolua_S, "Error in function call '#funcname#': Not called on an object instance"); + return cManualBindings::lua_do_error(tolua_S, "Error in function call '#funcname#': Cannot read parameters"); } - if (!lua_isfunction(tolua_S, 2)) + if (World == nullptr) { - return cManualBindings::lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a function for parameter #1"); + return cManualBindings::lua_do_error(tolua_S, "Error in function call '#funcname#': Not called on an object instance"); } - - // Create a reference to the function: - int FnRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); - if (FnRef == LUA_REFNIL) + if (!Task->IsValid()) { - return cManualBindings::lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get function reference of parameter #1"); + return cManualBindings::lua_do_error(tolua_S, "Error in function call '#funcname#': Could not store the callback parameter"); } - auto ResettableTask = std::make_shared<cLuaWorldTask>(*Plugin, FnRef); - Plugin->AddResettable(ResettableTask); - self->QueueTask(std::bind(&cLuaWorldTask::Run, ResettableTask, std::placeholders::_1)); + World->QueueTask([Task](cWorld & a_World) + { + Task->Call(&a_World); + } + ); return 0; } @@ -576,16 +552,8 @@ static int tolua_cWorld_SetSignLines(lua_State * tolua_S) static int tolua_cWorld_ScheduleTask(lua_State * tolua_S) { - // Binding for cWorld::ScheduleTask - // Params: function, Ticks - - // Retrieve the cPlugin from the LuaState: - cPluginLua * Plugin = cManualBindings::GetLuaPlugin(tolua_S); - if (Plugin == nullptr) - { - // An error message has been already printed in GetLuaPlugin() - return 0; - } + // Function signature: + // World:ScheduleTask(NumTicks, Callback) // Retrieve the args: cLuaState L(tolua_S); @@ -597,22 +565,27 @@ static int tolua_cWorld_ScheduleTask(lua_State * tolua_S) { return 0; } - cWorld * World = reinterpret_cast<cWorld *>(tolua_tousertype(tolua_S, 1, nullptr)); + cWorld * World; + int NumTicks; + auto Task = std::make_shared<cLuaState::cCallback>(); + if (!L.GetStackValues(1, World, NumTicks, Task)) + { + return cManualBindings::lua_do_error(tolua_S, "Error in function call '#funcname#': Cannot read parameters"); + } if (World == nullptr) { return cManualBindings::lua_do_error(tolua_S, "Error in function call '#funcname#': Not called on an object instance"); } - - // Create a reference to the function: - int FnRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); - if (FnRef == LUA_REFNIL) + if (!Task->IsValid()) { - return cManualBindings::lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get function reference of parameter #1"); + return cManualBindings::lua_do_error(tolua_S, "Error in function call '#funcname#': Could not store the callback parameter"); } - auto ResettableTask = std::make_shared<cLuaWorldTask>(*Plugin, FnRef); - Plugin->AddResettable(ResettableTask); - World->ScheduleTask(static_cast<int>(tolua_tonumber(tolua_S, 2, 0)), std::bind(&cLuaWorldTask::Run, ResettableTask, std::placeholders::_1)); + World->ScheduleTask(NumTicks, [Task](cWorld & a_World) + { + Task->Call(&a_World); + } + ); return 0; } diff --git a/src/Bindings/Plugin.h b/src/Bindings/Plugin.h index 3ee417361..3276fde67 100644 --- a/src/Bindings/Plugin.h +++ b/src/Bindings/Plugin.h @@ -110,21 +110,6 @@ public: virtual bool OnWorldStarted (cWorld & a_World) = 0; virtual bool OnWorldTick (cWorld & a_World, std::chrono::milliseconds a_Dt, std::chrono::milliseconds a_LastTickDurationMSec) = 0; - /** Handles the command split into a_Split, issued by player a_Player. - Command permissions have already been checked. - Returns true if command handled successfully. */ - virtual bool HandleCommand(const AStringVector & a_Split, cPlayer & a_Player, const AString & a_FullCommand) = 0; - - /** Handles the console command split into a_Split. - Returns true if command handled successfully. Output is to be sent to the a_Output callback. */ - virtual bool HandleConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output, const AString & a_FullCommand) = 0; - - /** All bound commands are to be removed, do any language-dependent cleanup here */ - virtual void ClearCommands(void) {} - - /** All bound console commands are to be removed, do any language-dependent cleanup here */ - virtual void ClearConsoleCommands(void) {} - // tolua_begin const AString & GetName(void) const { return m_Name; } void SetName(const AString & a_Name) { m_Name = a_Name; } diff --git a/src/Bindings/PluginLua.cpp b/src/Bindings/PluginLua.cpp index a266e6223..d1fc2ae4f 100644 --- a/src/Bindings/PluginLua.cpp +++ b/src/Bindings/PluginLua.cpp @@ -15,6 +15,8 @@ #include "../CommandOutput.h" #include "PluginManager.h" #include "../Item.h" +#include "../Root.h" +#include "../WebAdmin.h" extern "C" { @@ -43,7 +45,6 @@ cPluginLua::cPluginLua(const AString & a_PluginDirectory) : cPluginLua::~cPluginLua() { - cCSLock Lock(m_CriticalSection); Close(); } @@ -53,45 +54,22 @@ cPluginLua::~cPluginLua() void cPluginLua::Close(void) { - cCSLock Lock(m_CriticalSection); - + cOperation op(*this); // If already closed, bail out: - if (!m_LuaState.IsValid()) + if (!op().IsValid()) { - ASSERT(m_Resettables.empty()); ASSERT(m_HookMap.empty()); return; } - // Remove the command bindings and web tabs: - ClearCommands(); - ClearConsoleCommands(); - ClearTabs(); - - // Notify and remove all m_Resettables (unlock the m_CriticalSection while resetting them): - cResettablePtrs resettables; - std::swap(m_Resettables, resettables); - { - cCSUnlock Unlock(Lock); - for (auto resettable: resettables) - { - resettable->Reset(); - } - m_Resettables.clear(); - } // cCSUnlock (m_CriticalSection) + // Remove the web tabs: + ClearWebTabs(); // Release all the references in the hook map: - for (cHookMap::iterator itrH = m_HookMap.begin(), endH = m_HookMap.end(); itrH != endH; ++itrH) - { - for (cLuaRefs::iterator itrR = itrH->second.begin(), endR = itrH->second.end(); itrR != endR; ++itrR) - { - delete *itrR; - } // for itrR - itrH->second[] - } // for itrH - m_HookMap[] m_HookMap.clear(); // Close the Lua engine: - m_LuaState.Close(); + op().Close(); } @@ -100,8 +78,8 @@ void cPluginLua::Close(void) bool cPluginLua::Load(void) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) + cOperation op(*this); + if (!op().IsValid()) { m_LuaState.Create(); m_LuaState.RegisterAPILibs(); @@ -205,7 +183,7 @@ bool cPluginLua::Load(void) void cPluginLua::Unload(void) { - ClearTabs(); + ClearWebTabs(); super::Unload(); Close(); } @@ -216,12 +194,12 @@ void cPluginLua::Unload(void) void cPluginLua::OnDisable(void) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.HasFunction("OnDisable")) + cOperation op(*this); + if (!op().HasFunction("OnDisable")) { return; } - m_LuaState.Call("OnDisable"); + op().Call("OnDisable"); } @@ -230,16 +208,7 @@ void cPluginLua::OnDisable(void) void cPluginLua::Tick(float a_Dt) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return; - } - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_TICK]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), a_Dt); - } + CallSimpleHooks(cPluginManager::HOOK_TICK, a_Dt); } @@ -248,22 +217,7 @@ void cPluginLua::Tick(float a_Dt) bool cPluginLua::OnBlockSpread(cWorld & a_World, int a_BlockX, int a_BlockY, int a_BlockZ, eSpreadSource a_Source) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_BLOCK_SPREAD]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_World, a_BlockX, a_BlockY, a_BlockZ, a_Source, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_BLOCK_SPREAD, &a_World, a_BlockX, a_BlockY, a_BlockZ, a_Source); } @@ -272,22 +226,7 @@ bool cPluginLua::OnBlockSpread(cWorld & a_World, int a_BlockX, int a_BlockY, int bool cPluginLua::OnBlockToPickups(cWorld & a_World, cEntity * a_Digger, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, cItems & a_Pickups) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_BLOCK_TO_PICKUPS]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_World, a_Digger, a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta, &a_Pickups, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_BLOCK_TO_PICKUPS, &a_World, a_Digger, a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta, &a_Pickups); } @@ -296,22 +235,7 @@ bool cPluginLua::OnBlockToPickups(cWorld & a_World, cEntity * a_Digger, int a_Bl bool cPluginLua::OnBrewingCompleted(cWorld & a_World, cBrewingstandEntity & a_Brewingstand) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_BREWING_COMPLETED]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_World, &a_Brewingstand, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_BREWING_COMPLETED, &a_World, &a_Brewingstand); } @@ -320,22 +244,7 @@ bool cPluginLua::OnBrewingCompleted(cWorld & a_World, cBrewingstandEntity & a_Br bool cPluginLua::OnBrewingCompleting(cWorld & a_World, cBrewingstandEntity & a_Brewingstand) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_BREWING_COMPLETING]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_World, &a_Brewingstand, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_BREWING_COMPLETING, &a_World, &a_Brewingstand); } @@ -344,16 +253,16 @@ bool cPluginLua::OnBrewingCompleting(cWorld & a_World, cBrewingstandEntity & a_B bool cPluginLua::OnChat(cPlayer & a_Player, AString & a_Message) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) + cOperation op(*this); + if (!op().IsValid()) { return false; } bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_CHAT]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + auto & hooks = m_HookMap[cPluginManager::HOOK_CHAT]; + for (auto & hook: hooks) { - m_LuaState.Call(static_cast<int>(**itr), &a_Player, a_Message, cLuaState::Return, res, a_Message); + hook->Call(&a_Player, a_Message, cLuaState::Return, res, a_Message); if (res) { return true; @@ -368,22 +277,7 @@ bool cPluginLua::OnChat(cPlayer & a_Player, AString & a_Message) bool cPluginLua::OnChunkAvailable(cWorld & a_World, int a_ChunkX, int a_ChunkZ) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_CHUNK_AVAILABLE]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_World, a_ChunkX, a_ChunkZ, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_CHUNK_AVAILABLE, &a_World, a_ChunkX, a_ChunkZ); } @@ -392,22 +286,7 @@ bool cPluginLua::OnChunkAvailable(cWorld & a_World, int a_ChunkX, int a_ChunkZ) bool cPluginLua::OnChunkGenerated(cWorld & a_World, int a_ChunkX, int a_ChunkZ, cChunkDesc * a_ChunkDesc) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_CHUNK_GENERATED]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_World, a_ChunkX, a_ChunkZ, a_ChunkDesc, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_CHUNK_GENERATED, &a_World, a_ChunkX, a_ChunkZ, a_ChunkDesc); } @@ -416,22 +295,7 @@ bool cPluginLua::OnChunkGenerated(cWorld & a_World, int a_ChunkX, int a_ChunkZ, bool cPluginLua::OnChunkGenerating(cWorld & a_World, int a_ChunkX, int a_ChunkZ, cChunkDesc * a_ChunkDesc) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_CHUNK_GENERATING]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_World, a_ChunkX, a_ChunkZ, a_ChunkDesc, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_CHUNK_GENERATING, &a_World, a_ChunkX, a_ChunkZ, a_ChunkDesc); } @@ -440,22 +304,7 @@ bool cPluginLua::OnChunkGenerating(cWorld & a_World, int a_ChunkX, int a_ChunkZ, bool cPluginLua::OnChunkUnloaded(cWorld & a_World, int a_ChunkX, int a_ChunkZ) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_CHUNK_UNLOADED]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_World, a_ChunkX, a_ChunkZ, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_CHUNK_UNLOADED, &a_World, a_ChunkX, a_ChunkZ); } @@ -464,22 +313,7 @@ bool cPluginLua::OnChunkUnloaded(cWorld & a_World, int a_ChunkX, int a_ChunkZ) bool cPluginLua::OnChunkUnloading(cWorld & a_World, int a_ChunkX, int a_ChunkZ) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_CHUNK_UNLOADING]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_World, a_ChunkX, a_ChunkZ, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_CHUNK_UNLOADING, &a_World, a_ChunkX, a_ChunkZ); } @@ -488,22 +322,7 @@ bool cPluginLua::OnChunkUnloading(cWorld & a_World, int a_ChunkX, int a_ChunkZ) bool cPluginLua::OnCollectingPickup(cPlayer & a_Player, cPickup & a_Pickup) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_COLLECTING_PICKUP]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Player, &a_Pickup, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_COLLECTING_PICKUP, &a_Player, &a_Pickup); } @@ -512,22 +331,7 @@ bool cPluginLua::OnCollectingPickup(cPlayer & a_Player, cPickup & a_Pickup) bool cPluginLua::OnCraftingNoRecipe(cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe & a_Recipe) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_CRAFTING_NO_RECIPE]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Player, &a_Grid, &a_Recipe, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_CRAFTING_NO_RECIPE, &a_Player, &a_Grid, &a_Recipe); } @@ -536,22 +340,7 @@ bool cPluginLua::OnCraftingNoRecipe(cPlayer & a_Player, cCraftingGrid & a_Grid, bool cPluginLua::OnDisconnect(cClientHandle & a_Client, const AString & a_Reason) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_DISCONNECT]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Client, a_Reason, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_DISCONNECT, &a_Client, a_Reason); } @@ -560,22 +349,7 @@ bool cPluginLua::OnDisconnect(cClientHandle & a_Client, const AString & a_Reason bool cPluginLua::OnEntityAddEffect(cEntity & a_Entity, int a_EffectType, int a_EffectDurationTicks, int a_EffectIntensity, double a_DistanceModifier) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_ENTITY_ADD_EFFECT]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Entity, a_EffectType, a_EffectDurationTicks, a_EffectIntensity, a_DistanceModifier, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_ENTITY_ADD_EFFECT, &a_Entity, a_EffectType, a_EffectDurationTicks, a_EffectIntensity, a_DistanceModifier); } @@ -584,22 +358,7 @@ bool cPluginLua::OnEntityAddEffect(cEntity & a_Entity, int a_EffectType, int a_E bool cPluginLua::OnEntityChangingWorld(cEntity & a_Entity, cWorld & a_World) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_ENTITY_CHANGING_WORLD]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Entity, &a_World, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_ENTITY_CHANGING_WORLD, &a_Entity, &a_World); } @@ -608,22 +367,7 @@ bool cPluginLua::OnEntityChangingWorld(cEntity & a_Entity, cWorld & a_World) bool cPluginLua::OnEntityChangedWorld(cEntity & a_Entity, cWorld & a_World) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_ENTITY_CHANGED_WORLD]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Entity, &a_World, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_ENTITY_CHANGED_WORLD, &a_Entity, &a_World); } @@ -632,16 +376,16 @@ bool cPluginLua::OnEntityChangedWorld(cEntity & a_Entity, cWorld & a_World) bool cPluginLua::OnExecuteCommand(cPlayer * a_Player, const AStringVector & a_Split, const AString & a_EntireCommand, cPluginManager::CommandResult & a_Result) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) + cOperation op(*this); + if (!op().IsValid()) { return false; } bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_EXECUTE_COMMAND]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + auto & hooks = m_HookMap[cPluginManager::HOOK_EXECUTE_COMMAND]; + for (auto & hook: hooks) { - m_LuaState.Call(static_cast<int>(**itr), a_Player, a_Split, a_EntireCommand, cLuaState::Return, res, a_Result); + hook->Call(a_Player, a_Split, a_EntireCommand, cLuaState::Return, res, a_Result); if (res) { return true; @@ -656,26 +400,26 @@ bool cPluginLua::OnExecuteCommand(cPlayer * a_Player, const AStringVector & a_Sp bool cPluginLua::OnExploded(cWorld & a_World, double a_ExplosionSize, bool a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) + cOperation op(*this); + if (!op().IsValid()) { return false; } bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_EXPLODED]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + auto & hooks = m_HookMap[cPluginManager::HOOK_EXPLODED]; + for (auto & hook: hooks) { switch (a_Source) { - case esBed: m_LuaState.Call(static_cast<int>(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<Vector3i *> (a_SourceData), cLuaState::Return, res); break; - case esEnderCrystal: m_LuaState.Call(static_cast<int>(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<cEntity *> (a_SourceData), cLuaState::Return, res); break; - case esGhastFireball: m_LuaState.Call(static_cast<int>(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<cGhastFireballEntity *>(a_SourceData), cLuaState::Return, res); break; - case esMonster: m_LuaState.Call(static_cast<int>(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<cMonster *> (a_SourceData), cLuaState::Return, res); break; - case esOther: m_LuaState.Call(static_cast<int>(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, cLuaState::Return, res); break; - case esPlugin: m_LuaState.Call(static_cast<int>(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, cLuaState::Return, res); break; - case esPrimedTNT: m_LuaState.Call(static_cast<int>(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<cTNTEntity *> (a_SourceData), cLuaState::Return, res); break; - case esWitherBirth: m_LuaState.Call(static_cast<int>(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<cMonster *> (a_SourceData), cLuaState::Return, res); break; - case esWitherSkull: m_LuaState.Call(static_cast<int>(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<cWitherSkullEntity *> (a_SourceData), cLuaState::Return, res); break; + case esBed: hook->Call(&a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<Vector3i *> (a_SourceData), cLuaState::Return, res); break; + case esEnderCrystal: hook->Call(&a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<cEntity *> (a_SourceData), cLuaState::Return, res); break; + case esGhastFireball: hook->Call(&a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<cGhastFireballEntity *>(a_SourceData), cLuaState::Return, res); break; + case esMonster: hook->Call(&a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<cMonster *> (a_SourceData), cLuaState::Return, res); break; + case esOther: hook->Call(&a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, cLuaState::Return, res); break; + case esPlugin: hook->Call(&a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, cLuaState::Return, res); break; + case esPrimedTNT: hook->Call(&a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<cTNTEntity *> (a_SourceData), cLuaState::Return, res); break; + case esWitherBirth: hook->Call(&a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<cMonster *> (a_SourceData), cLuaState::Return, res); break; + case esWitherSkull: hook->Call(&a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<cWitherSkullEntity *> (a_SourceData), cLuaState::Return, res); break; case esMax: { ASSERT(!"Invalid explosion source"); @@ -696,26 +440,26 @@ bool cPluginLua::OnExploded(cWorld & a_World, double a_ExplosionSize, bool a_Can bool cPluginLua::OnExploding(cWorld & a_World, double & a_ExplosionSize, bool & a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) + cOperation op(*this); + if (!op().IsValid()) { return false; } bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_EXPLODING]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + auto & hooks = m_HookMap[cPluginManager::HOOK_EXPLODING]; + for (auto & hook: hooks) { switch (a_Source) { - case esBed: m_LuaState.Call(static_cast<int>(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<Vector3i *> (a_SourceData), cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; - case esEnderCrystal: m_LuaState.Call(static_cast<int>(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<cEntity *> (a_SourceData), cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; - case esGhastFireball: m_LuaState.Call(static_cast<int>(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<cGhastFireballEntity *>(a_SourceData), cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; - case esMonster: m_LuaState.Call(static_cast<int>(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<cMonster *> (a_SourceData), cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; - case esOther: m_LuaState.Call(static_cast<int>(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; - case esPlugin: m_LuaState.Call(static_cast<int>(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; - case esPrimedTNT: m_LuaState.Call(static_cast<int>(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<cTNTEntity *> (a_SourceData), cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; - case esWitherBirth: m_LuaState.Call(static_cast<int>(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<cMonster *> (a_SourceData), cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; - case esWitherSkull: m_LuaState.Call(static_cast<int>(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<cWitherSkullEntity *> (a_SourceData), cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; + case esBed: hook->Call(&a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<Vector3i *> (a_SourceData), cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; + case esEnderCrystal: hook->Call(&a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<cEntity *> (a_SourceData), cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; + case esGhastFireball: hook->Call(&a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<cGhastFireballEntity *>(a_SourceData), cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; + case esMonster: hook->Call(&a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<cMonster *> (a_SourceData), cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; + case esOther: hook->Call(&a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; + case esPlugin: hook->Call(&a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; + case esPrimedTNT: hook->Call(&a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<cTNTEntity *> (a_SourceData), cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; + case esWitherBirth: hook->Call(&a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<cMonster *> (a_SourceData), cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; + case esWitherSkull: hook->Call(&a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<cWitherSkullEntity *> (a_SourceData), cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; case esMax: { ASSERT(!"Invalid explosion source"); @@ -736,22 +480,7 @@ bool cPluginLua::OnExploding(cWorld & a_World, double & a_ExplosionSize, bool & bool cPluginLua::OnHandshake(cClientHandle & a_Client, const AString & a_Username) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_HANDSHAKE]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Client, a_Username, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_HANDSHAKE, &a_Client, a_Username); } @@ -760,22 +489,7 @@ bool cPluginLua::OnHandshake(cClientHandle & a_Client, const AString & a_Usernam bool cPluginLua::OnHopperPullingItem(cWorld & a_World, cHopperEntity & a_Hopper, int a_DstSlotNum, cBlockEntityWithItems & a_SrcEntity, int a_SrcSlotNum) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_HOPPER_PULLING_ITEM]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_World, &a_Hopper, a_DstSlotNum, &a_SrcEntity, a_SrcSlotNum, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_HOPPER_PULLING_ITEM, &a_World, &a_Hopper, a_DstSlotNum, &a_SrcEntity, a_SrcSlotNum); } @@ -784,22 +498,7 @@ bool cPluginLua::OnHopperPullingItem(cWorld & a_World, cHopperEntity & a_Hopper, bool cPluginLua::OnHopperPushingItem(cWorld & a_World, cHopperEntity & a_Hopper, int a_SrcSlotNum, cBlockEntityWithItems & a_DstEntity, int a_DstSlotNum) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_HOPPER_PUSHING_ITEM]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_World, &a_Hopper, a_SrcSlotNum, &a_DstEntity, a_DstSlotNum, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_HOPPER_PUSHING_ITEM, &a_World, &a_Hopper, a_SrcSlotNum, &a_DstEntity, a_DstSlotNum); } @@ -808,16 +507,16 @@ bool cPluginLua::OnHopperPushingItem(cWorld & a_World, cHopperEntity & a_Hopper, bool cPluginLua::OnKilled(cEntity & a_Victim, TakeDamageInfo & a_TDI, AString & a_DeathMessage) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) + cOperation op(*this); + if (!op().IsValid()) { return false; } bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_KILLED]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + auto & hooks = m_HookMap[cPluginManager::HOOK_KILLED]; + for (auto & hook: hooks) { - m_LuaState.Call(static_cast<int>(**itr), &a_Victim, &a_TDI, a_DeathMessage, cLuaState::Return, res, a_DeathMessage); + hook->Call(&a_Victim, &a_TDI, a_DeathMessage, cLuaState::Return, res, a_DeathMessage); if (res) { return true; @@ -832,22 +531,7 @@ bool cPluginLua::OnKilled(cEntity & a_Victim, TakeDamageInfo & a_TDI, AString & bool cPluginLua::OnKilling(cEntity & a_Victim, cEntity * a_Killer, TakeDamageInfo & a_TDI) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_KILLING]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Victim, a_Killer, &a_TDI, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_KILLING, &a_Victim, a_Killer, &a_TDI); } @@ -856,22 +540,7 @@ bool cPluginLua::OnKilling(cEntity & a_Victim, cEntity * a_Killer, TakeDamageInf bool cPluginLua::OnLogin(cClientHandle & a_Client, UInt32 a_ProtocolVersion, const AString & a_Username) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_LOGIN]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Client, a_ProtocolVersion, a_Username, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_LOGIN, &a_Client, a_ProtocolVersion, a_Username); } @@ -880,22 +549,7 @@ bool cPluginLua::OnLogin(cClientHandle & a_Client, UInt32 a_ProtocolVersion, con bool cPluginLua::OnPlayerAnimation(cPlayer & a_Player, int a_Animation) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_ANIMATION]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Player, a_Animation, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_PLAYER_ANIMATION, &a_Player, a_Animation); } @@ -904,22 +558,7 @@ bool cPluginLua::OnPlayerAnimation(cPlayer & a_Player, int a_Animation) bool cPluginLua::OnPlayerBreakingBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_BREAKING_BLOCK]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_BlockType, a_BlockMeta, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_PLAYER_BREAKING_BLOCK, &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_BlockType, a_BlockMeta); } @@ -928,22 +567,7 @@ bool cPluginLua::OnPlayerBreakingBlock(cPlayer & a_Player, int a_BlockX, int a_B bool cPluginLua::OnPlayerBrokenBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_BROKEN_BLOCK]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_BlockType, a_BlockMeta, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_PLAYER_BROKEN_BLOCK, &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_BlockType, a_BlockMeta); } @@ -952,22 +576,7 @@ bool cPluginLua::OnPlayerBrokenBlock(cPlayer & a_Player, int a_BlockX, int a_Blo bool cPluginLua::OnPlayerDestroyed(cPlayer & a_Player) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_DESTROYED]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Player, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_PLAYER_DESTROYED, &a_Player); } @@ -976,22 +585,7 @@ bool cPluginLua::OnPlayerDestroyed(cPlayer & a_Player) bool cPluginLua::OnPlayerEating(cPlayer & a_Player) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_EATING]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Player, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_PLAYER_EATING, &a_Player); } @@ -1000,22 +594,7 @@ bool cPluginLua::OnPlayerEating(cPlayer & a_Player) bool cPluginLua::OnPlayerFoodLevelChange(cPlayer & a_Player, int a_NewFoodLevel) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_FOOD_LEVEL_CHANGE]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Player, a_NewFoodLevel, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_PLAYER_FOOD_LEVEL_CHANGE, &a_Player, a_NewFoodLevel); } @@ -1024,22 +603,7 @@ bool cPluginLua::OnPlayerFoodLevelChange(cPlayer & a_Player, int a_NewFoodLevel) bool cPluginLua::OnPlayerFished(cPlayer & a_Player, const cItems & a_Reward) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_FISHED]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Player, a_Reward, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_PLAYER_FISHED, &a_Player, a_Reward); } @@ -1048,22 +612,7 @@ bool cPluginLua::OnPlayerFished(cPlayer & a_Player, const cItems & a_Reward) bool cPluginLua::OnPlayerFishing(cPlayer & a_Player, cItems & a_Reward) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_FISHING]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Player, &a_Reward, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_PLAYER_FISHING, &a_Player, &a_Reward); } @@ -1072,22 +621,7 @@ bool cPluginLua::OnPlayerFishing(cPlayer & a_Player, cItems & a_Reward) bool cPluginLua::OnPlayerJoined(cPlayer & a_Player) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_JOINED]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Player, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_PLAYER_JOINED, &a_Player); } @@ -1096,22 +630,7 @@ bool cPluginLua::OnPlayerJoined(cPlayer & a_Player) bool cPluginLua::OnPlayerLeftClick(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_LEFT_CLICK]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_Status, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_PLAYER_LEFT_CLICK, &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_Status); } @@ -1120,22 +639,7 @@ bool cPluginLua::OnPlayerLeftClick(cPlayer & a_Player, int a_BlockX, int a_Block bool cPluginLua::OnPlayerMoving(cPlayer & a_Player, const Vector3d & a_OldPosition, const Vector3d & a_NewPosition) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_MOVING]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Player, a_OldPosition, a_NewPosition, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_PLAYER_MOVING, &a_Player, a_OldPosition, a_NewPosition); } @@ -1144,22 +648,7 @@ bool cPluginLua::OnPlayerMoving(cPlayer & a_Player, const Vector3d & a_OldPositi bool cPluginLua::OnEntityTeleport(cEntity & a_Entity, const Vector3d & a_OldPosition, const Vector3d & a_NewPosition) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_ENTITY_TELEPORT]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Entity, a_OldPosition, a_NewPosition, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_ENTITY_TELEPORT, &a_Entity, a_OldPosition, a_NewPosition); } @@ -1168,27 +657,11 @@ bool cPluginLua::OnEntityTeleport(cEntity & a_Entity, const Vector3d & a_OldPosi bool cPluginLua::OnPlayerPlacedBlock(cPlayer & a_Player, const sSetBlock & a_BlockChange) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_PLACED_BLOCK]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Player, - a_BlockChange.GetX(), a_BlockChange.GetY(), a_BlockChange.GetZ(), - a_BlockChange.m_BlockType, a_BlockChange.m_BlockMeta, - cLuaState::Return, - res - ); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_PLAYER_PLACED_BLOCK, + &a_Player, + a_BlockChange.GetX(), a_BlockChange.GetY(), a_BlockChange.GetZ(), + a_BlockChange.m_BlockType, a_BlockChange.m_BlockMeta + ); } @@ -1197,27 +670,11 @@ bool cPluginLua::OnPlayerPlacedBlock(cPlayer & a_Player, const sSetBlock & a_Blo bool cPluginLua::OnPlayerPlacingBlock(cPlayer & a_Player, const sSetBlock & a_BlockChange) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_PLACING_BLOCK]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Player, - a_BlockChange.GetX(), a_BlockChange.GetY(), a_BlockChange.GetZ(), - a_BlockChange.m_BlockType, a_BlockChange.m_BlockMeta, - cLuaState::Return, - res - ); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_PLAYER_PLACING_BLOCK, + &a_Player, + a_BlockChange.GetX(), a_BlockChange.GetY(), a_BlockChange.GetZ(), + a_BlockChange.m_BlockType, a_BlockChange.m_BlockMeta + ); } @@ -1226,22 +683,7 @@ bool cPluginLua::OnPlayerPlacingBlock(cPlayer & a_Player, const sSetBlock & a_Bl bool cPluginLua::OnPlayerRightClick(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_RIGHT_CLICK]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_PLAYER_RIGHT_CLICK, &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ); } @@ -1250,22 +692,7 @@ bool cPluginLua::OnPlayerRightClick(cPlayer & a_Player, int a_BlockX, int a_Bloc bool cPluginLua::OnPlayerRightClickingEntity(cPlayer & a_Player, cEntity & a_Entity) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_RIGHT_CLICKING_ENTITY]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Player, &a_Entity, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_PLAYER_RIGHT_CLICKING_ENTITY, &a_Player, &a_Entity); } @@ -1274,22 +701,7 @@ bool cPluginLua::OnPlayerRightClickingEntity(cPlayer & a_Player, cEntity & a_Ent bool cPluginLua::OnPlayerShooting(cPlayer & a_Player) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_SHOOTING]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Player, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_PLAYER_SHOOTING, &a_Player); } @@ -1298,22 +710,7 @@ bool cPluginLua::OnPlayerShooting(cPlayer & a_Player) bool cPluginLua::OnPlayerSpawned(cPlayer & a_Player) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_SPAWNED]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Player, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_PLAYER_SPAWNED, &a_Player); } @@ -1322,22 +719,7 @@ bool cPluginLua::OnPlayerSpawned(cPlayer & a_Player) bool cPluginLua::OnPlayerTossingItem(cPlayer & a_Player) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_TOSSING_ITEM]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Player, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_PLAYER_TOSSING_ITEM, &a_Player); } @@ -1346,22 +728,7 @@ bool cPluginLua::OnPlayerTossingItem(cPlayer & a_Player) bool cPluginLua::OnPlayerUsedBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_USED_BLOCK]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, a_BlockType, a_BlockMeta, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_PLAYER_USED_BLOCK, &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, a_BlockType, a_BlockMeta); } @@ -1370,22 +737,7 @@ bool cPluginLua::OnPlayerUsedBlock(cPlayer & a_Player, int a_BlockX, int a_Block bool cPluginLua::OnPlayerUsedItem(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_USED_ITEM]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_PLAYER_USED_ITEM, &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ); } @@ -1394,22 +746,7 @@ bool cPluginLua::OnPlayerUsedItem(cPlayer & a_Player, int a_BlockX, int a_BlockY bool cPluginLua::OnPlayerUsingBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_USING_BLOCK]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, a_BlockType, a_BlockMeta, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_PLAYER_USING_BLOCK, &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, a_BlockType, a_BlockMeta); } @@ -1418,22 +755,7 @@ bool cPluginLua::OnPlayerUsingBlock(cPlayer & a_Player, int a_BlockX, int a_Bloc bool cPluginLua::OnPlayerUsingItem(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_USING_ITEM]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_PLAYER_USING_ITEM, &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ); } @@ -1442,22 +764,7 @@ bool cPluginLua::OnPlayerUsingItem(cPlayer & a_Player, int a_BlockX, int a_Block bool cPluginLua::OnPluginMessage(cClientHandle & a_Client, const AString & a_Channel, const AString & a_Message) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLUGIN_MESSAGE]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Client, a_Channel, a_Message, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_PLUGIN_MESSAGE, &a_Client, a_Channel, a_Message); } @@ -1466,17 +773,17 @@ bool cPluginLua::OnPluginMessage(cClientHandle & a_Client, const AString & a_Cha bool cPluginLua::OnPluginsLoaded(void) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) + cOperation op(*this); + if (!op().IsValid()) { return false; } bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLUGINS_LOADED]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + auto & hooks = m_HookMap[cPluginManager::HOOK_PLUGINS_LOADED]; + for (auto & hook: hooks) { bool ret = false; - m_LuaState.Call(static_cast<int>(**itr), cLuaState::Return, ret); + hook->Call(cLuaState::Return, ret); res = res || ret; } return res; @@ -1488,22 +795,7 @@ bool cPluginLua::OnPluginsLoaded(void) bool cPluginLua::OnPostCrafting(cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe & a_Recipe) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_POST_CRAFTING]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Player, &a_Grid, &a_Recipe, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_POST_CRAFTING, &a_Player, &a_Grid, &a_Recipe); } @@ -1512,22 +804,7 @@ bool cPluginLua::OnPostCrafting(cPlayer & a_Player, cCraftingGrid & a_Grid, cCra bool cPluginLua::OnPreCrafting(cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe & a_Recipe) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PRE_CRAFTING]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Player, &a_Grid, &a_Recipe, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_PRE_CRAFTING, &a_Player, &a_Grid, &a_Recipe); } @@ -1536,22 +813,7 @@ bool cPluginLua::OnPreCrafting(cPlayer & a_Player, cCraftingGrid & a_Grid, cCraf bool cPluginLua::OnProjectileHitBlock(cProjectileEntity & a_Projectile, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_Face, const Vector3d & a_BlockHitPos) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PROJECTILE_HIT_BLOCK]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Projectile, a_BlockX, a_BlockY, a_BlockZ, a_Face, a_BlockHitPos, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_PROJECTILE_HIT_BLOCK, &a_Projectile, a_BlockX, a_BlockY, a_BlockZ, a_Face, a_BlockHitPos); } @@ -1560,22 +822,7 @@ bool cPluginLua::OnProjectileHitBlock(cProjectileEntity & a_Projectile, int a_Bl bool cPluginLua::OnProjectileHitEntity(cProjectileEntity & a_Projectile, cEntity & a_HitEntity) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PROJECTILE_HIT_ENTITY]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Projectile, &a_HitEntity, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_PROJECTILE_HIT_ENTITY, &a_Projectile, &a_HitEntity); } @@ -1584,16 +831,16 @@ bool cPluginLua::OnProjectileHitEntity(cProjectileEntity & a_Projectile, cEntity bool cPluginLua::OnServerPing(cClientHandle & a_ClientHandle, AString & a_ServerDescription, int & a_OnlinePlayersCount, int & a_MaxPlayersCount, AString & a_Favicon) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) + cOperation op(*this); + if (!op().IsValid()) { return false; } bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_SERVER_PING]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + auto & hooks = m_HookMap[cPluginManager::HOOK_SERVER_PING]; + for (auto & hook: hooks) { - m_LuaState.Call(static_cast<int>(**itr), &a_ClientHandle, a_ServerDescription, a_OnlinePlayersCount, a_MaxPlayersCount, a_Favicon, cLuaState::Return, res, a_ServerDescription, a_OnlinePlayersCount, a_MaxPlayersCount, a_Favicon); + hook->Call(&a_ClientHandle, a_ServerDescription, a_OnlinePlayersCount, a_MaxPlayersCount, a_Favicon, cLuaState::Return, res, a_ServerDescription, a_OnlinePlayersCount, a_MaxPlayersCount, a_Favicon); if (res) { return true; @@ -1608,22 +855,7 @@ bool cPluginLua::OnServerPing(cClientHandle & a_ClientHandle, AString & a_Server bool cPluginLua::OnSpawnedEntity(cWorld & a_World, cEntity & a_Entity) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_SPAWNED_ENTITY]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_World, &a_Entity, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_SPAWNED_ENTITY, &a_World, &a_Entity); } @@ -1632,22 +864,7 @@ bool cPluginLua::OnSpawnedEntity(cWorld & a_World, cEntity & a_Entity) bool cPluginLua::OnSpawnedMonster(cWorld & a_World, cMonster & a_Monster) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_SPAWNED_MONSTER]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_World, &a_Monster, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_SPAWNED_MONSTER, &a_World, &a_Monster); } @@ -1656,22 +873,7 @@ bool cPluginLua::OnSpawnedMonster(cWorld & a_World, cMonster & a_Monster) bool cPluginLua::OnSpawningEntity(cWorld & a_World, cEntity & a_Entity) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_SPAWNING_ENTITY]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_World, &a_Entity, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_SPAWNING_ENTITY, &a_World, &a_Entity); } @@ -1680,22 +882,7 @@ bool cPluginLua::OnSpawningEntity(cWorld & a_World, cEntity & a_Entity) bool cPluginLua::OnSpawningMonster(cWorld & a_World, cMonster & a_Monster) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_SPAWNING_MONSTER]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_World, &a_Monster, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_SPAWNING_MONSTER, &a_World, &a_Monster); } @@ -1704,22 +891,7 @@ bool cPluginLua::OnSpawningMonster(cWorld & a_World, cMonster & a_Monster) bool cPluginLua::OnTakeDamage(cEntity & a_Receiver, TakeDamageInfo & a_TDI) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_TAKE_DAMAGE]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Receiver, &a_TDI, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_TAKE_DAMAGE, &a_Receiver, &a_TDI); } @@ -1733,22 +905,7 @@ bool cPluginLua::OnUpdatedSign( cPlayer * a_Player ) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_UPDATED_SIGN]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_World, a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4, a_Player, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_UPDATED_SIGN, &a_World, a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4, a_Player); } @@ -1762,16 +919,16 @@ bool cPluginLua::OnUpdatingSign( cPlayer * a_Player ) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) + cOperation op(*this); + if (!op().IsValid()) { return false; } bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_UPDATING_SIGN]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + auto & hooks = m_HookMap[cPluginManager::HOOK_UPDATING_SIGN]; + for (auto & hook: hooks) { - m_LuaState.Call(static_cast<int>(**itr), &a_World, a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4, a_Player, cLuaState::Return, res, a_Line1, a_Line2, a_Line3, a_Line4); + hook->Call(&a_World, a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4, a_Player, cLuaState::Return, res, a_Line1, a_Line2, a_Line3, a_Line4); if (res) { return true; @@ -1786,22 +943,7 @@ bool cPluginLua::OnUpdatingSign( bool cPluginLua::OnWeatherChanged(cWorld & a_World) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_WEATHER_CHANGED]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_World, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_WEATHER_CHANGED, &a_World); } @@ -1810,16 +952,16 @@ bool cPluginLua::OnWeatherChanged(cWorld & a_World) bool cPluginLua::OnWeatherChanging(cWorld & a_World, eWeather & a_NewWeather) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) + cOperation op(*this); + if (!op().IsValid()) { return false; } bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_WEATHER_CHANGING]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + auto & hooks = m_HookMap[cPluginManager::HOOK_WEATHER_CHANGING]; + for (auto & hook: hooks) { - m_LuaState.Call(static_cast<int>(**itr), &a_World, a_NewWeather, cLuaState::Return, res, a_NewWeather); + hook->Call(&a_World, a_NewWeather, cLuaState::Return, res, a_NewWeather); if (res) { return true; @@ -1834,17 +976,7 @@ bool cPluginLua::OnWeatherChanging(cWorld & a_World, eWeather & a_NewWeather) bool cPluginLua::OnWorldStarted(cWorld & a_World) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_WORLD_STARTED]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_World); - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_WORLD_STARTED, &a_World); } @@ -1853,102 +985,7 @@ bool cPluginLua::OnWorldStarted(cWorld & a_World) bool cPluginLua::OnWorldTick(cWorld & a_World, std::chrono::milliseconds a_Dt, std::chrono::milliseconds a_LastTickDurationMSec) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_WORLD_TICK]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_World, a_Dt, a_LastTickDurationMSec); - } - return false; -} - - - - - -bool cPluginLua::HandleCommand(const AStringVector & a_Split, cPlayer & a_Player, const AString & a_FullCommand) -{ - ASSERT(!a_Split.empty()); - CommandMap::iterator cmd = m_Commands.find(a_Split[0]); - if (cmd == m_Commands.end()) - { - LOGWARNING("Command handler is registered in cPluginManager but not in cPlugin, wtf? Command \"%s\".", a_Split[0].c_str()); - return false; - } - - cCSLock Lock(m_CriticalSection); - bool res = false; - m_LuaState.Call(cmd->second, a_Split, &a_Player, a_FullCommand, cLuaState::Return, res); - return res; -} - - - - - -bool cPluginLua::HandleConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output, const AString & a_FullCommand) -{ - ASSERT(!a_Split.empty()); - CommandMap::iterator cmd = m_ConsoleCommands.find(a_Split[0]); - if (cmd == m_ConsoleCommands.end()) - { - LOGWARNING("Console command handler is registered in cPluginManager but not in cPlugin, wtf? Console command \"%s\", plugin \"%s\".", - a_Split[0].c_str(), GetName().c_str() - ); - return false; - } - - cCSLock Lock(m_CriticalSection); - bool res = false; - AString str; - m_LuaState.Call(cmd->second, a_Split, a_FullCommand, cLuaState::Return, res, str); - if (res && !str.empty()) - { - a_Output.Out(str); - } - return res; -} - - - - - -void cPluginLua::ClearCommands(void) -{ - cCSLock Lock(m_CriticalSection); - - // Unreference the bound functions so that Lua can GC them - if (m_LuaState != nullptr) - { - for (CommandMap::iterator itr = m_Commands.begin(), end = m_Commands.end(); itr != end; ++itr) - { - luaL_unref(m_LuaState, LUA_REGISTRYINDEX, itr->second); - } - } - m_Commands.clear(); -} - - - - - -void cPluginLua::ClearConsoleCommands(void) -{ - cCSLock Lock(m_CriticalSection); - - // Unreference the bound functions so that Lua can GC them - if (m_LuaState != nullptr) - { - for (CommandMap::iterator itr = m_ConsoleCommands.begin(), end = m_ConsoleCommands.end(); itr != end; ++itr) - { - luaL_unref(m_LuaState, LUA_REGISTRYINDEX, itr->second); - } - } - m_ConsoleCommands.clear(); + return CallSimpleHooks(cPluginManager::HOOK_WORLD_TICK, &a_World, a_Dt, a_LastTickDurationMSec); } @@ -2059,22 +1096,9 @@ const char * cPluginLua::GetHookFnName(int a_HookType) -bool cPluginLua::AddHookRef(int a_HookType, int a_FnRefIdx) +bool cPluginLua::AddHookCallback(int a_HookType, cLuaState::cCallbackPtr && a_Callback) { - ASSERT(m_CriticalSection.IsLockedByCurrentThread()); // It probably has to be, how else would we have a LuaState? - - // Check if the function reference is valid: - cLuaState::cRef * Ref = new cLuaState::cRef(m_LuaState, a_FnRefIdx); - if ((Ref == nullptr) || !Ref->IsValid()) - { - LOGWARNING("Plugin %s tried to add a hook %d with bad handler function.", GetName().c_str(), a_HookType); - m_LuaState.LogStackTrace(); - delete Ref; - Ref = nullptr; - return false; - } - - m_HookMap[a_HookType].push_back(Ref); + m_HookMap[a_HookType].push_back(std::move(a_Callback)); return true; } @@ -2089,10 +1113,10 @@ int cPluginLua::CallFunctionFromForeignState( int a_ParamEnd ) { - cCSLock Lock(m_CriticalSection); + cOperation op(*this); // Call the function: - int NumReturns = m_LuaState.CallFunctionWithForeignParams(a_FunctionName, a_ForeignState, a_ParamStart, a_ParamEnd); + int NumReturns = op().CallFunctionWithForeignParams(a_FunctionName, a_ForeignState, a_ParamStart, a_ParamEnd); if (NumReturns < 0) { // The call has failed, an error has already been output to the log, so just silently bail out with the same error @@ -2116,133 +1140,13 @@ int cPluginLua::CallFunctionFromForeignState( -void cPluginLua::AddResettable(cPluginLua::cResettablePtr a_Resettable) -{ - cCSLock Lock(m_CriticalSection); - m_Resettables.push_back(a_Resettable); -} - - - - - -AString cPluginLua::HandleWebRequest(const HTTPRequest & a_Request) -{ - // Find the tab to use for the request: - auto TabName = GetTabNameForRequest(a_Request); - AString SafeTabTitle = TabName.second; - if (SafeTabTitle.empty()) - { - return ""; - } - auto Tab = GetTabBySafeTitle(SafeTabTitle); - if (Tab == nullptr) - { - return ""; - } - - // Get the page content from the plugin: - cCSLock Lock(m_CriticalSection); - AString Contents = Printf("WARNING: WebPlugin tab '%s' did not return a string!", Tab->m_Title.c_str()); - if (!m_LuaState.Call(Tab->m_UserData, &a_Request, cLuaState::Return, Contents)) - { - return "Lua encountered error while processing the page request"; - } - return Contents; -} - - - - - -bool cPluginLua::AddWebTab(const AString & a_Title, lua_State * a_LuaState, int a_FunctionReference) +void cPluginLua::ClearWebTabs(void) { - cCSLock Lock(m_CriticalSection); - if (a_LuaState != m_LuaState) + auto webAdmin = cRoot::Get()->GetWebAdmin(); + if (webAdmin != nullptr) // can be nullptr when shutting down the server { - LOGERROR("Only allowed to add a tab to a WebPlugin of your own Plugin!"); - return false; + webAdmin->RemoveAllPluginWebTabs(m_Name); } - AddNewWebTab(a_Title, a_FunctionReference); - return true; -} - - - - - -void cPluginLua::BindCommand(const AString & a_Command, int a_FnRef) -{ - ASSERT(m_Commands.find(a_Command) == m_Commands.end()); - m_Commands[a_Command] = a_FnRef; -} - - - - - -void cPluginLua::BindConsoleCommand(const AString & a_Command, int a_FnRef) -{ - ASSERT(m_ConsoleCommands.find(a_Command) == m_ConsoleCommands.end()); - m_ConsoleCommands[a_Command] = a_FnRef; -} - - - - - -void cPluginLua::Unreference(int a_LuaRef) -{ - cCSLock Lock(m_CriticalSection); - luaL_unref(m_LuaState, LUA_REGISTRYINDEX, a_LuaRef); -} - - - - - -bool cPluginLua::CallbackWindowClosing(int a_FnRef, cWindow & a_Window, cPlayer & a_Player, bool a_CanRefuse) -{ - ASSERT(a_FnRef != LUA_REFNIL); - - cCSLock Lock(m_CriticalSection); - bool res = false; - m_LuaState.Call(a_FnRef, &a_Window, &a_Player, a_CanRefuse, cLuaState::Return, res); - return res; -} - - - - - -void cPluginLua::CallbackWindowSlotChanged(int a_FnRef, cWindow & a_Window, int a_SlotNum) -{ - ASSERT(a_FnRef != LUA_REFNIL); - - cCSLock Lock(m_CriticalSection); - m_LuaState.Call(a_FnRef, &a_Window, a_SlotNum); -} - - - - - -//////////////////////////////////////////////////////////////////////////////// -// cPluginLua::cResettable: - -cPluginLua::cResettable::cResettable(cPluginLua & a_Plugin): - m_Plugin(&a_Plugin) -{ -} - - - - - -void cPluginLua::cResettable::Reset(void) -{ - cCSLock Lock(m_CSPlugin); - m_Plugin = nullptr; } diff --git a/src/Bindings/PluginLua.h b/src/Bindings/PluginLua.h index db6612671..dc3c91880 100644 --- a/src/Bindings/PluginLua.h +++ b/src/Bindings/PluginLua.h @@ -10,7 +10,6 @@ #pragma once #include "Plugin.h" -#include "WebPlugin.h" #include "LuaState.h" // Names for the global variables through which the plugin is identified in its LuaState @@ -29,8 +28,7 @@ class cWindow; // tolua_begin class cPluginLua : - public cPlugin, - public cWebPlugin + public cPlugin { typedef cPlugin super; @@ -49,7 +47,7 @@ public: public: cOperation(cPluginLua & a_Plugin) : m_Plugin(a_Plugin), - m_Lock(a_Plugin.m_CriticalSection) + m_Lock(a_Plugin.m_LuaState) { } @@ -58,42 +56,12 @@ public: protected: cPluginLua & m_Plugin; - /** RAII lock for m_Plugin.m_CriticalSection */ - cCSLock m_Lock; + /** RAII lock for the Lua state. */ + cLuaState::cLock m_Lock; } ; - /** A base class that represents something related to a plugin - The plugin can reset this class so that the instance can continue to exist but will not engage the (possibly non-existent) plugin anymore. - This is used for scheduled tasks etc., so that they can be queued and reset when the plugin is terminated, without removing them from the queue. */ - class cResettable - { - public: - /** Creates a new instance bound to the specified plugin. */ - cResettable(cPluginLua & a_Plugin); - - // Force a virtual destructor in descendants: - virtual ~cResettable() {} - - /** Resets the plugin instance stored within. - The instance will continue to exist, but should not call into the plugin anymore. */ - virtual void Reset(void); - - protected: - /** The plugin that this instance references. - If nullptr, the plugin has already unloaded and the instance should bail out any processing. - Protected against multithreaded access by m_CSPlugin. */ - cPluginLua * m_Plugin; - - /** The mutex protecting m_Plugin against multithreaded access. */ - cCriticalSection m_CSPlugin; - }; - - typedef SharedPtr<cResettable> cResettablePtr; - typedef std::vector<cResettablePtr> cResettablePtrs; - - cPluginLua(const AString & a_PluginDirectory); ~cPluginLua(); @@ -170,52 +138,15 @@ public: virtual bool OnWorldStarted (cWorld & a_World) override; virtual bool OnWorldTick (cWorld & a_World, std::chrono::milliseconds a_Dt, std::chrono::milliseconds a_LastTickDurationMSec) override; - virtual bool HandleCommand(const AStringVector & a_Split, cPlayer & a_Player, const AString & a_FullCommand) override; - - virtual bool HandleConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output, const AString & a_FullCommand) override; - - virtual void ClearCommands(void) override; - - virtual void ClearConsoleCommands(void) override; - /** Returns true if the plugin contains the function for the specified hook type, using the old-style registration (#121) */ bool CanAddOldStyleHook(int a_HookType); - // cWebPlugin overrides - virtual const AString GetWebTitle(void) const override {return GetName(); } - virtual AString HandleWebRequest(const HTTPRequest & a_Request) override; - - /** Adds a new web tab to webadmin. - Displaying the tab calls the referenced function. */ - bool AddWebTab(const AString & a_Title, lua_State * a_LuaState, int a_FunctionReference); // Exported in ManualBindings.cpp - - /** Binds the command to call the function specified by a Lua function reference. Simply adds to CommandMap. */ - void BindCommand(const AString & a_Command, int a_FnRef); - - /** Binds the console command to call the function specified by a Lua function reference. Simply adds to CommandMap. */ - void BindConsoleCommand(const AString & a_Command, int a_FnRef); - - cLuaState & GetLuaState(void) { return m_LuaState; } - - cCriticalSection & GetCriticalSection(void) { return m_CriticalSection; } - - /** Removes a previously referenced object (luaL_unref()) */ - void Unreference(int a_LuaRef); - - /** Calls the plugin-specified "cLuaWindow closing" callback. Returns true only if the callback returned true */ - bool CallbackWindowClosing(int a_FnRef, cWindow & a_Window, cPlayer & a_Player, bool a_CanRefuse); - - /** Calls the plugin-specified "cLuaWindow slot changed" callback. */ - void CallbackWindowSlotChanged(int a_FnRef, cWindow & a_Window, int a_SlotNum); - /** Returns the name of Lua function that should handle the specified hook type in the older (#121) API */ static const char * GetHookFnName(int a_HookType); - /** Adds a Lua function to be called for the specified hook. - The function has to be on the Lua stack at the specified index a_FnRefIdx - Returns true if the hook was added successfully. - */ - bool AddHookRef(int a_HookType, int a_FnRefIdx); + /** Adds a Lua callback to be called for the specified hook. + Returns true if the hook was added successfully. */ + bool AddHookCallback(int a_HookType, cLuaState::cCallbackPtr && a_Callback); /** Calls a function in this plugin's LuaState with parameters copied over from a_ForeignState. The values that the function returns are placed onto a_ForeignState. @@ -231,45 +162,50 @@ public: template <typename FnT, typename... Args> bool Call(FnT a_Fn, Args && ... a_Args) { - cCSLock Lock(m_CriticalSection); - return m_LuaState.Call(a_Fn, a_Args...); + return cOperation(*this)().Call(a_Fn, a_Args...); } - /** Adds the specified cResettable instance to m_Resettables, so that it is notified when the plugin is being closed. */ - void AddResettable(cResettablePtr a_Resettable); - protected: - /** Maps command name into Lua function reference */ - typedef std::map<AString, int> CommandMap; - /** Provides an array of Lua function references */ - typedef std::vector<cLuaState::cRef *> cLuaRefs; + typedef std::vector<cLuaState::cCallbackPtr> cLuaCallbacks; /** Maps hook types into arrays of Lua function references to call for each hook type */ - typedef std::map<int, cLuaRefs> cHookMap; - + typedef std::map<int, cLuaCallbacks> cHookMap; - /** The mutex protecting m_LuaState and each of the m_Resettables[] against multithreaded use. */ - cCriticalSection m_CriticalSection; /** The plugin's Lua state. */ cLuaState m_LuaState; - /** Objects that need notification when the plugin is about to be unloaded. */ - cResettablePtrs m_Resettables; - - /** In-game commands that the plugin has registered. */ - CommandMap m_Commands; - - /** Console commands that the plugin has registered. */ - CommandMap m_ConsoleCommands; - /** Hooks that the plugin has registered. */ cHookMap m_HookMap; /** Releases all Lua references, notifies and removes all m_Resettables[] and closes the m_LuaState. */ void Close(void); + + /** Removes all WebTabs currently registered for this plugin from the WebAdmin. */ + void ClearWebTabs(void); + + /** Calls a hook that has the simple format - single bool return value specifying whether the chain should continue. + The advanced hook types that need more processing implement a similar loop manually instead. + Returns true if any of hook calls wants to abort the hook (returned true), false if all hook calls returned false. */ + template <typename... Args> + bool CallSimpleHooks(int a_HookType, Args && ... a_Args) + { + cOperation op(*this); + auto & hooks = m_HookMap[a_HookType]; + bool res = false; + for (auto & hook: hooks) + { + hook->Call(std::forward<Args>(a_Args)..., cLuaState::Return, res); + if (res) + { + // Hook wants to terminate the chain processing + return true; + } + } + return false; + } } ; // tolua_export diff --git a/src/Bindings/PluginManager.cpp b/src/Bindings/PluginManager.cpp index 5b3ef7803..19d2e8b4d 100644 --- a/src/Bindings/PluginManager.cpp +++ b/src/Bindings/PluginManager.cpp @@ -1569,9 +1569,9 @@ cPluginManager::CommandResult cPluginManager::HandleCommand(cPlayer & a_Player, return crNoPermission; } - ASSERT(cmd->second.m_Plugin != nullptr); + ASSERT(cmd->second.m_Handler != nullptr); - if (!cmd->second.m_Plugin->HandleCommand(Split, a_Player, a_Command)) + if (!cmd->second.m_Handler->ExecuteCommand(Split, &a_Player, a_Command, nullptr)) { return crError; } @@ -1654,11 +1654,6 @@ void cPluginManager::RemoveHooks(cPlugin * a_Plugin) void cPluginManager::RemovePluginCommands(cPlugin * a_Plugin) { - if (a_Plugin != nullptr) - { - a_Plugin->ClearCommands(); - } - for (CommandMap::iterator itr = m_Commands.begin(); itr != m_Commands.end();) { if (itr->second.m_Plugin == a_Plugin) @@ -1694,7 +1689,13 @@ bool cPluginManager::IsPluginLoaded(const AString & a_PluginName) -bool cPluginManager::BindCommand(const AString & a_Command, cPlugin * a_Plugin, const AString & a_Permission, const AString & a_HelpString) +bool cPluginManager::BindCommand( + const AString & a_Command, + cPlugin * a_Plugin, + cCommandHandlerPtr a_Handler, + const AString & a_Permission, + const AString & a_HelpString +) { CommandMap::iterator cmd = m_Commands.find(a_Command); if (cmd != m_Commands.end()) @@ -1703,9 +1704,11 @@ bool cPluginManager::BindCommand(const AString & a_Command, cPlugin * a_Plugin, return false; } - m_Commands[a_Command].m_Plugin = a_Plugin; - m_Commands[a_Command].m_Permission = a_Permission; - m_Commands[a_Command].m_HelpString = a_HelpString; + auto & reg = m_Commands[a_Command]; + reg.m_Plugin = a_Plugin; + reg.m_Handler = a_Handler; + reg.m_Permission = a_Permission; + reg.m_HelpString = a_HelpString; return true; } @@ -1768,11 +1771,6 @@ cPluginManager::CommandResult cPluginManager::ForceExecuteCommand(cPlayer & a_Pl void cPluginManager::RemovePluginConsoleCommands(cPlugin * a_Plugin) { - if (a_Plugin != nullptr) - { - a_Plugin->ClearConsoleCommands(); - } - for (CommandMap::iterator itr = m_ConsoleCommands.begin(); itr != m_ConsoleCommands.end();) { if (itr->second.m_Plugin == a_Plugin) @@ -1792,7 +1790,12 @@ void cPluginManager::RemovePluginConsoleCommands(cPlugin * a_Plugin) -bool cPluginManager::BindConsoleCommand(const AString & a_Command, cPlugin * a_Plugin, const AString & a_HelpString) +bool cPluginManager::BindConsoleCommand( + const AString & a_Command, + cPlugin * a_Plugin, + cCommandHandlerPtr a_Handler, + const AString & a_HelpString +) { CommandMap::iterator cmd = m_ConsoleCommands.find(a_Command); if (cmd != m_ConsoleCommands.end()) @@ -1808,9 +1811,11 @@ bool cPluginManager::BindConsoleCommand(const AString & a_Command, cPlugin * a_P return false; } - m_ConsoleCommands[a_Command].m_Plugin = a_Plugin; - m_ConsoleCommands[a_Command].m_Permission = ""; - m_ConsoleCommands[a_Command].m_HelpString = a_HelpString; + auto & reg = m_ConsoleCommands[a_Command]; + reg.m_Plugin = a_Plugin; + reg.m_Handler = a_Handler; + reg.m_Permission = ""; + reg.m_HelpString = a_HelpString; return true; } @@ -1873,7 +1878,7 @@ bool cPluginManager::ExecuteConsoleCommand(const AStringVector & a_Split, cComma return (res == crExecuted); } - return cmd->second.m_Plugin->HandleConsoleCommand(a_Split, a_Output, a_Command); + return cmd->second.m_Handler->ExecuteCommand(a_Split, nullptr, a_Command, &a_Output); } @@ -1965,6 +1970,23 @@ bool cPluginManager::ForEachPlugin(cPluginCallback & a_Callback) +AString cPluginManager::GetPluginFolderName(const AString & a_PluginName) +{ + // TODO: Implement locking for plugins + for (auto & plugin: m_Plugins) + { + if (plugin->GetName() == a_PluginName) + { + return plugin->GetFolderName(); + } + } + return AString(); +} + + + + + void cPluginManager::AddHook(cPlugin * a_Plugin, int a_Hook) { if (a_Plugin == nullptr) diff --git a/src/Bindings/PluginManager.h b/src/Bindings/PluginManager.h index f3f0b6d0b..0423d6af1 100644 --- a/src/Bindings/PluginManager.h +++ b/src/Bindings/PluginManager.h @@ -152,6 +152,7 @@ public: HOOK_MAX = HOOK_NUM_HOOKS - 1, } ; // tolua_export + /** Used as a callback for enumerating bound commands */ class cCommandEnumCallback { @@ -164,6 +165,30 @@ public: virtual bool Command(const AString & a_Command, const cPlugin * a_Plugin, const AString & a_Permission, const AString & a_HelpString) = 0; } ; + + /** Interface that must be provided by any class that implements a command handler, either in-game or console command. */ + class cCommandHandler + { + public: + // Force a virtual destructor in descendants + virtual ~cCommandHandler() {} + + /** Executes the specified in-game command. + a_Split is the command string, split at the spaces. + a_Player is the player executing the command, nullptr in case of the console. + a_Command is the entire command string. + a_Output is the sink into which the additional text returned by the command handler should be sent; only used for console commands. */ + virtual bool ExecuteCommand( + const AStringVector & a_Split, + cPlayer * a_Player, + const AString & a_Command, + cCommandOutputCallback * a_Output = nullptr + ) = 0; + }; + + typedef SharedPtr<cCommandHandler> cCommandHandlerPtr; + + /** The interface used for enumerating and extern-calling plugins */ typedef cItemCallback<cPlugin> cPluginCallback; @@ -281,8 +306,16 @@ public: /** Returns true if the specified plugin is loaded. */ bool IsPluginLoaded(const AString & a_PluginName); // tolua_export - /** Binds a command to the specified plugin. Returns true if successful, false if command already bound. */ - bool BindCommand(const AString & a_Command, cPlugin * a_Plugin, const AString & a_Permission, const AString & a_HelpString); // Exported in ManualBindings.cpp, without the a_Plugin param + /** Binds a command to the specified handler. + Returns true if successful, false if command already bound. + Exported in ManualBindings.cpp. */ + bool BindCommand( + const AString & a_Command, + cPlugin * a_Plugin, + cCommandHandlerPtr a_Handler, + const AString & a_Permission, + const AString & a_HelpString + ); /** Calls a_Callback for each bound command, returns true if all commands were enumerated */ bool ForEachCommand(cCommandEnumCallback & a_Callback); // Exported in ManualBindings.cpp @@ -302,8 +335,15 @@ public: /** Removes all console command bindings that the specified plugin has made */ void RemovePluginConsoleCommands(cPlugin * a_Plugin); - /** Binds a console command to the specified plugin. Returns true if successful, false if command already bound. */ - bool BindConsoleCommand(const AString & a_Command, cPlugin * a_Plugin, const AString & a_HelpString); // Exported in ManualBindings.cpp, without the a_Plugin param + /** Binds a console command to the specified handler. + Returns true if successful, false if command already bound. + Exported in ManualBindings.cpp. */ + bool BindConsoleCommand( + const AString & a_Command, + cPlugin * a_Plugin, + cCommandHandlerPtr a_Handler, + const AString & a_HelpString + ); /** Calls a_Callback for each bound console command, returns true if all commands were enumerated */ bool ForEachConsoleCommand(cCommandEnumCallback & a_Callback); // Exported in ManualBindings.cpp @@ -332,6 +372,9 @@ public: Returns true if all plugins have been reported, false if the callback has aborted the enumeration by returning true. */ bool ForEachPlugin(cPluginCallback & a_Callback); + /** Returns the name of the folder (cPlugin::GetFolderName()) from which the specified plugin was loaded. */ + AString GetPluginFolderName(const AString & a_PluginName); // tolua_export + /** Returns the path where individual plugins' folders are expected. The path doesn't end in a slash. */ static AString GetPluginsPath(void) { return FILE_IO_PREFIX + AString("Plugins"); } // tolua_export @@ -345,6 +388,7 @@ private: cPlugin * m_Plugin; AString m_Permission; // Not used for console commands AString m_HelpString; + cCommandHandlerPtr m_Handler; } ; typedef std::map<int, cPluginManager::PluginList> HookMap; diff --git a/src/Bindings/WebPlugin.cpp b/src/Bindings/WebPlugin.cpp deleted file mode 100644 index 1eca7de93..000000000 --- a/src/Bindings/WebPlugin.cpp +++ /dev/null @@ -1,152 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "WebPlugin.h" -#include "../WebAdmin.h" -#include "../Root.h" - - - - - -cWebPlugin::cWebPlugin() -{ - cWebAdmin * WebAdmin = cRoot::Get()->GetWebAdmin(); - if (WebAdmin != nullptr) - { - WebAdmin->AddPlugin(this); - } -} - - - - - -cWebPlugin::~cWebPlugin() -{ - ASSERT(m_Tabs.empty()); // Has ClearTabs() been called? - - // Remove from WebAdmin: - cWebAdmin * WebAdmin = cRoot::Get()->GetWebAdmin(); - if (WebAdmin != nullptr) - { - WebAdmin->RemovePlugin(this); - } -} - - - - - -cWebPlugin::cTabNames cWebPlugin::GetTabNames(void) const -{ - std::list< std::pair<AString, AString>> NameList; - for (auto itr = m_Tabs.cbegin(), end = m_Tabs.cend(); itr != end; ++itr) - { - NameList.push_back(std::make_pair((*itr)->m_Title, (*itr)->m_SafeTitle)); - } - return NameList; -} - - - - - -cWebPlugin::cTabPtr cWebPlugin::GetTabBySafeTitle(const AString & a_SafeTitle) const -{ - cCSLock Lock(m_CSTabs); - for (auto itr = m_Tabs.cbegin(), end = m_Tabs.cend(); itr != end; ++itr) - { - if ((*itr)->m_SafeTitle == a_SafeTitle) - { - return *itr; - } - } - return nullptr; -} - - - - - -std::pair<AString, AString> cWebPlugin::GetTabNameForRequest(const HTTPRequest & a_Request) -{ - AStringVector Split = StringSplit(a_Request.Path, "/"); - if (Split.empty()) - { - return std::make_pair(AString(), AString()); - } - - cCSLock Lock(m_CSTabs); - cTabPtr Tab; - if (Split.size() > 2) // If we got the tab name, show that page - { - for (auto itr = m_Tabs.cbegin(), end = m_Tabs.cend(); itr != end; ++itr) - { - if ((*itr)->m_SafeTitle.compare(Split[2]) == 0) // This is the one! - { - return std::make_pair((*itr)->m_Title, (*itr)->m_SafeTitle); - } - } - // Tab name not found, display an "empty" page: - return std::make_pair(AString(), AString()); - } - - // Show the first tab: - if (!m_Tabs.empty()) - { - return std::make_pair(m_Tabs.front()->m_SafeTitle, m_Tabs.front()->m_SafeTitle); - } - - // No tabs at all: - return std::make_pair(AString(), AString()); -} - - - - -AString cWebPlugin::SafeString(const AString & a_String) -{ - AString RetVal; - auto len = a_String.size(); - RetVal.reserve(len); - for (size_t i = 0; i < len; ++i) - { - char c = a_String[i]; - if (c == ' ') - { - c = '_'; - } - RetVal.push_back(c); - } - return RetVal; -} - - - - - -void cWebPlugin::AddNewWebTab(const AString & a_Title, int a_UserData) -{ - auto Tab = std::make_shared<cTab>(a_Title, a_UserData); - cCSLock Lock(m_CSTabs); - m_Tabs.push_back(Tab); -} - - - - - -void cWebPlugin::ClearTabs(void) -{ - // Remove the webadmin tabs: - cTabPtrs Tabs; - { - cCSLock Lock(m_CSTabs); - std::swap(Tabs, m_Tabs); - } -} - - - - diff --git a/src/Bindings/WebPlugin.h b/src/Bindings/WebPlugin.h deleted file mode 100644 index 6dc8db801..000000000 --- a/src/Bindings/WebPlugin.h +++ /dev/null @@ -1,80 +0,0 @@ - -#pragma once - -struct HTTPRequest; - - - - - -// tolua_begin -class cWebPlugin -{ -public: - // tolua_end - - struct cTab - { - AString m_Title; - AString m_SafeTitle; - int m_UserData; - - cTab(const AString & a_Title, int a_UserData): - m_Title(a_Title), - m_SafeTitle(cWebPlugin::SafeString(a_Title)), - m_UserData(a_UserData) - { - } - }; - - typedef SharedPtr<cTab> cTabPtr; - typedef std::list<cTabPtr> cTabPtrs; - typedef std::list<std::pair<AString, AString>> cTabNames; - - - cWebPlugin(); - - virtual ~cWebPlugin(); - - // tolua_begin - - /** Returns the title of the plugin, as it should be presented in the webadmin's pages tree. */ - virtual const AString GetWebTitle(void) const = 0; - - /** Sanitizes the input string, replacing spaces with underscores. */ - static AString SafeString(const AString & a_String); - - // tolua_end - - virtual AString HandleWebRequest(const HTTPRequest & a_Request) = 0; - - /** Adds a new web tab with the specified contents. */ - void AddNewWebTab(const AString & a_Title, int a_UserData); - - /** Removes all the tabs. */ - void ClearTabs(void); - - /** Returns all the tabs that this plugin has registered. */ - const cTabPtrs & GetTabs(void) const { return m_Tabs; } - - /** Returns all of the tabs that this plugin has registered. */ - cTabNames GetTabNames(void) const; // Exported in ManualBindings.cpp - - /** Returns the tab that has the specified SafeTitle. - Returns nullptr if no such tab. */ - cTabPtr GetTabBySafeTitle(const AString & a_SafeTitle) const; - - std::pair<AString, AString> GetTabNameForRequest(const HTTPRequest & a_Request); - -private: - /** All tabs that this plugin has registered. - Protected against multithreaded access by m_CSTabs. */ - cTabPtrs m_Tabs; - - /** Protects m_Tabs against multithreaded access. */ - mutable cCriticalSection m_CSTabs; -}; // tolua_export - - - - diff --git a/src/Entities/Player.h b/src/Entities/Player.h index 2f1e892dc..f6e9da45e 100644 --- a/src/Entities/Player.h +++ b/src/Entities/Player.h @@ -224,11 +224,11 @@ public: cWindow * GetWindow(void) { return m_CurrentWindow; } // tolua_export const cWindow * GetWindow(void) const { return m_CurrentWindow; } - /** Opens the specified window; closes the current one first using CloseWindow() */ - void OpenWindow(cWindow * a_Window); // Exported in ManualBindings.cpp - // tolua_begin + /** Opens the specified window; closes the current one first using CloseWindow() */ + void OpenWindow(cWindow * a_Window); + /** Closes the current window, resets current window to m_InventoryWindow. A plugin may refuse the closing if a_CanRefuse is true */ void CloseWindow(bool a_CanRefuse = true); diff --git a/src/Server.cpp b/src/Server.cpp index 5548e77d1..8405109de 100644 --- a/src/Server.cpp +++ b/src/Server.cpp @@ -607,18 +607,35 @@ void cServer::PrintHelp(const AStringVector & a_Split, cCommandOutputCallback & void cServer::BindBuiltInConsoleCommands(void) { + // Create an empty handler - the actual handling for the commands is performed before they are handed off to cPluginManager + class cEmptyHandler: + public cPluginManager::cCommandHandler + { + virtual bool ExecuteCommand( + const AStringVector & a_Split, + cPlayer * a_Player, + const AString & a_Command, + cCommandOutputCallback * a_Output = nullptr + ) override + { + return false; + } + }; + auto handler = std::make_shared<cEmptyHandler>(); + + // Register internal commands: cPluginManager * PlgMgr = cPluginManager::Get(); - PlgMgr->BindConsoleCommand("help", nullptr, "Shows the available commands"); - PlgMgr->BindConsoleCommand("reload", nullptr, "Reloads all plugins"); - PlgMgr->BindConsoleCommand("restart", nullptr, "Restarts the server cleanly"); - PlgMgr->BindConsoleCommand("stop", nullptr, "Stops the server cleanly"); - PlgMgr->BindConsoleCommand("chunkstats", nullptr, "Displays detailed chunk memory statistics"); - PlgMgr->BindConsoleCommand("load <pluginname>", nullptr, "Adds and enables the specified plugin"); - PlgMgr->BindConsoleCommand("unload <pluginname>", nullptr, "Disables the specified plugin"); - PlgMgr->BindConsoleCommand("destroyentities", nullptr, "Destroys all entities in all worlds"); + PlgMgr->BindConsoleCommand("help", nullptr, handler, "Shows the available commands"); + PlgMgr->BindConsoleCommand("reload", nullptr, handler, "Reloads all plugins"); + PlgMgr->BindConsoleCommand("restart", nullptr, handler, "Restarts the server cleanly"); + PlgMgr->BindConsoleCommand("stop", nullptr, handler, "Stops the server cleanly"); + PlgMgr->BindConsoleCommand("chunkstats", nullptr, handler, "Displays detailed chunk memory statistics"); + PlgMgr->BindConsoleCommand("load", nullptr, handler, "Adds and enables the specified plugin"); + PlgMgr->BindConsoleCommand("unload", nullptr, handler, "Disables the specified plugin"); + PlgMgr->BindConsoleCommand("destroyentities", nullptr, handler, "Destroys all entities in all worlds"); #if defined(_MSC_VER) && defined(_DEBUG) && defined(ENABLE_LEAK_FINDER) - PlgMgr->BindConsoleCommand("dumpmem", nullptr, " - Dumps all used memory blocks together with their callstacks into memdump.xml"); + PlgMgr->BindConsoleCommand("dumpmem", nullptr, handler, " - Dumps all used memory blocks together with their callstacks into memdump.xml"); #endif } diff --git a/src/UI/Window.cpp b/src/UI/Window.cpp index 5a2e55feb..4582d6cf4 100644 --- a/src/UI/Window.cpp +++ b/src/UI/Window.cpp @@ -307,7 +307,7 @@ bool cWindow::ClosedByPlayer(cPlayer & a_Player, bool a_CanRefuse) // Checks whether the player is still holding an item if (!a_Player.GetDraggingItem().IsEmpty()) { - LOGD("Player holds item! Dropping it..."); + LOGD("Player is holding an item while closing their window, dropping it as a pickup..."); a_Player.TossHeldItem(a_Player.GetDraggingItem().m_ItemCount); } diff --git a/src/WebAdmin.cpp b/src/WebAdmin.cpp index e43d749dd..5c08deb0d 100644 --- a/src/WebAdmin.cpp +++ b/src/WebAdmin.cpp @@ -2,10 +2,6 @@ #include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules #include "WebAdmin.h" -#include "Bindings/WebPlugin.h" - -#include "Bindings/PluginManager.h" -#include "Bindings/Plugin.h" #include "World.h" #include "Entities/Player.h" @@ -87,9 +83,9 @@ public: // cWebAdmin: cWebAdmin::cWebAdmin(void) : + m_TemplateScript("<webadmin_template>"), m_IsInitialized(false), - m_IsRunning(false), - m_TemplateScript("<webadmin_template>") + m_IsRunning(false) { } @@ -106,40 +102,9 @@ cWebAdmin::~cWebAdmin() -void cWebAdmin::AddPlugin(cWebPlugin * a_Plugin) -{ - m_Plugins.remove(a_Plugin); - m_Plugins.push_back(a_Plugin); -} - - - - - -void cWebAdmin::RemovePlugin(cWebPlugin * a_Plugin) -{ - m_Plugins.remove(a_Plugin); -} - - - - - bool cWebAdmin::Init(void) { - if (!m_IniFile.ReadFile("webadmin.ini")) - { - LOGWARN("Regenerating webadmin.ini, all settings will be reset"); - m_IniFile.AddHeaderComment(" This file controls the webadmin feature of Cuberite"); - m_IniFile.AddHeaderComment(" Username format: [User:*username*]"); - m_IniFile.AddHeaderComment(" Password format: Password=*password*; for example:"); - m_IniFile.AddHeaderComment(" [User:admin]"); - m_IniFile.AddHeaderComment(" Password=admin"); - m_IniFile.SetValue("WebAdmin", "Ports", DEFAULT_WEBADMIN_PORTS); - m_IniFile.WriteFile("webadmin.ini"); - } - - if (!m_IniFile.GetValueSetB("WebAdmin", "Enabled", true)) + if (!LoadIniFile()) { // WebAdmin is disabled, bail out faking a success return true; @@ -147,31 +112,7 @@ bool cWebAdmin::Init(void) LOGD("Initialising WebAdmin..."); - // Initialize the WebAdmin template script and load the file - m_TemplateScript.Create(); - m_TemplateScript.RegisterAPILibs(); - if (!m_TemplateScript.LoadFile(FILE_IO_PREFIX "webadmin/template.lua")) - { - LOGWARN("Could not load WebAdmin template \"%s\". WebAdmin disabled!", FILE_IO_PREFIX "webadmin/template.lua"); - m_TemplateScript.Close(); - m_HTTPServer.Stop(); - return false; - } - - // Load the login template, provide a fallback default if not found: - if (!LoadLoginTemplate()) - { - LOGWARN("Could not load WebAdmin login template \"%s\", using fallback template.", FILE_IO_PREFIX "webadmin/login_template.html"); - - // Sets the fallback template: - m_LoginTemplate = \ - "<h1>Cuberite WebAdmin</h1>" \ - "<center>" \ - "<form method='get' action='webadmin/'>" \ - "<input type='submit' value='Log in'>" \ - "</form>" \ - "</center>"; - } + Reload(); // Read the ports to be used: // Note that historically the ports were stored in the "Port" and "PortsIPv6" values @@ -224,7 +165,7 @@ void cWebAdmin::Stop(void) -bool cWebAdmin::LoadLoginTemplate(void) +bool cWebAdmin::LoadLoginPage(void) { cFile File(FILE_IO_PREFIX "webadmin/login_template.html", cFile::fmRead); if (!File.IsOpen()) @@ -238,7 +179,8 @@ bool cWebAdmin::LoadLoginTemplate(void) return false; } - m_LoginTemplate = TemplateContent; + cCSLock Lock(m_CS); + m_LoginPage = TemplateContent; return true; } @@ -246,6 +188,89 @@ bool cWebAdmin::LoadLoginTemplate(void) +void cWebAdmin::RemoveAllPluginWebTabs(const AString & a_PluginName) +{ + cCSLock lock(m_CS); + m_WebTabs.erase(std::remove_if(m_WebTabs.begin(), m_WebTabs.end(), [=](cWebTabPtr a_CBWebTab) + { + return (a_CBWebTab->m_PluginName == a_PluginName); + }), + m_WebTabs.end() + ); +} + + + + + +void cWebAdmin::Reload(void) +{ + cCSLock lock(m_CS); + if (!LoadIniFile()) + { + // We are asked to disable the webadmin, cannot do that, so warn the admin: + LOGWARNING( + "WebAdmin was previously enabled and now the settings say to disable it." + " This will not take effect until you restart the server." + ); + } + + // Initialize the WebAdmin template script and reload the file: + if (m_TemplateScript.IsValid()) + { + m_TemplateScript.Close(); + } + m_TemplateScript.Create(); + m_TemplateScript.RegisterAPILibs(); + if (!m_TemplateScript.LoadFile(FILE_IO_PREFIX "webadmin/template.lua")) + { + LOGWARN("Could not load WebAdmin template \"%s\". WebAdmin will not work properly!", FILE_IO_PREFIX "webadmin/template.lua"); + m_TemplateScript.Close(); + } + + // Load the login template, provide a fallback default if not found: + if (!LoadLoginPage()) + { + LOGWARN("Could not load WebAdmin login page \"%s\", using fallback template.", FILE_IO_PREFIX "webadmin/login_template.html"); + + // Set the fallback: + m_LoginPage = \ + "<h1>Cuberite WebAdmin</h1>" \ + "<center>" \ + "<form method='get' action='webadmin/'>" \ + "<input type='submit' value='Log in'>" \ + "</form>" \ + "</center>"; + } +} + + + + + +bool cWebAdmin::LoadIniFile(void) +{ + m_IniFile.Clear(); + if (!m_IniFile.ReadFile("webadmin.ini")) + { + LOGWARN("Regenerating webadmin.ini, all settings will be reset"); + m_IniFile.AddHeaderComment(" This file controls the webadmin feature of Cuberite"); + m_IniFile.AddHeaderComment(" It specifies whether webadmin is enabled, and what logins are allowed. "); + m_IniFile.AddHeaderComment(" Username format: [User:*username*]"); + m_IniFile.AddHeaderComment(" Password format: Password=*password*; for example:"); + m_IniFile.AddHeaderComment(" [User:admin]"); + m_IniFile.AddHeaderComment(" Password=admin"); + m_IniFile.SetValue("WebAdmin", "Ports", DEFAULT_WEBADMIN_PORTS); + m_IniFile.WriteFile("webadmin.ini"); + } + + return m_IniFile.GetValueSetB("WebAdmin", "Enabled", true); +} + + + + + void cWebAdmin::HandleWebadminRequest(cHTTPServerConnection & a_Connection, cHTTPIncomingRequest & a_Request) { if (!a_Request.HasAuth()) @@ -255,17 +280,20 @@ void cWebAdmin::HandleWebadminRequest(cHTTPServerConnection & a_Connection, cHTT } // Check auth: - AString UserPassword = m_IniFile.GetValue("User:" + a_Request.GetAuthUsername(), "Password", ""); - if ((UserPassword == "") || (a_Request.GetAuthPassword() != UserPassword)) { - a_Connection.SendNeedAuth("Cuberite WebAdmin - bad username or password"); - return; + cCSLock Lock(m_CS); + AString UserPassword = m_IniFile.GetValue("User:" + a_Request.GetAuthUsername(), "Password", ""); + if ((UserPassword == "") || (a_Request.GetAuthPassword() != UserPassword)) + { + a_Connection.SendNeedAuth("Cuberite WebAdmin - bad username or password"); + return; + } } // Check if the contents should be wrapped in the template: auto BareURL = a_Request.GetURLPath(); ASSERT(BareURL.length() > 0); - bool ShouldWrapInTemplate = ((BareURL.length() > 1) && (BareURL[1] != '~')); + bool ShouldWrapInTemplate = (!BareURL.empty() && (BareURL[1] != '~')); // Retrieve the request data: auto Data = std::static_pointer_cast<cWebadminRequestData>(a_Request.GetUserData()); @@ -312,6 +340,7 @@ void cWebAdmin::HandleWebadminRequest(cHTTPServerConnection & a_Connection, cHTT // Try to get the template from the Lua template script if (ShouldWrapInTemplate) { + cCSLock Lock(m_CS); if (m_TemplateScript.Call("ShowPage", this, &TemplateRequest, cLuaState::Return, Template)) { cHTTPOutgoingResponse Resp; @@ -325,59 +354,12 @@ void cWebAdmin::HandleWebadminRequest(cHTTPServerConnection & a_Connection, cHTT return; } - AString BaseURL = GetBaseURL(BareURL); - AString Menu; - Template = "{CONTENT}"; - AString FoundPlugin; - - for (PluginList::iterator itr = m_Plugins.begin(); itr != m_Plugins.end(); ++itr) - { - cWebPlugin * WebPlugin = *itr; - std::list< std::pair<AString, AString> > NameList = WebPlugin->GetTabNames(); - for (std::list< std::pair<AString, AString> >::iterator Names = NameList.begin(); Names != NameList.end(); ++Names) - { - Menu += "<li><a href='" + BaseURL + WebPlugin->GetWebTitle().c_str() + "/" + (*Names).second + "'>" + (*Names).first + "</a></li>"; - } - } - - sWebAdminPage Page = GetPage(TemplateRequest.Request); - AString Content = Page.Content; - FoundPlugin = Page.PluginName; - if (!Page.TabName.empty()) - { - FoundPlugin += " - " + Page.TabName; - } - - if (FoundPlugin.empty()) // Default page - { - Content = GetDefaultPage(); - } - - int MemUsageKiB = cRoot::GetPhysicalRAMUsage(); - if (MemUsageKiB > 0) - { - ReplaceString(Template, "{MEM}", Printf("%.02f", static_cast<double>(MemUsageKiB) / 1024)); - ReplaceString(Template, "{MEMKIB}", Printf("%d", MemUsageKiB)); - } - else - { - ReplaceString(Template, "{MEM}", "unknown"); - ReplaceString(Template, "{MEMKIB}", "unknown"); - } - ReplaceString(Template, "{USERNAME}", a_Request.GetAuthUsername()); - ReplaceString(Template, "{MENU}", Menu); - ReplaceString(Template, "{PLUGIN_NAME}", FoundPlugin); - ReplaceString(Template, "{CONTENT}", Content); - ReplaceString(Template, "{TITLE}", "Cuberite"); - - AString NumChunks; - Printf(NumChunks, "%d", cRoot::Get()->GetTotalChunkCount()); - ReplaceString(Template, "{NUMCHUNKS}", NumChunks); - - cHTTPOutgoingResponse Resp; - Resp.SetContentType("text/html"); - a_Connection.Send(Resp); - a_Connection.Send(Template.c_str(), Template.length()); + // Send the un-decorated page content: + auto page = GetPage(TemplateRequest.Request); + cHTTPOutgoingResponse resp; + resp.SetContentType(page.ContentType); + a_Connection.Send(resp); + a_Connection.Send(page.Content.c_str(), page.Content.length()); a_Connection.FinishResponse(); } @@ -392,7 +374,7 @@ void cWebAdmin::HandleRootRequest(cHTTPServerConnection & a_Connection, cHTTPInc cHTTPOutgoingResponse Resp; Resp.SetContentType("text/html"); a_Connection.Send(Resp); - a_Connection.Send(m_LoginTemplate); + a_Connection.Send(m_LoginPage); a_Connection.FinishResponse(); } @@ -406,7 +388,7 @@ void cWebAdmin::HandleFileRequest(cHTTPServerConnection & a_Connection, cHTTPInc std::replace(FileURL.begin(), FileURL.end(), '\\', '/'); // Remove all leading backslashes: - if (FileURL[0] == '/') + if (!FileURL.empty() && (FileURL[0] == '/')) { size_t FirstCharToRead = FileURL.find_first_not_of('/'); if (FirstCharToRead != AString::npos) @@ -418,8 +400,9 @@ void cWebAdmin::HandleFileRequest(cHTTPServerConnection & a_Connection, cHTTPInc // Remove all "../" strings: ReplaceString(FileURL, "../", ""); - bool LoadedSuccessfull = false; + // Read the file contents and guess its mime-type, based on the extension: AString Content = "<h2>404 Not Found</h2>"; + AString ContentType; AString Path = Printf(FILE_IO_PREFIX "webadmin/files/%s", FileURL.c_str()); if (cFile::IsFile(Path)) { @@ -427,18 +410,17 @@ void cWebAdmin::HandleFileRequest(cHTTPServerConnection & a_Connection, cHTTPInc AString FileContent; if (File.IsOpen() && (File.ReadRestOfFile(FileContent) != -1)) { - LoadedSuccessfull = true; - Content = FileContent; + std::swap(Content, FileContent); + size_t LastPointPosition = Path.find_last_of('.'); + if (LastPointPosition != AString::npos) + { + ContentType = GetContentTypeFromFileExt(Path.substr(LastPointPosition + 1)); + } } } - - // Find content type (The currently method is very bad. We should change it later) - AString ContentType = "text/html"; - size_t LastPointPosition = Path.find_last_of('.'); - if (LoadedSuccessfull && (LastPointPosition != AString::npos) && (LastPointPosition < Path.length())) + if (ContentType.empty()) { - AString FileExtension = Path.substr(LastPointPosition + 1); - ContentType = GetContentTypeFromFileExt(FileExtension); + ContentType = "application/unknown"; } // Send the response: @@ -456,32 +438,36 @@ void cWebAdmin::HandleFileRequest(cHTTPServerConnection & a_Connection, cHTTPInc AString cWebAdmin::GetContentTypeFromFileExt(const AString & a_FileExtension) { static bool IsInitialized = false; - static std::map<AString, AString> ContentTypeMap; + static AStringMap ContentTypeMap; if (!IsInitialized) { // Initialize the ContentTypeMap: - ContentTypeMap["png"] = "image/png"; - ContentTypeMap["fif"] = "image/fif"; - ContentTypeMap["gif"] = "image/gif"; - ContentTypeMap["jpeg"] = "image/jpeg"; - ContentTypeMap["jpg"] = "image/jpeg"; - ContentTypeMap["jpe"] = "image/jpeg"; - ContentTypeMap["tiff"] = "image/tiff"; - ContentTypeMap["ico"] = "image/ico"; - ContentTypeMap["csv"] = "image/comma-separated-values"; - ContentTypeMap["css"] = "text/css"; - ContentTypeMap["js"] = "text/javascript"; - ContentTypeMap["txt"] = "text/plain"; - ContentTypeMap["rtx"] = "text/richtext"; - ContentTypeMap["xml"] = "text/xml"; - } - - AString FileExtension = StrToLower(a_FileExtension); - if (ContentTypeMap.find(a_FileExtension) == ContentTypeMap.end()) - { - return "text/html"; - } - return ContentTypeMap[FileExtension]; + ContentTypeMap["png"] = "image/png"; + ContentTypeMap["fif"] = "image/fif"; + ContentTypeMap["gif"] = "image/gif"; + ContentTypeMap["jpeg"] = "image/jpeg"; + ContentTypeMap["jpg"] = "image/jpeg"; + ContentTypeMap["jpe"] = "image/jpeg"; + ContentTypeMap["tiff"] = "image/tiff"; + ContentTypeMap["ico"] = "image/ico"; + ContentTypeMap["csv"] = "text/csv"; + ContentTypeMap["css"] = "text/css"; + ContentTypeMap["js"] = "text/javascript"; + ContentTypeMap["txt"] = "text/plain"; + ContentTypeMap["rtx"] = "text/richtext"; + ContentTypeMap["rtf"] = "text/richtext"; + ContentTypeMap["xml"] = "text/xml"; + ContentTypeMap["html"] = "text/html"; + ContentTypeMap["htm"] = "text/html"; + ContentTypeMap["xhtml"] = "application/xhtml+xml"; // Not recomended for IE6, but no-one uses that anymore + } + + auto itr = ContentTypeMap.find(StrToLower(a_FileExtension)); + if (itr == ContentTypeMap.end()) + { + return AString(); + } + return itr->second; } @@ -490,86 +476,93 @@ AString cWebAdmin::GetContentTypeFromFileExt(const AString & a_FileExtension) sWebAdminPage cWebAdmin::GetPage(const HTTPRequest & a_Request) { - sWebAdminPage Page; - AStringVector Split = StringSplit(a_Request.Path, "/"); + sWebAdminPage page; + auto split = StringSplit(a_Request.Path, "/"); + + // If no specific page was requested, return an empty object: + if (split.size() <= 2) + { + return page; + } - // Find the plugin that corresponds to the requested path - AString FoundPlugin; - if (Split.size() > 1) + // Find the WebTab handler responsible for the request: + cWebTabPtr tab; { - for (PluginList::iterator itr = m_Plugins.begin(); itr != m_Plugins.end(); ++itr) + cCSLock Lock(m_CS); + for (auto & wt: m_WebTabs) { - if ((*itr)->GetWebTitle() == Split[1]) + if ( + (wt->m_PluginName == split[1]) && + (wt->m_UrlPath == split[2]) + ) { - Page.Content = (*itr)->HandleWebRequest(a_Request); - cWebPlugin * WebPlugin = *itr; - FoundPlugin = WebPlugin->GetWebTitle(); - AString TabName = WebPlugin->GetTabNameForRequest(a_Request).first; - Page.PluginName = FoundPlugin; - Page.TabName = TabName; + tab = wt; break; } + } // for wt - m_WebTabs[] + } + + // If a WebTab handler was found, call it: + if (tab != nullptr) + { + page.ContentType = "text/html"; // Default to HTML content type, unless overridden by a plugin + if (!tab->m_Callback->Call(a_Request, split[1], page.Content, page.ContentType)) + { + page.Content = GetHTMLEscapedString(Printf( + "WebTab callback for plugin %s, page %s has failed.", + tab->m_PluginName.c_str(), tab->m_Title.c_str() + )); } + page.PluginName = tab->m_PluginName; + page.TabTitle = tab->m_Title; + page.TabUrlPath = split[1]; } - // Return the page contents - return Page; + return page; } -AString cWebAdmin::GetDefaultPage(void) +AString cWebAdmin::GetBaseURL(const AString & a_URL) { - AString Content; - Content += "<h4>Server Name:</h4>"; - Content += "<p>" + AString( cRoot::Get()->GetServer()->GetServerID()) + "</p>"; + return GetBaseURL(StringSplit(a_URL, "/")); +} - // Display a list of all plugins: - Content += "<h4>Plugins:</h4><ul>"; - struct cPluginCallback: - public cPluginManager::cPluginCallback - { - AString & m_Content; - cPluginCallback(AString & a_Content): - m_Content(a_Content) - { - } - virtual bool Item(cPlugin * a_Plugin) override - { - if (a_Plugin->IsLoaded()) - { - AppendPrintf(m_Content, "<li>%s V.%i</li>", a_Plugin->GetName().c_str(), a_Plugin->GetVersion()); - } - return false; - } - } Callback(Content); - cPluginManager::Get()->ForEachPlugin(Callback); - Content += "</ul>"; - // Display a list of all players: - Content += "<h4>Players:</h4><ul>"; - cPlayerAccum PlayerAccum; - cWorld * World = cRoot::Get()->GetDefaultWorld(); // TODO - Create a list of worlds and players - if (World != nullptr) - { - World->ForEachPlayer(PlayerAccum); - Content.append(PlayerAccum.m_Contents); - } - Content += "</ul><br>"; - return Content; + +void cWebAdmin::AddWebTab( + const AString & a_Title, + const AString & a_UrlPath, + const AString & a_PluginName, + SharedPtr<cWebAdmin::cWebTabCallback> a_Callback +) +{ + cCSLock lock(m_CS); + m_WebTabs.emplace_back(std::make_shared<cWebTab>(a_Title, a_UrlPath, a_PluginName, a_Callback)); } -AString cWebAdmin::GetBaseURL(const AString & a_URL) +bool cWebAdmin::DelWebTab(const AString & a_UrlPath) { - return GetBaseURL(StringSplit(a_URL, "/")); + cCSLock lock(m_CS); + for (auto itr = m_WebTabs.begin(), end = m_WebTabs.end(); itr != end; ++itr) + { + if ((*itr)->m_UrlPath == a_UrlPath) + { + m_WebTabs.erase(itr); + return true; + } + } // for itr - m_WebTabs[] + + // Not found: + return false; } diff --git a/src/WebAdmin.h b/src/WebAdmin.h index 5e48f597c..b76ca6df8 100644 --- a/src/WebAdmin.h +++ b/src/WebAdmin.h @@ -89,14 +89,14 @@ struct HTTPTemplateRequest -// tolua_begin struct sWebAdminPage { AString Content; AString PluginName; - AString TabName; + AString TabTitle; + AString TabUrlPath; + AString ContentType; }; -// tolua_end @@ -111,7 +111,49 @@ class cWebAdmin : public: // tolua_end - typedef std::list< cWebPlugin* > PluginList; + /** Interface for getting the content of a single WebTab. */ + class cWebTabCallback abstract + { + public: + // Force a virtual destructor in descendants + virtual ~cWebTabCallback() {} + + /** Returns the contents for the specified request. + Returns true if the call was successful, false on an error. + a_Request is the full HTTP request object, as received from the client. + a_UrlPath is the UrlPath of the WebTab registered for this request, as parsed from a_Request. + Descendants should fill a_Content with the page contents + and optionally set a_ContentType [defaults to "text/html"] */ + virtual bool Call( + const HTTPRequest & a_Request, + const AString & a_UrlPath, + AString & a_Content, + AString & a_ContentType + ) = 0; + }; + + + /** Container for a single web tab. + Each web tab has a title, URL path and an associated plugin's name. + Each web tab is registered with a callback to provide the content. */ + class cWebTab + { + public: + AString m_Title; + AString m_UrlPath; + AString m_PluginName; + SharedPtr<cWebTabCallback> m_Callback; + + cWebTab(const AString & a_Title, const AString & a_UrlPath, const AString & a_PluginName, SharedPtr<cWebTabCallback> a_Callback): + m_Title(a_Title), + m_UrlPath(a_UrlPath), + m_PluginName(a_PluginName), + m_Callback(a_Callback) + { + } + }; + typedef SharedPtr<cWebTab> cWebTabPtr; + typedef std::vector<cWebTabPtr> cWebTabPtrs; cWebAdmin(void); @@ -120,81 +162,115 @@ public: /** Initializes the object. Returns true if successfully initialized and ready to start */ bool Init(void); - /** Starts the HTTP server taking care of the admin. Returns true if successful */ + /** Starts the HTTP server taking care of the webadmin. Returns true if successful */ bool Start(void); /** Stops the HTTP server, if it was started. */ void Stop(void); - /** Loads the login template. Returns true if the loading succeeds, false if not. */ - bool LoadLoginTemplate(void); - - void AddPlugin(cWebPlugin * a_Plugin); - void RemovePlugin(cWebPlugin * a_Plugin); + /** Loads the login template into m_LoginPage. + Returns true if the loading succeeds, false if not. */ + bool LoadLoginPage(void); - // TODO: Convert this to the auto-locking callback mechanism used for looping players in worlds and such - PluginList GetPlugins() const { return m_Plugins; } // >> EXPORTED IN MANUALBINDINGS << + /** Returns a copy of all the registered web tabs. + Exported to Lua in ManualBindings.cpp. */ + cWebTabPtrs GetAllWebTabs(void) { return m_WebTabs; } - // tolua_begin + /** Removes all WebTabs registered by the specified plugin. */ + void RemoveAllPluginWebTabs(const AString & a_PluginName); + /** Returns the (inner) page contents for the specified request. + Calls the appropriate WebTab handler to get the contents. + Exported to Lua in ManualBindings.cpp. */ sWebAdminPage GetPage(const HTTPRequest & a_Request); - /** Returns the contents of the default page - the list of plugins and players */ - AString GetDefaultPage(void); + // tolua_begin - /** Returns the prefix needed for making a link point to the webadmin root from the given URL ("../../../webadmin"-style) */ - AString GetBaseURL(const AString & a_URL); + /** Reloads m_IniFile, m_LoginPage and m_TemplateScript. + Note that reloading will not change the "enabled" state of the server, and it will not update listening ports. */ + void Reload(void); - /** Returns the list of ports used for the webadmin. */ + /** Returns the list of ports on which the webadmin is configured to listen. */ AString GetPorts(void) const { return StringsConcat(m_Ports, ','); } - - /** OBSOLETE: Returns the list of IPv4 ports used for the webadmin. - Currently there is no distinction between IPv4 and IPv6; use GetPorts() instead. */ - AString GetIPv4Ports(void) const { return GetPorts(); } - - /** OBSOLETE: Returns the list of IPv6 ports used for the webadmin. - Currently there is no distinction between IPv4 and IPv6; use GetPorts() instead. */ - AString GetIPv6Ports(void) const { return GetPorts(); } - // tolua_end - /** Escapes text passed into it, so it can be embedded into html. */ + /** Adds a new WebTab handler. + a_Title is the display title of the tab + a_UrlPath is the part of the URL that uniquely identifies this tab. + a_PluginName is the display name of the plugin creating this tab. + a_Callback is used to provide the actual WebTab contents, when requested. + Exported in ManualBindings.cpp. */ + void AddWebTab( + const AString & a_Title, + const AString & a_UrlPath, + const AString & a_PluginName, + SharedPtr<cWebTabCallback> a_Callback + ); + + /** Removes the WebTab with the specified URL path. + Returns true if WebTab was found and removed, false if not found. + Exported in ManualBindings.cpp */ + bool DelWebTab(const AString & a_UrlPath); + + /** Escapes text passed into it, so it can be embedded into html. + Exported to Lua in ManualBindings.cpp. */ static AString GetHTMLEscapedString(const AString & a_Input); - /** Escapes the string for use in an URL */ + /** Escapes the string for use in an URL + Exported to Lua in ManualBindings.cpp. */ static AString GetURLEncodedString(const AString & a_Input); + /** Returns the prefix needed for making a link point to the webadmin root from the given URL ("../../../webadmin"-style). + Exported to Lua in ManualBindings.cpp. */ + static AString GetBaseURL(const AString & a_URL); + /** Returns the prefix needed for making a link point to the webadmin root from the given URL ("../../../webadmin"-style) */ static AString GetBaseURL(const AStringVector & a_URLSplit); - /** Returns the content type from the file extension. If the extension isn't in the list, the function returns "text/html" */ + /** Returns the content type from the file extension. + If the extension isn't in the list, the function returns an empty string. + Exported to Lua in ManualBindings.cpp. */ static AString GetContentTypeFromFileExt(const AString & a_FileExtension); protected: + /** Protects m_WebTabs, m_TemplateScript, m_LoginTemplate and m_IniFile against multithreaded access. */ + cCriticalSection m_CS; + + /** All registered WebTab handlers. + Protected against multithreaded access by m_CS. */ + cWebTabPtrs m_WebTabs; + + /** The Lua template script to provide templates. + Protected against multithreaded access by m_CS. */ + cLuaState m_TemplateScript; + + /** The HTML page that provides the login. + Protected against multithreaded access by m_CS. */ + AString m_LoginPage; + + /** The webadmin.ini file, used for the settings and allowed logins. + Protected against multithreaded access by m_CS. */ + cIniFile m_IniFile; + /** Set to true if Init() succeeds and the webadmin isn't to be disabled */ bool m_IsInitialized; /** Set to true if Start() succeeds in starting the server, reset back to false in Stop(). */ bool m_IsRunning; - /** The webadmin.ini file, used for the settings and allowed logins */ - cIniFile m_IniFile; - - PluginList m_Plugins; - /** The ports on which the webadmin is running. */ AStringVector m_Ports; - /** The Lua template script to provide templates: */ - cLuaState m_TemplateScript; - - /** The template that provides the login site: */ - AString m_LoginTemplate; - /** The HTTP server which provides the underlying HTTP parsing, serialization and events */ cHTTPServer m_HTTPServer; + + /** Loads webadmin.ini into m_IniFile. + Creates a default file if it doesn't exist. + Returns true if webadmin is enabled, false if disabled. */ + bool LoadIniFile(void); + /** Handles requests coming to the "/webadmin" or "/~webadmin" URLs */ void HandleWebadminRequest(cHTTPServerConnection & a_Connection, cHTTPIncomingRequest & a_Request); |