모딩/마인크래프트 모드 개발 일지

[마인크래프트 모드 개발 일지]25.06.23

kimchangmin02 2025. 6. 23. 23:12

오늘 버그수정한 것들 모음

 

사건 파일 #1: AI의 독점과 양보의 철학

  • 사건 현장: 모든 커스텀 주민(탱커, 궁수, 힐러)들이 아무런 행동도 하지 않고 멍하니 서 있거나, 바닐라 행동(도망치기 등)만 반복하는 치명적인 버그가 발생함.
  • 초동 수사: 각 주민의 AI 코드(TauntGoal, SkirmishGoal 등)에 문제가 있다고 생각하고 모든 파일을 하나하나 뜯어봤음. 하지만 개별 코드에는 문법적 오류가 없었음.
  • 진짜 범인: 뜻밖에도 탱커의 **RegenGoal(체력 회복 AI)**에 있었음.

Generated java

// 문제의 코드
@Override
public boolean canUse() {
    return true; 
}
Use code with caution.Java

나는 '체력 회복은 항상 가능해야지'라는 생각에 무심코 true를 반환하도록 했음. 하지만 이 RegenGoal은 **가장 높은 우선순위(0)**로 등록되어 있었음.

[밝혀진 원리] AI 태스크 스케줄러의 독점적 실행(Exclusive Execution)

마인크래프트의 AI 시스템(GoalSelector)은 **'독점적'**으로 작동함. 매 틱마다 가장 높은 우선순위의 AI부터 차례대로 canUse()를 물어보고, true를 반환하는 첫 번째 AI를 발견하면 그 AI에게 실행 권한을 독점적으로 부여함. 그리고 그보다 낮은 우선순위의 AI들에게는 더 이상 질문조차 하지 않음.

즉, 우선순위 0인 RegenGoal이 항상 "나 실행할 수 있어!"라고 외치고 있으니, 게임은 다른 모든 AI(도발, 공격 등)에게는 기회조차 주지 않고 매번 RegenGoal만 실행하려고 했던 것임. RegenGoal이 시스템 자원을 독점(Hogging)한 셈임.

  • 교훈: canUse()는 "언제든 할 수 있다"가 아니라, **"지금 당장 이 행동을 시작해야만 하는 명백한 이유가 있는가?"**라는 질문에 가까움.
  • 해결: canUse()의 조건을 "내 체력이 최댓값보다 낮을 때만!"으로 변경하자, RegenGoal은 필요한 순간에만 실행을 요청하게 되었고, 다른 AI들에게 실행 기회를 '양보'하게 되었음.

사건 파일 #2: 체크무늬의 비밀, 리소스와 네임스페이스

  • 사건 현장: 다이아몬드 좀비 스폰 알 아이템은 인게임에 추가되었지만, 텍스처가 깨진 것처럼 보라색과 검은색 체크무늬로 보였음.
  • 원인 분석: 리소스 로케이션(Resource Location)과 네임스페이스(Namespace) 시스템에 대한 이해 부족.

Java 코드는 아이템의 '정의(존재와 속성)'를 담당하고, resources 폴더는 아이템의 '외형(모델과 텍스처)'을 담당함. 스폰 알 아이템은 일반 .png 텍스처가 아니라, "마인크래프트의 기본 스폰 알 템플릿을 사용하고, Java 코드에 정의된 색상을 칠해라" 라고 지시하는 JSON 모델 파일이 필요했음.

이 모델 파일이 없었기 때문에, 렌더러는 아이템을 어떻게 그려야 할지 몰라 '텍스처를 찾을 수 없음' 에러 아이콘을 표시한 것임.

  • 교훈: assets/[모드ID]/models/item/과 같은 복잡한 폴더 구조는 단순히 파일을 정리하는 것이 아니라, 수많은 모드들이 서로의 리소스와 충돌하지 않도록 하는 네임스페이스 기반의 필수적인 규칙임.
  • 해결: 이 규칙에 맞춰 정확한 경로에 정확한 이름의 .json 파일을 만들어주자 문제가 해결됨.
  •  

사건 파일 #3: 사라진 기능과 Deprecated API의 경고

  • 사건 현장: 코드는 정상적으로 작동하고 게임도 실행되지만, 컴파일 시 Note: ... uses or overrides a deprecated API. 라는 경고 메시지가 계속해서 나타났음.
  • 원인 분석: API의 생명주기(Lifecycle)와 버전 호환성 문제.

deprecated는 '오류'가 아니라, "이 기능은 구식이므로 다음 메이저 업데이트에서는 사라질 예정입니다"라는 **'미래에 대한 경고'**임. Forge 개발자들은 더 좋은 새로운 방법을 만들면서, 기존 코드가 갑자기 망가지는 것을 막기 위해 구식 기능에 이런 딱지를 붙여둠.

문제의 코드는 new Item.Properties().tab(CreativeModeTab.TAB_MISC) 였음. 1.18.2 버전에서는 이 방법이 작동하지만, 이미 Forge는 "앞으로는 CreativeModeTabEvent를 사용하는 방식으로 바꿀 것이니, 미리 대비하세요" 라고 경고하고 있었던 것임.

  • 교훈: 단순히 코드가 '작동하는 것'에 만족해서는 안 되며, 컴파일러가 보내는 경고 메시지를 이해하고 미래의 버전과 호환되는 '더 올바른(Best Practice)' 방식으로 코드를 작성하는 습관이 중요함.
  • 해결: CreativeModeTabEvent를 사용하는 방식으로 코드를 리팩토링하며 이 문제를 해결함.

사건 파일 #4: 바닐라 시스템과의 충돌, 데이터 저장 방식

  • 사건 현장: 주민에게 레벨과 킬 수를 저장하기 위해 SynchedEntityData를 사용했더니, Duplicate id value for 17! 오류가 발생하며 게임이 실행조차 되지 않았음.
  • 원인 분석: SynchedEntityData는 바닐라 엔티티가 이미 사용하는 '주민등록번호' 같은 것임. 바닐라 주민은 이미 17번 ID를 '직업 정보'를 위해 사용하고 있는데, 우리가 그 번호를 또 쓰려고 해서 충돌이 난 것임.
  • 교훈: 바닐라의 시스템을 직접 건드리는 것은 매우 위험함. 그래서 Forge는 모드 개발자들이 안전하게 데이터를 추가할 수 있도록 '데이터 가방'인 Capability 시스템을 제공함.
  • 해결: 이 사건을 계기로 Capability 시스템을 학습하여, 충돌 없이 안전하게 커스텀 데이터를 저장하는 방법을 익힘.