Major gameplay changes: - Snakes no longer die from collisions - When blocked, snakes get "stuck" - head stays in place, tail shrinks by 1 per tick - Snakes auto-unstick when obstacle clears (other snakes move/shrink away) - Minimum snake length is 1 (head-only) - Game runs continuously without rounds or game-over state Color system: - Each player gets a persistent color for their entire connection - Colors assigned on join, rotate through available colors - Color follows player even after disconnect/reconnect - Works for both desktop and web clients Display improvements: - Show snake length instead of score - Length accurately reflects current snake size - Updates in real-time as snakes grow/shrink Server fixes: - Fixed HTTP server initialization issues - Changed default host to 0.0.0.0 for network multiplayer - Improved file serving with proper routing Testing: - Updated all collision tests for stuck mechanics - Added tests for stuck/unstick behavior - Added tests for color persistence - All 12 tests passing 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
99 lines
2.8 KiB
Python
99 lines
2.8 KiB
Python
"""Run the Snake game server."""
|
|
|
|
import asyncio
|
|
import argparse
|
|
from pathlib import Path
|
|
from src.server.game_server import GameServer
|
|
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:
|
|
"""Run the server with command line arguments."""
|
|
parser = argparse.ArgumentParser(description="Run the Snake game server")
|
|
parser.add_argument(
|
|
"--host",
|
|
default=DEFAULT_HOST,
|
|
help=f"Host address to bind to (default: {DEFAULT_HOST})",
|
|
)
|
|
parser.add_argument(
|
|
"--port",
|
|
type=int,
|
|
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",
|
|
default="Snake Server",
|
|
help="Server name for discovery (default: Snake Server)",
|
|
)
|
|
parser.add_argument(
|
|
"--no-discovery",
|
|
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,
|
|
)
|
|
|
|
# Start HTTP server if enabled
|
|
http_server = None
|
|
if not args.no_http and args.http_port > 0:
|
|
web_dir = Path(args.web_dir)
|
|
if web_dir.exists():
|
|
# Use same host as game server for HTTP
|
|
http_host = args.host if args.host != "0.0.0.0" else "localhost"
|
|
http_server = HTTPServer(web_dir, args.http_port, http_host)
|
|
await http_server.start()
|
|
else:
|
|
print(f"Warning: Web directory '{web_dir}' not found. HTTP server disabled.")
|
|
|
|
# Start game server
|
|
try:
|
|
await server.start()
|
|
finally:
|
|
if http_server:
|
|
await http_server.stop()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
asyncio.run(main())
|