Netplay: OSD for messages/chat
This commit is contained in:
parent
e214fa3a44
commit
e1f97277b2
|
@ -543,62 +543,46 @@ void Netplay::NpFreeBuffCb(void* ctx, void* buffer, int frame)
|
|||
|
||||
bool Netplay::NpOnEventCb(void* ctx, GGPOEvent* ev)
|
||||
{
|
||||
char buff[128];
|
||||
std::string msg, filename;
|
||||
switch (ev->code)
|
||||
{
|
||||
case GGPOEventCode::GGPO_EVENTCODE_CONNECTED_TO_PEER:
|
||||
sprintf(buff, "Netplay Connected To Player: %d", ev->u.connected.player);
|
||||
msg = buff;
|
||||
Host::OnNetplayMessage(fmt::format("Netplay Connected To Player: {}", ev->u.connected.player));
|
||||
break;
|
||||
case GGPOEventCode::GGPO_EVENTCODE_SYNCHRONIZING_WITH_PEER:
|
||||
sprintf(buff, "Netplay Synchronzing: %d/%d", ev->u.synchronizing.count, ev->u.synchronizing.total);
|
||||
msg = buff;
|
||||
Host::OnNetplayMessage(fmt::format("Netplay Synchronzing: {}/{}", ev->u.synchronizing.count, ev->u.synchronizing.total));
|
||||
break;
|
||||
case GGPOEventCode::GGPO_EVENTCODE_SYNCHRONIZED_WITH_PEER:
|
||||
sprintf(buff, "Netplay Synchronized With Player: %d", ev->u.synchronized.player);
|
||||
msg = buff;
|
||||
Host::OnNetplayMessage(fmt::format("Netplay Synchronized With Player: {}", ev->u.synchronized.player));
|
||||
break;
|
||||
case GGPOEventCode::GGPO_EVENTCODE_DISCONNECTED_FROM_PEER:
|
||||
sprintf(buff, "Netplay Player: %d Disconnected", ev->u.disconnected.player);
|
||||
msg = buff;
|
||||
Host::OnNetplayMessage(fmt::format("Netplay Player: %d Disconnected", ev->u.disconnected.player));
|
||||
break;
|
||||
case GGPOEventCode::GGPO_EVENTCODE_RUNNING:
|
||||
msg = "Netplay Is Running";
|
||||
Host::OnNetplayMessage("Netplay Is Running");
|
||||
break;
|
||||
case GGPOEventCode::GGPO_EVENTCODE_CONNECTION_INTERRUPTED:
|
||||
sprintf(buff, "Netplay Player: %d Connection Interupted, Timeout: %d", ev->u.connection_interrupted.player,
|
||||
ev->u.connection_interrupted.disconnect_timeout);
|
||||
msg = buff;
|
||||
Host::OnNetplayMessage(fmt::format("Netplay Player: {} Connection Interupted, Timeout: {}", ev->u.connection_interrupted.player,
|
||||
ev->u.connection_interrupted.disconnect_timeout));
|
||||
break;
|
||||
case GGPOEventCode::GGPO_EVENTCODE_CONNECTION_RESUMED:
|
||||
sprintf(buff, "Netplay Player: %d Connection Resumed", ev->u.connection_resumed.player);
|
||||
msg = buff;
|
||||
Host::OnNetplayMessage(fmt::format("Netplay Player: {} Connection Resumed", ev->u.connection_resumed.player));
|
||||
break;
|
||||
case GGPOEventCode::GGPO_EVENTCODE_CHAT:
|
||||
sprintf(buff, "%s", ev->u.chat.msg);
|
||||
msg = buff;
|
||||
Host::OnNetplayMessage(ev->u.chat.msg);
|
||||
break;
|
||||
case GGPOEventCode::GGPO_EVENTCODE_TIMESYNC:
|
||||
HandleTimeSyncEvent(ev->u.timesync.frames_ahead, ev->u.timesync.timeSyncPeriodInFrames);
|
||||
break;
|
||||
case GGPOEventCode::GGPO_EVENTCODE_DESYNC:
|
||||
sprintf(buff, "Desync Detected: Current Frame: %d, Desync Frame: %d, Diff: %d, L:%u, R:%u", CurrentFrame(),
|
||||
Host::OnNetplayMessage(fmt::format("Desync Detected: Current Frame: {}, Desync Frame: {}, Diff: {}, L:{}, R:{}", CurrentFrame(),
|
||||
ev->u.desync.nFrameOfDesync, CurrentFrame() - ev->u.desync.nFrameOfDesync, ev->u.desync.ourCheckSum,
|
||||
ev->u.desync.remoteChecksum);
|
||||
msg = buff;
|
||||
ev->u.desync.remoteChecksum));
|
||||
GenerateDesyncReport(ev->u.desync.nFrameOfDesync);
|
||||
Host::AddKeyedOSDMessage("Netplay", msg, 5);
|
||||
|
||||
return true;
|
||||
break;
|
||||
default:
|
||||
sprintf(buff, "Netplay Event Code: %d", ev->code);
|
||||
msg = buff;
|
||||
}
|
||||
if (!msg.empty())
|
||||
{
|
||||
Host::OnNetplayMessage(msg);
|
||||
Log_InfoPrintf("%s", msg.c_str());
|
||||
Host::OnNetplayMessage(fmt::format("Netplay Event Code: {}", static_cast<int>(ev->code)));
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -512,5 +512,5 @@ bool IsFullscreen();
|
|||
/// Alters fullscreen state of hosting application.
|
||||
void SetFullscreen(bool enabled);
|
||||
// netplay
|
||||
void OnNetplayMessage(std::string& message);
|
||||
void OnNetplayMessage(std::string message);
|
||||
} // namespace Host
|
||||
|
|
|
@ -38,8 +38,6 @@ void NetplayWidget::FillGameList()
|
|||
|
||||
void NetplayWidget::SetupConnections()
|
||||
{
|
||||
// connect netplay window messages
|
||||
connect(g_emu_thread, &EmuThread::onNetplayMessage, this, &NetplayWidget::OnMsgReceived);
|
||||
// connect sending messages when the chat button has been pressed
|
||||
connect(m_ui->btnSendMsg, &QPushButton::pressed, [this]() {
|
||||
// check if message aint empty and the complete message ( message + name + ":" + space) is below 120 characters
|
||||
|
|
|
@ -465,12 +465,6 @@ void EmuThread::startFullscreenUI()
|
|||
wakeThread();
|
||||
}
|
||||
|
||||
void Host::OnNetplayMessage(std::string& message)
|
||||
{
|
||||
QString msg(message.c_str());
|
||||
emit g_emu_thread->onNetplayMessage(msg);
|
||||
}
|
||||
|
||||
void EmuThread::stopFullscreenUI()
|
||||
{
|
||||
if (!isOnThread())
|
||||
|
@ -1513,6 +1507,8 @@ void EmuThread::renderDisplay(bool skip_present)
|
|||
if (!skip_present)
|
||||
{
|
||||
FullscreenUI::Render();
|
||||
if (Netplay::IsActive())
|
||||
ImGuiManager::RenderNetplayOverlays();
|
||||
ImGuiManager::RenderTextOverlays();
|
||||
ImGuiManager::RenderOSDMessages();
|
||||
}
|
||||
|
|
|
@ -143,7 +143,6 @@ Q_SIGNALS:
|
|||
void achievementsRefreshed(quint32 id, const QString& game_info_string, quint32 total, quint32 points);
|
||||
void achievementsChallengeModeChanged();
|
||||
void cheatEnabled(quint32 index, bool enabled);
|
||||
void onNetplayMessage(const QString& message);
|
||||
|
||||
public Q_SLOTS:
|
||||
void setDefaultSettings(bool system = true, bool controller = true);
|
||||
|
|
|
@ -16,6 +16,7 @@ add_library(frontend-common
|
|||
imgui_fullscreen.h
|
||||
imgui_manager.cpp
|
||||
imgui_manager.h
|
||||
imgui_netplay.cpp
|
||||
imgui_overlays.cpp
|
||||
imgui_overlays.h
|
||||
platform_misc.h
|
||||
|
|
|
@ -677,6 +677,11 @@ DEFINE_HOTKEY("OpenPauseMenu", TRANSLATABLE("Hotkeys", "General"), TRANSLATABLE(
|
|||
[](s32 pressed) {
|
||||
if (!pressed)
|
||||
FullscreenUI::OpenPauseMenu();
|
||||
})
|
||||
DEFINE_HOTKEY("OpenNetplayChat", TRANSLATABLE("Hotkeys", "General"), TRANSLATABLE("Hotkeys", "Open Netplay Chat"),
|
||||
[](s32 pressed) {
|
||||
if (!pressed)
|
||||
ImGuiManager::OpenNetplayChat();
|
||||
})
|
||||
#endif
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
<ExcludedFromBuild Condition="'$(Platform)'=='ARM64'">true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="imgui_manager.cpp" />
|
||||
<ClCompile Include="imgui_netplay.cpp" />
|
||||
<ClCompile Include="imgui_overlays.cpp" />
|
||||
<ClCompile Include="input_manager.cpp" />
|
||||
<ClCompile Include="input_source.cpp" />
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
<ClCompile Include="dinput_source.cpp" />
|
||||
<ClCompile Include="imgui_overlays.cpp" />
|
||||
<ClCompile Include="platform_misc_win32.cpp" />
|
||||
<ClCompile Include="imgui_netplay.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="icon.h" />
|
||||
|
|
|
@ -0,0 +1,218 @@
|
|||
// SPDX-FileCopyrightText: 2023 Connor McLaughlin <stenzek@gmail.com>
|
||||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
||||
|
||||
#define IMGUI_DEFINE_MATH_OPERATORS
|
||||
|
||||
#include "IconsFontAwesome5.h"
|
||||
#include "common/align.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/file_system.h"
|
||||
#include "common/log.h"
|
||||
#include "common/string_util.h"
|
||||
#include "common/timer.h"
|
||||
#include "common_host.h"
|
||||
#include "core/controller.h"
|
||||
#include "core/gpu.h"
|
||||
#include "core/host.h"
|
||||
#include "core/host_display.h"
|
||||
#include "core/host_settings.h"
|
||||
#include "core/netplay.h"
|
||||
#include "core/settings.h"
|
||||
#include "core/spu.h"
|
||||
#include "core/system.h"
|
||||
#include "fmt/chrono.h"
|
||||
#include "fmt/format.h"
|
||||
#include "fullscreen_ui.h"
|
||||
#include "gsl/span"
|
||||
#include "icon.h"
|
||||
#include "imgui.h"
|
||||
#include "imgui_fullscreen.h"
|
||||
#include "imgui_internal.h"
|
||||
#include "imgui_manager.h"
|
||||
#include "imgui_overlays.h"
|
||||
#include "imgui_stdlib.h"
|
||||
#include "input_manager.h"
|
||||
#include "util/audio_stream.h"
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
#include <deque>
|
||||
#include <mutex>
|
||||
#include <unordered_map>
|
||||
|
||||
#if defined(CPU_X64)
|
||||
#include <emmintrin.h>
|
||||
#elif defined(CPU_AARCH64)
|
||||
#ifdef _MSC_VER
|
||||
#include <arm64_neon.h>
|
||||
#else
|
||||
#include <arm_neon.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
Log_SetChannel(ImGuiManager);
|
||||
|
||||
namespace ImGuiManager {
|
||||
static void DrawNetplayMessages();
|
||||
static void DrawNetplayStats();
|
||||
static void DrawNetplayChatDialog();
|
||||
} // namespace ImGuiManager
|
||||
|
||||
static std::deque<std::pair<std::string, Common::Timer::Value>> s_netplay_messages;
|
||||
static constexpr u32 MAX_NETPLAY_MESSAGES = 15;
|
||||
static constexpr float NETPLAY_MESSAGE_DURATION = 15.0f;
|
||||
static constexpr float NETPLAY_MESSAGE_FADE_TIME = 2.0f;
|
||||
static bool s_netplay_chat_dialog_open = false;
|
||||
static bool s_netplay_chat_dialog_opening = false;
|
||||
static std::string s_netplay_chat_message;
|
||||
|
||||
void Host::OnNetplayMessage(std::string message)
|
||||
{
|
||||
Log_InfoPrintf("Netplay: %s", message.c_str());
|
||||
|
||||
while (s_netplay_messages.size() >= MAX_NETPLAY_MESSAGES)
|
||||
s_netplay_messages.pop_front();
|
||||
|
||||
s_netplay_messages.emplace_back(std::move(message), Common::Timer::GetCurrentValue() +
|
||||
Common::Timer::ConvertSecondsToValue(NETPLAY_MESSAGE_DURATION));
|
||||
}
|
||||
|
||||
void ImGuiManager::RenderNetplayOverlays()
|
||||
{
|
||||
DrawNetplayMessages();
|
||||
DrawNetplayStats();
|
||||
DrawNetplayChatDialog();
|
||||
}
|
||||
|
||||
void ImGuiManager::DrawNetplayMessages()
|
||||
{
|
||||
if (s_netplay_messages.empty())
|
||||
return;
|
||||
|
||||
const Common::Timer::Value ticks = Common::Timer::GetCurrentValue();
|
||||
const ImGuiIO& io = ImGui::GetIO();
|
||||
const float scale = ImGuiManager::GetGlobalScale();
|
||||
const float shadow_offset = 1.0f * scale;
|
||||
const float margin = 10.0f * scale;
|
||||
const float spacing = 5.0f * scale;
|
||||
const float msg_spacing = 2.0f * scale;
|
||||
ImFont* font = ImGuiManager::GetFixedFont();
|
||||
float position_y = io.DisplaySize.y - margin - ((spacing + font->FontSize) * 2.0f) - font->FontSize;
|
||||
ImDrawList* dl = ImGui::GetBackgroundDrawList();
|
||||
|
||||
// drop expired messages.. because of the reverse iteration below, we can't do it in there :/
|
||||
for (auto iter = s_netplay_messages.begin(); iter != s_netplay_messages.end();)
|
||||
{
|
||||
if (ticks >= iter->second)
|
||||
iter = s_netplay_messages.erase(iter);
|
||||
else
|
||||
++iter;
|
||||
}
|
||||
|
||||
for (auto iter = s_netplay_messages.rbegin(); iter != s_netplay_messages.rend(); ++iter)
|
||||
{
|
||||
const float remainder = static_cast<float>(Common::Timer::ConvertValueToSeconds(iter->second - ticks));
|
||||
const float opacity = std::min(remainder / NETPLAY_MESSAGE_FADE_TIME, 1.0f);
|
||||
const u32 alpha = static_cast<u32>(opacity * 255.0f);
|
||||
const u32 shadow_alpha = static_cast<u32>(opacity * 100.0f);
|
||||
|
||||
// TODO: line wrapping..
|
||||
const char* text_start = iter->first.c_str();
|
||||
const char* text_end = text_start + iter->first.length();
|
||||
const ImVec2 text_size = font->CalcTextSizeA(font->FontSize, io.DisplaySize.x, 0.0f, text_start, text_end, nullptr);
|
||||
|
||||
dl->AddText(font, font->FontSize,
|
||||
ImVec2(ImGui::GetIO().DisplaySize.x - margin - text_size.x + shadow_offset, position_y + shadow_offset),
|
||||
IM_COL32(0, 0, 0, shadow_alpha), text_start, text_end);
|
||||
dl->AddText(font, font->FontSize, ImVec2(ImGui::GetIO().DisplaySize.x - margin - text_size.x, position_y),
|
||||
IM_COL32(255, 255, 255, alpha), text_start, text_end);
|
||||
|
||||
position_y -= text_size.y + msg_spacing;
|
||||
}
|
||||
}
|
||||
|
||||
void ImGuiManager::DrawNetplayStats()
|
||||
{
|
||||
// Not much yet.. eventually we'll render chat and such here too.
|
||||
// We'll probably want to draw a graph too..
|
||||
|
||||
LargeString text;
|
||||
text.AppendFmtString("Ping: {}", Netplay::GetPing());
|
||||
|
||||
const float scale = ImGuiManager::GetGlobalScale();
|
||||
const float shadow_offset = 1.0f * scale;
|
||||
const float margin = 10.0f * scale;
|
||||
const float spacing = 5.0f * scale;
|
||||
ImFont* font = ImGuiManager::GetFixedFont();
|
||||
const float position_y = ImGui::GetIO().DisplaySize.y - margin - font->FontSize - spacing - font->FontSize;
|
||||
|
||||
ImDrawList* dl = ImGui::GetBackgroundDrawList();
|
||||
ImVec2 text_size = font->CalcTextSizeA(font->FontSize, std::numeric_limits<float>::max(), -1.0f, text,
|
||||
text.GetCharArray() + text.GetLength(), nullptr);
|
||||
dl->AddText(font, font->FontSize,
|
||||
ImVec2(ImGui::GetIO().DisplaySize.x - margin - text_size.x + shadow_offset, position_y + shadow_offset),
|
||||
IM_COL32(0, 0, 0, 100), text, text.GetCharArray() + text.GetLength());
|
||||
dl->AddText(font, font->FontSize, ImVec2(ImGui::GetIO().DisplaySize.x - margin - text_size.x, position_y),
|
||||
IM_COL32(255, 255, 255, 255), text, text.GetCharArray() + text.GetLength());
|
||||
}
|
||||
|
||||
void ImGuiManager::DrawNetplayChatDialog()
|
||||
{
|
||||
// TODO: This needs to block controller input...
|
||||
|
||||
if (s_netplay_chat_dialog_opening)
|
||||
{
|
||||
ImGui::OpenPopup("Netplay Chat");
|
||||
s_netplay_chat_dialog_open = true;
|
||||
s_netplay_chat_dialog_opening = false;
|
||||
}
|
||||
else if (!s_netplay_chat_dialog_open)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const bool send_message = ImGui::IsKeyPressed(ImGuiKey_Enter);
|
||||
const bool close_chat =
|
||||
send_message || (s_netplay_chat_message.empty() && (ImGui::IsKeyPressed(ImGuiKey_Backspace)) ||
|
||||
ImGui::IsKeyPressed(ImGuiKey_Escape));
|
||||
if (send_message && !s_netplay_chat_message.empty())
|
||||
Netplay::SendMsg(s_netplay_chat_message.c_str());
|
||||
|
||||
const ImGuiIO& io = ImGui::GetIO();
|
||||
const ImGuiStyle& style = ImGui::GetStyle();
|
||||
const float scale = ImGuiManager::GetGlobalScale();
|
||||
const float width = 600.0f * scale;
|
||||
const float height = 60.0f * scale;
|
||||
|
||||
ImGui::SetNextWindowSize(ImVec2(width, height));
|
||||
ImGui::SetNextWindowPos(io.DisplaySize * 0.5f, ImGuiCond_Always, ImVec2(0.5f, 0.5f));
|
||||
ImGui::SetNextWindowFocus();
|
||||
|
||||
if (ImGui::BeginPopupModal("Netplay Chat", nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize))
|
||||
{
|
||||
ImGui::SetNextItemWidth(width - style.WindowPadding.x * 2.0f);
|
||||
ImGui::SetKeyboardFocusHere();
|
||||
ImGui::InputText("##chatmsg", &s_netplay_chat_message);
|
||||
|
||||
if (!s_netplay_chat_dialog_open)
|
||||
ImGui::CloseCurrentPopup();
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
if (close_chat)
|
||||
{
|
||||
s_netplay_chat_message.clear();
|
||||
s_netplay_chat_dialog_open = false;
|
||||
}
|
||||
|
||||
s_netplay_chat_dialog_opening = false;
|
||||
}
|
||||
|
||||
void ImGuiManager::OpenNetplayChat()
|
||||
{
|
||||
if (s_netplay_chat_dialog_open)
|
||||
return;
|
||||
|
||||
s_netplay_chat_dialog_opening = true;
|
||||
}
|
|
@ -8,6 +8,9 @@
|
|||
namespace ImGuiManager {
|
||||
void RenderTextOverlays();
|
||||
void RenderOverlayWindows();
|
||||
|
||||
void RenderNetplayOverlays();
|
||||
void OpenNetplayChat();
|
||||
}
|
||||
|
||||
namespace SaveStateSelectorUI {
|
||||
|
|
Loading…
Reference in New Issue