200 lines
6.6 KiB
Python
200 lines
6.6 KiB
Python
"""
|
|
WebRTC Signaling Handlers
|
|
|
|
This module contains WebRTC signaling message handlers for peer-to-peer communication.
|
|
Handles ICE candidate relay and session description exchange between peers.
|
|
"""
|
|
|
|
from typing import Any, Dict, TYPE_CHECKING
|
|
from fastapi import WebSocket
|
|
|
|
from logger import logger
|
|
|
|
if TYPE_CHECKING:
|
|
from core.session_manager import Session
|
|
from core.lobby_manager import Lobby
|
|
|
|
|
|
class WebRTCSignalingHandlers:
|
|
"""WebRTC signaling message handlers for peer-to-peer communication."""
|
|
|
|
@staticmethod
|
|
async def handle_relay_ice_candidate(
|
|
websocket: WebSocket,
|
|
session: "Session",
|
|
lobby: "Lobby",
|
|
data: Dict[str, Any]
|
|
) -> None:
|
|
"""
|
|
Handle ICE candidate relay between peers.
|
|
|
|
Args:
|
|
websocket: The WebSocket connection
|
|
session: The sender session
|
|
lobby: The lobby context
|
|
data: Message data containing peer_id and candidate
|
|
"""
|
|
logger.info(f"{session.getName()} <- relayICECandidate")
|
|
|
|
if not data:
|
|
logger.error(f"{session.getName()} - relayICECandidate missing data")
|
|
await websocket.send_json({
|
|
"type": "error",
|
|
"data": {"error": "relayICECandidate missing data"}
|
|
})
|
|
return
|
|
|
|
# Check if session is properly joined to lobby with RTC peers
|
|
with session.session_lock:
|
|
if (lobby.id not in session.lobby_peers or
|
|
session.id not in lobby.sessions):
|
|
logger.error(
|
|
f"{session.short}:{session.name} <- relayICECandidate - "
|
|
f"Not an RTC peer ({session.id})"
|
|
)
|
|
await websocket.send_json({
|
|
"type": "error",
|
|
"data": {"error": "Not joined to lobby"}
|
|
})
|
|
return
|
|
|
|
session_peers = session.lobby_peers[lobby.id]
|
|
|
|
# Validate peer_id
|
|
peer_id = data.get("peer_id")
|
|
if peer_id not in session_peers:
|
|
logger.error(
|
|
f"{session.getName()} <- relayICECandidate - "
|
|
f"Not an RTC peer({peer_id}) in {session_peers}"
|
|
)
|
|
await websocket.send_json({
|
|
"type": "error",
|
|
"data": {"error": f"Target peer {peer_id} not found"}
|
|
})
|
|
return
|
|
|
|
# Get candidate data
|
|
candidate = data.get("candidate")
|
|
|
|
# Prepare message for target peer
|
|
message: Dict[str, Any] = {
|
|
"type": "iceCandidate",
|
|
"data": {
|
|
"peer_id": session.id,
|
|
"peer_name": session.name,
|
|
"candidate": candidate,
|
|
},
|
|
}
|
|
|
|
# Find target peer session and relay the message
|
|
peer_session = lobby.getSession(peer_id)
|
|
if not peer_session or not peer_session.ws:
|
|
logger.warning(
|
|
f"{session.getName()} - Live peer session {peer_id} "
|
|
f"not found in lobby {lobby.getName()}."
|
|
)
|
|
return
|
|
|
|
logger.info(
|
|
f"{session.getName()} -> iceCandidate({peer_session.getName()})"
|
|
)
|
|
|
|
try:
|
|
await peer_session.ws.send_json(message)
|
|
except Exception as e:
|
|
logger.warning(f"Failed to relay ICE candidate: {e}")
|
|
|
|
@staticmethod
|
|
async def handle_relay_session_description(
|
|
websocket: WebSocket,
|
|
session: "Session",
|
|
lobby: "Lobby",
|
|
data: Dict[str, Any]
|
|
) -> None:
|
|
"""
|
|
Handle session description relay between peers.
|
|
|
|
Args:
|
|
websocket: The WebSocket connection
|
|
session: The sender session
|
|
lobby: The lobby context
|
|
data: Message data containing peer_id and session_description
|
|
"""
|
|
logger.info(f"{session.getName()} <- relaySessionDescription")
|
|
|
|
if not data:
|
|
logger.error(f"{session.getName()} - relaySessionDescription missing data")
|
|
await websocket.send_json({
|
|
"type": "error",
|
|
"data": {"error": "relaySessionDescription missing data"}
|
|
})
|
|
return
|
|
|
|
# Check if session is properly joined to lobby with RTC peers
|
|
with session.session_lock:
|
|
if (lobby.id not in session.lobby_peers or
|
|
session.id not in lobby.sessions):
|
|
logger.error(
|
|
f"{session.short}:{session.name} <- relaySessionDescription - "
|
|
f"Not an RTC peer ({session.id})"
|
|
)
|
|
await websocket.send_json({
|
|
"type": "error",
|
|
"data": {"error": "Not joined to lobby"}
|
|
})
|
|
return
|
|
|
|
lobby_peers = session.lobby_peers[lobby.id]
|
|
|
|
# Validate peer_id
|
|
peer_id = data.get("peer_id")
|
|
if not peer_id:
|
|
logger.error(f"{session.getName()} - relaySessionDescription missing peer_id")
|
|
await websocket.send_json({
|
|
"type": "error",
|
|
"data": {"error": "relaySessionDescription missing peer_id"}
|
|
})
|
|
return
|
|
|
|
if peer_id not in lobby_peers:
|
|
logger.error(
|
|
f"{session.getName()} <- relaySessionDescription - "
|
|
f"Not an RTC peer({peer_id}) in {lobby_peers}"
|
|
)
|
|
await websocket.send_json({
|
|
"type": "error",
|
|
"data": {"error": f"Target peer {peer_id} not found"}
|
|
})
|
|
return
|
|
|
|
# Find target peer session
|
|
peer_session = lobby.getSession(peer_id)
|
|
if not peer_session or not peer_session.ws:
|
|
logger.warning(
|
|
f"{session.getName()} - Live peer session {peer_id} "
|
|
f"not found in lobby {lobby.getName()}."
|
|
)
|
|
return
|
|
|
|
# Get session description data
|
|
session_description = data.get("session_description")
|
|
|
|
# Prepare message for target peer
|
|
message: Dict[str, Any] = {
|
|
"type": "sessionDescription",
|
|
"data": {
|
|
"peer_id": session.id,
|
|
"peer_name": session.name,
|
|
"session_description": session_description,
|
|
},
|
|
}
|
|
|
|
logger.info(
|
|
f"{session.getName()} -> sessionDescription({peer_session.getName()})"
|
|
)
|
|
|
|
try:
|
|
await peer_session.ws.send_json(message)
|
|
except Exception as e:
|
|
logger.warning(f"Failed to relay session description: {e}")
|