122 lines
3.5 KiB
D
122 lines
3.5 KiB
D
|
module server_actions;
|
||
|
|
||
|
import std.stdio;
|
||
|
import std.process;
|
||
|
import std.format;
|
||
|
import std.algorithm;
|
||
|
import std.array;
|
||
|
import std.string;
|
||
|
import std.file;
|
||
|
import std.datetime;
|
||
|
import mcrcd;
|
||
|
|
||
|
import shared_utils.discord;
|
||
|
import shared_utils.server_status;
|
||
|
|
||
|
import server_metadata;
|
||
|
import config;
|
||
|
|
||
|
ServerStatus determineStatus(in ServerMetaData server) {
|
||
|
const bool online = isServiceActive(server.serviceName);
|
||
|
int playersOnline = 0;
|
||
|
string[] playerNames;
|
||
|
if (online) {
|
||
|
MCRconResponse response = executeRconCommand(server, "list");
|
||
|
string playersList;
|
||
|
int tmp;
|
||
|
response.text.formattedRead!"There are %d of a max of %d players online: %s"(
|
||
|
playersOnline,
|
||
|
tmp,
|
||
|
playersList
|
||
|
);
|
||
|
playerNames = playersList.strip.split(",")
|
||
|
.filter!(s => s !is null && s.strip.length > 0)
|
||
|
.map!(s => s.strip)
|
||
|
.array;
|
||
|
}
|
||
|
return ServerStatus(
|
||
|
server.name,
|
||
|
server.displayName,
|
||
|
server.description,
|
||
|
online,
|
||
|
playersOnline,
|
||
|
server.maxPlayers,
|
||
|
playerNames.idup
|
||
|
);
|
||
|
}
|
||
|
|
||
|
void startServer(in ServerMetaData server, in AgentConfig config) {
|
||
|
writeln("Starting server " ~ server.name);
|
||
|
Pid pid = spawnProcess(["sudo", "systemctl", "start", server.serviceName]);
|
||
|
int result = wait(pid);
|
||
|
if (result != 0) {
|
||
|
string msg = format!"Starting server %s failed with code %d."(server.name, result);
|
||
|
stderr.writeln(msg);
|
||
|
throw new Exception(msg);
|
||
|
}
|
||
|
sendDiscordMessage(
|
||
|
config.discordWebhookUrl,
|
||
|
format!"Started server %s as a result of a user request."(server.name)
|
||
|
);
|
||
|
}
|
||
|
|
||
|
void stopServer(in ServerMetaData server, in AgentConfig config) {
|
||
|
writeln("Shutting down server " ~ server.name ~ " after a period of inactivity.");
|
||
|
Pid pid = spawnProcess(["sudo", "systemctl", "stop", server.serviceName]);
|
||
|
int result = wait(pid);
|
||
|
if (result == 0) {
|
||
|
removeIdleTrackerFileIfPresent(server);
|
||
|
sendDiscordMessage(
|
||
|
config.discordWebhookUrl,
|
||
|
format!"Shut down server %s after inactivity for more than %d minutes."(
|
||
|
server.name,
|
||
|
config.serverInactivityTimeoutMinutes
|
||
|
)
|
||
|
);
|
||
|
} else {
|
||
|
stderr.writefln!"Failed to stop server %s. systemctl stop exited with code %d."(server.name, result);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Duration getOrCreateIdleTrackerFileAndGetAge(in ServerMetaData server) {
|
||
|
string filename = getIdleTrackerFilename(server);
|
||
|
if (exists(filename)) {
|
||
|
SysTime timestamp = std.file.timeLastModified(filename);
|
||
|
return Clock.currTime - timestamp;
|
||
|
} else {
|
||
|
File f = File(filename, "w");
|
||
|
f.close();
|
||
|
writeln("Created idle tracker for server " ~ server.name ~ ".");
|
||
|
return seconds(0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void removeIdleTrackerFileIfPresent(in ServerMetaData server) {
|
||
|
string trackerFile = getIdleTrackerFilename(server);
|
||
|
if (exists(trackerFile)) {
|
||
|
std.file.remove(trackerFile);
|
||
|
writeln("Removed idle tracker for server " ~ server.name);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private MCRconResponse executeRconCommand(in ServerMetaData server, string command) {
|
||
|
MCRcon rcon = new MCRcon();
|
||
|
rcon.connect("127.0.0.1", server.rconPort);
|
||
|
scope(exit) {
|
||
|
rcon.disconnect();
|
||
|
}
|
||
|
rcon.login(server.rconPassword);
|
||
|
return rcon.command(command);
|
||
|
}
|
||
|
|
||
|
private bool isServiceActive(string serviceName) {
|
||
|
import std.process;
|
||
|
Pid pid = spawnProcess(["systemctl", "is-active", "--quiet", serviceName]);
|
||
|
int result = wait(pid);
|
||
|
return result == 0;
|
||
|
}
|
||
|
|
||
|
private string getIdleTrackerFilename(in ServerMetaData server) {
|
||
|
return "agent-idle-tracker__" ~ server.name ~ ".txt";
|
||
|
}
|