import std.stdio; import std.algorithm; import std.array; import std.json; import std.datetime; import requests; import shared_utils.server_status; import config; import server_metadata; import server_actions; /** * This program will run frequently (maybe every minute or more), and does the following: * - Fetches a list of requested servers from the mc-server-manager API, and starts them. * - Checks for any servers without any players online, and writes a blank file to track. * - For servers with nobody online, and a timestamp file older than N minutes, the server is shut down. */ void main() { AgentConfig config = readConfig(); ServerMetaData[] servers = readServerMetaData(); ServerStatus[] statuses = servers.map!(determineStatus).array; try { sendServerStatusToWeb(statuses, config); } catch (Exception e) { stderr.writeln(e.msg); } checkForEmptyServers(servers, statuses, config); try { startRequestedServers(servers, config); } catch (Exception e) { stderr.writeln(e.msg); } } void sendServerStatusToWeb(in ServerStatus[] statuses, in AgentConfig config) { JSONValue jsonPayload = serializeServerStatuses(statuses); string payload = jsonPayload.toJSON(); Request rq = Request(); rq.addHeaders(["X-Agent-Key": config.agentKey]); Response resp = rq.post(config.webUrl ~ "/api/servers", payload, "application/json"); if (resp.code >= 300) { import std.format; throw new Exception( format!"Failed to post server statuses: Error code %d, Response body: %s"( resp.code, resp.responseBody ) ); } } void startRequestedServers(ServerMetaData[] servers, AgentConfig config) { auto content = getContent(config.webUrl ~ "/api/server-requests"); JSONValue jsonContent = parseJSON(cast(string) content.data); string[] requestedServerNames = jsonContent.array() .map!(node => node.str) .array; foreach (server; servers) { if (canFind(requestedServerNames, server.name)) { startServer(server, config); } } } void checkForEmptyServers(ServerMetaData[] servers, ServerStatus[] statuses, AgentConfig config) { foreach (i, server; servers) { ServerStatus status = statuses[i]; if (!status.online || (status.online && status.playersOnline > 0)) { removeIdleTrackerFileIfPresent(server); } else if (status.online && status.playersOnline == 0) { const Duration idleTime = getOrCreateIdleTrackerFileAndGetAge(server); if (idleTime.total!"minutes" > config.serverInactivityTimeoutMinutes) { writefln!"Server %s has been idle for %d minutes, shutting down."(server.name, idleTime.total!"minutes"); stopServer(server, config); } } } }