feat(addons): update portal blocks, recipes, and transfer scripts
All checks were successful
Deploy Addons / deploy (push) Successful in 17s
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:
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"mcpServers": {
|
||||
"minecraft": {
|
||||
"url": "http://10.0.0.247:3002/mcp"
|
||||
"mc-ai-bridge": {
|
||||
"url": "http://10.0.0.247:3002/sse"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"dragon_whistle": {
|
||||
"textures": "textures/items/dragon_whistle"
|
||||
},
|
||||
"dragon_egg": {
|
||||
"dragon_egg": {
|
||||
"textures": "textures/items/dragon_egg"
|
||||
},
|
||||
"dragon_toy": {
|
||||
@@ -16,6 +16,6 @@
|
||||
},
|
||||
"dragon_gravestone": {
|
||||
"textures": "textures/items/dragon_gravestone"
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,11 +24,33 @@ function doTransfer(player) {
|
||||
recentTransfers.set(player.name, now);
|
||||
world.sendMessage(`§a${player.name} is returning to the Hub...`);
|
||||
|
||||
try {
|
||||
transferPlayer(player, { hostname: LOBBY_HOST, port: LOBBY_PORT });
|
||||
} catch (e) {
|
||||
player.sendMessage(`§cTransfer failed: ${e.message}`);
|
||||
// Teleport player away from return portal before transfer so their saved
|
||||
// position is NOT on the portal (prevents re-transfer loop on lobby arrival)
|
||||
const portalJson = world.getDynamicProperty(PORTAL_PROP);
|
||||
if (portalJson) {
|
||||
try {
|
||||
const portal = JSON.parse(portalJson);
|
||||
const pos = player.location;
|
||||
const dx = Math.abs(pos.x - portal.x);
|
||||
const dz = Math.abs(pos.z - portal.z);
|
||||
// Only teleport if player is near the return portal
|
||||
if (dx <= PORTAL_RADIUS + 1 && dz <= PORTAL_RADIUS + 1) {
|
||||
const safePos = { x: pos.x, y: pos.y, z: pos.z + 4 };
|
||||
player.teleport(safePos);
|
||||
}
|
||||
} catch (e) {
|
||||
// Non-fatal — proceed with transfer anyway
|
||||
}
|
||||
}
|
||||
|
||||
// Wait 5 ticks for position to save, then transfer
|
||||
system.runTimeout(() => {
|
||||
try {
|
||||
transferPlayer(player, { hostname: LOBBY_HOST, port: LOBBY_PORT });
|
||||
} catch (e) {
|
||||
player.sendMessage(`§cTransfer failed: ${e.message}`);
|
||||
}
|
||||
}, 5);
|
||||
}
|
||||
|
||||
// ─── A. Compass Distribution ────────────────────────────────────
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"format_version": "1.21.40",
|
||||
"format_version": "1.21.0",
|
||||
"minecraft:block": {
|
||||
"description": {
|
||||
"identifier": "silverlabs:portal_frame",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"format_version": "1.21.40",
|
||||
"format_version": "1.21.0",
|
||||
"minecraft:block": {
|
||||
"description": {
|
||||
"identifier": "silverlabs:portal_jamie",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"format_version": "1.21.40",
|
||||
"format_version": "1.21.0",
|
||||
"minecraft:block": {
|
||||
"description": {
|
||||
"identifier": "silverlabs:portal_lyla",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"format_version": "1.21.40",
|
||||
"format_version": "1.21.0",
|
||||
"minecraft:block": {
|
||||
"description": {
|
||||
"identifier": "silverlabs:portal_mya",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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!");
|
||||
});
|
||||
|
||||
7
lobby-addon/lobby_transfer_RP/blocks.json
Normal file
7
lobby-addon/lobby_transfer_RP/blocks.json
Normal 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" }
|
||||
}
|
||||
Reference in New Issue
Block a user