diff --git a/common/build.gradle b/common/build.gradle index bc200dd..c77b26c 100644 --- a/common/build.gradle +++ b/common/build.gradle @@ -28,7 +28,7 @@ dependencies { // Mod implementations modImplementation "maven.modrinth:sodium:mc1.21.4-0.6.13-fabric" // Sodium modImplementation "maven.modrinth:sodium-extra:mc1.21.4-0.6.1+fabric" // Sodium Extra - modImplementation "maven.modrinth:iris:1.8.5+1.21.4-fabric" // Iris + modImplementation "maven.modrinth:iris:1.8.8+1.21.4-fabric" // Iris modImplementation "maven.modrinth:distanthorizons:2.2.1-a-1.21.1" // Distant Horizons modImplementation "maven.modrinth:controlify:2.1.1-fabric,1.21.4" // Controlify } diff --git a/common/src/main/java/net/trafficlunar/optionsprofiles/OptionsProfilesMod.java b/common/src/main/java/net/trafficlunar/optionsprofiles/OptionsProfilesMod.java index 88dbf59..e7b3357 100644 --- a/common/src/main/java/net/trafficlunar/optionsprofiles/OptionsProfilesMod.java +++ b/common/src/main/java/net/trafficlunar/optionsprofiles/OptionsProfilesMod.java @@ -1,14 +1,22 @@ package net.trafficlunar.optionsprofiles; import dev.architectury.event.events.client.ClientLifecycleEvent; +import dev.architectury.event.events.client.ClientPlayerEvent; +import net.minecraft.client.player.LocalPlayer; +import net.minecraft.network.Connection; +import net.trafficlunar.optionsprofiles.profiles.ProfileConfiguration; import net.trafficlunar.optionsprofiles.profiles.Profiles; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.SocketAddress; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.Arrays; +import java.util.stream.Stream; public class OptionsProfilesMod { public static final String MOD_ID = "optionsprofiles"; @@ -30,9 +38,33 @@ public class OptionsProfilesMod { // Load mod config CONFIG = OptionsProfilesModConfiguration.load(); - // Init profiles (for loading on startup) + // Load profiles marked to load on startup ClientLifecycleEvent.CLIENT_STARTED.register(client -> { - Profiles.init(); + try (Stream paths = Files.list(Profiles.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); + if (profileConfiguration.shouldLoadOnStartup()) { + Profiles.loadProfile(profileName); + OptionsProfilesMod.LOGGER.info("[Profile '{}']: Loaded on startup", profileName); + } + }); + } catch (IOException e) { + OptionsProfilesMod.LOGGER.error("An error occurred when initializing", e); + } + }); + + // Load profiles marked to load on server join + ClientPlayerEvent.CLIENT_PLAYER_JOIN.register((LocalPlayer player) -> { + handleClientPlayerEvent(player, false); + }); + + // Load profiles marked to load on server leave + ClientPlayerEvent.CLIENT_PLAYER_QUIT.register((LocalPlayer player) -> { + handleClientPlayerEvent(player, true); }); Keybinds.init(); @@ -46,4 +78,38 @@ public class OptionsProfilesMod { return CONFIG; } } + + private static void handleClientPlayerEvent(LocalPlayer player, boolean isOnLeave) { + if (player == null) return; + Connection connection = player.connection.getConnection(); + + // Check if it's not an integrated server + if (!connection.isMemoryConnection()) { + // Get IP address + SocketAddress address = connection.getRemoteAddress(); + if (address instanceof InetSocketAddress inetAddress) { + String ip = inetAddress.getHostString().trim(); + + // Go through all profiles and check what profiles to load + try (Stream paths = Files.list(Profiles.PROFILES_DIRECTORY)) { + paths.filter(Files::isDirectory) + .forEach(path -> { + String profileName = path.getFileName().toString(); + ProfileConfiguration profileConfiguration = ProfileConfiguration.get(profileName); + String[] servers = profileConfiguration.getServers().split(","); + + if (servers.length == 0) return; + + // Check if "leave" for the leave event or IP for the join event is in profile's servers + if (Arrays.asList(servers).contains(isOnLeave ? "leave" : ip)) { + Profiles.loadProfile(profileName); + OptionsProfilesMod.LOGGER.info("[Profile '{}']: Loaded on server ({})" + (isOnLeave ? "leave" : ""), profileName, ip); + } + }); + } catch (IOException e) { + OptionsProfilesMod.LOGGER.error("An error occurred when initializing", e); + } + } + } + } } diff --git a/common/src/main/java/net/trafficlunar/optionsprofiles/gui/EditProfileScreen.java b/common/src/main/java/net/trafficlunar/optionsprofiles/gui/EditProfileScreen.java index 7cd1557..b024d13 100644 --- a/common/src/main/java/net/trafficlunar/optionsprofiles/gui/EditProfileScreen.java +++ b/common/src/main/java/net/trafficlunar/optionsprofiles/gui/EditProfileScreen.java @@ -18,6 +18,7 @@ public class EditProfileScreen extends Screen { private final Component profileName; private final ProfileConfiguration profileConfiguration; private EditBox profileNameEdit; + private EditBox serversEdit; public EditProfileScreen(ProfilesScreen profilesScreen, Component profileName) { super(Component.literal(Component.translatable("gui.optionsprofiles.editing-profile-title").getString() + profileName.getString())); @@ -33,11 +34,20 @@ public class EditProfileScreen extends Screen { this.profileNameEdit = new EditBox(this.font, this.width / 2 - 102, 116, 204, 20, Component.empty()); this.profileNameEdit.setValue(profileName.getString()); + this.serversEdit = new EditBox(this.font, this.width / 2 - 102, 137, 204, 20, Component.empty()); + this.serversEdit.setMaxLength(128); + this.serversEdit.setValue(this.profileConfiguration.getServers()); + this.serversEdit.setHint(Component.translatable("gui.optionsprofiles.servers-hint").withStyle(ChatFormatting.GRAY)); + this.serversEdit.setTooltip(Tooltip.create(Component.translatable("gui.optionsprofiles.servers.tooltip"))); + LinearLayout linearLayoutContent = this.layout.addToContents(LinearLayout.vertical().spacing(12), LayoutSettings::alignHorizontallyCenter); - LinearLayout linearLayoutEditBox = linearLayoutContent.addChild(LinearLayout.vertical().spacing(6), LayoutSettings::alignHorizontallyCenter); + LinearLayout linearLayoutEditBox = linearLayoutContent.addChild(LinearLayout.vertical().spacing(3), LayoutSettings::alignHorizontallyCenter); linearLayoutEditBox.addChild(new StringWidget(Component.translatable("gui.optionsprofiles.profile-name-text"), this.font), LayoutSettings::alignHorizontallyCenter); linearLayoutEditBox.addChild(this.profileNameEdit); + linearLayoutEditBox.addChild(LinearLayout.vertical()); // add an extra spacing above edit box + linearLayoutEditBox.addChild(new StringWidget(Component.translatable("gui.optionsprofiles.servers-text"), this.font), LayoutSettings::alignHorizontallyCenter); + linearLayoutEditBox.addChild(this.serversEdit); LinearLayout linearLayoutButtons = linearLayoutContent.addChild(LinearLayout.vertical().spacing(1), LayoutSettings::alignHorizontallyCenter); linearLayoutButtons.addChild( @@ -53,18 +63,6 @@ public class EditProfileScreen extends Screen { .build(), LayoutSettings::alignHorizontallyCenter ); - linearLayoutButtons.addChild( - Button.builder( - Component.translatable("gui.optionsprofiles.rename-profile"), - (button) -> { - Profiles.renameProfile(profileName.getString(), this.profileNameEdit.getValue()); - this.minecraft.setScreen(new EditProfileScreen(profilesScreen, Component.literal(this.profileNameEdit.getValue()))); - }) - .size(150, 20) - .pos(this.width / 2 - 75, 166) - .build(), - LayoutSettings::alignHorizontallyCenter - ); linearLayoutButtons.addChild( Button.builder( Component.translatable("gui.optionsprofiles.options-toggle").append("..."), @@ -107,7 +105,7 @@ public class EditProfileScreen extends Screen { .withStyle(ChatFormatting.RED), (button) -> { Profiles.deleteProfile(profileName.getString()); - this.onClose(); + this.onClose(true); }) .width(50) .build(), @@ -122,8 +120,18 @@ public class EditProfileScreen extends Screen { this.layout.arrangeElements(); } + @Override public void onClose() { - this.profileConfiguration.save(); + this.onClose(false); + } + + public void onClose(boolean deleted) { + if (!deleted) { + this.profileConfiguration.setServers(this.serversEdit.getValue()); + this.profileConfiguration.save(); + + Profiles.renameProfile(profileName.getString(), this.profileNameEdit.getValue()); + } this.minecraft.setScreen(this.profilesScreen); this.profilesScreen.profilesList.refreshEntries(); } diff --git a/common/src/main/java/net/trafficlunar/optionsprofiles/gui/ProfilesList.java b/common/src/main/java/net/trafficlunar/optionsprofiles/gui/ProfilesList.java index e8d7d3a..438fcf3 100644 --- a/common/src/main/java/net/trafficlunar/optionsprofiles/gui/ProfilesList.java +++ b/common/src/main/java/net/trafficlunar/optionsprofiles/gui/ProfilesList.java @@ -50,12 +50,6 @@ public class ProfilesList extends ContainerObjectSelectionList narratables() { return ImmutableList.of(this.editButton, this.loadButton); } - - protected void checkLoaded() { - this.loadButton.active = !Profiles.isProfileLoaded(profileName.getString()); - } } } \ No newline at end of file diff --git a/common/src/main/java/net/trafficlunar/optionsprofiles/profiles/ProfileConfiguration.java b/common/src/main/java/net/trafficlunar/optionsprofiles/profiles/ProfileConfiguration.java index 11f3b9a..363f191 100644 --- a/common/src/main/java/net/trafficlunar/optionsprofiles/profiles/ProfileConfiguration.java +++ b/common/src/main/java/net/trafficlunar/optionsprofiles/profiles/ProfileConfiguration.java @@ -17,6 +17,7 @@ public class ProfileConfiguration { private static String profileName; private boolean loadOnStartup = false; + private String servers = ""; private int keybindIndex = 0; private List optionsToLoad = new ArrayList<>(); @@ -79,4 +80,12 @@ public class ProfileConfiguration { public void setOptionsToLoad(List optionsToLoad) { this.optionsToLoad = optionsToLoad; } + + public String getServers() { + return servers; + } + + public void setServers(String servers) { + this.servers = servers; + } } diff --git a/common/src/main/java/net/trafficlunar/optionsprofiles/profiles/Profiles.java b/common/src/main/java/net/trafficlunar/optionsprofiles/profiles/Profiles.java index 4cd9864..c0af69b 100644 --- a/common/src/main/java/net/trafficlunar/optionsprofiles/profiles/Profiles.java +++ b/common/src/main/java/net/trafficlunar/optionsprofiles/profiles/Profiles.java @@ -14,8 +14,9 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardCopyOption; -import java.util.*; -import java.util.concurrent.atomic.AtomicBoolean; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.function.Consumer; import java.util.stream.Stream; @@ -28,36 +29,6 @@ public class Profiles { public static final Path IRIS_OPTIONS_FILE = Paths.get("config/iris.properties"); public static final Path DISTANT_HORIZONS_OPTIONS_FILE = Paths.get("config/DistantHorizons.toml"); - public static void init() { - 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); - if (profileConfiguration.shouldLoadOnStartup()) { - Minecraft minecraft = Minecraft.getInstance(); - loadProfile(profileName); - - minecraft.options.load(); - - if (ProfileConfiguration.get(profileName).getOptionsToLoad().contains("resourcePacks")) { - minecraft.options.loadSelectedResourcePacks(minecraft.getResourcePackRepository()); - minecraft.reloadResourcePacks(); - } - - minecraft.options.save(); - minecraft.levelRenderer.allChanged(); - - OptionsProfilesMod.LOGGER.info("[Profile '{}']: Loaded on startup", profileName); - } - }); - } catch (IOException e) { - OptionsProfilesMod.LOGGER.error("An error occurred when initializing", e); - } - } - public static void createProfile() { String profileName = "Profile 1"; Path profile = PROFILES_DIRECTORY.resolve(profileName); @@ -145,59 +116,61 @@ public class Profiles { } } - public static boolean isProfileLoaded(String profileName) { - Path profile = PROFILES_DIRECTORY.resolve(profileName); - ProfileConfiguration profileConfiguration = ProfileConfiguration.get(profileName); +// public static boolean isProfileLoaded(String profileName) { + // TODO: rewrite/fix; returns incorrect results - List optionFiles = new ArrayList<>(); - optionFiles.add(OPTIONS_FILE); +// Path profile = PROFILES_DIRECTORY.resolve(profileName); +// ProfileConfiguration profileConfiguration = ProfileConfiguration.get(profileName); +// +// 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(file -> Platform.isModLoaded("sodium")).ifPresent(optionFiles::add); +// Optional.of(SODIUM_EXTRA_OPTIONS_FILE).filter(file -> Platform.isModLoaded("sodium-extra")).ifPresent(optionFiles::add); +// Optional.of(IRIS_OPTIONS_FILE).filter(file -> Platform.isModLoaded("iris")).ifPresent(optionFiles::add); +// Optional.of(DISTANT_HORIZONS_OPTIONS_FILE).filter(file -> Platform.isModLoaded("distanthorizons")).ifPresent(optionFiles::add); +// +// // Check if the original option file and the profile option file have the same content +// try { +// for (Path optionFile : optionFiles) { +// Path profileOptions = profile.resolve(optionFile.getFileName()); +// +// if (optionFile.getFileName().equals(OPTIONS_FILE)) { +// try (Stream lines = Files.lines(optionFile)) { +// List optionsToLoad = profileConfiguration.getOptionsToLoad(); +// AtomicBoolean loaded = new AtomicBoolean(false); +// +// lines.forEach((line) -> { +// String[] option = line.split(":"); +// +// if (optionsToLoad.contains(option[0])) { +// try (Stream profileLines = Files.lines(profileOptions)) { +// loaded.set(profileLines.anyMatch(profileLine -> profileLine.equals(line))); +// } catch (IOException e) { +// OptionsProfilesMod.LOGGER.error("[Profile '{}']: An error occurred when checking each line in options.txt if the profiles is loaded", profileName, e); +// } +// } +// }); +// +// return loaded.get(); +// } catch (IOException e) { +// OptionsProfilesMod.LOGGER.error("[Profile '{}']: An error occurred when opening options.txt to check if the profile is loaded", profileName, e); +// } +// } else { +// if (!FileUtils.contentEquals(optionFile.toFile(), profileOptions.toFile())) { +// return false; +// } +// } +// } +// } catch (IOException e) { +// OptionsProfilesMod.LOGGER.error("[Profile '{}']: An error occurred when checking if the profile is loaded", profileName, e); +// return false; +// } - // 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(file -> Platform.isModLoaded("sodium")).ifPresent(optionFiles::add); - Optional.of(SODIUM_EXTRA_OPTIONS_FILE).filter(file -> Platform.isModLoaded("sodium-extra")).ifPresent(optionFiles::add); - Optional.of(IRIS_OPTIONS_FILE).filter(file -> Platform.isModLoaded("iris")).ifPresent(optionFiles::add); - Optional.of(DISTANT_HORIZONS_OPTIONS_FILE).filter(file -> Platform.isModLoaded("distanthorizons")).ifPresent(optionFiles::add); - - // Check if the original option file and the profile option file have the same content - try { - for (Path optionFile : optionFiles) { - Path profileOptions = profile.resolve(optionFile.getFileName()); - - if (optionFile.getFileName().equals(OPTIONS_FILE)) { - try (Stream lines = Files.lines(optionFile)) { - List optionsToLoad = profileConfiguration.getOptionsToLoad(); - AtomicBoolean loaded = new AtomicBoolean(false); - - lines.forEach((line) -> { - String[] option = line.split(":"); - - if (optionsToLoad.contains(option[0])) { - try (Stream profileLines = Files.lines(profileOptions)) { - loaded.set(profileLines.anyMatch(profileLine -> profileLine.equals(line))); - } catch (IOException e) { - OptionsProfilesMod.LOGGER.error("[Profile '{}']: An error occurred when checking each line in options.txt if the profiles is loaded", profileName, e); - } - } - }); - - return loaded.get(); - } catch (IOException e) { - OptionsProfilesMod.LOGGER.error("[Profile '{}']: An error occurred when opening options.txt to check if the profile is loaded", profileName, e); - } - } else { - if (!FileUtils.contentEquals(optionFile.toFile(), profileOptions.toFile())) { - return false; - } - } - } - } catch (IOException e) { - OptionsProfilesMod.LOGGER.error("[Profile '{}']: An error occurred when checking if the profile is loaded", profileName, e); - return false; - } - - return true; - } +// return false; +// } private static void loadOptionFile(String profileName, Path options) { ProfileConfiguration profileConfiguration = ProfileConfiguration.get(profileName); @@ -292,6 +265,18 @@ public class Profiles { loadOptionFile(profileName, DISTANT_HORIZONS_OPTIONS_FILE); // Overwrite / load original Disant Horizons option file loadOptionFile(profileName, DISTANT_HORIZONS_OPTIONS_FILE, DistantHorizonsLoader::load); // Tell Distant Horizons mod to reload configuration } + + // Reload Minecraft options + Minecraft minecraft = Minecraft.getInstance(); + minecraft.options.load(); + + if (ProfileConfiguration.get(profileName).getOptionsToLoad().contains("resourcePacks")) { + minecraft.options.loadSelectedResourcePacks(minecraft.getResourcePackRepository()); + minecraft.reloadResourcePacks(); + } + + minecraft.options.save(); + minecraft.levelRenderer.allChanged(); } public static void renameProfile(String profileName, String newProfileName) { diff --git a/common/src/main/java/net/trafficlunar/optionsprofiles/profiles/loaders/SodiumLoader.java b/common/src/main/java/net/trafficlunar/optionsprofiles/profiles/loaders/SodiumLoader.java index 791c4e8..98201fc 100644 --- a/common/src/main/java/net/trafficlunar/optionsprofiles/profiles/loaders/SodiumLoader.java +++ b/common/src/main/java/net/trafficlunar/optionsprofiles/profiles/loaders/SodiumLoader.java @@ -42,8 +42,6 @@ public class SodiumLoader { SodiumClientMod.options().notifications.hasClearedDonationButton = configuration.notifications.has_cleared_donation_button; SodiumClientMod.options().notifications.hasSeenDonationPrompt = configuration.notifications.has_seen_donation_prompt; - SodiumClientMod.options().debug.terrainSortingEnabled = configuration.debug.terrain_sorting_enabled; - try { SodiumGameOptions.writeToDisk(SodiumClientMod.options()); } catch (IOException e) { @@ -56,7 +54,6 @@ public class SodiumLoader { public Advanced advanced; public Performance performance; public Notifications notifications; - public Debug debug; public static class Quality { public String weather_quality; @@ -84,9 +81,5 @@ public class SodiumLoader { public boolean has_cleared_donation_button; public boolean has_seen_donation_prompt; } - - public static class Debug { - public boolean terrain_sorting_enabled; - } } } \ 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 275ac2a..43cca52 100644 --- a/common/src/main/resources/assets/optionsprofiles/lang/en_us.json +++ b/common/src/main/resources/assets/optionsprofiles/lang/en_us.json @@ -6,6 +6,11 @@ "gui.optionsprofiles.editing-profile-title": "Editing Profile: ", "gui.optionsprofiles.profile-name-text": "Profile Name", + + "gui.optionsprofiles.servers-text": "Server List", + "gui.optionsprofiles.servers-hint": "mc.hypixel.net, play.mccis...", + "gui.optionsprofiles.servers.tooltip": "List servers (comma-separated) to load this profile on join. Add 'leave' to load on server leave.", + "gui.optionsprofiles.overwrite-options": "Overwrite", "gui.optionsprofiles.overwrite-options.tooltip": "Replaces the profile's options with your current options", "gui.optionsprofiles.rename-profile": "Rename", @@ -14,6 +19,7 @@ "gui.optionsprofiles.load-on-startup": "Load on startup", "gui.optionsprofiles.options-toggle": "Select options to toggle", + "gui.optionsprofiles.options-search-hint": "Search options...", "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", @@ -23,7 +29,7 @@ "gui.optionsprofiles.show-profiles-button": "Show Profiles Button", "gui.optionsprofiles.show-profiles-button.tooltip": "Toggle to remove the profiles button in Options. Access profiles through /optionsprofiles.", - "category.optionsprofiles.keys": "Options Profiles", + "key.category.optionsprofiles.keys": "Options Profiles", "key.optionsprofiles.profile_1": "Profile 1", "key.optionsprofiles.profile_2": "Profile 2", "key.optionsprofiles.profile_3": "Profile 3" diff --git a/gradle.properties b/gradle.properties index 45e4f04..54d20a1 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,7 +5,7 @@ org.gradle.parallel=true loom.ignoreDependencyLoomVersionValidation=true # Mod properties -mod_version=1.4.1 +mod_version=1.4.4 maven_group=net.trafficlunar.optionsprofiles archives_name=optionsprofiles enabled_platforms=fabric,neoforge diff --git a/neoforge/src/main/resources/META-INF/neoforge.mods.toml b/neoforge/src/main/resources/META-INF/neoforge.mods.toml index 64ca7c9..febf26a 100644 --- a/neoforge/src/main/resources/META-INF/neoforge.mods.toml +++ b/neoforge/src/main/resources/META-INF/neoforge.mods.toml @@ -1,5 +1,5 @@ modLoader = "javafml" -loaderVersion = "[2,)" +loaderVersion = "[6,)" issueTrackerURL = "https://github.com/trafficlunar/options-profiles/issues" license = "GNU GPL 3.0" diff --git a/settings.gradle b/settings.gradle index acfe092..37ed53e 100644 --- a/settings.gradle +++ b/settings.gradle @@ -11,4 +11,4 @@ include("common") include("fabric") include("neoforge") -rootProject.name = "optionsprofiles-v1.4.1-1.21.4" +rootProject.name = "optionsprofiles-v1.4.4-1.21.4"