Skip to content

Commit

Permalink
Add test, multiple fetchers
Browse files Browse the repository at this point in the history
Enable using multiple fetchers for downloading server.jar
Currently these are the fetchers:
LauncherManifest - Probably what you want, downloads the server.jar from
Mojang servers

DownloadURL - Downloads server.jar from the internet

CopyFile - Copies server.jar from a location on your PC

Also added a test to verify that the main config is always correct,
wouldn't want any bugs from that!

Signed-off-by: Kozova1 <mug66kk@gmail.com>
  • Loading branch information
Kozova1 committed Jul 8, 2021
1 parent 9e7487c commit c51e7b2
Show file tree
Hide file tree
Showing 15 changed files with 255 additions and 170 deletions.
25 changes: 7 additions & 18 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -17,24 +17,13 @@ tasks.withType<KotlinCompile>() {
kotlinOptions.jvmTarget = "1.8"
}

//tasks.withType<Jar> {
// duplicatesStrategy = DuplicatesStrategy.INCLUDE
//
// manifest {
// attributes["Main-Class"] = "MainKt"
// }
//
// from(sourceSets.main.get().output)
//
// dependsOn(configurations.runtimeClasspath)
// from({
// configurations.runtimeClasspath.get().filter {
// it.name.endsWith("jar")
// }.map {
// zipTree(it)
// }
// })
//}
dependencies {
testImplementation(kotlin("test"))
}

tasks.test {
useJUnitPlatform()
}

