const wsPort = document.getElementById("ws-port").value; const wsAdress = window.location.protocol === "https:" ? `wss://${location.host}/ws` : `ws://${location.hostname}:${wsPort}`; const ws = new WebSocket(wsAdress); const map = document.getElementById("map"); ws.onopen = () => console.log("Connected to WebSocket!"); ws.onmessage = (event) => { const players = JSON.parse(event.data); updatePlayers(players); }; function getImageData() { const naturalWidth = map.naturalWidth; const naturalHeight = map.naturalHeight; if (!naturalWidth || !naturalHeight) return; const container = map.parentElement; const containerWidth = container.clientWidth; const containerHeight = container.clientHeight; const imageAspect = naturalWidth / naturalHeight; const containerAspect = containerWidth / containerHeight; let displayWidth, displayHeight, offsetX, offsetY; // Compute letterbox dimensions if (imageAspect > containerAspect) { // Image fills width, black bars top/bottom displayWidth = containerWidth; displayHeight = containerWidth / imageAspect; offsetX = 0; offsetY = (containerHeight - displayHeight) / 2; } else { // Image fills height, black bars left/right displayHeight = containerHeight; displayWidth = containerHeight * imageAspect; offsetY = 0; offsetX = (containerWidth - displayWidth) / 2; } return { naturalWidth, naturalHeight, displayWidth, displayHeight, offsetX, offsetY }; } function updateMarkerPosition(marker) { const { naturalWidth, naturalHeight, displayWidth, displayHeight, offsetX, offsetY } = getImageData(); const dataPos = marker.getAttribute("data-position").split(","); const posX = parseFloat(dataPos[0]); const posZ = parseFloat(dataPos[1]); const x = offsetX + (posX / naturalWidth) * displayWidth; const y = offsetY + (posZ / naturalHeight) * displayHeight; marker.style.left = `${x}px`; marker.style.top = `${y}px`; } function updateAllMarkersPosition() { const markers = document.getElementsByClassName("player-marker"); for (let marker of markers) { updateMarkerPosition(marker); } } window.addEventListener("resize", updateAllMarkersPosition); map.addEventListener("load", updateAllMarkersPosition); function updatePlayers(players) { players.forEach(p => { const marker = document.querySelector(`[data-player='${p.username}']`); if (marker) { // Update existing marker marker.setAttribute("data-position", `${p.x},${p.z}`); updateMarkerPosition(marker); } else { // Create new marker const newMarker = document.createElement("div"); newMarker.className = "player-marker"; newMarker.setAttribute("data-player", p.username); newMarker.setAttribute("data-position", `${p.x},${p.z}`); map.parentElement.appendChild(newMarker); const headImage = document.createElement("img"); headImage.src = `https://crafatar.com/avatars/${p.uuid}?overlay&size=24`; newMarker.appendChild(headImage); updateMarkerPosition(newMarker); } }); // Delete markers for players no longer present const existingMarkers = document.getElementsByClassName("player-marker"); for (let marker of existingMarkers) { const username = marker.getAttribute("data-player"); if (!players.some(p => p.username === username)) { marker.remove(); } } }