mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-30 21:30:04 +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: | ||||
|         LOG_DEBUG(Network, "Successfully joined to the room"); | ||||
|         break; | ||||
|     case Network::RoomMember::State::Moderator: | ||||
|         LOG_DEBUG(Network, "Successfully joined the room as a moderator"); | ||||
|         break; | ||||
|     default: | ||||
|         break; | ||||
|     } | ||||
|  |  | |||
|  | @ -306,7 +306,9 @@ void ChatRoom::OnStatusMessageReceive(const Network::StatusMessageEntry& status_ | |||
| 
 | ||||
| void ChatRoom::OnSendChat() { | ||||
|     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; | ||||
|         } | ||||
|         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) { | ||||
|     if (state == Network::RoomMember::State::Joined) { | ||||
|     if (state == Network::RoomMember::State::Joined || | ||||
|         state == Network::RoomMember::State::Moderator) { | ||||
| 
 | ||||
|         ui->chat->Clear(); | ||||
|         ui->chat->AppendStatusMessage(tr("Connected")); | ||||
|         SetModPerms(state == Network::RoomMember::State::Moderator); | ||||
|     } | ||||
|     UpdateView(); | ||||
| } | ||||
|  |  | |||
|  | @ -18,7 +18,6 @@ public: | |||
|     ~ClientRoomWindow(); | ||||
| 
 | ||||
|     void RetranslateUi(); | ||||
|     void SetModPerms(bool is_mod); | ||||
| 
 | ||||
| public slots: | ||||
|     void OnRoomUpdate(const Network::RoomInformation&); | ||||
|  | @ -32,6 +31,7 @@ signals: | |||
| private: | ||||
|     void Disconnect(); | ||||
|     void UpdateView(); | ||||
|     void SetModPerms(bool is_mod); | ||||
| 
 | ||||
|     QStandardItemModel* player_list; | ||||
|     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.
 | ||||
|         if (member->GetState() == Network::RoomMember::State::Joining) { | ||||
|             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.
 | ||||
|             if (!NetworkMessage::WarnDisconnect()) { | ||||
|                 return; | ||||
|  | @ -122,7 +122,9 @@ void DirectConnectWindow::OnConnection() { | |||
|     EndConnecting(); | ||||
| 
 | ||||
|     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(); | ||||
|         } | ||||
|     } | ||||
|  |  | |||
|  | @ -113,7 +113,7 @@ void HostRoomWindow::Host() { | |||
|     if (auto member = Network::GetRoomMember().lock()) { | ||||
|         if (member->GetState() == Network::RoomMember::State::Joining) { | ||||
|             return; | ||||
|         } else if (member->GetState() == Network::RoomMember::State::Joined) { | ||||
|         } else if (member->IsConnected()) { | ||||
|             auto parent = static_cast<MultiplayerState*>(parentWidget()); | ||||
|             if (!parent->OnCloseRoom()) { | ||||
|                 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.
 | ||||
|         if (member->GetState() == Network::RoomMember::State::Joining) { | ||||
|             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.
 | ||||
|             if (!NetworkMessage::WarnDisconnect()) { | ||||
|                 return; | ||||
|  |  | |||
|  | @ -89,7 +89,9 @@ void MultiplayerState::retranslateUi() { | |||
| 
 | ||||
|     if (current_state == Network::RoomMember::State::Uninitialized) { | ||||
|         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")); | ||||
|     } else { | ||||
|         status_text->setText(tr("Not Connected")); | ||||
|  | @ -107,7 +109,9 @@ void MultiplayerState::retranslateUi() { | |||
| 
 | ||||
| void MultiplayerState::OnNetworkStateChanged(const Network::RoomMember::State& 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(); | ||||
|         status_icon->setPixmap(QIcon::fromTheme("connected").pixmap(16)); | ||||
|         status_text->setText(tr("Connected")); | ||||
|  | @ -183,7 +187,9 @@ void MultiplayerState::OnAnnounceFailed(const Common::WebResult& result) { | |||
| void MultiplayerState::UpdateThemedIcons() { | ||||
|     if (show_notification) { | ||||
|         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)); | ||||
|     } else { | ||||
|         status_icon->setPixmap(QIcon::fromTheme("disconnected").pixmap(16)); | ||||
|  | @ -258,12 +264,6 @@ void MultiplayerState::OnOpenNetworkRoom() { | |||
|                 connect(client_room, &ClientRoomWindow::ShowNotification, this, | ||||
|                         &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); | ||||
|             return; | ||||
|         } | ||||
|  |  | |||
|  | @ -140,7 +140,9 @@ std::list<Network::WifiPacket> GetReceivedBeacons(const MacAddress& sender) { | |||
| /// Sends a WifiPacket to the room we're currently connected to.
 | ||||
| void SendPacket(Network::WifiPacket& packet) { | ||||
|     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(); | ||||
|             room_member->SendWifiPacket(packet); | ||||
|         } | ||||
|  |  | |||
|  | @ -155,6 +155,12 @@ public: | |||
|      */ | ||||
|     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. | ||||
|      */ | ||||
|  | @ -401,7 +407,11 @@ void Room::RoomImpl::HandleJoinRequest(const ENetEvent* event) { | |||
| 
 | ||||
|     // Notify everyone that the room information has changed.
 | ||||
|     BroadcastRoomInformation(); | ||||
|     SendJoinSuccess(event->peer, preferred_mac); | ||||
|     if (HasModPermission(event->peer)) { | ||||
|         SendJoinSuccessAsMod(event->peer, preferred_mac); | ||||
|     } else { | ||||
|         SendJoinSuccess(event->peer, preferred_mac); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void Room::RoomImpl::HandleModKickPacket(const ENetEvent* event) { | ||||
|  | @ -588,10 +598,11 @@ bool Room::RoomImpl::HasModPermission(const ENetPeer* client) const { | |||
|     if (sending_member == members.end()) { | ||||
|         return false; | ||||
|     } | ||||
|     if (sending_member->user_data.username != room_information.host_username) { | ||||
|         return false; | ||||
|     } | ||||
|     return true; | ||||
|     if (sending_member->user_data.moderator) // Community moderator
 | ||||
|         return true; | ||||
|     if (sending_member->user_data.username == room_information.host_username) // Room host
 | ||||
|         return true; | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| void Room::RoomImpl::SendNameCollision(ENetPeer* client) { | ||||
|  | @ -665,6 +676,16 @@ void Room::RoomImpl::SendJoinSuccess(ENetPeer* client, MacAddress mac_address) { | |||
|     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) { | ||||
|     Packet packet; | ||||
|     packet << static_cast<u8>(IdHostKicked); | ||||
|  |  | |||
|  | @ -74,6 +74,7 @@ enum RoomMessageTypes : u8 { | |||
|     IdModBanListResponse, | ||||
|     IdModPermissionDenied, | ||||
|     IdModNoSuchUser, | ||||
|     IdJoinSuccessAsMod, | ||||
| }; | ||||
| 
 | ||||
| /// Types of system status messages
 | ||||
|  |  | |||
|  | @ -151,7 +151,7 @@ void RoomMember::RoomMemberImpl::SetError(const Error new_error) { | |||
| } | ||||
| 
 | ||||
| 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() { | ||||
|  | @ -176,12 +176,17 @@ void RoomMember::RoomMemberImpl::MemberLoop() { | |||
|                     HandleRoomInformationPacket(&event); | ||||
|                     break; | ||||
|                 case IdJoinSuccess: | ||||
|                 case IdJoinSuccessAsMod: | ||||
|                     // 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.
 | ||||
|                     ASSERT_MSG(member_information.size() > 0, | ||||
|                                "We have not yet received member information."); | ||||
|                     HandleJoinPacket(&event); // Get the MAC Address for the client
 | ||||
|                     SetState(State::Joined); | ||||
|                     if (event.packet->data[0] == IdJoinSuccessAsMod) { | ||||
|                         SetState(State::Moderator); | ||||
|                     } else { | ||||
|                         SetState(State::Joined); | ||||
|                     } | ||||
|                     break; | ||||
|                 case IdModBanListResponse: | ||||
|                     HandleModBanListResponsePacket(&event); | ||||
|  | @ -232,7 +237,7 @@ void RoomMember::RoomMemberImpl::MemberLoop() { | |||
|                 enet_packet_destroy(event.packet); | ||||
|                 break; | ||||
|             case ENET_EVENT_TYPE_DISCONNECT: | ||||
|                 if (state == State::Joined) { | ||||
|                 if (state == State::Joined || state == State::Moderator) { | ||||
|                     SetState(State::Idle); | ||||
|                     SetError(Error::LostConnection); | ||||
|                 } | ||||
|  | @ -331,7 +336,6 @@ void RoomMember::RoomMemberImpl::HandleJoinPacket(const ENetEvent* event) { | |||
| 
 | ||||
|     // Parse the MAC Address from the packet
 | ||||
|     packet >> mac_address; | ||||
|     SetState(State::Joined); | ||||
| } | ||||
| 
 | ||||
| void RoomMember::RoomMemberImpl::HandleWifiPackets(const ENetEvent* event) { | ||||
|  |  | |||
|  | @ -59,7 +59,8 @@ public: | |||
|         Uninitialized, ///< Not initialized
 | ||||
|         Idle,          ///< Default state (i.e. not connected)
 | ||||
|         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 { | ||||
|  | @ -270,6 +271,8 @@ static const char* GetStateStr(const RoomMember::State& s) { | |||
|         return "Joining"; | ||||
|     case RoomMember::State::Joined: | ||||
|         return "Joined"; | ||||
|     case RoomMember::State::Moderator: | ||||
|         return "Moderator"; | ||||
|     } | ||||
|     return "Unknown"; | ||||
| } | ||||
|  |  | |||
|  | @ -13,6 +13,7 @@ struct UserData { | |||
|     std::string username; | ||||
|     std::string display_name; | ||||
|     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")) { | ||||
|         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; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue