[android] Better error handling for username collision/validity (#76)
All checks were successful
eden-build / source (push) Successful in 6m43s
eden-build / linux (push) Successful in 26m6s
eden-build / windows (msvc) (push) Successful in 30m3s
eden-build / android (push) Successful in 32m19s

also removed the generate button from sw keyboard

Signed-off-by: crueter <crueter@eden-emu.dev>

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/76
This commit is contained in:
crueter 2025-07-18 20:02:13 +02:00
parent 2aab37b516
commit 1a35aef644
No known key found for this signature in database
GPG key ID: 425ACD2D4830EBC6
7 changed files with 87 additions and 34 deletions

View file

@ -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}) set(install_args ${install_args} install-tool --outputdir ${base_path} ${host} desktop ${target})
else() else()
set(prefix "${base_path}/${target}/${arch_path}") 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) if (YUZU_USE_QT_MULTIMEDIA)
set(install_args ${install_args} qtmultimedia) set(install_args ${install_args} qtmultimedia)

View file

@ -34,6 +34,7 @@ import org.yuzu.yuzu_emu.databinding.ItemBanListBinding
import org.yuzu.yuzu_emu.databinding.ItemButtonNetplayBinding import org.yuzu.yuzu_emu.databinding.ItemButtonNetplayBinding
import org.yuzu.yuzu_emu.databinding.ItemTextNetplayBinding import org.yuzu.yuzu_emu.databinding.ItemTextNetplayBinding
import org.yuzu.yuzu_emu.features.settings.model.StringSetting 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.network.NetPlayManager
import org.yuzu.yuzu_emu.utils.CompatUtils import org.yuzu.yuzu_emu.utils.CompatUtils
import org.yuzu.yuzu_emu.utils.GameHelper import org.yuzu.yuzu_emu.utils.GameHelper
@ -102,8 +103,16 @@ class NetPlayDialog(context: Context) : BottomSheetDialog(context) {
dismiss() dismiss()
} }
btnLobbyBrowser.setOnClickListener { btnLobbyBrowser.setOnClickListener {
LobbyBrowser(context).show() if (!NetDataValidators.username()) {
dismiss() 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 { 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) context.getString(R.string.multiplayer_required)
) { ) {
override fun validate(s: String): Boolean { 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) context.getString(R.string.multiplayer_token_required)
) { ) {
override fun validate(s: String): Boolean { override fun validate(s: String): Boolean {
if (s != context.getString(R.string.multiplayer_public_visibility)) { return NetDataValidators.roomVisibility(s, context)
return true;
}
val token = StringSetting.WEB_TOKEN.getString()
return token.matches(Regex("[a-z]{48}"))
} }
} }
@ -403,12 +407,7 @@ class NetPlayDialog(context: Context) : BottomSheetDialog(context) {
context.getString(R.string.multiplayer_ip_error) context.getString(R.string.multiplayer_ip_error)
) { ) {
override fun validate(s: String): Boolean { override fun validate(s: String): Boolean {
return try { return NetDataValidators.ipAddress(s)
InetAddress.getByName(s)
s.length >= 7
} catch (_: Exception) {
false
}
} }
} }
@ -418,7 +417,7 @@ class NetPlayDialog(context: Context) : BottomSheetDialog(context) {
context.getString(R.string.multiplayer_username_error) context.getString(R.string.multiplayer_username_error)
) { ) {
override fun validate(s: String): Boolean { 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) context.getString(R.string.multiplayer_port_error)
) { ) {
override fun validate(s: String): Boolean { override fun validate(s: String): Boolean {
return s.toIntOrNull() in 1..65535 return NetDataValidators.port(s)
} }
} }

View file

@ -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.LongSetting
import org.yuzu.yuzu_emu.features.settings.model.ShortSetting import org.yuzu.yuzu_emu.features.settings.model.ShortSetting
import org.yuzu.yuzu_emu.features.settings.model.StringSetting 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.GpuDriverHelper
import org.yuzu.yuzu_emu.utils.NativeConfig import org.yuzu.yuzu_emu.utils.NativeConfig
@ -300,9 +301,7 @@ abstract class SettingsItem(
val chars = "abcdefghijklmnopqrstuvwxyz" val chars = "abcdefghijklmnopqrstuvwxyz"
(1..48).map { chars.random() }.joinToString("") (1..48).map { chars.random() }.joinToString("")
}, },
validator = { s -> validator = NetDataValidators::token,
s?.matches(Regex("[a-z]{48}")) == true
},
errorId = R.string.multiplayer_token_error errorId = R.string.multiplayer_token_error
) )
) )
@ -312,9 +311,7 @@ abstract class SettingsItem(
StringSetting.WEB_USERNAME, StringSetting.WEB_USERNAME,
titleId = R.string.web_username, titleId = R.string.web_username,
descriptionId = R.string.web_username_description, descriptionId = R.string.web_username_description,
validator = { s -> validator = NetDataValidators::username,
s?.length in 4..20
},
errorId = R.string.multiplayer_username_error errorId = R.string.multiplayer_username_error
) )
) )

View file

