Wire TOML help topics into help command

This commit is contained in:
Jared Miller 2026-02-15 09:48:26 -05:00
parent b69c2e83d9
commit d7698ca830
Signed by: shmup
GPG key ID: 22B5C6D66A38B06C
3 changed files with 89 additions and 2 deletions

View file

@ -4,11 +4,14 @@ from typing import TYPE_CHECKING, cast
from mudlib.commands import CommandDefinition, _registry, register, resolve_prefix from mudlib.commands import CommandDefinition, _registry, register, resolve_prefix
from mudlib.commands.movement import DIRECTIONS from mudlib.commands.movement import DIRECTIONS
from mudlib.content import HelpTopic
from mudlib.player import Player from mudlib.player import Player
if TYPE_CHECKING: if TYPE_CHECKING:
from mudlib.combat.moves import CombatMove from mudlib.combat.moves import CombatMove
_help_topics: dict[str, HelpTopic] = {}
async def _show_command_detail(player: Player, command_name: str) -> None: async def _show_command_detail(player: Player, command_name: str) -> None:
"""Show detailed information about a specific command. """Show detailed information about a specific command.
@ -438,6 +441,15 @@ async def cmd_help(player: Player, args: str) -> None:
) )
return return
# Check TOML help topics first
topic = _help_topics.get(args)
if topic is not None:
if topic.admin and not player.is_admin:
await player.send(f"Unknown command: {args}\r\n")
return
await player.send(f"{topic.title}\r\n{topic.body}\r\n")
return
# Check if this is a help topic (hidden command) # Check if this is a help topic (hidden command)
defn = _registry.get(args) defn = _registry.get(args)
if defn is not None and defn.hidden: if defn is not None and defn.hidden:

View file

@ -42,7 +42,8 @@ import mudlib.commands.use
from mudlib.caps import parse_mtts from mudlib.caps import parse_mtts
from mudlib.combat.commands import register_combat_commands from mudlib.combat.commands import register_combat_commands
from mudlib.combat.engine import process_combat from mudlib.combat.engine import process_combat
from mudlib.content import load_commands from mudlib.commands.help import _help_topics
from mudlib.content import load_commands, load_help_topics
from mudlib.corpse import process_decomposing from mudlib.corpse import process_decomposing
from mudlib.crafting import load_recipes, recipes from mudlib.crafting import load_recipes, recipes
from mudlib.creation import character_creation from mudlib.creation import character_creation
@ -598,6 +599,13 @@ async def run_server() -> None:
mudlib.commands.register(cmd_def) mudlib.commands.register(cmd_def)
log.debug("registered content command: %s", cmd_def.name) log.debug("registered content command: %s", cmd_def.name)
# Load help topics from content/help/
help_dir = pathlib.Path(__file__).resolve().parents[2] / "content" / "help"
if help_dir.exists():
loaded_topics = load_help_topics(help_dir)
_help_topics.update(loaded_topics)
log.info("loaded %d help topics from %s", len(loaded_topics), help_dir)
# Load combat moves and register as commands # Load combat moves and register as commands
combat_dir = pathlib.Path(__file__).resolve().parents[2] / "content" / "combat" combat_dir = pathlib.Path(__file__).resolve().parents[2] / "content" / "combat"
if combat_dir.exists(): if combat_dir.exists():

View file

@ -1,10 +1,14 @@
"""Tests for TOML help topic loading.""" """Tests for TOML help topic loading."""
import textwrap import textwrap
from unittest.mock import AsyncMock, MagicMock
import pytest import pytest
from mudlib.content import load_help_topics from mudlib import commands
from mudlib.commands import help as help_mod # noqa: F401
from mudlib.commands.help import _help_topics
from mudlib.content import HelpTopic, load_help_topics
@pytest.fixture @pytest.fixture
@ -77,3 +81,66 @@ def test_load_help_topics_skips_bad_files(tmp_path):
topics = load_help_topics(tmp_path) topics = load_help_topics(tmp_path)
assert "good" in topics assert "good" in topics
assert "broken" not in topics assert "broken" not in topics
@pytest.fixture
def mock_writer():
writer = MagicMock()
writer.write = MagicMock()
writer.drain = AsyncMock()
return writer
@pytest.fixture
def player(mock_writer):
from mudlib.player import Player
return Player(name="Tester", x=0, y=0, reader=MagicMock(), writer=mock_writer)
@pytest.fixture
def admin_player(mock_writer):
from mudlib.player import Player
p = Player(name="Admin", x=0, y=0, reader=MagicMock(), writer=mock_writer)
p.is_admin = True
return p
@pytest.fixture(autouse=True)
def _clear_topics():
_help_topics.clear()
yield
_help_topics.clear()
@pytest.mark.asyncio
async def test_help_shows_toml_topic(player):
_help_topics["combat"] = HelpTopic(
name="combat", body="fight stuff", title="combat primer"
)
await commands.dispatch(player, "help combat")
output = "".join(c[0][0] for c in player.writer.write.call_args_list)
assert "combat primer" in output
assert "fight stuff" in output
@pytest.mark.asyncio
async def test_help_admin_topic_hidden_from_players(player):
_help_topics["secret"] = HelpTopic(
name="secret", body="hidden", title="secret stuff", admin=True
)
await commands.dispatch(player, "help secret")
output = "".join(c[0][0] for c in player.writer.write.call_args_list)
assert "hidden" not in output
assert "unknown" in output.lower()
@pytest.mark.asyncio
async def test_help_admin_topic_visible_to_admins(admin_player):
_help_topics["secret"] = HelpTopic(
name="secret", body="hidden", title="secret stuff", admin=True
)
await commands.dispatch(admin_player, "help secret")
output = "".join(c[0][0] for c in admin_player.writer.write.call_args_list)
assert "hidden" in output