account refactoring, eros: wip connecting to server

This commit is contained in:
Bixilon 2021-07-24 15:13:44 +02:00
parent 06fc142d51
commit 9f6c2fb6b0
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
48 changed files with 668 additions and 531 deletions

View File

@ -50,6 +50,8 @@ object Minosoft {
val GLOBAL_EVENT_MASTER = GlobalEventMaster() val GLOBAL_EVENT_MASTER = GlobalEventMaster()
val LANGUAGE_MANAGER = MultiLanguageManager() val LANGUAGE_MANAGER = MultiLanguageManager()
val START_UP_LATCH = CountUpAndDownLatch(1) val START_UP_LATCH = CountUpAndDownLatch(1)
@Deprecated("Will be singleton interface")
lateinit var config: Configuration lateinit var config: Configuration
var initialized: Boolean = false var initialized: Boolean = false

View File

@ -34,7 +34,7 @@ class Configuration(private val configName: String = RunConfiguration.CONFIG_FIL
init { init {
if (file.exists()) { if (file.exists()) {
val config = JSONSerializer.MAP_ADAPTER.fromJson(Util.readFile(file.absolutePath))!! val config = JSONSerializer.MUTABLE_MAP_ADAPTER.fromJson(Util.readFile(file.absolutePath))!!
migrate(config) migrate(config)
var wasMigrated = false var wasMigrated = false

View File

@ -14,12 +14,22 @@
package de.bixilon.minosoft.config.config.account package de.bixilon.minosoft.config.config.account
import com.squareup.moshi.Json import com.squareup.moshi.Json
import de.bixilon.minosoft.Minosoft
import de.bixilon.minosoft.data.accounts.Account import de.bixilon.minosoft.data.accounts.Account
import de.bixilon.minosoft.modding.event.events.account.AccountSelectEvent
import java.util.* import java.util.*
data class AccountConfig( data class AccountConfig(
var selected: String = "", @Json(name = "selected") var selectedAccountId: String? = null,
@Json(name = "client_token") @Json(name = "client_token") var clientToken: String = UUID.randomUUID().toString(),
var clientToken: String = UUID.randomUUID().toString(),
val entries: MutableMap<String, Account> = mutableMapOf(), val entries: MutableMap<String, Account> = mutableMapOf(),
) ) {
@Transient
var selected: Account? = null
get() = entries[selectedAccountId]
set(value) {
Minosoft.GLOBAL_EVENT_MASTER.fireEvent(AccountSelectEvent(selected, value))
field // To allow transient for moshi
selectedAccountId = value?.id
}
}

View File

@ -25,7 +25,7 @@ data class Server(
val id: Int = nextServerId++, // ToDo: Is duplicated in config (first key, then in value) val id: Int = nextServerId++, // ToDo: Is duplicated in config (first key, then in value)
var address: String, var address: String,
var name: ChatComponent = ChatComponent.of(address), var name: ChatComponent = ChatComponent.of(address),
@Json(name = "version") var desiredVersion: Int = -1, @Json(name = "version") var forcedVersion: Int? = null,
@Json(name = "favicon") var faviconHash: String? = null, @Json(name = "favicon") var faviconHash: String? = null,
var type: ServerTypes = ServerTypes.NORMAL, var type: ServerTypes = ServerTypes.NORMAL,
) { ) {
@ -56,6 +56,11 @@ data class Server(
if (id > nextServerId) { if (id > nextServerId) {
nextServerId = id + 1 nextServerId = id + 1
} }
forcedVersion?.let {
if (it < 0) {
forcedVersion = null
}
}
faviconHash?.let { favicon = AssetsUtil.readAsset(it, true) } faviconHash?.let { favicon = AssetsUtil.readAsset(it, true) }
} }

View File

@ -1,86 +0,0 @@
/*
* Minosoft
* Copyright (C) 2020 Moritz Zwerger
*
* 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 <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.data.accounts;
import de.bixilon.minosoft.Minosoft;
import de.bixilon.minosoft.util.logging.Log;
import de.bixilon.minosoft.util.mojang.api.exceptions.MojangJoinServerErrorException;
import de.bixilon.minosoft.util.mojang.api.exceptions.NoNetworkConnectionException;
import java.util.Map;
import java.util.UUID;
public abstract class Account {
protected final String username;
protected final UUID uuid;
protected Account(String username, UUID uuid) {
this.username = username;
this.uuid = uuid;
}
public static void addAccount(Account account) {
Minosoft.config.getConfig().getAccount().getEntries().put(account.getId(), account);
account.saveToConfig();
Log.info(String.format("Added and saved account (type=%s, id=%s, username=%s, uuid=%s)", account.getClass().getSimpleName(), account.getId(), account.getUsername(), account.getUUID()));
}
public String getUsername() {
return this.username;
}
public UUID getUUID() {
return this.uuid;
}
public abstract Map<String, Object> serialize();
public abstract void join(String serverId) throws MojangJoinServerErrorException, NoNetworkConnectionException;
public abstract boolean select();
public abstract void logout();
public abstract String getId();
@Override
public int hashCode() {
return getId().hashCode();
}
@Override
public boolean equals(Object obj) {
if (super.equals(obj)) {
return true;
}
if (obj == null) {
return false;
}
if (hashCode() != obj.hashCode()) {
return false;
}
Account account = (Account) obj;
return getId().equals(account.getId());
}
@Override
public String toString() {
return getId();
}
public void saveToConfig() {
Minosoft.config.getConfig().getAccount().getEntries().put(this.getId(), this);
Minosoft.config.saveToFile();
}
}

View File

@ -1,6 +1,6 @@
/* /*
* Minosoft * Minosoft
* Copyright (C) 2020 Moritz Zwerger * Copyright (C) 2021 Moritz Zwerger
* *
* 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 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.
* *
@ -11,19 +11,18 @@
* This software is not affiliated with Mojang AB, the original developer of Minecraft. * This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/ */
package de.bixilon.minosoft.util.mojang.api.exceptions; package de.bixilon.minosoft.data.accounts
public class NoNetworkConnectionException extends Exception { import de.bixilon.minosoft.data.registries.ResourceLocation
public NoNetworkConnectionException(Exception cause) { abstract class Account(
super(cause); val username: String,
} ) {
abstract val id: String
abstract val type: ResourceLocation
public NoNetworkConnectionException(String message) { abstract fun join(serverId: String)
super(message);
}
public NoNetworkConnectionException() { abstract fun logout()
abstract fun verify()
}
} }

View File

@ -0,0 +1,22 @@
/*
* Minosoft
* Copyright (C) 2021 Moritz Zwerger
*
* 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 <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.data.accounts
import de.bixilon.minosoft.data.registries.CompanionResourceLocation
import de.bixilon.minosoft.util.json.JSONSerializer
import kotlin.reflect.KClass
abstract class AccountType(`class`: KClass<out Account>) : CompanionResourceLocation {
val TYPE = JSONSerializer.MOSHI.adapter(`class`.java)
}

View File

@ -0,0 +1,28 @@
/*
* Minosoft
* Copyright (C) 2021 Moritz Zwerger
*
* 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 <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.data.accounts
import de.bixilon.minosoft.data.accounts.types.MicrosoftAccount
import de.bixilon.minosoft.data.accounts.types.MojangAccount
import de.bixilon.minosoft.data.accounts.types.OfflineAccount
import de.bixilon.minosoft.data.registries.ResourceLocation
import de.bixilon.minosoft.util.KUtil.asResourceLocationMap
object AccountTypes {
val ACCOUNT_TYPES: Map<ResourceLocation, AccountType> = listOf(
MicrosoftAccount,
MojangAccount,
OfflineAccount,
).asResourceLocationMap()
}

View File

@ -1,38 +0,0 @@
/*
* Minosoft
* Copyright (C) 2020 Moritz Zwerger
*
* 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 <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.data.accounts;
import com.google.gson.JsonObject;
import de.bixilon.minosoft.util.Util;
import java.util.Map;
import java.util.UUID;
public class MicrosoftAccount extends MojangAccount {
public MicrosoftAccount(String accessToken, String id, UUID uuid, String username) {
super(accessToken, id, uuid, username, null);
}
public static MicrosoftAccount deserialize(JsonObject json) {
return new MicrosoftAccount(json.get("accessToken").getAsString(), json.get("id").getAsString(), Util.getUUIDFromString(json.get("uuid").getAsString()), json.get("username").getAsString());
}
public Map<String, Object> serialize() {
Map<String, Object> json = super.serialize();
json.put("type", "microsoft");
json.remove("email");
return json;
}
}

View File

@ -1,128 +0,0 @@
/*
* Minosoft
* Copyright (C) 2020 Moritz Zwerger
*
* 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 <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.data.accounts;
import com.google.gson.JsonObject;
import de.bixilon.minosoft.Minosoft;
import de.bixilon.minosoft.util.Util;
import de.bixilon.minosoft.util.mojang.api.MojangAuthentication;
import de.bixilon.minosoft.util.mojang.api.exceptions.AuthenticationException;
import de.bixilon.minosoft.util.mojang.api.exceptions.MojangJoinServerErrorException;
import de.bixilon.minosoft.util.mojang.api.exceptions.NoNetworkConnectionException;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
public class MojangAccount extends Account {
protected final String id;
protected final String email;
protected String accessToken;
protected RefreshStates lastRefreshStatus;
protected boolean needsRefresh = true;
public MojangAccount(String username, JsonObject json) {
super(json.getAsJsonObject("selectedProfile").get("name").getAsString(), Util.getUUIDFromString(json.getAsJsonObject("selectedProfile").get("id").getAsString()));
this.accessToken = json.get("accessToken").getAsString();
this.id = json.getAsJsonObject("user").get("id").getAsString();
this.email = username;
}
public MojangAccount(String accessToken, String id, UUID uuid, String username, String email) {
super(username, uuid);
this.accessToken = accessToken;
this.id = id;
this.email = email;
}
public static MojangAccount deserialize(Map<String, Object> json) {
return new MojangAccount((String) json.get("accessToken"), (String) json.get("id"), Util.getUUIDFromString((String) json.get("uuid")), (String) json.get("username"), (String) json.get("email"));
}
public Map<String, Object> serialize() {
Map<String, Object> json = new HashMap<>();
json.put("id", this.id);
json.put("accessToken", this.accessToken);
json.put("uuid", getUUID().toString());
json.put("username", getUsername());
json.put("email", this.email);
json.put("type", "mojang");
return json;
}
public void join(String serverId) throws MojangJoinServerErrorException, NoNetworkConnectionException {
MojangAuthentication.joinServer(this, serverId);
}
@Override
public boolean select() {
if (this.needsRefresh) {
return refreshToken() != RefreshStates.ERROR;
}
return true;
}
@Override
public void logout() {
Minosoft.config.getConfig().getAccount().getEntries().remove(this.getId());
Minosoft.config.saveToFile();
}
@Override
public String getId() {
return this.id;
}
public RefreshStates refreshToken() {
try {
this.accessToken = MojangAuthentication.refresh(this.accessToken);
this.lastRefreshStatus = RefreshStates.SUCCESSFUL;
} catch (NoNetworkConnectionException e) {
e.printStackTrace();
this.lastRefreshStatus = RefreshStates.FAILED;
} catch (AuthenticationException e) {
e.printStackTrace();
this.lastRefreshStatus = RefreshStates.ERROR;
}
return this.lastRefreshStatus;
}
public String getAccessToken() {
return this.accessToken;
}
public void setAccessToken(String accessToken) {
this.accessToken = accessToken;
this.needsRefresh = false;
}
public String getEmail() {
return this.email;
}
public boolean needsRefresh() {
return this.needsRefresh;
}
public void setNeedRefresh(boolean needsRefresh) {
this.needsRefresh = needsRefresh;
}
public enum RefreshStates {
SUCCESSFUL,
ERROR, // account not valid anymore
FAILED // error occurred while checking -> Unknown state
}
}

View File

@ -1,62 +0,0 @@
/*
* Minosoft
* Copyright (C) 2020 Moritz Zwerger
*
* 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 <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.data.accounts;
import de.bixilon.minosoft.util.Util;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
public class OfflineAccount extends Account {
public OfflineAccount(String username) {
super(username, UUID.nameUUIDFromBytes(("OfflinePlayer:" + username).getBytes(StandardCharsets.UTF_8)));
}
public OfflineAccount(String username, UUID uuid) {
super(username, uuid);
}
public static OfflineAccount deserialize(Map<String, Object> json) {
return new OfflineAccount((String) json.get("username"), Util.getUUIDFromString((String) json.get("uuid")));
}
@Override
public Map<String, Object> serialize() {
Map<String, Object> json = new HashMap<>();
json.put("username", getUsername());
json.put("uuid", getUUID().toString());
json.put("type", "offline");
return json;
}
@Override
public void join(String serverId) {
}
@Override
public boolean select() {
return true;
}
@Override
public void logout() {
}
@Override
public String getId() {
return getUsername() + ":" + getUUID().toString();
}
}

View File

@ -0,0 +1,47 @@
/*
* Minosoft
* Copyright (C) 2021 Moritz Zwerger
*
* 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 <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.data.accounts.types
import com.squareup.moshi.Json
import de.bixilon.minosoft.data.accounts.Account
import de.bixilon.minosoft.data.accounts.AccountType
import de.bixilon.minosoft.data.registries.ResourceLocation
import de.bixilon.minosoft.util.KUtil.asResourceLocation
import java.util.*
class MicrosoftAccount(
override val id: String,
username: String,
val uuid: UUID,
val email: String,
@Json(name = "access_token") private var accessToken: String,
) : Account(username) {
override val type: ResourceLocation = RESOURCE_LOCATION
override fun join(serverId: String) {
TODO()
}
override fun logout() {
TODO()
}
override fun verify() {
TODO()
}
companion object : AccountType(MicrosoftAccount::class) {
override val RESOURCE_LOCATION: ResourceLocation = "minosoft:microsoft_account".asResourceLocation()
}
}

View File

@ -0,0 +1,134 @@
/*
* Minosoft
* Copyright (C) 2021 Moritz Zwerger
*
* 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 <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.data.accounts.types
import com.squareup.moshi.Json
import de.bixilon.minosoft.Minosoft
import de.bixilon.minosoft.data.accounts.Account
import de.bixilon.minosoft.data.accounts.AccountType
import de.bixilon.minosoft.data.registries.ResourceLocation
import de.bixilon.minosoft.util.KUtil.asResourceLocation
import de.bixilon.minosoft.util.KUtil.nullCast
import de.bixilon.minosoft.util.KUtil.trim
import de.bixilon.minosoft.util.KUtil.unsafeCast
import de.bixilon.minosoft.util.http.HTTP2.postJson
import de.bixilon.minosoft.util.http.exceptions.AuthenticationException
import de.bixilon.minosoft.util.logging.Log
import de.bixilon.minosoft.util.logging.LogLevels
import de.bixilon.minosoft.util.logging.LogMessageType
import de.bixilon.minosoft.util.nbt.tag.NBTUtil.asCompound
import java.util.*
class MojangAccount(
override val id: String,
username: String,
val uuid: UUID,
val email: String,
@Json(name = "access_token") private var accessToken: String,
) : Account(username) {
private var refreshed: Boolean = false
override val type: ResourceLocation = RESOURCE_LOCATION
override fun join(serverId: String) {
val response = mutableMapOf(
"accessToken" to accessToken,
"selectedProfile" to uuid.trim(),
"serverId" to serverId,
).postJson(MOJANG_URL_JOIN)
response.body!!
if (response.statusCode != 204) {
throw AuthenticationException(response.statusCode, response.body["errorMessage"]?.nullCast())
}
Log.log(LogMessageType.AUTHENTICATION, LogLevels.VERBOSE) { "Mojang server join successful (username=$username, serverId=$serverId)" }
}
override fun logout() {
val response = mutableMapOf(
"accessToken" to accessToken,
"clientToken" to Minosoft.config.config.account.clientToken,
).postJson(MOJANG_URL_INVALIDATE)
if (response.statusCode != 200) {
throw AuthenticationException(response.statusCode)
}
Log.log(LogMessageType.AUTHENTICATION, LogLevels.VERBOSE) { "Mojang account login successful (username=$username)" }
}
override fun verify() {
if (refreshed) {
return
}
refresh()
}
fun refresh() {
val response = mutableMapOf(
"accessToken" to accessToken,
"clientToken" to Minosoft.config.config.account.clientToken,
).postJson(MOJANG_URL_REFRESH)
response.body!!
if (response.statusCode != 200) {
throw AuthenticationException(response.statusCode, response.body["errorMessage"].nullCast())
}
this.accessToken = response.body["accessToken"].unsafeCast()
refreshed = true
Log.log(LogMessageType.AUTHENTICATION, LogLevels.VERBOSE) { "Mojang account refresh successful (username=$username)" }
}
companion object : AccountType(MojangAccount::class) {
private const val MOJANG_URL_LOGIN = "https://authserver.mojang.com/authenticate"
private const val MOJANG_URL_JOIN = "https://sessionserver.mojang.com/session/minecraft/join"
private const val MOJANG_URL_REFRESH = "https://authserver.mojang.com/refresh"
private const val MOJANG_URL_INVALIDATE = "https://authserver.mojang.com/invalidate"
override val RESOURCE_LOCATION: ResourceLocation = "minosoft:mojang_account".asResourceLocation()
fun login(clientToken: String = Minosoft.config.config.account.clientToken, email: String, password: String): MojangAccount {
val response = mutableMapOf(
"agent" to mutableMapOf(
"name" to "Minecraft",
"version" to 1,
),
"username" to email,
"password" to password,
"clientToken" to clientToken,
"requestUser" to true,
).postJson(MOJANG_URL_LOGIN)
response.body!!
if (response.statusCode != 200) {
throw AuthenticationException(response.statusCode, response.body["errorMessage"]?.nullCast())
}
Log.log(LogMessageType.AUTHENTICATION, LogLevels.VERBOSE) { "Mojang login successful (email=$email)" }
return MojangAccount(
id = response.body["user"].asCompound()["id"].unsafeCast(),
username = response.body["selectedProfile"].asCompound()["name"].unsafeCast(),
uuid = response.body["selectedProfile"].asCompound()["id"].unsafeCast(),
email = email,
accessToken = response.body["accessToken"].unsafeCast(),
)
}
}
}

View File

@ -0,0 +1,34 @@
/*
* Minosoft
* Copyright (C) 2021 Moritz Zwerger
*
* 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 <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.data.accounts.types
import de.bixilon.minosoft.data.accounts.Account
import de.bixilon.minosoft.data.accounts.AccountType
import de.bixilon.minosoft.data.registries.ResourceLocation
import de.bixilon.minosoft.util.KUtil.asResourceLocation
class OfflineAccount(username: String) : Account(username) {
override val id: String = username
override val type: ResourceLocation = RESOURCE_LOCATION
override fun join(serverId: String) {}
override fun logout() {}
override fun verify() {}
companion object : AccountType(OfflineAccount::class) {
override val RESOURCE_LOCATION: ResourceLocation = "minosoft:offline_account".asResourceLocation()
}
}

View File

@ -25,11 +25,13 @@ object Eros {
private val TITLE = "minosoft:eros_window_title".asResourceLocation() private val TITLE = "minosoft:eros_window_title".asResourceLocation()
private val LAYOUT = "minosoft:eros/main/main.fxml".asResourceLocation() private val LAYOUT = "minosoft:eros/main/main.fxml".asResourceLocation()
lateinit var mainErosController: MainErosController
init { init {
Minosoft.GLOBAL_EVENT_MASTER.registerEvent(CallbackEventInvoker.of<FinishInitializingEvent> { Minosoft.GLOBAL_EVENT_MASTER.registerEvent(CallbackEventInvoker.of<FinishInitializingEvent> {
Platform.runLater { Platform.runLater {
val mainErosController = JavaFXUtil.openModal<MainErosController>(TITLE, LAYOUT) mainErosController = JavaFXUtil.openModal<MainErosController>(TITLE, LAYOUT)
mainErosController.stage.show() mainErosController.stage.show()
} }
}) })

View File

@ -35,4 +35,6 @@ abstract class JavaFXController : Initializable {
} }
open fun init() {} open fun init() {}
open fun postInit() {}
} }

View File

@ -17,7 +17,4 @@ import javafx.stage.Stage
abstract class JavaFXWindowController : JavaFXController() { abstract class JavaFXWindowController : JavaFXController() {
lateinit var stage: Stage lateinit var stage: Stage
open fun postInit() {}
} }

View File

@ -82,6 +82,7 @@ class ErosCrashReport : JavaFXWindowController() {
} }
companion object { companion object {
private var alreadyCrashed = false
private val CRASH_REPORT_COMMENTS = listOf( private val CRASH_REPORT_COMMENTS = listOf(
"Let's blame Bixilon for this", "Let's blame Bixilon for this",
"But it worked once", "But it worked once",
@ -111,22 +112,10 @@ class ErosCrashReport : JavaFXWindowController() {
* Special: Does not use any general functions/translations/..., because when a crash happens, you can't rely on anything. * Special: Does not use any general functions/translations/..., because when a crash happens, you can't rely on anything.
*/ */
fun Throwable?.crash() { fun Throwable?.crash() {
if (RunConfiguration.DISABLE_EROS) { if (alreadyCrashed) {
ShutdownManager.shutdown(this?.message, ShutdownReasons.CRITICAL_EXCEPTION)
return return
} }
alreadyCrashed = true
if (!JavaFXInitializer.initializing && !JavaFXInitializer.initialized) {
try {
JavaFXInitializer.start()
} catch (exception: Throwable) {
Log.log(LogMessageType.JAVAFX, LogLevels.WARN) { "Can not show crash report screen!" }
exception.printStackTrace()
return
}
}
JavaFXInitializer.await()
// Kill some stuff // Kill some stuff
tryCatch(executor = { DefaultThreadPool.shutdownNow() }) tryCatch(executor = { DefaultThreadPool.shutdownNow() })
@ -149,6 +138,23 @@ class ErosCrashReport : JavaFXWindowController() {
crashReportPath = null crashReportPath = null
} }
if (RunConfiguration.DISABLE_EROS) {
ShutdownManager.shutdown(this?.message, ShutdownReasons.CRITICAL_EXCEPTION)
return
}
if (!JavaFXInitializer.initializing && !JavaFXInitializer.initialized) {
try {
JavaFXInitializer.start()
} catch (exception: Throwable) {
Log.log(LogMessageType.JAVAFX, LogLevels.WARN) { "Can not show crash report screen!" }
exception.printStackTrace()
return
}
}
JavaFXInitializer.await()
Platform.runLater { Platform.runLater {
val fxmlLoader = FXMLLoader(ErosCrashReport::class.java.getResource("/assets/minosoft/eros/crash/crash_screen.fxml")) val fxmlLoader = FXMLLoader(ErosCrashReport::class.java.getResource("/assets/minosoft/eros/crash/crash_screen.fxml"))
val parent = fxmlLoader.load<Parent>() val parent = fxmlLoader.load<Parent>()
@ -175,15 +181,16 @@ class ErosCrashReport : JavaFXWindowController() {
// ${CRASH_REPORT_COMMENTS.random()} // ${CRASH_REPORT_COMMENTS.random()}
Time: ${SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(System.currentTimeMillis())} (${System.currentTimeMillis() / 1000L}) Time: ${SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(System.currentTimeMillis())} (${System.currentTimeMillis() / 1000L})
Crash thread: ${Thread.currentThread().name}
${exception?.toStackTrace() ?: ""} ${exception?.toStackTrace() ?: ""}
-- Runtime Details -- -- Runtime Details --
Start arguments: ${CommandLineArguments.ARGUMENTS} Start arguments: ${CommandLineArguments.ARGUMENTS}
JVM Flags: ${ManagementFactory.getRuntimeMXBean().inputArguments} JVM flags: ${ManagementFactory.getRuntimeMXBean().inputArguments}
Home directory: ${RunConfiguration.HOME_DIRECTORY} Home directory: ${RunConfiguration.HOME_DIRECTORY}
Disable Eros: ${RunConfiguration.DISABLE_EROS} Disable Eros: ${RunConfiguration.DISABLE_EROS}
Disable Rendering: ${RunConfiguration.DISABLE_RENDERING} Disable rendering: ${RunConfiguration.DISABLE_RENDERING}
-- System Details -- -- System Details --
Operating system: ${SystemInformation.OS_TEXT} Operating system: ${SystemInformation.OS_TEXT}

View File

@ -13,11 +13,15 @@
package de.bixilon.minosoft.gui.eros.main package de.bixilon.minosoft.gui.eros.main
import de.bixilon.minosoft.Minosoft
import de.bixilon.minosoft.ShutdownReasons import de.bixilon.minosoft.ShutdownReasons
import de.bixilon.minosoft.config.StaticConfiguration import de.bixilon.minosoft.config.StaticConfiguration
import de.bixilon.minosoft.data.accounts.Account
import de.bixilon.minosoft.gui.eros.controller.JavaFXWindowController import de.bixilon.minosoft.gui.eros.controller.JavaFXWindowController
import de.bixilon.minosoft.gui.eros.main.play.PlayMainController import de.bixilon.minosoft.gui.eros.main.play.PlayMainController
import de.bixilon.minosoft.gui.eros.modding.invoker.JavaFXEventInvoker
import de.bixilon.minosoft.gui.eros.util.JavaFXUtil import de.bixilon.minosoft.gui.eros.util.JavaFXUtil
import de.bixilon.minosoft.modding.event.events.account.AccountSelectEvent
import de.bixilon.minosoft.util.GitInfo import de.bixilon.minosoft.util.GitInfo
import de.bixilon.minosoft.util.KUtil.asResourceLocation import de.bixilon.minosoft.util.KUtil.asResourceLocation
import de.bixilon.minosoft.util.KUtil.decide import de.bixilon.minosoft.util.KUtil.decide
@ -34,13 +38,11 @@ import org.kordamp.ikonli.javafx.FontIcon
class MainErosController : JavaFXWindowController() { class MainErosController : JavaFXWindowController() {
@FXML @FXML
private lateinit var logoFX: ImageView private lateinit var logoFX: ImageView
@FXML @FXML
private lateinit var versionTextFX: Text private lateinit var versionTextFX: Text
@FXML @FXML
private lateinit var playIconFX: FontIcon private lateinit var playIconFX: FontIcon
@FXML @FXML
private lateinit var settingsIconFX: FontIcon private lateinit var settingsIconFX: FontIcon
@ -56,6 +58,12 @@ class MainErosController : JavaFXWindowController() {
@FXML @FXML
private lateinit var contentFX: Pane private lateinit var contentFX: Pane
@FXML
private lateinit var accountImageFX: ImageView
@FXML
private lateinit var accountNameFX: Text
private lateinit var icons: List<FontIcon> private lateinit var icons: List<FontIcon>
@ -84,6 +92,12 @@ class MainErosController : JavaFXWindowController() {
} }
contentFX.children.setAll(JavaFXUtil.loadEmbeddedController<PlayMainController>("minosoft:eros/main/play/play.fxml".asResourceLocation()).root) contentFX.children.setAll(JavaFXUtil.loadEmbeddedController<PlayMainController>("minosoft:eros/main/play/play.fxml".asResourceLocation()).root)
Minosoft.GLOBAL_EVENT_MASTER.registerEvent(JavaFXEventInvoker.of<AccountSelectEvent> {
accountImageFX.image = JavaFXUtil.MINOSOFT_LOGO // ToDo
accountNameFX.text = it.account?.username
})
} }
override fun postInit() { override fun postInit() {
@ -91,4 +105,16 @@ class MainErosController : JavaFXWindowController() {
ShutdownManager.shutdown(reason = ShutdownReasons.REQUESTED_BY_USER) ShutdownManager.shutdown(reason = ShutdownReasons.REQUESTED_BY_USER)
} }
} }
fun requestAccountSelect() {
TODO("Not yet implemented")
}
fun verifyAccount(account: Account? = Minosoft.config.config.account.selected, onSuccess: (Account) -> Unit) {
if (account == null) {
requestAccountSelect()
return
}
TODO("Not yet implemented")
}
} }

View File

@ -16,13 +16,19 @@ package de.bixilon.minosoft.gui.eros.main.play.server
import de.bixilon.minosoft.Minosoft import de.bixilon.minosoft.Minosoft
import de.bixilon.minosoft.config.server.Server import de.bixilon.minosoft.config.server.Server
import de.bixilon.minosoft.data.registries.ResourceLocation import de.bixilon.minosoft.data.registries.ResourceLocation
import de.bixilon.minosoft.data.registries.versions.Versions
import de.bixilon.minosoft.data.text.ChatComponent import de.bixilon.minosoft.data.text.ChatComponent
import de.bixilon.minosoft.gui.eros.Eros
import de.bixilon.minosoft.gui.eros.controller.EmbeddedJavaFXController import de.bixilon.minosoft.gui.eros.controller.EmbeddedJavaFXController
import de.bixilon.minosoft.gui.eros.main.play.server.card.ServerCard import de.bixilon.minosoft.gui.eros.main.play.server.card.ServerCard
import de.bixilon.minosoft.gui.eros.main.play.server.card.ServerCardController import de.bixilon.minosoft.gui.eros.main.play.server.card.ServerCardController
import de.bixilon.minosoft.gui.eros.modding.invoker.JavaFXEventInvoker import de.bixilon.minosoft.gui.eros.modding.invoker.JavaFXEventInvoker
import de.bixilon.minosoft.modding.event.events.status.StatusConnectionUpdateEvent import de.bixilon.minosoft.modding.event.events.status.StatusConnectionUpdateEvent
import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection
import de.bixilon.minosoft.util.DNSUtil
import de.bixilon.minosoft.util.KUtil.asResourceLocation import de.bixilon.minosoft.util.KUtil.asResourceLocation
import de.bixilon.minosoft.util.KUtil.decide
import de.bixilon.minosoft.util.task.pool.DefaultThreadPool
import javafx.fxml.FXML import javafx.fxml.FXML
import javafx.geometry.HPos import javafx.geometry.HPos
import javafx.geometry.Insets import javafx.geometry.Insets
@ -63,13 +69,20 @@ class ServerListController : EmbeddedJavaFXController<Pane>() {
} }
} }
override fun postInit() {
root.setOnKeyPressed { serverListViewFX.selectionModel.select(null) } // ToDo: Only on escape; not working
}
@FXML @FXML
fun refresh() { fun refresh() {
val selected = serverListViewFX.selectionModel.selectedItem
serverListViewFX.items.clear() serverListViewFX.items.clear()
for (server in Minosoft.config.config.server.entries.values) { for (server in Minosoft.config.config.server.entries.values) {
updateServer(server) updateServer(server)
} }
serverListViewFX.selectionModel.select(serverListViewFX.items.contains(selected).decide(selected, null))
} }
private fun updateServer(server: Server) { private fun updateServer(server: Server) {
@ -121,8 +134,10 @@ class ServerListController : EmbeddedJavaFXController<Pane>() {
var row = 0 var row = 0
for ((key, property) in SERVER_INFO_PROPERTIES) { for ((key, property) in SERVER_INFO_PROPERTIES) {
val propertyValue = property(serverCard.server) ?: continue
it.add(Minosoft.LANGUAGE_MANAGER.translate(key).textFlow, 0, row) it.add(Minosoft.LANGUAGE_MANAGER.translate(key).textFlow, 0, row)
it.add(ChatComponent.of(property(serverCard.server)).textFlow, 1, row++) it.add(ChatComponent.of(propertyValue).textFlow, 1, row++)
} }
it.columnConstraints += ColumnConstraints(10.0, 100.0, 150.0) it.columnConstraints += ColumnConstraints(10.0, 100.0, 150.0)
@ -138,7 +153,14 @@ class ServerListController : EmbeddedJavaFXController<Pane>() {
it.add(Button("Delete"), 1, 0) it.add(Button("Delete"), 1, 0)
it.add(Button("Edit"), 2, 0) it.add(Button("Edit"), 2, 0)
it.add(Button("Connect"), 3, 0) it.add(Button("Connect").apply {
setOnAction {
Eros.mainErosController.verifyAccount { account ->
val connection = PlayConnection(serverCard.server.ping?.realAddress ?: DNSUtil.getServerAddress(serverCard.server.address), account, Versions.getVersionById(serverCard.server.forcedVersion!!)) // ToDo: Get ping version
DefaultThreadPool += { connection.connect() }
}
}
}, 3, 0)
it.hgap = 5.0 it.hgap = 5.0
@ -153,9 +175,10 @@ class ServerListController : EmbeddedJavaFXController<Pane>() {
private companion object { private companion object {
private val SERVER_INFO_PROPERTIES: Map<ResourceLocation, (server: Server) -> Any> = mapOf( private val SERVER_INFO_PROPERTIES: Map<ResourceLocation, (server: Server) -> Any?> = mapOf(
"minosoft:server_name".asResourceLocation() to { it.name }, "minosoft:server.info.server_name".asResourceLocation() to { it.name },
"minosoft:server_address".asResourceLocation() to { it.address }, "minosoft:server.info.server_address".asResourceLocation() to { it.address },
"minosoft:server.info.forced_version".asResourceLocation() to { it.forcedVersion?.let { version -> Versions.getVersionById(version)!! } },
) )
} }
} }

View File

@ -19,6 +19,7 @@ import de.bixilon.minosoft.modding.event.invoker.EventInstantFireable
import de.bixilon.minosoft.modding.event.invoker.EventInvoker import de.bixilon.minosoft.modding.event.invoker.EventInvoker
import de.bixilon.minosoft.modding.loading.Priorities import de.bixilon.minosoft.modding.loading.Priorities
import javafx.application.Platform import javafx.application.Platform
import kotlin.reflect.KClass
/** /**
* Basically a CallbackEventInvoker, bt the callback runs on the java fx ui thread * Basically a CallbackEventInvoker, bt the callback runs on the java fx ui thread
@ -26,6 +27,7 @@ import javafx.application.Platform
class JavaFXEventInvoker<E : Event> private constructor( class JavaFXEventInvoker<E : Event> private constructor(
ignoreCancelled: Boolean, ignoreCancelled: Boolean,
private val callback: (E) -> Unit, private val callback: (E) -> Unit,
override val kEventType: KClass<out Event>,
override val eventType: Class<out Event>, override val eventType: Class<out Event>,
override val instantFire: Boolean, override val instantFire: Boolean,
) : EventInvoker(ignoreCancelled, Priorities.NORMAL, null), EventInstantFireable { ) : EventInvoker(ignoreCancelled, Priorities.NORMAL, null), EventInstantFireable {
@ -46,6 +48,7 @@ class JavaFXEventInvoker<E : Event> private constructor(
return JavaFXEventInvoker( return JavaFXEventInvoker(
ignoreCancelled = ignoreCancelled, ignoreCancelled = ignoreCancelled,
callback = callback, callback = callback,
kEventType = E::class,
eventType = E::class.java, eventType = E::class.java,
instantFire = instantFire, instantFire = instantFire,
) )

View File

@ -14,6 +14,7 @@
package de.bixilon.minosoft.gui.eros.util package de.bixilon.minosoft.gui.eros.util
import de.bixilon.minosoft.Minosoft import de.bixilon.minosoft.Minosoft
import de.bixilon.minosoft.gui.eros.crash.ErosCrashReport.Companion.crash
import de.bixilon.minosoft.util.CountUpAndDownLatch import de.bixilon.minosoft.util.CountUpAndDownLatch
import de.bixilon.minosoft.util.KUtil.asResourceLocation import de.bixilon.minosoft.util.KUtil.asResourceLocation
import de.bixilon.minosoft.util.logging.Log import de.bixilon.minosoft.util.logging.Log
@ -49,6 +50,7 @@ class JavaFXInitializer internal constructor() : Application() {
@Synchronized @Synchronized
fun start() { fun start() {
check(LATCH.count == 2) { "Already initialized!" } check(LATCH.count == 2) { "Already initialized!" }
Thread.setDefaultUncaughtExceptionHandler { _, exception -> exception.crash() }
Log.log(LogMessageType.JAVAFX, LogLevels.VERBOSE) { "Initializing JavaFX Toolkit..." } Log.log(LogMessageType.JAVAFX, LogLevels.VERBOSE) { "Initializing JavaFX Toolkit..." }
Thread({ Application.launch(JavaFXInitializer::class.java) }, "JavaFX Toolkit Initializing Thread").start() Thread({ Application.launch(JavaFXInitializer::class.java) }, "JavaFX Toolkit Initializing Thread").start()

View File

@ -1,6 +1,6 @@
/* /*
* Minosoft * Minosoft
* Copyright (C) 2020 Moritz Zwerger * Copyright (C) 2021 Moritz Zwerger
* *
* 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 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.
* *
@ -11,14 +11,11 @@
* This software is not affiliated with Mojang AB, the original developer of Minecraft. * This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/ */
package de.bixilon.minosoft.util.mojang.api.exceptions; package de.bixilon.minosoft.modding.event
public class MojangJoinServerErrorException extends Exception { import de.bixilon.minosoft.modding.event.events.Event
public MojangJoinServerErrorException(String message) { interface EventInstantFire<T : Event> {
super(message);
}
public MojangJoinServerErrorException() { fun fire(): T
}
} }

View File

@ -0,0 +1,31 @@
/*
* Minosoft
* Copyright (C) 2020 Moritz Zwerger
*
* 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 <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.modding.event.events.account
import de.bixilon.minosoft.Minosoft
import de.bixilon.minosoft.data.accounts.Account
import de.bixilon.minosoft.modding.event.EventInstantFire
import de.bixilon.minosoft.modding.event.events.Event
class AccountSelectEvent(
val previous: Account?,
val account: Account?,
) : Event() {
companion object : EventInstantFire<AccountSelectEvent> {
override fun fire(): AccountSelectEvent {
return AccountSelectEvent(null, Minosoft.config.config.account.selected)
}
}
}

View File

@ -15,10 +15,12 @@ package de.bixilon.minosoft.modding.event.invoker
import de.bixilon.minosoft.modding.event.events.CancelableEvent import de.bixilon.minosoft.modding.event.events.CancelableEvent
import de.bixilon.minosoft.modding.event.events.Event import de.bixilon.minosoft.modding.event.events.Event
import de.bixilon.minosoft.modding.loading.Priorities import de.bixilon.minosoft.modding.loading.Priorities
import kotlin.reflect.KClass
class CallbackEventInvoker<E : Event> private constructor( class CallbackEventInvoker<E : Event> private constructor(
ignoreCancelled: Boolean, ignoreCancelled: Boolean,
private val callback: (E) -> Unit, private val callback: (E) -> Unit,
override val kEventType: KClass<out Event>,
override val eventType: Class<out Event>, override val eventType: Class<out Event>,
override val instantFire: Boolean, override val instantFire: Boolean,
) : EventInvoker(ignoreCancelled, Priorities.NORMAL, null), EventInstantFireable { ) : EventInvoker(ignoreCancelled, Priorities.NORMAL, null), EventInstantFireable {
@ -37,6 +39,7 @@ class CallbackEventInvoker<E : Event> private constructor(
return CallbackEventInvoker( return CallbackEventInvoker(
ignoreCancelled = ignoreCancelled, ignoreCancelled = ignoreCancelled,
callback = callback, callback = callback,
kEventType = E::class,
eventType = E::class.java, eventType = E::class.java,
instantFire = instantFire, instantFire = instantFire,
) )

View File

@ -15,13 +15,15 @@ package de.bixilon.minosoft.modding.event.invoker
import de.bixilon.minosoft.modding.event.EventListener import de.bixilon.minosoft.modding.event.EventListener
import de.bixilon.minosoft.modding.event.events.Event import de.bixilon.minosoft.modding.event.events.Event
import de.bixilon.minosoft.modding.loading.Priorities import de.bixilon.minosoft.modding.loading.Priorities
import kotlin.reflect.KClass
abstract class EventInvoker( abstract class EventInvoker(
val isIgnoreCancelled: Boolean, val isIgnoreCancelled: Boolean,
val priority: Priorities, val priority: Priorities,
protected val listener: EventListener?, protected val listener: EventListener?,
) { ) {
abstract val eventType: Class<out Event?> abstract val kEventType: KClass<out Event>?
abstract val eventType: Class<out Event>
abstract operator fun invoke(event: Event) abstract operator fun invoke(event: Event)
} }

View File

@ -18,6 +18,7 @@ import de.bixilon.minosoft.modding.event.events.Event
import de.bixilon.minosoft.modding.event.events.annotations.EventHandler import de.bixilon.minosoft.modding.event.events.annotations.EventHandler
import de.bixilon.minosoft.modding.loading.Priorities import de.bixilon.minosoft.modding.loading.Priorities
import java.lang.reflect.Method import java.lang.reflect.Method
import kotlin.reflect.KClass
class EventInvokerMethod( class EventInvokerMethod(
ignoreCancelled: Boolean, ignoreCancelled: Boolean,
@ -25,6 +26,7 @@ class EventInvokerMethod(
listener: EventListener, listener: EventListener,
val method: Method, val method: Method,
) : EventInvoker(ignoreCancelled, priority, listener) { ) : EventInvoker(ignoreCancelled, priority, listener) {
override val kEventType: KClass<out Event>? = null
override val eventType: Class<out Event> = method.parameters[0].type as Class<out Event> override val eventType: Class<out Event> = method.parameters[0].type as Class<out Event>
constructor(annotation: EventHandler, listener: EventListener, method: Method) : this(annotation.ignoreCancelled, annotation.priority, listener, method) constructor(annotation: EventHandler, listener: EventListener, method: Method) : this(annotation.ignoreCancelled, annotation.priority, listener, method)

View File

@ -13,12 +13,15 @@
package de.bixilon.minosoft.modding.event.master package de.bixilon.minosoft.modding.event.master
import de.bixilon.minosoft.modding.event.EventInstantFire
import de.bixilon.minosoft.modding.event.events.CancelableEvent import de.bixilon.minosoft.modding.event.events.CancelableEvent
import de.bixilon.minosoft.modding.event.events.Event import de.bixilon.minosoft.modding.event.events.Event
import de.bixilon.minosoft.modding.event.invoker.EventInstantFireable
import de.bixilon.minosoft.modding.event.invoker.EventInvoker import de.bixilon.minosoft.modding.event.invoker.EventInvoker
import de.bixilon.minosoft.util.KUtil.synchronizedSetOf import de.bixilon.minosoft.util.KUtil.synchronizedSetOf
import de.bixilon.minosoft.util.KUtil.toSynchronizedList import de.bixilon.minosoft.util.KUtil.toSynchronizedList
import de.bixilon.minosoft.util.KUtil.toSynchronizedSet import de.bixilon.minosoft.util.KUtil.toSynchronizedSet
import kotlin.reflect.full.companionObjectInstance
open class EventMaster(vararg parents: AbstractEventMaster) : AbstractEventMaster { open class EventMaster(vararg parents: AbstractEventMaster) : AbstractEventMaster {
val parents: MutableSet<AbstractEventMaster> = synchronizedSetOf(*parents) val parents: MutableSet<AbstractEventMaster> = synchronizedSetOf(*parents)
@ -62,10 +65,20 @@ open class EventMaster(vararg parents: AbstractEventMaster) : AbstractEventMaste
override fun registerEvent(invoker: EventInvoker) { override fun registerEvent(invoker: EventInvoker) {
eventInvokers += invoker eventInvokers += invoker
if (invoker is EventInstantFireable && invoker.instantFire) {
val companion = invoker.kEventType?.companionObjectInstance ?: return
if (companion is EventInstantFire<*>) {
invoker.invoke(companion.fire())
}
}
} }
override fun registerEvents(vararg invoker: EventInvoker) { override fun registerEvents(vararg invokers: EventInvoker) {
eventInvokers += invoker for (invoker in invokers) {
registerEvent(invoker)
}
} }
override fun iterator(): Iterator<EventInvoker> { override fun iterator(): Iterator<EventInvoker> {

View File

@ -175,7 +175,7 @@ class PlayConnection(
} }
} }
fun connect(latch: CountUpAndDownLatch) { fun connect(latch: CountUpAndDownLatch = CountUpAndDownLatch(1)) {
// Log.log(LogMessageType.OTHER, LogLevels.VERBOSE){TranslatableComponents.HELLO_WORLD(Minosoft.LANGUAGE_MANAGER, "Moritz", 17)} // Log.log(LogMessageType.OTHER, LogLevels.VERBOSE){TranslatableComponents.HELLO_WORLD(Minosoft.LANGUAGE_MANAGER, "Moritz", 17)}
try { try {
fireEvent(RegistriesLoadEvent(this, registries, RegistriesLoadEvent.States.PRE)) fireEvent(RegistriesLoadEvent(this, registries, RegistriesLoadEvent.States.PRE))

View File

@ -64,8 +64,8 @@ class StatusConnection(
field = value field = value
value?.let { value?.let {
fireEvent(StatusConnectionErrorEvent(this, EventInitiators.UNKNOWN, it)) fireEvent(StatusConnectionErrorEvent(this, EventInitiators.UNKNOWN, it))
pingStatus = StatusConnectionStatuses.ERROR
} }
pingStatus = StatusConnectionStatuses.ERROR
} }

View File

@ -26,7 +26,7 @@ class LoginKickS2CP(buffer: PlayInByteBuffer) : PlayS2CPacket() {
override fun handle(connection: PlayConnection) { override fun handle(connection: PlayConnection) {
connection.fireEvent(LoginKickEvent(connection, this)) connection.fireEvent(LoginKickEvent(connection, this))
Log.log(LogMessageType.NETWORK_STATUS, level = LogLevels.WARN) { "Kicked from: $reason" } Log.log(LogMessageType.NETWORK_STATUS, level = LogLevels.WARN) { "Kicked from ${connection.address}: $reason" }
connection.disconnect() connection.disconnect()
} }

View File

@ -249,7 +249,7 @@ open class InByteBuffer {
} }
fun readJson(): Map<String, Any> { fun readJson(): Map<String, Any> {
return JSONSerializer.MAP_ADAPTER.fromJson(readString())!! return JSONSerializer.MUTABLE_MAP_ADAPTER.fromJson(readString())!!
} }
fun readJsonArray(length: Int = readVarInt()): Array<Map<String, Any>> { fun readJsonArray(length: Int = readVarInt()): Array<Map<String, Any>> {

View File

@ -72,10 +72,6 @@ public final class ProtocolDefinition {
public static final String MOJANG_URL_PACKAGES = "https://launchermeta.mojang.com/v1/packages/%s/%s"; public static final String MOJANG_URL_PACKAGES = "https://launchermeta.mojang.com/v1/packages/%s/%s";
public static final String MOJANG_LAUNCHER_URL_PACKAGES = "https://launcher.mojang.com/v1/objects/%s/%s"; public static final String MOJANG_LAUNCHER_URL_PACKAGES = "https://launcher.mojang.com/v1/objects/%s/%s";
public static final String MOJANG_URL_BLOCKED_SERVERS = "https://sessionserver.mojang.com/blockedservers";
public static final String MOJANG_URL_LOGIN = "https://authserver.mojang.com/authenticate";
public static final String MOJANG_URL_JOIN = "https://sessionserver.mojang.com/session/minecraft/join";
public static final String MOJANG_URL_REFRESH = "https://authserver.mojang.com/refresh";
public static final String MICROSOFT_ACCOUNT_APPLICATION_ID = "00000000402b5328"; // ToDo: Should we use our own application id? public static final String MICROSOFT_ACCOUNT_APPLICATION_ID = "00000000402b5328"; // ToDo: Should we use our own application id?
// public static final String MICROSOFT_ACCOUNT_APPLICATION_ID = "fe6f0fbf-3038-486a-9c84-6a28b71e0455"; // public static final String MICROSOFT_ACCOUNT_APPLICATION_ID = "fe6f0fbf-3038-486a-9c84-6a28b71e0455";

View File

@ -24,6 +24,7 @@ import java.net.http.HttpRequest;
import java.net.http.HttpResponse; import java.net.http.HttpResponse;
import java.util.HashMap; import java.util.HashMap;
@Deprecated
public final class HTTP { public final class HTTP {
public static HttpResponse<String> postJson(String url, String json, HashMap<String, String> headers) throws IOException, InterruptedException { public static HttpResponse<String> postJson(String url, String json, HashMap<String, String> headers) throws IOException, InterruptedException {

View File

@ -17,6 +17,7 @@ import com.squareup.moshi.JsonAdapter
import com.squareup.moshi.JsonWriter import com.squareup.moshi.JsonWriter
import de.bixilon.minosoft.data.entities.entities.Entity import de.bixilon.minosoft.data.entities.entities.Entity
import de.bixilon.minosoft.data.registries.ResourceLocation import de.bixilon.minosoft.data.registries.ResourceLocation
import de.bixilon.minosoft.data.registries.ResourceLocationAble
import de.bixilon.minosoft.data.text.ChatColors import de.bixilon.minosoft.data.text.ChatColors
import de.bixilon.minosoft.data.text.ChatComponent import de.bixilon.minosoft.data.text.ChatComponent
import de.bixilon.minosoft.data.text.TextComponent import de.bixilon.minosoft.data.text.TextComponent
@ -169,6 +170,22 @@ object KUtil {
} }
} }
fun <T> Boolean.decide(`true`: T, `false`: () -> T): T {
return if (this) {
`true`
} else {
`false`()
}
}
fun <T> Boolean.decide(`true`: () -> T, `false`: T): T {
return if (this) {
`true`()
} else {
`false`
}
}
fun String.asUUID(): UUID { fun String.asUUID(): UUID {
return Util.getUUIDFromString(this) return Util.getUUIDFromString(this)
} }
@ -325,4 +342,19 @@ object KUtil {
val Class<*>.realName: String val Class<*>.realName: String
get() = this.name.removePrefix(this.packageName).removePrefix(".") get() = this.name.removePrefix(this.packageName).removePrefix(".")
fun UUID.trim(): String {
return this.toString().replace("-", "")
}
fun <T : ResourceLocationAble> List<T>.asResourceLocationMap(): Map<ResourceLocation, T> {
val ret: MutableMap<ResourceLocation, T> = mutableMapOf()
for (value in this) {
ret[value.resourceLocation] = value
}
return ret.toMap()
}
} }

View File

@ -0,0 +1,62 @@
/*
* Minosoft
* Copyright (C) 2021 Moritz Zwerger
*
* 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 <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.util.http
import de.bixilon.minosoft.util.KUtil.extend
import de.bixilon.minosoft.util.json.JSONSerializer
import java.net.URI
import java.net.http.HttpClient
import java.net.http.HttpRequest
import java.net.http.HttpResponse
object HTTP2 {
fun Map<String, Any>.headers(): Array<String> {
val headers: MutableList<String> = mutableListOf()
for ((key, value) in this) {
headers += key
headers += value.toString()
}
return headers.toTypedArray()
}
fun <Payload, Response> post(url: String, data: Payload, bodyPublisher: (Payload) -> String, bodyBuilder: (String) -> Response, headers: Map<String, Any> = mapOf()): HTTPResponse<Response> {
val client = HttpClient.newHttpClient()
val request = HttpRequest.newBuilder()
.uri(URI.create(url))
.POST(HttpRequest.BodyPublishers.ofString(bodyPublisher(data)))
.headers(*headers.headers())
.build()
val response = client.send(request, HttpResponse.BodyHandlers.ofString())
return HTTPResponse(response.statusCode(), bodyBuilder(response.body()))
}
fun Map<String, Any>.postJson(url: String, headers: Map<String, Any> = mapOf()): HTTPResponse<Map<String, Any>?> {
return post(
url = url,
data = this,
bodyPublisher = { JSONSerializer.MAP_ADAPTER.toJson(it) },
bodyBuilder = { JSONSerializer.MAP_ADAPTER.fromJson(it) },
headers = headers.extend(
"Content-Type" to "application/json",
"Accept" to "application/json",
)
)
}
}

View File

@ -0,0 +1,19 @@
/*
* Minosoft
* Copyright (C) 2021 Moritz Zwerger
*
* 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 <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.util.http
class HTTPResponse<T>(
val statusCode: Int,
val body: T,
)

View File

@ -10,12 +10,9 @@
* *
* This software is not affiliated with Mojang AB, the original developer of Minecraft. * This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/ */
package de.bixilon.minosoft.util.http.exceptions
package de.bixilon.minosoft.util.mojang.api.exceptions; class AuthenticationException(
statusCode: Int,
public class AuthenticationException extends Exception { message: String? = null,
) : HTTPException(statusCode, message)
public AuthenticationException(String message) {
super(message);
}
}

View File

@ -0,0 +1,19 @@
/*
* Minosoft
* Copyright (C) 2021 Moritz Zwerger
*
* 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 <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.util.http.exceptions
open class HTTPException(
val statusCode: Int,
message: String? = null,
) : Exception(message)

View File

@ -14,25 +14,22 @@
package de.bixilon.minosoft.util.json package de.bixilon.minosoft.util.json
import com.squareup.moshi.FromJson import com.squareup.moshi.FromJson
import com.squareup.moshi.JsonAdapter
import com.squareup.moshi.ToJson import com.squareup.moshi.ToJson
import de.bixilon.minosoft.data.accounts.Account import de.bixilon.minosoft.data.accounts.Account
import de.bixilon.minosoft.data.accounts.MicrosoftAccount import de.bixilon.minosoft.data.accounts.AccountTypes
import de.bixilon.minosoft.data.accounts.MojangAccount import de.bixilon.minosoft.util.KUtil.asResourceLocation
import de.bixilon.minosoft.data.accounts.OfflineAccount import de.bixilon.minosoft.util.KUtil.unsafeCast
import de.bixilon.minosoft.util.nbt.tag.NBTUtil.asCompound
object AccountSerializer { object AccountSerializer {
@FromJson @FromJson
fun fromJson(json: Map<String, Any>): Account { fun fromJson(json: Map<String, Any>): Account {
return when (json["type"]!!) { return AccountTypes.ACCOUNT_TYPES[json["type"]!!.asResourceLocation()]!!.TYPE.fromJsonValue(json)!!
"mojang" -> MojangAccount.deserialize(json)
"offline" -> OfflineAccount.deserialize(json)
"microsoft" -> MicrosoftAccount.deserialize(json)
else -> throw IllegalArgumentException("Invalid account type: ${json["type"]}")
}
} }
@ToJson @ToJson
fun toJson(account: Account): Map<String, Any> { fun toJson(account: Account): Map<String, Any> {
return account.serialize() return AccountTypes.ACCOUNT_TYPES[account.type]!!.TYPE.unsafeCast<JsonAdapter<Account>>().toJsonValue(account).asCompound()
} }
} }

View File

@ -21,19 +21,21 @@ import de.bixilon.minosoft.config.config.Config
import de.bixilon.minosoft.gui.rendering.textures.properties.ImageProperties import de.bixilon.minosoft.gui.rendering.textures.properties.ImageProperties
object JSONSerializer { object JSONSerializer {
private val MOSHI = Moshi.Builder() val MOSHI = Moshi.Builder()
.add(RGBColorSerializer) .add(RGBColorSerializer)
.add(Vec2Serializer) .add(Vec2Serializer)
.add(AccountSerializer) .add(AccountSerializer)
.add(ChatComponentSerializer) .add(ChatComponentSerializer)
.add(ServerAddressSerializer) .add(ServerAddressSerializer)
.add(ResourceLocationSerializer) .add(ResourceLocationSerializer)
.add(UUIDSerializer)
.add(KotlinJsonAdapterFactory()) .add(KotlinJsonAdapterFactory())
.build()!! .build()!!
val ANY_ADAPTER = MOSHI.adapter(Any::class.java)!! val ANY_ADAPTER = MOSHI.adapter(Any::class.java)!!
val CONFIG_ADAPTER = MOSHI.adapter(Config::class.java)!! val CONFIG_ADAPTER = MOSHI.adapter(Config::class.java)!!
val MAP_ADAPTER: JsonAdapter<MutableMap<String, Any>> = MOSHI.adapter(Types.newParameterizedType(MutableMap::class.java, String::class.java, Any::class.java)) val MUTABLE_MAP_ADAPTER: JsonAdapter<MutableMap<String, Any>> = MOSHI.adapter(Types.newParameterizedType(MutableMap::class.java, String::class.java, Any::class.java))
val MAP_ADAPTER: JsonAdapter<Map<String, Any>> = MOSHI.adapter(Types.newParameterizedType(Map::class.java, String::class.java, Any::class.java))
val IMAGE_PROPERTIES_ADAPTER = MOSHI.adapter(ImageProperties::class.java)!! val IMAGE_PROPERTIES_ADAPTER = MOSHI.adapter(ImageProperties::class.java)!!
} }

View File

@ -0,0 +1,37 @@
/*
* Minosoft
* Copyright (C) 2020 Moritz Zwerger
*
* 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 <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.util.json
import com.squareup.moshi.*
import de.bixilon.minosoft.util.Util
import java.util.*
object UUIDSerializer : JsonAdapter<UUID>() {
@FromJson
override fun fromJson(jsonReader: JsonReader): UUID? {
if (jsonReader.peek() == JsonReader.Token.NULL) {
return null
}
return Util.getUUIDFromString(jsonReader.nextString())
}
@ToJson
override fun toJson(jsonWriter: JsonWriter, uuid: UUID?) {
if (uuid == null) {
jsonWriter.nullValue()
return
}
jsonWriter.value(uuid.toString())
}
}

View File

@ -30,6 +30,8 @@ enum class LogMessageType(
VERSION_LOADING(ChatColors.YELLOW), VERSION_LOADING(ChatColors.YELLOW),
ASSETS(ChatColors.BLACK), ASSETS(ChatColors.BLACK),
AUTHENTICATION(ChatColors.BLACK),
NETWORK_RESOLVING(ChatColors.DARK_GREEN), NETWORK_RESOLVING(ChatColors.DARK_GREEN),
NETWORK_STATUS(ChatColors.DARK_GREEN), NETWORK_STATUS(ChatColors.DARK_GREEN),
NETWORK_PACKETS_IN(ChatColors.BLUE, mapOf( NETWORK_PACKETS_IN(ChatColors.BLUE, mapOf(

View File

@ -14,8 +14,7 @@
package de.bixilon.minosoft.util.microsoft package de.bixilon.minosoft.util.microsoft
import com.google.gson.JsonParser import com.google.gson.JsonParser
import de.bixilon.minosoft.data.accounts.Account import de.bixilon.minosoft.data.accounts.types.MicrosoftAccount
import de.bixilon.minosoft.data.accounts.MicrosoftAccount
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
import de.bixilon.minosoft.terminal.RunConfiguration import de.bixilon.minosoft.terminal.RunConfiguration
import de.bixilon.minosoft.util.HTTP import de.bixilon.minosoft.util.HTTP
@ -38,7 +37,7 @@ object MicrosoftOAuthUtils {
val xstsToken = getXSTSToken(xboxLiveToken.first) val xstsToken = getXSTSToken(xboxLiveToken.first)
val microsoftAccount = getMicrosoftAccount(getMinecraftAccessToken(xboxLiveToken.second, xstsToken)) val microsoftAccount = getMicrosoftAccount(getMinecraftAccessToken(xboxLiveToken.second, xstsToken))
Account.addAccount(microsoftAccount) // ToDo: Account.addAccount(microsoftAccount)
} catch (exception: Exception) { } catch (exception: Exception) {
Log.warn("Can not login into microsoft account") Log.warn("Can not login into microsoft account")
exception.printStackTrace() exception.printStackTrace()
@ -162,7 +161,8 @@ object MicrosoftOAuthUtils {
} }
val body = JsonParser.parseString(response.body()).asJsonObject val body = JsonParser.parseString(response.body()).asJsonObject
return MicrosoftAccount(bearerToken, body["id"].asString!!, Util.getUUIDFromString(body["id"].asString!!), body["name"].asString!!) // return MicrosoftAccount(bearerToken, body["id"].asString!!, Util.getUUIDFromString(body["id"].asString!!), body["name"].asString!!)
TODO()
} }
init { init {

View File

@ -1,125 +0,0 @@
/*
* Minosoft
* Copyright (C) 2020 Moritz Zwerger
*
* 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 <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.util.mojang.api;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import de.bixilon.minosoft.Minosoft;
import de.bixilon.minosoft.data.accounts.MojangAccount;
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition;
import de.bixilon.minosoft.util.HTTP;
import de.bixilon.minosoft.util.logging.Log;
import de.bixilon.minosoft.util.logging.LogMessageType;
import de.bixilon.minosoft.util.mojang.api.exceptions.AuthenticationException;
import de.bixilon.minosoft.util.mojang.api.exceptions.MojangJoinServerErrorException;
import de.bixilon.minosoft.util.mojang.api.exceptions.NoNetworkConnectionException;
import java.io.IOException;
import java.net.http.HttpResponse;
public final class MojangAuthentication {
public static MojangAccount login(String username, String password) throws AuthenticationException, NoNetworkConnectionException {
return login(Minosoft.config.getConfig().getAccount().getClientToken(), username, password);
}
public static MojangAccount login(String clientToken, String username, String password) throws NoNetworkConnectionException, AuthenticationException {
JsonObject agent = new JsonObject();
agent.addProperty("name", "Minecraft");
agent.addProperty("version", 1);
JsonObject payload = new JsonObject();
payload.add("agent", agent);
payload.addProperty("username", username);
payload.addProperty("password", password);
payload.addProperty("clientToken", clientToken);
payload.addProperty("requestUser", true);
HttpResponse<String> response;
try {
response = HTTP.postJson(ProtocolDefinition.MOJANG_URL_LOGIN, payload);
} catch (IOException | InterruptedException e) {
Log.printException(e, LogMessageType.OTHER);
throw new NoNetworkConnectionException(e);
}
if (response == null) {
Log.mojang(String.format("Failed to login with username %s", username));
throw new NoNetworkConnectionException("Unknown error, check your Internet connection");
}
JsonObject jsonResponse = JsonParser.parseString(response.body()).getAsJsonObject();
if (response.statusCode() != 200) {
Log.mojang(String.format("Failed to login with error code %d: %s", response.statusCode(), jsonResponse.get("errorMessage").getAsString()));
throw new AuthenticationException(jsonResponse.get("errorMessage").getAsString());
}
// now it is okay
return new MojangAccount(username, jsonResponse);
}
public static void joinServer(MojangAccount account, String serverId) throws NoNetworkConnectionException, MojangJoinServerErrorException {
JsonObject payload = new JsonObject();
payload.addProperty("accessToken", account.getAccessToken());
payload.addProperty("selectedProfile", account.getUUID().toString().replace("-", ""));
payload.addProperty("serverId", serverId);
HttpResponse<String> response;
try {
response = HTTP.postJson(ProtocolDefinition.MOJANG_URL_JOIN, payload);
} catch (IOException | InterruptedException e) {
throw new NoNetworkConnectionException(e);
}
if (response == null) {
Log.mojang(String.format("Failed to join server: %s", serverId));
throw new MojangJoinServerErrorException();
}
if (response.statusCode() != 204) {
JsonObject jsonResponse = JsonParser.parseString(response.body()).getAsJsonObject();
Log.mojang(String.format("Failed to join server with error code %d: %s", response.statusCode(), jsonResponse.has("errorMessage") ? jsonResponse.get("errorMessage").getAsString() : "null"));
throw new MojangJoinServerErrorException(jsonResponse.get("errorMessage").getAsString());
}
// joined
Log.mojang("Joined server successfully");
}
public static String refresh(String accessToken) throws NoNetworkConnectionException, AuthenticationException {
return refresh(Minosoft.config.getConfig().getAccount().getClientToken(), accessToken);
}
public static String refresh(String clientToken, String accessToken) throws NoNetworkConnectionException, AuthenticationException {
JsonObject payload = new JsonObject();
payload.addProperty("accessToken", accessToken);
payload.addProperty("clientToken", clientToken);
HttpResponse<String> response;
try {
response = HTTP.postJson(ProtocolDefinition.MOJANG_URL_REFRESH, payload);
} catch (IOException | InterruptedException e) {
Log.mojang(String.format("Could not connect to mojang server: %s", e.getCause().toString()));
throw new NoNetworkConnectionException(e);
}
if (response == null) {
Log.mojang("Failed to refresh session");
throw new NoNetworkConnectionException();
}
JsonObject jsonResponse = JsonParser.parseString(response.body()).getAsJsonObject();
if (response.statusCode() != 200) {
Log.mojang(String.format("Failed to refresh session with error code %d: %s", response.statusCode(), jsonResponse.get("errorMessage").getAsString()));
throw new AuthenticationException(jsonResponse.get("errorMessage").getAsString());
}
// now it is okay
Log.mojang("Refreshed 1 session token");
return jsonResponse.get("accessToken").getAsString();
}
}

View File

@ -14,6 +14,11 @@
package de.bixilon.minosoft.util.nbt.tag package de.bixilon.minosoft.util.nbt.tag
object NBTUtil { object NBTUtil {
fun compound(): MutableMap<String, Any> {
return mutableMapOf()
}
fun MutableMap<String, Any>.getAndRemove(key: String): Any? { fun MutableMap<String, Any>.getAndRemove(key: String): Any? {
val value = this[key] val value = this[key]
this.remove(key) this.remove(key)

View File

@ -5,7 +5,7 @@
<?import javafx.scene.layout.*?> <?import javafx.scene.layout.*?>
<?import javafx.scene.text.Text?> <?import javafx.scene.text.Text?>
<?import org.kordamp.ikonli.javafx.*?> <?import org.kordamp.ikonli.javafx.*?>
<HBox xmlns:fx="http://javafx.com/fxml/1" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="600.0" prefWidth="1000.0" fx:controller="de.bixilon.minosoft.gui.eros.main.MainErosController"> <HBox xmlns:fx="http://javafx.com/fxml/1" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="600.0" prefWidth="1000.0" xmlns="http://javafx.com/javafx/16" fx:controller="de.bixilon.minosoft.gui.eros.main.MainErosController">
<GridPane HBox.hgrow="ALWAYS"> <GridPane HBox.hgrow="ALWAYS">
<columnConstraints> <columnConstraints>
<ColumnConstraints hgrow="ALWAYS" minWidth="10.0" prefWidth="100.0"/> <ColumnConstraints hgrow="ALWAYS" minWidth="10.0" prefWidth="100.0"/>
@ -77,7 +77,21 @@
</GridPane.margin> </GridPane.margin>
</FontIcon> </FontIcon>
</GridPane> </GridPane>
<Text text="Account" GridPane.columnIndex="3"/> <GridPane GridPane.columnIndex="3">
<columnConstraints>
<ColumnConstraints hgrow="NEVER" minWidth="10.0"/>
<ColumnConstraints hgrow="NEVER"/>
</columnConstraints>
<rowConstraints>
<RowConstraints minHeight="10.0" vgrow="ALWAYS"/>
</rowConstraints>
<ImageView fx:id="accountImageFX" fitHeight="30.0" fitWidth="30.0" pickOnBounds="true" preserveRatio="true" GridPane.hgrow="ALWAYS" GridPane.vgrow="ALWAYS">
<GridPane.margin>
<Insets right="5.0"/>
</GridPane.margin>
</ImageView>
<Text fx:id="accountNameFX" text="Account" GridPane.columnIndex="1"/>
</GridPane>
<GridPane.margin> <GridPane.margin>
<Insets bottom="3.0" left="5.0" right="5.0" top="3.0"/> <Insets bottom="3.0" left="5.0" right="5.0" top="3.0"/>
</GridPane.margin> </GridPane.margin>

View File

@ -1,7 +1,9 @@
minosoft:hello.world=§aHi, my name is §e%1$s§a. I am §e%2$s §ayears old and I want to say the following: §cHello world! minosoft:hello.world=§aHi, my name is §e%1$s§a. I am §e%2$s §ayears old and I want to say the following: §cHello world!
minosoft:eros_window_title=Minosoft minosoft:eros_window_title=Minosoft
minosoft:server_name=Server name
minosoft:server_address=Server address minosoft:server.info.server_name=Server name
minosoft:server.info.server_address=Server address
minosoft:server.info.forced_version=Forced version
minosoft:status.connection.state.waiting=Waiting... minosoft:status.connection.state.waiting=Waiting...
minosoft:status.connection.state.resolving=Resolving hostname... minosoft:status.connection.state.resolving=Resolving hostname...