"""
HuggingFace-style UI Implementation
This module creates a modern, HuggingFace-inspired user interface for the MMORPG
with organized tabs, clean design, and intuitive navigation.
"""
import gradio as gr
from typing import Dict, Any, Optional, List
from ..core.game_engine import GameEngine
from ..facades.game_facade import GameFacade
from ..interfaces.npc_addon import get_registered_addons
class HuggingFaceUI:
"""HuggingFace-style UI manager for the MMORPG application."""
def __init__(self, game_facade: GameFacade):
self.game_facade = game_facade
from ..core.game_engine import get_game_engine
self.game_engine = get_game_engine()
# UI state management
self.player_state = None
self.chat_tabs_state = None
# Theme configuration
self.theme = gr.themes.Soft(
primary_hue="blue",
secondary_hue="slate",
neutral_hue="slate",
radius_size="md",
spacing_size="md"
)
# Custom CSS for HuggingFace-like styling with enhanced design
self.custom_css = """
/* HuggingFace-inspired styling with modern enhancements */
.main-container {
max-width: 1400px;
margin: 0 auto;
padding: 20px;
}
.hero-section {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 30px;
border-radius: 12px;
margin-bottom: 30px;
text-align: center;
box-shadow: 0 4px 20px rgba(0,0,0,0.1);
}
.hero-title {
font-size: 2.5em;
font-weight: bold;
margin-bottom: 10px;
text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
}
.hero-subtitle {
font-size: 1.2em;
opacity: 0.9;
margin-bottom: 15px;
}
.hero-features {
font-size: 1em;
opacity: 0.8;
}
.game-panel {
background: white;
border-radius: 12px;
padding: 20px;
box-shadow: 0 4px 15px rgba(0,0,0,0.1);
margin-bottom: 20px;
border: 1px solid #e9ecef;
}
.stats-card {
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
border: 1px solid #dee2e6;
border-radius: 12px;
padding: 20px;
margin: 15px 0;
box-shadow: 0 2px 10px rgba(0,0,0,0.08);
}
.movement-grid {
display: grid;
grid-template-columns: repeat(3, 60px);
gap: 12px;
max-width: 220px;
margin: 15px auto;
justify-content: center;
}
.movement-btn {
width: 60px;
height: 60px;
border-radius: 12px;
border: 2px solid #007bff;
background: linear-gradient(135deg, #ffffff 0%, #f8f9fa 100%);
font-size: 18px;
font-weight: bold;
cursor: pointer;
transition: all 0.3s ease;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 2px 8px rgba(0,123,255,0.2);
}
.movement-btn:hover {
background: linear-gradient(135deg, #007bff 0%, #0056b3 100%);
color: white;
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0,123,255,0.4);
}
.movement-btn:active {
transform: translateY(0px);
box-shadow: 0 2px 6px rgba(0,123,255,0.3);
}
.keyboard-status {
background: linear-gradient(135deg, #e8f5e8 0%, #d4edda 100%);
border: 2px solid #4caf50;
border-radius: 8px;
padding: 12px;
margin: 10px 0;
text-align: center;
font-weight: bold;
box-shadow: 0 2px 8px rgba(76,175,80,0.2);
}
.tab-content {
padding: 25px;
min-height: 600px;
background: #fafafa;
border-radius: 8px;
}
.doc-section {
margin-bottom: 30px;
padding: 25px;
border-left: 4px solid #007bff;
background: linear-gradient(135deg, #f8f9fa 0%, #ffffff 100%);
border-radius: 0 12px 12px 0;
box-shadow: 0 2px 10px rgba(0,0,0,0.05);
}
.feature-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
gap: 20px;
margin: 20px 0;
}
.feature-card {
background: white;
border: 1px solid #e9ecef;
border-radius: 12px;
padding: 25px;
box-shadow: 0 4px 15px rgba(0,0,0,0.08);
transition: all 0.3s ease;
}
.feature-card:hover {
transform: translateY(-5px);
box-shadow: 0 8px 25px rgba(0,0,0,0.15);
}
.feature-icon {
font-size: 2.5em;
margin-bottom: 15px;
display: block;
text-align: center;
}
.chat-tab {
background: linear-gradient(135deg, #f5f5f5 0%, #e9ecef 100%);
border: 2px solid #dee2e6;
border-radius: 8px;
padding: 10px 16px;
margin: 3px;
cursor: pointer;
display: inline-flex;
align-items: center;
gap: 8px;
transition: all 0.3s ease;
font-weight: 500;
}
.chat-tab.active {
background: linear-gradient(135deg, #e3f2fd 0%, #bbdefb 100%);
border-color: #2196f3;
color: #1976d2;
box-shadow: 0 2px 8px rgba(33,150,243,0.3);
}
.chat-tab:hover {
background: linear-gradient(135deg, #e9ecef 0%, #dee2e6 100%);
transform: translateY(-1px);
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
.npc-addon-panel {
border: 2px solid #e9ecef;
border-radius: 12px;
padding: 20px;
margin: 15px 0;
background: linear-gradient(135deg, #fafafa 0%, #ffffff 100%);
box-shadow: 0 2px 10px rgba(0,0,0,0.05);
}
.plugin-card {
background: white;
border: 2px solid #e9ecef;
border-radius: 12px;
padding: 20px;
margin: 15px 0;
box-shadow: 0 2px 10px rgba(0,0,0,0.08);
transition: all 0.3s ease;
}
.plugin-card:hover {
border-color: #007bff;
box-shadow: 0 4px 15px rgba(0,123,255,0.15);
}
.status-indicator {
display: inline-block;
width: 12px;
height: 12px;
border-radius: 50%;
margin-right: 8px;
box-shadow: 0 0 0 2px white, 0 0 0 3px currentColor;
}
.status-online {
background-color: #4caf50;
animation: pulse-green 2s infinite;
}
.status-offline {
background-color: #f44336;
}
.status-idle {
background-color: #ff9800;
}
@keyframes pulse-green {
0% { box-shadow: 0 0 0 2px white, 0 0 0 3px #4caf50; }
50% { box-shadow: 0 0 0 2px white, 0 0 0 6px rgba(76,175,80,0.5); }
100% { box-shadow: 0 0 0 2px white, 0 0 0 3px #4caf50; }
}
/* Enhanced buttons */
.gradio-button {
border-radius: 8px !important;
font-weight: 600 !important;
transition: all 0.3s ease !important;
}
.gradio-button:hover {
transform: translateY(-1px) !important;
box-shadow: 0 4px 12px rgba(0,0,0,0.15) !important;
}
/* Enhanced form controls */
.gradio-textbox, .gradio-dropdown, .gradio-slider {
border-radius: 8px !important;
border: 2px solid #e9ecef !important;
transition: all 0.3s ease !important;
}
.gradio-textbox:focus, .gradio-dropdown:focus {
border-color: #007bff !important;
box-shadow: 0 0 0 3px rgba(0,123,255,0.1) !important;
}
/* Responsive design */
@media (max-width: 768px) {
.main-container {
padding: 15px;
}
.hero-section {
padding: 20px;
}
.hero-title {
font-size: 2em;
}
.feature-grid {
grid-template-columns: 1fr;
}
.movement-grid {
grid-template-columns: repeat(3, 50px);
gap: 8px;
}
.movement-btn {
width: 50px;
height: 50px;
font-size: 16px;
}
}
/* Dark mode support */
@media (prefers-color-scheme: dark) {
.doc-section {
background: linear-gradient(135deg, #2d3748 0%, #4a5568 100%);
color: white;
}
.feature-card {
background: #2d3748;
border-color: #4a5568;
color: white;
}
.npc-addon-panel, .plugin-card {
background: #2d3748;
border-color: #4a5568;
color: white;
}
}
"""
def create_hero_section(self) -> gr.HTML:
"""Create the hero section with branding and overview."""
hero_html = """
๐ฎ MMORPG with MCP Integration
Modern Multiplayer Online Role-Playing Game
๐ Real-time multiplayer โข ๐ค AI agent support โข ๐ฅ Read2Burn messaging โข ๐ MCP add-ons โข โจ๏ธ Keyboard controls
"""
return gr.HTML(hero_html)
def create_keyboard_status(self) -> gr.HTML:
"""Create keyboard status indicator."""
return gr.HTML(
# value="๐ฎ Initializing keyboard controls...
"
)
def get_keyboard_script(self) -> str:
"""Get the keyboard control script for injection into ."""
keyboard_js = """
"""
return keyboard_js
def create_movement_controls(self) -> tuple:
"""Create enhanced movement control buttons."""
with gr.Group():
gr.Markdown("### ๐น๏ธ Movement Controls")
gr.Markdown("**Keyboard:** WASD or Arrow Keys | **Mouse:** Click buttons below")
with gr.Row():
gr.HTML("")
move_up = gr.Button("โ", size="lg", elem_classes=["movement-btn"])
gr.HTML("")
with gr.Row():
move_left = gr.Button("โ", size="lg", elem_classes=["movement-btn"])
action_btn = gr.Button("โ๏ธ", size="lg", elem_classes=["movement-btn"], variant="secondary")
move_right = gr.Button("โ", size="lg", elem_classes=["movement-btn"])
with gr.Row():
gr.HTML("")
move_down = gr.Button("โ", size="lg", elem_classes=["movement-btn"])
gr.HTML("")
return move_up, move_down, move_left, move_right, action_btn
def create_player_stats_panel(self) -> gr.JSON:
"""Create player statistics display panel."""
return gr.JSON(
label="๐งโโ๏ธ Player Stats",
value={"status": "Not connected", "info": "Join the game to see your stats"},
elem_classes=["stats-card"]
)
def create_online_players_panel(self) -> gr.Dataframe:
"""Create online players display panel."""
return gr.Dataframe(
headers=["Name", "Type", "Level"],
label="๐ฅ Online Players",
elem_classes=["stats-card"]
)
def create_game_world_view(self) -> gr.HTML:
"""Create the game world visualization."""
return gr.HTML(
value=self._generate_world_html(),
label="๐ Game World"
)
def create_chat_system(self) -> tuple:
"""Create the enhanced chat system with tabs."""
# Public chat
chat_display = gr.Chatbot(
label="๐ฌ Game Chat",
height=300,
type='messages',
value=[{"role": "assistant", "content": "Welcome! Join the game to start chatting!"}],
elem_classes=["game-panel"]
)
with gr.Row():
chat_input = gr.Textbox(
placeholder="Type your message... (use /help for commands)",
scale=4,
container=False
)
chat_send = gr.Button("Send", scale=1, variant="primary")
# Private chat system
with gr.Group():
gr.Markdown("### ๐ Private Messaging")
proximity_info = gr.HTML(
value="๐ Move near NPCs or players to chat privately
",
label="๐ฑ Nearby Entities"
)
with gr.Group(visible=False) as private_chat_group:
nearby_entities = gr.Dropdown(
label="๐ฌ Start new chat with",
choices=[],
interactive=True
)
with gr.Row():
start_chat_btn = gr.Button("Start Chat", variant="primary", scale=1)
clear_all_tabs_btn = gr.Button("Clear All", variant="secondary", scale=1)
# Chat tabs display
chat_tabs_state = gr.State({})
active_tabs_display = gr.HTML(
value="No active chats
",
label="Active Chats"
)
# Current chat display
current_chat_display = gr.Chatbot(
label="๐ Private Messages",
height=200,
type='messages',
value=[],
visible=False
)
with gr.Row(visible=False) as chat_input_row:
private_message_input = gr.Textbox(
placeholder="Type private message...",
scale=4,
container=False
)
private_send_btn = gr.Button("Send", scale=1, variant="secondary")
return (chat_display, chat_input, chat_send, proximity_info, private_chat_group,
nearby_entities, start_chat_btn, clear_all_tabs_btn, chat_tabs_state,
active_tabs_display, current_chat_display, chat_input_row,
private_message_input, private_send_btn)
def create_world_events_panel(self) -> gr.Textbox:
"""Create world events display panel."""
return gr.Textbox(
label="๐ World Events & NPC Interactions",
lines=4,
interactive=False,
placeholder="World events will appear here...\n\n๐ก Tip: Walk near NPCs (๐ฎ๐ช๐ค๏ธ) to interact with them!\nThen visit the 'NPC Add-ons' tab to use their features.",
elem_classes=["game-panel"]
)
def _generate_world_html(self, width: int = 800, height: int = 600) -> str:
"""Generate HTML for the game world visualization with enhanced tooltips."""
# Enhanced game world with tree collision detection
# Dynamically generate NPCs from GameFacade with enhanced tooltips from addons
npc_html = ""
for npc in self.game_facade.get_all_npcs().values():
try:
# Get rich tooltip information from addon system
tooltip_info = self._get_npc_tooltip_info(npc)
# Ensure tooltip_info is a string
if not isinstance(tooltip_info, str):
print(f"[UI] Warning: tooltip_info for {npc.get('name', 'Unknown')} is {type(tooltip_info)}: {tooltip_info}")
tooltip_info = str(tooltip_info)
# Escape any HTML characters in tooltip
tooltip_info = tooltip_info.replace('"', '"').replace("'", ''')
npc_html += f"""
{npc['char']}
{npc['name']}
"""
except Exception as npc_e:
print(f"[UI] Error generating HTML for NPC {npc.get('name', 'Unknown')}: {npc_e}")
print(f"[UI] NPC data: {npc}")
# Add a basic NPC without tooltip
npc_html += f"""
{npc['char']}
{npc['name']}
"""
# Generate player tooltips with enhanced information
players_html = ""
try:
players = self.game_facade.get_all_players()
for player_id, player in players.items():
try:
player_tooltip = self._get_player_tooltip_info(player)
# Ensure player_tooltip is a string
if not isinstance(player_tooltip, str):
print(f"[UI] Warning: player_tooltip for {player.name} is {type(player_tooltip)}: {player_tooltip}")
player_tooltip = str(player_tooltip)
# Escape any HTML characters in tooltip
player_tooltip = player_tooltip.replace('"', '"').replace("'", ''')
players_html += f"""
๐งโ๐ฆฐ
{player.name} (Lv.{player.level})
"""
except Exception as player_e:
print(f"[UI] Error generating player tooltip for {player.name}: {player_e}")
# Add basic player without tooltip
players_html += f"""
๐งโ๐ฆฐ
{player.name} (Lv.{player.level})
"""
except Exception as e:
print(f"[UI] Error generating player tooltips: {e}")
# Base world template with enhanced tooltip styling
world_html = f"""
๐ณ
๐ณ
๐ณ
๐ณ
๐ณ
๐ณ
๐ณ
{npc_html}
๐๏ธ
๐๏ธ
๐
๐
{players_html}
๐ Fantasy Realm |
Players: {len(players) if 'players' in locals() else 0}/20 |
Hover over NPCs and players for information
๐งโ๐ฆฐ Players | ๐ฎ๐ช๐ถ๐ง๐ค๏ธ NPCs | ๐ณ Trees
"""
return world_html
def _get_npc_tooltip_info(self, npc: dict) -> str:
"""Generate simple tooltip information for an NPC from addon data."""
try:
# Get addon information if available
from ..interfaces.npc_addon import get_registered_addons
registered_addons = get_registered_addons()
# Try to find matching addon
addon = None
npc_id = npc.get('id', '')
# Search for addon by NPC ID or name matching
for addon_id, registered_addon in registered_addons.items():
if hasattr(registered_addon, 'npc_config') and registered_addon.npc_config:
try:
npc_config = registered_addon.npc_config
if isinstance(npc_config, dict):
addon_npc_id = npc_config.get('id', '')
# Ensure both values are strings before comparison
if isinstance(addon_npc_id, str) and isinstance(npc_id, str):
if addon_npc_id == npc_id or addon_npc_id in npc_id:
addon = registered_addon
break
except Exception as config_e:
print(f"[UI] Error accessing npc_config for {addon_id}: {config_e}")
continue
# Use description from addon's npc_config if available
if addon and hasattr(addon, 'npc_config') and addon.npc_config:
description = addon.npc_config.get('description', '')
if description:
return description
# Fallback to basic NPC description
return npc.get('description', f"{npc['name']} - Interactive NPC")
except Exception as e:
print(f"[UI] Error generating NPC tooltip for {npc.get('name', 'Unknown')}: {e}")
return f"{npc['name']} - Interactive NPC"
def _get_player_tooltip_info(self, player) -> str:
"""Generate rich tooltip information for a player."""
try:
tooltip = f"๐ค {player.name}\n"
tooltip += f"โญ Level: {player.level}\n"
tooltip += f"โค๏ธ Health: {player.hp}/{player.max_hp}\n"
tooltip += f"๐ฐ Gold: {getattr(player, 'gold', 0)}\n"
tooltip += f"๐ฏ XP: {player.experience}\n"
tooltip += f"๐ Position: ({player.x}, {player.y})\n"
# Add status information
if hasattr(player, 'last_active'):
import time
time_diff = time.time() - player.last_active
if time_diff < 30:
tooltip += "๐ข Status: Active"
elif time_diff < 300:
tooltip += "๐ก Status: Away"
else:
tooltip += "๐ด Status: Idle"
tooltip += "\n\n๐ฌ Walk near to start private chat"
return tooltip
except Exception as e:
print(f"[UI] Error generating player tooltip: {e}")
return f"{getattr(player, 'name', 'Player')}\n๐ฌ Interactive player - Walk near to chat"
def create_interface(self) -> gr.Blocks:
"""Create the complete HuggingFace-style interface."""
with gr.Blocks(
title="๐ฎ MMORPG with MCP Integration",
theme=self.theme,
css=self.custom_css
) as interface:
# Hero section
self.create_hero_section()
# Keyboard status
keyboard_status = self.create_keyboard_status()
# Player state management
self.player_state = gr.State({})
with gr.Tabs():
# Main Game Tab
with gr.Tab("๐ Game World", elem_classes=["tab-content"]):
with gr.Row():
with gr.Column(scale=2):
# Player registration
with gr.Group():
gr.Markdown("### ๐ฎ Join the Adventure")
with gr.Row():
player_name = gr.Textbox(
label="Player Name",
placeholder="Enter your character name",
scale=3
)
join_btn = gr.Button("Join Game", variant="primary", scale=1)
leave_btn = gr.Button("Leave Game", variant="secondary", scale=1)
# Enhanced controls info
gr.Markdown("""
### โจ๏ธ Enhanced Controls
**Mouse:** Click movement buttons below
**Keyboard:** Use **WASD** or **Arrow Keys** for movement
**Action:** **Spacebar** for special action
๐ก *Keyboard controls automatically activate when buttons are ready*
""")
# Game world view with bigger map
game_view = self.create_game_world_view()
# Enhanced movement controls
move_up, move_down, move_left, move_right, action_btn = self.create_movement_controls()
with gr.Column(scale=1):
# Player stats
player_info = self.create_player_stats_panel()
# Online players
online_players = self.create_online_players_panel()
# World events
world_events = self.create_world_events_panel()
# Enhanced chat system
chat_components = self.create_chat_system()
(chat_display, chat_input, chat_send, proximity_info, private_chat_group,
nearby_entities, start_chat_btn, clear_all_tabs_btn, chat_tabs_state,
active_tabs_display, current_chat_display, chat_input_row,
private_message_input, private_send_btn) = chat_components
# Documentation Tab
with gr.Tab("๐ Documentation", elem_classes=["tab-content"]):
self._create_documentation_content()
# NPC Add-ons Tab
with gr.Tab("๐ค NPC Add-ons", elem_classes=["tab-content"]):
self._create_npc_addons_content()
# Plugin System Tab
with gr.Tab("๐ Plugin System", elem_classes=["tab-content"]):
self._create_plugin_system_content()
# MCP Integration Tab
with gr.Tab("๐ MCP Integration", elem_classes=["tab-content"]):
self._create_mcp_integration_content()
# Architecture Tab with gr.Tab("๐๏ธ Architecture", elem_classes=["tab-content"]):
self._create_architecture_content()
# Event handlers will be wired up by the InterfaceManager
return interface
def _create_documentation_content(self) -> None:
"""Create comprehensive documentation content."""
gr.Markdown("""
# ๐ MMORPG Documentation
Welcome to the comprehensive guide for our modern MMORPG with MCP integration!
""")
with gr.Tabs():
with gr.Tab("๐ Quick Start"):
gr.Markdown("""
## ๐ Quick Start Guide
### Getting Started
1. **Join the Game**: Enter your character name and click "Join Game"
2. **Move Around**: Use WASD keys or arrow keys to move your character
3. **Interact**: Walk near NPCs (๐ฎ๐ช๐ค๏ธ) to interact with them
4. **Chat**: Use the chat system to communicate with other players
5. **Explore**: Check out the NPC Add-ons tab for special features
### Basic Controls
- **Movement**: WASD or Arrow Keys
- **Action**: Spacebar
- **Chat**: Type in the chat box and press Enter
- **Private Messages**: Move near players/NPCs and use the private chat system
### Game Features
- **Real-time Multiplayer**: Up to 20 players can join simultaneously
- **AI Agent Support**: AI agents can connect via MCP protocol
- **NPC Interactions**: Each NPC has unique features and personalities
- **Plugin System**: Extensible architecture for custom add-ons
- **Tree Collision**: Navigate around trees in the expanded world
""")
with gr.Tab("๐ฎ Game Mechanics"):
gr.Markdown("""
## ๐ฎ Game Mechanics
### Player System
- **Health Points (HP)**: Start with 100 HP, maximum 100 HP
- **Experience Points**: Gain XP through interactions and activities
- **Leveling**: Level up as you gain experience
- **Gold**: Virtual currency for trading with NPCs
### World Interaction
- **NPC Proximity**: Walk within range of NPCs to interact
- **Collision Detection**: Trees and obstacles block movement
- **Chat System**: Public and private messaging
- **Real-time Updates**: All actions update in real-time
### NPCs Available
- **๐ฎ Secure Mailbox**: Read2Burn messaging system
- **๐ช Donald the Trader**: Nice trading NPC with funny responses
- **๐ถ Roaming Rick**: Moving NPC that wanders the world
- **๐ง Ancient Sage**: Mystical NPC with wisdom and magic
- **๐ค๏ธ Weather Oracle**: MCP-powered weather information
""")
with gr.Tab("๐ฌ Chat System"):
gr.Markdown("""
## ๐ฌ Advanced Chat System
### Public Chat
- Visible to all players in the game
- Supports emojis and basic formatting
- Command system with /help for available commands
### Private Messaging
- **Tab-based Interface**: Multiple simultaneous conversations
- **Proximity-based**: Start chats with nearby players/NPCs
- **Pin/Unpin Tabs**: Keep important conversations open
- **Auto-refresh**: Real-time message updates
### Chat Commands
- `/help` - Show available commands
- `/players` - List online players
- `/stats` - Show your character stats
- `/clear` - Clear chat history
- `/whisper ` - Send private message
### Chat Features
- **Message History**: Persistent chat history per conversation
- **Unread Indicators**: Visual badges for new messages
- **Emoji Support**: Full emoji support in messages
- **Timestamps**: All messages include timestamps
""")
with gr.Tab("๐ง Technical"):
gr.Markdown("""
## ๐ง Technical Details
### Architecture
- **Clean Architecture**: Separation of concerns with interfaces, services, and facades
- **Domain Models**: Core game entities (Player, GameWorld)
- **Service Layer**: Business logic separated into focused services
- **Facade Pattern**: Simplified interface for complex operations
### Technologies Used
- **Frontend**: Gradio with custom HuggingFace-style UI
- **Backend**: Python with asyncio for real-time features
- **MCP Integration**: Model Context Protocol for AI agent connectivity
- **Plugin System**: Hot-reloadable plugin architecture
### Performance Features
- **Thread Safety**: Proper locking mechanisms throughout - **Efficient Updates**: Optimized real-time data synchronization
- **Memory Management**: Careful resource management for long-running sessions
- **Error Handling**: Comprehensive exception handling and recovery
""")
def _create_npc_addons_content(self) -> None:
"""Create NPC add-ons content."""
gr.Markdown("""
# ๐ค NPC Add-ons System
Each NPC in the game has unique functionality through our extensible add-on system.
Walk near NPCs in the game world to unlock their features!
""")
with gr.Tabs():
# Auto-discover and create tabs for registered addons
self._create_dynamic_addon_tabs()
# Legacy static tabs for backward compatibility
with gr.Tab("๐ฅ Read2Burn Mailbox"):
gr.Markdown("""
## ๐ฅ Secure Read2Burn Messaging
The Read2Burn Mailbox provides secure, self-destructing messaging between players.
### Features
- **Self-Destructing Messages**: Messages automatically delete after reading
- **Secure Delivery**: End-to-end message protection
- **Anonymous Options**: Send messages without revealing identity
- **Time-based Expiry**: Messages expire after set time limits
### How to Use
1. Walk near the ๐ฎ Secure Mailbox NPC
2. Visit this NPC Add-ons tab
3. Select the Read2Burn tab
4. Compose and send secure messages
*Note: This feature requires proximity to the Secure Mailbox NPC*
""")
# Read2Burn interface
self._create_read2burn_interface()
with gr.Tab("๐ช Donald the Trader"):
gr.Markdown("""
## ๐ช Donald the Trader
Meet Donald, our charismatic trader with a unique personality and the best deals!
### Personality
Donald speaks in a distinctive style with phrases like:
- "Listen, I make the best deals. Nobody makes deals like me, believe me!"
- "You're looking at tremendous value here. Tremendous!"
- "This item? Fantastic. The best. Everyone says so."
### Trading Features
- **Unique Items**: Exclusive merchandise with "tremendous" quality
- **Negotiation**: Engage in deal making deal-making
- **Special Offers**: Limited-time deals with signature flair
- **Personality Responses**: Context-aware, entertaining interactions
### How to Interact
1. Walk near the ๐ช Donald the Trader NPC
2. Start a private chat or use the public chat
3. Mention trading, deals, or items to trigger responses
4. Enjoy the unique personality and humor!
""")
with gr.Tab("๐ค๏ธ Weather Oracle"):
gr.Markdown("""
## ๐ค๏ธ Weather Oracle (MCP)
The Weather Oracle demonstrates MCP (Model Context Protocol) integration.
### MCP Features
- **External Service**: Connects to external weather APIs
- **Real-time Data**: Live weather information
- **Protocol Demo**: Shows MCP integration capabilities
- **Extensible**: Template for other MCP services
### Available Commands
- Weather forecasts for any location
- Current conditions and alerts
- Historical weather data
- Climate information
*Note: This is a demonstration of MCP protocol integration*
""")
with gr.Tab("๐ช Example Merchant"):
# Get the example merchant addon interface
try:
from ..addons.example_npc_addon import ExampleNPCAddon
example_addon = ExampleNPCAddon()
example_addon.get_interface()
except Exception as e:
gr.Markdown(f"""
## ๐ช Example Merchant
*Interface temporarily unavailable: {str(e)}*
Please check that the Example Merchant addon is properly installed.
""")
gr.Markdown("""
### ๐ ๏ธ Add-on Development
Want to create your own NPC add-on? Check out the Plugin System tab for development guidelines! """)
def _create_dynamic_addon_tabs(self):
"""Create tabs for all registered addons that have ui_tab_name."""
try:
# Get registered addons from the global registry
registered_addons = get_registered_addons()
for addon_id, addon in registered_addons.items():
# Check if addon wants a UI tab
if hasattr(addon, 'ui_tab_name') and addon.ui_tab_name:
try:
with gr.Tab(f"{addon.ui_tab_name}"):
# Create a header with addon info
gr.Markdown(f"""
## {addon.addon_name}
*Self-contained addon with auto-registration*
**Addon ID:** `{addon.addon_id}`
**NPC Location:** {f"({addon.npc_config['x']}, {addon.npc_config['y']})" if addon.npc_config else "No NPC"}
---
""")
# Render the addon's interface
addon.get_interface()
except Exception as e:
# Create error tab for broken addons
with gr.Tab(f"โ {addon.ui_tab_name}"):
gr.Markdown(f"""
## โ {addon.addon_name}
*This addon interface failed to load*
**Error:** {str(e)}
**Addon ID:** `{addon.addon_id}`
Please check the addon configuration and try restarting the server.
""")
except Exception as e:
print(f"[UI] Error creating dynamic addon tabs: {e}")
# Fallback: create a single tab explaining the issue
with gr.Tab("โ ๏ธ Addon System"):
gr.Markdown(f"""
## โ ๏ธ Addon System Error
Failed to load registered addons: {str(e)}
Please check that the game engine is running and addons are properly registered.
""")
def _create_plugin_system_content(self) -> None:
"""Create plugin system documentation."""
gr.Markdown("""
# ๐ Plugin System Architecture
Our game features a sophisticated plugin system that allows for easy extension and customization.
Plugins are automatically discovered and can be hot-reloaded without restarting the server.
""")
with gr.Tabs():
with gr.Tab("๐ Overview"):
gr.Markdown("""
## ๐ Plugin System Overview
### Key Features
- **Hot Reload**: Load/unload plugins without restarting
- **Dependency Management**: Automatic dependency resolution
- **Event System**: Plugin communication through events
- **Type Safety**: Full interface-based architecture
- **Error Handling**: Robust error recovery and logging
### Plugin Types
1. **Game Plugins**: Extend core game functionality
2. **NPC Plugins**: Add new NPC behaviors and interactions
3. **UI Plugins**: Custom user interface components
4. **Service Plugins**: External service integrations
5. **Tool Plugins**: Development and administrative tools
### Plugin Directory Structure
```
plugins/
โโโ my_plugin.py # Single-file plugin
โโโ complex_plugin/ # Multi-file plugin
โ โโโ __init__.py
โ โโโ plugin.py
โ โโโ config.json
โ โโโ assets/
โโโ examples/ # Example plugins
โโโ hello_world.py
โโโ custom_npc.py
โโโ weather_service.py
```
""")
with gr.Tab("๐ป Development"):
gr.Markdown("""
## ๐ป Plugin Development Guide
### Simple Plugin Example
```python
from src.interfaces.plugin_interfaces import IGamePlugin, PluginMetadata
class HelloWorldPlugin(IGamePlugin):
@property
def metadata(self) -> PluginMetadata:
return PluginMetadata(
name="Hello World",
version="1.0.0",
description="A simple greeting plugin",
author="Your Name"
)
def initialize(self) -> bool:
print("Hello, World!")
return True
def cleanup(self) -> bool:
print("Goodbye, World!")
return True
# Plugin instance
plugin = HelloWorldPlugin()
```
### NPC Plugin Example
```python
from src.interfaces.plugin_interfaces import INPCPlugin
class CustomNPCPlugin(INPCPlugin):
@property
def npc_id(self) -> str:
return "custom_trader"
def handle_interaction(self, player_id: str, message: str) -> str:
return f"Hello {player_id}! You said: {message}"
def get_npc_data(self) -> dict:
return {
"name": "Custom Trader",
"position": {"x": 100, "y": 100},
"sprite": "๐"
}
```
### Installation
1. Create your plugin file in the `plugins/` directory
2. The plugin will be automatically discovered
3. Check the Plugin Manager for status
4. Use hot-reload to activate without restart
""")
with gr.Tab("๐ง Plugin Manager"):
gr.Markdown("## ๐ง Plugin Management Interface")
# Plugin status display
gr.HTML("""
๐ Plugin Discovery
Automatically scans the plugins/ directory for new plugins
Active
๐ Hot Reload
Load and unload plugins without server restart
Available
๐ Dependency Management
Automatic dependency resolution and loading order
Active
โ ๏ธ Error Handling
Robust error recovery and detailed logging
Active
""")
# Plugin actions
with gr.Row():
refresh_plugins_btn = gr.Button("๐ Refresh Plugins", variant="primary")
reload_all_btn = gr.Button("๐ Reload All", variant="secondary")
plugin_status = gr.JSON(
label="Plugin Status",
value={
"total_plugins": 0,
"active_plugins": 0,
"failed_plugins": 0,
"last_scan": "Never"
}
)
def _create_mcp_integration_content(self) -> None:
"""Create MCP integration documentation."""
gr.Markdown("""
# ๐ MCP Integration
Model Context Protocol (MCP) integration allows AI agents to connect and interact with the game world.
""")
with gr.Tabs():
with gr.Tab("๐ก Server Status"):
gr.Markdown("## ๐ก MCP Server Status")
mcp_status = gr.JSON(
label="Server Information",
value={
"server_status": "๐ข Active",
"server_url": "This Gradio app serves as MCP server",
"tools_available": [
"register_ai_agent",
"move_agent",
"send_chat",
"get_game_state",
"interact_with_npc"
],
"active_ai_agents": 0,
"total_players": 0
}
)
with gr.Tab("๐งช AI Agent Testing"):
self._create_ai_testing_tab()
def _create_ai_testing_tab(self):
"""Create enhanced AI testing tab."""
gr.Markdown("## ๐งช Test AI Agent Integration")
gr.Markdown("*Use this to simulate AI agent connections for testing*")
with gr.Row():
ai_name = gr.Textbox(
label="AI Agent Name",
placeholder="Claude the Explorer",
scale=3
)
register_ai_btn = gr.Button("Register AI Agent", variant="primary", scale=1)
with gr.Row():
ai_action = gr.Dropdown(
choices=["move up", "move down", "move left", "move right", "chat"],
label="AI Action",
scale=2
)
ai_message = gr.Textbox(
label="AI Message (for chat)",
placeholder="Hello humans!",
scale=3
)
execute_ai_btn = gr.Button("Execute AI Action", variant="secondary")
ai_result = gr.Textbox(label="AI Action Result", interactive=False, lines=5)
# Raw Data Testing Section
gr.Markdown("### ๐งช Test Raw Data Access")
gr.Markdown("*Test accessing comprehensive world data without HTML parsing*")
with gr.Row():
test_raw_data_btn = gr.Button("Get Raw World Data", variant="secondary")
test_available_npcs_btn = gr.Button("Get Available NPCs", variant="secondary")
raw_data_result = gr.JSON(label="Raw Data Output", visible=True)
# Wire up handlers (simplified versions of original handlers)
register_ai_btn.click(
self._handle_register_ai_agent,
inputs=[ai_name],
outputs=[ai_result]
)
execute_ai_btn.click(
self._handle_execute_ai_action,
inputs=[ai_action, ai_message],
outputs=[ai_result]
)
test_raw_data_btn.click(
self._handle_test_raw_world_data,
inputs=[],
outputs=[raw_data_result]
)
test_available_npcs_btn.click(
self._handle_test_available_npcs,
inputs=[],
outputs=[raw_data_result]
)
# =================================================================
def _handle_register_ai_agent(self, ai_name: str) -> str:
"""Handle AI agent registration."""
if not ai_name or not ai_name.strip():
return "โ Please enter a valid AI agent name"
try:
agent_id = f"test_agent_{uuid.uuid4().hex[:8]}"
result = self.game_facade.register_ai_agent(agent_id, ai_name.strip())
if result:
if not hasattr(self, 'registered_agents'):
self.registered_agents = {}
self.registered_agents[agent_id] = ai_name.strip()
return f"โ
AI agent '{ai_name.strip()}' registered successfully!\nAgent ID: {agent_id}"
else:
return f"โ Failed to register AI agent '{ai_name.strip()}'"
except Exception as e:
return f"โ Error registering AI agent: {str(e)}"
def _handle_execute_ai_action(self, action: str, message: str) -> str:
"""Handle AI agent action execution."""
if not hasattr(self, 'registered_agents') or not self.registered_agents:
return "โ No AI agents registered. Please register an AI agent first!"
if not action:
return "โ Please select an action to execute"
try:
agent_id = list(self.registered_agents.keys())[0]
agent_name = self.registered_agents[agent_id]
if action.startswith("move"):
direction = action.split()[1] if len(action.split()) > 1 else action
result = self.game_facade.move_ai_agent(agent_id, direction)
if result.get("success"):
position = result.get("position", {})
return f"๐ค {agent_name} moved {direction} successfully!\nNew position: ({position.get('x', '?')}, {position.get('y', '?')})"
else:
error_msg = result.get("error", "Movement failed")
return f"โ {agent_name} movement failed: {error_msg}"
elif action == "chat":
if not message or not message.strip():
return "โ Please enter a chat message"
result = self.game_facade.ai_agent_chat(agent_id, message.strip())
if result.get("success"):
return f"๐ฌ {agent_name} sent chat message: '{message.strip()}'"
else:
error_msg = result.get("error", "Chat failed")
return f"โ {agent_name} chat failed: {error_msg}"
else:
return f"โ Unknown action: {action}"
except Exception as e:
return f"โ Error executing AI action: {str(e)}"
except Exception as e:
return f"โ Error executing AI action: {str(e)}"
def _handle_test_raw_world_data(self) -> Dict:
"""Handle testing raw world data access."""
try:
raw_data = self.game_facade.get_raw_world_data()
return raw_data
except Exception as e:
return {"error": f"Failed to get raw world data: {str(e)}"}
def _handle_test_available_npcs(self) -> List[Dict]:
"""Handle testing available NPCs data access."""
try:
npcs_data = self.game_facade.get_available_npcs()
return npcs_data
except Exception as e:
return [{"error": f"Failed to get NPCs data: {str(e)}"}]
def _create_architecture_content(self) -> None:
"""Create architecture documentation."""
gr.Markdown("""
# ๐๏ธ System Architecture
Our MMORPG follows clean architecture principles with clear separation of concerns.
""")
with gr.Tabs():
with gr.Tab("๐๏ธ Clean Architecture"):
gr.Markdown("""
## ๐๏ธ Clean Architecture Implementation
### Architecture Layers
```
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ UI Layer โ
โ (Gradio Interface, HF-UI) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Depends on
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Facade Layer โ
โ (GameFacade) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Depends on
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Service Layer โ
โ (PlayerService, ChatService, etc) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Depends on
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Core Layer โ
โ (Player, GameWorld, Engine) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
```
### Key Principles
- **Dependency Inversion**: High-level modules don't depend on low-level modules
- **Interface Segregation**: Small, focused interfaces
- **Single Responsibility**: Each class has one reason to change
- **Open/Closed**: Open for extension, closed for modification
""")
with gr.Tab("๐ง Services"):
gr.Markdown("""
## ๐ง Service Architecture
### Core Services
**PlayerService**
- Player management and lifecycle
- Movement and position tracking
- Health and experience management
- Statistics and progression
**ChatService**
- Public and private messaging
- Message history and persistence
- Command processing
- Real-time communication
**NPCService**
- NPC behavior management
- Interaction handling
- Personality systems (like Donald's responses)
- Movement and AI logic
**MCPService**
- AI agent registration and management
- MCP protocol implementation
- External service integration
- Tool and capability exposure
**PluginService**
- Plugin discovery and loading
- Dependency management
- Hot-reload functionality
- Event system coordination
### Service Communication
- Services communicate through well-defined interfaces
- Event-driven architecture for loose coupling
- Centralized coordination through GameEngine
- Thread-safe operations with proper locking
""")
with gr.Tab("๐พ Data Flow"):
gr.Markdown("""
## ๐พ Data Flow Architecture
### Request Flow
```
User Input โ UI Layer โ Facade โ Service โ Core โ Response
```
### Real-time Updates
```
Game State Change โ Service โ Event System โ UI Update
```
### Plugin Integration
```
Plugin Event โ Plugin Service โ Core Services โ Game State
```
### MCP Agent Flow
```
External Agent โ MCP Protocol โ MCP Service โ Game Services
```
### Error Handling
- Comprehensive exception handling at each layer
- Graceful degradation for non-critical failures
- Detailed logging for debugging and monitoring
- Recovery mechanisms for transient failures
### Performance Optimizations
- Efficient state updates with minimal data transfer
- Optimized rendering for game world visualization
- Smart caching for frequently accessed data
- Asynchronous operations where beneficial
""")
def create_join_section(self) -> tuple:
"""Create the join game section with input and buttons."""
with gr.Row():
with gr.Column(scale=2):
join_name = gr.Textbox(
label="Player Name",
placeholder="Enter your player name...",
elem_id="join-name-input"
)
with gr.Column(scale=1):
join_btn = gr.Button("Join Game ๐ฎ", variant="primary", elem_id="join-btn")
leave_btn = gr.Button("Leave Game ๐ช", variant="secondary", elem_id="leave-btn")
# Player list display
player_list = gr.Dataframe(
headers=["Name", "Level", "Position", "Status"],
label="Online Players",
elem_id="player-list"
)
return (join_name, join_btn, leave_btn, player_list)
def create_game_display(self) -> tuple:
"""Create the main game display section."""
with gr.Row():
with gr.Column(scale=3):
game_canvas = gr.HTML(
value=self._get_initial_game_view(),
label="Game World",
elem_id="game-canvas"
)
with gr.Column(scale=1):
status_display = gr.JSON(
label="Game Status",
elem_id="status-display"
)
return (game_canvas, status_display)
def create_documentation_tabs(self):
"""Create documentation tabs using the DocumentationTabs component."""
from .documentation_tabs import DocumentationTabs
doc_tabs = DocumentationTabs()
return doc_tabs.create_documentation_tabs()
def _get_initial_game_view(self) -> str:
"""Get the initial HTML for the game view."""
return """
๐ฎ Join the game to start your adventure!
"""
def _create_read2burn_interface(self) -> None:
"""Create the Read2Burn mailbox interface."""
from ..addons.read2burn_addon import read2burn_service
with gr.Column():
gr.Markdown("""
### ๐ฅ Read2Burn Commands
**Commands:**
- `create Your secret message here` - Create new message
- `read MESSAGE_ID` - Read message (destroys it!)
- `list` - Show your created messages
- `help` - Show detailed help
""")
with gr.Row():
command_input = gr.Textbox(
label="Command",
placeholder="create Hello, this message will self-destruct!",
scale=3
)
send_btn = gr.Button("Send", variant="primary", scale=1)
result_output = gr.Textbox(
label="Mailbox Response",
lines=6,
interactive=False
)
# Message history
message_history = gr.Dataframe(
headers=["Message ID", "Created", "Status", "Reads Left"],
label="Your Messages",
interactive=False
)
def handle_mailbox_command(command: str):
from ..core.game_engine import get_game_engine
if not command.strip():
return "โ Please enter a command", []
try:
# Get current player from game engine
engine = get_game_engine()
players = engine.get_world().get_all_players()
if not players:
return "โ No players in the game! Please join the game first.", []
# Use the most recently active player
player_id = max(players.keys(), key=lambda pid: players[pid].last_active)
player_name = players[player_id].name
print(f"[Read2Burn] Command '{command}' from player {player_name} ({player_id})")
result = read2burn_service.handle_command(player_id, command)
history = read2burn_service.get_player_message_history(player_id)
# Add player info to the result
result = f"**Player:** {player_name}\n\n{result}"
return result, history
except Exception as e:
print(f"[Read2Burn] Error: {e}")
return f"โ Error processing command: {str(e)}", []
send_btn.click(
handle_mailbox_command,
inputs=[command_input],
outputs=[result_output, message_history]
)
command_input.submit(
handle_mailbox_command,
inputs=[command_input],
outputs=[result_output, message_history]
)