This commit is contained in:
huangyuhui
2017-02-15 20:28:01 +08:00
parent 55b36e734d
commit f836afa2ea
439 changed files with 1943 additions and 21556 deletions

41
HMCLCore/build.gradle Normal file
View File

@@ -0,0 +1,41 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 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/}.
*/
buildscript {
repositories {
mavenCentral();
dependencies {
classpath 'me.tatarka:gradle-retrolambda:3.1.0'
}
}
}
apply plugin: 'me.tatarka.retrolambda'
if (System.getenv("BUILD_NUMBER") != null)
version = System.getenv("BUILD_NUMBER")
dependencies {
compile project(":HMCLAPI")
compile group: "org.commonjava.googlecode.markdown4j", name: "markdown4j", version: "2.2-cj-1.0"
}
retrolambda {
javaVersion = JavaVersion.VERSION_1_7
}

View File

@@ -0,0 +1,41 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 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.core;
/**
* Thrown if we have some problem generating launch command.
* @author huangyuhui
*/
public class GameException extends Exception {
public GameException() {
}
public GameException(String message) {
super(message);
}
public GameException(String message, Throwable cause) {
super(message, cause);
}
public GameException(Throwable cause) {
super(cause);
}
}

View File

@@ -0,0 +1,81 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 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.core;
import java.io.File;
import java.io.IOException;
import org.jackhuang.hmcl.util.sys.FileUtils;
import org.jackhuang.hmcl.util.sys.OS;
/**
*
* @author huang
*/
public final class MCUtils {
public static File getWorkingDirectory(String baseName) {
String userhome = System.getProperty("user.home", ".");
File file;
switch (OS.os()) {
case LINUX:
file = new File(userhome, '.' + baseName + '/');
break;
case WINDOWS:
String appdata = System.getenv("APPDATA");
if (appdata != null)
file = new File(appdata, "." + baseName + '/');
else
file = new File(userhome, '.' + baseName + '/');
break;
case OSX:
file = new File(userhome, "Library/Application Support/" + baseName);
break;
default:
file = new File(userhome, baseName + '/');
}
return file;
}
public static File getLocation() {
return getWorkingDirectory("minecraft");
}
public static String minecraft() {
if (OS.os() == OS.OSX)
return "minecraft";
return ".minecraft";
}
public static File getInitGameDir() {
File gameDir = new File(MCUtils.minecraft());
if (!gameDir.exists()) {
File newFile = MCUtils.getLocation();
if (newFile.exists())
gameDir = newFile;
}
return gameDir;
}
public static final String PROFILE = "{\"selectedProfile\": \"(Default)\",\"profiles\": {\"(Default)\": {\"name\": \"(Default)\"}},\"clientToken\": \"88888888-8888-8888-8888-888888888888\"}";
public static void tryWriteProfile(File gameDir) throws IOException {
File file = new File(gameDir, "launcher_profiles.json");
if (!file.exists())
FileUtils.write(file, PROFILE);
}
}

View File

@@ -0,0 +1,40 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 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.core;
/**
*
* @author huang
*/
public class RuntimeGameException extends RuntimeException {
public RuntimeGameException() {
}
public RuntimeGameException(String message) {
super(message);
}
public RuntimeGameException(String message, Throwable cause) {
super(message, cause);
}
public RuntimeGameException(Throwable cause) {
super(cause);
}
}

View File

@@ -0,0 +1,55 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 huangyuhui
*
* 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.core.asset;
import com.google.gson.annotations.SerializedName;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
/**
*
* @author huangyuhui
*/
public class AssetsIndex {
public static final String DEFAULT_ASSET_NAME = "legacy";
@SerializedName("objects")
public Map<String, AssetsObject> objects;
@SerializedName("virtual")
public boolean virtual;
public AssetsIndex() {
this.objects = new LinkedHashMap<>();
}
public Map<String, AssetsObject> getFileMap() {
return this.objects;
}
public Set<AssetsObject> getUniqueObjects() {
return new HashSet<>(this.objects.values());
}
public boolean isVirtual() {
return this.virtual;
}
}

View File

@@ -0,0 +1,103 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 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.core.asset;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import org.jackhuang.hmcl.util.C;
import org.jackhuang.hmcl.api.HMCLog;
import org.jackhuang.hmcl.core.service.IMinecraftAssetService;
import org.jackhuang.hmcl.util.task.Task;
import org.jackhuang.hmcl.util.sys.FileUtils;
import org.jackhuang.hmcl.util.StrUtils;
import org.jackhuang.hmcl.core.download.IDownloadProvider;
import org.jackhuang.hmcl.core.version.MinecraftVersion;
import org.jackhuang.hmcl.api.VersionNumber;
import org.jackhuang.hmcl.util.task.TaskInfo;
/**
*
* @author huangyuhui
*/
public class AssetsMojangLoader extends IAssetsHandler {
public AssetsMojangLoader(String name) {
super(name);
}
@Override
public Task getList(final MinecraftVersion mv, final IMinecraftAssetService mp) {
Objects.requireNonNull(mv);
String assetsId = mv.getAssetsIndex().getId();
HMCLog.log("Gathering asset index: " + assetsId);
File f = mp.getIndexFile(assetsId);
return new TaskInfo("Gather asset index") {
@Override
public Collection<Task> getDependTasks() {
if (!f.exists())
return Arrays.asList(mp.downloadMinecraftAssetsIndex(mv.getAssetsIndex()));
else
return null;
}
@Override
public void executeTask(boolean areDependTasksSucceeded) throws Throwable {
if (!areDependTasksSucceeded)
throw new IllegalStateException("Failed to get asset index");
String result = FileUtils.read(f);
if (StrUtils.isBlank(result))
throw new IllegalStateException("Index json is empty, please redownload it!");
AssetsIndex o = C.GSON.fromJson(result, AssetsIndex.class);
assetsDownloadURLs = new ArrayList<>();
assetsLocalNames = new ArrayList<>();
assetsObjects = new ArrayList<>();
HashSet<String> loadedHashes = new HashSet<>();
int pgs = 0;
if (o != null && o.getFileMap() != null)
for (Map.Entry<String, AssetsObject> e : o.getFileMap().entrySet()) {
if (loadedHashes.contains(e.getValue().getHash()))
continue;
loadedHashes.add(e.getValue().getHash());
assetsObjects.add(e.getValue());
assetsDownloadURLs.add(e.getValue().getLocation());
assetsLocalNames.add(mp.getAssetObject(assetsId, e.getValue()));
if (ppl != null)
ppl.setProgress(this, ++pgs, o.getFileMap().size());
}
}
};
}
@Override
public Task getDownloadTask(IDownloadProvider sourceType) {
return new AssetsTask(sourceType.getAssetsDownloadURL());
}
@Override
public boolean isVersionAllowed(String formattedVersion) {
VersionNumber ur = VersionNumber.check(formattedVersion);
if (ur == null)
return false;
return VersionNumber.check("1.6.0").compareTo(ur) <= 0;
}
}

View File

@@ -0,0 +1,72 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 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.core.asset;
/**
*
* @author huangyuhui
*/
public class AssetsObject {
private String hash;
private long size;
public void setHash(String hash) {
this.hash = hash;
}
public void setSize(long size) {
this.size = size;
}
public AssetsObject(String hash, long size) {
this.hash = hash;
this.size = size;
}
public String getHash() {
return this.hash;
}
public long getSize() {
return this.size;
}
public String getLocation() {
return hash.substring(0, 2) + "/" + hash;
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
AssetsObject that = (AssetsObject) o;
if (this.size != that.size)
return false;
return this.hash.equals(that.hash);
}
@Override
public int hashCode() {
int result = this.hash.hashCode();
result = 31 * result + (int) (this.size ^ this.size >>> 32);
return result;
}
}

View File

@@ -0,0 +1,142 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 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.core.asset;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.jackhuang.hmcl.util.C;
import org.jackhuang.hmcl.api.HMCLog;
import org.jackhuang.hmcl.core.service.IMinecraftAssetService;
import org.jackhuang.hmcl.core.download.IDownloadProvider;
import org.jackhuang.hmcl.core.version.MinecraftVersion;
import org.jackhuang.hmcl.util.task.Task;
import org.jackhuang.hmcl.util.net.FileDownloadTask;
import org.jackhuang.hmcl.util.code.DigestUtils;
import org.jackhuang.hmcl.util.sys.FileUtils;
import org.jackhuang.hmcl.util.sys.IOUtils;
import org.jackhuang.hmcl.util.task.TaskInfo;
/**
* Assets
*
* @author huangyuhui
*/
public abstract class IAssetsHandler {
protected ArrayList<String> assetsDownloadURLs;
protected ArrayList<File> assetsLocalNames;
protected final String name;
protected List<AssetsObject> assetsObjects;
public IAssetsHandler(String name) {
this.name = name;
}
public static final IAssetsHandler ASSETS_HANDLER;
static {
ASSETS_HANDLER = new AssetsMojangLoader(C.i18n("assets.list.1_7_3_after"));
}
/**
* interface name
*
* @return
*/
public String getName() {
return name;
}
/**
* All the files assets needed
*
* @param mv The version that needs assets
* @param mp Asset Service
* @return just run it!
*/
public abstract Task getList(MinecraftVersion mv, IMinecraftAssetService mp);
/**
* Will be invoked when the user invoked "Download all assets".
*
* @param sourceType Download Source
*
* @return Download File Task
*/
public abstract Task getDownloadTask(IDownloadProvider sourceType);
public abstract boolean isVersionAllowed(String formattedVersion);
protected class AssetsTask extends TaskInfo {
ArrayList<Task> al;
String u;
public AssetsTask(String url) {
super(C.i18n("assets.download"));
this.u = url;
}
@Override
public void executeTask(boolean areDependTasksSucceeded) {
if (assetsDownloadURLs == null || assetsLocalNames == null || assetsObjects == null)
throw new IllegalStateException(C.i18n("assets.not_refreshed"));
int max = assetsDownloadURLs.size();
al = new ArrayList<>();
int hasDownloaded = 0;
for (int i = 0; i < max; i++) {
String mark = assetsDownloadURLs.get(i);
String url = u + mark;
File location = assetsLocalNames.get(i);
if (!FileUtils.makeDirectory(location.getParentFile()))
HMCLog.warn("Failed to make directories: " + location.getParent());
if (location.isDirectory())
continue;
boolean need = true;
try {
if (location.exists()) {
FileInputStream fis = FileUtils.openInputStream(location);
String sha = DigestUtils.sha1Hex(IOUtils.toByteArray(fis));
IOUtils.closeQuietly(fis);
if (assetsObjects.get(i).getHash().equals(sha)) {
++hasDownloaded;
HMCLog.log("File " + assetsLocalNames.get(i) + " has been downloaded successfully, skipped downloading.");
if (ppl != null)
ppl.setProgress(this, hasDownloaded, max);
continue;
}
}
} catch (IOException e) {
HMCLog.warn("Failed to get hash: " + location, e);
need = !location.exists();
}
if (need)
al.add(new FileDownloadTask(url, location).setTag(assetsObjects.get(i).getHash()));
}
}
@Override
public Collection<Task> getAfterTasks() {
return al;
}
}
}

View File

@@ -0,0 +1,224 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 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.core.asset;
import com.google.gson.JsonSyntaxException;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.Map;
import org.jackhuang.hmcl.core.GameException;
import org.jackhuang.hmcl.core.launch.IAssetProvider;
import org.jackhuang.hmcl.util.C;
import org.jackhuang.hmcl.core.service.IMinecraftAssetService;
import org.jackhuang.hmcl.core.service.IMinecraftService;
import org.jackhuang.hmcl.core.version.AssetIndexDownloadInfo;
import org.jackhuang.hmcl.core.version.MinecraftVersion;
import org.jackhuang.hmcl.util.MessageBox;
import org.jackhuang.hmcl.api.HMCLog;
import org.jackhuang.hmcl.util.task.Task;
import org.jackhuang.hmcl.util.task.TaskWindow;
import org.jackhuang.hmcl.util.net.FileDownloadTask;
import org.jackhuang.hmcl.util.sys.FileUtils;
import org.jackhuang.hmcl.util.sys.IOUtils;
import org.jackhuang.hmcl.util.task.TaskInfo;
/**
*
* @author huangyuhui
*/
public class MinecraftAssetService extends IMinecraftAssetService {
public MinecraftAssetService(IMinecraftService service) {
super(service);
}
@Override
public Task downloadAssets(final String mcVersion) throws GameException {
return downloadAssets(service.version().getVersionById(mcVersion));
}
public Task downloadAssets(final MinecraftVersion mv) throws GameException {
if (mv == null)
return null;
return IAssetsHandler.ASSETS_HANDLER.getList(mv.resolve(service.version()), service.asset()).with(IAssetsHandler.ASSETS_HANDLER.getDownloadTask(service.getDownloadType().getProvider()));
}
@Override
public boolean refreshAssetsIndex(String id) throws GameException {
MinecraftVersion mv = service.version().getVersionById(id);
if (mv == null)
return false;
return downloadMinecraftAssetsIndexAsync(mv.resolve(service.version()).getAssetsIndex());
}
@Override
public Task downloadMinecraftAssetsIndex(AssetIndexDownloadInfo assetIndex) {
File assetsLocation = getAssets(assetIndex.getId());
if (!FileUtils.makeDirectory(assetsLocation))
HMCLog.warn("Failed to make directories: " + assetsLocation);
File assetsIndex = getIndexFile(assetIndex.getId());
File renamed = null;
if (assetsIndex.exists()) {
renamed = new File(assetsLocation, "indexes/" + assetIndex.getId() + "-renamed.json");
if (assetsIndex.renameTo(renamed))
HMCLog.warn("Failed to rename " + assetsIndex + " to " + renamed);
}
File renamedFinal = renamed;
return new TaskInfo("Download Asset Index") {
@Override
public Collection<Task> getDependTasks() {
return Arrays.asList(new FileDownloadTask(assetIndex.getUrl(service.getDownloadType()), IOUtils.tryGetCanonicalFile(assetsIndex), assetIndex.sha1).setTag(assetIndex.getId() + ".json"));
}
@Override
public void executeTask(boolean areDependTasksSucceeded) throws Throwable {
if (areDependTasksSucceeded) {
if (renamedFinal != null && !renamedFinal.delete())
HMCLog.warn("Failed to delete " + renamedFinal + ", maybe you should do it.");
} else if (renamedFinal != null && !renamedFinal.renameTo(assetsIndex))
HMCLog.warn("Failed to rename " + renamedFinal + " to " + assetsIndex);
}
};
}
@Override
public boolean downloadMinecraftAssetsIndexAsync(AssetIndexDownloadInfo assetIndex) {
File assetsDir = getAssets(assetIndex.getId());
if (!FileUtils.makeDirectory(assetsDir))
HMCLog.warn("Failed to make directories: " + assetsDir);
File assetsIndex = getIndexFile(assetIndex.getId());
File renamed = null;
if (assetsIndex.exists()) {
renamed = new File(assetsDir, "indexes/" + assetIndex.getId() + "-renamed.json");
if (assetsIndex.renameTo(renamed))
HMCLog.warn("Failed to rename " + assetsIndex + " to " + renamed);
}
if (TaskWindow.factory()
.append(new FileDownloadTask(assetIndex.getUrl(service.getDownloadType()), IOUtils.tryGetCanonicalFile(assetsIndex), assetIndex.sha1).setTag(assetIndex.getId() + ".json"))
.execute()) {
if (renamed != null && !renamed.delete())
HMCLog.warn("Failed to delete " + renamed + ", maybe you should do it.");
return true;
}
if (renamed != null && !renamed.renameTo(assetsIndex))
HMCLog.warn("Failed to rename " + renamed + " to " + assetsIndex);
return false;
}
@Override
public File getAssets(String assetId) {
return new File(service.baseDirectory(), "assets");
}
@Override
public File getIndexFile(String assetId) {
return new File(getAssets(assetId), "indexes/" + assetId + ".json");
}
@Override
public File getAssetObject(String assetId, String name) throws IOException {
try {
AssetsIndex index = (AssetsIndex) C.GSON.fromJson(FileUtils.read(getIndexFile(assetId), "UTF-8"), AssetsIndex.class);
return getAssetObject(assetId, (AssetsObject) index.getFileMap().get(name));
} catch (JsonSyntaxException e) {
throw new IOException("Assets file format malformed.", e);
}
}
protected boolean checkAssetsExistance(AssetIndexDownloadInfo assetIndex) {
String assetId = assetIndex.getId();
File indexFile = getIndexFile(assetId);
File assetDir = getAssets(assetId);
if (!getAssets(assetId).exists() || !indexFile.isFile())
return false;
try {
String assetIndexContent = FileUtils.read(indexFile, "UTF-8");
AssetsIndex index = (AssetsIndex) C.GSON.fromJson(assetIndexContent, AssetsIndex.class);
if (index == null)
return false;
for (Map.Entry<String, AssetsObject> entry : index.getFileMap().entrySet())
if (!assetObjectPath(assetDir, (AssetsObject) entry.getValue()).exists())
return false;
return true;
} catch (IOException | JsonSyntaxException e) {
return false;
}
}
protected File reconstructAssets(AssetIndexDownloadInfo assetIndex) {
File assetsDir = getAssets(assetIndex.getId());
String assetVersion = assetIndex.getId();
File indexFile = getIndexFile(assetVersion);
File virtualRoot = new File(new File(assetsDir, "virtual"), assetVersion);
if (!indexFile.isFile()) {
HMCLog.warn("No assets index file " + virtualRoot + "; can't reconstruct assets");
return assetsDir;
}
try {
String assetIndexContent = FileUtils.read(indexFile, "UTF-8");
AssetsIndex index = (AssetsIndex) C.GSON.fromJson(assetIndexContent, AssetsIndex.class);
if (index == null)
return assetsDir;
if (index.isVirtual()) {
int cnt = 0;
HMCLog.log("Reconstructing virtual assets folder at " + virtualRoot);
int tot = index.getFileMap().entrySet().size();
for (Map.Entry<String, AssetsObject> entry : index.getFileMap().entrySet()) {
File target = new File(virtualRoot, (String) entry.getKey());
File original = assetObjectPath(assetsDir, (AssetsObject) entry.getValue());
if (original.exists()) {
cnt++;
if (!target.isFile())
FileUtils.copyFile(original, target);
}
}
// If the scale new format existent file is lower then 0.1, use the old format.
if (cnt * 10 < tot)
return assetsDir;
}
} catch (IOException | JsonSyntaxException e) {
HMCLog.warn("Failed to create virutal assets.", e);
}
return virtualRoot;
}
@Override
public File getAssetObject(String assetId, AssetsObject object) {
return assetObjectPath(getAssets(assetId), object);
}
public File assetObjectPath(File assetDir, AssetsObject object) {
return new File(assetDir, "objects/" + object.getLocation());
}
public final IAssetProvider ASSET_PROVIDER_IMPL = (t, allow) -> {
if (allow && !checkAssetsExistance(t.getAssetsIndex()))
if (MessageBox.show(C.i18n("assets.no_assets"), MessageBox.YES_NO_OPTION) == MessageBox.YES_OPTION)
TaskWindow.factory().execute(downloadAssets(t));
return reconstructAssets(t.getAssetsIndex()).getAbsolutePath();
};
}

View File

@@ -0,0 +1,105 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 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.core.auth;
import org.jackhuang.hmcl.api.auth.IAuthenticator;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.jackhuang.hmcl.api.PluginManager;
/**
* Login interface
*
* @author huangyuhui
*/
public abstract class AbstractAuthenticator implements IAuthenticator {
public static final List<IAuthenticator> LOGINS = new ArrayList<>();
static {
PluginManager.fireRegisterAuthenticators(LOGINS::add);
}
protected String clientToken, username, password;
public AbstractAuthenticator(String clientToken) {
this.clientToken = clientToken;
}
public String getClientToken() {
return clientToken;
}
/**
* Has password?
*
* @return has password?
*/
@Override
public boolean hasPassword() {
return true;
}
@Override
public boolean isLoggedIn() {
return false;
}
@Override
public void setRememberMe(boolean is) {
}
@Override
public Map<?, ?> onSaveSettings() {
HashMap<String, String> m = new HashMap<>();
m.put("IAuthenticator_UserName", username);
return m;
}
@Override
public void onLoadSettings(Map<?, ?> m) {
if (m == null)
return;
Object o = m.get("IAuthenticator_UserName");
username = o instanceof String ? (String) o : "";
}
@Override
public String getUserName() {
return username;
}
@Override
public void setUserName(String s) {
username = s;
}
@Override
public String getPassword() {
return password;
}
@Override
public void setPassword(String password) {
this.password = password;
}
}

View File

@@ -0,0 +1,107 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 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.core.auth;
import org.jackhuang.hmcl.api.auth.UserProfileProvider;
import org.jackhuang.hmcl.api.auth.LoginInfo;
import org.jackhuang.hmcl.api.auth.AuthenticationException;
import java.util.HashMap;
import java.util.Map;
import org.jackhuang.hmcl.util.C;
import org.jackhuang.hmcl.util.StrUtils;
import org.jackhuang.hmcl.util.code.DigestUtils;
/**
*
* @author huangyuhui
*/
public final class OfflineAuthenticator extends AbstractAuthenticator {
Map uuidMap = new HashMap<>();
public OfflineAuthenticator(String clientToken) {
super(clientToken);
}
@Override
public void onLoadSettings(Map m) {
super.onLoadSettings(m);
if (m == null)
return;
Object o = m.get("uuidMap");
if (o != null && o instanceof Map)
uuidMap = (Map<?, ?>) o;
}
@Override
public Map onSaveSettings() {
Map m = super.onSaveSettings();
m.put("uuidMap", uuidMap);
return m;
}
@Override
public UserProfileProvider login(LoginInfo info) throws AuthenticationException {
if (StrUtils.isBlank(info.username))
throw new AuthenticationException(C.i18n("login.no_Player007"));
String uuid = getUUIDFromUserName(info.username);
if (uuidMap != null && uuidMap.containsKey(info.username) && uuidMap.get(info.username) instanceof String)
uuid = (String) uuidMap.get(info.username);
else {
if (uuidMap == null)
uuidMap = new HashMap<>();
uuidMap.put(info.username, uuid);
}
return new UserProfileProvider()
.setUserName(info.username)
.setSession(uuid)
.setUserId(uuid)
.setAccessToken(uuid)
.setUserType("Legacy")
.setClientIdentifier(clientToken);
}
public static String getUUIDFromUserName(String str) {
return DigestUtils.md5Hex(str);
}
@Override
public String id() {
return "offline";
}
@Override
public String getName() {
return C.i18n("login.methods.offline");
}
@Override
public boolean hasPassword() {
return false;
}
@Override
public UserProfileProvider loginBySettings() {
return null;
}
@Override
public void logOut() {
}
}

View File

@@ -0,0 +1,146 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 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.core.auth;
import org.jackhuang.hmcl.api.auth.UserProfileProvider;
import org.jackhuang.hmcl.api.auth.LoginInfo;
import org.jackhuang.hmcl.api.auth.AuthenticationException;
import com.google.gson.GsonBuilder;
import java.net.Proxy;
import java.util.Map;
import javax.swing.JOptionPane;
import org.jackhuang.hmcl.util.C;
import org.jackhuang.hmcl.util.ArrayUtils;
import org.jackhuang.hmcl.core.auth.yggdrasil.GameProfile;
import org.jackhuang.hmcl.core.auth.yggdrasil.UUIDTypeAdapter;
import org.jackhuang.hmcl.core.auth.yggdrasil.PropertyMap;
import org.jackhuang.hmcl.core.auth.yggdrasil.YggdrasilAuthentication;
import org.jackhuang.hmcl.util.ui.SwingUtils;
/**
*
* @author huangyuhui
*/
public final class YggdrasilAuthenticator extends AbstractAuthenticator {
YggdrasilAuthentication ua;
public YggdrasilAuthenticator(String clientToken) {
super(clientToken);
ua = new YggdrasilAuthentication(Proxy.NO_PROXY, clientToken);
}
@Override
public UserProfileProvider login(LoginInfo info) throws AuthenticationException {
UserProfileProvider result = new UserProfileProvider();
if (ua.canPlayOnline()) {
result.setUserName(info.username)
.setUserId(UUIDTypeAdapter.fromUUID(ua.getSelectedProfile().id));
} else {
String usr = info.username;
if (info.username == null || !info.username.contains("@"))
throw new AuthenticationException(C.i18n("login.not_email"));
String pwd = info.password;
if (!ua.isLoggedIn())
ua.setPassword(pwd);
ua.setUserName(usr);
ua.logIn();
if (!ua.isLoggedIn())
throw new AuthenticationException(C.i18n("login.wrong_password"));
GameProfile selectedProfile = ua.getSelectedProfile();
GameProfile[] profiles = ua.getAvailableProfiles();
String username;
if (selectedProfile == null)
if (ArrayUtils.isNotEmpty(profiles)) {
String[] names = new String[profiles.length];
for (int i = 0; i < profiles.length; i++)
names[i] = profiles[i].name;
int sel = SwingUtils.select(names, C.i18n("login.choose_charactor"));
if (sel == -1)
throw new AuthenticationException("No selection");
selectedProfile = profiles[sel];
username = names[sel];
} else
username = JOptionPane.showInputDialog(C.i18n("login.no_charactor"));
else
username = selectedProfile.name;
if (username == null)
throw new AuthenticationException("No player");
result.setUserName(username)
.setUserId(selectedProfile == null ? OfflineAuthenticator.getUUIDFromUserName(username) : UUIDTypeAdapter.fromUUID(selectedProfile.id));
}
return result.setUserType("mojang")
.setUserProperties(new GsonBuilder().registerTypeAdapter(PropertyMap.class, new PropertyMap.LegacySerializer()).create().toJson(ua.getUserProperties()))
.setUserPropertyMap(new GsonBuilder().registerTypeAdapter(PropertyMap.class, new PropertyMap.Serializer()).create().toJson(ua.getUserProperties()))
.setAccessToken(ua.getAuthenticatedToken())
.setSession(ua.getAuthenticatedToken())
.setClientIdentifier(clientToken);
}
@Override
public boolean isLoggedIn() {
return ua.isLoggedIn();
}
@Override
public String id() {
return "yggdrasil";
}
@Override
public String getName() {
return C.i18n("login.methods.yggdrasil");
}
@Override
public Map onSaveSettings() {
Map m = ua.saveForStorage();
m.putAll(super.onSaveSettings());
return m;
}
@Override
public void onLoadSettings(Map settings) {
super.onLoadSettings(settings);
if (settings == null)
return;
ua.loadFromStorage(settings);
}
@Override
public UserProfileProvider loginBySettings() throws AuthenticationException {
UserProfileProvider result = new UserProfileProvider();
ua.logIn();
if (!ua.isLoggedIn())
throw new AuthenticationException(C.i18n("login.wrong_password"));
GameProfile profile = ua.getSelectedProfile();
result.setUserName(profile.name);
result.setUserId(profile.id.toString());
result.setUserProperties(new GsonBuilder().registerTypeAdapter(PropertyMap.class, new PropertyMap.LegacySerializer()).create().toJson(ua.getUserProperties()));
result.setUserPropertyMap(new GsonBuilder().registerTypeAdapter(PropertyMap.class, new PropertyMap.Serializer()).create().toJson(ua.getUserProperties()));
result.setAccessToken(ua.getAuthenticatedToken());
result.setSession(ua.getAuthenticatedToken());
return result;
}
@Override
public void logOut() {
ua.logOut();
}
}

