/* * @brief Searches sv.config_strings from the specified start, searching for the * desired name. If not found, the name can be optionally created and sent to * all connected clients. This allows the game to lazily load assets. */ static uint16_t Sv_FindIndex(const char *name, uint16_t start, uint16_t max, _Bool create) { uint16_t i; if (!name || !name[0]) return 0; for (i = 0; i < max && sv.config_strings[start + i][0]; i++) if (!g_strcmp0(sv.config_strings[start + i], name)) return i; if (!create) return 0; if (i == max) { Com_Warn("Max index for %s reached\n", name); return 0; } g_strlcpy(sv.config_strings[start + i], name, sizeof(sv.config_strings[i])); if (sv.state != SV_LOADING) { // send the update to everyone Sb_Clear(&sv.multicast); Msg_WriteChar(&sv.multicast, SV_CMD_CONFIG_STRING); Msg_WriteShort(&sv.multicast, start + i); Msg_WriteString(&sv.multicast, name); Sv_Multicast(vec3_origin, MULTICAST_ALL_R); } return i; }
/* * Sv_New_f * * Sends the first message from the server to a connected client. * This will be sent on the initial connection and upon each server load. */ static void Sv_New_f(void) { int player_num; Com_Debug("New() from %s\n", Sv_NetaddrToString(sv_client)); if (sv_client->state != SV_CLIENT_CONNECTED) { Com_Warn("Sv_New_f: %s already spawned\n", Sv_NetaddrToString(sv_client)); return; } // demo servers will send the demo file's server info packet if (sv.state == SV_ACTIVE_DEMO) { return; } // send the server data Msg_WriteByte(&sv_client->netchan.message, SV_CMD_SERVER_DATA); Msg_WriteLong(&sv_client->netchan.message, PROTOCOL); Msg_WriteLong(&sv_client->netchan.message, svs.spawn_count); Msg_WriteLong(&sv_client->netchan.message, svs.frame_rate); Msg_WriteByte(&sv_client->netchan.message, 0); Msg_WriteString(&sv_client->netchan.message, Cvar_GetString("game")); player_num = sv_client - svs.clients; Msg_WriteShort(&sv_client->netchan.message, player_num); // send full level name Msg_WriteString(&sv_client->netchan.message, sv.config_strings[CS_NAME]); // begin fetching config_strings Msg_WriteByte(&sv_client->netchan.message, SV_CMD_CBUF_TEXT); Msg_WriteString(&sv_client->netchan.message, va("config_strings %i 0\n", svs.spawn_count)); }
/* * Sv_NextDownload_f */ static void Sv_NextDownload_f(void) { int r, size, percent; byte buf[MAX_MSG_SIZE]; size_buf_t msg; if (!sv_client->download) return; r = sv_client->download_size - sv_client->download_count; if (r > 1024) // cap the download chunk at 1024 bytes r = 1024; Sb_Init(&msg, buf, MAX_MSG_SIZE); Msg_WriteByte(&msg, SV_CMD_DOWNLOAD); Msg_WriteShort(&msg, r); sv_client->download_count += r; size = sv_client->download_size; if (!size) size = 1; percent = sv_client->download_count * 100 / size; Msg_WriteByte(&msg, percent); Sb_Write(&msg, sv_client->download + sv_client->download_count - r, r); Sb_Write(&sv_client->netchan.message, msg.data, msg.size); if (sv_client->download_count != sv_client->download_size) return; Fs_FreeFile(sv_client->download); sv_client->download = NULL; }
/* * Sv_ConfigStrings_f */ static void Sv_ConfigStrings_f(void) { unsigned int start; Com_Debug("ConfigStrings() from %s\n", Sv_NetaddrToString(sv_client)); if (sv_client->state != SV_CLIENT_CONNECTED) { Com_Warn("Sv_ConfigStrings_f: %s already spawned\n", Sv_NetaddrToString(sv_client)); return; } // handle the case of a level changing while a client was connecting if (strtoul(Cmd_Argv(1), NULL, 0) != svs.spawn_count) { Com_Debug("Sv_ConfigStrings_f: Stale spawn count from %s\n", Sv_NetaddrToString(sv_client)); Sv_New_f(); return; } start = strtoul(Cmd_Argv(2), NULL, 0); if (start >= MAX_CONFIG_STRINGS) { // catch bad offsets Com_Warn("Sv_ConfigStrings_f: Bad config_string offset from %s\n", Sv_NetaddrToString(sv_client)); Sv_KickClient(sv_client, NULL); return; } // write a packet full of data while (sv_client->netchan.message.size < MAX_MSG_SIZE / 2 && start < MAX_CONFIG_STRINGS) { if (sv.config_strings[start][0]) { Msg_WriteByte(&sv_client->netchan.message, SV_CMD_CONFIG_STRING); Msg_WriteShort(&sv_client->netchan.message, start); Msg_WriteString(&sv_client->netchan.message, sv.config_strings[start]); } start++; } // send next command if (start == MAX_CONFIG_STRINGS) { Msg_WriteByte(&sv_client->netchan.message, SV_CMD_CBUF_TEXT); Msg_WriteString(&sv_client->netchan.message, va("baselines %i 0\n", svs.spawn_count)); } else { Msg_WriteByte(&sv_client->netchan.message, SV_CMD_CBUF_TEXT); Msg_WriteString(&sv_client->netchan.message, va("config_strings %i %i\n", svs.spawn_count, start)); } }
/* * Cl_WriteDemoHeader * * Writes server_data, config_strings, and baselines once a non-delta * compressed frame arrives from the server. */ static void Cl_WriteDemoHeader(void) { byte buffer[MAX_MSG_SIZE]; size_buf_t msg; int i; int len; entity_state_t null_state; // write out messages to hold the startup information Sb_Init(&msg, buffer, sizeof(buffer)); // write the server data Msg_WriteByte(&msg, SV_CMD_SERVER_DATA); Msg_WriteLong(&msg, PROTOCOL); Msg_WriteLong(&msg, cl.server_count); Msg_WriteLong(&msg, cl.server_frame_rate); Msg_WriteByte(&msg, 1); // demo_server byte Msg_WriteString(&msg, Cvar_GetString("game")); Msg_WriteShort(&msg, cl.player_num); Msg_WriteString(&msg, cl.config_strings[CS_NAME]); // and config_strings for (i = 0; i < MAX_CONFIG_STRINGS; i++) { if (*cl.config_strings[i] != '\0') { if (msg.size + strlen(cl.config_strings[i]) + 32 > msg.max_size) { // write it out len = LittleLong(msg.size); Fs_Write(&len, 4, 1, cls.demo_file); Fs_Write(msg.data, msg.size, 1, cls.demo_file); msg.size = 0; } Msg_WriteByte(&msg, SV_CMD_CONFIG_STRING); Msg_WriteShort(&msg, i); Msg_WriteString(&msg, cl.config_strings[i]); } } // and baselines for (i = 0; i < MAX_EDICTS; i++) { entity_state_t *ent = &cl.entities[i].baseline; if (!ent->number) continue; if (msg.size + 64 > msg.max_size) { // write it out len = LittleLong(msg.size); Fs_Write(&len, 4, 1, cls.demo_file); Fs_Write(msg.data, msg.size, 1, cls.demo_file); msg.size = 0; } memset(&null_state, 0, sizeof(null_state)); Msg_WriteByte(&msg, SV_CMD_ENTITY_BASELINE); Msg_WriteDeltaEntity(&null_state, &cl.entities[i].baseline, &msg, true, true); } Msg_WriteByte(&msg, SV_CMD_CBUF_TEXT); Msg_WriteString(&msg, "precache 0\n"); // write it to the demo file len = LittleLong(msg.size); Fs_Write(&len, 4, 1, cls.demo_file); Fs_Write(msg.data, msg.size, 1, cls.demo_file); Com_Print("Recording to %s.\n", cls.demo_path); // the rest of the demo file will be individual frames }
/* * Sv_Download_f */ static void Sv_Download_f(void) { const char *name; void *buf; unsigned int i = 0, offset = 0; name = Cmd_Argv(1); if (Cmd_Argc() > 2) offset = strtoul(Cmd_Argv(2), NULL, 0); // downloaded offset // catch illegal offset or file_names if (*name == '.' || *name == '/' || *name == '\\' || strstr( name, "..")) { Com_Warn("Sv_Download_f: Malicious download (%s:%d) from %s\n", name, offset, Sv_NetaddrToString(sv_client)); Sv_KickClient(sv_client, NULL); return; } while (downloadable[i]) { // ensure download name is allowed if (GlobMatch(downloadable[i], name)) break; i++; } if (!downloadable[i]) { // it wasn't Com_Warn("Sv_Download_f: Illegal download (%s) from %s\n", name, Sv_NetaddrToString(sv_client)); Sv_KickClient(sv_client, NULL); return; } if (!sv_udp_download->value) { // lastly, ensure server wishes to allow Msg_WriteByte(&sv_client->netchan.message, SV_CMD_DOWNLOAD); Msg_WriteShort(&sv_client->netchan.message, -1); Msg_WriteByte(&sv_client->netchan.message, 0); return; } if (sv_client->download) // free last download Fs_FreeFile(sv_client->download); sv_client->download_size = Fs_LoadFile(name, &buf); sv_client->download = (byte *) buf; sv_client->download_count = offset; if (offset > sv_client->download_size) sv_client->download_count = sv_client->download_size; if (!sv_client->download) { // legal file name, but missing file Com_Warn("Sv_Download_f: Couldn't download %s to %s\n", name, Sv_NetaddrToString(sv_client)); Msg_WriteByte(&sv_client->netchan.message, SV_CMD_DOWNLOAD); Msg_WriteShort(&sv_client->netchan.message, -1); Msg_WriteByte(&sv_client->netchan.message, 0); return; } Sv_NextDownload_f(); Com_Debug("Downloading %s to %s\n", name, sv_client->name); }