mirror of
https://github.com/TeamMidnightDust/MidnightLib.git
synced 2025-12-15 09:05:08 +01:00
Enable commands in all environments
- Many command-related fixes and improvements - Added test environment for easier development
This commit is contained in:
@@ -8,7 +8,7 @@ import net.fabricmc.api.Environment;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.UIManager;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -25,8 +25,7 @@ public class MidnightLib {
|
||||
} catch (Exception e) { LOGGER.error("Error setting system look and feel", e); }
|
||||
MidnightLibConfig.init(MOD_ID, MidnightLibConfig.class);
|
||||
}
|
||||
@Environment(EnvType.SERVER)
|
||||
public static void onInitializeServer() {
|
||||
public static void onInitialize() {
|
||||
MidnightConfig.configClass.forEach((modid, config) -> {
|
||||
for (Field field : config.getFields()) {
|
||||
if (field.isAnnotationPresent(MidnightConfig.Entry.class) && !field.isAnnotationPresent(MidnightConfig.Client.class) && !field.isAnnotationPresent(MidnightConfig.Hidden.class))
|
||||
|
||||
@@ -6,11 +6,16 @@ import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.api.Environment;
|
||||
import net.minecraft.client.gui.DrawContext;
|
||||
import net.minecraft.client.gui.screen.Screen;
|
||||
import net.minecraft.client.gui.widget.*;
|
||||
import net.minecraft.client.gui.widget.ButtonWidget;
|
||||
import net.minecraft.screen.ScreenTexts;
|
||||
import net.minecraft.text.*;
|
||||
import net.minecraft.text.Text;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import static eu.midnightdust.lib.config.MidnightConfig.MidnightConfigListWidget;
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
public class MidnightConfigOverviewScreen extends Screen {
|
||||
@@ -20,19 +25,19 @@ public class MidnightConfigOverviewScreen extends Screen {
|
||||
this.parent = parent;
|
||||
}
|
||||
private final Screen parent;
|
||||
private MidnightConfig.MidnightConfigListWidget list;
|
||||
private MidnightConfigListWidget list;
|
||||
|
||||
@Override
|
||||
protected void init() {
|
||||
this.addDrawableChild(ButtonWidget.builder(ScreenTexts.DONE, (button) -> Objects.requireNonNull(client).setScreen(parent)).dimensions(this.width / 2 - 100, this.height - 26, 200, 20).build());
|
||||
|
||||
this.addSelectableChild(this.list = new MidnightConfig.MidnightConfigListWidget(this.client, this.width, this.height - 57, 24, 25));
|
||||
this.addSelectableChild(this.list = new MidnightConfigListWidget(this.client, this.width, this.height - 57, 24, 25));
|
||||
List<String> sortedMods = new ArrayList<>(MidnightConfig.configClass.keySet());
|
||||
Collections.sort(sortedMods);
|
||||
sortedMods.forEach((modid) -> {
|
||||
if (!MidnightLib.hiddenMods.contains(modid)) {
|
||||
list.addButton(List.of(ButtonWidget.builder(Text.translatable(modid +".midnightconfig.title"), (button) ->
|
||||
Objects.requireNonNull(client).setScreen(MidnightConfig.getScreen(this,modid))).dimensions(this.width / 2 - 125, this.height - 28, 250, 20).build()), null, null);
|
||||
Objects.requireNonNull(client).setScreen(MidnightConfig.getScreen(this, modid))).dimensions(this.width / 2 - 125, this.height - 28, 250, 20).build()), null, null);
|
||||
}});
|
||||
super.init();
|
||||
}
|
||||
@@ -40,7 +45,6 @@ public class MidnightConfigOverviewScreen extends Screen {
|
||||
public void render(DrawContext context, int mouseX, int mouseY, float delta) {
|
||||
super.render(context, mouseX, mouseY, delta);
|
||||
this.list.render(context, mouseX, mouseY, delta);
|
||||
|
||||
context.drawCenteredTextWithShadow(textRenderer, title, width / 2, 10, 0xFFFFFF);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
package eu.midnightdust.lib.config;
|
||||
|
||||
import com.mojang.brigadier.arguments.*;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import eu.midnightdust.lib.util.PlatformFunctions;
|
||||
import net.minecraft.server.command.CommandManager;
|
||||
@@ -9,47 +8,41 @@ import net.minecraft.server.command.ServerCommandSource;
|
||||
import net.minecraft.text.Text;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import static eu.midnightdust.lib.config.MidnightConfig.Entry;
|
||||
|
||||
public class AutoCommand {
|
||||
public static List<LiteralArgumentBuilder<ServerCommandSource>> commands = new ArrayList<>();
|
||||
final static String BASE = "midnightconfig";
|
||||
final static String VALUE = "value";
|
||||
final Field field;
|
||||
final Class<?> type;
|
||||
final String modid;
|
||||
final boolean isList;
|
||||
final boolean isList, isNumber;
|
||||
|
||||
public AutoCommand(Field field, String modid) {
|
||||
this.field = field;
|
||||
this.modid = modid;
|
||||
this.type = field.getType();
|
||||
this.field = field; this.modid = modid;
|
||||
this.type = MidnightConfig.getUnderlyingType(field);
|
||||
this.isList = field.getType() == List.class;
|
||||
this.isNumber = type == int.class || type == double.class || type == float.class;
|
||||
|
||||
LiteralArgumentBuilder<ServerCommandSource> command = CommandManager.literal(BASE).requires(source -> source.hasPermissionLevel(2)).then(
|
||||
CommandManager.literal(modid).then(CommandManager.literal(field.getName()).executes(this::getValue)));
|
||||
var command = CommandManager.literal(field.getName()).executes(this::getValue);
|
||||
|
||||
if (type.isEnum()) {
|
||||
for (Object enumValue : field.getType().getEnumConstants()) {
|
||||
for (Object enumValue : field.getType().getEnumConstants())
|
||||
command = command.then(CommandManager.literal(enumValue.toString()).executes(ctx -> this.setValue(ctx.getSource(), enumValue, "")));
|
||||
}
|
||||
}
|
||||
else if (isList) {
|
||||
for (String action : List.of("add", "remove")) {
|
||||
} else if (isList) {
|
||||
for (String action : List.of("add", "remove"))
|
||||
command = command.then(CommandManager.literal(action).then(
|
||||
CommandManager.argument(VALUE, getArgType()).executes(ctx -> setValueFromArg(ctx, action))));
|
||||
}
|
||||
}
|
||||
else command = command.then(CommandManager.argument(VALUE, getArgType()).executes(ctx -> setValueFromArg(ctx, "")));
|
||||
} else command = command.then(CommandManager.argument(VALUE, getArgType()).executes(ctx -> setValueFromArg(ctx, "")));
|
||||
|
||||
PlatformFunctions.registerCommand(command); commands.add(command);
|
||||
PlatformFunctions.registerCommand(CommandManager.literal("midnightconfig").requires(source -> source.hasPermissionLevel(2)).then(CommandManager.literal(modid).then(command)));
|
||||
}
|
||||
|
||||
public ArgumentType<?> getArgType() {
|
||||
MidnightConfig.Entry entry = type.getAnnotation(MidnightConfig.Entry.class);
|
||||
if (type.isInstance(Number.class)) {
|
||||
if (isNumber) {
|
||||
Entry entry = field.getAnnotation(Entry.class);
|
||||
if (type == int.class) return IntegerArgumentType.integer((int) entry.min(), (int) entry.max());
|
||||
else if (type == double.class) return DoubleArgumentType.doubleArg(entry.min(), entry.max());
|
||||
else if (type == float.class) return FloatArgumentType.floatArg((float) entry.min(), (float) entry.max());
|
||||
@@ -58,7 +51,7 @@ public class AutoCommand {
|
||||
return StringArgumentType.string();
|
||||
}
|
||||
public int setValueFromArg(CommandContext<ServerCommandSource> context, String action) {
|
||||
if (type.isInstance(Number.class)) {
|
||||
if (isNumber) {
|
||||
if (type == int.class) return setValue(context.getSource(), IntegerArgumentType.getInteger(context, VALUE), action);
|
||||
else if (type == double.class) return setValue(context.getSource(), DoubleArgumentType.getDouble(context, VALUE), action);
|
||||
else if (type == float.class) return setValue(context.getSource(), FloatArgumentType.getFloat(context, VALUE), action);
|
||||
|
||||
@@ -107,15 +107,10 @@ public abstract class MidnightConfig {
|
||||
}
|
||||
@Environment(EnvType.CLIENT)
|
||||
private static void initClient(String modid, Field field, EntryInfo info) {
|
||||
info.dataType = field.getType();
|
||||
info.dataType = getUnderlyingType(field);
|
||||
Entry e = field.getAnnotation(Entry.class);
|
||||
info.width = e != null ? e.width() : 0;
|
||||
info.field = field; info.modid = modid;
|
||||
if (info.dataType == List.class) {
|
||||
Class<?> listType = (Class<?>) ((ParameterizedType) info.field.getGenericType()).getActualTypeArguments()[0];
|
||||
try { info.dataType = (Class<?>) listType.getField("TYPE").get(null);
|
||||
} catch (NoSuchFieldException | IllegalAccessException ignored) { info.dataType = listType; }
|
||||
}
|
||||
|
||||
if (e != null) {
|
||||
if (!e.name().isEmpty()) info.name = Text.translatable(e.name());
|
||||
@@ -138,6 +133,13 @@ public abstract class MidnightConfig {
|
||||
}}
|
||||
entries.add(info);
|
||||
}
|
||||
public static Class<?> getUnderlyingType(Field field) {
|
||||
if (field.getType() == List.class) {
|
||||
Class<?> listType = (Class<?>) ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0];
|
||||
try { return (Class<?>) listType.getField("TYPE").get(null);
|
||||
} catch (NoSuchFieldException | IllegalAccessException ignored) { return listType; }
|
||||
} else return field.getType();
|
||||
}
|
||||
public static Tooltip getTooltip(EntryInfo info) {
|
||||
String key = info.modid + ".midnightconfig."+info.field.getName()+".tooltip";
|
||||
return Tooltip.of(info.error != null ? info.error : I18n.hasTranslation(key) ? Text.translatable(key) : Text.empty());
|
||||
|
||||
@@ -2,13 +2,13 @@ package eu.midnightdust.fabric.core;
|
||||
|
||||
import eu.midnightdust.core.MidnightLib;
|
||||
import net.fabricmc.api.ClientModInitializer;
|
||||
import net.fabricmc.api.DedicatedServerModInitializer;
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.api.Environment;
|
||||
import net.fabricmc.api.ModInitializer;
|
||||
|
||||
public class MidnightLibFabric implements ClientModInitializer, DedicatedServerModInitializer {
|
||||
public class MidnightLibFabric implements ModInitializer, ClientModInitializer {
|
||||
@Override @Environment(EnvType.CLIENT)
|
||||
public void onInitializeClient() {MidnightLib.onInitializeClient();}
|
||||
@Override @Environment(EnvType.SERVER)
|
||||
public void onInitializeServer() {MidnightLib.onInitializeServer();}
|
||||
@Override
|
||||
public void onInitialize() {MidnightLib.onInitialize();}
|
||||
}
|
||||
|
||||
@@ -29,10 +29,10 @@
|
||||
|
||||
"environment": "*",
|
||||
"entrypoints": {
|
||||
"client": [
|
||||
"main": [
|
||||
"eu.midnightdust.fabric.core.MidnightLibFabric"
|
||||
],
|
||||
"server": [
|
||||
"client": [
|
||||
"eu.midnightdust.fabric.core.MidnightLibFabric"
|
||||
],
|
||||
"modmenu": [
|
||||
|
||||
@@ -9,6 +9,8 @@ import net.neoforged.fml.loading.FMLPaths;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static eu.midnightdust.neoforge.MidnightLibNeoForge.commands;
|
||||
|
||||
public class PlatformFunctionsImpl {
|
||||
public static String getPlatformName() {
|
||||
return "neoforge";
|
||||
@@ -26,6 +28,6 @@ public class PlatformFunctionsImpl {
|
||||
return ModList.get().isLoaded(modid);
|
||||
}
|
||||
public static void registerCommand(LiteralArgumentBuilder<ServerCommandSource> command) {
|
||||
// Ignored here, see MidnightLibNeoForge#registerCommands
|
||||
commands.add(command);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,11 +15,16 @@ import net.neoforged.fml.loading.FMLEnvironment;
|
||||
import net.neoforged.neoforge.client.gui.IConfigScreenFactory;
|
||||
import net.neoforged.neoforge.event.RegisterCommandsEvent;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Mod("midnightlib")
|
||||
public class MidnightLibNeoForge {
|
||||
public static List<LiteralArgumentBuilder<ServerCommandSource>> commands = new ArrayList<>();
|
||||
|
||||
public MidnightLibNeoForge() {
|
||||
if (FMLEnvironment.dist == Dist.CLIENT) MidnightLib.onInitializeClient();
|
||||
else MidnightLib.onInitializeServer();
|
||||
MidnightLib.onInitialize();
|
||||
}
|
||||
|
||||
@EventBusSubscriber(modid = "midnightlib", bus = EventBusSubscriber.Bus.MOD, value = Dist.CLIENT)
|
||||
@@ -38,9 +43,7 @@ public class MidnightLibNeoForge {
|
||||
public static class MidnightLibServerEvents {
|
||||
@SubscribeEvent
|
||||
public static void registerCommands(RegisterCommandsEvent event) {
|
||||
for (LiteralArgumentBuilder<ServerCommandSource> command : AutoCommand.commands) {
|
||||
event.getDispatcher().register(command);
|
||||
}
|
||||
commands.forEach(command -> event.getDispatcher().register(command));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,9 @@ pluginManagement {
|
||||
|
||||
include("common")
|
||||
include("fabric")
|
||||
//include("quilt")
|
||||
include("test-fabric")
|
||||
include("neoforge")
|
||||
include("test-neoforge")
|
||||
//include("quilt")
|
||||
|
||||
rootProject.name = "midnightlib"
|
||||
|
||||
32
test-fabric/build.gradle
Normal file
32
test-fabric/build.gradle
Normal file
@@ -0,0 +1,32 @@
|
||||
plugins {
|
||||
id 'com.github.johnrengelman.shadow'
|
||||
id "me.shedaniel.unified-publishing"
|
||||
}
|
||||
repositories {
|
||||
maven { url "https://maven.terraformersmc.com/releases" }
|
||||
}
|
||||
|
||||
architectury {
|
||||
platformSetupLoomIde()
|
||||
fabric()
|
||||
}
|
||||
|
||||
loom {
|
||||
}
|
||||
|
||||
configurations {
|
||||
common
|
||||
shadowCommon // Don't use shadow from the shadow plugin since it *excludes* files.
|
||||
compileClasspath.extendsFrom common
|
||||
runtimeClasspath.extendsFrom common
|
||||
developmentFabric.extendsFrom common
|
||||
archivesBaseName = rootProject.archives_base_name + "-fabric"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
modImplementation "net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}"
|
||||
modApi "net.fabricmc.fabric-api:fabric-api:${rootProject.fabric_api_version}"
|
||||
|
||||
implementation project(path: ":fabric", configuration: "namedElements")
|
||||
common(project(path: ":common", configuration: "namedElements")) { transitive false }
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package eu.midnightdust.fabric.example;
|
||||
|
||||
import eu.midnightdust.fabric.example.config.MidnightConfigExample;
|
||||
import net.fabricmc.api.ModInitializer;
|
||||
|
||||
public class MLExampleFabric implements ModInitializer {
|
||||
@Override
|
||||
public void onInitialize() {
|
||||
MidnightConfigExample.init("modid", MidnightConfigExample.class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
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";
|
||||
|
||||
@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) 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
|
||||
// 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
|
||||
|
||||
public static int imposter = 16777215; // - Entries without an @Entry or @Comment annotation are ignored
|
||||
}
|
||||
23
test-fabric/src/main/resources/assets/modid/en_US.json
Normal file
23
test-fabric/src/main/resources/assets/modid/en_US.json
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"modid.midnightconfig.title":"I am a title",
|
||||
"modid.midnightconfig.text1":"I am a comment *u*",
|
||||
"modid.midnightconfig.text2":"I am a centered comment (╯°□°)╯︵ ┻━┻",
|
||||
"modid.midnightconfig.name":"I am a string!",
|
||||
"modid.midnightconfig.name.tooltip":"I am a tooltip uwu \nI am a new line",
|
||||
"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"
|
||||
}
|
||||
23
test-fabric/src/main/resources/fabric.mod.json
Normal file
23
test-fabric/src/main/resources/fabric.mod.json
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "midnightlib-example",
|
||||
"version": "${version}",
|
||||
|
||||
"name": "MidnightLib Example",
|
||||
"description": "Wow, you can do so much.",
|
||||
"authors": [ "MidnightDust" ],
|
||||
|
||||
"license": "CC0",
|
||||
"icon": "assets/midnightlib/icon.png",
|
||||
|
||||
"environment": "*",
|
||||
"entrypoints": {
|
||||
"main": [
|
||||
"eu.midnightdust.fabric.example.MLExampleFabric"
|
||||
]
|
||||
},
|
||||
"depends": {
|
||||
"fabric-resource-loader-v0": "*",
|
||||
"midnightlib": ">=1.6.0"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user