View File

@@ -0,0 +1,77 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 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.core.auth.yggdrasil;
import java.util.HashMap;
public class AuthenticationRequest {
private HashMap<String, Object> agent;
private String username, password, clientToken;
private boolean requestUser = true;
public HashMap<String, Object> getAgent() {
return agent;
}
public void setAgent(HashMap<String, Object> agent) {
this.agent = agent;
}
public String getUserName() {
return username;
}
public void setUserName(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getClientToken() {
return clientToken;
}
public void setClientToken(String clientToken) {
this.clientToken = clientToken;
}
public boolean isRequestUser() {
return requestUser;
}
public void setRequestUser(boolean requestUser) {
this.requestUser = requestUser;
}
public AuthenticationRequest(String username, String password, String clientToken) {
agent = new HashMap<>();
agent.put("name", "Minecraft");
agent.put("version", 1);
this.username = username;
this.password = password;
this.clientToken = clientToken;
}
}

View File

@@ -0,0 +1,99 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 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.core.auth.yggdrasil;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import java.lang.reflect.Type;
import java.util.Objects;
import java.util.UUID;
import org.jackhuang.hmcl.util.StrUtils;
public class GameProfile {
public final UUID id;
public final String name;
public final PropertyMap properties = new PropertyMap();
private boolean legacy;
public GameProfile(UUID id, String name) {
if (id == null && StrUtils.isBlank(name))
throw new IllegalArgumentException("Name and ID cannot both be blank");
this.id = id;
this.name = name;
}
public boolean isComplete() {
return id != null && StrUtils.isNotBlank(name);
}
public boolean isLegacy() {
return legacy;
}
@Override
public int hashCode() {
int hash = 7;
hash = 29 * hash + Objects.hashCode(this.id);
hash = 29 * hash + Objects.hashCode(this.name);
return hash;
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
GameProfile that = (GameProfile) o;
if (id != null ? !id.equals(that.id) : that.id != null)
return false;
return name != null ? name.equals(that.name) : that.name == null;
}
public static class GameProfileSerializer implements JsonSerializer<GameProfile>, JsonDeserializer<GameProfile> {
@Override
public GameProfile deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
if (!(json instanceof JsonObject))
throw new JsonParseException("The json element is not a JsonObject.");
JsonObject object = (JsonObject) json;
UUID id = object.has("id") ? (UUID) context.deserialize(object.get("id"), UUID.class) : null;
String name = object.has("name") ? object.getAsJsonPrimitive("name").getAsString() : null;
return new GameProfile(id, name);
}
@Override
public JsonElement serialize(GameProfile src, Type typeOfSrc, JsonSerializationContext context) {
JsonObject result = new JsonObject();
if (src.id != null)
result.add("id", context.serialize(src.id));
if (src.name != null)
result.addProperty("name", src.name);
return result;
}
}
}

View File

@@ -0,0 +1,28 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 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.core.auth.yggdrasil;
public class Property {
public final String name, value;
public Property(String name, String value) {
this.name = name;
this.value = value;
}
}

View File

@@ -0,0 +1,116 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 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.core.auth.yggdrasil;
import com.google.gson.JsonArray;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.jackhuang.hmcl.api.HMCLog;
public class PropertyMap extends HashMap<String, Property> {
private static final long serialVersionUID = 362498820763181265L;
public List<Map<String, String>> list() {
List<Map<String, String>> properties = new ArrayList<>();
for (Property profileProperty : values()) {
Map<String, String> property = new HashMap<>();
property.put("name", profileProperty.name);
property.put("value", profileProperty.value);
properties.add(property);
}
return properties;
}
public void fromList(List<Map<String, String>> list) {
try {
for (Map<String, String> propertyMap : list) {
String name = propertyMap.get("name");
String value = propertyMap.get("value");
put(name, new Property(name, value));
}
} catch (Throwable t) {
HMCLog.warn("Failed to deserialize properties", t);
}
}
public static class Serializer implements JsonSerializer<PropertyMap>, JsonDeserializer<PropertyMap> {
@Override
public PropertyMap deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
PropertyMap result = new PropertyMap();
if ((json instanceof JsonObject)) {
JsonObject object = (JsonObject) json;
for (Map.Entry<String, JsonElement> entry : object.entrySet())
if (entry.getValue() instanceof JsonArray)
for (JsonElement element : (JsonArray) entry.getValue())
result.put(entry.getKey(),
new Property((String) entry.getKey(), element.getAsString()));
} else if ((json instanceof JsonArray))
for (JsonElement element : (JsonArray) json)
if ((element instanceof JsonObject)) {
JsonObject object = (JsonObject) element;
String name = object.getAsJsonPrimitive("name").getAsString();
String value = object.getAsJsonPrimitive("value").getAsString();
result.put(name, new Property(name, value));
}
return result;
}
@Override
public JsonElement serialize(PropertyMap src, Type typeOfSrc, JsonSerializationContext context) {
JsonArray result = new JsonArray();
for (Property property : src.values()) {
JsonObject object = new JsonObject();
object.addProperty("name", property.name);
object.addProperty("value", property.value);
result.add(object);
}
return result;
}
}
public static class LegacySerializer
implements JsonSerializer<PropertyMap> {
@Override
public JsonElement serialize(PropertyMap src, Type typeOfSrc, JsonSerializationContext context) {
JsonObject result = new JsonObject();
for (PropertyMap.Entry<String, Property> entry : src.entrySet()) {
JsonArray values = new JsonArray();
values.add(new JsonPrimitive(entry.getValue().value));
result.add(entry.getKey(), values);
}
return result;
}
}
}

View File

@@ -0,0 +1,64 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 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.core.auth.yggdrasil;
public class RefreshRequest {
private String clientToken;
private String accessToken;
private GameProfile selectedProfile;
private boolean requestUser = true;
public RefreshRequest(String accessToken, String clientToken) {
this.clientToken = clientToken;
this.accessToken = accessToken;
}
public String getClientToken() {
return clientToken;
}
public void setClientToken(String clientToken) {
this.clientToken = clientToken;
}
public String getAccessToken() {
return accessToken;
}
public void setAccessToken(String accessToken) {
this.accessToken = accessToken;
}
public GameProfile getSelectedProfile() {
return selectedProfile;
}
public void setSelectedProfile(GameProfile selectedProfile) {
this.selectedProfile = selectedProfile;
}
public boolean isRequestUser() {
return requestUser;
}
public void setRequestUser(boolean requestUser) {
this.requestUser = requestUser;
}
}

View File

@@ -0,0 +1,40 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 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.core.auth.yggdrasil;
import com.google.gson.annotations.SerializedName;
public class Response {
@SerializedName("accessToken")
public String accessToken;
@SerializedName("clientToken")
public String clientToken;
@SerializedName("selectedProfile")
public GameProfile selectedProfile;
@SerializedName("availableProfiles")
public GameProfile[] availableProfiles;
@SerializedName("user")
public User user;
@SerializedName("error")
public String error;
@SerializedName("errorMessage")
public String errorMessage;
@SerializedName("cause")
public String cause;
}

View File

@@ -0,0 +1,45 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 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.core.auth.yggdrasil;
import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import java.io.IOException;
import java.util.UUID;
public class UUIDTypeAdapter extends TypeAdapter<UUID> {
@Override
public void write(JsonWriter out, UUID value) throws IOException {
out.value(fromUUID(value));
}
@Override
public UUID read(JsonReader in) throws IOException {
return fromString(in.nextString());
}
public static String fromUUID(UUID value) {
return value.toString().replace("-", "");
}
public static UUID fromString(String input) {
return UUID.fromString(input.replaceFirst("(\\w{8})(\\w{4})(\\w{4})(\\w{4})(\\w{12})", "$1-$2-$3-$4-$5"));
}
}

View File

