Adds redstone-link-addon: a transmitter/receiver pair bound by channel name, giving redstone that runs anywhere without wire, support blocks or line of sight. Bedrock custom blocks can read redstone power but cannot emit it, so a live receiver is swapped to a vanilla redstone_block and back; receiver positions persist in a world dynamic property, with a reconcile pass at boot to repair anything a crash left inconsistent. Deployed and proven end-to-end on lobby (Hub World). Also commits family-goals-addon, which was running on jamie -- mounted in docker-compose.yml and pinned in the world -- while existing in no commit on any branch. It was unbacked-up production code. That omission was load-bearing for deploys: deploy.yml checks out docker-compose.yml from origin/main, so a deploy would have replaced the host compose with a version lacking the family-goals mount and silently killed the addon. Both new directories are added to the workflow's PATHS and push-trigger list, so nothing is mounted that CI does not deliver. The mount set in this file now matches the live host exactly. Note for a follow-up: dynamite-, hemp-, naturalist-lite-, smart-crafting-, tow-boat- and trees-features-addon are mounted but absent from PATHS, so CI never refreshes them. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015Gf1kZypRDfPL1YiWUS1LC
85 lines
2.8 KiB
JavaScript
85 lines
2.8 KiB
JavaScript
// Redstone input detection for the transmitter.
|
|
//
|
|
// Mirrors the approach already proven in hemp-addon's sun lamp: try the script
|
|
// API's getRedstonePower() first, and fall back to inspecting the six
|
|
// neighbouring blocks when it is unavailable or reports nothing.
|
|
|
|
export const REDSTONE_FACES = [
|
|
[1, 0, 0], [-1, 0, 0],
|
|
[0, 1, 0], [0, -1, 0],
|
|
[0, 0, 1], [0, 0, -1],
|
|
];
|
|
|
|
export function isPowerSource(b) {
|
|
if (!b) return false;
|
|
const t = b.typeId;
|
|
if (t === "minecraft:redstone_block") return true;
|
|
if (t === "minecraft:lit_redstone_torch" || t === "minecraft:redstone_torch") {
|
|
try { return b.permutation.getState("toggle_bit") !== false; } catch (_) {}
|
|
return t === "minecraft:lit_redstone_torch";
|
|
}
|
|
if (t === "minecraft:powered_repeater") return true;
|
|
if (t === "minecraft:powered_comparator") return true;
|
|
if (t === "minecraft:lever") {
|
|
try { return b.permutation.getState("open_bit") === true; } catch (_) { return false; }
|
|
}
|
|
if (t.endsWith("_button")) {
|
|
try { return b.permutation.getState("button_pressed_bit") === true; } catch (_) { return false; }
|
|
}
|
|
if (t.endsWith("_pressure_plate")) {
|
|
try {
|
|
const r = b.permutation.getState("redstone_signal") ?? 0;
|
|
return r > 0;
|
|
} catch (_) { return false; }
|
|
}
|
|
if (t === "minecraft:redstone_wire") {
|
|
try {
|
|
const p = b.permutation.getState("redstone_signal") ?? 0;
|
|
return p > 0;
|
|
} catch (_) { return false; }
|
|
}
|
|
if (t === "minecraft:daylight_detector_inverted") return true;
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Is this transmitter receiving power?
|
|
*
|
|
* `ignoreKeys` holds the position keys of receivers on the SAME channel. A
|
|
* powered receiver is a real redstone block, so without this guard a
|
|
* transmitter sitting next to its own channel's receiver would latch itself on
|
|
* forever. We skip those positions, and when one is adjacent we also skip the
|
|
* getRedstonePower() fast path, because that call cannot exclude a position.
|
|
*/
|
|
export function isTransmitterPowered(block, ignoreKeys) {
|
|
const dim = block.dimension;
|
|
const dimId = dim.id;
|
|
const { x, y, z } = block.location;
|
|
|
|
let adjacentSameChannelReceiver = false;
|
|
if (ignoreKeys && ignoreKeys.size > 0) {
|
|
for (const [dx, dy, dz] of REDSTONE_FACES) {
|
|
if (ignoreKeys.has(`${dimId}|${x + dx}|${y + dy}|${z + dz}`)) {
|
|
adjacentSameChannelReceiver = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!adjacentSameChannelReceiver) {
|
|
try {
|
|
const p = block.getRedstonePower();
|
|
if (typeof p === "number" && p > 0) return true;
|
|
} catch (_) {}
|
|
}
|
|
|
|
for (const [dx, dy, dz] of REDSTONE_FACES) {
|
|
const pos = { x: x + dx, y: y + dy, z: z + dz };
|
|
if (ignoreKeys && ignoreKeys.has(`${dimId}|${pos.x}|${pos.y}|${pos.z}`)) continue;
|
|
let nb;
|
|
try { nb = dim.getBlock(pos); } catch (_) { continue; }
|
|
if (isPowerSource(nb)) return true;
|
|
}
|
|
return false;
|
|
}
|