import { safeTags } from "./encoding"; interface AOServer { name: string; description: string; ip: string; players: number; online: string; port?: number; ws_port?: number; wss_port?: number; asset?: string; } const clientVersion = process.env.npm_package_version; // const MASTERSERVER_IP = 'master.aceattorneyonline.com:27014'; const serverlist_domain = "servers.aceattorneyonline.com"; const protocol = window.location.protocol; const serverlist_cache_key = "masterlist"; const servers: AOServer[] = []; servers[-1] = { name: "Localhost", description: "This is your computer on port 50001", ip: "127.0.0.1", players: 0, online: "Localhost", ws_port: 50001, } as AOServer; function main() { getServerlist().then((serverlist) => { processServerlist(serverlist); }); addServer(servers[-1]); processClientVersion(clientVersion); getMasterVersion().then((masterVersion) => { processMasterVersion(masterVersion); }); } main(); // Fetches the serverlist from the masterserver // Returns a properly typed list of servers async function getServerlist(): Promise { const url = `${protocol}//${serverlist_domain}/servers`; const response = await fetch(url); if (!response.ok) { console.error( `Bad status code from masterserver. status: ${response.status}, body: ${response.body}`, ); document.getElementById("ms_error").style.display = "block"; // If we get a bad status code, try to use the cached serverlist return getCachedServerlist(); } const data = await response.json(); const serverlist: AOServer[] = []; for (const item of data) { if (!item.name) { console.warn(`Server ${item} has no name, skipping`); continue; } if (!item.ip) { console.warn(`Server ${item.name} has no ip, skipping`); continue; } if (!item.description) { console.warn(`Server ${item.name} has no description`); } const newServer: AOServer = { name: item.name, description: item.description, ip: item.ip, players: item.players || 0, online: `Players: ${item.players}`, }; if (item.ws_port) { newServer.ws_port = item.ws_port; } if (item.wss_port) { newServer.wss_port = item.wss_port; } // if none of ws_port or wss_port are defined, skip if (!newServer.ws_port && !newServer.wss_port) { console.warn(`Server ${item.name} has no websocket port, skipping`); continue; } serverlist.push(newServer); } // Always cache the result when we get it localStorage.setItem(serverlist_cache_key, JSON.stringify(serverlist)); return serverlist; } function getCachedServerlist(): AOServer[] { // If it's not in the cache, return an empty list const cached = localStorage.getItem(serverlist_cache_key) || "[]"; return JSON.parse(cached) as AOServer[]; } // Constructs the client URL robustly, independent of domain and path function constructClientURL(protocol: string): string { const clientURL = new URL(window.location.href); // Use the given protocol clientURL.protocol = protocol; // Remove the last part of the pathname (e.g., "index.html") const pathname = clientURL.pathname; const parts = pathname.split("/"); parts.pop(); // Reconstruct the pathname clientURL.pathname = parts.join("/"); // If clientURL.pathname does not end with a slash, add one if (clientURL.pathname[clientURL.pathname.length - 1] !== "/") { clientURL.pathname += "/"; } clientURL.pathname += "client.html"; return clientURL.href; } function addServer(server: AOServer) { let ws_port = 0; let ws_protocol = ""; let http_protocol = ""; if (server.ws_port) { ws_port = server.ws_port; ws_protocol = "ws"; http_protocol = "http"; } if (server.wss_port && !window.navigator.userAgent.includes("Nintendo")) { ws_port = server.wss_port; ws_protocol = "wss"; http_protocol = "https"; } if (ws_port === 0 || ws_protocol === "" || http_protocol === "") { console.warn(`Server ${server.name} has no websocket port, skipping`); return; } const clientURL = constructClientURL(http_protocol); const connect = `${ws_protocol}://${server.ip}:${ws_port}`; const serverName = server.name; const fullClientWatchURL = `${clientURL}?mode=watch&connect=${connect}&serverName=${serverName}`; const fullClientJoinURL = `${clientURL}?mode=join&connect=${connect}&serverName=${serverName}`; servers.push(server); document.getElementById("masterlist").innerHTML += `
` + `

${safeTags(server.name)} (${server.players})

` + `Join` + `Watch
` + `

${safeTags(server.description)}

` + `
`; } function processServerlist(serverlist: AOServer[]) { for (let i = 0; i < serverlist.length; i++) { addServer(serverlist[i]); } } async function getMasterVersion(): Promise { const url = `${protocol}//${serverlist_domain}/version`; const response = await fetch(url); if (!response.ok) { console.error( `Bad status code from masterserver version check. status: ${response.status}, body: ${response.body}`, ); return "Unknown"; } return await response.text(); } function processClientVersion(data: string) { document.getElementById("clientinfo").innerHTML = `Client version: ${data}`; } function processMasterVersion(data: string) { document.getElementById("serverinfo").innerHTML = `Master server version: ${data}`; }