@@ -0,0 +1,44 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 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.core.auth.yggdrasil;
public class User {
private String id;
private PropertyMap properties;
public User() {
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public PropertyMap getProperties() {
return properties;
}
public void setProperties(PropertyMap properties) {
this.properties = properties;
}
}

View File

@@ -0,0 +1,55 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 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.core.auth.yggdrasil;
import java.util.HashMap;
import java.util.Map;
/**
*
* @author huang
*/
public enum UserType {
LEGACY("legacy"), MOJANG("mojang");
private static final Map<String, UserType> BY_NAME;
private final String name;
private UserType(String name) {
this.name = name;
}
public static UserType byName(String name) {
return (UserType) BY_NAME.get(name.toLowerCase());
}
public static UserType byLegacy(boolean isLegacy) {
return isLegacy ? LEGACY : MOJANG;
}
public String getName() {
return this.name;
}
static {
BY_NAME = new HashMap();
for (UserType type : values())
BY_NAME.put(type.name, type);
}
}

View File

@@ -0,0 +1,33 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 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.core.auth.yggdrasil;
/**
*
* @author huang
*/
public class ValidateRequest {
private String clientToken;
private String accessToken;
public ValidateRequest(YggdrasilAuthentication authentication) {
clientToken = authentication.getClientToken();
accessToken = authentication.getAuthenticatedToken();
}
}

View File

@@ -0,0 +1,274 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 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.core.auth.yggdrasil;
import org.jackhuang.hmcl.api.auth.AuthenticationException;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonParseException;
import java.io.IOException;
import java.net.Proxy;
import java.net.URL;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.jackhuang.hmcl.util.ArrayUtils;
import org.jackhuang.hmcl.util.C;
import org.jackhuang.hmcl.api.HMCLog;
import org.jackhuang.hmcl.util.net.NetUtils;
import org.jackhuang.hmcl.util.StrUtils;
public class YggdrasilAuthentication {
public static final Gson GSON = new GsonBuilder()
.registerTypeAdapter(GameProfile.class, new GameProfile.GameProfileSerializer())
.registerTypeAdapter(PropertyMap.class, new PropertyMap.Serializer())
.registerTypeAdapter(UUID.class, new UUIDTypeAdapter()).create();
protected static final String BASE_URL = "https://authserver.mojang.com/";
protected static final URL ROUTE_AUTHENTICATE = NetUtils.constantURL(BASE_URL + "authenticate");
protected static final URL ROUTE_REFRESH = NetUtils.constantURL(BASE_URL + "refresh");
protected static final URL ROUTE_VALIDATE = NetUtils.constantURL(BASE_URL + "validate");
protected static final String STORAGE_KEY_ACCESS_TOKEN = "accessToken";
protected static final String STORAGE_KEY_PROFILE_NAME = "displayName";
protected static final String STORAGE_KEY_PROFILE_ID = "uuid";
protected static final String STORAGE_KEY_PROFILE_PROPERTIES = "profileProperties";
protected static final String STORAGE_KEY_USER_NAME = "username";
protected static final String STORAGE_KEY_USER_ID = "userid";
protected static final String STORAGE_KEY_USER_PROPERTIES = "userProperties";
private final Proxy proxy;
private final String clientToken;
private final PropertyMap userProperties = new PropertyMap();
private String userid, username, password, accessToken;
private UserType userType;
private GameProfile selectedProfile;
private GameProfile[] profiles;
private boolean isOnline;
public YggdrasilAuthentication(Proxy proxy, String clientToken) {
this.proxy = proxy;
this.clientToken = clientToken;
}
// <editor-fold defaultstate="collapsed" desc="Get/Set">
public void setUserName(String username) {
if ((isLoggedIn()) && (canPlayOnline()))
throw new IllegalStateException("Cannot change username while logged in & online");
this.username = username;
}
public void setPassword(String password) {
if ((isLoggedIn()) && (canPlayOnline()) && (StrUtils.isNotBlank(password)))
throw new IllegalStateException("Cannot set password while logged in & online");
this.password = password;
}
public GameProfile getSelectedProfile() {
return this.selectedProfile;
}
public String getUserId() {
return this.userid;
}
public PropertyMap getUserProperties() {
if (isLoggedIn())
return (PropertyMap) userProperties.clone();
return new PropertyMap();
}
public GameProfile[] getAvailableProfiles() {
if (profiles == null)
return null;
else
return profiles.clone();
}
public String getAuthenticatedToken() {
return this.accessToken;
}
public String getClientToken() {
return clientToken;
}
// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="Log In/Out">
public boolean canPlayOnline() {
return isLoggedIn() && getSelectedProfile() != null && this.isOnline;
}
public boolean canLogIn() {
return !canPlayOnline() && StrUtils.isNotBlank(username) && (StrUtils.isNotBlank(password) || StrUtils.isNotBlank(getAuthenticatedToken()));
}
public boolean isLoggedIn() {
return StrUtils.isNotBlank(this.accessToken);
}
public void logIn() throws AuthenticationException {
if (StrUtils.isBlank(username))
throw new AuthenticationException(C.i18n("login.invalid_username"));
if (StrUtils.isNotBlank(getAuthenticatedToken())) {
if (StrUtils.isBlank(getUserId()))
if (StrUtils.isBlank(username))
userid = username;
else
throw new AuthenticationException(C.i18n("login.invalid_uuid_and_username"));
if (checkTokenValidity()) {
isOnline = true;
return;
}
logInImpl(ROUTE_REFRESH, new RefreshRequest(getAuthenticatedToken(), clientToken));
} else if (StrUtils.isNotBlank(password))
logInImpl(ROUTE_AUTHENTICATE, new AuthenticationRequest(username, password, clientToken));
else
throw new AuthenticationException(C.i18n("login.invalid_password"));
}
private void logInImpl(URL url, Object input) throws AuthenticationException {
Response response = makeRequest(url, input, Response.class);
if (!clientToken.equals(response.clientToken))
throw new AuthenticationException(C.i18n("login.changed_client_token"));
if (response.selectedProfile != null)
userType = UserType.byLegacy(response.selectedProfile.isLegacy());
else if (ArrayUtils.isNotEmpty(response.availableProfiles))
userType = UserType.byLegacy(response.availableProfiles[0].isLegacy());
User user = response.user;
userid = user != null && user.getId() != null ? user.getId() : username;
isOnline = true;
profiles = response.availableProfiles;
selectedProfile = response.selectedProfile;
userProperties.clear();
this.accessToken = response.accessToken;
if (user != null && user.getProperties() != null)
userProperties.putAll(user.getProperties());
}
protected <T extends Response> T makeRequest(URL url, Object input, Class<T> clazz)
throws AuthenticationException {
try {
String jsonResult = input == null ? NetUtils.get(url, proxy) : NetUtils.post(url, GSON.toJson(input), "application/json", proxy);
T response = (T) GSON.fromJson(jsonResult, clazz);
if (response == null)
return null;
if (StrUtils.isNotBlank(response.error)) {
HMCLog.err("Failed to log in, the auth server returned an error: " + response.error + ", message: " + response.errorMessage + ", cause: " + response.cause);
if (response.errorMessage != null && response.errorMessage.contains("Invalid token"))
response.errorMessage = C.i18n("login.invalid_token");
throw new AuthenticationException("Request error: " + response.errorMessage);
}
return response;
} catch (IOException | IllegalStateException | JsonParseException e) {
throw new AuthenticationException(C.i18n("login.failed.connect_authentication_server"), e);
}
}
protected boolean checkTokenValidity() {
ValidateRequest request = new ValidateRequest(this);
try {
makeRequest(ROUTE_VALIDATE, request, Response.class);
return true;
} catch (AuthenticationException ex) {
return false;
}
}
public void logOut() {
password = null;
userid = null;
selectedProfile = null;
userProperties.clear();
accessToken = null;
profiles = null;
isOnline = false;
}
// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="Settings Storage">
public void loadFromStorage(Map<String, Object> credentials) {
logOut();
try {
setUserName((String) credentials.get(STORAGE_KEY_USER_NAME));
if (credentials.containsKey(STORAGE_KEY_USER_ID))
userid = (String) credentials.get(STORAGE_KEY_USER_ID);
else
userid = username;
if (credentials.containsKey(STORAGE_KEY_USER_PROPERTIES))
userProperties.fromList((List<Map<String, String>>) credentials.get(STORAGE_KEY_USER_PROPERTIES));
if ((credentials.containsKey(STORAGE_KEY_PROFILE_NAME)) && (credentials.containsKey(STORAGE_KEY_PROFILE_ID))) {
GameProfile profile = new GameProfile(UUIDTypeAdapter.fromString((String) credentials.get(STORAGE_KEY_PROFILE_ID)), (String) credentials.get(STORAGE_KEY_PROFILE_NAME));
if (credentials.containsKey(STORAGE_KEY_PROFILE_PROPERTIES))
profile.properties.fromList((List<Map<String, String>>) credentials.get(STORAGE_KEY_PROFILE_PROPERTIES));
selectedProfile = profile;
}
this.accessToken = (String) credentials.get(STORAGE_KEY_ACCESS_TOKEN);
} catch (Exception e) {
HMCLog.err("Failed to load yggdrasil authenticator settings, maybe its format is malformed.", e);
logOut();
}
}
public Map<String, Object> saveForStorage() {
Map<String, Object> result = new HashMap<>();
if (username != null)
result.put(STORAGE_KEY_USER_NAME, username);
if (getUserId() != null)
result.put(STORAGE_KEY_USER_ID, getUserId());
if (!getUserProperties().isEmpty())
result.put(STORAGE_KEY_USER_PROPERTIES, getUserProperties().list());
GameProfile sel = getSelectedProfile();
if (sel != null) {
result.put(STORAGE_KEY_PROFILE_NAME, sel.name);
result.put(STORAGE_KEY_PROFILE_ID, sel.id);
if (!sel.properties.isEmpty())
result.put(STORAGE_KEY_PROFILE_PROPERTIES, sel.properties.list());
}
if (StrUtils.isNotBlank(getAuthenticatedToken()))
result.put(STORAGE_KEY_ACCESS_TOKEN, getAuthenticatedToken());
return result;
}
// </editor-fold>
}

View File

@@ -0,0 +1,85 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 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.core.download;
import org.jackhuang.hmcl.core.install.InstallerVersionList;
import org.jackhuang.hmcl.util.C;
/**
*
* @author huangyuhui
*/
public class BMCLAPIDownloadProvider extends IDownloadProvider {
@Override
public InstallerVersionList getForgeInstaller() {
return org.jackhuang.hmcl.core.install.forge.MinecraftForgeVersionList.getInstance();
}
@Override
public InstallerVersionList getLiteLoaderInstaller() {
return org.jackhuang.hmcl.core.install.liteloader.LiteLoaderVersionList.getInstance();
}
@Override
public InstallerVersionList getOptiFineInstaller() {
return org.jackhuang.hmcl.core.install.optifine.bmcl.OptiFineBMCLVersionList.getInstance();
}
@Override
public String getLibraryDownloadURL() {
return "http://bmclapi2.bangbang93.com/libraries";
}
@Override
public String getVersionsDownloadURL() {
return "http://bmclapi2.bangbang93.com/versions/";
}
@Override
public String getIndexesDownloadURL() {
return "http://bmclapi2.bangbang93.com/indexes/";
}
@Override
public String getVersionsListDownloadURL() {
return "http://bmclapi2.bangbang93.com/mc/game/version_manifest.json";
}
@Override
public String getAssetsDownloadURL() {
return "http://bmclapi2.bangbang93.com/assets/";
}
@Override
public String getParsedDownloadURL(String str) {
return str == null ? null
: str.replace("https://launchermeta.mojang.com", "http://bmclapi2.bangbang93.com")
.replace("https://launcher.mojang.com", "http://bmclapi2.bangbang93.com")
.replace("https://libraries.minecraft.net", "http://bmclapi2.bangbang93.com/libraries")
.replace("http://files.minecraftforge.net/maven", "http://bmclapi2.bangbang93.com/maven")
.replace(C.URL_LITELOADER_LIST, "http://bmclapi2.bangbang93.com/maven/com/mumfrey/liteloader/versions.json")
.replace("http://dl.liteloader.com/versions", "http://bmclapi2.bangbang93.com/maven");
}
@Override
public boolean isAllowedToUseSelfURL() {
return true;
}
}

View File

@@ -0,0 +1,31 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 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.core.download;
/**
*
* @author huangyuhui
*/
public class CurseDownloadProvider extends MojangDownloadProvider {
@Override
public String getParsedDownloadURL(String str) {
return str == null ? null : str.replace("http://files.minecraftforge.net/maven", "http://ftb.cursecdn.com/FTB2/maven");
}
}

View File

@@ -0,0 +1,68 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 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.core.download;
import org.jackhuang.hmcl.api.HMCLApi;
import org.jackhuang.hmcl.api.event.config.DownloadTypeChangedEvent;
import org.jackhuang.hmcl.util.C;
import org.jackhuang.hmcl.util.task.TaskWindow;
/**
*
* @author huangyuhui
*/
public enum DownloadType {
Mojang("download.mojang", new MojangDownloadProvider()),
BMCL("download.BMCL", new BMCLAPIDownloadProvider()),
Curse("Curse CDN", new CurseDownloadProvider());
private final String name;
private final IDownloadProvider provider;
DownloadType(String a, IDownloadProvider provider) {
name = a;
this.provider = provider;
}
public IDownloadProvider getProvider() {
return provider;
}
public String getName() {
return C.i18n(name);
}
private static DownloadType suggestedDownloadType = Mojang;
public static DownloadType getSuggestedDownloadType() {
return suggestedDownloadType;
}
public static void setSuggestedDownloadType(String id) {
DownloadType s = DownloadType.valueOf(id);
if (s == null)
throw new IllegalArgumentException("download type should not be null.");
TaskWindow.downloadSource = s.getName();
DownloadType.suggestedDownloadType = s;
}
static {
HMCLApi.EVENT_BUS.channel(DownloadTypeChangedEvent.class).register(t -> setSuggestedDownloadType(t.getValue()));
}
}

View File

@@ -0,0 +1,69 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 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.core.download;
import org.jackhuang.hmcl.core.install.InstallerType;
import org.jackhuang.hmcl.core.install.InstallerVersionList;
/**
*
* @author huangyuhui
*/
public abstract class IDownloadProvider {
public InstallerVersionList getInstallerByType(InstallerType type) {
switch (type) {
case Forge:
return getForgeInstaller();
case LiteLoader:
return getLiteLoaderInstaller();
case OptiFine:
return getOptiFineInstaller();
default:
return null;
}
}
public abstract InstallerVersionList getForgeInstaller();
public abstract InstallerVersionList getLiteLoaderInstaller();
public abstract InstallerVersionList getOptiFineInstaller();
public abstract String getLibraryDownloadURL();
public abstract String getVersionsDownloadURL();
public abstract String getIndexesDownloadURL();
public abstract String getVersionsListDownloadURL();
public abstract String getAssetsDownloadURL();
/**
* For example, minecraft.json/assetIndex/url or
* minecraft.json/downloads/client/url
*
* @param str baseURL
*
* @return parsedURL
*/
public abstract String getParsedDownloadURL(String str);
public abstract boolean isAllowedToUseSelfURL();
}

View File

@@ -0,0 +1,164 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 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.core.download;
import org.jackhuang.hmcl.api.event.launch.DownloadLibraryJob;
import org.jackhuang.hmcl.core.service.IMinecraftDownloadService;
import com.google.gson.JsonSyntaxException;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import org.jackhuang.hmcl.util.C;
import org.jackhuang.hmcl.api.HMCLog;
import org.jackhuang.hmcl.core.GameException;
import org.jackhuang.hmcl.core.service.IMinecraftService;
import org.jackhuang.hmcl.core.version.GameDownloadInfo;
import org.jackhuang.hmcl.api.game.IMinecraftLibrary;
import org.jackhuang.hmcl.core.version.MinecraftVersion;
import org.jackhuang.hmcl.util.net.FileDownloadTask;
import org.jackhuang.hmcl.api.func.Function;
import org.jackhuang.hmcl.util.sys.FileUtils;
import org.jackhuang.hmcl.util.task.Task;
import org.jackhuang.hmcl.util.task.TaskInfo;
/**
*
* @author huangyuhui
*/
public class MinecraftDownloadService extends IMinecraftDownloadService {
public MinecraftDownloadService(IMinecraftService service) {
super(service);
}
@Override
public List<DownloadLibraryJob> getDownloadLibraries(MinecraftVersion mv) throws GameException {
ArrayList<DownloadLibraryJob> downloadLibraries = new ArrayList<>();
if (mv == null)
return downloadLibraries;
MinecraftVersion v = mv.resolve(service.version());
for (IMinecraftLibrary l : v.getLibraries())
if (l != null && l.allow() && l.getDownloadURL(service.getDownloadType().name()) != null) {
File ff = l.getFilePath(service.baseDirectory());
if (!ff.exists()) {
String libURL = l.getDownloadURL(service.getDownloadType().name());
if (libURL != null)
downloadLibraries.add(new DownloadLibraryJob(l, libURL, ff));
}
}
return downloadLibraries;
}
@Override
public Task downloadMinecraft(String id) {
return new TaskInfo("Download Minecraft") {
@Override
public Collection<Task> getDependTasks() {
return Arrays.asList(downloadMinecraftVersionJson(id));
}
@Override
public void executeTask(boolean areDependTasksSucceeded) throws Throwable {
File vpath = new File(service.baseDirectory(), "versions/" + id);
if (!areDependTasksSucceeded) {
FileUtils.deleteDirectory(vpath);
throw new RuntimeException("Cannot continue because of download failing.");
}
File mvj = new File(vpath, id + ".jar");
if (mvj.exists() && !mvj.delete())
HMCLog.warn("Failed to delete " + mvj);
try {
MinecraftVersion mv = C.GSON.fromJson(FileUtils.readQuietly(new File(vpath, id + ".json")), MinecraftVersion.class);
if (mv == null)
throw new JsonSyntaxException("incorrect version");
afters.add(downloadMinecraftJar(mv, mvj));
} catch (JsonSyntaxException ex) {
HMCLog.err("Failed to parse minecraft version json.", ex);
FileUtils.deleteDirectory(vpath);
}
}
Collection<Task> afters = new HashSet<>();
@Override
public Collection<Task> getAfterTasks() {
return afters;
}
};
}
private static class DownloadTypeSwitcher implements Function<Integer, String> {
String suffix;
public DownloadTypeSwitcher(String suffix) {
this.suffix = suffix;
}
@Override
public String apply(Integer t) {
return DownloadType.values()[t / 2].getProvider().getVersionsDownloadURL() + suffix;
}
}
@Override
public Task downloadMinecraftJar(MinecraftVersion mv, File mvj) {
GameDownloadInfo i = mv.getClientDownloadInfo();
return new FileDownloadTask(i.getUrl(service.getDownloadType()), mvj, i.sha1)
.setFailedCallbackReturnsNewURL(new DownloadTypeSwitcher(mv.id + "/" + mv.id + ".jar")).setTag(mv.id + ".jar");
}
@Override
public Task downloadMinecraftVersionJson(String id) {
return new TaskInfo("Download Minecraft Json") {
@Override
public void executeTask(boolean areDependTasksSucceeded) throws Throwable {
List<MinecraftRemoteVersion> versions = MinecraftRemoteVersions.getRemoteVersions(service.getDownloadType()).justDo();
MinecraftRemoteVersion currentVersion = null;
for (MinecraftRemoteVersion v : versions)
if (id.equals(v.id)) {
currentVersion = v;
break;
}
if (currentVersion == null)
throw new RuntimeException("Cannot find version: " + id + " in remote repository.");
String jsonURL = currentVersion.getUrl(service.getDownloadType());
File vpath = new File(service.baseDirectory(), "versions/" + id);
File mvt = new File(vpath, id + ".json");
if (!FileUtils.makeDirectory(vpath))
HMCLog.warn("Failed to make directories: " + vpath);
if (mvt.exists() && !mvt.delete())
HMCLog.warn("Failed to delete " + mvt);
afters.add(new FileDownloadTask(jsonURL, mvt).setTag(id + ".json"));
}
Collection<Task> afters = new HashSet<>();
@Override
public Collection<Task> getAfterTasks() {
return afters;
}
};
}
}

View File

@@ -0,0 +1,34 @@
/*
* Hello Minecraft!.
* Copyright (C) 2013 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.core.download;
import com.google.gson.annotations.SerializedName;
/**
*
* @author huangyuhui
*/
public class MinecraftRemoteLatestVersion {
@SerializedName("snapshot")
public String snapshot;
@SerializedName("release")
public String release;
}

View File

@@ -0,0 +1,44 @@
/*
* Hello Minecraft!.
* Copyright (C) 2013 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.core.download;
import com.google.gson.annotations.SerializedName;
/**
*
* @author huangyuhui
*/
public class MinecraftRemoteVersion {
@SerializedName("id")
public String id;
@SerializedName("time")
public String time;
@SerializedName("releaseTime")
public String releaseTime;
@SerializedName("type")
public String type;
@SerializedName("url")
private String url;
public String getUrl(DownloadType type) {
if (url == null)
return type.getProvider().getVersionsDownloadURL() + id + "/" + id + ".json";
return type.getProvider().getParsedDownloadURL(url);
}
}

View File

@@ -0,0 +1,77 @@
/*
* Hello Minecraft!.
* Copyright (C) 2013 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.core.download;
import com.google.gson.annotations.SerializedName;
import java.util.ArrayList;
import org.jackhuang.hmcl.util.C;
import org.jackhuang.hmcl.util.net.NetUtils;
import org.jackhuang.hmcl.util.AbstractSwingWorker;
/**
*
* @author huangyuhui
*/
public class MinecraftRemoteVersions {
@SerializedName("versions")
public ArrayList<MinecraftRemoteVersion> versions;
@SerializedName("latest")
public MinecraftRemoteLatestVersion latest;
private static volatile MinecraftRemoteVersions INSTANCE = null;
private static final Object INSTANCE_LOCK = new Object();
public static RemoteVersionsTask getRemoteVersions(DownloadType type) {
return new RemoteVersionsTask(type) {
@Override
public void work() throws Exception {
synchronized (INSTANCE_LOCK) {
if (INSTANCE != null)
send(INSTANCE.versions.toArray(new MinecraftRemoteVersion[INSTANCE.versions.size()]));
else
super.work();
}
}
};
}
public static RemoteVersionsTask refreshRomoteVersions(DownloadType type) {
return new RemoteVersionsTask(type);
}
public static class RemoteVersionsTask extends AbstractSwingWorker<MinecraftRemoteVersion> {
DownloadType type;
public RemoteVersionsTask(DownloadType type) {
this.type = type;
}
@Override
public void work() throws Exception {
MinecraftRemoteVersions r = C.GSON.fromJson(NetUtils.get(type.getProvider().getVersionsListDownloadURL()), MinecraftRemoteVersions.class);
if (r != null && r.versions != null) {
INSTANCE = r;
send(r.versions.toArray(new MinecraftRemoteVersion[r.versions.size()]));
}
}
}
}

View File

@@ -0,0 +1,89 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 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.core.download;
import java.util.Locale;
import org.jackhuang.hmcl.core.install.InstallerVersionList;
import org.jackhuang.hmcl.util.lang.SupportedLocales;
/**
*
* @author huangyuhui
*/
public class MojangDownloadProvider extends IDownloadProvider {
@Override
public InstallerVersionList getForgeInstaller() {
return org.jackhuang.hmcl.core.install.forge.MinecraftForgeVersionList.getInstance();
}
@Override
public InstallerVersionList getLiteLoaderInstaller() {
return org.jackhuang.hmcl.core.install.liteloader.LiteLoaderVersionList.getInstance();
}
@Override
public InstallerVersionList getOptiFineInstaller() {
return org.jackhuang.hmcl.core.install.optifine.vanilla.OptiFineVersionList.getInstance();
}
@Override
public String getLibraryDownloadURL() {
return "https://libraries.minecraft.net";
}
@Override
public String getVersionsDownloadURL() {
return "http://s3.amazonaws.com/Minecraft.Download/versions/";
}
@Override
public String getIndexesDownloadURL() {
return "http://s3.amazonaws.com/Minecraft.Download/indexes/";
}
@Override
public String getVersionsListDownloadURL() {
return "https://launchermeta.mojang.com/mc/game/version_manifest.json";
}
@Override
public String getAssetsDownloadURL() {
return "http://resources.download.minecraft.net/";
}
@Override
public boolean isAllowedToUseSelfURL() {
return true;
}
@Override
public String getParsedDownloadURL(String str) {
if (str == null)
return null;
else if (str.contains("scala-swing") || str.contains("scala-xml") || str.contains("scala-parser-combinators"))
return str.replace("http://files.minecraftforge.net/maven", "http://ftb.cursecdn.com/FTB2/maven/");
else if (str.contains("typesafe") || str.contains("scala"))
if (SupportedLocales.NOW_LOCALE.self == Locale.CHINA)
return str.replace("http://files.minecraftforge.net/maven", "http://maven.aliyun.com/nexus/content/groups/public");
else
return str.replace("http://files.minecraftforge.net/maven", "http://repo1.maven.org/maven2");
else
return str;
}
}

View File

@@ -0,0 +1,34 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 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.core.install;
import com.google.gson.annotations.SerializedName;
import org.jackhuang.hmcl.core.version.MinecraftVersion;
import org.jackhuang.hmcl.core.install.forge.Install;
/**
*
* @author huangyuhui
*/
public class InstallProfile {
@SerializedName("install")
public Install install;
@SerializedName("versionInfo")
public MinecraftVersion versionInfo;
}

View File

@@ -0,0 +1,36 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 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.core.install;
/**
*
* @author huangyuhui
*/
public enum InstallerType {
Forge("forge"), OptiFine("optifine"), LiteLoader("liteloader");
public final String id;
private InstallerType(String id) {
this.id = id;
}
public String getLocalizedName() {
return this.name();
}
}

View File

@@ -0,0 +1,136 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 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.core.install;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import org.jackhuang.hmcl.util.StrUtils;
import org.jackhuang.hmcl.util.task.Task;
/**
*
* @author huangyuhui
*/
public abstract class InstallerVersionList {
public Map<String, List<InstallerVersion>> versionMap;
public List<InstallerVersion> versions;
/**
* Refresh installer versions list from the downloaded content.
*
* @param versions Minecraft versions you need to refresh
*
* @throws java.lang.Exception including network exceptions, IO exceptions.
*/
public abstract Task refresh(String[] versions);
/**
* Installer name.
*
* @return installer name.
*/
public abstract String getName();
/**
* Get installers you want.
*
* @param mcVersion the installers to this Minecraft version.
*
* @return cached result.
*/
public List<InstallerVersion> getVersionsImpl(String mcVersion) {
if (versions == null || versionMap == null)
return null;
if (StrUtils.isBlank(mcVersion))
return versions;
List<InstallerVersion> c = versionMap.get(mcVersion);
if (c == null)
return versions;
Collections.sort(c, InstallerVersionComparator.INSTANCE);
return c;
}
/**
* Get installers you want, please cache this method's result to save time.
*
* @param mcVersion the installers to this Minecraft version.
*
* @return a copy of the cached data to prevent
* ConcurrentModificationException.
*/
public List<InstallerVersion> getVersions(String mcVersion) {
List<InstallerVersion> a = getVersionsImpl(mcVersion);
if (a == null)
return null;
else
return new ArrayList<>(a);
}
public static class InstallerVersion implements Comparable<InstallerVersion> {
public String selfVersion, mcVersion;
public String installer, universal;
public String changelog;
public InstallerVersion(String selfVersion, String mcVersion) {
this.selfVersion = selfVersion;
this.mcVersion = mcVersion;
}
@Override
public int compareTo(InstallerVersion o) {
return selfVersion.compareTo(o.selfVersion);
}
@Override
public int hashCode() {
return selfVersion.hashCode();
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
final InstallerVersion other = (InstallerVersion) obj;
return Objects.equals(this.selfVersion, other.selfVersion);
}
}
public static class InstallerVersionComparator implements Comparator<InstallerVersion>, Serializable {
private static final long serialVersionUID = 3276198781795213723L;
public static final InstallerVersionComparator INSTANCE = new InstallerVersionComparator();
@Override
public int compare(InstallerVersion o1, InstallerVersion o2) {
return o2.compareTo(o1);
}
}
}

View File

@@ -0,0 +1,36 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 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.core.install;
import java.io.Serializable;
import java.util.Comparator;
import org.jackhuang.hmcl.core.install.InstallerVersionList.InstallerVersion;
/**
*
* @author huangyuhui
*/
public class InstallerVersionNewerComparator implements Comparator<InstallerVersion>, Serializable {
private static final long serialVersionUID = 14758562453742645L;
@Override
public int compare(InstallerVersion o1, InstallerVersion o2) {
return o2.compareTo(o1);
}
}

View File

@@ -0,0 +1,89 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 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.core.install;
import org.jackhuang.hmcl.core.service.IMinecraftInstallerService;
import java.io.File;
import org.jackhuang.hmcl.core.service.IMinecraftService;
import org.jackhuang.hmcl.core.install.InstallerVersionList.InstallerVersion;
import org.jackhuang.hmcl.core.install.forge.ForgeInstaller;
import org.jackhuang.hmcl.core.install.liteloader.LiteLoaderInstaller;
import org.jackhuang.hmcl.core.install.liteloader.LiteLoaderInstallerVersion;
import org.jackhuang.hmcl.core.install.optifine.OptiFineInstaller;
import org.jackhuang.hmcl.core.install.optifine.vanilla.OptiFineDownloadFormatter;
import org.jackhuang.hmcl.util.task.Task;
import org.jackhuang.hmcl.util.net.FileDownloadTask;
import org.jackhuang.hmcl.util.sys.IOUtils;
import org.jackhuang.hmcl.util.task.DeleteFileTask;
/**
*
* @author huangyuhui
*/
public final class MinecraftInstallerService extends IMinecraftInstallerService {
public MinecraftInstallerService(IMinecraftService service) {
super(service);
}
@Override
public Task download(String installId, InstallerVersion v, InstallerType type) {
switch (type) {
case Forge:
return downloadForge(installId, v);
case OptiFine:
return downloadOptiFine(installId, v);
case LiteLoader:
return downloadLiteLoader(installId, v);
default:
return null;
}
}
@Override
public Task downloadForge(String installId, InstallerVersion v) {
File filepath = IOUtils.tryGetCanonicalFile("forge-installer.jar");
if (v.installer == null)
return null;
else
return new FileDownloadTask(service.getDownloadType().getProvider().getParsedDownloadURL(v.installer), filepath).setTag("forge")
.with(new ForgeInstaller(service, filepath))
.with(new DeleteFileTask(filepath));
}
@Override
public Task downloadOptiFine(String installId, InstallerVersion v) {
File filepath = IOUtils.tryGetCanonicalFile("optifine-installer.jar");
if (v.installer == null)
return null;
OptiFineDownloadFormatter task = new OptiFineDownloadFormatter(v.installer);
return task.with(new FileDownloadTask(filepath).registerPreviousResult(task).setTag("optifine"))
.with(new OptiFineInstaller(service, installId, v, filepath))
.with(new DeleteFileTask(filepath));
}
@Override
public Task downloadLiteLoader(String installId, InstallerVersion v) {
if (!(v instanceof LiteLoaderInstallerVersion))
throw new Error("Download lite loader but the version is not ll's.");
File filepath = IOUtils.tryGetCanonicalFile("liteloader-universal.jar");
FileDownloadTask task = (FileDownloadTask) new FileDownloadTask(v.universal, filepath).setTag("LiteLoader");
return task.with(new LiteLoaderInstaller(service, installId, (LiteLoaderInstallerVersion) v).registerPreviousResult(task))
.with(new DeleteFileTask(filepath));
}
}

View File

@@ -0,0 +1,104 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 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.core.install.forge;
import org.jackhuang.hmcl.core.install.InstallProfile;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import org.jackhuang.hmcl.api.HMCLApi;
import org.jackhuang.hmcl.api.event.version.MinecraftLibraryPathEvent;
import org.jackhuang.hmcl.util.C;
import org.jackhuang.hmcl.api.HMCLog;
import org.jackhuang.hmcl.core.service.IMinecraftService;
import org.jackhuang.hmcl.util.task.Task;
import org.jackhuang.hmcl.util.sys.FileUtils;
import org.jackhuang.hmcl.core.version.MinecraftLibrary;
import org.jackhuang.hmcl.util.MessageBox;
import org.jackhuang.hmcl.api.Wrapper;
import org.jackhuang.hmcl.util.sys.IOUtils;
/**
*
* @author huangyuhui
*/
public class ForgeInstaller extends Task {
public File gameDir;
public File forgeInstaller;
public IMinecraftService mp;
public ForgeInstaller(IMinecraftService mp, File forgeInstaller) {
this.gameDir = mp.baseDirectory();
this.forgeInstaller = forgeInstaller;
this.mp = mp;
}
@Override
public void executeTask(boolean areDependTasksSucceeded) throws Exception {
HMCLog.log("Extracting install profiles...");
try (ZipFile zipFile = new ZipFile(forgeInstaller)) {
ZipEntry entry = zipFile.getEntry("install_profile.json");
String content = IOUtils.toString(zipFile.getInputStream(entry));
InstallProfile profile = C.GSON.fromJson(content, InstallProfile.class);
File from = new File(gameDir, "versions" + File.separator + profile.install.getMinecraft());
if (!from.exists())
if (MessageBox.show(C.i18n("install.no_version_if_intall")) == MessageBox.YES_OPTION) {
if (!mp.version().install(profile.install.getMinecraft(), null))
throw new IllegalStateException(C.i18n("install.no_version"));
} else
throw new IllegalStateException(C.i18n("install.no_version"));
File to = new File(gameDir, "versions" + File.separator + profile.install.getTarget());
if (!FileUtils.makeDirectory(to))
HMCLog.warn("Failed to make new version folder " + to);
HMCLog.log("Copying jar..." + profile.install.getMinecraft() + ".jar to " + profile.install.getTarget() + ".jar");
FileUtils.copyFile(new File(from, profile.install.getMinecraft() + ".jar"),
new File(to, profile.install.getTarget() + ".jar"));
HMCLog.log("Creating new version profile..." + profile.install.getTarget() + ".json");
FileUtils.write(new File(to, profile.install.getTarget() + ".json"), C.GSON.toJson(profile.versionInfo));
HMCLog.log("Extracting universal forge pack..." + profile.install.getFilePath());
entry = zipFile.getEntry(profile.install.getFilePath());
InputStream is = zipFile.getInputStream(entry);
MinecraftLibrary forge = new MinecraftLibrary(profile.install.getPath());
String path = "libraries/" + forge.getDownloadInfo().path;
MinecraftLibraryPathEvent event = new MinecraftLibraryPathEvent(this, path, new Wrapper<>(new File(gameDir, path)));
HMCLApi.EVENT_BUS.fireChannel(event);
File file = event.getFile().getValue();
if (!FileUtils.makeDirectory(file.getParentFile()))
HMCLog.warn("Failed to make library directory " + file.getParent());
try (FileOutputStream fos = FileUtils.openOutputStream(file)) {
IOUtils.copyStream(is, fos);
}
mp.version().refreshVersions();
}
}
@Override
public String getInfo() {
return C.i18n("install.forge.install");
}
}

View File

@@ -0,0 +1,122 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 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.core.install.forge;
import com.google.gson.annotations.SerializedName;
/**
*
* @author huangyuhui
*/
public class Install {
@SerializedName("profileName")
private String profileName;
@SerializedName("target")
private String target;
@SerializedName("path")
private String path;
@SerializedName("version")
private String version;
@SerializedName("filePath")
private String filePath;
@SerializedName("welcome")
private String welcome;
@SerializedName("minecraft")
private String minecraft;
@SerializedName("mirrorList")
private String mirrorList;
@SerializedName("logo")
private String logo;
public Install() {
}
public String getProfileName() {
return profileName;
}
public void setProfileName(String profileName) {
this.profileName = profileName;
}
public String getTarget() {
return target;
}
public void setTarget(String target) {
this.target = target;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
public String getFilePath() {
return filePath;
}
public void setFilePath(String filePath) {
this.filePath = filePath;
}
public String getWelcome() {
return welcome;
}
public void setWelcome(String welcome) {
this.welcome = welcome;
}
public String getMinecraft() {
return minecraft;
}
public void setMinecraft(String minecraft) {
this.minecraft = minecraft;
}
public String getMirrorList() {
return mirrorList;
}
public void setMirrorList(String mirrorList) {
this.mirrorList = mirrorList;
}
public String getLogo() {
return logo;
}
public void setLogo(String logo) {
this.logo = logo;
}
}

View File

@@ -0,0 +1,79 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 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.core.install.forge;
/**
*
* @author huangyuhui
*/
public class MinecraftForgeVersion {
public String branch, mcversion, jobver, version;
public int build;
public double modified;
public String[][] files;
public String getBranch() {
return branch;
}
public void setBranch(String branch) {
this.branch = branch;
}
public String getMCVersion() {
return mcversion;
}
public void setMCVersion(String mcversion) {
this.mcversion = mcversion;
}
public String getJobver() {
return jobver;
}
public void setJobver(String jobver) {
this.jobver = jobver;
}
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
public int getBuild() {
return build;
}
public void setBuild(int build) {
this.build = build;
}
public double getModified() {
return modified;
}
public void setModified(double modified) {
this.modified = modified;
}
}

View File

@@ -0,0 +1,120 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 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.core.install.forge;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.jackhuang.hmcl.util.C;
import org.jackhuang.hmcl.core.download.DownloadType;
import org.jackhuang.hmcl.util.StrUtils;
import org.jackhuang.hmcl.core.install.InstallerVersionList;
import org.jackhuang.hmcl.core.install.InstallerVersionNewerComparator;
import org.jackhuang.hmcl.util.task.Task;
import org.jackhuang.hmcl.util.task.TaskInfo;
import org.jackhuang.hmcl.util.net.HTTPGetTask;
/**
*
* @author huangyuhui
*/
public class MinecraftForgeVersionList extends InstallerVersionList {
private static volatile MinecraftForgeVersionList instance;
public static MinecraftForgeVersionList getInstance() {
if (instance == null)
instance = new MinecraftForgeVersionList();
return instance;
}
public MinecraftForgeVersionRoot root;
@Override
public Task refresh(String[] needed) {
if (root != null)
return null;
return new TaskInfo(C.i18n("install.forge.get_list")) {
HTTPGetTask task = new HTTPGetTask(DownloadType.getSuggestedDownloadType().getProvider().getParsedDownloadURL(C.URL_FORGE_LIST));
@Override
public Collection<Task> getDependTasks() {
return Arrays.asList(task.setTag("Official Forge Download Site"));
}
@Override
public void executeTask(boolean areDependTasksSucceeded) throws Throwable {
if (!areDependTasksSucceeded)
return;
String s = task.getResult();
root = C.GSON.fromJson(s, MinecraftForgeVersionRoot.class);
versionMap = new HashMap<>();
versions = new ArrayList<>();
for (Map.Entry<String, int[]> arr : root.mcversion.entrySet()) {
String mcver = StrUtils.formatVersion(arr.getKey());
ArrayList<InstallerVersion> al = new ArrayList<>();
for (int num : arr.getValue()) {
MinecraftForgeVersion v = root.number.get(num);
InstallerVersion iv = new InstallerVersion(v.version, StrUtils.formatVersion(v.mcversion));
for (String[] f : v.files) {
String ver = v.mcversion + "-" + v.version;
if (!StrUtils.isBlank(v.branch))
ver = ver + "-" + v.branch;
String filename = root.artifact + "-" + ver + "-" + f[1] + "." + f[0];
String url = DownloadType.getSuggestedDownloadType().getProvider().getParsedDownloadURL(root.webpath + ver + "/" + filename);
switch (f[1]) {
case "installer":
iv.installer = url;
break;
case "universal":
iv.universal = url;
break;
case "changelog":
iv.changelog = url;
break;
default:
break;
}
}
if (StrUtils.isBlank(iv.installer) || StrUtils.isBlank(iv.universal))
continue;
Collections.sort(al, new InstallerVersionNewerComparator());
al.add(iv);
versions.add(iv);
}
versionMap.put(StrUtils.formatVersion(mcver), al);
}
Collections.sort(versions, new InstallerVersionComparator());
}
};
}
@Override
public String getName() {
return "Forge - MinecraftForge Offical Site";
}
}

View File

@@ -0,0 +1,47 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 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.core.install.forge;
import com.google.gson.annotations.SerializedName;
import java.util.Map;
/**
*
* @author huangyuhui
*/
public class MinecraftForgeVersionRoot {
@SerializedName("artifact")
public String artifact;
@SerializedName("webpath")
public String webpath;
@SerializedName("adfly")
public String adfly;
@SerializedName("homepage")
public String homepage;
@SerializedName("name")
public String name;
@SerializedName("branches")
public Map<String, int[]> branches;
@SerializedName("mcversion")
public Map<String, int[]> mcversion;
@SerializedName("promos")
public Map<String, Integer> promos;
@SerializedName("number")
public Map<Integer, MinecraftForgeVersion> number;
}

View File

@@ -0,0 +1,107 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 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.core.install.liteloader;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import org.jackhuang.hmcl.api.HMCLApi;
import org.jackhuang.hmcl.api.event.version.MinecraftLibraryPathEvent;
import org.jackhuang.hmcl.util.C;
import org.jackhuang.hmcl.api.HMCLog;
import org.jackhuang.hmcl.core.service.IMinecraftService;
import org.jackhuang.hmcl.util.task.Task;
import org.jackhuang.hmcl.util.task.comm.PreviousResult;
import org.jackhuang.hmcl.util.task.comm.PreviousResultRegistrar;
import org.jackhuang.hmcl.util.sys.FileUtils;
import org.jackhuang.hmcl.core.version.MinecraftLibrary;
import org.jackhuang.hmcl.core.version.MinecraftVersion;
import org.jackhuang.hmcl.api.Wrapper;
/**
*
* @author huangyuhui
*/
public class LiteLoaderInstaller extends Task implements PreviousResultRegistrar<File> {
public LiteLoaderInstallerVersion version;
public File installer;
public String installId;
public IMinecraftService service;
public LiteLoaderInstaller(IMinecraftService service, String installId, LiteLoaderInstallerVersion v) {
this(service, installId, v, null);
}
public LiteLoaderInstaller(IMinecraftService service, String installId, LiteLoaderInstallerVersion v, File installer) {
this.service = service;
this.installId = installId;
this.version = v;
this.installer = installer;
}
@Override
public void executeTask(boolean areDependTasksSucceeded) throws Exception {
if (installId == null)
throw new IllegalStateException(C.i18n("install.no_version"));
if (pre.size() != 1 && installer == null)
throw new IllegalStateException("No registered previous task.");
if (installer == null)
installer = pre.get(pre.size() - 1).getResult();
MinecraftVersion mv = (MinecraftVersion) service.version().getVersionById(installId).clone();
mv.inheritsFrom = mv.id;
mv.jar = mv.jar == null ? mv.id : mv.jar;
mv.libraries = new ArrayList<>(Arrays.asList(version.libraries));
MinecraftLibrary ml = new MinecraftLibrary("com.mumfrey:liteloader:" + version.selfVersion);
//ml.url = "http://dl.liteloader.com/versions/com/mumfrey/liteloader/" + version.mcVersion + "/liteloader-" + version.selfVersion + ".jar";
mv.libraries.add(0, ml);
String path = "libraries/com/mumfrey/liteloader/" + version.selfVersion + "/liteloader-" + version.selfVersion + ".jar";
MinecraftLibraryPathEvent event = new MinecraftLibraryPathEvent(this, path, new Wrapper<>(new File(service.baseDirectory(), path)));
HMCLApi.EVENT_BUS.fireChannel(event);
FileUtils.copyFile(installer, event.getFile().getValue());
mv.id += "-LiteLoader" + version.selfVersion;
mv.mainClass = "net.minecraft.launchwrapper.Launch";
mv.minecraftArguments += " --tweakClass " + version.tweakClass;
File folder = new File(service.baseDirectory(), "versions/" + mv.id);
if (!FileUtils.makeDirectory(folder))
HMCLog.warn("Failed to create new liteloader version " + folder);
File json = new File(folder, mv.id + ".json");
HMCLog.log("Creating new version profile..." + mv.id + ".json");
FileUtils.write(json, C.GSON.toJson(mv));
service.version().refreshVersions();
}
@Override
public String getInfo() {
return C.i18n("install.liteloader.install");
}
ArrayList<PreviousResult<File>> pre = new ArrayList<>();
@Override
public Task registerPreviousResult(PreviousResult<File> pr) {
pre.add(pr);
return this;
}
}

View File

@@ -0,0 +1,58 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 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.core.install.liteloader;
import java.util.Arrays;
import java.util.Objects;
import org.jackhuang.hmcl.core.install.InstallerVersionList;
import org.jackhuang.hmcl.core.version.MinecraftLibrary;
/**
*
* @author huang
*/
public class LiteLoaderInstallerVersion extends InstallerVersionList.InstallerVersion {
public MinecraftLibrary[] libraries;
public String tweakClass;
public LiteLoaderInstallerVersion(String selfVersion, String mcVersion) {
super(selfVersion, mcVersion);
}
@Override
public int hashCode() {
int hash = 7;
hash = 13 * hash + Arrays.deepHashCode(this.libraries);
hash = 13 * hash + Objects.hashCode(this.tweakClass);
return hash;
}
@Override
public boolean equals(Object obj) {
if (obj == null || !(obj instanceof LiteLoaderVersionList))
return false;
if (this == obj)
return true;
final LiteLoaderInstallerVersion other = (LiteLoaderInstallerVersion) obj;
if (!Objects.equals(this.tweakClass, other.tweakClass))
return false;
return Arrays.deepEquals(this.libraries, other.libraries);
}
}

View File

@@ -0,0 +1,31 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 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.core.install.liteloader;
import com.google.gson.annotations.SerializedName;
import java.util.Map;
/**
*
* @author huangyuhui
*/
public class LiteLoaderMCVersions {
@SerializedName("artefacts")
public Map<String, Map<String, LiteLoaderVersion>> artefacts;
}

View File

@@ -0,0 +1,41 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 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.core.install.liteloader;
import com.google.gson.annotations.SerializedName;
import org.jackhuang.hmcl.core.version.MinecraftLibrary;
/**
*
* @author huangyuhui
*/
public class LiteLoaderVersion {
@SerializedName("tweakClass")
public String tweakClass;
@SerializedName("file")
public String file;
@SerializedName("version")
public String version;
@SerializedName("md5")
public String md5;
@SerializedName("timestamp")
public String timestamp;
@SerializedName("libraries")
public MinecraftLibrary[] libraries;
}

View File

@@ -0,0 +1,105 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 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.core.install.liteloader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.jackhuang.hmcl.core.download.DownloadType;
import org.jackhuang.hmcl.util.C;
import org.jackhuang.hmcl.core.install.InstallerVersionList;
import org.jackhuang.hmcl.core.install.InstallerVersionNewerComparator;
import org.jackhuang.hmcl.util.StrUtils;
import org.jackhuang.hmcl.util.task.Task;
import org.jackhuang.hmcl.util.task.TaskInfo;
import org.jackhuang.hmcl.util.net.HTTPGetTask;
/**
*
* @author huangyuhui
*/
public class LiteLoaderVersionList extends InstallerVersionList {
private static volatile LiteLoaderVersionList instance = null;
public static LiteLoaderVersionList getInstance() {
if (instance == null)
instance = new LiteLoaderVersionList();
return instance;
}
public LiteLoaderVersionsRoot root;
@Override
public Task refresh(String[] needed) {
if (root != null)
return null;
return new TaskInfo(C.i18n("install.liteloader.get_list")) {
HTTPGetTask task = new HTTPGetTask(DownloadType.getSuggestedDownloadType().getProvider().getParsedDownloadURL(C.URL_LITELOADER_LIST));
@Override
public Collection<Task> getDependTasks() {
return Arrays.asList(task.setTag("Official Liteloader Download Site"));
}
@Override
public void executeTask(boolean areDependTasksSucceeded) throws Throwable {
if (!areDependTasksSucceeded)
return;
String s = task.getResult();
root = C.GSON.fromJson(s, LiteLoaderVersionsRoot.class);
versionMap = new HashMap<>();
versions = new ArrayList<>();
for (Map.Entry<String, LiteLoaderMCVersions> arr : root.versions.entrySet()) {
ArrayList<InstallerVersion> al = new ArrayList<>();
LiteLoaderMCVersions mcv = arr.getValue();
if (mcv == null || mcv.artefacts == null || mcv.artefacts.get("com.mumfrey:liteloader") == null)
continue;
for (Map.Entry<String, LiteLoaderVersion> entry : mcv.artefacts.get("com.mumfrey:liteloader").entrySet()) {
if ("latest".equals(entry.getKey()))
continue;
LiteLoaderVersion v = entry.getValue();
LiteLoaderInstallerVersion iv = new LiteLoaderInstallerVersion(v.version, StrUtils.formatVersion(arr.getKey()));
iv.universal = DownloadType.getSuggestedDownloadType().getProvider().getParsedDownloadURL("http://dl.liteloader.com/versions/com/mumfrey/liteloader/" + arr.getKey() + "/" + v.file);
iv.tweakClass = v.tweakClass;
iv.libraries = Arrays.copyOf(v.libraries, v.libraries.length);
iv.installer = "http://dl.liteloader.com/redist/" + iv.mcVersion + "/liteloader-installer-" + iv.selfVersion.replace("_", "-") + ".jar";
al.add(iv);
versions.add(iv);
}
Collections.sort(al, new InstallerVersionNewerComparator());
versionMap.put(StrUtils.formatVersion(arr.getKey()), al);
}
Collections.sort(versions, InstallerVersionComparator.INSTANCE);
}
};
}
@Override
public String getName() {
return "LiteLoader - LiteLoader Official Site(By: Mumfrey)";
}
}

View File

@@ -0,0 +1,61 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 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.core.install.liteloader;
import com.google.gson.annotations.SerializedName;
/**
*
* @author huangyuhui
*/
public class LiteLoaderVersionsMeta {
@SerializedName("versions")
private String description;
@SerializedName("authors")
private String authors;
@SerializedName("url")
private String url;
public LiteLoaderVersionsMeta() {
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getAuthors() {
return authors;
}
public void setAuthors(String authors) {
this.authors = authors;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
}

View File

@@ -0,0 +1,33 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 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.core.install.liteloader;
import com.google.gson.annotations.SerializedName;
import java.util.Map;
/**
*
* @author huangyuhui
*/
public class LiteLoaderVersionsRoot {
@SerializedName("versions")
public Map<String, LiteLoaderMCVersions> versions;
@SerializedName("meta")
public LiteLoaderVersionsMeta meta;
}

View File

@@ -0,0 +1,112 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 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.core.install.optifine;
import java.io.File;
import java.util.ArrayList;
import java.util.zip.ZipFile;
import org.jackhuang.hmcl.api.HMCLApi;
import org.jackhuang.hmcl.api.event.version.MinecraftLibraryPathEvent;
import org.jackhuang.hmcl.util.C;
import org.jackhuang.hmcl.core.install.InstallerVersionList;
import org.jackhuang.hmcl.core.service.IMinecraftService;
import org.jackhuang.hmcl.core.version.LibrariesDownloadInfo;
import org.jackhuang.hmcl.core.version.LibraryDownloadInfo;
import org.jackhuang.hmcl.util.task.Task;
import org.jackhuang.hmcl.util.task.comm.PreviousResult;
import org.jackhuang.hmcl.util.task.comm.PreviousResultRegistrar;
import org.jackhuang.hmcl.util.sys.FileUtils;
import org.jackhuang.hmcl.core.version.MinecraftLibrary;
import org.jackhuang.hmcl.core.version.MinecraftVersion;
import org.jackhuang.hmcl.api.Wrapper;
import org.jackhuang.hmcl.api.HMCLog;
/**
*
* @author huangyuhui
*/
public class OptiFineInstaller extends Task implements PreviousResultRegistrar<File> {
public File installer;
public IMinecraftService service;
public InstallerVersionList.InstallerVersion version;
public String installId;
public OptiFineInstaller(IMinecraftService service, String installId, InstallerVersionList.InstallerVersion version, File installer) {
this.service = service;
this.installId = installId;
this.installer = installer;
this.version = version;
}
@Override
public void executeTask(boolean areDependTasksSucceeded) throws Exception {
if (installId == null)
throw new Exception(C.i18n("install.no_version"));
String selfId = version.selfVersion;
MinecraftVersion mv = (MinecraftVersion) service.version().getVersionById(installId).clone();
mv.inheritsFrom = mv.id;
mv.jar = mv.jar == null ? mv.id : mv.jar;
mv.libraries.clear();
MinecraftLibrary library = new MinecraftLibrary("optifine:OptiFine:" + selfId);
library.downloads = new LibrariesDownloadInfo();
library.downloads.artifact = new LibraryDownloadInfo();
library.downloads.artifact.path = "optifine/OptiFine/" + selfId + "/OptiFine-" + selfId + ".jar";
library.downloads.artifact.url = version.universal;
library.downloads.artifact.sha1 = null;
library.downloads.artifact.size = 0;
mv.libraries.add(0, library);
MinecraftLibraryPathEvent event = new MinecraftLibraryPathEvent(this, "libraries/" + library.downloads.artifact.path, new Wrapper<>(new File(service.baseDirectory(), "libraries/" + library.downloads.artifact.path)));
HMCLApi.EVENT_BUS.fireChannel(event);
FileUtils.copyFile(installer, event.getFile().getValue());
mv.id += "-" + selfId;
try (ZipFile zipFile = new ZipFile(installer)) {
if (zipFile.getEntry("optifine/OptiFineTweaker.class") != null) {
if (!mv.mainClass.startsWith("net.minecraft.launchwrapper.")) {
mv.mainClass = "net.minecraft.launchwrapper.Launch";
mv.libraries.add(1, new MinecraftLibrary("net.minecraft:launchwrapper:1.7"));
}
if (!mv.minecraftArguments.contains("FMLTweaker"))
mv.minecraftArguments += " --tweakClass optifine.OptiFineTweaker";
}
}
File loc = new File(service.baseDirectory(), "versions/" + mv.id);
if (!FileUtils.makeDirectory(loc))
HMCLog.warn("Failed to make directories: " + loc);
File json = new File(loc, mv.id + ".json");
FileUtils.write(json, C.GSON.toJson(mv, MinecraftVersion.class));
service.version().refreshVersions();
}
@Override
public String getInfo() {
return "OptiFine Installer";
}
ArrayList<PreviousResult<File>> pre = new ArrayList<>();
@Override
public Task registerPreviousResult(PreviousResult<File> pr) {
pre.add(pr);
return this;
}
}

View File

@@ -0,0 +1,68 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 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.core.install.optifine;
/**
*
* @author huangyuhui
*/
public class OptiFineVersion {
private String dl, ver, date, mirror, mcversion;
public String patch, type; // For BMCLAPI2.
public String getDownloadLink() {
return dl;
}
public void setDownloadLink(String dl) {
this.dl = dl;
}
public String getVersion() {
return ver;
}
public void setVersion(String ver) {
this.ver = ver;
}
public String getDate() {
return date;
}
public void setDate(String date) {
this.date = date;
}
public String getMirror() {
return mirror;
}
public void setMirror(String mirror) {
this.mirror = mirror;
}
public String getMCVersion() {
return mcversion;
}
public void setMCVersion(String mcver) {
this.mcversion = mcver;
}
}

View File

@@ -0,0 +1,104 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 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.core.install.optifine.bmcl;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import org.jackhuang.hmcl.util.C;
import org.jackhuang.hmcl.util.ArrayUtils;
import org.jackhuang.hmcl.core.install.InstallerVersionList;
import org.jackhuang.hmcl.core.install.optifine.OptiFineVersion;
import org.jackhuang.hmcl.util.StrUtils;
import org.jackhuang.hmcl.util.task.Task;
import org.jackhuang.hmcl.util.task.TaskInfo;
import org.jackhuang.hmcl.util.net.HTTPGetTask;
/**
*
* @author huangyuhui
*/
public class OptiFineBMCLVersionList extends InstallerVersionList {
private static OptiFineBMCLVersionList instance;
public static OptiFineBMCLVersionList getInstance() {
if (null == instance)
instance = new OptiFineBMCLVersionList();
return instance;
}
public ArrayList<OptiFineVersion> root;
private static final Type TYPE = new TypeToken<ArrayList<OptiFineVersion>>() {
}.getType();
@Override
public Task refresh(String[] needed) {
return new TaskInfo(C.i18n("install.optifine.get_list")) {
HTTPGetTask task = new HTTPGetTask("http://bmclapi.bangbang93.com/optifine/versionlist");
@Override
public Collection<Task> getDependTasks() {
return Arrays.asList(task.setTag("BMCL Optifine Download Site"));
}
@Override
public void executeTask(boolean areDependTasksSucceeded) throws Throwable {
String s = task.getResult();
versionMap = new HashMap<>();
versions = new ArrayList<>();
HashSet<String> duplicates = new HashSet<>();
if (s == null)
return;
root = C.GSON.fromJson(s, TYPE);
for (OptiFineVersion v : root) {
v.setVersion(v.type + '_' + v.patch);
v.setMirror(String.format("http://bmclapi2.bangbang93.com/optifine/%s/%s/%s", v.getMCVersion(), v.type, v.patch));
if (duplicates.contains(v.getMirror()))
continue;
else
duplicates.add(v.getMirror());
InstallerVersion iv = new InstallerVersion(v.getVersion(), StrUtils.formatVersion(v.getMCVersion()));
List<InstallerVersion> al = ArrayUtils.tryGetMapWithList(versionMap, StrUtils.formatVersion(v.getMCVersion()));
iv.installer = iv.universal = v.getMirror();
al.add(iv);
versions.add(iv);
}
Collections.sort(versions, InstallerVersionComparator.INSTANCE);
}
};
}
@Override
public String getName() {
return "OptiFine - BMCLAPI(By: bangbang93)";
}
}

View File

@@ -0,0 +1,58 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 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.core.install.optifine.vanilla;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.jackhuang.hmcl.util.C;
import org.jackhuang.hmcl.util.task.Task;
import org.jackhuang.hmcl.util.task.comm.PreviousResult;
import org.jackhuang.hmcl.util.net.NetUtils;
/**
*
* @author huangyuhui
*/
public class OptiFineDownloadFormatter extends Task implements PreviousResult<String> {
String url, result;
public OptiFineDownloadFormatter(String url) {
this.url = url;
}
@Override
public void executeTask(boolean areDependTasksSucceeded) throws Exception {
String content = NetUtils.get(url);
Pattern p = Pattern.compile("\"downloadx\\?f=OptiFine(.*)\"");
Matcher m = p.matcher(content);
while (m.find())
result = m.group(1);
result = "http://optifine.net/downloadx?f=OptiFine" + result;
}
@Override
public String getInfo() {
return C.i18n("install.optifine.get_download_link");
}
@Override
public String getResult() {
return result;
}
}

View File

@@ -0,0 +1,138 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 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.core.install.optifine.vanilla;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.jackhuang.hmcl.core.install.InstallerVersionList;
import org.jackhuang.hmcl.core.install.optifine.OptiFineVersion;
import org.jackhuang.hmcl.util.ArrayUtils;
import org.jackhuang.hmcl.util.C;
import org.jackhuang.hmcl.util.StrUtils;
import org.jackhuang.hmcl.util.task.Task;
import org.jackhuang.hmcl.util.task.TaskInfo;
import org.jackhuang.hmcl.util.net.HTTPGetTask;
import org.w3c.dom.DOMException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
/**
*
* @author huangyuhui
*/
public class OptiFineVersionList extends InstallerVersionList {
private static OptiFineVersionList instance;
public static OptiFineVersionList getInstance() {
if (null == instance)
instance = new OptiFineVersionList();
return instance;
}
public ArrayList<OptiFineVersion> root = new ArrayList<>();
@Override
public Task refresh(String[] sss) {
if (versions != null)
return null;
return new TaskInfo(C.i18n("install.optifine.get_list")) {
HTTPGetTask task = new HTTPGetTask("http://optifine.net/downloads");
@Override
public Collection<Task> getDependTasks() {
return Arrays.asList(task.setTag("Optifine Download Site"));
}
@Override
public void executeTask(boolean areDependTasksSucceeded) throws Throwable {
if (!areDependTasksSucceeded)
return;
String content = task.getResult();
versionMap = new HashMap<>();
versions = new ArrayList<>();
content = content.replace("&nbsp;", " ").replace("&gt;", ">").replace("&lt;", "<").replace("<br>", "<br />");
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder db = factory.newDocumentBuilder();
Document doc = db.parse(new ByteArrayInputStream(content.getBytes("UTF-8")));
Element r = doc.getDocumentElement();
NodeList tables = r.getElementsByTagName("table");
for (int i = 0; i < tables.getLength(); i++) {
Element e = (Element) tables.item(i);
if ("downloadTable".equals(e.getAttribute("class"))) {
NodeList tr = e.getElementsByTagName("tr");
for (int k = 0; k < tr.getLength(); k++) {
NodeList downloadLine = ((Element) tr.item(k)).getElementsByTagName("td");
OptiFineVersion v = new OptiFineVersion();
for (int j = 0; j < downloadLine.getLength(); j++) {
Element td = (Element) downloadLine.item(j);
if (StrUtils.startsWith(td.getAttribute("class"), "downloadLineMirror"))
v.setMirror(((Element) td.getElementsByTagName("a").item(0)).getAttribute("href"));
if (StrUtils.startsWith(td.getAttribute("class"), "downloadLineDownload"))
v.setDownloadLink(((Element) td.getElementsByTagName("a").item(0)).getAttribute("href"));
if (StrUtils.startsWith(td.getAttribute("class"), "downloadLineDate"))
v.setDate(td.getTextContent());
if (StrUtils.startsWith(td.getAttribute("class"), "downloadLineFile"))
v.setVersion(td.getTextContent());
}
if (StrUtils.isBlank(v.getMCVersion())) {
Pattern p = Pattern.compile("OptiFine (.*?) ");
Matcher m = p.matcher(v.getVersion());
while (m.find())
v.setMCVersion(StrUtils.formatVersion(m.group(1)));
}
InstallerVersion iv = new InstallerVersion(v.getVersion(), StrUtils.formatVersion(v.getMCVersion()));
iv.installer = iv.universal = v.getMirror();
root.add(v);
versions.add(iv);
List<InstallerVersion> ivl = ArrayUtils.tryGetMapWithList(versionMap, StrUtils.formatVersion(v.getMCVersion()));
ivl.add(iv);
}
}
}
} catch (ParserConfigurationException | SAXException | IOException | DOMException ex) {
throw new RuntimeException(ex);
}
Collections.sort(versions, InstallerVersionComparator.INSTANCE);
}
};
}
@Override
public String getName() {
return "OptiFine - OptiFine Official Site";
}
}

View File

@@ -0,0 +1,203 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 huangyuhui
*
* 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.core.launch;
import org.jackhuang.hmcl.api.game.LaunchOptions;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.jackhuang.hmcl.util.C;
import org.jackhuang.hmcl.api.HMCLog;
import org.jackhuang.hmcl.util.sys.JdkVersion;
import org.jackhuang.hmcl.util.MathUtils;
import org.jackhuang.hmcl.util.MessageBox;
import org.jackhuang.hmcl.util.sys.OS;
import org.jackhuang.hmcl.util.sys.Platform;
import org.jackhuang.hmcl.util.StrUtils;
import org.jackhuang.hmcl.core.GameException;
import org.jackhuang.hmcl.api.auth.UserProfileProvider;
import org.jackhuang.hmcl.core.version.MinecraftVersion;
import org.jackhuang.hmcl.core.service.IMinecraftService;
import org.jackhuang.hmcl.core.service.IMinecraftLoader;
/**
*
* @author huangyuhui
*/
public abstract class AbstractMinecraftLoader implements IMinecraftLoader {
protected LaunchOptions options;
protected UserProfileProvider lr;
protected File gameDir;
protected IMinecraftService service;
protected final MinecraftVersion version;
public AbstractMinecraftLoader(LaunchOptions options, IMinecraftService service, String versionId, UserProfileProvider lr) throws GameException {
if (service.version().getVersionById(versionId) == null)
throw new GameException("No version: " + versionId);
this.lr = lr;
this.options = options;
this.service = service;
this.gameDir = service.baseDirectory();
this.version = service.version().getVersionById(versionId).resolve(service.version());
}
@Override
public MinecraftVersion getMinecraftVersion() {
return version;
}
public void makeHeadCommand(List<String> res) {
HMCLog.log("On making head command.");
if (StrUtils.isNotBlank(options.getWrapper()))
res.add(options.getWrapper());
JdkVersion jv = null;
try {
jv = JdkVersion.getJavaVersionFromExecutable(options.getJavaDir());
} catch (IOException ex) {
HMCLog.err("Failed to read java version", ex);
}
res.add(options.getJavaDir());
if (options.hasJavaArgs())
res.addAll(Arrays.asList(StrUtils.tokenize(options.getJavaArgs())));
if (!options.isNoJVMArgs()) {
appendJVMArgs(res);
if (jv == null || !jv.isEarlyAccess()) {
if (OS.os() == OS.WINDOWS)
res.add("-XX:HeapDumpPath=MojangTricksIntelDriversForPerformance_javaw.exe_minecraft.exe.heapdump");
if (jv != null && jv.getParsedVersion() >= JdkVersion.JAVA_17)
res.add("-XX:+UseG1GC");
else
res.add("-Xincgc");
res.add("-XX:-UseAdaptiveSizePolicy");
res.add("-XX:-OmitStackTraceInFastThrow");
res.add("-Xmn128m");
}
if (!StrUtils.isBlank(options.getPermSize()))
if (jv == null || jv.getParsedVersion() < JdkVersion.JAVA_18)
res.add("-XX:PermSize=" + options.getPermSize() + "m");
else if (jv.getParsedVersion() >= JdkVersion.JAVA_18)
res.add("-XX:MetaspaceSize=" + options.getPermSize() + "m");
}
if (jv != null) {
HMCLog.log("Java Version: " + jv.getVersion());
HMCLog.log("Java Platform: " + jv.getPlatform().getBit());
}
HMCLog.log("System Platform: " + Platform.getPlatform().getBit());
if (jv != null && jv.getPlatform() == Platform.BIT_32 && Platform.getPlatform() == Platform.BIT_64)
MessageBox.show(C.i18n("advice.os64butjdk32"));
if (!StrUtils.isBlank(options.getMaxMemory())) {
int mem = MathUtils.parseMemory(options.getMaxMemory(), 2147483647);
if (jv != null && jv.getPlatform() == Platform.BIT_32 && mem > 1024)
MessageBox.show(C.i18n("launch.too_big_memory_alloc_64bit"));
else {
long a = OS.getTotalPhysicalMemory() / 1024 / 1024;
HMCLog.log("System Physical Memory: " + a);
if (a > 0 && a < mem)
MessageBox.show(C.i18n("launch.too_big_memory_alloc_free_space_too_low", a));
}
String a = "-Xmx" + options.getMaxMemory();
if (MathUtils.canParseInt(options.getMaxMemory()))
a += "m";
res.add(a);
}
res.add("-Djava.library.path=" + service.version().getDecompressNativesToLocation(version).getPath());
res.add("-Dfml.ignoreInvalidMinecraftCertificates=true");
res.add("-Dfml.ignorePatchDiscrepancies=true");
if (OS.os() != OS.WINDOWS)
res.add("-Duser.home=" + gameDir.getParent());
}
@Override
public List<String> makeLaunchingCommand() throws GameException {
HMCLog.log("*** Make shell command ***");
ArrayList<String> res = new ArrayList<>();
makeHeadCommand(res);
makeSelf(res);
HMCLog.log("On making launcher args.");
if (StrUtils.isNotBlank(options.getHeight()) && StrUtils.isNotBlank(options.getWidth())) {
res.add("--height");
res.add(options.getHeight());
res.add("--width");
res.add(options.getWidth());
}
String serverIp = options.getServerIp();
if (StrUtils.isNotBlank(serverIp)) {
String[] args = serverIp.split(":");
res.add("--server");
res.add(args[0]);
res.add("--port");
res.add(args.length > 1 ? args[1] : "25565");
}
if (options.isFullscreen())
res.add("--fullscreen");
if (StrUtils.isNotBlank(options.getProxyHost()) && StrUtils.isNotBlank(options.getProxyPort()) && MathUtils.canParseInt(options.getProxyPort())) {
res.add("--proxyHost");
res.add(options.getProxyHost());
res.add("--proxyPort");
res.add(options.getProxyPort());
if (StrUtils.isNotBlank(options.getProxyUser()) && StrUtils.isNotBlank(options.getProxyPass())) {
res.add("--proxyUser");
res.add(options.getProxyUser());
res.add("-=proxyPass");
res.add(options.getProxyPass());
}
}
if (StrUtils.isNotBlank(options.getMinecraftArgs()))
res.addAll(Arrays.asList(options.getMinecraftArgs().split(" ")));
return res;
}
/**
* You must do these things:
* <ul>
* <li>minecraft class path</li>
* <li>main class</li>
* <li>minecraft arguments</li>
* </ul>
*
* @param list the command list you shoud edit.
*/
protected abstract void makeSelf(List<String> list) throws GameException;
protected void appendJVMArgs(List<String> list) {
}
}

View File

@@ -0,0 +1,81 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 huangyuhui
*
* 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.core.launch;
import org.jackhuang.hmcl.api.game.LaunchOptions;
import java.io.IOException;
import java.util.HashSet;
import java.util.List;
import org.jackhuang.hmcl.api.HMCLApi;
import org.jackhuang.hmcl.api.event.ResultedSimpleEvent;
import org.jackhuang.hmcl.api.event.launch.DecompressLibrariesEvent;
import org.jackhuang.hmcl.api.event.launch.DownloadLibrariesEvent;
import org.jackhuang.hmcl.api.event.launch.DownloadLibraryJob;
import org.jackhuang.hmcl.api.auth.LoginInfo;
import org.jackhuang.hmcl.core.service.IMinecraftService;
import org.jackhuang.hmcl.util.C;
import org.jackhuang.hmcl.util.MessageBox;
import org.jackhuang.hmcl.api.HMCLog;
import org.jackhuang.hmcl.util.sys.CompressingUtils;
import org.jackhuang.hmcl.util.task.ParallelTask;
import org.jackhuang.hmcl.util.task.TaskWindow;
import org.jackhuang.hmcl.api.auth.IAuthenticator;
public class DefaultGameLauncher extends GameLauncher {
public DefaultGameLauncher(LaunchOptions options, IMinecraftService service, LoginInfo info, IAuthenticator lg) {
super(options, service, info, lg);
register();
}
private void register() {
HMCLApi.EVENT_BUS.channel(DownloadLibrariesEvent.class).register(t -> {
ResultedSimpleEvent<List<DownloadLibraryJob>> event = (ResultedSimpleEvent) t;
final TaskWindow.TaskWindowFactory dw = TaskWindow.factory();
ParallelTask parallelTask = new ParallelTask();
HashSet<String> names = new HashSet<>();
for (DownloadLibraryJob s : t.getValue()) {
if (names.contains(s.lib.getName()))
continue;
names.add(s.lib.getName());
parallelTask.addTask(new LibraryDownloadTask(s));
}
dw.append(parallelTask);
boolean flag = true;
if (t.getValue().size() > 0)
flag = dw.execute();
if (!flag && MessageBox.show(C.i18n("launch.not_finished_downloading_libraries"), MessageBox.YES_NO_OPTION) == MessageBox.YES_OPTION)
flag = true;
t.setResult(flag);
});
HMCLApi.EVENT_BUS.channel(DecompressLibrariesEvent.class).register(t -> {
if (t.getValue() == null) {
t.setResult(false);
return;
}
for (int i = 0; i < t.getValue().decompressFiles.length; i++)
try {
CompressingUtils.unzip(t.getValue().decompressFiles[i], t.getValue().getDecompressTo(), t.getValue().extractRules[i]::allow, false);
} catch (IOException ex) {
HMCLog.err("Unable to decompress library: " + t.getValue().decompressFiles[i] + " to " + t.getValue().getDecompressTo(), ex);
}
});
}
}

View File

@@ -0,0 +1,216 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 huangyuhui
*
* 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.core.launch;
import org.jackhuang.hmcl.api.game.LaunchOptions;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.List;
import org.jackhuang.hmcl.api.HMCLApi;
import org.jackhuang.hmcl.api.event.launch.DecompressLibrariesEvent;
import org.jackhuang.hmcl.api.game.DecompressLibraryJob;
import org.jackhuang.hmcl.api.event.launch.DownloadLibrariesEvent;
import org.jackhuang.hmcl.api.event.launch.LaunchEvent;
import org.jackhuang.hmcl.api.event.launch.LaunchSucceededEvent;
import org.jackhuang.hmcl.api.event.launch.LaunchingState;
import org.jackhuang.hmcl.api.event.launch.LaunchingStateChangedEvent;
import org.jackhuang.hmcl.api.event.launch.ProcessingLoginResultEvent;
import org.jackhuang.hmcl.core.GameException;
import org.jackhuang.hmcl.core.RuntimeGameException;
import org.jackhuang.hmcl.api.auth.AuthenticationException;
import org.jackhuang.hmcl.api.auth.LoginInfo;
import org.jackhuang.hmcl.api.auth.UserProfileProvider;
import org.jackhuang.hmcl.core.service.IMinecraftLoader;
import org.jackhuang.hmcl.core.service.IMinecraftService;
import org.jackhuang.hmcl.util.C;
import org.jackhuang.hmcl.util.StrUtils;
import org.jackhuang.hmcl.util.code.Charsets;
import org.jackhuang.hmcl.api.HMCLog;
import org.jackhuang.hmcl.util.sys.FileUtils;
import org.jackhuang.hmcl.util.sys.IOUtils;
import org.jackhuang.hmcl.util.sys.JavaProcess;
import org.jackhuang.hmcl.util.sys.OS;
import org.jackhuang.hmcl.api.auth.IAuthenticator;
public class GameLauncher {
LaunchOptions options;
IMinecraftService service;
LoginInfo info;
UserProfileProvider result;
IAuthenticator login;
public GameLauncher(LaunchOptions options, IMinecraftService version, LoginInfo info, IAuthenticator lg) {
this.options = options;
this.service = version;
this.info = info;
this.login = lg;
}
public LaunchOptions getOptions() {
return options;
}
public IMinecraftService getService() {
return service;
}
public LoginInfo getInfo() {
return info;
}
public UserProfileProvider getLoginResult() {
return result;
}
private Object tag;
public Object getTag() {
return tag;
}
public void setTag(Object tag) {
this.tag = tag;
}
/**
* Generates the launch command.
* @throws AuthenticationException having trouble logging in.
* @throws GameException having trouble completing the game or making lanch command.
* @throws RuntimeGameException will be thrown when someone processing login result.
* @see LaunchingStateChangedEvent
* @see DecompressLibrariesEvent
* @see LaunchSucceededEvent
* @see DownloadLibrariesEvent
* @see ProcessingLoginResultEvent
*/
public void makeLaunchCommand() throws AuthenticationException, GameException, RuntimeGameException {
HMCLog.log("Building process");
HMCLog.log("Logging in...");
HMCLApi.EVENT_BUS.fireChannel(new LaunchingStateChangedEvent(this, LaunchingState.LoggingIn));
IMinecraftLoader loader;
if (info != null)
result = login.login(info);
else
result = login.loginBySettings();
if (result == null)
throw new AuthenticationException("Result can not be null.");
HMCLApi.EVENT_BUS.fireChannel(new ProcessingLoginResultEvent(this, result));
HMCLApi.EVENT_BUS.fireChannel(new LaunchingStateChangedEvent(this, LaunchingState.GeneratingLaunchingCodes));
loader = service.launch(options, result);
File file = service.version().getDecompressNativesToLocation(loader.getMinecraftVersion());
if (file != null)
FileUtils.cleanDirectoryQuietly(file);
if (!options.isNotCheckGame()) {
HMCLog.log("Detecting libraries...");
HMCLApi.EVENT_BUS.fireChannel(new LaunchingStateChangedEvent(this, LaunchingState.DownloadingLibraries));
if (!HMCLApi.EVENT_BUS.fireChannelResulted(new DownloadLibrariesEvent(this, service.download().getDownloadLibraries(loader.getMinecraftVersion()))))
throw new GameException("Failed to download libraries");
}
HMCLog.log("Unpacking natives...");
HMCLApi.EVENT_BUS.fireChannel(new LaunchingStateChangedEvent(this, LaunchingState.DecompressingNatives));
DecompressLibraryJob job = service.version().getDecompressLibraries(loader.getMinecraftVersion());
if (!HMCLApi.EVENT_BUS.fireChannelResulted(new DecompressLibrariesEvent(this, job)))
throw new GameException("Failed to decompress natives");
HMCLApi.EVENT_BUS.fireChannel(new LaunchSucceededEvent(this, loader.makeLaunchingCommand()));
}
/**
* Launch the game "as soon as possible".
*
* @param str launch command
* @throws IOException failed creating process
*/
public void launch(List<String> str) throws IOException {
if (!service.version().onLaunch(options.getLaunchVersion()))
return;
if (StrUtils.isNotBlank(options.getPrecalledCommand())) {
Process p = Runtime.getRuntime().exec(options.getPrecalledCommand());
try {
if (p.isAlive())
p.waitFor();
} catch (InterruptedException ex) {
HMCLog.warn("Failed to invoke precalled command", ex);
}
}
HMCLog.log("Starting process");
HMCLog.log(str.toString());
ProcessBuilder builder = new ProcessBuilder(str);
if (options.getLaunchVersion() == null || service.baseDirectory() == null)
throw new Error("Fucking bug!");
builder.redirectErrorStream(true).directory(service.version().getRunDirectory(options.getLaunchVersion()))
.environment().put("APPDATA", service.baseDirectory().getAbsolutePath());
JavaProcess jp = new JavaProcess(str, builder.start());
HMCLog.log("Have started the process");
HMCLApi.EVENT_BUS.fireChannel(new LaunchEvent(this, jp));
}
/**
* According to the name...
*
* @param launcherName the name of launch bat/sh
* @param str launch command
*
* @return launcher location
*
* @throws java.io.IOException write contents failed.
*/
public File makeLauncher(String launcherName, List<String> str) throws IOException {
HMCLog.log("Making shell launcher...");
service.version().onLaunch(options.getLaunchVersion());
boolean isWin = OS.os() == OS.WINDOWS;
File f = new File(launcherName + (isWin ? ".bat" : ".sh"));
if (!f.exists() && !f.createNewFile())
HMCLog.warn("Failed to create " + f);
BufferedWriter writer;
try (FileOutputStream fos = FileUtils.openOutputStream(f)) {
writer = new BufferedWriter(new OutputStreamWriter(fos, Charsets.toCharset()));
if (isWin) {
writer.write("@echo off");
writer.newLine();
String appdata = IOUtils.tryGetCanonicalFilePath(service.baseDirectory());
if (appdata != null) {
writer.write("set appdata=" + appdata);
writer.newLine();
writer.write("cd /D %appdata%");
writer.newLine();
}
}
if (StrUtils.isNotBlank(options.getPrecalledCommand())) {
writer.write(options.getPrecalledCommand());
writer.newLine();
}
writer.write(StrUtils.makeCommand(str));
writer.close();
}
if (!f.setExecutable(true))
throw new IOException(C.i18n("launch.failed_sh_permission"));
HMCLog.log("Command: " + StrUtils.parseParams("", str, " "));
return f;
}
}

View File

@@ -0,0 +1,30 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 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.core.launch;
import org.jackhuang.hmcl.core.GameException;
import org.jackhuang.hmcl.core.version.MinecraftVersion;
/**
*
* @author huang
*/
public interface IAssetProvider {
String provide(MinecraftVersion mv, Boolean allowChecking) throws GameException;
}

View File

@@ -0,0 +1,42 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 huangyuhui
*
* 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.core.launch;
import org.jackhuang.hmcl.util.C;
import org.jackhuang.hmcl.api.event.launch.DownloadLibraryJob;
import org.jackhuang.hmcl.util.net.FileDownloadTask;
/**
*
* @author huangyuhui
*/
public class LibraryDownloadTask extends FileDownloadTask {
DownloadLibraryJob job;
public LibraryDownloadTask(DownloadLibraryJob job) {
super(job.parse().url, job.path);
this.job = job;
}
@Override
public String getInfo() {
return C.i18n("download") + ": " + job.lib.getName();
}
}

View File

@@ -0,0 +1,129 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 huangyuhui
*
* 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.core.launch;
import org.jackhuang.hmcl.api.game.LaunchOptions;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import org.jackhuang.hmcl.util.StrUtils;
import org.jackhuang.hmcl.api.HMCLog;
import org.jackhuang.hmcl.util.sys.IOUtils;
import org.jackhuang.hmcl.util.sys.OS;
import org.jackhuang.hmcl.core.GameException;
import org.jackhuang.hmcl.api.auth.UserProfileProvider;
import org.jackhuang.hmcl.core.version.MinecraftLibrary;
import org.jackhuang.hmcl.core.service.IMinecraftService;
/**
*
* @author huangyuhui
*/
public class MinecraftLoader extends AbstractMinecraftLoader {
public MinecraftLoader(LaunchOptions p, IMinecraftService provider, UserProfileProvider lr) throws GameException {
super(p, provider, p.getLaunchVersion(), lr);
}
@Override
protected void makeSelf(List<String> res) throws GameException {
StringBuilder library = new StringBuilder("");
ArrayList<MinecraftLibrary> opt = new ArrayList<>();
for (MinecraftLibrary l : version.libraries)
if (l.allow() && !l.isRequiredToUnzip()) {
if (l.getName().toLowerCase(Locale.US).contains("optifine")) {
opt.add(l);
continue;
}
File f = l.getFilePath(gameDir);
if (f == null)
continue;
library.append(f.getAbsolutePath()).append(File.pathSeparator);
}
for (MinecraftLibrary l : opt) {
File f = l.getFilePath(gameDir);
if (f == null)
continue;
library.append(f.getAbsolutePath()).append(File.pathSeparator);
}
File f = version.getJar(service.baseDirectory());
if (!f.exists())
throw new GameException("Minecraft jar does not exists");
library.append(IOUtils.tryGetCanonicalFilePath(f)).append(File.pathSeparator);
res.add("-cp");
res.add(library.toString().substring(0, library.length() - File.pathSeparator.length()));
res.add(version.mainClass);
if (version.minecraftArguments == null)
throw new GameException(new NullPointerException("Minecraft Arguments can not be null."));
String[] splitted = StrUtils.tokenize(version.minecraftArguments);
String game_assets = assetProvider.provide(version, !options.isNotCheckGame());
for (String t : splitted) {
t = t.replace("${auth_player_name}", lr.getUserName());
t = t.replace("${auth_session}", lr.getSession());
t = t.replace("${auth_uuid}", lr.getUserId());
t = t.replace("${version_name}", options.getVersionName());
t = t.replace("${profile_name}", options.getName());
t = t.replace("${version_type}", options.getType());
t = t.replace("${game_directory}", service.version().getRunDirectory(version.id).getAbsolutePath());
t = t.replace("${game_assets}", game_assets);
t = t.replace("${assets_root}", service.asset().getAssets(version.getAssetsIndex().getId()).getAbsolutePath());
t = t.replace("${auth_access_token}", lr.getAccessToken());
t = t.replace("${user_type}", lr.getUserType());
t = t.replace("${assets_index_name}", version.getAssetsIndex().getId());
t = t.replace("${user_properties}", lr.getUserProperties());
res.add(t);
}
if (res.indexOf("--gameDir") != -1 && res.indexOf("--workDir") != -1) {
res.add("--workDir");
res.add(gameDir.getAbsolutePath());
}
}
@Override
protected void appendJVMArgs(List<String> list) {
super.appendJVMArgs(list);
try {
if (OS.os() == OS.OSX) {
list.add("-Xdock:icon=" + service.asset().getAssetObject(version.getAssetsIndex().getId(), "icons/minecraft.icns").getAbsolutePath());
list.add("-Xdock:name=Minecraft");
}
} catch (IOException e) {
HMCLog.err("Failed to append jvm arguments when searching for asset objects.", e);
}
}
private final IAssetProvider DEFAULT_ASSET_PROVIDER = (t, allow) -> {
return new File(service.baseDirectory(), "assets").getAbsolutePath();
};
private IAssetProvider assetProvider = DEFAULT_ASSET_PROVIDER;
public void setAssetProvider(IAssetProvider assetProvider) {
if (assetProvider == null)
this.assetProvider = DEFAULT_ASSET_PROVIDER;
else
this.assetProvider = assetProvider;
}
}

View File

@@ -0,0 +1,114 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 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.core.mod;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.jackhuang.hmcl.api.HMCLog;
import org.jackhuang.hmcl.core.service.IMinecraftModService;
import org.jackhuang.hmcl.core.service.IMinecraftService;
import org.jackhuang.hmcl.util.sys.FileUtils;
/**
*
* @author huangyuhui
*/
public class MinecraftModService extends IMinecraftModService {
Map<String, List<ModInfo>> modCache = Collections.synchronizedMap(new HashMap<>());
public MinecraftModService(IMinecraftService service) {
super(service);
}
@Override
public List<ModInfo> getMods(String id) {
if (modCache.containsKey(id))
return modCache.get(id);
else
return recacheMods(id);
}
@Override
public List<ModInfo> recacheMods(String id) {
File modsFolder = service.version().getRunDirectory(id, "mods");
ArrayList<ModInfo> mods = new ArrayList<>();
File[] fs = modsFolder.listFiles();
if (fs != null)
for (File f : fs)
if (ModInfo.isFileMod(f))
mods.add(ModInfo.readModInfo(f));
else if (f.isDirectory()) {
File[] ss = f.listFiles();
if (ss != null)
for (File ff : ss)
if (ModInfo.isFileMod(ff))
mods.add(ModInfo.readModInfo(ff));
}
Collections.sort(mods);
modCache.put(id, mods);
return mods;
}
@Override
public boolean addMod(String id, File f) {
try {
if (!ModInfo.isFileMod(f))
return false;
if (!modCache.containsKey(id))
recacheMods(id);
File modsFolder = service.version().getRunDirectory(id, "mods");
if (!FileUtils.makeDirectory(modsFolder))
HMCLog.warn("Failed to make directories: " + modsFolder);
File newf = new File(modsFolder, f.getName());
FileUtils.copyFile(f, newf);
ModInfo i = ModInfo.readModInfo(f);
modCache.get(id).add(i);
return true;
} catch (IOException ex) {
HMCLog.warn("Failed to copy mod", ex);
return false;
}
}
@Override
public boolean removeMod(String id, Object[] rows) {
if (rows.length == 0)
return true;
boolean flag = true;
for (Object r : rows)
if (r instanceof ModInfo) {
if (!((ModInfo) r).location.delete()) {
HMCLog.warn("Failed to delete mod" + r);
flag = false;
}
} else if (r instanceof Number)
if (!getMods(id).get(((Number) r).intValue()).location.delete()) {
HMCLog.warn("Failed to delete mod " + r + ", maybe not a file?");
flag = false;
}
recacheMods(id);
return flag;
}
}

View File

@@ -0,0 +1,34 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 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.core.mod;
/**
*
* @author huang
*/
public interface ModAdviser {
ModSuggestion advise(String fileName, boolean isDirectory);
public static enum ModSuggestion {
SUGGESTED,
NORMAL,
HIDDEN
}
}

View File

@@ -0,0 +1,186 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 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.core.mod;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.JsonSyntaxException;
import com.google.gson.annotations.SerializedName;
import com.google.gson.reflect.TypeToken;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import org.jackhuang.hmcl.util.C;
import org.jackhuang.hmcl.api.HMCLog;
import org.jackhuang.hmcl.util.StrUtils;
import org.jackhuang.hmcl.util.sys.FileUtils;
/**
*
* @author huangyuhui
*/
public class ModInfo implements Comparable<ModInfo> {
@SerializedName("location")
public File location;
@SerializedName("modid")
public String modid;
@SerializedName("name")
public String name;
@SerializedName("description")
public String description;
@SerializedName("author")
public String author;
@SerializedName("version")
public String version;
@SerializedName("mcversion")
public String mcversion;
@SerializedName("url")
public String url;
@SerializedName("updateUrl")
public String updateUrl;
@SerializedName("credits")
public String credits;
@SerializedName("authorList")
public String[] authorList;
@SerializedName("authors")
public String[] authors;
public boolean isActive() {
return !location.getName().endsWith(".disabled");
}
public void reverseModState() {
File f = location, newf;
if (f.getName().endsWith(".disabled"))
newf = new File(f.getParentFile(), f.getName().substring(0, f.getName().length() - ".disabled".length()));
else
newf = new File(f.getParentFile(), f.getName() + ".disabled");
if (f.renameTo(newf))
location = newf;
}
@Override
public int compareTo(ModInfo o) {
return getFileName().compareToIgnoreCase(o.getFileName());
}
public String getName() {
return name == null ? FileUtils.removeExtension(location.getName()) : name;
}
public String getAuthor() {
if (authorList != null && authorList.length > 0)
return StrUtils.parseParams("", authorList, ", ");
else if (authors != null && authors.length > 0)
return StrUtils.parseParams("", authors, ", ");
else if (StrUtils.isNotBlank(author))
return author;
else
return "Unknown";
}
@Override
public boolean equals(Object obj) {
return obj != null && obj instanceof ModInfo && (((ModInfo) obj).location == location || ((ModInfo) obj).location.equals(location));
}
@Override
public int hashCode() {
return location.hashCode();
}
@Override
public String toString() {
return getFileName();
}
public String getFileName() {
String n = location.getName();
return FileUtils.removeExtension(isActive() ? n : n.substring(0, n.length() - ".disabled".length()));
}
public static boolean isFileMod(File file) {
if (file == null)
return false;
String name = file.getName();
boolean disabled = name.endsWith(".disabled");
if (disabled)
name = name.substring(0, name.length() - ".disabled".length());
return name.endsWith(".zip") || name.endsWith(".jar") || name.endsWith("litemod");
}
private static ModInfo getForgeModInfo(File f, ZipFile jar, ZipEntry entry) throws IOException, JsonSyntaxException {
ModInfo i = new ModInfo();
i.location = f;
InputStreamReader streamReader = new InputStreamReader(jar.getInputStream(entry), "UTF-8");
JsonParser parser = new JsonParser();
JsonElement element = parser.parse(streamReader);
List<ModInfo> m = null;
if (element.isJsonArray())
m = C.GSON.fromJson(element, new TypeToken<List<ModInfo>>() {
}.getType());
else if (element.isJsonObject()) {
JsonObject modInfo = element.getAsJsonObject();
if (modInfo.has("modList") && modInfo.get("modList").isJsonArray())
m = C.GSON.fromJson(modInfo.get("modList"), new TypeToken<List<ModInfo>>() {
}.getType());
}
if (m != null && m.size() > 0) {
i = m.get(0);
i.location = f;
}
return i;
}
private static ModInfo getLiteLoaderModInfo(File f, ZipFile jar, ZipEntry entry) throws IOException {
ModInfo m = C.GSON.fromJson(new InputStreamReader(jar.getInputStream(entry), "UTF-8"), ModInfo.class);
if (m == null)
m = new ModInfo();
m.location = f;
return m;
}
public static ModInfo readModInfo(File f) {
ModInfo i = new ModInfo();
i.location = f;
try {
try (ZipFile jar = new ZipFile(f)) {
ZipEntry entry = jar.getEntry("mcmod.info");
if (entry != null)
return getForgeModInfo(f, jar, entry);
entry = jar.getEntry("litemod.json");
if (entry != null)
return getLiteLoaderModInfo(f, jar, entry);
return i;
}
} catch (IOException ex) {
HMCLog.warn("File " + f + " is not a jar.", ex);
} catch (JsonSyntaxException ignore) {
}
return i;
}
}

View File

@@ -0,0 +1,273 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 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.core.mod;
import java.awt.Dimension;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.file.FileSystemException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.zip.ZipFile;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import org.jackhuang.hmcl.util.C;
import org.jackhuang.hmcl.api.HMCLog;
import org.jackhuang.hmcl.core.GameException;
import org.jackhuang.hmcl.core.service.IMinecraftProvider;
import org.jackhuang.hmcl.core.service.IMinecraftService;
import org.jackhuang.hmcl.core.version.MinecraftVersion;
import org.jackhuang.hmcl.api.func.CallbackIO;
import org.jackhuang.hmcl.util.sys.CompressingUtils;
import org.jackhuang.hmcl.util.sys.FileUtils;
import org.jackhuang.hmcl.util.sys.ZipEngine;
import org.jackhuang.hmcl.util.task.Task;
import org.jackhuang.hmcl.util.net.WebPage;
import org.jackhuang.hmcl.util.MinecraftVersionRequest;
import org.jackhuang.hmcl.util.sys.IOUtils;
import org.jackhuang.hmcl.util.task.NoShownTaskException;
/**
* A mod pack(*.zip) includes these things:
* <ul>
* <li>sth created by the game automatically, including "mods", "scripts",
* "config", etc..
* <li>pack.json, the same as Minecraft version configuration file:
* "gameDir/versions/{MCVER}/{MCVER}.json", will be renamed to that one.
* <li>all things should be included in "minecraft" folder under the root
* folder.
* </ul>
*
* This class can manage mod packs, for example, importing and exporting, the
* format of game is the offical one.
* Not compatible with MultiMC(no instance.cfg processor) & FTB(not leaving
* mcversion(jar) in pack.json).
*
* @author huangyuhui
*/
public final class ModpackManager {
/**
* Install the compressed modpack.
*
* @param input modpack.zip
* @param service MinecraftService, whose version service only supports
* MinecraftVersionManager.
* @param id new version id, if null, will use suggested name from
* modpack
*
* @return The installing Task, may take long time, please consider
* TaskWindow.
*/
public static Task install(JFrame parFrame, final File input, final IMinecraftService service, final String idFUCK) {
return new Task() {
Collection<Task> c = new ArrayList<>();
@Override
public void executeTask(boolean areDependTasksSucceeded) throws Throwable {
String id = idFUCK;
String description = C.i18n("modpack.task.install.will");
// Read modpack name and description from `modpack.json`
try (ZipFile zip = new ZipFile(input)) {
HashMap<String, String> map = C.GSON.fromJson(new InputStreamReader(zip.getInputStream(zip.getEntry("modpack.json")), "UTF-8"), HashMap.class);
if (map != null) {
if (id == null)
if (map.containsKey("name") && map.get("name") instanceof String)
id = map.get("name");
if (id != null)
description += id;
if (map.containsKey("description") && map.get("description") instanceof String)
description += "\n" + map.get("description");
}
if (id == null)
throw new IllegalStateException("Illegal modpack id!");
}
// Show a window to let the user know that what and how the modpack is.
Object msgs[] = new Object[2];
msgs[0] = C.i18n("modpack.task.install");
msgs[1] = new WebPage(description);
((WebPage) msgs[1]).setPreferredSize(new Dimension(800, 350));
int result = JOptionPane.showOptionDialog(parFrame, msgs, (String) msgs[0], JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null, null, null);
if (result == JOptionPane.NO_OPTION)
throw new NoShownTaskException("Operation was canceled by user.");
File versions = new File(service.baseDirectory(), "versions");
// `minecraft` folder is the existent root folder of the modpack
// Then we will decompress the modpack and there would be a folder named `minecraft`
// So if `minecraft` folder does exist, backup it and then restore it.
File oldFile = new File(versions, "minecraft"), newFile = null;
if (oldFile.exists()) {
newFile = new File(versions, "minecraft-" + System.currentTimeMillis());
if (newFile.isDirectory())
FileUtils.deleteDirectory(newFile);
else if (newFile.isFile())
if (!newFile.delete())
HMCLog.warn("Failed to delete file " + newFile);
if (!oldFile.renameTo(newFile))
HMCLog.warn("Failed to rename " + oldFile + " to " + newFile);
}
// If the user install the modpack into an existent version, maybe it wants to update the modpack
// So backup the game, copy the saved games.
File preVersion = new File(versions, id), preVersionRenamed = null;
if (preVersion.exists()) {
HMCLog.log("Backing up the game");
String preId = id + "-" + System.currentTimeMillis();
if (!preVersion.renameTo(preVersionRenamed = new File(versions, preId)))
HMCLog.warn("Failed to rename pre-version folder " + preVersion + " to a temp folder " + preVersionRenamed);
if (!new File(preVersionRenamed, id + ".json").renameTo(new File(preVersionRenamed, preId + ".json")))
HMCLog.warn("Failed to rename pre json to new json");
if (!new File(preVersionRenamed, id + ".jar").renameTo(new File(preVersionRenamed, preId + ".jar")))
HMCLog.warn("Failed to rename pre jar to new jar");
}
try {
final AtomicInteger b = new AtomicInteger(0);
HMCLog.log("Decompressing modpack");
CompressingUtils.unzip(input, versions, t -> {
if (t.equals("minecraft/pack.json"))
b.incrementAndGet();
return true;
}, true);
// No pack.json here, illegal modpack.
if (b.get() < 1)
throw new FileNotFoundException(C.i18n("modpack.incorrect_format.no_json"));
File nowFile = new File(versions, id);
if (oldFile.exists() && !oldFile.renameTo(nowFile))
HMCLog.warn("Failed to rename incorrect json " + oldFile + " to " + nowFile);
File json = new File(nowFile, "pack.json");
MinecraftVersion mv = C.GSON.fromJson(FileUtils.read(json), MinecraftVersion.class);
if (mv.jar == null)
throw new FileNotFoundException(C.i18n("modpack.incorrect_format.no_jar"));
c.add(service.download().downloadMinecraftJar(mv, new File(nowFile, id + ".jar")));
mv.jar = null;
FileUtils.write(json, C.GSON.toJson(mv));
if (!json.renameTo(new File(nowFile, id + ".json")))
HMCLog.warn("Failed to rename pack.json to new id");
// Restore the saved game from the old version.
if (preVersionRenamed != null) {
HMCLog.log("Restoring saves");
File presaves = new File(preVersionRenamed, "saves");
File saves = new File(nowFile, "saves");
if (presaves.exists()) {
FileUtils.deleteDirectory(saves);
FileUtils.copyDirectory(presaves, saves);
}
}
} finally {
FileUtils.deleteDirectoryQuietly(oldFile);
if (newFile != null && !newFile.renameTo(oldFile))
HMCLog.warn("Failed to restore version minecraft");
}
}
@Override
public String getInfo() {
return C.i18n("modpack.task.install");
}
@Override
public Collection<Task> getAfterTasks() {
return c;
}
};
}
public static final List<String> MODPACK_BLACK_LIST = Arrays.asList(new String[] { "usernamecache.json", "asm", "logs", "backups", "versions", "assets", "usercache.json", "libraries", "crash-reports", "launcher_profiles.json", "NVIDIA", "AMD", "TCNodeTracker", "screenshots", "natives", "native", "$native", "pack.json", "launcher.jar", "minetweaker.log", "launcher.pack.lzma", "hmclmc.log" });
public static final List<String> MODPACK_SUGGESTED_BLACK_LIST = Arrays.asList(new String[] { "fonts", "saves", "servers.dat", "options.txt", "optionsof.txt", "journeymap", "optionsshaders.txt", "mods/VoxelMods" });
public static ModAdviser MODPACK_PREDICATE = (String fileName, boolean isDirectory) -> {
if (match(MODPACK_BLACK_LIST, fileName, isDirectory))
return ModAdviser.ModSuggestion.HIDDEN;
if (match(MODPACK_SUGGESTED_BLACK_LIST, fileName, isDirectory))
return ModAdviser.ModSuggestion.NORMAL;
return ModAdviser.ModSuggestion.SUGGESTED;
};
private static boolean match(final List<String> l, String fileName, boolean isDirectory) {
for (String s : l)
if (isDirectory) {
if (fileName.startsWith(s + "/"))
return true;
} else if (fileName.equals(s))
return true;
return false;
}
/**
* Export the game to a mod pack file.
*
* @param output mod pack file.
* @param baseFolder if the game dir type is ROOT_FOLDER, use ".minecraft",
* or use ".minecraft/versions/{MCVER}/"
* @param version to locate version.json
*
* @throws IOException if create tmp directory failed
*/
public static void export(File output, IMinecraftProvider provider, String version, List<String> blacklist, Map<String, String> modpackPreferences, CallbackIO<ZipEngine> callback) throws IOException, GameException {
final ArrayList<String> b = new ArrayList<>(MODPACK_BLACK_LIST);
if (blacklist != null)
b.addAll(blacklist);
b.add(version + ".jar");
b.add(version + ".json");
HMCLog.log("Compressing game files without some files in blacklist, including files or directories: usernamecache.json, asm, logs, backups, versions, assets, usercache.json, libraries, crash-reports, launcher_profiles.json, NVIDIA, TCNodeTracker");
ZipEngine zip = null;
try {
zip = new ZipEngine(output);
zip.putDirectory(provider.getRunDirectory(version), (String x, Boolean y) -> {
for (String s : b)
if (y) {
if (x.startsWith(s + "/"))
return null;
} else if (x.equals(s))
return null;
return "minecraft/" + x;
});
MinecraftVersion mv = provider.getVersionById(version).resolve(provider);
MinecraftVersionRequest r = MinecraftVersionRequest.minecraftVersion(provider.getMinecraftJar(version));
if (r.type != MinecraftVersionRequest.OK)
throw new FileSystemException(C.i18n("modpack.cannot_read_version") + ": " + MinecraftVersionRequest.getResponse(r));
mv.jar = r.version;
mv.runDir = "version";
zip.putTextFile(C.GSON.toJson(mv), "minecraft/pack.json");
zip.putTextFile(C.GSON.toJson(modpackPreferences), "modpack.json");
if (callback != null)
callback.call(zip);
} finally {
IOUtils.closeQuietly(zip);
}
}
}

View File

@@ -0,0 +1,59 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 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.core.service;
import java.io.File;
import java.io.IOException;
import org.jackhuang.hmcl.core.GameException;
import org.jackhuang.hmcl.core.asset.AssetsObject;
import org.jackhuang.hmcl.core.version.AssetIndexDownloadInfo;
import org.jackhuang.hmcl.util.task.Task;
/**
*
* @author huangyuhui
*/
public abstract class IMinecraftAssetService extends IMinecraftBasicService {
public IMinecraftAssetService(IMinecraftService service) {
super(service);
}
public abstract Task downloadAssets(String mcVersion) throws GameException;
public abstract File getAssets(String assetId);
public abstract File getIndexFile(String assetId);
/**
* Redownload the Asset index json of the given version.
*
* @param mcVersion the given version name
*
* @return Is the action successful?
*/
public abstract boolean refreshAssetsIndex(String mcVersion) throws GameException;
public abstract boolean downloadMinecraftAssetsIndexAsync(AssetIndexDownloadInfo assetId);
public abstract Task downloadMinecraftAssetsIndex(AssetIndexDownloadInfo assetId);
public abstract File getAssetObject(String assetVersion, String name) throws IOException;
public abstract File getAssetObject(String assetId, AssetsObject assetsObject);
}

View File

@@ -0,0 +1,32 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 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.core.service;
/**
*
* @author huangyuhui
*/
public abstract class IMinecraftBasicService {
public IMinecraftService service;
public IMinecraftBasicService(IMinecraftService service) {
this.service = service;
}
}

View File

@@ -0,0 +1,50 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 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.core.service;
import java.io.File;
import java.util.List;
import org.jackhuang.hmcl.core.GameException;
import org.jackhuang.hmcl.api.event.launch.DownloadLibraryJob;
import org.jackhuang.hmcl.core.version.MinecraftVersion;
import org.jackhuang.hmcl.util.task.Task;
/**
*
* @author huangyuhui
*/
public abstract class IMinecraftDownloadService extends IMinecraftBasicService {
public IMinecraftDownloadService(IMinecraftService service) {
super(service);
}
public abstract Task downloadMinecraft(String id);
public abstract Task downloadMinecraftJar(MinecraftVersion mv, File f);
public abstract Task downloadMinecraftVersionJson(String id);
/**
* Get the libraries that need to download.
*
* @return the library collection
*/
public abstract List<DownloadLibraryJob> getDownloadLibraries(MinecraftVersion mv) throws GameException;
}

View File

@@ -0,0 +1,42 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 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.core.service;
import org.jackhuang.hmcl.core.install.InstallerType;
import org.jackhuang.hmcl.core.install.InstallerVersionList;
import org.jackhuang.hmcl.util.task.Task;
/**
*
* @author huangyuhui
*/
public abstract class IMinecraftInstallerService extends IMinecraftBasicService {
public IMinecraftInstallerService(IMinecraftService service) {
super(service);
}
public abstract Task download(String installId, InstallerVersionList.InstallerVersion v, InstallerType type);
public abstract Task downloadForge(String installId, InstallerVersionList.InstallerVersion v);
public abstract Task downloadOptiFine(String installId, InstallerVersionList.InstallerVersion v);
public abstract Task downloadLiteLoader(String installId, InstallerVersionList.InstallerVersion v);
}

View File

@@ -0,0 +1,33 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 huangyuhui
*
* 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.core.service;
import java.util.List;
import org.jackhuang.hmcl.core.GameException;
import org.jackhuang.hmcl.core.version.MinecraftVersion;
/**
*
* @author huangyuhui
*/
public interface IMinecraftLoader {
MinecraftVersion getMinecraftVersion();
List<String> makeLaunchingCommand() throws GameException;
}

View File

@@ -0,0 +1,41 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 huangyuhui
*
* 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.core.service;
import java.io.File;
import java.util.List;
import org.jackhuang.hmcl.core.mod.ModInfo;
/**
*
* @author huangyuhui
*/
public abstract class IMinecraftModService extends IMinecraftBasicService {
public IMinecraftModService(IMinecraftService service) {
super(service);
}
public abstract List<ModInfo> getMods(String id);
public abstract List<ModInfo> recacheMods(String id);
public abstract boolean addMod(String id, File f);
public abstract boolean removeMod(String id, Object[] mods);
}

View File

@@ -0,0 +1,162 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 huangyuhui
*
* 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.core.service;
import java.io.File;
import java.util.Collection;
import org.jackhuang.hmcl.core.GameException;
import org.jackhuang.hmcl.api.game.DecompressLibraryJob;
import org.jackhuang.hmcl.core.version.MinecraftVersion;
import org.jackhuang.hmcl.api.event.EventHandler;
import org.jackhuang.hmcl.api.func.Consumer;
import org.jackhuang.hmcl.api.func.Predicate;
/**
* Provide everything of the Minecraft of a Profile.
*
* @see
* org.jackhuang.hmcl.core.version.MinecraftVersionManager
* @author huangyuhui
*/
public abstract class IMinecraftProvider {
protected IMinecraftService service;
public IMinecraftProvider(IMinecraftService service) {
this.service = service;
}
/**
* To download mod packs.
*/
public abstract void initializeMiencraft();
/**
* Get the run directory of given version.
*
* @param id the given version name
*
* @return the run directory
*/
public abstract File getRunDirectory(String id);
public abstract File versionRoot(String id);
public File getRunDirectory(String id, String subFolder) {
return new File(getRunDirectory(id), subFolder);
}
public abstract void open(String version, String folder);
/**
* Install a new version to this profile.
*
* @param version the new version name
*
* @return Is the action successful?
*/
public abstract boolean install(String version, Consumer<MinecraftVersion> callback);
/**
*
* @param v should be resolved
*
* @return libraries of resolved minecraft version v.
*/
public abstract DecompressLibraryJob getDecompressLibraries(MinecraftVersion v) throws GameException;
public abstract File getDecompressNativesToLocation(MinecraftVersion v);
/**
* @return the Minecraft jar of selected version.
*/
public abstract File getMinecraftJar(String id);
/**
* Rename version
*
* @param from The old name
* @param to The new name
*
* @return Is the action successful?
*/
public abstract boolean renameVersion(String from, String to);
/**
* Remove the given version from disk.
*
* @param a the version name
*
* @return Is the action successful?
*/
public abstract boolean removeVersionFromDisk(String a);
/**
* Choose a version randomly.
*
* @return the version
*/
public abstract MinecraftVersion getOneVersion(Predicate<MinecraftVersion> p);
/**
* All Minecraft version in this profile.
*
* @return the collection of all Minecraft version
*/
public abstract Collection<MinecraftVersion> getVersions();
/**
* Get the Minecraft json instance of given version.
*
* @param id the given version name
*
* @return the Minecraft json instance
*/
public abstract MinecraftVersion getVersionById(String id);
/**
* getVersions().size()
*
* @return getVersions().size()
*/
public abstract int getVersionCount();
/**
* Refind the versions in this profile.
* Must call onRefreshingVersions, onRefreshedVersions, onLoadedVersion
* Events.
*/
public abstract void refreshVersions();
/**
* Clean redundant files.
*/
public abstract void cleanFolder();
/**
* When GameLauncher launches the game, this function will be called.
*
* @return if false, will break the launch process.
*/
public abstract boolean onLaunch(String id);
public File baseDirectory() {
return service.baseDirectory();
}
}

View File

@@ -0,0 +1,60 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 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.core.service;
import java.io.File;
import org.jackhuang.hmcl.core.GameException;
import org.jackhuang.hmcl.api.auth.UserProfileProvider;
import org.jackhuang.hmcl.core.download.DownloadType;
import org.jackhuang.hmcl.api.game.LaunchOptions;
/**
*
* @author huangyuhui
*/
public abstract class IMinecraftService {
public abstract File baseDirectory();
public DownloadType getDownloadType() {
return DownloadType.getSuggestedDownloadType();
}
public abstract IMinecraftAssetService asset();
public abstract IMinecraftDownloadService download();
public abstract IMinecraftModService mod();
public abstract IMinecraftProvider version();
public abstract IMinecraftInstallerService install();
/**
* Provide the Minecraft Loader to generate the launching command.
*
* @see org.jackhuang.hmcl.core.service.IMinecraftLoader
* @param p player informations, including username & auth_token
*
* @return what you want
*
* @throws GameException circular denpendency versions
*/
public abstract IMinecraftLoader launch(LaunchOptions options, UserProfileProvider p) throws GameException;
}

View File

@@ -0,0 +1,69 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 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.core.version;
import org.jackhuang.hmcl.core.download.DownloadType;
import org.jackhuang.hmcl.api.game.IMinecraftLibrary;
/**
*
* @author huangyuhui
*/
public abstract class AbstractMinecraftLibrary implements IMinecraftLibrary {
private String name;
public AbstractMinecraftLibrary(String name) {
this.name = name;
}
@Override
public String getName() {
return name;
}
public abstract LibraryDownloadInfo getDownloadInfo();
@Override
public String getDownloadURL(String downloadSource) {
return getDownloadInfo().getUrl(DownloadType.valueOf(downloadSource));
}
@Override
public boolean equals(Object obj) {
if (obj instanceof MinecraftLibrary)
return ((MinecraftLibrary) obj).getName().equals(name);
return false;
}
@Override
public int hashCode() {
int hash = 3;
hash = 89 * hash + (this.name != null ? this.name.hashCode() : 0);
return hash;
}
@Override
public Object clone() {
try {
return super.clone();
} catch (CloneNotSupportedException ex) {
throw new InternalError(ex);
}
}
}

View File

@@ -0,0 +1,46 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 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.core.version;
import org.jackhuang.hmcl.core.download.DownloadType;
/**
*
* @author huangyuhui
*/
public class AssetIndexDownloadInfo extends GameDownloadInfo {
int totalSize;
public AssetIndexDownloadInfo() {
}
public AssetIndexDownloadInfo(String id) {
this.id = id;
url = null;
}
@Override
public String getCustomizedURL(DownloadType dt) {
return dt.getProvider().getIndexesDownloadURL() + id + ".json";
}
public int getTotalSize() {
return totalSize;
}
}

View File

@@ -0,0 +1,83 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 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.core.version;
import com.google.gson.annotations.SerializedName;
import org.jackhuang.hmcl.core.download.DownloadType;
/**
*
* @author huangyuhui
*/
public class GameDownloadInfo implements Cloneable {
@SerializedName("sha1")
public String sha1;
@SerializedName("size")
public int size;
@SerializedName("url")
public String url;
/**
* Ready for AssetIndexDownloadInfo, and GameDownloadInfo also need this.
*/
protected String id;
/**
* Get the game download url.
*
* @param dt where to download?
*
* @return the download url
*/
public String getUrl(DownloadType dt) {
return getUrl(dt, dt.getProvider().isAllowedToUseSelfURL());
}
/**
* Get the game download url.
*
* @param dt where to download?
* @param allowSelf allow this game to be downloaded from its modified url?
*
* @return the download url
*/
public String getUrl(DownloadType dt, boolean allowSelf) {
if (url != null && allowSelf)
return dt.getProvider().getParsedDownloadURL(url);
else
return getCustomizedURL(dt);
}
protected String getCustomizedURL(DownloadType dt) {
return dt.getProvider().getVersionsDownloadURL() + id + "/" + id + ".jar";
}
public String getId() {
return id;
}
@Override
public Object clone() {
try {
return super.clone();
} catch (CloneNotSupportedException ex) {
throw new Error(ex);
}
}
}

View File

@@ -0,0 +1,33 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 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.core.version;
import com.google.gson.annotations.SerializedName;
import java.util.Map;
/**
*
* @author huangyuhui
*/
public class LibrariesDownloadInfo {
@SerializedName("classifiers")
public Map<String, LibraryDownloadInfo> classifiers;
@SerializedName("artifact")
public LibraryDownloadInfo artifact;
}

View File

@@ -0,0 +1,48 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 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.core.version;
import com.google.gson.annotations.SerializedName;
import org.jackhuang.hmcl.core.download.DownloadType;
import org.jackhuang.hmcl.util.StrUtils;
import org.jackhuang.hmcl.util.sys.IOUtils;
/**
*
* @author huangyuhui
*/
public class LibraryDownloadInfo extends GameDownloadInfo {
@SerializedName("path")
public String path;
@SerializedName("forgeURL")
public String forgeURL;
@Override
public String getUrl(DownloadType dt, boolean allowSelf) {
String myURL = (forgeURL == null ? dt.getProvider().getLibraryDownloadURL() : forgeURL);
if (StrUtils.isNotBlank(url) && allowSelf)
myURL = dt.getProvider().getParsedDownloadURL(url);
if (!myURL.endsWith(".jar"))
if (path == null)
return null;
else
myURL = IOUtils.addURLSeparator(myURL) + path.replace('\\', '/');
return myURL;
}
}

View File

@@ -0,0 +1,74 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 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.core.version;
import java.io.File;
import java.util.ArrayList;
import java.util.Set;
import org.jackhuang.hmcl.core.service.IMinecraftProvider;
/**
*
* @author huangyuhui
*/
public class MinecraftClassicVersion extends MinecraftVersion {
public MinecraftClassicVersion() {
super();
mainClass = "net.minecraft.client.Minecraft";
id = "Classic";
type = "release";
processArguments = assets = releaseTime = time = null;
minecraftArguments = "${auth_player_name} ${auth_session} --workDir ${game_directory}";
libraries = new ArrayList<>();
libraries.add(new MinecraftOldLibrary("lwjgl"));
libraries.add(new MinecraftOldLibrary("jinput"));
libraries.add(new MinecraftOldLibrary("lwjgl_util"));
}
@Override
public Object clone() {
return super.clone();
}
@Override
public MinecraftVersion resolve(IMinecraftProvider manager, Set<String> resolvedSoFar) {
return this;
}
@Override
public File getJar(File gameDir) {
return new File(gameDir, "bin/minecraft.jar");
}
@Override
public File getJar(File gameDir, String suffix) {
return new File(gameDir, "bin/minecraft" + suffix + ".jar");
}
@Override
public File getNatives(File gameDir) {
return new File(gameDir, "bin/natives");
}
@Override
public boolean isAllowedToUnpackNatives() {
return false;
}
}

View File

@@ -0,0 +1,151 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 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.core.version;
import org.jackhuang.hmcl.api.game.Extract;
import com.google.gson.annotations.SerializedName;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import org.jackhuang.hmcl.api.HMCLApi;
import org.jackhuang.hmcl.api.event.version.MinecraftLibraryPathEvent;
import org.jackhuang.hmcl.util.sys.OS;
import org.jackhuang.hmcl.util.sys.Platform;
import org.jackhuang.hmcl.util.StrUtils;
import org.jackhuang.hmcl.api.Wrapper;
/**
*
* @author huangyuhui
*/
public class MinecraftLibrary extends AbstractMinecraftLibrary {
@SerializedName("rules")
public ArrayList<Rules> rules;
@SerializedName("url")
public String url;
@SerializedName("natives")
public Natives natives;
@SerializedName("extract")
public Extract extract;
@SerializedName("downloads")
public LibrariesDownloadInfo downloads;
public MinecraftLibrary(String name) {
super(name);
}
/**
* is the library allowed to load.
*
* @return
*/
@Override
public boolean allow() {
if (rules != null) {
boolean flag = false;
for (Rules r : rules)
if ("disallow".equals(r.action()))
return false;
else if ("allow".equals(r.action()))
flag = true;
return flag;
} else
return true;
}
private String formatArch(String nati) {
return nati == null ? "" : nati.replace("${arch}", Platform.getPlatform().getBit());
}
private String getNative() {
if (natives == null)
return "";
switch (OS.os()) {
case WINDOWS:
return formatArch(natives.windows);
case OSX:
return formatArch(natives.osx);
default:
return formatArch(natives.linux);
}
}
@Override
public boolean isRequiredToUnzip() {
return natives != null && allow();
}
public String formatName() {
if (getName() == null)
return null;
String[] s = getName().split(":");
if (s.length < 3)
return null;
StringBuilder sb = new StringBuilder(s[0].replace('.', '/')).append('/').append(s[1]).append('/').append(s[2]).append('/').append(s[1]).append('-').append(s[2]);
if (natives != null)
sb.append('-').append(getNative());
return sb.append(".jar").toString();
}
@Override
public File getFilePath(File gameDir) {
LibraryDownloadInfo info = getDownloadInfo();
if (info == null)
return null;
MinecraftLibraryPathEvent event = new MinecraftLibraryPathEvent(this, "libraries/" + info.path, new Wrapper<>(new File(gameDir, "libraries/" + info.path)));
HMCLApi.EVENT_BUS.fireChannel(event);
return event.getFile().getValue();
}
@Override
public Extract getDecompressExtractRules() {
return extract == null ? new Extract() : extract;
}
@Override
public LibraryDownloadInfo getDownloadInfo() {
if (getName() == null)
return null;
if (downloads == null)
downloads = new LibrariesDownloadInfo();
LibraryDownloadInfo info;
if (natives != null) {
if (downloads.classifiers == null)
downloads.classifiers = new HashMap<>();
if (!downloads.classifiers.containsKey(getNative()))
downloads.classifiers.put(getNative(), info = new LibraryDownloadInfo());
else {
info = downloads.classifiers.get(getNative());
if (info == null)
info = new LibraryDownloadInfo();
}
} else {
if (downloads.artifact == null)
downloads.artifact = new LibraryDownloadInfo();
info = downloads.artifact;
}
if (StrUtils.isBlank(info.path)) {
info.path = formatName();
if (info.path == null)
return null;
}
info.forgeURL = this.url;
return info;
}
}

View File

@@ -0,0 +1,57 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 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.core.version;
import java.io.File;
/**
*
* @author huangyuhui
*/
public class MinecraftOldLibrary extends MinecraftLibrary {
public MinecraftOldLibrary(String name) {
super(name);
}
@Override
public boolean isRequiredToUnzip() {
return false;
}
@Override
public boolean allow() {
return true;
}
@Override
public File getFilePath(File gameDir) {
return new File(gameDir, "bin/" + getName() + ".jar");
}
@Override
public Object clone() {
return super.clone();
}
@Override
public LibraryDownloadInfo getDownloadInfo() {
return new LibraryDownloadInfo();
}
}

View File

@@ -0,0 +1,218 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 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.core.version;
import org.jackhuang.hmcl.api.game.IMinecraftLibrary;
import com.google.gson.annotations.SerializedName;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import org.jackhuang.hmcl.util.C;
import org.jackhuang.hmcl.core.GameException;
import org.jackhuang.hmcl.core.service.IMinecraftProvider;
import org.jackhuang.hmcl.core.asset.AssetsIndex;
import org.jackhuang.hmcl.util.ArrayUtils;
/**
*
* @author huangyuhui
*/
public class MinecraftVersion implements Cloneable, Comparable<MinecraftVersion> {
@SerializedName("minecraftArguments")
public String minecraftArguments;
@SerializedName("mainClass")
public String mainClass;
@SerializedName("time")
public String time;
@SerializedName("id")
public String id;
@SerializedName("type")
public String type;
@SerializedName("processArguments")
public String processArguments;
@SerializedName("releaseTime")
public String releaseTime;
@SerializedName("jar")
public String jar;
@SerializedName("inheritsFrom")
public String inheritsFrom;
@SerializedName("runDir")
public String runDir;
@SerializedName("assets")
protected String assets;
@SerializedName("minimumLauncherVersion")
public int minimumLauncherVersion;
@SerializedName("hidden")
public boolean hidden;
@SerializedName("assetIndex")
public AssetIndexDownloadInfo assetIndex;
@SerializedName("downloads")
private Map<String, GameDownloadInfo> downloads;
@SerializedName("libraries")
public ArrayList<MinecraftLibrary> libraries;
public MinecraftVersion() {
}
public MinecraftVersion(String minecraftArguments, String mainClass, String time, String id, String type, String processArguments, String releaseTime, String assets, String jar, String inheritsFrom, String runDir, int minimumLauncherVersion, List<MinecraftLibrary> libraries, boolean hidden, Map<String, GameDownloadInfo> downloads, AssetIndexDownloadInfo assetIndexDownloadInfo) {
this();
this.minecraftArguments = minecraftArguments;
this.mainClass = mainClass;
this.time = time;
this.id = id;
this.type = type;
this.processArguments = processArguments;
this.releaseTime = releaseTime;
this.assets = assets;
this.jar = jar;
this.inheritsFrom = inheritsFrom;
this.minimumLauncherVersion = minimumLauncherVersion;
this.hidden = hidden;
this.runDir = runDir;
if (assetIndexDownloadInfo == null)
this.assetIndex = null;
else
this.assetIndex = (AssetIndexDownloadInfo) assetIndexDownloadInfo.clone();
if (libraries == null)
this.libraries = new ArrayList<>();
else {
this.libraries = new ArrayList<>(libraries.size());
for (MinecraftLibrary library : libraries)
if (library != null)
this.libraries.add((MinecraftLibrary) library.clone());
}
if (downloads == null)
this.downloads = null;
else {
this.downloads = new HashMap<>(downloads.size());
for (Map.Entry<String, GameDownloadInfo> entry : downloads.entrySet())
this.downloads.put(entry.getKey(), (GameDownloadInfo) entry.getValue().clone());
}
}
@Override
public Object clone() {
try {
MinecraftVersion mv = (MinecraftVersion) super.clone();
mv.libraries = (ArrayList<MinecraftLibrary>) mv.libraries.clone();
return mv;
} catch (CloneNotSupportedException ex) {
throw new InternalError();
}
}
public MinecraftVersion resolve(IMinecraftProvider provider) throws GameException {
return resolve(provider, new HashSet<>());
}
protected MinecraftVersion resolve(IMinecraftProvider provider, Set<String> resolvedSoFar) throws GameException {
if (inheritsFrom == null)
return this;
if (!resolvedSoFar.add(id))
throw new GameException(C.i18n("launch.circular_dependency_versions"));
MinecraftVersion parent = provider.getVersionById(inheritsFrom);
if (parent == null) {
if (!provider.install(inheritsFrom, t -> t.hidden = true))
return this;
parent = provider.getVersionById(inheritsFrom);
}
parent = parent.resolve(provider, resolvedSoFar);
MinecraftVersion result = new MinecraftVersion(
this.minecraftArguments != null ? this.minecraftArguments : parent.minecraftArguments,
this.mainClass != null ? this.mainClass : parent.mainClass,
this.time, this.id, this.type, parent.processArguments, this.releaseTime,
this.assets != null ? this.assets : parent.assets,
this.jar != null ? this.jar : parent.jar,
null, this.runDir, parent.minimumLauncherVersion,
this.libraries != null ? ArrayUtils.merge(this.libraries, parent.libraries) : parent.libraries, this.hidden,
this.downloads != null ? this.downloads : parent.downloads,
this.assetIndex != null ? this.assetIndex : parent.assetIndex);
return result;
}
public File getJar(File gameDir) {
String jarId = this.jar == null ? this.id : this.jar;
return new File(gameDir, "versions/" + jarId + "/" + jarId + ".jar");
}
public File getJar(File gameDir, String suffix) {
String jarId = this.jar == null ? this.id : this.jar;
return new File(gameDir, "versions/" + jarId + "/" + jarId + suffix + ".jar");
}
public File getNatives(File gameDir) {
return new File(gameDir, "versions/" + id + "/" + id
+ "-natives");
}
public boolean isAllowedToUnpackNatives() {
return true;
}
@Override
public int compareTo(MinecraftVersion o) {
return id.compareTo(o.id);
}
@Override
public int hashCode() {
int hash = 7;
hash = 53 * hash + Objects.hashCode(this.id);
return hash;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null || getClass() != obj.getClass())
return false;
return Objects.equals(this.id, ((MinecraftVersion) obj).id);
}
public AssetIndexDownloadInfo getAssetsIndex() {
if (assetIndex == null)
assetIndex = new AssetIndexDownloadInfo(assets == null ? AssetsIndex.DEFAULT_ASSET_NAME : assets);
return assetIndex;
}
public GameDownloadInfo getClientDownloadInfo() {
if (downloads == null)
downloads = new HashMap<>();
if (!downloads.containsKey("client"))
downloads.put("client", new GameDownloadInfo());
GameDownloadInfo i = downloads.get("client");
if (i.id == null)
i.id = id;
return i;
}
public Set<IMinecraftLibrary> getLibraries() {
return libraries == null ? new HashSet<>() : new HashSet<>(libraries);
}
}

View File

@@ -0,0 +1,289 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 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.core.version;
import org.jackhuang.hmcl.api.game.Extract;
import org.jackhuang.hmcl.api.game.IMinecraftLibrary;
import org.jackhuang.hmcl.api.game.DecompressLibraryJob;
import com.google.gson.JsonSyntaxException;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;
import java.util.TreeMap;
import org.jackhuang.hmcl.api.HMCLApi;
import org.jackhuang.hmcl.api.event.version.LoadedOneVersionEvent;
import org.jackhuang.hmcl.api.event.version.RefreshedVersionsEvent;
import org.jackhuang.hmcl.api.event.version.RefreshingVersionsEvent;
import org.jackhuang.hmcl.util.C;
import org.jackhuang.hmcl.api.HMCLog;
import org.jackhuang.hmcl.core.GameException;
import org.jackhuang.hmcl.core.service.IMinecraftProvider;
import org.jackhuang.hmcl.core.service.IMinecraftService;
import org.jackhuang.hmcl.util.sys.FileUtils;
import org.jackhuang.hmcl.core.MCUtils;
import org.jackhuang.hmcl.util.task.TaskWindow;
import org.jackhuang.hmcl.util.sys.IOUtils;
import org.jackhuang.hmcl.util.MessageBox;
import org.jackhuang.hmcl.util.StrUtils;
import org.jackhuang.hmcl.api.func.Consumer;
import org.jackhuang.hmcl.api.func.Predicate;
import org.jackhuang.hmcl.util.ui.SwingUtils;
/**
*
* @author huangyuhui
*/
public class MinecraftVersionManager extends IMinecraftProvider {
final Map<String, MinecraftVersion> versions = new TreeMap<>();
/**
*
* @param p
*/
public MinecraftVersionManager(IMinecraftService p) {
super(p);
}
@Override
public Collection<MinecraftVersion> getVersions() {
return versions.values();
}
@Override
public int getVersionCount() {
return versions.size();
}
@Override
public synchronized void refreshVersions() {
HMCLApi.EVENT_BUS.fireChannel(new RefreshingVersionsEvent(this));
try {
MCUtils.tryWriteProfile(service.baseDirectory());
} catch (IOException ex) {
HMCLog.warn("Failed to create launcher_profiles.json, Forge/LiteLoader installer will not work.", ex);
}
versions.clear();
File oldDir = new File(service.baseDirectory(), "bin");
if (oldDir.exists()) {
MinecraftClassicVersion v = new MinecraftClassicVersion();
versions.put(v.id, v);
}
File version = new File(service.baseDirectory(), "versions");
File[] files = version.listFiles();
if (files != null && files.length > 0)
for (File dir : files) {
String id = dir.getName();
File jsonFile = new File(dir, id + ".json");
if (!dir.isDirectory())
continue;
boolean ask = false;
File[] jsons = null;
if (!jsonFile.exists()) {
jsons = FileUtils.searchSuffix(dir, "json");
if (jsons.length == 1)
ask = true;
}
if (ask) {
HMCLog.warn("Found not matched filenames version: " + id + ", json: " + jsons[0].getName());
if (MessageBox.show(String.format(C.i18n("launcher.versions_json_not_matched"), id, jsons[0].getName()), MessageBox.YES_NO_OPTION) == MessageBox.YES_OPTION)
if (!jsons[0].renameTo(new File(jsons[0].getParent(), id + ".json")))
HMCLog.warn("Failed to rename version json " + jsons[0]);
}
if (!jsonFile.exists()) {
if (MessageBox.show(C.i18n("launcher.versions_json_not_matched_cannot_auto_completion", id), MessageBox.YES_NO_OPTION) == MessageBox.YES_OPTION)
FileUtils.deleteDirectoryQuietly(dir);
continue;
}
MinecraftVersion mcVersion;
try {
mcVersion = C.GSON.fromJson(FileUtils.read(jsonFile), MinecraftVersion.class);
if (mcVersion == null)
throw new JsonSyntaxException("Wrong json format, got null.");
} catch (JsonSyntaxException | IOException e) {
HMCLog.warn("Found wrong format json, try to fix it.", e);
if (MessageBox.show(C.i18n("launcher.versions_json_not_formatted", id), MessageBox.YES_NO_OPTION) == MessageBox.YES_OPTION) {
TaskWindow.factory().execute(service.download().downloadMinecraftVersionJson(id));
try {
mcVersion = C.GSON.fromJson(FileUtils.read(jsonFile), MinecraftVersion.class);
if (mcVersion == null)
throw new JsonSyntaxException("Wrong json format, got null.");
} catch (IOException | JsonSyntaxException ex) {
HMCLog.warn("Ignoring: " + dir + ", the json of this Minecraft is malformed.", ex);
continue;
}
} else
continue;
}
try {
if (!id.equals(mcVersion.id)) {
HMCLog.warn("Found: " + dir + ", it contains id: " + mcVersion.id + ", expected: " + id + ", this app will fix this problem.");
mcVersion.id = id;
FileUtils.writeQuietly(jsonFile, C.GSON.toJson(mcVersion));
}
versions.put(id, mcVersion);
HMCLApi.EVENT_BUS.fireChannel(new LoadedOneVersionEvent(this, id));
} catch (Exception e) {
HMCLog.warn("Ignoring: " + dir + ", the json of this Minecraft is malformed.", e);
}
}
HMCLApi.EVENT_BUS.fireChannel(new RefreshedVersionsEvent(this));
}
@Override
public File versionRoot(String id) {
return new File(service.baseDirectory(), "versions/" + id);
}
@Override
public boolean removeVersionFromDisk(String name) {
File version = versionRoot(name);
if (!version.exists())
return true;
versions.remove(name);
return FileUtils.deleteDirectoryQuietly(version);
}
@Override
public boolean renameVersion(String from, String to) {
try {
File fromJson = new File(versionRoot(from), from + ".json");
MinecraftVersion mcVersion = C.GSON.fromJson(FileUtils.read(fromJson), MinecraftVersion.class);
mcVersion.id = to;
FileUtils.writeQuietly(fromJson, C.GSON.toJson(mcVersion));
File toDir = versionRoot(to);
if (!versionRoot(from).renameTo(toDir))
HMCLog.warn("MinecraftVersionManager.RenameVersion: Failed to rename version root " + from + " to " + to);
File toJson = new File(toDir, to + ".json");
File toJar = new File(toDir, to + ".jar");
if (new File(toDir, from + ".json").renameTo(toJson))
HMCLog.warn("MinecraftVersionManager.RenameVersion: Failed to rename json");
File newJar = new File(toDir, from + ".jar");
if (newJar.exists() && !newJar.renameTo(toJar))
HMCLog.warn("Failed to rename pre jar " + newJar + " to new jar " + toJar);
return true;
} catch (IOException | JsonSyntaxException e) {
HMCLog.warn("Failed to rename " + from + " to " + to + ", the json of this Minecraft is malformed.", e);
return false;
}
}
@Override
public File getRunDirectory(String id) {
if (getVersionById(id) != null)
if ("version".equals(getVersionById(id).runDir))
return versionRoot(id);
return baseDirectory();
}
@Override
public boolean install(String id, Consumer<MinecraftVersion> callback) {
if (!TaskWindow.factory().append(service.download().downloadMinecraft(id)).execute())
return false;
if (callback != null) {
File mvt = new File(versionRoot(id), id + ".json");
MinecraftVersion v = C.GSON.fromJson(FileUtils.readQuietly(mvt), MinecraftVersion.class);
if (v == null)
return false;
callback.accept(v);
FileUtils.writeQuietly(mvt, C.GSON.toJson(v));
}
refreshVersions();
return true;
}
@Override
public void open(String mv, String name) {
SwingUtils.openFolder((name == null) ? getRunDirectory(mv) : new File(getRunDirectory(mv), name));
}
@Override
public DecompressLibraryJob getDecompressLibraries(MinecraftVersion v) throws GameException {
if (v.libraries == null)
throw new GameException("Wrong format: minecraft.json");
ArrayList<File> unzippings = new ArrayList<>();
ArrayList<Extract> extractRules = new ArrayList<>();
for (IMinecraftLibrary l : v.libraries)
if (l.isRequiredToUnzip() && v.isAllowedToUnpackNatives()) {
unzippings.add(IOUtils.tryGetCanonicalFile(l.getFilePath(service.baseDirectory())));
extractRules.add(l.getDecompressExtractRules());
}
return new DecompressLibraryJob(unzippings.toArray(new File[unzippings.size()]), extractRules.toArray(new Extract[extractRules.size()]), getDecompressNativesToLocation(v));
}
@Override
public File getDecompressNativesToLocation(MinecraftVersion v) {
return v == null ? null : v.getNatives(service.baseDirectory());
}
@Override
public File getMinecraftJar(String id) {
if (versions.containsKey(id))
return versions.get(id).getJar(service.baseDirectory());
else
return null;
}
@Override
public MinecraftVersion getOneVersion(Predicate<MinecraftVersion> pred) {
for (MinecraftVersion v : versions.values())
if (pred == null || pred.apply(v))
return v;
return null;
}
@Override
public MinecraftVersion getVersionById(String id) {
return StrUtils.isBlank(id) ? null : versions.get(id);
}
@Override
public boolean onLaunch(String id) {
File resourcePacks = new File(getRunDirectory(id), "resourcepacks");
if (!FileUtils.makeDirectory(resourcePacks))
HMCLog.warn("Failed to make resourcePacks: " + resourcePacks);
return true;
}
@Override
public void cleanFolder() {
for (MinecraftVersion s : getVersions()) {
FileUtils.deleteDirectoryQuietly(new File(versionRoot(s.id), s.id + "-natives"));
File f = getRunDirectory(s.id);
String[] dir = { "natives", "native", "$native", "AMD", "crash-reports", "logs", "asm", "NVIDIA", "server-resource-packs", "natives", "native" };
for (String str : dir)
FileUtils.deleteDirectoryQuietly(new File(f, str));
String[] files = { "output-client.log", "usercache.json", "usernamecache.json", "hmclmc.log" };
for (String str : files)
if (!new File(f, str).delete())
HMCLog.warn("Failed to delete " + str);
}
}
@Override
public void initializeMiencraft() {
}
}

View File

@@ -0,0 +1,43 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 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.core.version;
import com.google.gson.annotations.SerializedName;
/**
*
* @author huangyuhui
*/
public class Natives implements Cloneable {
@SerializedName("windows")
public String windows;
@SerializedName("osx")
public String osx;
@SerializedName("linux")
public String linux;
@Override
protected Object clone() {
try {
return super.clone();
} catch (CloneNotSupportedException ex) {
throw new InternalError(ex);
}
}
}

View File

@@ -0,0 +1,54 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 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.core.version;
import com.google.gson.annotations.SerializedName;
import org.jackhuang.hmcl.util.StrUtils;
import org.jackhuang.hmcl.util.sys.OS;
/**
*
* @author huangyuhui
*/
public class OSRestriction {
@SerializedName("version")
private String version;
@SerializedName("name")
public String name;
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public boolean isCurrentOS() {
return StrUtils.isBlank(getName()) || OS.os().name().equalsIgnoreCase(getName());
}
}

View File

@@ -0,0 +1,40 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 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.core.version;
import com.google.gson.annotations.SerializedName;
/**
*
* @author huangyuhui
*/
public class Rules {
@SerializedName("action")
private String action;
@SerializedName("os")
private OSRestriction os;
public Rules() {
}
public String action() {
return os == null || os.isCurrentOS() ? action : null;
}
}

View File

@@ -0,0 +1,79 @@
/*
* Hello Minecraft!.
* Copyright (C) 2013 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;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import javax.swing.SwingWorker;
import org.jackhuang.hmcl.api.func.Consumer;
/**
*
* @author huangyuhui
*/
public abstract class AbstractSwingWorker<T> extends SwingWorker<Void, T> {
List<Consumer<T>> processListeners = new ArrayList<>();
List<Runnable> doneListeners = new ArrayList<>();
protected abstract void work() throws Exception;
@Override
protected void done() {
for (Runnable c : doneListeners)
c.run();
}
@Override
protected Void doInBackground() throws Exception {
work();
return null;
}
public AbstractSwingWorker<T> reg(Consumer<T> c) {
processListeners.add(Objects.requireNonNull(c));
return this;
}
public AbstractSwingWorker<T> regDone(Runnable c) {
doneListeners.add(Objects.requireNonNull(c));
return this;
}
@Override
protected void process(List<T> chunks) {
for (T t : chunks)
for (Consumer<T> c : processListeners)
c.accept(t);
}
final List<T> lastChunks = new ArrayList<>();
protected void send(T... t) {
lastChunks.addAll(Arrays.asList(t));
publish(t);
}
public List<T> justDo() throws Exception {
work();
return lastChunks;
}
}

View File

@@ -0,0 +1,107 @@
/*
* Hello Minecraft!.
* Copyright (C) 2013 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;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
/**
*
* @author huangyuhui
*/
public final class ArrayUtils {
private ArrayUtils() {
}
public static <T> boolean isEmpty(T[] array) {
return array == null || array.length <= 0;
}
public static <T> boolean isNotEmpty(T[] array) {
return !isEmpty(array);
}
public static <T> boolean contains(T[] array, T objectToFind) {
return indexOf(array, objectToFind) != -1;
}
public static <T> int indexOf(T[] array, T valueToFind) {
return indexOf(array, valueToFind, 0);
}
public static <T> int indexOf(T[] array, T valueToFind, int startIndex) {
if (array == null)
return -1;
if (startIndex < 0)
startIndex = 0;
for (int i = startIndex; i < array.length; i++)
if (valueToFind.equals(array[i]))
return i;
return -1;
}
public static <T> int lastIndexOf(T[] array, T valueToFind, int startIndex) {
if (array == null)
return -1;
if (startIndex < 0)
return -1;
if (startIndex >= array.length)
startIndex = array.length - 1;
for (int i = startIndex; i >= 0; i--)
if (valueToFind.equals(array[i]))
return i;
return -1;
}
public static <T> ArrayList<T> merge(List<T> a, List<T> b) {
ArrayList<T> al = new ArrayList<>(a.size() + b.size());
al.addAll(a);
al.addAll(b);
return al;
}
public static <T> List<T> tryGetMapWithList(Map<String, List<T>> map, String key) {
List<T> l = (List<T>) map.get(key);
if (l == null)
map.put(key, l = new ArrayList<>());
return l;
}
public static <T> int matchArray(byte[] a, byte[] b) {
for (int i = 0; i < a.length - b.length; i++) {
int j = 1;
for (int k = 0; k < b.length; k++) {
if (b[k] == a[(i + k)])
continue;
j = 0;
break;
}
if (j != 0)
return i;
}
return -1;
}
public static <T> boolean hasDuplicateElements(T[] t) {
return new HashSet<>(Arrays.asList(t)).size() < t.length;
}
}

View File

@@ -0,0 +1,48 @@
/*
* Hello Minecraft!.
* Copyright (C) 2013 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;
import org.jackhuang.hmcl.util.lang.SupportedLocales;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
/**
*
* @author huangyuhui
*/
public final class C {
public static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();
//http://repo1.maven.org/maven2
public static final String URL_PUBLISH = "http://www.mcbbs.net/thread-142335-1-1.html";
public static final String URL_TIEBA = "http://tieba.baidu.com/f?kw=hellominecraftlauncher";
public static final String URL_GITHUB = "https://github.com/huanghongxun/HMCL/issues";
public static final String URL_MINECRAFTFORUM = "http://www.minecraftforum.net/forums/mapping-and-modding/minecraft-tools/1265720-hello-minecraft-launcher-1-9-3-mc-1-7-4-auto";
public static final String URL_FORGE_LIST = "http://files.minecraftforge.net/maven/net/minecraftforge/forge/json";
public static final String URL_LITELOADER_LIST = "http://dl.liteloader.com/versions/versions.json";
private C() {
}
public static String i18n(String a, Object... format) {
return SupportedLocales.NOW_LOCALE.translate(a, format);
}
}

View File

@@ -0,0 +1,52 @@
/*
* Hello Minecraft!.
* Copyright (C) 2013 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;
import org.jackhuang.hmcl.api.func.Predicate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
/**
*
* @author huangyuhui
*/
public final class CollectionUtils {
private CollectionUtils() {
}
public static <T> ArrayList<T> filter(Collection<T> coll, Predicate<T> p) {
ArrayList<T> newColl = new ArrayList<>();
for (T t : coll)
if (p.apply(t))
newColl.add(t);
return newColl;
}
public static <T> boolean removeIf(Collection<T> coll, Predicate<T> p) {
boolean removed = false;
final Iterator<T> each = coll.iterator();
while (each.hasNext())
if (p.apply(each.next())) {
each.remove();
removed = true;
}
return removed;
}
}

View File

@@ -0,0 +1,90 @@
/*
* Hello Minecraft!.
* Copyright (C) 2013 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;
import java.io.IOException;
import java.io.OutputStream;
/**
*
* @author huangyuhui
*/
public class DoubleOutputStream extends OutputStream {
private OutputStream os1 = null;
private OutputStream os2 = null;
private boolean autoFlush = true;
public DoubleOutputStream(OutputStream os1, OutputStream os2) {
this(os1, os2, true);
}
private DoubleOutputStream(OutputStream os1, OutputStream os2, boolean autoFlush) {
this.os1 = os1;
this.os2 = os2;
this.autoFlush = autoFlush;
}
@Override
public final void write(byte[] arr, int off, int len) throws IOException {
if (this.os1 != null)
this.os1.write(arr, off, len);
if (this.os2 != null)
this.os2.write(arr, off, len);
if (this.autoFlush)
flush();
}
@Override
public final void write(byte[] arr) throws IOException {
if (this.os1 != null)
this.os1.write(arr);
if (this.os2 != null)
this.os2.write(arr);
if (this.autoFlush)
flush();
}
@Override
public final void write(int i) throws IOException {
if (this.os1 != null)
this.os1.write(i);
if (this.os2 != null)
this.os2.write(i);
if (this.autoFlush)
flush();
}
@Override
public final void close() throws IOException {
flush();
if (this.os1 != null)
this.os1.close();
if (this.os2 != null)
this.os2.close();
}
@Override
public final void flush() throws IOException {
if (this.os1 != null)
this.os1.flush();
if (this.os2 != null)
this.os2.flush();
}
}

View File

@@ -0,0 +1,62 @@
/*
* Hello Minecraft!.
* Copyright (C) 2013 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;
import org.jackhuang.hmcl.api.VersionNumber;
import java.util.Map;
/**
*
* @author huangyuhui
*/
public interface IUpdateChecker {
/**
*
*/
void checkOutdate();
/**
* Get the <b>cached</b> newest version number, use "process" method to
* download!
*
* @return the newest version number
*
* @see process
*/
VersionNumber getNewVersion();
/**
* Download the version number synchronously. When you execute this method
* first, should leave "showMessage" false.
*
* @param showMessage If it is requested to warn the user that there is a
* new version.
*
* @return the process observable.
*/
AbstractSwingWorker<VersionNumber> process(boolean showMessage);
/**
* Get the download links.
*
* @return a JSON, which contains the server response.
*/
AbstractSwingWorker<Map<String, String>> requestDownloadLink();
}

View File

@@ -0,0 +1,60 @@
/*
* Hello Minecraft!.
* Copyright (C) 2013 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 huang
*/
public final class MathUtils {
private MathUtils() {
}
public static int parseInt(String s, int def) {
try {
return Integer.parseInt(s);
} catch (Exception e) {
return def;
}
}
public static boolean canParseInt(String s) {
try {
Integer.parseInt(s);
return true;
} catch (Exception e) {
return false;
}
}
public static int parseMemory(String s, int def) {
try {
return Integer.parseInt(s);
} catch (Exception e) {
int a = parseInt(s.substring(0, s.length() - 1), def);
if (s.endsWith("g"))
return a * 1024;
else if (s.endsWith("k"))
return a / 1024;
else
return a;
}
}
}

View File

@@ -0,0 +1,133 @@
/*
* Hello Minecraft!.
* Copyright (C) 2013 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;
import org.jackhuang.hmcl.util.ui.SwingUtils;
import javax.swing.JOptionPane;
/**
* @author huangyuhui
*/
public class MessageBox {
private static final String TITLE = C.i18n("message.info");
/**
* Buttons: OK
*/
public static final int DEFAULT_OPTION = -1;
/**
* Buttons: Yes No
*/
public static final int YES_NO_OPTION = 10;
/**
* Buttons: Yes No Cancel
*/
public static final int YES_NO_CANCEL_OPTION = 11;
/**
* Buttons: OK Cancel
*/
public static final int OK_CANCEL_OPTION = 12;
/**
* User Operation: Yes
*/
public static final int YES_OPTION = 0;
/**
* User Operation: No
*/
public static final int NO_OPTION = 1;
/**
* User Operation: Cancel
*/
public static final int CANCEL_OPTION = 2;
/**
* User Operation: OK
*/
public static final int OK_OPTION = 0;
/**
* User Operation: Closed Message Box
*/
public static final int CLOSED_OPTION = -1;
/**
* Message Box Type: Error
*/
public static final int ERROR_MESSAGE = 0;
/**
* Message Box Type: Info
*/
public static final int INFORMATION_MESSAGE = 1;
/**
* Message Box Type: Warning
*/
public static final int WARNING_MESSAGE = 2;
/**
* Message Box Type: Question
*/
public static final int QUESTION_MESSAGE = 3;
/**
* Message Box Type: Plain
*/
public static final int PLAIN_MESSAGE = -1;
/**
* Show MsgBox with title and options
*
* @param Msg The Message
* @param Title The title of MsgBox.
* @param Option The type of MsgBox.
*
* @return user operation.
*/
public static int show(String Msg, String Title, int Option) {
switch (Option) {
case YES_NO_OPTION:
case YES_NO_CANCEL_OPTION:
case OK_CANCEL_OPTION:
return SwingUtils.invokeAndWait(() -> JOptionPane.showConfirmDialog(null, Msg, Title, Option - 10));
default:
SwingUtils.invokeAndWait(() -> JOptionPane.showMessageDialog(null, Msg, Title, Option));
}
return 0;
}
/**
* Show MsgBox with options
*
* @param Msg The Message
* @param Option The type of MsgBox.
*
* @return User Operation
*/
public static int show(String Msg, int Option) {
return show(Msg, TITLE, Option);
}
/**
* Show Default MsgBox
*
* @param Msg The Message
*
* @return User Operation
*/
public static int show(String Msg) {
return show(Msg, TITLE, INFORMATION_MESSAGE);
}
public static int showLocalized(String msg) {
return show(C.i18n(msg));
}
}

View File

@@ -0,0 +1,181 @@
/*
* Hello Minecraft!.
* Copyright (C) 2013 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;
import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import org.jackhuang.hmcl.api.HMCLog;
import org.jackhuang.hmcl.util.sys.IOUtils;
/**
* @author huangyuhui
*/
public class MinecraftVersionRequest implements Serializable {
private static final long serialVersionUID = 1L;
public static final int UNKOWN = 0, INVALID = 1, INVALID_JAR = 2,
MODIFIED = 3, OK = 4, NOT_FOUND = 5, UNREADABLE = 6, NOT_FILE = 7;
public int type;
public String version;
public static String getResponse(MinecraftVersionRequest minecraftVersion) {
switch (minecraftVersion.type) {
case MinecraftVersionRequest.INVALID:
return C.i18n("minecraft.invalid");
case MinecraftVersionRequest.INVALID_JAR:
return C.i18n("minecraft.invalid_jar");
case MinecraftVersionRequest.NOT_FILE:
return C.i18n("minecraft.not_a_file");
case MinecraftVersionRequest.NOT_FOUND:
return C.i18n("minecraft.not_found");
case MinecraftVersionRequest.UNREADABLE:
return C.i18n("minecraft.not_readable");
case MinecraftVersionRequest.MODIFIED:
return C.i18n("minecraft.modified") + ' ' + minecraftVersion.version;
case MinecraftVersionRequest.OK:
return minecraftVersion.version;
case MinecraftVersionRequest.UNKOWN:
default:
return "???";
}
}
private static int lessThan32(byte[] b, int x) {
for (; x < b.length; x++)
if (b[x] < 32)
return x;
return -1;
}
private static MinecraftVersionRequest getVersionOfOldMinecraft(ZipFile file, ZipEntry entry) throws IOException {
MinecraftVersionRequest r = new MinecraftVersionRequest();
byte[] tmp = IOUtils.toByteArray(file.getInputStream(entry));
byte[] bytes = "Minecraft Minecraft ".getBytes("ASCII");
int j = ArrayUtils.matchArray(tmp, bytes);
if (j < 0) {
r.type = MinecraftVersionRequest.UNKOWN;
return r;
}
int i = j + bytes.length;
if ((j = lessThan32(tmp, i)) < 0) {
r.type = MinecraftVersionRequest.UNKOWN;
return r;
}
String ver = new String(tmp, i, j - i, "ASCII");
r.version = ver;
r.type = file.getEntry("META-INF/MANIFEST.MF") == null
? MinecraftVersionRequest.MODIFIED : MinecraftVersionRequest.OK;
return r;
}
private static MinecraftVersionRequest getVersionOfNewMinecraft(ZipFile file, ZipEntry entry) throws IOException {
MinecraftVersionRequest r = new MinecraftVersionRequest();
byte[] tmp = IOUtils.toByteArray(file.getInputStream(entry));
byte[] str = "-server.txt".getBytes("ASCII");
int j = ArrayUtils.matchArray(tmp, str);
if (j < 0) {
r.type = MinecraftVersionRequest.UNKOWN;
return r;
}
int i = j + str.length;
i += 11;
j = lessThan32(tmp, i);
if (j < 0) {
r.type = MinecraftVersionRequest.UNKOWN;
return r;
}
r.version = new String(tmp, i, j - i, "ASCII");
char ch = r.version.charAt(0);
// 1.8.1+
if (ch < '0' || ch > '9') {
str = "Can't keep up! Did the system time change, or is the server overloaded?".getBytes("ASCII");
j = ArrayUtils.matchArray(tmp, str);
if (j < 0) {
r.type = MinecraftVersionRequest.UNKOWN;
return r;
}
i = -1;
while (j > 0) {
if (tmp[j] >= 48 && tmp[j] <= 57) {
i = j;
break;
}
j--;
}
if (i == -1) {
r.type = MinecraftVersionRequest.UNKOWN;
return r;
}
int k = i;
if (tmp[i + 1] >= (int) 'a' && tmp[i + 1] <= (int) 'z')
i++;
while (tmp[k] >= 48 && tmp[k] <= 57 || tmp[k] == (int) '-' || tmp[k] == (int) '.' || tmp[k] >= 97 && tmp[k] <= (int) 'z')
k--;
k++;
r.version = new String(tmp, k, i - k + 1, "ASCII");
}
r.type = file.getEntry("META-INF/MANIFEST.MF") == null
? MinecraftVersionRequest.MODIFIED : MinecraftVersionRequest.OK;
return r;
}
public static MinecraftVersionRequest minecraftVersion(File file) {
MinecraftVersionRequest r = new MinecraftVersionRequest();
if (file == null || !file.exists()) {
r.type = MinecraftVersionRequest.NOT_FOUND;
return r;
}
if (!file.isFile()) {
r.type = MinecraftVersionRequest.NOT_FILE;
return r;
}
if (!file.canRead()) {
r.type = MinecraftVersionRequest.UNREADABLE;
return r;
}
ZipFile f = null;
try {
f = new ZipFile(file);
ZipEntry minecraft = f
.getEntry("net/minecraft/client/Minecraft.class");
if (minecraft != null)
return getVersionOfOldMinecraft(f, minecraft);
ZipEntry main = f.getEntry("net/minecraft/client/main/Main.class");
ZipEntry minecraftserver = f.getEntry("net/minecraft/server/MinecraftServer.class");
if ((main != null) && (minecraftserver != null))
return getVersionOfNewMinecraft(f, minecraftserver);
r.type = MinecraftVersionRequest.INVALID;
return r;
} catch (IOException e) {
HMCLog.warn("Zip file is invalid", e);
r.type = MinecraftVersionRequest.INVALID_JAR;
return r;
} finally {
IOUtils.closeQuietly(f);
}
}
}

View File

@@ -0,0 +1,78 @@
/*
* Hello Minecraft!.
* Copyright (C) 2013 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;
import java.util.Map;
import java.util.Objects;
/**
*
* @author huangyuhui
* @param <K> K Type
* @param <V> V Type
*/
public class Pair<K, V> implements Map.Entry<K, V> {
public K key;
public V value;
public Pair(K k, V v) {
key = k;
value = v;
}
@Override
public K getKey() {
return key;
}
@Override
public V getValue() {
return value;
}
@Override
public V setValue(V value) {
V t = this.value;
this.value = value;
return t;
}
@Override
public int hashCode() {
int hash = 5;
hash = 29 * hash + Objects.hashCode(this.key);
hash = 29 * hash + Objects.hashCode(this.value);
return hash;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
final Pair<?, ?> other = (Pair<?, ?>) obj;
if (!Objects.equals(this.key, other.key))
return false;
return Objects.equals(this.value, other.value);
}
}

View File

@@ -0,0 +1,186 @@
/*
* Hello Minecraft!.
* Copyright (C) 2013 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;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.StringTokenizer;
import org.jackhuang.hmcl.api.func.Function;
import org.jackhuang.hmcl.api.func.Predicate;
/**
*
* @author huang
*/
public final class StrUtils {
private StrUtils() {
}
public static String makeCommand(List<String> cmd) {
StringBuilder cmdbuf = new StringBuilder(120);
for (int i = 0; i < cmd.size(); i++) {
if (i > 0)
cmdbuf.append(' ');
String s = cmd.get(i);
if (s.indexOf(' ') >= 0 || s.indexOf('\t') >= 0)
if (s.charAt(0) != '"') {
cmdbuf.append('"');
cmdbuf.append(s);
if (s.endsWith("\\"))
cmdbuf.append("\\");
cmdbuf.append('"');
} else if (s.endsWith("\""))
/*
* The argument has already been quoted.
*/
cmdbuf.append(s);
else
/*
* Unmatched quote for the argument.
*/
throw new IllegalArgumentException();
else
cmdbuf.append(s);
}
String str = cmdbuf.toString();
return str;
}
public static boolean startsWith(String base, String match) {
return base != null && base.startsWith(match);
}
public static boolean startsWithOne(Collection<String> a, String match) {
if (a == null)
return false;
for (String b : a)
if (startsWith(match, b))
return true;
return false;
}
public static boolean equalsOne(String base, String... a) {
for (String s : a)
if (base.equals(s))
return true;
return false;
}
public static boolean containsOne(List<String> base, List<String> match, Predicate<String> pred) {
for (String a : base)
for (String b : match)
if (pred.apply(a) && a.toLowerCase().contains(b.toLowerCase()))
return true;
return false;
}
public static int getCharShowTime(String s, char c) {
int res = 0;
for (int i = 0; i < s.length(); i++)
if (s.charAt(i) == c)
res++;
return res;
}
public static String formatVersion(String ver) {
if (isBlank(ver))
return null;
else
for (char ch : ver.toCharArray())
if ((ch < '0' || ch > '9') && ch != '.')
return null;
int i = getCharShowTime(ver, '.');
if (i == 1)
return ver + ".0";
else
return ver;
}
public static String parseParams(String addBefore, Collection<?> objects, String addAfter) {
return parseParams(addBefore, objects.toArray(), addAfter);
}
public static String parseParams(String addBefore, Object[] objects, String addAfter) {
return parseParams(t -> addBefore, objects, t -> addAfter);
}
public static String parseParams(Function<Object, String> beforeFunc, Object[] params, Function<Object, String> afterFunc) {
if (params == null)
return "";
StringBuilder sb = new StringBuilder();
for (int i = 0; i < params.length; i++) {
Object param = params[i];
String addBefore = beforeFunc.apply(param), addAfter = afterFunc.apply(param);
if (i > 0)
sb.append(addAfter).append(addBefore);
if (param == null)
sb.append("null");
else if (param.getClass().isArray()) {
sb.append("[");
if ((param instanceof Object[])) {
Object[] objs = (Object[]) param;
sb.append(parseParams(beforeFunc, objs, afterFunc));
} else
for (int j = 0; j < Array.getLength(param); j++) {
if (j > 0)
sb.append(addAfter);
sb.append(addBefore).append(Array.get(param, j));
}
sb.append("]");
} else
sb.append(addBefore).append(params[i]);
}
return sb.toString();
}
public static String[] tokenize(String paramString1) {
return tokenize(paramString1, " \t\n\r\f");
}
public static String[] tokenize(String str, String delim) {
ArrayList<String> al = new ArrayList<>();
StringTokenizer tokenizer = new StringTokenizer(str, delim);
while (tokenizer.hasMoreTokens()) {
delim = tokenizer.nextToken();
al.add(delim);
}
return al.toArray(new String[al.size()]);
}
public static boolean isBlank(String s) {
return s == null || s.trim().length() <= 0;
}
public static boolean isNotBlank(String s) {
return !isBlank(s);
}
public static String getStackTrace(Throwable t) {
StringWriter trace = new StringWriter();
PrintWriter writer = new PrintWriter(trace);
t.printStackTrace(writer);
return trace.toString();
}
}

View File

@@ -0,0 +1,105 @@
/*
* Hello Minecraft!.
* Copyright (C) 2013 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;
import org.jackhuang.hmcl.api.VersionNumber;
import org.jackhuang.hmcl.api.event.EventHandler;
import org.jackhuang.hmcl.util.net.NetUtils;
import com.google.gson.JsonSyntaxException;
import java.io.IOException;
import org.jackhuang.hmcl.api.HMCLog;
import java.util.Map;
import org.jackhuang.hmcl.api.HMCLApi;
import org.jackhuang.hmcl.api.event.SimpleEvent;
import org.jackhuang.hmcl.api.event.OutOfDateEvent;
/**
*
* @author huangyuhui
*/
public final class UpdateChecker implements IUpdateChecker {
private volatile boolean outOfDate = false;
public VersionNumber base;
public String versionString;
public String type;
private Map<String, String> download_link = null;
public UpdateChecker(VersionNumber base, String type) {
this.base = base;
this.type = type;
}
VersionNumber value;
public boolean isOutOfDate() {
return outOfDate;
}
@Override
public AbstractSwingWorker<VersionNumber> process(final boolean showMessage) {
return new AbstractSwingWorker<VersionNumber>() {
@Override
protected void work() throws Exception {
if (value == null) {
versionString = NetUtils.get("http://huangyuhui.duapp.com/info.php?type=" + type);
value = VersionNumber.check(versionString);
}
if (value == null) {
HMCLog.warn("Failed to check update...");
if (showMessage)
MessageBox.show(C.i18n("update.failed"));
} else if (VersionNumber.isOlder(base, value))
outOfDate = true;
if (outOfDate)
publish(value);
}
};
}
@Override
public VersionNumber getNewVersion() {
return value;
}
@Override
public synchronized AbstractSwingWorker<Map<String, String>> requestDownloadLink() {
return new AbstractSwingWorker<Map<String, String>>() {
@Override
protected void work() throws Exception {
if (download_link == null)
try {
download_link = C.GSON.<Map<String, String>>fromJson(NetUtils.get("http://huangyuhui.duapp.com/update_link.php?type=" + type), Map.class);
} catch (JsonSyntaxException | IOException e) {
HMCLog.warn("Failed to get update link.", e);
}
publish(download_link);
}
};
}
public final EventHandler<SimpleEvent<VersionNumber>> upgrade = new EventHandler<>();
@Override
public void checkOutdate() {
if (outOfDate)
if (HMCLApi.EVENT_BUS.fireChannelResulted(new OutOfDateEvent(this, getNewVersion())))
upgrade.fire(new SimpleEvent<>(this, getNewVersion()));
}
}

View File

@@ -0,0 +1,62 @@
/*
* Hello Minecraft!.
* Copyright (C) 2013 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;
import java.awt.HeadlessException;
import java.awt.Toolkit;
import java.awt.datatransfer.StringSelection;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.security.AccessController;
import java.security.PrivilegedExceptionAction;
/**
* @author huangyuhui
*/
public final class Utils {
private Utils() {
}
public static URL[] getURL() {
return ((URLClassLoader) Utils.class.getClassLoader()).getURLs();
}
public static void setClipborad(String text) {
try {
Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(text), null);
} catch(HeadlessException ignored) {
}
}
/**
* In order to fight against the permission manager by Minecraft Forge.
*
* @param status exit code
*/
public static void shutdownForcely(int status) throws Exception {
AccessController.doPrivileged((PrivilegedExceptionAction<Void>) () -> {
Class<?> z = Class.forName("java.lang.Shutdown");
Method exit = z.getDeclaredMethod("exit", int.class);
exit.setAccessible(true);
exit.invoke(z, status);
return null;
});
}
}

View File

@@ -0,0 +1,55 @@
/*
* Hello Minecraft!.
* Copyright (C) 2013 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.code;
import java.nio.charset.Charset;
import java.nio.charset.UnsupportedCharsetException;
import org.jackhuang.hmcl.util.sys.IOUtils;
public final class Charsets {
private Charsets() {
}
/*public static final Charset ISO_8859_1 = Charset.forName("ISO-8859-1");
public static final Charset US_ASCII = Charset.forName("US-ASCII");
public static final Charset UTF_16 = Charset.forName("UTF-16");
public static final Charset UTF_16BE = Charset.forName("UTF-16BE");
public static final Charset UTF_16LE = Charset.forName("UTF-16LE");*/
public static final Charset UTF_8 = Charset.forName("UTF-8");
public static final Charset DEFAULT_CHARSET = UTF_8;
public static Charset toCharset(String charset) {
if (charset == null) return Charset.defaultCharset();
try {
return Charset.forName(charset);
} catch(UnsupportedCharsetException ignored) {
return Charset.defaultCharset();
}
}
public static Charset toCharset() {
return toCharset(System.getProperty("sun.jnu.encoding", IOUtils.DEFAULT_CHARSET));
}
}

