Netplay: Improve disconnection handling
This commit is contained in:
parent
5de071900d
commit
a7e787456c
|
@ -27,3 +27,6 @@
|
|||
#if defined(DeleteFile)
|
||||
#undef DeleteFile
|
||||
#endif
|
||||
#if defined(GetMessage)
|
||||
#undef GetMessage
|
||||
#endif
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -9,8 +9,15 @@ enum : s32
|
|||
{
|
||||
// Maximum number of emulated controllers.
|
||||
MAX_PLAYERS = 2,
|
||||
|
||||
// Maximum netplay prediction frames
|
||||
MAX_ROLLBACK_FRAMES = 8,
|
||||
|
||||
// Maximum length of a nickname
|
||||
MAX_NICKNAME_LENGTH = 128,
|
||||
|
||||
// Maximum name of password for session
|
||||
MAX_SESSION_PASSWORD_LENGTH = 128,
|
||||
};
|
||||
|
||||
enum : u8
|
||||
|
@ -29,14 +36,14 @@ bool IsActive();
|
|||
|
||||
/// Frees up resources associated with the current netplay session.
|
||||
/// Should only be called by System::ShutdownSystem().
|
||||
void CloseSession();
|
||||
void SystemDestroyed();
|
||||
|
||||
/// Runs the VM and netplay loop. when the netplay loop cancels it switches to normal execute mode.
|
||||
void ExecuteNetplay();
|
||||
|
||||
void CollectInput(u32 slot, u32 bind, float value);
|
||||
|
||||
void SendMsg(std::string msg);
|
||||
void SendChatMessage(const std::string_view& msg);
|
||||
|
||||
s32 GetPing();
|
||||
u32 GetMaxPrediction();
|
||||
|
|
|
@ -0,0 +1,241 @@
|
|||
// SPDX-FileCopyrightText: 2023 Connor McLaughlin <stenzek@gmail.com> and contributors.
|
||||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "host.h"
|
||||
#include "types.h"
|
||||
|
||||
#include "fmt/format.h"
|
||||
|
||||
namespace Netplay {
|
||||
|
||||
enum class SessionState
|
||||
{
|
||||
Inactive,
|
||||
Initializing,
|
||||
Connecting,
|
||||
Resetting,
|
||||
Running,
|
||||
ClosingSession,
|
||||
};
|
||||
|
||||
enum class ControlMessage : u32
|
||||
{
|
||||
// host->player
|
||||
ConnectResponse,
|
||||
Reset,
|
||||
ResumeSession,
|
||||
PlayerJoined,
|
||||
DropPlayer,
|
||||
CloseSession,
|
||||
|
||||
// player->host
|
||||
ConnectRequest,
|
||||
ResetComplete,
|
||||
ResetRequest,
|
||||
|
||||
// bi-directional
|
||||
SetNickname,
|
||||
ChatMessage,
|
||||
};
|
||||
|
||||
enum class DropPlayerReason : u32
|
||||
{
|
||||
ConnectTimeout,
|
||||
DisconnectedFromHost,
|
||||
};
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct ControlMessageHeader
|
||||
{
|
||||
ControlMessage type;
|
||||
u32 size;
|
||||
};
|
||||
|
||||
struct ConnectRequestMessage
|
||||
{
|
||||
enum class Mode
|
||||
{
|
||||
Player,
|
||||
Spectator,
|
||||
};
|
||||
|
||||
ControlMessageHeader header;
|
||||
|
||||
Mode mode;
|
||||
s32 requested_player_id;
|
||||
char nickname[MAX_NICKNAME_LENGTH];
|
||||
char session_password[MAX_SESSION_PASSWORD_LENGTH];
|
||||
|
||||
std::string_view GetNickname() const
|
||||
{
|
||||
const size_t len = strnlen(nickname, std::size(nickname));
|
||||
return std::string_view(nickname, len);
|
||||
}
|
||||
|
||||
std::string_view GetSessionPassword() const
|
||||
{
|
||||
const size_t len = strnlen(session_password, std::size(session_password));
|
||||
return std::string_view(session_password, len);
|
||||
}
|
||||
|
||||
static ControlMessage MessageType() { return ControlMessage::ConnectRequest; }
|
||||
};
|
||||
|
||||
struct ConnectResponseMessage
|
||||
{
|
||||
enum class Result : u32
|
||||
{
|
||||
Success = 0,
|
||||
ServerFull,
|
||||
PlayerIDInUse,
|
||||
SessionClosed,
|
||||
};
|
||||
|
||||
ControlMessageHeader header;
|
||||
|
||||
Result result;
|
||||
s32 player_id;
|
||||
|
||||
static ControlMessage MessageType() { return ControlMessage::ConnectResponse; }
|
||||
};
|
||||
|
||||
struct ResetMessage
|
||||
{
|
||||
struct PlayerAddress
|
||||
{
|
||||
u32 host;
|
||||
u16 port;
|
||||
s16 controller_port; // -1 if not present
|
||||
char nickname[MAX_NICKNAME_LENGTH];
|
||||
|
||||
std::string_view GetNickname() const
|
||||
{
|
||||
const size_t len = strnlen(nickname, std::size(nickname));
|
||||
return std::string_view(nickname, len);
|
||||
}
|
||||
};
|
||||
|
||||
ControlMessageHeader header;
|
||||
u32 cookie;
|
||||
s32 num_players;
|
||||
PlayerAddress players[MAX_PLAYERS];
|
||||
u32 state_data_size;
|
||||
// state_data_size bytes of state data follows
|
||||
|
||||
static ControlMessage MessageType() { return ControlMessage::Reset; }
|
||||
};
|
||||
|
||||
struct ResetCompleteMessage
|
||||
{
|
||||
ControlMessageHeader header;
|
||||
|
||||
u32 cookie;
|
||||
|
||||
static ControlMessage MessageType() { return ControlMessage::ResetComplete; }
|
||||
};
|
||||
|
||||
struct ResumeSessionMessage
|
||||
{
|
||||
ControlMessageHeader header;
|
||||
|
||||
static ControlMessage MessageType() { return ControlMessage::ResumeSession; }
|
||||
};
|
||||
|
||||
struct PlayerJoinedMessage
|
||||
{
|
||||
ControlMessageHeader header;
|
||||
s32 player_id;
|
||||
|
||||
static ControlMessage MessageType() { return ControlMessage::PlayerJoined; }
|
||||
};
|
||||
|
||||
struct DropPlayerMessage
|
||||
{
|
||||
ControlMessageHeader header;
|
||||
DropPlayerReason reason;
|
||||
s32 player_id;
|
||||
|
||||
static ControlMessage MessageType() { return ControlMessage::DropPlayer; }
|
||||
};
|
||||
|
||||
struct ResetRequestMessage
|
||||
{
|
||||
enum class Reason : u32
|
||||
{
|
||||
ConnectionLost,
|
||||
};
|
||||
|
||||
ControlMessageHeader header;
|
||||
Reason reason;
|
||||
s32 causing_player_id;
|
||||
|
||||
std::string ReasonToString() const
|
||||
{
|
||||
switch (reason)
|
||||
{
|
||||
case Reason::ConnectionLost:
|
||||
return fmt::format(Host::TranslateString("Netplay", "Connection lost to player {}.").GetCharArray(),
|
||||
causing_player_id);
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
static ControlMessage MessageType() { return ControlMessage::ResetRequest; }
|
||||
};
|
||||
|
||||
struct CloseSessionMessage
|
||||
{
|
||||
enum class Reason : u32
|
||||
{
|
||||
HostRequest,
|
||||
HostShutdown,
|
||||
};
|
||||
|
||||
ControlMessageHeader header;
|
||||
Reason reason;
|
||||
|
||||
std::string ReasonToString() const
|
||||
{
|
||||
switch (reason)
|
||||
{
|
||||
case Reason::HostRequest:
|
||||
return Host::TranslateStdString("Netplay", "Session closed due to host request.");
|
||||
|
||||
case Reason::HostShutdown:
|
||||
return Host::TranslateStdString("Netplay", "Session closed due to host shutdown.");
|
||||
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
static ControlMessage MessageType() { return ControlMessage::CloseSession; }
|
||||
};
|
||||
|
||||
struct SetNicknameMessage
|
||||
{
|
||||
ControlMessageHeader header;
|
||||
|
||||
static ControlMessage MessageType() { return ControlMessage::SetNickname; }
|
||||
};
|
||||
|
||||
struct ChatMessage
|
||||
{
|
||||
ControlMessageHeader header;
|
||||
|
||||
std::string_view GetMessage() const
|
||||
{
|
||||
return (header.size > sizeof(ChatMessage)) ?
|
||||
std::string_view(reinterpret_cast<const char*>(this) + sizeof(ChatMessage),
|
||||
header.size - sizeof(ChatMessage)) :
|
||||
std::string_view();
|
||||
}
|
||||
|
||||
static ControlMessage MessageType() { return ControlMessage::ChatMessage; }
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
} // namespace Netplay
|
|
@ -3757,7 +3757,7 @@ void System::ShutdownSystem(bool save_resume_state)
|
|||
return;
|
||||
|
||||
if (Netplay::IsActive())
|
||||
Netplay::CloseSession();
|
||||
Netplay::SystemDestroyed();
|
||||
|
||||
if (save_resume_state)
|
||||
SaveResumeState();
|
||||
|
|
|
@ -2227,6 +2227,9 @@ int main(int argc, char* argv[])
|
|||
{
|
||||
Host::RunOnCPUThread([]() {
|
||||
const bool first = (s_netplay_test == 0);
|
||||
if (!first)
|
||||
QtHost::RunOnUIThread([]() { g_main_window->move(g_main_window->pos() + QPoint(500, 0)); });
|
||||
|
||||
const int h = first ? 1 : 2;
|
||||
const int nh = first ? 2 : 1;
|
||||
const int port_base = 31200;
|
||||
|
|
|
@ -97,7 +97,7 @@ void ImGuiManager::DrawNetplayMessages()
|
|||
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;
|
||||
float position_y = io.DisplaySize.y - margin - (100.0f * scale) - font->FontSize - spacing;
|
||||
ImDrawList* dl = ImGui::GetBackgroundDrawList();
|
||||
|
||||
// drop expired messages.. because of the reverse iteration below, we can't do it in there :/
|
||||
|
@ -121,11 +121,9 @@ void ImGuiManager::DrawNetplayMessages()
|
|||
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),
|
||||
dl->AddText(font, font->FontSize, ImVec2(margin + 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);
|
||||
dl->AddText(font, font->FontSize, ImVec2(margin, position_y), IM_COL32(255, 255, 255, alpha), text_start, text_end);
|
||||
|
||||
position_y -= text_size.y + msg_spacing;
|
||||
}
|
||||
|
@ -144,16 +142,13 @@ void ImGuiManager::DrawNetplayStats()
|
|||
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;
|
||||
const float position_y = ImGui::GetIO().DisplaySize.y - margin - (100.0f * scale);
|
||||
|
||||
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());
|
||||
dl->AddText(font, font->FontSize, ImVec2(margin + shadow_offset, position_y + shadow_offset), IM_COL32(0, 0, 0, 100),
|
||||
text, text.GetCharArray() + text.GetLength());
|
||||
dl->AddText(font, font->FontSize, ImVec2(margin, position_y), IM_COL32(255, 255, 255, 255), text,
|
||||
text.GetCharArray() + text.GetLength());
|
||||
}
|
||||
|
||||
void ImGuiManager::DrawNetplayChatDialog()
|
||||
|
@ -178,7 +173,7 @@ void ImGuiManager::DrawNetplayChatDialog()
|
|||
|
||||
// sending netplay message
|
||||
if (send_message && !s_netplay_chat_message.empty())
|
||||
Netplay::SendMsg(s_netplay_chat_message);
|
||||
Netplay::SendChatMessage(s_netplay_chat_message);
|
||||
|
||||
const ImGuiIO& io = ImGui::GetIO();
|
||||
const ImGuiStyle& style = ImGui::GetStyle();
|
||||
|
|
Loading…
Reference in New Issue