/* * @brief */ static void Sv_ReadPackets(void) { int32_t i; sv_client_t * cl; byte qport; while (Net_ReceiveDatagram(NS_UDP_SERVER, &net_from, &net_message)) { // check for connectionless packet (0xffffffff) first if (*(uint32_t *) net_message.data == 0xffffffff) { Sv_ConnectionlessPacket(); continue; } // read the qport out of the message so we can fix up // stupid address translating routers Net_BeginReading(&net_message); Net_ReadLong(&net_message); // sequence number Net_ReadLong(&net_message); // sequence number qport = Net_ReadByte(&net_message) & 0xff; // check for packets from connected clients for (i = 0, cl = svs.clients; i < sv_max_clients->integer; i++, cl++) { if (cl->state == SV_CLIENT_FREE) continue; if (!Net_CompareClientNetaddr(&net_from, &cl->net_chan.remote_address)) continue; if (cl->net_chan.qport != qport) continue; if (cl->net_chan.remote_address.port != net_from.port) { Com_Warn("Fixing up a translated port\n"); cl->net_chan.remote_address.port = net_from.port; } // this is a valid, sequenced packet, so process it if (Netchan_Process(&cl->net_chan, &net_message)) { cl->last_message = svs.real_time; // nudge timeout Sv_ParseClientMessage(cl); } // we've processed the packet for the correct client, so break break; } } }
/* * @brief A connection-less packet has four leading 0xff bytes to distinguish * it from a game channel. Clients that are in the game can still send these, * and they will be handled here. */ static void Sv_ConnectionlessPacket(void) { Net_BeginReading(&net_message); Net_ReadLong(&net_message); // skip the -1 marker const char *s = Net_ReadStringLine(&net_message); Cmd_TokenizeString(s); const char *c = Cmd_Argv(0); const char *a = Net_NetaddrToString(&net_from); Com_Debug("Packet from %s: %s\n", a, c); if (!g_strcmp0(c, "ping")) Svc_Ping(); else if (!g_strcmp0(c, "ack")) Svc_Ack(); else if (!g_strcmp0(c, "status")) Svc_Status(); else if (!g_strcmp0(c, "info")) Svc_Info(); else if (!g_strcmp0(c, "get_challenge")) Svc_GetChallenge(); else if (!g_strcmp0(c, "connect")) Svc_Connect(); else if (!g_strcmp0(c, "rcon")) Svc_RemoteCommand(); else Com_Print("Bad connectionless packet from %s:\n%s\n", a, s); }
/* * @brief */ static void Cl_ParseServerData(void) { char *str; int32_t i; // wipe the cl_client_t struct Cl_ClearState(); cls.state = CL_CONNECTED; cls.key_state.dest = KEY_CONSOLE; // parse protocol version number i = Net_ReadLong(&net_message); // ensure protocol matches if (i != PROTOCOL) { Com_Error(ERR_DROP, "Server is using unknown protocol %d\n", i); } // retrieve spawn count and packet rate cl.server_count = Net_ReadLong(&net_message); cl.server_hz = Net_ReadLong(&net_message); // determine if we're viewing a demo cl.demo_server = Net_ReadByte(&net_message); // game directory str = Net_ReadString(&net_message); if (g_strcmp0(Cvar_GetString("game"), str)) { Fs_SetGame(str); // reload the client game Cl_InitCgame(); } // parse player entity number cl.entity_num = Net_ReadShort(&net_message); // get the full level name str = Net_ReadString(&net_message); Com_Print("\n"); Com_Print("%c%s\n", 2, str); }
/** * @brief Receive data from the specified TCP stream. */ _Bool Net_ReceiveStream(int32_t sock, mem_buf_t *buf) { buf->size = buf->read = 0; const ssize_t received = recv(sock, (void *) buf->data, buf->max_size, 0); if (received == -1) { if (Net_GetError() == EWOULDBLOCK) { return false; // no data, don't crap our pants } Com_Warn("%s\n", Net_GetErrorString()); return false; } buf->size = received; // check the packet length return Net_ReadLong(buf) > -1; }
/* * @brief Called when the current net_message is from remote_address * modifies net_message so that it points to the packet payload */ _Bool Netchan_Process(net_chan_t *chan, mem_buf_t *msg) { uint32_t sequence, sequence_ack; uint32_t reliable_ack, reliable_message; // get sequence numbers Net_BeginReading(msg); sequence = Net_ReadLong(msg); sequence_ack = Net_ReadLong(msg); // read the qport if we are a server if (chan->source == NS_UDP_SERVER) Net_ReadByte(msg); reliable_message = sequence >> 31; reliable_ack = sequence_ack >> 31; sequence &= ~(1 << 31); sequence_ack &= ~(1 << 31); if (net_showpackets->value) { if (reliable_message) Com_Print("Recv %u bytes: s=%i reliable=%i ack=%i rack=%i\n", (uint32_t) msg->size, sequence, chan->incoming_reliable_sequence ^ 1, sequence_ack, reliable_ack); else Com_Print("Recv %u bytes : s=%i ack=%i rack=%i\n", (uint32_t) msg->size, sequence, sequence_ack, reliable_ack); } // discard stale or duplicated packets if (sequence <= chan->incoming_sequence) { if (net_showdrop->value) Com_Print("%s:Out of order packet %i at %i\n", Net_NetaddrToString(&chan->remote_address), sequence, chan->incoming_sequence); return false; } // dropped packets don't keep the message from being used chan->dropped = sequence - (chan->incoming_sequence + 1); if (chan->dropped > 0) { if (net_showdrop->value) Com_Print("%s:Dropped %i packets at %i\n", Net_NetaddrToString(&chan->remote_address), chan->dropped, sequence); } // if the current outgoing reliable message has been acknowledged // clear the buffer to make way for the next if (reliable_ack == chan->reliable_sequence) chan->reliable_size = 0; // it has been received // if this message contains a reliable message, bump incoming_reliable_sequence chan->incoming_sequence = sequence; chan->incoming_acknowledged = sequence_ack; chan->incoming_reliable_acknowledged = reliable_ack; if (reliable_message) { chan->incoming_reliable_sequence ^= 1; } // the message can now be read from the current message pointer chan->last_received = quake2world.time; return true; }