aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAleksandr Popovich <alekpopo@pm.me>2025-06-05 18:59:47 +0000
committercrueter <crueter@noreply.localhost>2025-06-05 18:59:47 +0000
commit76fa5255926da8f6339e7be5bf106646acedf78a (patch)
tree8ea533a7d748a9a5404e15a1063b11a832b2f007 /src
parent7e13da47af71814d612c6c060be68e8ba84271ff (diff)
Added the public lobby to android. (#125)
This is adapted from kleidis old PR to Azahar. Changes from it: - Fixed inconsistent button styling in the dialog for connection - Allowed to hide both empty and full rooms. - Proper serving of preferred games - Enables web service for android by default - Better implementation of multiplayer.cpp that works with oop Also fixes the room network class and turns it into a static namespace in network Signed-off-by: Aleksandr Popovich <alekpopo@pm.me> Co-authored-by: swurl <swurl@swurl.xyz> Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/125 Co-authored-by: Aleksandr Popovich <alekpopo@pm.me> Co-committed-by: Aleksandr Popovich <alekpopo@pm.me>
Diffstat (limited to 'src')
-rw-r--r--src/android/app/build.gradle.kts13
-rw-r--r--src/android/app/src/main/AndroidManifest.xml4
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt5
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/dialogs/ChatDialog.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/dialogs/LobbyBrowser.kt252
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/dialogs/NetPlayDialog.kt53
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/StringSetting.kt4
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt7
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt6
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AboutFragment.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AddGameFolderDialogFragment.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AddonsFragment.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AppletLauncherFragment.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameFoldersFragment.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameInfoFragment.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GamePropertiesFragment.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt8
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/InstallableFragment.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/LicensesFragment.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeViewModel.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/network/NetPlayManager.kt193
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt9
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/CompatUtils.kt6
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt20
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ThemeHelper.kt2
-rw-r--r--src/android/app/src/main/jni/native.cpp63
-rw-r--r--src/android/app/src/main/res/drawable/ic_ip.xml9
-rw-r--r--src/android/app/src/main/res/drawable/ic_joined.xml10
-rw-r--r--src/android/app/src/main/res/drawable/ic_multiplayer.xml9
-rw-r--r--src/android/app/src/main/res/layout/dialog_lobby_browser.xml220
-rw-r--r--src/android/app/src/main/res/layout/dialog_multiplayer_connect.xml44
-rw-r--r--src/android/app/src/main/res/layout/dialog_multiplayer_room.xml15
-rw-r--r--src/android/app/src/main/res/layout/dialog_password_input.xml21
-rw-r--r--src/android/app/src/main/res/layout/dropdown_item.xml9
-rw-r--r--src/android/app/src/main/res/layout/item_lobby_room.xml113
-rw-r--r--src/android/app/src/main/res/values-fr-rSN/strings.xml4
-rw-r--r--src/android/app/src/main/res/values/strings.xml5
-rw-r--r--src/common/android/android_common.cpp2
-rw-r--r--src/common/android/android_common.h2
-rw-r--r--src/common/android/id_cache.cpp7
-rw-r--r--src/common/android/id_cache.h2
-rw-r--r--src/common/android/multiplayer/multiplayer.cpp134
-rw-r--r--src/common/android/multiplayer/multiplayer.h80
-rw-r--r--src/common/announce_multiplayer_room.h2
-rw-r--r--src/core/core.cpp17
-rw-r--r--src/core/core.h13
-rw-r--r--src/core/hle/service/ldn/lan_discovery.cpp12
-rw-r--r--src/core/hle/service/ldn/lan_discovery.h7
-rw-r--r--src/core/hle/service/ldn/user_local_communication_service.cpp13
-rw-r--r--src/core/hle/service/ldn/user_local_communication_service.h8
-rw-r--r--src/core/hle/service/nifm/nifm.cpp11
-rw-r--r--src/core/hle/service/nifm/nifm.h9
-rw-r--r--src/core/hle/service/sockets/bsd.cpp13
-rw-r--r--src/core/hle/service/sockets/bsd.h5
-rw-r--r--src/core/internal_network/network_interface.cpp2
-rw-r--r--src/core/internal_network/socket_proxy.cpp11
-rw-r--r--src/core/internal_network/socket_proxy.h9
-rw-r--r--src/dedicated_room/yuzu_room.cpp56
-rw-r--r--src/network/announce_multiplayer_session.cpp10
-rw-r--r--src/network/announce_multiplayer_session.h9
-rw-r--r--src/network/network.cpp41
-rw-r--r--src/network/network.h28
-rw-r--r--src/network/room.cpp2
-rw-r--r--src/network/room.h2
-rw-r--r--src/yuzu/CMakeLists.txt2
-rw-r--r--src/yuzu/bootmanager.cpp2
-rw-r--r--src/yuzu/configuration/configure_touch_from_button.cpp2
-rw-r--r--src/yuzu/configuration/configure_ui.cpp2
-rw-r--r--src/yuzu/configuration/configure_web.cpp119
-rw-r--r--src/yuzu/configuration/configure_web.ui20
-rw-r--r--src/yuzu/game_list.cpp2
-rw-r--r--src/yuzu/main.cpp6
-rw-r--r--src/yuzu/multiplayer/chat_room.cpp27
-rw-r--r--src/yuzu/multiplayer/chat_room.h6
-rw-r--r--src/yuzu/multiplayer/client_room.cpp15
-rw-r--r--src/yuzu/multiplayer/client_room.h6
-rw-r--r--src/yuzu/multiplayer/direct_connect.cpp12
-rw-r--r--src/yuzu/multiplayer/direct_connect.h4
-rw-r--r--src/yuzu/multiplayer/host_room.cpp14
-rw-r--r--src/yuzu/multiplayer/host_room.h4
-rw-r--r--src/yuzu/multiplayer/lobby.cpp12
-rw-r--r--src/yuzu/multiplayer/lobby.h4
-rw-r--r--src/yuzu/multiplayer/moderation_dialog.cpp17
-rw-r--r--src/yuzu/multiplayer/moderation_dialog.h7
-rw-r--r--src/yuzu/multiplayer/state.cpp21
-rw-r--r--src/yuzu/multiplayer/state.h4
-rw-r--r--src/yuzu_cmd/yuzu.cpp5
95 files changed, 1464 insertions, 498 deletions
diff --git a/src/android/app/build.gradle.kts b/src/android/app/build.gradle.kts
index 6c5592c1f5..de5d891f01 100644
--- a/src/android/app/build.gradle.kts
+++ b/src/android/app/build.gradle.kts
@@ -1,4 +1,7 @@
-// SPDX-FileCopyrightText: Copyright yuzu/Citra Emulator Project / Eden Emulator Project
+// SPDX-FileCopyrightText: Copyright yuzu/Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
import android.annotation.SuppressLint
@@ -162,7 +165,7 @@ android {
arguments(
"-DENABLE_QT=0", // Don't use QT
"-DENABLE_SDL2=0", // Don't use SDL
- "-DENABLE_WEB_SERVICE=0", // Don't use telemetry
+ "-DENABLE_WEB_SERVICE=1", // Enable web service
"-DANDROID_ARM_NEON=true", // cryptopp requires Neon to work
"-DYUZU_USE_BUNDLED_VCPKG=ON",
"-DYUZU_USE_BUNDLED_FFMPEG=ON",
@@ -176,9 +179,9 @@ android {
}
}
-tasks.create<Delete>("ktlintReset") {
- delete(File(buildDir.path + File.separator + "intermediates/ktLint"))
-}
+tasks.register<Delete>("ktlintReset", fun Delete.() {
+ delete(File(layout.buildDirectory.toString() + File.separator + "intermediates/ktLint"))
+})
val showFormatHelp = {
logger.lifecycle(
diff --git a/src/android/app/src/main/AndroidManifest.xml b/src/android/app/src/main/AndroidManifest.xml
index 0a43b13e1c..e4885e5126 100644
--- a/src/android/app/src/main/AndroidManifest.xml
+++ b/src/android/app/src/main/AndroidManifest.xml
@@ -3,6 +3,9 @@
<!--
SPDX-FileCopyrightText: 2023 yuzu Emulator Project
SPDX-License-Identifier: GPL-3.0-or-later
+
+SPDX-FileCopyrightText: 2025 Eden Emulator Project
+SPDX-License-Identifier: GPL-3.0-or-later
-->
<!--
SPDX-FileCopyrightText: Eden Emulator Project
@@ -15,6 +18,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.NFC" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
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 0be943eb86..c8f44c060e 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
@@ -1,7 +1,7 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
-// SPDX-FileCopyrightText: Copyright yuzu/Citra Emulator Project / Eden Emulator Project
+// SPDX-FileCopyrightText: 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
@@ -270,8 +270,7 @@ object NativeLibrary {
NetPlayManager.clearChat()
}
-
- external fun netPlayInit()
+ external fun initMultiplayer()
@Keep
@JvmStatic
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt
index 496fc99e4c..8622aa658e 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt
@@ -1,7 +1,7 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
-// SPDX-FileCopyrightText: Copyright yuzu/Citra Emulator Project / Eden Emulator Project
+// SPDX-FileCopyrightText: 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt
index 671e897448..2596ab0e93 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: Copyright yuzu/Citra Emulator Project / Eden Emulator Project
+// SPDX-FileCopyrightText: 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.adapters
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/dialogs/ChatDialog.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/dialogs/ChatDialog.kt
index 9f7e80e7d0..25122fb79b 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/dialogs/ChatDialog.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/dialogs/ChatDialog.kt
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: Copyright yuzu/Citra Emulator Project / Eden Emulator Project
+// SPDX-FileCopyrightText: 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/dialogs/LobbyBrowser.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/dialogs/LobbyBrowser.kt
new file mode 100644
index 0000000000..ae588bdfa8
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/dialogs/LobbyBrowser.kt
@@ -0,0 +1,252 @@
+// SPDX-FileCopyrightText: 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package org.yuzu.yuzu_emu.dialogs
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.content.res.Configuration
+import android.os.Bundle
+import android.os.Handler
+import android.os.Looper
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.view.inputmethod.EditorInfo
+import android.view.inputmethod.InputMethodManager
+import androidx.core.content.getSystemService
+import androidx.core.widget.doOnTextChanged
+import androidx.recyclerview.widget.DividerItemDecoration
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+import com.google.android.material.bottomsheet.BottomSheetBehavior
+import com.google.android.material.bottomsheet.BottomSheetDialog
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
+import com.google.android.material.textfield.TextInputEditText
+import info.debatty.java.stringsimilarity.Jaccard
+import info.debatty.java.stringsimilarity.JaroWinkler
+import org.yuzu.yuzu_emu.R
+import org.yuzu.yuzu_emu.databinding.DialogLobbyBrowserBinding
+import org.yuzu.yuzu_emu.databinding.ItemLobbyRoomBinding
+import org.yuzu.yuzu_emu.network.NetPlayManager
+import java.util.Locale
+
+class LobbyBrowser(context: Context) : BottomSheetDialog(context) {
+ private lateinit var binding: DialogLobbyBrowserBinding
+ private lateinit var adapter: LobbyRoomAdapter
+ private val handler = Handler(Looper.getMainLooper())
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ behavior.state = BottomSheetBehavior.STATE_EXPANDED
+ behavior.skipCollapsed =
+ context.resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE
+
+ binding = DialogLobbyBrowserBinding.inflate(layoutInflater)
+ setContentView(binding.root)
+
+ binding.emptyRefreshButton.setOnClickListener {
+ binding.progressBar.visibility = View.VISIBLE
+ refreshRoomList()
+ }
+
+ setupRecyclerView()
+ setupRefreshButton()
+ refreshRoomList()
+ setupSearchBar()
+ }
+
+ private fun setupRecyclerView() {
+ adapter = LobbyRoomAdapter { room -> handleRoomSelection(room) }
+
+ binding.roomList.apply {
+ layoutManager = LinearLayoutManager(context)
+ adapter = this@LobbyBrowser.adapter
+ addItemDecoration(DividerItemDecoration(context, DividerItemDecoration.VERTICAL))
+ }
+ }
+
+ private fun setupRefreshButton() {
+ binding.refreshButton.setOnClickListener {
+ binding.refreshButton.isEnabled = false
+ binding.progressBar.visibility = View.VISIBLE
+ refreshRoomList()
+ }
+ }
+
+ private fun setupSearchBar() {
+ binding.chipHideFull.setOnCheckedChangeListener { _, _ -> adapter.filterAndSearch() }
+ binding.chipHideEmpty.setOnCheckedChangeListener { _, _ -> adapter.filterAndSearch() }
+
+ binding.searchText.doOnTextChanged { text: CharSequence?, _: Int, _: Int, _: Int ->
+ if (text.toString().isNotEmpty()) {
+ binding.clearButton.visibility = View.VISIBLE
+ } else {
+ binding.clearButton.visibility = View.INVISIBLE
+ }
+ }
+
+ binding.searchText.setOnEditorActionListener { v, action, _ ->
+ if (action == EditorInfo.IME_ACTION_DONE) {
+ v.clearFocus()
+
+ val imm = context.getSystemService<InputMethodManager>()
+ imm?.hideSoftInputFromWindow(v.windowToken, 0)
+
+ adapter.filterAndSearch()
+ true
+ } else {
+ false
+ }
+ }
+
+ binding.btnSubmit.setOnClickListener { adapter.filterAndSearch() }
+
+ binding.clearButton.setOnClickListener {
+ binding.searchText.setText("")
+ adapter.updateRooms(NetPlayManager.getPublicRooms())
+ }
+ }
+
+ private fun refreshRoomList() {
+ NetPlayManager.refreshRoomListAsync { rooms ->
+ binding.emptyView.visibility = if (rooms.isEmpty()) View.VISIBLE else View.GONE
+ binding.roomList.visibility = if (rooms.isEmpty()) View.GONE else View.VISIBLE
+ binding.appbar.visibility = if (rooms.isEmpty()) View.GONE else View.VISIBLE
+ adapter.updateRooms(rooms)
+ adapter.filterAndSearch()
+ binding.refreshButton.isEnabled = true
+ binding.progressBar.visibility = View.GONE
+ }
+ }
+
+ private fun handleRoomSelection(room: NetPlayManager.RoomInfo) {
+ if (room.hasPassword) {
+ showPasswordDialog(room)
+ } else {
+ joinRoom(room, "")
+ }
+ }
+
+ private fun showPasswordDialog(room: NetPlayManager.RoomInfo) {
+ val dialogView = LayoutInflater.from(context).inflate(R.layout.dialog_password_input, null)
+ val passwordInput = dialogView.findViewById<TextInputEditText>(R.id.password_input)
+
+ MaterialAlertDialogBuilder(context)
+ .setTitle(context.getString(R.string.multiplayer_password_required))
+ .setView(dialogView)
+ .setPositiveButton(R.string.multiplayer_join_room) { _, _ ->
+ joinRoom(room, passwordInput.text.toString())
+ }
+ .setNegativeButton(android.R.string.cancel, null)
+ .show()
+ }
+
+ private fun joinRoom(room: NetPlayManager.RoomInfo, password: String) {
+ val username = NetPlayManager.getUsername(context)
+
+ Thread {
+ val result = NetPlayManager.netPlayJoinRoom(room.ip, room.port, username, password)
+
+ handler.post {
+ if (result == 0) {
+ dismiss()
+ NetPlayDialog(context).show()
+ }
+ }
+ }.start()
+ }
+
+ inner class LobbyRoomAdapter(private val onRoomSelected: (NetPlayManager.RoomInfo) -> Unit) :
+ RecyclerView.Adapter<LobbyRoomAdapter.RoomViewHolder>() {
+
+ private val rooms = mutableListOf<NetPlayManager.RoomInfo>()
+
+ inner class RoomViewHolder(private val binding: ItemLobbyRoomBinding) :
+ RecyclerView.ViewHolder(binding.root) {
+ fun bind(room: NetPlayManager.RoomInfo) {
+ binding.roomName.text = room.name
+ binding.roomOwner.text = room.owner
+ binding.playerCount.text = context.getString(
+ R.string.multiplayer_player_count,
+ room.members.size,
+ room.maxPlayers
+ )
+
+ binding.lockIcon.visibility = if (room.hasPassword) View.VISIBLE else View.GONE
+
+ if (room.preferredGameName.isNotEmpty() && room.preferredGameId != 0L) {
+ binding.gameName.text = room.preferredGameName
+ } else {
+ binding.gameName.text = context.getString(R.string.multiplayer_no_game_info)
+ }
+
+ itemView.setOnClickListener { onRoomSelected(room) }
+ }
+ }
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RoomViewHolder {
+ val binding = ItemLobbyRoomBinding.inflate(
+ LayoutInflater.from(parent.context),
+ parent,
+ false
+ )
+ return RoomViewHolder(binding)
+ }
+
+ override fun onBindViewHolder(holder: RoomViewHolder, position: Int) {
+ holder.bind(rooms[position])
+ }
+
+ override fun getItemCount() = rooms.size
+
+ @SuppressLint("NotifyDataSetChanged")
+ fun updateRooms(newRooms: List<NetPlayManager.RoomInfo>) {
+ rooms.clear()
+ rooms.addAll(newRooms)
+ notifyDataSetChanged()
+ }
+
+ fun filterAndSearch() {
+ if (binding.searchText.text.toString().isEmpty() &&
+ !binding.chipHideFull.isChecked && !binding.chipHideEmpty.isChecked
+ ) {
+ adapter.updateRooms(NetPlayManager.getPublicRooms())
+ return
+ }
+
+ val baseList = NetPlayManager.getPublicRooms()
+ val filteredList = baseList.filter { room ->
+ (!binding.chipHideFull.isChecked || room.members.size < room.maxPlayers) &&
+ (!binding.chipHideEmpty.isChecked || room.members.isNotEmpty())
+ }
+
+ if (binding.searchText.text.toString().isEmpty() &&
+ (binding.chipHideFull.isChecked || binding.chipHideEmpty.isChecked)
+ ) {
+ adapter.updateRooms(filteredList)
+ return
+ }
+
+ val searchTerm = binding.searchText.text.toString().lowercase(Locale.getDefault())
+ val searchAlgorithm = if (searchTerm.length > 1) Jaccard(2) else JaroWinkler()
+ val sortedList: List<NetPlayManager.RoomInfo> = filteredList.mapNotNull { room ->
+ val roomName = room.name.lowercase(Locale.getDefault())
+
+ val score = searchAlgorithm.similarity(roomName, searchTerm)
+ if (score > 0.03) {
+ ScoreItem(score, room)
+ } else {
+ null
+ }
+ }.sortedByDescending { it ->
+ it.score
+ }.map { it.item }
+ adapter.updateRooms(sortedList)
+
+ }
+ }
+
+ private inner class ScoreItem(val score: Double, val item: NetPlayManager.RoomInfo)
+}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/dialogs/NetPlayDialog.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/dialogs/NetPlayDialog.kt
index 494b8524ff..a95b0255d3 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/dialogs/NetPlayDialog.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/dialogs/NetPlayDialog.kt
@@ -1,12 +1,9 @@
-// Copyright 2024 Mandarine Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-// SPDX-FileCopyrightText: Copyright yuzu/Citra Emulator Project / Eden Emulator Project
+// SPDX-FileCopyrightText: 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.dialogs
+import android.annotation.SuppressLint
import android.content.Context
import org.yuzu.yuzu_emu.R
import android.content.res.Configuration
@@ -16,6 +13,7 @@ import android.os.Looper
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
+import android.widget.ArrayAdapter
import android.widget.PopupMenu
import android.widget.Toast
import androidx.recyclerview.widget.LinearLayoutManager
@@ -32,10 +30,13 @@ import org.yuzu.yuzu_emu.databinding.ItemButtonNetplayBinding
import org.yuzu.yuzu_emu.databinding.ItemTextNetplayBinding
import org.yuzu.yuzu_emu.utils.CompatUtils
import org.yuzu.yuzu_emu.network.NetPlayManager
+import org.yuzu.yuzu_emu.utils.GameHelper
class NetPlayDialog(context: Context) : BottomSheetDialog(context) {
private lateinit var adapter: NetPlayAdapter
+ private val gameNameList: MutableList<Array<String>> = mutableListOf()
+ private val gameIdList: MutableList<Array<Long>> = mutableListOf()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -71,6 +72,17 @@ class NetPlayDialog(context: Context) : BottomSheetDialog(context) {
else -> {
DialogMultiplayerConnectBinding.inflate(layoutInflater).apply {
setContentView(root)
+ for (game in GameHelper.cachedGameList) {
+ val gameName = game.title
+ if (gameNameList.none { it[0] == gameName }) {
+ gameNameList.add(arrayOf(gameName))
+ }
+
+ val gameId = game.programId.toLong()
+ if (gameIdList.none { it[0] == gameId }) {
+ gameIdList.add(arrayOf(gameId))
+ }
+ }
btnCreate.setOnClickListener {
showNetPlayInputDialog(true)
dismiss()
@@ -79,6 +91,10 @@ class NetPlayDialog(context: Context) : BottomSheetDialog(context) {
showNetPlayInputDialog(false)
dismiss()
}
+ btnLobbyBrowser.setOnClickListener {
+ LobbyBrowser(context).show()
+ dismiss()
+ }
}
}
}
@@ -208,10 +224,11 @@ class NetPlayDialog(context: Context) : BottomSheetDialog(context) {
override fun getItemCount() = netPlayItems.size
}
+ @SuppressLint("NotifyDataSetChanged")
fun refreshAdapterItems() {
val handler = Handler(Looper.getMainLooper())
- NetPlayManager.setOnAdapterRefreshListener() { type, msg ->
+ NetPlayManager.setOnAdapterRefreshListener() { _, _ ->
handler.post {
adapter.netPlayItems.clear()
adapter.loadMultiplayerMenu()
@@ -244,6 +261,17 @@ class NetPlayDialog(context: Context) : BottomSheetDialog(context) {
binding.ipPort.setText(NetPlayManager.getRoomPort(activity))
binding.username.setText(NetPlayManager.getUsername(activity))
+ binding.dropdownPreferredGameName.apply {
+ setAdapter(
+ ArrayAdapter(
+ activity,
+ R.layout.dropdown_item,
+ gameNameList.map { it[0] }
+ )
+ )
+ }
+
+ binding.preferredGameName.visibility = if (isCreateRoom) View.VISIBLE else View.GONE
binding.roomName.visibility = if (isCreateRoom) View.VISIBLE else View.GONE
binding.maxPlayersContainer.visibility = if (isCreateRoom) View.VISIBLE else View.GONE
binding.maxPlayersLabel.text = context.getString(R.string.multiplayer_max_players_value, binding.maxPlayers.value.toInt())
@@ -259,6 +287,8 @@ class NetPlayDialog(context: Context) : BottomSheetDialog(context) {
val ipAddress = binding.ipAddress.text.toString()
val username = binding.username.text.toString()
val portStr = binding.ipPort.text.toString()
+ val preferredGameName = binding.dropdownPreferredGameName.text.toString()
+ val preferredGameId = gameIdList[gameNameList.indexOfFirst { it[0] == preferredGameName }][0]
val password = binding.password.text.toString()
val port = portStr.toIntOrNull() ?: run {
Toast.makeText(activity, R.string.multiplayer_port_invalid, Toast.LENGTH_LONG).show()
@@ -276,6 +306,13 @@ class NetPlayDialog(context: Context) : BottomSheetDialog(context) {
return@setOnClickListener
}
+ if (isCreateRoom && preferredGameName.isEmpty()) {
+ Toast.makeText(activity, R.string.multiplayer_preferred_game_name_invalid, Toast.LENGTH_LONG).show();
+ binding.btnConfirm.isEnabled = false
+ binding.btnConfirm.text = activity.getString(R.string.original_button_text)
+ return@setOnClickListener
+ }
+
if (ipAddress.length < 7 || username.length < 5) {
Toast.makeText(activity, R.string.multiplayer_input_invalid, Toast.LENGTH_LONG).show()
binding.btnConfirm.isEnabled = true
@@ -283,7 +320,7 @@ class NetPlayDialog(context: Context) : BottomSheetDialog(context) {
} else {
Handler(Looper.getMainLooper()).post {
val result = if (isCreateRoom) {
- NetPlayManager.netPlayCreateRoom(ipAddress, port, username, password, roomName, maxPlayers)
+ NetPlayManager.netPlayCreateRoom(ipAddress, port, username, preferredGameName, preferredGameId, password, roomName, maxPlayers)
} else {
NetPlayManager.netPlayJoinRoom(ipAddress, port, username, password)
}
@@ -397,4 +434,4 @@ class NetPlayDialog(context: Context) : BottomSheetDialog(context) {
}
}
-} \ No newline at end of file
+}
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 3a44e30bdb..c482e16492 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
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: Copyright yuzu/Citra Emulator Project / Eden Emulator Project
+// SPDX-FileCopyrightText: 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
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 6f16cf5b19..6eb6eb6d52 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
@@ -7,7 +7,9 @@ import org.yuzu.yuzu_emu.utils.NativeConfig
enum class StringSetting(override val key: String) : AbstractStringSetting {
DRIVER_PATH("driver_path"),
- DEVICE_NAME("device_name");
+ DEVICE_NAME("device_name"),
+ WEB_TOKEN("yuzu_token"),
+ ;
override fun getString(needsGlobal: Boolean): String = NativeConfig.getString(key, needsGlobal)
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 b0944c5f18..70d510b328 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
@@ -274,6 +274,13 @@ abstract class SettingsItem(
descriptionId = R.string.use_custom_rtc_description
)
)
+ put(
+ StringInputSetting(
+ StringSetting.WEB_TOKEN,
+ titleId = R.string.web_token,
+ descriptionId = R.string.web_token_description
+ )
+ )
put(DateTimeSetting(LongSetting.CUSTOM_RTC, titleId = R.string.set_custom_rtc))
put(
SingleChoiceSetting(
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 4bfe503b1d..08080a26f2 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
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: Copyright yuzu/Citra Emulator Project / Eden Emulator Project
+// SPDX-FileCopyrightText: 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.features.settings.ui
@@ -200,6 +200,10 @@ class SettingsFragmentPresenter(
add(IntSetting.LANGUAGE_INDEX.key)
add(BooleanSetting.USE_CUSTOM_RTC.key)
add(LongSetting.CUSTOM_RTC.key)
+
+ // TODO(alekpop): Add functionality
+// add(HeaderSetting(R.string.network))
+// add(StringSetting.WEB_TOKEN.key)
}
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AboutFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AboutFragment.kt
index fc627473a2..2879310007 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AboutFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AboutFragment.kt
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: Copyright yuzu/Citra Emulator Project / Eden Emulator Project
+// SPDX-FileCopyrightText: 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AddGameFolderDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AddGameFolderDialogFragment.kt
index b027547cef..cc95544d25 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AddGameFolderDialogFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AddGameFolderDialogFragment.kt
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: Copyright yuzu/Citra Emulator Project / Eden Emulator Project
+// SPDX-FileCopyrightText: 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.fragments
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AddonsFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AddonsFragment.kt
index 5229e25475..573549d84b 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AddonsFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AddonsFragment.kt
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: Copyright yuzu/Citra Emulator Project / Eden Emulator Project
+// SPDX-FileCopyrightText: 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.fragments
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AppletLauncherFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AppletLauncherFragment.kt
index f39bb1affd..3ab171a8d4 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AppletLauncherFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AppletLauncherFragment.kt
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: Copyright yuzu/Citra Emulator Project / Eden Emulator Project
+// SPDX-FileCopyrightText: 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.fragments
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt
index ce5c85011d..47b84a403a 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: Copyright yuzu/Citra Emulator Project / Eden Emulator Project
+// SPDX-FileCopyrightText: 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.fragments
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt
index 2f5918b59a..a79ce4619a 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt
@@ -1,7 +1,7 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
-// SPDX-FileCopyrightText: Copyright yuzu/Citra Emulator Project / Eden Emulator Project
+// SPDX-FileCopyrightText: 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameFoldersFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameFoldersFragment.kt
index 83ae5622a2..87b1533408 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameFoldersFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameFoldersFragment.kt
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: Copyright yuzu/Citra Emulator Project / Eden Emulator Project
+// SPDX-FileCopyrightText: 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.fragments
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameInfoFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameInfoFragment.kt
index 3cdee36d9c..7863e40ff5 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameInfoFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameInfoFragment.kt
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: Copyright yuzu/Citra Emulator Project / Eden Emulator Project
+// SPDX-FileCopyrightText: 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.fragments
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GamePropertiesFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GamePropertiesFragment.kt
index 8dbd69121b..f55edb418e 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GamePropertiesFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GamePropertiesFragment.kt
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: Copyright yuzu/Citra Emulator Project / Eden Emulator Project
+// SPDX-FileCopyrightText: 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.fragments
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 319d2d6a19..f9ecd6f49f 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
@@ -1,10 +1,6 @@
-// SPDX-FileCopyrightText: Copyright yuzu/Citra Emulator Project / Eden Emulator Project
+// SPDX-FileCopyrightText: 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
-// SPDX-FileCopyrightText: Copyright yuzu/Citra Emulator Project / Eden Emulator Project
-// SPDX-License-Identifier: GPL-3.0-or-later
-
-
package org.yuzu.yuzu_emu.fragments
import android.Manifest
@@ -128,7 +124,7 @@ class HomeSettingsFragment : Fragment() {
R.string.multiplayer_description,
R.drawable.ic_two_users,
{
- val action = mainActivity.displayMultiplayerDialog()
+ mainActivity.displayMultiplayerDialog()
},
)
)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/InstallableFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/InstallableFragment.kt
index 9c39f7b166..33146c82ca 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/InstallableFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/InstallableFragment.kt
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: Copyright yuzu/Citra Emulator Project / Eden Emulator Project
+// SPDX-FileCopyrightText: 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.fragments
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/LicensesFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/LicensesFragment.kt
index 5fdcea29fa..52d9ef43d4 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/LicensesFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/LicensesFragment.kt
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: Copyright yuzu/Citra Emulator Project / Eden Emulator Project
+// SPDX-FileCopyrightText: 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt
index bf622b9299..6828b32e84 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: Copyright yuzu/Citra Emulator Project / Eden Emulator Project
+// SPDX-FileCopyrightText: 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.fragments
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt
index 72a15ccef3..8e4b566766 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: Copyright yuzu/Citra Emulator Project / Eden Emulator Project
+// SPDX-FileCopyrightText: 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.model
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeViewModel.kt
index eb1a329e8d..08eee8c815 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeViewModel.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeViewModel.kt
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: Copyright yuzu/Citra Emulator Project / Eden Emulator Project
+// SPDX-FileCopyrightText: 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.model
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/network/NetPlayManager.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/network/NetPlayManager.kt
index 1570f04468..d1f538b38a 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/network/NetPlayManager.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/network/NetPlayManager.kt
@@ -1,16 +1,14 @@
-// Copyright 2024 Mandarine Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-// SPDX-FileCopyrightText: Copyright yuzu/Citra Emulator Project / Eden Emulator Project
+// SPDX-FileCopyrightText: 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
-
package org.yuzu.yuzu_emu.network
import android.app.Activity
import android.content.Context
+import android.net.ConnectivityManager
+import android.net.NetworkCapabilities
import android.net.wifi.WifiManager
+import android.os.Build
import android.os.Handler
import android.os.Looper
import android.text.format.Formatter
@@ -19,10 +17,27 @@ import androidx.preference.PreferenceManager
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.dialogs.ChatMessage
+import java.net.Inet4Address
object NetPlayManager {
- external fun netPlayCreateRoom(ipAddress: String, port: Int, username: String, password: String, roomName: String, maxPlayers: Int): Int
- external fun netPlayJoinRoom(ipAddress: String, port: Int, username: String, password: String): Int
+ external fun netPlayCreateRoom(
+ ipAddress: String,
+ port: Int,
+ username: String,
+ preferredGameName: String,
+ preferredGameId: Long,
+ password: String,
+ roomName: String,
+ maxPlayers: Int
+ ): Int
+
+ external fun netPlayJoinRoom(
+ ipAddress: String,
+ port: Int,
+ username: String,
+ password: String
+ ): Int
+
external fun netPlayRoomInfo(): Array<String>
external fun netPlayIsJoined(): Boolean
external fun netPlayIsHostedRoom(): Boolean
@@ -33,6 +48,28 @@ object NetPlayManager {
external fun netPlayGetBanList(): Array<String>
external fun netPlayBanUser(username: String)
external fun netPlayUnbanUser(username: String)
+ external fun netPlayGetPublicRooms(): Array<String>
+
+ data class RoomInfo(
+ val name: String,
+ val hasPassword: Boolean,
+ val maxPlayers: Int,
+ val ip: String,
+ val port: Int,
+ val description: String,
+ val owner: String,
+ val preferredGameId: Long,
+ val preferredGameName: String,
+ val members: MutableList<RoomMember> = mutableListOf()
+ )
+
+ data class RoomMember(
+ val username: String,
+ val nickname: String,
+ val gameId: Long,
+ val gameName: String
+ )
+
private var messageListener: ((Int, String) -> Unit)? = null
private var adapterRefreshListener: ((Int, String) -> Unit)? = null
@@ -41,11 +78,57 @@ object NetPlayManager {
messageListener = listener
}
+ fun getPublicRooms(): List<RoomInfo> {
+ val roomData = netPlayGetPublicRooms()
+ val rooms = mutableMapOf<String, RoomInfo>()
+
+ for (data in roomData) {
+ val parts = data.split("|")
+
+ if (parts[0] == "MEMBER" && parts.size >= 6) {
+ val roomName = parts[1]
+ val member = RoomMember(
+ username = parts[2],
+ nickname = parts[3],
+ gameId = parts[4].toLongOrNull() ?: 0L,
+ gameName = parts[5]
+ )
+ rooms[roomName]?.members?.add(member)
+ } else if (parts.size >= 9) {
+ val roomInfo = RoomInfo(
+ name = parts[0],
+ hasPassword = parts[1] == "1",
+ maxPlayers = parts[2].toIntOrNull() ?: 0,
+ ip = parts[3],
+ port = parts[4].toIntOrNull() ?: 0,
+ description = parts[5],
+ owner = parts[6],
+ preferredGameId = parts[7].toLongOrNull() ?: 0L,
+ preferredGameName = parts[8]
+ )
+ rooms[roomInfo.name] = roomInfo
+ }
+ }
+
+ return rooms.values.toList()
+ }
+
+ fun refreshRoomListAsync(callback: (List<RoomInfo>) -> Unit) {
+ Thread {
+ val rooms = getPublicRooms()
+
+ Handler(Looper.getMainLooper()).post {
+ callback(rooms)
+ }
+ }.start()
+ }
+
fun setOnAdapterRefreshListener(listener: (Int, String) -> Unit) {
adapterRefreshListener = listener
}
- fun getUsername(activity: Context): String { val prefs = PreferenceManager.getDefaultSharedPreferences(activity)
+ fun getUsername(activity: Context): String {
+ val prefs = PreferenceManager.getDefaultSharedPreferences(activity)
val name = "Eden${(Math.random() * 100).toInt()}"
return prefs.getString("NetPlayUsername", name) ?: name
}
@@ -103,31 +186,36 @@ object NetPlayManager {
if (parts.size == 2) {
val nickname = parts[0].trim()
val chatMessage = parts[1].trim()
- addChatMessage(ChatMessage(
- nickname = nickname,
- username = "",
- message = chatMessage
- ))
+ addChatMessage(
+ ChatMessage(
+ nickname = nickname,
+ username = "",
+ message = chatMessage
+ )
+ )
}
}
+
NetPlayStatus.MEMBER_JOIN,
NetPlayStatus.MEMBER_LEAVE,
NetPlayStatus.MEMBER_KICKED,
NetPlayStatus.MEMBER_BANNED -> {
- addChatMessage(ChatMessage(
- nickname = "System",
- username = "",
- message = message
- ))
+ addChatMessage(
+ ChatMessage(
+ nickname = "System",
+ username = "",
+ message = message
+ )
+ )
}
}
- Handler(Looper.getMainLooper()).post {
- if (!isChatOpen) {
- Toast.makeText(context, message, Toast.LENGTH_SHORT).show()
- }
+ Handler(Looper.getMainLooper()).post {
+ if (!isChatOpen) {
+ Toast.makeText(context, message, Toast.LENGTH_SHORT).show()
}
+ }
messageListener?.invoke(type, msg)
@@ -159,34 +247,59 @@ object NetPlayManager {
NetPlayStatus.ROOM_MODERATOR -> context.getString(R.string.multiplayer_room_moderator)
NetPlayStatus.MEMBER_JOIN -> context.getString(R.string.multiplayer_member_join, msg)
NetPlayStatus.MEMBER_LEAVE -> context.getString(R.string.multiplayer_member_leave, msg)
- NetPlayStatus.MEMBER_KICKED -> context.getString(R.string.multiplayer_member_kicked, msg)
- NetPlayStatus.MEMBER_BANNED -> context.getString(R.string.multiplayer_member_banned, msg)
+ NetPlayStatus.MEMBER_KICKED -> context.getString(
+ R.string.multiplayer_member_kicked,
+ msg
+ )
+
+ NetPlayStatus.MEMBER_BANNED -> context.getString(
+ R.string.multiplayer_member_banned,
+ msg
+ )
+
NetPlayStatus.ADDRESS_UNBANNED -> context.getString(R.string.multiplayer_address_unbanned)
NetPlayStatus.CHAT_MESSAGE -> msg
else -> ""
}
}
+ fun isConnectedToWifi(activity: Activity): Boolean {
+ val connectivityManager = activity.getSystemService(ConnectivityManager::class.java)
+ val network = connectivityManager.activeNetwork
+ val capabilities = connectivityManager.getNetworkCapabilities(network)
+ return capabilities?.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) == true
+ }
+
fun getIpAddressByWifi(activity: Activity): String {
- var ipAddress = 0
- val wifiManager = activity.getSystemService(WifiManager::class.java)
- val wifiInfo = wifiManager.connectionInfo
- if (wifiInfo != null) {
- ipAddress = wifiInfo.ipAddress
- }
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+ // For Android 12 (API 31) and above
+ val connectivityManager = activity.getSystemService(ConnectivityManager::class.java)
+ val network = connectivityManager.activeNetwork
+ val capabilities = connectivityManager.getNetworkCapabilities(network)
- if (ipAddress == 0) {
- val dhcpInfo = wifiManager.dhcpInfo
- if (dhcpInfo != null) {
- ipAddress = dhcpInfo.ipAddress
+ if (capabilities?.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) == true) {
+ val linkProperties = connectivityManager.getLinkProperties(network)
+ linkProperties?.linkAddresses?.firstOrNull { it.address is Inet4Address }?.let {
+ return it.address.hostAddress ?: "192.168.0.1"
+ }
}
}
- return if (ipAddress == 0) {
- "192.168.0.1"
- } else {
- Formatter.formatIpAddress(ipAddress)
+ // For Android 11 (API 30) and below
+ try {
+ val connectivityManager = activity.getSystemService(ConnectivityManager::class.java)
+ val network = connectivityManager.activeNetwork
+ if (network != null) {
+ val linkProperties = connectivityManager.getLinkProperties(network)
+ linkProperties?.linkAddresses?.firstOrNull { it.address is Inet4Address }?.let {
+ return it.address.hostAddress ?: "192.168.0.1"
+ }
+ }
+ } catch (e: Exception) {
+ e.printStackTrace()
}
+
+ return "192.168.0.1"
}
fun getBanList(): List<String> {
@@ -223,4 +336,4 @@ object NetPlayManager {
const val ADDRESS_UNBANNED = 26
const val CHAT_MESSAGE = 27
}
-} \ No newline at end of file
+}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt
index b0121ad132..893aa5f41a 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: Copyright yuzu/Citra Emulator Project / Eden Emulator Project
+// SPDX-FileCopyrightText: 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.ui
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
index eb8837b8f1..3e3759d7eb 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
@@ -1,10 +1,6 @@
-// SPDX-FileCopyrightText: Copyright yuzu/Citra Emulator Project / Eden Emulator Project
+// SPDX-FileCopyrightText: 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
-// SPDX-FileCopyrightText: Copyright yuzu/Citra Emulator Project / Eden Emulator Project
-// SPDX-License-Identifier: GPL-3.0-or-later
-
-
package org.yuzu.yuzu_emu.ui.main
import android.content.Intent
@@ -72,9 +68,8 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
ThemeHelper.ThemeChangeListener(this)
ThemeHelper.setTheme(this)
- NativeLibrary.netPlayInit()
-
super.onCreate(savedInstanceState)
+ NativeLibrary.initMultiplayer()
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/CompatUtils.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/CompatUtils.kt
index 6467f7c931..640aa8f80c 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/CompatUtils.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/CompatUtils.kt
@@ -1,8 +1,4 @@
-// Copyright 2024 Mandarine Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-// SPDX-FileCopyrightText: Copyright yuzu/Citra Emulator Project / Eden Emulator Project
+// SPDX-FileCopyrightText: 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.utils
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt
index 579b600f1a..e27bc94696 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt
@@ -1,6 +1,9 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-FileCopyrightText: 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
package org.yuzu.yuzu_emu.utils
import android.content.SharedPreferences
@@ -13,11 +16,15 @@ import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.model.Game
import org.yuzu.yuzu_emu.model.GameDir
import org.yuzu.yuzu_emu.model.MinimalDocumentFile
+import androidx.core.content.edit
+import androidx.core.net.toUri
object GameHelper {
private const val KEY_OLD_GAME_PATH = "game_path"
const val KEY_GAMES = "Games"
+ var cachedGameList = mutableListOf<Game>()
+
private lateinit var preferences: SharedPreferences
fun getGames(): List<Game> {
@@ -29,7 +36,7 @@ object GameHelper {
val oldGamesDir = preferences.getString(KEY_OLD_GAME_PATH, "") ?: ""
if (oldGamesDir.isNotEmpty()) {
gameDirs.add(GameDir(oldGamesDir, true))
- preferences.edit().remove(KEY_OLD_GAME_PATH).apply()
+ preferences.edit() { remove(KEY_OLD_GAME_PATH) }
}
gameDirs.addAll(NativeConfig.getGameDirs())
@@ -44,7 +51,7 @@ object GameHelper {
val badDirs = mutableListOf<Int>()
gameDirs.forEachIndexed { index: Int, gameDir: GameDir ->
- val gameDirUri = Uri.parse(gameDir.uriString)
+ val gameDirUri = gameDir.uriString.toUri()
val isValid = FileUtil.isTreeUriValid(gameDirUri)
if (isValid) {
addGamesRecursive(
@@ -72,11 +79,12 @@ object GameHelper {
games.forEach {
serializedGames.add(Json.encodeToString(it))
}
- preferences.edit()
- .remove(KEY_GAMES)
- .putStringSet(KEY_GAMES, serializedGames)
- .apply()
+ preferences.edit() {
+ remove(KEY_GAMES)
+ .putStringSet(KEY_GAMES, serializedGames)
+ }
+ cachedGameList = games.toMutableList()
return games.toList()
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ThemeHelper.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ThemeHelper.kt
index 89ec3672ca..a035628588 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ThemeHelper.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ThemeHelper.kt
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: Copyright yuzu/Citra Emulator Project / Eden Emulator Project
+// SPDX-FileCopyrightText: 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.utils
diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp
index f18fc2e180..7a450c068a 100644
--- a/src/android/app/src/main/jni/native.cpp
+++ b/src/android/app/src/main/jni/native.cpp
@@ -1,7 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
-// SPDX-FileCopyrightText: Copyright yuzu/Citra Emulator Project / Eden Emulator Project
+// SPDX-FileCopyrightText: 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
@@ -68,12 +68,17 @@
#include "video_core/vulkan_common/vulkan_instance.h"
#include "video_core/vulkan_common/vulkan_surface.h"
#include "video_core/shader_notify.h"
+#include "network/announce_multiplayer_session.h"
#define jconst [[maybe_unused]] const auto
#define jauto [[maybe_unused]] auto
static EmulationSession s_instance;
+//Abdroid Multiplayer which can be initialized with parameters
+std::unique_ptr<AndroidMultiplayer> multiplayer{nullptr};
+std::shared_ptr<Core::AnnounceMultiplayerSession> announce_multiplayer_session;
+
EmulationSession::EmulationSession() {
m_vfs = std::make_shared<FileSys::RealVfsFilesystem>();
}
@@ -916,12 +921,33 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_areKeysPresent(JNIEnv* env, jobje
return ContentManager::AreKeysPresent();
}
+JNIEXPORT void JNICALL
+Java_org_yuzu_yuzu_1emu_NativeLibrary_initMultiplayer(
+ JNIEnv* env, [[maybe_unused]] jobject obj) {
+ if (multiplayer) {
+ return;
+ }
+
+ announce_multiplayer_session = std::make_shared<Core::AnnounceMultiplayerSession>();
+
+ multiplayer = std::make_unique<AndroidMultiplayer>(s_instance.System(), announce_multiplayer_session);
+ multiplayer->NetworkInit();
+}
+
+JNIEXPORT jobjectArray JNICALL
+Java_org_yuzu_yuzu_1emu_network_NetPlayManager_netPlayGetPublicRooms(
+ JNIEnv *env, [[maybe_unused]] jobject obj) {
+ return Common::Android::ToJStringArray(env, multiplayer->NetPlayGetPublicRooms());
+}
+
JNIEXPORT jint JNICALL Java_org_yuzu_yuzu_1emu_network_NetPlayManager_netPlayCreateRoom(
JNIEnv* env, [[maybe_unused]] jobject obj, jstring ipaddress, jint port,
- jstring username, jstring password, jstring room_name, jint max_players) {
+ jstring username, jstring preferredGameName, jlong preferredGameId, jstring password,
+ jstring room_name, jint max_players) {
return static_cast<jint>(
- NetPlayCreateRoom(Common::Android::GetJString(env, ipaddress), port,
- Common::Android::GetJString(env, username), Common::Android::GetJString(env, password),
+ multiplayer->NetPlayCreateRoom(Common::Android::GetJString(env, ipaddress), port,
+ Common::Android::GetJString(env, username), Common::Android::GetJString(env, preferredGameName),
+ preferredGameId,Common::Android::GetJString(env, password),
Common::Android::GetJString(env, room_name), max_players));
}
@@ -929,70 +955,63 @@ JNIEXPORT jint JNICALL Java_org_yuzu_yuzu_1emu_network_NetPlayManager_netPlayJoi
JNIEnv* env, [[maybe_unused]] jobject obj, jstring ipaddress, jint port,
jstring username, jstring password) {
return static_cast<jint>(
- NetPlayJoinRoom(Common::Android::GetJString(env, ipaddress), port,
+ multiplayer->NetPlayJoinRoom(Common::Android::GetJString(env, ipaddress), port,
Common::Android::GetJString(env, username), Common::Android::GetJString(env, password)));
}
JNIEXPORT jobjectArray JNICALL
Java_org_yuzu_yuzu_1emu_network_NetPlayManager_netPlayRoomInfo(
JNIEnv* env, [[maybe_unused]] jobject obj) {
- return Common::Android::ToJStringArray(env, NetPlayRoomInfo());
+ return Common::Android::ToJStringArray(env, multiplayer->NetPlayRoomInfo());
}
JNIEXPORT jboolean JNICALL
Java_org_yuzu_yuzu_1emu_network_NetPlayManager_netPlayIsJoined(
[[maybe_unused]] JNIEnv* env, [[maybe_unused]] jobject obj) {
- return NetPlayIsJoined();
+ return multiplayer->NetPlayIsJoined();
}
JNIEXPORT jboolean JNICALL
Java_org_yuzu_yuzu_1emu_network_NetPlayManager_netPlayIsHostedRoom(
[[maybe_unused]] JNIEnv* env, [[maybe_unused]] jobject obj) {
- return NetPlayIsHostedRoom();
+ return multiplayer->NetPlayIsHostedRoom();
}
JNIEXPORT void JNICALL
Java_org_yuzu_yuzu_1emu_network_NetPlayManager_netPlaySendMessage(
JNIEnv* env, [[maybe_unused]] jobject obj, jstring msg) {
- NetPlaySendMessage(Common::Android::GetJString(env, msg));
+ multiplayer->NetPlaySendMessage(Common::Android::GetJString(env, msg));
}
JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_network_NetPlayManager_netPlayKickUser(
JNIEnv* env, [[maybe_unused]] jobject obj, jstring username) {
- NetPlayKickUser(Common::Android::GetJString(env, username));
+ multiplayer->NetPlayKickUser(Common::Android::GetJString(env, username));
}
JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_network_NetPlayManager_netPlayLeaveRoom(
[[maybe_unused]] JNIEnv* env, [[maybe_unused]] jobject obj) {
- NetPlayLeaveRoom();
+ multiplayer->NetPlayLeaveRoom();
}
JNIEXPORT jboolean JNICALL
Java_org_yuzu_yuzu_1emu_network_NetPlayManager_netPlayIsModerator(
[[maybe_unused]] JNIEnv* env, [[maybe_unused]] jobject obj) {
- return NetPlayIsModerator();
+ return multiplayer->NetPlayIsModerator();
}
JNIEXPORT jobjectArray JNICALL
Java_org_yuzu_yuzu_1emu_network_NetPlayManager_netPlayGetBanList(
JNIEnv* env, [[maybe_unused]] jobject obj) {
- return Common::Android::ToJStringArray(env, NetPlayGetBanList());
+ return Common::Android::ToJStringArray(env, multiplayer->NetPlayGetBanList());
}
JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_network_NetPlayManager_netPlayBanUser(
JNIEnv* env, [[maybe_unused]] jobject obj, jstring username) {
- NetPlayBanUser(Common::Android::GetJString(env, username));
+ multiplayer->NetPlayBanUser(Common::Android::GetJString(env, username));
}
JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_network_NetPlayManager_netPlayUnbanUser(
JNIEnv* env, [[maybe_unused]] jobject obj, jstring username) {
- NetPlayUnbanUser(Common::Android::GetJString(env, username));
-}
-
-JNIEXPORT void JNICALL
-Java_org_yuzu_yuzu_1emu_NativeLibrary_netPlayInit(
- JNIEnv* env, [[maybe_unused]] jobject obj) {
- NetworkInit(&EmulationSession::GetInstance().System().GetRoomNetwork());
+ multiplayer->NetPlayUnbanUser(Common::Android::GetJString(env, username));
}
-
} // extern "C"
diff --git a/src/android/app/src/main/res/drawable/ic_ip.xml b/src/android/app/src/main/res/drawable/ic_ip.xml
new file mode 100644
index 0000000000..ee02b72e29
--- /dev/null
+++ b/src/android/app/src/main/res/drawable/ic_ip.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="?attr/colorControlNormal"
+ android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM11,19.93c-3.95,-0.49 -7,-3.85 -7,-7.93 0,-0.62 0.08,-1.21 0.21,-1.79L9,15v1c0,1.1 0.9,2 2,2v1.93zM17.9,17.39c-0.26,-0.81 -1,-1.39 -1.9,-1.39h-1v-3c0,-0.55 -0.45,-1 -1,-1L8,12v-2h2c0.55,0 1,-0.45 1,-1L11,7h2c1.1,0 2,-0.9 2,-2v-0.41c2.93,1.19 5,4.06 5,7.41 0,2.08 -0.8,3.97 -2.1,5.39z"/>
+</vector>
diff --git a/src/android/app/src/main/res/drawable/ic_joined.xml b/src/android/app/src/main/res/drawable/ic_joined.xml
new file mode 100644
index 0000000000..c8f3dd5225
--- /dev/null
+++ b/src/android/app/src/main/res/drawable/ic_joined.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M16,11c1.66,0 2.99,-1.34 2.99,-3S17.66,5 16,5c-1.66,0 -3,1.34 -3,3s1.34,3 3,3zM8,11c1.66,0 2.99,-1.34 2.99,-3S9.66,5 8,5C6.34,5 5,6.34 5,8s1.34,3 3,3zM8,13c-2.33,0 -7,1.17 -7,3.5L1,19h14v-2.5c0,-2.33 -4.67,-3.5 -7,-3.5zM16,13c-0.29,0 -0.62,0.02 -0.97,0.05 1.16,0.84 1.97,1.97 1.97,3.45L17,19h6v-2.5c0,-2.33 -4.67,-3.5 -7,-3.5z"/>
+</vector>
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..8248dc2c24
--- /dev/null
+++ b/src/android/app/src/main/res/drawable/ic_multiplayer.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M16,11c1.66,0 2.99,-1.34 2.99,-3S17.66,5 16,5c-1.66,0 -3,1.34 -3,3s1.34,3 3,3zM8,11c1.66,0 2.99,-1.34 2.99,-3S9.66,5 8,5C6.34,5 5,6.34 5,8s1.34,3 3,3zM8,13c-2.33,0 -7,1.17 -7,3.5L1,19h14v-2.5c0,-2.33 -4.67,-3.5 -7,-3.5zM16,13c-0.29,0 -0.62,0.02 -0.97,0.05 1.16,0.84 1.97,1.97 1.97,3.45L17,19h6v-2.5c0,-2.33 -4.67,-3.5 -7,-3.5z"/>
+</vector>
diff --git a/src/android/app/src/main/res/layout/dialog_lobby_browser.xml b/src/android/app/src/main/res/layout/dialog_lobby_browser.xml
new file mode 100644
index 0000000000..a016e48823
--- /dev/null
+++ b/src/android/app/src/main/res/layout/dialog_lobby_browser.xml
@@ -0,0 +1,220 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <com.google.android.material.appbar.AppBarLayout
+ android:id="@+id/appbar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="@android:color/transparent"
+ android:elevation="0dp">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ app:layout_scrollFlags="scroll|enterAlways|snap">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:paddingTop="16dp"
+ android:paddingHorizontal="16dp"
+ android:gravity="center_vertical">
+
+ <Space
+ android:layout_width="48dp"
+ android:layout_height="0dp" />
+
+ <TextView
+ android:id="@+id/title"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:gravity="center"
+ android:text="@string/multiplayer_room_browser"
+ android:textAppearance="@style/TextAppearance.Material3.TitleLarge" />
+
+ <FrameLayout
+ android:layout_width="48dp"
+ android:layout_height="48dp">
+
+ <com.google.android.material.button.MaterialButton
+ android:id="@+id/refresh_button"
+ style="@style/Widget.Material3.Button.IconButton"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_gravity="center"
+ android:text="@string/refresh"
+ app:icon="@drawable/ic_refresh" />
+
+ <ProgressBar
+ android:id="@+id/progress_bar"
+ style="?android:attr/progressBarStyleSmall"
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:layout_gravity="center"
+ android:visibility="gone" />
+ </FrameLayout>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="horizontal">
+
+ <com.google.android.material.card.MaterialCardView
+ android:id="@+id/search_background"
+ style="?attr/materialCardViewFilledStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ android:layout_marginStart="16dp"
+ android:layout_marginTop="12dp"
+ app:cardCornerRadius="24dp">
+
+ <LinearLayout
+ android:id="@+id/search_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_marginStart="16dp"
+ android:layout_marginEnd="48dp"
+ android:orientation="horizontal">
+
+ <ImageView
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:layout_gravity="center_vertical"
+ android:layout_marginEnd="16dp"
+ android:src="@drawable/ic_search"
+ app:tint="?attr/colorOnSurfaceVariant" />
+
+ <EditText
+ android:id="@+id/search_text"
+ android:layout_width="180dp"
+ android:layout_height="match_parent"
+ android:background="@android:color/transparent"
+ android:hint="@string/multiplayer_search_public_lobbies"
+ android:imeOptions="flagNoFullscreen"
+ android:inputType="text"
+ android:maxLines="1" />
+
+ </LinearLayout>
+
+ <ImageView
+ android:id="@+id/clear_button"
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:layout_gravity="center_vertical|end"
+ android:layout_marginEnd="16dp"
+ android:background="?attr/selectableItemBackground"
+ android:src="@drawable/ic_clear"
+ android:visibility="invisible"
+ app:tint="?attr/colorOnSurfaceVariant"
+ tools:visibility="visible" />
+
+ </com.google.android.material.card.MaterialCardView>
+
+ <com.google.android.material.button.MaterialButton
+ android:id="@+id/btn_submit"
+ style="@style/Widget.Material3.Button.ElevatedButton"
+ android:layout_width="110dp"
+ android:layout_height="48dp"
+ android:layout_marginTop="12dp"
+ android:text="@string/submit" />
+ </LinearLayout>
+
+
+ <HorizontalScrollView
+ android:id="@+id/horizontalScrollView"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:fadingEdge="horizontal"
+ android:scrollbars="none"
+ android:layout_marginVertical="8dp"
+ android:clipToPadding="false"
+ android:paddingHorizontal="16dp">
+
+ <LinearLayout
+ android:id="@+id/chips"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:clipToPadding="false">
+
+ <com.google.android.material.chip.Chip
+ android:id="@+id/chip_hide_empty"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:checkable="true"
+ android:checked="false"
+ android:text="@string/multiplayer_hide_empty_rooms"
+ app:chipCornerRadius="16dp" />
+
+ <com.google.android.material.chip.Chip
+ android:id="@+id/chip_hide_full"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:checkable="true"
+ android:checked="false"
+ android:text="@string/multiplayer_hide_full_rooms"
+ app:chipCornerRadius="16dp" />
+
+ </LinearLayout>
+ </HorizontalScrollView>
+ </LinearLayout>
+ </com.google.android.material.appbar.AppBarLayout>
+
+ <androidx.recyclerview.widget.RecyclerView
+ android:id="@+id/room_list"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:clipToPadding="false"
+ android:contentDescription="@string/room_list"
+ android:paddingBottom="16dp"
+ android:scrollbars="vertical"
+ app:layout_behavior="@string/appbar_scrolling_view_behavior" />
+
+ <LinearLayout
+ android:id="@+id/empty_view"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:orientation="vertical"
+ android:gravity="center"
+ android:padding="32dp"
+ android:visibility="gone">
+
+ <ImageView
+ android:layout_width="72dp"
+ android:layout_height="72dp"
+ android:layout_marginBottom="16dp"
+ android:src="@drawable/ic_refresh"
+ android:alpha="0.5"
+ app:tint="?attr/colorOnSurface" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/multiplayer_no_rooms_found"
+ android:textAppearance="@style/TextAppearance.Material3.TitleMedium" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dp"
+ android:text="@string/multiplayer_tap_refresh_to_check_again"
+ android:textAppearance="@style/TextAppearance.Material3.BodyMedium" />
+
+ <com.google.android.material.button.MaterialButton
+ android:id="@+id/empty_refresh_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="16dp"
+ android:text="@string/refresh"
+ app:icon="@drawable/ic_refresh" />
+ </LinearLayout>
+
+</androidx.coordinatorlayout.widget.CoordinatorLayout> \ No newline at end of file
diff --git a/src/android/app/src/main/res/layout/dialog_multiplayer_connect.xml b/src/android/app/src/main/res/layout/dialog_multiplayer_connect.xml
index 36a77d3951..a33f65896e 100644
--- a/src/android/app/src/main/res/layout/dialog_multiplayer_connect.xml
+++ b/src/android/app/src/main/res/layout/dialog_multiplayer_connect.xml
@@ -28,7 +28,7 @@
<ImageView
android:layout_width="140dp"
- android:layout_height="140dp"
+ android:layout_height="114dp"
android:layout_gravity="center"
android:layout_marginTop="16dp"
android:layout_marginBottom="24dp"
@@ -38,19 +38,45 @@
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:orientation="horizontal"
+ android:orientation="horizontal">
+
+ <Space
+ android:layout_width="20dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1" />
+
+ <com.google.android.material.button.MaterialButton
+ android:id="@+id/btn_lobby_browser"
+ style="@style/Widget.Material3.Button.ElevatedButton"
+ android:layout_width="175dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="@string/multiplayer_public_room"
+ app:cornerRadius="16dp"
+ app:icon="@drawable/ic_search" />
+
+ <Space
+ android:layout_width="20dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp"
- android:layout_marginBottom="8dp">
+ android:layout_marginBottom="8dp"
+ android:orientation="horizontal">
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_join"
- style="@style/Widget.Material3.Button.TonalButton"
+ style="@style/Widget.Material3.Button.ElevatedButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/multiplayer_join_room"
- app:icon="@drawable/ic_install"
- app:cornerRadius="16dp" />
+ app:cornerRadius="16dp"
+ app:icon="@drawable/ic_install" />
<Space
android:layout_width="16dp"
@@ -58,13 +84,13 @@
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_create"
- style="@style/Widget.Material3.Button"
+ style="@style/Widget.Material3.Button.ElevatedButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/multiplayer_create_room"
- app:icon="@drawable/ic_add"
- app:cornerRadius="16dp" />
+ app:cornerRadius="16dp"
+ app:icon="@drawable/ic_add" />
</LinearLayout>
diff --git a/src/android/app/src/main/res/layout/dialog_multiplayer_room.xml b/src/android/app/src/main/res/layout/dialog_multiplayer_room.xml
index 53afda9319..c79be8a2ca 100644
--- a/src/android/app/src/main/res/layout/dialog_multiplayer_room.xml
+++ b/src/android/app/src/main/res/layout/dialog_multiplayer_room.xml
@@ -60,6 +60,21 @@
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
+ style="@style/Widget.Material3.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
+ android:id="@+id/preferred_game_name"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:padding="8dp"
+ android:hint="@string/multiplayer_preferred_game_name">
+
+ <com.google.android.material.textfield.MaterialAutoCompleteTextView
+ android:id="@+id/dropdown_preferred_game_name"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:inputType="none" />
+ </com.google.android.material.textfield.TextInputLayout>
+
+ <com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/multiplayer_password"
diff --git a/src/android/app/src/main/res/layout/dialog_password_input.xml b/src/android/app/src/main/res/layout/dialog_password_input.xml
new file mode 100644
index 0000000000..f21a63952b
--- /dev/null
+++ b/src/android/app/src/main/res/layout/dialog_password_input.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:padding="16dp">
+
+ <com.google.android.material.textfield.TextInputLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:hint="@string/multiplayer_password"
+ android:layout_marginTop="8dp">
+
+ <com.google.android.material.textfield.TextInputEditText
+ android:id="@+id/password_input"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:inputType="textPassword" />
+
+ </com.google.android.material.textfield.TextInputLayout>
+</LinearLayout> \ No newline at end of file
diff --git a/src/android/app/src/main/res/layout/dropdown_item.xml b/src/android/app/src/main/res/layout/dropdown_item.xml
new file mode 100644
index 0000000000..e620712e11
--- /dev/null
+++ b/src/android/app/src/main/res/layout/dropdown_item.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/dropdown_text"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:padding="16dp"
+ android:ellipsize="end"
+ android:maxLines="1"
+ android:textAppearance="?attr/textAppearanceSubtitle1" />
diff --git a/src/android/app/src/main/res/layout/item_lobby_room.xml b/src/android/app/src/main/res/layout/item_lobby_room.xml
new file mode 100644
index 0000000000..b69090e212
--- /dev/null
+++ b/src/android/app/src/main/res/layout/item_lobby_room.xml
@@ -0,0 +1,113 @@
+<?xml version="1.0" encoding="utf-8"?>
+<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ style="?attr/materialCardViewFilledStyle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="24dp"
+ android:layout_marginHorizontal="12dp"
+ android:background="?attr/selectableItemBackground"
+ android:backgroundTint="?attr/colorSurfaceVariant"
+ android:clickable="true"
+ android:focusable="true">
+
+ <androidx.constraintlayout.widget.ConstraintLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:padding="12dp">
+
+ <ImageView
+ android:id="@+id/lock_icon"
+ android:layout_width="20dp"
+ android:layout_height="20dp"
+ android:contentDescription="@string/multiplayer_password_protected"
+ android:src="@drawable/ic_lock"
+ android:visibility="gone"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ app:tint="?attr/colorOnSurface" />
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:layout_marginEnd="8dp"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintEnd_toStartOf="@id/lock_icon"
+ app:layout_constraintBottom_toBottomOf="parent">
+
+ <com.google.android.material.textview.MaterialTextView
+ android:id="@+id/room_name"
+ style="@style/TextAppearance.Material3.BodyMedium"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAlignment="viewStart"
+ android:textSize="16sp"
+ android:textStyle="bold"
+ tools:text="Room Name" />
+
+ <com.google.android.material.textview.MaterialTextView
+ android:id="@+id/room_owner"
+ style="@style/TextAppearance.Material3.BodySmall"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="5dp"
+ android:textAlignment="viewStart"
+ android:textSize="14sp"
+ tools:text="Hosted by: Owner" />
+
+ <LinearLayout
+ android:id="@+id/game_info"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="5dp"
+ android:orientation="horizontal">
+
+ <ImageView
+ android:layout_width="16dp"
+ android:layout_height="16dp"
+ android:layout_gravity="center_vertical"
+ android:contentDescription="@string/multiplayer_game"
+ android:src="@drawable/ic_controller" />
+
+ <com.google.android.material.textview.MaterialTextView
+ android:id="@+id/game_name"
+ style="@style/TextAppearance.Material3.LabelMedium"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="4dp"
+ android:ellipsize="end"
+ android:singleLine="true"
+ android:textStyle="bold"
+ tools:text="Game Name" />
+ </LinearLayout>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:gravity="center_vertical"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent">
+
+ <ImageView
+ android:layout_width="16dp"
+ android:layout_height="16dp"
+ android:src="@drawable/ic_user"
+ android:contentDescription="@string/multiplayer_player_count"
+ app:tint="?attr/colorAccent" />
+
+ <com.google.android.material.textview.MaterialTextView
+ android:id="@+id/player_count"
+ style="@style/TextAppearance.Material3.BodyMedium"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="4dp"
+ android:textColor="?attr/colorAccent"
+ tools:text="2/4" />
+ </LinearLayout>
+ </androidx.constraintlayout.widget.ConstraintLayout>
+</com.google.android.material.card.MaterialCardView> \ No newline at end of file
diff --git a/src/android/app/src/main/res/values-fr-rSN/strings.xml b/src/android/app/src/main/res/values-fr-rSN/strings.xml
new file mode 100644
index 0000000000..96b0b3a560
--- /dev/null
+++ b/src/android/app/src/main/res/values-fr-rSN/strings.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="app_name">Eden</string>
+</resources> \ No newline at end of file
diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml
index e953666d20..ed5dfca596 100644
--- a/src/android/app/src/main/res/values/strings.xml
+++ b/src/android/app/src/main/res/values/strings.xml
@@ -399,6 +399,11 @@
<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="web_token">Web Token</string>
+ <string name="web_token_description">Web token used for creating public lobbies. It is a 48-character string containing only lowercase a-z.</string>
+ <string name="network">Network</string>
+
<!-- Graphics settings strings -->
<string name="frame_skipping">WIP: Frameskip</string>
<string name="frame_skipping_description">Toggle frame skipping to improve performance by reducing the number of rendered frames. This feature is still being worked on and will be enabled in future releases.</string>
diff --git a/src/common/android/android_common.cpp b/src/common/android/android_common.cpp
index 0df2dd7b35..a1cf081695 100644
--- a/src/common/android/android_common.cpp
+++ b/src/common/android/android_common.cpp
@@ -1,7 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
-// SPDX-FileCopyrightText: Copyright yuzu/Citra Emulator Project / Eden Emulator Project
+// SPDX-FileCopyrightText: 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "android_common.h"
diff --git a/src/common/android/android_common.h b/src/common/android/android_common.h
index 05e2c2515b..a16add3bdf 100644
--- a/src/common/android/android_common.h
+++ b/src/common/android/android_common.h
@@ -1,7 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
-// SPDX-FileCopyrightText: Copyright yuzu/Citra Emulator Project / Eden Emulator Project
+// SPDX-FileCopyrightText: 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
diff --git a/src/common/android/id_cache.cpp b/src/common/android/id_cache.cpp
index 649fafca09..2625e55c35 100644
--- a/src/common/android/id_cache.cpp
+++ b/src/common/android/id_cache.cpp
@@ -1,7 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
-// SPDX-FileCopyrightText: Copyright yuzu/Citra Emulator Project / Eden Emulator Project
+// SPDX-FileCopyrightText: 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include <jni.h>
@@ -94,10 +94,10 @@ static jmethodID s_yuzu_input_device_get_supports_vibration;
static jmethodID s_yuzu_input_device_vibrate;
static jmethodID s_yuzu_input_device_get_axes;
static jmethodID s_yuzu_input_device_has_keys;
+
static jmethodID s_add_netplay_message;
static jmethodID s_clear_chat;
-
static constexpr jint JNI_VERSION = JNI_VERSION_1_6;
namespace Common::Android {
@@ -405,7 +405,6 @@ jmethodID ClearChat() {
return s_clear_chat;
}
-
#ifdef __cplusplus
extern "C" {
#endif
@@ -605,7 +604,7 @@ void JNI_OnUnload(JavaVM* vm, void* reserved) {
// UnInitialize applets
SoftwareKeyboard::CleanupJNI(env);
- NetworkShutdown();
+ AndroidMultiplayer::NetworkShutdown();
}
#ifdef __cplusplus
diff --git a/src/common/android/id_cache.h b/src/common/android/id_cache.h
index 0b64bbc866..cbfbf36be8 100644
--- a/src/common/android/id_cache.h
+++ b/src/common/android/id_cache.h
@@ -1,7 +1,7 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
-// SPDX-FileCopyrightText: Copyright yuzu/Citra Emulator Project / Eden Emulator Project
+// SPDX-FileCopyrightText: 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
diff --git a/src/common/android/multiplayer/multiplayer.cpp b/src/common/android/multiplayer/multiplayer.cpp
index 41d46d71b3..fe6408fced 100644
--- a/src/common/android/multiplayer/multiplayer.cpp
+++ b/src/common/android/multiplayer/multiplayer.cpp
@@ -1,8 +1,4 @@
-// Copyright 2024 Mandarine Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-// SPDX-FileCopyrightText: Copyright yuzu/Citra Emulator Project / Eden Emulator Project
+// SPDX-FileCopyrightText: 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "common/android/id_cache.h"
@@ -19,35 +15,38 @@
#include <chrono>
namespace IDCache = Common::Android;
-Network::RoomNetwork* room_network;
-void AddNetPlayMessage(jint type, jstring msg) {
+AndroidMultiplayer::AndroidMultiplayer(Core::System& system_,
+ std::shared_ptr<Core::AnnounceMultiplayerSession> session)
+ : system{system_}, announce_multiplayer_session(session) {}
+
+AndroidMultiplayer::~AndroidMultiplayer() = default;
+
+void AndroidMultiplayer::AddNetPlayMessage(jint type, jstring msg) {
IDCache::GetEnvForThread()->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(),
IDCache::GetAddNetPlayMessage(), type, msg);
}
-void AddNetPlayMessage(int type, const std::string& msg) {
+void AndroidMultiplayer::AddNetPlayMessage(int type, const std::string& msg) {
JNIEnv* env = IDCache::GetEnvForThread();
AddNetPlayMessage(type, Common::Android::ToJString(env, msg));
}
-void ClearChat() {
+void AndroidMultiplayer::ClearChat() {
IDCache::GetEnvForThread()->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(),
IDCache::ClearChat());
}
-
-bool NetworkInit(Network::RoomNetwork* room_network_) {
- room_network = room_network_;
- bool result = room_network->Init();
+bool AndroidMultiplayer::NetworkInit() {
+ bool result = Network::Init();
if (!result) {
return false;
}
- if (auto member = room_network->GetRoomMember().lock()) {
+ if (auto member = Network::GetRoomMember().lock()) {
// register the network structs to use in slots and signals
- member->BindOnStateChanged([](const Network::RoomMember::State& state) {
+ member->BindOnStateChanged([this](const Network::RoomMember::State& state) {
if (state == Network::RoomMember::State::Joined ||
state == Network::RoomMember::State::Moderator) {
NetPlayStatus status;
@@ -65,7 +64,7 @@ bool NetworkInit(Network::RoomNetwork* room_network_) {
AddNetPlayMessage(static_cast<int>(status), msg);
}
});
- member->BindOnError([](const Network::RoomMember::Error& error) {
+ member->BindOnError([this](const Network::RoomMember::Error& error) {
NetPlayStatus status;
std::string msg;
switch (error) {
@@ -108,7 +107,7 @@ bool NetworkInit(Network::RoomNetwork* room_network_) {
}
AddNetPlayMessage(static_cast<int>(status), msg);
});
- member->BindOnStatusMessageReceived([](const Network::StatusMessageEntry& status_message) {
+ member->BindOnStatusMessageReceived([this](const Network::StatusMessageEntry& status_message) {
NetPlayStatus status = NetPlayStatus::NO_ERROR;
std::string msg(status_message.nickname);
switch (status_message.type) {
@@ -130,7 +129,7 @@ bool NetworkInit(Network::RoomNetwork* room_network_) {
}
AddNetPlayMessage(static_cast<int>(status), msg);
});
- member->BindOnChatMessageReceived([](const Network::ChatEntry& chat) {
+ member->BindOnChatMessageReceived([this](const Network::ChatEntry& chat) {
NetPlayStatus status = NetPlayStatus::CHAT_MESSAGE;
std::string msg(chat.nickname);
msg += ": ";
@@ -141,13 +140,11 @@ bool NetworkInit(Network::RoomNetwork* room_network_) {
return true;
}
-NetPlayStatus NetPlayCreateRoom(const std::string& ipaddress, int port,
- const std::string& username, const std::string& password,
+NetPlayStatus AndroidMultiplayer::NetPlayCreateRoom(const std::string& ipaddress, int port,
+ const std::string& username, const std::string &preferredGameName,
+ const u64 &preferredGameId, const std::string& password,
const std::string& room_name, int max_players) {
-
- __android_log_print(ANDROID_LOG_INFO, "NetPlay", "NetPlayCreateRoom called with ipaddress: %s, port: %d, username: %s, room_name: %s, max_players: %d", ipaddress.c_str(), port, username.c_str(), room_name.c_str(), max_players);
-
- auto member = room_network->GetRoomMember().lock();
+ auto member = Network::GetRoomMember().lock();
if (!member) {
return NetPlayStatus::NETWORK_ERROR;
}
@@ -156,7 +153,7 @@ NetPlayStatus NetPlayCreateRoom(const std::string& ipaddress, int port,
return NetPlayStatus::ALREADY_IN_ROOM;
}
- auto room = room_network->GetRoom().lock();
+ auto room = Network::GetRoom().lock();
if (!room) {
return NetPlayStatus::NETWORK_ERROR;
}
@@ -167,14 +164,14 @@ NetPlayStatus NetPlayCreateRoom(const std::string& ipaddress, int port,
// Placeholder game info
const AnnounceMultiplayerRoom::GameInfo game{
- .name = "Default Game",
- .id = 0, // Default program ID
+ .name = preferredGameName,
+ .id = preferredGameId,
};
port = (port == 0) ? Network::DefaultRoomPort : static_cast<u16>(port);
if (!room->Create(room_name, "", ipaddress, static_cast<u16>(port), password,
- static_cast<u32>(std::min(max_players, 16)), username, game, nullptr, {})) {
+ static_cast<u32>(std::min(max_players, 16)), username, game, std::make_unique<Network::VerifyUser::NullBackend>(), {})) {
return NetPlayStatus::CREATE_ROOM_ERROR;
}
@@ -197,9 +194,9 @@ NetPlayStatus NetPlayCreateRoom(const std::string& ipaddress, int port,
return NetPlayStatus::CREATE_ROOM_ERROR;
}
-NetPlayStatus NetPlayJoinRoom(const std::string& ipaddress, int port,
+NetPlayStatus AndroidMultiplayer::NetPlayJoinRoom(const std::string& ipaddress, int port,
const std::string& username, const std::string& password) {
- auto member = room_network->GetRoomMember().lock();
+ auto member = Network::GetRoomMember().lock();
if (!member) {
return NetPlayStatus::NETWORK_ERROR;
}
@@ -229,8 +226,8 @@ NetPlayStatus NetPlayJoinRoom(const std::string& ipaddress, int port,
return NetPlayStatus::WRONG_PASSWORD;
}
-void NetPlaySendMessage(const std::string& msg) {
- if (auto room = room_network->GetRoomMember().lock()) {
+void AndroidMultiplayer::NetPlaySendMessage(const std::string& msg) {
+ if (auto room = Network::GetRoomMember().lock()) {
if (room->GetState() != Network::RoomMember::State::Joined &&
room->GetState() != Network::RoomMember::State::Moderator) {
@@ -240,8 +237,8 @@ void NetPlaySendMessage(const std::string& msg) {
}
}
-void NetPlayKickUser(const std::string& username) {
- if (auto room = room_network->GetRoomMember().lock()) {
+void AndroidMultiplayer::NetPlayKickUser(const std::string& username) {
+ if (auto room = Network::GetRoomMember().lock()) {
auto members = room->GetMemberInformation();
auto it = std::find_if(members.begin(), members.end(),
[&username](const Network::RoomMember::MemberInformation& member) {
@@ -253,8 +250,8 @@ void NetPlayKickUser(const std::string& username) {
}
}
-void NetPlayBanUser(const std::string& username) {
- if (auto room = room_network->GetRoomMember().lock()) {
+void AndroidMultiplayer::NetPlayBanUser(const std::string& username) {
+ if (auto room = Network::GetRoomMember().lock()) {
auto members = room->GetMemberInformation();
auto it = std::find_if(members.begin(), members.end(),
[&username](const Network::RoomMember::MemberInformation& member) {
@@ -266,15 +263,15 @@ void NetPlayBanUser(const std::string& username) {
}
}
-void NetPlayUnbanUser(const std::string& username) {
- if (auto room = room_network->GetRoomMember().lock()) {
+void AndroidMultiplayer::NetPlayUnbanUser(const std::string& username) {
+ if (auto room = Network::GetRoomMember().lock()) {
room->SendModerationRequest(Network::RoomMessageTypes::IdModUnban, username);
}
}
-std::vector<std::string> NetPlayRoomInfo() {
+std::vector<std::string> AndroidMultiplayer::NetPlayRoomInfo() {
std::vector<std::string> info_list;
- if (auto room = room_network->GetRoomMember().lock()) {
+ if (auto room = Network::GetRoomMember().lock()) {
auto members = room->GetMemberInformation();
if (!members.empty()) {
// name and max players
@@ -289,8 +286,8 @@ std::vector<std::string> NetPlayRoomInfo() {
return info_list;
}
-bool NetPlayIsJoined() {
- auto member = room_network->GetRoomMember().lock();
+bool AndroidMultiplayer::NetPlayIsJoined() {
+ auto member = Network::GetRoomMember().lock();
if (!member) {
return false;
}
@@ -299,17 +296,17 @@ bool NetPlayIsJoined() {
member->GetState() == Network::RoomMember::State::Moderator);
}
-bool NetPlayIsHostedRoom() {
- if (auto room = room_network->GetRoom().lock()) {
+bool AndroidMultiplayer::NetPlayIsHostedRoom() {
+ if (auto room = Network::GetRoom().lock()) {
return room->GetState() == Network::Room::State::Open;
}
return false;
}
-void NetPlayLeaveRoom() {
- if (auto room = room_network->GetRoom().lock()) {
+void AndroidMultiplayer::NetPlayLeaveRoom() {
+ if (auto room = Network::GetRoom().lock()) {
// if you are in a room, leave it
- if (auto member = room_network->GetRoomMember().lock()) {
+ if (auto member = Network::GetRoomMember().lock()) {
member->Leave();
}
@@ -322,21 +319,52 @@ void NetPlayLeaveRoom() {
}
}
-void NetworkShutdown() {
- room_network->Shutdown();
+void AndroidMultiplayer::NetworkShutdown() {
+ Network::Shutdown();
}
-bool NetPlayIsModerator() {
- auto member = room_network->GetRoomMember().lock();
+bool AndroidMultiplayer::NetPlayIsModerator() {
+ auto member = Network::GetRoomMember().lock();
if (!member) {
return false;
}
return member->GetState() == Network::RoomMember::State::Moderator;
}
-std::vector<std::string> NetPlayGetBanList() {
+std::vector<std::string> AndroidMultiplayer::NetPlayGetPublicRooms() {
+ std::vector<std::string> room_list;
+
+ if (auto session = announce_multiplayer_session.lock()) {
+ auto rooms = session->GetRoomList();
+ for (const auto &room: rooms) {
+ room_list.push_back(room.information.name + "|" +
+ (room.has_password ? "1" : "0") + "|" +
+ std::to_string(room.information.member_slots) + "|" +
+ room.ip + "|" +
+ std::to_string(room.information.port) + "|" +
+ room.information.description + "|" +
+ room.information.host_username + "|" +
+ std::to_string(room.information.preferred_game.id) + "|" +
+ room.information.preferred_game.name + "|" +
+ room.information.preferred_game.version);
+
+ for (const auto &member: room.members) {
+ room_list.push_back("MEMBER|" + room.information.name + "|" +
+ member.username + "|" +
+ member.nickname + "|" +
+ std::to_string(member.game.id) + "|" +
+ member.game.name);
+ }
+ }
+
+ }
+ return room_list;
+
+}
+
+std::vector<std::string> AndroidMultiplayer::NetPlayGetBanList() {
std::vector<std::string> ban_list;
- if (auto room = room_network->GetRoom().lock()) {
+ if (auto room = Network::GetRoom().lock()) {
auto [username_bans, ip_bans] = room->GetBanList();
// Add username bans
diff --git a/src/common/android/multiplayer/multiplayer.h b/src/common/android/multiplayer/multiplayer.h
index 4f32c514e0..591d3be8fd 100644
--- a/src/common/android/multiplayer/multiplayer.h
+++ b/src/common/android/multiplayer/multiplayer.h
@@ -1,8 +1,4 @@
-// Copyright 2024 Mandarine Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-// SPDX-FileCopyrightText: Copyright yuzu/Citra Emulator Project / Eden Emulator Project
+// SPDX-FileCopyrightText: 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
@@ -12,6 +8,12 @@
#include <common/common_types.h>
#include <network/network.h>
+#include <network/announce_multiplayer_session.h>
+
+namespace Core {
+ class System;
+ class AnnounceMultiplayerSession;
+}
enum class NetPlayStatus : s32 {
NO_ERROR,
@@ -48,21 +50,53 @@ enum class NetPlayStatus : s32 {
CHAT_MESSAGE,
};
-bool NetworkInit(Network::RoomNetwork* room_network);
-NetPlayStatus NetPlayCreateRoom(const std::string& ipaddress, int port,
- const std::string& username, const std::string& password,
- const std::string& room_name, int max_players);
-NetPlayStatus NetPlayJoinRoom(const std::string& ipaddress, int port,
- const std::string& username, const std::string& password);
-std::vector<std::string> NetPlayRoomInfo();
-bool NetPlayIsJoined();
-bool NetPlayIsHostedRoom();
-bool NetPlayIsModerator();
-void NetPlaySendMessage(const std::string& msg);
-void NetPlayKickUser(const std::string& username);
-void NetPlayBanUser(const std::string& username);
-void NetPlayLeaveRoom();
-std::string NetPlayGetConsoleId();
-void NetworkShutdown();
-std::vector<std::string> NetPlayGetBanList();
-void NetPlayUnbanUser(const std::string& username);
+class AndroidMultiplayer {
+public:
+ explicit AndroidMultiplayer(Core::System& system,
+ std::shared_ptr<Core::AnnounceMultiplayerSession> session);
+ ~AndroidMultiplayer();
+
+ bool NetworkInit();
+
+ void AddNetPlayMessage(int status, const std::string& msg);
+ void AddNetPlayMessage(jint type, jstring msg);
+
+ void ClearChat();
+
+ NetPlayStatus NetPlayCreateRoom(const std::string &ipaddress, int port,
+ const std::string &username, const std::string &preferredGameName,
+ const u64 &preferredGameId, const std::string &password,
+ const std::string &room_name, int max_players);
+
+ NetPlayStatus NetPlayJoinRoom(const std::string &ipaddress, int port,
+ const std::string &username, const std::string &password);
+
+ std::vector<std::string> NetPlayRoomInfo();
+
+ bool NetPlayIsJoined();
+
+ bool NetPlayIsHostedRoom();
+
+ bool NetPlayIsModerator();
+
+ void NetPlaySendMessage(const std::string &msg);
+
+ void NetPlayKickUser(const std::string &username);
+
+ void NetPlayBanUser(const std::string &username);
+
+ void NetPlayLeaveRoom();
+
+ static void NetworkShutdown();
+
+ std::vector<std::string> NetPlayGetBanList();
+
+ void NetPlayUnbanUser(const std::string &username);
+
+ std::vector<std::string> NetPlayGetPublicRooms();
+
+private:
+ Core::System& system;
+ static std::unique_ptr<Network::VerifyUser::Backend> CreateVerifyBackend(bool use_validation) ;
+ std::weak_ptr<Core::AnnounceMultiplayerSession> announce_multiplayer_session;
+};
diff --git a/src/common/announce_multiplayer_room.h b/src/common/announce_multiplayer_room.h
index 6f7ccfa7f0..419890481a 100644
--- a/src/common/announce_multiplayer_room.h
+++ b/src/common/announce_multiplayer_room.h
@@ -1,7 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
-// SPDX-FileCopyrightText: Copyright yuzu/Citra Emulator Project / Eden Emulator Project
+// SPDX-FileCopyrightText: 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 61b8182c08..0058c09b6e 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: Copyright yuzu/Citra Emulator Project / Eden Emulator Project
+// SPDX-FileCopyrightText: 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include <array>
@@ -111,7 +111,7 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
struct System::Impl {
explicit Impl(System& system)
- : kernel{system}, fs_controller{system}, hid_core{}, room_network{}, cpu_manager{system},
+ : kernel{system}, fs_controller{system}, hid_core{}, cpu_manager{system},
reporter{system}, applet_manager{system}, frontend_applets{system}, profile_manager{} {}
u64 program_id;
@@ -421,7 +421,7 @@ struct System::Impl {
}
LoadOverrides(program_id);
- if (auto room_member = room_network.GetRoomMember().lock()) {
+ if (auto room_member = Network::GetRoomMember().lock()) {
Network::GameInfo game_info;
game_info.name = name;
game_info.id = params.program_id;
@@ -466,7 +466,7 @@ struct System::Impl {
stop_event = {};
Network::RestartSocketOperations();
- if (auto room_member = room_network.GetRoomMember().lock()) {
+ if (auto room_member = Network::GetRoomMember().lock()) {
Network::GameInfo game_info{};
room_member->SendGameInfo(game_info);
}
@@ -520,7 +520,6 @@ struct System::Impl {
std::unique_ptr<Core::DeviceMemory> device_memory;
std::unique_ptr<AudioCore::AudioCore> audio_core;
Core::HID::HIDCore hid_core;
- Network::RoomNetwork room_network;
CpuManager cpu_manager;
std::atomic_bool is_powered_on{};
@@ -979,14 +978,6 @@ const Core::Debugger& System::GetDebugger() const {
return *impl->debugger;
}
-Network::RoomNetwork& System::GetRoomNetwork() {
- return impl->room_network;
-}
-
-const Network::RoomNetwork& System::GetRoomNetwork() const {
- return impl->room_network;
-}
-
Tools::RenderdocAPI& System::GetRenderdocAPI() {
return *impl->renderdoc_api;
}
diff --git a/src/core/core.h b/src/core/core.h
index 0cc1108b80..99b1c5641f 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -1,6 +1,9 @@
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-FileCopyrightText: 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
#pragma once
#include <cstddef>
@@ -104,10 +107,6 @@ namespace Core::HID {
class HIDCore;
}
-namespace Network {
-class RoomNetwork;
-}
-
namespace Tools {
class RenderdocAPI;
}
@@ -380,12 +379,6 @@ public:
[[nodiscard]] Core::Debugger& GetDebugger();
[[nodiscard]] const Core::Debugger& GetDebugger() const;
- /// Gets a mutable reference to the Room Network.
- [[nodiscard]] Network::RoomNetwork& GetRoomNetwork();
-
- /// Gets an immutable reference to the Room Network.
- [[nodiscard]] const Network::RoomNetwork& GetRoomNetwork() const;
-
[[nodiscard]] Tools::RenderdocAPI& GetRenderdocAPI();
void SetExitLocked(bool locked);
diff --git a/src/core/hle/service/ldn/lan_discovery.cpp b/src/core/hle/service/ldn/lan_discovery.cpp
index b9db19618a..7d677e6664 100644
--- a/src/core/hle/service/ldn/lan_discovery.cpp
+++ b/src/core/hle/service/ldn/lan_discovery.cpp
@@ -1,6 +1,9 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
#include "core/hle/service/ldn/lan_discovery.h"
#include "core/internal_network/network.h"
#include "core/internal_network/network_interface.h"
@@ -33,9 +36,8 @@ void LanStation::OverrideInfo() {
node_info->is_connected = connected ? 1 : 0;
}
-LANDiscovery::LANDiscovery(Network::RoomNetwork& room_network_)
- : stations({{{1, this}, {2, this}, {3, this}, {4, this}, {5, this}, {6, this}, {7, this}}}),
- room_network{room_network_} {}
+LANDiscovery::LANDiscovery()
+ : stations({{{1, this}, {2, this}, {3, this}, {4, this}, {5, this}, {6, this}, {7, this}}}){}
LANDiscovery::~LANDiscovery() {
if (inited) {
@@ -410,7 +412,7 @@ void LANDiscovery::OnNetworkInfoChanged() {
Network::IPv4Address LANDiscovery::GetLocalIp() const {
Network::IPv4Address local_ip{0xFF, 0xFF, 0xFF, 0xFF};
- if (auto room_member = room_network.GetRoomMember().lock()) {
+ if (auto room_member = Network::GetRoomMember().lock()) {
if (room_member->IsConnected()) {
local_ip = room_member->GetFakeIpAddress();
}
@@ -468,7 +470,7 @@ void LANDiscovery::SendBroadcast(Network::LDNPacketType type) {
}
void LANDiscovery::SendPacket(const Network::LDNPacket& packet) {
- if (auto room_member = room_network.GetRoomMember().lock()) {
+ if (auto room_member = Network::GetRoomMember().lock()) {
if (room_member->IsConnected()) {
room_member->SendLdnPacket(packet);
}
diff --git a/src/core/hle/service/ldn/lan_discovery.h b/src/core/hle/service/ldn/lan_discovery.h
index 8f7a8dfc45..0d15583172 100644
--- a/src/core/hle/service/ldn/lan_discovery.h
+++ b/src/core/hle/service/ldn/lan_discovery.h
@@ -1,6 +1,9 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
#pragma once
#include <array>
@@ -47,7 +50,7 @@ class LANDiscovery {
public:
using LanEventFunc = std::function<void()>;
- LANDiscovery(Network::RoomNetwork& room_network_);
+ LANDiscovery();
~LANDiscovery();
State GetState() const;
@@ -127,7 +130,5 @@ protected:
std::optional<Ipv4Address> host_ip;
LanEventFunc lan_event;
-
- Network::RoomNetwork& room_network;
};
} // namespace Service::LDN
diff --git a/src/core/hle/service/ldn/user_local_communication_service.cpp b/src/core/hle/service/ldn/user_local_communication_service.cpp
index f28368962f..69170a879d 100644
--- a/src/core/hle/service/ldn/user_local_communication_service.cpp
+++ b/src/core/hle/service/ldn/user_local_communication_service.cpp
@@ -1,6 +1,9 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
#include <memory>
#include "core/core.h"
@@ -22,7 +25,7 @@ namespace Service::LDN {
IUserLocalCommunicationService::IUserLocalCommunicationService(Core::System& system_)
: ServiceFramework{system_, "IUserLocalCommunicationService"},
service_context{system, "IUserLocalCommunicationService"},
- room_network{system_.GetRoomNetwork()}, lan_discovery{room_network} {
+ lan_discovery{} {
// clang-format off
static const FunctionInfo functions[] = {
{0, C<&IUserLocalCommunicationService::GetState>, "GetState"},
@@ -65,7 +68,7 @@ IUserLocalCommunicationService::IUserLocalCommunicationService(Core::System& sys
IUserLocalCommunicationService::~IUserLocalCommunicationService() {
if (is_initialized) {
- if (auto room_member = room_network.GetRoomMember().lock()) {
+ if (auto room_member = Network::GetRoomMember().lock()) {
room_member->Unbind(ldn_packet_received);
}
}
@@ -103,7 +106,7 @@ Result IUserLocalCommunicationService::GetIpv4Address(Out<Ipv4Address> out_curre
*out_subnet_mask = {Network::TranslateIPv4(network_interface->subnet_mask)};
// When we're connected to a room, spoof the hosts IP address
- if (auto room_member = room_network.GetRoomMember().lock()) {
+ if (auto room_member = Network::GetRoomMember().lock()) {
if (room_member->IsConnected()) {
*out_current_address = room_member->GetFakeIpAddress();
}
@@ -280,7 +283,7 @@ Result IUserLocalCommunicationService::Initialize(ClientProcessId aruid) {
const auto network_interface = Network::GetSelectedNetworkInterface();
R_UNLESS(network_interface, ResultAirplaneModeEnabled);
- if (auto room_member = room_network.GetRoomMember().lock()) {
+ if (auto room_member = Network::GetRoomMember().lock()) {
ldn_packet_received = room_member->BindOnLdnPacketReceived(
[this](const Network::LDNPacket& packet) { OnLDNPacketReceived(packet); });
} else {
@@ -295,7 +298,7 @@ Result IUserLocalCommunicationService::Initialize(ClientProcessId aruid) {
Result IUserLocalCommunicationService::Finalize() {
LOG_INFO(Service_LDN, "called");
- if (auto room_member = room_network.GetRoomMember().lock()) {
+ if (auto room_member = Network::GetRoomMember().lock()) {
room_member->Unbind(ldn_packet_received);
}
diff --git a/src/core/hle/service/ldn/user_local_communication_service.h b/src/core/hle/service/ldn/user_local_communication_service.h
index 6698d10d22..55320fbc91 100644
--- a/src/core/hle/service/ldn/user_local_communication_service.h
+++ b/src/core/hle/service/ldn/user_local_communication_service.h
@@ -1,6 +1,9 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
#pragma once
#include "core/hle/service/cmif_types.h"
@@ -13,10 +16,6 @@ namespace Core {
class System;
}
-namespace Network {
-class RoomNetwork;
-}
-
namespace Service::LDN {
class IUserLocalCommunicationService final
@@ -91,7 +90,6 @@ private:
KernelHelpers::ServiceContext service_context;
Kernel::KEvent* state_change_event;
- Network::RoomNetwork& room_network;
LANDiscovery lan_discovery;
// Callback identifier for the OnLDNPacketReceived event.
diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp
index d758e9aa3d..bb6e719cb0 100644
--- a/src/core/hle/service/nifm/nifm.cpp
+++ b/src/core/hle/service/nifm/nifm.cpp
@@ -1,6 +1,9 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
#include "core/core.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/service/ipc_helpers.h"
@@ -406,7 +409,7 @@ void IGeneralService::GetCurrentNetworkProfile(HLERequestContext& ctx) {
}();
// When we're connected to a room, spoof the hosts IP address
- if (auto room_member = network.GetRoomMember().lock()) {
+ if (auto room_member = Network::GetRoomMember().lock()) {
if (room_member->IsConnected()) {
network_profile_data.ip_setting_data.ip_address_setting.ip_address =
room_member->GetFakeIpAddress();
@@ -454,7 +457,7 @@ void IGeneralService::GetCurrentIpAddress(HLERequestContext& ctx) {
}
// When we're connected to a room, spoof the hosts IP address
- if (auto room_member = network.GetRoomMember().lock()) {
+ if (auto room_member = Network::GetRoomMember().lock()) {
if (room_member->IsConnected()) {
ipv4 = room_member->GetFakeIpAddress();
}
@@ -513,7 +516,7 @@ void IGeneralService::GetCurrentIpConfigInfo(HLERequestContext& ctx) {
}();
// When we're connected to a room, spoof the hosts IP address
- if (auto room_member = network.GetRoomMember().lock()) {
+ if (auto room_member = Network::GetRoomMember().lock()) {
if (room_member->IsConnected()) {
ip_config_info.ip_address_setting.ip_address = room_member->GetFakeIpAddress();
}
@@ -643,7 +646,7 @@ void IGeneralService::GetCurrentAccessPoint(HLERequestContext& ctx) {
}
IGeneralService::IGeneralService(Core::System& system_)
- : ServiceFramework{system_, "IGeneralService"}, network{system_.GetRoomNetwork()} {
+ : ServiceFramework{system_, "IGeneralService"} {
// clang-format off
static const FunctionInfo functions[] = {
{1, &IGeneralService::GetClientId, "GetClientId"},
diff --git a/src/core/hle/service/nifm/nifm.h b/src/core/hle/service/nifm/nifm.h
index 4682a4a273..7071fa90d7 100644
--- a/src/core/hle/service/nifm/nifm.h
+++ b/src/core/hle/service/nifm/nifm.h
@@ -1,6 +1,9 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
#pragma once
#include "core/hle/service/service.h"
@@ -9,10 +12,6 @@ namespace Core {
class System;
}
-namespace Network {
-class RoomNetwork;
-}
-
namespace Service::NIFM {
void LoopProcess(Core::System& system);
@@ -42,8 +41,6 @@ private:
void ConfirmSystemAvailability(HLERequestContext& ctx);
void SetBackgroundRequestEnabled(HLERequestContext& ctx);
void GetCurrentAccessPoint(HLERequestContext& ctx);
-
- Network::RoomNetwork& network;
};
} // namespace Service::NIFM
diff --git a/src/core/hle/service/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp
index ef03203349..4be70e0559 100644
--- a/src/core/hle/service/sockets/bsd.cpp
+++ b/src/core/hle/service/sockets/bsd.cpp
@@ -1,6 +1,9 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
#include <array>
#include <memory>
#include <utility>
@@ -508,9 +511,9 @@ std::pair<s32, Errno> BSD::SocketImpl(Domain domain, Type type, Protocol protoco
LOG_INFO(Service, "New socket fd={}", fd);
- auto room_member = room_network.GetRoomMember().lock();
+ auto room_member = Network::GetRoomMember().lock();
if (room_member && room_member->IsConnected()) {
- descriptor.socket = std::make_shared<Network::ProxySocket>(room_network);
+ descriptor.socket = std::make_shared<Network::ProxySocket>();
} else {
descriptor.socket = std::make_shared<Network::Socket>();
}
@@ -970,7 +973,7 @@ void BSD::OnProxyPacketReceived(const Network::ProxyPacket& packet) {
}
BSD::BSD(Core::System& system_, const char* name)
- : ServiceFramework{system_, name}, room_network{system_.GetRoomNetwork()} {
+ : ServiceFramework{system_, name} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &BSD::RegisterClient, "RegisterClient"},
@@ -1012,7 +1015,7 @@ BSD::BSD(Core::System& system_, const char* name)
RegisterHandlers(functions);
- if (auto room_member = room_network.GetRoomMember().lock()) {
+ if (auto room_member = Network::GetRoomMember().lock()) {
proxy_packet_received = room_member->BindOnProxyPacketReceived(
[this](const Network::ProxyPacket& packet) { OnProxyPacketReceived(packet); });
} else {
@@ -1021,7 +1024,7 @@ BSD::BSD(Core::System& system_, const char* name)
}
BSD::~BSD() {
- if (auto room_member = room_network.GetRoomMember().lock()) {
+ if (auto room_member = Network::GetRoomMember().lock()) {
room_member->Unbind(proxy_packet_received);
}
}
diff --git a/src/core/hle/service/sockets/bsd.h b/src/core/hle/service/sockets/bsd.h
index 4f69d382c9..ed6b576a65 100644
--- a/src/core/hle/service/sockets/bsd.h
+++ b/src/core/hle/service/sockets/bsd.h
@@ -1,6 +1,9 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
#pragma once
#include <memory>
@@ -179,8 +182,6 @@ private:
std::array<std::optional<FileDescriptor>, MAX_FD> file_descriptors;
- Network::RoomNetwork& room_network;
-
/// Callback to parse and handle a received wifi packet.
void OnProxyPacketReceived(const Network::ProxyPacket& packet);
diff --git a/src/core/internal_network/network_interface.cpp b/src/core/internal_network/network_interface.cpp
index 3d99bdf5ce..0f5ef0741f 100644
--- a/src/core/internal_network/network_interface.cpp
+++ b/src/core/internal_network/network_interface.cpp
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: Copyright yuzu/Citra Emulator Project / Eden Emulator Project
+// SPDX-FileCopyrightText: 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include <algorithm>
diff --git a/src/core/internal_network/socket_proxy.cpp b/src/core/internal_network/socket_proxy.cpp
index ce0dee9705..eba1e28c5e 100644
--- a/src/core/internal_network/socket_proxy.cpp
+++ b/src/core/internal_network/socket_proxy.cpp
@@ -1,6 +1,9 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
#include <chrono>
#include <thread>
@@ -18,7 +21,7 @@
namespace Network {
-ProxySocket::ProxySocket(RoomNetwork& room_network_) noexcept : room_network{room_network_} {}
+ProxySocket::ProxySocket() noexcept {}
ProxySocket::~ProxySocket() {
if (fd == INVALID_SOCKET) {
@@ -187,7 +190,7 @@ std::pair<s32, Errno> ProxySocket::Send(std::span<const u8> message, int flags)
}
void ProxySocket::SendPacket(ProxyPacket& packet) {
- if (auto room_member = room_network.GetRoomMember().lock()) {
+ if (auto room_member = Network::GetRoomMember().lock()) {
if (room_member->IsConnected()) {
packet.data = Common::Compression::CompressDataZSTDDefault(packet.data.data(),
packet.data.size());
@@ -205,7 +208,7 @@ std::pair<s32, Errno> ProxySocket::SendTo(u32 flags, std::span<const u8> message
return {static_cast<s32>(message.size()), Errno::SUCCESS};
}
- if (auto room_member = room_network.GetRoomMember().lock()) {
+ if (auto room_member = Network::GetRoomMember().lock()) {
if (!room_member->IsConnected()) {
return {static_cast<s32>(message.size()), Errno::SUCCESS};
}
@@ -222,7 +225,7 @@ std::pair<s32, Errno> ProxySocket::SendTo(u32 flags, std::span<const u8> message
// If the ip is all zeroes (INADDR_ANY) or if it matches the hosts ip address,
// replace it with a "fake" routing address
if (std::all_of(ip.begin(), ip.end(), [](u8 i) { return i == 0; }) || (ipv4 && ipv4 == ip)) {
- if (auto room_member = room_network.GetRoomMember().lock()) {
+ if (auto room_member = Network::GetRoomMember().lock()) {
packet.local_endpoint.ip = room_member->GetFakeIpAddress();
}
}
diff --git a/src/core/internal_network/socket_proxy.h b/src/core/internal_network/socket_proxy.h
index 70500cf4a2..93cf0757af 100644
--- a/src/core/internal_network/socket_proxy.h
+++ b/src/core/internal_network/socket_proxy.h
@@ -1,6 +1,9 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
#pragma once
#include <mutex>
@@ -14,11 +17,9 @@
namespace Network {
-class RoomNetwork;
-
class ProxySocket : public SocketBase {
public:
- explicit ProxySocket(RoomNetwork& room_network_) noexcept;
+ explicit ProxySocket() noexcept;
~ProxySocket() override;
void HandleProxyPacket(const ProxyPacket& packet) override;
@@ -92,8 +93,6 @@ private:
Protocol protocol;
std::mutex packets_mutex;
-
- RoomNetwork& room_network;
};
} // namespace Network
diff --git a/src/dedicated_room/yuzu_room.cpp b/src/dedicated_room/yuzu_room.cpp
index f02407780f..afba0ad4c8 100644
--- a/src/dedicated_room/yuzu_room.cpp
+++ b/src/dedicated_room/yuzu_room.cpp
@@ -4,7 +4,7 @@
// SPDX-FileCopyrightText: Copyright yuzu/Citra Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
-// SPDX-FileCopyrightText: 2025 eden Emulator Project
+// SPDX-FileCopyrightText: 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include <chrono>
@@ -55,22 +55,22 @@ static void PrintHelp(const char* argv0) {
LOG_INFO(Network,
"Usage: {}"
" [options] <filename>\n"
- "--room-name The name of the room\n"
- "--room-description The room description\n"
- "--bind-address The bind address for the room\n"
- "--port The port used for the room\n"
- "--max_members The maximum number of players for this room\n"
- "--password The password for the room\n"
- "--preferred-game The preferred game for this room\n"
- "--preferred-game-id The preferred game-id for this room\n"
- "--username The username used for announce\n"
- "--token The token used for announce\n"
- "--web-api-url yuzu Web API url\n"
- "--ban-list-file The file for storing the room ban list\n"
- "--log-file The file for storing the room log\n"
- "--enable-yuzu-mods Allow yuzu Community Moderators to moderate on your room\n"
- "-h, --help Display this help and exit\n"
- "-v, --version Output version information and exit\n",
+ "-n, --room-name The name of the room\n"
+ "-d, --room-description The room description\n"
+ "-s, --bind-address The bind address for the room\n"
+ "-p, --port The port used for the room\n"
+ "-m, --max-members The maximum number of players for this room\n"
+ "-w, --password The password for the room\n"
+ "-g, --preferred-game The preferred game for this room\n"
+ "-i, --preferred-game-id The preferred game-id for this room\n"
+ "-u, --username The username used for announce\n"
+ "-t, --token The token used for announce\n"
+ "-a, --web-api-url yuzu Web API url\n"
+ "-b, --ban-list-file The file for storing the room ban list\n"
+ "-l, --log-file The file for storing the room log\n"
+ "-e, --enable-mods Allow Community Moderators to moderate on your room\n"
+ "-h, --help Display this help and exit\n"
+ "-v, --version Output version information and exit\n",
argv0);
}
@@ -209,19 +209,18 @@ void LaunchRoom(int argc, char** argv, bool called_by_option)
std::string token;
std::string web_api_url;
std::string ban_list_file;
- std::string log_file = "yuzu-room.log";
+ std::string log_file = "eden-room.log";
std::string bind_address;
u64 preferred_game_id = 0;
u32 port = Network::DefaultRoomPort;
u32 max_members = 16;
- // TODO(alekpop): Implement this into main executable, for --room and a few others.
static struct option long_options[] = {
{"room-name", required_argument, 0, 'n'},
{"room-description", required_argument, 0, 'd'},
{"bind-address", required_argument, 0, 's'},
{"port", required_argument, 0, 'p'},
- {"max_members", required_argument, 0, 'm'},
+ {"max-members", required_argument, 0, 'm'},
{"password", required_argument, 0, 'w'},
{"preferred-game", required_argument, 0, 'g'},
{"preferred-game-id", required_argument, 0, 'i'},
@@ -230,7 +229,7 @@ void LaunchRoom(int argc, char** argv, bool called_by_option)
{"web-api-url", required_argument, 0, 'a'},
{"ban-list-file", required_argument, 0, 'b'},
{"log-file", required_argument, 0, 'l'},
- {"enable-yuzu-mods", no_argument, 0, 'e'},
+ {"enable-mods", no_argument, 0, 'e'},
{"help", no_argument, 0, 'h'},
{"version", no_argument, 0, 'v'},
// Entry option
@@ -243,7 +242,9 @@ void LaunchRoom(int argc, char** argv, bool called_by_option)
while (optind < argc) {
int arg = getopt_long(argc, argv, "n:d:s:p:m:w:g:u:t:a:i:l:hv", long_options, &option_index);
if (arg != -1) {
- switch (static_cast<char>(arg)) {
+ char carg = static_cast<char>(arg);
+
+ switch (carg) {
case 'n':
room_name.assign(optarg);
break;
@@ -289,6 +290,8 @@ void LaunchRoom(int argc, char** argv, bool called_by_option)
case 'v':
PrintVersion();
std::exit(0);
+ default:
+ break;
}
}
}
@@ -373,9 +376,8 @@ void LaunchRoom(int argc, char** argv, bool called_by_option)
verify_backend = std::make_unique<Network::VerifyUser::NullBackend>();
}
- Network::RoomNetwork network{};
- network.Init();
- if (auto room = network.GetRoom().lock()) {
+ Network::Init();
+ if (auto room = Network::GetRoom().lock()) {
AnnounceMultiplayerRoom::GameInfo preferred_game_info{.name = preferred_game,
.id = preferred_game_id};
if (!room->Create(room_name, room_description, bind_address, static_cast<u16>(port),
@@ -385,7 +387,7 @@ void LaunchRoom(int argc, char** argv, bool called_by_option)
std::exit(-1);
}
LOG_INFO(Network, "Room is open. Close with Q+Enter...");
- auto announce_session = std::make_unique<Core::AnnounceMultiplayerSession>(network);
+ auto announce_session = std::make_unique<Core::AnnounceMultiplayerSession>();
if (announce) {
announce_session->Start();
}
@@ -407,7 +409,7 @@ void LaunchRoom(int argc, char** argv, bool called_by_option)
}
room->Destroy();
}
- network.Shutdown();
+ Network::Shutdown();
detached_tasks.WaitForAllTasks();
std::exit(0);
}
diff --git a/src/network/announce_multiplayer_session.cpp b/src/network/announce_multiplayer_session.cpp
index 6737ce85ad..35082cbd31 100644
--- a/src/network/announce_multiplayer_session.cpp
+++ b/src/network/announce_multiplayer_session.cpp
@@ -1,6 +1,9 @@
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
#include <chrono>
#include <future>
#include <vector>
@@ -19,8 +22,7 @@ namespace Core {
// Time between room is announced to web_service
static constexpr std::chrono::seconds announce_time_interval(15);
-AnnounceMultiplayerSession::AnnounceMultiplayerSession(Network::RoomNetwork& room_network_)
- : room_network{room_network_} {
+AnnounceMultiplayerSession::AnnounceMultiplayerSession() {
#ifdef ENABLE_WEB_SERVICE
backend = std::make_unique<WebService::RoomJson>(Settings::values.web_api_url.GetValue(),
Settings::values.yuzu_username.GetValue(),
@@ -31,7 +33,7 @@ AnnounceMultiplayerSession::AnnounceMultiplayerSession(Network::RoomNetwork& roo
}
WebService::WebResult AnnounceMultiplayerSession::Register() {
- auto room = room_network.GetRoom().lock();
+ auto room = Network::GetRoom().lock();
if (!room) {
return WebService::WebResult{WebService::WebResult::Code::LibError,
"Network is not initialized", ""};
@@ -120,7 +122,7 @@ void AnnounceMultiplayerSession::AnnounceMultiplayerLoop() {
std::future<WebService::WebResult> future;
while (!shutdown_event.WaitUntil(update_time)) {
update_time += announce_time_interval;
- auto room = room_network.GetRoom().lock();
+ auto room = Network::GetRoom().lock();
if (!room) {
break;
}
diff --git a/src/network/announce_multiplayer_session.h b/src/network/announce_multiplayer_session.h
index db790f7d2d..9d9673d97a 100644
--- a/src/network/announce_multiplayer_session.h
+++ b/src/network/announce_multiplayer_session.h
@@ -1,6 +1,9 @@
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
#pragma once
#include <atomic>
@@ -15,7 +18,6 @@
namespace Network {
class Room;
-class RoomNetwork;
} // namespace Network
namespace Core {
@@ -28,7 +30,8 @@ namespace Core {
class AnnounceMultiplayerSession {
public:
using CallbackHandle = std::shared_ptr<std::function<void(const WebService::WebResult&)>>;
- AnnounceMultiplayerSession(Network::RoomNetwork& room_network_);
+
+ AnnounceMultiplayerSession();
~AnnounceMultiplayerSession();
/**
@@ -91,8 +94,6 @@ private:
std::unique_ptr<AnnounceMultiplayerRoom::Backend> backend;
std::atomic_bool registered = false; ///< Whether the room has been registered
-
- Network::RoomNetwork& room_network;
};
} // namespace Core
diff --git a/src/network/network.cpp b/src/network/network.cpp
index 6652a186bb..0314b51d2c 100644
--- a/src/network/network.cpp
+++ b/src/network/network.cpp
@@ -1,6 +1,9 @@
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
#include "common/assert.h"
#include "common/logging/log.h"
#include "enet/enet.h"
@@ -8,40 +11,38 @@
namespace Network {
-RoomNetwork::RoomNetwork() {
- m_room = std::make_shared<Room>();
- m_room_member = std::make_shared<RoomMember>();
-}
+static std::shared_ptr<RoomMember> g_room_member; ///< RoomMember (Client) for network games
+static std::shared_ptr<Room> g_room; ///< Room (Server) for network games
-bool RoomNetwork::Init() {
+bool Init() {
if (enet_initialize() != 0) {
LOG_ERROR(Network, "Error initializing ENet");
return false;
}
- m_room = std::make_shared<Room>();
- m_room_member = std::make_shared<RoomMember>();
+ g_room = std::make_shared<Room>();
+ g_room_member = std::make_shared<RoomMember>();
LOG_DEBUG(Network, "initialized OK");
return true;
}
-std::weak_ptr<Room> RoomNetwork::GetRoom() {
- return m_room;
+std::weak_ptr<Room> GetRoom() {
+ return g_room;
}
-std::weak_ptr<RoomMember> RoomNetwork::GetRoomMember() {
- return m_room_member;
+std::weak_ptr<RoomMember> GetRoomMember() {
+ return g_room_member;
}
-void RoomNetwork::Shutdown() {
- if (m_room_member) {
- if (m_room_member->IsConnected())
- m_room_member->Leave();
- m_room_member.reset();
+void Shutdown() {
+ if (g_room_member) {
+ if (g_room_member->IsConnected())
+ g_room_member->Leave();
+ g_room_member.reset();
}
- if (m_room) {
- if (m_room->GetState() == Room::State::Open)
- m_room->Destroy();
- m_room.reset();
+ if (g_room) {
+ if (g_room->GetState() == Room::State::Open)
+ g_room->Destroy();
+ g_room.reset();
}
enet_deinitialize();
LOG_DEBUG(Network, "shutdown OK");
diff --git a/src/network/network.h b/src/network/network.h
index e4de207b27..1e1a19a3b9 100644
--- a/src/network/network.h
+++ b/src/network/network.h
@@ -1,6 +1,9 @@
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
#pragma once
#include <memory>
@@ -9,25 +12,16 @@
namespace Network {
-class RoomNetwork {
-public:
- RoomNetwork();
-
- /// Initializes and registers the network device, the room, and the room member.
- bool Init();
-
- /// Returns a pointer to the room handle
- std::weak_ptr<Room> GetRoom();
+/// Initializes and registers the network device, the room, and the room member.
+bool Init();
- /// Returns a pointer to the room member handle
- std::weak_ptr<RoomMember> GetRoomMember();
+/// Returns a pointer to the room handle
+std::weak_ptr<Room> GetRoom();
- /// Unregisters the network device, the room, and the room member and shut them down.
- void Shutdown();
+/// Returns a pointer to the room member handle
+std::weak_ptr<RoomMember> GetRoomMember();
-private:
- std::shared_ptr<RoomMember> m_room_member; ///< RoomMember (Client) for network games
- std::shared_ptr<Room> m_room; ///< Room (Server) for network games
-};
+/// Unregisters the network device, the room, and the room member and shut them down.
+void Shutdown();
} // namespace Network
diff --git a/src/network/room.cpp b/src/network/room.cpp
index 49d7ed3313..a4acdad859 100644
--- a/src/network/room.cpp
+++ b/src/network/room.cpp
@@ -1,7 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
-// SPDX-FileCopyrightText: Copyright yuzu/Citra Emulator Project / Eden Emulator Project
+// SPDX-FileCopyrightText: 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
diff --git a/src/network/room.h b/src/network/room.h
index 780629ada3..a2ae792431 100644
--- a/src/network/room.h
+++ b/src/network/room.h
@@ -1,7 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
-// SPDX-FileCopyrightText: Copyright yuzu/Citra Emulator Project / Eden Emulator Project
+// SPDX-FileCopyrightText: 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index 440ef3c46d..1319511989 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -1,4 +1,4 @@
-# SPDX-FileCopyrightText: Copyright yuzu/Citra Emulator Project / Eden Emulator Project
+# SPDX-FileCopyrightText: 2025 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
set(CMAKE_AUTOMOC ON)
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index 8d773753c1..fb75096a7c 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: Copyright yuzu/Citra Emulator Project / Eden Emulator Project
+// SPDX-FileCopyrightText: 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include <algorithm>
diff --git a/src/yuzu/configuration/configure_touch_from_button.cpp b/src/yuzu/configuration/configure_touch_from_button.cpp
index a6c0cd6c77..dd8a75ef8e 100644
--- a/src/yuzu/configuration/configure_touch_from_button.cpp
+++ b/src/yuzu/configuration/configure_touch_from_button.cpp
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: Copyright yuzu/Citra Emulator Project / Eden Emulator Project
+// SPDX-FileCopyrightText: 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include <QInputDialog>
diff --git a/src/yuzu/configuration/configure_ui.cpp b/src/yuzu/configuration/configure_ui.cpp
index 8c3bbf1af4..67221d2485 100644
--- a/src/yuzu/configuration/configure_ui.cpp
+++ b/src/yuzu/configuration/configure_ui.cpp
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: Copyright yuzu/Citra Emulator Project / Eden Emulator Project
+// SPDX-FileCopyrightText: 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "yuzu/configuration/configure_ui.h"
diff --git a/src/yuzu/configuration/configure_web.cpp b/src/yuzu/configuration/configure_web.cpp
index a513e1c1b0..f7902fb773 100644
--- a/src/yuzu/configuration/configure_web.cpp
+++ b/src/yuzu/configuration/configure_web.cpp
@@ -9,30 +9,30 @@
#include "yuzu/configuration/configure_web.h"
#include "yuzu/uisettings.h"
-static constexpr char token_delimiter{':'};
-
-static std::string GenerateDisplayToken(const std::string& username, const std::string& token) {
- if (username.empty() || token.empty()) {
- return {};
- }
-
- const std::string unencoded_display_token{username + token_delimiter + token};
- QByteArray b{unencoded_display_token.c_str()};
- QByteArray b64 = b.toBase64();
- return b64.toStdString();
-}
-
-static std::string UsernameFromDisplayToken(const std::string& display_token) {
- const std::string unencoded_display_token{
- QByteArray::fromBase64(display_token.c_str()).toStdString()};
- return unencoded_display_token.substr(0, unencoded_display_token.find(token_delimiter));
-}
-
-static std::string TokenFromDisplayToken(const std::string& display_token) {
- const std::string unencoded_display_token{
- QByteArray::fromBase64(display_token.c_str()).toStdString()};
- return unencoded_display_token.substr(unencoded_display_token.find(token_delimiter) + 1);
-}
+// static constexpr char token_delimiter{':'};
+
+// static std::string GenerateDisplayToken(const std::string& username, const std::string& token) {
+// if (username.empty() || token.empty()) {
+// return {};
+// }
+
+// const std::string unencoded_display_token{username + token_delimiter + token};
+// QByteArray b{unencoded_display_token.c_str()};
+// QByteArray b64 = b.toBase64();
+// return b64.toStdString();
+// }
+
+// static std::string UsernameFromDisplayToken(const std::string& display_token) {
+// const std::string unencoded_display_token{
+// QByteArray::fromBase64(display_token.c_str()).toStdString()};
+// return unencoded_display_token.substr(0, unencoded_display_token.find(token_delimiter));
+// }
+
+// static std::string TokenFromDisplayToken(const std::string& display_token) {
+// const std::string unencoded_display_token{
+// QByteArray::fromBase64(display_token.c_str()).toStdString()};
+// return unencoded_display_token.substr(unencoded_display_token.find(token_delimiter) + 1);
+// }
ConfigureWeb::ConfigureWeb(QWidget* parent)
: QWidget(parent), ui(std::make_unique<Ui::ConfigureWeb>()) {
@@ -66,7 +66,7 @@ void ConfigureWeb::RetranslateUI() {
"color:#039be5;\">Sign up</span></a>"));
ui->web_token_info_link->setText(
- tr("<a href='https://eden-emulator.github.io/wiki/yuzu-web-service/'><span style=\"text-decoration: "
+ tr("<a href='https://evilperson1337.notion.site/Hosting-a-Room-Inside-of-Eden-20457c2edaf680108abac6215a79acdb'><span style=\"text-decoration: "
"underline; color:#039be5;\">What is my token?</span></a>"));
}
@@ -76,14 +76,10 @@ void ConfigureWeb::SetConfiguration() {
ui->web_signup_link->setOpenExternalLinks(true);
ui->web_token_info_link->setOpenExternalLinks(true);
- if (Settings::values.yuzu_username.GetValue().empty()) {
- ui->username->setText(tr("Unspecified"));
- } else {
- ui->username->setText(QString::fromStdString(Settings::values.yuzu_username.GetValue()));
- }
-
- ui->edit_token->setText(QString::fromStdString(GenerateDisplayToken(
- Settings::values.yuzu_username.GetValue(), Settings::values.yuzu_token.GetValue())));
+ ui->edit_username->setText(QString::fromStdString(Settings::values.yuzu_username.GetValue()));
+ ui->edit_token->setText(QString::fromStdString(Settings::values.yuzu_token.GetValue()));
+ // ui->edit_token->setText(QString::fromStdString(GenerateDisplayToken(
+ // Settings::values.yuzu_username.GetValue(), Settings::values.yuzu_token.GetValue())));
// Connect after setting the values, to avoid calling OnLoginChanged now
connect(ui->edit_token, &QLineEdit::textChanged, this, &ConfigureWeb::OnLoginChanged);
@@ -95,15 +91,8 @@ void ConfigureWeb::SetConfiguration() {
void ConfigureWeb::ApplyConfiguration() {
UISettings::values.enable_discord_presence = ui->toggle_discordrpc->isChecked();
- if (user_verified) {
- Settings::values.yuzu_username =
- UsernameFromDisplayToken(ui->edit_token->text().toStdString());
- Settings::values.yuzu_token = TokenFromDisplayToken(ui->edit_token->text().toStdString());
- } else {
- QMessageBox::warning(
- this, tr("Token not verified"),
- tr("Token was not verified. The change to your token has not been saved."));
- }
+ Settings::values.yuzu_username = ui->edit_username->text().toStdString();
+ Settings::values.yuzu_token = ui->edit_token->text().toStdString();
}
void ConfigureWeb::OnLoginChanged() {
@@ -124,30 +113,34 @@ void ConfigureWeb::OnLoginChanged() {
}
void ConfigureWeb::VerifyLogin() {
- ui->button_verify_login->setDisabled(true);
- ui->button_verify_login->setText(tr("Verifying..."));
- ui->label_token_verified->setPixmap(QIcon::fromTheme(QStringLiteral("sync")).pixmap(16));
- ui->label_token_verified->setToolTip(tr("Verifying..."));
+ QMessageBox::warning(this,
+ tr("Warning"),
+ tr("Verification is currently nonfunctional, instead generate a random "
+ "48-character string with only lowercase a-z."));
+ // ui->button_verify_login->setDisabled(true);
+ // ui->button_verify_login->setText(tr("Verifying..."));
+ // ui->label_token_verified->setPixmap(QIcon::fromTheme(QStringLiteral("sync")).pixmap(16));
+ // ui->label_token_verified->setToolTip(tr("Verifying..."));
}
void ConfigureWeb::OnLoginVerified() {
- ui->button_verify_login->setEnabled(true);
- ui->button_verify_login->setText(tr("Verify"));
- if (verify_watcher.result()) {
- user_verified = true;
-
- ui->label_token_verified->setPixmap(QIcon::fromTheme(QStringLiteral("checked")).pixmap(16));
- ui->label_token_verified->setToolTip(tr("Verified", "Tooltip"));
- ui->username->setText(
- QString::fromStdString(UsernameFromDisplayToken(ui->edit_token->text().toStdString())));
- } else {
- ui->label_token_verified->setPixmap(QIcon::fromTheme(QStringLiteral("failed")).pixmap(16));
- ui->label_token_verified->setToolTip(tr("Verification failed", "Tooltip"));
- ui->username->setText(tr("Unspecified"));
- QMessageBox::critical(this, tr("Verification failed"),
- tr("Verification failed. Check that you have entered your token "
- "correctly, and that your internet connection is working."));
- }
+ // ui->button_verify_login->setEnabled(true);
+ // ui->button_verify_login->setText(tr("Verify"));
+ // if (verify_watcher.result()) {
+ // user_verified = true;
+
+ // ui->label_token_verified->setPixmap(QIcon::fromTheme(QStringLiteral("checked")).pixmap(16));
+ // ui->label_token_verified->setToolTip(tr("Verified", "Tooltip"));
+ // ui->username->setText(
+ // QString::fromStdString(UsernameFromDisplayToken(ui->edit_token->text().toStdString())));
+ // } else {
+ // ui->label_token_verified->setPixmap(QIcon::fromTheme(QStringLiteral("failed")).pixmap(16));
+ // ui->label_token_verified->setToolTip(tr("Verification failed", "Tooltip"));
+ // ui->username->setText(tr("Unspecified"));
+ // QMessageBox::critical(this, tr("Verification failed"),
+ // tr("Verification failed. Check that you have entered your token "
+ // "correctly, and that your internet connection is working."));
+ // }
}
void ConfigureWeb::SetWebServiceConfigEnabled(bool enabled) {
diff --git a/src/yuzu/configuration/configure_web.ui b/src/yuzu/configuration/configure_web.ui
index 942fb05b26..ac9b6954cd 100644
--- a/src/yuzu/configuration/configure_web.ui
+++ b/src/yuzu/configuration/configure_web.ui
@@ -43,7 +43,7 @@
</sizepolicy>
</property>
<property name="layoutDirection">
- <enum>Qt::RightToLeft</enum>
+ <enum>Qt::LayoutDirection::RightToLeft</enum>
</property>
<property name="text">
<string>Verify</string>
@@ -52,13 +52,23 @@
</item>
<item row="2" column="0">
<widget class="QLabel" name="web_signup_link">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
<property name="text">
<string>Sign up</string>
</property>
</widget>
</item>
<item row="0" column="1" colspan="3">
- <widget class="QLabel" name="username"/>
+ <widget class="QLineEdit" name="edit_username">
+ <property name="maxLength">
+ <number>20</number>
+ </property>
+ <property name="echoMode">
+ <enum>QLineEdit::EchoMode::Normal</enum>
+ </property>
+ </widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_token">
@@ -83,7 +93,7 @@
<number>80</number>
</property>
<property name="echoMode">
- <enum>QLineEdit::Password</enum>
+ <enum>QLineEdit::EchoMode::Normal</enum>
</property>
</widget>
</item>
@@ -97,7 +107,7 @@
<item row="2" column="2">
<spacer name="horizontalSpacer">
<property name="orientation">
- <enum>Qt::Horizontal</enum>
+ <enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
@@ -143,7 +153,7 @@
<item>
<spacer name="verticalSpacer">
<property name="orientation">
- <enum>Qt::Vertical</enum>
+ <enum>Qt::Orientation::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp
index 3fec5b7f61..1cfa37c9e4 100644
--- a/src/yuzu/game_list.cpp
+++ b/src/yuzu/game_list.cpp
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: Copyright yuzu/Citra Emulator Project / Eden Emulator Project
+// SPDX-FileCopyrightText: 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include <regex>
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 7e31fdae4d..c1f71237ad 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: Copyright yuzu/Citra Emulator Project / Eden Emulator Project
+// SPDX-FileCopyrightText: 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include <cinttypes>
@@ -351,7 +351,7 @@ GMainWindow::GMainWindow(bool has_broken_vulkan)
play_time_manager = std::make_unique<PlayTime::PlayTimeManager>(system->GetProfileManager());
- system->GetRoomNetwork().Init();
+ Network::Init();
RegisterMetaTypes();
@@ -5183,7 +5183,7 @@ void GMainWindow::closeEvent(QCloseEvent* event) {
render_window->close();
multiplayer_state->Close();
system->HIDCore().UnloadInputDevices();
- system->GetRoomNetwork().Shutdown();
+ Network::Shutdown();
QWidget::closeEvent(event);
}
diff --git a/src/yuzu/multiplayer/chat_room.cpp b/src/yuzu/multiplayer/chat_room.cpp
index 4463616b45..5925266b40 100644
--- a/src/yuzu/multiplayer/chat_room.cpp
+++ b/src/yuzu/multiplayer/chat_room.cpp
@@ -1,6 +1,9 @@
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
#include <array>
#include <future>
#include <QColor>
@@ -27,8 +30,7 @@
class ChatMessage {
public:
- explicit ChatMessage(const Network::ChatEntry& chat, Network::RoomNetwork& room_network,
- QTime ts = {}) {
+ explicit ChatMessage(const Network::ChatEntry& chat, QTime ts = {}) {
/// Convert the time to their default locale defined format
QLocale locale;
timestamp = locale.toString(ts.isValid() ? ts : QTime::currentTime(), QLocale::ShortFormat);
@@ -38,7 +40,7 @@ public:
// Check for user pings
QString cur_nickname, cur_username;
- if (auto room = room_network.GetRoomMember().lock()) {
+ if (auto room = Network::GetRoomMember().lock()) {
cur_nickname = QString::fromStdString(room->GetNickname());
cur_username = QString::fromStdString(room->GetUsername());
}
@@ -202,10 +204,9 @@ ChatRoom::ChatRoom(QWidget* parent) : QWidget(parent), ui(std::make_unique<Ui::C
ChatRoom::~ChatRoom() = default;
-void ChatRoom::Initialize(Network::RoomNetwork* room_network_) {
- room_network = room_network_;
+void ChatRoom::Initialize() {
// setup the callbacks for network updates
- if (auto member = room_network->GetRoomMember().lock()) {
+ if (auto member = Network::GetRoomMember().lock()) {
member->BindOnChatMessageReceived(
[this](const Network::ChatEntry& chat) { emit ChatReceived(chat); });
member->BindOnStatusMessageReceived(
@@ -239,7 +240,7 @@ void ChatRoom::AppendChatMessage(const QString& msg) {
}
void ChatRoom::SendModerationRequest(Network::RoomMessageTypes type, const std::string& nickname) {
- if (auto room = room_network->GetRoomMember().lock()) {
+ if (auto room = Network::GetRoomMember().lock()) {
auto members = room->GetMemberInformation();
auto it = std::find_if(members.begin(), members.end(),
[&nickname](const Network::RoomMember::MemberInformation& member) {
@@ -259,7 +260,7 @@ bool ChatRoom::ValidateMessage(const std::string& msg) {
void ChatRoom::OnRoomUpdate(const Network::RoomInformation& info) {
// TODO(B3N30): change title
- if (auto room_member = room_network->GetRoomMember().lock()) {
+ if (auto room_member = Network::GetRoomMember().lock()) {
SetPlayerList(room_member->GetMemberInformation());
}
}
@@ -278,7 +279,7 @@ void ChatRoom::OnChatReceive(const Network::ChatEntry& chat) {
if (!ValidateMessage(chat.message)) {
return;
}
- if (auto room = room_network->GetRoomMember().lock()) {
+ if (auto room = Network::GetRoomMember().lock()) {
// get the id of the player
auto members = room->GetMemberInformation();
auto it = std::find_if(members.begin(), members.end(),
@@ -296,7 +297,7 @@ void ChatRoom::OnChatReceive(const Network::ChatEntry& chat) {
return;
}
auto player = std::distance(members.begin(), it);
- ChatMessage m(chat, *room_network);
+ ChatMessage m(chat);
if (m.ContainsPing()) {
emit UserPinged();
}
@@ -335,7 +336,7 @@ void ChatRoom::OnStatusMessageReceive(const Network::StatusMessageEntry& status_
}
void ChatRoom::OnSendChat() {
- if (auto room_member = room_network->GetRoomMember().lock()) {
+ if (auto room_member = Network::GetRoomMember().lock()) {
if (!room_member->IsConnected()) {
return;
}
@@ -357,7 +358,7 @@ void ChatRoom::OnSendChat() {
LOG_INFO(Network, "Cannot find self in the player list when sending a message.");
}
auto player = std::distance(members.begin(), it);
- ChatMessage m(chat, *room_network);
+ ChatMessage m(chat);
room_member->SendChatMessage(message);
AppendChatMessage(m.GetPlayerChatMessage(player));
ui->chat_message->clear();
@@ -451,7 +452,7 @@ void ChatRoom::PopupContextMenu(const QPoint& menu_location) {
}
std::string cur_nickname;
- if (auto room = room_network->GetRoomMember().lock()) {
+ if (auto room = Network::GetRoomMember().lock()) {
cur_nickname = room->GetNickname();
}
diff --git a/src/yuzu/multiplayer/chat_room.h b/src/yuzu/multiplayer/chat_room.h
index dd71ea4cdd..f295ea7a7c 100644
--- a/src/yuzu/multiplayer/chat_room.h
+++ b/src/yuzu/multiplayer/chat_room.h
@@ -1,6 +1,9 @@
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
#pragma once
#include <memory>
@@ -30,7 +33,7 @@ class ChatRoom : public QWidget {
public:
explicit ChatRoom(QWidget* parent);
- void Initialize(Network::RoomNetwork* room_network);
+ void Initialize();
void RetranslateUi();
void SetPlayerList(const Network::RoomMember::MemberList& member_list);
void Clear();
@@ -66,7 +69,6 @@ private:
std::unique_ptr<Ui::ChatRoom> ui;
std::unordered_set<std::string> block_list;
std::unordered_map<std::string, QPixmap> icon_cache;
- Network::RoomNetwork* room_network;
};
Q_DECLARE_METATYPE(Network::ChatEntry);
diff --git a/src/yuzu/multiplayer/client_room.cpp b/src/yuzu/multiplayer/client_room.cpp
index caf34a414f..3453ae34d5 100644
--- a/src/yuzu/multiplayer/client_room.cpp
+++ b/src/yuzu/multiplayer/client_room.cpp
@@ -1,6 +1,9 @@
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
#include <future>
#include <QColor>
#include <QImage>
@@ -18,14 +21,14 @@
#include "yuzu/multiplayer/moderation_dialog.h"
#include "yuzu/multiplayer/state.h"
-ClientRoomWindow::ClientRoomWindow(QWidget* parent, Network::RoomNetwork& room_network_)
+ClientRoomWindow::ClientRoomWindow(QWidget* parent)
: QDialog(parent, Qt::WindowTitleHint | Qt::WindowCloseButtonHint | Qt::WindowSystemMenuHint),
- ui(std::make_unique<Ui::ClientRoom>()), room_network{room_network_} {
+ ui(std::make_unique<Ui::ClientRoom>()) {
ui->setupUi(this);
- ui->chat->Initialize(&room_network);
+ ui->chat->Initialize();
// setup the callbacks for network updates
- if (auto member = room_network.GetRoomMember().lock()) {
+ if (auto member = Network::GetRoomMember().lock()) {
member->BindOnRoomInformationChanged(
[this](const Network::RoomInformation& info) { emit RoomInformationChanged(info); });
member->BindOnStateChanged(
@@ -44,7 +47,7 @@ ClientRoomWindow::ClientRoomWindow(QWidget* parent, Network::RoomNetwork& room_n
ui->disconnect->setDefault(false);
ui->disconnect->setAutoDefault(false);
connect(ui->moderation, &QPushButton::clicked, [this] {
- ModerationDialog dialog(room_network, this);
+ ModerationDialog dialog(this);
dialog.exec();
});
ui->moderation->setDefault(false);
@@ -90,7 +93,7 @@ void ClientRoomWindow::Disconnect() {
}
void ClientRoomWindow::UpdateView() {
- if (auto member = room_network.GetRoomMember().lock()) {
+ if (auto member = Network::GetRoomMember().lock()) {
if (member->IsConnected()) {
ui->chat->Enable();
ui->disconnect->setEnabled(true);
diff --git a/src/yuzu/multiplayer/client_room.h b/src/yuzu/multiplayer/client_room.h
index f338e3c592..6990c56e50 100644
--- a/src/yuzu/multiplayer/client_room.h
+++ b/src/yuzu/multiplayer/client_room.h
@@ -1,6 +1,9 @@
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
#pragma once
#include "yuzu/multiplayer/chat_room.h"
@@ -13,7 +16,7 @@ class ClientRoomWindow : public QDialog {
Q_OBJECT
public:
- explicit ClientRoomWindow(QWidget* parent, Network::RoomNetwork& room_network_);
+ explicit ClientRoomWindow(QWidget* parent);
~ClientRoomWindow();
void RetranslateUi();
@@ -35,5 +38,4 @@ private:
QStandardItemModel* player_list;
std::unique_ptr<Ui::ClientRoom> ui;
- Network::RoomNetwork& room_network;
};
diff --git a/src/yuzu/multiplayer/direct_connect.cpp b/src/yuzu/multiplayer/direct_connect.cpp
index 74da97e218..693cffe0d0 100644
--- a/src/yuzu/multiplayer/direct_connect.cpp
+++ b/src/yuzu/multiplayer/direct_connect.cpp
@@ -1,6 +1,9 @@
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
#include <QComboBox>
#include <QFuture>
#include <QIntValidator>
@@ -24,8 +27,7 @@ enum class ConnectionType : u8 { TraversalServer, IP };
DirectConnectWindow::DirectConnectWindow(Core::System& system_, QWidget* parent)
: QDialog(parent, Qt::WindowTitleHint | Qt::WindowCloseButtonHint | Qt::WindowSystemMenuHint),
- ui(std::make_unique<Ui::DirectConnect>()), system{system_}, room_network{
- system.GetRoomNetwork()} {
+ ui(std::make_unique<Ui::DirectConnect>()), system{system_} {
ui->setupUi(this);
@@ -71,7 +73,7 @@ void DirectConnectWindow::Connect() {
return;
}
}
- if (const auto member = room_network.GetRoomMember().lock()) {
+ if (const auto member = Network::GetRoomMember().lock()) {
// Prevent the user from trying to join a room while they are already joining.
if (member->GetState() == Network::RoomMember::State::Joining) {
return;
@@ -104,7 +106,7 @@ void DirectConnectWindow::Connect() {
// attempt to connect in a different thread
QFuture<void> f = QtConcurrent::run([&] {
- if (auto room_member = room_network.GetRoomMember().lock()) {
+ if (auto room_member = Network::GetRoomMember().lock()) {
auto port = UISettings::values.multiplayer_port.GetValue();
room_member->Join(ui->nickname->text().toStdString(),
ui->ip->text().toStdString().c_str(), port, 0, Network::NoPreferredIP,
@@ -129,7 +131,7 @@ void DirectConnectWindow::EndConnecting() {
void DirectConnectWindow::OnConnection() {
EndConnecting();
- if (auto room_member = room_network.GetRoomMember().lock()) {
+ if (auto room_member = Network::GetRoomMember().lock()) {
if (room_member->IsConnected()) {
close();
}
diff --git a/src/yuzu/multiplayer/direct_connect.h b/src/yuzu/multiplayer/direct_connect.h
index b8f66cfb2b..e7efaecbd2 100644
--- a/src/yuzu/multiplayer/direct_connect.h
+++ b/src/yuzu/multiplayer/direct_connect.h
@@ -1,6 +1,9 @@
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
#pragma once
#include <memory>
@@ -45,5 +48,4 @@ private:
std::unique_ptr<Ui::DirectConnect> ui;
Validation validation;
Core::System& system;
- Network::RoomNetwork& room_network;
};
diff --git a/src/yuzu/multiplayer/host_room.cpp b/src/yuzu/multiplayer/host_room.cpp
index 09f783ad9f..fd047b4b5a 100644
--- a/src/yuzu/multiplayer/host_room.cpp
+++ b/src/yuzu/multiplayer/host_room.cpp
@@ -1,6 +1,9 @@
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
#include <future>
#include <QColor>
#include <QImage>
@@ -32,8 +35,7 @@ HostRoomWindow::HostRoomWindow(QWidget* parent, QStandardItemModel* list,
Core::System& system_)
: QDialog(parent, Qt::WindowTitleHint | Qt::WindowCloseButtonHint | Qt::WindowSystemMenuHint),
ui(std::make_unique<Ui::HostRoom>()),
- announce_multiplayer_session(session), system{system_}, room_network{
- system.GetRoomNetwork()} {
+ announce_multiplayer_session(session), system{system_} {
ui->setupUi(this);
// set up validation for all of the fields
@@ -137,7 +139,7 @@ void HostRoomWindow::Host() {
return;
}
}
- if (auto member = room_network.GetRoomMember().lock()) {
+ if (auto member = Network::GetRoomMember().lock()) {
if (member->GetState() == Network::RoomMember::State::Joining) {
return;
} else if (member->IsConnected()) {
@@ -161,7 +163,7 @@ void HostRoomWindow::Host() {
if (ui->load_ban_list->isChecked()) {
ban_list = UISettings::values.multiplayer_ban_list;
}
- if (auto room = room_network.GetRoom().lock()) {
+ if (auto room = Network::GetRoom().lock()) {
const bool created =
room->Create(ui->room_name->text().toStdString(),
ui->room_description->toPlainText().toStdString(), "", port, password,
@@ -190,7 +192,7 @@ void HostRoomWindow::Host() {
QString::fromStdString(result.result_string),
QMessageBox::Ok);
ui->host->setEnabled(true);
- if (auto room = room_network.GetRoom().lock()) {
+ if (auto room = Network::GetRoom().lock()) {
room->Destroy();
}
return;
@@ -206,7 +208,7 @@ void HostRoomWindow::Host() {
WebService::Client client(Settings::values.web_api_url.GetValue(),
Settings::values.yuzu_username.GetValue(),
Settings::values.yuzu_token.GetValue());
- if (auto room = room_network.GetRoom().lock()) {
+ if (auto room = Network::GetRoom().lock()) {
token = client.GetExternalJWT(room->GetVerifyUID()).returned_data;
}
if (token.empty()) {
diff --git a/src/yuzu/multiplayer/host_room.h b/src/yuzu/multiplayer/host_room.h
index ae816e2e03..d9b08d2c8c 100644
--- a/src/yuzu/multiplayer/host_room.h
+++ b/src/yuzu/multiplayer/host_room.h
@@ -1,6 +1,9 @@
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
#pragma once
#include <memory>
@@ -59,7 +62,6 @@ private:
ComboBoxProxyModel* proxy;
Validation validation;
Core::System& system;
- Network::RoomNetwork& room_network;
};
/**
diff --git a/src/yuzu/multiplayer/lobby.cpp b/src/yuzu/multiplayer/lobby.cpp
index 52df2456e5..bc91145b97 100644
--- a/src/yuzu/multiplayer/lobby.cpp
+++ b/src/yuzu/multiplayer/lobby.cpp
@@ -1,6 +1,9 @@
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
#include <QInputDialog>
#include <QList>
#include <QtConcurrent/QtConcurrentRun>
@@ -28,8 +31,7 @@ Lobby::Lobby(QWidget* parent, QStandardItemModel* list,
std::shared_ptr<Core::AnnounceMultiplayerSession> session, Core::System& system_)
: QDialog(parent, Qt::WindowTitleHint | Qt::WindowCloseButtonHint | Qt::WindowSystemMenuHint),
ui(std::make_unique<Ui::Lobby>()),
- announce_multiplayer_session(session), system{system_}, room_network{
- system.GetRoomNetwork()} {
+ announce_multiplayer_session(session), system{system_} {
ui->setupUi(this);
// setup the watcher for background connections
@@ -146,7 +148,7 @@ void Lobby::OnJoinRoom(const QModelIndex& source) {
}
}
- if (const auto member = room_network.GetRoomMember().lock()) {
+ if (const auto member = Network::GetRoomMember().lock()) {
// Prevent the user from trying to join a room while they are already joining.
if (member->GetState() == Network::RoomMember::State::Joining) {
return;
@@ -184,7 +186,7 @@ void Lobby::OnJoinRoom(const QModelIndex& source) {
proxy->data(connection_index, LobbyItemHost::HostVerifyUIDRole).toString().toStdString();
// attempt to connect in a different thread
- QFuture<void> f = QtConcurrent::run([nickname, ip, port, password, verify_uid, this] {
+ QFuture<void> f = QtConcurrent::run([nickname, ip, port, password, verify_uid] {
std::string token;
#ifdef ENABLE_WEB_SERVICE
if (!Settings::values.yuzu_username.GetValue().empty() &&
@@ -200,7 +202,7 @@ void Lobby::OnJoinRoom(const QModelIndex& source) {
}
}
#endif
- if (auto room_member = room_network.GetRoomMember().lock()) {
+ if (auto room_member = Network::GetRoomMember().lock()) {
room_member->Join(nickname, ip.c_str(), port, 0, Network::NoPreferredIP, password,
token);
}
diff --git a/src/yuzu/multiplayer/lobby.h b/src/yuzu/multiplayer/lobby.h
index e78c9cae3c..4d4e7bd5e4 100644
--- a/src/yuzu/multiplayer/lobby.h
+++ b/src/yuzu/multiplayer/lobby.h
@@ -1,6 +1,9 @@
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
#pragma once
#include <memory>
@@ -102,7 +105,6 @@ private:
QFutureWatcher<void>* watcher;
Validation validation;
Core::System& system;
- Network::RoomNetwork& room_network;
};
/**
diff --git a/src/yuzu/multiplayer/moderation_dialog.cpp b/src/yuzu/multiplayer/moderation_dialog.cpp
index c9b8ed397a..1a06d661d5 100644
--- a/src/yuzu/multiplayer/moderation_dialog.cpp
+++ b/src/yuzu/multiplayer/moderation_dialog.cpp
@@ -1,6 +1,9 @@
// SPDX-FileCopyrightText: Copyright 2018 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
#include <QStandardItem>
#include <QStandardItemModel>
#include "network/network.h"
@@ -16,13 +19,13 @@ enum {
};
}
-ModerationDialog::ModerationDialog(Network::RoomNetwork& room_network_, QWidget* parent)
- : QDialog(parent), ui(std::make_unique<Ui::ModerationDialog>()), room_network{room_network_} {
+ModerationDialog::ModerationDialog(QWidget* parent)
+ : QDialog(parent), ui(std::make_unique<Ui::ModerationDialog>()) {
ui->setupUi(this);
qRegisterMetaType<Network::Room::BanList>();
- if (auto member = room_network.GetRoomMember().lock()) {
+ if (auto member = Network::GetRoomMember().lock()) {
callback_handle_status_message = member->BindOnStatusMessageReceived(
[this](const Network::StatusMessageEntry& status_message) {
emit StatusMessageReceived(status_message);
@@ -55,20 +58,20 @@ ModerationDialog::ModerationDialog(Network::RoomNetwork& room_network_, QWidget*
ModerationDialog::~ModerationDialog() {
if (callback_handle_status_message) {
- if (auto room = room_network.GetRoomMember().lock()) {
+ if (auto room = Network::GetRoomMember().lock()) {
room->Unbind(callback_handle_status_message);
}
}
if (callback_handle_ban_list) {
- if (auto room = room_network.GetRoomMember().lock()) {
+ if (auto room = Network::GetRoomMember().lock()) {
room->Unbind(callback_handle_ban_list);
}
}
}
void ModerationDialog::LoadBanList() {
- if (auto room = room_network.GetRoomMember().lock()) {
+ if (auto room = Network::GetRoomMember().lock()) {
ui->refresh->setEnabled(false);
ui->refresh->setText(tr("Refreshing"));
ui->unban->setEnabled(false);
@@ -97,7 +100,7 @@ void ModerationDialog::PopulateBanList(const Network::Room::BanList& ban_list) {
}
void ModerationDialog::SendUnbanRequest(const QString& subject) {
- if (auto room = room_network.GetRoomMember().lock()) {
+ if (auto room = Network::GetRoomMember().lock()) {
room->SendModerationRequest(Network::IdModUnban, subject.toStdString());
}
}
diff --git a/src/yuzu/multiplayer/moderation_dialog.h b/src/yuzu/multiplayer/moderation_dialog.h
index e9e5daff79..4d32a6895e 100644
--- a/src/yuzu/multiplayer/moderation_dialog.h
+++ b/src/yuzu/multiplayer/moderation_dialog.h
@@ -1,6 +1,9 @@
// SPDX-FileCopyrightText: Copyright 2018 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
#pragma once
#include <memory>
@@ -19,7 +22,7 @@ class ModerationDialog : public QDialog {
Q_OBJECT
public:
- explicit ModerationDialog(Network::RoomNetwork& room_network_, QWidget* parent = nullptr);
+ explicit ModerationDialog(QWidget* parent = nullptr);
~ModerationDialog();
signals:
@@ -36,8 +39,6 @@ private:
QStandardItemModel* model;
Network::RoomMember::CallbackHandle<Network::StatusMessageEntry> callback_handle_status_message;
Network::RoomMember::CallbackHandle<Network::Room::BanList> callback_handle_ban_list;
-
- Network::RoomNetwork& room_network;
};
Q_DECLARE_METATYPE(Network::Room::BanList);
diff --git a/src/yuzu/multiplayer/state.cpp b/src/yuzu/multiplayer/state.cpp
index d82ca9aee5..d5529712b0 100644
--- a/src/yuzu/multiplayer/state.cpp
+++ b/src/yuzu/multiplayer/state.cpp
@@ -1,6 +1,9 @@
// SPDX-FileCopyrightText: Copyright 2018 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
#include <QAction>
#include <QApplication>
#include <QIcon>
@@ -22,8 +25,8 @@
MultiplayerState::MultiplayerState(QWidget* parent, QStandardItemModel* game_list_model_,
QAction* leave_room_, QAction* show_room_, Core::System& system_)
: QWidget(parent), game_list_model(game_list_model_), leave_room(leave_room_),
- show_room(show_room_), system{system_}, room_network{system.GetRoomNetwork()} {
- if (auto member = room_network.GetRoomMember().lock()) {
+ show_room(show_room_), system{system_} {
+ if (auto member = Network::GetRoomMember().lock()) {
// register the network structs to use in slots and signals
state_callback_handle = member->BindOnStateChanged(
[this](const Network::RoomMember::State& state) { emit NetworkStateChanged(state); });
@@ -37,7 +40,7 @@ MultiplayerState::MultiplayerState(QWidget* parent, QStandardItemModel* game_lis
qRegisterMetaType<Network::RoomMember::State>();
qRegisterMetaType<Network::RoomMember::Error>();
qRegisterMetaType<WebService::WebResult>();
- announce_multiplayer_session = std::make_shared<Core::AnnounceMultiplayerSession>(room_network);
+ announce_multiplayer_session = std::make_shared<Core::AnnounceMultiplayerSession>();
announce_multiplayer_session->BindErrorCallback(
[this](const WebService::WebResult& result) { emit AnnounceFailed(result); });
connect(this, &MultiplayerState::AnnounceFailed, this, &MultiplayerState::OnAnnounceFailed);
@@ -62,13 +65,13 @@ MultiplayerState::~MultiplayerState() = default;
void MultiplayerState::Close() {
if (state_callback_handle) {
- if (auto member = room_network.GetRoomMember().lock()) {
+ if (auto member = Network::GetRoomMember().lock()) {
member->Unbind(state_callback_handle);
}
}
if (error_callback_handle) {
- if (auto member = room_network.GetRoomMember().lock()) {
+ if (auto member = Network::GetRoomMember().lock()) {
member->Unbind(error_callback_handle);
}
}
@@ -256,9 +259,9 @@ void MultiplayerState::OnCreateRoom() {
bool MultiplayerState::OnCloseRoom() {
if (!NetworkMessage::WarnCloseRoom())
return false;
- if (auto room = room_network.GetRoom().lock()) {
+ if (auto room = Network::GetRoom().lock()) {
// if you are in a room, leave it
- if (auto member = room_network.GetRoomMember().lock()) {
+ if (auto member = Network::GetRoomMember().lock()) {
member->Leave();
LOG_DEBUG(Frontend, "Left the room (as a client)");
}
@@ -292,10 +295,10 @@ void MultiplayerState::HideNotification() {
}
void MultiplayerState::OnOpenNetworkRoom() {
- if (auto member = room_network.GetRoomMember().lock()) {
+ if (auto member = Network::GetRoomMember().lock()) {
if (member->IsConnected()) {
if (client_room == nullptr) {
- client_room = new ClientRoomWindow(this, room_network);
+ client_room = new ClientRoomWindow(this);
connect(client_room, &ClientRoomWindow::ShowNotification, this,
&MultiplayerState::ShowNotification);
}
diff --git a/src/yuzu/multiplayer/state.h b/src/yuzu/multiplayer/state.h
index d6149838f8..4d3dde2162 100644
--- a/src/yuzu/multiplayer/state.h
+++ b/src/yuzu/multiplayer/state.h
@@ -1,6 +1,9 @@
// SPDX-FileCopyrightText: Copyright 2018 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
#pragma once
#include <QWidget>
@@ -105,7 +108,6 @@ private:
bool show_notification = false;
Core::System& system;
- Network::RoomNetwork& room_network;
};
Q_DECLARE_METATYPE(WebService::WebResult);
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index 35dc58e4e9..005f5bf1db 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -1,6 +1,9 @@
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
#include <chrono>
#include <iostream>
#include <memory>
@@ -404,7 +407,7 @@ int main(int argc, char** argv) {
}
if (use_multiplayer) {
- if (auto member = system.GetRoomNetwork().GetRoomMember().lock()) {
+ if (auto member = Network::GetRoomMember().lock()) {
member->BindOnChatMessageReceived(OnMessageReceived);
member->BindOnStatusMessageReceived(OnStatusMessageReceived);
member->BindOnStateChanged(OnStateChanged);