sourceSets.main {
java.srcDirs("src/main/kotlin")
Expand Down
2 changes: 1 addition & 1 deletion src/main/kotlin/net/vogman/mcdeploy/Command.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
package net.vogman.mcdeploy

sealed interface Command {
suspend fun run(args: Array<String>): Result
suspend fun run(args: Array<String>): Result<Unit, Error>
}
26 changes: 22 additions & 4 deletions src/main/kotlin/net/vogman/mcdeploy/Config.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,35 @@ import io.ktor.client.*
import io.ktor.client.call.*
import io.ktor.client.request.*
import io.ktor.client.statement.*
import java.net.URI
import java.io.File
import java.net.URL
import java.nio.file.Path

data class EnvironmentConfig(
val JavaArgs: List<String>,
val PreLaunchCommands: List<String>,
val PostExitCommands: List<String>
)

data class ServerConfig(val JsonManifestUrl: String, val Version: String, val AgreeToEULA: Boolean)
sealed class ServerJarFetcher : Fetcher {
enum class LauncherManifestFetcher { LauncherManifest }
data class LauncherManifest(val Fetcher: LauncherManifestFetcher, val Version: String, val LauncherManifestURL: URL) : ServerJarFetcher() {
override suspend fun fetch(config: Config): Result<ByteArray, Error> = fetchImpl(config)
}
enum class DownloadURLFetcher { DownloadURL }
data class DownloadURL(val Fetcher: DownloadURLFetcher, val ServerJarURL: URL, val Sha1Sum: String) : ServerJarFetcher() {
override suspend fun fetch(config: Config): Result<ByteArray, Error> = fetchImpl(config)
}

enum class CopyFileFetcher { CopyFile }
data class CopyFile(val Fetcher: CopyFileFetcher, val CopyFrom: File) : ServerJarFetcher() {
override suspend fun fetch(config: Config): Result<ByteArray, Error> = fetchImpl(config)
}
}

data class ServerConfig(
val JarSource: ServerJarFetcher,
val AgreeToEULA: Boolean,
)

data class Datapack(val URL: URL, val FileName: String, val Sha1Sum: String) {
suspend fun fetch(client: HttpClient): ByteArray {
Expand All @@ -24,8 +42,8 @@ data class Datapack(val URL: URL, val FileName: String, val Sha1Sum: String) {
}

data class Config(
val Environment: EnvironmentConfig,
val Server: ServerConfig,
val Environment: EnvironmentConfig,
val Datapacks: List<Datapack>?,
val Properties: Map<String, String>
) {
Expand Down
13 changes: 13 additions & 0 deletions src/main/kotlin/net/vogman/mcdeploy/CopyFile.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package net.vogman.mcdeploy

fun ServerJarFetcher.CopyFile.fetchImpl(config: Config): Result<ByteArray, Error> {
assert(config.Server.JarSource is ServerJarFetcher.CopyFile)
if (!CopyFrom.exists()) {
logErr("File ${CopyFrom.canonicalPath} does not exist!")
return Result.Err(Error.User)
}
println("Copying server.jar from ${CopyFrom.canonicalPath}")
val bytes = CopyFrom.readBytes()
logOk("server.jar read successfully")
return Result.Ok(bytes)
}
57 changes: 6 additions & 51 deletions src/main/kotlin/net/vogman/mcdeploy/DeployServer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,12 @@ import com.sksamuel.hoplite.fp.getOrElse
import com.sksamuel.hoplite.fp.onInvalid
import com.sksamuel.hoplite.fp.onValid
import io.ktor.client.*
import io.ktor.client.call.*
import io.ktor.client.request.*
import io.ktor.client.statement.*
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import java.io.File
import java.lang.IllegalStateException
import kotlin.io.path.Path
import kotlin.io.path.createDirectory

object DeployServer : Command {
override suspend fun run(args: Array<String>): Result {
override suspend fun run(args: Array<String>): Result<Unit, Error> {
if (args.isNotEmpty()) {
logErr("'deploy' subcommand accepts exactly zero arguments")
return Result.Err(Error.User)
Expand Down Expand Up @@ -56,50 +50,11 @@ object DeployServer : Command {

val client = HttpClient()

println("Downloading launcher manifest...")
val versionResponse: HttpResponse = client.get(config.Server.JsonManifestUrl)
val versions: Versions = Json { ignoreUnknownKeys = true }.decodeFromString(versionResponse.receive())
val versionUrl = versions.findURI(
when {
config.Server.Version.equals("latest-release", ignoreCase = true) -> versions.latest.release
config.Server.Version.equals("latest-snapshot", ignoreCase = true) -> versions.latest.snapshot
else -> config.Server.Version
}
)

if (versionUrl == null) {
logErr("No manifest URL found for version ${config.Server.Version} (Are you sure this is the correct version?)")
return Result.Err(Error.Server)
}

println()

println("Downloading manifest for selected version...")
val responseManifest: HttpResponse = client.get(versionUrl)
val manifest: VersionManifest = Json { ignoreUnknownKeys = true }.decodeFromString(responseManifest.receive())
logOk("Received manifest for version ${config.Server.Version}")

if (manifest.downloads.server == null) {
logErr("No server jar for version ${config.Server.Version}")
return Result.Err(Error.Server)
}

// Download server.jar for the selected version
println("Downloading server.jar...")
val serverResponse: HttpResponse = client.get(manifest.downloads.server.url)
val serverJar: ByteArray = serverResponse.receive()
logOk("Downloaded server.jar")
println("Verifying server.jar...")

val serverJarHash = sha1sum(serverJar)
println("Downloaded: $serverJarHash")
println("Manifest: ${manifest.downloads.server.sha1}")
if (manifest.downloads.server.sha1 == serverJarHash) {
logOk("SHA-1 Match! Continuing")
} else {
logErr("SHA-1 Mismatch! Exiting")
return Result.Err(Error.Hash)
val serverJarResult = config.Server.JarSource.fetch(config)
if (serverJarResult is Result.Err) {
return serverJarResult.map { }
}
val serverJar = (serverJarResult as Result.Ok).ok

if (config.Datapacks != null) {
val numDatapacks = config.Datapacks.size
Expand Down Expand Up @@ -147,6 +102,6 @@ object DeployServer : Command {
logOk("written server.properties")

logOk("Done! Please run the server now.")
return Result.Ok
return Result.Ok(Unit)
}
}
15 changes: 15 additions & 0 deletions src/main/kotlin/net/vogman/mcdeploy/DownloadURL.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package net.vogman.mcdeploy

import io.ktor.client.*
import io.ktor.client.call.*
import io.ktor.client.request.*
import io.ktor.client.statement.*

suspend fun ServerJarFetcher.DownloadURL.fetchImpl(config: Config): Result<ByteArray, Error> =
HttpClient().use { client ->
assert(config.Server.JarSource is ServerJarFetcher.DownloadURL)
println("Downloading server.jar from $ServerJarURL")
val response: HttpResponse = client.get(ServerJarURL)
val serverJar: ByteArray = response.receive()
return Result.Ok(serverJar)
}
2 changes: 1 addition & 1 deletion src/main/kotlin/net/vogman/mcdeploy/ErrorOut.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package net.vogman.mcdeploy

class ErrorOut(private val message: String, private val error: Error) : Command {
override suspend fun run(args: Array<String>): Result {
override suspend fun run(args: Array<String>): Result<Unit, Error> {
logErr(message)
return Result.Err(error)
}
Expand Down
5 changes: 5 additions & 0 deletions src/main/kotlin/net/vogman/mcdeploy/Fetcher.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package net.vogman.mcdeploy

interface Fetcher {
suspend fun fetch(config: Config): Result<ByteArray, Error>
}
4 changes: 2 additions & 2 deletions src/main/kotlin/net/vogman/mcdeploy/HelpPrinter.kt
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
package net.vogman.mcdeploy

object HelpPrinter : Command {
override suspend fun run(args: Array<String>): Result {
override suspend fun run(args: Array<String>): Result<Unit, Error> {
if (args.isNotEmpty()) {
logErr("'help' subcommand accepts exactly zero arguments")
return Result.Err(Error.User)
}
println(HELP)
return Result.Ok
return Result.Ok(Unit)
}
}
59 changes: 59 additions & 0 deletions src/main/kotlin/net/vogman/mcdeploy/LauncherManifest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package net.vogman.mcdeploy

import io.ktor.client.*
import io.ktor.client.call.*
import io.ktor.client.request.*
import io.ktor.client.statement.*
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json

suspend fun ServerJarFetcher.LauncherManifest.fetchImpl(config: Config): Result<ByteArray, Error> =
HttpClient().use { client ->
assert(config.Server.JarSource is ServerJarFetcher.LauncherManifest)
println("Downloading launcher manifest...")
val versionResponse: HttpResponse = client.get(LauncherManifestURL)
val versions: Versions = Json { ignoreUnknownKeys = true }.decodeFromString(versionResponse.receive())
val versionUrl = versions.findURI(
when {
Version.equals("latest-release", ignoreCase = true) -> versions.latest.release
Version.equals("latest-snapshot", ignoreCase = true) -> versions.latest.snapshot
else -> Version
}
)

if (versionUrl == null) {
logErr("No manifest URL found for version $Version (Are you sure this is the correct version?)")
return Result.Err(Error.Server)
}

println()

println("Downloading manifest for selected version...")
val responseManifest: HttpResponse = client.get(versionUrl)
val manifest: VersionManifest =
Json { ignoreUnknownKeys = true }.decodeFromString(responseManifest.receive())
logOk("Received manifest for version $Version")

if (manifest.downloads.server == null) {
logErr("No server jar for version $Version")
return Result.Err(Error.Server)
}

// Download server.jar for the selected version
println("Downloading server.jar...")
val serverResponse: HttpResponse = client.get(manifest.downloads.server.url)
val serverJar: ByteArray = serverResponse.receive()
logOk("Downloaded server.jar")
println("Verifying server.jar...")

val serverJarHash = sha1sum(serverJar)
println("Downloaded: $serverJarHash")
println("Manifest: ${manifest.downloads.server.sha1}")
return if (manifest.downloads.server.sha1 == serverJarHash) {
logOk("SHA-1 Match! Continuing")
Result.Ok(serverJar)
} else {
logErr("SHA-1 Mismatch! Exiting")
Result.Err(Error.Hash)
}
}
4 changes: 2 additions & 2 deletions src/main/kotlin/net/vogman/mcdeploy/NewTemplate.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import kotlin.io.path.createDirectories
import kotlin.io.path.createFile

object NewTemplate : Command {
override suspend fun run(args: Array<String>): Result {
override suspend fun run(args: Array<String>): Result<Unit, Error> {
if (args.size != 1) {
logErr("'new' subcommand accepts exactly one argument")
return Result.Err(Error.User)
Expand All @@ -21,6 +21,6 @@ object NewTemplate : Command {
return Result.Err(Error.User)
}
logOk("Server template created in $target!")
return Result.Ok
return Result.Ok(Unit)
}
}
24 changes: 19 additions & 5 deletions src/main/kotlin/net/vogman/mcdeploy/Result.kt
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package net.vogman.mcdeploy

sealed class Result {
object Ok: Result()
data class Err(val error: Error) : Result()
sealed class Result<T, E> {
data class Ok<T, E>(val ok: T) : Result<T, E>()
data class Err<T, E>(val error: E) : Result<T, E>()
}

enum class Error {
Expand All @@ -11,7 +11,7 @@ enum class Error {
Hash,
}

fun Result.toInt(): Int =
fun <T> Result<T, Error>.toInt(): Int =
when (this) {
is Result.Ok -> 0
is Result.Err ->
Expand All @@ -20,4 +20,18 @@ fun Result.toInt(): Int =
Error.Server -> 2
Error.Hash -> 4
}
}
}

fun <T, E, R> Result<T, E>.mapErr(lambda: (E) -> R): Result<T, R> {
return when (this) {
is Result.Err -> Result.Err(lambda(this.error))
is Result.Ok -> Result.Ok(this.ok)
}
}

fun <T, E, R> Result<T, E>.map(lambda: (T) -> R): Result<R, E> {
return when (this) {
is Result.Err -> Result.Err(this.error)
is Result.Ok -> Result.Ok(lambda(this.ok))
}
}
Loading

0 comments on commit c51e7b2

Please sign in to comment.