Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,12 @@ tasks.test {
}

tasks.processResources {
filesMatching("**") {
filesMatching("**/*.json") {
expand(
"version" to project.version,
)
}
filesMatching("**/*.mod.json") {
expand(
"version" to project.version,
)
Expand Down
37 changes: 36 additions & 1 deletion src/main/java/i18nupdatemod/I18nUpdateMod.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import i18nupdatemod.core.ResourcePack;
import i18nupdatemod.core.ResourcePackConverter;
import i18nupdatemod.entity.GameAssetDetail;
import i18nupdatemod.core.LoadDetailUI;
import i18nupdatemod.entity.LoadStage;
import i18nupdatemod.util.FileUtil;
import i18nupdatemod.util.Log;

Expand All @@ -24,10 +26,13 @@
public class I18nUpdateMod {
public static final String MOD_ID = "i18nupdatemod";
public static String MOD_VERSION;

public static volatile boolean shouldShutdown = false;
public static final Gson GSON = new Gson();

public static void init(Path minecraftPath, String minecraftVersion, String loader) {
LoadDetailUI.show();
LoadDetailUI.setStage(LoadStage.INIT);

try (InputStream is = I18nUpdateMod.class.getResourceAsStream("/i18nMetaData.json")) {
MOD_VERSION = GSON.fromJson(new InputStreamReader(is), JsonObject.class).get("version").getAsString();
} catch (Exception e) {
Expand All @@ -47,6 +52,11 @@ public static void init(Path minecraftPath, String minecraftVersion, String load
Log.warning("I18nUpdateMod会从互联网获取内容不可控的资源包。");
Log.warning("这一行为违背了网易我的世界「开发者内容审核制度」:禁止上传与提审内容不一致的游戏内容。");
Log.warning("为了遵循这一制度,I18nUpdateMod不会下载任何内容。");

LoadDetailUI.appendLog("I18nUpdateMod会从互联网获取内容不可控的资源包。");
LoadDetailUI.appendLog("这一行为违背了网易我的世界「开发者内容审核制度」:禁止上传与提审内容不一致的游戏内容。");
LoadDetailUI.appendLog("为了遵循这一制度,I18nUpdateMod不会下载任何内容。");
LoadDetailUI.appendLog("请您手动关闭此窗口");
return;
} catch (ClassNotFoundException ignored) {
}
Expand All @@ -57,36 +67,61 @@ public static void init(Path minecraftPath, String minecraftVersion, String load

try {
//Get asset
if (shouldShutdown) {
return;
}
GameAssetDetail assets = I18nConfig.getAssetDetail(minecraftVersion, loader);

//Update resource pack
LoadDetailUI.setStage(LoadStage.DOWNLOAD_ASSET);
if (shouldShutdown) {
return;
}
List<ResourcePack> languagePacks = new ArrayList<>();
boolean convertNotNeed = assets.downloads.size() == 1 && assets.downloads.get(0).targetVersion.equals(minecraftVersion);
String applyFileName = assets.downloads.get(0).fileName;
for (GameAssetDetail.AssetDownloadDetail it : assets.downloads) {
if (shouldShutdown) {
return;
}
FileUtil.setTemporaryDirPath(Paths.get(localStorage, "." + MOD_ID, it.targetVersion));
ResourcePack languagePack = new ResourcePack(it.fileName, convertNotNeed);
languagePack.checkUpdate(it.fileUrl, it.md5Url);
languagePacks.add(languagePack);
}

//Convert resourcepack
LoadDetailUI.setStage(LoadStage.CONVERT_RESOURCE_PACK);
if (shouldShutdown){
Copy link

Copilot AI Nov 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inconsistent spacing in the if statement condition. Line 95 has if (shouldShutdown){ (no space before {), while line 77 has if (shouldShutdown) { (with space). This should be consistent throughout the file. Add a space before the opening brace: if (shouldShutdown) {

Copilot uses AI. Check for mistakes.
return;
}
if (!convertNotNeed) {
FileUtil.setTemporaryDirPath(Paths.get(localStorage, "." + MOD_ID, minecraftVersion));
applyFileName = assets.covertFileName;
ResourcePackConverter converter = new ResourcePackConverter(languagePacks, applyFileName);
converter.convert(assets.covertPackFormat, getResourcePackDescription(assets.downloads));
}
LoadDetailUI.appendLog("资源包已转换完成。");

//Apply resource pack
LoadDetailUI.setStage(LoadStage.APPLY_RESOURCE_PACK);
if (shouldShutdown){
return;
}
GameConfig config = new GameConfig(minecraftPath.resolve("options.txt"));
config.addResourcePack("Minecraft-Mod-Language-Modpack",
(minecraftMajorVersion <= 12 ? "" : "file/") + applyFileName);
config.writeToFile();
LoadDetailUI.appendLog("资源包已应用。");
LoadDetailUI.setStage(LoadStage.FINISH);
} catch (Exception e) {
Log.warning(String.format("Failed to update resource pack: %s", e));
LoadDetailUI.appendLog(String.format("I18n Update Mod 运行失败: %s", e));
LoadDetailUI.appendLog("请您手动关闭此窗口");
return;
// e.printStackTrace();
}
LoadDetailUI.hide();
}

private static String getResourcePackDescription(List<GameAssetDetail.AssetDownloadDetail> downloads) {
Expand Down
7 changes: 3 additions & 4 deletions src/main/java/i18nupdatemod/core/I18nConfig.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
package i18nupdatemod.core;

import com.google.gson.Gson;
import i18nupdatemod.entity.AssetMetaData;
import i18nupdatemod.entity.GameAssetDetail;
import i18nupdatemod.entity.GameMetaData;
import i18nupdatemod.entity.I18nMetaData;
import i18nupdatemod.entity.*;
import i18nupdatemod.util.Log;
import i18nupdatemod.util.Version;
import i18nupdatemod.util.VersionRange;
Expand Down Expand Up @@ -62,8 +59,10 @@ public static GameAssetDetail getAssetDetail(String minecraftVersion, String loa
GameMetaData convert = getGameMetaData(minecraftVersion);
GameAssetDetail ret = new GameAssetDetail();

LoadDetailUI.appendLog("正在获取最快的镜像源...");
String assetRoot = getFastestUrl();
Log.debug("Using asset root: " + assetRoot);
LoadDetailUI.appendLog("即将从"+assetRoot+"下载资源包");

if (assetRoot.equals("https://raw.githubusercontent.com/")) {
ret.downloads = createDownloadDetailsFromGit(convert, loader);
Expand Down
152 changes: 152 additions & 0 deletions src/main/java/i18nupdatemod/core/LoadDetailUI.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
package i18nupdatemod.core;

import i18nupdatemod.I18nUpdateMod;
import i18nupdatemod.entity.LoadStage;
import i18nupdatemod.util.Log;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.net.URL;

public class LoadDetailUI {
private static volatile LoadDetailUI instance;
private final JFrame frame;
private final JProgressBar statusBar;
private final JTextArea logArea;
private final boolean useGUI;

private LoadDetailUI() {
useGUI = !Boolean.parseBoolean(System.getProperty("java.awt.headless", "false"));

if (!useGUI) {
frame = null;
statusBar = null;
logArea = null;
return;
}

frame = new JFrame();
frame.setTitle("I18nUpdateMod-资源包下载进度");
frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
frame.setSize(854, 480);
frame.setLocationRelativeTo(null);
frame.setLayout(new BorderLayout());
frame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
shutdown();
}
});


URL iconURL = getClass().getResource("/icons/CFPA.png");
if (iconURL != null) {
Image icon = Toolkit.getDefaultToolkit().getImage(iconURL);
frame.setIconImage(icon);
}

// 主面板
JPanel panel = new JPanel();
panel.setBackground(new Color(220, 220, 220));
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
panel.setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15));

// 状态栏
statusBar = new JProgressBar();
statusBar.setString("正在合并资源包");
statusBar.setStringPainted(true);
statusBar.setMaximum(LoadStage.values().length - 1);
statusBar.setValue(100);
statusBar.setForeground(new Color(102, 255, 102));
panel.add(statusBar);
panel.add(Box.createVerticalStrut(10));

// 日志输出区
logArea = new JTextArea(6, 30);
logArea.setEditable(false);
logArea.setFont(new Font("Monospaced", Font.PLAIN, 13));
JScrollPane scrollPane = new JScrollPane(logArea);
scrollPane.setBorder(BorderFactory.createLineBorder(Color.GRAY));
panel.add(scrollPane);
panel.add(Box.createVerticalStrut(10));

// 提示文字
JLabel tip = new JLabel("如遇到进度卡住,可以点击下方按钮/关闭此窗口停止此次下载。");
tip.setAlignmentX(Component.CENTER_ALIGNMENT);
panel.add(tip);
panel.add(Box.createVerticalStrut(10));

// 停止按钮
JButton stopButton = new JButton("停止此次下载");
stopButton.setFocusPainted(false);
stopButton.setBackground(Color.WHITE);
stopButton.setAlignmentX(Component.CENTER_ALIGNMENT);
stopButton.addActionListener((ActionEvent e) -> shutdown());
panel.add(stopButton);

frame.add(panel, BorderLayout.CENTER);
}

public static LoadDetailUI getInstance() {
if (instance == null) {
synchronized (LoadDetailUI.class) {
if (instance == null) {
instance = new LoadDetailUI();
}
}
}
return instance;
}

public static void show() {
LoadDetailUI gui = getInstance();
if (!gui.useGUI || gui.frame == null) {
return;
}
gui.frame.setVisible(true);
}

public static void hide() {
LoadDetailUI gui = getInstance();
if (!gui.useGUI || gui.frame == null) {
return;
}
gui.frame.setVisible(false);
}

private void shutdown(){
I18nUpdateMod.shouldShutdown = true;
Log.info("User shutdown task");
if (!useGUI) {
return;
}
hide();
}

public static void setStage(LoadStage stage) {
LoadDetailUI gui = getInstance();
if (!gui.useGUI || gui.statusBar == null || gui.logArea == null) {
return;
}
SwingUtilities.invokeLater(() -> {
gui.statusBar.setString(LoadStage.getDescription(stage));
gui.statusBar.setValue(stage.getValue());
gui.logArea.append("当前阶段: " + LoadStage.getDescription(stage) + "\n");
gui.logArea.setCaretPosition(gui.logArea.getDocument().getLength());
});
}

public static void appendLog(String log) {
LoadDetailUI gui = getInstance();
if (!gui.useGUI || gui.logArea == null) {
return;
}
SwingUtilities.invokeLater(() -> {
gui.logArea.append(log + "\n");
gui.logArea.setCaretPosition(gui.logArea.getDocument().getLength());
});
}
}
3 changes: 3 additions & 0 deletions src/main/java/i18nupdatemod/core/ResourcePack.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,14 @@ public ResourcePack(String filename, boolean saveToGame) {

public void checkUpdate(String fileUrl, String md5Url) throws IOException, URISyntaxException, NoSuchAlgorithmException {
if (isUpToDate(md5Url)) {
LoadDetailUI.appendLog(filename + "无需更新");
Log.debug("Already up to date.");
return;
}
//In this time, we can only download full file
LoadDetailUI.appendLog("正在下载" + filename);
downloadFull(fileUrl, md5Url);
LoadDetailUI.appendLog(filename + "下载完成");
//In the future, we will download patch file and merge local file
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ public void convert(int packFormat, String description) throws Exception {
// zos.setMethod(ZipOutputStream.STORED);
for (Path p : sourcePath) {
Log.info("Converting: " + p);
LoadDetailUI.appendLog("正在转换" + p);
try (ZipFile zf = new ZipFile(p.toFile(), StandardCharsets.UTF_8)) {
for (Enumeration<? extends ZipEntry> e = zf.entries(); e.hasMoreElements(); ) {
ZipEntry ze = e.nextElement();
Expand Down
36 changes: 36 additions & 0 deletions src/main/java/i18nupdatemod/entity/LoadStage.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package i18nupdatemod.entity;

public enum LoadStage{
INIT(0),
DOWNLOAD_ASSET(1),
CONVERT_RESOURCE_PACK(2),
APPLY_RESOURCE_PACK(3),
FINISH(4);

private final int value;

LoadStage(int value) {
this.value = value;
}

public int getValue() {
return value;
}

public static String getDescription(LoadStage stage) {
switch (stage){
case INIT:
return "初始化";
case DOWNLOAD_ASSET:
return "更新资源包";
case CONVERT_RESOURCE_PACK:
return "转换资源包";
case APPLY_RESOURCE_PACK:
return "应用资源包";
case FINISH:
return "完成";
default:
return "未知";
}
}
}
Binary file added src/main/resources/icons/CFPA.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/main/resources/icons/CFPA_with_title.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.