View File

@@ -0,0 +1,251 @@
/*
* Hello Minecraft!.
* Copyright (C) 2013 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.code;
import java.io.IOException;
import java.io.InputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
/**
*
* @author huangyuhui
*/
public final class DigestUtils {
private DigestUtils() {
}
private static final int STREAM_BUFFER_LENGTH = 1024;
private static byte[] digest(MessageDigest digest, InputStream data)
throws IOException {
return updateDigest(digest, data).digest();
}
public static MessageDigest getDigest(String algorithm) {
try {
return MessageDigest.getInstance(algorithm);
} catch (NoSuchAlgorithmException e) {
throw new IllegalArgumentException(e);
}
}
public static MessageDigest getMd2Digest() {
return getDigest("MD2");
}
public static MessageDigest getMd5Digest() {
return getDigest("MD5");
}
public static MessageDigest getSha1Digest() {
return getDigest("SHA-1");
}
public static MessageDigest getSha256Digest() {
return getDigest("SHA-256");
}
public static MessageDigest getSha384Digest() {
return getDigest("SHA-384");
}
public static MessageDigest getSha512Digest() {
return getDigest("SHA-512");
}
public static byte[] md2(byte[] data) {
return getMd2Digest().digest(data);
}
public static byte[] md2(InputStream data)
throws IOException {
return digest(getMd2Digest(), data);
}
public static byte[] md2(String data) {
return md2(data.getBytes(Charsets.UTF_8));
}
public static String md2Hex(byte[] data) {
return Hex.encodeHexString(md2(data));
}
public static String md2Hex(InputStream data)
throws IOException {
return Hex.encodeHexString(md2(data));
}
public static String md2Hex(String data) {
return Hex.encodeHexString(md2(data));
}
public static byte[] md5(byte[] data) {
return getMd5Digest().digest(data);
}
public static byte[] md5(InputStream data)
throws IOException {
return digest(getMd5Digest(), data);
}
public static byte[] md5(String data) {
return md5(data.getBytes(Charsets.UTF_8));
}
public static String md5Hex(byte[] data) {
return Hex.encodeHexString(md5(data));
}
public static String md5Hex(InputStream data)
throws IOException {
return Hex.encodeHexString(md5(data));
}
public static String md5Hex(String data) {
return Hex.encodeHexString(md5(data));
}
public static byte[] sha1(byte[] data) {
return getSha1Digest().digest(data);
}
public static byte[] sha1(InputStream data)
throws IOException {
return digest(getSha1Digest(), data);
}
public static byte[] sha1(String data) {
return sha1(data.getBytes(Charsets.UTF_8));
}
public static String sha1Hex(byte[] data) {
return Hex.encodeHexString(sha1(data));
}
public static String sha1Hex(InputStream data)
throws IOException {
return Hex.encodeHexString(sha1(data));
}
public static String sha1Hex(String data) {
return Hex.encodeHexString(sha1(data));
}
public static byte[] sha256(byte[] data) {
return getSha256Digest().digest(data);
}
public static byte[] sha256(InputStream data)
throws IOException {
return digest(getSha256Digest(), data);
}
public static byte[] sha256(String data) {
return sha256(data.getBytes(Charsets.UTF_8));
}
public static String sha256Hex(byte[] data) {
return Hex.encodeHexString(sha256(data));
}
public static String sha256Hex(InputStream data)
throws IOException {
return Hex.encodeHexString(sha256(data));
}
public static String sha256Hex(String data) {
return Hex.encodeHexString(sha256(data));
}
public static byte[] sha384(byte[] data) {
return getSha384Digest().digest(data);
}
public static byte[] sha384(InputStream data)
throws IOException {
return digest(getSha384Digest(), data);
}
public static byte[] sha384(String data) {
return sha384(data.getBytes(Charsets.UTF_8));
}
public static String sha384Hex(byte[] data) {
return Hex.encodeHexString(sha384(data));
}
public static String sha384Hex(InputStream data)
throws IOException {
return Hex.encodeHexString(sha384(data));
}
public static String sha384Hex(String data) {
return Hex.encodeHexString(sha384(data));
}
public static byte[] sha512(byte[] data) {
return getSha512Digest().digest(data);
}
public static byte[] sha512(InputStream data)
throws IOException {
return digest(getSha512Digest(), data);
}
public static byte[] sha512(String data) {
return sha512(data.getBytes(Charsets.UTF_8));
}
public static String sha512Hex(byte[] data) {
return Hex.encodeHexString(sha512(data));
}
public static String sha512Hex(InputStream data)
throws IOException {
return Hex.encodeHexString(sha512(data));
}
public static String sha512Hex(String data) {
return Hex.encodeHexString(sha512(data));
}
public static MessageDigest updateDigest(MessageDigest messageDigest, byte[] valueToDigest) {
messageDigest.update(valueToDigest);
return messageDigest;
}
public static MessageDigest updateDigest(MessageDigest digest, InputStream data)
throws IOException {
byte[] buffer = new byte[STREAM_BUFFER_LENGTH];
int read = data.read(buffer, 0, STREAM_BUFFER_LENGTH);
while (read > -1) {
digest.update(buffer, 0, read);
read = data.read(buffer, 0, STREAM_BUFFER_LENGTH);
}
return digest;
}
public static MessageDigest updateDigest(MessageDigest messageDigest, String valueToDigest) {
messageDigest.update(valueToDigest.getBytes(Charsets.UTF_8));
return messageDigest;
}
}

Some files were not shown because too many files have changed in this diff Show More