summaryrefslogtreecommitdiff
path: root/plugin/src/main/kotlin/moe/nea/archenemy/mojang/MinecraftProvider.kt
blob: 605d485ffd590677e3f98475d1b55fb94f382683 (plain)
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
package moe.nea.archenemy.mojang

import kotlinx.serialization.json.Json
import kotlinx.serialization.json.decodeFromStream
import moe.nea.archenemy.util.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.util.concurrent.ConcurrentHashMap

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()
    }
}