// // manifoldDomain.js — Manifold Domain Script // // Assignment client script — do NOT wrap in an IIFE. // Requires EntityViewer to be initialized before entity queries work. // // Deploy via Domain Settings > Scripts. // var WS_URL = "wss://wizards.cyou/ws"; var SCAN_CENTER = { x: 0, y: 0, z: 0 }; var SCAN_RADIUS = 500; var ws = null; var reconnectTimer = null; var heartbeatTimer = null; var isReady = false; // ── EntityViewer init ──────────────────────────────────────────── // AC scripts have no spatial presence — must set up EntityViewer // before findEntitiesByType will return anything. var initInterval = Script.setInterval(function () { if (Entities.serversExist() && Entities.canRez()) { EntityViewer.setPosition(SCAN_CENTER); EntityViewer.setCenterRadius(SCAN_RADIUS); EntityViewer.queryOctree(); Script.clearInterval(initInterval); Script.setTimeout(function () { isReady = true; print("[Manifold] EntityViewer ready"); connect(); // Re-query octree periodically so newly spawned entities stay visible heartbeatTimer = Script.setInterval(function () { EntityViewer.queryOctree(); }, 10000); }, 2000); } }, 500); // ── entity cleanup ─────────────────────────────────────────────── function deleteClonesFor(username) { if (!isReady) return; EntityViewer.queryOctree(); Script.setTimeout(function () { // Use untyped findEntities to avoid type-specific visibility issues // (Box entities are not always returned by findEntitiesByType) var ids = Entities.findEntities(SCAN_CENTER, SCAN_RADIUS); var deleted = 0; ids.forEach(function (id) { try { var ud = JSON.parse(Entities.getEntityProperties(id, ["userData"]).userData || "{}"); if (ud.manifoldClone && ud.owner === username) { Entities.deleteEntity(id); deleted++; } } catch (e) {} }); print("[Manifold] deleted " + deleted + " clone(s) for " + username); }, 1000); } // ── websocket ──────────────────────────────────────────────────── function connect() { print("[Manifold] Connecting to " + WS_URL); ws = new WebSocket(WS_URL); ws.onopen = function () { print("[Manifold] Connected"); if (reconnectTimer) { Script.clearTimeout(reconnectTimer); reconnectTimer = null; } }; ws.onmessage = function (event) { try { var msg = JSON.parse(event.data); switch (msg.type) { case "user:connected": print("[Manifold] >>> " + msg.username + " joined"); break; case "user:disconnected": print("[Manifold] <<< " + msg.username + " left"); deleteClonesFor(msg.username); break; case "recall": print("[Manifold] recall for " + msg.username); deleteClonesFor(msg.username); break; } } catch (e) { print("[Manifold] message parse error: " + e); } }; ws.onerror = function (err) { print("[Manifold] WS error: " + JSON.stringify(err)); }; ws.onclose = function (event) { print("[Manifold] Disconnected (code=" + event.code + "). Reconnecting in 5s..."); ws = null; reconnectTimer = Script.setTimeout(connect, 5000); }; } // ── lifecycle ──────────────────────────────────────────────────── Script.scriptEnding.connect(function () { print("[Manifold] script ending"); if (reconnectTimer) { Script.clearTimeout(reconnectTimer); } if (heartbeatTimer) { Script.clearInterval(heartbeatTimer); } if (ws) { ws.close(); ws = null; } });