diff --git a/src/mcedit2/worldlist.py b/src/mcedit2/worldlist.py index bba9f16..6a79dce 100644 --- a/src/mcedit2/worldlist.py +++ b/src/mcedit2/worldlist.py @@ -27,26 +27,11 @@ from mceditlib import worldeditor import logging from mceditlib.findadapter import isLevel, findAdapter from mceditlib.util import displayName +from mceditlib.worldeditor import WorldEditor log = logging.getLogger(__name__) -def lastPlayedTime(adapter): - try: - time = adapter.metadata.LastPlayed - dt = arrow.Arrow.fromtimestamp(time / 1000.0) - return dt - except (AttributeError, ValueError) as e: # no lastplayed, or time is before 1970 - return None - - -def usefulFilename(adapter): - if hasattr(adapter, 'worldFolder'): - return os.path.basename(adapter.worldFolder.filename) - else: - return os.path.basename(adapter.filename) - - class WorldListItemWidget(QtGui.QWidget): doubleClicked = QtCore.Signal() @@ -81,10 +66,18 @@ class WorldListItemWidget(QtGui.QWidget): self.setLayout(layout) - def setWorldInfo(self, (name, lastPlayedText, versionInfo)): + def setWorldInfo(self, (name, lastPlayed, versionInfo)): self.displayNameLabel.setText(name) - self.lastPlayedLabel.setText(lastPlayedText) - self.versionInfoLabel.setText(versionInfo) + if lastPlayed: + lastPlayedText = arrow.Arrow.fromtimestamp(lastPlayed / 1000.0).humanize() + self.lastPlayedLabel.setText(lastPlayedText) + else: + self.lastPlayedLabel.setText("") + + if versionInfo: + self.versionInfoLabel.setText(versionInfo) + else: + self.versionInfoLabel.setText("") def mouseDoubleClickEvent(self, event): self.doubleClicked.emit() @@ -93,39 +86,6 @@ class WorldListItemWidget(QtGui.QWidget): self.sizeLabel.setText(msg) -def getWorldInfo(filename): - worldAdapter = findAdapter(filename, readonly=True) - try: - displayNameLimit = 40 - name = displayName(worldAdapter.filename) - - if len(name) > displayNameLimit: - name = name[:displayNameLimit] + "..." - if usefulFilename(worldAdapter) != displayName(worldAdapter.filename): - name = "%s (%s)" % (name, usefulFilename(worldAdapter)) - - lastPlayed = lastPlayedTime(worldAdapter) - lastPlayedText = lastPlayed.humanize() if lastPlayed else "Unknown" - - version = "Unknown Version" - try: - stackVersion = worldAdapter.blocktypes.itemStackVersion - if stackVersion == VERSION_1_7: - version = "Minecraft 1.7" - if "FML" in worldAdapter.metadata.metadataTag: - version = "MinecraftForge 1.7" - - if stackVersion == VERSION_1_8: - version = "Minecraft 1.8" - except Exception as e: - log.warn("Failed to get version info for %s: %r", filename, e) - return name, lastPlayedText, version - - except Exception as e: - log.error("Failed getting world info for %s: %r", filename, e) - return str(e), "", "" - - class WorldListItemDelegate(QtGui.QStyledItemDelegate): def __init__(self): super(WorldListItemDelegate, self).__init__() @@ -172,8 +132,9 @@ class WorldListModel(QtCore.QAbstractListModel): self.worlds = [] for f in worlds: try: - info = getWorldInfo(f) + info = WorldEditor.getWorldInfo(f) except Exception as e: + log.warn("Error while getting world info, skipping...", exc_info=1) continue else: self.worlds.append((f, info)) @@ -301,7 +262,7 @@ class WorldListWidget(QtGui.QDialog): dead.append(filename) continue try: - displayName, lastPlayed, versionInfo = getWorldInfo(filename) + displayName, lastPlayed, versionInfo = WorldEditor.getWorldInfo(filename) action = self.recentWorldsMenu.addAction(displayName) action._editWorld = _triggered(filename) action.triggered.connect(action._editWorld) diff --git a/src/mceditlib/anvil/adapter.py b/src/mceditlib/anvil/adapter.py index 5e62489..b920e06 100644 --- a/src/mceditlib/anvil/adapter.py +++ b/src/mceditlib/anvil/adapter.py @@ -26,7 +26,7 @@ from mceditlib.selection import BoundingBox from mceditlib import nbtattr from mceditlib.exceptions import PlayerNotFound, ChunkNotPresent, LevelFormatError from mceditlib.revisionhistory import RevisionHistory -from mceditlib.util import exhaust +from mceditlib.util import exhaust, displayName, WorldInfo log = logging.getLogger(__name__) @@ -464,11 +464,65 @@ class AnvilWorldAdapter(object): else: self.loadMetadata() - - def __repr__(self): return "AnvilWorldAdapter(%r)" % self.filename + # --- Summary info --- + + @classmethod + def getWorldInfo(cls, filename, displayNameLimit=40): + try: + if os.path.isdir(filename): + folderName = os.path.basename(filename) + levelDat = os.path.join(filename, "level.dat") + else: + folderName = os.path.basename(os.path.dirname(filename)) + levelDat = filename + + levelTag = nbt.load(levelDat) + try: + displayName = levelTag['Data']['LevelName'].value + + if len(displayName) > displayNameLimit: + displayName = displayName[:displayNameLimit] + "..." + if len(folderName) > displayNameLimit: + folderName = folderName[:displayNameLimit] + "..." + + if folderName != displayName: + displayName = "%s (%s)" % (displayName, folderName) + except Exception as e: + log.warn("Failed to get display name for level.", exc_info=1) + displayName = folderName + + try: + lastPlayedTime = levelTag['Data']['LastPlayed'].value + except Exception as e: + log.warn("Failed to get last-played time for level.", exc_info=1) + lastPlayedTime = 0 + + version = "Unknown Version" + try: + metadata = AnvilWorldMetadata(levelTag) + stackVersion = VERSION_1_8 if metadata.is1_8World() else VERSION_1_7 + + if stackVersion == VERSION_1_7: + version = "Minecraft 1.7" + if "FML" in metadata.metadataTag: + version = "MinecraftForge 1.7" + + if stackVersion == VERSION_1_8: + version = "Minecraft 1.8" + + except Exception as e: + log.warn("Failed to get version info for %s: %r", filename, e, exc_info=1) + + return WorldInfo(displayName, lastPlayedTime, version) + + except Exception as e: + log.error("Failed getting world info for %s: %r", filename, e) + return WorldInfo(str(e), 0, "") + + # --- Create, save, close --- def loadMetadata(self): diff --git a/src/mceditlib/findadapter.py b/src/mceditlib/findadapter.py index 70da231..10b05a5 100644 --- a/src/mceditlib/findadapter.py +++ b/src/mceditlib/findadapter.py @@ -21,7 +21,7 @@ log = logging.getLogger(__name__) # return WorldEditor(filename) -def findAdapter(filename, readonly=False, resume=None): +def findAdapter(filename, readonly=False, resume=None, getInfo=False): """ Try to identify the given file and find an adapter that will open it. Returns an Adapter object if a class is found, and raises ValueError if @@ -30,6 +30,9 @@ def findAdapter(filename, readonly=False, resume=None): Knows about ZipSchematic, PocketWorldAdapter, WorldEditor, JavaLevel, IndevLevel, SchematicFile, and INVEditChest + If getInfo is True, returns a WorldInfo object that contains at least + the world's readable name and Minecraft version. + :param filename: The file to open :type filename: string """ @@ -49,7 +52,10 @@ def findAdapter(filename, readonly=False, resume=None): log.debug("%s: Attempting", cls.__name__) if isLevel(cls, filename): log.debug("%s: Opening", cls.__name__) - level = cls(filename=filename, readonly=readonly, resume=resume) + if getInfo: + level = cls.getWorldInfo(filename) + else: + level = cls(filename=filename, readonly=readonly, resume=resume) log.debug("%s: Opened%s", cls.__name__, " read-only" if readonly else "") return level diff --git a/src/mceditlib/util/__init__.py b/src/mceditlib/util/__init__.py index 74de9d1..d78de91 100644 --- a/src/mceditlib/util/__init__.py +++ b/src/mceditlib/util/__init__.py @@ -2,11 +2,14 @@ util.py """ from __future__ import absolute_import +import collections from contextlib import contextmanager from math import floor import os import sys +WorldInfo = collections.namedtuple("WorldInfo", "displayName lastPlayedTime versionInfo") + @contextmanager def notclosing(f): yield f diff --git a/src/mceditlib/worldeditor.py b/src/mceditlib/worldeditor.py index 4ec14d4..ba49ccf 100644 --- a/src/mceditlib/worldeditor.py +++ b/src/mceditlib/worldeditor.py @@ -247,6 +247,12 @@ class WorldEditor(object): def __repr__(self): return "WorldEditor(adapter=%r)" % self.adapter + # --- Summary Info --- + + @classmethod + def getWorldInfo(cls, filename): + worldInfo = findAdapter(filename, readonly=True, getInfo=True) + return worldInfo # --- Debug ---