Able to launch game now
This commit is contained in:
@@ -24,5 +24,5 @@ abstract class Account() {
|
||||
@Throws(AuthenticationException::class)
|
||||
abstract fun logIn(proxy: Proxy = Proxy.NO_PROXY): AuthInfo
|
||||
abstract fun logOut()
|
||||
abstract fun toStorage(): Map<out Any, Any>
|
||||
abstract fun toStorage(): MutableMap<Any, Any>
|
||||
}
|
||||
27
HMCLCore/src/main/java/org/jackhuang/hmcl/auth/Accounts.kt
Normal file
27
HMCLCore/src/main/java/org/jackhuang/hmcl/auth/Accounts.kt
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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.auth
|
||||
|
||||
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount
|
||||
|
||||
object Accounts {
|
||||
val ACCOUNTS = mapOf(
|
||||
"offline" to OfflineAccount,
|
||||
"yggdrasil" to YggdrasilAccount
|
||||
)
|
||||
}
|
||||
@@ -35,13 +35,15 @@ class OfflineAccount private constructor(val uuid: String, override val username
|
||||
// Offline account need not log out.
|
||||
}
|
||||
|
||||
override fun toStorage(): Map<Any, Any> {
|
||||
return mapOf(
|
||||
override fun toStorage(): MutableMap<Any, Any> {
|
||||
return mutableMapOf(
|
||||
"uuid" to uuid,
|
||||
"username" to username
|
||||
)
|
||||
}
|
||||
|
||||
override fun toString() = "OfflineAccount[username=$username,uuid=$uuid]"
|
||||
|
||||
companion object OfflineAccountFactory : AccountFactory<OfflineAccount> {
|
||||
|
||||
override fun fromUsername(username: String, password: String): OfflineAccount {
|
||||
|
||||
@@ -135,8 +135,8 @@ class YggdrasilAccount private constructor(override val username: String): Accou
|
||||
selectedProfile = null
|
||||
}
|
||||
|
||||
override fun toStorage(): Map<out Any, Any> {
|
||||
val result = HashMap<String, Any>()
|
||||
override fun toStorage(): MutableMap<Any, Any> {
|
||||
val result = HashMap<Any, Any>()
|
||||
|
||||
result[STORAGE_KEY_USER_NAME] = username
|
||||
if (userId != null)
|
||||
@@ -189,6 +189,8 @@ class YggdrasilAccount private constructor(override val username: String): Accou
|
||||
}
|
||||
}
|
||||
|
||||
override fun toString() = "YggdrasilAccount[username=$username]"
|
||||
|
||||
companion object YggdrasilAccountFactory : AccountFactory<YggdrasilAccount> {
|
||||
private val GSON = GsonBuilder()
|
||||
.registerTypeAdapter(GameProfile::class.java, GameProfile)
|
||||
|
||||
@@ -19,6 +19,7 @@ package org.jackhuang.hmcl.launch
|
||||
|
||||
import org.jackhuang.hmcl.auth.AuthInfo
|
||||
import org.jackhuang.hmcl.game.*
|
||||
import org.jackhuang.hmcl.task.TaskResult
|
||||
import org.jackhuang.hmcl.util.*
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
@@ -26,12 +27,12 @@ import java.util.*
|
||||
import kotlin.concurrent.thread
|
||||
|
||||
/**
|
||||
* @param version A resolved version(calling [Version.resolve])
|
||||
* @param versionId The version to be launched.
|
||||
* @param account The user account
|
||||
* @param options The launching configuration
|
||||
*/
|
||||
open class DefaultLauncher(repository: GameRepository, version: Version, account: AuthInfo, options: LaunchOptions, listener: ProcessListener? = null, isDaemon: Boolean = true)
|
||||
: Launcher(repository, version, account, options, listener, isDaemon) {
|
||||
open class DefaultLauncher(repository: GameRepository, versionId: String, account: AuthInfo, options: LaunchOptions, listener: ProcessListener? = null, isDaemon: Boolean = true)
|
||||
: Launcher(repository, versionId, account, options, listener, isDaemon) {
|
||||
|
||||
protected val native: File by lazy { repository.getNativeDirectory(version.id) }
|
||||
|
||||
@@ -234,7 +235,7 @@ open class DefaultLauncher(repository: GameRepository, version: Version, account
|
||||
}
|
||||
|
||||
builder.directory(repository.getRunDirectory(version.id))
|
||||
.environment().put("APPDATA", options.gameDir.parent)
|
||||
.environment().put("APPDATA", options.gameDir.absoluteFile.parent)
|
||||
val p = JavaProcess(builder.start(), rawCommandLine)
|
||||
if (listener == null)
|
||||
startMonitors(p)
|
||||
@@ -243,6 +244,14 @@ open class DefaultLauncher(repository: GameRepository, version: Version, account
|
||||
return p
|
||||
}
|
||||
|
||||
fun launchAsync(): TaskResult<JavaProcess> {
|
||||
return object : TaskResult<JavaProcess>() {
|
||||
override fun execute() {
|
||||
result = launch()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun makeLaunchScript(file: String): File {
|
||||
val isWindows = OS.WINDOWS == OS.CURRENT_OS
|
||||
val scriptFile = File(file + (if (isWindows) ".bat" else ".sh"))
|
||||
|
||||
@@ -26,12 +26,13 @@ import java.io.File
|
||||
|
||||
abstract class Launcher(
|
||||
protected val repository: GameRepository,
|
||||
protected val version: Version,
|
||||
protected val versionId: String,
|
||||
protected val account: AuthInfo,
|
||||
protected val options: LaunchOptions,
|
||||
protected val listener: ProcessListener? = null,
|
||||
protected val isDaemon: Boolean = true) {
|
||||
|
||||
val version: Version = repository.getVersion(versionId).resolve(repository)
|
||||
abstract val rawCommandLine: List<String>
|
||||
abstract fun launch(): JavaProcess
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.task
|
||||
|
||||
private class CoupleTask<P: Task>(private val pred: P, private val succ: Task.(P) -> Task?, override val reliant: Boolean) : Task() {
|
||||
internal class CoupleTask<P: Task>(private val pred: P, private val succ: Task.(P) -> Task?, override val reliant: Boolean) : Task() {
|
||||
override val hidden: Boolean = true
|
||||
|
||||
override val dependents: Collection<Task> = listOf(pred)
|
||||
@@ -30,11 +30,12 @@ private class CoupleTask<P: Task>(private val pred: P, private val succ: Task.(P
|
||||
}
|
||||
}
|
||||
|
||||
infix fun Task.then(b: Task): Task = CoupleTask(this, { b }, true)
|
||||
|
||||
/**
|
||||
* @param b A runnable that decides what to do next, You can also do something here.
|
||||
*/
|
||||
infix fun <T: Task> T.then(b: Task.(T) -> Task?): Task = CoupleTask(this, b, true)
|
||||
|
||||
infix fun Task.with(b: Task): Task = CoupleTask(this, { b }, false)
|
||||
/**
|
||||
* @param b A runnable that decides what to do next, You can also do something here.
|
||||
*/
|
||||
infix fun <T: Task> T.with(b: Task.(T) -> Task?): Task = CoupleTask(this, b, false)
|
||||
@@ -23,16 +23,17 @@ import java.util.concurrent.atomic.AtomicReference
|
||||
import javax.swing.SwingUtilities
|
||||
|
||||
interface Scheduler {
|
||||
fun schedule(block: () -> Unit): Future<*>? = schedule(Callable { block() })
|
||||
fun schedule(block: Callable<Unit>): Future<*>?
|
||||
|
||||
companion object Schedulers {
|
||||
val JAVAFX: Scheduler = SchedulerImpl(Platform::runLater)
|
||||
val SWING: Scheduler = SchedulerImpl(SwingUtilities::invokeLater)
|
||||
private class SchedulerImpl(val executor: (() -> Unit) -> Unit) : Scheduler {
|
||||
private class SchedulerImpl(val executor: (Runnable) -> Unit) : Scheduler {
|
||||
override fun schedule(block: Callable<Unit>): Future<*>? {
|
||||
val latch = CountDownLatch(1)
|
||||
val wrapper = AtomicReference<Exception>()
|
||||
executor {
|
||||
executor.invoke(Runnable {
|
||||
try {
|
||||
block.call()
|
||||
} catch (e: Exception) {
|
||||
@@ -40,7 +41,7 @@ interface Scheduler {
|
||||
} finally {
|
||||
latch.countDown()
|
||||
}
|
||||
}
|
||||
})
|
||||
return object : Future<Unit> {
|
||||
override fun get(timeout: Long, unit: TimeUnit) {
|
||||
latch.await(timeout, unit)
|
||||
@@ -66,7 +67,9 @@ interface Scheduler {
|
||||
}
|
||||
val DEFAULT = NEW_THREAD
|
||||
private val CACHED_EXECUTOR: ExecutorService by lazy {
|
||||
Executors.newCachedThreadPool()
|
||||
ThreadPoolExecutor(0, Integer.MAX_VALUE,
|
||||
60L, TimeUnit.SECONDS,
|
||||
SynchronousQueue<Runnable>());
|
||||
}
|
||||
|
||||
private val IO_EXECUTOR: ExecutorService by lazy {
|
||||
|
||||
@@ -57,6 +57,8 @@ abstract class Task {
|
||||
abstract fun execute()
|
||||
|
||||
infix fun parallel(couple: Task): Task = ParallelTask(this, couple)
|
||||
infix fun then(b: Task): Task = CoupleTask(this, { b }, true)
|
||||
infix fun with(b: Task): Task = CoupleTask(this, { b }, false)
|
||||
|
||||
/**
|
||||
* The collection of sub-tasks that should execute before this task running.
|
||||
@@ -112,18 +114,18 @@ abstract class Task {
|
||||
|
||||
fun executor() = TaskExecutor().submit(this)
|
||||
|
||||
fun subscribe(subscriber: Task) {
|
||||
executor().submit(subscriber).start()
|
||||
fun subscribe(subscriber: Task) = executor().apply {
|
||||
submit(subscriber).start()
|
||||
}
|
||||
|
||||
fun subscribe(scheduler: Scheduler = Scheduler.DEFAULT, closure: () -> Unit) = subscribe(Task.of(closure, scheduler))
|
||||
fun subscribe(scheduler: Scheduler = Scheduler.DEFAULT, closure: () -> Unit) = subscribe(Task.of(scheduler, closure))
|
||||
|
||||
override fun toString(): String {
|
||||
return title
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun of(closure: () -> Unit, scheduler: Scheduler = Scheduler.DEFAULT): Task = SimpleTask(closure, scheduler)
|
||||
fun of(scheduler: Scheduler = Scheduler.DEFAULT, closure: () -> Unit): Task = SimpleTask(closure, scheduler)
|
||||
fun <V> of(callable: Callable<V>): TaskResult<V> = TaskCallable(callable)
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,7 @@ class TaskExecutor() {
|
||||
|
||||
var canceled = false
|
||||
private set
|
||||
private val totTask = AtomicInteger(0)
|
||||
val totTask = AtomicInteger(0)
|
||||
private val taskQueue = ConcurrentLinkedQueue<Task>()
|
||||
private val workerQueue = ConcurrentLinkedQueue<Future<*>>()
|
||||
|
||||
@@ -47,9 +47,10 @@ class TaskExecutor() {
|
||||
* Start the subscription and run all registered tasks asynchronously.
|
||||
*/
|
||||
fun start() {
|
||||
thread {
|
||||
workerQueue.add(Scheduler.Schedulers.NEW_THREAD.schedule(Callable {
|
||||
totTask.addAndGet(taskQueue.size)
|
||||
while (!taskQueue.isEmpty() && !canceled) {
|
||||
while (!taskQueue.isEmpty()) {
|
||||
if (canceled) break
|
||||
val task = taskQueue.poll()
|
||||
if (task != null) {
|
||||
val future = task.scheduler.schedule(Callable { executeTask(task); Unit })
|
||||
@@ -61,9 +62,9 @@ class TaskExecutor() {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!canceled)
|
||||
if (canceled || Thread.interrupted())
|
||||
taskListener?.onTerminate()
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -91,9 +92,11 @@ class TaskExecutor() {
|
||||
if (future != null)
|
||||
workerQueue.add(future)
|
||||
}
|
||||
if (canceled)
|
||||
return false
|
||||
try {
|
||||
counter.await()
|
||||
return success.get()
|
||||
return success.get() && !canceled
|
||||
} catch (e: InterruptedException) {
|
||||
Thread.currentThread().interrupt()
|
||||
// Once interrupted, we are aborting the subscription.
|
||||
@@ -110,9 +113,10 @@ class TaskExecutor() {
|
||||
LOG.fine("Executing task: ${t.title}")
|
||||
taskListener?.onReady(t)
|
||||
val doDependentsSucceeded = executeTasks(t.dependents)
|
||||
|
||||
var flag = false
|
||||
try {
|
||||
if (!doDependentsSucceeded && t.reliant)
|
||||
if (!doDependentsSucceeded && t.reliant || canceled)
|
||||
throw SilentException()
|
||||
|
||||
t.execute()
|
||||
|
||||
@@ -52,6 +52,7 @@ data class JavaVersion internal constructor(
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
fun fromExecutable(file: File): JavaVersion {
|
||||
var platform = Platform.BIT_32
|
||||
var version: String? = null
|
||||
@@ -66,6 +67,7 @@ data class JavaVersion internal constructor(
|
||||
platform = Platform.BIT_64
|
||||
}
|
||||
} catch (e: InterruptedException) {
|
||||
Thread.currentThread().interrupt()
|
||||
throw IOException("Java process is interrupted", e)
|
||||
}
|
||||
val thisVersion = version ?: throw IOException("Java version not matched")
|
||||
|
||||
@@ -49,9 +49,9 @@ enum class OS {
|
||||
ReflectionHelper.get<Long>(ManagementFactory.getOperatingSystemMXBean(), "getTotalPhysicalMemorySize") ?: 1024L
|
||||
}
|
||||
|
||||
val SUGGESTED_MEMORY: Long by lazy {
|
||||
val SUGGESTED_MEMORY: Int by lazy {
|
||||
val memory = TOTAL_MEMORY / 1024 / 1024 / 4
|
||||
Math.round(1.0 * memory / 128.0) * 128
|
||||
(Math.round(1.0 * memory / 128.0) * 128).toInt()
|
||||
}
|
||||
|
||||
val PATH_SEPARATOR: String = File.pathSeparator
|
||||
|
||||
@@ -19,7 +19,7 @@ package org.jackhuang.hmcl
|
||||
|
||||
import org.jackhuang.hmcl.auth.OfflineAccount
|
||||
import org.jackhuang.hmcl.download.DefaultDependencyManager
|
||||
import org.jackhuang.hmcl.download.liteloader.LiteLoaderVersionList
|
||||
import org.jackhuang.hmcl.download.LiteLoaderVersionList
|
||||
import org.jackhuang.hmcl.download.BMCLAPIDownloadProvider
|
||||
import org.jackhuang.hmcl.download.MojangDownloadProvider
|
||||
import org.jackhuang.hmcl.game.DefaultGameRepository
|
||||
@@ -50,7 +50,7 @@ class Test {
|
||||
fun launch() {
|
||||
val launcher = DefaultLauncher(
|
||||
repository = repository,
|
||||
version = repository.getVersion("test"),
|
||||
versionId = "test",
|
||||
account = OfflineAccount.fromUsername("player007").logIn(),
|
||||
options = LaunchOptions(gameDir = repository.baseDirectory),
|
||||
listener = object : ProcessListener {
|
||||
|
||||
Reference in New Issue
Block a user