""" 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] )