/* * 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 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 Inserts the specified media into the shared table. */ void S_RegisterMedia(s_media_t *media) { // check to see if we're already seeded if (media->seed != s_media_state.seed) { s_media_t *m; if ((m = g_hash_table_lookup(s_media_state.media, media->name))) { if (m != media) { Com_Debug(DEBUG_SOUND, "Replacing %s\n", media->name); S_FreeMedia_(NULL, m, m); g_hash_table_replace(s_media_state.media, media->name, media); } else { Com_Debug(DEBUG_SOUND, "Retaining %s\n", media->name); } } else { Com_Debug(DEBUG_SOUND, "Inserting %s\n", media->name); g_hash_table_insert(s_media_state.media, media->name, media); s_media_state.keys = g_list_insert_sorted(s_media_state.keys, media->name, S_RegisterMedia_Compare); } // re-seed the media to retain it media->seed = s_media_state.seed; } // finally re-register all dependencies GList *d = media->dependencies; while (d) { S_RegisterMedia((s_media_t *) d->data); d = d->next; } }
/* * @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 Marks all nodes that can be reached by entites */ _Bool FloodEntities(tree_t *tree) { int32_t i; vec3_t origin; const char *cl; _Bool inside; node_t *head_node; head_node = tree->head_node; Com_Debug("--- FloodEntities ---\n"); inside = false; tree->outside_node.occupied = 0; cl = ""; for (i = 1; i < num_entities; i++) { GetVectorForKey(&entities[i], "origin", origin); if (VectorCompare(origin, vec3_origin)) continue; cl = ValueForKey(&entities[i], "classname"); origin[2] += 1; // so objects on floor are ok // nudge playerstart around if needed so clipping hulls always // have a valid point if (!g_strcmp0(cl, "info_player_start")) { int32_t x, y; for (x = -16; x <= 16; x += 16) { for (y = -16; y <= 16; y += 16) { origin[0] += x; origin[1] += y; if (PlaceOccupant(head_node, origin, &entities[i])) { inside = true; goto gotit; } origin[0] -= x; origin[1] -= y; } } gotit: ; } else { if (PlaceOccupant(head_node, origin, &entities[i])) inside = true; } } if (!inside) { Com_Debug("no entities in open -- no filling\n"); } else if (tree->outside_node.occupied) { Com_Debug("entity %s reached from outside -- no filling\n", cl); } return inside && !tree->outside_node.occupied; }
/* * 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 Plays the next track in the configured playlist. */ void S_NextTrack_f(void) { if (s_music_volume->value) { s_music_t *music = S_NextMusic(); if (music) { S_PlayMusic(music); } else { Com_Debug("No music available\n"); } } else { Com_Debug("Music is muted\n"); } }
/* * 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)); } }
/* * @brief Initializes the client console. */ void Cl_InitConsole(void) { memset(&cl_console, 0, sizeof(cl_console)); cl_console.Append = Cl_Print; Con_AddConsole(&cl_console); file_t *file = Fs_OpenRead("history"); if (file) { Con_ReadHistory(&cl_console, file); Fs_Close(file); } else { Com_Debug("Couldn't read history"); } memset(&cl_chat_console, 0, sizeof(cl_chat_console)); cl_chat_console.level = PRINT_CHAT | PRINT_TEAM_CHAT; cl_draw_chat = Cvar_Get("cl_draw_chat", "1", 0, "Draw recent chat messages"); cl_draw_notify = Cvar_Get("cl_draw_notify", "1", 0, "Draw recent console activity"); cl_notify_lines = Cvar_Get("cl_console_notify_lines", "3", CVAR_ARCHIVE, NULL); cl_notify_time = Cvar_Get("cl_notify_time", "3.0", CVAR_ARCHIVE, NULL); cl_chat_lines = Cvar_Get("cl_chat_lines", "3", CVAR_ARCHIVE, NULL); cl_chat_time = Cvar_Get("cl_chat_time", "10.0", CVAR_ARCHIVE, NULL); Cmd_Add("cl_toggle_console", Cl_ToggleConsole_f, CMD_SYSTEM | CMD_CLIENT, "Toggle the console"); Cmd_Add("cl_message_mode", Cl_MessageMode_f, CMD_CLIENT, "Activate chat"); Cmd_Add("cl_message_mode_2", Cl_MessageMode2_f, CMD_CLIENT, "Activate team chat"); Com_Print("Client console initialized\n"); }
/** * @brief Entry point for client-side prediction. For each server frame, run * the player movement code with the user commands we've sent to the server * but have not yet received acknowledgment for. Store the resulting move so * that it may be interpolated into by Cl_UpdateView. * * Most of the work is passed off to the client game, which is responsible for * the implementation Pm_Move. */ void Cl_PredictMovement(void) { if (!cls.cgame->UsePrediction()) { return; } const uint32_t last = cls.net_chan.outgoing_sequence; uint32_t ack = cls.net_chan.incoming_acknowledged; // if we are too far out of date, just freeze in place if (last - ack >= CMD_BACKUP) { Com_Debug(DEBUG_CLIENT, "Exceeded CMD_BACKUP\n"); return; } GList *cmds = NULL; while (++ack <= last) { cmds = g_list_append(cmds, &cl.cmds[ack & CMD_MASK]); } cls.cgame->PredictMovement(cmds); g_list_free(cmds); }
/* * @brief Checks for client side prediction errors. Problems here can indicate * that Pm_Move or the protocol are not functioning correctly. */ void Cl_CheckPredictionError(void) { vec3_t delta; VectorClear(cl.predicted_state.error); if (!Cl_UsePrediction()) return; // calculate the last user_cmd_t we sent that the server has processed const uint32_t frame = (cls.net_chan.incoming_acknowledged & CMD_MASK); // compare what the server returned with what we had predicted it to be VectorSubtract(cl.frame.ps.pm_state.origin, cl.predicted_state.origins[frame], delta); const vec_t error = VectorLength(delta); if (error > 1.0) { Com_Debug("%s\n", vtos(delta)); if (error > 256.0) { // do not interpolate VectorClear(delta); } } VectorCopy(delta, cl.predicted_state.error); }
/* * Cl_LoadLocations * * Parse a .loc file for the current level. */ void Cl_LoadLocations(void) { const char *c; char file_name[MAX_QPATH]; FILE *f; int i; Cl_ClearLocations(); // clear any resident locations i = 0; // load the locations file c = Basename(cl.config_strings[CS_MODELS + 1]); snprintf(file_name, sizeof(file_name), "locations/%s", c); strcpy(file_name + strlen(file_name) - 3, "loc"); if (Fs_OpenFile(file_name, &f, FILE_READ) == -1) { Com_Debug("Couldn't load %s\n", file_name); return; } while (i < MAX_LOCATIONS) { const int err = fscanf(f, "%f %f %f %[^\n]", &locations[i].loc[0], &locations[i].loc[1], &locations[i].loc[2], locations[i].desc); num_locations = i; if (err == EOF) break; i++; } Cl_LoadProgress(100); Com_Print("Loaded %i locations.\n", num_locations); Fs_CloseFile(f); }
/* * 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 Updates the interpolation fraction for the current client frame. * Because the client typically runs at a higher framerate than the server, we * use linear interpolation between the last 2 server frames. We aim to reach * the current server time just as a new packet arrives. */ static void Cl_UpdateLerp(const cl_frame_t *from) { if (time_demo->value) { cl.time = cl.frame.time; cl.lerp = 1.0; return; } if (cl.time > cl.frame.time) { // Com_Debug("High clamp: %dms\n", cl.time - cl.frame.time); cl.time = cl.frame.time; cl.lerp = 1.0; } else if (cl.time < from->time) { // Com_Debug("Low clamp: %dms\n", from->time - cl.time); cl.time = from->time; cl.lerp = 0.0; } else { const uint32_t delta = cl.time - from->time; const uint32_t interval = cl.frame.time - from->time; if (interval == 0) { Com_Debug("Bad clamp\n"); cl.time = cl.frame.time; cl.lerp = 1.0; return; } cl.lerp = delta / (vec_t) interval; } }
/* * @brief Loads the music by the specified name. */ s_music_t *S_LoadMusic(const char *name) { char key[MAX_QPATH]; s_music_t *music; StripExtension(name, key); if (!(music = (s_music_t *) S_FindMedia(key))) { void *buffer; SDL_RWops *rw; Mix_Music *mus; if (S_LoadMusicFile(key, &buffer, &rw, &mus)) { music = (s_music_t *) S_AllocMedia(key, sizeof(s_music_t)); music->media.Retain = S_RetainMusic; music->media.Free = S_FreeMusic; music->buffer = buffer; music->rw = rw; music->music = mus; S_RegisterMedia((s_media_t *) music); } else { Com_Debug("S_LoadMusic: Couldn't load %s\n", key); music = NULL; } } if (music) { s_music_state.playlist = g_list_append(s_music_state.playlist, music); } return music; }
/* * @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_KeyDown(cl_button_t *b) { SDL_Scancode k; const char *c = Cmd_Argv(1); if (c[0]) k = atoi(c); else k = SDL_NUM_SCANCODES; // typed manually at the console for continuous down if (k == b->keys[0] || k == b->keys[1]) return; // repeating key if (b->keys[0] == SDL_SCANCODE_UNKNOWN) b->keys[0] = k; else if (b->keys[1] == SDL_SCANCODE_UNKNOWN) b->keys[1] = k; else { Com_Debug("3 keys down for button\n"); return; } if (b->state & 1) return; // still down // save the down time so that we can calculate fractional time later const char *t = Cmd_Argv(2); b->down_time = atoi(t); if (!b->down_time) b->down_time = cls.real_time; b->state |= 1; }
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); } }
/* * Sys_FindFirst * * Returns the first full path name matched by the specified search path in * the Quake file system. Wildcards are partially supported. */ const char *Sys_FindFirst(const char *path) { struct dirent *d; char *p; if (fdir) { Com_Debug("Sys_FindFirst without Sys_FindClose"); Sys_FindClose(); } strcpy(findbase, path); if ((p = strrchr(findbase, '/')) != NULL) { *p = 0; strcpy(findpattern, p + 1); } else strcpy(findpattern, "*"); if (strcmp(findpattern, "*.*") == 0) strcpy(findpattern, "*"); if ((fdir = opendir(findbase)) == NULL) return NULL; while ((d = readdir(fdir)) != NULL) { if (!*findpattern || GlobMatch(findpattern, d->d_name)) { sprintf(findpath, "%s/%s", findbase, d->d_name); return findpath; } } return NULL; }
/* * @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; } }
/** * @brief Adds an illumination with the given parameters. */ static void R_AddIllumination(const r_illumination_t *il) { if (r_illuminations.num_illuminations == LIGHTING_MAX_ILLUMINATIONS) { Com_Debug(DEBUG_RENDERER, "LIGHTING_MAX_ILLUMINATIONS\n"); return; } r_illuminations.illuminations[r_illuminations.num_illuminations++] = *il; }
/* * Sv_Frame */ void Sv_Frame(unsigned int msec) { unsigned int frame_millis; // if server is not active, do nothing if (!svs.initialized) return; // update time reference svs.real_time += msec; // keep the random time dependent rand(); // check timeouts Sv_CheckTimeouts(); // get packets from clients Sv_ReadPackets(); frame_millis = 1000 / svs.frame_rate; // keep the game module's time in sync with reality if (!time_demo->value && svs.real_time < sv.time) { // if the server has fallen far behind the game, try to catch up if (sv.time - svs.real_time > frame_millis) { Com_Debug("Sv_Frame: Low clamp: %dms.\n", (sv.time - svs.real_time - frame_millis)); svs.real_time = sv.time - frame_millis; } else { // wait until its time to run the next frame Net_Sleep(sv.time - svs.real_time); return; } } // update ping based on the last known frame from all clients Sv_UpdatePings(); // give the clients some timeslices Sv_CheckCommandTimes(); // let everything in the world think and move Sv_RunGameFrame(); // send messages back to the clients that had packets read this frame Sv_SendClientMessages(); // send a heartbeat to the master if needed Sv_HeartbeatMasters(); // clear entity flags, etc for next frame Sv_ResetEntities(); #ifdef HAVE_CURSES Curses_Frame(msec); #endif }
/* * @brief Merges the portal visibility for a leaf. */ static void ClusterMerge(uint32_t leaf_num) { leaf_t *leaf; byte portalvector[MAX_BSP_PORTALS / 8]; byte uncompressed[MAX_BSP_LEAFS / 8]; byte compressed[MAX_BSP_LEAFS / 8]; uint32_t i, j; int32_t numvis; byte *dest; portal_t *p; int32_t pnum; if (map_vis.portal_bytes > sizeof(portalvector)) { Com_Error(ERR_FATAL, "VIS overflow. Try making more brushes CONTENTS_DETAIL.\n"); } // OR together all the portal vis bits memset(portalvector, 0, map_vis.portal_bytes); leaf = &map_vis.leafs[leaf_num]; for (i = 0; i < leaf->num_portals; i++) { p = leaf->portals[i]; if (p->status != stat_done) Com_Error(ERR_FATAL, "Portal not done\n"); for (j = 0; j < map_vis.portal_longs; j++) ((long *) portalvector)[j] |= ((long *) p->vis)[j]; pnum = p - map_vis.portals; portalvector[pnum >> 3] |= 1 << (pnum & 7); } // convert portal bits to leaf bits numvis = LeafVectorFromPortalVector(portalvector, uncompressed); if (uncompressed[leaf_num >> 3] & (1 << (leaf_num & 7))) { Com_Warn("Leaf portals saw into leaf\n"); } uncompressed[leaf_num >> 3] |= (1 << (leaf_num & 7)); numvis++; // count the leaf itself // save uncompressed for PHS calculation memcpy(map_vis.uncompressed + leaf_num * map_vis.leaf_bytes, uncompressed, map_vis.leaf_bytes); // compress the bit string Com_Debug("Cluster %4i : %4i visible\n", leaf_num, numvis); visibility_count += numvis; i = CompressVis(uncompressed, compressed); dest = map_vis.pointer; map_vis.pointer += i; if (map_vis.pointer > map_vis.end) Com_Error(ERR_FATAL, "VIS expansion overflow\n"); d_vis->bit_offsets[leaf_num][DVIS_PVS] = dest - map_vis.base; memcpy(dest, compressed, i); }
/** * @brief */ static int32_t S_AllocChannel(void) { for (int32_t i = 0; i < MAX_CHANNELS; i++) { if (!s_env.channels[i].sample) { return i; } } Com_Debug(DEBUG_SOUND, "Failed\n"); return -1; }
/* * @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 Add a stain to the map. */ void R_AddStain(const r_stain_t *s) { if (!r_stainmap->value) { return; } if (r_view.num_stains == MAX_STAINS) { Com_Debug(DEBUG_RENDERER, "MAX_STAINS reached\n"); return; } r_view.stains[r_view.num_stains++] = *s; }
/* * @brief Inserts the specified media into the shared table, re-registering all * of its dependencies as well. */ void R_RegisterMedia(r_media_t *media) { // check to see if we're already seeded if (media->seed != r_media_state.seed) { r_media_t *m; if ((m = g_hash_table_lookup(r_media_state.media, media->name))) { if (m != media) { // the old instance will eventually be freed Com_Debug("Replacing %s\n", media->name); R_FreeMedia_(NULL, m, m); g_hash_table_replace(r_media_state.media, media->name, media); } else { Com_Debug("Retaining %s\n", media->name); } } else { Com_Debug("Inserting %s\n", media->name); g_hash_table_insert(r_media_state.media, media->name, media); } r_media_state.keys = g_list_insert_sorted(r_media_state.keys, media->name, R_RegisterMedia_Compare); // re-seed the media to retain it media->seed = r_media_state.seed; } // call the implementation-specific register function if (media->Register) { media->Register(media); } // and finally re-register all dependencies GList *d = media->dependencies; while (d) { R_RegisterMedia((r_media_t *) d->data); d = d->next; } }
/* * @brief Inserts the specified media into the shared table. */ void S_RegisterMedia(s_media_t *media) { // check to see if we're already seeded if (media->seed != s_media_state.seed) { s_media_t *m; if ((m = g_hash_table_lookup(s_media_state.media, media->name))) { if (m != media) { Com_Error(ERR_DROP, "Name collision: %s\n", media->name); } else { Com_Debug("Retaining %s\n", media->name); } } else { Com_Debug("Inserting %s\n", media->name); g_hash_table_insert(s_media_state.media, media->name, media); s_media_state.keys = g_list_insert_sorted(s_media_state.keys, media->name, S_RegisterMedia_Compare); } // re-seed the media to retain it media->seed = s_media_state.seed; } }
/* * @brief Updates the game module's time and runs its frame function once per * server frame. */ static void Sv_RunGameFrame(void) { sv.frame_num++; sv.time = sv.frame_num * 1000 / svs.frame_rate; if (sv.time < svs.real_time) { Com_Debug("Sv_RunGameFrame: High clamp: %dms\n", svs.real_time - sv.time); svs.real_time = sv.time; } if (sv.state == SV_ACTIVE_GAME) { svs.game->Frame(); } }
/** * @brief Checks for client side prediction errors. These will occur under normal gameplay * conditions if the client is pushed by another entity on the server (projectile, platform, etc.). */ void Cl_CheckPredictionError(void) { if (!cls.cgame->UsePrediction()) { return; } cl_predicted_state_t *pr = &cl.predicted_state; if (cl.delta_frame) { // calculate the last cl_cmd_t we sent that the server has processed const uint32_t cmd = cls.net_chan.incoming_acknowledged & CMD_MASK; // subtract what the server returned with what we had predicted it to be VectorSubtract(cl.frame.ps.pm_state.origin, pr->origins[cmd], pr->error); // if the error is too large, it was likely a teleport or respawn, so ignore it const vec_t len = VectorLength(pr->error); if (len > 64.0) { Com_Debug(DEBUG_CLIENT, "Clear %s\n", vtos(pr->error)); VectorClear(pr->error); } else if (len > 0.1) { Com_Debug(DEBUG_CLIENT, "Error %s\n", vtos(pr->error)); } } else { Com_Debug(DEBUG_CLIENT, "No delta\n"); VectorCopy(cl.frame.ps.pm_state.origin, pr->view.origin); UnpackVector(cl.frame.ps.pm_state.view_offset, pr->view.offset); UnpackAngles(cl.frame.ps.pm_state.view_angles, pr->view.angles); VectorClear(pr->error); } }