Compare commits

...

2 Commits

Author SHA1 Message Date
Martin Prokoph
333af2cfe3 docs: document all developer-facing methods 2025-11-20 21:45:16 +01:00
Martin Prokoph
0cf6dde5ef stonecutter: cleaner versioned code 2025-11-20 20:09:52 +01:00
6 changed files with 86 additions and 48 deletions

View File

@@ -112,7 +112,6 @@ public class MidnightLib {
/*? if neoforge {*/
/*public static List<LiteralArgumentBuilder<CommandSourceStack>> commands = new ArrayList<>();
public MidnightLib() {

View File

@@ -7,22 +7,17 @@ public class MidnightLibConfig extends MidnightConfig {
//? if fabric {
@Entry public static ConfigButton config_screen_list = PlatformFunctions.isModLoaded("modmenu") ? ConfigButton.MODMENU : ConfigButton.TRUE;
public enum ConfigButton {
TRUE, FALSE, MODMENU
}
public static boolean shouldShowButton() {
return config_screen_list.equals(ConfigButton.TRUE) || (config_screen_list.equals(ConfigButton.MODMENU) && !PlatformFunctions.isModLoaded("modmenu"));
}
//?} else {
/*@Entry public static ConfigButton config_screen_list = ConfigButton.FALSE;
public enum ConfigButton {
TRUE, FALSE
}
public static boolean shouldShowButton() {
return config_screen_list.equals(ConfigButton.TRUE);
}
*///?}
public enum ConfigButton {
TRUE, FALSE /*? if fabric {*/, MODMENU /*?}*/
}
}

View File

