import { world, system } from "@minecraft/server"; import { transferPlayer } from "@minecraft/server-admin"; // Portal definitions: name, center position, color, and direct transfer target const portals = [ { name: "Jamie's World", x: -15, y: 65, z: -24, host: "10.0.0.247", port: 19133, color: "§a" }, { name: "Lyla's World", x: 0, y: 65, z: -24, host: "10.0.0.247", port: 19134, color: "§d" }, { name: "Mya's World", x: 15, y: 65, z: -24, host: "10.0.0.247", port: 19135, color: "§b" }, ]; const PORTAL_RADIUS_X = 2.5; const PORTAL_RADIUS_Z = 2.0; const PORTAL_RADIUS_Y = 2.0; const COOLDOWN_TICKS = 100; // 5 seconds cooldown const SPAWN_PROTECTION_TICKS = 200; // 10 seconds — ignore portal detection after spawn // Track cooldowns per player const cooldowns = new Map(); // Track when players spawned (to prevent transfer loop on arrival) const spawnTicks = new Map(); world.afterEvents.playerSpawn.subscribe((event) => { spawnTicks.set(event.player.id, system.currentTick); }); system.runInterval(() => { for (const player of world.getAllPlayers()) { const pos = player.location; const playerId = player.id; // Check cooldown const lastTransfer = cooldowns.get(playerId) || 0; if (system.currentTick - lastTransfer < COOLDOWN_TICKS) continue; // Skip if player just spawned (prevents loop when arriving from child world) const spawnedAt = spawnTicks.get(playerId) || 0; if (system.currentTick - spawnedAt < SPAWN_PROTECTION_TICKS) continue; for (const portal of portals) { const dx = Math.abs(pos.x - portal.x); const dy = Math.abs(pos.y - portal.y); const dz = Math.abs(pos.z - portal.z); if (dx < PORTAL_RADIUS_X && dy < PORTAL_RADIUS_Y && dz < PORTAL_RADIUS_Z) { cooldowns.set(playerId, system.currentTick); // Show title notification player.runCommand(`titleraw @s title {"rawtext":[{"text":"${portal.color}${portal.name}"}]}`); player.runCommand(`titleraw @s subtitle {"rawtext":[{"text":"§7Transferring..."}]}`); player.sendMessage(`§6Transferring to ${portal.name}...`); try { transferPlayer(player, { hostname: portal.host, port: portal.port }); } catch (e) { player.sendMessage(`§cTransfer failed: ${e.message}`); } break; } } } }, 10); // Check every half second // Clean up tracking when players leave world.afterEvents.playerLeave.subscribe((event) => { cooldowns.delete(event.playerId); spawnTicks.delete(event.playerId); }); // ─── Place signs above each portal on load ────────────────────── function placePortalSigns() { const overworld = world.getDimension("overworld"); const signs = [ { x: -15, y: 70, z: -23, name: "Jamie's", color: "§a" }, { x: 0, y: 70, z: -23, name: "Lyla's", color: "§d" }, { x: 15, y: 70, z: -23, name: "Mya's", color: "§b" }, ]; for (const sign of signs) { try { // Place wall sign facing south (toward players approaching from +Z) overworld.runCommand(`setblock ${sign.x} ${sign.y} ${sign.z} oak_wall_sign ["facing_direction":3]`); } catch (e) { // Non-fatal — chunks may not be loaded } } // Delay to let sign blocks register, then write text system.runTimeout(() => { for (const sign of signs) { try { const block = overworld.getBlock({ x: sign.x, y: sign.y, z: sign.z }); if (!block) continue; const signComponent = block.getComponent("minecraft:sign"); if (!signComponent) continue; signComponent.setText(`${sign.color}${sign.name}\n${sign.color}World\n§7▼ Step in ▼`); } catch (e) { // Non-fatal } } }, 10); } system.runTimeout(() => { placePortalSigns(); }, 40); system.run(() => { world.sendMessage("§6[Hub] §7Portal transfer system loaded!"); });