From ab4404bbdba8851a9ec1a9232ef70252f0ec2b5e Mon Sep 17 00:00:00 2001 From: roadhog360 Date: Fri, 6 Sep 2024 01:36:13 -0500 Subject: [PATCH] Add first config options Signed-off-by: roadhog360 --- .../configuration/ConfigBase.java | 107 ++++++++++++++++++ .../configuration/configs/ConfigMain.java | 34 ++++++ .../configs/ConfigModCompat.java | 33 ++++++ .../core/DefaultPlayerSkin.java | 94 +++++++++++++++ .../simpleskinbackport/core/Utils.java | 48 -------- .../SimpleSkinBackportEarlyMixins.java | 7 ++ .../early/MixinAbstractClientPlayer.java | 16 +-- .../mixins/late/MixinRenderTFGiant.java | 21 +++- 8 files changed, 302 insertions(+), 58 deletions(-) create mode 100644 src/main/java/roadhog360/simpleskinbackport/configuration/ConfigBase.java create mode 100644 src/main/java/roadhog360/simpleskinbackport/configuration/configs/ConfigMain.java create mode 100644 src/main/java/roadhog360/simpleskinbackport/configuration/configs/ConfigModCompat.java create mode 100644 src/main/java/roadhog360/simpleskinbackport/core/DefaultPlayerSkin.java diff --git a/src/main/java/roadhog360/simpleskinbackport/configuration/ConfigBase.java b/src/main/java/roadhog360/simpleskinbackport/configuration/ConfigBase.java new file mode 100644 index 0000000..ed4621e --- /dev/null +++ b/src/main/java/roadhog360/simpleskinbackport/configuration/ConfigBase.java @@ -0,0 +1,107 @@ +package roadhog360.simpleskinbackport.configuration; + +import cpw.mods.fml.client.event.ConfigChangedEvent; +import cpw.mods.fml.common.eventhandler.SubscribeEvent; +import net.minecraft.launchwrapper.Launch; +import net.minecraftforge.common.config.ConfigCategory; +import net.minecraftforge.common.config.Configuration; +import net.minecraftforge.common.config.Property; +import org.apache.commons.lang3.ArrayUtils; +import org.spongepowered.asm.mixin.MixinEnvironment; +import roadhog360.simpleskinbackport.SimpleSkinBackport; +import roadhog360.simpleskinbackport.configuration.configs.ConfigMain; +import roadhog360.simpleskinbackport.configuration.configs.ConfigModCompat; +import roadhog360.simpleskinbackport.core.DefaultPlayerSkin; +import roadhog360.simpleskinbackport.mixinplugin.SimpleSkinBackportEarlyMixins; + +import java.io.File; +import java.util.*; + +public abstract class ConfigBase extends Configuration { + protected final List configCats = new ArrayList<>(); + private static final Set CONFIGS = new HashSet<>(); + + public static final String configDir = "config" + File.separator + SimpleSkinBackport.MODID + File.separator; + + public static final ConfigBase MAIN = new ConfigMain(createConfigFile("main")); + public static final ConfigBase MOD_COMPAT = new ConfigModCompat(createConfigFile("modcompat")); + + public ConfigBase(File file) { + super(file); + CONFIGS.add(this); + } + + private static File createConfigFile(String name) { + return new File(Launch.minecraftHome, configDir + name + ".cfg"); + } + + public static void initializeConfigs() { + for (ConfigBase config : CONFIGS) { + config.syncConfig(); + } + } + + private void syncConfig() { + syncConfigOptions(); + + for (ConfigCategory cat : configCats) { + if (SimpleSkinBackportEarlyMixins.SIDE == MixinEnvironment.Side.SERVER) { + if (cat.getName().toLowerCase().contains("client")) { + for (Property prop : cat.getOrderedValues()) { + cat.remove(prop.getName()); + } + } + } + + if (cat.isEmpty() && !cat.getName().toLowerCase().contains("experiment")) { + removeCategory(cat); + } + } + + if (hasChanged()) { + save(); + } + } + + protected abstract void syncConfigOptions(); + + /** + * Used in case we need to wait till later to initialize some config values. + */ + protected void initValues() { + } + + public static void postInit() { + for (ConfigBase config : CONFIGS) { + config.initValues(); + } + } + + @SubscribeEvent + public void onConfigChanged(ConfigChangedEvent.OnConfigChangedEvent eventArgs) { + if (SimpleSkinBackport.MODID.equals(eventArgs.modID)) + syncConfig(); + } + + protected static String[] getSkinReplacementModes(boolean isNoneAllowed) { + String[] array = new String[0]; + if(isNoneAllowed) { + ArrayUtils.add(array, "NONE"); + } + Arrays.stream(DefaultPlayerSkin.Set.values()).forEachOrdered(mode -> ArrayUtils.add(array, mode.name())); + return array; + } + + protected static String getSkinReplacementDescriptions() { + StringBuilder sb = new StringBuilder(); + DefaultPlayerSkin.Set[] values = DefaultPlayerSkin.Set.values(); + for (int i = 0, valuesLength = values.length; i < valuesLength; i++) { + DefaultPlayerSkin.Set skinSet = values[i]; + sb.append(skinSet.name()).append(": ").append(skinSet.getDescription()); + if(i < valuesLength - 1) { + sb.append("\n"); + } + } + return sb.toString(); + } +} diff --git a/src/main/java/roadhog360/simpleskinbackport/configuration/configs/ConfigMain.java b/src/main/java/roadhog360/simpleskinbackport/configuration/configs/ConfigMain.java new file mode 100644 index 0000000..a85b21f --- /dev/null +++ b/src/main/java/roadhog360/simpleskinbackport/configuration/configs/ConfigMain.java @@ -0,0 +1,34 @@ +package roadhog360.simpleskinbackport.configuration.configs; + +import roadhog360.simpleskinbackport.configuration.ConfigBase; +import roadhog360.simpleskinbackport.core.DefaultPlayerSkin; + +import java.io.File; + +public class ConfigMain extends ConfigBase { + + public static DefaultPlayerSkin.Set defaultSkinSet; + + static final String catSkins = "skins"; + static final String catHeads = "skins"; + + public ConfigMain(File file) { + super(file); + + getCategory(catSkins).setComment("Settings for mod-specific patches."); + + configCats.add(getCategory(catSkins)); + } + + @Override + protected void syncConfigOptions() { + defaultSkinSet = DefaultPlayerSkin.Set.valueOf( + getString("defaultSkinSet", catSkins, DefaultPlayerSkin.Set.ALL_DEFAULTS.name(), + """ + What default skin set should we use for players? This only affects players whose skins are not loaded, or do not have a custom skin. + Note that players may also set their skin to one of the new defaults in their Minecraft launcher, and that will take precedent over this option. + Each set contains the following skins: + """ + ConfigBase.getSkinReplacementDescriptions(), ConfigBase.getSkinReplacementModes(false)) + ); + } +} diff --git a/src/main/java/roadhog360/simpleskinbackport/configuration/configs/ConfigModCompat.java b/src/main/java/roadhog360/simpleskinbackport/configuration/configs/ConfigModCompat.java new file mode 100644 index 0000000..78da239 --- /dev/null +++ b/src/main/java/roadhog360/simpleskinbackport/configuration/configs/ConfigModCompat.java @@ -0,0 +1,33 @@ +package roadhog360.simpleskinbackport.configuration.configs; + +import roadhog360.simpleskinbackport.configuration.ConfigBase; +import roadhog360.simpleskinbackport.core.DefaultPlayerSkin; + +import java.io.File; + +public class ConfigModCompat extends ConfigBase { + + public static DefaultPlayerSkin.Set TFgiantSkinSet; + + static final String catMisc = "misc"; + + public ConfigModCompat(File file) { + super(file); + + getCategory(catMisc).setComment("Settings for mod-specific patches."); + + configCats.add(getCategory(catMisc)); + } + + + //TODO: Move Iron Chest checks here + @Override + protected void syncConfigOptions() { + TFgiantSkinSet = DefaultPlayerSkin.Set.valueOfOrNull( + getString("TwilightForest:giantSkinSet", catMisc, DefaultPlayerSkin.NONE, + """ + What default skin set should we use for Twilight Forest giants? NONE means they copy the client player's skin and arms + The other values set a skin randomly assigned based on the Giant's UUID. See the main config for more info on these.""", ConfigBase.getSkinReplacementModes(true)) + ); + } +} diff --git a/src/main/java/roadhog360/simpleskinbackport/core/DefaultPlayerSkin.java b/src/main/java/roadhog360/simpleskinbackport/core/DefaultPlayerSkin.java new file mode 100644 index 0000000..d2fb7c9 --- /dev/null +++ b/src/main/java/roadhog360/simpleskinbackport/core/DefaultPlayerSkin.java @@ -0,0 +1,94 @@ +package roadhog360.simpleskinbackport.core; + +import com.google.common.collect.Lists; +import net.minecraft.util.ResourceLocation; +import org.apache.commons.lang3.tuple.Pair; + +import java.util.Arrays; +import java.util.List; +import java.util.UUID; + +public class DefaultPlayerSkin { + private static final ResourceLocation[] DEFAULT_SKINS = new ResourceLocation[] { + new ResourceLocation("textures/entity/player/slim/alex.png"), + new ResourceLocation("textures/entity/player/slim/ari.png"), + new ResourceLocation("textures/entity/player/slim/efe.png"), + new ResourceLocation("textures/entity/player/slim/kai.png"), + new ResourceLocation("textures/entity/player/slim/makena.png"), + new ResourceLocation("textures/entity/player/slim/noor.png"), + new ResourceLocation("textures/entity/player/slim/steve.png"), + new ResourceLocation("textures/entity/player/slim/sunny.png"), + new ResourceLocation("textures/entity/player/slim/zuri.png"), + + new ResourceLocation("textures/entity/player/wide/alex.png"), + new ResourceLocation("textures/entity/player/wide/ari.png"), + new ResourceLocation("textures/entity/player/wide/efe.png"), + new ResourceLocation("textures/entity/player/wide/kai.png"), + new ResourceLocation("textures/entity/player/wide/makena.png"), + new ResourceLocation("textures/entity/player/wide/noor.png"), + new ResourceLocation("textures/entity/player/wide/steve.png"), + new ResourceLocation("textures/entity/player/wide/sunny.png"), + new ResourceLocation("textures/entity/player/wide/zuri.png")}; + + public static final ResourceLocation ALEX = DEFAULT_SKINS[0]; + public static final ResourceLocation STEVE = DEFAULT_SKINS[15]; + + public static final UUID NULL_UUID = UUID.randomUUID(); //Used if the UUID passed in is somehow null. + + public static final String NONE = "NONE"; + + public enum Set { + STEVE_ONLY("The only default skin will be Steve, the tried-and-true classic player model. His arms will always be wide.", STEVE), + STEVE_ALEX("The default skins will either be wide-armed Steve or slim-armed Alex, as it was in 1.8 to 1.19.2.", STEVE, ALEX), + ALL_DEFAULTS("Steve, Alex, and all of the new 1.19.3 default skins will be assigned. They all can appear in slim or wide armed variants.", DEFAULT_SKINS), +// BONUS_CHARACTERS, //Some of the alternate skins from the old legacy console default skin packs. Currently not implemented + ; + + private final List> skins = Lists.newLinkedList(); + private final String description; + + Set(String description, ResourceLocation... locs) { + this.description = description; + for(ResourceLocation location : locs) { + skins.add(Pair.of(location, location.toString().contains("slim"))); + } + } + + public ResourceLocation getDefaultSkin(int index) { + return getEntryFromIndex(index).getLeft(); + } + + public ResourceLocation getDefaultSkin(UUID uuid) { + return getDefaultSkin(uuid.hashCode()); + } + + public boolean isDefaultSkinSlim(int index) { + return getEntryFromIndex(index).getRight(); + } + + public boolean isDefaultSkinSlim(UUID uuid) { + return isDefaultSkinSlim(uuid.hashCode()); + } + + private Pair getEntryFromIndex(int index) { + if(skins.isEmpty()) { + throw new RuntimeException("Default skin set " + name() + " had empty skin list. What are you doing!?"); + } + if(skins.size() == 1) { + return skins.get(0); + } + if(skins.size() == 2) { + return skins.get(index & 1);//1.8 skin choosing logic, returns Alex if UUID hash & 1 == 1 + } + return skins.get(Math.floorMod(index, skins.size()));//1.19+ skin choosing logic. Uses Math.floorMod to roll the index of skin based on UUID hash + } + + public String getDescription() { + return description; + } + + public static Set valueOfOrNull(String value) { + return Arrays.stream(Set.values()).filter(skinSet -> skinSet.name().equals(value)).findFirst().orElse(null); + } + } +} diff --git a/src/main/java/roadhog360/simpleskinbackport/core/Utils.java b/src/main/java/roadhog360/simpleskinbackport/core/Utils.java index df8b3fc..0deb2f7 100644 --- a/src/main/java/roadhog360/simpleskinbackport/core/Utils.java +++ b/src/main/java/roadhog360/simpleskinbackport/core/Utils.java @@ -12,7 +12,6 @@ import net.minecraft.client.resources.SkinManager; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.util.MathHelper; -import net.minecraft.util.ResourceLocation; import roadhog360.simpleskinbackport.ducks.IArmsState; import roadhog360.simpleskinbackport.ducks.ITransparentBox; @@ -20,55 +19,8 @@ import java.util.Base64; import java.util.List; import java.util.Objects; -import java.util.UUID; public class Utils { - private static final ResourceLocation[] DEFAULT_SKINS = new ResourceLocation[] { - new ResourceLocation("textures/entity/player/slim/alex.png"), - new ResourceLocation("textures/entity/player/slim/ari.png"), - new ResourceLocation("textures/entity/player/slim/efe.png"), - new ResourceLocation("textures/entity/player/slim/kai.png"), - new ResourceLocation("textures/entity/player/slim/makena.png"), - new ResourceLocation("textures/entity/player/slim/noor.png"), - new ResourceLocation("textures/entity/player/slim/steve.png"), - new ResourceLocation("textures/entity/player/slim/sunny.png"), - new ResourceLocation("textures/entity/player/slim/zuri.png"), - - new ResourceLocation("textures/entity/player/wide/alex.png"), - new ResourceLocation("textures/entity/player/wide/ari.png"), - new ResourceLocation("textures/entity/player/wide/efe.png"), - new ResourceLocation("textures/entity/player/wide/kai.png"), - new ResourceLocation("textures/entity/player/wide/makena.png"), - new ResourceLocation("textures/entity/player/wide/noor.png"), - new ResourceLocation("textures/entity/player/wide/steve.png"), - new ResourceLocation("textures/entity/player/wide/sunny.png"), - new ResourceLocation("textures/entity/player/wide/zuri.png")}; - - public static final ResourceLocation STEVE = DEFAULT_SKINS[15]; - public static final ResourceLocation ALEX = DEFAULT_SKINS[0]; - - public static int getIndexFromUUID(UUID uuid) { - if(uuid == null) { //TODO Config to not use new skins perhaps? - return 15; - } - return Math.floorMod(uuid.hashCode(), DEFAULT_SKINS.length); - } - - public static ResourceLocation getDefaultSkin(int index) { - return DEFAULT_SKINS[index % DEFAULT_SKINS.length]; - } - - public static ResourceLocation getDefaultSkin(UUID uuid) { - return getDefaultSkin(getIndexFromUUID(uuid)); - } - - public static boolean isDefaultSkinSlim(int index) { - return index % DEFAULT_SKINS.length < DEFAULT_SKINS.length / 2; - } - - public static boolean isDefaultSkinSlim(UUID uuid) { - return isDefaultSkinSlim(getIndexFromUUID(uuid)); - } public static boolean getSlimFromBase64Data(String base64) { JsonObject props = new Gson().fromJson(new String(Base64.getDecoder().decode(base64), StandardCharsets.UTF_8), JsonObject.class); diff --git a/src/main/java/roadhog360/simpleskinbackport/mixinplugin/SimpleSkinBackportEarlyMixins.java b/src/main/java/roadhog360/simpleskinbackport/mixinplugin/SimpleSkinBackportEarlyMixins.java index e0d89db..354f317 100644 --- a/src/main/java/roadhog360/simpleskinbackport/mixinplugin/SimpleSkinBackportEarlyMixins.java +++ b/src/main/java/roadhog360/simpleskinbackport/mixinplugin/SimpleSkinBackportEarlyMixins.java @@ -3,6 +3,7 @@ import com.gtnewhorizon.gtnhmixins.IEarlyMixinLoader; import cpw.mods.fml.relauncher.IFMLLoadingPlugin; import org.spongepowered.asm.mixin.MixinEnvironment; +import roadhog360.simpleskinbackport.configuration.ConfigBase; import java.util.ArrayList; import java.util.List; @@ -20,6 +21,12 @@ public String getMixinConfig() { @Override public List getMixins(Set loadedCoreMods) { + try { + ConfigBase.initializeConfigs(); + } catch (Exception e) { + throw new RuntimeException("Configs failed to load!", e); + } + List mixins = new ArrayList<>(); mixins.add("MixinEntityPlayer"); if (SIDE == MixinEnvironment.Side.CLIENT) { diff --git a/src/main/java/roadhog360/simpleskinbackport/mixins/early/MixinAbstractClientPlayer.java b/src/main/java/roadhog360/simpleskinbackport/mixins/early/MixinAbstractClientPlayer.java index 4bb4601..7405179 100644 --- a/src/main/java/roadhog360/simpleskinbackport/mixins/early/MixinAbstractClientPlayer.java +++ b/src/main/java/roadhog360/simpleskinbackport/mixins/early/MixinAbstractClientPlayer.java @@ -11,7 +11,7 @@ import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import roadhog360.simpleskinbackport.core.Utils; +import roadhog360.simpleskinbackport.configuration.configs.ConfigMain; import roadhog360.simpleskinbackport.ducks.IArmsState; @Mixin(AbstractClientPlayer.class) @@ -23,13 +23,13 @@ public MixinAbstractClientPlayer(World p_i45324_1_, GameProfile p_i45324_2_) { @Inject(method = "", at = @At(value = "TAIL")) private void setDefaultSkinState(World p_i45074_1_, GameProfile p_i45074_2_, CallbackInfo ci) { - simpleSkinBackport$setSlim(Utils.isDefaultSkinSlim(getPersistentID())); + simpleSkinBackport$setSlim(ConfigMain.defaultSkinSet.isDefaultSkinSlim(getPersistentID())); } @Redirect(method = "getLocationSkin()Lnet/minecraft/util/ResourceLocation;", at = @At(value = "FIELD", target = "Lnet/minecraft/client/entity/AbstractClientPlayer;locationStevePng:Lnet/minecraft/util/ResourceLocation;")) private ResourceLocation setNewDefaultSkinBehavior() { - return Utils.getDefaultSkin(getPersistentID()); + return ConfigMain.defaultSkinSet.getDefaultSkin(getPersistentID()); } // UUID uuid = UUID.randomUUID(); @@ -42,9 +42,11 @@ private ResourceLocation setNewDefaultSkinBehavior() { // @WrapOperation(method = "getLocationSkin()Lnet/minecraft/util/ResourceLocation;", // at = @At(value = "FIELD", target = "Lnet/minecraft/client/entity/AbstractClientPlayer;locationSkin:Lnet/minecraft/util/ResourceLocation;", ordinal = 1)) // private ResourceLocation setNewSkinModel(AbstractClientPlayer instance, Operation original) { -// boolean slim = Utils.isDefaultSkinSlim(uuid); -// ResourceLocation skin = Utils.getDefaultSkin(uuid); -// simpleSkinBackport$setSlim(slim); -// return skin; +//// boolean slim = Utils.isDefaultSkinSlim(uuid); +//// ResourceLocation skin = Utils.getDefaultSkin(uuid); +//// simpleSkinBackport$setSlim(slim); +//// return skin; +// simpleSkinBackport$setSlim(ConfigMain.defaultSkinSet.isDefaultSkinSlim(getPersistentID())); +// return ConfigMain.defaultSkinSet.getDefaultSkin(getPersistentID()); // } } diff --git a/src/main/java/roadhog360/simpleskinbackport/mixins/late/MixinRenderTFGiant.java b/src/main/java/roadhog360/simpleskinbackport/mixins/late/MixinRenderTFGiant.java index 0c47663..85ed95d 100644 --- a/src/main/java/roadhog360/simpleskinbackport/mixins/late/MixinRenderTFGiant.java +++ b/src/main/java/roadhog360/simpleskinbackport/mixins/late/MixinRenderTFGiant.java @@ -1,14 +1,17 @@ package roadhog360.simpleskinbackport.mixins.late; +import cpw.mods.fml.client.FMLClientHandler; import net.minecraft.client.model.ModelBiped; import net.minecraft.client.renderer.entity.RenderBiped; import net.minecraft.entity.Entity; import net.minecraft.util.ResourceLocation; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; -import roadhog360.simpleskinbackport.core.Utils; +import roadhog360.simpleskinbackport.configuration.configs.ConfigModCompat; +import roadhog360.simpleskinbackport.ducks.IArmsState; import roadhog360.simpleskinbackport.ducks.INewBipedModel; import twilightforest.client.renderer.entity.RenderTFGiant; @@ -20,9 +23,21 @@ public MixinRenderTFGiant(ModelBiped p_i1257_1_, float p_i1257_2_) { @Inject(method = "getEntityTexture", at = @At(value = "HEAD"), cancellable = true) private void overrideSkinAndSetResourceLocation(Entity par1Entity, CallbackInfoReturnable cir) { + boolean usePlayerModel = ConfigModCompat.TFgiantSkinSet == null; + boolean slim = usePlayerModel ? simpleSkinBackport$isClientPlayerSlim() : ConfigModCompat.TFgiantSkinSet.isDefaultSkinSlim(par1Entity.getPersistentID()); if(this.modelBipedMain instanceof INewBipedModel model) { - model.simpleSkinBackport$setSlim(Utils.isDefaultSkinSlim(par1Entity.getEntityId())); + model.simpleSkinBackport$setSlim(slim); } - cir.setReturnValue(Utils.getDefaultSkin(par1Entity.getEntityId())); + if(!usePlayerModel) { + cir.setReturnValue(ConfigModCompat.TFgiantSkinSet.getDefaultSkin(par1Entity.getPersistentID())); + } + } + + @Unique + private boolean simpleSkinBackport$isClientPlayerSlim() { + if(FMLClientHandler.instance().getClientPlayerEntity() instanceof IArmsState player) { + return player.simpleSkinBackport$isSlim(); + } + return false; } }