Files
netris-nestri/packages/play-standalone/src/pages/play.astro

196 lines
5.3 KiB
Plaintext

---
import DefaultLayout from "../layouts/DefaultLayout.astro";
// Passing of environment variables to the client side
// gotta love node and it's ecosystem..
const envs_map: Map<string, string | undefined> = new Map();
import { PEER_URL, getSecret } from "astro:env/server";
if (PEER_URL) {
envs_map.set("PEER_URL", getSecret("PEER_URL"));
}
let envs: string = "";
if (envs_map.size > 0) {
envs = JSON.stringify(Array.from(envs_map.entries()));
}
---
<DefaultLayout>
<h1 id="offlineText" class="offline">Offline</h1>
<h1 id="loadingText" class="loading">Warming up the GPU...</h1>
<canvas id="playCanvas" class="playCanvas"></canvas>
<div id="ENVS" data-envs={envs}></div>
</DefaultLayout>
<script>
import { Mouse, Keyboard, Controller, WebRTCStream } from "@nestri/input";
const ENVS = document.getElementById("ENVS")!.dataset.envs as string;
let ENVS_MAP: Map<string, string | undefined> | null = null;
if (ENVS && ENVS.length > 0) {
ENVS_MAP = new Map(JSON.parse(ENVS));
console.debug("ENVS_MAP:", ENVS_MAP);
}
// Method which returns true if mobile device
const isMobile = () => {
return /Mobi|Android/i.test(navigator.userAgent);
};
// Elements
const canvas = document.getElementById("playCanvas")! as HTMLCanvasElement;
const offlineText = document.getElementById("offlineText")! as HTMLHeadingElement;
const loadingText = document.getElementById("loadingText")! as HTMLHeadingElement;
const room = window.location.hash.substring(1);
if (!room || room.length <= 0) {
throw new Error("Room parameter is required in URL hash");
}
offlineText.style.display = "flex";
loadingText.style.display = "none";
// Get query parameter "peerURL" from the URL
let peerURL = new URLSearchParams(window.location.search).get("peerURL");
if (!peerURL || peerURL.length <= 0) {
peerURL = (ENVS_MAP && ENVS_MAP.get("PEER_URL")) ?? "/dnsaddr/relay.dathorse.com/p2p/12D3KooWPK4v5wKYNYx9oXWjqLM8Xix6nm13o91j1Feqq98fLBsw";
}
console.debug("Using Peer URL:", peerURL);
// Stream
const stream = new WebRTCStream(peerURL, room, async (mediaStream) => {
if (mediaStream && video.srcObject === null) {
video.srcObject = mediaStream;
offlineText.style.display = "none";
loadingText.style.display = "flex";
await video.play().catch((e) => {
console.error("Failed to play video:", e);
});
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
const ctx = canvas.getContext("2d");
const renderer = () => {
if (ctx && video.srcObject) {
ctx.drawImage(video, 0, 0);
video.requestVideoFrameCallback(renderer);
}
};
video.requestVideoFrameCallback(renderer);
loadingText.style.display = "none";
}
});
const video = document.createElement("video") as HTMLVideoElement;
// Input
let nestriMouse: Mouse | null = null;
let nestriKeyboard: Keyboard | null = null;
let nestriControllers: Controller[] = [];
window.addEventListener("gamepadconnected", (e) => {
console.log("Gamepad connected:", e.gamepad);
const controller = new Controller({
webrtc: stream,
e: e,
});
nestriControllers.push(controller);
});
window.addEventListener("gamepaddisconnected", (e) => {
console.log("Gamepad disconnected:", e.gamepad);
if (e.gamepad.id.toLowerCase().includes("nestri"))
return;
let disconnected = nestriControllers.find((c) => c.getSlot() === e.gamepad.index);
if (disconnected) {
disconnected.dispose();
nestriControllers = nestriControllers.filter((c) => c !== disconnected);
}
});
document.addEventListener("pointerlockchange", () => {
if (document.pointerLockElement === canvas) {
if (nestriMouse)
return;
nestriMouse = new Mouse({
canvas: canvas,
webrtc: stream,
});
} else {
if (nestriMouse) {
nestriMouse.dispose();
nestriMouse = null;
}
}
});
document.addEventListener("fullscreenchange", () => {
if (document.fullscreenElement) {
if (nestriKeyboard)
return;
nestriKeyboard = new Keyboard({
webrtc: stream,
});
nestriControllers.forEach((c) => c.run());
if ("keyboard" in navigator && "lock" in (navigator.keyboard as any)) {
const keys = [
"AltLeft", "AltRight", "Tab", "Escape",
"ContextMenu", "MetaLeft", "MetaRight"
];
(navigator.keyboard as any).lock(keys).then(() => {
console.log("Keyboard lock acquired");
}).catch((err: any) => {
console.error("Failed to acquire keyboard lock:", err);
});
}
} else {
if (nestriKeyboard) {
nestriKeyboard.dispose();
nestriKeyboard = null;
}
nestriControllers.forEach((c) => c.stop());
}
})
const lockPlay = async function () {
if (document.fullscreenElement)
return;
await canvas.requestFullscreen();
if (!isMobile())
await canvas.requestPointerLock();
};
canvas.addEventListener("click", lockPlay);
</script>
<style>
.playCanvas {
width: 100%;
height: 100%;
max-height: 100vh;
object-fit: contain;
aspect-ratio: 16 / 9;
margin: 0;
padding: 0;
}
.offline, .loading {
position: absolute;
width: 100%;
height: 100%;
align-items: center;
display: flex;
justify-content: center;
color: lightgray;
font-size: 1.5rem;
line-height: 2rem;
font-weight: 600;
}
</style>