static void WriteCache( void ) { char buffer[MAX_OSPATH]; qhandle_t f; int i; char *map, *pov; demoEntry_t *e; size_t len; if( m_demos.list.numItems == m_demos.numDirs ) { return; } len = Q_concat( buffer, sizeof( buffer ), m_demos.browse, "/" COM_DEMOCACHE_NAME, NULL ); if( len >= sizeof( buffer ) ) { return; } FS_FOpenFile( buffer, &f, FS_MODE_WRITE ); if( !f ) { return; } for( i = 0; i < 16; i++ ) { FS_FPrintf( f, "%02x", m_demos.hash[i] ); } FS_FPrintf( f, "\\" ); for( i = m_demos.numDirs; i < m_demos.list.numItems; i++ ) { e = m_demos.list.items[i]; map = UI_GetColumn( e->name, COL_MAP ); pov = UI_GetColumn( e->name, COL_POV ); FS_FPrintf( f, "%s\\%s\\", map, pov ); } FS_FCloseFile( f ); }
/* ============ R_SetSky ============ */ void R_SetSky( const char *name, float rotate, vec3_t axis ) { int i; char pathname[MAX_QPATH]; image_t *image; size_t len; // 3dstudio environment map names static const char suf[6][3] = { "rt", "bk", "lf", "ft", "up", "dn" }; if( !gl_drawsky->integer ) { R_UnsetSky(); return; } skyrotate = rotate; VectorCopy (axis, skyaxis); for( i = 0; i < 6; i++ ) { len = Q_concat( pathname, sizeof( pathname ), "env/", name, suf[i], ".tga", NULL ); if( len >= sizeof( pathname ) ) { R_UnsetSky(); return; } FS_NormalizePath( pathname, pathname ); image = IMG_Find( pathname, it_sky ); if( image->texnum == TEXNUM_DEFAULT ) { R_UnsetSky(); return; } sky_images[i] = image->texnum; } }
/* ================= ProcessTexinfo ================= */ static void ProcessTexinfo(bsp_t *bsp) { mtexinfo_t *tex; int i; vec_t len1, len2; char name[MAX_QPATH]; tex = bsp->texinfo; for (i = 0; i < bsp->numtexinfo; i++, tex++) { len1 = VectorLength(tex->axis[0]); len2 = VectorLength(tex->axis[1]); len1 = (len1 + len2) / 2; if (len1 < 0.32) tex->mipadjust = 4; else if (len1 < 0.49) tex->mipadjust = 3; else if (len1 < 0.99) tex->mipadjust = 2; else tex->mipadjust = 1; Q_concat(name, sizeof(name), "textures/", tex->name, ".wal", NULL); FS_NormalizePath(name, name); tex->image = IMG_Find(name, IT_WALL); } }
static void Sys_DeleteService_f(void) { char serviceName[256]; SC_HANDLE scm, service; DWORD error; if (Cmd_Argc() < 2) { Com_Printf("Usage: %s <servicename>\n", Cmd_Argv(0)); return; } scm = OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_ALL_ACCESS); if (!scm) { error = GetLastError(); if (error == ERROR_ACCESS_DENIED) { Com_Printf("Insufficient privileges for opening Service Control Manager.\n"); } else { Com_EPrintf("%#lx opening Service Control Manager.\n", error); } return; } Q_concat(serviceName, sizeof(serviceName), "Q2PRO - ", Cmd_Argv(1), NULL); service = OpenService( scm, serviceName, DELETE); if (!service) { error = GetLastError(); if (error == ERROR_SERVICE_DOES_NOT_EXIST) { Com_Printf("Service doesn't exist.\n"); } else { Com_EPrintf("%#lx opening service.\n", error); } goto fail; } if (!DeleteService(service)) { error = GetLastError(); if (error == ERROR_SERVICE_MARKED_FOR_DELETE) { Com_Printf("Service has already been marked for deletion.\n"); } else { Com_EPrintf("%#lx deleting service.\n", error); } } else { Com_Printf("Service deleted successfully.\n"); } CloseServiceHandle(service); fail: CloseServiceHandle(scm); }
static char *LoadCache(void **list) { char buffer[MAX_OSPATH], *cache; int i; size_t len; uint8_t hash[16]; len = Q_concat(buffer, sizeof(buffer), m_demos.browse, "/" COM_DEMOCACHE_NAME, NULL); if (len >= sizeof(buffer)) { return NULL; } len = FS_LoadFileEx(buffer, (void **)&cache, FS_TYPE_REAL | FS_PATH_GAME, TAG_FILESYSTEM); if (!cache) { return NULL; } if (len < 33) { goto fail; } for (i = 0; i < 16; i++) { int c1 = Q_charhex(cache[i * 2 + 0]); int c2 = Q_charhex(cache[i * 2 + 1]); hash[i] = (c1 << 4) | c2; } if (cache[32] != '\\') { goto fail; } if (memcmp(hash, m_demos.hash, 16)) { goto fail; } Com_DPrintf("%s: loading from cache\n", __func__); return cache; fail: FS_FreeFile(cache); return NULL; }
// optionally load the entity string from external source static void override_entity_string( const char *server ) { char *path = map_override_path->string; char buffer[MAX_QPATH], *str; ssize_t len; if( !*path ) { return; } len = Q_concat( buffer, sizeof( buffer ), path, server, ".ent", NULL ); if( len >= sizeof( buffer ) ) { len = Q_ERR_NAMETOOLONG; goto fail1; } len = SV_LoadFile( buffer, ( void ** )&str ); if( !str ) { if( len == Q_ERR_NOENT ) { return; } goto fail1; } if( len > MAX_MAP_ENTSTRING ) { len = Q_ERR_FBIG; goto fail2; } Com_Printf( "Loaded entity string from %s\n", buffer ); sv.entitystring = str; return; fail2: SV_FreeFile( str ); fail1: Com_EPrintf( "Couldn't load entity string from %s: %s\n", buffer, Q_ErrorString( len ) ); }
/* @@@@@@@@@@@@@@@@@@@@@ R_BeginRegistration Specifies the model that will be used as the world @@@@@@@@@@@@@@@@@@@@@ */ void R_BeginRegistration(const char *model) { char fullname[MAX_QPATH]; bsp_t *bsp; qerror_t ret; int i; registration_sequence++; r_oldviewcluster = -1; // force markleafs D_FlushCaches(); Q_concat(fullname, sizeof(fullname), "maps/", model, ".bsp", NULL); ret = BSP_Load(fullname, &bsp); if (!bsp) { Com_Error(ERR_DROP, "%s: couldn't load %s: %s", __func__, fullname, Q_ErrorString(ret)); } if (bsp == r_worldmodel) { for (i = 0; i < bsp->numtexinfo; i++) { bsp->texinfo[i].image->registration_sequence = registration_sequence; } bsp->refcount--; return; } BSP_Free(r_worldmodel); r_worldmodel = bsp; ProcessTexinfo(bsp); ProcessFaces(bsp); // TODO R_NewMap(); }
/* ==================== Prompt_CompleteCommand ==================== */ void Prompt_CompleteCommand(commandPrompt_t *prompt, qboolean backslash) { inputField_t *inputLine = &prompt->inputLine; char *text, *partial, *s; int i, argc, currentArg, argnum; size_t size, len, pos; char *first, *last; genctx_t ctx; char *matches[MAX_MATCHES], *sortedMatches[MAX_MATCHES]; int numCommands, numCvars, numAliases; text = inputLine->text; size = inputLine->maxChars + 1; pos = inputLine->cursorPos; // prepend backslash if missing if (backslash) { if (inputLine->text[0] != '\\' && inputLine->text[0] != '/') { memmove(inputLine->text + 1, inputLine->text, size - 1); inputLine->text[0] = '\\'; } text++; size--; pos--; } // parse the input line into tokens Cmd_TokenizeString(text, qfalse); argc = Cmd_Argc(); // determine absolute argument number to be completed currentArg = Cmd_FindArgForOffset(pos); if (currentArg == argc - 1 && Cmd_WhiteSpaceTail()) { // start completing new argument if command line has trailing whitespace currentArg++; } // determine relative argument number to be completed argnum = 0; for (i = 0; i < currentArg; i++) { s = Cmd_Argv(i); argnum++; if (*s == ';') { // semicolon starts a new command argnum = 0; } } // get the partial argument string to be completed partial = Cmd_Argv(currentArg); if (*partial == ';') { // semicolon starts a new command currentArg++; partial = Cmd_Argv(currentArg); argnum = 0; } // generate matches memset(&ctx, 0, sizeof(ctx)); ctx.partial = partial; ctx.length = strlen(partial); ctx.argnum = currentArg; ctx.matches = matches; ctx.size = MAX_MATCHES; if (argnum) { // complete a command/cvar argument Com_Generic_c(&ctx, argnum); numCommands = numCvars = numAliases = 0; } else { // complete a command/cvar/alias name Cmd_Command_g(&ctx); numCommands = ctx.count; Cvar_Variable_g(&ctx); numCvars = ctx.count - numCommands; Cmd_Alias_g(&ctx); numAliases = ctx.count - numCvars - numCommands; } if (!ctx.count) { pos = strlen(inputLine->text); prompt->tooMany = qfalse; goto finish2; // nothing found } pos = Cmd_ArgOffset(currentArg); text += pos; size -= pos; // append whitespace since Cmd_TokenizeString eats it if (currentArg == argc && Cmd_WhiteSpaceTail()) { *text++ = ' '; pos++; size--; } if (ctx.count == 1) { // we have finished completion! s = Cmd_RawArgsFrom(currentArg + 1); if (needs_quotes(matches[0])) { pos += Q_concat(text, size, "\"", matches[0], "\" ", s, NULL); } else { pos += Q_concat(text, size, matches[0], " ", s, NULL); } pos++; prompt->tooMany = qfalse; goto finish1; } if (ctx.count > com_completion_treshold->integer && !prompt->tooMany) { prompt->printf("Press TAB again to display all %d possibilities.\n", ctx.count); pos = strlen(inputLine->text); prompt->tooMany = qtrue; goto finish1; } prompt->tooMany = qfalse; // sort matches alphabethically for (i = 0; i < ctx.count; i++) { sortedMatches[i] = matches[i]; } qsort(sortedMatches, ctx.count, sizeof(sortedMatches[0]), ctx.ignorecase ? SortStricmp : SortStrcmp); // copy matching part first = sortedMatches[0]; last = sortedMatches[ctx.count - 1]; len = 0; do { if (*first != *last) { if (!ctx.ignorecase || Q_tolower(*first) != Q_tolower(*last)) { break; } } text[len++] = *first; if (len == size - 1) { break; } first++; last++; } while (*first); text[len] = 0; pos += len; size -= len; // copy trailing arguments if (currentArg + 1 < argc) { s = Cmd_RawArgsFrom(currentArg + 1); pos += Q_concat(text + len, size, " ", s, NULL); } pos++; prompt->printf("]\\%s\n", Cmd_ArgsFrom(0)); if (argnum) { goto multi; } switch (com_completion_mode->integer) { case 0: // print in solid list for (i = 0; i < ctx.count; i++) { prompt->printf("%s\n", sortedMatches[i]); } break; case 1: multi: // print in multiple columns Prompt_ShowMatches(prompt, sortedMatches, 0, ctx.count); break; case 2: default: // resort matches by type and print in multiple columns Prompt_ShowIndividualMatches(prompt, matches, numCommands, numAliases, numCvars); break; } finish1: // free matches for (i = 0; i < ctx.count; i++) { Z_Free(matches[i]); } finish2: // move cursor if (pos >= inputLine->maxChars) { pos = inputLine->maxChars - 1; } inputLine->cursorPos = pos; }
/* ================= Sys_ListFiles_r Internal function to filesystem. Conventions apply: - files should hold at least MAX_LISTED_FILES - *count_p must be initialized in range [0, MAX_LISTED_FILES - 1] - depth must be 0 on the first call ================= */ void Sys_ListFiles_r(const char *path, const char *filter, unsigned flags, size_t baselen, int *count_p, void **files, int depth) { struct dirent *ent; DIR *dir; struct stat st; char fullpath[MAX_OSPATH]; char *name; size_t len; void *info; if ((dir = opendir(path)) == NULL) { return; } while ((ent = readdir(dir)) != NULL) { if (ent->d_name[0] == '.') { continue; // ignore dotfiles } len = Q_concat(fullpath, sizeof(fullpath), path, "/", ent->d_name, NULL); if (len >= sizeof(fullpath)) { continue; } st.st_mode = 0; #ifdef _DIRENT_HAVE_D_TYPE // try to avoid stat() if possible if (!(flags & FS_SEARCH_EXTRAINFO) && ent->d_type != DT_UNKNOWN && ent->d_type != DT_LNK) { st.st_mode = DTTOIF(ent->d_type); } #endif if (st.st_mode == 0 && stat(fullpath, &st) == -1) { continue; } // pattern search implies recursive search if ((flags & FS_SEARCH_BYFILTER) && S_ISDIR(st.st_mode) && depth < MAX_LISTED_DEPTH) { Sys_ListFiles_r(fullpath, filter, flags, baselen, count_p, files, depth + 1); // re-check count if (*count_p >= MAX_LISTED_FILES) { break; } } // check type if (flags & FS_SEARCH_DIRSONLY) { if (!S_ISDIR(st.st_mode)) { continue; } } else { if (!S_ISREG(st.st_mode)) { continue; } } // check filter if (filter) { if (flags & FS_SEARCH_BYFILTER) { if (!FS_WildCmp(filter, fullpath + baselen)) { continue; } } else { if (!FS_ExtCmp(filter, ent->d_name)) { continue; } } } // strip path if (flags & FS_SEARCH_SAVEPATH) { name = fullpath + baselen; } else { name = ent->d_name; } // strip extension if (flags & FS_SEARCH_STRIPEXT) { *COM_FileExtension(name) = 0; if (!*name) { continue; } } // copy info off if (flags & FS_SEARCH_EXTRAINFO) { info = FS_CopyInfo(name, st.st_size, st.st_ctime, st.st_mtime); } else { info = FS_CopyString(name); } files[(*count_p)++] = info; if (*count_p >= MAX_LISTED_FILES) { break; } } closedir(dir); }
/* ================ SV_SpawnServer Change the server to a new map, taking all connected clients along with it. ================ */ void SV_SpawnServer( cm_t *cm, const char *server, const char *spawnpoint ) { int i; client_t *client; #if USE_CLIENT SCR_BeginLoadingPlaque(); // for local system #endif Com_Printf( "------- Server Initialization -------\n" ); Com_Printf( "SpawnServer: %s\n", server ); // everyone needs to reconnect FOR_EACH_CLIENT( client ) { SV_ClientReset( client ); } SV_BroadcastCommand( "changing map=%s\n", server ); SV_SendClientMessages(); SV_SendAsyncPackets(); // free current level CM_FreeMap( &sv.cm ); SV_FreeFile( sv.entitystring ); // wipe the entire per-level structure memset( &sv, 0, sizeof( sv ) ); sv.spawncount = ( rand() | ( rand() << 16 ) ) ^ Sys_Milliseconds(); sv.spawncount &= 0x7FFFFFFF; // set legacy spawncounts FOR_EACH_CLIENT( client ) { client->spawncount = sv.spawncount; } // reset entity counter svs.next_entity = 0; #if USE_FPS // set framerate parameters set_frame_time(); #endif // save name for levels that don't set message Q_strlcpy( sv.configstrings[CS_NAME], server, MAX_QPATH ); Q_strlcpy( sv.name, server, sizeof( sv.name ) ); if( Cvar_VariableInteger( "deathmatch" ) ) { sprintf( sv.configstrings[CS_AIRACCEL], "%d", sv_airaccelerate->integer ); } else { strcpy( sv.configstrings[CS_AIRACCEL], "0" ); } #if !USE_CLIENT resolve_masters(); #endif override_entity_string( server ); sv.cm = *cm; sprintf( sv.configstrings[CS_MAPCHECKSUM], "%d", ( int )cm->cache->checksum ); // set inline model names Q_concat( sv.configstrings[CS_MODELS + 1], MAX_QPATH, "maps/", server, ".bsp", NULL ); for( i = 1; i < cm->cache->nummodels; i++ ) { sprintf( sv.configstrings[ CS_MODELS + 1 + i ], "*%d", i ); } // // clear physics interaction links // SV_ClearWorld(); // // spawn the rest of the entities on the map // // precache and static commands can be issued during // map initialization sv.state = ss_loading; X86_PUSH_FPCW; X86_SINGLE_FPCW; // load and spawn all other entities ge->SpawnEntities ( sv.name, sv.entitystring ? sv.entitystring : cm->cache->entitystring, spawnpoint ); // run two frames to allow everything to settle ge->RunFrame (); sv.framenum++; ge->RunFrame (); sv.framenum++; X86_POP_FPCW; // make sure maxclients string is correct sprintf( sv.configstrings[CS_MAXCLIENTS], "%d", sv_maxclients->integer ); // all precaches are complete sv.state = ss_game; #if USE_MVD_SERVER // respawn dummy MVD client, set base states, etc SV_MvdMapChanged(); #endif // set serverinfo variable SV_InfoSet( "mapname", sv.name ); SV_InfoSet( "port", net_port->string ); Cvar_SetInteger( sv_running, ss_game, FROM_CODE ); Cvar_Set( "sv_paused", "0" ); Cvar_Set( "timedemo", "0" ); EXEC_TRIGGER( sv_changemapcmd ); #if USE_SYSCON SV_SetConsoleTitle(); #endif SV_BroadcastCommand( "reconnect\n" ); Com_Printf ("-------------------------------------\n"); }
static void BuildName( const file_info_t *info, char **cache ) { char buffer[MAX_OSPATH]; char date[MAX_QPATH]; demoInfo_t demo; demoEntry_t *e; struct tm *tm; size_t len; memset( &demo, 0, sizeof( demo ) ); strcpy( demo.map, "???" ); strcpy( demo.pov, "???" ); if( cache ) { char *s = *cache; char *p = strchr( s, '\\' ); if( p ) { *p = 0; Q_strlcpy( demo.map, s, sizeof( demo.map ) ); s = p + 1; p = strchr( s, '\\' ); if( p ) { *p = 0; Q_strlcpy( demo.pov, s, sizeof( demo.pov ) ); s = p + 1; } } *cache = s; } else { Q_concat( buffer, sizeof( buffer ), m_demos.browse, "/", info->name, NULL ); CL_GetDemoInfo( buffer, &demo ); if( demo.mvd ) { strcpy( demo.pov, DEMO_MVD_POV ); } } // resize columns len = strlen( demo.map ); if( len > 8 ) { len = 8; } if( len > m_demos.widest_map ) { m_demos.widest_map = len; } len = strlen( demo.pov ); if( len > m_demos.widest_pov ) { m_demos.widest_pov = len; } // format date if( ( tm = localtime( &info->mtime ) ) != NULL ) { if( tm->tm_year == m_demos.year ) { strftime( date, sizeof( date ), "%b %d %H:%M", tm ); } else { strftime( date, sizeof( date ), "%b %d %Y", tm ); } } else { strcpy( date, "???" ); } Com_FormatSize( buffer, sizeof( buffer ), info->size ); e = UI_FormatColumns( DEMO_EXTRASIZE, info->name, date, buffer, demo.map, demo.pov, NULL ); e->type = ENTRY_DEMO; e->size = info->size; e->mtime = info->mtime; m_demos.total_bytes += info->size; m_demos.list.items[m_demos.list.numItems++] = e; }
/* ================= Sys_ListFiles_r Internal function to filesystem. Conventions apply: - files should hold at least MAX_LISTED_FILES - *count_p must be initialized in range [0, MAX_LISTED_FILES - 1] - depth must be 0 on the first call ================= */ void Sys_ListFiles_r(const char *path, const char *filter, unsigned flags, size_t baselen, int *count_p, void **files, int depth) { WIN32_FIND_DATAA data; HANDLE handle; char fullpath[MAX_OSPATH], *name; size_t pathlen, len; unsigned mask; void *info; // optimize single extension search if (!(flags & FS_SEARCH_BYFILTER) && filter && !strchr(filter, ';')) { if (*filter == '.') { filter++; } len = Q_concat(fullpath, sizeof(fullpath), path, "\\*.", filter, NULL); filter = NULL; // do not check it later } else { len = Q_concat(fullpath, sizeof(fullpath), path, "\\*", NULL); } if (len >= sizeof(fullpath)) { return; } // format path to windows style // done on the first run only if (!depth) { FS_ReplaceSeparators(fullpath, '\\'); } handle = FindFirstFileA(fullpath, &data); if (handle == INVALID_HANDLE_VALUE) { return; } // make it point right after the slash pathlen = strlen(path) + 1; do { if (!strcmp(data.cFileName, ".") || !strcmp(data.cFileName, "..")) { continue; // ignore special entries } // construct full path len = strlen(data.cFileName); if (pathlen + len >= sizeof(fullpath)) { continue; } memcpy(fullpath + pathlen, data.cFileName, len + 1); if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { mask = FS_SEARCH_DIRSONLY; } else { mask = 0; } // pattern search implies recursive search if ((flags & FS_SEARCH_BYFILTER) && mask && depth < MAX_LISTED_DEPTH) { Sys_ListFiles_r(fullpath, filter, flags, baselen, count_p, files, depth + 1); // re-check count if (*count_p >= MAX_LISTED_FILES) { break; } } // check type if ((flags & FS_SEARCH_DIRSONLY) != mask) { continue; } // check filter if (filter) { if (flags & FS_SEARCH_BYFILTER) { if (!FS_WildCmp(filter, fullpath + baselen)) { continue; } } else { if (!FS_ExtCmp(filter, data.cFileName)) { continue; } } } // strip path if (flags & FS_SEARCH_SAVEPATH) { name = fullpath + baselen; } else { name = data.cFileName; } // reformat it back to quake filesystem style FS_ReplaceSeparators(name, '/'); // strip extension if (flags & FS_SEARCH_STRIPEXT) { *COM_FileExtension(name) = 0; if (!*name) { continue; } } // copy info off if (flags & FS_SEARCH_EXTRAINFO) { info = copy_info(name, &data); } else { info = FS_CopyString(name); } files[(*count_p)++] = info; } while (*count_p < MAX_LISTED_FILES && FindNextFileA(handle, &data) != FALSE); FindClose(handle); }
static void Sys_InstallService_f(void) { char servicePath[256]; char serviceName[1024]; SC_HANDLE scm, service; DWORD error, length; char *commandline; if (Cmd_Argc() < 3) { Com_Printf("Usage: %s <servicename> <+command> [...]\n" "Example: %s test +set net_port 27910 +map q2dm1\n", Cmd_Argv(0), Cmd_Argv(0)); return; } scm = OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_ALL_ACCESS); if (!scm) { error = GetLastError(); if (error == ERROR_ACCESS_DENIED) { Com_Printf("Insufficient privileges for opening Service Control Manager.\n"); } else { Com_EPrintf("%#lx opening Service Control Manager.\n", error); } return; } Q_concat(serviceName, sizeof(serviceName), "Q2PRO - ", Cmd_Argv(1), NULL); length = GetModuleFileName(NULL, servicePath, MAX_PATH); if (!length) { error = GetLastError(); Com_EPrintf("%#lx getting module file name.\n", error); goto fail; } commandline = Cmd_RawArgsFrom(2); if (length + strlen(commandline) + 10 > sizeof(servicePath) - 1) { Com_Printf("Oversize service command line.\n"); goto fail; } strcpy(servicePath + length, " -service "); strcpy(servicePath + length + 10, commandline); service = CreateService( scm, serviceName, serviceName, SERVICE_START, SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_IGNORE, servicePath, NULL, NULL, NULL, NULL, NULL); if (!service) { error = GetLastError(); if (error == ERROR_SERVICE_EXISTS || error == ERROR_DUPLICATE_SERVICE_NAME) { Com_Printf("Service already exists.\n"); } else { Com_EPrintf("%#lx creating service.\n", error); } goto fail; } Com_Printf("Service created successfully.\n"); CloseServiceHandle(service); fail: CloseServiceHandle(scm); }
/* ============== SpawnEntities Creates a server's entity / program execution context by parsing textual entity definitions out of an ent file. ============== */ void G_SpawnEntities(const char *mapname, const char *entities, const char *spawnpoint) { edict_t *ent; gclient_t *client; int i; client_persistant_t pers; char *token; char playerskin[MAX_QPATH]; #if USE_SQLITE G_OpenDatabase(); G_LogClients(); #endif gi.FreeTags(TAG_LEVEL); memset(&level, 0, sizeof(level)); memset(g_edicts, 0, game.maxentities * sizeof(g_edicts[0])); Q_strlcpy(level.mapname, mapname, sizeof(level.mapname)); G_LoadScores(); // set client fields on player ents for (i = 0; i < game.maxclients; i++) { ent = &g_edicts[i + 1]; client = &game.clients[i]; ent->client = client; ent->inuse = qfalse; if (!client->pers.connected) { continue; } // clear everything but the persistant data pers = client->pers; memset(client, 0, sizeof(*client)); client->pers = pers; client->edict = ent; client->clientNum = i; client->pers.connected = CONN_CONNECTED; // combine name and skin into a configstring Q_concat(playerskin, sizeof(playerskin), client->pers.netname, "\\", client->pers.skin, NULL); gi.configstring(CS_PLAYERSKINS + i, playerskin); gi.configstring(CS_PLAYERNAMES + i, client->pers.netname); } // parse worldspawn token = COM_Parse(&entities); if (!entities) gi.error("%s: empty entity string", __func__); if (token[0] != '{') gi.error("%s: found %s when expecting {", __func__, token); ent = g_edicts; ED_ParseEdict(&entities, ent); ED_CallSpawn(ent); level.entstring = entities; G_ParseString(); G_FindTeams(); //G_UpdateItemBans(); // find spawnpoints ent = NULL; while ((ent = G_Find(ent, FOFS(classname), "info_player_deathmatch")) != NULL) { level.spawns[level.numspawns++] = ent; if (level.numspawns == MAX_SPAWNS) { break; } } gi.dprintf("%d spawn points\n", level.numspawns); }
void PlayerModel_Load(void) { char scratch[MAX_QPATH]; size_t len; int ndirs = 0; char *dirnames[MAX_PLAYERMODELS]; int i, j; char **list; char *s, *p; int numFiles; playerModelInfo_t *pmi; uis.numPlayerModels = 0; // get a list of directories if (!(list = (char **)FS_ListFiles(NULL, "players/*/tris.md2", FS_SEARCH_BYFILTER | FS_SEARCH_SAVEPATH, &numFiles))) { return; } for (i = 0; i < numFiles; i++) { len = Q_strlcpy(scratch, list[i], sizeof(scratch)); if (len >= sizeof(scratch)) continue; // make short name for the model if (!(s = strchr(scratch, '/'))) continue; s++; if (!(p = strchr(s, '/'))) continue; *p = 0; for (j = 0; j < ndirs; j++) { if (!strcmp(dirnames[j], s)) { break; } } if (j != ndirs) { continue; } dirnames[ndirs++] = UI_CopyString(s); if (ndirs == MAX_PLAYERMODELS) { break; } } FS_FreeList((void **)list); if (!ndirs) { return; } // go through the subdirectories for (i = 0; i < ndirs; i++) { int k, s; char **pcxnames; char **skinnames; int npcxfiles; int nskins = 0; // verify the existence of tris.md2 Q_concat(scratch, sizeof(scratch), "players/", dirnames[i], "/tris.md2", NULL); if (!FS_FileExists(scratch)) { goto skip; } // verify the existence of at least one pcx skin Q_concat(scratch, sizeof(scratch), "players/", dirnames[i], NULL); pcxnames = (char **)FS_ListFiles(scratch, ".pcx", 0, &npcxfiles); if (!pcxnames) { goto skip; } // count valid skins, which consist of a skin with a matching "_i" icon for (k = 0; k < npcxfiles; k++) { if (!strstr(pcxnames[k], "_i.pcx")) { if (IconOfSkinExists(pcxnames[k], pcxnames, npcxfiles)) { nskins++; } } } if (!nskins) { FS_FreeList((void **)pcxnames); goto skip; } skinnames = UI_Malloc(sizeof(char *) * (nskins + 1)); skinnames[nskins] = NULL; // copy the valid skins for (s = 0, k = 0; k < npcxfiles; k++) { if (!strstr(pcxnames[k], "_i.pcx")) { if (IconOfSkinExists(pcxnames[k], pcxnames, npcxfiles)) { COM_StripExtension(scratch, pcxnames[k], sizeof(scratch)); skinnames[s++] = UI_CopyString(scratch); } } } FS_FreeList((void **)pcxnames); // at this point we have a valid player model pmi = &uis.pmi[uis.numPlayerModels++]; pmi->nskins = nskins; pmi->skindisplaynames = skinnames; pmi->directory = dirnames[i]; continue; skip: Z_Free(dirnames[i]); } qsort(uis.pmi, uis.numPlayerModels, sizeof(uis.pmi[0]), pmicmpfnc); }