---
layout: ../../layouts/MarkdownLayout.astro
title: MidnightLib Wiki
---

import GradleVersionInfo from '../../components/GradleVersionInfo.astro'
import CustomTabs from "../../components/CustomTabs.astro"
import { Icon } from 'astro-icon/components'
import { Notification } from 'accessible-astro-components'

# MidnightLib Wiki

Welcome to the MidnightLib wiki. Thanks for showing interest in our library!
This documentation aims to show you how to use the config system and the other features the library provides.
Use the tabs on the right side to switch between the different pages.

Explore MidnightLib's features on this page:
[Get to know the features](/midnightlib)

To use the library in your mods, just edit `build.gradle` and `gradle.properties` as seen below:
<h3 data-tab-condition="kotlin">`build.gradle.kts (Kotlin DSL)`</h3>
<h3 data-tab-condition="groovy" class="hidden">`build.gradle (Groovy DSL)`</h3>
<h3 data-tab-condition="kotlin-legacy" class="hidden">`build.gradle.kts (Kotlin DSL)`</h3>
<h3 data-tab-condition="groovy-legacy" class="hidden">`build.gradle (Groovy DSL)`</h3>
<CustomTabs tabs={[
{
    name: `build.gradle.kts`,
    id: 'kotlin',
    icon: 'simple-icons:kotlin',
    active: true
},
{
    name: `build.gradle`,
    icon: 'simple-icons:apachegroovy',
    id: 'groovy'
},
{
    name: `build.gradle.kts (Legacy)`,
    id: 'kotlin-legacy',
    icon: 'simple-icons:kotlin'
},
{
    name: `build.gradle (Legacy)`,
    icon: 'simple-icons:apachegroovy',
    id: 'groovy-legacy'
}
]}>
    <div data-tab-condition="kotlin" data-active="true">
    ```kotlin
    repositories {
      // other repos...
      maven("https://maven.midnightdust.eu/releases")
    }
    dependencies {
      // other dependencies...
      val midnightlib = "eu.midnightdust:midnightlib:${project.midnightlib_version}"
      modImplementation(midnightlib)
      include(midnightlib)
    }
    ```
    </div>
    <div data-tab-condition="groovy" class="hidden">
    ```groovy
    repositories {
      // other repos...
      maven {
        url = "https://maven.midnightdust.eu/releases"
      }
    }
    dependencies {
      // other dependencies...
      modImplementation include ("eu.midnightdust:midnightlib:${project.midnightlib_version}")
    }
    ```
    </div>
    <div data-tab-condition="kotlin-legacy" class="hidden">
    ```kotlin
    repositories {
      // other repos...
      maven("https://api.modrinth.com/maven")
    }
    dependencies {
      // other dependencies...
      val midnightlib = "maven.modrinth:midnightlib:${project.midnightlib_version}"
      modImplementation(midnightlib)
      include(midnightlib)
    }
    ```
    </div>
    <div data-tab-condition="groovy-legacy" class="hidden">
    ```groovy
    repositories {
      // other repos...
      maven {
        url = "https://api.modrinth.com/maven"
      }
    }
    dependencies {
      // other dependencies...
      modImplementation include ("maven.modrinth:midnightlib:${project.midnightlib_version}")
    }
    ```
    </div>
</CustomTabs>
<Notification type="warning">
  <Icon name="ion:information-circle-outline" />
  <p>
    <strong>Legacy:</strong> For versions below 1.9.0, use the Modrinth maven repo provided in the legacy tabs.
  </p>
</Notification>
### `gradle.properties`
<GradleVersionInfo modid='midnightlib' />

<Notification type="info">
  <Icon name="ion:information-circle-outline" />
  <p>
    <strong>Info:</strong> You should always pick the version that suits your modloader and Minecraft version best.
    Forge support for 1.21+ is retired in favor of NeoForge.
  </p>
</Notification>

