module server_metadata; import std.json; import std.file; import std.path; import std.string : endsWith; import std.array; import std.algorithm; import std.stdio; import properd; /** * The set of information about each server that we can obtain by reading the * server's directory and files in it (service file, properties, etc.). */ struct ServerMetaData { /// The name of the server, as declared in servers.json. const string name; /// The display name of the server, as declared in servers.json. const string displayName; /// The description of the server, as declared in servers.json. const string description; /// The directory to find the server's files in. const string directory; /// The name of the server's SystemD service, as determined by searching the server's directory. const string serviceName; /// The port used for player connections. const ushort port; /// The password for connecting to the RCON service of the server. const string rconPassword; /// The port for connecting to the RCON service. const ushort rconPort; /// The maximum number of players for the server. const int maxPlayers; } /** * Reads server metadata from each server defined in "servers.json", by parsing * its "server.properties" file, SystemD service file, and more. * Returns: The list of server metadata structs. */ ServerMetaData[] readServerMetaData() { import properd; JSONValue arr = parseJSON(readText("servers.json")); return arr.array.map!((s) { string name = s.object["name"].str; string displayName = s.object["displayName"].str; string description = s.object["description"].str; string directory = s.object["directory"].str; string systemDServiceName = findSystemDServiceName(directory); string propsFile = buildPath(directory, "server.properties"); if (!exists(propsFile)) throw new Exception("Missing server properties file: " ~ propsFile); try { auto props = readProperties(propsFile); ushort port = props.as!(ushort)("server-port"); string rconPassword = props["rcon.password"]; ushort rconPort = props.as!(ushort)("rcon.port"); int maxPlayers = props.as!(int)("max-players"); return ServerMetaData( name, displayName, description, directory, systemDServiceName, port, rconPassword, rconPort, maxPlayers ); } catch (PropertyException e) { stderr.writefln!"Error parsing properties from %s: %s"(propsFile, e.msg); throw e; } }).array; } private string findSystemDServiceName(string serverDir) { foreach (DirEntry entry; dirEntries(serverDir, SpanMode.shallow, false)) { if (entry.name.endsWith(".service")) { return baseName(stripExtension(entry.name)); } } throw new Exception("No SystemD service file found in " ~ serverDir); }