diff --git a/Source/dvlnet/base_client.cpp b/Source/dvlnet/base_client.cpp index 5731fd363d2..a0b334fb336 100644 --- a/Source/dvlnet/base_client.cpp +++ b/Source/dvlnet/base_client.cpp @@ -40,7 +40,10 @@ void base_client::disconnect_net(plr_t pnum) void base_client::recv_connect(packet& pkt) { - // connected_table[pkt.pktConnectPlr()] = CON_CONNECTED; // this can probably be removed + plr_t pkt_src = pkt.pktSrc(); + + if (pkt_src < MAX_PLRS) + connected_table[pkt_src] = CON_CONNECTED; } void base_client::recv_accept(packet& pkt) @@ -63,7 +66,13 @@ void base_client::recv_accept(packet& pkt) plr_self = PLR_BROADCAST; return; } - connected_table[plr_self] = CON_CONNECTED; + plr_t pmask = pkt.pktJoinAccMsk(); + // assert(pmask & (1 << plr_self)); + for (int i = 0; i < MAX_PLRS; i++) { + if (pmask & (1 << i)) { + connected_table[i] = CON_CONNECTED; + } + } #ifdef ZEROTIER // we joined and did not create game_init_info = buffer_t((BYTE*)&pkt_info, (BYTE*)&pkt_info + sizeof(SNetGameData)); @@ -120,9 +129,6 @@ void base_client::recv_local(packet& pkt) // FIXME: the server could still impersonate a player... plr_t pkt_plr = pkt.pktSrc(); - if (pkt_plr < MAX_PLRS) { - connected_table[pkt_plr] |= CON_CONNECTED; - } switch (pkt.pktType()) { case PT_MESSAGE: net_assert(pkt_plr < MAX_PLRS || pkt_plr == SNPLAYER_MASTER); diff --git a/Source/dvlnet/base_protocol.h b/Source/dvlnet/base_protocol.h index 2ad1a6b5850..e621f9f1263 100644 --- a/Source/dvlnet/base_protocol.h +++ b/Source/dvlnet/base_protocol.h @@ -209,27 +209,43 @@ void base_protocol

::poll() template void base_protocol

::handle_join_request(packet& pkt, endpoint sender) { - plr_t i; - for (i = 0; i < MAX_PLRS; i++) { - if (i != plr_self && !peers[i]) { - peers[i] = sender; + plr_t i, pnum, pmask; + packet* reply; + + for (pnum = 0; pnum < MAX_PLRS; pnum++) { + if (pnum != plr_self && !peers[pnum]) { + peers[pnum] = sender; break; } } - if (i >= MAX_PLRS) { + if (pnum >= MAX_PLRS) { //already full return; } - for (plr_t j = 0; j < MAX_PLRS; j++) { - if ((j != plr_self) && (j != i) && peers[j]) { - packet* infopkt = pktfty.make_out_packet(PLR_MASTER, PLR_BROADCAST, j, buffer_t(peers[j].addr.begin(), peers[j].addr.end())); - proto.send(sender, infopkt->encrypted_data()); - delete infopkt; + // reply to the new player + pmask = 0; + for (i = 0; i < MAX_PLRS; i++) { + if (peers[i]) { + static_assert(sizeof(pmask) * 8 >= MAX_PLRS, "handle_join_request can not send the active connections to the client."); + pmask |= 1 << i; } } - packet* reply = pktfty.make_out_packet(plr_self, PLR_BROADCAST, pkt.pktJoinReqCookie(), i, game_init_info); + reply = pktfty.make_out_packet(plr_self, PLR_BROADCAST, pkt.pktJoinReqCookie(), pnum, game_init_info, pmask); proto.send(sender, reply->encrypted_data()); delete reply; + // notify the old players + reply = pktfty.make_out_packet(pnum, PLR_BROADCAST, PLR_MASTER, buffer_t()); + send_packet(*reply); + delete reply; + // send the addresses of the old players to the new player TODO: send with PT_JOIN_ACCEPT? + pmask &= ~((1 << pnum) | (1 << plr_self)); + for (plr_t i = 0; i < MAX_PLRS; i++) { + if (pmask & (1 << i)) { + reply = pktfty.make_out_packet(PLR_MASTER, PLR_BROADCAST, i, buffer_t(peers[i].addr.begin(), peers[i].addr.end())); + proto.send(sender, reply->encrypted_data()); + delete reply; + } + } } template diff --git a/Source/dvlnet/packet.h b/Source/dvlnet/packet.h index 30ea822bffe..db913f3454b 100644 --- a/Source/dvlnet/packet.h +++ b/Source/dvlnet/packet.h @@ -19,7 +19,7 @@ enum packet_type : uint8_t { PT_TURN, PT_JOIN_REQUEST, PT_JOIN_ACCEPT, - PT_CONNECT, // tcpd, zt-only + PT_CONNECT, // tcp, zt-only PT_DISCONNECT, PT_INFO_REQUEST, // zt-only PT_INFO_REPLY, // zt-only @@ -67,6 +67,7 @@ typedef struct NetPktJoinAccept { cookie_t m_cookie; plr_t m_newplr; SNetGameData m_info; + plr_t m_plrmask; } NetPktJoinAccept; typedef struct NetPktConnect { @@ -154,6 +155,10 @@ class packet { { return reinterpret_cast(decrypted_buffer.data())->m_info; } + plr_t pktJoinAccMsk() const + { + return reinterpret_cast(decrypted_buffer.data())->m_plrmask; + } // PT_INFO_REPLY buffer_t::const_iterator pktInfoReplyNameBegin() const { @@ -259,7 +264,7 @@ inline void packet_out::create(plr_t s, plr_t d, cookie_t c) template <> inline void packet_out::create(plr_t s, plr_t d, cookie_t c, - plr_t n, buffer_t i) + plr_t n, buffer_t i, plr_t p) { decrypted_buffer.resize(sizeof(NetPktJoinAccept)); NetPktJoinAccept* data = (NetPktJoinAccept*)decrypted_buffer.data(); @@ -268,6 +273,7 @@ inline void packet_out::create(plr_t s, plr_t d, cookie_t c, data->npHdr.m_dest = d; data->m_cookie = c; data->m_newplr = n; + data->m_plrmask = p; memcpy(&data->m_info, i.data(), sizeof(SNetGameData)); } diff --git a/Source/dvlnet/tcp_server.cpp b/Source/dvlnet/tcp_server.cpp index 01c983f3922..eebddef1ec2 100644 --- a/Source/dvlnet/tcp_server.cpp +++ b/Source/dvlnet/tcp_server.cpp @@ -144,7 +144,8 @@ void tcp_server::handle_recv(const scc& con, const asio::error_code& ec, size_t bool tcp_server::handle_recv_newplr(const scc& con, packet& pkt) { - plr_t i, pnum; + plr_t i, pnum, pmask; + packet* reply; if (pkt.pktType() != PT_JOIN_REQUEST) { // DoLog("Invalid join packet."); @@ -162,18 +163,32 @@ bool tcp_server::handle_recv_newplr(const scc& con, packet& pkt) pending_connections[i] = NULL; active_connections[pnum] = con; con->pnum = pnum; - packet* reply = pktfty.make_out_packet(PLR_MASTER, PLR_BROADCAST, pkt.pktJoinReqCookie(), pnum, game_init_info); + // reply to the new player + pmask = 0; + for (i = 0; i < MAX_PLRS; i++) { + if (active_connections[i] != NULL) { + static_assert(sizeof(pmask) * 8 >= MAX_PLRS, "handle_recv_newplr can not send the active connections to the client."); + pmask |= 1 << i; + } + } + reply = pktfty.make_out_packet(PLR_MASTER, PLR_BROADCAST, pkt.pktJoinReqCookie(), pnum, game_init_info, pmask); start_send(con, *reply); delete reply; + // notify the old players + reply = pktfty.make_out_packet(pnum, PLR_BROADCAST, PLR_MASTER, buffer_t()); + send_packet(*reply); + delete reply; //send_connect(con); + // send the addresses of the old players to the new player TODO: send with PT_JOIN_ACCEPT? if (serverType == SRV_DIRECT) { + pmask &= ~(1 << pnum); std::string addr; for (i = 0; i < MAX_PLRS; i++) { - if (active_connections[i] != NULL && active_connections[i] != con) { + if (pmask & (1 << i)) { endpoint_to_string(active_connections[i], addr); - packet* oldConPkt = pktfty.make_out_packet(PLR_MASTER, PLR_BROADCAST, i, buffer_t(addr.begin(), addr.end())); - start_send(con, *oldConPkt); - delete oldConPkt; + reply = pktfty.make_out_packet(PLR_MASTER, PLR_BROADCAST, i, buffer_t(addr.begin(), addr.end())); + start_send(con, *reply); + delete reply; } } } diff --git a/Source/dvlnet/tcpd_client.cpp b/Source/dvlnet/tcpd_client.cpp index 8185ec76db0..a4d8fe1c0e9 100644 --- a/Source/dvlnet/tcpd_client.cpp +++ b/Source/dvlnet/tcpd_client.cpp @@ -133,6 +133,8 @@ plr_t tcpd_client::next_free_queue() void tcpd_client::recv_connect(packet& pkt) { + base_client::recv_connect(pkt); + plr_t pnum = pkt.pktConnectPlr(); if (pnum == plr_self || pnum >= MAX_PLRS || active_connections[pnum] != NULL) return;