Games Backup

Minecraft, Terraria, and Starbound backup script

Python scripts

Minecraft

import os
import shutil
import datetime
import argparse

from typing import Union


def backup(minecraft_path: str,
           world_name: str,
           backup_dir: str,
           max_num_backups: int,
           download_dir: Union[None, str]) -> None:
    """Automatically creates backups of a Minecraft world.
    :param minecraft_path: The path to the Minecraft directory.
    :param world_name: The name of the world to backup.
    :param backup_dir: The path to the backup directory.
    :param max_num_backups: The maximum number of backups to keep.
    :param download_dir: The path to the download directory.
    :return: None
    """
    assert max_num_backups > 0, "max_num_backups must be greater than 0"

    # Create a timestamp for the backup
    timestamp = datetime.datetime.now().timestamp()
    backup_name = str(int(timestamp))

    # Construct the full paths
    world_path = os.path.join(os.path.abspath(minecraft_path), world_name)
    backup_path = os.path.join(os.path.abspath(backup_dir), backup_name, world_name)

    # List all the backups
    backups = os.listdir(backup_dir)

    # Sort the backups by timestamp, where the oldest backup is first
    backups.sort(key=lambda x: int(x))

    # Delete the oldest backups if the number of backups will exceed max_num_backups
    while len(backups) > max_num_backups - 1:
        oldest_backup = backups.pop(0)
        backup_to_delete = os.path.join(backup_dir, oldest_backup)
        shutil.rmtree(backup_to_delete)

    # Copy the world to the backup directory
    shutil.copytree(world_path, backup_path)

    print(f"Backup created: {backup_name}")

    # If a download directory is specified, zip the backup to the download directory
    if download_dir is not None:
        # Clean the download directory
        for file in os.listdir(download_dir):
            try:
                os.remove(os.path.join(download_dir, file))
            # Ignore the file if it is currently being used
            except (PermissionError, Exception) as e:
                print(f"Failed to delete {file}: {e}")

        # Zip the backup to the download directory
        shutil.make_archive(os.path.join(os.path.abspath(download_dir), backup_name), 'zip',
                            os.path.join(os.path.abspath(backup_dir), backup_name))

        print(f"Backup {backup_name} zipped to {download_dir}")


if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='Automatically creates backups of a Minecraft world.')
    parser.add_argument('--minecraft_path', type=str, help='The path to the Minecraft directory.')
    parser.add_argument('--world_name', type=str, help='The name of the world to backup.')
    parser.add_argument('--backup_dir', type=str, help='The path to the backup directory.')
    parser.add_argument('--max_num_backups', type=int, default=5,
                        help='The maximum number of backups to keep.')
    parser.add_argument('--download_dir', type=str, default=None, help='The path to the download directory.')
    args = parser.parse_args()

    backup(args.minecraft_path, args.world_name, args.backup_dir, args.max_num_backups, args.download_dir)

Terraria

import os
import shutil
import datetime
import argparse

from typing import Union


def backup(terraria_path: str,
           world_name: str,
           backup_dir: str,
           max_num_backups: int,
           download_dir: Union[None, str]) -> None:
    """Automatically creates backups of a Terraria world.
    :param terraria_path: The path to the Terraria directory.
    :param world_name: The name of the world to backup.
    :param backup_dir: The path to the backup directory.
    :param max_num_backups: The maximum number of backups to keep.
    :param download_dir: The path to the download directory.
    :return: None
    """
    assert max_num_backups > 0, "max_num_backups must be greater than 0"

    # Create a timestamp for the backup
    timestamp = datetime.datetime.now().timestamp()
    backup_name = str(int(timestamp))

    # Construct the full paths
    world_path = os.path.join(os.path.abspath(terraria_path), f'{world_name}.wld')
    backup_dir_name = os.path.join(os.path.abspath(backup_dir), backup_name)
    backup_path = os.path.join(backup_dir_name, f'{world_name}.wld')

    # List all the backups
    backups = os.listdir(backup_dir)

    # Sort the backups by timestamp, where the oldest backup is first
    backups.sort(key=lambda x: int(x))

    # Delete the oldest backups if the number of backups will exceed max_num_backups
    while len(backups) > max_num_backups - 1:
        oldest_backup = backups.pop(0)
        backup_to_delete = os.path.join(backup_dir, oldest_backup)
        shutil.rmtree(backup_to_delete)

    # Create the backup directory
    os.makedirs(backup_dir_name, exist_ok=False)
    # Copy the world to the backup directory
    shutil.copy(world_path, backup_path)

    print(f"Backup created: {backup_name}")

    # If a download directory is specified, zip the backup to the download directory
    if download_dir is not None:
        # Clean the download directory
        for file in os.listdir(download_dir):
            try:
                os.remove(os.path.join(download_dir, file))
            # Ignore the file if it is currently being used
            except (PermissionError, Exception) as e:
                print(f"Failed to delete {file}: {e}")

        # Zip the backup to the download directory
        shutil.make_archive(os.path.join(os.path.abspath(download_dir), backup_name), 'zip',
                            os.path.join(os.path.abspath(backup_dir), backup_name))

        print(f"Backup {backup_name} zipped to {download_dir}")


