본문으로 건너뛰기
버전: Minecraft 26.2

생명주기와 스레드

모드 코드가 언제, 어느 스레드에서 호출되는지 정리한 문서입니다. 내용은 각 타입의 Javadoc과 Poly 모드 로더 런타임의 실제 동작을 따릅니다.

한눈에 보기​

JVM 시작 ─ 메인 스레드
│
├─ 모드 탐색 · poly.mod.json 검사 · 의존성 해결 POLY-METADATA-*, POLY-DEPENDENCY-*
├─ 시스템 교체 충돌 해결 (모드 코드 실행 전) POLY-SYSTEM-001/002/004/005
├─ 변환 계획 확정 POLY-TRANSFORM-*
├─ SystemProvider.create (시스템마다 1회) POLY-SYSTEM-003
├─ common 엔트리포인트 (모든 모드, 의존성 순서) POLY-ENTRYPOINT-*
├─ client 또는 server 엔트리포인트 (의존성 순서)
├─ 게임 계층 시작 POLY-BOOTSTRAP-005
│
└─ 게임 main
├─ Minecraft 부트스트랩 뒤: 다른 로더의 모드 초기화(Fabric), 그다음
│ LifecycleEvents.MODS_INITIALIZED
├─ 레지스트리 동결 직전: 선언을 게임 객체로 만들고 등록을 닫음
│ ── 이후 선언은 POLY-*-002 ──
├─ 서버 스레드: 이벤트, 틱, 명령어, AI, 예약 작업, 서버 쪽 페이로드
├─ 클라이언트(렌더) 스레드: HUD, 화면, 그리기, 클라이언트 쪽 페이로드
├─ 월드 생성 스레드: 코드 배치 기능, density function, 구조물, 지형 생성기
└─ 네트워크 스레드: 페이로드 코덱
│
JVM 종료 ─ "Poly 모드 로더 Shutdown" 스레드
└─ onShutdown 작업, 등록 역순 POLY-SHUTDOWN-001

시작 순서​

순서실행되는 것스레드실패
1모드 탐색, 메타데이터 검사, 의존성 해결, 시스템 교체 충돌 해결메인POLY-METADATA-*, POLY-DEPENDENCY-*, POLY-SYSTEM-*. 모드 코드는 아직 실행되지 않습니다
2교체할 시스템의 SystemProvider.create메인, 시스템마다 1회POLY-SYSTEM-003으로 시작 중단
3모든 모드의 CommonModInitializer메인POLY-ENTRYPOINT-003으로 시작 중단
4클라이언트면 모든 모드의 ClientModInitializer, 전용 서버면 DedicatedServerModInitializer메인위와 같음
5게임 계층 시작메인POLY-BOOTSTRAP-005
6게임 main메인—
7게임 시작 안, Minecraft 부트스트랩 뒤: 다른 로더의 모드가 자기 로더에서와 같은 자리에서 초기화되고(Fabric: main, 그다음 client 또는 server), 이어서 LifecycleEvents.MODS_INITIALIZED메인예외를 던진 리스너는 꺼집니다(POLY-EVENT-001)
  • 3, 4단계는 각각 모든 모드를 의존성 순서로 순회합니다. 의존하는 모드가 먼저 실행되고, 순서가 같으면 모드 ID 순으로 실행합니다. 그래서 모든 common 엔트리포인트가 끝난 뒤에야 첫 client(또는 server) 엔트리포인트가 실행됩니다.
  • 싱글 플레이의 내장 서버는 클라이언트 프로세스 안에서 동작하며, 이때 server 엔트리포인트는 호출되지 않습니다. server 엔트리포인트는 전용 서버에서만 실행됩니다.
  • 엔트리포인트가 실행되는 시점에는 Minecraft 레지스트리와 게임 객체가 아직 없으므로, 엔트리포인트에서는 선언만 하세요.
  • 다른 로더의 모드(Fabric)는 7단계에서, 자기 로더가 부르는 자리에서 초기화됩니다. 다른 로더의 모드가 초기화된 뒤에 할 일이 있는 Poly 모드는 LifecycleEvents.MODS_INITIALIZED를 듣습니다. 모든 모드가 끝난 뒤 한 번 오고, 다른 로더의 모드가 없어도 같은 자리에서 옵니다. 자세한 내용은 호환성을 참고하세요.
context.events().register(LifecycleEvents.MODS_INITIALIZED, () -> {
// 모든 로더의 모든 모드가 초기화를 마쳤습니다.
});

엔트리포인트 종류와 poly.mod.json 키는 엔트리포인트에서 설명합니다.

