컴포넌트 타입은 DeferredRegister.DataComponents로 등록합니다. 아이템 등록과 같은 패턴입니다.
// examplemod-template-26.1.2/src/main/java/com/example/examplemod/ModDataComponents.javapublic class ModDataComponents { // 26.1: createDataComponents 는 (레지스트리 키, modid) 2-인자. 단일 String 오버로드 없음. public static final DeferredRegister.DataComponents COMPONENTS = DeferredRegister.createDataComponents(Registries.DATA_COMPONENT_TYPE, ExampleMod.MODID); // 정수형 충전량 컴포넌트 public static final Supplier<DataComponentType<Integer>> CHARGE = COMPONENTS.registerComponentType( "charge", builder -> builder .persistent(Codec.INT) // 저장/로드용 Codec .networkSynchronized(ByteBufCodecs.INT) // 네트워크 동기화용 StreamCodec ); // 문자열형 소유자 컴포넌트 public static final Supplier<DataComponentType<String>> OWNER = COMPONENTS.registerComponentType( "owner", builder -> builder .persistent(Codec.STRING) .networkSynchronized(ByteBufCodecs.STRING_UTF8) );}
그리고 메인 모드 클래스에서 버스에 등록합니다.
// examplemod-template-26.1.2/src/main/java/com/example/examplemod/ExampleMod.java@Mod("examplemod")public class ExampleMod { public ExampleMod(IEventBus modEventBus) { ModDataComponents.COMPONENTS.register(modEventBus); // ... 나머지 등록 }}
마법 막대를 우클릭할 때마다 충전량이 오르고, 충전량이 가득 차면 방전되는 아이템을 만들어 봅니다.
// examplemod-template-26.1.2/src/main/java/com/example/examplemod/item/MagicWandItem.javapublic class MagicWandItem extends Item { private static final int MAX_CHARGE = 5; public MagicWandItem(Properties properties) { super(properties); } @Override public InteractionResult use(Level level, Player player, InteractionHand hand) { // 손에 든 스택을 직접(in-place) 수정한다. 26.1 에서 InteractionResultHolder 가 삭제되어 // 더 이상 새 스택을 래핑해 반환하지 않는다. ItemStack stack = player.getItemInHand(hand); int current = stack.getOrDefault(ModDataComponents.CHARGE.get(), 0); if (current >= MAX_CHARGE) { // 방전 stack.set(ModDataComponents.CHARGE.get(), 0); player.sendSystemMessage(Component.literal("방전!")); } else { // 충전 stack.set(ModDataComponents.CHARGE.get(), current + 1); player.sendSystemMessage( Component.literal("충전량: " + (current + 1) + "/" + MAX_CHARGE) ); } return InteractionResult.SUCCESS; }}
아이템을 등록할 때는 26.1 의 registerItem 을 씁니다. 람다는 NeoForge 가 id 를 미리 채운 Properties 를 넘겨주므로, 그 안에서 변형(.component(), .durability(), .stacksTo() 등)을 적용합니다. 람다 밖에서 new Item.Properties() 를 직접 만들면 id 가 설정되지 않아 크래시합니다. 기본 컴포넌트 값이 필요하면 람다 안에서 p -> p.component(ModDataComponents.CHARGE.get(), 0) 처럼 붙이면 됩니다.
// examplemod-template-26.1.2/src/main/java/com/example/examplemod/ExampleMod.java// 아이템 등록 — registerItem 의 props 람다 안에서 변형을 적용한다.public static final DeferredItem<MagicWandItem> MAGIC_WAND = ITEMS.registerItem("magic_wand", MagicWandItem::new, p -> p.stacksTo(1));
단순 int나 String이 아닌 사용자 정의 값 타입을 저장할 때는 record로 정의하고, Codec과 StreamCodec을 함께 제공합니다. 아래 예제는 0~100 범위의 신선도 값 하나를 갖는 record로, Codec.intRange(...).xmap(...)으로 범위를 검증하고 ByteBufCodecs.VAR_INT.map(...)으로 네트워크 동기화합니다.
// examplemod-template-26.1.2/src/main/java/com/example/examplemod/data/FoodFreshness.javapublic record FoodFreshness(int freshness) { // 저장/로드용 Codec — 0~100 범위로 검증한다. public static final Codec<FoodFreshness> CODEC = Codec.intRange(0, 100).xmap(FoodFreshness::new, FoodFreshness::freshness); // 네트워크 동기화용 StreamCodec. VAR_INT 기반이라 버퍼 타입은 ByteBuf 이다. public static final StreamCodec<ByteBuf, FoodFreshness> STREAM_CODEC = ByteBufCodecs.VAR_INT.map(FoodFreshness::new, FoodFreshness::freshness);}
stack.set(ModDataComponents.FRESHNESS.get(), new FoodFreshness(100));FoodFreshness freshness = stack.getOrDefault( ModDataComponents.FRESHNESS.get(), new FoodFreshness(0));