From 2eb1240e14986e1cccc02a96713133a003347f8b Mon Sep 17 00:00:00 2001 From: "madmaxoft@gmail.com" Date: Thu, 30 May 2013 20:40:43 +0000 Subject: Added the OnClosing callback to cLuaWindow API git-svn-id: http://mc-server.googlecode.com/svn/trunk@1534 0a769ca7-a7f5-676a-18bf-c427514a06d6 --- MCServer/Plugins/Debuggers/Debuggers.lua | 9 ++++++ source/LuaWindow.cpp | 48 ++++++++++++++++++++++++++++++-- source/LuaWindow.h | 13 +++++++++ source/ManualBindings.cpp | 39 ++++++++++++++++++++++++++ source/Player.cpp | 21 ++++++++++---- source/Plugin_NewLua.cpp | 24 ++++++++++++++++ source/Plugin_NewLua.h | 7 +++++ source/UI/Window.cpp | 4 ++- source/UI/Window.h | 4 ++- 9 files changed, 160 insertions(+), 9 deletions(-) diff --git a/MCServer/Plugins/Debuggers/Debuggers.lua b/MCServer/Plugins/Debuggers/Debuggers.lua index 076cec9ca..484ea88be 100644 --- a/MCServer/Plugins/Debuggers/Debuggers.lua +++ b/MCServer/Plugins/Debuggers/Debuggers.lua @@ -580,8 +580,17 @@ function HandleTestWndCmd(a_Split, a_Player) return true; end + -- Test out the OnClosing callback's ability to refuse to close the window + local attempt = 1; + local OnClosing = function(Window, Player) + Player:SendMessage("Window closing attempt #" .. attempt); + attempt = attempt + 1; + return (attempt <= 3); -- refuse twice, then allow + end + local Window = cLuaWindow(WindowType, WindowSizeX, WindowSizeY, "TestWnd"); Window:SetSlot(a_Player, 0, cItem(E_ITEM_DIAMOND, 64)); + Window:SetOnClosing(OnClosing); a_Player:OpenWindow(Window); diff --git a/source/LuaWindow.cpp b/source/LuaWindow.cpp index d7b67d552..5ed521793 100644 --- a/source/LuaWindow.cpp +++ b/source/LuaWindow.cpp @@ -7,6 +7,7 @@ #include "LuaWindow.h" #include "UI/SlotArea.h" #include "Plugin_NewLua.h" +#include "Player.h" #include "lauxlib.h" // Needed for LUA_REFNIL @@ -20,7 +21,9 @@ cLuaWindow::cLuaWindow(cWindow::WindowType a_WindowType, int a_SlotsX, int a_Slo super(a_WindowType, a_Title), m_Contents(a_SlotsX, a_SlotsY), m_Plugin(NULL), - m_LuaRef(LUA_REFNIL) + m_LuaRef(LUA_REFNIL), + m_OnClosingFnRef(LUA_REFNIL), + m_OnSlotChangedFnRef(LUA_REFNIL) { m_SlotAreas.push_back(new cSlotAreaItemGrid(m_Contents, *this)); @@ -53,7 +56,8 @@ cLuaWindow::~cLuaWindow() void cLuaWindow::SetLuaRef(cPlugin_NewLua * a_Plugin, int a_LuaRef) { - ASSERT(m_Plugin == NULL); + // Either m_Plugin is not set or equal to the passed plugin; only one plugin can use one cLuaWindow object + ASSERT((m_Plugin == NULL) || (m_Plugin == a_Plugin)); ASSERT(m_LuaRef == LUA_REFNIL); m_Plugin = a_Plugin; m_LuaRef = a_LuaRef; @@ -72,6 +76,46 @@ bool cLuaWindow::IsLuaReferenced(void) const +void cLuaWindow::SetOnClosing(cPlugin_NewLua * a_Plugin, int a_FnRef) +{ + // Either m_Plugin is not set or equal to the passed plugin; only one plugin can use one cLuaWindow object + ASSERT((m_Plugin == NULL) || (m_Plugin == a_Plugin)); + + // 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; +} + + + + + +bool cLuaWindow::ClosedByPlayer(cPlayer & a_Player) +{ + // First notify the plugin through the registered callback: + if (m_OnClosingFnRef != LUA_REFNIL) + { + ASSERT(m_Plugin != NULL); + if (m_Plugin->CallbackWindowClosing(m_OnClosingFnRef, *this, a_Player)) + { + // The callback disagrees + return false; + } + } + + return super::ClosedByPlayer(a_Player); +} + + + + + void cLuaWindow::Destroy(void) { super::Destroy(); diff --git a/source/LuaWindow.h b/source/LuaWindow.h index c474fa1ab..30c07bdbf 100644 --- a/source/LuaWindow.h +++ b/source/LuaWindow.h @@ -58,6 +58,12 @@ public: /// 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(cPlugin_NewLua * a_Plugin, int a_FnRef); + + /// Sets the callback function (Lua reference) to call when a slot is changed + void SetOnSlotChanged(cPlugin_NewLua * a_Plugin, int a_FnRef); + protected: /// Contents of the non-inventory part cItemGrid m_Contents; @@ -68,7 +74,14 @@ protected: /// The Lua object reference, used for keeping the object alive as long as any player has the window open int m_LuaRef; + /// The Lua reference for the callback to call when the window is closing for any player + int m_OnClosingFnRef; + + /// The Lua reference for the callback to call when a slot has changed + int m_OnSlotChangedFnRef; + // cWindow overrides: + virtual bool ClosedByPlayer(cPlayer & a_Player) override; virtual void Destroy(void) override; } ; // tolua_export diff --git a/source/ManualBindings.cpp b/source/ManualBindings.cpp index ceb24da30..96cd14370 100644 --- a/source/ManualBindings.cpp +++ b/source/ManualBindings.cpp @@ -938,6 +938,41 @@ static int tolua_cPlayer_OpenWindow(lua_State * tolua_S) +static int tolua_cLuaWindow_SetOnClosing(lua_State * tolua_S) +{ + // Function signature: cPlayer:SetOnClosing(CallbackFunction) + + // Retrieve the plugin instance from the Lua state + cPlugin_NewLua * Plugin = GetLuaPlugin(tolua_S); + if (Plugin == NULL) + { + // Warning message has already been printed by GetLuaPlugin(), bail out silently + return 0; + } + + // Get the parameters: + cLuaWindow * self = (cLuaWindow *)tolua_tousertype(tolua_S, 1, NULL); + if (self == NULL) + { + LOGWARNING("%s: invalid self (%p)", __FUNCTION__, self); + return 0; + } + int FnRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); // Store function reference + if (FnRef == LUA_REFNIL) + { + LOGERROR("%s: Cannot create a function reference. Callback not set.", __FUNCTION__); + return 0; + } + + // Set the callback + self->SetOnClosing(Plugin, FnRef); + return 0; +} + + + + + static int tolua_cPlugin_NewLua_AddWebTab(lua_State * tolua_S) { cPlugin_NewLua * self = (cPlugin_NewLua*)tolua_tousertype(tolua_S,1,0); @@ -1265,6 +1300,10 @@ void ManualBindings::Bind( lua_State* tolua_S ) tolua_function(tolua_S, "OpenWindow", tolua_cPlayer_OpenWindow); tolua_endmodule(tolua_S); + tolua_beginmodule(tolua_S, "cLuaWindow"); + tolua_function(tolua_S, "SetOnClosing", tolua_cLuaWindow_SetOnClosing); + tolua_endmodule(tolua_S); + tolua_beginmodule(tolua_S, "cPlugin_NewLua"); tolua_function(tolua_S, "AddWebTab", tolua_cPlugin_NewLua_AddWebTab); tolua_function(tolua_S, "AddTab", tolua_cPlugin_NewLua_AddTab); diff --git a/source/Player.cpp b/source/Player.cpp index 8970719f1..519837f6a 100644 --- a/source/Player.cpp +++ b/source/Player.cpp @@ -435,7 +435,10 @@ Vector3d cPlayer::GetEyePosition() void cPlayer::OpenWindow(cWindow * a_Window) { - CloseWindow(); + if (a_Window != m_CurrentWindow) + { + CloseWindow(); + } a_Window->OpenedByPlayer(*this); m_CurrentWindow = a_Window; a_Window->SendWholeWindow(*GetClientHandle()); @@ -453,8 +456,17 @@ void cPlayer::CloseWindow(void) return; } - m_CurrentWindow->ClosedByPlayer(*this); - m_CurrentWindow = m_InventoryWindow; + if (m_CurrentWindow->ClosedByPlayer(*this)) + { + // Close accepted, go back to inventory window (the default): + m_CurrentWindow = m_InventoryWindow; + } + else + { + // Re-open the window + m_CurrentWindow->OpenedByPlayer(*this); + m_CurrentWindow->SendWholeWindow(*GetClientHandle()); + } } @@ -467,8 +479,7 @@ void cPlayer::CloseWindowIfID(char a_WindowID) { return; } - m_CurrentWindow->ClosedByPlayer(*this); - m_CurrentWindow = m_InventoryWindow; + CloseWindow(); } diff --git a/source/Plugin_NewLua.cpp b/source/Plugin_NewLua.cpp index df18cfa9b..dd3db7d6a 100644 --- a/source/Plugin_NewLua.cpp +++ b/source/Plugin_NewLua.cpp @@ -1665,6 +1665,30 @@ void cPlugin_NewLua::Unreference(int a_LuaRef) +bool cPlugin_NewLua::CallbackWindowClosing(int a_FnRef, cWindow & a_Window, cPlayer & a_Player) +{ + cCSLock Lock(m_CriticalSection); + lua_rawgeti(m_LuaState, LUA_REGISTRYINDEX, a_FnRef); // Push the function to be called + tolua_pushusertype(m_LuaState, &a_Window, "cWindow"); + tolua_pushusertype(m_LuaState, &a_Player, "cPlayer"); + + // Call function: + int s = lua_pcall(m_LuaState, 2, 1, 0); + if (report_errors(m_LuaState, s)) + { + LOGERROR("LUA error in %s. Stack size: %i", __FUNCTION__, lua_gettop(m_LuaState)); + return false; + } + + bool bRetVal = (tolua_toboolean(m_LuaState, -1, false) > 0); + lua_pop(m_LuaState, 1); + return bRetVal; +} + + + + + // Helper functions bool cPlugin_NewLua::PushFunction(const char * a_FunctionName, bool a_bLogError /* = true */) { diff --git a/source/Plugin_NewLua.h b/source/Plugin_NewLua.h index acc43c7a4..fc3565bbc 100644 --- a/source/Plugin_NewLua.h +++ b/source/Plugin_NewLua.h @@ -11,8 +11,12 @@ +// fwd: Lua typedef struct lua_State lua_State; +// fwd: UI/Window.h +class cWindow; + @@ -101,6 +105,9 @@ public: /// Removes a previously referenced object (luaL_unref()) void Unreference(int a_LuaRef); + /// Calls the plugin-specified "cLuaWindow closing" callback. Returns true only if the callback returned true + bool CallbackWindowClosing(int a_FnRef, cWindow & a_Window, cPlayer & a_Player); + protected: cCriticalSection m_CriticalSection; lua_State * m_LuaState; diff --git a/source/UI/Window.cpp b/source/UI/Window.cpp index 661f9f62f..f353d0721 100644 --- a/source/UI/Window.cpp +++ b/source/UI/Window.cpp @@ -251,7 +251,7 @@ void cWindow::OpenedByPlayer(cPlayer & a_Player) -void cWindow::ClosedByPlayer(cPlayer & a_Player) +bool cWindow::ClosedByPlayer(cPlayer & a_Player) { // Checks whether the player is still holding an item if (a_Player.IsDraggingItem()) @@ -285,6 +285,8 @@ void cWindow::ClosedByPlayer(cPlayer & a_Player) { delete this; } + + return true; } diff --git a/source/UI/Window.h b/source/UI/Window.h index 1f2495a46..68774aa59 100644 --- a/source/UI/Window.h +++ b/source/UI/Window.h @@ -106,7 +106,9 @@ public: ); void OpenedByPlayer(cPlayer & a_Player); - void ClosedByPlayer(cPlayer & a_Player); + + /// Called when a player closes this window; notifies all slot areas. Returns true if close accepted + virtual bool ClosedByPlayer(cPlayer & a_Player); void SendWholeWindow(cClientHandle & a_Client); void BroadcastWholeWindow(void); -- cgit v1.2.3