Compare commits
37
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
881b0e9127 | ||
|
|
77e06c8820 | ||
|
|
bc2405cf12 | ||
|
|
72b5428f8b | ||
|
|
e7e8d45497 | ||
|
|
82dbe18d78 | ||
|
|
ecb7ef0867 | ||
|
|
d40742a624 | ||
|
|
6f8002a0cf | ||
|
|
3d3896c5c4 | ||
|
|
a24f8e2ec0 | ||
|
|
fce08a6923 | ||
|
|
5fa01c8838 | ||
|
|
15a3fb5f59 | ||
|
|
d5ab14f476 | ||
|
|
554ecfd6d5 | ||
|
|
c2abe755f4 | ||
|
|
c0410c46ff | ||
|
|
da93b4c12d | ||
|
|
c16d6e8d5f | ||
|
|
6df73aeb68 | ||
|
|
23d11eca81 | ||
|
|
0e307f3b71 | ||
|
|
33b791f2b0 | ||
|
|
de01946ab2 | ||
|
|
5526bac70e | ||
|
|
741f65d37c | ||
|
|
ecafc9d6f0 | ||
|
|
8795d522b0 | ||
|
|
d8e7e0e97e | ||
|
|
8236dfc7e5 | ||
|
|
8a15b7d274 | ||
|
|
fce4c13515 | ||
|
|
2f36506099 | ||
|
|
5e60eaf595 | ||
|
|
f70fb4ae08 | ||
|
|
38adb48051 |
No files matched your search
@@ -20,6 +20,7 @@ minecraft-exported-crash-info*
|
||||
/HMCLCore/build/
|
||||
/HMCLTransformerDiscoveryService/build/
|
||||
/minecraft/libraries/HMCLTransformerDiscoveryService/build/
|
||||
/buildSrc/build/
|
||||
|
||||
# idea
|
||||
.idea
|
||||
|
||||
+3
-1
@@ -1,2 +1,4 @@
|
||||
/data.csv
|
||||
/data.json
|
||||
/data.json
|
||||
/mod.json
|
||||
/modpack.json
|
||||
@@ -1,3 +1,4 @@
|
||||
import org.jackhuang.hmcl.gradle.mod.ParseModDataTask
|
||||
import java.net.URI
|
||||
import java.nio.file.FileSystems
|
||||
import java.nio.file.Files
|
||||
@@ -253,3 +254,15 @@ tasks.create<JavaExec>("run") {
|
||||
logger.quiet("HMCL_JAVA_OPTS: $vmOptions")
|
||||
}
|
||||
}
|
||||
|
||||
// mcmod data
|
||||
|
||||
tasks.register<ParseModDataTask>("parseModData") {
|
||||
inputFile.set(layout.projectDirectory.file("mod.json"))
|
||||
outputFile.set(layout.projectDirectory.file("src/main/resources/assets/mod_data.txt"))
|
||||
}
|
||||
|
||||
tasks.register<ParseModDataTask>("parseModPackData") {
|
||||
inputFile.set(layout.projectDirectory.file("modpack.json"))
|
||||
outputFile.set(layout.projectDirectory.file("src/main/resources/assets/modpack_data.txt"))
|
||||
}
|
||||
@@ -1,123 +0,0 @@
|
||||
#
|
||||
# Hello Minecraft! Launcher
|
||||
# Copyright (C) 2021 huangyuhui <huanghongxun2008@126.com> and contributors
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
import json
|
||||
import codecs
|
||||
import re
|
||||
import sys
|
||||
from urllib.parse import urlparse, parse_qs
|
||||
|
||||
S = ';'
|
||||
|
||||
MOD_SEPARATOR = ','
|
||||
|
||||
CURSEFORGE_PATTERN1 = re.compile(
|
||||
r'^/(minecraft|Minecraft|minecraft-bedrock)/(mc-mods|modpacks|customization|mc-addons|texture-packs|customization/configuration|addons)/+(?P<modid>[\w-]+)(/(.*?))?$')
|
||||
CURSEFORGE_PATTERN2 = re.compile(
|
||||
r'^/projects/(?P<modid>[\w-]+)(/(.*?))?$')
|
||||
CURSEFORGE_PATTERN3 = re.compile(
|
||||
r'^/mc-mods/minecraft/(?P<modid>[\w-]+)(/(.*?))?$')
|
||||
CURSEFORGE_PATTERN4 = re.compile(
|
||||
r'^/legacy/mc-mods/minecraft/(\d+)-(?P<modid>[\w-]+)'
|
||||
)
|
||||
|
||||
|
||||
def parseCurseforge(url):
|
||||
res = urlparse(url)
|
||||
if res.scheme not in ['http', 'https']:
|
||||
return ''
|
||||
for pattern in [CURSEFORGE_PATTERN1, CURSEFORGE_PATTERN2, CURSEFORGE_PATTERN3, CURSEFORGE_PATTERN4]:
|
||||
match = pattern.match(res.path)
|
||||
if match:
|
||||
return match.group('modid')
|
||||
return ''
|
||||
|
||||
|
||||
MCMOD_PATTERN = re.compile(
|
||||
r'^http(s)?://www\.mcmod\.cn/(class|modpack)/(?P<modid>\d+)\.html$')
|
||||
|
||||
|
||||
def parseMcmod(url):
|
||||
match = MCMOD_PATTERN.match(url)
|
||||
if match:
|
||||
return match.group('modid')
|
||||
return ''
|
||||
|
||||
skip = [
|
||||
'Minecraft',
|
||||
'The Building Game'
|
||||
]
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
json_name = sys.argv[1] or 'data.json'
|
||||
|
||||
with codecs.open(json_name, mode='r', encoding='utf-8-sig') as jsonfile, codecs.open('data.csv', mode='w', encoding='utf-8') as outfile:
|
||||
data = json.load(jsonfile)
|
||||
|
||||
for mod in data:
|
||||
chinese_name = mod['name']['main']
|
||||
sub_name = mod['name']['sub']
|
||||
abbr = mod['name']['abbr']
|
||||
|
||||
if sub_name in skip:
|
||||
continue
|
||||
|
||||
if S in chinese_name:
|
||||
print('Error! ' + chinese_name)
|
||||
exit(1)
|
||||
|
||||
if S in sub_name:
|
||||
print('Error! ' + chinese_name)
|
||||
exit(1)
|
||||
|
||||
black_lists = [
|
||||
'Wood Converter'
|
||||
]
|
||||
|
||||
curseforge_id = ''
|
||||
mcmod_id = ''
|
||||
links = mod['links']['list']
|
||||
if 'curseforge' in links and links['curseforge'] and sub_name not in black_lists and chinese_name not in black_lists:
|
||||
for link in links['curseforge']:
|
||||
curseforge_id = parseCurseforge(link['url'])
|
||||
if curseforge_id != '':
|
||||
break
|
||||
if curseforge_id == '':
|
||||
print('Error curseforge ' + chinese_name)
|
||||
exit(1)
|
||||
if 'mcmod' in links and links['mcmod']:
|
||||
mcmod_id = parseMcmod(links['mcmod'][0]['url'])
|
||||
if mcmod_id == '':
|
||||
print('Error mcmod ' + chinese_name)
|
||||
exit(1)
|
||||
|
||||
mod_id = []
|
||||
if 'modid' in mod and 'list' in mod['modid']:
|
||||
for id in mod['modid']['list']:
|
||||
if MOD_SEPARATOR in id:
|
||||
print('Error mod id!' + id)
|
||||
exit(1)
|
||||
|
||||
mod_id.append(id)
|
||||
mod_ids = MOD_SEPARATOR.join([str(id) for id in mod_id])
|
||||
|
||||
outfile.write(
|
||||
f'{curseforge_id}{S}{mcmod_id}{S}{mod_ids}{S}{chinese_name}{S}{sub_name}{S}{abbr}\n')
|
||||
|
||||
print('Success!')
|
||||
@@ -225,8 +225,11 @@ public final class Launcher extends Application {
|
||||
LOG.info("Operating System: " + (OperatingSystem.OS_RELEASE_PRETTY_NAME == null
|
||||
? OperatingSystem.SYSTEM_NAME + ' ' + OperatingSystem.SYSTEM_VERSION
|
||||
: OperatingSystem.OS_RELEASE_PRETTY_NAME + " (" + OperatingSystem.SYSTEM_NAME + ' ' + OperatingSystem.SYSTEM_VERSION + ')'));
|
||||
LOG.info("System Architecture: " + Architecture.SYSTEM_ARCH_NAME);
|
||||
LOG.info("Java Architecture: " + Architecture.CURRENT_ARCH_NAME);
|
||||
if (OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS) {
|
||||
LOG.info("Processor Identifier: " + System.getenv("PROCESSOR_IDENTIFIER"));
|
||||
}
|
||||
LOG.info("System Architecture: " + Architecture.SYSTEM_ARCH.getDisplayName());
|
||||
LOG.info("Java Architecture: " + Architecture.CURRENT_ARCH.getDisplayName());
|
||||
LOG.info("Java Version: " + System.getProperty("java.version") + ", " + System.getProperty("java.vendor"));
|
||||
LOG.info("Java VM Version: " + System.getProperty("java.vm.name") + " (" + System.getProperty("java.vm.info") + "), " + System.getProperty("java.vm.vendor"));
|
||||
LOG.info("Java Home: " + System.getProperty("java.home"));
|
||||
@@ -245,7 +248,6 @@ public final class Launcher extends Application {
|
||||
LOG.info("XDG Session Type: " + System.getenv("XDG_SESSION_TYPE"));
|
||||
LOG.info("XDG Current Desktop: " + System.getenv("XDG_CURRENT_DESKTOP"));
|
||||
}
|
||||
|
||||
launch(Launcher.class, args);
|
||||
} catch (Throwable e) { // Fucking JavaFX will suppress the exception and will break our crash reporter.
|
||||
CRASH_REPORTER.uncaughtException(Thread.currentThread(), e);
|
||||
|
||||
@@ -43,8 +43,8 @@ public class CrashReport {
|
||||
stackTrace + "\n\n" +
|
||||
"-- System Details --\n" +
|
||||
" Operating System: " + OperatingSystem.SYSTEM_NAME + ' ' + OperatingSystem.SYSTEM_VERSION + "\n" +
|
||||
" System Architecture: " + Architecture.SYSTEM_ARCH_NAME + "\n" +
|
||||
" Java Architecture: " + Architecture.CURRENT_ARCH_NAME + "\n" +
|
||||
" System Architecture: " + Architecture.SYSTEM_ARCH.getDisplayName() + "\n" +
|
||||
" Java Architecture: " + Architecture.CURRENT_ARCH.getDisplayName() + "\n" +
|
||||
" Java Version: " + System.getProperty("java.version") + ", " + System.getProperty("java.vendor") + "\n" +
|
||||
" Java VM Version: " + System.getProperty("java.vm.name") + " (" + System.getProperty("java.vm.info") + "), " + System.getProperty("java.vm.vendor") + "\n" +
|
||||
" JVM Max Memory: " + Runtime.getRuntime().maxMemory() + "\n" +
|
||||
|
||||
@@ -38,7 +38,6 @@ import org.jackhuang.hmcl.util.Lang;
|
||||
import org.jackhuang.hmcl.util.StringUtils;
|
||||
import org.jackhuang.hmcl.util.gson.JsonUtils;
|
||||
import org.jackhuang.hmcl.util.io.FileUtils;
|
||||
import org.jackhuang.hmcl.util.javafx.PropertyUtils;
|
||||
import org.jackhuang.hmcl.java.JavaRuntime;
|
||||
import org.jackhuang.hmcl.util.platform.OperatingSystem;
|
||||
import org.jackhuang.hmcl.util.versioning.VersionNumber;
|
||||
@@ -359,12 +358,9 @@ public class HMCLGameRepository extends DefaultGameRepository {
|
||||
vs = createLocalVersionSetting(id);
|
||||
if (vs == null)
|
||||
return null;
|
||||
VersionIconType versionIcon = vs.getVersionIcon();
|
||||
if (vs.isUsesGlobal()) {
|
||||
PropertyUtils.copyProperties(profile.getGlobal(), vs);
|
||||
vs.setUsesGlobal(false);
|
||||
}
|
||||
vs.setVersionIcon(versionIcon); // versionIcon is preserved
|
||||
return vs;
|
||||
}
|
||||
|
||||
@@ -374,7 +370,7 @@ public class HMCLGameRepository extends DefaultGameRepository {
|
||||
vs.setUsesGlobal(true);
|
||||
}
|
||||
|
||||
public LaunchOptions getLaunchOptions(String version, JavaRuntime javaVersion, File gameDir, List<String> javaAgents, boolean makeLaunchScript) {
|
||||
public LaunchOptions getLaunchOptions(String version, JavaRuntime javaVersion, File gameDir, List<String> javaAgents, List<String> javaArguments, boolean makeLaunchScript) {
|
||||
VersionSetting vs = getVersionSetting(version);
|
||||
|
||||
LaunchOptions.Builder builder = new LaunchOptions.Builder()
|
||||
@@ -417,7 +413,8 @@ public class HMCLGameRepository extends DefaultGameRepository {
|
||||
.setUseNativeGLFW(vs.isUseNativeGLFW())
|
||||
.setUseNativeOpenAL(vs.isUseNativeOpenAL())
|
||||
.setDaemon(!makeLaunchScript && vs.getLauncherVisibility().isDaemon())
|
||||
.setJavaAgents(javaAgents);
|
||||
.setJavaAgents(javaAgents)
|
||||
.setJavaArguments(javaArguments);
|
||||
if (config().hasProxy()) {
|
||||
builder.setProxy(ProxyManager.getProxy());
|
||||
if (config().hasProxyAuth()) {
|
||||
|
||||
@@ -131,13 +131,14 @@ public final class LauncherHelper {
|
||||
boolean integrityCheck = repository.unmarkVersionLaunchedAbnormally(selectedVersion);
|
||||
CountDownLatch launchingLatch = new CountDownLatch(1);
|
||||
List<String> javaAgents = new ArrayList<>(0);
|
||||
List<String> javaArguments = new ArrayList<>(0);
|
||||
|
||||
AtomicReference<JavaRuntime> javaVersionRef = new AtomicReference<>();
|
||||
|
||||
TaskExecutor executor = checkGameState(profile, setting, version.get())
|
||||
.thenComposeAsync(java -> {
|
||||
javaVersionRef.set(Objects.requireNonNull(java));
|
||||
version.set(NativePatcher.patchNative(version.get(), gameVersion.orElse(null), java, setting));
|
||||
version.set(NativePatcher.patchNative(repository, version.get(), gameVersion.orElse(null), java, setting, javaArguments));
|
||||
if (setting.isNotCheckGame())
|
||||
return null;
|
||||
return Task.allOf(
|
||||
@@ -155,7 +156,7 @@ public final class LauncherHelper {
|
||||
Task.composeAsync(() -> {
|
||||
Renderer renderer = setting.getRenderer();
|
||||
if (renderer != Renderer.DEFAULT && OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS) {
|
||||
Library lib = NativePatcher.getMesaLoader(java, renderer);
|
||||
Library lib = NativePatcher.getMesaLoader(java);
|
||||
if (lib == null)
|
||||
return null;
|
||||
File file = dependencyManager.getGameRepository().getLibraryFile(version.get(), lib);
|
||||
@@ -182,7 +183,8 @@ public final class LauncherHelper {
|
||||
.thenComposeAsync(() -> gameVersion.map(s -> new GameVerificationFixTask(dependencyManager, s, version.get())).orElse(null))
|
||||
.thenComposeAsync(() -> logIn(account).withStage("launch.state.logging_in"))
|
||||
.thenComposeAsync(authInfo -> Task.supplyAsync(() -> {
|
||||
LaunchOptions launchOptions = repository.getLaunchOptions(selectedVersion, javaVersionRef.get(), profile.getGameDir(), javaAgents, scriptFile != null);
|
||||
LaunchOptions launchOptions = repository.getLaunchOptions(
|
||||
selectedVersion, javaVersionRef.get(), profile.getGameDir(), javaAgents, javaArguments, scriptFile != null);
|
||||
|
||||
LOG.info("Here's the structure of game mod directory:\n" + FileUtils.printFileStructure(repository.getModManager(selectedVersion).getModsDirectory(), 10));
|
||||
|
||||
|
||||
@@ -376,8 +376,8 @@ public final class JavaManager {
|
||||
searchAllJavaInDirectory(javaRuntimes, Paths.get(System.getProperty("user.home"), "/.sdkman/candidates/java")); // SDKMAN!
|
||||
break;
|
||||
case OSX:
|
||||
tryAddJavaHome(javaRuntimes, Paths.get("/Library/Java/JavaVirtualMachines/Contents/Home"));
|
||||
tryAddJavaHome(javaRuntimes, Paths.get(System.getProperty("user.home"), "/Library/Java/JavaVirtualMachines/Contents/Home"));
|
||||
searchJavaInMacJavaVirtualMachines(javaRuntimes, Paths.get("/Library/Java/JavaVirtualMachines"));
|
||||
searchJavaInMacJavaVirtualMachines(javaRuntimes, Paths.get(System.getProperty("user.home"), "/Library/Java/JavaVirtualMachines"));
|
||||
tryAddJavaExecutable(javaRuntimes, Paths.get("/Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home/bin/java"));
|
||||
tryAddJavaExecutable(javaRuntimes, Paths.get("/Applications/Xcode.app/Contents/Applications/Application Loader.app/Contents/MacOS/itms/java/bin/java"));
|
||||
// Homebrew
|
||||
@@ -631,6 +631,20 @@ public final class JavaManager {
|
||||
}
|
||||
}
|
||||
|
||||
private static void searchJavaInMacJavaVirtualMachines(Map<Path, JavaRuntime> javaRuntimes, Path directory) {
|
||||
if (!Files.isDirectory(directory)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try (DirectoryStream<Path> stream = Files.newDirectoryStream(directory)) {
|
||||
for (Path subDir : stream) {
|
||||
tryAddJavaHome(javaRuntimes, subDir.resolve("Contents/Home"));
|
||||
}
|
||||
} catch (IOException e) {
|
||||
LOG.warning("Failed to find Java in " + directory, e);
|
||||
}
|
||||
}
|
||||
|
||||
// ==== Windows Registry Support ====
|
||||
private static void queryJavaInRegistryKey(Map<Path, JavaRuntime> javaRuntimes, String location) {
|
||||
for (String java : querySubFolders(location)) {
|
||||
|
||||
@@ -25,6 +25,7 @@ import javafx.beans.binding.ObjectBinding;
|
||||
import javafx.scene.paint.Color;
|
||||
import javafx.scene.text.Font;
|
||||
import org.jackhuang.hmcl.ui.FXUtils;
|
||||
import org.jackhuang.hmcl.util.Pair;
|
||||
import org.jackhuang.hmcl.util.io.FileUtils;
|
||||
|
||||
import java.io.File;
|
||||
@@ -123,6 +124,45 @@ public class Theme {
|
||||
opacity);
|
||||
}
|
||||
|
||||
private static Pair<String, String> parseFontStyle(String style) {
|
||||
if (style == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
style = style.toLowerCase(Locale.ROOT);
|
||||
|
||||
String weight;
|
||||
String posture;
|
||||
|
||||
if (style.contains("thin")) {
|
||||
weight = "100";
|
||||
} else if (style.contains("extralight") || style.contains("extra light") || style.contains("ultralight") | style.contains("ultra light")) {
|
||||
weight = "200";
|
||||
} else if (style.contains("medium")) {
|
||||
weight = "500";
|
||||
} else if (style.contains("semibold") || style.contains("semi bold") || style.contains("demibold") || style.contains("demi bold")) {
|
||||
weight = "600";
|
||||
} else if (style.contains("extrabold") || style.contains("extra bold") || style.contains("ultrabold") || style.contains("ultra bold")) {
|
||||
weight = "800";
|
||||
} else if (style.contains("black") || style.contains("heavy")) {
|
||||
weight = "900";
|
||||
} else if (style.contains("light")) {
|
||||
weight = "lighter";
|
||||
} else if (style.contains("bold")) {
|
||||
weight = "bold";
|
||||
} else {
|
||||
weight = null;
|
||||
}
|
||||
|
||||
if (style.contains("italic") || style.contains("oblique")) {
|
||||
posture = "italic";
|
||||
} else {
|
||||
posture = null;
|
||||
}
|
||||
|
||||
return Pair.pair(weight, posture);
|
||||
}
|
||||
|
||||
public String[] getStylesheets(String overrideFontFamily) {
|
||||
String css = "/assets/css/blue.css";
|
||||
|
||||
@@ -130,12 +170,12 @@ public class Theme {
|
||||
? System.getProperty("hmcl.font.override", System.getenv("HMCL_FONT"))
|
||||
: overrideFontFamily;
|
||||
|
||||
String fontStyle = null;
|
||||
Pair<String, String> fontStyle = null;
|
||||
if (fontFamily == null) {
|
||||
Optional<Font> font = tryLoadFont();
|
||||
if (font.isPresent()) {
|
||||
fontFamily = font.get().getFamily();
|
||||
fontStyle = font.get().getStyle();
|
||||
fontStyle = parseFontStyle(font.get().getStyle());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,8 +199,13 @@ public class Theme {
|
||||
else
|
||||
themeBuilder.append("-fx-font-family:\"").append(fontFamily).append("\";");
|
||||
|
||||
if (fontStyle != null && !fontStyle.isEmpty())
|
||||
themeBuilder.append("-fx-font-style:\"").append(fontStyle).append("\";");
|
||||
if (fontStyle != null) {
|
||||
if (fontStyle.getKey() != null)
|
||||
themeBuilder.append("-fx-font-weight:").append(fontStyle.getKey()).append(";");
|
||||
|
||||
if (fontStyle.getValue() != null)
|
||||
themeBuilder.append("-fx-font-style:").append(fontStyle.getValue()).append(";");
|
||||
}
|
||||
|
||||
themeBuilder.append('}');
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ import javafx.application.Platform;
|
||||
import javafx.beans.InvalidationListener;
|
||||
import javafx.beans.Observable;
|
||||
import javafx.beans.WeakInvalidationListener;
|
||||
import javafx.beans.WeakListener;
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.Property;
|
||||
import javafx.beans.value.*;
|
||||
@@ -79,6 +80,7 @@ import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import java.io.*;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.net.*;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
@@ -582,7 +584,7 @@ public final class FXUtils {
|
||||
focusedListener = (observable, oldFocused, newFocused) -> {
|
||||
if (oldFocused && !newFocused) {
|
||||
if (textField.validate()) {
|
||||
uppdateProperty();
|
||||
updateProperty();
|
||||
} else {
|
||||
// Rollback to old value
|
||||
updateTextField();
|
||||
@@ -594,7 +596,7 @@ public final class FXUtils {
|
||||
if (oldScene != null && newScene == null) {
|
||||
// Component is being removed from scene
|
||||
if (textField.validate()) {
|
||||
uppdateProperty();
|
||||
updateProperty();
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -604,7 +606,7 @@ public final class FXUtils {
|
||||
};
|
||||
}
|
||||
|
||||
public void uppdateProperty() {
|
||||
public void updateProperty() {
|
||||
String newText = textField.getText();
|
||||
@SuppressWarnings("unchecked")
|
||||
T newValue = converter == null ? (T) newText : converter.fromString(newText);
|
||||
@@ -636,26 +638,100 @@ public final class FXUtils {
|
||||
checkBox.selectedProperty().unbindBidirectional(property);
|
||||
}
|
||||
|
||||
private static final class EnumBidirectionalBinding<E extends Enum<E>> implements InvalidationListener, WeakListener {
|
||||
private final WeakReference<JFXComboBox<E>> comboBoxRef;
|
||||
private final WeakReference<Property<E>> propertyRef;
|
||||
private final int hashCode;
|
||||
|
||||
private boolean updating = false;
|
||||
|
||||
private EnumBidirectionalBinding(JFXComboBox<E> comboBox, Property<E> property) {
|
||||
this.comboBoxRef = new WeakReference<>(comboBox);
|
||||
this.propertyRef = new WeakReference<>(property);
|
||||
this.hashCode = System.identityHashCode(comboBox) ^ System.identityHashCode(property);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidated(Observable sourceProperty) {
|
||||
if (!updating) {
|
||||
final JFXComboBox<E> comboBox = comboBoxRef.get();
|
||||
final Property<E> property = propertyRef.get();
|
||||
|
||||
if (comboBox == null || property == null) {
|
||||
if (comboBox != null) {
|
||||
comboBox.getSelectionModel().selectedItemProperty().removeListener(this);
|
||||
}
|
||||
|
||||
if (property != null) {
|
||||
property.removeListener(this);
|
||||
}
|
||||
} else {
|
||||
updating = true;
|
||||
try {
|
||||
if (property == sourceProperty) {
|
||||
E newValue = property.getValue();
|
||||
comboBox.getSelectionModel().select(newValue);
|
||||
} else {
|
||||
E newValue = comboBox.getSelectionModel().getSelectedItem();
|
||||
property.setValue(newValue);
|
||||
}
|
||||
} finally {
|
||||
updating = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean wasGarbageCollected() {
|
||||
return comboBoxRef.get() == null || propertyRef.get() == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o)
|
||||
return true;
|
||||
if (!(o instanceof EnumBidirectionalBinding))
|
||||
return false;
|
||||
|
||||
EnumBidirectionalBinding<?> that = (EnumBidirectionalBinding<?>) o;
|
||||
|
||||
final JFXComboBox<E> comboBox = this.comboBoxRef.get();
|
||||
final Property<E> property = this.propertyRef.get();
|
||||
|
||||
final JFXComboBox<?> thatComboBox = that.comboBoxRef.get();
|
||||
final Property<?> thatProperty = that.propertyRef.get();
|
||||
|
||||
if (comboBox == null || property == null || thatComboBox == null || thatProperty == null)
|
||||
return false;
|
||||
|
||||
return comboBox == thatComboBox && property == thatProperty;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind combo box selection with given enum property bidirectionally.
|
||||
* You should <b>only and always</b> use {@code bindEnum} as well as {@code unbindEnum} at the same time.
|
||||
*
|
||||
* @param comboBox the combo box being bound with {@code property}.
|
||||
* @param property the property being bound with {@code combo box}.
|
||||
* @see #unbindEnum(JFXComboBox)
|
||||
* @see #unbindEnum(JFXComboBox, Property)
|
||||
* @see ExtendedProperties#selectedItemPropertyFor(ComboBox)
|
||||
*/
|
||||
public static <T extends Enum<T>> void bindEnum(JFXComboBox<T> comboBox, Property<T> property) {
|
||||
unbindEnum(comboBox);
|
||||
EnumBidirectionalBinding<T> binding = new EnumBidirectionalBinding<>(comboBox, property);
|
||||
|
||||
T currentValue = property.getValue();
|
||||
@SuppressWarnings("unchecked")
|
||||
T[] enumConstants = (T[]) currentValue.getClass().getEnumConstants();
|
||||
ChangeListener<Number> listener = (a, b, newValue) -> property.setValue(enumConstants[newValue.intValue()]);
|
||||
comboBox.getSelectionModel().selectedItemProperty().removeListener(binding);
|
||||
property.removeListener(binding);
|
||||
|
||||
comboBox.getSelectionModel().select(currentValue.ordinal());
|
||||
comboBox.getProperties().put("FXUtils.bindEnum.listener", listener);
|
||||
comboBox.getSelectionModel().selectedIndexProperty().addListener(listener);
|
||||
comboBox.getSelectionModel().select(property.getValue());
|
||||
comboBox.getSelectionModel().selectedItemProperty().addListener(binding);
|
||||
property.addListener(binding);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -666,11 +742,10 @@ public final class FXUtils {
|
||||
* @see #bindEnum(JFXComboBox, Property)
|
||||
* @see ExtendedProperties#selectedItemPropertyFor(ComboBox)
|
||||
*/
|
||||
public static void unbindEnum(JFXComboBox<? extends Enum<?>> comboBox) {
|
||||
@SuppressWarnings("unchecked")
|
||||
ChangeListener<Number> listener = (ChangeListener<Number>) comboBox.getProperties().remove("FXUtils.bindEnum.listener");
|
||||
if (listener != null)
|
||||
comboBox.getSelectionModel().selectedIndexProperty().removeListener(listener);
|
||||
public static <T extends Enum<T>> void unbindEnum(JFXComboBox<T> comboBox, Property<T> property) {
|
||||
EnumBidirectionalBinding<T> binding = new EnumBidirectionalBinding<>(comboBox, property);
|
||||
comboBox.getSelectionModel().selectedItemProperty().removeListener(binding);
|
||||
property.removeListener(binding);
|
||||
}
|
||||
|
||||
public static void bindAllEnabled(BooleanProperty allEnabled, BooleanProperty... children) {
|
||||
|
||||
@@ -108,10 +108,10 @@ public class OfflineAccountSkinPane extends StackPane {
|
||||
|
||||
skinItem.loadChildren(Arrays.asList(
|
||||
new MultiFileItem.Option<>(i18n("message.default"), Skin.Type.DEFAULT),
|
||||
new MultiFileItem.Option<>("Steve", Skin.Type.STEVE),
|
||||
new MultiFileItem.Option<>("Alex", Skin.Type.ALEX),
|
||||
new MultiFileItem.Option<>(i18n("account.skin.type.steve"), Skin.Type.STEVE),
|
||||
new MultiFileItem.Option<>(i18n("account.skin.type.alex"), Skin.Type.ALEX),
|
||||
new MultiFileItem.Option<>(i18n("account.skin.type.local_file"), Skin.Type.LOCAL_FILE),
|
||||
new MultiFileItem.Option<>("LittleSkin", Skin.Type.LITTLE_SKIN),
|
||||
new MultiFileItem.Option<>(i18n("account.skin.type.little_skin"), Skin.Type.LITTLE_SKIN),
|
||||
new MultiFileItem.Option<>(i18n("account.skin.type.csl_api"), Skin.Type.CUSTOM_SKIN_LOADER_API)
|
||||
));
|
||||
|
||||
@@ -201,6 +201,12 @@ public class OfflineAccountSkinPane extends StackPane {
|
||||
}
|
||||
|
||||
private Skin getSkin() {
|
||||
return new Skin(skinItem.getSelectedData(), cslApiField.getText(), modelCombobox.getValue(), skinSelector.getValue(), capeSelector.getValue());
|
||||
Skin.Type type = skinItem.getSelectedData();
|
||||
if (type == Skin.Type.LOCAL_FILE) {
|
||||
return new Skin(type, cslApiField.getText(), modelCombobox.getValue(), skinSelector.getValue(), capeSelector.getValue());
|
||||
} else {
|
||||
String cslApi = type == Skin.Type.CUSTOM_SKIN_LOADER_API ? cslApiField.getText() : null;
|
||||
return new Skin(type, cslApi, null, null, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,8 @@ import javafx.scene.layout.Pane;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import javafx.scene.layout.VBox;
|
||||
import org.jackhuang.hmcl.ui.FXUtils;
|
||||
import org.jackhuang.hmcl.ui.SVG;
|
||||
import org.jackhuang.hmcl.ui.versions.VersionPage;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@@ -54,11 +56,36 @@ public class AdvancedListBox extends ScrollPane {
|
||||
return this;
|
||||
}
|
||||
|
||||
public AdvancedListBox addNavigationDrawerItem(Consumer<AdvancedListItem> fn) {
|
||||
private AdvancedListItem createNavigationDrawerItem(String title, SVG leftGraphic) {
|
||||
AdvancedListItem item = new AdvancedListItem();
|
||||
item.getStyleClass().add("navigation-drawer-item");
|
||||
item.setActionButtonVisible(false);
|
||||
fn.accept(item);
|
||||
item.setTitle(title);
|
||||
if (leftGraphic != null) {
|
||||
item.setLeftGraphic(VersionPage.wrap(leftGraphic));
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
public AdvancedListBox addNavigationDrawerItem(String title, SVG leftGraphic, Runnable onAction) {
|
||||
return addNavigationDrawerItem(title, leftGraphic, onAction, null);
|
||||
}
|
||||
|
||||
public AdvancedListBox addNavigationDrawerItem(String title, SVG leftGraphic, Runnable onAction, Consumer<AdvancedListItem> initializer) {
|
||||
AdvancedListItem item = createNavigationDrawerItem(title, leftGraphic);
|
||||
if (onAction != null) {
|
||||
item.setOnAction(e -> onAction.run());
|
||||
}
|
||||
if (initializer != null) {
|
||||
initializer.accept(item);
|
||||
}
|
||||
return add(item);
|
||||
}
|
||||
|
||||
public AdvancedListBox addNavigationDrawerTab(TabHeader tabHeader, TabControl.Tab<?> tab, String title, SVG leftGraphic) {
|
||||
AdvancedListItem item = createNavigationDrawerItem(title, leftGraphic);
|
||||
item.activeProperty().bind(tabHeader.getSelectionModel().selectedItemProperty().isEqualTo(tab));
|
||||
item.setOnAction(e -> tabHeader.select(tab));
|
||||
return add(item);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher
|
||||
* Copyright (C) 2021 huangyuhui <huanghongxun2008@126.com> and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.jackhuang.hmcl.ui.construct;
|
||||
|
||||
import javafx.scene.Cursor;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.layout.BorderPane;
|
||||
import javafx.scene.layout.VBox;
|
||||
import javafx.scene.text.TextFlow;
|
||||
import org.jackhuang.hmcl.setting.Theme;
|
||||
import org.jackhuang.hmcl.ui.Controllers;
|
||||
import org.jackhuang.hmcl.ui.FXUtils;
|
||||
import org.jackhuang.hmcl.ui.SVG;
|
||||
|
||||
public final class AnnouncementCard extends VBox {
|
||||
|
||||
public AnnouncementCard(String title, String content, Runnable onClose) {
|
||||
TextFlow body = FXUtils.segmentToTextFlow(content, Controllers::onHyperlinkAction);
|
||||
body.setLineSpacing(4);
|
||||
|
||||
BorderPane titleBar = new BorderPane();
|
||||
titleBar.getStyleClass().add("title");
|
||||
titleBar.setLeft(new Label(title));
|
||||
|
||||
if (onClose != null) {
|
||||
Node hideNode = SVG.CLOSE.createIcon(Theme.blackFill(), 20, 20);
|
||||
hideNode.setOnMouseClicked(e -> onClose.run());
|
||||
hideNode.setCursor(Cursor.HAND);
|
||||
titleBar.setRight(hideNode);
|
||||
}
|
||||
|
||||
getChildren().setAll(titleBar, body);
|
||||
setSpacing(16);
|
||||
getStyleClass().addAll("card", "announcement");
|
||||
}
|
||||
}
|
||||
@@ -149,8 +149,8 @@ final class ComponentListCell extends StackPane {
|
||||
}
|
||||
|
||||
Platform.runLater(() -> {
|
||||
double newAnimatedHeight = (list.prefHeight(-1) + 8 + 10) * (expanded ? 1 : -1);
|
||||
double newHeight = expanded ? getHeight() + newAnimatedHeight : prefHeight(-1);
|
||||
double newAnimatedHeight = (list.prefHeight(list.getWidth()) + 8 + 10) * (expanded ? 1 : -1);
|
||||
double newHeight = expanded ? getHeight() + newAnimatedHeight : prefHeight(list.getWidth());
|
||||
double contentHeight = expanded ? newAnimatedHeight : 0;
|
||||
|
||||
if (expanded) {
|
||||
|
||||
@@ -60,7 +60,6 @@ import java.util.concurrent.CancellationException;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import static org.jackhuang.hmcl.ui.FXUtils.runInFX;
|
||||
import static org.jackhuang.hmcl.ui.versions.VersionPage.wrap;
|
||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||
|
||||
public class DownloadPage extends DecoratorAnimatedPage implements DecoratorPage {
|
||||
@@ -100,43 +99,16 @@ public class DownloadPage extends DecoratorAnimatedPage implements DecoratorPage
|
||||
transitionPane.setContent(newValue.getNode(), ContainerAnimations.FADE);
|
||||
});
|
||||
|
||||
{
|
||||
AdvancedListBox sideBar = new AdvancedListBox()
|
||||
.startCategory(i18n("download.game").toUpperCase(Locale.ROOT))
|
||||
.addNavigationDrawerItem(item -> {
|
||||
item.setTitle(i18n("game"));
|
||||
item.setLeftGraphic(wrap(SVG.GAMEPAD));
|
||||
item.activeProperty().bind(tab.getSelectionModel().selectedItemProperty().isEqualTo(newGameTab));
|
||||
item.setOnAction(e -> tab.select(newGameTab));
|
||||
})
|
||||
.addNavigationDrawerItem(settingsItem -> {
|
||||
settingsItem.setTitle(i18n("modpack"));
|
||||
settingsItem.setLeftGraphic(wrap(SVG.PACK));
|
||||
settingsItem.activeProperty().bind(tab.getSelectionModel().selectedItemProperty().isEqualTo(modpackTab));
|
||||
settingsItem.setOnAction(e -> tab.select(modpackTab));
|
||||
})
|
||||
.startCategory(i18n("download.content").toUpperCase(Locale.ROOT))
|
||||
.addNavigationDrawerItem(item -> {
|
||||
item.setTitle(i18n("mods"));
|
||||
item.setLeftGraphic(wrap(SVG.PUZZLE));
|
||||
item.activeProperty().bind(tab.getSelectionModel().selectedItemProperty().isEqualTo(modTab));
|
||||
item.setOnAction(e -> tab.select(modTab));
|
||||
})
|
||||
.addNavigationDrawerItem(item -> {
|
||||
item.setTitle(i18n("resourcepack"));
|
||||
item.setLeftGraphic(wrap(SVG.TEXTURE_BOX));
|
||||
item.activeProperty().bind(tab.getSelectionModel().selectedItemProperty().isEqualTo(resourcePackTab));
|
||||
item.setOnAction(e -> tab.select(resourcePackTab));
|
||||
})
|
||||
.addNavigationDrawerItem(item -> {
|
||||
item.setTitle(i18n("world"));
|
||||
item.setLeftGraphic(wrap(SVG.EARTH));
|
||||
item.activeProperty().bind(tab.getSelectionModel().selectedItemProperty().isEqualTo(worldTab));
|
||||
item.setOnAction(e -> selectTabIfCurseForgeAvailable(worldTab));
|
||||
});
|
||||
FXUtils.setLimitWidth(sideBar, 200);
|
||||
setLeft(sideBar);
|
||||
}
|
||||
AdvancedListBox sideBar = new AdvancedListBox()
|
||||
.startCategory(i18n("download.game").toUpperCase(Locale.ROOT))
|
||||
.addNavigationDrawerTab(tab, newGameTab, i18n("game"), SVG.GAMEPAD)
|
||||
.addNavigationDrawerTab(tab, modpackTab, i18n("modpack"), SVG.PACK)
|
||||
.startCategory(i18n("download.content").toUpperCase(Locale.ROOT))
|
||||
.addNavigationDrawerTab(tab, modTab, i18n("mods"), SVG.PUZZLE)
|
||||
.addNavigationDrawerTab(tab, resourcePackTab, i18n("resourcepack"), SVG.TEXTURE_BOX)
|
||||
.addNavigationDrawerTab(tab, worldTab, i18n("world"), SVG.EARTH);
|
||||
FXUtils.setLimitWidth(sideBar, 200);
|
||||
setLeft(sideBar);
|
||||
|
||||
setCenter(transitionPane);
|
||||
}
|
||||
|
||||
@@ -25,6 +25,8 @@ import com.jfoenix.controls.JFXTextField;
|
||||
import javafx.animation.PauseTransition;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.InvalidationListener;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.Label;
|
||||
@@ -59,6 +61,8 @@ import org.jackhuang.hmcl.ui.wizard.Navigation;
|
||||
import org.jackhuang.hmcl.ui.wizard.Refreshable;
|
||||
import org.jackhuang.hmcl.ui.wizard.WizardPage;
|
||||
import org.jackhuang.hmcl.util.Holder;
|
||||
import org.jackhuang.hmcl.util.StringUtils;
|
||||
import org.jackhuang.hmcl.util.i18n.I18n;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
@@ -72,9 +76,7 @@ import static org.jackhuang.hmcl.ui.FXUtils.ignoreEvent;
|
||||
import static org.jackhuang.hmcl.ui.FXUtils.onEscPressed;
|
||||
import static org.jackhuang.hmcl.ui.ToolbarListPageSkin.wrap;
|
||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
import static org.jackhuang.hmcl.util.i18n.I18n.formatDateTime;
|
||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||
import static org.jackhuang.hmcl.util.StringUtils.isBlank;
|
||||
|
||||
public final class VersionsPage extends BorderPane implements WizardPage, Refreshable {
|
||||
private final String gameVersion;
|
||||
@@ -90,18 +92,14 @@ public final class VersionsPage extends BorderPane implements WizardPage, Refres
|
||||
private final JFXCheckBox chkRelease;
|
||||
private final JFXCheckBox chkSnapshot;
|
||||
private final JFXCheckBox chkOld;
|
||||
private final JFXButton btnRefresh;
|
||||
private final HBox checkPane;
|
||||
private final ComponentList centrePane;
|
||||
private final StackPane center;
|
||||
|
||||
private final VersionList<?> versionList;
|
||||
private CompletableFuture<?> executor;
|
||||
|
||||
private final TransitionPane toolbarPane;
|
||||
private final HBox searchBar;
|
||||
private final JFXTextField searchField;
|
||||
private boolean isSearching = false;
|
||||
private final StringProperty queryString = new SimpleStringProperty();
|
||||
|
||||
public VersionsPage(Navigation navigation, String title, String gameVersion, DownloadProvider downloadProvider, String libraryId, Runnable callback) {
|
||||
this.title = title;
|
||||
@@ -117,6 +115,8 @@ public final class VersionsPage extends BorderPane implements WizardPage, Refres
|
||||
this.setTop(hintPane);
|
||||
|
||||
root = new TransitionPane();
|
||||
BorderPane toolbarPane = new BorderPane();
|
||||
JFXButton btnRefresh;
|
||||
{
|
||||
spinner = new JFXSpinner();
|
||||
|
||||
@@ -126,7 +126,7 @@ public final class VersionsPage extends BorderPane implements WizardPage, Refres
|
||||
centrePane = new ComponentList();
|
||||
centrePane.getStyleClass().add("no-padding");
|
||||
{
|
||||
checkPane = new HBox();
|
||||
HBox checkPane = new HBox();
|
||||
checkPane.setSpacing(10);
|
||||
{
|
||||
chkRelease = new JFXCheckBox(i18n("version.game.releases"));
|
||||
@@ -139,24 +139,69 @@ public final class VersionsPage extends BorderPane implements WizardPage, Refres
|
||||
chkOld = new JFXCheckBox(i18n("version.game.old"));
|
||||
HBox.setMargin(chkOld, new Insets(10, 0, 10, 0));
|
||||
|
||||
HBox pane = new HBox();
|
||||
HBox.setHgrow(pane, Priority.ALWAYS);
|
||||
|
||||
btnRefresh = new JFXButton(i18n("button.refresh"));
|
||||
btnRefresh.getStyleClass().add("jfx-tool-bar-button");
|
||||
btnRefresh.setOnAction(e -> onRefresh());
|
||||
|
||||
checkPane.getChildren().setAll(chkRelease, chkSnapshot, chkOld, pane, btnRefresh);
|
||||
checkPane.getChildren().setAll(chkRelease, chkSnapshot, chkOld);
|
||||
}
|
||||
|
||||
list = new JFXListView<>();
|
||||
list.getStyleClass().add("jfx-list-view-float");
|
||||
VBox.setVgrow(list, Priority.ALWAYS);
|
||||
|
||||
TransitionPane rightToolbarPane = new TransitionPane();
|
||||
{
|
||||
HBox refreshPane = new HBox();
|
||||
refreshPane.setAlignment(Pos.CENTER_RIGHT);
|
||||
|
||||
btnRefresh = new JFXButton(i18n("button.refresh"));
|
||||
btnRefresh.getStyleClass().add("jfx-tool-bar-button");
|
||||
btnRefresh.setOnAction(e -> onRefresh());
|
||||
|
||||
JFXButton btnSearch = new JFXButton(i18n("search"));
|
||||
btnSearch.getStyleClass().add("jfx-tool-bar-button");
|
||||
btnSearch.setGraphic(wrap(SVG.MAGNIFY.createIcon(Theme.blackFill(), -1, -1)));
|
||||
|
||||
searchBar = new HBox();
|
||||
{
|
||||
searchBar.setAlignment(Pos.CENTER);
|
||||
searchBar.setPadding(new Insets(0, 5, 0, 0));
|
||||
|
||||
JFXTextField searchField = new JFXTextField();
|
||||
searchField.setPromptText(i18n("search"));
|
||||
HBox.setHgrow(searchField, Priority.ALWAYS);
|
||||
|
||||
JFXButton closeSearchBar = new JFXButton();
|
||||
closeSearchBar.getStyleClass().add("jfx-tool-bar-button");
|
||||
closeSearchBar.setGraphic(wrap(SVG.CLOSE.createIcon(Theme.blackFill(), -1, -1)));
|
||||
closeSearchBar.setOnAction(e -> {
|
||||
searchField.clear();
|
||||
rightToolbarPane.setContent(refreshPane, ContainerAnimations.FADE);
|
||||
});
|
||||
onEscPressed(searchField, closeSearchBar::fire);
|
||||
PauseTransition pause = new PauseTransition(Duration.millis(100));
|
||||
pause.setOnFinished(e -> queryString.set(searchField.getText()));
|
||||
searchField.textProperty().addListener((observable, oldValue, newValue) -> {
|
||||
pause.setRate(1);
|
||||
pause.playFromStart();
|
||||
});
|
||||
|
||||
searchBar.getChildren().setAll(searchField, closeSearchBar);
|
||||
|
||||
btnSearch.setOnAction(e -> {
|
||||
rightToolbarPane.setContent(searchBar, ContainerAnimations.FADE);
|
||||
searchField.requestFocus();
|
||||
});
|
||||
}
|
||||
|
||||
refreshPane.getChildren().setAll(new HBox(btnSearch, btnRefresh));
|
||||
rightToolbarPane.setContent(refreshPane, ContainerAnimations.NONE);
|
||||
}
|
||||
|
||||
// ListViewBehavior would consume ESC pressed event, preventing us from handling it, so we ignore it here
|
||||
ignoreEvent(list, KeyEvent.KEY_PRESSED, e -> e.getCode() == KeyCode.ESCAPE);
|
||||
|
||||
centrePane.getContent().setAll(checkPane, list);
|
||||
toolbarPane.setLeft(checkPane);
|
||||
toolbarPane.setRight(rightToolbarPane);
|
||||
|
||||
centrePane.getContent().setAll(toolbarPane, list);
|
||||
}
|
||||
|
||||
center.getChildren().setAll(centrePane);
|
||||
@@ -192,16 +237,39 @@ public final class VersionsPage extends BorderPane implements WizardPage, Refres
|
||||
chkOld.setVisible(hasType);
|
||||
|
||||
if (hasType) {
|
||||
centrePane.getContent().setAll(checkPane, list);
|
||||
centrePane.getContent().setAll(toolbarPane, list);
|
||||
} else {
|
||||
centrePane.getContent().setAll(list);
|
||||
}
|
||||
ComponentList.setVgrow(list, Priority.ALWAYS);
|
||||
|
||||
InvalidationListener listener = o -> list.getItems().setAll(loadVersions());
|
||||
InvalidationListener listener = o -> {
|
||||
List<RemoteVersion> versions = loadVersions();
|
||||
String query = queryString.get();
|
||||
if (!StringUtils.isBlank(query)) {
|
||||
Predicate<RemoteVersion> predicate;
|
||||
if (query.startsWith("regex:")) {
|
||||
try {
|
||||
Pattern pattern = Pattern.compile(query.substring("regex:".length()));
|
||||
predicate = it -> pattern.matcher(it.getSelfVersion()).find();
|
||||
} catch (Throwable e) {
|
||||
LOG.warning("Illegal regular expression", e);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
String lowerQueryString = query.toLowerCase(Locale.ROOT);
|
||||
predicate = it -> it.getSelfVersion().toLowerCase(Locale.ROOT).contains(lowerQueryString);
|
||||
}
|
||||
|
||||
versions = versions.stream().filter(predicate).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
list.getItems().setAll(versions);
|
||||
};
|
||||
chkRelease.selectedProperty().addListener(listener);
|
||||
chkSnapshot.selectedProperty().addListener(listener);
|
||||
chkOld.selectedProperty().addListener(listener);
|
||||
queryString.addListener(listener);
|
||||
|
||||
btnRefresh.setGraphic(wrap(SVG.REFRESH.createIcon(Theme.blackFill(), -1, -1)));
|
||||
|
||||
@@ -215,50 +283,6 @@ public final class VersionsPage extends BorderPane implements WizardPage, Refres
|
||||
callback.run();
|
||||
});
|
||||
|
||||
{
|
||||
toolbarPane = new TransitionPane();
|
||||
searchBar = new HBox();
|
||||
searchBar.setAlignment(Pos.CENTER);
|
||||
searchBar.setPadding(new Insets(0, 5, 0, 5));
|
||||
searchField = new JFXTextField();
|
||||
searchField.setPromptText(i18n("search"));
|
||||
HBox.setHgrow(searchField, Priority.ALWAYS);
|
||||
|
||||
JFXButton closeSearchBar = new JFXButton();
|
||||
closeSearchBar.getStyleClass().add("jfx-tool-bar-button");
|
||||
closeSearchBar.setGraphic(wrap(SVG.CLOSE.createIcon(Theme.blackFill(), -1, -1)));
|
||||
closeSearchBar.setOnAction(e -> {
|
||||
toolbarPane.setContent(checkPane, ContainerAnimations.FADE);
|
||||
isSearching = false;
|
||||
searchField.clear();
|
||||
list.getItems().setAll(loadVersions());
|
||||
});
|
||||
onEscPressed(searchField, closeSearchBar::fire);
|
||||
|
||||
searchBar.getChildren().setAll(searchField, closeSearchBar);
|
||||
|
||||
JFXButton searchButton = new JFXButton(i18n("search"));
|
||||
searchButton.getStyleClass().add("jfx-tool-bar-button");
|
||||
searchButton.setGraphic(wrap(SVG.MAGNIFY.createIcon(Theme.blackFill(), -1, -1)));
|
||||
searchButton.setOnAction(e -> {
|
||||
toolbarPane.setContent(searchBar, ContainerAnimations.FADE);
|
||||
searchField.requestFocus();
|
||||
});
|
||||
|
||||
checkPane.getChildren().add(checkPane.getChildren().size() - 1, searchButton);
|
||||
|
||||
centrePane.getContent().remove(checkPane);
|
||||
toolbarPane.setContent(checkPane, ContainerAnimations.FADE);
|
||||
centrePane.getContent().add(0, toolbarPane);
|
||||
|
||||
PauseTransition pause = new PauseTransition(Duration.millis(100));
|
||||
pause.setOnFinished(e -> search());
|
||||
searchField.textProperty().addListener((observable, oldValue, newValue) -> {
|
||||
pause.setRate(1);
|
||||
pause.playFromStart();
|
||||
});
|
||||
}
|
||||
|
||||
refresh();
|
||||
}
|
||||
|
||||
@@ -331,43 +355,14 @@ public final class VersionsPage extends BorderPane implements WizardPage, Refres
|
||||
refresh();
|
||||
}
|
||||
|
||||
private void onBack() { navigation.onPrev(true); }
|
||||
private void onBack() {
|
||||
navigation.onPrev(true);
|
||||
}
|
||||
|
||||
private void onSponsor() {
|
||||
FXUtils.openLink("https://bmclapidoc.bangbang93.com");
|
||||
}
|
||||
|
||||
private void search() {
|
||||
isSearching = true;
|
||||
String queryString = searchField.getText();
|
||||
|
||||
if (isBlank(queryString)) {
|
||||
list.getItems().setAll(loadVersions());
|
||||
} else {
|
||||
list.getItems().clear();
|
||||
|
||||
Predicate<String> predicate;
|
||||
if (queryString.startsWith("regex:")) {
|
||||
try {
|
||||
Pattern pattern = Pattern.compile(queryString.substring("regex:".length()));
|
||||
predicate = s -> pattern.matcher(s).find();
|
||||
} catch (Throwable e) {
|
||||
LOG.warning("Illegal regular expression", e);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
String lowerQueryString = queryString.toLowerCase(Locale.ROOT);
|
||||
predicate = s -> s.toLowerCase(Locale.ROOT).contains(lowerQueryString);
|
||||
}
|
||||
|
||||
for (RemoteVersion version : loadVersions()) {
|
||||
if (predicate.test(version.getSelfVersion())) {
|
||||
list.getItems().add(version);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class RemoteVersionListCell extends ListCell<RemoteVersion> {
|
||||
final IconedTwoLineListItem content = new IconedTwoLineListItem();
|
||||
final RipplerContainer ripplerContainer = new RipplerContainer(content);
|
||||
@@ -404,7 +399,7 @@ public final class VersionsPage extends BorderPane implements WizardPage, Refres
|
||||
|
||||
content.setTitle(remoteVersion.getSelfVersion());
|
||||
if (remoteVersion.getReleaseDate() != null) {
|
||||
content.setSubtitle(formatDateTime(remoteVersion.getReleaseDate()));
|
||||
content.setSubtitle(I18n.formatDateTime(remoteVersion.getReleaseDate()));
|
||||
} else {
|
||||
content.setSubtitle(null);
|
||||
}
|
||||
|
||||
@@ -216,6 +216,7 @@ public final class JavaDownloadDialog extends StackPane {
|
||||
distributionBox.setItems(FXCollections.observableList(new ArrayList<>(distributions)));
|
||||
|
||||
FXUtils.onChange(packageTypeBox.getSelectionModel().selectedItemProperty(), packageType -> {
|
||||
|
||||
ObservableList<DiscoJavaRemoteVersion> versions;
|
||||
if (packageType == null
|
||||
|| currentJavaVersionList.get() == null
|
||||
@@ -224,8 +225,26 @@ public final class JavaDownloadDialog extends StackPane {
|
||||
return;
|
||||
}
|
||||
|
||||
DiscoJavaRemoteVersion oldVersion = remoteVersionBox.getSelectionModel().getSelectedItem();
|
||||
remoteVersionBox.setItems(versions);
|
||||
|
||||
if (oldVersion != null) {
|
||||
for (int i = 0; i < versions.size(); i++) {
|
||||
DiscoJavaRemoteVersion version = versions.get(i);
|
||||
if (Objects.equals(version.getDistributionVersion(), oldVersion.getDistributionVersion())) {
|
||||
remoteVersionBox.getSelectionModel().select(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < versions.size(); i++) {
|
||||
DiscoJavaRemoteVersion version = versions.get(i);
|
||||
if (version.getJdkVersion() == oldVersion.getJdkVersion()) {
|
||||
remoteVersionBox.getSelectionModel().select(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < versions.size(); i++) {
|
||||
DiscoJavaRemoteVersion version = versions.get(i);
|
||||
if (version.getJdkVersion() == GameJavaVersion.LATEST.getMajorVersion()) {
|
||||
|
||||
@@ -35,7 +35,6 @@ import org.jackhuang.hmcl.ui.versions.VersionSettingsPage;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import static org.jackhuang.hmcl.ui.versions.VersionPage.wrap;
|
||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||
|
||||
public class LauncherSettingsPage extends DecoratorAnimatedPage implements DecoratorPage, PageAware {
|
||||
@@ -69,61 +68,19 @@ public class LauncherSettingsPage extends DecoratorAnimatedPage implements Decor
|
||||
transitionPane.setContent(newValue.getNode(), ContainerAnimations.FADE);
|
||||
});
|
||||
|
||||
{
|
||||
AdvancedListBox sideBar = new AdvancedListBox()
|
||||
.addNavigationDrawerItem(settingsItem -> {
|
||||
settingsItem.setTitle(i18n("settings.type.global.manage"));
|
||||
settingsItem.setLeftGraphic(wrap(SVG.GAMEPAD));
|
||||
settingsItem.activeProperty().bind(tab.getSelectionModel().selectedItemProperty().isEqualTo(gameTab));
|
||||
settingsItem.setOnAction(e -> tab.select(gameTab));
|
||||
})
|
||||
.addNavigationDrawerItem(javaItem -> {
|
||||
javaItem.setTitle(i18n("java.management"));
|
||||
javaItem.setLeftGraphic(wrap(SVG.WRENCH_OUTLINE));
|
||||
javaItem.activeProperty().bind(tab.getSelectionModel().selectedItemProperty().isEqualTo(javaManagementTab));
|
||||
javaItem.setOnAction(e -> tab.select(javaManagementTab));
|
||||
})
|
||||
.startCategory(i18n("launcher").toUpperCase(Locale.ROOT))
|
||||
.addNavigationDrawerItem(settingsItem -> {
|
||||
settingsItem.setTitle(i18n("settings.launcher.general"));
|
||||
settingsItem.setLeftGraphic(wrap(SVG.APPLICATION_OUTLINE));
|
||||
settingsItem.activeProperty().bind(tab.getSelectionModel().selectedItemProperty().isEqualTo(settingsTab));
|
||||
settingsItem.setOnAction(e -> tab.select(settingsTab));
|
||||
})
|
||||
.addNavigationDrawerItem(personalizationItem -> {
|
||||
personalizationItem.setTitle(i18n("settings.launcher.appearance"));
|
||||
personalizationItem.setLeftGraphic(wrap(SVG.STYLE_OUTLINE));
|
||||
personalizationItem.activeProperty().bind(tab.getSelectionModel().selectedItemProperty().isEqualTo(personalizationTab));
|
||||
personalizationItem.setOnAction(e -> tab.select(personalizationTab));
|
||||
})
|
||||
.addNavigationDrawerItem(downloadItem -> {
|
||||
downloadItem.setTitle(i18n("download"));
|
||||
downloadItem.setLeftGraphic(wrap(SVG.DOWNLOAD_OUTLINE));
|
||||
downloadItem.activeProperty().bind(tab.getSelectionModel().selectedItemProperty().isEqualTo(downloadTab));
|
||||
downloadItem.setOnAction(e -> tab.select(downloadTab));
|
||||
})
|
||||
.startCategory(i18n("help").toUpperCase(Locale.ROOT))
|
||||
.addNavigationDrawerItem(helpItem -> {
|
||||
helpItem.setTitle(i18n("help"));
|
||||
helpItem.setLeftGraphic(wrap(SVG.HELP_CIRCLE_OUTLINE));
|
||||
helpItem.activeProperty().bind(tab.getSelectionModel().selectedItemProperty().isEqualTo(helpTab));
|
||||
helpItem.setOnAction(e -> tab.select(helpTab));
|
||||
})
|
||||
.addNavigationDrawerItem(feedbackItem -> {
|
||||
feedbackItem.setTitle(i18n("feedback"));
|
||||
feedbackItem.setLeftGraphic(wrap(SVG.MESSAGE_ALERT_OUTLINE));
|
||||
feedbackItem.activeProperty().bind(tab.getSelectionModel().selectedItemProperty().isEqualTo(feedbackTab));
|
||||
feedbackItem.setOnAction(e -> tab.select(feedbackTab));
|
||||
})
|
||||
.addNavigationDrawerItem(aboutItem -> {
|
||||
aboutItem.setTitle(i18n("about"));
|
||||
aboutItem.setLeftGraphic(wrap(SVG.INFORMATION_OUTLINE));
|
||||
aboutItem.activeProperty().bind(tab.getSelectionModel().selectedItemProperty().isEqualTo(aboutTab));
|
||||
aboutItem.setOnAction(e -> tab.select(aboutTab));
|
||||
});
|
||||
FXUtils.setLimitWidth(sideBar, 200);
|
||||
setLeft(sideBar);
|
||||
}
|
||||
AdvancedListBox sideBar = new AdvancedListBox()
|
||||
.addNavigationDrawerTab(tab, gameTab, i18n("settings.type.global.manage"), SVG.GAMEPAD)
|
||||
.addNavigationDrawerTab(tab, javaManagementTab, i18n("java.management"), SVG.WRENCH_OUTLINE)
|
||||
.startCategory(i18n("launcher").toUpperCase(Locale.ROOT))
|
||||
.addNavigationDrawerTab(tab, settingsTab, i18n("settings.launcher.general"), SVG.APPLICATION_OUTLINE)
|
||||
.addNavigationDrawerTab(tab, personalizationTab, i18n("settings.launcher.appearance"), SVG.STYLE_OUTLINE)
|
||||
.addNavigationDrawerTab(tab, downloadTab, i18n("download"), SVG.DOWNLOAD_OUTLINE)
|
||||
.startCategory(i18n("help").toUpperCase(Locale.ROOT))
|
||||
.addNavigationDrawerTab(tab, helpTab, i18n("help"), SVG.HELP_CIRCLE_OUTLINE)
|
||||
.addNavigationDrawerTab(tab, feedbackTab, i18n("feedback"), SVG.MESSAGE_ALERT_OUTLINE)
|
||||
.addNavigationDrawerTab(tab, aboutTab, i18n("about"), SVG.INFORMATION_OUTLINE);
|
||||
FXUtils.setLimitWidth(sideBar, 200);
|
||||
setLeft(sideBar);
|
||||
|
||||
setCenter(transitionPane);
|
||||
}
|
||||
|
||||
@@ -28,13 +28,16 @@ import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.Cursor;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.layout.BorderPane;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import javafx.scene.layout.VBox;
|
||||
import javafx.scene.shape.Rectangle;
|
||||
import javafx.scene.text.TextFlow;
|
||||
import javafx.util.Duration;
|
||||
import org.jackhuang.hmcl.Metadata;
|
||||
import org.jackhuang.hmcl.game.Version;
|
||||
@@ -47,7 +50,6 @@ import org.jackhuang.hmcl.ui.SVG;
|
||||
import org.jackhuang.hmcl.ui.animation.AnimationUtils;
|
||||
import org.jackhuang.hmcl.ui.animation.ContainerAnimations;
|
||||
import org.jackhuang.hmcl.ui.animation.TransitionPane;
|
||||
import org.jackhuang.hmcl.ui.construct.AnnouncementCard;
|
||||
import org.jackhuang.hmcl.ui.construct.MessageDialogPane;
|
||||
import org.jackhuang.hmcl.ui.construct.PopupMenu;
|
||||
import org.jackhuang.hmcl.ui.construct.TwoLineListItem;
|
||||
@@ -102,23 +104,49 @@ public final class MainPage extends StackPane implements DecoratorPage {
|
||||
setPadding(new Insets(20));
|
||||
|
||||
if (Metadata.isNightly() || (Metadata.isDev() && !Objects.equals(Metadata.VERSION, config().getShownTips().get(ANNOUNCEMENT)))) {
|
||||
AnnouncementCard announcementCard = null;
|
||||
|
||||
String title;
|
||||
String content;
|
||||
if (Metadata.isNightly()) {
|
||||
announcementCard = new AnnouncementCard(i18n("update.channel.nightly.title"), i18n("update.channel.nightly.hint"), null);
|
||||
} else if (Metadata.isDev()) {
|
||||
announcementCard = new AnnouncementCard(i18n("update.channel.dev.title"), i18n("update.channel.dev.hint"), this::hideAnnouncementPane);
|
||||
title = i18n("update.channel.nightly.title");
|
||||
content = i18n("update.channel.nightly.hint");
|
||||
} else {
|
||||
title = i18n("update.channel.dev.title");
|
||||
content = i18n("update.channel.dev.hint");
|
||||
}
|
||||
|
||||
if (announcementCard != null) {
|
||||
VBox announcementBox = new VBox(16);
|
||||
announcementBox.getChildren().add(announcementCard);
|
||||
VBox announcementCard = new VBox();
|
||||
|
||||
announcementPane = new TransitionPane();
|
||||
announcementPane.setContent(announcementBox, ContainerAnimations.NONE);
|
||||
BorderPane titleBar = new BorderPane();
|
||||
titleBar.getStyleClass().add("title");
|
||||
titleBar.setLeft(new Label(title));
|
||||
|
||||
getChildren().add(announcementPane);
|
||||
}
|
||||
Node hideNode = SVG.CLOSE.createIcon(Theme.blackFill(), 20, 20);
|
||||
hideNode.setCursor(Cursor.HAND);
|
||||
titleBar.setRight(hideNode);
|
||||
FXUtils.onClicked(hideNode, () -> {
|
||||
if (announcementPane != null) {
|
||||
if (Metadata.isDev()) {
|
||||
config().getShownTips().put(ANNOUNCEMENT, Metadata.VERSION);
|
||||
}
|
||||
|
||||
announcementPane.setContent(new StackPane(), ContainerAnimations.FADE);
|
||||
}
|
||||
});
|
||||
|
||||
TextFlow body = FXUtils.segmentToTextFlow(content, Controllers::onHyperlinkAction);
|
||||
body.setLineSpacing(4);
|
||||
|
||||
announcementCard.getChildren().setAll(titleBar, body);
|
||||
announcementCard.setSpacing(16);
|
||||
announcementCard.getStyleClass().addAll("card", "announcement");
|
||||
|
||||
VBox announcementBox = new VBox(16);
|
||||
announcementBox.getChildren().add(announcementCard);
|
||||
|
||||
announcementPane = new TransitionPane();
|
||||
announcementPane.setContent(announcementBox, ContainerAnimations.NONE);
|
||||
|
||||
getChildren().add(announcementPane);
|
||||
}
|
||||
|
||||
updatePane = new StackPane();
|
||||
@@ -297,13 +325,6 @@ public final class MainPage extends StackPane implements DecoratorPage {
|
||||
showUpdate.set(false);
|
||||
}
|
||||
|
||||
public void hideAnnouncementPane() {
|
||||
if (announcementPane != null) {
|
||||
config().getShownTips().put(ANNOUNCEMENT, Metadata.VERSION);
|
||||
announcementPane.setContent(new StackPane(), ContainerAnimations.FADE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReadOnlyObjectWrapper<State> stateProperty() {
|
||||
return state;
|
||||
|
||||
@@ -240,7 +240,7 @@ public final class AdvancedVersionSettingPage extends StackPane implements Decor
|
||||
FXUtils.unbind(txtWrapper, versionSetting.wrapperProperty());
|
||||
FXUtils.unbind(txtPreLaunchCommand, versionSetting.preLaunchCommandProperty());
|
||||
FXUtils.unbind(txtPostExitCommand, versionSetting.postExitCommandProperty());
|
||||
FXUtils.unbindEnum(cboRenderer);
|
||||
FXUtils.unbindEnum(cboRenderer, versionSetting.rendererProperty());
|
||||
noGameCheckPane.selectedProperty().unbindBidirectional(versionSetting.notCheckGameProperty());
|
||||
noJVMCheckPane.selectedProperty().unbindBidirectional(versionSetting.notCheckJVMProperty());
|
||||
noJVMArgsPane.selectedProperty().unbindBidirectional(versionSetting.noJVMArgsProperty());
|
||||
|
||||
@@ -95,11 +95,7 @@ public class DownloadListPage extends Control implements DecoratorPage, VersionP
|
||||
private Runnable retrySearch;
|
||||
|
||||
public DownloadListPage(RemoteModRepository repository) {
|
||||
this(repository, null);
|
||||
}
|
||||
|
||||
public DownloadListPage(RemoteModRepository repository, DownloadPage.DownloadCallback callback) {
|
||||
this(repository, callback, false);
|
||||
this(repository, null, false);
|
||||
}
|
||||
|
||||
public DownloadListPage(RemoteModRepository repository, DownloadPage.DownloadCallback callback, boolean versionSelection) {
|
||||
|
||||
@@ -55,9 +55,6 @@ import org.jackhuang.hmcl.util.versioning.GameVersionNumber;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.File;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.FormatStyle;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
@@ -308,7 +305,7 @@ public class DownloadPage extends Control implements DecoratorPage {
|
||||
|
||||
ComponentList sublist = new ComponentList(() -> {
|
||||
ArrayList<ModItem> items = new ArrayList<>(versions.size());
|
||||
for (RemoteMod.Version v: versions) {
|
||||
for (RemoteMod.Version v : versions) {
|
||||
items.add(new ModItem(v, control));
|
||||
}
|
||||
return items;
|
||||
@@ -388,7 +385,7 @@ public class DownloadPage extends Control implements DecoratorPage {
|
||||
TwoLineListItem content = new TwoLineListItem();
|
||||
HBox.setHgrow(content, Priority.ALWAYS);
|
||||
content.setTitle(dataItem.getName());
|
||||
content.setSubtitle(FORMATTER.format(dataItem.getDatePublished()));
|
||||
content.setSubtitle(I18n.formatDateTime(dataItem.getDatePublished()));
|
||||
|
||||
switch (dataItem.getVersionType()) {
|
||||
case Alpha:
|
||||
@@ -442,7 +439,9 @@ public class DownloadPage extends Control implements DecoratorPage {
|
||||
|
||||
private static final class ModVersion extends JFXDialogLayout {
|
||||
public ModVersion(RemoteMod.Version version, DownloadPage selfPage) {
|
||||
this.setHeading(new HBox(new Label(i18n("mods.download.title", version.getName()))));
|
||||
boolean isModpack = selfPage.repository.getType() == RemoteModRepository.Type.MODPACK;
|
||||
|
||||
this.setHeading(new HBox(new Label(i18n(isModpack ? "modpack.download.title" : "mods.download.title", version.getName()))));
|
||||
|
||||
VBox box = new VBox(8);
|
||||
box.setPadding(new Insets(8));
|
||||
@@ -464,10 +463,10 @@ public class DownloadPage extends Control implements DecoratorPage {
|
||||
|
||||
this.setBody(box);
|
||||
|
||||
JFXButton downloadButton = new JFXButton(i18n("mods.install"));
|
||||
JFXButton downloadButton = new JFXButton(isModpack ? i18n("install.modpack") : i18n("mods.install"));
|
||||
downloadButton.getStyleClass().add("dialog-accept");
|
||||
downloadButton.setOnAction(e -> {
|
||||
if (!spinnerPane.isLoading() && spinnerPane.getFailedReason() == null) {
|
||||
if (isModpack || !spinnerPane.isLoading() && spinnerPane.getFailedReason() == null) {
|
||||
fireEvent(new DialogCloseEvent());
|
||||
}
|
||||
selfPage.download(version);
|
||||
@@ -528,8 +527,6 @@ public class DownloadPage extends Control implements DecoratorPage {
|
||||
}
|
||||
}
|
||||
|
||||
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.FULL).withLocale(Locale.getDefault()).withZone(ZoneId.systemDefault());
|
||||
|
||||
public interface DownloadCallback {
|
||||
void download(Profile profile, @Nullable String version, RemoteMod.Version file);
|
||||
}
|
||||
|
||||
@@ -87,26 +87,10 @@ public class GameListPage extends DecoratorAnimatedPage implements DecoratorPage
|
||||
}
|
||||
|
||||
AdvancedListBox bottomLeftCornerList = new AdvancedListBox()
|
||||
.addNavigationDrawerItem(installNewGameItem -> {
|
||||
installNewGameItem.setTitle(i18n("install.new_game"));
|
||||
installNewGameItem.setLeftGraphic(VersionPage.wrap(SVG.PLUS_CIRCLE_OUTLINE));
|
||||
installNewGameItem.setOnAction(e -> Versions.addNewGame());
|
||||
})
|
||||
.addNavigationDrawerItem(installModpackItem -> {
|
||||
installModpackItem.setTitle(i18n("install.modpack"));
|
||||
installModpackItem.setLeftGraphic(VersionPage.wrap(SVG.PACK));
|
||||
installModpackItem.setOnAction(e -> Versions.importModpack());
|
||||
})
|
||||
.addNavigationDrawerItem(refreshItem -> {
|
||||
refreshItem.setTitle(i18n("button.refresh"));
|
||||
refreshItem.setLeftGraphic(VersionPage.wrap(SVG.REFRESH));
|
||||
refreshItem.setOnAction(e -> gameList.refreshList());
|
||||
})
|
||||
.addNavigationDrawerItem(globalManageItem -> {
|
||||
globalManageItem.setTitle(i18n("settings.type.global.manage"));
|
||||
globalManageItem.setLeftGraphic(VersionPage.wrap(SVG.GEAR_OUTLINE));
|
||||
globalManageItem.setOnAction(e -> modifyGlobalGameSettings());
|
||||
});
|
||||
.addNavigationDrawerItem(i18n("install.new_game"), SVG.PLUS_CIRCLE_OUTLINE, Versions::addNewGame)
|
||||
.addNavigationDrawerItem(i18n("install.modpack"), SVG.PACK, Versions::importModpack)
|
||||
.addNavigationDrawerItem(i18n("button.refresh"), SVG.REFRESH, gameList::refreshList)
|
||||
.addNavigationDrawerItem(i18n("settings.type.global.manage"), SVG.GEAR_OUTLINE, this::modifyGlobalGameSettings);
|
||||
FXUtils.setLimitHeight(bottomLeftCornerList, 40 * 4 + 12 * 2);
|
||||
setLeft(pane, bottomLeftCornerList);
|
||||
}
|
||||
|
||||
@@ -308,25 +308,14 @@ public class VersionPage extends DecoratorAnimatedPage implements DecoratorPage
|
||||
);
|
||||
|
||||
AdvancedListBox toolbar = new AdvancedListBox()
|
||||
.addNavigationDrawerItem(upgradeItem -> {
|
||||
upgradeItem.setTitle(i18n("version.update"));
|
||||
upgradeItem.setLeftGraphic(wrap(SVG.UPDATE));
|
||||
.addNavigationDrawerItem(i18n("version.update"), SVG.UPDATE, control::updateGame, upgradeItem -> {
|
||||
upgradeItem.visibleProperty().bind(control.currentVersionUpgradable);
|
||||
upgradeItem.setOnAction(e -> control.updateGame());
|
||||
})
|
||||
.addNavigationDrawerItem(testGameItem -> {
|
||||
testGameItem.setTitle(i18n("version.launch.test"));
|
||||
testGameItem.setLeftGraphic(wrap(SVG.ROCKET_LAUNCH_OUTLINE));
|
||||
testGameItem.setOnAction(e -> control.testGame());
|
||||
})
|
||||
.addNavigationDrawerItem(browseMenuItem -> {
|
||||
browseMenuItem.setTitle(i18n("settings.game.exploration"));
|
||||
browseMenuItem.setLeftGraphic(wrap(SVG.FOLDER_OUTLINE));
|
||||
.addNavigationDrawerItem(i18n("version.launch.test"), SVG.ROCKET_LAUNCH_OUTLINE, control::testGame)
|
||||
.addNavigationDrawerItem(i18n("settings.game.exploration"), SVG.FOLDER_OUTLINE, null, browseMenuItem -> {
|
||||
browseMenuItem.setOnAction(e -> browsePopup.show(browseMenuItem, JFXPopup.PopupVPosition.BOTTOM, JFXPopup.PopupHPosition.LEFT, browseMenuItem.getWidth(), 0));
|
||||
})
|
||||
.addNavigationDrawerItem(managementItem -> {
|
||||
managementItem.setTitle(i18n("settings.game.management"));
|
||||
managementItem.setLeftGraphic(wrap(SVG.WRENCH_OUTLINE));
|
||||
.addNavigationDrawerItem(i18n("settings.game.management"), SVG.WRENCH_OUTLINE, null, managementItem -> {
|
||||
managementItem.setOnAction(e -> managementPopup.show(managementItem, JFXPopup.PopupVPosition.BOTTOM, JFXPopup.PopupHPosition.LEFT, managementItem.getWidth(), 0));
|
||||
});
|
||||
toolbar.getStyleClass().add("advanced-list-box-clear-padding");
|
||||
|
||||
@@ -46,6 +46,7 @@ import org.jackhuang.hmcl.ui.decorator.DecoratorPage;
|
||||
import org.jackhuang.hmcl.util.Lang;
|
||||
import org.jackhuang.hmcl.util.Pair;
|
||||
import org.jackhuang.hmcl.util.javafx.BindingMapping;
|
||||
import org.jackhuang.hmcl.util.javafx.PropertyUtils;
|
||||
import org.jackhuang.hmcl.util.javafx.SafeStringConverter;
|
||||
import org.jackhuang.hmcl.util.platform.Architecture;
|
||||
import org.jackhuang.hmcl.java.JavaRuntime;
|
||||
@@ -65,6 +66,7 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
|
||||
|
||||
private static final ObjectProperty<OperatingSystem.PhysicalMemoryStatus> memoryStatus = new SimpleObjectProperty<>(OperatingSystem.PhysicalMemoryStatus.INVALID);
|
||||
private static TimerTask memoryStatusUpdateTask;
|
||||
|
||||
private static void initMemoryStatusUpdateTask() {
|
||||
FXUtils.checkFxUserThread();
|
||||
if (memoryStatusUpdateTask != null)
|
||||
@@ -135,10 +137,6 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
|
||||
rootPane.getStyleClass().add("card-list");
|
||||
|
||||
if (globalSetting) {
|
||||
HintPane skinHint = new HintPane(MessageDialogPane.MessageType.INFO);
|
||||
skinHint.setText(i18n("settings.skin"));
|
||||
rootPane.getChildren().add(skinHint);
|
||||
|
||||
HintPane specificSettingsHint = new HintPane(MessageDialogPane.MessageType.WARNING);
|
||||
Text text = new Text();
|
||||
text.textProperty().bind(BindingMapping.of(selectedVersion)
|
||||
@@ -190,6 +188,31 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
|
||||
componentList = new ComponentList();
|
||||
componentList.setDepth(1);
|
||||
|
||||
if (!globalSetting) {
|
||||
BorderPane copyGlobalPane = new BorderPane();
|
||||
{
|
||||
Label label = new Label(i18n("settings.game.copy_global"));
|
||||
copyGlobalPane.setLeft(label);
|
||||
BorderPane.setAlignment(label, Pos.CENTER_LEFT);
|
||||
|
||||
JFXButton copyAll = new JFXButton(i18n("settings.game.copy_global.copy_all"));
|
||||
copyAll.disableProperty().bind(modpack);
|
||||
copyGlobalPane.setRight(copyAll);
|
||||
copyAll.setOnAction(e -> Controllers.confirm(i18n("settings.game.copy_global.copy_all.confirm"), null, () -> {
|
||||
Set<String> ignored = new HashSet<>(Arrays.asList(
|
||||
"usesGlobal",
|
||||
"versionIcon"
|
||||
));
|
||||
|
||||
PropertyUtils.copyProperties(profile.getGlobal(), lastVersionSetting, name -> !ignored.contains(name));
|
||||
}, null));
|
||||
copyAll.getStyleClass().add("jfx-button-border");
|
||||
BorderPane.setAlignment(copyAll, Pos.CENTER_RIGHT);
|
||||
}
|
||||
|
||||
componentList.getContent().add(copyGlobalPane);
|
||||
}
|
||||
|
||||
javaItem = new MultiFileItem<>();
|
||||
javaSublist = new ComponentSublist();
|
||||
javaSublist.getContent().add(javaItem);
|
||||
@@ -446,7 +469,7 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
|
||||
showAdvancedSettingPane.setRight(button);
|
||||
}
|
||||
|
||||
componentList.getContent().setAll(
|
||||
componentList.getContent().addAll(
|
||||
javaSublist,
|
||||
gameDirSublist,
|
||||
maxMemoryPane,
|
||||
@@ -531,8 +554,8 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
|
||||
FXUtils.unbindBoolean(chkAutoAllocate, lastVersionSetting.autoMemoryProperty());
|
||||
FXUtils.unbindBoolean(chkFullscreen, lastVersionSetting.fullscreenProperty());
|
||||
showLogsPane.selectedProperty().unbindBidirectional(lastVersionSetting.showLogsProperty());
|
||||
FXUtils.unbindEnum(cboLauncherVisibility);
|
||||
FXUtils.unbindEnum(cboProcessPriority);
|
||||
FXUtils.unbindEnum(cboLauncherVisibility, lastVersionSetting.launcherVisibilityProperty());
|
||||
FXUtils.unbindEnum(cboProcessPriority, lastVersionSetting.processPriorityProperty());
|
||||
|
||||
lastVersionSetting.usesGlobalProperty().removeListener(usesGlobalListener);
|
||||
lastVersionSetting.javaVersionTypeProperty().removeListener(javaListener);
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
package org.jackhuang.hmcl.util;
|
||||
|
||||
import org.jackhuang.hmcl.game.*;
|
||||
import org.jackhuang.hmcl.mod.LocalModFile;
|
||||
import org.jackhuang.hmcl.mod.ModManager;
|
||||
import org.jackhuang.hmcl.setting.VersionSetting;
|
||||
import org.jackhuang.hmcl.util.gson.JsonUtils;
|
||||
import org.jackhuang.hmcl.util.platform.Architecture;
|
||||
@@ -60,7 +62,11 @@ public final class NativePatcher {
|
||||
});
|
||||
}
|
||||
|
||||
public static Version patchNative(Version version, String gameVersion, JavaRuntime javaVersion, VersionSetting settings) {
|
||||
public static Version patchNative(DefaultGameRepository repository,
|
||||
Version version, String gameVersion,
|
||||
JavaRuntime javaVersion,
|
||||
VersionSetting settings,
|
||||
List<String> javaArguments) {
|
||||
if (settings.getNativesDirType() == NativesDirectoryType.CUSTOM) {
|
||||
if (gameVersion != null && GameVersionNumber.compare(gameVersion, "1.19") < 0)
|
||||
return version;
|
||||
@@ -83,7 +89,7 @@ public final class NativePatcher {
|
||||
final boolean useNativeOpenAL = settings.isUseNativeOpenAL();
|
||||
|
||||
if (OperatingSystem.CURRENT_OS.isLinuxOrBSD() && (useNativeGLFW || useNativeOpenAL)
|
||||
&& GameVersionNumber.compare(gameVersion, "1.19") >= 0) {
|
||||
&& gameVersion != null && GameVersionNumber.compare(gameVersion, "1.19") >= 0) {
|
||||
|
||||
version = version.setLibraries(version.getLibraries().stream()
|
||||
.filter(library -> {
|
||||
@@ -124,6 +130,7 @@ public final class NativePatcher {
|
||||
return version;
|
||||
}
|
||||
|
||||
boolean lwjglVersionChanged = false;
|
||||
ArrayList<Library> newLibraries = new ArrayList<>();
|
||||
for (Library library : version.getLibraries()) {
|
||||
if (!library.appliesToCurrentEnvironment())
|
||||
@@ -145,14 +152,33 @@ public final class NativePatcher {
|
||||
} else if (replacement != null) {
|
||||
LOG.info("Replace " + library.getName() + " with " + replacement.getName());
|
||||
newLibraries.add(replacement);
|
||||
|
||||
if ("org.lwjgl:lwjgl".equals(library.getName()) && !Objects.equals(library.getVersion(), replacement.getVersion())) {
|
||||
lwjglVersionChanged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (lwjglVersionChanged) {
|
||||
ModManager modManager = repository.getModManager(version.getId());
|
||||
try {
|
||||
for (LocalModFile mod : modManager.getMods()) {
|
||||
if ("sodium".equals(mod.getId())) {
|
||||
// https://github.com/CaffeineMC/sodium/issues/2561
|
||||
javaArguments.add("-Dsodium.checks.issue2561=false");
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
LOG.warning("Failed to get mods", e);
|
||||
}
|
||||
}
|
||||
|
||||
return version.setLibraries(newLibraries);
|
||||
}
|
||||
|
||||
public static Library getMesaLoader(JavaRuntime javaVersion, Renderer renderer) {
|
||||
public static Library getMesaLoader(JavaRuntime javaVersion) {
|
||||
return getNatives(javaVersion.getPlatform()).get("mesa-loader");
|
||||
}
|
||||
}
|
||||
@@ -77,7 +77,7 @@ public final class I18n {
|
||||
public static String formatDateTime(Instant instant) {
|
||||
DateTimeFormatter formatter = dateTimeFormatter;
|
||||
if (formatter == null) {
|
||||
formatter = dateTimeFormatter = DateTimeFormatter.ofPattern(getResourceBundle().getString("world.time")).withZone(ZoneId.systemDefault());
|
||||
formatter = dateTimeFormatter = DateTimeFormatter.ofPattern(getResourceBundle().getString("datetime.format")).withZone(ZoneId.systemDefault());
|
||||
}
|
||||
|
||||
return formatter.format(instant);
|
||||
|
||||
@@ -152,15 +152,15 @@ fi
|
||||
|
||||
# Java not found
|
||||
|
||||
if [ "$_HMCL_OS" == "osx" ]; then
|
||||
_HMCL_DOWNLOAD_PAGE_OS="macos"
|
||||
else
|
||||
_HMCL_DOWNLOAD_PAGE_OS="$_HMCL_OS"
|
||||
fi
|
||||
|
||||
case "$_HMCL_OS-$_HMCL_ARCH" in
|
||||
windows-x86|windows-x86_64|windows-arm64|linux-x86|linux-x86_64|linux-arm32|linux-arm64|linux-riscv64|linux-loongarch64|macos-x86_64|macos-arm64)
|
||||
_HMCL_JAVA_DOWNLOAD_PAGE="https://docs.hmcl.net/downloads/$_HMCL_DOWNLOAD_PAGE_OS/$_HMCL_ARCH.html"
|
||||
windows-x86|windows-x86_64|windows-arm64)
|
||||
_HMCL_JAVA_DOWNLOAD_PAGE="https://docs.hmcl.net/downloads/windows/$_HMCL_ARCH.html"
|
||||
;;
|
||||
linux-x86|linux-x86_64|linux-arm64|linux-arm32|linux-riscv64|linux-loongarch64)
|
||||
_HMCL_JAVA_DOWNLOAD_PAGE="https://docs.hmcl.net/downloads/linux/$_HMCL_ARCH.html"
|
||||
;;
|
||||
osx-x86_64|osx-arm64)
|
||||
_HMCL_JAVA_DOWNLOAD_PAGE="https://docs.hmcl.net/downloads/macos/$_HMCL_ARCH.html"
|
||||
;;
|
||||
esac
|
||||
|
||||
|
||||
@@ -141,18 +141,20 @@ account.move_to_global=Convert to Global Account\nThe account information will b
|
||||
account.move_to_portable=Convert to Portable Account\nThe account information will be saved in a config file in the same directory as HMCL.
|
||||
account.not_logged_in=Not Logged in
|
||||
account.password=Password
|
||||
account.portable=Portable Account
|
||||
account.portable=Portable
|
||||
account.skin=Skin
|
||||
account.skin.file=Skin File
|
||||
account.skin.model=Model
|
||||
account.skin.model.default=Classic
|
||||
account.skin.model.slim=Slim
|
||||
account.skin.type.alex=Alex
|
||||
account.skin.type.csl_api=Blessing Skin
|
||||
account.skin.type.csl_api.location=Address
|
||||
account.skin.type.csl_api.location.hint=CustomSkinAPI URL
|
||||
account.skin.type.little_skin=LittleSkin
|
||||
account.skin.type.little_skin.hint=You need to create a player with the same player name as your offline account on your skin provider site. Your skin will now be set to the skin assigned to your player on the skin provider site.
|
||||
account.skin.type.local_file=Local Skin File
|
||||
account.skin.type.steve=Steve
|
||||
account.skin.upload=Upload/Edit Skin
|
||||
account.skin.upload.failed=Failed to upload skin.
|
||||
account.skin.invalid_skin=Invalid skin file.
|
||||
@@ -319,6 +321,8 @@ curse.sort.name=Name
|
||||
curse.sort.popularity=Popularity
|
||||
curse.sort.total_downloads=Total Downloads
|
||||
|
||||
datetime.format=MMM d, yyyy, h\:mm\:ss a
|
||||
|
||||
download=Download
|
||||
download.hint=Install games and modpacks or download mods, resource packs, and worlds.
|
||||
download.code.404=File "%s" not found on the remote server.
|
||||
@@ -846,6 +850,7 @@ modpack.completion=Downloading dependencies
|
||||
modpack.desc=Describe your modpack, including an introduction and probably some changelog. Markdown and images from URL are currently supported.
|
||||
modpack.description=Modpack Description
|
||||
modpack.download=Download Modpacks
|
||||
modpack.download.title=Download Modpack - %1s
|
||||
modpack.enter_name=Enter a name for this modpack.
|
||||
modpack.export=Export as Modpack
|
||||
modpack.export.as=Export Modpack As...
|
||||
@@ -1088,7 +1093,6 @@ world.name=World Name
|
||||
world.name.enter=Enter the world name
|
||||
world.reveal=Reveal in Explorer
|
||||
world.show_all=Show All
|
||||
world.time=h\:mm\:ss a, EEE, MMM d yyyy
|
||||
|
||||
profile=Game Directories
|
||||
profile.already_exists=This name already exists. Please use a different name.
|
||||
@@ -1204,6 +1208,9 @@ settings.advanced.wrapper_launcher.prompt=Allows launching using an extra wrappe
|
||||
settings.custom=Custom
|
||||
|
||||
settings.game=Settings
|
||||
settings.game.copy_global=Copy from Global Settings
|
||||
settings.game.copy_global.copy_all=Copy All
|
||||
settings.game.copy_global.copy_all.confirm=Are you sure you want to overwrite the current instance settings? This action cannot be undone!
|
||||
settings.game.current=Game
|
||||
settings.game.dimension=Resolution
|
||||
settings.game.exploration=Explore
|
||||
@@ -1253,7 +1260,7 @@ settings.launcher.proxy.http=HTTP
|
||||
settings.launcher.proxy.none=No Proxy
|
||||
settings.launcher.proxy.password=Password
|
||||
settings.launcher.proxy.port=Port
|
||||
settings.launcher.proxy.socks=Socks
|
||||
settings.launcher.proxy.socks=SOCKS
|
||||
settings.launcher.proxy.username=Username
|
||||
settings.launcher.theme=Theme
|
||||
settings.launcher.title_transparent=Transparent Titlebar
|
||||
@@ -1270,7 +1277,6 @@ settings.memory.lower_bound=Minimum Memory
|
||||
settings.memory.used_per_total=%1$.1f GB Used / %2$.1f GB Total
|
||||
settings.physical_memory=Physical Memory Size
|
||||
settings.show_log=Show Logs
|
||||
settings.skin=Skins for offline accounts are now supported. You can navigate to your account list to change your skin and cape, but other players cannot see it in multiplayer.
|
||||
settings.tabs.installers=Loaders
|
||||
settings.take_effect_after_restart=Applies After Restart
|
||||
settings.type=Settings Type of Instance
|
||||
|
||||
@@ -319,6 +319,8 @@ curse.sort.name=Nombre
|
||||
curse.sort.popularity=Popularidad
|
||||
curse.sort.total_downloads=Descargas totales
|
||||
|
||||
datetime.format=d MMM yyyy, H\:mm\:ss
|
||||
|
||||
download=Descargar
|
||||
download.hint=Instalar juegos y modpacks o descargar mods, paquetes de recursos y mundos.
|
||||
download.code.404=Archivo no encontrado en el servidor remoto: %s
|
||||
@@ -1035,7 +1037,6 @@ world.name=Nombre del mundo
|
||||
world.name.enter=Introducir el nombre del mundo
|
||||
world.reveal=Abrir en el Explorador
|
||||
world.show_all=Mostrar todo
|
||||
world.time=EEE, MMM d, yyyy HH\:mm\:ss
|
||||
|
||||
profile=Directorios del juego
|
||||
profile.already_exists=Este nombre ya existe, por favor, utilice un nombre diferente.
|
||||
@@ -1199,7 +1200,7 @@ settings.launcher.proxy.http=HTTP
|
||||
settings.launcher.proxy.none=Sin proxy
|
||||
settings.launcher.proxy.password=Contraseña
|
||||
settings.launcher.proxy.port=Puerto
|
||||
settings.launcher.proxy.socks=Socks
|
||||
settings.launcher.proxy.socks=SOCKS
|
||||
settings.launcher.proxy.username=Nombre de usuario
|
||||
settings.launcher.theme=Tema
|
||||
settings.launcher.title_transparent=Barra de título transparente
|
||||
@@ -1216,7 +1217,6 @@ settings.memory.lower_bound=Memoria mínima
|
||||
settings.memory.used_per_total=%1$.1f GB Utilizados / %2$.1f GB Totales
|
||||
settings.physical_memory=Tamaño de la memoria física
|
||||
settings.show_log=Mostrar registros
|
||||
settings.skin=Ahora se admiten los skins para las cuentas sin conexión. Puedes ir a la configuración de la cuenta para cambiar tu skin y tu capa, pero otros jugadores no podrán verlo en el multijugador.
|
||||
settings.tabs.installers=Cargadores
|
||||
settings.take_effect_after_restart=Se aplica después de reiniciar
|
||||
settings.type=Tipo de configuración de la instancia
|
||||
|
||||
@@ -283,6 +283,8 @@ curse.sort.name=名前
|
||||
curse.sort.popularity=人気
|
||||
curse.sort.total_downloads=合計ダウンロード数
|
||||
|
||||
datetime.format=yyyy/MM/dd H:mm:ss
|
||||
|
||||
download=ダウンロード
|
||||
download.hint=ゲームや modpack をインストールするか、mod、リソース パック、マップをダウンロードします
|
||||
download.code.404=リモートサーバーにファイルが見つかりません:%s
|
||||
@@ -717,7 +719,6 @@ world.name=マップ名
|
||||
world.name.enter=マップ名を入力してください
|
||||
world.reveal=フォルダを表示
|
||||
world.show_all=すべて表示
|
||||
world.time=EEE、MMM d、yyyy HH:mm:ss
|
||||
|
||||
profile=ゲームディレクトリ
|
||||
profile.already_exists=この名前はすでに存在します。別の名前を使用してください。
|
||||
@@ -857,7 +858,7 @@ settings.launcher.proxy.http=HTTP
|
||||
settings.launcher.proxy.none=プロキシなし
|
||||
settings.launcher.proxy.password=パスワード
|
||||
settings.launcher.proxy.port=ポート
|
||||
settings.launcher.proxy.socks=Socks
|
||||
settings.launcher.proxy.socks=SOCKS
|
||||
settings.launcher.proxy.username=アカウント
|
||||
settings.launcher.theme=テーマ
|
||||
settings.launcher.title_transparent=タイトルの透明性
|
||||
@@ -873,7 +874,6 @@ settings.memory.lower_bound=最小メモリ
|
||||
settings.memory.used_per_total=%1$.1f GB使用済み/%2$.1fGB合計
|
||||
settings.physical_memory=物理メモリサイズ
|
||||
settings.show_log=ログを表示
|
||||
settings.skin=オフラインアカウントのスキンの変更がサポートされるようになりました!アカウントページに移動して、今すぐ肌を変えてください!
|
||||
settings.tabs.installers=Installers
|
||||
settings.take_effect_after_restart=再起動後に有効になります
|
||||
settings.type=バージョン設定タイプ
|
||||
|
||||
@@ -50,33 +50,35 @@ about.open_source.statement=GPL v3 (https://github.com/HMCL-dev/HMCL)
|
||||
|
||||
account=Аккаунты
|
||||
account.cape=Плащ
|
||||
account.character=Персонаж
|
||||
account.choose=Выберите персонажа
|
||||
account.character=Игрок
|
||||
account.choose=Выберите игрока
|
||||
account.create=Добавить аккаунт
|
||||
account.create.microsoft=Добавить аккаунт Microsoft
|
||||
account.create.offline=Добавить автономный аккаунт
|
||||
account.create.authlibInjector=Создайте аккаунт authlib-injector
|
||||
account.create.offline=Добавить аккаунт в режиме офлайн
|
||||
account.create.authlibInjector=Добавить аккаунт authlib-injector
|
||||
account.email=Почта
|
||||
account.failed=Не удалось проверить аккаунт
|
||||
account.failed.character_deleted=Персонаж уже был удалён.
|
||||
account.failed=Не удалось обновить аккаунт.
|
||||
account.failed.character_deleted=Игрок уже был удален.
|
||||
account.failed.connect_authentication_server=Не удаётся связаться с серверами авторизации, проверьте свой интернет.
|
||||
account.failed.connect_injector_server=Не удаётся подключиться к серверу авторизации. Проверьте свой интернет и убедитесь, что вы ввели правильный URL-адрес.
|
||||
account.failed.injector_download_failure=Не удаётся скачать authlib-injector. Проверьте свой интернет или попробуйте переключиться на другое зеркало скачивания.
|
||||
account.failed.invalid_credentials=Неверный пароль или ограничена скорость, повторите попытку позже.
|
||||
account.failed.invalid_password=Неверный пароль
|
||||
account.failed.invalid_token=Попробуйте выйти из аккаунта и повторно войти введя пароль
|
||||
account.failed.invalid_token=Попробуйте выйти из аккаунта и повторно войти.
|
||||
account.failed.migration=Ваш аккаунт должен быть перенесён в Microsoft. Если вы уже сделали это, вам следует войти в свой перенесенный аккаунт Microsoft.
|
||||
account.failed.no_character=Аккаунт не имеет персонажей.
|
||||
account.failed.server_disconnected=Невозможно получить доступ к серверу авторизации, не удалось проверить информацию о аккаунте. \n\
|
||||
Вы можете выбрать «Повторить проверку аккаунта», чтобы попытаться снова. \n\
|
||||
Также можно выбрать «Пропустить проверку аккаунта», чтобы продолжить запуск игры, \n\
|
||||
но информация об аккаунте может быть не актуальной. Если срок действия информации \nо аккаунте истечёт, будет невозможно войти на сервер, который требует подтверждения \nаккаунта.
|
||||
account.failed.server_disconnected=Невозможно получить доступ к серверу авторизации, не удалось проверить информацию о аккаунте.\n\
|
||||
Вы можете выбрать «Повторить проверку аккаунта», чтобы попытаться снова.\n\
|
||||
Также можно выбрать «Пропустить проверку аккаунта», чтобы продолжить запуск игры,\n\
|
||||
но информация об аккаунте может быть не актуальной. Если срок действия информации\n\
|
||||
о аккаунте истечёт, будет невозможно войти на сервер, который требует подтверждения\n\
|
||||
аккаунта.
|
||||
account.failed.server_response_malformed=Неверный ответ сервера, видимо сервер авторизации не работает.
|
||||
account.failed.ssl=При подключении к серверу произошла ошибка SSL. Пожалуйста, попробуйте обновить Java.
|
||||
account.failed.wrong_account=Вы вошли в неверный аккаунт.
|
||||
account.hmcl.hint=Необходимо нажать на «Войти» и завершить процесс в открывшейся вкладке вашего браузера.
|
||||
# avoid too long sequence.
|
||||
account.injector.add=Новый сервер авторизации
|
||||
account.injector.empty=Ничего (Вы можете нажать на кнопку с плюсом справа, чтобы добавить)
|
||||
account.injector.empty=Ничего (Вы можете нажать кнопку «плюс», чтобы добавить еще)
|
||||
account.injector.http=Предупреждение\: Этот сервер использует небезопасный протокол HTTP, и любой игрок, находящийся между вашими соединениями, сможет увидеть ваши учётные данные в открытом виде.
|
||||
account.injector.link.homepage=Домашняя страница
|
||||
account.injector.link.register=Зарегистрироваться
|
||||
@@ -85,53 +87,78 @@ account.injector.server_url=URL-адрес сервера
|
||||
account.injector.server_name=Имя сервера
|
||||
account.login=Войти
|
||||
account.login.hint=Мы не храним ваш пароль.
|
||||
account.login.refresh=Повторный вход
|
||||
account.login.refresh.microsoft.hint=Поскольку авторизация учетной записи недействительна, вам необходимо повторно добавить свою учетную запись Microsoft
|
||||
account.login.skip=Войти в режиме офлайн
|
||||
account.login.retry=Повторить снова
|
||||
account.login.refresh=Войти снова
|
||||
account.login.refresh.microsoft.hint=Вам необходимо снова войти в аккаунт Microsoft, поскольку авторизация аккаунта недействительна.
|
||||
account.logout=Выйти
|
||||
account.register=Зарегестрироваться
|
||||
account.manage=Список аккаунтов
|
||||
account.copy_uuid=Копирование UUID
|
||||
account.methods=Способ входа
|
||||
account.methods.authlib_injector=authlib-injector
|
||||
account.methods.microsoft=Аккаунт Microsoft
|
||||
account.methods.microsoft.birth=Как изменить настройки дня рождения...
|
||||
account.methods.microsoft.deauthorize=Отменить авторизацию аккаунта
|
||||
account.methods.microsoft=Microsoft
|
||||
account.methods.microsoft.birth=Как изменить настройки даты рождения...
|
||||
account.methods.microsoft.close_page=Авторизация аккаунта Microsoft завершена. Осталось выполнить некоторые шаги, которые необходимо завершить позже. Вы можете закрыть эту страницу.
|
||||
account.methods.microsoft.deauthorize=Отменить авторизацию аккаунта
|
||||
account.methods.microsoft.error.add_family=Поскольку тебе ещё нет 18 лет, взрослый должен добавить тебя в семейный доступ, чтобы ты мог играть в Minecraft.
|
||||
account.methods.microsoft.error.add_family_probably=Проверьте в настройках своего аккаунта, исполнилось ли вам уже 18 лет или нет. Если нет, необходимо добавить семейный аккаунт.
|
||||
account.methods.microsoft.error.add_family_probably=Проверьте, что возраст, указанный в настройках вашего аккаунта, составляет не менее 18 лет. Если нет и вы считаете, что это ошибка, вы можете нажать «Как изменить настройки даты рождения...», чтобы узнать, как ее изменить.
|
||||
account.methods.microsoft.error.country_unavailable=Xbox Live недоступен в вашей стране/регионе.
|
||||
account.methods.microsoft.error.missing_xbox_account=Ваш аккаунт Microsoft не подключена к Xbox. Создайте его, прежде чем продолжить.
|
||||
account.methods.microsoft.error.no_character=На аккаунте отсутствует профиль Minecraft Java.\nФайл игры не может быть создан, пожалуйста,\nнажмите на ссылку выше, чтобы создать его
|
||||
account.methods.microsoft.error.no_character=Убедитесь, что вы приобрели Minecraft: Java Edition.\n\
|
||||
Если вы уже приобрели игру, профиль может быть еще не создан.\n\
|
||||
Нажмите «Создать профиль / Редактировать имя профиля», чтобы создать его.
|
||||
account.methods.microsoft.error.unknown=Не удалось войти. Microsoft отвечает с кодом ошибки %d.
|
||||
account.methods.microsoft.error.wrong_verify_method=Пожалуйста, войдите в систему, используя свой пароль на странице входа в аккаунт Microsoft, и не используйте проверочный код для авторизации.
|
||||
account.methods.microsoft.logging_in=Авторизация...
|
||||
account.methods.microsoft.makegameidsettings=Создать архив / изменение имени профиля
|
||||
account.methods.microsoft.hint=Вам необходимо нажать кнопку «Войти» и продолжить процесс авторизации во вновь открывшемся окне браузера.\nЕсли токен, используемый для входа в аккаунт Microsoft случайно утёк, вы можете нажать «Отменить авторизацию аккаунта» ниже, чтобы отменить авторизацию.\nЕсли у вас возникнут какие-либо проблемы, вы можете нажать кнопку справки в правом верхнем углу для получения помощи.
|
||||
account.methods.microsoft.manual=После нажатия кнопки «Войти», вы должны завершить авторизацию во вновь открывшемся окне браузера. Если окно браузера не открылось, вы можете щёлкнуть здесь, чтобы скопировать ссылку и вручную открыть её в браузере.\nЕсли токен, используемый для входа в аккаунт Microsoft случайно утёк, вы можете нажать «Отменить авторизацию аккаунта» ниже, чтобы отменить авторизацию.\n問題が発生した場合は、右上隅にあるヘルプ ボタンをクリックするとヘルプが表示されます。
|
||||
account.methods.microsoft.hint=Вам необходимо нажать кнопку «Войти» и продолжить процесс авторизации во вновь открывшемся окне браузера.\n\
|
||||
\n\
|
||||
Если токен, используемый для входа в аккаунт Microsoft случайно утёк, вы можете нажать «Отменить авторизацию аккаунта» ниже, чтобы отменить авторизацию.
|
||||
account.methods.microsoft.manual=Ваш код устройства - <b>%1$s</b>. Нажмите здесь, чтобы скопировать.\n\
|
||||
\n\
|
||||
После нажатия кнопки «Войти», вы должны завершить авторизацию во вновь открывшемся окне браузера. Если окно браузера не открылось, вы можете щёлкнуть здесь, чтобы скопировать ссылку и вручную открыть её в браузере.\n\
|
||||
\n\
|
||||
Если токен, используемый для входа в аккаунт Microsoft случайно утёк, вы можете нажать «Отменить авторизацию аккаунта» ниже, чтобы отменить авторизацию.
|
||||
account.methods.microsoft.makegameidsettings=Создать / редактировать профиль
|
||||
account.methods.microsoft.profile=Профиль аккаунта...
|
||||
account.methods.microsoft.purchase=Купить Minecraft
|
||||
account.methods.forgot_password=ЗАБЫЛ ПАРОЛЬ
|
||||
account.methods.microsoft.snapshot=Вы используете неофициальное построение HMCL, загрузите официальное построение для входа в Microsoft.
|
||||
account.methods.microsoft.snapshot.website=Официальный сайт
|
||||
account.methods.offline=Офлайн
|
||||
account.methods.offline.name.special_characters=Рекомендуется использовать только английские буквы, цифры и символы подчёркивания.
|
||||
account.methods.offline.name.invalid=Как правило, игровые имена пользователя ограничиваются английскими буквами, цифрами и символами подчёркивания и не могут превышать 16 символов.\n\
|
||||
\n\
|
||||
\ · Действительный: HuangYu, huang_Yu, Huang_Yu_123;\n\
|
||||
\ · Недействительно: Huang Yu, Huang-Yu_%%%, Huang_Yu_hello_world_hello_world.\n\
|
||||
\n\
|
||||
Если вы считаете, что на стороне сервера есть соответствующий мод или плагин, снимающий это ограничение, вы можете проигнорировать это предупреждение.
|
||||
account.methods.offline.uuid=UUID
|
||||
account.methods.offline.uuid.hint=UUID - это уникальный идентификатор игрового персонажа в Minecraft. Способ генерации UUID различается в разных лаунчерах игр. Изменение UUID на тот, который генерируется другим лаунчером гарантирует, что игровые блоки/предметы в рюкзаке вашего офлайн аккаунта останутся. Этот параметр предназначен для экспертов. Если вы не знаете что делаете, мы не советуем вам изменять этот параметр.\nЭта опция не требуется для присоединения к серверам.
|
||||
account.methods.offline.uuid.malformed=Malformed
|
||||
account.methods.offline.uuid.hint=UUID - это уникальный идентификатор игрового персонажа в Minecraft. Способ генерации UUID различается в разных лаунчерах игр. Изменение UUID на тот, который генерируется другим лаунчером гарантирует, что игровые блоки/предметы в рюкзаке вашего офлайн аккаунта останутся. Этот параметр предназначен для экспертов. Если вы не знаете что делаете, мы не советуем вам изменять этот параметр. Эта опция не требуется для присоединения к серверам.
|
||||
account.methods.offline.uuid.malformed=Недопустимый формат.
|
||||
account.methods.forgot_password=Забыли пароль
|
||||
account.missing=Нет аккаунтов
|
||||
account.missing.add=Нажмите здесь, чтобы добавить.
|
||||
account.move_to_global=Конвертировать в глобальный аккаунт\nИнформация об аккаунте будет сохранена в файле конфигурации в каталоге текущего пользователя системы.
|
||||
account.move_to_portable=Конвертировать в портативный аккаунт\nИнформация об аккаунте будет сохранена в файле конфигурации в той же папке, что и HMCL.
|
||||
account.not_logged_in=Вход не произведён
|
||||
account.password=Пароль
|
||||
account.portable=Портативный
|
||||
account.skin=Скин
|
||||
account.skin.file=Файл скина
|
||||
account.skin.model=Модель
|
||||
account.skin.model.default=Классическая
|
||||
account.skin.model.slim=Стройная
|
||||
account.skin.type.alex=Алекс
|
||||
account.skin.type.csl_api=Blessing Skin
|
||||
account.skin.type.csl_api.location=Адрес
|
||||
account.skin.type.csl_api.location.hint=URL-адрес CustomSkinAPI
|
||||
account.skin.type.little_skin=LittleSkin
|
||||
account.skin.type.little_skin.hint=Необходимо создать персонажа с тем же именем, что и на автономном аккаунте на сайте поставщика скинов. Затем ваш скин будет вашим загруженным скином на этот сайт.
|
||||
account.skin.type.little_skin.hint=Вам нужно создать игрока с тем же именем, что и у вашего офлайн-аукнта на сайте поставщика скинов. Теперь ваш скин будет установлен на скин, назначенный вашему игроку на сайте поставщика скинов.
|
||||
account.skin.type.local_file=Локальный файл скина
|
||||
account.skin.type.steve=Стив
|
||||
account.skin.upload=Загрузить скин
|
||||
account.skin.upload.failed=Не удаётся загрузить скин
|
||||
account.skin.invalid_skin=Недопустимый файл скина
|
||||
account.skin.upload.failed=Не удаётся загрузить скин.
|
||||
account.skin.invalid_skin=Недопустимый файл скина.
|
||||
account.username=Имя пользователя
|
||||
|
||||
archive.author=Автор(ы)
|
||||
@@ -295,6 +322,8 @@ curse.sort.name=Название
|
||||
curse.sort.popularity=Популярность
|
||||
curse.sort.total_downloads=Скачиваний
|
||||
|
||||
datetime.format=d MMM yyyy г., HH\:mm\:ss
|
||||
|
||||
download=Скачать
|
||||
download.hint=Установите игры и пакеты модов или загрузите моды, пакеты ресурсов и карты
|
||||
download.code.404=Файл не найден на удалённом сервере: %s
|
||||
@@ -776,7 +805,6 @@ world.name=Название мира
|
||||
world.name.enter=Введите название мира
|
||||
world.reveal=Показать в проводнике
|
||||
world.show_all=Показать все
|
||||
world.time=EEE, MMM d, yyyy HH\:mm\:ss
|
||||
|
||||
profile=Папки с играми
|
||||
profile.already_exists=Имя уже существует, используйте другое имя.
|
||||
@@ -942,7 +970,6 @@ settings.memory.lower_bound=Мин. объём памяти
|
||||
settings.memory.used_per_total=Использовано %1$.1f ГБ / Всего %2$.1f ГБ
|
||||
settings.physical_memory=Размер физической памяти
|
||||
settings.show_log=Показать логи
|
||||
settings.skin=Теперь поддерживаются скины для автономных аккаунтов. Можно зайти в настройки аккаунта, чтобы изменить свой скин и плащ, но другие игроки не смогут увидеть их в мультиплеере.
|
||||
settings.tabs.installers=Загрузчик модов/OptiFine
|
||||
settings.take_effect_after_restart=Применится после перезапуска
|
||||
settings.type=Тип настроек экземпляра
|
||||
|
||||
@@ -148,12 +148,14 @@ account.skin.file=外觀圖片檔案
|
||||
account.skin.model=模型
|
||||
account.skin.model.default=寬型
|
||||
account.skin.model.slim=纖細
|
||||
account.skin.type.alex=Alex
|
||||
account.skin.type.csl_api=Blessing Skin 伺服器
|
||||
account.skin.type.csl_api.location=伺服器位址
|
||||
account.skin.type.csl_api.location.hint=CustomSkinAPI 位址
|
||||
account.skin.type.little_skin=LittleSkin 皮膚站
|
||||
account.skin.type.little_skin.hint=你需要在皮膚站中加入並使用和該離線帳戶同名角色,此時離線帳戶外觀將為皮膚站上對應角色所設定的外觀。
|
||||
account.skin.type.local_file=本機外觀圖片檔案
|
||||
account.skin.type.steve=Steve
|
||||
account.skin.upload=上傳/編輯外觀
|
||||
account.skin.upload.failed=外觀上傳失敗
|
||||
account.skin.invalid_skin=無法識別的外觀檔案
|
||||
@@ -319,6 +321,8 @@ curse.sort.name=名稱
|
||||
curse.sort.popularity=熱度
|
||||
curse.sort.total_downloads=下載量
|
||||
|
||||
datetime.format=yyyy 年 MM 月 dd 日 HH:mm:ss
|
||||
|
||||
download=下載
|
||||
download.hint=安裝遊戲和模組包或下載模組、資源包和地圖
|
||||
download.code.404=遠端伺服器沒有需要下載的檔案:%s
|
||||
@@ -628,7 +632,7 @@ logwindow.terminate_game=結束遊戲處理程式
|
||||
logwindow.title=日誌
|
||||
logwindow.help=你可以前往 HMCL 社群,向他人尋求幫助
|
||||
logwindow.autoscroll=自動滾動
|
||||
logwindow.export_game_crash_logs=匯出遊戲崩潰訊息
|
||||
logwindow.export_game_crash_logs=匯出遊戲崩潰資訊
|
||||
logwindow.export_dump=匯出遊戲執行堆疊
|
||||
logwindow.export_dump.no_dependency=你的 Java 不包含用於建立遊戲執行堆疊的相依元件。請前往 Discord 或 HMCL QQ 群尋求幫助。
|
||||
|
||||
@@ -660,6 +664,7 @@ modpack.completion=下載模組包相關檔案
|
||||
modpack.desc=描述你要製作的模組包,比如模組包注意事項和更新紀錄,支援 Markdown (圖片請上傳至網路)。
|
||||
modpack.description=模組包描述
|
||||
modpack.download=下載模組包
|
||||
modpack.download.title=模組包下載 - %1s
|
||||
modpack.enter_name=給遊戲取個你喜歡的名稱
|
||||
modpack.export=匯出模組包
|
||||
modpack.export.as=請選取模組包類型。若你無法決定,請選取 MCBBS 類型。
|
||||
@@ -892,7 +897,6 @@ world.name=世界名稱
|
||||
world.name.enter=輸入世界名稱
|
||||
world.reveal=開啟目錄
|
||||
world.show_all=全部顯示
|
||||
world.time=yyyy 年 M 月 d 日, HH:mm:ss
|
||||
|
||||
profile=遊戲目錄
|
||||
profile.already_exists=該名稱已存在
|
||||
@@ -976,7 +980,7 @@ settings.advanced.natives_directory.hint=本選項提供給 Apple M1 等未受
|
||||
settings.advanced.no_jvm_args=不加入預設的 JVM 參數
|
||||
settings.advanced.precall_command=遊戲啟動前執行指令
|
||||
settings.advanced.precall_command.prompt=將在遊戲啟動前呼叫使用
|
||||
settings.advanced.process_priority=處理程序優先度
|
||||
settings.advanced.process_priority=處理程式優先度
|
||||
settings.advanced.process_priority.low=低 (節省遊戲占用資源,可能會造成遊戲卡頓)
|
||||
settings.advanced.process_priority.below_normal=較低 (節省遊戲占用資源,可能會造成遊戲卡頓)
|
||||
settings.advanced.process_priority.normal=中 (平衡)
|
||||
@@ -1001,6 +1005,9 @@ settings.advanced.wrapper_launcher.prompt=如填寫「optirun」後,啟動指
|
||||
settings.custom=自訂
|
||||
|
||||
settings.game=遊戲設定
|
||||
settings.game.copy_global=複製全域遊戲設定
|
||||
settings.game.copy_global.copy_all=複製全部
|
||||
settings.game.copy_global.copy_all.confirm=你確定要覆寫目前實例特定遊戲設定嗎?該操作無法復原!
|
||||
settings.game.current=遊戲
|
||||
settings.game.dimension=遊戲介面解析度大小
|
||||
settings.game.exploration=瀏覽
|
||||
@@ -1048,7 +1055,7 @@ settings.launcher.proxy.http=HTTP
|
||||
settings.launcher.proxy.none=不使用代理
|
||||
settings.launcher.proxy.password=密碼
|
||||
settings.launcher.proxy.port=連線埠
|
||||
settings.launcher.proxy.socks=Socks
|
||||
settings.launcher.proxy.socks=SOCKS
|
||||
settings.launcher.proxy.username=帳戶
|
||||
settings.launcher.theme=主題
|
||||
settings.launcher.title_transparent=標題欄透明
|
||||
@@ -1065,7 +1072,6 @@ settings.memory.lower_bound=最低分配
|
||||
settings.memory.used_per_total=已使用 %1$.1f GB / 總記憶體 %2$.1f GB
|
||||
settings.physical_memory=實體記憶體大小
|
||||
settings.show_log=查看日誌
|
||||
settings.skin=現已支援離線帳戶更換外觀,你可以到帳戶頁面更改離線帳戶的外觀和披風 (多人遊戲下其他玩家無法看到你的外觀)。
|
||||
settings.tabs.installers=自動安裝
|
||||
settings.take_effect_after_restart=重啟後生效
|
||||
settings.type=實例遊戲設定類型
|
||||
|
||||
@@ -157,12 +157,14 @@ account.skin.file=皮肤图片文件
|
||||
account.skin.model=模型
|
||||
account.skin.model.default=宽型
|
||||
account.skin.model.slim=纤细
|
||||
account.skin.type.alex=Alex
|
||||
account.skin.type.csl_api=Blessing Skin 服务器
|
||||
account.skin.type.csl_api.location=服务器地址
|
||||
account.skin.type.csl_api.location.hint=CustomSkinAPI 地址
|
||||
account.skin.type.little_skin=LittleSkin 皮肤站
|
||||
account.skin.type.little_skin.hint=你需要在皮肤站中创建并使用和该离线账户同名的角色,此时离线账户皮肤将显示为皮肤站上对应角色所设置的皮肤。\n你可以点击右上角帮助按钮进行求助。
|
||||
account.skin.type.local_file=本地皮肤图片文件
|
||||
account.skin.type.steve=Steve
|
||||
account.skin.upload=上传/编辑皮肤
|
||||
account.skin.upload.failed=皮肤上传失败
|
||||
account.skin.invalid_skin=无法识别的皮肤文件
|
||||
@@ -328,6 +330,8 @@ curse.sort.name=名称
|
||||
curse.sort.popularity=热度
|
||||
curse.sort.total_downloads=下载量
|
||||
|
||||
datetime.format=yyyy 年 MM 月 dd 日 HH:mm:ss
|
||||
|
||||
download=下载
|
||||
download.hint=安装游戏和整合包或下载模组、资源包和地图
|
||||
download.code.404=远程服务器不包含需要下载的文件: %s\n你可以点击右上角帮助按钮进行求助。
|
||||
@@ -671,6 +675,7 @@ modpack.completion=下载整合包相关文件
|
||||
modpack.desc=描述你要制作的整合包,比如整合包注意事项和更新记录,支持 Markdown (图片请用网络链接)
|
||||
modpack.description=整合包描述
|
||||
modpack.download=下载整合包
|
||||
modpack.download.title=整合包下载 - %1s
|
||||
modpack.enter_name=给游戏起个你喜欢的名字
|
||||
modpack.export=导出整合包
|
||||
modpack.export.as=请选择整合包类型 (若无法决定,请选择“我的世界中文论坛整合包标准”)
|
||||
@@ -903,7 +908,6 @@ world.name=世界名称
|
||||
world.name.enter=输入世界名称
|
||||
world.reveal=打开文件夹
|
||||
world.show_all=显示全部
|
||||
world.time=yyyy 年 MM 月 dd 日, HH:mm:ss
|
||||
|
||||
profile=游戏文件夹
|
||||
profile.already_exists=该名称已存在
|
||||
@@ -1012,6 +1016,9 @@ settings.advanced.wrapper_launcher.prompt=如填写“optirun”后,启动命
|
||||
settings.custom=自定义
|
||||
|
||||
settings.game=游戏设置
|
||||
settings.game.copy_global=复制全局游戏设置
|
||||
settings.game.copy_global.copy_all=复制全部
|
||||
settings.game.copy_global.copy_all.confirm=你确定要覆盖当前版本特定游戏设置吗?此操作无法撤销!
|
||||
settings.game.current=游戏
|
||||
settings.game.dimension=游戏窗口分辨率
|
||||
settings.game.exploration=浏览
|
||||
@@ -1059,7 +1066,7 @@ settings.launcher.proxy.http=HTTP
|
||||
settings.launcher.proxy.none=不使用代理
|
||||
settings.launcher.proxy.password=密码
|
||||
settings.launcher.proxy.port=端口
|
||||
settings.launcher.proxy.socks=Socks
|
||||
settings.launcher.proxy.socks=SOCKS
|
||||
settings.launcher.proxy.username=账户
|
||||
settings.launcher.theme=主题
|
||||
settings.launcher.title_transparent=标题栏透明
|
||||
@@ -1076,7 +1083,6 @@ settings.memory.lower_bound=最低内存分配
|
||||
settings.memory.used_per_total=设备中已使用 %1$.1f GB / 设备总内存 %2$.1f GB
|
||||
settings.physical_memory=物理内存大小
|
||||
settings.show_log=查看日志
|
||||
settings.skin=现已支持离线账户更换皮肤,你可以到账户页面更改离线账户的皮肤和披风 (多人游戏下其他玩家无法看到你的皮肤)。
|
||||
settings.tabs.installers=自动安装
|
||||
settings.take_effect_after_restart=重启后生效
|
||||
settings.type=版本游戏设置类型
|
||||
|
||||
+5177
-1143
File diff suppressed because it is too large.
Load diff
@@ -1,9 +1,9 @@
|
||||
#
|
||||
# Hello Minecraft! Launcher
|
||||
# Copyright (C) 2024 huangyuhui <huanghongxun2008@126.com> and contributors
|
||||
# Copyright (C) 2025 huangyuhui <huanghongxun2008@126.com> and contributors
|
||||
#
|
||||
# mcmod.cn
|
||||
# Copyright (C) 2024. All Rights Reserved.
|
||||
# Copyright (C) 2025. All Rights Reserved.
|
||||
#
|
||||
gt-new-horizons;1;;格雷科技:新视野;GT: New Horizons;GTNH
|
||||
enigtech-2;2;;玄理2;EnigTech 2;ET2
|
||||
@@ -39,7 +39,7 @@ modern-skyblock-3-departed;31;;现代空岛3;Modern Skyblock 3: Departed;
|
||||
gregblock;32;;格雷空岛;Gregblock;
|
||||
sevtech-ages;33;;赛文科技;SevTech: Ages;STA
|
||||
;34;;伊卡洛斯号迫降;Icarus Landing;IL
|
||||
greedycraft;35;;贪婪整合包;GreedyCraft;
|
||||
greedycraft;35;;贪婪整合包;GreedyCraft;GC
|
||||
stoneblock;36;;StoneBlock;;
|
||||
ftb-presents-skyfactory-3;37;;天空工厂3;FTB Presents SkyFactory 3;SF3
|
||||
space-astronomy;38;;Space Astronomy;;
|
||||
@@ -142,7 +142,7 @@ awakening-sky-of-diamonds;135;;Awakening - Sky of Diamonds;;
|
||||
ragnamod-v;136;;Ragnamod V;;
|
||||
all-the-mods-3-expert;137;;All the Mods 3 专家版;All the Mods 3 Expert;ATM3E
|
||||
seablock-rustic-waters;138;;乡村水域;Seablock: Rustic Waters;
|
||||
engineers-life;139;;Engineer's Life;;
|
||||
engineers-life;139;;Engineer's Life;;EL
|
||||
star-factory;140;;Star Factory;;
|
||||
lunar-laboratory;141;;Lunar Laboratory;;
|
||||
glacial-awakening;142;;Glacial Awakening;;
|
||||
@@ -170,7 +170,7 @@ all-the-mods-6;164;;All the Mods 6;;ATM6
|
||||
garden-of-glass-questbook-edition;165;;Garden of Glass (Questbook Edition);;
|
||||
;166;;圣剑传奇;Sword Legendary;
|
||||
;167;;FTB Presents Direwolf20 1.16;;
|
||||
team-rustic;168;;Team Rustic;;TR
|
||||
team-rustic;168;;Rusty Sink 1.20;;
|
||||
enigmatica2expertskyblock;169;;Enigmatica 2: Expert Skyblock;;E2ES
|
||||
;170;;休闲-生存-2021版;Leisure Survival-2021;ls21
|
||||
hexxit-updated;171;;Hexxit Updated;;
|
||||
@@ -217,7 +217,7 @@ airaka-tech;211;;艾瑞卡科技;Airaka Tech;
|
||||
originssmp;213;;起源SMP;OriginsSMP;
|
||||
create-live-2;214;;Create Live 2;;
|
||||
origin-smp-copy;215;;Origin - SMP;;
|
||||
;216;;逆迫降;INUERSE FORCED LANDING;
|
||||
;216;;逆迫降;INVERSE FORCED LANDING;
|
||||
avatar-the-four-elements;217;;Avatar: The Four Elements;;
|
||||
seaopolis;218;;Seaopolis;;
|
||||
zombie-apocalypse-remastered;219;;僵尸启示录:重制;Zombie Apocalypse Remastered;ZAR
|
||||
@@ -253,9 +253,9 @@ evolution-reset;250;;进化:重启;Evolution: Reset;ER
|
||||
the-nuclear-wasteland;251;;核荒原;The Nuclear Wasteland;
|
||||
blessed-or-cursed-expedition-to-infinite-force;252;;祝福或诅咒:无限力量之征;Blessed or Cursed: Expedition to Infinite Force;BOC
|
||||
;253;;边界生存;Border Survival;BSV
|
||||
medieval-mc-fabric;255;;Medieval Minecraft;;
|
||||
medieval-mc-fabric;255;;Medieval Minecraft;;MMC
|
||||
heavens-of-sorcery;256;;巫术天堂;Heavens of Sorcery;
|
||||
herodotus;257;;希罗多德;Herodotus;HDS
|
||||
herodotus;257;;希罗多德;Herodotus-Authentic Edition:Official;HDS
|
||||
;258;;重生世界;Rebirth world;RW
|
||||
permafrost-eots;259;;永久冻土:风暴之眼;Permafrost - Eyes of the Storm;
|
||||
ultimate-alchemy;260;;终极炼金;Ultimate Alchemy;
|
||||
@@ -269,7 +269,7 @@ the-kingdom-of-daldar-forge-labs;267;;The Kingdom of Daldar - Forge Labs;;
|
||||
demon-slayers-unleashed;268;;Demon Slayers Unleashed;;
|
||||
valhelsia-enhanced-vanilla;269;;Valhelsia: Enhanced Vanilla;;
|
||||
;270;;FTB University 1.16;;
|
||||
thaumic-renaissance-kedition;271;;神秘复兴;Thaumic Renaissance Kedition;
|
||||
thaumic-renaissance-kedition;271;;神秘复兴;Thaumic Renaissance / Thaumic Renaissance - Kedition;
|
||||
the-winter-rescue;273;;冬季救援;The Winter Rescue;TWR
|
||||
;274;;飞翔之路;Flying Road;
|
||||
forever-stranded-lost-souls;275;;Forever Stranded Lost Souls;;
|
||||
@@ -281,11 +281,11 @@ manufactio;281;;Manufactio;;
|
||||
world-of-dragons-ii;282;;World of Dragons II;;
|
||||
tensura-unleashed;283;;Tensura Unleashed;;
|
||||
skyopolis-4;284;;Skyopolis 4;;
|
||||
;286;;FTB OceanBlock;;
|
||||
ftb-oceanblock;286;;FTB OceanBlock;;
|
||||
tolkiencraft-iii-return-to-middle-earth;287;;TolkienCraft III - Return to Middle-earth;;
|
||||
delivery-inc;288;;Delivery Inc.;;
|
||||
poetica;289;;Poetica;;
|
||||
divine-journey-2;290;;Divine Journey 2;;DJ2
|
||||
divine-journey-2;290;;神圣之旅2;Divine Journey 2;DJ2
|
||||
all-the-mods-7;291;;All the Mods 7;;ATM7
|
||||
skyfactory-one;292;;SkyFactory One;;
|
||||
feed-the-singularity;293;;喂养奇点生存;Feed the Singularity;
|
||||
@@ -374,12 +374,11 @@ jurassic-world-reborn;376;;Jurassic World Reborn;;
|
||||
;377;;FTB Omnia;;
|
||||
fantazy-tech-expert-mode;378;;幻想科技;Fantazy Tech;
|
||||
crainer-craft-2;379;;Crainer Craft 2;;
|
||||
;380;;All The Forge 8;;
|
||||
all-the-forge-8;380;;All The Forge 8;;
|
||||
colony-space-modpack;381;;KN: Colony New Worlds;;
|
||||
skyopolis-3;382;;Skyopolis 3;;
|
||||
aboe-3;383;;A Bit of Everything 3;;
|
||||
aboe-3;383;;A Bit of Everything 3;;ABOE3
|
||||
sky-alchemist;384;;天空炼金术士;Sky Alchemist;
|
||||
epicadventure;385;;奥德赛;OdysseyCraft;OC
|
||||
;386;;字字落实;;
|
||||
nuclear-earth-reirradiated;387;;核地球:再辐照;Nuclear Earth: Reirradiated;
|
||||
god-block;388;;God Block;;
|
||||
@@ -392,7 +391,7 @@ gregtech-community-pack;393;;GregTech Community Pack;;GCP
|
||||
create-yourself;395;;Create Yourself;;
|
||||
starrynight-pray-stp;397;;星夜祈临;StarryNight Pray;StP
|
||||
rustic-waters-ii;398;;乡村水域2;Rustic Waters II;
|
||||
enigmatica6expert;399;;Enigmatica 6 Expert;;E6E
|
||||
enigmatica6expert;399;;Enigmatica 6: Expert;;E6E
|
||||
celestial-journey;400;;Celestial Journey;;
|
||||
;401;;天空舞台;Skyblock Create Show;
|
||||
project-architect;402;;Project Architect;;
|
||||
@@ -406,10 +405,10 @@ kimetsu-no-yaiba-demon-slayer-mod-pack;409;;鬼灭之刃;Kimetsu no Yaiba;
|
||||
;410;;七曜之际;FOR A WEEK;
|
||||
not-too-complicated;411;;Not Too Complicated;;NTC
|
||||
fiefdom;412;;列土封疆;Fiefdom;
|
||||
reinforce-reality;413;;死亡工艺&重生;Deathcraft & Rebirth;
|
||||
reinforce-reality;413;;死亡工艺&重生;Deathcraft & Rebirth / Reinforce Reality;
|
||||
afterlife;414;;The Afterlife;;
|
||||
create-skyblock;415;;CREATE Skyblock;;
|
||||
lightcraftproject;416;;Light Craft;;
|
||||
lightcraftprojectlite;416;;Light Craft;;
|
||||
epoch-runner-formerly-nuclear-beyond-modpack;417;;时代领跑人;Epoch Runner;
|
||||
;418;;星海征途;The Journey of Star Sea;TJoSS
|
||||
roguelike-adventures-and-dungeons-2;419;;冒险与地牢2;Roguelike Adventures and Dungeons 2;RAD2
|
||||
@@ -422,7 +421,7 @@ magnificia;423;;Magnificia;;
|
||||
chroma-sky-2;426;;色度空岛2;Chroma Sky 2;
|
||||
infinity-fabric;427;;无限;Infinity;IF
|
||||
enigmatica-2-expert-extended;428;;Enigmatica 2: Expert - Extended;;E2E-E
|
||||
;429;;水星迫降重制版;Mercury landing;
|
||||
mercury-landing;429;;水星迫降重制版;Mercury landing;
|
||||
magic-sky;430;;Magic Sky;;
|
||||
;431;;科技飞升;Rise of Tech;RoT
|
||||
not-too-complicated-2;432;;Not Too Complicated 2;;
|
||||
@@ -448,7 +447,7 @@ lcw;453;;失落文明:野原;Lost_Civilization:Wilds;LCW
|
||||
promo-cinematic;454;;Minecraft trailer recreation | Promo-Cinematic Modpack;;
|
||||
the-last-one;455;;最后一人;The Last One;TLO
|
||||
;456;;帕斯特之梦;PasterDream;PD
|
||||
;457;;失落的米德加尔特;Lost Midgard;LM
|
||||
lost-midgard;457;;失落的米德加尔特;Lost Midgard;LM
|
||||
;458;;真实空岛;Real Island;RI
|
||||
abyss-hunter;459;;深渊猎人;Abyss Hunter;AH
|
||||
blackstone-block;460;;Blackstone Block;;
|
||||
@@ -458,14 +457,14 @@ mcdoom-modpack;463;;MCDoom Modpack;;
|
||||
poko;464;;Poko;;
|
||||
;465;;永无止境:重缝;Cabricality;CABF
|
||||
aperomods;466;;Aperomods;;APM
|
||||
;467;;XPlus 2.0 基础整合;XPlus Modpack;X+
|
||||
;467;;XPlus 2.0 基础整合;XPlus 2.0 Modpack;X+
|
||||
crazy-crave-5;468;;Crazy Crave 5;;
|
||||
sky-research;469;;Sky Research;;
|
||||
;470;;魔力做功;Work Done by Mana;WDM
|
||||
;471;;Beyond Reality;;
|
||||
slcp4;472;;微笑草坪4;SmileLawn 4;SLCP4
|
||||
crazy-craft-updated;473;;Crazy Craft Updated;;
|
||||
planetary;474;;Planetary;;
|
||||
planetary;474;;行星;Planetary;
|
||||
slcp5;475;;微笑草坪5;SmileLawn 5;SLCP5
|
||||
;476;;寂静传说;tales of silent;TOS
|
||||
Wightcraft;477;;Wightcraft;;
|
||||
@@ -481,7 +480,7 @@ jetpack-cat;482;;喷气背包猫;Jetpack Cat;
|
||||
mineshafts-monsters-lite;487;;Mineshafts & Monsters - LITE;;
|
||||
;488;;AELife;;AEL
|
||||
mineshafts-monsters-adventure;489;;Mineshafts & Monsters - Adventure;;
|
||||
dimensionhopper;490;;DimensionHopper;;
|
||||
dimensionhopper;490;;Dimension Hopper:The Fall;;
|
||||
technology-empty-island;491;;科技空岛;RxanGrben;
|
||||
greed-the-resource-4-infinity;492;;Greed the Resource 4 - Infinity;;
|
||||
caveopolis;493;;Caveopolis;;
|
||||
@@ -500,8 +499,8 @@ other-body;504;;Other Body;;
|
||||
;506;;莱姆星云;Lime Nebula;LN
|
||||
void-genesis;507;;Void Genesis;;
|
||||
create-flavored;508;;Create Flavored;;
|
||||
terrafirmagreg;509;;TerraFirmaGreg;;TFG
|
||||
create-astral;510;;Create: Astral;;
|
||||
terrafirmagreg-vintage;509;;TerraFirmaGreg;;TFG
|
||||
create-astral;510;;机械动力:星辰;Create: Astral;
|
||||
;511;;起源创造;CreateOrigin;CO
|
||||
random-item-skyblock-recondite-i;512;;Random Item Island - Recondite I;;
|
||||
infinity-foundation;513;;Infinity Foundation;;
|
||||
@@ -517,7 +516,7 @@ tankoptimization;521;;坦坦优化;Tankoptimization;
|
||||
pick-the-stars;523;;摘星;Pick The Stars;PTS
|
||||
earth-technology;524;;Earth Technology;;
|
||||
;525;;核乐不为;;ZMC
|
||||
;526;;FTB StoneBlock 3;;
|
||||
ftb-stoneblock-3;526;;FTB StoneBlock 3;;
|
||||
create-mod-plus;527;;Create +;;
|
||||
multiplayer-optimization;528;;RawDiamondMC的优化包;RawDiamondMC's optimization pack;MPO
|
||||
chroma-endless;529;;Chroma Endless;;
|
||||
@@ -532,12 +531,12 @@ fos-community-edition;534;;奇点工厂1:社区版;Factory of Singularity:Comm
|
||||
;538;;跃迁计划;CT TransitionPlan;CTTP
|
||||
astropolis;539;;Astropolis;;
|
||||
;540;;筑梦者;Builder Dream;
|
||||
;541;;尘埃传说;dusttale;
|
||||
;541;;尘埃传说;;
|
||||
magiculture-2;542;;Magiculture 2;;
|
||||
;543;;交错维度2;Among Dimensions 2;AD
|
||||
hexteria-skies;544;;Hexteria Skies;;
|
||||
;545;;Omniworld;;
|
||||
a-l;546;;冒险生活;Adventure Life;
|
||||
al-legacy;546;;冒险生活-L;Adventure Life - Legacy;ALL
|
||||
skymachina;547;;SkyMachina;;
|
||||
all-of-fabric-6;548;;All of Fabric 6;;AOF6
|
||||
;549;;机械动力:锄与锤;;CHH
|
||||
@@ -546,11 +545,11 @@ technologicaljourney;552;;Technological Journey;;TJ
|
||||
;553;;末世工匠;End-Time Craftsmen;EdTC
|
||||
;554;;光芒消逝之日;;VL
|
||||
gregic-skies;555;;Gregic Skies;;
|
||||
;556;;勇者之章;Chapter of Yuusha;
|
||||
;556;;勇者之章;Chapter of Yuusha;CY
|
||||
;557;;齿轮颂歌:暮光;Gear Carol;
|
||||
pluma;558;;Pluma, a Journey to the Future!;;Pluma
|
||||
all-the-mods-7-to-the-sky;559;;All the Mods 7 - To the Sky;;ATM7S
|
||||
;560;;月见苍穹传说:大地演化;TskimiCanopyCraft:Terra Evolution;TSCC:TE
|
||||
;560;;月见苍穹传说:大地演化;TskimiCanopyCraft: Terra Evolution;TSCC:TE
|
||||
;561;;最后的战役:劫后余生;The Last Stand:Aftermath;LSA/TLS
|
||||
;562;;小猪配平;Peppa Pig's Burning Broom;2P2B
|
||||
zerblands-remastered;563;;Zerblands-Remastered;;
|
||||
@@ -565,7 +564,7 @@ provefrom;569;;忘却之刻;Provefrom;PvF
|
||||
darkrpg-forge;572;;DarkRPG Forge;;
|
||||
expert-of-magic-art-world;573;;魔艺世界;Expert of Magic Art World;EOMAW
|
||||
;574;;龙境;Dragoncraft;DGC
|
||||
nuclear-radiation-2;576;;核污染二;;NR2
|
||||
nuclear-radiation-2;576;;核污染二;nuclear radiation 2;NR2
|
||||
;577;;白狐铃的奇思妙想;Arctic Fox Ling Fantasy;AFLF
|
||||
;578;;MineSpace;;MS
|
||||
;579;;史诗战斗:兵团战争;EPIC Fight Corps War;EFC
|
||||
@@ -579,22 +578,22 @@ multiblock-madness-2;583;;MultiBlock Madness 2;;MBM2
|
||||
e2eu;587;;Enigmatica 2 Expert Unofficial;;E2Eu
|
||||
e2eus;588;;Enigmatica 2 Expert Unofficial Skybound;;E2EuS
|
||||
;589;;龙之冒险;;
|
||||
;590;;格雷:量子跃迁;GregTech:QuantumTransition;GTQT
|
||||
gregtech-quantum-transition;590;;格雷:量子跃迁;GregTech:QuantumTransition;GTQT
|
||||
the-winter-frontier;591;;冬境边域;The Winter Frontier;TWF
|
||||
;592;;林深不知处;NO, is killed by... Twlight Forest?;NON
|
||||
;593;;太阳机械厂;Create Sun;CS
|
||||
dawn-craft;594;;破晓之界;DawnCraft;
|
||||
dawn-craft;594;;破晓之界;DawnCraft: Echoes of Legends;DC
|
||||
aof-presents-skylore;595;;AOF: Skylore;;
|
||||
;596;;可乐兔的格雷整合包;GtMagic;
|
||||
;597;;FTB Skies;;
|
||||
keletus-gregpack;596;;可乐兔的格雷整合包;Keletu's GregPack;
|
||||
ftb-skies;597;;FTB Skies;;
|
||||
tnp-limitless-5;598;;TNP Limitless 5;;
|
||||
create-back-on-track;599;;Create: Back on Track;;
|
||||
;600;;重构:炼金术;Refactoring: Alchemy;RA
|
||||
;601;;地心;Geocentric;GOC
|
||||
;602;;巫师·古老恶意;;
|
||||
;602;;巫师·古老恶意;Wizard against wicked;WaW
|
||||
;603;;进化太阳生存;Evolutionary Sun Survival;ESS
|
||||
changfeng;604;;长峰漫路;Long peak Long road;LPR
|
||||
techopolis-2;605;;Techopolis 2;;
|
||||
techopolis-2;605;;科技城2;Techopolis 2;
|
||||
;606;;Arcomua 原版整合;Arcomua Modpack;ARC
|
||||
;607;;UnityCraft;;UC
|
||||
;608;;灵犀;;
|
||||
@@ -606,7 +605,7 @@ create-beyond-earth;612;;Create: Beyond Earth;;
|
||||
;614;;Additive;;
|
||||
;615;;无人荒漠-新世界;;
|
||||
new-game-modpack-release;616;;New Game - An Improved Vanilla Experience;;
|
||||
prodigium-reforged;617;;Prodigium Reforged;;
|
||||
prodigium-reforged;617;;Prodigium Reforged (Terraria Pack);;
|
||||
;618;;FTB Presents Integration by Parts DX;;
|
||||
striving-standoff;619;;抗争之际;Striving Standoff;SS
|
||||
gun-fire-survival;620;;枪火生存;Gun fire survival;
|
||||
@@ -628,10 +627,10 @@ cobblemon-forge;635;;Cobblemon Official Modpack;;
|
||||
sbeevs-industrial-revolution;636;;Sbeev's Industrial Revolution;;
|
||||
;637;;Gensokyo Reimagined QOL;;
|
||||
statech-industry;638;;StaTech Industry;;
|
||||
vault-hunters-1-18-2;639;;Vault Hunters 3rd Edition;;
|
||||
vault-hunters-1-18-2;639;;宝藏猎人3;Vault Hunters 3rd Edition;
|
||||
survival-hard-rock;640;;生存硬摇滚;Survival Hard Rock;
|
||||
aged;641;;Aged;;
|
||||
;642;;The Lost Era;;
|
||||
the-lost-era-modpack;642;;The Lost Era;;
|
||||
vault-hunters-official-modpack;643;;Vault Hunters 2nd Edition;;
|
||||
;644;;Sodium Plus;;
|
||||
;645;;霜叶表达法;Wither Expression;WE
|
||||
@@ -640,7 +639,7 @@ create-live_4;646;;Create Live 4;;
|
||||
valhelsia-5;648;;Valhelsia 5;;
|
||||
caves-cliffs-plus;649;;洞穴与山崖:增强版;Caves & Cliffs: Plus;CC+
|
||||
enigmatica9expert;650;;Enigmatica 9: Expert;;E9E
|
||||
;651;;虚无之主;The Lord of Ascension;TLoA
|
||||
the-lord-of-ascension;651;;虚无之主;The Lord of Ascension;TLoA
|
||||
;652;;寄生科技;;PTY
|
||||
rise-of-tech-2;653;;科技飞升2;Rise of Tech 2;RoT2
|
||||
;654;;虚空居者;Void Dweller;
|
||||
@@ -653,7 +652,7 @@ valhelsia-6;660;;Valhelsia 6;;
|
||||
all-the-mods-volcanoblock;661;;All the Mods: Volcano Block;;ATMVB
|
||||
gregtronix-revoluton;662;;Gregtronix Revolution;;GR
|
||||
;663;;空岛物语;Empty Island RPG;EIRPG
|
||||
prominence-fabric;664;;Prominence I [FABRIC];;
|
||||
prominence-fabric;664;;卓越1 [FABRIC];Prominence I;
|
||||
multiskyblock-2;665;;模块化空岛2;MultiSkyBlock 2;MSB2
|
||||
;666;;The Minehattan Project;;
|
||||
;667;;万象天工;Millions of Heavens industries;MOHI
|
||||
@@ -677,7 +676,7 @@ musketeer;683;;Musketeer - One for All;;
|
||||
;685;;我的骑士:怪物大乱斗;;
|
||||
;686;;塔可努的原生优化整合包;TechNoob's Vanilla Boost Pack;TNVBP
|
||||
;687;;灵茗屿空岛;LMY SKY;
|
||||
;688;;绿色版红石生电优化;;
|
||||
;688;;红石生电优化;Redstone Survival Optimization;RSO
|
||||
;689;;恐惧深渊;Abyss of Fear;AoF
|
||||
;690;;德雷戈拉旅人日记;Diary of Dregora's Traveller;DoDT
|
||||
nwtcraft;691;;近西之旅;NwtCraft;Nwtc
|
||||
@@ -685,26 +684,26 @@ nwtcraft;691;;近西之旅;NwtCraft;Nwtc
|
||||
aeropolis;693;;Aeropolis;;
|
||||
project-sacrifice;694;;Project Sacrifice;;
|
||||
chosens-modded-adventure;695;;Chosen's Modded Adventure;;
|
||||
ciscos-fantasy-medieval-adventure-rpg;696;;Cisco's Fantasy Medieval Adventure RPG;;
|
||||
ciscos-fantasy-medieval-adventure-rpg;696;;Cisco's Fantasy Medieval RPG [Lite];;
|
||||
;697;;命运之轮;Wheel of Fate;
|
||||
all-the-mods-gravitas2;698;;All The Mods - Gravitas²;;ATMG2
|
||||
enigmatica9;699;;Enigmatica 9;;E9
|
||||
;700;;死亡突围;Deadout;DT
|
||||
create-arcane-engineering;701;;机械动力:奥术工程;Create: Arcane Engineering;CAE
|
||||
chroma-endless-2;702;;Chroma Endless 2;;
|
||||
techrevolution;703;;TechRevolution;;TECHR
|
||||
techrevolution;703;;TechRevolution;;
|
||||
cabin;704;;CABIN: Create Above & Beyond In Newer;;CABIN
|
||||
aoc;705;;All of Create;;Aofc
|
||||
aoc;705;;All of Create;;
|
||||
;706;;交错维度3;Among Dimensions 3;AD
|
||||
gregfactory-sky;707;;GregFactory Sky;;GFS
|
||||
;708;;飞翔之路4;Flying Road 4;
|
||||
;709;;星月传说;Celestial Legend;CL
|
||||
ctc-create-the-creation;710;;Create:the Creation;;CTC
|
||||
create-perfect-world;711;;Create: Perfect World;;
|
||||
new-simple-mods;712;;New Simple Mods;;
|
||||
;713;;灾变录;Apocalypse of Catastrophe;ACC
|
||||
new-simple-mods;712;;New Simple Mods - Easy to Understand;;
|
||||
;713;;灾变录;Catastrophe Record;CR
|
||||
sky-greg;714;;Sky Greg;;
|
||||
deceasedcraft;715;;DeceasedCraft;;
|
||||
deceasedcraft;715;;亡者世界;Deceased Craft;
|
||||
;716;;生欲:求生;Vivere The Zombie Apocalypse;VZA
|
||||
create-planetary;717;;Create Planetary;;
|
||||
master-mabaoguo;718;;Master mabaoguo;;
|
||||
@@ -717,41 +716,322 @@ the-backrooms-survival;724;;The Backrooms Survival;;
|
||||
;725;;13年前的梦;Dreams 13 years ago;
|
||||
cookiecrafter;726;;CookieCrafter;;
|
||||
;727;;理想国:科克肖特;Utopia:Cockshott;UCST
|
||||
bigchadguys-plus;728;;BigChadGuys Plus;;BCG+
|
||||
bcg;728;;BigChadGuys Plus;;BCG+
|
||||
;729;;异世界探险;ISEKAIBOUKEN;YSB
|
||||
create-explore-c-e;730;;Create & Explore;;C&E
|
||||
twistercraft;731;;TwisterCraft;;
|
||||
all-the-horror;732;;恐怖生存;All The Horror;
|
||||
the-backrooms-liminal-spaces;733;;The Backrooms;;
|
||||
;734;;伊甸空天;Eden Aerospace;EAS
|
||||
;735;;亚特兰深渊;AtlanAbyss;
|
||||
atlanabyss;735;;亚特兰深渊;AtlanAbyss;
|
||||
;736;;龙境II;Dragon Odyssey;DO
|
||||
;737;;龙之冒险:新征程;;
|
||||
all-the-mods-9-no-frills;738;;All the Mods 9 - No Frills;;ATM9NF
|
||||
toms-cobblemon;739;;Tom's Cobblemon;;
|
||||
life-in-the-village-3;740;;Life in the village 3;;LitV3
|
||||
;741;;联缘;NEXUS;
|
||||
;741;;联缘:NEXUS;;
|
||||
;742;;Create: Cinematic;;
|
||||
;743;;神秘纪元;Thaumic Epoch;ThE
|
||||
;745;;FTB Genesis;;
|
||||
cook-in-skyblock;746;;天空厨房;Cook in skyland;CiS
|
||||
;747;;被支配的恐惧;Dominated by fear;DF
|
||||
;748;;新的原版!;New Vanilla;NVA
|
||||
;747;;被恐惧支配 1;Dominated by Fear 1;DoF
|
||||
new-vanilla-modpack;748;;新的原版;New Vanilla;NVA
|
||||
;749;;群峦救援非官方版;Terrafirma Rescue Unofficial;TFRU
|
||||
stoneopolis;750;;Stoneopolis;;
|
||||
;751;;FTB Skies Expert;;
|
||||
ftb-skies-expert;751;;FTB Skies Expert;;
|
||||
wmshd-pack-what-mojang-should-have-done;752;;What Mojang Should Have Done;;WMSHD
|
||||
;753;;无限:重生;Infinity-Reborn;IF2
|
||||
;754;;随心之旅;spontaneous journey;
|
||||
oxmodpack;755;;OxMODPACK 8 +Online;;
|
||||
gregtech-community-pack-modern;756;;GregTech Community Pack Modern;;GCPM
|
||||
;757;;锻造大师2;Master Blacksmith2;MB2
|
||||
;757;;锻造大师3;Master Blacksmith3;MB3
|
||||
;758;;科技用于探索世界;Technology Exploration World;
|
||||
;759;;幻梦闲笔;Aetherial Voyage;AV
|
||||
;759;;幻梦闲笔;Aetherial Voyage;
|
||||
fear-the-mist;760;;雾中的恐惧;Fear The Mist;
|
||||
tekkit-resurrection;761;;Tekkit The Resurrection;;
|
||||
;762;;Cup Pineapple UBLU;;CPU
|
||||
;763;;FTB Arcanum Institute;;
|
||||
ftb-arcanum-institute;763;;FTB Arcanum Institute;;
|
||||
create-haven-adventures;764;;Create: Haven Adventures;;
|
||||
modecube;765;;ModéCube;;
|
||||
electrictechnocraft;766;;ElectricTechnoCraft;;
|
||||
create-2-mekanism;767;;Create 2 Mekanism;;C2M
|
||||
randblock;768;;随机空岛;RandBlock;RD
|
||||
;769;;格雷科技休闲版;GregTech Leisure;GTL
|
||||
frozenopolis;770;;Frozenopolis;;
|
||||
infernal-rpg;771;;Infernal Origins;;
|
||||
;772;;天狼星;Sirius;
|
||||
;773;;悠沙海;Ethereal Sand Sea;ESS
|
||||
;774;;AIR for JAVA;;AFJ
|
||||
;775;;Create Extra;;
|
||||
frs-optimisation-for-survival;776;;Fr的生存优化;Fr's Optimisation for Survival;FROS
|
||||
;777;;Exile Magic Constructor;;EMC
|
||||
;778;;Landscapes Reimagined Genesis;;
|
||||
al-nl;779;;冒险生活 - 新生活;Adventure Life - New Life;AL-NL
|
||||
harvista;780;;拾穗;Harvista;
|
||||
;781;;时间之书 : 星之旅行;BookofTime StarTravel;BKSL
|
||||
create-applied-energevation;783;;机械动力:应用能源时代;Create: Applied Energevation;CAEV
|
||||
;784;;新星工程:世界;Nova Engineering - World;NEW
|
||||
create-chronicles-bosses-and-beyond;785;;Create Chronicles: Bosses and Beyond;;
|
||||
;786;;Adrenaline;;
|
||||
forget-me-not;787;;勿忘我;Forget Me Not;FMN
|
||||
modpack-essentials;788;;整合优选;Modpack Essentials;ME
|
||||
desertopolis;789;;Desertopolis;;
|
||||
omniopolis;790;;Omniopolis;;
|
||||
;791;;战斗的法则;The Law of Battle;
|
||||
factory-of-singularity-ii-colorfulness;792;;奇点工厂II:绚彩;Factory of Singularity II: Colorfulness;FoS2
|
||||
prey-beta;793;;Prey;;
|
||||
;794;;魔法之旅;Magic journey;MJ
|
||||
;795;;轻松冒险;Easy adventure;EAT
|
||||
;796;;魔幻大陆2;Enchanted Lands 2;
|
||||
;797;;艾利克斯的世界;Alexcraft;AC
|
||||
terrafirmacraft-wiph-walden-lagu;798;;瓦尔登湖畔;Wiph Walden Lagu;WWL
|
||||
aocfabric;799;;All of Create Fabric;;
|
||||
craftingcraft;800;;合成工艺;CraftingCraft;
|
||||
;801;;感染者手册;Handbook for Infected People;HIP
|
||||
;802;;居无定所—中世旷野;;
|
||||
;803;;宾果难题;Bingo Madness;
|
||||
chemillas-reborn;804;;化学:重生;Chemilla's Reborn;CR
|
||||
;805;;群峦传说·异世界行纪;Terra Firma Craft:Travel in Another World;TFC:TAW
|
||||
milkyway;806;;Create: Milkyway;;
|
||||
farming-crossing-4;807;;Farming Crossing 4;;
|
||||
hardrock-terrafirmacraft-4-realistic-extreme;808;;HardRock TerraFirmaCraft;;TFCH/HTFC
|
||||
terrafirma-rebirth;809;;群峦:重生;TerraFirma: Rebirth;TFCR
|
||||
create-live-5-skyblock;810;;Create Live 5;;
|
||||
;811;;万象征程;Cosmos of Endless Allegory;CEA
|
||||
;812;;远古之遗;;
|
||||
;813;;奈の奇妙冒险;Kotona's Mystery Adventure;KSMA
|
||||
;814;;战争魔匠;war craftman;WC
|
||||
;815;;命定IV;unique destiny IV;
|
||||
;816;;Wither Storm Enhanced;;
|
||||
;817;;朝花夕拾;Dawn Blossoms Plucked at Dusk;
|
||||
la;818;;胶兽大冒险;Latex Advanture;LA
|
||||
mystics-monstrosity;819;;Mystic's Monstrosity;;
|
||||
;820;;更完美的MC;More Perfect Minecraft;MPMC
|
||||
;821;;动画世界:烂柯人;AnimateCraft: Lankeren;
|
||||
;822;;New Create;;NC
|
||||
land-of-memories;823;;回忆之地;;LOM
|
||||
;824;;异世界冒险2;;
|
||||
ragnamod-vii;825;;Ragnamod VII;;
|
||||
path-of-truth;826;;真理之路;Path of Truth;PoT
|
||||
;827;;TFC生活质量更新;;
|
||||
all-the-mods-9-to-the-sky;828;;All The Mod 9 - To The Sky;;ATM9S
|
||||
;829;;魔金:探秘;ManaMetalMod Exploration;M3E
|
||||
fallenpetals;830;;落英;FallenPetals;FP
|
||||
dungeon-heroes;831;;Dungeon Heroes (RPG Series);;
|
||||
play-as-dragon-gothic-edition;832;;Play as Dragon: Gothic Edition;;
|
||||
skyblock-burgeria;833;;空岛汉堡店;;
|
||||
crafting-craft-2;834;;合成工艺2;Crafting Craft 2;
|
||||
;835;;僵尸入侵100天;Zombie Invade 100 Days;ZI100D
|
||||
compact-world-expert;836;;Compact World Expert;;CWE
|
||||
a-fragment-of-peace;837;;宁然一隅;A Fragment of Peace;AFoP
|
||||
easy-create-modpack;838;;Easy Create;;
|
||||
;839;;流线鱼概念;Fish Spi Rally;FSRMP
|
||||
meatballcraft;840;;肉丸工艺,次元提升;MeatballCraft, Dimensional Ascension;
|
||||
seaopolis-submerged;841;;海之城: 潜没水中;Seaopolis: Submerged;
|
||||
;842;;空中厕所;PoopSky;
|
||||
compact-sky;843;;收缩空岛;Compact Sky;
|
||||
back-to-back;844;;背靠背;Back to Back;BTB
|
||||
adventure-exploration-part2;845;;冒险与探索;Adventure & Exploration;AnE
|
||||
;846;;复兴;Renaissance;
|
||||
live-like-a-castaway;847;;木筏求生2;Raftcraft2;RC2
|
||||
mermaid-legend;848;;人鱼传说;Mermaid legend;
|
||||
;849;;文明时代;;Aoc
|
||||
create-lost-and-renaissance;850;;机械动力:失落与复兴;Create Lost and Renaissance;CLR
|
||||
all-the-mods-10;851;;All the Mods 10;;ATM10
|
||||
;852;;格雷伊甸:建造;GregEden:Building;GEB
|
||||
;853;;迷失时代;LOST ERA;LE
|
||||
create-alchemy-plan;854;;机械动力:炼金计划;Create:Alchemy Plan;CAP
|
||||
deus-ex-machina;855;;Deus Ex Machina;;
|
||||
create-prepare-to-dye;856;;Create Prepare to Dye;;
|
||||
;857;;All The Fabric 5;;
|
||||
create-leisurely-delight;858;;机械动力:悠然乐事;Create Delight;CD
|
||||
;859;;蔚蓝悠悠;Azure Leisurely;AL
|
||||
infinityevolved-reloaded;860;;Infinity Evolved: Reloaded;;
|
||||
;861;;空中厕所:生产线;Poopsky:Assembly Line;PAL
|
||||
;862;;佛罗伦萨迷思;Fantasy in Florence;FIF
|
||||
craftoria;863;;Craftoria;;
|
||||
mechanic-craft;864;;MechanicCraft;;
|
||||
;865;;无用之人-群星;Good For Nothing:Stellaris;GFN
|
||||
;866;;打金无限;;
|
||||
ciscos-adventure-rpg-ultimate;867;;Cisco's Fantasy Medieval RPG [Ultimate];;
|
||||
jour-avancee-daylight;868;;Jour avancée/DayLight;;DL
|
||||
;869;;勇者之章 Ⅱ;Chapter of Yuusha II;CY2
|
||||
;870;;Beta 1.7.4;;B1.7.4
|
||||
;871;;神不在的星期天;God Without Create;GWC
|
||||
createa-colony;872;;Create'a Colony;;
|
||||
;873;;黑暗深处;Deep Dark;
|
||||
;874;;自在极境;Free Extreme Realm;
|
||||
all-of-fabric-7;875;;All of Fabric 7;;AOF7
|
||||
ftb-neotech;876;;FTB NeoTech;;
|
||||
;877;;Minecraft+;Minecraft plus;MCP
|
||||
;878;;龙珠Super;Dragon Ball Super;
|
||||
create-vanilla-exploration;879;;Vanilla Create Exploration;;
|
||||
progress;880;;Progress: Create;;
|
||||
aeon-automata;881;;Aeon Automata;;AA
|
||||
;882;;被恐惧支配2:收容失效;Dominated by Fear 2 Containment Breach;DoF2
|
||||
;883;;剑拔弩张之时;;TC
|
||||
project-architect-2;884;;Project Architect 2;;
|
||||
;885;;路网通途 / 更好的我的世界铁路;Railway Network Access;RNA
|
||||
create-mekanized;886;;Create: Mekanized;;
|
||||
create-perfect-world-2;887;;Create: Perfect World 2;;
|
||||
create-stellar;888;;Create Stellar;;
|
||||
monifactory;889;;Monifactory;;
|
||||
create-factory-builder;890;;Create: Factory Builder;;
|
||||
;891;;群峦传说:宝宝快乐版;TerraFirma: Baby Happy Edition;TFBH
|
||||
;892;;阶段: 残骸;Phase: the Debris;
|
||||
;893;;AE管道;AE Pipes;APE
|
||||
skyt-2;894;;SkyT 2;;
|
||||
stagecraft;895;;StageCraft;;
|
||||
;896;;我的世界从来不简单;Minecraft Is Never Easy;MINE
|
||||
ctnh;897;;机械动力:新视野;Create: New Horizon;CTNH
|
||||
agora-interregnum;898;;Agora Interregnum;;
|
||||
evolving-technology;899;;Evolving Technology;;
|
||||
enlightened-6-expert;900;;Enlightened 6 Expert;;En6E
|
||||
;901;;历久尤新;Old But New;OBN
|
||||
;902;;继承;Inherit;IN
|
||||
lucky-world-invasion;903;;幸运世界入侵;Lucky World Invasion;LWI
|
||||
ftb-interactions-remastered;904;;FTB Interactions Remastered;;FTBIR
|
||||
;905;;崩坏-新生;Broken-New Life;BNL
|
||||
;906;;持之永恒重生;Stay With Eternity Rebirth;SWER
|
||||
shadow-awakening;907;;暗影觉醒;Shadow Awakening;SAG
|
||||
;908;;理想箱庭物语;Second Eden;SE
|
||||
finality-genesis-1-20-1;909;;Finality Genesis;;
|
||||
;910;;死亡突围 · 重临死境;DeadoutRe;DTR
|
||||
soa3;911;;Slow Start AoA3;;SoA3
|
||||
new-vanilla-2;912;;新的原版 2;New Vanilla 2;NVA2
|
||||
prominence-2-rpg;913;;卓越 II RPG;Prominence II RPG;
|
||||
;914;;伟大冒险者:世界之眼;Great Adventurer:the Eye of the World;GAEW
|
||||
;915;;破碎;Fractures Rainimator;Rain
|
||||
;916;;更好的原版;Better Vanilla;BV
|
||||
strictly-medieval-3-forge;917;;Strictly Medival 3;;
|
||||
;918;;潘神的迷宫;Pan's Labyrinth;PL
|
||||
;919;;持之永恒-天空挑战者;Stay With Eternity Sky Challenger;SWESC
|
||||
easy-to-ascension;920;;易如飞升;Easy to Ascension;
|
||||
;921;;幽匿末日;The Last Day of Sculk;TLDS
|
||||
;922;;交错空岛;Crossisland;CrI
|
||||
;923;;机械动力:魔法乐事;Create Magic's Delight;CRMD
|
||||
minetech-evolution;924;;科技进化;Minetech Evolution;MTE
|
||||
gregified-sky-edition;925;;Gregified: Sky Edition;;
|
||||
;926;;原版扩展;Additonal Vanilla;AV
|
||||
create-on-a-potato;927;;Create on a Potato PC: Ultimate;;CPU
|
||||
;928;;现实主义;realism;RLM
|
||||
;929;;晋升之路;path of advance;POA
|
||||
;930;;永无止境:超越常新;Above and Beyond : Refresh;CABR
|
||||
cosmic-frontiers;931;;Cosmic Frontiers;;
|
||||
integrated-minecraft;932;;Integrated MC;;
|
||||
;933;;Rising Legends;;
|
||||
;934;;城乡之旅;Travel Township;TT
|
||||
;935;;龙鸣牢大;FarmerKobeBryant;FKB
|
||||
setgamedifficulty-reika;936;;SetGameDifficulty Reika;;
|
||||
epic-journey-gtceu;937;;Epic Journey GTCEu;;
|
||||
;938;;飞渡群峦;TerraFirmaCraft:Leap The Top;TFC:LTT
|
||||
gregtech-skybound;939;;Gregtech: Skybound;;
|
||||
simple-tfc;940;;简单群峦;SimpleTFC;
|
||||
ftb-presents-direwolf20-s14;941;;FTB Presents Direwolf20 1.21;;
|
||||
final-judgment-save-the-world;942;;Final Judgment:Save the world;;
|
||||
;943;;极端世界:重置;Extreme World Reset;EWR
|
||||
explore-everything;944;;空岛:探索;Explore Everything;SEE
|
||||
epic-journey-gtceu-conquerors-edition;945;;Epic Journey GTCEu - Conqueror's Edition;;
|
||||
the-undead-blooms;946;;亡灵绽放 | 真菌孢子启示录 |;The Undead Blooms | Fungal Spore Apocalypse |;
|
||||
;947;;星地征程;Starland Journey;SJ
|
||||
;948;;希望与绝望-惊变100天;Hope and Despair;had
|
||||
;949;;梦篡光阴;Dreams Distort Overture;DDO
|
||||
;950;;最后的光芒;The Last Light;TLL
|
||||
endil-expert-edition;951;;Endil's Expert Edition;;EEE
|
||||
peace-of-mind;952;;Peace of Mind;;
|
||||
distantly-optimized;953;;Distantly Optimized;;DO
|
||||
farming-experience;954;;Farming Experience;;
|
||||
hungry-for-tech-2;955;;Hungry For Tech 2;;
|
||||
remain;957;;Remain;;
|
||||
;958;;末日狂徒;;
|
||||
gregtania6-skyblock;959;;Gregtania6 Skyblock;;
|
||||
harmonious-engineering;960;;Harmonious Engineering;;
|
||||
valley-of-farming-and-ranching;961;;农牧物语;Valley of Farming and Ranching;
|
||||
;962;;机械动力:火车;Create Train;
|
||||
;963;;理想国冒险之旅;;AOIC
|
||||
;964;;生欲:求生2;Vivere The Zombie Apocalypse-II;VZA2
|
||||
;965;;起源-世界;OriginCraft Pulses;
|
||||
;966;;Legacies;;
|
||||
the-legend-of-2900;967;;海上机械师;The Legend Of 2900;TLOU
|
||||
mirasites-100-days;968;;畸变:蛊之奇境;Mirasites-BrokenMemories;MBM
|
||||
;969;;末日余晖:独自一人;;MRYH
|
||||
;970;;幸存者与僵尸;Survivors And Zombies;SAZ
|
||||
;971;;海岛寿司店;Sushi Island;
|
||||
infinite-alchemy;972;;Infinite Alchemy;;IA
|
||||
;973;;探索的时光;Adventuring Time;AT
|
||||
;974;;饕餮飨食;;
|
||||
;975;;丰富的MC;RichMC;RM
|
||||
;976;;刀剑异闻录;Sword Strange Tales;SST
|
||||
etbw;977;;探索光明的世界;Explore the Bright World;EtBW
|
||||
hardship-era;978;;苦难年代;Hardship Era;HE
|
||||
rlcraft-dregora;979;;RLCraft Dregora;;
|
||||
;980;;Farm The Ingots;;FTI
|
||||
venoms-craft;981;;Venom's Craft;;
|
||||
;982;;慢生活冒险;;
|
||||
;983;;多元工艺:绘镇曲;Painted Town Melody;PTM
|
||||
;984;;遗忘之海;Leisurely Life;
|
||||
;985;;幻想国度;F_Land;
|
||||
;986;;群峦传说:重制;TerraFirmaCraft Remake;TFCRe
|
||||
;987;;未来;;
|
||||
;988;;起源生物—崛起;;
|
||||
;989;;工匠:技艺革新;Construct:Technological Innovation;CTI
|
||||
erstwhile-journey;990;;往昔之旅;Erstwhile Journey;EJ
|
||||
;991;;Exploria;;
|
||||
ftb-evolution;992;;FTB Evolution;;
|
||||
;994;;云程发轫;cloud journey begins;CJB
|
||||
forever-factory;995;;永恒工厂;Forever Factory;
|
||||
;996;;FTB University 1.19;;
|
||||
symbolica;997;;Symbolica;;
|
||||
aelstar;998;;Aelstar;;
|
||||
srp-qz;999;;SRP:隔离区;SRP: Quarantine Zone;
|
||||
industrial-machines;1000;;Industrial Machines;;
|
||||
auto-terrafirmacraft-tfc-create;1001;;Auto-TerraFirmaCraft (TFC + Create);;ATFC
|
||||
;1002;;时间之书:星的日记;The Book of Time: A Star's Journal;TASL
|
||||
;1003;;特摄世界;Tokusatsu World;TW
|
||||
journey-of-reincarnation;1004;;转世之旅;Journey of Reincarnation;
|
||||
;1005;;冒险与休闲;Adventure and Leisure;AL
|
||||
enigmatica10;1006;;Enigmatica 10;;E10
|
||||
mc-chocolate-edition;1007;;The Chocolate Edition;;
|
||||
civilization-l;1008;;Civilization-L;;
|
||||
;1009;;群峦:降临;TerraFirma:Advent;TFA
|
||||
valhelsia-7;1010;;Valhelsia 7;;
|
||||
mechanical-mastery-plus;1011;;Mechanical Mastery Plus;;
|
||||
ashes-to-ashes;1012;;尘归尘;Ashes To Ashes;ATA
|
||||
;1013;;格雷乐事;GT So Easy;GTse
|
||||
create-silence-and-prosperity;1014;;机械动力:寂静与繁荣;Create Silence and Prosperity;CSP
|
||||
mif;1015;;MI:Foundation;;MIF
|
||||
c2mse2;1016;;Create 2 Mekanism: Sky Edition 2;;C2MSE2
|
||||
;1017;;Tsuri No Gatsu 5 - FDCraft Version;;TSNG5-FDC
|
||||
;1018;;无限战争4;Infinity War4;INF4
|
||||
;1019;;群峦传说:工业长路;;
|
||||
;1020;;自然冒险 初生;Nature Adventure Newborn;NAB
|
||||
;1021;;羽舲的基础优化;Hanekmio's Basis Optimization;
|
||||
create-star-valley;1022;;Create: Gears of History;;
|
||||
;1023;;阿尔法计划;AlphaPlan;AP
|
||||
spell-dimension;1024;;咒次元;Spell Dimension;SD
|
||||
skyfactory-5;1025;;天空工厂 5;SkyFactory 5;SF5
|
||||
;1026;;破劫之刃;Break Disaster;
|
||||
reminiscent-create;1027;;Reminiscent Create;;
|
||||
techsert;1028;;Techsert;;TH
|
||||
prehistoric-world-dinosaurs-adventure;1029;;Prehistoric World - Dinosaurs & Adventure;;
|
||||
terminal;1030;;终末;Terminal;
|
||||
;1031;;异次元水晶 3;Isolated Crystal 3;
|
||||
;1032;;机械动力:无限总是小于零;Create:Infinity Always Smaller Than Zero;CIASZ
|
||||
;1033;;踪灭I-开端;;
|
||||
;1034;;踪灭II;;
|
||||
rabbits-dawn-craft;1035;;某兔子的破晓之界;A Certain Rabbit's DawnCraft - Dawn of the Deep Soul;RDC
|
||||
;1036;;铁砧天空;;ASKY
|
||||
grand-architect-journey;1037;;伟大工程巡礼;Grand Architecture Journey;GAJ
|
||||
the-adventure-of-valkyriens;1038;;瓦尔基里大冒险;The Adventure of Valkyriens;TAOV
|
||||
;1039;;方块宝可梦:奥瑞星光;Cobblemon Ori Starlight;COS
|
||||
;1040;;群峦传说:天才版;;
|
||||
compression;1041;;Compression;;
|
||||
compact-sky-easy;1042;;Compact Sky: Easy;;CSE
|
||||
;1043;;丰收与探索;;
|
||||
;1044;;脆骨症:黯光;No Flesh Within Chest-DIM;NFWC-DIM
|
||||
;1045;;小雷的优化冒险;;
|
||||
cobblemon-eternal-edition;1046;;Cobblemon Eternal;;
|
||||
;1047;;Tinkerer's Quilt;;
|
||||
gregtech-expert;1048;;GregTech Expert;;
|
||||
;1049;;Beta Horizons;;
|
||||
minecraft-legendary-edition;1050;;Legendary Edition;;
|
||||
@@ -12,7 +12,7 @@ dependencies {
|
||||
api("com.github.steveice10:opennbt:1.5")
|
||||
api("org.nanohttpd:nanohttpd:2.3.1")
|
||||
api("org.apache.commons:commons-compress:1.25.0")
|
||||
api("org.jsoup:jsoup:1.18.1")
|
||||
api("org.jsoup:jsoup:1.18.3")
|
||||
compileOnlyApi("org.jetbrains:annotations:26.0.1")
|
||||
|
||||
if (JavaVersion.current().isJava8) {
|
||||
|
||||
@@ -72,6 +72,7 @@ public final class BMCLAPIDownloadProvider implements DownloadProvider {
|
||||
pair("https://files.minecraftforge.net/maven", apiRoot + "/maven"),
|
||||
pair("https://maven.minecraftforge.net", apiRoot + "/maven"),
|
||||
pair("https://maven.neoforged.net/releases/net/neoforged/forge", apiRoot + "/maven/net/neoforged/forge"),
|
||||
pair("https://maven.neoforged.net/releases/net/neoforged/neoforge", apiRoot + "/maven/net/neoforged/neoforge"),
|
||||
pair("http://dl.liteloader.com/versions/versions.json", apiRoot + "/maven/com/mumfrey/liteloader/versions.json"),
|
||||
pair("http://dl.liteloader.com/versions", apiRoot + "/maven"),
|
||||
pair("https://meta.fabricmc.net", apiRoot + "/fabric-meta"),
|
||||
|
||||
@@ -216,11 +216,6 @@ public final class LibraryAnalyzer implements Iterable<LibraryAnalyzer.LibraryMa
|
||||
|
||||
@Override
|
||||
protected String patchVersion(Version gameVersion, String libraryVersion) {
|
||||
Matcher matcher = NEO_FORGE_VERSION_MATCHER.matcher(libraryVersion);
|
||||
if (matcher.find()) {
|
||||
return matcher.group("forge");
|
||||
}
|
||||
|
||||
String res = scanVersion(gameVersion);
|
||||
if (res != null) {
|
||||
return res;
|
||||
@@ -233,6 +228,11 @@ public final class LibraryAnalyzer implements Iterable<LibraryAnalyzer.LibraryMa
|
||||
}
|
||||
}
|
||||
|
||||
Matcher matcher = NEO_FORGE_VERSION_MATCHER.matcher(libraryVersion);
|
||||
if (matcher.find()) {
|
||||
return matcher.group("forge");
|
||||
}
|
||||
|
||||
return super.patchVersion(gameVersion, libraryVersion);
|
||||
}
|
||||
|
||||
@@ -248,12 +248,15 @@ public final class LibraryAnalyzer implements Iterable<LibraryAnalyzer.LibraryMa
|
||||
|
||||
for (int i = 0; i < gameArguments.size() - 1; i++) {
|
||||
Argument argument = gameArguments.get(i);
|
||||
if (argument instanceof StringArgument && "--fml.neoForgeVersion".equals(((StringArgument) argument).getArgument())) {
|
||||
Argument next = gameArguments.get(i + 1);
|
||||
if (next instanceof StringArgument) {
|
||||
return ((StringArgument) next).getArgument();
|
||||
if (argument instanceof StringArgument) {
|
||||
String argumentValue = ((StringArgument) argument).getArgument();
|
||||
if ("--fml.neoForgeVersion".equals(argumentValue) || "--fml.forgeVersion".equals(argumentValue)) {
|
||||
Argument next = gameArguments.get(i + 1);
|
||||
if (next instanceof StringArgument) {
|
||||
return ((StringArgument) next).getArgument();
|
||||
}
|
||||
return null; // Normally, there should not be two --fml.neoForgeVersion argument.
|
||||
}
|
||||
return null; // Normally, there should not be two --fml.neoForgeVersion argument.
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
@@ -50,6 +50,8 @@ import static org.jackhuang.hmcl.util.Pair.pair;
|
||||
*/
|
||||
public class DefaultLauncher extends Launcher {
|
||||
|
||||
private final LibraryAnalyzer analyzer;
|
||||
|
||||
public DefaultLauncher(GameRepository repository, Version version, AuthInfo authInfo, LaunchOptions options) {
|
||||
this(repository, version, authInfo, options, null);
|
||||
}
|
||||
@@ -60,6 +62,8 @@ public class DefaultLauncher extends Launcher {
|
||||
|
||||
public DefaultLauncher(GameRepository repository, Version version, AuthInfo authInfo, LaunchOptions options, ProcessListener listener, boolean daemon) {
|
||||
super(repository, version, authInfo, options, listener, daemon);
|
||||
|
||||
this.analyzer = LibraryAnalyzer.analyze(version, repository.getGameVersion(version).orElse(null));
|
||||
}
|
||||
|
||||
private Command generateCommandLine(File nativeFolder) throws IOException {
|
||||
@@ -496,7 +500,7 @@ public class DefaultLauncher extends Launcher {
|
||||
break;
|
||||
case ZINK:
|
||||
env.put("MESA_LOADER_DRIVER_OVERRIDE", "zink");
|
||||
/**
|
||||
/*
|
||||
* The amdgpu DDX is missing support for modifiers, causing Zink to fail.
|
||||
* Disable DRI3 to workaround this issue.
|
||||
*
|
||||
@@ -508,7 +512,6 @@ public class DefaultLauncher extends Launcher {
|
||||
}
|
||||
}
|
||||
|
||||
LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(version, repository.getGameVersion(version).orElse(null));
|
||||
if (analyzer.has(LibraryAnalyzer.LibraryType.FORGE)) {
|
||||
env.put("INST_FORGE", "1");
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import javafx.beans.InvalidationListener;
|
||||
import javafx.beans.Observable;
|
||||
@@ -158,6 +159,21 @@ public final class PropertyUtils {
|
||||
});
|
||||
}
|
||||
|
||||
public static void copyProperties(Object from, Object to, Predicate<String> predicate) {
|
||||
Class<?> type = from.getClass();
|
||||
while (!type.isInstance(to))
|
||||
type = type.getSuperclass();
|
||||
|
||||
getPropertyHandleFactories(type)
|
||||
.forEach((name, factory) -> {
|
||||
if (predicate.test(name)) {
|
||||
PropertyHandle src = factory.apply(from);
|
||||
PropertyHandle target = factory.apply(to);
|
||||
target.accessor.setValue(src.accessor.getValue());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void attachListener(Object instance, InvalidationListener listener) {
|
||||
getPropertyHandleFactories(instance.getClass())
|
||||
.forEach((name, factory) -> factory.apply(instance).observable.addListener(listener));
|
||||
|
||||
@@ -85,8 +85,6 @@ public enum Architecture {
|
||||
return this == X86 || this == X86_64;
|
||||
}
|
||||
|
||||
public static final String CURRENT_ARCH_NAME;
|
||||
public static final String SYSTEM_ARCH_NAME;
|
||||
public static final Architecture CURRENT_ARCH;
|
||||
public static final Architecture SYSTEM_ARCH;
|
||||
|
||||
@@ -104,6 +102,7 @@ public enum Architecture {
|
||||
case "ia32e":
|
||||
case "em64t":
|
||||
case "x64":
|
||||
case "intel64":
|
||||
return X86_64;
|
||||
case "x8632":
|
||||
case "x86-32":
|
||||
@@ -191,8 +190,7 @@ public enum Architecture {
|
||||
}
|
||||
|
||||
static {
|
||||
CURRENT_ARCH_NAME = System.getProperty("os.arch");
|
||||
CURRENT_ARCH = parseArchName(CURRENT_ARCH_NAME);
|
||||
CURRENT_ARCH = parseArchName(System.getProperty("os.arch"));
|
||||
|
||||
String sysArchName = null;
|
||||
if (OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS) {
|
||||
@@ -218,12 +216,6 @@ public enum Architecture {
|
||||
}
|
||||
|
||||
Architecture sysArch = parseArchName(sysArchName);
|
||||
if (sysArch == UNKNOWN) {
|
||||
SYSTEM_ARCH_NAME = CURRENT_ARCH_NAME;
|
||||
SYSTEM_ARCH = CURRENT_ARCH;
|
||||
} else {
|
||||
SYSTEM_ARCH_NAME = sysArchName;
|
||||
SYSTEM_ARCH = sysArch;
|
||||
}
|
||||
SYSTEM_ARCH = sysArch == UNKNOWN ? CURRENT_ARCH : sysArch;
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||

|
||||

|
||||
[](https://discord.gg/jVvC7HfM6U)
|
||||
[](https://docs.hmcl.net/groups.html)
|
||||
[](https://docs.hmcl.net/groups.html)
|
||||
|
||||
**English** | [简体中文](README_cn.md) | [繁體中文](README_tw.md)
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
HMCL is an open-source, cross-platform Minecraft launcher that supports Mod Management, Game Customizing, ModLoader Installing (Forge, NeoForge, Fabric, Quilt, LiteLoader, and OptiFine), Modpack Creating, UI Customization, and more.
|
||||
|
||||
HMCL has amazing cross-platform capabilities. Not only does it run on different operating systems like Windows, Linux, and macOS, but it also supports various CPU architectures such as x86, ARM, MIPS, and LoongArch. You can easily enjoy Minecraft across different platforms through HMCL.
|
||||
HMCL has amazing cross-platform capabilities. Not only does it run on different operating systems like Windows, Linux, and macOS, but it also supports various CPU architectures such as x86, ARM, RISC-V, MIPS, and LoongArch. You can easily enjoy Minecraft across different platforms through HMCL.
|
||||
|
||||
For systems and CPU architectures supported by HMCL, please refer to [this table](PLATFORM.md).
|
||||
|
||||
|
||||
+2
-2
@@ -4,7 +4,7 @@
|
||||

|
||||

|
||||
[](https://discord.gg/jVvC7HfM6U)
|
||||
[](https://docs.hmcl.net/groups.html)
|
||||
[](https://docs.hmcl.net/groups.html)
|
||||
|
||||
[English](README.md) | **简体中文** | [繁體中文](README_tw.md)
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
HMCL 是一款开源、跨平台的 Minecraft 启动器,支持模组管理、游戏自定义、游戏自动安装 (Forge、NeoForge、Fabric、Quilt、LiteLoader 和 OptiFine)、整合包创建、界面自定义等功能。
|
||||
|
||||
HMCL 有着强大的跨平台能力。它不仅支持 Windows、Linux、macOS 等常见的操作系统,同时也支持 x86、ARM、MIPS、LoongArch 等不同的 CPU 架构。你可以使用 HMCL 在不同平台上轻松地游玩 Minecraft。
|
||||
HMCL 有着强大的跨平台能力。它不仅支持 Windows、Linux、macOS 等常见的操作系统,同时也支持 x86、ARM、RISC-V、MIPS、LoongArch 等不同的 CPU 架构。你可以使用 HMCL 在不同平台上轻松地游玩 Minecraft。
|
||||
|
||||
如果你想要了解 HMCL 对不同平台的支持程度,请参见 [此表格](PLATFORM_cn.md)。
|
||||
|
||||
|
||||
+2
-2
@@ -4,7 +4,7 @@
|
||||

|
||||

|
||||
[](https://discord.gg/jVvC7HfM6U)
|
||||
[](https://docs.hmcl.net/groups.html)
|
||||
[](https://docs.hmcl.net/groups.html)
|
||||
|
||||
[English](README.md) | [简体中文](README_cn.md) | **繁體中文**
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
HMCL 是一款開源、跨平臺的 Minecraft 啟動器,支援模組管理、遊戲客製化、遊戲自動安裝 (Forge、NeoForge、Fabric、Quilt、LiteLoader 和 OptiFine)、模組包建立、介面客製化等功能。
|
||||
|
||||
HMCL 有著強大的跨平臺能力。它不僅支援 Windows、Linux、macOS 等常見的作業系統,同時也支援 x86、ARM、MIPS、LoongArch 等不同的 CPU 架構。你可以使用 HMCL 在不同平臺上輕鬆地遊玩 Minecraft。
|
||||
HMCL 有著強大的跨平臺能力。它不僅支援 Windows、Linux、macOS 等常見的作業系統,同時也支援 x86、ARM、RISC-V、MIPS、LoongArch 等不同的 CPU 架構。你可以使用 HMCL 在不同平臺上輕鬆地遊玩 Minecraft。
|
||||
|
||||
如果你想要了解 HMCL 對不同平臺的支援程度,請參見 [此表格](PLATFORM_tw.md)。
|
||||
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("com.google.code.gson:gson:2.11.0")
|
||||
}
|
||||
|
||||
java {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
@@ -0,0 +1,218 @@
|
||||
package org.jackhuang.hmcl.gradle.mod;
|
||||
|
||||
import com.google.gson.*;
|
||||
import com.google.gson.annotations.JsonAdapter;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import org.gradle.api.DefaultTask;
|
||||
import org.gradle.api.GradleException;
|
||||
import org.gradle.api.file.RegularFileProperty;
|
||||
import org.gradle.api.logging.Logger;
|
||||
import org.gradle.api.logging.Logging;
|
||||
import org.gradle.api.tasks.InputFile;
|
||||
import org.gradle.api.tasks.OutputFile;
|
||||
import org.gradle.api.tasks.TaskAction;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Type;
|
||||
import java.net.URI;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.util.*;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public abstract class ParseModDataTask extends DefaultTask {
|
||||
|
||||
@InputFile
|
||||
public abstract RegularFileProperty getInputFile();
|
||||
|
||||
@OutputFile
|
||||
public abstract RegularFileProperty getOutputFile();
|
||||
|
||||
// ---
|
||||
|
||||
private static final Logger LOGGER = Logging.getLogger(ParseModDataTask.class);
|
||||
|
||||
private static final String S = ";";
|
||||
private static final String MOD_SEPARATOR = ",";
|
||||
|
||||
private static final Pattern[] CURSEFORGE_PATTERNS = {
|
||||
Pattern.compile("^/(minecraft|Minecraft|minecraft-bedrock)/(mc-mods|data-packs|modpacks|customization|mc-addons|texture-packs|customization/configuration|addons)/+(?<modid>[\\w-]+)(/(.*?))?$"),
|
||||
Pattern.compile("^/projects/(?<modid>[\\w-]+)(/(.*?))?$"),
|
||||
Pattern.compile("^/mc-mods/minecraft/(?<modid>[\\w-]+)(/(.*?))?$"),
|
||||
Pattern.compile("^/legacy/mc-mods/minecraft/(\\d+)-(?<modid>[\\w-]+)"),
|
||||
Pattern.compile("^//minecraft/mc-mods/(?<modid>eyes-mod)$") // Workaround for Eye Blocks
|
||||
};
|
||||
|
||||
private static String parseCurseforge(String url) {
|
||||
URI res = URI.create(url);
|
||||
|
||||
if (!"http".equals(res.getScheme())
|
||||
&& !"https".equals(res.getScheme())
|
||||
&& !"www.curseforge.com".equals(res.getHost())) { // Workaround for DakerACG
|
||||
return "";
|
||||
}
|
||||
|
||||
for (Pattern pattern : CURSEFORGE_PATTERNS) {
|
||||
Matcher matcher = pattern.matcher(res.getPath());
|
||||
if (matcher.matches()) {
|
||||
return matcher.group("modid");
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
private static final Pattern MCMOD_PATTERN =
|
||||
Pattern.compile("^https://www\\.mcmod\\.cn/(class|modpack)/(?<modid>\\d+)\\.html$");
|
||||
|
||||
private static String parseMcMod(String url) {
|
||||
Matcher matcher = MCMOD_PATTERN.matcher(url);
|
||||
if (matcher.matches()) {
|
||||
return matcher.group("modid");
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
private static final Set<String> skip = new HashSet<>(Arrays.asList(
|
||||
"Minecraft",
|
||||
"The Building Game"
|
||||
));
|
||||
|
||||
@TaskAction
|
||||
public void run() throws IOException {
|
||||
Path inputFile = getInputFile().get().getAsFile().toPath().toAbsolutePath();
|
||||
Path outputFile = getOutputFile().get().getAsFile().toPath().toAbsolutePath();
|
||||
|
||||
Files.createDirectories(outputFile.getParent());
|
||||
|
||||
|
||||
List<ModData> modDatas;
|
||||
try (BufferedReader reader = Files.newBufferedReader(inputFile)) {
|
||||
modDatas = new Gson().fromJson(reader, TypeToken.getParameterized(List.class, ModData.class).getType());
|
||||
}
|
||||
|
||||
try (BufferedWriter writer = Files.newBufferedWriter(outputFile, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE)) {
|
||||
writer.write("#\n" +
|
||||
"# Hello Minecraft! Launcher\n" +
|
||||
"# Copyright (C) 2025 huangyuhui <huanghongxun2008@126.com> and contributors\n" +
|
||||
"#\n" +
|
||||
"# mcmod.cn\n" +
|
||||
"# Copyright (C) 2025. All Rights Reserved.\n" +
|
||||
"#\n");
|
||||
for (ModData mod : modDatas) {
|
||||
String chineseName = mod.name.main;
|
||||
String subName = mod.name.sub;
|
||||
String abbr = mod.name.abbr;
|
||||
|
||||
if (chineseName == null)
|
||||
chineseName = "";
|
||||
if (subName == null)
|
||||
subName = "";
|
||||
if (abbr == null)
|
||||
abbr = "";
|
||||
|
||||
if (skip.contains(subName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (chineseName.contains(S) || subName.contains(S)) {
|
||||
throw new GradleException("Error: " + chineseName);
|
||||
}
|
||||
|
||||
String curseforgeId = "";
|
||||
String mcmodId = "";
|
||||
|
||||
Map<String, List<ModData.Link>> links = mod.links.list;
|
||||
List<ModData.Link> curseforgeLinks = links.get("curseforge");
|
||||
List<ModData.Link> mcmodLinks = links.get("mcmod");
|
||||
|
||||
if (curseforgeLinks != null && !curseforgeLinks.isEmpty()) {
|
||||
for (ModData.Link link : curseforgeLinks) {
|
||||
curseforgeId = parseCurseforge(link.url);
|
||||
if (!curseforgeId.isEmpty()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (curseforgeId.isEmpty()) {
|
||||
LOGGER.warn("Error curseforge: {}", chineseName);
|
||||
}
|
||||
}
|
||||
|
||||
if (mcmodLinks != null && !mcmodLinks.isEmpty()) {
|
||||
mcmodId = parseMcMod(mcmodLinks.get(0).url);
|
||||
if (mcmodId.isEmpty()) {
|
||||
throw new GradleException("Error mcmod: " + chineseName);
|
||||
}
|
||||
}
|
||||
|
||||
List<String> modId = new ArrayList<>();
|
||||
if (mod.modid != null) {
|
||||
for (String id : mod.modid) {
|
||||
if (id.contains(MOD_SEPARATOR)) {
|
||||
throw new GradleException("Error modid: " + id);
|
||||
}
|
||||
|
||||
modId.add(id);
|
||||
}
|
||||
}
|
||||
|
||||
String modIds = String.join(MOD_SEPARATOR, modId);
|
||||
|
||||
writer.write(curseforgeId + S + mcmodId + S + modIds + S + chineseName + S + subName + S + abbr + "\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static final class ModData {
|
||||
|
||||
public Name name;
|
||||
|
||||
@JsonAdapter(ModIdDeserializer.class)
|
||||
public List<String> modid;
|
||||
|
||||
public Links links;
|
||||
|
||||
public static final class Name {
|
||||
public String main;
|
||||
public String sub;
|
||||
public String abbr;
|
||||
}
|
||||
|
||||
public static final class Link {
|
||||
public String url;
|
||||
public String content;
|
||||
}
|
||||
|
||||
public static final class Links {
|
||||
public Map<String, List<Link>> list;
|
||||
}
|
||||
|
||||
|
||||
public static final class ModIdDeserializer implements JsonDeserializer<List<String>> {
|
||||
private static final Type STRING_LIST = TypeToken.getParameterized(List.class, String.class).getType();
|
||||
|
||||
@Override
|
||||
public List<String> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
|
||||
if (json.isJsonNull()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (json.isJsonArray()) {
|
||||
return context.deserialize(json, STRING_LIST);
|
||||
} else {
|
||||
JsonObject jsonObject = json.getAsJsonObject();
|
||||
JsonElement list = jsonObject.get("list");
|
||||
if (list == null) {
|
||||
return null;
|
||||
} else {
|
||||
return context.deserialize(list, STRING_LIST);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,3 @@
|
||||
buildscript {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath("com.google.code.gson:gson:2.11.0")
|
||||
}
|
||||
}
|
||||
|
||||
val jfxVersion = "17.0.13"
|
||||
val oldJfxVersion = "19.0.2.1"
|
||||
|
||||
|
||||
Reference in new issue
Block a user