#!/usr/bin/env python3 """ Debug version of the web app to isolate the black screen issue """ import asyncio import base64 import io import json import logging import os import sys from pathlib import Path from typing import Dict, List, Optional, Set import numpy as np import torch from fastapi import FastAPI, WebSocket, WebSocketDisconnect from fastapi.responses import HTMLResponse from PIL import Image # Add src to Python path src_path = Path(__file__).parent / "src" sys.path.insert(0, str(src_path)) # Configure logging logging.basicConfig(level=logging.DEBUG) logger = logging.getLogger(__name__) # Simple debug app app = FastAPI(title="Diamond CSGO AI Player - Debug") class DebugGameEngine: """Simple debug version to test image rendering""" def __init__(self): self.obs = None self.frame_count = 0 self.initialized = False def create_test_observation(self): """Create a test observation for debugging""" # Create a test image with some pattern height, width = 150, 600 # Create RGB pattern img_array = np.zeros((height, width, 3), dtype=np.uint8) # Add some pattern to make it visible for y in range(height): for x in range(width): img_array[y, x, 0] = (x % 256) # Red gradient img_array[y, x, 1] = (y % 256) # Green gradient img_array[y, x, 2] = ((x + y) % 256) # Blue pattern # Convert to torch tensor in the expected format [-1, 1] tensor = torch.from_numpy(img_array).float().permute(2, 0, 1) # CHW format tensor = tensor.div(255).mul(2).sub(1) # Convert to [-1, 1] range tensor = tensor.unsqueeze(0) # Add batch dimension logger.info(f"Created test observation: {tensor.shape}, range: {tensor.min():.3f} to {tensor.max():.3f}") return tensor def obs_to_base64(self, obs: torch.Tensor) -> str: """Convert observation tensor to base64 image for web display""" if obs is None: logger.warning("Observation is None") return "" try: logger.debug(f"Converting obs: shape={obs.shape}, dtype={obs.dtype}") # Convert tensor to PIL Image if obs.ndim == 4 and obs.size(0) == 1: img_array = obs[0].add(1).div(2).mul(255).byte().permute(1, 2, 0).cpu().numpy() else: img_array = obs.add(1).div(2).mul(255).byte().permute(1, 2, 0).cpu().numpy() logger.debug(f"Image array: shape={img_array.shape}, range={img_array.min()} to {img_array.max()}") img = Image.fromarray(img_array) # Resize for web display img = img.resize((600, 300), Image.BICUBIC) # Convert to base64 buffer = io.BytesIO() img.save(buffer, format='PNG') img_str = base64.b64encode(buffer.getvalue()).decode() logger.debug(f"Successfully converted to base64, length: {len(img_str)}") return f"data:image/png;base64,{img_str}" except Exception as e: logger.error(f"Error converting observation to base64: {e}") import traceback traceback.print_exc() return "" async def initialize(self): """Initialize with test data""" logger.info("Initializing debug game engine...") self.obs = self.create_test_observation() self.initialized = True logger.info("Debug game engine initialized successfully!") return True # Global debug engine debug_engine = DebugGameEngine() connected_clients: Set[WebSocket] = set() @app.on_event("startup") async def startup_event(): """Initialize debug engine""" success = await debug_engine.initialize() if success: # Start a simple game loop asyncio.create_task(debug_game_loop()) async def debug_game_loop(): """Simple debug game loop""" while True: try: if debug_engine.initialized and connected_clients: # Send test frame to all connected clients frame_data = { 'type': 'frame', 'image': debug_engine.obs_to_base64(debug_engine.obs), 'frame_count': debug_engine.frame_count, 'reward': 0.0, 'info': f"Debug frame {debug_engine.frame_count}" } logger.debug(f"Sending frame {debug_engine.frame_count}") # Send to all connected clients disconnected = set() for client in connected_clients.copy(): try: await client.send_text(json.dumps(frame_data)) except Exception as e: logger.error(f"Error sending to client: {e}") disconnected.add(client) # Remove disconnected clients connected_clients.difference_update(disconnected) debug_engine.frame_count += 1 await asyncio.sleep(1.0 / 15) # 15 FPS except Exception as e: logger.error(f"Error in debug game loop: {e}") await asyncio.sleep(0.1) @app.get("/", response_class=HTMLResponse) async def get_homepage(): """Serve debug interface""" html_content = """ Diamond CSGO AI - Debug

🔧 Diamond CSGO AI - Debug Mode

Testing image rendering and WebSocket communication

Status: Connecting...
Frame: 0
Info: Waiting...

Debug Information

No updates yet

No image data

""" return html_content @app.websocket("/ws") async def websocket_endpoint(websocket: WebSocket): """Handle WebSocket connections""" await websocket.accept() connected_clients.add(websocket) logger.info(f"Client connected. Total clients: {len(connected_clients)}") try: while True: # Just wait for disconnection await websocket.receive_text() except WebSocketDisconnect: connected_clients.discard(websocket) logger.info(f"Client disconnected. Total clients: {len(connected_clients)}") except Exception as e: logger.error(f"WebSocket error: {e}") connected_clients.discard(websocket) if __name__ == "__main__": import uvicorn uvicorn.run("debug_app:app", host="0.0.0.0", port=7861, reload=False)