Skip to content

Instantly share code, notes, and snippets.

@impiaaa
Created March 29, 2014 04:00
Show Gist options
  • Save impiaaa/9848097 to your computer and use it in GitHub Desktop.
Save impiaaa/9848097 to your computer and use it in GitHub Desktop.
import pymclevel, sys, numpy
MCEDIT = False
if sys.version_info[0] < 3:
input = raw_input
def info(format, *args, **kwargs):
sys.stdout.write(format.format(*args, **kwargs)+"\n")
def warning(format, *args, **kwargs):
sys.stderr.write(format.format(*args, **kwargs)+"\n")
def error(format, *args, **kwargs):
s = format.format(*args, **kwargs)
if MCEDIT:
raise Exception(s)
else:
sys.stderr.write(s+"\n")
sys.exit(1)
def findConflicting(itemdata):
for item1 in itemdata:
key1, id1 = item1['K'].value, item1['V'].value
name1 = key1[1:]
for item2 in itemdata:
key2, id2 = item2['K'].value, item2['V'].value
name2 = key2[1:]
if id1 == id2 and name1 != name2 and not name1.startswith("minecraft"):
info("{0} and {1} are conflicting for {2}", name1, name2, id1)
yield item1
def getCorrespondingItem(block, itemdata):
blockName = block['K'].value[1:]
# See GameData.java:243
isBlock = ord(block['K'].value[0]) == 1
assert isBlock
for item in itemdata:
if ord(item['K'].value[0]) == 1: continue
itemName = item['K'].value[1:]
if blockName == itemName:
return item
localUsedIds = []
def getFreeId(itemdata):
for i in range(4096):
if i in localUsedIds: continue
available = True
for item in itemdata:
if item['V'].value == i:
localUsedIds.append(i)
available = False
continue
if available:
localUsedIds.append(i)
return i
def findNewIds(conflicting, itemdata):
for block in conflicting:
item = getCorrespondingItem(block, itemdata)
newId = getFreeId(itemdata)
if not newId:
error("Couldn't find any unused IDs!")
info("Moving block and item {0} to {1}", block['K'].value[1:], newId)
yield block, newId
yield item, newId
def moveIds(newIds, level):
for item, newId in newIds:
oldId = item['V'].value
item['V'].value = newId
if ord(item['K'].value[0]) == 1:
for chunkXPos, chunkZPos in level.allChunks:
chunk = level.getChunk(chunkXPos, chunkZPos)
slice = chunk.Blocks==oldId
count = len(numpy.where(slice.flat)[0])
if count > 0:
info("Replacing {0} blocks in chunk at {1}, {2}", count, chunkXPos, chunkZPos)
chunk.Blocks[slice] = newId
chunk.chunkChanged()
def main(level, okayToRun):
itemdata = level.root_tag["FML"]["ItemData"]
conflicting = findConflicting(itemdata)
# Calling list() holds all the results in memory. Even though you're
# supposed to back up before running the script, it's safer to do the
# migration in a single go.
newIds = list(findNewIds(conflicting, itemdata))
if MCEDIT:
if not okayToRun:
return
elif sys.stdin:
warning("""
About to move IDs.
*** PLEASE BACK UP YOUR WORLD BEFORE CONTINUING ***
Once you have backed up your world, please type the word "Continue" (no quotes,
caps sensitive), and press enter.
""")
if input() != "Continue":
error("Bailing!")
info("Working, please wait.")
moveIds(newIds, level)
level.saveInPlace()
info("All done!")
def commandMain(args):
''' Command-line entry point '''
if len(args) < 2:
error("Please give a level file")
main(pymclevel.mclevel.fromFile(args[1]), "--dry-run" not in args)
inputs = (
("World is backed up", False),
)
def perform(level, box, options):
''' MCEdit filter entry point '''
global MCEDIT
MCEDIT = True
main(level, options["World is backed up"])
if __name__ == "__main__":
commandMain(sys.argv)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment