import { world, system, ItemStack } from "@minecraft/server"; // ─── Constants ────────────────────────────────────────────── const PRIVATE_BLOCK = "silverlabs:private_chest"; const VANILLA_CHEST = "minecraft:chest"; const PROP_KEY = "private_chests_v1"; // ─── State ────────────────────────────────────────────────── // In-memory mirror of dynamic property: { "x,y,z,dim": { ownerId, ownerName } } let chests = {}; function loadState() { try { const raw = world.getDynamicProperty(PROP_KEY); if (raw && typeof raw === "string") { chests = JSON.parse(raw); } } catch (e) { world.sendMessage(`§c[Private Chest] Failed to load state: ${e.message}`); chests = {}; } } function saveState() { try { world.setDynamicProperty(PROP_KEY, JSON.stringify(chests)); } catch (e) { world.sendMessage(`§c[Private Chest] Failed to save state: ${e.message}`); } } function keyOf(loc, dimensionId) { return `${Math.floor(loc.x)},${Math.floor(loc.y)},${Math.floor(loc.z)},${dimensionId}`; } function claim(loc, dimensionId, player) { chests[keyOf(loc, dimensionId)] = { ownerId: player.id, ownerName: player.name }; saveState(); } function release(loc, dimensionId) { delete chests[keyOf(loc, dimensionId)]; saveState(); } function getOwner(loc, dimensionId) { return chests[keyOf(loc, dimensionId)] || null; } // ─── Chest facing from player rotation ────────────────────── // Bedrock yaw: 0=south, 90=west, 180=north, -90=east. Chest front faces the player. function chestFacing(yaw) { let y = yaw; while (y > 180) y -= 360; while (y < -180) y += 360; if (y >= -45 && y < 45) return "north"; // player facing south if (y >= 45 && y < 135) return "east"; // player facing west if (y >= -135 && y < -45) return "west"; // player facing east return "south"; // player facing north } // ─── Placement: swap custom block → vanilla chest + claim ── world.afterEvents.playerPlaceBlock.subscribe((event) => { const block = event.block; if (block.typeId !== PRIVATE_BLOCK) return; const player = event.player; const loc = block.location; const dim = block.dimension; const facing = chestFacing(player.getRotation().y); try { dim.runCommand( `setblock ${loc.x} ${loc.y} ${loc.z} chest ["minecraft:cardinal_direction":"${facing}"]` ); } catch (e) { // Older block-state syntax fallback (rare) try { dim.runCommand(`setblock ${loc.x} ${loc.y} ${loc.z} chest`); } catch (_) {} } claim(loc, dim.id, player); player.sendMessage(`§6[Private Chest] §7Locked to you. Only you can open or break it.`); }); // ─── Interact: block opening for non-owners ───────────────── try { world.beforeEvents.playerInteractWithBlock.subscribe((event) => { const block = event.block; if (block.typeId !== VANILLA_CHEST) return; const owner = getOwner(block.location, block.dimension.id); if (!owner) return; if (owner.ownerId === event.player.id) return; event.cancel = true; const playerRef = event.player; system.run(() => playerRef.sendMessage(`§c[Private Chest] §7This chest belongs to §f${owner.ownerName}§7.`) ); }); } catch (e) { console.warn(`[Private Chest] beforeEvents.playerInteractWithBlock unavailable: ${e}`); } // ─── Break: protect for non-owners; owner break drops the custom item back ── try { world.beforeEvents.playerBreakBlock.subscribe((event) => { const block = event.block; if (block.typeId !== VANILLA_CHEST) return; const owner = getOwner(block.location, block.dimension.id); if (!owner) return; const player = event.player; if (owner.ownerId !== player.id) { event.cancel = true; system.run(() => player.sendMessage(`§c[Private Chest] §7This chest belongs to §f${owner.ownerName}§7. You can't break it.`) ); return; } // Owner breaking — cancel default drop, manually eject contents + return the custom item. event.cancel = true; const loc = { x: block.location.x, y: block.location.y, z: block.location.z }; const dim = block.dimension; system.run(() => { try { const inv = dim.getBlock(loc)?.getComponent("inventory"); const container = inv?.container; const dropPos = { x: loc.x + 0.5, y: loc.y + 0.5, z: loc.z + 0.5 }; if (container) { for (let i = 0; i < container.size; i++) { const item = container.getItem(i); if (item) { dim.spawnItem(item, dropPos); container.setItem(i, undefined); } } } dim.spawnItem(new ItemStack(PRIVATE_BLOCK, 1), dropPos); dim.runCommand(`setblock ${loc.x} ${loc.y} ${loc.z} air`); } catch (e) { player.sendMessage(`§c[Private Chest] Error during break: ${e.message}`); } release(loc, dim.id); }); }); } catch (e) { console.warn(`[Private Chest] beforeEvents.playerBreakBlock unavailable: ${e}`); } // ─── Boot ────────────────────────────────────────────────── system.run(() => { loadState(); world.sendMessage("§6[Private Chest] §7Owner-locked chest system loaded."); });