#!/usr/bin/env python2.7 # -*- coding: utf8 -*_ """ mcedit.py Startup, main menu, keyboard configuration, automatic updating. """ import OpenGL import sys import os if "-debug" not in sys.argv: OpenGL.ERROR_CHECKING = False import logging # Setup file and stderr logging. logger = logging.getLogger() # Set the log level up while importing OpenGL.GL to hide some obnoxious warnings about old array handlers logger.setLevel(logging.WARN) from OpenGL import GL logger.setLevel(logging.DEBUG) logfile = 'mcedit.log' if hasattr(sys, 'frozen'): if sys.platform == "win32": import esky app = esky.Esky(sys.executable) logfile = os.path.join(app.appdir, logfile) elif sys.platform == "darwin": logfile = os.path.expanduser("~/Library/Logs/" + logfile) fh = logging.FileHandler(logfile, mode="w") fh.setLevel(logging.DEBUG) ch = logging.StreamHandler() ch.setLevel(logging.WARN) if "-v" in sys.argv: ch.setLevel(logging.INFO) if "-vv" in sys.argv: ch.setLevel(logging.DEBUG) class FileLineFormatter(logging.Formatter): def format(self, record): record.__dict__['fileline'] = "%(module)s.py:%(lineno)d" % record.__dict__ record.__dict__['nameline'] = "%(name)s.py:%(lineno)d" % record.__dict__ return super(FileLineFormatter, self).format(record) fmt = FileLineFormatter( '[%(levelname)8s][%(nameline)30s]:%(message)s' ) fh.setFormatter(fmt) ch.setFormatter(fmt) logger.addHandler(fh) logger.addHandler(ch) import albow from albow.dialogs import Dialog from albow.openglwidgets import GLViewport from albow.root import RootWidget import config import directories import functools from glbackground import Panel import glutils import leveleditor from leveleditor import ControlSettings, Settings import mceutils import mcplatform from mcplatform import platform_open import numpy import os import os.path import pygame from pygame import display, key, rect import pymclevel import release import shutil import sys import traceback ESCAPE = '\033' class FileOpener(albow.Widget): is_gl_container = True def __init__(self, mcedit, *args, **kwargs): kwargs['rect'] = mcedit.rect albow.Widget.__init__(self, *args, **kwargs) self.anchor = 'tlbr' self.mcedit = mcedit helpColumn = [] label = albow.Label("{0} {1} {2} {3} {4} {5}".format( config.config.get('Keys', 'Forward'), config.config.get('Keys', 'Left'), config.config.get('Keys', 'Back'), config.config.get('Keys', 'Right'), config.config.get('Keys', 'Up'), config.config.get('Keys', 'Down'), ).upper() + " to move") label.anchor = 'whrt' label.align = 'r' helpColumn.append(label) def addHelp(text): label = albow.Label(text) label.anchor = 'whrt' label.align = "r" helpColumn.append(label) addHelp("{0}".format(config.config.get('Keys', 'Brake').upper()) + " to slow down") addHelp("Right-click to toggle camera control") addHelp("Mousewheel to control tool distance") addHelp("Hold SHIFT to move along a major axis") addHelp("Hold ALT for details") helpColumn = albow.Column(helpColumn, align="r") helpColumn.topright = self.topright helpColumn.anchor = "whrt" #helpColumn.is_gl_container = True self.add(helpColumn) keysColumn = [albow.Label("")] buttonsColumn = [leveleditor.ControlPanel.getHeader()] shortnames = [] for world in self.mcedit.recentWorlds(): shortname = os.path.basename(world) try: if pymclevel.MCInfdevOldLevel.isLevel(world): lev = pymclevel.MCInfdevOldLevel(world, readonly=True) shortname = lev.LevelName if lev.LevelName != lev.displayName: shortname = u"{0} ({1})".format(lev.LevelName, lev.displayName) except Exception, e: logging.warning( 'Couldn\'t get name from recent world: {0!r}'.format(e)) if shortname == "level.dat": shortname = os.path.basename(os.path.dirname(world)) if len(shortname) > 40: shortname = shortname[:37] + "..." shortnames.append(shortname) hotkeys = ([('N', 'Create New World', self.createNewWorld), ('L', 'Load World...', self.mcedit.editor.askLoadWorld), ('O', 'Open a level...', self.promptOpenAndLoad)] + [ ('F{0}'.format(i + 1), shortnames[i], self.createLoadButtonHandler(world)) for i, world in enumerate(self.mcedit.recentWorlds())]) commandRow = mceutils.HotkeyColumn(hotkeys, keysColumn, buttonsColumn) commandRow.anchor = 'lrh' sideColumn = mcedit.makeSideColumn() sideColumn.anchor = 'wh' contentRow = albow.Row((commandRow, sideColumn)) contentRow.center = self.center contentRow.anchor = "rh" self.add(contentRow) self.sideColumn = sideColumn def gl_draw_self(self, root, offset): #self.mcedit.editor.mainViewport.setPerspective(); self.mcedit.editor.drawStars() def idleevent(self, evt): self.mcedit.editor.doWorkUnit() #self.invalidate() def key_down(self, evt): keyname = key.name(evt.key) if keyname == 'f4' and (key.get_mods() & (pygame.KMOD_ALT | pygame.KMOD_LALT | pygame.KMOD_RALT)): raise SystemExit if keyname in ('f1', 'f2', 'f3', 'f4', 'f5'): self.mcedit.loadRecentWorldNumber(int(keyname[1])) if keyname is "o": self.promptOpenAndLoad() if keyname is "n": self.createNewWorld() if keyname is "l": self.mcedit.editor.askLoadWorld() def promptOpenAndLoad(self): try: filename = mcplatform.askOpenFile() if filename: self.mcedit.loadFile(filename) except Exception, e: logging.error('Error during proptOpenAndLoad: {0!r}'.format(e)) def createNewWorld(self): self.parent.createNewWorld() def createLoadButtonHandler(self, filename): return lambda: self.mcedit.loadFile(filename) class KeyConfigPanel(Dialog): keyConfigKeys = [ "", "Forward", "Back", "Left", "Right", "Up", "Down", "Brake", "", "", "Pan Left", "Pan Right", "Pan Up", "Pan Down", "", "", "Rotate", "Roll", "Flip", "Mirror", "Swap", "Increase Reach", "Decrease Reach", "Reset Reach", ] presets = {"WASD": [ ("Forward", "w"), ("Back", "s"), ("Left", "a"), ("Right", "d"), ("Up", "q"), ("Down", "z"), ("Brake", "space"), ("Rotate", "e"), ("Roll", "r"), ("Flip", "f"), ("Mirror", "g"), ("Swap", "x"), ("Increase Reach", "mouse4"), ("Decrease Reach", "mouse5"), ("Reset Reach", "mouse3"), ], "Arrows": [ ("Forward", "up"), ("Back", "down"), ("Left", "left"), ("Right", "right"), ("Up", "page up"), ("Down", "page down"), ("Brake", "space"), ("Rotate", "home"), ("Roll", "end"), ("Flip", "insert"), ("Mirror", "delete"), ("Swap", "\\"), ("Increase Reach", "mouse4"), ("Decrease Reach", "mouse5"), ("Reset Reach", "mouse3"), ], "Numpad": [ ("Forward", "[8]"), ("Back", "[5]"), ("Left", "[4]"), ("Right", "[6]"), ("Up", "[9]"), ("Down", "[3]"), ("Brake", "[0]"), ("Rotate", "[-]"), ("Roll", "[+]"), ("Flip", "[/]"), ("Mirror", "[*]"), ("Swap", "[.]"), ("Increase Reach", "mouse4"), ("Decrease Reach", "mouse5"), ("Reset Reach", "mouse3"), ]} selectedKeyIndex = 0 def __init__(self): Dialog.__init__(self) keyConfigTable = albow.TableView(columns=[albow.TableColumn("Command", 400, "l"), albow.TableColumn("Assigned Key", 150, "r")]) keyConfigTable.num_rows = lambda: len(self.keyConfigKeys) keyConfigTable.row_data = self.getRowData keyConfigTable.row_is_selected = lambda x: x == self.selectedKeyIndex keyConfigTable.click_row = self.selectTableRow tableWidget = albow.Widget() tableWidget.add(keyConfigTable) tableWidget.shrink_wrap() self.keyConfigTable = keyConfigTable buttonRow = (albow.Button("Assign Key...", action=self.askAssignSelectedKey), albow.Button("Done", action=self.dismiss)) buttonRow = albow.Row(buttonRow) choiceButton = mceutils.ChoiceButton(["WASD", "Arrows", "Numpad"], choose=self.choosePreset) if config.config.get("Keys", "Forward") == "up": choiceButton.selectedChoice = "Arrows" if config.config.get("Keys", "Forward") == "[8]": choiceButton.selectedChoice = "Numpad" choiceRow = albow.Row((albow.Label("Presets: "), choiceButton)) self.choiceButton = choiceButton col = albow.Column((tableWidget, choiceRow, buttonRow)) self.add(col) self.shrink_wrap() def choosePreset(self): preset = self.choiceButton.selectedChoice keypairs = self.presets[preset] for configKey, key in keypairs: config.config.set("Keys", configKey, key) def getRowData(self, i): configKey = self.keyConfigKeys[i] if self.isConfigKey(configKey): key = config.config.get("Keys", configKey) else: key = "" return configKey, key def isConfigKey(self, configKey): return not (len(configKey) == 0 or configKey[0] == "<") def selectTableRow(self, i, evt): self.selectedKeyIndex = i if evt.num_clicks == 2: self.askAssignSelectedKey() def askAssignSelectedKey(self): self.askAssignKey(self.keyConfigKeys[self.selectedKeyIndex]) def askAssignKey(self, configKey, labelString=None): if not self.isConfigKey(configKey): return panel = Panel() panel.bg_color = (0.5, 0.5, 0.6, 1.0) if labelString is None: labelString = "Press a key to assign to the action \"{0}\"\n\nPress ESC to cancel.".format(configKey) label = albow.Label(labelString) panel.add(label) panel.shrink_wrap() def panelKeyDown(evt): keyname = key.name(evt.key) panel.dismiss(keyname) def panelMouseDown(evt): button = leveleditor.remapMouseButton(evt.button) if button > 2: keyname = "mouse{0}".format(button) panel.dismiss(keyname) panel.key_down = panelKeyDown panel.mouse_down = panelMouseDown keyname = panel.present() if keyname != "escape": occupiedKeys = [(v, k) for (k, v) in config.config.items("Keys") if v == keyname] oldkey = config.config.get("Keys", configKey) config.config.set("Keys", configKey, keyname) for keyname, setting in occupiedKeys: if self.askAssignKey(setting, "The key {0} is no longer bound to {1}.\n" "Press a new key for the action \"{1}\"\n\n" "Press ESC to cancel." .format(keyname, setting)): config.config.set("Keys", configKey, oldkey) return True else: return True class GraphicsPanel(Panel): def __init__(self, mcedit): Panel.__init__(self) self.mcedit = mcedit # # def getPacks(): # return ["[Default]", "[Current]"] + mcplatform.getTexturePacks() # # def packChanged(): # self.texturePack = self.texturePackChoice.selectedChoice # packs = getPacks() # if self.texturePack not in packs: # self.texturePack = "[Default]" # self.texturePackChoice.selectedChoice = self.texturePack # self.texturePackChoice.choices = packs # # self.texturePackChoice = texturePackChoice = mceutils.ChoiceButton(getPacks(), choose=packChanged) # if self.texturePack in self.texturePackChoice.choices: # self.texturePackChoice.selectedChoice = self.texturePack # # texturePackRow = albow.Row((albow.Label("Skin: "), texturePackChoice)) fieldOfViewRow = mceutils.FloatInputRow("Field of View: ", ref=Settings.fov.propertyRef(), width=100, min=25, max=120) targetFPSRow = mceutils.IntInputRow("Target FPS: ", ref=Settings.targetFPS.propertyRef(), width=100, min=1, max=60) bufferLimitRow = mceutils.IntInputRow("Vertex Buffer Limit (MB): ", ref=Settings.vertexBufferLimit.propertyRef(), width=100, min=0) fastLeavesRow = mceutils.CheckBoxLabel("Fast Leaves", ref=Settings.fastLeaves.propertyRef(), tooltipText="Leaves are solid, like Minecraft's 'Fast' graphics") roughGraphicsRow = mceutils.CheckBoxLabel("Rough Graphics", ref=Settings.roughGraphics.propertyRef(), tooltipText="All blocks are drawn the same way (overrides 'Fast Leaves')") enableMouseLagRow = mceutils.CheckBoxLabel("Enable Mouse Lag", ref=Settings.enableMouseLag.propertyRef(), tooltipText="Enable choppy mouse movement for faster loading.") settingsColumn = albow.Column((fastLeavesRow, roughGraphicsRow, enableMouseLagRow, # texturePackRow, fieldOfViewRow, targetFPSRow, bufferLimitRow, ), align='r') settingsColumn = albow.Column((albow.Label("Settings"), settingsColumn)) settingsRow = albow.Row((settingsColumn,)) optionsColumn = albow.Column((settingsRow, albow.Button("OK", action=mcedit.removeGraphicOptions))) self.add(optionsColumn) self.shrink_wrap() def _reloadTextures(self, pack): if hasattr(pymclevel.alphaMaterials, "terrainTexture"): self.mcedit.displayContext.loadTextures() texturePack = Settings.skin.configProperty(_reloadTextures) class OptionsPanel(Dialog): anchor = 'wh' def __init__(self, mcedit): Dialog.__init__(self) self.mcedit = mcedit autoBrakeRow = mceutils.CheckBoxLabel("Autobrake", ref=ControlSettings.autobrake.propertyRef(), tooltipText="Apply brake when not pressing movement keys") swapAxesRow = mceutils.CheckBoxLabel("Swap Axes Looking Down", ref=ControlSettings.swapAxes.propertyRef(), tooltipText="Change the direction of the Forward and Backward keys when looking down") cameraAccelRow = mceutils.FloatInputRow("Camera Acceleration: ", ref=ControlSettings.cameraAccel.propertyRef(), width=100, min=5.0) cameraDragRow = mceutils.FloatInputRow("Camera Drag: ", ref=ControlSettings.cameraDrag.propertyRef(), width=100, min=1.0) cameraMaxSpeedRow = mceutils.FloatInputRow("Camera Max Speed: ", ref=ControlSettings.cameraMaxSpeed.propertyRef(), width=100, min=1.0) cameraBrakeSpeedRow = mceutils.FloatInputRow("Camera Braking Speed: ", ref=ControlSettings.cameraBrakingSpeed.propertyRef(), width=100, min=1.0) mouseSpeedRow = mceutils.FloatInputRow("Mouse Speed: ", ref=ControlSettings.mouseSpeed.propertyRef(), width=100, min=0.1, max=20.0) undoLimitRow = mceutils.IntInputRow("Undo Limit: ", ref=Settings.undoLimit.propertyRef(), width=100, min=0) invertRow = mceutils.CheckBoxLabel("Invert Mouse", ref=ControlSettings.invertMousePitch.propertyRef(), tooltipText="Reverse the up and down motion of the mouse.") spaceHeightRow = mceutils.IntInputRow("Low Detail Height", ref=Settings.spaceHeight.propertyRef(), tooltipText="When you are this far above the top of the world, move fast and use low-detail mode.") blockBufferRow = mceutils.IntInputRow("Block Buffer (MB):", ref=albow.AttrRef(self, 'blockBuffer'), min=1, tooltipText="Amount of memory used for temporary storage. When more than this is needed, the disk is used instead.") setWindowPlacementRow = mceutils.CheckBoxLabel("Set Window Placement", ref=Settings.setWindowPlacement.propertyRef(), tooltipText="Try to save and restore the window position.") windowSizeRow = mceutils.CheckBoxLabel("Window Resize Alert", ref=Settings.shouldResizeAlert.propertyRef(), tooltipText="Reminds you that the cursor won't work correctly after resizing the window.") visibilityCheckRow = mceutils.CheckBoxLabel("Visibility Check", ref=Settings.visibilityCheck.propertyRef(), tooltipText="Do a visibility check on chunks while loading. May cause a crash.") longDistanceRow = mceutils.CheckBoxLabel("Long-Distance Mode", ref=Settings.longDistanceMode.propertyRef(), tooltipText="Always target the farthest block under the cursor, even in mouselook mode. Shortcut: ALT-Z") flyModeRow = mceutils.CheckBoxLabel("Fly Mode", ref=Settings.flyMode.propertyRef(), tooltipText="Moving forward and backward will not change your altitude in Fly Mode.") self.goPortableButton = goPortableButton = albow.Button("Change", action=self.togglePortable) goPortableButton.tooltipText = self.portableButtonTooltip() goPortableRow = albow.Row((albow.ValueDisplay(ref=albow.AttrRef(self, 'portableLabelText'), width=250, align='r'), goPortableButton)) reportRow = mceutils.CheckBoxLabel("Report Errors", ref=Settings.reportCrashes.propertyRef(), tooltipText="Automatically report errors to the developer.") inputs = ( spaceHeightRow, cameraAccelRow, cameraDragRow, cameraMaxSpeedRow, cameraBrakeSpeedRow, blockBufferRow, mouseSpeedRow, undoLimitRow, ) options = ( longDistanceRow, flyModeRow, autoBrakeRow, swapAxesRow, invertRow, visibilityCheckRow, ) + ( ((sys.platform == "win32" and pygame.version.vernum == (1, 9, 1)) and (windowSizeRow,) or ()) ) + ( reportRow, ) + ( (sys.platform == "win32") and (setWindowPlacementRow,) or () ) + ( goPortableRow, ) rightcol = albow.Column(options, align='r') leftcol = albow.Column(inputs, align='r') optionsColumn = albow.Column((albow.Label("Options"), albow.Row((leftcol, rightcol), align="t"))) settingsRow = albow.Row((optionsColumn,)) optionsColumn = albow.Column((settingsRow, albow.Button("OK", action=self.dismiss))) self.add(optionsColumn) self.shrink_wrap() @property def blockBuffer(self): return Settings.blockBuffer.get() / 1048576 @blockBuffer.setter def blockBuffer(self, val): Settings.blockBuffer.set(int(val * 1048576)) def portableButtonTooltip(self): return ("Click to make your MCEdit install self-contained by moving the settings and schematics into the program folder", "Click to make your MCEdit install persistent by moving the settings and schematics into your Documents folder")[mcplatform.portable] @property def portableLabelText(self): return ("Install Mode: Portable", "Install Mode: Fixed")[1 - mcplatform.portable] def togglePortable(self): textChoices = [ "This will make your MCEdit \"portable\" by moving your settings and schematics into the same folder as {0}. Continue?".format((sys.platform == "darwin" and "the MCEdit application" or "MCEditData")), "This will move your settings and schematics to your Documents folder. Continue?", ] if sys.platform == "darwin": textChoices[1] = "This will move your schematics to your Documents folder and your settings to your Preferences folder. Continue?" alertText = textChoices[mcplatform.portable] if albow.ask(alertText) == "OK": try: [mcplatform.goPortable, mcplatform.goFixed][mcplatform.portable]() except Exception, e: traceback.print_exc() albow.alert(u"Error while moving files: {0}".format(repr(e))) self.goPortableButton.tooltipText = self.portableButtonTooltip() class MCEdit(GLViewport): #debug_resize = True def __init__(self, displayContext, *args): ws = displayContext.getWindowSize() r = rect.Rect(0, 0, ws[0], ws[1]) GLViewport.__init__(self, r) self.displayContext = displayContext self.bg_color = (0, 0, 0, 1) self.anchor = 'tlbr' if not config.config.has_section("Recent Worlds"): config.config.add_section("Recent Worlds") self.setRecentWorlds([""] * 5) self.optionsPanel = OptionsPanel(self) self.graphicOptionsPanel = GraphicsPanel(self) self.keyConfigPanel = KeyConfigPanel() self.droppedLevel = None self.reloadEditor() """ check command line for files dropped from explorer """ if len(sys.argv) > 1: for arg in sys.argv[1:]: f = arg.decode(sys.getfilesystemencoding()) if os.path.isdir(os.path.join(pymclevel.saveFileDir, f)): f = os.path.join(pymclevel.saveFileDir, f) self.droppedLevel = f break if os.path.exists(f): self.droppedLevel = f break self.fileOpener = FileOpener(self) self.add(self.fileOpener) self.fileOpener.focus() editor = None def reloadEditor(self): reload(leveleditor) level = None pos = None if self.editor: level = self.editor.level self.remove(self.editor) c = self.editor.mainViewport pos, yaw, pitch = c.position, c.yaw, c.pitch self.editor = leveleditor.LevelEditor(self) self.editor.anchor = 'tlbr' if level: self.add(self.editor) self.editor.gotoLevel(level) self.focus_switch = self.editor if pos is not None: c = self.editor.mainViewport c.position, c.yaw, c.pitch = pos, yaw, pitch def removeGraphicOptions(self): self.removePanel(self.graphicOptionsPanel) def removePanel(self, panel): if panel.parent: panel.set_parent(None) if self.editor.parent: self.focus_switch = self.editor elif self.fileOpener.parent: self.focus_switch = self.fileOpener def add_right(self, widget): w, h = self.size widget.centery = h // 2 widget.right = w self.add(widget) def showPanel(self, optionsPanel): if optionsPanel.parent: optionsPanel.set_parent(None) optionsPanel.anchor = "whr" self.add_right(optionsPanel) self.editor.mouseLookOff() def showOptions(self): self.optionsPanel.present() def showGraphicOptions(self): self.showPanel(self.graphicOptionsPanel) def showKeyConfig(self): self.keyConfigPanel.present() def loadRecentWorldNumber(self, i): worlds = list(self.recentWorlds()) if i - 1 < len(worlds): self.loadFile(worlds[i - 1]) numRecentWorlds = 5 def removeLevelDat(self, filename): if filename.endswith("level.dat"): filename = os.path.dirname(filename) return filename def recentWorlds(self): worlds = [] for i in range(self.numRecentWorlds): if config.config.has_option("Recent Worlds", str(i)): try: filename = (config.config.get("Recent Worlds", str(i)).decode('utf-8')) worlds.append(self.removeLevelDat(filename)) except Exception, e: logging.error(repr(e)) return list((f for f in worlds if f and os.path.exists(f))) def addRecentWorld(self, filename): filename = self.removeLevelDat(filename) rw = list(self.recentWorlds()) if filename in rw: return rw = [filename] + rw[:self.numRecentWorlds - 1] self.setRecentWorlds(rw) def setRecentWorlds(self, worlds): for i, filename in enumerate(worlds): config.config.set("Recent Worlds", str(i), filename.encode('utf-8')) def makeSideColumn(self): def showLicense(): platform_open(os.path.join(directories.dataDir, "LICENSE.txt")) readmePath = os.path.join(directories.dataDir, "README.html") hotkeys = ([("", "Keys", self.showKeyConfig), ("", "Graphics", self.showGraphicOptions), ("", "Options", self.showOptions), ("", "Homepage", lambda: platform_open("http://www.mcedit.net/")), ("", "About MCEdit", lambda: platform_open("http://www.mcedit.net/about.html")), ("", "Recent Changes", lambda: platform_open("http://www.mcedit.net/changes/%s.html" % release.release)), ("", "License", showLicense), ]) c = mceutils.HotkeyColumn(hotkeys) return c def resized(self, dw, dh): """ Handle window resizing events. """ GLViewport.resized(self, dw, dh) (w, h) = self.size if w == 0 and h == 0: # The window has been minimized, no need to draw anything. self.editor.renderer.render = False return if not self.editor.renderer.render: self.editor.renderer.render = True surf = pygame.display.get_surface() assert isinstance(surf, pygame.Surface) dw, dh = surf.get_size() if w > 0 and h > 0: Settings.windowWidth.set(w) Settings.windowHeight.set(h) config.saveConfig() if pygame.version.vernum == (1, 9, 1): if sys.platform == "win32": if w - dw > 20 or h - dh > 20: if not hasattr(self, 'resizeAlert'): self.resizeAlert = self.shouldResizeAlert if self.resizeAlert: albow.alert("Window size increased. You may have problems using the cursor until MCEdit is restarted.") self.resizeAlert = False shouldResizeAlert = Settings.shouldResizeAlert.configProperty() def loadFile(self, filename): self.removeGraphicOptions() if os.path.exists(filename): try: self.editor.loadFile(filename) except Exception, e: logging.error(u'Failed to load file {0}: {1!r}'.format( filename, e)) return None self.remove(self.fileOpener) self.fileOpener = None if self.editor.level: self.editor.size = self.size self.add(self.editor) self.focus_switch = self.editor def createNewWorld(self): level = self.editor.createNewLevel() if level: self.remove(self.fileOpener) self.editor.size = self.size self.add(self.editor) self.focus_switch = self.editor albow.alert("World created. To expand this infinite world, explore the world in Minecraft or use the Chunk Control tool to add or delete chunks.") def removeEditor(self): self.remove(self.editor) self.fileOpener = FileOpener(self) self.add(self.fileOpener) self.focus_switch = self.fileOpener def confirm_quit(self): if self.editor.unsavedEdits: result = albow.ask("There are {0} unsaved changes.".format(self.editor.unsavedEdits), responses=["Save and Quit", "Quit", "Cancel"]) if result == "Save and Quit": self.saveAndQuit() elif result == "Quit": self.justQuit() elif result == "Cancel": return False else: raise SystemExit def saveAndQuit(self): self.editor.saveFile() raise SystemExit def justQuit(self): raise SystemExit closeMinecraftWarning = Settings.closeMinecraftWarning.configProperty() @classmethod def main(self): displayContext = GLDisplayContext() rootwidget = RootWidget(displayContext.display) mcedit = MCEdit(displayContext) rootwidget.displayContext = displayContext rootwidget.confirm_quit = mcedit.confirm_quit rootwidget.mcedit = mcedit rootwidget.add(mcedit) rootwidget.focus_switch = mcedit if 0 == len(pymclevel.alphaMaterials.yamlDatas): albow.alert("Failed to load minecraft.yaml. Check the console window for details.") if mcedit.droppedLevel: mcedit.loadFile(mcedit.droppedLevel) if hasattr(sys, 'frozen'): # We're being run from a bundle, check for updates. import esky app = esky.Esky( sys.executable.decode(sys.getfilesystemencoding()), 'https://bitbucket.org/codewarrior0/mcedit/downloads' ) try: update_version = app.find_update() except: # FIXME: Horrible, hacky kludge. update_version = None logging.exception('Error while checking for updates') if update_version: answer = albow.ask( 'Version "%s" is available, would you like to ' 'download it?' % update_version, [ 'Yes', 'No', ], default=0, cancel=1 ) if answer == 'Yes': def callback(args): status = args['status'] status_texts = { 'searching': u"Finding updates...", 'found': u"Found version {new_version}", 'downloading': u"Downloading: {received} / {size}", 'ready': u"Downloaded {path}", 'installing': u"Installing {new_version}", 'cleaning up': u"Cleaning up...", 'done': u"Done." } text = status_texts.get(status, 'Unknown').format(**args) panel = Dialog() panel.idleevent = lambda event: panel.dismiss() label = albow.Label(text, width=600) panel.add(label) panel.size = (500, 250) panel.present() try: app.auto_update(callback) except (esky.EskyVersionError, EnvironmentError): albow.alert("Failed to install update %s" % update_version) else: albow.alert("Version %s installed. Restart MCEdit to begin using it." % update_version) raise SystemExit() if mcedit.closeMinecraftWarning: answer = albow.ask("Warning: Only open a world in one program at a time. If you open a world at the same time in MCEdit and in Minecraft, you will lose your work and possibly damage your save file.\n\n If you are using Minecraft 1.3 or earlier, you need to close Minecraft completely before you use MCEdit.", ["Don't remind me again.", "OK"], default=1, cancel=1) if answer == "Don't remind me again.": mcedit.closeMinecraftWarning = False if not Settings.reportCrashesAsked.get(): answer = albow.ask( "When an error occurs, MCEdit can report the details of the error to its developers. " "The error report will include your operating system version, MCEdit version, " "OpenGL version, plus the make and model of your CPU and graphics processor. No personal " "information will be collected.\n\n" "Error reporting can be enabled or disabled in the Options dialog.\n\n" "Enable error reporting?", ["Yes", "No"], default=0) Settings.reportCrashes.set(answer == "Yes") Settings.reportCrashesAsked.set(True) config.saveConfig() if "-causeError" in sys.argv: raise ValueError, "Error requested via -causeError" while True: try: rootwidget.run() except SystemExit: if sys.platform == "win32" and Settings.setWindowPlacement.get(): (flags, showCmd, ptMin, ptMax, rect) = mcplatform.win32gui.GetWindowPlacement(display.get_wm_info()['window']) X, Y, r, b = rect #w = r-X #h = b-Y if (showCmd == mcplatform.win32con.SW_MINIMIZE or showCmd == mcplatform.win32con.SW_SHOWMINIMIZED): showCmd = mcplatform.win32con.SW_SHOWNORMAL Settings.windowX.set(X) Settings.windowY.set(Y) Settings.windowShowCmd.set(showCmd) config.saveConfig() mcedit.editor.renderer.discardAllChunks() mcedit.editor.deleteAllCopiedSchematics() raise except MemoryError: traceback.print_exc() mcedit.editor.handleMemoryError() def main(argv): """ Setup display, bundled schematics. Handle unclean shutdowns. """ try: import squash_python squash_python.uploader.SquashUploader.headers.pop("Content-encoding", None) squash_python.uploader.SquashUploader.headers.pop("Accept-encoding", None) version = release.get_version() client = squash_python.get_client() client.APIKey = "6ea52b17-ac76-4fd8-8db4-2d7303473ca2" client.environment = "testing" if "build" in version else "production" client.host = "http://bugs.mcedit.net" client.notifyPath = "/bugs.php" client.revision = release.get_commit() client.build = version client.timeout = 5 client.disabled = not config.config.getboolean("Settings", "report crashes new") def _reportingChanged(val): client.disabled = not val Settings.reportCrashes.addObserver(client, '_enabled', _reportingChanged) client.reportErrors() client.hook() except (ImportError, UnicodeError) as e: pass try: display.init() except pygame.error, e: os.environ['SDL_VIDEODRIVER'] = 'directx' try: display.init() except pygame.error: os.environ['SDL_VIDEODRIVER'] = 'windib' display.init() pygame.font.init() try: if not os.path.exists(mcplatform.schematicsDir): shutil.copytree( os.path.join(directories.dataDir, u'stock-schematics'), mcplatform.schematicsDir ) except Exception, e: logging.warning('Error copying bundled schematics: {0!r}'.format(e)) try: os.mkdir(mcplatform.schematicsDir) except Exception, e: logging.warning('Error creating schematics folder: {0!r}'.format(e)) try: MCEdit.main() except Exception as e: logging.error("MCEdit version %s", release.get_version()) display.quit() if hasattr(sys, 'frozen') and sys.platform == 'win32': logging.exception("%s", e) print "Press RETURN or close this window to dismiss." raw_input() raise return 0 class GLDisplayContext(object): def __init__(self): self.reset() def getWindowSize(self): w, h = (Settings.windowWidth.get(), Settings.windowHeight.get()) return max(20, w), max(20, h) def displayMode(self): return pygame.OPENGL | pygame.RESIZABLE | pygame.DOUBLEBUF def reset(self): pygame.key.set_repeat(500, 100) try: display.gl_set_attribute(pygame.GL_SWAP_CONTROL, Settings.vsync.get()) except Exception, e: logging.warning('Unable to set vertical sync: {0!r}'.format(e)) display.gl_set_attribute(pygame.GL_ALPHA_SIZE, 8) d = display.set_mode(self.getWindowSize(), self.displayMode()) try: pygame.scrap.init() except: logging.warning('PyGame clipboard integration disabled.') display.set_caption('MCEdit ~ ' + release.release, 'MCEdit') if sys.platform == 'win32' and Settings.setWindowPlacement.get(): Settings.setWindowPlacement.set(False) config.saveConfig() X, Y = Settings.windowX.get(), Settings.windowY.get() if X: w, h = self.getWindowSize() hwndOwner = display.get_wm_info()['window'] flags, showCmd, ptMin, ptMax, rect = mcplatform.win32gui.GetWindowPlacement(hwndOwner) realW = rect[2] - rect[0] realH = rect[3] - rect[1] showCmd = Settings.windowShowCmd.get() rect = (X, Y, X + realW, Y + realH) mcplatform.win32gui.SetWindowPlacement(hwndOwner, (0, showCmd, ptMin, ptMax, rect)) Settings.setWindowPlacement.set(True) config.saveConfig() try: iconpath = os.path.join(directories.dataDir, 'favicon.png') iconfile = file(iconpath, 'rb') icon = pygame.image.load(iconfile, 'favicon.png') display.set_icon(icon) except Exception, e: logging.warning('Unable to set icon: {0!r}'.format(e)) self.display = d GL.glEnableClientState(GL.GL_VERTEX_ARRAY) GL.glAlphaFunc(GL.GL_NOTEQUAL, 0) GL.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA) # textures are 256x256, so with this we can specify pixel coordinates GL.glMatrixMode(GL.GL_TEXTURE) GL.glScale(1 / 256., 1 / 256., 1 / 256.) self.loadTextures() def getTerrainTexture(self, level): return self.terrainTextures.get(level.materials.name, self.terrainTextures["Alpha"]) def loadTextures(self): self.terrainTextures = {} def makeTerrainTexture(mats): w, h = 1, 1 teximage = numpy.zeros((w, h, 4), dtype='uint8') teximage[:] = 127, 127, 127, 255 GL.glTexImage2D( GL.GL_TEXTURE_2D, 0, GL.GL_RGBA8, w, h, 0, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, teximage ) textures = ( (pymclevel.classicMaterials, 'terrain-classic.png'), (pymclevel.indevMaterials, 'terrain-classic.png'), (pymclevel.alphaMaterials, 'terrain.png'), (pymclevel.pocketMaterials, 'terrain-pocket.png') ) for mats, matFile in textures: try: if mats.name == 'Alpha': tex = mceutils.loadAlphaTerrainTexture() else: tex = mceutils.loadPNGTexture(matFile) self.terrainTextures[mats.name] = tex except Exception, e: logging.warning( 'Unable to load terrain from {0}, using flat colors.' 'Error was: {1!r}'.format(matFile, e) ) self.terrainTextures[mats.name] = glutils.Texture( functools.partial(makeTerrainTexture, mats) ) mats.terrainTexture = self.terrainTextures[mats.name] def weird_fix(): try: from OpenGL.platform import win32 win32 except Exception: pass if __name__ == "__main__": sys.exit(main(sys.argv))