Implement collision detection in register

This commit is contained in:
Jared Miller 2026-02-08 13:33:19 -05:00
parent 5efdbaf4e6
commit 7d3b02f6ff
Signed by: shmup
GPG key ID: 22B5C6D66A38B06C

View file

@ -1,10 +1,13 @@
"""Command registry and dispatcher.""" """Command registry and dispatcher."""
import logging
from collections.abc import Awaitable, Callable from collections.abc import Awaitable, Callable
from dataclasses import dataclass, field from dataclasses import dataclass, field
from mudlib.player import Player from mudlib.player import Player
logger = logging.getLogger(__name__)
# Type alias for command handlers # Type alias for command handlers
CommandHandler = Callable[[Player, str], Awaitable[None]] CommandHandler = Callable[[Player, str], Awaitable[None]]
@ -31,8 +34,32 @@ def register(defn: CommandDefinition) -> None:
Args: Args:
defn: The command definition to register defn: The command definition to register
""" """
# Check for collision on the main name
if defn.name in _registry:
existing = _registry[defn.name]
if existing is not defn:
logger.warning(
"Command collision: '%s' already registered for '%s', "
"overwriting with '%s'",
defn.name,
existing.name,
defn.name,
)
_registry[defn.name] = defn _registry[defn.name] = defn
# Check for collisions on each alias
for alias in defn.aliases: for alias in defn.aliases:
if alias in _registry:
existing = _registry[alias]
if existing is not defn:
logger.warning(
"Command collision: '%s' already registered for '%s', "
"overwriting with '%s'",
alias,
existing.name,
defn.name,
)
_registry[alias] = defn _registry[alias] = defn
@ -53,13 +80,37 @@ async def dispatch(player: Player, raw_input: str) -> None:
command = parts[0].lower() command = parts[0].lower()
args = parts[1] if len(parts) > 1 else "" args = parts[1] if len(parts) > 1 else ""
# Look up the definition # Look up the definition - exact match first (fast path)
defn = _registry.get(command) defn = _registry.get(command)
if defn is None: if defn is None:
# Try prefix matching
prefix_matches = [key for key in _registry if key.startswith(command)]
# Deduplicate by CommandDefinition identity
unique_defns = {}
for key in prefix_matches:
defn_obj = _registry[key]
unique_defns[id(defn_obj)] = defn_obj
if len(unique_defns) == 0:
# No matches
player.writer.write(f"Unknown command: {command}\r\n") player.writer.write(f"Unknown command: {command}\r\n")
await player.writer.drain() await player.writer.drain()
return return
elif len(unique_defns) == 1:
# Unique match
defn = next(iter(unique_defns.values()))
else:
# Multiple matches - show disambiguation
names = sorted([d.name for d in unique_defns.values()])
if len(names) == 2:
msg = f"{names[0]} or {names[1]}?\r\n"
else:
msg = f"{', '.join(names[:-1])}, or {names[-1]}?\r\n"
player.writer.write(msg)
await player.writer.drain()
return
# Check mode restriction # Check mode restriction
if defn.mode != "*" and defn.mode != player.mode: if defn.mode != "*" and defn.mode != player.mode: