Skip to content
Draft
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions HMCL/src/main/java/org/jackhuang/hmcl/ui/SVG.java
Original file line number Diff line number Diff line change
Expand Up @@ -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"),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* Hello Minecraft! Launcher
* Copyright (C) 2026 huangyuhui <[email protected]> and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.jackhuang.hmcl.ui.nbt;

import org.jetbrains.annotations.NotNull;

import java.util.ArrayDeque;
import java.util.Deque;
import java.util.List;

public class EditHistoryManager {
private final Deque<MacroCommand> undoStack = new ArrayDeque<>();
private final Deque<MacroCommand> 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<NBTEditCommand> commands) {
public void execute() {
for (NBTEditCommand cmd : commands) {
cmd.execute();
}
}

public void undo() {
for (int i = commands.size() - 1; i >= 0; i--) {
commands.get(i).undo();
}
}
}
}
132 changes: 132 additions & 0 deletions HMCL/src/main/java/org/jackhuang/hmcl/ui/nbt/NBTEditCommand.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
/*
* Hello Minecraft! Launcher
* Copyright (C) 2026 huangyuhui <[email protected]> and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.jackhuang.hmcl.ui.nbt;

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 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<T extends Tag>(@NotNull T target, @NotNull ListTag<T> 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 AddInListCommand<T extends Tag>(@NotNull T target,
@NotNull ListTag<T> 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<T extends Tag>(@NotNull T target,
@NotNull ListTag<T> father) implements NBTEditCommand {

@Override
public void execute() {
father.remove(target);
}

@Override
public void undo() {
father.add(target);
}
}
}
59 changes: 35 additions & 24 deletions HMCL/src/main/java/org/jackhuang/hmcl/ui/nbt/NBTEditorPage.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@
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.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;
Expand All @@ -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
Expand All @@ -50,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);

Expand All @@ -65,26 +66,28 @@ public NBTEditorPage(Path file) throws IOException {
setContent(root);
setLoading(true);

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"));
// 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()));
onEscPressed(this, cancelButton::fire);

actions.getChildren().setAll(saveButton, cancelButton);
loadTree();
}

public void loadTree() {
Task.supplyAsync(() -> type.readAsTree(file))
.whenComplete(Schedulers.javafx(), (result, exception) -> {
if (exception == null) {
Expand All @@ -100,12 +103,20 @@ public NBTEditorPage(Path file) throws IOException {
}).start();
}

public void save() throws IOException {
// TODO
}

@Override
public ReadOnlyObjectProperty<State> stateProperty() {
return state;
}

@Override
public void refresh() {
root.setCenter(null);
setLoading(true);
loadTree();
}

@Override
public BooleanProperty refreshableProperty() {
return new SimpleBooleanProperty(true);
}
}
26 changes: 11 additions & 15 deletions HMCL/src/main/java/org/jackhuang/hmcl/ui/nbt/NBTFileType.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -41,7 +37,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))) {
Expand Down Expand Up @@ -109,20 +105,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) {
Expand Down Expand Up @@ -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;
}
}
Loading