plugins { java } group = "kr.tkrmagid" version = "0.1.0" // ───────────────────────────────────────────────────────────────────────────── // 이 프로젝트는 Fabric Loom 을 쓰지 않는다. 의도된 선택이다. // // Loom 은 마인크래프트 jar 를 내려받아 매핑(Yarn/Mojang)을 적용해 컴파일 대상으로 // 제공하는 도구다. 즉 "마인크래프트 클래스를 참조하는 모드"를 위한 것이다. // 이 모드는 마인크래프트 클래스를 단 하나도 참조하지 않으므로(docs/DESIGN.md 참고) // Loom, 매핑, 버전별 소스셋, Stonecutter 가 전부 불필요하다. // // 결과: 평범한 자바 빌드 하나로 모든 마인크래프트 버전용 단일 jar 가 나온다. // ───────────────────────────────────────────────────────────────────────────── repositories { mavenCentral() maven("https://maven.fabricmc.net/") { name = "FabricMC" } } dependencies { // Fabric Loader 는 런타임에 이미 존재한다 — 번들하면 안 된다. // 우리가 쓰는 것은 net.fabricmc.api.ClientModInitializer 하나뿐이고, // 이 인터페이스는 로더 소유라 리매핑 대상이 아니며 모든 버전에서 동일하다. compileOnly("net.fabricmc:fabric-loader:0.18.4") // LAN 최대 인원 믹스인(mixin/LanMaxPlayersMixin) 의 어노테이션만 쓴다. Mixin 은 로더가 // 런타임에 들고 있으므로 번들하지 않는다. 대상은 문자열이라 매핑도 Loom 도 필요 없다. compileOnly("net.fabricmc:sponge-mixin:0.17.4+mixin.0.8.7") // 순수 자바 QUIC. 네이티브 라이브러리 0개 (docs/DESIGN.md 의 전송 계층 비교 참고). implementation("tech.kwik:kwik:0.11") testImplementation(platform("org.junit:junit-bom:5.14.1")) testImplementation("org.junit.jupiter:junit-jupiter") testRuntimeOnly("org.junit.platform:junit-platform-launcher") } java { // 17 로 고정한다. 마인크래프트 1.18 이상은 JVM 17+ 로 실행되므로 그대로 동작하고, // 1.17 이하도 사용자가 JVM 17+ 로 띄우면 동작한다. Kwik 자체는 major 55(Java 11) // 로 컴파일돼 있어 하한을 더 낮출 수도 있지만, record/switch 식 등을 쓰기 위해 17 을 택했다. sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 } tasks.withType().configureEach { options.release.set(17) options.encoding = "UTF-8" options.compilerArgs.add("-Xlint:all,-serial,-this-escape") } tasks.test { useJUnitPlatform() testLogging { events("passed", "skipped", "failed") showStandardStreams = false } } tasks.processResources { filesMatching("fabric.mod.json") { expand("version" to project.version) } } // 팻 jar. shadow 플러그인을 쓰지 않는 이유는 의존성이 Kwik 계열 4개뿐이라 // 재배치(relocation)가 필요 없고, 플러그인 하나를 덜 받는 쪽이 빌드가 단순해서다. tasks.jar { from(configurations.runtimeClasspath.get().map { if (it.isDirectory) it else zipTree(it) }) duplicatesStrategy = DuplicatesStrategy.EXCLUDE exclude( "META-INF/*.SF", "META-INF/*.DSA", "META-INF/*.RSA", "META-INF/*.MF", "META-INF/LICENSE*", "META-INF/NOTICE*", // 번들 라이브러리의 module-info 는 우리 jar 안에서 의미가 없고 충돌만 만든다. "module-info.class", "META-INF/versions/*/module-info.class", ) manifest { attributes( "Implementation-Title" to "mc_p2p_mod", "Implementation-Version" to project.version, ) } } // 시그널링/릴레이 서버를 따로 돌릴 수 있게 실행 가능한 jar 도 같이 만든다. // 모드 jar 와 클래스는 동일하고 Main-Class 만 다르다. tasks.register("rendezvousJar") { archiveClassifier.set("rendezvous") from(sourceSets.main.get().output) from(configurations.runtimeClasspath.get().map { if (it.isDirectory) it else zipTree(it) }) duplicatesStrategy = DuplicatesStrategy.EXCLUDE exclude( "META-INF/*.SF", "META-INF/*.DSA", "META-INF/*.RSA", "META-INF/*.MF", "module-info.class", "META-INF/versions/*/module-info.class", ) manifest { attributes("Main-Class" to "kr.tkrmagid.mcp2p.rendezvous.RendezvousServer") } } tasks.build { dependsOn("rendezvousJar") }