NeoForge 26.1 Docs
  • 문서
  • 노트
NeoForge 26.1
NeoForge 26.1
v26.1
학습 진도
0 / 77 챕터 완료방문 0
마스터 트랙 #2: 차원 모드 프로젝트 셋업차원 등록 — 데이터팩 3종 JSON포탈 블록 + 점화 아이템포탈 프레임 인식 로직차원 진입·복귀 로직차원 룰셋 — 낮/밤, 침대, 스카이박스차원 모드 마무리 + JAR 빌드
마스터차원

차원 진입·복귀 로직

MagicPortalBlock.entityInside 오버라이드로 플레이어가 포탈 블록에 접촉할 때 magic_realm ↔ 오버월드 간 양방향 차원 이동을 구현합니다. 무한 TP 루프 방지 쿨다운 패턴도 다룹니다.

차원 진입·복귀 로직

이 챕터에서는 포탈 블록에 엔티티가 접촉할 때 실제 차원 이동이 일어나도록 entityInside 메서드를 구현합니다.

완성 후 동작:

  • 오버월드 → magic_realm: 포탈 블록을 밟으면 마법 차원으로 순간이동
  • magic_realm → 오버월드: 마법 차원의 포탈 블록을 밟으면 복귀
  • 무한 TP 루프 방지: 쿨다운 체크로 즉각 재진입 차단

1. entityInside 오버라이드

왜 entityInside인가?

⚠️ NetherPortalBlock 직접 상속 금지

NetherPortalBlock은 내부 포탈 상태 관리 로직이 복잡합니다. Block을 직접 상속하고 entityInside만 오버라이드하면 버전 간 안정성이 높습니다.

entityInside(BlockState, Level, BlockPos, Entity)는 매 틱마다 해당 블록 위치에 엔티티가 존재하면 호출됩니다. 이 메서드에서 서버 측 플레이어를 감지해 차원 이동을 처리합니다.

전체 구현

// examplemod-master-projects/dimension/src/main/java/com/example/master/dimension/block/MagicPortalBlock.java
package com.example.master.dimension.block;
 
import net.minecraft.core.BlockPos;
import net.minecraft.resources.Identifier;
import net.minecraft.resources.ResourceKey;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Portal;
import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.portal.TeleportTransition;
import net.minecraft.world.phys.Vec3;
import net.minecraft.core.registries.Registries;
 
public class MagicPortalBlock extends Block implements Portal {
 
    /** magic_realm 차원 키 — 데이터팩 JSON과 동일한 네임스페이스/ID */
    public static final ResourceKey<Level> MAGIC_REALM_KEY =
            ResourceKey.create(Registries.DIMENSION,
                    Identifier.fromNamespaceAndPath("master_dimension", "magic_realm"));
 
    public MagicPortalBlock(BlockBehaviour.Properties props) {
        super(props
                .noCollision()
                .strength(-1.0F, 3600000.0F)
                .noLootTable());
    }
 
    public void entityInside(BlockState state, Level level, BlockPos pos, Entity entity) {
        // 엔티티가 포탈에 들어오면 PortalProcessor가 작동하도록 등록
        entity.setAsInsidePortal(this, pos);
    }
 
    @Override
    public TeleportTransition getPortalDestination(ServerLevel serverLevel, Entity entity, BlockPos pos) {
        ServerLevel target;
        if (serverLevel.dimension() == MAGIC_REALM_KEY) {
            // magic_realm → 오버월드 복귀
            target = serverLevel.getServer().getLevel(Level.OVERWORLD);
        } else {
            // 오버월드(또는 기타 차원) → magic_realm 진입
            target = serverLevel.getServer().getLevel(MAGIC_REALM_KEY);
        }
 
        if (target == null) {
            // 차원이 로드되지 않았거나 데이터팩 JSON 누락 시 무동작
            return null;
        }
 
        // 현재 좌표 기준 Y=64 안전 고도로 이동
        Vec3 currentPos = entity.position();
        return new TeleportTransition(
                target,
                new Vec3(currentPos.x(), 64.0, currentPos.z()),  // 안전 높이 Y=64
                entity.getDeltaMovement(),
                entity.getYRot(),
                entity.getXRot(),
                TeleportTransition.PLAY_PORTAL_SOUND              // 포탈 사운드 재생
        );
    }
}

2. 무한 TP 루프 방지

⚠️ 안티패턴 — 쿨다운 없는 즉시 TP

// ❌ 쿨다운 체크 없이 즉시 TP
public void entityInside(BlockState state, Level level, BlockPos pos, Entity entity) {
    if (level.isClientSide() || !(entity instanceof ServerPlayer player)) return;
    ServerLevel target = player.getServer().getLevel(MAGIC_REALM_KEY);
    player.changeDimension(...);  // ← 매 틱 호출 → 무한 루프
}
 
// ✅ 쿨다운 체크
if (player.isOnPortalCooldown()) return;
player.setPortalCooldown();

changeDimension 호출 전 반드시 setPortalCooldown()를 설정하세요. 도착 차원에서도 같은 포탈 블록에 충돌하면 다시 entityInside가 호출되어 무한 루프가 발생합니다.

쿨다운 동작 원리

메서드역할
player.isOnPortalCooldown()포탈 쿨다운이 남아 있으면 true 반환
player.setPortalCooldown()기본 쿨다운(약 1초) 설정

쿨다운 중에는 entityInside가 호출되더라도 return으로 즉시 탈출합니다.


