# Simple Game Client Guide This guide shows you how to create a simple client to connect to the MMORPG game server using the Model Context Protocol (MCP). No LLM integration required - just basic game operations. ## Table of Contents 1. [Overview](#overview) 2. [Prerequisites](#prerequisites) 3. [MCP Server Connection](#mcp-server-connection) 4. [Basic Client Example](#basic-client-example) 5. [Available Game Operations](#available-game-operations) 6. [Command Reference](#command-reference) 7. [Error Handling](#error-handling) 8. [Advanced Features](#advanced-features) --- ## Overview The MMORPG game server exposes its functionality through MCP (Model Context Protocol), allowing external clients to: - 🎮 **Connect** to the game server - 👤 **Join/Leave** the game as a player - 🗺️ **Move** around the game world - 💬 **Send chat** messages to other players - 🎯 **Interact** with NPCs and game objects - 📊 **Get game state** information ### Game Server Details - **MCP Endpoint**: `http://localhost:7868/gradio_api/mcp/sse` (when running locally) - **Protocol**: Server-Sent Events (SSE) over HTTP - **Authentication**: None required for basic operations - **Data Format**: JSON messages following MCP specification --- ## Prerequisites Before building your client, ensure you have: ### Required Dependencies ```bash pip install mcp asyncio aiohttp contextlib ``` ### Python Version - Python 3.8 or higher - Support for async/await syntax ### Game Server Running Make sure the MMORPG game server is running: ```bash cd c:/Users/Chris4K/Projekte/projecthub/projects/MMOP_second_try python app.py ``` The server should be accessible at `http://localhost:7868` (UI) and the MCP endpoint at `http://localhost:7868/gradio_api/mcp/sse`. --- ## MCP Server Connection Here's how to establish a connection to the game server: ### Basic Connection Setup ```python import asyncio from mcp import ClientSession from mcp.client.sse import sse_client from contextlib import AsyncExitStack class GameClient: def __init__(self, server_url="http://localhost: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 = None async def connect(self): """Connect to the game server""" try: # Clean up any existing connection if self.exit_stack: await self.exit_stack.aclose() self.exit_stack = AsyncExitStack() # Establish SSE connection transport = await self.exit_stack.enter_async_context( sse_client(self.server_url) ) read_stream, write_callable = transport # Create MCP 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 response = await self.session.list_tools() self.tools = response.tools self.connected = True print(f"✅ Connected to game server!") print(f"Available commands: {[tool.name for tool in self.tools]}") return True except Exception as e: print(f"❌ Connection failed: {e}") self.connected = False return False async def disconnect(self): """Disconnect from the game server""" if self.exit_stack: await self.exit_stack.aclose() self.connected = False print("🔌 Disconnected from game server") ``` ### Connection Test ```python async def test_connection(): client = GameClient() if await client.connect(): print("Connection successful!") await client.disconnect() else: print("Connection failed!") # Run the test asyncio.run(test_connection()) ``` --- ## Basic Client Example Here's a complete working client that can perform basic game operations: ```python import asyncio import json import uuid from typing import Dict, List, Optional class SimpleGameClient: def __init__(self, server_url="http://localhost: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 = str(uuid.uuid4())[:8] self.agent_id = None self.player_name = None async def connect(self): """Connect to the game server""" try: if self.exit_stack: await self.exit_stack.aclose() self.exit_stack = AsyncExitStack() transport = await self.exit_stack.enter_async_context( sse_client(self.server_url) ) read_stream, write_callable = transport self.session = await self.exit_stack.enter_async_context( ClientSession(read_stream, write_callable) ) await self.session.initialize() response = await self.session.list_tools() self.tools = response.tools self.connected = True return True except Exception as e: print(f"❌ Connection failed: {e}") 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 try: # Find the register tool register_tool = next((t for t in self.tools if 'register' in t.name), None) if not register_tool: print("❌ Register tool not available") 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) if "registered" in content.lower(): self.player_name = player_name # Extract agent_id from response if available if "agent_id" in content or "ID:" in content: # Parse agent ID from response lines = content.split('\n') for line in lines: if "agent_id" in line.lower() or "id:" in line: parts = line.split(':') if len(parts) > 1: self.agent_id = parts[1].strip() break print(f"✅ Registered as player: {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.connected or not self.agent_id: print("❌ Not connected or not registered") return False try: # Find the move tool move_tool = next((t for t in self.tools if 'move' in t.name), None) if not move_tool: print("❌ Move tool not available") return False # Move the player result = await self.session.call_tool( move_tool.name, {"client_id": self.client_id, "direction": direction} ) content = self._extract_content(result) print(f"🚶 Move result: {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.connected or not self.agent_id: print("❌ Not connected or not registered") return False try: # Find the chat tool chat_tool = next((t for t in self.tools if 'chat' in t.name), None) 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 = next((t for t in self.tools if 'state' in t.name or 'game' in t.name), None) 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.connected or not self.agent_id: print("❌ Not connected or not registered") return False try: # Find the interact tool interact_tool = next((t for t in self.tools if 'interact' in t.name or 'npc' in t.name), None) if not interact_tool: print("❌ Interact 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 {npc_id}: {content}") return True except Exception as e: print(f"❌ Interaction error: {e}") return False def _extract_content(self, result): """Extract content from MCP result""" if hasattr(result, 'content') and result.content: if isinstance(result.content, list): return ''.join(str(item) for item in result.content) return str(result.content) return str(result) async def disconnect(self): """Disconnect from the server""" if self.exit_stack: await self.exit_stack.aclose() self.connected = False print("🔌 Disconnected from server") # Import required modules at the top from mcp import ClientSession from mcp.client.sse import sse_client from contextlib import AsyncExitStack ``` --- ## Available Game Operations The game server exposes these main operations through MCP tools: ### 1. Player Management - **`register_ai_agent`** - Register a new player - **`move_agent`** - Move player in game world - **`get_game_state`** - Get current world state ### 2. Communication - **`send_chat`** - Send chat messages - **`interact_with_npc`** - Talk to NPCs ### 3. Game Information - **`get_game_state`** - World state and player list - **Player proximity** - Detect nearby players/NPCs ### Tool Parameters Each tool expects specific parameters: ```python # Register Player { "name": "PlayerName", "client_id": "unique_client_id" } # Move Player { "client_id": "your_client_id", "direction": "north|south|east|west" } # Send Chat { "client_id": "your_client_id", "message": "Hello world!" } # Interact with NPC { "client_id": "your_client_id", "npc_id": "npc_identifier", "message": "Your message to NPC" } ``` --- ## Command Reference ### Basic Usage Example ```python async def main(): # Create and connect client client = SimpleGameClient() if not await client.connect(): print("Failed to connect!") return # Register as a player if not await client.register_player("MyPlayer"): print("Failed to register!") return # Get current game state state = await client.get_game_state() print(f"Game state: {state}") # Move around await client.move_player("north") await client.move_player("east") # Send a chat message await client.send_chat("Hello everyone!") # Interact with an NPC await client.interact_with_npc("weather_oracle", "What's the weather in Berlin?") # Disconnect await client.disconnect() # Run the client asyncio.run(main()) ``` ### Interactive Console Client ```python async def interactive_client(): client = SimpleGameClient() print("🎮 MMORPG Simple Client") print("Commands: connect, register , move , chat , state, quit") while True: command = input("> ").strip().split() if not command: continue cmd = command[0].lower() if cmd == "connect": if await client.connect(): print("✅ Connected!") else: print("❌ Connection failed!") elif cmd == "register" and len(command) > 1: name = " ".join(command[1:]) if await client.register_player(name): print(f"✅ Registered as {name}") else: print("❌ Registration failed!") elif cmd == "move" and len(command) > 1: direction = command[1] await client.move_player(direction) elif cmd == "chat" and len(command) > 1: message = " ".join(command[1:]) await client.send_chat(message) elif cmd == "state": state = await client.get_game_state() print(f"📊 Game State: {json.dumps(state, indent=2)}") elif cmd == "npc" and len(command) > 2: npc_id = command[1] message = " ".join(command[2:]) await client.interact_with_npc(npc_id, message) elif cmd == "quit": await client.disconnect() break else: print("❓ Unknown command") # Run interactive client asyncio.run(interactive_client()) ``` --- ## Error Handling ### Common Issues and Solutions #### 1. Connection Errors ```python async def robust_connect(client, max_retries=3): for attempt in range(max_retries): try: if await client.connect(): return True print(f"Attempt {attempt + 1} failed, retrying...") await asyncio.sleep(2) except Exception as e: print(f"Connection attempt {attempt + 1} error: {e}") print("❌ All connection attempts failed") return False ``` #### 2. Tool Not Available ```python def find_tool_safe(tools, keywords): """Safely find a tool by keywords""" for keyword in keywords: tool = next((t for t in tools if keyword in t.name.lower()), None) if tool: return tool return None # Usage move_tool = find_tool_safe(client.tools, ['move', 'agent', 'player']) if not move_tool: print("❌ No movement tool available") ``` #### 3. Response Parsing ```python def safe_parse_response(result): """Safely parse MCP response""" try: content = client._extract_content(result) # Try JSON first try: return json.loads(content) except json.JSONDecodeError: # Return as structured text return {"message": content, "type": "text"} except Exception as e: return {"error": str(e), "type": "error"} ``` --- ## Advanced Features ### 1. Automatic Reconnection ```python class RobustGameClient(SimpleGameClient): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.auto_reconnect = True self.reconnect_delay = 5 async def _ensure_connected(self): """Ensure connection is active, reconnect if needed""" if not self.connected and self.auto_reconnect: print("🔄 Attempting to reconnect...") await self.connect() if self.player_name: await self.register_player(self.player_name) async def move_player(self, direction): await self._ensure_connected() return await super().move_player(direction) ``` ### 2. Event Monitoring ```python class EventMonitorClient(SimpleGameClient): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.event_handlers = {} def on_chat_message(self, handler): """Register handler for chat messages""" self.event_handlers['chat'] = handler def on_player_move(self, handler): """Register handler for player movements""" self.event_handlers['move'] = handler async def poll_events(self): """Poll for game events""" while self.connected: try: state = await self.get_game_state() # Process state changes and trigger handlers # Implementation depends on game state format await asyncio.sleep(1) except Exception as e: print(f"Event polling error: {e}") await asyncio.sleep(5) ``` ### 3. Batch Operations ```python async def batch_moves(client, moves): """Execute multiple moves in sequence""" for direction in moves: result = await client.move_player(direction) if not result: print(f"❌ Failed to move {direction}") break await asyncio.sleep(0.5) # Small delay between moves # Usage await batch_moves(client, ["north", "north", "east", "south"]) ``` ### 4. Configuration Management ```python import json class ConfigurableClient(SimpleGameClient): def __init__(self, config_file="client_config.json"): # Load configuration try: with open(config_file, 'r') as f: config = json.load(f) except FileNotFoundError: config = self.default_config() super().__init__(config.get('server_url', 'http://localhost:7860/gradio_api/mcp/sse')) self.config = config def default_config(self): return { "server_url": "http://localhost:7860/gradio_api/mcp/sse", "player_name": "DefaultPlayer", "auto_reconnect": True, "move_delay": 0.5 } ``` --- ## Quick Start Script Save this as `quick_client.py` for immediate testing: ```python #!/usr/bin/env python3 """ Quick Game Client - Connect and play immediately Usage: python quick_client.py [player_name] """ import asyncio import sys from simple_game_client import SimpleGameClient async def quick_play(player_name="TestPlayer"): client = SimpleGameClient() print(f"🎮 Connecting as {player_name}...") # Connect and register if not await client.connect(): print("❌ Failed to connect!") return if not await client.register_player(player_name): print("❌ Failed to register!") return print("✅ Connected and registered successfully!") # Basic game session await client.send_chat(f"Hello! {player_name} has joined the game!") # Move around a bit moves = ["north", "east", "south", "west"] for move in moves: print(f"🚶 Moving {move}...") await client.move_player(move) await asyncio.sleep(1) # Get final state state = await client.get_game_state() print(f"📊 Final state: {state}") await client.disconnect() print("👋 Session ended!") if __name__ == "__main__": player_name = sys.argv[1] if len(sys.argv) > 1 else "TestPlayer" asyncio.run(quick_play(player_name)) ``` Run with: ```bash python quick_client.py MyPlayerName ``` --- ## Next Steps 1. **Customize the client** for your specific needs 2. **Add game-specific features** like inventory management 3. **Implement advanced AI** for automated gameplay 4. **Create GUI interfaces** using tkinter, PyQt, or web frameworks 5. **Build monitoring tools** for game statistics This guide provides the foundation for any client application that needs to interact with the MMORPG game server!