From 6b07a052f47609856040f0f7380c02317c71a120 Mon Sep 17 00:00:00 2001 From: trafficlunar Date: Wed, 15 Oct 2025 21:22:04 +0100 Subject: [PATCH] feat: profiles for specific servers (#43) --- .../optionsprofiles/Keybinds.java | 12 ---- .../optionsprofiles/OptionsProfilesMod.java | 70 ++++++++++++++++++- .../gui/EditProfileScreen.java | 15 +++- .../optionsprofiles/gui/ProfilesList.java | 11 +-- .../profiles/ProfileConfiguration.java | 9 +++ .../optionsprofiles/profiles/Profiles.java | 52 +++++--------- .../assets/optionsprofiles/lang/en_us.json | 5 ++ 7 files changed, 114 insertions(+), 60 deletions(-) diff --git a/common/src/main/java/net/trafficlunar/optionsprofiles/Keybinds.java b/common/src/main/java/net/trafficlunar/optionsprofiles/Keybinds.java index 376cc49..9e3f178 100644 --- a/common/src/main/java/net/trafficlunar/optionsprofiles/Keybinds.java +++ b/common/src/main/java/net/trafficlunar/optionsprofiles/Keybinds.java @@ -47,19 +47,7 @@ public class Keybinds { ProfileConfiguration profileConfiguration = ProfileConfiguration.get(profileName); if (profileConfiguration.getKeybindIndex() == keybindIndex) { - Minecraft minecraft = Minecraft.getInstance(); - Profiles.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.warn("[Profile '{}']: Loaded through keybind", profileName); } }); 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 d2a09e6..37c59fb 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,19 @@ 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.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( @@ -128,8 +137,10 @@ public class EditProfileScreen extends Screen { } public void onClose(boolean deleted) { - if (!deleted) + if (!deleted) { + this.profileConfiguration.setServers(this.serversEdit.getValue()); this.profileConfiguration.save(); + } 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 f01fc08..27fa2ba 100644 --- a/common/src/main/java/net/trafficlunar/optionsprofiles/gui/ProfilesList.java +++ b/common/src/main/java/net/trafficlunar/optionsprofiles/gui/ProfilesList.java @@ -84,16 +84,7 @@ public class ProfilesList extends ContainerObjectSelectionList { Profiles.loadProfile(profileName.getString()); - - minecraft.options.load(); - - if (ProfileConfiguration.get(profileName.getString()).getOptionsToLoad().contains("resourcePacks")) { - minecraft.options.loadSelectedResourcePacks(minecraft.getResourcePackRepository()); - minecraft.reloadResourcePacks(); - } - - minecraft.options.save(); - minecraft.levelRenderer.allChanged(); + OptionsProfilesMod.LOGGER.warn("[Profile '{}']: Loaded through button", profileName); // ProfilesList.this.checkEntriesLoaded(); // button.active = false; 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 dac09d6..483158b 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,35 +29,7 @@ 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); - } - } + private static final Minecraft minecraft = Minecraft.getInstance(); public static void createProfile() { String profileName = "Profile 1"; @@ -145,7 +118,7 @@ public class Profiles { } } - public static boolean isProfileLoaded(String profileName) { +// public static boolean isProfileLoaded(String profileName) { // TODO: rewrite/fix; returns incorrect results // Path profile = PROFILES_DIRECTORY.resolve(profileName); @@ -198,8 +171,8 @@ public class Profiles { // return false; // } - return false; - } +// return false; +// } private static void loadOptionFile(String profileName, Path options) { ProfileConfiguration profileConfiguration = ProfileConfiguration.get(profileName); @@ -294,6 +267,17 @@ 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.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/resources/assets/optionsprofiles/lang/en_us.json b/common/src/main/resources/assets/optionsprofiles/lang/en_us.json index f2432a3..d7a3491 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",