Skip to content

Commit

Permalink
first commit
Browse files Browse the repository at this point in the history
  • Loading branch information
zyxkad committed Jan 6, 2022
0 parents commit c7b4e86
Show file tree
Hide file tree
Showing 9 changed files with 816 additions and 0 deletions.
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

# Mac OS
.DS_Store

# output
/output/
11 changes: 11 additions & 0 deletions mcdreforged.plugin.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"id": "smart_backup",
"version": "0.0.1",
"name": "SmartBackup",
"description": "A Minecraft Backup Plugin",
"author": "zyxkad",
"link": "https://github.com/zyxkad/smart_backup_mcdr",
"dependencies": {
"mcdreforged": ">=2.0.0"
}
}
45 changes: 45 additions & 0 deletions publisher.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#!/bin/bash

COMMIT=true
RELEASE=true

while [ -n "$1" ]; do
case $1 in
-n | --no-commit)
COMMIT=''
;;
-R | --no-release)
RELEASE=''
;;
esac
shift
done

cd $(dirname $0)

echo '==> Reading plugin metadata...'
data=($(python3 -c 'import sys,json;o=json.load(open("mcdreforged.plugin.json","r"));n=o["name"];d=o["id"];v=o["version"];an=o.get("archive_name");print(((n and n.replace(" ", ""))or d)+"-v"+v if not an else an.format(id=d,version=v));print(v)'))
if [ $? -ne 0 ]; then
echo '[ERROR] Cannot parse "mcdreforged.plugin.json"'
exit 1
fi
name="${data[0]}"
version="v${data[1]}"

echo '==> Packing source files...'
python3 -m mcdreforged pack -o ./output -n "$name" || exit $?

if [ -n "$COMMIT" ]; then

echo '==> Commiting git repo...'
( git add . && git commit -m "$version" && git push ) || exit $?

if [ -n "$RELEASE" ]; then

echo '==> Creating github release...'
gh release create "$version" "./output/${name}.mcdr" -t "$version" -n '' || exit $?

fi
fi

echo '==> Done'
Empty file added requirements.txt
Empty file.
20 changes: 20 additions & 0 deletions smart_backup/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@

import mcdreforged.api.all as MCDR
from .utils import *
from . import globals as GL
from . import commands as CMD

def on_load(server: MCDR.PluginServerInterface, prev_module):
if prev_module is None:
log_info('Smart backup is on LOAD')
else:
log_info('Smart backup is on RELOAD')
GL.init(server)
CMD.register(server)

def on_unload(server: MCDR.PluginServerInterface):
log_info('Smart backup is on UNLOAD')
GL.destory()

def on_info(server: MCDR.ServerInterface, info: MCDR.Info):
CMD.on_info(server, info)
141 changes: 141 additions & 0 deletions smart_backup/commands.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@

import mcdreforged.api.all as MCDR
from .utils import *
from . import globals as GL
from .objects import *

Prefix = '!!smb'

HelpMessage = '''
{0} help 显示帮助信息
{0} list [<limit>] 列出[所有/<limit>条]备份
{0} query <id> 查询备份详细信息
{0} make [<comment>] 创建新备份(差异/全盘)
{0} makefull [<comment>] 创建全盘备份
{0} rm <id> [force] 删除指定备份(及其子备份)
{0} restore [<id>] 回档至[上次/指定id]备份
{0} confirm 确认操作
{0} abort 取消操作
{0} reload 重新加载配置文件
{0} save 保存配置文件
'''.strip().format(Prefix)

game_saved_callback = None

def on_info(server: MCDR.ServerInterface, info: MCDR.Info):
if not info.is_user:
global game_saved_callback
if game_saved_callback is not None and GL.Config.test_backup_trigger(info.content):
c, game_saved_callback = game_saved_callback, None
c()

def register(server: MCDR.PluginServerInterface):
server.register_command(
MCDR.Literal(Prefix).
runs(command_help).
then(GL.Config.literal('help').runs(command_help)).
then(GL.Config.literal('list').
runs(lambda src: command_list_backup(src, 10)).
then(MCDR.Integer('limit').at_min(0).
runs(lambda src, ctx: command_list_backup(src, ctx['limit'])))).
then(GL.Config.literal('query').
then(MCDR.Text('id').
runs(lambda src, ctx: command_query_backup(src, ctx['id'])))).
then(GL.Config.literal('make').
runs(lambda src: command_make(src, 'None')).
then(MCDR.GreedyText('comment').runs(lambda src, ctx: command_make(src, ctx['comment'])))).
then(GL.Config.literal('makefull').
runs(lambda src: command_makefull(src, 'None')).
then(MCDR.GreedyText('comment').runs(lambda src, ctx: command_makefull(src, ctx['comment'])))).
then(GL.Config.literal('confirm').runs(command_confirm)).
then(GL.Config.literal('abort').runs(command_abort)).
then(GL.Config.literal('reload').runs(command_config_load)).
then(GL.Config.literal('save').runs(command_config_save))
)

def command_help(source: MCDR.CommandSource):
send_block_message(source, HelpMessage)

def command_list_backup(source: MCDR.CommandSource, limit: int):
send_message(source, 'TODO: list "{}" backup'.format(limit))

@new_thread
def command_query_backup(source: MCDR.CommandSource, bid: str):
send_message(source, 'TODO: query backup "{}"'.format(bid))

@new_thread
@new_job('make backup')
def command_make(source: MCDR.CommandSource, comment: str):
server = source.get_server()
send_message(source, 'Making backup "{}"'.format(comment), log=True)
tuple(map(server.execute, GL.Config.befor_backup))

