|
|
|
@ -4,9 +4,9 @@ |
|
|
|
|
|
|
|
#include <algorithm>
|
|
|
|
#include <atomic>
|
|
|
|
#include <mutex>
|
|
|
|
#include <random>
|
|
|
|
#include <thread>
|
|
|
|
#include <vector>
|
|
|
|
#include "enet/enet.h"
|
|
|
|
#include "network/packet.h"
|
|
|
|
#include "network/room.h"
|
|
|
|
@ -29,12 +29,14 @@ public: |
|
|
|
|
|
|
|
struct Member { |
|
|
|
std::string nickname; ///< The nickname of the member.
|
|
|
|
std::string game_name; ///< The current game of the member
|
|
|
|
GameInfo game_info; ///< The current game of the member
|
|
|
|
MacAddress mac_address; ///< The assigned mac address of the member.
|
|
|
|
ENetPeer* peer; ///< The remote peer.
|
|
|
|
}; |
|
|
|
using MemberList = std::vector<Member>; |
|
|
|
MemberList members; ///< Information about the members of this room.
|
|
|
|
MemberList members; ///< Information about the members of this room
|
|
|
|
mutable std::mutex member_mutex; ///< Mutex for locking the members list
|
|
|
|
/// This should be a std::shared_mutex as soon as C++17 is supported
|
|
|
|
|
|
|
|
RoomImpl() |
|
|
|
: random_gen(std::random_device()()), NintendoOUI{0x00, 0x1F, 0x32, 0x00, 0x00, 0x00} {} |
|
|
|
@ -147,7 +149,7 @@ void Room::RoomImpl::ServerLoop() { |
|
|
|
case IdJoinRequest: |
|
|
|
HandleJoinRequest(&event); |
|
|
|
break; |
|
|
|
case IdSetGameName: |
|
|
|
case IdSetGameInfo: |
|
|
|
HandleGameNamePacket(&event); |
|
|
|
break; |
|
|
|
case IdWifiPacket: |
|
|
|
@ -213,7 +215,10 @@ void Room::RoomImpl::HandleJoinRequest(const ENetEvent* event) { |
|
|
|
member.nickname = nickname; |
|
|
|
member.peer = event->peer; |
|
|
|
|
|
|
|
members.push_back(std::move(member)); |
|
|
|
{ |
|
|
|
std::lock_guard<std::mutex> lock(member_mutex); |
|
|
|
members.push_back(std::move(member)); |
|
|
|
} |
|
|
|
|
|
|
|
// Notify everyone that the room information has changed.
|
|
|
|
BroadcastRoomInformation(); |
|
|
|
@ -223,12 +228,14 @@ void Room::RoomImpl::HandleJoinRequest(const ENetEvent* event) { |
|
|
|
bool Room::RoomImpl::IsValidNickname(const std::string& nickname) const { |
|
|
|
// A nickname is valid if it is not already taken by anybody else in the room.
|
|
|
|
// TODO(B3N30): Check for empty names, spaces, etc.
|
|
|
|
std::lock_guard<std::mutex> lock(member_mutex); |
|
|
|
return std::all_of(members.begin(), members.end(), |
|
|
|
[&nickname](const auto& member) { return member.nickname != nickname; }); |
|
|
|
} |
|
|
|
|
|
|
|
bool Room::RoomImpl::IsValidMacAddress(const MacAddress& address) const { |
|
|
|
// A MAC address is valid if it is not already taken by anybody else in the room.
|
|
|
|
std::lock_guard<std::mutex> lock(member_mutex); |
|
|
|
return std::all_of(members.begin(), members.end(), |
|
|
|
[&address](const auto& member) { return member.mac_address != address; }); |
|
|
|
} |
|
|
|
@ -279,6 +286,7 @@ void Room::RoomImpl::SendCloseMessage() { |
|
|
|
packet << static_cast<u8>(IdCloseRoom); |
|
|
|
ENetPacket* enet_packet = |
|
|
|
enet_packet_create(packet.GetData(), packet.GetDataSize(), ENET_PACKET_FLAG_RELIABLE); |
|
|
|
std::lock_guard<std::mutex> lock(member_mutex); |
|
|
|
for (auto& member : members) { |
|
|
|
enet_peer_send(member.peer, 0, enet_packet); |
|
|
|
} |
|
|
|
@ -295,10 +303,14 @@ void Room::RoomImpl::BroadcastRoomInformation() { |
|
|
|
packet << room_information.member_slots; |
|
|
|
|
|
|
|
packet << static_cast<u32>(members.size()); |
|
|
|
for (const auto& member : members) { |
|
|
|
packet << member.nickname; |
|
|
|
packet << member.mac_address; |
|
|
|
packet << member.game_name; |
|
|
|
{ |
|
|
|
std::lock_guard<std::mutex> lock(member_mutex); |
|
|
|
for (const auto& member : members) { |
|
|
|
packet << member.nickname; |
|
|
|
packet << member.mac_address; |
|
|
|
packet << member.game_info.name; |
|
|
|
packet << member.game_info.id; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
ENetPacket* enet_packet = |
|
|
|
@ -335,11 +347,13 @@ void Room::RoomImpl::HandleWifiPacket(const ENetEvent* event) { |
|
|
|
ENET_PACKET_FLAG_RELIABLE); |
|
|
|
|
|
|
|
if (destination_address == BroadcastMac) { // Send the data to everyone except the sender
|
|
|
|
std::lock_guard<std::mutex> lock(member_mutex); |
|
|
|
for (const auto& member : members) { |
|
|
|
if (member.peer != event->peer) |
|
|
|
enet_peer_send(member.peer, 0, enet_packet); |
|
|
|
} |
|
|
|
} else { // Send the data only to the destination client
|
|
|
|
std::lock_guard<std::mutex> lock(member_mutex); |
|
|
|
auto member = std::find_if(members.begin(), members.end(), |
|
|
|
[destination_address](const Member& member) -> bool { |
|
|
|
return member.mac_address == destination_address; |
|
|
|
@ -361,6 +375,8 @@ void Room::RoomImpl::HandleChatPacket(const ENetEvent* event) { |
|
|
|
auto CompareNetworkAddress = [event](const Member member) -> bool { |
|
|
|
return member.peer == event->peer; |
|
|
|
}; |
|
|
|
|
|
|
|
std::lock_guard<std::mutex> lock(member_mutex); |
|
|
|
const auto sending_member = std::find_if(members.begin(), members.end(), CompareNetworkAddress); |
|
|
|
if (sending_member == members.end()) { |
|
|
|
return; // Received a chat message from a unknown sender
|
|
|
|
@ -385,22 +401,32 @@ void Room::RoomImpl::HandleGameNamePacket(const ENetEvent* event) { |
|
|
|
in_packet.Append(event->packet->data, event->packet->dataLength); |
|
|
|
|
|
|
|
in_packet.IgnoreBytes(sizeof(u8)); // Igonore the message type
|
|
|
|
std::string game_name; |
|
|
|
in_packet >> game_name; |
|
|
|
auto member = |
|
|
|
std::find_if(members.begin(), members.end(), |
|
|
|
[event](const Member& member) -> bool { return member.peer == event->peer; }); |
|
|
|
if (member != members.end()) { |
|
|
|
member->game_name = game_name; |
|
|
|
BroadcastRoomInformation(); |
|
|
|
GameInfo game_info; |
|
|
|
in_packet >> game_info.name; |
|
|
|
in_packet >> game_info.id; |
|
|
|
|
|
|
|
{ |
|
|
|
std::lock_guard<std::mutex> lock(member_mutex); |
|
|
|
auto member = |
|
|
|
std::find_if(members.begin(), members.end(), [event](const Member& member) -> bool { |
|
|
|
return member.peer == event->peer; |
|
|
|
}); |
|
|
|
if (member != members.end()) { |
|
|
|
member->game_info = game_info; |
|
|
|
} |
|
|
|
} |
|
|
|
BroadcastRoomInformation(); |
|
|
|
} |
|
|
|
|
|
|
|
void Room::RoomImpl::HandleClientDisconnection(ENetPeer* client) { |
|
|
|
// Remove the client from the members list.
|
|
|
|
members.erase(std::remove_if(members.begin(), members.end(), |
|
|
|
[client](const Member& member) { return member.peer == client; }), |
|
|
|
members.end()); |
|
|
|
{ |
|
|
|
std::lock_guard<std::mutex> lock(member_mutex); |
|
|
|
members.erase( |
|
|
|
std::remove_if(members.begin(), members.end(), |
|
|
|
[client](const Member& member) { return member.peer == client; }), |
|
|
|
members.end()); |
|
|
|
} |
|
|
|
|
|
|
|
// Announce the change to all clients.
|
|
|
|
enet_peer_disconnect(client, 0); |
|
|
|
@ -437,6 +463,19 @@ const RoomInformation& Room::GetRoomInformation() const { |
|
|
|
return room_impl->room_information; |
|
|
|
} |
|
|
|
|
|
|
|
std::vector<Room::Member> Room::GetRoomMemberList() const { |
|
|
|
std::vector<Room::Member> member_list; |
|
|
|
std::lock_guard<std::mutex> lock(room_impl->member_mutex); |
|
|
|
for (const auto& member_impl : room_impl->members) { |
|
|
|
Member member; |
|
|
|
member.nickname = member_impl.nickname; |
|
|
|
member.mac_address = member_impl.mac_address; |
|
|
|
member.game_info = member_impl.game_info; |
|
|
|
member_list.push_back(member); |
|
|
|
} |
|
|
|
return member_list; |
|
|
|
}; |
|
|
|
|
|
|
|
void Room::Destroy() { |
|
|
|
room_impl->state = State::Closed; |
|
|
|
room_impl->room_thread->join(); |
|
|
|
@ -447,7 +486,10 @@ void Room::Destroy() { |
|
|
|
} |
|
|
|
room_impl->room_information = {}; |
|
|
|
room_impl->server = nullptr; |
|
|
|
room_impl->members.clear(); |
|
|
|
{ |
|
|
|
std::lock_guard<std::mutex> lock(room_impl->member_mutex); |
|
|
|
room_impl->members.clear(); |
|
|
|
} |
|
|
|
room_impl->room_information.member_slots = 0; |
|
|
|
room_impl->room_information.name.clear(); |
|
|
|
} |
|
|
|
|