// Persistent node registry for Redstone Link. // // A "node" is one placed transmitter or receiver. We have to remember them // because a powered receiver is physically replaced by a vanilla redstone // block (Bedrock custom blocks cannot emit redstone power), so the block // itself stops being evidence that a receiver is there. // // State lives in a world dynamic property, the same approach family-goals and // private-chest use, so it survives container restarts. import { world } from "@minecraft/server"; const DP_KEY = "rlink:nodes"; // Bounded so the serialised registry stays well inside the dynamic-property // size budget and the tick loop stays cheap. export const MAX_NODES = 256; // node shape: { d: dimensionId, x, y, z, r: "tx" | "rx", c: channel } let cache = null; export function load() { if (cache) return cache; let raw; try { raw = world.getDynamicProperty(DP_KEY); } catch (_) { raw = undefined; } if (typeof raw !== "string" || raw.length === 0) { cache = []; return cache; } try { const parsed = JSON.parse(raw); cache = Array.isArray(parsed) ? parsed : []; } catch (_) { console.warn("[RedstoneLink] registry was corrupt, starting empty"); cache = []; } return cache; } export function save() { if (!cache) return; try { world.setDynamicProperty(DP_KEY, JSON.stringify(cache)); } catch (e) { console.warn(`[RedstoneLink] registry save failed: ${e}`); } } export function keyOf(dimensionId, loc) { return `${dimensionId}|${Math.floor(loc.x)}|${Math.floor(loc.y)}|${Math.floor(loc.z)}`; } export function nodeKey(n) { return `${n.d}|${n.x}|${n.y}|${n.z}`; } export function find(dimensionId, loc) { const k = keyOf(dimensionId, loc); return load().find((n) => nodeKey(n) === k) ?? null; } /** Returns the node, or null if the registry is full. */ export function add(dimensionId, loc, role, channel) { const list = load(); const k = keyOf(dimensionId, loc); const existing = list.find((n) => nodeKey(n) === k); if (existing) { existing.r = role; existing.c = channel; save(); return existing; } if (list.length >= MAX_NODES) return null; const node = { d: dimensionId, x: Math.floor(loc.x), y: Math.floor(loc.y), z: Math.floor(loc.z), r: role, c: channel, }; list.push(node); save(); return node; } export function remove(dimensionId, loc) { const list = load(); const k = keyOf(dimensionId, loc); const i = list.findIndex((n) => nodeKey(n) === k); if (i < 0) return false; list.splice(i, 1); save(); return true; } /** Bulk removal by node key; one save for the whole batch. */ export function removeKeys(keys) { if (!keys || keys.size === 0) return 0; const list = load(); let removed = 0; for (let i = list.length - 1; i >= 0; i--) { if (keys.has(nodeKey(list[i]))) { list.splice(i, 1); removed++; } } if (removed > 0) save(); return removed; } export function all() { return load(); }