def call():
mode = BackupMode.FULL
if 'differential_count' not in GL.Config.cache or GL.Config.cache['differential_count'] >= GL.Config.differential_backup_limit:
GL.Config.cache['differential_count'] = 0
else:
mode = BackupMode.DIFFERENTIAL
GL.Config.cache['differential_count'] += 1
backup = Backup.create(mode, comment,
source.get_server().get_mcdr_config()['working_directory'], GL.Config.backup_needs, GL.Config.backup_ignores)
tuple(map(server.execute, GL.Config.after_backup))
send_message(source, 'Saving backup "{}"'.format(comment), log=True)
backup.save(GL.Config.backup_path)
send_message(source, 'Saved backup "{}"'.format(comment), log=True)

if len(GL.Config.start_backup_trigger_info) > 0:
begin_job()
global game_saved_callback
game_saved_callback = new_thread(lambda: (call(), after_job()))
else:
call()

@new_thread
@new_job('make full backup')
def command_makefull(source: MCDR.CommandSource, comment: str):
server = source.get_server()

def call():
backup = Backup.create(BackupMode.FULL, comment,
source.get_server().get_mcdr_config()['working_directory'], GL.Config.backup_needs, GL.Config.backup_ignores)
tuple(map(server.execute, GL.Config.after_backup))
send_message(source, 'Saving backup "{}"'.format(comment), log=True)
backup.save(GL.Config.backup_path)
send_message(source, 'Saved backup "{}"'.format(comment), log=True)

begin_job()
global game_saved_callback
game_saved_callback = new_thread(lambda: (call(), after_job()))

send_message(source, 'Making full backup "{}"'.format(comment), log=True)
tuple(map(server.execute, GL.Config.befor_backup))


def command_confirm(source: MCDR.CommandSource):
confirm_map.pop(source.player if source.is_player else '', (lambda s: send_message(s, '当前没有正在执行的操作'), 0))[0](source)

def command_abort(source: MCDR.CommandSource):
c = confirm_map.pop(source.player if source.is_player else '', (0, 0))[1]
if not c:
c = confirm_map.pop(None, (0, lambda s: send_message(s, '当前没有正在执行的操作')))[1]
c(source)

@new_thread
def command_config_load(source: MCDR.CommandSource):
GL.Config = server.load_config_simple(target_class=GL.SMBConfig, source_to_reply=source)

@new_thread
def command_config_save(source: MCDR.CommandSource):
GL.Config.save()
send_message(source, 'Save config file SUCCESS')

confirm_map = {}

def __warp_call(call):
def c(*b):
return call(*b[:call.__code__.co_argcount])
return c

def register_confirm(player: str, confirm_call, abort_call=lambda: 0):
confirm_map[player] = (__warp_call(confirm_call), __warp_call(abort_call))
78 changes: 78 additions & 0 deletions smart_backup/globals.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@

import re
from typing import List, Dict, Any

import mcdreforged.api.all as MCDR

__all__ = [
'MSG_ID', 'BIG_BLOCK_BEFOR', 'BIG_BLOCK_AFTER', 'SMBConfig', 'Config', 'SERVER_INS', 'init', 'destory'
]

MSG_ID = MCDR.RText('[SMB]', color=MCDR.RColor.green)
BIG_BLOCK_BEFOR = '------------ {0} v{1} ::::'
BIG_BLOCK_AFTER = ':::: {0} v{1} ============'

class SMBConfig(MCDR.Serializable):
differential_backup_limit: int = 10
full_backup_limit: int = 10
backup_interval: int = 60 * 60 * 1 # 1 hours
last_backup_time: int = 0
restore_timeout: int = 30
backup_path: str = './smt_backups'
overwrite_path: str = './smt_backup_overwrite'
backup_needs: List[str] = ['world']
backup_ignores: List[str] = ['session.lock']
befor_backup: List[str] = ['save-off', 'save-all flush']
start_backup_trigger_info: str = r'Saved the (?:game|world)'
after_backup: List[str] = ['save-on']
# 0:guest 1:user 2:helper 3:admin 4:owner
minimum_permission_level: Dict[str, int] = {
'help': 0,
'status': 1,
'list': 1,
'make': 2,
'makefull': 3,
'back': 3,
'confirm': 1,
'abort': 1,
'reload': 3,
'save': 3,
}
_cache: Dict[str, Any] = {}

def test_backup_trigger(self, info: str):
if not hasattr(self, '__start_backup_trigger') or self.__start_backup_trigger_info != self.start_backup_trigger_info:
self.__start_backup_trigger_info = self.start_backup_trigger_info
self.__start_backup_trigger = re.compile(self.start_backup_trigger_info)
return self.__start_backup_trigger.fullmatch(info) is not None

@property
def cache(self):
return self._cache

def literal(self, literal: str):
lvl = self.minimum_permission_level.get(literal, 0)
return MCDR.Literal(literal).requires(lambda src: src.has_permission(lvl),
lambda: MCDR.RText(MSG_ID.to_plain_text() + ' 权限不足', color=MCDR.RColor.red))

def save(self):
SERVER_INS.save_config_simple(self)


Config: SMBConfig = SMBConfig()
SERVER_INS: MCDR.PluginServerInterface = None

def init(server: MCDR.PluginServerInterface):
global SERVER_INS
SERVER_INS = server
global BIG_BLOCK_BEFOR, BIG_BLOCK_AFTER
metadata = server.get_self_metadata()
BIG_BLOCK_BEFOR = BIG_BLOCK_BEFOR.format(metadata.name, metadata.version)
BIG_BLOCK_AFTER = BIG_BLOCK_AFTER.format(metadata.name, metadata.version)
global Config
Config = server.load_config_simple(target_class=SMBConfig)

def destory():
global SERVER_INS
Config.save()
SERVER_INS = None
Loading

0 comments on commit c7b4e86

Please sign in to comment.