mirror of https://github.com/minetest/minetest.git
295 lines
8.3 KiB
Python
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();
|
|
|