/* * @brief */ static void Cl_ParseSound(void) { vec3_t origin; vec_t *org; uint16_t index; uint16_t ent_num; int32_t atten; int32_t flags; flags = Net_ReadByte(&net_message); if ((index = Net_ReadByte(&net_message)) > MAX_SOUNDS) Com_Error(ERR_DROP, "Bad index (%d)\n", index); if (flags & S_ATTEN) atten = Net_ReadByte(&net_message); else atten = ATTEN_DEFAULT; if (flags & S_ENTNUM) { // entity relative ent_num = Net_ReadShort(&net_message); if (ent_num > MAX_EDICTS) Com_Error(ERR_DROP, "Bad entity number (%d)\n", ent_num); } else { ent_num = 0; } if (flags & S_ORIGIN) { // positioned in space Net_ReadPosition(&net_message, origin); org = origin; } else // use ent_num org = NULL; if (!cl.sound_precache[index]) return; S_PlaySample(org, ent_num, cl.sound_precache[index], atten); }
/* * @brief A download message has been received from the server. */ static void Cl_ParseDownload(void) { int32_t size, percent; // read the data size = Net_ReadShort(&net_message); percent = Net_ReadByte(&net_message); if (size < 0) { Com_Debug("Server does not have this file\n"); if (cls.download.file) { // if here, we tried to resume a file but the server said no Fs_Close(cls.download.file); cls.download.file = NULL; } Cl_RequestNextDownload(); return; } // open the file if not opened yet if (!cls.download.file) { if (!(cls.download.file = Fs_OpenWrite(cls.download.tempname))) { net_message.read += size; Com_Warn("Failed to open %s\n", cls.download.tempname); Cl_RequestNextDownload(); return; } } Fs_Write(cls.download.file, net_message.data + net_message.read, 1, size); net_message.read += size; if (percent != 100) { Net_WriteByte(&cls.net_chan.message, CL_CMD_STRING); Net_WriteString(&cls.net_chan.message, "nextdl"); } else { Fs_Close(cls.download.file); cls.download.file = NULL; // add new archives to the search path if (Fs_Rename(cls.download.tempname, cls.download.name)) { if (strstr(cls.download.name, ".zip")) { Fs_AddToSearchPath(cls.download.name); } } else { Com_Error(ERR_DROP, "Failed to rename %s\n", cls.download.name); } // get another file if needed Cl_RequestNextDownload(); } }
/* * @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 Parses an incoming SVC_PRINT message. */ static void Cl_ParsePrint(void) { const byte level = Net_ReadByte(&net_message); const char *string = Net_ReadString(&net_message); // the server shouldn't have sent us anything below our level anyway if (level >= message_level->integer) { // check to see if we should ignore the message if (*cl_ignore->string) { char patterns[MAX_STRING_CHARS]; g_strlcpy(patterns, cl_ignore->string, sizeof(patterns)); const char *p = patterns; while (true) { const char *pattern = ParseToken(&p); if (pattern == NULL) { break; } if (GlobMatch(pattern, string)) { return; } } } char *sample = NULL; switch (level) { case PRINT_CHAT: case PRINT_TEAM_CHAT: if (level == PRINT_CHAT && *cl_chat_sound->string) { sample = cl_chat_sound->string; } else if (level == PRINT_TEAM_CHAT && *cl_team_chat_sound->string) { sample = cl_team_chat_sound->string; } break; default: break; } if (sample) { S_AddSample(&(const s_play_sample_t) { .sample = S_LoadSample(sample) }); } Con_Append(level, string); }
/* * @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 */ static void Cl_ParseServerData(void) { // wipe the cl_client_t struct Cl_ClearState(); Cl_SetKeyDest(KEY_CONSOLE); // parse protocol version number const uint16_t major = Net_ReadShort(&net_message); const uint16_t minor = Net_ReadShort(&net_message); // ensure protocol major matches if (major != PROTOCOL_MAJOR) { Com_Error(ERROR_DROP, "Server is using protocol major %d\n", major); } // determine if we're viewing a demo cl.demo_server = Net_ReadByte(&net_message); // game directory char *str = Net_ReadString(&net_message); if (g_strcmp0(Cvar_GetString("game"), str)) { Fs_SetGame(str); // reload the client game Cl_InitCgame(); } // ensure protocol minor matches if (minor != cls.cgame->protocol) { Com_Error(ERROR_DROP, "Server is using protocol minor %d\n", minor); } // parse client slot number, which is our entity number + 1 cl.client_num = Net_ReadShort(&net_message); // get the full level name str = Net_ReadString(&net_message); Com_Print("\n"); Com_Print("^2%s^7\n", str); }
/* * @brief */ void Cl_ParseServerMessage(void) { int32_t cmd, old_cmd; char *s; int32_t i; if (cl_show_net_messages->integer == 1) Com_Print("%u ", (uint32_t) net_message.size); else if (cl_show_net_messages->integer >= 2) Com_Print("------------------\n"); cl.byte_counter += net_message.size; cmd = 0; // parse the message while (true) { if (net_message.read > net_message.size) { Com_Error(ERR_DROP, "Bad server message\n"); } old_cmd = cmd; cmd = Net_ReadByte(&net_message); if (cmd == -1) { Cl_ShowNet("END OF MESSAGE"); break; } if (cl_show_net_messages->integer >= 2 && sv_cmd_names[cmd]) Cl_ShowNet(sv_cmd_names[cmd]); switch (cmd) { case SV_CMD_BASELINE: Cl_ParseBaseline(); break; case SV_CMD_CBUF_TEXT: s = Net_ReadString(&net_message); Cbuf_AddText(s); break; case SV_CMD_CONFIG_STRING: Cl_ParseConfigString(); break; case SV_CMD_DISCONNECT: Com_Error(ERR_DROP, "Server disconnected\n"); break; case SV_CMD_DOWNLOAD: Cl_ParseDownload(); break; case SV_CMD_FRAME: Cl_ParseFrame(); break; case SV_CMD_PRINT: i = Net_ReadByte(&net_message); s = Net_ReadString(&net_message); if (i == PRINT_CHAT) { if (Cl_IgnoreChatMessage(s)) // filter /ignore'd chatters break; if (*cl_chat_sound->string) // trigger chat sound S_StartLocalSample(cl_chat_sound->string); } else if (i == PRINT_TEAMCHAT) { if (Cl_IgnoreChatMessage(s)) // filter /ignore'd chatters break; if (*cl_team_chat_sound->string) // trigger chat sound S_StartLocalSample(cl_team_chat_sound->string); } Com_Print("%s", s); break; case SV_CMD_RECONNECT: Com_Print("Server disconnected, reconnecting...\n"); // stop download if (cls.download.file) { if (cls.download.http) // clean up http downloads Cl_HttpDownload_Complete(); else // or just stop legacy ones Fs_Close(cls.download.file); cls.download.name[0] = '\0'; cls.download.file = NULL; } cls.state = CL_CONNECTING; cls.connect_time = 0; // fire immediately break; case SV_CMD_SERVER_DATA: Cl_ParseServerData(); break; case SV_CMD_SOUND: Cl_ParseSound(); break; default: // delegate to the client game module before failing if (!cls.cgame->ParseMessage(cmd)) { Com_Error(ERR_DROP, "Illegible server message:\n" " %d: last command was %s\n", cmd, sv_cmd_names[old_cmd]); } break; } } Cl_AddNetGraph(); Cl_WriteDemoMessage(); }
/* * @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; }