Make background transparent?

This commit is contained in:
huangyuhui
2018-01-09 00:03:34 +08:00
parent a40c5fdd40
commit 4b65d4da06
80 changed files with 2201 additions and 1406 deletions

View File

@@ -28,11 +28,11 @@ public abstract class Account {
public abstract String getUsername();
public AuthInfo logIn() throws AuthenticationException {
return logIn(Proxy.NO_PROXY);
public AuthInfo logIn(MultiCharacterSelector selector) throws AuthenticationException {
return logIn(selector, Proxy.NO_PROXY);
}
public abstract AuthInfo logIn(Proxy proxy) throws AuthenticationException;
public abstract AuthInfo logIn(MultiCharacterSelector selector, Proxy proxy) throws AuthenticationException;
public abstract void logOut();

View File

@@ -0,0 +1,39 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* 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 {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.auth;
import org.jackhuang.hmcl.auth.yggdrasil.GameProfile;
import java.util.List;
/**
* This interface is for your application to open a GUI for user to choose the character
* when a having-multi-character yggdrasil account is being logging in..
*/
public interface MultiCharacterSelector {
/**
* Select one of {@code names} GameProfiles to login.
* @param names available game profiles.
* @throws NoSelectedCharacterException if cannot select any character may because user close the selection window or cancel the selection.
* @return your choice of game profile.
*/
GameProfile select(Account account, List<GameProfile> names) throws NoSelectedCharacterException;
MultiCharacterSelector DEFAULT = (account, names) -> names.stream().findFirst().orElseThrow(() -> new NoSelectedCharacterException(account));
}

View File

@@ -0,0 +1,34 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* 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 {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.auth;
/**
* This exception gets threw when authenticating a yggdrasil account and there is no valid character.
* (A account may hold more than one characters.)
*/
public final class NoCharacterException extends AuthenticationException {
private final Account account;
public NoCharacterException(Account account) {
this.account = account;
}
public Account getAccount() {
return account;
}
}

View File

@@ -0,0 +1,41 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* 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 {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.auth;
/**
* This exception gets threw when a monitor of {@link MultiCharacterSelector} cannot select a
* valid character.
*
* @see org.jackhuang.hmcl.auth.MultiCharacterSelector
* @author huangyuhui
*/
public final class NoSelectedCharacterException extends AuthenticationException {
private final Account account;
/**
*
* @param account the error yggdrasil account.
*/
public NoSelectedCharacterException(Account account) {
this.account = account;
}
public Account getAccount() {
return account;
}
}

View File

@@ -54,7 +54,7 @@ public class OfflineAccount extends Account {
}
@Override
public AuthInfo logIn(Proxy proxy) throws AuthenticationException {
public AuthInfo logIn(MultiCharacterSelector selector, Proxy proxy) throws AuthenticationException {
if (StringUtils.isBlank(username) || StringUtils.isBlank(uuid))
throw new AuthenticationException("Username cannot be empty");

View File

@@ -24,13 +24,16 @@ import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import org.jackhuang.hmcl.util.Immutable;
import java.lang.reflect.Type;
import java.util.UUID;
/**
*
* @author huang
* @author huangyuhui
*/
@Immutable
public final class GameProfile {
private final UUID id;

View File

@@ -23,14 +23,9 @@ import com.google.gson.JsonParseException;
import java.io.IOException;
import java.net.Proxy;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import org.jackhuang.hmcl.auth.Account;
import org.jackhuang.hmcl.auth.AuthInfo;
import org.jackhuang.hmcl.auth.AuthenticationException;
import org.jackhuang.hmcl.auth.UserType;
import java.util.*;
import org.jackhuang.hmcl.auth.*;
import org.jackhuang.hmcl.util.NetworkUtils;
import org.jackhuang.hmcl.util.StringUtils;
import org.jackhuang.hmcl.util.UUIDTypeAdapter;
@@ -111,7 +106,7 @@ public final class YggdrasilAccount extends Account {
}
@Override
public AuthInfo logIn(Proxy proxy) throws AuthenticationException {
public AuthInfo logIn(MultiCharacterSelector selector, Proxy proxy) throws AuthenticationException {
if (canPlayOnline())
return new AuthInfo(selectedProfile, accessToken, userType, GSON.toJson(userProperties));
else {
@@ -119,11 +114,13 @@ public final class YggdrasilAccount extends Account {
if (!isLoggedIn())
throw new AuthenticationException("Wrong password for account " + username);
if (selectedProfile == null)
// TODO: multi-available-profiles support
throw new UnsupportedOperationException("Do not support multi-available-profiles account yet.");
else
return new AuthInfo(selectedProfile, accessToken, userType, GSON.toJson(userProperties));
if (selectedProfile == null) {
if (profiles == null || profiles.length <= 0)
throw new NoCharacterException(this);
selectedProfile = selector.select(this, Arrays.asList(profiles));
}
return new AuthInfo(selectedProfile, accessToken, userType, GSON.toJson(userProperties));
}
}

View File

@@ -24,7 +24,7 @@ import org.jackhuang.hmcl.util.Immutable;
* @author huangyuhui
*/
@Immutable
public final class Install {
public final class ForgeInstall {
private final String profileName;
private final String target;
@@ -36,11 +36,11 @@ public final class Install {
private final String mirrorList;
private final String logo;
public Install() {
public ForgeInstall() {
this(null, null, null, null, null, null, null, null, null);
}
public Install(String profileName, String target, String path, String version, String filePath, String welcome, String minecraft, String mirrorList, String logo) {
public ForgeInstall(String profileName, String target, String path, String version, String filePath, String welcome, String minecraft, String mirrorList, String logo) {
this.profileName = profileName;
this.target = target;
this.path = path;

View File

@@ -28,20 +28,20 @@ import org.jackhuang.hmcl.util.Validation;
* @author huangyuhui
*/
@Immutable
public final class InstallProfile implements Validation {
public final class ForgeInstallProfile implements Validation {
@SerializedName("install")
private final Install install;
private final ForgeInstall install;
@SerializedName("versionInfo")
private final Version versionInfo;
public InstallProfile(Install install, Version versionInfo) {
public ForgeInstallProfile(ForgeInstall install, Version versionInfo) {
this.install = install;
this.versionInfo = versionInfo;
}
public Install getInstall() {
public ForgeInstall getInstall() {
return install;
}

View File

@@ -107,7 +107,7 @@ public final class ForgeInstallTask extends TaskResult<Version> {
if (stream == null)
throw new IOException("Malformed forge installer file, install_profile.json does not exist.");
String json = IOUtils.readFullyAsString(stream);
InstallProfile installProfile = Constants.GSON.fromJson(json, InstallProfile.class);
ForgeInstallProfile installProfile = Constants.GSON.fromJson(json, ForgeInstallProfile.class);
if (installProfile == null)
throw new IOException("Malformed forge installer file, install_profile.json does not exist.");

View File

@@ -57,14 +57,13 @@ public final class GameLibrariesTask extends Task {
@Override
public void execute() throws Exception {
for (Library library : version.getLibraries())
if (library.appliesToCurrentEnvironment()) {
File file = dependencyManager.getGameRepository().getLibraryFile(version, library);
if (!file.exists())
dependencies.add(new FileDownloadTask(
NetworkUtils.toURL(dependencyManager.getDownloadProvider().injectURL(library.getDownload().getUrl())),
file, dependencyManager.getProxy(), library.getDownload().getSha1()));
}
version.getLibraries().stream().filter(Library::appliesToCurrentEnvironment).forEach(library -> {
File file = dependencyManager.getGameRepository().getLibraryFile(version, library);
if (!file.exists())
dependencies.add(new FileDownloadTask(
NetworkUtils.toURL(dependencyManager.getDownloadProvider().injectURL(library.getDownload().getUrl())),
file, dependencyManager.getProxy(), library.getDownload().getSha1()));
});
}
}

View File

@@ -36,7 +36,7 @@ public final class GameRemoteVersions {
private final GameRemoteLatestVersions latest;
public GameRemoteVersions() {
this(Collections.EMPTY_LIST, null);
this(Collections.emptyList(), null);
}
public GameRemoteVersions(List<GameRemoteVersion> versions, GameRemoteLatestVersions latest) {

View File

@@ -61,7 +61,7 @@ public final class VersionJsonDownloadTask extends Task {
@Override
public void execute() throws Exception {
RemoteVersion<?> remoteVersion = gameVersionList.getVersions(gameVersion).stream().findFirst()
.orElseThrow(() -> new IllegalStateException("Cannot find specific version "+gameVersion+" in remote repository"));
.orElseThrow(() -> new IllegalStateException("Cannot find specific version " + gameVersion + " in remote repository"));
String jsonURL = dependencyManager.getDownloadProvider().injectURL(remoteVersion.getUrl());
dependencies.add(new GetTask(NetworkUtils.toURL(jsonURL), Proxy.NO_PROXY, ID));
}

View File

@@ -38,7 +38,7 @@ public final class LiteLoaderBranch {
private final Map<String, LiteLoaderVersion> liteLoader;
public LiteLoaderBranch() {
this(Collections.EMPTY_SET, Collections.EMPTY_MAP);
this(Collections.emptySet(), Collections.emptyMap());
}
public LiteLoaderBranch(Collection<Library> libraries, Map<String, LiteLoaderVersion> liteLoader) {

View File

@@ -96,11 +96,11 @@ public final class LiteLoaderInstallTask extends TaskResult<Version> {
new LibrariesDownloadInfo(new LibraryDownloadInfo(null, remote.getUrl()))
);
Version tempVersion = version.setLibraries(Lang.merge(remote.getTag().getLibraries(), Arrays.asList(library)));
Version tempVersion = version.setLibraries(Lang.merge(remote.getTag().getLibraries(), Collections.singleton(library)));
setResult(version
.setMainClass("net.minecraft.launchwrapper.Launch")
.setLibraries(Lang.merge(tempVersion.getLibraries(), version.getLibraries()))
.setLogging(Collections.EMPTY_MAP)
.setLogging(Collections.emptyMap())
.setMinecraftArguments(version.getMinecraftArguments().orElse("") + " --tweakClass " + remote.getTag().getTweakClass())
//.setArguments(Arguments.addGameArguments(Lang.get(version.getArguments()), "--tweakClass", remote.getTag().getTweakClass()))
);

View File

@@ -113,7 +113,7 @@ public final class OptiFineInstallTask extends TaskResult<Version> {
hasFMLTweaker = true;
if (version.getArguments().isPresent()) {
List<Argument> game = version.getArguments().get().getGame();
if (game.stream().anyMatch(arg -> arg.toString(Collections.EMPTY_MAP, Collections.EMPTY_MAP).contains("FMLTweaker")))
if (game.stream().anyMatch(arg -> arg.toString(Collections.emptyMap(), Collections.emptyMap()).contains("FMLTweaker")))
hasFMLTweaker = true;
}

View File

@@ -80,10 +80,13 @@ public final class OptiFineVersionList extends VersionList<Void> {
if (td.getAttribute("class") != null && td.getAttribute("class").startsWith("downloadLineFile"))
version = td.getTextContent();
}
if (version == null || url == null)
continue;
Matcher matcher = PATTERN.matcher(version);
while (matcher.find())
gameVersion = matcher.group(1);
if (gameVersion == null || version == null || url == null)
if (gameVersion == null)
continue;
versions.put(gameVersion, new RemoteVersion<>(gameVersion, version, url, null));
}

View File

@@ -62,7 +62,7 @@ public class Event extends EventObject {
return false;
}
private Result result;
private Result result = Result.DEFAULT;
/**
* Retutns the value set as the result of this event

View File

@@ -17,7 +17,6 @@
*/
package org.jackhuang.hmcl.event;
import java.util.EventObject;
import java.util.HashMap;
import org.jackhuang.hmcl.task.Schedulers;
@@ -29,14 +28,14 @@ public final class EventBus {
private final HashMap<Class<?>, EventManager<?>> events = new HashMap<>();
public <T extends EventObject> EventManager<T> channel(Class<T> clazz) {
public <T extends Event> EventManager<T> channel(Class<T> clazz) {
if (!events.containsKey(clazz))
events.put(clazz, new EventManager<>(Schedulers.computation()));
events.put(clazz, new EventManager<>());
return (EventManager<T>) events.get(clazz);
}
public void fireEvent(EventObject obj) {
channel((Class<EventObject>) obj.getClass()).fireEvent(obj);
public Event.Result fireEvent(Event obj) {
return channel((Class<Event>) obj.getClass()).fireEvent(obj);
}
public static final EventBus EVENT_BUS = new EventBus();

View File

@@ -18,7 +18,6 @@
package org.jackhuang.hmcl.event;
import java.util.EnumMap;
import java.util.EventObject;
import java.util.HashSet;
import java.util.function.Consumer;
import org.jackhuang.hmcl.task.Scheduler;
@@ -29,22 +28,13 @@ import org.jackhuang.hmcl.util.SimpleMultimap;
*
* @author huangyuhui
*/
public final class EventManager<T extends EventObject> {
public final class EventManager<T extends Event> {
private final Scheduler scheduler;
private final SimpleMultimap<EventPriority, Consumer<T>> handlers
= new SimpleMultimap<>(() -> new EnumMap<>(EventPriority.class), HashSet::new);
private final SimpleMultimap<EventPriority, Runnable> handlers2
= new SimpleMultimap<>(() -> new EnumMap<>(EventPriority.class), HashSet::new);
public EventManager() {
this(Schedulers.immediate());
}
public EventManager(Scheduler scheduler) {
this.scheduler = scheduler;
}
public void register(Consumer<T> consumer) {
register(consumer, EventPriority.NORMAL);
}
@@ -71,15 +61,18 @@ public final class EventManager<T extends EventObject> {
handlers2.removeValue(runnable);
}
public void fireEvent(T event) {
scheduler.schedule(() -> {
for (EventPriority priority : EventPriority.values()) {
for (Consumer<T> handler : handlers.get(priority))
handler.accept(event);
for (Runnable runnable : handlers2.get(priority))
runnable.run();
}
});
public Event.Result fireEvent(T event) {
for (EventPriority priority : EventPriority.values()) {
for (Consumer<T> handler : handlers.get(priority))
handler.accept(event);
for (Runnable runnable : handlers2.get(priority))
runnable.run();
}
if (event.hasResult())
return event.getResult();
else
return Event.Result.DEFAULT;
}
}

View File

@@ -23,7 +23,7 @@ import java.util.EventObject;
*
* @author huang
*/
public class FailedEvent<T> extends EventObject {
public class FailedEvent<T> extends Event {
private final int failedTime;
private T newResult;

View File

@@ -0,0 +1,52 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* 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 {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.event;
import java.io.File;
/**
* This event gets fired when json of a game version is malformed. You can do something here.
* auto making up for the missing json, don't forget to set result to {@link Event.Result#ALLOW}.
* and even asking for removing the redundant version folder.
*
* The result ALLOW means you have corrected the json.
*/
public final class GameJsonParseFailedEvent extends Event {
private final String version;
private final File jsonFile;
/**
*
* @param source {@link org.jackhuang.hmcl.game.DefaultGameRepository}
* @param jsonFile the minecraft.json file.
* @param version the version name
*/
public GameJsonParseFailedEvent(Object source, File jsonFile, String version) {
super(source);
this.version = version;
this.jsonFile = jsonFile;
}
public File getJsonFile() {
return jsonFile;
}
public String getVersion() {
return version;
}
}

View File

@@ -23,16 +23,20 @@ import org.jackhuang.hmcl.util.ManagedProcess;
/**
* This event gets fired when we launch the JVM and it got crashed.
* <br>
* This event is fired on the [org.jackhuang.hmcl.event.EVENT_BUS]
* This event is fired on the {@link org.jackhuang.hmcl.event.EventBus#EVENT_BUS}
*
* @param source [org.jackhuang.hmcl.launch.ExitWaiter]
* @param value the crashed process.
* @author huangyuhui
*/
public class JVMLaunchFailedEvent extends EventObject {
public class JVMLaunchFailedEvent extends Event {
private final ManagedProcess process;
/**
* Constructor.
*
* @param source {@link org.jackhuang.hmcl.launch.ExitWaiter}
* @param process the crashed process.
*/
public JVMLaunchFailedEvent(Object source, ManagedProcess process) {
super(source);
this.process = process;

View File

@@ -17,28 +17,37 @@
*/
package org.jackhuang.hmcl.event;
import org.jackhuang.hmcl.game.Version;
import java.util.EventObject;
/**
* This event gets fired when a minecraft version has been loaded.
* <br>
* This event is fired on the {@link org.jackhuang.hmcl.event.EVENT_BUS}
*
* @param source {@link org.jackhuang.hmcl.game.GameRepository}
* @param version the version id.
* This event is fired on the {@link org.jackhuang.hmcl.event.EventBus#EVENT_BUS}
*
* @author huangyuhui
*/
public final class LoadedOneVersionEvent extends EventObject {
public final class LoadedOneVersionEvent extends Event {
private final String version;
private final Version version;
public LoadedOneVersionEvent(Object source, String version) {
/**
*
* @param source {@link org.jackhuang.hmcl.game.GameRepository}
* @param version the version id.
*/
public LoadedOneVersionEvent(Object source, Version version) {
super(source);
this.version = version;
}
public String getVersion() {
public Version getVersion() {
return version;
}
@Override
public boolean hasResult() {
return true;
}
}

View File

@@ -23,16 +23,20 @@ import org.jackhuang.hmcl.util.ManagedProcess;
/**
* This event gets fired when a JavaProcess exited abnormally and the exit code is not zero.
* <br></br>
* This event is fired on the {@link org.jackhuang.hmcl.event.EVENT_BUS}
* This event is fired on the {@link org.jackhuang.hmcl.event.EventBus#EVENT_BUS}
*
* @param source {@link org.jackhuang.hmcl.launch.ExitWaiter}
* @param value The process that exited abnormally.
* @author huangyuhui
*/
public final class ProcessExitedAbnormallyEvent extends EventObject {
public final class ProcessExitedAbnormallyEvent extends Event {
private final ManagedProcess process;
/**
* Constructor.
*
* @param source {@link org.jackhuang.hmcl.launch.ExitWaiter}
* @param process The process that exited abnormally.
*/
public ProcessExitedAbnormallyEvent(Object source, ManagedProcess process) {
super(source);
this.process = process;

View File

@@ -23,16 +23,20 @@ import org.jackhuang.hmcl.util.ManagedProcess;
/**
* This event gets fired when minecraft process exited successfully and the exit code is 0.
* <br>
* This event is fired on the {@link org.jackhuang.hmcl.event.EVENT_BUS}
* This event is fired on the {@link org.jackhuang.hmcl.event.EventBus#EVENT_BUS}
*
* @param source {@link org.jackhuang.hmcl.launch.ExitWaiter}
* @param value minecraft process
* @author huangyuhui
*/
public class ProcessStoppedEvent extends EventObject {
public class ProcessStoppedEvent extends Event {
private final ManagedProcess process;
/**
* Constructor.
*
* @param source {@link org.jackhuang.hmcl.launch.ExitWaiter}
* @param process minecraft process
*/
public ProcessStoppedEvent(Object source, ManagedProcess process) {
super(source);
this.process = process;

View File

@@ -22,14 +22,17 @@ import java.util.EventObject;
/**
* This event gets fired when all the versions in .minecraft folder are loaded.
* <br>
* This event is fired on the {@link org.jackhuang.hmcl.api.HMCLApi#EVENT_BUS}
*
* @param source {@link org.jackhuang.hmcl.game.GameRepository]
* This event is fired on the {@link org.jackhuang.hmcl.event.EventBus#EVENT_BUS}
*
* @author huangyuhui
*/
public final class RefreshedVersionsEvent extends EventObject {
public final class RefreshedVersionsEvent extends Event {
/**
* Constructor.
*
* @param source {@link org.jackhuang.hmcl.game.GameRepository]
*/
public RefreshedVersionsEvent(Object source) {
super(source);
}

View File

@@ -22,14 +22,17 @@ import java.util.EventObject;
/**
* This event gets fired when loading versions in a .minecraft folder.
* <br>
* This event is fired on the {@link org.jackhuang.hmcl.event.EVENT_BUS}
*
* @param source {@link org.jackhuang.hmcl.game.GameRepository}
* This event is fired on the {@link org.jackhuang.hmcl.event.EventBus#EVENT_BUS}
*
* @author huangyuhui
*/
public final class RefreshingVersionsEvent extends EventObject {
public final class RefreshingVersionsEvent extends Event {
/**
* Constructor.
*
* @param source {@link org.jackhuang.hmcl.game.GameRepository}
*/
public RefreshingVersionsEvent(Object source) {
super(source);
}

View File

@@ -50,11 +50,11 @@ public final class Arguments {
}
public List<Argument> getGame() {
return game == null ? Collections.EMPTY_LIST : Collections.unmodifiableList(game);
return game == null ? Collections.emptyList() : Collections.unmodifiableList(game);
}
public List<Argument> getJvm() {
return jvm == null ? Collections.EMPTY_LIST : Collections.unmodifiableList(jvm);
return jvm == null ? Collections.emptyList() : Collections.unmodifiableList(jvm);
}
public static Arguments addGameArguments(Arguments arguments, String... gameArguments) {
@@ -83,7 +83,7 @@ public final class Arguments {
}
public static List<String> parseArguments(List<Argument> arguments, Map<String, String> keys) {
return parseArguments(arguments, keys, Collections.EMPTY_MAP);
return parseArguments(arguments, keys, Collections.emptyMap());
}
public static List<String> parseArguments(List<Argument> arguments, Map<String, String> keys, Map<String, Boolean> features) {

View File

@@ -35,7 +35,7 @@ public final class AssetIndex {
private final Map<String, AssetObject> objects;
public AssetIndex() {
this(false, Collections.EMPTY_MAP);
this(false, Collections.emptyMap());
}
public AssetIndex(boolean virtual, Map<String, AssetObject> objects) {

View File

@@ -21,16 +21,11 @@ import com.google.gson.JsonParseException;
import com.google.gson.JsonSyntaxException;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.TreeMap;
import java.util.*;
import java.util.logging.Level;
import org.jackhuang.hmcl.event.EventBus;
import org.jackhuang.hmcl.event.LoadedOneVersionEvent;
import org.jackhuang.hmcl.event.RefreshedVersionsEvent;
import org.jackhuang.hmcl.event.RefreshingVersionsEvent;
import org.jackhuang.hmcl.event.*;
import org.jackhuang.hmcl.task.Schedulers;
import org.jackhuang.hmcl.util.Constants;
import org.jackhuang.hmcl.util.FileUtils;
import org.jackhuang.hmcl.util.Lang;
@@ -94,7 +89,7 @@ public class DefaultGameRepository implements GameRepository {
@Override
public File getVersionJar(Version version) {
Version v = version.resolve(this);
String id = Lang.nonNull(v.getJar(), v.getId());
String id = Optional.ofNullable(v.getJar()).orElse(v.getId());
return new File(getVersionRoot(id), id + ".jar");
}
@@ -184,9 +179,15 @@ public class DefaultGameRepository implements GameRepository {
version = Objects.requireNonNull(readVersionJson(json));
} catch (Exception e) {
// JsonSyntaxException or IOException or NullPointerException(!!)
// TODO: auto making up for the missing json
// TODO: and even asking for removing the redundant version folder.
continue;
if (EventBus.EVENT_BUS.fireEvent(new GameJsonParseFailedEvent(this, json, id)) != Event.Result.ALLOW)
continue;
try {
version = Objects.requireNonNull(readVersionJson(json));
} catch (Exception e2) {
Logging.LOG.log(Level.SEVERE, "User corrected version json is still malformed");
continue;
}
}
if (!id.equals(version.getId())) {
@@ -199,18 +200,20 @@ public class DefaultGameRepository implements GameRepository {
}
}
versions.put(id, version);
EventBus.EVENT_BUS.fireEvent(new LoadedOneVersionEvent(this, id));
if (EventBus.EVENT_BUS.fireEvent(new LoadedOneVersionEvent(this, version)) != Event.Result.DENY)
versions.put(id, version);
}
loaded = true;
}
@Override
public final void refreshVersions() {
public void refreshVersions() {
EventBus.EVENT_BUS.fireEvent(new RefreshingVersionsEvent(this));
refreshVersionsImpl();
EventBus.EVENT_BUS.fireEvent(new RefreshedVersionsEvent(this));
Schedulers.newThread().schedule(() -> {
refreshVersionsImpl();
EventBus.EVENT_BUS.fireEvent(new RefreshedVersionsEvent(this));
});
}
@Override

View File

@@ -29,6 +29,8 @@ import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import org.jackhuang.hmcl.util.Constants;
import org.jackhuang.hmcl.util.Lang;
import org.jackhuang.hmcl.util.OperatingSystem;
@@ -99,7 +101,7 @@ public class Library {
+ (this.classifier == null ? "" : "-" + this.classifier) + ".jar";
download = new LibraryDownloadInfo(path,
Lang.nonNull(Lang.nonNull(temp != null ? temp.getUrl() : null), Lang.nonNull(url, Constants.DEFAULT_LIBRARY_URL) + path),
Optional.ofNullable(temp).map(LibraryDownloadInfo::getUrl).orElse(Optional.ofNullable(url).orElse(Constants.DEFAULT_LIBRARY_URL) + path),
temp != null ? temp.getSha1() : null,
temp != null ? temp.getSize() : 0
);

View File

@@ -75,7 +75,7 @@ public class RuledArgument implements Argument {
.map(StringArgument::new)
.map(str -> str.toString(keys, features).get(0))
.collect(Collectors.toList());
return Collections.EMPTY_LIST;
return Collections.emptyList();
}
public static class Serializer implements JsonSerializer<RuledArgument>, JsonDeserializer<RuledArgument> {

View File

@@ -155,8 +155,6 @@ public class Version implements Comparable<Version>, Validation {
/**
* Resolve given version
*
* @throws CircleDependencyException
*/
public Version resolve(VersionProvider provider) {
return resolve(provider, new HashSet<>());
@@ -231,10 +229,7 @@ public class Version implements Comparable<Version>, Validation {
@Override
public boolean equals(Object obj) {
if (obj instanceof Version)
return Objects.equals(id, ((Version) obj).id);
else
return false;
return obj instanceof Version && Objects.equals(id, ((Version) obj).id);
}
@Override

View File

@@ -22,11 +22,7 @@ import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import org.jackhuang.hmcl.auth.AuthInfo;
@@ -207,7 +203,7 @@ public class DefaultLauncher extends Launcher {
.filter(it -> !getForbiddens().containsKey(it) || !getForbiddens().get(it).get())
.collect(Collectors.toList());
}
//http://jenkins.liteloader.com/job/LiteLoader%201.12.2/lastSuccessfulBuild/artifact/build/libs/liteloader-1.12.2-SNAPSHOT-release.jar
public Map<String, Boolean> getFeatures() {
return Collections.singletonMap(
"has_custom_resolution",
@@ -251,14 +247,14 @@ public class DefaultLauncher extends Launcher {
false);
}
public Map<String, String> getConfigurations() {
protected Map<String, String> getConfigurations() {
return Lang.mapOf(
new Pair<>("${auth_player_name}", authInfo.getUsername()),
new Pair<>("${auth_session}", authInfo.getAuthToken()),
new Pair<>("${auth_access_token}", authInfo.getAuthToken()),
new Pair<>("${auth_uuid}", authInfo.getUserId()),
new Pair<>("${version_name}", Lang.nonNull(options.getVersionName(), version.getId())),
new Pair<>("${profile_name}", Lang.nonNull(options.getProfileName(), "Minecraft")),
new Pair<>("${version_name}", Optional.ofNullable(options.getVersionName()).orElse(version.getId())),
new Pair<>("${profile_name}", Optional.ofNullable(options.getProfileName()).orElse("Minecraft")),
new Pair<>("${version_type}", version.getType().getId()),
new Pair<>("${game_directory}", repository.getRunDirectory(version.getId()).getAbsolutePath()),
new Pair<>("${user_type}", authInfo.getUserType().toString().toLowerCase()),
@@ -275,11 +271,8 @@ public class DefaultLauncher extends Launcher {
decompressNatives();
if (StringUtils.isNotBlank(options.getPrecalledCommand())) {
Process process = Runtime.getRuntime().exec(options.getPrecalledCommand());
if (process.isAlive())
process.waitFor();
}
if (StringUtils.isNotBlank(options.getPrecalledCommand()))
Runtime.getRuntime().exec(options.getPrecalledCommand()).waitFor();
builder.directory(repository.getRunDirectory(version.getId()))
.environment().put("APPDATA", options.getGameDir().getAbsoluteFile().getParent());
@@ -372,7 +365,7 @@ public class DefaultLauncher extends Launcher {
private void startMonitorsWithoutLoggingInfo(ManagedProcess managedProcess, ProcessListener processListener, boolean isDaemon) {
processListener.setProcess(managedProcess);
Thread stdout = Lang.thread(new StreamPump(managedProcess.getProcess().getInputStream(), it -> {
processListener.onLog(it + OperatingSystem.LINE_SEPARATOR, Lang.nonNull(Log4jLevel.guessLevel(it), Log4jLevel.INFO));
processListener.onLog(it + OperatingSystem.LINE_SEPARATOR, Optional.ofNullable(Log4jLevel.guessLevel(it)).orElse(Log4jLevel.INFO));
managedProcess.addLine(it);
}), "stdout-pump", isDaemon);
managedProcess.addRelatedThread(stdout);

View File

@@ -17,11 +17,9 @@
*/
package org.jackhuang.hmcl.launch;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.io.*;
import java.util.Date;
import java.util.Optional;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.BiConsumer;
@@ -55,6 +53,7 @@ final class Log4jHandler extends Thread {
public Log4jHandler(BiConsumer<String, Log4jLevel> callback) {
this.callback = callback;
newLine("<output>");
reader = Lang.invoke(() -> XMLReaderFactory.createXMLReader());
reader.setContentHandler(new Log4jHandlerImpl());
@@ -63,7 +62,6 @@ final class Log4jHandler extends Thread {
@Override
public void run() {
setName("log4j-handler");
newLine("<output>");
try {
reader.parse(new InputSource(inputStream));
@@ -88,17 +86,14 @@ final class Log4jHandler extends Thread {
}).get());
}
public Future<?> newLine(String content) {
public Future<?> newLine(String log) {
return Schedulers.computation().schedule(() -> {
String log = content;
if (!log.trim().startsWith("<"))
log = "<![CDATA[" + log.replace("]]>", "") + "]]>";
outputStream.write((log + OperatingSystem.LINE_SEPARATOR)
byte[] bytes = (log + OperatingSystem.LINE_SEPARATOR)
.replace("log4j:Event", "log4j_Event")
.replace("log4j:Message", "log4j_Message")
.replace("log4j:Throwable", "log4j_Throwable")
.getBytes()
);
.getBytes();
outputStream.write(bytes);
outputStream.flush();
});
}
@@ -155,7 +150,7 @@ final class Log4jHandler extends Thread {
if (readingMessage)
message.append(line).append(OperatingSystem.LINE_SEPARATOR);
else
callback.accept(line, Lang.nonNull(Log4jLevel.guessLevel(line), Log4jLevel.INFO));
callback.accept(line, Optional.ofNullable(Log4jLevel.guessLevel(line)).orElse(Log4jLevel.INFO));
}
}
}

View File

@@ -49,7 +49,7 @@ public final class CurseInstallTask extends Task {
* @param zipFile the CurseForge modpack file.
* @param manifest The manifest content of given CurseForge modpack.
* @param name the new version name
* @see readCurseForgeModpackManifest
* @see CurseManifest#readCurseForgeModpackManifest
*/
public CurseInstallTask(DefaultDependencyManager dependencyManager, File zipFile, CurseManifest manifest, String name) {
this.dependencyManager = dependencyManager;

View File

@@ -23,6 +23,8 @@ import java.io.File;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import org.jackhuang.hmcl.util.CompressingUtils;
import org.jackhuang.hmcl.util.Constants;
import org.jackhuang.hmcl.util.Immutable;
@@ -122,7 +124,7 @@ public final class CurseManifest {
if (manifest == null)
throw new JsonParseException("`manifest.json` not found. Not a valid Curse modpack.");
return new Modpack(manifest.getName(), manifest.getAuthor(), manifest.getVersion(), manifest.getMinecraft().getGameVersion(),
Lang.nonNull(CompressingUtils.readTextZipEntryQuietly(f, "modlist.html"), "No description"), manifest);
Optional.ofNullable(CompressingUtils.readTextZipEntryQuietly(f, "modlist.html")).orElse( "No description"), manifest);
}
public static final String MINECRAFT_MODPACK = "minecraftModpack";

View File

@@ -20,6 +20,7 @@ package org.jackhuang.hmcl.mod;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Optional;
import java.util.Properties;
import org.apache.commons.compress.archivers.zip.ZipFile;
@@ -83,7 +84,7 @@ public final class MultiMCInstanceConfiguration {
showConsoleOnError = Boolean.parseBoolean(p.getProperty("ShowConsoleOnError"));
wrapperCommand = p.getProperty("WrapperCommand");
name = defaultName;
notes = Lang.nonNull(p.getProperty("notes"), "");
notes = Optional.ofNullable(p.getProperty("notes")).orElse("");
}
/**

View File

@@ -19,6 +19,7 @@ package org.jackhuang.hmcl.task;
import java.util.function.Consumer;
import org.jackhuang.hmcl.util.AutoTypingMap;
import org.jackhuang.hmcl.util.ExceptionalConsumer;
/**
*
@@ -26,16 +27,18 @@ import org.jackhuang.hmcl.util.AutoTypingMap;
*/
class SimpleTask extends Task {
private final Consumer<AutoTypingMap<String>> consumer;
private final ExceptionalConsumer<AutoTypingMap<String>, ?> consumer;
private final Scheduler scheduler;
public SimpleTask(Consumer<AutoTypingMap<String>> consumer) {
public SimpleTask(ExceptionalConsumer<AutoTypingMap<String>, ?> consumer) {
this(consumer, Schedulers.defaultScheduler());
}
public SimpleTask(Consumer<AutoTypingMap<String>> consumer, Scheduler scheduler) {
public SimpleTask(ExceptionalConsumer<AutoTypingMap<String>, ?> consumer, Scheduler scheduler) {
this.consumer = consumer;
this.scheduler = scheduler;
setName(consumer.toString());
}
@Override

View File

@@ -29,6 +29,8 @@ import javafx.beans.property.ReadOnlyStringProperty;
import javafx.beans.property.ReadOnlyStringWrapper;
import org.jackhuang.hmcl.util.AutoTypingMap;
import org.jackhuang.hmcl.event.EventManager;
import org.jackhuang.hmcl.util.ExceptionalConsumer;
import org.jackhuang.hmcl.util.ExceptionalRunnable;
import org.jackhuang.hmcl.util.Properties;
/**
@@ -204,11 +206,11 @@ public abstract class Task {
return executor;
}
public final TaskExecutor subscribe(Scheduler scheduler, Consumer<AutoTypingMap<String>> closure) {
public final TaskExecutor subscribe(Scheduler scheduler, ExceptionalConsumer<AutoTypingMap<String>, ?> closure) {
return subscribe(of(closure, scheduler));
}
public final TaskExecutor subscribe(Consumer<AutoTypingMap<String>> closure) {
public final TaskExecutor subscribe(ExceptionalConsumer<AutoTypingMap<String>, ?> closure) {
return subscribe(of(closure));
}
@@ -228,15 +230,15 @@ public abstract class Task {
return new CoupleTask<>(this, b, false);
}
public static Task of(Runnable runnable) {
public static Task of(ExceptionalRunnable<?> runnable) {
return of(s -> runnable.run());
}
public static Task of(Consumer<AutoTypingMap<String>> closure) {
public static Task of(ExceptionalConsumer<AutoTypingMap<String>, ?> closure) {
return of(closure, Schedulers.defaultScheduler());
}
public static Task of(Consumer<AutoTypingMap<String>> closure, Scheduler scheduler) {
public static Task of(ExceptionalConsumer<AutoTypingMap<String>, ?> closure, Scheduler scheduler) {
return new SimpleTask(closure, scheduler);
}

View File

@@ -17,13 +17,13 @@
*/
package org.jackhuang.hmcl.task;
import java.util.EventObject;
import org.jackhuang.hmcl.event.Event;
/**
*
* @author huang
*/
public class TaskEvent extends EventObject {
public class TaskEvent extends Event {
private final Task task;
private final boolean failed;

View File

@@ -0,0 +1,25 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* 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 {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.util;
/**
* @author huangyuhui
*/
public interface ExceptionalConsumer<T, E extends Exception> {
void accept(T t) throws E;
}

View File

@@ -59,7 +59,7 @@ public final class Lang {
*
* @param <T> type of argument.
* @param <R> type of result.
* @param supplier your method.
* @param function your method.
* @return the result of the method to invoke.
*/
public static <T, R, E extends Exception> R invoke(ExceptionalFunction<T, R, E> function, T t) {
@@ -190,19 +190,19 @@ public final class Lang {
return convert(map.get(key), clazz, defaultValue);
}
public static <T> List<T> merge(Collection<T>... collections) {
public static <T> List<T> merge(Collection<T> a, Collection<T> b) {
LinkedList<T> result = new LinkedList<>();
for (Collection<T> collection : collections)
if (collection != null)
result.addAll(collection);
result.addAll(a);
result.addAll(b);
return result;
}
public static <T> T nonNull(T... t) {
for (T a : t)
if (a != null)
return a;
return null;
public static <T> List<T> merge(Collection<T> a, Collection<T> b, Collection<T> c) {
LinkedList<T> result = new LinkedList<>();
result.addAll(a);
result.addAll(b);
result.addAll(c);
return result;
}
public static Thread thread(Runnable runnable) {
@@ -223,10 +223,6 @@ public final class Lang {
return thread;
}
public static <T> T get(Optional<T> optional) {
return optional.isPresent() ? optional.get() : null;
}
public static <T> Iterator<T> asIterator(Enumeration<T> enumeration) {
return new Iterator<T>() {
@Override
@@ -244,4 +240,12 @@ public final class Lang {
public static <T> Iterable<T> asIterable(Enumeration<T> enumeration) {
return () -> asIterator(enumeration);
}
public static int parseInt(String string, int defaultValue) {
try {
return Integer.parseInt(string);
} catch (NumberFormatException e) {
return defaultValue;
}
}
}

View File

@@ -65,12 +65,12 @@ public enum OperatingSystem {
/**
* The total memory/MB this computer have.
*/
public static final long TOTAL_MEMORY;
public static final int TOTAL_MEMORY;
/**
* The suggested memory size/MB for Minecraft to allocate.
*/
public static final long SUGGESTED_MEMORY;
public static final int SUGGESTED_MEMORY;
public static final String PATH_SEPARATOR = File.pathSeparator;
public static final String FILE_SEPARATOR = File.separator;
@@ -104,11 +104,11 @@ public enum OperatingSystem {
Object bytes = ReflectionHelper.call(ManagementFactory.getOperatingSystemMXBean(), "getTotalPhysicalMemorySize");
if (bytes instanceof Long)
TOTAL_MEMORY = ((Long) bytes) / 1024 / 1024;
TOTAL_MEMORY = (int) (((Long) bytes) / 1024 / 1024);
else
TOTAL_MEMORY = 1024;
SUGGESTED_MEMORY = Math.round(1.0 * TOTAL_MEMORY / 4.0 / 128.0) * 128;
SUGGESTED_MEMORY = (int) (Math.round(1.0 * TOTAL_MEMORY / 4.0 / 128.0) * 128);
String arch = System.getProperty("sun.arch.data.model");
if (arch == null)

View File

@@ -29,7 +29,7 @@ public abstract class VersionNumber implements Comparable<VersionNumber> {
/**
* @throws IllegalArgumentException if there are some characters excluding digits and dots.
* @param version
* @param version version string in form x.x.x
* @return the int version number
*/
public static IntVersionNumber asIntVersionNumber(String version) {