This commit is contained in:
Timotej Leginus 2023-06-16 09:54:15 +08:00 committed by GitHub
commit b36511d583
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 536 additions and 5 deletions

View file

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="48dp"
android:height="48dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M0,720L0,667Q0,628.43 41.5,604.22Q83,580 150.38,580Q162.54,580 173.77,580.5Q185,581 196,582.65Q188,600 184,617.82Q180,635.63 180,655L180,720L0,720ZM240,720L240,655Q240,623 257.5,596.5Q275,570 307,550Q339,530 383.5,520Q428,510 480,510Q533,510 577.5,520Q622,530 654,550Q686,570 703,596.5Q720,623 720,655L720,720L240,720ZM780,720L780,655Q780,635.14 776.5,617.57Q773,600 765,582.73Q776,581 787.17,580.5Q798.34,580 810,580Q877.5,580 918.75,603.77Q960,627.54 960,667L960,720L780,720ZM300,660L660,660L660,654Q660,617 609.5,593.5Q559,570 480,570Q401,570 350.5,593.5Q300,617 300,655L300,660ZM149.57,550Q121,550 100.5,529.44Q80,508.88 80,480Q80,451 100.56,430.5Q121.13,410 150,410Q179,410 199.5,430.5Q220,451 220,480.43Q220,509 199.5,529.5Q179,550 149.57,550ZM809.57,550Q781,550 760.5,529.44Q740,508.88 740,480Q740,451 760.56,430.5Q781.13,410 810,410Q839,410 859.5,430.5Q880,451 880,480.43Q880,509 859.5,529.5Q839,550 809.57,550ZM480,480Q430,480 395,445Q360,410 360,360Q360,309 395,274.5Q430,240 480,240Q531,240 565.5,274.5Q600,309 600,360Q600,410 565.5,445Q531,480 480,480ZM480.35,420Q506,420 523,402.65Q540,385.3 540,359.65Q540,334 522.85,317Q505.7,300 480.35,300Q455,300 437.5,317.15Q420,334.3 420,359.65Q420,385 437.35,402.5Q454.7,420 480.35,420ZM480,660L480,660Q480,660 480,660Q480,660 480,660Q480,660 480,660Q480,660 480,660L480,660L480,660ZM480,360Q480,360 480,360Q480,360 480,360Q480,360 480,360Q480,360 480,360Q480,360 480,360Q480,360 480,360Q480,360 480,360Q480,360 480,360Z"/>
</vector>

View file

@ -11,6 +11,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
<uses-feature android:name="android.software.leanback" android:required="false" />
<uses-feature android:name="android.hardware.vulkan.version" android:version="0x401000" android:required="true" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.NFC" />

View file

@ -491,6 +491,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
*/

View file

@ -10,6 +10,8 @@ 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
fun Context.getPublicFilesDir() : File = getExternalFilesDir(null) ?: filesDir
@ -46,6 +48,8 @@ class YuzuApplication : Application() {
documentsTree = DocumentsTree()
DirectoryInitialization.start(applicationContext)
GpuDriverHelper.initializeDriverParameters(applicationContext)
NetworkHelper.getRoute(applicationContext)
MultiplayerHelper.initRoom(applicationContext)
NativeLibrary.logDeviceInfo()
createNotificationChannels();

View file

@ -107,7 +107,9 @@ 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_MULTIPLAYER = "Multiplayer"
const val SECTION_DEBUG = "Debug"
const val PREF_OVERLAY_INIT = "OverlayInit"
@ -130,6 +132,14 @@ 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"
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"
@ -154,6 +164,7 @@ class Settings {
SECTION_SYSTEM,
SECTION_RENDERER,
SECTION_AUDIO,
SECTION_NETWORK,
SECTION_CPU
)
}

View file

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

View file

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

View file

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

View file

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

View file

@ -7,12 +7,12 @@ 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
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 +67,8 @@ 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_MULTIPLAYER -> addMultiplayerSettings(sl)
Settings.SECTION_THEME -> addThemeSettings(sl)
Settings.SECTION_DEBUG -> addDebugSettings(sl)
else -> {
@ -102,6 +104,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,
@ -466,6 +475,179 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
}
}
private fun addNetworkSettings(sl: ArrayList<SettingsItem>) {
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,
R.string.set_network_route,
R.string.network_route_desc,
StringSetting.NETWORK_ROUTE.key,
StringSetting.NETWORK_ROUTE.defaultValue
)
)
}
}
private fun addMultiplayerSettings(sl: ArrayList<SettingsItem>) {
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<SettingsItem>) {
settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.preferences_debug))
sl.apply {

View file

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

View file

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

View file

@ -0,0 +1,24 @@
// 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 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)
}
}
}

View file

