mirror of
https://github.com/Creators-of-Create/Create.git
synced 2024-11-10 12:02:48 +01:00
all the configs
- make it easier for addon devs to hook into create's config ui - change the config command to allow for: - opening any mod's registered config - changing single values at a time - possibly solve incompatibility with CalemiUtils mod and placement helpers
This commit is contained in:
parent
2e37807e7f
commit
975d898ac6
12 changed files with 468 additions and 76 deletions
|
@ -106,7 +106,7 @@ public class ClientEvents {
|
|||
AirCurrent.tickClientPlayerSounds();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
SoundScapes.tick();
|
||||
AnimationTickHolder.tick();
|
||||
|
@ -339,8 +339,8 @@ public class ClientEvents {
|
|||
}
|
||||
|
||||
public static void loadCompleted(FMLLoadCompleteEvent event) {
|
||||
ModContainer createContainer = ModList.get().getModContainerById("create").orElseThrow(() -> new IllegalStateException("Create Mod Container missing after loadCompleted"));
|
||||
createContainer.registerExtensionPoint(ExtensionPoint.CONFIGGUIFACTORY, () -> (mc, previousScreen) -> new BaseConfigScreen(previousScreen));
|
||||
ModContainer createContainer = ModList.get().getModContainerById(Create.ID).orElseThrow(() -> new IllegalStateException("Create Mod Container missing after loadCompleted"));
|
||||
createContainer.registerExtensionPoint(ExtensionPoint.CONFIGGUIFACTORY, () -> (mc, previousScreen) -> BaseConfigScreen.forCreate(previousScreen));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,14 +1,25 @@
|
|||
package com.simibubi.create.foundation.command;
|
||||
|
||||
import com.mojang.brigadier.Command;
|
||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||
import com.mojang.brigadier.builder.ArgumentBuilder;
|
||||
import com.simibubi.create.Create;
|
||||
import com.simibubi.create.foundation.config.ui.ConfigHelper;
|
||||
import com.simibubi.create.foundation.networking.AllPackets;
|
||||
|
||||
import net.minecraft.command.CommandSource;
|
||||
import net.minecraft.command.Commands;
|
||||
import net.minecraft.entity.player.ServerPlayerEntity;
|
||||
import net.minecraft.util.text.StringTextComponent;
|
||||
import net.minecraftforge.fml.config.ModConfig;
|
||||
import net.minecraftforge.fml.network.PacketDistributor;
|
||||
|
||||
import com.mojang.brigadier.Command;
|
||||
import com.mojang.brigadier.builder.ArgumentBuilder;
|
||||
import com.simibubi.create.foundation.networking.AllPackets;
|
||||
|
||||
/**
|
||||
* Examples:
|
||||
* /create config client - to open Create's ConfigGui with the client config already selected
|
||||
* /create config "botania:common" - to open Create's ConfigGui with Botania's common config already selected
|
||||
* /create config "create:client.client.rainbowDebug" set false - to disable Create's rainbow debug for the sender
|
||||
*/
|
||||
public class ConfigCommand {
|
||||
|
||||
public static ArgumentBuilder<CommandSource, ?> register() {
|
||||
|
@ -21,7 +32,59 @@ public class ConfigCommand {
|
|||
);
|
||||
|
||||
return Command.SINGLE_SUCCESS;
|
||||
});
|
||||
})
|
||||
.then(Commands.argument("path", StringArgumentType.string())
|
||||
.executes(ctx -> {
|
||||
ServerPlayerEntity player = ctx.getSource().asPlayer();
|
||||
AllPackets.channel.send(
|
||||
PacketDistributor.PLAYER.with(() -> player),
|
||||
new SConfigureConfigPacket(SConfigureConfigPacket.Actions.configScreen.name(), StringArgumentType.getString(ctx, "path"))
|
||||
);
|
||||
|
||||
return Command.SINGLE_SUCCESS;
|
||||
})
|
||||
.then(Commands.literal("set")
|
||||
.requires(cs -> cs.hasPermissionLevel(2))
|
||||
.then(Commands.argument("value", StringArgumentType.string())
|
||||
.executes(ctx -> {
|
||||
String path = StringArgumentType.getString(ctx, "path");
|
||||
String value = StringArgumentType.getString(ctx, "value");
|
||||
|
||||
|
||||
ConfigHelper.ConfigPath configPath;
|
||||
try {
|
||||
configPath = ConfigHelper.ConfigPath.parse(path);
|
||||
} catch (IllegalArgumentException e) {
|
||||
ctx.getSource().sendErrorMessage(new StringTextComponent(e.getMessage()));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (configPath.getType() == ModConfig.Type.CLIENT) {
|
||||
ServerPlayerEntity player = ctx.getSource().asPlayer();
|
||||
AllPackets.channel.send(
|
||||
PacketDistributor.PLAYER.with(() -> player),
|
||||
new SConfigureConfigPacket("SET" + path, value)
|
||||
);
|
||||
|
||||
return Command.SINGLE_SUCCESS;
|
||||
}
|
||||
|
||||
try {
|
||||
ConfigHelper.setConfigValue(configPath, value);
|
||||
ctx.getSource().sendFeedback(new StringTextComponent("Great Success!"), false);
|
||||
return Command.SINGLE_SUCCESS;
|
||||
} catch (ConfigHelper.InvalidValueException e) {
|
||||
ctx.getSource().sendErrorMessage(new StringTextComponent("Config could not be set the the specified value!"));
|
||||
return 0;
|
||||
} catch (Exception e) {
|
||||
ctx.getSource().sendErrorMessage(new StringTextComponent("Something went wrong while trying to set config value. Check the server logs for more information"));
|
||||
Create.LOGGER.warn("Exception during server-side config value set:", e);
|
||||
return 0;
|
||||
}
|
||||
})
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -9,6 +9,8 @@ import com.simibubi.create.Create;
|
|||
import com.simibubi.create.content.contraptions.goggles.GoggleConfigScreen;
|
||||
import com.simibubi.create.foundation.config.AllConfigs;
|
||||
import com.simibubi.create.foundation.config.ui.BaseConfigScreen;
|
||||
import com.simibubi.create.foundation.config.ui.ConfigHelper;
|
||||
import com.simibubi.create.foundation.config.ui.SubMenuConfigScreen;
|
||||
import com.simibubi.create.foundation.gui.ScreenOpener;
|
||||
import com.simibubi.create.foundation.networking.SimplePacketBase;
|
||||
import com.simibubi.create.foundation.ponder.PonderRegistry;
|
||||
|
@ -30,6 +32,7 @@ import net.minecraftforge.api.distmarker.Dist;
|
|||
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
import net.minecraftforge.common.ForgeConfig;
|
||||
import net.minecraftforge.fml.DistExecutor;
|
||||
import net.minecraftforge.fml.config.ModConfig;
|
||||
import net.minecraftforge.fml.network.NetworkEvent;
|
||||
|
||||
public class SConfigureConfigPacket extends SimplePacketBase {
|
||||
|
@ -57,6 +60,11 @@ public class SConfigureConfigPacket extends SimplePacketBase {
|
|||
public void handle(Supplier<NetworkEvent.Context> ctx) {
|
||||
ctx.get()
|
||||
.enqueueWork(() -> DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> {
|
||||
if (option.startsWith("SET")) {
|
||||
trySetConfig(option.substring(3), value);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
Actions.valueOf(option)
|
||||
.performAction(value);
|
||||
|
@ -70,6 +78,36 @@ public class SConfigureConfigPacket extends SimplePacketBase {
|
|||
.setPacketHandled(true);
|
||||
}
|
||||
|
||||
private static void trySetConfig(String option, String value) {
|
||||
ClientPlayerEntity player = Minecraft.getInstance().player;
|
||||
if (player == null)
|
||||
return;
|
||||
|
||||
ConfigHelper.ConfigPath configPath;
|
||||
try {
|
||||
configPath = ConfigHelper.ConfigPath.parse(option);
|
||||
} catch (IllegalArgumentException e) {
|
||||
player.sendStatusMessage(new StringTextComponent(e.getMessage()), false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (configPath.getType() != ModConfig.Type.CLIENT) {
|
||||
Create.LOGGER.warn("Received type-mismatched config packet on client");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
ConfigHelper.setConfigValue(configPath, value);
|
||||
player.sendStatusMessage(new StringTextComponent("Great Success!"), false);
|
||||
} catch (ConfigHelper.InvalidValueException e) {
|
||||
player.sendStatusMessage(new StringTextComponent("Config could not be set the the specified value!"), false);
|
||||
} catch (Exception e) {
|
||||
player.sendStatusMessage(new StringTextComponent("Something went wrong while trying to set config value. Check the client logs for more information"), false);
|
||||
Create.LOGGER.warn("Exception during client-side config value set:", e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public enum Actions {
|
||||
configScreen(() -> Actions::configScreen),
|
||||
rainbowDebug(() -> Actions::rainbowDebug),
|
||||
|
@ -95,7 +133,25 @@ public class SConfigureConfigPacket extends SimplePacketBase {
|
|||
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
private static void configScreen(String value) {
|
||||
ScreenOpener.open(new BaseConfigScreen(null));
|
||||
if (value.equals("")) {
|
||||
ScreenOpener.open(BaseConfigScreen.forCreate(null));
|
||||
return;
|
||||
}
|
||||
|
||||
ClientPlayerEntity player = Minecraft.getInstance().player;
|
||||
ConfigHelper.ConfigPath configPath;
|
||||
try {
|
||||
configPath = ConfigHelper.ConfigPath.parse(value);
|
||||
} catch (IllegalArgumentException e) {
|
||||
player.sendStatusMessage(new StringTextComponent(e.getMessage()), false);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
ScreenOpener.open(SubMenuConfigScreen.find(configPath));
|
||||
} catch (Exception e) {
|
||||
player.sendStatusMessage(new StringTextComponent("Unable to find the specified config"), false);
|
||||
}
|
||||
}
|
||||
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
package com.simibubi.create.foundation.config.ui;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import com.simibubi.create.Create;
|
||||
import com.simibubi.create.foundation.config.AllConfigs;
|
||||
import com.simibubi.create.foundation.gui.DelegatedStencilElement;
|
||||
import com.simibubi.create.foundation.gui.ScreenOpener;
|
||||
import com.simibubi.create.foundation.gui.TextStencilElement;
|
||||
import com.simibubi.create.foundation.gui.Theme;
|
||||
|
@ -11,49 +16,134 @@ import net.minecraft.client.Minecraft;
|
|||
import net.minecraft.client.gui.screen.Screen;
|
||||
import net.minecraft.util.text.StringTextComponent;
|
||||
import net.minecraft.util.text.TextFormatting;
|
||||
import net.minecraftforge.common.ForgeConfigSpec;
|
||||
import net.minecraftforge.fml.config.ModConfig;
|
||||
|
||||
public class BaseConfigScreen extends ConfigScreen {
|
||||
|
||||
private static final DelegatedStencilElement.ElementRenderer DISABLED_RENDERER = (ms, width, height, alpha) -> UIRenderHelper.angledGradient(ms, 0, 0, height / 2, height, width, Theme.i(Theme.Key.BUTTON_DISABLE, true), Theme.i(Theme.Key.BUTTON_DISABLE, false) | 0x40_000000);
|
||||
|
||||
public static BaseConfigScreen forCreate(Screen parent) {
|
||||
return new BaseConfigScreen(parent)
|
||||
.withTitles("Client Settings", "World Generation Settings", "Gameplay Settings")
|
||||
.withSpecs(AllConfigs.CLIENT.specification, AllConfigs.COMMON.specification, AllConfigs.SERVER.specification);
|
||||
}
|
||||
|
||||
BoxWidget clientConfigWidget;
|
||||
BoxWidget commonConfigWidget;
|
||||
BoxWidget serverConfigWidget;
|
||||
|
||||
public BaseConfigScreen(Screen parent) {
|
||||
ForgeConfigSpec clientSpec;
|
||||
ForgeConfigSpec commonSpec;
|
||||
ForgeConfigSpec serverSpec;
|
||||
String clientTile = "CLIENT CONFIG";
|
||||
String commonTile = "COMMON CONFIG";
|
||||
String serverTile = "SERVER CONFIG";
|
||||
String modID = Create.ID;
|
||||
|
||||
/**
|
||||
* If you are a Create Addon dev and want to make use of the same GUI
|
||||
* for your mod's config, use this Constructor to create a entry point
|
||||
*
|
||||
* @param parent the previously opened screen
|
||||
* @param modID the modID of your addon/mod
|
||||
*/
|
||||
public BaseConfigScreen(Screen parent, @Nonnull String modID) {
|
||||
this(parent);
|
||||
this.modID = modID;
|
||||
}
|
||||
|
||||
private BaseConfigScreen(Screen parent) {
|
||||
super(parent);
|
||||
}
|
||||
|
||||
/**
|
||||
* If you have static references to your Configs or ConfigSpecs (like Create does in {@link AllConfigs}),
|
||||
* please use {@link #withSpecs(ForgeConfigSpec, ForgeConfigSpec, ForgeConfigSpec)} instead
|
||||
*/
|
||||
public BaseConfigScreen searchForSpecsInModContainer() {
|
||||
try {
|
||||
clientSpec = ConfigHelper.findConfigSpecFor(ModConfig.Type.CLIENT, this.modID);
|
||||
} catch (Exception e) {
|
||||
Create.LOGGER.warn("Unable to find ClientConfigSpec for mod: " + this.modID);
|
||||
}
|
||||
|
||||
try {
|
||||
commonSpec = ConfigHelper.findConfigSpecFor(ModConfig.Type.COMMON, this.modID);
|
||||
} catch (Exception e) {
|
||||
Create.LOGGER.warn("Unable to find CommonConfigSpec for mod: " + this.modID, e);
|
||||
}
|
||||
|
||||
try {
|
||||
serverSpec = ConfigHelper.findConfigSpecFor(ModConfig.Type.SERVER, this.modID);
|
||||
} catch (Exception e) {
|
||||
Create.LOGGER.warn("Unable to find ServerConfigSpec for mod: " + this.modID, e);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public BaseConfigScreen withSpecs(@Nullable ForgeConfigSpec client, @Nullable ForgeConfigSpec common, @Nullable ForgeConfigSpec server) {
|
||||
clientSpec = client;
|
||||
commonSpec = common;
|
||||
serverSpec = server;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BaseConfigScreen withTitles(@Nullable String client, @Nullable String common, @Nullable String server) {
|
||||
if (client != null)
|
||||
clientTile = client;
|
||||
|
||||
if (common != null)
|
||||
commonTile = common;
|
||||
|
||||
if (server != null)
|
||||
serverTile = server;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void init() {
|
||||
widgets.clear();
|
||||
super.init();
|
||||
|
||||
TextStencilElement text = new TextStencilElement(client.fontRenderer, new StringTextComponent("Client Settings").formatted(TextFormatting.BOLD)).centered(true, true);
|
||||
widgets.add(clientConfigWidget = new BoxWidget(width / 2 - 100, height / 2 - 15 - 30, 200, 16)
|
||||
.showingElement(text)
|
||||
.withCallback(() -> ScreenOpener.open(new SubMenuConfigScreen(this, ModConfig.Type.CLIENT, AllConfigs.CLIENT.specification)))
|
||||
);
|
||||
text.withElementRenderer(BoxWidget.gradientFactory.apply(clientConfigWidget));
|
||||
TextStencilElement clientText = new TextStencilElement(client.fontRenderer, new StringTextComponent(clientTile).formatted(TextFormatting.BOLD)).centered(true, true);
|
||||
widgets.add(clientConfigWidget = new BoxWidget(width / 2 - 100, height / 2 - 15 - 30, 200, 16).showingElement(clientText));
|
||||
|
||||
TextStencilElement text2 = new TextStencilElement(client.fontRenderer, new StringTextComponent("World Generation Settings").formatted(TextFormatting.BOLD)).centered(true, true);
|
||||
widgets.add(commonConfigWidget = new BoxWidget(width / 2 - 100, height / 2 - 15, 200, 16)
|
||||
.showingElement(text2)
|
||||
.withCallback(() -> ScreenOpener.open(new SubMenuConfigScreen(this, ModConfig.Type.COMMON, AllConfigs.COMMON.specification)))
|
||||
);
|
||||
text2.withElementRenderer(BoxWidget.gradientFactory.apply(commonConfigWidget));
|
||||
if (clientSpec != null) {
|
||||
clientConfigWidget.withCallback(() -> ScreenOpener.open(new SubMenuConfigScreen(this, ModConfig.Type.CLIENT, clientSpec)));
|
||||
clientText.withElementRenderer(BoxWidget.gradientFactory.apply(clientConfigWidget));
|
||||
} else {
|
||||
clientConfigWidget.active = false;
|
||||
clientConfigWidget.updateColorsFromState();
|
||||
clientText.withElementRenderer(DISABLED_RENDERER);
|
||||
}
|
||||
|
||||
TextStencilElement text3 = new TextStencilElement(client.fontRenderer, new StringTextComponent("Gameplay Settings").formatted(TextFormatting.BOLD)).centered(true, true);
|
||||
widgets.add(serverConfigWidget = new BoxWidget(width / 2 - 100, height / 2 - 15 + 30, 200, 16)
|
||||
.showingElement(text3)
|
||||
);
|
||||
TextStencilElement commonText = new TextStencilElement(client.fontRenderer, new StringTextComponent(commonTile).formatted(TextFormatting.BOLD)).centered(true, true);
|
||||
widgets.add(commonConfigWidget = new BoxWidget(width / 2 - 100, height / 2 - 15, 200, 16).showingElement(commonText));
|
||||
|
||||
if (Minecraft.getInstance().world != null) {
|
||||
serverConfigWidget.withCallback(() -> ScreenOpener.open(new SubMenuConfigScreen(this, ModConfig.Type.SERVER, AllConfigs.SERVER.specification)));
|
||||
text3.withElementRenderer(BoxWidget.gradientFactory.apply(serverConfigWidget));
|
||||
if (commonSpec != null) {
|
||||
commonConfigWidget.withCallback(() -> ScreenOpener.open(new SubMenuConfigScreen(this, ModConfig.Type.COMMON, commonSpec)));
|
||||
commonText.withElementRenderer(BoxWidget.gradientFactory.apply(commonConfigWidget));
|
||||
} else {
|
||||
commonConfigWidget.active = false;
|
||||
commonConfigWidget.updateColorsFromState();
|
||||
commonText.withElementRenderer(DISABLED_RENDERER);
|
||||
}
|
||||
|
||||
TextStencilElement serverText = new TextStencilElement(client.fontRenderer, new StringTextComponent(serverTile).formatted(TextFormatting.BOLD)).centered(true, true);
|
||||
widgets.add(serverConfigWidget = new BoxWidget(width / 2 - 100, height / 2 - 15 + 30, 200, 16).showingElement(serverText));
|
||||
|
||||
if (serverSpec != null && Minecraft.getInstance().world != null) {
|
||||
serverConfigWidget.withCallback(() -> ScreenOpener.open(new SubMenuConfigScreen(this, ModConfig.Type.SERVER, serverSpec)));
|
||||
serverText.withElementRenderer(BoxWidget.gradientFactory.apply(serverConfigWidget));
|
||||
} else {
|
||||
serverConfigWidget.active = false;
|
||||
serverConfigWidget.updateColorsFromState();
|
||||
text3.withElementRenderer((ms, width, height, alpha) -> UIRenderHelper.angledGradient(ms, 0, 0, height / 2, height, width, Theme.i(Theme.Key.BUTTON_DISABLE, true), Theme.i(Theme.Key.BUTTON_DISABLE, false) | 0x40_000000));
|
||||
serverText.withElementRenderer(DISABLED_RENDERER);
|
||||
}
|
||||
|
||||
ConfigScreen.modID = this.modID;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,54 +1,65 @@
|
|||
package com.simibubi.create.foundation.config.ui;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import com.simibubi.create.foundation.command.SConfigureConfigPacket;
|
||||
import com.simibubi.create.foundation.config.AllConfigs;
|
||||
import com.simibubi.create.foundation.networking.AllPackets;
|
||||
import com.simibubi.create.Create;
|
||||
import com.simibubi.create.foundation.networking.SimplePacketBase;
|
||||
|
||||
import net.minecraft.entity.player.ServerPlayerEntity;
|
||||
import net.minecraft.network.PacketBuffer;
|
||||
import net.minecraftforge.common.ForgeConfigSpec;
|
||||
import net.minecraftforge.fml.config.ModConfig;
|
||||
import net.minecraftforge.fml.network.NetworkEvent;
|
||||
import net.minecraftforge.fml.network.PacketDistributor;
|
||||
|
||||
public class CConfigureConfigPacket<T> extends SimplePacketBase {
|
||||
|
||||
private String modID;
|
||||
private String path;
|
||||
private String value;
|
||||
|
||||
public CConfigureConfigPacket(String path, T value) {
|
||||
public CConfigureConfigPacket(String modID, String path, T value) {
|
||||
this.modID = Objects.requireNonNull(modID);
|
||||
this.path = path;
|
||||
this.value = serialize(value);
|
||||
}
|
||||
|
||||
public CConfigureConfigPacket(PacketBuffer buffer) {
|
||||
this.modID = buffer.readString(32767);
|
||||
this.path = buffer.readString(32767);
|
||||
this.value = buffer.readString(32767);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(PacketBuffer buffer) {
|
||||
buffer.writeString(modID);
|
||||
buffer.writeString(path);
|
||||
buffer.writeString(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(Supplier<NetworkEvent.Context> context) {
|
||||
ServerPlayerEntity sender = context.get().getSender();
|
||||
if (sender == null || !sender.hasPermissionLevel(2))
|
||||
return;
|
||||
context.get().enqueueWork(() -> {
|
||||
try {
|
||||
ServerPlayerEntity sender = context.get().getSender();
|
||||
if (sender == null || !sender.hasPermissionLevel(2))
|
||||
return;
|
||||
|
||||
ForgeConfigSpec.ValueSpec valueSpec = AllConfigs.SERVER.specification.getRaw(path);
|
||||
ForgeConfigSpec.ConfigValue<T> configValue = AllConfigs.SERVER.specification.getValues().get(path);
|
||||
ForgeConfigSpec spec = ConfigHelper.findConfigSpecFor(ModConfig.Type.SERVER, modID);
|
||||
ForgeConfigSpec.ValueSpec valueSpec = spec.getRaw(path);
|
||||
ForgeConfigSpec.ConfigValue<T> configValue = spec.getValues().get(path);
|
||||
|
||||
T v = (T) deserialize(configValue.get(), value);
|
||||
if (!valueSpec.test(v))
|
||||
return;
|
||||
T v = (T) deserialize(configValue.get(), value);
|
||||
if (!valueSpec.test(v))
|
||||
return;
|
||||
|
||||
configValue.set(v);
|
||||
configValue.set(v);
|
||||
} catch (Exception e) {
|
||||
Create.LOGGER.warn("Unable to handle ConfigureConfig Packet. ", e);
|
||||
}
|
||||
});
|
||||
|
||||
context.get().setPacketHandled(true);
|
||||
}
|
||||
|
||||
public String serialize(T value) {
|
||||
|
@ -66,7 +77,7 @@ public class CConfigureConfigPacket<T> extends SimplePacketBase {
|
|||
throw new IllegalArgumentException("unknown type " + value + ": " + value.getClass().getSimpleName());
|
||||
}
|
||||
|
||||
public Object deserialize(Object type, String sValue) {
|
||||
public static Object deserialize(Object type, String sValue) {
|
||||
if (type instanceof Boolean)
|
||||
return Boolean.parseBoolean(sValue);
|
||||
if (type instanceof Enum<?>)
|
||||
|
|
|
@ -0,0 +1,142 @@
|
|||
package com.simibubi.create.foundation.config.ui;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.EnumMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.simibubi.create.Create;
|
||||
import com.simibubi.create.foundation.config.AllConfigs;
|
||||
|
||||
import net.minecraftforge.common.ForgeConfigSpec;
|
||||
import net.minecraftforge.fml.ModContainer;
|
||||
import net.minecraftforge.fml.ModList;
|
||||
import net.minecraftforge.fml.common.ObfuscationReflectionHelper;
|
||||
import net.minecraftforge.fml.config.ModConfig;
|
||||
|
||||
public class ConfigHelper {
|
||||
|
||||
private static final LoadingCache<String, EnumMap<ModConfig.Type, ModConfig>> configCache = CacheBuilder.newBuilder().expireAfterAccess(15, TimeUnit.SECONDS).build(
|
||||
new CacheLoader<String, EnumMap<ModConfig.Type, ModConfig>>() {
|
||||
@Override
|
||||
public EnumMap<ModConfig.Type, ModConfig> load(@Nonnull String key) {
|
||||
return findModConfigsUncached(key);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
private static EnumMap<ModConfig.Type, ModConfig> findModConfigsUncached(String modID) {
|
||||
ModContainer modContainer = ModList.get().getModContainerById(modID).orElseThrow(() -> new IllegalArgumentException("Unable to find ModContainer for id: " + modID));
|
||||
EnumMap<ModConfig.Type, ModConfig> configs = ObfuscationReflectionHelper.getPrivateValue(ModContainer.class, modContainer, "configs");
|
||||
return Objects.requireNonNull(configs);
|
||||
}
|
||||
|
||||
public static ForgeConfigSpec findConfigSpecFor(ModConfig.Type type, String modID) {
|
||||
if (!modID.equals(Create.ID))
|
||||
return configCache.getUnchecked(modID).get(type).getSpec();
|
||||
|
||||
switch (type) {
|
||||
case COMMON:
|
||||
return AllConfigs.COMMON.specification;
|
||||
case CLIENT:
|
||||
return AllConfigs.CLIENT.specification;
|
||||
case SERVER:
|
||||
return AllConfigs.SERVER.specification;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
//Directly set a value
|
||||
public static <T> void setConfigValue(ConfigPath path, String value) throws InvalidValueException {
|
||||
ForgeConfigSpec spec = findConfigSpecFor(path.getType(), path.getModID());
|
||||
List<String> pathList = Arrays.asList(path.getPath());
|
||||
ForgeConfigSpec.ValueSpec valueSpec = spec.getRaw(pathList);
|
||||
ForgeConfigSpec.ConfigValue<T> configValue = spec.getValues().get(pathList);
|
||||
T v = (T) CConfigureConfigPacket.deserialize(configValue.get(), value);
|
||||
if (!valueSpec.test(v))
|
||||
throw new InvalidValueException();
|
||||
|
||||
configValue.set(v);
|
||||
}
|
||||
|
||||
//Add a value to the current UI's changes list
|
||||
public static <T> void setValue(String path, ForgeConfigSpec.ConfigValue<T> configValue, T value) {
|
||||
if (value.equals(configValue.get())) {
|
||||
ConfigScreen.changes.remove(path);
|
||||
} else {
|
||||
ConfigScreen.changes.put(path, value);
|
||||
}
|
||||
}
|
||||
|
||||
//Get a value from the current UI's changes list or the config value, if its unchanged
|
||||
public static <T> T getValue(String path, ForgeConfigSpec.ConfigValue<T> configValue) {
|
||||
//noinspection unchecked
|
||||
return (T) ConfigScreen.changes.getOrDefault(path, configValue.get());
|
||||
}
|
||||
|
||||
public static class ConfigPath {
|
||||
private String modID = Create.ID;
|
||||
private ModConfig.Type type = ModConfig.Type.CLIENT;
|
||||
private String[] path;
|
||||
|
||||
public static ConfigPath parse(String string) {
|
||||
ConfigPath cp = new ConfigPath();
|
||||
String p = string;
|
||||
int index = string.indexOf(":");
|
||||
if (index >= 0) {
|
||||
p = string.substring(index + 1);
|
||||
if (index >= 1) {
|
||||
cp.modID = string.substring(0, index);
|
||||
}
|
||||
}
|
||||
String[] split = p.split("\\.");
|
||||
try {
|
||||
cp.type = ModConfig.Type.valueOf(split[0].toUpperCase(Locale.ROOT));
|
||||
} catch (Exception e) {
|
||||
throw new IllegalArgumentException("path must start with either 'client.', 'common.' or 'server.'");
|
||||
}
|
||||
|
||||
cp.path = new String[split.length - 1];
|
||||
System.arraycopy(split, 1, cp.path, 0, cp.path.length);
|
||||
|
||||
return cp;
|
||||
}
|
||||
|
||||
public ConfigPath setID(String modID) {
|
||||
this.modID = modID;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ConfigPath setType(ModConfig.Type type) {
|
||||
this.type = type;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ConfigPath setPath(String[] path) {
|
||||
this.path = path;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getModID() {
|
||||
return modID;
|
||||
}
|
||||
|
||||
public ModConfig.Type getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public String[] getPath() {
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
public static class InvalidValueException extends Exception {}
|
||||
}
|
|
@ -41,6 +41,7 @@ public abstract class ConfigScreen extends AbstractSimiScreen {
|
|||
public static final PhysicalFloat cogSpin = PhysicalFloat.create().withDrag(0.3).addForce(new Force.Static(.2f));
|
||||
public static final BlockState cogwheelState = AllBlocks.LARGE_COGWHEEL.getDefaultState().with(CogWheelBlock.AXIS, Direction.Axis.Y);
|
||||
public static final Map<String, Object> changes = new HashMap<>();
|
||||
public static String modID = null;
|
||||
protected final Screen parent;
|
||||
|
||||
public ConfigScreen(Screen parent) {
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
package com.simibubi.create.foundation.config.ui;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.mojang.blaze3d.matrix.MatrixStack;
|
||||
|
||||
import com.simibubi.create.AllItems;
|
||||
|
||||
import com.simibubi.create.foundation.config.AllConfigs;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
|
@ -19,10 +21,6 @@ import net.minecraftforge.client.event.GuiScreenEvent;
|
|||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class OpenConfigButton extends Button {
|
||||
|
||||
public static ItemStack icon = AllItems.GOGGLES.asStack();
|
||||
|
@ -38,7 +36,7 @@ public class OpenConfigButton extends Button {
|
|||
}
|
||||
|
||||
public static void click(Button b) {
|
||||
Minecraft.getInstance().displayGuiScreen(new BaseConfigScreen(Minecraft.getInstance().currentScreen));
|
||||
Minecraft.getInstance().displayGuiScreen(BaseConfigScreen.forCreate(Minecraft.getInstance().currentScreen));
|
||||
}
|
||||
|
||||
public static class SingleMenuRow {
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
package com.simibubi.create.foundation.config.ui;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
@ -9,6 +12,7 @@ import org.lwjgl.glfw.GLFW;
|
|||
|
||||
import com.electronwill.nightconfig.core.AbstractConfig;
|
||||
import com.electronwill.nightconfig.core.UnmodifiableConfig;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.mojang.blaze3d.matrix.MatrixStack;
|
||||
import com.simibubi.create.foundation.config.ui.entries.BooleanEntry;
|
||||
import com.simibubi.create.foundation.config.ui.entries.EnumEntry;
|
||||
|
@ -50,6 +54,37 @@ public class SubMenuConfigScreen extends ConfigScreen {
|
|||
protected int listWidth;
|
||||
protected String title;
|
||||
|
||||
public static SubMenuConfigScreen find(ConfigHelper.ConfigPath path) {
|
||||
ForgeConfigSpec spec = ConfigHelper.findConfigSpecFor(path.getType(), path.getModID());
|
||||
UnmodifiableConfig values = spec.getValues();
|
||||
BaseConfigScreen base = new BaseConfigScreen(null, path.getModID()).searchForSpecsInModContainer();
|
||||
SubMenuConfigScreen screen = new SubMenuConfigScreen(base, "root", path.getType(), spec, values);
|
||||
List<String> remainingPath = Lists.newArrayList(path.getPath());
|
||||
|
||||
path: while (!remainingPath.isEmpty()) {
|
||||
String next = remainingPath.remove(0);
|
||||
for (Map.Entry<String, Object> entry : values.valueMap().entrySet()) {
|
||||
String key = entry.getKey();
|
||||
Object obj = entry.getValue();
|
||||
if (!key.equalsIgnoreCase(next))
|
||||
continue;
|
||||
|
||||
if (!(obj instanceof AbstractConfig)) {
|
||||
//highlight entry
|
||||
continue;
|
||||
}
|
||||
|
||||
values = (UnmodifiableConfig) obj;
|
||||
screen = new SubMenuConfigScreen(screen, toHumanReadable(key), path.getType(), spec, values);
|
||||
continue path;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
ConfigScreen.modID = path.getModID();
|
||||
return screen;
|
||||
}
|
||||
|
||||
public SubMenuConfigScreen(Screen parent, String title, ModConfig.Type type, ForgeConfigSpec configSpec, UnmodifiableConfig configGroup) {
|
||||
super(parent);
|
||||
|
@ -81,7 +116,7 @@ public class SubMenuConfigScreen extends ConfigScreen {
|
|||
ForgeConfigSpec.ConfigValue configValue = values.get(path);
|
||||
configValue.set(value);
|
||||
if (type == ModConfig.Type.SERVER) {
|
||||
AllPackets.channel.sendToServer(new CConfigureConfigPacket<>(path, value));
|
||||
AllPackets.channel.sendToServer(new CConfigureConfigPacket<>(ConfigScreen.modID, path, value));
|
||||
}
|
||||
});
|
||||
clearChanges();
|
||||
|
@ -92,11 +127,10 @@ public class SubMenuConfigScreen extends ConfigScreen {
|
|||
if (obj instanceof AbstractConfig) {
|
||||
resetConfig((UnmodifiableConfig) obj);
|
||||
} else if (obj instanceof ForgeConfigSpec.ConfigValue<?>) {
|
||||
ForgeConfigSpec.ConfigValue<?> configValue = (ForgeConfigSpec.ConfigValue<?>) obj;
|
||||
ForgeConfigSpec.ValueSpec valueSpec = spec.getRaw(configValue.getPath());
|
||||
ForgeConfigSpec.ConfigValue configValue = (ForgeConfigSpec.ConfigValue<?>) obj;
|
||||
ForgeConfigSpec.ValueSpec valueSpec = spec.getRaw((List<String>) configValue.getPath());
|
||||
|
||||
if (!configValue.get().equals(valueSpec.getDefault()))
|
||||
changes.put(String.join(".", configValue.getPath()), valueSpec.getDefault());
|
||||
ConfigHelper.setValue(String.join(".", configValue.getPath()), configValue, valueSpec.getDefault());
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -265,7 +299,7 @@ public class SubMenuConfigScreen extends ConfigScreen {
|
|||
super.renderWindow(ms, mouseX, mouseY, partialTicks);
|
||||
|
||||
int x = width/2;
|
||||
drawCenteredString(ms, client.fontRenderer, "Editing config: " + type.toString() + "@" + title, x, 15, Theme.i(Theme.Key.TEXT));
|
||||
drawCenteredString(ms, client.fontRenderer, "Editing config: " + ConfigScreen.modID + ":" + type.toString().toLowerCase(Locale.ROOT) + "@" + title, x, 15, Theme.i(Theme.Key.TEXT));
|
||||
|
||||
list.render(ms, mouseX, mouseY, partialTicks);
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import javax.annotation.Nonnull;
|
|||
import org.apache.commons.lang3.ArrayUtils;
|
||||
|
||||
import com.mojang.blaze3d.matrix.MatrixStack;
|
||||
import com.simibubi.create.foundation.config.ui.ConfigHelper;
|
||||
import com.simibubi.create.foundation.config.ui.ConfigScreen;
|
||||
import com.simibubi.create.foundation.config.ui.ConfigScreenList;
|
||||
import com.simibubi.create.foundation.gui.AllIcons;
|
||||
|
@ -115,20 +116,13 @@ public class ValueEntry<T> extends ConfigScreenList.LabeledEntry {
|
|||
}
|
||||
|
||||
public void setValue(@Nonnull T value) {
|
||||
if (value.equals(this.value.get())) {
|
||||
ConfigScreen.changes.remove(path);
|
||||
onValueChange(value);
|
||||
return;
|
||||
}
|
||||
|
||||
ConfigScreen.changes.put(path, value);
|
||||
ConfigHelper.setValue(path, this.value, value);
|
||||
onValueChange(value);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public T getValue() {
|
||||
//noinspection unchecked
|
||||
return (T) ConfigScreen.changes.getOrDefault(path, this.value.get());
|
||||
return ConfigHelper.getValue(path, this.value);
|
||||
}
|
||||
|
||||
protected boolean isCurrentValueChanged() {
|
||||
|
|
|
@ -99,6 +99,14 @@ public class ConfirmationScreen extends AbstractSimiScreen {
|
|||
textHeight = text.size() * (client.fontRenderer.FONT_HEIGHT + 1) + 4;
|
||||
textWidth = 300;
|
||||
|
||||
if (centered) {
|
||||
x = width/2 - textWidth/2 - 2;
|
||||
y = height/2 - textHeight/2 - 16;
|
||||
} else {
|
||||
x = Math.max(0, x - textWidth / 2);
|
||||
y = Math.max(0, y -= textHeight);
|
||||
}
|
||||
|
||||
if (x + textWidth > width) {
|
||||
x = width - textWidth;
|
||||
}
|
||||
|
@ -107,11 +115,6 @@ public class ConfirmationScreen extends AbstractSimiScreen {
|
|||
y = height - textHeight - 30;
|
||||
}
|
||||
|
||||
if (centered) {
|
||||
x = width/2 - textWidth/2 - 2;
|
||||
y = height/2 - textHeight/2 - 16;
|
||||
}
|
||||
|
||||
TextStencilElement confirmText = new TextStencilElement(client.fontRenderer, "Confirm").centered(true, true);
|
||||
confirm = new BoxWidget(x + 4, y + textHeight + 2 , textWidth/2 - 10, 20)
|
||||
.withCallback(() -> accept(true));
|
||||
|
|
|
@ -102,12 +102,15 @@ public class PlacementOffset {
|
|||
|
||||
return world.getBlockState(new BlockPos(pos)).getMaterial().isReplaceable();
|
||||
}
|
||||
|
||||
|
||||
public ActionResultType placeInWorld(World world, BlockItem blockItem, PlayerEntity player, Hand hand, BlockRayTraceResult ray) {
|
||||
|
||||
if (!isReplaceable(world))
|
||||
return ActionResultType.PASS;
|
||||
|
||||
if (world.isRemote)
|
||||
return ActionResultType.SUCCESS;
|
||||
|
||||
ItemUseContext context = new ItemUseContext(player, hand, ray);
|
||||
BlockPos newPos = new BlockPos(pos);
|
||||
|
||||
|
@ -135,9 +138,6 @@ public class PlacementOffset {
|
|||
|
||||
player.addStat(Stats.ITEM_USED.get(blockItem));
|
||||
|
||||
if (world.isRemote)
|
||||
return ActionResultType.SUCCESS;
|
||||
|
||||
if (player instanceof ServerPlayerEntity)
|
||||
CriteriaTriggers.PLACED_BLOCK.trigger((ServerPlayerEntity) player, newPos, context.getItem());
|
||||
|
||||
|
|
Loading…
Reference in a new issue