From eb778943153cee9dcadca227f3b4fdae6f50ab84 Mon Sep 17 00:00:00 2001 From: Connor McLaughlin Date: Fri, 5 May 2023 18:40:04 +1000 Subject: [PATCH 1/8] Update README.md --- README.md | 24 ++---------------------- 1 file changed, 2 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 7e75d4aad..3ed22abc3 100644 --- a/README.md +++ b/README.md @@ -104,13 +104,9 @@ You will need a device with armv7 (32-bit ARM), AArch64 (64-bit ARM), or x86_64 Google Play is the preferred distribution mechanism and will result in smaller download sizes: https://play.google.com/store/apps/details?id=com.github.stenzek.duckstation -**No support is provided for the Android app**, it is free and your expectations should be in line with that. Please **do not** email me about issues about it, they will be ignored. This repository should also not be used to raise issues about the app, as it does not contain the app code, only the desktop versions. +**No support is provided for the Android app**, it is free and your expectations should be in line with that. Please **do not** email me about issues about it, they will be ignored. -If you must use an APK, download links are: - -Download link: https://www.duckstation.org/android/duckstation-android.apk - -Changelog link: https://www.duckstation.org/android/changelog.txt +If you must use an APK, download links are listed in https://www.duckstation.org/android/ To use: 1. Install and run the app for the first time. @@ -215,22 +211,6 @@ Hotkeys: - **Tab:** Temporarily disable speed limiter - **Space:** Pause/resume emulation -## Screenshots -

- Monkey Hero - Ridge Racer Type 4 - Tomb Raider 2 - Quake 2 - Croc - Croc 2 - Final Fantasy 7 - Mega Man 8 - Final Fantasy 8 in Fullscreen UI - Spyro in Fullscreen UI - Threads of Fate in Fullscreen UI - Game Grid -

