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