1{
2 config,
3 lib,
4 pkgs,
5 ...
6}:
7let
8 cfg = config.services.bluemap;
9 format = pkgs.formats.hocon { };
10
11 coreConfig = format.generate "core.conf" cfg.coreSettings;
12 webappConfig = format.generate "webapp.conf" cfg.webappSettings;
13 webserverConfig = format.generate "webserver.conf" cfg.webserverSettings;
14
15 mapsFolder = pkgs.linkFarm "maps" (
16 lib.attrsets.mapAttrs' (
17 name: value: lib.nameValuePair "${name}.conf" (format.generate "${name}.conf" value)
18 ) cfg.maps
19 );
20
21 storageFolder = pkgs.linkFarm "storage" (
22 lib.attrsets.mapAttrs' (
23 name: value: lib.nameValuePair "${name}.conf" (format.generate "${name}.conf" value)
24 ) cfg.storage
25 );
26
27 configFolder = pkgs.linkFarm "bluemap-config" {
28 "maps" = mapsFolder;
29 "storages" = storageFolder;
30 "core.conf" = coreConfig;
31 "webapp.conf" = webappConfig;
32 "webserver.conf" = webserverConfig;
33 "packs" = pkgs.linkFarm "packs" cfg.packs;
34 };
35
36 inherit (lib) mkOption;
37in
38{
39 imports = [
40 (lib.mkRenamedOptionModule
41 [ "services" "bluemap" "resourcepacks" ]
42 [ "services" "bluemap" "packs" ]
43 )
44 (lib.mkRenamedOptionModule [ "services" "bluemap" "addons" ] [ "services" "bluemap" "packs" ])
45 ];
46
47 options.services.bluemap = {
48 enable = lib.mkEnableOption "bluemap";
49
50 eula = mkOption {
51 type = lib.types.bool;
52 description = ''
53 By changing this option to true you confirm that you own a copy of minecraft Java Edition,
54 and that you agree to minecrafts EULA.
55 '';
56 default = false;
57 };
58
59 defaultWorld = mkOption {
60 type = lib.types.path;
61 description = ''
62 The world used by the default map ruleset.
63 If you configure your own maps you do not need to set this.
64 '';
65 example = lib.literalExpression "\${config.services.minecraft.dataDir}/world";
66 };
67
68 enableRender = mkOption {
69 type = lib.types.bool;
70 description = "Enable rendering";
71 default = true;
72 };
73
74 webRoot = mkOption {
75 type = lib.types.path;
76 default = "/var/lib/bluemap/web";
77 description = "The directory for saving and serving the webapp and the maps";
78 };
79
80 enableNginx = mkOption {
81 type = lib.types.bool;
82 default = true;
83 description = "Enable configuring a virtualHost for serving the bluemap webapp";
84 };
85
86 host = mkOption {
87 type = lib.types.str;
88 description = "Domain on which nginx will serve the bluemap webapp";
89 };
90
91 onCalendar = mkOption {
92 type = lib.types.str;
93 description = ''
94 How often to trigger rendering the map,
95 in the format of a systemd timer onCalendar configuration.
96 See {manpage}`systemd.timer(5)`.
97 '';
98 default = "*-*-* 03:10:00";
99 };
100
101 coreSettings = mkOption {
102 type = lib.types.submodule {
103 freeformType = format.type;
104 options = {
105 data = mkOption {
106 type = lib.types.path;
107 description = "Folder for where bluemap stores its data";
108 default = "/var/lib/bluemap";
109 };
110 metrics = lib.mkEnableOption "Sending usage metrics containing the version of bluemap in use";
111 };
112 };
113 description = "Settings for the core.conf file, [see upstream docs](https://github.com/BlueMap-Minecraft/BlueMap/blob/master/BlueMapCommon/src/main/resources/de/bluecolored/bluemap/config/core.conf).";
114 };
115
116 webappSettings = mkOption {
117 type = lib.types.submodule {
118 freeformType = format.type;
119 };
120 default = {
121 enabled = true;
122 webroot = cfg.webRoot;
123 };
124 defaultText = lib.literalExpression ''
125 {
126 enabled = true;
127 webroot = config.services.bluemap.webRoot;
128 }
129 '';
130 description = "Settings for the webapp.conf file, see [upstream docs](https://github.com/BlueMap-Minecraft/BlueMap/blob/master/BlueMapCommon/src/main/resources/de/bluecolored/bluemap/config/webapp.conf).";
131 };
132
133 webserverSettings = mkOption {
134 type = lib.types.submodule {
135 freeformType = format.type;
136 options = {
137 enabled = mkOption {
138 type = lib.types.bool;
139 description = ''
140 Enable bluemap's built-in webserver.
141 Disabled by default in nixos for use of nginx directly.
142 '';
143 default = false;
144 };
145 };
146 };
147 default = { };
148 description = ''
149 Settings for the webserver.conf file, usually not required.
150 [See upstream docs](https://github.com/BlueMap-Minecraft/BlueMap/blob/master/BlueMapCommon/src/main/resources/de/bluecolored/bluemap/config/webserver.conf).
151 '';
152 };
153
154 maps = mkOption {
155 type = lib.types.attrsOf (
156 lib.types.submodule {
157 freeformType = format.type;
158 options = {
159 world = lib.mkOption {
160 type = lib.types.path;
161 description = "Path to world folder containing the dimension to render";
162 };
163 };
164 }
165 );
166 default = {
167 "overworld" = {
168 world = "${cfg.defaultWorld}";
169 ambient-light = 0.1;
170 cave-detection-ocean-floor = -5;
171 };
172
173 "nether" = {
174 world = "${cfg.defaultWorld}/DIM-1";
175 sorting = 100;
176 sky-color = "#290000";
177 void-color = "#150000";
178 ambient-light = 0.6;
179 world-sky-light = 0;
180 remove-caves-below-y = -10000;
181 cave-detection-ocean-floor = -5;
182 cave-detection-uses-block-light = true;
183 max-y = 90;
184 };
185
186 "end" = {
187 world = "${cfg.defaultWorld}/DIM1";
188 sorting = 200;
189 sky-color = "#080010";
190 void-color = "#080010";
191 ambient-light = 0.6;
192 world-sky-light = 0;
193 remove-caves-below-y = -10000;
194 cave-detection-ocean-floor = -5;
195 };
196 };
197 defaultText = lib.literalExpression ''
198 {
199 "overworld" = {
200 world = "''${cfg.defaultWorld}";
201 ambient-light = 0.1;
202 cave-detection-ocean-floor = -5;
203 };
204
205 "nether" = {
206 world = "''${cfg.defaultWorld}/DIM-1";
207 sorting = 100;
208 sky-color = "#290000";
209 void-color = "#150000";
210 ambient-light = 0.6;
211 world-sky-light = 0;
212 remove-caves-below-y = -10000;
213 cave-detection-ocean-floor = -5;
214 cave-detection-uses-block-light = true;
215 max-y = 90;
216 };
217
218 "end" = {
219 world = "''${cfg.defaultWorld}/DIM1";
220 sorting = 200;
221 sky-color = "#080010";
222 void-color = "#080010";
223 ambient-light = 0.6;
224 world-sky-light = 0;
225 remove-caves-below-y = -10000;
226 cave-detection-ocean-floor = -5;
227 };
228 };
229 '';
230 description = ''
231 Settings for files in `maps/`.
232 If you define anything here you must define everything yourself.
233 See the default for an example with good options for the different world types.
234 For valid values [consult upstream docs](https://github.com/BlueMap-Minecraft/BlueMap/blob/master/BlueMapCommon/src/main/resources/de/bluecolored/bluemap/config/maps/map.conf).
235 '';
236 };
237
238 storage = mkOption {
239 type = lib.types.attrsOf (
240 lib.types.submodule {
241 freeformType = format.type;
242 options = {
243 storage-type = mkOption {
244 type = lib.types.enum [
245 "FILE"
246 "SQL"
247 ];
248 description = "Type of storage config";
249 default = "FILE";
250 };
251 };
252 }
253 );
254 description = ''
255 Where the rendered map will be stored.
256 Unless you are doing something advanced you should probably leave this alone and configure webRoot instead.
257 [See upstream docs](https://github.com/BlueMap-Minecraft/BlueMap/tree/master/BlueMapCommon/src/main/resources/de/bluecolored/bluemap/config/storages)
258 '';
259 default = {
260 "file" = {
261 root = "${cfg.webRoot}/maps";
262 };
263 };
264 defaultText = lib.literalExpression ''
265 {
266 "file" = {
267 root = "''${config.services.bluemap.webRoot}/maps";
268 };
269 }
270 '';
271 };
272
273 packs = mkOption {
274 type = lib.types.attrsOf lib.types.pathInStore;
275 default = { };
276 description = ''
277 A set of resourcepacks, datapacks, and mods to extract resources from,
278 loaded in alphabetical order.
279 '';
280 };
281 };
282
283 config = lib.mkIf cfg.enable {
284 assertions = [
285 {
286 assertion = config.services.bluemap.eula;
287 message = ''
288 You have enabled bluemap but have not accepted minecraft's EULA.
289 You can achieve this through setting `services.bluemap.eula = true`
290 '';
291 }
292 ];
293
294 services.bluemap.coreSettings.accept-download = cfg.eula;
295
296 systemd.services."render-bluemap-maps" = lib.mkIf cfg.enableRender {
297 serviceConfig = {
298 Type = "oneshot";
299 Group = "nginx";
300 UMask = "026";
301 };
302 script = ''
303 ${lib.getExe pkgs.bluemap} -c ${configFolder} -gs -r
304 '';
305 };
306
307 systemd.timers."render-bluemap-maps" = lib.mkIf cfg.enableRender {
308 wantedBy = [ "timers.target" ];
309 timerConfig = {
310 OnCalendar = cfg.onCalendar;
311 Persistent = true;
312 Unit = "render-bluemap-maps.service";
313 };
314 };
315
316 services.nginx.virtualHosts = lib.mkIf cfg.enableNginx {
317 "${cfg.host}" = {
318 root = config.services.bluemap.webRoot;
319 locations = {
320 "@empty".return = "204";
321
322 "~* ^/maps/[^/]*/tiles/".extraConfig = ''
323 error_page 404 = @empty;
324 gzip_static always;
325 '';
326 };
327 };
328 };
329 };
330
331 meta = {
332 maintainers = with lib.maintainers; [
333 dandellion
334 h7x4
335 ];
336 };
337}