diff --git a/README.md b/README.md index e9b7af0..e5f7f70 100644 --- a/README.md +++ b/README.md @@ -16,14 +16,16 @@ To install, run: ``` pip install --upgrade setuptools -pip install https://github.com/random-geek/MapEdit/archive/master.zip +pip install --upgrade https://github.com/random-geek/MapEdit/archive/master.zip ``` This will install MapEdit as a script/executable which can be run from anywhere. -This is the easiest way to install, as it will download the latest code directly from the repository. +This is the easiest way to install, as it will download the latest code directly from the repository. If you wish to install from a downloaded copy instead, install `setuptools` as usual, then run `python setup.py install` in the project directory. +You may also be prompted to add a directory to PATH. If so, follow instructions for your operating system on how to do this. + ## Usage #### About mapblocks @@ -206,6 +208,18 @@ Arguments: - **`--p1, --p2`**: Area in which to delete objects. If not specified, objects will be deleted across the entire map. - **`--invert`**: Only delete objects *outside* the given area. +### `vacuum` + +**Usage:** `vacuum` + +Vacuums the database. This reduces the size of the database, but may take a long time. + +All this does is perform an SQLite `VACUUM` command. This shrinks and optimizes the database by efficiently "repacking" all mapblocks. +No map data is changed or deleted. + +**Note:** Because data is copied into another file, this command could require as much free disk space as is already occupied by the map. +For example, if your database is 10 GB, make sure you have **at least 10 GB** of free space! + ## Acknowledgments Some of the code for this project was inspired by code from the [map_unexplore](https://github.com/AndrejIT/map_unexplore) project by AndrejIT. All due credit goes to the author(s) of that project. diff --git a/mapedit/commands.py b/mapedit/commands.py index 3ea0979..3d5ae23 100644 --- a/mapedit/commands.py +++ b/mapedit/commands.py @@ -545,7 +545,7 @@ def replace_in_inv(inst, args): inst.db.set_block(key, block.serialize()) # -# deletetimers +# deletetimers command # def delete_timers(inst, args): @@ -591,7 +591,7 @@ def delete_timers(inst, args): inst.db.set_block(key, block.serialize()) # -# deleteobjects +# deleteobjects command # def delete_objects(inst, args): @@ -639,6 +639,18 @@ def delete_objects(inst, args): block.serialize_static_objects(objList) inst.db.set_block(key, block.serialize()) +# +# vacuum command +# + +def vacuum(inst, args): + inst.log("warning", "Vacuum could require AS MUCH free disk space as\n" + "the current size of the database!") + + inst.begin(progBar=False) + inst.log("info", "Vacuuming...") + inst.db.vacuum() + COMMAND_DEFS = { # Argument format: (: ) @@ -754,7 +766,7 @@ COMMAND_DEFS = { "deleteobjects": { "func": delete_objects, - "help": "Delete static objects of a certain name and/or from a" + "help": "Delete static objects of a certain name and/or from a " "certain area.", "args": { "searchobj": False, @@ -763,6 +775,13 @@ COMMAND_DEFS = { "invert": False, } }, + + "vacuum": { + "func": vacuum, + "help": "Vacuum the database. This reduces the size of the database, " + "but may take a long time.", + "args": {} + }, } @@ -789,9 +808,13 @@ class MapEditInstance: self.print_warnings = True self.db = None self.sdb = None + self.has_begun = False def log(self, level, msg): - if level == "info": + if level == "": + # Print with no formatting. + print(msg) + elif level == "info": print("INFO: " + "\n ".join(msg.split("\n"))) elif level == "warning": @@ -803,28 +826,30 @@ class MapEditInstance: "\n ".join(msg.split("\n"))) raise MapEditError() - def begin(self): + def begin(self, progBar=True): if self.print_warnings: self.log("warning", self.STANDARD_WARNING) + if input("Proceed? (Y/n): ").lower() != "y": + self.log("", "Exiting.") raise MapEditError() - self.progress.set_start() + self.has_begun = True + if progBar: + self.progress.set_start() def finalize(self): - committed = False - - if self.db: - if self.db.is_modified(): - committed = True - self.log("info", "Committing to database...") - - self.db.close(commit=True) - if self.sdb: self.sdb.close() - if committed: + if self.db: + if self.db.is_modified(): + self.log("info", "Committing to database...") + self.db.commit() + + self.db.close() + + if self.has_begun: self.log("info", "Finished.") def update_progress(self, completed, total): @@ -833,7 +858,8 @@ class MapEditInstance: def _verify_and_run(self, args): self.print_warnings = not args.no_warnings - if bool(args.p1) != bool(args.p2): + if (hasattr(args, "p1") and hasattr(args, "p2") and + bool(args.p1) != bool(args.p2)): self.log("fatal", "Missing --p1 or --p2 argument.") if args.has_not_none("p1") and args.has_not_none("p2"): diff --git a/mapedit/utils.py b/mapedit/utils.py index 4730a85..f265499 100644 --- a/mapedit/utils.py +++ b/mapedit/utils.py @@ -158,6 +158,9 @@ class DatabaseHandler: except sqlite3.DatabaseError: raise + def is_modified(self): + return self.database.in_transaction + def get_block(self, key): self.cursor.execute("SELECT data FROM blocks WHERE pos = ?", (key,)) if data := self.cursor.fetchone(): @@ -181,13 +184,15 @@ class DatabaseHandler: self.cursor.execute("UPDATE blocks SET data = ? WHERE pos = ?", (data, key)) - def is_modified(self): - return self.database.in_transaction + def vacuum(self): + self.commit() # In case the database has been modified. + self.cursor.execute("VACUUM") - def close(self, commit=False): - if self.is_modified() and commit: + def commit(self): + if self.is_modified(): self.database.commit() + def close(self): self.database.close()