mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2025-07-19 11:15:45 +00:00
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>
This commit is contained in:
parent
7e13da47af
commit
76fa525592
99 changed files with 1470 additions and 498 deletions
7
.gitignore
vendored
7
.gitignore
vendored
|
@ -1,6 +1,9 @@
|
|||
# SPDX-FileCopyrightText: 2013 Citra Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
# SPDX-FileCopyrightText: Eden Emulator Project
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
# Build directory
|
||||
[Bb]uild*/
|
||||
doc-build/
|
||||
|
@ -36,3 +39,7 @@ CMakeSettings.json
|
|||
# Windows global filetypes
|
||||
Thumbs.db
|
||||
|
||||
# Artifacts
|
||||
eden-windows-msvc
|
||||
artifacts
|
||||
*.AppImage*
|
||||
|
|
|
@ -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
|
||||
|
||||
cmake_minimum_required(VERSION 3.22)
|
||||
|
|
|
@ -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
|
||||
|
||||
# This function downloads a binary library package from our external repo.
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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" />
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -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) {
|
|||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
},
|
||||
)
|
||||
)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 getIpAddressByWifi(activity: Activity): String {
|
||||
var ipAddress = 0
|
||||
val wifiManager = activity.getSystemService(WifiManager::class.java)
|
||||
val wifiInfo = wifiManager.connectionInfo
|
||||
if (wifiInfo != null) {
|
||||
ipAddress = wifiInfo.ipAddress
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
if (ipAddress == 0) {
|
||||
val dhcpInfo = wifiManager.dhcpInfo
|
||||
if (dhcpInfo != null) {
|
||||
ipAddress = dhcpInfo.ipAddress
|
||||
fun getIpAddressByWifi(activity: Activity): String {
|
||||
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 (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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
multiplayer->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());
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
|
|
9
src/android/app/src/main/res/drawable/ic_ip.xml
Normal file
9
src/android/app/src/main/res/drawable/ic_ip.xml
Normal file
|
@ -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>
|
10
src/android/app/src/main/res/drawable/ic_joined.xml
Normal file
10
src/android/app/src/main/res/drawable/ic_joined.xml
Normal file
|
@ -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>
|
9
src/android/app/src/main/res/drawable/ic_multiplayer.xml
Normal file
9
src/android/app/src/main/res/drawable/ic_multiplayer.xml
Normal file
|
@ -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>
|
220
src/android/app/src/main/res/layout/dialog_lobby_browser.xml
Normal file
220
src/android/app/src/main/res/layout/dialog_lobby_browser.xml
Normal file
|
@ -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>
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -59,6 +59,21 @@
|
|||
android:inputType="text" />
|
||||
</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"
|
||||
|
|
|
@ -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>
|
9
src/android/app/src/main/res/layout/dropdown_item.xml
Normal file
9
src/android/app/src/main/res/layout/dropdown_item.xml
Normal file
|
@ -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" />
|
113
src/android/app/src/main/res/layout/item_lobby_room.xml
Normal file
113
src/android/app/src/main/res/layout/item_lobby_room.xml
Normal file
|
@ -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>
|
4
src/android/app/src/main/res/values-fr-rSN/strings.xml
Normal file
4
src/android/app/src/main/res/values-fr-rSN/strings.xml
Normal file
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">Eden</string>
|
||||
</resources>
|
|
@ -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>
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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"},
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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();
|
||||
|
||||
/// 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();
|
||||
|
||||
/// Returns a pointer to the room handle
|
||||
std::weak_ptr<Room> GetRoom();
|
||||
/// Returns a pointer to the room member handle
|
||||
std::weak_ptr<RoomMember> GetRoomMember();
|
||||
|
||||
/// Returns a pointer to the room member handle
|
||||
std::weak_ptr<RoomMember> GetRoomMember();
|
||||
|
||||
/// Unregisters the network device, the room, and the room member and shut them down.
|
||||
void Shutdown();
|
||||
|
||||
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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -9,30 +9,30 @@
|
|||
#include "yuzu/configuration/configure_web.h"
|
||||
#include "yuzu/uisettings.h"
|
||||
|
||||
static constexpr char token_delimiter{':'};
|
||||
// static constexpr char token_delimiter{':'};
|
||||
|
||||
static std::string GenerateDisplayToken(const std::string& username, const std::string& token) {
|
||||
if (username.empty() || token.empty()) {
|
||||
return {};
|
||||
}
|
||||
// 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();
|
||||
}
|
||||
// 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 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 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->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->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) {
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -38,8 +38,7 @@
|
|||
"description": "Enable web services (telemetry, etc.)",
|
||||
"dependencies": [
|
||||
{
|
||||
"name": "openssl",
|
||||
"platform": "windows"
|
||||
"name": "openssl"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue