mirror of
https://github.com/HMCL-dev/HMCL.git
synced 2025-01-12 14:14:52 +08:00
UI works
This commit is contained in:
parent
3c32ef39c7
commit
41498b5d93
43
HMCL/src/main/java/org/jackhuang/hmcl/Events.kt
Normal file
43
HMCL/src/main/java/org/jackhuang/hmcl/Events.kt
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||
*/
|
||||
package org.jackhuang.hmcl
|
||||
|
||||
import org.jackhuang.hmcl.setting.Profile
|
||||
import java.util.EventObject
|
||||
|
||||
/**
|
||||
* This event gets fired when the selected profile changed.
|
||||
* <br></br>
|
||||
* This event is fired on the [org.jackhuang.hmcl.event.EVENT_BUS]
|
||||
* @param source [org.jackhuang.hmcl.setting.Settings]
|
||||
* *
|
||||
* @param Profile the new profile.
|
||||
* *
|
||||
* @author huangyuhui
|
||||
*/
|
||||
class ProfileChangedEvent(source: Any, val value: Profile) : EventObject(source)
|
||||
|
||||
/**
|
||||
* This event gets fired when loading profiles.
|
||||
* <br></br>
|
||||
* This event is fired on the [org.jackhuang.hmcl.event.EVENT_BUS]
|
||||
* @param source [org.jackhuang.hmcl.setting.Settings]
|
||||
* *
|
||||
* @author huangyuhui
|
||||
*/
|
||||
class ProfileLoadingEvent(source: Any) : EventObject(source)
|
@ -17,7 +17,12 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.game
|
||||
|
||||
import com.google.gson.GsonBuilder
|
||||
import javafx.beans.InvalidationListener
|
||||
import org.jackhuang.hmcl.setting.VersionSetting
|
||||
import org.jackhuang.hmcl.util.GSON
|
||||
import org.jackhuang.hmcl.util.LOG
|
||||
import org.jackhuang.hmcl.util.fromJson
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.util.logging.Level
|
||||
@ -25,19 +30,69 @@ import java.util.logging.Level
|
||||
class HMCLGameRepository(baseDirectory: File)
|
||||
: DefaultGameRepository(baseDirectory) {
|
||||
|
||||
val PROFILE = "{\"selectedProfile\": \"(Default)\",\"profiles\": {\"(Default)\": {\"name\": \"(Default)\"}},\"clientToken\": \"88888888-8888-8888-8888-888888888888\"}"
|
||||
private val versionSettings = HashMap<String, VersionSetting>()
|
||||
|
||||
@Synchronized
|
||||
override fun refreshVersions() {
|
||||
super.refreshVersions()
|
||||
override fun refreshVersionsImpl() {
|
||||
versionSettings.clear()
|
||||
|
||||
super.refreshVersionsImpl()
|
||||
|
||||
versions.keys.forEach(this::loadVersionSetting)
|
||||
|
||||
checkModpack()
|
||||
|
||||
try {
|
||||
val file = baseDirectory.resolve("launcher_profiles.json")
|
||||
if (!file.exists())
|
||||
if (!file.exists() && versions.isNotEmpty())
|
||||
file.writeText(PROFILE)
|
||||
} catch (ex: IOException) {
|
||||
LOG.log(Level.WARNING, "Unable to create launcher_profiles.json, Forge/LiteLoader installer will not work.", ex)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun checkModpack() {}
|
||||
|
||||
private fun getVersionSettingFile(id: String) = getVersionRoot(id).resolve("hmclversion.cfg")
|
||||
|
||||
private fun loadVersionSetting(id: String) {
|
||||
val file = getVersionSettingFile(id)
|
||||
if (file.exists()) {
|
||||
try {
|
||||
val versionSetting = GSON.fromJson<VersionSetting>(file.readText())!!
|
||||
initVersionSetting(id, versionSetting)
|
||||
} catch (ignore: Exception) {
|
||||
// If [JsonParseException], [IOException] or [NullPointerException] happens, the json file is malformed and needed to be recreated.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun saveVersionSetting(id: String) {
|
||||
if (!versionSettings.containsKey(id))
|
||||
return
|
||||
|
||||
getVersionSettingFile(id).writeText(GSON.toJson(versionSettings[id]))
|
||||
}
|
||||
|
||||
private fun initVersionSetting(id: String, vs: VersionSetting): VersionSetting {
|
||||
vs.addPropertyChangedListener(InvalidationListener { saveVersionSetting(id) })
|
||||
versionSettings[id] = vs
|
||||
return vs
|
||||
}
|
||||
|
||||
internal fun createVersionSetting(id: String): VersionSetting? {
|
||||
if (!hasVersion(id)) return null
|
||||
return versionSettings[id] ?: initVersionSetting(id, VersionSetting())
|
||||
}
|
||||
|
||||
fun getVersionSetting(id: String): VersionSetting? {
|
||||
if (!versionSettings.containsKey(id))
|
||||
loadVersionSetting(id)
|
||||
return versionSettings[id]
|
||||
}
|
||||
|
||||
companion object {
|
||||
val PROFILE = "{\"selectedProfile\": \"(Default)\",\"profiles\": {\"(Default)\": {\"name\": \"(Default)\"}},\"clientToken\": \"88888888-8888-8888-8888-888888888888\"}"
|
||||
val GSON = GsonBuilder().registerTypeAdapter(VersionSetting::class.java, VersionSetting).setPrettyPrinting().create()
|
||||
}
|
||||
}
|
123
HMCL/src/main/java/org/jackhuang/hmcl/setting/Config.kt
Normal file
123
HMCL/src/main/java/org/jackhuang/hmcl/setting/Config.kt
Normal file
@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||
*/
|
||||
package org.jackhuang.hmcl.setting
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import org.jackhuang.hmcl.MainApplication
|
||||
import org.jackhuang.hmcl.util.JavaVersion
|
||||
import java.io.File
|
||||
import java.util.TreeMap
|
||||
|
||||
class Config {
|
||||
@SerializedName("last")
|
||||
var last: String = ""
|
||||
set(value) {
|
||||
field = value
|
||||
Settings.save()
|
||||
}
|
||||
@SerializedName("bgpath")
|
||||
var bgpath: String? = null
|
||||
set(value) {
|
||||
field = value
|
||||
Settings.save()
|
||||
}
|
||||
@SerializedName("commonpath")
|
||||
var commonpath: File = MainApplication.getMinecraftDirectory()
|
||||
set(value) {
|
||||
field = value
|
||||
Settings.save()
|
||||
}
|
||||
@SerializedName("proxyHost")
|
||||
var proxyHost: String? = null
|
||||
set(value) {
|
||||
field = value
|
||||
Settings.save()
|
||||
}
|
||||
@SerializedName("proxyPort")
|
||||
var proxyPort: String? = null
|
||||
set(value) {
|
||||
field = value
|
||||
Settings.save()
|
||||
}
|
||||
@SerializedName("proxyUserName")
|
||||
var proxyUserName: String? = null
|
||||
set(value) {
|
||||
field = value
|
||||
Settings.save()
|
||||
}
|
||||
@SerializedName("proxyPassword")
|
||||
var proxyPassword: String? = null
|
||||
set(value) {
|
||||
field = value
|
||||
Settings.save()
|
||||
}
|
||||
@SerializedName("theme")
|
||||
var theme: String? = null
|
||||
set(value) {
|
||||
field = value
|
||||
Settings.save()
|
||||
}
|
||||
@SerializedName("java")
|
||||
var java: List<JavaVersion>? = null
|
||||
set(value) {
|
||||
field = value
|
||||
Settings.save()
|
||||
}
|
||||
@SerializedName("localization")
|
||||
var localization: String? = null
|
||||
set(value) {
|
||||
field = value
|
||||
Settings.save()
|
||||
}
|
||||
@SerializedName("downloadtype")
|
||||
var downloadtype: Int = 0
|
||||
set(value) {
|
||||
field = value
|
||||
Settings.save()
|
||||
}
|
||||
@SerializedName("configurations")
|
||||
var configurations: MutableMap<String, Profile> = TreeMap()
|
||||
set(value) {
|
||||
field = value
|
||||
Settings.save()
|
||||
}
|
||||
@SerializedName("accounts")
|
||||
var accounts: MutableMap<String, MutableMap<*, *>> = TreeMap()
|
||||
set(value) {
|
||||
field = value
|
||||
Settings.save()
|
||||
}
|
||||
@SerializedName("fontFamily")
|
||||
var fontFamily: String? = null
|
||||
set(value) {
|
||||
field = value
|
||||
Settings.save()
|
||||
}
|
||||
@SerializedName("fontSize")
|
||||
var fontSize: Int = 12
|
||||
set(value) {
|
||||
field = value
|
||||
Settings.save()
|
||||
}
|
||||
@SerializedName("logLines")
|
||||
var logLines: Int = 100
|
||||
set(value) {
|
||||
field = value
|
||||
Settings.save()
|
||||
}
|
||||
}
|
117
HMCL/src/main/java/org/jackhuang/hmcl/setting/Profile.kt
Normal file
117
HMCL/src/main/java/org/jackhuang/hmcl/setting/Profile.kt
Normal file
@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||
*/
|
||||
package org.jackhuang.hmcl.setting
|
||||
|
||||
import com.google.gson.*
|
||||
import javafx.beans.InvalidationListener
|
||||
import javafx.beans.property.*
|
||||
import org.jackhuang.hmcl.game.HMCLGameRepository
|
||||
import org.jackhuang.hmcl.util.*
|
||||
import java.io.File
|
||||
import java.lang.reflect.Type
|
||||
|
||||
class Profile(var name: String = "Default", gameDir: File = File(".minecraft")) {
|
||||
val globalProperty = SimpleObjectProperty<VersionSetting>(this, "global", VersionSetting())
|
||||
var global: VersionSetting by globalProperty
|
||||
|
||||
val selectedVersionProperty = SimpleStringProperty(this, "selectedVersion", "")
|
||||
var selectedVersion: String by selectedVersionProperty
|
||||
|
||||
val gameDirProperty = SimpleObjectProperty<File>(this, "gameDir", gameDir)
|
||||
var gameDir: File by gameDirProperty
|
||||
|
||||
val noCommonProperty = SimpleBooleanProperty(this, "noCommon", false)
|
||||
var noCommon: Boolean by noCommonProperty
|
||||
|
||||
var repository = HMCLGameRepository(gameDir)
|
||||
|
||||
init {
|
||||
gameDirProperty.addListener { _, _, newValue ->
|
||||
repository.baseDirectory = newValue
|
||||
repository.refreshVersions()
|
||||
}
|
||||
|
||||
selectedVersionProperty.addListener { _, _, newValue ->
|
||||
if (newValue.isNotBlank() && !repository.hasVersion(newValue)) {
|
||||
val newVersion = repository.getVersions().firstOrNull()
|
||||
// will cause anthor change event, we must insure that there will not be dead recursion.
|
||||
selectedVersion = newVersion?.id ?: ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun specializeVersionSetting(id: String) {
|
||||
var vs = repository.getVersionSetting(id)
|
||||
if (vs == null)
|
||||
vs = repository.createVersionSetting(id) ?: return
|
||||
vs.usesGlobal = false
|
||||
}
|
||||
|
||||
fun globalizeVersionSetting(id: String) {
|
||||
repository.getVersionSetting(id)?.usesGlobal = true
|
||||
}
|
||||
|
||||
fun isVersionGlobal(id: String): Boolean {
|
||||
return repository.getVersionSetting(id)?.usesGlobal ?: true
|
||||
}
|
||||
|
||||
fun getVersionSetting(id: String): VersionSetting {
|
||||
val vs = repository.getVersionSetting(id)
|
||||
if (vs == null || vs.usesGlobal) {
|
||||
global.isGlobal = true // always keep global.isGlobal = true
|
||||
return global
|
||||
} else
|
||||
return vs
|
||||
}
|
||||
|
||||
fun getSelectedVersionSetting(): VersionSetting =
|
||||
getVersionSetting(selectedVersion)
|
||||
|
||||
fun addPropertyChangedListener(listener: InvalidationListener) {
|
||||
globalProperty.addListener(listener)
|
||||
selectedVersionProperty.addListener(listener)
|
||||
gameDirProperty.addListener(listener)
|
||||
noCommonProperty.addListener(listener)
|
||||
}
|
||||
|
||||
companion object Serializer: JsonSerializer<Profile>, JsonDeserializer<Profile> {
|
||||
override fun serialize(src: Profile?, typeOfSrc: Type?, context: JsonSerializationContext): JsonElement {
|
||||
if (src == null) return JsonNull.INSTANCE
|
||||
val jsonObject = JsonObject()
|
||||
with(jsonObject) {
|
||||
add("global", context.serialize(src.global))
|
||||
addProperty("selectedVersion", src.selectedVersion)
|
||||
addProperty("gameDir", src.gameDir.path)
|
||||
addProperty("noCommon", src.noCommon)
|
||||
}
|
||||
|
||||
return jsonObject
|
||||
}
|
||||
|
||||
override fun deserialize(json: JsonElement?, typeOfT: Type?, context: JsonDeserializationContext): Profile? {
|
||||
if (json == null || json == JsonNull.INSTANCE || json !is JsonObject) return null
|
||||
|
||||
return Profile(gameDir = File(json["gameDir"]?.asString ?: "")).apply {
|
||||
global = context.deserialize(json["global"], VersionSetting::class.java)
|
||||
selectedVersion = json["selectedVersion"]?.asString ?: ""
|
||||
noCommon = json["noCommon"]?.asBoolean ?: false
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -17,8 +17,154 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.setting
|
||||
|
||||
import com.google.gson.GsonBuilder
|
||||
import javafx.beans.InvalidationListener
|
||||
import java.io.IOException
|
||||
import org.jackhuang.hmcl.MainApplication
|
||||
import org.jackhuang.hmcl.download.BMCLAPIDownloadProvider
|
||||
import org.jackhuang.hmcl.download.DownloadProvider
|
||||
import org.jackhuang.hmcl.download.MojangDownloadProvider
|
||||
import org.jackhuang.hmcl.util.GSON
|
||||
import org.jackhuang.hmcl.util.LOG
|
||||
import java.io.File
|
||||
import java.util.logging.Level
|
||||
import org.jackhuang.hmcl.ProfileLoadingEvent
|
||||
import org.jackhuang.hmcl.ProfileChangedEvent
|
||||
import org.jackhuang.hmcl.event.EVENT_BUS
|
||||
import org.jackhuang.hmcl.util.FileTypeAdapter
|
||||
|
||||
|
||||
object Settings {
|
||||
val GSON = GsonBuilder()
|
||||
.registerTypeAdapter(VersionSetting::class.java, VersionSetting)
|
||||
.registerTypeAdapter(Profile::class.java, Profile)
|
||||
.registerTypeAdapter(File::class.java, FileTypeAdapter)
|
||||
.setPrettyPrinting().create()
|
||||
|
||||
val DEFAULT_PROFILE = "Default"
|
||||
val HOME_PROFILE = "Home"
|
||||
|
||||
val SETTINGS_FILE = File("hmcl.json").absoluteFile
|
||||
|
||||
val SETTINGS: Config
|
||||
|
||||
init {
|
||||
|
||||
SETTINGS = initSettings();
|
||||
save()
|
||||
|
||||
if (!getProfiles().containsKey(DEFAULT_PROFILE))
|
||||
getProfiles().put(DEFAULT_PROFILE, Profile());
|
||||
|
||||
for ((name, profile) in getProfiles().entries) {
|
||||
profile.name = name
|
||||
profile.addPropertyChangedListener(InvalidationListener { save() })
|
||||
}
|
||||
}
|
||||
|
||||
fun getDownloadProvider(): DownloadProvider = when (SETTINGS.downloadtype) {
|
||||
0 -> MojangDownloadProvider
|
||||
1 -> BMCLAPIDownloadProvider
|
||||
else -> MojangDownloadProvider
|
||||
}
|
||||
|
||||
private fun initSettings(): Config {
|
||||
var c = Config()
|
||||
if (SETTINGS_FILE.exists())
|
||||
try {
|
||||
val str = SETTINGS_FILE.readText()
|
||||
if (str.trim() == "")
|
||||
LOG.finer("Settings file is empty, use the default settings.")
|
||||
else {
|
||||
val d = GSON.fromJson(str, Config::class.java)
|
||||
if (d != null)
|
||||
c = d
|
||||
}
|
||||
LOG.finest("Initialized settings.")
|
||||
} catch (e: Exception) {
|
||||
LOG.log(Level.WARNING, "Something happened wrongly when load settings.", e)
|
||||
}
|
||||
else {
|
||||
LOG.config("No settings file here, may be first loading.")
|
||||
if (!c.configurations.containsKey(HOME_PROFILE))
|
||||
c.configurations[HOME_PROFILE] = Profile(HOME_PROFILE, MainApplication.getMinecraftDirectory())
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
fun save() {
|
||||
try {
|
||||
SETTINGS_FILE.writeText(GSON.toJson(SETTINGS))
|
||||
} catch (ex: IOException) {
|
||||
LOG.log(Level.SEVERE, "Failed to save config", ex)
|
||||
}
|
||||
}
|
||||
|
||||
fun getLastProfile(): Profile {
|
||||
if (!hasProfile(SETTINGS.last))
|
||||
SETTINGS.last = DEFAULT_PROFILE
|
||||
return getProfile(SETTINGS.last)
|
||||
}
|
||||
|
||||
fun getProfile(name: String?): Profile {
|
||||
var p: Profile? = getProfiles()[name ?: DEFAULT_PROFILE]
|
||||
if (p == null)
|
||||
if (getProfiles().containsKey(DEFAULT_PROFILE))
|
||||
p = getProfiles()[DEFAULT_PROFILE]!!
|
||||
else {
|
||||
p = Profile()
|
||||
getProfiles().put(DEFAULT_PROFILE, p)
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
fun hasProfile(name: String?): Boolean {
|
||||
return getProfiles().containsKey(name ?: DEFAULT_PROFILE)
|
||||
}
|
||||
|
||||
fun getProfiles(): MutableMap<String, Profile> {
|
||||
return SETTINGS.configurations
|
||||
}
|
||||
|
||||
fun getProfilesFiltered(): Collection<Profile> {
|
||||
return getProfiles().values.filter { t -> t.name.isNotBlank() }
|
||||
}
|
||||
|
||||
fun putProfile(ver: Profile?): Boolean {
|
||||
if (ver == null || ver.name.isBlank() || getProfiles().containsKey(ver.name))
|
||||
return false
|
||||
getProfiles().put(ver.name, ver)
|
||||
return true
|
||||
}
|
||||
|
||||
fun delProfile(ver: Profile): Boolean {
|
||||
return delProfile(ver.name)
|
||||
}
|
||||
|
||||
fun delProfile(ver: String): Boolean {
|
||||
if (DEFAULT_PROFILE == ver) {
|
||||
return false
|
||||
}
|
||||
var notify = false
|
||||
if (getLastProfile().name == ver)
|
||||
notify = true
|
||||
val flag = getProfiles().remove(ver) != null
|
||||
if (notify && flag)
|
||||
onProfileChanged()
|
||||
return flag
|
||||
}
|
||||
|
||||
internal fun onProfileChanged() {
|
||||
val p = getLastProfile()
|
||||
EVENT_BUS.fireEvent(ProfileChangedEvent(SETTINGS, p))
|
||||
p.repository.refreshVersions()
|
||||
}
|
||||
|
||||
/**
|
||||
* Start profiles loading process.
|
||||
* Invoked by loading GUI phase.
|
||||
*/
|
||||
fun onProfileLoading() {
|
||||
EVENT_BUS.fireEvent(ProfileLoadingEvent(SETTINGS))
|
||||
onProfileChanged()
|
||||
}
|
||||
}
|
@ -18,17 +18,14 @@
|
||||
package org.jackhuang.hmcl.setting
|
||||
|
||||
import com.google.gson.*
|
||||
import javafx.beans.InvalidationListener
|
||||
import javafx.beans.property.*
|
||||
import org.jackhuang.hmcl.util.*
|
||||
import java.lang.reflect.Type
|
||||
|
||||
class VersionSetting() {
|
||||
|
||||
/**
|
||||
* The displayed name.
|
||||
*/
|
||||
val nameProperty = SimpleStringProperty(this, "name", "")
|
||||
var name: String by nameProperty
|
||||
var isGlobal: Boolean = false
|
||||
|
||||
/**
|
||||
* HMCL Version Settings have been divided into 2 parts.
|
||||
@ -162,15 +159,31 @@ class VersionSetting() {
|
||||
val launcherVisibilityProperty = SimpleObjectProperty<LauncherVisibility>(this, "launcherVisibility", LauncherVisibility.HIDE)
|
||||
var launcherVisibility: LauncherVisibility by launcherVisibilityProperty
|
||||
|
||||
val gameVersion: String
|
||||
get() = "1.7.10"
|
||||
fun addPropertyChangedListener(listener: InvalidationListener) {
|
||||
usesGlobalProperty.addListener(listener)
|
||||
javaProperty.addListener(listener)
|
||||
javaDirProperty.addListener(listener)
|
||||
wrapperProperty.addListener(listener)
|
||||
permSizeProperty.addListener(listener)
|
||||
maxMemoryProperty.addListener(listener)
|
||||
precalledCommandProperty.addListener(listener)
|
||||
javaArgsProperty.addListener(listener)
|
||||
minecraftArgsProperty.addListener(listener)
|
||||
noJVMArgsProperty.addListener(listener)
|
||||
notCheckGameProperty.addListener(listener)
|
||||
serverIpProperty.addListener(listener)
|
||||
fullscreenProperty.addListener(listener)
|
||||
widthProperty.addListener(listener)
|
||||
heightProperty.addListener(listener)
|
||||
gameDirTypeProperty.addListener(listener)
|
||||
launcherVisibilityProperty.addListener(listener)
|
||||
}
|
||||
|
||||
companion object Serializer: JsonSerializer<VersionSetting>, JsonDeserializer<VersionSetting> {
|
||||
override fun serialize(src: VersionSetting?, typeOfSrc: Type?, context: JsonSerializationContext?): JsonElement {
|
||||
if (src == null) return JsonNull.INSTANCE
|
||||
val jsonObject = JsonObject()
|
||||
with(jsonObject) {
|
||||
addProperty("name", src.name)
|
||||
addProperty("usesGlobal", src.usesGlobal)
|
||||
addProperty("javaArgs", src.javaArgs)
|
||||
addProperty("minecraftArgs", src.minecraftArgs)
|
||||
@ -197,7 +210,6 @@ class VersionSetting() {
|
||||
if (json == null || json == JsonNull.INSTANCE || json !is JsonObject) return null
|
||||
|
||||
return VersionSetting().apply {
|
||||
name = json["name"]?.asString ?: ""
|
||||
usesGlobal = json["usesGlobal"]?.asBoolean ?: false
|
||||
javaArgs = json["javaArgs"]?.asString ?: ""
|
||||
minecraftArgs = json["minecraftArgs"]?.asString ?: ""
|
||||
|
@ -21,10 +21,17 @@ import com.jfoenix.controls.JFXButton
|
||||
import com.jfoenix.controls.JFXComboBox
|
||||
import com.jfoenix.controls.JFXListCell
|
||||
import com.jfoenix.controls.JFXListView
|
||||
import javafx.collections.FXCollections
|
||||
import javafx.fxml.FXML
|
||||
import javafx.scene.Node
|
||||
import javafx.scene.layout.Pane
|
||||
import javafx.scene.layout.StackPane
|
||||
import org.jackhuang.hmcl.ProfileChangedEvent
|
||||
import org.jackhuang.hmcl.ProfileLoadingEvent
|
||||
import org.jackhuang.hmcl.event.EVENT_BUS
|
||||
import org.jackhuang.hmcl.event.RefreshedVersionsEvent
|
||||
import org.jackhuang.hmcl.game.minecraftVersion
|
||||
import org.jackhuang.hmcl.setting.Settings
|
||||
import org.jackhuang.hmcl.setting.VersionSetting
|
||||
import org.jackhuang.hmcl.ui.animation.ContainerAnimations
|
||||
import org.jackhuang.hmcl.ui.download.DownloadWizardProvider
|
||||
@ -51,7 +58,7 @@ class MainController {
|
||||
*/
|
||||
@FXML lateinit var page: StackPane
|
||||
|
||||
@FXML lateinit var listVersions: JFXListView<VersionSetting> // TODO: JFXListView<Version> including icon, title, game version(if equals to title, hidden)
|
||||
@FXML lateinit var listVersions: JFXListView<VersionListItem> // TODO: JFXListView<Version> including icon, title, game version(if equals to title, hidden)
|
||||
|
||||
lateinit var animationHandler: TransitionHandler
|
||||
|
||||
@ -61,56 +68,23 @@ class MainController {
|
||||
|
||||
animationHandler = TransitionHandler(page)
|
||||
|
||||
listVersions.items.add(VersionSetting("1"))
|
||||
listVersions.items.add(VersionSetting("2"))
|
||||
listVersions.items.add(VersionSetting("3"))
|
||||
listVersions.items.add(VersionSetting("4"))
|
||||
listVersions.items.add(VersionSetting("5"))
|
||||
listVersions.items.add(VersionSetting("6"))
|
||||
listVersions.items.add(VersionSetting("7"))
|
||||
listVersions.items.add(VersionSetting("8"))
|
||||
listVersions.items.add(VersionSetting("9"))
|
||||
listVersions.items.add(VersionSetting("10"))
|
||||
listVersions.items.add(VersionSetting("11"))
|
||||
listVersions.items.add(VersionSetting("12"))
|
||||
|
||||
listVersions.setCellFactory {
|
||||
object : JFXListCell<VersionSetting>() {
|
||||
override fun updateItem(item: VersionSetting?, empty: Boolean) {
|
||||
super.updateItem(item, empty)
|
||||
|
||||
if (item == null || empty) return
|
||||
val g = VersionListItem(item, item.gameVersion)
|
||||
g.onSettingsButtonClicked {
|
||||
setContentPage(Controllers.versionPane)
|
||||
Controllers.versionController.loadVersionSetting(g.setting)
|
||||
}
|
||||
graphic = g
|
||||
}
|
||||
}
|
||||
}
|
||||
EVENT_BUS.channel<RefreshedVersionsEvent>() += this::loadVersions
|
||||
EVENT_BUS.channel<ProfileLoadingEvent>() += this::onProfilesLoading
|
||||
EVENT_BUS.channel<ProfileChangedEvent>() += this::onProfileChanged
|
||||
|
||||
listVersions.setOnMouseClicked {
|
||||
if (it.clickCount == 2) {
|
||||
setContentPage(Controllers.versionPane)
|
||||
Controllers.versionController.loadVersionSetting(listVersions.selectionModel.selectedItem)
|
||||
val id = listVersions.selectionModel.selectedItem.id
|
||||
|
||||
Controllers.versionController.loadVersionSetting(id, Settings.getLastProfile().getVersionSetting(id))
|
||||
} else
|
||||
it.consume()
|
||||
}
|
||||
|
||||
comboProfiles.items.add("SA")
|
||||
comboProfiles.items.add("SB")
|
||||
comboProfiles.items.add("SC")
|
||||
comboProfiles.items.add("SD")
|
||||
comboProfiles.items.add("SE")
|
||||
comboProfiles.items.add("SF")
|
||||
comboProfiles.items.add("SG")
|
||||
comboProfiles.items.add("SH")
|
||||
comboProfiles.items.add("SI")
|
||||
comboProfiles.items.add("SJ")
|
||||
comboProfiles.items.add("SK")
|
||||
|
||||
listVersions.smoothScrolling()
|
||||
|
||||
Settings.onProfileLoading()
|
||||
}
|
||||
|
||||
private val empty = Pane()
|
||||
@ -122,4 +96,34 @@ class MainController {
|
||||
fun installNewVersion() {
|
||||
setContentPage(Wizard.createWizard("Install New Game", DownloadWizardProvider()))
|
||||
}
|
||||
|
||||
fun onProfilesLoading() {
|
||||
// TODO: Profiles
|
||||
}
|
||||
|
||||
fun onProfileChanged(event: ProfileChangedEvent) {
|
||||
val profile = event.value
|
||||
profile.selectedVersionProperty.addListener { _, _, newValue ->
|
||||
versionChanged(newValue)
|
||||
}
|
||||
}
|
||||
|
||||
val versionListItems = mutableMapOf<String, VersionListItem>()
|
||||
|
||||
fun loadVersions() {
|
||||
val profile = Settings.getLastProfile()
|
||||
val list = mutableListOf<VersionListItem>()
|
||||
versionListItems.clear()
|
||||
profile.repository.getVersions().forEach {
|
||||
val item = VersionListItem(it.id, minecraftVersion(Settings.getLastProfile().repository.getVersionJar(it.id)) ?: "Unknown")
|
||||
list += item
|
||||
versionListItems += it.id to item
|
||||
}
|
||||
|
||||
listVersions.items = FXCollections.observableList(list)
|
||||
}
|
||||
|
||||
fun versionChanged(selectedVersion: String) {
|
||||
listVersions.selectionModel.select(versionListItems[selectedVersion])
|
||||
}
|
||||
}
|
@ -59,8 +59,8 @@ class VersionController {
|
||||
JFXScrollPane.smoothScrolling(scroll)
|
||||
}
|
||||
|
||||
fun loadVersionSetting(version: VersionSetting) {
|
||||
titleLabel.text = version.name
|
||||
fun loadVersionSetting(id: String, version: VersionSetting) {
|
||||
titleLabel.text = id
|
||||
}
|
||||
|
||||
fun onExploreJavaDir() {
|
||||
|
@ -22,7 +22,7 @@ import javafx.scene.control.Label
|
||||
import javafx.scene.layout.BorderPane
|
||||
import org.jackhuang.hmcl.setting.VersionSetting
|
||||
|
||||
class VersionListItem(val setting: VersionSetting, val gameVersion: String) : BorderPane() {
|
||||
class VersionListItem(val versionName: String, val gameVersion: String) : BorderPane() {
|
||||
|
||||
@FXML lateinit var lblVersionName: Label
|
||||
@FXML lateinit var lblGameVersion: Label
|
||||
@ -31,7 +31,7 @@ class VersionListItem(val setting: VersionSetting, val gameVersion: String) : Bo
|
||||
|
||||
init {
|
||||
loadFXML("/assets/fxml/version-list-item.fxml")
|
||||
lblVersionName.text = setting.name
|
||||
lblVersionName.text = versionName
|
||||
lblGameVersion.text = gameVersion
|
||||
}
|
||||
|
||||
|
@ -24,7 +24,7 @@ import org.jackhuang.hmcl.task.Task
|
||||
import org.jackhuang.hmcl.task.then
|
||||
import java.net.Proxy
|
||||
|
||||
class DefaultDependencyManager(override val repository: DefaultGameRepository, override val downloadProvider: DownloadProvider, val proxy: Proxy = Proxy.NO_PROXY)
|
||||
class DefaultDependencyManager(override val repository: DefaultGameRepository, override var downloadProvider: DownloadProvider, val proxy: Proxy = Proxy.NO_PROXY)
|
||||
: AbstractDependencyManager(repository) {
|
||||
|
||||
override fun gameBuilder(): GameBuilder = DefaultGameBuilder(this)
|
||||
|
@ -35,4 +35,6 @@ class EventBus {
|
||||
channel(obj.javaClass).fireEvent(obj)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
val EVENT_BUS = EventBus()
|
@ -17,30 +17,43 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.event
|
||||
|
||||
import org.jackhuang.hmcl.util.SimpleMultimap
|
||||
import java.util.*
|
||||
|
||||
class EventManager<T : EventObject> {
|
||||
private val handlers = EnumMap<EventPriority, MutableList<(T) -> Unit>>(EventPriority::class.java).apply {
|
||||
for (value in EventPriority.values())
|
||||
put(value, LinkedList<(T) -> Unit>())
|
||||
}
|
||||
private val handlers = SimpleMultimap<EventPriority, (T) -> Unit>({ EnumMap(EventPriority::class.java) }, ::HashSet)
|
||||
private val handlers2 = SimpleMultimap<EventPriority, () -> Unit>({ EnumMap(EventPriority::class.java) }, ::HashSet)
|
||||
|
||||
fun register(func: (T) -> Unit, priority: EventPriority = EventPriority.NORMAL) {
|
||||
if (!handlers[priority]!!.contains(func))
|
||||
handlers[priority]!!.add(func)
|
||||
if (!handlers[priority].contains(func))
|
||||
handlers.put(priority, func)
|
||||
}
|
||||
|
||||
fun register(func: () -> Unit, priority: EventPriority = EventPriority.NORMAL) {
|
||||
if (!handlers2[priority].contains(func))
|
||||
handlers2.put(priority, func)
|
||||
}
|
||||
|
||||
fun unregister(func: (T) -> Unit) {
|
||||
EventPriority.values().forEach { handlers[it]!!.remove(func) }
|
||||
handlers.remove(func)
|
||||
}
|
||||
|
||||
fun unregister(func: () -> Unit) {
|
||||
handlers2.remove(func)
|
||||
}
|
||||
|
||||
fun fireEvent(event: T) {
|
||||
for (priority in EventPriority.values())
|
||||
for (handler in handlers[priority]!!)
|
||||
for (priority in EventPriority.values()) {
|
||||
for (handler in handlers[priority])
|
||||
handler(event)
|
||||
for (handler in handlers2[priority])
|
||||
handler()
|
||||
}
|
||||
}
|
||||
|
||||
operator fun plusAssign(func: (T) -> Unit) = register(func)
|
||||
operator fun plusAssign(func: () -> Unit) = register(func)
|
||||
operator fun minusAssign(func: (T) -> Unit) = unregister(func)
|
||||
operator fun minusAssign(func: () -> Unit) = unregister(func)
|
||||
operator fun invoke(event: T) = fireEvent(event)
|
||||
}
|
51
HMCLCore/src/main/java/org/jackhuang/hmcl/event/Events.kt
Normal file
51
HMCLCore/src/main/java/org/jackhuang/hmcl/event/Events.kt
Normal file
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||
*/
|
||||
package org.jackhuang.hmcl.event
|
||||
|
||||
import java.util.EventObject
|
||||
|
||||
/**
|
||||
* This event gets fired when loading versions in a .minecraft folder.
|
||||
* <br></br>
|
||||
* This event is fired on the [org.jackhuang.hmcl.api.HMCLApi.EVENT_BUS]
|
||||
* @param source [org.jackhuang.hmcl.core.version.MinecraftVersionManager]
|
||||
* *
|
||||
* @param IMinecraftService .minecraft folder.
|
||||
* *
|
||||
* @author huangyuhui
|
||||
*/
|
||||
class RefreshingVersionsEvent(source: Any) : EventObject(source)
|
||||
|
||||
/**
|
||||
* This event gets fired when all the versions in .minecraft folder are loaded.
|
||||
* <br>
|
||||
* This event is fired on the {@link org.jackhuang.hmcl.api.HMCLApi#EVENT_BUS}
|
||||
* @param source [org.jackhuang.hmcl.game.GameRepository]
|
||||
* @author huangyuhui
|
||||
*/
|
||||
class RefreshedVersionsEvent(source: Any) : EventObject(source)
|
||||
|
||||
/**
|
||||
* This event gets fired when a minecraft version has been loaded.
|
||||
* <br></br>
|
||||
* This event is fired on the [org.jackhuang.hmcl.api.HMCLApi.EVENT_BUS]
|
||||
* @param source [org.jackhuang.hmcl.core.version.MinecraftVersionManager]
|
||||
* @param version the version id.
|
||||
* @author huangyuhui
|
||||
*/
|
||||
class LoadedOneVersionEvent(source: Any, val version: String) : EventObject(source)
|
@ -18,6 +18,7 @@
|
||||
package org.jackhuang.hmcl.game
|
||||
|
||||
import com.google.gson.JsonSyntaxException
|
||||
import org.jackhuang.hmcl.event.*
|
||||
import org.jackhuang.hmcl.util.GSON
|
||||
import org.jackhuang.hmcl.util.LOG
|
||||
import org.jackhuang.hmcl.util.fromJson
|
||||
@ -27,7 +28,7 @@ import java.io.IOException
|
||||
import java.util.*
|
||||
import java.util.logging.Level
|
||||
|
||||
open class DefaultGameRepository(val baseDirectory: File): GameRepository {
|
||||
open class DefaultGameRepository(var baseDirectory: File): GameRepository {
|
||||
protected val versions: MutableMap<String, Version> = TreeMap<String, Version>()
|
||||
|
||||
override fun hasVersion(id: String) = versions.containsKey(id)
|
||||
@ -81,9 +82,8 @@ open class DefaultGameRepository(val baseDirectory: File): GameRepository {
|
||||
return file.deleteRecursively()
|
||||
}
|
||||
|
||||
protected open fun refreshVersionsImpl() {
|
||||
|
||||
@Synchronized
|
||||
override fun refreshVersions() {
|
||||
versions.clear()
|
||||
|
||||
if (ClassicVersion.hasClassicVersion(baseDirectory)) {
|
||||
@ -123,7 +123,16 @@ open class DefaultGameRepository(val baseDirectory: File): GameRepository {
|
||||
}
|
||||
|
||||
versions[id] = version
|
||||
EVENT_BUS.fireEvent(LoadedOneVersionEvent(this, id))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
final override fun refreshVersions() {
|
||||
EVENT_BUS.fireEvent(RefreshingVersionsEvent(this))
|
||||
refreshVersionsImpl()
|
||||
EVENT_BUS.fireEvent(RefreshedVersionsEvent(this))
|
||||
}
|
||||
|
||||
override fun getAssetIndex(assetId: String): AssetIndex {
|
||||
|
@ -95,6 +95,14 @@ interface GameRepository : VersionProvider {
|
||||
*/
|
||||
fun getVersionJar(version: Version): File
|
||||
|
||||
/**
|
||||
* Get minecraft jar
|
||||
*
|
||||
* @param version version id
|
||||
* @return the minecraft jar
|
||||
*/
|
||||
fun getVersionJar(version: String): File = getVersionJar(getVersion(version).resolve(this))
|
||||
|
||||
/**
|
||||
* Rename given version to new name.
|
||||
*
|
||||
|
139
HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameVersion.kt
Normal file
139
HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameVersion.kt
Normal file
@ -0,0 +1,139 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||
*/
|
||||
package org.jackhuang.hmcl.game
|
||||
|
||||
import org.jackhuang.hmcl.util.closeQuietly
|
||||
import org.jackhuang.hmcl.util.readFullyAsByteArray
|
||||
import java.io.IOException
|
||||
import java.util.zip.ZipEntry
|
||||
import java.util.zip.ZipFile
|
||||
import java.io.File
|
||||
|
||||
private fun lessThan32(b: ByteArray, x: Int): Int {
|
||||
var x = x
|
||||
while (x < b.size) {
|
||||
if (b[x] < 32)
|
||||
return x
|
||||
x++
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
fun matchArray(a: ByteArray, b: ByteArray): Int {
|
||||
for (i in 0..a.size - b.size - 1) {
|
||||
var j = 1
|
||||
for (k in b.indices) {
|
||||
if (b[k] == a[i + k])
|
||||
continue
|
||||
j = 0
|
||||
break
|
||||
}
|
||||
if (j != 0)
|
||||
return i
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
private fun getVersionOfOldMinecraft(file: ZipFile, entry: ZipEntry): String? {
|
||||
val tmp = file.getInputStream(entry).readFullyAsByteArray()
|
||||
|
||||
val bytes = "Minecraft Minecraft ".toByteArray(Charsets.US_ASCII)
|
||||
var j = matchArray(tmp, bytes)
|
||||
if (j < 0) {
|
||||
return null
|
||||
}
|
||||
val i = j + bytes.size
|
||||
j = lessThan32(tmp, i)
|
||||
|
||||
if (j < 0) {
|
||||
return null
|
||||
}
|
||||
val ver = String(tmp, i, j - i, Charsets.US_ASCII)
|
||||
return ver
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
private fun getVersionOfNewMinecraft(file: ZipFile, entry: ZipEntry): String? {
|
||||
val tmp = file.getInputStream(entry).readFullyAsByteArray()
|
||||
|
||||
var str = "-server.txt".toByteArray(charset("ASCII"))
|
||||
var j = matchArray(tmp, str)
|
||||
if (j < 0) {
|
||||
return null
|
||||
}
|
||||
var i = j + str.size
|
||||
i += 11
|
||||
j = lessThan32(tmp, i)
|
||||
if (j < 0) {
|
||||
return null
|
||||
}
|
||||
val result = String(tmp, i, j - i, Charsets.US_ASCII)
|
||||
|
||||
val ch = result[0]
|
||||
// 1.8.1+
|
||||
if (ch < '0' || ch > '9') {
|
||||
str = "Can't keep up! Did the system time change, or is the server overloaded?".toByteArray(charset("ASCII"))
|
||||
j = matchArray(tmp, str)
|
||||
if (j < 0) {
|
||||
return null
|
||||
}
|
||||
i = -1
|
||||
while (j > 0) {
|
||||
if (tmp[j] in 48..57) {
|
||||
i = j
|
||||
break
|
||||
}
|
||||
j--
|
||||
}
|
||||
if (i == -1) {
|
||||
return null
|
||||
}
|
||||
var k = i
|
||||
if (tmp[i + 1] >= 'a'.toInt() && tmp[i + 1] <= 'z'.toInt())
|
||||
i++
|
||||
while (tmp[k] in 48..57 || tmp[k] == '-'.toByte() || tmp[k] == '.'.toByte() || tmp[k] >= 97 && tmp[k] <= 'z'.toByte())
|
||||
k--
|
||||
k++
|
||||
return String(tmp, k, i - k + 1, Charsets.US_ASCII)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
fun minecraftVersion(file: File?): String? {
|
||||
if (file == null || !file.isFile || !file.canRead()) {
|
||||
return null
|
||||
}
|
||||
var f: ZipFile? = null
|
||||
try {
|
||||
f = ZipFile(file)
|
||||
val minecraft = f
|
||||
.getEntry("net/minecraft/client/Minecraft.class")
|
||||
if (minecraft != null)
|
||||
return getVersionOfOldMinecraft(f, minecraft)
|
||||
val main = f.getEntry("net/minecraft/client/main/Main.class")
|
||||
val minecraftserver = f.getEntry("net/minecraft/server/MinecraftServer.class")
|
||||
if (main != null && minecraftserver != null)
|
||||
return getVersionOfNewMinecraft(f, minecraftserver)
|
||||
return null
|
||||
} catch (e: IOException) {
|
||||
return null
|
||||
} finally {
|
||||
f?.closeQuietly()
|
||||
}
|
||||
}
|
@ -28,6 +28,7 @@ import com.google.gson.TypeAdapter
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.TypeAdapterFactory
|
||||
import org.jackhuang.hmcl.game.Library
|
||||
import java.io.File
|
||||
import java.text.DateFormat
|
||||
import java.text.ParseException
|
||||
import java.text.SimpleDateFormat
|
||||
@ -40,6 +41,8 @@ val GSON: Gson = GsonBuilder()
|
||||
.registerTypeAdapter(Library::class.java, Library)
|
||||
.registerTypeAdapter(Date::class.java, DateTypeAdapter)
|
||||
.registerTypeAdapter(UUID::class.java, UUIDTypeAdapter)
|
||||
.registerTypeAdapter(Platform::class.java, Platform)
|
||||
.registerTypeAdapter(File::class.java, FileTypeAdapter)
|
||||
.registerTypeAdapterFactory(ValidationTypeAdapterFactory)
|
||||
.registerTypeAdapterFactory(LowerCaseEnumTypeAdapterFactory)
|
||||
.create()
|
||||
@ -48,6 +51,14 @@ inline fun <reified T> typeOf(): Type = object : TypeToken<T>() {}.type
|
||||
|
||||
inline fun <reified T> Gson.fromJson(json: String): T? = fromJson<T>(json, T::class.java)
|
||||
|
||||
inline fun <reified T> Gson.fromJsonQuietly(json: String): T? {
|
||||
try {
|
||||
return fromJson<T>(json)
|
||||
} catch (json: JsonParseException) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the json object's fields automatically filled by Gson are in right format.
|
||||
*/
|
||||
@ -189,4 +200,17 @@ object DateTypeAdapter : JsonSerializer<Date>, JsonDeserializer<Date> {
|
||||
return result.substring(0, 22) + ":" + result.substring(22)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object FileTypeAdapter : JsonSerializer<File>, JsonDeserializer<File> {
|
||||
override fun serialize(src: File?, typeOfSrc: Type?, context: JsonSerializationContext?): JsonElement {
|
||||
if (src == null) return JsonNull.INSTANCE
|
||||
else return JsonPrimitive(src.path)
|
||||
}
|
||||
|
||||
override fun deserialize(json: JsonElement?, typeOfT: Type?, context: JsonDeserializationContext?): File? {
|
||||
if (json == null) return null
|
||||
else return File(json.asString)
|
||||
}
|
||||
|
||||
}
|
@ -17,12 +17,14 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.util
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.io.Serializable
|
||||
import java.util.regex.Pattern
|
||||
|
||||
data class JavaVersion internal constructor(
|
||||
@SerializedName("location")
|
||||
val binary: File,
|
||||
val version: Int,
|
||||
val platform: Platform) : Serializable
|
||||
@ -74,8 +76,8 @@ data class JavaVersion internal constructor(
|
||||
}
|
||||
|
||||
fun getJavaFile(home: File): File {
|
||||
var path = home.resolve("bin")
|
||||
var javaw = path.resolve("javaw.exe")
|
||||
val path = home.resolve("bin")
|
||||
val javaw = path.resolve("javaw.exe")
|
||||
if (OS.CURRENT_OS === OS.WINDOWS && javaw.isFile)
|
||||
return javaw
|
||||
else
|
||||
|
@ -17,12 +17,33 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.util
|
||||
|
||||
import com.google.gson.*
|
||||
import java.lang.reflect.Type
|
||||
|
||||
enum class Platform(val bit: String) {
|
||||
BIT_32("32"),
|
||||
BIT_64("64"),
|
||||
UNKNOWN("unknown");
|
||||
|
||||
companion object {
|
||||
companion object Serializer: JsonSerializer<Platform>, JsonDeserializer<Platform> {
|
||||
|
||||
override fun deserialize(json: JsonElement?, typeOfT: Type?, context: JsonDeserializationContext?): Platform? {
|
||||
if (json == null) return null
|
||||
return when (json.asInt) {
|
||||
0 -> BIT_32
|
||||
1 -> BIT_64
|
||||
else -> UNKNOWN
|
||||
}
|
||||
}
|
||||
|
||||
override fun serialize(src: Platform?, typeOfSrc: Type?, context: JsonSerializationContext?): JsonElement? {
|
||||
if (src == null) return null
|
||||
return when (src) {
|
||||
BIT_32 -> JsonPrimitive(0)
|
||||
BIT_64 -> JsonPrimitive(1)
|
||||
UNKNOWN -> JsonPrimitive(-1)
|
||||
}
|
||||
}
|
||||
|
||||
val PLATFORM: Platform by lazy {
|
||||
if (IS_64_BIT) BIT_64 else BIT_32
|
||||
|
@ -40,13 +40,19 @@ class SimpleMultimap<K, V>(val maper: () -> MutableMap<K, Collection<V>>, val va
|
||||
valuesImpl += value
|
||||
}
|
||||
|
||||
fun remove(key: K): Collection<V>? {
|
||||
fun removeAll(key: K): Collection<V>? {
|
||||
val result = map.remove(key)
|
||||
if (result != null)
|
||||
valuesImpl.removeAll(result)
|
||||
return result
|
||||
}
|
||||
|
||||
fun remove(value: V) {
|
||||
map.values.forEach {
|
||||
it.remove(value)
|
||||
}
|
||||
}
|
||||
|
||||
fun clear() {
|
||||
map.clear()
|
||||
valuesImpl.clear()
|
||||
|
Loading…
Reference in New Issue
Block a user