Compare commits

..

8 Commits

Author SHA1 Message Date
comp500
8beded7b41 Improve UX when there are no optional mods 2020-12-06 19:05:56 +00:00
comp500
91060dcd54 Put an error message there. Later is now! 2020-11-30 00:24:47 +00:00
comp500
e06ee21f3b Add User-Agent to download requests 2020-10-22 20:53:36 +01:00
comp500
b3370739a5 Fix Swing multithreading issue, clean up slightly 2020-09-29 02:14:56 +01:00
comp500
ecc6f0440a Remove IntelliJ metadata from repo 2020-09-29 02:14:05 +01:00
comp500
92b44352b3 Fix RequestHandlerGithub heuristics, so that Github Releases files work properly 2020-06-20 03:15:18 +01:00
comp500
1d5a787b02 Add JvmStatic to fix --help command (bootstrapper calls these) 2020-06-16 04:05:02 +01:00
comp500
b5983800e8 Update README.md 2020-05-11 17:48:41 +01:00
14 changed files with 118 additions and 173 deletions

30
.gitignore vendored
View File

@@ -1,9 +1,9 @@
# Created by https://www.gitignore.io/api/java,gradle,intellij
# Edit at https://www.gitignore.io/?templates=java,gradle,intellij
# Created by https://www.toptal.com/developers/gitignore/api/java,gradle,intellij+all
# Edit at https://www.toptal.com/developers/gitignore?templates=java,gradle,intellij+all
### Intellij ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
### Intellij+all ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff
@@ -33,6 +33,9 @@
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
# .idea/artifacts
# .idea/compiler.xml
# .idea/jarRepositories.xml
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
@@ -72,13 +75,18 @@ fabric.properties
# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser
### Intellij Patch ###
# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
### Intellij+all Patch ###
# Ignores the whole .idea folder and all .iml files
# See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360
# *.iml
# modules.xml
# .idea/misc.xml
# *.ipr
.idea/
# Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023
*.iml
modules.xml
.idea/misc.xml
*.ipr
# Sonarlint plugin
.idea/sonarlint
@@ -127,4 +135,4 @@ gradle-app.setting
### Gradle Patch ###
**/build/
# End of https://www.gitignore.io/api/java,gradle,intellij
# End of https://www.toptal.com/developers/gitignore/api/java,gradle,intellij+all

View File

@@ -1,5 +0,0 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
</state>
</component>

6
.idea/compiler.xml generated
View File

@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<bytecodeTargetLevel target="1.8" />
</component>
</project>

View File

@@ -1,36 +0,0 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="JavaDoc" enabled="true" level="WARNING" enabled_by_default="true">
<option name="TOP_LEVEL_CLASS_OPTIONS">
<value>
<option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" />
<option name="REQUIRED_TAGS" value="" />
</value>
</option>
<option name="INNER_CLASS_OPTIONS">
<value>
<option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" />
<option name="REQUIRED_TAGS" value="" />
</value>
</option>
<option name="METHOD_OPTIONS">
<value>
<option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" />
<option name="REQUIRED_TAGS" value="@return@param@throws or @exception" />
</value>
</option>
<option name="FIELD_OPTIONS">
<value>
<option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" />
<option name="REQUIRED_TAGS" value="" />
</value>
</option>
<option name="IGNORE_DEPRECATED" value="false" />
<option name="IGNORE_JAVADOC_PERIOD" value="true" />
<option name="IGNORE_DUPLICATED_THROWS" value="false" />
<option name="IGNORE_POINT_TO_ITSELF" value="false" />
<option name="myAdditionalJavadocTags" value="wbp.parser.entryPoint" />
</inspection_tool>
</profile>
</component>

View File

@@ -1,20 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RemoteRepositoriesConfiguration">
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Maven Central repository" />
<option name="url" value="https://repo1.maven.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="jboss.community" />
<option name="name" value="JBoss Community repository" />
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
</remote-repository>
<remote-repository>
<option name="id" value="BintrayJCenter" />
<option name="name" value="BintrayJCenter" />
<option name="url" value="https://jcenter.bintray.com/" />
</remote-repository>
</component>
</project>

10
.idea/misc.xml generated
View File

@@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="EntryPointsManager">
<list size="1">
<item index="0" class="java.lang.String" itemvalue="com.google.gson.annotations.SerializedName" />
</list>
</component>
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="false" project-jdk-name="11" project-jdk-type="JavaSDK" />
</project>

View File

@@ -1,2 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4" />

6
.idea/vcs.xml generated
View File

@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

View File

@@ -1,2 +1,2 @@
# packwiz-installer
An installer for launching packwiz modpacks with MultiMC.
An installer for launching packwiz modpacks with MultiMC. You'll need [the bootstrapper](https://github.com/comp500/packwiz-installer-bootstrap/releases) to actually use this.

View File

