From 9ddc3635d6a04ff4b78611df00c905cc86e166ae Mon Sep 17 00:00:00 2001 From: Feyo Korenhof <35343640+feyokorenhof@users.noreply.github.com> Date: Wed, 26 May 2021 18:07:32 +0200 Subject: Implemented cServer::ScheduleTask() and cServer::TickQueuedTasks() (#5224) --- src/Bindings/ManualBindings.cpp | 43 ++++++++++++++++++++++++++++ src/Bindings/ManualBindings.h | 4 --- src/Server.cpp | 62 ++++++++++++++++++++++++++++++++++++++++- src/Server.h | 20 +++++++++++++ 4 files changed, 124 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/Bindings/ManualBindings.cpp b/src/Bindings/ManualBindings.cpp index f06f4da37..fe5a1e6cb 100644 --- a/src/Bindings/ManualBindings.cpp +++ b/src/Bindings/ManualBindings.cpp @@ -3585,6 +3585,48 @@ static int tolua_cServer_RegisterForgeMod(lua_State * a_LuaState) +static int tolua_cServer_ScheduleTask(lua_State * a_LuaState) +{ + // Function signature: + // Server:ScheduleTask(NumTicks, Callback) + + // Retrieve the args: + cLuaState L(a_LuaState); + if ( + !L.CheckParamUserType(1, "cServer") || + !L.CheckParamNumber(2) || + !L.CheckParamFunction(3) + ) + { + return 0; + } + cServer * Server; + int NumTicks; + auto Task = std::make_shared(); + if (!L.GetStackValues(1, Server, NumTicks, Task)) + { + return cManualBindings::lua_do_error(a_LuaState, "Error in function call '#funcname#': Cannot read parameters"); + } + if (Server == nullptr) + { + return cManualBindings::lua_do_error(a_LuaState, "Error in function call '#funcname#': Not called on an object instance"); + } + if (!Task->IsValid()) + { + return cManualBindings::lua_do_error(a_LuaState, "Error in function call '#funcname#': Could not store the callback parameter"); + } + + Server->ScheduleTask(cTickTime(NumTicks), [Task](cServer & a_Server) + { + Task->Call(&a_Server); + }); + return 0; +} + + + + + static int tolua_cScoreboard_GetTeamNames(lua_State * L) { cLuaState S(L); @@ -4625,6 +4667,7 @@ void cManualBindings::Bind(lua_State * tolua_S) tolua_beginmodule(tolua_S, "cServer"); tolua_function(tolua_S, "RegisterForgeMod", tolua_cServer_RegisterForgeMod); + tolua_function(tolua_S, "ScheduleTask", tolua_cServer_ScheduleTask); tolua_endmodule(tolua_S); tolua_beginmodule(tolua_S, "cStringCompression"); diff --git a/src/Bindings/ManualBindings.h b/src/Bindings/ManualBindings.h index 780c6ce41..f0b7cb607 100644 --- a/src/Bindings/ManualBindings.h +++ b/src/Bindings/ManualBindings.h @@ -298,7 +298,3 @@ public: return 1; } }; - - - - diff --git a/src/Server.cpp b/src/Server.cpp index 3402a80fa..ae0df7544 100644 --- a/src/Server.cpp +++ b/src/Server.cpp @@ -117,7 +117,8 @@ cServer::cServer(void) : m_MaxPlayers(0), m_bIsHardcore(false), m_TickThread(*this), - m_ShouldAuthenticate(false) + m_ShouldAuthenticate(false), + m_UpTime(0) { // Initialize the LuaStateTracker singleton before the app goes multithreaded: cLuaStateTracker::GetStats(); @@ -326,6 +327,9 @@ cTCPLink::cCallbacksPtr cServer::OnConnectionAccepted(const AString & a_RemoteIP void cServer::Tick(float a_Dt) { + // Update server uptime + m_UpTime++; + // Send the tick to the plugins, as well as let the plugin manager reload, if asked to (issue #102): cPluginManager::Get()->Tick(a_Dt); @@ -334,6 +338,9 @@ void cServer::Tick(float a_Dt) // Tick all clients not yet assigned to a world: TickClients(a_Dt); + + // Process all queued tasks + TickQueuedTasks(); } @@ -442,6 +449,20 @@ void cServer::QueueExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCa +void cServer::ScheduleTask(cTickTime a_DelayTicks, std::function a_Task) +{ + const auto TargetTick = a_DelayTicks + m_UpTime; + // Insert the task into the list of scheduled tasks + { + cCSLock Lock(m_CSTasks); + m_Tasks.emplace_back(TargetTick, std::move(a_Task)); + } +} + + + + + void cServer::ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallback & a_Output) { AStringVector split = StringSplit(a_Cmd, " "); @@ -725,3 +746,42 @@ void cServer::TickCommands(void) ExecuteConsoleCommand(Command.first, *Command.second); } } + + + + + +void cServer::TickQueuedTasks(void) +{ + // Move the tasks to be executed to a seperate vector to avoid deadlocks on + // accessing m_Tasks + decltype(m_Tasks) Tasks; + { + cCSLock Lock(m_CSTasks); + if (m_Tasks.empty()) + { + return; + } + + // Partition everything to be executed by returning false to move to end + // of list if time reached + auto MoveBeginIterator = std::partition( + m_Tasks.begin(), m_Tasks.end(), + [this](const decltype(m_Tasks)::value_type & a_Task) + { + return a_Task.first >= m_UpTime; + }); + + // Cut all the due tasks from m_Tasks into Tasks: + Tasks.insert( + Tasks.end(), std::make_move_iterator(MoveBeginIterator), + std::make_move_iterator(m_Tasks.end())); + m_Tasks.erase(MoveBeginIterator, m_Tasks.end()); + } + + // Execute each task: + for (const auto & Task : Tasks) + { + Task.second(*this); + } // for itr - m_Tasks[] +} diff --git a/src/Server.h b/src/Server.h index 3d272d7c0..7701faa69 100644 --- a/src/Server.h +++ b/src/Server.h @@ -105,6 +105,9 @@ public: The command's output will be written to the a_Output callback. */ void QueueExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallback & a_Output); + /** Queues a lambda task onto the server tick thread, with the specified delay in ticks. */ + void ScheduleTask(cTickTime a_DelayTicks, std::function a_Task); + /** Lists all available console commands and their helpstrings */ void PrintHelp(const AStringVector & a_Split, cCommandOutputCallback & a_Output); @@ -245,6 +248,18 @@ private: AStringVector m_Ports; + /** Time, in ticks, since the server started + Not persistent across server restarts */ + cTickTimeLong m_UpTime; + + /** Guards the m_Tasks */ + cCriticalSection m_CSTasks; + + /** Tasks that have been queued onto the tick thread, possibly to be + executed at target tick in the future; guarded by m_CSTasks */ + std::vector>> m_Tasks; + + cServer(void); /** Executes the console command, sends output through the specified callback. */ @@ -268,6 +283,11 @@ private: /** Executes commands queued in the command queue. */ void TickCommands(void); + + /** Executes all tasks queued onto the tick thread */ + void TickQueuedTasks(void); + + }; // tolua_export -- cgit v1.2.3