Created
March 29, 2014 04:00
-
-
Save impiaaa/9848097 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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