Rewrote configurations to get rid of reflection

This commit is contained in:
Sollace 2019-05-30 21:24:57 +02:00
parent 2fd94f9434
commit 0d5656685c
9 changed files with 162 additions and 168 deletions

View file

@ -19,7 +19,7 @@ import com.minelittlepony.client.gui.hdskins.MineLPHDSkins;
import com.minelittlepony.client.mixin.MixinBlockEntityRenderDispatcher; import com.minelittlepony.client.mixin.MixinBlockEntityRenderDispatcher;
import com.minelittlepony.client.settings.ClientPonyConfig; import com.minelittlepony.client.settings.ClientPonyConfig;
import com.minelittlepony.hdskins.mixin.MixinEntityRenderDispatcher; import com.minelittlepony.hdskins.mixin.MixinEntityRenderDispatcher;
import com.minelittlepony.settings.SensibleJsonConfig; import com.minelittlepony.settings.JsonConfig;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.function.Function; import java.util.function.Function;
@ -36,7 +36,7 @@ public class FabMod implements ClientModInitializer, IModUtilities {
mlp = new MineLPClient(this); mlp = new MineLPClient(this);
} }
mlp.init(SensibleJsonConfig.of(getConfigDirectory().resolve("minelp.json"), ClientPonyConfig::new)); mlp.init(JsonConfig.of(getConfigDirectory().resolve("minelp.json"), ClientPonyConfig::new));
} }
@Override @Override

View file

