feat: module mtp_back

This commit is contained in:
2026-01-14 22:34:35 +08:00
parent 4ab2395aeb
commit 0d48cbb022
9 changed files with 236 additions and 0 deletions

13
.gitignore vendored Normal file
View File

@@ -0,0 +1,13 @@
# Python-generated files
__pycache__/
*.py[oc]
build/
dist/
wheels/
*.egg-info
# Virtual environments
.venv
.python-version
.venv
uv.lock

11
back/README.md Normal file
View File

@@ -0,0 +1,11 @@
# MTP Back
这里是返回死亡点模块的命令帮助信息。
## Usage
`!!back` MTP Back的主命令玩家可当作快速的命令别名用于便捷返回。
`!!help back` 显示此命令帮助页面。
`!!back death` 返回死亡点的命令,默认情况下玩家执行时会直接被传送到上次死亡的地点。
`!!back death history` 查看死亡点的历史记录信息。
`!!back return` 返回到上次执行传送相关命令时所在的位置。
`!!reback` 命令`!!back return`的别名。

View File

@@ -0,0 +1,17 @@
{
"id": "mtp_back",
"version": "0.0.1",
"name": "MTP Back",
"description": {
"en_us": "Single back module of ModernTeleport.",
"zh_cn": "现代化传送单模块:返回死亡点。"
},
"author": "Mooling0602",
"link": "https://github.com",
"dependencies": {
"mcdreforged": ">=2.14.1"
},
"resources": [
"lang"
]
}

View File

@@ -0,0 +1,60 @@
from pathlib import Path
import time
import mtp_back.runtime as rt
from mcdreforged.api.all import PluginServerInterface, event_listener
from mtp_back.utils import write_data, execute_if
do_reload: bool = False
loaded: bool = False
def on_load(s: PluginServerInterface, prev_module):
global do_reload
s.logger.info("init.on_load.start")
if prev_module is None:
if not s.is_server_running():
s.logger.info("init.need_reload")
else:
loader(s)
else:
if prev_module.do_reload:
loader(s)
s.logger.info("init.on_load.finish")
do_reload = True
def loader(s: PluginServerInterface):
global loaded
if "gamemode" in s.get_plugin_list():
rt.command_node = "mtp:back"
from mtp_back.commands import command_register
command_register(s)
_config_dir: str = s.get_data_folder()
rt.config_dir = Path(_config_dir)
loaded = True
def on_server_start_pre(s: PluginServerInterface):
if not loaded:
s.reload_plugin(s.get_self_metadata().id)
@event_listener("PlayerDeathEvent")
@execute_if(lambda: loaded is True)
def on_player_death(s: PluginServerInterface, player: str, event: str, content: list):
message: str = ""
for i in content:
if i.locale == s.get_mcdr_language():
message = i.raw
death_info: dict = {
"player": player,
"event": event,
"message": message,
"location": "not implemented yet.",
}
timestamp = time.time()
data = {"info": death_info, "time": timestamp}
config_file_path: Path = rt.config_dir / "data.json"
write_data(data, config_file_path)

View File

@@ -0,0 +1,51 @@
from typing import Callable, Literal
import mtp_back.runtime as rt
from mcdreforged.api.all import (
CommandContext,
CommandSource,
PluginServerInterface,
SimpleCommandBuilder,
)
builder = SimpleCommandBuilder()
CommandSourceType = Literal["admin", "player", "console"]
def command_register(s: PluginServerInterface):
builder.register(s)
def command_checker(
src: CommandSource,
_: CommandContext,
mode: list[CommandSourceType] | CommandSourceType,
) -> bool:
if not isinstance(mode, list):
mode = [mode]
check_map: dict[str, Callable[[], bool]] = {
"admin": lambda: src.has_permission_higher_than(2),
"player": lambda: src.is_player,
"console": lambda: src.is_console,
}
return all(check_map[m]() for m in mode if m in check_map)
@builder.command(f"{rt.command_pfx}{rt.command_node}")
def on_command_back_main(src: CommandSource, ctx: CommandContext):
if command_checker(src, ctx, "player"):
player_command_back_main(src, ctx)
return
if command_checker(src, ctx, "console"):
show_console_back_main_help(src, ctx)
return
def player_command_back_main(src: CommandSource, _: CommandContext):
src.reply("back.auto_detect")
src.reply("feature is not implemented.")
src.reply("back.execute")
def show_console_back_main_help(src: CommandSource, _: CommandContext):
src.reply("back.help_page_console")

View File

@@ -0,0 +1,4 @@
# from mcdreforged.api.all import RText
HELP_PAGE_MAIN = ""

View File

@@ -0,0 +1,7 @@
from pathlib import Path
# Command build part
command_pfx: str = "!!"
command_node: str = "back"
config_dir: Path = Path("mtp_back")

View File

@@ -0,0 +1,64 @@
import json
from typing import Callable, TypeVar, ParamSpec
from functools import wraps
from pathlib import Path
from mcdreforged.api.all import PluginServerInterface
P = ParamSpec("P")
T = TypeVar("T")
class ConditionError(RuntimeError):
pass
def execute_if(condition: bool | Callable[[], bool], raise_error: bool = False):
def decorator(func: Callable[P, T]) -> Callable[P, T | None]:
@wraps(func)
def wrapper(*args: P.args, **kwargs: P.kwargs) -> T | None:
_condition: bool = condition() if callable(condition) else condition
if _condition:
return func(*args, **kwargs)
else:
if raise_error:
raise ConditionError("Condition must be satisfied!")
return None
return wrapper
return decorator
def write_data(data: dict, path: str | Path):
file_path: Path = Path(path)
file_path.parent.mkdir(parents=True, exist_ok=True)
all_data: list[dict] = []
if not file_path.exists():
all_data.append(data)
else:
try:
if file_path.stat().st_size != 0:
with open(file_path, "r", encoding="utf-8") as f:
all_data = json.load(f)
except Exception as e:
raise e
all_data.append(data)
with open(file_path, "w", encoding="utf-8") as f:
json.dump(all_data, f, indent=2, ensure_ascii=False)
def tr(server: PluginServerInterface, tr_key: str, return_str: bool = True, *args):
plg_self = server.get_self_metadata()
if tr_key.startswith(f"{plg_self.id}"):
translation = server.rtr(f"{tr_key}")
else:
if tr_key.startswith("#"):
translation = server.rtr(tr_key.replace("#", ""), *args)
else:
translation = server.rtr(f"{plg_self.id}.{tr_key}", *args)
if return_str:
tr_to_str: str = str(translation)
return tr_to_str
else:
return translation

9
pyproject.toml Normal file
View File

@@ -0,0 +1,9 @@
[project]
name = "tp-plugins"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.11"
dependencies = [
"mcdreforged>=2.15.7",
]