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:
B3n30 2017-08-19 19:14:33 +02:00 committed by James Rowe
parent 21204ba488
commit 5d0a1e7efd
9 changed files with 310 additions and 37 deletions

View file

@ -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)

View file

@ -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");
} }

View file

@ -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

View file

@ -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;

View file

@ -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);

View file

@ -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;
{
std::lock_guard<std::mutex> lock(member_mutex);
members.push_back(std::move(member)); 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());
{
std::lock_guard<std::mutex> lock(member_mutex);
for (const auto& member : members) { for (const auto& member : members) {
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;
}
} }
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;
in_packet >> game_info.id;
{
std::lock_guard<std::mutex> lock(member_mutex);
auto member = auto member =
std::find_if(members.begin(), members.end(), std::find_if(members.begin(), members.end(), [event](const Member& member) -> bool {
[event](const Member& member) -> bool { return member.peer == event->peer; }); return member.peer == event->peer;
});
if (member != members.end()) { if (member != members.end()) {
member->game_name = game_name; member->game_info = game_info;
BroadcastRoomInformation();
} }
} }
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(), {
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; }), [client](const Member& member) { return member.peer == client; }),
members.end()); 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;
{
std::lock_guard<std::mutex> lock(room_impl->member_mutex);
room_impl->members.clear(); 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();
} }

View file

@ -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.

View file

@ -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) {
if (state != new_state) {
state = new_state; state = new_state;
// TODO(B3N30): Invoke the callback functions 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

View file

@ -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.