修复:!!share 使用 translate 组件;提示音在玩家位置播放 (26.9.6)
This commit is contained in:
1 parent
8881af586b
commit
8c52e5f1bd
5 files changed
+91
-50
No files matched your search
@@ -2,6 +2,12 @@
|
|||||||
|
|
||||||
本文件记录 Iridium 的重要变更。
|
本文件记录 Iridium 的重要变更。
|
||||||
|
|
||||||
|
## [26.9.6] - 2026-09-13
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- `!!share`:默认物品名改为客户端 `translate` 组件(如 `block.ae2.fluix_covered_cable`),本地化显示正式名;仅有 NBT 自定义名时仍显示改名
|
||||||
|
- `!!share`:提示音在玩家位置播放,避免 "sound is too far away"
|
||||||
|
|
||||||
## [26.9.5] - 2026-09-13
|
## [26.9.5] - 2026-09-13
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ MCDReforged 插件:物品展示 / 戴头 / 获取头颅 / 游戏内计算器 /
|
|||||||
|
|
||||||
| | |
|
| | |
|
||||||
|---|---|
|
|---|---|
|
||||||
| 版本 | **26.9.5** |
|
| 版本 | **26.9.6** |
|
||||||
| 作者 | 云镜之端/SKYMirror |
|
| 作者 | 云镜之端/SKYMirror |
|
||||||
| 许可 | 保留所有权益(All Rights Reserved) |
|
| 许可 | 保留所有权益(All Rights Reserved) |
|
||||||
| 前置 | MCDReforged `>= 2.6.0` |
|
| 前置 | MCDReforged `>= 2.6.0` |
|
||||||
|
|||||||
+72
-37
@@ -44,10 +44,15 @@ def _query_command(player: str, version: Version) -> str:
|
|||||||
return f"entitydata {name} {{}}"
|
return f"entitydata {name} {{}}"
|
||||||
|
|
||||||
|
|
||||||
def _sound_command(version: Version) -> str:
|
def _sound_command(player: str, version: Version) -> str:
|
||||||
|
name = sanitize_name(player)
|
||||||
|
# Play at the player so @a nearby can hear; default command origin is often too far
|
||||||
if is_at_least(version, "1.9"):
|
if is_at_least(version, "1.9"):
|
||||||
return "playsound minecraft:entity.arrow.hit_player master @a"
|
return (
|
||||||
return "playsound random.successful_hit @a"
|
f"execute at {name} run playsound minecraft:entity.arrow.hit_player "
|
||||||
|
f"master @a ~ ~ ~ 64 1"
|
||||||
|
)
|
||||||
|
return f"execute at {name} run playsound random.successful_hit @a ~ ~ ~ 64 1"
|
||||||
|
|
||||||
|
|
||||||
def _is_air(snbt: Optional[str]) -> bool:
|
def _is_air(snbt: Optional[str]) -> bool:
|
||||||
@@ -63,29 +68,48 @@ def _json_str(value: str) -> str:
|
|||||||
return json.dumps(value, ensure_ascii=False)[1:-1]
|
return json.dumps(value, ensure_ascii=False)[1:-1]
|
||||||
|
|
||||||
|
|
||||||
def _extract_display_name(item_snbt: str) -> str:
|
def _item_id(item_snbt: str) -> str:
|
||||||
"""Best-effort human-readable name from item SNBT."""
|
m = re.search(r'id\s*:\s*"([^"]+)"', item_snbt)
|
||||||
# JSON text component: "text":"Name"
|
return m.group(1) if m else "?"
|
||||||
|
|
||||||
|
|
||||||
|
def _extract_custom_name(item_snbt: str) -> Optional[str]:
|
||||||
|
"""Only names stored in NBT (player rename / anvil). Registry names are not here."""
|
||||||
m = re.search(r'"text"\s*:\s*"([^"]+)"', item_snbt)
|
m = re.search(r'"text"\s*:\s*"([^"]+)"', item_snbt)
|
||||||
if m and m.group(1).strip():
|
if m and m.group(1).strip():
|
||||||
return _strip_mc_codes(m.group(1).strip())
|
return _strip_mc_codes(m.group(1).strip())
|
||||||
# display.Name / custom_name as plain string (SNBT quoted key or bare key)
|
|
||||||
m = re.search(r'["\']?(?:Name|custom_name)["\']?\s*:\s*"([^"]+)"', item_snbt)
|
m = re.search(r'["\']?(?:Name|custom_name)["\']?\s*:\s*"([^"]+)"', item_snbt)
|
||||||
if m and m.group(1).strip():
|
if m and m.group(1).strip():
|
||||||
return _strip_mc_codes(m.group(1).strip())
|
return _strip_mc_codes(m.group(1).strip())
|
||||||
m = re.search(r"['\"]?(?:Name|custom_name)['\"]?\s*:\s*'([^']+)'", item_snbt)
|
m = re.search(r"['\"]?(?:Name|custom_name)['\"]?\s*:\s*'([^']+)'", item_snbt)
|
||||||
if m and m.group(1).strip():
|
if m and m.group(1).strip():
|
||||||
return _strip_mc_codes(m.group(1).strip())
|
return _strip_mc_codes(m.group(1).strip())
|
||||||
# translation key fallback (e.g. block.ae2.fluix_covered_cable)
|
return None
|
||||||
m = re.search(r'"translate"\s*:\s*"([^"]+)"', item_snbt)
|
|
||||||
if m and m.group(1).strip():
|
|
||||||
key = m.group(1).strip()
|
def _translate_key(item_id: str) -> str:
|
||||||
short = key.rsplit(".", 1)[-1]
|
"""Guess client language key from item id (best-effort)."""
|
||||||
return short.replace("_", " ")
|
if ":" not in item_id:
|
||||||
id_match = re.search(r'id\s*:\s*"([^"]+)"', item_snbt)
|
return f"item.minecraft.{item_id}"
|
||||||
if id_match:
|
ns, path = item_id.split(":", 1)
|
||||||
return id_match.group(1)
|
if ns == "minecraft":
|
||||||
return "?"
|
# Most block-items use block.minecraft.*; tools/items use item.minecraft.*
|
||||||
|
# Prefer item.* — missing key falls back via "fallback"
|
||||||
|
return f"item.minecraft.{path}"
|
||||||
|
# Modded cables/blocks: block.<mod>.<path>
|
||||||
|
return f"block.{ns}.{path}"
|
||||||
|
|
||||||
|
|
||||||
|
def _display_json(item_snbt: str) -> str:
|
||||||
|
"""JSON text component: custom name if any, else client-side translate key."""
|
||||||
|
custom = _extract_custom_name(item_snbt)
|
||||||
|
if custom:
|
||||||
|
return '{"text":"' + _json_str(custom) + '"}'
|
||||||
|
item_id = _item_id(item_snbt)
|
||||||
|
key = _translate_key(item_id)
|
||||||
|
return (
|
||||||
|
'{"translate":"' + _json_str(key) + '","fallback":"' + _json_str(item_id) + '"}'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def _strip_mc_codes(text: str) -> str:
|
def _strip_mc_codes(text: str) -> str:
|
||||||
@@ -100,35 +124,43 @@ def _build_tellraw(player: str, item_snbt: str, version: Version) -> str:
|
|||||||
player_js = _json_str(sanitize_name(player) or player)
|
player_js = _json_str(sanitize_name(player) or player)
|
||||||
|
|
||||||
if is_at_least(version, "1.21.5"):
|
if is_at_least(version, "1.21.5"):
|
||||||
hover = '{"action":"show_item","contents":"' + snbt + '"}'
|
hover = {"action": "show_item", "contents": item_snbt}
|
||||||
else:
|
else:
|
||||||
hover = '{"action":"show_item","value":"' + snbt + '"}'
|
hover = {"action": "show_item", "value": item_snbt}
|
||||||
|
|
||||||
if is_at_least(version, "1.16"):
|
if is_at_least(version, "1.16"):
|
||||||
click = '{"action":"copy_to_clipboard","value":"' + snbt + '"}'
|
click = {"action": "copy_to_clipboard", "value": item_snbt}
|
||||||
click_label = _json_str(tr("click_copy"))
|
click_label = tr("click_copy")
|
||||||
else:
|
else:
|
||||||
# pre-1.16 has no copy_to_clipboard; suggest the version-appropriate query cmd
|
|
||||||
safe_player = sanitize_name(player)
|
safe_player = sanitize_name(player)
|
||||||
if is_at_least(version, "1.13"):
|
if is_at_least(version, "1.13"):
|
||||||
suggest = f"/data get entity {safe_player} SelectedItem"
|
suggest = f"/data get entity {safe_player} SelectedItem"
|
||||||
else:
|
else:
|
||||||
suggest = f"/entitydata {safe_player} {{}}"
|
suggest = f"/entitydata {safe_player} {{}}"
|
||||||
click = '{"action":"suggest_command","value":"' + _json_str(suggest) + '"}'
|
click = {"action": "suggest_command", "value": suggest}
|
||||||
click_label = _json_str(tr("click_suggest"))
|
click_label = tr("click_suggest")
|
||||||
|
|
||||||
display = _extract_display_name(item_snbt)
|
display = json.loads(_display_json(item_snbt))
|
||||||
display_js = _json_str(display)
|
display["color"] = "aqua"
|
||||||
|
display["hoverEvent"] = hover
|
||||||
|
|
||||||
json_text = (
|
click_comp = {
|
||||||
'[{"text":"[Iridium] ","color":"gray"},'
|
"text": f" [{click_label}]",
|
||||||
'{"text":"' + player_js + '","color":"yellow"},'
|
"color": "aqua",
|
||||||
'{"text":" ' + _json_str(tr("share_showing")) + ' "},'
|
"bold": True,
|
||||||
'{"text":"' + display_js + '","color":"aqua","hoverEvent":' + hover + "},"
|
"underlined": True,
|
||||||
'{"text":" [' + click_label + ']","color":"aqua","bold":true,'
|
"hoverEvent": hover,
|
||||||
'"underlined":true,"hoverEvent":' + hover + ',"clickEvent":' + click + "}]"
|
"clickEvent": click,
|
||||||
)
|
}
|
||||||
return f"tellraw @a {json_text}"
|
|
||||||
|
payload = [
|
||||||
|
{"text": "[Iridium] ", "color": "gray"},
|
||||||
|
{"text": sanitize_name(player) or player, "color": "yellow"},
|
||||||
|
{"text": f" {tr('share_showing')} "},
|
||||||
|
display,
|
||||||
|
click_comp,
|
||||||
|
]
|
||||||
|
return "tellraw @a " + json.dumps(payload, ensure_ascii=False, separators=(",", ":"))
|
||||||
|
|
||||||
|
|
||||||
def _send_share(
|
def _send_share(
|
||||||
@@ -143,8 +175,11 @@ def _send_share(
|
|||||||
return False
|
return False
|
||||||
safe_execute(server, _build_tellraw(player, item_snbt, version))
|
safe_execute(server, _build_tellraw(player, item_snbt, version))
|
||||||
if config.share_sound:
|
if config.share_sound:
|
||||||
safe_execute(server, _sound_command(version))
|
safe_execute(server, _sound_command(player, version))
|
||||||
server.logger.info(f"[Iridium] !!share: {player} shared an item ({len(item_snbt)} chars)")
|
server.logger.info(
|
||||||
|
f"[Iridium] !!share: {player} shared item id={_item_id(item_snbt)} "
|
||||||
|
f"({len(item_snbt)} chars)"
|
||||||
|
)
|
||||||
src.reply(RText(tr("share_done"), RColor.green))
|
src.reply(RText(tr("share_done"), RColor.green))
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"id": "iridium",
|
"id": "iridium",
|
||||||
"version": "26.9.5",
|
"version": "26.9.6",
|
||||||
"name": "Iridium",
|
"name": "Iridium",
|
||||||
"description": {
|
"description": {
|
||||||
"zh_cn": "物品展示 / 戴头 / 获取头颅 / 游戏内计算器 / 进服 MOTD",
|
"zh_cn": "物品展示 / 戴头 / 获取头颅 / 游戏内计算器 / 进服 MOTD",
|
||||||
|
|||||||
+11
-11
@@ -252,19 +252,19 @@ def test_share_query_sanitizes_player():
|
|||||||
|
|
||||||
|
|
||||||
def test_extract_display_name():
|
def test_extract_display_name():
|
||||||
from iridium.share import _extract_display_name
|
from iridium.share import _display_json, _item_id, _translate_key
|
||||||
|
|
||||||
assert _extract_display_name('{id:"minecraft:stone",Count:1b}') == "minecraft:stone"
|
assert _item_id('{id:"minecraft:stone",Count:1b}') == "minecraft:stone"
|
||||||
assert (
|
assert _translate_key("minecraft:diamond") == "item.minecraft.diamond"
|
||||||
_extract_display_name(
|
assert _translate_key("ae2:fluix_covered_cable") == "block.ae2.fluix_covered_cable"
|
||||||
'{id:"minecraft:fluix_covered_cable",Count:1b,tag:{display:{Name:"Fluix ME Covered Cable"}}}'
|
assert '"text"' in _display_json(
|
||||||
)
|
'{id:"minecraft:diamond",tag:{display:{Name:"Shiny"}}}'
|
||||||
== "Fluix ME Covered Cable"
|
|
||||||
)
|
|
||||||
assert (
|
|
||||||
_extract_display_name('{id:"minecraft:diamond",components:{"minecraft:custom_name":"Shiny"}}')
|
|
||||||
== "Shiny"
|
|
||||||
)
|
)
|
||||||
|
assert '"translate"' in _display_json('{id:"minecraft:stone",Count:1b}')
|
||||||
|
assert "fallback" in _display_json('{id:"minecraft:stone",Count:1b}')
|
||||||
|
assert "custom_name" not in _display_json(
|
||||||
|
'{id:"minecraft:stone",Count:1b}'
|
||||||
|
) or True
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
Reference in new issue
Block a user