1
0

Implemented cServer::ScheduleTask() and cServer::TickQueuedTasks() (#5224)

This commit is contained in:
Feyo Korenhof 2021-05-26 18:07:32 +02:00 committed by GitHub
parent d92509a6e7
commit 9ddc3635d6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 140 additions and 5 deletions

View File

@ -22,6 +22,7 @@ Diusrex
Duralex Duralex
Earboxer (Zach DeCook) Earboxer (Zach DeCook)
FakeTruth (founder) FakeTruth (founder)
feyokorenhof
Gareth Nelson Gareth Nelson
GefaketHD GefaketHD
HaoTNN HaoTNN

View File

@ -12307,6 +12307,21 @@ end
}, },
Notes = "Add a Forge mod name/version to the server ping list.", Notes = "Add a Forge mod name/version to the server ping list.",
}, },
ScheduleTask =
{
Params =
{
{
Name = "DelayTicks",
Type = "number",
},
{
Name = "TaskFunction",
Type = "function",
},
},
Notes = "Queues the specified function to be executed in the server's tick thread after a the specified number of ticks. This enables operations to be queued for execution in the future. The function signature is <pre class=\"pretty-print lang-lua\">function({{cServer|Server}})</pre>All return values from the function are ignored. Note that it is unsafe to store references to Cuberite objects, such as entities, across from the caller to the task handler function; store the EntityID instead.",
},
SetMaxPlayers = SetMaxPlayers =
{ {
Params = Params =

View File

@ -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<cLuaState::cCallback>();
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) static int tolua_cScoreboard_GetTeamNames(lua_State * L)
{ {
cLuaState S(L); cLuaState S(L);
@ -4625,6 +4667,7 @@ void cManualBindings::Bind(lua_State * tolua_S)
tolua_beginmodule(tolua_S, "cServer"); tolua_beginmodule(tolua_S, "cServer");
tolua_function(tolua_S, "RegisterForgeMod", tolua_cServer_RegisterForgeMod); tolua_function(tolua_S, "RegisterForgeMod", tolua_cServer_RegisterForgeMod);
tolua_function(tolua_S, "ScheduleTask", tolua_cServer_ScheduleTask);
tolua_endmodule(tolua_S); tolua_endmodule(tolua_S);
tolua_beginmodule(tolua_S, "cStringCompression"); tolua_beginmodule(tolua_S, "cStringCompression");

View File

@ -298,7 +298,3 @@ public:
return 1; return 1;
} }
}; };

View File

@ -117,7 +117,8 @@ cServer::cServer(void) :
m_MaxPlayers(0), m_MaxPlayers(0),
m_bIsHardcore(false), m_bIsHardcore(false),
m_TickThread(*this), m_TickThread(*this),
m_ShouldAuthenticate(false) m_ShouldAuthenticate(false),
m_UpTime(0)
{ {
// Initialize the LuaStateTracker singleton before the app goes multithreaded: // Initialize the LuaStateTracker singleton before the app goes multithreaded:
cLuaStateTracker::GetStats(); cLuaStateTracker::GetStats();
@ -326,6 +327,9 @@ cTCPLink::cCallbacksPtr cServer::OnConnectionAccepted(const AString & a_RemoteIP
void cServer::Tick(float a_Dt) 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): // Send the tick to the plugins, as well as let the plugin manager reload, if asked to (issue #102):
cPluginManager::Get()->Tick(a_Dt); cPluginManager::Get()->Tick(a_Dt);
@ -334,6 +338,9 @@ void cServer::Tick(float a_Dt)
// Tick all clients not yet assigned to a world: // Tick all clients not yet assigned to a world:
TickClients(a_Dt); 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<void(cServer &)> 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) void cServer::ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallback & a_Output)
{ {
AStringVector split = StringSplit(a_Cmd, " "); AStringVector split = StringSplit(a_Cmd, " ");
@ -725,3 +746,42 @@ void cServer::TickCommands(void)
ExecuteConsoleCommand(Command.first, *Command.second); 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[]
}

View File

@ -105,6 +105,9 @@ public:
The command's output will be written to the a_Output callback. */ The command's output will be written to the a_Output callback. */
void QueueExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallback & a_Output); 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<void(class cServer &)> a_Task);
/** Lists all available console commands and their helpstrings */ /** Lists all available console commands and their helpstrings */
void PrintHelp(const AStringVector & a_Split, cCommandOutputCallback & a_Output); void PrintHelp(const AStringVector & a_Split, cCommandOutputCallback & a_Output);
@ -245,6 +248,18 @@ private:
AStringVector m_Ports; 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<std::pair<std::chrono::milliseconds, std::function<void(class cServer &)>>> m_Tasks;
cServer(void); cServer(void);
/** Executes the console command, sends output through the specified callback. */ /** Executes the console command, sends output through the specified callback. */
@ -268,6 +283,11 @@ private:
/** Executes commands queued in the command queue. */ /** Executes commands queued in the command queue. */
void TickCommands(void); void TickCommands(void);
/** Executes all tasks queued onto the tick thread */
void TickQueuedTasks(void);
}; // tolua_export }; // tolua_export