Skip to content

Commit

Permalink
Rework packet receiving in ServerThread
Browse files Browse the repository at this point in the history
Notably it tries to receive all queued packets
between server steps, not just one.
  • Loading branch information
sfan5 committed Nov 19, 2019
1 parent 9776460 commit c10952b
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 24 deletions.
27 changes: 22 additions & 5 deletions src/network/connection.cpp
Expand Up @@ -1323,24 +1323,29 @@ void Connection::Disconnect()
putCommand(c);
}

void Connection::Receive(NetworkPacket* pkt)
bool Connection::Receive(NetworkPacket *pkt, u32 timeout)
{
/*
Note that this function can potentially wait infinitely if non-data
events keep happening before the timeout expires.
This is not considered to be a problem (is it?)
*/
for(;;) {
ConnectionEvent e = waitEvent(m_bc_receive_timeout);
ConnectionEvent e = waitEvent(timeout);
if (e.type != CONNEVENT_NONE)
LOG(dout_con << getDesc() << ": Receive: got event: "
<< e.describe() << std::endl);
switch(e.type) {
case CONNEVENT_NONE:
throw NoIncomingDataException("No incoming data");
return false;
case CONNEVENT_DATA_RECEIVED:
// Data size is lesser than command size, ignoring packet
if (e.data.getSize() < 2) {
continue;
}

pkt->putRawPacket(*e.data, e.data.getSize(), e.peer_id);
return;
return true;
case CONNEVENT_PEER_ADDED: {
UDPPeer tmp(e.peer_id, e.address, this);
if (m_bc_peerhandler)
Expand All @@ -1358,7 +1363,19 @@ void Connection::Receive(NetworkPacket* pkt)
"(port already in use?)");
}
}
throw NoIncomingDataException("No incoming data");
return false;
}

void Connection::Receive(NetworkPacket *pkt)
{
bool any = Receive(pkt, m_bc_receive_timeout);
if (!any)
throw NoIncomingDataException("No incoming data");
}

bool Connection::TryReceive(NetworkPacket *pkt)
{
return Receive(pkt, 0);
}

void Connection::Send(session_t peer_id, u8 channelnum,
Expand Down
3 changes: 3 additions & 0 deletions src/network/connection.h
Expand Up @@ -771,6 +771,7 @@ class Connection
bool Connected();
void Disconnect();
void Receive(NetworkPacket* pkt);
bool TryReceive(NetworkPacket *pkt);
void Send(session_t peer_id, u8 channelnum, NetworkPacket *pkt, bool reliable);
session_t GetPeerID() const { return m_peer_id; }
Address GetPeerAddress(session_t peer_id);
Expand Down Expand Up @@ -803,6 +804,8 @@ class Connection
UDPSocket m_udpSocket;
MutexedQueue<ConnectionCommand> m_command_queue;

bool Receive(NetworkPacket *pkt, u32 timeout);

void putEvent(ConnectionEvent &e);

void TriggerSend();
Expand Down
9 changes: 9 additions & 0 deletions src/network/networkpacket.cpp
Expand Up @@ -66,6 +66,15 @@ void NetworkPacket::putRawPacket(u8 *data, u32 datasize, session_t peer_id)
memcpy(m_data.data(), &data[2], m_datasize);
}

void NetworkPacket::clear()
{
m_data.clear();
m_datasize = 0;
m_read_offset = 0;
m_command = 0;
m_peer_id = 0;
}

const char* NetworkPacket::getString(u32 from_offset)
{
checkReadOffset(from_offset, 0);
Expand Down
1 change: 1 addition & 0 deletions src/network/networkpacket.h
Expand Up @@ -35,6 +35,7 @@ class NetworkPacket
~NetworkPacket();

void putRawPacket(u8 *data, u32 datasize, session_t peer_id);
void clear();

// Getters
u32 getSize() const { return m_datasize; }
Expand Down
70 changes: 51 additions & 19 deletions src/server.cpp
Expand Up @@ -93,6 +93,15 @@ void *ServerThread::run()
{
BEGIN_DEBUG_EXCEPTION_HANDLER

/*
* The real business of the server happens on the ServerThread.
* How this works:
* AsyncRunStep() runs an actual server step as soon as enough time has
* passed (dedicated_server_loop keeps track of that).
* Receive() blocks at least(!) 30ms waiting for a packet (so this loop
* doesn't busy wait) and will process any remaining packets.
*/

m_server->AsyncRunStep(true);

while (!stopRequested()) {
Expand All @@ -101,7 +110,6 @@ void *ServerThread::run()

m_server->Receive();

} catch (con::NoIncomingDataException &e) {
} catch (con::PeerNotFoundException &e) {
infostream<<"Server: PeerNotFoundException"<<std::endl;
} catch (ClientNotFoundException &e) {
Expand Down Expand Up @@ -911,24 +919,43 @@ void Server::AsyncRunStep(bool initial_step)

void Server::Receive()
{
session_t peer_id = 0;
try {
NetworkPacket pkt;
m_con->Receive(&pkt);
peer_id = pkt.getPeerId();
ProcessData(&pkt);
} catch (const con::InvalidIncomingDataException &e) {
infostream << "Server::Receive(): InvalidIncomingDataException: what()="
<< e.what() << std::endl;
} catch (const SerializationError &e) {
infostream << "Server::Receive(): SerializationError: what()="
<< e.what() << std::endl;
} catch (const ClientStateError &e) {
errorstream << "ProcessData: peer=" << peer_id << e.what() << std::endl;
DenyAccess_Legacy(peer_id, L"Your client sent something server didn't expect."
L"Try reconnecting or updating your client");
} catch (const con::PeerNotFoundException &e) {
// Do nothing
NetworkPacket pkt;
session_t peer_id;
bool first = true;
for (;;) {
pkt.clear();
peer_id = 0;
try {
/*
In the first iteration *wait* for a packet, afterwards process
all packets that are immediately available (no waiting).
*/
if (first) {
m_con->Receive(&pkt);
first = false;
} else {
if (!m_con->TryReceive(&pkt))
return;
}

peer_id = pkt.getPeerId();
ProcessData(&pkt);
} catch (const con::InvalidIncomingDataException &e) {
infostream << "Server::Receive(): InvalidIncomingDataException: what()="
<< e.what() << std::endl;
} catch (const SerializationError &e) {
infostream << "Server::Receive(): SerializationError: what()="
<< e.what() << std::endl;
} catch (const ClientStateError &e) {
errorstream << "ProcessData: peer=" << peer_id << " what()="
<< e.what() << std::endl;
DenyAccess_Legacy(peer_id, L"Your client sent something server didn't expect."
L"Try reconnecting or updating your client");
} catch (const con::PeerNotFoundException &e) {
// Do nothing
} catch (const con::NoIncomingDataException &e) {
return;
}
}
}

Expand Down Expand Up @@ -3728,6 +3755,11 @@ void dedicated_server_loop(Server &server, bool &kill)
static thread_local const float profiler_print_interval =
g_settings->getFloat("profiler_print_interval");

/*
* The dedicated server loop only does time-keeping (in Server::step) and
* provides a way to main.cpp to kill the server externally (bool &kill).
*/

for(;;) {
// This is kind of a hack but can be done like this
// because server.step() is very light
Expand Down

0 comments on commit c10952b

Please sign in to comment.