Compare commits

...
18 Commits
Author SHA1 Message Date
DraylarandGitHub 48f5962f57 Merge pull request #3 from frqnny/modularize
Some more important changes
2021-06-15 23:06:11 -05:00
ffrann 6099b30f3d Add new mo structures as test 2021-06-15 23:48:09 -04:00
ffrann 09658ffee6 Fix mixin compat level being set to JAVA_16
Delete NoValidConstructorException.java (waste of space smh)
2021-06-15 23:39:08 -04:00
ffrann b6b7a67866 1.0.7 update :) 2021-06-11 12:08:11 -04:00
DraylarandGitHub 73730feced Merge pull request #2 from frqnny/modularize
Port to java 16 and MC 1.17
2021-05-27 10:43:32 -05:00
ffrann f468546eea Port to java 16 and MC 1.17 2021-05-26 20:27:14 -04:00
Draylar c2d7cacdab different approach to jitpack j16 support 2021-05-26 14:52:34 -05:00
Draylar c21e5ff3ff force openjdk16 for jitpack 2021-05-26 14:43:31 -05:00
Draylar a6d2e3b1f8 1.0.5 update 2021-05-26 14:30:56 -05:00
Draylar 0ff71f0a09 add support for gradle 7 / j16 2021-05-26 14:30:32 -05:00
Draylar 1b636fa0d8 adjust class config test 2021-04-05 19:19:56 -05:00
Draylar 1e3e886346 adjust error handling to catch all errors when reading config 2021-04-03 17:23:36 -05:00
Draylar 8b42f5a9c5 refactor tests + add static class test 2021-04-03 17:14:37 -05:00
Draylar 9e9eac06fc implement custom file extensions and directories with appropriate tests, vbump to 1.0.4 2021-03-25 11:32:56 -05:00
Draylar 4e24988fa3 steal mo structures config for testing and make it workm, 1.0.3 2021-03-13 17:17:40 -06:00
Draylar e0f1ef8b5e vbump to 1.0.2 2021-03-13 16:19:27 -06:00
Draylar c6e72489cc add proguard output to gitignore 2021-03-13 16:19:16 -06:00
Draylar a2611a3302 wildcard deps for loader and mc versions 2021-03-13 16:19:06 -06:00
32 changed files with 378 additions and 188 deletions
+2
View File
@@ -123,3 +123,5 @@ run/
/run/
/server-run/
/out.map
/omega-config-base/out.map
/omega-config-gui/out.map
+12 -6
View File
@@ -4,13 +4,15 @@
*The last config library you will ever use.*
ΩConfig is a hyper-minimal config library based on [Auto Config](https://github.com/shedaniel/AutoConfig). It aims to achieve
the following goals:
ΩConfig is a hyper-minimal config library based on [Auto Config](https://github.com/shedaniel/AutoConfig). It aims to
achieve the following goals:
- Be lightweight (<10 KB) for JIJ usage
- Exceedingly simple design & API for developers
- Intuition and usability for players
The following is an example of a simple ΩConfig setup:
```java
public class TestConfig implements Config {
@@ -24,6 +26,7 @@ public class TestConfig implements Config {
}
```
```java
public class MyModInitializer {
@@ -35,7 +38,9 @@ public class MyModInitializer {
}
}
```
---
### Extra API Utilities
ΩConfig provides several utility methods for developers.
@@ -43,11 +48,12 @@ public class MyModInitializer {
**save()** - *saves a modified configuration instance to disk*
```java
MyModInitializer.CONFIG.value = false;
MyModInitializer.CONFIG.save(); // writes the new value to disk
MyModInitializer.CONFIG.value=false;
MyModInitializer.CONFIG.save(); // writes the new value to disk
```
---
### License
ΩConfig is available under Public Domain.
You are encouraged to utilize the code in this repository in any way you wish.
ΩConfig is available under Public Domain. You are encouraged to utilize the code in this repository in any way you wish.
+19 -17
View File
@@ -5,17 +5,16 @@ import java.nio.charset.StandardCharsets
buildscript {
repositories {
mavenLocal()
jcenter()
google()
}
dependencies {
classpath 'com.guardsquare:proguard-gradle:7.0.1'
classpath 'com.guardsquare:proguard-gradle:7.1.0-beta4'
}
}
plugins {
id "fabric-loom" version "0.7-SNAPSHOT"
id "fabric-loom" version "0.8-SNAPSHOT"
id "maven-publish"
id "java-library"
}
@@ -105,10 +104,10 @@ subprojects {
tasks.publish.dependsOn build //stupid fix for maven/loom not publishing the main artifact
task ('proguard', type: proguard.gradle.ProGuardTask) {
task('proguard', type: proguard.gradle.ProGuardTask) {
configuration './proguard.conf'
verbose
injars project.buildDir.toString() + '/libs/' + archivesBaseName + "-" + project.mod_version + ".jar"
injars project.buildDir.toString() + '/libs/' + archivesBaseName + "-" + project.mod_version + ".jar"
outjars project.buildDir.toString() + '/libs/' + archivesBaseName + "-" + project.mod_version + "-min.jar"
printmapping 'out.map'
keepparameternames
@@ -137,7 +136,7 @@ subprojects {
allprojects {
apply plugin: "fabric-loom"
sourceCompatibility = targetCompatibility = JavaVersion.VERSION_1_8
sourceCompatibility = targetCompatibility = JavaVersion.VERSION_16
version = System.getenv("TRAVIS_TAG") ?: project.mod_version
repositories {
@@ -146,7 +145,7 @@ allprojects {
url = "https://maven.fabricmc.net"
}
maven { url = "https://maven.terraformersmc.com/releases/"}
maven { url = "https://maven.terraformersmc.com/releases/" }
}
dependencies {
@@ -155,10 +154,19 @@ allprojects {
mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2"
modImplementation "net.fabricmc:fabric-loader:${project.loader_version}"
modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"
modRuntime "com.terraformersmc:modmenu:1.16.8"
modCompileOnly "com.terraformersmc:modmenu:1.16.8"
modRuntime "com.terraformersmc:modmenu:2.0.0-beta.4"
modCompileOnly "com.terraformersmc:modmenu:2.0.0-beta.4"
}
}
jar {
from(rootProject.file("LICENSE.md")) {
rename { "LICENSE_${project.archivesBaseName.replace('-', '_')}" }
}
}
afterEvaluate {
processResources {
// this will ensure that this task is redone when there"s a change
inputs.property "version", mod_version
@@ -176,12 +184,6 @@ allprojects {
exclude "fabric.mod.json"
}
}
jar {
from(rootProject.file("LICENSE.md")) {
rename { "LICENSE_${project.archivesBaseName.replace('-', '_')}"}
}
}
}
sourceSets {
@@ -207,8 +209,8 @@ repositories {
}
dependencies {
compile project(":omega-config-base")
compile project(":omega-config-gui")
implementation project(":omega-config-base")
implementation project(":omega-config-gui")
modRuntime "com.terraformersmc:modmenu:1.16.8"
modCompileOnly "com.terraformersmc:modmenu:1.16.8"
+6 -9
View File
@@ -1,17 +1,14 @@
# Done to increase the memory available to gradle.
org.gradle.jvmargs=-Xmx1G
# Fabric Properties
# check these on https://modmuss50.me/fabric.html
minecraft_version=1.16.5
yarn_mappings=1.16.5+build.5
loader_version=0.11.2
minecraft_version=1.17
yarn_mappings=1.17+build.7
loader_version=0.11.3
#Fabric api
fabric_version=0.34.10+1.17
# Mod Properties
mod_version=1.0.1
mod_version=1.0.8
maven_group=draylar
archives_base_name=omega-config
# Dependencies
# check this on https://modmuss50.me/fabric.html
fabric_version=0.32.0+1.16
+1 -1
View File
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
+4
View File
@@ -0,0 +1,4 @@
before_install:
- wget https://github.com/sormuras/bach/raw/master/install-jdk.sh
- source install-jdk.sh --feature 16
- jshell --version
@@ -4,14 +4,13 @@ import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import draylar.omegaconfig.api.Comment;
import draylar.omegaconfig.api.Config;
import draylar.omegaconfig.exception.NoValidConstructorException;
import draylar.omegaconfig.gson.SyncableExclusionStrategy;
import io.netty.buffer.Unpooled;
import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.nbt.NbtList;
import net.minecraft.network.PacketByteBuf;
import net.minecraft.util.Identifier;
import org.apache.logging.log4j.LogManager;
@@ -28,36 +27,33 @@ import java.util.*;
public class OmegaConfig {
private static final Logger LOGGER = LogManager.getLogger();
private static final List<Config> REGISTERED_CONFIGURATIONS = new ArrayList<>();
public static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();
public static final Identifier CONFIG_SYNC_PACKET = new Identifier("omegaconfig", "sync");
public static final Gson SYNC_ONLY_GSON = new GsonBuilder().addSerializationExclusionStrategy(new SyncableExclusionStrategy()).setPrettyPrinting().create();
private static final Logger LOGGER = LogManager.getLogger();
private static final List<Config> REGISTERED_CONFIGURATIONS = new ArrayList<>();
static {
ServerPlayConnectionEvents.JOIN.register((handler, sender, server) -> {
server.execute(() -> {
PacketByteBuf packet = new PacketByteBuf(Unpooled.buffer());
ServerPlayConnectionEvents.JOIN.register((handler, sender, server) -> server.execute(() -> {
PacketByteBuf packet = new PacketByteBuf(Unpooled.buffer());
// list of configurations that are synced
CompoundTag root = new CompoundTag();
ListTag configurations = new ListTag();
// list of configurations that are synced
NbtCompound root = new NbtCompound();
NbtList configurations = new NbtList();
// Iterate over each configuration.
// Find values that should be synced and send the value over.
OmegaConfig.getRegisteredConfigurations().forEach(config -> {
if(config.hasAnySyncable()) {
configurations.add(config.writeSyncingTag());
}
});
// save to packet and send to user
root.put("Configurations", configurations);
packet.writeCompoundTag(root);
handler.sendPacket(ServerPlayNetworking.createS2CPacket(CONFIG_SYNC_PACKET, packet));
// Iterate over each configuration.
// Find values that should be synced and send the value over.
OmegaConfig.getRegisteredConfigurations().forEach(config -> {
if (config.hasAnySyncable()) {
configurations.add(config.writeSyncingTag());
}
});
});
// save to packet and send to user
root.put("Configurations", configurations);
packet.writeNbt(root);
handler.sendPacket(ServerPlayNetworking.createS2CPacket(CONFIG_SYNC_PACKET, packet));
}));
}
public static <T extends Config> T register(Class<T> configClass) {
@@ -69,8 +65,8 @@ public class OmegaConfig {
// We want to provide access to the config as soon as it is created, so we:
// 1. serialize to disk if the config does not already exist
// 2. read from disk if it does exist
if(!configExists(config)) {
writeConfig(configClass, config);
if (!configExists(config)) {
config.save();
REGISTERED_CONFIGURATIONS.add(config);
} else {
try {
@@ -82,39 +78,41 @@ public class OmegaConfig {
T object = GSON.fromJson(res.toString(), configClass);
// re-write the config to add new values
writeConfig(configClass, object);
object.save();
REGISTERED_CONFIGURATIONS.add(object);
return object;
} catch (IOException ioException) {
LOGGER.error(ioException);
LOGGER.info(String.format("Read error, using default values for config %s.", configClass.toString()));
} catch (Exception e) {
LOGGER.error(e);
LOGGER.info(String.format("Encountered an error while reading %s config, falling back to default values.", config.getName()));
LOGGER.info(String.format("If this problem persists, delete the config file %s and try again.", config.getName() + "." + config.getExtension()));
}
}
return config;
} catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException exception) {
throw new NoValidConstructorException();
exception.printStackTrace();
throw new RuntimeException("No valid constructor found for: " + configClass.getName());
}
}
public static <T extends Config> void writeConfig(Class<T> configClass, T instance) {
public static <T extends Config> void writeConfig(Class<T> configClass, T instance) {
// Write the config to disk with the default values.
String json = GSON.toJson(instance);
// Cursed time.
List<String> lines = new ArrayList<>(Arrays.asList(json.split("\n")));
Map<Integer, String> insertions = new HashMap<>();
Map<Integer, String> insertions = new TreeMap<>();
Map<String, String> keyToComments = new HashMap<>();
// populate key -> comments map
for(Field field : configClass.getDeclaredFields()) {
for (Field field : configClass.getDeclaredFields()) {
addFieldComments(field, keyToComments);
}
// get inner-class fields
// TODO: recursively get inner classes?
for(Class<?> innerClass : configClass.getDeclaredClasses()) {
for(Field field : innerClass.getDeclaredFields()) {
for (Class<?> innerClass : configClass.getDeclaredClasses()) {
for (Field field : innerClass.getDeclaredFields()) {
addFieldComments(field, keyToComments);
}
}
@@ -136,17 +134,23 @@ public class OmegaConfig {
}
// insertions -> list
insertions.forEach(lines::add);
for (Map.Entry<Integer, String> entry : insertions.entrySet()) {
Integer key = entry.getKey();
String value = entry.getValue();
lines.add(key, value);
}
// list -> string
StringBuilder res = new StringBuilder();
lines.forEach(str -> res.append(String.format("%s%n", str)));
try {
Files.write(getConfigPath(instance), res.toString().getBytes());
Path configPath = getConfigPath(instance);
configPath.toFile().getParentFile().mkdirs();
Files.write(configPath, res.toString().getBytes());
} catch (IOException ioException) {
LOGGER.error(ioException);
LOGGER.info(String.format("Write error, using default values for config %s.", configClass.toString()));
LOGGER.info(String.format("Write error, using default values for config %s.", configClass));
}
}
@@ -156,7 +160,7 @@ public class OmegaConfig {
// Find comment
for (Annotation annotation : annotations) {
if(annotation instanceof Comment) {
if (annotation instanceof Comment) {
keyToComments.put(fieldName, ((Comment) annotation).value());
break;
}
@@ -171,23 +175,23 @@ public class OmegaConfig {
* "p" -> ""
* " p" -> " "
*
* @param input input to retrieve whitespaces from
* @return starting whitespaces from the given input
* @param input input to retrieve whitespaces from
* @return starting whitespaces from the given input
*/
private static String getStartingWhitespace(String input) {
int index = -1;
char[] chars = input.toCharArray();
for(int i = 0; i < chars.length; i++) {
for (int i = 0; i < chars.length; i++) {
char at = chars[i];
if(at != ' ') {
if (at != ' ') {
index = i;
break;
}
}
if(index != -1) {
if (index != -1) {
return input.substring(0, index);
} else {
return "";
@@ -195,7 +199,7 @@ public class OmegaConfig {
}
public static Path getConfigPath(Config config) {
return Paths.get(FabricLoader.getInstance().getConfigDir().toString(), String.format("%s.json", config.getName()));
return Paths.get(FabricLoader.getInstance().getConfigDir().toString(), config.getDirectory(), String.format("%s.%s", config.getName(), config.getExtension()));
}
public static boolean configExists(Config config) {
@@ -10,7 +10,7 @@ import java.lang.annotation.Target;
*
* <p>
* When a configuration is serialized, any field elements with the {@link Comment} annotation
* will be prefixed with a // comment on the previous line, with the value specified by this annotation.
* will be prefixed with a // comment on the previous line, with the value specified by this annotation.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@@ -1,7 +1,7 @@
package draylar.omegaconfig.api;
import draylar.omegaconfig.OmegaConfig;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtCompound;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
@@ -16,12 +16,12 @@ public interface Config {
OmegaConfig.writeConfig((Class<Config>) getClass(), this);
}
default CompoundTag writeSyncingTag() {
CompoundTag tag = new CompoundTag();
default NbtCompound writeSyncingTag() {
NbtCompound tag = new NbtCompound();
tag.putString("ConfigName", getName());
// all config vs. individual fields
if(Arrays.stream(getClass().getAnnotations()).anyMatch(annotation -> annotation instanceof Syncing)) {
if (Arrays.stream(getClass().getAnnotations()).anyMatch(annotation -> annotation instanceof Syncing)) {
// write ALL fields to tag
String json = OmegaConfig.GSON.toJson(this);
tag.putString("Serialized", json);
@@ -37,7 +37,7 @@ public interface Config {
}
/**
* @return true if this {@link Config} has any values that should be synced to the client
* @return true if this {@link Config} has any values that should be synced to the client
*/
default boolean hasAnySyncable() {
boolean hasSyncingField = Arrays.stream(getClass().getDeclaredFields()).anyMatch(field -> Arrays.stream(field.getDeclaredAnnotations()).anyMatch(annotation -> annotation instanceof Syncing));
@@ -55,7 +55,7 @@ public interface Config {
* <li>No special characters ($, %, ^, etc.)
* <li>No spaces
*
* @return the name of this config, which is used for the name of the config file saved to disk.
* @return the name of this config, which is used for the name of the config file saved to disk.
*/
String getName();
@@ -64,21 +64,47 @@ public interface Config {
*
* <p>
* This functionality is used for libraries like ModMenu, which depend on
* modids for configuration screen instances in their menu.
* modids for configuration screen instances in their menu.
* If you are intending for this config to have a ModMenu config
* screen, this method should return the modid specified in your fabric.mod.json.
*
* screen, this method should return the modid specified in your fabric.mod.json.
* <p>
* If this method is not overridden, null will be returned, which
* means this config is not explicitly associated with any particular mod.
* means this config is not explicitly associated with any particular mod.
*
* @return the modid of the mod associated with this config, or null if none was specified
* @return the modid of the mod associated with this config, or null if none was specified
*/
@Nullable
default String getModid() {
return null;
}
default boolean hasMenu() {
return true;
/**
* Returns the file extension of this config.
*
* <p>
* The file extension is used while serializing this config to a local file.
* The primary use-case of switching this would be supporting existing config files
* when porting from other json5 config libraries.
*
* @return the file extension of this config
*/
default String getExtension() {
return "json";
}
/**
* Returns the directory of this config, assuming the base directory is the instance config directory.
*
* <p>
* By default, a config such as 'my_config' will appear at /config/my_config.json.
* If this method specifies a directory, such as 'configurations',
* the config file will appear at /config/configurations/my_config.json.
* <p>
* Nested directories can be specified by using a string such as 'configurations/client'.
*
* @return the directory of this config
*/
default String getDirectory() {
return "";
}
}
@@ -1,5 +0,0 @@
package draylar.omegaconfig.exception;
public class NoValidConstructorException extends RuntimeException {
}
@@ -3,13 +3,12 @@ package draylar.omegaconfig.mixin;
import draylar.omegaconfig.OmegaConfig;
import draylar.omegaconfig.api.Config;
import draylar.omegaconfig.api.Syncing;
import net.fabricmc.fabric.api.network.ClientSidePacketRegistry;
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
import net.fabricmc.fabric.api.util.NbtType;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.RunArgs;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.nbt.NbtList;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
@@ -29,17 +28,17 @@ public class ClientMixin {
@Inject(
method = "<init>",
at = @At("RETURN"))
at = @At("RETURN"))
private void onReturn(RunArgs args, CallbackInfo ci) {
ClientSidePacketRegistry.INSTANCE.register(OmegaConfig.CONFIG_SYNC_PACKET, (context, buffer) -> {
CompoundTag tag = buffer.readCompoundTag();
ClientPlayNetworking.registerGlobalReceiver(OmegaConfig.CONFIG_SYNC_PACKET, (client, handler, buf, responseSender) -> {
NbtCompound tag = buf.readNbt();
savedClientConfig.clear();
context.getTaskQueue().execute(() -> {
if(tag != null && tag.contains("Configurations")) {
ListTag list = tag.getList("Configurations", NbtType.COMPOUND);
client.execute(() -> {
if (tag != null && tag.contains("Configurations")) {
NbtList list = tag.getList("Configurations", NbtType.COMPOUND);
list.forEach(compound -> {
CompoundTag syncedConfiguration = (CompoundTag) compound;
NbtCompound syncedConfiguration = (NbtCompound) compound;
String name = syncedConfiguration.getString("ConfigName");
String json = syncedConfiguration.getString("Serialized");
boolean allSync = syncedConfiguration.getBoolean("AllSync");
@@ -56,7 +55,7 @@ public class ClientMixin {
// locate all fields that differ between the client and server, assign values from server to client (this will mutate the stored object)
for (Field field : server.getClass().getDeclaredFields()) {
if(allSync || Arrays.stream(field.getAnnotations()).anyMatch(annotation -> annotation instanceof Syncing)) {
if (allSync || Arrays.stream(field.getAnnotations()).anyMatch(annotation -> annotation instanceof Syncing)) {
try {
field.setAccessible(true);
Object serverValue = field.get(server);
@@ -79,15 +78,15 @@ public class ClientMixin {
@Inject(
method = "disconnect(Lnet/minecraft/client/gui/screen/Screen;)V",
at = @At("RETURN"))
private void restoreConfigurations(Screen screen, CallbackInfo ci) {
for(Config config : savedClientConfig) {
for(Config potentiallySynced : OmegaConfig.getRegisteredConfigurations()) {
if(config.getName().equals(potentiallySynced.getName())) {
private void restoreConfigurations(CallbackInfo ci) {
for (Config config : savedClientConfig) {
for (Config potentiallySynced : OmegaConfig.getRegisteredConfigurations()) {
if (config.getName().equals(potentiallySynced.getName())) {
boolean allConfigSyncs = Arrays.stream(config.getClass().getAnnotations()).anyMatch(annotation -> annotation instanceof Syncing);
// mutate object in registered configurations
for (Field field : config.getClass().getDeclaredFields()) {
if(allConfigSyncs || Arrays.stream(field.getAnnotations()).anyMatch(annotation -> annotation instanceof Syncing)) {
if (allConfigSyncs || Arrays.stream(field.getAnnotations()).anyMatch(annotation -> annotation instanceof Syncing)) {
try {
field.setAccessible(true);
Object preSyncValue = field.get(config);
@@ -1,11 +1,12 @@
{
"schemaVersion": 1,
"id": "omega-config",
"version": "${version}",
"version": "1.0.8",
"name": "OmegaConfig",
"description": "The last config solution you will ever use.",
"authors": [
"Draylar"
"Draylar",
"Frqnny"
],
"contact": {},
"license": "MIT",
@@ -14,8 +15,8 @@
"omega-config.mixins.json"
],
"depends": {
"fabricloader": ">=0.11.2",
"fabricloader": "*",
"fabric": "*",
"minecraft": "1.16.5"
"minecraft": "*"
}
}
@@ -2,9 +2,8 @@
"required": true,
"minVersion": "0.8",
"package": "draylar.omegaconfig.mixin",
"compatibilityLevel": "JAVA_8",
"compatibilityLevel": "JAVA_16",
"mixins": [
],
"client": [
"ClientMixin"
+1 -1
View File
@@ -1,3 +1,3 @@
dependencies {
compile project(":omega-config-base")
implementation project(":omega-config-base")
}
@@ -12,11 +12,12 @@ public class OmegaConfigGui {
/**
* Registers a ModMenu configuration screen for the given {@link Config} instance.
* @param config registered config to create a ModMenu screen for
* @param <T> config type
*
* @param config registered config to create a ModMenu screen for
* @param <T> config type
*/
public static <T extends Config> void registerConfigScreen(T config) {
if(FabricLoader.getInstance().isModLoaded("modmenu")) {
if (FabricLoader.getInstance().isModLoaded("modmenu")) {
ModMenuHelper.injectScreen(config, parent -> new OmegaConfigScreen<>(config, parent));
}
}
@@ -5,19 +5,16 @@ import draylar.omegaconfig.api.Config;
import draylar.omegaconfiggui.api.screen.widget.LabelWidget;
import draylar.omegaconfiggui.api.screen.widget.TypeWidgets;
import draylar.omegaconfiggui.api.screen.widget.WidgetSupplier;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.Drawable;
import net.minecraft.client.gui.Element;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.widget.AbstractButtonWidget;
import net.minecraft.client.gui.widget.ButtonWidget;
import net.minecraft.client.gui.widget.ClickableWidget;
import net.minecraft.client.gui.widget.TextFieldWidget;
import net.minecraft.client.render.BufferBuilder;
import net.minecraft.client.render.Tessellator;
import net.minecraft.client.render.VertexFormat;
import net.minecraft.client.render.VertexFormats;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.text.LiteralText;
import net.minecraft.util.Identifier;
import net.minecraft.util.Pair;
import java.lang.reflect.Field;
@@ -32,7 +29,7 @@ public class OmegaConfigScreen<T extends Config> extends Screen {
private static final List<Class<?>> validClasses = Arrays.asList(Double.class, String.class, Boolean.class);
private final T config;
private final Screen parent;
private final Map<Field, Pair<WidgetSupplier<Object, AbstractButtonWidget>, AbstractButtonWidget>> fieldWidgets = new HashMap<>();
private final Map<Field, Pair<WidgetSupplier<Object, ClickableWidget>, ClickableWidget>> fieldWidgets = new HashMap<>();
public OmegaConfigScreen(T config, Screen parent) {
super(new LiteralText(""));
@@ -54,24 +51,24 @@ public class OmegaConfigScreen<T extends Config> extends Screen {
Class<?> unbox = TypeWidgets.unbox(field.getType());
// label
addChild(new LabelWidget(150, 15 + height / 6 + 20 * over, new LiteralText(field.getName())));
addDrawable(new LabelWidget(150, 15 + height / 6 + 20 * over, new LiteralText(field.getName())));
// get value
field.setAccessible(true);
Object value = field.get(config);
// button / interact
AbstractButtonWidget button;
WidgetSupplier<Object, AbstractButtonWidget> widgetSupplier = (WidgetSupplier<Object, AbstractButtonWidget>) TypeWidgets.get(unbox);
if(widgetSupplier != null) {
button = widgetSupplier.create(this,250, 5 + height / 6 + 25 * over, 100, 20, new LiteralText(field.getName()), value);
ClickableWidget button;
WidgetSupplier<Object, ClickableWidget> widgetSupplier = (WidgetSupplier<Object, ClickableWidget>) TypeWidgets.get(unbox);
if (widgetSupplier != null) {
button = widgetSupplier.create(this, 250, 5 + height / 6 + 25 * over, 100, 20, new LiteralText(field.getName()), value);
} else {
button = new TextFieldWidget(client.textRenderer, 250, 5 + height / 6 + 25 * over, 100, 20, new LiteralText(field.getName()));
}
fieldWidgets.put(field, new Pair<>(widgetSupplier, button));
addButton(button);
addSelectableChild(button);
over++;
}
@@ -92,15 +89,17 @@ public class OmegaConfigScreen<T extends Config> extends Screen {
config.save();
});
addButton(save);
addButton(exit);
addSelectableChild(save);
addSelectableChild(exit);
}
@Override
public void render(MatrixStack matrices, int mouseX, int mouseY, float delta) {
renderBackground(matrices);
for (AbstractButtonWidget button : this.buttons) {
//TODO make this work. for Drawables (children) it works in parent method
/*
for (ClickableWidget button : this.) {
button.render(matrices, mouseX, mouseY, delta);
}
@@ -109,6 +108,8 @@ public class OmegaConfigScreen<T extends Config> extends Screen {
((Drawable) element).render(matrices, mouseX, mouseY, delta);
}
}
*/
}
@Override
@@ -129,13 +130,13 @@ public class OmegaConfigScreen<T extends Config> extends Screen {
Tessellator tessellator = Tessellator.getInstance();
BufferBuilder bufferBuilder = tessellator.getBuffer();
this.client.getTextureManager().bindTexture(OPTIONS_BACKGROUND_TEXTURE);
RenderSystem.color4f(1.0F, 1.0F, 1.0F, 1.0F);
RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F);
bufferBuilder.begin(7, VertexFormats.POSITION_TEXTURE_COLOR);
bufferBuilder.vertex(x, height + y, 0.0D).texture(0.0F, (float)height / 32.0F + (float)vOffset).color(r, g, b, 255).next();
bufferBuilder.vertex(width + x, height + y, 0.0D).texture((float)width / 32.0F, (float)height / 32.0F + (float)vOffset).color(r, g, b, 255).next();
bufferBuilder.vertex(width + x, y, 0.0D).texture((float)width / 32.0F, (float)vOffset).color(r, g, b, 255).next();
bufferBuilder.vertex(x, y, 0.0D).texture(0.0F, (float)vOffset).color(r, g, b, 255).next();
bufferBuilder.begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_TEXTURE_COLOR);
bufferBuilder.vertex(x, height + y, 0.0D).texture(0.0F, (float) height / 32.0F + (float) vOffset).color(r, g, b, 255).next();
bufferBuilder.vertex(width + x, height + y, 0.0D).texture((float) width / 32.0F, (float) height / 32.0F + (float) vOffset).color(r, g, b, 255).next();
bufferBuilder.vertex(width + x, y, 0.0D).texture((float) width / 32.0F, (float) vOffset).color(r, g, b, 255).next();
bufferBuilder.vertex(x, y, 0.0D).texture(0.0F, (float) vOffset).color(r, g, b, 255).next();
tessellator.draw();
}
}
@@ -1,9 +1,9 @@
package draylar.omegaconfiggui.api.screen.widget;
import draylar.omegaconfiggui.mixin.AbstractButtonWidgetAccessor;
import draylar.omegaconfiggui.mixin.ClickableWidgetInvoker;
import net.minecraft.client.font.TextRenderer;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.widget.AbstractButtonWidget;
import net.minecraft.client.gui.widget.ClickableWidget;
import net.minecraft.client.gui.widget.TextFieldWidget;
import net.minecraft.text.Text;
@@ -20,8 +20,8 @@ public class BaseTextFieldWidget extends TextFieldWidget {
public void setTextFieldFocused(boolean focused) {
// unfocus all others
parent.children().forEach(element -> {
if(element instanceof AbstractButtonWidget) {
((AbstractButtonWidgetAccessor) element).callSetFocused(false);
if (element instanceof ClickableWidget) {
((ClickableWidgetInvoker) element).callSetFocused(false);
}
});
@@ -13,14 +13,12 @@ public class DoubleFieldWidget extends BaseTextFieldWidget {
@Override
public void write(String string) {
try {
if(string.equals(".") && !getText().contains(".")) {
super.write(string);
} else {
if (!string.equals(".") || getText().contains(".")) {
Double.parseDouble(string);
super.write(string);
}
super.write(string);
} catch(NumberFormatException ignored) {
} catch (NumberFormatException ignored) {
}
}
@@ -30,6 +30,10 @@ public class TypeWidgets {
PRIMITIVE_TO_BOXED.put(void.class, Void.class);
}
private TypeWidgets() {
// NO-OP
}
@Nullable
public static <T> WidgetSupplier<T, ?> get(Class<T> typeClass) {
return (WidgetSupplier<T, ?>) CLASS_WIDGETS.get(unbox(typeClass));
@@ -38,8 +42,4 @@ public class TypeWidgets {
public static Class<?> unbox(Class<?> c) {
return PRIMITIVE_TO_BOXED.getOrDefault(c, c);
}
private TypeWidgets() {
// NO-OP
}
}
@@ -1,10 +1,11 @@
package draylar.omegaconfiggui.api.screen.widget;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.widget.AbstractButtonWidget;
import net.minecraft.client.gui.widget.ClickableWidget;
import net.minecraft.text.LiteralText;
public interface WidgetSupplier<T, A extends AbstractButtonWidget> {
AbstractButtonWidget create(Screen parent, int x, int y, int width, int height, LiteralText prompt, T value);
public interface WidgetSupplier<T, A extends ClickableWidget> {
ClickableWidget create(Screen parent, int x, int y, int width, int height, LiteralText prompt, T value);
T get(A widget);
}
@@ -21,12 +21,12 @@ public class DoubleWidgetSupplier implements WidgetSupplier<Double, DoubleFieldW
String message = widget.getText();
// if it is just a . or empty, return 0
if(message.equals(".") || message.isEmpty()) {
if (message.equals(".") || message.isEmpty()) {
return 0.0;
}
// trim potential trailing .
if(message.indexOf(".") == message.length()) {
if (message.indexOf(".") == message.length()) {
message = message.substring(0, message.length() - 1);
}
@@ -4,7 +4,6 @@ import draylar.omegaconfiggui.api.screen.widget.BaseTextFieldWidget;
import draylar.omegaconfiggui.api.screen.widget.WidgetSupplier;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.widget.AbstractButtonWidget;
import net.minecraft.client.gui.widget.TextFieldWidget;
import net.minecraft.text.LiteralText;
@@ -1,11 +1,11 @@
package draylar.omegaconfiggui.mixin;
import net.minecraft.client.gui.widget.AbstractButtonWidget;
import net.minecraft.client.gui.widget.ClickableWidget;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Invoker;
@Mixin(AbstractButtonWidget.class)
public interface AbstractButtonWidgetAccessor {
@Mixin(ClickableWidget.class)
public interface ClickableWidgetInvoker {
@Invoker
void callSetFocused(boolean focused);
}
@@ -14,8 +14,8 @@
"omega-config-gui.mixins.json"
],
"depends": {
"fabricloader": ">=0.11.2",
"fabricloader": "*",
"fabric": "*",
"minecraft": "1.16.5"
"minecraft": "*"
}
}
@@ -2,9 +2,9 @@
"required": true,
"minVersion": "0.8",
"package": "draylar.omegaconfiggui.mixin",
"compatibilityLevel": "JAVA_8",
"compatibilityLevel": "JAVA_16",
"mixins": [
"AbstractButtonWidgetAccessor",
"ClickableWidgetInvoker",
"ModMenuAccessor"
],
"client": [
@@ -1,11 +1,18 @@
package draylar.omegatest;
import draylar.omegaconfig.OmegaConfig;
import draylar.omegatest.config.ClassConfigTest;
import draylar.omegatest.config.NestedConfigTest;
import draylar.omegatest.config.SimpleConfigTest;
import draylar.omegatest.config.StructuresConfigTest;
import net.fabricmc.api.ModInitializer;
public class OmegaTestMain implements ModInitializer {
public static final TestConfig CONFIG = OmegaConfig.register(TestConfig.class);
public static final SimpleConfigTest CONFIG = OmegaConfig.register(SimpleConfigTest.class);
public static final StructuresConfigTest MO_CONFIG = OmegaConfig.register(StructuresConfigTest.class);
public static final NestedConfigTest NESTED = OmegaConfig.register(NestedConfigTest.class);
public static final ClassConfigTest CLASS = OmegaConfig.register(ClassConfigTest.class);
@Override
public void onInitialize() {
@@ -0,0 +1,30 @@
package draylar.omegatest.config;
import draylar.omegaconfig.api.Config;
import java.util.Arrays;
import java.util.List;
public class ClassConfigTest implements Config {
public List<TestClass> l = Arrays.asList(
new TestClass(true, 1, "hello")
);
@Override
public String getName() {
return "class-config";
}
public static class TestClass {
public final boolean a;
public final int b;
public String c;
public TestClass(boolean a, int b, String c) {
this.a = a;
this.b = b;
this.c = c;
}
}
}
@@ -0,0 +1,18 @@
package draylar.omegatest.config;
import draylar.omegaconfig.api.Config;
public class NestedConfigTest implements Config {
public boolean test = false;
@Override
public String getName() {
return "nested";
}
@Override
public String getDirectory() {
return "test";
}
}
@@ -1,4 +1,4 @@
package draylar.omegatest;
package draylar.omegatest.config;
import draylar.omegaconfig.api.Comment;
import draylar.omegaconfig.api.Config;
@@ -6,25 +6,19 @@ import draylar.omegaconfig.api.Syncing;
import org.jetbrains.annotations.Nullable;
@Syncing
public class TestConfig implements Config {
public class SimpleConfigTest implements Config {
@Comment(value = "Hello!")
boolean v = false;
public boolean v = false;
@Comment(value = "I'm a double.")
double doubleTest = 0.0;
public double doubleTest = 0.0;
String stringTest = "Hello, world!";
public String stringTest = "Hello, world!";
@Comment(value = "This is an inner static class.")
public Test test = new Test();
public static class Test {
@Comment(value = "This is the value inside the class!")
public boolean innerTest = false;
}
@Override
public String getName() {
return "test-config";
@@ -34,4 +28,10 @@ public class TestConfig implements Config {
public @Nullable String getModid() {
return "omega-config-test";
}
public static class Test {
@Comment(value = "This is the value inside the class!")
public boolean innerTest = false;
}
}
@@ -0,0 +1,42 @@
package draylar.omegatest.config;
import java.util.Map;
public class StructureConfigEntry {
public final boolean activated;
public final int separation;
public final int spacing;
public final int salt;
private StructureConfigEntry(int separation, int spacing, int salt) {
this.activated = true;
this.spacing = spacing;
this.separation = separation;
this.salt = salt;
}
public static StructureConfigEntry of(int separation, int spacing, int salt) {
return new StructureConfigEntry(separation, spacing, salt);
}
public static void computeConfigMap(Map<String, StructureConfigEntry> map) {
map.computeIfAbsent("abandoned_church", (id) -> of(14, 38, 66996840));
map.computeIfAbsent("barn_house", (id) -> of(8, 38, 165757306));
map.computeIfAbsent("big_pyramid", (id) -> of(5, 25, 239284294));
map.computeIfAbsent("jungle_pyramid", (id) -> of(5, 25, 312178642));
map.computeIfAbsent("the_castle_in_the_sky", (id) -> of(8, 30, 423494938));
map.computeIfAbsent("killer_bunny_castle", (id) -> of(25, 45, 48123900));
map.computeIfAbsent("villager_tower", (id) -> of(16, 34, 550292492));
map.computeIfAbsent("villager_market", (id) -> of(16, 36, 784939542));
map.computeIfAbsent("pillager_factory", (id) -> of(16, 36, 839204924));
map.computeIfAbsent("ice_tower", (id) -> of(8, 28, 964058305));
map.computeIfAbsent("tavern", (id) -> of(12, 32, 19296726));
map.computeIfAbsent("pirate_ship", (id) -> of(16, 40, 583957395));
map.computeIfAbsent("lighthouse", (id) -> of(16, 32, 29502322));
map.computeIfAbsent("volcanic_vent", (id) -> of(4, 8, 84981094));
map.computeIfAbsent("moai", (id) -> of(4, 8, 12994829));
map.computeIfAbsent("air_balloon", (id) -> of(1, 6, 29483148));
}
}
@@ -0,0 +1,58 @@
package draylar.omegatest.config;
import draylar.omegaconfig.api.Comment;
import draylar.omegaconfig.api.Config;
import net.minecraft.util.Identifier;
import java.util.HashMap;
import java.util.Map;
//taken straight out of Mo' Structures
public class StructuresConfigTest implements Config {
@Comment("""
Welcome to Mo'Structures Config!
//
// Here, you can turn off structures, change their chance, and also change their salt.
//
// To turn off a structure, simply go to the corresponding entry and set `activated` to false.
//
// Mo' Structures uses the vanilla structure spawning system. That is-
// - Seperation is the minimum chunks between structures
// - Spacing is the average chunks between structures
//
// Salt is a special field that gives structures unique spawning positions. DO NOT TOUCH IT, ONLY ADVANCED TROUBLESHOOTING!
""")
public final Map<String, StructureConfigEntry> structureConfigEntries = new HashMap<>(17);
@Override
public String getName() {
return "mostructures-config-v2";
}
@Override
public String getExtension() {
return "json5";
}
public StructureConfigEntry get(Identifier id) {
for (Map.Entry<String, StructureConfigEntry> entry : structureConfigEntries.entrySet()) {
if (entry.getKey().equals(id.getPath())) {
return entry.getValue();
}
}
throw new NullPointerException("Tried StructureConfigEntry with id: " + id + ", but it was null!");
}
@Override
public void save() {
StructureConfigEntry.computeConfigMap(structureConfigEntries);
Config.super.save();
}
}
+2 -2
View File
@@ -19,8 +19,8 @@
]
},
"depends": {
"fabricloader": ">=0.11.2",
"fabricloader": "*",
"fabric": "*",
"minecraft": "1.16.5"
"minecraft": "*"
}
}