feat(portals): add portal signs, transfer notifications, and welcome titles

Lobby portals now have oak wall signs showing whose world each portal leads to
(Jamie/Lyla/Mya with color-coded text). Players see a title notification when
entering a portal. Child worlds show a welcome title with the world name on
arrival, read from variables.json via @minecraft/server-admin.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-19 02:46:03 +00:00
parent c12a468958
commit 5eefd26f67
4 changed files with 62 additions and 10 deletions

View File

@@ -4,7 +4,7 @@
"name": "Hub Return Transfer", "name": "Hub Return Transfer",
"description": "Transfers players back to lobby when they step on the return portal", "description": "Transfers players back to lobby when they step on the return portal",
"uuid": "b2c3d4e5-1111-2222-3333-fedcba654321", "uuid": "b2c3d4e5-1111-2222-3333-fedcba654321",
"version": [1, 0, 3], "version": [1, 0, 4],
"min_engine_version": [1, 21, 0] "min_engine_version": [1, 21, 0]
}, },
"modules": [ "modules": [
@@ -12,7 +12,7 @@
"type": "script", "type": "script",
"language": "javascript", "language": "javascript",
"uuid": "b2c3d4e5-4444-5555-6666-fedcba987654", "uuid": "b2c3d4e5-4444-5555-6666-fedcba987654",
"version": [1, 0, 3], "version": [1, 0, 4],
"entry": "scripts/main.js" "entry": "scripts/main.js"
} }
], ],

View File

@@ -1,5 +1,5 @@
import { world, system, ItemStack } from "@minecraft/server"; import { world, system, ItemStack } from "@minecraft/server";
import { transferPlayer } from "@minecraft/server-admin"; import { transferPlayer, variables } from "@minecraft/server-admin";
const LOBBY_HOST = "10.0.0.247"; const LOBBY_HOST = "10.0.0.247";
const LOBBY_PORT = 19132; const LOBBY_PORT = 19132;
@@ -47,13 +47,23 @@ function giveCompassIfMissing(player) {
player.sendMessage("§b[Hub] §fYou received a §dHub Compass§f — use it to return to the lobby!"); player.sendMessage("§b[Hub] §fYou received a §dHub Compass§f — use it to return to the lobby!");
} }
// Get the world name from server variables.json for the welcome message
let WORLD_NAME = "this world";
try {
WORLD_NAME = variables.get("world_name") || "this world";
} catch (e) {
// variables.json may not exist — use fallback
}
world.afterEvents.playerSpawn.subscribe((event) => { world.afterEvents.playerSpawn.subscribe((event) => {
const player = event.player; const player = event.player;
// Mark spawn time for portal protection (prevents transfer loop on arrival) // Mark spawn time for portal protection (prevents transfer loop on arrival)
spawnTimes.set(player.name, Date.now()); spawnTimes.set(player.name, Date.now());
// Small delay to let inventory load // Show welcome title and give compass after a short delay
system.runTimeout(() => { system.runTimeout(() => {
try { try {
player.runCommand(`titleraw @s title {"rawtext":[{"text":"§6Welcome!"}]}`);
player.runCommand(`titleraw @s subtitle {"rawtext":[{"text":"§7You are now in §e${WORLD_NAME}"}]}`);
giveCompassIfMissing(player); giveCompassIfMissing(player);
} catch (e) { } catch (e) {
// Silently ignore — player may have disconnected // Silently ignore — player may have disconnected

View File

@@ -4,7 +4,7 @@
"name": "Lobby Portal Transfer", "name": "Lobby Portal Transfer",
"description": "Auto-transfers players when they step into portal areas", "description": "Auto-transfers players when they step into portal areas",
"uuid": "a1b2c3d4-1111-2222-3333-abcdef123456", "uuid": "a1b2c3d4-1111-2222-3333-abcdef123456",
"version": [1, 0, 3], "version": [1, 0, 4],
"min_engine_version": [1, 21, 0] "min_engine_version": [1, 21, 0]
}, },
"modules": [ "modules": [
@@ -12,7 +12,7 @@
"type": "script", "type": "script",
"language": "javascript", "language": "javascript",
"uuid": "a1b2c3d4-4444-5555-6666-abcdef789012", "uuid": "a1b2c3d4-4444-5555-6666-abcdef789012",
"version": [1, 0, 3], "version": [1, 0, 4],
"entry": "scripts/main.js" "entry": "scripts/main.js"
} }
], ],

View File

@@ -1,11 +1,11 @@
import { world, system } from "@minecraft/server"; import { world, system } from "@minecraft/server";
import { transferPlayer } from "@minecraft/server-admin"; import { transferPlayer } from "@minecraft/server-admin";
// Portal definitions: name, center position, and direct transfer target // Portal definitions: name, center position, color, and direct transfer target
const portals = [ const portals = [
{ name: "Jamie's World", x: -15, y: 65, z: -24, host: "10.0.0.247", port: 19133 }, { 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 }, { 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 }, { 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_X = 2.5;
@@ -43,6 +43,9 @@ system.runInterval(() => {
if (dx < PORTAL_RADIUS_X && dy < PORTAL_RADIUS_Y && dz < PORTAL_RADIUS_Z) { if (dx < PORTAL_RADIUS_X && dy < PORTAL_RADIUS_Y && dz < PORTAL_RADIUS_Z) {
cooldowns.set(playerId, system.currentTick); 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}...`); player.sendMessage(`§6Transferring to ${portal.name}...`);
try { try {
transferPlayer(player, { hostname: portal.host, port: portal.port }); transferPlayer(player, { hostname: portal.host, port: portal.port });
@@ -61,6 +64,45 @@ world.afterEvents.playerLeave.subscribe((event) => {
spawnTicks.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(() => { system.run(() => {
world.sendMessage("§6[Hub] §7Portal transfer system loaded!"); world.sendMessage("§6[Hub] §7Portal transfer system loaded!");
}); });