3. DimensionTransition 파라미터

player.changeDimension(new DimensionTransition(
    target,                                        // 목적지 ServerLevel
    new Vec3(currentPos.x(), 64.0, currentPos.z()), // 스폰 좌표 (Y=64 안전 고도)
    player.getDeltaMovement(),                     // 속도 유지
    player.getYRot(),                              // 수평 회전 유지
    player.getXRot(),                              // 수직 회전 유지
    DimensionTransition.PLAY_PORTAL_SOUND          // 포탈 사운드 재생
));
파라미터설명
target이동할 ServerLevel — getLevel(key)로 획득
new Vec3(x, 64.0, z)목적지 좌표. currentPos의 XZ 유지, Y=64 고정
getDeltaMovement()현재 이동 벡터 — 관성 유지
getYRot() / getXRot()카메라 방향 유지
PLAY_PORTAL_SOUND포탈 통과 사운드 재생 콜백

Y=64 선택 이유: 이 챕터의 magic_realm은 min_y: -64, height: 384로 설정되어 있어 Y=64는 지형 중간 안전 지점입니다. 목적지 지형이 달라질 경우 이 값을 조정하세요.


4. 양방향 이동 로직

entityInside 호출
    │
    ├─ isClientSide? → return (클라이언트 무시)
    ├─ instanceof ServerPlayer? → NO → return (몬스터/아이템 무시)
    ├─ isOnPortalCooldown? → YES → return (쿨다운 중)
    │
    └─ setPortalCooldown()
           │
           ├─ dimension == MAGIC_REALM_KEY?
           │     ├─ YES → target = OVERWORLD (복귀)
           │     └─ NO  → target = MAGIC_REALM_KEY (진입)
           │
           └─ changeDimension(DimensionTransition)

5. PortalForcer 패턴 (선택 — 다음 챕터)

현재 구현에서는 목적지에 포탈이 없어도 Y=64로 이동합니다. 실제 제작 시에는 귀환 포탈 자동 생성 기능이 필요합니다.

PortalForcer 또는 커스텀 텔레포터를 구현해 목적지에 포탈 프레임을 자동 생성하는 로직은 05-finish-build 챕터에서 마무리합니다.

// 05-finish-build에서 추가 예정 — 현재는 스텁
// DimensionTransition의 5번째 파라미터에 커스텀 포탈 콜백 주입 가능

6. 2초 대기 후 진입 (선택)

포탈에 닿는 즉시 이동하는 대신 2초 대기 후 진입하는 방식:

// 선택적 구현 — 네더 포탈과 동일한 방식
@Override
public void entityInside(BlockState state, Level level, BlockPos pos, Entity entity) {
    if (level.isClientSide()) return;
    if (!(entity instanceof ServerPlayer player)) return;
 
    // 진입 틱 카운트 (포탈 블록이 BlockState를 통해 틱을 관리하는 방식)
    // 단순 구현: setPortalCooldown 사용 시 즉시 이동
    if (player.isOnPortalCooldown()) return;
    player.setPortalCooldown();
 
    // 2초 대기는 BlockEntity + tick을 활용하거나
    // player.startUsingPortal() 등을 사용 (NeoForge API 참고)
    // 이 챕터에서는 즉시 이동 방식을 채택
    teleportPlayer(player);
}

이 챕터에서는 즉시 이동 방식을 채택합니다. 2초 대기가 필요하다면 BlockEntity 틱 카운터 또는 NeoForge의 IPortalForcer 확장을 활용하세요.


7. 서버 전용 실행의 중요성

if (level.isClientSide()) return;
환경동작
클라이언트isClientSide() == true → 즉시 return
서버isClientSide() == false → TP 로직 실행

changeDimension은 서버 전용 API입니다. 클라이언트에서 호출하면 ClassCastException 또는 NullPointerException이 발생합니다.


다음 단계

entityInside 구현으로 포탈 진입·복귀 기능이 완성됩니다.

  1. 05-finish-build — Gradle 빌드 통합 + 인게임 종합 검증
    • 포탈 프레임 점화 → 차원 진입 → 복귀 전체 시나리오 테스트
    • 귀환 포탈 자동 생성 (PortalForcer)

포탈 프레임 인식 로직

PortalShape 헬퍼 클래스로 흑요석 프레임을 탐색·검증하고, MagicIgniter에서 isValidFrame을 호출해 유효한 프레임에만 포탈 블록을 채우는 로직을 구현합니다.

차원 룰셋 — 낮/밤, 침대, 스카이박스

dimension_type JSON의 fixed_time, bed_works, effects 등 핵심 속성을 이해하고 magic_realm을 영구 밤 차원으로 설정하는 방법을 다룹니다.

On this page

차원 진입·복귀 로직1. entityInside 오버라이드왜 entityInside인가?전체 구현2. 무한 TP 루프 방지쿨다운 동작 원리3. DimensionTransition 파라미터4. 양방향 이동 로직5. PortalForcer 패턴 (선택 — 다음 챕터)6. 2초 대기 후 진입 (선택)7. 서버 전용 실행의 중요성다음 단계
NeoForge 26.1 Docs

NeoForge 26.1 모딩 개발 문서 사이트

GitHubDiscord

문서

  • 문서
  • 노트

GitHub

  • GitHub
  • Discord

© 2026 NeoForge 26.1 Docs. 콘텐츠는 MIT 라이선스로 제공됩니다.

Built with Next.js · Tailwind CSS · shadcn/ui