/* DESCRIPTION: ED_Alloc // LOCATION: pr_edict.c // PATH: lots // // Allocates a new edict. And by 'allocates' I mean it picks one of // the already allocated edicts. QW's picking was much more involved, // which makes sense as there are plenty of ways to screw with a mod // here due to the memory reuse. */ edict_t * ED_Alloc() { unsigned int i; edict_t * e; for(i = global_svs.allocated_client_slots + 1; i < global_sv.num_edicts; i++) { e = &(global_sv.edicts[i]); if(e->free && (e->freetime < 2 || global_sv.time0c - e->freetime > 0.5 )) { //0.5 was originally zero, I trust QW to be smarter. ED_ClearEdict(e); return(e); } } if(i < global_sv.max_edicts) { global_sv.num_edicts++; e = &(global_sv.edicts[i]); ED_ClearEdict(e); return(e); } Sys_Error("%s: No edicts available.", __FUNCTION__); }
/* ================= ED_Alloc Either finds a free edict, or allocates a new one. Try to avoid reusing an entity that was recently freed, because it can cause the client to think the entity morphed into something else instead of being removed and recreated, which can cause interpolated angles and bad trails. ================= */ edict_t *ED_Alloc (void) { int i; edict_t *e; for ( i=MAX_CLIENTS+1 ; i<sv.num_edicts ; i++) { e = EDICT_NUM(i); // the first couple seconds of server time can involve a lot of // freeing and allocating, so relax the replacement policy if (e->e->free && ( e->e->freetime < 2 || sv.time - e->e->freetime > 0.5 ) ) { ED_ClearEdict (e); return e; } } if (i == MAX_EDICTS) { Con_Printf ("WARNING: ED_Alloc: no free edicts\n"); // step on whatever is the last edict e = EDICT_NUM(--i); SV_UnlinkEdict(e); } else sv.num_edicts++; e = EDICT_NUM(i); ED_ClearEdict (e); return e; }
/* ================= ED_Alloc Either finds a free edict, or allocates a new_ptr one. Try to avoid reusing an entity that was recently freed, because it can cause the client to think the entity morphed into something else instead of being removed and recreated, which can cause interpolated angles and bad trails. ================= */ edict_t *ED_Alloc (void) { int i; edict_t *e; for ( i=svs.maxclients+1 ; i<sv.num_edicts ; i++) { e = EDICT_NUM(i); // the first couple seconds of server time can involve a lot of // freeing and allocating, so relax the replacement policy if (e->free && ( e->freetime < 2 || sv.time - e->freetime > 0.5 ) ) { ED_ClearEdict (e); return e; } } if (i == MAX_EDICTS) Sys_Error ("ED_Alloc: no free edicts"); sv.num_edicts++; e = EDICT_NUM(i); ED_ClearEdict (e); return e; }
edict_t *ED_Alloc( int iForceEdictIndex ) { if ( iForceEdictIndex >= 0 ) { if ( iForceEdictIndex >= sv.num_edicts ) { Warning( "ED_Alloc( %d ) - invalid edict index specified.", iForceEdictIndex ); return NULL; } edict_t *e = &sv.edicts[iForceEdictIndex]; if ( e->IsFree() ) { ED_ClearEdict( e ); return e; } else { return NULL; } } // Check the free list first. edict_t *pEdict = sv.edicts + sv.GetMaxClients() + 1; for ( int i=sv.GetMaxClients()+1; i < sv.num_edicts; i++ ) { if ( pEdict->IsFree() && (pEdict->freetime < 2 || sv.GetTime() - pEdict->freetime >= EDICT_FREETIME) ) { // If we have no freetime, we've had AllowImmediateReuse() called. We need // to explicitly delete this old entity. if ( pEdict->freetime == 0 && sv_useexplicitdelete.GetBool() ) { //Warning("ADDING SLOT to snapshot: %d\n", i ); framesnapshotmanager->AddExplicitDelete( i ); } ED_ClearEdict( pEdict ); return pEdict; } pEdict++; } // Allocate a new edict. if ( sv.num_edicts >= sv.max_edicts ) { AssertMsg( 0, "Can't allocate edict" ); if ( sv.max_edicts == 0 ) Sys_Error( "ED_Alloc: No edicts yet" ); Sys_Error ("ED_Alloc: no free edicts"); } // Do this before clearing since clear now needs to call back into the edict to deduce the index so can get the changeinfo data in the parallel structure sv.num_edicts++; ED_ClearEdict( pEdict ); return pEdict; }
/* ================= ED_Alloc Either finds a free edict, or allocates a new one. Try to avoid reusing an entity that was recently freed, because it can cause the client to think the entity morphed into something else instead of being removed and recreated, which can cause interpolated angles and bad trails. ================= */ edict_t *ED_Alloc (void) { int i; edict_t *e; for (i = svs.maxclients + 1; i < sv.num_edicts; i++) { e = EDICT_NUM(i); // the first couple seconds of server time can involve a lot of // freeing and allocating, so relax the replacement policy if (e->free && ( e->freetime < 2 || sv.time - e->freetime > 0.5 ) ) { ED_ClearEdict (e); return e; } } if (i == sv.max_edicts) //johnfitz -- use sv.max_edicts instead of MAX_EDICTS Host_Error ("ED_Alloc: no free edicts (max_edicts is %i)", sv.max_edicts); sv.num_edicts++; e = EDICT_NUM(i); memset(e, 0, pr_edict_size); // ericw -- switched sv.edicts to malloc(), so we are accessing uninitialized memory and must fully zero it, not just ED_ClearEdict return e; }
/* ================== SVC_DirectConnect A connection request that did not come from the master ================== */ static void SVC_DirectConnect (void) { char userinfo[1024]; static int userid; netadr_t adr; int i; client_t *cl, *newcl; client_t temp; edict_t *ent; int edictnum; const char *s; int clients, spectators; qboolean spectator; q_strlcpy (userinfo, Cmd_Argv(2), sizeof(userinfo)); // check for password or spectator_password s = Info_ValueForKey (userinfo, "spectator"); if (s[0] && strcmp(s, "0")) { if (spectator_password.string[0] && q_strcasecmp(spectator_password.string, "none") && strcmp(spectator_password.string, s) ) { // failed Con_Printf ("%s:spectator password failed\n", NET_AdrToString (net_from)); Netchan_OutOfBandPrint (net_from, "%c\nrequires a spectator password\n\n", A2C_PRINT); return; } Info_SetValueForStarKey (userinfo, "*spectator", "1", MAX_INFO_STRING); spectator = true; Info_RemoveKey (userinfo, "spectator"); // remove passwd } else { s = Info_ValueForKey (userinfo, "password"); if (password.string[0] && q_strcasecmp(password.string, "none") && strcmp(password.string, s) ) { Con_Printf ("%s:password failed\n", NET_AdrToString (net_from)); Netchan_OutOfBandPrint (net_from, "%c\nserver requires a password\n\n", A2C_PRINT); return; } spectator = false; Info_RemoveKey (userinfo, "password"); // remove passwd } adr = net_from; userid++; // so every client gets a unique id newcl = &temp; memset (newcl, 0, sizeof(client_t)); newcl->userid = userid; newcl->portals = atoi(Cmd_Argv(1)); // works properly if (!sv_highchars.integer) { byte *p, *q; for (p = (byte *)newcl->userinfo, q = (byte *)userinfo; *q && p < (byte *)newcl->userinfo + sizeof(newcl->userinfo)-1; q++) { if (*q > 31 && *q <= 127) *p++ = *q; } } else { q_strlcpy (newcl->userinfo, userinfo, sizeof(newcl->userinfo)); } // if there is already a slot for this ip, drop it for (i = 0, cl = svs.clients; i < MAX_CLIENTS; i++, cl++) { if (cl->state == cs_free) continue; if (NET_CompareAdr (adr, cl->netchan.remote_address)) { Con_Printf ("%s:reconnect\n", NET_AdrToString (adr)); SV_DropClient (cl); break; } } // count up the clients and spectators clients = 0; spectators = 0; for (i = 0, cl = svs.clients; i < MAX_CLIENTS; i++, cl++) { if (cl->state == cs_free) continue; if (cl->spectator) spectators++; else clients++; } // if at server limits, refuse connection if (maxclients.integer > MAX_CLIENTS) Cvar_SetValue ("maxclients", MAX_CLIENTS); if (maxspectators.integer > MAX_CLIENTS) Cvar_SetValue ("maxspectators", MAX_CLIENTS); if (maxspectators.integer + maxclients.integer > MAX_CLIENTS) Cvar_SetValue ("maxspectators", MAX_CLIENTS - maxspectators.integer + maxclients.integer); if ( (spectator && spectators >= maxspectators.integer) || (!spectator && clients >= maxclients.integer) ) { Con_Printf ("%s:full connect\n", NET_AdrToString (adr)); Netchan_OutOfBandPrint (adr, "%c\nserver is full\n\n", A2C_PRINT); return; } // find a client slot newcl = NULL; for (i = 0, cl = svs.clients; i < MAX_CLIENTS; i++, cl++) { if (cl->state == cs_free) { newcl = cl; break; } } if (!newcl) { Con_Printf ("WARNING: miscounted available clients\n"); return; } // build a new connection // accept the new client // this is the only place a client_t is ever initialized *newcl = temp; Netchan_OutOfBandPrint (adr, "%c", S2C_CONNECTION ); edictnum = (newcl-svs.clients)+1; Netchan_Setup (&newcl->netchan, adr); newcl->state = cs_connected; SZ_Init (&newcl->datagram, newcl->datagram_buf, sizeof(newcl->datagram_buf)); newcl->datagram.allowoverflow = true; // spectator mode can ONLY be set at join time newcl->spectator = spectator; ent = EDICT_NUM(edictnum); newcl->edict = ent; ED_ClearEdict (ent); // parse some info from the info strings SV_ExtractFromUserinfo (newcl); // JACK: Init the floodprot stuff. for (i = 0; i < 10; i++) newcl->whensaid[i] = 0.0; newcl->whensaidhead = 0; newcl->lockedtill = 0; // call the progs to get default spawn parms for the new client PR_ExecuteProgram (pr_global_struct->SetNewParms); for (i = 0; i < NUM_SPAWN_PARMS; i++) newcl->spawn_parms[i] = (&pr_global_struct->parm1)[i]; if (newcl->spectator) Con_Printf ("Spectator %s connected\n", newcl->name); else Con_DPrintf ("Client %s connected\n", newcl->name); }
void SV_LoadGame_f (void) { extern cvar_t sv_progtype; char name[MAX_OSPATH], mapname[MAX_QPATH], str[32 * 1024], *start; FILE *f; float time, tfloat, spawn_parms[NUM_SPAWN_PARMS]; edict_t *ent; int entnum, version, r; unsigned int i; if (Cmd_Argc() != 2) { Con_Printf ("Usage: %s <savename> : load a game\n", Cmd_Argv(0)); return; } snprintf (name, sizeof (name), "%s/save/%s", fs_gamedir, Cmd_Argv(1)); COM_DefaultExtension (name, ".sav"); Con_Printf ("Loading game from %s...\n", name); if (!(f = fopen (name, "rb"))) { Con_Printf ("ERROR: couldn't open.\n"); return; } if (fscanf (f, "%i\n", &version) != 1) { fclose (f); Con_Printf ("Error reading savegame data\n"); return; } if (version != SAVEGAME_VERSION) { fclose (f); Con_Printf ("Savegame is version %i, not %i\n", version, SAVEGAME_VERSION); return; } if (fscanf (f, "%s\n", str) != 1) { fclose (f); Con_Printf ("Error reading savegame data\n"); return; } for (i = 0; i < NUM_SPAWN_PARMS; i++) { if (fscanf (f, "%f\n", &spawn_parms[i]) != 1) { fclose (f); Con_Printf ("Error reading savegame data\n"); return; } } // this silliness is so we can load 1.06 save files, which have float skill values if (fscanf (f, "%f\n", &tfloat) != 1) { fclose (f); Con_Printf ("Error reading savegame data\n"); return; } current_skill = (int)(tfloat + 0.1); Cvar_Set (&skill, va("%i", current_skill)); Cvar_SetValue (&deathmatch, 0); Cvar_SetValue (&coop, 0); Cvar_SetValue (&teamplay, 0); Cvar_SetValue (&maxclients, 1); Cvar_Set (&sv_progsname, "spprogs"); // force progsname #ifdef USE_PR2 Cvar_Set (&sv_progtype, "0"); // force .dat #endif if (fscanf (f, "%s\n", mapname) != 1) { fclose (f); Con_Printf ("Error reading savegame data\n"); return; } if (fscanf (f, "%f\n", &time) != 1) { fclose (f); Con_Printf ("Error reading savegame data\n"); return; } #ifndef SERVERONLY Host_EndGame(); CL_BeginLocalConnection (); #endif SV_SpawnServer (mapname, false); if (sv.state != ss_active) { Con_Printf ("Couldn't load map\n"); fclose (f); return; } // load the light styles for (i = 0; i < MAX_LIGHTSTYLES; i++) { if (fscanf (f, "%s\n", str) != 1) { Con_Printf("Couldn't read lightstyles\n"); fclose (f); return; } sv.lightstyles[i] = (char *) Hunk_Alloc (strlen(str) + 1); strlcpy (sv.lightstyles[i], str, strlen(str) + 1); } // pause until all clients connect if (!(sv.paused & 1)) SV_TogglePause (NULL, 1); sv.loadgame = true; // load the edicts out of the savegame file entnum = -1; // -1 is the globals while (!feof(f)) { for (i = 0; i < sizeof(str) - 1; i++) { r = fgetc (f); if (r == EOF || !r) break; str[i] = r; if (r == '}') { i++; break; } } if (i == sizeof(str)-1) Host_Error ("Loadgame buffer overflow"); str[i] = 0; start = str; start = COM_Parse(str); if (!com_token[0]) break; // end of file if (strcmp(com_token,"{")) Host_Error ("First token isn't a brace"); if (entnum == -1) { // parse the global vars ED_ParseGlobals (start); } else { // parse an edict ent = EDICT_NUM(entnum); ED_ClearEdict (ent); // FIXME: we also clear world edict here, is it OK? ED_ParseEdict (start, ent); // link it into the bsp tree if (!ent->e->free) SV_LinkEdict (ent, false); } entnum++; } sv.num_edicts = entnum; sv.time = time; fclose (f); for (i = 0; i < NUM_SPAWN_PARMS; i++) svs.clients->spawn_parms[i] = spawn_parms[i]; }