at 18.03-beta 7.0 kB view raw
1{ config, lib, pkgs, ... }: 2 3with lib; 4 5let 6 cfg = config.services.factorio; 7 factorio = pkgs.factorio-headless; 8 name = "Factorio"; 9 stateDir = cfg.stateDir; 10 mkSavePath = name: "${stateDir}/saves/${name}.zip"; 11 configFile = pkgs.writeText "factorio.conf" '' 12 use-system-read-write-data-directories=true 13 [path] 14 read-data=${factorio}/share/factorio/data 15 write-data=${stateDir} 16 ''; 17 serverSettings = { 18 name = cfg.game-name; 19 description = cfg.description; 20 visibility = { 21 public = cfg.public; 22 lan = cfg.lan; 23 }; 24 username = cfg.username; 25 password = cfg.password; 26 token = cfg.token; 27 game_password = cfg.game-password; 28 require_user_verification = cfg.requireUserVerification; 29 max_upload_in_kilobytes_per_second = 0; 30 minimum_latency_in_ticks = 0; 31 ignore_player_limit_for_returning_players = false; 32 allow_commands = "admins-only"; 33 autosave_interval = cfg.autosave-interval; 34 autosave_slots = 5; 35 afk_autokick_interval = 0; 36 auto_pause = true; 37 only_admins_can_pause_the_game = true; 38 autosave_only_on_server = true; 39 admins = []; 40 }; 41 serverSettingsFile = pkgs.writeText "server-settings.json" (builtins.toJSON (filterAttrsRecursive (n: v: v != null) serverSettings)); 42 modDir = pkgs.factorio-utils.mkModDirDrv cfg.mods; 43in 44{ 45 options = { 46 services.factorio = { 47 enable = mkEnableOption name; 48 port = mkOption { 49 type = types.int; 50 default = 34197; 51 description = '' 52 The port to which the service should bind. 53 54 This option will also open up the UDP port in the firewall configuration. 55 ''; 56 }; 57 saveName = mkOption { 58 type = types.string; 59 default = "default"; 60 description = '' 61 The name of the savegame that will be used by the server. 62 63 When not present in ${stateDir}/saves, a new map with default 64 settings will be generated before starting the service. 65 ''; 66 }; 67 # TODO Add more individual settings as nixos-options? 68 # TODO XXX The server tries to copy a newly created config file over the old one 69 # on shutdown, but fails, because it's in the nix store. When is this needed? 70 # Can an admin set options in-game and expect to have them persisted? 71 configFile = mkOption { 72 type = types.path; 73 default = configFile; 74 defaultText = "configFile"; 75 description = '' 76 The server's configuration file. 77 78 The default file generated by this module contains lines essential to 79 the server's operation. Use its contents as a basis for any 80 customizations. 81 ''; 82 }; 83 stateDir = mkOption { 84 type = types.path; 85 default = "/var/lib/factorio"; 86 description = '' 87 The server's data directory. 88 89 The configuration and map will be stored here. 90 ''; 91 }; 92 mods = mkOption { 93 type = types.listOf types.package; 94 default = []; 95 description = '' 96 Mods the server should install and activate. 97 98 The derivations in this list must "build" the mod by simply copying 99 the .zip, named correctly, into the output directory. Eventually, 100 there will be a way to pull in the most up-to-date list of 101 derivations via nixos-channel. Until then, this is for experts only. 102 ''; 103 }; 104 game-name = mkOption { 105 type = types.nullOr types.string; 106 default = "Factorio Game"; 107 description = '' 108 Name of the game as it will appear in the game listing. 109 ''; 110 }; 111 description = mkOption { 112 type = types.nullOr types.string; 113 default = ""; 114 description = '' 115 Description of the game that will appear in the listing. 116 ''; 117 }; 118 public = mkOption { 119 type = types.bool; 120 default = false; 121 description = '' 122 Game will be published on the official Factorio matching server. 123 ''; 124 }; 125 lan = mkOption { 126 type = types.bool; 127 default = false; 128 description = '' 129 Game will be broadcast on LAN. 130 ''; 131 }; 132 username = mkOption { 133 type = types.nullOr types.string; 134 default = null; 135 description = '' 136 Your factorio.com login credentials. Required for games with visibility public. 137 ''; 138 }; 139 password = mkOption { 140 type = types.nullOr types.string; 141 default = null; 142 description = '' 143 Your factorio.com login credentials. Required for games with visibility public. 144 ''; 145 }; 146 token = mkOption { 147 type = types.nullOr types.string; 148 default = null; 149 description = '' 150 Authentication token. May be used instead of 'password' above. 151 ''; 152 }; 153 game-password = mkOption { 154 type = types.nullOr types.string; 155 default = null; 156 description = '' 157 Game password. 158 ''; 159 }; 160 requireUserVerification = mkOption { 161 type = types.bool; 162 default = true; 163 description = '' 164 When set to true, the server will only allow clients that have a valid factorio.com account. 165 ''; 166 }; 167 autosave-interval = mkOption { 168 type = types.nullOr types.int; 169 default = null; 170 example = 10; 171 description = '' 172 Autosave interval in minutes. 173 ''; 174 }; 175 }; 176 }; 177 178 config = mkIf cfg.enable { 179 users = { 180 users.factorio = { 181 uid = config.ids.uids.factorio; 182 description = "Factorio server user"; 183 group = "factorio"; 184 home = stateDir; 185 createHome = true; 186 }; 187 188 groups.factorio = { 189 gid = config.ids.gids.factorio; 190 }; 191 }; 192 193 systemd.services.factorio = { 194 description = "Factorio headless server"; 195 wantedBy = [ "multi-user.target" ]; 196 after = [ "network.target" ]; 197 198 preStart = toString [ 199 "test -e ${stateDir}/saves/${cfg.saveName}.zip" 200 "||" 201 "${factorio}/bin/factorio" 202 "--config=${cfg.configFile}" 203 "--create=${mkSavePath cfg.saveName}" 204 (optionalString (cfg.mods != []) "--mod-directory=${modDir}") 205 ]; 206 207 serviceConfig = { 208 User = "factorio"; 209 Group = "factorio"; 210 Restart = "always"; 211 KillSignal = "SIGINT"; 212 WorkingDirectory = stateDir; 213 PrivateTmp = true; 214 UMask = "0007"; 215 ExecStart = toString [ 216 "${factorio}/bin/factorio" 217 "--config=${cfg.configFile}" 218 "--port=${toString cfg.port}" 219 "--start-server=${mkSavePath cfg.saveName}" 220 "--server-settings=${serverSettingsFile}" 221 (optionalString (cfg.mods != []) "--mod-directory=${modDir}") 222 ]; 223 }; 224 }; 225 226 networking.firewall.allowedUDPPorts = [ cfg.port ]; 227 }; 228}