Skip to content

Commit

Permalink
add require_online_players and require_online_players_blacklist f…
Browse files Browse the repository at this point in the history
…or scheduled backup

cherry-picked changes from 6bb21da
added mermaid support to the doc
resolved #13
  • Loading branch information
Fallen-Breath committed Feb 24, 2024
1 parent 35b5e11 commit 9472ff4
Show file tree
Hide file tree
Showing 11 changed files with 2,145 additions and 19 deletions.
80 changes: 79 additions & 1 deletion docs/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -356,7 +356,9 @@ It's for creating a backup periodically and automatically for your server
"interval": "12h",
"crontab": null,
"jitter": "10s",
"reset_timer_on_backup": true
"reset_timer_on_backup": true,
"require_online_players": false,
"require_online_players_blacklist": []
}
```

Expand All @@ -373,6 +375,82 @@ This feature is only effective when the job is in interval mode
- Type: `bool`
- Default: `true`

#### require_online_players

If set to `true`, scheduled backups will only run normally when players are present on the server.
If all players logged out, then after the next scheduled backup is created, no more scheduled backup will be created

Example timeline:

```mermaid
flowchart LR
subgraph g1[Players online]
b1[8:00\ncreated] --> b2
end
subgraph g2[No players online]
b2[9:00\ncreated] --> b3
b3[10:00\ncreated] --> b4:::disabled
b4[11:00\nskipped] --> b5:::disabled
end
subgraph g3[Players online]
b5[12:00\nskipped] --> b6
b6[13:00\ncreated]
end
classDef disabled fill:#eee,stroke:#aaa,color:gray
```

!!! note

This feature requires the Prime Backup plugin to be loaded **before the server starts**.
It's needed so Prime Backup can calculate the online player count correctly

If the Prime Backup plugin is loaded when the server is already running,
the player detection feature will be disabled, as if [require_online_players](#require_online_players) is set to `false`

Exception: If [MinecraftDataAPI](https://github.com/MCDReforged/MinecraftDataAPI) plugin is present,
then Prime Backup will utilize its API to query online player dynamically, and the requirement mentioned above no longer exists

!!! note

The player detection logic might not work in heavily modded servers that behave too differently from vanilla servers

!!! tip

Since Prime Backup supports file deduplication and highly customizable [prune config](#prune-config),
having backups even when no player is on the server will not result in excessive space usage

- Type: `bool`
- Default: `false`

#### require_online_players_blacklist

In [require_online_players](#require_online_players), when determining if there are players online,
the list of regex patterns for player names to be excluded

If you want the server to still be considered as having no players online and thus not perform scheduled backups,
when only certain players are online, you can utilize this option

Example:

```json
"require_online_players_blacklist": [
"bot_.*", // Matches any player name starting with "bot_"
"Steve" // Matches the player name "Steve"
]
```

!!! note

The regex patterns will perform [full match](https://docs.python.org/3/library/re.html#re.fullmatch) checks on the player names

Matches are **case-insensitive**

- Type: `List[re.Pattern]`
- Default: `[]`

---

### Prune config
Expand Down
81 changes: 80 additions & 1 deletion docs/config.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,9 @@ Prime Backup 除了会保存 `world` 这个符号链接外,还会保存 `foo`
"interval": "12h",
"crontab": null,
"jitter": "10s",
"reset_timer_on_backup": true
"reset_timer_on_backup": true,
"require_online_players": false,
"require_online_players_blacklist": []
}
```

Expand All @@ -372,6 +374,83 @@ Prime Backup 除了会保存 `world` 这个符号链接外,还会保存 `foo`
- 类型:`bool`
- 默认值:`true`

#### require_online_players

若设为 `true`,则只有在服务器中存在玩家时,才正常进行定时备份。
如果所有玩家均已离线,则在下一次定时备份完成后,之后的定时备份都会被跳过。
也就是说,Prime Backup 在没有玩家在线上时,只会触发一次定时备份

时间线举例:

```mermaid
flowchart LR
subgraph g1[有玩家在线]
b1[8:00\n备份创建] --> b2
end
subgraph g2[无玩家在线]
b2[9:00\n备份创建] --> b3
b3[10:00\n备份创建] --> b4:::disabled
b4[11:00\n备份跳过] --> b5:::disabled
end
subgraph g3[有玩家在线]
b5[12:00\n备份跳过] --> b6
b6[13:00\n备份创建]
end
classDef disabled fill:#eee,stroke:#aaa,color:gray
```

