Home Manager module for Ninjabrain-Bot w/ Stylix support
mcsr
minecraft
ninjabrain-bot
1{ self, inputs }:
2{
3 config,
4 pkgs,
5 lib,
6 ...
7}:
8let
9 # values from https://javadoc.io/doc/com.1stleg/jnativehook/2.0.2/constant-values.html
10 MODIFIER_MASKS = {
11 ALT_L = 8;
12 ALT_R = 128;
13 CTRL_L = 2;
14 CTRL_R = 32;
15 META_L = 4;
16 META_R = 64;
17 SHIFT_L = 1;
18 SHIFT_R = 16;
19 };
20 LOCATION_VALUES = {
21 UNKNOWN = 0;
22 STANDARD = 1;
23 RIGHT = 3;
24 NUMPAD = 4;
25 LEFT = 2;
26 };
27
28 cfg = config.programs.ninjabrain-bot;
29
30 # filters null & expands hotkeys into two attributes
31 hotkeyExpandedSettings =
32 let
33 expandHotkey = name: value: {
34 # reference: https://github.com/Ninjabrain1/Ninjabrain-Bot/blob/34117a46d700610aab0237f73bd9418a09940e5f/src/main/java/ninjabrainbot/io/preferences/HotkeyPreference.java#L64
35 # equivalent to (key | location << 16)
36 "${name}_code" = builtins.bitOr value.key (LOCATION_VALUES.${value.location} * 65536);
37 "${name}_modifier" = value.modifiers;
38 };
39 in
40 lib.concatMapAttrs (
41 name: value:
42 if builtins.isNull value then
43 { }
44 else if (builtins.isAttrs value) && (value ? "key") then
45 expandHotkey name value
46 else
47 { ${name} = value; }
48 ) cfg.settings;
49
50 mkNinbLines =
51 settings:
52 let
53 valueString =
54 value:
55 if builtins.isBool value then (if value then "true" else "false") else builtins.toString value;
56 in
57 lib.concatLines (
58 lib.mapAttrsToList (k: v: "<entry key=\"${k}\" value=\"${valueString v}\"/>") settings
59 );
60
61 mkStylixTheme =
62 lib.pipe
63 (pkgs.runCommand "generate-ninb-theme" { } ''
64 cat ${config.lib.stylix.colors.outPath} | ${
65 lib.getExe' self.packages.${pkgs.stdenv.hostPlatform.system}.serializer "serializer"
66 } > $out
67 '')
68 [
69 builtins.readFile
70 lib.escapeXML
71 ];
72
73 mkEnumOption =
74 enum: default:
75 let
76 big_enum = (builtins.length enum) > 2;
77 in
78 lib.mkOption {
79 inherit default;
80 description = ''
81 When translated to XML, this option is turned into an integer,
82 with `0` being "${builtins.head enum}",${
83 lib.optionalString (!big_enum) " and"
84 } `1` being "${builtins.elemAt enum 1}"${lib.optionalString big_enum ", and so on"}.
85 '';
86 type = lib.types.enum enum;
87 apply = value: lib.lists.findFirstIndex (x: x == value) 0 enum;
88 };
89
90 mkHotkeyOption =
91 name:
92 lib.mkOption {
93 description = "Hotkey for ${name}";
94 default = null;
95 type = lib.types.nullOr (
96 lib.types.submodule {
97 options = {
98 modifiers = lib.mkOption {
99 type = lib.types.listOf (
100 lib.types.enum [
101 "ALT_L"
102 "ALT_R"
103 "CTRL_L"
104 "CTRL_R"
105 "META_L"
106 "META_R"
107 "SHIFT_L"
108 "SHIFT_R"
109 ]
110 );
111 default = [ ];
112 example = [ "CTRL_L" ];
113 description = "List of modifier keys.";
114 # bitwise or of all the values summed together
115 apply = list: builtins.foldl' (acc: element: builtins.bitOr acc MODIFIER_MASKS.${element}) 0 list;
116 };
117 location = lib.mkOption {
118 type = lib.types.enum [
119 "STANDARD"
120 "RIGHT"
121 "NUMPAD"
122 "LEFT"
123 "UNKNOWN"
124 ];
125 default = "UNKNOWN";
126 description = ''
127 The location of the key.
128
129 If you want to refer to '0' on your numpad, you would set this value to NUMPAD. Most of the time, the default is what you want.
130 '';
131 };
132 key = lib.mkOption {
133 type = lib.types.int;
134 example = 19;
135 description = ''
136 Integer keycode.
137
138 These can be obtained by finding the key from the list of constants at https://javadoc.io/doc/com.1stleg/jnativehook/2.0.2/constant-values.html.
139
140 For example, the key "A" corresponds to the field "VC_A", which has the value 30, so you should set this value to 30.
141
142 The key "R" would correspond to "VC_R", F6 to "VC_F6", etc.
143 '';
144 };
145 };
146 }
147 );
148 };
149in
150{
151 options.programs.ninjabrain-bot = {
152 enable = lib.mkEnableOption "ninjabrain-bot";
153 package =
154 lib.mkPackageOption inputs.mcsr-nixos.packages.${pkgs.stdenv.hostPlatform.system} "ninjabrain-bot"
155 {
156 extraDescription = "Default's to uku's ninjabrain-bot package. Setting to null will skip installing the package.";
157 nullable = true;
158 pkgsText = "mcsrPkgs";
159 };
160
161 stylix = lib.mkEnableOption "style ninjabrain-bot with stylix";
162
163 force = lib.mkOption {
164 type = lib.types.bool;
165 description = "Ninjabrain-bot will wipe the symlink to the nix store in it's config file, so this option exists to always force and overwrite the file back into place.";
166 default = false;
167 };
168
169 additional_custom_themes = lib.mkOption {
170 type = lib.types.listOf (
171 lib.types.submodule {
172 options = {
173 name = lib.mkOption {
174 type = lib.types.strMatching "^[^.]*$";
175 description = "Name of the theme. Must not contain a `.`";
176 };
177 value = lib.mkOption {
178 type = lib.types.strMatching "^[^.]*$";
179 description = ''
180 Theme colours encoded string. Find by creating the theme in the GUI then copying it from the preferences file.
181 Must not contain a `.`.
182 '';
183 };
184 };
185 }
186 );
187 default = [ ];
188 description = ''
189 List of custom themes.
190 Obtaining a theme string is quite hard and it's recommended you create one in the GUI first then copying it manually from the preferences file.
191 Ninjabrain-bot separates themes by a `.`.
192 '';
193 };
194
195 settings = lib.mkOption {
196 default = { };
197 description = "Settings for ninjabrain-bot";
198 type = lib.types.submodule {
199 options = {
200 theme = lib.mkOption {
201 type = lib.types.int;
202 description = ''
203 Theme index.
204 Positive values refer to the builtin themes that come with ninjabrain bot.
205 Negative values refer to custom themes, backwards. `-1` would refer to the first custom theme, `-2` the second, and so on.
206 '';
207 default = if cfg.stylix then -1 else 1;
208 };
209
210 custom_themes = lib.mkOption {
211 type = lib.types.str;
212 internal = true;
213 visible = false;
214 default = lib.join "." (
215 (map (x: x.value) cfg.additional_custom_themes) ++ (lib.optional cfg.stylix mkStylixTheme)
216 );
217 };
218
219 custom_themes_names = lib.mkOption {
220 type = lib.types.str;
221 internal = true;
222 visible = false;
223 default = lib.join "." (
224 (map (x: x.name) cfg.additional_custom_themes) ++ (lib.optional cfg.stylix "Stylix")
225 );
226 };
227
228 settings_version = lib.mkOption {
229 type = lib.types.ints.positive;
230 default = 3;
231 internal = true;
232 visible = false;
233 };
234
235 window_x = lib.mkOption {
236 type = lib.types.int;
237 default = 100;
238 description = "Window X position";
239 };
240
241 window_y = lib.mkOption {
242 type = lib.types.int;
243 default = 100;
244 description = "Window Y position";
245 };
246
247 sensitivity_manual = lib.mkOption {
248 type = lib.types.numbers.between 0 1;
249 default = 0.4341732;
250 };
251
252 sigma = lib.mkOption {
253 type = lib.types.numbers.between 0.001 1;
254 default = 0.1;
255 };
256
257 sigma_alt = lib.mkOption {
258 type = lib.types.numbers.between 0.001 1;
259 default = 0.1;
260 };
261
262 sigma_manual = lib.mkOption {
263 type = lib.types.numbers.between 0.001 1;
264 default = 0.03;
265 };
266
267 sigma_boat = lib.mkOption {
268 type = lib.types.numbers.between 0.0001 1;
269 default = 0.001;
270 };
271
272 resolution_height = lib.mkOption {
273 type = lib.types.numbers.between 1 16384;
274 default = 16384;
275 };
276
277 boat_error = lib.mkOption {
278 type = lib.types.numbers.between 0 0.7;
279 default = 0.03;
280 };
281
282 overlay_hide_delay = lib.mkOption {
283 type = lib.types.numbers.between 1 3600;
284 default = 30.0;
285 };
286
287 sensitivity = lib.mkOption {
288 type = lib.types.numbers.between 0 1;
289 default = 0.012727597;
290 };
291
292 custom_adjustment = lib.mkOption {
293 type = lib.types.numbers.between 0 1;
294 default = 0.01;
295 };
296
297 crosshair_correction = lib.mkOption {
298 type = lib.types.numbers.between (-1) 1;
299 default = 0;
300 };
301
302 check_for_updates = lib.mkOption {
303 type = lib.types.bool;
304 default = true;
305 };
306
307 translucent = lib.mkOption {
308 type = lib.types.bool;
309 default = false;
310 };
311
312 always_on_top = lib.mkOption {
313 type = lib.types.bool;
314 default = true;
315 };
316
317 show_nether_coords = lib.mkOption {
318 type = lib.types.bool;
319 default = true;
320 };
321
322 show_angle_updates = lib.mkOption {
323 type = lib.types.bool;
324 default = false;
325 };
326
327 show_angle_errors = lib.mkOption {
328 type = lib.types.bool;
329 default = false;
330 };
331
332 auto_reset = lib.mkOption {
333 type = lib.types.bool;
334 default = false;
335 description = "Automatically reset the calculator after some period of time.";
336 };
337
338 auto_reset_on_instance_change = lib.mkOption {
339 type = lib.types.bool;
340 default = false;
341 };
342
343 use_adv_statistics = lib.mkOption {
344 type = lib.types.bool;
345 default = true;
346 };
347
348 alt_clipboard_reader = lib.mkOption {
349 type = lib.types.bool;
350 default = false;
351 description = "Whether or not to use the 'alternative clipboard reader'";
352 };
353
354 use_alt_std = lib.mkOption {
355 type = lib.types.bool;
356 default = false;
357 };
358
359 color_negative_coords = lib.mkOption {
360 type = lib.types.bool;
361 default = false;
362 };
363
364 use_precise_angle = lib.mkOption {
365 type = lib.types.bool;
366 default = false;
367 };
368
369 use_obs_overlay = lib.mkOption {
370 type = lib.types.bool;
371 default = false;
372 };
373
374 save_state = lib.mkOption {
375 type = lib.types.bool;
376 default = true;
377 };
378
379 enable_http_server = lib.mkOption {
380 type = lib.types.bool;
381 default = false;
382 description = "Enable the HTTP API server";
383 };
384
385 overlay_auto_hide = lib.mkOption {
386 type = lib.types.bool;
387 default = false;
388 };
389
390 overlay_lock_hide = lib.mkOption {
391 type = lib.types.bool;
392 default = false;
393 };
394
395 all_advancements = lib.mkOption {
396 type = lib.types.bool;
397 default = false;
398 };
399
400 one_dot_twenty_plus_aa = lib.mkOption {
401 type = lib.types.bool;
402 default = false;
403 };
404
405 mismeasure_warning_enabled = lib.mkOption {
406 type = lib.types.bool;
407 default = false;
408 };
409
410 direction_help_enabled = lib.mkOption {
411 type = lib.types.bool;
412 default = false;
413 };
414
415 combined_offset_information_enabled = lib.mkOption {
416 type = lib.types.bool;
417 default = true;
418 };
419
420 portal_linking_warning_enabled = lib.mkOption {
421 type = lib.types.bool;
422 default = true;
423 };
424
425 language_v2 = lib.mkOption {
426 description = "Language code. Leave to default for the system language.";
427 type = lib.types.str;
428 default = "";
429 };
430
431 size = mkEnumOption [ "small" "medium" "large" ] "small";
432 stronghold_display_type = mkEnumOption [ "fourfour" "eighteight" "chunk" ] "fourfour";
433 view = mkEnumOption [ "basic" "detailed" ] "basic";
434 mc_version = mkEnumOption [ "pre_119" "post_119" ] "pre_119";
435 aa_toggle_type = mkEnumOption [ "automatic" "hotkey" ] "automatic";
436 default_boat_type = mkEnumOption [ "gray" "blue" "green" ] "gray";
437 angle_adjustment_type = mkEnumOption [ "subpixel" "tall" "custom" ] "subpixel";
438 angle_adjustment_display_type = mkEnumOption [ "angle_change" "increments" ] "angle_change";
439
440 hotkey_increment = mkHotkeyOption "increment";
441 hotkey_decrement = mkHotkeyOption "decrement";
442 hotkey_reset = mkHotkeyOption "reset";
443 hotkey_undo = mkHotkeyOption "undo";
444 hotkey_redo = mkHotkeyOption "redo";
445 hotkey_minimize = mkHotkeyOption "minimize";
446 hotkey_alt_std = mkHotkeyOption "alt_std";
447 hotkey_lock = mkHotkeyOption "lock";
448 hotkey_boat = mkHotkeyOption "boat";
449 hotkey_mod_360 = mkHotkeyOption "mod_360";
450 hotkey_toggle_aa_mode = mkHotkeyOption "toggle_aa_mode";
451 };
452 };
453 };
454 };
455
456 config = lib.mkMerge [
457 (lib.mkIf cfg.enable {
458 home.file.".java/.userPrefs/ninjabrainbot/prefs.xml" = {
459 inherit (cfg) force;
460
461 text = ''
462 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
463 <!DOCTYPE map SYSTEM "http://java.sun.com/dtd/preferences.dtd">
464 <map MAP_XML_VERSION="1.0">
465 ''
466 + (mkNinbLines hotkeyExpandedSettings)
467 + ''
468 </map>
469 '';
470 };
471 })
472
473 (lib.mkIf (!(builtins.isNull cfg.package)) {
474 home.packages = [ cfg.package ];
475 })
476 ];
477}