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;