@ -0,0 +1,44 @@
// 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
import org.yuzu.yuzu_emu.features.settings.model.Settings
object NetworkHelper {
/**
* 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 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]
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
return "$ifName;$addr;$mask;$gw"
}
}

View file

@ -248,6 +248,10 @@ 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";

View file

@ -14,6 +14,8 @@
#include <android/api-level.h>
#include <android/native_window_jni.h>
#include <core/loader/nro.h>
#include <network/network.h>
#include <network/room_member.h>
#include "common/detached_tasks.h"
#include "common/dynamic_library.h"
@ -420,6 +422,30 @@ 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;
@ -860,4 +886,40 @@ 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"

View file

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="48dp"
android:height="48dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M0,720L0,667Q0,628.43 41.5,604.22Q83,580 150.38,580Q162.54,580 173.77,580.5Q185,581 196,582.65Q188,600 184,617.82Q180,635.63 180,655L180,720L0,720ZM240,720L240,655Q240,623 257.5,596.5Q275,570 307,550Q339,530 383.5,520Q428,510 480,510Q533,510 577.5,520Q622,530 654,550Q686,570 703,596.5Q720,623 720,655L720,720L240,720ZM780,720L780,655Q780,635.14 776.5,617.57Q773,600 765,582.73Q776,581 787.17,580.5Q798.34,580 810,580Q877.5,580 918.75,603.77Q960,627.54 960,667L960,720L780,720ZM300,660L660,660L660,654Q660,617 609.5,593.5Q559,570 480,570Q401,570 350.5,593.5Q300,617 300,655L300,660ZM149.57,550Q121,550 100.5,529.44Q80,508.88 80,480Q80,451 100.56,430.5Q121.13,410 150,410Q179,410 199.5,430.5Q220,451 220,480.43Q220,509 199.5,529.5Q179,550 149.57,550ZM809.57,550Q781,550 760.5,529.44Q740,508.88 740,480Q740,451 760.56,430.5Q781.13,410 810,410Q839,410 859.5,430.5Q880,451 880,480.43Q880,509 859.5,529.5Q839,550 809.57,550ZM480,480Q430,480 395,445Q360,410 360,360Q360,309 395,274.5Q430,240 480,240Q531,240 565.5,274.5Q600,309 600,360Q600,410 565.5,445Q531,480 480,480ZM480.35,420Q506,420 523,402.65Q540,385.3 540,359.65Q540,334 522.85,317Q505.7,300 480.35,300Q455,300 437.5,317.15Q420,334.3 420,359.65Q420,385 437.35,402.5Q454.7,420 480.35,420ZM480,660L480,660Q480,660 480,660Q480,660 480,660Q480,660 480,660Q480,660 480,660L480,660L480,660ZM480,360Q480,360 480,360Q480,360 480,360Q480,360 480,360Q480,360 480,360Q480,360 480,360Q480,360 480,360Q480,360 480,360Q480,360 480,360Z"/>
</vector>

View file

@ -81,6 +81,7 @@
<string name="open_user_folder">Open yuzu folder</string>
<string name="open_user_folder_description">Manage yuzu\'s internal files</string>
<string name="theme_and_color_description">Modify the look of the app</string>
<string name="multiplayer_description">Play with your friends!</string>
<string name="no_file_manager">No file manager found</string>
<string name="notification_no_directory_link">Could not open yuzu directory</string>
<string name="notification_no_directory_link_description">Please locate the user folder with the file manager\'s side panel manually.</string>
@ -157,6 +158,19 @@
<string name="use_custom_rtc_description">Allows you to set a custom real-time clock separate from your current system time.</string>
<string name="set_custom_rtc">Set custom RTC</string>
<!-- Network settings strings -->
<string name="network_route_desc">Sets the default network route</string>
<string name="set_network_route">Set network route</string>
<string name="set_force_wifi">Force Wi-Fi</string>
<string name="force_wifi_desc">Forces yuzu to use only Wi-Fi (non-metered connections in general)</string>
<!-- Multiplayer settings strings -->
<string name="multiplayer_room_server_address">Server address</string>
<string name="multiplayer_room_server_port">Port</string>
<string name="multiplayer_room_nickname">Nickname</string>
<string name="multiplayer_room_password">Password</string>
<string name="multiplayer_room_connect_on_start">Connect on start</string>
<!-- Graphics settings strings -->
<string name="renderer_api">API</string>
<string name="renderer_accuracy">Accuracy level</string>
@ -212,9 +226,11 @@
<string name="preferences_settings">Settings</string>
<string name="preferences_general">General</string>
<string name="preferences_system">System</string>
<string name="preferences_network">Network</string>
<string name="preferences_graphics">Graphics</string>
<string name="preferences_audio">Audio</string>
<string name="preferences_theme">Theme and color</string>
<string name="preferences_multiplayer">Multiplayer</string>
<string name="preferences_debug">Debug</string>
<!-- ROM loading errors -->

View file

@ -581,6 +581,7 @@ struct Values {
// Network
Setting<std::string> network_interface{std::string(), "network_interface"};
Setting<std::string> network_route{std::string(), "network_route"};
// WebService
Setting<bool> enable_telemetry{true, "enable_telemetry"};

View file

@ -6,6 +6,8 @@
#include <sstream>
#include <vector>
#include <boost/algorithm/string.hpp>
#include "common/bit_cast.h"
#include "common/common_types.h"
#include "common/logging/log.h"
@ -18,8 +20,10 @@
#include <iphlpapi.h>
#else
#include <cerrno>
#include <arpa/inet.h>
#include <ifaddrs.h>
#include <net/if.h>
#endif
namespace Network {
@ -91,8 +95,28 @@ std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
return result;
}
#else
#elif defined(__ANDROID__)
std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
std::vector<NetworkInterface> result;
std::vector<std::string> 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<NetworkInterface> GetAvailableNetworkInterfaces() {
struct ifaddrs* ifaddr = nullptr;
@ -187,6 +211,10 @@ std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
#endif
std::optional<NetworkInterface> 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()) {

View file

@ -81,6 +81,7 @@ void DirectConnectWindow::Connect() {
}
}
}
if (!ui->ip->hasAcceptableInput()) {
NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::IP_ADDRESS_NOT_VALID);
return;