### `YourConfigClass.java`
To get started with implementing the config, you must create a public class that extends MidnightConfig.
In this class, your variables can be stored and accessed. Here you can see the contents of an example config class:
```java
package eu.midnightdust.fabric.example.config;

import com.google.common.collect.Lists;
import eu.midnightdust.lib.config.MidnightConfig;
import net.minecraft.util.Identifier;

import javax.swing.*;
import java.util.ArrayList;
import java.util.List;

/** Every option in a MidnightConfig class has to be public and static, so we can access it from other classes.
 * The config class also has to extend MidnightConfig*/

public class MidnightConfigExample extends MidnightConfig {
    public static final String TEXT = "text";
    public static final String NUMBERS = "numbers";
    public static final String SLIDERS = "sliders";
    public static final String LISTS = "lists";
    public static final String FILES = "files";
    public static final String CONDITIONS = "conditions";

    @Comment(category = TEXT) public static Comment text1;                       // Comments are rendered like an option without a button and are excluded from the config file
    @Comment(category = TEXT, centered = true) public static Comment text2;      // Centered comments are the same as normal ones - just centered!
    @Comment(category = TEXT) public static Comment spacer1;                     // Comments containing the word "spacer" will just appear as a blank line
    @Entry(category = TEXT) public static boolean showInfo = true;               // Example for a boolean option
    @Entry(category = TEXT, name="I am a (non-primitive) Boolean") public static Boolean nonPrimitive = true;           // Example for a non-primative boolean option
    @Entry(category = TEXT) public static String name = "Hello World!";          // Example for a string option, which is in a category!
    @Entry(category = TEXT, width = 7, min = 7, isColor = true, name = "I am a color!") public static String titleColor = "#ffffff"; // The isColor property adds a color chooser for a hexadecimal color
    @Entry(category = TEXT, idMode = 0) public static Identifier id = Identifier.ofVanilla("diamond");          // Example for an identifier with matching items displayed next to it!
    @Entry(category = TEXT) public static TestEnum testEnum = TestEnum.FABRIC;   // Example for an enum option
    public enum TestEnum {                               // Enums allow the user to cycle through predefined options
        QUILT, FABRIC, FORGE
    }
    @Entry(category = NUMBERS) public static int fabric = 16777215;                 // Example for an int option
    @Entry(category = NUMBERS) public static double world = 1.4D;                   // Example for a double option
    @Entry(category = NUMBERS, min=69,max=420) public static int hello = 420;   // - The entered number has to be larger than 69 and smaller than 420
    @Entry(category = SLIDERS, name = "I am an int slider.",isSlider = true, min = 0, max = 100) public static int intSlider = 35; // Int fields can also be displayed as a Slider
    @Entry(category = SLIDERS, name = "I am a float slider!", isSlider = true, min = 0f, max = 1f, precision = 1000) public static float floatSlider = 0.24f; // And so can floats! Precision defines the amount of decimal places
    @Entry(category = SLIDERS, name = "I am a non-primitive double slider!", isSlider = true, min = 0d, max = 4d, precision = 10000) public static Double nonPrimitiveDoubleSlider = 3.76d; // Even works for non-primitive fields
    // The name field can be used to specify a custom translation string or plain text
    @Entry(category = LISTS, name = "I am a string list!") public static List<String> stringList = Lists.newArrayList("String1", "String2"); // Array String Lists are also supported
    @Entry(category = LISTS, isColor = true, name = "I am a color list!") public static List<String> colorList = Lists.newArrayList("#ac5f99", "#11aa44"); // Lists also support colors
    @Entry(category = LISTS, name = "I am an identifier list!", idMode = 1) public static List<Identifier> idList = Lists.newArrayList(Identifier.ofVanilla("dirt")); // A list of block identifiers
    @Entry(category = LISTS, name = "I am an integer list!") public static List<Integer> intList = Lists.newArrayList(69, 420);
    @Entry(category = LISTS, name = "I am a float list!") public static List<Float> floatList = Lists.newArrayList(4.1f, -1.3f, -1f);

    @Entry(category = FILES,
            selectionMode = JFileChooser.FILES_ONLY,
            fileExtensions = {"json", "txt", "log"}, // Define valid file extensions
            fileChooserType = JFileChooser.SAVE_DIALOG,
            name = "I am a file!")
    public static String myFile = ""; // The isFile property adds a file picker button

    @Entry(category = FILES,
            selectionMode = JFileChooser.DIRECTORIES_ONLY,
            fileChooserType = JFileChooser.OPEN_DIALOG,
            name = "I am a directory!")
    public static String myDirectory = ""; // The isDirectory property adds a directory picker button

    @Entry(category = FILES,
            selectionMode = JFileChooser.FILES_AND_DIRECTORIES,
            fileExtensions = {"png", "jpg", "jpeg"},
            fileChooserType = JFileChooser.OPEN_DIALOG,
            name = "I can choose both files & directories!")
    public static String myFileOrDirectory = ""; // The isFileOrDirectory property adds a file or directory picker button
    @Entry(category = FILES,
            selectionMode = JFileChooser.FILES_AND_DIRECTORIES,
            fileExtensions = {"png", "jpg", "jpeg"},
            fileChooserType = JFileChooser.OPEN_DIALOG,
            name = "I am a mf file/directory list!")
    public static List<String> fileOrDirectoryList = new ArrayList<>(); // Yes, that's right – you can even have lists of files/directories

    @Condition(requiredModId = "midnightlib") // Conditional options are here!
    @Entry(category = CONDITIONS, name="Turn me on!")
    public static boolean turnMeOn = false;
    @Condition(requiredOption = "modid:turnMeOn", visibleButLocked = true)
    @Entry(category = CONDITIONS, name="Turn me off!")
    public static Boolean turnMeOff = true;
    @Condition(requiredOption = "modid:turnMeOff", requiredValue = "false")
    @Entry(category = CONDITIONS, name="Which is the best modloader?")
    public static String bestModloader = "";
    @Condition(requiredOption = "bestModloader", requiredValue = "Forge")
    @Comment(category = CONDITIONS, name="❌ You have bad taste :(", centered = true) // Don't take this too seriously btw :)
    public static Comment answerForge;   // Comments can also be conditional!
    @Condition(requiredOption = "bestModloader", requiredValue = "NeoForge")
    @Comment(category = CONDITIONS, name="⛏ Not quite, but it's alright!", centered = true)
    public static Comment answerNeoforge;
    @Condition(requiredOption = "bestModloader", requiredValue = "Fabric")
    @Comment(category = CONDITIONS, name="⭐ Correct! Fabric (and Quilt) are the best!", centered = true)
    public static Comment answerFabric;
    @Condition(requiredOption = "bestModloader", requiredValue = "Quilt")
    @Comment(category = CONDITIONS, name="⭐ Correct! Quilt (and Fabric) are the best!", centered = true)
    public static Comment answerQuilt;

    @Condition(requiredOption = "midnightlib:config_screen_list", requiredValue = "FALSE") // Access options of other mods that are also using MidnightLib
    @Comment(category = CONDITIONS) public static Comment spaceracer;
    @Condition(requiredOption = "midnightlib:config_screen_list", requiredValue = "FALSE")
    @Comment(category = CONDITIONS, name="You disabled MidnightLib's config screen list. Why? :(", centered = true)  public static Comment why;

    public static int imposter = 16777215; // - Entries without an @Entry or @Comment annotation are ignored
}
```
### `assets/modid/lang/`**`en_us.json`**
The .json language file for your config class could look similar to this:
```json
{
  "modid.midnightconfig.title":"I am a title",
	// "*.midnightconfig.title" defines the title of the screen
	"modid.midnightconfig.text1":"I am a comment *u*",
	// Translation for the comment "text1" defined in the example config
	"modid.midnightconfig.text2":"I am a centered comment (╯°□°）╯︵ ┻━┻",
	"modid.midnightconfig.name":"I am a string!",
	// Translation for the field "name" defined in the example config
	"modid.midnightconfig.name.label.tooltip":"I am a label tooltip \nWohoo!",
	// When hovering over the *text label* for the option "name",
	// this text will appear as a tooltip.
	// "\n" inserts a line break.
	"modid.midnightconfig.name.tooltip":"I am a tooltip uwu \nI am a new line",
	// When hovering over the *button* for the option "name",
	// this text will appear as a tooltip.
	// "\n" inserts a line break.
	"modid.midnightconfig.fabric":"I am an int",
	"modid.midnightconfig.world":"I am a double",
	"modid.midnightconfig.showInfo":"I am a boolean",
	"modid.midnightconfig.hello":"I am a limited int!",
	"modid.midnightconfig.id":"I am an Item Identifier!",
	"modid.midnightconfig.testEnum":"I am an enum!",
	"modid.midnightconfig.enum.TestEnum.FORGE":"Slow",
	"modid.midnightconfig.enum.TestEnum.FABRIC":"Fancy",
	"modid.midnightconfig.enum.TestEnum.QUILT":"Fabulous",
	"modid.midnightconfig.myFileOrDirectory.fileChooser": "Select an image or directory",
	"modid.midnightconfig.myFileOrDirectory.fileFilter": "Supported Images (.png, .jpg, .jpeg)",
	"modid.midnightconfig.category.numbers": "Numbers",
	"modid.midnightconfig.category.text": "Text",
	"modid.midnightconfig.category.sliders": "Sliders",
	"modid.midnightconfig.category.lists": "Lists",
	"modid.midnightconfig.category.files": "Files",
  "modid.midnightconfig.category.conditions": "Quiz"
}
```

### `YourModInitializer.java`
To initialize the config you **have** to call:
```java
MidnightConfig.init("modid", MidnightConfigExample.class);
```
in your ModInitializer.
If you want it to show up properly in ModMenu, be sure to do so in the main initializer (instead of the client/dedicated server one).

To get an instance of the config screen you can call:
```java
MidnightConfig.getScreen(parent, "modid");
```

### `ModMenuInit.java`
If you don't use the whole library and therefore not the automatic ModMenu integration, the code in your ModMenu integration class would look something like this:
```java
@Override
public ConfigScreenFactory<?> getModConfigScreenFactory() {
  return parent -> MidnightConfig.getScreen(parent, "modid");
}
```
