UI works
This commit is contained in:
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user