Add: Common folder to save libraries and assets

This commit is contained in:
huangyuhui
2017-02-13 13:10:11 +08:00
parent eb311abe83
commit 20be2f76c6
25 changed files with 388 additions and 179 deletions

View File

@@ -0,0 +1,52 @@
/*
* 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.api.event.version;
import java.io.File;
import java.util.EventObject;
import org.jackhuang.hellominecraft.util.Wrapper;
/**
* This event gets fired when we getting minecraft library path.
* <br>
* This event is fired on the {@link org.jackhuang.hellominecraft.api.HMCLAPI#EVENT_BUS}
* @param source {@link org.jackhuang.hellominecraft.launcher.core.version.MinecraftLibrary}
* @param {@code Wrapper<File>} modify this thing to change to your wanted mc lib.
* @author huangyuhui
*/
public class MinecraftLibraryPathEvent extends EventObject {
String location;
Wrapper<File> file;
public MinecraftLibraryPathEvent(Object source, String location, Wrapper<File> value) {
super(source);
this.location = location;
this.file = value;
}
public String getLocation() {
return location;
}
public Wrapper<File> getFile() {
return file;
}
}

View File

@@ -50,9 +50,9 @@ public class AssetsMojangLoader extends IAssetsHandler {
public Task getList(final MinecraftVersion mv, final IMinecraftAssetService mp) { public Task getList(final MinecraftVersion mv, final IMinecraftAssetService mp) {
Objects.requireNonNull(mv); Objects.requireNonNull(mv);
String assetsId = mv.getAssetsIndex().getId(); String assetsId = mv.getAssetsIndex().getId();
File assets = mp.getAssets(); File assets = mp.getAssets(assetsId);
HMCLog.log("Gathering asset index: " + assetsId); HMCLog.log("Gathering asset index: " + assetsId);
File f = IOUtils.tryGetCanonicalFile(new File(assets, "indexes/" + assetsId + ".json")); File f = mp.getIndexFile(assetsId);
return new TaskInfo("Gather asset index") { return new TaskInfo("Gather asset index") {
@Override @Override
public Collection<Task> getDependTasks() { public Collection<Task> getDependTasks() {
@@ -72,7 +72,7 @@ public class AssetsMojangLoader extends IAssetsHandler {
AssetsIndex o = C.GSON.fromJson(result, AssetsIndex.class); AssetsIndex o = C.GSON.fromJson(result, AssetsIndex.class);
assetsDownloadURLs = new ArrayList<>(); assetsDownloadURLs = new ArrayList<>();
assetsLocalNames = new ArrayList<>(); assetsLocalNames = new ArrayList<>();
contents = new ArrayList<>(); assetsObjects = new ArrayList<>();
HashSet<String> loadedHashes = new HashSet<>(); HashSet<String> loadedHashes = new HashSet<>();
int pgs = 0; int pgs = 0;
if (o != null && o.getFileMap() != null) if (o != null && o.getFileMap() != null)
@@ -80,13 +80,9 @@ public class AssetsMojangLoader extends IAssetsHandler {
if (loadedHashes.contains(e.getValue().getHash())) if (loadedHashes.contains(e.getValue().getHash()))
continue; continue;
loadedHashes.add(e.getValue().getHash()); loadedHashes.add(e.getValue().getHash());
Contents c = new Contents(); assetsObjects.add(e.getValue());
c.eTag = e.getValue().getHash(); assetsDownloadURLs.add(e.getValue().getLocation());
c.key = c.eTag.substring(0, 2) + "/" + e.getValue().getHash(); assetsLocalNames.add(mp.getAssetObject(assetsId, e.getValue()));
c.size = e.getValue().getSize();
contents.add(c);
assetsDownloadURLs.add(c.key);
assetsLocalNames.add(new File(assets, "objects" + File.separator + c.key.replace("/", File.separator)));
if (ppl != null) if (ppl != null)
ppl.setProgress(this, ++pgs, o.getFileMap().size()); ppl.setProgress(this, ++pgs, o.getFileMap().size());
} }

View File

@@ -34,7 +34,9 @@ public class AssetsObject {
this.size = size; this.size = size;
} }
public AssetsObject() { public AssetsObject(String hash, long size) {
this.hash = hash;
this.size = size;
} }
public String getHash() { public String getHash() {
@@ -44,6 +46,10 @@ public class AssetsObject {
public long getSize() { public long getSize() {
return this.size; return this.size;
} }
public String getLocation() {
return hash.substring(0, 2) + "/" + hash;
}
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {

View File

@@ -1,72 +0,0 @@
/*
* 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.asset;
/**
*
* @author huangyuhui
*/
public class Contents {
public String key, eTag, lastModified, storageClass;
public Contents() {
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String geteTag() {
return eTag;
}
public void seteTag(String eTag) {
this.eTag = eTag;
}
public String getLastModified() {
return lastModified;
}
public void setLastModified(String lastModified) {
this.lastModified = lastModified;
}
public String getStorageClass() {
return storageClass;
}
public void setStorageClass(String storageClass) {
this.storageClass = storageClass;
}
public long getSize() {
return size;
}
public void setSize(long size) {
this.size = size;
}
public long size;
}

View File

@@ -45,7 +45,7 @@ public abstract class IAssetsHandler {
protected ArrayList<String> assetsDownloadURLs; protected ArrayList<String> assetsDownloadURLs;
protected ArrayList<File> assetsLocalNames; protected ArrayList<File> assetsLocalNames;
protected final String name; protected final String name;
protected List<Contents> contents; protected List<AssetsObject> assetsObjects;
public IAssetsHandler(String name) { public IAssetsHandler(String name) {
this.name = name; this.name = name;
@@ -98,7 +98,7 @@ public abstract class IAssetsHandler {
@Override @Override
public void executeTask(boolean areDependTasksSucceeded) { public void executeTask(boolean areDependTasksSucceeded) {
if (assetsDownloadURLs == null || assetsLocalNames == null || contents == null) if (assetsDownloadURLs == null || assetsLocalNames == null || assetsObjects == null)
throw new IllegalStateException(C.i18n("assets.not_refreshed")); throw new IllegalStateException(C.i18n("assets.not_refreshed"));
int max = assetsDownloadURLs.size(); int max = assetsDownloadURLs.size();
al = new ArrayList<>(); al = new ArrayList<>();
@@ -117,7 +117,7 @@ public abstract class IAssetsHandler {
FileInputStream fis = FileUtils.openInputStream(location); FileInputStream fis = FileUtils.openInputStream(location);
String sha = DigestUtils.sha1Hex(IOUtils.toByteArray(fis)); String sha = DigestUtils.sha1Hex(IOUtils.toByteArray(fis));
IOUtils.closeQuietly(fis); IOUtils.closeQuietly(fis);
if (contents.get(i).geteTag().equals(sha)) { if (assetsObjects.get(i).getHash().equals(sha)) {
++hasDownloaded; ++hasDownloaded;
HMCLog.log("File " + assetsLocalNames.get(i) + " has been downloaded successfully, skipped downloading."); HMCLog.log("File " + assetsLocalNames.get(i) + " has been downloaded successfully, skipped downloading.");
if (ppl != null) if (ppl != null)
@@ -130,7 +130,7 @@ public abstract class IAssetsHandler {
need = !location.exists(); need = !location.exists();
} }
if (need) if (need)
al.add(new FileDownloadTask(url, location).setTag(contents.get(i).geteTag())); al.add(new FileDownloadTask(url, location).setTag(assetsObjects.get(i).getHash()));
} }
} }

View File

@@ -70,7 +70,7 @@ public class MinecraftAssetService extends IMinecraftAssetService {
@Override @Override
public Task downloadMinecraftAssetsIndex(AssetIndexDownloadInfo assetIndex) { public Task downloadMinecraftAssetsIndex(AssetIndexDownloadInfo assetIndex) {
File assetsLocation = getAssets(); File assetsLocation = getAssets(assetIndex.getId());
if (!FileUtils.makeDirectory(assetsLocation)) if (!FileUtils.makeDirectory(assetsLocation))
HMCLog.warn("Failed to make directories: " + assetsLocation); HMCLog.warn("Failed to make directories: " + assetsLocation);
File assetsIndex = getIndexFile(assetIndex.getId()); File assetsIndex = getIndexFile(assetIndex.getId());
@@ -100,7 +100,7 @@ public class MinecraftAssetService extends IMinecraftAssetService {
@Override @Override
public boolean downloadMinecraftAssetsIndexAsync(AssetIndexDownloadInfo assetIndex) { public boolean downloadMinecraftAssetsIndexAsync(AssetIndexDownloadInfo assetIndex) {
File assetsDir = getAssets(); File assetsDir = getAssets(assetIndex.getId());
if (!FileUtils.makeDirectory(assetsDir)) if (!FileUtils.makeDirectory(assetsDir))
HMCLog.warn("Failed to make directories: " + assetsDir); HMCLog.warn("Failed to make directories: " + assetsDir);
File assetsIndex = getIndexFile(assetIndex.getId()); File assetsIndex = getIndexFile(assetIndex.getId());
@@ -111,8 +111,8 @@ public class MinecraftAssetService extends IMinecraftAssetService {
HMCLog.warn("Failed to rename " + assetsIndex + " to " + renamed); HMCLog.warn("Failed to rename " + assetsIndex + " to " + renamed);
} }
if (TaskWindow.factory() if (TaskWindow.factory()
.append(new FileDownloadTask(assetIndex.getUrl(service.getDownloadType()), IOUtils.tryGetCanonicalFile(assetsIndex), assetIndex.sha1).setTag(assetIndex.getId() + ".json")) .append(new FileDownloadTask(assetIndex.getUrl(service.getDownloadType()), IOUtils.tryGetCanonicalFile(assetsIndex), assetIndex.sha1).setTag(assetIndex.getId() + ".json"))
.execute()) { .execute()) {
if (renamed != null && !renamed.delete()) if (renamed != null && !renamed.delete())
HMCLog.warn("Failed to delete " + renamed + ", maybe you should do it."); HMCLog.warn("Failed to delete " + renamed + ", maybe you should do it.");
return true; return true;
@@ -123,30 +123,31 @@ public class MinecraftAssetService extends IMinecraftAssetService {
} }
@Override @Override
public File getAssets() { public File getAssets(String assetId) {
return new File(service.baseDirectory(), "assets"); return new File(service.baseDirectory(), "assets");
} }
private File getIndexFile(String assetVersion) {
return new File(getAssets(), "indexes/" + assetVersion + ".json");
}
@Override @Override
public File getAssetObject(String assetVersion, String name) throws IOException { public File getIndexFile(String assetId) {
try { return new File(getAssets(assetId), "indexes/" + assetId + ".json");
AssetsIndex index = (AssetsIndex) C.GSON.fromJson(FileUtils.read(getIndexFile(assetVersion), "UTF-8"), AssetsIndex.class); }
String hash = ((AssetsObject) index.getFileMap().get(name)).getHash(); @Override
return new File(getAssets(), "objects/" + hash.substring(0, 2) + "/" + hash); 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) { } catch (JsonSyntaxException e) {
throw new IOException("Assets file format malformed.", e); throw new IOException("Assets file format malformed.", e);
} }
} }
private boolean checkAssetsExistance(AssetIndexDownloadInfo assetIndex) { protected boolean checkAssetsExistance(AssetIndexDownloadInfo assetIndex) {
File indexFile = getIndexFile(assetIndex.getId()); String assetId = assetIndex.getId();
File indexFile = getIndexFile(assetId);
File assetDir = getAssets(assetId);
if (!getAssets().exists() || !indexFile.isFile()) if (!getAssets(assetId).exists() || !indexFile.isFile())
return false; return false;
try { try {
@@ -156,7 +157,7 @@ public class MinecraftAssetService extends IMinecraftAssetService {
if (index == null) if (index == null)
return false; return false;
for (Map.Entry<String, AssetsObject> entry : index.getFileMap().entrySet()) for (Map.Entry<String, AssetsObject> entry : index.getFileMap().entrySet())
if (!new File(getAssets(), "objects/" + ((AssetsObject) entry.getValue()).getHash().substring(0, 2) + "/" + ((AssetsObject) entry.getValue()).getHash()).exists()) if (!assetObjectPath(assetDir, (AssetsObject) entry.getValue()).exists())
return false; return false;
return true; return true;
} catch (IOException | JsonSyntaxException e) { } catch (IOException | JsonSyntaxException e) {
@@ -164,8 +165,8 @@ public class MinecraftAssetService extends IMinecraftAssetService {
} }
} }
private File reconstructAssets(AssetIndexDownloadInfo assetIndex) { protected File reconstructAssets(AssetIndexDownloadInfo assetIndex) {
File assetsDir = getAssets(); File assetsDir = getAssets(assetIndex.getId());
String assetVersion = assetIndex.getId(); String assetVersion = assetIndex.getId();
File indexFile = getIndexFile(assetVersion); File indexFile = getIndexFile(assetVersion);
File virtualRoot = new File(new File(assetsDir, "virtual"), assetVersion); File virtualRoot = new File(new File(assetsDir, "virtual"), assetVersion);
@@ -187,7 +188,7 @@ public class MinecraftAssetService extends IMinecraftAssetService {
int tot = index.getFileMap().entrySet().size(); int tot = index.getFileMap().entrySet().size();
for (Map.Entry<String, AssetsObject> entry : index.getFileMap().entrySet()) { for (Map.Entry<String, AssetsObject> entry : index.getFileMap().entrySet()) {
File target = new File(virtualRoot, (String) entry.getKey()); File target = new File(virtualRoot, (String) entry.getKey());
File original = new File(assetsDir, "objects/" + ((AssetsObject) entry.getValue()).getHash().substring(0, 2) + "/" + ((AssetsObject) entry.getValue()).getHash()); File original = assetObjectPath(assetsDir, (AssetsObject) entry.getValue());
if (original.exists()) { if (original.exists()) {
cnt++; cnt++;
if (!target.isFile()) if (!target.isFile())
@@ -205,6 +206,15 @@ public class MinecraftAssetService extends IMinecraftAssetService {
return virtualRoot; 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) -> { public final IAssetProvider ASSET_PROVIDER_IMPL = (t, allow) -> {
if (allow && !checkAssetsExistance(t.getAssetsIndex())) if (allow && !checkAssetsExistance(t.getAssetsIndex()))
if (MessageBox.show(C.i18n("assets.no_assets"), MessageBox.YES_NO_OPTION) == MessageBox.YES_OPTION) if (MessageBox.show(C.i18n("assets.no_assets"), MessageBox.YES_NO_OPTION) == MessageBox.YES_OPTION)

View File

@@ -85,7 +85,7 @@ public class MinecraftLoader extends AbstractMinecraftLoader {
t = t.replace("${version_type}", options.getType()); t = t.replace("${version_type}", options.getType());
t = t.replace("${game_directory}", service.version().getRunDirectory(version.id).getAbsolutePath()); t = t.replace("${game_directory}", service.version().getRunDirectory(version.id).getAbsolutePath());
t = t.replace("${game_assets}", game_assets); t = t.replace("${game_assets}", game_assets);
t = t.replace("${assets_root}", service.asset().getAssets().getAbsolutePath()); t = t.replace("${assets_root}", service.asset().getAssets(version.getAssetsIndex().getId()).getAbsolutePath());
t = t.replace("${auth_access_token}", lr.getAccessToken()); t = t.replace("${auth_access_token}", lr.getAccessToken());
t = t.replace("${user_type}", lr.getUserType()); t = t.replace("${user_type}", lr.getUserType());
t = t.replace("${assets_index_name}", version.getAssetsIndex().getId()); t = t.replace("${assets_index_name}", version.getAssetsIndex().getId());

View File

@@ -20,6 +20,7 @@ package org.jackhuang.hellominecraft.launcher.core.service;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import org.jackhuang.hellominecraft.launcher.core.GameException; import org.jackhuang.hellominecraft.launcher.core.GameException;
import org.jackhuang.hellominecraft.launcher.core.asset.AssetsObject;
import org.jackhuang.hellominecraft.launcher.core.version.AssetIndexDownloadInfo; import org.jackhuang.hellominecraft.launcher.core.version.AssetIndexDownloadInfo;
import org.jackhuang.hellominecraft.util.task.Task; import org.jackhuang.hellominecraft.util.task.Task;
@@ -35,20 +36,24 @@ public abstract class IMinecraftAssetService extends IMinecraftBasicService {
public abstract Task downloadAssets(String mcVersion) throws GameException; public abstract Task downloadAssets(String mcVersion) throws GameException;
public abstract File getAssets(); public abstract File getAssets(String assetId);
public abstract File getIndexFile(String assetId);
/** /**
* Redownload the Asset index json of the given version. * Redownload the Asset index json of the given version.
* *
* @param a the given version name * @param mcVersion the given version name
* *
* @return Is the action successful? * @return Is the action successful?
*/ */
public abstract boolean refreshAssetsIndex(String a) throws GameException; public abstract boolean refreshAssetsIndex(String mcVersion) throws GameException;
public abstract boolean downloadMinecraftAssetsIndexAsync(AssetIndexDownloadInfo assetsId); public abstract boolean downloadMinecraftAssetsIndexAsync(AssetIndexDownloadInfo assetId);
public abstract Task downloadMinecraftAssetsIndex(AssetIndexDownloadInfo assetsId); public abstract Task downloadMinecraftAssetsIndex(AssetIndexDownloadInfo assetId);
public abstract File getAssetObject(String assetVersion, String name) throws IOException; public abstract File getAssetObject(String assetVersion, String name) throws IOException;
public abstract File getAssetObject(String assetId, AssetsObject assetsObject);
} }

View File

@@ -40,10 +40,6 @@ public class AssetIndexDownloadInfo extends GameDownloadInfo {
return dt.getProvider().getIndexesDownloadURL() + id + ".json"; return dt.getProvider().getIndexesDownloadURL() + id + ".json";
} }
public String getId() {
return id;
}
public int getTotalSize() { public int getTotalSize() {
return totalSize; return totalSize;
} }

View File

@@ -36,7 +36,7 @@ public class GameDownloadInfo implements Cloneable {
/** /**
* Ready for AssetIndexDownloadInfo, and GameDownloadInfo also need this. * Ready for AssetIndexDownloadInfo, and GameDownloadInfo also need this.
*/ */
public String id = null; protected String id;
/** /**
* Get the game download url. * Get the game download url.
@@ -68,6 +68,10 @@ public class GameDownloadInfo implements Cloneable {
return dt.getProvider().getVersionsDownloadURL() + id + "/" + id + ".jar"; return dt.getProvider().getVersionsDownloadURL() + id + "/" + id + ".jar";
} }
public String getId() {
return id;
}
@Override @Override
public Object clone() { public Object clone() {
try { try {

View File

@@ -21,9 +21,12 @@ import com.google.gson.annotations.SerializedName;
import java.io.File; import java.io.File;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import org.jackhuang.hellominecraft.api.HMCLAPI;
import org.jackhuang.hellominecraft.launcher.api.event.version.MinecraftLibraryPathEvent;
import org.jackhuang.hellominecraft.util.sys.OS; import org.jackhuang.hellominecraft.util.sys.OS;
import org.jackhuang.hellominecraft.util.sys.Platform; import org.jackhuang.hellominecraft.util.sys.Platform;
import org.jackhuang.hellominecraft.util.StrUtils; import org.jackhuang.hellominecraft.util.StrUtils;
import org.jackhuang.hellominecraft.util.Wrapper;
/** /**
* *
@@ -104,7 +107,9 @@ public class MinecraftLibrary extends IMinecraftLibrary {
LibraryDownloadInfo info = getDownloadInfo(); LibraryDownloadInfo info = getDownloadInfo();
if (info == null) if (info == null)
return null; return null;
return new File(gameDir, "libraries/" + info.path); 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 @Override

View File

@@ -31,6 +31,7 @@ import org.jackhuang.hellominecraft.api.HMCLAPI;
import org.jackhuang.hellominecraft.launcher.api.event.config.AuthenticatorChangedEvent; import org.jackhuang.hellominecraft.launcher.api.event.config.AuthenticatorChangedEvent;
import org.jackhuang.hellominecraft.launcher.api.event.config.DownloadTypeChangedEvent; import org.jackhuang.hellominecraft.launcher.api.event.config.DownloadTypeChangedEvent;
import org.jackhuang.hellominecraft.launcher.api.event.config.ThemeChangedEvent; import org.jackhuang.hellominecraft.launcher.api.event.config.ThemeChangedEvent;
import org.jackhuang.hellominecraft.launcher.core.MCUtils;
import org.jackhuang.hellominecraft.util.sys.JdkVersion; import org.jackhuang.hellominecraft.util.sys.JdkVersion;
import org.jackhuang.hellominecraft.util.sys.OS; import org.jackhuang.hellominecraft.util.sys.OS;
@@ -44,6 +45,8 @@ public final class Config implements Cloneable {
private String last; private String last;
@SerializedName("bgpath") @SerializedName("bgpath")
private String bgpath; private String bgpath;
@SerializedName("commonpath")
private String commonpath;
@SerializedName("clientToken") @SerializedName("clientToken")
private final String clientToken; private final String clientToken;
@SerializedName("proxyHost") @SerializedName("proxyHost")
@@ -152,6 +155,15 @@ public final class Config implements Cloneable {
Settings.save(); Settings.save();
} }
public String getCommonpath() {
return commonpath;
}
public void setCommonpath(String commonpath) {
this.commonpath = commonpath;
Settings.save();
}
public String getClientToken() { public String getClientToken() {
return clientToken; return clientToken;
} }
@@ -210,6 +222,7 @@ public final class Config implements Cloneable {
theme = 4; theme = 4;
decorated = OS.os() == OS.LINUX; decorated = OS.os() == OS.LINUX;
auth = new HashMap<>(); auth = new HashMap<>();
commonpath = MCUtils.getWorkingDirectory("hmcl").getAbsolutePath();
} }
public DownloadType getDownloadSource() { public DownloadType getDownloadSource() {

View File

@@ -87,7 +87,7 @@ public final class GameSettingsPanel extends RepaintPage implements DropTargetLi
public GameSettingsPanel() { public GameSettingsPanel() {
HMCLAPI.EVENT_BUS.channel(RefreshedVersionsEvent.class).register(t -> { HMCLAPI.EVENT_BUS.channel(RefreshedVersionsEvent.class).register(t -> {
if (Settings.getLastProfile().service() == t.getValue()) if (Settings.getLastProfile().service() == t.getValue() && t.getValue().version().getVersions().isEmpty())
if (!showedNoVersion && Settings.getLastProfile().service().checkingModpack) { if (!showedNoVersion && Settings.getLastProfile().service().checkingModpack) {
showedNoVersion = true; showedNoVersion = true;
SwingUtilities.invokeLater(() -> { SwingUtilities.invokeLater(() -> {

View File

@@ -19,7 +19,7 @@
<Group type="102" attributes="0"> <Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0"> <Group type="102" alignment="1" attributes="0">
<Component id="lblProxy" min="-2" max="-2" attributes="0"/> <Component id="lblProxy" min="-2" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/> <EmptySpace max="32767" attributes="0"/>
<Component id="lblProxyHost" min="-2" max="-2" attributes="0"/> <Component id="lblProxyHost" min="-2" max="-2" attributes="0"/>
@@ -38,35 +38,9 @@
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Component id="txtProxyPassword" min="-2" pref="80" max="-2" attributes="0"/> <Component id="txtProxyPassword" min="-2" pref="80" max="-2" attributes="0"/>
</Group> </Group>
<Group type="102" alignment="0" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="lblDownloadSource" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="lblTheme" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="lblBackground" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="lblLang" alignment="0" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="cboLang" max="32767" attributes="0"/>
<Group type="102" alignment="1" attributes="0">
<Component id="txtBackgroundPath" pref="664" max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="btnSelBackgroundPath" min="-2" max="-2" attributes="0"/>
</Group>
<Component id="cboDownloadSource" alignment="1" max="32767" attributes="0"/>
<Component id="cboTheme" alignment="1" max="32767" attributes="0"/>
</Group>
</Group>
<Group type="102" attributes="0"> <Group type="102" attributes="0">
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0"> <Component id="chkDecorated" min="-2" max="-2" attributes="0"/>
<Component id="btnCheckUpdate" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="btnMCBBS" min="-2" max="-2" attributes="0"/>
</Group>
<Component id="lblAbout" min="-2" max="-2" attributes="0"/>
<Component id="lblModpack" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="lblRestart" alignment="0" min="-2" max="-2" attributes="0"/>
<Group type="102" attributes="0"> <Group type="102" attributes="0">
<Component id="chkEnableShadow" min="-2" max="-2" attributes="0"/> <Component id="chkEnableShadow" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
@@ -74,10 +48,42 @@
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Component id="chkEnableAnimation" min="-2" max="-2" attributes="0"/> <Component id="chkEnableAnimation" min="-2" max="-2" attributes="0"/>
</Group> </Group>
<Component id="chkDecorated" alignment="0" min="-2" max="-2" attributes="0"/> <Component id="lblAbout" alignment="0" min="-2" max="-2" attributes="0"/>
<Group type="102" alignment="0" attributes="0">
<Component id="btnCheckUpdate" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="btnMCBBS" min="-2" max="-2" attributes="0"/>
</Group>
<Component id="lblModpack" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="lblRestart" alignment="0" min="-2" max="-2" attributes="0"/>
</Group> </Group>
<EmptySpace min="0" pref="0" max="32767" attributes="0"/> <EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group> </Group>
<Group type="102" alignment="0" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="lblDownloadSource" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="lblTheme" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="lblBackground" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="lblLang" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="lblCommonPath" alignment="0" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Component id="txtCommonPath" max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="btnSetCommonPath" min="-2" max="-2" attributes="0"/>
</Group>
<Component id="cboLang" max="32767" attributes="0"/>
<Group type="102" alignment="1" attributes="0">
<Component id="txtBackgroundPath" pref="585" max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="btnSelBackgroundPath" min="-2" max="-2" attributes="0"/>
</Group>
<Component id="cboDownloadSource" alignment="1" max="32767" attributes="0"/>
<Component id="cboTheme" alignment="1" max="32767" attributes="0"/>
</Group>
</Group>
</Group> </Group>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
</Group> </Group>
@@ -86,6 +92,12 @@
<DimensionLayout dim="1"> <DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0"> <Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="lblCommonPath" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="txtCommonPath" alignment="3" min="-2" pref="26" max="-2" attributes="0"/>
<Component id="btnSetCommonPath" alignment="3" min="-2" pref="26" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0"> <Group type="103" groupAlignment="3" attributes="0">
<Component id="lblBackground" alignment="3" min="-2" max="-2" attributes="0"/> <Component id="lblBackground" alignment="3" min="-2" max="-2" attributes="0"/>
@@ -127,7 +139,7 @@
</Group> </Group>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Component id="chkDecorated" min="-2" max="-2" attributes="0"/> <Component id="chkDecorated" min="-2" max="-2" attributes="0"/>
<EmptySpace pref="6" max="32767" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0"> <Group type="103" groupAlignment="3" attributes="0">
<Component id="btnCheckUpdate" alignment="3" min="-2" pref="26" max="-2" attributes="0"/> <Component id="btnCheckUpdate" alignment="3" min="-2" pref="26" max="-2" attributes="0"/>
<Component id="btnMCBBS" alignment="3" max="32767" attributes="0"/> <Component id="btnMCBBS" alignment="3" max="32767" attributes="0"/>
@@ -138,7 +150,7 @@
<Component id="lblModpack" min="-2" max="-2" attributes="0"/> <Component id="lblModpack" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Component id="lblAbout" min="-2" max="-2" attributes="0"/> <Component id="lblAbout" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/> <EmptySpace min="-2" max="-2" attributes="0"/>
</Group> </Group>
</Group> </Group>
</DimensionLayout> </DimensionLayout>
@@ -367,5 +379,29 @@
<EventHandler event="itemStateChanged" listener="java.awt.event.ItemListener" parameters="java.awt.event.ItemEvent" handler="chkEnableAnimationItemStateChanged"/> <EventHandler event="itemStateChanged" listener="java.awt.event.ItemListener" parameters="java.awt.event.ItemEvent" handler="chkEnableAnimationItemStateChanged"/>
</Events> </Events>
</Component> </Component>
<Component class="javax.swing.JLabel" name="lblCommonPath">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/jackhuang/hellominecraft/lang/I18N.properties" key="launcher.common_location" replaceFormat="C.i18n(&quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JTextField" name="txtCommonPath">
<Properties>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/jackhuang/hellominecraft/lang/I18N.properties" key="launcher.commpath_tooltip" replaceFormat="C.i18n(&quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JButton" name="btnSetCommonPath">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/jackhuang/hellominecraft/lang/I18N.properties" key="ui.button.explore" replaceFormat="C.i18n(&quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="btnSetCommonPathActionPerformed"/>
</Events>
</Component>
</SubComponents> </SubComponents>
</Form> </Form>

View File

@@ -64,6 +64,7 @@ public class LauncherSettingsPanel extends RepaintPage {
cboLang.setSelectedIndex(id); cboLang.setSelectedIndex(id);
txtBackgroundPath.setText(Settings.getInstance().getBgpath()); txtBackgroundPath.setText(Settings.getInstance().getBgpath());
txtCommonPath.setText(Settings.getInstance().getCommonpath());
txtProxyHost.setText(Settings.getInstance().getProxyHost()); txtProxyHost.setText(Settings.getInstance().getProxyHost());
txtProxyPort.setText(Settings.getInstance().getProxyPort()); txtProxyPort.setText(Settings.getInstance().getProxyPort());
txtProxyUsername.setText(Settings.getInstance().getProxyUserName()); txtProxyUsername.setText(Settings.getInstance().getProxyUserName());
@@ -118,6 +119,9 @@ public class LauncherSettingsPanel extends RepaintPage {
btnMCBBS = new javax.swing.JButton(); btnMCBBS = new javax.swing.JButton();
chkEnableBlur = new javax.swing.JCheckBox(); chkEnableBlur = new javax.swing.JCheckBox();
chkEnableAnimation = new javax.swing.JCheckBox(); chkEnableAnimation = new javax.swing.JCheckBox();
lblCommonPath = new javax.swing.JLabel();
txtCommonPath = new javax.swing.JTextField();
btnSetCommonPath = new javax.swing.JButton();
cboDownloadSource.addItemListener(new java.awt.event.ItemListener() { cboDownloadSource.addItemListener(new java.awt.event.ItemListener() {
public void itemStateChanged(java.awt.event.ItemEvent evt) { public void itemStateChanged(java.awt.event.ItemEvent evt) {
@@ -252,6 +256,17 @@ public class LauncherSettingsPanel extends RepaintPage {
} }
}); });
lblCommonPath.setText(C.i18n("launcher.common_location")); // NOI18N
txtCommonPath.setToolTipText(C.i18n("launcher.commpath_tooltip")); // NOI18N
btnSetCommonPath.setText(C.i18n("ui.button.explore")); // NOI18N
btnSetCommonPath.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
btnSetCommonPathActionPerformed(evt);
}
});
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout); this.setLayout(layout);
layout.setHorizontalGroup( layout.setHorizontalGroup(
@@ -259,7 +274,7 @@ public class LauncherSettingsPanel extends RepaintPage {
.addGroup(layout.createSequentialGroup() .addGroup(layout.createSequentialGroup()
.addContainerGap() .addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup() .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addComponent(lblProxy) .addComponent(lblProxy)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(lblProxyHost) .addComponent(lblProxyHost)
@@ -279,42 +294,52 @@ public class LauncherSettingsPanel extends RepaintPage {
.addComponent(txtProxyPassword, javax.swing.GroupLayout.PREFERRED_SIZE, 80, javax.swing.GroupLayout.PREFERRED_SIZE)) .addComponent(txtProxyPassword, javax.swing.GroupLayout.PREFERRED_SIZE, 80, javax.swing.GroupLayout.PREFERRED_SIZE))
.addGroup(layout.createSequentialGroup() .addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(lblDownloadSource) .addComponent(chkDecorated)
.addComponent(lblTheme)
.addComponent(lblBackground)
.addComponent(lblLang))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(cboLang, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addComponent(txtBackgroundPath, javax.swing.GroupLayout.DEFAULT_SIZE, 664, Short.MAX_VALUE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(btnSelBackgroundPath))
.addComponent(cboDownloadSource, javax.swing.GroupLayout.Alignment.TRAILING, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(cboTheme, javax.swing.GroupLayout.Alignment.TRAILING, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))
.addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addComponent(btnCheckUpdate)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(btnMCBBS))
.addComponent(lblAbout, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(lblModpack, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(lblRestart)
.addGroup(layout.createSequentialGroup() .addGroup(layout.createSequentialGroup()
.addComponent(chkEnableShadow) .addComponent(chkEnableShadow)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(chkEnableBlur) .addComponent(chkEnableBlur)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(chkEnableAnimation)) .addComponent(chkEnableAnimation))
.addComponent(chkDecorated)) .addComponent(lblAbout, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(0, 0, Short.MAX_VALUE))) .addGroup(layout.createSequentialGroup()
.addComponent(btnCheckUpdate)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(btnMCBBS))
.addComponent(lblModpack, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(lblRestart))
.addGap(0, 0, Short.MAX_VALUE))
.addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(lblDownloadSource)
.addComponent(lblTheme)
.addComponent(lblBackground)
.addComponent(lblLang)
.addComponent(lblCommonPath))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addComponent(txtCommonPath)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(btnSetCommonPath))
.addComponent(cboLang, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addComponent(txtBackgroundPath, javax.swing.GroupLayout.DEFAULT_SIZE, 585, Short.MAX_VALUE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(btnSelBackgroundPath))
.addComponent(cboDownloadSource, javax.swing.GroupLayout.Alignment.TRAILING, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(cboTheme, javax.swing.GroupLayout.Alignment.TRAILING, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))))
.addContainerGap()) .addContainerGap())
); );
layout.setVerticalGroup( layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup() .addGroup(layout.createSequentialGroup()
.addContainerGap() .addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(lblCommonPath)
.addComponent(txtCommonPath, javax.swing.GroupLayout.PREFERRED_SIZE, 26, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(btnSetCommonPath, javax.swing.GroupLayout.PREFERRED_SIZE, 26, javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(lblBackground) .addComponent(lblBackground)
.addComponent(btnSelBackgroundPath, javax.swing.GroupLayout.PREFERRED_SIZE, 26, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(btnSelBackgroundPath, javax.swing.GroupLayout.PREFERRED_SIZE, 26, javax.swing.GroupLayout.PREFERRED_SIZE)
@@ -349,7 +374,7 @@ public class LauncherSettingsPanel extends RepaintPage {
.addComponent(chkEnableAnimation)) .addComponent(chkEnableAnimation))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(chkDecorated) .addComponent(chkDecorated)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 6, Short.MAX_VALUE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(btnCheckUpdate, javax.swing.GroupLayout.PREFERRED_SIZE, 26, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(btnCheckUpdate, javax.swing.GroupLayout.PREFERRED_SIZE, 26, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(btnMCBBS, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) .addComponent(btnMCBBS, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
@@ -447,10 +472,29 @@ public class LauncherSettingsPanel extends RepaintPage {
Settings.getInstance().setEnableAnimation(chkEnableAnimation.isSelected()); Settings.getInstance().setEnableAnimation(chkEnableAnimation.isSelected());
}//GEN-LAST:event_chkEnableAnimationItemStateChanged }//GEN-LAST:event_chkEnableAnimationItemStateChanged
private void btnSetCommonPathActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnSetCommonPathActionPerformed
JSystemFileChooser fc = new JSystemFileChooser();
fc.setFileSelectionMode(JSystemFileChooser.DIRECTORIES_ONLY);
fc.setDialogTitle(C.i18n("launcher.choose_commonpath"));
fc.setMultiSelectionEnabled(false);
fc.showOpenDialog(this);
if (fc.getSelectedFile() == null)
return;
try {
String path = fc.getSelectedFile().getCanonicalPath();
txtCommonPath.setText(path);
Settings.getInstance().setCommonpath(path);
} catch (IOException e) {
HMCLog.warn("Failed to set common path.", e);
MessageBox.show(C.i18n("ui.label.failed_set") + e.getMessage());
}
}//GEN-LAST:event_btnSetCommonPathActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables // Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JButton btnCheckUpdate; private javax.swing.JButton btnCheckUpdate;
private javax.swing.JButton btnMCBBS; private javax.swing.JButton btnMCBBS;
private javax.swing.JButton btnSelBackgroundPath; private javax.swing.JButton btnSelBackgroundPath;
private javax.swing.JButton btnSetCommonPath;
private javax.swing.JComboBox cboDownloadSource; private javax.swing.JComboBox cboDownloadSource;
private javax.swing.JComboBox cboLang; private javax.swing.JComboBox cboLang;
private javax.swing.JComboBox cboTheme; private javax.swing.JComboBox cboTheme;
@@ -460,6 +504,7 @@ public class LauncherSettingsPanel extends RepaintPage {
private javax.swing.JCheckBox chkEnableShadow; private javax.swing.JCheckBox chkEnableShadow;
private javax.swing.JLabel lblAbout; private javax.swing.JLabel lblAbout;
private javax.swing.JLabel lblBackground; private javax.swing.JLabel lblBackground;
private javax.swing.JLabel lblCommonPath;
private javax.swing.JLabel lblDownloadSource; private javax.swing.JLabel lblDownloadSource;
private javax.swing.JLabel lblLang; private javax.swing.JLabel lblLang;
private javax.swing.JLabel lblModpack; private javax.swing.JLabel lblModpack;
@@ -471,6 +516,7 @@ public class LauncherSettingsPanel extends RepaintPage {
private javax.swing.JLabel lblRestart; private javax.swing.JLabel lblRestart;
private javax.swing.JLabel lblTheme; private javax.swing.JLabel lblTheme;
private javax.swing.JTextField txtBackgroundPath; private javax.swing.JTextField txtBackgroundPath;
private javax.swing.JTextField txtCommonPath;
private javax.swing.JTextField txtProxyHost; private javax.swing.JTextField txtProxyHost;
private javax.swing.JTextField txtProxyPassword; private javax.swing.JTextField txtProxyPassword;
private javax.swing.JTextField txtProxyPort; private javax.swing.JTextField txtProxyPort;

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.hellominecraft.launcher.util;
import java.io.File;
import org.jackhuang.hellominecraft.launcher.core.asset.MinecraftAssetService;
import org.jackhuang.hellominecraft.launcher.core.service.IMinecraftService;
import org.jackhuang.hellominecraft.launcher.setting.Settings;
/**
*
* @author huang
*/
public class HMCLAssetService extends MinecraftAssetService {
public HMCLAssetService(IMinecraftService service) {
super(service);
}
private boolean useSelf(String assetId) {
return new File(service.baseDirectory(), "assets/indexes/" + assetId + ",json").exists();
}
@Override
public File getAssets(String assetId) {
if (useSelf(assetId))
return new File(service.baseDirectory(), "assets");
else
return new File(Settings.getInstance().getCommonpath(), "assets");
}
}

View File

@@ -18,8 +18,11 @@
package org.jackhuang.hellominecraft.launcher.util; package org.jackhuang.hellominecraft.launcher.util;
import java.io.File; import java.io.File;
import org.jackhuang.hellominecraft.api.HMCLAPI;
import org.jackhuang.hellominecraft.launcher.api.event.version.MinecraftLibraryPathEvent;
import org.jackhuang.hellominecraft.launcher.core.version.GameDirType; import org.jackhuang.hellominecraft.launcher.core.version.GameDirType;
import org.jackhuang.hellominecraft.launcher.core.version.MinecraftVersionManager; import org.jackhuang.hellominecraft.launcher.core.version.MinecraftVersionManager;
import org.jackhuang.hellominecraft.launcher.setting.Settings;
import org.jackhuang.hellominecraft.launcher.setting.VersionSetting; import org.jackhuang.hellominecraft.launcher.setting.VersionSetting;
/** /**
@@ -30,6 +33,11 @@ public class HMCLGameProvider extends MinecraftVersionManager {
public HMCLGameProvider(HMCLMinecraftService p) { public HMCLGameProvider(HMCLMinecraftService p) {
super(p); super(p);
HMCLAPI.EVENT_BUS.channel(MinecraftLibraryPathEvent.class).register(t -> {
if (!t.getFile().getValue().exists())
t.getFile().setValue(new File(Settings.getInstance().getCommonpath(), t.getLocation()));
});
} }
@Override @Override

View File

@@ -75,7 +75,7 @@ public class HMCLMinecraftService extends IMinecraftService {
HMCLAPI.EVENT_BUS.channel(LoadedOneVersionEvent.class).register(e -> loadVersionSetting(e.getValue())); HMCLAPI.EVENT_BUS.channel(LoadedOneVersionEvent.class).register(e -> loadVersionSetting(e.getValue()));
this.mms = new MinecraftModService(this); this.mms = new MinecraftModService(this);
this.mds = new MinecraftDownloadService(this); this.mds = new MinecraftDownloadService(this);
this.mas = new MinecraftAssetService(this); this.mas = new HMCLAssetService(this);
this.mis = new MinecraftInstallerService(this); this.mis = new MinecraftInstallerService(this);
} }

View File

@@ -0,0 +1,39 @@
/*
* 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.util;
/**
*
* @author huang
*/
public class Wrapper<T> {
T value;
public Wrapper(T value) {
this.value = value;
}
public T getValue() {
return value;
}
public void setValue(T value) {
this.value = value;
}
}

View File

@@ -308,11 +308,14 @@ mainwindow.no_version=No version found. Switch to Game Downloads Tab?
launcher.about=<html>About Author<br/>Minecraft Forum ID: klkl6523<br/>Copyright (c) 2013 huangyuhui<br/>Opened source under GPL v3 license:http://github.com/huanghongxun/HMCL/<br/>This software used project Gson which is under Apache License 2.0, thanks contributors.</html> launcher.about=<html>About Author<br/>Minecraft Forum ID: klkl6523<br/>Copyright (c) 2013 huangyuhui<br/>Opened source under GPL v3 license:http://github.com/huanghongxun/HMCL/<br/>This software used project Gson which is under Apache License 2.0, thanks contributors.</html>
launcher.download_source=Download Source launcher.download_source=Download Source
launcher.background_location=Background Location launcher.background_location=Background Location
launcher.common_location=Common Location
launcher.exit_failed=Failed to shutdown. launcher.exit_failed=Failed to shutdown.
launcher.versions_json_not_matched=The version %s is malformed! There are a json:%s in this version. Do you want to fix this problem? launcher.versions_json_not_matched=The version %s is malformed! There are a json:%s in this version. Do you want to fix this problem?
launcher.versions_json_not_matched_cannot_auto_completion=The version %s lost version information file, delete it? launcher.versions_json_not_matched_cannot_auto_completion=The version %s lost version information file, delete it?
launcher.versions_json_not_formatted=The version information of %s is malformed! Redownload it? launcher.versions_json_not_formatted=The version information of %s is malformed! Redownload it?
launcher.choose_bgpath=Choose background path. launcher.choose_bgpath=Choose background path.
launcher.choose_commonpath=Choose common path.
launcher.commpath_tooltip=<html><body>This app will save all game libraries and assets here unless there are existant files in game folder.</body></html>
launcher.background_tooltip=<html><body>The laucher uses a default background.<br/>If you use custom background.png, link it and it will be used.<br />If there is "bg" subdirectory, this app will chooses one picture in "bgskin" randomly.<br />If you set the background setting, this app will use it.</body></html> launcher.background_tooltip=<html><body>The laucher uses a default background.<br/>If you use custom background.png, link it and it will be used.<br />If there is "bg" subdirectory, this app will chooses one picture in "bgskin" randomly.<br />If you set the background setting, this app will use it.</body></html>
launcher.update_launcher=Check for update launcher.update_launcher=Check for update
launcher.enable_shadow=Enable Window Shadow launcher.enable_shadow=Enable Window Shadow

View File

@@ -308,11 +308,14 @@ mainwindow.no_version=No version found. Switch to Game Downloads Tab?
launcher.about=<html>About Author<br/>Minecraft Forum ID: klkl6523<br/>Copyright (c) 2013 huangyuhui<br/>Opened source under GPL v3 license:http://github.com/huanghongxun/HMCL/<br/>This software used project Gson which is under Apache License 2.0, thanks contributors.</html> launcher.about=<html>About Author<br/>Minecraft Forum ID: klkl6523<br/>Copyright (c) 2013 huangyuhui<br/>Opened source under GPL v3 license:http://github.com/huanghongxun/HMCL/<br/>This software used project Gson which is under Apache License 2.0, thanks contributors.</html>
launcher.download_source=Download Source launcher.download_source=Download Source
launcher.background_location=Background Location launcher.background_location=Background Location
launcher.common_location=Common Location
launcher.exit_failed=Failed to shutdown. launcher.exit_failed=Failed to shutdown.
launcher.versions_json_not_matched=The version %s is malformed! There are a json:%s in this version. Do you want to fix this problem? launcher.versions_json_not_matched=The version %s is malformed! There are a json:%s in this version. Do you want to fix this problem?
launcher.versions_json_not_matched_cannot_auto_completion=The version %s lost version information file, delete it? launcher.versions_json_not_matched_cannot_auto_completion=The version %s lost version information file, delete it?
launcher.versions_json_not_formatted=The version information of %s is malformed! Redownload it? launcher.versions_json_not_formatted=The version information of %s is malformed! Redownload it?
launcher.choose_bgpath=Choose background path. launcher.choose_bgpath=Choose background path.
launcher.choose_commonpath=Choose common path.
launcher.commpath_tooltip=<html><body>This app will save all game libraries and assets here unless there are existant files in game folder.</body></html>
launcher.background_tooltip=<html><body>The laucher uses a default background.<br/>If you use custom background.png, link it and it will be used.<br />If there is "bg" subdirectory, this app will chooses one picture in "bgskin" randomly.<br />If you set the background setting, this app will use it.</body></html> launcher.background_tooltip=<html><body>The laucher uses a default background.<br/>If you use custom background.png, link it and it will be used.<br />If there is "bg" subdirectory, this app will chooses one picture in "bgskin" randomly.<br />If you set the background setting, this app will use it.</body></html>
launcher.update_launcher=Check for update launcher.update_launcher=Check for update
launcher.enable_shadow=Enable Window Shadow launcher.enable_shadow=Enable Window Shadow

View File

@@ -306,11 +306,14 @@ mainwindow.no_version=Không có phiên bản minecraft nào được tìm thấ
launcher.about=<html>Về tác giả<br/>Minecraft Forum ID: klkl6523<br/>Copyright (c) 2013 huangyuhui<br/>http://github.com/huanghongxun/HMCL/<br/>Phần mềm này dùng project Gson, cảm ơn người đóng góp.</html> launcher.about=<html>Về tác giả<br/>Minecraft Forum ID: klkl6523<br/>Copyright (c) 2013 huangyuhui<br/>http://github.com/huanghongxun/HMCL/<br/>Phần mềm này dùng project Gson, cảm ơn người đóng góp.</html>
launcher.download_source=Download Source launcher.download_source=Download Source
launcher.background_location=Background Location launcher.background_location=Background Location
launcher.common_location=Common Location
launcher.exit_failed=Tắt launcher thất bại. launcher.exit_failed=Tắt launcher thất bại.
launcher.versions_json_not_matched=The version %s is malformed! There are a json:%s in this version. Do you want to fix this problem? launcher.versions_json_not_matched=The version %s is malformed! There are a json:%s in this version. Do you want to fix this problem?
launcher.versions_json_not_matched_cannot_auto_completion=The version %s lost version information file, delete it? launcher.versions_json_not_matched_cannot_auto_completion=The version %s lost version information file, delete it?
launcher.versions_json_not_formatted=The version information of %s is malformed! Redownload it? launcher.versions_json_not_formatted=The version information of %s is malformed! Redownload it?
launcher.choose_bgpath=Choose background path. launcher.choose_bgpath=Choose background path.
launcher.choose_commonpath=Choose common path.
launcher.commpath_tooltip=<html><body>This app will save all game libraries and assets here unless there are existant files in game folder.</body></html>
launcher.background_tooltip=<html><body>This app uses the default background at first.<br />If there is background.png in the directory, it will be used.<br />If there is "bg" subdirectory, this app will chooses one picture in "bgskin" randomly.<br />If you set the background setting, this app will use it.</body></html> launcher.background_tooltip=<html><body>This app uses the default background at first.<br />If there is background.png in the directory, it will be used.<br />If there is "bg" subdirectory, this app will chooses one picture in "bgskin" randomly.<br />If you set the background setting, this app will use it.</body></html>
launcher.update_launcher=Check for update launcher.update_launcher=Check for update
launcher.enable_shadow=Enable Window Shadow launcher.enable_shadow=Enable Window Shadow

View File

@@ -306,11 +306,14 @@ mainwindow.no_version=Kh\u00f4ng c\u00f3 phi\u00ean b\u1ea3n minecraft n\u00e0o
launcher.about=<html>V\u1ec1 t\u00e1c gi\u1ea3<br/>Minecraft Forum ID: klkl6523<br/>Copyright (c) 2013 huangyuhui<br/>http://github.com/huanghongxun/HMCL/<br/>Ph\u1ea7n m\u1ec1m n\u00e0y d\u00f9ng project Gson, c\u1ea3m \u01a1n ng\u01b0\u1eddi \u0111\u00f3ng g\u00f3p.</html> launcher.about=<html>V\u1ec1 t\u00e1c gi\u1ea3<br/>Minecraft Forum ID: klkl6523<br/>Copyright (c) 2013 huangyuhui<br/>http://github.com/huanghongxun/HMCL/<br/>Ph\u1ea7n m\u1ec1m n\u00e0y d\u00f9ng project Gson, c\u1ea3m \u01a1n ng\u01b0\u1eddi \u0111\u00f3ng g\u00f3p.</html>
launcher.download_source=Download Source launcher.download_source=Download Source
launcher.background_location=Background Location launcher.background_location=Background Location
launcher.common_location=Common Location
launcher.exit_failed=T\u1eaft launcher th\u1ea5t b\u1ea1i. launcher.exit_failed=T\u1eaft launcher th\u1ea5t b\u1ea1i.
launcher.versions_json_not_matched=The version %s is malformed! There are a json:%s in this version. Do you want to fix this problem? launcher.versions_json_not_matched=The version %s is malformed! There are a json:%s in this version. Do you want to fix this problem?
launcher.versions_json_not_matched_cannot_auto_completion=The version %s lost version information file, delete it? launcher.versions_json_not_matched_cannot_auto_completion=The version %s lost version information file, delete it?
launcher.versions_json_not_formatted=The version information of %s is malformed! Redownload it? launcher.versions_json_not_formatted=The version information of %s is malformed! Redownload it?
launcher.choose_bgpath=Choose background path. launcher.choose_bgpath=Choose background path.
launcher.choose_commonpath=Choose common path.
launcher.commpath_tooltip=<html><body>This app will save all game libraries and assets here unless there are existant files in game folder.</body></html>
launcher.background_tooltip=<html><body>This app uses the default background at first.<br />If there is background.png in the directory, it will be used.<br />If there is "bg" subdirectory, this app will chooses one picture in "bgskin" randomly.<br />If you set the background setting, this app will use it.</body></html> launcher.background_tooltip=<html><body>This app uses the default background at first.<br />If there is background.png in the directory, it will be used.<br />If there is "bg" subdirectory, this app will chooses one picture in "bgskin" randomly.<br />If you set the background setting, this app will use it.</body></html>
launcher.update_launcher=Check for update launcher.update_launcher=Check for update
launcher.enable_shadow=Enable Window Shadow launcher.enable_shadow=Enable Window Shadow

View File

@@ -308,11 +308,14 @@ mainwindow.no_version=未找到任何版本,是否进入游戏下载?
launcher.about=<html>默认背景图感谢gamerteam提供。<br/>关于作者:<br/>百度IDhuanghongxun20<br/>mcbbshuanghongxun<br/>Minecraft Forum ID: klkl6523<br/>欢迎提交Bug哦<br/>Copyright (c) 2013-2017 huangyuhui.<br/>免责声明Minecraft软件版权归Mojang AB所有使用本软件产生的版权问题本软件制作方概不负责。<br/>本启动器在GPLv3协议下开源:https://github.com/huanghongxun/HMCL/ ,感谢issues和pull requests贡献者<br/>本软件使用了基于Apache License 2.0的Gson项目感谢贡献者。</html> launcher.about=<html>默认背景图感谢gamerteam提供。<br/>关于作者:<br/>百度IDhuanghongxun20<br/>mcbbshuanghongxun<br/>Minecraft Forum ID: klkl6523<br/>欢迎提交Bug哦<br/>Copyright (c) 2013-2017 huangyuhui.<br/>免责声明Minecraft软件版权归Mojang AB所有使用本软件产生的版权问题本软件制作方概不负责。<br/>本启动器在GPLv3协议下开源:https://github.com/huanghongxun/HMCL/ ,感谢issues和pull requests贡献者<br/>本软件使用了基于Apache License 2.0的Gson项目感谢贡献者。</html>
launcher.download_source=下载源 launcher.download_source=下载源
launcher.background_location=背景地址 launcher.background_location=背景地址
launcher.common_location=公共文件夹
launcher.exit_failed=强制退出失败可能是Forge 1.7.10及更高版本导致的,无法解决。 launcher.exit_failed=强制退出失败可能是Forge 1.7.10及更高版本导致的,无法解决。
launcher.versions_json_not_matched=版本%s格式不规范该版本文件夹下有json:%s是否更名这个文件来规范格式 launcher.versions_json_not_matched=版本%s格式不规范该版本文件夹下有json:%s是否更名这个文件来规范格式
launcher.versions_json_not_matched_cannot_auto_completion=版本%s缺失必要的版本信息文件是否删除该版本 launcher.versions_json_not_matched_cannot_auto_completion=版本%s缺失必要的版本信息文件是否删除该版本
launcher.versions_json_not_formatted=版本%s信息文件格式错误是否重新下载 launcher.versions_json_not_formatted=版本%s信息文件格式错误是否重新下载
launcher.choose_bgpath=选择背景路径 launcher.choose_bgpath=选择背景路径
launcher.choose_commonpath=选择公共路径
launcher.commpath_tooltip=<html><body>启动器将所有游戏资源及依赖库文件放于此集中管理,如果游戏文件夹内有现成的将不会使用公共库文件</body></html>
launcher.background_tooltip=<html><body>启动器默认使用自带的背景<br />如果当前目录有background.png则会使用该文件作为背景<br />如果当前目录有bg子目录则会随机使用里面的一张图作为背景<br />如果该背景地址被修改,则会使用背景地址里的一张图作为背景<br />背景地址允许有多个地址,使用半角分号";"(不包含双引号)分隔</body></html> launcher.background_tooltip=<html><body>启动器默认使用自带的背景<br />如果当前目录有background.png则会使用该文件作为背景<br />如果当前目录有bg子目录则会随机使用里面的一张图作为背景<br />如果该背景地址被修改,则会使用背景地址里的一张图作为背景<br />背景地址允许有多个地址,使用半角分号";"(不包含双引号)分隔</body></html>
launcher.update_launcher=检查更新 launcher.update_launcher=检查更新
launcher.enable_shadow=启用窗口阴影 launcher.enable_shadow=启用窗口阴影

View File

@@ -308,11 +308,14 @@ mainwindow.no_version=\u672a\u627e\u5230\u4efb\u4f55\u7248\u672c\uff0c\u662f\u54
launcher.about=<html>\u9ed8\u8ba4\u80cc\u666f\u56fe\u611f\u8c22gamerteam\u63d0\u4f9b\u3002<br/>\u5173\u4e8e\u4f5c\u8005\uff1a<br/>\u767e\u5ea6ID\uff1ahuanghongxun20<br/>mcbbs\uff1ahuanghongxun<br/>Minecraft Forum ID: klkl6523<br/>\u6b22\u8fce\u63d0\u4ea4Bug\u54e6<br/>Copyright (c) 2013-2017 huangyuhui.<br/>\u514d\u8d23\u58f0\u660e\uff1aMinecraft\u8f6f\u4ef6\u7248\u6743\u5f52Mojang AB\u6240\u6709\uff0c\u4f7f\u7528\u672c\u8f6f\u4ef6\u4ea7\u751f\u7684\u7248\u6743\u95ee\u9898\u672c\u8f6f\u4ef6\u5236\u4f5c\u65b9\u6982\u4e0d\u8d1f\u8d23\u3002<br/>\u672c\u542f\u52a8\u5668\u5728GPLv3\u534f\u8bae\u4e0b\u5f00\u6e90:https://github.com/huanghongxun/HMCL/ ,\u611f\u8c22issues\u548cpull requests\u8d21\u732e\u8005<br/>\u672c\u8f6f\u4ef6\u4f7f\u7528\u4e86\u57fa\u4e8eApache License 2.0\u7684Gson\u9879\u76ee\uff0c\u611f\u8c22\u8d21\u732e\u8005\u3002</html> launcher.about=<html>\u9ed8\u8ba4\u80cc\u666f\u56fe\u611f\u8c22gamerteam\u63d0\u4f9b\u3002<br/>\u5173\u4e8e\u4f5c\u8005\uff1a<br/>\u767e\u5ea6ID\uff1ahuanghongxun20<br/>mcbbs\uff1ahuanghongxun<br/>Minecraft Forum ID: klkl6523<br/>\u6b22\u8fce\u63d0\u4ea4Bug\u54e6<br/>Copyright (c) 2013-2017 huangyuhui.<br/>\u514d\u8d23\u58f0\u660e\uff1aMinecraft\u8f6f\u4ef6\u7248\u6743\u5f52Mojang AB\u6240\u6709\uff0c\u4f7f\u7528\u672c\u8f6f\u4ef6\u4ea7\u751f\u7684\u7248\u6743\u95ee\u9898\u672c\u8f6f\u4ef6\u5236\u4f5c\u65b9\u6982\u4e0d\u8d1f\u8d23\u3002<br/>\u672c\u542f\u52a8\u5668\u5728GPLv3\u534f\u8bae\u4e0b\u5f00\u6e90:https://github.com/huanghongxun/HMCL/ ,\u611f\u8c22issues\u548cpull requests\u8d21\u732e\u8005<br/>\u672c\u8f6f\u4ef6\u4f7f\u7528\u4e86\u57fa\u4e8eApache License 2.0\u7684Gson\u9879\u76ee\uff0c\u611f\u8c22\u8d21\u732e\u8005\u3002</html>
launcher.download_source=\u4e0b\u8f7d\u6e90 launcher.download_source=\u4e0b\u8f7d\u6e90
launcher.background_location=\u80cc\u666f\u5730\u5740 launcher.background_location=\u80cc\u666f\u5730\u5740
launcher.common_location=\u516c\u5171\u6587\u4ef6\u5939
launcher.exit_failed=\u5f3a\u5236\u9000\u51fa\u5931\u8d25\uff0c\u53ef\u80fd\u662fForge 1.7.10\u53ca\u66f4\u9ad8\u7248\u672c\u5bfc\u81f4\u7684\uff0c\u65e0\u6cd5\u89e3\u51b3\u3002 launcher.exit_failed=\u5f3a\u5236\u9000\u51fa\u5931\u8d25\uff0c\u53ef\u80fd\u662fForge 1.7.10\u53ca\u66f4\u9ad8\u7248\u672c\u5bfc\u81f4\u7684\uff0c\u65e0\u6cd5\u89e3\u51b3\u3002
launcher.versions_json_not_matched=\u7248\u672c%s\u683c\u5f0f\u4e0d\u89c4\u8303\uff01\u8be5\u7248\u672c\u6587\u4ef6\u5939\u4e0b\u6709json:%s\uff0c\u662f\u5426\u66f4\u540d\u8fd9\u4e2a\u6587\u4ef6\u6765\u89c4\u8303\u683c\u5f0f\uff1f launcher.versions_json_not_matched=\u7248\u672c%s\u683c\u5f0f\u4e0d\u89c4\u8303\uff01\u8be5\u7248\u672c\u6587\u4ef6\u5939\u4e0b\u6709json:%s\uff0c\u662f\u5426\u66f4\u540d\u8fd9\u4e2a\u6587\u4ef6\u6765\u89c4\u8303\u683c\u5f0f\uff1f
launcher.versions_json_not_matched_cannot_auto_completion=\u7248\u672c%s\u7f3a\u5931\u5fc5\u8981\u7684\u7248\u672c\u4fe1\u606f\u6587\u4ef6\uff0c\u662f\u5426\u5220\u9664\u8be5\u7248\u672c\uff1f launcher.versions_json_not_matched_cannot_auto_completion=\u7248\u672c%s\u7f3a\u5931\u5fc5\u8981\u7684\u7248\u672c\u4fe1\u606f\u6587\u4ef6\uff0c\u662f\u5426\u5220\u9664\u8be5\u7248\u672c\uff1f
launcher.versions_json_not_formatted=\u7248\u672c%s\u4fe1\u606f\u6587\u4ef6\u683c\u5f0f\u9519\u8bef\uff0c\u662f\u5426\u91cd\u65b0\u4e0b\u8f7d\uff1f launcher.versions_json_not_formatted=\u7248\u672c%s\u4fe1\u606f\u6587\u4ef6\u683c\u5f0f\u9519\u8bef\uff0c\u662f\u5426\u91cd\u65b0\u4e0b\u8f7d\uff1f
launcher.choose_bgpath=\u9009\u62e9\u80cc\u666f\u8def\u5f84 launcher.choose_bgpath=\u9009\u62e9\u80cc\u666f\u8def\u5f84
launcher.choose_commonpath=\u9009\u62e9\u516c\u5171\u8def\u5f84
launcher.commpath_tooltip=<html><body>\u542f\u52a8\u5668\u5c06\u6240\u6709\u6e38\u620f\u8d44\u6e90\u53ca\u4f9d\u8d56\u5e93\u6587\u4ef6\u653e\u4e8e\u6b64\u96c6\u4e2d\u7ba1\u7406\uff0c\u5982\u679c\u6e38\u620f\u6587\u4ef6\u5939\u5185\u6709\u73b0\u6210\u7684\u5c06\u4e0d\u4f1a\u4f7f\u7528\u516c\u5171\u5e93\u6587\u4ef6</body></html>
launcher.background_tooltip=<html><body>\u542f\u52a8\u5668\u9ed8\u8ba4\u4f7f\u7528\u81ea\u5e26\u7684\u80cc\u666f<br />\u5982\u679c\u5f53\u524d\u76ee\u5f55\u6709background.png\uff0c\u5219\u4f1a\u4f7f\u7528\u8be5\u6587\u4ef6\u4f5c\u4e3a\u80cc\u666f<br />\u5982\u679c\u5f53\u524d\u76ee\u5f55\u6709bg\u5b50\u76ee\u5f55\uff0c\u5219\u4f1a\u968f\u673a\u4f7f\u7528\u91cc\u9762\u7684\u4e00\u5f20\u56fe\u4f5c\u4e3a\u80cc\u666f<br />\u5982\u679c\u8be5\u80cc\u666f\u5730\u5740\u88ab\u4fee\u6539\uff0c\u5219\u4f1a\u4f7f\u7528\u80cc\u666f\u5730\u5740\u91cc\u7684\u4e00\u5f20\u56fe\u4f5c\u4e3a\u80cc\u666f<br />\u80cc\u666f\u5730\u5740\u5141\u8bb8\u6709\u591a\u4e2a\u5730\u5740\uff0c\u4f7f\u7528\u534a\u89d2\u5206\u53f7";"(\u4e0d\u5305\u542b\u53cc\u5f15\u53f7)\u5206\u9694</body></html> launcher.background_tooltip=<html><body>\u542f\u52a8\u5668\u9ed8\u8ba4\u4f7f\u7528\u81ea\u5e26\u7684\u80cc\u666f<br />\u5982\u679c\u5f53\u524d\u76ee\u5f55\u6709background.png\uff0c\u5219\u4f1a\u4f7f\u7528\u8be5\u6587\u4ef6\u4f5c\u4e3a\u80cc\u666f<br />\u5982\u679c\u5f53\u524d\u76ee\u5f55\u6709bg\u5b50\u76ee\u5f55\uff0c\u5219\u4f1a\u968f\u673a\u4f7f\u7528\u91cc\u9762\u7684\u4e00\u5f20\u56fe\u4f5c\u4e3a\u80cc\u666f<br />\u5982\u679c\u8be5\u80cc\u666f\u5730\u5740\u88ab\u4fee\u6539\uff0c\u5219\u4f1a\u4f7f\u7528\u80cc\u666f\u5730\u5740\u91cc\u7684\u4e00\u5f20\u56fe\u4f5c\u4e3a\u80cc\u666f<br />\u80cc\u666f\u5730\u5740\u5141\u8bb8\u6709\u591a\u4e2a\u5730\u5740\uff0c\u4f7f\u7528\u534a\u89d2\u5206\u53f7";"(\u4e0d\u5305\u542b\u53cc\u5f15\u53f7)\u5206\u9694</body></html>
launcher.update_launcher=\u68c0\u67e5\u66f4\u65b0 launcher.update_launcher=\u68c0\u67e5\u66f4\u65b0
launcher.enable_shadow=\u542f\u7528\u7a97\u53e3\u9634\u5f71 launcher.enable_shadow=\u542f\u7528\u7a97\u53e3\u9634\u5f71