if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='Automatically creates backups of a Terraria world.')
    parser.add_argument('--terraria_path', type=str, help='The path to the Terraria directory.')
    parser.add_argument('--world_name', type=str, help='The name of the world to backup.')
    parser.add_argument('--backup_dir', type=str, help='The path to the backup directory.')
    parser.add_argument('--max_num_backups', type=int, default=5,
                        help='The maximum number of backups to keep.')
    parser.add_argument('--download_dir', type=str, default=None, help='The path to the download directory.')
    args = parser.parse_args()

    backup(args.terraria_path, args.world_name, args.backup_dir, args.max_num_backups, args.download_dir)

Starbound

import os
import shutil
import datetime
import argparse

from typing import Union


def backup(starbound_storage_path: str,
           backup_dir: str,
           max_num_backups: int,
           download_dir: Union[None, str]) -> None:
    """Automatically creates backups of a Minecraft world.
    :param starbound_storage_path: The path to the Starbound storage directory.
    :param backup_dir: The path to the backup directory.
    :param max_num_backups: The maximum number of backups to keep.
    :param download_dir: The path to the download directory.
    :return: None
    """
    assert max_num_backups > 0, "max_num_backups must be greater than 0"

    # Create a timestamp for the backup
    timestamp = datetime.datetime.now().timestamp()
    backup_name = str(int(timestamp))

    # Construct the full paths
    universe_name = 'universe'
    world_path = os.path.join(os.path.abspath(starbound_storage_path), universe_name)
    backup_path = os.path.join(os.path.abspath(backup_dir), backup_name, universe_name)

    # List all the backups
    backups = os.listdir(backup_dir)

    # Sort the backups by timestamp, where the oldest backup is first
    backups.sort(key=lambda x: int(x))

    # Delete the oldest backups if the number of backups will exceed max_num_backups
    while len(backups) > max_num_backups - 1:
        oldest_backup = backups.pop(0)
        backup_to_delete = os.path.join(backup_dir, oldest_backup)
        shutil.rmtree(backup_to_delete)

    # Copy the world to the backup directory
    shutil.copytree(world_path, backup_path)

    print(f"Backup created: {backup_name}")

    # If a download directory is specified, zip the backup to the download directory
    if download_dir is not None:
        # Clean the download directory
        for file in os.listdir(download_dir):
            try:
                os.remove(os.path.join(download_dir, file))
            # Ignore the file if it is currently being used
            except (PermissionError, Exception) as e:
                print(f"Failed to delete {file}: {e}")

        # Zip the backup to the download directory
        shutil.make_archive(os.path.join(os.path.abspath(download_dir), backup_name), 'zip',
                            os.path.join(os.path.abspath(backup_dir), backup_name))

        print(f"Backup {backup_name} zipped to {download_dir}")


if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='Automatically creates backups of a Starbound universe.')
    parser.add_argument('--starbound_storage_path', type=str, help='The path to the Starbound storage directory.')
    parser.add_argument('--backup_dir', type=str, help='The path to the backup directory.')
    parser.add_argument('--max_num_backups', type=int, default=5,
                        help='The maximum number of backups to keep.')
    parser.add_argument('--download_dir', type=str, default=None, help='The path to the download directory.')
    args = parser.parse_args()

    backup(args.starbound_storage_path, args.backup_dir, args.max_num_backups, args.download_dir)