将 org.jackhuang.hmcl.setting 从 java.io.File 迁移至 NIO (#4498)

This commit is contained in:
Glavo 2025-09-17 18:39:34 +08:00 committed by GitHub
parent 26e274a89a
commit 94994ffdb6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 98 additions and 34 deletions

View File

@ -190,7 +190,7 @@ public final class LauncherHelper {
.thenComposeAsync(() -> logIn(account).withStage("launch.state.logging_in"))
.thenComposeAsync(authInfo -> Task.supplyAsync(() -> {
LaunchOptions launchOptions = repository.getLaunchOptions(
selectedVersion, javaVersionRef.get(), profile.getGameDir().toPath(), javaAgents, javaArguments, scriptFile != null);
selectedVersion, javaVersionRef.get(), profile.getGameDir(), javaAgents, javaArguments, scriptFile != null);
LOG.info("Here's the structure of game mod directory:\n" + FileUtils.printFileStructure(repository.getModManager(selectedVersion).getModsDirectory(), 10));

View File

@ -184,7 +184,7 @@ public final class ModpackHelper {
return new ManuallyCreatedModpackInstallTask(profile, zipFile.toPath(), charset, name)
.thenAcceptAsync(Schedulers.javafx(), location -> {
Profile newProfile = new Profile(name, location.toFile());
Profile newProfile = new Profile(name, location);
newProfile.setUseRelativePath(true);
Profiles.getProfiles().add(newProfile);
Profiles.setSelectedProfile(newProfile);

View File

@ -21,6 +21,7 @@ import com.google.gson.JsonParseException;
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorServer;
import org.jackhuang.hmcl.task.Schedulers;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.util.gson.JsonSerializable;
import org.jackhuang.hmcl.util.gson.JsonUtils;
import org.jackhuang.hmcl.util.gson.TolerableValidationException;
import org.jackhuang.hmcl.util.gson.Validation;
@ -37,6 +38,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
import static org.jackhuang.hmcl.setting.ConfigHolder.config;
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
@JsonSerializable
public final class AuthlibInjectorServers implements Validation {
public static final String CONFIG_FILENAME = "authlib-injectors.json";

View File

@ -45,6 +45,7 @@ import java.io.File;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.*;
import java.net.Proxy;
import java.nio.file.Path;
import java.util.*;
@JsonAdapter(value = Config.Adapter.class)
@ -55,6 +56,7 @@ public final class Config implements Observable {
public static final Gson CONFIG_GSON = new GsonBuilder()
.registerTypeAdapter(File.class, FileTypeAdapter.INSTANCE)
.registerTypeAdapter(Path.class, PathTypeAdapter.INSTANCE)
.registerTypeAdapter(ObservableList.class, new ObservableListCreator())
.registerTypeAdapter(ObservableSet.class, new ObservableSetCreator())
.registerTypeAdapter(ObservableMap.class, new ObservableMapCreator())

View File

@ -35,8 +35,8 @@ import org.jackhuang.hmcl.ui.WeakListenerHolder;
import org.jackhuang.hmcl.util.ToStringBuilder;
import org.jackhuang.hmcl.util.javafx.ObservableHelper;
import java.io.File;
import java.lang.reflect.Type;
import java.nio.file.Path;
import java.util.Optional;
import static org.jackhuang.hmcl.ui.FXUtils.onInvalidating;
@ -65,17 +65,17 @@ public final class Profile implements Observable {
this.selectedVersion.set(selectedVersion);
}
private final ObjectProperty<File> gameDir;
private final ObjectProperty<Path> gameDir;
public ObjectProperty<File> gameDirProperty() {
public ObjectProperty<Path> gameDirProperty() {
return gameDir;
}
public File getGameDir() {
public Path getGameDir() {
return gameDir.get();
}
public void setGameDir(File gameDir) {
public void setGameDir(Path gameDir) {
this.gameDir.set(gameDir);
}
@ -103,7 +103,7 @@ public final class Profile implements Observable {
this.name.set(name);
}
private BooleanProperty useRelativePath = new SimpleBooleanProperty(this, "useRelativePath", false);
private final BooleanProperty useRelativePath = new SimpleBooleanProperty(this, "useRelativePath", false);
public BooleanProperty useRelativePathProperty() {
return useRelativePath;
@ -118,26 +118,26 @@ public final class Profile implements Observable {
}
public Profile(String name) {
this(name, new File(".minecraft"));
this(name, Path.of(".minecraft"));
}
public Profile(String name, File initialGameDir) {
public Profile(String name, Path initialGameDir) {
this(name, initialGameDir, new VersionSetting());
}
public Profile(String name, File initialGameDir, VersionSetting global) {
public Profile(String name, Path initialGameDir, VersionSetting global) {
this(name, initialGameDir, global, null, false);
}
public Profile(String name, File initialGameDir, VersionSetting global, String selectedVersion, boolean useRelativePath) {
public Profile(String name, Path initialGameDir, VersionSetting global, String selectedVersion, boolean useRelativePath) {
this.name = new SimpleStringProperty(this, "name", name);
gameDir = new SimpleObjectProperty<>(this, "gameDir", initialGameDir);
repository = new HMCLGameRepository(this, initialGameDir.toPath());
repository = new HMCLGameRepository(this, initialGameDir);
this.global.set(global == null ? new VersionSetting() : global);
this.selectedVersion.set(selectedVersion);
this.useRelativePath.set(useRelativePath);
gameDir.addListener((a, b, newValue) -> repository.changeDirectory(newValue.toPath()));
gameDir.addListener((a, b, newValue) -> repository.changeDirectory(newValue));
this.selectedVersion.addListener(o -> checkSelectedVersion());
listenerHolder.add(EventBus.EVENT_BUS.channel(RefreshedVersionsEvent.class).registerWeak(event -> checkSelectedVersion(), EventPriority.HIGHEST));
@ -234,7 +234,7 @@ public final class Profile implements Observable {
JsonObject jsonObject = new JsonObject();
jsonObject.add("global", context.serialize(src.getGlobal()));
jsonObject.addProperty("gameDir", src.getGameDir().getPath());
jsonObject.addProperty("gameDir", src.getGameDir().toString());
jsonObject.addProperty("useRelativePath", src.isUseRelativePath());
jsonObject.addProperty("selectedMinecraftVersion", src.getSelectedVersion());
@ -243,12 +243,11 @@ public final class Profile implements Observable {
@Override
public Profile deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
if (json == JsonNull.INSTANCE || !(json instanceof JsonObject)) return null;
JsonObject obj = (JsonObject) json;
if (!(json instanceof JsonObject obj)) return null;
String gameDir = Optional.ofNullable(obj.get("gameDir")).map(JsonElement::getAsString).orElse("");
return new Profile("Default",
new File(gameDir),
Path.of(gameDir),
context.deserialize(obj.get("global"), VersionSetting.class),
Optional.ofNullable(obj.get("selectedMinecraftVersion")).map(JsonElement::getAsString).orElse(""),
Optional.ofNullable(obj.get("useRelativePath")).map(JsonElement::getAsBoolean).orElse(false));

View File

@ -26,7 +26,7 @@ import org.jackhuang.hmcl.Metadata;
import org.jackhuang.hmcl.event.EventBus;
import org.jackhuang.hmcl.event.RefreshedVersionsEvent;
import java.io.File;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
@ -48,20 +48,17 @@ public final class Profiles {
}
public static String getProfileDisplayName(Profile profile) {
switch (profile.getName()) {
case Profiles.DEFAULT_PROFILE:
return i18n("profile.default");
case Profiles.HOME_PROFILE:
return i18n("profile.home");
default:
return profile.getName();
}
return switch (profile.getName()) {
case Profiles.DEFAULT_PROFILE -> i18n("profile.default");
case Profiles.HOME_PROFILE -> i18n("profile.home");
default -> profile.getName();
};
}
private static final ObservableList<Profile> profiles = observableArrayList(profile -> new Observable[] { profile });
private static final ReadOnlyListWrapper<Profile> profilesWrapper = new ReadOnlyListWrapper<>(profiles);
private static ObjectProperty<Profile> selectedProfile = new SimpleObjectProperty<Profile>() {
private static final ObjectProperty<Profile> selectedProfile = new SimpleObjectProperty<Profile>() {
{
profiles.addListener(onInvalidating(this::invalidated));
}
@ -104,8 +101,8 @@ public final class Profiles {
private static void checkProfiles() {
if (profiles.isEmpty()) {
Profile current = new Profile(Profiles.DEFAULT_PROFILE, new File(".minecraft"), new VersionSetting(), null, true);
Profile home = new Profile(Profiles.HOME_PROFILE, Metadata.MINECRAFT_DIRECTORY.toFile());
Profile current = new Profile(Profiles.DEFAULT_PROFILE, Path.of(".minecraft"), new VersionSetting(), null, true);
Profile home = new Profile(Profiles.HOME_PROFILE, Metadata.MINECRAFT_DIRECTORY);
Platform.runLater(() -> profiles.addAll(current, home));
}
}

View File

@ -42,8 +42,9 @@ import org.jackhuang.hmcl.ui.construct.OptionToggleButton;
import org.jackhuang.hmcl.ui.construct.PageCloseEvent;
import org.jackhuang.hmcl.ui.decorator.DecoratorPage;
import org.jackhuang.hmcl.util.StringUtils;
import org.jackhuang.hmcl.util.io.FileUtils;
import java.io.File;
import java.nio.file.Path;
import java.util.Optional;
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
@ -68,7 +69,7 @@ public final class ProfilePage extends BorderPane implements DecoratorPage {
state.set(State.fromTitle(profile == null ? i18n("profile.new") : i18n("profile") + " - " + profileDisplayName));
location = new SimpleStringProperty(this, "location",
Optional.ofNullable(profile).map(Profile::getGameDir).map(File::getAbsolutePath).orElse(".minecraft"));
Optional.ofNullable(profile).map(Profile::getGameDir).map(FileUtils::getAbsolutePath).orElse(".minecraft"));
ScrollPane scroll = new ScrollPane();
this.setCenter(scroll);
@ -153,13 +154,13 @@ public final class ProfilePage extends BorderPane implements DecoratorPage {
profile.setName(txtProfileName.getText());
profile.setUseRelativePath(toggleUseRelativePath.isSelected());
if (StringUtils.isNotBlank(getLocation())) {
profile.setGameDir(new File(getLocation()));
profile.setGameDir(Path.of(getLocation()));
}
} else {
if (StringUtils.isBlank(getLocation())) {
gameDir.onExplore();
}
Profile newProfile = new Profile(txtProfileName.getText(), new File(getLocation()));
Profile newProfile = new Profile(txtProfileName.getText(), Path.of(getLocation()));
newProfile.setUseRelativePath(toggleUseRelativePath.isSelected());
Profiles.getProfiles().add(newProfile);
}

View File

@ -151,6 +151,7 @@ public final class JsonUtils {
.registerTypeAdapter(Instant.class, InstantTypeAdapter.INSTANCE)
.registerTypeAdapter(UUID.class, UUIDTypeAdapter.INSTANCE)
.registerTypeAdapter(File.class, FileTypeAdapter.INSTANCE)
.registerTypeAdapter(Path.class, PathTypeAdapter.INSTANCE)
.registerTypeAdapterFactory(ValidationTypeAdapterFactory.INSTANCE)
.registerTypeAdapterFactory(LowerCaseEnumTypeAdapterFactory.INSTANCE)
.registerTypeAdapterFactory(JsonTypeAdapterFactory.INSTANCE);

View File

@ -0,0 +1,62 @@
/*
* Hello Minecraft! Launcher
* Copyright (C) 2025 huangyuhui <huanghongxun2008@126.com> and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.jackhuang.hmcl.util.gson;
import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;
import java.io.File;
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Path;
/// @author Glavo
public final class PathTypeAdapter extends TypeAdapter<Path> {
public static final PathTypeAdapter INSTANCE = new PathTypeAdapter();
@Override
public Path read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}
String value = in.nextString();
if (File.separatorChar == '\\')
value = value.replace('/', '\\');
return Path.of(value);
}
@Override
public void write(JsonWriter out, Path path) throws IOException {
if (path != null) {
if (path.getFileSystem() != FileSystems.getDefault())
throw new IOException("Unsupported file system: " + path.getFileSystem());
String value = path.toString();
if (!path.isAbsolute() && File.separatorChar == '\\')
value = value.replace('\\', '/');
out.value(value);
} else {
out.nullValue();
}
}
}