import { world, system } from "@minecraft/server";
// 플레이어별 미리보기 엔티티 관리용 맵
const previewEntities = new Map();
const CUSTOM_ENTITY_ID = "bridge:miribogi";
// ============================
// 🧭 1. 방향 계산 엔진
// ============================
function getAbsoluteRotation(player) {
const dir = player.getViewDirection();
const ax = Math.abs(dir.x);
const az = Math.abs(dir.z);
if (az > ax) {
return dir.z > 0 ? { yRot: 0, dirX: 0, dirZ: 1 } : { yRot: 180, dirX: 0, dirZ: -1 };
} else {
return dir.x > 0 ? { yRot: 270, dirX: 1, dirZ: 0 } : { yRot: 90, dirX: -1, dirZ: 0 };
}
}
// ============================
// 🧱 2. 미리보기 엔진
// ============================
system.runInterval(() => {
for (const player of world.getAllPlayers()) {
const equippable = player.getComponent("minecraft:equippable");
if (!equippable) continue;
const item = equippable.getEquipment("Mainhand");
if (!item || item.typeId !== "minecraft:smooth_stone_slab") {
removePreviewEntity(player.id);
continue;
}
const ray = player.getBlockFromViewDirection({ maxDistance: 5 });
if (!ray) {
removePreviewEntity(player.id);
continue;
}
const face = String(ray.face).toLowerCase();
let targetPos = { ...ray.block.location };
if (face === "up") targetPos.y += 1;
else if (face === "down") targetPos.y -= 1;
else if (face === "north") targetPos.z -= 1;
else if (face === "south") targetPos.z += 1;
else if (face === "west") targetPos.x -= 1;
else if (face === "east") targetPos.x += 1;
const spawnPos = { x: targetPos.x + 0.5, y: targetPos.y, z: targetPos.z + 0.5 };
const rotInfo = getAbsoluteRotation(player);
let entity = previewEntities.get(player.id);
if (!entity || !entity.isValid()) {
entity = player.dimension.spawnEntity(CUSTOM_ENTITY_ID, spawnPos);
previewEntities.set(player.id, entity);
}
try {
entity.teleport(spawnPos, { rotation: { x: 0, y: rotInfo.yRot } });
} catch (e) {}
}
}, 2);
function removePreviewEntity(playerId) {
const entity = previewEntities.get(playerId);
// 1. 엔티티가 존재하고, 유효한(isValid) 객체인지 먼저 확인
if (entity && typeof entity.remove === 'function' && entity.isValid()) {
try {
entity.remove();
} catch (e) {
// 삭제 중 발생할 수 있는 잠재적 오류 무시
}
}
// 2. 맵에서 안전하게 삭제
previewEntities.delete(playerId);
}
// ============================
// 🏗️ 3. 설치 엔진
// ============================
world.afterEvents.itemUseOn.subscribe(ev => {
const player = ev.source;
if (ev.itemStack?.typeId !== "minecraft:smooth_stone_slab") return;
removePreviewEntity(player.id);
const rotInfo = getAbsoluteRotation(player);
const pos = ev.block.location;
const face = String(ev.blockFace).toLowerCase();
let targetPos = { ...pos };
if (face === "up") targetPos.y += 1;
else if (face === "down") targetPos.y -= 1;
else if (face === "north") targetPos.z -= 1;
else if (face === "south") targetPos.z += 1;
else if (face === "west") targetPos.x -= 1;
else if (face === "east") targetPos.x += 1;
player.dimension.runCommandAsync(`setblock ${targetPos.x} ${targetPos.y} ${targetPos.z} smooth_stone_slab`);
const entity = player.dimension.spawnEntity(CUSTOM_ENTITY_ID, { x: targetPos.x + 0.5, y: targetPos.y, z: targetPos.z + 0.5 });
entity.nameTag = `conv:${rotInfo.dirX}:${rotInfo.dirZ}`;
entity.teleport(entity.location, { rotation: { x: 0, y: rotInfo.yRot } });
});
// ============================
// 🚀 4. 정밀 이동 엔진
// ============================
system.runInterval(() => {
for (const player of world.getAllPlayers()) {
const dim = player.dimension;
for (const item of dim.getEntities({ type: "item" })) {
if (!item.isValid()) continue;
const nearBelts = dim.getEntities({ type: CUSTOM_ENTITY_ID, location: item.location, maxDistance: 0.6 });
if (nearBelts.length === 0) continue;
const tag = nearBelts[0].nameTag;
if (!tag || !tag.startsWith("conv:")) continue;
const [_, dx, dz] = tag.split(":");
const dirX = parseInt(dx);
const dirZ = parseInt(dz);
// 물리 오류 방지: setLinearVelocity 대신 속도를 반전시켜 정지 구현
const vel = item.getVelocity();
if (Math.abs(vel.x) > 0.2 || Math.abs(vel.z) > 0.2) {
item.applyImpulse({ x: -vel.x * 0.8, y: 0, z: -vel.z * 0.8 });
} else {
item.applyImpulse({ x: dirX * 0.08, y: 0, z: dirZ * 0.08 });
}
}
}
}, 2);
To embed this project on your website, copy the following code and paste it into your website's HTML: