From e6f28606f427739941d39eead4c47c2672b153b1 Mon Sep 17 00:00:00 2001 From: mine_ Date: Tue, 30 Dec 2025 19:43:27 +0800 Subject: [PATCH 1/8] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E5=A4=8D?= =?UTF-8?q?=E5=88=B6=E5=8A=9F=E8=83=BD=EF=BC=8C=E6=B7=BB=E5=8A=A0=E6=9B=B4?= =?UTF-8?q?=E5=A4=9A=E6=A0=BC=E5=BC=8F=E6=94=AF=E6=8C=81=EF=BC=8C=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E6=89=93=E5=BC=80=E9=A1=B5=E9=9D=A2=E6=97=B6=E6=89=93?= =?UTF-8?q?=E5=BC=80=E6=96=B0nbt=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jackhuang/hmcl/ui/nbt/NBTEditorPage.java | 15 +++- .../jackhuang/hmcl/ui/nbt/NBTFileType.java | 18 ++--- .../jackhuang/hmcl/ui/nbt/NBTTreeView.java | 73 ++++++++++--------- 3 files changed, 62 insertions(+), 44 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/nbt/NBTEditorPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/nbt/NBTEditorPage.java index fca15907bd..1171cf93a7 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/nbt/NBTEditorPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/nbt/NBTEditorPage.java @@ -38,8 +38,8 @@ import java.nio.file.Path; import static org.jackhuang.hmcl.ui.FXUtils.onEscPressed; -import static org.jackhuang.hmcl.util.logging.Logger.LOG; import static org.jackhuang.hmcl.util.i18n.I18n.i18n; +import static org.jackhuang.hmcl.util.logging.Logger.LOG; /** * @author Glavo @@ -65,6 +65,19 @@ public NBTEditorPage(Path file) throws IOException { setContent(root); setLoading(true); + FXUtils.applyDragListener(this, + NBTFileType::isNBTFileByExtension, + paths -> { + Path path = paths.get(0); + try { + Controllers.navigate(new NBTEditorPage(path)); + } catch (Throwable e) { + LOG.warning("Fail to open nbt file", e); + Controllers.dialog(i18n("nbt.open.failed") + "\n\n" + StringUtils.getStackTrace(e), + i18n("message.error"), MessageDialogPane.MessageType.ERROR); + } + }); + HBox actions = new HBox(8); actions.setPadding(new Insets(8)); actions.setAlignment(Pos.CENTER_RIGHT); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/nbt/NBTFileType.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/nbt/NBTFileType.java index ceab275040..01daa62534 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/nbt/NBTFileType.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/nbt/NBTFileType.java @@ -41,7 +41,7 @@ * @author Glavo */ public enum NBTFileType { - COMPRESSED("dat", "dat_old") { + COMPRESSED("dat", "dat_old", "litematic", "nbt", "schematic", "schem") { @Override public Tag read(Path file) throws IOException { try (BufferedInputStream fileInputStream = new BufferedInputStream(Files.newInputStream(file))) { @@ -109,20 +109,20 @@ public Tag read(Path file) throws IOException { input = new BoundedInputStream(input, chunkLength - 1); switch (buffer[4]) { - case 0x01: + case 0x01 -> { // GZip input = new GZIPInputStream(input); - break; - case 0x02: + } + case 0x02 -> { // Zlib inflater.reset(); input = new InflaterInputStream(input, inflater); - break; - case 0x03: + } + case 0x03 -> { // Uncompressed - break; - default: - throw new IOException("Unsupported compression method: " + Integer.toHexString(buffer[4] & 0xff)); + } + default -> + throw new IOException("Unsupported compression method: " + Integer.toHexString(buffer[4] & 0xff)); } try (InputStream in = input) { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/nbt/NBTTreeView.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/nbt/NBTTreeView.java index 1448855a9b..6f0ae84235 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/nbt/NBTTreeView.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/nbt/NBTTreeView.java @@ -26,7 +26,12 @@ import javafx.scene.control.TreeView; import javafx.scene.image.Image; import javafx.scene.image.ImageView; +import javafx.scene.input.KeyCode; +import javafx.scene.input.KeyCodeCombination; +import javafx.scene.input.KeyCombination; +import javafx.scene.input.KeyEvent; import javafx.util.Callback; +import org.jackhuang.hmcl.ui.FXUtils; import java.lang.reflect.Array; import java.util.EnumMap; @@ -37,26 +42,51 @@ * @author Glavo */ public final class NBTTreeView extends JFXTreeView { + final KeyCombination COPY_COMBO = new KeyCodeCombination(KeyCode.C, KeyCombination.SHORTCUT_DOWN); public NBTTreeView(NBTTreeView.Item tree) { this.setRoot(tree); this.setCellFactory(cellFactory()); + + this.addEventFilter(KeyEvent.KEY_PRESSED, event -> { + if (!COPY_COMBO.match(event)) return; + + TreeItem current = getSelectionModel().getSelectedItem(); + + if (current instanceof Item item && item.getText() != null) { + FXUtils.copyText(item.getText()); + event.consume(); + } + }); } private static Callback, TreeCell> cellFactory() { EnumMap icons = new EnumMap<>(NBTTagType.class); return view -> new TreeCell<>() { + final ImageView imageView; + + { + imageView = new ImageView(); + this.setGraphic(imageView); + imageView.setFitHeight(16); + imageView.setFitWidth(16); + } + private void setTagText(String text) { - String name = ((Item) getTreeItem()).getName(); + Item item = (Item) getTreeItem(); + String name = item.getName(); + String displayText; if (name == null) { - setText(text); + displayText = text; } else if (text == null) { - setText(name); + displayText = name; } else { - setText(name + ": " + text); + displayText = name + ": " + text; } + item.setText(displayText); + setText(displayText); } private void setTagText(int nEntries) { @@ -67,12 +97,6 @@ private void setTagText(int nEntries) { public void updateItem(Tag item, boolean empty) { super.updateItem(item, empty); - ImageView imageView = (ImageView) this.getGraphic(); - if (imageView == null) { - imageView = new ImageView(); - this.setGraphic(imageView); - } - if (item == null) { imageView.setImage(null); setText(null); @@ -81,35 +105,16 @@ public void updateItem(Tag item, boolean empty) { NBTTagType tagType = NBTTagType.typeOf(item); imageView.setImage(icons.computeIfAbsent(tagType, type -> new Image(type.getIconUrl()))); - imageView.setFitHeight(16); - imageView.setFitWidth(16); if (((Item) getTreeItem()).getText() != null) { setText(((Item) getTreeItem()).getText()); } else { switch (tagType) { - case BYTE: - case SHORT: - case INT: - case LONG: - case FLOAT: - case DOUBLE: - case STRING: - setTagText(item.getValue().toString()); - break; - case BYTE_ARRAY: - case INT_ARRAY: - case LONG_ARRAY: - setTagText(Array.getLength(item.getValue())); - break; - case LIST: - setTagText(((ListTag) item).size()); - break; - case COMPOUND: - setTagText(((CompoundTag) item).size()); - break; - default: - setTagText(null); + case BYTE, SHORT, INT, LONG, FLOAT, DOUBLE, STRING -> setTagText(item.getValue().toString()); + case BYTE_ARRAY, INT_ARRAY, LONG_ARRAY -> setTagText(Array.getLength(item.getValue())); + case LIST -> setTagText(((ListTag) item).size()); + case COMPOUND -> setTagText(((CompoundTag) item).size()); + default -> setTagText(null); } } } From ebb72f8062d37750a745842856d6a4cd4a946619 Mon Sep 17 00:00:00 2001 From: mine_ Date: Wed, 31 Dec 2025 23:15:17 +0800 Subject: [PATCH 2/8] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E5=8F=B3?= =?UTF-8?q?=E9=94=AE=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jackhuang/hmcl/ui/nbt/NBTTreeView.java | 43 +++++++++++++++++-- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/nbt/NBTTreeView.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/nbt/NBTTreeView.java index 6f0ae84235..1cd6457934 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/nbt/NBTTreeView.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/nbt/NBTTreeView.java @@ -20,18 +20,20 @@ import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.github.steveice10.opennbt.tag.builtin.ListTag; import com.github.steveice10.opennbt.tag.builtin.Tag; +import com.jfoenix.controls.JFXPopup; import com.jfoenix.controls.JFXTreeView; import javafx.scene.control.TreeCell; import javafx.scene.control.TreeItem; import javafx.scene.control.TreeView; import javafx.scene.image.Image; import javafx.scene.image.ImageView; -import javafx.scene.input.KeyCode; -import javafx.scene.input.KeyCodeCombination; -import javafx.scene.input.KeyCombination; -import javafx.scene.input.KeyEvent; +import javafx.scene.input.*; import javafx.util.Callback; +import org.jackhuang.hmcl.ui.Controllers; import org.jackhuang.hmcl.ui.FXUtils; +import org.jackhuang.hmcl.ui.SVG; +import org.jackhuang.hmcl.ui.construct.IconedMenuItem; +import org.jackhuang.hmcl.ui.construct.PopupMenu; import java.lang.reflect.Array; import java.util.EnumMap; @@ -58,12 +60,45 @@ public NBTTreeView(NBTTreeView.Item tree) { event.consume(); } }); + + this.setOnContextMenuRequested(event -> { + + TreeItem current = getSelectionModel().getSelectedItem(); + + if (current instanceof Item item) { + showPopupMenu(item, event); + } + }); + } + + private void showPopupMenu(Item item, ContextMenuEvent event) { + PopupMenu menu = new PopupMenu(); + JFXPopup popup = new JFXPopup(menu); + + IconedMenuItem copyShownItem = new IconedMenuItem(SVG.CONTENT_COPY, "copy shown", () -> { + String tagValue = item.getText(); + FXUtils.copyText(tagValue); + }, popup); + + IconedMenuItem copyRawItem = new IconedMenuItem(SVG.CONTENT_COPY, "copy detail", () -> { + String tagValue = item.getValue().toString(); + FXUtils.copyText(tagValue); + }, popup); + + menu.getContent().addAll( + copyShownItem, + copyRawItem + ); + + popup.show(Controllers.getStage(), event.getSceneX(), event.getSceneY(), JFXPopup.PopupVPosition.TOP, JFXPopup.PopupHPosition.LEFT, 0, 0); } private static Callback, TreeCell> cellFactory() { EnumMap icons = new EnumMap<>(NBTTagType.class); + return view -> new TreeCell<>() { + final ImageView imageView; { From 91bff27c7e64c87adc710fa11c78e95e037eae1b Mon Sep 17 00:00:00 2001 From: mine_ Date: Thu, 1 Jan 2026 15:24:59 +0800 Subject: [PATCH 3/8] =?UTF-8?q?feat:=20=E6=98=BE=E7=A4=BA=E6=96=87?= =?UTF-8?q?=E5=AD=97=E7=8E=B0=E5=9C=A8=E6=98=AFsnbt=EF=BC=8C=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E5=A4=8D=E5=88=B6=E4=B8=BAsnbt=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jackhuang/hmcl/ui/nbt/NBTEditorPage.java | 22 --- .../jackhuang/hmcl/ui/nbt/NBTFileType.java | 8 +- .../jackhuang/hmcl/ui/nbt/NBTTreeView.java | 166 +++++++++--------- .../org/jackhuang/hmcl/ui/nbt/NBTUtils.java | 23 +++ 4 files changed, 108 insertions(+), 111 deletions(-) create mode 100644 HMCL/src/main/java/org/jackhuang/hmcl/ui/nbt/NBTUtils.java diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/nbt/NBTEditorPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/nbt/NBTEditorPage.java index 1171cf93a7..3aa4fbaa5f 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/nbt/NBTEditorPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/nbt/NBTEditorPage.java @@ -21,9 +21,7 @@ import javafx.beans.property.ReadOnlyObjectProperty; import javafx.beans.property.ReadOnlyObjectWrapper; import javafx.geometry.Insets; -import javafx.geometry.Pos; import javafx.scene.layout.BorderPane; -import javafx.scene.layout.HBox; import org.jackhuang.hmcl.task.Schedulers; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.ui.Controllers; @@ -78,26 +76,10 @@ public NBTEditorPage(Path file) throws IOException { } }); - HBox actions = new HBox(8); - actions.setPadding(new Insets(8)); - actions.setAlignment(Pos.CENTER_RIGHT); - - JFXButton saveButton = FXUtils.newRaisedButton(i18n("button.save")); - saveButton.setOnAction(e -> { - try { - save(); - } catch (IOException ex) { - LOG.warning("Failed to save NBT file", ex); - Controllers.dialog(i18n("nbt.save.failed") + "\n\n" + StringUtils.getStackTrace(ex)); - } - }); - JFXButton cancelButton = FXUtils.newRaisedButton(i18n("button.cancel")); cancelButton.setOnAction(e -> fireEvent(new PageCloseEvent())); onEscPressed(this, cancelButton::fire); - actions.getChildren().setAll(saveButton, cancelButton); - Task.supplyAsync(() -> type.readAsTree(file)) .whenComplete(Schedulers.javafx(), (result, exception) -> { if (exception == null) { @@ -113,10 +95,6 @@ public NBTEditorPage(Path file) throws IOException { }).start(); } - public void save() throws IOException { - // TODO - } - @Override public ReadOnlyObjectProperty stateProperty() { return state; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/nbt/NBTFileType.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/nbt/NBTFileType.java index 01daa62534..f5d44c50da 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/nbt/NBTFileType.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/nbt/NBTFileType.java @@ -25,11 +25,7 @@ import kala.compress.utils.BoundedInputStream; import org.jackhuang.hmcl.util.io.FileUtils; -import java.io.BufferedInputStream; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.RandomAccessFile; +import java.io.*; import java.nio.file.Files; import java.nio.file.Path; import java.util.Arrays; @@ -190,7 +186,7 @@ public static NBTFileType ofFile(Path file) { public NBTTreeView.Item readAsTree(Path file) throws IOException { NBTTreeView.Item root = NBTTreeView.buildTree(read(file)); - root.setName(file.getFileName().toString()); + root.setCustomName(file.getFileName().toString()); return root; } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/nbt/NBTTreeView.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/nbt/NBTTreeView.java index 1cd6457934..d93c1ad3f4 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/nbt/NBTTreeView.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/nbt/NBTTreeView.java @@ -22,18 +22,17 @@ import com.github.steveice10.opennbt.tag.builtin.Tag; import com.jfoenix.controls.JFXPopup; import com.jfoenix.controls.JFXTreeView; +import javafx.scene.Node; import javafx.scene.control.TreeCell; import javafx.scene.control.TreeItem; -import javafx.scene.control.TreeView; import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.input.*; -import javafx.util.Callback; -import org.jackhuang.hmcl.ui.Controllers; import org.jackhuang.hmcl.ui.FXUtils; import org.jackhuang.hmcl.ui.SVG; import org.jackhuang.hmcl.ui.construct.IconedMenuItem; import org.jackhuang.hmcl.ui.construct.PopupMenu; +import org.jackhuang.hmcl.util.StringUtils; import java.lang.reflect.Array; import java.util.EnumMap; @@ -45,10 +44,11 @@ */ public final class NBTTreeView extends JFXTreeView { final KeyCombination COPY_COMBO = new KeyCodeCombination(KeyCode.C, KeyCombination.SHORTCUT_DOWN); + private final EnumMap icons = new EnumMap<>(NBTTagType.class); public NBTTreeView(NBTTreeView.Item tree) { this.setRoot(tree); - this.setCellFactory(cellFactory()); + this.setCellFactory(view -> new TagTreeCell(icons)); this.addEventFilter(KeyEvent.KEY_PRESSED, event -> { if (!COPY_COMBO.match(event)) return; @@ -66,22 +66,22 @@ public NBTTreeView(NBTTreeView.Item tree) { TreeItem current = getSelectionModel().getSelectedItem(); if (current instanceof Item item) { - showPopupMenu(item, event); + showPopupMenu(item, event, this); } }); } - private void showPopupMenu(Item item, ContextMenuEvent event) { + private void showPopupMenu(Item item, ContextMenuEvent event, Node node) { PopupMenu menu = new PopupMenu(); JFXPopup popup = new JFXPopup(menu); - IconedMenuItem copyShownItem = new IconedMenuItem(SVG.CONTENT_COPY, "copy shown", () -> { + IconedMenuItem copyShownItem = new IconedMenuItem(SVG.CONTENT_COPY, "copy shown text", () -> { String tagValue = item.getText(); FXUtils.copyText(tagValue); }, popup); - IconedMenuItem copyRawItem = new IconedMenuItem(SVG.CONTENT_COPY, "copy detail", () -> { - String tagValue = item.getValue().toString(); + IconedMenuItem copyRawItem = new IconedMenuItem(SVG.CONTENT_COPY, "copy as snbt", () -> { + String tagValue = NBTUtils.getSNBT(item.getValue()); FXUtils.copyText(tagValue); }, popup); @@ -90,84 +90,21 @@ private void showPopupMenu(Item item, ContextMenuEvent event) { copyRawItem ); - popup.show(Controllers.getStage(), event.getSceneX(), event.getSceneY(), JFXPopup.PopupVPosition.TOP, JFXPopup.PopupHPosition.LEFT, 0, 0); - } - - private static Callback, TreeCell> cellFactory() { - EnumMap icons = new EnumMap<>(NBTTagType.class); - - - return view -> new TreeCell<>() { - - final ImageView imageView; - - { - imageView = new ImageView(); - this.setGraphic(imageView); - imageView.setFitHeight(16); - imageView.setFitWidth(16); - } - - private void setTagText(String text) { - Item item = (Item) getTreeItem(); - String name = item.getName(); - - String displayText; - if (name == null) { - displayText = text; - } else if (text == null) { - displayText = name; - } else { - displayText = name + ": " + text; - } - item.setText(displayText); - setText(displayText); - } - - private void setTagText(int nEntries) { - setTagText(i18n("nbt.entries", nEntries)); - } - - @Override - public void updateItem(Tag item, boolean empty) { - super.updateItem(item, empty); - - if (item == null) { - imageView.setImage(null); - setText(null); - return; - } - - NBTTagType tagType = NBTTagType.typeOf(item); - imageView.setImage(icons.computeIfAbsent(tagType, type -> new Image(type.getIconUrl()))); - - if (((Item) getTreeItem()).getText() != null) { - setText(((Item) getTreeItem()).getText()); - } else { - switch (tagType) { - case BYTE, SHORT, INT, LONG, FLOAT, DOUBLE, STRING -> setTagText(item.getValue().toString()); - case BYTE_ARRAY, INT_ARRAY, LONG_ARRAY -> setTagText(Array.getLength(item.getValue())); - case LIST -> setTagText(((ListTag) item).size()); - case COMPOUND -> setTagText(((CompoundTag) item).size()); - default -> setTagText(null); - } - } - } - }; + popup.show(node, JFXPopup.PopupVPosition.TOP, JFXPopup.PopupHPosition.LEFT, event.getX(), event.getY()); } public static Item buildTree(Tag tag) { Item item = new Item(tag); - if (tag instanceof CompoundTag) { - for (Tag subTag : ((CompoundTag) tag)) { + if (tag instanceof CompoundTag compoundTag) { + for (Tag subTag : compoundTag) { item.getChildren().add(buildTree(subTag)); } - } else if (tag instanceof ListTag) { + } else if (tag instanceof ListTag listTag) { int idx = 0; - for (Tag subTag : ((ListTag) tag)) { + for (Tag subTag : listTag) { Item subTree = buildTree(subTag); - subTree.setName(String.valueOf(idx++)); + subTree.setCustomName(String.valueOf(idx++)); item.getChildren().add(subTree); } } @@ -182,7 +119,7 @@ public CompoundTag getRootTag() { public static class Item extends TreeItem { private String text; - private String name; + private String customName; public Item() { } @@ -199,12 +136,75 @@ public void setText(String text) { this.text = text; } - public void setName(String name) { - this.name = name; + public void setCustomName(String customName) { + this.customName = customName; } - public String getName() { - return name == null ? getValue().getName() : name; + public String getCustomName() { + return customName; + } + } + + private static class TagTreeCell extends TreeCell { + + private final ImageView imageView = new ImageView(); + private final EnumMap icons; + + public TagTreeCell(EnumMap icons) { + this.icons = icons; + this.setGraphic(imageView); + imageView.setFitHeight(16); + imageView.setFitWidth(16); + } + + private void setTagText(String text, boolean containName) { + Item item = (Item) getTreeItem(); + String displayText = text == null ? "" : text; + + if (!containName) { + String customName = item.getCustomName(); + String name = item.getValue().getName(); + + if (StringUtils.isNotBlank(customName)) { + displayText = customName + ": " + (text == null ? "" : text); + } else if (StringUtils.isNotBlank(name)) { + displayText = name + ": " + (text == null ? "" : text); + } else { + displayText = text; + } + } + item.setText(displayText); + setText(displayText); + } + + private void setTagText(int nEntries) { + setTagText(i18n("nbt.entries", nEntries), false); + } + + @Override + public void updateItem(Tag item, boolean empty) { + super.updateItem(item, empty); + + if (item == null) { + imageView.setImage(null); + setText(null); + return; + } + + NBTTagType tagType = NBTTagType.typeOf(item); + imageView.setImage(icons.computeIfAbsent(tagType, type -> new Image(type.getIconUrl()))); + + if (((Item) getTreeItem()).getText() != null) { + setText(((Item) getTreeItem()).getText()); + } else { + switch (tagType) { + case BYTE, SHORT, INT, LONG, FLOAT, DOUBLE, STRING -> setTagText(NBTUtils.getSNBT(item), true); + case BYTE_ARRAY, INT_ARRAY, LONG_ARRAY -> setTagText(Array.getLength(item.getValue())); + case LIST -> setTagText(((ListTag) item).size()); + case COMPOUND -> setTagText(((CompoundTag) item).size()); + default -> setTagText(null, true); + } + } } } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/nbt/NBTUtils.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/nbt/NBTUtils.java new file mode 100644 index 0000000000..9e983dac2c --- /dev/null +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/nbt/NBTUtils.java @@ -0,0 +1,23 @@ +package org.jackhuang.hmcl.ui.nbt; + +import com.github.steveice10.opennbt.SNBTIO; +import com.github.steveice10.opennbt.tag.builtin.Tag; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; + +public final class NBTUtils { + + private NBTUtils() { + } + + public static String getSNBT(Tag tag) { + try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { + SNBTIO.writeTag(baos, tag, false); + return baos.toString(StandardCharsets.UTF_8); + } catch (IOException e) { + throw new RuntimeException(e); + } + } +} From 7412cbb2044d7296bd1b518e63cd044dd088cf19 Mon Sep 17 00:00:00 2001 From: mine_ Date: Thu, 1 Jan 2026 17:30:26 +0800 Subject: [PATCH 4/8] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E5=88=B7?= =?UTF-8?q?=E6=96=B0=E5=8A=9F=E8=83=BD=EF=BC=8C=E6=B7=BB=E5=8A=A0=E6=8A=98?= =?UTF-8?q?=E5=8F=A0/=E5=B1=95=E5=BC=80=E5=8F=B3=E9=94=AE=E8=8F=9C?= =?UTF-8?q?=E5=8D=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/org/jackhuang/hmcl/ui/SVG.java | 1 + .../jackhuang/hmcl/ui/nbt/NBTEditorPage.java | 24 +++++++++++++++++-- .../jackhuang/hmcl/ui/nbt/NBTTreeView.java | 17 +++++++++++-- 3 files changed, 38 insertions(+), 4 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/SVG.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/SVG.java index 2f1ea2aa13..e6a174b6fb 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/SVG.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/SVG.java @@ -103,6 +103,7 @@ public enum SVG { PUBLIC("M12 22Q9.925 22 8.1 21.2125T4.925 19.075Q3.575 17.725 2.7875 15.9T2 12Q2 9.925 2.7875 8.1T4.925 4.925Q6.275 3.575 8.1 2.7875T12 2Q14.075 2 15.9 2.7875T19.075 4.925Q20.425 6.275 21.2125 8.1T22 12Q22 14.075 21.2125 15.9T19.075 19.075Q17.725 20.425 15.9 21.2125T12 22ZM11 19.95V18Q10.175 18 9.5875 17.4125T9 16V15L4.2 10.2Q4.125 10.65 4.0625 11.1T4 12Q4 15.025 5.9875 17.3T11 19.95ZM17.9 17.4Q18.925 16.275 19.4625 14.8875T20 12Q20 9.55 18.6375 7.525T15 4.6V5Q15 5.825 14.4125 6.4125T13 7H11V9Q11 9.425 10.7125 9.7125T10 10H8V12H14Q14.425 12 14.7125 12.2875T15 13V16H16Q16.65 16 17.175 16.3875T17.9 17.4Z"), REFRESH("M12 20Q8.65 20 6.325 17.675T4 12Q4 8.65 6.325 6.325T12 4Q13.725 4 15.3 4.7125T18 6.75V4H20V11H13V9H17.2Q16.4 7.6 15.0125 6.8T12 6Q9.5 6 7.75 7.75T6 12Q6 14.5 7.75 16.25T12 18Q13.925 18 15.475 16.9T17.65 14H19.75Q19.05 16.65 16.9 18.325T12 20Z"), RELEASE_CIRCLE("M9,7H13A2,2 0 0,1 15,9V11C15,11.84 14.5,12.55 13.76,12.85L15,17H13L11.8,13H11V17H9V7M11,9V11H13V9H11M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2M12,4A8,8 0 0,0 4,12C4,16.41 7.58,20 12,20A8,8 0 0,0 20,12A8,8 0 0,0 12,4Z"), // Not Material + REMOVE("M 5 13 v -2 h 14 v 2 H 5 Z"), RESTORE("M12 21Q8.55 21 5.9875 18.7125T3.05 13H5.1Q5.45 15.6 7.4125 17.3T12 19Q14.925 19 16.9625 16.9625T19 12Q19 9.075 16.9625 7.0375T12 5Q10.275 5 8.775 5.8T6.25 8H9V10H3V4H5V6.35Q6.275 4.75 8.1125 3.875T12 3Q13.875 3 15.5125 3.7125T18.3625 5.6375Q19.575 6.85 20.2875 8.4875T21 12Q21 13.875 20.2875 15.5125T18.3625 18.3625Q17.15 19.575 15.5125 20.2875T12 21Z"), // Not Material ROCKET_LAUNCH("M5.65 10.025 7.6 10.85Q7.95 10.15 8.325 9.5T9.15 8.2L7.75 7.925 5.65 10.025ZM9.2 12.1 12.05 14.925Q13.1 14.525 14.3 13.7T16.55 11.825Q18.3 10.075 19.2875 7.9375T20.15 4Q18.35 3.875 16.2 4.8625T12.3 7.6Q11.25 8.65 10.425 9.85T9.2 12.1ZM13.65 10.475Q13.075 9.9 13.075 9.0625T13.65 7.65Q14.225 7.075 15.075 7.075T16.5 7.65Q17.075 8.225 17.075 9.0625T16.5 10.475Q15.925 11.05 15.075 11.05T13.65 10.475ZM14.125 18.5 16.225 16.4 15.95 15Q15.3 15.45 14.65 15.8125T13.3 16.525L14.125 18.5ZM21.95 2.175Q22.425 5.2 21.3625 8.0625T17.7 13.525L18.2 16Q18.3 16.5 18.15 16.975T17.65 17.8L13.45 22 11.35 17.075 7.075 12.8 2.15 10.7 6.325 6.5Q6.675 6.15 7.1625 6T8.15 5.95L10.625 6.45Q13.225 3.85 16.075 2.775T21.95 2.175ZM3.925 15.975Q4.8 15.1 6.0625 15.0875T8.2 15.95Q9.075 16.825 9.0625 18.0875T8.175 20.225Q7.55 20.85 6.0875 21.3T2.05 22.1Q2.4 19.525 2.85 18.0625T3.925 15.975ZM5.35 17.375Q5.1 17.625 4.85 18.2875T4.5 19.625Q5.175 19.525 5.8375 19.2875T6.75 18.8Q7.05 18.5 7.075 18.075T6.8 17.35Q6.5 17.05 6.075 17.0625T5.35 17.375Z"), SCHEMA("M4 23V17H6.5V15H4V9H6.5V7H4V1h7V7H8.5V9H11v2h3V9h7v6H14V13H11v2H8.5v2H11v6H4Zm2-2H9V19H6v2Zm0-8H9V11H6v2Zm10 0h3V11H16v2ZM6 5H9V3H6V5ZM7.5 4Zm0 8Zm10 0Zm-10 8Z"), diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/nbt/NBTEditorPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/nbt/NBTEditorPage.java index 3aa4fbaa5f..1da9387992 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/nbt/NBTEditorPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/nbt/NBTEditorPage.java @@ -18,8 +18,10 @@ package org.jackhuang.hmcl.ui.nbt; import com.jfoenix.controls.JFXButton; +import javafx.beans.property.BooleanProperty; import javafx.beans.property.ReadOnlyObjectProperty; import javafx.beans.property.ReadOnlyObjectWrapper; +import javafx.beans.property.SimpleBooleanProperty; import javafx.geometry.Insets; import javafx.scene.layout.BorderPane; import org.jackhuang.hmcl.task.Schedulers; @@ -48,11 +50,12 @@ public final class NBTEditorPage extends SpinnerPane implements DecoratorPage { private final NBTFileType type; private final BorderPane root = new BorderPane(); + JFXButton cancelButton; public NBTEditorPage(Path file) throws IOException { getStyleClass().add("gray-background"); - this.state = new ReadOnlyObjectWrapper<>(State.fromTitle(i18n("nbt.title", file.toString()))); + this.state = new ReadOnlyObjectWrapper<>(new State(i18n("nbt.title", file.toString()), null, true, true, true)); this.file = file; this.type = NBTFileType.ofFile(file); @@ -68,6 +71,7 @@ public NBTEditorPage(Path file) throws IOException { paths -> { Path path = paths.get(0); try { + fireEvent(new PageCloseEvent()); Controllers.navigate(new NBTEditorPage(path)); } catch (Throwable e) { LOG.warning("Fail to open nbt file", e); @@ -76,10 +80,14 @@ public NBTEditorPage(Path file) throws IOException { } }); - JFXButton cancelButton = FXUtils.newRaisedButton(i18n("button.cancel")); + cancelButton = FXUtils.newRaisedButton(i18n("button.cancel")); cancelButton.setOnAction(e -> fireEvent(new PageCloseEvent())); onEscPressed(this, cancelButton::fire); + loadTree(); + } + + public void loadTree() { Task.supplyAsync(() -> type.readAsTree(file)) .whenComplete(Schedulers.javafx(), (result, exception) -> { if (exception == null) { @@ -99,4 +107,16 @@ public NBTEditorPage(Path file) throws IOException { public ReadOnlyObjectProperty stateProperty() { return state; } + + @Override + public void refresh() { + root.setCenter(null); + setLoading(true); + loadTree(); + } + + @Override + public BooleanProperty refreshableProperty() { + return new SimpleBooleanProperty(true); + } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/nbt/NBTTreeView.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/nbt/NBTTreeView.java index d93c1ad3f4..7b90cab249 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/nbt/NBTTreeView.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/nbt/NBTTreeView.java @@ -31,6 +31,7 @@ import org.jackhuang.hmcl.ui.FXUtils; import org.jackhuang.hmcl.ui.SVG; import org.jackhuang.hmcl.ui.construct.IconedMenuItem; +import org.jackhuang.hmcl.ui.construct.MenuSeparator; import org.jackhuang.hmcl.ui.construct.PopupMenu; import org.jackhuang.hmcl.util.StringUtils; @@ -56,7 +57,7 @@ public NBTTreeView(NBTTreeView.Item tree) { TreeItem current = getSelectionModel().getSelectedItem(); if (current instanceof Item item && item.getText() != null) { - FXUtils.copyText(item.getText()); + FXUtils.copyText(NBTUtils.getSNBT(item.getValue())); event.consume(); } }); @@ -64,7 +65,6 @@ public NBTTreeView(NBTTreeView.Item tree) { this.setOnContextMenuRequested(event -> { TreeItem current = getSelectionModel().getSelectedItem(); - if (current instanceof Item item) { showPopupMenu(item, event, this); } @@ -90,6 +90,19 @@ private void showPopupMenu(Item item, ContextMenuEvent event, Node node) { copyRawItem ); + if (!item.isLeaf()) { + IconedMenuItem expandItem; + if (item.isExpanded()) { + expandItem = new IconedMenuItem(SVG.REMOVE, "fold", () -> item.setExpanded(false), popup); + } else { + expandItem = new IconedMenuItem(SVG.ADD, "expand", () -> item.setExpanded(true), popup); + } + menu.getContent().addAll( + new MenuSeparator(), + expandItem + ); + } + popup.show(node, JFXPopup.PopupVPosition.TOP, JFXPopup.PopupHPosition.LEFT, event.getX(), event.getY()); } From 7fd3305df49553ebe9afd4058e9cc6264bb3e65d Mon Sep 17 00:00:00 2001 From: mine_ Date: Fri, 2 Jan 2026 11:06:49 +0800 Subject: [PATCH 5/8] feat: update --- .../jackhuang/hmcl/ui/nbt/NBTEditCommand.java | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 HMCL/src/main/java/org/jackhuang/hmcl/ui/nbt/NBTEditCommand.java diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/nbt/NBTEditCommand.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/nbt/NBTEditCommand.java new file mode 100644 index 0000000000..2e61561521 --- /dev/null +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/nbt/NBTEditCommand.java @@ -0,0 +1,35 @@ +package org.jackhuang.hmcl.ui.nbt; + +import com.github.steveice10.opennbt.tag.builtin.Tag; + +public interface NBTEditCommand { + void execute(); + + void undo(); + + public record EditValueCommand(Tag target, String newValue, String oldValue) implements NBTEditCommand { + + @Override + public void execute() { + + } + + @Override + public void undo() { + + } + } + + public record EditNameCommand(Tag target, String newName, String oldName) implements NBTEditCommand { + + @Override + public void execute() { + + } + + @Override + public void undo() { + + } + } +} \ No newline at end of file From 8987fa6556259c412d2130c5f428a041026a44a3 Mon Sep 17 00:00:00 2001 From: mine_ Date: Fri, 2 Jan 2026 15:51:43 +0800 Subject: [PATCH 6/8] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E4=BE=9D?= =?UTF-8?q?=E8=B5=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- HMCLCore/build.gradle.kts | 1 + build.gradle.kts | 1 + gradle/libs.versions.toml | 2 ++ 3 files changed, 4 insertions(+) diff --git a/HMCLCore/build.gradle.kts b/HMCLCore/build.gradle.kts index 86ca2bde92..905c43a8d9 100644 --- a/HMCLCore/build.gradle.kts +++ b/HMCLCore/build.gradle.kts @@ -26,6 +26,7 @@ dependencies { api(libs.chardet) api(libs.jna) api(libs.pci.ids) + api(libs.nbt) compileOnlyApi(libs.jetbrains.annotations) diff --git a/build.gradle.kts b/build.gradle.kts index 2c72a13cec..317c9d2252 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -30,6 +30,7 @@ subprojects { maven(url = repo) } + maven(url = "https://repo.viaversion.com") maven(url = "https://jitpack.io") maven(url = "https://libraries.minecraft.net") } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index d0df83a453..ebace6a96c 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -18,6 +18,7 @@ pci-ids = "0.4.0" java-info = "1.0" authlib-injector = "1.2.7" monet-fx = "0.4.0" +nbt= "5.1.2" # testing junit = "6.0.1" @@ -48,6 +49,7 @@ pci-ids = { module = "org.glavo:pci-ids", version.ref = "pci-ids" } java-info = { module = "org.glavo:java-info", version.ref = "java-info" } authlib-injector = { module = "org.glavo.hmcl:authlib-injector", version.ref = "authlib-injector" } monet-fx = { module = "org.glavo:MonetFX", version.ref = "monet-fx" } +nbt = { module = "com.viaversion:nbt", version.ref = "nbt" } # testing junit-jupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit" } From a54d7966bf892641b68891dcf0a052053067839e Mon Sep 17 00:00:00 2001 From: mine_ Date: Fri, 2 Jan 2026 15:57:27 +0800 Subject: [PATCH 7/8] =?UTF-8?q?feat:=20=E5=88=9D=E6=AD=A5=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E6=92=A4=E9=94=80/=E9=87=8D=E5=81=9A=E7=B3=BB?= =?UTF-8?q?=E7=BB=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hmcl/ui/nbt/EditHistoryManager.java | 70 ++++++++++++ .../jackhuang/hmcl/ui/nbt/NBTEditCommand.java | 105 +++++++++++++++++- .../jackhuang/hmcl/ui/nbt/NBTEditorPage.java | 26 ++--- .../org/jackhuang/hmcl/ui/nbt/NBTUtils.java | 57 +++++++++- 4 files changed, 239 insertions(+), 19 deletions(-) create mode 100644 HMCL/src/main/java/org/jackhuang/hmcl/ui/nbt/EditHistoryManager.java diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/nbt/EditHistoryManager.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/nbt/EditHistoryManager.java new file mode 100644 index 0000000000..ea838f5ef7 --- /dev/null +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/nbt/EditHistoryManager.java @@ -0,0 +1,70 @@ +/* + * Hello Minecraft! Launcher + * Copyright (C) 2026 huangyuhui 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 . + */ +package org.jackhuang.hmcl.ui.nbt; + +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.List; + +public class EditHistoryManager { + private final Deque undoStack = new ArrayDeque<>(); + private final Deque redoStack = new ArrayDeque<>(); + private static final int MAX_HISTORY = 100; + + public void pushAndExecute(@NotNull NBTEditCommand... cmd) { + MacroCommand macroCmd = new MacroCommand(List.of(cmd)); + pushAndExecute(macroCmd); + } + + public void pushAndExecute(MacroCommand macroCmd) { + macroCmd.execute(); + undoStack.push(macroCmd); + redoStack.clear(); + if (undoStack.size() > MAX_HISTORY) undoStack.removeLast(); + } + + public void undo() { + if (undoStack.isEmpty()) return; + MacroCommand macroCmd = undoStack.pop(); + macroCmd.undo(); + redoStack.push(macroCmd); + } + + public void redo() { + if (redoStack.isEmpty()) return; + MacroCommand macroCmd = redoStack.pop(); + macroCmd.execute(); + undoStack.push(macroCmd); + } + + record MacroCommand(List commands) { + public void execute() { + for (NBTEditCommand cmd : commands) { + cmd.execute(); + } + } + + public void undo() { + for (NBTEditCommand cmd : commands.reversed()) { + cmd.undo(); + } + } + } +} diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/nbt/NBTEditCommand.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/nbt/NBTEditCommand.java index 2e61561521..3ef26250c9 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/nbt/NBTEditCommand.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/nbt/NBTEditCommand.java @@ -1,35 +1,132 @@ +/* + * Hello Minecraft! Launcher + * Copyright (C) 2026 huangyuhui 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 . + */ package org.jackhuang.hmcl.ui.nbt; -import com.github.steveice10.opennbt.tag.builtin.Tag; +import com.viaversion.nbt.tag.CompoundTag; +import com.viaversion.nbt.tag.ListTag; +import com.viaversion.nbt.tag.Tag; +import org.jetbrains.annotations.NotNull; public interface NBTEditCommand { void execute(); void undo(); - public record EditValueCommand(Tag target, String newValue, String oldValue) implements NBTEditCommand { + public record EditValueInCompoundTagCommand(@NotNull Tag target, @NotNull CompoundTag father, + @NotNull String tagName, + @NotNull Tag newTag) implements NBTEditCommand { + @Override + public void execute() { + father.put(tagName, newTag); + } + + @Override + public void undo() { + father.remove(tagName); + father.put(tagName, target); + } + } + + public record EditValueInListCommand(@NotNull T target, @NotNull ListTag father, + int index, + @NotNull T newTag) implements NBTEditCommand { @Override public void execute() { + father.set(index, newTag); + } + @Override + public void undo() { + father.set(index, target); + } + } + + public record EditNameInCompoundTagCommand(@NotNull Tag target, @NotNull CompoundTag father, + @NotNull String newName, + @NotNull String oldName) implements NBTEditCommand { + + @Override + public void execute() { + father.remove(oldName); + father.put(newName, target); } @Override public void undo() { + father.remove(newName); + father.put(oldName, target); + } + } + + public record AddInCompoundTagCommand(@NotNull Tag target, @NotNull CompoundTag father, + @NotNull String name) implements NBTEditCommand { + @Override + public void execute() { + father.put(name, target); + } + + @Override + public void undo() { + father.remove(name); } } - public record EditNameCommand(Tag target, String newName, String oldName) implements NBTEditCommand { + public record AddInListCommand(@NotNull T target, + @NotNull ListTag father) implements NBTEditCommand { @Override public void execute() { + father.add(target); + } + + @Override + public void undo() { + father.remove(target); + } + } + + public record DeleteInCompoundTagCommand(@NotNull Tag target, @NotNull CompoundTag father, + @NotNull String tagName) implements NBTEditCommand { + @Override + public void execute() { + father.remove(tagName); } @Override public void undo() { + father.put(tagName, target); + } + } + + public record DeleteInListCommand(@NotNull T target, + @NotNull ListTag father) implements NBTEditCommand { + @Override + public void execute() { + father.remove(target); + } + + @Override + public void undo() { + father.add(target); } } -} \ No newline at end of file +} diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/nbt/NBTEditorPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/nbt/NBTEditorPage.java index 1da9387992..33d7ab0cf4 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/nbt/NBTEditorPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/nbt/NBTEditorPage.java @@ -66,19 +66,19 @@ public NBTEditorPage(Path file) throws IOException { setContent(root); setLoading(true); - FXUtils.applyDragListener(this, - NBTFileType::isNBTFileByExtension, - paths -> { - Path path = paths.get(0); - try { - fireEvent(new PageCloseEvent()); - Controllers.navigate(new NBTEditorPage(path)); - } catch (Throwable e) { - LOG.warning("Fail to open nbt file", e); - Controllers.dialog(i18n("nbt.open.failed") + "\n\n" + StringUtils.getStackTrace(e), - i18n("message.error"), MessageDialogPane.MessageType.ERROR); - } - }); +// FXUtils.applyDragListener(this, +// NBTFileType::isNBTFileByExtension, +// paths -> { +// Path path = paths.get(0); +// try { +// fireEvent(new PageCloseEvent()); +// Controllers.navigate(new NBTEditorPage(path)); +// } catch (Throwable e) { +// LOG.warning("Fail to open nbt file", e); +// Controllers.dialog(i18n("nbt.open.failed") + "\n\n" + StringUtils.getStackTrace(e), +// i18n("message.error"), MessageDialogPane.MessageType.ERROR); +// } +// }); cancelButton = FXUtils.newRaisedButton(i18n("button.cancel")); cancelButton.setOnAction(e -> fireEvent(new PageCloseEvent())); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/nbt/NBTUtils.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/nbt/NBTUtils.java index 9e983dac2c..50b6112ec2 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/nbt/NBTUtils.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/nbt/NBTUtils.java @@ -1,18 +1,36 @@ +/* + * Hello Minecraft! Launcher + * Copyright (C) 2026 huangyuhui 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 . + */ package org.jackhuang.hmcl.ui.nbt; import com.github.steveice10.opennbt.SNBTIO; -import com.github.steveice10.opennbt.tag.builtin.Tag; +import com.viaversion.nbt.tag.*; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; +import java.util.Map; public final class NBTUtils { private NBTUtils() { } - public static String getSNBT(Tag tag) { + public static String getSNBT(com.github.steveice10.opennbt.tag.builtin.Tag tag) { try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { SNBTIO.writeTag(baos, tag, false); return baos.toString(StandardCharsets.UTF_8); @@ -20,4 +38,39 @@ public static String getSNBT(Tag tag) { throw new RuntimeException(e); } } + + public static Tag getTag(Object value) { + if (value instanceof Byte b) { + return new ByteTag(b); + } else if (value instanceof Double d) { + return new DoubleTag(d); + } else if (value instanceof Float f) { + return new FloatTag(f); + } else if (value instanceof Integer i) { + return new IntTag(i); + } else if (value instanceof Long l) { + return new LongTag(l); + } else if (value instanceof Short s) { + return new ShortTag(s); + } else if (value instanceof String s) { + return new StringTag(s); + } + return null; + } + + public static Tag getTag(Map map) { + return new CompoundTag(map); + } + + public static Tag getTag(byte[] value) { + return new ByteArrayTag(value); + } + + public static Tag getTag(int[] value) { + return new IntArrayTag(value); + } + + public static Tag getTag(long[] value) { + return new LongArrayTag(value); + } } From 9447eea84f957d66c000f5425cf2bfb0f1d93598 Mon Sep 17 00:00:00 2001 From: mine_ Date: Fri, 2 Jan 2026 16:02:11 +0800 Subject: [PATCH 8/8] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E4=BD=BF=E7=94=A8?= =?UTF-8?q?=E6=9B=B4=E9=AB=98=E7=89=88=E6=9C=AC=E6=89=8D=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E7=9A=84=E7=89=B9=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/org/jackhuang/hmcl/ui/nbt/EditHistoryManager.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/nbt/EditHistoryManager.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/nbt/EditHistoryManager.java index ea838f5ef7..925ce161cc 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/nbt/EditHistoryManager.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/nbt/EditHistoryManager.java @@ -62,8 +62,8 @@ public void execute() { } public void undo() { - for (NBTEditCommand cmd : commands.reversed()) { - cmd.undo(); + for (int i = commands.size() - 1; i >= 0; i--) { + commands.get(i).undo(); } } }