mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-31 13:50:03 +00:00 
			
		
		
		
	network, citra_qt: Give moderation permission to community mods
Based on the `roles` payload in the JWT, the rooms will now give mod permission to Citra Community Moderators. To notify the client of its permissions, a new response, IdJoinSuccessAsMod is added, and there's now a new RoomMember::State called Moderator.
This commit is contained in:
		
							parent
							
								
									94be4050bc
								
							
						
					
					
						commit
						9d062d63da
					
				
					 15 changed files with 73 additions and 27 deletions
				
			
		|  | @ -81,6 +81,9 @@ static void OnStateChanged(const Network::RoomMember::State& state) { | ||||||
|     case Network::RoomMember::State::Joined: |     case Network::RoomMember::State::Joined: | ||||||
|         LOG_DEBUG(Network, "Successfully joined to the room"); |         LOG_DEBUG(Network, "Successfully joined to the room"); | ||||||
|         break; |         break; | ||||||
|  |     case Network::RoomMember::State::Moderator: | ||||||
|  |         LOG_DEBUG(Network, "Successfully joined the room as a moderator"); | ||||||
|  |         break; | ||||||
|     default: |     default: | ||||||
|         break; |         break; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | @ -306,7 +306,9 @@ void ChatRoom::OnStatusMessageReceive(const Network::StatusMessageEntry& status_ | ||||||
| 
 | 
 | ||||||
| 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 && | ||||||
|  |             room->GetState() != Network::RoomMember::State::Moderator) { | ||||||
|  | 
 | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|         auto message = ui->chat_message->text().toStdString(); |         auto message = ui->chat_message->text().toStdString(); | ||||||
|  |  | ||||||
|  | @ -72,9 +72,12 @@ void ClientRoomWindow::OnRoomUpdate(const Network::RoomInformation& info) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void ClientRoomWindow::OnStateChange(const Network::RoomMember::State& state) { | void ClientRoomWindow::OnStateChange(const Network::RoomMember::State& state) { | ||||||
|     if (state == Network::RoomMember::State::Joined) { |     if (state == Network::RoomMember::State::Joined || | ||||||
|  |         state == Network::RoomMember::State::Moderator) { | ||||||
|  | 
 | ||||||
|         ui->chat->Clear(); |         ui->chat->Clear(); | ||||||
|         ui->chat->AppendStatusMessage(tr("Connected")); |         ui->chat->AppendStatusMessage(tr("Connected")); | ||||||
|  |         SetModPerms(state == Network::RoomMember::State::Moderator); | ||||||
|     } |     } | ||||||
|     UpdateView(); |     UpdateView(); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -18,7 +18,6 @@ public: | ||||||
|     ~ClientRoomWindow(); |     ~ClientRoomWindow(); | ||||||
| 
 | 
 | ||||||
|     void RetranslateUi(); |     void RetranslateUi(); | ||||||
|     void SetModPerms(bool is_mod); |  | ||||||
| 
 | 
 | ||||||
| public slots: | public slots: | ||||||
|     void OnRoomUpdate(const Network::RoomInformation&); |     void OnRoomUpdate(const Network::RoomInformation&); | ||||||
|  | @ -32,6 +31,7 @@ signals: | ||||||
| private: | private: | ||||||
|     void Disconnect(); |     void Disconnect(); | ||||||
|     void UpdateView(); |     void UpdateView(); | ||||||
|  |     void SetModPerms(bool is_mod); | ||||||
| 
 | 
 | ||||||
|     QStandardItemModel* player_list; |     QStandardItemModel* player_list; | ||||||
|     std::unique_ptr<Ui::ClientRoom> ui; |     std::unique_ptr<Ui::ClientRoom> ui; | ||||||
|  |  | ||||||
|  | @ -63,7 +63,7 @@ void DirectConnectWindow::Connect() { | ||||||
|         // Prevent the user from trying to join a room while they are already joining.
 |         // Prevent the user from trying to join a room while they are already joining.
 | ||||||
|         if (member->GetState() == Network::RoomMember::State::Joining) { |         if (member->GetState() == Network::RoomMember::State::Joining) { | ||||||
|             return; |             return; | ||||||
|         } else if (member->GetState() == Network::RoomMember::State::Joined) { |         } else if (member->IsConnected()) { | ||||||
|             // And ask if they want to leave the room if they are already in one.
 |             // And ask if they want to leave the room if they are already in one.
 | ||||||
|             if (!NetworkMessage::WarnDisconnect()) { |             if (!NetworkMessage::WarnDisconnect()) { | ||||||
|                 return; |                 return; | ||||||
|  | @ -122,7 +122,9 @@ void DirectConnectWindow::OnConnection() { | ||||||
|     EndConnecting(); |     EndConnecting(); | ||||||
| 
 | 
 | ||||||
|     if (auto room_member = Network::GetRoomMember().lock()) { |     if (auto room_member = Network::GetRoomMember().lock()) { | ||||||
|         if (room_member->GetState() == Network::RoomMember::State::Joined) { |         if (room_member->GetState() == Network::RoomMember::State::Joined || | ||||||
|  |             room_member->GetState() == Network::RoomMember::State::Moderator) { | ||||||
|  | 
 | ||||||
|             close(); |             close(); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | @ -113,7 +113,7 @@ void HostRoomWindow::Host() { | ||||||
|     if (auto member = Network::GetRoomMember().lock()) { |     if (auto member = Network::GetRoomMember().lock()) { | ||||||
|         if (member->GetState() == Network::RoomMember::State::Joining) { |         if (member->GetState() == Network::RoomMember::State::Joining) { | ||||||
|             return; |             return; | ||||||
|         } else if (member->GetState() == Network::RoomMember::State::Joined) { |         } else if (member->IsConnected()) { | ||||||
|             auto parent = static_cast<MultiplayerState*>(parentWidget()); |             auto parent = static_cast<MultiplayerState*>(parentWidget()); | ||||||
|             if (!parent->OnCloseRoom()) { |             if (!parent->OnCloseRoom()) { | ||||||
|                 close(); |                 close(); | ||||||
|  |  | ||||||
|  | @ -109,7 +109,7 @@ void Lobby::OnJoinRoom(const QModelIndex& source) { | ||||||
|         // Prevent the user from trying to join a room while they are already joining.
 |         // Prevent the user from trying to join a room while they are already joining.
 | ||||||
|         if (member->GetState() == Network::RoomMember::State::Joining) { |         if (member->GetState() == Network::RoomMember::State::Joining) { | ||||||
|             return; |             return; | ||||||
|         } else if (member->GetState() == Network::RoomMember::State::Joined) { |         } else if (member->IsConnected()) { | ||||||
|             // And ask if they want to leave the room if they are already in one.
 |             // And ask if they want to leave the room if they are already in one.
 | ||||||
|             if (!NetworkMessage::WarnDisconnect()) { |             if (!NetworkMessage::WarnDisconnect()) { | ||||||
|                 return; |                 return; | ||||||
|  |  | ||||||
|  | @ -89,7 +89,9 @@ void MultiplayerState::retranslateUi() { | ||||||
| 
 | 
 | ||||||
|     if (current_state == Network::RoomMember::State::Uninitialized) { |     if (current_state == Network::RoomMember::State::Uninitialized) { | ||||||
|         status_text->setText(tr("Not Connected. Click here to find a room!")); |         status_text->setText(tr("Not Connected. Click here to find a room!")); | ||||||
|     } else if (current_state == Network::RoomMember::State::Joined) { |     } else if (current_state == Network::RoomMember::State::Joined || | ||||||
|  |                current_state == Network::RoomMember::State::Moderator) { | ||||||
|  | 
 | ||||||
|         status_text->setText(tr("Connected")); |         status_text->setText(tr("Connected")); | ||||||
|     } else { |     } else { | ||||||
|         status_text->setText(tr("Not Connected")); |         status_text->setText(tr("Not Connected")); | ||||||
|  | @ -107,7 +109,9 @@ void MultiplayerState::retranslateUi() { | ||||||
| 
 | 
 | ||||||
| void MultiplayerState::OnNetworkStateChanged(const Network::RoomMember::State& state) { | void MultiplayerState::OnNetworkStateChanged(const Network::RoomMember::State& state) { | ||||||
|     LOG_DEBUG(Frontend, "Network State: {}", Network::GetStateStr(state)); |     LOG_DEBUG(Frontend, "Network State: {}", Network::GetStateStr(state)); | ||||||
|     if (state == Network::RoomMember::State::Joined) { |     if (state == Network::RoomMember::State::Joined || | ||||||
|  |         state == Network::RoomMember::State::Moderator) { | ||||||
|  | 
 | ||||||
|         OnOpenNetworkRoom(); |         OnOpenNetworkRoom(); | ||||||
|         status_icon->setPixmap(QIcon::fromTheme("connected").pixmap(16)); |         status_icon->setPixmap(QIcon::fromTheme("connected").pixmap(16)); | ||||||
|         status_text->setText(tr("Connected")); |         status_text->setText(tr("Connected")); | ||||||
|  | @ -183,7 +187,9 @@ void MultiplayerState::OnAnnounceFailed(const Common::WebResult& result) { | ||||||
| void MultiplayerState::UpdateThemedIcons() { | void MultiplayerState::UpdateThemedIcons() { | ||||||
|     if (show_notification) { |     if (show_notification) { | ||||||
|         status_icon->setPixmap(QIcon::fromTheme("connected_notification").pixmap(16)); |         status_icon->setPixmap(QIcon::fromTheme("connected_notification").pixmap(16)); | ||||||
|     } else if (current_state == Network::RoomMember::State::Joined) { |     } else if (current_state == Network::RoomMember::State::Joined || | ||||||
|  |                current_state == Network::RoomMember::State::Moderator) { | ||||||
|  | 
 | ||||||
|         status_icon->setPixmap(QIcon::fromTheme("connected").pixmap(16)); |         status_icon->setPixmap(QIcon::fromTheme("connected").pixmap(16)); | ||||||
|     } else { |     } else { | ||||||
|         status_icon->setPixmap(QIcon::fromTheme("disconnected").pixmap(16)); |         status_icon->setPixmap(QIcon::fromTheme("disconnected").pixmap(16)); | ||||||
|  | @ -258,12 +264,6 @@ void MultiplayerState::OnOpenNetworkRoom() { | ||||||
|                 connect(client_room, &ClientRoomWindow::ShowNotification, this, |                 connect(client_room, &ClientRoomWindow::ShowNotification, this, | ||||||
|                         &MultiplayerState::ShowNotification); |                         &MultiplayerState::ShowNotification); | ||||||
|             } |             } | ||||||
|             const std::string host_username = member->GetRoomInformation().host_username; |  | ||||||
|             if (host_username.empty()) { |  | ||||||
|                 client_room->SetModPerms(false); |  | ||||||
|             } else { |  | ||||||
|                 client_room->SetModPerms(member->GetUsername() == host_username); |  | ||||||
|             } |  | ||||||
|             BringWidgetToFront(client_room); |             BringWidgetToFront(client_room); | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  | @ -140,7 +140,9 @@ std::list<Network::WifiPacket> GetReceivedBeacons(const MacAddress& sender) { | ||||||
| /// Sends a WifiPacket to the room we're currently connected to.
 | /// Sends a WifiPacket to the room we're currently connected to.
 | ||||||
| void SendPacket(Network::WifiPacket& packet) { | void SendPacket(Network::WifiPacket& packet) { | ||||||
|     if (auto room_member = Network::GetRoomMember().lock()) { |     if (auto room_member = Network::GetRoomMember().lock()) { | ||||||
|         if (room_member->GetState() == Network::RoomMember::State::Joined) { |         if (room_member->GetState() == Network::RoomMember::State::Joined || | ||||||
|  |             room_member->GetState() == Network::RoomMember::State::Moderator) { | ||||||
|  | 
 | ||||||
|             packet.transmitter_address = room_member->GetMacAddress(); |             packet.transmitter_address = room_member->GetMacAddress(); | ||||||
|             room_member->SendWifiPacket(packet); |             room_member->SendWifiPacket(packet); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  | @ -155,6 +155,12 @@ public: | ||||||
|      */ |      */ | ||||||
|     void SendJoinSuccess(ENetPeer* client, MacAddress mac_address); |     void SendJoinSuccess(ENetPeer* client, MacAddress mac_address); | ||||||
| 
 | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Notifies the member that its connection attempt was successful, | ||||||
|  |      * and it is now part of the room, and it has been granted mod permissions. | ||||||
|  |      */ | ||||||
|  |     void SendJoinSuccessAsMod(ENetPeer* client, MacAddress mac_address); | ||||||
|  | 
 | ||||||
|     /**
 |     /**
 | ||||||
|      * Sends a IdHostKicked message telling the client that they have been kicked. |      * Sends a IdHostKicked message telling the client that they have been kicked. | ||||||
|      */ |      */ | ||||||
|  | @ -401,7 +407,11 @@ void Room::RoomImpl::HandleJoinRequest(const ENetEvent* event) { | ||||||
| 
 | 
 | ||||||
|     // Notify everyone that the room information has changed.
 |     // Notify everyone that the room information has changed.
 | ||||||
|     BroadcastRoomInformation(); |     BroadcastRoomInformation(); | ||||||
|  |     if (HasModPermission(event->peer)) { | ||||||
|  |         SendJoinSuccessAsMod(event->peer, preferred_mac); | ||||||
|  |     } else { | ||||||
|         SendJoinSuccess(event->peer, preferred_mac); |         SendJoinSuccess(event->peer, preferred_mac); | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Room::RoomImpl::HandleModKickPacket(const ENetEvent* event) { | void Room::RoomImpl::HandleModKickPacket(const ENetEvent* event) { | ||||||
|  | @ -588,10 +598,11 @@ bool Room::RoomImpl::HasModPermission(const ENetPeer* client) const { | ||||||
|     if (sending_member == members.end()) { |     if (sending_member == members.end()) { | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
|     if (sending_member->user_data.username != room_information.host_username) { |     if (sending_member->user_data.moderator) // Community moderator
 | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
|         return true; |         return true; | ||||||
|  |     if (sending_member->user_data.username == room_information.host_username) // Room host
 | ||||||
|  |         return true; | ||||||
|  |     return false; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Room::RoomImpl::SendNameCollision(ENetPeer* client) { | void Room::RoomImpl::SendNameCollision(ENetPeer* client) { | ||||||
|  | @ -665,6 +676,16 @@ void Room::RoomImpl::SendJoinSuccess(ENetPeer* client, MacAddress mac_address) { | ||||||
|     enet_host_flush(server); |     enet_host_flush(server); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void Room::RoomImpl::SendJoinSuccessAsMod(ENetPeer* client, MacAddress mac_address) { | ||||||
|  |     Packet packet; | ||||||
|  |     packet << static_cast<u8>(IdJoinSuccessAsMod); | ||||||
|  |     packet << mac_address; | ||||||
|  |     ENetPacket* enet_packet = | ||||||
|  |         enet_packet_create(packet.GetData(), packet.GetDataSize(), ENET_PACKET_FLAG_RELIABLE); | ||||||
|  |     enet_peer_send(client, 0, enet_packet); | ||||||
|  |     enet_host_flush(server); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void Room::RoomImpl::SendUserKicked(ENetPeer* client) { | void Room::RoomImpl::SendUserKicked(ENetPeer* client) { | ||||||
|     Packet packet; |     Packet packet; | ||||||
|     packet << static_cast<u8>(IdHostKicked); |     packet << static_cast<u8>(IdHostKicked); | ||||||
|  |  | ||||||
|  | @ -74,6 +74,7 @@ enum RoomMessageTypes : u8 { | ||||||
|     IdModBanListResponse, |     IdModBanListResponse, | ||||||
|     IdModPermissionDenied, |     IdModPermissionDenied, | ||||||
|     IdModNoSuchUser, |     IdModNoSuchUser, | ||||||
|  |     IdJoinSuccessAsMod, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /// Types of system status messages
 | /// Types of system status messages
 | ||||||
|  |  | ||||||
|  | @ -151,7 +151,7 @@ void RoomMember::RoomMemberImpl::SetError(const Error new_error) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool RoomMember::RoomMemberImpl::IsConnected() const { | bool RoomMember::RoomMemberImpl::IsConnected() const { | ||||||
|     return state == State::Joining || state == State::Joined; |     return state == State::Joining || state == State::Joined || state == State::Moderator; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void RoomMember::RoomMemberImpl::MemberLoop() { | void RoomMember::RoomMemberImpl::MemberLoop() { | ||||||
|  | @ -176,12 +176,17 @@ void RoomMember::RoomMemberImpl::MemberLoop() { | ||||||
|                     HandleRoomInformationPacket(&event); |                     HandleRoomInformationPacket(&event); | ||||||
|                     break; |                     break; | ||||||
|                 case IdJoinSuccess: |                 case IdJoinSuccess: | ||||||
|  |                 case IdJoinSuccessAsMod: | ||||||
|                     // The join request was successful, we are now in the room.
 |                     // The join request was successful, we are now in the room.
 | ||||||
|                     // If we joined successfully, there must be at least one client in the room: us.
 |                     // If we joined successfully, there must be at least one client in the room: us.
 | ||||||
|                     ASSERT_MSG(member_information.size() > 0, |                     ASSERT_MSG(member_information.size() > 0, | ||||||
|                                "We have not yet received member information."); |                                "We have not yet received member information."); | ||||||
|                     HandleJoinPacket(&event); // Get the MAC Address for the client
 |                     HandleJoinPacket(&event); // Get the MAC Address for the client
 | ||||||
|  |                     if (event.packet->data[0] == IdJoinSuccessAsMod) { | ||||||
|  |                         SetState(State::Moderator); | ||||||
|  |                     } else { | ||||||
|                         SetState(State::Joined); |                         SetState(State::Joined); | ||||||
|  |                     } | ||||||
|                     break; |                     break; | ||||||
|                 case IdModBanListResponse: |                 case IdModBanListResponse: | ||||||
|                     HandleModBanListResponsePacket(&event); |                     HandleModBanListResponsePacket(&event); | ||||||
|  | @ -232,7 +237,7 @@ void RoomMember::RoomMemberImpl::MemberLoop() { | ||||||
|                 enet_packet_destroy(event.packet); |                 enet_packet_destroy(event.packet); | ||||||
|                 break; |                 break; | ||||||
|             case ENET_EVENT_TYPE_DISCONNECT: |             case ENET_EVENT_TYPE_DISCONNECT: | ||||||
|                 if (state == State::Joined) { |                 if (state == State::Joined || state == State::Moderator) { | ||||||
|                     SetState(State::Idle); |                     SetState(State::Idle); | ||||||
|                     SetError(Error::LostConnection); |                     SetError(Error::LostConnection); | ||||||
|                 } |                 } | ||||||
|  | @ -331,7 +336,6 @@ void RoomMember::RoomMemberImpl::HandleJoinPacket(const ENetEvent* event) { | ||||||
| 
 | 
 | ||||||
|     // Parse the MAC Address from the packet
 |     // Parse the MAC Address from the packet
 | ||||||
|     packet >> mac_address; |     packet >> mac_address; | ||||||
|     SetState(State::Joined); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void RoomMember::RoomMemberImpl::HandleWifiPackets(const ENetEvent* event) { | void RoomMember::RoomMemberImpl::HandleWifiPackets(const ENetEvent* event) { | ||||||
|  |  | ||||||
|  | @ -60,6 +60,7 @@ public: | ||||||
|         Idle,          ///< Default state (i.e. not connected)
 |         Idle,          ///< Default state (i.e. not connected)
 | ||||||
|         Joining,       ///< The client is attempting to join a room.
 |         Joining,       ///< The client is attempting to join a room.
 | ||||||
|         Joined,    ///< The client is connected to the room and is ready to send/receive packets.
 |         Joined,    ///< The client is connected to the room and is ready to send/receive packets.
 | ||||||
|  |         Moderator, ///< The client is connnected to the room and is granted mod permissions.
 | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     enum class Error : u8 { |     enum class Error : u8 { | ||||||
|  | @ -270,6 +271,8 @@ static const char* GetStateStr(const RoomMember::State& s) { | ||||||
|         return "Joining"; |         return "Joining"; | ||||||
|     case RoomMember::State::Joined: |     case RoomMember::State::Joined: | ||||||
|         return "Joined"; |         return "Joined"; | ||||||
|  |     case RoomMember::State::Moderator: | ||||||
|  |         return "Moderator"; | ||||||
|     } |     } | ||||||
|     return "Unknown"; |     return "Unknown"; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -13,6 +13,7 @@ struct UserData { | ||||||
|     std::string username; |     std::string username; | ||||||
|     std::string display_name; |     std::string display_name; | ||||||
|     std::string avatar_url; |     std::string avatar_url; | ||||||
|  |     bool moderator = false; ///< Whether the user is a Citra Moderator.
 | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  |  | ||||||
|  | @ -50,6 +50,10 @@ Network::VerifyUser::UserData VerifyUserJWT::LoadUserData(const std::string& ver | ||||||
|     if (decoded.payload().has_claim("avatarUrl")) { |     if (decoded.payload().has_claim("avatarUrl")) { | ||||||
|         user_data.avatar_url = decoded.payload().get_claim_value<std::string>("avatarUrl"); |         user_data.avatar_url = decoded.payload().get_claim_value<std::string>("avatarUrl"); | ||||||
|     } |     } | ||||||
|  |     if (decoded.payload().has_claim("roles")) { | ||||||
|  |         auto roles = decoded.payload().get_claim_value<std::vector<std::string>>("roles"); | ||||||
|  |         user_data.moderator = std::find(roles.begin(), roles.end(), "moderator") != roles.end(); | ||||||
|  |     } | ||||||
|     return user_data; |     return user_data; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue