Skip to content

Commit

Permalink
v1.4.4
Browse files Browse the repository at this point in the history
neater lock thing, more type hints, delete on another thread
  • Loading branch information
Fallen-Breath committed Dec 9, 2021
1 parent becdbf6 commit a5994ed
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 73 deletions.
10 changes: 6 additions & 4 deletions lang/en_us.yml
Expand Up @@ -22,13 +22,17 @@ quick_backup_multi:
unknown_slot: Slot format wrong, it should be a number between [{0}, {1}]
empty_slot: Slot §6{}§r is empty

lock.warning: Executing "{0}", please don't spam
operations:
delete: §aDeleting slot§r
create: §aBacking up§r
restore: §cRestoring§r

delete_backup:
success: §aSlot §6{0}§r delete success§r
fail: "§4Slot §6{0}§r delete fail, error code {1}§r"

create_backup:
restoring: §cRestoring§r, don't back up
backing_up: §aBacking up§r, don't spam
start: §aBacking up§r, please wait
abort.plugin_unload: Plugin unloaded, §aback up§r aborted!
abort.no_slot: Available slot not found, §aback up§r aborted!
Expand All @@ -45,8 +49,6 @@ quick_backup_multi:
confirm_restore.nothing_to_confirm: Nothing to confirm

do_restore:
backing_up: §aBacking up§r, don't restore
restoring: §cRestoring§r, don't spam
countdown.intro: §cRestore§r after 10 second
countdown.text: "{0} second later the world will be §crestored§r to slot §6{1}§r, {2}"
countdown.hover: Click to ABORT restore!
Expand Down
10 changes: 6 additions & 4 deletions lang/zh_cn.yml
Expand Up @@ -22,13 +22,17 @@ quick_backup_multi:
unknown_slot: 槽位输入错误,应输入一个位于[{0}, {1}]的数字
empty_slot: 槽位输入错误,槽位§6{0}§r为空

lock.warning: 正在{0}中,请等待操作执行完成
operations:
delete: §a删除槽位§r
create: §a备份§r
restore: §c回档§r

delete_backup:
success: §a删除槽位§6{0}§r完成§r
fail: "§4删除槽位§6{0}§r失败§r,错误代码: {1}"

create_backup:
restoring: 正在§c回档§r中,请不要尝试备份
backing_up: 正在§a备份§r中,请不要重复输入
start: §a备份§r中...请稍等
abort.plugin_unload: 插件重载,§a备份§r中断!
abort.no_slot: 未找到可用槽位,§a备份§r中断!
Expand All @@ -45,8 +49,6 @@ quick_backup_multi:
confirm_restore.nothing_to_confirm: 没有什么需要确认的

