diff --git a/README.md b/README.md index f982389..849cfc2 100644 --- a/README.md +++ b/README.md @@ -1,27 +1,48 @@ -# Options Profiles +# [1.18.2] Options Profiles [![Modrinth](https://github.com/intergrav/devins-badges/blob/v3/assets/cozy/available/modrinth_64h.png?raw=true)](https://modrinth.com/mod/options-profiles) [![Curseforge](https://github.com/intergrav/devins-badges/blob/v3/assets/cozy/available/curseforge_64h.png?raw=true)](https://curseforge.com/minecraft/mc-mods/options-profiles) -[![GitHub](https://github.com/intergrav/devins-badges/blob/v3/assets/cozy/available/github_64h.png?raw=true)](https://github.com/AxolotlMaid/options-profiles) +[![GitHub](https://github.com/intergrav/devins-badges/blob/v3/assets/cozy/available/git_64h.png?raw=true)](https://github.com/AxolotlMaid/options-profiles) ---- - -### ⚠️⚠️ Mod requires Architectury API ⚠️⚠️ - -Options Profiles lets you load and save your options as profiles from in-game. - -You may use this mod in modpacks. +Options Profiles is a Minecraft mod that lets you load and save your options as profiles from in-game. ## Features -- Profiles are saved in a folder called "options-profiles" in the specified ".minecraft" directory. -- Sodium support -- Forge, Fabric and Quilt support -- Resource packs support -- Save current options -- Edit profiles in-game (deleting, renaming, overwriting) +- Save and load profiles in-game +- Load specific options (like only load keybinds, resource packs or FOV and GUI scale) +- Edit profiles in-game (deleting, renaming, overwriting, pick options to only load) +- Fabric and Forge support +- Third party mod support (see below) + +## Mod Support +Options Profiles supports these mods which means you can create and load profiles with them and the mod will load their configuration. + +- Sodium +- Sodium Extra +- Embeddium +- Distant Horizons +- OptiFine + +If you would like support for another mod, open an issue. + +## Frequently Asked Questions +- Can you port [version]? + > Open an issue in the GitHub repository. +- Can I use this in my modpack? + > You may use this mod in modpacks with credit. +- Where are the profiles saved? + > Profiles are saved in a folder called "options-profiles" in the specified ".minecraft" directory. +- Where can I find the source code for older versions? + > You can find them in the branches. +- Dependencies? + > Versions 1.1 require Architectury API, however versions 1.2+ do not. +- For any other questions, create an issue or contact me at hello@axolotlmaid.com ## Gallery -Profiles Menu | Edit Profile Screen | Options Screen -:-------------------------:|:-------------------------:|:-------------------------: -profiles list | edit profile screen | options screen +Profiles Menu | Edit Profile Screen +:-------------------------:|:-------------------------: +profiles list | edit profile screen + +Options Toggle Menu | Options Screen +:-------------------------:|:-------------------------: +options toggle menu | options screen \ No newline at end of file diff --git a/build.gradle b/build.gradle index ed3778d..ef97479 100644 --- a/build.gradle +++ b/build.gradle @@ -1,40 +1,28 @@ plugins { - id "architectury-plugin" version "3.4-SNAPSHOT" - id "dev.architectury.loom" version "1.4-SNAPSHOT" apply false + id 'dev.architectury.loom' version '1.6-SNAPSHOT' apply false + id 'architectury-plugin' version '3.4-SNAPSHOT' + id 'com.github.johnrengelman.shadow' version '8.1.1' apply false } architectury { - minecraft = rootProject.minecraft_version -} - -subprojects { - apply plugin: "dev.architectury.loom" - - loom { - silentMojangMappingsLicense() - } - - dependencies { - minecraft "com.mojang:minecraft:${rootProject.minecraft_version}" - // The following line declares the mojmap mappings, you may use other mappings as well - mappings loom.officialMojangMappings() - // The following line declares the yarn mappings you may select this one as well. - // mappings "net.fabricmc:yarn:1.20.1+build.10:v2" - } + minecraft = project.minecraft_version } allprojects { - apply plugin: "java" - apply plugin: "architectury-plugin" - apply plugin: "maven-publish" + group = rootProject.maven_group + version = rootProject.mod_version +} + +subprojects { + apply plugin: 'dev.architectury.loom' + apply plugin: 'architectury-plugin' + apply plugin: 'maven-publish' base { - archivesName = rootProject.archives_base_name + // Set up a suffixed format for the mod jar names, e.g. `example-fabric`. + archivesName = "$rootProject.archives_name-$project.name" } - version = rootProject.mod_version - group = rootProject.maven_group - repositories { // Add repositories to retrieve artifacts from in here. // You should only use this when depending on other mods because @@ -43,12 +31,44 @@ allprojects { // for more information about repositories. } - tasks.withType(JavaCompile) { - options.encoding = "UTF-8" - options.release = 17 + loom { + silentMojangMappingsLicense() + } + + dependencies { + minecraft "net.minecraft:minecraft:$rootProject.minecraft_version" + mappings loom.officialMojangMappings() } java { + // Loom will automatically attach sourcesJar to a RemapSourcesJar task and to the "build" task + // if it is present. + // If you remove this line, sources will not be generated. withSourcesJar() + + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + } + + tasks.withType(JavaCompile).configureEach { + it.options.release = 17 + } + + // Configure Maven publishing. + publishing { + publications { + mavenJava(MavenPublication) { + artifactId = base.archivesName.get() + from components.java + } + } + + // See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing. + repositories { + // Add repositories to publish to here. + // Notice: This block does NOT have the same function as the block in the top level. + // The repositories here will be used for publishing your artifact, not for + // retrieving dependencies. + } } } diff --git a/common/build.gradle b/common/build.gradle index 744ed58..44fd481 100644 --- a/common/build.gradle +++ b/common/build.gradle @@ -1,9 +1,5 @@ architectury { - common(rootProject.enabled_platforms.split(",")) -} - -loom { - accessWidenerPath = file("src/main/resources/optionsprofiles.accesswidener") + common rootProject.enabled_platforms.split(',') } repositories { @@ -21,26 +17,14 @@ repositories { } dependencies { - // We depend on fabric loader here to use the fabric @Environment annotations and get the mixin dependencies - // Do NOT use other classes from fabric loader - modImplementation "net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}" - // Remove the next line if you don't want to depend on the API - modApi "dev.architectury:architectury:${rootProject.architectury_version}" + // We depend on Fabric Loader here to use the Fabric @Environment annotations, + // which get remapped to the correct annotations on each platform. + // Do NOT use other classes from Fabric Loader. + modImplementation "net.fabricmc:fabric-loader:$rootProject.fabric_loader_version" - // sodium - modImplementation "maven.modrinth:sodium:mc1.18.2-0.4.1" -} - -publishing { - publications { - mavenCommon(MavenPublication) { - artifactId = rootProject.archives_base_name - from components.java - } - } - - // See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing. - repositories { - // Add repositories to publish to here. - } -} + // Mod implementations + modImplementation "maven.modrinth:sodium:mc1.18.2-0.4.1" // Sodium + modImplementation "maven.modrinth:sodium-extra:mc1.18.2-0.4.18" // Sodium Extra + modImplementation "maven.modrinth:embeddium:0.3.18+mc1.18.2" // Embeddium + modImplementation "maven.modrinth:distanthorizons:2.1.2-a-1.18.2" // Distant Horizons +} \ No newline at end of file diff --git a/common/src/main/java/com/axolotlmaid/optionsprofiles/OptionsProfilesMod.java b/common/src/main/java/com/axolotlmaid/optionsprofiles/OptionsProfilesMod.java index 07951b2..05147f1 100644 --- a/common/src/main/java/com/axolotlmaid/optionsprofiles/OptionsProfilesMod.java +++ b/common/src/main/java/com/axolotlmaid/optionsprofiles/OptionsProfilesMod.java @@ -1,5 +1,9 @@ package com.axolotlmaid.optionsprofiles; +import com.axolotlmaid.optionsprofiles.profiles.Profiles; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; @@ -7,6 +11,7 @@ import java.nio.file.Paths; public class OptionsProfilesMod { public static final String MOD_ID = "optionsprofiles"; + public static final Logger LOGGER = LogManager.getLogger("Options Profiles"); public static void init() { Path profilesDirectory = Paths.get("options-profiles"); @@ -15,9 +20,11 @@ public class OptionsProfilesMod { try { Files.createDirectory(profilesDirectory); } catch (IOException e) { - System.out.println("An error occurred when creating the 'options-profiles' directory."); - e.printStackTrace(); + LOGGER.error("An error occurred when creating the 'options-profiles' directory.", e); } } + + // Update / add configuration for existing profiles + Profiles.updateProfiles(); } -} +} \ No newline at end of file diff --git a/common/src/main/java/com/axolotlmaid/optionsprofiles/gui/EditProfileScreen.java b/common/src/main/java/com/axolotlmaid/optionsprofiles/gui/EditProfileScreen.java index c8241fa..17facee 100644 --- a/common/src/main/java/com/axolotlmaid/optionsprofiles/gui/EditProfileScreen.java +++ b/common/src/main/java/com/axolotlmaid/optionsprofiles/gui/EditProfileScreen.java @@ -27,7 +27,7 @@ public class EditProfileScreen extends Screen { this.addRenderableWidget(this.profileNameEdit); this.addRenderableWidget(new Button(this.width / 2 - 50, this.height - 85, 100, 20, new TranslatableComponent("gui.optionsprofiles.overwrite-options"), (button) -> { - Profiles.writeOptionsFilesIntoProfile(profileName); + Profiles.writeProfile(profileName, true); this.minecraft.setScreen(this.lastScreen); })); diff --git a/common/src/main/java/com/axolotlmaid/optionsprofiles/gui/OptionsToggleList.java b/common/src/main/java/com/axolotlmaid/optionsprofiles/gui/OptionsToggleList.java new file mode 100644 index 0000000..155f08d --- /dev/null +++ b/common/src/main/java/com/axolotlmaid/optionsprofiles/gui/OptionsToggleList.java @@ -0,0 +1,141 @@ +package com.axolotlmaid.optionsprofiles.gui; + +import com.axolotlmaid.optionsprofiles.OptionsProfilesMod; +import com.axolotlmaid.optionsprofiles.profiles.ProfileConfiguration; +import com.axolotlmaid.optionsprofiles.profiles.Profiles; +import com.google.common.collect.ImmutableList; +import com.mojang.blaze3d.vertex.PoseStack; +import net.minecraft.ChatFormatting; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.Font; +import net.minecraft.client.gui.GuiComponent; +import net.minecraft.client.gui.components.ContainerObjectSelectionList; +import net.minecraft.client.gui.components.CycleButton; +import net.minecraft.client.gui.components.events.GuiEventListener; +import net.minecraft.client.gui.narration.NarratableEntry; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TextComponent; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Stream; + +public class OptionsToggleList extends ContainerObjectSelectionList { + private final String profileName; + private final ProfileConfiguration profileConfiguration; + + public OptionsToggleList(OptionsToggleScreen optionsToggleScreen, Minecraft minecraft, String profileName) { + super(minecraft, optionsToggleScreen.width, optionsToggleScreen.height, 20, optionsToggleScreen.height - 32, 20); + this.profileConfiguration = optionsToggleScreen.profileConfiguration; + this.profileName = profileName; + + refreshEntries(false, false); + } + + // If overriding boolean is set to true then this function will set every option in the list to overrideToggle (false or true) + public void refreshEntries(boolean overriding, boolean overrideToggle) { + this.clearEntries(); + + Path profile = Profiles.PROFILES_DIRECTORY.resolve(profileName); + Path optionsFile = profile.resolve("options.txt"); + + if (overriding) { + if (!overrideToggle) { // If set to false, just set the list to nothing instead of removing them one by one. + profileConfiguration.setOptionsToLoad(new ArrayList<>()); + } + } + + try (Stream lines = Files.lines(optionsFile)) { + lines.forEach((line) -> { + String[] option = line.split(":"); + + if (option.length > 1) { // If the option value exists (e.g. "lastServer") + if (overrideToggle) { // We don't need to check for the overriding boolean since this should never be true while the overriding boolean is false. + List optionsToLoad = profileConfiguration.getOptionsToLoad(); + optionsToLoad.add(option[0]); // Add every option because overrideToggle is set to true + + profileConfiguration.setOptionsToLoad(optionsToLoad); // Configuration is saved in the OptionsToggleScreen.java when the player presses "Done" + } + + // Add entry with option key and value and if the key is in the profile configuration + this.addEntry(new OptionEntry(option[0], option[1], profileConfiguration.getOptionsToLoad().contains(option[0]))); + } else { + this.addEntry(new OptionEntry(option[0], "", profileConfiguration.getOptionsToLoad().contains(option[0]))); + } + }); + } catch (IOException e) { + OptionsProfilesMod.LOGGER.error("An error occurred when listing options", e); + } + } + + protected int getScrollbarPosition() { + return super.getScrollbarPosition() + 15; + } + + public int getRowWidth() { + return 340; + } + + public class OptionEntry extends Entry { + private final Component optionKey; + private final CycleButton toggleButton; + + OptionEntry(String optionKey, String optionValue, boolean toggled) { + this.optionKey = new TextComponent(optionKey); + + this.toggleButton = CycleButton.onOffBuilder(toggled).displayOnlyValue().create(0, 0, 44, 20, TextComponent.EMPTY, (button, boolean_) -> { + List optionsToLoad = profileConfiguration.getOptionsToLoad(); + + // If toggled to true + if (boolean_) { + button.setMessage(button.getMessage().copy().withStyle(ChatFormatting.GREEN)); // Set the button's color to green + optionsToLoad.add(optionKey); + } else { + button.setMessage(button.getMessage().copy().withStyle(ChatFormatting.RED)); // Set the button's color to red + optionsToLoad.remove(optionKey); + } + + profileConfiguration.setOptionsToLoad(optionsToLoad); + }); + + // Set tooltip to the option value (e.g. "ao" will show "true") +// this.toggleButton.renderToolTip(Tooltip.create(Component.literal(optionValue))); + + if (toggled) { + this.toggleButton.setMessage(this.toggleButton.getMessage().copy().withStyle(ChatFormatting.GREEN)); // Set the button's color to green + } else { + this.toggleButton.setMessage(this.toggleButton.getMessage().copy().withStyle(ChatFormatting.RED)); // Set the button's color to red + } + } + + public void render(PoseStack poseStack, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta) { + Font fontRenderer = OptionsToggleList.this.minecraft.font; + + int posX = OptionsToggleList.this.getScrollbarPosition() - this.toggleButton.getWidth() - 10; + int posY = y - 2; + int textY = y + entryHeight / 2; + + GuiComponent.drawString(poseStack, fontRenderer, this.optionKey, x, textY - 9 / 2, 16777215); + + this.toggleButton.x = posX; + this.toggleButton.y = posY; + this.toggleButton.render(poseStack, mouseX, mouseY, tickDelta); + } + + public List children() { + return ImmutableList.of(this.toggleButton); + } + + public List narratables() { + return ImmutableList.of(this.toggleButton); + } + } + + public abstract static class Entry extends ContainerObjectSelectionList.Entry { + public Entry() { + } + } +} \ No newline at end of file diff --git a/common/src/main/java/com/axolotlmaid/optionsprofiles/gui/OptionsToggleScreen.java b/common/src/main/java/com/axolotlmaid/optionsprofiles/gui/OptionsToggleScreen.java new file mode 100644 index 0000000..fe64769 --- /dev/null +++ b/common/src/main/java/com/axolotlmaid/optionsprofiles/gui/OptionsToggleScreen.java @@ -0,0 +1,47 @@ +package com.axolotlmaid.optionsprofiles.gui; + +import com.axolotlmaid.optionsprofiles.profiles.ProfileConfiguration; +import com.axolotlmaid.optionsprofiles.profiles.Profiles; +import com.mojang.blaze3d.vertex.PoseStack; +import net.minecraft.client.gui.GuiComponent; +import net.minecraft.client.gui.components.Button; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.network.chat.CommonComponents; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TranslatableComponent; + +public class OptionsToggleScreen extends Screen { + private final Screen lastScreen; + private final Component profileName; + private OptionsToggleList optionsToggleList; + + public ProfileConfiguration profileConfiguration; + + public OptionsToggleScreen(Screen lastScreen, Component profileName) { + super(new TranslatableComponent("gui.optionsprofiles.profiles-menu")); + this.lastScreen = lastScreen; + this.profileName = profileName; + this.profileConfiguration = ProfileConfiguration.get(profileName.getString()); + } + + protected void init() { + this.optionsToggleList = new OptionsToggleList(this, this.minecraft, profileName.getString()); + this.addWidget(this.optionsToggleList); + + // buttons + this.addRenderableWidget(new Button(this.width / 2 - 155, this.height - 29, 150, 20, new TranslatableComponent("gui.optionsprofiles.save-current-options"), (button) -> { +// Profiles.createProfile(); + this.optionsToggleList.refreshEntries(false, false); + })); + + this.addRenderableWidget(new Button(this.width / 2 + 5, this.height - 29, 150, 20, CommonComponents.GUI_DONE, (button) -> { + this.minecraft.setScreen(this.lastScreen); + })); + } + + public void render(PoseStack poseStack, int mouseX, int mouseY, float delta) { + this.optionsToggleList.render(poseStack, mouseX, mouseY, delta); + GuiComponent.drawCenteredString(poseStack, this.font, this.title, this.width / 2, 8, 16777215); + super.render(poseStack, mouseX, mouseY, delta); + } +} diff --git a/common/src/main/java/com/axolotlmaid/optionsprofiles/gui/ProfilesList.java b/common/src/main/java/com/axolotlmaid/optionsprofiles/gui/ProfilesList.java index 463f0fe..05892f2 100644 --- a/common/src/main/java/com/axolotlmaid/optionsprofiles/gui/ProfilesList.java +++ b/common/src/main/java/com/axolotlmaid/optionsprofiles/gui/ProfilesList.java @@ -25,7 +25,7 @@ public class ProfilesList extends ContainerObjectSelectionList { + this.addRenderableWidget(new Button(5, 5, 75, 20, new TranslatableComponent("gui.optionsprofiles.profiles-menu"), (button) -> { this.minecraft.setScreen(new ProfilesScreen(this)); })); } diff --git a/common/src/main/java/com/axolotlmaid/optionsprofiles/profiles/ProfileConfiguration.java b/common/src/main/java/com/axolotlmaid/optionsprofiles/profiles/ProfileConfiguration.java new file mode 100644 index 0000000..355ba86 --- /dev/null +++ b/common/src/main/java/com/axolotlmaid/optionsprofiles/profiles/ProfileConfiguration.java @@ -0,0 +1,77 @@ +package com.axolotlmaid.optionsprofiles.profiles; + +import com.axolotlmaid.optionsprofiles.OptionsProfilesMod; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; + +public class ProfileConfiguration { + private static final Path profilesDirectory = Profiles.PROFILES_DIRECTORY; + private static Path configurationFile; + private static String profileName; + + public static int configurationVersion = 1; // Used to update configuration in later revisions + private int version = configurationVersion; // ^ same here - this variable is used to show it in the configuration.json file + private List optionsToLoad = new ArrayList<>(); + + public ProfileConfiguration save() { + ProfileConfiguration configuration = new ProfileConfiguration(); + + Gson gson = new GsonBuilder() + .setPrettyPrinting() + .create(); + + try (BufferedWriter writer = Files.newBufferedWriter(configurationFile)) { + gson.toJson(this, writer); + OptionsProfilesMod.LOGGER.info("[Profile '{}']: Profile configuration saved", profileName); + } catch (IOException e) { + OptionsProfilesMod.LOGGER.error("Unable to write configuration.json to profile!", e); + } + + return configuration; + } + + public static ProfileConfiguration get(String profile_name) { + ProfileConfiguration configuration = new ProfileConfiguration(); + + Path profile = profilesDirectory.resolve(profile_name); + configurationFile = profile.resolve("configuration.json"); + profileName = profile_name; + + if (Files.notExists(configurationFile)) { + configuration.save(); + } + + try (BufferedReader reader = Files.newBufferedReader(configurationFile)) { + Gson gson = new Gson(); + configuration = gson.fromJson(reader, ProfileConfiguration.class); + } catch (IOException e) { + OptionsProfilesMod.LOGGER.error("[Profile '{}']: An error occurred when reading configuration.json", profileName, e); + } + + return configuration; + } + + public int getVersion() { + return version; + } + + public void setVersion(int version) { + this.version = version; + } + + public List getOptionsToLoad() { + return optionsToLoad; + } + + public void setOptionsToLoad(List optionsToLoad) { + this.optionsToLoad = optionsToLoad; + } +} \ No newline at end of file diff --git a/common/src/main/java/com/axolotlmaid/optionsprofiles/profiles/Profiles.java b/common/src/main/java/com/axolotlmaid/optionsprofiles/profiles/Profiles.java index 1348b7a..d102698 100644 --- a/common/src/main/java/com/axolotlmaid/optionsprofiles/profiles/Profiles.java +++ b/common/src/main/java/com/axolotlmaid/optionsprofiles/profiles/Profiles.java @@ -1,197 +1,283 @@ package com.axolotlmaid.optionsprofiles.profiles; -import dev.architectury.platform.Platform; +import com.axolotlmaid.optionsprofiles.OptionsProfilesMod; +import com.axolotlmaid.optionsprofiles.profiles.loaders.DistantHorizonsLoader; +//import com.axolotlmaid.optionsprofiles.profiles.loaders.EmbeddiumLoader; +//import com.axolotlmaid.optionsprofiles.profiles.loaders.SodiumExtraLoader; +import com.axolotlmaid.optionsprofiles.profiles.loaders.SodiumLoader; +import org.apache.commons.io.FileUtils; -import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.nio.file.StandardOpenOption; -import java.util.Comparator; -import java.util.List; +import java.nio.file.StandardCopyOption; +import java.util.*; +import java.util.function.Consumer; import java.util.stream.Stream; public class Profiles { + public static final Path PROFILES_DIRECTORY = Paths.get("options-profiles/"); + public static final Path OPTIONS_FILE = Paths.get("options.txt"); + public static final Path OPTIFINE_OPTIONS_FILE = Paths.get("optionsof.txt"); + public static final Path SODIUM_OPTIONS_FILE = Paths.get("config/sodium-options.json"); + public static final Path SODIUM_EXTRA_OPTIONS_FILE = Paths.get("config/sodium-extra-options.json"); + public static final Path EMBEDDIUM_OPTIONS_FILE = Paths.get("config/embeddium-options.json"); + public static final Path DISTANT_HORIZONS_OPTIONS_FILE = Paths.get("config/DistantHorizons.toml"); + + // This function goes through every profile and updates / adds the configuration file if it doesn't exist + public static void updateProfiles() { + try (Stream paths = Files.list(PROFILES_DIRECTORY)) { + paths.filter(Files::isDirectory) + .forEach(path -> { + String profileName = path.getFileName().toString(); + + // This gets the configuration but also creates the configuration file if it is not there + ProfileConfiguration profileConfiguration = ProfileConfiguration.get(profileName); + List optionsToLoad = profileConfiguration.getOptionsToLoad(); + + // Checks for updates to the configuration + if (profileConfiguration.getVersion() != ProfileConfiguration.configurationVersion) { + Path configurationFile = path.resolve("configuration.json"); + + try { + Files.delete(configurationFile); + } catch (IOException e) { + OptionsProfilesMod.LOGGER.error("[Profile '{}']: Error deleting configuration file", profileName, e); + } + + // Create the configuration.json again thus updating it + profileConfiguration = ProfileConfiguration.get(profileName); + + // Add player's old configuration + profileConfiguration.setOptionsToLoad(optionsToLoad); + + // Save configuration + profileConfiguration.save(); + } + + OptionsProfilesMod.LOGGER.warn("[Profile '{}']: Profile configuration updated / added", profileName); + }); + } catch (IOException e) { + OptionsProfilesMod.LOGGER.error("An error occurred when updating profiles", e); + } + } + public static void createProfile() { String profileName = "Profile 1"; - Path profile = Paths.get("options-profiles/" + profileName); + Path profile = PROFILES_DIRECTORY.resolve(profileName); + // Increases the number in 'Profile 1' if it already exists for (int i = 1; Files.exists(profile); i++) { profileName = "Profile " + i; - profile = Paths.get("options-profiles/" + profileName); + profile = Paths.get(PROFILES_DIRECTORY.toString(), profileName); } try { Files.createDirectory(profile); if (Files.exists(profile)) { - System.out.println("Profile created."); - - writeOptionsFilesIntoProfile(profileName); + OptionsProfilesMod.LOGGER.info("[Profile '{}']: created", profileName); + writeProfile(profileName, false); } else { - System.out.println("Profile was not created successfully."); + OptionsProfilesMod.LOGGER.warn("[Profile '{}']: Profile already exists?", profileName); } } catch (IOException e) { - System.out.println("An error occurred when creating a profile."); - e.printStackTrace(); + OptionsProfilesMod.LOGGER.error("[Profile '{}']: An error occurred when creating a profile", profileName, e); } } - public static void writeOptionsFilesIntoProfile(String profileName) { - Path profile = Paths.get("options-profiles/" + profileName); + private static void copyOptionFile(Path profile, Path options) { + if (Files.exists(options)) { + Path profileOptions = profile.resolve(options.getFileName()); - // options.txt - Path options = Paths.get("options.txt"); - Path profileOptions = Paths.get(profile.toAbsolutePath() + "/options.txt"); + try { + Files.copy(options, profileOptions); + } catch (IOException e) { + OptionsProfilesMod.LOGGER.error("[Profile '{}']: Unable to copy '{}'", profile.getFileName().toString(), options.getFileName().toString(), e); + } + } + } - try (Stream paths = Files.lines(options)) { - if (Files.exists(profileOptions)) - Files.newBufferedWriter(profileOptions, StandardOpenOption.TRUNCATE_EXISTING); + public static void writeProfile(String profileName, boolean overwriting) { + Path profile = PROFILES_DIRECTORY.resolve(profileName); + Path profileOptions = profile.resolve("options.txt"); - paths.forEach(line -> { - try { - Files.write(profileOptions, line.getBytes(), StandardOpenOption.CREATE, StandardOpenOption.APPEND); - Files.write(profileOptions, "\n".getBytes(), StandardOpenOption.APPEND); - } catch (IOException e) { - System.out.println("An error occurred when writing a profile."); - e.printStackTrace(); - } - }); - } catch (IOException e) { - System.out.println("An error occurred when reading options.txt."); - e.printStackTrace(); + ProfileConfiguration profileConfiguration = ProfileConfiguration.get(profileName); + + if (overwriting) { + try { + // Removes old option files + FileUtils.cleanDirectory(profile.toFile()); + } catch (IOException e) { + OptionsProfilesMod.LOGGER.error("[Profile '{}']: An error occurred when clearing old options files", profileName, e); + } } - // sodium-options.json - if (Platform.isFabric()) { - if (Platform.isModLoaded("sodium")) { - Path sodiumConfiguration = Paths.get("config/sodium-options.json"); - Path sodiumConfigurationProfile = Paths.get(profile.toAbsolutePath() + "/sodium-options.json"); + copyOptionFile(profile, OPTIONS_FILE); + copyOptionFile(profile, OPTIFINE_OPTIONS_FILE); + copyOptionFile(profile, SODIUM_OPTIONS_FILE); + copyOptionFile(profile, SODIUM_EXTRA_OPTIONS_FILE); + copyOptionFile(profile, EMBEDDIUM_OPTIONS_FILE); + copyOptionFile(profile, DISTANT_HORIZONS_OPTIONS_FILE); - try (Stream paths = Files.lines(sodiumConfiguration)) { - if (Files.exists(sodiumConfigurationProfile)) - Files.newBufferedWriter(sodiumConfigurationProfile, StandardOpenOption.TRUNCATE_EXISTING); + // Add every option value to configuration + try (Stream lines = Files.lines(profileOptions)) { + List optionsToLoad = profileConfiguration.getOptionsToLoad(); - paths.forEach(line -> { - try { - Files.write(sodiumConfigurationProfile, line.getBytes(), StandardOpenOption.CREATE, StandardOpenOption.APPEND); - Files.write(sodiumConfigurationProfile, "\n".getBytes(), StandardOpenOption.APPEND); - } catch (IOException e) { - System.out.println("An error occurred when writing a profile."); - e.printStackTrace(); - } - }); - } catch (IOException e) { - System.out.println("An error occurred when reading options.txt."); - e.printStackTrace(); - } - } + lines.forEach((line) -> { + String[] option = line.split(":"); + optionsToLoad.add(option[0]); + }); + + profileConfiguration.save(); + } catch (IOException e) { + OptionsProfilesMod.LOGGER.error("[Profile '{}']: An error occurred when adding options to the configuration file", profileName, e); } } public static boolean isProfileLoaded(String profileName) { - Path profile = Paths.get("options-profiles/" + profileName); + Path profile = PROFILES_DIRECTORY.resolve(profileName); - Path options = Paths.get("options.txt"); - Path profileOptions = Paths.get(profile.toAbsolutePath() + "/options.txt"); + List optionFiles = new ArrayList<>(); + optionFiles.add(OPTIONS_FILE); + // The next few lines check if the specified file exists. If so, it adds it to the optionFiles ArrayList. + Optional.of(OPTIFINE_OPTIONS_FILE).filter(Files::exists).ifPresent(optionFiles::add); + Optional.of(SODIUM_OPTIONS_FILE).filter(Files::exists).ifPresent(optionFiles::add); + Optional.of(SODIUM_EXTRA_OPTIONS_FILE).filter(Files::exists).ifPresent(optionFiles::add); + Optional.of(EMBEDDIUM_OPTIONS_FILE).filter(Files::exists).ifPresent(optionFiles::add); + Optional.of(DISTANT_HORIZONS_OPTIONS_FILE).filter(Files::exists).ifPresent(optionFiles::add); + + // Check if the original option file and the profile option file have the same content try { - List linesOptions = Files.readAllLines(options); - List linesProfileOptions = Files.readAllLines(profileOptions); - - if (Platform.isFabric()) { - if (Platform.isModLoaded("sodium")) { - Path sodiumConfiguration = Paths.get("config/sodium-options.json"); - Path sodiumConfigurationProfile = Paths.get(profile.toAbsolutePath() + "/sodium-options.json"); - - if (Files.exists(sodiumConfigurationProfile)) { - List linesSodiumConfig = Files.readAllLines(sodiumConfiguration); - List linesSodiumConfigProfile = Files.readAllLines(sodiumConfigurationProfile); - - return linesOptions.equals(linesProfileOptions) && linesSodiumConfig.equals(linesSodiumConfigProfile); - } + for (Path optionFile : optionFiles) { + Path profileOptions = profile.resolve(optionFile.getFileName()); + if (!FileUtils.contentEquals(optionFile.toFile(), profileOptions.toFile())) { + return false; } } - - return linesOptions.equals(linesProfileOptions); } catch (IOException e) { - e.printStackTrace(); + OptionsProfilesMod.LOGGER.error("[Profile '{}']: An error occurred when checking if the profile is loaded", profileName, e); + return false; } - return false; + return true; + } + + private static void loadOptionFile(String profileName, Path options) { + ProfileConfiguration profileConfiguration = ProfileConfiguration.get(profileName); + + Path profile = PROFILES_DIRECTORY.resolve(profileName); + Path profileOptions = profile.resolve(options.getFileName()); + + if (Files.exists(profileOptions)) { + // This if statement is for loading specific options. + if (options.getFileName().toString().equals("options.txt")) { // If file is options.txt - only doing options.txt for now + Map optionsToWrite = new HashMap<>(); + + // Read options.txt + try (Stream lines = Files.lines(options)) { + lines.forEach(line -> { + String[] option = line.split(":"); // Split key and value + + if (option.length > 1) { + optionsToWrite.put(option[0], option[1]); // Add them to the map + } else { + optionsToWrite.put(option[0], ""); + } + }); + } catch (IOException e) { + OptionsProfilesMod.LOGGER.error("[Profile '{}']: An error occurred reading options.txt to load the profile", profileName, e); + } + + // Read profile options.txt + try (Stream lines = Files.lines(profileOptions)) { + lines.forEach(line -> { + String[] option = line.split(":"); // Split key and value + + if (option.length > 1) { + if (profileConfiguration.getOptionsToLoad().contains(option[0])) { + optionsToWrite.put(option[0], option[1]); // Updates old value set by reading options.txt + } + } + }); + } catch (IOException e) { + OptionsProfilesMod.LOGGER.error("[Profile '{}']: An error occurred reading profile options.txt to load the profile", profileName, e); + } + + // Write into options.txt + try { + Files.write(options, () -> + optionsToWrite + .entrySet() + .stream() + .map(entry -> entry.getKey() + ":" + entry.getValue()) + .iterator() + ); + + OptionsProfilesMod.LOGGER.info("[Profile '{}']: '{}' loaded with specific options", profileName, options.getFileName()); + } catch (IOException e) { + OptionsProfilesMod.LOGGER.error("[Profile '{}']: An error occurred writing hashmap into options.txt", profileName, e); + } + + return; // Return the function, thus not running the next few lines + } + + try { + // Replaces the original option file with the profile option file + Files.copy(profileOptions, options, StandardCopyOption.REPLACE_EXISTING); + OptionsProfilesMod.LOGGER.info("[Profile '{}']: '{}' loaded by copying", profileName, options.getFileName()); + } catch (IOException e) { + OptionsProfilesMod.LOGGER.error("[Profile '{}']: An error occurred when loading the profile", profileName, e); + } + } + } + + private static void loadOptionFile(String profileName, Path options, Consumer loader) { + Path profile = PROFILES_DIRECTORY.resolve(profileName); + Path profileOptions = profile.resolve(options.getFileName()); + + if (Files.exists(profileOptions)) { + loader.accept(profileOptions); + OptionsProfilesMod.LOGGER.info("[Profile '{}']: '{}' loaded using loader class", profileName, options.getFileName()); + } } public static void loadProfile(String profileName) { - Path profile = Paths.get("options-profiles/" + profileName); - - // options.txt - Path options = Paths.get("options.txt"); - Path profileOptions = Paths.get(profile.toAbsolutePath() + "/options.txt"); - - try (Stream paths = Files.lines(profileOptions)) { - Files.newBufferedWriter(options, StandardOpenOption.TRUNCATE_EXISTING); - - paths.forEach(line -> { - try { - Files.write(options, line.getBytes(), StandardOpenOption.APPEND); - Files.write(options, "\n".getBytes(), StandardOpenOption.APPEND); - } catch (IOException e) { - System.out.println("An error occurred when loading a profile."); - e.printStackTrace(); - } - }); - } catch (IOException e) { - System.out.println("An error occurred when loading a profile."); - e.printStackTrace(); - } - - // sodium-options.json - if (Platform.isFabric()) { - if (Platform.isModLoaded("sodium")) { - Path sodiumConfigurationProfile = Paths.get(profile.toAbsolutePath() + "/sodium-options.json"); - - if (Files.exists(sodiumConfigurationProfile)) { - SodiumConfigLoader.load(sodiumConfigurationProfile); - } - } - } + loadOptionFile(profileName, OPTIONS_FILE); + loadOptionFile(profileName, OPTIFINE_OPTIONS_FILE); + loadOptionFile(profileName, SODIUM_OPTIONS_FILE, SodiumLoader::load); +// loadOptionFile(profileName, SODIUM_EXTRA_OPTIONS_FILE, SodiumExtraLoader::load); +// loadOptionFile(profileName, EMBEDDIUM_OPTIONS_FILE, EmbeddiumLoader::load); + loadOptionFile(profileName, DISTANT_HORIZONS_OPTIONS_FILE, DistantHorizonsLoader::load); } public static void renameProfile(String profileName, String newProfileName) { - Path profile = Paths.get("options-profiles/" + profileName); - Path newProfile = Paths.get("options-profiles/" + newProfileName); + Path profile = PROFILES_DIRECTORY.resolve(profileName); + Path newProfile = PROFILES_DIRECTORY.resolve(newProfileName); - if (Files.exists(newProfile)) - System.out.println("New profile already exists!"); + if (Files.exists(newProfile)) { + OptionsProfilesMod.LOGGER.warn("[Profile '{}']: A profile with that name already exists!", profileName); + return; + } try { Files.move(profile, newProfile); - - if (Files.exists(newProfile)) { - System.out.println("Profile renamed."); - } else { - System.out.println("Profile was not renamed successfully."); - } + OptionsProfilesMod.LOGGER.info("[Profile '{}']: renamed. Old name: {}", newProfileName, profileName); } catch (IOException e) { - System.out.println("Profile was not renamed successfully."); - e.printStackTrace(); + OptionsProfilesMod.LOGGER.error("[Profile '{}']: An error occurred when renaming the profile", profileName, e); } } public static void deleteProfile(String profileName) { - Path profile = Paths.get("options-profiles/" + profileName); + Path profile = PROFILES_DIRECTORY.resolve(profileName); - try (Stream files = Files.walk(profile)) { - files - .sorted(Comparator.reverseOrder()) - .map(Path::toFile) - .forEach(File::delete); + try { + FileUtils.deleteDirectory(profile.toFile()); + OptionsProfilesMod.LOGGER.info("[Profile '{}']: deleted", profileName); } catch (IOException e) { - System.out.println("Profile was not deleted."); - e.printStackTrace(); + OptionsProfilesMod.LOGGER.error("[Profile '{}']: Profile was not deleted", profileName, e); } - - System.out.println("Profile deleted."); } } \ No newline at end of file diff --git a/common/src/main/java/com/axolotlmaid/optionsprofiles/profiles/loaders/DistantHorizonsLoader.java b/common/src/main/java/com/axolotlmaid/optionsprofiles/profiles/loaders/DistantHorizonsLoader.java new file mode 100644 index 0000000..64741ee --- /dev/null +++ b/common/src/main/java/com/axolotlmaid/optionsprofiles/profiles/loaders/DistantHorizonsLoader.java @@ -0,0 +1,11 @@ +package com.axolotlmaid.optionsprofiles.profiles.loaders; + +import com.seibel.distanthorizons.core.config.ConfigBase; + +import java.nio.file.Path; + +public class DistantHorizonsLoader { + public static void load(Path file) { + ConfigBase.INSTANCE.configFileINSTANCE.loadFromFile(); + } +} diff --git a/common/src/main/java/com/axolotlmaid/optionsprofiles/profiles/loaders/SodiumLoader.java b/common/src/main/java/com/axolotlmaid/optionsprofiles/profiles/loaders/SodiumLoader.java new file mode 100644 index 0000000..d0867b0 --- /dev/null +++ b/common/src/main/java/com/axolotlmaid/optionsprofiles/profiles/loaders/SodiumLoader.java @@ -0,0 +1,87 @@ +package com.axolotlmaid.optionsprofiles.profiles.loaders; + +import com.axolotlmaid.optionsprofiles.OptionsProfilesMod; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import me.jellysquid.mods.sodium.client.SodiumClientMod; +import me.jellysquid.mods.sodium.client.gui.SodiumGameOptions; + +import java.io.FileReader; +import java.io.IOException; +import java.nio.file.Path; + +public class SodiumLoader { + public static void load(Path file) { + try (FileReader reader = new FileReader(file.toFile())) { + Gson gson = new GsonBuilder().create(); + Configuration configuration = gson.fromJson(reader, Configuration.class); + + apply(configuration); + } catch (IOException e) { + OptionsProfilesMod.LOGGER.error("An error occurred when loading Sodium's configuration", e); + } + } + + private static void apply(Configuration configuration) { + SodiumClientMod.options().quality.weatherQuality = SodiumGameOptions.GraphicsQuality.valueOf(configuration.quality.weather_quality); + SodiumClientMod.options().quality.leavesQuality = SodiumGameOptions.GraphicsQuality.valueOf(configuration.quality.leaves_quality); + SodiumClientMod.options().quality.enableVignette = configuration.quality.enable_vignette; + + SodiumClientMod.options().advanced.arenaMemoryAllocator = SodiumGameOptions.ArenaMemoryAllocator.valueOf(configuration.advanced.arena_memory_allocator); + SodiumClientMod.options().advanced.allowDirectMemoryAccess = configuration.advanced.allow_direct_memory_access; + SodiumClientMod.options().advanced.enableMemoryTracing = configuration.advanced.enable_memory_tracing; + SodiumClientMod.options().advanced.useAdvancedStagingBuffers = configuration.advanced.use_advanced_staging_buffers; + SodiumClientMod.options().advanced.cpuRenderAheadLimit = configuration.advanced.cpu_render_ahead_limit; + + SodiumClientMod.options().performance.chunkBuilderThreads = configuration.performance.chunk_builder_threads; + SodiumClientMod.options().performance.alwaysDeferChunkUpdates = configuration.performance.always_defer_chunk_updates; + SodiumClientMod.options().performance.animateOnlyVisibleTextures = configuration.performance.animate_only_visible_textures; + SodiumClientMod.options().performance.useEntityCulling = configuration.performance.use_entity_culling; + SodiumClientMod.options().performance.useParticleCulling = configuration.performance.use_particle_culling; + SodiumClientMod.options().performance.useFogOcclusion = configuration.performance.use_fog_occlusion; + SodiumClientMod.options().performance.useBlockFaceCulling = configuration.performance.use_block_face_culling; + + SodiumClientMod.options().notifications.hideDonationButton = configuration.notifications.hide_donation_button; + + try { + SodiumClientMod.options().writeChanges(); + } catch (IOException e) { + OptionsProfilesMod.LOGGER.error("An error occurred when loading Sodium's configuration", e); + } + } + + public static class Configuration { + public Quality quality; + public Advanced advanced; + public Performance performance; + public Notifications notifications; + + public static class Quality { + public String weather_quality; + public String leaves_quality; + public boolean enable_vignette; + } + + public static class Advanced { + public String arena_memory_allocator; + public boolean allow_direct_memory_access; + public boolean enable_memory_tracing; + public boolean use_advanced_staging_buffers; + public int cpu_render_ahead_limit; + } + + public static class Performance { + public int chunk_builder_threads; + public boolean always_defer_chunk_updates; + public boolean animate_only_visible_textures; + public boolean use_entity_culling; + public boolean use_particle_culling; + public boolean use_fog_occlusion; + public boolean use_block_face_culling; + } + + public static class Notifications { + public boolean hide_donation_button; + } + } +} \ No newline at end of file diff --git a/common/src/main/resources/architectury.common.json b/common/src/main/resources/architectury.common.json deleted file mode 100644 index 675ac01..0000000 --- a/common/src/main/resources/architectury.common.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "accessWidener": "optionsprofiles.accesswidener" -} \ No newline at end of file diff --git a/common/src/main/resources/assets/optionsprofiles/lang/en_us.json b/common/src/main/resources/assets/optionsprofiles/lang/en_us.json index 76abbc3..e3b4a31 100644 --- a/common/src/main/resources/assets/optionsprofiles/lang/en_us.json +++ b/common/src/main/resources/assets/optionsprofiles/lang/en_us.json @@ -7,6 +7,12 @@ "gui.optionsprofiles.editing-profile-title": "Editing Profile: ", "gui.optionsprofiles.profile-name-text": "Profile Name", "gui.optionsprofiles.overwrite-options": "Overwrite", + "gui.optionsprofiles.overwrite-options.tooltip": "Replaces the profile's options with your current options", "gui.optionsprofiles.rename-profile": "Rename", - "gui.optionsprofiles.delete-profile": "Delete" + "gui.optionsprofiles.delete-profile": "Delete", + + "gui.optionsprofiles.options-toggle": "Select options to toggle", + "gui.optionsprofiles.options-toggle.tooltip": "Select the options you want to load in this profile", + "gui.optionsprofiles.all-on": "All ON", + "gui.optionsprofiles.all-off": "All OFF" } \ No newline at end of file diff --git a/common/src/main/resources/assets/optionsprofiles/lang/ru_ru.json b/common/src/main/resources/assets/optionsprofiles/lang/ru_ru.json index 4ffe547..d625136 100644 --- a/common/src/main/resources/assets/optionsprofiles/lang/ru_ru.json +++ b/common/src/main/resources/assets/optionsprofiles/lang/ru_ru.json @@ -7,9 +7,15 @@ "gui.optionsprofiles.editing-profile-title": "Изменение профиля: ", "gui.optionsprofiles.profile-name-text": "Имя профиля", "gui.optionsprofiles.overwrite-options": "Перезаписать", + "gui.optionsprofiles.overwrite-options.tooltip": "Заменяет параметры профиля на ваши текущие параметры", "gui.optionsprofiles.rename-profile": "Переименовать", "gui.optionsprofiles.delete-profile": "удалить", + "gui.optionsprofiles.options-toggle": "Выберите параметры для переключения", + "gui.optionsprofiles.options-toggle.tooltip": "Выберите параметры, которые вы хотите загрузить в этот профиль", + "gui.optionsprofiles.all-on": "Включи всё", + "gui.optionsprofiles.all-off": "Все выключить", + "modmenu.summaryTranslation.options-profiles": "Cохраняйте и загружайте профили настроек, не выходя из игры.", "modmenu.descriptionTranslation.options-profiles": "Мод, позволяющий сохранять профили текущих настроек и загружать их, не выходя из игры." } \ No newline at end of file diff --git a/common/src/main/resources/assets/optionsprofiles/lang/tt_ru.json b/common/src/main/resources/assets/optionsprofiles/lang/tt_ru.json index 8314a93..652e987 100644 --- a/common/src/main/resources/assets/optionsprofiles/lang/tt_ru.json +++ b/common/src/main/resources/assets/optionsprofiles/lang/tt_ru.json @@ -7,9 +7,15 @@ "gui.optionsprofiles.editing-profile-title": "Профильне үзгәртү: ", "gui.optionsprofiles.profile-name-text": "Профиль исеме", "gui.optionsprofiles.overwrite-options": "перезапись", + "gui.optionsprofiles.overwrite-options.tooltip": "Профиль вариантларын хәзерге вариантларыгыз белән алыштыра", "gui.optionsprofiles.rename-profile": "переименовывать", "gui.optionsprofiles.delete-profile": "бетерү", + "gui.optionsprofiles.options-toggle": "Күчерү вариантларын сайлагыз", + "gui.optionsprofiles.options-toggle.tooltip": "Бу профильгә йөгерергә теләгән вариантларны сайлагыз", + "gui.optionsprofiles.all-on": "Барысын да кабызыгыз", + "gui.optionsprofiles.all-off": "Барысын да сүндерегез", + "modmenu.summaryTranslation.options-profiles": "Хәзерге көйләүләр профильләрен саклагыз һәм уеннан чыгусыз йөкләгез.", "modmenu.descriptionTranslation.options-profiles": "Хәзерге көйләүләр профильләрен сакларга һәм уеннан чыгусыз йөкләргә рөхсәт итә торган мод." } diff --git a/common/src/main/resources/assets/optionsprofiles/lang/zh_cn.json b/common/src/main/resources/assets/optionsprofiles/lang/zh_cn.json new file mode 100644 index 0000000..22b1010 --- /dev/null +++ b/common/src/main/resources/assets/optionsprofiles/lang/zh_cn.json @@ -0,0 +1,18 @@ +{ + "gui.optionsprofiles.profiles-menu": "配置预设", + "gui.optionsprofiles.save-current-options": "保存当前配置", + "gui.optionsprofiles.load-profile": "✔(载入)", + "gui.optionsprofiles.edit-profile": "✎(编辑)", + + "gui.optionsprofiles.editing-profile-title": "正在编辑:", + "gui.optionsprofiles.profile-name-text": "预设名", + "gui.optionsprofiles.overwrite-options": "覆盖", + "gui.optionsprofiles.overwrite-options.tooltip": "将配置文件选项替换为您当前的选项", + "gui.optionsprofiles.rename-profile": "重命名", + "gui.optionsprofiles.delete-profile": "删除", + + "gui.optionsprofiles.options-toggle": "选择要切换的选项", + "gui.optionsprofiles.options-toggle.tooltip": "选择您要加载到此配置文件中的选项", + "gui.optionsprofiles.all-on": "全部开启", + "gui.optionsprofiles.all-off": "关掉一切" +} diff --git a/common/src/main/resources/assets/optionsprofiles/lang/zh_tw.json b/common/src/main/resources/assets/optionsprofiles/lang/zh_tw.json new file mode 100644 index 0000000..99c2ce7 --- /dev/null +++ b/common/src/main/resources/assets/optionsprofiles/lang/zh_tw.json @@ -0,0 +1,18 @@ +{ + "gui.optionsprofiles.profiles-menu": "配置檔", + "gui.optionsprofiles.save-current-options": "保存目前配置", + "gui.optionsprofiles.load-profile": "✔(載入)", + "gui.optionsprofiles.edit-profile": "✎(編輯)", + + "gui.optionsprofiles.editing-profile-title": "正在編輯:", + "gui.optionsprofiles.profile-name-text": "預設名稱", + "gui.optionsprofiles.overwrite-options": "覆蓋", + "gui.optionsprofiles.overwrite-options.tooltip": "將配置文件選項替換為您目前的選項", + "gui.optionsprofiles.rename-profile": "重新命名", + "gui.optionsprofiles.delete-profile": "刪除", + + "gui.optionsprofiles.options-toggle": "選擇要切換的選項", + "gui.optionsprofiles.options-toggle.tooltip": "選擇您要載入到此配置文件中的選項", + "gui.optionsprofiles.all-on": "全部 開啟", + "gui.optionsprofiles.all-off": "全部 關閉" +} diff --git a/common/src/main/resources/optionsprofiles.accesswidener b/common/src/main/resources/optionsprofiles.accesswidener deleted file mode 100644 index 13268c3..0000000 --- a/common/src/main/resources/optionsprofiles.accesswidener +++ /dev/null @@ -1 +0,0 @@ -accessWidener v2 named \ No newline at end of file diff --git a/common/src/main/resources/optionsprofiles-common.mixins.json b/common/src/main/resources/optionsprofiles.mixins.json similarity index 100% rename from common/src/main/resources/optionsprofiles-common.mixins.json rename to common/src/main/resources/optionsprofiles.mixins.json diff --git a/fabric/build.gradle b/fabric/build.gradle index 5ca331b..d9f3e92 100644 --- a/fabric/build.gradle +++ b/fabric/build.gradle @@ -1,5 +1,5 @@ plugins { - id "com.github.johnrengelman.shadow" version "7.1.2" + id 'com.github.johnrengelman.shadow' } architectury { @@ -7,71 +7,46 @@ architectury { fabric() } -loom { - accessWidenerPath = project(":common").loom.accessWidenerPath -} - configurations { - common - shadowCommon // Don't use shadow from the shadow plugin since it *excludes* files. + common { + canBeResolved = true + canBeConsumed = false + } compileClasspath.extendsFrom common runtimeClasspath.extendsFrom common developmentFabric.extendsFrom common + + // Files in this configuration will be bundled into your mod using the Shadow plugin. + // Don't use the `shadow` configuration from the plugin itself as it's meant for excluding files. + shadowBundle { + canBeResolved = true + canBeConsumed = false + } } dependencies { - modImplementation "net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}" - modApi "net.fabricmc.fabric-api:fabric-api:${rootProject.fabric_api_version}" - // Remove the next line if you don't want to depend on the API - modApi "dev.architectury:architectury-fabric:${rootProject.architectury_version}" + modImplementation "net.fabricmc:fabric-loader:$rootProject.fabric_loader_version" - common(project(path: ":common", configuration: "namedElements")) { transitive false } - shadowCommon(project(path: ":common", configuration: "transformProductionFabric")) { transitive false } + // Fabric API. This is technically optional, but you probably want it anyway. + modImplementation "net.fabricmc.fabric-api:fabric-api:$rootProject.fabric_api_version" + + common(project(path: ':common', configuration: 'namedElements')) { transitive false } + shadowBundle project(path: ':common', configuration: 'transformProductionFabric') } processResources { - inputs.property "version", project.version + inputs.property 'version', project.version - filesMatching("fabric.mod.json") { - expand "version": project.version + filesMatching('fabric.mod.json') { + expand version: project.version } } shadowJar { - exclude "architectury.common.json" - - configurations = [project.configurations.shadowCommon] - archiveClassifier = "dev-shadow" + configurations = [project.configurations.shadowBundle] + archiveClassifier = 'dev-shadow' } remapJar { - injectAccessWidener = true input.set shadowJar.archiveFile - dependsOn shadowJar -} - -sourcesJar { - def commonSources = project(":common").sourcesJar - dependsOn commonSources - from commonSources.archiveFile.map { zipTree(it) } -} - -components.java { - withVariantsFromConfiguration(project.configurations.shadowRuntimeElements) { - skip() - } -} - -publishing { - publications { - mavenFabric(MavenPublication) { - artifactId = rootProject.archives_base_name + "-" + project.name - from components.java - } - } - - // See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing. - repositories { - // Add repositories to publish to here. - } } diff --git a/fabric/src/main/resources/fabric.mod.json b/fabric/src/main/resources/fabric.mod.json index ce6b6aa..f4ecc8c 100644 --- a/fabric/src/main/resources/fabric.mod.json +++ b/fabric/src/main/resources/fabric.mod.json @@ -21,12 +21,12 @@ ] }, "mixins": [ - "optionsprofiles.mixins.json", - "optionsprofiles-common.mixins.json" + "optionsprofiles.mixins.json" ], "depends": { - "fabric": "*", - "minecraft": ">=1.18.2", - "architectury": ">=4.11.93" + "fabricloader": ">=0.15.11", + "minecraft": "~1.18.2", + "java": ">=17", + "fabric": "*" } } \ No newline at end of file diff --git a/fabric/src/main/resources/optionsprofiles.mixins.json b/fabric/src/main/resources/optionsprofiles.mixins.json deleted file mode 100644 index 9d4e801..0000000 --- a/fabric/src/main/resources/optionsprofiles.mixins.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "required": true, - "package": "com.axolotlmaid.optionsprofiles.mixin", - "compatibilityLevel": "JAVA_17", - "mixins": [ - ], - "injectors": { - "defaultRequire": 1 - } -} \ No newline at end of file diff --git a/forge/build.gradle b/forge/build.gradle index ce10b06..a00392b 100644 --- a/forge/build.gradle +++ b/forge/build.gradle @@ -1,5 +1,11 @@ plugins { - id "com.github.johnrengelman.shadow" version "7.1.2" + id 'com.github.johnrengelman.shadow' +} + +loom { + forge { + mixinConfig "optionsprofiles.mixins.json" + } } architectury { @@ -7,78 +13,43 @@ architectury { forge() } -loom { - accessWidenerPath = project(":common").loom.accessWidenerPath - - forge { - convertAccessWideners = true - extraAccessWideners.add loom.accessWidenerPath.get().asFile.name - - mixinConfig "optionsprofiles-common.mixins.json" - mixinConfig "optionsprofiles.mixins.json" - } -} - configurations { - common - shadowCommon // Don't use shadow from the shadow plugin since it *excludes* files. + common { + canBeResolved = true + canBeConsumed = false + } compileClasspath.extendsFrom common runtimeClasspath.extendsFrom common developmentForge.extendsFrom common + + // Files in this configuration will be bundled into your mod using the Shadow plugin. + // Don't use the `shadow` configuration from the plugin itself as it's meant for excluding files. + shadowBundle { + canBeResolved = true + canBeConsumed = false + } } dependencies { - forge "net.minecraftforge:forge:${rootProject.forge_version}" - // Remove the next line if you don't want to depend on the API - modApi "dev.architectury:architectury-forge:${rootProject.architectury_version}" + forge "net.minecraftforge:forge:$rootProject.forge_version" - common(project(path: ":common", configuration: "namedElements")) { transitive false } - shadowCommon(project(path: ":common", configuration: "transformProductionForge")) { transitive = false } + common(project(path: ':common', configuration: 'namedElements')) { transitive false } + shadowBundle project(path: ':common', configuration: 'transformProductionForge') } processResources { - inputs.property "version", project.version + inputs.property 'version', project.version - filesMatching("META-INF/mods.toml") { - expand "version": project.version + filesMatching('META-INF/mods.toml') { + expand version: project.version } } shadowJar { - exclude "fabric.mod.json" - exclude "architectury.common.json" - - configurations = [project.configurations.shadowCommon] - archiveClassifier = "dev-shadow" + configurations = [project.configurations.shadowBundle] + archiveClassifier = 'dev-shadow' } remapJar { input.set shadowJar.archiveFile - dependsOn shadowJar -} - -sourcesJar { - def commonSources = project(":common").sourcesJar - dependsOn commonSources - from commonSources.archiveFile.map { zipTree(it) } -} - -components.java { - withVariantsFromConfiguration(project.configurations.shadowRuntimeElements) { - skip() - } -} - -publishing { - publications { - mavenForge(MavenPublication) { - artifactId = rootProject.archives_base_name + "-" + project.name - from components.java - } - } - - // See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing. - repositories { - // Add repositories to publish to here. - } } diff --git a/forge/src/main/resources/META-INF/mods.toml b/forge/src/main/resources/META-INF/mods.toml index 17c8b66..707dddd 100644 --- a/forge/src/main/resources/META-INF/mods.toml +++ b/forge/src/main/resources/META-INF/mods.toml @@ -25,11 +25,4 @@ modId = "minecraft" mandatory = true versionRange = "[1.18.2,)" ordering = "NONE" -side = "BOTH" - -[[dependencies.optionsprofiles]] -modId = "architectury" -mandatory = true -versionRange = "[4.11.93,)" -ordering = "AFTER" -side = "BOTH" +side = "BOTH" \ No newline at end of file diff --git a/forge/src/main/resources/optionsprofiles.mixins.json b/forge/src/main/resources/optionsprofiles.mixins.json deleted file mode 100644 index 5a28846..0000000 --- a/forge/src/main/resources/optionsprofiles.mixins.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "required": true, - "package": "com.axolotlmaid.optionsprofiles.mixin.forge", - "compatibilityLevel": "JAVA_17", - "minVersion": "0.8", - "client": [ - ], - "mixins": [ - ], - "injectors": { - "defaultRequire": 1 - } -} \ No newline at end of file diff --git a/forge/src/main/resources/pack.mcmeta b/forge/src/main/resources/pack.mcmeta index 4efb538..a1536a9 100644 --- a/forge/src/main/resources/pack.mcmeta +++ b/forge/src/main/resources/pack.mcmeta @@ -1,6 +1,7 @@ { "pack": { "description": "Options Profiles", - "pack_format": 15 + "forge:data_pack_format": 9, + "pack_format": 8 } -} +} \ No newline at end of file diff --git a/gallery/options-toggle-menu.png b/gallery/options-toggle-menu.png new file mode 100644 index 0000000..b50dbbb Binary files /dev/null and b/gallery/options-toggle-menu.png differ diff --git a/gradle.properties b/gradle.properties index c7174a3..52f2edf 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,15 +1,17 @@ +# Done to increase the memory available to Gradle. org.gradle.jvmargs=-Xmx6G +org.gradle.parallel=true -minecraft_version=1.18.2 +# Mod properties +mod_version=1.3 +maven_group=com.axolotlmaid.optionsprofiles +archives_name=optionsprofiles enabled_platforms=fabric,forge -archives_base_name=optionsprofiles -mod_version=1.1 -maven_group=com.axolotlmaid.optionsprofiles +# Minecraft properties +minecraft_version = 1.18.2 -architectury_version=4.11.93 - -fabric_loader_version=0.14.23 -fabric_api_version=0.76.0+1.18.2 - -forge_version=1.18.2-40.2.10 \ No newline at end of file +# Dependencies +fabric_loader_version = 0.15.11 +fabric_api_version = 0.77.0+1.18.2 +forge_version = 1.18.2-40.2.21 \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 744c64d..dab2a01 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists +zipStorePath=wrapper/dists \ No newline at end of file diff --git a/gradlew b/gradlew index 79a61d4..1aa94a4 100644 --- a/gradlew +++ b/gradlew @@ -83,10 +83,8 @@ done # This is normally unused # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -133,10 +131,13 @@ location of your Java installation." fi else JAVACMD=java - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. @@ -144,7 +145,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 + # shellcheck disable=SC2039,SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac @@ -152,7 +153,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then '' | soft) :;; #( *) # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 + # shellcheck disable=SC2039,SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac @@ -197,11 +198,15 @@ if "$cygwin" || "$msys" ; then done fi -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ diff --git a/gradlew.bat b/gradlew.bat index 93e3f59..25da30d 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -43,11 +43,11 @@ set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if %ERRORLEVEL% equ 0 goto execute -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -57,11 +57,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail diff --git a/settings.gradle b/settings.gradle index 03230de..e1f7b7d 100644 --- a/settings.gradle +++ b/settings.gradle @@ -2,13 +2,13 @@ pluginManagement { repositories { maven { url "https://maven.fabricmc.net/" } maven { url "https://maven.architectury.dev/" } - maven { url "https://maven.minecraftforge.net/" } + maven { url "https://files.minecraftforge.net/maven/" } gradlePluginPortal() } } -include("common") -include("fabric") -include("forge") +rootProject.name = 'optionsprofiles-v1.3-1.18.2' -rootProject.name = "optionsprofiles-v1.1-1.18.2" +include 'common' +include 'fabric' +include 'forge'