4 Commits
11 changed files with 462 additions and 134 deletions

No files matched your search

+107 -34
View File
@@ -1,69 +1,142 @@
# Changelog
# Changelog / 更新日志
本文件记录 Iridium 的重要变更。
This file records notable changes to Iridium.
---
## [26.9.16] - 2026-09-14
### 变更 / Changed
- 配置格式改为 **YAML**:`config/iridium/iridium.yml`(`#` 原生注释)
- Config format is now **YAML**: `config/iridium/iridium.yml` (native `#` comments)
- 自动从旧 JSON(`config/iridium.json` 或 `config/iridium/iridium.json`)迁移
- Auto-migrates from legacy JSON configs
---
## [26.9.15] - 2026-09-14
### 修复 / Fixed
- 配置文件写到错误路径导致「删掉后重载不生成」:改用 MCDR 数据目录 `config/iridium/iridium.json`(`get_data_folder`)
- Config written to the wrong path so delete+reload did not recreate it; now uses MCDR data folder `config/iridium/iridium.json`
### 变更 / Changed
- 文档中的配置路径更新为 `config/iridium/iridium.json`
- Docs updated to the correct config path
---
## [26.9.14] - 2026-09-14
### 修复 / Fixed
- 删除 `config/iridium.json` 后重载可正确重新生成(多路径探测 + 写入失败日志)
- Deleting `config/iridium.json` then reloading correctly recreates the file (multi-path probe + write-failure logs)
### 变更 / Changed
- `motd_start_day` 默认为配置生成当天(YYYY-MM-DD),不再为空
- `motd_start_day` defaults to the config creation date (`YYYY-MM-DD`), no longer empty
- 去掉 MOTD 标题「服务器信息」/「Server Info」
- Removed the MOTD header title (`服务器信息` / `Server Info`)
- 默认 QQ 群增加可点击链接 `https://skymirror.top/qq`
- Default QQ group line is now a clickable link to `https://skymirror.top/qq`
- 进服命令提示文案去掉「已启用 Iridium」
- Join tip header no longer says “Iridium is enabled”
### 新增 / Added
- 英文文档 `README_en.md`
- English documentation `README_en.md`
---
## [26.9.13] - 2026-09-13
### Added
### 新增 / Added
- 首次启动自动生成带中文注释的 `config/iridium.json`
- On first load, generate a commented `config/iridium.json`
- 配置文件支持 `//` 行注释
- Config file supports `//` line comments
### Changed
### 变更 / Changed
- 提交信息改为中文
- Commit messages rewritten in Chinese
- 清理历史 Release,仅保留当前版本
- Cleaned old Releases; keep only the current version
---
## [26.9.8] - 2026-09-13
### Changed
- `!!share`:去掉「点击复制完整数据」,仅保留物品名 + 悬停预览(与 ShowIt 风格一致)
### 变更 / Changed
- `!!share`:去掉「点击复制完整数据」,仅保留物品名 + 悬停预览
- `!!share`: removed the copy-to-clipboard button; name + hover only
---
## [26.9.7] - 2026-09-13
### Fixed
- `!!share`:无 NBT 改名时,正文 fallback 改为 ID 美化名(如 `Time Twister Wireless`),不再直接显示 `kubejs:...`;语言键命中则显示正式本地化名
### 修复 / Fixed
- `!!share`:无 NBT 改名时 fallback 为 ID 美化名(如 `Time Twister Wireless`)
- `!!share`: humanized ID fallback when the item has no NBT custom name
---
## [26.9.6] - 2026-09-13
### Fixed
- `!!share`:默认物品名改为客户端 `translate` 组件(如 `block.ae2.fluix_covered_cable`),本地化显示正式名;仅有 NBT 自定义名时仍显示改名
- `!!share`:提示音在玩家位置播放,避免 "sound is too far away"
### 修复 / Fixed
- `!!share`:使用客户端 `translate` 组件显示本地化名
- `!!share`: use client `translate` component for localized names
- `!!share`:提示音在玩家位置播放
- `!!share`: play sound at the player to avoid “too far away”
---
## [26.9.5] - 2026-09-13
### Fixed
- `!!hat`(1.17+):改用副手缓冲做真正交换,避免复杂 NBT 重建失败导致原头盔消失;副手非空时拒绝并提示
- `!!share`:展示名优先取 `custom_name` / `display.Name` / translate,不再只显示物品 ID
### 修复 / Fixed
- `!!hat`(1.17+):副手缓冲交换,避免复杂 NBT 重建失败导致物品消失
- `!!hat` (1.17+): offhand-buffer swap so complex NBT cannot wipe items
- `!!share`:优先显示自定义名 / translate
- `!!share`: prefer custom name / translate over raw item id
---
## [26.9.4] - 2026-09-13
### Added
- 操作日志:`!!share` / `!!hat` / `!!head` / `!!c` 成功时写入 MCDR 控制台(`[Iridium] ...`)
### 新增 / Added
- 操作日志写入 MCDR 控制台
- Command operations logged to the MCDR console
---
## [26.9.3] - 2026-09-13
### Fixed
- `!!share` / `!!hat` / `!!head`:`info.version` 若为字符串时比较崩溃(`Cannot compare Version and str`)
- `!!c`:命令回调少绑定 `server` 导致 `do_calc() missing ... context`
### 修复 / Fixed
- 版本比较在 `str` / `Version` 之间崩溃
- Version comparison crash between `str` and `Version`
- `!!c` 回调缺少 `server` 绑定
- `!!c` callback missing `server` binding
---
## [26.9.2] - 2026-09-13
### Fixed
- 语言文件 YAML 解析失败(值中含 `:` 未加引号导致 `mapping values are not allowed`)
- 进服事件名错误:`PLAYER_JOIN` → `PLAYER_JOINED`(否则插件 on_load 失败)
### 修复 / Fixed
- 语言文件 YAML 因值中冒号未加引号而解析失败
- Language YAML failed when values contained unquoted colons
- 进服事件名:`PLAYER_JOIN` → `PLAYER_JOINED`
- Join event name: `PLAYER_JOIN` → `PLAYER_JOINED`
---
## [26.9.1] - 2026-09-13
### Added
- `!!share`:展示主手物品(悬停完整数据,1.16+ 点击复制,可选提示音)
- `!!hat`:主手物品与头部装备互换(需 RCON)
- `!!head [player]`:获取一个玩家头颅,支持冷却与权限
- `!!c <expression>`:游戏内四则运算计算器(AST 白名单,零依赖)
- 进服 MOTD:开服天数、自定义文案、可点击链接、`§` 颜色
- 进服命令提示(可点击填入聊天框)
- 按 Minecraft 版本分支;不支持的功能会明确提示
### 新增 / Added
- `!!share` / `!!hat` / `!!head` / `!!c`、进服 MOTD 与命令提示
- `!!share` / `!!hat` / `!!head` / `!!c`, join MOTD and command tips
- 按 Minecraft 版本分支;不支持的功能会提示
- Minecraft-version branching with clear prompts when unsupported
### Security
- 玩家名统一清洗为 `[A-Za-z0-9_]` 并限长后再拼进 `/give`、`data get`、`item replace`
- 控制台命令去除换行,防止拆成多行二次执行
- tellraw 对物品 SNBT 做 JSON 转义与长度截断
- MOTD 链接仅允许 `http://` / `https://`
### 安全 / Security
- 玩家名清洗、命令去换行、tellraw 转义、链接仅 http(s)
- Name sanitization, newline stripping, tellraw escaping, http(s)-only links
+18 -21
View File
@@ -4,7 +4,7 @@ MCDReforged 插件:物品展示 / 戴头 / 获取头颅 / 游戏内计算器 /
| | |
|---|---|
| 版本 | **26.9.13** |
| 版本 | **26.9.16** |
| 作者 | 云镜之端/SKYMirror |
| 许可 | 保留所有权益(All Rights Reserved) |
| 前置 | MCDReforged `>= 2.6.0` |
@@ -79,26 +79,23 @@ rcon.password=your_password
## 配置
路径:`config/iridium.json`(首次加载自动生成)
路径:`config/iridium/iridium.yml`(YAML,`#` 注释;首次加载自动生成;旧的 `config/iridium.json` / `config/iridium/iridium.json` 会自动迁移)
```json
{
"permission": 1,
"head_cooldown_seconds": 60,
"hat_cooldown_seconds": 3,
"share_sound": true,
"force_version": "",
"join_tip": true,
"join_tip_delay_seconds": 1.5,
"motd_enabled": true,
"motd_start_day": "",
"motd_lines": [
"§6欢迎 §e{player}§6 加入服务器!",
"§7服务器已开服 §b{days}§7 天",
"§7官网: {link:§b点击打开官网|https://www.skymirror.top}",
"§7QQ群: §f985402607"
]
}
```yaml
permission: 1
head_cooldown_seconds: 60
hat_cooldown_seconds: 3
share_sound: true
force_version: ""
join_tip: true
join_tip_delay_seconds: 1.5
motd_enabled: true
motd_start_day: "2026-09-14"
motd_lines:
- "§6欢迎 §e{player}§6 加入服务器!"
- "§7服务器已开服 §b{days}§7 天"
- "§7官网: {link:§b点击打开官网|https://www.skymirror.top}"
- "§7QQ群: {link:§f985402607|https://skymirror.top/qq}"
```
| 字段 | 默认 | 说明 |
@@ -111,7 +108,7 @@ rcon.password=your_password
| `join_tip` | `true` | 进服是否提示可用命令 |
| `join_tip_delay_seconds` | `1.5` | 进服消息延迟;`0` 立即 |
| `motd_enabled` | `true` | 是否发送 MOTD |
| `motd_start_day` | `""` | 开服日期 `YYYY-MM-DD`(当天算第 1 天);留空不显示天数 |
| `motd_start_day` | 生成配置当天 | 开服日期 `YYYY-MM-DD`(当天算第 1 天);可改成真实开服日 |
| `motd_lines` | 见上 | MOTD 文本行 |
修改配置后执行 `!!MCDR reload plugin` 生效。
+95
View File
@@ -0,0 +1,95 @@
# Iridium
MCDReforged plugin: item share / wear on head / player head / calculator / join MOTD.
| | |
|---|---|
| Version | **26.9.16** |
| Author | 云镜之端/SKYMirror |
| License | All Rights Reserved |
| Requires | MCDReforged `>= 2.6.0` |
| Minecraft | **1.7.10+** (feature-gated by version) |
| Python deps | None |
## Features
| Command | Description |
|---------|-------------|
| `!!share` | Share held item: name in chat, hover for full data |
| `!!hat` | Swap main-hand item with head slot |
| `!!head [player]` | Get **one** player head (default: yourself); cooldown |
| `!!c <expression>` | In-game calculator: `+ - * / // % ** ^` |
| Join MOTD | Uptime days, custom lines, clickable links, `§` colors |
| Join tips | Command list; click fills chat |
### Version support
| Feature | 1.7.10 | 1.8–1.12 | 1.13–1.16 | 1.17–1.20.4 | 1.20.5+ |
|---------|:------:|:--------:|:---------:|:-----------:|:-------:|
| `!!c` | ✅ | ✅ | ✅ | ✅ | ✅ |
| `!!head` | ✅ | ✅ | ✅ | ✅ | ✅ profile |
| `!!share` | ❌ | ✅ entitydata | ✅ data get | ✅ | ✅ |
| `!!hat` | ❌ | ✅ RCON | ✅ RCON | ✅ RCON | ✅ RCON |
| MOTD / tips | ✅ | ✅ | ✅ | ✅ | ✅ |
## Install
1. Download `Iridium.mcdr`
2. Put it in MCDR `plugins/`
3. `!!MCDR reload plugin` or restart MCDR
### RCON (required for `!!hat`)
```properties
enable-rcon=true
rcon.port=25575
rcon.password=your_password
```
## Config
Path: `config/iridium/iridium.yml` (YAML with `#` comments; auto-created on first load; migrates legacy JSON configs)
| Field | Default | Description |
|------|---------|-------------|
| `permission` | `1` | Min permission for commands |
| `head_cooldown_seconds` | `60` | `!!head` cooldown; `0` = none |
| `hat_cooldown_seconds` | `3` | `!!hat` cooldown; `0` = none |
| `share_sound` | `true` | Sound on `!!share` |
| `force_version` | `""` | Force MC version; empty = auto |
| `join_tip` | `true` | Command tips on join |
| `join_tip_delay_seconds` | `1.5` | Join message delay |
| `motd_enabled` | `true` | Send join MOTD |
| `motd_start_day` | today | Server start date `YYYY-MM-DD` (day 1 = that date) |
| `motd_lines` | see file | MOTD lines with placeholders |
Default `motd_lines` (QQ group is clickable):
```text
§6Welcome §e{player}§6 !
§7Server has been open for §b{days}§7 days
§7Website: {link:§bOpen website|https://www.skymirror.top}
§7QQ: {link:§f985402607|https://skymirror.top/qq}
```
### MOTD placeholders
| Token | Meaning |
|-------|---------|
| `{player}` | Joining player |
| `{days}` / `{day}` | Days since `motd_start_day` |
| `{online}` | Online count or `?` |
| `{link:text\|https://url}` | Clickable link |
| `{link:https://url}` | Clickable URL |
| `§` | Vanilla color/style codes |
## Security
- Calculator: AST whitelist, no arbitrary Python
- Player names sanitized to `[A-Za-z0-9_]` before game commands
- Newlines stripped from console commands
- MOTD links limited to `http(s)` only
## Copyright
Copyright (c) 2026 云镜之端/SKYMirror. All Rights Reserved. See `LICENSE`.
+23
View File
@@ -0,0 +1,23 @@
## Plugin
- ID: `iridium`
- Name: Iridium
- Repository: https://github.com/HzSKYMirror/Iridium
- Release: https://github.com/HzSKYMirror/Iridium/releases/tag/v26.9.13
- Labels: `tool`
## Summary
- `!!share`:展示主手物品(悬停完整数据)
- `!!hat`:主手与头部装备互换(需 RCON)
- `!!head [player]`:获取一个玩家头颅
- `!!c <expression>`:游戏内四则运算
- 进服 MOTD 与命令提示
面向 Minecraft 1.7.10+,按版本分支;需要 MCDR >= 2.6.0。
打包插件为 `.mcdr`,GitHub Release 已附带成品。
---
- [x] 我已阅读并检查,此 PR 符合 [贡献指南](https://github.com/MCDReforged/PluginCatalogue/blob/master/CONTRIBUTING_zh_cn.md) 中的要求
I have read and checked that my PR meets the requirements in the [Contributing Guidelines](https://github.com/MCDReforged/PluginCatalogue/blob/master/CONTRIBUTING.md)
+1 -1
View File
@@ -12,6 +12,6 @@
"labels": ["tool"],
"introduction": {
"zh_cn": "README.md",
"en_us": "README.md"
"en_us": "README_en.md"
}
}
+186 -62
View File
@@ -1,10 +1,21 @@
import json
import re
import shutil
from datetime import date
from io import StringIO
from pathlib import Path
from typing import List, Optional
from typing import Any, List, Optional
from mcdreforged.api.all import *
try:
from ruamel.yaml import YAML
except ImportError: # pragma: no cover
YAML = None # type: ignore
def _today() -> str:
return date.today().strftime("%Y-%m-%d")
class Config(Serializable):
# 使用 !!share / !!hat / !!head / !!c 所需的最低权限等级
@@ -23,70 +34,76 @@ class Config(Serializable):
join_tip_delay_seconds: float = 1.5
# 是否发送进服 MOTD(开服天数 + 自定义行)
motd_enabled: bool = True
# 开服日期,格式 YYYY-MM-DD,用于计算开服天数;留空则不显示天数
motd_start_day: str = ""
# MOTD 文本行。支持:
# {player} 玩家名
# {days} 开服天数
# {online} 在线人数(拿不到时显示 ?)
# {link:显示文字|https://url} 可点击链接
# {link:https://url} 链接,显示为完整 URL
# § 颜色代码
# 开服日期,格式 YYYY-MM-DD;默认为配置生成当天(当天算第 1 天)
motd_start_day: str = _today()
# MOTD 文本行
motd_lines: List[str] = [
"§6欢迎 §e{player}§6 加入服务器!",
"§7服务器已开服 §b{days}§7 天",
"§7官网: {link:§b点击打开官网|https://www.skymirror.top}",
"§7QQ群: §f985402607",
"§7QQ群: {link:§f985402607|https://skymirror.top/qq}",
]
CONFIG_FILE = "config/iridium.json"
# 当前配置:MCDR 数据目录 → config/iridium/iridium.yml
CONFIG_FILE_NAME = "iridium.yml"
# 历史路径(仅用于迁移)
LEGACY_JSON_NAMES = (
"iridium.json", # config/iridium/iridium.json
)
# 首次生成时写入带注释的 JSON(MCDR 本身不写注释)
DEFAULT_CONFIG_TEXT = """{
// 使用 !!share / !!hat / !!head / !!c 所需的最低权限等级
// 0=控制台 1=普通玩家 2=OP/管理员
"permission": 1,
// !!head 冷却时间(秒),0 表示不限制
"head_cooldown_seconds": 60,
def build_default_config_text() -> str:
"""带注释的默认 YAML 配置;开服日期为生成当天。"""
today = _today()
return f"""# Iridium 配置文件 (YAML)
# 修改后执行: !!MCDR reload plugin
# This file uses standard YAML. '#' starts a comment.
// !!hat 冷却时间(秒),0 表示不限制
"hat_cooldown_seconds": 3,
# 使用 !!share / !!hat / !!head / !!c 所需的最低权限等级
# 0=控制台 1=普通玩家 2=OP/管理员
permission: 1
// !!share 是否播放提示音
"share_sound": true,
# !!head 冷却时间(秒),0 表示不限制
head_cooldown_seconds: 60
// 强制指定 Minecraft 版本(如 "1.12.2"、"1.20.1")
// 留空则自动检测;检测失败时可手动填写
"force_version": "",
# !!hat 冷却时间(秒),0 表示不限制
hat_cooldown_seconds: 3
// 玩家进服时是否提示可用命令
"join_tip": true,
# !!share 是否播放提示音
share_sound: true
// 进服消息延迟(秒),0 为立即发送
"join_tip_delay_seconds": 1.5,
# 强制指定 Minecraft 版本(如 "1.12.2"、"1.20.1")
# 留空则自动检测;检测失败时可手动填写
force_version: ""
// 是否发送进服 MOTD(开服天数 + 自定义文案)
"motd_enabled": true,
# 玩家进服时是否提示可用命令
join_tip: true
// 开服日期,格式 YYYY-MM-DD(当天算第 1 天)
// 留空则不显示开服天数
"motd_start_day": "",
# 进服消息延迟(秒),0 为立即发送
join_tip_delay_seconds: 1.5
// MOTD 文本行
// 占位符: {player} 玩家名 {days} 开服天数 {online} 在线人数
// 链接: {link:显示文字|https://url} 或 {link:https://url}
// 颜色: 支持原版 § 颜色代码
"motd_lines": [
"§6欢迎 §e{player}§6 加入服务器!",
"§7服务器已开服 §b{days}§7 天",
"§7官网: {link:§b点击打开官网|https://www.skymirror.top}",
"§7QQ群: §f985402607"
]
}
# 是否发送进服 MOTD(开服天数 + 自定义文案)
motd_enabled: true
# 开服日期,格式 YYYY-MM-DD(当天算第 1 天)
# 默认为本配置文件生成当天,可自行修改为真实开服日
motd_start_day: "{today}"
# MOTD 文本行
# 占位符: {{player}} 玩家名 {{days}} 开服天数 {{online}} 在线人数
# 链接: {{link:显示文字|https://url}} 或 {{link:https://url}}
# 颜色: 支持原版 § 颜色代码
motd_lines:
- "§6欢迎 §e{{player}}§6 加入服务器!"
- "§7服务器已开服 §b{{days}}§7 天"
- "§7官网: {{link:§b点击打开官网|https://www.skymirror.top}}"
- "§7QQ群: {{link:§f985402607|https://skymirror.top/qq}}"
"""
DEFAULT_CONFIG_TEXT = build_default_config_text()
# 模块级单例;on_load 时原地更新字段,避免各模块持有过期引用
config = Config()
@@ -104,8 +121,26 @@ def apply_config(new_cfg: Config) -> None:
config.motd_lines = list(new_cfg.motd_lines)
def _yaml():
if YAML is None:
raise RuntimeError("ruamel.yaml is required")
return YAML(typ="safe")
def _load_yaml_text(text: str) -> Any:
return _yaml().load(StringIO(text))
def _dump_yaml(data: dict) -> str:
buf = StringIO()
y = _yaml()
y.default_flow_style = False
y.dump(data, buf)
return buf.getvalue()
def _strip_json_comments(text: str) -> str:
"""Remove // line comments outside of strings."""
"""Remove // line comments outside of strings (legacy JSON only)."""
out = []
i = 0
n = len(text)
@@ -136,22 +171,111 @@ def _strip_json_comments(text: str) -> str:
return "".join(out)
def _config_path(server: PluginServerInterface) -> Path:
wd = Path(server.get_mcdr_config()["working_directory"])
return wd / CONFIG_FILE
def config_file_path(server: PluginServerInterface) -> Path:
"""Canonical path: MCDR data folder → config/iridium/iridium.yml"""
return Path(server.get_data_folder()) / CONFIG_FILE_NAME
def _working_dir(server: PluginServerInterface) -> Path:
try:
wd = server.get_mcdr_config().get("working_directory") or "."
return Path(wd).expanduser()
except Exception:
return Path.cwd()
def _legacy_json_paths(server: PluginServerInterface) -> List[Path]:
data_dir = Path(server.get_data_folder())
wd = _working_dir(server)
return [
data_dir / "iridium.json",
wd / "config" / "iridium.json",
]
def _write_default(path: Path, server: PluginServerInterface) -> bool:
try:
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(build_default_config_text(), encoding="utf-8")
server.logger.info(f"已生成配置文件: {path}")
return True
except Exception as e:
server.logger.error(f"写入配置文件失败 {path}: {e}")
return False
def _try_load_dict_from_text(text: str) -> Optional[dict]:
text = text.strip()
if not text:
return None
if text.startswith("{"):
return json.loads(_strip_json_comments(text))
data = _load_yaml_text(text)
return data if isinstance(data, dict) else None
def _migrate_legacy_json(server: PluginServerInterface, dest: Path) -> None:
"""Convert old JSON config (with optional // comments) to YAML if dest is missing."""
if dest.is_file():
return
for src in _legacy_json_paths(server):
try:
if not src.is_file():
continue
raw = src.read_text(encoding="utf-8")
data = _try_load_dict_from_text(raw)
if not data:
continue
dest.parent.mkdir(parents=True, exist_ok=True)
# keep comments in default template + overlay known keys via dump
cfg = Config.deserialize(data)
overlay = {
"permission": cfg.permission,
"head_cooldown_seconds": cfg.head_cooldown_seconds,
"hat_cooldown_seconds": cfg.hat_cooldown_seconds,
"share_sound": cfg.share_sound,
"force_version": cfg.force_version,
"join_tip": cfg.join_tip,
"join_tip_delay_seconds": cfg.join_tip_delay_seconds,
"motd_enabled": cfg.motd_enabled,
"motd_start_day": cfg.motd_start_day,
"motd_lines": list(cfg.motd_lines),
}
# Prefer commented default template when values match defaults is complex;
# dump parsed values with a short header for clarity.
header = (
"# Iridium 配置 (从旧 JSON 迁移)\n"
"# Migrated from legacy JSON config\n\n"
)
dest.write_text(header + _dump_yaml(overlay), encoding="utf-8")
server.logger.info(f"已从旧 JSON 迁移配置: {src} -> {dest}")
return
except Exception as e:
server.logger.warning(f"迁移旧配置失败 {src}: {e}")
def load_config(server: PluginServerInterface) -> Config:
"""Load config/iridium.json; create a commented default on first run."""
path = _config_path(server)
path.parent.mkdir(parents=True, exist_ok=True)
if not path.exists():
path.write_text(DEFAULT_CONFIG_TEXT, encoding="utf-8")
server.logger.info(f"已生成配置文件: {CONFIG_FILE}")
raw = path.read_text(encoding="utf-8")
try:
data = json.loads(_strip_json_comments(raw))
except json.JSONDecodeError as e:
server.logger.error(f"配置文件 JSON 解析失败,使用默认值: {e}")
"""
Load config/iridium/iridium.yml (MCDR data folder).
Create commented default if missing; migrate legacy JSON if needed.
"""
path = config_file_path(server)
_migrate_legacy_json(server, path)
if not path.is_file():
_write_default(path, server)
if not path.is_file():
server.logger.warning(f"配置文件不存在且无法创建,使用内存默认值: {path}")
return Config()
try:
raw = path.read_text(encoding="utf-8")
data = _load_yaml_text(raw)
if not isinstance(data, dict):
raise ValueError("YAML root must be a mapping")
server.logger.info(f"配置已加载: {path}")
return Config.deserialize(data)
except Exception as e:
server.logger.error(f"读取/解析 YAML 配置失败 {path}: {e},使用默认值")
return Config()
+3 -4
View File
@@ -175,10 +175,9 @@ def build_motd(player: str, server: ServerInterface) -> Optional[RTextList]:
lines = list(config.motd_lines or [])
if not lines:
return None
parts: List[RText] = [
RText(tr("motd_header"), RColor.gold, styles=[RStyle.bold])
]
for line in lines:
parts: List[RText] = []
for i, line in enumerate(lines):
if i:
parts.append(RText("\n"))
parts.extend(render_motd_line(line, player, server))
# Only auto-append the day footer when no line already uses {days}/{day}
+1 -2
View File
@@ -27,7 +27,6 @@ iridium.calc_help: 'Evaluate an expression, e.g. !!c 1+2*(3-1)'
iridium.calc_usage: 'Usage: !!c <expression>, e.g. !!c 1+2*(3-1)'
iridium.calc_error: 'Cannot calculate {0}: {1}'
iridium.calc_div_zero: 'Division by zero'
iridium.join_tip_header: 'Iridium is enabled. Available commands: (click to fill chat)'
iridium.join_tip_header: 'Available commands: (click to fill chat)'
iridium.join_tip_click: 'click to fill chat'
iridium.motd_header: 'Server Info'
iridium.motd_days: 'Day {0} since server start'
+1 -2
View File
@@ -27,7 +27,6 @@ iridium.calc_help: '计算表达式,例如 !!c 1+2*(3-1)'
iridium.calc_usage: '用法: !!c <表达式>,例如 !!c 1+2*(3-1)'
iridium.calc_error: '无法计算 {0}: {1}'
iridium.calc_div_zero: '除数不能为 0'
iridium.join_tip_header: '本服已启用 Iridium,可用命令:(点击命令可填入聊天框)'
iridium.join_tip_header: '可用命令:(点击命令可填入聊天框)'
iridium.join_tip_click: '点击填入聊天栏'
iridium.motd_header: '服务器信息'
iridium.motd_days: '开服第 {0} 天'
+1 -1
View File
@@ -1,6 +1,6 @@
{
"id": "iridium",
"version": "26.9.13",
"version": "26.9.16",
"name": "Iridium",
"description": {
"zh_cn": "物品展示 / 戴头 / 获取头颅 / 游戏内计算器 / 进服 MOTD",
+26 -7
View File
@@ -1,6 +1,7 @@
"""Pure-function unit tests (no live MCDR server required)."""
import sys
import json
import os
import sys
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
@@ -268,17 +269,35 @@ def test_extract_display_name():
def test_strip_json_comments():
import json
from datetime import date
from iridium.config import DEFAULT_CONFIG_TEXT, _strip_json_comments
from iridium.config import _strip_json_comments, build_default_config_text
parsed = json.loads(_strip_json_comments(DEFAULT_CONFIG_TEXT))
assert parsed["permission"] == 1
assert "skymirror.top" in parsed["motd_lines"][2]
# comment-looking text inside string is kept
# legacy JSON comment stripper still works
assert json.loads(_strip_json_comments('{"a": 1 // x\n}'))["a"] == 1
assert json.loads(_strip_json_comments('{"a": "http://x"}'))["a"] == "http://x"
def test_default_yaml_config():
from datetime import date
from iridium.config import _load_yaml_text, build_default_config_text
data = _load_yaml_text(build_default_config_text())
assert data["permission"] == 1
assert data["motd_start_day"] == date.today().strftime("%Y-%m-%d")
assert any("skymirror.top" in line for line in data["motd_lines"])
assert any("skymirror.top/qq" in line for line in data["motd_lines"])
def test_legacy_json_to_yaml_parse():
from iridium.config import _try_load_dict_from_text
assert _try_load_dict_from_text('{"permission": 2}')["permission"] == 2
assert _try_load_dict_from_text('{\n // c\n "permission": 3\n} ')["permission"] == 3
assert _try_load_dict_from_text("permission: 4\nmotd_start_day: '2020-01-01'\n")["permission"] == 4
if __name__ == "__main__":
fns = [v for k, v in sorted(globals().items()) if k.startswith("test_") and callable(v)]
failed = 0