NeoForge 26은 구식 SimpleChannel 대신 CustomPacketPayload 인터페이스를 사용합니다. Java record로 패킷 구조를 선언하고, StreamCodec으로 직렬화 방식을 지정하며, RegisterPayloadHandlersEvent로 핸들러를 등록합니다. 이 챕터에서는 서버→클라이언트 인사 메시지 패킷(GreetingPayload)을 예제로 전체 흐름을 익힙니다.
패킷 하나는 CustomPacketPayload를 구현하는 record 하나에 대응합니다.
// examplemod-template-26.1.2/src/main/java/com/example/examplemod/network/GreetingPayload.javapublic record GreetingPayload(String message) implements CustomPacketPayload { /** 패킷 식별자 — 네임스페이스:경로 형태의 고유 ID */ public static final CustomPacketPayload.Type<GreetingPayload> TYPE = new CustomPacketPayload.Type<>( Identifier.fromNamespaceAndPath("examplemod", "greeting") ); /** * StreamCodec — 네트워크 직렬화/역직렬화 정의. * composite()은 필드별 코덱과 생성자를 묶어 줍니다. */ public static final StreamCodec<FriendlyByteBuf, GreetingPayload> STREAM_CODEC = StreamCodec.composite( ByteBufCodecs.STRING_UTF8, GreetingPayload::message, GreetingPayload::new ); @Override public Type<? extends CustomPacketPayload> type() { return TYPE; }}
// 서버 → 특정 플레이어PacketDistributor.sendToPlayer(serverPlayer, new GreetingPayload("Hello!"));// 서버 → 청크를 추적 중인 모든 플레이어 (예: 블록 변경 알림)PacketDistributor.sendToPlayersTrackingChunk(serverLevel, chunkPos, payload);// 서버 → 전체 플레이어PacketDistributor.sendToAllPlayers(payload);// 클라이언트 → 서버 (클라이언트 코드에서만 호출 가능)PacketDistributor.sendToServer(payload);
// ❌ Forge Bus에 등록 → 이벤트 수신 안 됨NeoForge.EVENT_BUS.addListener(NetworkHandlers::onRegisterPayloadHandlers);// ✅ Mod Bus에 등록modEventBus.addListener(NetworkHandlers::onRegisterPayloadHandlers);
RegisterPayloadHandlersEvent는 Forge Bus(NeoForge.EVENT_BUS)가 아닌 Mod Bus에서 발행됩니다. Forge Bus에 등록하면 이벤트를 받지 못해 모든 패킷 핸들러가 누락됩니다.