/* * 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)); }
/* * @brief Sends the shutdown message message to all connected clients. The message * is sent immediately, because the server could completely terminate after * returning from this function. */ static void Sv_ShutdownMessage(const char *msg, _Bool reconnect) { sv_client_t * cl; int32_t i; if (!svs.initialized) return; Sb_Clear(&net_message); if (msg) { // send message Msg_WriteByte(&net_message, SV_CMD_PRINT); Msg_WriteByte(&net_message, PRINT_HIGH); Msg_WriteString(&net_message, msg); } if (reconnect) // send reconnect Msg_WriteByte(&net_message, SV_CMD_RECONNECT); else // or just disconnect Msg_WriteByte(&net_message, SV_CMD_DISCONNECT); for (i = 0, cl = svs.clients; i < sv_max_clients->integer; i++, cl++) if (cl->state >= SV_CLIENT_CONNECTED) Netchan_Transmit(&cl->netchan, net_message.size, net_message.data); }
/* * 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; }
//========================================================================== // Msg_WritePackedShort // Only 15 bits can be used for the number because the high bit of the // lower byte is used to determine whether the upper byte follows or not. //========================================================================== void Msg_WritePackedShort(short w) { if(w < 0x80) // Can the number be represented with 7 bits? Msg_WriteByte(w); else { Msg_WriteByte(0x80 | w); Msg_WriteByte(w >> 7); // Highest bit is lost. } }
/* * 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)); } }
/* * Sv_Baselines_f */ static void Sv_Baselines_f(void) { unsigned int start; entity_state_t nullstate; entity_state_t *base; Com_Debug("Baselines() from %s\n", Sv_NetaddrToString(sv_client)); if (sv_client->state != SV_CLIENT_CONNECTED) { Com_Warn("Sv_Baselines_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_Baselines_f: Stale spawn count from %s\n", Sv_NetaddrToString(sv_client)); Sv_New_f(); return; } start = strtoul(Cmd_Argv(2), NULL, 0); memset(&nullstate, 0, sizeof(nullstate)); // write a packet full of data while (sv_client->netchan.message.size < MAX_MSG_SIZE / 2 && start < MAX_EDICTS) { base = &sv.baselines[start]; if (base->model1 || base->sound || base->effects) { Msg_WriteByte(&sv_client->netchan.message, SV_CMD_ENTITY_BASELINE); Msg_WriteDeltaEntity(&nullstate, base, &sv_client->netchan.message, true, true); } start++; } // send next command if (start == MAX_EDICTS) { Msg_WriteByte(&sv_client->netchan.message, SV_CMD_CBUF_TEXT); Msg_WriteString(&sv_client->netchan.message, va("precache %i\n", svs.spawn_count)); } else { Msg_WriteByte(&sv_client->netchan.message, SV_CMD_CBUF_TEXT); Msg_WriteString(&sv_client->netchan.message, va("baselines %i %i\n", svs.spawn_count, start)); } }
/* * Sv_DropClient * * Called when the player is totally leaving the server, either willingly * or unwillingly. This is NOT called if the entire server is quitting * or crashing. */ void Sv_DropClient(sv_client_t *cl) { g_edict_t *ent; if (cl->state > SV_CLIENT_FREE) { // send the disconnect if (cl->state == SV_CLIENT_ACTIVE) { // after informing the game module svs.game->ClientDisconnect(cl->edict); } Msg_WriteByte(&cl->netchan.message, SV_CMD_DISCONNECT); Netchan_Transmit(&cl->netchan, cl->netchan.message.size, cl->netchan.message.data); } if (cl->download) { Fs_FreeFile(cl->download); cl->download = NULL; } ent = cl->edict; memset(cl, 0, sizeof(*cl)); cl->edict = ent; cl->last_frame = -1; }
/* * 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); }