Skip to content

Commit

Permalink
Adapted Config UI
Browse files Browse the repository at this point in the history
  • Loading branch information
HenryLoenwind committed Jul 24, 2024
1 parent d91c411 commit 3d71462
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 49 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import com.google.common.collect.ImmutableList;
import com.mojang.realmsclient.RealmsMainScreen;
import com.mojang.serialization.Codec;
import it.unimi.dsi.fastutil.booleans.BooleanConsumer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
Expand Down Expand Up @@ -68,6 +69,8 @@
import net.neoforged.neoforge.common.ModConfigSpec.ConfigValue;
import net.neoforged.neoforge.common.ModConfigSpec.ListValueSpec;
import net.neoforged.neoforge.common.ModConfigSpec.Range;
import net.neoforged.neoforge.common.ModConfigSpec.RawSupplier;
import net.neoforged.neoforge.common.ModConfigSpec.RestartType;
import net.neoforged.neoforge.common.ModConfigSpec.ValueSpec;
import net.neoforged.neoforge.common.TranslatableEnum;
import org.apache.logging.log4j.LogManager;
Expand Down Expand Up @@ -104,6 +107,30 @@
*
*/
public final class ConfigurationScreen extends OptionsSubScreen {
private static final class TooltipConfirmScreen extends ConfirmScreen {
boolean seenYes = false;

private TooltipConfirmScreen(BooleanConsumer p_95658_, Component p_95659_, Component p_95660_, Component p_95661_, Component p_95662_) {
super(p_95658_, p_95659_, p_95660_, p_95661_, p_95662_);
}

@Override
protected void init() {
seenYes = false;
super.init();
}

@Override
protected void addExitButton(Button button) {
if (seenYes) {
button.setTooltip(Tooltip.create(RESTART_NO_TOOLTIP));
} else {
seenYes = true;
}
super.addExitButton(button);
}
}

public static class TranslationChecker {
private static final Logger LOGGER = LogManager.getLogger();
private final Set<String> untranslatables = new HashSet<>();
Expand Down Expand Up @@ -157,14 +184,6 @@ public void finish() {
}
}

public enum RestartType {
NONE, SERVER, GAME;

RestartType with(RestartType other) {
return other == NONE ? this : (other == GAME || this == GAME) ? GAME : SERVER;
}
}

/**
* Prefix for static keys the configuration screens use internally.
*/
Expand Down Expand Up @@ -210,6 +229,7 @@ RestartType with(RestartType other) {
public static final Component RETURN_TO_MENU = Component.translatable("menu.returnToMenu"); // PauseScreen.RETURN_TO_MENU
public static final Component SAVING_LEVEL = Component.translatable("menu.savingLevel"); // PauseScreen.SAVING_LEVEL
public static final Component RESTART_NO = Component.translatable(LANG_PREFIX + "restart.return");
public static final Component RESTART_NO_TOOLTIP = Component.translatable(LANG_PREFIX + "restart.return.tooltip");
public static final Component UNDO = Component.translatable(LANG_PREFIX + "undo");
public static final Component UNDO_TOOLTIP = Component.translatable(LANG_PREFIX + "undo.tooltip");
public static final Component RESET = Component.translatable(LANG_PREFIX + "reset");
Expand Down Expand Up @@ -303,24 +323,24 @@ public void onClose() {
translationChecker.finish();
switch (needsRestart) {
case GAME -> {
minecraft.setScreen(new ConfirmScreen(b -> {
minecraft.setScreen(new TooltipConfirmScreen(b -> {
if (b) {
minecraft.stop();
} else {
minecraft.setScreen(this);
super.onClose();
}
}, GAME_RESTART_TITLE, GAME_RESTART_MESSAGE, GAME_RESTART_YES, RESTART_NO));
return;
}
case SERVER -> {
case WORLD -> {
if (minecraft.level != null) {
minecraft.setScreen(new ConfirmScreen(b -> {
minecraft.setScreen(new TooltipConfirmScreen(b -> {
if (b) {
// when changing server configs from the client is added, this is where we tell the server to restart and activate the new config.
// also needs a different text in MP ("server will restart/exit, yada yada") than in SP
onDisconnect();
} else {
minecraft.setScreen(this);
super.onClose();
}
}, SERVER_RESTART_TITLE, SERVER_RESTART_MESSAGE, minecraft.isLocalServer() ? RETURN_TO_MENU : CommonComponents.GUI_DISCONNECT, RESTART_NO));
return;
Expand Down Expand Up @@ -547,8 +567,8 @@ protected Component getTooltipComponent(final String key, @Nullable Range<?> ran
protected void onChanged(final String key) {
changed = true;
final ValueSpec valueSpec = getValueSpec(key);
if (valueSpec != null && valueSpec.restartType() == ModConfigSpec.RestartType.WORLD) {
needsRestart = needsRestart.with(RestartType.SERVER);
if (valueSpec != null) {
needsRestart = needsRestart.with(valueSpec.restartType());
}
}

Expand Down Expand Up @@ -627,7 +647,7 @@ protected Collection<? extends Element> createSyntheticValues() {
}

protected boolean isNonDefault(ModConfigSpec.ConfigValue<?> cv) {
return !Objects.equals(cv.get(), cv.getDefault());
return !Objects.equals(cv.getRaw(), cv.getDefault());
}

protected boolean isAnyNondefault() {
Expand All @@ -642,22 +662,22 @@ protected boolean isAnyNondefault() {
}

@Nullable
protected Element createStringValue(final String key, final Predicate<String> tester, final Supplier<String> source, final Consumer<String> target) {
protected Element createStringValue(final String key, final Predicate<String> tester, final RawSupplier<String> source, final Consumer<String> target) {
final EditBox box = new EditBox(font, Button.DEFAULT_WIDTH, Button.DEFAULT_HEIGHT, getTranslationComponent(key));
box.setEditable(true);
// no filter or the user wouldn't be able to type
box.setTooltip(Tooltip.create(getTooltipComponent(key, null)));
box.setValue(source.get());
box.setValue(source.getRaw());
box.setResponder(newValue -> {
if (newValue != null && tester.test(newValue)) {
if (!newValue.equals(source.get())) {
if (!newValue.equals(source.getRaw())) {
undoManager.add(v -> {
target.accept(v);
onChanged(key);
}, newValue, v -> {
target.accept(v);
onChanged(key);
}, source.get());
}, source.getRaw());
}
box.setTextColor(EditBox.DEFAULT_TEXT_COLOR);
return;
Expand Down Expand Up @@ -692,7 +712,7 @@ protected Element createOtherSection(final String key, final Object value) {
*/
@Nullable
protected Element createOtherValue(final String key, final ConfigValue<?> value) {
final StringWidget label = new StringWidget(Button.DEFAULT_WIDTH, Button.DEFAULT_HEIGHT, Component.literal(Objects.toString(value.get())), font).alignLeft();
final StringWidget label = new StringWidget(Button.DEFAULT_WIDTH, Button.DEFAULT_HEIGHT, Component.literal(Objects.toString(value.getRaw())), font).alignLeft();
label.setTooltip(Tooltip.create(UNSUPPORTED_ELEMENT));
return new Element(getTranslationComponent(key), getTooltipComponent(key, null), label, false);
}
Expand Down Expand Up @@ -729,22 +749,22 @@ public Codec<T> codec() {
}

@Nullable
protected Element createBooleanValue(final String key, final ValueSpec spec, final Supplier<Boolean> source, final Consumer<Boolean> target) {
protected Element createBooleanValue(final String key, final ValueSpec spec, final RawSupplier<Boolean> source, final Consumer<Boolean> target) {
return new Element(getTranslationComponent(key), getTooltipComponent(key, null),
new OptionInstance<>(getTranslationKey(key), getTooltip(key, null), OptionInstance.BOOLEAN_TO_STRING, Custom.BOOLEAN_VALUES_NO_PREFIX, source.get(), newValue -> {
new OptionInstance<>(getTranslationKey(key), getTooltip(key, null), OptionInstance.BOOLEAN_TO_STRING, Custom.BOOLEAN_VALUES_NO_PREFIX, source.getRaw(), newValue -> {
// regarding change detection: new value always is different (cycle button)
undoManager.add(v -> {
target.accept(v);
onChanged(key);
}, newValue, v -> {
target.accept(v);
onChanged(key);
}, source.get());
}, source.getRaw());
}));
}

@Nullable
protected <T extends Enum<T>> Element createEnumValue(final String key, final ValueSpec spec, final Supplier<T> source, final Consumer<T> target) {
protected <T extends Enum<T>> Element createEnumValue(final String key, final ValueSpec spec, final RawSupplier<T> source, final Consumer<T> target) {
@SuppressWarnings("unchecked")
final Class<T> clazz = (Class<T>) spec.getClazz();
assert clazz != null;
Expand All @@ -753,20 +773,20 @@ protected <T extends Enum<T>> Element createEnumValue(final String key, final Va

return new Element(getTranslationComponent(key), getTooltipComponent(key, null),
new OptionInstance<>(getTranslationKey(key), getTooltip(key, null), (caption, displayvalue) -> displayvalue instanceof TranslatableEnum tenum ? tenum.getTranslatedName() : Component.literal(displayvalue.name()),
new Custom<>(list), source.get(), newValue -> {
new Custom<>(list), source.getRaw(), newValue -> {
// regarding change detection: new value always is different (cycle button)
undoManager.add(v -> {
target.accept(v);
onChanged(key);
}, newValue, v -> {
target.accept(v);
onChanged(key);
}, source.get());
}, source.getRaw());
}));
}

@Nullable
protected Element createIntegerValue(final String key, final ValueSpec spec, final Supplier<Integer> source, final Consumer<Integer> target) {
protected Element createIntegerValue(final String key, final ValueSpec spec, final RawSupplier<Integer> source, final Consumer<Integer> target) {
final Range<Integer> range = spec.getRange();
final int min = range != null ? range.getMin() : 0;
final int max = range != null ? range.getMax() : Integer.MAX_VALUE;
Expand All @@ -779,31 +799,31 @@ protected Element createIntegerValue(final String key, final ValueSpec spec, fin
}

@Nullable
protected Element createSlider(final String key, final Supplier<Integer> source, final Consumer<Integer> target, final @Nullable Range<Integer> range) {
protected Element createSlider(final String key, final RawSupplier<Integer> source, final Consumer<Integer> target, final @Nullable Range<Integer> range) {
return new Element(getTranslationComponent(key), getTooltipComponent(key, null),
new OptionInstance<>(getTranslationKey(key), getTooltip(key, range),
(caption, displayvalue) -> Component.literal("" + displayvalue), new OptionInstance.IntRange(range != null ? range.getMin() : 0, range != null ? range.getMax() : Integer.MAX_VALUE),
null, source.get(), newValue -> {
if (!newValue.equals(source.get())) {
null, source.getRaw(), newValue -> {
if (!newValue.equals(source.getRaw())) {
undoManager.add(v -> {
target.accept(v);
onChanged(key);
}, newValue, v -> {
target.accept(v);
onChanged(key);
}, source.get());
}, source.getRaw());
}
}));
}

@Nullable
protected Element createLongValue(final String key, final ValueSpec spec, final Supplier<Long> source, final Consumer<Long> target) {
protected Element createLongValue(final String key, final ValueSpec spec, final RawSupplier<Long> source, final Consumer<Long> target) {
return createNumberBox(key, spec, source, target, null, Long::decode, 0L);
}

// if someone knows how to get a proper zero inside...
@Nullable
protected <T extends Number & Comparable<? super T>> Element createNumberBox(final String key, final ValueSpec spec, final Supplier<T> source,
protected <T extends Number & Comparable<? super T>> Element createNumberBox(final String key, final ValueSpec spec, final RawSupplier<T> source,
final Consumer<T> target, @Nullable final Predicate<T> tester, final Function<String, T> parser, final T zero) {
final Range<T> range = spec.getRange();

Expand All @@ -818,19 +838,19 @@ protected <T extends Number & Comparable<? super T>> Element createNumberBox(fin
}
});
box.setTooltip(Tooltip.create(getTooltipComponent(key, range)));
box.setValue(source.get() + "");
box.setValue(source.getRaw() + "");
box.setResponder(newValueString -> {
try {
final T newValue = parser.apply(newValueString);
if (tester != null ? tester.test(newValue) : (newValue != null && (range == null || range.test(newValue)) && spec.test(newValue))) {
if (!newValue.equals(source.get())) {
if (!newValue.equals(source.getRaw())) {
undoManager.add(v -> {
target.accept(v);
onChanged(key);
}, newValue, v -> {
target.accept(v);
onChanged(key);
}, source.get());
}, source.getRaw());
}
box.setTextColor(EditBox.DEFAULT_TEXT_COLOR);
return;
Expand All @@ -844,7 +864,7 @@ protected <T extends Number & Comparable<? super T>> Element createNumberBox(fin
}

@Nullable
protected Element createDoubleValue(final String key, final ValueSpec spec, final Supplier<Double> source, final Consumer<Double> target) {
protected Element createDoubleValue(final String key, final ValueSpec spec, final RawSupplier<Double> source, final Consumer<Double> target) {
return createNumberBox(key, spec, source, target, null, Double::parseDouble, 0.0);
}

Expand Down Expand Up @@ -921,7 +941,7 @@ protected void createResetButton() {
}, getValueSpec(key).correct(null), v -> {
cv.set(v);
onChanged(key);
}, cv.get()));
}, cv.getRaw()));
}
}
undoManager.add(list);
Expand Down Expand Up @@ -986,7 +1006,7 @@ public ConfigurationListScreen(final Context context, final String key, final Co
this.key = key;
this.spec = spec;
this.valueList = valueList; // === (ListValueSpec)getValueSpec(key)
this.cfgList = new ArrayList<>(valueList.get());
this.cfgList = new ArrayList<>(valueList.getRaw());
}

@Override
Expand Down
30 changes: 24 additions & 6 deletions src/main/java/net/neoforged/neoforge/common/ModConfigSpec.java
Original file line number Diff line number Diff line change
Expand Up @@ -1184,7 +1184,12 @@ public Range<Integer> getSizeRange() {
}
}

public static class ConfigValue<T> implements Supplier<T> {
@FunctionalInterface
public interface RawSupplier<T> {
T getRaw();
}

public static class ConfigValue<T> implements Supplier<T>, RawSupplier<T> {
private final Builder parent;
private final List<String> path;
private final Supplier<T> defaultSupplier;
Expand Down Expand Up @@ -1219,16 +1224,25 @@ public List<String> getPath() {
*/
@Override
public T get() {
Preconditions.checkNotNull(spec, "Cannot get config value before spec is built");
var loadedConfig = spec.loadedConfig;
Preconditions.checkState(loadedConfig != null, "Cannot get config value before config is loaded.");

if (cachedValue == null) {
cachedValue = getRaw(loadedConfig.config(), path, defaultSupplier);
cachedValue = getRaw();
}
return cachedValue;
}

/**
* Returns the uncached value for the configuration setting, throwing if the config has not yet been loaded.
* <p>
* <em>Do not call this for any other purpose than editing the value. Use {@link #get()} instead.</em>
*/
@Override
public T getRaw() {
Preconditions.checkNotNull(spec, "Cannot get config value before spec is built");
var loadedConfig = spec.loadedConfig;
Preconditions.checkState(loadedConfig != null, "Cannot get config value before config is loaded.");
return getRaw(loadedConfig.config(), path, defaultSupplier);
}

public T getRaw(Config config, List<String> path, Supplier<T> defaultSupplier) {
return config.getOrElse(path, defaultSupplier);
}
Expand Down Expand Up @@ -1395,5 +1409,9 @@ public enum RestartType {
private boolean isValid(ModConfig.Type type) {
return !invalidTypes.contains(type);
}

public RestartType with(RestartType other) {
return other == NONE ? this : (other == GAME || this == GAME) ? GAME : WORLD;
}
}
}
13 changes: 10 additions & 3 deletions src/main/resources/assets/neoforge/lang/en_us.json
Original file line number Diff line number Diff line change
Expand Up @@ -216,10 +216,17 @@
"neoforge.configuration.uitext.server": "Server Options",
"neoforge.configuration.uitext.startup": "Startup Options",
"neoforge.configuration.uitext.restart.game.title": "Minecraft needs to be restarted",
"neoforge.configuration.uitext.restart.game.text": "One or more of the configuration option that were changed only take effect when the game is started.",
"neoforge.configuration.uitext.restart.game.text": "One or more of the configuration option that were changed will only take effect when the game is started.",
"neoforge.configuration.uitext.restart.server.title": "World needs to be reloaded",
"neoforge.configuration.uitext.restart.server.text": "One or more of the configuration option that were changed only take effect when a world is started or loaded.",
"neoforge.configuration.uitext.restart.return": "Not yet...",
"neoforge.configuration.uitext.restart.server.text": "One or more of the configuration option that were changed will only take effect when the world is reloaded.",
"neoforge.configuration.uitext.restart.return": "Ignore",
"neoforge.configuration.uitext.restart.return.tooltip": [
{
"text": "Your changes will have no effect until you restart!",
"color": "red",
"bold": true
}
],

"neoforge.configuration.title": "NeoForge Configuration",
"neoforge.configuration.section.neoforge.client.toml": "Client settings",
Expand Down
Loading

0 comments on commit 3d71462

Please sign in to comment.