/* * @brief Handles the actual loading of .ogg music files. */ static _Bool S_LoadMusicFile(const char *name, void **buffer, SDL_RWops **rw, Mix_Music **music) { char path[MAX_QPATH]; *music = NULL; StripExtension(name, path); g_snprintf(path, sizeof(path), "music/%s.ogg", name); int32_t len; if ((len = Fs_Load(path, buffer)) != -1) { if ((*rw = SDL_RWFromMem(*buffer, len))) { if ((*music = Mix_LoadMUS_RW(*rw))) { Com_Debug("Loaded %s\n", name); } else { Com_Warn("Failed to load %s: %s\n", name, Mix_GetError()); SDL_FreeRW(*rw); } } else { Com_Warn("Failed to create SDL_RWops for %s\n", name); Fs_Free(*buffer); } } else { Com_Debug("Failed to load %s\n", name); } return *music != NULL; }
/* * @brief */ void R_ProgramVariable(r_variable_t *variable, GLenum type, const char *name) { memset(variable, 0, sizeof(*variable)); variable->location = -1; if (!r_state.active_program) { Com_Warn("No program currently bound\n"); return; } switch (type) { case R_ATTRIBUTE: variable->location = qglGetAttribLocation(r_state.active_program->id, name); break; default: variable->location = qglGetUniformLocation(r_state.active_program->id, name); break; } if (variable->location == -1) { Com_Warn("Failed to resolve variable %s in program %s\n", name, r_state.active_program->name); return; } variable->type = type; g_strlcpy(variable->name, name, sizeof(variable->name)); memset(&variable->value, 0xff, sizeof(variable->value)); }
/* * @brief */ s_sample_t *S_LoadModelSample(entity_state_t *ent, const char *name) { char model[MAX_QPATH]; char path[MAX_QPATH]; char alias[MAX_QPATH]; s_sample_t *sample; if (!s_env.initialized) return NULL; // determine what model the client is using memset(model, 0, sizeof(model)); if (ent->number - 1 >= MAX_CLIENTS) { Com_Warn("Invalid client entity: %d\n", ent->number - 1); return NULL; } uint16_t n = CS_CLIENTS + ent->number - 1; if (cl.config_strings[n][0]) { char *p = strchr(cl.config_strings[n], '\\'); if (p) { p += 1; strcpy(model, p); p = strchr(model, '/'); if (p) *p = 0; } } // if we can't figure it out, use common if (*model == '\0') strcpy(model, "common"); // see if we already know of the model-specific sound g_snprintf(alias, sizeof(path), "#players/%s/%s", model, name + 1); sample = (s_sample_t *) S_FindMedia(alias); if (sample) return sample; // we don't, try it sample = S_LoadSample(alias); if (sample->chunk) return sample; // that didn't work, so load the common one and alias it // the media subsystem will free the previous sample for us g_snprintf(path, sizeof(path), "#players/common/%s", name + 1); sample = S_LoadSample(path); if (sample->chunk) return S_AliasSample(sample, alias); Com_Warn("Failed to load %s\n", alias); return NULL; }
/* * @brief */ static void S_LoadSampleChunk(s_sample_t *sample) { char path[MAX_QPATH]; void *buf; int32_t i, len; SDL_RWops *rw; if (sample->media.name[0] == '*') // place holder return; if (sample->media.name[0] == '#') { // global path g_strlcpy(path, (sample->media.name + 1), sizeof(path)); } else { // or relative g_snprintf(path, sizeof(path), "sounds/%s", sample->media.name); } buf = NULL; rw = NULL; i = 0; while (SAMPLE_TYPES[i]) { StripExtension(path, path); g_strlcat(path, SAMPLE_TYPES[i++], sizeof(path)); if ((len = Fs_Load(path, &buf)) == -1) continue; if (!(rw = SDL_RWFromMem(buf, len))) { Fs_Free(buf); continue; } if (!(sample->chunk = Mix_LoadWAV_RW(rw, false))) Com_Warn("%s\n", Mix_GetError()); Fs_Free(buf); SDL_FreeRW(rw); if (sample->chunk) { // success break; } } if (sample->chunk) { Mix_VolumeChunk(sample->chunk, s_volume->value * MIX_MAX_VOLUME); Com_Debug("Loaded %s\n", path); } else { if (g_str_has_prefix(sample->media.name, "#players")) { Com_Debug("Failed to load player sample %s\n", sample->media.name); } else { Com_Warn("Failed to load %s\n", sample->media.name); } } }
/* * @brief */ static r_program_t *R_LoadProgram(const char *name, void(*Init)(void)) { r_program_t *prog; char log[MAX_STRING_CHARS]; uint32_t e; int32_t i; for (i = 0; i < MAX_PROGRAMS; i++) { prog = &r_state.programs[i]; if (!prog->id) break; } if (i == MAX_PROGRAMS) { Com_Warn("MAX_PROGRAMS reached\n"); return NULL; } g_strlcpy(prog->name, name, sizeof(prog->name)); prog->id = qglCreateProgram(); prog->v = R_LoadShader(GL_VERTEX_SHADER, va("%s_vs.glsl", name)); prog->f = R_LoadShader(GL_FRAGMENT_SHADER, va("%s_fs.glsl", name)); if (prog->v) qglAttachShader(prog->id, prog->v->id); if (prog->f) qglAttachShader(prog->id, prog->f->id); qglLinkProgram(prog->id); qglGetProgramiv(prog->id, GL_LINK_STATUS, &e); if (!e) { qglGetProgramInfoLog(prog->id, sizeof(log) - 1, NULL, log); Com_Warn("%s: %s\n", prog->name, log); R_ShutdownProgram(prog); return NULL; } prog->Init = Init; if (prog->Init) { // invoke initialization function R_UseProgram(prog); prog->Init(); R_UseProgram(NULL); } R_GetError(prog->name); return prog; }
/* * Pak_ReadPakfile * * Return a populated Pakfile from the specified path, with entries * hashed by name for fast finds. */ pak_t *Pak_ReadPakfile(const char *pakfile) { pak_header_t header; int i; pak_t *pak; pak = (pak_t *) Z_Malloc(sizeof(*pak)); pak->handle = fopen(pakfile, "rb"); if (!pak->handle) { Com_Warn("Pak_ReadPakfile: Couldn't open %s.\n", pakfile); Z_Free(pak); return NULL; } strcpy(pak->file_name, pakfile); Fs_Read(&header, 1, sizeof(pak_header_t), pak->handle); if (LittleLong(header.ident) != PAK_HEADER) { Com_Warn("Pak_ReadPakfile: %s is not a pak file.\n", pakfile); Fs_CloseFile(pak->handle); Z_Free(pak); return NULL; } header.dir_ofs = LittleLong(header.dir_ofs); header.dir_len = LittleLong(header.dir_len); pak->num_entries = header.dir_len / sizeof(pak_entry_t); if (pak->num_entries > MAX_PAK_ENTRIES) { Com_Warn("Pak_ReadPakfile: %s has %i files.\n", pakfile, pak->num_entries); Fs_CloseFile(pak->handle); Z_Free(pak); return NULL; } pak->entries = (pak_entry_t *) Z_Malloc( pak->num_entries * sizeof(pak_entry_t)); fseek(pak->handle, header.dir_ofs, SEEK_SET); Fs_Read(pak->entries, 1, header.dir_len, pak->handle); Hash_Init(&pak->hash_table); // parse the directory for (i = 0; i < pak->num_entries; ++i) { pak->entries[i].file_ofs = LittleLong(pak->entries[i].file_ofs); pak->entries[i].file_len = LittleLong(pak->entries[i].file_len); Hash_Put(&pak->hash_table, pak->entries[i].name, &pak->entries[i]); } return pak; }
/* * 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)); } }
/* * @brief */ void R_InitGlExtensions(void) { // multitexture if (strstr(r_config.extensions_string, "GL_ARB_multitexture")) { qglActiveTexture = SDL_GL_GetProcAddress("glActiveTexture"); qglClientActiveTexture = SDL_GL_GetProcAddress("glClientActiveTexture"); } else Com_Error(ERR_FATAL, "GL_ARB_multitexture not found\n"); // vertex buffer objects if (strstr(r_config.extensions_string, "GL_ARB_vertex_buffer_object")) { qglGenBuffers = SDL_GL_GetProcAddress("glGenBuffers"); qglDeleteBuffers = SDL_GL_GetProcAddress("glDeleteBuffers"); qglBindBuffer = SDL_GL_GetProcAddress("glBindBuffer"); qglBufferData = SDL_GL_GetProcAddress("glBufferData"); } else Com_Warn("GL_ARB_vertex_buffer_object not found\n"); // glsl vertex and fragment shaders and programs if (strstr(r_config.extensions_string, "GL_ARB_fragment_shader")) { qglCreateShader = SDL_GL_GetProcAddress("glCreateShader"); qglDeleteShader = SDL_GL_GetProcAddress("glDeleteShader"); qglShaderSource = SDL_GL_GetProcAddress("glShaderSource"); qglCompileShader = SDL_GL_GetProcAddress("glCompileShader"); qglGetShaderiv = SDL_GL_GetProcAddress("glGetShaderiv"); qglGetShaderInfoLog = SDL_GL_GetProcAddress("glGetShaderInfoLog"); qglCreateProgram = SDL_GL_GetProcAddress("glCreateProgram"); qglDeleteProgram = SDL_GL_GetProcAddress("glDeleteProgram"); qglAttachShader = SDL_GL_GetProcAddress("glAttachShader"); qglDetachShader = SDL_GL_GetProcAddress("glDetachShader"); qglLinkProgram = SDL_GL_GetProcAddress("glLinkProgram"); qglUseProgram = SDL_GL_GetProcAddress("glUseProgram"); qglGetProgramiv = SDL_GL_GetProcAddress("glGetProgramiv"); qglGetProgramInfoLog = SDL_GL_GetProcAddress("glGetProgramInfoLog"); qglGetUniformLocation = SDL_GL_GetProcAddress("glGetUniformLocation"); qglUniform1i = SDL_GL_GetProcAddress("glUniform1i"); qglUniform1f = SDL_GL_GetProcAddress("glUniform1f"); qglUniform3fv = SDL_GL_GetProcAddress("glUniform3fv"); qglUniform4fv = SDL_GL_GetProcAddress("glUniform4fv"); qglGetAttribLocation = SDL_GL_GetProcAddress("glGetAttribLocation"); // vertex attribute arrays qglEnableVertexAttribArray = SDL_GL_GetProcAddress("glEnableVertexAttribArray"); qglDisableVertexAttribArray = SDL_GL_GetProcAddress("glDisableVertexAttribArray"); qglVertexAttribPointer = SDL_GL_GetProcAddress("glVertexAttribPointer"); } else Com_Warn("GL_ARB_fragment_shader not found\n"); }
/** * @brief Send data to the specified TCP stream. */ _Bool Net_SendStream(int32_t sock, const void *data, size_t len) { mem_buf_t buf; byte buffer[MAX_MSG_SIZE]; Mem_InitBuffer(&buf, buffer, sizeof(buffer)); // write the packet length Net_WriteLong(&buf, (int32_t) len); // and copy the payload Net_WriteData(&buf, data, len); ssize_t sent = 0; while ((size_t) sent < buf.size) { const ssize_t s = send(sock, (void *)(buf.data + sent), buf.size - sent, 0); if (s == -1) { if (Net_GetError() != EWOULDBLOCK) { Com_Warn("%s\n", Net_GetErrorString()); return false; } } sent += s; } return sent == (ssize_t) buf.size; }
/* * 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)); }
/* * Cl_Record_f * * record <demo name> * * Begin recording a demo from the current frame until `stop` is issued. */ void Cl_Record_f(void) { if (Cmd_Argc() != 2) { Com_Print("Usage: %s <demo name>\n", Cmd_Argv(0)); return; } if (cls.demo_file) { Com_Print("Already recording.\n"); return; } if (cls.state != CL_ACTIVE) { Com_Print("You must be in a level to record.\n"); return; } // open the demo file snprintf(cls.demo_path, sizeof(cls.demo_path), "%s/demos/%s.dem", Fs_Gamedir(), Cmd_Argv(1)); Fs_CreatePath(cls.demo_path); cls.demo_file = fopen(cls.demo_path, "wb"); if (!cls.demo_file) { Com_Warn("Cl_Record_f: couldn't open %s.\n", cls.demo_path); return; } // don't start saving messages until a non-delta compressed message is received cls.demo_waiting = true; // update user info var to inform server to send angles Cvar_ForceSet("recording", "1"); Com_Print("Requesting demo support from server...\n"); }
/* * @return The current executable path (argv[0]). */ const char *Sys_ExecutablePath(void) { static char path[MAX_OS_PATH]; #if defined(__APPLE__) uint32_t i = sizeof(path); if (_NSGetExecutablePath(path, &i) > -1) { return path; } #elif defined(__linux__) if (readlink(va("/proc/%d/exe", getpid()), path, sizeof(path)) > -1) { return path; } #elif defined(_WIN32) if (GetModuleFileName(0, path, sizeof(path))) { return path; } #endif Com_Warn("Failed to resolve executable path\n"); return NULL; }
/* * Sv_UserStringCommand * * Invoke the specified user string command. If we don't have a function for * it, pass it off to the game module. */ static void Sv_UserStringCommand(const char *s) { sv_user_string_cmd_t *c; Cmd_TokenizeString(s); if (strchr(s, '\xFF')) { // catch end of message exploit Com_Warn("Sv_ExecuteUserCommand: Illegal command from %s\n", Sv_NetaddrToString(sv_client)); Sv_KickClient(sv_client, NULL); return; } for (c = sv_user_string_cmds; c->name; c++) { if (!strcmp(Cmd_Argv(0), c->name)) { c->func(); break; } } if (!c->name) { // unmatched command if (sv.state == SV_ACTIVE_GAME) // maybe the game knows what to do with it svs.game->ClientCommand(sv_player); } }
/** * @brief */ static void Ms_ParseMessage(struct sockaddr_in *from, char *data) { char *cmd = data; char *line = data; while (*line && *line != '\n') { line++; } *(line++) = '\0'; cmd += 4; if (!g_ascii_strncasecmp(cmd, "ping", 4)) { Ms_AddServer(from); } else if (!g_ascii_strncasecmp(cmd, "heartbeat", 9) || !g_ascii_strncasecmp(cmd, "print", 5)) { Ms_Heartbeat(from); } else if (!g_ascii_strncasecmp(cmd, "ack", 3)) { Ms_Ack(from); } else if (!g_ascii_strncasecmp(cmd, "shutdown", 8)) { Ms_RemoveServer(from); } else if (!g_ascii_strncasecmp(cmd, "getservers", 10) || !g_ascii_strncasecmp(cmd, "y", 1)) { Ms_GetServers(from); } else { Com_Warn("Unknown command from %s: '%s'", atos(from), cmd); } }
/** * @brief Binds a linked model to its parent, and copies it into the view structure. */ const r_entity_t *R_AddLinkedEntity(const r_entity_t *parent, const r_model_t *model, const char *tag_name) { if (!parent) { Com_Warn("NULL parent\n"); return NULL; } r_entity_t ent = *parent; ent.parent = parent; ent.tag_name = tag_name; ent.model = model; memset(ent.skins, 0, sizeof(ent.skins)); ent.num_skins = 0; ent.frame = ent.old_frame = 0; ent.lerp = 1.0; ent.back_lerp = 0.0; return R_AddEntity(&ent); }
/* * @brief */ void R_DrawLine(r_pixel_t x1, r_pixel_t y1, r_pixel_t x2, r_pixel_t y2, int32_t c, vec_t a) { byte color[4]; if (a > 1.0) { Com_Warn("Bad alpha %f\n", a); return; } if (a < 0.0) { // RGBA integer memcpy(color, &c, 4); } else { // palette index memcpy(color, &img_palette[c], sizeof(color)); color[3] = a * 255; } // duplicate color data to all 4 verts memcpy(&r_draw.line_arrays.colors[r_draw.line_arrays.color_index + 0], color, 4); memcpy(&r_draw.line_arrays.colors[r_draw.line_arrays.color_index + 4], color, 4); r_draw.line_arrays.color_index += 8; // populate verts r_draw.line_arrays.verts[r_draw.line_arrays.vert_index + 0] = x1; r_draw.line_arrays.verts[r_draw.line_arrays.vert_index + 1] = y1; r_draw.line_arrays.verts[r_draw.line_arrays.vert_index + 2] = x2; r_draw.line_arrays.verts[r_draw.line_arrays.vert_index + 3] = y2; r_draw.line_arrays.vert_index += 4; }
static void R_LoadHeightmap(const char *name, const SDL_Surface *surf) { char heightmap[MAX_QPATH]; g_strlcpy(heightmap, name, sizeof(heightmap)); char *c = strrchr(heightmap, '_'); if (c) { *c = '\0'; } SDL_Surface *hsurf; if (Img_LoadImage(va("%s_h", heightmap), &hsurf)) { if (hsurf->w == surf->w && hsurf->h == surf->h) { Com_Debug("Merging heightmap %s\n", heightmap); byte *in = hsurf->pixels; byte *out = surf->pixels; const size_t len = surf->w * surf->h; for (size_t i = 0; i < len; i++, in += 4, out += 4) { out[3] = (in[0] + in[1] + in[2]) / 3.0; } } else { Com_Warn("Incorrect heightmap resolution for %s\n", name); } SDL_FreeSurface(hsurf); } }
/* * @brief Loads all r_bsp_inline_model_t for the specified BSP model. These are * later registered as first-class r_model_t's in R_SetupBspInlineModels. */ static void R_LoadBspInlineModels(r_bsp_model_t *bsp, const d_bsp_lump_t *l) { r_bsp_inline_model_t *out; uint16_t i, j; const d_bsp_model_t *in = (const void *) (mod_base + l->file_ofs); if (l->file_len % sizeof(*in)) { Com_Error(ERR_DROP, "Funny lump size\n"); } bsp->num_inline_models = l->file_len / sizeof(*in); bsp->inline_models = out = Z_LinkMalloc(bsp->num_inline_models * sizeof(*out), bsp); for (i = 0; i < bsp->num_inline_models; i++, in++, out++) { for (j = 0; j < 3; j++) { // spread the bounds slightly out->mins[j] = LittleFloat(in->mins[j]) - 1.0; out->maxs[j] = LittleFloat(in->maxs[j]) + 1.0; out->origin[j] = LittleFloat(in->origin[j]); } out->radius = R_RadiusFromBounds(out->mins, out->maxs); out->head_node = LittleLong(in->head_node); // some (old) maps have invalid inline model head_nodes if (out->head_node < 0 || out->head_node >= bsp->num_nodes) { Com_Warn("Bad head_node for %d: %d\n", i, out->head_node); out->head_node = -1; } out->first_surface = (uint16_t) LittleLong(in->first_face); out->num_surfaces = (uint16_t) LittleLong(in->num_faces); } }
/* * @brief Binds the specified font, returning the character width and height. */ void R_BindFont(const char *name, r_pixel_t *cw, r_pixel_t *ch) { r_draw.font = &r_draw.fonts[1]; // medium is the default font if (name) { // try to find it uint16_t i; for (i = 0; i < r_draw.num_fonts; i++) { if (!g_strcmp0(name, r_draw.fonts[i].name)) { r_draw.font = &r_draw.fonts[i]; break; } } if (i == r_draw.num_fonts) { Com_Warn("Unknown font: %s\n", name); } } if (cw) *cw = r_draw.font->char_width; if (ch) *ch = r_draw.font->char_height; }
/* * Cl_PredictMovement * * Run the latest movement command through the player movement code locally, * using the resulting origin and angles to reduce perceived latency. */ void Cl_PredictMovement(void) { int i, ack, current; pm_move_t pm; float step; if (cls.state != CL_ACTIVE) return; if (!cl_predict->value || (cl.frame.ps.pmove.pm_flags & PMF_NO_PREDICTION)) { for (i = 0; i < 3; i++) { // just set angles cl.predicted_angles[i] = cl.angles[i] + SHORT2ANGLE(cl.frame.ps.pmove.delta_angles[i]); } return; } ack = cls.netchan.incoming_acknowledged; current = cls.netchan.outgoing_sequence; // if we are too far out of date, just freeze if (current - ack >= CMD_BACKUP) { Com_Warn("Cl_PredictMovement: Exceeded CMD_BACKUP.\n"); return; } // copy current state to pmove memset(&pm, 0, sizeof(pm)); pm.Trace = Cl_Trace; pm.PointContents = Cl_Pointcontents; pm.s = cl.frame.ps.pmove; pm.s.gravity = cl_gravity; // run frames while (++ack <= current) { const int frame = ack & CMD_MASK; const user_cmd_t *cmd = &cl.cmds[frame]; if (!cmd->msec) continue; pm.cmd = *cmd; Pmove(&pm); // save for debug checking VectorCopy(pm.s.origin, cl.predicted_origins[frame]); } step = pm.s.origin[2] * 0.125 - cl.predicted_origin[2]; if ((pm.s.pm_flags & PMF_ON_STAIRS) && step > 4.0) { // save for stair lerping cl.predicted_step_time = cls.real_time; cl.predicted_step = step; } // copy results out for rendering VectorScale(pm.s.origin, 0.125, cl.predicted_origin); VectorCopy(pm.angles, cl.predicted_angles); cl.underwater = pm.water_level > 2; }
/** * @brief record <demo name> * * Begin recording a demo from the current frame until `stop` is issued. */ void Cl_Record_f(void) { if (Cmd_Argc() != 2) { Com_Print("Usage: %s <demo name>\n", Cmd_Argv(0)); return; } if (cls.demo_file) { Com_Print("Already recording\n"); return; } if (cls.state != CL_ACTIVE) { Com_Print("You must be in a level to record\n"); return; } g_snprintf(cls.demo_filename, sizeof(cls.demo_filename), "demos/%s.demo", Cmd_Argv(1)); // open the demo file if (!(cls.demo_file = Fs_OpenWrite(cls.demo_filename))) { Com_Warn("Couldn't open %s\n", cls.demo_filename); return; } Com_Print("Recording to %s\n", cls.demo_filename); }
/** * @brief Send the servers list to the specified client address. */ static void Ms_GetServers(struct sockaddr_in *from) { mem_buf_t buf; byte buffer[0xffff]; Mem_InitBuffer(&buf, buffer, sizeof(buffer)); const char *servers = "\xFF\xFF\xFF\xFF" "servers "; Mem_WriteBuffer(&buf, servers, strlen(servers)); uint32_t i = 0; GList *s = ms_servers; while (s) { const ms_server_t *server = (ms_server_t *) s->data; if (server->validated) { Mem_WriteBuffer(&buf, &server->addr.sin_addr, sizeof(server->addr.sin_addr)); Mem_WriteBuffer(&buf, &server->addr.sin_port, sizeof(server->addr.sin_port)); i++; } s = s->next; } if ((sendto(ms_sock, buf.data, buf.size, 0, (struct sockaddr *) from, sizeof(*from))) == -1) { Com_Warn("%s: %s\n", atos(from), strerror(errno)); } else { Com_Verbose("Sent %d servers to %s\n", i, atos(from)); } }
/* * @brief */ void Net_SendPacket(net_src_t source, size_t size, void *data, net_addr_t to) { struct sockaddr_in to_addr; int32_t sock, ret; if (to.type == NA_LOCAL) { Net_SendLocalPacket(source, size, data); return; } if (to.type == NA_IP_BROADCAST) { sock = ip_sockets[source]; if (!sock) return; } else if (to.type == NA_IP) { sock = ip_sockets[source]; if (!sock) return; } else { Com_Error(ERR_DROP, "Bad address type\n"); return; } Net_NetAddrToSockaddr(&to, &to_addr); ret = sendto(sock, data, size, 0, (struct sockaddr *) &to_addr, sizeof(to_addr)); if (ret == -1) Com_Warn("%s to %s.\n", Net_ErrorString(), Net_NetaddrToString(to)); }
/* * @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_Begin_f */ static void Sv_Begin_f(void) { Com_Debug("Begin() from %s\n", Sv_NetaddrToString(sv_client)); if (sv_client->state != SV_CLIENT_CONNECTED) { // catch duplicate spawns Com_Warn("Sv_Begin_f: Invalid Begin() from %s\n", Sv_NetaddrToString(sv_client)); Sv_KickClient(sv_client, NULL); return; } if (sv.state == SV_ACTIVE_DEMO) 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_Begin_f: Stale spawn count from %s\n", Sv_NetaddrToString(sv_client)); Sv_New_f(); return; } sv_client->state = SV_CLIENT_ACTIVE; // call the game begin function svs.game->ClientBegin(sv_player); Cbuf_InsertFromDefer(); }
/* * @brief */ static void Ms_SendServersList(struct sockaddr_in *from) { int32_t buflen; char buff[0xffff]; buflen = 0; memset(buff, 0, sizeof(buff)); memcpy(buff, "\xFF\xFF\xFF\xFF""servers ", 12); buflen += 12; GList *s = ms_servers; while (s) { const ms_server_t *server = (ms_server_t *) s->data; if (server->validated) { memcpy(buff + buflen, &server->ip.sin_addr, 4); buflen += 4; memcpy(buff + buflen, &server->port, 2); buflen += 2; } s = s->next; } if ((sendto(ms_sock, buff, buflen, 0, (struct sockaddr *) from, sizeof(*from))) == -1) Com_Warn("Socket error on send: %s.\n", strerror(errno)); else Com_Print("Sent server list to %s\n", inet_ntoa(from->sin_addr)); }
/* * @brief */ static void Cm_DecompressVis(const byte *in, byte *out) { const int32_t row = (cm_vis->num_clusters + 7) >> 3; byte *out_p = out; if (!in || !cm_bsp.num_visibility) { // no vis info, so make all visible for (int32_t i = 0; i < row; i++) { *out_p++ = 0xff; } } else { do { if (*in) { *out_p++ = *in++; continue; } int32_t c = in[1]; in += 2; if ((out_p - out) + c > row) { c = row - (out_p - out); Com_Warn("Overrun\n"); } while (c) { *out_p++ = 0; c--; } } while (out_p - out < row); } }
/** * @brief Upload data to a sub-position in already-created buffer. You could also use this function to * resize an existing buffer without uploading data, although it won't make the * buffer smaller. * @param data_offset Whether the data pointer should be offset by start or not. */ void R_UploadToSubBuffer(r_buffer_t *buffer, const size_t start, const size_t size, const void *data, const _Bool data_offset) { assert(buffer->bufnum != 0); // Check size. This is benign really, but it's usually a bug. if (!size) { Com_Warn("Attempted to upload 0 bytes to GPU\n"); return; } // Don't allow null ptrs since bufferSubData does not allow it. if (!data) { Com_Error(ERROR_DROP, "Fatal: attempted to upload null to GPU. bufferSubData does not allow this.\n"); } // offset ptr if requested if (start && data && data_offset) { data += start; } R_BindBuffer(buffer); // if the buffer isn't big enough to hold what we had already, // we have to resize the buffer const size_t total_size = start + size; if (total_size > buffer->size) { // if we passed a "start", the data is offset, so // just reset to null. This is an odd edge case and // it's fairly rare you'll be editing at the end first, // but who knows. if (start) { glBufferData(buffer->target, total_size, NULL, buffer->hint); R_GetError("Partial resize"); r_view.buffer_stats[buffer->type].num_full_uploads++; glBufferSubData(buffer->target, start, size, data); R_GetError("Partial update"); r_view.buffer_stats[buffer->type].num_partial_uploads++; } else { glBufferData(buffer->target, total_size, data, buffer->hint); R_GetError("Full resize"); r_view.buffer_stats[buffer->type].num_full_uploads++; } r_state.buffers_total_bytes -= buffer->size; r_state.buffers_total_bytes += total_size; buffer->size = total_size; } else { // just update the range we specified glBufferSubData(buffer->target, start, size, data); R_GetError("Updating existing buffer"); r_view.buffer_stats[buffer->type].num_partial_uploads++; } r_view.buffer_stats[buffer->type].size_uploaded += size; }
/* * @brief */ void Cl_ParseServers(void) { cl_server_info_t *server; byte *buffptr = net_message.data + 12; byte *buffend = buffptr + net_message.size - 12; // parse the list while (buffptr + 1 < buffend) { net_addr_t addr; byte ip[4]; ip[0] = *buffptr++; // parse the address ip[1] = *buffptr++; ip[2] = *buffptr++; ip[3] = *buffptr++; uint16_t port = (*buffptr++) << 8; // and the port port += *buffptr++; char s[32]; g_snprintf(s, sizeof(s), "%d.%d.%d.%d:%d", ip[0], ip[1], ip[2], ip[3], port); Com_Debug("Parsed %s\n", s); if (!Net_StringToNetaddr(s, &addr)) { // make sure it's valid Com_Warn("Invalid address: %s\n", s); break; } if (!addr.port) // 0's mean we're done break; server = Cl_ServerForNetaddr(&addr); if (!server) server = Cl_AddServer(&addr); server->source = SERVER_SOURCE_INTERNET; } net_message.read = net_message.size; // then ping them GList *e = cls.servers; while (e) { server = (cl_server_info_t *) e->data; if (server->source == SERVER_SOURCE_INTERNET) { server->ping_time = cls.real_time; server->ping = 0; Netchan_OutOfBandPrint(NS_UDP_CLIENT, &server->addr, "info %i", PROTOCOL_MAJOR); } e = e->next; } }
/* * Cl_ParseServersList */ void Cl_ParseServersList(void) { byte *buffptr; byte *buffend; byte ip[4]; unsigned short port; net_addr_t addr; cl_server_info_t *server; char s[32]; buffptr = net_message.data + 12; buffend = buffptr + net_message.size - 12; // parse the list while (buffptr + 1 < buffend) { ip[0] = *buffptr++; // parse the address ip[1] = *buffptr++; ip[2] = *buffptr++; ip[3] = *buffptr++; port = (*buffptr++) << 8; // and the port port += *buffptr++; snprintf(s, sizeof(s), "%d.%d.%d.%d:%d", ip[0], ip[1], ip[2], ip[3], port); if (!Net_StringToNetaddr(s, &addr)) { // make sure it's valid Com_Warn("Cl_ParseServersList: Invalid address: %s.\n", s); break; } if (!addr.port) // 0's mean we're done break; server = Cl_ServerForNetaddr(&addr); if (!server) server = Cl_AddServer(&addr); server->source = SERVER_SOURCE_INTERNET; } net_message.read = net_message.size; // then ping them server = cls.servers; while (server) { if (server->source == SERVER_SOURCE_INTERNET) { server->ping_time = cls.real_time; server->ping = 0; Netchan_OutOfBandPrint(NS_CLIENT, server->addr, "info %i", PROTOCOL); } server = server->next; } }