diff options
Diffstat (limited to 'src/Bindings')
-rw-r--r-- | src/Bindings/AllToLua.pkg | 81 | ||||
-rw-r--r-- | src/Bindings/LuaWindow.cpp | 18 | ||||
-rw-r--r-- | src/Bindings/LuaWindow.h | 7 | ||||
-rw-r--r-- | src/Bindings/ManualBindings.cpp | 89 | ||||
-rw-r--r-- | src/Bindings/ManualBindings_Network.cpp | 35 | ||||
-rw-r--r-- | src/Bindings/Plugin.h | 5 | ||||
-rw-r--r-- | src/Bindings/PluginLua.cpp | 112 | ||||
-rw-r--r-- | src/Bindings/PluginLua.h | 88 | ||||
-rw-r--r-- | src/Bindings/PluginManager.cpp | 24 | ||||
-rw-r--r-- | src/Bindings/PluginManager.h | 4 |
10 files changed, 331 insertions, 132 deletions
diff --git a/src/Bindings/AllToLua.pkg b/src/Bindings/AllToLua.pkg index 7e174e770..8f55eba07 100644 --- a/src/Bindings/AllToLua.pkg +++ b/src/Bindings/AllToLua.pkg @@ -1,4 +1,19 @@ +// AllToLua.pkg + +// Defines the bindings that are exported to Lua by the ToLua processor + +/* + Each file referenced in the $cfile is parsed by ToLua, and bindings are generated for classes and functions +marked with "// tolua_export", or between the "// tolua_begin" and "// tolua_end" markers. + + Note that if class D inherits from class B, then class B needs to be parsed before class D, otherwise the +inheritance doesn't work properly (#1789). +*/ + + + + $#include "../Globals.h" // Typedefs from Globals.h, so that we don't have to process that file: @@ -27,14 +42,38 @@ $cfile "WebPlugin.h" $cfile "LuaWindow.h" $cfile "../BlockID.h" -$cfile "../Mobs/MonsterTypes.h" $cfile "../BlockInfo.h" $cfile "../StringUtils.h" $cfile "../Defines.h" $cfile "../ChatColor.h" $cfile "../ClientHandle.h" -$cfile "../Entities/ArrowEntity.h" +$cfile "../Server.h" +$cfile "../World.h" +$cfile "../Inventory.h" +$cfile "../Enchantments.h" +$cfile "../Item.h" +$cfile "../ItemGrid.h" +$cfile "../WebAdmin.h" +$cfile "../Root.h" +$cfile "../Cuboid.h" +$cfile "../BoundingBox.h" +$cfile "../Tracer.h" +$cfile "../BlockArea.h" +$cfile "../Generating/ChunkDesc.h" +$cfile "../CraftingRecipes.h" +$cfile "../UI/Window.h" +$cfile "../CompositeChat.h" +$cfile "../Map.h" +$cfile "../MapManager.h" +$cfile "../Scoreboard.h" +$cfile "../Statistics.h" +$cfile "../Protocol/MojangAPI.h" + +// Entities: $cfile "../Entities/Entity.h" +$cfile "../Entities/Pawn.h" +$cfile "../Entities/ProjectileEntity.h" +$cfile "../Entities/ArrowEntity.h" $cfile "../Entities/EntityEffect.h" $cfile "../Entities/ExpBottleEntity.h" $cfile "../Entities/FireChargeEntity.h" @@ -43,25 +82,21 @@ $cfile "../Entities/Floater.h" $cfile "../Entities/GhastFireballEntity.h" $cfile "../Entities/HangingEntity.h" $cfile "../Entities/ItemFrame.h" -$cfile "../Entities/Pawn.h" $cfile "../Entities/Player.h" $cfile "../Entities/Painting.h" $cfile "../Entities/Pickup.h" -$cfile "../Entities/ProjectileEntity.h" $cfile "../Entities/SplashPotionEntity.h" $cfile "../Entities/ThrownEggEntity.h" $cfile "../Entities/ThrownEnderPearlEntity.h" $cfile "../Entities/ThrownSnowballEntity.h" $cfile "../Entities/TNTEntity.h" $cfile "../Entities/WitherSkullEntity.h" -$cfile "../Server.h" -$cfile "../World.h" -$cfile "../Inventory.h" -$cfile "../Enchantments.h" -$cfile "../Item.h" -$cfile "../ItemGrid.h" -$cfile "../BlockEntities/BeaconEntity.h" +$cfile "../Mobs/MonsterTypes.h" +$cfile "../Mobs/Monster.h" + +// Block entities: $cfile "../BlockEntities/BlockEntity.h" +$cfile "../BlockEntities/BeaconEntity.h" $cfile "../BlockEntities/BlockEntityWithItems.h" $cfile "../BlockEntities/ChestEntity.h" $cfile "../BlockEntities/CommandBlockEntity.h" @@ -76,30 +111,6 @@ $cfile "../BlockEntities/SignEntity.h" $cfile "../BlockEntities/MobHeadEntity.h" $cfile "../BlockEntities/MobSpawnerEntity.h" $cfile "../BlockEntities/FlowerPotEntity.h" -$cfile "../WebAdmin.h" -$cfile "../Root.h" -$cfile "../Cuboid.h" -$cfile "../BoundingBox.h" -$cfile "../Tracer.h" -$cfile "../BlockArea.h" -$cfile "../Generating/ChunkDesc.h" -$cfile "../CraftingRecipes.h" -$cfile "../UI/Window.h" -$cfile "../Mobs/Monster.h" -$cfile "../CompositeChat.h" -$cfile "../Map.h" -$cfile "../MapManager.h" -$cfile "../Scoreboard.h" -$cfile "../Statistics.h" -$cfile "../Protocol/MojangAPI.h" - - - - - -// Need to declare this class so that the usertype is properly registered in Bindings.cpp - -// it seems impossible to register a usertype in ManualBindings.cpp -class cLineBlockTracer; diff --git a/src/Bindings/LuaWindow.cpp b/src/Bindings/LuaWindow.cpp index 35730878d..d4014059b 100644 --- a/src/Bindings/LuaWindow.cpp +++ b/src/Bindings/LuaWindow.cpp @@ -167,6 +167,24 @@ void cLuaWindow::Destroy(void) +void cLuaWindow::DistributeStack(cItem & a_ItemStack, int a_Slot, cPlayer& a_Player, cSlotArea * a_ClickedArea, bool a_ShouldApply) +{ + cSlotAreas Areas; + for (auto Area : m_SlotAreas) + { + if (Area != a_ClickedArea) + { + Areas.push_back(Area); + } + } + + super::DistributeStackToAreas(a_ItemStack, a_Player, Areas, a_ShouldApply, false); +} + + + + + void cLuaWindow::OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) { if (a_ItemGrid != &m_Contents) diff --git a/src/Bindings/LuaWindow.h b/src/Bindings/LuaWindow.h index dab99a2e2..d4fc58660 100644 --- a/src/Bindings/LuaWindow.h +++ b/src/Bindings/LuaWindow.h @@ -35,8 +35,10 @@ This reference needs to be unreferenced in the Destroy() function. */ // tolua_begin class cLuaWindow : - public cWindow, - public cItemGrid::cListener + public cWindow + // tolua_end + , public cItemGrid::cListener + // tolua_begin { typedef cWindow super; @@ -84,6 +86,7 @@ protected: // cWindow overrides: 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; // cItemGrid::cListener overrides: virtual void OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) override; diff --git a/src/Bindings/ManualBindings.cpp b/src/Bindings/ManualBindings.cpp index a6ae4869b..cdc01ae09 100644 --- a/src/Bindings/ManualBindings.cpp +++ b/src/Bindings/ManualBindings.cpp @@ -266,6 +266,24 @@ static int tolua_StringSplit(lua_State * tolua_S) +static int tolua_StringSplitWithQuotes(lua_State * tolua_S) +{ + cLuaState S(tolua_S); + + AString str; + AString delim; + + S.GetStackValues(1, str, delim); + + AStringVector Split = StringSplitWithQuotes(str, delim); + S.Push(Split); + return 1; +} + + + + + static int tolua_StringSplitAndTrim(lua_State * tolua_S) { cLuaState LuaState(tolua_S); @@ -620,11 +638,11 @@ static int tolua_DoWithID(lua_State* tolua_S) private: virtual bool Item(Ty2 * a_Item) override { - lua_rawgeti(LuaState, LUA_REGISTRYINDEX, FuncRef); // Push function to call - tolua_pushusertype(LuaState, a_Item, Ty2::GetClassStatic()); // Push the item + lua_rawgeti(LuaState, LUA_REGISTRYINDEX, FuncRef); // Push function to call + tolua_pushusertype(LuaState, a_Item, a_Item->GetClass()); // Push the item if (TableRef != LUA_REFNIL) { - lua_rawgeti(LuaState, LUA_REGISTRYINDEX, TableRef); // Push the optional callbackdata param + lua_rawgeti(LuaState, LUA_REGISTRYINDEX, TableRef); // Push the optional callbackdata param } int s = lua_pcall(LuaState, (TableRef == LUA_REFNIL ? 1 : 2), 1, 0); @@ -1281,23 +1299,27 @@ tolua_lerror: class cLuaWorldTask : - public cWorld::cTask + public cWorld::cTask, + public cPluginLua::cResettable { public: cLuaWorldTask(cPluginLua & a_Plugin, int a_FnRef) : - m_Plugin(a_Plugin), + cPluginLua::cResettable(a_Plugin), m_FnRef(a_FnRef) { } protected: - cPluginLua & m_Plugin; int m_FnRef; // cWorld::cTask overrides: virtual void Run(cWorld & a_World) override { - m_Plugin.Call(m_FnRef, &a_World); + cCSLock Lock(m_CSPlugin); + if (m_Plugin != nullptr) + { + m_Plugin->Call(m_FnRef, &a_World); + } } } ; @@ -1336,7 +1358,9 @@ static int tolua_cWorld_QueueTask(lua_State * tolua_S) return lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get function reference of parameter #1"); } - self->QueueTask(make_unique<cLuaWorldTask>(*Plugin, FnRef)); + auto task = std::make_shared<cLuaWorldTask>(*Plugin, FnRef); + Plugin->AddResettable(task); + self->QueueTask(task); return 0; } @@ -1345,23 +1369,27 @@ static int tolua_cWorld_QueueTask(lua_State * tolua_S) class cLuaScheduledWorldTask : - public cWorld::cTask + public cWorld::cTask, + public cPluginLua::cResettable { public: cLuaScheduledWorldTask(cPluginLua & a_Plugin, int a_FnRef) : - m_Plugin(a_Plugin), + cPluginLua::cResettable(a_Plugin), m_FnRef(a_FnRef) { } protected: - cPluginLua & m_Plugin; int m_FnRef; // cWorld::cTask overrides: virtual void Run(cWorld & a_World) override { - m_Plugin.Call(m_FnRef, &a_World); + cCSLock Lock(m_CSPlugin); + if (m_Plugin != nullptr) + { + m_Plugin->Call(m_FnRef, &a_World); + } } }; @@ -1407,7 +1435,9 @@ static int tolua_cWorld_ScheduleTask(lua_State * tolua_S) int DelayTicks = (int)tolua_tonumber(tolua_S, 2, 0); - World->ScheduleTask(DelayTicks, new cLuaScheduledWorldTask(*Plugin, FnRef)); + auto task = std::make_shared<cLuaScheduledWorldTask>(*Plugin, FnRef); + Plugin->AddResettable(task); + World->ScheduleTask(DelayTicks, task); return 0; } @@ -2392,10 +2422,10 @@ static int tolua_md5HexString(lua_State * tolua_S) // Convert the md5 checksum to hex string: std::stringstream Output; - Output << std::hex << std::setw(2) << std::setfill('0'); + Output << std::hex << std::setfill('0'); for (size_t i = 0; i < ARRAYCOUNT(md5Output); i++) { - Output << static_cast<unsigned short>(md5Output[i]); // Need to cast to a number, otherwise a char is output + Output << std::setw(2) << static_cast<unsigned short>(md5Output[i]); // Need to cast to a number, otherwise a char is output } lua_pushlstring(tolua_S, Output.str().c_str(), Output.str().size()); return 1; @@ -2438,10 +2468,10 @@ static int tolua_sha1HexString(lua_State * tolua_S) // Convert the sha1 checksum to hex string: std::stringstream Output; - Output << std::hex << std::setw(2) << std::setfill('0'); + Output << std::hex << std::setfill('0'); for (size_t i = 0; i < ARRAYCOUNT(sha1Output); i++) { - Output << static_cast<unsigned short>(sha1Output[i]); // Need to cast to a number, otherwise a char is output + Output << std::setw(2) << static_cast<unsigned short>(sha1Output[i]); // Need to cast to a number, otherwise a char is output } lua_pushlstring(tolua_S, Output.str().c_str(), Output.str().size()); return 1; @@ -3659,19 +3689,22 @@ void ManualBindings::Bind(lua_State * tolua_S) tolua_cclass(tolua_S, "cCryptoHash", "cCryptoHash", "", nullptr); tolua_usertype(tolua_S, "cStringCompression"); tolua_cclass(tolua_S, "cStringCompression", "cStringCompression", "", nullptr); + tolua_usertype(tolua_S, "cLineBlockTracer"); + tolua_cclass(tolua_S, "cLineBlockTracer", "cLineBlockTracer", "", nullptr); // Globals: - tolua_function(tolua_S, "Clamp", tolua_Clamp); - tolua_function(tolua_S, "StringSplit", tolua_StringSplit); - tolua_function(tolua_S, "StringSplitAndTrim", tolua_StringSplitAndTrim); - tolua_function(tolua_S, "LOG", tolua_LOG); - tolua_function(tolua_S, "LOGINFO", tolua_LOGINFO); - tolua_function(tolua_S, "LOGWARN", tolua_LOGWARN); - tolua_function(tolua_S, "LOGWARNING", tolua_LOGWARN); - tolua_function(tolua_S, "LOGERROR", tolua_LOGERROR); - tolua_function(tolua_S, "Base64Encode", tolua_Base64Encode); - tolua_function(tolua_S, "Base64Decode", tolua_Base64Decode); - tolua_function(tolua_S, "md5", tolua_md5_obsolete); // OBSOLETE, use cCryptoHash.md5() instead + tolua_function(tolua_S, "Clamp", tolua_Clamp); + tolua_function(tolua_S, "StringSplit", tolua_StringSplit); + tolua_function(tolua_S, "StringSplitWithQuotes", tolua_StringSplitWithQuotes); + tolua_function(tolua_S, "StringSplitAndTrim", tolua_StringSplitAndTrim); + tolua_function(tolua_S, "LOG", tolua_LOG); + tolua_function(tolua_S, "LOGINFO", tolua_LOGINFO); + tolua_function(tolua_S, "LOGWARN", tolua_LOGWARN); + tolua_function(tolua_S, "LOGWARNING", tolua_LOGWARN); + tolua_function(tolua_S, "LOGERROR", tolua_LOGERROR); + tolua_function(tolua_S, "Base64Encode", tolua_Base64Encode); + tolua_function(tolua_S, "Base64Decode", tolua_Base64Decode); + tolua_function(tolua_S, "md5", tolua_md5_obsolete); // OBSOLETE, use cCryptoHash.md5() instead tolua_beginmodule(tolua_S, "cFile"); tolua_function(tolua_S, "GetFolderContents", tolua_cFile_GetFolderContents); diff --git a/src/Bindings/ManualBindings_Network.cpp b/src/Bindings/ManualBindings_Network.cpp index a628eb9ca..628cda7f0 100644 --- a/src/Bindings/ManualBindings_Network.cpp +++ b/src/Bindings/ManualBindings_Network.cpp @@ -129,6 +129,30 @@ static int tolua_cNetwork_CreateUDPEndpoint(lua_State * L) +/** Binds cNetwork::EnumLocalIPAddresses */ +static int tolua_cNetwork_EnumLocalIPAddresses(lua_State * L) +{ + // Function signature: + // cNetwork:EnumLocalIPAddresses() -> {string, ...} + + cLuaState S(L); + if ( + !S.CheckParamUserTable(1, "cNetwork") || + !S.CheckParamEnd(2) + ) + { + return 0; + } + + // Push the enumerated addresses: + S.Push(cNetwork::EnumLocalIPAddresses()); + return 1; +} + + + + + /** Binds cNetwork::HostnameToIP */ static int tolua_cNetwork_HostnameToIP(lua_State * L) { @@ -903,11 +927,12 @@ void ManualBindings::BindNetwork(lua_State * tolua_S) // Fill in the functions (alpha-sorted): tolua_beginmodule(tolua_S, "cNetwork"); - tolua_function(tolua_S, "Connect", tolua_cNetwork_Connect); - tolua_function(tolua_S, "CreateUDPEndpoint", tolua_cNetwork_CreateUDPEndpoint); - tolua_function(tolua_S, "HostnameToIP", tolua_cNetwork_HostnameToIP); - tolua_function(tolua_S, "IPToHostname", tolua_cNetwork_IPToHostname); - tolua_function(tolua_S, "Listen", tolua_cNetwork_Listen); + tolua_function(tolua_S, "Connect", tolua_cNetwork_Connect); + tolua_function(tolua_S, "CreateUDPEndpoint", tolua_cNetwork_CreateUDPEndpoint); + tolua_function(tolua_S, "EnumLocalIPAddresses", tolua_cNetwork_EnumLocalIPAddresses); + tolua_function(tolua_S, "HostnameToIP", tolua_cNetwork_HostnameToIP); + tolua_function(tolua_S, "IPToHostname", tolua_cNetwork_IPToHostname); + tolua_function(tolua_S, "Listen", tolua_cNetwork_Listen); tolua_endmodule(tolua_S); tolua_beginmodule(tolua_S, "cServerHandle"); diff --git a/src/Bindings/Plugin.h b/src/Bindings/Plugin.h index 6210dbed4..3f9fa7655 100644 --- a/src/Bindings/Plugin.h +++ b/src/Bindings/Plugin.h @@ -57,6 +57,7 @@ public: virtual bool OnCraftingNoRecipe (cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe & a_Recipe) = 0; virtual bool OnDisconnect (cClientHandle & a_Client, const AString & a_Reason) = 0; virtual bool OnEntityAddEffect (cEntity & a_Entity, int a_EffectType, int a_EffectDurationTicks, int a_EffectIntensity, double a_DistanceModifier) = 0; + virtual bool OnEntityTeleport (cEntity & a_Entity, const Vector3d & a_OldPosition, const Vector3d & a_NewPosition) = 0; virtual bool OnExecuteCommand (cPlayer * a_Player, const AStringVector & a_Split) = 0; virtual bool OnExploded (cWorld & a_World, double a_ExplosionSize, bool a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) = 0; virtual bool OnExploding (cWorld & a_World, double & a_ExplosionSize, bool & a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) = 0; @@ -110,12 +111,12 @@ public: Command permissions have already been checked. Returns true if command handled successfully */ - virtual bool HandleCommand(const AStringVector & a_Split, cPlayer & a_Player) = 0; + virtual bool HandleCommand(const AStringVector & a_Split, cPlayer & a_Player, const AString & a_FullCommand) = 0; /** Handles the console command split into a_Split. Returns true if command handled successfully. Output is to be sent to the a_Output callback. */ - virtual bool HandleConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output) = 0; + virtual bool HandleConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output, const AString & a_FullCommand) = 0; /// All bound commands are to be removed, do any language-dependent cleanup here virtual void ClearCommands(void) {} diff --git a/src/Bindings/PluginLua.cpp b/src/Bindings/PluginLua.cpp index 500913e76..0a2a8411d 100644 --- a/src/Bindings/PluginLua.cpp +++ b/src/Bindings/PluginLua.cpp @@ -6,10 +6,11 @@ #include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules #ifdef __APPLE__ -#define LUA_USE_MACOSX + #define LUA_USE_MACOSX #else -#define LUA_USE_POSIX + #define LUA_USE_POSIX #endif + #include "PluginLua.h" #include "../CommandOutput.h" #include "PluginManager.h" @@ -52,24 +53,40 @@ cPluginLua::~cPluginLua() void cPluginLua::Close(void) { - if (m_LuaState.IsValid()) - { - // 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(); - - m_LuaState.Close(); - } - else + cCSLock Lock(m_CriticalSection); + + // If already closed, bail out: + if (!m_LuaState.IsValid()) { + ASSERT(m_Resettables.empty()); ASSERT(m_HookMap.empty()); + return; } + + // Notify and remove all m_Resettables (unlock the m_CriticalSection while resetting them): + cResettablePtrs resettables; + std::swap(m_Resettables, resettables); + { + cCSUnlock Unlock(Lock); + for (auto resettable: resettables) + { + resettable->Reset(); + } + m_Resettables.clear(); + } // cCSUnlock (m_CriticalSection) + + // Release all the references in the hook map: + for (cHookMap::iterator itrH = m_HookMap.begin(), endH = m_HookMap.end(); itrH != endH; ++itrH) + { + for (cLuaRefs::iterator itrR = itrH->second.begin(), endR = itrH->second.end(); itrR != endR; ++itrR) + { + delete *itrR; + } // for itrR - itrH->second[] + } // for itrH - m_HookMap[] + m_HookMap.clear(); + + // Close the Lua engine: + m_LuaState.Close(); } @@ -857,6 +874,26 @@ 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); + 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((int)(**itr), &a_Entity, a_OldPosition, a_NewPosition, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + bool cPluginLua::OnPlayerPlacedBlock(cPlayer & a_Player, const sSetBlock & a_BlockChange) { cCSLock Lock(m_CriticalSection); @@ -1445,7 +1482,7 @@ bool cPluginLua::OnWorldTick(cWorld & a_World, std::chrono::milliseconds a_Dt, s -bool cPluginLua::HandleCommand(const AStringVector & a_Split, cPlayer & a_Player) +bool cPluginLua::HandleCommand(const AStringVector & a_Split, cPlayer & a_Player, const AString & a_FullCommand) { ASSERT(!a_Split.empty()); CommandMap::iterator cmd = m_Commands.find(a_Split[0]); @@ -1457,7 +1494,7 @@ bool cPluginLua::HandleCommand(const AStringVector & a_Split, cPlayer & a_Player cCSLock Lock(m_CriticalSection); bool res = false; - m_LuaState.Call(cmd->second, a_Split, &a_Player, cLuaState::Return, res); + m_LuaState.Call(cmd->second, a_Split, &a_Player, a_FullCommand, cLuaState::Return, res); return res; } @@ -1465,7 +1502,7 @@ bool cPluginLua::HandleCommand(const AStringVector & a_Split, cPlayer & a_Player -bool cPluginLua::HandleConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output) +bool cPluginLua::HandleConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output, const AString & a_FullCommand) { ASSERT(!a_Split.empty()); CommandMap::iterator cmd = m_ConsoleCommands.find(a_Split[0]); @@ -1480,7 +1517,7 @@ bool cPluginLua::HandleConsoleCommand(const AStringVector & a_Split, cCommandOut cCSLock Lock(m_CriticalSection); bool res = false; AString str; - m_LuaState.Call(cmd->second, a_Split, cLuaState::Return, res, str); + m_LuaState.Call(cmd->second, a_Split, a_FullCommand, cLuaState::Return, res, str); if (res && !str.empty()) { a_Output.Out(str); @@ -1577,6 +1614,7 @@ const char * cPluginLua::GetHookFnName(int a_HookType) case cPluginManager::HOOK_DISCONNECT: return "OnDisconnect"; case cPluginManager::HOOK_PLAYER_ANIMATION: return "OnPlayerAnimation"; case cPluginManager::HOOK_ENTITY_ADD_EFFECT: return "OnEntityAddEffect"; + case cPluginManager::HOOK_ENTITY_TELEPORT: return "OnEntityTeleport"; case cPluginManager::HOOK_EXECUTE_COMMAND: return "OnExecuteCommand"; case cPluginManager::HOOK_HANDSHAKE: return "OnHandshake"; case cPluginManager::HOOK_KILLING: return "OnKilling"; @@ -1688,6 +1726,16 @@ 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) { cCSLock Lock(m_CriticalSection); @@ -1805,3 +1853,25 @@ 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) +{ + cCSLock Lock(m_CSPlugin); + m_Plugin = nullptr; +} + + + + diff --git a/src/Bindings/PluginLua.h b/src/Bindings/PluginLua.h index f443f5fc0..c14b02687 100644 --- a/src/Bindings/PluginLua.h +++ b/src/Bindings/PluginLua.h @@ -59,6 +59,37 @@ public: /** RAII lock for m_Plugin.m_CriticalSection */ cCSLock m_Lock; } ; + + + + /** 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); @@ -106,6 +137,7 @@ public: virtual bool OnPlayerRightClickingEntity(cPlayer & a_Player, cEntity & a_Entity) override; virtual bool OnPlayerShooting (cPlayer & a_Player) override; virtual bool OnPlayerSpawned (cPlayer & a_Player) override; + virtual bool OnEntityTeleport (cEntity & a_Entity, const Vector3d & a_OldPosition, const Vector3d & a_NewPosition) override; virtual bool OnPlayerTossingItem (cPlayer & a_Player) override; virtual bool 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) override; virtual bool 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) override; @@ -130,9 +162,9 @@ public: virtual bool OnWorldStarted (cWorld & a_World) override; virtual bool OnWorldTick (cWorld & a_World, std::chrono::milliseconds a_Dt, std::chrono::milliseconds a_LastTickDurationMSec) override; - virtual bool HandleCommand(const AStringVector & a_Split, cPlayer & a_Player) override; + virtual bool HandleCommand(const AStringVector & a_Split, cPlayer & a_Player, const AString & a_FullCommand) override; - virtual bool HandleConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output) override; + virtual bool HandleConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output, const AString & a_FullCommand) override; virtual void ClearCommands(void) override; @@ -186,42 +218,16 @@ public: int a_ParamEnd ); - // The following templates allow calls to arbitrary Lua functions residing in the plugin: - - /** Call a Lua function with 0 args */ - template <typename FnT> bool Call(FnT a_Fn) - { - cCSLock Lock(m_CriticalSection); - return m_LuaState.Call(a_Fn); - } - - /** Call a Lua function with 1 arg */ - template <typename FnT, typename ArgT0> bool Call(FnT a_Fn, ArgT0 a_Arg0) + /** Call a Lua function residing in the plugin. */ + template <typename FnT, typename... Args> + bool Call(FnT a_Fn, Args && ... a_Args) { cCSLock Lock(m_CriticalSection); - return m_LuaState.Call(a_Fn, a_Arg0); + return m_LuaState.Call(a_Fn, a_Args...); } - /** Call a Lua function with 2 args */ - template <typename FnT, typename ArgT0, typename ArgT1> bool Call(FnT a_Fn, ArgT0 a_Arg0, ArgT1 a_Arg1) - { - cCSLock Lock(m_CriticalSection); - return m_LuaState.Call(a_Fn, a_Arg0, a_Arg1); - } - - /** Call a Lua function with 3 args */ - template <typename FnT, typename ArgT0, typename ArgT1, typename ArgT2> bool Call(FnT a_Fn, ArgT0 a_Arg0, ArgT1 a_Arg1, ArgT2 a_Arg2) - { - cCSLock Lock(m_CriticalSection); - return m_LuaState.Call(a_Fn, a_Arg0, a_Arg1, a_Arg2); - } - - /** Call a Lua function with 4 args */ - template <typename FnT, typename ArgT0, typename ArgT1, typename ArgT2, typename ArgT3> bool Call(FnT a_Fn, ArgT0 a_Arg0, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3) - { - cCSLock Lock(m_CriticalSection); - return m_LuaState.Call(a_Fn, a_Arg0, a_Arg1, a_Arg2, a_Arg3); - } + /** 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 */ @@ -233,15 +239,27 @@ protected: /** Maps hook types into arrays of Lua function references to call for each hook type */ typedef std::map<int, cLuaRefs> cHookMap; + + /** The mutex protecting m_LuaState and each of the m_Resettables[] against multithreaded use. */ cCriticalSection m_CriticalSection; + + /** The plugin's Lua state. */ cLuaState m_LuaState; + /** Objects that need notification when the plugin is about to be unloaded. */ + cResettablePtrs m_Resettables; + + /** In-game commands that the plugin has registered. */ CommandMap m_Commands; + + /** Console commands that the plugin has registered. */ CommandMap m_ConsoleCommands; + /** Hooks that the plugin has registered. */ cHookMap m_HookMap; - /** Releases all Lua references and closes the LuaState */ + + /** Releases all Lua references, notifies and removes all m_Resettables[] and closes the m_LuaState. */ void Close(void); } ; // tolua_export diff --git a/src/Bindings/PluginManager.cpp b/src/Bindings/PluginManager.cpp index 9d86c64a2..8935f7dd3 100644 --- a/src/Bindings/PluginManager.cpp +++ b/src/Bindings/PluginManager.cpp @@ -505,6 +505,24 @@ bool cPluginManager::CallHookEntityAddEffect(cEntity & a_Entity, int a_EffectTyp +bool cPluginManager::CallHookEntityTeleport(cEntity & a_Entity, const Vector3d & a_OldPosition, const Vector3d & a_NewPosition) +{ + FIND_HOOK(HOOK_ENTITY_TELEPORT); + VERIFY_HOOK; + + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnEntityTeleport(a_Entity, a_OldPosition, a_NewPosition)) + { + return true; + } + } + return false; +} + + + + bool cPluginManager::CallHookExecuteCommand(cPlayer * a_Player, const AStringVector & a_Split) { FIND_HOOK(HOOK_EXECUTE_COMMAND); @@ -1447,7 +1465,7 @@ cPluginManager::CommandResult cPluginManager::HandleCommand(cPlayer & a_Player, ASSERT(cmd->second.m_Plugin != nullptr); - if (!cmd->second.m_Plugin->HandleCommand(Split, a_Player)) + if (!cmd->second.m_Plugin->HandleCommand(Split, a_Player, a_Command)) { return crError; } @@ -1750,7 +1768,7 @@ bool cPluginManager::IsConsoleCommandBound(const AString & a_Command) -bool cPluginManager::ExecuteConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output) +bool cPluginManager::ExecuteConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output, const AString & a_Command) { if (a_Split.empty()) { @@ -1777,7 +1795,7 @@ bool cPluginManager::ExecuteConsoleCommand(const AStringVector & a_Split, cComma return false; } - return cmd->second.m_Plugin->HandleConsoleCommand(a_Split, a_Output); + return cmd->second.m_Plugin->HandleConsoleCommand(a_Split, a_Output, a_Command); } diff --git a/src/Bindings/PluginManager.h b/src/Bindings/PluginManager.h index 97e91c1df..4efcbb6f3 100644 --- a/src/Bindings/PluginManager.h +++ b/src/Bindings/PluginManager.h @@ -109,6 +109,7 @@ public: HOOK_PLAYER_RIGHT_CLICKING_ENTITY, HOOK_PLAYER_SHOOTING, HOOK_PLAYER_SPAWNED, + HOOK_ENTITY_TELEPORT, HOOK_PLAYER_TOSSING_ITEM, HOOK_PLAYER_USED_BLOCK, HOOK_PLAYER_USED_ITEM, @@ -190,6 +191,7 @@ public: bool CallHookCraftingNoRecipe (cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe & a_Recipe); bool CallHookDisconnect (cClientHandle & a_Client, const AString & a_Reason); bool CallHookEntityAddEffect (cEntity & a_Entity, int a_EffectType, int a_EffectDurationTicks, int a_EffectIntensity, double a_DistanceModifier); + bool CallHookEntityTeleport (cEntity & a_Entity, const Vector3d & a_OldPosition, const Vector3d & a_NewPosition); bool CallHookExecuteCommand (cPlayer * a_Player, const AStringVector & a_Split); // If a_Player == nullptr, it is a console cmd bool CallHookExploded (cWorld & a_World, double a_ExplosionSize, bool a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData); bool CallHookExploding (cWorld & a_World, double & a_ExplosionSize, bool & a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData); @@ -282,7 +284,7 @@ public: bool IsConsoleCommandBound(const AString & a_Command); // tolua_export /** Executes the command split into a_Split, as if it was given on the console. Returns true if executed. Output is sent to the a_Output callback */ - bool ExecuteConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output); + bool ExecuteConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output, const AString & a_Command); /** Appends all commands beginning with a_Text (case-insensitive) into a_Results. If a_Player is not nullptr, only commands for which the player has permissions are added. |