게임 계층과 등록 종료​

게임 계층(poly-game-v26_2)은 모든 엔트리포인트가 끝난 뒤 게임 main이 시작되기 전에 시작합니다. 시작하지 못하면 POLY-BOOTSTRAP-005와 함께 멈춥니다.

등록은 게임이 레지스트리를 동결(freeze)하기 직전에 닫힙니다.

  1. 엔트리포인트의 선언은 Poly 모드 로더가 그 자리에서 검증합니다. 이름이 잘못되었거나 중복이면 곧바로 예외가 발생합니다.
  2. 게임이 레지스트리를 동결하기 직전에, 게임 계층이 모든 선언을 실제 게임 객체로 만듭니다.
  3. 그 직후 등록이 닫힙니다. 이후의 선언은 모두 IllegalStateException을 일으키며, 메시지는 <코드> … must be declared in a mod entrypoint; registration is closed입니다.

선언은 언제나 엔트리포인트 안에서 끝내세요. 이벤트 리스너, 예약 작업, 다른 스레드에서는 선언하지 마세요.

콜백별 스레드​

콜백스레드지켜야 할 것
CommonModInitializer, ClientModInitializer, DedicatedServerModInitializer메인선언만 합니다
SystemProvider.create메인, 엔트리포인트 전에 1회인수 없는 public 생성자가 필요합니다
GameBrandSystem.brand아무 스레드, 여러 번상태를 두지 말고 빠르게 처리합니다
EventRegistry.register아무 스레드, 아무 때나스레드 안전합니다
서버 이벤트(ServerEvents, PlayerEvents, BlockEvents, EntityEvents, WorldEvents, InteractionEvents)서버ServerEvents.TICK은 매 틱 호출되므로 빠르게 처리합니다
ClientEvents클라이언트클라이언트에서만 발생합니다. ITEM_TOOLTIP은 툴팁이 보이는 동안 매 프레임 불리므로 빠르게 처리합니다
BlockBehavior, ItemBehavior해당 월드의 스레드, 서버와 클라이언트 양쪽world.isClientSide()를 확인하고 서버에서만 상태를 바꿉니다
BlockEntityTicker, AiGoal, ProjectileBehavior서버받은 뷰는 호출되는 동안에만 씁니다
CommandAction, PermissionProvider서버권한 검사는 명령어 제안 때도 자주 호출되므로 빠르게 처리합니다
WorldView.schedule의 작업서버, 지정한 틱 뒤—
ServerPayloadHandler / ClientPayloadHandler서버 / 클라이언트—
PayloadCodec네트워크 스레드게임 상태에 접근하지 않습니다
ConfigurationExchange서버 쪽은 서버, 클라이언트 답은 클라이언트서버 쪽은 common 엔트리포인트에서, 클라이언트 답은 client 엔트리포인트에서 정합니다
HudLayer, BlockEntityPainter, WorldPainter클라이언트(렌더), 매 프레임빠르게 처리합니다. 블록 엔티티 그리기는 클라이언트 사본을 읽습니다
ModScreen의 모든 메서드, 화면 버튼클라이언트(렌더)—
CodeFeature, StructureBuilder월드 생성 스레드, 여러 청크에서 동시에상태는 지역 변수에만 둡니다. 배치 기능의 무작위 값은 FeatureContext.random()으로만 얻습니다
NoiseFunction월드 생성 스레드, 동시에 매우 자주상태를 두지 말고 빠르게 처리하며, 같은 위치에는 언제나 같은 값을 돌려줍니다
TerrainGenerator월드 생성 스레드, 여러 열에서 동시에스레드 안전해야 하며, 같은 위치와 시드에는 같은 열을 만듭니다
onDataReload, onDataFiles서버서버를 시작할 때와 /reload 때 호출됩니다
onAssetReload, onAssetFiles클라이언트클라이언트를 시작할 때와 F3+T 때 호출됩니다. 전용 서버에서는 호출되지 않습니다
onShutdown의 작업Poly 모드 로더 종료 스레드(Poly 모드 로더 Shutdown)아래 종료를 참고하세요
뷰는 넘겨받은 스레드에서, 호출 동안만

WorldView, EntityView, PlayerView 같은 뷰는 그 뷰를 넘겨준 스레드(서버 또는 클라이언트)에서, 콜백이 실행되는 동안에만 유효합니다. 필드에 저장하거나 다른 스레드로 넘기지 마세요. 월드 생성 콜백에서는 뷰 대신 전달받은 context만 사용하세요.

콜백이 예외를 던지면​