@@ -18,6 +18,9 @@ import kotlin.system.exitProcess
@Suppress("unused")
class Main(args: Array<String>) {
// Don't attempt to start a GUI if we are headless
var guiEnabled = !GraphicsEnvironment.isHeadless()
private fun startup(args: Array<String>) {
val options = Options()
addNonBootstrapOptions(options)
@@ -28,19 +31,24 @@ class Main(args: Array<String>) {
parser.parse(options, args)
} catch (e: ParseException) {
e.printStackTrace()
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName())
} catch (e1: Exception) {
// Ignore the exceptions, just continue using the ugly L&F
if (guiEnabled) {
EventQueue.invokeAndWait {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName())
} catch (ignored: Exception) {
// Ignore the exceptions, just continue using the ugly L&F
}
JOptionPane.showMessageDialog(null, e.message, "packwiz-installer", JOptionPane.ERROR_MESSAGE)
}
}
JOptionPane.showMessageDialog(null, e.message, "packwiz-installer", JOptionPane.ERROR_MESSAGE)
exitProcess(1)
}
// if "headless", GUI creation will fail anyway!
val ui = if (cmd.hasOption("no-gui") || GraphicsEnvironment.isHeadless()) {
CLIHandler()
} else InstallWindow()
if (guiEnabled && cmd.hasOption("no-gui")) {
guiEnabled = false
}
val ui = if (guiEnabled) InstallWindow() else CLIHandler()
val unparsedArgs = cmd.args
if (unparsedArgs.size > 1) {
@@ -49,15 +57,13 @@ class Main(args: Array<String>) {
ui.handleExceptionAndExit(RuntimeException("URI to install from must be specified!"))
}
cmd.getOptionValue("title")?.also {
ui.setTitle(it)
}
cmd.getOptionValue("title")?.also(ui::setTitle)
val inputStateHandler = InputStateHandler()
ui.show(inputStateHandler)
val uOptions = UpdateManager.Options().apply {
side = cmd.getOptionValue("side")?.let { UpdateManager.Options.Side.from(it) } ?: side
side = cmd.getOptionValue("side")?.let((UpdateManager.Options.Side)::from) ?: side
packFolder = cmd.getOptionValue("pack-folder") ?: packFolder
manifestFile = cmd.getOptionValue("meta-file") ?: manifestFile
}
@@ -86,6 +92,7 @@ class Main(args: Array<String>) {
companion object {
// Called by packwiz-installer-bootstrap to set up the help command
@JvmStatic
fun addNonBootstrapOptions(options: Options) {
options.addOption("s", "side", true, "Side to install mods from (client/server, defaults to client)")
options.addOption(null, "title", true, "Title of the installer window")
@@ -94,6 +101,7 @@ class Main(args: Array<String>) {
}
// TODO: link these somehow so they're only defined once?
@JvmStatic
private fun addBootstrapOptions(options: Options) {
options.addOption(null, "bootstrap-update-url", true, "Github API URL for checking for updates")
options.addOption(null, "bootstrap-update-token", true, "Github API Access Token, for private repositories")
@@ -111,11 +119,13 @@ class Main(args: Array<String>) {
startup(args)
} catch (e: Exception) {
e.printStackTrace()
EventQueue.invokeLater {
JOptionPane.showMessageDialog(null,
if (guiEnabled) {
EventQueue.invokeLater {
JOptionPane.showMessageDialog(null,
"A fatal error occurred: \n" + e.javaClass.canonicalName + ": " + e.message,
"packwiz-installer", JOptionPane.ERROR_MESSAGE)
exitProcess(1)
exitProcess(1)
}
}
// In case the EventQueue is broken, exit after 1 minute
Thread.sleep(60 * 1000.toLong())

View File

@@ -255,8 +255,7 @@ class UpdateManager internal constructor(private val opts: Options, val ui: IUse
return
}
if (!indexFileSource.hashIsEqual(indexHash)) {
// TODO: throw exception
println("I was meant to put an error message here but I'll do that later")
ui.handleExceptionAndExit(RuntimeException("Your index hash is invalid! Please run packwiz refresh on the pack again"))
return
}
if (stateHandler.cancelButton) {

View File

@@ -65,7 +65,7 @@ class RequestHandlerGithub : RequestHandlerZip(true) {
if (!("http" == scheme || "https" == scheme)) {
return false
}
return "github.com" == loc.host
// TODO: sanity checks, support for more github urls
// TODO: more match testing?
return "github.com" == loc.host && branchMatcherPattern.matcher(loc.path).matches()
}
}

View File

@@ -4,6 +4,7 @@ import link.infra.packwiz.installer.metadata.SpaceSafeURI
import link.infra.packwiz.installer.request.IRequestHandler
import okio.Source
import okio.source
import java.net.HttpURLConnection
open class RequestHandlerHTTP : IRequestHandler {
override fun matchesHandler(loc: SpaceSafeURI): Boolean {
@@ -12,14 +13,17 @@ open class RequestHandlerHTTP : IRequestHandler {
}
override fun getFileSource(loc: SpaceSafeURI): Source? {
val conn = loc.toURL().openConnection()
val conn = loc.toURL().openConnection() as HttpURLConnection
// TODO: when do we send specific headers??? should there be a way to signal this?
// github *sometimes* requires it, sometimes not!
//conn.addRequestProperty("Accept", "application/octet-stream");
conn.addRequestProperty("Accept", "application/octet-stream")
// TODO: include version?
conn.addRequestProperty("User-Agent", "packwiz-installer")
conn.apply {
// 30 second read timeout
readTimeout = 30 * 1000
requestMethod = "GET"
}
return conn.getInputStream().source()
return conn.inputStream.source()
}
}

View File

@@ -10,10 +10,10 @@ import javax.swing.border.EmptyBorder
import kotlin.system.exitProcess
class InstallWindow : IUserInterface {
private val frmPackwizlauncher: JFrame
private val lblProgresslabel: JLabel
private val progressBar: JProgressBar
private val btnOptions: JButton
private lateinit var frmPackwizlauncher: JFrame
private lateinit var lblProgresslabel: JLabel
private lateinit var progressBar: JProgressBar
private lateinit var btnOptions: JButton
private var inputStateHandler: InputStateHandler? = null
private var title = "Updating modpack..."
@@ -23,55 +23,57 @@ class InstallWindow : IUserInterface {
// TODO: separate JFrame junk from IUserInterface junk?
init {
frmPackwizlauncher = JFrame().apply {
title = this@InstallWindow.title
setBounds(100, 100, 493, 95)
defaultCloseOperation = JFrame.EXIT_ON_CLOSE
setLocationRelativeTo(null)
EventQueue.invokeAndWait {
frmPackwizlauncher = JFrame().apply {
title = this@InstallWindow.title
setBounds(100, 100, 493, 95)
defaultCloseOperation = JFrame.EXIT_ON_CLOSE
setLocationRelativeTo(null)
// Progress bar and loading text
add(JPanel().apply {
border = EmptyBorder(10, 10, 10, 10)
layout = BorderLayout(0, 0)
// Progress bar and loading text
add(JPanel().apply {
border = EmptyBorder(10, 10, 10, 10)
layout = BorderLayout(0, 0)
progressBar = JProgressBar().apply {
isIndeterminate = true
}
add(progressBar, BorderLayout.CENTER)
lblProgresslabel = JLabel("Loading...")
add(lblProgresslabel, BorderLayout.SOUTH)
}, BorderLayout.CENTER)
// Buttons
add(JPanel().apply {
border = EmptyBorder(0, 5, 0, 5)
layout = GridBagLayout()
btnOptions = JButton("Optional mods...").apply {
alignmentX = Component.CENTER_ALIGNMENT
addActionListener {
text = "Loading..."
isEnabled = false
inputStateHandler?.pressOptionsButton()
progressBar = JProgressBar().apply {
isIndeterminate = true
}
}
add(btnOptions, GridBagConstraints().apply {
gridx = 0
gridy = 0
})
add(progressBar, BorderLayout.CENTER)
add(JButton("Cancel").apply {
addActionListener {
isEnabled = false
inputStateHandler?.pressCancelButton()
lblProgresslabel = JLabel("Loading...")
add(lblProgresslabel, BorderLayout.SOUTH)
}, BorderLayout.CENTER)
// Buttons
add(JPanel().apply {
border = EmptyBorder(0, 5, 0, 5)
layout = GridBagLayout()
btnOptions = JButton("Optional mods...").apply {
alignmentX = Component.CENTER_ALIGNMENT
addActionListener {
text = "Loading..."
isEnabled = false
inputStateHandler?.pressOptionsButton()
}
}
}, GridBagConstraints().apply {
gridx = 0
gridy = 1
})
}, BorderLayout.EAST)
add(btnOptions, GridBagConstraints().apply {
gridx = 0
gridy = 0
})
add(JButton("Cancel").apply {
addActionListener {
isEnabled = false
inputStateHandler?.pressCancelButton()
}
}, GridBagConstraints().apply {
gridx = 0
gridy = 1
})
}, BorderLayout.EAST)
}
}
}
@@ -178,9 +180,16 @@ class InstallWindow : IUserInterface {
override fun showOptions(options: List<IOptionDetails>): Future<Boolean> {
val future = CompletableFuture<Boolean>()
EventQueue.invokeLater {
OptionsSelectWindow(options, future, frmPackwizlauncher).apply {
defaultCloseOperation = JDialog.DISPOSE_ON_CLOSE
isVisible = true
if (options.isEmpty()) {
JOptionPane.showMessageDialog(null,
"This modpack has no optional mods!",
"Optional mods", JOptionPane.INFORMATION_MESSAGE)
future.complete(false)
} else {
OptionsSelectWindow(options, future, frmPackwizlauncher).apply {
defaultCloseOperation = JDialog.DISPOSE_ON_CLOSE
isVisible = true
}
}
}
return future
@@ -199,7 +208,7 @@ class InstallWindow : IUserInterface {
override fun disableOptionsButton() {
btnOptions.apply {
text = "Optional mods..."
text = "No optional mods"
isEnabled = false
}
}