summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/citra/CMakeLists.txt2
-rw-r--r--src/citra/emu_window/emu_window_sdl2.cpp4
-rw-r--r--src/network/CMakeLists.txt2
-rw-r--r--src/network/packet.cpp225
-rw-r--r--src/network/packet.h162
-rw-r--r--src/network/room.cpp402
-rw-r--r--src/network/room.h26
-rw-r--r--src/network/room_member.cpp334
-rw-r--r--src/network/room_member.h70
9 files changed, 1206 insertions, 21 deletions
diff --git a/src/citra/CMakeLists.txt b/src/citra/CMakeLists.txt
index d72d2b5f4..a885f22f8 100644
--- a/src/citra/CMakeLists.txt
+++ b/src/citra/CMakeLists.txt
@@ -16,7 +16,7 @@ set(HEADERS
16create_directory_groups(${SRCS} ${HEADERS}) 16create_directory_groups(${SRCS} ${HEADERS})
17 17
18add_executable(citra ${SRCS} ${HEADERS}) 18add_executable(citra ${SRCS} ${HEADERS})
19target_link_libraries(citra PRIVATE common core input_common) 19target_link_libraries(citra PRIVATE common core input_common network)
20target_link_libraries(citra PRIVATE inih glad) 20target_link_libraries(citra PRIVATE inih glad)
21if (MSVC) 21if (MSVC)
22 target_link_libraries(citra PRIVATE getopt) 22 target_link_libraries(citra PRIVATE getopt)
diff --git a/src/citra/emu_window/emu_window_sdl2.cpp b/src/citra/emu_window/emu_window_sdl2.cpp
index 47aadd60c..b0f808399 100644
--- a/src/citra/emu_window/emu_window_sdl2.cpp
+++ b/src/citra/emu_window/emu_window_sdl2.cpp
@@ -16,6 +16,7 @@
16#include "core/settings.h" 16#include "core/settings.h"
17#include "input_common/keyboard.h" 17#include "input_common/keyboard.h"
18#include "input_common/main.h" 18#include "input_common/main.h"
19#include "network/network.h"
19 20
20void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) { 21void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) {
21 TouchMoved((unsigned)std::max(x, 0), (unsigned)std::max(y, 0)); 22 TouchMoved((unsigned)std::max(x, 0), (unsigned)std::max(y, 0));
@@ -58,6 +59,7 @@ void EmuWindow_SDL2::OnResize() {
58 59
59EmuWindow_SDL2::EmuWindow_SDL2() { 60EmuWindow_SDL2::EmuWindow_SDL2() {
60 InputCommon::Init(); 61 InputCommon::Init();
62 Network::Init();
61 63
62 motion_emu = std::make_unique<Motion::MotionEmu>(*this); 64 motion_emu = std::make_unique<Motion::MotionEmu>(*this);
63 65
@@ -116,6 +118,8 @@ EmuWindow_SDL2::~EmuWindow_SDL2() {
116 SDL_GL_DeleteContext(gl_context); 118 SDL_GL_DeleteContext(gl_context);
117 SDL_Quit(); 119 SDL_Quit();
118 motion_emu = nullptr; 120 motion_emu = nullptr;
121
122 Network::Shutdown();
119 InputCommon::Shutdown(); 123 InputCommon::Shutdown();
120} 124}
121 125
diff --git a/src/network/CMakeLists.txt b/src/network/CMakeLists.txt
index aeabe430e..ac9d028da 100644
--- a/src/network/CMakeLists.txt
+++ b/src/network/CMakeLists.txt
@@ -1,11 +1,13 @@
1set(SRCS 1set(SRCS
2 network.cpp 2 network.cpp
3 packet.cpp
3 room.cpp 4 room.cpp
4 room_member.cpp 5 room_member.cpp
5 ) 6 )
6 7
7set(HEADERS 8set(HEADERS
8 network.h 9 network.h
10 packet.h
9 room.h 11 room.h
10 room_member.h 12 room_member.h
11 ) 13 )
diff --git a/src/network/packet.cpp b/src/network/packet.cpp
new file mode 100644
index 000000000..660e92c0d
--- /dev/null
+++ b/src/network/packet.cpp
@@ -0,0 +1,225 @@
1// Copyright 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#ifdef _WIN32
6#include <winsock2.h>
7#else
8#include <arpa/inet.h>
9#endif
10#include <cstring>
11#include <string>
12#include "network/packet.h"
13
14namespace Network {
15
16void Packet::Append(const void* in_data, std::size_t size_in_bytes) {
17 if (in_data && (size_in_bytes > 0)) {
18 std::size_t start = data.size();
19 data.resize(start + size_in_bytes);
20 std::memcpy(&data[start], in_data, size_in_bytes);
21 }
22}
23
24void Packet::Read(void* out_data, std::size_t size_in_bytes) {
25 if (out_data && CheckSize(size_in_bytes)) {
26 std::memcpy(out_data, &data[read_pos], size_in_bytes);
27 read_pos += size_in_bytes;
28 }
29}
30
31void Packet::Clear() {
32 data.clear();
33 read_pos = 0;
34 is_valid = true;
35}
36
37const void* Packet::GetData() const {
38 return !data.empty() ? &data[0] : nullptr;
39}
40
41void Packet::IgnoreBytes(u32 length) {
42 read_pos += length;
43}
44
45std::size_t Packet::GetDataSize() const {
46 return data.size();
47}
48
49bool Packet::EndOfPacket() const {
50 return read_pos >= data.size();
51}
52
53Packet::operator bool() const {
54 return is_valid ? &Packet::CheckSize : nullptr;
55}
56
57Packet& Packet::operator>>(bool& out_data) {
58 u8 value;
59 if (*this >> value) {
60 out_data = (value != 0);
61 }
62 return *this;
63}
64
65Packet& Packet::operator>>(s8& out_data) {
66 Read(&out_data, sizeof(out_data));
67 return *this;
68}
69
70Packet& Packet::operator>>(u8& out_data) {
71 Read(&out_data, sizeof(out_data));
72 return *this;
73}
74
75Packet& Packet::operator>>(s16& out_data) {
76 s16 value;
77 Read(&value, sizeof(value));
78 out_data = ntohs(value);
79 return *this;
80}
81
82Packet& Packet::operator>>(u16& out_data) {
83 u16 value;
84 Read(&value, sizeof(value));
85 out_data = ntohs(value);
86 return *this;
87}
88
89Packet& Packet::operator>>(s32& out_data) {
90 s32 value;
91 Read(&value, sizeof(value));
92 out_data = ntohl(value);
93 return *this;
94}
95
96Packet& Packet::operator>>(u32& out_data) {
97 u32 value;
98 Read(&value, sizeof(value));
99 out_data = ntohl(value);
100 return *this;
101}
102
103Packet& Packet::operator>>(float& out_data) {
104 Read(&out_data, sizeof(out_data));
105 return *this;
106}
107
108Packet& Packet::operator>>(double& out_data) {
109 Read(&out_data, sizeof(out_data));
110 return *this;
111}
112
113Packet& Packet::operator>>(char* out_data) {
114 // First extract string length
115 u32 length = 0;
116 *this >> length;
117
118 if ((length > 0) && CheckSize(length)) {
119 // Then extract characters
120 std::memcpy(out_data, &data[read_pos], length);
121 out_data[length] = '\0';
122
123 // Update reading position
124 read_pos += length;
125 }
126
127 return *this;
128}
129
130Packet& Packet::operator>>(std::string& out_data) {
131 // First extract string length
132 u32 length = 0;
133 *this >> length;
134
135 out_data.clear();
136 if ((length > 0) && CheckSize(length)) {
137 // Then extract characters
138 out_data.assign(&data[read_pos], length);
139
140 // Update reading position
141 read_pos += length;
142 }
143
144 return *this;
145}
146
147Packet& Packet::operator<<(bool in_data) {
148 *this << static_cast<u8>(in_data);
149 return *this;
150}
151
152Packet& Packet::operator<<(s8 in_data) {
153 Append(&in_data, sizeof(in_data));
154 return *this;
155}
156
157Packet& Packet::operator<<(u8 in_data) {
158 Append(&in_data, sizeof(in_data));
159 return *this;
160}
161
162Packet& Packet::operator<<(s16 in_data) {
163 s16 toWrite = htons(in_data);
164 Append(&toWrite, sizeof(toWrite));
165 return *this;
166}
167
168Packet& Packet::operator<<(u16 in_data) {
169 u16 toWrite = htons(in_data);
170 Append(&toWrite, sizeof(toWrite));
171 return *this;
172}
173
174Packet& Packet::operator<<(s32 in_data) {
175 s32 toWrite = htonl(in_data);
176 Append(&toWrite, sizeof(toWrite));
177 return *this;
178}
179
180Packet& Packet::operator<<(u32 in_data) {
181 u32 toWrite = htonl(in_data);
182 Append(&toWrite, sizeof(toWrite));
183 return *this;
184}
185
186Packet& Packet::operator<<(float in_data) {
187 Append(&in_data, sizeof(in_data));
188 return *this;
189}
190
191Packet& Packet::operator<<(double in_data) {
192 Append(&in_data, sizeof(in_data));
193 return *this;
194}
195
196Packet& Packet::operator<<(const char* in_data) {
197 // First insert string length
198 u32 length = std::strlen(in_data);
199 *this << length;
200
201 // Then insert characters
202 Append(in_data, length * sizeof(char));
203
204 return *this;
205}
206
207Packet& Packet::operator<<(const std::string& in_data) {
208 // First insert string length
209 u32 length = static_cast<u32>(in_data.size());
210 *this << length;
211
212 // Then insert characters
213 if (length > 0)
214 Append(in_data.c_str(), length * sizeof(std::string::value_type));
215
216 return *this;
217}
218
219bool Packet::CheckSize(std::size_t size) {
220 is_valid = is_valid && (read_pos + size <= data.size());
221
222 return is_valid;
223}
224
225} // namespace Network
diff --git a/src/network/packet.h b/src/network/packet.h
new file mode 100644
index 000000000..94b351ab1
--- /dev/null
+++ b/src/network/packet.h
@@ -0,0 +1,162 @@
1// Copyright 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8#include <vector>
9#include "common/common_types.h"
10
11namespace Network {
12
13/// A class that serializes data for network transfer. It also handles endianess
14class Packet {
15public:
16 Packet() = default;
17 ~Packet() = default;
18
19 /**
20 * Append data to the end of the packet
21 * @param data Pointer to the sequence of bytes to append
22 * @param size_in_bytes Number of bytes to append
23 */
24 void Append(const void* data, std::size_t size_in_bytes);
25
26 /**
27 * Reads data from the current read position of the packet
28 * @param out_data Pointer where the data should get written to
29 * @param size_in_bytes Number of bytes to read
30 */
31 void Read(void* out_data, std::size_t size_in_bytes);
32
33 /**
34 * Clear the packet
35 * After calling Clear, the packet is empty.
36 */
37 void Clear();
38
39 /**
40 * Ignores bytes while reading
41 * @param length THe number of bytes to ignore
42 */
43 void IgnoreBytes(u32 length);
44
45 /**
46 * Get a pointer to the data contained in the packet
47 * @return Pointer to the data
48 */
49 const void* GetData() const;
50
51 /**
52 * This function returns the number of bytes pointed to by
53 * what getData returns.
54 * @return Data size, in bytes
55 */
56 std::size_t GetDataSize() const;
57
58 /**
59 * This function is useful to know if there is some data
60 * left to be read, without actually reading it.
61 * @return True if all data was read, false otherwise
62 */
63 bool EndOfPacket() const;
64
65 explicit operator bool() const;
66
67 /// Overloads of operator >> to read data from the packet
68 Packet& operator>>(bool& out_data);
69 Packet& operator>>(s8& out_data);
70 Packet& operator>>(u8& out_data);
71 Packet& operator>>(s16& out_data);
72 Packet& operator>>(u16& out_data);
73 Packet& operator>>(s32& out_data);
74 Packet& operator>>(u32& out_data);
75 Packet& operator>>(float& out_data);
76 Packet& operator>>(double& out_data);
77 Packet& operator>>(char* out_data);
78 Packet& operator>>(std::string& out_data);
79 template <typename T>
80 Packet& operator>>(std::vector<T>& out_data);
81 template <typename T, std::size_t S>
82 Packet& operator>>(std::array<T, S>& out_data);
83
84 /// Overloads of operator << to write data into the packet
85 Packet& operator<<(bool in_data);
86 Packet& operator<<(s8 in_data);
87 Packet& operator<<(u8 in_data);
88 Packet& operator<<(s16 in_data);
89 Packet& operator<<(u16 in_data);
90 Packet& operator<<(s32 in_data);
91 Packet& operator<<(u32 in_data);
92 Packet& operator<<(float in_data);
93 Packet& operator<<(double in_data);
94 Packet& operator<<(const char* in_data);
95 Packet& operator<<(const std::string& in_data);
96 template <typename T>
97 Packet& operator<<(const std::vector<T>& in_data);
98 template <typename T, std::size_t S>
99 Packet& operator<<(const std::array<T, S>& data);
100
101private:
102 /**
103 * Check if the packet can extract a given number of bytes
104 * This function updates accordingly the state of the packet.
105 * @param size Size to check
106 * @return True if size bytes can be read from the packet
107 */
108 bool CheckSize(std::size_t size);
109
110 // Member data
111 std::vector<char> data; ///< Data stored in the packet
112 std::size_t read_pos = 0; ///< Current reading position in the packet
113 bool is_valid = true; ///< Reading state of the packet
114};
115
116template <typename T>
117Packet& Packet::operator>>(std::vector<T>& out_data) {
118 // First extract the size
119 u32 size = 0;
120 *this >> size;
121 out_data.resize(size);
122
123 // Then extract the data
124 for (std::size_t i = 0; i < out_data.size(); ++i) {
125 T character = 0;
126 *this >> character;
127 out_data[i] = character;
128 }
129 return *this;
130}
131
132template <typename T, std::size_t S>
133Packet& Packet::operator>>(std::array<T, S>& out_data) {
134 for (std::size_t i = 0; i < out_data.size(); ++i) {
135 T character = 0;
136 *this >> character;
137 out_data[i] = character;
138 }
139 return *this;
140}
141
142template <typename T>
143Packet& Packet::operator<<(const std::vector<T>& in_data) {
144 // First insert the size
145 *this << static_cast<u32>(in_data.size());
146
147 // Then insert the data
148 for (std::size_t i = 0; i < in_data.size(); ++i) {
149 *this << in_data[i];
150 }
151 return *this;
152}
153
154template <typename T, std::size_t S>
155Packet& Packet::operator<<(const std::array<T, S>& in_data) {
156 for (std::size_t i = 0; i < in_data.size(); ++i) {
157 *this << in_data[i];
158 }
159 return *this;
160}
161
162} // namespace Network
diff --git a/src/network/room.cpp b/src/network/room.cpp
index 48de2f5cb..8b7915bb7 100644
--- a/src/network/room.cpp
+++ b/src/network/room.cpp
@@ -2,7 +2,13 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm>
6#include <atomic>
7#include <random>
8#include <thread>
9#include <vector>
5#include "enet/enet.h" 10#include "enet/enet.h"
11#include "network/packet.h"
6#include "network/room.h" 12#include "network/room.h"
7 13
8namespace Network { 14namespace Network {
@@ -12,12 +18,395 @@ static constexpr u32 MaxConcurrentConnections = 10;
12 18
13class Room::RoomImpl { 19class Room::RoomImpl {
14public: 20public:
21 // This MAC address is used to generate a 'Nintendo' like Mac address.
22 const MacAddress NintendoOUI = {0x00, 0x1F, 0x32, 0x00, 0x00, 0x00};
23 std::mt19937 random_gen; ///< Random number generator. Used for GenerateMacAddress
24
15 ENetHost* server = nullptr; ///< Network interface. 25 ENetHost* server = nullptr; ///< Network interface.
16 26
17 std::atomic<State> state{State::Closed}; ///< Current state of the room. 27 std::atomic<State> state{State::Closed}; ///< Current state of the room.
18 RoomInformation room_information; ///< Information about this room. 28 RoomInformation room_information; ///< Information about this room.
29
30 struct Member {
31 std::string nickname; ///< The nickname of the member.
32 std::string game_name; ///< The current game of the member
33 MacAddress mac_address; ///< The assigned mac address of the member.
34 ENetPeer* peer; ///< The remote peer.
35 };
36 using MemberList = std::vector<Member>;
37 MemberList members; ///< Information about the members of this room.
38
39 RoomImpl() : random_gen(std::random_device()()) {}
40
41 /// Thread that receives and dispatches network packets
42 std::unique_ptr<std::thread> room_thread;
43
44 /// Thread function that will receive and dispatch messages until the room is destroyed.
45 void ServerLoop();
46 void StartLoop();
47
48 /**
49 * Parses and answers a room join request from a client.
50 * Validates the uniqueness of the username and assigns the MAC address
51 * that the client will use for the remainder of the connection.
52 */
53 void HandleJoinRequest(const ENetEvent* event);
54
55 /**
56 * Returns whether the nickname is valid, ie. isn't already taken by someone else in the room.
57 */
58 bool IsValidNickname(const std::string& nickname) const;
59
60 /**
61 * Returns whether the MAC address is valid, ie. isn't already taken by someone else in the
62 * room.
63 */
64 bool IsValidMacAddress(const MacAddress& address) const;
65
66 /**
67 * Sends a ID_ROOM_NAME_COLLISION message telling the client that the name is invalid.
68 */
69 void SendNameCollision(ENetPeer* client);
70
71 /**
72 * Sends a ID_ROOM_MAC_COLLISION message telling the client that the MAC is invalid.
73 */
74 void SendMacCollision(ENetPeer* client);
75
76 /**
77 * Sends a ID_ROOM_VERSION_MISMATCH message telling the client that the version is invalid.
78 */
79 void SendVersionMismatch(ENetPeer* client);
80
81 /**
82 * Notifies the member that its connection attempt was successful,
83 * and it is now part of the room.
84 */
85 void SendJoinSuccess(ENetPeer* client, MacAddress mac_address);
86
87 /**
88 * Notifies the members that the room is closed,
89 */
90 void SendCloseMessage();
91
92 /**
93 * Sends the information about the room, along with the list of members
94 * to every connected client in the room.
95 * The packet has the structure:
96 * <MessageID>ID_ROOM_INFORMATION
97 * <String> room_name
98 * <u32> member_slots: The max number of clients allowed in this room
99 * <u32> num_members: the number of currently joined clients
100 * This is followed by the following three values for each member:
101 * <String> nickname of that member
102 * <MacAddress> mac_address of that member
103 * <String> game_name of that member
104 */
105 void BroadcastRoomInformation();
106
107 /**
108 * Generates a free MAC address to assign to a new client.
109 * The first 3 bytes are the NintendoOUI 0x00, 0x1F, 0x32
110 */
111 MacAddress GenerateMacAddress();
112
113 /**
114 * Broadcasts this packet to all members except the sender.
115 * @param event The ENet event containing the data
116 */
117 void HandleWifiPacket(const ENetEvent* event);
118
119 /**
120 * Extracts a chat entry from a received ENet packet and adds it to the chat queue.
121 * @param event The ENet event that was received.
122 */
123 void HandleChatPacket(const ENetEvent* event);
124
125 /**
126 * Extracts the game name from a received ENet packet and broadcasts it.
127 * @param event The ENet event that was received.
128 */
129 void HandleGameNamePacket(const ENetEvent* event);
130
131 /**
132 * Removes the client from the members list if it was in it and announces the change
133 * to all other clients.
134 */
135 void HandleClientDisconnection(ENetPeer* client);
19}; 136};
20 137
138// RoomImpl
139void Room::RoomImpl::ServerLoop() {
140 while (state != State::Closed) {
141 ENetEvent event;
142 if (enet_host_service(server, &event, 100) > 0) {
143 switch (event.type) {
144 case ENET_EVENT_TYPE_RECEIVE:
145 switch (event.packet->data[0]) {
146 case IdJoinRequest:
147 HandleJoinRequest(&event);
148 break;
149 case IdSetGameName:
150 HandleGameNamePacket(&event);
151 break;
152 case IdWifiPacket:
153 HandleWifiPacket(&event);
154 break;
155 case IdChatMessage:
156 HandleChatPacket(&event);
157 break;
158 }
159 enet_packet_destroy(event.packet);
160 break;
161 case ENET_EVENT_TYPE_DISCONNECT:
162 HandleClientDisconnection(event.peer);
163 break;
164 }
165 }
166 }
167 // Close the connection to all members:
168 SendCloseMessage();
169}
170
171void Room::RoomImpl::StartLoop() {
172 room_thread = std::make_unique<std::thread>(&Room::RoomImpl::ServerLoop, this);
173}
174
175void Room::RoomImpl::HandleJoinRequest(const ENetEvent* event) {
176 Packet packet;
177 packet.Append(event->packet->data, event->packet->dataLength);
178 packet.IgnoreBytes(sizeof(u8)); // Igonore the message type
179 std::string nickname;
180 packet >> nickname;
181
182 MacAddress preferred_mac;
183 packet >> preferred_mac;
184
185 u32 client_version;
186 packet >> client_version;
187
188 if (!IsValidNickname(nickname)) {
189 SendNameCollision(event->peer);
190 return;
191 }
192
193 if (preferred_mac != NoPreferredMac) {
194 // Verify if the preferred mac is available
195 if (!IsValidMacAddress(preferred_mac)) {
196 SendMacCollision(event->peer);
197 return;
198 }
199 } else {
200 // Assign a MAC address of this client automatically
201 preferred_mac = GenerateMacAddress();
202 }
203
204 if (client_version != network_version) {
205 SendVersionMismatch(event->peer);
206 return;
207 }
208
209 // At this point the client is ready to be added to the room.
210 Member member{};
211 member.mac_address = preferred_mac;
212 member.nickname = nickname;
213 member.peer = event->peer;
214
215 members.push_back(std::move(member));
216
217 // Notify everyone that the room information has changed.
218 BroadcastRoomInformation();
219 SendJoinSuccess(event->peer, preferred_mac);
220}
221
222bool Room::RoomImpl::IsValidNickname(const std::string& nickname) const {
223 // A nickname is valid if it is not already taken by anybody else in the room.
224 // TODO(B3N30): Check for empty names, spaces, etc.
225 return std::all_of(members.begin(), members.end(),
226 [&nickname](const auto& member) { return member.nickname != nickname; });
227}
228
229bool Room::RoomImpl::IsValidMacAddress(const MacAddress& address) const {
230 // A MAC address is valid if it is not already taken by anybody else in the room.
231 return std::all_of(members.begin(), members.end(),
232 [&address](const auto& member) { return member.mac_address != address; });
233}
234
235void Room::RoomImpl::SendNameCollision(ENetPeer* client) {
236 Packet packet;
237 packet << static_cast<u8>(IdNameCollision);
238
239 ENetPacket* enet_packet =
240 enet_packet_create(packet.GetData(), packet.GetDataSize(), ENET_PACKET_FLAG_RELIABLE);
241 enet_peer_send(client, 0, enet_packet);
242 enet_host_flush(server);
243}
244
245void Room::RoomImpl::SendMacCollision(ENetPeer* client) {
246 Packet packet;
247 packet << static_cast<u8>(IdMacCollision);
248
249 ENetPacket* enet_packet =
250 enet_packet_create(packet.GetData(), packet.GetDataSize(), ENET_PACKET_FLAG_RELIABLE);
251 enet_peer_send(client, 0, enet_packet);
252 enet_host_flush(server);
253}
254
255void Room::RoomImpl::SendVersionMismatch(ENetPeer* client) {
256 Packet packet;
257 packet << static_cast<u8>(IdVersionMismatch);
258 packet << network_version;
259
260 ENetPacket* enet_packet =
261 enet_packet_create(packet.GetData(), packet.GetDataSize(), ENET_PACKET_FLAG_RELIABLE);
262 enet_peer_send(client, 0, enet_packet);
263 enet_host_flush(server);
264}
265
266void Room::RoomImpl::SendJoinSuccess(ENetPeer* client, MacAddress mac_address) {
267 Packet packet;
268 packet << static_cast<u8>(IdJoinSuccess);
269 packet << mac_address;
270 ENetPacket* enet_packet =
271 enet_packet_create(packet.GetData(), packet.GetDataSize(), ENET_PACKET_FLAG_RELIABLE);
272 enet_peer_send(client, 0, enet_packet);
273 enet_host_flush(server);
274}
275
276void Room::RoomImpl::SendCloseMessage() {
277 Packet packet;
278 packet << static_cast<u8>(IdCloseRoom);
279 ENetPacket* enet_packet =
280 enet_packet_create(packet.GetData(), packet.GetDataSize(), ENET_PACKET_FLAG_RELIABLE);
281 for (auto& member : members) {
282 enet_peer_send(member.peer, 0, enet_packet);
283 }
284 enet_host_flush(server);
285 for (auto& member : members) {
286 enet_peer_disconnect(member.peer, 0);
287 }
288}
289
290void Room::RoomImpl::BroadcastRoomInformation() {
291 Packet packet;
292 packet << static_cast<u8>(IdRoomInformation);
293 packet << room_information.name;
294 packet << room_information.member_slots;
295
296 packet << static_cast<u32>(members.size());
297 for (const auto& member : members) {
298 packet << member.nickname;
299 packet << member.mac_address;
300 packet << member.game_name;
301 }
302
303 ENetPacket* enet_packet =
304 enet_packet_create(packet.GetData(), packet.GetDataSize(), ENET_PACKET_FLAG_RELIABLE);
305 enet_host_broadcast(server, 0, enet_packet);
306 enet_host_flush(server);
307}
308
309MacAddress Room::RoomImpl::GenerateMacAddress() {
310 MacAddress result_mac =
311 NintendoOUI; // The first three bytes of each MAC address will be the NintendoOUI
312 std::uniform_int_distribution<> dis(0x00, 0xFF); // Random byte between 0 and 0xFF
313 do {
314 for (size_t i = 3; i < result_mac.size(); ++i) {
315 result_mac[i] = dis(random_gen);
316 }
317 } while (!IsValidMacAddress(result_mac));
318 return result_mac;
319}
320
321void Room::RoomImpl::HandleWifiPacket(const ENetEvent* event) {
322 Packet in_packet;
323 in_packet.Append(event->packet->data, event->packet->dataLength);
324 in_packet.IgnoreBytes(sizeof(u8)); // Message type
325 in_packet.IgnoreBytes(sizeof(u8)); // WifiPacket Type
326 in_packet.IgnoreBytes(sizeof(u8)); // WifiPacket Channel
327 in_packet.IgnoreBytes(sizeof(MacAddress)); // WifiPacket Transmitter Address
328 MacAddress destination_address;
329 in_packet >> destination_address;
330
331 Packet out_packet;
332 out_packet.Append(event->packet->data, event->packet->dataLength);
333 ENetPacket* enet_packet = enet_packet_create(out_packet.GetData(), out_packet.GetDataSize(),
334 ENET_PACKET_FLAG_RELIABLE);
335
336 if (destination_address == BroadcastMac) { // Send the data to everyone except the sender
337 for (const auto& member : members) {
338 if (member.peer != event->peer)
339 enet_peer_send(member.peer, 0, enet_packet);
340 }
341 } else { // Send the data only to the destination client
342 auto member = std::find_if(members.begin(), members.end(),
343 [destination_address](const Member& member) -> bool {
344 return member.mac_address == destination_address;
345 });
346 if (member != members.end()) {
347 enet_peer_send(member->peer, 0, enet_packet);
348 }
349 }
350 enet_host_flush(server);
351}
352
353void Room::RoomImpl::HandleChatPacket(const ENetEvent* event) {
354 Packet in_packet;
355 in_packet.Append(event->packet->data, event->packet->dataLength);
356
357 in_packet.IgnoreBytes(sizeof(u8)); // Igonore the message type
358 std::string message;
359 in_packet >> message;
360 auto CompareNetworkAddress = [event](const Member member) -> bool {
361 return member.peer == event->peer;
362 };
363 const auto sending_member = std::find_if(members.begin(), members.end(), CompareNetworkAddress);
364 if (sending_member == members.end()) {
365 return; // Received a chat message from a unknown sender
366 }
367
368 Packet out_packet;
369 out_packet << static_cast<u8>(IdChatMessage);
370 out_packet << sending_member->nickname;
371 out_packet << message;
372
373 ENetPacket* enet_packet = enet_packet_create(out_packet.GetData(), out_packet.GetDataSize(),
374 ENET_PACKET_FLAG_RELIABLE);
375 for (const auto& member : members) {
376 if (member.peer != event->peer)
377 enet_peer_send(member.peer, 0, enet_packet);
378 }
379 enet_host_flush(server);
380}
381
382void Room::RoomImpl::HandleGameNamePacket(const ENetEvent* event) {
383 Packet in_packet;
384 in_packet.Append(event->packet->data, event->packet->dataLength);
385
386 in_packet.IgnoreBytes(sizeof(u8)); // Igonore the message type
387 std::string game_name;
388 in_packet >> game_name;
389 auto member =
390 std::find_if(members.begin(), members.end(),
391 [event](const Member& member) -> bool { return member.peer == event->peer; });
392 if (member != members.end()) {
393 member->game_name = game_name;
394 BroadcastRoomInformation();
395 }
396}
397
398void Room::RoomImpl::HandleClientDisconnection(ENetPeer* client) {
399 // Remove the client from the members list.
400 members.erase(std::remove_if(members.begin(), members.end(),
401 [client](const Member& member) { return member.peer == client; }),
402 members.end());
403
404 // Announce the change to all clients.
405 enet_peer_disconnect(client, 0);
406 BroadcastRoomInformation();
407}
408
409// Room
21Room::Room() : room_impl{std::make_unique<RoomImpl>()} {} 410Room::Room() : room_impl{std::make_unique<RoomImpl>()} {}
22 411
23Room::~Room() = default; 412Room::~Room() = default;
@@ -25,7 +414,9 @@ Room::~Room() = default;
25void Room::Create(const std::string& name, const std::string& server_address, u16 server_port) { 414void Room::Create(const std::string& name, const std::string& server_address, u16 server_port) {
26 ENetAddress address; 415 ENetAddress address;
27 address.host = ENET_HOST_ANY; 416 address.host = ENET_HOST_ANY;
28 enet_address_set_host(&address, server_address.c_str()); 417 if (!server_address.empty()) {
418 enet_address_set_host(&address, server_address.c_str());
419 }
29 address.port = server_port; 420 address.port = server_port;
30 421
31 room_impl->server = enet_host_create(&address, MaxConcurrentConnections, NumChannels, 0, 0); 422 room_impl->server = enet_host_create(&address, MaxConcurrentConnections, NumChannels, 0, 0);
@@ -34,8 +425,7 @@ void Room::Create(const std::string& name, const std::string& server_address, u1
34 425
35 room_impl->room_information.name = name; 426 room_impl->room_information.name = name;
36 room_impl->room_information.member_slots = MaxConcurrentConnections; 427 room_impl->room_information.member_slots = MaxConcurrentConnections;
37 428 room_impl->StartLoop();
38 // TODO(B3N30): Start the receiving thread
39} 429}
40 430
41Room::State Room::GetState() const { 431Room::State Room::GetState() const {
@@ -48,13 +438,17 @@ const RoomInformation& Room::GetRoomInformation() const {
48 438
49void Room::Destroy() { 439void Room::Destroy() {
50 room_impl->state = State::Closed; 440 room_impl->state = State::Closed;
51 // TODO(B3n30): Join the receiving thread 441 room_impl->room_thread->join();
442 room_impl->room_thread.reset();
52 443
53 if (room_impl->server) { 444 if (room_impl->server) {
54 enet_host_destroy(room_impl->server); 445 enet_host_destroy(room_impl->server);
55 } 446 }
56 room_impl->room_information = {}; 447 room_impl->room_information = {};
57 room_impl->server = nullptr; 448 room_impl->server = nullptr;
449 room_impl->members.clear();
450 room_impl->room_information.member_slots = 0;
451 room_impl->room_information.name.clear();
58} 452}
59 453
60} // namespace Network 454} // namespace Network
diff --git a/src/network/room.h b/src/network/room.h
index 70c64d5f1..54cccf0ae 100644
--- a/src/network/room.h
+++ b/src/network/room.h
@@ -4,13 +4,15 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <atomic> 7#include <array>
8#include <memory> 8#include <memory>
9#include <string> 9#include <string>
10#include "common/common_types.h" 10#include "common/common_types.h"
11 11
12namespace Network { 12namespace Network {
13 13
14constexpr u32 network_version = 1; ///< The version of this Room and RoomMember
15
14constexpr u16 DefaultRoomPort = 1234; 16constexpr u16 DefaultRoomPort = 1234;
15constexpr size_t NumChannels = 1; // Number of channels used for the connection 17constexpr size_t NumChannels = 1; // Number of channels used for the connection
16 18
@@ -19,6 +21,28 @@ struct RoomInformation {
19 u32 member_slots; ///< Maximum number of members in this room 21 u32 member_slots; ///< Maximum number of members in this room
20}; 22};
21 23
24using MacAddress = std::array<u8, 6>;
25/// A special MAC address that tells the room we're joining to assign us a MAC address
26/// automatically.
27const MacAddress NoPreferredMac = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
28
29// 802.11 broadcast MAC address
30constexpr MacAddress BroadcastMac = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
31
32// The different types of messages that can be sent. The first byte of each packet defines the type
33enum RoomMessageTypes : u8 {
34 IdJoinRequest = 1,
35 IdJoinSuccess,
36 IdRoomInformation,
37 IdSetGameName,
38 IdWifiPacket,
39 IdChatMessage,
40 IdNameCollision,
41 IdMacCollision,
42 IdVersionMismatch,
43 IdCloseRoom
44};
45
22/// This is what a server [person creating a server] would use. 46/// This is what a server [person creating a server] would use.
23class Room final { 47class Room final {
24public: 48public:
diff --git a/src/network/room_member.cpp b/src/network/room_member.cpp
index c87f009f4..dac9bacae 100644
--- a/src/network/room_member.cpp
+++ b/src/network/room_member.cpp
@@ -2,8 +2,13 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <atomic>
6#include <list>
7#include <mutex>
8#include <thread>
5#include "common/assert.h" 9#include "common/assert.h"
6#include "enet/enet.h" 10#include "enet/enet.h"
11#include "network/packet.h"
7#include "network/room_member.h" 12#include "network/room_member.h"
8 13
9namespace Network { 14namespace Network {
@@ -15,11 +20,263 @@ public:
15 ENetHost* client = nullptr; ///< ENet network interface. 20 ENetHost* client = nullptr; ///< ENet network interface.
16 ENetPeer* server = nullptr; ///< The server peer the client is connected to 21 ENetPeer* server = nullptr; ///< The server peer the client is connected to
17 22
23 /// Information about the clients connected to the same room as us.
24 MemberList member_information;
25 /// Information about the room we're connected to.
26 RoomInformation room_information;
27
18 std::atomic<State> state{State::Idle}; ///< Current state of the RoomMember. 28 std::atomic<State> state{State::Idle}; ///< Current state of the RoomMember.
29 void SetState(const State new_state);
30 bool IsConnected() const;
31
32 std::string nickname; ///< The nickname of this member.
33 MacAddress mac_address; ///< The mac_address of this member.
34
35 std::mutex network_mutex; ///< Mutex that controls access to the `client` variable.
36 /// Thread that receives and dispatches network packets
37 std::unique_ptr<std::thread> loop_thread;
38 std::mutex send_list_mutex; ///< Mutex that controls access to the `send_list` variable.
39 std::list<Packet> send_list; ///< A list that stores all packets to send the async
40 void MemberLoop();
41
42 void StartLoop();
43
44 /**
45 * Sends data to the room. It will be send on channel 0 with flag RELIABLE
46 * @param packet The data to send
47 */
48 void Send(Packet&& packet);
49
50 /**
51 * Sends a request to the server, asking for permission to join a room with the specified
52 * nickname and preferred mac.
53 * @params nickname The desired nickname.
54 * @params preferred_mac The preferred MAC address to use in the room, the NoPreferredMac tells
55 * the server to assign one for us.
56 */
57 void SendJoinRequest(const std::string& nickname,
58 const MacAddress& preferred_mac = NoPreferredMac);
59
60 /**
61 * Extracts a MAC Address from a received ENet packet.
62 * @param event The ENet event that was received.
63 */
64 void HandleJoinPacket(const ENetEvent* event);
65 /**
66 * Extracts RoomInformation and MemberInformation from a received RakNet packet.
67 * @param event The ENet event that was received.
68 */
69 void HandleRoomInformationPacket(const ENetEvent* event);
70
71 /**
72 * Extracts a WifiPacket from a received ENet packet.
73 * @param event The ENet event that was received.
74 */
75 void HandleWifiPackets(const ENetEvent* event);
76
77 /**
78 * Extracts a chat entry from a received ENet packet and adds it to the chat queue.
79 * @param event The ENet event that was received.
80 */
81 void HandleChatPacket(const ENetEvent* event);
19 82
20 std::string nickname; ///< The nickname of this member. 83 /**
84 * Disconnects the RoomMember from the Room
85 */
86 void Disconnect();
21}; 87};
22 88
89// RoomMemberImpl
90void RoomMember::RoomMemberImpl::SetState(const State new_state) {
91 state = new_state;
92 // TODO(B3N30): Invoke the callback functions
93}
94
95bool RoomMember::RoomMemberImpl::IsConnected() const {
96 return state == State::Joining || state == State::Joined;
97}
98
99void RoomMember::RoomMemberImpl::MemberLoop() {
100 // Receive packets while the connection is open
101 while (IsConnected()) {
102 std::lock_guard<std::mutex> lock(network_mutex);
103 ENetEvent event;
104 if (enet_host_service(client, &event, 100) > 0) {
105 switch (event.type) {
106 case ENET_EVENT_TYPE_RECEIVE:
107 switch (event.packet->data[0]) {
108 case IdWifiPacket:
109 HandleWifiPackets(&event);
110 break;
111 case IdChatMessage:
112 HandleChatPacket(&event);
113 break;
114 case IdRoomInformation:
115 HandleRoomInformationPacket(&event);
116 break;
117 case IdJoinSuccess:
118 // The join request was successful, we are now in the room.
119 // If we joined successfully, there must be at least one client in the room: us.
120 ASSERT_MSG(member_information.size() > 0,
121 "We have not yet received member information.");
122 HandleJoinPacket(&event); // Get the MAC Address for the client
123 SetState(State::Joined);
124 break;
125 case IdNameCollision:
126 SetState(State::NameCollision);
127 break;
128 case IdMacCollision:
129 SetState(State::MacCollision);
130 break;
131 case IdVersionMismatch:
132 SetState(State::WrongVersion);
133 break;
134 case IdCloseRoom:
135 SetState(State::LostConnection);
136 break;
137 }
138 enet_packet_destroy(event.packet);
139 break;
140 case ENET_EVENT_TYPE_DISCONNECT:
141 SetState(State::LostConnection);
142 break;
143 }
144 }
145 {
146 std::lock_guard<std::mutex> lock(send_list_mutex);
147 for (const auto& packet : send_list) {
148 ENetPacket* enetPacket = enet_packet_create(packet.GetData(), packet.GetDataSize(),
149 ENET_PACKET_FLAG_RELIABLE);
150 enet_peer_send(server, 0, enetPacket);
151 }
152 enet_host_flush(client);
153 send_list.clear();
154 }
155 }
156 Disconnect();
157};
158
159void RoomMember::RoomMemberImpl::StartLoop() {
160 loop_thread = std::make_unique<std::thread>(&RoomMember::RoomMemberImpl::MemberLoop, this);
161}
162
163void RoomMember::RoomMemberImpl::Send(Packet&& packet) {
164 std::lock_guard<std::mutex> lock(send_list_mutex);
165 send_list.push_back(std::move(packet));
166}
167
168void RoomMember::RoomMemberImpl::SendJoinRequest(const std::string& nickname,
169 const MacAddress& preferred_mac) {
170 Packet packet;
171 packet << static_cast<u8>(IdJoinRequest);
172 packet << nickname;
173 packet << preferred_mac;
174 packet << network_version;
175 Send(std::move(packet));
176}
177
178void RoomMember::RoomMemberImpl::HandleRoomInformationPacket(const ENetEvent* event) {
179 Packet packet;
180 packet.Append(event->packet->data, event->packet->dataLength);
181
182 // Ignore the first byte, which is the message id.
183 packet.IgnoreBytes(sizeof(u8)); // Igonore the message type
184
185 RoomInformation info{};
186 packet >> info.name;
187 packet >> info.member_slots;
188 room_information.name = info.name;
189 room_information.member_slots = info.member_slots;
190
191 u32 num_members;
192 packet >> num_members;
193 member_information.resize(num_members);
194
195 for (auto& member : member_information) {
196 packet >> member.nickname;
197 packet >> member.mac_address;
198 packet >> member.game_name;
199 }
200 // TODO(B3N30): Invoke callbacks
201}
202
203void RoomMember::RoomMemberImpl::HandleJoinPacket(const ENetEvent* event) {
204 Packet packet;
205 packet.Append(event->packet->data, event->packet->dataLength);
206
207 // Ignore the first byte, which is the message id.
208 packet.IgnoreBytes(sizeof(u8)); // Igonore the message type
209
210 // Parse the MAC Address from the packet
211 packet >> mac_address;
212 // TODO(B3N30): Invoke callbacks
213}
214
215void RoomMember::RoomMemberImpl::HandleWifiPackets(const ENetEvent* event) {
216 WifiPacket wifi_packet{};
217 Packet packet;
218 packet.Append(event->packet->data, event->packet->dataLength);
219
220 // Ignore the first byte, which is the message id.
221 packet.IgnoreBytes(sizeof(u8)); // Igonore the message type
222
223 // Parse the WifiPacket from the packet
224 u8 frame_type;
225 packet >> frame_type;
226 WifiPacket::PacketType type = static_cast<WifiPacket::PacketType>(frame_type);
227
228 wifi_packet.type = type;
229 packet >> wifi_packet.channel;
230 packet >> wifi_packet.transmitter_address;
231 packet >> wifi_packet.destination_address;
232
233 u32 data_length;
234 packet >> data_length;
235
236 packet >> wifi_packet.data;
237
238 // TODO(B3N30): Invoke callbacks
239}
240
241void RoomMember::RoomMemberImpl::HandleChatPacket(const ENetEvent* event) {
242 Packet packet;
243 packet.Append(event->packet->data, event->packet->dataLength);
244
245 // Ignore the first byte, which is the message id.
246 packet.IgnoreBytes(sizeof(u8));
247
248 ChatEntry chat_entry{};
249 packet >> chat_entry.nickname;
250 packet >> chat_entry.message;
251 // TODO(B3N30): Invoke callbacks
252}
253
254void RoomMember::RoomMemberImpl::Disconnect() {
255 member_information.clear();
256 room_information.member_slots = 0;
257 room_information.name.clear();
258
259 if (!server)
260 return;
261 enet_peer_disconnect(server, 0);
262
263 ENetEvent event;
264 while (enet_host_service(client, &event, ConnectionTimeoutMs) > 0) {
265 switch (event.type) {
266 case ENET_EVENT_TYPE_RECEIVE:
267 enet_packet_destroy(event.packet); // Ignore all incoming data
268 break;
269 case ENET_EVENT_TYPE_DISCONNECT:
270 server = nullptr;
271 return;
272 }
273 }
274 // didn't disconnect gracefully force disconnect
275 enet_peer_reset(server);
276 server = nullptr;
277}
278
279// RoomMember
23RoomMember::RoomMember() : room_member_impl{std::make_unique<RoomMemberImpl>()} { 280RoomMember::RoomMember() : room_member_impl{std::make_unique<RoomMemberImpl>()} {
24 room_member_impl->client = enet_host_create(nullptr, 1, NumChannels, 0, 0); 281 room_member_impl->client = enet_host_create(nullptr, 1, NumChannels, 0, 0);
25 ASSERT_MSG(room_member_impl->client != nullptr, "Could not create client"); 282 ASSERT_MSG(room_member_impl->client != nullptr, "Could not create client");
@@ -34,17 +291,44 @@ RoomMember::State RoomMember::GetState() const {
34 return room_member_impl->state; 291 return room_member_impl->state;
35} 292}
36 293
294const RoomMember::MemberList& RoomMember::GetMemberInformation() const {
295 return room_member_impl->member_information;
296}
297
298const std::string& RoomMember::GetNickname() const {
299 return room_member_impl->nickname;
300}
301
302const MacAddress& RoomMember::GetMacAddress() const {
303 ASSERT_MSG(IsConnected(), "Tried to get MAC address while not connected");
304 return room_member_impl->mac_address;
305}
306
307RoomInformation RoomMember::GetRoomInformation() const {
308 return room_member_impl->room_information;
309}
310
37void RoomMember::Join(const std::string& nick, const char* server_addr, u16 server_port, 311void RoomMember::Join(const std::string& nick, const char* server_addr, u16 server_port,
38 u16 client_port) { 312 u16 client_port, const MacAddress& preferred_mac) {
313 // If the member is connected, kill the connection first
314 if (room_member_impl->loop_thread && room_member_impl->loop_thread->joinable()) {
315 room_member_impl->SetState(State::Error);
316 room_member_impl->loop_thread->join();
317 room_member_impl->loop_thread.reset();
318 }
319 // If the thread isn't running but the ptr still exists, reset it
320 else if (room_member_impl->loop_thread) {
321 room_member_impl->loop_thread.reset();
322 }
323
39 ENetAddress address{}; 324 ENetAddress address{};
40 enet_address_set_host(&address, server_addr); 325 enet_address_set_host(&address, server_addr);
41 address.port = server_port; 326 address.port = server_port;
42
43 room_member_impl->server = 327 room_member_impl->server =
44 enet_host_connect(room_member_impl->client, &address, NumChannels, 0); 328 enet_host_connect(room_member_impl->client, &address, NumChannels, 0);
45 329
46 if (!room_member_impl->server) { 330 if (!room_member_impl->server) {
47 room_member_impl->state = State::Error; 331 room_member_impl->SetState(State::Error);
48 return; 332 return;
49 } 333 }
50 334
@@ -52,23 +336,47 @@ void RoomMember::Join(const std::string& nick, const char* server_addr, u16 serv
52 int net = enet_host_service(room_member_impl->client, &event, ConnectionTimeoutMs); 336 int net = enet_host_service(room_member_impl->client, &event, ConnectionTimeoutMs);
53 if (net > 0 && event.type == ENET_EVENT_TYPE_CONNECT) { 337 if (net > 0 && event.type == ENET_EVENT_TYPE_CONNECT) {
54 room_member_impl->nickname = nick; 338 room_member_impl->nickname = nick;
55 room_member_impl->state = State::Joining; 339 room_member_impl->SetState(State::Joining);
56 // TODO(B3N30): Send a join request with the nickname to the server 340 room_member_impl->StartLoop();
57 // TODO(B3N30): Start the receive thread 341 room_member_impl->SendJoinRequest(nick, preferred_mac);
58 } else { 342 } else {
59 room_member_impl->state = State::CouldNotConnect; 343 room_member_impl->SetState(State::CouldNotConnect);
60 } 344 }
61} 345}
62 346
63bool RoomMember::IsConnected() const { 347bool RoomMember::IsConnected() const {
64 return room_member_impl->state == State::Joining || room_member_impl->state == State::Joined; 348 return room_member_impl->IsConnected();
349}
350
351void RoomMember::SendWifiPacket(const WifiPacket& wifi_packet) {
352 Packet packet;
353 packet << static_cast<u8>(IdWifiPacket);
354 packet << static_cast<u8>(wifi_packet.type);
355 packet << wifi_packet.channel;
356 packet << wifi_packet.transmitter_address;
357 packet << wifi_packet.destination_address;
358 packet << wifi_packet.data;
359 room_member_impl->Send(std::move(packet));
360}
361
362void RoomMember::SendChatMessage(const std::string& message) {
363 Packet packet;
364 packet << static_cast<u8>(IdChatMessage);
365 packet << message;
366 room_member_impl->Send(std::move(packet));
367}
368
369void RoomMember::SendGameName(const std::string& game_name) {
370 Packet packet;
371 packet << static_cast<u8>(IdSetGameName);
372 packet << game_name;
373 room_member_impl->Send(std::move(packet));
65} 374}
66 375
67void RoomMember::Leave() { 376void RoomMember::Leave() {
68 enet_peer_disconnect(room_member_impl->server, 0); 377 room_member_impl->SetState(State::Idle);
69 room_member_impl->state = State::Idle; 378 room_member_impl->loop_thread->join();
70 // TODO(B3N30): Close the receive thread 379 room_member_impl->loop_thread.reset();
71 enet_peer_reset(room_member_impl->server);
72} 380}
73 381
74} // namespace Network 382} // namespace Network
diff --git a/src/network/room_member.h b/src/network/room_member.h
index 177622b69..bc1af3a7e 100644
--- a/src/network/room_member.h
+++ b/src/network/room_member.h
@@ -4,14 +4,32 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <atomic>
8#include <memory> 7#include <memory>
9#include <string> 8#include <string>
9#include <vector>
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "network/room.h" 11#include "network/room.h"
12 12
13namespace Network { 13namespace Network {
14 14
15/// Information about the received WiFi packets.
16/// Acts as our own 802.11 header.
17struct WifiPacket {
18 enum class PacketType : u8 { Beacon, Data, Authentication, AssociationResponse };
19 PacketType type; ///< The type of 802.11 frame.
20 std::vector<u8> data; ///< Raw 802.11 frame data, starting at the management frame header
21 /// for management frames.
22 MacAddress transmitter_address; ///< Mac address of the transmitter.
23 MacAddress destination_address; ///< Mac address of the receiver.
24 u8 channel; ///< WiFi channel where this frame was transmitted.
25};
26
27/// Represents a chat message.
28struct ChatEntry {
29 std::string nickname; ///< Nickname of the client who sent this message.
30 std::string message; ///< Body of the message.
31};
32
15/** 33/**
16 * This is what a client [person joining a server] would use. 34 * This is what a client [person joining a server] would use.
17 * It also has to be used if you host a game yourself (You'd create both, a Room and a 35 * It also has to be used if you host a game yourself (You'd create both, a Room and a
@@ -29,9 +47,18 @@ public:
29 // Reasons why connection was rejected 47 // Reasons why connection was rejected
30 NameCollision, ///< Somebody is already using this name 48 NameCollision, ///< Somebody is already using this name
31 MacCollision, ///< Somebody is already using that mac-address 49 MacCollision, ///< Somebody is already using that mac-address
50 WrongVersion, ///< The room version is not the same as for this RoomMember
32 CouldNotConnect ///< The room is not responding to a connection attempt 51 CouldNotConnect ///< The room is not responding to a connection attempt
33 }; 52 };
34 53
54 struct MemberInformation {
55 std::string nickname; ///< Nickname of the member.
56 std::string game_name; ///< Name of the game they're currently playing, or empty if they're
57 /// not playing anything.
58 MacAddress mac_address; ///< MAC address associated with this member.
59 };
60 using MemberList = std::vector<MemberInformation>;
61
35 RoomMember(); 62 RoomMember();
36 ~RoomMember(); 63 ~RoomMember();
37 64
@@ -41,6 +68,26 @@ public:
41 State GetState() const; 68 State GetState() const;
42 69
43 /** 70 /**
71 * Returns information about the members in the room we're currently connected to.
72 */
73 const MemberList& GetMemberInformation() const;
74
75 /**
76 * Returns the nickname of the RoomMember.
77 */
78 const std::string& GetNickname() const;
79
80 /**
81 * Returns the MAC address of the RoomMember.
82 */
83 const MacAddress& GetMacAddress() const;
84
85 /**
86 * Returns information about the room we're currently connected to.
87 */
88 RoomInformation GetRoomInformation() const;
89
90 /**
44 * Returns whether we're connected to a server or not. 91 * Returns whether we're connected to a server or not.
45 */ 92 */
46 bool IsConnected() const; 93 bool IsConnected() const;
@@ -50,7 +97,26 @@ public:
50 * This may fail if the username is already taken. 97 * This may fail if the username is already taken.
51 */ 98 */
52 void Join(const std::string& nickname, const char* server_addr = "127.0.0.1", 99 void Join(const std::string& nickname, const char* server_addr = "127.0.0.1",
53 const u16 serverPort = DefaultRoomPort, const u16 clientPort = 0); 100 const u16 serverPort = DefaultRoomPort, const u16 clientPort = 0,
101 const MacAddress& preferred_mac = NoPreferredMac);
102
103 /**
104 * Sends a WiFi packet to the room.
105 * @param packet The WiFi packet to send.
106 */
107 void SendWifiPacket(const WifiPacket& packet);
108
109 /**
110 * Sends a chat message to the room.
111 * @param message The contents of the message.
112 */
113 void SendChatMessage(const std::string& message);
114
115 /**
116 * Sends the current game name to the room.
117 * @param game_name The game name.
118 */
119 void SendGameName(const std::string& game_name);
54 120
55 /** 121 /**
56 * Leaves the current room. 122 * Leaves the current room.