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

poly.mod.json 스키마

poly.mod.json(스키마 1)은 모드 jar의 루트에 둡니다. build의 validatePolyModJson 작업과 Poly 모드 로더 실행 시 검사기가 같은 규칙으로 검사하며, 문제를 모두 모아 한 번에 POLY-METADATA-*로 보고합니다. 모든 필드는 PREVIEW입니다.

기본 필드​

필드필수내용
schemaVersion예1
id예모드 ID. 소문자·숫자·밑줄. 예약 ID poly, minecraft, java는 쓸 수 없음(POLY-DEPENDENCY-008)
version예모드 버전(SemVer)
name예표시 이름
environment예both, client, server
minecraft예지원하는 Minecraft 버전 목록. 정확한 버전("26.2"), 와일드카드("26.*"), 범위(">=26.2 <27")
entrypoints—common, client, server 키마다 클래스 이름 목록
depends—모드 ID → 버전 범위. 예: "poly": ">=0.1.0-alpha <0.2.0"
conflicts—함께 로드할 수 없는 모드 ID 목록(POLY-DEPENDENCY-005)

기능 권한 — capabilities​

위험하거나 게임 깊숙이 개입하는 기능을 쓰려면 먼저 capabilities에 권한을 선언해야 합니다. 필요한 권한이 없으면 POLY-METADATA-012 오류가, 모르는 권한이면 POLY-METADATA-013 경고가 납니다.

현재 Poly 모드 로더가 아는 권한 ID: content.registry.register, gameplay.rules.extend, worldgen.provider.replace, render.pipeline.extend, system.provider.replace, internal.member.access, internal.callsite.intercept, internal.bytecode.transform, runtime.bootstrap.modify, network.protocol.extend, world.data.migrate.

지금 실제로 검사하는 것은 아래 두 기능입니다.

기능필요한 권한
replacesSystemssystem.provider.replace
transformsinternal.bytecode.transform
transforms의 redirect_callinternal.bytecode.transform, internal.callsite.intercept
accessWideningsinternal.member.access

시스템 교체 — replacesSystems​

모드가 게임 시스템의 구현을 바꿉니다. 현재 교체할 수 있는 시스템은 poly:game_brand(게임 브랜드 문자열) 하나입니다.

{
"capabilities": ["system.provider.replace"],
"replacesSystems": [
{ "system": "poly:game_brand", "provider": "com.example.poly.brand.ExampleBrandProvider" }
]
}
  • 형식: {system, provider, override?}. provider는 SystemProvider를 구현한 클래스입니다.
  • 충돌은 모드 코드가 실행되기 전에 해결합니다. 교체가 둘 이상이면 override: true이면서 다른 교체 모드 모두에 (전이적으로) 의존하는 모드 하나만 채택되고, 그렇지 않으면 POLY-SYSTEM-001로 시작을 멈춥니다.
  • 활성 시스템과 제공자는 로그와 .poly/startup-report.json에 기록됩니다.

예제: Poly 모드 로더 저장소의 examples/custom-system-mod.

코드 변환 — transforms​

UNSAFE 기능

모드 선언 변환은 게임 바이트코드를 직접 바꿉니다. 게임 버전마다 다시 확인해야 하며, 지원하는 동작은 엔진이 검토를 마친 것으로 한정됩니다.

