Add a getWorldInfo method to WorldEditor
This allows the WorldList to get summary info for each world without loading it and parsing its blocktypes, etc
This commit is contained in:
parent
1a11979d4a
commit
6c425d651d
@ -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)
|
||||
|
@ -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):
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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 ---
|
||||
|
||||
|
Reference in New Issue
Block a user