NeoForge 26.1 Docs
  • 문서
  • 노트
NeoForge 26.1
NeoForge 26.1
v26.1
학습 진도
0 / 77 챕터 완료방문 0
첫 블록 등록 — first_blockBlockBehaviour.Properties — strength, lightLevel, sound, requiresCorrectToolForDropsblockstate JSON과 모델 JSON특수 블록 타입 (계단·슬랩·펜스)블록 이벤트 — use, onPlace, onRemove루트 테이블 — 블록 드롭 JSON 직접 작성
블록

블록 이벤트 — use, onPlace, onRemove

커스텀 Block 클래스를 만들어 use(우클릭), onPlace(설치), onRemove(제거) 이벤트를 오버라이드하는 방법을 학습합니다. 사이드 분기 가드 패턴도 함께 다룹니다.

블록 이벤트 — use, onPlace, onRemove

기본 Block 클래스는 우클릭·설치·제거 같은 상호작용에 아무 반응도 하지 않습니다. 이 챕터에서는 Block을 상속해 커스텀 Block 클래스를 만들고, 세 가지 핵심 이벤트 메소드를 오버라이드하는 방법을 배웁니다.


1. 커스텀 Block 클래스 만들기

Block을 직접 상속하는 클래스를 새로 만듭니다. 파일 위치는 ExampleMod.java와 같은 패키지 안에 두면 됩니다.

// examplemod-template-26.1.2/src/main/java/com/example/examplemod/block/GreetingBlock.java
package com.example.examplemod.block;
 
import net.minecraft.core.BlockPos;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.BlockHitResult;
 
public class GreetingBlock extends Block {
 
    public GreetingBlock(BlockBehaviour.Properties props) {
        super(props);
    }
 
    // 플레이어가 빈손으로 블록을 우클릭할 때 호출 (26.1.2: use → useWithoutItem)
    @Override
    protected InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos,
                                               Player player, BlockHitResult hit) {
        if (!level.isClientSide()) {
            player.sendSystemMessage(Component.literal("Hello from block at " + pos));
        }
        return InteractionResult.SUCCESS;
    }
 
    // 블록이 세계에 설치될 때 호출 (시그니처 26.1.2 확인 완료)
    @Override
    protected void onPlace(BlockState state, Level level, BlockPos pos,
                           BlockState oldState, boolean movedByPiston) {
        if (!level.isClientSide()) {
            level.playSound(null, pos, SoundEvents.AMETHYST_BLOCK_PLACE,
                            SoundSource.BLOCKS, 1.0f, 1.0f);
        }
    }
 
    // 블록이 세계에서 제거된 뒤 이웃 블록을 갱신할 때 호출 (26.1.2: onRemove → affectNeighborsAfterRemoval, Level→ServerLevel)
    @Override
    protected void affectNeighborsAfterRemoval(BlockState state, ServerLevel level, BlockPos pos,
                                               boolean movedByPiston) {
        // BlockEntity를 사용했다면 여기서 정리
        super.affectNeighborsAfterRemoval(state, level, pos, movedByPiston);
    }
}

super.affectNeighborsAfterRemoval(...) 호출을 빠뜨리면 BlockEntity가 있는 블록에서 데이터가 누수됩니다. 항상 super를 먼저 호출하거나, 정리 로직을 super 호출 전에 배치하세요.


2. 오버라이드 시점 표

메소드호출 시점주요 용도
useWithoutItem플레이어가 빈손으로 우클릭GUI 열기, 메시지 전송, 아이템 소비
onPlace블록 설치 시사운드 재생, 이웃 블록 알림
affectNeighborsAfterRemoval블록 제거 후BlockEntity 정리, 드롭 처리
tick매 tick (등록 필요)타이머, 성장, 자동화
neighborChanged이웃 블록 변경 시레드스톤 반응, 연쇄 업데이트

tick은 BlockState에 randomTicks(true) 또는 BlockBehaviour.Properties.randomTicks()를 설정해야 활성화됩니다.


3. InteractionResult 반환값

useWithoutItem 메소드는 반드시 InteractionResult를 반환해야 합니다. 반환값에 따라 게임이 다음 동작을 결정합니다.

값의미
SUCCESS성공. 플레이어 손 흔들기 애니메이션 재생
CONSUME성공. 손 흔들기 애니메이션 없음
PASS이 핸들러를 건너뜀. 다음 핸들러로 전달
FAIL실패. 아무 동작도 하지 않음