@ -154,8 +154,6 @@ class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener
stringInputBinding.generate.setOnClickListener { stringInputBinding.generate.setOnClickListener {
stringInputBinding.editText.setText(onGenerate()) stringInputBinding.editText.setText(onGenerate())
} }
} else {
stringInputBinding.generate.isVisible = false
} }
val validator = item.validator val validator = item.validator
@ -179,8 +177,9 @@ class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener
} }
override fun afterTextChanged(s: Editable?) { override fun afterTextChanged(s: Editable?) {
stringInputBinding.editText.error = val isValid = validator(s.toString())
if (validator(s.toString())) null else requireContext().getString(item.errorId) stringInputBinding.editTextLayout.isErrorEnabled = !isValid
stringInputBinding.editTextLayout.error = if (isValid) null else requireContext().getString(item.errorId)
} }
} }

View file

@ -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
}
}

View file

@ -27,6 +27,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="15dp" android:layout_marginTop="15dp"
android:text="@string/generate" android:text="@string/generate"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="@+id/edit_text_layout" app:layout_constraintEnd_toEndOf="@+id/edit_text_layout"
app:layout_constraintTop_toBottomOf="@+id/edit_text_layout" /> app:layout_constraintTop_toBottomOf="@+id/edit_text_layout" />

View file

@ -148,18 +148,18 @@
<string name="multiplayer_join_room_failed">Failed to join room</string> <string name="multiplayer_join_room_failed">Failed to join room</string>
<string name="multiplayer_name_invalid">Name is too short</string> <string name="multiplayer_name_invalid">Name is too short</string>
<string name="multiplayer_address_invalid">Invalid address</string> <string name="multiplayer_address_invalid">Invalid address</string>
<string name="multiplayer_port_invalid">Invalid port!</string> <string name="multiplayer_port_invalid">Invalid port</string>
<string name="multiplayer_exit_room">Exit Room</string> <string name="multiplayer_exit_room">Exit Room</string>
<string name="multiplayer_network_error">Network error</string> <string name="multiplayer_network_error">Network error</string>
<string name="multiplayer_lost_connection">Lost connection</string> <string name="multiplayer_lost_connection">Lost connection</string>
<string name="multiplayer_name_collision">Name collision</string> <string name="multiplayer_name_collision">Username already taken</string>
<string name="multiplayer_mac_collision">MAC Address collision</string> <string name="multiplayer_mac_collision">MAC Address collision</string>
<string name="multiplayer_console_id_collision">Console ID collision</string> <string name="multiplayer_console_id_collision">Console ID collision</string>
<string name="multiplayer_wrong_version">Wrong version</string> <string name="multiplayer_wrong_version">Wrong version</string>
<string name="multiplayer_wrong_password">Wrong password</string> <string name="multiplayer_wrong_password">Wrong password</string>
<string name="multiplayer_could_not_connect">Could not connect</string> <string name="multiplayer_could_not_connect">Could not connect</string>
<string name="multiplayer_room_is_full">Room is full</string> <string name="multiplayer_room_is_full">Room is full</string>
<string name="multiplayer_host_banned">Host banned</string> <string name="multiplayer_host_banned">You are banned from this room</string>
<string name="multiplayer_permission_denied">Permission denied</string> <string name="multiplayer_permission_denied">Permission denied</string>
<string name="multiplayer_no_such_user">No such user</string> <string name="multiplayer_no_such_user">No such user</string>
<string name="multiplayer_already_in_room">Already in room</string> <string name="multiplayer_already_in_room">Already in room</string>
@ -221,7 +221,8 @@
<string name="multiplayer_required">Required</string> <string name="multiplayer_required">Required</string>
<string name="multiplayer_token_required">Web Token required, go to Advanced Settings -> System -> Network</string> <string name="multiplayer_token_required">Web Token required, go to Advanced Settings -> System -> Network</string>
<string name="multiplayer_ip_error">Invalid IP format</string> <string name="multiplayer_ip_error">Invalid IP format</string>
<string name="multiplayer_username_error">Must be between 420 characters</string> <string name="multiplayer_username_error">Must be between 420 characters, and contain alphanumeric characters, periods, dashes, underscores, and spaces only</string>
<string name="multiplayer_nickname_invalid">Username invalid, ensure it is set properly in System -> Network</string>
<string name="multiplayer_token_error">Must be 48 characters, and lowercase a-z only</string> <string name="multiplayer_token_error">Must be 48 characters, and lowercase a-z only</string>
<string name="multiplayer_port_error">Must be between 1 and 65535</string> <string name="multiplayer_port_error">Must be between 1 and 65535</string>
<string name="cancel">Cancel</string> <string name="cancel">Cancel</string>
@ -467,7 +468,7 @@
<string name="web_token">Web Token</string> <string name="web_token">Web Token</string>
<string name="web_token_description">Web token used for creating public lobbies. It is a 48-character string containing only lowercase a-z.</string> <string name="web_token_description">Web token used for creating public lobbies. It is a 48-character string containing only lowercase a-z.</string>
<string name="web_username">Web Username</string> <string name="web_username">Web Username</string>
<string name="web_username_description">Username to be shown in multiplayer lobbies. It must be 420 characters.</string> <string name="web_username_description">Username to be shown in multiplayer lobbies. It must be 420 characters, containing only alphanumeric characters, dashes, periods, underscores, and spaces.</string>
<string name="network">Network</string> <string name="network">Network</string>
<!-- Graphics settings strings --> <!-- Graphics settings strings -->