From 70461dbde3012d63a0d8de94ca990142a77e90b1 Mon Sep 17 00:00:00 2001 From: ShulkerSakura <2531493755@qq.com> Date: Fri, 28 Mar 2025 11:27:12 +0800 Subject: [PATCH 01/13] AssetsCleaner --- .../jackhuang/hmcl/ui/main/SettingsView.java | 8 +- .../shulker/assetscleaner/AssetsCleaner.java | 79 +++++++++++++++++++ .../shulker/assetscleaner/TestCleaner.java | 7 ++ 3 files changed, 93 insertions(+), 1 deletion(-) create mode 100644 HMCLCore/src/main/java/neko/shulker/assetscleaner/AssetsCleaner.java create mode 100644 HMCLCore/src/main/java/neko/shulker/assetscleaner/TestCleaner.java diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsView.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsView.java index 5f57b36a0..e67ed3c01 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsView.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsView.java @@ -30,6 +30,7 @@ import javafx.scene.layout.*; import javafx.scene.text.Text; import javafx.scene.text.TextAlignment; import javafx.scene.text.TextFlow; +import neko.shulker.assetscleaner.TestCleaner; import org.jackhuang.hmcl.Metadata; import org.jackhuang.hmcl.setting.EnumCommonDirectory; import org.jackhuang.hmcl.setting.Theme; @@ -196,13 +197,18 @@ public abstract class SettingsView extends StackPane { openLogFolderButton.setOnAction(e -> openLogFolder()); openLogFolderButton.getStyleClass().add("jfx-button-border"); + // 测试Clean + JFXButton testCleanerButton = new JFXButton(i18n("testCleaner")); + testCleanerButton.setOnAction(e -> {TestCleaner.test();}); + testCleanerButton.getStyleClass().add("jfx-button-border"); + JFXButton logButton = new JFXButton(i18n("settings.launcher.launcher_log.export")); logButton.setOnAction(e -> onExportLogs()); logButton.getStyleClass().add("jfx-button-border"); HBox buttonBox = new HBox(); buttonBox.setSpacing(10); - buttonBox.getChildren().addAll(openLogFolderButton, logButton); + buttonBox.getChildren().addAll(openLogFolderButton, testCleanerButton, logButton); BorderPane.setAlignment(buttonBox, Pos.CENTER_RIGHT); debugPane.setRight(buttonBox); diff --git a/HMCLCore/src/main/java/neko/shulker/assetscleaner/AssetsCleaner.java b/HMCLCore/src/main/java/neko/shulker/assetscleaner/AssetsCleaner.java new file mode 100644 index 000000000..fb02b3e26 --- /dev/null +++ b/HMCLCore/src/main/java/neko/shulker/assetscleaner/AssetsCleaner.java @@ -0,0 +1,79 @@ +package neko.shulker.assetscleaner; + +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; + +import java.io.FileReader; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.HashMap; +import java.util.Map; +import java.lang.reflect.Type; + +public class AssetsCleaner { + public static void clean(String indexesDirectory, String assetsDirectory) { + // 用于存储所有 index 中 hash 键值的 Map + Map> hashMap = new HashMap<>(); + + try { + // 遍历 indexesDirectory 目录下的所有 JSON 文件 + Path dirPath = Paths.get(indexesDirectory); + Files.walk(dirPath) + .filter(Files::isRegularFile) + .filter(path -> path.toString().endsWith(".json")) + .forEach(jsonFile -> { + try (FileReader reader = new FileReader(jsonFile.toString())) { + // 解析每个 JSON 文件 + Gson gson = new Gson(); + Type type = new TypeToken>>>() {}.getType(); + Map>> jsonData = gson.fromJson(reader, type); + + // 提取 hash 的键值 + Map> objects = jsonData.get("objects"); + if (objects != null) { + for (Map.Entry> entry : objects.entrySet()) { + String filePath = entry.getKey(); + Map fileData = entry.getValue(); + String hash = (String) fileData.get("hash"); + if (hash != null && !hashMap.containsKey(hash)) { + hashMap.put(hash, fileData); + } + } + } + } catch (IOException e) { + System.err.println("[AssetsCleaner]: Error reading JSON file: " + jsonFile); + e.printStackTrace(); + } + }); + + // 输出 hashMap 中保存的所有 hash 值 + System.out.println("[AssetsCleaner]: 所有 hash 值:"); + for (String hash : hashMap.keySet()) { + System.out.println(hash); + } + + // 遍历目标处理目录文件 + Path assetsDirPath = Paths.get(assetsDirectory); + Files.walk(assetsDirPath) + .filter(Files::isRegularFile) + .forEach(file -> { + String fileName = file.getFileName().toString(); + // 判断文件是否在 hashMap 中 + if (!hashMap.containsKey(fileName)) { + // 如果不在则删除 + try { + Files.delete(file); + System.out.println("[AssetsCleaner]: Deleted file: " + file); + } catch (IOException e) { + System.err.println("[AssetsCleaner]: Error deleting file: " + file); + e.printStackTrace(); + } + } + }); + } catch (IOException e) { + e.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/HMCLCore/src/main/java/neko/shulker/assetscleaner/TestCleaner.java b/HMCLCore/src/main/java/neko/shulker/assetscleaner/TestCleaner.java new file mode 100644 index 000000000..0d1d9e5f1 --- /dev/null +++ b/HMCLCore/src/main/java/neko/shulker/assetscleaner/TestCleaner.java @@ -0,0 +1,7 @@ +package neko.shulker.assetscleaner; + +public class TestCleaner { + public static void test(){ + AssetsCleaner.clean("/home/shulker/ttt/assets/indexes/", "/home/shulker/ttt/assets/objects"); + } +} From 669dd6edd87ba8e5f47d08d504fa3ae3adab102a Mon Sep 17 00:00:00 2001 From: ShulkerSakura <2531493755@qq.com> Date: Fri, 28 Mar 2025 12:41:53 +0800 Subject: [PATCH 02/13] Style Check --- .../java/org/jackhuang/hmcl/ui/main/SettingsView.java | 8 +------- .../java/neko/shulker/assetscleaner/AssetsCleaner.java | 4 ++-- .../main/java/neko/shulker/assetscleaner/TestCleaner.java | 4 ++-- 3 files changed, 5 insertions(+), 11 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsView.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsView.java index e67ed3c01..5f57b36a0 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsView.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsView.java @@ -30,7 +30,6 @@ import javafx.scene.layout.*; import javafx.scene.text.Text; import javafx.scene.text.TextAlignment; import javafx.scene.text.TextFlow; -import neko.shulker.assetscleaner.TestCleaner; import org.jackhuang.hmcl.Metadata; import org.jackhuang.hmcl.setting.EnumCommonDirectory; import org.jackhuang.hmcl.setting.Theme; @@ -197,18 +196,13 @@ public abstract class SettingsView extends StackPane { openLogFolderButton.setOnAction(e -> openLogFolder()); openLogFolderButton.getStyleClass().add("jfx-button-border"); - // 测试Clean - JFXButton testCleanerButton = new JFXButton(i18n("testCleaner")); - testCleanerButton.setOnAction(e -> {TestCleaner.test();}); - testCleanerButton.getStyleClass().add("jfx-button-border"); - JFXButton logButton = new JFXButton(i18n("settings.launcher.launcher_log.export")); logButton.setOnAction(e -> onExportLogs()); logButton.getStyleClass().add("jfx-button-border"); HBox buttonBox = new HBox(); buttonBox.setSpacing(10); - buttonBox.getChildren().addAll(openLogFolderButton, testCleanerButton, logButton); + buttonBox.getChildren().addAll(openLogFolderButton, logButton); BorderPane.setAlignment(buttonBox, Pos.CENTER_RIGHT); debugPane.setRight(buttonBox); diff --git a/HMCLCore/src/main/java/neko/shulker/assetscleaner/AssetsCleaner.java b/HMCLCore/src/main/java/neko/shulker/assetscleaner/AssetsCleaner.java index fb02b3e26..d1394aab8 100644 --- a/HMCLCore/src/main/java/neko/shulker/assetscleaner/AssetsCleaner.java +++ b/HMCLCore/src/main/java/neko/shulker/assetscleaner/AssetsCleaner.java @@ -12,7 +12,7 @@ import java.util.HashMap; import java.util.Map; import java.lang.reflect.Type; -public class AssetsCleaner { +class AssetsCleaner { public static void clean(String indexesDirectory, String assetsDirectory) { // 用于存储所有 index 中 hash 键值的 Map Map> hashMap = new HashMap<>(); @@ -76,4 +76,4 @@ public class AssetsCleaner { e.printStackTrace(); } } -} \ No newline at end of file +} diff --git a/HMCLCore/src/main/java/neko/shulker/assetscleaner/TestCleaner.java b/HMCLCore/src/main/java/neko/shulker/assetscleaner/TestCleaner.java index 0d1d9e5f1..d17c9060e 100644 --- a/HMCLCore/src/main/java/neko/shulker/assetscleaner/TestCleaner.java +++ b/HMCLCore/src/main/java/neko/shulker/assetscleaner/TestCleaner.java @@ -1,7 +1,7 @@ package neko.shulker.assetscleaner; -public class TestCleaner { - public static void test(){ +class TestCleaner { + public static void test() { AssetsCleaner.clean("/home/shulker/ttt/assets/indexes/", "/home/shulker/ttt/assets/objects"); } } From eeb4303ec075837fcf964b4e4fafbc39269087a3 Mon Sep 17 00:00:00 2001 From: ShulkerSakura <2531493755@qq.com> Date: Fri, 28 Mar 2025 13:08:29 +0800 Subject: [PATCH 03/13] Style Check --- .../java/neko/shulker/assetscleaner/AssetsCleaner.java | 5 ++++- .../main/java/neko/shulker/assetscleaner/TestCleaner.java | 7 ------- 2 files changed, 4 insertions(+), 8 deletions(-) delete mode 100644 HMCLCore/src/main/java/neko/shulker/assetscleaner/TestCleaner.java diff --git a/HMCLCore/src/main/java/neko/shulker/assetscleaner/AssetsCleaner.java b/HMCLCore/src/main/java/neko/shulker/assetscleaner/AssetsCleaner.java index d1394aab8..3b91e875a 100644 --- a/HMCLCore/src/main/java/neko/shulker/assetscleaner/AssetsCleaner.java +++ b/HMCLCore/src/main/java/neko/shulker/assetscleaner/AssetsCleaner.java @@ -12,7 +12,10 @@ import java.util.HashMap; import java.util.Map; import java.lang.reflect.Type; -class AssetsCleaner { +public final class AssetsCleaner { + private AssetsCleaner( ) { + throw new UnsupportedOperationException(); + } public static void clean(String indexesDirectory, String assetsDirectory) { // 用于存储所有 index 中 hash 键值的 Map Map> hashMap = new HashMap<>(); diff --git a/HMCLCore/src/main/java/neko/shulker/assetscleaner/TestCleaner.java b/HMCLCore/src/main/java/neko/shulker/assetscleaner/TestCleaner.java deleted file mode 100644 index d17c9060e..000000000 --- a/HMCLCore/src/main/java/neko/shulker/assetscleaner/TestCleaner.java +++ /dev/null @@ -1,7 +0,0 @@ -package neko.shulker.assetscleaner; - -class TestCleaner { - public static void test() { - AssetsCleaner.clean("/home/shulker/ttt/assets/indexes/", "/home/shulker/ttt/assets/objects"); - } -} From 5146f4f337a0220b72ce50d4fe07b3c0c523a54c Mon Sep 17 00:00:00 2001 From: ShulkerSakura <2531493755@qq.com> Date: Fri, 28 Mar 2025 13:16:21 +0800 Subject: [PATCH 04/13] Style Check --- .../src/main/java/neko/shulker/assetscleaner/AssetsCleaner.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/HMCLCore/src/main/java/neko/shulker/assetscleaner/AssetsCleaner.java b/HMCLCore/src/main/java/neko/shulker/assetscleaner/AssetsCleaner.java index 3b91e875a..2debea3ae 100644 --- a/HMCLCore/src/main/java/neko/shulker/assetscleaner/AssetsCleaner.java +++ b/HMCLCore/src/main/java/neko/shulker/assetscleaner/AssetsCleaner.java @@ -13,9 +13,11 @@ import java.util.Map; import java.lang.reflect.Type; public final class AssetsCleaner { + private AssetsCleaner( ) { throw new UnsupportedOperationException(); } + public static void clean(String indexesDirectory, String assetsDirectory) { // 用于存储所有 index 中 hash 键值的 Map Map> hashMap = new HashMap<>(); From fa48ad7e4406fef4a9327a9d445149372ec3efec Mon Sep 17 00:00:00 2001 From: ShulkerSakura <2531493755@qq.com> Date: Sun, 30 Mar 2025 21:47:40 +0800 Subject: [PATCH 05/13] =?UTF-8?q?=E6=B7=BB=E5=8A=A0indexes=E6=B8=85?= =?UTF-8?q?=E7=90=86=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../shulker/assetscleaner/AssetsCleaner.java | 182 +++++++++++++----- 1 file changed, 132 insertions(+), 50 deletions(-) diff --git a/HMCLCore/src/main/java/neko/shulker/assetscleaner/AssetsCleaner.java b/HMCLCore/src/main/java/neko/shulker/assetscleaner/AssetsCleaner.java index 2debea3ae..6842ce9d8 100644 --- a/HMCLCore/src/main/java/neko/shulker/assetscleaner/AssetsCleaner.java +++ b/HMCLCore/src/main/java/neko/shulker/assetscleaner/AssetsCleaner.java @@ -3,55 +3,120 @@ package neko.shulker.assetscleaner; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; +import java.io.File; import java.io.FileReader; +import java.io.FilenameFilter; import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; +import java.io.Reader; import java.nio.file.Paths; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; -import java.lang.reflect.Type; public final class AssetsCleaner { - private AssetsCleaner( ) { + private AssetsCleaner() { throw new UnsupportedOperationException(); } - - public static void clean(String indexesDirectory, String assetsDirectory) { - // 用于存储所有 index 中 hash 键值的 Map - Map> hashMap = new HashMap<>(); - try { - // 遍历 indexesDirectory 目录下的所有 JSON 文件 - Path dirPath = Paths.get(indexesDirectory); - Files.walk(dirPath) - .filter(Files::isRegularFile) - .filter(path -> path.toString().endsWith(".json")) - .forEach(jsonFile -> { - try (FileReader reader = new FileReader(jsonFile.toString())) { - // 解析每个 JSON 文件 + private static List getIndexes(String versionsDirectory) { + List indexes = new ArrayList<>(); + File versionsDir = new File(versionsDirectory); + + // 遍历 versionsDirectory 中的所有文件夹 + if (versionsDir.exists() && versionsDir.isDirectory()) { + File[] versionFolders = versionsDir.listFiles(File::isDirectory); + if (versionFolders != null) { + for (File versionFolder : versionFolders) { + String versionName = versionFolder.getName(); + File jsonFile = new File(versionFolder, versionName + ".json"); + + if (jsonFile.exists() && jsonFile.isFile()) { + try (Reader reader = new FileReader(jsonFile)) { + // 解析 JSON 文件 Gson gson = new Gson(); - Type type = new TypeToken>>>() {}.getType(); - Map>> jsonData = gson.fromJson(reader, type); + Map versionData = gson.fromJson(reader, Map.class); - // 提取 hash 的键值 - Map> objects = jsonData.get("objects"); - if (objects != null) { - for (Map.Entry> entry : objects.entrySet()) { - String filePath = entry.getKey(); - Map fileData = entry.getValue(); - String hash = (String) fileData.get("hash"); - if (hash != null && !hashMap.containsKey(hash)) { - hashMap.put(hash, fileData); - } - } + // 提取 "assets" 的值 + Object assetsValue = versionData.get("assets"); + if (assetsValue instanceof String) { + String assets = (String) assetsValue; + indexes.add(assets); } } catch (IOException e) { System.err.println("[AssetsCleaner]: Error reading JSON file: " + jsonFile); e.printStackTrace(); } - }); + } + } + } + } + + return indexes; + } + + public static void clean(String repoDirectory) { + // 构造路径 + String versionsDirectory = Paths.get(repoDirectory, "versions").toString(); + String indexesDirectory = Paths.get(repoDirectory, "assets", "indexes").toString(); + String objectsDirectory = Paths.get(repoDirectory, "assets", "objects").toString(); + + // 获取 indexes + List requiredIndexes = getIndexes(versionsDirectory); + + // 用于存储所有 index 中 hash 键值的 Map + Map> hashMap = new HashMap<>(); + + try { + // 遍历 indexesDirectory 目录下的所有 JSON 文件 + File indexesDir = new File(indexesDirectory); + if (!indexesDir.exists() || !indexesDir.isDirectory()) { + System.err.println("[AssetsCleaner]: Indexes directory does not exist: " + indexesDir); + return; + } + + // 获取 indexesDirectory 中的所有 JSON 文件 + File[] indexFiles = indexesDir.listFiles((FilenameFilter) (dir, name) -> name.endsWith(".json")); + if (indexFiles != null) { + for (File indexFile : indexFiles) { + String indexFileName = indexFile.getName(); + String indexName = indexFileName.substring(0, indexFileName.lastIndexOf('.')); + + // 检查该 index 是否在 requiredIndexes 中 + if (!requiredIndexes.contains(indexName)) { + // 如果不在 requiredIndexes 中,则删除该 index 文件 + if (!indexFile.delete()) { + System.err.println("[AssetsCleaner]: Error deleting index file: " + indexFile); + } else { + System.out.println("[AssetsCleaner]: Deleted unnecessary index: " + indexFile); + } + continue; + } + + // 解析 JSON 文件 + try (Reader reader = new FileReader(indexFile)) { + Gson gson = new Gson(); + Map>> jsonData = gson.fromJson(reader, new TypeToken>>>() {}.getType()); + + // 提取 hash 的键值 + Map> objects = jsonData.get("objects"); + if (objects != null) { + for (Map.Entry> entry : objects.entrySet()) { + String filePath = entry.getKey(); + Map fileData = entry.getValue(); + String hash = (String) fileData.get("hash"); + if (hash != null && !hashMap.containsKey(hash)) { + hashMap.put(hash, fileData); + } + } + } + } catch (IOException e) { + System.err.println("[AssetsCleaner]: Error reading JSON file: " + indexFile); + e.printStackTrace(); + } + } + } // 输出 hashMap 中保存的所有 hash 值 System.out.println("[AssetsCleaner]: 所有 hash 值:"); @@ -60,25 +125,42 @@ public final class AssetsCleaner { } // 遍历目标处理目录文件 - Path assetsDirPath = Paths.get(assetsDirectory); - Files.walk(assetsDirPath) - .filter(Files::isRegularFile) - .forEach(file -> { - String fileName = file.getFileName().toString(); - // 判断文件是否在 hashMap 中 - if (!hashMap.containsKey(fileName)) { - // 如果不在则删除 - try { - Files.delete(file); - System.out.println("[AssetsCleaner]: Deleted file: " + file); - } catch (IOException e) { - System.err.println("[AssetsCleaner]: Error deleting file: " + file); - e.printStackTrace(); - } - } - }); - } catch (IOException e) { + File objectsDir = new File(objectsDirectory); + if (!objectsDir.exists() || !objectsDir.isDirectory()) { + System.err.println("[AssetsCleaner]: Objects directory does not exist: " + objectsDir); + return; + } + + // 递归遍历 objectsDirectory + traverseAndCleanDirectory(objectsDir, hashMap); + } catch (Exception e) { e.printStackTrace(); } } -} + + private static void traverseAndCleanDirectory(File directory, Map> hashMap) { + if (!directory.exists() || !directory.isDirectory()) { + return; + } + + File[] files = directory.listFiles(); + if (files != null) { + for (File file : files) { + if (file.isDirectory()) { + traverseAndCleanDirectory(file, hashMap); + } else { + String fileName = file.getName(); + // 判断文件是否在 hashMap 中 + if (!hashMap.containsKey(fileName)) { + // 如果不在则删除 + if (!file.delete()) { + System.err.println("[AssetsCleaner]: Error deleting file: " + file); + } else { + System.out.println("[AssetsCleaner]: Deleted file: " + file); + } + } + } + } + } + } +} \ No newline at end of file From 2317b6951443b857bb2dd6b3a716fbf228bc3bd0 Mon Sep 17 00:00:00 2001 From: ShulkerSakura <2531493755@qq.com> Date: Sun, 30 Mar 2025 21:52:45 +0800 Subject: [PATCH 06/13] =?UTF-8?q?=E6=B7=BB=E5=8A=A0indexes=E6=B8=85?= =?UTF-8?q?=E7=90=86=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/neko/shulker/assetscleaner/AssetsCleaner.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HMCLCore/src/main/java/neko/shulker/assetscleaner/AssetsCleaner.java b/HMCLCore/src/main/java/neko/shulker/assetscleaner/AssetsCleaner.java index 6842ce9d8..b2386b3ca 100644 --- a/HMCLCore/src/main/java/neko/shulker/assetscleaner/AssetsCleaner.java +++ b/HMCLCore/src/main/java/neko/shulker/assetscleaner/AssetsCleaner.java @@ -163,4 +163,4 @@ public final class AssetsCleaner { } } } -} \ No newline at end of file +} From bbc36edfc0c9679ec2d13dfabe678a580c96bf89 Mon Sep 17 00:00:00 2001 From: ShulkerSakura <2531493755@qq.com> Date: Thu, 17 Jul 2025 14:25:52 +0800 Subject: [PATCH 07/13] Added username length detection --- .../org/jackhuang/hmcl/ui/account/CreateAccountPane.java | 5 ++++- HMCL/src/main/resources/assets/lang/I18N.properties | 2 +- HMCL/src/main/resources/assets/lang/I18N_zh.properties | 2 +- HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties | 2 +- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/CreateAccountPane.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/CreateAccountPane.java index ff04f3bba..893fb275d 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/CreateAccountPane.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/CreateAccountPane.java @@ -267,7 +267,10 @@ public class CreateAccountPane extends JFXDialogLayout implements DialogAware { }).executor(true); }; - if (factory instanceof OfflineAccountFactory && username != null && !USERNAME_CHECKER_PATTERN.matcher(username).matches()) { + if (factory instanceof OfflineAccountFactory && username != null && !USERNAME_CHECKER_PATTERN.matcher(username).matches() || username.length() > 16) { + System.out.println(username); + System.out.println("USERNAME LENGTH: " + username.length()); + System.out.println(username.length() > 16); JFXButton btnYes = new JFXButton(i18n("button.ok")); btnYes.getStyleClass().add("dialog-error"); btnYes.setOnAction(e -> doCreate.run()); diff --git a/HMCL/src/main/resources/assets/lang/I18N.properties b/HMCL/src/main/resources/assets/lang/I18N.properties index 09f9932cc..4b2d9a1b0 100644 --- a/HMCL/src/main/resources/assets/lang/I18N.properties +++ b/HMCL/src/main/resources/assets/lang/I18N.properties @@ -124,7 +124,7 @@ account.methods.microsoft.purchase=Buy Minecraft account.methods.microsoft.snapshot=You are using an unofficial build of HMCL. Please download the official build to log in. account.methods.microsoft.snapshot.website=Official Website account.methods.offline=Offline -account.methods.offline.name.special_characters=Using only English letters, numbers, and underscores is recommended +account.methods.offline.name.special_characters=Use only letters, numbers, and underscores (max 16 chars). account.methods.offline.name.invalid=It is recommended to use only English letters, numbers and underscores for the username, and the length should not exceed 16 characters.\n\ \n\ \ · Legitimate: HuangYu, huang_Yu, Huang_Yu_123;\n\ diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh.properties b/HMCL/src/main/resources/assets/lang/I18N_zh.properties index 4a7459a84..751178533 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh.properties @@ -131,7 +131,7 @@ account.methods.ban_query=查詢帳戶是否被封禁 account.methods.microsoft.snapshot=你正在使用第三方提供的 HMCL。請下載官方版本進行登入。 account.methods.microsoft.snapshot.website=官方網站 account.methods.offline=離線模式 -account.methods.offline.name.special_characters=建議使用英文字母、數字以及底線命名 +account.methods.offline.name.special_characters=建議使用英文字母、數字以及底線命名,且長度不超過 16 個字元。 account.methods.offline.name.invalid=遊戲使用者名稱建議僅使用英文字母、數字及底線,且長度不超過 16 個字元。\n\ \n\ \ · 一些有效的使用者名稱:HuangYu、huang_Yu、Huang_Yu_123;\n\ diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties index b39b8064b..e438cc5bd 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties @@ -139,7 +139,7 @@ account.methods.ban_query=检测账户是否被封禁 account.methods.microsoft.snapshot=你正在使用第三方提供的 HMCL。请下载官方版本来登录微软账户。 account.methods.microsoft.snapshot.website=官方网站 account.methods.offline=离线模式 -account.methods.offline.name.special_characters=建议使用英文字符、数字以及下划线命名 +account.methods.offline.name.special_characters=建议使用英文字符、数字以及下划线命名,且长度不超过 16 个字符。 account.methods.offline.name.invalid=游戏用户名建议仅使用英文字母、数字及下划线,且长度不超过 16 个字符。\n\ \n\ \ · 一些合法用户名:HuangYu、huang_Yu、Huang_Yu_123;\n\ From 447583d07c584ec981eb88be0789c8bd4aa46709 Mon Sep 17 00:00:00 2001 From: ShulkerSakura <2531493755@qq.com> Date: Thu, 17 Jul 2025 14:31:17 +0800 Subject: [PATCH 08/13] Delete test code --- .../java/org/jackhuang/hmcl/ui/account/CreateAccountPane.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/CreateAccountPane.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/CreateAccountPane.java index 893fb275d..11756f8bc 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/CreateAccountPane.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/CreateAccountPane.java @@ -268,9 +268,6 @@ public class CreateAccountPane extends JFXDialogLayout implements DialogAware { }; if (factory instanceof OfflineAccountFactory && username != null && !USERNAME_CHECKER_PATTERN.matcher(username).matches() || username.length() > 16) { - System.out.println(username); - System.out.println("USERNAME LENGTH: " + username.length()); - System.out.println(username.length() > 16); JFXButton btnYes = new JFXButton(i18n("button.ok")); btnYes.getStyleClass().add("dialog-error"); btnYes.setOnAction(e -> doCreate.run()); From 052a950ed386d755f3020c4f85d4666abd484a11 Mon Sep 17 00:00:00 2001 From: ShulkerSakura <2531493755@qq.com> Date: Thu, 17 Jul 2025 14:47:32 +0800 Subject: [PATCH 09/13] Deleted testing asstes cleaner module --- .../shulker/assetscleaner/AssetsCleaner.java | 166 ------------------ 1 file changed, 166 deletions(-) delete mode 100644 HMCLCore/src/main/java/neko/shulker/assetscleaner/AssetsCleaner.java diff --git a/HMCLCore/src/main/java/neko/shulker/assetscleaner/AssetsCleaner.java b/HMCLCore/src/main/java/neko/shulker/assetscleaner/AssetsCleaner.java deleted file mode 100644 index b2386b3ca..000000000 --- a/HMCLCore/src/main/java/neko/shulker/assetscleaner/AssetsCleaner.java +++ /dev/null @@ -1,166 +0,0 @@ -package neko.shulker.assetscleaner; - -import com.google.gson.Gson; -import com.google.gson.reflect.TypeToken; - -import java.io.File; -import java.io.FileReader; -import java.io.FilenameFilter; -import java.io.IOException; -import java.io.Reader; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -public final class AssetsCleaner { - - private AssetsCleaner() { - throw new UnsupportedOperationException(); - } - - private static List getIndexes(String versionsDirectory) { - List indexes = new ArrayList<>(); - File versionsDir = new File(versionsDirectory); - - // 遍历 versionsDirectory 中的所有文件夹 - if (versionsDir.exists() && versionsDir.isDirectory()) { - File[] versionFolders = versionsDir.listFiles(File::isDirectory); - if (versionFolders != null) { - for (File versionFolder : versionFolders) { - String versionName = versionFolder.getName(); - File jsonFile = new File(versionFolder, versionName + ".json"); - - if (jsonFile.exists() && jsonFile.isFile()) { - try (Reader reader = new FileReader(jsonFile)) { - // 解析 JSON 文件 - Gson gson = new Gson(); - Map versionData = gson.fromJson(reader, Map.class); - - // 提取 "assets" 的值 - Object assetsValue = versionData.get("assets"); - if (assetsValue instanceof String) { - String assets = (String) assetsValue; - indexes.add(assets); - } - } catch (IOException e) { - System.err.println("[AssetsCleaner]: Error reading JSON file: " + jsonFile); - e.printStackTrace(); - } - } - } - } - } - - return indexes; - } - - public static void clean(String repoDirectory) { - // 构造路径 - String versionsDirectory = Paths.get(repoDirectory, "versions").toString(); - String indexesDirectory = Paths.get(repoDirectory, "assets", "indexes").toString(); - String objectsDirectory = Paths.get(repoDirectory, "assets", "objects").toString(); - - // 获取 indexes - List requiredIndexes = getIndexes(versionsDirectory); - - // 用于存储所有 index 中 hash 键值的 Map - Map> hashMap = new HashMap<>(); - - try { - // 遍历 indexesDirectory 目录下的所有 JSON 文件 - File indexesDir = new File(indexesDirectory); - if (!indexesDir.exists() || !indexesDir.isDirectory()) { - System.err.println("[AssetsCleaner]: Indexes directory does not exist: " + indexesDir); - return; - } - - // 获取 indexesDirectory 中的所有 JSON 文件 - File[] indexFiles = indexesDir.listFiles((FilenameFilter) (dir, name) -> name.endsWith(".json")); - if (indexFiles != null) { - for (File indexFile : indexFiles) { - String indexFileName = indexFile.getName(); - String indexName = indexFileName.substring(0, indexFileName.lastIndexOf('.')); - - // 检查该 index 是否在 requiredIndexes 中 - if (!requiredIndexes.contains(indexName)) { - // 如果不在 requiredIndexes 中,则删除该 index 文件 - if (!indexFile.delete()) { - System.err.println("[AssetsCleaner]: Error deleting index file: " + indexFile); - } else { - System.out.println("[AssetsCleaner]: Deleted unnecessary index: " + indexFile); - } - continue; - } - - // 解析 JSON 文件 - try (Reader reader = new FileReader(indexFile)) { - Gson gson = new Gson(); - Map>> jsonData = gson.fromJson(reader, new TypeToken>>>() {}.getType()); - - // 提取 hash 的键值 - Map> objects = jsonData.get("objects"); - if (objects != null) { - for (Map.Entry> entry : objects.entrySet()) { - String filePath = entry.getKey(); - Map fileData = entry.getValue(); - String hash = (String) fileData.get("hash"); - if (hash != null && !hashMap.containsKey(hash)) { - hashMap.put(hash, fileData); - } - } - } - } catch (IOException e) { - System.err.println("[AssetsCleaner]: Error reading JSON file: " + indexFile); - e.printStackTrace(); - } - } - } - - // 输出 hashMap 中保存的所有 hash 值 - System.out.println("[AssetsCleaner]: 所有 hash 值:"); - for (String hash : hashMap.keySet()) { - System.out.println(hash); - } - - // 遍历目标处理目录文件 - File objectsDir = new File(objectsDirectory); - if (!objectsDir.exists() || !objectsDir.isDirectory()) { - System.err.println("[AssetsCleaner]: Objects directory does not exist: " + objectsDir); - return; - } - - // 递归遍历 objectsDirectory - traverseAndCleanDirectory(objectsDir, hashMap); - } catch (Exception e) { - e.printStackTrace(); - } - } - - private static void traverseAndCleanDirectory(File directory, Map> hashMap) { - if (!directory.exists() || !directory.isDirectory()) { - return; - } - - File[] files = directory.listFiles(); - if (files != null) { - for (File file : files) { - if (file.isDirectory()) { - traverseAndCleanDirectory(file, hashMap); - } else { - String fileName = file.getName(); - // 判断文件是否在 hashMap 中 - if (!hashMap.containsKey(fileName)) { - // 如果不在则删除 - if (!file.delete()) { - System.err.println("[AssetsCleaner]: Error deleting file: " + file); - } else { - System.out.println("[AssetsCleaner]: Deleted file: " + file); - } - } - } - } - } - } -} From f77e47e80f0f377ef25c031a385957690f66e866 Mon Sep 17 00:00:00 2001 From: Glavo Date: Mon, 28 Jul 2025 15:55:46 +0800 Subject: [PATCH 10/13] update --- HMCL/src/main/resources/assets/lang/I18N.properties | 2 +- HMCL/src/main/resources/assets/lang/I18N_zh.properties | 2 +- HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/HMCL/src/main/resources/assets/lang/I18N.properties b/HMCL/src/main/resources/assets/lang/I18N.properties index 4b2d9a1b0..650380081 100644 --- a/HMCL/src/main/resources/assets/lang/I18N.properties +++ b/HMCL/src/main/resources/assets/lang/I18N.properties @@ -124,7 +124,7 @@ account.methods.microsoft.purchase=Buy Minecraft account.methods.microsoft.snapshot=You are using an unofficial build of HMCL. Please download the official build to log in. account.methods.microsoft.snapshot.website=Official Website account.methods.offline=Offline -account.methods.offline.name.special_characters=Use only letters, numbers, and underscores (max 16 chars). +account.methods.offline.name.special_characters=Use only letters, numbers, and underscores (max 16 chars) account.methods.offline.name.invalid=It is recommended to use only English letters, numbers and underscores for the username, and the length should not exceed 16 characters.\n\ \n\ \ · Legitimate: HuangYu, huang_Yu, Huang_Yu_123;\n\ diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh.properties b/HMCL/src/main/resources/assets/lang/I18N_zh.properties index 751178533..c7b3c14bd 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh.properties @@ -131,7 +131,7 @@ account.methods.ban_query=查詢帳戶是否被封禁 account.methods.microsoft.snapshot=你正在使用第三方提供的 HMCL。請下載官方版本進行登入。 account.methods.microsoft.snapshot.website=官方網站 account.methods.offline=離線模式 -account.methods.offline.name.special_characters=建議使用英文字母、數字以及底線命名,且長度不超過 16 個字元。 +account.methods.offline.name.special_characters=建議使用英文字母、數字以及底線命名,且長度不超過 16 個字元 account.methods.offline.name.invalid=遊戲使用者名稱建議僅使用英文字母、數字及底線,且長度不超過 16 個字元。\n\ \n\ \ · 一些有效的使用者名稱:HuangYu、huang_Yu、Huang_Yu_123;\n\ diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties index e438cc5bd..1775f4511 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties @@ -139,7 +139,7 @@ account.methods.ban_query=检测账户是否被封禁 account.methods.microsoft.snapshot=你正在使用第三方提供的 HMCL。请下载官方版本来登录微软账户。 account.methods.microsoft.snapshot.website=官方网站 account.methods.offline=离线模式 -account.methods.offline.name.special_characters=建议使用英文字符、数字以及下划线命名,且长度不超过 16 个字符。 +account.methods.offline.name.special_characters=建议使用英文字符、数字以及下划线命名,且长度不超过 16 个字符 account.methods.offline.name.invalid=游戏用户名建议仅使用英文字母、数字及下划线,且长度不超过 16 个字符。\n\ \n\ \ · 一些合法用户名:HuangYu、huang_Yu、Huang_Yu_123;\n\ From 90558ebd0cd7207d5e592b26127cf8baa741d9f5 Mon Sep 17 00:00:00 2001 From: Glavo Date: Mon, 28 Jul 2025 16:01:45 +0800 Subject: [PATCH 11/13] update --- .../java/org/jackhuang/hmcl/ui/account/CreateAccountPane.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/CreateAccountPane.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/CreateAccountPane.java index 11756f8bc..9b4bd3f09 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/CreateAccountPane.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/CreateAccountPane.java @@ -267,7 +267,7 @@ public class CreateAccountPane extends JFXDialogLayout implements DialogAware { }).executor(true); }; - if (factory instanceof OfflineAccountFactory && username != null && !USERNAME_CHECKER_PATTERN.matcher(username).matches() || username.length() > 16) { + if (factory instanceof OfflineAccountFactory && username != null && (!USERNAME_CHECKER_PATTERN.matcher(username).matches() || username.length() > 16)) { JFXButton btnYes = new JFXButton(i18n("button.ok")); btnYes.getStyleClass().add("dialog-error"); btnYes.setOnAction(e -> doCreate.run()); From cc175505dbc2c6e0e2fd484e6a38cbccfecc4acd Mon Sep 17 00:00:00 2001 From: ShulkerSakura <2531493755@qq.com> Date: Thu, 4 Sep 2025 19:25:08 +0800 Subject: [PATCH 12/13] Add Admin Checker --- .../java/org/jackhuang/hmcl/Launcher.java | 6 +-- .../org/jackhuang/hmcl/util/AdminChecker.java | 43 +++++++++++++++++++ 2 files changed, 45 insertions(+), 4 deletions(-) create mode 100755 HMCL/src/main/java/org/jackhuang/hmcl/util/AdminChecker.java diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/Launcher.java b/HMCL/src/main/java/org/jackhuang/hmcl/Launcher.java index efa971ced..b060de4b6 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/Launcher.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/Launcher.java @@ -29,15 +29,12 @@ import javafx.stage.Stage; import org.jackhuang.hmcl.setting.ConfigHolder; import org.jackhuang.hmcl.setting.SambaException; import org.jackhuang.hmcl.ui.FXUtils; -import org.jackhuang.hmcl.util.FileSaver; +import org.jackhuang.hmcl.util.*; import org.jackhuang.hmcl.task.AsyncTaskExecutor; import org.jackhuang.hmcl.task.Schedulers; import org.jackhuang.hmcl.ui.Controllers; import org.jackhuang.hmcl.upgrade.UpdateChecker; import org.jackhuang.hmcl.upgrade.UpdateHandler; -import org.jackhuang.hmcl.util.CrashReporter; -import org.jackhuang.hmcl.util.Lang; -import org.jackhuang.hmcl.util.StringUtils; import org.jackhuang.hmcl.util.io.JarUtils; import org.jackhuang.hmcl.util.platform.Architecture; import org.jackhuang.hmcl.util.platform.CommandBuilder; @@ -254,6 +251,7 @@ public final class Launcher extends Application { 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")); + LOG.info("User Privilege: " + AdminChecker.isAdmin()); LOG.info("Current Directory: " + Metadata.CURRENT_DIRECTORY); LOG.info("HMCL Global Directory: " + Metadata.HMCL_GLOBAL_DIRECTORY); LOG.info("HMCL Current Directory: " + Metadata.HMCL_CURRENT_DIRECTORY); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/util/AdminChecker.java b/HMCL/src/main/java/org/jackhuang/hmcl/util/AdminChecker.java new file mode 100755 index 000000000..b3fcb95e7 --- /dev/null +++ b/HMCL/src/main/java/org/jackhuang/hmcl/util/AdminChecker.java @@ -0,0 +1,43 @@ +package org.jackhuang.hmcl.util; + +import org.jackhuang.hmcl.util.platform.OperatingSystem; +import org.jackhuang.hmcl.util.platform.windows.WinReg; + +public class AdminChecker { + + public static boolean isAdmin() { + if (OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS) { + return isWindowsAdmin(); + } else if (OperatingSystem.CURRENT_OS.isLinuxOrBSD() && OperatingSystem.CURRENT_OS == OperatingSystem.MACOS) { + return isUnixRoot(); + } else { + // 未知系统,保守返回 false + System.err.println("Unknown OS: " + OperatingSystem.CURRENT_OS); + return false; + } + } + + private static boolean isWindowsAdmin() { + WinReg reg = WinReg.INSTANCE; + try { + return reg.exists(WinReg.HKEY.HKEY_USERS, "S-1-5-19"); + } catch (Throwable t) { + // 捕获 AccessException、JNA 错误等 + return false; + } + } + + private static boolean isUnixRoot() { + try { + ProcessBuilder pb = new ProcessBuilder("id", "-u"); + Process process = pb.start(); + java.util.Scanner scanner = new java.util.Scanner(process.getInputStream()); + String uid = scanner.hasNext() ? scanner.next() : ""; + scanner.close(); + process.waitFor(); + return "0".equals(uid.trim()); + } catch (Exception e) { + return false; + } + } +} \ No newline at end of file From c3dabfaab44aa6be5fb194ec022cb4d325f6f08e Mon Sep 17 00:00:00 2001 From: ShulkerSakura <2531493755@qq.com> Date: Thu, 4 Sep 2025 19:36:25 +0800 Subject: [PATCH 13/13] Style Check --- HMCL/src/main/java/org/jackhuang/hmcl/util/AdminChecker.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/util/AdminChecker.java b/HMCL/src/main/java/org/jackhuang/hmcl/util/AdminChecker.java index b3fcb95e7..da6fd031e 100755 --- a/HMCL/src/main/java/org/jackhuang/hmcl/util/AdminChecker.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/util/AdminChecker.java @@ -5,6 +5,9 @@ import org.jackhuang.hmcl.util.platform.windows.WinReg; public class AdminChecker { + private AdminChecker() { + } + public static boolean isAdmin() { if (OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS) { return isWindowsAdmin(); @@ -40,4 +43,4 @@ public class AdminChecker { return false; } } -} \ No newline at end of file +}