summaryrefslogtreecommitdiff
path: root/src/network/room.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/network/room.cpp402
1 files changed, 398 insertions, 4 deletions
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