Refactor account login handling into the AccountsSubsystem

Now we have a cool way of doing the startup login instead of dealing
with funky environment variables
This commit is contained in:
Charlotte Som 2022-03-08 01:00:00 +00:00
parent 81ef894926
commit ca66d553bd
3 changed files with 129 additions and 87 deletions

View file

@ -13,6 +13,8 @@ import codes.som.hibiscus.features.player.NoFallDamage
import codes.som.hibiscus.features.player.NoSprintingPacket
import codes.som.hibiscus.gui.screens.AccountManagerUIScreen
import codes.som.hibiscus.gui.screens.InGameClientUIScreen
import codes.som.hibiscus.mixins.MixinExtMinecraftClient
import codes.som.hibiscus.subsystems.accounts.AccountsSubsystem
import codes.som.hibiscus.util.command.ChatCommandListener
import codes.som.hibiscus.util.input.KeybindDispatcher
import codes.som.hibiscus.util.netmoving.NetworkMovingDispatcher
@ -56,6 +58,18 @@ object Hibiscus : ModInitializer {
bus.register(KeybindDispatcher())
defaultConfig()
AccountsSubsystem.reload()
if (mc.session.accessToken == "FabricMC") { // Launched from IDE
try {
AccountsSubsystem.getAccounts().firstOrNull()?.let { account ->
AccountsSubsystem.signIn(account)
?.let(AccountsSubsystem::createSession)
?.let((mc as MixinExtMinecraftClient)::setSession)
}
} catch (_: Exception) {
}
}
}
fun shutdown() {

View file

@ -1,8 +1,8 @@
package codes.som.hibiscus.gui.screens
import codes.som.hibiscus.Hibiscus
import codes.som.hibiscus.data.MinecraftAccount
import codes.som.hibiscus.mc
import codes.som.hibiscus.mixins.MixinExtMinecraftClient
import codes.som.hibiscus.subsystems.accounts.AccountsSubsystem
import imgui.ImGui
import imgui.flag.ImGuiInputTextFlags
import imgui.flag.ImGuiWindowFlags
@ -10,30 +10,14 @@ import imgui.type.ImBoolean
import imgui.type.ImInt
import imgui.type.ImString
import net.hycrafthd.minecraft_authenticator.login.Authenticator
import net.hycrafthd.minecraft_authenticator.login.User
import net.hycrafthd.minecraft_authenticator.login.file.AuthenticationFile
import net.minecraft.client.gui.screen.Screen
import net.minecraft.client.util.Session
import net.minecraft.client.util.math.MatrixStack
import net.minecraft.text.Text
import java.io.ByteArrayOutputStream
import java.util.*
object AccountManagerUIScreen : Screen(Text.of("account management hacker menu")) {
private var accounts = emptyList<MinecraftAccount>()
private val createAccountPanelOpen = ImBoolean(false)
private var selectedAccountId = -1
init {
refreshAccounts()
}
private fun refreshAccounts() {
this.accounts = Hibiscus.data.txn {
MinecraftAccount.all().toList()
}
}
override fun render(matrices: MatrixStack?, mouseX: Int, mouseY: Int, delta: Float) {
renderBackground(matrices)
@ -42,13 +26,16 @@ object AccountManagerUIScreen : Screen(Text.of("account management hacker menu")
}
private fun drawAccountsPanel() {
val accounts = AccountsSubsystem.getAccounts().toList()
if (ImGui.begin("Accounts", ImGuiWindowFlags.AlwaysAutoResize)) {
ImGui.text("Available accounts: ${accounts.size}")
ImGui.text("Logged in as: ${client!!.session.username}")
ImGui.text("Logged in as: ${mc.session.username}")
if (ImGui.button("Add Account")) {
createAccountPanelOpen.set(true)
}
ImGui.sameLine()
if (ImGui.button("Random")) {
selectedAccountId = accounts
@ -70,11 +57,18 @@ object AccountManagerUIScreen : Screen(Text.of("account management hacker menu")
accounts.firstOrNull { it.id.value == selectedAccountId }?.let { account ->
if (ImGui.button("Log In")) {
signInToAccount(account)
try {
val user = AccountsSubsystem.signIn(account)
if (user != null) {
(mc as MixinExtMinecraftClient).setSession(AccountsSubsystem.createSession(user))
}
} catch (e: Exception) {
e.printStackTrace()
}
}
ImGui.sameLine()
if (ImGui.button("Remove")) {
removeAccount(account)
AccountsSubsystem.deleteAccount(account)
}
ImGui.text("Selected account: ${account.name}")
@ -86,36 +80,6 @@ object AccountManagerUIScreen : Screen(Text.of("account management hacker menu")
ImGui.end()
}
private fun signInToAccount(account: MinecraftAccount) {
try {
val loginFile = AuthenticationFile.read(account.loginData.byteInputStream(Charsets.UTF_8))
val loginResult = Authenticator.of(loginFile).shouldAuthenticate().run()
if (loginResult.user.isPresent) {
val user = loginResult.user.get()
(client!! as MixinExtMinecraftClient).setSession(user.createSession())
val loginData = ByteArrayOutputStream()
.apply { loginResult.resultFile.write(this) }
.toString(Charsets.UTF_8)
Hibiscus.data.txn {
account.lastKnownDisplayName = user.name
account.loginData = loginData
refreshAccounts()
}
}
} catch (e: Exception) {
e.printStackTrace()
}
}
private fun removeAccount(account: MinecraftAccount) {
Hibiscus.data.txn {
account.delete()
refreshAccounts()
}
}
private val loginType = ImInt(1)
private val yggdrasilUsername = ImString(320)
private val yggdrasilPassword = ImString(256)
@ -161,35 +125,13 @@ object AccountManagerUIScreen : Screen(Text.of("account management hacker menu")
0 -> Authenticator.ofYggdrasil("-", yggdrasilUsername.get(), yggdrasilPassword.get())
1 -> Authenticator.ofMicrosoft(microsoftToken.get())
else -> throw IllegalStateException()
}.shouldAuthenticate()
}
try {
val loginResult = authenticator.run()
if (loginResult.user.isPresent) {
val user = loginResult.user.get()
yggdrasilUsername.set("")
yggdrasilPassword.set("")
microsoftToken.set("")
val loginData = ByteArrayOutputStream()
.apply { loginResult.resultFile.write(this) }
.toString(Charsets.UTF_8)
Hibiscus.data.txn {
MinecraftAccount.new {
this.name = user.name
this.lastKnownDisplayName = user.name
this.loginData = loginData
}
refreshAccounts()
(client!! as MixinExtMinecraftClient).setSession(user.createSession())
}
if (AccountsSubsystem.addAccount(authenticator)) {
sequenceOf(yggdrasilUsername, yggdrasilPassword, microsoftToken).forEach {
it.set("")
}
} catch (e: Exception) {
e.printStackTrace()
} else {
// TODO: Display some error
}
}
@ -197,13 +139,4 @@ object AccountManagerUIScreen : Screen(Text.of("account management hacker menu")
ImGui.end()
}
}
private fun User.createSession() = Session(
name,
uuid,
accessToken,
Optional.empty(),
Optional.empty(),
Session.AccountType.byName(type)
)
}

View file

@ -0,0 +1,95 @@
package codes.som.hibiscus.subsystems.accounts
import codes.som.hibiscus.Hibiscus
import codes.som.hibiscus.data.MinecraftAccount
import codes.som.hibiscus.mc
import codes.som.hibiscus.mixins.MixinExtMinecraftClient
import net.hycrafthd.minecraft_authenticator.login.Authenticator
import net.hycrafthd.minecraft_authenticator.login.User
import net.hycrafthd.minecraft_authenticator.login.file.AuthenticationFile
import net.minecraft.client.util.Session
import java.io.ByteArrayOutputStream
import java.util.*
object AccountsSubsystem {
private var accounts = emptyList<MinecraftAccount>()
fun getAccounts(): Sequence<MinecraftAccount> = accounts.asSequence()
fun addAccount(authenticator: Authenticator.Builder): Boolean {
try {
val loginResult = authenticator.shouldAuthenticate().run()
if (loginResult.user.isPresent) {
val user = loginResult.user.get()
val loginData = ByteArrayOutputStream()
.apply { loginResult.resultFile.write(this) }
.toString(Charsets.UTF_8)
Hibiscus.data.txn {
val account = MinecraftAccount.new {
this.name = user.name
this.lastKnownDisplayName = user.name
this.loginData = loginData
}
accounts = accounts + account
(mc as MixinExtMinecraftClient).setSession(createSession(user))
}
return true
}
} catch (e: Exception) {
e.printStackTrace()
}
return false
}
fun deleteAccount(account: MinecraftAccount) {
val id = account.id.value
accounts = accounts.filter { it.id.value == id }
account.delete()
}
fun reload() {
this.accounts = Hibiscus.data.txn {
MinecraftAccount.all().toList()
}
}
fun signIn(account: MinecraftAccount): User? {
try {
val loginFile = AuthenticationFile.read(account.loginData.byteInputStream(Charsets.UTF_8))
val loginResult = Authenticator.of(loginFile).shouldAuthenticate().run()
if (loginResult.user.isPresent) {
val user = loginResult.user.get()
val loginData = ByteArrayOutputStream()
.apply { loginResult.resultFile.write(this) }
.toString(Charsets.UTF_8)
Hibiscus.data.txn {
account.lastKnownDisplayName = user.name
account.loginData = loginData
}
return user
}
} catch (_: Exception) {
}
return null
}
fun createSession(user: User) = with(user) {
Session(
name,
uuid,
accessToken,
Optional.empty(),
Optional.empty(),
Session.AccountType.byName(type)
)
}
}