1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
|
package moe.nea.archenemy.mojang
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.decodeFromStream
import moe.nea.archenemy.DownloadUtils
import moe.nea.archenemy.MCSide
import moe.nea.archenemy.util.getNullsafeIdentifier
import net.minecraftforge.artifactural.api.artifact.Artifact
import net.minecraftforge.artifactural.api.artifact.ArtifactIdentifier
import net.minecraftforge.artifactural.api.artifact.ArtifactType
import net.minecraftforge.artifactural.api.artifact.Streamable
import net.minecraftforge.artifactural.api.repository.Repository
import net.minecraftforge.artifactural.base.artifact.StreamableArtifact
import java.io.File
import java.io.IOException
import java.net.URL
import java.security.MessageDigest
import java.util.concurrent.ConcurrentHashMap
fun MessageDigest.updateField(text: String, value: String) {
this.update(text)
this.update(":")
this.update(value)
this.update(";")
}
fun MessageDigest.update(text: String) {
this.update(text.encodeToByteArray())
}
class MinecraftProvider(val sharedExtension: ArchenemySharedExtension) : Repository {
data class MinecraftCoordinate(
val version: String,
val side: MCSide,
)
private val manifest by lazy {
URL("https://launchermeta.mojang.com/mc/game/version_manifest.json").openStream().use {
Json.decodeFromStream<MojangVersionManifest>(it)
}
}
private val providers = mutableSetOf<MinecraftCoordinate>()
private val versionManifest: MutableMap<String, MojangVersionMetadata> = ConcurrentHashMap()
private val json = Json {
ignoreUnknownKeys = true
}
fun getDependencyCoordinate(minecraftCoordinate: MinecraftCoordinate): String {
providers.add(minecraftCoordinate)
return "archenemy.mojang:minecraft:${minecraftCoordinate.version}:${minecraftCoordinate.side}"
}
fun getMappingsDependencyCoordinate(minecraftCoordinate: MinecraftCoordinate): String {
providers.add(minecraftCoordinate)
return "archenemy.mojang:minecraft:${minecraftCoordinate.version}:${minecraftCoordinate.side}-mappings@txt"
}
private fun getVersionManifest(version: String): MojangVersionMetadata {
return versionManifest.computeIfAbsent(version) {
val versionMetadata = manifest.versions.find { it.id == version }
if (versionMetadata == null)
throw IOException("Invalid minecraft version $version")
val metadata = URL(versionMetadata.url).openStream().use {
json.decodeFromStream<MojangVersionMetadata>(it)
}
metadata
}
}
private fun downloadMinecraft(coordinate: MinecraftCoordinate, mappings: Boolean): File {
val metadata = getVersionManifest(coordinate.version)
val downloadType = when (coordinate.side) {
MCSide.CLIENT -> "client"
MCSide.SERVER -> "server"
} + if (mappings) "_mappings" else ""
val download = metadata.downloads[downloadType]
?: throw IOException("Invalid minecraft side $downloadType for ${coordinate.version}")
val targetFile =
sharedExtension.getLocalCacheDirectory().resolve("minecraft-raw")
.resolve("minecraft-${coordinate.version}-${coordinate.side}.${if (mappings) "txt" else "jar"}")
DownloadUtils.downloadFile(URL(download.url), download.sha1, targetFile)
return targetFile
}
private fun provideStreamableMinecraftJar(coordinate: MinecraftCoordinate): Streamable {
return Streamable {
downloadMinecraft(coordinate, false).inputStream()
}
}
private fun provideStreamableMappings(coordinate: MinecraftCoordinate): Streamable {
return Streamable {
downloadMinecraft(coordinate, true).inputStream()
}
}
override fun getArtifact(identifier: ArtifactIdentifier?): Artifact {
if (identifier == null) return Artifact.none()
if (identifier.name != "minecraft") return Artifact.none()
if (identifier.group != "archenemy.mojang") return Artifact.none()
if (identifier.extension == "pom") return Artifact.none()
val coordinate =
MinecraftCoordinate(identifier.version, MCSide.valueOf(identifier.classifier.removeSuffix("-mappings")))
val isMappings = identifier.classifier.endsWith("-mappings")
if (!providers.contains(coordinate))
error("Unregistered minecraft dependency")
if (identifier.extension == "jar" && !isMappings) {
return StreamableArtifact.ofStreamable(
getNullsafeIdentifier(identifier),
ArtifactType.BINARY,
provideStreamableMinecraftJar(coordinate)
)
}
if (identifier.extension == "txt" && isMappings) {
return StreamableArtifact.ofStreamable(
getNullsafeIdentifier(identifier),
ArtifactType.OTHER,
provideStreamableMappings(coordinate)
)
}
return Artifact.none()
}
}
|