1
0
Fork 0

Changed cLuaWindow callbacks to use cLuaState::cCallback.

This commit is contained in:
Mattes D 2016-06-10 21:30:07 +02:00
parent a473725121
commit fb4c3fc4d9
7 changed files with 245 additions and 185 deletions

View File

@ -180,6 +180,25 @@ bool cLuaState::cCallback::IsValid(void)
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);
@ -919,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;
@ -1748,16 +1779,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:");
@ -1783,6 +1814,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));
@ -1820,17 +1866,16 @@ int cLuaState::BreakIntoDebugger(lua_State * a_LuaState)
void cLuaState::TrackCallback(cCallback & a_Callback)
{
// Get the CanonLuaState global from Lua:
auto cb = WalkToNamedGlobal(g_CanonLuaStateGlobalName);
if (!cb.IsValid())
auto canonState = QueryCanonLuaState();
if (canonState == nullptr)
{
LOGWARNING("%s: Lua state %p has invalid CanonLuaState!", __FUNCTION__, reinterpret_cast<void *>(m_LuaState));
return;
}
auto & canonState = *reinterpret_cast<cLuaState *>(lua_touserdata(m_LuaState, -1));
// Add the callback:
cCSLock Lock(canonState.m_CSTrackedCallbacks);
canonState.m_TrackedCallbacks.push_back(&a_Callback);
cCSLock Lock(canonState->m_CSTrackedCallbacks);
canonState->m_TrackedCallbacks.push_back(&a_Callback);
}
@ -1840,17 +1885,16 @@ void cLuaState::TrackCallback(cCallback & a_Callback)
void cLuaState::UntrackCallback(cCallback & a_Callback)
{
// Get the CanonLuaState global from Lua:
auto cb = WalkToNamedGlobal(g_CanonLuaStateGlobalName);
if (!cb.IsValid())
auto canonState = QueryCanonLuaState();
if (canonState == nullptr)
{
LOGWARNING("%s: Lua state %p has invalid CanonLuaState!", __FUNCTION__, reinterpret_cast<void *>(m_LuaState));
return;
}
auto & canonState = *reinterpret_cast<cLuaState *>(lua_touserdata(m_LuaState, -1));
// Remove the callback:
cCSLock Lock(canonState.m_CSTrackedCallbacks);
auto & trackedCallbacks = canonState.m_TrackedCallbacks;
cCSLock Lock(canonState->m_CSTrackedCallbacks);
auto & trackedCallbacks = canonState->m_TrackedCallbacks;
trackedCallbacks.erase(std::remove_if(trackedCallbacks.begin(), trackedCallbacks.end(),
[&a_Callback](cCallback * a_StoredCallback)
{

View File

@ -83,6 +83,15 @@ public:
/** Returns the Lua state associated with the value. */
lua_State * GetLuaState(void) { return m_LuaState; }
/** Creates a Lua reference to the specified object instance in the specified Lua state.
This is useful to make anti-GC references for objects that were created by Lua and need to stay alive longer than Lua GC would normally guarantee. */
template <typename T> void CreateFromObject(cLuaState & a_LuaState, T && a_Object)
{
a_LuaState.Push(std::forward<T>(a_Object));
RefStack(a_LuaState, -1);
a_LuaState.Pop();
}
protected:
lua_State * m_LuaState;
int m_Ref;
@ -158,6 +167,10 @@ public:
/** 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;
@ -330,6 +343,9 @@ 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.
@ -511,10 +527,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:

View File

@ -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));
// Store the new reference, releasing the old one if appropriate:
m_OnClosing = a_OnClosing;
}
bool cLuaWindow::IsLuaReferenced(void) const
void cLuaWindow::SetOnSlotChanged(cLuaState::cCallbackPtr a_OnSlotChanged)
{
return ((m_Plugin != nullptr) && (m_LuaRef != LUA_REFNIL));
// Only one Lua state can be a cLuaWindow object callback:
ASSERT(a_OnSlotChanged->IsSameLuaState(*m_LuaState));
// Store the new reference, releasing the old one if appropriate:
m_OnSlotChanged = a_OnSlotChanged;
}
void cLuaWindow::SetOnClosing(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_OnClosingFnRef != 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_OnClosingFnRef);
m_LuaRef.CreateFromObject(*m_LuaState, this);
}
// Store the new reference
m_Plugin = a_Plugin;
m_OnClosingFnRef = a_FnRef;
}
void cLuaWindow::SetOnSlotChanged(cPluginLua * 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 == nullptr) || (m_Plugin == a_Plugin));
// If there already was a function, unreference it first
if (m_OnSlotChangedFnRef != LUA_REFNIL)
{
m_Plugin->Unreference(m_OnSlotChangedFnRef);
}
// 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);
}
}

View File

@ -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);
/** Sets the Lua callback function to call when the window is about to close */
void SetOnClosing(cLuaState::cCallbackPtr a_OnClosing);
/** 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 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 object reference, used for keeping the object alive as long as any player has the window open */
int m_LuaRef;
/** The Lua callback to call when the window is closing for any player */
cLuaState::cCallbackPtr m_OnClosing;
/** The Lua reference for the callback to call when the window is closing for any player */
int m_OnClosingFnRef;
/** The Lua callback to call when a slot has changed */
cLuaState::cCallbackPtr m_OnSlotChanged;
/** Number of players that are currently using the window.
Used to manager the m_LuaRef lifetime. */
std::atomic<int> m_PlayerCount;
/** 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;

View File

@ -1600,55 +1600,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
@ -1679,36 +1630,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)
cLuaState L(tolua_S);
OBJTYPE * self;
cLuaState::cCallbackPtr callback;
if (!L.GetStackValues(1, self, callback))
{
LOGWARNING("%s: invalid self (%p)", __FUNCTION__, static_cast<void *>(self));
return 0;
}
int FnRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); // Store function reference for later retrieval
if (FnRef == LUA_REFNIL)
{
LOGERROR("%s: Cannot create a function reference. Callback not set.", __FUNCTION__);
LOGWARNING("%s: Cannot get parameters", __FUNCTION__);
L.LogStackTrace();
return 0;
}
// Set the callback
(self->*SetCallback)(Plugin, FnRef);
(self->*SetCallback)(callback);
return 0;
}
@ -2800,6 +2740,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);
@ -3343,7 +3356,7 @@ static int tolua_cBoundingBox_CalcLineIntersection(lua_State * a_LuaState)
const cBoundingBox * bbox;
if (!L.GetStackValues(1, bbox, pt1, pt2)) // Try the regular signature
{
L.LogStack();
L.LogStackValues();
tolua_error(a_LuaState, "Invalid function params. Expected either bbox:CalcLineIntersection(pt1, pt2) or cBoundingBox:CalcLineIntersection(min, max, pt1, pt2).", nullptr);
return 0;
}
@ -3373,7 +3386,7 @@ static int tolua_cBoundingBox_Intersect(lua_State * a_LuaState)
const cBoundingBox * other;
if (!L.GetStackValues(1, self, other))
{
L.LogStack();
L.LogStackValues();
tolua_error(a_LuaState, "Invalid function params. Expected bbox:Intersect(otherBbox).", nullptr);
return 0;
}
@ -3746,6 +3759,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);
@ -3766,7 +3782,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);

View File

@ -224,11 +224,11 @@ public:
cWindow * GetWindow(void) { return m_CurrentWindow; } // tolua_export
const cWindow * GetWindow(void) const { return m_CurrentWindow; }
/** Opens the specified window; closes the current one first using CloseWindow() */
void OpenWindow(cWindow * a_Window); // Exported in ManualBindings.cpp
// tolua_begin
/** Opens the specified window; closes the current one first using CloseWindow() */
void OpenWindow(cWindow * a_Window);
/** Closes the current window, resets current window to m_InventoryWindow. A plugin may refuse the closing if a_CanRefuse is true */
void CloseWindow(bool a_CanRefuse = true);

View File

@ -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);
}