NeoForge 26.1 Docs
  • 문서
  • 노트
NeoForge 26.1
NeoForge 26.1
v26.1
학습 진도
0 / 77 챕터 완료방문 0
이벤트 핸들러와 @SubscribeEventModBus vs NeoForge EventBus — 두 버스 완전 정복플레이어 이벤트틱 이벤트 — 매 tick 실행되는 로직 작성커맨드 등록 — Brigadier로 /명령어 만들기사운드 등록과 재생파티클 등록과 스폰 — 시각 효과 직접 만들기
이벤트

틱 이벤트 — 매 tick 실행되는 로직 작성

ServerTickEvent, ClientTickEvent, EntityTickEvent, LevelTickEvent의 종류와 사용법을 배우고, 카운터 패턴으로 TPS를 지키는 성능 최적화 기법을 익힙니다.

틱이란?

Minecraft의 게임 루프는 초당 20번 실행됩니다. 이 한 번의 실행 단위를 **틱(tick)**이라고 부릅니다.

단위값
1 tick0.05초 = 50ms
20 tick1초
1200 tick1분

서버가 정상 상태라면 TPS(Ticks Per Second)는 20을 유지합니다. 틱 이벤트 핸들러에서 무거운 연산을 실행하면 TPS가 떨어지고, 게임이 느려집니다.


틱 이벤트 종류

NeoForge 26은 네 가지 틱 이벤트를 제공합니다.

ServerTickEvent

서버 게임 루프의 매 tick마다 발생합니다. Pre는 tick 처리 전, Post는 tick 처리 후에 호출됩니다.

@SubscribeEvent
public static void onServerTickPre(ServerTickEvent.Pre event) {
    // tick 처리 시작 전
}
 
@SubscribeEvent
public static void onServerTickPost(ServerTickEvent.Post event) {
    // tick 처리 완료 후
    MinecraftServer server = event.getServer();
}

ServerTickEvent.Post에서 event.getServer()로 MinecraftServer 인스턴스에 접근할 수 있습니다.

ClientTickEvent

클라이언트 게임 루프의 매 tick마다 발생합니다. 클라이언트 전용 로직(HUD 업데이트, 파티클 등)에 사용합니다.

@SubscribeEvent
public static void onClientTick(ClientTickEvent.Post event) {
    // 클라이언트 tick 완료 후
    Minecraft mc = Minecraft.getInstance();
}

주의: ClientTickEvent는 클라이언트 전용입니다. @EventBusSubscriber(value = Dist.CLIENT)로 등록해야 합니다.

EntityTickEvent

엔티티별로 매 tick마다 발생합니다. 특정 엔티티 타입에만 로직을 적용할 때 유용합니다.

@SubscribeEvent
public static void onEntityTick(EntityTickEvent.Post event) {
    Entity entity = event.getEntity();
    if (entity instanceof Zombie zombie) {
        // 좀비 tick마다 실행
    }
}

LevelTickEvent

월드(Level) 단위로 매 tick마다 발생합니다. 특정 차원의 로직을 처리할 때 사용합니다.

@SubscribeEvent
public static void onLevelTick(LevelTickEvent.Post event) {
    Level level = event.getLevel();
    if (level.dimension() == Level.NETHER) {
        // 네더 월드 tick마다 실행
    }
}

실습 — 5초마다 메시지 전송

카운터를 사용해 5초(100 tick)마다 모든 플레이어에게 메시지를 보내는 핸들러를 작성합니다.

examplemod-template-26.1.2/src/main/java/com/example/examplemod/events/TickHandlers.java

package com.example.examplemod.events;
 
import net.minecraft.network.chat.Component;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerPlayer;
import net.neoforged.bus.api.SubscribeEvent;
import net.neoforged.fml.common.EventBusSubscriber;
import net.neoforged.neoforge.event.tick.ServerTickEvent;
 
@EventBusSubscriber(modid = "examplemod")
public class TickHandlers {
 
    private static int counter = 0;
 
    @SubscribeEvent
    public static void onServerTick(ServerTickEvent.Post event) {
        counter++;
        if (counter >= 100) {  // 5초 = 100 tick
            MinecraftServer server = event.getServer();
            for (ServerPlayer player : server.getPlayerList().getPlayers()) {
                player.sendSystemMessage(
                    Component.literal("5초마다 메시지")
                );
            }
            counter = 0;
        }
    }
}

