diff options
author | Mattes D <github@xoft.cz> | 2016-03-21 10:40:38 +0100 |
---|---|---|
committer | Mattes D <github@xoft.cz> | 2016-03-21 10:40:38 +0100 |
commit | fa53e98419152a83b2a74c5ea45a60449abc59c2 (patch) | |
tree | 766f4703a8c30a01bfc5148fe54c6d6b4a3e3cc7 /src | |
parent | Merge pull request #3084 from cuberite/seadragon91-patch-1 (diff) | |
parent | Revert "Lua callback" (diff) | |
download | cuberite-fa53e98419152a83b2a74c5ea45a60449abc59c2.tar cuberite-fa53e98419152a83b2a74c5ea45a60449abc59c2.tar.gz cuberite-fa53e98419152a83b2a74c5ea45a60449abc59c2.tar.bz2 cuberite-fa53e98419152a83b2a74c5ea45a60449abc59c2.tar.lz cuberite-fa53e98419152a83b2a74c5ea45a60449abc59c2.tar.xz cuberite-fa53e98419152a83b2a74c5ea45a60449abc59c2.tar.zst cuberite-fa53e98419152a83b2a74c5ea45a60449abc59c2.zip |
Diffstat (limited to 'src')
-rw-r--r-- | src/Bindings/AllToLua.pkg | 1 | ||||
-rw-r--r-- | src/Bindings/CMakeLists.txt | 3 | ||||
-rw-r--r-- | src/Bindings/LuaState.cpp | 220 | ||||
-rw-r--r-- | src/Bindings/LuaState.h | 117 | ||||
-rw-r--r-- | src/Bindings/LuaWindow.cpp | 95 | ||||
-rw-r--r-- | src/Bindings/LuaWindow.h | 72 | ||||
-rw-r--r-- | src/Bindings/ManualBindings.cpp | 461 | ||||
-rw-r--r-- | src/Bindings/ManualBindings_World.cpp | 105 | ||||
-rw-r--r-- | src/Bindings/PluginLua.cpp | 1191 | ||||
-rw-r--r-- | src/Bindings/PluginLua.h | 86 | ||||
-rw-r--r-- | src/Bindings/PluginManager.cpp | 17 | ||||
-rw-r--r-- | src/Bindings/PluginManager.h | 3 | ||||
-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/UI/Window.cpp | 2 | ||||
-rw-r--r-- | src/WebAdmin.cpp | 415 | ||||
-rw-r--r-- | src/WebAdmin.h | 158 |
18 files changed, 1925 insertions, 1259 deletions
diff --git a/src/Bindings/AllToLua.pkg b/src/Bindings/AllToLua.pkg index 6ca9c8658..991ed0ddd 100644 --- a/src/Bindings/AllToLua.pkg +++ b/src/Bindings/AllToLua.pkg @@ -38,6 +38,7 @@ $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 4f25f2cf4..10cda1efb 100644 --- a/src/Bindings/CMakeLists.txt +++ b/src/Bindings/CMakeLists.txt @@ -23,6 +23,7 @@ SET (SRCS Plugin.cpp PluginLua.cpp PluginManager.cpp + WebPlugin.cpp ) SET (HDRS @@ -43,6 +44,7 @@ SET (HDRS Plugin.h PluginLua.h PluginManager.h + WebPlugin.h tolua++.h ) @@ -64,6 +66,7 @@ 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/LuaState.cpp b/src/Bindings/LuaState.cpp index 98507e96c..200878cf7 100644 --- a/src/Bindings/LuaState.cpp +++ b/src/Bindings/LuaState.cpp @@ -20,10 +20,6 @@ extern "C" #include "../Entities/Entity.h" #include "../BlockEntities/BlockEntity.h" - - - - // fwd: "SQLite/lsqlite3.c" extern "C" { @@ -43,10 +39,6 @@ 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"; - @@ -122,101 +114,6 @@ cLuaStateTracker & cLuaStateTracker::Get(void) //////////////////////////////////////////////////////////////////////////////// -// cLuaState::cCallback: - -bool cLuaState::cCallback::RefStack(cLuaState & a_LuaState, int a_StackPos) -{ - // Check if the stack contains a function: - if (!lua_isfunction(a_LuaState, a_StackPos)) - { - return false; - } - - // Clear any previous callback: - Clear(); - - // Add self to LuaState's callback-tracking: - a_LuaState.TrackCallback(*this); - - // Store the new callback: - cCSLock Lock(m_CS); - m_Ref.RefStack(a_LuaState, a_StackPos); - return true; -} - - - - - -void cLuaState::cCallback::Clear(void) -{ - // Free the callback reference: - lua_State * luaState = nullptr; - { - cCSLock Lock(m_CS); - if (!m_Ref.IsValid()) - { - return; - } - luaState = m_Ref.GetLuaState(); - m_Ref.UnRef(); - } - - // Remove from LuaState's callback-tracking: - cLuaState(luaState).UntrackCallback(*this); -} - - - - - -bool cLuaState::cCallback::IsValid(void) -{ - cCSLock lock(m_CS); - return m_Ref.IsValid(); -} - - - - - -bool cLuaState::cCallback::IsSameLuaState(cLuaState & a_LuaState) -{ - cCSLock lock(m_CS); - if (!m_Ref.IsValid()) - { - return false; - } - auto canonState = a_LuaState.QueryCanonLuaState(); - if (canonState == nullptr) - { - return false; - } - return (m_Ref.GetLuaState() == static_cast<lua_State *>(*canonState)); -} - - - - - -void cLuaState::cCallback::Invalidate(void) -{ - cCSLock Lock(m_CS); - if (!m_Ref.IsValid()) - { - LOGD("%s: Invalidating an already invalid callback at %p, this should not happen", - __FUNCTION__, reinterpret_cast<void *>(this) - ); - return; - } - m_Ref.UnRef(); -} - - - - - -//////////////////////////////////////////////////////////////////////////////// // cLuaState: cLuaState::cLuaState(const AString & a_SubsystemName) : @@ -273,10 +170,6 @@ 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); } @@ -313,16 +206,6 @@ 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; @@ -938,18 +821,6 @@ 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; @@ -976,24 +847,6 @@ 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) -{ - return a_Callback->RefStack(*this, a_StackPos); -} - - - - - bool cLuaState::GetStackValue(int a_StackPos, cPluginManager::CommandResult & a_Result) { if (lua_isnumber(m_LuaState, a_StackPos)) @@ -1678,7 +1531,7 @@ int cLuaState::CopyStackFrom(cLuaState & a_SrcLuaState, int a_SrcStart, int a_Sr LOGWARNING("%s: Unsupported value: '%s' at stack position %d. Can only copy numbers, strings, bools and classes!", __FUNCTION__, lua_typename(a_SrcLuaState, t), i ); - a_SrcLuaState.LogStackValues("Stack where copying failed:"); + a_SrcLuaState.LogStack("Stack where copying failed:"); lua_pop(m_LuaState, i - a_SrcStart); return -1; } @@ -1705,16 +1558,16 @@ void cLuaState::ToString(int a_StackPos, AString & a_String) -void cLuaState::LogStackValues(const char * a_Header) +void cLuaState::LogStack(const char * a_Header) { - LogStackValues(m_LuaState, a_Header); + LogStack(m_LuaState, a_Header); } -void cLuaState::LogStackValues(lua_State * a_LuaState, const char * a_Header) +void cLuaState::LogStack(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:"); @@ -1739,21 +1592,6 @@ void cLuaState::LogStackValues(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)); @@ -1788,50 +1626,6 @@ 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: @@ -1887,7 +1681,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); } @@ -1898,9 +1692,11 @@ 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 0509b09ed..215980033 100644 --- a/src/Bindings/LuaState.h +++ b/src/Bindings/LuaState.h @@ -80,20 +80,8 @@ 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: - lua_State * m_LuaState; + cLuaState * m_LuaState; int m_Ref; // Remove the copy-constructor: @@ -124,76 +112,6 @@ 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(). - 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) = default; - - ~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) - { - cCSLock Lock(m_CS); - if (!m_Ref.IsValid()) - { - return false; - } - cLuaState(m_Ref.GetLuaState()).Call(m_Ref, std::forward<Args>(args)...); - return true; - } - - /** 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. */ - cCallback(const cCallback &) = delete; - - /** This class cannot be moved, because it is tracked in the LuaState by-ptr. */ - cCallback(cCallback &&) = delete; - }; - typedef SharedPtr<cCallback> cCallbackPtr; - - /** A dummy class that's used only to delimit function args from return values for cLuaState::Call() */ class cRet { @@ -343,16 +261,11 @@ 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, cPluginManager::CommandResult & a_Result); bool GetStackValue(int a_StackPos, cRef & a_Ref); bool GetStackValue(int a_StackPos, double & a_Value); @@ -515,14 +428,10 @@ 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 LogStackValues(const char * a_Header = nullptr); + void LogStack(const char * a_Header = nullptr); /** Logs all the elements' types on the API stack, with an optional header for the listing. */ - 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); + static void LogStack(lua_State * a_LuaState, const char * a_Header = nullptr); protected: @@ -532,7 +441,8 @@ protected: 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) */ @@ -541,15 +451,6 @@ 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) @@ -632,14 +533,6 @@ 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/LuaWindow.cpp b/src/Bindings/LuaWindow.cpp index bf3f7cfde..706397a27 100644 --- a/src/Bindings/LuaWindow.cpp +++ b/src/Bindings/LuaWindow.cpp @@ -15,13 +15,14 @@ //////////////////////////////////////////////////////////////////////////////// // cLuaWindow: -cLuaWindow::cLuaWindow(cLuaState & a_LuaState, cWindow::WindowType a_WindowType, int a_SlotsX, int a_SlotsY, const AString & a_Title) : - Super(a_WindowType, a_Title), +cLuaWindow::cLuaWindow(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_LuaState(a_LuaState.QueryCanonLuaState()) + m_Plugin(nullptr), + m_LuaRef(LUA_REFNIL), + m_OnClosingFnRef(LUA_REFNIL), + m_OnSlotChangedFnRef(LUA_REFNIL) { - 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)); @@ -66,42 +67,62 @@ cLuaWindow::~cLuaWindow() -void cLuaWindow::SetOnClosing(cLuaState::cCallbackPtr a_OnClosing) +void cLuaWindow::SetLuaRef(cPluginLua * a_Plugin, int a_LuaRef) { - // Only one Lua state can be a cLuaWindow object callback: - ASSERT(a_OnClosing->IsSameLuaState(*m_LuaState)); + // 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; +} + + + - // Store the new reference, releasing the old one if appropriate: - m_OnClosing = a_OnClosing; + +bool cLuaWindow::IsLuaReferenced(void) const +{ + return ((m_Plugin != nullptr) && (m_LuaRef != LUA_REFNIL)); } -void cLuaWindow::SetOnSlotChanged(cLuaState::cCallbackPtr a_OnSlotChanged) +void cLuaWindow::SetOnClosing(cPluginLua * a_Plugin, int a_FnRef) { - // Only one Lua state can be a cLuaWindow object callback: - ASSERT(a_OnSlotChanged->IsSameLuaState(*m_LuaState)); + // 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)); - // Store the new reference, releasing the old one if appropriate: - m_OnSlotChanged = a_OnSlotChanged; + // 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; } -void cLuaWindow::OpenedByPlayer(cPlayer & a_Player) +void cLuaWindow::SetOnSlotChanged(cPluginLua * a_Plugin, int a_FnRef) { - // 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) + // 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) { - m_LuaRef.CreateFromObject(*m_LuaState, this); + m_Plugin->Unreference(m_OnSlotChangedFnRef); } - ++m_PlayerCount; - Super::OpenedByPlayer(a_Player); + // Store the new reference + m_Plugin = a_Plugin; + m_OnSlotChangedFnRef = a_FnRef; } @@ -111,27 +132,17 @@ void cLuaWindow::OpenedByPlayer(cPlayer & a_Player) bool cLuaWindow::ClosedByPlayer(cPlayer & a_Player, bool a_CanRefuse) { // First notify the plugin through the registered callback: - if (m_OnClosing != nullptr) + if (m_OnClosingFnRef != LUA_REFNIL) { - 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 - ) + ASSERT(m_Plugin != nullptr); + if (m_Plugin->CallbackWindowClosing(m_OnClosingFnRef, *this, a_Player, a_CanRefuse)) { // The callback disagrees (the higher levels check the CanRefuse flag compliance) return false; } } - // 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); + return super::ClosedByPlayer(a_Player, a_CanRefuse); } @@ -140,7 +151,13 @@ bool cLuaWindow::ClosedByPlayer(cPlayer & a_Player, bool a_CanRefuse) void cLuaWindow::Destroy(void) { - Super::Destroy(); + super::Destroy(); + + if ((m_LuaRef != LUA_REFNIL) && (m_Plugin != nullptr)) + { + // The object is referenced by Lua, un-reference it + m_Plugin->Unreference(m_LuaRef); + } // Lua will take care of this object, it will garbage-collect it, so we must not delete it! m_IsDestroyed = false; @@ -161,7 +178,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); } @@ -177,9 +194,9 @@ void cLuaWindow::OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) } // If an OnSlotChanged callback has been registered, call it: - if (m_OnSlotChanged != nullptr) + if (m_OnSlotChangedFnRef != LUA_REFNIL) { - m_OnSlotChanged->Call(this, a_SlotNum); + m_Plugin->CallbackWindowSlotChanged(m_OnSlotChangedFnRef, *this, a_SlotNum); } } diff --git a/src/Bindings/LuaWindow.h b/src/Bindings/LuaWindow.h index 2a16d91ed..f292a5154 100644 --- a/src/Bindings/LuaWindow.h +++ b/src/Bindings/LuaWindow.h @@ -9,8 +9,6 @@ #pragma once -#include <atomic> -#include "LuaState.h" #include "../UI/Window.h" #include "../ItemGrid.h" @@ -18,30 +16,35 @@ +// 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. -- 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. -*/ +- 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. */ // tolua_begin class cLuaWindow : public cWindow // tolua_end , public cItemGrid::cListener -{ // tolua_export - typedef cWindow Super; + // tolua_begin +{ + typedef cWindow super; public: - /** 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); + /** 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); - // tolua_begin virtual ~cLuaWindow(); /** Returns the internal representation of the contents that are manipulated by Lua */ @@ -49,37 +52,36 @@ public: // tolua_end - /** Sets the Lua callback function to call when the window is about to close */ - void SetOnClosing(cLuaState::cCallbackPtr a_OnClosing); + /** 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); - /** Sets the Lua callback function to call when a slot is changed */ - void SetOnSlotChanged(cLuaState::cCallbackPtr a_OnSlotChanged); + /** Returns true if SetLuaRef() has been called */ + bool IsLuaReferenced(void) const; -protected: + /** 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 callback function (Lua reference) to call when a slot is changed */ + void SetOnSlotChanged(cPluginLua * a_Plugin, int a_FnRef); +protected: /** Contents of the non-inventory part */ cItemGrid m_Contents; - /** 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 plugin that has opened the window and owns the m_LuaRef */ + cPluginLua * m_Plugin; - /** Number of players that are currently using the window. - Used to manager the m_LuaRef lifetime. */ - std::atomic<int> m_PlayerCount; + /** The Lua object reference, used for keeping the object alive as long as any player has the window open */ + int m_LuaRef; - /** 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 the window is closing for any player */ + int m_OnClosingFnRef; + /** 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 f041ef524..523244ed2 100644 --- a/src/Bindings/ManualBindings.cpp +++ b/src/Bindings/ManualBindings.cpp @@ -992,13 +992,7 @@ static int tolua_cPluginManager_AddHook_FnRef(cPluginManager * a_PluginManager, } // Retrieve and check the hook type - int HookType; - if (!S.GetStackValue(a_ParamIdx, HookType)) - { - LOGWARNING("cPluginManager.AddHook(): Cannot read the hook type."); - S.LogStackTrace(); - return 0; - } + int HookType = static_cast<int>(tolua_tonumber(S, a_ParamIdx, -1)); if (!a_PluginManager->IsValidHookType(HookType)) { LOGWARNING("cPluginManager.AddHook(): Invalid HOOK_TYPE parameter: %d", HookType); @@ -1007,14 +1001,7 @@ static int tolua_cPluginManager_AddHook_FnRef(cPluginManager * a_PluginManager, } // Add the hook to the plugin - auto callback = std::make_shared<cLuaState::cCallback>(); - if (!S.GetStackValue(a_ParamIdx + 1, callback)) - { - LOGWARNING("cPluginManager.AddHook(): Cannot read the callback parameter"); - S.LogStackTrace(); - return 0; - } - if (!Plugin->AddHookCallback(HookType, callback)) + if (!Plugin->AddHookRef(HookType, a_ParamIdx + 1)) { LOGWARNING("cPluginManager.AddHook(): Cannot add hook %d, unknown error.", HookType); S.LogStackTrace(); @@ -1071,11 +1058,10 @@ static int tolua_cPluginManager_AddHook_DefFn(cPluginManager * a_PluginManager, } // Retrieve the function to call and add it to the plugin: - auto callback = std::make_shared<cLuaState::cCallback>(); - lua_getglobal(S, FnName); - bool res = S.GetStackValue(-1, callback); - lua_pop(S, 1); - if (!res || !callback->IsValid()) + lua_pushstring(S, FnName); + bool res = Plugin->AddHookRef(HookType, 1); + lua_pop(S, 1); // Pop the function off the stack + if (!res) { LOGWARNING("cPluginManager.AddHook(): Function %s not found. Hook not added.", FnName); S.LogStackTrace(); @@ -1599,6 +1585,55 @@ 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 @@ -1629,25 +1664,36 @@ static int tolua_cPlayer_PermissionMatches(lua_State * tolua_S) template < class OBJTYPE, - void (OBJTYPE::*SetCallback)(cLuaState::cCallbackPtr a_CallbackFn) + void (OBJTYPE::*SetCallback)(cPluginLua * a_Plugin, int a_FnRef) > 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: - cLuaState L(tolua_S); - OBJTYPE * self; - cLuaState::cCallbackPtr callback; - if (!L.GetStackValues(1, self, callback)) + OBJTYPE * self = reinterpret_cast<OBJTYPE *>(tolua_tousertype(tolua_S, 1, nullptr)); + if (self == nullptr) { - LOGWARNING("%s: Cannot get parameters", __FUNCTION__); - L.LogStackTrace(); + 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) + { + LOGERROR("%s: Cannot create a function reference. Callback not set.", __FUNCTION__); return 0; } // Set the callback - (self->*SetCallback)(callback); + (self->*SetCallback)(Plugin, FnRef); return 0; } @@ -1655,71 +1701,47 @@ static int tolua_SetObjectCallback(lua_State * tolua_S) -// Callback class used for the WebTab: -class cWebTabCallback: - public cWebAdmin::cWebTabCallback +static int tolua_cPluginLua_AddWebTab(lua_State * tolua_S) { -public: - /** The Lua callback to call to generate the page contents. */ - cLuaState::cCallback m_Callback; + cLuaState LuaState(tolua_S); + cPluginLua * self = nullptr; - virtual bool Call( - const HTTPRequest & a_Request, - const AString & a_UrlPath, - AString & a_Content, - AString & a_ContentType - ) override + if (!LuaState.GetStackValue(1, self)) { - AString content, contentType; - return m_Callback.Call(&a_Request, a_UrlPath, cLuaState::Return, a_Content, a_ContentType); + LOGWARNING("cPluginLua:AddWebTab: invalid self as first argument"); + return 0; } -}; - - - + tolua_Error tolua_err; + tolua_err.array = 0; + tolua_err.index = 3; + tolua_err.type = "function"; -static int tolua_cPluginLua_AddWebTab(lua_State * tolua_S) -{ - // OBSOLETE, use cWebAdmin:AddWebTab() instead! - // Function signature: - // cPluginLua:AddWebTab(Title, CallbackFn, [UrlPath]) - - // TODO: Warn about obsolete API usage - // Only implement after merging the new API change and letting some time for changes in the plugins + std::string Title; + int Reference = LUA_REFNIL; - // Check params: - cLuaState LuaState(tolua_S); - cPluginLua * self = cManualBindings::GetLuaPlugin(tolua_S); - if (self == nullptr) + if (LuaState.CheckParamString(2) && LuaState.CheckParamFunction(3)) { - return 0; + Reference = luaL_ref(tolua_S, LUA_REGISTRYINDEX); + LuaState.GetStackValue(2, Title); } - if ( - !LuaState.CheckParamString(2) || - !LuaState.CheckParamFunction(3) || - // Optional string as param 4 - !LuaState.CheckParamEnd(5) - ) + else { - return 0; + return cManualBindings::tolua_do_error(tolua_S, "#ferror calling function '#funcname#'", &tolua_err); } - // Read the params: - AString title, urlPath; - auto callback = std::make_shared<cWebTabCallback>(); - if (!LuaState.GetStackValues(2, title, callback->m_Callback)) + if (Reference != LUA_REFNIL) { - LOGWARNING("cPlugin:AddWebTab(): Cannot read required parameters"); - return 0; + if (!self->AddWebTab(Title.c_str(), tolua_S, Reference)) + { + luaL_unref(tolua_S, LUA_REGISTRYINDEX, Reference); + } } - if (!LuaState.GetStackValue(4, urlPath)) + else { - urlPath = cWebAdmin::GetURLEncodedString(title); + LOGWARNING("cPluginLua:AddWebTab: invalid function reference in 2nd argument (Title: \"%s\")", Title.c_str()); } - cRoot::Get()->GetWebAdmin()->AddWebTab(title, urlPath, self->GetName(), callback); - return 0; } @@ -2084,68 +2106,22 @@ static int tolua_cUrlParser_ParseAuthorityPart(lua_State * a_LuaState) -static int tolua_cWebAdmin_AddWebTab(lua_State * tolua_S) +static int tolua_cWebAdmin_GetPlugins(lua_State * tolua_S) { - // Function signatures: - // cWebAdmin:AddWebTab(Title, UrlPath, CallbackFn) + cWebAdmin * self = reinterpret_cast<cWebAdmin *>(tolua_tousertype(tolua_S, 1, nullptr)); - // 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; - } + const cWebAdmin::PluginList & AllPlugins = self->GetPlugins(); - // 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) -{ - // Function signature: - // cWebAdmin:GetAllWebTabs() -> { {"PluginName", "UrlPath", "Title"}, {"PluginName", "UrlPath", "Title"}, ...} - - // Don't care about params at all - - auto webTabs = cRoot::Get()->GetWebAdmin()->GetAllWebTabs(); - lua_createtable(tolua_S, static_cast<int>(webTabs.size()), 0); + lua_createtable(tolua_S, static_cast<int>(AllPlugins.size()), 0); int newTable = lua_gettop(tolua_S); int index = 1; - 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"); + 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"); lua_rawseti(tolua_S, newTable, index); + ++iter; ++index; } return 1; @@ -2155,42 +2131,14 @@ static int tolua_cWebAdmin_GetAllWebTabs(lua_State * tolua_S) -/** Binding for cWebAdmin::GetBaseURL. -Manual code required because ToLua generates an extra return value */ -static int tolua_cWebAdmin_GetBaseURL(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::GetBaseURL(Input)); - return 1; -} - - - - - -/** Binding for cWebAdmin::GetContentTypeFromFileExt. +/** Binding for cWebAdmin::GetHTMLEscapedString. Manual code required because ToLua generates an extra return value */ -static int tolua_cWebAdmin_GetContentTypeFromFileExt(lua_State * tolua_S) +static int tolua_AllToLua_cWebAdmin_GetHTMLEscapedString(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.CheckParamUserTable(1, "cWebAdmin") || !S.CheckParamString(2) || !S.CheckParamEnd(3) ) @@ -2203,7 +2151,7 @@ static int tolua_cWebAdmin_GetContentTypeFromFileExt(lua_State * tolua_S) S.GetStackValue(2, Input); // Convert and return: - S.Push(cWebAdmin::GetContentTypeFromFileExt(Input)); + S.Push(cWebAdmin::GetHTMLEscapedString(Input)); return 1; } @@ -2211,14 +2159,14 @@ static int tolua_cWebAdmin_GetContentTypeFromFileExt(lua_State * tolua_S) -/** Binding for cWebAdmin::GetHTMLEscapedString. +/** Binding for cWebAdmin::GetURLEncodedString. Manual code required because ToLua generates an extra return value */ -static int tolua_cWebAdmin_GetHTMLEscapedString(lua_State * tolua_S) +static int tolua_AllToLua_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.CheckParamUserTable(1, "cWebAdmin") || !S.CheckParamString(2) || !S.CheckParamEnd(3) ) @@ -2231,64 +2179,7 @@ static int tolua_cWebAdmin_GetHTMLEscapedString(lua_State * tolua_S) 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"); + S.Push(cWebAdmin::GetURLEncodedString(Input)); return 1; } @@ -2296,27 +2187,20 @@ static int tolua_cWebAdmin_GetPage(lua_State * tolua_S) -/** Binding for cWebAdmin::GetURLEncodedString. -Manual code required because ToLua generates an extra return value */ -static int tolua_cWebAdmin_GetURLEncodedString(lua_State * tolua_S) +static int tolua_cWebPlugin_GetTabNames(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) - ) + // 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) { - return 0; + 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; } - - // Get the parameters: - AString Input; - S.GetStackValue(2, Input); - - // Convert and return: - S.Push(cWebAdmin::GetURLEncodedString(Input)); return 1; } @@ -2739,79 +2623,6 @@ 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); @@ -3655,9 +3466,6 @@ 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); @@ -3678,6 +3486,7 @@ 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); @@ -3741,13 +3550,13 @@ void cManualBindings::Bind(lua_State * tolua_S) tolua_endmodule(tolua_S); tolua_beginmodule(tolua_S, "cWebAdmin"); - 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_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_endmodule(tolua_S); tolua_beginmodule(tolua_S, "HTTPRequest"); diff --git a/src/Bindings/ManualBindings_World.cpp b/src/Bindings/ManualBindings_World.cpp index 3faf038aa..00d2169d8 100644 --- a/src/Bindings/ManualBindings_World.cpp +++ b/src/Bindings/ManualBindings_World.cpp @@ -466,41 +466,67 @@ 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) { - // Function signature: - // World:QueueTask(Callback) + // Binding for cWorld::QueueTask + // Params: function - // Retrieve the args: - cLuaState L(tolua_S); - if ( - !L.CheckParamUserType(1, "cWorld") || - !L.CheckParamNumber (2) || - !L.CheckParamFunction(3) - ) + // 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; } - cWorld * World; - auto Task = std::make_shared<cLuaState::cCallback>(); - if (!L.GetStackValues(1, World, Task)) + + // Retrieve the args: + cWorld * self = reinterpret_cast<cWorld *>(tolua_tousertype(tolua_S, 1, nullptr)); + if (self == nullptr) { - return cManualBindings::lua_do_error(tolua_S, "Error in function call '#funcname#': Cannot read parameters"); + return cManualBindings::lua_do_error(tolua_S, "Error in function call '#funcname#': Not called on an object instance"); } - if (World == nullptr) + if (!lua_isfunction(tolua_S, 2)) { - 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#': Expected a function for parameter #1"); } - if (!Task->IsValid()) + + // Create a reference to the function: + int FnRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); + if (FnRef == LUA_REFNIL) { - return cManualBindings::lua_do_error(tolua_S, "Error in function call '#funcname#': Could not store the callback parameter"); + return cManualBindings::lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get function reference of parameter #1"); } - World->QueueTask([Task](cWorld & a_World) - { - Task->Call(&a_World); - } - ); + auto ResettableTask = std::make_shared<cLuaWorldTask>(*Plugin, FnRef); + Plugin->AddResettable(ResettableTask); + self->QueueTask(std::bind(&cLuaWorldTask::Run, ResettableTask, std::placeholders::_1)); return 0; } @@ -550,8 +576,16 @@ static int tolua_cWorld_SetSignLines(lua_State * tolua_S) static int tolua_cWorld_ScheduleTask(lua_State * tolua_S) { - // Function signature: - // World:ScheduleTask(NumTicks, Callback) + // 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; + } // Retrieve the args: cLuaState L(tolua_S); @@ -563,27 +597,22 @@ static int tolua_cWorld_ScheduleTask(lua_State * tolua_S) { return 0; } - 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"); - } + cWorld * World = reinterpret_cast<cWorld *>(tolua_tousertype(tolua_S, 1, nullptr)); if (World == nullptr) { return cManualBindings::lua_do_error(tolua_S, "Error in function call '#funcname#': Not called on an object instance"); } - if (!Task->IsValid()) + + // Create a reference to the function: + int FnRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); + if (FnRef == LUA_REFNIL) { - return cManualBindings::lua_do_error(tolua_S, "Error in function call '#funcname#': Could not store the callback parameter"); + return cManualBindings::lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get function reference of parameter #1"); } - World->ScheduleTask(NumTicks, [Task](cWorld & a_World) - { - Task->Call(&a_World); - } - ); + 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)); return 0; } diff --git a/src/Bindings/PluginLua.cpp b/src/Bindings/PluginLua.cpp index fd3e8bc69..a266e6223 100644 --- a/src/Bindings/PluginLua.cpp +++ b/src/Bindings/PluginLua.cpp @@ -15,8 +15,6 @@ #include "../CommandOutput.h" #include "PluginManager.h" #include "../Item.h" -#include "../Root.h" -#include "../WebAdmin.h" extern "C" { @@ -60,6 +58,7 @@ void cPluginLua::Close(void) // If already closed, bail out: if (!m_LuaState.IsValid()) { + ASSERT(m_Resettables.empty()); ASSERT(m_HookMap.empty()); return; } @@ -67,9 +66,28 @@ void cPluginLua::Close(void) // Remove the command bindings and web tabs: ClearCommands(); ClearConsoleCommands(); - ClearWebTabs(); + 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) // 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: @@ -187,7 +205,7 @@ bool cPluginLua::Load(void) void cPluginLua::Unload(void) { - ClearWebTabs(); + ClearTabs(); super::Unload(); Close(); } @@ -212,7 +230,16 @@ void cPluginLua::OnDisable(void) void cPluginLua::Tick(float a_Dt) { - CallSimpleHooks(cPluginManager::HOOK_TICK, 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); + } } @@ -221,7 +248,22 @@ void cPluginLua::Tick(float a_Dt) bool cPluginLua::OnBlockSpread(cWorld & a_World, int a_BlockX, int a_BlockY, int a_BlockZ, eSpreadSource a_Source) { - return CallSimpleHooks(cPluginManager::HOOK_BLOCK_SPREAD, &a_World, a_BlockX, a_BlockY, a_BlockZ, 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; } @@ -230,7 +272,22 @@ 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) { - return CallSimpleHooks(cPluginManager::HOOK_BLOCK_TO_PICKUPS, &a_World, a_Digger, a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta, &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; } @@ -239,7 +296,22 @@ bool cPluginLua::OnBlockToPickups(cWorld & a_World, cEntity * a_Digger, int a_Bl bool cPluginLua::OnBrewingCompleted(cWorld & a_World, cBrewingstandEntity & a_Brewingstand) { - return CallSimpleHooks(cPluginManager::HOOK_BREWING_COMPLETED, &a_World, &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; } @@ -248,7 +320,22 @@ bool cPluginLua::OnBrewingCompleted(cWorld & a_World, cBrewingstandEntity & a_Br bool cPluginLua::OnBrewingCompleting(cWorld & a_World, cBrewingstandEntity & a_Brewingstand) { - return CallSimpleHooks(cPluginManager::HOOK_BREWING_COMPLETING, &a_World, &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; } @@ -263,10 +350,10 @@ bool cPluginLua::OnChat(cPlayer & a_Player, AString & a_Message) return false; } bool res = false; - auto & hooks = m_HookMap[cPluginManager::HOOK_CHAT]; - for (auto hook: hooks) + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_CHAT]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) { - hook->Call(&a_Player, a_Message, cLuaState::Return, res, a_Message); + m_LuaState.Call(static_cast<int>(**itr), &a_Player, a_Message, cLuaState::Return, res, a_Message); if (res) { return true; @@ -281,7 +368,22 @@ bool cPluginLua::OnChat(cPlayer & a_Player, AString & a_Message) bool cPluginLua::OnChunkAvailable(cWorld & a_World, int a_ChunkX, int a_ChunkZ) { - return CallSimpleHooks(cPluginManager::HOOK_CHUNK_AVAILABLE, &a_World, a_ChunkX, 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; } @@ -290,7 +392,22 @@ 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) { - return CallSimpleHooks(cPluginManager::HOOK_CHUNK_GENERATED, &a_World, a_ChunkX, a_ChunkZ, 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; } @@ -299,7 +416,22 @@ 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) { - return CallSimpleHooks(cPluginManager::HOOK_CHUNK_GENERATING, &a_World, a_ChunkX, a_ChunkZ, 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; } @@ -308,7 +440,22 @@ bool cPluginLua::OnChunkGenerating(cWorld & a_World, int a_ChunkX, int a_ChunkZ, bool cPluginLua::OnChunkUnloaded(cWorld & a_World, int a_ChunkX, int a_ChunkZ) { - return CallSimpleHooks(cPluginManager::HOOK_CHUNK_UNLOADED, &a_World, a_ChunkX, 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; } @@ -317,7 +464,22 @@ bool cPluginLua::OnChunkUnloaded(cWorld & a_World, int a_ChunkX, int a_ChunkZ) bool cPluginLua::OnChunkUnloading(cWorld & a_World, int a_ChunkX, int a_ChunkZ) { - return CallSimpleHooks(cPluginManager::HOOK_CHUNK_UNLOADING, &a_World, a_ChunkX, 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; } @@ -326,7 +488,22 @@ bool cPluginLua::OnChunkUnloading(cWorld & a_World, int a_ChunkX, int a_ChunkZ) bool cPluginLua::OnCollectingPickup(cPlayer & a_Player, cPickup & a_Pickup) { - return CallSimpleHooks(cPluginManager::HOOK_COLLECTING_PICKUP, &a_Player, &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; } @@ -335,7 +512,22 @@ bool cPluginLua::OnCollectingPickup(cPlayer & a_Player, cPickup & a_Pickup) bool cPluginLua::OnCraftingNoRecipe(cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe & a_Recipe) { - return CallSimpleHooks(cPluginManager::HOOK_CRAFTING_NO_RECIPE, &a_Player, &a_Grid, &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; } @@ -344,7 +536,22 @@ bool cPluginLua::OnCraftingNoRecipe(cPlayer & a_Player, cCraftingGrid & a_Grid, bool cPluginLua::OnDisconnect(cClientHandle & a_Client, const AString & a_Reason) { - return CallSimpleHooks(cPluginManager::HOOK_DISCONNECT, &a_Client, 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; } @@ -353,7 +560,22 @@ 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) { - return CallSimpleHooks(cPluginManager::HOOK_ENTITY_ADD_EFFECT, &a_Entity, a_EffectType, a_EffectDurationTicks, a_EffectIntensity, 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; } @@ -362,7 +584,22 @@ bool cPluginLua::OnEntityAddEffect(cEntity & a_Entity, int a_EffectType, int a_E bool cPluginLua::OnEntityChangingWorld(cEntity & a_Entity, cWorld & a_World) { - return CallSimpleHooks(cPluginManager::HOOK_ENTITY_CHANGING_WORLD, &a_Entity, &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; } @@ -371,7 +608,22 @@ bool cPluginLua::OnEntityChangingWorld(cEntity & a_Entity, cWorld & a_World) bool cPluginLua::OnEntityChangedWorld(cEntity & a_Entity, cWorld & a_World) { - return CallSimpleHooks(cPluginManager::HOOK_ENTITY_CHANGED_WORLD, &a_Entity, &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; } @@ -386,10 +638,10 @@ bool cPluginLua::OnExecuteCommand(cPlayer * a_Player, const AStringVector & a_Sp return false; } bool res = false; - auto & hooks = m_HookMap[cPluginManager::HOOK_EXECUTE_COMMAND]; - for (auto & hook: hooks) + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_EXECUTE_COMMAND]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) { - hook->Call(a_Player, a_Split, a_EntireCommand, cLuaState::Return, res, a_Result); + m_LuaState.Call(static_cast<int>(**itr), a_Player, a_Split, a_EntireCommand, cLuaState::Return, res, a_Result); if (res) { return true; @@ -410,20 +662,20 @@ bool cPluginLua::OnExploded(cWorld & a_World, double a_ExplosionSize, bool a_Can return false; } bool res = false; - auto & hooks = m_HookMap[cPluginManager::HOOK_EXPLODED]; - for (auto & hook: hooks) + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_EXPLODED]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) { switch (a_Source) { - 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 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 esMax: { ASSERT(!"Invalid explosion source"); @@ -450,20 +702,20 @@ bool cPluginLua::OnExploding(cWorld & a_World, double & a_ExplosionSize, bool & return false; } bool res = false; - auto & hooks = m_HookMap[cPluginManager::HOOK_EXPLODING]; - for (auto & hook: hooks) + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_EXPLODING]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) { switch (a_Source) { - 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 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 esMax: { ASSERT(!"Invalid explosion source"); @@ -484,7 +736,22 @@ bool cPluginLua::OnExploding(cWorld & a_World, double & a_ExplosionSize, bool & bool cPluginLua::OnHandshake(cClientHandle & a_Client, const AString & a_Username) { - return CallSimpleHooks(cPluginManager::HOOK_HANDSHAKE, &a_Client, 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; } @@ -493,7 +760,22 @@ 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) { - return CallSimpleHooks(cPluginManager::HOOK_HOPPER_PULLING_ITEM, &a_World, &a_Hopper, a_DstSlotNum, &a_SrcEntity, 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; } @@ -502,7 +784,22 @@ 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) { - return CallSimpleHooks(cPluginManager::HOOK_HOPPER_PUSHING_ITEM, &a_World, &a_Hopper, a_SrcSlotNum, &a_DstEntity, 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; } @@ -517,10 +814,10 @@ bool cPluginLua::OnKilled(cEntity & a_Victim, TakeDamageInfo & a_TDI, AString & return false; } bool res = false; - auto & hooks = m_HookMap[cPluginManager::HOOK_KILLED]; - for (auto & hook: hooks) + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_KILLED]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) { - hook->Call(&a_Victim, &a_TDI, a_DeathMessage, cLuaState::Return, res, a_DeathMessage); + m_LuaState.Call(static_cast<int>(**itr), &a_Victim, &a_TDI, a_DeathMessage, cLuaState::Return, res, a_DeathMessage); if (res) { return true; @@ -535,7 +832,22 @@ bool cPluginLua::OnKilled(cEntity & a_Victim, TakeDamageInfo & a_TDI, AString & bool cPluginLua::OnKilling(cEntity & a_Victim, cEntity * a_Killer, TakeDamageInfo & a_TDI) { - return CallSimpleHooks(cPluginManager::HOOK_KILLING, &a_Victim, a_Killer, &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; } @@ -544,7 +856,22 @@ bool cPluginLua::OnKilling(cEntity & a_Victim, cEntity * a_Killer, TakeDamageInf bool cPluginLua::OnLogin(cClientHandle & a_Client, UInt32 a_ProtocolVersion, const AString & a_Username) { - return CallSimpleHooks(cPluginManager::HOOK_LOGIN, &a_Client, a_ProtocolVersion, 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; } @@ -553,7 +880,22 @@ bool cPluginLua::OnLogin(cClientHandle & a_Client, UInt32 a_ProtocolVersion, con bool cPluginLua::OnPlayerAnimation(cPlayer & a_Player, int a_Animation) { - return CallSimpleHooks(cPluginManager::HOOK_PLAYER_ANIMATION, &a_Player, 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; } @@ -562,7 +904,22 @@ 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) { - return CallSimpleHooks(cPluginManager::HOOK_PLAYER_BREAKING_BLOCK, &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_BlockType, 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; } @@ -571,7 +928,22 @@ 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) { - return CallSimpleHooks(cPluginManager::HOOK_PLAYER_BROKEN_BLOCK, &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_BlockType, 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; } @@ -580,7 +952,22 @@ bool cPluginLua::OnPlayerBrokenBlock(cPlayer & a_Player, int a_BlockX, int a_Blo bool cPluginLua::OnPlayerDestroyed(cPlayer & a_Player) { - return CallSimpleHooks(cPluginManager::HOOK_PLAYER_DESTROYED, &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; } @@ -589,7 +976,22 @@ bool cPluginLua::OnPlayerDestroyed(cPlayer & a_Player) bool cPluginLua::OnPlayerEating(cPlayer & a_Player) { - return CallSimpleHooks(cPluginManager::HOOK_PLAYER_EATING, &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; } @@ -598,7 +1000,22 @@ bool cPluginLua::OnPlayerEating(cPlayer & a_Player) bool cPluginLua::OnPlayerFoodLevelChange(cPlayer & a_Player, int a_NewFoodLevel) { - return CallSimpleHooks(cPluginManager::HOOK_PLAYER_FOOD_LEVEL_CHANGE, &a_Player, 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; } @@ -607,7 +1024,22 @@ bool cPluginLua::OnPlayerFoodLevelChange(cPlayer & a_Player, int a_NewFoodLevel) bool cPluginLua::OnPlayerFished(cPlayer & a_Player, const cItems & a_Reward) { - return CallSimpleHooks(cPluginManager::HOOK_PLAYER_FISHED, &a_Player, 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; } @@ -616,7 +1048,22 @@ bool cPluginLua::OnPlayerFished(cPlayer & a_Player, const cItems & a_Reward) bool cPluginLua::OnPlayerFishing(cPlayer & a_Player, cItems & a_Reward) { - return CallSimpleHooks(cPluginManager::HOOK_PLAYER_FISHING, &a_Player, &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; } @@ -625,7 +1072,22 @@ bool cPluginLua::OnPlayerFishing(cPlayer & a_Player, cItems & a_Reward) bool cPluginLua::OnPlayerJoined(cPlayer & a_Player) { - return CallSimpleHooks(cPluginManager::HOOK_PLAYER_JOINED, &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; } @@ -634,7 +1096,22 @@ 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) { - return CallSimpleHooks(cPluginManager::HOOK_PLAYER_LEFT_CLICK, &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, 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; } @@ -643,7 +1120,22 @@ 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) { - return CallSimpleHooks(cPluginManager::HOOK_PLAYER_MOVING, &a_Player, a_OldPosition, 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; } @@ -652,7 +1144,22 @@ bool cPluginLua::OnPlayerMoving(cPlayer & a_Player, const Vector3d & a_OldPositi bool cPluginLua::OnEntityTeleport(cEntity & a_Entity, const Vector3d & a_OldPosition, const Vector3d & a_NewPosition) { - return CallSimpleHooks(cPluginManager::HOOK_ENTITY_TELEPORT, &a_Entity, a_OldPosition, 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; } @@ -661,11 +1168,27 @@ bool cPluginLua::OnEntityTeleport(cEntity & a_Entity, const Vector3d & a_OldPosi bool cPluginLua::OnPlayerPlacedBlock(cPlayer & a_Player, const sSetBlock & a_BlockChange) { - 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 - ); + 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; } @@ -674,11 +1197,27 @@ bool cPluginLua::OnPlayerPlacedBlock(cPlayer & a_Player, const sSetBlock & a_Blo bool cPluginLua::OnPlayerPlacingBlock(cPlayer & a_Player, const sSetBlock & a_BlockChange) { - 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 - ); + 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; } @@ -687,7 +1226,22 @@ 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) { - return CallSimpleHooks(cPluginManager::HOOK_PLAYER_RIGHT_CLICK, &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, 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; } @@ -696,7 +1250,22 @@ bool cPluginLua::OnPlayerRightClick(cPlayer & a_Player, int a_BlockX, int a_Bloc bool cPluginLua::OnPlayerRightClickingEntity(cPlayer & a_Player, cEntity & a_Entity) { - return CallSimpleHooks(cPluginManager::HOOK_PLAYER_RIGHT_CLICKING_ENTITY, &a_Player, &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; } @@ -705,7 +1274,22 @@ bool cPluginLua::OnPlayerRightClickingEntity(cPlayer & a_Player, cEntity & a_Ent bool cPluginLua::OnPlayerShooting(cPlayer & a_Player) { - return CallSimpleHooks(cPluginManager::HOOK_PLAYER_SHOOTING, &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; } @@ -714,7 +1298,22 @@ bool cPluginLua::OnPlayerShooting(cPlayer & a_Player) bool cPluginLua::OnPlayerSpawned(cPlayer & a_Player) { - return CallSimpleHooks(cPluginManager::HOOK_PLAYER_SPAWNED, &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; } @@ -723,7 +1322,22 @@ bool cPluginLua::OnPlayerSpawned(cPlayer & a_Player) bool cPluginLua::OnPlayerTossingItem(cPlayer & a_Player) { - return CallSimpleHooks(cPluginManager::HOOK_PLAYER_TOSSING_ITEM, &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; } @@ -732,7 +1346,22 @@ 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) { - 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); + 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; } @@ -741,7 +1370,22 @@ 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) { - return CallSimpleHooks(cPluginManager::HOOK_PLAYER_USED_ITEM, &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, 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; } @@ -750,7 +1394,22 @@ 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) { - 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); + 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; } @@ -759,7 +1418,22 @@ 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) { - return CallSimpleHooks(cPluginManager::HOOK_PLAYER_USING_ITEM, &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, 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; } @@ -768,7 +1442,22 @@ 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) { - return CallSimpleHooks(cPluginManager::HOOK_PLUGIN_MESSAGE, &a_Client, a_Channel, 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; } @@ -783,11 +1472,11 @@ bool cPluginLua::OnPluginsLoaded(void) return false; } bool res = false; - auto & hooks = m_HookMap[cPluginManager::HOOK_PLUGINS_LOADED]; - for (auto & hook: hooks) + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLUGINS_LOADED]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) { bool ret = false; - hook->Call(cLuaState::Return, ret); + m_LuaState.Call(static_cast<int>(**itr), cLuaState::Return, ret); res = res || ret; } return res; @@ -799,7 +1488,22 @@ bool cPluginLua::OnPluginsLoaded(void) bool cPluginLua::OnPostCrafting(cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe & a_Recipe) { - return CallSimpleHooks(cPluginManager::HOOK_POST_CRAFTING, &a_Player, &a_Grid, &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; } @@ -808,7 +1512,22 @@ bool cPluginLua::OnPostCrafting(cPlayer & a_Player, cCraftingGrid & a_Grid, cCra bool cPluginLua::OnPreCrafting(cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe & a_Recipe) { - return CallSimpleHooks(cPluginManager::HOOK_PRE_CRAFTING, &a_Player, &a_Grid, &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; } @@ -817,7 +1536,22 @@ 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) { - return CallSimpleHooks(cPluginManager::HOOK_PROJECTILE_HIT_BLOCK, &a_Projectile, a_BlockX, a_BlockY, a_BlockZ, a_Face, 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; } @@ -826,7 +1560,22 @@ bool cPluginLua::OnProjectileHitBlock(cProjectileEntity & a_Projectile, int a_Bl bool cPluginLua::OnProjectileHitEntity(cProjectileEntity & a_Projectile, cEntity & a_HitEntity) { - return CallSimpleHooks(cPluginManager::HOOK_PROJECTILE_HIT_ENTITY, &a_Projectile, &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; } @@ -841,10 +1590,10 @@ bool cPluginLua::OnServerPing(cClientHandle & a_ClientHandle, AString & a_Server return false; } bool res = false; - auto & hooks = m_HookMap[cPluginManager::HOOK_SERVER_PING]; - for (auto & hook: hooks) + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_SERVER_PING]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) { - hook->Call(&a_ClientHandle, a_ServerDescription, a_OnlinePlayersCount, a_MaxPlayersCount, a_Favicon, cLuaState::Return, res, a_ServerDescription, a_OnlinePlayersCount, a_MaxPlayersCount, a_Favicon); + 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); if (res) { return true; @@ -859,7 +1608,22 @@ bool cPluginLua::OnServerPing(cClientHandle & a_ClientHandle, AString & a_Server bool cPluginLua::OnSpawnedEntity(cWorld & a_World, cEntity & a_Entity) { - return CallSimpleHooks(cPluginManager::HOOK_SPAWNED_ENTITY, &a_World, &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; } @@ -868,7 +1632,22 @@ bool cPluginLua::OnSpawnedEntity(cWorld & a_World, cEntity & a_Entity) bool cPluginLua::OnSpawnedMonster(cWorld & a_World, cMonster & a_Monster) { - return CallSimpleHooks(cPluginManager::HOOK_SPAWNED_MONSTER, &a_World, &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; } @@ -877,7 +1656,22 @@ bool cPluginLua::OnSpawnedMonster(cWorld & a_World, cMonster & a_Monster) bool cPluginLua::OnSpawningEntity(cWorld & a_World, cEntity & a_Entity) { - return CallSimpleHooks(cPluginManager::HOOK_SPAWNING_ENTITY, &a_World, &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; } @@ -886,7 +1680,22 @@ bool cPluginLua::OnSpawningEntity(cWorld & a_World, cEntity & a_Entity) bool cPluginLua::OnSpawningMonster(cWorld & a_World, cMonster & a_Monster) { - return CallSimpleHooks(cPluginManager::HOOK_SPAWNING_MONSTER, &a_World, &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; } @@ -895,7 +1704,22 @@ bool cPluginLua::OnSpawningMonster(cWorld & a_World, cMonster & a_Monster) bool cPluginLua::OnTakeDamage(cEntity & a_Receiver, TakeDamageInfo & a_TDI) { - return CallSimpleHooks(cPluginManager::HOOK_TAKE_DAMAGE, &a_Receiver, &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; } @@ -909,7 +1733,22 @@ bool cPluginLua::OnUpdatedSign( cPlayer * a_Player ) { - return CallSimpleHooks(cPluginManager::HOOK_UPDATED_SIGN, &a_World, a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4, 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; } @@ -929,10 +1768,10 @@ bool cPluginLua::OnUpdatingSign( return false; } bool res = false; - auto & hooks = m_HookMap[cPluginManager::HOOK_UPDATING_SIGN]; - for (auto & hook: hooks) + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_UPDATING_SIGN]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) { - 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); + 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); if (res) { return true; @@ -947,7 +1786,22 @@ bool cPluginLua::OnUpdatingSign( bool cPluginLua::OnWeatherChanged(cWorld & a_World) { - return CallSimpleHooks(cPluginManager::HOOK_WEATHER_CHANGED, &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; } @@ -962,10 +1816,10 @@ bool cPluginLua::OnWeatherChanging(cWorld & a_World, eWeather & a_NewWeather) return false; } bool res = false; - auto & hooks = m_HookMap[cPluginManager::HOOK_WEATHER_CHANGING]; - for (auto & hook: hooks) + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_WEATHER_CHANGING]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) { - hook->Call(&a_World, a_NewWeather, cLuaState::Return, res, a_NewWeather); + m_LuaState.Call(static_cast<int>(**itr), &a_World, a_NewWeather, cLuaState::Return, res, a_NewWeather); if (res) { return true; @@ -980,7 +1834,17 @@ bool cPluginLua::OnWeatherChanging(cWorld & a_World, eWeather & a_NewWeather) bool cPluginLua::OnWorldStarted(cWorld & a_World) { - return CallSimpleHooks(cPluginManager::HOOK_WORLD_STARTED, &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; } @@ -989,7 +1853,17 @@ bool cPluginLua::OnWorldStarted(cWorld & a_World) bool cPluginLua::OnWorldTick(cWorld & a_World, std::chrono::milliseconds a_Dt, std::chrono::milliseconds a_LastTickDurationMSec) { - return CallSimpleHooks(cPluginManager::HOOK_WORLD_TICK, &a_World, a_Dt, 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; } @@ -1185,11 +2059,22 @@ const char * cPluginLua::GetHookFnName(int a_HookType) -bool cPluginLua::AddHookCallback(int a_HookType, cLuaState::cCallbackPtr a_Callback) +bool cPluginLua::AddHookRef(int a_HookType, int a_FnRefIdx) { ASSERT(m_CriticalSection.IsLockedByCurrentThread()); // It probably has to be, how else would we have a LuaState? - m_HookMap[a_HookType].push_back(a_Callback); + // 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); return true; } @@ -1231,6 +2116,61 @@ 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) +{ + cCSLock Lock(m_CriticalSection); + if (a_LuaState != m_LuaState) + { + LOGERROR("Only allowed to add a tab to a WebPlugin of your own Plugin!"); + return false; + } + 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()); @@ -1287,13 +2227,22 @@ void cPluginLua::CallbackWindowSlotChanged(int a_FnRef, cWindow & a_Window, int -void cPluginLua::ClearWebTabs(void) +//////////////////////////////////////////////////////////////////////////////// +// cPluginLua::cResettable: + +cPluginLua::cResettable::cResettable(cPluginLua & a_Plugin): + m_Plugin(&a_Plugin) { - auto webAdmin = cRoot::Get()->GetWebAdmin(); - if (webAdmin != nullptr) // can be nullptr when shutting down the server - { - webAdmin->RemoveAllPluginWebTabs(m_Name); - } +} + + + + + +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 e53fbaaa8..db6612671 100644 --- a/src/Bindings/PluginLua.h +++ b/src/Bindings/PluginLua.h @@ -10,6 +10,7 @@ #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 @@ -28,7 +29,8 @@ class cWindow; // tolua_begin class cPluginLua : - public cPlugin + public cPlugin, + public cWebPlugin { typedef cPlugin super; @@ -62,6 +64,36 @@ public: + /** 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(); @@ -149,6 +181,14 @@ public: /** 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); @@ -171,9 +211,11 @@ public: /** 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 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); + /** 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); /** 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. @@ -193,23 +235,29 @@ public: return m_LuaState.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::cCallbackPtr> cLuaCallbacks; + typedef std::vector<cLuaState::cRef *> cLuaRefs; /** Maps hook types into arrays of Lua function references to call for each hook type */ - typedef std::map<int, cLuaCallbacks> cHookMap; + typedef std::map<int, cLuaRefs> cHookMap; - /** The mutex protecting m_LuaState against multithreaded use. */ + /** 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; @@ -222,30 +270,6 @@ protected: /** 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) - { - cCSLock lock(m_CriticalSection); - 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 203450505..5b3ef7803 100644 --- a/src/Bindings/PluginManager.cpp +++ b/src/Bindings/PluginManager.cpp @@ -1965,23 +1965,6 @@ 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 a97582fbe..f3f0b6d0b 100644 --- a/src/Bindings/PluginManager.h +++ b/src/Bindings/PluginManager.h @@ -332,9 +332,6 @@ 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 diff --git a/src/Bindings/WebPlugin.cpp b/src/Bindings/WebPlugin.cpp new file mode 100644 index 000000000..1eca7de93 --- /dev/null +++ b/src/Bindings/WebPlugin.cpp @@ -0,0 +1,152 @@ + +#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 new file mode 100644 index 000000000..6dc8db801 --- /dev/null +++ b/src/Bindings/WebPlugin.h @@ -0,0 +1,80 @@ + +#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 bca9c5547..6d092eeb3 100644 --- a/src/Entities/Player.h +++ b/src/Entities/Player.h @@ -225,10 +225,10 @@ public: cWindow * GetWindow(void) { return m_CurrentWindow; } // tolua_export const cWindow * GetWindow(void) const { return m_CurrentWindow; } - // tolua_begin - /** Opens the specified window; closes the current one first using CloseWindow() */ - void OpenWindow(cWindow * a_Window); + void OpenWindow(cWindow * a_Window); // Exported in ManualBindings.cpp + + // tolua_begin /** 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/UI/Window.cpp b/src/UI/Window.cpp index 4582d6cf4..5a2e55feb 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 is holding an item while closing their window, dropping it as a pickup..."); + LOGD("Player holds item! Dropping it..."); a_Player.TossHeldItem(a_Player.GetDraggingItem().m_ItemCount); } diff --git a/src/WebAdmin.cpp b/src/WebAdmin.cpp index 5c08deb0d..e43d749dd 100644 --- a/src/WebAdmin.cpp +++ b/src/WebAdmin.cpp @@ -2,6 +2,10 @@ #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" @@ -83,9 +87,9 @@ public: // cWebAdmin: cWebAdmin::cWebAdmin(void) : - m_TemplateScript("<webadmin_template>"), m_IsInitialized(false), - m_IsRunning(false) + m_IsRunning(false), + m_TemplateScript("<webadmin_template>") { } @@ -102,9 +106,40 @@ 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 (!LoadIniFile()) + 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)) { // WebAdmin is disabled, bail out faking a success return true; @@ -112,7 +147,31 @@ bool cWebAdmin::Init(void) LOGD("Initialising WebAdmin..."); - Reload(); + // 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>"; + } // Read the ports to be used: // Note that historically the ports were stored in the "Port" and "PortsIPv6" values @@ -165,7 +224,7 @@ void cWebAdmin::Stop(void) -bool cWebAdmin::LoadLoginPage(void) +bool cWebAdmin::LoadLoginTemplate(void) { cFile File(FILE_IO_PREFIX "webadmin/login_template.html", cFile::fmRead); if (!File.IsOpen()) @@ -179,8 +238,7 @@ bool cWebAdmin::LoadLoginPage(void) return false; } - cCSLock Lock(m_CS); - m_LoginPage = TemplateContent; + m_LoginTemplate = TemplateContent; return true; } @@ -188,89 +246,6 @@ bool cWebAdmin::LoadLoginPage(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()) @@ -280,20 +255,17 @@ 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)) { - 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; - } + 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.empty() && (BareURL[1] != '~')); + bool ShouldWrapInTemplate = ((BareURL.length() > 1) && (BareURL[1] != '~')); // Retrieve the request data: auto Data = std::static_pointer_cast<cWebadminRequestData>(a_Request.GetUserData()); @@ -340,7 +312,6 @@ 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; @@ -354,12 +325,59 @@ void cWebAdmin::HandleWebadminRequest(cHTTPServerConnection & a_Connection, cHTT return; } - // 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()); + 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()); a_Connection.FinishResponse(); } @@ -374,7 +392,7 @@ void cWebAdmin::HandleRootRequest(cHTTPServerConnection & a_Connection, cHTTPInc cHTTPOutgoingResponse Resp; Resp.SetContentType("text/html"); a_Connection.Send(Resp); - a_Connection.Send(m_LoginPage); + a_Connection.Send(m_LoginTemplate); a_Connection.FinishResponse(); } @@ -388,7 +406,7 @@ void cWebAdmin::HandleFileRequest(cHTTPServerConnection & a_Connection, cHTTPInc std::replace(FileURL.begin(), FileURL.end(), '\\', '/'); // Remove all leading backslashes: - if (!FileURL.empty() && (FileURL[0] == '/')) + if (FileURL[0] == '/') { size_t FirstCharToRead = FileURL.find_first_not_of('/'); if (FirstCharToRead != AString::npos) @@ -400,9 +418,8 @@ void cWebAdmin::HandleFileRequest(cHTTPServerConnection & a_Connection, cHTTPInc // Remove all "../" strings: ReplaceString(FileURL, "../", ""); - // Read the file contents and guess its mime-type, based on the extension: + bool LoadedSuccessfull = false; 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)) { @@ -410,17 +427,18 @@ void cWebAdmin::HandleFileRequest(cHTTPServerConnection & a_Connection, cHTTPInc AString FileContent; if (File.IsOpen() && (File.ReadRestOfFile(FileContent) != -1)) { - std::swap(Content, FileContent); - size_t LastPointPosition = Path.find_last_of('.'); - if (LastPointPosition != AString::npos) - { - ContentType = GetContentTypeFromFileExt(Path.substr(LastPointPosition + 1)); - } + LoadedSuccessfull = true; + Content = FileContent; } } - if (ContentType.empty()) + + // 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())) { - ContentType = "application/unknown"; + AString FileExtension = Path.substr(LastPointPosition + 1); + ContentType = GetContentTypeFromFileExt(FileExtension); } // Send the response: @@ -438,36 +456,32 @@ void cWebAdmin::HandleFileRequest(cHTTPServerConnection & a_Connection, cHTTPInc AString cWebAdmin::GetContentTypeFromFileExt(const AString & a_FileExtension) { static bool IsInitialized = false; - static AStringMap ContentTypeMap; + static std::map<AString, AString> 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"] = "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; + 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]; } @@ -476,93 +490,86 @@ AString cWebAdmin::GetContentTypeFromFileExt(const AString & a_FileExtension) sWebAdminPage cWebAdmin::GetPage(const HTTPRequest & a_Request) { - sWebAdminPage page; - auto split = StringSplit(a_Request.Path, "/"); - - // If no specific page was requested, return an empty object: - if (split.size() <= 2) - { - return page; - } + sWebAdminPage Page; + AStringVector Split = StringSplit(a_Request.Path, "/"); - // Find the WebTab handler responsible for the request: - cWebTabPtr tab; + // Find the plugin that corresponds to the requested path + AString FoundPlugin; + if (Split.size() > 1) { - cCSLock Lock(m_CS); - for (auto & wt: m_WebTabs) + for (PluginList::iterator itr = m_Plugins.begin(); itr != m_Plugins.end(); ++itr) { - if ( - (wt->m_PluginName == split[1]) && - (wt->m_UrlPath == split[2]) - ) + if ((*itr)->GetWebTitle() == Split[1]) { - tab = wt; + 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; 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 page; + // Return the page contents + return Page; } -AString cWebAdmin::GetBaseURL(const AString & a_URL) +AString cWebAdmin::GetDefaultPage(void) { - return GetBaseURL(StringSplit(a_URL, "/")); -} - + AString Content; + Content += "<h4>Server Name:</h4>"; + Content += "<p>" + AString( cRoot::Get()->GetServer()->GetServerID()) + "</p>"; + // 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>"; -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)); + // 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; } -bool cWebAdmin::DelWebTab(const AString & a_UrlPath) +AString cWebAdmin::GetBaseURL(const AString & 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; + return GetBaseURL(StringSplit(a_URL, "/")); } diff --git a/src/WebAdmin.h b/src/WebAdmin.h index b76ca6df8..5e48f597c 100644 --- a/src/WebAdmin.h +++ b/src/WebAdmin.h @@ -89,14 +89,14 @@ struct HTTPTemplateRequest +// tolua_begin struct sWebAdminPage { AString Content; AString PluginName; - AString TabTitle; - AString TabUrlPath; - AString ContentType; + AString TabName; }; +// tolua_end @@ -111,49 +111,7 @@ class cWebAdmin : public: // tolua_end - /** 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; + typedef std::list< cWebPlugin* > PluginList; cWebAdmin(void); @@ -162,114 +120,80 @@ public: /** Initializes the object. Returns true if successfully initialized and ready to start */ bool Init(void); - /** Starts the HTTP server taking care of the webadmin. Returns true if successful */ + /** Starts the HTTP server taking care of the admin. Returns true if successful */ bool Start(void); /** Stops the HTTP server, if it was started. */ void Stop(void); - /** Loads the login template into m_LoginPage. - Returns true if the loading succeeds, false if not. */ - bool LoadLoginPage(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); - /** Returns a copy of all the registered web tabs. - Exported to Lua in ManualBindings.cpp. */ - cWebTabPtrs GetAllWebTabs(void) { return m_WebTabs; } + // 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 << - /** Removes all WebTabs registered by the specified plugin. */ - void RemoveAllPluginWebTabs(const AString & a_PluginName); + // tolua_begin - /** 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); - // tolua_begin + /** Returns the contents of the default page - the list of plugins and players */ + AString GetDefaultPage(void); - /** 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 prefix needed for making a link point to the webadmin root from the given URL ("../../../webadmin"-style) */ + AString GetBaseURL(const AString & a_URL); - /** Returns the list of ports on which the webadmin is configured to listen. */ + /** Returns the list of ports used for the webadmin. */ 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 - /** 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. */ + /** Escapes text passed into it, so it can be embedded into html. */ static AString GetHTMLEscapedString(const AString & a_Input); - /** Escapes the string for use in an URL - Exported to Lua in ManualBindings.cpp. */ + /** Escapes the string for use in an URL */ 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 an empty string. - Exported to Lua in ManualBindings.cpp. */ + /** Returns the content type from the file extension. If the extension isn't in the list, the function returns "text/html" */ 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 HTTP server which provides the underlying HTTP parsing, serialization and events */ - cHTTPServer m_HTTPServer; + /** The Lua template script to provide templates: */ + cLuaState m_TemplateScript; + /** The template that provides the login site: */ + AString m_LoginTemplate; - /** 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); + /** The HTTP server which provides the underlying HTTP parsing, serialization and events */ + cHTTPServer m_HTTPServer; /** Handles requests coming to the "/webadmin" or "/~webadmin" URLs */ void HandleWebadminRequest(cHTTPServerConnection & a_Connection, cHTTPIncomingRequest & a_Request); |