|
|
|
@ -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;
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
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());
|
|
|
|
|
{
|
|
|
|
|
std::lock_guard<std::mutex> lock(member_mutex);
|
|
|
|
|
for (const auto& member : members) {
|
|
|
|
|
packet << member.nickname;
|
|
|
|
|
packet << member.mac_address;
|
|
|
|
|
packet << member.game_name;
|
|
|
|
|
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;
|
|
|
|
|
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; });
|
|
|
|
|
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();
|
|
|
|
|
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(),
|
|
|
|
|
{
|
|
|
|
|
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;
|
|
|
|
|
{
|
|
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|