diff --git a/src/Bindings/ManualBindings.cpp b/src/Bindings/ManualBindings.cpp index a9368f613..b12fa5f03 100644 --- a/src/Bindings/ManualBindings.cpp +++ b/src/Bindings/ManualBindings.cpp @@ -941,10 +941,6 @@ protected: } } ; - - - - static int tolua_cWorld_QueueTask(lua_State * tolua_S) { // Binding for cWorld::QueueTask @@ -980,7 +976,65 @@ static int tolua_cWorld_QueueTask(lua_State * tolua_S) return 0; } +class cLuaScheduledWorldTask : + public cWorld::cScheduledTask +{ +public: + cLuaScheduledWorldTask(cPluginLua & a_Plugin, int a_FnRef, int a_Ticks) : + cScheduledTask(a_Ticks), + m_Plugin(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); + } +}; + + +static int tolua_cWorld_ScheduleTask(lua_State * tolua_S) +{ + // Binding for cWorld::ScheduleTask + // Params: function, Ticks + + // Retrieve the cPlugin from the LuaState: + cPluginLua * Plugin = GetLuaPlugin(tolua_S); + if (Plugin == NULL) + { + // An error message has been already printed in GetLuaPlugin() + return 0; + } + + // Retrieve the args: + cWorld * self = (cWorld *)tolua_tousertype(tolua_S, 1, 0); + if (self == NULL) + { + return lua_do_error(tolua_S, "Error in function call '#funcname#': Not called on an object instance"); + } + if (!lua_isfunction(tolua_S, 2)) + { + return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a function for parameter #1"); + } + + // Create a reference to the function: + int FnRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); + if (FnRef == LUA_REFNIL) + { + return lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get function reference of parameter #1"); + } + + int Ticks = (int) tolua_tonumber (tolua_S, 3, 0); + + self->ScheduleTask(new cLuaScheduledWorldTask(*Plugin, FnRef,Ticks)); + return 0; +} diff --git a/src/World.cpp b/src/World.cpp index 1cf82d641..2b85e4b58 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -690,6 +690,7 @@ void cWorld::Tick(float a_Dt, int a_LastTickDurationMSec) TickClients(a_Dt); TickQueuedBlocks(); TickQueuedTasks(); + TickScheduledTasks(); GetSimulatorManager()->Simulate(a_Dt); @@ -861,6 +862,31 @@ void cWorld::TickQueuedTasks(void) } // for itr - m_Tasks[] } +void cWorld::TickScheduledTasks() +{ + ScheduledTaskList Tasks; + // Make a copy of the tasks to avoid deadlocks on accessing m_Tasks + { + cCSLock Lock(m_CSScheduledTasks); + ScheduledTaskList::iterator itr = m_ScheduledTasks.begin(); + while (itr != m_ScheduledTasks.end() && (*itr)->Ticks > 0) + { + Tasks.push_back(m_ScheduledTasks.front()); + m_ScheduledTasks.pop_front(); + } + for(;itr != m_ScheduledTasks.end(); itr++) + { + (*itr)->Ticks--; + } + } + + // Execute and delete each task: + for (ScheduledTaskList::iterator itr = Tasks.begin(), end = Tasks.end(); itr != end; ++itr) + { + (*itr)->Run(*this); + delete *itr; + } // for itr - m_Tasks[] +} @@ -2571,6 +2597,19 @@ void cWorld::QueueTask(cTask * a_Task) m_Tasks.push_back(a_Task); } +void cWorld::ScheduleTask(cScheduledTask * a_Task) +{ + cCSLock Lock(m_CSScheduledTasks); + for(ScheduledTaskList::iterator itr = m_ScheduledTasks.begin(); itr != m_ScheduledTasks.end(); itr++) + { + if((*itr)->Ticks >= a_Task->Ticks) + { + m_ScheduledTasks.insert(itr, a_Task); + return; + } + } + m_ScheduledTasks.push_back(a_Task); +} diff --git a/src/World.h b/src/World.h index b61708d03..6ddb3ec86 100644 --- a/src/World.h +++ b/src/World.h @@ -82,7 +82,18 @@ public: virtual void Run(cWorld & a_World) = 0; } ; + /// A common ancestor for all scheduled tasks queued onto the tick thread + class cScheduledTask + { + public: + cScheduledTask(const int a_Ticks) : Ticks(a_Ticks) {}; + virtual ~cScheduledTask() {}; + virtual void Run(cWorld & a_World) = 0; + int Ticks; + }; + typedef std::vector cTasks; + typedef std::list ScheduledTaskList; class cTaskSaveAllChunks : public cTask @@ -533,6 +544,9 @@ public: /// Queues a task onto the tick thread. The task object will be deleted once the task is finished void QueueTask(cTask * a_Task); // Exported in ManualBindings.cpp + + // Queues a task onto the tick thread. The task object will be deleted once the task is finished + void ScheduleTask(cScheduledTask * a_Task); /// Returns the number of chunks loaded int GetNumChunks() const; // tolua_export @@ -745,9 +759,16 @@ private: /// Guards the m_Tasks cCriticalSection m_CSTasks; + /// Guards the m_ScheduledTasks + cCriticalSection m_CSScheduledTasks; + /// Tasks that have been queued onto the tick thread; guarded by m_CSTasks cTasks m_Tasks; + /// Tasks that have been queued to be executed on the tick thread at some number of ticks in + /// the future; guarded by m_CSScheduledTasks + ScheduledTaskList m_ScheduledTasks; + /// Guards m_Clients cCriticalSection m_CSClients; @@ -775,6 +796,9 @@ private: /// Executes all tasks queued onto the tick thread void TickQueuedTasks(void); + /// Executes all tasks queued onto the tick thread + void TickScheduledTasks(void); + /// Ticks all clients that are in this world void TickClients(float a_Dt);