Spaces:
Runtime error
Runtime error
| """ | |
| Game Facade - Simplified interface for complex game operations. | |
| """ | |
| import time | |
| from typing import Dict, Any, Optional, List, Tuple | |
| from ..core.game_engine import get_game_engine | |
| from ..core.player import Player | |
| class GameFacade: | |
| """Facade providing simplified access to game functionality.""" | |
| def __init__(self): | |
| self.engine = get_game_engine() | |
| def start_game(self) -> bool: | |
| """Start the game engine and all services.""" | |
| return self.engine.start() | |
| def stop_game(self) -> bool: | |
| """Stop the game engine and all services.""" | |
| return self.engine.stop() | |
| # Player Operations | |
| def join_game(self, name: str, player_type: str = "human") -> Optional[str]: | |
| """Join a player to the game.""" | |
| try: | |
| player_service = self.engine.get_player_service() | |
| # Create player | |
| player = player_service.create_player(name, player_type) | |
| # Add to world | |
| success = player_service.add_player_to_world(player) | |
| if success: | |
| # Notify plugins | |
| self.engine.get_plugin_service().call_plugin_event('on_player_join', player.id) | |
| return player.id | |
| return None | |
| except Exception as e: | |
| print(f"[GameFacade] Error joining game: {e}") | |
| return None | |
| def leave_game(self, player_id: str) -> bool: | |
| """Remove a player from the game.""" | |
| try: | |
| player_service = self.engine.get_player_service() | |
| # Notify plugins before removal | |
| self.engine.get_plugin_service().call_plugin_event('on_player_leave', player_id) | |
| return player_service.remove_player_from_world(player_id) | |
| except Exception as e: | |
| print(f"[GameFacade] Error leaving game: {e}") | |
| return False | |
| def move_player(self, player_id: str, direction: str) -> Tuple[bool, Dict[str, int], List[str]]: | |
| """Move a player in specified direction, returning success, new position, and triggered events.""" | |
| from math import sqrt | |
| success = self.engine.get_player_service().move_player(player_id, direction) | |
| new_position = {} | |
| events = [] | |
| if success: | |
| # Get updated player position | |
| player = self.engine.get_player_service().get_player(player_id) | |
| new_position = {'x': player.x, 'y': player.y} | |
| # Gather world events since movement | |
| world_events = self.engine.get_world().get_world_events() | |
| # Extract only new events messages | |
| events = [ev.get('event') for ev in world_events] | |
| return success, new_position, events | |
| def get_player_stats(self, player_id: str) -> Dict: | |
| """Get player statistics.""" | |
| return self.engine.get_player_service().get_player_stats(player_id) | |
| def handle_action(self, player_id: str) -> str: | |
| """Handle special action: interact with nearest NPC if in range.""" | |
| # Determine proximity | |
| prox = self.get_proximity_info(player_id) | |
| nearby = prox.get('nearby_entities', []) | |
| if nearby: | |
| npc_id = nearby[0]['id'] | |
| # Interact with NPC via default empty message | |
| return self.interact_with_npc(player_id, npc_id, "") | |
| return "No entity nearby to interact." | |
| def get_proximity_info(self, player_id: str) -> Dict[str, Any]: | |
| """Get nearby entities (NPCs and other players) within interaction range.""" | |
| import math | |
| result = {'nearby_entities': []} | |
| # Get player position | |
| player = self.engine.get_player_service().get_player(player_id) | |
| if not player: | |
| return result | |
| # Check NPCs | |
| world = self.engine.get_world() | |
| for npc_id, npc_data in world.get_all_npcs().items(): | |
| dx = player.x - npc_data.get('x', 0) | |
| dy = player.y - npc_data.get('y', 0) | |
| distance = math.hypot(dx, dy) | |
| if distance <= 50: | |
| result['nearby_entities'].append({'id': npc_id, 'name': npc_data.get('name')}) | |
| # Optionally include other players | |
| for other_id, other in world.get_all_players().items(): | |
| if other_id == player_id: | |
| continue | |
| dx = player.x - other.x | |
| dy = player.y - other.y | |
| if math.hypot(dx, dy) <= 50: | |
| result['nearby_entities'].append({'id': other_id, 'name': other.name}) | |
| return result | |
| # Chat Operations | |
| def send_chat_message(self, player_id: str, message: str) -> bool: | |
| """Send a public chat message.""" | |
| try: | |
| chat_service = self.engine.get_chat_service() | |
| success = chat_service.send_public_message(player_id, message) | |
| if success: | |
| # Notify plugins | |
| self.engine.get_plugin_service().call_plugin_event( | |
| 'on_chat_message', player_id, message, 'public' | |
| ) | |
| return success | |
| except Exception as e: | |
| print(f"[GameFacade] Error sending chat: {e}") | |
| return False | |
| def get_chat_history(self, limit: int = 10) -> List[Dict]: | |
| """Get recent chat messages.""" | |
| return self.engine.get_chat_service().get_chat_history(limit) | |
| # NPC Operations | |
| def interact_with_npc(self, player_id: str, npc_id: str, message: str) -> str: | |
| """Interact with an NPC.""" | |
| return self.engine.get_npc_service().get_npc_response(npc_id, message, player_id) | |
| def get_npc_data(self, npc_id: str) -> Optional[Dict]: | |
| """Get NPC information.""" | |
| return self.engine.get_npc_service().get_npc(npc_id) | |
| # World State | |
| def get_game_state(self) -> Dict: | |
| """Get current game state.""" | |
| return self.engine.get_world().get_world_state() | |
| def get_all_players(self) -> Dict[str, Player]: | |
| """Get all players in the game.""" | |
| return self.engine.get_player_service().get_all_players() | |
| def get_all_npcs(self) -> Dict[str, Dict]: | |
| """Get all NPCs in the game.""" | |
| return self.engine.get_npc_service().get_all_npcs() | |
| # MCP Operations | |
| def register_ai_agent(self, agent_id: str, name: str) -> str: | |
| """Register an AI agent via MCP.""" | |
| return self.engine.get_mcp_service().register_ai_agent(agent_id, name) | |
| def move_ai_agent(self, agent_id: str, direction: str) -> Dict: | |
| """Move an AI agent.""" | |
| return self.engine.get_mcp_service().move_agent(agent_id, direction) | |
| def ai_agent_chat(self, agent_id: str, message: str) -> Dict: | |
| """Send chat message from AI agent.""" | |
| return self.engine.get_mcp_service().send_chat(agent_id, message) | |
| def get_available_npcs(self) -> List[Dict[str, Any]]: | |
| """Get list of available NPCs with their data for MCP integration.""" | |
| try: | |
| npcs = self.get_all_npcs() | |
| available_npcs = [] | |
| for npc_id, npc_data in npcs.items(): | |
| available_npcs.append({ | |
| 'id': npc_id, | |
| 'name': npc_data.get('name', 'Unknown NPC'), | |
| 'type': npc_data.get('type', 'npc'), | |
| 'position': { | |
| 'x': npc_data.get('x', 0), | |
| 'y': npc_data.get('y', 0) | |
| }, | |
| 'description': npc_data.get('description', ''), | |
| 'dialogue_count': len(npc_data.get('dialogue_history', [])), | |
| 'active': npc_data.get('active', True) | |
| }) | |
| return available_npcs | |
| except Exception as e: | |
| print(f"[GameFacade] Error getting available NPCs: {e}") | |
| return [] | |
| def get_available_players(self) -> List[Dict[str, Any]]: | |
| """Get list of available players with their data for MCP integration.""" | |
| try: | |
| players = self.get_all_players() | |
| available_players = [] | |
| for player_id, player in players.items(): | |
| available_players.append({ | |
| 'id': player.id, | |
| 'name': player.name, | |
| 'type': player.type, | |
| 'position': { | |
| 'x': player.x, | |
| 'y': player.y | |
| }, | |
| 'level': player.level, | |
| 'hp': f"{player.hp}/{player.max_hp}", | |
| 'gold': player.gold, | |
| 'experience': player.experience, | |
| 'last_active': player.last_active, | |
| 'session_hash': getattr(player, 'session_hash', None) | |
| }) | |
| return available_players | |
| except Exception as e: | |
| print(f"[GameFacade] Error getting available players: {e}") | |
| return [] | |
| def get_raw_world_data(self) -> Dict[str, Any]: | |
| """Get comprehensive raw world data without HTML parsing for MCP integration.""" | |
| try: | |
| return { | |
| 'players': self.get_available_players(), | |
| 'npcs': self.get_available_npcs(), | |
| 'world_state': self.get_game_state(), | |
| 'world_events': self.get_world_events(), | |
| 'chat_history': self.get_chat_history(50), # More chat history for context | |
| 'system_status': self.get_system_status(), | |
| 'loaded_plugins': self.get_loaded_plugins(), | |
| 'timestamp': time.time() | |
| } | |
| except Exception as e: | |
| print(f"[GameFacade] Error getting raw world data: {e}") | |
| return { | |
| 'error': f"Failed to get raw world data: {str(e)}", | |
| 'timestamp': time.time() | |
| } | |
| def get_recent_chat_messages(self, limit: int = 20) -> List[Dict]: | |
| """Get recent chat messages for MCP integration.""" | |
| return self.get_chat_history(limit) | |
| def get_recent_world_events(self, limit: int = 10) -> List[Dict[str, Any]]: | |
| """Get recent world events for MCP integration.""" | |
| try: | |
| all_events = self.get_world_events() | |
| return all_events[-limit:] if len(all_events) > limit else all_events | |
| except Exception as e: | |
| print(f"[GameFacade] Error getting recent world events: {e}") | |
| return [] | |
| # Plugin Operations | |
| def reload_plugin(self, plugin_id: str) -> bool: | |
| """Reload a plugin (hot-reload).""" | |
| return self.engine.get_plugin_service().reload_plugin(plugin_id) | |
| def get_loaded_plugins(self) -> List[str]: | |
| """Get list of loaded plugins.""" | |
| return self.engine.get_plugin_service().get_loaded_plugins() | |
| def get_plugin_status(self, plugin_id: str) -> Optional[Dict]: | |
| """Get status of a specific plugin.""" | |
| return self.engine.get_plugin_service().get_plugin_status(plugin_id) | |
| # World Events | |
| def get_world_events(self) -> List[Dict[str, Any]]: | |
| """Get all world events.""" | |
| try: | |
| world = self.engine.get_world() | |
| return world.get_world_events() | |
| except Exception as e: | |
| print(f"[GameFacade] Error getting world events: {e}") | |
| return [] | |
| # System Operations | |
| def get_system_status(self) -> Dict[str, Any]: | |
| """Get comprehensive system status.""" | |
| return self.engine.get_engine_status() | |
| def cleanup_inactive_players(self, timeout_seconds: int = 300) -> int: | |
| """Remove inactive players.""" | |
| return self.engine.get_player_service().cleanup_inactive_players(timeout_seconds) | |
| def is_running(self) -> bool: | |
| """Check if game is running.""" | |
| return self.engine.is_running() | |
| # Private Messaging | |
| def send_private_message(self, sender_id: str, sender_name: str, recipient_id: str, message: str) -> bool: | |
| """Send a private message between players or to NPCs.""" | |
| success, _ = self.engine.get_chat_service().send_private_message(sender_id, recipient_id, message) | |
| if success: | |
| self.engine.get_plugin_service().call_plugin_event('on_chat_message', sender_id, message, 'private') | |
| return success | |
| def get_private_messages(self, player_id: str, entity_id: str) -> List[Dict]: | |
| """Get private messages between player and entity.""" | |
| history = self.engine.get_chat_service().get_chat_history() | |
| private_msgs = [] | |
| for msg in history: | |
| msg_type = msg.get('type', '') | |
| # Check for private messages between player and entity (using backup version logic) | |
| if msg_type in ['private_to_npc', 'private_from_npc', 'private_to_player', 'private_from_player']: | |
| # Message is to/from this specific entity and player | |
| if ((msg.get('sender_id') == player_id and msg.get('target') == entity_id) or | |
| (msg.get('sender_id') == entity_id and msg.get('target') == player_id)): | |
| private_msgs.append({ | |
| 'timestamp': msg.get('timestamp', ''), | |
| 'sender': msg.get('sender', 'Unknown'), | |
| 'sender_id': msg.get('sender_id', ''), | |
| 'message': msg.get('message', ''), | |
| 'type': msg_type, | |
| 'target': msg.get('target', '') | |
| }) | |
| print(f"[GameFacade] Found {len(private_msgs)} private messages between {player_id} and {entity_id}") | |
| return private_msgs | |
| # Utility methods for UI | |
| def get_world_html(self) -> str: | |
| """Get HTML representation of the game world (for UI).""" | |
| try: | |
| players = self.get_all_players() | |
| npcs = self.get_all_npcs() | |
| # This would be implemented based on the original HTML generation logic | |
| # For now, return a placeholder | |
| return f"<div>Game World - {len(players)} players, {len(npcs)} NPCs</div>" | |
| except Exception as e: | |
| return f"<div>Error loading world: {e}</div>" | |
| def get_player_from_session(self, session_hash: str) -> Optional[Player]: | |
| """Get player by session hash.""" | |
| try: | |
| all_players = self.get_all_players() | |
| for player in all_players.values(): | |
| if player.session_hash == session_hash: | |
| return player | |
| return None | |
| except Exception as e: | |
| print(f"[GameFacade] Error getting player from session: {e}") | |
| return None | |
| def update_player_session(self, player_id: str, session_hash: str) -> bool: | |
| """Update player session hash.""" | |
| try: | |
| player = self.engine.get_player_service().get_player(player_id) | |
| if player: | |
| player.session_hash = session_hash | |
| return self.engine.get_player_service().update_player(player) | |
| return False | |
| except Exception as e: | |
| print(f"[GameFacade] Error updating player session: {e}") | |
| return False | |