{
"capabilities": ["internal.bytecode.transform"],
"transforms": [
{
"id": "transform_demo_mod:storage_format_name",
"minecraft": "26.2",
"sides": ["client", "dedicated_server"],
"target": {
"owner": "net/minecraft/world/level/storage/LevelStorageSource",
"method": "getName",
"descriptor": "()Ljava/lang/String;"
},
"action": {
"type": "replace_returned_string_constant",
"expected": "Anvil",
"replacement": "Anvil (Poly Mod Loader transform demo)"
},
"onFailure": "disable_rule"
}
]
}
필드내용
id<모드 ID>:<경로>. 다른 모드의 이름공간은 쓸 수 없음
minecraft규칙이 적용되는 정확한 게임 버전
sidesclient, dedicated_server
targetowner(내부 이름), method, descriptor(JVM descriptor)
action.type아래 동작 표 참고
after같은 메서드에서 먼저 실행돼야 하는 규칙 ID 목록(선택). Poly 모드 로더 내장 규칙(poly:…)도 적을 수 있음
onFailureabort_startup 또는 disable_rule(기본값)
action.type인자뜻
replace_returned_string_constantexpected, replacement본문이 정확히 LDC <expected>; ARETURN일 때 상수를 바꿈. 다른 규칙과 같은 메서드에 쓸 수 없음
invoke_hook_at_headhookOwner, hookMethod, result(continue, cancel_if_true, return_if_non_null)메서드 시작에서 정적 훅 호출. 훅은 this(정적이 아니면)와 인자를 받음
invoke_hook_before_returnhookOwner, hookMethod모든 반환 직전에 훅 호출. 훅은 this, 인자, 반환값을 받고 반환할 값을 돌려줌
redirect_callinvokedOwner, invokedName, invokedDescriptor, hookOwner, hookMethod, ordinal(선택, "all" 기본 또는 0부터 센 번호)메서드 안의 그 호출을 정적 훅 호출로 바꿈. 훅은 받는 쪽(정적 호출이면 없음)과 인자를 받고 같은 형식을 돌려줌. 권한 internal.callsite.intercept도 필요

클래스 이름은 com/example/Hooks와 com.example.Hooks 모두 받습니다. 훅의 descriptor는 대상과 정확히 맞아야 합니다. 게임 타입을 쓰면 그 모드는 게임 jar를 대상으로 컴파일해야 합니다.

같은 메서드의 여러 규칙​

같은 메서드를 고치는 규칙(다른 모드나 Poly 모드 로더 자신의 것 포함)은 after가 하나의 순서를 정할 때만 함께 적용됩니다. 그렇지 않으면 모두 거부됩니다(POLY-TRANSFORM-004).

  • 시작 훅과 반환 직전 훅은 선언 순서대로 실행됩니다. 반환 직전 훅은 앞 훅의 결과를 받습니다.
  • 시작 훅이 취소하거나 값을 돌려주면 반환 직전 훅은 실행되지 않습니다.
  • 같은 호출을 바꾸는 redirect_call 둘과 상수 교체는 함께 쓸 수 없습니다.

접근 확대 — accessWidenings​

게임의 클래스·메서드·필드의 접근 범위를 넓혀 모드 코드가 직접 쓸 수 있게 합니다. 의미는 Fabric access widener와 같습니다. 권한 internal.member.access가 필요합니다.

{
"capabilities": ["internal.member.access"],
"accessWidenings": [
{
"minecraft": "26.2",
"sides": ["client", "dedicated_server"],
"owner": "net/minecraft/server/MinecraftServer",
"kind": "field",
"name": "tickCount",
"descriptor": "I",
"access": ["accessible"]
}
]
}
필드뜻
kindclass, method, field. 클래스는 name·descriptor 없음
accessaccessible(public으로, private 메서드는 final도 됨), extendable(클래스·메서드: final 해제, 메서드는 최소 protected, owner 안의 호출은 가상 호출로), mutable(필드: final 해제)
onFailureabort_startup(기본값) 또는 skip. 대상이 없으면 POLY-TRANSFORM-010
  • 여러 모드가 같은 대상을 넓혀도 합쳐질 뿐 충돌하지 않습니다.

  • 실행 시에만 적용됩니다. static final 상수 필드는 읽는 쪽 코드에 값이 복사돼 있어 mutable로 바꿔도 그 코드에는 반영되지 않습니다.

  • 모드 규칙은 Poly 모드 로더 내장 규칙과 같은 계획 단계를 거치며, 검사 항목은 대상 존재, descriptor, 바이트코드 사전조건, 게임 버전, 다른 모든 규칙과의 충돌입니다.

  • 모드가 Poly 모드 로더 내장 규칙과 같은 메서드를 고치면 두 규칙이 모두 거부됩니다(POLY-TRANSFORM-004).

예제: Poly 모드 로더 저장소의 examples/transform-demo-mod.

예약 필드​

필드상태
provides예약됨. 비어 있지 않으면 POLY-METADATA-010으로 거부

모르는 필드는 POLY-METADATA-011 경고입니다.

Buy me a coffee