Add web client with WebSocket support
Implemented browser-based web client alongside existing pygame desktop client with dual-protocol server architecture supporting both TCP and WebSocket. Backend Changes: - Refactored GameServer for dual-protocol support (TCP + WebSocket) - Added WebSocketHandler for handling WebSocket connections - Added HTTPServer using aiohttp for serving web client files - Updated protocol handling to work with both connection types - Server tracks clients with protocol metadata (TCP/WebSocket) - Protocol-agnostic message sending and broadcasting - Added WebSocket port (8889) and HTTP port (8000) configuration Web Client: - Complete HTML5/CSS/JavaScript implementation - Responsive dark-themed UI - HTML5 Canvas rendering matching pygame visual style - WebSocket connection with auto-detected server URL - Real-time multiplayer gameplay in browser - Player list with scores and status - Mobile-friendly responsive design Deployment Options: - Development: Built-in HTTP server for local testing - Production: Disable HTTP server, use nginx/Apache for static files - Flexible server configuration (--no-http, --no-websocket flags) - Comprehensive nginx/Apache deployment documentation New Files: - src/server/websocket_handler.py - WebSocket connection handler - src/server/http_server.py - Static file server - web/index.html - Web client interface - web/style.css - Responsive styling - web/protocol.js - Protocol implementation - web/game.js - Game client with Canvas rendering - web/README.md - Deployment documentation Updated Files: - requirements.txt - Added websockets and aiohttp dependencies - src/server/game_server.py - Dual-protocol support - src/shared/constants.py - WebSocket and HTTP port constants - run_server.py - Server options for web support - README.md - Web client documentation - CLAUDE.md - Architecture documentation Features: - Web and desktop clients can play together simultaneously - Same JSON protocol for both client types - Independent server components (disable what you don't need) - Production-ready with reverse proxy support 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -2,8 +2,10 @@
|
||||
|
||||
import asyncio
|
||||
import argparse
|
||||
from pathlib import Path
|
||||
from src.server.game_server import GameServer
|
||||
from src.shared.constants import DEFAULT_HOST, DEFAULT_PORT
|
||||
from src.server.http_server import HTTPServer
|
||||
from src.shared.constants import DEFAULT_HOST, DEFAULT_PORT, DEFAULT_WS_PORT, DEFAULT_HTTP_PORT
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
@@ -18,7 +20,24 @@ async def main() -> None:
|
||||
"--port",
|
||||
type=int,
|
||||
default=DEFAULT_PORT,
|
||||
help=f"Port number to bind to (default: {DEFAULT_PORT})",
|
||||
help=f"TCP port number (default: {DEFAULT_PORT})",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--ws-port",
|
||||
type=int,
|
||||
default=DEFAULT_WS_PORT,
|
||||
help=f"WebSocket port (default: {DEFAULT_WS_PORT}, 0 to disable)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--http-port",
|
||||
type=int,
|
||||
default=DEFAULT_HTTP_PORT,
|
||||
help=f"HTTP server port (default: {DEFAULT_HTTP_PORT}, 0 to disable)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--web-dir",
|
||||
default="web",
|
||||
help="Directory containing web client files (default: web)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--name",
|
||||
@@ -30,17 +49,48 @@ async def main() -> None:
|
||||
action="store_true",
|
||||
help="Disable multicast discovery beacon",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--no-websocket",
|
||||
action="store_true",
|
||||
help="Disable WebSocket server",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--no-http",
|
||||
action="store_true",
|
||||
help="Disable HTTP server (for production with external web server)",
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# Determine WebSocket port
|
||||
ws_port = None if args.no_websocket or args.ws_port == 0 else args.ws_port
|
||||
|
||||
# Create game server
|
||||
server = GameServer(
|
||||
host=args.host,
|
||||
port=args.port,
|
||||
server_name=args.name,
|
||||
enable_discovery=not args.no_discovery,
|
||||
ws_port=ws_port,
|
||||
)
|
||||
|
||||
await server.start()
|
||||
# Start HTTP server if enabled
|
||||
http_task = None
|
||||
if not args.no_http and args.http_port > 0:
|
||||
web_dir = Path(args.web_dir)
|
||||
if web_dir.exists():
|
||||
http_server = HTTPServer(web_dir, args.http_port, "0.0.0.0")
|
||||
await http_server.start()
|
||||
http_task = asyncio.create_task(asyncio.Future()) # Keep running
|
||||
else:
|
||||
print(f"Warning: Web directory '{web_dir}' not found. HTTP server disabled.")
|
||||
|
||||
# Start game server
|
||||
try:
|
||||
await server.start()
|
||||
finally:
|
||||
if http_task:
|
||||
http_task.cancel()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
Reference in New Issue
Block a user