import { Text } from "@nestri/www/ui/text"; import { createSignal, createEffect, onCleanup, onMount, Show } from "solid-js"; import { useParams } from "@solidjs/router"; import { Keyboard, Mouse, WebRTCStream } from "@nestri/input"; import { Container, FullScreen } from "@nestri/www/ui/layout"; export function PlayComponent() { const params = useParams(); const id = params.id; const [showBannerModal, setShowBannerModal] = createSignal(false); const [showButtonModal, setShowButtonModal] = createSignal(false); const [gamepadConnected, setGamepadConnected] = createSignal(false); const [buttonPressed, setButtonPressed] = createSignal(null); const [leftStickX, setLeftStickX] = createSignal(0); const [leftStickY, setLeftStickY] = createSignal(0); const [hasStream, setHasStream] = createSignal(false); const [nestriLock, setNestriLock] = createSignal(false); const [showOffline, setShowOffline] = createSignal(false); const [canvas, setCanvas] = createSignal(undefined); let video: HTMLVideoElement; let webrtc: WebRTCStream; let nestriMouse: Mouse , nestriKeyboard: Keyboard; const initializeInputDevices = () => { const canvasElement = canvas(); if (!canvasElement || !webrtc) return; try { nestriMouse = new Mouse({ canvas: canvasElement, webrtc }); nestriKeyboard = new Keyboard({ canvas: canvasElement, webrtc }); console.log("Input devices initialized successfully"); } catch (error) { console.error("Failed to initialize input devices:", error); } }; /*const initializeGamepad = () => { console.log("Initializing gamepad..."); const updateGamepadState = () => { const gamepads = navigator.getGamepads(); const gamepad = gamepads[0]; if (gamepad) { setButtonPressed(gamepad.buttons.findIndex(btn => btn.pressed) !== -1 ? "Button pressed" : null); setLeftStickX(Number(gamepad.axes[0].toFixed(2))); setLeftStickY(Number(gamepad.axes[1].toFixed(2))); } requestAnimationFrame(updateGamepadState); }; window.addEventListener("gamepadconnected", () => { setGamepadConnected(true); console.log("Gamepad connected!"); updateGamepadState(); }); window.addEventListener("gamepaddisconnected", () => { setGamepadConnected(false); console.log("Gamepad disconnected!"); }); };*/ const lockPlay = async () => { const canvasElement = canvas(); if (!canvasElement || !hasStream()) return; try { await canvasElement.requestPointerLock(); await canvasElement.requestFullscreen(); //initializeGamepad(); if (document.fullscreenElement !== null) { if ('keyboard' in navigator && 'lock' in (navigator.keyboard as any)) { const keys = [ "AltLeft", "AltRight", "Tab", "Escape", "ContextMenu", "MetaLeft", "MetaRight" ]; try { await (navigator.keyboard as any).lock(keys); setNestriLock(true); console.log("Keyboard lock acquired"); } catch (e) { console.warn("Keyboard lock failed:", e); setNestriLock(false); } } } } catch (error) { console.error("Error during lock sequence:", error); } }; const setupPointerLockListener = () => { document.addEventListener("pointerlockchange", () => { const canvasElement = canvas(); if (!canvasElement) return; if (document.pointerLockElement === canvasElement) { initializeInputDevices(); } else { if (!showBannerModal) { const playing = sessionStorage.getItem("showedBanner"); setShowBannerModal(!playing || playing !== "true"); setShowButtonModal(playing === "false"); } nestriKeyboard?.dispose(); nestriMouse?.dispose(); } }); }; const handleVideoInput = async () => { const canvasElement = canvas(); if (!video || !canvasElement) return; try { await video.play(); if (canvasElement && video) { canvasElement.width = video.videoWidth; canvasElement.height = video.videoHeight; const ctx = canvasElement.getContext("2d"); const renderer = () => { if (ctx && hasStream() && video) { ctx.drawImage(video, 0, 0); video.requestVideoFrameCallback(renderer); } }; video.requestVideoFrameCallback(renderer); } } catch (error) { console.error("Error playing video:", error); } }; onMount(() => { const canvasElement = canvas(); if(!canvasElement) return; setupPointerLockListener(); video = document.createElement("video"); video.style.visibility = "hidden"; webrtc = new WebRTCStream("https://relay.dathorse.com", id, async (mediaStream) => { if (video && mediaStream) { video.srcObject = mediaStream; setHasStream(true); setShowOffline(false); await handleVideoInput(); } else if (mediaStream === null) { console.log("MediaStream is null, Room is offline"); setShowOffline(true); setHasStream(false); const ctx = canvasElement.getContext("2d"); if (ctx) ctx.clearRect(0, 0, canvasElement.width, canvasElement.height); } else if (video && video.srcObject !== null) { setHasStream(true); setShowOffline(true); await handleVideoInput(); } }); }); onCleanup(() => { nestriKeyboard?.dispose(); nestriMouse?.dispose(); }); return ( {showOffline() ? (
Offline
) : ( )}
); } interface ModalProps { show: () => boolean; setShow: (value: boolean) => void; closeOnBackdropClick?: boolean; handleVideoInput?: () => Promise; lockPlay?: () => Promise; } function Modal(props: ModalProps) { return (
props.closeOnBackdropClick && props.setShow(false)} >
); }