summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Bindings/ManualBindings.cpp43
-rw-r--r--src/Bindings/ManualBindings.h4
-rw-r--r--src/Server.cpp62
-rw-r--r--src/Server.h20
4 files changed, 124 insertions, 5 deletions
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<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)
{
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<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)
{
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<void(class cServer &)> 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<std::pair<std::chrono::milliseconds, std::function<void(class cServer &)>>> 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