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."""
import logging
from collections.abc import Awaitable, Callable
from dataclasses import dataclass, field
from mudlib.player import Player
logger = logging.getLogger(__name__)
# Type alias for command handlers
CommandHandler = Callable[[Player, str], Awaitable[None]]
@ -31,8 +34,32 @@ def register(defn: CommandDefinition) -> None:
Args:
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
# Check for collisions on each alias
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
@ -53,13 +80,37 @@ async def dispatch(player: Player, raw_input: str) -> None:
command = parts[0].lower()
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)
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")
await player.writer.drain()
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
if defn.mode != "*" and defn.mode != player.mode: