Compare commits

...
47 Commits
38 ... 63
Author SHA1 Message Date
南宫临风 c15ec71be9 update travis 2016-07-08 20:31:25 +08:00
南宫临风 93273d555d update README.md 2016-07-08 19:54:48 +08:00
南宫临风 793f81e287 update travis 2016-07-08 19:49:06 +08:00
南宫临风 d87b768fc0 修正模组信息解析问题 2016-07-07 18:01:58 +08:00
南宫临风 c07d2dc6d3 update link 2016-07-07 16:53:46 +08:00
南宫临风 95af0222f3 update dynamic provider 2016-07-07 14:32:35 +08:00
南宫临风 3f15eddb0c 修正使用java 8 API导致启动失败 2016-07-06 16:24:33 +08:00
南宫临风 c39f1ad75f 使用gradle管理buildNumber 2016-07-05 12:03:34 +08:00
南宫临风 f27b60e031 udpate version 2016-07-05 11:25:36 +08:00
南宫临风 5f1d0a5936 强制更新的时候不刷新标题栏 2016-07-04 18:22:01 +08:00
南宫临风 4fdadd5474 统计版本号 2016-07-04 18:16:34 +08:00
南宫临风 b2ea6d449e 忽略版本更新功能 2016-07-04 17:29:22 +08:00
南宫临风 3afff687b2 修改版本升级地址 2016-07-04 14:34:04 +08:00
evilwk 37ddecf629 加入强制升级和升级提示 2016-07-03 18:33:53 +08:00
南宫临风 27e58edb1f MainPagePanel content 2016-06-30 20:05:12 +08:00
南宫临风 3dd421a2bf fix library url parse 2016-06-29 16:54:29 +08:00
南宫临风 4537f9e48f 修正library补全Bug 2016-06-29 15:47:40 +08:00
南宫临风 94b6cda86e 添加可以在线控制的下载源 2016-06-28 20:48:27 +08:00
南宫临风 9ae010a391 改进自动下载数据排序
自动下载表格加入表头排序
2016-06-27 17:43:34 +08:00
南宫临风 13c105472e Update README.md 2016-06-27 10:23:39 +08:00
huanghongxun 88194e479e Update README.md 2016-06-07 23:00:07 +08:00
huangyuhui b36c194f67 Merge branch 'master' of https://github.com/huanghongxun/HMCL 2016-05-28 23:19:21 +08:00
huangyuhui 7c7b6e0fd9 Fixed #73 2016-05-28 23:17:49 +08:00
huanghongxun 01b77f1f5f Merge pull request #71 from Cat7373/master
Windows 启动脚本中允许跨分区 cd
2016-05-23 19:28:18 +08:00
Cat73 840a0ad840 Windows 启动脚本中允许跨分区 cd
修复跨分区的情况下一些文件如 logs 仍然生成在脚本所在目录,而不是 .minecraft 目录
2016-05-23 13:25:58 +08:00
huangyuhui ab1ca18488 Fixed NullPointerException when minecraftArguments=null 2016-05-23 13:09:30 +08:00
huangyuhui 75e0fa2def Fixed mainClass of macAppBundle 2016-05-16 18:10:30 +08:00
huangyuhui ca3ed47d21 Applied macosapp. Closes #63 2016-05-16 12:55:35 +08:00
huangyuhui 61a8ab5199 Supplements: 'SerializedName' 2016-05-14 20:24:42 +08:00
huangyuhui ce721c2e09 Reconstruct codes 2016-05-13 18:25:55 +08:00
huangyuhui 2862e3a6ee TWords in title bar can be dragged now 2016-05-11 13:42:46 +08:00
huangyuhui 75c4a24cbd Fixed #68 2016-05-10 13:24:06 +08:00
huanghongxun 1cf7d791cc Changes logo. Closes #67 2016-05-08 22:46:11 +08:00
huangyuhui 62fecc08ac Fixed #65 2016-05-08 10:04:23 +08:00
huangyuhui 491ca63342 Merge branch 'master' of https://github.com/huanghongxun/HMCL 2016-05-08 09:42:00 +08:00
huangyuhui b232a255e7 Fixed #66 2016-05-08 09:41:27 +08:00
huanghongxun 0870f42557 Merge pull request #64 from Cat7373/master
在 Windows 的启动脚本里增加一行 cd %appdata%
2016-05-03 19:12:59 +08:00
Cat73 85237eac7e 在 Windows 的启动脚本里增加一行 cd %appdata%
这样可以避免一些文件如 logs 生成在脚本所在目录,而不是 .minecraft 目录
2016-05-03 09:41:55 +08:00
huangyuhui 8d5088d2e4 Removed findbugs 2016-04-25 23:28:29 +08:00
huangyuhui 47f5bf1ea5 Added an options to disable online game complete check 2016-04-25 23:09:56 +08:00
huangyuhui deebadef3a Fixed #61 2016-04-11 13:40:06 +08:00
huangyuhui ce24ae4fae Fixed #60 2016-04-06 19:41:33 +08:00
huangyuhui 16152de39d Fixed ClassCastException 2016-04-05 22:16:29 +08:00
huangyuhui 4a2ac63d25 Added removal of installers for Issue #55 2016-03-28 20:57:01 +08:00
huangyuhui 5437968c83 Fixed #56 2016-03-28 20:45:06 +08:00
huangyuhui 8022252452 Fixed task system 2016-03-27 14:13:59 +08:00
huangyuhui ea6571fdb6 Fix #53 2016-03-26 13:02:47 +08:00
340 changed files with 2732 additions and 12742 deletions

No files matched your search

