diff --git a/CMakeModules/DownloadExternals.cmake b/CMakeModules/DownloadExternals.cmake index e65827290d..3e9bc26d69 100644 --- a/CMakeModules/DownloadExternals.cmake +++ b/CMakeModules/DownloadExternals.cmake @@ -133,7 +133,7 @@ function(download_qt_configuration prefix_out target host type arch arch_path ba set(install_args ${install_args} install-tool --outputdir ${base_path} ${host} desktop ${target}) else() set(prefix "${base_path}/${target}/${arch_path}") - set(install_args ${install_args} install-qt --outputdir ${base_path} ${host} ${type} ${target} ${arch} -m qtbase) + set(install_args ${install_args} install-qt --outputdir ${base_path} ${host} ${type} ${target} ${arch} -m qt_base) if (YUZU_USE_QT_MULTIMEDIA) set(install_args ${install_args} qtmultimedia) diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/dialogs/NetPlayDialog.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/dialogs/NetPlayDialog.kt index ede2cfafa4..cd3e9a4474 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/dialogs/NetPlayDialog.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/dialogs/NetPlayDialog.kt @@ -34,6 +34,7 @@ import org.yuzu.yuzu_emu.databinding.ItemBanListBinding import org.yuzu.yuzu_emu.databinding.ItemButtonNetplayBinding import org.yuzu.yuzu_emu.databinding.ItemTextNetplayBinding import org.yuzu.yuzu_emu.features.settings.model.StringSetting +import org.yuzu.yuzu_emu.network.NetDataValidators import org.yuzu.yuzu_emu.network.NetPlayManager import org.yuzu.yuzu_emu.utils.CompatUtils import org.yuzu.yuzu_emu.utils.GameHelper @@ -102,8 +103,16 @@ class NetPlayDialog(context: Context) : BottomSheetDialog(context) { dismiss() } btnLobbyBrowser.setOnClickListener { - LobbyBrowser(context).show() - dismiss() + if (!NetDataValidators.username()) { + Toast.makeText( + context, + R.string.multiplayer_nickname_invalid, + Toast.LENGTH_LONG + ).show() + } else { + LobbyBrowser(context).show() + dismiss() + } } } } @@ -368,7 +377,7 @@ class NetPlayDialog(context: Context) : BottomSheetDialog(context) { ) ) { override fun validate(s: String): Boolean { - return s.length in 3..20 + return NetDataValidators.roomName(s) } } @@ -378,7 +387,7 @@ class NetPlayDialog(context: Context) : BottomSheetDialog(context) { context.getString(R.string.multiplayer_required) ) { override fun validate(s: String): Boolean { - return s.isNotEmpty() + return NetDataValidators.notEmpty(s) } } @@ -388,12 +397,7 @@ class NetPlayDialog(context: Context) : BottomSheetDialog(context) { context.getString(R.string.multiplayer_token_required) ) { override fun validate(s: String): Boolean { - if (s != context.getString(R.string.multiplayer_public_visibility)) { - return true; - } - - val token = StringSetting.WEB_TOKEN.getString() - return token.matches(Regex("[a-z]{48}")) + return NetDataValidators.roomVisibility(s, context) } } @@ -403,12 +407,7 @@ class NetPlayDialog(context: Context) : BottomSheetDialog(context) { context.getString(R.string.multiplayer_ip_error) ) { override fun validate(s: String): Boolean { - return try { - InetAddress.getByName(s) - s.length >= 7 - } catch (_: Exception) { - false - } + return NetDataValidators.ipAddress(s) } } @@ -418,7 +417,7 @@ class NetPlayDialog(context: Context) : BottomSheetDialog(context) { context.getString(R.string.multiplayer_username_error) ) { override fun validate(s: String): Boolean { - return s.length in 4..20 + return NetDataValidators.username(s) } } @@ -428,7 +427,7 @@ class NetPlayDialog(context: Context) : BottomSheetDialog(context) { context.getString(R.string.multiplayer_port_error) ) { override fun validate(s: String): Boolean { - return s.toIntOrNull() in 1..65535 + return NetDataValidators.port(s) } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt index ab35a9180c..a269cab254 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt @@ -20,6 +20,7 @@ import org.yuzu.yuzu_emu.features.settings.model.IntSetting import org.yuzu.yuzu_emu.features.settings.model.LongSetting import org.yuzu.yuzu_emu.features.settings.model.ShortSetting import org.yuzu.yuzu_emu.features.settings.model.StringSetting +import org.yuzu.yuzu_emu.network.NetDataValidators import org.yuzu.yuzu_emu.utils.GpuDriverHelper import org.yuzu.yuzu_emu.utils.NativeConfig @@ -300,9 +301,7 @@ abstract class SettingsItem( val chars = "abcdefghijklmnopqrstuvwxyz" (1..48).map { chars.random() }.joinToString("") }, - validator = { s -> - s?.matches(Regex("[a-z]{48}")) == true - }, + validator = NetDataValidators::token, errorId = R.string.multiplayer_token_error ) ) @@ -312,9 +311,7 @@ abstract class SettingsItem( StringSetting.WEB_USERNAME, titleId = R.string.web_username, descriptionId = R.string.web_username_description, - validator = { s -> - s?.length in 4..20 - }, + validator = NetDataValidators::username, errorId = R.string.multiplayer_username_error ) ) diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsDialogFragment.kt index e50ebe50f4..aa17d05e34 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsDialogFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsDialogFragment.kt @@ -154,8 +154,6 @@ class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener stringInputBinding.generate.setOnClickListener { stringInputBinding.editText.setText(onGenerate()) } - } else { - stringInputBinding.generate.isVisible = false } val validator = item.validator @@ -179,8 +177,9 @@ class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener } override fun afterTextChanged(s: Editable?) { - stringInputBinding.editText.error = - if (validator(s.toString())) null else requireContext().getString(item.errorId) + val isValid = validator(s.toString()) + stringInputBinding.editTextLayout.isErrorEnabled = !isValid + stringInputBinding.editTextLayout.error = if (isValid) null else requireContext().getString(item.errorId) } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/network/NetDataValidators.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/network/NetDataValidators.kt new file mode 100644 index 0000000000..b3edf35d8e --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/network/NetDataValidators.kt @@ -0,0 +1,56 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +package org.yuzu.yuzu_emu.network + +import android.content.Context +import org.yuzu.yuzu_emu.R +import org.yuzu.yuzu_emu.features.settings.model.StringSetting +import java.net.InetAddress + +object NetDataValidators { + fun roomName(s: String): Boolean { + return s.length in 3..20 + } + + fun notEmpty(s: String): Boolean { + return s.isNotEmpty() + } + + fun token(s: String?): Boolean { + return s?.matches(Regex("[a-z]{48}")) == true + } + + fun token(): Boolean { + return token(StringSetting.WEB_TOKEN.getString()) + } + + fun roomVisibility(s: String, context: Context): Boolean { + if (s != context.getString(R.string.multiplayer_public_visibility)) { + return true; + } + + return token() + } + + fun ipAddress(s: String): Boolean { + return try { + InetAddress.getByName(s) + s.length >= 7 + } catch (_: Exception) { + false + } + } + + fun username(s: String?): Boolean { + return s?.matches(Regex("^[ a-zA-Z0-9._-]{4,20}$")) == true + } + + fun username(): Boolean { + return username(StringSetting.WEB_USERNAME.getString()) + } + + fun port(s: String): Boolean { + return s.toIntOrNull() in 1..65535 + } +} \ No newline at end of file diff --git a/src/android/app/src/main/res/layout/dialog_edit_text.xml b/src/android/app/src/main/res/layout/dialog_edit_text.xml index 3612f46eae..bc7864a395 100644 --- a/src/android/app/src/main/res/layout/dialog_edit_text.xml +++ b/src/android/app/src/main/res/layout/dialog_edit_text.xml @@ -27,6 +27,7 @@ android:layout_height="wrap_content" android:layout_marginTop="15dp" android:text="@string/generate" + android:visibility="gone" app:layout_constraintEnd_toEndOf="@+id/edit_text_layout" app:layout_constraintTop_toBottomOf="@+id/edit_text_layout" /> diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index e611e66c1f..3814105ea0 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -148,18 +148,18 @@ Failed to join room Name is too short Invalid address - Invalid port! + Invalid port Exit Room Network error Lost connection - Name collision + Username already taken MAC Address collision Console ID collision Wrong version Wrong password Could not connect Room is full - Host banned + You are banned from this room Permission denied No such user Already in room @@ -221,7 +221,8 @@ Required Web Token required, go to Advanced Settings -> System -> Network Invalid IP format - Must be between 4–20 characters + Must be between 4–20 characters, and contain alphanumeric characters, periods, dashes, underscores, and spaces only + Username invalid, ensure it is set properly in System -> Network Must be 48 characters, and lowercase a-z only Must be between 1 and 65535 Cancel @@ -467,7 +468,7 @@ Web Token Web token used for creating public lobbies. It is a 48-character string containing only lowercase a-z. Web Username - Username to be shown in multiplayer lobbies. It must be 4–20 characters. + Username to be shown in multiplayer lobbies. It must be 4–20 characters, containing only alphanumeric characters, dashes, periods, underscores, and spaces. Network