From bfdc9e312f10e3b6efa043bcd3498051cc2e853a Mon Sep 17 00:00:00 2001 From: Timotej Leginus Date: Sat, 10 Jun 2023 22:52:50 +0200 Subject: [PATCH 01/10] Fixed network interfaces on Android --- src/android/app/src/main/AndroidManifest.xml | 1 + .../java/org/yuzu/yuzu_emu/YuzuApplication.kt | 2 + .../features/settings/model/Settings.kt | 2 + .../features/settings/model/StringSetting.kt | 6 ++- .../settings/model/view/SettingsItem.kt | 1 + .../settings/model/view/TextSetting.kt | 31 +++++++++++++ .../features/settings/ui/SettingsAdapter.kt | 41 ++++++++++++++++- .../settings/ui/SettingsFragmentPresenter.kt | 25 ++++++++++- .../ui/viewholder/TextSettingViewHolder.kt | 44 +++++++++++++++++++ .../org/yuzu/yuzu_emu/utils/NetworkHelper.kt | 20 +++++++++ src/android/app/src/main/jni/config.cpp | 5 +++ .../app/src/main/res/values/strings.xml | 5 +++ src/common/settings.h | 1 + .../internal_network/network_interface.cpp | 33 +++++++++++++- 14 files changed, 212 insertions(+), 5 deletions(-) create mode 100644 src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/TextSetting.kt create mode 100644 src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/TextSettingViewHolder.kt create mode 100644 src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NetworkHelper.kt diff --git a/src/android/app/src/main/AndroidManifest.xml b/src/android/app/src/main/AndroidManifest.xml index eef5660425..ddf52ce961 100644 --- a/src/android/app/src/main/AndroidManifest.xml +++ b/src/android/app/src/main/AndroidManifest.xml @@ -11,6 +11,7 @@ SPDX-License-Identifier: GPL-3.0-or-later + diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt index 4c947b7869..9fd9add6de 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt @@ -10,6 +10,7 @@ import android.content.Context import org.yuzu.yuzu_emu.utils.DirectoryInitialization import org.yuzu.yuzu_emu.utils.DocumentsTree import org.yuzu.yuzu_emu.utils.GpuDriverHelper +import org.yuzu.yuzu_emu.utils.NetworkHelper import java.io.File fun Context.getPublicFilesDir() : File = getExternalFilesDir(null) ?: filesDir @@ -46,6 +47,7 @@ class YuzuApplication : Application() { documentsTree = DocumentsTree() DirectoryInitialization.start(applicationContext) GpuDriverHelper.initializeDriverParameters(applicationContext) + NetworkHelper.setRoutes(applicationContext) NativeLibrary.logDeviceInfo() createNotificationChannels(); diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt index 8df20b928f..4eefa0bcce 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt @@ -107,6 +107,7 @@ class Settings { const val SECTION_RENDERER = "Renderer" const val SECTION_AUDIO = "Audio" const val SECTION_CPU = "Cpu" + const val SECTION_NETWORK = "Network" const val SECTION_THEME = "Theme" const val SECTION_DEBUG = "Debug" @@ -151,6 +152,7 @@ class Settings { SECTION_SYSTEM, SECTION_RENDERER, SECTION_AUDIO, + SECTION_NETWORK, SECTION_CPU ) } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/StringSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/StringSetting.kt index 63f95690c7..999f080cb0 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/StringSetting.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/StringSetting.kt @@ -8,7 +8,8 @@ enum class StringSetting( override val section: String, override val defaultValue: String ) : AbstractStringSetting { - CUSTOM_RTC("custom_rtc", Settings.SECTION_SYSTEM, "0"); + CUSTOM_RTC("custom_rtc", Settings.SECTION_SYSTEM, "0"), + NETWORK_ROUTE("network_route", Settings.SECTION_NETWORK, ";;;;"); override var string: String = defaultValue @@ -27,7 +28,8 @@ enum class StringSetting( companion object { private val NOT_RUNTIME_EDITABLE = listOf( - CUSTOM_RTC + CUSTOM_RTC, + NETWORK_ROUTE ) fun from(key: String): StringSetting? = StringSetting.values().firstOrNull { it.key == key } 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 07520849ec..d12b9331ee 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 @@ -35,5 +35,6 @@ abstract class SettingsItem( const val TYPE_STRING_SINGLE_CHOICE = 5 const val TYPE_DATETIME_SETTING = 6 const val TYPE_RUNNABLE = 7 + const val TYPE_TEXT_SETTING = 8 } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/TextSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/TextSetting.kt new file mode 100644 index 0000000000..c1fc51c16f --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/TextSetting.kt @@ -0,0 +1,31 @@ +// SPDX-FileCopyrightText: 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.yuzu.yuzu_emu.features.settings.model.view + +import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting +import org.yuzu.yuzu_emu.features.settings.model.AbstractStringSetting + +class TextSetting( + setting: AbstractSetting?, + titleId: Int, + descriptionId: Int, + val key: String? = null, + private val defaultValue: String? = null +) : SettingsItem(setting, titleId, descriptionId) { + override val type = TYPE_TEXT_SETTING + + val value: String + get() = if (setting != null) { + val setting = setting as AbstractStringSetting + setting.string + } else { + defaultValue!! + } + + fun setSelectedValue(string: String): AbstractStringSetting { + val stringSetting = setting as AbstractStringSetting + stringSetting.string = string + return stringSetting + } +} \ No newline at end of file diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt index 1eb4899fcb..2dc205a7f5 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt @@ -7,13 +7,14 @@ import android.content.Context import android.content.DialogInterface import android.icu.util.Calendar import android.icu.util.TimeZone +import android.text.TextWatcher import android.text.format.DateFormat import android.view.LayoutInflater import android.view.ViewGroup import android.widget.TextView import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity -import androidx.fragment.app.setFragmentResultListener +import androidx.core.widget.doAfterTextChanged import androidx.recyclerview.widget.RecyclerView import com.google.android.material.datepicker.MaterialDatePicker import com.google.android.material.dialog.MaterialAlertDialogBuilder @@ -21,6 +22,7 @@ import com.google.android.material.slider.Slider import com.google.android.material.timepicker.MaterialTimePicker import com.google.android.material.timepicker.TimeFormat import org.yuzu.yuzu_emu.R +import org.yuzu.yuzu_emu.databinding.DialogEditTextBinding import org.yuzu.yuzu_emu.databinding.DialogSliderBinding import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding import org.yuzu.yuzu_emu.databinding.ListItemSettingSwitchBinding @@ -83,6 +85,10 @@ class SettingsAdapter( RunnableViewHolder(ListItemSettingBinding.inflate(inflater), this) } + SettingsItem.TYPE_TEXT_SETTING -> { + TextSettingViewHolder(ListItemSettingBinding.inflate(inflater), this) + } + else -> { // TODO: Create an error view since we can't return null now HeaderViewHolder(ListItemSettingsHeaderBinding.inflate(inflater), this) @@ -167,6 +173,7 @@ class SettingsAdapter( .setSelection(storedTime) .setTitleText(R.string.select_rtc_date) .build() + val timePicker: MaterialTimePicker = MaterialTimePicker.Builder() .setTimeFormat(timeFormat) .setHour(calendar.get(Calendar.HOUR_OF_DAY)) @@ -199,6 +206,38 @@ class SettingsAdapter( ) } + fun onTextSettingClick(item: TextSetting, position: Int) { + clickedItem = item + clickedPosition = position + var value = item.value + + val inflater = LayoutInflater.from(context) + val editTextBinding = DialogEditTextBinding.inflate(inflater) + + editTextBinding.editText.setText(value) + + editTextBinding.editText.doAfterTextChanged { + value = it.toString() + } + + dialog = MaterialAlertDialogBuilder(context) + .setTitle(item.nameId) + .setView(editTextBinding.root) + .setPositiveButton(android.R.string.ok) { _, _ -> + if (item.value != value) { + fragmentView.onSettingChanged() + } + notifyItemChanged(clickedPosition) + + val setting = item.setSelectedValue(value) + fragmentView.putSetting(setting) + clickedItem = null + } + .setNegativeButton(android.R.string.cancel, defaultCancelListener) + .show() + + } + fun onSliderClick(item: SliderSetting, position: Int) { clickedItem = item clickedPosition = position diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt index 061046b2e7..e64f3760b9 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt @@ -7,7 +7,6 @@ import android.content.SharedPreferences import android.os.Build import android.text.TextUtils import androidx.preference.PreferenceManager -import com.google.android.material.dialog.MaterialAlertDialogBuilder import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.YuzuApplication import org.yuzu.yuzu_emu.features.settings.model.AbstractBooleanSetting @@ -67,6 +66,7 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView) Settings.SECTION_SYSTEM -> addSystemSettings(sl) Settings.SECTION_RENDERER -> addGraphicsSettings(sl) Settings.SECTION_AUDIO -> addAudioSettings(sl) + Settings.SECTION_NETWORK -> addNetworkSettings(sl) Settings.SECTION_THEME -> addThemeSettings(sl) Settings.SECTION_DEBUG -> addDebugSettings(sl) else -> { @@ -102,6 +102,13 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView) Settings.SECTION_RENDERER ) ) + add( + SubmenuSetting( + R.string.preferences_network, + 0, + Settings.SECTION_NETWORK + ) + ) add( SubmenuSetting( R.string.preferences_audio, @@ -437,6 +444,22 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView) } } + private fun addNetworkSettings(sl: ArrayList) { + settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.preferences_network)) + + sl.apply { + add( + TextSetting( + StringSetting.NETWORK_ROUTE, + R.string.set_network_route, + R.string.network_route_desc, + StringSetting.NETWORK_ROUTE.key, + StringSetting.NETWORK_ROUTE.defaultValue + ) + ) + } + } + private fun addDebugSettings(sl: ArrayList) { settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.preferences_debug)) sl.apply { diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/TextSettingViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/TextSettingViewHolder.kt new file mode 100644 index 0000000000..9557b07fca --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/TextSettingViewHolder.kt @@ -0,0 +1,44 @@ +// SPDX-FileCopyrightText: 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.yuzu.yuzu_emu.features.settings.ui.viewholder + +import android.view.View +import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding +import org.yuzu.yuzu_emu.features.settings.model.view.DateTimeSetting +import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem +import org.yuzu.yuzu_emu.features.settings.model.view.TextSetting +import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter +import java.time.Instant +import java.time.ZoneId +import java.time.ZonedDateTime +import java.time.format.DateTimeFormatter +import java.time.format.FormatStyle + +class TextSettingViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) : + SettingViewHolder(binding.root, adapter) { + private lateinit var setting: TextSetting + + override fun bind(item: SettingsItem) { + setting = item as TextSetting + + binding.textSettingName.setText(item.nameId) + if (item.descriptionId != 0) { + binding.textSettingDescription.setText(item.descriptionId) + binding.textSettingDescription.visibility = View.VISIBLE + } + } + + override fun onClick(clicked: View) { + if (setting.isEditable) { + adapter.onTextSettingClick(setting, bindingAdapterPosition) + } + } + + override fun onLongClick(clicked: View): Boolean { + if (setting.isEditable) { + return adapter.onLongClick(setting.setting!!, bindingAdapterPosition) + } + return false + } +} diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NetworkHelper.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NetworkHelper.kt new file mode 100644 index 0000000000..b5b32b5aad --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NetworkHelper.kt @@ -0,0 +1,20 @@ +package org.yuzu.yuzu_emu.utils + +import android.content.Context +import android.net.ConnectivityManager + + + + +object NetworkHelper { + fun setRoutes(context: Context) { + val connectivity = + context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager + + val lp = connectivity.getLinkProperties(connectivity.activeNetwork) + + // lp.routes + + + } +} \ No newline at end of file diff --git a/src/android/app/src/main/jni/config.cpp b/src/android/app/src/main/jni/config.cpp index 2d622a048c..b96dd1f7ee 100644 --- a/src/android/app/src/main/jni/config.cpp +++ b/src/android/app/src/main/jni/config.cpp @@ -244,6 +244,11 @@ void Config::ReadValues() { ReadSetting("Audio", Settings::values.audio_output_device_id); ReadSetting("Audio", Settings::values.volume); + // Network + + Settings::values.network_route = + config->GetString("Network", "network_route", ""); + // Miscellaneous // log_filter has a different default here than from common Settings::values.log_filter = "*:Info"; diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index 0ae69afb47..a2358a93ab 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -151,6 +151,10 @@ Allows you to set a custom real-time clock separate from your current system time. Set custom RTC + + Sets the default network route + Set network route + API Accuracy level @@ -203,6 +207,7 @@ Settings General System + Network Graphics Audio Theme and color diff --git a/src/common/settings.h b/src/common/settings.h index 9682281b08..0613d6f596 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -581,6 +581,7 @@ struct Values { // Network Setting network_interface{std::string(), "network_interface"}; + Setting network_route{std::string(), "network_route"}; // WebService Setting enable_telemetry{true, "enable_telemetry"}; diff --git a/src/core/internal_network/network_interface.cpp b/src/core/internal_network/network_interface.cpp index 4c909a6d3c..0c84db932c 100644 --- a/src/core/internal_network/network_interface.cpp +++ b/src/core/internal_network/network_interface.cpp @@ -6,6 +6,8 @@ #include #include +#include + #include "common/bit_cast.h" #include "common/common_types.h" #include "common/logging/log.h" @@ -20,6 +22,8 @@ #include #include #include +#include + #endif namespace Network { @@ -91,8 +95,31 @@ std::vector GetAvailableNetworkInterfaces() { return result; } -#else +#elif defined(__ANDROID__) +std::vector GetAvailableNetworkInterfaces() { + std::vector result; + std::vector route_parts; + + boost::split(route_parts, Settings::values.network_route.GetValue(), boost::is_any_of(";")); + + struct in_addr ip{}, sm{}, gw{}; + + inet_pton(AF_INET, route_parts[1].c_str(), &ip); + inet_pton(AF_INET, route_parts[2].c_str(), &sm); + inet_pton(AF_INET, route_parts[3].c_str(), &gw); + + result.emplace_back(NetworkInterface{ + .name{route_parts[0]}, + .ip_address{ip}, + .subnet_mask{sm}, + .gateway{gw} + }); + + return result; +} + +#else std::vector GetAvailableNetworkInterfaces() { struct ifaddrs* ifaddr = nullptr; @@ -187,6 +214,10 @@ std::vector GetAvailableNetworkInterfaces() { #endif std::optional GetSelectedNetworkInterface() { +#ifdef __ANDROID__ + Network::SelectFirstNetworkInterface(); // TODO ANDROID +#endif + const auto& selected_network_interface = Settings::values.network_interface.GetValue(); const auto network_interfaces = Network::GetAvailableNetworkInterfaces(); if (network_interfaces.empty()) { From c4a0dbfb20f01dbc41ad8a064fb4284a98fdb138 Mon Sep 17 00:00:00 2001 From: Timotej Leginus Date: Sun, 11 Jun 2023 03:04:32 +0200 Subject: [PATCH 02/10] Supports manual network IF loading and network IF can be acquired via Kotlin --- .../org/yuzu/yuzu_emu/utils/NetworkHelper.kt | 26 +++++++++++++++---- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NetworkHelper.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NetworkHelper.kt index b5b32b5aad..bfb80b5014 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NetworkHelper.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NetworkHelper.kt @@ -1,20 +1,36 @@ +// SPDX-FileCopyrightText: 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + package org.yuzu.yuzu_emu.utils import android.content.Context import android.net.ConnectivityManager - - - +import androidx.preference.PreferenceManager object NetworkHelper { fun setRoutes(context: Context) { val connectivity = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager - val lp = connectivity.getLinkProperties(connectivity.activeNetwork) + val lp = connectivity.getLinkProperties(connectivity.activeNetwork) ?: return - // lp.routes + val ifName = lp.interfaceName + val addr = lp.linkAddresses[0] + val cidr = addr.prefixLength + val bits = 0xffffffff xor ((1 shl 32 - cidr)).toLong() - 1 + val mask = String.format( + "%d.%d.%d.%d", + bits and 0x0000000000ff000000L shr 24, + bits and 0x000000000000ff0000L shr 16, + bits and 0x00000000000000ff00L shr 8, + bits and 0x0000000000000000ffL shr 0 + ) + val gw = lp.routes.last { it.isDefaultRoute }.gateway?.hostAddress + + val settingFormattedRoute = "$ifName;$addr;$mask;$gw" + + // TODO set this value to settings } } \ No newline at end of file From 17693651b3534284beb29f19eea65b089202b05f Mon Sep 17 00:00:00 2001 From: Timotej Leginus Date: Sun, 11 Jun 2023 20:12:53 +0200 Subject: [PATCH 03/10] Added multiplayer settings, now can direct connect to a room --- .../src/ea/res/drawable/ic_multiplayer.xml | 10 ++ .../java/org/yuzu/yuzu_emu/NativeLibrary.kt | 11 ++ .../java/org/yuzu/yuzu_emu/YuzuApplication.kt | 2 + .../features/settings/model/Settings.kt | 7 + .../settings/ui/SettingsFragmentPresenter.kt | 134 ++++++++++++++++++ .../fragments/HomeSettingsFragment.kt | 5 + .../yuzu/yuzu_emu/utils/MultiplayerHelper.kt | 21 +++ .../org/yuzu/yuzu_emu/utils/NetworkHelper.kt | 1 - src/android/app/src/main/jni/native.cpp | 66 +++++++++ .../app/src/main/res/values/strings.xml | 9 ++ src/yuzu/multiplayer/direct_connect.cpp | 1 + 11 files changed, 266 insertions(+), 1 deletion(-) create mode 100644 src/android/app/src/ea/res/drawable/ic_multiplayer.xml create mode 100644 src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/MultiplayerHelper.kt diff --git a/src/android/app/src/ea/res/drawable/ic_multiplayer.xml b/src/android/app/src/ea/res/drawable/ic_multiplayer.xml new file mode 100644 index 0000000000..6457435a41 --- /dev/null +++ b/src/android/app/src/ea/res/drawable/ic_multiplayer.xml @@ -0,0 +1,10 @@ + + + diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt index c11b6bc169..022dd28fcd 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt @@ -464,6 +464,17 @@ object NativeLibrary { */ external fun submitInlineKeyboardInput(key_code: Int) + /** + * Connects to a room (similar with desktop version's "direct connect") + */ + external fun connectToRoom(nickname: String, server_addr: String, server_port: Int, password: String) + + /** + * Returns the state of the room member (client) + * @return The state as a string + */ + external fun getRoomMemberState(): String + /** * Button type for use in onTouchEvent */ diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt index 9fd9add6de..2e358cf8fa 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt @@ -10,6 +10,7 @@ import android.content.Context import org.yuzu.yuzu_emu.utils.DirectoryInitialization import org.yuzu.yuzu_emu.utils.DocumentsTree import org.yuzu.yuzu_emu.utils.GpuDriverHelper +import org.yuzu.yuzu_emu.utils.MultiplayerHelper import org.yuzu.yuzu_emu.utils.NetworkHelper import java.io.File @@ -48,6 +49,7 @@ class YuzuApplication : Application() { DirectoryInitialization.start(applicationContext) GpuDriverHelper.initializeDriverParameters(applicationContext) NetworkHelper.setRoutes(applicationContext) + MultiplayerHelper.initRoom(applicationContext) NativeLibrary.logDeviceInfo() createNotificationChannels(); diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt index 4eefa0bcce..858c103fba 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt @@ -109,6 +109,7 @@ class Settings { const val SECTION_CPU = "Cpu" const val SECTION_NETWORK = "Network" const val SECTION_THEME = "Theme" + const val SECTION_MULTIPLAYER = "Multiplayer" const val SECTION_DEBUG = "Debug" const val PREF_OVERLAY_INIT = "OverlayInit" @@ -131,6 +132,12 @@ class Settings { const val PREF_BUTTON_TOGGLE_13 = "buttonToggle13" const val PREF_BUTTON_TOGGLE_14 = "buttonToggle14" + const val PREF_ROOM_ADDRESS = "MultiplayerRoom_ServerAddress" + const val PREF_ROOM_PORT = "MultiplayerRoom_ServerPort" + const val PREF_ROOM_NICKNAME = "MultiplayerRoom_Nickname" + const val PREF_ROOM_PASSWORD = "MultiplayerRoom_Password" + const val PREF_ROOM_CONNECT_ON_START = "MultiplayerRoom_ConnectOnStart" + const val PREF_MENU_SETTINGS_JOYSTICK_REL_CENTER = "EmulationMenuSettings_JoystickRelCenter" const val PREF_MENU_SETTINGS_DPAD_SLIDE = "EmulationMenuSettings_DpadSlideEnable" const val PREF_MENU_SETTINGS_HAPTICS = "EmulationMenuSettings_Haptics" diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt index e64f3760b9..9f12261726 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt @@ -12,6 +12,7 @@ import org.yuzu.yuzu_emu.YuzuApplication import org.yuzu.yuzu_emu.features.settings.model.AbstractBooleanSetting import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting +import org.yuzu.yuzu_emu.features.settings.model.AbstractStringSetting import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting import org.yuzu.yuzu_emu.features.settings.model.IntSetting import org.yuzu.yuzu_emu.features.settings.model.Settings @@ -67,6 +68,7 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView) Settings.SECTION_RENDERER -> addGraphicsSettings(sl) Settings.SECTION_AUDIO -> addAudioSettings(sl) Settings.SECTION_NETWORK -> addNetworkSettings(sl) + Settings.SECTION_MULTIPLAYER -> addMultiplayerSettings(sl) Settings.SECTION_THEME -> addThemeSettings(sl) Settings.SECTION_DEBUG -> addDebugSettings(sl) else -> { @@ -460,6 +462,138 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView) } } + private fun addMultiplayerSettings(sl: ArrayList) { + settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.preferences_multiplayer)) + + sl.apply { + val serverAddress: AbstractStringSetting = object : AbstractStringSetting { + override var string: String + get() = preferences.getString(Settings.PREF_ROOM_ADDRESS, "") ?: "" + set(value) { + preferences.edit() + .putString(Settings.PREF_ROOM_ADDRESS, value) + .apply() + } + override val key: String? = null + override val section: String? = null + override val isRuntimeEditable: Boolean = false + override val valueAsString: String + get() = preferences.getString(Settings.PREF_ROOM_ADDRESS, "") ?: "" + override val defaultValue: Any = -1 + } + + val serverPort: AbstractStringSetting = object : AbstractStringSetting { + override var string: String + get() = preferences.getString(Settings.PREF_ROOM_PORT, "24872") ?: "24872" + set(value) { + preferences.edit() + .putString(Settings.PREF_ROOM_PORT, value) + .apply() + } + override val key: String? = null + override val section: String? = null + override val isRuntimeEditable: Boolean = false + override val valueAsString: String + get() = preferences.getString(Settings.PREF_ROOM_PORT, "24872") ?: "24872" + override val defaultValue: Any = "24872" + } + + val nickname: AbstractStringSetting = object : AbstractStringSetting { + override var string: String + get() = preferences.getString(Settings.PREF_ROOM_NICKNAME, "") ?: "" + set(value) { + preferences.edit() + .putString(Settings.PREF_ROOM_NICKNAME, value) + .apply() + } + override val key: String? = null + override val section: String? = null + override val isRuntimeEditable: Boolean = false + override val valueAsString: String + get() = preferences.getString(Settings.PREF_ROOM_NICKNAME, "") ?: "" + override val defaultValue: Any = -1 + } + + val password: AbstractStringSetting = object : AbstractStringSetting { + override var string: String + get() = preferences.getString(Settings.PREF_ROOM_PASSWORD, "") ?: "" + set(value) { + preferences.edit() + .putString(Settings.PREF_ROOM_PASSWORD, value) + .apply() + } + override val key: String? = null + override val section: String? = null + override val isRuntimeEditable: Boolean = false + override val valueAsString: String + get() = preferences.getString(Settings.PREF_ROOM_PASSWORD, "") ?: "" + override val defaultValue: Any = -1 + } + + val connectOnStart: AbstractBooleanSetting = object : AbstractBooleanSetting { + override var boolean: Boolean + get() = preferences.getBoolean(Settings.PREF_ROOM_CONNECT_ON_START, false) + set(value) { + preferences.edit() + .putBoolean(Settings.PREF_ROOM_CONNECT_ON_START, value) + .apply() + } + override val key: String? = null + override val section: String? = null + override val isRuntimeEditable: Boolean = false + override val valueAsString: String + get() = preferences.getBoolean(Settings.PREF_ROOM_CONNECT_ON_START, false).toString() + override val defaultValue: Any = -1 + } + + add( + TextSetting( + serverAddress, + R.string.multiplayer_room_server_address, + 0, + "", + "" + ) + ) + add( + TextSetting( + serverPort, + R.string.multiplayer_room_server_port, + 0, + "", + "" + ) + ) + add( + TextSetting( + nickname, + R.string.multiplayer_room_nickname, + 0, + "", + "" + ) + ) + add( + TextSetting( + password, + R.string.multiplayer_room_password, + 0, + "", + "" + ) + ) + add( + SwitchSetting( + connectOnStart, + R.string.multiplayer_room_connect_on_start, + 0, + "", + false + ) + ) + } + } + private fun addDebugSettings(sl: ArrayList) { settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.preferences_debug)) sl.apply { diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt index bdc3375016..addbdd81d8 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt @@ -84,6 +84,11 @@ class HomeSettingsFragment : Fragment() { R.string.theme_and_color_description, R.drawable.ic_palette ) { SettingsActivity.launch(requireContext(), Settings.SECTION_THEME, "") }, + HomeSetting( + R.string.preferences_multiplayer, + R.string.multiplayer_description, + R.drawable.ic_multiplayer + ) { SettingsActivity.launch(requireContext(), Settings.SECTION_MULTIPLAYER, "") }, HomeSetting( R.string.install_gpu_driver, R.string.install_gpu_driver_description, diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/MultiplayerHelper.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/MultiplayerHelper.kt new file mode 100644 index 0000000000..167305593c --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/MultiplayerHelper.kt @@ -0,0 +1,21 @@ +package org.yuzu.yuzu_emu.utils + +import android.content.Context +import androidx.preference.PreferenceManager +import org.yuzu.yuzu_emu.NativeLibrary +import org.yuzu.yuzu_emu.features.settings.model.Settings + +object MultiplayerHelper { + fun initRoom(context: Context) { + val preferences = PreferenceManager.getDefaultSharedPreferences(context) + + if(preferences.getBoolean(Settings.PREF_ROOM_CONNECT_ON_START, false)) { + val addr = preferences.getString(Settings.PREF_ROOM_ADDRESS, "") ?: "" + val port = preferences.getString(Settings.PREF_ROOM_PORT, "24872") ?: "24872" + val nickname = preferences.getString(Settings.PREF_ROOM_NICKNAME, "") ?: "" + val password = preferences.getString(Settings.PREF_ROOM_PASSWORD, "") ?: "" + + NativeLibrary.connectToRoom(nickname, addr, port.toIntOrNull() ?: 0, password) + } + } +} \ No newline at end of file diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NetworkHelper.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NetworkHelper.kt index bfb80b5014..0e7ca5ba79 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NetworkHelper.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NetworkHelper.kt @@ -5,7 +5,6 @@ package org.yuzu.yuzu_emu.utils import android.content.Context import android.net.ConnectivityManager -import androidx.preference.PreferenceManager object NetworkHelper { fun setRoutes(context: Context) { diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp index b87e04b3d8..8a91c562f7 100644 --- a/src/android/app/src/main/jni/native.cpp +++ b/src/android/app/src/main/jni/native.cpp @@ -13,6 +13,8 @@ #include #include +#include +#include #include "common/detached_tasks.h" #include "common/dynamic_library.h" @@ -344,6 +346,29 @@ public: return m_software_keyboard; } + void DirectConnectToRoom(const std::string& nickname, const char* server_addr = "127.0.0.1", + u16 server_port = Network::DefaultRoomPort, + const std::string& password = "") { + auto room_network = m_system.GetRoomNetwork(); + + if (const auto member = room_network.GetRoomMember().lock()) { + // Prevent the user from trying to join a room while they are already joining. + if (member->GetState() == Network::RoomMember::State::Joining || member->IsConnected()) { + return; + } else { + member->Join(nickname, server_addr, server_port); + } + } + } + + Network::RoomMember::State GetRoomMemberState() { + if (const auto member = m_system.GetRoomNetwork().GetRoomMember().lock()) { + return member->GetState(); + } else { + return Network::RoomMember::State::Idle; + } + } + private: struct RomMetadata { std::string title; @@ -771,4 +796,45 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_submitInlineKeyboardInput(JNIEnv* env EmulationSession::GetInstance().SoftwareKeyboard()->SubmitInlineKeyboardInput(j_key_code); } +void Java_org_yuzu_yuzu_1emu_NativeLibrary_connectToRoom(JNIEnv* env, jclass clazz, + jstring nickname, + jstring server_addr, + jint server_port, + jstring password) { + EmulationSession::GetInstance().DirectConnectToRoom( + GetJString(env, nickname), + GetJString(env, server_addr).c_str(), + server_port, + GetJString(env, password) + ); +} + +jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getRoomMemberState(JNIEnv* env, jclass clazz) { + auto state = EmulationSession::GetInstance().GetRoomMemberState(); + + std::string state_str{}; + + switch(state) { + using State = Network::RoomMember::State; + + case State::Uninitialized: + state_str = "Uninitialized"; + break; + case State::Idle: + state_str = "Idle"; + break; + case State::Joining: + state_str = "Joining"; + break; + case State::Joined: + state_str = "Joined"; + break; + case State::Moderator: + state_str = "Moderator"; + break; + } + + return env->NewStringUTF(state_str.c_str()); +} + } // extern "C" diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index a2358a93ab..350eb1dedb 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -84,6 +84,7 @@ Open yuzu folder Manage yuzu\'s internal files Modify the look of the app + Play with your friends! No file manager found Could not open yuzu directory Please locate the user folder with the file manager\'s side panel manually. @@ -155,6 +156,13 @@ Sets the default network route Set network route + + Server address + Port + Nickname + Password + Connect on start + API Accuracy level @@ -211,6 +219,7 @@ Graphics Audio Theme and color + Multiplayer Debug diff --git a/src/yuzu/multiplayer/direct_connect.cpp b/src/yuzu/multiplayer/direct_connect.cpp index d71cc23a72..7ffe68bf2b 100644 --- a/src/yuzu/multiplayer/direct_connect.cpp +++ b/src/yuzu/multiplayer/direct_connect.cpp @@ -81,6 +81,7 @@ void DirectConnectWindow::Connect() { } } } + if (!ui->ip->hasAcceptableInput()) { NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::IP_ADDRESS_NOT_VALID); return; From ca6e4b88027ee7497be70b258dad6b966d78ab29 Mon Sep 17 00:00:00 2001 From: Timotej Leginus Date: Sun, 11 Jun 2023 20:21:46 +0200 Subject: [PATCH 04/10] Added JNI methods --- src/android/app/src/main/jni/native.cpp | 68 ++++++++++++++++++++++++- 1 file changed, 67 insertions(+), 1 deletion(-) diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp index cdf7622caf..4f2201a38b 100644 --- a/src/android/app/src/main/jni/native.cpp +++ b/src/android/app/src/main/jni/native.cpp @@ -13,6 +13,8 @@ #include #include +#include +#include #include #include "common/detached_tasks.h" @@ -341,6 +343,29 @@ namespace { return m_software_keyboard; } + void DirectConnectToRoom(const std::string& nickname, const char* server_addr = "127.0.0.1", + u16 server_port = Network::DefaultRoomPort, + const std::string& password = "") { + auto room_network = m_system.GetRoomNetwork(); + a + if (const auto member = room_network.GetRoomMember().lock()) { + // Prevent the user from trying to join a room while they are already joining. + if (member->GetState() == Network::RoomMember::State::Joining || member->IsConnected()) { + return; + } else { + member->Join(nickname, server_addr, server_port); + } + } + } + + Network::RoomMember::State GetRoomMemberState() { + if (const auto member = m_system.GetRoomNetwork().GetRoomMember().lock()) { + return member->GetState(); + } else { + return Network::RoomMember::State::Idle; + } + } + private: struct RomMetadata { std::string title; @@ -769,4 +794,45 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_submitInlineKeyboardInput(JNIEnv* env EmulationSession::GetInstance().SoftwareKeyboard()->SubmitInlineKeyboardInput(j_key_code); } -} // extern "C" \ No newline at end of file +void Java_org_yuzu_yuzu_1emu_NativeLibrary_connectToRoom(JNIEnv* env, jclass clazz, + jstring nickname, + jstring server_addr, + jint server_port, + jstring password) { + EmulationSession::GetInstance().DirectConnectToRoom( + GetJString(env, nickname), + GetJString(env, server_addr).c_str(), + server_port, + GetJString(env, password) + ); +} + +jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getRoomMemberState(JNIEnv* env, jclass clazz) { + auto state = EmulationSession::GetInstance().GetRoomMemberState(); + + std::string state_str{}; + + switch(state) { + using State = Network::RoomMember::State; + + case State::Uninitialized: + state_str = "Uninitialized"; + break; + case State::Idle: + state_str = "Idle"; + break; + case State::Joining: + state_str = "Joining"; + break; + case State::Joined: + state_str = "Joined"; + break; + case State::Moderator: + state_str = "Moderator"; + break; + } + + return env->NewStringUTF(state_str.c_str()); +} + +} // extern "C" From aa4003cdc878cc44634473a865a5dd7076550b5b Mon Sep 17 00:00:00 2001 From: Timotej Leginus Date: Sun, 11 Jun 2023 20:26:34 +0200 Subject: [PATCH 05/10] Implemented getting network IF info (routes) via Kotlin --- .../src/main/java/org/yuzu/yuzu_emu/utils/NetworkHelper.kt | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NetworkHelper.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NetworkHelper.kt index 0e7ca5ba79..0ce0e8f6cf 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NetworkHelper.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NetworkHelper.kt @@ -7,7 +7,7 @@ import android.content.Context import android.net.ConnectivityManager object NetworkHelper { - fun setRoutes(context: Context) { + fun getRoutes(context: Context): String { val connectivity = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager @@ -28,8 +28,6 @@ object NetworkHelper { val gw = lp.routes.last { it.isDefaultRoute }.gateway?.hostAddress - val settingFormattedRoute = "$ifName;$addr;$mask;$gw" - - // TODO set this value to settings + return "$ifName;$addr;$mask;$gw" } } \ No newline at end of file From 6be08026c0bfa0eda2141b2447ce4d68612cba5f Mon Sep 17 00:00:00 2001 From: Timotej Leginus Date: Sun, 11 Jun 2023 20:26:34 +0200 Subject: [PATCH 06/10] Implemented getting network IF info (routes) via Kotlin --- .../main/java/org/yuzu/yuzu_emu/utils/NetworkHelper.kt | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NetworkHelper.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NetworkHelper.kt index 0e7ca5ba79..805f778768 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NetworkHelper.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NetworkHelper.kt @@ -7,11 +7,11 @@ import android.content.Context import android.net.ConnectivityManager object NetworkHelper { - fun setRoutes(context: Context) { + fun getRoutes(context: Context): String { val connectivity = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager - val lp = connectivity.getLinkProperties(connectivity.activeNetwork) ?: return + val lp = connectivity.getLinkProperties(connectivity.activeNetwork) ?: return "" val ifName = lp.interfaceName val addr = lp.linkAddresses[0] @@ -28,8 +28,6 @@ object NetworkHelper { val gw = lp.routes.last { it.isDefaultRoute }.gateway?.hostAddress - val settingFormattedRoute = "$ifName;$addr;$mask;$gw" - - // TODO set this value to settings + return "$ifName;$addr;$mask;$gw" } } \ No newline at end of file From 8a1f3b7c4164ffe5042a7d3ca62cb3bf8c4af3da Mon Sep 17 00:00:00 2001 From: Timotej Leginus Date: Sun, 11 Jun 2023 21:18:25 +0200 Subject: [PATCH 07/10] Added a Wi-Fi (non-metered) only setting --- .../java/org/yuzu/yuzu_emu/YuzuApplication.kt | 2 +- .../features/settings/model/Settings.kt | 2 ++ .../settings/ui/SettingsFragmentPresenter.kt | 25 +++++++++++++++++++ .../org/yuzu/yuzu_emu/utils/NetworkHelper.kt | 15 +++++++++-- src/android/app/src/main/jni/native.cpp | 2 +- .../app/src/main/res/values/strings.xml | 2 ++ 6 files changed, 44 insertions(+), 4 deletions(-) diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt index 2e358cf8fa..2b24ef2d32 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt @@ -48,7 +48,7 @@ class YuzuApplication : Application() { documentsTree = DocumentsTree() DirectoryInitialization.start(applicationContext) GpuDriverHelper.initializeDriverParameters(applicationContext) - NetworkHelper.setRoutes(applicationContext) + NetworkHelper.getRoute(applicationContext) MultiplayerHelper.initRoom(applicationContext) NativeLibrary.logDeviceInfo() diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt index 858c103fba..bced01ab0d 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt @@ -132,6 +132,8 @@ class Settings { const val PREF_BUTTON_TOGGLE_13 = "buttonToggle13" const val PREF_BUTTON_TOGGLE_14 = "buttonToggle14" + const val PREF_FORCE_WIFI = "Network_ForceWifi" + const val PREF_ROOM_ADDRESS = "MultiplayerRoom_ServerAddress" const val PREF_ROOM_PORT = "MultiplayerRoom_ServerPort" const val PREF_ROOM_NICKNAME = "MultiplayerRoom_Nickname" diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt index 9f12261726..2bd4a7faf7 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt @@ -450,6 +450,31 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView) settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.preferences_network)) sl.apply { + val forceWifi: AbstractBooleanSetting = object : AbstractBooleanSetting { + override var boolean: Boolean + get() = preferences.getBoolean(Settings.PREF_FORCE_WIFI, false) + set(value) { + preferences.edit() + .putBoolean(Settings.PREF_FORCE_WIFI, value) + .apply() + } + override val key: String? = null + override val section: String? = null + override val isRuntimeEditable: Boolean = false + override val valueAsString: String + get() = preferences.getBoolean(Settings.PREF_FORCE_WIFI, false).toString() + override val defaultValue: Any = false + } + + add( + SwitchSetting( + forceWifi, + R.string.set_force_wifi, + R.string.force_wifi_desc, + "", + false + ) + ) add( TextSetting( StringSetting.NETWORK_ROUTE, diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NetworkHelper.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NetworkHelper.kt index 805f778768..2ed12fd57e 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NetworkHelper.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NetworkHelper.kt @@ -5,13 +5,24 @@ package org.yuzu.yuzu_emu.utils import android.content.Context import android.net.ConnectivityManager +import androidx.preference.PreferenceManager +import org.yuzu.yuzu_emu.features.settings.model.Settings object NetworkHelper { - fun getRoutes(context: Context): String { + /** + * Gets available network interface info/route info - currently the active network info. + * @return The route info separated by semicolons (interface, address, netmask, gateway), or null if no networks are available. + */ + fun getRoute(context: Context): String? { val connectivity = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager - val lp = connectivity.getLinkProperties(connectivity.activeNetwork) ?: return "" + val preferences = PreferenceManager.getDefaultSharedPreferences(context) + + if(connectivity.isActiveNetworkMetered && preferences.getBoolean(Settings.PREF_FORCE_WIFI, false)) + return null + + val lp = connectivity.getLinkProperties(connectivity.activeNetwork) ?: return null val ifName = lp.interfaceName val addr = lp.linkAddresses[0] diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp index 4f2201a38b..0ba155be03 100644 --- a/src/android/app/src/main/jni/native.cpp +++ b/src/android/app/src/main/jni/native.cpp @@ -347,7 +347,7 @@ namespace { u16 server_port = Network::DefaultRoomPort, const std::string& password = "") { auto room_network = m_system.GetRoomNetwork(); - a + if (const auto member = room_network.GetRoomMember().lock()) { // Prevent the user from trying to join a room while they are already joining. if (member->GetState() == Network::RoomMember::State::Joining || member->IsConnected()) { diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index 350eb1dedb..d1314aee64 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -155,6 +155,8 @@ Sets the default network route Set network route + Force Wi-Fi + Forces yuzu to use only Wi-Fi (non-metered connections in general) Server address From c793a9c95c6ad6e51dbc9ec62c2cbb4b6f564863 Mon Sep 17 00:00:00 2001 From: Timotej Leginus Date: Sun, 11 Jun 2023 22:02:11 +0200 Subject: [PATCH 08/10] Fixed formatting --- src/android/app/src/main/jni/config.cpp | 3 +- src/android/app/src/main/jni/native.cpp | 728 +++++++++--------- .../internal_network/network_interface.cpp | 13 +- 3 files changed, 368 insertions(+), 376 deletions(-) diff --git a/src/android/app/src/main/jni/config.cpp b/src/android/app/src/main/jni/config.cpp index b96dd1f7ee..1ee7c2205a 100644 --- a/src/android/app/src/main/jni/config.cpp +++ b/src/android/app/src/main/jni/config.cpp @@ -246,8 +246,7 @@ void Config::ReadValues() { // Network - Settings::values.network_route = - config->GetString("Network", "network_route", ""); + Settings::values.network_route = config->GetString("Network", "network_route", ""); // Miscellaneous // log_filter has a different default here than from common diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp index 0ba155be03..859468c87d 100644 --- a/src/android/app/src/main/jni/native.cpp +++ b/src/android/app/src/main/jni/native.cpp @@ -13,9 +13,9 @@ #include #include -#include -#include #include +#include +#include #include "common/detached_tasks.h" #include "common/dynamic_library.h" @@ -60,384 +60,385 @@ namespace { - class EmulationSession final { - public: - EmulationSession() { - m_vfs = std::make_shared(); - } +class EmulationSession final { +public: + EmulationSession() { + m_vfs = std::make_shared(); + } - ~EmulationSession() = default; + ~EmulationSession() = default; - static EmulationSession& GetInstance() { - return s_instance; - } + static EmulationSession& GetInstance() { + return s_instance; + } - const Core::System& System() const { - return m_system; - } + const Core::System& System() const { + return m_system; + } - Core::System& System() { - return m_system; - } + Core::System& System() { + return m_system; + } - const EmuWindow_Android& Window() const { - return *m_window; - } + const EmuWindow_Android& Window() const { + return *m_window; + } - EmuWindow_Android& Window() { - return *m_window; - } + EmuWindow_Android& Window() { + return *m_window; + } - ANativeWindow* NativeWindow() const { - return m_native_window; - } + ANativeWindow* NativeWindow() const { + return m_native_window; + } - void SetNativeWindow(ANativeWindow* native_window) { - m_native_window = native_window; - } + void SetNativeWindow(ANativeWindow* native_window) { + m_native_window = native_window; + } - void InitializeGpuDriver(const std::string& hook_lib_dir, const std::string& custom_driver_dir, - const std::string& custom_driver_name, - const std::string& file_redirect_dir) { + void InitializeGpuDriver(const std::string& hook_lib_dir, const std::string& custom_driver_dir, + const std::string& custom_driver_name, + const std::string& file_redirect_dir) { #ifdef ARCHITECTURE_arm64 - void* handle{}; - const char* file_redirect_dir_{}; - int featureFlags{}; + void* handle{}; + const char* file_redirect_dir_{}; + int featureFlags{}; - // Enable driver file redirection when renderer debugging is enabled. - if (Settings::values.renderer_debug && file_redirect_dir.size()) { - featureFlags |= ADRENOTOOLS_DRIVER_FILE_REDIRECT; - file_redirect_dir_ = file_redirect_dir.c_str(); - } + // Enable driver file redirection when renderer debugging is enabled. + if (Settings::values.renderer_debug && file_redirect_dir.size()) { + featureFlags |= ADRENOTOOLS_DRIVER_FILE_REDIRECT; + file_redirect_dir_ = file_redirect_dir.c_str(); + } - // Try to load a custom driver. - if (custom_driver_name.size()) { - handle = adrenotools_open_libvulkan( - RTLD_NOW, featureFlags | ADRENOTOOLS_DRIVER_CUSTOM, nullptr, hook_lib_dir.c_str(), - custom_driver_dir.c_str(), custom_driver_name.c_str(), file_redirect_dir_, nullptr); - } + // Try to load a custom driver. + if (custom_driver_name.size()) { + handle = adrenotools_open_libvulkan( + RTLD_NOW, featureFlags | ADRENOTOOLS_DRIVER_CUSTOM, nullptr, hook_lib_dir.c_str(), + custom_driver_dir.c_str(), custom_driver_name.c_str(), file_redirect_dir_, nullptr); + } - // Try to load the system driver. - if (!handle) { - handle = - adrenotools_open_libvulkan(RTLD_NOW, featureFlags, nullptr, hook_lib_dir.c_str(), - nullptr, nullptr, file_redirect_dir_, nullptr); - } + // Try to load the system driver. + if (!handle) { + handle = + adrenotools_open_libvulkan(RTLD_NOW, featureFlags, nullptr, hook_lib_dir.c_str(), + nullptr, nullptr, file_redirect_dir_, nullptr); + } - m_vulkan_library = std::make_shared(handle); + m_vulkan_library = std::make_shared(handle); #endif + } + + bool IsRunning() const { + std::scoped_lock lock(m_mutex); + return m_is_running; + } + + const Core::PerfStatsResults& PerfStats() const { + std::scoped_lock m_perf_stats_lock(m_perf_stats_mutex); + return m_perf_stats; + } + + void SurfaceChanged() { + if (!IsRunning()) { + return; + } + m_window->OnSurfaceChanged(m_native_window); + m_system.Renderer().NotifySurfaceChanged(); + } + + Core::SystemResultStatus InitializeEmulation(const std::string& filepath) { + std::scoped_lock lock(m_mutex); + + // Loads the configuration. + Config{}; + + // Create the render window. + m_window = std::make_unique(&m_input_subsystem, m_native_window, + m_vulkan_library); + + // Initialize system. + auto android_keyboard = std::make_unique(); + m_software_keyboard = android_keyboard.get(); + m_system.SetShuttingDown(false); + m_system.ApplySettings(); + m_system.HIDCore().ReloadInputDevices(); + m_system.SetContentProvider(std::make_unique()); + m_system.SetFilesystem(std::make_shared()); + m_system.SetAppletFrontendSet({ + nullptr, // Amiibo Settings + nullptr, // Controller Selector + nullptr, // Error Display + nullptr, // Mii Editor + nullptr, // Parental Controls + nullptr, // Photo Viewer + nullptr, // Profile Selector + std::move(android_keyboard), // Software Keyboard + nullptr, // Web Browser + }); + m_system.GetFileSystemController().CreateFactories(*m_system.GetFilesystem()); + + // Initialize account manager + m_profile_manager = std::make_unique(); + + // Load the ROM. + m_load_result = m_system.Load(EmulationSession::GetInstance().Window(), filepath); + if (m_load_result != Core::SystemResultStatus::Success) { + return m_load_result; } - bool IsRunning() const { + // Complete initialization. + m_system.GPU().Start(); + m_system.GetCpuManager().OnGpuReady(); + m_system.RegisterExitCallback([&] { HaltEmulation(); }); + + return Core::SystemResultStatus::Success; + } + + void ShutdownEmulation() { + std::scoped_lock lock(m_mutex); + + m_is_running = false; + + // Unload user input. + m_system.HIDCore().UnloadInputDevices(); + + // Shutdown the main emulated process + if (m_load_result == Core::SystemResultStatus::Success) { + m_system.DetachDebugger(); + m_system.ShutdownMainProcess(); + m_detached_tasks.WaitForAllTasks(); + m_load_result = Core::SystemResultStatus::ErrorNotInitialized; + } + + // Tear down the render window. + m_window.reset(); + } + + void PauseEmulation() { + std::scoped_lock lock(m_mutex); + m_system.Pause(); + } + + void UnPauseEmulation() { + std::scoped_lock lock(m_mutex); + m_system.Run(); + } + + void HaltEmulation() { + std::scoped_lock lock(m_mutex); + m_is_running = false; + m_cv.notify_one(); + } + + void RunEmulation() { + { std::scoped_lock lock(m_mutex); - return m_is_running; + m_is_running = true; } - const Core::PerfStatsResults& PerfStats() const { - std::scoped_lock m_perf_stats_lock(m_perf_stats_mutex); - return m_perf_stats; + // Load the disk shader cache. + if (Settings::values.use_disk_shader_cache.GetValue()) { + LoadDiskCacheProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0); + m_system.Renderer().ReadRasterizer()->LoadDiskResources( + m_system.GetApplicationProcessProgramID(), std::stop_token{}, + LoadDiskCacheProgress); + LoadDiskCacheProgress(VideoCore::LoadCallbackStage::Complete, 0, 0); } - void SurfaceChanged() { - if (!IsRunning()) { - return; - } - m_window->OnSurfaceChanged(m_native_window); - m_system.Renderer().NotifySurfaceChanged(); + void(m_system.Run()); + + if (m_system.DebuggerEnabled()) { + m_system.InitializeDebugger(); } - Core::SystemResultStatus InitializeEmulation(const std::string& filepath) { - std::scoped_lock lock(m_mutex); - - // Loads the configuration. - Config{}; - - // Create the render window. - m_window = std::make_unique(&m_input_subsystem, m_native_window, - m_vulkan_library); - - // Initialize system. - auto android_keyboard = std::make_unique(); - m_software_keyboard = android_keyboard.get(); - m_system.SetShuttingDown(false); - m_system.ApplySettings(); - m_system.HIDCore().ReloadInputDevices(); - m_system.SetContentProvider(std::make_unique()); - m_system.SetFilesystem(std::make_shared()); - m_system.SetAppletFrontendSet({ - nullptr, // Amiibo Settings - nullptr, // Controller Selector - nullptr, // Error Display - nullptr, // Mii Editor - nullptr, // Parental Controls - nullptr, // Photo Viewer - nullptr, // Profile Selector - std::move(android_keyboard), // Software Keyboard - nullptr, // Web Browser - }); - m_system.GetFileSystemController().CreateFactories(*m_system.GetFilesystem()); - - // Initialize account manager - m_profile_manager = std::make_unique(); - - // Load the ROM. - m_load_result = m_system.Load(EmulationSession::GetInstance().Window(), filepath); - if (m_load_result != Core::SystemResultStatus::Success) { - return m_load_result; - } - - // Complete initialization. - m_system.GPU().Start(); - m_system.GetCpuManager().OnGpuReady(); - m_system.RegisterExitCallback([&] { HaltEmulation(); }); - - return Core::SystemResultStatus::Success; - } - - void ShutdownEmulation() { - std::scoped_lock lock(m_mutex); - - m_is_running = false; - - // Unload user input. - m_system.HIDCore().UnloadInputDevices(); - - // Shutdown the main emulated process - if (m_load_result == Core::SystemResultStatus::Success) { - m_system.DetachDebugger(); - m_system.ShutdownMainProcess(); - m_detached_tasks.WaitForAllTasks(); - m_load_result = Core::SystemResultStatus::ErrorNotInitialized; - } - - // Tear down the render window. - m_window.reset(); - } - - void PauseEmulation() { - std::scoped_lock lock(m_mutex); - m_system.Pause(); - } - - void UnPauseEmulation() { - std::scoped_lock lock(m_mutex); - m_system.Run(); - } - - void HaltEmulation() { - std::scoped_lock lock(m_mutex); - m_is_running = false; - m_cv.notify_one(); - } - - void RunEmulation() { + while (true) { { - std::scoped_lock lock(m_mutex); - m_is_running = true; - } - - // Load the disk shader cache. - if (Settings::values.use_disk_shader_cache.GetValue()) { - LoadDiskCacheProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0); - m_system.Renderer().ReadRasterizer()->LoadDiskResources( - m_system.GetApplicationProcessProgramID(), std::stop_token{}, - LoadDiskCacheProgress); - LoadDiskCacheProgress(VideoCore::LoadCallbackStage::Complete, 0, 0); - } - - void(m_system.Run()); - - if (m_system.DebuggerEnabled()) { - m_system.InitializeDebugger(); - } - - while (true) { - { - std::unique_lock lock(m_mutex); - if (m_cv.wait_for(lock, std::chrono::milliseconds(800), - [&]() { return !m_is_running; })) { - // Emulation halted. - break; - } - } - { - // Refresh performance stats. - std::scoped_lock m_perf_stats_lock(m_perf_stats_mutex); - m_perf_stats = m_system.GetAndResetPerfStats(); + std::unique_lock lock(m_mutex); + if (m_cv.wait_for(lock, std::chrono::milliseconds(800), + [&]() { return !m_is_running; })) { + // Emulation halted. + break; } } - } - - std::string GetRomTitle(const std::string& path) { - return GetRomMetadata(path).title; - } - - std::vector GetRomIcon(const std::string& path) { - return GetRomMetadata(path).icon; - } - - bool GetIsHomebrew(const std::string& path) { - return GetRomMetadata(path).isHomebrew; - } - - void ResetRomMetadata() { - m_rom_metadata_cache.clear(); - } - - bool IsHandheldOnly() { - const auto npad_style_set = m_system.HIDCore().GetSupportedStyleTag(); - - if (npad_style_set.fullkey == 1) { - return false; + { + // Refresh performance stats. + std::scoped_lock m_perf_stats_lock(m_perf_stats_mutex); + m_perf_stats = m_system.GetAndResetPerfStats(); } + } + } - if (npad_style_set.handheld == 0) { - return false; - } + std::string GetRomTitle(const std::string& path) { + return GetRomMetadata(path).title; + } - return !Settings::values.use_docked_mode.GetValue(); + std::vector GetRomIcon(const std::string& path) { + return GetRomMetadata(path).icon; + } + + bool GetIsHomebrew(const std::string& path) { + return GetRomMetadata(path).isHomebrew; + } + + void ResetRomMetadata() { + m_rom_metadata_cache.clear(); + } + + bool IsHandheldOnly() { + const auto npad_style_set = m_system.HIDCore().GetSupportedStyleTag(); + + if (npad_style_set.fullkey == 1) { + return false; } - void SetDeviceType(int index, int type) { - auto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index); - controller->SetNpadStyleIndex(static_cast(type)); + if (npad_style_set.handheld == 0) { + return false; } - void OnGamepadConnectEvent(int index) { - auto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index); + return !Settings::values.use_docked_mode.GetValue(); + } - // Ensure that player1 is configured correctly and handheld disconnected - if (controller->GetNpadIdType() == Core::HID::NpadIdType::Player1) { - auto handheld = - m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld); + void SetDeviceType(int index, int type) { + auto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index); + controller->SetNpadStyleIndex(static_cast(type)); + } - if (controller->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::Handheld) { - handheld->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController); - controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController); - handheld->Disconnect(); - } - } + void OnGamepadConnectEvent(int index) { + auto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index); - // Ensure that handheld is configured correctly and player 1 disconnected - if (controller->GetNpadIdType() == Core::HID::NpadIdType::Handheld) { - auto player1 = m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1); + // Ensure that player1 is configured correctly and handheld disconnected + if (controller->GetNpadIdType() == Core::HID::NpadIdType::Player1) { + auto handheld = + m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld); - if (controller->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::Handheld) { - player1->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld); - controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld); - player1->Disconnect(); - } - } - - if (!controller->IsConnected()) { - controller->Connect(); + if (controller->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::Handheld) { + handheld->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController); + controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController); + handheld->Disconnect(); } } - void OnGamepadDisconnectEvent(int index) { - auto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index); - controller->Disconnect(); - } + // Ensure that handheld is configured correctly and player 1 disconnected + if (controller->GetNpadIdType() == Core::HID::NpadIdType::Handheld) { + auto player1 = m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1); - SoftwareKeyboard::AndroidKeyboard* SoftwareKeyboard() { - return m_software_keyboard; - } - - void DirectConnectToRoom(const std::string& nickname, const char* server_addr = "127.0.0.1", - u16 server_port = Network::DefaultRoomPort, - const std::string& password = "") { - auto room_network = m_system.GetRoomNetwork(); - - if (const auto member = room_network.GetRoomMember().lock()) { - // Prevent the user from trying to join a room while they are already joining. - if (member->GetState() == Network::RoomMember::State::Joining || member->IsConnected()) { - return; - } else { - member->Join(nickname, server_addr, server_port); - } + if (controller->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::Handheld) { + player1->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld); + controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld); + player1->Disconnect(); } } - Network::RoomMember::State GetRoomMemberState() { - if (const auto member = m_system.GetRoomNetwork().GetRoomMember().lock()) { - return member->GetState(); + if (!controller->IsConnected()) { + controller->Connect(); + } + } + + void OnGamepadDisconnectEvent(int index) { + auto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index); + controller->Disconnect(); + } + + SoftwareKeyboard::AndroidKeyboard* SoftwareKeyboard() { + return m_software_keyboard; + } + + void DirectConnectToRoom(const std::string& nickname, const char* server_addr = "127.0.0.1", + u16 server_port = Network::DefaultRoomPort, + const std::string& password = "") { + auto room_network = m_system.GetRoomNetwork(); + + if (const auto member = room_network.GetRoomMember().lock()) { + // Prevent the user from trying to join a room while they are already joining. + if (member->GetState() == Network::RoomMember::State::Joining || + member->IsConnected()) { + return; } else { - return Network::RoomMember::State::Idle; + member->Join(nickname, server_addr, server_port); } } + } - private: - struct RomMetadata { - std::string title; - std::vector icon; - bool isHomebrew; - }; - - RomMetadata GetRomMetadata(const std::string& path) { - if (auto search = m_rom_metadata_cache.find(path); search != m_rom_metadata_cache.end()) { - return search->second; - } - - return CacheRomMetadata(path); + Network::RoomMember::State GetRoomMemberState() { + if (const auto member = m_system.GetRoomNetwork().GetRoomMember().lock()) { + return member->GetState(); + } else { + return Network::RoomMember::State::Idle; } + } - RomMetadata CacheRomMetadata(const std::string& path) { - const auto file = Core::GetGameFileFromPath(m_vfs, path); - auto loader = Loader::GetLoader(EmulationSession::GetInstance().System(), file, 0, 0); - - RomMetadata entry; - loader->ReadTitle(entry.title); - loader->ReadIcon(entry.icon); - if (loader->GetFileType() == Loader::FileType::NRO) { - auto loader_nro = dynamic_cast(loader.get()); - entry.isHomebrew = loader_nro->IsHomebrew(); - } else { - entry.isHomebrew = false; - } - - m_rom_metadata_cache[path] = entry; - - return entry; - } - - private: - static void LoadDiskCacheProgress(VideoCore::LoadCallbackStage stage, int progress, int max) { - JNIEnv* env = IDCache::GetEnvForThread(); - env->CallStaticVoidMethod(IDCache::GetDiskCacheProgressClass(), - IDCache::GetDiskCacheLoadProgress(), static_cast(stage), - static_cast(progress), static_cast(max)); - } - - private: - static EmulationSession s_instance; - - // Frontend management - std::unordered_map m_rom_metadata_cache; - - // Window management - std::unique_ptr m_window; - ANativeWindow* m_native_window{}; - - // Core emulation - Core::System m_system; - InputCommon::InputSubsystem m_input_subsystem; - Common::DetachedTasks m_detached_tasks; - Core::PerfStatsResults m_perf_stats{}; - std::shared_ptr m_vfs; - Core::SystemResultStatus m_load_result{Core::SystemResultStatus::ErrorNotInitialized}; - bool m_is_running{}; - SoftwareKeyboard::AndroidKeyboard* m_software_keyboard{}; - std::unique_ptr m_profile_manager; - - // GPU driver parameters - std::shared_ptr m_vulkan_library; - - // Synchronization - std::condition_variable_any m_cv; - mutable std::mutex m_perf_stats_mutex; - mutable std::mutex m_mutex; +private: + struct RomMetadata { + std::string title; + std::vector icon; + bool isHomebrew; }; + RomMetadata GetRomMetadata(const std::string& path) { + if (auto search = m_rom_metadata_cache.find(path); search != m_rom_metadata_cache.end()) { + return search->second; + } + + return CacheRomMetadata(path); + } + + RomMetadata CacheRomMetadata(const std::string& path) { + const auto file = Core::GetGameFileFromPath(m_vfs, path); + auto loader = Loader::GetLoader(EmulationSession::GetInstance().System(), file, 0, 0); + + RomMetadata entry; + loader->ReadTitle(entry.title); + loader->ReadIcon(entry.icon); + if (loader->GetFileType() == Loader::FileType::NRO) { + auto loader_nro = dynamic_cast(loader.get()); + entry.isHomebrew = loader_nro->IsHomebrew(); + } else { + entry.isHomebrew = false; + } + + m_rom_metadata_cache[path] = entry; + + return entry; + } + +private: + static void LoadDiskCacheProgress(VideoCore::LoadCallbackStage stage, int progress, int max) { + JNIEnv* env = IDCache::GetEnvForThread(); + env->CallStaticVoidMethod(IDCache::GetDiskCacheProgressClass(), + IDCache::GetDiskCacheLoadProgress(), static_cast(stage), + static_cast(progress), static_cast(max)); + } + +private: + static EmulationSession s_instance; + + // Frontend management + std::unordered_map m_rom_metadata_cache; + + // Window management + std::unique_ptr m_window; + ANativeWindow* m_native_window{}; + + // Core emulation + Core::System m_system; + InputCommon::InputSubsystem m_input_subsystem; + Common::DetachedTasks m_detached_tasks; + Core::PerfStatsResults m_perf_stats{}; + std::shared_ptr m_vfs; + Core::SystemResultStatus m_load_result{Core::SystemResultStatus::ErrorNotInitialized}; + bool m_is_running{}; + SoftwareKeyboard::AndroidKeyboard* m_software_keyboard{}; + std::unique_ptr m_profile_manager; + + // GPU driver parameters + std::shared_ptr m_vulkan_library; + + // Synchronization + std::condition_variable_any m_cv; + mutable std::mutex m_perf_stats_mutex; + mutable std::mutex m_mutex; +}; + /*static*/ EmulationSession EmulationSession::s_instance; } // Anonymous namespace @@ -492,11 +493,11 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_setAppDirectory(JNIEnv* env, } void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeGpuDriver( - JNIEnv* env, [[maybe_unused]] jclass clazz, jstring hook_lib_dir, jstring custom_driver_dir, - jstring custom_driver_name, jstring file_redirect_dir) { + JNIEnv* env, [[maybe_unused]] jclass clazz, jstring hook_lib_dir, jstring custom_driver_dir, + jstring custom_driver_name, jstring file_redirect_dir) { EmulationSession::GetInstance().InitializeGpuDriver( - GetJString(env, hook_lib_dir), GetJString(env, custom_driver_dir), - GetJString(env, custom_driver_name), GetJString(env, file_redirect_dir)); + GetJString(env, hook_lib_dir), GetJString(env, custom_driver_dir), + GetJString(env, custom_driver_name), GetJString(env, file_redirect_dir)); } jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_reloadKeys(JNIEnv* env, @@ -554,7 +555,7 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadConnectEvent([[maybe_unu } jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadDisconnectEvent( - [[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jint j_device) { + [[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jint j_device) { if (EmulationSession::GetInstance().IsRunning()) { EmulationSession::GetInstance().OnGamepadDisconnectEvent(j_device); } @@ -584,12 +585,12 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadJoystickEvent([[maybe_un } jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadMotionEvent( - [[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jint j_device, - jlong delta_timestamp, jfloat gyro_x, jfloat gyro_y, jfloat gyro_z, jfloat accel_x, - jfloat accel_y, jfloat accel_z) { + [[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jint j_device, + jlong delta_timestamp, jfloat gyro_x, jfloat gyro_y, jfloat gyro_z, jfloat accel_x, + jfloat accel_y, jfloat accel_z) { if (EmulationSession::GetInstance().IsRunning()) { EmulationSession::GetInstance().Window().OnGamepadMotionEvent( - j_device, delta_timestamp, gyro_x, gyro_y, gyro_z, accel_x, accel_y, accel_z); + j_device, delta_timestamp, gyro_x, gyro_y, gyro_z, accel_x, accel_y, accel_z); } return static_cast(true); } @@ -686,7 +687,7 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isHomebrew([[maybe_unused]] JNIEn } void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeEmulation -[[maybe_unused]] (JNIEnv* env, [[maybe_unused]] jclass clazz) { + [[maybe_unused]] (JNIEnv* env, [[maybe_unused]] jclass clazz) { // Create the default config.ini. Config{}; // Initialize the emulated system. @@ -699,8 +700,8 @@ jint Java_org_yuzu_yuzu_1emu_NativeLibrary_defaultCPUCore([[maybe_unused]] JNIEn } void Java_org_yuzu_yuzu_1emu_NativeLibrary_run__Ljava_lang_String_2Ljava_lang_String_2Z( - [[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, [[maybe_unused]] jstring j_file, - [[maybe_unused]] jstring j_savestate, [[maybe_unused]] jboolean j_delete_savestate) {} + [[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, [[maybe_unused]] jstring j_file, + [[maybe_unused]] jstring j_savestate, [[maybe_unused]] jboolean j_delete_savestate) {} void Java_org_yuzu_yuzu_1emu_NativeLibrary_reloadSettings([[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz) { @@ -763,7 +764,7 @@ jdoubleArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPerfStats([[maybe_unused]] } void Java_org_yuzu_yuzu_1emu_utils_DirectoryInitialization_setSysDirectory( - [[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jstring j_path) {} + [[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jstring j_path) {} void Java_org_yuzu_yuzu_1emu_NativeLibrary_run__Ljava_lang_String_2([[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, @@ -795,16 +796,11 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_submitInlineKeyboardInput(JNIEnv* env } void Java_org_yuzu_yuzu_1emu_NativeLibrary_connectToRoom(JNIEnv* env, jclass clazz, - jstring nickname, - jstring server_addr, - jint server_port, - jstring password) { - EmulationSession::GetInstance().DirectConnectToRoom( - GetJString(env, nickname), - GetJString(env, server_addr).c_str(), - server_port, - GetJString(env, password) - ); + jstring nickname, jstring server_addr, + jint server_port, jstring password) { + EmulationSession::GetInstance().DirectConnectToRoom(GetJString(env, nickname), + GetJString(env, server_addr).c_str(), + server_port, GetJString(env, password)); } jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getRoomMemberState(JNIEnv* env, jclass clazz) { @@ -812,24 +808,24 @@ jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getRoomMemberState(JNIEnv* env, jc std::string state_str{}; - switch(state) { + switch (state) { using State = Network::RoomMember::State; - case State::Uninitialized: - state_str = "Uninitialized"; - break; - case State::Idle: - state_str = "Idle"; - break; - case State::Joining: - state_str = "Joining"; - break; - case State::Joined: - state_str = "Joined"; - break; - case State::Moderator: - state_str = "Moderator"; - break; + case State::Uninitialized: + state_str = "Uninitialized"; + break; + case State::Idle: + state_str = "Idle"; + break; + case State::Joining: + state_str = "Joining"; + break; + case State::Joined: + state_str = "Joined"; + break; + case State::Moderator: + state_str = "Moderator"; + break; } return env->NewStringUTF(state_str.c_str()); diff --git a/src/core/internal_network/network_interface.cpp b/src/core/internal_network/network_interface.cpp index 0c84db932c..da2c328adf 100644 --- a/src/core/internal_network/network_interface.cpp +++ b/src/core/internal_network/network_interface.cpp @@ -20,9 +20,9 @@ #include #else #include +#include #include #include -#include #endif @@ -103,18 +103,15 @@ std::vector GetAvailableNetworkInterfaces() { boost::split(route_parts, Settings::values.network_route.GetValue(), boost::is_any_of(";")); - struct in_addr ip{}, sm{}, gw{}; + struct in_addr ip { + }, sm{}, gw{}; inet_pton(AF_INET, route_parts[1].c_str(), &ip); inet_pton(AF_INET, route_parts[2].c_str(), &sm); inet_pton(AF_INET, route_parts[3].c_str(), &gw); - result.emplace_back(NetworkInterface{ - .name{route_parts[0]}, - .ip_address{ip}, - .subnet_mask{sm}, - .gateway{gw} - }); + result.emplace_back( + NetworkInterface{.name{route_parts[0]}, .ip_address{ip}, .subnet_mask{sm}, .gateway{gw}}); return result; } From 9b2aebee25fb5c9dc49542ada1cdcdd20d9c0e0b Mon Sep 17 00:00:00 2001 From: Timotej Leginus Date: Sun, 11 Jun 2023 22:47:33 +0200 Subject: [PATCH 09/10] Fixed icon being in ea flavor resources --- .../app/src/main/res/drawable/ic_multiplayer.xml | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 src/android/app/src/main/res/drawable/ic_multiplayer.xml diff --git a/src/android/app/src/main/res/drawable/ic_multiplayer.xml b/src/android/app/src/main/res/drawable/ic_multiplayer.xml new file mode 100644 index 0000000000..6457435a41 --- /dev/null +++ b/src/android/app/src/main/res/drawable/ic_multiplayer.xml @@ -0,0 +1,10 @@ + + + From 71a0fc21ab2e2c8cc454dad0e857eb89ba3bdb05 Mon Sep 17 00:00:00 2001 From: Timotej Leginus Date: Sun, 11 Jun 2023 23:21:49 +0200 Subject: [PATCH 10/10] Fixed licensing info --- .../src/main/java/org/yuzu/yuzu_emu/utils/MultiplayerHelper.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/MultiplayerHelper.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/MultiplayerHelper.kt index 167305593c..bec797134e 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/MultiplayerHelper.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/MultiplayerHelper.kt @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + package org.yuzu.yuzu_emu.utils import android.content.Context