diff options
author | Mattes D <github@xoft.cz> | 2016-03-17 22:21:10 +0100 |
---|---|---|
committer | Mattes D <github@xoft.cz> | 2016-03-17 22:21:10 +0100 |
commit | 46430911feef2247c22141a31182b245081f00a6 (patch) | |
tree | 10155f63ac6ba948d965853ae20a083f7f49ecba /src | |
parent | Merge pull request #3081 from Arthur2e5/master (diff) | |
parent | Changed cLuaWindow callbacks to use cLuaState::cCallback. (diff) | |
download | cuberite-46430911feef2247c22141a31182b245081f00a6.tar cuberite-46430911feef2247c22141a31182b245081f00a6.tar.gz cuberite-46430911feef2247c22141a31182b245081f00a6.tar.bz2 cuberite-46430911feef2247c22141a31182b245081f00a6.tar.lz cuberite-46430911feef2247c22141a31182b245081f00a6.tar.xz cuberite-46430911feef2247c22141a31182b245081f00a6.tar.zst cuberite-46430911feef2247c22141a31182b245081f00a6.zip |
Diffstat (limited to '')
-rw-r--r-- | src/Bindings/AllToLua.pkg | 1 | ||||
-rw-r--r-- | src/Bindings/CMakeLists.txt | 3 | ||||
-rw-r--r-- | src/Bindings/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, 1259 insertions, 1925 deletions
diff --git a/src/Bindings/AllToLua.pkg b/src/Bindings/AllToLua.pkg index 991ed0ddd..6ca9c8658 100644 --- a/src/Bindings/AllToLua.pkg +++ b/src/Bindings/AllToLua.pkg @@ -38,7 +38,6 @@ $cfile "LuaFunctions.h" $cfile "PluginManager.h" $cfile "Plugin.h" $cfile "PluginLua.h" -$cfile "WebPlugin.h" $cfile "LuaWindow.h" $cfile "../BlockID.h" diff --git a/src/Bindings/CMakeLists.txt b/src/Bindings/CMakeLists.txt index 10cda1efb..4f25f2cf4 100644 --- a/src/Bindings/CMakeLists.txt +++ b/src/Bindings/CMakeLists.txt @@ -23,7 +23,6 @@ SET (SRCS Plugin.cpp PluginLua.cpp PluginManager.cpp - WebPlugin.cpp ) SET (HDRS @@ -44,7 +43,6 @@ SET (HDRS Plugin.h PluginLua.h PluginManager.h - WebPlugin.h tolua++.h ) @@ -66,7 +64,6 @@ set(BINDING_DEPENDENCIES ../Bindings/Plugin.h ../Bindings/PluginLua.h ../Bindings/PluginManager.h - ../Bindings/WebPlugin.h ../BiomeDef.h ../BlockArea.h ../BlockEntities/BeaconEntity.h diff --git a/src/Bindings/LuaState.cpp b/src/Bindings/LuaState.cpp index 200878cf7..98507e96c 100644 --- a/src/Bindings/LuaState.cpp +++ b/src/Bindings/LuaState.cpp @@ -20,6 +20,10 @@ extern "C" #include "../Entities/Entity.h" #include "../BlockEntities/BlockEntity.h" + + + + // fwd: "SQLite/lsqlite3.c" extern "C" { @@ -39,6 +43,10 @@ extern "C" const cLuaState::cRet cLuaState::Return = {}; +/** Each Lua state stores a pointer to its creating cLuaState in Lua globals, under this name. +This way any cLuaState can reference the main cLuaState's TrackedCallbacks, mutex etc. */ +static const char * g_CanonLuaStateGlobalName = "_CuberiteInternal_CanonLuaState"; + @@ -114,6 +122,101 @@ 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) : @@ -170,6 +273,10 @@ void cLuaState::Create(void) luaL_openlibs(m_LuaState); m_IsOwned = true; cLuaStateTracker::Add(*this); + + // Add the CanonLuaState value into the Lua state, so that we can get it from anywhere: + lua_pushlightuserdata(m_LuaState, reinterpret_cast<void *>(this)); + lua_setglobal(m_LuaState, g_CanonLuaStateGlobalName); } @@ -206,6 +313,16 @@ void cLuaState::Close(void) Detach(); return; } + + // Invalidate all callbacks: + { + cCSLock Lock(m_CSTrackedCallbacks); + for (auto & c: m_TrackedCallbacks) + { + c->Invalidate(); + } + } + cLuaStateTracker::Del(*this); lua_close(m_LuaState); m_LuaState = nullptr; @@ -821,6 +938,18 @@ void cLuaState::Push(std::chrono::milliseconds a_Value) +void cLuaState::Pop(int a_NumValuesToPop) +{ + ASSERT(IsValid()); + + lua_pop(m_LuaState, a_NumValuesToPop); + m_NumCurrentFunctionArgs -= a_NumValuesToPop; +} + + + + + bool cLuaState::GetStackValue(int a_StackPos, AString & a_Value) { size_t len = 0; @@ -847,6 +976,24 @@ 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)) @@ -1531,7 +1678,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.LogStack("Stack where copying failed:"); + a_SrcLuaState.LogStackValues("Stack where copying failed:"); lua_pop(m_LuaState, i - a_SrcStart); return -1; } @@ -1558,16 +1705,16 @@ void cLuaState::ToString(int a_StackPos, AString & a_String) -void cLuaState::LogStack(const char * a_Header) +void cLuaState::LogStackValues(const char * a_Header) { - LogStack(m_LuaState, a_Header); + LogStackValues(m_LuaState, a_Header); } -void cLuaState::LogStack(lua_State * a_LuaState, const char * a_Header) +void cLuaState::LogStackValues(lua_State * a_LuaState, const char * a_Header) { // Format string consisting only of %s is used to appease the compiler LOG("%s", (a_Header != nullptr) ? a_Header : "Lua C API Stack contents:"); @@ -1592,6 +1739,21 @@ void cLuaState::LogStack(lua_State * a_LuaState, const char * a_Header) +cLuaState * cLuaState::QueryCanonLuaState(void) +{ + // Get the CanonLuaState global from Lua: + auto cb = WalkToNamedGlobal(g_CanonLuaStateGlobalName); + if (!cb.IsValid()) + { + return nullptr; + } + return reinterpret_cast<cLuaState *>(lua_touserdata(m_LuaState, -1)); +} + + + + + int cLuaState::ReportFnCallErrors(lua_State * a_LuaState) { LOGWARNING("LUA: %s", lua_tostring(a_LuaState, -1)); @@ -1626,6 +1788,50 @@ int cLuaState::BreakIntoDebugger(lua_State * a_LuaState) +void cLuaState::TrackCallback(cCallback & a_Callback) +{ + // Get the CanonLuaState global from Lua: + auto canonState = QueryCanonLuaState(); + if (canonState == nullptr) + { + LOGWARNING("%s: Lua state %p has invalid CanonLuaState!", __FUNCTION__, reinterpret_cast<void *>(m_LuaState)); + return; + } + + // Add the callback: + cCSLock Lock(canonState->m_CSTrackedCallbacks); + canonState->m_TrackedCallbacks.push_back(&a_Callback); +} + + + + + +void cLuaState::UntrackCallback(cCallback & a_Callback) +{ + // Get the CanonLuaState global from Lua: + auto canonState = QueryCanonLuaState(); + if (canonState == nullptr) + { + LOGWARNING("%s: Lua state %p has invalid CanonLuaState!", __FUNCTION__, reinterpret_cast<void *>(m_LuaState)); + return; + } + + // Remove the callback: + cCSLock Lock(canonState->m_CSTrackedCallbacks); + auto & trackedCallbacks = canonState->m_TrackedCallbacks; + trackedCallbacks.erase(std::remove_if(trackedCallbacks.begin(), trackedCallbacks.end(), + [&a_Callback](cCallback * a_StoredCallback) + { + return (a_StoredCallback == &a_Callback); + } + )); +} + + + + + //////////////////////////////////////////////////////////////////////////////// // cLuaState::cRef: @@ -1681,7 +1887,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); } @@ -1692,11 +1898,9 @@ void cLuaState::cRef::RefStack(cLuaState & a_LuaState, int a_StackPos) void cLuaState::cRef::UnRef(void) { - ASSERT(m_LuaState->IsValid()); // The reference should be destroyed before destroying the LuaState - if (IsValid()) { - luaL_unref(*m_LuaState, LUA_REGISTRYINDEX, m_Ref); + luaL_unref(m_LuaState, LUA_REGISTRYINDEX, m_Ref); } m_LuaState = nullptr; m_Ref = LUA_REFNIL; diff --git a/src/Bindings/LuaState.h b/src/Bindings/LuaState.h index 215980033..0509b09ed 100644 --- a/src/Bindings/LuaState.h +++ b/src/Bindings/LuaState.h @@ -80,8 +80,20 @@ public: /** Allows to use this class wherever an int (i. e. ref) is to be used */ explicit operator int(void) const { return m_Ref; } + /** Returns the Lua state associated with the value. */ + lua_State * GetLuaState(void) { return m_LuaState; } + + /** Creates a Lua reference to the specified object instance in the specified Lua state. + This is useful to make anti-GC references for objects that were created by Lua and need to stay alive longer than Lua GC would normally guarantee. */ + template <typename T> void CreateFromObject(cLuaState & a_LuaState, T && a_Object) + { + a_LuaState.Push(std::forward<T>(a_Object)); + RefStack(a_LuaState, -1); + a_LuaState.Pop(); + } + protected: - cLuaState * m_LuaState; + lua_State * m_LuaState; int m_Ref; // Remove the copy-constructor: @@ -112,6 +124,76 @@ 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 { @@ -261,11 +343,16 @@ 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); @@ -428,10 +515,14 @@ public: void ToString(int a_StackPos, AString & a_String); /** Logs all the elements' types on the API stack, with an optional header for the listing. */ - void LogStack(const char * a_Header = nullptr); + void LogStackValues(const char * a_Header = nullptr); /** Logs all the elements' types on the API stack, with an optional header for the listing. */ - static void LogStack(lua_State * a_LuaState, const char * a_Header = nullptr); + static void LogStackValues(lua_State * a_LuaState, const char * a_Header = nullptr); + + /** Returns the canon Lua state (the primary cLuaState instance that was used to create, rather than attach, the lua_State structure). + Returns nullptr if the canon Lua state cannot be queried. */ + cLuaState * QueryCanonLuaState(void); protected: @@ -441,8 +532,7 @@ 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) */ @@ -451,6 +541,15 @@ protected: /** Number of arguments currently pushed (for the Push / Call chain) */ int m_NumCurrentFunctionArgs; + /** The tracked callbacks. + This object will invalidate all of these when it is about to be closed. + Protected against multithreaded access by m_CSTrackedCallbacks. */ + std::vector<cCallback *> m_TrackedCallbacks; + + /** Protects m_TrackedTallbacks against multithreaded access. */ + cCriticalSection m_CSTrackedCallbacks; + + /** Variadic template terminator: If there's nothing more to push / pop, just call the function. Note that there are no return values either, because those are prefixed by a cRet value, so the arg list is never empty. */ bool PushCallPop(void) @@ -533,6 +632,14 @@ protected: /** Tries to break into the MobDebug debugger, if it is installed. */ static int BreakIntoDebugger(lua_State * a_LuaState); + + /** Adds the specified callback to tracking. + The callback will be invalidated when this Lua state is about to be closed. */ + void TrackCallback(cCallback & a_Callback); + + /** Removes the specified callback from tracking. + The callback will no longer be invalidated when this Lua state is about to be closed. */ + void UntrackCallback(cCallback & a_Callback); } ; diff --git a/src/Bindings/LuaWindow.cpp b/src/Bindings/LuaWindow.cpp index 706397a27..bf3f7cfde 100644 --- a/src/Bindings/LuaWindow.cpp +++ b/src/Bindings/LuaWindow.cpp @@ -15,14 +15,13 @@ //////////////////////////////////////////////////////////////////////////////// // cLuaWindow: -cLuaWindow::cLuaWindow(cWindow::WindowType a_WindowType, int a_SlotsX, int a_SlotsY, const AString & a_Title) : - super(a_WindowType, a_Title), +cLuaWindow::cLuaWindow(cLuaState & a_LuaState, cWindow::WindowType a_WindowType, int a_SlotsX, int a_SlotsY, const AString & a_Title) : + Super(a_WindowType, a_Title), m_Contents(a_SlotsX, a_SlotsY), - m_Plugin(nullptr), - m_LuaRef(LUA_REFNIL), - m_OnClosingFnRef(LUA_REFNIL), - m_OnSlotChangedFnRef(LUA_REFNIL) + m_LuaState(a_LuaState.QueryCanonLuaState()) { + ASSERT(m_LuaState != nullptr); // We must have a valid Lua state; this assert fails only if there was no Canon Lua state + m_Contents.AddListener(*this); m_SlotAreas.push_back(new cSlotAreaItemGrid(m_Contents, *this)); @@ -67,62 +66,42 @@ cLuaWindow::~cLuaWindow() -void cLuaWindow::SetLuaRef(cPluginLua * a_Plugin, int a_LuaRef) +void cLuaWindow::SetOnClosing(cLuaState::cCallbackPtr a_OnClosing) { - // Either m_Plugin is not set or equal to the passed plugin; only one plugin can use one cLuaWindow object - ASSERT((m_Plugin == nullptr) || (m_Plugin == a_Plugin)); - ASSERT(m_LuaRef == LUA_REFNIL); - m_Plugin = a_Plugin; - m_LuaRef = a_LuaRef; -} - - - + // Only one Lua state can be a cLuaWindow object callback: + ASSERT(a_OnClosing->IsSameLuaState(*m_LuaState)); - -bool cLuaWindow::IsLuaReferenced(void) const -{ - return ((m_Plugin != nullptr) && (m_LuaRef != LUA_REFNIL)); + // Store the new reference, releasing the old one if appropriate: + m_OnClosing = a_OnClosing; } -void cLuaWindow::SetOnClosing(cPluginLua * a_Plugin, int a_FnRef) +void cLuaWindow::SetOnSlotChanged(cLuaState::cCallbackPtr a_OnSlotChanged) { - // Either m_Plugin is not set or equal to the passed plugin; only one plugin can use one cLuaWindow object - ASSERT((m_Plugin == nullptr) || (m_Plugin == a_Plugin)); + // Only one Lua state can be a cLuaWindow object callback: + ASSERT(a_OnSlotChanged->IsSameLuaState(*m_LuaState)); - // If there already was a function, unreference it first - if (m_OnClosingFnRef != LUA_REFNIL) - { - m_Plugin->Unreference(m_OnClosingFnRef); - } - - // Store the new reference - m_Plugin = a_Plugin; - m_OnClosingFnRef = a_FnRef; + // Store the new reference, releasing the old one if appropriate: + m_OnSlotChanged = a_OnSlotChanged; } -void cLuaWindow::SetOnSlotChanged(cPluginLua * a_Plugin, int a_FnRef) +void cLuaWindow::OpenedByPlayer(cPlayer & a_Player) { - // Either m_Plugin is not set or equal to the passed plugin; only one plugin can use one cLuaWindow object - ASSERT((m_Plugin == nullptr) || (m_Plugin == a_Plugin)); - - // If there already was a function, unreference it first - if (m_OnSlotChangedFnRef != LUA_REFNIL) + // If the first player is opening the window, create a Lua Reference to the window object so that Lua will not GC it until the last player closes the window: + if (m_PlayerCount == 0) { - m_Plugin->Unreference(m_OnSlotChangedFnRef); + m_LuaRef.CreateFromObject(*m_LuaState, this); } - // Store the new reference - m_Plugin = a_Plugin; - m_OnSlotChangedFnRef = a_FnRef; + ++m_PlayerCount; + Super::OpenedByPlayer(a_Player); } @@ -132,17 +111,27 @@ void cLuaWindow::SetOnSlotChanged(cPluginLua * a_Plugin, int a_FnRef) bool cLuaWindow::ClosedByPlayer(cPlayer & a_Player, bool a_CanRefuse) { // First notify the plugin through the registered callback: - if (m_OnClosingFnRef != LUA_REFNIL) + if (m_OnClosing != nullptr) { - ASSERT(m_Plugin != nullptr); - if (m_Plugin->CallbackWindowClosing(m_OnClosingFnRef, *this, a_Player, a_CanRefuse)) + bool res; + if ( + m_OnClosing->Call(this, &a_Player, a_CanRefuse, cLuaState::Return, res) && // The callback succeeded + res // The callback says not to close the window + ) { // The callback disagrees (the higher levels check the CanRefuse flag compliance) return false; } } - return super::ClosedByPlayer(a_Player, a_CanRefuse); + // If the last player has closed the window, release the Lua reference, so that Lua may GC the object: + --m_PlayerCount; + if (m_PlayerCount == 0) + { + m_LuaRef.UnRef(); + } + + return Super::ClosedByPlayer(a_Player, a_CanRefuse); } @@ -151,13 +140,7 @@ bool cLuaWindow::ClosedByPlayer(cPlayer & a_Player, bool a_CanRefuse) void cLuaWindow::Destroy(void) { - super::Destroy(); - - if ((m_LuaRef != LUA_REFNIL) && (m_Plugin != nullptr)) - { - // The object is referenced by Lua, un-reference it - m_Plugin->Unreference(m_LuaRef); - } + Super::Destroy(); // Lua will take care of this object, it will garbage-collect it, so we must not delete it! m_IsDestroyed = false; @@ -178,7 +161,7 @@ void cLuaWindow::DistributeStack(cItem & a_ItemStack, int a_Slot, cPlayer & a_Pl } } - super::DistributeStackToAreas(a_ItemStack, a_Player, Areas, a_ShouldApply, false); + Super::DistributeStackToAreas(a_ItemStack, a_Player, Areas, a_ShouldApply, false); } @@ -194,9 +177,9 @@ void cLuaWindow::OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) } // If an OnSlotChanged callback has been registered, call it: - if (m_OnSlotChangedFnRef != LUA_REFNIL) + if (m_OnSlotChanged != nullptr) { - m_Plugin->CallbackWindowSlotChanged(m_OnSlotChangedFnRef, *this, a_SlotNum); + m_OnSlotChanged->Call(this, a_SlotNum); } } diff --git a/src/Bindings/LuaWindow.h b/src/Bindings/LuaWindow.h index f292a5154..2a16d91ed 100644 --- a/src/Bindings/LuaWindow.h +++ b/src/Bindings/LuaWindow.h @@ -9,6 +9,8 @@ #pragma once +#include <atomic> +#include "LuaState.h" #include "../UI/Window.h" #include "../ItemGrid.h" @@ -16,35 +18,30 @@ -// fwd: PluginLua.h -class cPluginLua; - - - - - /** A window that has been created by a Lua plugin and is handled entirely by that plugin This object needs extra care with its lifetime management: - It is created by Lua, so Lua expects to garbage-collect it later -- normal cWindow objects are deleted in their ClosedByPlayer() function if the last player closes them -To overcome this, this object overloads the Destroy functions, which doesn't let the ClosedByPlayer() -delete the window, but rather leaves it dangling, with only Lua having the reference to it. -Additionally, to forbid Lua from deleting this object while it is used by players, the manual bindings for -cPlayer:OpenWindow check if the window is of this class, and if so, make a global Lua reference for this object. -This reference needs to be unreferenced in the Destroy() function. */ +- Normal cWindow objects are deleted in their ClosedByPlayer() function if the last player closes them + To overcome this, this object overloads the Destroy functions, which doesn't let the ClosedByPlayer() + delete the window, but rather leaves it dangling, with only Lua having the reference to it. +- Lua could GC the window while a player is still using it + The object creates a Lua reference to itself when opened by a player and + removes the reference when the last player closes the window. +*/ // tolua_begin class cLuaWindow : public cWindow // tolua_end , public cItemGrid::cListener - // tolua_begin -{ - typedef cWindow super; +{ // tolua_export + typedef cWindow Super; public: - /** Create a window of the specified type, with a slot grid of a_SlotsX * a_SlotsY size */ - cLuaWindow(cWindow::WindowType a_WindowType, int a_SlotsX, int a_SlotsY, const AString & a_Title); + /** Create a window of the specified type, with a slot grid of a_SlotsX * a_SlotsY size. + Exported in ManualBindings.cpp */ + cLuaWindow(cLuaState & a_LuaState, cWindow::WindowType a_WindowType, int a_SlotsX, int a_SlotsY, const AString & a_Title); + // tolua_begin virtual ~cLuaWindow(); /** Returns the internal representation of the contents that are manipulated by Lua */ @@ -52,36 +49,37 @@ public: // tolua_end - /** Sets the plugin reference and the internal Lua object reference index - used for preventing Lua's GC to collect this class while the window is open. */ - void SetLuaRef(cPluginLua * a_Plugin, int a_LuaRef); - - /** Returns true if SetLuaRef() has been called */ - bool IsLuaReferenced(void) const; - - /** Sets the callback function (Lua reference) to call when the window is about to close */ - void SetOnClosing(cPluginLua * a_Plugin, int a_FnRef); + /** Sets the Lua callback function to call when the window is about to close */ + void SetOnClosing(cLuaState::cCallbackPtr a_OnClosing); - /** Sets the callback function (Lua reference) to call when a slot is changed */ - void SetOnSlotChanged(cPluginLua * a_Plugin, int a_FnRef); + /** Sets the Lua callback function to call when a slot is changed */ + void SetOnSlotChanged(cLuaState::cCallbackPtr a_OnSlotChanged); protected: + /** Contents of the non-inventory part */ cItemGrid m_Contents; - /** The plugin that has opened the window and owns the m_LuaRef */ - cPluginLua * m_Plugin; + /** The Lua state that has opened the window and owns the m_LuaRef */ + cLuaState * m_LuaState; + + /** The Lua callback to call when the window is closing for any player */ + cLuaState::cCallbackPtr m_OnClosing; + + /** The Lua callback to call when a slot has changed */ + cLuaState::cCallbackPtr m_OnSlotChanged; - /** The Lua object reference, used for keeping the object alive as long as any player has the window open */ - int m_LuaRef; + /** Number of players that are currently using the window. + Used to manager the m_LuaRef lifetime. */ + std::atomic<int> m_PlayerCount; - /** The Lua reference for the callback to call when the window is closing for any player */ - int m_OnClosingFnRef; + /** Reference to self, to keep Lua from GCing the object while a player is still using it. + Created when the first player opens the window, destroyed when the last player closes the window. */ + cLuaState::cRef m_LuaRef; - /** The Lua reference for the callback to call when a slot has changed */ - int m_OnSlotChangedFnRef; // cWindow overrides: + virtual void OpenedByPlayer(cPlayer & a_Player) override; virtual bool ClosedByPlayer(cPlayer & a_Player, bool a_CanRefuse) override; virtual void Destroy(void) override; virtual void DistributeStack(cItem & a_ItemStack, int a_Slot, cPlayer & a_Player, cSlotArea * a_ClickedArea, bool a_ShouldApply) override; diff --git a/src/Bindings/ManualBindings.cpp b/src/Bindings/ManualBindings.cpp index 523244ed2..f041ef524 100644 --- a/src/Bindings/ManualBindings.cpp +++ b/src/Bindings/ManualBindings.cpp @@ -992,7 +992,13 @@ static int tolua_cPluginManager_AddHook_FnRef(cPluginManager * a_PluginManager, } // Retrieve and check the hook type - int HookType = static_cast<int>(tolua_tonumber(S, a_ParamIdx, -1)); + int HookType; + if (!S.GetStackValue(a_ParamIdx, HookType)) + { + LOGWARNING("cPluginManager.AddHook(): Cannot read the hook type."); + S.LogStackTrace(); + return 0; + } if (!a_PluginManager->IsValidHookType(HookType)) { LOGWARNING("cPluginManager.AddHook(): Invalid HOOK_TYPE parameter: %d", HookType); @@ -1001,7 +1007,14 @@ static int tolua_cPluginManager_AddHook_FnRef(cPluginManager * a_PluginManager, } // Add the hook to the plugin - if (!Plugin->AddHookRef(HookType, a_ParamIdx + 1)) + 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)) { LOGWARNING("cPluginManager.AddHook(): Cannot add hook %d, unknown error.", HookType); S.LogStackTrace(); @@ -1058,10 +1071,11 @@ static int tolua_cPluginManager_AddHook_DefFn(cPluginManager * a_PluginManager, } // Retrieve the function to call and add it to the plugin: - lua_pushstring(S, FnName); - bool res = Plugin->AddHookRef(HookType, 1); - lua_pop(S, 1); // Pop the function off the stack - if (!res) + 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()) { LOGWARNING("cPluginManager.AddHook(): Function %s not found. Hook not added.", FnName); S.LogStackTrace(); @@ -1585,55 +1599,6 @@ static int tolua_cPlayer_GetRestrictions(lua_State * tolua_S) -static int tolua_cPlayer_OpenWindow(lua_State * tolua_S) -{ - // Function signature: cPlayer:OpenWindow(Window) - - // Retrieve the plugin instance from the Lua state - cPluginLua * Plugin = cManualBindings::GetLuaPlugin(tolua_S); - if (Plugin == nullptr) - { - return 0; - } - - // Get the parameters: - cPlayer * self = reinterpret_cast<cPlayer *>(tolua_tousertype(tolua_S, 1, nullptr)); - cWindow * wnd = reinterpret_cast<cWindow *>(tolua_tousertype(tolua_S, 2, nullptr)); - if ((self == nullptr) || (wnd == nullptr)) - { - LOGWARNING("%s: invalid self (%p) or wnd (%p)", __FUNCTION__, static_cast<void *>(self), static_cast<void *>(wnd)); - return 0; - } - - // If cLuaWindow, add a reference, so that Lua won't delete the cLuaWindow object mid-processing - tolua_Error err; - if (tolua_isusertype(tolua_S, 2, "cLuaWindow", 0, &err)) - { - cLuaWindow * LuaWnd = reinterpret_cast<cLuaWindow *>(wnd); - // Only if not already referenced - if (!LuaWnd->IsLuaReferenced()) - { - int LuaRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); - if (LuaRef == LUA_REFNIL) - { - LOGWARNING("%s: Cannot create a window reference. Cannot open window \"%s\".", - __FUNCTION__, wnd->GetWindowTitle().c_str() - ); - return 0; - } - LuaWnd->SetLuaRef(Plugin, LuaRef); - } - } - - // Open the window - self->OpenWindow(wnd); - return 0; -} - - - - - static int tolua_cPlayer_PermissionMatches(lua_State * tolua_S) { // Function signature: cPlayer:PermissionMatches(PermissionStr, TemplateStr) -> bool @@ -1664,36 +1629,25 @@ static int tolua_cPlayer_PermissionMatches(lua_State * tolua_S) template < class OBJTYPE, - void (OBJTYPE::*SetCallback)(cPluginLua * a_Plugin, int a_FnRef) + void (OBJTYPE::*SetCallback)(cLuaState::cCallbackPtr a_CallbackFn) > static int tolua_SetObjectCallback(lua_State * tolua_S) { // Function signature: OBJTYPE:SetWhateverCallback(CallbackFunction) - // Retrieve the plugin instance from the Lua state - cPluginLua * Plugin = cManualBindings::GetLuaPlugin(tolua_S); - if (Plugin == nullptr) - { - // Warning message has already been printed by GetLuaPlugin(), bail out silently - return 0; - } - // Get the parameters - self and the function reference: - OBJTYPE * self = reinterpret_cast<OBJTYPE *>(tolua_tousertype(tolua_S, 1, nullptr)); - if (self == nullptr) - { - LOGWARNING("%s: invalid self (%p)", __FUNCTION__, static_cast<void *>(self)); - return 0; - } - int FnRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); // Store function reference for later retrieval - if (FnRef == LUA_REFNIL) + cLuaState L(tolua_S); + OBJTYPE * self; + cLuaState::cCallbackPtr callback; + if (!L.GetStackValues(1, self, callback)) { - LOGERROR("%s: Cannot create a function reference. Callback not set.", __FUNCTION__); + LOGWARNING("%s: Cannot get parameters", __FUNCTION__); + L.LogStackTrace(); return 0; } // Set the callback - (self->*SetCallback)(Plugin, FnRef); + (self->*SetCallback)(callback); return 0; } @@ -1701,47 +1655,71 @@ static int tolua_SetObjectCallback(lua_State * tolua_S) -static int tolua_cPluginLua_AddWebTab(lua_State * tolua_S) +// Callback class used for the WebTab: +class cWebTabCallback: + public cWebAdmin::cWebTabCallback { - cLuaState LuaState(tolua_S); - cPluginLua * self = nullptr; +public: + /** The Lua callback to call to generate the page contents. */ + cLuaState::cCallback m_Callback; - if (!LuaState.GetStackValue(1, self)) + virtual bool Call( + const HTTPRequest & a_Request, + const AString & a_UrlPath, + AString & a_Content, + AString & a_ContentType + ) override { - LOGWARNING("cPluginLua:AddWebTab: invalid self as first argument"); - return 0; + AString content, contentType; + return m_Callback.Call(&a_Request, a_UrlPath, cLuaState::Return, a_Content, a_ContentType); } +}; + + - tolua_Error tolua_err; - tolua_err.array = 0; - tolua_err.index = 3; - tolua_err.type = "function"; - std::string Title; - int Reference = LUA_REFNIL; - if (LuaState.CheckParamString(2) && LuaState.CheckParamFunction(3)) +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 + + // Check params: + cLuaState LuaState(tolua_S); + cPluginLua * self = cManualBindings::GetLuaPlugin(tolua_S); + if (self == nullptr) { - Reference = luaL_ref(tolua_S, LUA_REGISTRYINDEX); - LuaState.GetStackValue(2, Title); + return 0; } - else + if ( + !LuaState.CheckParamString(2) || + !LuaState.CheckParamFunction(3) || + // Optional string as param 4 + !LuaState.CheckParamEnd(5) + ) { - return cManualBindings::tolua_do_error(tolua_S, "#ferror calling function '#funcname#'", &tolua_err); + return 0; } - if (Reference != LUA_REFNIL) + // Read the params: + AString title, urlPath; + auto callback = std::make_shared<cWebTabCallback>(); + if (!LuaState.GetStackValues(2, title, callback->m_Callback)) { - if (!self->AddWebTab(Title.c_str(), tolua_S, Reference)) - { - luaL_unref(tolua_S, LUA_REGISTRYINDEX, Reference); - } + LOGWARNING("cPlugin:AddWebTab(): Cannot read required parameters"); + return 0; } - else + if (!LuaState.GetStackValue(4, urlPath)) { - LOGWARNING("cPluginLua:AddWebTab: invalid function reference in 2nd argument (Title: \"%s\")", Title.c_str()); + urlPath = cWebAdmin::GetURLEncodedString(title); } + cRoot::Get()->GetWebAdmin()->AddWebTab(title, urlPath, self->GetName(), callback); + return 0; } @@ -2106,22 +2084,68 @@ static int tolua_cUrlParser_ParseAuthorityPart(lua_State * a_LuaState) -static int tolua_cWebAdmin_GetPlugins(lua_State * tolua_S) +static int tolua_cWebAdmin_AddWebTab(lua_State * tolua_S) { - cWebAdmin * self = reinterpret_cast<cWebAdmin *>(tolua_tousertype(tolua_S, 1, nullptr)); + // Function signatures: + // cWebAdmin:AddWebTab(Title, UrlPath, CallbackFn) - const cWebAdmin::PluginList & AllPlugins = self->GetPlugins(); + // 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; + } - lua_createtable(tolua_S, static_cast<int>(AllPlugins.size()), 0); + // Read the params: + AString title, urlPath; + auto callback = std::make_shared<cWebTabCallback>(); + if (!LuaState.GetStackValues(2, title, urlPath, callback->m_Callback)) + { + LOGWARNING("cWebAdmin:AddWebTab(): Cannot read required parameters"); + return 0; + } + + cRoot::Get()->GetWebAdmin()->AddWebTab(title, urlPath, self->GetName(), callback); + + return 0; +} + + + + + +static int tolua_cWebAdmin_GetAllWebTabs(lua_State * tolua_S) +{ + // 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); int newTable = lua_gettop(tolua_S); int index = 1; - cWebAdmin::PluginList::const_iterator iter = AllPlugins.begin(); - while (iter != AllPlugins.end()) - { - const cWebPlugin * Plugin = *iter; - tolua_pushusertype(tolua_S, reinterpret_cast<void *>(const_cast<cWebPlugin*>(Plugin)), "const cWebPlugin"); + cLuaState L(tolua_S); + for (const auto & wt: webTabs) + { + lua_createtable(tolua_S, 0, 3); + L.Push(wt->m_PluginName); + lua_setfield(tolua_S, -2, "PluginName"); + L.Push(wt->m_UrlPath); + lua_setfield(tolua_S, -2, "UrlPath"); + L.Push(wt->m_Title); + lua_setfield(tolua_S, -2, "Title"); lua_rawseti(tolua_S, newTable, index); - ++iter; ++index; } return 1; @@ -2131,14 +2155,14 @@ static int tolua_cWebAdmin_GetPlugins(lua_State * tolua_S) -/** Binding for cWebAdmin::GetHTMLEscapedString. +/** Binding for cWebAdmin::GetBaseURL. Manual code required because ToLua generates an extra return value */ -static int tolua_AllToLua_cWebAdmin_GetHTMLEscapedString(lua_State * tolua_S) +static int tolua_cWebAdmin_GetBaseURL(lua_State * tolua_S) { // Check the param types: cLuaState S(tolua_S); if ( - !S.CheckParamUserTable(1, "cWebAdmin") || + // Don't care whether the first param is a cWebAdmin instance or class !S.CheckParamString(2) || !S.CheckParamEnd(3) ) @@ -2151,7 +2175,7 @@ static int tolua_AllToLua_cWebAdmin_GetHTMLEscapedString(lua_State * tolua_S) S.GetStackValue(2, Input); // Convert and return: - S.Push(cWebAdmin::GetHTMLEscapedString(Input)); + S.Push(cWebAdmin::GetBaseURL(Input)); return 1; } @@ -2159,14 +2183,14 @@ static int tolua_AllToLua_cWebAdmin_GetHTMLEscapedString(lua_State * tolua_S) -/** Binding for cWebAdmin::GetURLEncodedString. +/** Binding for cWebAdmin::GetContentTypeFromFileExt. Manual code required because ToLua generates an extra return value */ -static int tolua_AllToLua_cWebAdmin_GetURLEncodedString(lua_State * tolua_S) +static int tolua_cWebAdmin_GetContentTypeFromFileExt(lua_State * tolua_S) { // Check the param types: cLuaState S(tolua_S); if ( - !S.CheckParamUserTable(1, "cWebAdmin") || + // Don't care whether the first param is a cWebAdmin instance or class !S.CheckParamString(2) || !S.CheckParamEnd(3) ) @@ -2179,7 +2203,7 @@ static int tolua_AllToLua_cWebAdmin_GetURLEncodedString(lua_State * tolua_S) S.GetStackValue(2, Input); // Convert and return: - S.Push(cWebAdmin::GetURLEncodedString(Input)); + S.Push(cWebAdmin::GetContentTypeFromFileExt(Input)); return 1; } @@ -2187,20 +2211,112 @@ static int tolua_AllToLua_cWebAdmin_GetURLEncodedString(lua_State * tolua_S) -static int tolua_cWebPlugin_GetTabNames(lua_State * tolua_S) +/** Binding for cWebAdmin::GetHTMLEscapedString. +Manual code required because ToLua generates an extra return value */ +static int tolua_cWebAdmin_GetHTMLEscapedString(lua_State * tolua_S) { - // Returns a map of (SafeTitle -> Title) for the plugin's web tabs. - auto self = reinterpret_cast<cWebPlugin *>(tolua_tousertype(tolua_S, 1, nullptr)); - auto TabNames = self->GetTabNames(); - lua_newtable(tolua_S); - int index = 1; - for (auto itr = TabNames.cbegin(), end = TabNames.cend(); itr != end; ++itr) + // Check the param types: + cLuaState S(tolua_S); + if ( + // Don't care whether the first param is a cWebAdmin instance or class + !S.CheckParamString(2) || + !S.CheckParamEnd(3) + ) { - tolua_pushstring(tolua_S, itr->second.c_str()); // Because the SafeTitle is supposed to be unique, use it as key - tolua_pushstring(tolua_S, itr->first.c_str()); - lua_rawset(tolua_S, -3); - ++index; + return 0; + } + + // Get the parameters: + AString Input; + S.GetStackValue(2, Input); + + // Convert and return: + S.Push(cWebAdmin::GetHTMLEscapedString(Input)); + return 1; +} + + + + + +/** Binding for cWebAdmin::GetPage. */ +static int tolua_cWebAdmin_GetPage(lua_State * tolua_S) +{ + /* + Function signature: + cWebAdmin:GetPage(a_HTTPRequest) -> + { + Content = "", // Content generated by the plugin + ContentType = "", // Content type generated by the plugin (default: "text/html") + UrlPath = "", // URL path of the tab + TabTitle = "", // Tab's title, as register via cWebAdmin:AddWebTab() + PluginName = "", // Plugin's API name + PluginFolder = "", // Plugin's folder name (display name) + } + */ + + // Check the param types: + cLuaState S(tolua_S); + if ( + // Don't care about first param, whether it's cWebAdmin instance or class + !S.CheckParamUserType(2, "HTTPRequest") || + !S.CheckParamEnd(3) + ) + { + return 0; } + + // Get the parameters: + HTTPRequest * request = nullptr; + if (!S.GetStackValue(2, request)) + { + LOGWARNING("cWebAdmin:GetPage(): Cannot read the HTTPRequest parameter."); + return 0; + } + + // Generate the page and push the results as a dictionary-table: + auto page = cRoot::Get()->GetWebAdmin()->GetPage(*request); + lua_createtable(S, 0, 6); + S.Push(page.Content); + lua_setfield(S, -2, "Content"); + S.Push(page.ContentType); + lua_setfield(S, -2, "ContentType"); + S.Push(page.TabUrlPath); + lua_setfield(S, -2, "UrlPath"); + S.Push(page.TabTitle); + lua_setfield(S, -2, "TabTitle"); + S.Push(page.PluginName); + lua_setfield(S, -2, "PluginName"); + S.Push(cPluginManager::Get()->GetPluginFolderName(page.PluginName)); + lua_setfield(S, -2, "PluginFolder"); + return 1; +} + + + + + +/** Binding for cWebAdmin::GetURLEncodedString. +Manual code required because ToLua generates an extra return value */ +static int tolua_cWebAdmin_GetURLEncodedString(lua_State * tolua_S) +{ + // Check the param types: + cLuaState S(tolua_S); + if ( + // Don't care whether the first param is a cWebAdmin instance or class + !S.CheckParamString(2) || + !S.CheckParamEnd(3) + ) + { + return 0; + } + + // Get the parameters: + AString Input; + S.GetStackValue(2, Input); + + // Convert and return: + S.Push(cWebAdmin::GetURLEncodedString(Input)); return 1; } @@ -2623,6 +2739,79 @@ static int tolua_cLineBlockTracer_Trace(lua_State * tolua_S) +static int tolua_cLuaWindow_new(lua_State * tolua_S) +{ + // Function signature: + // cLuaWindow:new(type, slotsX, slotsY, title) + + // Check params: + cLuaState L(tolua_S); + if ( + !L.CheckParamUserTable(1, "cLuaWindow") || + !L.CheckParamNumber(2, 4) || + !L.CheckParamString(5) || + !L.CheckParamEnd(6) + ) + { + return 0; + } + + // Read params: + int windowType, slotsX, slotsY; + AString title; + if (!L.GetStackValues(2, windowType, slotsX, slotsY, title)) + { + LOGWARNING("%s: Cannot read Lua parameters", __FUNCTION__); + L.LogStackValues(); + L.LogStackTrace(); + } + + // Create the window and return it: + L.Push(new cLuaWindow(L, static_cast<cLuaWindow::WindowType>(windowType), slotsX, slotsY, title)); + return 1; +} + + + + + +static int tolua_cLuaWindow_new_local(lua_State * tolua_S) +{ + // Function signature: + // cLuaWindow:new(type, slotsX, slotsY, title) + + // Check params: + cLuaState L(tolua_S); + if ( + !L.CheckParamUserTable(1, "cLuaWindow") || + !L.CheckParamNumber(2, 4) || + !L.CheckParamString(5) || + !L.CheckParamEnd(6) + ) + { + return 0; + } + + // Read params: + int windowType, slotsX, slotsY; + AString title; + if (!L.GetStackValues(2, windowType, slotsX, slotsY, title)) + { + LOGWARNING("%s: Cannot read Lua parameters", __FUNCTION__); + L.LogStackValues(); + L.LogStackTrace(); + } + + // Create the window, register it for GC and return it: + L.Push(new cLuaWindow(L, static_cast<cLuaWindow::WindowType>(windowType), slotsX, slotsY, title)); + tolua_register_gc(tolua_S, lua_gettop(tolua_S)); + return 1; +} + + + + + static int tolua_cRoot_GetBuildCommitID(lua_State * tolua_S) { cLuaState L(tolua_S); @@ -3466,6 +3655,9 @@ void cManualBindings::Bind(lua_State * tolua_S) tolua_endmodule(tolua_S); tolua_beginmodule(tolua_S, "cLuaWindow"); + tolua_function(tolua_S, "new", tolua_cLuaWindow_new); + tolua_function(tolua_S, "new_local", tolua_cLuaWindow_new_local); + tolua_function(tolua_S, ".call", tolua_cLuaWindow_new_local); tolua_function(tolua_S, "SetOnClosing", tolua_SetObjectCallback<cLuaWindow, &cLuaWindow::SetOnClosing>); tolua_function(tolua_S, "SetOnSlotChanged", tolua_SetObjectCallback<cLuaWindow, &cLuaWindow::SetOnSlotChanged>); tolua_endmodule(tolua_S); @@ -3486,7 +3678,6 @@ void cManualBindings::Bind(lua_State * tolua_S) tolua_beginmodule(tolua_S, "cPlayer"); tolua_function(tolua_S, "GetPermissions", tolua_cPlayer_GetPermissions); tolua_function(tolua_S, "GetRestrictions", tolua_cPlayer_GetRestrictions); - tolua_function(tolua_S, "OpenWindow", tolua_cPlayer_OpenWindow); tolua_function(tolua_S, "PermissionMatches", tolua_cPlayer_PermissionMatches); tolua_endmodule(tolua_S); @@ -3550,13 +3741,13 @@ void cManualBindings::Bind(lua_State * tolua_S) tolua_endmodule(tolua_S); tolua_beginmodule(tolua_S, "cWebAdmin"); - tolua_function(tolua_S, "GetHTMLEscapedString", tolua_AllToLua_cWebAdmin_GetHTMLEscapedString); - tolua_function(tolua_S, "GetPlugins", tolua_cWebAdmin_GetPlugins); - tolua_function(tolua_S, "GetURLEncodedString", tolua_AllToLua_cWebAdmin_GetURLEncodedString); - tolua_endmodule(tolua_S); - - tolua_beginmodule(tolua_S, "cWebPlugin"); - tolua_function(tolua_S, "GetTabNames", tolua_cWebPlugin_GetTabNames); + tolua_function(tolua_S, "AddWebTab", tolua_cWebAdmin_AddWebTab); + tolua_function(tolua_S, "GetAllWebTabs", tolua_cWebAdmin_GetAllWebTabs); + tolua_function(tolua_S, "GetBaseURL", tolua_cWebAdmin_GetBaseURL); + tolua_function(tolua_S, "GetContentTypeFromFileExt", tolua_cWebAdmin_GetContentTypeFromFileExt); + tolua_function(tolua_S, "GetHTMLEscapedString", tolua_cWebAdmin_GetHTMLEscapedString); + tolua_function(tolua_S, "GetPage", tolua_cWebAdmin_GetPage); + tolua_function(tolua_S, "GetURLEncodedString", tolua_cWebAdmin_GetURLEncodedString); tolua_endmodule(tolua_S); tolua_beginmodule(tolua_S, "HTTPRequest"); diff --git a/src/Bindings/ManualBindings_World.cpp b/src/Bindings/ManualBindings_World.cpp index 00d2169d8..3faf038aa 100644 --- a/src/Bindings/ManualBindings_World.cpp +++ b/src/Bindings/ManualBindings_World.cpp @@ -466,67 +466,41 @@ static int tolua_cWorld_PrepareChunk(lua_State * tolua_S) -class cLuaWorldTask : - public cPluginLua::cResettable -{ -public: - cLuaWorldTask(cPluginLua & a_Plugin, int a_FnRef) : - cPluginLua::cResettable(a_Plugin), - m_FnRef(a_FnRef) - { - } - - void Run(cWorld & a_World) - { - cCSLock Lock(m_CSPlugin); - if (m_Plugin != nullptr) - { - m_Plugin->Call(m_FnRef, &a_World); - } - } - -protected: - int m_FnRef; -}; - - - - - static int tolua_cWorld_QueueTask(lua_State * tolua_S) { - // Binding for cWorld::QueueTask - // Params: function + // Function signature: + // World:QueueTask(Callback) - // Retrieve the cPlugin from the LuaState: - cPluginLua * Plugin = cManualBindings::GetLuaPlugin(tolua_S); - if (Plugin == nullptr) + // Retrieve the args: + cLuaState L(tolua_S); + if ( + !L.CheckParamUserType(1, "cWorld") || + !L.CheckParamNumber (2) || + !L.CheckParamFunction(3) + ) { - // An error message has been already printed in GetLuaPlugin() return 0; } - - // Retrieve the args: - cWorld * self = reinterpret_cast<cWorld *>(tolua_tousertype(tolua_S, 1, nullptr)); - if (self == nullptr) + cWorld * World; + auto Task = std::make_shared<cLuaState::cCallback>(); + if (!L.GetStackValues(1, World, Task)) { - return cManualBindings::lua_do_error(tolua_S, "Error in function call '#funcname#': Not called on an object instance"); + return cManualBindings::lua_do_error(tolua_S, "Error in function call '#funcname#': Cannot read parameters"); } - if (!lua_isfunction(tolua_S, 2)) + if (World == nullptr) { - return cManualBindings::lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a function for parameter #1"); + return cManualBindings::lua_do_error(tolua_S, "Error in function call '#funcname#': Not called on an object instance"); } - - // Create a reference to the function: - int FnRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); - if (FnRef == LUA_REFNIL) + if (!Task->IsValid()) { - return cManualBindings::lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get function reference of parameter #1"); + return cManualBindings::lua_do_error(tolua_S, "Error in function call '#funcname#': Could not store the callback parameter"); } - auto ResettableTask = std::make_shared<cLuaWorldTask>(*Plugin, FnRef); - Plugin->AddResettable(ResettableTask); - self->QueueTask(std::bind(&cLuaWorldTask::Run, ResettableTask, std::placeholders::_1)); + World->QueueTask([Task](cWorld & a_World) + { + Task->Call(&a_World); + } + ); return 0; } @@ -576,16 +550,8 @@ static int tolua_cWorld_SetSignLines(lua_State * tolua_S) static int tolua_cWorld_ScheduleTask(lua_State * tolua_S) { - // Binding for cWorld::ScheduleTask - // Params: function, Ticks - - // Retrieve the cPlugin from the LuaState: - cPluginLua * Plugin = cManualBindings::GetLuaPlugin(tolua_S); - if (Plugin == nullptr) - { - // An error message has been already printed in GetLuaPlugin() - return 0; - } + // Function signature: + // World:ScheduleTask(NumTicks, Callback) // Retrieve the args: cLuaState L(tolua_S); @@ -597,22 +563,27 @@ static int tolua_cWorld_ScheduleTask(lua_State * tolua_S) { return 0; } - cWorld * World = reinterpret_cast<cWorld *>(tolua_tousertype(tolua_S, 1, nullptr)); + cWorld * World; + int NumTicks; + auto Task = std::make_shared<cLuaState::cCallback>(); + if (!L.GetStackValues(1, World, NumTicks, Task)) + { + return cManualBindings::lua_do_error(tolua_S, "Error in function call '#funcname#': Cannot read parameters"); + } if (World == nullptr) { return cManualBindings::lua_do_error(tolua_S, "Error in function call '#funcname#': Not called on an object instance"); } - - // Create a reference to the function: - int FnRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); - if (FnRef == LUA_REFNIL) + if (!Task->IsValid()) { - return cManualBindings::lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get function reference of parameter #1"); + return cManualBindings::lua_do_error(tolua_S, "Error in function call '#funcname#': Could not store the callback parameter"); } - auto ResettableTask = std::make_shared<cLuaWorldTask>(*Plugin, FnRef); - Plugin->AddResettable(ResettableTask); - World->ScheduleTask(static_cast<int>(tolua_tonumber(tolua_S, 2, 0)), std::bind(&cLuaWorldTask::Run, ResettableTask, std::placeholders::_1)); + World->ScheduleTask(NumTicks, [Task](cWorld & a_World) + { + Task->Call(&a_World); + } + ); return 0; } diff --git a/src/Bindings/PluginLua.cpp b/src/Bindings/PluginLua.cpp index a266e6223..fd3e8bc69 100644 --- a/src/Bindings/PluginLua.cpp +++ b/src/Bindings/PluginLua.cpp @@ -15,6 +15,8 @@ #include "../CommandOutput.h" #include "PluginManager.h" #include "../Item.h" +#include "../Root.h" +#include "../WebAdmin.h" extern "C" { @@ -58,7 +60,6 @@ void cPluginLua::Close(void) // If already closed, bail out: if (!m_LuaState.IsValid()) { - ASSERT(m_Resettables.empty()); ASSERT(m_HookMap.empty()); return; } @@ -66,28 +67,9 @@ void cPluginLua::Close(void) // Remove the command bindings and web tabs: ClearCommands(); ClearConsoleCommands(); - ClearTabs(); - - // Notify and remove all m_Resettables (unlock the m_CriticalSection while resetting them): - cResettablePtrs resettables; - std::swap(m_Resettables, resettables); - { - cCSUnlock Unlock(Lock); - for (auto resettable: resettables) - { - resettable->Reset(); - } - m_Resettables.clear(); - } // cCSUnlock (m_CriticalSection) + ClearWebTabs(); // Release all the references in the hook map: - for (cHookMap::iterator itrH = m_HookMap.begin(), endH = m_HookMap.end(); itrH != endH; ++itrH) - { - for (cLuaRefs::iterator itrR = itrH->second.begin(), endR = itrH->second.end(); itrR != endR; ++itrR) - { - delete *itrR; - } // for itrR - itrH->second[] - } // for itrH - m_HookMap[] m_HookMap.clear(); // Close the Lua engine: @@ -205,7 +187,7 @@ bool cPluginLua::Load(void) void cPluginLua::Unload(void) { - ClearTabs(); + ClearWebTabs(); super::Unload(); Close(); } @@ -230,16 +212,7 @@ void cPluginLua::OnDisable(void) void cPluginLua::Tick(float a_Dt) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return; - } - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_TICK]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), a_Dt); - } + CallSimpleHooks(cPluginManager::HOOK_TICK, a_Dt); } @@ -248,22 +221,7 @@ void cPluginLua::Tick(float a_Dt) bool cPluginLua::OnBlockSpread(cWorld & a_World, int a_BlockX, int a_BlockY, int a_BlockZ, eSpreadSource a_Source) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_BLOCK_SPREAD]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_World, a_BlockX, a_BlockY, a_BlockZ, a_Source, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_BLOCK_SPREAD, &a_World, a_BlockX, a_BlockY, a_BlockZ, a_Source); } @@ -272,22 +230,7 @@ bool cPluginLua::OnBlockSpread(cWorld & a_World, int a_BlockX, int a_BlockY, int bool cPluginLua::OnBlockToPickups(cWorld & a_World, cEntity * a_Digger, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, cItems & a_Pickups) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_BLOCK_TO_PICKUPS]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_World, a_Digger, a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta, &a_Pickups, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_BLOCK_TO_PICKUPS, &a_World, a_Digger, a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta, &a_Pickups); } @@ -296,22 +239,7 @@ bool cPluginLua::OnBlockToPickups(cWorld & a_World, cEntity * a_Digger, int a_Bl bool cPluginLua::OnBrewingCompleted(cWorld & a_World, cBrewingstandEntity & a_Brewingstand) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_BREWING_COMPLETED]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_World, &a_Brewingstand, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_BREWING_COMPLETED, &a_World, &a_Brewingstand); } @@ -320,22 +248,7 @@ bool cPluginLua::OnBrewingCompleted(cWorld & a_World, cBrewingstandEntity & a_Br bool cPluginLua::OnBrewingCompleting(cWorld & a_World, cBrewingstandEntity & a_Brewingstand) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_BREWING_COMPLETING]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_World, &a_Brewingstand, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_BREWING_COMPLETING, &a_World, &a_Brewingstand); } @@ -350,10 +263,10 @@ bool cPluginLua::OnChat(cPlayer & a_Player, AString & a_Message) return false; } bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_CHAT]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + auto & hooks = m_HookMap[cPluginManager::HOOK_CHAT]; + for (auto hook: hooks) { - m_LuaState.Call(static_cast<int>(**itr), &a_Player, a_Message, cLuaState::Return, res, a_Message); + hook->Call(&a_Player, a_Message, cLuaState::Return, res, a_Message); if (res) { return true; @@ -368,22 +281,7 @@ bool cPluginLua::OnChat(cPlayer & a_Player, AString & a_Message) bool cPluginLua::OnChunkAvailable(cWorld & a_World, int a_ChunkX, int a_ChunkZ) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_CHUNK_AVAILABLE]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_World, a_ChunkX, a_ChunkZ, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_CHUNK_AVAILABLE, &a_World, a_ChunkX, a_ChunkZ); } @@ -392,22 +290,7 @@ bool cPluginLua::OnChunkAvailable(cWorld & a_World, int a_ChunkX, int a_ChunkZ) bool cPluginLua::OnChunkGenerated(cWorld & a_World, int a_ChunkX, int a_ChunkZ, cChunkDesc * a_ChunkDesc) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_CHUNK_GENERATED]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_World, a_ChunkX, a_ChunkZ, a_ChunkDesc, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_CHUNK_GENERATED, &a_World, a_ChunkX, a_ChunkZ, a_ChunkDesc); } @@ -416,22 +299,7 @@ bool cPluginLua::OnChunkGenerated(cWorld & a_World, int a_ChunkX, int a_ChunkZ, bool cPluginLua::OnChunkGenerating(cWorld & a_World, int a_ChunkX, int a_ChunkZ, cChunkDesc * a_ChunkDesc) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_CHUNK_GENERATING]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_World, a_ChunkX, a_ChunkZ, a_ChunkDesc, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_CHUNK_GENERATING, &a_World, a_ChunkX, a_ChunkZ, a_ChunkDesc); } @@ -440,22 +308,7 @@ bool cPluginLua::OnChunkGenerating(cWorld & a_World, int a_ChunkX, int a_ChunkZ, bool cPluginLua::OnChunkUnloaded(cWorld & a_World, int a_ChunkX, int a_ChunkZ) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_CHUNK_UNLOADED]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_World, a_ChunkX, a_ChunkZ, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_CHUNK_UNLOADED, &a_World, a_ChunkX, a_ChunkZ); } @@ -464,22 +317,7 @@ bool cPluginLua::OnChunkUnloaded(cWorld & a_World, int a_ChunkX, int a_ChunkZ) bool cPluginLua::OnChunkUnloading(cWorld & a_World, int a_ChunkX, int a_ChunkZ) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_CHUNK_UNLOADING]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_World, a_ChunkX, a_ChunkZ, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_CHUNK_UNLOADING, &a_World, a_ChunkX, a_ChunkZ); } @@ -488,22 +326,7 @@ bool cPluginLua::OnChunkUnloading(cWorld & a_World, int a_ChunkX, int a_ChunkZ) bool cPluginLua::OnCollectingPickup(cPlayer & a_Player, cPickup & a_Pickup) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_COLLECTING_PICKUP]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Player, &a_Pickup, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_COLLECTING_PICKUP, &a_Player, &a_Pickup); } @@ -512,22 +335,7 @@ bool cPluginLua::OnCollectingPickup(cPlayer & a_Player, cPickup & a_Pickup) bool cPluginLua::OnCraftingNoRecipe(cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe & a_Recipe) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_CRAFTING_NO_RECIPE]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Player, &a_Grid, &a_Recipe, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_CRAFTING_NO_RECIPE, &a_Player, &a_Grid, &a_Recipe); } @@ -536,22 +344,7 @@ bool cPluginLua::OnCraftingNoRecipe(cPlayer & a_Player, cCraftingGrid & a_Grid, bool cPluginLua::OnDisconnect(cClientHandle & a_Client, const AString & a_Reason) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_DISCONNECT]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Client, a_Reason, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_DISCONNECT, &a_Client, a_Reason); } @@ -560,22 +353,7 @@ bool cPluginLua::OnDisconnect(cClientHandle & a_Client, const AString & a_Reason bool cPluginLua::OnEntityAddEffect(cEntity & a_Entity, int a_EffectType, int a_EffectDurationTicks, int a_EffectIntensity, double a_DistanceModifier) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_ENTITY_ADD_EFFECT]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Entity, a_EffectType, a_EffectDurationTicks, a_EffectIntensity, a_DistanceModifier, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_ENTITY_ADD_EFFECT, &a_Entity, a_EffectType, a_EffectDurationTicks, a_EffectIntensity, a_DistanceModifier); } @@ -584,22 +362,7 @@ bool cPluginLua::OnEntityAddEffect(cEntity & a_Entity, int a_EffectType, int a_E bool cPluginLua::OnEntityChangingWorld(cEntity & a_Entity, cWorld & a_World) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_ENTITY_CHANGING_WORLD]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Entity, &a_World, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_ENTITY_CHANGING_WORLD, &a_Entity, &a_World); } @@ -608,22 +371,7 @@ bool cPluginLua::OnEntityChangingWorld(cEntity & a_Entity, cWorld & a_World) bool cPluginLua::OnEntityChangedWorld(cEntity & a_Entity, cWorld & a_World) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_ENTITY_CHANGED_WORLD]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Entity, &a_World, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_ENTITY_CHANGED_WORLD, &a_Entity, &a_World); } @@ -638,10 +386,10 @@ bool cPluginLua::OnExecuteCommand(cPlayer * a_Player, const AStringVector & a_Sp return false; } bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_EXECUTE_COMMAND]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + auto & hooks = m_HookMap[cPluginManager::HOOK_EXECUTE_COMMAND]; + for (auto & hook: hooks) { - m_LuaState.Call(static_cast<int>(**itr), a_Player, a_Split, a_EntireCommand, cLuaState::Return, res, a_Result); + hook->Call(a_Player, a_Split, a_EntireCommand, cLuaState::Return, res, a_Result); if (res) { return true; @@ -662,20 +410,20 @@ bool cPluginLua::OnExploded(cWorld & a_World, double a_ExplosionSize, bool a_Can return false; } bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_EXPLODED]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + auto & hooks = m_HookMap[cPluginManager::HOOK_EXPLODED]; + for (auto & hook: hooks) { switch (a_Source) { - case esBed: m_LuaState.Call(static_cast<int>(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<Vector3i *> (a_SourceData), cLuaState::Return, res); break; - case esEnderCrystal: m_LuaState.Call(static_cast<int>(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<cEntity *> (a_SourceData), cLuaState::Return, res); break; - case esGhastFireball: m_LuaState.Call(static_cast<int>(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<cGhastFireballEntity *>(a_SourceData), cLuaState::Return, res); break; - case esMonster: m_LuaState.Call(static_cast<int>(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<cMonster *> (a_SourceData), cLuaState::Return, res); break; - case esOther: m_LuaState.Call(static_cast<int>(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, cLuaState::Return, res); break; - case esPlugin: m_LuaState.Call(static_cast<int>(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, cLuaState::Return, res); break; - case esPrimedTNT: m_LuaState.Call(static_cast<int>(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<cTNTEntity *> (a_SourceData), cLuaState::Return, res); break; - case esWitherBirth: m_LuaState.Call(static_cast<int>(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<cMonster *> (a_SourceData), cLuaState::Return, res); break; - case esWitherSkull: m_LuaState.Call(static_cast<int>(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<cWitherSkullEntity *> (a_SourceData), cLuaState::Return, res); break; + case esBed: hook->Call(&a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<Vector3i *> (a_SourceData), cLuaState::Return, res); break; + case esEnderCrystal: hook->Call(&a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<cEntity *> (a_SourceData), cLuaState::Return, res); break; + case esGhastFireball: hook->Call(&a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<cGhastFireballEntity *>(a_SourceData), cLuaState::Return, res); break; + case esMonster: hook->Call(&a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<cMonster *> (a_SourceData), cLuaState::Return, res); break; + case esOther: hook->Call(&a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, cLuaState::Return, res); break; + case esPlugin: hook->Call(&a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, cLuaState::Return, res); break; + case esPrimedTNT: hook->Call(&a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<cTNTEntity *> (a_SourceData), cLuaState::Return, res); break; + case esWitherBirth: hook->Call(&a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<cMonster *> (a_SourceData), cLuaState::Return, res); break; + case esWitherSkull: hook->Call(&a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<cWitherSkullEntity *> (a_SourceData), cLuaState::Return, res); break; case esMax: { ASSERT(!"Invalid explosion source"); @@ -702,20 +450,20 @@ bool cPluginLua::OnExploding(cWorld & a_World, double & a_ExplosionSize, bool & return false; } bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_EXPLODING]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + auto & hooks = m_HookMap[cPluginManager::HOOK_EXPLODING]; + for (auto & hook: hooks) { switch (a_Source) { - case esBed: m_LuaState.Call(static_cast<int>(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<Vector3i *> (a_SourceData), cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; - case esEnderCrystal: m_LuaState.Call(static_cast<int>(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<cEntity *> (a_SourceData), cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; - case esGhastFireball: m_LuaState.Call(static_cast<int>(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<cGhastFireballEntity *>(a_SourceData), cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; - case esMonster: m_LuaState.Call(static_cast<int>(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<cMonster *> (a_SourceData), cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; - case esOther: m_LuaState.Call(static_cast<int>(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; - case esPlugin: m_LuaState.Call(static_cast<int>(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; - case esPrimedTNT: m_LuaState.Call(static_cast<int>(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<cTNTEntity *> (a_SourceData), cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; - case esWitherBirth: m_LuaState.Call(static_cast<int>(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<cMonster *> (a_SourceData), cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; - case esWitherSkull: m_LuaState.Call(static_cast<int>(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<cWitherSkullEntity *> (a_SourceData), cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; + case esBed: hook->Call(&a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<Vector3i *> (a_SourceData), cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; + case esEnderCrystal: hook->Call(&a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<cEntity *> (a_SourceData), cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; + case esGhastFireball: hook->Call(&a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<cGhastFireballEntity *>(a_SourceData), cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; + case esMonster: hook->Call(&a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<cMonster *> (a_SourceData), cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; + case esOther: hook->Call(&a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; + case esPlugin: hook->Call(&a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; + case esPrimedTNT: hook->Call(&a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<cTNTEntity *> (a_SourceData), cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; + case esWitherBirth: hook->Call(&a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<cMonster *> (a_SourceData), cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; + case esWitherSkull: hook->Call(&a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<cWitherSkullEntity *> (a_SourceData), cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; case esMax: { ASSERT(!"Invalid explosion source"); @@ -736,22 +484,7 @@ bool cPluginLua::OnExploding(cWorld & a_World, double & a_ExplosionSize, bool & bool cPluginLua::OnHandshake(cClientHandle & a_Client, const AString & a_Username) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_HANDSHAKE]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Client, a_Username, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_HANDSHAKE, &a_Client, a_Username); } @@ -760,22 +493,7 @@ bool cPluginLua::OnHandshake(cClientHandle & a_Client, const AString & a_Usernam bool cPluginLua::OnHopperPullingItem(cWorld & a_World, cHopperEntity & a_Hopper, int a_DstSlotNum, cBlockEntityWithItems & a_SrcEntity, int a_SrcSlotNum) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_HOPPER_PULLING_ITEM]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_World, &a_Hopper, a_DstSlotNum, &a_SrcEntity, a_SrcSlotNum, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_HOPPER_PULLING_ITEM, &a_World, &a_Hopper, a_DstSlotNum, &a_SrcEntity, a_SrcSlotNum); } @@ -784,22 +502,7 @@ bool cPluginLua::OnHopperPullingItem(cWorld & a_World, cHopperEntity & a_Hopper, bool cPluginLua::OnHopperPushingItem(cWorld & a_World, cHopperEntity & a_Hopper, int a_SrcSlotNum, cBlockEntityWithItems & a_DstEntity, int a_DstSlotNum) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_HOPPER_PUSHING_ITEM]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_World, &a_Hopper, a_SrcSlotNum, &a_DstEntity, a_DstSlotNum, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_HOPPER_PUSHING_ITEM, &a_World, &a_Hopper, a_SrcSlotNum, &a_DstEntity, a_DstSlotNum); } @@ -814,10 +517,10 @@ bool cPluginLua::OnKilled(cEntity & a_Victim, TakeDamageInfo & a_TDI, AString & return false; } bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_KILLED]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + auto & hooks = m_HookMap[cPluginManager::HOOK_KILLED]; + for (auto & hook: hooks) { - m_LuaState.Call(static_cast<int>(**itr), &a_Victim, &a_TDI, a_DeathMessage, cLuaState::Return, res, a_DeathMessage); + hook->Call(&a_Victim, &a_TDI, a_DeathMessage, cLuaState::Return, res, a_DeathMessage); if (res) { return true; @@ -832,22 +535,7 @@ bool cPluginLua::OnKilled(cEntity & a_Victim, TakeDamageInfo & a_TDI, AString & bool cPluginLua::OnKilling(cEntity & a_Victim, cEntity * a_Killer, TakeDamageInfo & a_TDI) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_KILLING]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Victim, a_Killer, &a_TDI, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_KILLING, &a_Victim, a_Killer, &a_TDI); } @@ -856,22 +544,7 @@ bool cPluginLua::OnKilling(cEntity & a_Victim, cEntity * a_Killer, TakeDamageInf bool cPluginLua::OnLogin(cClientHandle & a_Client, UInt32 a_ProtocolVersion, const AString & a_Username) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_LOGIN]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Client, a_ProtocolVersion, a_Username, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_LOGIN, &a_Client, a_ProtocolVersion, a_Username); } @@ -880,22 +553,7 @@ bool cPluginLua::OnLogin(cClientHandle & a_Client, UInt32 a_ProtocolVersion, con bool cPluginLua::OnPlayerAnimation(cPlayer & a_Player, int a_Animation) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_ANIMATION]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Player, a_Animation, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_PLAYER_ANIMATION, &a_Player, a_Animation); } @@ -904,22 +562,7 @@ bool cPluginLua::OnPlayerAnimation(cPlayer & a_Player, int a_Animation) bool cPluginLua::OnPlayerBreakingBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_BREAKING_BLOCK]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_BlockType, a_BlockMeta, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_PLAYER_BREAKING_BLOCK, &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_BlockType, a_BlockMeta); } @@ -928,22 +571,7 @@ bool cPluginLua::OnPlayerBreakingBlock(cPlayer & a_Player, int a_BlockX, int a_B bool cPluginLua::OnPlayerBrokenBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_BROKEN_BLOCK]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_BlockType, a_BlockMeta, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_PLAYER_BROKEN_BLOCK, &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_BlockType, a_BlockMeta); } @@ -952,22 +580,7 @@ bool cPluginLua::OnPlayerBrokenBlock(cPlayer & a_Player, int a_BlockX, int a_Blo bool cPluginLua::OnPlayerDestroyed(cPlayer & a_Player) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_DESTROYED]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Player, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_PLAYER_DESTROYED, &a_Player); } @@ -976,22 +589,7 @@ bool cPluginLua::OnPlayerDestroyed(cPlayer & a_Player) bool cPluginLua::OnPlayerEating(cPlayer & a_Player) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_EATING]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Player, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_PLAYER_EATING, &a_Player); } @@ -1000,22 +598,7 @@ bool cPluginLua::OnPlayerEating(cPlayer & a_Player) bool cPluginLua::OnPlayerFoodLevelChange(cPlayer & a_Player, int a_NewFoodLevel) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_FOOD_LEVEL_CHANGE]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Player, a_NewFoodLevel, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_PLAYER_FOOD_LEVEL_CHANGE, &a_Player, a_NewFoodLevel); } @@ -1024,22 +607,7 @@ bool cPluginLua::OnPlayerFoodLevelChange(cPlayer & a_Player, int a_NewFoodLevel) bool cPluginLua::OnPlayerFished(cPlayer & a_Player, const cItems & a_Reward) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_FISHED]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Player, a_Reward, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_PLAYER_FISHED, &a_Player, a_Reward); } @@ -1048,22 +616,7 @@ bool cPluginLua::OnPlayerFished(cPlayer & a_Player, const cItems & a_Reward) bool cPluginLua::OnPlayerFishing(cPlayer & a_Player, cItems & a_Reward) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_FISHING]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Player, &a_Reward, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_PLAYER_FISHING, &a_Player, &a_Reward); } @@ -1072,22 +625,7 @@ bool cPluginLua::OnPlayerFishing(cPlayer & a_Player, cItems & a_Reward) bool cPluginLua::OnPlayerJoined(cPlayer & a_Player) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_JOINED]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Player, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_PLAYER_JOINED, &a_Player); } @@ -1096,22 +634,7 @@ bool cPluginLua::OnPlayerJoined(cPlayer & a_Player) bool cPluginLua::OnPlayerLeftClick(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_LEFT_CLICK]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_Status, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_PLAYER_LEFT_CLICK, &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_Status); } @@ -1120,22 +643,7 @@ bool cPluginLua::OnPlayerLeftClick(cPlayer & a_Player, int a_BlockX, int a_Block bool cPluginLua::OnPlayerMoving(cPlayer & a_Player, const Vector3d & a_OldPosition, const Vector3d & a_NewPosition) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_MOVING]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Player, a_OldPosition, a_NewPosition, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_PLAYER_MOVING, &a_Player, a_OldPosition, a_NewPosition); } @@ -1144,22 +652,7 @@ bool cPluginLua::OnPlayerMoving(cPlayer & a_Player, const Vector3d & a_OldPositi bool cPluginLua::OnEntityTeleport(cEntity & a_Entity, const Vector3d & a_OldPosition, const Vector3d & a_NewPosition) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_ENTITY_TELEPORT]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Entity, a_OldPosition, a_NewPosition, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_ENTITY_TELEPORT, &a_Entity, a_OldPosition, a_NewPosition); } @@ -1168,27 +661,11 @@ bool cPluginLua::OnEntityTeleport(cEntity & a_Entity, const Vector3d & a_OldPosi bool cPluginLua::OnPlayerPlacedBlock(cPlayer & a_Player, const sSetBlock & a_BlockChange) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_PLACED_BLOCK]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Player, - a_BlockChange.GetX(), a_BlockChange.GetY(), a_BlockChange.GetZ(), - a_BlockChange.m_BlockType, a_BlockChange.m_BlockMeta, - cLuaState::Return, - res - ); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_PLAYER_PLACED_BLOCK, + &a_Player, + a_BlockChange.GetX(), a_BlockChange.GetY(), a_BlockChange.GetZ(), + a_BlockChange.m_BlockType, a_BlockChange.m_BlockMeta + ); } @@ -1197,27 +674,11 @@ bool cPluginLua::OnPlayerPlacedBlock(cPlayer & a_Player, const sSetBlock & a_Blo bool cPluginLua::OnPlayerPlacingBlock(cPlayer & a_Player, const sSetBlock & a_BlockChange) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_PLACING_BLOCK]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Player, - a_BlockChange.GetX(), a_BlockChange.GetY(), a_BlockChange.GetZ(), - a_BlockChange.m_BlockType, a_BlockChange.m_BlockMeta, - cLuaState::Return, - res - ); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_PLAYER_PLACING_BLOCK, + &a_Player, + a_BlockChange.GetX(), a_BlockChange.GetY(), a_BlockChange.GetZ(), + a_BlockChange.m_BlockType, a_BlockChange.m_BlockMeta + ); } @@ -1226,22 +687,7 @@ bool cPluginLua::OnPlayerPlacingBlock(cPlayer & a_Player, const sSetBlock & a_Bl bool cPluginLua::OnPlayerRightClick(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_RIGHT_CLICK]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_PLAYER_RIGHT_CLICK, &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ); } @@ -1250,22 +696,7 @@ bool cPluginLua::OnPlayerRightClick(cPlayer & a_Player, int a_BlockX, int a_Bloc bool cPluginLua::OnPlayerRightClickingEntity(cPlayer & a_Player, cEntity & a_Entity) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_RIGHT_CLICKING_ENTITY]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Player, &a_Entity, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_PLAYER_RIGHT_CLICKING_ENTITY, &a_Player, &a_Entity); } @@ -1274,22 +705,7 @@ bool cPluginLua::OnPlayerRightClickingEntity(cPlayer & a_Player, cEntity & a_Ent bool cPluginLua::OnPlayerShooting(cPlayer & a_Player) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_SHOOTING]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Player, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_PLAYER_SHOOTING, &a_Player); } @@ -1298,22 +714,7 @@ bool cPluginLua::OnPlayerShooting(cPlayer & a_Player) bool cPluginLua::OnPlayerSpawned(cPlayer & a_Player) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_SPAWNED]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Player, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_PLAYER_SPAWNED, &a_Player); } @@ -1322,22 +723,7 @@ bool cPluginLua::OnPlayerSpawned(cPlayer & a_Player) bool cPluginLua::OnPlayerTossingItem(cPlayer & a_Player) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_TOSSING_ITEM]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Player, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_PLAYER_TOSSING_ITEM, &a_Player); } @@ -1346,22 +732,7 @@ bool cPluginLua::OnPlayerTossingItem(cPlayer & a_Player) bool cPluginLua::OnPlayerUsedBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_USED_BLOCK]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, a_BlockType, a_BlockMeta, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_PLAYER_USED_BLOCK, &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, a_BlockType, a_BlockMeta); } @@ -1370,22 +741,7 @@ bool cPluginLua::OnPlayerUsedBlock(cPlayer & a_Player, int a_BlockX, int a_Block bool cPluginLua::OnPlayerUsedItem(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_USED_ITEM]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_PLAYER_USED_ITEM, &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ); } @@ -1394,22 +750,7 @@ bool cPluginLua::OnPlayerUsedItem(cPlayer & a_Player, int a_BlockX, int a_BlockY bool cPluginLua::OnPlayerUsingBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_USING_BLOCK]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, a_BlockType, a_BlockMeta, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_PLAYER_USING_BLOCK, &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, a_BlockType, a_BlockMeta); } @@ -1418,22 +759,7 @@ bool cPluginLua::OnPlayerUsingBlock(cPlayer & a_Player, int a_BlockX, int a_Bloc bool cPluginLua::OnPlayerUsingItem(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_USING_ITEM]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_PLAYER_USING_ITEM, &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ); } @@ -1442,22 +768,7 @@ bool cPluginLua::OnPlayerUsingItem(cPlayer & a_Player, int a_BlockX, int a_Block bool cPluginLua::OnPluginMessage(cClientHandle & a_Client, const AString & a_Channel, const AString & a_Message) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLUGIN_MESSAGE]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Client, a_Channel, a_Message, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_PLUGIN_MESSAGE, &a_Client, a_Channel, a_Message); } @@ -1472,11 +783,11 @@ bool cPluginLua::OnPluginsLoaded(void) return false; } bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLUGINS_LOADED]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + auto & hooks = m_HookMap[cPluginManager::HOOK_PLUGINS_LOADED]; + for (auto & hook: hooks) { bool ret = false; - m_LuaState.Call(static_cast<int>(**itr), cLuaState::Return, ret); + hook->Call(cLuaState::Return, ret); res = res || ret; } return res; @@ -1488,22 +799,7 @@ bool cPluginLua::OnPluginsLoaded(void) bool cPluginLua::OnPostCrafting(cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe & a_Recipe) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_POST_CRAFTING]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Player, &a_Grid, &a_Recipe, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_POST_CRAFTING, &a_Player, &a_Grid, &a_Recipe); } @@ -1512,22 +808,7 @@ bool cPluginLua::OnPostCrafting(cPlayer & a_Player, cCraftingGrid & a_Grid, cCra bool cPluginLua::OnPreCrafting(cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe & a_Recipe) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PRE_CRAFTING]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Player, &a_Grid, &a_Recipe, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_PRE_CRAFTING, &a_Player, &a_Grid, &a_Recipe); } @@ -1536,22 +817,7 @@ bool cPluginLua::OnPreCrafting(cPlayer & a_Player, cCraftingGrid & a_Grid, cCraf bool cPluginLua::OnProjectileHitBlock(cProjectileEntity & a_Projectile, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_Face, const Vector3d & a_BlockHitPos) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PROJECTILE_HIT_BLOCK]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Projectile, a_BlockX, a_BlockY, a_BlockZ, a_Face, a_BlockHitPos, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_PROJECTILE_HIT_BLOCK, &a_Projectile, a_BlockX, a_BlockY, a_BlockZ, a_Face, a_BlockHitPos); } @@ -1560,22 +826,7 @@ bool cPluginLua::OnProjectileHitBlock(cProjectileEntity & a_Projectile, int a_Bl bool cPluginLua::OnProjectileHitEntity(cProjectileEntity & a_Projectile, cEntity & a_HitEntity) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PROJECTILE_HIT_ENTITY]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Projectile, &a_HitEntity, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_PROJECTILE_HIT_ENTITY, &a_Projectile, &a_HitEntity); } @@ -1590,10 +841,10 @@ bool cPluginLua::OnServerPing(cClientHandle & a_ClientHandle, AString & a_Server return false; } bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_SERVER_PING]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + auto & hooks = m_HookMap[cPluginManager::HOOK_SERVER_PING]; + for (auto & hook: hooks) { - m_LuaState.Call(static_cast<int>(**itr), &a_ClientHandle, a_ServerDescription, a_OnlinePlayersCount, a_MaxPlayersCount, a_Favicon, cLuaState::Return, res, a_ServerDescription, a_OnlinePlayersCount, a_MaxPlayersCount, a_Favicon); + hook->Call(&a_ClientHandle, a_ServerDescription, a_OnlinePlayersCount, a_MaxPlayersCount, a_Favicon, cLuaState::Return, res, a_ServerDescription, a_OnlinePlayersCount, a_MaxPlayersCount, a_Favicon); if (res) { return true; @@ -1608,22 +859,7 @@ bool cPluginLua::OnServerPing(cClientHandle & a_ClientHandle, AString & a_Server bool cPluginLua::OnSpawnedEntity(cWorld & a_World, cEntity & a_Entity) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_SPAWNED_ENTITY]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_World, &a_Entity, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_SPAWNED_ENTITY, &a_World, &a_Entity); } @@ -1632,22 +868,7 @@ bool cPluginLua::OnSpawnedEntity(cWorld & a_World, cEntity & a_Entity) bool cPluginLua::OnSpawnedMonster(cWorld & a_World, cMonster & a_Monster) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_SPAWNED_MONSTER]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_World, &a_Monster, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_SPAWNED_MONSTER, &a_World, &a_Monster); } @@ -1656,22 +877,7 @@ bool cPluginLua::OnSpawnedMonster(cWorld & a_World, cMonster & a_Monster) bool cPluginLua::OnSpawningEntity(cWorld & a_World, cEntity & a_Entity) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_SPAWNING_ENTITY]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_World, &a_Entity, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_SPAWNING_ENTITY, &a_World, &a_Entity); } @@ -1680,22 +886,7 @@ bool cPluginLua::OnSpawningEntity(cWorld & a_World, cEntity & a_Entity) bool cPluginLua::OnSpawningMonster(cWorld & a_World, cMonster & a_Monster) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_SPAWNING_MONSTER]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_World, &a_Monster, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_SPAWNING_MONSTER, &a_World, &a_Monster); } @@ -1704,22 +895,7 @@ bool cPluginLua::OnSpawningMonster(cWorld & a_World, cMonster & a_Monster) bool cPluginLua::OnTakeDamage(cEntity & a_Receiver, TakeDamageInfo & a_TDI) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_TAKE_DAMAGE]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Receiver, &a_TDI, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_TAKE_DAMAGE, &a_Receiver, &a_TDI); } @@ -1733,22 +909,7 @@ bool cPluginLua::OnUpdatedSign( cPlayer * a_Player ) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_UPDATED_SIGN]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_World, a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4, a_Player, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_UPDATED_SIGN, &a_World, a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4, a_Player); } @@ -1768,10 +929,10 @@ bool cPluginLua::OnUpdatingSign( return false; } bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_UPDATING_SIGN]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + auto & hooks = m_HookMap[cPluginManager::HOOK_UPDATING_SIGN]; + for (auto & hook: hooks) { - m_LuaState.Call(static_cast<int>(**itr), &a_World, a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4, a_Player, cLuaState::Return, res, a_Line1, a_Line2, a_Line3, a_Line4); + hook->Call(&a_World, a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4, a_Player, cLuaState::Return, res, a_Line1, a_Line2, a_Line3, a_Line4); if (res) { return true; @@ -1786,22 +947,7 @@ bool cPluginLua::OnUpdatingSign( bool cPluginLua::OnWeatherChanged(cWorld & a_World) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_WEATHER_CHANGED]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_World, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_WEATHER_CHANGED, &a_World); } @@ -1816,10 +962,10 @@ bool cPluginLua::OnWeatherChanging(cWorld & a_World, eWeather & a_NewWeather) return false; } bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_WEATHER_CHANGING]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + auto & hooks = m_HookMap[cPluginManager::HOOK_WEATHER_CHANGING]; + for (auto & hook: hooks) { - m_LuaState.Call(static_cast<int>(**itr), &a_World, a_NewWeather, cLuaState::Return, res, a_NewWeather); + hook->Call(&a_World, a_NewWeather, cLuaState::Return, res, a_NewWeather); if (res) { return true; @@ -1834,17 +980,7 @@ bool cPluginLua::OnWeatherChanging(cWorld & a_World, eWeather & a_NewWeather) bool cPluginLua::OnWorldStarted(cWorld & a_World) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_WORLD_STARTED]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_World); - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_WORLD_STARTED, &a_World); } @@ -1853,17 +989,7 @@ bool cPluginLua::OnWorldStarted(cWorld & a_World) bool cPluginLua::OnWorldTick(cWorld & a_World, std::chrono::milliseconds a_Dt, std::chrono::milliseconds a_LastTickDurationMSec) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_WORLD_TICK]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_World, a_Dt, a_LastTickDurationMSec); - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_WORLD_TICK, &a_World, a_Dt, a_LastTickDurationMSec); } @@ -2059,22 +1185,11 @@ const char * cPluginLua::GetHookFnName(int a_HookType) -bool cPluginLua::AddHookRef(int a_HookType, int a_FnRefIdx) +bool cPluginLua::AddHookCallback(int a_HookType, cLuaState::cCallbackPtr a_Callback) { ASSERT(m_CriticalSection.IsLockedByCurrentThread()); // It probably has to be, how else would we have a LuaState? - // Check if the function reference is valid: - cLuaState::cRef * Ref = new cLuaState::cRef(m_LuaState, a_FnRefIdx); - if ((Ref == nullptr) || !Ref->IsValid()) - { - LOGWARNING("Plugin %s tried to add a hook %d with bad handler function.", GetName().c_str(), a_HookType); - m_LuaState.LogStackTrace(); - delete Ref; - Ref = nullptr; - return false; - } - - m_HookMap[a_HookType].push_back(Ref); + m_HookMap[a_HookType].push_back(a_Callback); return true; } @@ -2116,61 +1231,6 @@ 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()); @@ -2227,22 +1287,13 @@ void cPluginLua::CallbackWindowSlotChanged(int a_FnRef, cWindow & a_Window, int -//////////////////////////////////////////////////////////////////////////////// -// cPluginLua::cResettable: - -cPluginLua::cResettable::cResettable(cPluginLua & a_Plugin): - m_Plugin(&a_Plugin) -{ -} - - - - - -void cPluginLua::cResettable::Reset(void) +void cPluginLua::ClearWebTabs(void) { - cCSLock Lock(m_CSPlugin); - m_Plugin = nullptr; + auto webAdmin = cRoot::Get()->GetWebAdmin(); + if (webAdmin != nullptr) // can be nullptr when shutting down the server + { + webAdmin->RemoveAllPluginWebTabs(m_Name); + } } diff --git a/src/Bindings/PluginLua.h b/src/Bindings/PluginLua.h index db6612671..e53fbaaa8 100644 --- a/src/Bindings/PluginLua.h +++ b/src/Bindings/PluginLua.h @@ -10,7 +10,6 @@ #pragma once #include "Plugin.h" -#include "WebPlugin.h" #include "LuaState.h" // Names for the global variables through which the plugin is identified in its LuaState @@ -29,8 +28,7 @@ class cWindow; // tolua_begin class cPluginLua : - public cPlugin, - public cWebPlugin + public cPlugin { typedef cPlugin super; @@ -64,36 +62,6 @@ 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(); @@ -181,14 +149,6 @@ 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); @@ -211,11 +171,9 @@ 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 function to be called for the specified hook. - The function has to be on the Lua stack at the specified index a_FnRefIdx - Returns true if the hook was added successfully. - */ - bool AddHookRef(int a_HookType, int a_FnRefIdx); + /** Adds a Lua callback to be called for the specified hook. + Returns true if the hook was added successfully. */ + bool AddHookCallback(int a_HookType, cLuaState::cCallbackPtr a_Callback); /** Calls a function in this plugin's LuaState with parameters copied over from a_ForeignState. The values that the function returns are placed onto a_ForeignState. @@ -235,29 +193,23 @@ 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::cRef *> cLuaRefs; + typedef std::vector<cLuaState::cCallbackPtr> cLuaCallbacks; /** Maps hook types into arrays of Lua function references to call for each hook type */ - typedef std::map<int, cLuaRefs> cHookMap; + typedef std::map<int, cLuaCallbacks> cHookMap; - /** The mutex protecting m_LuaState and each of the m_Resettables[] against multithreaded use. */ + /** The mutex protecting m_LuaState 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; @@ -270,6 +222,30 @@ 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 5b3ef7803..203450505 100644 --- a/src/Bindings/PluginManager.cpp +++ b/src/Bindings/PluginManager.cpp @@ -1965,6 +1965,23 @@ bool cPluginManager::ForEachPlugin(cPluginCallback & a_Callback) +AString cPluginManager::GetPluginFolderName(const AString & a_PluginName) +{ + // TODO: Implement locking for plugins + for (auto & plugin: m_Plugins) + { + if (plugin->GetName() == a_PluginName) + { + return plugin->GetFolderName(); + } + } + return AString(); +} + + + + + void cPluginManager::AddHook(cPlugin * a_Plugin, int a_Hook) { if (a_Plugin == nullptr) diff --git a/src/Bindings/PluginManager.h b/src/Bindings/PluginManager.h index f3f0b6d0b..a97582fbe 100644 --- a/src/Bindings/PluginManager.h +++ b/src/Bindings/PluginManager.h @@ -332,6 +332,9 @@ public: Returns true if all plugins have been reported, false if the callback has aborted the enumeration by returning true. */ bool ForEachPlugin(cPluginCallback & a_Callback); + /** Returns the name of the folder (cPlugin::GetFolderName()) from which the specified plugin was loaded. */ + AString GetPluginFolderName(const AString & a_PluginName); // tolua_export + /** Returns the path where individual plugins' folders are expected. The path doesn't end in a slash. */ static AString GetPluginsPath(void) { return FILE_IO_PREFIX + AString("Plugins"); } // tolua_export diff --git a/src/Bindings/WebPlugin.cpp b/src/Bindings/WebPlugin.cpp deleted file mode 100644 index 1eca7de93..000000000 --- a/src/Bindings/WebPlugin.cpp +++ /dev/null @@ -1,152 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "WebPlugin.h" -#include "../WebAdmin.h" -#include "../Root.h" - - - - - -cWebPlugin::cWebPlugin() -{ - cWebAdmin * WebAdmin = cRoot::Get()->GetWebAdmin(); - if (WebAdmin != nullptr) - { - WebAdmin->AddPlugin(this); - } -} - - - - - -cWebPlugin::~cWebPlugin() -{ - ASSERT(m_Tabs.empty()); // Has ClearTabs() been called? - - // Remove from WebAdmin: - cWebAdmin * WebAdmin = cRoot::Get()->GetWebAdmin(); - if (WebAdmin != nullptr) - { - WebAdmin->RemovePlugin(this); - } -} - - - - - -cWebPlugin::cTabNames cWebPlugin::GetTabNames(void) const -{ - std::list< std::pair<AString, AString>> NameList; - for (auto itr = m_Tabs.cbegin(), end = m_Tabs.cend(); itr != end; ++itr) - { - NameList.push_back(std::make_pair((*itr)->m_Title, (*itr)->m_SafeTitle)); - } - return NameList; -} - - - - - -cWebPlugin::cTabPtr cWebPlugin::GetTabBySafeTitle(const AString & a_SafeTitle) const -{ - cCSLock Lock(m_CSTabs); - for (auto itr = m_Tabs.cbegin(), end = m_Tabs.cend(); itr != end; ++itr) - { - if ((*itr)->m_SafeTitle == a_SafeTitle) - { - return *itr; - } - } - return nullptr; -} - - - - - -std::pair<AString, AString> cWebPlugin::GetTabNameForRequest(const HTTPRequest & a_Request) -{ - AStringVector Split = StringSplit(a_Request.Path, "/"); - if (Split.empty()) - { - return std::make_pair(AString(), AString()); - } - - cCSLock Lock(m_CSTabs); - cTabPtr Tab; - if (Split.size() > 2) // If we got the tab name, show that page - { - for (auto itr = m_Tabs.cbegin(), end = m_Tabs.cend(); itr != end; ++itr) - { - if ((*itr)->m_SafeTitle.compare(Split[2]) == 0) // This is the one! - { - return std::make_pair((*itr)->m_Title, (*itr)->m_SafeTitle); - } - } - // Tab name not found, display an "empty" page: - return std::make_pair(AString(), AString()); - } - - // Show the first tab: - if (!m_Tabs.empty()) - { - return std::make_pair(m_Tabs.front()->m_SafeTitle, m_Tabs.front()->m_SafeTitle); - } - - // No tabs at all: - return std::make_pair(AString(), AString()); -} - - - - -AString cWebPlugin::SafeString(const AString & a_String) -{ - AString RetVal; - auto len = a_String.size(); - RetVal.reserve(len); - for (size_t i = 0; i < len; ++i) - { - char c = a_String[i]; - if (c == ' ') - { - c = '_'; - } - RetVal.push_back(c); - } - return RetVal; -} - - - - - -void cWebPlugin::AddNewWebTab(const AString & a_Title, int a_UserData) -{ - auto Tab = std::make_shared<cTab>(a_Title, a_UserData); - cCSLock Lock(m_CSTabs); - m_Tabs.push_back(Tab); -} - - - - - -void cWebPlugin::ClearTabs(void) -{ - // Remove the webadmin tabs: - cTabPtrs Tabs; - { - cCSLock Lock(m_CSTabs); - std::swap(Tabs, m_Tabs); - } -} - - - - diff --git a/src/Bindings/WebPlugin.h b/src/Bindings/WebPlugin.h deleted file mode 100644 index 6dc8db801..000000000 --- a/src/Bindings/WebPlugin.h +++ /dev/null @@ -1,80 +0,0 @@ - -#pragma once - -struct HTTPRequest; - - - - - -// tolua_begin -class cWebPlugin -{ -public: - // tolua_end - - struct cTab - { - AString m_Title; - AString m_SafeTitle; - int m_UserData; - - cTab(const AString & a_Title, int a_UserData): - m_Title(a_Title), - m_SafeTitle(cWebPlugin::SafeString(a_Title)), - m_UserData(a_UserData) - { - } - }; - - typedef SharedPtr<cTab> cTabPtr; - typedef std::list<cTabPtr> cTabPtrs; - typedef std::list<std::pair<AString, AString>> cTabNames; - - - cWebPlugin(); - - virtual ~cWebPlugin(); - - // tolua_begin - - /** Returns the title of the plugin, as it should be presented in the webadmin's pages tree. */ - virtual const AString GetWebTitle(void) const = 0; - - /** Sanitizes the input string, replacing spaces with underscores. */ - static AString SafeString(const AString & a_String); - - // tolua_end - - virtual AString HandleWebRequest(const HTTPRequest & a_Request) = 0; - - /** Adds a new web tab with the specified contents. */ - void AddNewWebTab(const AString & a_Title, int a_UserData); - - /** Removes all the tabs. */ - void ClearTabs(void); - - /** Returns all the tabs that this plugin has registered. */ - const cTabPtrs & GetTabs(void) const { return m_Tabs; } - - /** Returns all of the tabs that this plugin has registered. */ - cTabNames GetTabNames(void) const; // Exported in ManualBindings.cpp - - /** Returns the tab that has the specified SafeTitle. - Returns nullptr if no such tab. */ - cTabPtr GetTabBySafeTitle(const AString & a_SafeTitle) const; - - std::pair<AString, AString> GetTabNameForRequest(const HTTPRequest & a_Request); - -private: - /** All tabs that this plugin has registered. - Protected against multithreaded access by m_CSTabs. */ - cTabPtrs m_Tabs; - - /** Protects m_Tabs against multithreaded access. */ - mutable cCriticalSection m_CSTabs; -}; // tolua_export - - - - diff --git a/src/Entities/Player.h b/src/Entities/Player.h index 6d092eeb3..bca9c5547 100644 --- a/src/Entities/Player.h +++ b/src/Entities/Player.h @@ -225,11 +225,11 @@ public: cWindow * GetWindow(void) { return m_CurrentWindow; } // tolua_export const cWindow * GetWindow(void) const { return m_CurrentWindow; } - /** Opens the specified window; closes the current one first using CloseWindow() */ - void OpenWindow(cWindow * a_Window); // Exported in ManualBindings.cpp - // tolua_begin + /** Opens the specified window; closes the current one first using CloseWindow() */ + void OpenWindow(cWindow * a_Window); + /** Closes the current window, resets current window to m_InventoryWindow. A plugin may refuse the closing if a_CanRefuse is true */ void CloseWindow(bool a_CanRefuse = true); diff --git a/src/UI/Window.cpp b/src/UI/Window.cpp index 5a2e55feb..4582d6cf4 100644 --- a/src/UI/Window.cpp +++ b/src/UI/Window.cpp @@ -307,7 +307,7 @@ bool cWindow::ClosedByPlayer(cPlayer & a_Player, bool a_CanRefuse) // Checks whether the player is still holding an item if (!a_Player.GetDraggingItem().IsEmpty()) { - LOGD("Player holds item! Dropping it..."); + LOGD("Player is holding an item while closing their window, dropping it as a pickup..."); a_Player.TossHeldItem(a_Player.GetDraggingItem().m_ItemCount); } diff --git a/src/WebAdmin.cpp b/src/WebAdmin.cpp index e43d749dd..5c08deb0d 100644 --- a/src/WebAdmin.cpp +++ b/src/WebAdmin.cpp @@ -2,10 +2,6 @@ #include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules #include "WebAdmin.h" -#include "Bindings/WebPlugin.h" - -#include "Bindings/PluginManager.h" -#include "Bindings/Plugin.h" #include "World.h" #include "Entities/Player.h" @@ -87,9 +83,9 @@ public: // cWebAdmin: cWebAdmin::cWebAdmin(void) : + m_TemplateScript("<webadmin_template>"), m_IsInitialized(false), - m_IsRunning(false), - m_TemplateScript("<webadmin_template>") + m_IsRunning(false) { } @@ -106,40 +102,9 @@ cWebAdmin::~cWebAdmin() -void cWebAdmin::AddPlugin(cWebPlugin * a_Plugin) -{ - m_Plugins.remove(a_Plugin); - m_Plugins.push_back(a_Plugin); -} - - - - - -void cWebAdmin::RemovePlugin(cWebPlugin * a_Plugin) -{ - m_Plugins.remove(a_Plugin); -} - - - - - bool cWebAdmin::Init(void) { - if (!m_IniFile.ReadFile("webadmin.ini")) - { - LOGWARN("Regenerating webadmin.ini, all settings will be reset"); - m_IniFile.AddHeaderComment(" This file controls the webadmin feature of Cuberite"); - m_IniFile.AddHeaderComment(" Username format: [User:*username*]"); - m_IniFile.AddHeaderComment(" Password format: Password=*password*; for example:"); - m_IniFile.AddHeaderComment(" [User:admin]"); - m_IniFile.AddHeaderComment(" Password=admin"); - m_IniFile.SetValue("WebAdmin", "Ports", DEFAULT_WEBADMIN_PORTS); - m_IniFile.WriteFile("webadmin.ini"); - } - - if (!m_IniFile.GetValueSetB("WebAdmin", "Enabled", true)) + if (!LoadIniFile()) { // WebAdmin is disabled, bail out faking a success return true; @@ -147,31 +112,7 @@ bool cWebAdmin::Init(void) LOGD("Initialising WebAdmin..."); - // Initialize the WebAdmin template script and load the file - m_TemplateScript.Create(); - m_TemplateScript.RegisterAPILibs(); - if (!m_TemplateScript.LoadFile(FILE_IO_PREFIX "webadmin/template.lua")) - { - LOGWARN("Could not load WebAdmin template \"%s\". WebAdmin disabled!", FILE_IO_PREFIX "webadmin/template.lua"); - m_TemplateScript.Close(); - m_HTTPServer.Stop(); - return false; - } - - // Load the login template, provide a fallback default if not found: - if (!LoadLoginTemplate()) - { - LOGWARN("Could not load WebAdmin login template \"%s\", using fallback template.", FILE_IO_PREFIX "webadmin/login_template.html"); - - // Sets the fallback template: - m_LoginTemplate = \ - "<h1>Cuberite WebAdmin</h1>" \ - "<center>" \ - "<form method='get' action='webadmin/'>" \ - "<input type='submit' value='Log in'>" \ - "</form>" \ - "</center>"; - } + Reload(); // Read the ports to be used: // Note that historically the ports were stored in the "Port" and "PortsIPv6" values @@ -224,7 +165,7 @@ void cWebAdmin::Stop(void) -bool cWebAdmin::LoadLoginTemplate(void) +bool cWebAdmin::LoadLoginPage(void) { cFile File(FILE_IO_PREFIX "webadmin/login_template.html", cFile::fmRead); if (!File.IsOpen()) @@ -238,7 +179,8 @@ bool cWebAdmin::LoadLoginTemplate(void) return false; } - m_LoginTemplate = TemplateContent; + cCSLock Lock(m_CS); + m_LoginPage = TemplateContent; return true; } @@ -246,6 +188,89 @@ bool cWebAdmin::LoadLoginTemplate(void) +void cWebAdmin::RemoveAllPluginWebTabs(const AString & a_PluginName) +{ + cCSLock lock(m_CS); + m_WebTabs.erase(std::remove_if(m_WebTabs.begin(), m_WebTabs.end(), [=](cWebTabPtr a_CBWebTab) + { + return (a_CBWebTab->m_PluginName == a_PluginName); + }), + m_WebTabs.end() + ); +} + + + + + +void cWebAdmin::Reload(void) +{ + cCSLock lock(m_CS); + if (!LoadIniFile()) + { + // We are asked to disable the webadmin, cannot do that, so warn the admin: + LOGWARNING( + "WebAdmin was previously enabled and now the settings say to disable it." + " This will not take effect until you restart the server." + ); + } + + // Initialize the WebAdmin template script and reload the file: + if (m_TemplateScript.IsValid()) + { + m_TemplateScript.Close(); + } + m_TemplateScript.Create(); + m_TemplateScript.RegisterAPILibs(); + if (!m_TemplateScript.LoadFile(FILE_IO_PREFIX "webadmin/template.lua")) + { + LOGWARN("Could not load WebAdmin template \"%s\". WebAdmin will not work properly!", FILE_IO_PREFIX "webadmin/template.lua"); + m_TemplateScript.Close(); + } + + // Load the login template, provide a fallback default if not found: + if (!LoadLoginPage()) + { + LOGWARN("Could not load WebAdmin login page \"%s\", using fallback template.", FILE_IO_PREFIX "webadmin/login_template.html"); + + // Set the fallback: + m_LoginPage = \ + "<h1>Cuberite WebAdmin</h1>" \ + "<center>" \ + "<form method='get' action='webadmin/'>" \ + "<input type='submit' value='Log in'>" \ + "</form>" \ + "</center>"; + } +} + + + + + +bool cWebAdmin::LoadIniFile(void) +{ + m_IniFile.Clear(); + if (!m_IniFile.ReadFile("webadmin.ini")) + { + LOGWARN("Regenerating webadmin.ini, all settings will be reset"); + m_IniFile.AddHeaderComment(" This file controls the webadmin feature of Cuberite"); + m_IniFile.AddHeaderComment(" It specifies whether webadmin is enabled, and what logins are allowed. "); + m_IniFile.AddHeaderComment(" Username format: [User:*username*]"); + m_IniFile.AddHeaderComment(" Password format: Password=*password*; for example:"); + m_IniFile.AddHeaderComment(" [User:admin]"); + m_IniFile.AddHeaderComment(" Password=admin"); + m_IniFile.SetValue("WebAdmin", "Ports", DEFAULT_WEBADMIN_PORTS); + m_IniFile.WriteFile("webadmin.ini"); + } + + return m_IniFile.GetValueSetB("WebAdmin", "Enabled", true); +} + + + + + void cWebAdmin::HandleWebadminRequest(cHTTPServerConnection & a_Connection, cHTTPIncomingRequest & a_Request) { if (!a_Request.HasAuth()) @@ -255,17 +280,20 @@ void cWebAdmin::HandleWebadminRequest(cHTTPServerConnection & a_Connection, cHTT } // Check auth: - AString UserPassword = m_IniFile.GetValue("User:" + a_Request.GetAuthUsername(), "Password", ""); - if ((UserPassword == "") || (a_Request.GetAuthPassword() != UserPassword)) { - a_Connection.SendNeedAuth("Cuberite WebAdmin - bad username or password"); - return; + cCSLock Lock(m_CS); + AString UserPassword = m_IniFile.GetValue("User:" + a_Request.GetAuthUsername(), "Password", ""); + if ((UserPassword == "") || (a_Request.GetAuthPassword() != UserPassword)) + { + a_Connection.SendNeedAuth("Cuberite WebAdmin - bad username or password"); + return; + } } // Check if the contents should be wrapped in the template: auto BareURL = a_Request.GetURLPath(); ASSERT(BareURL.length() > 0); - bool ShouldWrapInTemplate = ((BareURL.length() > 1) && (BareURL[1] != '~')); + bool ShouldWrapInTemplate = (!BareURL.empty() && (BareURL[1] != '~')); // Retrieve the request data: auto Data = std::static_pointer_cast<cWebadminRequestData>(a_Request.GetUserData()); @@ -312,6 +340,7 @@ void cWebAdmin::HandleWebadminRequest(cHTTPServerConnection & a_Connection, cHTT // Try to get the template from the Lua template script if (ShouldWrapInTemplate) { + cCSLock Lock(m_CS); if (m_TemplateScript.Call("ShowPage", this, &TemplateRequest, cLuaState::Return, Template)) { cHTTPOutgoingResponse Resp; @@ -325,59 +354,12 @@ void cWebAdmin::HandleWebadminRequest(cHTTPServerConnection & a_Connection, cHTT return; } - AString BaseURL = GetBaseURL(BareURL); - AString Menu; - Template = "{CONTENT}"; - AString FoundPlugin; - - for (PluginList::iterator itr = m_Plugins.begin(); itr != m_Plugins.end(); ++itr) - { - cWebPlugin * WebPlugin = *itr; - std::list< std::pair<AString, AString> > NameList = WebPlugin->GetTabNames(); - for (std::list< std::pair<AString, AString> >::iterator Names = NameList.begin(); Names != NameList.end(); ++Names) - { - Menu += "<li><a href='" + BaseURL + WebPlugin->GetWebTitle().c_str() + "/" + (*Names).second + "'>" + (*Names).first + "</a></li>"; - } - } - - sWebAdminPage Page = GetPage(TemplateRequest.Request); - AString Content = Page.Content; - FoundPlugin = Page.PluginName; - if (!Page.TabName.empty()) - { - FoundPlugin += " - " + Page.TabName; - } - - if (FoundPlugin.empty()) // Default page - { - Content = GetDefaultPage(); - } - - int MemUsageKiB = cRoot::GetPhysicalRAMUsage(); - if (MemUsageKiB > 0) - { - ReplaceString(Template, "{MEM}", Printf("%.02f", static_cast<double>(MemUsageKiB) / 1024)); - ReplaceString(Template, "{MEMKIB}", Printf("%d", MemUsageKiB)); - } - else - { - ReplaceString(Template, "{MEM}", "unknown"); - ReplaceString(Template, "{MEMKIB}", "unknown"); - } - ReplaceString(Template, "{USERNAME}", a_Request.GetAuthUsername()); - ReplaceString(Template, "{MENU}", Menu); - ReplaceString(Template, "{PLUGIN_NAME}", FoundPlugin); - ReplaceString(Template, "{CONTENT}", Content); - ReplaceString(Template, "{TITLE}", "Cuberite"); - - AString NumChunks; - Printf(NumChunks, "%d", cRoot::Get()->GetTotalChunkCount()); - ReplaceString(Template, "{NUMCHUNKS}", NumChunks); - - cHTTPOutgoingResponse Resp; - Resp.SetContentType("text/html"); - a_Connection.Send(Resp); - a_Connection.Send(Template.c_str(), Template.length()); + // Send the un-decorated page content: + auto page = GetPage(TemplateRequest.Request); + cHTTPOutgoingResponse resp; + resp.SetContentType(page.ContentType); + a_Connection.Send(resp); + a_Connection.Send(page.Content.c_str(), page.Content.length()); a_Connection.FinishResponse(); } @@ -392,7 +374,7 @@ void cWebAdmin::HandleRootRequest(cHTTPServerConnection & a_Connection, cHTTPInc cHTTPOutgoingResponse Resp; Resp.SetContentType("text/html"); a_Connection.Send(Resp); - a_Connection.Send(m_LoginTemplate); + a_Connection.Send(m_LoginPage); a_Connection.FinishResponse(); } @@ -406,7 +388,7 @@ void cWebAdmin::HandleFileRequest(cHTTPServerConnection & a_Connection, cHTTPInc std::replace(FileURL.begin(), FileURL.end(), '\\', '/'); // Remove all leading backslashes: - if (FileURL[0] == '/') + if (!FileURL.empty() && (FileURL[0] == '/')) { size_t FirstCharToRead = FileURL.find_first_not_of('/'); if (FirstCharToRead != AString::npos) @@ -418,8 +400,9 @@ void cWebAdmin::HandleFileRequest(cHTTPServerConnection & a_Connection, cHTTPInc // Remove all "../" strings: ReplaceString(FileURL, "../", ""); - bool LoadedSuccessfull = false; + // Read the file contents and guess its mime-type, based on the extension: AString Content = "<h2>404 Not Found</h2>"; + AString ContentType; AString Path = Printf(FILE_IO_PREFIX "webadmin/files/%s", FileURL.c_str()); if (cFile::IsFile(Path)) { @@ -427,18 +410,17 @@ void cWebAdmin::HandleFileRequest(cHTTPServerConnection & a_Connection, cHTTPInc AString FileContent; if (File.IsOpen() && (File.ReadRestOfFile(FileContent) != -1)) { - LoadedSuccessfull = true; - Content = FileContent; + std::swap(Content, FileContent); + size_t LastPointPosition = Path.find_last_of('.'); + if (LastPointPosition != AString::npos) + { + ContentType = GetContentTypeFromFileExt(Path.substr(LastPointPosition + 1)); + } } } - - // Find content type (The currently method is very bad. We should change it later) - AString ContentType = "text/html"; - size_t LastPointPosition = Path.find_last_of('.'); - if (LoadedSuccessfull && (LastPointPosition != AString::npos) && (LastPointPosition < Path.length())) + if (ContentType.empty()) { - AString FileExtension = Path.substr(LastPointPosition + 1); - ContentType = GetContentTypeFromFileExt(FileExtension); + ContentType = "application/unknown"; } // Send the response: @@ -456,32 +438,36 @@ void cWebAdmin::HandleFileRequest(cHTTPServerConnection & a_Connection, cHTTPInc AString cWebAdmin::GetContentTypeFromFileExt(const AString & a_FileExtension) { static bool IsInitialized = false; - static std::map<AString, AString> ContentTypeMap; + static AStringMap ContentTypeMap; if (!IsInitialized) { // Initialize the ContentTypeMap: - ContentTypeMap["png"] = "image/png"; - ContentTypeMap["fif"] = "image/fif"; - ContentTypeMap["gif"] = "image/gif"; - ContentTypeMap["jpeg"] = "image/jpeg"; - ContentTypeMap["jpg"] = "image/jpeg"; - ContentTypeMap["jpe"] = "image/jpeg"; - ContentTypeMap["tiff"] = "image/tiff"; - ContentTypeMap["ico"] = "image/ico"; - ContentTypeMap["csv"] = "image/comma-separated-values"; - ContentTypeMap["css"] = "text/css"; - ContentTypeMap["js"] = "text/javascript"; - ContentTypeMap["txt"] = "text/plain"; - ContentTypeMap["rtx"] = "text/richtext"; - ContentTypeMap["xml"] = "text/xml"; - } - - AString FileExtension = StrToLower(a_FileExtension); - if (ContentTypeMap.find(a_FileExtension) == ContentTypeMap.end()) - { - return "text/html"; - } - return ContentTypeMap[FileExtension]; + ContentTypeMap["png"] = "image/png"; + ContentTypeMap["fif"] = "image/fif"; + ContentTypeMap["gif"] = "image/gif"; + ContentTypeMap["jpeg"] = "image/jpeg"; + ContentTypeMap["jpg"] = "image/jpeg"; + ContentTypeMap["jpe"] = "image/jpeg"; + ContentTypeMap["tiff"] = "image/tiff"; + ContentTypeMap["ico"] = "image/ico"; + ContentTypeMap["csv"] = "text/csv"; + ContentTypeMap["css"] = "text/css"; + ContentTypeMap["js"] = "text/javascript"; + ContentTypeMap["txt"] = "text/plain"; + ContentTypeMap["rtx"] = "text/richtext"; + ContentTypeMap["rtf"] = "text/richtext"; + ContentTypeMap["xml"] = "text/xml"; + ContentTypeMap["html"] = "text/html"; + ContentTypeMap["htm"] = "text/html"; + ContentTypeMap["xhtml"] = "application/xhtml+xml"; // Not recomended for IE6, but no-one uses that anymore + } + + auto itr = ContentTypeMap.find(StrToLower(a_FileExtension)); + if (itr == ContentTypeMap.end()) + { + return AString(); + } + return itr->second; } @@ -490,86 +476,93 @@ AString cWebAdmin::GetContentTypeFromFileExt(const AString & a_FileExtension) sWebAdminPage cWebAdmin::GetPage(const HTTPRequest & a_Request) { - sWebAdminPage Page; - AStringVector Split = StringSplit(a_Request.Path, "/"); + sWebAdminPage page; + auto split = StringSplit(a_Request.Path, "/"); + + // If no specific page was requested, return an empty object: + if (split.size() <= 2) + { + return page; + } - // Find the plugin that corresponds to the requested path - AString FoundPlugin; - if (Split.size() > 1) + // Find the WebTab handler responsible for the request: + cWebTabPtr tab; { - for (PluginList::iterator itr = m_Plugins.begin(); itr != m_Plugins.end(); ++itr) + cCSLock Lock(m_CS); + for (auto & wt: m_WebTabs) { - if ((*itr)->GetWebTitle() == Split[1]) + if ( + (wt->m_PluginName == split[1]) && + (wt->m_UrlPath == split[2]) + ) { - Page.Content = (*itr)->HandleWebRequest(a_Request); - cWebPlugin * WebPlugin = *itr; - FoundPlugin = WebPlugin->GetWebTitle(); - AString TabName = WebPlugin->GetTabNameForRequest(a_Request).first; - Page.PluginName = FoundPlugin; - Page.TabName = TabName; + tab = wt; break; } + } // for wt - m_WebTabs[] + } + + // If a WebTab handler was found, call it: + if (tab != nullptr) + { + page.ContentType = "text/html"; // Default to HTML content type, unless overridden by a plugin + if (!tab->m_Callback->Call(a_Request, split[1], page.Content, page.ContentType)) + { + page.Content = GetHTMLEscapedString(Printf( + "WebTab callback for plugin %s, page %s has failed.", + tab->m_PluginName.c_str(), tab->m_Title.c_str() + )); } + page.PluginName = tab->m_PluginName; + page.TabTitle = tab->m_Title; + page.TabUrlPath = split[1]; } - // Return the page contents - return Page; + return page; } -AString cWebAdmin::GetDefaultPage(void) +AString cWebAdmin::GetBaseURL(const AString & a_URL) { - AString Content; - Content += "<h4>Server Name:</h4>"; - Content += "<p>" + AString( cRoot::Get()->GetServer()->GetServerID()) + "</p>"; + return GetBaseURL(StringSplit(a_URL, "/")); +} - // Display a list of all plugins: - Content += "<h4>Plugins:</h4><ul>"; - struct cPluginCallback: - public cPluginManager::cPluginCallback - { - AString & m_Content; - cPluginCallback(AString & a_Content): - m_Content(a_Content) - { - } - virtual bool Item(cPlugin * a_Plugin) override - { - if (a_Plugin->IsLoaded()) - { - AppendPrintf(m_Content, "<li>%s V.%i</li>", a_Plugin->GetName().c_str(), a_Plugin->GetVersion()); - } - return false; - } - } Callback(Content); - cPluginManager::Get()->ForEachPlugin(Callback); - Content += "</ul>"; - // Display a list of all players: - Content += "<h4>Players:</h4><ul>"; - cPlayerAccum PlayerAccum; - cWorld * World = cRoot::Get()->GetDefaultWorld(); // TODO - Create a list of worlds and players - if (World != nullptr) - { - World->ForEachPlayer(PlayerAccum); - Content.append(PlayerAccum.m_Contents); - } - Content += "</ul><br>"; - return Content; + +void cWebAdmin::AddWebTab( + const AString & a_Title, + const AString & a_UrlPath, + const AString & a_PluginName, + SharedPtr<cWebAdmin::cWebTabCallback> a_Callback +) +{ + cCSLock lock(m_CS); + m_WebTabs.emplace_back(std::make_shared<cWebTab>(a_Title, a_UrlPath, a_PluginName, a_Callback)); } -AString cWebAdmin::GetBaseURL(const AString & a_URL) +bool cWebAdmin::DelWebTab(const AString & a_UrlPath) { - return GetBaseURL(StringSplit(a_URL, "/")); + cCSLock lock(m_CS); + for (auto itr = m_WebTabs.begin(), end = m_WebTabs.end(); itr != end; ++itr) + { + if ((*itr)->m_UrlPath == a_UrlPath) + { + m_WebTabs.erase(itr); + return true; + } + } // for itr - m_WebTabs[] + + // Not found: + return false; } diff --git a/src/WebAdmin.h b/src/WebAdmin.h index 5e48f597c..b76ca6df8 100644 --- a/src/WebAdmin.h +++ b/src/WebAdmin.h @@ -89,14 +89,14 @@ struct HTTPTemplateRequest -// tolua_begin struct sWebAdminPage { AString Content; AString PluginName; - AString TabName; + AString TabTitle; + AString TabUrlPath; + AString ContentType; }; -// tolua_end @@ -111,7 +111,49 @@ class cWebAdmin : public: // tolua_end - typedef std::list< cWebPlugin* > PluginList; + /** Interface for getting the content of a single WebTab. */ + class cWebTabCallback abstract + { + public: + // Force a virtual destructor in descendants + virtual ~cWebTabCallback() {} + + /** Returns the contents for the specified request. + Returns true if the call was successful, false on an error. + a_Request is the full HTTP request object, as received from the client. + a_UrlPath is the UrlPath of the WebTab registered for this request, as parsed from a_Request. + Descendants should fill a_Content with the page contents + and optionally set a_ContentType [defaults to "text/html"] */ + virtual bool Call( + const HTTPRequest & a_Request, + const AString & a_UrlPath, + AString & a_Content, + AString & a_ContentType + ) = 0; + }; + + + /** Container for a single web tab. + Each web tab has a title, URL path and an associated plugin's name. + Each web tab is registered with a callback to provide the content. */ + class cWebTab + { + public: + AString m_Title; + AString m_UrlPath; + AString m_PluginName; + SharedPtr<cWebTabCallback> m_Callback; + + cWebTab(const AString & a_Title, const AString & a_UrlPath, const AString & a_PluginName, SharedPtr<cWebTabCallback> a_Callback): + m_Title(a_Title), + m_UrlPath(a_UrlPath), + m_PluginName(a_PluginName), + m_Callback(a_Callback) + { + } + }; + typedef SharedPtr<cWebTab> cWebTabPtr; + typedef std::vector<cWebTabPtr> cWebTabPtrs; cWebAdmin(void); @@ -120,81 +162,115 @@ public: /** Initializes the object. Returns true if successfully initialized and ready to start */ bool Init(void); - /** Starts the HTTP server taking care of the admin. Returns true if successful */ + /** Starts the HTTP server taking care of the webadmin. Returns true if successful */ bool Start(void); /** Stops the HTTP server, if it was started. */ void Stop(void); - /** Loads the login template. Returns true if the loading succeeds, false if not. */ - bool LoadLoginTemplate(void); - - void AddPlugin(cWebPlugin * a_Plugin); - void RemovePlugin(cWebPlugin * a_Plugin); + /** Loads the login template into m_LoginPage. + Returns true if the loading succeeds, false if not. */ + bool LoadLoginPage(void); - // TODO: Convert this to the auto-locking callback mechanism used for looping players in worlds and such - PluginList GetPlugins() const { return m_Plugins; } // >> EXPORTED IN MANUALBINDINGS << + /** Returns a copy of all the registered web tabs. + Exported to Lua in ManualBindings.cpp. */ + cWebTabPtrs GetAllWebTabs(void) { return m_WebTabs; } - // tolua_begin + /** Removes all WebTabs registered by the specified plugin. */ + void RemoveAllPluginWebTabs(const AString & a_PluginName); + /** Returns the (inner) page contents for the specified request. + Calls the appropriate WebTab handler to get the contents. + Exported to Lua in ManualBindings.cpp. */ sWebAdminPage GetPage(const HTTPRequest & a_Request); - /** Returns the contents of the default page - the list of plugins and players */ - AString GetDefaultPage(void); + // tolua_begin - /** Returns the prefix needed for making a link point to the webadmin root from the given URL ("../../../webadmin"-style) */ - AString GetBaseURL(const AString & a_URL); + /** Reloads m_IniFile, m_LoginPage and m_TemplateScript. + Note that reloading will not change the "enabled" state of the server, and it will not update listening ports. */ + void Reload(void); - /** Returns the list of ports used for the webadmin. */ + /** Returns the list of ports on which the webadmin is configured to listen. */ AString GetPorts(void) const { return StringsConcat(m_Ports, ','); } - - /** OBSOLETE: Returns the list of IPv4 ports used for the webadmin. - Currently there is no distinction between IPv4 and IPv6; use GetPorts() instead. */ - AString GetIPv4Ports(void) const { return GetPorts(); } - - /** OBSOLETE: Returns the list of IPv6 ports used for the webadmin. - Currently there is no distinction between IPv4 and IPv6; use GetPorts() instead. */ - AString GetIPv6Ports(void) const { return GetPorts(); } - // tolua_end - /** Escapes text passed into it, so it can be embedded into html. */ + /** Adds a new WebTab handler. + a_Title is the display title of the tab + a_UrlPath is the part of the URL that uniquely identifies this tab. + a_PluginName is the display name of the plugin creating this tab. + a_Callback is used to provide the actual WebTab contents, when requested. + Exported in ManualBindings.cpp. */ + void AddWebTab( + const AString & a_Title, + const AString & a_UrlPath, + const AString & a_PluginName, + SharedPtr<cWebTabCallback> a_Callback + ); + + /** Removes the WebTab with the specified URL path. + Returns true if WebTab was found and removed, false if not found. + Exported in ManualBindings.cpp */ + bool DelWebTab(const AString & a_UrlPath); + + /** Escapes text passed into it, so it can be embedded into html. + Exported to Lua in ManualBindings.cpp. */ static AString GetHTMLEscapedString(const AString & a_Input); - /** Escapes the string for use in an URL */ + /** Escapes the string for use in an URL + Exported to Lua in ManualBindings.cpp. */ static AString GetURLEncodedString(const AString & a_Input); + /** Returns the prefix needed for making a link point to the webadmin root from the given URL ("../../../webadmin"-style). + Exported to Lua in ManualBindings.cpp. */ + static AString GetBaseURL(const AString & a_URL); + /** Returns the prefix needed for making a link point to the webadmin root from the given URL ("../../../webadmin"-style) */ static AString GetBaseURL(const AStringVector & a_URLSplit); - /** Returns the content type from the file extension. If the extension isn't in the list, the function returns "text/html" */ + /** Returns the content type from the file extension. + If the extension isn't in the list, the function returns an empty string. + Exported to Lua in ManualBindings.cpp. */ static AString GetContentTypeFromFileExt(const AString & a_FileExtension); protected: + /** Protects m_WebTabs, m_TemplateScript, m_LoginTemplate and m_IniFile against multithreaded access. */ + cCriticalSection m_CS; + + /** All registered WebTab handlers. + Protected against multithreaded access by m_CS. */ + cWebTabPtrs m_WebTabs; + + /** The Lua template script to provide templates. + Protected against multithreaded access by m_CS. */ + cLuaState m_TemplateScript; + + /** The HTML page that provides the login. + Protected against multithreaded access by m_CS. */ + AString m_LoginPage; + + /** The webadmin.ini file, used for the settings and allowed logins. + Protected against multithreaded access by m_CS. */ + cIniFile m_IniFile; + /** Set to true if Init() succeeds and the webadmin isn't to be disabled */ bool m_IsInitialized; /** Set to true if Start() succeeds in starting the server, reset back to false in Stop(). */ bool m_IsRunning; - /** The webadmin.ini file, used for the settings and allowed logins */ - cIniFile m_IniFile; - - PluginList m_Plugins; - /** The ports on which the webadmin is running. */ AStringVector m_Ports; - /** The Lua template script to provide templates: */ - cLuaState m_TemplateScript; - - /** The template that provides the login site: */ - AString m_LoginTemplate; - /** The HTTP server which provides the underlying HTTP parsing, serialization and events */ cHTTPServer m_HTTPServer; + + /** Loads webadmin.ini into m_IniFile. + Creates a default file if it doesn't exist. + Returns true if webadmin is enabled, false if disabled. */ + bool LoadIniFile(void); + /** Handles requests coming to the "/webadmin" or "/~webadmin" URLs */ void HandleWebadminRequest(cHTTPServerConnection & a_Connection, cHTTPIncomingRequest & a_Request); |