"""Copyright (c) 2010-2012 David Rio Vierra
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE."""
# Moving this here to get log entries ASAP -- D.C.-G.
import logging
from pymclevel.schematic import StructureNBT
log = logging.getLogger(__name__)
#-# Modified by D.C.-G. for translation purpose
#.# Marks the layout modifications. -- D.C.-G.
from editortools.thumbview import ThumbView
import keys
import pygame
from albow.fields import FloatField
from albow import ChoiceButton, TextInputRow, CheckBoxLabel, showProgress, IntInputRow, ScrollPanel
from editortools.blockview import BlockButton
import ftp_client
import sys
from pymclevel import nbt
from editortools.select import SelectionTool
from pymclevel.box import BoundingBox
from waypoints import WaypointManager
from editortools.timeditor import TimeEditor
"""
leveleditor.py
Viewport objects for Camera and Chunk views, which respond to some keyboard and
mouse input. LevelEditor object responds to some other keyboard and mouse
input, plus handles the undo stack and implements tile entity editors for
chests, signs, and more. Toolbar object which holds instances of EditorTool
imported from editortools/
"""
import gc
import os
import math
import csv
import copy
import time
import numpy
from config import config
from config import DEF_ENC
import frustum
import glutils
import release
import mceutils
import platform
import functools
import editortools
import itertools
import mcplatform
import pymclevel
import renderer
import directories
import panels
import viewports
import shutil
from os.path import dirname, isdir
from datetime import datetime, timedelta
from collections import defaultdict, deque
from OpenGL import GL
from albow import alert, ask, AttrRef, Button, Column, input_text, IntField, Row, \
TableColumn, TableView, TextFieldWrapped, TimeField, Widget, CheckBox, \
unparented
import albow.resource
albow.resource.font_proportion = config.settings.fontProportion.get()
get_font = albow.resource.get_font
from albow.controls import Label, ValueDisplay, Image, RotatableImage
from albow.dialogs import Dialog, QuickDialog, wrapped_label
from albow.openglwidgets import GLOrtho, GLViewport
from albow.translate import _
from pygame import display, event, mouse, MOUSEMOTION, image
from depths import DepthOffset
from editortools.operation import Operation
from editortools.chunk import GeneratorPanel, ChunkTool
from glbackground import GLBackground, Panel
from glutils import Texture
from mcplatform import askSaveFile
from pymclevel.minecraft_server import alphanum_key # ?????
from renderer import MCRenderer
from pymclevel.entity import Entity
from pymclevel.infiniteworld import AnvilWorldFolder, SessionLockLost, MCAlphaDimension,\
MCInfdevOldLevel
# Block and item translation
from mclangres import translate as trn
try:
import resource # @UnresolvedImport
resource.setrlimit(resource.RLIMIT_NOFILE, (500, -1))
except:
pass
# Label = GLLabel
arch = platform.architecture()[0]
[docs]def DebugDisplay(obj, *attrs):
col = []
for attr in attrs:
def _get(attr):
return lambda: str(getattr(obj, attr))
col.append(Row((Label(attr + " = "), ValueDisplay(width=600, get_value=_get(attr)))))
col = Column(col, align="l")
b = GLBackground()
b.add(col)
b.shrink_wrap()
return b
# if self.defaultScale >= 0.5:
# return super(ChunkViewport, self).drawCeiling()
[docs]class LevelEditor(GLViewport):
anchor = "tlbr"
__maxCopies = 32
def __init__(self, mcedit):
self.mcedit = mcedit
rect = mcedit.rect
GLViewport.__init__(self, rect)
self.currentCopyPage = 0
self.frames = 0
self.frameStartTime = datetime.now()
self.oldFrameStartTime = self.frameStartTime
self.dragInProgress = False
self.debug = 0
self.debugString = ""
self.testBoardKey = 0
self.world_from_ftp = False
self.perfSamples = 5
self.frameSamples = [timedelta(0, 0, 0)] * 5
self.unsavedEdits = 0
self.undoStack = []
self.redoStack = []
self.copyStack = []
self.nbtCopyBuffer = mcedit.nbtCopyBuffer
self.level = None
# Tracking the dimension changes.
self.prev_dimension = None
self.new_dimension = None
self.cameraInputs = [0., 0., 0.]
self.cameraPanKeys = [0., 0.]
self.movements = [
config.keys.left.get(),
config.keys.right.get(),
config.keys.forward.get(),
config.keys.back.get(),
config.keys.up.get(),
config.keys.down.get()
]
self.toolbarKeys = [
config.keys.select.get(),
config.keys.brush.get(),
config.keys.clone.get(),
config.keys.fillAndReplace.get(),
config.keys.filter.get(),
config.keys.importKey.get(),
config.keys.players.get(),
config.keys.worldSpawnpoint.get(),
config.keys.chunkControl.get(),
config.keys.nbtExplorer.get()
]
self.cameraPan = [
config.keys.panLeft.get(),
config.keys.panRight.get(),
config.keys.panUp.get(),
config.keys.panDown.get()
]
self.sprintKey = config.keys.sprint.get()
self.different_keys = {
"mouse1": "Mouse1",
"mouse2": "Mouse2",
"mouse3": "Button 3",
"mouse4": "Scroll Up",
"mouse5": "Scroll Down",
"mouse6": "Button 4",
"mouse7": "Button 5",
"Delete": "Del"
}
self.rightClickNudge = False
self.root = self.get_root()
self.cameraToolDistance = self.defaultCameraToolDistance
self.createRenderers()
self.sixteenBlockTex = self.genSixteenBlockTexture()
self.generateStars()
self.optionsBar = Widget()
self.mcEditButton = Button("Menu", action=self.showControls)
def chooseDistance():
self.changeViewDistance(int(self.viewDistanceReadout.get_value()))
if self.renderer.viewDistance not in range(2,32,2):
self.renderer.viewDistance = 8
self.viewDistanceReadout = ChoiceButton(["%s"%a for a in range(2,34,2)], width=20, ref=AttrRef(self.renderer, "viewDistance"), choose=chooseDistance)
self.viewDistanceReadout.selectedChoice = "%s"%self.renderer.viewDistance
self.viewDistanceReadout.shrink_wrap()
def showViewOptions():
col = [CheckBoxLabel("Entities", expand=0, fg_color=(0xff, 0x22, 0x22),
ref=config.settings.drawEntities),
CheckBoxLabel("Items", expand=0, fg_color=(0x22, 0xff, 0x22), ref=config.settings.drawItems),
CheckBoxLabel("TileEntities", expand=0, fg_color=(0xff, 0xff, 0x22),
ref=config.settings.drawTileEntities),
CheckBoxLabel("TileTicks", expand=0, ref=config.settings.drawTileTicks),
CheckBoxLabel("Player Heads", expand=0, ref=config.settings.drawPlayerHeads),
CheckBoxLabel("Unpopulated Chunks", expand=0, fg_color=renderer.TerrainPopulatedRenderer.color,
ref=config.settings.drawUnpopulatedChunks),
CheckBoxLabel("Chunks Borders", expand=0, fg_color=renderer.ChunkBorderRenderer.color,
ref=config.settings.drawChunkBorders),
CheckBoxLabel("Sky", expand=0, ref=config.settings.drawSky),
CheckBoxLabel("Fog", expand=0, ref=config.settings.drawFog),
CheckBoxLabel("Ceiling", expand=0, ref=config.settings.showCeiling),
CheckBoxLabel("Chunk Redraw", expand=0, fg_color=(0xff, 0x99, 0x99),
ref=config.settings.showChunkRedraw),
CheckBoxLabel("Hidden Ores", expand=0, ref=config.settings.showHiddenOres,
tooltipText="Check to show/hide specific ores using the settings below.")]
for ore in config.settings.hiddableOres.get():
col.append(CheckBoxLabel("* " + _(self.level.materials[ore].name.replace(" Ore", "")), expand=0,
ref=config.settings["showOre{}".format(ore)]))
col = Column(col, align="r", spacing=4, expand='h')
d = QuickDialog()
d.add(col)
d.shrink_wrap()
d.topleft = self.viewButton.bottomleft
d.present(centered=False)
self.viewButton = Button("Show...", action=showViewOptions)
self.waypointManager = WaypointManager(editor=self)
self.waypointManager.load()
#self.loadWaypoints()
self.waypointsButton = Button("Waypoints", action=self.showWaypointsDialog)
self.viewportButton = Button("Camera View", action=self.swapViewports,
tooltipText=_("Shortcut: {0}").format(_(config.keys.toggleView.get())))
self.recordUndoButton = CheckBoxLabel("Undo", ref=AttrRef(self, 'recordUndo'))
# TODO: Mark
self.sessionLockLock = Image(image.load(open(directories.getDataDir(os.path.join(u"toolicons",
u"session_good.png")), 'rb'), 'rb'))
self.sessionLockLock.tooltipText = "Session Lock is being used by MCEdit"
self.sessionLockLock.mouse_down = self.mouse_down_session
self.sessionLockLabel = Label("Session:", margin=0)
self.sessionLockLabel.tooltipText = "Session Lock is being used by MCEdit"
self.sessionLockLabel.mouse_down = self.mouse_down_session
# TODO: Marker
row = (self.mcEditButton, Label("View Distance:"), self.viewDistanceReadout,
self.viewButton, self.viewportButton, self.recordUndoButton,
Row((self.sessionLockLabel, self.sessionLockLock), spacing=2), self.waypointsButton)
self.topRow = row = Row(row)
self.add(row)
self.statusLabel = ValueDisplay(width=self.width, ref=AttrRef(self, "statusText"))
self.mainViewport = viewports.CameraViewport(self)
self.chunkViewport = viewports.ChunkViewport(self)
self.mainViewport.height -= row.height
self.mainViewport.height -= self.statusLabel.height
self.statusLabel.bottom = self.bottom
self.statusLabel.anchor = "blrh"
self.add(self.statusLabel)
self.viewportContainer = Widget(is_gl_container=True, anchor="tlbr")
self.viewportContainer.top = row.bottom
self.viewportContainer.size = self.mainViewport.size
self.add(self.viewportContainer)
config.settings.viewMode.addObserver(self)
config.settings.undoLimit.addObserver(self)
self.reloadToolbar()
self.currentTool = None
self.toolbar.selectTool(0)
self.controlPanel = panels.ControlPanel(self)
# logger = logging.getLogger()
# adapter = logging.StreamHandler(sys.stdout)
# adapter.addFilter(LogFilter(self))
# logger.addHandler(adapter)
self.revertPlayerSkins = False
#-# Translation live update preparation
[docs] def set_update_ui(self, v):
GLViewport.set_update_ui(self, v)
if v:
self.statusLabel.width = self.width
self.topRow.calc_size()
self.controlPanel.set_update_ui(v)
# Update the unparented widgets.
[a.set_update_ui(v) for a in unparented.values()]
#-#
def __del__(self):
self.deleteAllCopiedSchematics()
[docs] def showCreateDialog(self):
widg = Widget()
nameField = TextFieldWrapped(width=100)
xField = FloatField()
yField = FloatField()
zField = FloatField()
saveCameraRotation = CheckBoxLabel("Save Rotation")
xField.value = round(self.mainViewport.cameraPosition[0], 2)
yField.value = round(self.mainViewport.cameraPosition[1], 2)
zField.value = round(self.mainViewport.cameraPosition[2], 2)
coordRow = Row((Label("X:"), xField, Label("Y:"), yField, Label("Z:"), zField))
col = Column((Row((Label("Waypoint Name:"), nameField)), coordRow, saveCameraRotation), align="c")
widg.add(col)
widg.shrink_wrap()
result = Dialog(widg, ["Create", "Cancel"]).present()
if result == "Create":
if nameField.value in self.waypointManager.waypoint_names:
self.Notify("You cannot have duplicate waypoint names")
return
if saveCameraRotation.checkbox.value:
self.waypointManager.add_waypoint(nameField.value, (xField.value, yField.value, zField.value), (self.mainViewport.yaw, self.mainViewport.pitch), self.level.dimNo)
else:
self.waypointManager.add_waypoint(nameField.value, (xField.value, yField.value, zField.value), (0.0, 0.0), self.level.dimNo)
if "Empty" in self.waypointManager.waypoints:
del self.waypointManager.waypoints["Empty"]
self.waypointDialog.dismiss()
self.waypointManager.save()
[docs] def gotoWaypoint(self):
if self.waypointsChoiceButton.value == "Empty":
return
self.gotoDimension(self.waypointManager.waypoints[self.waypointsChoiceButton.value][5])
self.mainViewport.skyList = None
self.mainViewport.drawSkyBackground()
self.mainViewport.cameraPosition = self.waypointManager.waypoints[self.waypointsChoiceButton.value][:3]
self.mainViewport.yaw = self.waypointManager.waypoints[self.waypointsChoiceButton.value][3]
self.mainViewport.pitch = self.waypointManager.waypoints[self.waypointsChoiceButton.value][4]
self.mainViewport.skyList = None
self.mainViewport.drawSkyBackground()
self.waypointDialog.dismiss()
[docs] def deleteWaypoint(self):
self.waypointDialog.dismiss()
if self.waypointsChoiceButton.value == "Empty":
return
self.waypointManager.delete(self.waypointsChoiceButton.value)
[docs] def gotoLastWaypoint(self, lastPos):
#!# Added checks to verify the waypoint NBT data consistency. (Avoid crashed in case of corrupted file.)
if lastPos.get("Dimension") and lastPos.get("Coordinates") and lastPos.get("Rotation"):
self.gotoDimension(lastPos["Dimension"].value)
self.mainViewport.skyList = None
self.mainViewport.drawSkyBackground()
self.mainViewport.cameraPosition = [lastPos["Coordinates"][0].value,
lastPos["Coordinates"][1].value,
lastPos["Coordinates"][2].value
]
self.mainViewport.yaw = lastPos["Rotation"][0].value
self.mainViewport.pitch = lastPos["Rotation"][1].value
[docs] def showWaypointsDialog(self):
if not isinstance(self.level, (MCInfdevOldLevel, MCAlphaDimension)):
print type(self.level)
self.Notify("Waypoints currently only support PC Worlds")
return
self.waypointDialog = QuickDialog()
self.waypointsChoiceButton = ChoiceButton(self.waypointManager.waypoints.keys())
createWaypointButton = Button("Create Waypoint", action=self.showCreateDialog)
gotoWaypointButton = Button("Goto Waypoint", action=self.gotoWaypoint)
deleteWaypointButton = Button("Delete Waypoint", action=self.deleteWaypoint)
saveCameraOnClose = CheckBoxLabel("Save Camera position on world close",
ref=config.settings.savePositionOnClose)
col = Column((self.waypointsChoiceButton, Row((createWaypointButton, gotoWaypointButton, deleteWaypointButton)), saveCameraOnClose, Button("Close", action=self.waypointDialog.dismiss)))
self.waypointDialog.add(col)
self.waypointDialog.shrink_wrap()
#qd.topleft = self.waypointsButton.bottomleft
self.waypointDialog.present(True)
[docs] def mouse_down_session(self, evt):
class SessionLockOptions(Panel):
def __init__(self, parent):
Panel.__init__(self, parent)
self.autoChooseCheckBox = CheckBoxLabel("Override Minecraft Changes (Not Recommended)",
ref=config.session.override,
tooltipText="Always override Minecraft changes when map is open in MCEdit. (Not recommended)")
col = Column((Label("Session Lock Options"), self.autoChooseCheckBox, Button("OK", action=self.dismiss)))
self.add(col)
self.shrink_wrap()
if evt.button == 3:
sessionLockPanel = SessionLockOptions(self)
sessionLockPanel.present()
_viewMode = None
_level = None
@property
def level(self):
return self._level
@level.setter
def level(self, level):
self._level = level
if hasattr(level, "setSessionLockCallback"):
level.setSessionLockCallback(self.lockAcquired, self.lockLost)
@property
def viewMode(self):
return self._viewMode
@viewMode.setter
def viewMode(self, val):
if val == self._viewMode:
return
ports = {"Chunk": self.chunkViewport, "Camera": self.mainViewport}
for p in ports.values():
p.set_parent(None)
port = ports.get(val, self.mainViewport)
self.mainViewport.mouseLookOff()
self._viewMode = val
if val == "Camera":
x, y, z = self.chunkViewport.cameraPosition
try:
h = self.level.heightMapAt(int(x), int(z))
except:
h = 0
y = max(self.mainViewport.cameraPosition[1], h + 2)
self.mainViewport.cameraPosition = x, y, z
# self.mainViewport.yaw = 180.0
# self.mainViewport.pitch = 90.0
self.mainViewport.cameraVector = self.mainViewport._cameraVector()
self.renderer.overheadMode = False
self.viewportButton.text = "Chunk View"
else:
x, y, z = self.mainViewport.cameraPosition
self.chunkViewport.cameraPosition = x, y, z
self.renderer.overheadMode = True
self.viewportButton.text = "Camera View"
self.viewportContainer.add(port)
self.currentViewport = port
self.chunkViewport.size = self.mainViewport.size = self.viewportContainer.size
self.renderer.loadNearbyChunks()
@staticmethod
[docs] def swapViewports():
if config.settings.viewMode.get() == "Chunk":
config.settings.viewMode.set("Camera")
else:
config.settings.viewMode.set("Chunk")
[docs] def addCopiedSchematic(self, sch):
self.copyStack.insert(0, sch)
if len(self.copyStack) > self.maxCopies:
self.deleteCopiedSchematic(self.copyStack[-1])
self.updateCopyPanel()
@staticmethod
def _deleteSchematic(sch):
if hasattr(sch, 'close'):
sch.close()
if sch.filename and os.path.exists(sch.filename):
os.remove(sch.filename)
[docs] def deleteCopiedSchematic(self, sch):
self._deleteSchematic(sch)
self.copyStack = [s for s in self.copyStack if s is not sch]
self.updateCopyPanel()
[docs] def deleteAllCopiedSchematics(self):
for s in self.copyStack:
self._deleteSchematic(s)
copyPanel = None
[docs] def updateCopyPanel(self):
if self.copyPanel:
self.copyPanel.set_parent(None)
self.copyPanel = None
if 0 == len(self.copyStack):
return
self.copyPanel = self.createCopyPanel()
self.copyPanel.right = self.mainViewport.right
self.copyPanel.top = self.subwidgets[0].bottom + 2
self.add(self.copyPanel)
thumbCache = None
fboCache = None
def __getMaxCopies(self):
return config.settings.maxCopies.get() or self.__maxCopies
def __setMaxCopies(self, *args, **kwargs):
return
def __delMaxCopies(self):
return
maxCopies = property(__getMaxCopies, __setMaxCopies, __delMaxCopies, "Copy stack size.")
[docs] def createCopyPanel(self):
panel = GLBackground()
panel.bg_color = (0.0, 0.0, 0.0, 0.5)
panel.pages = []
if len(self.copyStack) > self.maxCopies:
for sch in self.copyStack[self.maxCopies:]:
self.deleteCopiedSchematic(sch)
prevButton = Button("Previous Page")
self.thumbCache = thumbCache = self.thumbCache or {}
self.fboCache = self.fboCache or {}
for k in self.thumbCache.keys():
if k not in self.copyStack:
del self.thumbCache[k]
inner_height = 0
itemNo = Label("#%s" % ("W" * len("%s" % self.maxCopies)), doNotTranslate=True)
fixedwidth = 0 + itemNo.width
del itemNo
def createOneCopyPanel(sch, i):
p = GLBackground()
p.bg_color = (0.0, 0.0, 0.0, 0.4)
itemNo = Label("#%s%s" % (" " * (len("%s" % self.maxCopies) - len("%s" % (i + 1))), (i + 1)),
doNotTranslate=True)
thumb = thumbCache.get(sch)
if thumb is None:
thumb = ThumbView(sch)
thumb.mouse_down = lambda e: self.pasteSchematic(sch)
thumb.tooltipText = "Click to import this item."
thumbCache[sch] = thumb
self.addWorker(thumb.renderer)
deleteButton = Button("Delete", action=lambda: (self.deleteCopiedSchematic(sch)))
saveButton = Button("Save", action=lambda: (self.exportSchematic(sch)))
sizeLabel = Label("{0} x {1} x {2}".format(sch.Length, sch.Width, sch.Height))
r = Row((itemNo, thumb, Column((sizeLabel, Row((deleteButton, saveButton))), spacing=5)))
p.add(r)
itemNo.width = 0 + fixedwidth
p.shrink_wrap()
return p
page = []
for i in range(len(self.copyStack)):
sch = self.copyStack[i]
p = createOneCopyPanel(sch, i)
if self.netherPanel is None:
bottom = pygame.display.get_surface().get_height() - 60
else:
bottom = self.netherPanel.top - 2
if inner_height + p.height + 2 <= bottom - (self.subwidgets[0].bottom + 2) - prevButton.height - (panel.margin * 2):
inner_height += p.height + 2
page.append(p)
else:
inner_height = p.height
panel.pages.append(Column(page, spacing=2, align="l", margin=0))
panel.pages[-1].shrink_wrap()
page = [p]
if page:
panel.pages.append(Column(page, spacing=2, align="l", margin=0))
panel.pages[-1].shrink_wrap()
prevButton.shrink_wrap()
self.currentCopyPage = min(self.currentCopyPage, len(panel.pages) - 1)
col = Column([panel.pages[self.currentCopyPage]], spacing=2, align="l", margin=0)
col.shrink_wrap()
def changeCopyPage(this, delta):
if delta > 0:
m = min
a = self.currentCopyPage + delta, len(this.pages) - 1
elif delta < 0:
m = max
a = self.currentCopyPage - 1, 0
else:
return
self.currentCopyPage = m(*a)
for i in range(len(this.pages)):
page = this.pages[i]
if i == self.currentCopyPage:
page.visible = True
this.subwidgets[0].subwidgets[1].subwidgets[0] = page
page.parent = this.subwidgets[0].subwidgets[1]
else:
page.visible = False
pb = this.subwidgets[0].subwidgets[0].subwidgets[0]
nb = this.subwidgets[0].subwidgets[0].subwidgets[1]
if self.currentCopyPage == 0:
pb.enabled = False
nb.enabled = True
elif 0 < self.currentCopyPage < len(this.pages) - 1:
pb.enabled = True
nb.enabled = True
elif self.currentCopyPage == len(this.pages) - 1:
pb.enabled = True
nb.enabled = False
this.subwidgets[0].subwidgets[1].shrink_wrap()
this.subwidgets[0].shrink_wrap()
this.shrink_wrap()
this.width = 0 + this.orgwidth
nextButton = Button("Next Page", action=lambda: changeCopyPage(panel, 1), width=prevButton.width,
height=prevButton.height)
prevButton.action = lambda: changeCopyPage(panel, -1)
if len(panel.pages) < 2:
prevButton.enabled = False
nextButton.enabled = False
elif self.currentCopyPage == 0:
prevButton.enabled = False
nextButton.enabled = True
elif 0 < self.currentCopyPage < len(panel.pages) - 1:
prevButton.enabled = True
nextButton.enabled = True
elif self.currentCopyPage == len(panel.pages) - 1:
prevButton.enabled = True
nextButton.enabled = False
btns = Row((prevButton, nextButton), spacing=2, align='c')
btns.shrink_wrap()
mainCol = Column((btns, col), spacing=2, align='c')
mainCol.shrink_wrap()
panel.add(mainCol)
panel.shrink_wrap()
panel.anchor = "whrt"
panel.orgwidth = 0 + panel.width
return panel
@mceutils.alertException
def showAnalysis(self, schematic):
self.analyzeBox(schematic, schematic.bounds)
[docs] def analyzeBox(self, level, box):
entityCounts = defaultdict(int)
tileEntityCounts = defaultdict(int)
types = numpy.zeros(65536, dtype='uint32')
def _analyzeBox():
i = 0
for (chunk, slices, point) in level.getChunkSlices(box):
i += 1
yield i, box.chunkCount
blocks = numpy.array(chunk.Blocks[slices], dtype='uint16')
blocks |= (numpy.array(chunk.Data[slices], dtype='uint16') << 12)
b = numpy.bincount(blocks.ravel())
types[:b.shape[0]] = types[:b.shape[0]].astype(int) + b
for ent in chunk.getEntitiesInBox(box):
entID = level.__class__.entityClass.getId(ent["id"].value)
if ent["id"].value == "Item":
try:
v = pymclevel.items.items.findItem(ent["Item"]["id"].value,
ent["Item"]["Damage"].value).name
v += " (Item)"
except pymclevel.items.ItemNotFound:
v = "Unknown Item"
else:
v = ent["id"].value
entityCounts[(entID, v)] += 1
for ent in chunk.getTileEntitiesInBox(box):
tileEntityCounts[ent["id"].value] += 1
with mceutils.setWindowCaption("ANALYZING - "):
showProgress(_("Analyzing {0} blocks...").format(box.volume), _analyzeBox(), cancel=True)
entitySum = numpy.sum(entityCounts.values())
tileEntitySum = numpy.sum(tileEntityCounts.values())
presentTypes = types.nonzero()
blockCounts = sorted([(level.materials[t & 0xfff, t >> 12], types[t]) for t in presentTypes[0]])
blockRows = [("", "", ""), (box.volume, "<%s>"%_("Blocks"), "")]
rows = list(blockRows)
rows.extend([[count, trn(block.name), ("({0}:{1})".format(block.ID, block.blockData))] for block, count in blockCounts])
#rows.sort(key=lambda x: alphanum_key(x[2]), reverse=True)
def extendEntities():
if entitySum:
rows.extend([("", "", ""), (entitySum, "<%s>"%_("Entities"), "")])
rows.extend([(count, trn(id[1]), id[0]) for (id, count) in sorted(entityCounts.iteritems())])
if tileEntitySum:
rows.extend([("", "", ""), (tileEntitySum, "<%s>"%_("TileEntities"), "")])
rows.extend([(count, trn(id), "") for (id, count) in sorted(tileEntityCounts.iteritems())])
extendEntities()
columns = [
TableColumn("Count", 100),
TableColumn("Name", 400),
TableColumn("ID", 120),
]
table = TableView(columns=columns)
table.sortColumn = columns[2]
table.reverseSort = True
def sortColumn(col):
if table.sortColumn == col:
table.reverseSort = not table.reverseSort
else:
table.reverseSort = (col.title == "Count")
colnum = columns.index(col)
def sortKey(x):
val = x[colnum]
if isinstance(val, basestring):
alphanum_key(val)
return val
blockRows.sort(key=sortKey,
reverse=table.reverseSort)
table.sortColumn = col
rows[:] = blockRows
extendEntities()
table.num_rows = lambda: len(rows)
table.row_data = lambda i: rows[i]
table.row_is_selected = lambda x: False
table.click_column_header = sortColumn
tableBacking = Widget()
tableBacking.add(table)
tableBacking.shrink_wrap()
def saveToFile():
dt = datetime.now().strftime("%Y-%m-%d--%H-%M-%S")
filename = askSaveFile(directories.docsFolder,
title='Save analysis...',
defaultName=self.level.displayName + "_analysis_" + dt + ".txt",
filetype='Comma Separated Values\0*.txt\0\0',
suffix="txt",
)
if filename:
try:
csvfile = csv.writer(open(filename, "wb"))
except Exception, e:
alert(str(e))
else:
for row in rows:
_row=[]
if row == ("", "", ""):
_row = ["Number", "Type", "ID"]
else:
for a in row:
if type(a) == unicode:
_row.append(a.encode('utf-8'))
else:
_row.append(a)
csvfile.writerow(_row)
saveButton = Button("Save to file...", action=saveToFile)
col = Column((Label("Analysis"), tableBacking, saveButton))
dlg = Dialog(client=col, responses=["OK"])
def dispatch_key(name, evt):
super(Dialog, dlg).dispatch_key(name, evt)
if not hasattr(evt, 'key'):
return
if name == 'key_down':
keyname = self.root.getKey(evt)
if keyname == 'Up':
table.rows.scroll_up()
elif keyname == 'Down':
table.rows.scroll_down()
elif keyname == 'Page up':
table.rows.scroll_to_item(max(0, table.rows.cell_to_item_no(0, 0) - table.rows.num_rows()))
elif keyname == 'Page down' and table.rows.cell_to_item_no(table.rows.num_rows(), 0) != None:
table.rows.scroll_to_item(min(len(rows), table.rows.cell_to_item_no(table.rows.num_rows(), 0)))
dlg.dispatch_key = dispatch_key
dlg.present()
[docs] def exportSchematic(self, schematic):
filename = mcplatform.askSaveSchematic(
directories.schematicsDir, self.level.displayName, ({"Minecraft Schematics": ["schematic"], "Minecraft Structure NBT": ["nbt"]},[]))
if filename:
if filename.endswith(".schematic"):
schematic.saveToFile(filename)
elif filename.endswith(".nbt"):
StructureNBT.fromSchematic(schematic).save(filename)
[docs] def getLastCopiedSchematic(self):
if len(self.copyStack) == 0:
return None
return self.copyStack[0]
toolbar = None
[docs] def yes(self):
self.yon.dismiss()
self.user_yon_response = True
[docs] def no(self):
self.yon.dismiss()
self.user_yon_response = False
def _resize_selection_box(self, new_box):
import inspect # Let's get our hands dirty
stack = inspect.stack()
filename = os.path.basename(stack[1][1])
old_box = self.selectionTool.selectionBox()
msg = """
Filter "{0}" wants to resize the selection box
Origin: {1} -> {2}
Size: {3} -> {4}
Do you want to resize the Selection Box?""".format(
filename,
(old_box.origin[0], old_box.origin[1], old_box.origin[2]),
(new_box.origin[0], new_box.origin[1], new_box.origin[2]),
(old_box.size[0], old_box.size[1], old_box.size[2]),
(new_box.size[0], new_box.size[1], new_box.size[2])
)
result = ask(msg, ["Yes", "No"])
if result == "Yes":
self.selectionTool.setSelection(new_box)
return new_box
else:
return False
@staticmethod
[docs] def Notify(msg):
ask(msg, ["Close"], cancel=0)
is_gl_container = True
maxDebug = 1
allBlend = False
onscreen = True
mouseEntered = True
defaultCameraToolDistance = 10
mouseSensitivity = 5.0
longDistanceMode = config.settings.longDistanceMode.property()
@staticmethod
[docs] def genSixteenBlockTexture():
has12 = GL.glGetString(GL.GL_VERSION) >= "1.2"
if has12:
maxLevel = 2
mode = GL.GL_LINEAR_MIPMAP_NEAREST
else:
maxLevel = 1
mode = GL.GL_LINEAR
def makeSixteenBlockTex():
darkColor = (0x30, 0x30, 0x30, 0xff)
lightColor = (0x80, 0x80, 0x80, 0xff)
w, h, = 256, 256
teximage = numpy.zeros((w, h, 4), dtype='uint8')
teximage[:] = 0xff
teximage[:, ::16] = lightColor
teximage[::16, :] = lightColor
teximage[:2] = darkColor
teximage[-1:] = darkColor
teximage[:, -1:] = darkColor
teximage[:, :2] = darkColor
# GL.glTexParameter(GL.GL_TEXTURE_2D,
# GL.GL_TEXTURE_MIN_FILTER,
# GL.GL_NEAREST_MIPMAP_NEAREST),
GL.glTexParameter(GL.GL_TEXTURE_2D,
GL.GL_TEXTURE_MAX_LEVEL,
maxLevel - 1)
for lev in range(maxLevel):
step = 1 << lev
if lev:
teximage[::16] = 0xff
teximage[:, ::16] = 0xff
teximage[:2] = darkColor
teximage[-1:] = darkColor
teximage[:, -1:] = darkColor
teximage[:, :2] = darkColor
GL.glTexImage2D(GL.GL_TEXTURE_2D, lev, GL.GL_RGBA8,
w / step, h / step, 0,
GL.GL_RGBA, GL.GL_UNSIGNED_BYTE,
teximage[::step, ::step].ravel())
return Texture(makeSixteenBlockTex, mode)
@staticmethod
[docs] def showProgress(*a, **kw):
return showProgress(*a, **kw)
[docs] def drawConstructionCube(self, box, color, texture=None):
if texture is None:
texture = self.sixteenBlockTex
# textured cube faces
GL.glEnable(GL.GL_BLEND)
GL.glEnable(GL.GL_DEPTH_TEST)
GL.glDepthMask(False)
# edges within terrain
GL.glDepthFunc(GL.GL_GREATER)
try:
GL.glColor(color[0], color[1], color[2], max(color[3], 0.35))
except IndexError:
raise
GL.glLineWidth(1.0)
mceutils.drawCube(box, cubeType=GL.GL_LINE_STRIP)
# edges on or outside terrain
GL.glDepthFunc(GL.GL_LEQUAL)
GL.glColor(color[0], color[1], color[2], max(color[3] * 2, 0.75))
GL.glLineWidth(2.0)
mceutils.drawCube(box, cubeType=GL.GL_LINE_STRIP)
GL.glDepthFunc(GL.GL_LESS)
GL.glColor(color[0], color[1], color[2], color[3])
GL.glDepthFunc(GL.GL_LEQUAL)
mceutils.drawCube(box, texture=texture, selectionBox=True)
GL.glDepthMask(True)
GL.glDisable(GL.GL_BLEND)
GL.glDisable(GL.GL_DEPTH_TEST)
[docs] def loadFile(self, filename, addToRecent=True):
"""
Called when the user picks a level using Load World or Open File.
"""
if self.level and self.unsavedEdits > 0:
resp = ask("Save unsaved edits before loading?", ["Cancel", "Don't Save", "Save"], default=2, cancel=0)
if resp == "Cancel":
return
if resp == "Save":
self.saveFile()
self.root.RemoveEditFiles()
self.freezeStatus(_("Loading ") + filename)
if self.level:
self.selectionTool.endSelection()
self.level.close()
try:
level = pymclevel.fromFile(filename)
except Exception, e:
logging.exception(
'Wasn\'t able to open a file {file => %s}' % filename
)
alert(_(u"I don't know how to open {0}:\n\n{1!r}").format(filename, e))
self.closeEditor()
return
assert level
log.debug("Loaded world is %s"%repr(level))
if addToRecent:
self.mcedit.addRecentWorld(filename)
if len(level.players) >= 50 and config.settings.downloadPlayerSkins.get():
result = ask("MCEdit has detected that there are a large amount of players in this world, would you like to still download skins (This can take a decent amount of time)",
responses=["Download Skins", "Don't Download"])
if result == "Don't Download":
config.settings.downloadPlayerSkins.set(False)
self.revertPlayerSkins = True
try:
self.currentViewport.cameraPosition = level.getPlayerPosition()
y, p = level.getPlayerOrientation()
if p == -90.0:
p += 0.000000001
if p == 90.0:
p -= 0.000000001
self.mainViewport.yaw, self.mainViewport.pitch = y, p
pdim = level.getPlayerDimension()
if pdim and pdim in level.dimensions:
level = level.dimensions[pdim]
except (KeyError, pymclevel.PlayerNotFound): # TagNotFound
# player tag not found, maybe
try:
self.currentViewport.cameraPosition = level.playerSpawnPosition()
except KeyError: # TagNotFound
self.currentViewport.cameraPosition = numpy.array((0, level.Height * 0.75, 0))
self.mainViewport.yaw = -45.
self.mainViewport.pitch = 0.0
self.removeNetherPanel()
gameVersion = level.gameVersion
if gameVersion in ('Schematic'):
log.info('Loading \'%s\' file.'%gameVersion)
else:
log.info('Loading world for version {}.'.format({True: "pior to 1.9 (detection says 'Unknown')", False: gameVersion}[gameVersion == 'Unknown']))
self.loadLevel(level)
self.renderer.position = self.currentViewport.cameraPosition
self.renderer.loadNearbyChunks()
[docs] def loadLevel(self, level, saveChanges=False):
"""
Called to load a level, world, or dimension into the editor and display it in the viewport.
"""
self.level = level
if hasattr(level, 'acquireSessionLock'):
level.acquireSessionLock()
self.toolbar.removeToolPanels()
self.selectedChunks = set()
self.mainViewport.stopMoving()
self.renderer.level = level
self.addWorker(self.renderer)
self.recordUndo = True
if not saveChanges:
self.undoStack = []
self.afterSaveUndoStack = []
self.redoStack = []
self.clearUnsavedEdits()
self.initWindowCaption()
self.selectionTool.selectNone()
[t.levelChanged() for t in self.toolbar.tools]
if "select" not in str(self.currentTool):
self.toolbar.selectTool(0)
if isinstance(self.level, pymclevel.MCInfdevOldLevel):
if self.level.parentWorld:
dimensions = self.level.parentWorld.dimensions
else:
dimensions = self.level.dimensions
dimensionsMenu = [("Overworld", "0")]
dimensionsMenu += [
(pymclevel.MCAlphaDimension.dimensionNames.get(dimNo, "Dimension {0}".format(dimNo)), str(dimNo)) for
dimNo in dimensions]
for dim, name in pymclevel.MCAlphaDimension.dimensionNames.iteritems():
if dim not in dimensions:
dimensionsMenu.append((name, str(dim)))
def presentMenu():
try:
dimNo = int([d[1] for d in dimensionsMenu if d[0] == self.netherButton.selectedChoice][0])
except:
return
self.gotoDimension(dimNo)
self.mainViewport.skyList = None
self.mainViewport.drawSkyBackground()
#!# The two following lines has been moved down.
# self.waypointManager = WaypointManager(os.path.dirname(self.level.filename), self)
# self.waypointManager.load()
dimensionsList = [d[0] for d in dimensionsMenu]
self.netherButton = ChoiceButton(dimensionsList, choose=presentMenu)
self.netherButton.selectedChoice = [d[0] for d in dimensionsMenu if d[1] == str(self.level.dimNo)][0]
self.remove(self.topRow)
# TODO: Marker
self.topRow = Row((self.mcEditButton, Label("View Distance:"),
self.viewDistanceReadout, self.viewButton,
self.viewportButton, self.recordUndoButton, self.netherButton,
Row((self.sessionLockLabel, self.sessionLockLock), spacing=2), self.waypointsButton))
self.add(self.topRow, 0)
else:
self.remove(self.topRow)
self.topRow = Row((
self.mcEditButton, Label("View Distance:"), self.viewDistanceReadout,
self.viewButton, self.viewportButton, self.recordUndoButton))
self.add(self.topRow, 0)
self.level.sessionLockLock = self.sessionLockLock
#!# Adding waypoints handling for all world types
# Need to take care of the dimension.
# If the camera last position was saved, changing dimension is broken; the view is sticked to the overworld.
#!#
if self.prev_dimension == self.new_dimension:
self.waypointManager = WaypointManager(os.path.dirname(self.level.filename), self)
self.waypointManager.load()
if len(list(self.level.allChunks)) == 0:
resp = ask(
"It looks like this level is completely empty!\nYou'll have to create some chunks before you can get started.",
responses=["Create Chunks", "Cancel"])
if resp == "Create Chunks":
x, y, z = self.mainViewport.cameraPosition
box = pymclevel.BoundingBox((x - 128, 0, z - 128), (256, self.level.Height, 256))
self.selectionTool.setSelection(box)
self.toolbar.selectTool(8)
self.toolbar.tools[8].createChunks()
self.mainViewport.cameraPosition = (x, self.level.Height, z)
[docs] def removeNetherPanel(self):
if self.netherPanel:
self.remove(self.netherPanel)
self.netherPanel = None
@mceutils.alertException
def gotoEarth(self):
assert self.level.parentWorld
self.removeNetherPanel()
self.loadLevel(self.level.parentWorld, True)
x, y, z = self.mainViewport.cameraPosition
self.mainViewport.cameraPosition = [x * 8, y, z * 8]
@mceutils.alertException
def gotoNether(self):
self.removeNetherPanel()
x, y, z = self.mainViewport.cameraPosition
self.mainViewport.cameraPosition = [x / 8, y, z / 8]
self.loadLevel(self.level.getDimension(-1), True)
[docs] def gotoDimension(self, dimNo):
if dimNo == self.level.dimNo:
return
else:
# Record the new dimension
self.new_dimension = dimNo
if dimNo == -1 and self.level.dimNo == 0:
self.gotoNether()
elif dimNo == 0 and self.level.dimNo == -1:
self.gotoEarth()
else:
self.removeNetherPanel()
if dimNo:
if dimNo == 1:
self.mainViewport.cameraPosition = (0, 96, 0)
self.loadLevel(self.level.getDimension(dimNo), True)
else:
self.loadLevel(self.level.parentWorld, True)
netherPanel = None
[docs] def initWindowCaption(self):
filename = self.level.filename
# s = os.path.split(filename)
# title = os.path.split(s[0])[1] + os.sep + s[1] + u" - MCEdit ~ " + release.get_version()%_("for")
last_dir, f_name = os.path.split(filename)
last_dir = os.path.basename(last_dir)
title = u"{f_name} - Unified ~ {ver}".format(f_name=os.path.join(last_dir, f_name), ver=release.get_version()%_("for"))
# if DEF_ENC != "UTF-8":
# title = title.encode('utf-8')
title = title.encode('utf-8')
display.set_caption(title)
@mceutils.alertException
def reload(self):
filename = self.level.filename
for p in self.toolbar.tools[6].nonSavedPlayers:
if os.path.exists(p):
os.remove(p)
# self.discardAllChunks()
self.loadFile(filename)
@mceutils.alertException
def saveFile(self):
with mceutils.setWindowCaption("SAVING - "):
if isinstance(self.level, pymclevel.ChunkedLevelMixin): # xxx relight indev levels?
level = self.level
if level.parentWorld:
level = level.parentWorld
if hasattr(level, 'checkSessionLock'):
try:
level.checkSessionLock()
except SessionLockLost, e:
alert(_(e.message) + _("\n\nYour changes cannot be saved."))
return
if hasattr(level, 'dimensions'):
for level in itertools.chain(level.dimensions.itervalues(), [level]):
if "Canceled" == showProgress("Lighting chunks", level.generateLightsIter(), cancel=True):
return
if self.level == level:
if isinstance(level, pymclevel.MCInfdevOldLevel):
needsRefresh = [c.chunkPosition for c in level._loadedChunkData.itervalues() if c.dirty]
needsRefresh.extend(level.unsavedWorkFolder.listChunks())
else:
needsRefresh = [c for c in level.allChunks if level.getChunk(*c).dirty]
#xxx change MCInfdevOldLevel to monitor changes since last call
self.invalidateChunks(needsRefresh)
else:
if "Canceled" == showProgress("Lighting chunks", level.generateLightsIter(), cancel=True):
return
if self.level == level:
if isinstance(level, pymclevel.MCInfdevOldLevel):
needsRefresh = [c.chunkPosition for c in level._loadedChunkData.itervalues() if c.dirty]
needsRefresh.extend(level.unsavedWorkFolder.listChunks())
else:
needsRefresh = [c for c in level.allChunks if level.getChunk(*c).dirty]
#xxx change MCInfdevOldLevel to monitor changes since last call
self.invalidateChunks(needsRefresh)
self.freezeStatus("Saving...")
chunks = self.level.chunkCount
count = [0]
def copyChunks():
for _ in self.level.saveInPlaceGen():
count[0] += 1
yield count[0], chunks
if "Canceled" == showProgress("Copying chunks", copyChunks(), cancel=True):
return
self.recordUndo = True
self.clearUnsavedEdits(True)
self.toolbar.tools[6].nonSavedPlayers = []
@mceutils.alertException
def saveAs(self):
shortName = os.path.split(os.path.split(self.level.filename)[0])[1]
filename = mcplatform.askSaveFile(directories.minecraftSaveFileDir, _("Name the new copy."),
shortName + " - Copy", _('Minecraft World\0*.*\0\0'), "")
# shortName + " - Copy", "", "")
if filename is None:
return
shutil.copytree(self.level.worldFolder.filename, filename)
self.level.worldFolder = AnvilWorldFolder(filename)
self.level.filename = os.path.join(self.level.worldFolder.filename, "level.dat")
if hasattr(self.level, "acquireSessionLock"):
self.level.acquireSessionLock()
self.saveFile()
self.initWindowCaption()
[docs] def addUnsavedEdit(self):
if self.unsavedEdits:
self.remove(self.saveInfoBackground)
self.unsavedEdits += 1
self.saveInfoBackground = GLBackground()
self.saveInfoBackground.bg_color = (0.0, 0.0, 0.0, 0.6)
self.saveInfoLabel = Label(self.saveInfoLabelText)
self.saveInfoLabel.anchor = "blwh"
self.saveInfoBackground.add(self.saveInfoLabel)
self.saveInfoBackground.shrink_wrap()
self.saveInfoBackground.left = 50
self.saveInfoBackground.bottom = self.toolbar.toolbarRectInWindowCoords()[1]
self.add(self.saveInfoBackground)
self.saveInfoBackground = self.saveInfoBackground
[docs] def removeUnsavedEdit(self):
self.unsavedEdits -= 1
self.remove(self.saveInfoBackground)
if self.unsavedEdits:
self.saveInfoBackground = GLBackground()
self.saveInfoBackground.bg_color = (0.0, 0.0, 0.0, 0.6)
self.saveInfoLabel = Label(self.saveInfoLabelText)
self.saveInfoLabel.anchor = "blwh"
self.saveInfoBackground.add(self.saveInfoLabel)
self.saveInfoBackground.shrink_wrap()
self.saveInfoBackground.left = 50
self.saveInfoBackground.bottom = self.toolbar.toolbarRectInWindowCoords()[1]
self.add(self.saveInfoBackground)
self.saveInfoBackground = self.saveInfoBackground
[docs] def clearUnsavedEdits(self, saving=False):
if self.unsavedEdits:
self.unsavedEdits = 0
self.remove(self.saveInfoBackground)
if saving:
for operation in self.undoStack:
self.afterSaveUndoStack.append(operation)
self.undoStack = []
@property
def saveInfoLabelText(self):
if self.unsavedEdits == 0:
return ""
return _("{0} unsaved edits. {1} to save. {2}").format(self.unsavedEdits,
config.keys.save.get(),
"" if self.recordUndo else "(UNDO DISABLED)")
@property
def viewDistanceLabelText(self):
return _("View Distance ({0})").format(self.renderer.viewDistance)
[docs] def createRenderers(self):
self.renderer = MCRenderer()
self.workers = deque()
if self.level:
self.renderer.level = self.level
self.addWorker(self.renderer)
self.renderer.viewDistance = int(config.settings.viewDistance.get())
[docs] def addWorker(self, chunkWorker):
if chunkWorker not in self.workers:
self.workers.appendleft(chunkWorker)
[docs] def removeWorker(self, chunkWorker):
if chunkWorker in self.workers:
self.workers.remove(chunkWorker)
[docs] def getFrameDuration(self):
frameDuration = timedelta(0, 1, 0) / self.renderer.targetFPS
return frameDuration
lastRendererDraw = datetime.now()
[docs] def idleevent(self, e):
if any(self.cameraInputs) or any(self.cameraPanKeys):
self.postMouseMoved()
if (self.renderer.needsImmediateRedraw or
(self.renderer.needsRedraw and datetime.now() - self.lastRendererDraw > timedelta(0, 1, 0) / 3)):
self.invalidate()
self.lastRendererDraw = datetime.now()
if self.renderer.needsImmediateRedraw:
self.invalidate()
if not self.root.bonus_draw_time:
frameDuration = self.getFrameDuration()
while frameDuration > (datetime.now() - self.frameStartTime):
self.doWorkUnit()
else:
return
self.doWorkUnit()
[docs] def activeevent(self, evt):
self.mainViewport.activeevent(evt)
if evt.state & 0x4: # minimized
if evt.gain == 0:
logging.debug("Offscreen")
self.onscreen = False
self.mouseLookOff()
else:
logging.debug("Onscreen")
self.onscreen = True
self.invalidate()
if evt.state & 0x1: # mouse enter/leave
if evt.gain == 0:
logging.debug("Mouse left")
self.mouseEntered = False
self.mouseLookOff()
else:
logging.debug("Mouse entered")
self.mouseEntered = True
[docs] def swapDebugLevels(self):
self.debug += 1
if self.debug > self.maxDebug:
self.debug = 0
if self.debug:
self.showDebugPanel()
else:
self.hideDebugPanel()
[docs] def showDebugPanel(self):
dp = GLBackground()
debugLabel = ValueDisplay(width=1100, ref=AttrRef(self, "debugString"))
inspectLabel = ValueDisplay(width=1100, ref=AttrRef(self, "inspectionString"))
dp.add(Column((debugLabel, inspectLabel)))
dp.shrink_wrap()
dp.bg_color = (0, 0, 0, 0.6)
self.add(dp)
dp.top = 40
self.debugPanel = dp
[docs] def hideDebugPanel(self):
self.remove(self.debugPanel)
@property
def statusText(self):
try:
return self.currentTool.statusText
except Exception, e:
return repr(e)
[docs] def mouse_up(self, evt):
button = keys.remapMouseButton(evt.button)
evt.dict['keyname'] = "mouse{}".format(button)
self.key_up(evt)
[docs] def mouse_drag(self, evt):
# if 'button' not in evt.dict or evt.button != 1:
# return
if self.level:
f = self.blockFaceUnderCursor
if None != f:
(focusPoint, direction) = f
self.currentTool.mouseDrag(evt, focusPoint, direction)
[docs] def mouse_down(self, evt):
button = keys.remapMouseButton(evt.button)
evt.dict['keyname'] = "mouse{}".format(button)
self.mcedit.focus_switch = self
self.turn_off_focus()
self.key_down(evt)
'''
def mouseDragOn(self):
x,y = mouse.get_pos(0)
if None != self.currentOperation:
self.dragInProgress = True
self.dragStartPoint = (x,y)
self.currentOperation.dragStart(x,y)
def mouseDragOff(self):
if self.dragInProgress:
self.dragInProgress = False
'''
[docs] def mouseLookOff(self):
self.mouseWasCaptured = False
self.mainViewport.mouseLookOff()
[docs] def mouseLookOn(self):
self.mainViewport.mouseLookOn()
[docs] def turn_off_focus(self):
self.focus_switch = None
@property
def blockFaceUnderCursor(self):
return self.currentViewport.blockFaceUnderCursor
# @property
# def worldTooltipText(self):
# try:
# if self.blockFaceUnderCursor:
# pos = self.blockFaceUnderCursor[0]
# blockID = self.level.blockAt(*pos)
# blockData = self.level.blockDataAt(*pos)
#
# return "{name} ({bid})\n{pos}".format(name=self.level.materials.names[blockID][blockData], bid=blockID,pos=pos)
#
# except Exception, e:
# return None
#
[docs] def generateStars(self):
starDistance = 999.0
starCount = 2000
r = starDistance
randPoints = (numpy.random.random(size=starCount * 3)) * 2.0 * r
randPoints.shape = (starCount, 3)
nearbyPoints = (randPoints[:, 0] < r) & (randPoints[:, 1] < r) & (randPoints[:, 2] < r)
randPoints[nearbyPoints] += r
randPoints[:starCount / 2, 0] = -randPoints[:starCount / 2, 0]
randPoints[::2, 1] = -randPoints[::2, 1]
randPoints[::4, 2] = -randPoints[::4, 2]
randPoints[1::4, 2] = -randPoints[1::4, 2]
randsizes = numpy.random.random(size=starCount) * 6 + 0.8
vertsPerStar = 4
vertexBuffer = numpy.zeros((starCount, vertsPerStar, 3), dtype='float32')
def normvector(x):
return x / numpy.sqrt(numpy.sum(x * x, 1))[:, numpy.newaxis]
viewVector = normvector(randPoints)
rmod = numpy.random.random(size=starCount * 3) * 2.0 - 1.0
rmod.shape = (starCount, 3)
referenceVector = viewVector + rmod
rightVector = normvector(numpy.cross(referenceVector, viewVector)) * randsizes[:,
numpy.newaxis] # vector perpendicular to viewing line
upVector = normvector(numpy.cross(rightVector, viewVector)) * randsizes[:,
numpy.newaxis] # vector perpendicular previous vector and viewing line
p = randPoints
p1 = p + (- upVector - rightVector)
p2 = p + (upVector - rightVector)
p3 = p + (upVector + rightVector)
p4 = p + (- upVector + rightVector)
vertexBuffer[:, 0, :] = p1
vertexBuffer[:, 1, :] = p2
vertexBuffer[:, 2, :] = p3
vertexBuffer[:, 3, :] = p4
self.starVertices = vertexBuffer.ravel()
starColor = None
[docs] def drawStars(self):
pos = self.mainViewport.cameraPosition
self.mainViewport.cameraPosition = map(lambda x: x / 128.0, pos)
self.mainViewport.setModelview()
GL.glColor(.5, .5, .5, 1.)
GL.glVertexPointer(3, GL.GL_FLOAT, 0, self.starVertices)
GL.glDrawArrays(GL.GL_QUADS, 0, len(self.starVertices) / 3)
self.mainViewport.cameraPosition = pos
self.mainViewport.setModelview()
fractionalReachAdjustment = True
@staticmethod
[docs] def postMouseMoved():
evt = event.Event(MOUSEMOTION, rel=(0, 0), pos=mouse.get_pos(), buttons=mouse.get_pressed())
event.post(evt)
[docs] def resetReach(self):
self.postMouseMoved()
if self.currentTool.resetToolReach():
return
self.cameraToolDistance = self.defaultCameraToolDistance
[docs] def increaseReach(self):
self.postMouseMoved()
if self.currentTool.increaseToolReach():
return
self.cameraToolDistance = self._incrementReach(self.cameraToolDistance)
[docs] def decreaseReach(self):
self.postMouseMoved()
if self.currentTool.decreaseToolReach():
return
self.cameraToolDistance = self._decrementReach(self.cameraToolDistance)
def _incrementReach(self, reach):
reach += 1
if reach > 30 and self.fractionalReachAdjustment:
reach *= 1.05
return reach
def _decrementReach(self, reach):
reach -= 1
if reach > 30 and self.fractionalReachAdjustment:
reach *= 0.95
return reach
[docs] def key_up(self, evt):
self.currentTool.keyUp(evt)
keyname = evt.dict.get('keyname', None) or self.root.getKey(evt)
try:
keyname = self.different_keys[keyname]
except:
pass
if keyname == config.keys.brake.get():
self.mainViewport.brakeOff()
if keyname == config.keys.fastNudge.get():
self.rightClickNudge = False
if keyname == 'F7':
self.testBoardKey = 0
if keyname == config.keys.fastNudge.get():
self.rightClickNudge = False
[docs] def key_down(self, evt):
if not pygame.key.get_focused():
return
keyname = evt.dict.get('keyname', None) or self.root.getKey(evt)
try:
keyname = self.different_keys[keyname]
except:
pass
#!# D.C.-G.
#!# Here we have the part which is responsible for the fallback to the
#!# select tool when pressing 'Escape' key.
#!# It may be interesting to work on this to be able to return to a tool
#!# which have called another.
if keyname == 'Escape':
if self.selectionTool.selectionInProgress:
self.selectionTool.cancel()
elif "fill" in str(self.currentTool) and self.toolbar.tools[3].replacing:
self.toolbar.tools[3].replacing = False
self.toolbar.tools[3].showPanel()
elif "select" not in str(self.currentTool):
self.toolbar.selectTool(0)
else:
self.mouseLookOff()
self.showControls()
# return
#!#
self.currentTool.keyDown(evt)
if keyname == "Alt-F4":
self.quit()
return
if keyname == config.keys.longDistanceMode.get():
self.longDistanceMode = not self.longDistanceMode
if keyname == "Alt-1" or keyname == "Alt-2" or keyname == "Alt-3" or keyname == "Alt-4" or keyname == "Alt-5":
name = "option" + keyname[len(keyname) - 1:]
if hasattr(self.currentTool, name):
getattr(self.currentTool, name)()
if keyname == config.keys.fastNudge.get():
self.rightClickNudge = True
if "clone" in str(self.currentTool):
blocksOnlyModifier = config.keys.blocksOnlyModifier.get()
if keyname.startswith(blocksOnlyModifier):
tempKeyname = keyname[len(blocksOnlyModifier) + 1:]
blocksOnly = True
else:
tempKeyname = keyname
blocksOnly = False
if tempKeyname == config.keys.flip.get():
self.currentTool.flip(blocksOnly=blocksOnly)
if tempKeyname == config.keys.rollClone.get():
self.currentTool.roll(blocksOnly=blocksOnly)
if tempKeyname == config.keys.rotateClone.get():
self.currentTool.rotate(blocksOnly=blocksOnly)
if tempKeyname == config.keys.mirror.get():
self.currentTool.mirror(blocksOnly=blocksOnly)
if "Brush" in str(self.currentTool):
if keyname == config.keys.decreaseBrush.get():
self.currentTool.decreaseBrushSize()
if keyname == config.keys.increaseBrush.get():
self.currentTool.increaseBrushSize()
blocksOnlyModifier = config.keys.blocksOnlyModifier.get()
if keyname.startswith(blocksOnlyModifier):
tempKeyname = keyname[len(blocksOnlyModifier) + 1:]
blocksOnly = True
else:
tempKeyname = keyname
blocksOnly = False
if tempKeyname == config.keys.rotateBrush.get():
self.currentTool.rotate(blocksOnly=blocksOnly)
if tempKeyname == config.keys.rollBrush.get():
self.currentTool.roll(blocksOnly=blocksOnly)
if "fill" in str(self.currentTool) and keyname == config.keys.replaceShortcut.get():
self.currentTool.openReplace()
if keyname == config.keys.quit.get():
self.quit()
return
if keyname == config.keys.viewDistance.get():
self.swapViewDistance()
if keyname == config.keys.selectAll.get():
self.selectAll()
if keyname == config.keys.deselect.get():
self.deselect()
if keyname == config.keys.cut.get():
self.cutSelection()
if keyname == config.keys.copy.get():
self.copySelection()
if keyname == config.keys.paste.get():
self.pasteSelection()
if keyname == config.keys.reloadWorld.get():
self.reload()
if keyname == config.keys.open.get():
self.askOpenFile()
if keyname == config.keys.quickLoad.get():
self.askLoadWorld()
if keyname == config.keys.undo.get():
self.undo()
if keyname == config.keys.redo.get():
self.redo()
if keyname == config.keys.save.get():
self.saveFile()
if keyname == config.keys.newWorld.get():
self.createNewLevel()
if keyname == config.keys.closeWorld.get():
self.closeEditor()
if keyname == config.keys.worldInfo.get():
self.showWorldInfo()
if keyname == config.keys.gotoPanel.get():
self.showGotoPanel()
if keyname == config.keys.saveAs.get():
self.saveAs()
if keyname == config.keys.exportSelection.get():
self.selectionTool.exportSelection()
if keyname == 'Ctrl-Alt-F9':
try:
expr = input_text(">>> ", 600)
expr = compile(expr, 'eval', 'single')
alert("Result: {0!r}".format(eval(expr, globals(), locals())))
except Exception, e:
alert("Exception: {0!r}".format(e))
if keyname == 'Ctrl-Alt-F10':
alert("MCEdit, a Minecraft World Editor\n\nCopyright 2010 David Rio Vierra")
if keyname == config.keys.toggleView.get():
self.swapViewports()
if keyname == config.keys.brake.get():
self.mainViewport.brakeOn()
if keyname == config.keys.resetReach.get():
self.resetReach()
if keyname == config.keys.increaseReach.get():
self.increaseReach()
if keyname == config.keys.decreaseReach.get():
self.decreaseReach()
if keyname == config.keys.swap.get():
self.currentTool.swap()
# #!# D.C.-G.
# #!# Here we have the part which is responsible for the fallback to the
# #!# select tool when pressing 'Escape' key.
# #!# It may be interesting to work on this to be able to return to a tool
# #!# which have called another.
# if keyname == 'Escape':
# if self.selectionTool.selectionInProgress:
# self.selectionTool.cancel()
# elif self.toolbar.tools[3].replacing:
# self.toolbar.tools[3].replacing = False
# self.toolbar.tools[3].showPanel()
# elif "select" not in str(self.currentTool):
# self.toolbar.selectTool(0)
# else:
# self.mouseLookOff()
# self.showControls()
# #!#
if keyname == config.keys.confirmConstruction.get():
self.confirmConstruction()
if keyname == config.keys.debugOverlay.get():
self.swapDebugLevels()
if keyname == config.keys.toggleRenderer.get():
self.renderer.render = not self.renderer.render
if keyname == config.keys.deleteBlocks.get():
self.deleteSelectedBlocks()
if keyname == config.keys.flyMode.get():
config.settings.flyMode.set(not config.settings.flyMode.get())
config.save()
for i, keyName in enumerate(self.toolbarKeys):
if keyname == keyName:
self.toolbar.selectTool(i)
if keyname in ('F1', 'F2', 'F3', 'F4', 'F5'):
self.mcedit.loadRecentWorldNumber(int(keyname[1]))
if keyname == 'F7':
self.testBoardKey = 1
if self.selectionSize():
filter_keys = [i for (i, j) in config.config._sections["Filter Keys"].items() if j == keyname]
if filter_keys:
if not self.toolbar.tools[4].filterModules:
self.toolbar.tools[4].reloadFilters()
filters = [i for i in self.toolbar.tools[4].filterModules if i.lower() == filter_keys[0]]
if filters:
if self.currentTool != 4:
self.toolbar.selectTool(4)
if self.toolbar.tools[4].panel.selectedName != filters[0]:
self.toolbar.tools[4].panel.selectedName = filters[0]
self.toolbar.tools[4].panel.reload()
self.root.fix_sticky_ctrl()
[docs] def showGotoPanel(self):
gotoPanel = Widget()
gotoPanel.X, gotoPanel.Y, gotoPanel.Z = map(int, self.mainViewport.cameraPosition)
inputRow = (
Label("X: "), IntField(ref=AttrRef(gotoPanel, "X")),
Label("Y: "), IntField(ref=AttrRef(gotoPanel, "Y")),
Label("Z: "), IntField(ref=AttrRef(gotoPanel, "Z")),
)
inputRow = Row(inputRow)
column = (
Label("Goto Position:"),
Label("(click anywhere to teleport)"),
inputRow,
# Row( (Button("Cancel"), Button("Goto")), align="r" )
)
column = Column(column)
gotoPanel.add(column)
gotoPanel.shrink_wrap()
d = Dialog(client=gotoPanel, responses=["Goto", "Cancel"])
def click_outside(event):
if event not in d:
x, y, z = self.blockFaceUnderCursor[0]
if y == 0:
y = 64
y += 3
gotoPanel.X, gotoPanel.Y, gotoPanel.Z = x, y, z
if event.num_clicks == 2:
d.dismiss("Goto")
d.mouse_down = click_outside
d.top = self.viewportContainer.top + 10
d.centerx = self.viewportContainer.centerx
if d.present(centered=False) == "Goto":
destPoint = [gotoPanel.X, gotoPanel.Y, gotoPanel.Z]
if self.currentViewport is self.chunkViewport:
self.swapViewports()
self.mainViewport.cameraPosition = destPoint
# TODO: Close marker
[docs] def closeEditor(self):
if self.unsavedEdits:
answer = ask("Save unsaved edits before closing?", ["Cancel", "Don't Save", "Save"], default=-1, cancel=0)
self.root.fix_sticky_ctrl()
if answer == "Save":
self.saveFile()
if answer == "Cancel":
return
for p in self.toolbar.tools[6].nonSavedPlayers:
if os.path.exists(p):
os.remove(p)
if config.settings.savePositionOnClose.get():
self.waypointManager.saveLastPosition(self.mainViewport, self.level.dimNo)
self.waypointManager.save()
self.clearUnsavedEdits()
self.unsavedEdits = 0
self.root.RemoveEditFiles()
self.root.fix_sticky_ctrl()
self.selectionTool.endSelection()
self.mainViewport.mouseLookOff()
if self.level:
self.level.close()
self.level = None
self.renderer.stopWork()
self.removeWorker(self.renderer)
self.renderer.level = None
self.mcedit.removeEditor()
self.controlPanel.dismiss()
display.set_caption(("MCEdit ~ " + release.get_version()%_("for")).encode('utf-8'))
if self.revertPlayerSkins:
config.settings.downloadPlayerSkins.set(True)
self.revertPlayerSkins = False
# TODO: Load marker
[docs] def loadWorldFromFTP(self):
widget = Widget()
ftp_ip_lbl = Label("FTP Server IP:")
ftp_ip_field = TextFieldWrapped(width=400)
ip_row = Row((ftp_ip_lbl, ftp_ip_field))
ftp_user_lbl = Label("FTP Username:")
ftp_user_field = TextFieldWrapped(width=400)
user_row = Row((ftp_user_lbl, ftp_user_field))
ftp_pass_lbl = Label("FTP Password:")
ftp_pass_field = TextFieldWrapped(width=400)
pass_row = Row((ftp_pass_lbl, ftp_pass_field))
note_creds = Label("NOTE: MCEdit-Unified will not use any FTP server info other than to login to the server")
note_wait = Label("Please wait while MCEdit-Unified downloads the world. It will be opened once completed")
col = Column((ip_row, user_row, pass_row, note_creds, note_wait))
widget.add(col)
widget.shrink_wrap()
d = Dialog(widget, ["Connect", "Cancel"])
if d.present() == "Connect":
if ftp_user_field.get_text() == "" and ftp_pass_field.get_text() == "":
self._ftp_client = ftp_client.FTPClient(ftp_ip_field.get_text())
else:
try:
self._ftp_client = ftp_client.FTPClient(ftp_ip_field.get_text(), username=ftp_user_field.get_text(),
password=ftp_pass_field.get_text())
except ftp_client.InvalidCreditdentialsException as e:
alert(e.message)
return
self._ftp_client.safe_download()
self.mcedit.loadFile(os.path.join(self._ftp_client.get_level_path(), 'level.dat'), addToRecent=False)
self.world_from_ftp = True
[docs] def uploadChanges(self):
if self.world_from_ftp:
if self.unsavedEdits:
answer = ask("Save unsaved edits before closing?", ["Cancel", "Don't Save", "Save"], default=-1,
cancel=0)
self.root.fix_sticky_ctrl()
if answer == "Save":
self.saveFile()
if answer == "Cancel":
return
self._ftp_client.upload()
self.clearUnsavedEdits()
self.unsavedEdits = 0
self.root.RemoveEditFiles()
self.root.fix_sticky_ctrl()
self.selectionTool.endSelection()
self.mainViewport.mouseLookOff()
if self.level:
self.level.close()
self.level = None
self.renderer.stopWork()
self.removeWorker(self.renderer)
self.renderer.level = None
self.mcedit.removeEditor()
self.controlPanel.dismiss()
display.set_caption(("MCEdit ~ " + release.get_version()%_("for")).encode('utf-8'))
self._ftp_client.cleanup()
else:
alert("This world was not downloaded from a FTP server. Uploading worlds that were not downloaded from a FTP server is currently not possible")
'''
else:
if self.unsavedEdits:
answer = ask("Save unsaved edits before closing?", ["Cancel", "Don't Save", "Save"], default=-1, cancel=0)
self.root.fix_sticky_ctrl()
if answer == "Save":
self.saveFile()
if answer == "Cancel":
return
widget = Widget()
ftp_ip_lbl = Label("FTP Server IP:")
ftp_ip_field = TextFieldWrapped(width=400)
ip_row = Row((ftp_ip_lbl, ftp_ip_field))
ftp_user_lbl = Label("FTP Username:")
ftp_user_field = TextFieldWrapped(width=400)
user_row = Row((ftp_user_lbl, ftp_user_field))
ftp_pass_lbl = Label("FTP Password:")
ftp_pass_field = TextFieldWrapped(width=400)
pass_row = Row((ftp_pass_lbl, ftp_pass_field))
note_creds = Label("NOTE: MCEdit-Unified will not use any FTP server info other than to login to the server")
note_wait = Label("Please wait while MCEdit-Unified upload the world. The world will be closed once completed")
col = Column((ip_row, user_row, pass_row, note_creds, note_wait))
widget.add(col)
widget.shrink_wrap()
d = Dialog(widget, ["Upload", "Cancel"])
if d.present() == "Upload":
if ftp_user_field.get_text() == "" and ftp_pass_field.get_text() == "":
self._ftp_client = ftp_client.FTPClient(ftp_ip_field.get_text())
else:
try:
self._ftp_client = ftp_client.FTPClient(ftp_ip_field.get_text(), username=ftp_user_field.get_text(), password=ftp_pass_field.get_text())
except ftp_client.InvalidCreditdentialsException as e:
alert(e.message)
return
self._ftp_client.upload_new_world(self.level)
'''
[docs] def repairRegions(self):
worldFolder = self.level.worldFolder
for filename in worldFolder.findRegionFiles():
rf = worldFolder.tryLoadRegionFile(filename)
if rf:
rf.repair()
alert("Repairs complete. See the console window for details.")
@mceutils.alertException
def showWorldInfo(self):
worldInfoPanel = Dialog()
items = []
t = functools.partial(isinstance, self.level)
if t(pymclevel.MCInfdevOldLevel):
if self.level.version == pymclevel.MCInfdevOldLevel.VERSION_ANVIL:
levelFormat = "Minecraft Infinite World (Anvil Format)"
elif self.level.version == pymclevel.MCInfdevOldLevel.VERSION_MCR:
levelFormat = "Minecraft Infinite World (Region Format)"
else:
levelFormat = "Minecraft Infinite World (Old Chunk Format)"
elif t(pymclevel.MCIndevLevel):
levelFormat = "Minecraft Indev (.mclevel format)"
elif t(pymclevel.MCSchematic):
levelFormat = "MCEdit Schematic"
elif t(pymclevel.ZipSchematic):
levelFormat = "MCEdit Schematic (Zipped Format)"
elif t(pymclevel.MCJavaLevel):
levelFormat = "Minecraft Classic or raw block array"
elif t(pymclevel.PocketLeveldbWorld):
levelFormat = "Minecraft Pocket Edition"
else:
levelFormat = "Unknown"
formatLabel = Label(levelFormat)
items.append(Row([Label("Format:"), formatLabel]))
nameField = TextFieldWrapped(width=300, value=self.level.LevelName)
def alt21():
nameField.insertion_point = len(nameField.text)
nameField.insert_char(u'\xa7')
alt21button = Button(u"\xa7", action=alt21)
label = Label("Name:")
items.append(Row((label, nameField, alt21button)))
if hasattr(self.level, 'Time'):
time = self.level.Time
# timezone adjust -
# minecraft time shows 0:00 on day 0 at the first sunrise
# I want that to be 6:00 on day 1, so I add 30 hours
time_editor = TimeEditor(current_tick_time=time)
items.append(time_editor)
if hasattr(self.level, 'RandomSeed'):
seedField = IntField(width=250, value=self.level.RandomSeed)
seedLabel = Label("RandomSeed: ")
items.append(Row((seedLabel, seedField)))
if hasattr(self.level, 'GameType'):
t = self.level.GameType
types = ["Survival", "Creative"]
def gametype(t):
if t < len(types):
return types[t]
return "Unknown"
def action():
if b.gametype < 2:
b.gametype = 1 - b.gametype
b.text = gametype(b.gametype)
b = Button(gametype(t), action=action)
b.gametype = t
gametypeRow = Row((Label("Game Type:"), b))
items.append(gametypeRow)
button = Button("Repair regions", action=self.repairRegions)
items.append(button)
def openFolder():
filename = self.level.filename
if not isdir(filename):
filename = dirname(filename)
mcplatform.platform_open(filename)
revealButton = Button("Open Folder", action=openFolder)
items.append(revealButton)
if isinstance(self.level, pymclevel.MCInfdevOldLevel):
chunkCount = self.level.chunkCount
chunkCountLabel = Label(_("Number of chunks: {0}").format(chunkCount))
items.append(chunkCountLabel)
if hasattr(self.level, 'worldFolder'):
if hasattr(self.level.worldFolder, 'regionFiles'):
worldFolder = self.level.worldFolder
regionCount = len(worldFolder.regionFiles)
regionCountLabel = Label(_("Number of regions: {0}").format(regionCount))
items.append(regionCountLabel)
size = self.level.size
sizelabel = Label("{L}L x {W}W x {H}H".format(L=size[2], H=size[1], W=size[0]))
items.append(sizelabel)
#items.append(TimeEditor(current_tick_time=0))
if hasattr(self.level, "Entities"):
label = Label(_("{0} Entities").format(len(self.level.Entities)))
items.append(label)
if hasattr(self.level, "TileEntities"):
label = Label(_("{0} TileEntities").format(len(self.level.TileEntities)))
items.append(label)
col = Column(items)
self.change = False
def dismiss(*args, **kwargs):
self.change = True
worldInfoPanel.dismiss(self, *args, **kwargs)
def cancel(*args, **kwargs):
Changes = False
if hasattr(self.level, 'Time'):
time = time_editor.get_time_value()
if self.level.Time != time:
Changes = True
if hasattr(self.level, 'DayTime'):
day_time = time_editor.get_daytime_value()
if self.level.DayTime != day_time:
Changes = True
if hasattr(self.level, 'RandomSeed'):
if seedField.value != self.level.RandomSeed:
Changes = True
if hasattr(self.level, 'LevelName'):
if nameField.value != self.level.LevelName:
Changes = True
if hasattr(self.level, 'GameType'):
if b.gametype != self.level.GameType:
Changes = True
if not Changes:
worldInfoPanel.dismiss(self, *args, **kwargs)
return
result = ask("Do you want to keep your changes?", ["Yes", "No", "Cancel"])
if result == "Cancel":
return
if result == "No":
worldInfoPanel.dismiss(self, *args, **kwargs)
return
if result == "Yes":
dismiss(*args, **kwargs)
col = Column((col, Row((Button("OK", action=dismiss), Button("Cancel", action=cancel)))))
worldInfoPanel.add(col)
worldInfoPanel.shrink_wrap()
def dispatchKey(name, evt):
dispatch_key_saved(name, evt)
if name == "key_down":
keyname = self.get_root().getKey(evt)
if keyname == 'Escape':
cancel()
dispatch_key_saved = worldInfoPanel.dispatch_key
worldInfoPanel.dispatch_key = dispatchKey
worldInfoPanel.present()
if not self.change:
return
class WorldInfoChangedOperation(Operation):
def __init__(self, editor, level):
self.editor = editor
self.level = level
self.canUndo = True
self.changeLevelName = changeLevelName
self.changeTime = changeTime
self.changeDayTime = changeDayTime
self.changeSeed = changeSeed
self.changeGameType = changeGameType
def perform(self, recordUndo=True):
if recordUndo:
if changeLevelName:
self.UndoText = self.level.LevelName
self.RedoText = nameField.value
if changeTime:
self.UndoTime = self.level.Time
self.RedoTime = time
if changeDayTime:
self.UndoDayTime = self.level.DayTime
self.RedoDayTime = day_time
if changeSeed:
self.UndoSeed = self.level.RandomSeed
self.RedoSeed = seedField.value
if changeGameType:
self.UndoGameType = self.level.GameType
self.RedoGameType = b.gametype
if changeLevelName:
self.level.LevelName = nameField.value
if changeTime:
self.level.Time = time
if changeDayTime:
self.level.DayTime = day_time
if changeSeed:
self.level.RandomSeed = seedField.value
if changeGameType:
self.level.GameType = b.gametype
def undo(self):
if self.changeLevelName:
self.level.LevelName = self.UndoText
if self.changeTime:
self.level.Time = self.UndoTime
if self.changeDayTime:
self.level.DayTime = self.UndoDayTime
if self.changeSeed:
self.level.RandomSeed = self.UndoSeed
if self.changeGameType:
self.level.GameType = self.UndoGameType
def redo(self):
if self.changeLevelName:
self.level.LevelName = self.RedoText
if self.changeTime:
self.level.Time = self.RedoTime
if self.changeDayTime:
self.level.DayTime = self.RedoDayTime
if self.changeSeed:
self.level.RandomSeed = self.RedoSeed
if self.changeGameType:
self.level.GameType = self.RedoGameType
changeTime = False
changeSeed = False
changeLevelName = False
changeGameType = False
changeDayTime = False
if hasattr(self.level, 'Time'):
time = time_editor.get_time_value()
if self.level.Time != time:
changeTime = True
if hasattr(self.level, 'DayTime'):
day_time = time_editor.get_daytime_value()
if self.level.DayTime != day_time:
changeDayTime = True
if hasattr(self.level, 'RandomSeed'):
if seedField.value != self.level.RandomSeed:
changeSeed = True
if hasattr(self.level, 'LevelName'):
if nameField.value != self.level.LevelName:
changeLevelName = True
if hasattr(self.level, 'GameType'):
if b.gametype != self.level.GameType:
changeGameType = True
if changeTime or changeSeed or changeLevelName or changeGameType:
op = WorldInfoChangedOperation(self, self.level)
self.addOperation(op)
self.addUnsavedEdit()
[docs] def swapViewDistance(self):
if self.renderer.viewDistance >= self.renderer.maxViewDistance:
self.renderer.viewDistance = self.renderer.minViewDistance
else:
self.renderer.viewDistance += 2
self.addWorker(self.renderer)
config.settings.viewDistance.set(self.renderer.viewDistance)
[docs] def changeViewDistance(self, dist):
self.renderer.viewDistance = min(self.renderer.maxViewDistance, dist)
self.addWorker(self.renderer)
config.settings.viewDistance.set(self.renderer.viewDistance)
@mceutils.alertException
def askLoadWorld(self):
if not os.path.isdir(directories.minecraftSaveFileDir):
alert(_(u"Could not find the Minecraft saves directory!\n\n({0} was not found or is not a directory)").format(
directories.minecraftSaveFileDir))
return
worldPanel = Widget()
potentialWorlds = os.listdir(directories.minecraftSaveFileDir)
potentialWorlds = [os.path.join(directories.minecraftSaveFileDir, p) for p in potentialWorlds]
worldFiles = [p for p in potentialWorlds if pymclevel.MCInfdevOldLevel.isLevel(p)]
worlds = []
for f in worldFiles:
try:
lev = pymclevel.MCInfdevOldLevel(f, readonly=True)
except Exception:
continue
else:
worlds.append(lev)
if len(worlds) == 0:
alert("No worlds found! You should probably play Minecraft to create your first world.")
return
def loadWorld():
self.mcedit.loadFile(self.worldData[worldTable.selectedWorldIndex][2].filename)
self.root.fix_sticky_ctrl()
def click_row(i, evt):
worldTable.selectedWorldIndex = i
if evt.num_clicks == 2:
loadWorld()
dialog.dismiss("Cancel")
def dispatch_key(name, evt):
if name != "key_down":
return
keyname = self.root.getKey(evt)
if keyname == "Escape":
dialog.dismiss("Cancel")
elif keyname == "Up":
worldTable.selectedWorldIndex = max(0, worldTable.selectedWorldIndex - 1)
worldTable.rows.scroll_to_item(worldTable.selectedWorldIndex)
elif keyname == "Down":
worldTable.selectedWorldIndex = min(len(worlds) - 1, worldTable.selectedWorldIndex + 1)
worldTable.rows.scroll_to_item(worldTable.selectedWorldIndex)
elif keyname == 'Page up':
worldTable.selectedWorldIndex = max(0, worldTable.selectedWorldIndex - worldTable.rows.num_rows())
worldTable.rows.scroll_to_item(worldTable.selectedWorldIndex)
elif keyname == 'Page down':
worldTable.selectedWorldIndex = min(len(worlds) - 1, worldTable.selectedWorldIndex + worldTable.rows.num_rows())
worldTable.rows.scroll_to_item(worldTable.selectedWorldIndex)
elif keyname == "Return":
loadWorld()
dialog.dismiss("Cancel")
else:
old_dispatch_key(name, evt)
text = fld.text.lower()
worldsToUse = []
splitText = text.split(" ")
amount = len(splitText)
for v in allWorlds:
nameParts = nameFormat(v).lower().split(" ")
i = 0
spiltTextUsed = []
for v2 in nameParts:
Start = True
j = 0
while j < len(splitText) and Start:
if splitText[j] in v2 and j not in spiltTextUsed:
i += 1
spiltTextUsed.append(j)
Start = False
j += 1
if i == amount:
worldsToUse.append(v)
self.worldData = [[dateFormat(d), nameFormat(w), w, d]
for w, d in ((w, dateobj(w.LastPlayed)) for w in worldsToUse)]
self.worldData.sort(key=lambda (a, b, w, d): d, reverse=True)
worldTable.selectedWorldIndex = 0
worldTable.num_rows = lambda: len(self.worldData)
worldTable.row_data = lambda i: self.worldData[i]
worldTable.rows.scroll_to_item(0)
def key_up(evt):
pass
allWorlds = worlds
lbl = Label("Search")
fld = TextFieldWrapped(300)
old_dispatch_key = fld.dispatch_key
fld.dispatch_key = dispatch_key
row = Row((lbl, fld))
worldTable = TableView(columns=[
TableColumn("Last Played", 200, "l"),
TableColumn("Level Name (filename)", 500, "l"),
])
def dateobj(lp):
try:
return datetime.utcfromtimestamp(lp / 1000.0)
except:
return datetime.utcfromtimestamp(0.0)
def dateFormat(lp):
try:
return lp.strftime("%x %X").decode('utf-8')
except:
return u"{0} seconds since the epoch.".format(lp)
def nameFormat(w):
try:
if w.LevelName == w.displayName.decode("utf-8"):
return w.LevelName
return u"{0} ({1})".format(w.LevelName, w.displayName.decode("utf-8"))
except:
try:
return w.LevelName
except:
try:
return w.displayName
except:
return "[UNABLE TO READ]"
self.worldData = [[dateFormat(d), nameFormat(w), w, d]
for w, d in ((w, dateobj(w.LastPlayed)) for w in worlds)]
self.worldData.sort(key=lambda (a, b, w, d): d, reverse=True)
# worlds = [w[2] for w in self.worldData]
worldTable.selectedWorldIndex = 0
worldTable.num_rows = lambda: len(self.worldData)
worldTable.row_data = lambda i: self.worldData[i]
worldTable.row_is_selected = lambda x: x == worldTable.selectedWorldIndex
worldTable.click_row = click_row
worldTable.top = row.bottom
worldPanel.add(row)
worldPanel.add(worldTable)
worldPanel.shrink_wrap()
dialog = Dialog(worldPanel, ["Load", "From FTP Server", "Cancel"])
dialog.key_up = key_up
result = dialog.present()
if result == "Load":
loadWorld()
if result == "From FTP Server":
self.loadWorldFromFTP()
[docs] def askOpenFile(self):
self.mouseLookOff()
try:
filename = mcplatform.askOpenFile(schematics=True)
if filename:
self.parent.loadFile(filename)
except Exception:
logging.exception('Error while asking user for filename')
return
[docs] def createNewLevel(self):
self.mouseLookOff()
newWorldPanel = Widget()
newWorldPanel.w = newWorldPanel.h = 16
newWorldPanel.x = newWorldPanel.z = newWorldPanel.f = 0
newWorldPanel.y = 64
newWorldPanel.seed = 0
label = Label("Creating a new world.")
generatorPanel = GeneratorPanel()
xinput = IntInputRow("X: ", ref=AttrRef(newWorldPanel, "x"))
yinput = IntInputRow("Y: ", ref=AttrRef(newWorldPanel, "y"))
zinput = IntInputRow("Z: ", ref=AttrRef(newWorldPanel, "z"))
finput = IntInputRow("f: ", ref=AttrRef(newWorldPanel, "f"), min=0, max=3)
xyzrow = Row([xinput, yinput, zinput, finput])
seedinput = IntInputRow("Seed: ", width=250, ref=AttrRef(newWorldPanel, "seed"))
winput = IntInputRow("East-West Chunks: ", ref=AttrRef(newWorldPanel, "w"), min=0)
hinput = IntInputRow("North-South Chunks: ", ref=AttrRef(newWorldPanel, "h"), min=0)
# grassinputrow = Row( (Label("Grass: ")
# from editortools import BlockButton
# blockInput = BlockButton(pymclevel.alphaMaterials, pymclevel.alphaMaterials.Grass)
# blockInputRow = Row( (Label("Surface: "), blockInput) )
gametypes = ["Survival", "Creative"]
worldtypes = {"Default": ("DEFAULT", "default"), "Superflat": ("FLAT", "flat"),
"Large Biomes": ("LARGEBIOMES", "largeBiomes"), "Amplified": ("AMPLIFIED", "amplified")}
def gametype(t):
if t < len(gametypes):
return gametypes[t]
return "Unknown"
def gametypeAction():
if gametypeButton.gametype < 2:
gametypeButton.gametype = 1 - gametypeButton.gametype
gametypeButton.text = gametype(gametypeButton.gametype)
gametypeButton = Button(gametype(0), action=gametypeAction)
gametypeButton.gametype = 0
gametypeRow = Row((Label("Game Type:"), gametypeButton))
worldtypeButton = ChoiceButton(worldtypes.keys())
worldtypeRow = Row((Label("World Type:"), worldtypeButton))
newWorldPanel.add(
Column((label, Row([winput, hinput]), xyzrow, seedinput, gametypeRow, worldtypeRow, generatorPanel),
align="l"))
newWorldPanel.shrink_wrap()
result = Dialog(client=newWorldPanel, responses=["Create", "Cancel"]).present()
if result == "Cancel":
return
filename = mcplatform.askCreateWorld(directories.minecraftSaveFileDir)
if not filename:
return
w = newWorldPanel.w
h = newWorldPanel.h
x = newWorldPanel.x
y = newWorldPanel.y
z = newWorldPanel.z
f = newWorldPanel.f
seed = newWorldPanel.seed or None
generationtype = worldtypes[worldtypeButton.get_value()][0]
self.freezeStatus("Creating world...")
try:
newlevel = pymclevel.MCInfdevOldLevel(filename=filename, create=True, random_seed=seed)
# chunks = list(itertools.product(xrange(w / 2 - w + cx, w / 2 + cx), xrange(h / 2 - h + cz, h / 2 + cz)))
if generatorPanel.generatorChoice.selectedChoice == "Flatland":
y = generatorPanel.chunkHeight
newlevel.setPlayerPosition((x + 0.5, y + 2.8, z + 0.5))
newlevel.setPlayerOrientation((f * 90.0, 0.0))
newlevel.setPlayerSpawnPosition((x, y + 1, z))
newlevel.GameType = gametypeButton.gametype
newlevel.GeneratorName = worldtypes[worldtypeButton.get_value()][1]
newlevel.saveInPlace()
worker = generatorPanel.generate(newlevel, pymclevel.BoundingBox((x - w * 8, 0, z - h * 8),
(w * 16, newlevel.Height, h * 16)),
useWorldType=generationtype)
if "Canceled" == showProgress("Generating chunks...", worker, cancel=True):
raise RuntimeError("Canceled.")
if y < 64:
y = 64
newlevel.setBlockAt(x, y, z, pymclevel.alphaMaterials.Sponge.ID)
if newlevel.parentWorld:
newlevel = newlevel.parentWorld
newlevel.acquireSessionLock()
newlevel.saveInPlace()
self.loadFile(filename)
except Exception:
logging.exception(
'Error while creating world. {world => %s}' % filename
)
return
return newlevel
[docs] def confirmConstruction(self):
self.currentTool.confirm()
[docs] def selectionToChunks(self, remove=False, add=False):
box = self.selectionBox()
if box:
if box == self.level.bounds:
self.selectedChunks = set(self.level.allChunks)
return
selectedChunks = self.selectedChunks
boxedChunks = set(box.chunkPositions)
if boxedChunks.issubset(selectedChunks):
remove = True
if remove and not add:
selectedChunks.difference_update(boxedChunks)
else:
selectedChunks.update(boxedChunks)
self.selectionTool.selectNone()
[docs] def chunksToSelection(self):
if len(self.selectedChunks) == 0:
return
starting_chunk = self.selectedChunks.pop()
box = self.selectionTool.selectionBoxForCorners((starting_chunk[0] << 4, 0, starting_chunk[1] << 4), ((starting_chunk[0] << 4) + 15, 256, (starting_chunk[1] << 4) + 15))
for c in self.selectedChunks:
box = box.union(self.selectionTool.selectionBoxForCorners((c[0] << 4, 0, c[1] << 4), ((c[0] << 4) + 15, 256, (c[1] << 4) + 15)))
self.selectedChunks = set([])
self.selectionTool.selectNone()
self.selectionTool.setSelection(box)
[docs] def selectAll(self):
if self.currentViewport is self.chunkViewport:
self.selectedChunks = set(self.level.allChunks)
else:
self.selectionTool.selectAll()
[docs] def deselect(self):
self.selectionTool.deselect()
self.selectedChunks.clear()
[docs] def endSelection(self):
self.selectionTool.endSelection()
[docs] def cutSelection(self):
self.selectionTool.cutSelection()
[docs] def copySelection(self):
self.selectionTool.copySelection()
[docs] def pasteSelection(self):
schematic = self.getLastCopiedSchematic()
self.pasteSchematic(schematic)
[docs] def pasteSchematic(self, schematic):
if schematic is None:
return
self.currentTool.cancel()
self.currentTool = self.toolbar.tools[5]
self.currentTool.loadLevel(schematic)
[docs] def deleteSelectedBlocks(self):
self.selectionTool.deleteBlocks()
@mceutils.alertException
def undo(self):
if len(self.undoStack) == 0 and len(self.afterSaveUndoStack) == 0:
return
with mceutils.setWindowCaption("UNDOING - "):
self.freezeStatus("Undoing the previous operation...")
wasSelectionBox = False
if self.selectionBox():
wasSelectionBox = True
if len(self.undoStack) > 0:
op = self.undoStack.pop()
normalUndo = True
else:
op = self.afterSaveUndoStack.pop()
normalUndo = False
if self.recordUndo:
self.redoStack.append(op)
if len(self.redoStack) > self.undoLimit:
self.redoStack.pop(0)
op.undo()
changedBox = op.dirtyBox()
if changedBox is not None:
self.invalidateBox(changedBox)
if not self.selectionBox() and wasSelectionBox:
self.toolbar.selectTool(0)
self.toolbar.tools[0].currentCorner = 1
if ".SelectionOperation" not in str(op) and ".NudgeSelectionOperation" not in str(op):
if normalUndo:
self.removeUnsavedEdit()
else:
self.addUnsavedEdit()
self.root.fix_sticky_ctrl()
[docs] def redo(self):
if len(self.redoStack) == 0:
return
with mceutils.setWindowCaption("REDOING - "):
self.freezeStatus("Redoing the previous operation...")
op = self.redoStack.pop()
if self.recordUndo:
self.undoStack.append(op)
if len(self.undoStack) > self.undoLimit:
self.undoStack.pop(0)
op.redo()
changedBox = op.dirtyBox()
if changedBox is not None:
self.invalidateBox(changedBox)
if op.changedLevel:
self.addUnsavedEdit()
self.root.fix_sticky_ctrl()
[docs] def invalidateBox(self, box):
self.renderer.invalidateChunksInBox(box)
[docs] def invalidateChunks(self, c):
self.renderer.invalidateChunks(c)
[docs] def invalidateAllChunks(self):
self.renderer.invalidateAllChunks()
[docs] def discardAllChunks(self):
self.renderer.discardAllChunks()
[docs] def addDebugString(self, string):
if self.debug:
self.debugString += string
averageFPS = 0.0
averageCPS = 0.0
shouldLoadAndRender = True
[docs] def gl_draw(self):
self.debugString = ""
self.inspectionString = ""
if not self.level:
return
if not self.shouldLoadAndRender:
return
self.renderer.loadVisibleChunks()
self.addWorker(self.renderer)
if self.currentTool.previewRenderer:
self.currentTool.previewRenderer.loadVisibleChunks()
self.addWorker(self.currentTool.previewRenderer)
self.frames += 1
frameDuration = self.getFrameDuration()
while frameDuration > (
datetime.now() - self.frameStartTime): # if it's less than 0ms until the next frame, go draw. otherwise, go work.
self.doWorkUnit()
frameStartTime = datetime.now()
timeDelta = frameStartTime - self.frameStartTime
# self.addDebugString("FrameStart: {0} CameraTick: {1}".format(frameStartTime, self.mainViewport.lastTick))
# self.addDebugString("D: %d, " % () )
self.currentFrameDelta = timeDelta
self.frameSamples.pop(0)
self.frameSamples.append(timeDelta)
frameTotal = numpy.sum(self.frameSamples)
self.averageFPS = 1000000. / (
(frameTotal.microseconds + 1000000 * frameTotal.seconds) / float(len(self.frameSamples)) + 0.00001)
r = self.renderer
chunkTotal = numpy.sum(r.chunkSamples)
cps = 1000000. / (
(chunkTotal.microseconds + 1000000 * chunkTotal.seconds) / float(len(r.chunkSamples)) + 0.00001)
self.averageCPS = cps
self.oldFrameStartTime = self.frameStartTime
self.frameStartTime = frameStartTime
if self.debug > 0:
self.debugString = _("FPS: %0.1f/%0.1f, CPS: %0.1f, VD: %d, W: %d, WF: %d, MBv: %0.1f, ") % (
1000000. / (float(timeDelta.microseconds) + 0.000001),
self.averageFPS,
cps,
self.renderer.viewDistance,
len(self.workers),
self.renderer.workFactor,
self.renderer.bufferUsage / 1000000.)
self.debugString += _("DL: {dl} ({dlcount}), Tx: {t}, gc: {g}, ").format(
dl=len(glutils.DisplayList.allLists), dlcount=glutils.gl.listCount,
t=len(glutils.Texture.allTextures), g=len(gc.garbage))
if self.renderer:
self.renderer.addDebugInfo(self.addDebugString)
[docs] def doWorkUnit(self, onMenu=False):
if len(self.workers):
try:
w = self.workers.popleft()
w.next()
self.workers.append(w)
except StopIteration:
if hasattr(w, "needsRedraw") and w.needsRedraw:
self.invalidate()
time.sleep(0.001)
[docs] def updateInspectionString(self, blockPosition):
self.inspectionString += str(blockPosition) + ": "
x, y, z = blockPosition
cx, cz = x // 16, z // 16
try:
if self.debug:
if isinstance(self.level, pymclevel.MCIndevLevel):
bl = self.level.blockLightAt(*blockPosition)
blockID = self.level.blockAt(*blockPosition)
bdata = self.level.blockDataAt(*blockPosition)
self.inspectionString += _("ID: %d:%d (%s), ") % (
blockID, bdata, self.level.materials.names[blockID][bdata])
self.inspectionString += _("Data: %d, Light: %d, ") % (bdata, bl)
elif isinstance(self.level, pymclevel.ChunkedLevelMixin):
sl = self.level.skylightAt(*blockPosition)
bl = self.level.blockLightAt(*blockPosition)
bdata = self.level.blockDataAt(*blockPosition)
blockID = self.level.blockAt(*blockPosition)
self.inspectionString += _("ID: %d:%d (%s), ") % (
blockID, bdata, self.level.materials.names[blockID][bdata])
try:
path = self.level.getChunk(cx, cz).filename
except:
path = "chunks.dat"
self.inspectionString += _("Data: %d, L: %d, SL: %d") % (
bdata, bl, sl)
try:
hm = self.level.heightMapAt(x, z)
self.inspectionString += _(", H: %d") % hm
except:
pass
try:
tp = self.level.getChunk(cx, cz).TerrainPopulated
self.inspectionString += _(", TP: %d") % tp
except:
pass
self.inspectionString += _(", D: %d") % self.level.getChunk(cx, cz).dirty
self.inspectionString += _(", NL: %d") % self.level.getChunk(cx, cz).needsLighting
try:
biome = self.level.getChunk(cx, cz).Biomes[x & 15, z & 15]
from pymclevel import biome_types
self.inspectionString += _(", Bio: %s") % biome_types.biome_types[biome]
except AttributeError:
pass
if isinstance(self.level, pymclevel.pocket.PocketWorld):
ch = self.level.getChunk(cx, cz)
self.inspectionString += _(", DC: %s") % ch.DirtyColumns[z & 15, x & 15]
self.inspectionString += _(", Ch(%d, %d): %s") % (cx, cz, path)
else: # classic
blockID = self.level.blockAt(*blockPosition)
self.inspectionString += _("ID: %d (%s), ") % (
blockID, self.level.materials.names[blockID][0])
except Exception, e:
self.inspectionString += _("Chunk {0} had an error: {1!r}").format(
(int(numpy.floor(blockPosition[0])) >> 4, int(numpy.floor(blockPosition[2])) >> 4), e)
pass
[docs] def drawWireCubeReticle(self, color=(1.0, 1.0, 1.0, 1.0), position=None):
GL.glPolygonOffset(DepthOffset.TerrainWire, DepthOffset.TerrainWire)
GL.glEnable(GL.GL_POLYGON_OFFSET_FILL)
blockPosition, faceDirection = self.blockFaceUnderCursor
blockPosition = position or blockPosition
mceutils.drawTerrainCuttingWire(pymclevel.BoundingBox(blockPosition, (1, 1, 1)), c1=color)
GL.glDisable(GL.GL_POLYGON_OFFSET_FILL)
@staticmethod
[docs] def drawString(x, y, color, string):
return
@staticmethod
[docs] def freezeStatus(string):
return
# GL.glColor(1.0, 0., 0., 1.0)
#
# # glDrawBuffer(GL.GL_FRONT)
# GL.glMatrixMode(GL.GL_PROJECTION)
# GL.glPushMatrix()
# glRasterPos(50, 100)
# for i in string:
# glutBitmapCharacter(GLUT_BITMAP_HELVETICA_18, ord(i))
#
# # glDrawBuffer(GL.GL_BACK)
# GL.glMatrixMode(GL.GL_PROJECTION)
# GL.glPopMatrix()
# glFlush()
# display.flip()
# # while(True): pass
[docs] def selectionSize(self):
return self.selectionTool.selectionSize()
[docs] def selectionBox(self):
return self.selectionTool.selectionBox()
[docs] def selectionChanged(self):
if not self.currentTool.toolEnabled():
self.toolbar.selectTool(0)
self.currentTool.selectionChanged()
[docs] def addOperation(self, op):
self.performWithRetry(op)
if self.recordUndo and op.canUndo:
self.undoStack.append(op)
if len(self.undoStack) > self.undoLimit:
self.undoStack.pop(0)
recordUndo = True
[docs] def quit(self):
if config.settings.savePositionOnClose.get():
self.waypointManager.saveLastPosition(self.mainViewport, self.level.getPlayerDimension())
self.waypointManager.save()
self.mouseLookOff()
self.mcedit.confirm_quit()
mouseWasCaptured = False
[docs] def showControls(self):
#.#
self.controlPanel.top = self.subwidgets[0].bottom
self.controlPanel.left = (self.width - self.controlPanel.width) / 2
#.#
self.controlPanel.present(False)
infoPanel = None
[docs] def showChunkRendererInfo(self):
if self.infoPanel:
self.infoPanel.set_parent(None)
return
self.infoPanel = infoPanel = Widget(bg_color=(0, 0, 0, 80))
infoPanel.add(Label(""))
def idleHandler(evt):
x, y, z = self.blockFaceUnderCursor[0]
cx, cz = x // 16, z // 16
cr = self.renderer.chunkRenderers.get((cx, cz))
if None is cr:
return
crNames = [_("%s - %0.1fkb") % (type(br).__name__, br.bufferSize() / 1000.0) for br in cr.blockRenderers]
infoLabel = Label("\n".join(crNames))
infoPanel.remove(infoPanel.subwidgets[0])
infoPanel.add(infoLabel)
infoPanel.shrink_wrap()
self.invalidate()
infoPanel.idleevent = idleHandler
infoPanel.topleft = self.viewportContainer.topleft
self.add(infoPanel)
infoPanel.click_outside_response = -1
# infoPanel.present()
## def testGLSL(self):
## print "Hello"
## level = MCLevel.fromFile("mrchunk.schematic")
## blocks = level.Blocks
## blockCount = level.Width * level.Length * level.Height,
## fbo = glGenFramebuffersEXT(1)
## glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo)
##
## print blockCount, fbo
##
## destBlocks = numpy.zeros(blockCount, 'uint8')
## (sourceTex, destTex) = glGenTextures(2)
##
## glBindTexture(GL_TEXTURE_3D, sourceTex)
## glTexImage3D(GL_TEXTURE_3D, 0, 1,
## level.Width, level.Length, level.Height,
## 0, GL_RED, GL.GL_UNSIGNED_BYTE,
## blocks)
##
## # return
##
## glBindTexture(GL.GL_TEXTURE_2D, destTex)
## glTexImage2D(GL.GL_TEXTURE_2D, 0, 1,
## level.Width, level.Length,
## 0, GL_RED, GL.GL_UNSIGNED_BYTE, destBlocks)
## glTexParameter(GL.GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
## glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL.GL_TEXTURE_2D, destTex, 0)
##
## vertShader = glCreateShader(GL_VERTEX_SHADER)
##
## vertShaderSource = """
## void main()
## {
## gl_Position = gl_Vertex
## }
## """
##
## glShaderSource(vertShader, vertShaderSource);
## glCompileShader(vertShader);
##
## fragShader = glCreateShader(GL_FRAGMENT_SHADER)
##
## fragShaderSource = """
## void main()
## {
## gl_FragColor = vec4(1.0, 0.0, 1.0, 0.75);
## }
## """
##
## glShaderSource(fragShader, fragShaderSource);
## glCompileShader(fragShader);
##
##
##
## prog = glCreateProgram()
##
## glAttachShader(prog, vertShader)
## glAttachShader(prog, fragShader)
## glLinkProgram(prog)
##
## glUseProgram(prog);
## # return
## GL.glDisable(GL.GL_DEPTH_TEST);
## GL.glVertexPointer(2, GL.GL_FLOAT, 0, [0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0]);
## GL.glDrawArrays(GL.GL_QUADS, 0, 4);
## GL.glEnable(GL.GL_DEPTH_TEST);
##
## glFlush();
## destBlocks = glGetTexImage(GL.GL_TEXTURE_2D, 0, GL_RED, GL.GL_UNSIGNED_BYTE);
## print destBlocks, destBlocks[0:8];
## raise SystemExit;
[docs] def handleMemoryError(self):
if self.renderer.viewDistance <= 2:
raise MemoryError("Out of memory. Please restart MCEdit.")
if hasattr(self.level, 'compressAllChunks'):
self.level.compressAllChunks()
self.toolbar.selectTool(0)
self.renderer.viewDistance -= 4
self.renderer.discardAllChunks()
logging.warning(
'Out of memory, decreasing view distance. {view => %s}' % (
self.renderer.viewDistance
)
)
config.settings.viewDistance.set(self.renderer.viewDistance)
config.save()
[docs] def lockLost(self):
image_path = directories.getDataDir(os.path.join("toolicons", "session_bad.png"))
self.sessionLockLock.set_image(get_image(image_path, prefix=""))
self.sessionLockLock.tooltipText = "Session Lock is being used by Minecraft"
self.sessionLockLabel.tooltipText = "Session Lock is being used by Minecraft"
self.waypointManager.saveLastPosition(self.mainViewport, self.level.getPlayerDimension())
[docs] def lockAcquired(self):
image_path = directories.getDataDir(os.path.join("toolicons", "session_good.png"))
self.sessionLockLock.set_image(get_image(image_path, prefix=""))
self.sessionLockLock.tooltipText = "Session Lock is being used by MCEdit"
self.sessionLockLabel.tooltipText = "Session Lock is being used by MCEdit"
self.root.sessionStolen = False
from albow.resource import get_image
# class LogFilter(logging.Filter):
# def __init__(self, editor):
# super(LogFilter, self).__init__()
# self.level = logging.WARNING
# self.editor = editor
#
# def filter(self, record):
# message = record.getMessage()
# if "Session lock lost. This world is being accessed from another location." in message:
# image_path = directories.getDataDir(os.path.join("toolicons", "session_bad.png"))
# self.editor.sessionLockLock.set_image(get_image(image_path, prefix=""))
# self.editor.sessionLockLock.tooltipText = "Session Lock is being used by Minecraft"
# self.editor.sessionLockLabel.tooltipText = "Session Lock is being used by Minecraft"
# if "Re-acquired session lock" in message: