minetest/util/minesplice.py

295 lines
8.3 KiB
Python

#!/usr/bin/env python
# -*- coding: utf-8 -*-
'''
minesplice.py: copy, swap, and delete minetest blocks between map files
Copyright (C) 2012 Bad_Command
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
'''
from optparse import OptionParser
import re
import math
import sqlite3 as sql
def blockAsInt(x, y, z):
return int(z * 16777216 + y * 4096 + x)
def swapBlocks(srcmap, srcvol, dstmap, dstpos):
try:
srcCon = sql.connect(srcmap);
dstCon = sql.connect(dstmap);
srcCurs = srcCon.cursor();
dstCurs = dstCon.cursor();
selectStmt = "SELECT `data` FROM `blocks` WHERE `pos` = ?1"
insertStmt = "REPLACE INTO `blocks`(`pos`, `data`) VALUES (?1, ?2)"
srcvol = srcvol.toBlockCoord();
tX = 0;
tY = 0;
tZ = 0;
if dstpos:
dstpos = dstpos.toBlockCoord();
tX = dstpos.X - srcvol.X
tY = dstpos.Y - srcvol.Y
tZ = dstpos.Z - srcvol.Z
swapcount = 0
progresscount = 0
totalvolume = srcvol.dX * srcvol.dY * srcvol.dZ
for x in range(0, srcvol.dX):
for y in range(0, srcvol.dY):
for z in range(0, srcvol.dZ):
srcblockid = blockAsInt(srcvol.X + x, srcvol.Y + y, srcvol.Z + z)
srcCurs.execute(selectStmt, (srcblockid,))
srcdata = srcCurs.fetchone();
dstblockid = blockAsInt(srcvol.X + x + tX, srcvol.Y + y + tY, srcvol.Z + z + tZ)
dstCurs.execute(selectStmt, (dstblockid,))
dstdata = dstCurs.fetchone();
if srcdata:
dstCurs.execute(insertStmt, (dstblockid, srcdata[0]))
if dstdata:
srcCurs.execute(insertStmt, (srcblockid, dstdata[0]))
if ( srcdata and dstdata ):
swapcount += 1;
progresscount += 1
if ( progresscount % (totalvolume/10) == 0):
print 100 * progresscount / totalvolume, "% done";
srcCon.commit()
dstCon.commit()
print "Swapped", swapcount, "/", totalvolume, "blocks,", (100 * swapcount) / totalvolume, "% (anything else had void space)"
except sql.Error, e:
print "Error %s:" % e.args[0]
finally:
if srcCon:
srcCon.close
if dstCon:
dstCon.close
def copyBlocks(srcmap, srcvol, dstmap, dstpos):
try:
srcCon = sql.connect(srcmap);
dstCon = sql.connect(dstmap);
srcCurs = srcCon.cursor();
dstCurs = dstCon.cursor();
selectStmt = "SELECT `data` FROM `blocks` WHERE `pos` = ?1"
insertStmt = "REPLACE INTO `blocks`(`pos`, `data`) VALUES (?1, ?2)"
srcvol = srcvol.toBlockCoord();
tX = 0;
tY = 0;
tZ = 0;
if dstpos:
dstpos = dstpos.toBlockCoord();
tX = dstpos.X - srcvol.X
tY = dstpos.Y - srcvol.Y
tZ = dstpos.Z - srcvol.Z
copycount = 0
progresscount = 0
totalvolume = srcvol.dX * srcvol.dY * srcvol.dZ
for x in range(0, srcvol.dX):
for y in range(0, srcvol.dY):
for z in range(0, srcvol.dZ):
blockid = blockAsInt(srcvol.X + x, srcvol.Y + y, srcvol.Z + z)
srcCurs.execute(selectStmt, (blockid,))
data = srcCurs.fetchone();
if data:
blockid = blockAsInt(srcvol.X + x + tX, srcvol.Y + y + tY, srcvol.Z + z + tZ)
dstCurs.execute(insertStmt, (blockid, data[0]))
copycount += 1
progresscount += 1
if ( progresscount % (totalvolume/10) == 0):
print 100 * progresscount / totalvolume, "% done";
dstCon.commit()
print "Copied", copycount, "/", totalvolume, "blocks,", (100 * copycount) / totalvolume, "% (anything else was void space)"
except sql.Error, e:
print "Error %s:" % e.args[0]
finally:
if srcCon:
srcCon.close
if dstCon:
dstCon.close
def deleteBlocks(srcmap, srcvol):
try:
con = sql.connect(srcmap);
curs = con.cursor();
deleteStmt = "DELETE FROM `blocks` WHERE `pos` = ?1"
srcvol = srcvol.toBlockCoord();
progresscount = 0
deletecount = 0
totalvolume = srcvol.dX * srcvol.dY * srcvol.dZ
for x in range(0, srcvol.dX):
for y in range(0, srcvol.dY):
for z in range(0, srcvol.dZ):
blockid = blockAsInt(srcvol.X + x, srcvol.Y + y, srcvol.Z + z)
curs.execute(deleteStmt, (blockid,))
if ( curs.rowcount > 0 ):
deletecount += 1
progresscount += 1
if ( progresscount % (totalvolume/10) == 0):
print 100 * progresscount / totalvolume, "% done";
con.commit()
print "Deleted", deletecount, "/", totalvolume, "blocks,", (100 * deletecount) / totalvolume, "% (anything else was void space)"
except sql.Error, e:
print "Error %s:" % e.args[0]
finally:
if con:
con.close
class Coordinates:
X = 0;
Y = 0;
Z = 0;
dX = 0;
dY = 0;
dZ = 0;
def toBlockCoord(self):
coords = Coordinates();
coords.X = int(math.floor(self.X / 16.0));
coords.Y = int(math.floor(self.Y / 16.0));
coords.Z = int(math.floor(self.Z / 16.0));
coords.dX = int(math.ceil(self.dX / 16.0));
coords.dY = int(math.ceil(self.dY / 16.0));
coords.dZ = int(math.ceil(self.dZ / 16.0));
return coords;
def parseVolume(vol):
p = re.compile("^src=(-?\\d{1,5}),(-?\\d{1,5}),(-?\\d{1,5})\\+(\\d{1,5})\\+(\\d{1,5})\\+(\\d{1,5})$")
match = p.match(vol);
if match == None:
return None;
o = Coordinates()
o.X = int(match.group(1));
o.Y = int(match.group(2));
o.Z = int(match.group(3));
o.dX = int(match.group(4));
o.dY = int(match.group(5));
o.dZ = int(match.group(6));
return o;
def parseCoord(coord):
p = re.compile("^dst=(-?\\d{1,5}),(-?\\d{1,5}),(-?\\d{1,5})$")
match = p.match(coord);
if match == None:
return None;
o = Coordinates()
o.X = int(match.group(1));
o.Y = int(match.group(2));
o.Z = int(match.group(3));
return o;
def main():
parser = OptionParser("""Usage: %prog [-c|-s|-d] source-map.sqlite src=X,Y,Z+dX+dY+dZ [destination-map.sqlite] [dst=X',Y',Z']
source-map.sqlite is the source map
X,Y,Z are coordinates for the start of the operation volume
+dX is the length (along X-axis) of the operation volume
+dY is the height (along Y-axis) of the operation volume
+dZ is the width (along Z-axis) of the operation volume
destination-map.sqlite is the destination map (not needed when deleting)
X',Y',Z' are the coordinates in the destination map (used when moving blocks)
Ideally coordinates should be multiples of 16 (block boundaries). If not:
X, Y, Z will round down
dX, dY, dZ will round up""")
parser.add_option("-c", "--copy", action="store_true", dest="copy",
help="Copy blocks from source-map to destination-map")
parser.add_option("-s", "--swap", action="store_true", dest="swap",
help="Swap the specified blocks between the source-map and destination-map")
parser.add_option("-d", "--delete", action="store_true", dest="delete",
help="Delete blocks from source-map")
(options, args) = parser.parse_args()
print options;
print args;
optCount = 0;
if options.copy == True:
optCount += 1;
if options.swap == True:
optCount += 1;
if options.delete == True:
optCount += 1;
if optCount > 1 or optCount < 1:
print "Must specify one and only one option"
return
if len(args) < 2:
print "Must at least specify source.mt X,Y,Z+dX+dY+dZ"
return
if len(args) > 4:
print "Too many arguments"
return
srcmap = args[0];
srcvol = parseVolume(args[1]);
if srcvol == None:
print "Invalid source volume: ", args[1];
return;
if len(args) >= 3:
dstmap = args[2];
dstpos = None
if len(args) == 4:
dstpos = parseCoord(args[3]);
if dstpos == None:
print "Invalid destination location: " , args[3];
return
if options.copy == True:
if len(args) < 3:
print "Must specify source.mt X,Y,Z+dX+dY+dZ destination.mt";
return;
copyBlocks(srcmap, srcvol, dstmap, dstpos);
if options.swap == True:
if len(args) < 3:
print "Must specify source.mt X,Y,Z+dX+dY+dZ destination.mt";
return;
swapBlocks(srcmap, srcvol, dstmap, dstpos);
if options.delete == True:
if len(args) != 2:
print "Must specify source.mt X,Y,Z+dX+dY+dZ";
return;
deleteBlocks(srcmap, srcvol);
if __name__ == '__main__':
main();