|
import os |
|
import shutil |
|
import zipfile |
|
from datetime import datetime |
|
|
|
# 尝试导入第三方库,若未安装则自动安装 |
|
try: |
|
from rich.console import Console |
|
from rich.table import Table |
|
except ImportError: |
|
os.system("pip install rich") |
|
from rich.console import Console |
|
from rich.table import Table |
|
|
|
try: |
|
from InquirerPy import inquirer |
|
except ImportError: |
|
os.system("pip install InquirerPy") |
|
from InquirerPy import inquirer |
|
|
|
console = Console() |
|
|
|
def get_mca_file(x, z, is_chunk=False): |
|
# 计算区块坐标,如果是方块坐标,需转换 |
|
if not is_chunk: |
|
chunk_x = x // 16 |
|
chunk_z = z // 16 |
|
else: |
|
chunk_x = x |
|
chunk_z = z |
|
|
|
# 计算地图块坐标 |
|
region_x = chunk_x // 32 |
|
region_z = chunk_z // 32 |
|
|
|
# 返回 MCA 文件名和路径 |
|
mca_file = f"r.{region_x}.{region_z}.mca" |
|
path = os.path.join("world", "region", mca_file) |
|
return mca_file, path |
|
|
|
def backup_mca_file(mca_file, path): |
|
# 备份文件:将其重命名为 .bak |
|
bak_file = f"{mca_file}.bak" |
|
bak_path = os.path.join("world", "region", bak_file) |
|
shutil.move(path, bak_path) |
|
console.print(f"[green]已备份文件为: {bak_file}[/green]") |
|
return bak_path |
|
|
|
def list_backups(backup_dir="simplebackups"): |
|
# 获取备份文件夹中的所有zip文件 |
|
backups = [f for f in os.listdir(backup_dir) if f.endswith(".zip")] |
|
backups.sort() |
|
return backups |
|
|
|
def check_backup_contains_mca(backup_zip, mca_file): |
|
# 检查备份文件中是否含有指定mca文件 |
|
with zipfile.ZipFile(os.path.join("simplebackups", backup_zip), 'r') as zip_ref: |
|
return f"world/region/{mca_file}" in zip_ref.namelist() |
|
|
|
def choose_backup(backups, mca_file): |
|
# 展示备份时间及是否含有指定mca文件 |
|
table = Table(title="可用备份列表") |
|
table.add_column("编号", justify="center") |
|
table.add_column("备份时间", justify="center") |
|
table.add_column("状态", justify="center") |
|
|
|
backup_status = [] |
|
for idx, backup in enumerate(backups): |
|
# 获取备份时间戳 |
|
timestamp_str = backup.split("_")[1] + "_" + backup.split("_")[2].replace(".zip", "") |
|
timestamp = datetime.strptime(timestamp_str, "%Y-%m-%d_%H-%M-%S") |
|
formatted_date = timestamp.strftime("%Y年%m月%d日 %H:%M:%S") |
|
|
|
# 检查备份中是否包含 .mca 文件 |
|
contains_mca = check_backup_contains_mca(backup, mca_file) |
|
status = "[green]已加载[/green]" if contains_mca else "[red]未加载[/red]" |
|
|
|
# 显示编号、时间和状态 |
|
table.add_row(str(idx), formatted_date, status) |
|
backup_status.append((backup, contains_mca)) # 保存备份文件和加载状态 |
|
|
|
console.print(table) |
|
|
|
# 为用户选择提供带编号、时间和状态的选项 |
|
choices = [f"{idx}. {datetime.strptime(backup.split('_')[1] + '_' + backup.split('_')[2].replace('.zip', ''), '%Y-%m-%d_%H-%M-%S').strftime('%Y年%m月%d日 %H:%M:%S')} [{status}]" |
|
for idx, (backup, contains_mca) in enumerate(backup_status)] |
|
|
|
# 用户选择备份 |
|
choice = inquirer.select( |
|
message="请选择要回溯的备份编号:", |
|
choices=choices |
|
).execute() |
|
|
|
selected_index = int(choice.split('.')[0]) # 提取用户选择的编号 |
|
return backups[selected_index], backup_status[selected_index][1] # 返回所选的备份文件名及其状态 |
|
|
|
def restore_mca_file(backup_zip, mca_file): |
|
# 从备份zip中解压出指定的mca文件 |
|
with zipfile.ZipFile(os.path.join("simplebackups", backup_zip), 'r') as zip_ref: |
|
file_to_extract = f"world/region/{mca_file}" |
|
zip_ref.extract(file_to_extract, ".") |
|
console.print(f"[green]已从备份恢复文件: {mca_file}[/green]") |
|
|
|
def display_welcome_message(): |
|
console.print("[bold blue]欢迎使用BaimoRegionManager.py,一款专为Minecraft服务器设计的轻量化区块删除/回溯器。[/bold blue]") |
|
console.print("[bold cyan]适配SimpleBackup提供回溯功能。[/bold cyan]") |
|
console.print("[bold yellow]了解作者:https://me.baimoqilin.top/[/bold yellow]") |
|
console.print("[bold magenta]广告位招租联系[email protected][/bold magenta]") |
|
console.print("[bold green]Hypiworld机械动力服务器 QQ群号640159882[/bold green]") |
|
|
|
def main(): |
|
# 显示欢迎信息 |
|
display_welcome_message() |
|
|
|
# 选择输入方块坐标或区块坐标 |
|
coord_type = inquirer.select( |
|
message="请选择输入类型:", |
|
choices=["方块坐标", "区块坐标"], |
|
).execute() |
|
|
|
if coord_type == "方块坐标": |
|
x = int(inquirer.text(message="请输入X坐标").execute()) |
|
z = int(inquirer.text(message="请输入Z坐标").execute()) |
|
mca_file, path = get_mca_file(x, z, is_chunk=False) |
|
else: |
|
chunk_x = int(inquirer.text(message="请输入区块X坐标").execute()) |
|
chunk_z = int(inquirer.text(message="请输入区块Z坐标").execute()) |
|
mca_file, path = get_mca_file(chunk_x, chunk_z, is_chunk=True) |
|
|
|
if not os.path.exists(path): |
|
console.print(f"[red]MCA文件不存在: {path}[/red]") |
|
return |
|
|
|
# 备份文件 |
|
bak_path = backup_mca_file(mca_file, path) |
|
|
|
# 询问用户是否删除或回溯 |
|
action = inquirer.select( |
|
message="是否要删除文件(重命名为.bak),还是从备份中回溯?", |
|
choices=["删除", "回溯"], |
|
).execute() |
|
|
|
if action == "删除": |
|
console.print("[bold red]已将文件重命名为.bak,删除成功。[/bold red]") |
|
return |
|
|
|
# 获取备份列表 |
|
backups = list_backups() |
|
if not backups: |
|
console.print("[red]没有可用的备份[/red]") |
|
return |
|
|
|
# 选择要回溯的备份 |
|
selected_backup, is_loaded = choose_backup(backups, mca_file) |
|
|
|
# 如果备份未加载,进行删除操作 |
|
if not is_loaded: |
|
console.print("[bold red]备份中未找到对应的mca文件,执行删除操作。[/bold red]") |
|
return |
|
|
|
# 从备份中恢复文件 |
|
restore_mca_file(selected_backup, mca_file) |
|
|
|
# 提示用户重启服务器 |
|
console.print("[bold green]文件已成功恢复,重启服务器以使更改生效。[/bold green]") |
|
|
|
if __name__ == "__main__": |
|
main() |