mirror of
https://github.com/packwiz/packwiz-installer.git
synced 2025-10-16 16:04:32 +02:00
Compare commits
15 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
758385c225 | ||
|
304fb802ed | ||
|
cc063773d8 | ||
|
1deed7dd0d | ||
|
ad951b9b44 | ||
|
4e415c1e1a | ||
|
84bbbe0770 | ||
|
fa9fe18215 | ||
|
01dcc09a78 | ||
|
a8f8444d45 | ||
|
d98baaf832 | ||
|
783e35cf73 | ||
|
ca172bdefc | ||
|
b8cb9cc1aa | ||
|
6f0beac1a1 |
27
.github/workflows/pr.yml
vendored
Normal file
27
.github/workflows/pr.yml
vendored
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
name: Java Gradle Build
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
- name: Set up JDK 8
|
||||||
|
uses: actions/setup-java@v2
|
||||||
|
with:
|
||||||
|
java-version: '8'
|
||||||
|
distribution: 'temurin'
|
||||||
|
cache: gradle
|
||||||
|
- name: Build with Gradle
|
||||||
|
run: ./gradlew build
|
||||||
|
- name: Cleanup Gradle Cache
|
||||||
|
# Remove some files from the Gradle cache, so they aren't cached by GitHub Actions.
|
||||||
|
# Restoring these files from a GitHub Actions cache might cause problems for future builds.
|
||||||
|
run: |
|
||||||
|
rm -f ~/.gradle/caches/modules-2/modules-2.lock
|
||||||
|
rm -f ~/.gradle/caches/modules-2/gc.properties
|
5
.github/workflows/snapshot.yml
vendored
5
.github/workflows/snapshot.yml
vendored
@@ -1,6 +1,9 @@
|
|||||||
name: Java Gradle Snapshot
|
name: Java Gradle Snapshot
|
||||||
|
|
||||||
on: ["push", "pull_request"]
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- 'main'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
|
@@ -22,7 +22,7 @@ repositories {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val r8 by configurations.creating
|
val r8 by configurations.creating
|
||||||
val shrinkJarOutput by configurations.creating {
|
val distJarOutput by configurations.creating {
|
||||||
isCanBeResolved = false
|
isCanBeResolved = false
|
||||||
isCanBeConsumed = true
|
isCanBeConsumed = true
|
||||||
|
|
||||||
@@ -104,15 +104,27 @@ val shrinkJar by tasks.registering(JavaExec::class) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MANIFEST.MF must be one of the first 2 entries in the zip for JarInputStream to see it
|
||||||
|
// Gradle's JAR creation handles this whereas R8 doesn't, so the dist JAR is repacked
|
||||||
|
val distJar by tasks.registering(Jar::class) {
|
||||||
|
from(shrinkJar.map { zipTree(it.outputs.files.singleFile) })
|
||||||
|
archiveClassifier.set("all-repacked")
|
||||||
|
manifest {
|
||||||
|
from(shrinkJar.map { zipTree(it.outputs.files.singleFile).matching {
|
||||||
|
include("META-INF/MANIFEST.MF")
|
||||||
|
}.singleFile })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
artifacts {
|
artifacts {
|
||||||
add("shrinkJarOutput", shrinkJar) {
|
add("distJarOutput", distJar) {
|
||||||
classifier = "dist"
|
classifier = "dist"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Used for vscode launch.json
|
// Used for vscode launch.json
|
||||||
val copyJar by tasks.registering(Copy::class) {
|
val copyJar by tasks.registering(Copy::class) {
|
||||||
from(shrinkJar)
|
from(distJar)
|
||||||
rename("packwiz-installer-(.*)\\.jar", "packwiz-installer.jar")
|
rename("packwiz-installer-(.*)\\.jar", "packwiz-installer.jar")
|
||||||
into(layout.buildDirectory.dir("dist"))
|
into(layout.buildDirectory.dir("dist"))
|
||||||
outputs.file(layout.buildDirectory.dir("dist").map { it.file("packwiz-installer.jar") })
|
outputs.file(layout.buildDirectory.dir("dist").map { it.file("packwiz-installer.jar") })
|
||||||
@@ -130,12 +142,11 @@ githubRelease {
|
|||||||
draft(true)
|
draft(true)
|
||||||
token(findProperty("github.token") as String?)
|
token(findProperty("github.token") as String?)
|
||||||
releaseAssets(layout.buildDirectory.dir("dist").map { it.file("packwiz-installer.jar") }.get())
|
releaseAssets(layout.buildDirectory.dir("dist").map { it.file("packwiz-installer.jar") }.get())
|
||||||
dryRun(project.findProperty("release") != "true")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.githubRelease {
|
tasks.githubRelease {
|
||||||
dependsOn(copyJar)
|
dependsOn(copyJar)
|
||||||
enabled = project.hasProperty("github.token")
|
enabled = project.hasProperty("github.token") && project.findProperty("release") == "true"
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.publish {
|
tasks.publish {
|
||||||
@@ -156,7 +167,7 @@ tasks.compileTestKotlin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val javaComponent = components["java"] as AdhocComponentWithVariants
|
val javaComponent = components["java"] as AdhocComponentWithVariants
|
||||||
javaComponent.addVariantsFromConfiguration(shrinkJarOutput) {
|
javaComponent.addVariantsFromConfiguration(distJarOutput) {
|
||||||
mapToMavenScope("runtime")
|
mapToMavenScope("runtime")
|
||||||
mapToOptional()
|
mapToOptional()
|
||||||
}
|
}
|
||||||
|
@@ -37,7 +37,7 @@ internal class DownloadTask private constructor(val metadata: IndexFile.File, va
|
|||||||
|
|
||||||
fun isNewOptional() = isOptional && newOptional
|
fun isNewOptional() = isOptional && newOptional
|
||||||
|
|
||||||
fun correctSide() = metadata.linkedFile?.side?.hasSide(downloadSide) ?: true
|
fun correctSide() = metadata.linkedFile?.side?.let { downloadSide.hasSide(it) } ?: true
|
||||||
|
|
||||||
override val name get() = metadata.name
|
override val name get() = metadata.name
|
||||||
|
|
||||||
@@ -109,9 +109,9 @@ internal class DownloadTask private constructor(val metadata: IndexFile.File, va
|
|||||||
cachedFile.optionValue = linkedFile.option.defaultValue
|
cachedFile.optionValue = linkedFile.option.defaultValue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cachedFile.isOptional = isOptional
|
|
||||||
cachedFile.onlyOtherSide = !correctSide()
|
|
||||||
}
|
}
|
||||||
|
cachedFile.isOptional = isOptional
|
||||||
|
cachedFile.onlyOtherSide = !correctSide()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -50,7 +50,20 @@ class LauncherUtils internal constructor(private val opts: UpdateManager.Options
|
|||||||
"net.minecraftforge" to "forge",
|
"net.minecraftforge" to "forge",
|
||||||
"net.fabricmc.fabric-loader" to "fabric",
|
"net.fabricmc.fabric-loader" to "fabric",
|
||||||
"org.quiltmc.quilt-loader" to "quilt",
|
"org.quiltmc.quilt-loader" to "quilt",
|
||||||
"com.mumfrey.liteloader" to "liteloader")
|
"com.mumfrey.liteloader" to "liteloader"
|
||||||
|
)
|
||||||
|
// MultiMC requires components to be sorted; this is defined in the MultiMC meta repo, but they seem to
|
||||||
|
// be the same for every version so they are just used directly here
|
||||||
|
val componentOrders = mapOf(
|
||||||
|
"net.minecraft" to -2,
|
||||||
|
"org.lwjgl" to -1,
|
||||||
|
"org.lwjgl3" to -1,
|
||||||
|
"net.minecraftforge" to 5,
|
||||||
|
"net.fabricmc.fabric-loader" to 10,
|
||||||
|
"org.quiltmc.quilt-loader" to 10,
|
||||||
|
"com.mumfrey.liteloader" to 10,
|
||||||
|
"net.fabricmc.intermediary" to 11
|
||||||
|
)
|
||||||
val modLoadersClasses = modLoaders.entries.associate{(k,v)-> v to k}
|
val modLoadersClasses = modLoaders.entries.associate{(k,v)-> v to k}
|
||||||
val loaderVersionsFound = HashMap<String, String?>()
|
val loaderVersionsFound = HashMap<String, String?>()
|
||||||
val outdatedLoaders = mutableSetOf<String>()
|
val outdatedLoaders = mutableSetOf<String>()
|
||||||
@@ -92,6 +105,15 @@ class LauncherUtils internal constructor(private val opts: UpdateManager.Options
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (manifestModified) {
|
if (manifestModified) {
|
||||||
|
// Sort manifest by component order
|
||||||
|
val sortedComponents = components.sortedWith(nullsLast(compareBy {
|
||||||
|
if (it.isJsonObject) {
|
||||||
|
componentOrders[it.asJsonObject["uid"]?.asString]
|
||||||
|
} else { null }
|
||||||
|
}))
|
||||||
|
components.removeAll { true }
|
||||||
|
sortedComponents.forEach { components.add(it) }
|
||||||
|
|
||||||
// The manifest has been modified, so before saving it we'll ask the user
|
// The manifest has been modified, so before saving it we'll ask the user
|
||||||
// if they wanna update it, continue without updating it, or exit
|
// if they wanna update it, continue without updating it, or exit
|
||||||
val oldVers = loaderVersionsFound.map { Pair(it.key, it.value) }
|
val oldVers = loaderVersionsFound.map { Pair(it.key, it.value) }
|
||||||
|
@@ -101,12 +101,15 @@ class Main(args: Array<String>) {
|
|||||||
cmd.getOptionValue("multimc-folder")?.let{ PackwizFilePath(it.toPath()) } ?: PackwizFilePath("..".toPath())
|
cmd.getOptionValue("multimc-folder")?.let{ PackwizFilePath(it.toPath()) } ?: PackwizFilePath("..".toPath())
|
||||||
}
|
}
|
||||||
val manifestFile = ui.wrap("Invalid manifest file path") {
|
val manifestFile = ui.wrap("Invalid manifest file path") {
|
||||||
packFolder / (cmd.getOptionValue("meta-file") ?: "manifest.json")
|
packFolder / (cmd.getOptionValue("meta-file") ?: "packwiz.json")
|
||||||
|
}
|
||||||
|
val timeout = ui.wrap("Invalid timeout value") {
|
||||||
|
cmd.getOptionValue("timeout")?.toLong() ?: 10
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start update process!
|
// Start update process!
|
||||||
try {
|
try {
|
||||||
UpdateManager(UpdateManager.Options(packFile, manifestFile, packFolder, multimcFolder, side), ui)
|
UpdateManager(UpdateManager.Options(packFile, manifestFile, packFolder, multimcFolder, side, timeout), ui)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
ui.showErrorAndExit("Update process failed", e)
|
ui.showErrorAndExit("Update process failed", e)
|
||||||
}
|
}
|
||||||
@@ -123,6 +126,7 @@ class Main(args: Array<String>) {
|
|||||||
options.addOption(null, "pack-folder", true, "Folder to install the pack to (defaults to the JAR directory)")
|
options.addOption(null, "pack-folder", true, "Folder to install the pack to (defaults to the JAR directory)")
|
||||||
options.addOption(null, "multimc-folder", true, "The MultiMC pack folder (defaults to the parent of the pack directory)")
|
options.addOption(null, "multimc-folder", true, "The MultiMC pack folder (defaults to the parent of the pack directory)")
|
||||||
options.addOption(null, "meta-file", true, "JSON file to store pack metadata, relative to the pack folder (defaults to packwiz.json)")
|
options.addOption(null, "meta-file", true, "JSON file to store pack metadata, relative to the pack folder (defaults to packwiz.json)")
|
||||||
|
options.addOption("t", "timeout", true, "Seconds to wait before automatically launching when asking about optional mods (defaults to 10)")
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: link these somehow so they're only defined once?
|
// TODO: link these somehow so they're only defined once?
|
||||||
@@ -135,6 +139,12 @@ class Main(args: Array<String>) {
|
|||||||
options.addOption("g", "no-gui", false, "Don't display a GUI to show update progress")
|
options.addOption("g", "no-gui", false, "Don't display a GUI to show update progress")
|
||||||
options.addOption("h", "help", false, "Display this message") // Implemented in packwiz-installer-bootstrap!
|
options.addOption("h", "help", false, "Display this message") // Implemented in packwiz-installer-bootstrap!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun main(args: Array<String>) {
|
||||||
|
Log.info("packwiz-installer was started without packwiz-installer-bootstrap. Use the bootstrapper for automatic updates! (Disregard this message if you have your own update mechanism)")
|
||||||
|
Main(args)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actual main() is in RequiresBootstrap!
|
// Actual main() is in RequiresBootstrap!
|
||||||
|
@@ -23,7 +23,6 @@ import link.infra.packwiz.installer.ui.IUserInterface.ExceptionListResult
|
|||||||
import link.infra.packwiz.installer.ui.data.InstallProgress
|
import link.infra.packwiz.installer.ui.data.InstallProgress
|
||||||
import link.infra.packwiz.installer.util.Log
|
import link.infra.packwiz.installer.util.Log
|
||||||
import okio.buffer
|
import okio.buffer
|
||||||
import java.io.FileWriter
|
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.io.InputStreamReader
|
import java.io.InputStreamReader
|
||||||
import java.nio.charset.StandardCharsets
|
import java.nio.charset.StandardCharsets
|
||||||
@@ -48,7 +47,8 @@ class UpdateManager internal constructor(private val opts: Options, val ui: IUse
|
|||||||
val manifestFile: PackwizFilePath,
|
val manifestFile: PackwizFilePath,
|
||||||
val packFolder: PackwizFilePath,
|
val packFolder: PackwizFilePath,
|
||||||
val multimcFolder: PackwizFilePath,
|
val multimcFolder: PackwizFilePath,
|
||||||
val side: Side
|
val side: Side,
|
||||||
|
val timeout: Long,
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO: make this return a value based on results?
|
// TODO: make this return a value based on results?
|
||||||
@@ -157,7 +157,7 @@ class UpdateManager internal constructor(private val opts: Options, val ui: IUse
|
|||||||
// todo: --force?
|
// todo: --force?
|
||||||
ui.submitProgress(InstallProgress("Modpack is already up to date!", 1, 1))
|
ui.submitProgress(InstallProgress("Modpack is already up to date!", 1, 1))
|
||||||
if (manifest.cachedFiles.any { it.value.isOptional }) {
|
if (manifest.cachedFiles.any { it.value.isOptional }) {
|
||||||
ui.awaitOptionalButton(false)
|
ui.awaitOptionalButton(false, opts.timeout)
|
||||||
}
|
}
|
||||||
if (!ui.optionsButtonPressed) {
|
if (!ui.optionsButtonPressed) {
|
||||||
return
|
return
|
||||||
@@ -196,7 +196,7 @@ class UpdateManager internal constructor(private val opts: Options, val ui: IUse
|
|||||||
|
|
||||||
manifest.cachedSide = opts.side
|
manifest.cachedSide = opts.side
|
||||||
try {
|
try {
|
||||||
FileWriter(opts.manifestFile.nioPath.toFile()).use { writer -> gson.toJson(manifest, writer) }
|
Files.newBufferedWriter(opts.manifestFile.nioPath, StandardCharsets.UTF_8).use { writer -> gson.toJson(manifest, writer) }
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
ui.showErrorAndExit("Failed to save local manifest file", e)
|
ui.showErrorAndExit("Failed to save local manifest file", e)
|
||||||
}
|
}
|
||||||
@@ -206,7 +206,7 @@ class UpdateManager internal constructor(private val opts: Options, val ui: IUse
|
|||||||
if (manifest.indexFileHash == indexHash && invalidatedFiles.isEmpty()) {
|
if (manifest.indexFileHash == indexHash && invalidatedFiles.isEmpty()) {
|
||||||
ui.submitProgress(InstallProgress("Modpack files are already up to date!", 1, 1))
|
ui.submitProgress(InstallProgress("Modpack files are already up to date!", 1, 1))
|
||||||
if (manifest.cachedFiles.any { it.value.isOptional }) {
|
if (manifest.cachedFiles.any { it.value.isOptional }) {
|
||||||
ui.awaitOptionalButton(false)
|
ui.awaitOptionalButton(false, opts.timeout)
|
||||||
}
|
}
|
||||||
if (!ui.optionsButtonPressed) {
|
if (!ui.optionsButtonPressed) {
|
||||||
return
|
return
|
||||||
@@ -338,7 +338,7 @@ class UpdateManager internal constructor(private val opts: Options, val ui: IUse
|
|||||||
if (!ui.optionsButtonPressed) {
|
if (!ui.optionsButtonPressed) {
|
||||||
// TODO: this is so ugly
|
// TODO: this is so ugly
|
||||||
ui.submitProgress(InstallProgress("Reconfigure optional mods?", 0,1))
|
ui.submitProgress(InstallProgress("Reconfigure optional mods?", 0,1))
|
||||||
ui.awaitOptionalButton(true)
|
ui.awaitOptionalButton(true, opts.timeout)
|
||||||
if (ui.cancelButtonPressed) {
|
if (ui.cancelButtonPressed) {
|
||||||
showCancellationDialog()
|
showCancellationDialog()
|
||||||
return
|
return
|
||||||
|
@@ -97,6 +97,16 @@ fun resolveCfMetadata(mods: List<IndexFile.File>, packFolder: PackwizFilePath, c
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Some file types don't show up in the API at all! (e.g. shaderpacks)
|
||||||
|
// Add unresolved files to manualDownloadMods
|
||||||
|
for ((fileId, file) in fileIdMap) {
|
||||||
|
if (file.linkedFile != null) {
|
||||||
|
if (file.linkedFile!!.resolvedUpdateData["curseforge"] == null) {
|
||||||
|
manualDownloadMods[(file.linkedFile!!.update["curseforge"] as CurseForgeUpdateData).projectId] = Pair(file, fileId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (manualDownloadMods.isNotEmpty()) {
|
if (manualDownloadMods.isNotEmpty()) {
|
||||||
val reqModsData = GetModsRequest(manualDownloadMods.keys.toList())
|
val reqModsData = GetModsRequest(manualDownloadMods.keys.toList())
|
||||||
val reqMods = Request.Builder()
|
val reqMods = Request.Builder()
|
||||||
|
@@ -21,7 +21,14 @@ data class Hash<T>(val type: HashFormat<T>, val value: T) {
|
|||||||
|
|
||||||
object UInt: Encoding<kotlin.UInt> {
|
object UInt: Encoding<kotlin.UInt> {
|
||||||
override fun encodeToString(value: kotlin.UInt) = value.toString()
|
override fun encodeToString(value: kotlin.UInt) = value.toString()
|
||||||
override fun decodeFromString(str: String) = str.toUInt()
|
override fun decodeFromString(str: String) =
|
||||||
|
try {
|
||||||
|
str.toUInt()
|
||||||
|
} catch (e: NumberFormatException) {
|
||||||
|
// Old packwiz.json values are signed; if they are negative they should be parsed as signed integers
|
||||||
|
// and reinterpreted as unsigned integers
|
||||||
|
str.toInt().toUInt()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -8,9 +8,12 @@ import java.net.SocketTimeoutException
|
|||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
class ClientHolder {
|
class ClientHolder {
|
||||||
|
// Tries 10s timeouts (default), then 15s timeouts, then 60s timeouts
|
||||||
|
private val retryTimes = arrayOf(15, 60)
|
||||||
|
|
||||||
// TODO: a button to increase timeouts temporarily when retrying? manual retry button?
|
// TODO: a button to increase timeouts temporarily when retrying? manual retry button?
|
||||||
val okHttpClient by lazy { OkHttpClient.Builder()
|
val okHttpClient by lazy { OkHttpClient.Builder()
|
||||||
// Retry requests up to 3 times, increasing the timeouts slightly if it failed
|
// Retry requests according to retryTimes list
|
||||||
.addInterceptor {
|
.addInterceptor {
|
||||||
val req = it.request()
|
val req = it.request()
|
||||||
|
|
||||||
@@ -24,20 +27,20 @@ class ClientHolder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var tryCount = 0
|
var tryCount = 0
|
||||||
while (res == null && tryCount < 3) {
|
while (res == null && tryCount < retryTimes.size) {
|
||||||
tryCount++
|
Log.info("OkHttp connection to ${req.url} timed out; retrying... (${tryCount + 1}/${retryTimes.size})")
|
||||||
|
|
||||||
Log.info("OkHttp connection to ${req.url} timed out; retrying... ($tryCount/3)")
|
|
||||||
|
|
||||||
val longerTimeoutChain = it
|
val longerTimeoutChain = it
|
||||||
.withConnectTimeout(10 * tryCount, TimeUnit.SECONDS)
|
.withConnectTimeout(retryTimes[tryCount], TimeUnit.SECONDS)
|
||||||
.withReadTimeout(10 * tryCount, TimeUnit.SECONDS)
|
.withReadTimeout(retryTimes[tryCount], TimeUnit.SECONDS)
|
||||||
.withWriteTimeout(10 * tryCount, TimeUnit.SECONDS)
|
.withWriteTimeout(retryTimes[tryCount], TimeUnit.SECONDS)
|
||||||
try {
|
try {
|
||||||
res = longerTimeoutChain.proceed(req)
|
res = longerTimeoutChain.proceed(req)
|
||||||
} catch (e: SocketTimeoutException) {
|
} catch (e: SocketTimeoutException) {
|
||||||
lastException = e
|
lastException = e
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tryCount++
|
||||||
}
|
}
|
||||||
|
|
||||||
res ?: throw lastException!!
|
res ?: throw lastException!!
|
||||||
|
@@ -4,42 +4,29 @@ import cc.ekblad.toml.model.TomlValue
|
|||||||
import cc.ekblad.toml.tomlMapper
|
import cc.ekblad.toml.tomlMapper
|
||||||
import com.google.gson.annotations.SerializedName
|
import com.google.gson.annotations.SerializedName
|
||||||
|
|
||||||
enum class Side {
|
enum class Side(sideName: String) {
|
||||||
@SerializedName("client")
|
@SerializedName("client")
|
||||||
CLIENT("client"),
|
CLIENT("client"),
|
||||||
@SerializedName("server")
|
@SerializedName("server")
|
||||||
SERVER("server"),
|
SERVER("server"),
|
||||||
@SerializedName("both")
|
@SerializedName("both")
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
BOTH("both", arrayOf(CLIENT, SERVER));
|
BOTH("both") {
|
||||||
|
override fun hasSide(tSide: Side): Boolean {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
private val sideName: String
|
private val sideName: String
|
||||||
private val depSides: Array<Side>?
|
|
||||||
|
|
||||||
constructor(sideName: String) {
|
init {
|
||||||
this.sideName = sideName.lowercase()
|
this.sideName = sideName.lowercase()
|
||||||
depSides = null
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(sideName: String, depSides: Array<Side>) {
|
|
||||||
this.sideName = sideName.lowercase()
|
|
||||||
this.depSides = depSides
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun toString() = sideName
|
override fun toString() = sideName
|
||||||
|
|
||||||
fun hasSide(tSide: Side): Boolean {
|
open fun hasSide(tSide: Side): Boolean {
|
||||||
if (this == tSide) {
|
return this == tSide || tSide == BOTH
|
||||||
return true
|
|
||||||
}
|
|
||||||
if (depSides != null) {
|
|
||||||
for (depSide in depSides) {
|
|
||||||
if (depSide == tSide) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@@ -43,9 +43,11 @@ abstract class PackwizPath<T: PackwizPath<T>>(path: String? = null) {
|
|||||||
} else {
|
} else {
|
||||||
canonicalised += componentNorm
|
canonicalised += componentNorm
|
||||||
// Don't allow volume letters (allows traversal to the root on Windows)
|
// Don't allow volume letters (allows traversal to the root on Windows)
|
||||||
if (componentNorm[0] in 'a'..'z' || componentNorm[0] in 'A'..'Z') {
|
if (componentNorm.length == 2) {
|
||||||
if (componentNorm[1] == ':') {
|
if (componentNorm[0] in 'a'..'z' || componentNorm[0] in 'A'..'Z') {
|
||||||
throw RequestException.Validation.PathContainsVolumeLetter(path)
|
if (componentNorm[1] == ':') {
|
||||||
|
throw RequestException.Validation.PathContainsVolumeLetter(path)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -25,7 +25,7 @@ interface IUserInterface {
|
|||||||
|
|
||||||
fun showUpdateConfirmationDialog(oldVersions: List<Pair<String, String?>>, newVersions: List<Pair<String, String?>>): UpdateConfirmationResult = UpdateConfirmationResult.CANCELLED
|
fun showUpdateConfirmationDialog(oldVersions: List<Pair<String, String?>>, newVersions: List<Pair<String, String?>>): UpdateConfirmationResult = UpdateConfirmationResult.CANCELLED
|
||||||
|
|
||||||
fun awaitOptionalButton(showCancel: Boolean)
|
fun awaitOptionalButton(showCancel: Boolean, timeout: Long)
|
||||||
|
|
||||||
enum class ExceptionListResult {
|
enum class ExceptionListResult {
|
||||||
CONTINUE, CANCEL, IGNORE
|
CONTINUE, CANCEL, IGNORE
|
||||||
|
@@ -63,7 +63,7 @@ class CLIHandler : IUserInterface {
|
|||||||
return ExceptionListResult.CANCEL
|
return ExceptionListResult.CANCEL
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun awaitOptionalButton(showCancel: Boolean) {
|
override fun awaitOptionalButton(showCancel: Boolean, timeout: Long) {
|
||||||
// Do nothing
|
// Do nothing
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -7,11 +7,13 @@ import link.infra.packwiz.installer.ui.data.IOptionDetails
|
|||||||
import link.infra.packwiz.installer.ui.data.InstallProgress
|
import link.infra.packwiz.installer.ui.data.InstallProgress
|
||||||
import link.infra.packwiz.installer.util.Log
|
import link.infra.packwiz.installer.util.Log
|
||||||
import java.awt.EventQueue
|
import java.awt.EventQueue
|
||||||
|
import java.util.Timer
|
||||||
import java.util.concurrent.CompletableFuture
|
import java.util.concurrent.CompletableFuture
|
||||||
import java.util.concurrent.CountDownLatch
|
import java.util.concurrent.CountDownLatch
|
||||||
import javax.swing.JDialog
|
import javax.swing.JDialog
|
||||||
import javax.swing.JOptionPane
|
import javax.swing.JOptionPane
|
||||||
import javax.swing.UIManager
|
import javax.swing.UIManager
|
||||||
|
import kotlin.concurrent.timer
|
||||||
import kotlin.system.exitProcess
|
import kotlin.system.exitProcess
|
||||||
|
|
||||||
class GUIHandler : IUserInterface {
|
class GUIHandler : IUserInterface {
|
||||||
@@ -220,12 +222,28 @@ class GUIHandler : IUserInterface {
|
|||||||
return future.get()
|
return future.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun awaitOptionalButton(showCancel: Boolean) {
|
override fun awaitOptionalButton(showCancel: Boolean, timeout: Long) {
|
||||||
EventQueue.invokeAndWait {
|
EventQueue.invokeAndWait {
|
||||||
frmPackwizlauncher.showOk(!showCancel)
|
frmPackwizlauncher.showOk(!showCancel)
|
||||||
}
|
}
|
||||||
visibleCountdownLatch.await()
|
visibleCountdownLatch.await()
|
||||||
|
|
||||||
|
var closeTimer: Timer? = null
|
||||||
|
if (timeout >= 0) {
|
||||||
|
var count = 0
|
||||||
|
closeTimer = timer("timeout", true, 0, 1000) {
|
||||||
|
if (count >= timeout) {
|
||||||
|
optionalSelectedLatch.countDown()
|
||||||
|
cancel()
|
||||||
|
} else {
|
||||||
|
frmPackwizlauncher.timeoutOk(timeout - count)
|
||||||
|
count += 1
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
optionalSelectedLatch.await()
|
optionalSelectedLatch.await()
|
||||||
|
closeTimer?.cancel()
|
||||||
EventQueue.invokeLater {
|
EventQueue.invokeLater {
|
||||||
frmPackwizlauncher.hideOk()
|
frmPackwizlauncher.hideOk()
|
||||||
}
|
}
|
||||||
|
@@ -121,4 +121,8 @@ class InstallWindow(private val handler: GUIHandler) : JFrame() {
|
|||||||
}
|
}
|
||||||
buttonsPanel.revalidate()
|
buttonsPanel.revalidate()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun timeoutOk(remaining: Long) {
|
||||||
|
btnOk.text = "Continue ($remaining)"
|
||||||
|
}
|
||||||
}
|
}
|
Reference in New Issue
Block a user