Executable → Regular
+2
View File
@@ -25,3 +25,5 @@ HMCL/build/
HMCLServer/
HMCSM/build/
MetroLookAndFeel/build/
/HMCUtils/build/
/HMCLaF/build/
+10 -1
View File
@@ -14,6 +14,15 @@
<task-args/>
<task-jvm-args/>
</task>
<task>
<display-name>createApp</display-name>
<non-blocking>no</non-blocking>
<task-names>
<name must-exist="yes">createApp</name>
</task-names>
<task-args/>
<task-jvm-args/>
</task>
</common-tasks>
<script-platform>
<spec-name>j2se</spec-name>
@@ -22,6 +31,6 @@
<license-header>
<name>GNU General Public License</name>
<template>license-header.txt</template>
<property name="organization">huangyuhui</property>
<property name="organization">HMCLSoft</property>
</license-header>
</gradle-project-properties>
+5 -3
View File
@@ -11,15 +11,15 @@ before_deploy:
- git push -q ${REPO} --tags
deploy:
provider: releases
api_key:
secure: lfFEwKtij/s9Ao/SKL7oVdsWcfASshZuwT27gllMrTx1ZQqLCG2I5mGJOp5ukx7lb3y4Tu316nFasLjy9NI3eLXSx9f0w4q+wMf0K5Yj9iOwu/TIn7JkLyHDMQ20qRwGxZplS2WUb669gIRg+l6jUHeTBW+dlTxr0mMa6WHIl8UDpHwfFrGC3i6dL4y6WlDwGl2rYktvOoA/iqvtglBcHuXaL2sDfhp5MXRcYULTIw+b9zmiQXP665DQN+Su5I3gwB4y5X8RH72GAacZn13fEIo5i8UUoHM7UQxM8y8DKSO3avyFCfpUle9+9sErQVW7b6JV1c+AoLWuAhNJLMlUTKarZE7UMqfjvoQHxX5hp2B2WHLEON/UJiGf25HJWx6VhJitE0/odujqnG/umesqki2jMTs6gPcxbc9iBjzUg+ZmFztdtC+Emnni7lpqVVl+Y5hsJRn3Z6rIiNgwmaE0r2OQgAm1vRYFc7jvOdRfyFG+uCRZF6nS56BzJ7dLc0WW8dTgSft6YFll4ZYL6hiI9qiIaBAK948y5HV+6VBWuO3xf/G2CUFbE1Y9qUCjToh3ahxNDzZvXB+rXqvlvNQOi5l8n594oQ61n45qIHSKlMBn9Bxhw29oWT5mo1aYIaNXbpzKam2w4mK0T3VrgTszu/3282wRbNr+VAwdGMfwW1s=
api_key: ${DEPLOY_REPO}
file:
- HMCL/build/libs/HMCL-${HMCL_VERSION_ROOT}.${TRAVIS_BUILD_NUMBER}.jar
- HMCL/build/libs/HMCL-${HMCL_VERSION_ROOT}.${TRAVIS_BUILD_NUMBER}.exe
- HMCL/build/libs/HMCL-${HMCL_VERSION_ROOT}.${TRAVIS_BUILD_NUMBER}-MacOSApp.zip
- HMCSM/build/libs/HMCSM-${HMCSM_VERSION_ROOT}.${TRAVIS_BUILD_NUMBER}.jar
- HMCSM/build/libs/HMCSM-${HMCSM_VERSION_ROOT}.${TRAVIS_BUILD_NUMBER}.exe
- HMCSM/build/libs/HMCSM-${HMCSM_VERSION_ROOT}.${TRAVIS_BUILD_NUMBER}-MacOSApp.zip
on:
repo: huanghongxun/HMCL
tags: false
skip_cleanup: true
install: echo "skip 'gradle assemble' step"
@@ -30,3 +30,5 @@ cache:
directories:
- "$HOME/.gradle/caches/"
- "$HOME/.gradle/wrapper/"
git:
depth: 1
+109 -47
View File
@@ -15,46 +15,6 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
import java.util.jar.JarOutputStream
import java.util.zip.ZipEntry
import java.util.zip.ZipFile
apply plugin: 'launch4j'
apply plugin: 'me.tatarka.retrolambda'
if (!hasProperty('mainClass')) {
ext.mainClass = 'org.jackhuang.hellominecraft.launcher.Main'
}
def buildnumber = System.getenv("TRAVIS_BUILD_NUMBER")
if (buildnumber == null)
buildnumber = System.getenv("BUILD_NUMBER")
if (buildnumber == null)
buildnumber = "233"
def versionroot = System.getenv("VERSION_ROOT")
if (versionroot == null)
versionroot = "2.4.1"
String mavenGroupId = 'HMCL'
String mavenVersion = versionroot + '.' + buildnumber
String bundleName = "Hello Minecraft! Launcher"
group = mavenGroupId
version = mavenVersion
String mavenArtifactId = name
task generateSources(type: Copy) {
from 'src/main/java'
into "$buildDir/generated-src"
filter(org.apache.tools.ant.filters.ReplaceTokens, tokens: [
'HELLO_MINECRAFT_LAUNCHER_VERSION_FOR_GRADLE_REPLACING': mavenVersion
])
}
compileJava.setSource "$buildDir/generated-src"
compileJava.dependsOn generateSources
buildscript {
repositories {
mavenCentral();
@@ -71,6 +31,109 @@ buildscript {
}
}
plugins {
id "edu.sc.seis.macAppBundle" version "2.1.6"
}
import java.util.jar.JarOutputStream
import java.util.zip.ZipEntry
import java.util.zip.ZipFile
apply plugin: 'launch4j'
apply plugin: 'me.tatarka.retrolambda'
if (!hasProperty('mainClass')) {
ext.mainClass = 'org.jackhuang.hellominecraft.launcher.Main'
}
def readBuildNumber() {
def versionPropsFile = file('version.properties')
if (versionPropsFile.canRead()) {
def Properties versionProps = new Properties()
versionProps.load(new FileInputStream(versionPropsFile))
def code = versionProps['VERSION_CODE'].toInteger() + 1
versionProps['VERSION_CODE'] = code.toString()
versionProps.store(versionPropsFile.newWriter(), null)
return code.toString()
}
return "233"
}
def buildnumber = System.getenv("TRAVIS_BUILD_NUMBER")
if (buildnumber == null)
buildnumber = System.getenv("BUILD_NUMBER")
if (buildnumber == null)
buildnumber = readBuildNumber()
def versionroot = System.getenv("VERSION_ROOT")
if (versionroot == null)
versionroot = "2.5.0"
String mavenGroupId = 'HMCL'
String mavenVersion = versionroot + '.' + buildnumber
String bundleName = "Hello Minecraft! Launcher"
group = mavenGroupId
version = mavenVersion
String mavenArtifactId = name
task generateSources(type: Copy) {
from 'src/main/java'
from 'src/core/java'
into "$buildDir/generated-src"
filter(org.apache.tools.ant.filters.ReplaceTokens, tokens: [
'HELLO_MINECRAFT_LAUNCHER_VERSION_FOR_GRADLE_REPLACING': mavenVersion
])
}
compileJava.setSource "$buildDir/generated-src"
compileJava.dependsOn generateSources
task macAppCompressed(type: Zip, dependsOn: createApp) {
archiveName "HMCL-$mavenVersion-MacOSApp.zip"
include '**'
destinationDir file("$buildDir/libs/")
from "$buildDir/macApp"
}
macAppBundle {
mainClassName = mainClass
icon = "src/main/icon.icns"
javaProperties.put("apple.laf.useScreenMenuBar", "true")
}
sourceSets {
main {
java {
srcDirs = [
'src/core/java/',
'src/main/java/'
]
}
resources {
srcDirs = ['src/main/resources/']
}
}
core {
java {
compileClasspath += main.compileClasspath
runtimeClasspath += main.runtimeClasspath
srcDirs 'src/core/java/'
}
resources {
srcDirs 'src/main/resources/'
}
}
}
configurations {
coreCompile.extendsFrom compile
coreRuntime.extendsFrom runtime
}
configure(install.repositories.mavenInstaller) {
pom.project {
groupId = mavenGroupId
@@ -80,8 +143,8 @@ configure(install.repositories.mavenInstaller) {
}
dependencies {
compile project(":MetroLookAndFeel")
compile project(":HMCLAPI")
compile project(":HMCLaF")
compile project(":HMCUtils")
compile group: "org.commonjava.googlecode.markdown4j", name: "markdown4j", version: "2.2-cj-1.0"
}
@@ -93,22 +156,20 @@ jar {
from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
manifest {
attributes 'Created-By' : 'Copyright(c) 2013-2016 huangyuhui.',
attributes 'Created-By' : 'Copyright(c) 2013-2016 HMCLSoft.',
'Main-Class' : mainClass
}
}
launch4j {
launch4jCmd = 'D:\\Develop\\Java\\Launch4j\\launch4j.exe'
//launch4jCmd = '/home/huangyuhui/softwares/launch4j/launch4j'
supportUrl = 'http://www.mcbbs.net/thread-142335-1-1.html'
supportUrl = 'http://www.hmclsoft.com'
jreMinVersion = '1.6.0'
mainClassName = mainClass
icon = new File(project.buildDir, '../icon.ico').absolutePath
version = mavenVersion
downloadUrl = 'http://java.com/download'
copyright = "Copyright(c) 2013-2015 huangyuhui."
copyright = "Copyright(c) 2013-2015 HMCLSoft."
jar = new File(project.buildDir, 'libs/' + mavenGroupId + '-' + mavenVersion + '.jar').absolutePath
outfile = mavenGroupId + '-' + mavenVersion + '.exe'
@@ -123,3 +184,4 @@ processResources {
build.dependsOn makeExecutable
build.dependsOn makePackGZ
build.dependsOn macAppCompressed
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
@@ -19,7 +19,6 @@ package org.jackhuang.hellominecraft.launcher.api;
import org.jackhuang.hellominecraft.launcher.core.GameException;
import org.jackhuang.hellominecraft.launcher.core.service.IMinecraftService;
import org.jackhuang.hellominecraft.launcher.setting.Profile;
import org.jackhuang.hellominecraft.launcher.core.auth.AuthenticationException;
import org.jackhuang.hellominecraft.launcher.core.auth.IAuthenticator;
import org.jackhuang.hellominecraft.launcher.core.auth.UserProfileProvider;
@@ -35,12 +34,12 @@ public interface IPlugin {
/**
* You can modify the application actions by this method.
*
* @param profile info to the Minecraft Loader
* @param obj minecraft service wanted
*
* @return For example, you can implement IMinecraftProvider to support
* MultiMC
*/
IMinecraftService provideMinecraftService(Profile profile);
IMinecraftService provideMinecraftService(Object obj);
/**
* Register authenticators by calling IAuthenticator.LOGINS.add.
@@ -18,7 +18,6 @@
package org.jackhuang.hellominecraft.launcher.api;
import org.jackhuang.hellominecraft.util.logging.HMCLog;
import org.jackhuang.hellominecraft.launcher.util.DefaultPlugin;
/**
*
@@ -26,17 +25,21 @@ import org.jackhuang.hellominecraft.launcher.util.DefaultPlugin;
*/
public class PluginManager {
private static IPlugin NOW_PLUGIN = new DefaultPlugin();
private static IPlugin NOW_PLUGIN;
public static void getServerPlugin() {
try {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
Class c = cl.loadClass("org.jackhuang.hellominecraft.launcher.server.ServerPlugin");
IPlugin p = (IPlugin) c.newInstance();
NOW_PLUGIN = p;
getPlugin(Thread.currentThread().getContextClassLoader().loadClass("org.jackhuang.hellominecraft.launcher.server.ServerPlugin"));
} catch (ClassNotFoundException ignore) {
}
}
public static void getPlugin(Class<?> cls) {
try {
IPlugin p = (IPlugin) cls.newInstance();
NOW_PLUGIN = p;
} catch (Exception e) {
HMCLog.err("Failed to new instance");
HMCLog.err("Failed to new instance");
}
}
@@ -20,7 +20,6 @@ package org.jackhuang.hellominecraft.launcher.core;
import java.io.File;
import java.io.IOException;
import org.jackhuang.hellominecraft.util.system.FileUtils;
import org.jackhuang.hellominecraft.util.system.IOUtils;
import org.jackhuang.hellominecraft.util.system.OS;
/**
@@ -63,14 +62,11 @@ public final class MCUtils {
}
public static File getInitGameDir() {
File gameDir = IOUtils.currentDir();
if (gameDir.exists()) {
gameDir = new File(gameDir, MCUtils.minecraft());
if (!gameDir.exists()) {
File newFile = MCUtils.getLocation();
if (newFile.exists())
gameDir = newFile;
}
File gameDir = new File(MCUtils.minecraft());
if (!gameDir.exists()) {
File newFile = MCUtils.getLocation();
if (newFile.exists())
gameDir = newFile;
}
return gameDir;
}
@@ -80,6 +76,6 @@ public final class MCUtils {
public static void tryWriteProfile(File gameDir) throws IOException {
File file = new File(gameDir, "launcher_profiles.json");
if (!file.exists())
FileUtils.writeStringToFile(file, PROFILE);
FileUtils.write(file, PROFILE);
}
}
@@ -17,6 +17,7 @@
*/
package org.jackhuang.hellominecraft.launcher.core.asset;
import com.google.gson.annotations.SerializedName;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
@@ -29,7 +30,11 @@ import java.util.Set;
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() {
@@ -66,7 +66,7 @@ public class AssetsMojangLoader extends IAssetsHandler {
public void executeTask() throws Throwable {
if (!areDependTasksSucceeded)
throw new IllegalStateException("Failed to get asset index");
String result = FileUtils.readFileToString(f);
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);
@@ -98,7 +98,7 @@ public class AssetsMojangLoader extends IAssetsHandler {
@Override
public Task getDownloadTask(IDownloadProvider sourceType) {
return new AssetsTask(sourceType.getAssetsDownloadURL());
return new AssetsTask(sourceType.getAssetsDownloadURL(), sourceType.getRetryAssetsDownloadURL());
}
@Override
@@ -34,6 +34,8 @@ import org.jackhuang.hellominecraft.util.code.DigestUtils;
import org.jackhuang.hellominecraft.util.system.IOUtils;
import org.jackhuang.hellominecraft.util.NetUtils;
import org.jackhuang.hellominecraft.util.OverridableSwingWorker;
import org.jackhuang.hellominecraft.util.StrUtils;
import org.jackhuang.hellominecraft.util.func.Function;
import org.jackhuang.hellominecraft.util.tasks.TaskInfo;
/**
@@ -89,30 +91,37 @@ public abstract class IAssetsHandler {
protected class AssetsTask extends TaskInfo {
ArrayList<Task> al;
String u;
ArrayList<Task> tasks;
String baseUrl;
String retryBaseUrl;
public AssetsTask(String url) {
public AssetsTask(String url, String retryUrl) {
super(C.i18n("assets.download"));
this.u = url;
this.baseUrl = url;
this.retryBaseUrl = retryUrl;
}
@Override
public void executeTask() {
if (assetsDownloadURLs == null || assetsLocalNames == null || contents == null)
throw new IllegalStateException(C.i18n("assets.not_refreshed"));
tasks = new ArrayList<>();
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;
String downloadUrl = baseUrl + mark;
String downloadRetryUrl = StrUtils.isNotBlank(retryBaseUrl) ? retryBaseUrl + mark : "";
File location = assetsLocalNames.get(i);
if (!location.getParentFile().exists() && !location.getParentFile().mkdirs())
HMCLog.warn("Failed to make directories: " + location.getParent());
if (location.isDirectory())
continue;
boolean need = true;
boolean needDownload = true;
try {
if (location.exists()) {
FileInputStream fis = new FileInputStream(location);
@@ -121,23 +130,39 @@ public abstract class IAssetsHandler {
if (contents.get(i).geteTag().equals(sha)) {
++hasDownloaded;
HMCLog.log("File " + assetsLocalNames.get(i) + " has been downloaded successfully, skipped downloading.");
if (ppl != null)
if (ppl != null) {
ppl.setProgress(this, hasDownloaded, max);
}
continue;
}
}
} catch (IOException e) {
HMCLog.warn("Failed to get hash: " + location, e);
need = !location.exists();
needDownload = !location.exists();
}
if (need)
al.add(new FileDownloadTask(url, location).setTag(mark));
if (needDownload) {
FileDownloadTask fileDownloadTask = new FileDownloadTask(downloadUrl, location);
fileDownloadTask.setTag(mark);
// retry
if (StrUtils.isNotBlank(downloadRetryUrl)) {
fileDownloadTask.setFailedCallbackReturnsNewURL(new Function<Integer, String>() {
@Override
public String apply(Integer t) {
return downloadRetryUrl;
}
});
}
tasks.add(fileDownloadTask);
}
}
}
@Override
public Collection<Task> getAfterTasks() {
return al;
return tasks;
}
}
}
@@ -22,7 +22,6 @@ import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import org.jackhuang.hellominecraft.util.C;
import org.jackhuang.hellominecraft.launcher.core.service.IMinecraftAssetService;
@@ -30,8 +29,7 @@ import org.jackhuang.hellominecraft.launcher.core.service.IMinecraftService;
import org.jackhuang.hellominecraft.launcher.core.version.AssetIndexDownloadInfo;
import org.jackhuang.hellominecraft.launcher.core.version.MinecraftVersion;
import org.jackhuang.hellominecraft.util.MessageBox;
import org.jackhuang.hellominecraft.util.Utils;
import org.jackhuang.hellominecraft.util.func.Function;
import org.jackhuang.hellominecraft.util.func.BiFunction;
import org.jackhuang.hellominecraft.util.logging.HMCLog;
import org.jackhuang.hellominecraft.util.tasks.Task;
import org.jackhuang.hellominecraft.util.tasks.TaskWindow;
@@ -58,23 +56,8 @@ public class MinecraftAssetService extends IMinecraftAssetService {
public Task downloadAssets(final MinecraftVersion mv) {
if (mv == null)
return null;
return new TaskInfo("Download Assets") {
Collection<Task> afters = new HashSet<>();
@Override
public Collection<Task> getDependTasks() {
return Arrays.asList(IAssetsHandler.ASSETS_HANDLER.getList(mv, service.asset()));
}
@Override
public void executeTask() {
}
@Override
public Collection<Task> getAfterTasks() {
return Arrays.asList(IAssetsHandler.ASSETS_HANDLER.getDownloadTask(service.getDownloadType().getProvider()));
}
};
return IAssetsHandler.ASSETS_HANDLER.getList(mv, service.asset())
.after(IAssetsHandler.ASSETS_HANDLER.getDownloadTask(service.getDownloadType().getProvider()));
}
@Override
@@ -151,7 +134,7 @@ public class MinecraftAssetService extends IMinecraftAssetService {
File objectsDir = new File(assetsDir, "objects");
File indexFile = new File(indexDir, assetVersion + ".json");
try {
AssetsIndex index = (AssetsIndex) C.GSON.fromJson(FileUtils.readFileToString(indexFile, "UTF-8"), AssetsIndex.class);
AssetsIndex index = (AssetsIndex) C.GSON.fromJson(FileUtils.read(indexFile, "UTF-8"), AssetsIndex.class);
String hash = ((AssetsObject) index.getFileMap().get(name)).getHash();
return new File(objectsDir, hash.substring(0, 2) + "/" + hash);
@@ -170,7 +153,7 @@ public class MinecraftAssetService extends IMinecraftAssetService {
return false;
try {
String assetIndexContent = FileUtils.readFileToString(indexFile, "UTF-8");
String assetIndexContent = FileUtils.read(indexFile, "UTF-8");
AssetsIndex index = (AssetsIndex) C.GSON.fromJson(assetIndexContent, AssetsIndex.class);
if (index == null)
@@ -198,7 +181,7 @@ public class MinecraftAssetService extends IMinecraftAssetService {
}
try {
String assetIndexContent = FileUtils.readFileToString(indexFile, "UTF-8");
String assetIndexContent = FileUtils.read(indexFile, "UTF-8");
AssetsIndex index = (AssetsIndex) C.GSON.fromJson(assetIndexContent, AssetsIndex.class);
if (index == null)
@@ -213,7 +196,7 @@ public class MinecraftAssetService extends IMinecraftAssetService {
if (original.exists()) {
cnt++;
if (!target.isFile())
FileUtils.copyFile(original, target, false);
FileUtils.copyFile(original, target);
}
}
// If the scale new format existent file is lower then 0.1, use the old format.
@@ -227,8 +210,8 @@ public class MinecraftAssetService extends IMinecraftAssetService {
return virtualRoot;
}
public final Function<MinecraftVersion, String> ASSET_PROVIDER_IMPL = t -> {
if (!checkAssetsExistance(t.getAssetsIndex()))
public final BiFunction<MinecraftVersion, Boolean, String> 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.execute(downloadAssets(t));
return reconstructAssets(t.getAssetsIndex()).getAbsolutePath();
@@ -96,7 +96,8 @@ public abstract class IAuthenticator {
public void onLoadSettings(Map m) {
if (m == null)
return;
username = (String) m.get("IAuthenticator_UserName");
Object o = m.get("IAuthenticator_UserName");
username = o instanceof String ? (String) o : "";
}
public String getUserName() {
@@ -17,14 +17,24 @@
*/
package org.jackhuang.hellominecraft.launcher.core.auth.yggdrasil;
import com.google.gson.annotations.SerializedName;
public class Response {
public String accessToken, clientToken;
@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;
}
@@ -148,7 +148,7 @@ public class YggdrasilAuthentication {
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.contains("Invalid token"))
if (response.errorMessage != null && response.errorMessage.contains("Invalid token"))
response.errorMessage = C.i18n("login.invalid_token");
throw new AuthenticationException("Request error: " + response.errorMessage);
}
@@ -25,7 +25,7 @@ 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/");
return str == null ? null : str.replace("http://files.minecraftforge.net/maven", "http://ftb.cursecdn.com/FTB2/maven");
}
}
@@ -29,10 +29,12 @@ public class DownloadLibraryJob {
public IMinecraftLibrary lib;
public String url;
public String retryUrl;
public File path;
public DownloadLibraryJob(IMinecraftLibrary n, String u, File p) {
public DownloadLibraryJob(IMinecraftLibrary n, String u, String retry, File p) {
url = u;
retryUrl = retry;
lib = n;
path = IOUtils.tryGetCanonicalFile(p);
}
@@ -29,7 +29,8 @@ public enum DownloadType {
Mojang("download.mojang", new MojangDownloadProvider()),
BMCL("download.BMCL", new BMCLAPIDownloadProvider()),
//RapidData("download.rapid_data", new RapidDataDownloadProvider()),
Curse("Curse CDN", new CurseDownloadProvider());
Curse("Curse CDN", new CurseDownloadProvider()),
Dynamic("HMCLSoft", DynamicDownloadProvider.getInstance());
private final String name;
private final IDownloadProvider provider;
@@ -47,7 +48,7 @@ public enum DownloadType {
return C.i18n(name);
}
private static DownloadType suggestedDownloadType = Mojang;
private static DownloadType suggestedDownloadType = Dynamic;
public static DownloadType getSuggestedDownloadType() {
return suggestedDownloadType;
@@ -0,0 +1,160 @@
/*
* 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.hellominecraft.launcher.core.download;
import com.google.gson.reflect.TypeToken;
import java.io.IOException;
import java.util.Map;
import org.jackhuang.hellominecraft.util.C;
import org.jackhuang.hellominecraft.util.NetUtils;
import org.jackhuang.hellominecraft.util.StrUtils;
/**
*
* @author evilwk <evilwk@gmail.com>
*/
public class DynamicDownloadProvider extends MojangDownloadProvider {
private static final String PROVIDER_ADDR = "http://client.api.mcgogogo.com:81/provider.php";
private volatile static DynamicDownloadProvider instance;
private String versionManifestAddr = null;
private String launcherMetaAddr = null;
private String launcherAddr = null;
private String librariesAddr = null;
private String assetsAddr = null;
public void setVersionManifestAddr(String versionManifestAddr) {
this.versionManifestAddr = versionManifestAddr;
}
public void setLauncherMetaAddr(String launcherMetaAddr) {
this.launcherMetaAddr = launcherMetaAddr;
}
public void setLauncherAddr(String launcherAddr) {
this.launcherAddr = launcherAddr;
}
public void setLibrariesAddr(String librariesAddr) {
this.librariesAddr = librariesAddr;
}
public void setAssetsAddr(String assetsAddr) {
this.assetsAddr = assetsAddr;
}
private DynamicDownloadProvider() {
}
public static DynamicDownloadProvider getInstance() {
if (instance == null) {
synchronized (DynamicDownloadProvider.class) {
if (instance == null) {
instance = new DynamicDownloadProvider();
}
}
}
return instance;
}
@Override
public String getRetryAssetsDownloadURL() {
return super.getAssetsDownloadURL();
}
@Override
public String getRetryLibraryDownloadURL() {
return super.getLibraryDownloadURL();
}
@Override
public String getAssetsDownloadURL() {
if (StrUtils.isNotBlank(assetsAddr)) {
return assetsAddr;
}
return super.getAssetsDownloadURL();
}
@Override
public String getLibraryDownloadURL() {
if (StrUtils.isNotBlank(librariesAddr)) {
return librariesAddr;
}
return super.getLibraryDownloadURL();
}
@Override
public String getVersionsListDownloadURL() {
if (StrUtils.isNotBlank(versionManifestAddr)) {
return versionManifestAddr;
}
return super.getVersionsListDownloadURL();
}
@Override
public String getParsedDownloadURL(String str) {
if (StrUtils.isNotBlank(librariesAddr)) {
str = str.replace("https://libraries.minecraft.net", librariesAddr);
}
if (StrUtils.isNotBlank(launcherMetaAddr)) {
str = str.replace("https://launchermeta.mojang.com", launcherMetaAddr);
}
if (StrUtils.isNotBlank(launcherMetaAddr)) {
str = str.replace("https://launcher.mojang.com", launcherAddr);
}
return super.getParsedDownloadURL(str);
}
public void init() {
new Thread() {
private String getValue(Map<String, String> addrInfo, String key) {
String value = null;
do {
if (!addrInfo.containsKey(key))
continue;
value = addrInfo.get(key);
} while(false);
return value;
}
@Override
public void run() {
try {
String providerInfo = NetUtils.get(PROVIDER_ADDR);
Map<String, String> addrInfo = null;
addrInfo = C.GSON.fromJson(providerInfo, new TypeToken<Map<String, String>>() {
}.getType());
if (addrInfo != null) {
setLibrariesAddr(getValue(addrInfo, "libraries"));
setAssetsAddr(getValue(addrInfo, "assets"));
setLauncherMetaAddr(getValue(addrInfo, "launcherMeta"));
setLauncherAddr(getValue(addrInfo, "launcher"));
setVersionManifestAddr(getValue(addrInfo, "versionManifest"));
}
} catch (IOException ex) {
}
}
}.start();
}
}
@@ -38,7 +38,7 @@ public abstract class IDownloadProvider {
return null;
}
}
public abstract InstallerVersionList getForgeInstaller();
public abstract InstallerVersionList getLiteLoaderInstaller();
@@ -46,6 +46,10 @@ public abstract class IDownloadProvider {
public abstract InstallerVersionList getOptiFineInstaller();
public abstract String getLibraryDownloadURL();
public String getRetryLibraryDownloadURL() {
return "";
}
public abstract String getVersionsDownloadURL();
@@ -55,6 +59,10 @@ public abstract class IDownloadProvider {
public abstract String getAssetsDownloadURL();
public String getRetryAssetsDownloadURL() {
return "";
}
/**
* For example, minecraft.json/assetIndex/url or
* minecraft.json/downloads/client/url
@@ -31,6 +31,7 @@ import org.jackhuang.hellominecraft.launcher.core.GameException;
import org.jackhuang.hellominecraft.launcher.core.service.IMinecraftService;
import org.jackhuang.hellominecraft.launcher.core.version.GameDownloadInfo;
import org.jackhuang.hellominecraft.launcher.core.version.IMinecraftLibrary;
import org.jackhuang.hellominecraft.launcher.core.version.LibraryDownloadInfo;
import org.jackhuang.hellominecraft.launcher.core.version.MinecraftVersion;
import org.jackhuang.hellominecraft.util.tasks.download.FileDownloadTask;
import org.jackhuang.hellominecraft.util.func.Function;
@@ -53,14 +54,17 @@ public class MinecraftDownloadService extends IMinecraftDownloadService {
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.getDownloadInfo() != null) {
File ff = l.getFilePath(service.baseDirectory());
for (IMinecraftLibrary libraryInfo : v.getLibraries())
if (libraryInfo != null && libraryInfo.allow() && libraryInfo.getDownloadInfo() != null) {
File ff = libraryInfo.getFilePath(service.baseDirectory());
if (!ff.exists()) {
String libURL = l.getDownloadInfo().getUrl(service.getDownloadType());
if (libURL != null)
downloadLibraries.add(new DownloadLibraryJob(l, libURL, ff));
LibraryDownloadInfo downloadInfo = libraryInfo.getDownloadInfo();
String downloadUrl = downloadInfo.getUrl(service.getDownloadType());
String retryDownloadUrl = downloadInfo.getRetryUrl(service.getDownloadType());
if (downloadUrl != null)
downloadLibraries.add(new DownloadLibraryJob(libraryInfo, downloadUrl, retryDownloadUrl, ff));
}
}
return downloadLibraries;
@@ -85,7 +89,7 @@ public class MinecraftDownloadService extends IMinecraftDownloadService {
if (mvj.exists() && !mvj.delete())
HMCLog.warn("Failed to delete " + mvj);
try {
MinecraftVersion mv = C.GSON.fromJson(FileUtils.readFileToStringQuietly(new File(vpath, id + ".json")), MinecraftVersion.class);
MinecraftVersion mv = C.GSON.fromJson(FileUtils.readQuietly(new File(vpath, id + ".json")), MinecraftVersion.class);
if (mv == null)
throw new JsonSyntaxException("incorrect version");
@@ -114,10 +118,13 @@ public class MinecraftDownloadService extends IMinecraftDownloadService {
}
@Override
public String apply(Integer t) {
return DownloadType.values()[t / 2].getProvider().getVersionsDownloadURL() + suffix;
public String apply(Integer repeat) {
int index = repeat / 2;
if (index > DownloadType.values().length) {
index = 0;
}
return DownloadType.values()[index].getProvider().getVersionsDownloadURL() + suffix;
}
}
@Override
@@ -29,6 +29,6 @@ public class MinecraftRemoteVersion {
public String getUrl(DownloadType type) {
if (url == null)
return type.getProvider().getVersionsDownloadURL() + id + "/" + id + ".json";
return url;
return type.getProvider().getParsedDownloadURL(url);
}
}
@@ -17,6 +17,7 @@
*/
package org.jackhuang.hellominecraft.launcher.core.download;
import com.google.gson.annotations.SerializedName;
import java.util.ArrayList;
import org.jackhuang.hellominecraft.util.C;
import org.jackhuang.hellominecraft.util.NetUtils;
@@ -28,7 +29,9 @@ import org.jackhuang.hellominecraft.util.OverridableSwingWorker;
*/
public class MinecraftRemoteVersions {
@SerializedName("versions")
public ArrayList<MinecraftRemoteVersion> versions;
@SerializedName("latest")
public MinecraftRemoteLatestVersion latest;
private static volatile MinecraftRemoteVersions INSTANCE = null;
@@ -41,30 +41,35 @@ public class MojangDownloadProvider extends IDownloadProvider {
public InstallerVersionList getOptiFineInstaller() {
return org.jackhuang.hellominecraft.launcher.core.install.optifine.vanilla.OptiFineVersionList.getInstance();
}
// jars
@Override
public String getLibraryDownloadURL() {
return "https://libraries.minecraft.net";
}
// jars
@Override
public String getVersionsDownloadURL() {
return "http://s3.amazonaws.com/Minecraft.Download/versions/";
}
// resource
@Override
public String getIndexesDownloadURL() {
return "http://s3.amazonaws.com/Minecraft.Download/indexes/";
}
// game versions json
@Override
public String getVersionsListDownloadURL() {
return "https://launchermeta.mojang.com/mc/game/version_manifest.json";
}
// resource
@Override
public String getAssetsDownloadURL() {
return "http://resources.download.minecraft.net/";
return "https://resources.download.minecraft.net/";
}
@Override
@@ -0,0 +1,139 @@
/*
* 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.hellominecraft.launcher.core.install;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import org.jackhuang.hellominecraft.util.tasks.Task;
/**
*
* @author huangyuhui
*/
public abstract class InstallerVersionList {
/**
* 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.
*/
protected abstract List<InstallerVersion> getVersionsImpl(String mcVersion);
/**
* 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 int compareVersion(String verOne, String verTwo) {
String[] verInfoOne = verOne.split("\\.");
String[] verInfoTwo = verTwo.split("\\.");
int idx = 0;
int minLength = Math.min(verInfoOne.length, verInfoTwo.length);
int diff = 0;
while (idx < minLength
&& (diff = verInfoOne[idx].length() - verInfoTwo[idx].length()) == 0
&& (diff = verInfoOne[idx].compareTo(verInfoTwo[idx])) == 0) {
++idx;
}
diff = (diff != 0) ? diff : verInfoOne.length - verInfoTwo.length;
return (diff == 0) ? 0 : (diff < 0 ? -1 : 1);
}
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 compareVersion(selfVersion, 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);
}
}
}
@@ -27,10 +27,9 @@ import org.jackhuang.hellominecraft.launcher.core.install.liteloader.LiteLoaderV
import org.jackhuang.hellominecraft.launcher.core.install.optifine.OptiFineInstaller;
import org.jackhuang.hellominecraft.launcher.core.install.optifine.vanilla.OptiFineDownloadFormatter;
import org.jackhuang.hellominecraft.util.tasks.Task;
import org.jackhuang.hellominecraft.util.tasks.TaskInfo;
import org.jackhuang.hellominecraft.util.tasks.TaskWindow;
import org.jackhuang.hellominecraft.util.tasks.download.FileDownloadTask;
import org.jackhuang.hellominecraft.util.system.IOUtils;
import org.jackhuang.hellominecraft.util.tasks.DeleteFileTask;
/**
*
@@ -58,47 +57,31 @@ public final class MinecraftInstallerService extends IMinecraftInstallerService
@Override
public Task downloadForge(String installId, InstallerVersion v) {
return new TaskInfo("Forge Downloader") {
@Override
public void executeTask() {
File filepath = IOUtils.tryGetCanonicalFile(IOUtils.currentDirWithSeparator() + "forge-installer.jar");
if (v.installer != null)
TaskWindow.factory()
.append(new FileDownloadTask(service.getDownloadType().getProvider().getParsedDownloadURL(v.installer), filepath).setTag("forge"))
.append(new ForgeInstaller(service, filepath))
.create();
}
};
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")
.after(new ForgeInstaller(service, filepath))
.after(new DeleteFileTask(filepath));
}
@Override
public Task downloadOptiFine(String installId, InstallerVersion v) {
return new TaskInfo("OptiFine Downloader") {
@Override
public void executeTask() {
File filepath = IOUtils.tryGetCanonicalFile(IOUtils.currentDirWithSeparator() + "optifine-installer.jar");
if (v.installer != null) {
OptiFineDownloadFormatter task = new OptiFineDownloadFormatter(v.installer);
TaskWindow.factory().append(task)
.append(new FileDownloadTask(filepath).registerPreviousResult(task).setTag("optifine"))
.append(new OptiFineInstaller(service, installId, v, filepath))
.create();
}
}
};
File filepath = IOUtils.tryGetCanonicalFile("optifine-installer.jar");
if (v.installer == null)
return null;
OptiFineDownloadFormatter task = new OptiFineDownloadFormatter(v.installer);
return task.after(new FileDownloadTask(filepath).registerPreviousResult(task).setTag("optifine"))
.after(new OptiFineInstaller(service, installId, v, filepath))
.after(new DeleteFileTask(filepath));
}
@Override
public Task downloadLiteLoader(String installId, InstallerVersion v) {
return new TaskInfo("LiteLoader Downloader") {
@Override
public void executeTask() {
File filepath = IOUtils.tryGetCanonicalFile(IOUtils.currentDirWithSeparator() + "liteloader-universal.jar");
FileDownloadTask task = (FileDownloadTask) new FileDownloadTask(v.universal, filepath).setTag("LiteLoader");
TaskWindow.factory()
.append(task).append(new LiteLoaderInstaller(service, installId, (LiteLoaderVersionList.LiteLoaderInstallerVersion) v).registerPreviousResult(task))
.create();
}
};
File filepath = IOUtils.tryGetCanonicalFile("liteloader-universal.jar");
FileDownloadTask task = (FileDownloadTask) new FileDownloadTask(v.universal, filepath).setTag("LiteLoader");
return task.after(new LiteLoaderInstaller(service, installId, (LiteLoaderVersionList.LiteLoaderInstallerVersion) v).registerPreviousResult(task))
.after(new DeleteFileTask(filepath));
}
}
@@ -23,7 +23,6 @@ import java.util.ArrayList;
import org.jackhuang.hellominecraft.util.logging.HMCLog;
import org.jackhuang.hellominecraft.util.system.Compressor;
import org.jackhuang.hellominecraft.util.system.FileUtils;
import org.jackhuang.hellominecraft.util.system.IOUtils;
/**
*
@@ -40,7 +39,7 @@ public class PackMinecraftInstaller {
}
public void install() throws IOException {
File file = new File(IOUtils.currentDir(), "HMCL-MERGE-TEMP");
File file = new File("HMCL-MERGE-TEMP");
if (!file.exists() && !file.mkdirs())
HMCLog.warn("Failed to make directories: " + file);
for (String src1 : src)
@@ -18,7 +18,6 @@
package org.jackhuang.hellominecraft.launcher.core.install.forge;
import org.jackhuang.hellominecraft.launcher.core.install.InstallProfile;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
@@ -32,6 +31,7 @@ import org.jackhuang.hellominecraft.util.system.FileUtils;
import org.jackhuang.hellominecraft.util.NetUtils;
import org.jackhuang.hellominecraft.launcher.core.version.MinecraftLibrary;
import org.jackhuang.hellominecraft.util.MessageBox;
import org.jackhuang.hellominecraft.util.system.IOUtils;
/**
*
@@ -79,10 +79,8 @@ public class ForgeInstaller extends Task {
File file = new File(gameDir, "libraries/" + forge.getDownloadInfo().path);
if (file.getParentFile().mkdirs())
HMCLog.warn("Failed to make library directory " + file.getParent());
try (FileOutputStream fos = new FileOutputStream(file); BufferedOutputStream bos = new BufferedOutputStream(fos)) {
int c;
while ((c = is.read()) != -1)
bos.write((byte) c);
try (FileOutputStream fos = new FileOutputStream(file)) {
IOUtils.copyStream(is, fos);
}
mp.version().refreshVersions();
}
@@ -17,20 +17,31 @@
*/
package org.jackhuang.hellominecraft.launcher.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() {
@@ -0,0 +1,139 @@
/*
* 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.hellominecraft.launcher.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.List;
import java.util.Map;
import org.jackhuang.hellominecraft.util.C;
import org.jackhuang.hellominecraft.launcher.core.download.DownloadType;
import org.jackhuang.hellominecraft.util.StrUtils;
import org.jackhuang.hellominecraft.launcher.core.install.InstallerVersionList;
import org.jackhuang.hellominecraft.launcher.core.install.InstallerVersionNewerComparator;
import org.jackhuang.hellominecraft.util.tasks.Task;
import org.jackhuang.hellominecraft.util.tasks.TaskInfo;
import org.jackhuang.hellominecraft.util.tasks.download.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;
public Map<String, List<InstallerVersion>> versionMap;
public List<InstallerVersion> versions;
@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);
}
@Override
public void executeTask() 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());
if (mcver == null) {
mcver = arr.getKey();
}
ArrayList<InstallerVersion> al = new ArrayList<>();
for (int num : arr.getValue()) {
MinecraftForgeVersion v = root.number.get(num);
InstallerVersion iv = new InstallerVersion(v.version, 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(mcver, al);
}
Collections.sort(versions, new InstallerVersionComparator());
}
};
}
@Override
public List<InstallerVersion> getVersionsImpl(String mcVersion) {
if (versions == null || versionMap == null)
return null;
if (StrUtils.isBlank(mcVersion))
return versions;
List c = versionMap.get(mcVersion);
if (c == null)
return versions;
Collections.sort(c, InstallerVersionComparator.INSTANCE);
return c;
}
@Override
public String getName() {
return "Forge - MinecraftForge Offical Site";
}
}
@@ -17,6 +17,7 @@
*/
package org.jackhuang.hellominecraft.launcher.core.install.forge;
import com.google.gson.annotations.SerializedName;
import java.util.Map;
/**
@@ -25,8 +26,22 @@ import java.util.Map;
*/
public class MinecraftForgeVersionRoot {
public String artifact, webpath, adfly, homepage, name;
public Map<String, int[]> branches, mcversion;
@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;
}
@@ -17,6 +17,7 @@
*/
package org.jackhuang.hellominecraft.launcher.core.install.liteloader;
import com.google.gson.annotations.SerializedName;
import java.util.Map;
/**
@@ -25,5 +26,6 @@ import java.util.Map;
*/
public class LiteLoaderMCVersions {
@SerializedName("artefacts")
public Map<String, Map<String, LiteLoaderVersion>> artefacts;
}
@@ -17,6 +17,7 @@
*/
package org.jackhuang.hellominecraft.launcher.core.install.liteloader;
import com.google.gson.annotations.SerializedName;
import org.jackhuang.hellominecraft.launcher.core.version.MinecraftLibrary;
/**
@@ -25,6 +26,16 @@ import org.jackhuang.hellominecraft.launcher.core.version.MinecraftLibrary;
*/
public class LiteLoaderVersion {
public String tweakClass, file, version, md5, timestamp;
@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;
}
@@ -19,6 +19,7 @@ package org.jackhuang.hellominecraft.launcher.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.List;
@@ -28,8 +29,10 @@ import org.jackhuang.hellominecraft.launcher.core.version.MinecraftLibrary;
import org.jackhuang.hellominecraft.launcher.core.install.InstallerVersionList;
import org.jackhuang.hellominecraft.launcher.core.install.InstallerVersionList.InstallerVersion;
import org.jackhuang.hellominecraft.launcher.core.install.InstallerVersionNewerComparator;
import org.jackhuang.hellominecraft.util.NetUtils;
import org.jackhuang.hellominecraft.util.StrUtils;
import org.jackhuang.hellominecraft.util.tasks.Task;
import org.jackhuang.hellominecraft.util.tasks.TaskInfo;
import org.jackhuang.hellominecraft.util.tasks.download.HTTPGetTask;
/**
*
@@ -50,38 +53,52 @@ public class LiteLoaderVersionList extends InstallerVersionList {
public List<InstallerVersion> versions;
@Override
public void refreshList(String[] needed) throws Exception {
String s = NetUtils.get(C.URL_LITELOADER_LIST);
public Task refresh(String[] needed) {
if (root != null)
return;
return null;
return new TaskInfo(C.i18n("install.liteloader.get_list")) {
HTTPGetTask task = new HTTPGetTask(C.URL_LITELOADER_LIST);
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 = "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);
@Override
public Collection<Task> getDependTasks() {
return Arrays.asList(task);
}
Collections.sort(al, new InstallerVersionNewerComparator());
versionMap.put(StrUtils.formatVersion(arr.getKey()), al);
}
Collections.sort(versions, InstallerVersionComparator.INSTANCE);
@Override
public void executeTask() 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 = "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
@@ -17,13 +17,20 @@
*/
package org.jackhuang.hellominecraft.launcher.core.install.liteloader;
import com.google.gson.annotations.SerializedName;
/**
*
* @author huangyuhui
*/
public class LiteLoaderVersionsMeta {
private String description, authors, url;
@SerializedName("versions")
private String description;
@SerializedName("authors")
private String authors;
@SerializedName("url")
private String url;
public LiteLoaderVersionsMeta() {
}
@@ -17,6 +17,7 @@
*/
package org.jackhuang.hellominecraft.launcher.core.install.liteloader;
import com.google.gson.annotations.SerializedName;
import java.util.Map;
/**
@@ -25,6 +26,8 @@ import java.util.Map;
*/
public class LiteLoaderVersionsRoot {
@SerializedName("versions")
public Map<String, LiteLoaderMCVersions> versions;
@SerializedName("meta")
public LiteLoaderVersionsMeta meta;
}
@@ -72,14 +72,15 @@ public class OptiFineInstaller extends Task implements PreviousResultRegistrar<F
mv.mainClass = "net.minecraft.launchwrapper.Launch";
mv.libraries.add(1, new MinecraftLibrary("net.minecraft:launchwrapper:1.7"));
}
mv.minecraftArguments += " --tweakClass optifine.OptiFineTweaker";
if (!mv.minecraftArguments.contains("FMLTweaker"))
mv.minecraftArguments += " --tweakClass optifine.OptiFineTweaker";
}
}
File loc = new File(service.baseDirectory(), "versions/" + mv.id);
if (!loc.exists() && loc.mkdirs())
HMCLog.warn("Failed to make directories: " + loc);
File json = new File(loc, mv.id + ".json");
FileUtils.writeStringToFile(json, C.GSON.toJson(mv, MinecraftVersion.class));
FileUtils.write(json, C.GSON.toJson(mv, MinecraftVersion.class));
service.version().refreshVersions();
}
@@ -20,6 +20,8 @@ package org.jackhuang.hellominecraft.launcher.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.List;
@@ -30,8 +32,10 @@ import org.jackhuang.hellominecraft.util.C;
import org.jackhuang.hellominecraft.util.ArrayUtils;
import org.jackhuang.hellominecraft.launcher.core.install.InstallerVersionList;
import org.jackhuang.hellominecraft.launcher.core.install.optifine.OptiFineVersion;
import org.jackhuang.hellominecraft.util.NetUtils;
import org.jackhuang.hellominecraft.util.StrUtils;
import org.jackhuang.hellominecraft.util.tasks.Task;
import org.jackhuang.hellominecraft.util.tasks.TaskInfo;
import org.jackhuang.hellominecraft.util.tasks.download.HTTPGetTask;
/**
*
@@ -55,34 +59,46 @@ public class OptiFineBMCLVersionList extends InstallerVersionList {
}.getType();
@Override
public void refreshList(String[] needed) throws Exception {
String s = NetUtils.get("http://bmclapi.bangbang93.com/optifine/versionlist");
public Task refresh(String[] needed) {
return new TaskInfo(C.i18n("install.optifine.get_list")) {
HTTPGetTask task = new HTTPGetTask("http://bmclapi.bangbang93.com/optifine/versionlist");
versionMap = new HashMap<>();
versions = new ArrayList<>();
if (s == null)
return;
root = C.GSON.fromJson(s, TYPE);
for (OptiFineVersion v : root) {
v.setMirror(v.getMirror().replace("http://optifine.net/http://optifine.net/", "http://optifine.net/"));
if (StrUtils.isBlank(v.getMCVersion())) {
Pattern p = Pattern.compile("OptiFine (.*) HD");
Matcher m = p.matcher(v.getVersion());
while (m.find())
v.setMCVersion(m.group(1));
@Override
public Collection<Task> getDependTasks() {
return Arrays.asList(task);
}
InstallerVersion iv = new InstallerVersion(v.getVersion(), StrUtils.formatVersion(v.getMCVersion()));
List<InstallerVersion> al = ArrayUtils.tryGetMapWithList(versionMap, StrUtils.formatVersion(v.getMCVersion()));
//String url = "http://bmclapi.bangbang93.com/optifine/" + iv.selfVersion.replace(" ", "%20");
iv.installer = iv.universal = v.getMirror();
al.add(iv);
versions.add(iv);
}
@Override
public void executeTask() throws Throwable {
String s = task.getResult();
Collections.sort(versions, InstallerVersionComparator.INSTANCE);
versionMap = new HashMap<>();
versions = new ArrayList<>();
if (s == null)
return;
root = C.GSON.fromJson(s, TYPE);
for (OptiFineVersion v : root) {
v.setMirror(v.getMirror().replace("http://optifine.net/http://optifine.net/", "http://optifine.net/"));
if (StrUtils.isBlank(v.getMCVersion())) {
Pattern p = Pattern.compile("OptiFine (.*) HD");
Matcher m = p.matcher(v.getVersion());
while (m.find())
v.setMCVersion(m.group(1));
}
InstallerVersion iv = new InstallerVersion(v.getVersion(), StrUtils.formatVersion(v.getMCVersion()));
List<InstallerVersion> al = ArrayUtils.tryGetMapWithList(versionMap, StrUtils.formatVersion(v.getMCVersion()));
//String url = "http://bmclapi.bangbang93.com/optifine/" + iv.selfVersion.replace(" ", "%20");
iv.installer = iv.universal = v.getMirror();
al.add(iv);
versions.add(iv);
}
Collections.sort(versions, InstallerVersionComparator.INSTANCE);
}
};
}
@Override
@@ -0,0 +1,155 @@
/*
* 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.hellominecraft.launcher.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.Map;
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.hellominecraft.launcher.core.install.InstallerVersionList;
import org.jackhuang.hellominecraft.launcher.core.install.optifine.OptiFineVersion;
import org.jackhuang.hellominecraft.util.ArrayUtils;
import org.jackhuang.hellominecraft.util.C;
import org.jackhuang.hellominecraft.util.StrUtils;
import org.jackhuang.hellominecraft.util.tasks.Task;
import org.jackhuang.hellominecraft.util.tasks.TaskInfo;
import org.jackhuang.hellominecraft.util.tasks.download.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();
public Map<String, List<InstallerVersion>> versionMap;
public List<InstallerVersion> versions;
@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);
}
@Override
public void executeTask() 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";
}
@Override
public List<InstallerVersion> getVersionsImpl(String mcVersion) {
if (versions == null || versionMap == null)
return null;
if (StrUtils.isBlank(mcVersion))
return versions;
List c = versionMap.get(mcVersion);
if (c == null)
return versions;
Collections.sort(c, InstallerVersionComparator.INSTANCE);
return c;
}
}
@@ -29,7 +29,6 @@ import org.jackhuang.hellominecraft.util.MessageBox;
import org.jackhuang.hellominecraft.util.system.OS;
import org.jackhuang.hellominecraft.util.system.Platform;
import org.jackhuang.hellominecraft.util.StrUtils;
import org.jackhuang.hellominecraft.util.Utils;
import org.jackhuang.hellominecraft.launcher.core.GameException;
import org.jackhuang.hellominecraft.launcher.core.auth.UserProfileProvider;
import org.jackhuang.hellominecraft.launcher.core.version.MinecraftVersion;
@@ -96,10 +96,12 @@ public class GameLauncher {
if (file != null)
FileUtils.cleanDirectoryQuietly(file);
HMCLog.log("Detecting libraries...");
launchingStateChangedEvent.execute(LaunchingState.DownloadingLibraries);
if (!downloadLibrariesEvent.execute(service.download().getDownloadLibraries(loader.getMinecraftVersion())))
throw new GameException("Failed to download libraries");
if (!options.isNotCheckGame()) {
HMCLog.log("Detecting libraries...");
launchingStateChangedEvent.execute(LaunchingState.DownloadingLibraries);
if (!downloadLibrariesEvent.execute(service.download().getDownloadLibraries(loader.getMinecraftVersion())))
throw new GameException("Failed to download libraries");
}
HMCLog.log("Unpacking natives...");
launchingStateChangedEvent.execute(LaunchingState.DecompressingNatives);
@@ -118,8 +120,8 @@ public class GameLauncher {
*
* @throws IOException failed creating process
*/
public void launch(List str) throws IOException {
if (!service.version().onLaunch())
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());
@@ -131,6 +133,7 @@ public class GameLauncher {
}
}
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!");
@@ -153,7 +156,7 @@ public class GameLauncher {
*/
public File makeLauncher(String launcherName, List str) throws IOException {
HMCLog.log("Making shell launcher...");
service.version().onLaunch();
service.version().onLaunch(options.getLaunchVersion());
boolean isWin = OS.os() == OS.WINDOWS;
File f = new File(launcherName + (isWin ? ".bat" : ".sh"));
if (!f.exists() && !f.createNewFile())
@@ -172,6 +175,8 @@ public class GameLauncher {
if (appdata != null) {
writer.write("set appdata=" + appdata);
writer.newLine();
writer.write("cd /D %appdata%");
writer.newLine();
}
}
if (StrUtils.isNotBlank(options.getPrecalledCommand())) {
@@ -30,7 +30,7 @@ public class LaunchOptions {
private String name, versionName, javaArgs, minecraftArgs, maxMemory, permSize, width, height, serverIp, wrapper;
private String proxyHost, proxyPort, proxyUser, proxyPass, javaDir, launchVersion, type, precalledCommand;
private boolean fullscreen, noJVMArgs;
private boolean fullscreen, noJVMArgs, notCheckGame;
private JdkVersion java;
private File gameDir;
private GameDirType gameDirType;
@@ -222,4 +222,12 @@ public class LaunchOptions {
public void setType(String type) {
this.type = type;
}
public boolean isNotCheckGame() {
return notCheckGame;
}
public void setNotCheckGame(boolean notCheckGame) {
this.notCheckGame = notCheckGame;
}
}
@@ -21,6 +21,8 @@ import java.io.File;
import java.net.URL;
import org.jackhuang.hellominecraft.util.C;
import org.jackhuang.hellominecraft.launcher.core.download.DownloadLibraryJob;
import org.jackhuang.hellominecraft.util.StrUtils;
import org.jackhuang.hellominecraft.util.func.Function;
import org.jackhuang.hellominecraft.util.tasks.download.FileDownloadTask;
/**
@@ -49,12 +51,20 @@ public class LibraryDownloadTask extends FileDownloadTask {
if (s.length == 3 && s[2].length() > 3)
job.url = "http://dl.liteloader.com/versions/com/mumfrey/liteloader/" + s[2].substring(0, s[2].length() - 3) + "/liteloader-" + s[2] + ".jar";
}
download(new URL(job.url), job.path);
download(new URL(job.url), job.retryUrl, job.path);
}
void download(URL url, File filePath) throws Throwable {
void download(URL url, String retryUrl, File filePath) throws Throwable {
this.url = url;
this.filePath = filePath;
if (StrUtils.isNotBlank(retryUrl)) {
this.failedCallbackReturnsNewURL = new Function<Integer, String>() {
@Override
public String apply(Integer t) {
return retryUrl;
}
};
}
super.executeTask();
}
@@ -19,9 +19,9 @@ package org.jackhuang.hellominecraft.launcher.core.launch;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.jackhuang.hellominecraft.util.StrUtils;
import org.jackhuang.hellominecraft.util.func.Function;
import org.jackhuang.hellominecraft.util.logging.HMCLog;
import org.jackhuang.hellominecraft.util.system.IOUtils;
import org.jackhuang.hellominecraft.util.system.OS;
@@ -30,6 +30,7 @@ import org.jackhuang.hellominecraft.launcher.core.auth.UserProfileProvider;
import org.jackhuang.hellominecraft.launcher.core.version.MinecraftLibrary;
import org.jackhuang.hellominecraft.launcher.core.version.MinecraftVersion;
import org.jackhuang.hellominecraft.launcher.core.service.IMinecraftService;
import org.jackhuang.hellominecraft.util.func.BiFunction;
/**
*
@@ -44,9 +45,24 @@ public class MinecraftLoader extends AbstractMinecraftLoader {
@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())
library.append(l.getFilePath(gameDir).getAbsolutePath()).append(File.pathSeparator);
if (l.allow() && !l.isRequiredToUnzip()) {
if (l.name.toLowerCase().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");
@@ -55,9 +71,11 @@ public class MinecraftLoader extends AbstractMinecraftLoader {
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.apply(version);
String game_assets = assetProvider.apply(version, !options.isNotCheckGame());
for (String t : splitted) {
t = t.replace("${auth_player_name}", lr.getUserName());
@@ -96,13 +114,13 @@ public class MinecraftLoader extends AbstractMinecraftLoader {
}
}
private final Function<MinecraftVersion, String> DEFAULT_ASSET_PROVIDER = t -> {
private final BiFunction<MinecraftVersion, Boolean, String> DEFAULT_ASSET_PROVIDER = (t, allow) -> {
return new File(service.baseDirectory(), "assets").getAbsolutePath();
};
private Function<MinecraftVersion, String> assetProvider = DEFAULT_ASSET_PROVIDER;
private BiFunction<MinecraftVersion, Boolean, String> assetProvider = DEFAULT_ASSET_PROVIDER;
public void setAssetProvider(Function<MinecraftVersion, String> assetProvider) {
public void setAssetProvider(BiFunction<MinecraftVersion, Boolean, String> assetProvider) {
if (assetProvider == null)
this.assetProvider = DEFAULT_ASSET_PROVIDER;
else
@@ -17,12 +17,15 @@
*/
package org.jackhuang.hellominecraft.launcher.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.lang.reflect.Type;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
@@ -37,9 +40,30 @@ import org.jackhuang.hellominecraft.util.system.FileUtils;
*/
public class ModInfo implements Comparable<ModInfo> {
@SerializedName("location")
public File location;
public String modid, name, description, author, version, mcversion, url, updateUrl, credits;
@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");
@@ -67,6 +91,8 @@ public class ModInfo implements Comparable<ModInfo> {
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
@@ -103,18 +129,30 @@ public class ModInfo implements Comparable<ModInfo> {
return name.endsWith(".zip") || name.endsWith(".jar") || name.endsWith("litemod");
}
private static final Type TYPE = new TypeToken<List<ModInfo>>() {
}.getType();
private static ModInfo getForgeModInfo(File f, ZipFile jar, ZipEntry entry) throws IOException {
ModInfo i = new ModInfo();
i.location = f;
List<ModInfo> m = C.GSON.fromJson(new InputStreamReader(jar.getInputStream(entry), "UTF-8"), TYPE);
if (m != null && m.size() > 0) {
i = m.get(0);
i.location = f;
}
return i;
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 {
@@ -164,13 +164,13 @@ public final class ModpackManager {
HMCLog.warn("Failed to rename incorrect json " + oldFile + " to " + nowFile);
File json = new File(nowFile, "pack.json");
MinecraftVersion mv = C.GSON.fromJson(FileUtils.readFileToString(json), MinecraftVersion.class);
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.writeStringToFile(json, C.GSON.toJson(mv));
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");
@@ -72,13 +72,6 @@ public abstract class IMinecraftProvider {
*/
public abstract boolean install(String version, Consumer<MinecraftVersion> callback);
/**
* Returns the thing like ".minecraft/resourcepacks".
*
* @return the thing
*/
public abstract File getResourcePacks();
/**
*
* @param v should be resolved
@@ -164,7 +157,7 @@ public abstract class IMinecraftProvider {
*
* @return if false, will break the launch process.
*/
public abstract boolean onLaunch();
public abstract boolean onLaunch(String id);
public File baseDirectory() {
return service.baseDirectory();
@@ -17,6 +17,7 @@
*/
package org.jackhuang.hellominecraft.launcher.core.version;
import com.google.gson.annotations.SerializedName;
import java.util.ArrayList;
import java.util.List;
import org.jackhuang.hellominecraft.util.StrUtils;
@@ -27,6 +28,7 @@ import org.jackhuang.hellominecraft.util.StrUtils;
*/
public class Extract implements Cloneable {
@SerializedName("exclude")
public List<String> exclude = new ArrayList<>();
public boolean allow(String path) {
@@ -17,6 +17,7 @@
*/
package org.jackhuang.hellominecraft.launcher.core.version;
import com.google.gson.annotations.SerializedName;
import org.jackhuang.hellominecraft.launcher.core.download.DownloadType;
/**
@@ -25,8 +26,11 @@ import org.jackhuang.hellominecraft.launcher.core.download.DownloadType;
*/
public class GameDownloadInfo implements Cloneable {
@SerializedName("sha1")
public String sha1;
@SerializedName("size")
public int size;
@SerializedName("url")
protected String url;
/**
@@ -17,6 +17,7 @@
*/
package org.jackhuang.hellominecraft.launcher.core.version;
import com.google.gson.annotations.SerializedName;
import java.util.Map;
/**
@@ -25,6 +26,8 @@ import java.util.Map;
*/
public class LibrariesDownloadInfo {
@SerializedName("classifiers")
Map<String, LibraryDownloadInfo> classifiers;
@SerializedName("artifact")
LibraryDownloadInfo artifact;
}
@@ -0,0 +1,78 @@
/*
* 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.hellominecraft.launcher.core.version;
import com.google.gson.annotations.SerializedName;
import org.jackhuang.hellominecraft.launcher.core.download.DownloadType;
import org.jackhuang.hellominecraft.launcher.core.download.IDownloadProvider;
import org.jackhuang.hellominecraft.util.StrUtils;
import org.jackhuang.hellominecraft.util.system.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) {
IDownloadProvider provider = dt.getProvider();
String downloadUrl = (forgeURL == null ? provider.getLibraryDownloadURL() : forgeURL);
if (StrUtils.isNotBlank(url) && allowSelf) {
downloadUrl = url;
} // forced replace: Let different download sources into force
return provider.getParsedDownloadURL(getUrlWithBaseUrl(downloadUrl));
}
public String getRetryUrl(DownloadType dt) {
IDownloadProvider provider = dt.getProvider();
String retryBaseUrl = provider.getRetryLibraryDownloadURL();
String downloadUrl = (forgeURL == null ? retryBaseUrl : forgeURL);
if (StrUtils.isNotBlank(url) && provider.isAllowedToUseSelfURL()) {
downloadUrl = url;
}
if (StrUtils.isBlank(downloadUrl)) {
return null;
}
downloadUrl = getUrlWithBaseUrl(downloadUrl);
if (downloadUrl.contains("minecraftforge")) { // fix forge url
downloadUrl = provider.getParsedDownloadURL(downloadUrl);
}
return downloadUrl;
}
private String getUrlWithBaseUrl(String baseUrl) {
if (!baseUrl.endsWith(".jar")) {
if (path == null) {
return null;
} else {
baseUrl = IOUtils.addURLSeparator(baseUrl) + path.replace('\\', '/');
}
}
return baseUrl;
}
}
@@ -17,6 +17,7 @@
*/
package org.jackhuang.hellominecraft.launcher.core.version;
import com.google.gson.annotations.SerializedName;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
@@ -30,10 +31,15 @@ import org.jackhuang.hellominecraft.util.StrUtils;
*/
public class MinecraftLibrary extends IMinecraftLibrary {
@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) {
@@ -42,29 +48,25 @@ public class MinecraftLibrary extends IMinecraftLibrary {
public MinecraftLibrary(ArrayList<Rules> rules, String url, Natives natives, String name, Extract extract, LibraryDownloadInfo downloads) {
super(name);
this.rules = rules == null ? null : (ArrayList<Rules>) rules.clone();
this.rules = (rules == null) ? null : (ArrayList<Rules>) rules.clone();
this.url = url;
this.natives = natives == null ? null : (Natives) natives.clone();
this.extract = extract == null ? null : (Extract) extract.clone();
this.natives = (natives == null) ? null : (Natives) natives.clone();
this.extract = (extract == null) ? null : (Extract) extract.clone();
}
/**
* is the library allowed to load.
*
* @return
*/
@Override
public boolean allow() {
if (rules != null) {
boolean flag = false;
for (Rules r : rules)
for (Rules r : rules) {
if ("disallow".equals(r.action()))
return false;
else if ("allow".equals(r.action()))
flag = true;
}
return flag;
} else
return true;
}
return true;
}
private String formatArch(String nati) {
@@ -89,17 +91,25 @@ public class MinecraftLibrary extends IMinecraftLibrary {
public String formatName() {
String[] s = name.split(":");
if (s.length < 3)
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)
if (natives != null) {
sb.append('-').append(getNative());
}
return sb.append(".jar").toString();
}
@Override
public File getFilePath(File gameDir) {
return new File(gameDir, "libraries/" + getDownloadInfo().path);
LibraryDownloadInfo info = getDownloadInfo();
if (info == null) {
return null;
}
return new File(gameDir, "libraries/" + info.path);
}
@Override
@@ -108,25 +118,34 @@ public class MinecraftLibrary extends IMinecraftLibrary {
}
public LibraryDownloadInfo getDownloadInfo() {
if (downloads == null)
if (downloads == null) {
downloads = new LibrariesDownloadInfo();
LibraryDownloadInfo info;
}
LibraryDownloadInfo info = null;
if (natives != null) {
if (downloads.classifiers == 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());
} else if (downloads.artifact == null)
} else {
if (!downloads.classifiers.containsKey(getNative())) {
downloads.classifiers.put(getNative(), info = new LibraryDownloadInfo());
} else {
info = downloads.classifiers.get(getNative());
}
}
} else if (downloads.artifact == null) {
downloads.artifact = info = new LibraryDownloadInfo();
else
} else {
info = downloads.artifact;
}
if (StrUtils.isBlank(info.path)) {
info.path = formatName();
if (info.path == null)
if (info.path == null) {
return null;
}
}
info.forgeURL = this.url;
return info;
}
@@ -17,6 +17,7 @@
*/
package org.jackhuang.hellominecraft.launcher.core.version;
import com.google.gson.annotations.SerializedName;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
@@ -37,14 +38,37 @@ import org.jackhuang.hellominecraft.util.Utils;
*/
public class MinecraftVersion implements Cloneable, Comparable<MinecraftVersion> {
public String minecraftArguments, mainClass, time, id, type, processArguments,
releaseTime, jar, inheritsFrom, runDir;
@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() {
@@ -111,7 +111,7 @@ public class MinecraftVersionManager extends IMinecraftProvider {
}
MinecraftVersion mcVersion;
try {
mcVersion = C.GSON.fromJson(FileUtils.readFileToString(jsonFile), MinecraftVersion.class);
mcVersion = C.GSON.fromJson(FileUtils.read(jsonFile), MinecraftVersion.class);
if (mcVersion == null)
throw new GameException("Wrong json format, got null.");
} catch (Exception e) {
@@ -119,7 +119,7 @@ public class MinecraftVersionManager extends IMinecraftProvider {
if (MessageBox.Show(C.i18n("launcher.versions_json_not_formatted", id), MessageBox.YES_NO_OPTION) == MessageBox.YES_OPTION) {
service.download().downloadMinecraftVersionJson(id);
try {
mcVersion = C.GSON.fromJson(FileUtils.readFileToString(jsonFile), MinecraftVersion.class);
mcVersion = C.GSON.fromJson(FileUtils.read(jsonFile), MinecraftVersion.class);
if (mcVersion == null)
throw new GameException("Wrong json format, got null.");
} catch (IOException | GameException | JsonSyntaxException ex) {
@@ -164,7 +164,7 @@ public class MinecraftVersionManager extends IMinecraftProvider {
public boolean renameVersion(String from, String to) {
try {
File fromJson = new File(versionRoot(from), from + ".json");
MinecraftVersion mcVersion = C.GSON.fromJson(FileUtils.readFileToString(fromJson), MinecraftVersion.class);
MinecraftVersion mcVersion = C.GSON.fromJson(FileUtils.read(fromJson), MinecraftVersion.class);
mcVersion.id = to;
FileUtils.writeQuietly(fromJson, C.GSON.toJson(mcVersion));
File toDir = versionRoot(to);
@@ -198,7 +198,7 @@ public class MinecraftVersionManager extends IMinecraftProvider {
return false;
if (callback != null) {
File mvt = new File(versionRoot(id), id + ".json");
MinecraftVersion v = C.GSON.fromJson(FileUtils.readFileToStringQuietly(mvt), MinecraftVersion.class);
MinecraftVersion v = C.GSON.fromJson(FileUtils.readQuietly(mvt), MinecraftVersion.class);
if (v == null)
return false;
callback.accept(v);
@@ -254,13 +254,8 @@ public class MinecraftVersionManager extends IMinecraftProvider {
}
@Override
public File getResourcePacks() {
return new File(service.baseDirectory(), "resourcepacks");
}
@Override
public boolean onLaunch() {
File resourcePacks = getResourcePacks();
public boolean onLaunch(String id) {
File resourcePacks = new File(getRunDirectory(id), "resourcepacks");
if (!resourcePacks.exists() && !resourcePacks.mkdirs())
HMCLog.warn("Failed to make resourcePacks: " + resourcePacks);
return true;
@@ -17,13 +17,20 @@
*/
package org.jackhuang.hellominecraft.launcher.core.version;
import com.google.gson.annotations.SerializedName;
/**
*
* @author huangyuhui
*/
public class Natives implements Cloneable {
public String windows, osx, linux;
@SerializedName("windows")
public String windows;
@SerializedName("osx")
public String osx;
@SerializedName("linux")
public String linux;
@Override
@SuppressWarnings("CloneDeclaresCloneNotSupported")
Loaded 100 of 340 files, more files were not shown because too many files have changed in this diff. Show more