summaryrefslogtreecommitdiff
path: root/src/network/room.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/network/room.cpp')
-rw-r--r--src/network/room.cpp182
1 files changed, 76 insertions, 106 deletions
diff --git a/src/network/room.cpp b/src/network/room.cpp
index 3fc3a0383..8c63b255b 100644
--- a/src/network/room.cpp
+++ b/src/network/room.cpp
@@ -20,9 +20,7 @@ namespace Network {
20 20
21class Room::RoomImpl { 21class Room::RoomImpl {
22public: 22public:
23 // This MAC address is used to generate a 'Nintendo' like Mac address. 23 std::mt19937 random_gen; ///< Random number generator. Used for GenerateFakeIPAddress
24 const MacAddress NintendoOUI;
25 std::mt19937 random_gen; ///< Random number generator. Used for GenerateMacAddress
26 24
27 ENetHost* server = nullptr; ///< Network interface. 25 ENetHost* server = nullptr; ///< Network interface.
28 26
@@ -35,10 +33,9 @@ public:
35 std::string password; ///< The password required to connect to this room. 33 std::string password; ///< The password required to connect to this room.
36 34
37 struct Member { 35 struct Member {
38 std::string nickname; ///< The nickname of the member. 36 std::string nickname; ///< The nickname of the member.
39 std::string console_id_hash; ///< A hash of the console ID of the member. 37 GameInfo game_info; ///< The current game of the member
40 GameInfo game_info; ///< The current game of the member 38 IPv4Address fake_ip; ///< The assigned fake ip address of the member.
41 MacAddress mac_address; ///< The assigned mac address of the member.
42 /// Data of the user, often including authenticated forum username. 39 /// Data of the user, often including authenticated forum username.
43 VerifyUser::UserData user_data; 40 VerifyUser::UserData user_data;
44 ENetPeer* peer; ///< The remote peer. 41 ENetPeer* peer; ///< The remote peer.
@@ -51,8 +48,7 @@ public:
51 IPBanList ip_ban_list; ///< List of banned IP addresses 48 IPBanList ip_ban_list; ///< List of banned IP addresses
52 mutable std::mutex ban_list_mutex; ///< Mutex for the ban lists 49 mutable std::mutex ban_list_mutex; ///< Mutex for the ban lists
53 50
54 RoomImpl() 51 RoomImpl() : random_gen(std::random_device()()) {}
55 : NintendoOUI{0x00, 0x1F, 0x32, 0x00, 0x00, 0x00}, random_gen(std::random_device()()) {}
56 52
57 /// Thread that receives and dispatches network packets 53 /// Thread that receives and dispatches network packets
58 std::unique_ptr<std::thread> room_thread; 54 std::unique_ptr<std::thread> room_thread;
@@ -101,16 +97,10 @@ public:
101 bool IsValidNickname(const std::string& nickname) const; 97 bool IsValidNickname(const std::string& nickname) const;
102 98
103 /** 99 /**
104 * Returns whether the MAC address is valid, ie. isn't already taken by someone else in the 100 * Returns whether the fake ip address is valid, ie. isn't already taken by someone else in the
105 * room. 101 * room.
106 */ 102 */
107 bool IsValidMacAddress(const MacAddress& address) const; 103 bool IsValidFakeIPAddress(const IPv4Address& address) const;
108
109 /**
110 * Returns whether the console ID (hash) is valid, ie. isn't already taken by someone else in
111 * the room.
112 */
113 bool IsValidConsoleId(const std::string& console_id_hash) const;
114 104
115 /** 105 /**
116 * Returns whether a user has mod permissions. 106 * Returns whether a user has mod permissions.
@@ -128,15 +118,9 @@ public:
128 void SendNameCollision(ENetPeer* client); 118 void SendNameCollision(ENetPeer* client);
129 119
130 /** 120 /**
131 * Sends a ID_ROOM_MAC_COLLISION message telling the client that the MAC is invalid. 121 * Sends a ID_ROOM_IP_COLLISION message telling the client that the IP is invalid.
132 */ 122 */
133 void SendMacCollision(ENetPeer* client); 123 void SendIPCollision(ENetPeer* client);
134
135 /**
136 * Sends a IdConsoleIdCollison message telling the client that another member with the same
137 * console ID exists.
138 */
139 void SendConsoleIdCollision(ENetPeer* client);
140 124
141 /** 125 /**
142 * Sends a ID_ROOM_VERSION_MISMATCH message telling the client that the version is invalid. 126 * Sends a ID_ROOM_VERSION_MISMATCH message telling the client that the version is invalid.
@@ -152,13 +136,13 @@ public:
152 * Notifies the member that its connection attempt was successful, 136 * Notifies the member that its connection attempt was successful,
153 * and it is now part of the room. 137 * and it is now part of the room.
154 */ 138 */
155 void SendJoinSuccess(ENetPeer* client, MacAddress mac_address); 139 void SendJoinSuccess(ENetPeer* client, IPv4Address fake_ip);
156 140
157 /** 141 /**
158 * Notifies the member that its connection attempt was successful, 142 * Notifies the member that its connection attempt was successful,
159 * and it is now part of the room, and it has been granted mod permissions. 143 * and it is now part of the room, and it has been granted mod permissions.
160 */ 144 */
161 void SendJoinSuccessAsMod(ENetPeer* client, MacAddress mac_address); 145 void SendJoinSuccessAsMod(ENetPeer* client, IPv4Address fake_ip);
162 146
163 /** 147 /**
164 * Sends a IdHostKicked message telling the client that they have been kicked. 148 * Sends a IdHostKicked message telling the client that they have been kicked.
@@ -210,7 +194,7 @@ public:
210 * <u32> num_members: the number of currently joined clients 194 * <u32> num_members: the number of currently joined clients
211 * This is followed by the following three values for each member: 195 * This is followed by the following three values for each member:
212 * <String> nickname of that member 196 * <String> nickname of that member
213 * <MacAddress> mac_address of that member 197 * <IPv4Address> fake_ip of that member
214 * <String> game_name of that member 198 * <String> game_name of that member
215 */ 199 */
216 void BroadcastRoomInformation(); 200 void BroadcastRoomInformation();
@@ -219,13 +203,13 @@ public:
219 * Generates a free MAC address to assign to a new client. 203 * Generates a free MAC address to assign to a new client.
220 * The first 3 bytes are the NintendoOUI 0x00, 0x1F, 0x32 204 * The first 3 bytes are the NintendoOUI 0x00, 0x1F, 0x32
221 */ 205 */
222 MacAddress GenerateMacAddress(); 206 IPv4Address GenerateFakeIPAddress();
223 207
224 /** 208 /**
225 * Broadcasts this packet to all members except the sender. 209 * Broadcasts this packet to all members except the sender.
226 * @param event The ENet event containing the data 210 * @param event The ENet event containing the data
227 */ 211 */
228 void HandleWifiPacket(const ENetEvent* event); 212 void HandleProxyPacket(const ENetEvent* event);
229 213
230 /** 214 /**
231 * Extracts a chat entry from a received ENet packet and adds it to the chat queue. 215 * Extracts a chat entry from a received ENet packet and adds it to the chat queue.
@@ -237,7 +221,7 @@ public:
237 * Extracts the game name from a received ENet packet and broadcasts it. 221 * Extracts the game name from a received ENet packet and broadcasts it.
238 * @param event The ENet event that was received. 222 * @param event The ENet event that was received.
239 */ 223 */
240 void HandleGameNamePacket(const ENetEvent* event); 224 void HandleGameInfoPacket(const ENetEvent* event);
241 225
242 /** 226 /**
243 * Removes the client from the members list if it was in it and announces the change 227 * Removes the client from the members list if it was in it and announces the change
@@ -250,7 +234,7 @@ public:
250void Room::RoomImpl::ServerLoop() { 234void Room::RoomImpl::ServerLoop() {
251 while (state != State::Closed) { 235 while (state != State::Closed) {
252 ENetEvent event; 236 ENetEvent event;
253 if (enet_host_service(server, &event, 16) > 0) { 237 if (enet_host_service(server, &event, 5) > 0) {
254 switch (event.type) { 238 switch (event.type) {
255 case ENET_EVENT_TYPE_RECEIVE: 239 case ENET_EVENT_TYPE_RECEIVE:
256 switch (event.packet->data[0]) { 240 switch (event.packet->data[0]) {
@@ -258,10 +242,10 @@ void Room::RoomImpl::ServerLoop() {
258 HandleJoinRequest(&event); 242 HandleJoinRequest(&event);
259 break; 243 break;
260 case IdSetGameInfo: 244 case IdSetGameInfo:
261 HandleGameNamePacket(&event); 245 HandleGameInfoPacket(&event);
262 break; 246 break;
263 case IdWifiPacket: 247 case IdProxyPacket:
264 HandleWifiPacket(&event); 248 HandleProxyPacket(&event);
265 break; 249 break;
266 case IdChatMessage: 250 case IdChatMessage:
267 HandleChatPacket(&event); 251 HandleChatPacket(&event);
@@ -313,11 +297,8 @@ void Room::RoomImpl::HandleJoinRequest(const ENetEvent* event) {
313 std::string nickname; 297 std::string nickname;
314 packet.Read(nickname); 298 packet.Read(nickname);
315 299
316 std::string console_id_hash; 300 IPv4Address preferred_fake_ip;
317 packet.Read(console_id_hash); 301 packet.Read(preferred_fake_ip);
318
319 MacAddress preferred_mac;
320 packet.Read(preferred_mac);
321 302
322 u32 client_version; 303 u32 client_version;
323 packet.Read(client_version); 304 packet.Read(client_version);
@@ -338,20 +319,15 @@ void Room::RoomImpl::HandleJoinRequest(const ENetEvent* event) {
338 return; 319 return;
339 } 320 }
340 321
341 if (preferred_mac != NoPreferredMac) { 322 if (preferred_fake_ip != NoPreferredIP) {
342 // Verify if the preferred mac is available 323 // Verify if the preferred fake ip is available
343 if (!IsValidMacAddress(preferred_mac)) { 324 if (!IsValidFakeIPAddress(preferred_fake_ip)) {
344 SendMacCollision(event->peer); 325 SendIPCollision(event->peer);
345 return; 326 return;
346 } 327 }
347 } else { 328 } else {
348 // Assign a MAC address of this client automatically 329 // Assign a fake ip address of this client automatically
349 preferred_mac = GenerateMacAddress(); 330 preferred_fake_ip = GenerateFakeIPAddress();
350 }
351
352 if (!IsValidConsoleId(console_id_hash)) {
353 SendConsoleIdCollision(event->peer);
354 return;
355 } 331 }
356 332
357 if (client_version != network_version) { 333 if (client_version != network_version) {
@@ -361,8 +337,7 @@ void Room::RoomImpl::HandleJoinRequest(const ENetEvent* event) {
361 337
362 // At this point the client is ready to be added to the room. 338 // At this point the client is ready to be added to the room.
363 Member member{}; 339 Member member{};
364 member.mac_address = preferred_mac; 340 member.fake_ip = preferred_fake_ip;
365 member.console_id_hash = console_id_hash;
366 member.nickname = nickname; 341 member.nickname = nickname;
367 member.peer = event->peer; 342 member.peer = event->peer;
368 343
@@ -408,9 +383,9 @@ void Room::RoomImpl::HandleJoinRequest(const ENetEvent* event) {
408 // Notify everyone that the room information has changed. 383 // Notify everyone that the room information has changed.
409 BroadcastRoomInformation(); 384 BroadcastRoomInformation();
410 if (HasModPermission(event->peer)) { 385 if (HasModPermission(event->peer)) {
411 SendJoinSuccessAsMod(event->peer, preferred_mac); 386 SendJoinSuccessAsMod(event->peer, preferred_fake_ip);
412 } else { 387 } else {
413 SendJoinSuccess(event->peer, preferred_mac); 388 SendJoinSuccess(event->peer, preferred_fake_ip);
414 } 389 }
415} 390}
416 391
@@ -575,19 +550,11 @@ bool Room::RoomImpl::IsValidNickname(const std::string& nickname) const {
575 [&nickname](const auto& member) { return member.nickname != nickname; }); 550 [&nickname](const auto& member) { return member.nickname != nickname; });
576} 551}
577 552
578bool Room::RoomImpl::IsValidMacAddress(const MacAddress& address) const { 553bool Room::RoomImpl::IsValidFakeIPAddress(const IPv4Address& address) const {
579 // A MAC address is valid if it is not already taken by anybody else in the room. 554 // An IP address is valid if it is not already taken by anybody else in the room.
580 std::lock_guard lock(member_mutex); 555 std::lock_guard lock(member_mutex);
581 return std::all_of(members.begin(), members.end(), 556 return std::all_of(members.begin(), members.end(),
582 [&address](const auto& member) { return member.mac_address != address; }); 557 [&address](const auto& member) { return member.fake_ip != address; });
583}
584
585bool Room::RoomImpl::IsValidConsoleId(const std::string& console_id_hash) const {
586 // A Console ID is valid if it is not already taken by anybody else in the room.
587 std::lock_guard lock(member_mutex);
588 return std::all_of(members.begin(), members.end(), [&console_id_hash](const auto& member) {
589 return member.console_id_hash != console_id_hash;
590 });
591} 558}
592 559
593bool Room::RoomImpl::HasModPermission(const ENetPeer* client) const { 560bool Room::RoomImpl::HasModPermission(const ENetPeer* client) const {
@@ -621,19 +588,9 @@ void Room::RoomImpl::SendNameCollision(ENetPeer* client) {
621 enet_host_flush(server); 588 enet_host_flush(server);
622} 589}
623 590
624void Room::RoomImpl::SendMacCollision(ENetPeer* client) { 591void Room::RoomImpl::SendIPCollision(ENetPeer* client) {
625 Packet packet; 592 Packet packet;
626 packet.Write(static_cast<u8>(IdMacCollision)); 593 packet.Write(static_cast<u8>(IdIpCollision));
627
628 ENetPacket* enet_packet =
629 enet_packet_create(packet.GetData(), packet.GetDataSize(), ENET_PACKET_FLAG_RELIABLE);
630 enet_peer_send(client, 0, enet_packet);
631 enet_host_flush(server);
632}
633
634void Room::RoomImpl::SendConsoleIdCollision(ENetPeer* client) {
635 Packet packet;
636 packet.Write(static_cast<u8>(IdConsoleIdCollision));
637 594
638 ENetPacket* enet_packet = 595 ENetPacket* enet_packet =
639 enet_packet_create(packet.GetData(), packet.GetDataSize(), ENET_PACKET_FLAG_RELIABLE); 596 enet_packet_create(packet.GetData(), packet.GetDataSize(), ENET_PACKET_FLAG_RELIABLE);
@@ -672,20 +629,20 @@ void Room::RoomImpl::SendVersionMismatch(ENetPeer* client) {
672 enet_host_flush(server); 629 enet_host_flush(server);
673} 630}
674 631
675void Room::RoomImpl::SendJoinSuccess(ENetPeer* client, MacAddress mac_address) { 632void Room::RoomImpl::SendJoinSuccess(ENetPeer* client, IPv4Address fake_ip) {
676 Packet packet; 633 Packet packet;
677 packet.Write(static_cast<u8>(IdJoinSuccess)); 634 packet.Write(static_cast<u8>(IdJoinSuccess));
678 packet.Write(mac_address); 635 packet.Write(fake_ip);
679 ENetPacket* enet_packet = 636 ENetPacket* enet_packet =
680 enet_packet_create(packet.GetData(), packet.GetDataSize(), ENET_PACKET_FLAG_RELIABLE); 637 enet_packet_create(packet.GetData(), packet.GetDataSize(), ENET_PACKET_FLAG_RELIABLE);
681 enet_peer_send(client, 0, enet_packet); 638 enet_peer_send(client, 0, enet_packet);
682 enet_host_flush(server); 639 enet_host_flush(server);
683} 640}
684 641
685void Room::RoomImpl::SendJoinSuccessAsMod(ENetPeer* client, MacAddress mac_address) { 642void Room::RoomImpl::SendJoinSuccessAsMod(ENetPeer* client, IPv4Address fake_ip) {
686 Packet packet; 643 Packet packet;
687 packet.Write(static_cast<u8>(IdJoinSuccessAsMod)); 644 packet.Write(static_cast<u8>(IdJoinSuccessAsMod));
688 packet.Write(mac_address); 645 packet.Write(fake_ip);
689 ENetPacket* enet_packet = 646 ENetPacket* enet_packet =
690 enet_packet_create(packet.GetData(), packet.GetDataSize(), ENET_PACKET_FLAG_RELIABLE); 647 enet_packet_create(packet.GetData(), packet.GetDataSize(), ENET_PACKET_FLAG_RELIABLE);
691 enet_peer_send(client, 0, enet_packet); 648 enet_peer_send(client, 0, enet_packet);
@@ -818,9 +775,10 @@ void Room::RoomImpl::BroadcastRoomInformation() {
818 std::lock_guard lock(member_mutex); 775 std::lock_guard lock(member_mutex);
819 for (const auto& member : members) { 776 for (const auto& member : members) {
820 packet.Write(member.nickname); 777 packet.Write(member.nickname);
821 packet.Write(member.mac_address); 778 packet.Write(member.fake_ip);
822 packet.Write(member.game_info.name); 779 packet.Write(member.game_info.name);
823 packet.Write(member.game_info.id); 780 packet.Write(member.game_info.id);
781 packet.Write(member.game_info.version);
824 packet.Write(member.user_data.username); 782 packet.Write(member.user_data.username);
825 packet.Write(member.user_data.display_name); 783 packet.Write(member.user_data.display_name);
826 packet.Write(member.user_data.avatar_url); 784 packet.Write(member.user_data.avatar_url);
@@ -833,34 +791,44 @@ void Room::RoomImpl::BroadcastRoomInformation() {
833 enet_host_flush(server); 791 enet_host_flush(server);
834} 792}
835 793
836MacAddress Room::RoomImpl::GenerateMacAddress() { 794IPv4Address Room::RoomImpl::GenerateFakeIPAddress() {
837 MacAddress result_mac = 795 IPv4Address result_ip{192, 168, 0, 0};
838 NintendoOUI; // The first three bytes of each MAC address will be the NintendoOUI 796 std::uniform_int_distribution<> dis(0x01, 0xFE); // Random byte between 1 and 0xFE
839 std::uniform_int_distribution<> dis(0x00, 0xFF); // Random byte between 0 and 0xFF
840 do { 797 do {
841 for (std::size_t i = 3; i < result_mac.size(); ++i) { 798 for (std::size_t i = 2; i < result_ip.size(); ++i) {
842 result_mac[i] = dis(random_gen); 799 result_ip[i] = dis(random_gen);
843 } 800 }
844 } while (!IsValidMacAddress(result_mac)); 801 } while (!IsValidFakeIPAddress(result_ip));
845 return result_mac; 802
803 return result_ip;
846} 804}
847 805
848void Room::RoomImpl::HandleWifiPacket(const ENetEvent* event) { 806void Room::RoomImpl::HandleProxyPacket(const ENetEvent* event) {
849 Packet in_packet; 807 Packet in_packet;
850 in_packet.Append(event->packet->data, event->packet->dataLength); 808 in_packet.Append(event->packet->data, event->packet->dataLength);
851 in_packet.IgnoreBytes(sizeof(u8)); // Message type 809 in_packet.IgnoreBytes(sizeof(u8)); // Message type
852 in_packet.IgnoreBytes(sizeof(u8)); // WifiPacket Type 810
853 in_packet.IgnoreBytes(sizeof(u8)); // WifiPacket Channel 811 in_packet.IgnoreBytes(sizeof(u8)); // Domain
854 in_packet.IgnoreBytes(sizeof(MacAddress)); // WifiPacket Transmitter Address 812 in_packet.IgnoreBytes(sizeof(IPv4Address)); // IP
855 MacAddress destination_address; 813 in_packet.IgnoreBytes(sizeof(u16)); // Port
856 in_packet.Read(destination_address); 814
815 in_packet.IgnoreBytes(sizeof(u8)); // Domain
816 IPv4Address remote_ip;
817 in_packet.Read(remote_ip); // IP
818 in_packet.IgnoreBytes(sizeof(u16)); // Port
819
820 in_packet.IgnoreBytes(sizeof(u8)); // Protocol
821
822 bool broadcast;
823 in_packet.Read(broadcast); // Broadcast
857 824
858 Packet out_packet; 825 Packet out_packet;
859 out_packet.Append(event->packet->data, event->packet->dataLength); 826 out_packet.Append(event->packet->data, event->packet->dataLength);
860 ENetPacket* enet_packet = enet_packet_create(out_packet.GetData(), out_packet.GetDataSize(), 827 ENetPacket* enet_packet = enet_packet_create(out_packet.GetData(), out_packet.GetDataSize(),
861 ENET_PACKET_FLAG_RELIABLE); 828 ENET_PACKET_FLAG_RELIABLE);
862 829
863 if (destination_address == BroadcastMac) { // Send the data to everyone except the sender 830 const auto& destination_address = remote_ip;
831 if (broadcast) { // Send the data to everyone except the sender
864 std::lock_guard lock(member_mutex); 832 std::lock_guard lock(member_mutex);
865 bool sent_packet = false; 833 bool sent_packet = false;
866 for (const auto& member : members) { 834 for (const auto& member : members) {
@@ -877,16 +845,16 @@ void Room::RoomImpl::HandleWifiPacket(const ENetEvent* event) {
877 std::lock_guard lock(member_mutex); 845 std::lock_guard lock(member_mutex);
878 auto member = std::find_if(members.begin(), members.end(), 846 auto member = std::find_if(members.begin(), members.end(),
879 [destination_address](const Member& member_entry) -> bool { 847 [destination_address](const Member& member_entry) -> bool {
880 return member_entry.mac_address == destination_address; 848 return member_entry.fake_ip == destination_address;
881 }); 849 });
882 if (member != members.end()) { 850 if (member != members.end()) {
883 enet_peer_send(member->peer, 0, enet_packet); 851 enet_peer_send(member->peer, 0, enet_packet);
884 } else { 852 } else {
885 LOG_ERROR(Network, 853 LOG_ERROR(Network,
886 "Attempting to send to unknown MAC address: " 854 "Attempting to send to unknown IP address: "
887 "{:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}", 855 "{}.{}.{}.{}",
888 destination_address[0], destination_address[1], destination_address[2], 856 destination_address[0], destination_address[1], destination_address[2],
889 destination_address[3], destination_address[4], destination_address[5]); 857 destination_address[3]);
890 enet_packet_destroy(enet_packet); 858 enet_packet_destroy(enet_packet);
891 } 859 }
892 } 860 }
@@ -943,7 +911,7 @@ void Room::RoomImpl::HandleChatPacket(const ENetEvent* event) {
943 } 911 }
944} 912}
945 913
946void Room::RoomImpl::HandleGameNamePacket(const ENetEvent* event) { 914void Room::RoomImpl::HandleGameInfoPacket(const ENetEvent* event) {
947 Packet in_packet; 915 Packet in_packet;
948 in_packet.Append(event->packet->data, event->packet->dataLength); 916 in_packet.Append(event->packet->data, event->packet->dataLength);
949 917
@@ -951,6 +919,7 @@ void Room::RoomImpl::HandleGameNamePacket(const ENetEvent* event) {
951 GameInfo game_info; 919 GameInfo game_info;
952 in_packet.Read(game_info.name); 920 in_packet.Read(game_info.name);
953 in_packet.Read(game_info.id); 921 in_packet.Read(game_info.id);
922 in_packet.Read(game_info.version);
954 923
955 { 924 {
956 std::lock_guard lock(member_mutex); 925 std::lock_guard lock(member_mutex);
@@ -969,7 +938,8 @@ void Room::RoomImpl::HandleGameNamePacket(const ENetEvent* event) {
969 if (game_info.name.empty()) { 938 if (game_info.name.empty()) {
970 LOG_INFO(Network, "{} is not playing", display_name); 939 LOG_INFO(Network, "{} is not playing", display_name);
971 } else { 940 } else {
972 LOG_INFO(Network, "{} is playing {}", display_name, game_info.name); 941 LOG_INFO(Network, "{} is playing {} ({})", display_name, game_info.name,
942 game_info.version);
973 } 943 }
974 } 944 }
975 } 945 }
@@ -1073,7 +1043,7 @@ std::vector<Member> Room::GetRoomMemberList() const {
1073 member.username = member_impl.user_data.username; 1043 member.username = member_impl.user_data.username;
1074 member.display_name = member_impl.user_data.display_name; 1044 member.display_name = member_impl.user_data.display_name;
1075 member.avatar_url = member_impl.user_data.avatar_url; 1045 member.avatar_url = member_impl.user_data.avatar_url;
1076 member.mac_address = member_impl.mac_address; 1046 member.fake_ip = member_impl.fake_ip;
1077 member.game = member_impl.game_info; 1047 member.game = member_impl.game_info;
1078 member_list.push_back(member); 1048 member_list.push_back(member);
1079 } 1049 }