@@ -17,7 +17,7 @@ import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
//? if >= 1.21.9 {
import net.minecraft.client.input.MouseButtonEvent;
import net.minecraft.client.input.MouseButtonEvent;
//?}
public class ButtonEntry extends ContainerObjectSelectionList.Entry<ButtonEntry> {
@@ -37,13 +37,7 @@ public class ButtonEntry extends ContainerObjectSelectionList.Entry<ButtonEntry>
int scaledWidth = Minecraft.getInstance().getWindow().getGuiScaledWidth();
if (text != null && (!text.getString().contains("spacer") || !buttons.isEmpty())) {
title = new MultiLineTextWidget(12, 0,
//? if >= 1.21 {
Component.translationArg(text)
//?} else {
/*text.copy()
*///?}
, textRenderer).setCentered(centered);
title = new MultiLineTextWidget(12, 0, text.copy(), textRenderer).setCentered(centered);
if (info != null)
title.setTooltip(info.getTooltip(false));
title.setMaxWidth(!buttons.isEmpty() ? buttons.get(buttons.size() > 2 ? buttons.size() - 1 : 0).getX() - 16 : scaledWidth - 24);
@@ -70,13 +64,8 @@ public class ButtonEntry extends ContainerObjectSelectionList.Entry<ButtonEntry>
Optional.ofNullable(this.buttons.get(0)).ifPresent(widget -> {
int idMode = this.info.entry.idMode();
if (idMode != -1) context.renderItem(idMode == 0 ?
//? if >= 1.21.4 {
BuiltInRegistries.ITEM.getValue(ResourceLocation.tryParse(this.info.tempValue)).getDefaultInstance()
: BuiltInRegistries.BLOCK.getValue(ResourceLocation.tryParse(this.info.tempValue)).asItem().getDefaultInstance(),
//?} else {
/*BuiltInRegistries.ITEM.get(ResourceLocation.tryParse(this.info.tempValue)).getDefaultInstance()
: BuiltInRegistries.BLOCK.get(ResourceLocation.tryParse(this.info.tempValue)).asItem().getDefaultInstance(),
*///?}
BuiltInRegistries.ITEM./*? if >= 1.21.4 {*/ getValue /*?} else {*/ /*get*/ /*?}*/(ResourceLocation.tryParse(this.info.tempValue)).getDefaultInstance()
: BuiltInRegistries.BLOCK./*? if >= 1.21.4 {*/ getValue /*?} else {*/ /*get*/ /*?}*/(ResourceLocation.tryParse(this.info.tempValue)).asItem().getDefaultInstance(),
widget.getX() + widget.getWidth() - 18, y + 2);
});
}

View File

@@ -64,7 +64,11 @@ public abstract class MidnightConfig {
protected boolean reloadScreen = false;
public Class<? extends MidnightConfig> configClass;
public static <T extends MidnightConfig> T createInstance(String modid, Class<? extends MidnightConfig> configClass) { // This is basically an argumented constructor without the requirement of having one in each config class
/**
* This is basically an argumented constructor without the requirement of having one in each config class.<br>
* Not meant to be used externally.
* */
protected static <T extends MidnightConfig> T createInstance(String modid, Class<? extends MidnightConfig> configClass) {
try {
T instance = (T) configClass.getDeclaredConstructor().newInstance();
instance.modid = modid;
@@ -75,6 +79,11 @@ public abstract class MidnightConfig {
catch (Exception e) { throw new RuntimeException(e); }
}
/**
* Initializes the config by registering all fields annotated with {@link Entry} or {@link Comment}<br>
* @param modid Your mod's id
* @param config The class containing your mod's config
* */
public static void init(String modid, Class<? extends MidnightConfig> config) {
MidnightConfig instance = createInstance(modid, config);
@@ -89,7 +98,12 @@ public abstract class MidnightConfig {
instance.loadValuesFromJson();
}
public void addClientEntry(Field field, EntryInfo info) {
/**
* Loads the config entry and saves relevant information into the {@link EntryInfo} object.
* @param field The config entry's Java field
* @param info The {@link EntryInfo} object to associate with this field
* */
protected void addClientEntry(Field field, EntryInfo info) {
Entry e = info.entry;
if (e != null && info.dataType != null) {
if (info.dataType == int.class) textField(info, Integer::parseInt, INTEGER_ONLY, (int) e.min(), (int) e.max(), true);
@@ -117,6 +131,12 @@ public abstract class MidnightConfig {
entries.put(modid + ":" + field.getName(), info);
}
/**
* Identifies a field's underlying data type.<br>
* For non-primitive data types, the class of the primitive equivalent is returned.<br>
* For lists, this is the data type of list entries.
* @param field The field to investigate
* */
public static Class<?> getUnderlyingType(Field field) {
Class<?> rawType = field.getType();
if (field.getType() == List.class)
@@ -125,18 +145,15 @@ public abstract class MidnightConfig {
} catch (NoSuchFieldException | IllegalAccessException ignored) { return rawType; }
}
private static void textField(EntryInfo info, Function<String,Number> f, Pattern pattern, double min, double max, boolean cast) {
/**
* Defines a function to validate number, text, identifier or color inputs and saves it into the {@link EntryInfo} object.
* */
protected static void textField(EntryInfo info, Function<String,Number> f, Pattern pattern, double min, double max, boolean cast) {
boolean isNumber = pattern != null;
info.function = (BiFunction<EditBox, Button, Predicate<String>>) (t, b) -> s -> {
s = s.trim();
if (!(s.isEmpty() || !isNumber || pattern.matcher(s).matches()) ||
(info.dataType == ResourceLocation.class && ResourceLocation.read(s)
//? if >= 1.21 {
.isError()
//?} else {
/*.error().isPresent()
*///?}
)) return false;
(info.dataType == ResourceLocation.class && ResourceLocation.read(s)./*? if >= 1.21 {*/isError() /*?} else {*/ /*error().isPresent()*/ /*?}*/)) return false;
Number value = 0; boolean inLimits = false; info.error = null;
if (!(isNumber && s.isEmpty()) && !s.equals("-") && !s.equals(".")) {
@@ -169,6 +186,11 @@ public abstract class MidnightConfig {
};
}
/**
* Gets the translated title of an enum option
* @param value the enum option to translate
* @param info the associated {@link EntryInfo} object
* */
protected Component getEnumTranslatableText(Object value, EntryInfo info) {
if (value instanceof OptionEnum translatableOption) return translatableOption.getCaption();
@@ -176,6 +198,9 @@ public abstract class MidnightConfig {
return I18n.exists(translationKey) ? Component.translatable(translationKey) : Component.literal(info.toTemporaryValue());
}
/**
* (Re-)Loads the config by reading json file defined at {@link #getJsonFilePath()}
* */
public void loadValuesFromJson() {
try {
gson.fromJson(Files.newBufferedReader(getJsonFilePath()), configClass);
@@ -194,15 +219,28 @@ public abstract class MidnightConfig {
});
}
/**
* Writes the mod's current config state to disk.
* @param modid Specifies which mod's config to save.
* */
public static void write(String modid) {
configInstances.get(modid).writeChanges(modid);
}
/**
* DO NOT USE OR OVERRIDE!<br>
* This is only present to keep compatibility with mods that were overriding the previous method.
* */
@Deprecated
public void writeChanges(String modid) {
this.writeChanges();
}
/**
* Writes the mod's current config state to disk.<br>
* This method can be overridden to define custom onSave behaviour.<br>
* Make sure to call {@code super.writeChanges()}!
* */
public void writeChanges() {
try {
Path path;
@@ -212,23 +250,48 @@ public abstract class MidnightConfig {
} catch (Exception e) { e.fillInStackTrace(); }
}
/**
* Gets the path to store the config json file at.<br>
* Override to set a custom file path.
* */
public Path getJsonFilePath() {
return PlatformFunctions.getConfigDirectory().resolve(modid + ".json");
}
@SuppressWarnings("unused") // Utility for mod authors
/**
* Gets a config field's default value.
* @param modid The entry's mod id
* @param entry The entry's field name
* */
@SuppressWarnings("unused")
public static @Nullable Object getDefaultValue(String modid, String entry) {
String key = modid + ":" + entry;
return entries.containsKey(key) ? entries.get(key).defaultValue : null;
}
// Overridable method
/**
* Add custom widgets to the config screen by overriding this method.
* @param tabName Name of the currently selected tab
* @param list The scrollable list containing regular config entries
* @param screen The entire config screen
* */
public void onTabInit(String tabName, MidnightConfigListWidget list, MidnightConfigScreen screen) {
}
/**
* Creates an instance of the config screen.
* @param parent The parent screen, which will be returned to when exiting the config
* @param modid The mod of which to load the config screen
* */
public static MidnightConfigScreen getScreen(Screen parent, String modid) {
return configInstances.get(modid).getScreen(parent);
}
/**
* Creates an instance of the config screen.
* This can be overridden to return a fully custom config screen.
* @param parent The parent screen, which will be returned to when exiting the config
* */
public MidnightConfigScreen getScreen(Screen parent) {
return new MidnightConfigScreen(parent, modid);
}

View File

@@ -18,19 +18,11 @@ public class MidnightConfigListWidget extends ContainerObjectSelectionList<Butto
public boolean renderHeaderSeparator = true;
public MidnightConfigListWidget(Minecraft client, int width, int height, int y, int itemHeight) {
//? if >= 1.21 {
super(client, width, height, y, itemHeight);
//?} else {
/*super(client, width, height, y, height + y, itemHeight);
*///?}
super(client, width, height, y, /*? if < 1.21 {*/ /*height + y,*/ /*?}*/ itemHeight);
}
@Override
//? if >= 1.21.4 {
public int scrollBarX() {
//?} else {
/*public int getScrollbarPosition() {
*///?}
public int /*? if >= 1.21.4 {*/ scrollBarX() /*?} else {*/ /*getScrollbarPosition()*/ /*?}*/ {
return this.width - 7;
}

View File

@@ -30,7 +30,7 @@ import net.minecraft.client.input.KeyEvent;
//?}
//? if >=1.21 {
import net.minecraft.client.gui.components.SpriteIconButton;
import net.minecraft.client.gui.components.SpriteIconButton;
//?}
public class MidnightConfigScreen extends Screen {
@@ -79,7 +79,7 @@ public class MidnightConfigScreen extends Screen {
updateList();
list.setScrollAmount(0);
}
scrollProgress = /*? < 1.21.4 {*/ /*list.getScrollAmount() *//*?} else {*/ list.scrollAmount() /*?}*/;
scrollProgress = /*? if < 1.21.4 {*/ /*list.getScrollAmount() *//*?} else {*/ list.scrollAmount() /*?}*/;
for (EntryInfo info : MidnightConfig.entries.values())
if (Objects.equals(modid, info.modid)) info.updateFieldValue();
updateButtons();