아이템을 소비하는 상호작용(예: 물약 사용)에는 CONSUME을 씁니다. 단순 정보 표시에는 SUCCESS가 적합합니다.


4. 블록 등록에 커스텀 클래스 연결

ExampleMod.java에서 GreetingBlock을 등록합니다.

// examplemod-template-26.1.2/src/main/java/com/example/examplemod/ExampleMod.java
public static final DeferredBlock<GreetingBlock> GREETING_BLOCK = BLOCKS.registerBlock("greeting_block",
        GreetingBlock::new,
        p -> p.strength(2.0f));

26.1.2에서는 BLOCKS.registerBlock(...)에 GreetingBlock::new를 팩토리로 넘기고, 마지막 람다에서 Properties(파라미터 p)에 속성을 체이닝합니다. DeferredBlock의 타입 파라미터도 GreetingBlock으로 맞춰 두면 나중에 캐스팅 없이 접근할 수 있습니다.


5. 사이드 분기 — 가드 패턴

⚠️ 양 사이드 호출 미고려

// ❌ 클라이언트와 서버 모두에서 메시지를 보내면 이중 출력
protected InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos,
                                           Player player, BlockHitResult hit) {
    player.sendSystemMessage(Component.literal("Hello!"));  // 양 사이드 호출됨
    return InteractionResult.SUCCESS;
}
 
// ✅ 서버에서만 실행
protected InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos,
                                           Player player, BlockHitResult hit) {
    if (!level.isClientSide()) {
        player.sendSystemMessage(Component.literal("Hello!"));
    }
    return InteractionResult.SUCCESS;
}

useWithoutItem, onPlace, affectNeighborsAfterRemoval은 클라이언트와 서버 양쪽에서 모두 호출됩니다. 서버 전용 로직(메시지 전송, DB 저장, 아이템 지급)은 반드시 if (!level.isClientSide()) 가드 안에 넣으세요.

반대로 파티클·사운드 재생은 클라이언트에서만 해야 할 때도 있습니다. 그럴 때는 if (level.isClientSide()) 블록을 씁니다.


6. 주의사항

  • 무거운 작업 금지: useWithoutItem·onPlace·affectNeighborsAfterRemoval은 게임 루프 메인 스레드에서 실행됩니다. 네트워크 호출·파일 I/O·긴 루프는 절대 넣지 마세요. 서버 TPS가 즉시 떨어집니다.
  • BlockEntity 없이 상태 저장 금지: 블록 자체에 필드를 추가해 상태를 저장하려는 시도는 동작하지 않습니다. 블록 인스턴스는 공유되기 때문입니다. 상태가 필요하면 BlockEntity를 사용하세요 (phase-4에서 다룹니다).
  • affectNeighborsAfterRemoval에서 super 호출: BlockEntity가 없더라도 super.affectNeighborsAfterRemoval(...)를 호출하는 습관을 들이세요. 나중에 BlockEntity를 추가할 때 버그를 예방합니다.

정리

이 챕터에서 배운 내용:

  • Block을 상속해 커스텀 클래스를 만드는 방법
  • useWithoutItem, onPlace, affectNeighborsAfterRemoval 오버라이드 시점과 용도
  • InteractionResult 반환값의 차이
  • if (!level.isClientSide()) 가드로 서버 전용 로직 분리

다음 챕터에서는 블록에 BlockEntity를 붙여 상태를 저장하는 방법을 다룹니다.

특수 블록 타입 (계단·슬랩·펜스)

StairBlock, SlabBlock, FenceBlock을 등록하고 각 타입에 맞는 blockstate variant 패턴을 작성하는 방법을 학습합니다. variant 누락 시 보라/검정 큐브가 나타나는 원인과 해결법도 다룹니다.

루트 테이블 — 블록 드롭 JSON 직접 작성

data/<modId>/loot_table/blocks/ 경로에 JSON을 직접 작성해 블록 채굴 드롭을 정의합니다. 단순 드롭, 변동 드롭, Fortune 보너스, 올바른 도구 조건까지 다룹니다.

On this page

블록 이벤트 — use, onPlace, onRemove1. 커스텀 Block 클래스 만들기2. 오버라이드 시점 표3. InteractionResult 반환값4. 블록 등록에 커스텀 클래스 연결5. 사이드 분기 — 가드 패턴6. 주의사항정리
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