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