diff options
-rw-r--r-- | Server/Plugins/APIDump/APIDesc.lua | 32 | ||||
-rw-r--r-- | Server/Plugins/APIDump/Classes/Plugins.lua | 4 | ||||
-rw-r--r-- | Server/Plugins/APIDump/Hooks/OnPlayerOpeningWindow.lua | 20 | ||||
-rw-r--r-- | src/Bindings/LuaState.cpp | 11 | ||||
-rw-r--r-- | src/Bindings/LuaState.h | 1 | ||||
-rw-r--r-- | src/Bindings/LuaWindow.cpp | 36 | ||||
-rw-r--r-- | src/Bindings/LuaWindow.h | 12 | ||||
-rw-r--r-- | src/Bindings/ManualBindings.cpp | 1 | ||||
-rw-r--r-- | src/Bindings/Plugin.h | 1 | ||||
-rw-r--r-- | src/Bindings/PluginLua.cpp | 10 | ||||
-rw-r--r-- | src/Bindings/PluginLua.h | 1 | ||||
-rw-r--r-- | src/Bindings/PluginManager.cpp | 19 | ||||
-rw-r--r-- | src/Bindings/PluginManager.h | 3 | ||||
-rw-r--r-- | src/Entities/Player.cpp | 6 | ||||
-rw-r--r-- | src/UI/Window.h | 2 |
15 files changed, 156 insertions, 3 deletions
diff --git a/Server/Plugins/APIDump/APIDesc.lua b/Server/Plugins/APIDump/APIDesc.lua index bcc9b8ec4..38340d0f3 100644 --- a/Server/Plugins/APIDump/APIDesc.lua +++ b/Server/Plugins/APIDump/APIDesc.lua @@ -8029,6 +8029,17 @@ This class is used by plugins wishing to display a custom window to the player, }, Notes = "Returns the cItemGrid object representing the internal storage in this window", }, + SetOnClicked = + { + Params = + { + { + Name = "OnClickedCallback", + Type = "function", + }, + }, + Notes = "Sets the function that the window will call when it is about to process a click from a player. See {{#additionalinfo_1|below}} for the signature of the callback function.", + }, SetOnClosing = { Params = @@ -8061,6 +8072,17 @@ This class is used by plugins wishing to display a custom window to the player, ]], }, { + Header = "OnClicked Callback", + Contents = [[ + This callback, settable via the SetOnClicked() function, will be called when the player clicks a slot in the window. The callback can cancel the click.</p> +<pre class="prettyprint lang-lua"> +function OnWindowClicked(a_Window, a_Player, a_SlotNum, a_ClickAction, a_ClickedItem) +</pre> + <p> + The a_Window parameter is the cLuaWindow object representing the window, a_Player is the player who made the click, a_SlotNum is the slot the player clicked, a_ClickAction is the type of click the player made, and a_ClickedItem is the item the player clicked on, if applicable. If the function returns true, the click is cancelled (internally, the server resends the window slots to the player to keep the player in sync). + ]], + }, + { Header = "OnClosing Callback", Contents = [[ This callback, settable via the SetOnClosing() function, will be called when the player tries to close the window, or the window is closed for any other reason (such as a player disconnecting).</p> @@ -8086,7 +8108,7 @@ function OnWindowSlotChanged(a_Window, a_SlotNum) { Header = "Example", Contents = [[ - This example is taken from the Debuggers plugin, used to test the API functionality. It opens a window and refuse to close it 3 times. It also logs slot changes to the server console. + This example is taken from the Debuggers plugin, used to test the API functionality. It opens a window and refuse to close it 3 times. It also logs slot changes to the server console and prevents shift-clicking in the window. <pre class="prettyprint lang-lua"> -- Callback that refuses to close the window twice, then allows: local Attempt = 1; @@ -8101,10 +8123,18 @@ local OnSlotChanged = function(Window, SlotNum) LOG("Window \"" .. Window:GetWindowTitle() .. "\" slot " .. SlotNum .. " changed."); end +-- Prevent shift-clicking: +local OnClicked = function(Window, ClickingPlayer, SlotNum, ClickAction, ClickedItem) + if ClickAction == caShiftLeftClick then + return true + end +end + -- Set window contents: -- a_Player is a cPlayer object received from the outside of this code fragment local Window = cLuaWindow(cWindow.wtHopper, 3, 3, "TestWnd"); Window:SetSlot(a_Player, 0, cItem(E_ITEM_DIAMOND, 64)); +Window:SetOnClicked(OnClicked); Window:SetOnClosing(OnClosing); Window:SetOnSlotChanged(OnSlotChanged); diff --git a/Server/Plugins/APIDump/Classes/Plugins.lua b/Server/Plugins/APIDump/Classes/Plugins.lua index c00735412..e22f4e3a0 100644 --- a/Server/Plugins/APIDump/Classes/Plugins.lua +++ b/Server/Plugins/APIDump/Classes/Plugins.lua @@ -840,6 +840,10 @@ cPluginManager.AddHook(cPluginManager.HOOK_CHAT, OnChatMessage); { Notes = "Called when the player has moved and the movement is now being applied.", }, + HOOK_PLAYER_OPENING_WINDOW = + { + Notes = "Called when the player is about to open a window. The plugin can return true to cancel the window opening.", + }, HOOK_PLAYER_PLACED_BLOCK = { Notes = "Called when the player has just placed a block", diff --git a/Server/Plugins/APIDump/Hooks/OnPlayerOpeningWindow.lua b/Server/Plugins/APIDump/Hooks/OnPlayerOpeningWindow.lua new file mode 100644 index 000000000..04563df89 --- /dev/null +++ b/Server/Plugins/APIDump/Hooks/OnPlayerOpeningWindow.lua @@ -0,0 +1,20 @@ +return +{ + HOOK_PLAYER_OPENING_WINDOW = + { + CalledWhen = "Called when a player is about to open a window", + DefaultFnName = "OnPlayerOpeningWindow", -- also used as pagename + Desc = [[ + This hook is called when a player is about to open a window, e.g. when they click on a chest or a furnace. + ]], + Params = + { + { Name = "Player", Type = "{{cPlayer}}", Notes = "The player who is opening the window" }, + { Name = "Window", Type = "{{cWindow}}", Notes = "The window that is being opened" }, + }, + Returns = [[ + If the function returns false or no value, the next plugin's callback is called, and finally + Cuberite will process the opening window. If the function returns true, no other callback is called for this event. + ]], + }, -- HOOK_PLAYER_OPENING_WINDOW +} diff --git a/src/Bindings/LuaState.cpp b/src/Bindings/LuaState.cpp index e30d0ed5f..07a91f49e 100644 --- a/src/Bindings/LuaState.cpp +++ b/src/Bindings/LuaState.cpp @@ -875,6 +875,17 @@ void cLuaState::Push(const char * a_Value) +void cLuaState::Push(const cItem & a_Item) +{ + ASSERT(IsValid()); + auto c = new cItem(a_Item); + tolua_pushusertype_and_takeownership(m_LuaState, c, "cItem"); +} + + + + + void cLuaState::Push(const cNil & a_Nil) { ASSERT(IsValid()); diff --git a/src/Bindings/LuaState.h b/src/Bindings/LuaState.h index 3d5b1e645..1d2598813 100644 --- a/src/Bindings/LuaState.h +++ b/src/Bindings/LuaState.h @@ -614,6 +614,7 @@ public: void Push(const AStringMap & a_Dictionary); void Push(const AStringVector & a_Vector); void Push(const char * a_Value); + void Push(const cItem & a_Item); void Push(const cNil & a_Nil); void Push(const cRef & a_Ref); void Push(const Vector3d & a_Vector); diff --git a/src/Bindings/LuaWindow.cpp b/src/Bindings/LuaWindow.cpp index fd714390e..2802c69db 100644 --- a/src/Bindings/LuaWindow.cpp +++ b/src/Bindings/LuaWindow.cpp @@ -9,7 +9,7 @@ #include "PluginLua.h" #include "Root.h" #include "lua/src/lauxlib.h" // Needed for LUA_REFNIL - +#include "ClientHandle.h" @@ -86,6 +86,19 @@ cLuaWindow::~cLuaWindow() +void cLuaWindow::SetOnClicked(cLuaState::cCallbackPtr && a_OnClicked) +{ + // Only one Lua state can be a cLuaWindow object callback: + ASSERT(a_OnClicked->IsSameLuaState(*m_LuaState)); + + // Store the new reference, releasing the old one if appropriate: + m_OnClicked = std::move(a_OnClicked); +} + + + + + void cLuaWindow::SetOnClosing(cLuaState::cCallbackPtr && a_OnClosing) { // Only one Lua state can be a cLuaWindow object callback: @@ -206,3 +219,24 @@ void cLuaWindow::OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) + +void cLuaWindow::Clicked(cPlayer & a_Player, int a_WindowID, short a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem) +{ + if (m_OnClicked != nullptr) + { + // Plugin can stop a click + if (m_OnClicked->Call(this, &a_Player, a_SlotNum, a_ClickAction, a_ClickedItem)) + { + // Tell the client the actual state of the window + a_Player.GetClientHandle()->SendInventorySlot(-1, -1, a_Player.GetDraggingItem()); + BroadcastWholeWindow(); + return; + } + } + + cWindow::Clicked(a_Player, a_WindowID, a_SlotNum, a_ClickAction, a_ClickedItem); +} + + + + diff --git a/src/Bindings/LuaWindow.h b/src/Bindings/LuaWindow.h index fb21c1c4e..8f3349d00 100644 --- a/src/Bindings/LuaWindow.h +++ b/src/Bindings/LuaWindow.h @@ -49,6 +49,10 @@ public: // tolua_end + /** Sets the Lua callback to call when the player clicks on the window. + The window can stop the click from propogating. */ + void SetOnClicked(cLuaState::cCallbackPtr && a_OnClicked); + /** Sets the Lua callback function to call when the window is about to close */ void SetOnClosing(cLuaState::cCallbackPtr && a_OnClosing); @@ -63,6 +67,9 @@ protected: /** The canon Lua state that has opened the window and owns the m_LuaRef */ cLuaState * m_LuaState; + /** The Lua callback to call when the player clicked on a slot */ + cLuaState::cCallbackPtr m_OnClicked; + /** The Lua callback to call when the window is closing for any player */ cLuaState::cCallbackPtr m_OnClosing; @@ -80,6 +87,11 @@ protected: // cWindow overrides: virtual void OpenedByPlayer(cPlayer & a_Player) override; + virtual void Clicked( + cPlayer & a_Player, int a_WindowID, + short a_SlotNum, eClickAction a_ClickAction, + const cItem & a_ClickedItem + ) 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 e4410dd14..2251c64b9 100644 --- a/src/Bindings/ManualBindings.cpp +++ b/src/Bindings/ManualBindings.cpp @@ -3816,6 +3816,7 @@ void cManualBindings::Bind(lua_State * tolua_S) 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, "SetOnClicked", tolua_SetObjectCallback<cLuaWindow, &cLuaWindow::SetOnClicked>); tolua_function(tolua_S, "SetOnClosing", tolua_SetObjectCallback<cLuaWindow, &cLuaWindow::SetOnClosing>); tolua_function(tolua_S, "SetOnSlotChanged", tolua_SetObjectCallback<cLuaWindow, &cLuaWindow::SetOnSlotChanged>); tolua_endmodule(tolua_S); diff --git a/src/Bindings/Plugin.h b/src/Bindings/Plugin.h index 3276fde67..22e8f15e2 100644 --- a/src/Bindings/Plugin.h +++ b/src/Bindings/Plugin.h @@ -80,6 +80,7 @@ public: virtual bool OnPlayerJoined (cPlayer & a_Player) = 0; virtual bool OnPlayerLeftClick (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status) = 0; virtual bool OnPlayerMoving (cPlayer & a_Player, const Vector3d & a_OldPosition, const Vector3d & a_NewPosition) = 0; + virtual bool OnPlayerOpeningWindow(cPlayer & a_Player, cWindow & a_Window) = 0; virtual bool OnPlayerPlacedBlock (cPlayer & a_Player, const sSetBlock & a_BlockChange) = 0; virtual bool OnPlayerPlacingBlock (cPlayer & a_Player, const sSetBlock & a_BlockChange) = 0; virtual bool 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) = 0; diff --git a/src/Bindings/PluginLua.cpp b/src/Bindings/PluginLua.cpp index e3aa63aa1..5af336a95 100644 --- a/src/Bindings/PluginLua.cpp +++ b/src/Bindings/PluginLua.cpp @@ -659,6 +659,15 @@ bool cPluginLua::OnEntityTeleport(cEntity & a_Entity, const Vector3d & a_OldPosi +bool cPluginLua::OnPlayerOpeningWindow(cPlayer & a_Player, cWindow & a_Window) +{ + return CallSimpleHooks(cPluginManager::HOOK_PLAYER_OPENING_WINDOW, &a_Player, &a_Window); +} + + + + + bool cPluginLua::OnPlayerPlacedBlock(cPlayer & a_Player, const sSetBlock & a_BlockChange) { return CallSimpleHooks(cPluginManager::HOOK_PLAYER_PLACED_BLOCK, @@ -1056,6 +1065,7 @@ const char * cPluginLua::GetHookFnName(int a_HookType) case cPluginManager::HOOK_PLAYER_JOINED: return "OnPlayerJoined"; case cPluginManager::HOOK_PLAYER_LEFT_CLICK: return "OnPlayerLeftClick"; case cPluginManager::HOOK_PLAYER_MOVING: return "OnPlayerMoving"; + case cPluginManager::HOOK_PLAYER_OPENING_WINDOW: return "OnPlayerOpeningWindow"; case cPluginManager::HOOK_PLAYER_PLACED_BLOCK: return "OnPlayerPlacedBlock"; case cPluginManager::HOOK_PLAYER_PLACING_BLOCK: return "OnPlayerPlacingBlock"; case cPluginManager::HOOK_PLAYER_RIGHT_CLICK: return "OnPlayerRightClick"; diff --git a/src/Bindings/PluginLua.h b/src/Bindings/PluginLua.h index ff5e8d726..4de5751e7 100644 --- a/src/Bindings/PluginLua.h +++ b/src/Bindings/PluginLua.h @@ -101,6 +101,7 @@ public: virtual bool OnPlayerJoined (cPlayer & a_Player) override; virtual bool OnPlayerLeftClick (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status) override; virtual bool OnPlayerMoving (cPlayer & a_Player, const Vector3d & a_OldPosition, const Vector3d & a_NewPosition) override; + virtual bool OnPlayerOpeningWindow(cPlayer & a_Player, cWindow & a_Window) override; virtual bool OnPlayerPlacedBlock (cPlayer & a_Player, const sSetBlock & a_BlockChange) override; virtual bool OnPlayerPlacingBlock (cPlayer & a_Player, const sSetBlock & a_BlockChange) override; virtual bool 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) override; diff --git a/src/Bindings/PluginManager.cpp b/src/Bindings/PluginManager.cpp index 066ccf9d7..1d977fcde 100644 --- a/src/Bindings/PluginManager.cpp +++ b/src/Bindings/PluginManager.cpp @@ -999,6 +999,25 @@ bool cPluginManager::CallHookPlayerMoving(cPlayer & a_Player, const Vector3d & a +bool cPluginManager::CallHookPlayerOpeningWindow(cPlayer & a_Player, cWindow & a_Window) +{ + FIND_HOOK(HOOK_PLAYER_OPENING_WINDOW); + VERIFY_HOOK; + + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnPlayerOpeningWindow(a_Player, a_Window)) + { + return true; + } + } + return false; +} + + + + + bool cPluginManager::CallHookPlayerPlacedBlock(cPlayer & a_Player, const sSetBlock & a_BlockChange) { FIND_HOOK(HOOK_PLAYER_PLACED_BLOCK); diff --git a/src/Bindings/PluginManager.h b/src/Bindings/PluginManager.h index f38ac8fa1..f3fc3551a 100644 --- a/src/Bindings/PluginManager.h +++ b/src/Bindings/PluginManager.h @@ -24,6 +24,7 @@ class cPickup; class cPlayer; class cPlugin; class cProjectileEntity; +class cWindow; class cWorld; class cSettingsRepositoryInterface; class cDeadlockDetect; @@ -111,6 +112,7 @@ public: HOOK_PLAYER_JOINED, HOOK_PLAYER_LEFT_CLICK, HOOK_PLAYER_MOVING, + HOOK_PLAYER_OPENING_WINDOW, HOOK_PLAYER_PLACED_BLOCK, HOOK_PLAYER_PLACING_BLOCK, HOOK_PLAYER_RIGHT_CLICK, @@ -257,6 +259,7 @@ public: bool CallHookPlayerJoined (cPlayer & a_Player); bool CallHookPlayerLeftClick (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status); bool CallHookPlayerMoving (cPlayer & a_Player, const Vector3d & a_OldPosition, const Vector3d & a_NewPosition); + bool CallHookPlayerOpeningWindow (cPlayer & a_Player, cWindow & a_Window); bool CallHookPlayerPlacedBlock (cPlayer & a_Player, const sSetBlock & a_BlockChange); bool CallHookPlayerPlacingBlock (cPlayer & a_Player, const sSetBlock & a_BlockChange); bool CallHookPlayerRightClick (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ); diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index 3bbe334fb..3e6d912dd 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -1321,10 +1321,16 @@ cTeam * cPlayer::UpdateTeam(void) void cPlayer::OpenWindow(cWindow & a_Window) { + if (cRoot::Get()->GetPluginManager()->CallHookPlayerOpeningWindow(*this, a_Window)) + { + return; + } + if (&a_Window != m_CurrentWindow) { CloseWindow(false); } + a_Window.OpenedByPlayer(*this); m_CurrentWindow = &a_Window; a_Window.SendWholeWindow(*GetClientHandle()); diff --git a/src/UI/Window.h b/src/UI/Window.h index 5bb90a75e..d7a29dc47 100644 --- a/src/UI/Window.h +++ b/src/UI/Window.h @@ -113,7 +113,7 @@ public: void GetSlots(cPlayer & a_Player, cItems & a_Slots) const; /** Handles a click event from a player */ - void Clicked( + virtual void Clicked( cPlayer & a_Player, int a_WindowID, short a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem |