mirror of
https://github.com/citra-mirror/citra.git
synced 2024-08-20 02:13:23 +02:00
multiplayer: Add status message for user joining/leaving
The room server is now able to send a new type of packet: IdStatusMessage which is parsed and displayed by the client.
This commit is contained in:
parent
386bf5c861
commit
0319e51960
@ -70,12 +70,11 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
QString GetSystemChatMessage() const {
|
QString GetSystemChatMessage() const {
|
||||||
return QString("[%1] <font color='%2'><i>%3</i></font>")
|
return QString("[%1] <font color='%2'>* %3</font>").arg(timestamp, system_color, message);
|
||||||
.arg(timestamp, system_color, message);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static constexpr const char system_color[] = "#888888";
|
static constexpr const char system_color[] = "#FF8C00";
|
||||||
QString timestamp;
|
QString timestamp;
|
||||||
QString message;
|
QString message;
|
||||||
};
|
};
|
||||||
@ -133,6 +132,7 @@ ChatRoom::ChatRoom(QWidget* parent) : QWidget(parent), ui(std::make_unique<Ui::C
|
|||||||
|
|
||||||
// register the network structs to use in slots and signals
|
// register the network structs to use in slots and signals
|
||||||
qRegisterMetaType<Network::ChatEntry>();
|
qRegisterMetaType<Network::ChatEntry>();
|
||||||
|
qRegisterMetaType<Network::StatusMessageEntry>();
|
||||||
qRegisterMetaType<Network::RoomInformation>();
|
qRegisterMetaType<Network::RoomInformation>();
|
||||||
qRegisterMetaType<Network::RoomMember::State>();
|
qRegisterMetaType<Network::RoomMember::State>();
|
||||||
|
|
||||||
@ -140,7 +140,12 @@ ChatRoom::ChatRoom(QWidget* parent) : QWidget(parent), ui(std::make_unique<Ui::C
|
|||||||
if (auto member = Network::GetRoomMember().lock()) {
|
if (auto member = Network::GetRoomMember().lock()) {
|
||||||
member->BindOnChatMessageRecieved(
|
member->BindOnChatMessageRecieved(
|
||||||
[this](const Network::ChatEntry& chat) { emit ChatReceived(chat); });
|
[this](const Network::ChatEntry& chat) { emit ChatReceived(chat); });
|
||||||
|
member->BindOnStatusMessageReceived(
|
||||||
|
[this](const Network::StatusMessageEntry& status_message) {
|
||||||
|
emit StatusMessageReceived(status_message);
|
||||||
|
});
|
||||||
connect(this, &ChatRoom::ChatReceived, this, &ChatRoom::OnChatReceive);
|
connect(this, &ChatRoom::ChatReceived, this, &ChatRoom::OnChatReceive);
|
||||||
|
connect(this, &ChatRoom::StatusMessageReceived, this, &ChatRoom::OnStatusMessageReceive);
|
||||||
} else {
|
} else {
|
||||||
// TODO (jroweboy) network was not initialized?
|
// TODO (jroweboy) network was not initialized?
|
||||||
}
|
}
|
||||||
@ -220,6 +225,27 @@ void ChatRoom::OnChatReceive(const Network::ChatEntry& chat) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ChatRoom::OnStatusMessageReceive(const Network::StatusMessageEntry& status_message) {
|
||||||
|
QString name;
|
||||||
|
if (status_message.username.empty() || status_message.username == status_message.nickname) {
|
||||||
|
name = QString::fromStdString(status_message.nickname);
|
||||||
|
} else {
|
||||||
|
name = QString("%1 (%2)").arg(QString::fromStdString(status_message.nickname),
|
||||||
|
QString::fromStdString(status_message.username));
|
||||||
|
}
|
||||||
|
QString message;
|
||||||
|
switch (status_message.type) {
|
||||||
|
case Network::IdMemberJoin:
|
||||||
|
message = tr("%1 has joined").arg(name);
|
||||||
|
break;
|
||||||
|
case Network::IdMemberLeave:
|
||||||
|
message = tr("%1 has left").arg(name);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!message.isEmpty())
|
||||||
|
AppendStatusMessage(message);
|
||||||
|
}
|
||||||
|
|
||||||
void ChatRoom::OnSendChat() {
|
void ChatRoom::OnSendChat() {
|
||||||
if (auto room = Network::GetRoomMember().lock()) {
|
if (auto room = Network::GetRoomMember().lock()) {
|
||||||
if (room->GetState() != Network::RoomMember::State::Joined) {
|
if (room->GetState() != Network::RoomMember::State::Joined) {
|
||||||
|
@ -39,6 +39,7 @@ public:
|
|||||||
public slots:
|
public slots:
|
||||||
void OnRoomUpdate(const Network::RoomInformation& info);
|
void OnRoomUpdate(const Network::RoomInformation& info);
|
||||||
void OnChatReceive(const Network::ChatEntry&);
|
void OnChatReceive(const Network::ChatEntry&);
|
||||||
|
void OnStatusMessageReceive(const Network::StatusMessageEntry&);
|
||||||
void OnSendChat();
|
void OnSendChat();
|
||||||
void OnChatTextChanged();
|
void OnChatTextChanged();
|
||||||
void PopupContextMenu(const QPoint& menu_location);
|
void PopupContextMenu(const QPoint& menu_location);
|
||||||
@ -47,6 +48,7 @@ public slots:
|
|||||||
|
|
||||||
signals:
|
signals:
|
||||||
void ChatReceived(const Network::ChatEntry&);
|
void ChatReceived(const Network::ChatEntry&);
|
||||||
|
void StatusMessageReceived(const Network::StatusMessageEntry&);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static constexpr u32 max_chat_lines = 1000;
|
static constexpr u32 max_chat_lines = 1000;
|
||||||
@ -61,5 +63,6 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(Network::ChatEntry);
|
Q_DECLARE_METATYPE(Network::ChatEntry);
|
||||||
|
Q_DECLARE_METATYPE(Network::StatusMessageEntry);
|
||||||
Q_DECLARE_METATYPE(Network::RoomInformation);
|
Q_DECLARE_METATYPE(Network::RoomInformation);
|
||||||
Q_DECLARE_METATYPE(Network::RoomMember::State);
|
Q_DECLARE_METATYPE(Network::RoomMember::State);
|
||||||
|
@ -127,6 +127,12 @@ public:
|
|||||||
*/
|
*/
|
||||||
void SendCloseMessage();
|
void SendCloseMessage();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a system message to all the connected clients.
|
||||||
|
*/
|
||||||
|
void SendStatusMessage(StatusMessageTypes type, const std::string& nickname,
|
||||||
|
const std::string& username);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends the information about the room, along with the list of members
|
* Sends the information about the room, along with the list of members
|
||||||
* to every connected client in the room.
|
* to every connected client in the room.
|
||||||
@ -290,6 +296,9 @@ void Room::RoomImpl::HandleJoinRequest(const ENetEvent* event) {
|
|||||||
}
|
}
|
||||||
member.user_data = verify_backend->LoadUserData(uid, token);
|
member.user_data = verify_backend->LoadUserData(uid, token);
|
||||||
|
|
||||||
|
// Notify everyone that the user has joined.
|
||||||
|
SendStatusMessage(IdMemberJoin, member.nickname, member.user_data.username);
|
||||||
|
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(member_mutex);
|
std::lock_guard<std::mutex> lock(member_mutex);
|
||||||
members.push_back(std::move(member));
|
members.push_back(std::move(member));
|
||||||
@ -415,6 +424,24 @@ void Room::RoomImpl::SendCloseMessage() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Room::RoomImpl::SendStatusMessage(StatusMessageTypes type, const std::string& nickname,
|
||||||
|
const std::string& username) {
|
||||||
|
Packet packet;
|
||||||
|
packet << static_cast<u8>(IdStatusMessage);
|
||||||
|
packet << static_cast<u8>(type);
|
||||||
|
packet << nickname;
|
||||||
|
packet << username;
|
||||||
|
std::lock_guard<std::mutex> lock(member_mutex);
|
||||||
|
if (!members.empty()) {
|
||||||
|
ENetPacket* enet_packet =
|
||||||
|
enet_packet_create(packet.GetData(), packet.GetDataSize(), ENET_PACKET_FLAG_RELIABLE);
|
||||||
|
for (auto& member : members) {
|
||||||
|
enet_peer_send(member.peer, 0, enet_packet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
enet_host_flush(server);
|
||||||
|
}
|
||||||
|
|
||||||
void Room::RoomImpl::BroadcastRoomInformation() {
|
void Room::RoomImpl::BroadcastRoomInformation() {
|
||||||
Packet packet;
|
Packet packet;
|
||||||
packet << static_cast<u8>(IdRoomInformation);
|
packet << static_cast<u8>(IdRoomInformation);
|
||||||
@ -571,16 +598,23 @@ void Room::RoomImpl::HandleGameNamePacket(const ENetEvent* event) {
|
|||||||
|
|
||||||
void Room::RoomImpl::HandleClientDisconnection(ENetPeer* client) {
|
void Room::RoomImpl::HandleClientDisconnection(ENetPeer* client) {
|
||||||
// Remove the client from the members list.
|
// Remove the client from the members list.
|
||||||
|
std::string nickname, username;
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(member_mutex);
|
std::lock_guard<std::mutex> lock(member_mutex);
|
||||||
members.erase(
|
auto member = std::find_if(members.begin(), members.end(), [client](const Member& member) {
|
||||||
std::remove_if(members.begin(), members.end(),
|
return member.peer == client;
|
||||||
[client](const Member& member) { return member.peer == client; }),
|
});
|
||||||
members.end());
|
if (member != members.end()) {
|
||||||
|
nickname = member->nickname;
|
||||||
|
username = member->user_data.username;
|
||||||
|
members.erase(member);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Announce the change to all clients.
|
// Announce the change to all clients.
|
||||||
enet_peer_disconnect(client, 0);
|
enet_peer_disconnect(client, 0);
|
||||||
|
if (!nickname.empty())
|
||||||
|
SendStatusMessage(IdMemberLeave, nickname, username);
|
||||||
BroadcastRoomInformation();
|
BroadcastRoomInformation();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,6 +61,13 @@ enum RoomMessageTypes : u8 {
|
|||||||
IdCloseRoom,
|
IdCloseRoom,
|
||||||
IdRoomIsFull,
|
IdRoomIsFull,
|
||||||
IdConsoleIdCollision,
|
IdConsoleIdCollision,
|
||||||
|
IdStatusMessage,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Types of system status messages
|
||||||
|
enum StatusMessageTypes : u8 {
|
||||||
|
IdMemberJoin = 1, ///< Member joining
|
||||||
|
IdMemberLeave, ///< Member leaving
|
||||||
};
|
};
|
||||||
|
|
||||||
/// This is what a server [person creating a server] would use.
|
/// This is what a server [person creating a server] would use.
|
||||||
|
@ -58,6 +58,7 @@ public:
|
|||||||
private:
|
private:
|
||||||
CallbackSet<WifiPacket> callback_set_wifi_packet;
|
CallbackSet<WifiPacket> callback_set_wifi_packet;
|
||||||
CallbackSet<ChatEntry> callback_set_chat_messages;
|
CallbackSet<ChatEntry> callback_set_chat_messages;
|
||||||
|
CallbackSet<StatusMessageEntry> callback_set_status_messages;
|
||||||
CallbackSet<RoomInformation> callback_set_room_information;
|
CallbackSet<RoomInformation> callback_set_room_information;
|
||||||
CallbackSet<State> callback_set_state;
|
CallbackSet<State> callback_set_state;
|
||||||
};
|
};
|
||||||
@ -109,6 +110,13 @@ public:
|
|||||||
*/
|
*/
|
||||||
void HandleChatPacket(const ENetEvent* event);
|
void HandleChatPacket(const ENetEvent* event);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts a system message entry from a received ENet packet and adds it to the system message
|
||||||
|
* queue.
|
||||||
|
* @param event The ENet event that was received.
|
||||||
|
*/
|
||||||
|
void HandleStatusMessagePacket(const ENetEvent* event);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Disconnects the RoomMember from the Room
|
* Disconnects the RoomMember from the Room
|
||||||
*/
|
*/
|
||||||
@ -148,6 +156,9 @@ void RoomMember::RoomMemberImpl::MemberLoop() {
|
|||||||
case IdChatMessage:
|
case IdChatMessage:
|
||||||
HandleChatPacket(&event);
|
HandleChatPacket(&event);
|
||||||
break;
|
break;
|
||||||
|
case IdStatusMessage:
|
||||||
|
HandleStatusMessagePacket(&event);
|
||||||
|
break;
|
||||||
case IdRoomInformation:
|
case IdRoomInformation:
|
||||||
HandleRoomInformationPacket(&event);
|
HandleRoomInformationPacket(&event);
|
||||||
break;
|
break;
|
||||||
@ -317,6 +328,22 @@ void RoomMember::RoomMemberImpl::HandleChatPacket(const ENetEvent* event) {
|
|||||||
Invoke<ChatEntry>(chat_entry);
|
Invoke<ChatEntry>(chat_entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RoomMember::RoomMemberImpl::HandleStatusMessagePacket(const ENetEvent* event) {
|
||||||
|
Packet packet;
|
||||||
|
packet.Append(event->packet->data, event->packet->dataLength);
|
||||||
|
|
||||||
|
// Ignore the first byte, which is the message id.
|
||||||
|
packet.IgnoreBytes(sizeof(u8));
|
||||||
|
|
||||||
|
StatusMessageEntry status_message_entry{};
|
||||||
|
u8 type{};
|
||||||
|
packet >> type;
|
||||||
|
status_message_entry.type = static_cast<StatusMessageTypes>(type);
|
||||||
|
packet >> status_message_entry.nickname;
|
||||||
|
packet >> status_message_entry.username;
|
||||||
|
Invoke<StatusMessageEntry>(status_message_entry);
|
||||||
|
}
|
||||||
|
|
||||||
void RoomMember::RoomMemberImpl::Disconnect() {
|
void RoomMember::RoomMemberImpl::Disconnect() {
|
||||||
member_information.clear();
|
member_information.clear();
|
||||||
room_information.member_slots = 0;
|
room_information.member_slots = 0;
|
||||||
@ -367,6 +394,12 @@ RoomMember::RoomMemberImpl::CallbackSet<ChatEntry>& RoomMember::RoomMemberImpl::
|
|||||||
return callback_set_chat_messages;
|
return callback_set_chat_messages;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
RoomMember::RoomMemberImpl::CallbackSet<StatusMessageEntry>&
|
||||||
|
RoomMember::RoomMemberImpl::Callbacks::Get() {
|
||||||
|
return callback_set_status_messages;
|
||||||
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void RoomMember::RoomMemberImpl::Invoke(const T& data) {
|
void RoomMember::RoomMemberImpl::Invoke(const T& data) {
|
||||||
std::lock_guard<std::mutex> lock(callback_mutex);
|
std::lock_guard<std::mutex> lock(callback_mutex);
|
||||||
@ -519,6 +552,11 @@ RoomMember::CallbackHandle<ChatEntry> RoomMember::BindOnChatMessageRecieved(
|
|||||||
return room_member_impl->Bind(callback);
|
return room_member_impl->Bind(callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RoomMember::CallbackHandle<StatusMessageEntry> RoomMember::BindOnStatusMessageReceived(
|
||||||
|
std::function<void(const StatusMessageEntry&)> callback) {
|
||||||
|
return room_member_impl->Bind(callback);
|
||||||
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void RoomMember::Unbind(CallbackHandle<T> handle) {
|
void RoomMember::Unbind(CallbackHandle<T> handle) {
|
||||||
std::lock_guard<std::mutex> lock(room_member_impl->callback_mutex);
|
std::lock_guard<std::mutex> lock(room_member_impl->callback_mutex);
|
||||||
@ -538,5 +576,6 @@ template void RoomMember::Unbind(CallbackHandle<WifiPacket>);
|
|||||||
template void RoomMember::Unbind(CallbackHandle<RoomMember::State>);
|
template void RoomMember::Unbind(CallbackHandle<RoomMember::State>);
|
||||||
template void RoomMember::Unbind(CallbackHandle<RoomInformation>);
|
template void RoomMember::Unbind(CallbackHandle<RoomInformation>);
|
||||||
template void RoomMember::Unbind(CallbackHandle<ChatEntry>);
|
template void RoomMember::Unbind(CallbackHandle<ChatEntry>);
|
||||||
|
template void RoomMember::Unbind(CallbackHandle<StatusMessageEntry>);
|
||||||
|
|
||||||
} // namespace Network
|
} // namespace Network
|
||||||
|
@ -40,6 +40,14 @@ struct ChatEntry {
|
|||||||
std::string message; ///< Body of the message.
|
std::string message; ///< Body of the message.
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Represents a system status message.
|
||||||
|
struct StatusMessageEntry {
|
||||||
|
StatusMessageTypes type; ///< Type of the message
|
||||||
|
/// Subject of the message. i.e. the user who is joining/leaving/being banned, etc.
|
||||||
|
std::string nickname;
|
||||||
|
std::string username;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is what a client [person joining a server] would use.
|
* This is what a client [person joining a server] would use.
|
||||||
* It also has to be used if you host a game yourself (You'd create both, a Room and a
|
* It also has to be used if you host a game yourself (You'd create both, a Room and a
|
||||||
@ -192,6 +200,16 @@ public:
|
|||||||
CallbackHandle<ChatEntry> BindOnChatMessageRecieved(
|
CallbackHandle<ChatEntry> BindOnChatMessageRecieved(
|
||||||
std::function<void(const ChatEntry&)> callback);
|
std::function<void(const ChatEntry&)> callback);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Binds a function to an event that will be triggered every time a StatusMessage is
|
||||||
|
* received. The function will be called every time the event is triggered. The callback
|
||||||
|
* function must not bind or unbind a function. Doing so will cause a deadlock
|
||||||
|
* @param callback The function to call
|
||||||
|
* @return A handle used for removing the function from the registered list
|
||||||
|
*/
|
||||||
|
CallbackHandle<StatusMessageEntry> BindOnStatusMessageReceived(
|
||||||
|
std::function<void(const StatusMessageEntry&)> callback);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Leaves the current room.
|
* Leaves the current room.
|
||||||
*/
|
*/
|
||||||
|
Loading…
Reference in New Issue
Block a user