콜백결과
엔트리포인트POLY-ENTRYPOINT-003. 시작이 중단됩니다
SystemProvider, 시스템 검증POLY-SYSTEM-003. 시작이 중단됩니다
이벤트 리스너POLY-EVENT-001. 해당 리스너만 끄고, 다른 리스너와 게임은 계속 동작합니다. 예외를 던지거나 null을 돌려준(POLY-EVENT-003) 결정 리스너는 PASS로 칩니다
HudLayer, BlockEntityPainter, WorldPainterPOLY-CLIENT-003. 해당 레이어나 그리기만 끕니다
CodeFeature, NoiseFunction, StructureBuilderPOLY-WORLDGEN-005. 한 번만 기록하고 실패한 자리는 건너뜁니다
WorldView.schedule의 작업POLY-SCHEDULE-001. 기록하고 계속 진행합니다
PayloadCodec.decodePOLY-NETWORK-004. 해당 페이로드를 버립니다
ConfigurationExchange의 모드 코드POLY-NETWORK-005
BlockBehavior, ItemBehavior게임 자체 블록에서 난 예외처럼 게임으로 전달됩니다
종료 작업POLY-SHUTDOWN-001. 기록하고 남은 작업을 계속 실행합니다

너무 늦게, 또는 잘못된 쪽에서 등록하면​

등록이 닫힌 뒤(예: 이벤트 리스너 안)에 선언하면 영역별 코드와 함께 IllegalStateException이 발생합니다.

등록닫힌 뒤 오류
블록, 아이템, 엔티티, 투사체, 효과, 사운드, 파티클, 게임 규칙, 아이템 컴포넌트, 엔티티 데이터, 발전 과제 트리거POLY-CONTENT-002
명령어POLY-COMMAND-002
페이로드 채널, 접속 준비 교환POLY-NETWORK-002
배치 기능, 코드 배치 기능, density function, 구조물, 바이옴, 지형 생성기POLY-WORLDGEN-002
권한 노드, 권한 공급자POLY-PERMISSION-002
리로드 리스너POLY-RESOURCE-002
게임 테스트POLY-GAMETEST-002
키 바인딩, HUD 레이어, 블록 엔티티 그리기, 동적 텍스처, 월드 그리기, 셰이더 파이프라인POLY-CLIENT-002
onShutdown (종료가 시작된 뒤)POLY-SHUTDOWN-002

서버와 클라이언트 중 잘못된 쪽에서 호출한 경우:

상황결과
전용 서버에서 mod.client() 호출IllegalStateException, POLY-CLIENT-001. 클라이언트 등록은 client 엔트리포인트로 옮기세요
연결되기 전에 페이로드 전송IllegalStateException, POLY-NETWORK-002
전용 서버에서 sendToServer 호출, 또는 서버 쪽 플레이어가 아닌 대상에 sendToPlayer 호출IllegalStateException
클라이언트에서 월드를 바꾸는 메서드 호출IllegalStateException
클라이언트 스레드가 아닌 곳에서 동적 텍스처 업로드POLY-CLIENT-005
콘텐츠를 client 엔트리포인트에서만 선언별도의 오류 코드는 없지만 서버와 콘텐츠 목록이 달라져 동기화가 어긋납니다. 콘텐츠는 common 엔트리포인트에서 선언하세요

종료​

mod.onShutdown("close database", () -> database.close());
  • 종료 작업은 JVM이 종료될 때 Poly 모드 로더 종료 스레드(Poly 모드 로더 Shutdown)에서 한 번씩 실행합니다.
  • 모든 모드의 작업을 등록한 역순으로 실행하므로, 의존하는 모드가 의존 대상보다 먼저 정리됩니다.
  • 작업은 짧게 끝내고, 새 스레드를 만들지 말고, 게임 상태를 건드리지 마세요. 게임 상태는 이미 사라졌을 수 있습니다.
  • 한 작업이 예외를 던지면 모드 ID와 함께 POLY-SHUTDOWN-001로 기록하고 남은 작업은 계속 실행합니다.
  • 종료가 시작된 뒤에 onShutdown을 호출하면 POLY-SHUTDOWN-002 오류가 납니다.

서버가 멈추는 순간에 할 일은 종료 작업이 아니라 서버 스레드의 서버 이벤트에서 처리하세요. 월드를 저장하기 전이라면 ServerEvents.STOPPING, 차원마다 저장을 마친 뒤라면 WorldEvents.UNLOADED, 월드를 모두 닫은 뒤라면 ServerEvents.STOPPED입니다.

함께 보기​

Buy me a coffee