mc-server-manager/agent/source/server_actions.d

126 lines
3.7 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) {
try {
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;
} catch (Exception e) {
stderr.writefln!"Failed to get players from server: %s"(e.msg);
}
}
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";
}