mirror of
https://github.com/Lime3DS/Lime3DS
synced 2024-12-26 09:02:44 -06:00
Added missing parts in libnetwork (#2838)
* Network: Set and send the game information over enet Added Callbacks for RoomMember and GetMemberList to Room in preparation for web_services.
This commit is contained in:
parent
21204ba488
commit
5d0a1e7efd
9 changed files with 310 additions and 37 deletions
|
@ -388,7 +388,7 @@ set(HEADERS
|
||||||
|
|
||||||
create_directory_groups(${SRCS} ${HEADERS})
|
create_directory_groups(${SRCS} ${HEADERS})
|
||||||
add_library(core STATIC ${SRCS} ${HEADERS})
|
add_library(core STATIC ${SRCS} ${HEADERS})
|
||||||
target_link_libraries(core PUBLIC common PRIVATE audio_core video_core)
|
target_link_libraries(core PUBLIC common PRIVATE audio_core network video_core)
|
||||||
target_link_libraries(core PUBLIC Boost::boost PRIVATE cryptopp dynarmic fmt)
|
target_link_libraries(core PUBLIC Boost::boost PRIVATE cryptopp dynarmic fmt)
|
||||||
if (ENABLE_WEB_SERVICE)
|
if (ENABLE_WEB_SERVICE)
|
||||||
target_link_libraries(core PUBLIC json-headers web_service)
|
target_link_libraries(core PUBLIC json-headers web_service)
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#include "core/loader/loader.h"
|
#include "core/loader/loader.h"
|
||||||
#include "core/memory_setup.h"
|
#include "core/memory_setup.h"
|
||||||
#include "core/settings.h"
|
#include "core/settings.h"
|
||||||
|
#include "network/network.h"
|
||||||
#include "video_core/video_core.h"
|
#include "video_core/video_core.h"
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
|
@ -188,6 +189,10 @@ void System::Shutdown() {
|
||||||
cpu_core = nullptr;
|
cpu_core = nullptr;
|
||||||
app_loader = nullptr;
|
app_loader = nullptr;
|
||||||
telemetry_session = nullptr;
|
telemetry_session = nullptr;
|
||||||
|
if (auto room_member = Network::GetRoomMember().lock()) {
|
||||||
|
Network::GameInfo game_info{};
|
||||||
|
room_member->SendGameInfo(game_info);
|
||||||
|
}
|
||||||
|
|
||||||
LOG_DEBUG(Core, "Shutdown OK");
|
LOG_DEBUG(Core, "Shutdown OK");
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include "core/loader/ncch.h"
|
#include "core/loader/ncch.h"
|
||||||
#include "core/loader/smdh.h"
|
#include "core/loader/smdh.h"
|
||||||
#include "core/memory.h"
|
#include "core/memory.h"
|
||||||
|
#include "network/network.h"
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Loader namespace
|
// Loader namespace
|
||||||
|
@ -350,6 +351,13 @@ ResultStatus AppLoader_NCCH::Load() {
|
||||||
|
|
||||||
Core::Telemetry().AddField(Telemetry::FieldType::Session, "ProgramId", program_id);
|
Core::Telemetry().AddField(Telemetry::FieldType::Session, "ProgramId", program_id);
|
||||||
|
|
||||||
|
if (auto room_member = Network::GetRoomMember().lock()) {
|
||||||
|
Network::GameInfo game_info;
|
||||||
|
ReadTitle(game_info.name);
|
||||||
|
game_info.id = ncch_header.program_id;
|
||||||
|
room_member->SendGameInfo(game_info);
|
||||||
|
}
|
||||||
|
|
||||||
is_loaded = true; // Set state to loaded
|
is_loaded = true; // Set state to loaded
|
||||||
|
|
||||||
result = LoadExec(); // Load the executable into memory for booting
|
result = LoadExec(); // Load the executable into memory for booting
|
||||||
|
|
|
@ -13,6 +13,18 @@
|
||||||
|
|
||||||
namespace Network {
|
namespace Network {
|
||||||
|
|
||||||
|
#ifndef htonll
|
||||||
|
u64 htonll(u64 x) {
|
||||||
|
return ((1 == htonl(1)) ? (x) : ((uint64_t)htonl((x)&0xFFFFFFFF) << 32) | htonl((x) >> 32));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef ntohll
|
||||||
|
u64 ntohll(u64 x) {
|
||||||
|
return ((1 == ntohl(1)) ? (x) : ((uint64_t)ntohl((x)&0xFFFFFFFF) << 32) | ntohl((x) >> 32));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void Packet::Append(const void* in_data, std::size_t size_in_bytes) {
|
void Packet::Append(const void* in_data, std::size_t size_in_bytes) {
|
||||||
if (in_data && (size_in_bytes > 0)) {
|
if (in_data && (size_in_bytes > 0)) {
|
||||||
std::size_t start = data.size();
|
std::size_t start = data.size();
|
||||||
|
@ -100,6 +112,20 @@ Packet& Packet::operator>>(u32& out_data) {
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Packet& Packet::operator>>(s64& out_data) {
|
||||||
|
s64 value;
|
||||||
|
Read(&value, sizeof(value));
|
||||||
|
out_data = ntohll(value);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Packet& Packet::operator>>(u64& out_data) {
|
||||||
|
u64 value;
|
||||||
|
Read(&value, sizeof(value));
|
||||||
|
out_data = ntohll(value);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
Packet& Packet::operator>>(float& out_data) {
|
Packet& Packet::operator>>(float& out_data) {
|
||||||
Read(&out_data, sizeof(out_data));
|
Read(&out_data, sizeof(out_data));
|
||||||
return *this;
|
return *this;
|
||||||
|
@ -183,6 +209,18 @@ Packet& Packet::operator<<(u32 in_data) {
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Packet& Packet::operator<<(s64 in_data) {
|
||||||
|
s64 toWrite = htonll(in_data);
|
||||||
|
Append(&toWrite, sizeof(toWrite));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Packet& Packet::operator<<(u64 in_data) {
|
||||||
|
u64 toWrite = htonll(in_data);
|
||||||
|
Append(&toWrite, sizeof(toWrite));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
Packet& Packet::operator<<(float in_data) {
|
Packet& Packet::operator<<(float in_data) {
|
||||||
Append(&in_data, sizeof(in_data));
|
Append(&in_data, sizeof(in_data));
|
||||||
return *this;
|
return *this;
|
||||||
|
|
|
@ -72,6 +72,8 @@ public:
|
||||||
Packet& operator>>(u16& out_data);
|
Packet& operator>>(u16& out_data);
|
||||||
Packet& operator>>(s32& out_data);
|
Packet& operator>>(s32& out_data);
|
||||||
Packet& operator>>(u32& out_data);
|
Packet& operator>>(u32& out_data);
|
||||||
|
Packet& operator>>(s64& out_data);
|
||||||
|
Packet& operator>>(u64& out_data);
|
||||||
Packet& operator>>(float& out_data);
|
Packet& operator>>(float& out_data);
|
||||||
Packet& operator>>(double& out_data);
|
Packet& operator>>(double& out_data);
|
||||||
Packet& operator>>(char* out_data);
|
Packet& operator>>(char* out_data);
|
||||||
|
@ -89,6 +91,8 @@ public:
|
||||||
Packet& operator<<(u16 in_data);
|
Packet& operator<<(u16 in_data);
|
||||||
Packet& operator<<(s32 in_data);
|
Packet& operator<<(s32 in_data);
|
||||||
Packet& operator<<(u32 in_data);
|
Packet& operator<<(u32 in_data);
|
||||||
|
Packet& operator<<(s64 in_data);
|
||||||
|
Packet& operator<<(u64 in_data);
|
||||||
Packet& operator<<(float in_data);
|
Packet& operator<<(float in_data);
|
||||||
Packet& operator<<(double in_data);
|
Packet& operator<<(double in_data);
|
||||||
Packet& operator<<(const char* in_data);
|
Packet& operator<<(const char* in_data);
|
||||||
|
|
|
@ -4,9 +4,9 @@
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
#include <mutex>
|
||||||
#include <random>
|
#include <random>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <vector>
|
|
||||||
#include "enet/enet.h"
|
#include "enet/enet.h"
|
||||||
#include "network/packet.h"
|
#include "network/packet.h"
|
||||||
#include "network/room.h"
|
#include "network/room.h"
|
||||||
|
@ -29,12 +29,14 @@ public:
|
||||||
|
|
||||||
struct Member {
|
struct Member {
|
||||||
std::string nickname; ///< The nickname of the 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.
|
MacAddress mac_address; ///< The assigned mac address of the member.
|
||||||
ENetPeer* peer; ///< The remote peer.
|
ENetPeer* peer; ///< The remote peer.
|
||||||
};
|
};
|
||||||
using MemberList = std::vector<Member>;
|
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()
|
RoomImpl()
|
||||||
: random_gen(std::random_device()()), NintendoOUI{0x00, 0x1F, 0x32, 0x00, 0x00, 0x00} {}
|
: random_gen(std::random_device()()), NintendoOUI{0x00, 0x1F, 0x32, 0x00, 0x00, 0x00} {}
|
||||||
|
@ -147,7 +149,7 @@ void Room::RoomImpl::ServerLoop() {
|
||||||
case IdJoinRequest:
|
case IdJoinRequest:
|
||||||
HandleJoinRequest(&event);
|
HandleJoinRequest(&event);
|
||||||
break;
|
break;
|
||||||
case IdSetGameName:
|
case IdSetGameInfo:
|
||||||
HandleGameNamePacket(&event);
|
HandleGameNamePacket(&event);
|
||||||
break;
|
break;
|
||||||
case IdWifiPacket:
|
case IdWifiPacket:
|
||||||
|
@ -213,7 +215,10 @@ void Room::RoomImpl::HandleJoinRequest(const ENetEvent* event) {
|
||||||
member.nickname = nickname;
|
member.nickname = nickname;
|
||||||
member.peer = event->peer;
|
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.
|
// Notify everyone that the room information has changed.
|
||||||
BroadcastRoomInformation();
|
BroadcastRoomInformation();
|
||||||
|
@ -223,12 +228,14 @@ void Room::RoomImpl::HandleJoinRequest(const ENetEvent* event) {
|
||||||
bool Room::RoomImpl::IsValidNickname(const std::string& nickname) const {
|
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.
|
// A nickname is valid if it is not already taken by anybody else in the room.
|
||||||
// TODO(B3N30): Check for empty names, spaces, etc.
|
// TODO(B3N30): Check for empty names, spaces, etc.
|
||||||
|
std::lock_guard<std::mutex> lock(member_mutex);
|
||||||
return std::all_of(members.begin(), members.end(),
|
return std::all_of(members.begin(), members.end(),
|
||||||
[&nickname](const auto& member) { return member.nickname != nickname; });
|
[&nickname](const auto& member) { return member.nickname != nickname; });
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Room::RoomImpl::IsValidMacAddress(const MacAddress& address) const {
|
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.
|
// 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(),
|
return std::all_of(members.begin(), members.end(),
|
||||||
[&address](const auto& member) { return member.mac_address != address; });
|
[&address](const auto& member) { return member.mac_address != address; });
|
||||||
}
|
}
|
||||||
|
@ -279,6 +286,7 @@ void Room::RoomImpl::SendCloseMessage() {
|
||||||
packet << static_cast<u8>(IdCloseRoom);
|
packet << static_cast<u8>(IdCloseRoom);
|
||||||
ENetPacket* enet_packet =
|
ENetPacket* enet_packet =
|
||||||
enet_packet_create(packet.GetData(), packet.GetDataSize(), ENET_PACKET_FLAG_RELIABLE);
|
enet_packet_create(packet.GetData(), packet.GetDataSize(), ENET_PACKET_FLAG_RELIABLE);
|
||||||
|
std::lock_guard<std::mutex> lock(member_mutex);
|
||||||
for (auto& member : members) {
|
for (auto& member : members) {
|
||||||
enet_peer_send(member.peer, 0, enet_packet);
|
enet_peer_send(member.peer, 0, enet_packet);
|
||||||
}
|
}
|
||||||
|
@ -295,10 +303,14 @@ void Room::RoomImpl::BroadcastRoomInformation() {
|
||||||
packet << room_information.member_slots;
|
packet << room_information.member_slots;
|
||||||
|
|
||||||
packet << static_cast<u32>(members.size());
|
packet << static_cast<u32>(members.size());
|
||||||
for (const auto& member : members) {
|
{
|
||||||
packet << member.nickname;
|
std::lock_guard<std::mutex> lock(member_mutex);
|
||||||
packet << member.mac_address;
|
for (const auto& member : members) {
|
||||||
packet << member.game_name;
|
packet << member.nickname;
|
||||||
|
packet << member.mac_address;
|
||||||
|
packet << member.game_info.name;
|
||||||
|
packet << member.game_info.id;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ENetPacket* enet_packet =
|
ENetPacket* enet_packet =
|
||||||
|
@ -335,11 +347,13 @@ void Room::RoomImpl::HandleWifiPacket(const ENetEvent* event) {
|
||||||
ENET_PACKET_FLAG_RELIABLE);
|
ENET_PACKET_FLAG_RELIABLE);
|
||||||
|
|
||||||
if (destination_address == BroadcastMac) { // Send the data to everyone except the sender
|
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) {
|
for (const auto& member : members) {
|
||||||
if (member.peer != event->peer)
|
if (member.peer != event->peer)
|
||||||
enet_peer_send(member.peer, 0, enet_packet);
|
enet_peer_send(member.peer, 0, enet_packet);
|
||||||
}
|
}
|
||||||
} else { // Send the data only to the destination client
|
} 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(),
|
auto member = std::find_if(members.begin(), members.end(),
|
||||||
[destination_address](const Member& member) -> bool {
|
[destination_address](const Member& member) -> bool {
|
||||||
return member.mac_address == destination_address;
|
return member.mac_address == destination_address;
|
||||||
|
@ -361,6 +375,8 @@ void Room::RoomImpl::HandleChatPacket(const ENetEvent* event) {
|
||||||
auto CompareNetworkAddress = [event](const Member member) -> bool {
|
auto CompareNetworkAddress = [event](const Member member) -> bool {
|
||||||
return member.peer == event->peer;
|
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);
|
const auto sending_member = std::find_if(members.begin(), members.end(), CompareNetworkAddress);
|
||||||
if (sending_member == members.end()) {
|
if (sending_member == members.end()) {
|
||||||
return; // Received a chat message from a unknown sender
|
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.Append(event->packet->data, event->packet->dataLength);
|
||||||
|
|
||||||
in_packet.IgnoreBytes(sizeof(u8)); // Igonore the message type
|
in_packet.IgnoreBytes(sizeof(u8)); // Igonore the message type
|
||||||
std::string game_name;
|
GameInfo game_info;
|
||||||
in_packet >> game_name;
|
in_packet >> game_info.name;
|
||||||
auto member =
|
in_packet >> game_info.id;
|
||||||
std::find_if(members.begin(), members.end(),
|
|
||||||
[event](const Member& member) -> bool { return member.peer == event->peer; });
|
{
|
||||||
if (member != members.end()) {
|
std::lock_guard<std::mutex> lock(member_mutex);
|
||||||
member->game_name = game_name;
|
auto member =
|
||||||
BroadcastRoomInformation();
|
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) {
|
void Room::RoomImpl::HandleClientDisconnection(ENetPeer* client) {
|
||||||
// Remove the client from the members list.
|
// Remove the client from the members list.
|
||||||
members.erase(std::remove_if(members.begin(), members.end(),
|
{
|
||||||
[client](const Member& member) { return member.peer == client; }),
|
std::lock_guard<std::mutex> lock(member_mutex);
|
||||||
members.end());
|
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.
|
// Announce the change to all clients.
|
||||||
enet_peer_disconnect(client, 0);
|
enet_peer_disconnect(client, 0);
|
||||||
|
@ -437,6 +463,19 @@ const RoomInformation& Room::GetRoomInformation() const {
|
||||||
return room_impl->room_information;
|
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() {
|
void Room::Destroy() {
|
||||||
room_impl->state = State::Closed;
|
room_impl->state = State::Closed;
|
||||||
room_impl->room_thread->join();
|
room_impl->room_thread->join();
|
||||||
|
@ -447,7 +486,10 @@ void Room::Destroy() {
|
||||||
}
|
}
|
||||||
room_impl->room_information = {};
|
room_impl->room_information = {};
|
||||||
room_impl->server = nullptr;
|
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.member_slots = 0;
|
||||||
room_impl->room_information.name.clear();
|
room_impl->room_information.name.clear();
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
|
||||||
namespace Network {
|
namespace Network {
|
||||||
|
@ -21,6 +22,11 @@ struct RoomInformation {
|
||||||
u32 member_slots; ///< Maximum number of members in this room
|
u32 member_slots; ///< Maximum number of members in this room
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct GameInfo {
|
||||||
|
std::string name{""};
|
||||||
|
u64 id{0};
|
||||||
|
};
|
||||||
|
|
||||||
using MacAddress = std::array<u8, 6>;
|
using MacAddress = std::array<u8, 6>;
|
||||||
/// A special MAC address that tells the room we're joining to assign us a MAC address
|
/// A special MAC address that tells the room we're joining to assign us a MAC address
|
||||||
/// automatically.
|
/// automatically.
|
||||||
|
@ -34,7 +40,7 @@ enum RoomMessageTypes : u8 {
|
||||||
IdJoinRequest = 1,
|
IdJoinRequest = 1,
|
||||||
IdJoinSuccess,
|
IdJoinSuccess,
|
||||||
IdRoomInformation,
|
IdRoomInformation,
|
||||||
IdSetGameName,
|
IdSetGameInfo,
|
||||||
IdWifiPacket,
|
IdWifiPacket,
|
||||||
IdChatMessage,
|
IdChatMessage,
|
||||||
IdNameCollision,
|
IdNameCollision,
|
||||||
|
@ -51,6 +57,12 @@ public:
|
||||||
Closed, ///< The room is not opened and can not accept connections.
|
Closed, ///< The room is not opened and can not accept connections.
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct Member {
|
||||||
|
std::string nickname; ///< The nickname of the member.
|
||||||
|
GameInfo game_info; ///< The current game of the member
|
||||||
|
MacAddress mac_address; ///< The assigned mac address of the member.
|
||||||
|
};
|
||||||
|
|
||||||
Room();
|
Room();
|
||||||
~Room();
|
~Room();
|
||||||
|
|
||||||
|
@ -64,6 +76,11 @@ public:
|
||||||
*/
|
*/
|
||||||
const RoomInformation& GetRoomInformation() const;
|
const RoomInformation& GetRoomInformation() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a list of the mbmers connected to the room.
|
||||||
|
*/
|
||||||
|
std::vector<Member> GetRoomMemberList() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates the socket for this room. Will bind to default address if
|
* Creates the socket for this room. Will bind to default address if
|
||||||
* server is empty string.
|
* server is empty string.
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
#include <set>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "enet/enet.h"
|
#include "enet/enet.h"
|
||||||
|
@ -25,6 +26,9 @@ public:
|
||||||
/// Information about the room we're connected to.
|
/// Information about the room we're connected to.
|
||||||
RoomInformation room_information;
|
RoomInformation room_information;
|
||||||
|
|
||||||
|
/// The current game name, id and version
|
||||||
|
GameInfo current_game_info;
|
||||||
|
|
||||||
std::atomic<State> state{State::Idle}; ///< Current state of the RoomMember.
|
std::atomic<State> state{State::Idle}; ///< Current state of the RoomMember.
|
||||||
void SetState(const State new_state);
|
void SetState(const State new_state);
|
||||||
bool IsConnected() const;
|
bool IsConnected() const;
|
||||||
|
@ -37,6 +41,24 @@ public:
|
||||||
std::unique_ptr<std::thread> loop_thread;
|
std::unique_ptr<std::thread> loop_thread;
|
||||||
std::mutex send_list_mutex; ///< Mutex that controls access to the `send_list` variable.
|
std::mutex send_list_mutex; ///< Mutex that controls access to the `send_list` variable.
|
||||||
std::list<Packet> send_list; ///< A list that stores all packets to send the async
|
std::list<Packet> send_list; ///< A list that stores all packets to send the async
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
using CallbackSet = std::set<CallbackHandle<T>>;
|
||||||
|
std::mutex callback_mutex; ///< The mutex used for handling callbacks
|
||||||
|
|
||||||
|
class Callbacks {
|
||||||
|
public:
|
||||||
|
template <typename T>
|
||||||
|
CallbackSet<T>& Get();
|
||||||
|
|
||||||
|
private:
|
||||||
|
CallbackSet<WifiPacket> callback_set_wifi_packet;
|
||||||
|
CallbackSet<ChatEntry> callback_set_chat_messages;
|
||||||
|
CallbackSet<RoomInformation> callback_set_room_information;
|
||||||
|
CallbackSet<State> callback_set_state;
|
||||||
|
};
|
||||||
|
Callbacks callbacks; ///< All CallbackSets to all events
|
||||||
|
|
||||||
void MemberLoop();
|
void MemberLoop();
|
||||||
|
|
||||||
void StartLoop();
|
void StartLoop();
|
||||||
|
@ -84,12 +106,20 @@ public:
|
||||||
* Disconnects the RoomMember from the Room
|
* Disconnects the RoomMember from the Room
|
||||||
*/
|
*/
|
||||||
void Disconnect();
|
void Disconnect();
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void Invoke(const T& data);
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
CallbackHandle<T> Bind(std::function<void(const T&)> callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
// RoomMemberImpl
|
// RoomMemberImpl
|
||||||
void RoomMember::RoomMemberImpl::SetState(const State new_state) {
|
void RoomMember::RoomMemberImpl::SetState(const State new_state) {
|
||||||
state = new_state;
|
if (state != new_state) {
|
||||||
// TODO(B3N30): Invoke the callback functions
|
state = new_state;
|
||||||
|
Invoke<State>(state);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RoomMember::RoomMemberImpl::IsConnected() const {
|
bool RoomMember::RoomMemberImpl::IsConnected() const {
|
||||||
|
@ -195,9 +225,10 @@ void RoomMember::RoomMemberImpl::HandleRoomInformationPacket(const ENetEvent* ev
|
||||||
for (auto& member : member_information) {
|
for (auto& member : member_information) {
|
||||||
packet >> member.nickname;
|
packet >> member.nickname;
|
||||||
packet >> member.mac_address;
|
packet >> member.mac_address;
|
||||||
packet >> member.game_name;
|
packet >> member.game_info.name;
|
||||||
|
packet >> member.game_info.id;
|
||||||
}
|
}
|
||||||
// TODO(B3N30): Invoke callbacks
|
Invoke(room_information);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RoomMember::RoomMemberImpl::HandleJoinPacket(const ENetEvent* event) {
|
void RoomMember::RoomMemberImpl::HandleJoinPacket(const ENetEvent* event) {
|
||||||
|
@ -209,7 +240,7 @@ 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;
|
||||||
// TODO(B3N30): Invoke callbacks
|
SetState(State::Joined);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RoomMember::RoomMemberImpl::HandleWifiPackets(const ENetEvent* event) {
|
void RoomMember::RoomMemberImpl::HandleWifiPackets(const ENetEvent* event) {
|
||||||
|
@ -235,7 +266,7 @@ void RoomMember::RoomMemberImpl::HandleWifiPackets(const ENetEvent* event) {
|
||||||
|
|
||||||
packet >> wifi_packet.data;
|
packet >> wifi_packet.data;
|
||||||
|
|
||||||
// TODO(B3N30): Invoke callbacks
|
Invoke<WifiPacket>(wifi_packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RoomMember::RoomMemberImpl::HandleChatPacket(const ENetEvent* event) {
|
void RoomMember::RoomMemberImpl::HandleChatPacket(const ENetEvent* event) {
|
||||||
|
@ -248,7 +279,7 @@ void RoomMember::RoomMemberImpl::HandleChatPacket(const ENetEvent* event) {
|
||||||
ChatEntry chat_entry{};
|
ChatEntry chat_entry{};
|
||||||
packet >> chat_entry.nickname;
|
packet >> chat_entry.nickname;
|
||||||
packet >> chat_entry.message;
|
packet >> chat_entry.message;
|
||||||
// TODO(B3N30): Invoke callbacks
|
Invoke<ChatEntry>(chat_entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RoomMember::RoomMemberImpl::Disconnect() {
|
void RoomMember::RoomMemberImpl::Disconnect() {
|
||||||
|
@ -276,6 +307,46 @@ void RoomMember::RoomMemberImpl::Disconnect() {
|
||||||
server = nullptr;
|
server = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
RoomMember::RoomMemberImpl::CallbackSet<WifiPacket>& RoomMember::RoomMemberImpl::Callbacks::Get() {
|
||||||
|
return callback_set_wifi_packet;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
RoomMember::RoomMemberImpl::CallbackSet<RoomMember::State>&
|
||||||
|
RoomMember::RoomMemberImpl::Callbacks::Get() {
|
||||||
|
return callback_set_state;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
RoomMember::RoomMemberImpl::CallbackSet<RoomInformation>&
|
||||||
|
RoomMember::RoomMemberImpl::Callbacks::Get() {
|
||||||
|
return callback_set_room_information;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
RoomMember::RoomMemberImpl::CallbackSet<ChatEntry>& RoomMember::RoomMemberImpl::Callbacks::Get() {
|
||||||
|
return callback_set_chat_messages;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void RoomMember::RoomMemberImpl::Invoke(const T& data) {
|
||||||
|
std::lock_guard<std::mutex> lock(callback_mutex);
|
||||||
|
CallbackSet<T> callback_set = callbacks.Get<T>();
|
||||||
|
for (auto const& callback : callback_set)
|
||||||
|
(*callback)(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
RoomMember::CallbackHandle<T> RoomMember::RoomMemberImpl::Bind(
|
||||||
|
std::function<void(const T&)> callback) {
|
||||||
|
std::lock_guard<std::mutex> lock(callback_mutex);
|
||||||
|
CallbackHandle<T> handle;
|
||||||
|
handle = std::make_shared<std::function<void(const T&)>>(callback);
|
||||||
|
callbacks.Get<T>().insert(handle);
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
|
||||||
// RoomMember
|
// RoomMember
|
||||||
RoomMember::RoomMember() : room_member_impl{std::make_unique<RoomMemberImpl>()} {
|
RoomMember::RoomMember() : room_member_impl{std::make_unique<RoomMemberImpl>()} {
|
||||||
room_member_impl->client = enet_host_create(nullptr, 1, NumChannels, 0, 0);
|
room_member_impl->client = enet_host_create(nullptr, 1, NumChannels, 0, 0);
|
||||||
|
@ -339,6 +410,7 @@ void RoomMember::Join(const std::string& nick, const char* server_addr, u16 serv
|
||||||
room_member_impl->SetState(State::Joining);
|
room_member_impl->SetState(State::Joining);
|
||||||
room_member_impl->StartLoop();
|
room_member_impl->StartLoop();
|
||||||
room_member_impl->SendJoinRequest(nick, preferred_mac);
|
room_member_impl->SendJoinRequest(nick, preferred_mac);
|
||||||
|
SendGameInfo(room_member_impl->current_game_info);
|
||||||
} else {
|
} else {
|
||||||
room_member_impl->SetState(State::CouldNotConnect);
|
room_member_impl->SetState(State::CouldNotConnect);
|
||||||
}
|
}
|
||||||
|
@ -366,17 +438,53 @@ void RoomMember::SendChatMessage(const std::string& message) {
|
||||||
room_member_impl->Send(std::move(packet));
|
room_member_impl->Send(std::move(packet));
|
||||||
}
|
}
|
||||||
|
|
||||||
void RoomMember::SendGameName(const std::string& game_name) {
|
void RoomMember::SendGameInfo(const GameInfo& game_info) {
|
||||||
|
room_member_impl->current_game_info = game_info;
|
||||||
|
if (!IsConnected())
|
||||||
|
return;
|
||||||
|
|
||||||
Packet packet;
|
Packet packet;
|
||||||
packet << static_cast<u8>(IdSetGameName);
|
packet << static_cast<u8>(IdSetGameInfo);
|
||||||
packet << game_name;
|
packet << game_info.name;
|
||||||
|
packet << game_info.id;
|
||||||
room_member_impl->Send(std::move(packet));
|
room_member_impl->Send(std::move(packet));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RoomMember::CallbackHandle<RoomMember::State> RoomMember::BindOnStateChanged(
|
||||||
|
std::function<void(const RoomMember::State&)> callback) {
|
||||||
|
return room_member_impl->Bind(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
RoomMember::CallbackHandle<WifiPacket> RoomMember::BindOnWifiPacketReceived(
|
||||||
|
std::function<void(const WifiPacket&)> callback) {
|
||||||
|
return room_member_impl->Bind(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
RoomMember::CallbackHandle<RoomInformation> RoomMember::BindOnRoomInformationChanged(
|
||||||
|
std::function<void(const RoomInformation&)> callback) {
|
||||||
|
return room_member_impl->Bind(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
RoomMember::CallbackHandle<ChatEntry> RoomMember::BindOnChatMessageRecieved(
|
||||||
|
std::function<void(const ChatEntry&)> callback) {
|
||||||
|
return room_member_impl->Bind(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void RoomMember::Unbind(CallbackHandle<T> handle) {
|
||||||
|
std::lock_guard<std::mutex> lock(room_member_impl->callback_mutex);
|
||||||
|
room_member_impl->callbacks.Get<T>().erase(handle);
|
||||||
|
}
|
||||||
|
|
||||||
void RoomMember::Leave() {
|
void RoomMember::Leave() {
|
||||||
room_member_impl->SetState(State::Idle);
|
room_member_impl->SetState(State::Idle);
|
||||||
room_member_impl->loop_thread->join();
|
room_member_impl->loop_thread->join();
|
||||||
room_member_impl->loop_thread.reset();
|
room_member_impl->loop_thread.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template void RoomMember::Unbind(CallbackHandle<WifiPacket>);
|
||||||
|
template void RoomMember::Unbind(CallbackHandle<RoomMember::State>);
|
||||||
|
template void RoomMember::Unbind(CallbackHandle<RoomInformation>);
|
||||||
|
template void RoomMember::Unbind(CallbackHandle<ChatEntry>);
|
||||||
|
|
||||||
} // namespace Network
|
} // namespace Network
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
@ -53,12 +54,23 @@ public:
|
||||||
|
|
||||||
struct MemberInformation {
|
struct MemberInformation {
|
||||||
std::string nickname; ///< Nickname of the member.
|
std::string nickname; ///< Nickname of the member.
|
||||||
std::string game_name; ///< Name of the game they're currently playing, or empty if they're
|
GameInfo game_info; ///< Name of the game they're currently playing, or empty if they're
|
||||||
/// not playing anything.
|
/// not playing anything.
|
||||||
MacAddress mac_address; ///< MAC address associated with this member.
|
MacAddress mac_address; ///< MAC address associated with this member.
|
||||||
};
|
};
|
||||||
using MemberList = std::vector<MemberInformation>;
|
using MemberList = std::vector<MemberInformation>;
|
||||||
|
|
||||||
|
// The handle for the callback functions
|
||||||
|
template <typename T>
|
||||||
|
using CallbackHandle = std::shared_ptr<std::function<void(const T&)>>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unbinds a callback function from the events.
|
||||||
|
* @param handle The connection handle to disconnect
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
void Unbind(CallbackHandle<T> handle);
|
||||||
|
|
||||||
RoomMember();
|
RoomMember();
|
||||||
~RoomMember();
|
~RoomMember();
|
||||||
|
|
||||||
|
@ -113,10 +125,49 @@ public:
|
||||||
void SendChatMessage(const std::string& message);
|
void SendChatMessage(const std::string& message);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends the current game name to the room.
|
* Sends the current game info to the room.
|
||||||
* @param game_name The game name.
|
* @param game_info The game information.
|
||||||
*/
|
*/
|
||||||
void SendGameName(const std::string& game_name);
|
void SendGameInfo(const GameInfo& game_info);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Binds a function to an event that will be triggered every time the State of the member
|
||||||
|
* changed. The function wil 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<State> BindOnStateChanged(std::function<void(const State&)> callback);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Binds a function to an event that will be triggered every time a WifiPacket is received.
|
||||||
|
* The function wil be called everytime 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<WifiPacket> BindOnWifiPacketReceived(
|
||||||
|
std::function<void(const WifiPacket&)> callback);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Binds a function to an event that will be triggered every time the RoomInformation changes.
|
||||||
|
* The function wil 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<RoomInformation> BindOnRoomInformationChanged(
|
||||||
|
std::function<void(const RoomInformation&)> callback);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Binds a function to an event that will be triggered every time a ChatMessage is received.
|
||||||
|
* The function wil 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<ChatEntry> BindOnChatMessageRecieved(
|
||||||
|
std::function<void(const ChatEntry&)> callback);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Leaves the current room.
|
* Leaves the current room.
|
||||||
|
|
Loading…
Reference in a new issue