!!! note

该功能需要 Prime Backup 插件在 **服务器启动前** 就被加载。
这是必需的,以便 Prime Backup 能够正确计算在线玩家数量

如果 Prime Backup 插件是在服务器运行过程中被加载的,
玩家检测功能将被禁用,就如同 [require_online_players](#require_online_players) 被设置为了 `false` 一样。

特例:如果存在 [MinecraftDataAPI](https://github.com/MCDReforged/MinecraftDataAPI) 插件,
那么 Prime Backup 将利用其 API 动态地查询在线玩家,此时上述需求将不再存在

!!! note

对于那些行为表现过于不原版的服务器,Prime Backup 的玩家在线计算逻辑可能无法正常工作

!!! tip

由于 Prime Backup 支持文件去重和高可自定义的 [清理配置](#清理配置) 功能,
即使在玩家不在线也照常进行定时备份,也不会占用过多的磁盘空间

- 类型:`bool`
- 默认值:`false`

#### require_online_players_blacklist

[require_online_players](#require_online_players) 判断是否存在玩家在线时,
排除的玩家名正则表达式列表

如果你希望服务器中只有某些玩家在线的时候,也视作服务器中不存在玩家,
不进行定时备份,则可以使用该选项

配置举例:

```json
"require_online_players_blacklist": [
"bot_.*", // 匹配所有以 "bot_" 为前缀的玩家名
"Steve" // 匹配 "Steve" 这个玩家名
]
```

!!! note

列表中的正则表达式将对玩家名执行 [全串匹配](https://docs.python.org/3/library/re.html#re.fullmatch) 检查

匹配时 **忽略大小写**

- 类型:`List[re.Pattern]`
- 默认值:`[]`

---

### 清理配置
Expand Down
1,759 changes: 1,759 additions & 0 deletions docs/js/mermaid-10.8.0.min.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions docs/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
mkdocs
mkdocs-material
mkdocs-mermaid2-plugin
mkdocs-static-i18n[material]
pymdown-extensions
10 changes: 10 additions & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,13 @@ markdown_extensions:
- pymdownx.emoji:
emoji_index: !!python/name:material.extensions.emoji.twemoji
emoji_generator: !!python/name:material.extensions.emoji.to_svg
# https://mkdocs-mermaid2.readthedocs.io/en/latest/#use-of-the-material-theme
- pymdownx.superfences:
# make exceptions to highlighting of code:
custom_fences:
- name: mermaid
class: mermaid
format: !!python/name:mermaid2.fence_mermaid_custom
theme:
name: material
features:
Expand Down Expand Up @@ -73,6 +80,9 @@ plugins:
site_name: Prime Backup 文档
nav_translations:
Reference: 参考
# https://mkdocs-mermaid2.readthedocs.io/
- mermaid2:
javascript: js/mermaid-10.8.0.min.js

nav:
- index.md
Expand Down
2 changes: 1 addition & 1 deletion prime_backup/config/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from prime_backup.config.command_config import CommandConfig
from prime_backup.config.database_config import DatabaseConfig
from prime_backup.config.prune_config import PruneConfig
from prime_backup.config.scheduled_backup import ScheduledBackupConfig
from prime_backup.config.scheduled_backup_config import ScheduledBackupConfig
from prime_backup.config.server_config import ServerConfig


Expand Down
12 changes: 0 additions & 12 deletions prime_backup/config/scheduled_backup.py

This file was deleted.

23 changes: 23 additions & 0 deletions prime_backup/config/scheduled_backup_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import re
from typing import Optional, List

from prime_backup.config.config_common import CrontabJobSetting
from prime_backup.types.units import Duration


class ScheduledBackupConfig(CrontabJobSetting):
enabled: bool = False
interval: Optional[Duration] = Duration('12h')
crontab: Optional[str] = None
jitter: Duration = Duration('10s')

reset_timer_on_backup: bool = True
require_online_players: bool = False
require_online_players_blacklist: List[re.Pattern] = []

def on_deserialization(self, **kwargs):
# recompile patterns with re.IGNORECASE
self.require_online_players_blacklist = [
re.compile(p.pattern, re.IGNORECASE)
for p in self.require_online_players_blacklist
]
30 changes: 29 additions & 1 deletion prime_backup/mcdr/crontab_job/scheduled_backup_job.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@
from apscheduler.schedulers.base import BaseScheduler

from prime_backup.config.config_common import CrontabJobSetting
from prime_backup.config.scheduled_backup import ScheduledBackupConfig
from prime_backup.config.scheduled_backup_config import ScheduledBackupConfig
from prime_backup.mcdr import mcdr_globals
from prime_backup.mcdr.crontab_job import CrontabJobEvent, CrontabJobId
from prime_backup.mcdr.crontab_job.basic_job import BasicCrontabJob
from prime_backup.mcdr.online_player_counter import OnlinePlayerCounter
from prime_backup.mcdr.task.backup.create_backup_task import CreateBackupTask
from prime_backup.types.operator import Operator, PrimeBackupOperatorNames
from prime_backup.utils import backup_utils
Expand All @@ -34,13 +35,40 @@ def id(self) -> CrontabJobId:
def job_config(self) -> CrontabJobSetting:
return self.config

@property
def __store(self) -> dict:
return OnlinePlayerCounter.get().job_data_store

@property
def __backups_without_players(self) -> int:
return self.__store.get('backups_without_players', 0)

@__backups_without_players.setter
def __backups_without_players(self, value: int):
self.__store['backups_without_players'] = value

def run(self):
if not self.config.enabled:
return

if not mcdr_globals.server.is_server_running():
return

if self.config.require_online_players:
online_players = OnlinePlayerCounter.get().get_online_players()
base_msg = 'Scheduled backup player check: valid={} ignored={}'.format(online_players.valid, online_players.ignored)
if online_players is not None and len(online_players.valid) == 0:
if self.__backups_without_players > 1:
self.logger.info('{}, backup skipped'.format(base_msg))
return
self.__backups_without_players = self.__backups_without_players + 1
self.logger.info('{}, performing the last backup'.format(base_msg))
else:
# player exists (True), or no valid data (None)
# let's perform the backup
self.__backups_without_players = 0
self.logger.info('{}, performing normally'.format(base_msg))

broadcast_message(self.tr('triggered', self.get_name_text_titled()))
with contextlib.ExitStack() as exit_stack:
self.is_executing.set()
Expand Down
27 changes: 24 additions & 3 deletions prime_backup/mcdr/entrypoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@
from prime_backup.mcdr import mcdr_globals
from prime_backup.mcdr.command.commands import CommandManager
from prime_backup.mcdr.crontab_manager import CrontabManager
from prime_backup.mcdr.online_player_counter import OnlinePlayerCounter
from prime_backup.mcdr.task_manager import TaskManager
from prime_backup.utils import misc_utils

config: Optional[Config] = None
task_manager: Optional[TaskManager] = None
command_manager: Optional[CommandManager] = None
crontab_manager: Optional[CrontabManager] = None
online_player_counter: Optional[OnlinePlayerCounter] = None
mcdr_globals.load()
init_ok = False

Expand All @@ -37,7 +39,7 @@ def is_enabled() -> bool:


def on_load(server: PluginServerInterface, old):
global config, task_manager, command_manager, crontab_manager
global config, task_manager, command_manager, crontab_manager, online_player_counter
try:
config = server.load_config_simple(target_class=Config, failure_policy='raise')
set_config_instance(config)
Expand All @@ -47,12 +49,16 @@ def on_load(server: PluginServerInterface, old):

DbAccess.init()
__check_config(server)

task_manager = TaskManager()
task_manager.start()
crontab_manager = CrontabManager(task_manager)
crontab_manager.start()
command_manager = CommandManager(server, task_manager, crontab_manager)
online_player_counter = OnlinePlayerCounter(server)

task_manager.start()
crontab_manager.start()
command_manager.register_commands()
online_player_counter.on_load(getattr(old, 'online_player_counter', None))

server.register_help_message(config.command.prefix, mcdr_globals.metadata.get_description_rtext())
except Exception:
Expand Down Expand Up @@ -121,3 +127,18 @@ def on_info(server: PluginServerInterface, info: Info):
if pattern.fullmatch(info.content):
task_manager.on_world_saved()
break


def on_server_start(server: PluginServerInterface):
if init_ok and online_player_counter is not None:
online_player_counter.on_server_start()


def on_player_joined(server: PluginServerInterface, player: str, info: Info):
if init_ok and online_player_counter is not None:
online_player_counter.on_player_joined(player)


def on_player_left(_: PluginServerInterface, player: str):
if init_ok and online_player_counter is not None:
online_player_counter.on_player_left(player)
Loading

0 comments on commit 9472ff4

Please sign in to comment.