@ -3,8 +3,8 @@ package com.minelittlepony.client.render.entities;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.minelittlepony.MineLittlePony; import com.minelittlepony.MineLittlePony;
import com.minelittlepony.client.PonyRenderManager; import com.minelittlepony.client.PonyRenderManager;
import com.minelittlepony.settings.SensibleConfig; import com.minelittlepony.settings.Config;
import com.minelittlepony.settings.SensibleConfig.Setting; import com.minelittlepony.settings.Config.Setting;
import java.util.List; import java.util.List;
@ -13,10 +13,8 @@ import net.minecraft.entity.passive.*;
/** /**
* Central location where new entity renderers are registered and applied. * Central location where new entity renderers are registered and applied.
*
* Due to the limitations in Mumfrey's framework, needs to be paired with a field in PonyConfig.
*/ */
public enum MobRenderers implements Setting { public enum MobRenderers implements Setting<Boolean> {
VILLAGERS { VILLAGERS {
@Override @Override
void register(boolean state, PonyRenderManager pony) { void register(boolean state, PonyRenderManager pony) {
@ -73,13 +71,18 @@ public enum MobRenderers implements Setting {
public static final List<MobRenderers> registry = Lists.newArrayList(values()); public static final List<MobRenderers> registry = Lists.newArrayList(values());
@Override @Override
public void set(boolean value) { public Boolean getDefault() {
return true;
}
@Override
public void set(Boolean value) {
Setting.super.set(value); Setting.super.set(value);
apply(PonyRenderManager.getInstance()); apply(PonyRenderManager.getInstance());
} }
@Override @Override
public SensibleConfig config() { public Config config() {
return MineLittlePony.getInstance().getConfig(); return MineLittlePony.getInstance().getConfig();
} }

View file

@ -1,11 +1,16 @@
package com.minelittlepony.client.settings; package com.minelittlepony.client.settings;
import com.minelittlepony.client.render.entities.MobRenderers;
import com.minelittlepony.hdskins.HDSkins; import com.minelittlepony.hdskins.HDSkins;
import com.minelittlepony.settings.PonyConfig; import com.minelittlepony.settings.PonyConfig;
import com.minelittlepony.settings.PonyLevel; import com.minelittlepony.settings.PonyLevel;
public class ClientPonyConfig extends PonyConfig { public class ClientPonyConfig extends PonyConfig {
public ClientPonyConfig() {
initWith(MobRenderers.values());
}
@Override @Override
public void setPonyLevel(PonyLevel ponylevel) { public void setPonyLevel(PonyLevel ponylevel) {
// only trigger reloads when the value actually changes // only trigger reloads when the value actually changes

View file

@ -0,0 +1,104 @@
package com.minelittlepony.settings;
import com.minelittlepony.common.client.gui.IField.IChangeCallback;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.HashMap;
import java.util.Map;
/**
* A configuration container that lets you programmatically index values by a key.
*/
public abstract class Config {
protected Map<String, Object> entries = new HashMap<>();
protected void initWith(Setting<?>... settings) {
for (Setting<?> s : settings) {
entries.putIfAbsent(s.name(), s.getDefault());
}
}
public abstract void save();
/**
* Any value that can be stored in this config file.
*/
public class Value<T> implements Setting<T> {
private final T def;
private final String name;
public Value(String name, T def) {
this.name = name;
this.def = def;
entries.putIfAbsent(name(), def);
}
@Override
public String name() {
return name;
}
@Nonnull
@Override
public T getDefault() {
return def;
}
@Override
public Config config() {
return Config.this;
}
@Override
public String toString() {
return name();
}
}
/**
* Any settings.
*/
public interface Setting<T> extends IChangeCallback<T> {
String name();
@Nonnull
T getDefault();
Config config();
/**
* Gets the config value associated with this entry.
*/
@Nonnull
@SuppressWarnings("unchecked")
default T get() {
T t = (T)config().entries.computeIfAbsent(name(), k -> getDefault());
if (t == null) {
t = getDefault();
set(t);
}
return t;
}
/**
* Sets the config value associated with this entry.
*/
default void set(@Nullable T value) {
value = value == null ? getDefault() : value;
config().entries.put(name(), value);
}
@Override
default T perform(T v) {
set(v);
return v;
}
}
}

View file

@ -3,15 +3,20 @@ package com.minelittlepony.settings;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.GsonBuilder; import com.google.gson.GsonBuilder;
import com.google.gson.stream.JsonWriter; import com.google.gson.stream.JsonWriter;
import com.minelittlepony.client.settings.ClientPonyConfig;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Supplier; import java.util.function.Supplier;
public class SensibleJsonConfig extends SensibleConfig { public class JsonConfig extends Config {
public static <T extends JsonConfig> T of(Path file, Supplier<T> creator) {
return creator.get().load(file);
}
static final Gson gson = new GsonBuilder() static final Gson gson = new GsonBuilder()
.setPrettyPrinting() .setPrettyPrinting()
@ -22,42 +27,32 @@ public class SensibleJsonConfig extends SensibleConfig {
@Override @Override
public void save() { public void save() {
try (JsonWriter writer = new JsonWriter(Files.newBufferedWriter(configFile))) { try (JsonWriter writer = new JsonWriter(Files.newBufferedWriter(configFile))) {
writer.setIndent(" "); writer.setIndent(" ");
gson.toJson(this, ClientPonyConfig.class, writer); gson.toJson(entries, HashMap.class, writer);
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} }
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
protected <T extends SensibleJsonConfig> T load(Path file) { protected <T extends JsonConfig> T load(Path file) {
SensibleJsonConfig result = this;
try { try {
if (Files.exists(file)) { if (Files.exists(file)) {
try (BufferedReader s = Files.newBufferedReader(file)) { try (BufferedReader s = Files.newBufferedReader(file)) {
result = gson.fromJson(s, getClass()); Map<String, Object> parsed = gson.fromJson(s, HashMap.class);
} catch (IOException ignored) {
result = null;
}
}
if (result == null) { if (parsed != null) {
result = this; entries = parsed;
} }
} catch (IOException ignored) { }
result.configFile = file; }
configFile = file;
} finally { } finally {
result.save(); save();
} }
return (T)result; return (T)this;
}
public static <T extends SensibleJsonConfig> T of(Path file, Supplier<T> creator) {
return creator.get().load(file);
} }
} }

View file

@ -1,5 +0,0 @@
package com.minelittlepony.settings;
public enum MobSettings {
}

View file

@ -2,32 +2,17 @@ package com.minelittlepony.settings;
import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.MathHelper;
import com.google.gson.annotations.Expose;
/** /**
* Storage container for MineLP client settings. * Storage container for MineLP client settings.
*/ */
public abstract class PonyConfig extends SensibleJsonConfig { public class PonyConfig extends JsonConfig {
@Expose private PonyLevel ponylevel = PonyLevel.PONIES; private final Setting<PonyLevel> ponyLevel = new Value<>("ponylevel", PonyLevel.PONIES);
private final Setting<Float> scaleFactor = new Value<>("globalScaleFactor", 0.9F);
@Expose boolean sizes = true; public PonyConfig() {
@Expose boolean snuzzles = true; initWith(PonySettings.values());
@Expose boolean hd = true; }
@Expose boolean showscale = true;
@Expose boolean fpsmagic = true;
@Expose boolean ponyskulls = true;
@Expose boolean frustrum = true;
@Expose boolean villagers = true;
@Expose boolean zombies = true;
@Expose boolean pigzombies = true;
@Expose boolean skeletons = true;
@Expose boolean illagers = true;
@Expose boolean guardians = true;
@Expose boolean endermen = true;
@Expose private float globalScaleFactor = 0.9F;
/** /**
* Gets the current PonyLevel. That is the level of ponies you would like to see. * Gets the current PonyLevel. That is the level of ponies you would like to see.
@ -42,10 +27,7 @@ public abstract class PonyConfig extends SensibleJsonConfig {
* Actually gets the pony level value. No option to ignore reality here. * Actually gets the pony level value. No option to ignore reality here.
*/ */
public PonyLevel getPonyLevel() { public PonyLevel getPonyLevel() {
if (ponylevel == null) { return ponyLevel.get();
ponylevel = PonyLevel.PONIES;
}
return ponylevel;
} }
/** /**
@ -54,18 +36,20 @@ public abstract class PonyConfig extends SensibleJsonConfig {
* @param ponylevel * @param ponylevel
*/ */
public void setPonyLevel(PonyLevel ponylevel) { public void setPonyLevel(PonyLevel ponylevel) {
this.ponylevel = ponylevel; ponyLevel.set(ponylevel);
} }
public void setGlobalScaleFactor(float f) { public void setGlobalScaleFactor(float f) {
globalScaleFactor = Math.round(MathHelper.clamp(f, 0.1F, 3) * 100) / 100F; f = Math.round(MathHelper.clamp(f, 0.1F, 3) * 100) / 100F;
showscale = globalScaleFactor != 1;
scaleFactor.set(f);
PonySettings.SHOWSCALE.set(f != 1);
} }
/** /**
* Gets the universal scale factor used to determine how tall ponies are. * Gets the universal scale factor used to determine how tall ponies are.
*/ */
public float getGlobalScaleFactor() { public float getGlobalScaleFactor() {
return showscale ? globalScaleFactor : 1; return PonySettings.SHOWSCALE.get() ? scaleFactor.get() : 1;
} }
} }

View file

@ -1,9 +1,12 @@
package com.minelittlepony.settings; package com.minelittlepony.settings;
import com.minelittlepony.MineLittlePony; import com.minelittlepony.MineLittlePony;
import com.minelittlepony.settings.SensibleConfig.Setting; import com.minelittlepony.settings.Config.Setting;
public enum PonySettings implements Setting { /**
* Mod settings.
*/
public enum PonySettings implements Setting<Boolean> {
SIZES, SIZES,
SNUZZLES, SNUZZLES,
HD, HD,
@ -13,7 +16,12 @@ public enum PonySettings implements Setting {
FRUSTRUM; FRUSTRUM;
@Override @Override
public SensibleConfig config() { public Boolean getDefault() {
return true;
}
@Override
public Config config() {
return MineLittlePony.getInstance().getConfig(); return MineLittlePony.getInstance().getConfig();
} }
} }

View file

@ -1,100 +0,0 @@
package com.minelittlepony.settings;
import com.minelittlepony.common.client.gui.IField.IChangeCallback;
import javax.annotation.Nonnull;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
/**
* A sensible config container that actually lets us programmatically index values by a key.
*
* Reflection because Mumfrey pls.
*
*/
// Mumfrey pls.
// TODO: Reflection
public abstract class SensibleConfig {
public abstract void save();
public interface Setting extends IChangeCallback<Boolean> {
String name();
/**
* Gets the config value associated with this entry.
*/
default boolean get() {
return config().getValue(this);
}
/**
* Sets the config value associated with this entry.
*/
default void set(boolean value) {
config().setValue(this, value);
}
SensibleConfig config();
@Override
default Boolean perform(Boolean v) {
set(v);
return v;
}
}
private Map<Setting, Boolean> entries = new HashMap<>();
private Map<Setting, Field> fieldEntries = new HashMap<>();
public boolean getValue(Setting key) {
return entries.computeIfAbsent(key, this::reflectGetValue);
}
public boolean setValue(Setting key, boolean value) {
entries.put(key, value);
try {
findField(getClass(), key).setBoolean(this, value);
} catch (IllegalArgumentException | IllegalAccessException e) {
e.printStackTrace();
}
return value;
}
@Nonnull
protected Field findField(Class<?> type, Setting key) {
return fieldEntries.computeIfAbsent(key, k -> recurseFindField(type, key));
}
private boolean reflectGetValue(Setting key) {
try {
return findField(getClass(), key).getBoolean(this);
} catch (IllegalArgumentException | IllegalAccessException | SecurityException e) {
e.printStackTrace();
return true;
}
}
@Nonnull
private Field recurseFindField(Class<?> type, Setting key) {
try {
Field f = type.getDeclaredField(key.name().toLowerCase());
f.setAccessible(true);
fieldEntries.put(key, f);
return f;
} catch (IllegalArgumentException | NoSuchFieldException | SecurityException e) {
Class<?> superType = type.getSuperclass();
if (superType != null && superType != Object.class) {
return recurseFindField(superType, key);
}
throw new RuntimeException(String.format("Config option %s was not defined", key), e);
}
}
}