This repository has been archived on 2024-06-13. You can view files and clone it, but cannot push or open issues or pull requests.
mcedit/mcedit.py

1194 lines
41 KiB
Python
Executable File

#!/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 = [
"<Movement Controls>",
"Forward",
"Back",
"Left",
"Right",
"Up",
"Down",
"Brake",
"",
"<Camera Controls>",
"Pan Left",
"Pan Right",
"Pan Up",
"Pan Down",
"",
"<Tool Controls>",
"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))