room.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454
  1. // Copyright 2017 Citra Emulator Project
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #include <algorithm>
  5. #include <atomic>
  6. #include <random>
  7. #include <thread>
  8. #include <vector>
  9. #include "enet/enet.h"
  10. #include "network/packet.h"
  11. #include "network/room.h"
  12. namespace Network {
  13. /// Maximum number of concurrent connections allowed to this room.
  14. static constexpr u32 MaxConcurrentConnections = 10;
  15. class Room::RoomImpl {
  16. public:
  17. // This MAC address is used to generate a 'Nintendo' like Mac address.
  18. const MacAddress NintendoOUI = {0x00, 0x1F, 0x32, 0x00, 0x00, 0x00};
  19. std::mt19937 random_gen; ///< Random number generator. Used for GenerateMacAddress
  20. ENetHost* server = nullptr; ///< Network interface.
  21. std::atomic<State> state{State::Closed}; ///< Current state of the room.
  22. RoomInformation room_information; ///< Information about this room.
  23. struct Member {
  24. std::string nickname; ///< The nickname of the member.
  25. std::string game_name; ///< The current game of the member
  26. MacAddress mac_address; ///< The assigned mac address of the member.
  27. ENetPeer* peer; ///< The remote peer.
  28. };
  29. using MemberList = std::vector<Member>;
  30. MemberList members; ///< Information about the members of this room.
  31. RoomImpl() : random_gen(std::random_device()()) {}
  32. /// Thread that receives and dispatches network packets
  33. std::unique_ptr<std::thread> room_thread;
  34. /// Thread function that will receive and dispatch messages until the room is destroyed.
  35. void ServerLoop();
  36. void StartLoop();
  37. /**
  38. * Parses and answers a room join request from a client.
  39. * Validates the uniqueness of the username and assigns the MAC address
  40. * that the client will use for the remainder of the connection.
  41. */
  42. void HandleJoinRequest(const ENetEvent* event);
  43. /**
  44. * Returns whether the nickname is valid, ie. isn't already taken by someone else in the room.
  45. */
  46. bool IsValidNickname(const std::string& nickname) const;
  47. /**
  48. * Returns whether the MAC address is valid, ie. isn't already taken by someone else in the
  49. * room.
  50. */
  51. bool IsValidMacAddress(const MacAddress& address) const;
  52. /**
  53. * Sends a ID_ROOM_NAME_COLLISION message telling the client that the name is invalid.
  54. */
  55. void SendNameCollision(ENetPeer* client);
  56. /**
  57. * Sends a ID_ROOM_MAC_COLLISION message telling the client that the MAC is invalid.
  58. */
  59. void SendMacCollision(ENetPeer* client);
  60. /**
  61. * Sends a ID_ROOM_VERSION_MISMATCH message telling the client that the version is invalid.
  62. */
  63. void SendVersionMismatch(ENetPeer* client);
  64. /**
  65. * Notifies the member that its connection attempt was successful,
  66. * and it is now part of the room.
  67. */
  68. void SendJoinSuccess(ENetPeer* client, MacAddress mac_address);
  69. /**
  70. * Notifies the members that the room is closed,
  71. */
  72. void SendCloseMessage();
  73. /**
  74. * Sends the information about the room, along with the list of members
  75. * to every connected client in the room.
  76. * The packet has the structure:
  77. * <MessageID>ID_ROOM_INFORMATION
  78. * <String> room_name
  79. * <u32> member_slots: The max number of clients allowed in this room
  80. * <u32> num_members: the number of currently joined clients
  81. * This is followed by the following three values for each member:
  82. * <String> nickname of that member
  83. * <MacAddress> mac_address of that member
  84. * <String> game_name of that member
  85. */
  86. void BroadcastRoomInformation();
  87. /**
  88. * Generates a free MAC address to assign to a new client.
  89. * The first 3 bytes are the NintendoOUI 0x00, 0x1F, 0x32
  90. */
  91. MacAddress GenerateMacAddress();
  92. /**
  93. * Broadcasts this packet to all members except the sender.
  94. * @param event The ENet event containing the data
  95. */
  96. void HandleWifiPacket(const ENetEvent* event);
  97. /**
  98. * Extracts a chat entry from a received ENet packet and adds it to the chat queue.
  99. * @param event The ENet event that was received.
  100. */
  101. void HandleChatPacket(const ENetEvent* event);
  102. /**
  103. * Extracts the game name from a received ENet packet and broadcasts it.
  104. * @param event The ENet event that was received.
  105. */
  106. void HandleGameNamePacket(const ENetEvent* event);
  107. /**
  108. * Removes the client from the members list if it was in it and announces the change
  109. * to all other clients.
  110. */
  111. void HandleClientDisconnection(ENetPeer* client);
  112. };
  113. // RoomImpl
  114. void Room::RoomImpl::ServerLoop() {
  115. while (state != State::Closed) {
  116. ENetEvent event;
  117. if (enet_host_service(server, &event, 100) > 0) {
  118. switch (event.type) {
  119. case ENET_EVENT_TYPE_RECEIVE:
  120. switch (event.packet->data[0]) {
  121. case IdJoinRequest:
  122. HandleJoinRequest(&event);
  123. break;
  124. case IdSetGameName:
  125. HandleGameNamePacket(&event);
  126. break;
  127. case IdWifiPacket:
  128. HandleWifiPacket(&event);
  129. break;
  130. case IdChatMessage:
  131. HandleChatPacket(&event);
  132. break;
  133. }
  134. enet_packet_destroy(event.packet);
  135. break;
  136. case ENET_EVENT_TYPE_DISCONNECT:
  137. HandleClientDisconnection(event.peer);
  138. break;
  139. }
  140. }
  141. }
  142. // Close the connection to all members:
  143. SendCloseMessage();
  144. }
  145. void Room::RoomImpl::StartLoop() {
  146. room_thread = std::make_unique<std::thread>(&Room::RoomImpl::ServerLoop, this);
  147. }
  148. void Room::RoomImpl::HandleJoinRequest(const ENetEvent* event) {
  149. Packet packet;
  150. packet.Append(event->packet->data, event->packet->dataLength);
  151. packet.IgnoreBytes(sizeof(u8)); // Igonore the message type
  152. std::string nickname;
  153. packet >> nickname;
  154. MacAddress preferred_mac;
  155. packet >> preferred_mac;
  156. u32 client_version;
  157. packet >> client_version;
  158. if (!IsValidNickname(nickname)) {
  159. SendNameCollision(event->peer);
  160. return;
  161. }
  162. if (preferred_mac != NoPreferredMac) {
  163. // Verify if the preferred mac is available
  164. if (!IsValidMacAddress(preferred_mac)) {
  165. SendMacCollision(event->peer);
  166. return;
  167. }
  168. } else {
  169. // Assign a MAC address of this client automatically
  170. preferred_mac = GenerateMacAddress();
  171. }
  172. if (client_version != network_version) {
  173. SendVersionMismatch(event->peer);
  174. return;
  175. }
  176. // At this point the client is ready to be added to the room.
  177. Member member{};
  178. member.mac_address = preferred_mac;
  179. member.nickname = nickname;
  180. member.peer = event->peer;
  181. members.push_back(std::move(member));
  182. // Notify everyone that the room information has changed.
  183. BroadcastRoomInformation();
  184. SendJoinSuccess(event->peer, preferred_mac);
  185. }
  186. bool Room::RoomImpl::IsValidNickname(const std::string& nickname) const {
  187. // A nickname is valid if it is not already taken by anybody else in the room.
  188. // TODO(B3N30): Check for empty names, spaces, etc.
  189. return std::all_of(members.begin(), members.end(),
  190. [&nickname](const auto& member) { return member.nickname != nickname; });
  191. }
  192. bool Room::RoomImpl::IsValidMacAddress(const MacAddress& address) const {
  193. // A MAC address is valid if it is not already taken by anybody else in the room.
  194. return std::all_of(members.begin(), members.end(),
  195. [&address](const auto& member) { return member.mac_address != address; });
  196. }
  197. void Room::RoomImpl::SendNameCollision(ENetPeer* client) {
  198. Packet packet;
  199. packet << static_cast<u8>(IdNameCollision);
  200. ENetPacket* enet_packet =
  201. enet_packet_create(packet.GetData(), packet.GetDataSize(), ENET_PACKET_FLAG_RELIABLE);
  202. enet_peer_send(client, 0, enet_packet);
  203. enet_host_flush(server);
  204. }
  205. void Room::RoomImpl::SendMacCollision(ENetPeer* client) {
  206. Packet packet;
  207. packet << static_cast<u8>(IdMacCollision);
  208. ENetPacket* enet_packet =
  209. enet_packet_create(packet.GetData(), packet.GetDataSize(), ENET_PACKET_FLAG_RELIABLE);
  210. enet_peer_send(client, 0, enet_packet);
  211. enet_host_flush(server);
  212. }
  213. void Room::RoomImpl::SendVersionMismatch(ENetPeer* client) {
  214. Packet packet;
  215. packet << static_cast<u8>(IdVersionMismatch);
  216. packet << network_version;
  217. ENetPacket* enet_packet =
  218. enet_packet_create(packet.GetData(), packet.GetDataSize(), ENET_PACKET_FLAG_RELIABLE);
  219. enet_peer_send(client, 0, enet_packet);
  220. enet_host_flush(server);
  221. }
  222. void Room::RoomImpl::SendJoinSuccess(ENetPeer* client, MacAddress mac_address) {
  223. Packet packet;
  224. packet << static_cast<u8>(IdJoinSuccess);
  225. packet << mac_address;
  226. ENetPacket* enet_packet =
  227. enet_packet_create(packet.GetData(), packet.GetDataSize(), ENET_PACKET_FLAG_RELIABLE);
  228. enet_peer_send(client, 0, enet_packet);
  229. enet_host_flush(server);
  230. }
  231. void Room::RoomImpl::SendCloseMessage() {
  232. Packet packet;
  233. packet << static_cast<u8>(IdCloseRoom);
  234. ENetPacket* enet_packet =
  235. enet_packet_create(packet.GetData(), packet.GetDataSize(), ENET_PACKET_FLAG_RELIABLE);
  236. for (auto& member : members) {
  237. enet_peer_send(member.peer, 0, enet_packet);
  238. }
  239. enet_host_flush(server);
  240. for (auto& member : members) {
  241. enet_peer_disconnect(member.peer, 0);
  242. }
  243. }
  244. void Room::RoomImpl::BroadcastRoomInformation() {
  245. Packet packet;
  246. packet << static_cast<u8>(IdRoomInformation);
  247. packet << room_information.name;
  248. packet << room_information.member_slots;
  249. packet << static_cast<u32>(members.size());
  250. for (const auto& member : members) {
  251. packet << member.nickname;
  252. packet << member.mac_address;
  253. packet << member.game_name;
  254. }
  255. ENetPacket* enet_packet =
  256. enet_packet_create(packet.GetData(), packet.GetDataSize(), ENET_PACKET_FLAG_RELIABLE);
  257. enet_host_broadcast(server, 0, enet_packet);
  258. enet_host_flush(server);
  259. }
  260. MacAddress Room::RoomImpl::GenerateMacAddress() {
  261. MacAddress result_mac =
  262. NintendoOUI; // The first three bytes of each MAC address will be the NintendoOUI
  263. std::uniform_int_distribution<> dis(0x00, 0xFF); // Random byte between 0 and 0xFF
  264. do {
  265. for (size_t i = 3; i < result_mac.size(); ++i) {
  266. result_mac[i] = dis(random_gen);
  267. }
  268. } while (!IsValidMacAddress(result_mac));
  269. return result_mac;
  270. }
  271. void Room::RoomImpl::HandleWifiPacket(const ENetEvent* event) {
  272. Packet in_packet;
  273. in_packet.Append(event->packet->data, event->packet->dataLength);
  274. in_packet.IgnoreBytes(sizeof(u8)); // Message type
  275. in_packet.IgnoreBytes(sizeof(u8)); // WifiPacket Type
  276. in_packet.IgnoreBytes(sizeof(u8)); // WifiPacket Channel
  277. in_packet.IgnoreBytes(sizeof(MacAddress)); // WifiPacket Transmitter Address
  278. MacAddress destination_address;
  279. in_packet >> destination_address;
  280. Packet out_packet;
  281. out_packet.Append(event->packet->data, event->packet->dataLength);
  282. ENetPacket* enet_packet = enet_packet_create(out_packet.GetData(), out_packet.GetDataSize(),
  283. ENET_PACKET_FLAG_RELIABLE);
  284. if (destination_address == BroadcastMac) { // Send the data to everyone except the sender
  285. for (const auto& member : members) {
  286. if (member.peer != event->peer)
  287. enet_peer_send(member.peer, 0, enet_packet);
  288. }
  289. } else { // Send the data only to the destination client
  290. auto member = std::find_if(members.begin(), members.end(),
  291. [destination_address](const Member& member) -> bool {
  292. return member.mac_address == destination_address;
  293. });
  294. if (member != members.end()) {
  295. enet_peer_send(member->peer, 0, enet_packet);
  296. }
  297. }
  298. enet_host_flush(server);
  299. }
  300. void Room::RoomImpl::HandleChatPacket(const ENetEvent* event) {
  301. Packet in_packet;
  302. in_packet.Append(event->packet->data, event->packet->dataLength);
  303. in_packet.IgnoreBytes(sizeof(u8)); // Igonore the message type
  304. std::string message;
  305. in_packet >> message;
  306. auto CompareNetworkAddress = [event](const Member member) -> bool {
  307. return member.peer == event->peer;
  308. };
  309. const auto sending_member = std::find_if(members.begin(), members.end(), CompareNetworkAddress);
  310. if (sending_member == members.end()) {
  311. return; // Received a chat message from a unknown sender
  312. }
  313. Packet out_packet;
  314. out_packet << static_cast<u8>(IdChatMessage);
  315. out_packet << sending_member->nickname;
  316. out_packet << message;
  317. ENetPacket* enet_packet = enet_packet_create(out_packet.GetData(), out_packet.GetDataSize(),
  318. ENET_PACKET_FLAG_RELIABLE);
  319. for (const auto& member : members) {
  320. if (member.peer != event->peer)
  321. enet_peer_send(member.peer, 0, enet_packet);
  322. }
  323. enet_host_flush(server);
  324. }
  325. void Room::RoomImpl::HandleGameNamePacket(const ENetEvent* event) {
  326. Packet in_packet;
  327. in_packet.Append(event->packet->data, event->packet->dataLength);
  328. in_packet.IgnoreBytes(sizeof(u8)); // Igonore the message type
  329. std::string game_name;
  330. in_packet >> game_name;
  331. auto member =
  332. std::find_if(members.begin(), members.end(),
  333. [event](const Member& member) -> bool { return member.peer == event->peer; });
  334. if (member != members.end()) {
  335. member->game_name = game_name;
  336. BroadcastRoomInformation();
  337. }
  338. }
  339. void Room::RoomImpl::HandleClientDisconnection(ENetPeer* client) {
  340. // Remove the client from the members list.
  341. members.erase(std::remove_if(members.begin(), members.end(),
  342. [client](const Member& member) { return member.peer == client; }),
  343. members.end());
  344. // Announce the change to all clients.
  345. enet_peer_disconnect(client, 0);
  346. BroadcastRoomInformation();
  347. }
  348. // Room
  349. Room::Room() : room_impl{std::make_unique<RoomImpl>()} {}
  350. Room::~Room() = default;
  351. void Room::Create(const std::string& name, const std::string& server_address, u16 server_port) {
  352. ENetAddress address;
  353. address.host = ENET_HOST_ANY;
  354. if (!server_address.empty()) {
  355. enet_address_set_host(&address, server_address.c_str());
  356. }
  357. address.port = server_port;
  358. room_impl->server = enet_host_create(&address, MaxConcurrentConnections, NumChannels, 0, 0);
  359. // TODO(B3N30): Allow specifying the maximum number of concurrent connections.
  360. room_impl->state = State::Open;
  361. room_impl->room_information.name = name;
  362. room_impl->room_information.member_slots = MaxConcurrentConnections;
  363. room_impl->StartLoop();
  364. }
  365. Room::State Room::GetState() const {
  366. return room_impl->state;
  367. }
  368. const RoomInformation& Room::GetRoomInformation() const {
  369. return room_impl->room_information;
  370. }
  371. void Room::Destroy() {
  372. room_impl->state = State::Closed;
  373. room_impl->room_thread->join();
  374. room_impl->room_thread.reset();
  375. if (room_impl->server) {
  376. enet_host_destroy(room_impl->server);
  377. }
  378. room_impl->room_information = {};
  379. room_impl->server = nullptr;
  380. room_impl->members.clear();
  381. room_impl->room_information.member_slots = 0;
  382. room_impl->room_information.name.clear();
  383. }
  384. } // namespace Network