Add NBT editor

This commit is contained in:
leha-code 2022-04-08 07:59:20 -04:00
parent f52127380d
commit 98ce32ff4d
No known key found for this signature in database
GPG Key ID: 15227A6455DDF7EE
7 changed files with 287 additions and 33 deletions

View File

@ -92,5 +92,4 @@ Use [our PPA](https://github.com/mcpiscript/ppa)
### Additional Credits
Heart, Planet, Pi, Steve and Portal icons by LEHAtupointow
Wrench by [Santoniche on OpenGameArt.org](https://opengameart.org/content/wrench-0).

BIN
planet/assets/mcpiedit.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
planet/assets/wrench.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 255 B

BIN
planet/assets/wrench512.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -50,6 +50,7 @@ if os.path.exists("/usr/lib/planet-launcher/"):
import launcher
from splashes import SPLASHES
import web
import mcpiedit
# PyQt5 imports
from PyQt5.QtCore import *
@ -263,7 +264,10 @@ class Planet(QMainWindow):
self.setWindowIcon(
QIcon(f"{absolute_path}/assets/logo512.png")
) # Set the window icon
self.widget = QWidget()
self.layout = QStackedLayout()
tabs = QTabWidget() # Create the tabs
tabs.setTabPosition(QTabWidget.North) # Select the tab position.
tabs.setMovable(True) # Allow tab movement.
@ -285,12 +289,16 @@ class Planet(QMainWindow):
) # Set the icon
# mods_tab = tabs.addTab(self.custom_mods_tab(), "Mods")
# tabs.setTabIcon(mods_tab, QIcon(f"{absolute_path}/assets/portal512.png"))
changelog_tab = tabs.addTab(self.changelog_tab(), "Changelog") # Changelog tab
settings_tab = tabs.addTab(self.settings_tab(), "Settings") # Changelog tab
tabs.setTabIcon(
changelog_tab, QIcon(f"{absolute_path}/assets/git.png")
) # Set the icon
settings_tab, QIcon(f"{absolute_path}/assets/wrench512.png")
)
self.layout.addWidget(tabs)
self.widget.setLayout(self.layout)
self.setCentralWidget(tabs) # Set the central widget to the tabs
self.setCentralWidget(self.widget) # Set the central widget to the tabs
self.setGeometry(
600, 900, 200, 200
@ -393,25 +401,22 @@ class Planet(QMainWindow):
"Hide Launcher"
) # RadioButton used for hiding the launcher
self.skin_button = QPushButton("Select Skin")
self.skin_button.clicked.connect(self.select_skin)
self.versionbox = QComboBox()
versions = json.loads(web.get_versions())["versions"]
#versions = json.loads(web.get_versions())["versions"]
version_list = list()
#version_list = list()
for version in versions:
version_list.append(versions[version])
#for version in versions:
# version_list.append(versions[version])
version_name_list = list()
#version_name_list = list()
for version in version_list:
version_name_list.append(version["name"])
#for version in version_list:
# version_name_list.append(version["name"])
self.versionbox.addItems(version_name_list) # Set the values
self.versionbox.setCurrentText("Short") # Set the default option
# self.versionbox.addItems(version_name_list) # Set the values
#self.versionbox.setCurrentText("Short") # Set the default option
self.playbutton = QPushButton("Play") # The most powerful button
@ -445,9 +450,7 @@ class Planet(QMainWindow):
layout.addWidget(self.showlauncher, 6, 4)
layout.addWidget(self.skin_button, 6, 0)
layout.addWidget(self.versionbox, 8, 0, 1, 3)
#layout.addWidget(self.versionbox, 8, 0, 1, 3)
layout.addWidget(self.playbutton, 8, 4, 1, 2)
@ -563,14 +566,71 @@ class Planet(QMainWindow):
return widget
def changelog_tab(self):
web = QWebEngineView() # Create a webview object
web.load(
def changelog_widget(self):
web_engine = QWebEngineView() # Create a webview object
web_engine.load(
QUrl().fromLocalFile(f"{absolute_path}/assets/changelog.html")
) # Load the local file
# TODO: Use two different tabs for the webview
return web
return web_engine
def settings_widget(self):
widget = QWidget()
layout = QGridLayout()
skin_label = QLabel("Set the skin")
self.skin_button = QPushButton("Select Skin")
self.skin_button.clicked.connect(self.select_skin)
config_label = QLabel("Reset config")
self.delete_config_button = QPushButton("Delete config")
self.delete_config_button.clicked.connect(self.delete_config)
appimage_label = QLabel("Delete executable")
self.delete_appimage_button = QPushButton("Delete")
self.delete_appimage_button.clicked.connect(self.delete_appimage)
layout.addWidget(skin_label, 0, 0)
layout.addWidget(self.skin_button, 0, 1)
layout.addWidget(config_label, 1, 0)
layout.addWidget(self.delete_config_button, 1, 1)
layout.addWidget(appimage_label, 2, 0)
layout.addWidget(self.delete_appimage_button, 2, 1)
widget.setLayout(layout)
return widget
def settings_tab(self):
tabs = QTabWidget()
tabs.setTabPosition(QTabWidget.South)
settings_tab = tabs.addTab(self.settings_widget(), "General")
changelog_tab = tabs.addTab(self.changelog_widget(), "Changelog")
editor_tab = tabs.addTab(mcpiedit.NBTEditor(), "MCPIEdit")
tabs.setTabIcon(
settings_tab, QIcon(f"{absolute_path}/assets/wrench512.png")
) # Set the icon
tabs.setTabIcon(
changelog_tab, QIcon(f"{absolute_path}/assets/git.png")
) # Set the icon
tabs.setTabIcon(
editor_tab, QIcon(f"{absolute_path}/assets/mcpiedit.png")
) # Set the icon
return tabs
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:
@ -629,6 +689,16 @@ class Planet(QMainWindow):
Image.open(filename[0]).crop((0, 0, 64, 32)).convert("RGBA").save(
f"/home/{USER}/.minecraft-pi/overrides/images/mob/char.png"
)
def delete_config(self):
os.remove(f"/home/{USER}/.planet-launcher/config.json")
self.hide()
sys.exit()
def delete_appimage(self):
os.remove(f"/home/{USER}/.planet-launcher/minecraft.AppImage")
self.hide()
sys.exit()
def launch(self):
self.save_profile()

177
planet/mcpiedit.py Normal file
View File

@ -0,0 +1,177 @@
# MCPIEdit
# This is a different editor from revival's MCPIedit!
# This one is intended to work with Planet or it can work on its own
import sys
import os
import pathlib
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
import nbt
USER = os.getenv("USER") # Get the username, used for later
absolute_path = pathlib.Path(__file__).parent.absolute()
if str(absolute_path).startswith("/usr/bin"):
absolute_path = "/usr/lib/planet-launcher/"
sys.path.append(absolute_path)
if os.path.exists("/usr/lib/planet-launcher/"):
sys.path.append("/usr/lib/planet-launcher/")
if not os.path.exists(f"/home/{USER}/.minecraft-pi/games/com.mojang/minecraftWorlds/"):
os.makedirs(f"/home/{USER}/.minecraft-pi/games/com.mojang/minecraftWorlds/")
GAME_TYPES = {
"Survival" : nbt.nbtlib.Int(0),
"Creative" : nbt.nbtlib.Int(1),
}
GAME_INTREGERS = {
"0" : "Survival",
"1" : "Creative"
}
class FileSelectorTab(QWidget):
def __init__(self):
super().__init__()
layout = QGridLayout()
self.load_button = QPushButton("Select NBT File")
layout.addWidget(self.load_button, 0, 0)
self.setLayout(layout)
class EditorTab(QWidget):
def __init__(self, filename):
super().__init__()
layout = QVBoxLayout()
self.nbt = nbt.load_nbt(filename, True)
self.filename = filename
self.tabs = QTabWidget()
self.tabs.setTabPosition(QTabWidget.West)
self.tabs.setMovable(True)
self.tabs.addTab(self.main_tab(), "World")
self.name_edit.setText(str(self.nbt["LevelName"]))
self.timestamp_box.setValue(int(self.nbt["LastPlayed"]))
self.game_box.setCurrentText(GAME_INTREGERS[str(int(self.nbt["GameType"]))])
self.seed_edit.setText(str(int(self.nbt["RandomSeed"])))
layout.addWidget(self.tabs)
self.setLayout(layout)
def main_tab(self):
widget = QWidget()
layout = QGridLayout()
self.name_label = QLabel("World name")
self.name_edit = QLineEdit()
self.name_edit.setPlaceholderText("OneChunk")
self.seed_label = QLabel("World Seed")
self.seed_edit = QLineEdit()
self.seed_edit.setPlaceholderText("-121542953")
self.seed_edit.setValidator(QIntValidator())
self.timestamp_label = QLabel("Last Played Timestamp")
self.timestamp_box = QSpinBox()
self.timestamp_box.setMaximum(2147483647)
self.game_label = QLabel("Game mode")
self.game_box = QComboBox()
self.game_box.addItems(
[
"Survival",
"Creative"
]
)
self.save_button = QPushButton("Save")
self.save_button.clicked.connect(self.save)
layout.addWidget(self.name_label, 0, 0)
layout.addWidget(self.name_edit, 0, 1)
layout.addWidget(self.seed_label, 1, 0)
layout.addWidget(self.seed_edit, 1, 1)
layout.addWidget(self.timestamp_label, 2, 0)
layout.addWidget(self.timestamp_box, 2, 1)
layout.addWidget(self.game_label, 3, 0)
layout.addWidget(self.game_box, 3, 1)
layout.addWidget(self.save_button, 4, 1)
widget.setLayout(layout)
return widget
def save(self):
self.nbt['LevelName'] = nbt.nbtlib.String(self.name_edit.text())
self.nbt["LastPlayed"] = nbt.nbtlib.Long(self.timestamp_box.value())
self.nbt["GameType"] = GAME_TYPES[self.game_box.currentText()]
self.nbt["RandomSeed"] = nbt.nbtlib.Long(int(self.seed_edit.text()))
nbt.save_nbt(self.nbt, self.filename)
class NBTEditor(QWidget):
def __init__(self):
super().__init__()
self.nbt_file = ""
self.layout = QStackedLayout()
selector = FileSelectorTab()
selector.load_button.clicked.connect(self.load_nbt)
self.layout.addWidget(selector)
self.setLayout(self.layout)
def load_nbt(self):
print("Hellow, Cruel World!")
fname = QFileDialog.getOpenFileName(self, 'Open NBT File', f'/home/{USER}/.minecraft-pi/games/com.mojang/minecraftWorlds/',"Minecraft Pi Level NBT (level.dat)")
self.layout.addWidget(EditorTab(fname[0]))
self.layout.setCurrentIndex(1)
self.setLayout(self.layout)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = QMainWindow()
window.setCentralWidget(NBTEditor())
window.setWindowTitle("MCPIEdit")
window.show()
app.exec()

View File

@ -37,18 +37,26 @@ def remove_header(filename: str):
# write_file.write(data[8:])
# This is WIP code! Do not use!
return subprocess.Popen(["/usr/bin/pi-nbt", "remove-header", filename])
return subprocess.Popen(["pi-nbt", "remove-header", filename, filename+"_temp.dat"]).wait()
def add_header(filename: str):
return subprocess.Popen(["/usr/bin/pi-nbt", "add-header", filename])
return subprocess.Popen(["pi-nbt", "add-header", filename+"_temp.dat", filename]).wait()
def load_nbt(filename: str, header_type = None):
if header_type == "level.dat":
def load_nbt(filename: str, header=False):
if header:
remove_header(filename)
elif header_type not in ["level.dat", None]:
raise Exception("Invalid file! Please use header_type=None to open a file without a header")
return nbtlib.load(filename, gzipped=False, byteorder="little")
with nbtlib.load(filename=filename+"_temp.dat", gzipped=False, byteorder="little") as nbt:
return nbt
def save_nbt(nbt: nbtlib.File, filename: str, header=True):
nbt.save(filename+"_temp.dat")
if header:
add_header(filename)