#!/usr/bin/env python3 """ Simple MMORPG Game Client ======================== A basic client implementation for connecting to the MMORPG game server via MCP. This client can join the game, move around, chat, and interact with NPCs. Requirements: pip install mcp aiohttp asyncio Usage: python simple_game_client.py Features: - Connect to game server via MCP - Register as a player - Move around the game world - Send chat messages - Interact with NPCs - Get game state information - Interactive console interface """ import asyncio import json import uuid import sys from typing import Dict, List, Optional try: from mcp import ClientSession from mcp.client.sse import sse_client from contextlib import AsyncExitStack except ImportError: print("āŒ Missing dependencies. Install with: pip install mcp aiohttp") sys.exit(1) class SimpleGameClient: """Simple client for connecting to the MMORPG game server""" def __init__(self, server_url="http://127.0.0.1:7868/gradio_api/mcp/sse"): self.server_url = server_url self.session = None self.connected = False self.tools = [] self.exit_stack = None self.client_id = f"client_{uuid.uuid4().hex[:8]}" self.agent_id = None self.player_name = None print(f"šŸŽ® Game Client initialized with ID: {self.client_id}") async def connect(self): """Connect to the game server""" print(f"šŸ”Œ Connecting to {self.server_url}...") try: # Clean up any existing connection if self.exit_stack: await self.exit_stack.aclose() self.exit_stack = AsyncExitStack() # Establish SSE connection to MCP server transport = await self.exit_stack.enter_async_context( sse_client(self.server_url) ) read_stream, write_callable = transport # Create MCP client session self.session = await self.exit_stack.enter_async_context( ClientSession(read_stream, write_callable) ) # Initialize the session await self.session.initialize() # Get available tools/commands from server response = await self.session.list_tools() self.tools = response.tools self.connected = True tool_names = [tool.name for tool in self.tools] print(f"āœ… Connected successfully!") print(f"šŸ“‹ Available commands: {', '.join(tool_names)}") return True except Exception as e: print(f"āŒ Connection failed: {e}") self.connected = False return False async def register_player(self, player_name: str): """Register as a new player in the game""" if not self.connected: print("āŒ Not connected to server") return False print(f"šŸ‘¤ Registering player: {player_name}") try: # Find the register tool register_tool = self._find_tool(['register', 'agent']) if not register_tool: print("āŒ Register tool not available on server") return False # Register the player result = await self.session.call_tool( register_tool.name, {"name": player_name, "client_id": self.client_id} ) # Parse the result content = self._extract_content(result) print(f"šŸ“ Registration response: {content}") if "registered" in content.lower() or "success" in content.lower(): self.player_name = player_name # Try to extract agent_id from response self._parse_agent_id(content) print(f"āœ… Successfully registered as: {player_name}") return True else: print(f"āŒ Registration failed: {content}") return False except Exception as e: print(f"āŒ Registration error: {e}") return False async def move_player(self, direction: str): """Move the player in the specified direction""" if not self._check_ready(): return False direction = direction.lower() valid_directions = ['north', 'south', 'east', 'west', 'up', 'down'] if direction not in valid_directions: print(f"āŒ Invalid direction. Use: {', '.join(valid_directions)}") return False try: # Find the move tool move_tool = self._find_tool(['move', 'agent']) if not move_tool: print("āŒ Move tool not available") return False # Execute the move result = await self.session.call_tool( move_tool.name, {"client_id": self.client_id, "direction": direction} ) content = self._extract_content(result) print(f"🚶 Move {direction}: {content}") return True except Exception as e: print(f"āŒ Move error: {e}") return False async def send_chat(self, message: str): """Send a chat message to other players""" if not self._check_ready(): return False if not message.strip(): print("āŒ Cannot send empty message") return False try: # Find the chat tool chat_tool = self._find_tool(['chat', 'send']) if not chat_tool: print("āŒ Chat tool not available") return False # Send the chat message result = await self.session.call_tool( chat_tool.name, {"client_id": self.client_id, "message": message} ) content = self._extract_content(result) print(f"šŸ’¬ Chat sent: {content}") return True except Exception as e: print(f"āŒ Chat error: {e}") return False async def get_game_state(self): """Get current game state information""" if not self.connected: print("āŒ Not connected to server") return None try: # Find the game state tool state_tool = self._find_tool(['state', 'game']) if not state_tool: print("āŒ Game state tool not available") return None # Get game state result = await self.session.call_tool(state_tool.name, {}) content = self._extract_content(result) try: # Try to parse as JSON game_state = json.loads(content) return game_state except json.JSONDecodeError: # Return as text if not JSON return {"info": content} except Exception as e: print(f"āŒ Game state error: {e}") return None async def interact_with_npc(self, npc_id: str, message: str): """Interact with an NPC""" if not self._check_ready(): return False if not npc_id.strip() or not message.strip(): print("āŒ NPC ID and message are required") return False try: # Find the interact tool interact_tool = self._find_tool(['interact', 'npc']) if not interact_tool: print("āŒ NPC interaction tool not available") return False # Interact with NPC result = await self.session.call_tool( interact_tool.name, {"client_id": self.client_id, "npc_id": npc_id, "message": message} ) content = self._extract_content(result) print(f"šŸ¤– {npc_id}: {content}") return True except Exception as e: print(f"āŒ NPC interaction error: {e}") return False async def list_tools(self): """List all available tools on the server""" if not self.connected: print("āŒ Not connected to server") return print("šŸ› ļø Available Tools:") for i, tool in enumerate(self.tools, 1): print(f" {i}. {tool.name}") if hasattr(tool, 'description') and tool.description: print(f" Description: {tool.description}") def _find_tool(self, keywords: List[str]): """Find a tool by keywords""" for keyword in keywords: tool = next((t for t in self.tools if keyword in t.name.lower()), None) if tool: return tool return None def _extract_content(self, result): """Extract content from MCP result""" try: if hasattr(result, 'content') and result.content: if isinstance(result.content, list): content_parts = [] for item in result.content: if hasattr(item, 'text'): content_parts.append(item.text) else: content_parts.append(str(item)) return ''.join(content_parts) elif hasattr(result.content, 'text'): return result.content.text else: return str(result.content) return str(result) except Exception as e: return f"Error extracting content: {e}" def _parse_agent_id(self, content: str): """Try to extract agent ID from response""" lines = content.split('\n') for line in lines: if any(keyword in line.lower() for keyword in ['agent_id', 'id:', 'player id']): parts = line.split(':') if len(parts) > 1: self.agent_id = parts[1].strip() print(f"šŸ†” Agent ID: {self.agent_id}") break def _check_ready(self): """Check if client is ready for game operations""" if not self.connected: print("āŒ Not connected to server") return False if not self.player_name: print("āŒ Not registered as a player") return False return True async def disconnect(self): """Disconnect from the server""" if self.exit_stack: await self.exit_stack.aclose() self.connected = False self.player_name = None self.agent_id = None print("šŸ”Œ Disconnected from server") class InteractiveGameClient: """Interactive console interface for the game client""" def __init__(self): self.client = SimpleGameClient() self.running = True async def start(self): """Start the interactive client""" print("šŸŽ® MMORPG Simple Game Client") print("=" * 40) self.show_help() while self.running: try: command = input("\n> ").strip() if command: await self.process_command(command) except KeyboardInterrupt: print("\n\nšŸ‘‹ Goodbye!") break except EOFError: break await self.client.disconnect() async def process_command(self, command: str): """Process a user command""" parts = command.split() if not parts: return cmd = parts[0].lower() args = parts[1:] if cmd == "help": self.show_help() elif cmd == "connect": await self.client.connect() elif cmd == "register": if args: name = " ".join(args) await self.client.register_player(name) else: print("āŒ Usage: register ") elif cmd == "move": if args: direction = args[0] await self.client.move_player(direction) else: print("āŒ Usage: move ") print(" Directions: north, south, east, west") elif cmd == "chat": if args: message = " ".join(args) await self.client.send_chat(message) else: print("āŒ Usage: chat ") elif cmd == "state": state = await self.client.get_game_state() if state: print("šŸ“Š Game State:") print(json.dumps(state, indent=2)) elif cmd == "npc": if len(args) >= 2: npc_id = args[0] message = " ".join(args[1:]) await self.client.interact_with_npc(npc_id, message) else: print("āŒ Usage: npc ") print(" Example: npc weather_oracle What's the weather in Berlin?") elif cmd == "tools": await self.client.list_tools() elif cmd in ["quit", "exit", "q"]: self.running = False else: print(f"ā“ Unknown command: {cmd}") print(" Type 'help' for available commands") def show_help(self): """Show available commands""" print("\nšŸ“‹ Available Commands:") print(" help - Show this help") print(" connect - Connect to game server") print(" register - Register as a player") print(" move - Move player (north/south/east/west)") print(" chat - Send chat message") print(" npc - Talk to an NPC") print(" state - Get game state") print(" tools - List available server tools") print(" quit - Exit the client") print("\nšŸ’” Example session:") print(" > connect") print(" > register MyPlayer") print(" > move north") print(" > chat Hello everyone!") print(" > npc weather_oracle What's the weather?") async def quick_demo(): """Quick demo showing basic functionality""" print("šŸš€ Running Quick Demo...") client = SimpleGameClient() # Connect if not await client.connect(): print("āŒ Demo failed - could not connect") return # Register if not await client.register_player("DemoPlayer"): print("āŒ Demo failed - could not register") return # Send greeting await client.send_chat("Hello! Demo player here!") # Move around moves = ["north", "east", "south", "west"] for direction in moves: await client.move_player(direction) await asyncio.sleep(1) # Get state state = await client.get_game_state() if state: print(f"šŸ“Š Current game state: {json.dumps(state, indent=2)}") # Try NPC interaction await client.interact_with_npc("weather_oracle", "Hello there!") await client.disconnect() print("āœ… Demo completed!") def main(): """Main entry point""" if len(sys.argv) > 1 and sys.argv[1] == "demo": # Run quick demo asyncio.run(quick_demo()) else: # Run interactive client client = InteractiveGameClient() asyncio.run(client.start()) if __name__ == "__main__": main()