feat(addons): update portal blocks, recipes, and transfer scripts
All checks were successful
Deploy Addons / deploy (push) Successful in 17s

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-21 11:31:40 +00:00
parent 4bc7eb05b6
commit c32dbf42c4
14 changed files with 154 additions and 51 deletions

View File

@@ -1,5 +1,5 @@
{
"format_version": "1.21.40",
"format_version": "1.21.0",
"minecraft:block": {
"description": {
"identifier": "silverlabs:portal_frame",

View File

@@ -1,5 +1,5 @@
{
"format_version": "1.21.40",
"format_version": "1.21.0",
"minecraft:block": {
"description": {
"identifier": "silverlabs:portal_jamie",

View File

@@ -1,5 +1,5 @@
{
"format_version": "1.21.40",
"format_version": "1.21.0",
"minecraft:block": {
"description": {
"identifier": "silverlabs:portal_lyla",

View File

@@ -1,5 +1,5 @@
{
"format_version": "1.21.40",
"format_version": "1.21.0",
"minecraft:block": {
"description": {
"identifier": "silverlabs:portal_mya",

View File

@@ -8,6 +8,11 @@
"min_engine_version": [1, 21, 0]
},
"modules": [
{
"type": "data",
"uuid": "a1b2c3d4-7777-8888-9999-abcdef345678",
"version": [1, 1, 0]
},
{
"type": "script",
"language": "javascript",

View File

@@ -1,8 +1,8 @@
{
"format_version": "1.21.40",
"format_version": "1.21.0",
"minecraft:recipe_shaped": {
"description": {
"identifier": "silverlabs:portal_frame"
"identifier": "silverlabs:portal_frame_recipe"
},
"tags": ["crafting_table"],
"pattern": [
@@ -14,9 +14,6 @@
"O": { "item": "minecraft:obsidian" },
"E": { "item": "minecraft:ender_pearl" }
},
"unlock": [
{ "item": "minecraft:obsidian" }
],
"result": {
"item": "silverlabs:portal_frame",
"count": 1

View File

@@ -1,17 +1,14 @@
{
"format_version": "1.21.40",
"format_version": "1.21.0",
"minecraft:recipe_shapeless": {
"description": {
"identifier": "silverlabs:portal_jamie"
"identifier": "silverlabs:portal_jamie_recipe"
},
"tags": ["crafting_table"],
"ingredients": [
{ "item": "silverlabs:portal_frame" },
{ "item": "minecraft:emerald" }
],
"unlock": [
{ "item": "silverlabs:portal_frame" }
],
"result": {
"item": "silverlabs:portal_jamie",
"count": 1

View File

@@ -1,17 +1,14 @@
{
"format_version": "1.21.40",
"format_version": "1.21.0",
"minecraft:recipe_shapeless": {
"description": {
"identifier": "silverlabs:portal_lyla"
"identifier": "silverlabs:portal_lyla_recipe"
},
"tags": ["crafting_table"],
"ingredients": [
{ "item": "silverlabs:portal_frame" },
{ "item": "minecraft:amethyst_shard" }
],
"unlock": [
{ "item": "silverlabs:portal_frame" }
],
"result": {
"item": "silverlabs:portal_lyla",
"count": 1

View File

@@ -1,17 +1,14 @@
{
"format_version": "1.21.40",
"format_version": "1.21.0",
"minecraft:recipe_shapeless": {
"description": {
"identifier": "silverlabs:portal_mya"
"identifier": "silverlabs:portal_mya_recipe"
},
"tags": ["crafting_table"],
"ingredients": [
{ "item": "silverlabs:portal_frame" },
{ "item": "minecraft:prismarine_crystals" }
],
"unlock": [
{ "item": "silverlabs:portal_frame" }
],
"result": {
"item": "silverlabs:portal_mya",
"count": 1

View File

@@ -1,13 +1,23 @@
import { world, system } from "@minecraft/server";
import { transferPlayer } from "@minecraft/server-admin";
// Portal block → transfer target mapping
// Portal block → transfer target mapping (custom blocks — priority detection)
const PORTAL_BLOCKS = {
"silverlabs:portal_jamie": { name: "Jamie's World", host: "10.0.0.247", port: 19133, color: "§a" },
"silverlabs:portal_lyla": { name: "Lyla's World", host: "10.0.0.247", port: 19134, color: "§d" },
"silverlabs:portal_mya": { name: "Mya's World", host: "10.0.0.247", port: 19135, color: "§b" },
};
// Coordinate-based portal zones (fallback detection)
const PORTAL_ZONES = [
{ 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
@@ -20,9 +30,41 @@ world.afterEvents.playerSpawn.subscribe((event) => {
spawnTicks.set(event.player.id, system.currentTick);
});
/**
* Check if player is standing on/in a custom portal block (priority method).
* Returns the portal config or null.
*/
function checkBlockPortal(player) {
const pos = player.location;
const dimension = player.dimension;
const blockAtFeet = dimension.getBlock({ x: Math.floor(pos.x), y: Math.floor(pos.y), z: Math.floor(pos.z) });
const blockBelow = dimension.getBlock({ x: Math.floor(pos.x), y: Math.floor(pos.y) - 1, z: Math.floor(pos.z) });
const feetId = blockAtFeet?.typeId;
const belowId = blockBelow?.typeId;
return PORTAL_BLOCKS[feetId] || PORTAL_BLOCKS[belowId] || null;
}
/**
* Check if player is within a coordinate-based portal zone (fallback method).
* Returns the portal config or null.
*/
function checkZonePortal(player) {
const pos = player.location;
for (const portal of PORTAL_ZONES) {
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) {
return portal;
}
}
return null;
}
system.runInterval(() => {
for (const player of world.getAllPlayers()) {
const pos = player.location;
const playerId = player.id;
// Check cooldown
@@ -33,29 +75,31 @@ system.runInterval(() => {
const spawnedAt = spawnTicks.get(playerId) || 0;
if (system.currentTick - spawnedAt < SPAWN_PROTECTION_TICKS) continue;
// Check block at player's feet and one block below
const dimension = player.dimension;
const blockAtFeet = dimension.getBlock({ x: Math.floor(pos.x), y: Math.floor(pos.y), z: Math.floor(pos.z) });
const blockBelow = dimension.getBlock({ x: Math.floor(pos.x), y: Math.floor(pos.y) - 1, z: Math.floor(pos.z) });
const feetId = blockAtFeet?.typeId;
const belowId = blockBelow?.typeId;
const portal = PORTAL_BLOCKS[feetId] || PORTAL_BLOCKS[belowId];
// Hybrid detection: custom block first, then coordinate fallback
const portal = checkBlockPortal(player) || checkZonePortal(player);
if (!portal) continue;
cooldowns.set(playerId, system.currentTick);
// Teleport player away from the portal block so they don't land on it on return
player.teleport({ x: pos.x, y: pos.y, z: pos.z + 3 });
// 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}`);
}
// Teleport player away from portal before transfer so their saved position
// is NOT on the portal (prevents re-transfer loop when they return to lobby)
const safePos = player.location;
safePos.z += 4; // Move 4 blocks away from portals (portals are at z=-24)
player.teleport(safePos);
// Wait 5 ticks for position to save, then transfer
system.runTimeout(() => {
try {
transferPlayer(player, { hostname: portal.host, port: portal.port });
} catch (e) {
player.sendMessage(`§cTransfer failed: ${e.message}`);
}
}, 5);
}
}, 10); // Check every half second
@@ -65,6 +109,43 @@ world.afterEvents.playerLeave.subscribe((event) => {
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 {
overworld.runCommand(`setblock ${sign.x} ${sign.y} ${sign.z} oak_wall_sign ["facing_direction":3]`);
} catch (e) {
// Non-fatal — chunks may not be loaded
}
}
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! Place portal blocks to create portals.");
world.sendMessage("§6[Hub] §7Portal transfer system loaded!");
});

View File

@@ -0,0 +1,7 @@
{
"format_version": [1, 1, 0],
"silverlabs:portal_frame": { "sound": "stone" },
"silverlabs:portal_jamie": { "sound": "stone" },
"silverlabs:portal_lyla": { "sound": "stone" },
"silverlabs:portal_mya": { "sound": "stone" }
}