feat: port to v1.4.4

This commit is contained in:
trafficlunar 2025-11-07 21:21:59 +00:00
parent 738517594b
commit 0a3c79ec54
11 changed files with 179 additions and 125 deletions

View file

@ -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
}

View file

@ -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<Path> 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<Path> 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);
}
}
}
}
}

View file

@ -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();
}

View file

@ -50,12 +50,6 @@ public class ProfilesList extends ContainerObjectSelectionList<ProfilesList.Prof
} catch (Exception e) {
OptionsProfilesMod.LOGGER.error("An error occurred when listing profiles", e);
}
checkEntriesLoaded();
}
public void checkEntriesLoaded() {
this.children().forEach(ProfileEntry::checkLoaded);
}
protected int scrollBarX() {
@ -94,9 +88,6 @@ public class ProfilesList extends ContainerObjectSelectionList<ProfilesList.Prof
minecraft.options.save();
minecraft.levelRenderer.allChanged();
ProfilesList.this.checkEntriesLoaded();
button.active = false;
})
.size(75, 20)
.build();
@ -125,9 +116,5 @@ public class ProfilesList extends ContainerObjectSelectionList<ProfilesList.Prof
public List<? extends NarratableEntry> narratables() {
return ImmutableList.of(this.editButton, this.loadButton);
}
protected void checkLoaded() {
this.loadButton.active = !Profiles.isProfileLoaded(profileName.getString());
}
}
}

View file

@ -17,6 +17,7 @@ public class ProfileConfiguration {
private static String profileName;
private boolean loadOnStartup = false;
private String servers = "";
private int keybindIndex = 0;
private List<String> optionsToLoad = new ArrayList<>();
@ -79,4 +80,12 @@ public class ProfileConfiguration {
public void setOptionsToLoad(List<String> optionsToLoad) {
this.optionsToLoad = optionsToLoad;
}
public String getServers() {
return servers;
}
public void setServers(String servers) {
this.servers = servers;
}
}

View file

@ -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<Path> 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<Path> optionFiles = new ArrayList<>();
optionFiles.add(OPTIONS_FILE);
// Path profile = PROFILES_DIRECTORY.resolve(profileName);
// ProfileConfiguration profileConfiguration = ProfileConfiguration.get(profileName);
//
// List<Path> 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<String> lines = Files.lines(optionFile)) {
// List<String> optionsToLoad = profileConfiguration.getOptionsToLoad();
// AtomicBoolean loaded = new AtomicBoolean(false);
//
// lines.forEach((line) -> {
// String[] option = line.split(":");
//
// if (optionsToLoad.contains(option[0])) {
// try (Stream<String> 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<String> lines = Files.lines(optionFile)) {
List<String> optionsToLoad = profileConfiguration.getOptionsToLoad();
AtomicBoolean loaded = new AtomicBoolean(false);
lines.forEach((line) -> {
String[] option = line.split(":");
if (optionsToLoad.contains(option[0])) {
try (Stream<String> 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) {

View file

@ -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;
}
}
}

View file

@ -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"

View file

@ -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

View file

@ -1,5 +1,5 @@
modLoader = "javafml"
loaderVersion = "[2,)"
loaderVersion = "[6,)"
issueTrackerURL = "https://github.com/trafficlunar/options-profiles/issues"
license = "GNU GPL 3.0"

View file

@ -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"