핵심 포인트:

  • counter는 static 필드로 선언합니다. 인스턴스가 매 tick 생성되지 않도록 클래스 수준에서 상태를 유지합니다.
  • counter >= 100 조건이 참이면 로직을 실행하고 즉시 counter = 0으로 초기화합니다.
  • server.getPlayerList().getPlayers()는 현재 접속 중인 모든 플레이어 목록을 반환합니다.

카운터 패턴 심화

5초 간격 외에도 다양한 주기를 카운터로 표현할 수 있습니다.

@SubscribeEvent
public static void onServerTick(ServerTickEvent.Post event) {
    counter++;
 
    // 1초마다 (20 tick)
    if (counter % 20 == 0) {
        doEverySecond();
    }
 
    // 5초마다 (100 tick)
    if (counter % 100 == 0) {
        doEvery5Seconds();
    }
 
    // 1분마다 (1200 tick)
    if (counter % 1200 == 0) {
        doEveryMinute();
        counter = 0;  // 오버플로 방지
    }
}

% 연산자를 사용하면 단일 카운터로 여러 주기를 동시에 처리할 수 있습니다. 단, counter가 Integer.MAX_VALUE를 넘지 않도록 적절한 시점에 초기화해야 합니다.


성능 고려사항

틱 이벤트는 초당 20번 호출됩니다. 핸들러 하나가 1ms만 지연돼도 TPS에 영향을 줄 수 있습니다.

TPS 확인 방법: 게임 내에서 F3 키를 누르면 디버그 화면이 열립니다. 우측 상단에 TPS 수치가 표시됩니다. 20 이하로 떨어지면 성능 문제가 있다는 신호입니다.

비용이 큰 연산 목록:

연산비용대안
파일 I/O매우 높음비동기 처리 또는 주기 늘리기
네트워크 요청매우 높음별도 스레드
복잡한 경로 탐색높음캐시 또는 주기 늘리기
대규모 컬렉션 순회중간청크 단위 분할 처리
새 객체 생성낮음~중간객체 재사용 (풀링)

안티패턴

⚠️ 매 tick I/O → TPS 감소

// ❌ 매 tick 파일 쓰기 → 디스크 I/O 병목
@SubscribeEvent
public static void onServerTick(ServerTickEvent.Post event) {
    Files.writeString(Path.of("data.txt"), "tick");  // 초당 20번 쓰기
}
 
// ❌ 매 tick 새 List 생성 → GC 압박
@SubscribeEvent
public static void onServerTick(ServerTickEvent.Post event) {
    List<Player> players = new ArrayList<>(server.getPlayerList().getPlayers());  // 매 tick
}
 
// ✅ 카운터 + 캐시
@SubscribeEvent
public static void onServerTick(ServerTickEvent.Post event) {
    if (counter++ % 100 == 0) {  // 5초마다 한 번
        // 필요한 로직만 실행
    }
}

F3 화면에서 TPS를 확인하세요. 20 이하로 떨어지면 틱 핸들러를 점검해야 합니다.


정리

  • 틱 이벤트는 초당 20번 호출됩니다. 1 tick = 50ms.
  • ServerTickEvent.Post에서 event.getServer()로 서버 인스턴스에 접근합니다.
  • 카운터 패턴(counter % N == 0)으로 실행 빈도를 조절합니다.
  • 매 tick 파일 I/O, 네트워크 요청, 대규모 객체 생성은 TPS를 떨어뜨립니다.
  • F3 디버그 화면으로 TPS를 실시간 확인할 수 있습니다.

다음 챕터에서는 플레이어 이벤트를 다룹니다. 로그인, 로그아웃, 아이템 사용 등 플레이어 행동에 반응하는 핸들러를 작성합니다.

플레이어 이벤트

PlayerLoggedInEvent, LivingHurtEvent, LivingDeathEvent 등 주요 플레이어/Living 이벤트를 구현하고, 사이드 분기 처리로 클라이언트-서버 동기화 문제를 예방합니다.

커맨드 등록 — Brigadier로 /명령어 만들기

RegisterCommandsEvent와 Brigadier 빌더 패턴을 사용해 커스텀 슬래시 커맨드를 등록하는 방법을 배웁니다. 인자 타입, 권한 레벨, 실행 로직까지 단계별로 익힙니다.

On this page

틱이란?틱 이벤트 종류ServerTickEventClientTickEventEntityTickEventLevelTickEvent실습 — 5초마다 메시지 전송카운터 패턴 심화성능 고려사항안티패턴정리
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