Implement collision detection in register
This commit is contained in:
parent
5efdbaf4e6
commit
7d3b02f6ff
1 changed files with 55 additions and 4 deletions
|
|
@ -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:
|
||||
|
|
|
|||
Loading…
Reference in a new issue