do_restore:
backing_up: 正在§a备份§r中,请不要尝试回档
restoring: 正在准备§c回档§r中,请不要重复输入
countdown.intro: 10秒后关闭服务器§c回档§r
countdown.text: 还有{0}秒,将§c回档§r为槽位§6{1}§r,{2}
countdown.hover: 点击终止回档!
Expand Down
2 changes: 1 addition & 1 deletion mcdreforged.plugin.json
@@ -1,6 +1,6 @@
{
"id": "quick_backup_multi",
"version": "1.4.3",
"version": "1.4.4",
"name": "Quick Backup Multi",
"description": {
"en_us": "A backup / restore plugin, with multiple backup slot",
Expand Down
110 changes: 46 additions & 64 deletions quick_backup_multi/__init__.py
@@ -1,10 +1,11 @@
import functools
import json
import os
import re
import shutil
import time
from threading import Lock
from typing import Optional, Any
from typing import Optional, Any, Callable

from mcdreforged.api.all import *

Expand All @@ -19,8 +20,8 @@
abort_restore = False
game_saved = False
plugin_unloaded = False
creating_backup_lock = Lock()
restoring_backup_lock = Lock()
operation_lock = Lock()
operation_name = RText('?')


def tr(translation_key: str, *args) -> RTextMCDRTranslation:
Expand Down Expand Up @@ -56,7 +57,7 @@ def copy_worlds(src: str, dst: str):
server_inst.logger.warning('{} does not exist while copying ({} -> {})'.format(src_path, src_path, dst_path))


def remove_worlds(folder):
def remove_worlds(folder: str):
for world in config.world_names:
target_path = os.path.join(folder, world)
if os.path.isdir(target_path):
Expand All @@ -71,11 +72,11 @@ def get_slot_count():
return len(config.slots)


def get_slot_folder(slot):
def get_slot_folder(slot: int):
return os.path.join(config.backup_path, f"slot{slot}")


def get_slot_info(slot):
def get_slot_info(slot: int):
"""
:param int slot: the index of the slot
:return: the slot info
Expand Down Expand Up @@ -104,22 +105,21 @@ def format_protection_time(time_length: float) -> RTextBase:
return tr('day', round(time_length / 60 / 60 / 24, 2))


def format_slot_info(info_dict=None, slot_number=None):
if type(info_dict) is dict:
def format_slot_info(info_dict: Optional[dict] = None, slot_number: Optional[int] = None) -> Optional[RTextBase]:
if isinstance(info_dict, dict):
info = info_dict
elif type(slot_number) is not None:
elif slot_number is not None:
info = get_slot_info(slot_number)
else:
return None

if info is None:
return None
msg = tr('slot_info', info['time'], info.get('comment', tr('empty_comment')))
return msg
return tr('slot_info', info['time'], info.get('comment', tr('empty_comment')))


def touch_backup_folder():
def mkdir(path):
def mkdir(path: str):
if os.path.isfile(path):
os.remove(path)
if not os.path.isdir(path):
Expand All @@ -130,21 +130,8 @@ def mkdir(path):
mkdir(get_slot_folder(i + 1))


def slot_number_formatter(slot):
flag_fail = False
if type(slot) is not int:
try:
slot = int(slot)
except ValueError:
flag_fail = True
if flag_fail or not 1 <= slot <= get_slot_count():
return None
return slot


def slot_check(source, slot):
slot = slot_number_formatter(slot)
if slot is None:
def slot_check(source: CommandSource, slot: int):
if not 1 <= slot <= get_slot_count():
print_message(source, tr('unknown_slot', 1, get_slot_count()))
return None

Expand All @@ -155,10 +142,25 @@ def slot_check(source, slot):
return slot, slot_info


def delete_backup(source, slot):
global creating_backup_lock, restoring_backup_lock
if creating_backup_lock.locked() or restoring_backup_lock.locked():
return
def single_op(name: RTextBase):
def wrapper(func: Callable):
@functools.wraps(func)
def wrap(source: CommandSource, *args, **kwargs):
acq = operation_lock.acquire(blocking=False)
if acq:
try:
func(source, *args, **kwargs)
finally:
operation_lock.release()
else:
print_message(source, tr('lock.warning', name))
return wrap
return wrapper


@new_thread('QBM - delete')
@single_op(tr('operations.delete'))
def delete_backup(source: CommandSource, slot: int):
if slot_check(source, slot) is None:
return
try:
Expand Down Expand Up @@ -212,20 +214,13 @@ def clean_up_slot_1():
return False


@new_thread('QBM')
@new_thread('QBM - create')
def create_backup(source: CommandSource, comment: Optional[str]):
_create_backup(source, comment)


@single_op(tr('operations.create'))
def _create_backup(source: CommandSource, comment: Optional[str]):
global restoring_backup_lock, creating_backup_lock
if restoring_backup_lock.locked():
print_message(source, tr('create_backup.restoring'), tell=False)
return
acquired = creating_backup_lock.acquire(blocking=False)
if not acquired:
print_message(source, tr('create_backup.backing_up'), tell=False)
return
try:
print_message(source, tr('create_backup.start'), tell=False)
start_time = time.time()
Expand Down Expand Up @@ -273,12 +268,10 @@ def _create_backup(source: CommandSource, comment: Optional[str]):
else:
source.get_server().dispatch_event(BACKUP_DONE_EVENT, (source, slot_info))
finally:
creating_backup_lock.release()
if config.turn_off_auto_save:
source.get_server().execute('save-on')


@new_thread('QBM')
def restore_backup(source: CommandSource, slot: int):
ret = slot_check(source, slot)
if ret is None:
Expand All @@ -298,7 +291,7 @@ def restore_backup(source: CommandSource, slot: int):
)


@new_thread('QBM')
@new_thread('QBM - restore')
def confirm_restore(source: CommandSource):
global slot_selected
if slot_selected is None:
Expand All @@ -309,15 +302,8 @@ def confirm_restore(source: CommandSource):
_do_restore_backup(source, slot)


@single_op(tr('operations.restore'))
def _do_restore_backup(source: CommandSource, slot: int):
global restoring_backup_lock, creating_backup_lock
if creating_backup_lock.locked():
print_message(source, tr('do_restore.backing_up'), tell=False)
return
acquired = restoring_backup_lock.acquire(blocking=False)
if not acquired:
print_message(source, tr('do_restore.restoring'), tell=False)
return
try:
print_message(source, tr('do_restore.countdown.intro'), tell=False)
slot_info = get_slot_info(slot)
Expand Down Expand Up @@ -358,29 +344,27 @@ def _do_restore_backup(source: CommandSource, slot: int):
server_inst.logger.exception('Fail to restore backup to slot {}, triggered by {}'.format(slot, source))
else:
source.get_server().dispatch_event(RESTORE_DONE_EVENT, (source, slot, slot_info)) # async dispatch
finally:
restoring_backup_lock.release()


def trigger_abort(source):
def trigger_abort(source: CommandSource):
global abort_restore, slot_selected
abort_restore = True
slot_selected = None
print_message(source, tr('trigger_abort.abort'), tell=False)


@new_thread('QBM')
@new_thread('QBM - list')
def list_backup(source: CommandSource, size_display: bool = None):
if size_display is None:
size_display = config.size_display

def get_dir_size(dir):
def get_dir_size(dir_: str):
size = 0
for root, dirs, files in os.walk(dir):
for root, dirs, files in os.walk(dir_):
size += sum([os.path.getsize(os.path.join(root, name)) for name in files])
return size

def format_dir_size(size):
def format_dir_size(size: int):
if size < 2 ** 30:
return f'{round(size / 2 ** 20, 2)} MB'
else:
Expand Down Expand Up @@ -414,7 +398,7 @@ def format_dir_size(size):
print_message(source, tr('list_backup.total_space', format_dir_size(backup_size)), prefix='')


@new_thread('QBM')
@new_thread('QBM - help')
def print_help_message(source: CommandSource):
if source.is_player:
source.reply('')
Expand Down Expand Up @@ -508,12 +492,10 @@ def register_event_listeners(server: PluginServerInterface):


def on_load(server: PluginServerInterface, old):
global creating_backup_lock, restoring_backup_lock, HelpMessage, server_inst
global operation_lock, HelpMessage, server_inst
server_inst = server
if hasattr(old, 'creating_backup_lock') and type(old.creating_backup_lock) == type(creating_backup_lock):
creating_backup_lock = old.creating_backup_lock
if hasattr(old, 'restoring_backup_lock') and type(old.restoring_backup_lock) == type(restoring_backup_lock):
restoring_backup_lock = old.restoring_backup_lock
if hasattr(old, 'operation_lock') and type(old.operation_lock) == type(operation_lock):
operation_lock = old.operation_lock

meta = server.get_self_metadata()
HelpMessage = tr('help_message', Prefix, meta.name, meta.version)
Expand Down

0 comments on commit a5994ed

Please sign in to comment.