From d9eafd2c12ed9295e08d6529781d28d96e250180 Mon Sep 17 00:00:00 2001 From: SysAdmin Date: Thu, 19 Mar 2026 14:23:23 +0000 Subject: [PATCH] feat(portals): add craftable portal blocks replacing hardcoded coordinates Portal frame crafted from obsidian + ender pearl, combined with crystals (emerald/amethyst/prismarine) to create world-specific portal blocks. Stepping on a portal block triggers transfer. Includes resource pack with vanilla textures, block definitions, crafting recipes, and updated script. Co-Authored-By: Claude Opus 4.6 (1M context) --- docker-compose.yml | 1 + .../blocks/portal_frame.json | 28 ++++++ .../blocks/portal_jamie.json | 28 ++++++ .../lobby_transfer_BP/blocks/portal_lyla.json | 28 ++++++ .../lobby_transfer_BP/blocks/portal_mya.json | 28 ++++++ lobby-addon/lobby_transfer_BP/manifest.json | 8 +- .../recipes/portal_frame.json | 25 +++++ .../recipes/portal_jamie.json | 20 ++++ .../recipes/portal_lyla.json | 20 ++++ .../lobby_transfer_BP/recipes/portal_mya.json | 20 ++++ lobby-addon/lobby_transfer_BP/scripts/main.js | 94 ++++++------------ lobby-addon/lobby_transfer_RP/manifest.json | 17 ++++ lobby-addon/lobby_transfer_RP/pack_icon.png | Bin 0 -> 971 bytes .../lobby_transfer_RP/texts/en_US.lang | 4 + .../textures/terrain_texture.json | 20 ++++ 15 files changed, 273 insertions(+), 68 deletions(-) create mode 100644 lobby-addon/lobby_transfer_BP/blocks/portal_frame.json create mode 100644 lobby-addon/lobby_transfer_BP/blocks/portal_jamie.json create mode 100644 lobby-addon/lobby_transfer_BP/blocks/portal_lyla.json create mode 100644 lobby-addon/lobby_transfer_BP/blocks/portal_mya.json create mode 100644 lobby-addon/lobby_transfer_BP/recipes/portal_frame.json create mode 100644 lobby-addon/lobby_transfer_BP/recipes/portal_jamie.json create mode 100644 lobby-addon/lobby_transfer_BP/recipes/portal_lyla.json create mode 100644 lobby-addon/lobby_transfer_BP/recipes/portal_mya.json create mode 100644 lobby-addon/lobby_transfer_RP/manifest.json create mode 100644 lobby-addon/lobby_transfer_RP/pack_icon.png create mode 100644 lobby-addon/lobby_transfer_RP/texts/en_US.lang create mode 100644 lobby-addon/lobby_transfer_RP/textures/terrain_texture.json diff --git a/docker-compose.yml b/docker-compose.yml index fbfd489..6dc3bc3 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -19,6 +19,7 @@ services: volumes: - lobby-data:/data - ./lobby-addon/lobby_transfer_BP:/data/behavior_packs/lobby_transfer_BP + - ./lobby-addon/lobby_transfer_RP:/data/resource_packs/lobby_transfer_RP - ./addon/spark_pet_BP:/data/behavior_packs/spark_pet_BP - ./addon/spark_pet_RP:/data/resource_packs/spark_pet_RP restart: unless-stopped diff --git a/lobby-addon/lobby_transfer_BP/blocks/portal_frame.json b/lobby-addon/lobby_transfer_BP/blocks/portal_frame.json new file mode 100644 index 0000000..b8add25 --- /dev/null +++ b/lobby-addon/lobby_transfer_BP/blocks/portal_frame.json @@ -0,0 +1,28 @@ +{ + "format_version": "1.21.40", + "minecraft:block": { + "description": { + "identifier": "silverlabs:portal_frame", + "menu_category": { + "category": "items", + "group": "itemGroup.name.decorations" + } + }, + "components": { + "minecraft:destructible_by_mining": { + "seconds_to_destroy": 5.0 + }, + "minecraft:destructible_by_explosion": { + "explosion_resistance": 1200.0 + }, + "minecraft:light_emission": 10, + "minecraft:map_color": "#5C6D74", + "minecraft:material_instances": { + "*": { + "texture": "portal_frame", + "render_method": "opaque" + } + } + } + } +} diff --git a/lobby-addon/lobby_transfer_BP/blocks/portal_jamie.json b/lobby-addon/lobby_transfer_BP/blocks/portal_jamie.json new file mode 100644 index 0000000..2dd520d --- /dev/null +++ b/lobby-addon/lobby_transfer_BP/blocks/portal_jamie.json @@ -0,0 +1,28 @@ +{ + "format_version": "1.21.40", + "minecraft:block": { + "description": { + "identifier": "silverlabs:portal_jamie", + "menu_category": { + "category": "items", + "group": "itemGroup.name.decorations" + } + }, + "components": { + "minecraft:destructible_by_mining": { + "seconds_to_destroy": 5.0 + }, + "minecraft:destructible_by_explosion": { + "explosion_resistance": 1200.0 + }, + "minecraft:light_emission": 10, + "minecraft:map_color": "#2D9C2D", + "minecraft:material_instances": { + "*": { + "texture": "portal_jamie", + "render_method": "opaque" + } + } + } + } +} diff --git a/lobby-addon/lobby_transfer_BP/blocks/portal_lyla.json b/lobby-addon/lobby_transfer_BP/blocks/portal_lyla.json new file mode 100644 index 0000000..57dc87d --- /dev/null +++ b/lobby-addon/lobby_transfer_BP/blocks/portal_lyla.json @@ -0,0 +1,28 @@ +{ + "format_version": "1.21.40", + "minecraft:block": { + "description": { + "identifier": "silverlabs:portal_lyla", + "menu_category": { + "category": "items", + "group": "itemGroup.name.decorations" + } + }, + "components": { + "minecraft:destructible_by_mining": { + "seconds_to_destroy": 5.0 + }, + "minecraft:destructible_by_explosion": { + "explosion_resistance": 1200.0 + }, + "minecraft:light_emission": 10, + "minecraft:map_color": "#9B59B6", + "minecraft:material_instances": { + "*": { + "texture": "portal_lyla", + "render_method": "opaque" + } + } + } + } +} diff --git a/lobby-addon/lobby_transfer_BP/blocks/portal_mya.json b/lobby-addon/lobby_transfer_BP/blocks/portal_mya.json new file mode 100644 index 0000000..b06f162 --- /dev/null +++ b/lobby-addon/lobby_transfer_BP/blocks/portal_mya.json @@ -0,0 +1,28 @@ +{ + "format_version": "1.21.40", + "minecraft:block": { + "description": { + "identifier": "silverlabs:portal_mya", + "menu_category": { + "category": "items", + "group": "itemGroup.name.decorations" + } + }, + "components": { + "minecraft:destructible_by_mining": { + "seconds_to_destroy": 5.0 + }, + "minecraft:destructible_by_explosion": { + "explosion_resistance": 1200.0 + }, + "minecraft:light_emission": 10, + "minecraft:map_color": "#4FC1E9", + "minecraft:material_instances": { + "*": { + "texture": "portal_mya", + "render_method": "opaque" + } + } + } + } +} diff --git a/lobby-addon/lobby_transfer_BP/manifest.json b/lobby-addon/lobby_transfer_BP/manifest.json index 829de53..e31ca6c 100644 --- a/lobby-addon/lobby_transfer_BP/manifest.json +++ b/lobby-addon/lobby_transfer_BP/manifest.json @@ -4,7 +4,7 @@ "name": "Lobby Portal Transfer", "description": "Auto-transfers players when they step into portal areas", "uuid": "a1b2c3d4-1111-2222-3333-abcdef123456", - "version": [1, 0, 4], + "version": [1, 1, 0], "min_engine_version": [1, 21, 0] }, "modules": [ @@ -12,7 +12,7 @@ "type": "script", "language": "javascript", "uuid": "a1b2c3d4-4444-5555-6666-abcdef789012", - "version": [1, 0, 4], + "version": [1, 1, 0], "entry": "scripts/main.js" } ], @@ -24,6 +24,10 @@ { "module_name": "@minecraft/server-admin", "version": "1.0.0-beta" + }, + { + "uuid": "b2c3d4e5-1111-2222-3333-fedcba654321", + "version": [1, 0, 0] } ] } diff --git a/lobby-addon/lobby_transfer_BP/recipes/portal_frame.json b/lobby-addon/lobby_transfer_BP/recipes/portal_frame.json new file mode 100644 index 0000000..388e551 --- /dev/null +++ b/lobby-addon/lobby_transfer_BP/recipes/portal_frame.json @@ -0,0 +1,25 @@ +{ + "format_version": "1.21.40", + "minecraft:recipe_shaped": { + "description": { + "identifier": "silverlabs:portal_frame" + }, + "tags": ["crafting_table"], + "pattern": [ + " O ", + "OEO", + " O " + ], + "key": { + "O": { "item": "minecraft:obsidian" }, + "E": { "item": "minecraft:ender_pearl" } + }, + "unlock": [ + { "item": "minecraft:obsidian" } + ], + "result": { + "item": "silverlabs:portal_frame", + "count": 1 + } + } +} diff --git a/lobby-addon/lobby_transfer_BP/recipes/portal_jamie.json b/lobby-addon/lobby_transfer_BP/recipes/portal_jamie.json new file mode 100644 index 0000000..e13b9f9 --- /dev/null +++ b/lobby-addon/lobby_transfer_BP/recipes/portal_jamie.json @@ -0,0 +1,20 @@ +{ + "format_version": "1.21.40", + "minecraft:recipe_shapeless": { + "description": { + "identifier": "silverlabs:portal_jamie" + }, + "tags": ["crafting_table"], + "ingredients": [ + { "item": "silverlabs:portal_frame" }, + { "item": "minecraft:emerald" } + ], + "unlock": [ + { "item": "silverlabs:portal_frame" } + ], + "result": { + "item": "silverlabs:portal_jamie", + "count": 1 + } + } +} diff --git a/lobby-addon/lobby_transfer_BP/recipes/portal_lyla.json b/lobby-addon/lobby_transfer_BP/recipes/portal_lyla.json new file mode 100644 index 0000000..9c27b0e --- /dev/null +++ b/lobby-addon/lobby_transfer_BP/recipes/portal_lyla.json @@ -0,0 +1,20 @@ +{ + "format_version": "1.21.40", + "minecraft:recipe_shapeless": { + "description": { + "identifier": "silverlabs:portal_lyla" + }, + "tags": ["crafting_table"], + "ingredients": [ + { "item": "silverlabs:portal_frame" }, + { "item": "minecraft:amethyst_shard" } + ], + "unlock": [ + { "item": "silverlabs:portal_frame" } + ], + "result": { + "item": "silverlabs:portal_lyla", + "count": 1 + } + } +} diff --git a/lobby-addon/lobby_transfer_BP/recipes/portal_mya.json b/lobby-addon/lobby_transfer_BP/recipes/portal_mya.json new file mode 100644 index 0000000..c1fe813 --- /dev/null +++ b/lobby-addon/lobby_transfer_BP/recipes/portal_mya.json @@ -0,0 +1,20 @@ +{ + "format_version": "1.21.40", + "minecraft:recipe_shapeless": { + "description": { + "identifier": "silverlabs:portal_mya" + }, + "tags": ["crafting_table"], + "ingredients": [ + { "item": "silverlabs:portal_frame" }, + { "item": "minecraft:prismarine_crystals" } + ], + "unlock": [ + { "item": "silverlabs:portal_frame" } + ], + "result": { + "item": "silverlabs:portal_mya", + "count": 1 + } + } +} diff --git a/lobby-addon/lobby_transfer_BP/scripts/main.js b/lobby-addon/lobby_transfer_BP/scripts/main.js index 4ae6d94..e6c0d8b 100644 --- a/lobby-addon/lobby_transfer_BP/scripts/main.js +++ b/lobby-addon/lobby_transfer_BP/scripts/main.js @@ -1,16 +1,13 @@ 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" }, -]; +// Portal block → transfer target mapping +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" }, +}; -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 @@ -36,24 +33,28 @@ system.runInterval(() => { 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); + // 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) }); - 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; - } + const feetId = blockAtFeet?.typeId; + const belowId = blockBelow?.typeId; + + const portal = PORTAL_BLOCKS[feetId] || PORTAL_BLOCKS[belowId]; + 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}`); } } }, 10); // Check every half second @@ -64,45 +65,6 @@ 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 { - // 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!"); + world.sendMessage("§6[Hub] §7Portal transfer system loaded! Place portal blocks to create portals."); }); diff --git a/lobby-addon/lobby_transfer_RP/manifest.json b/lobby-addon/lobby_transfer_RP/manifest.json new file mode 100644 index 0000000..d1bc25a --- /dev/null +++ b/lobby-addon/lobby_transfer_RP/manifest.json @@ -0,0 +1,17 @@ +{ + "format_version": 2, + "header": { + "name": "Lobby Portal Transfer Resources", + "description": "Textures and lang for craftable portal blocks", + "uuid": "b2c3d4e5-1111-2222-3333-fedcba654321", + "version": [1, 0, 0], + "min_engine_version": [1, 21, 0] + }, + "modules": [ + { + "type": "resources", + "uuid": "b2c3d4e5-4444-5555-6666-fedcba987654", + "version": [1, 0, 0] + } + ] +} diff --git a/lobby-addon/lobby_transfer_RP/pack_icon.png b/lobby-addon/lobby_transfer_RP/pack_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a507642d37648f9bf0fb7e5ed2a3229eb2392bb9 GIT binary patch literal 971 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H1|$#LC7xzrV4mdZ;uumf=j~m?JmElr_K%#W z1cNTSgzR&$QsJ9mCBC5F;boALN{hqGm&&hX6oh2OABbfJw=>P#9Cx$g?7mv#&;RbP z_fmNrG4K55b7{hTa{2!Ac{asW*fac4VkkU**#F$Ru(*C^-oJnTWdAjkc93E0P-ZA( zQRrhhBG_QTB;d~Qhy%Oeosa)!e@U${^;uY>IwgkTwuZYSgY^m*M+Wv6hFnZDG^<5e zHZ=GI3OFbSE>>W0f2?8C^uQ%RAntYp?}IbH4GypsJh81YHf!kSXx6{M(CaF-Pf~ip z%nT5R*F|=pB%_Yrrw?Z>Saj5WJ>WJwz_f|yeh+Uz=(2<2ISpP$eD`&3uvyseyi+}U zcl+$4^2Zf7@$KUfJTT>8f4;P0%bUaI+yxvKDL+jA#p>Gf+&@#vd`9-s&y%U2eJ`AT zz`dO};I-(TBH=G)HJcUQGcI~v_@VVQ+ls%dVvD%H*wk!h$Y*M|tEqqdSKt~jeD3n! z*9l-q{UN9nAW45J^l@#@t)iuahWT*&%y zd*8b6nJ+D!avQ|5cU*wpn{?uE083X^;Ucd%Y~EvB)_<-sn-jsx?5 zD=zrXv8b5;z#D&-y=?-u+%3P%KEw;U)GKY~+Q|6rq}`tr{r0X55qy|2>NJ=0&eL}f z)xMmZvN3%1bm{r^KR=#(Iq}VOrG_{6_W#@U&mxK^lj+1Z*0N49=UEI@COrqwsCR@g ze%ZuY_VEVaN+yHX_DzdIdEEQAFZX{s*=N7FxMkDFQr^d*_Sf$ywEYkEyv5Mvq3E-7 z)ygoQVkHT-3GW09#GXzt_&RCZ!v|UUZT~0QF>HRWeqXu8^14x9{82f_52c)I$ztaD0e F0stcJlKlVx literal 0 HcmV?d00001 diff --git a/lobby-addon/lobby_transfer_RP/texts/en_US.lang b/lobby-addon/lobby_transfer_RP/texts/en_US.lang new file mode 100644 index 0000000..fe64cfc --- /dev/null +++ b/lobby-addon/lobby_transfer_RP/texts/en_US.lang @@ -0,0 +1,4 @@ +tile.silverlabs:portal_frame.name=Portal Frame +tile.silverlabs:portal_jamie.name=Jamie's World Portal +tile.silverlabs:portal_lyla.name=Lyla's World Portal +tile.silverlabs:portal_mya.name=Mya's World Portal diff --git a/lobby-addon/lobby_transfer_RP/textures/terrain_texture.json b/lobby-addon/lobby_transfer_RP/textures/terrain_texture.json new file mode 100644 index 0000000..fd06532 --- /dev/null +++ b/lobby-addon/lobby_transfer_RP/textures/terrain_texture.json @@ -0,0 +1,20 @@ +{ + "resource_pack_name": "lobby_transfer_RP", + "texture_name": "atlas.terrain", + "padding": 8, + "num_mip_levels": 4, + "texture_data": { + "portal_frame": { + "textures": "textures/blocks/end_portal_frame_top" + }, + "portal_jamie": { + "textures": "textures/blocks/emerald_block" + }, + "portal_lyla": { + "textures": "textures/blocks/amethyst_block" + }, + "portal_mya": { + "textures": "textures/blocks/prismarine_bricks" + } + } +}