- ## Disclaimers Icon by icons8: https://icons8.com/icon/74847/platforms.undefined.short-title From 7681551544c39294369d0f0366971a0d76cf045c Mon Sep 17 00:00:00 2001 From: Stenzek Date: Mon, 8 May 2023 13:07:19 +1000 Subject: [PATCH 2/8] System: Don't auto enable analog on unknown games --- src/core/analog_controller.cpp | 5 ++--- src/core/system.cpp | 16 ++++++---------- src/core/system.h | 2 +- 3 files changed, 9 insertions(+), 14 deletions(-) diff --git a/src/core/analog_controller.cpp b/src/core/analog_controller.cpp index bfe67fb05..2e1288214 100644 --- a/src/core/analog_controller.cpp +++ b/src/core/analog_controller.cpp @@ -53,7 +53,7 @@ void AnalogController::Reset() if (m_force_analog_on_reset) { - if (g_settings.controller_disable_analog_mode_forcing || System::IsRunningBIOS()) + if (g_settings.controller_disable_analog_mode_forcing || System::IsRunningUnknownGame()) { Host::AddIconOSDMessage( fmt::format("Controller{}AnalogMode", m_index), ICON_FA_GAMEPAD, @@ -835,8 +835,7 @@ static const char* s_invert_settings[] = {TRANSLATABLE("AnalogController", "Not static const SettingInfo s_settings[] = { {SettingInfo::Type::Boolean, "ForceAnalogOnReset", TRANSLATABLE("AnalogController", "Force Analog Mode on Reset"), - TRANSLATABLE("AnalogController", "Forces the controller to analog mode when the console is reset/powered on. May " - "cause issues with games, so it is recommended to leave this option off."), + TRANSLATABLE("AnalogController", "Forces the controller to analog mode when the console is reset/powered on."), "true"}, {SettingInfo::Type::Boolean, "AnalogDPadInDigitalMode", TRANSLATABLE("AnalogController", "Use Analog Sticks for D-Pad in Digital Mode"), diff --git a/src/core/system.cpp b/src/core/system.cpp index 59b2b8d7a..3b82f717e 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp @@ -147,7 +147,7 @@ static BIOS::Hash s_bios_hash = {}; static std::string s_running_game_path; static std::string s_running_game_serial; static std::string s_running_game_title; -static bool s_running_bios; +static bool s_running_unknown_game; static float s_throttle_frequency = 60.0f; static float s_target_speed = 1.0f; @@ -329,9 +329,9 @@ const std::string& System::GetRunningTitle() return s_running_game_title; } -bool System::IsRunningBIOS() +bool System::IsRunningUnknownGame() { - return s_running_bios; + return s_running_unknown_game; } const BIOS::ImageInfo* System::GetBIOSImageInfo() @@ -967,9 +967,6 @@ void System::ResetSystem() ResetPerformanceCounters(); ResetThrottler(); Host::AddOSDMessage(Host::TranslateStdString("OSDMessage", "System reset.")); - - // need to clear this here, because of eject disc -> reset. - s_running_bios = !s_running_game_path.empty(); } void System::PauseSystem(bool paused) @@ -1248,9 +1245,6 @@ bool System::BootSystem(SystemBootParameters parameters) return false; } - // Allow controller analog mode for EXEs and PSFs. - s_running_bios = s_running_game_path.empty() && exe_boot.empty() && psf_boot.empty(); - UpdateControllers(); UpdateMemoryCardTypes(); UpdateMultitaps(); @@ -1519,7 +1513,7 @@ void System::ClearRunningGame() s_running_game_serial.clear(); s_running_game_path.clear(); s_running_game_title.clear(); - s_running_bios = false; + s_running_unknown_game = false; s_cheat_list.reset(); s_state = State::Shutdown; @@ -3037,6 +3031,7 @@ void System::UpdateRunningGame(const char* path, CDImage* image, bool booting) s_running_game_path.clear(); s_running_game_serial.clear(); s_running_game_title.clear(); + s_running_unknown_game = true; if (path && std::strlen(path) > 0) { @@ -3054,6 +3049,7 @@ void System::UpdateRunningGame(const char* path, CDImage* image, bool booting) { s_running_game_serial = entry->serial; s_running_game_title = entry->title; + s_running_unknown_game = false; } else { diff --git a/src/core/system.h b/src/core/system.h index 7dfd22902..adbec80ca 100644 --- a/src/core/system.h +++ b/src/core/system.h @@ -181,7 +181,7 @@ const std::string& GetRunningPath(); const std::string& GetRunningSerial(); const std::string& GetRunningTitle(); -bool IsRunningBIOS(); +bool IsRunningUnknownGame(); const BIOS::ImageInfo* GetBIOSImageInfo(); const BIOS::Hash& GetBIOSHash(); From ddaa7172c82ada87444ea98d109442a725c62240 Mon Sep 17 00:00:00 2001 From: HeatXD <45072324+HeatXD@users.noreply.github.com> Date: Wed, 10 May 2023 04:06:29 +0200 Subject: [PATCH 3/8] Netplay: Wired up netplay chat. --- src/core/netplay.cpp | 184 +++++++++++++++++++------- src/core/netplay.h | 5 +- src/duckstation-qt/qthost.cpp | 9 +- src/frontend-common/imgui_netplay.cpp | 4 +- 4 files changed, 143 insertions(+), 59 deletions(-) diff --git a/src/core/netplay.cpp b/src/core/netplay.cpp index 74b0aa48a..78802ccc4 100644 --- a/src/core/netplay.cpp +++ b/src/core/netplay.cpp @@ -48,6 +48,11 @@ enum class ControlMessage : u32 SynchronizeComplete, }; +enum class SessionMessage : u32 +{ + ChatMessage, +}; + #pragma pack(push, 1) struct ControlMessageHeader { @@ -55,6 +60,13 @@ struct ControlMessageHeader u32 size; }; +#pragma pack(push, 1) +struct SessionMessageHeader +{ + SessionMessage type; + u32 size; +}; + struct ControlConnectResponseMessage { enum class Result : u32 @@ -94,6 +106,15 @@ struct ControlSynchronizeCompleteMessage static ControlMessage MessageType() { return ControlMessage::SynchronizeComplete; } }; + +struct SessionChatMessage +{ + SessionMessageHeader header; + + u32 chat_message_size; + + static SessionMessage MessageType() { return SessionMessage::ChatMessage; } +}; #pragma pack(pop) using SaveStateBuffer = std::unique_ptr; @@ -142,6 +163,10 @@ static void HandleConnectResponseMessage(s32 player_id, const ENetPacket* pkt); static void HandleSynchronizeSessionMessage(s32 player_id, const ENetPacket* pkt); static void HandleSynchronizeCompleteMessage(s32 player_id, const ENetPacket* pkt); +// Sessionpackets +static void HandleSessionMessage(s32 player_id, const ENetPacket* pkt); +static void HandleSessionChatMessage(s32 player_id, const ENetPacket* pkt); + // l = local, r = remote static bool CreateGGPOSession(); static void DestroyGGPOSession(); @@ -170,7 +195,6 @@ static void Throttle(); // Desync Detection static void GenerateChecksumForFrame(int* checksum, int frame, unsigned char* buffer, int buffer_size); -static void GenerateDesyncReport(s32 desync_frame); ////////////////////////////////////////////////////////////////////////// // Variables @@ -252,7 +276,34 @@ static bool SendControlPacket(s32 player_id, const PacketWrapper& pkt) DebugAssert(player_id >= 0 && player_id < MAX_PLAYERS && s_peers[player_id].peer); return SendControlPacket(s_peers[player_id].peer, pkt); } +template +static PacketWrapper NewSessionPacket(u32 size = sizeof(T), u32 flags = ENET_PACKET_FLAG_RELIABLE) +{ + PacketWrapper ret = NewWrappedPacket(size, flags); + SessionMessageHeader* hdr = reinterpret_cast(ret.pkt->data); + hdr->type = T::MessageType(); + hdr->size = size; + return ret; +} +template +static bool SendSessionPacket(ENetPeer* peer, const PacketWrapper& pkt) +{ + const int rc = enet_peer_send(peer, ENET_CHANNEL_SESSION, pkt.pkt); + if (rc != 0) + { + Log_ErrorPrintf("enet_peer_send() failed: %d", rc); + enet_packet_destroy(pkt.pkt); + return false; + } + return true; +} +template +static bool SendSessionPacket(s32 player_id, const PacketWrapper& pkt) +{ + DebugAssert(player_id >= 0 && player_id < MAX_PLAYERS && s_peers[player_id].peer); + return SendSessionPacket(s_peers[player_id].peer, pkt); +} } // namespace Netplay // Netplay Impl @@ -472,6 +523,10 @@ void Netplay::HandleEnetEvent(const ENetEvent* event) { HandleControlMessage(player_id, event->packet); } + else if (event->channelID == ENET_CHANNEL_SESSION) + { + HandleSessionMessage(player_id, event->packet); + } else if (event->channelID == ENET_CHANNEL_GGPO) { Log_TracePrintf("Received %zu ggpo bytes from player %d", event->packet->dataLength, player_id); @@ -505,6 +560,10 @@ void Netplay::PollEnet(Common::Timer::Value until_time) const u32 enet_timeout = (current_time >= until_time) ? 0 : static_cast(Common::Timer::ConvertValueToMilliseconds(until_time - current_time)); + + // make sure s_enet_host exists + Assert(s_enet_host); + const int res = enet_host_service(s_enet_host, &event, enet_timeout); if (res > 0) { @@ -514,7 +573,6 @@ void Netplay::PollEnet(Common::Timer::Value until_time) current_time = Common::Timer::GetCurrentValue(); continue; } - // exit once we're nonblocking current_time = Common::Timer::GetCurrentValue(); if (enet_timeout == 0 || current_time >= until_time) @@ -1003,6 +1061,43 @@ void Netplay::HandleSynchronizeCompleteMessage(s32 player_id, const ENetPacket* CheckForCompleteResynchronize(); } +void Netplay::HandleSessionMessage(s32 player_id, const ENetPacket* pkt) +{ + if (pkt->dataLength < sizeof(ControlMessageHeader)) + { + Log_ErrorPrintf("Invalid control packet from player %d of size %zu", player_id, pkt->dataLength); + return; + } + + const SessionMessageHeader* hdr = reinterpret_cast(pkt->data); + switch (hdr->type) + { + case SessionMessage::ChatMessage: + HandleSessionChatMessage(player_id, pkt); + break; + + default: + Log_ErrorPrintf("Unhandled session packet %u from player %d of size %zu", hdr->type, player_id, pkt->dataLength); + break; + } +} + +void Netplay::HandleSessionChatMessage(s32 player_id, const ENetPacket* pkt) +{ + const SessionChatMessage* msg = reinterpret_cast(pkt->data); + if (pkt->dataLength < sizeof(SessionChatMessage) || + pkt->dataLength < (sizeof(SessionChatMessage) + msg->chat_message_size)) + { + // invalid chat message. ignore. + return; + } + + std::string message(pkt->data + sizeof(SessionChatMessage), + pkt->data + sizeof(SessionChatMessage) + msg->chat_message_size); + + Host::OnNetplayMessage(fmt::format("Player {}: {}", player_id + 1, message)); +} + void Netplay::CheckForCompleteResynchronize() { if (s_synchronized_players == s_num_players) @@ -1037,8 +1132,6 @@ void Netplay::SetSettings() // no block linking, it degrades savestate loading performance si.SetBoolValue("CPU", "RecompilerBlockLinking", false); - // not sure its needed but enabled for now... TODO - si.SetBoolValue("GPU", "UseSoftwareRendererForReadbacks", true); Host::Internal::SetNetplaySettingsLayer(&si); System::ApplySettings(false); @@ -1065,6 +1158,9 @@ void Netplay::UpdateThrottlePeriod() void Netplay::HandleTimeSyncEvent(float frame_delta, int update_interval) { + // only activate timesync if its worth correcting. + if (std::abs(frame_delta) < 1.0f) + return; // Distribute the frame difference over the next N * 0.75 frames. // only part of the interval time is used since we want to come back to normal speed. // otherwise we will keep spiraling into unplayable gameplay. @@ -1140,37 +1236,6 @@ void Netplay::GenerateChecksumForFrame(int* checksum, int frame, unsigned char* // Log_VerbosePrintf("Netplay Checksum: f:%d wf:%d c:%u", frame, frame % num_group_of_pages, *checksum); } -void Netplay::GenerateDesyncReport(s32 desync_frame) -{ - std::string path = "\\netplaylogs\\desync_frame_" + std::to_string(desync_frame) + "_p" + - std::to_string(s_local_handle) + "_" + System::GetRunningSerial() + "_.txt"; - std::string filename = EmuFolders::Dumps + path; - - std::unique_ptr stream = - ByteStream::OpenFile(filename.c_str(), BYTESTREAM_OPEN_CREATE | BYTESTREAM_OPEN_WRITE | BYTESTREAM_OPEN_TRUNCATE | - BYTESTREAM_OPEN_ATOMIC_UPDATE | BYTESTREAM_OPEN_STREAMED); - if (!stream) - { - Log_VerbosePrint("desync log creation failed to create stream"); - return; - } - - if (!ByteStream::WriteBinaryToStream(stream.get(), - s_save_buffer_pool.back().get()->state_stream.get()->GetMemoryPointer(), - s_save_buffer_pool.back().get()->state_stream.get()->GetMemorySize())) - { - Log_VerbosePrint("desync log creation failed to write the stream"); - stream->Discard(); - return; - } - /* stream->Write(s_save_buffer_pool.back().get()->state_stream.get()->GetMemoryPointer(), - s_save_buffer_pool.back().get()->state_stream.get()->GetMemorySize());*/ - - stream->Commit(); - - Log_VerbosePrintf("desync log created for frame %d", desync_frame); -} - void Netplay::AdvanceFrame() { ggpo_advance_frame(s_ggpo, 0); @@ -1178,19 +1243,13 @@ void Netplay::AdvanceFrame() void Netplay::RunFrame() { + PollEnet(0); + + if (!s_ggpo) + return; // housekeeping - // TODO: get rid of double polling - PollEnet(0); - if (!s_ggpo) - return; - ggpo_network_idle(s_ggpo); - PollEnet(0); - if (!s_ggpo) - return; - ggpo_idle(s_ggpo); - // run game auto result = GGPO_OK; int disconnect_flags = 0; @@ -1238,7 +1297,36 @@ Netplay::Input Netplay::ReadLocalInput() return inp; } -void Netplay::SendMsg(const char* msg) {} +void Netplay::SendMsg(std::string msg) +{ + SessionChatMessage header{}; + const size_t msg_size = msg.size(); + + header.header.type = SessionMessage::ChatMessage; + header.header.size = sizeof(SessionChatMessage) + msg_size; + header.chat_message_size = msg_size; + + ENetPacket* pkt = enet_packet_create(nullptr, sizeof(header) + msg_size, ENET_PACKET_FLAG_RELIABLE); + std::memcpy(pkt->data, &header, sizeof(header)); + std::memcpy(pkt->data + sizeof(header), msg.c_str(), msg_size); + + for (s32 i = 0; i < MAX_PLAYERS; i++) + { + if (!s_peers[i].peer) + continue; + + const int err = enet_peer_send(s_peers[i].peer, ENET_CHANNEL_SESSION, pkt); + if (err != 0) + { + // failed to send netplay message? just clean it up. + Log_ErrorPrint("Failed to send netplay message"); + enet_packet_destroy(pkt); + } + } + + // add own netplay message locally to netplay messages + Host::OnNetplayMessage(fmt::format("Player {}: {}", s_local_handle, msg)); +} GGPOErrorCode Netplay::SyncInput(Netplay::Input inputs[2], int* disconnect_flags) { @@ -1377,8 +1465,7 @@ bool Netplay::NpAdvFrameCb(void* ctx, int flags) bool Netplay::NpSaveFrameCb(void* ctx, unsigned char** buffer, int* len, int* checksum, int frame) { SaveStateBuffer our_buffer; - // min size is 2 because otherwise the desync logger doesnt have enough time to dump the state. - if (s_save_buffer_pool.size() < 2) + if (s_save_buffer_pool.empty()) { our_buffer = std::make_unique(); } @@ -1465,7 +1552,6 @@ bool Netplay::NpOnEventCb(void* ctx, GGPOEvent* ev) CurrentFrame(), ev->u.desync.nFrameOfDesync, CurrentFrame() - ev->u.desync.nFrameOfDesync, ev->u.desync.ourCheckSum, ev->u.desync.remoteChecksum)); - GenerateDesyncReport(ev->u.desync.nFrameOfDesync); break; default: Host::OnNetplayMessage(fmt::format("Netplay Event Code: {}", static_cast(ev->code))); diff --git a/src/core/netplay.h b/src/core/netplay.h index 7456a748b..513ead53c 100644 --- a/src/core/netplay.h +++ b/src/core/netplay.h @@ -8,7 +8,7 @@ namespace Netplay { enum : s32 { // Maximum number of emulated controllers. - MAX_PLAYERS = 2, + MAX_PLAYERS = 2, // Maximum netplay prediction frames MAX_ROLLBACK_FRAMES = 8, }; @@ -17,6 +17,7 @@ enum : u8 { ENET_CHANNEL_CONTROL = 0, ENET_CHANNEL_GGPO = 1, + ENET_CHANNEL_SESSION = 2, NUM_ENET_CHANNELS, }; @@ -36,7 +37,7 @@ void ExecuteNetplay(); void CollectInput(u32 slot, u32 bind, float value); -void SendMsg(const char* msg); +void SendMsg(std::string msg); s32 GetPing(); u32 GetMaxPrediction(); diff --git a/src/duckstation-qt/qthost.cpp b/src/duckstation-qt/qthost.cpp index 9570a48a9..b8aab71f0 100644 --- a/src/duckstation-qt/qthost.cpp +++ b/src/duckstation-qt/qthost.cpp @@ -1079,12 +1079,6 @@ void EmuThread::startNetplaySession(int local_handle, quint16 local_port, const Q_ARG(quint16, remote_port), Q_ARG(int, input_delay), Q_ARG(const QString&, game_path)); return; } - // disable block linking and disable rewind and runahead during a netplay session - g_settings.cpu_recompiler_block_linking = false; - g_settings.rewind_enable = false; - g_settings.runahead_frames = 0; - - Log_WarningPrintf("Disabling block linking, runahead and rewind due to rollback."); auto remAddr = remote_addr.trimmed().toStdString(); auto gamePath = game_path.trimmed().toStdString(); @@ -1101,7 +1095,8 @@ void EmuThread::sendNetplayMessage(const QString& message) QMetaObject::invokeMethod(this, "sendNetplayMessage", Qt::QueuedConnection, Q_ARG(const QString&, message)); return; } - Netplay::SendMsg(message.toStdString().c_str()); + // TODO REDO NETPLAY UI + // Netplay::SendMsg(message.toStdString().c_str()); } void EmuThread::stopNetplaySession() diff --git a/src/frontend-common/imgui_netplay.cpp b/src/frontend-common/imgui_netplay.cpp index 8560ee2ab..09a4d7ce2 100644 --- a/src/frontend-common/imgui_netplay.cpp +++ b/src/frontend-common/imgui_netplay.cpp @@ -175,8 +175,10 @@ void ImGuiManager::DrawNetplayChatDialog() const bool close_chat = send_message || (s_netplay_chat_message.empty() && (ImGui::IsKeyPressed(ImGuiKey_Backspace)) || ImGui::IsKeyPressed(ImGuiKey_Escape)); + + // sending netplay message if (send_message && !s_netplay_chat_message.empty()) - Netplay::SendMsg(s_netplay_chat_message.c_str()); + Netplay::SendMsg(s_netplay_chat_message); const ImGuiIO& io = ImGui::GetIO(); const ImGuiStyle& style = ImGui::GetStyle(); From c2e75c4daaf52ca39aee60548ea2b0b092dfa7ec Mon Sep 17 00:00:00 2001 From: HeatXD <45072324+HeatXD@users.noreply.github.com> Date: Wed, 10 May 2023 04:11:03 +0200 Subject: [PATCH 4/8] Netplay HandleSessionMessage: fix err msg. --- src/core/netplay.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/netplay.cpp b/src/core/netplay.cpp index 78802ccc4..d32b5deab 100644 --- a/src/core/netplay.cpp +++ b/src/core/netplay.cpp @@ -1063,9 +1063,9 @@ void Netplay::HandleSynchronizeCompleteMessage(s32 player_id, const ENetPacket* void Netplay::HandleSessionMessage(s32 player_id, const ENetPacket* pkt) { - if (pkt->dataLength < sizeof(ControlMessageHeader)) + if (pkt->dataLength < sizeof(SessionMessageHeader)) { - Log_ErrorPrintf("Invalid control packet from player %d of size %zu", player_id, pkt->dataLength); + Log_ErrorPrintf("Invalid session packet from player %d of size %zu", player_id, pkt->dataLength); return; } From 8a7cbf2f669d5706cf88653cc4b0b37ed1d803f4 Mon Sep 17 00:00:00 2001 From: HeatXD <45072324+HeatXD@users.noreply.github.com> Date: Wed, 10 May 2023 05:01:55 +0200 Subject: [PATCH 5/8] Netplay / Qt: remove ping widget from mainwindow. --- src/core/netplay.cpp | 12 +++++++----- src/duckstation-qt/mainwindow.cpp | 5 ----- src/duckstation-qt/mainwindow.h | 2 -- src/duckstation-qt/qthost.cpp | 8 -------- 4 files changed, 7 insertions(+), 20 deletions(-) diff --git a/src/core/netplay.cpp b/src/core/netplay.cpp index d32b5deab..b6a802580 100644 --- a/src/core/netplay.cpp +++ b/src/core/netplay.cpp @@ -523,10 +523,6 @@ void Netplay::HandleEnetEvent(const ENetEvent* event) { HandleControlMessage(player_id, event->packet); } - else if (event->channelID == ENET_CHANNEL_SESSION) - { - HandleSessionMessage(player_id, event->packet); - } else if (event->channelID == ENET_CHANNEL_GGPO) { Log_TracePrintf("Received %zu ggpo bytes from player %d", event->packet->dataLength, player_id); @@ -534,6 +530,10 @@ void Netplay::HandleEnetEvent(const ENetEvent* event) if (rc != GGPO_OK) Log_ErrorPrintf("Failed to process GGPO packet!"); } + else if (event->channelID == ENET_CHANNEL_SESSION) + { + HandleSessionMessage(player_id, event->packet); + } else { Log_ErrorPrintf("Unexpected packet channel %u", event->channelID); @@ -1095,7 +1095,7 @@ void Netplay::HandleSessionChatMessage(s32 player_id, const ENetPacket* pkt) std::string message(pkt->data + sizeof(SessionChatMessage), pkt->data + sizeof(SessionChatMessage) + msg->chat_message_size); - Host::OnNetplayMessage(fmt::format("Player {}: {}", player_id + 1, message)); + Host::OnNetplayMessage(fmt::format("Player {}: {}", PlayerIdToGGPOHandle(player_id), message)); } void Netplay::CheckForCompleteResynchronize() @@ -1132,6 +1132,8 @@ void Netplay::SetSettings() // no block linking, it degrades savestate loading performance si.SetBoolValue("CPU", "RecompilerBlockLinking", false); + // not sure its needed but enabled for now... TODO + si.SetBoolValue("GPU", "UseSoftwareRendererForReadbacks", true); Host::Internal::SetNetplaySettingsLayer(&si); System::ApplySettings(false); diff --git a/src/duckstation-qt/mainwindow.cpp b/src/duckstation-qt/mainwindow.cpp index 5d7d61cf4..5ae89e482 100644 --- a/src/duckstation-qt/mainwindow.cpp +++ b/src/duckstation-qt/mainwindow.cpp @@ -1568,10 +1568,6 @@ void MainWindow::setupAdditionalUi() m_status_vps_widget->setFixedSize(120, 16); m_status_vps_widget->hide(); - m_status_ping_widget = new QLabel(m_ui.statusBar); - m_status_ping_widget->setFixedSize(110, 16); - m_status_ping_widget->hide(); - m_settings_toolbar_menu = new QMenu(m_ui.toolBar); m_settings_toolbar_menu->addAction(m_ui.actionSettings); m_settings_toolbar_menu->addAction(m_ui.actionViewGameProperties); @@ -1777,7 +1773,6 @@ void MainWindow::updateStatusBarWidgetVisibility() Update(m_status_resolution_widget, s_system_valid && !s_system_paused, 0); Update(m_status_fps_widget, s_system_valid && !s_system_paused, 0); Update(m_status_vps_widget, s_system_valid && !s_system_paused, 0); - Update(m_status_ping_widget, s_system_valid && !s_system_paused && m_netplay_window != nullptr, 0); } void MainWindow::updateWindowTitle() diff --git a/src/duckstation-qt/mainwindow.h b/src/duckstation-qt/mainwindow.h index 6aa3ecc0c..20ce67b66 100644 --- a/src/duckstation-qt/mainwindow.h +++ b/src/duckstation-qt/mainwindow.h @@ -90,7 +90,6 @@ public: ALWAYS_INLINE QLabel* getStatusResolutionWidget() const { return m_status_resolution_widget; } ALWAYS_INLINE QLabel* getStatusFPSWidget() const { return m_status_fps_widget; } ALWAYS_INLINE QLabel* getStatusVPSWidget() const { return m_status_vps_widget; } - ALWAYS_INLINE QLabel* getStatusPingWidget() const { return m_status_ping_widget; } public Q_SLOTS: /// Updates debug menu visibility (hides if disabled). @@ -266,7 +265,6 @@ private: QLabel* m_status_renderer_widget = nullptr; QLabel* m_status_fps_widget = nullptr; QLabel* m_status_vps_widget = nullptr; - QLabel* m_status_ping_widget = nullptr; QLabel* m_status_resolution_widget = nullptr; QMenu* m_settings_toolbar_menu = nullptr; diff --git a/src/duckstation-qt/qthost.cpp b/src/duckstation-qt/qthost.cpp index b8aab71f0..c68aea10b 100644 --- a/src/duckstation-qt/qthost.cpp +++ b/src/duckstation-qt/qthost.cpp @@ -1709,14 +1709,6 @@ void EmuThread::updatePerformanceCounters() m_last_speed = speed; m_last_video_fps = vfps; } - - const s32 ping = Netplay::GetPing(); - if (m_last_ping != ping) - { - QMetaObject::invokeMethod(g_main_window->getStatusPingWidget(), "setText", Qt::QueuedConnection, - Q_ARG(const QString&, tr("Netplay Ping: %1 ").arg(ping, 0, 'f', 0))); - m_last_ping = ping; - } } void EmuThread::resetPerformanceCounters() From 91e7caab16b8fd5a55cdc737a45560ef15933342 Mon Sep 17 00:00:00 2001 From: Jamie Meyer <45072324+HeatXD@users.noreply.github.com> Date: Wed, 10 May 2023 11:32:03 +0200 Subject: [PATCH 6/8] Netplay: Move chat back to the control channel --- src/core/netplay.cpp | 99 ++++++++------------------------------------ 1 file changed, 18 insertions(+), 81 deletions(-) diff --git a/src/core/netplay.cpp b/src/core/netplay.cpp index b6a802580..531e429b9 100644 --- a/src/core/netplay.cpp +++ b/src/core/netplay.cpp @@ -46,10 +46,6 @@ enum class ControlMessage : u32 ConnectResponse, SynchronizeSession, SynchronizeComplete, -}; - -enum class SessionMessage : u32 -{ ChatMessage, }; @@ -60,13 +56,6 @@ struct ControlMessageHeader u32 size; }; -#pragma pack(push, 1) -struct SessionMessageHeader -{ - SessionMessage type; - u32 size; -}; - struct ControlConnectResponseMessage { enum class Result : u32 @@ -107,13 +96,13 @@ struct ControlSynchronizeCompleteMessage static ControlMessage MessageType() { return ControlMessage::SynchronizeComplete; } }; -struct SessionChatMessage +struct ControlChatMessage { - SessionMessageHeader header; + ControlMessageHeader header; u32 chat_message_size; - static SessionMessage MessageType() { return SessionMessage::ChatMessage; } + static ControlMessage MessageType() { return ControlMessage::ChatMessage; } }; #pragma pack(pop) @@ -162,10 +151,7 @@ static void HandleControlMessage(s32 player_id, const ENetPacket* pkt); static void HandleConnectResponseMessage(s32 player_id, const ENetPacket* pkt); static void HandleSynchronizeSessionMessage(s32 player_id, const ENetPacket* pkt); static void HandleSynchronizeCompleteMessage(s32 player_id, const ENetPacket* pkt); - -// Sessionpackets -static void HandleSessionMessage(s32 player_id, const ENetPacket* pkt); -static void HandleSessionChatMessage(s32 player_id, const ENetPacket* pkt); +static void HandleControlChatMessage(s32 player_id, const ENetPacket* pkt); // l = local, r = remote static bool CreateGGPOSession(); @@ -276,34 +262,6 @@ static bool SendControlPacket(s32 player_id, const PacketWrapper& pkt) DebugAssert(player_id >= 0 && player_id < MAX_PLAYERS && s_peers[player_id].peer); return SendControlPacket(s_peers[player_id].peer, pkt); } -template -static PacketWrapper NewSessionPacket(u32 size = sizeof(T), u32 flags = ENET_PACKET_FLAG_RELIABLE) -{ - PacketWrapper ret = NewWrappedPacket(size, flags); - SessionMessageHeader* hdr = reinterpret_cast(ret.pkt->data); - hdr->type = T::MessageType(); - hdr->size = size; - return ret; -} -template -static bool SendSessionPacket(ENetPeer* peer, const PacketWrapper& pkt) -{ - const int rc = enet_peer_send(peer, ENET_CHANNEL_SESSION, pkt.pkt); - if (rc != 0) - { - Log_ErrorPrintf("enet_peer_send() failed: %d", rc); - enet_packet_destroy(pkt.pkt); - return false; - } - - return true; -} -template -static bool SendSessionPacket(s32 player_id, const PacketWrapper& pkt) -{ - DebugAssert(player_id >= 0 && player_id < MAX_PLAYERS && s_peers[player_id].peer); - return SendSessionPacket(s_peers[player_id].peer, pkt); -} } // namespace Netplay // Netplay Impl @@ -530,10 +488,6 @@ void Netplay::HandleEnetEvent(const ENetEvent* event) if (rc != GGPO_OK) Log_ErrorPrintf("Failed to process GGPO packet!"); } - else if (event->channelID == ENET_CHANNEL_SESSION) - { - HandleSessionMessage(player_id, event->packet); - } else { Log_ErrorPrintf("Unexpected packet channel %u", event->channelID); @@ -751,6 +705,10 @@ void Netplay::HandleControlMessage(s32 player_id, const ENetPacket* pkt) HandleSynchronizeCompleteMessage(player_id, pkt); break; + case ControlMessage::ChatMessage: + HandleControlChatMessage(player_id, pkt); + break; + default: Log_ErrorPrintf("Unhandled control packet %u from player %d of size %zu", hdr->type, player_id, pkt->dataLength); break; @@ -1061,39 +1019,18 @@ void Netplay::HandleSynchronizeCompleteMessage(s32 player_id, const ENetPacket* CheckForCompleteResynchronize(); } -void Netplay::HandleSessionMessage(s32 player_id, const ENetPacket* pkt) +void Netplay::HandleControlChatMessage(s32 player_id, const ENetPacket* pkt) { - if (pkt->dataLength < sizeof(SessionMessageHeader)) - { - Log_ErrorPrintf("Invalid session packet from player %d of size %zu", player_id, pkt->dataLength); - return; - } - - const SessionMessageHeader* hdr = reinterpret_cast(pkt->data); - switch (hdr->type) - { - case SessionMessage::ChatMessage: - HandleSessionChatMessage(player_id, pkt); - break; - - default: - Log_ErrorPrintf("Unhandled session packet %u from player %d of size %zu", hdr->type, player_id, pkt->dataLength); - break; - } -} - -void Netplay::HandleSessionChatMessage(s32 player_id, const ENetPacket* pkt) -{ - const SessionChatMessage* msg = reinterpret_cast(pkt->data); - if (pkt->dataLength < sizeof(SessionChatMessage) || - pkt->dataLength < (sizeof(SessionChatMessage) + msg->chat_message_size)) + const ControlChatMessage* msg = reinterpret_cast(pkt->data); + if (pkt->dataLength < sizeof(ControlChatMessage) || + pkt->dataLength < (sizeof(ControlChatMessage) + msg->chat_message_size)) { // invalid chat message. ignore. return; } - std::string message(pkt->data + sizeof(SessionChatMessage), - pkt->data + sizeof(SessionChatMessage) + msg->chat_message_size); + std::string message(pkt->data + sizeof(ControlChatMessage), + pkt->data + sizeof(ControlChatMessage) + msg->chat_message_size); Host::OnNetplayMessage(fmt::format("Player {}: {}", PlayerIdToGGPOHandle(player_id), message)); } @@ -1301,11 +1238,11 @@ Netplay::Input Netplay::ReadLocalInput() void Netplay::SendMsg(std::string msg) { - SessionChatMessage header{}; + ControlChatMessage header{}; const size_t msg_size = msg.size(); - header.header.type = SessionMessage::ChatMessage; - header.header.size = sizeof(SessionChatMessage) + msg_size; + header.header.type = ControlMessage::ChatMessage; + header.header.size = sizeof(ControlChatMessage) + msg_size; header.chat_message_size = msg_size; ENetPacket* pkt = enet_packet_create(nullptr, sizeof(header) + msg_size, ENET_PACKET_FLAG_RELIABLE); @@ -1317,7 +1254,7 @@ void Netplay::SendMsg(std::string msg) if (!s_peers[i].peer) continue; - const int err = enet_peer_send(s_peers[i].peer, ENET_CHANNEL_SESSION, pkt); + const int err = enet_peer_send(s_peers[i].peer, ENET_CHANNEL_CONTROL, pkt); if (err != 0) { // failed to send netplay message? just clean it up. From 59bcb05bd02879d7c3d3575f666ed77291599dc7 Mon Sep 17 00:00:00 2001 From: Jamie Meyer <45072324+HeatXD@users.noreply.github.com> Date: Wed, 10 May 2023 11:32:25 +0200 Subject: [PATCH 7/8] Update netplay.h --- src/core/netplay.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/core/netplay.h b/src/core/netplay.h index 513ead53c..985d5cd41 100644 --- a/src/core/netplay.h +++ b/src/core/netplay.h @@ -17,7 +17,6 @@ enum : u8 { ENET_CHANNEL_CONTROL = 0, ENET_CHANNEL_GGPO = 1, - ENET_CHANNEL_SESSION = 2, NUM_ENET_CHANNELS, }; From a876a8df5543327e5135b880b1daa72991f6ad0e Mon Sep 17 00:00:00 2001 From: Jamie Meyer <45072324+HeatXD@users.noreply.github.com> Date: Wed, 10 May 2023 13:25:18 +0200 Subject: [PATCH 8/8] Netplay: Only load netplay savestate whenever you are the host. the hostwill share state to the other peers. --- src/core/netplay.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/core/netplay.cpp b/src/core/netplay.cpp index 531e429b9..aa9728b17 100644 --- a/src/core/netplay.cpp +++ b/src/core/netplay.cpp @@ -1315,9 +1315,10 @@ void Netplay::StartNetplaySession(s32 local_handle, u16 local_port, std::string& Log_ErrorPrint("Failed to Create Netplay Session!"); System::ShutdownSystem(false); } - else + else if (IsHost()) { - // Load savestate if available + // Load savestate if available and only when you are the host. + // the other peers will get state from the host std::string save = EmuFolders::SaveStates + "/netplay/" + System::GetRunningSerial() + ".sav"; System::LoadState(save.c_str()); }