/* Host_Map_f handle a map <servername> command from the console. Active clients are kicked off. */ static void Host_Map_f (void) { char name[MAX_QPATH]; const char *expanded; QFile *f; if (cmd_source != src_command) return; if (Cmd_Argc () > 2) { Sys_Printf ("map <levelname> : continue game on a new level\n"); return; } if (Cmd_Argc () == 1) { Sys_Printf ("map is %s \"%s\" (%s)\n", sv.name, PR_GetString (&sv_pr_state, SVstring (sv.edicts, message)), nice_time (sv.time)); return; } // check to make sure the level exists expanded = va ("maps/%s.bsp", Cmd_Argv (1)); QFS_FOpenFile (expanded, &f); if (!f) { Sys_Printf ("Can't find %s\n", expanded); return; } Qclose (f); cls.demonum = -1; // stop demo loop in case this fails CL_Disconnect (); Host_ShutdownServer (false); cl.loading = true; CL_UpdateScreen (cl.time); svs.serverflags = 0; // haven't completed an episode yet strcpy (name, Cmd_Argv (1)); SV_SpawnServer (name); if (!sv.active) return; if (cls.state != ca_dedicated) { Cmd_ExecuteString ("connect local", src_command); } }
void Host_Main(void) { double time1 = 0; double time2 = 0; double time3 = 0; double cl_timer = 0, sv_timer = 0; double clframetime, deltacleantime, olddirtytime, dirtytime; double wait; int pass1, pass2, pass3, i; char vabuf[1024]; qboolean playing; Host_Init(); realtime = 0; host_dirtytime = Sys_DirtyTime(); for (;;) { if (setjmp(host_abortframe)) { SCR_ClearLoadingScreen(false); continue; // something bad happened, or the server disconnected } olddirtytime = host_dirtytime; dirtytime = Sys_DirtyTime(); deltacleantime = dirtytime - olddirtytime; if (deltacleantime < 0) { // warn if it's significant if (deltacleantime < -0.01) Con_Printf("Host_Mingled: time stepped backwards (went from %f to %f, difference %f)\n", olddirtytime, dirtytime, deltacleantime); deltacleantime = 0; } else if (deltacleantime >= 1800) { Con_Printf("Host_Mingled: time stepped forward (went from %f to %f, difference %f)\n", olddirtytime, dirtytime, deltacleantime); deltacleantime = 0; } realtime += deltacleantime; host_dirtytime = dirtytime; cl_timer += deltacleantime; sv_timer += deltacleantime; if (!svs.threaded) { svs.perf_acc_realtime += deltacleantime; // Look for clients who have spawned playing = false; for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++) if(host_client->begun) if(host_client->netconnection) playing = true; if(sv.time < 10) { // don't accumulate time for the first 10 seconds of a match // so things can settle svs.perf_acc_realtime = svs.perf_acc_sleeptime = svs.perf_acc_lost = svs.perf_acc_offset = svs.perf_acc_offset_squared = svs.perf_acc_offset_max = svs.perf_acc_offset_samples = 0; } else if(svs.perf_acc_realtime > 5) { svs.perf_cpuload = 1 - svs.perf_acc_sleeptime / svs.perf_acc_realtime; svs.perf_lost = svs.perf_acc_lost / svs.perf_acc_realtime; if(svs.perf_acc_offset_samples > 0) { svs.perf_offset_max = svs.perf_acc_offset_max; svs.perf_offset_avg = svs.perf_acc_offset / svs.perf_acc_offset_samples; svs.perf_offset_sdev = sqrt(svs.perf_acc_offset_squared / svs.perf_acc_offset_samples - svs.perf_offset_avg * svs.perf_offset_avg); } if(svs.perf_lost > 0 && developer_extra.integer) if(playing) // only complain if anyone is looking Con_DPrintf("Server can't keep up: %s\n", Host_TimingReport(vabuf, sizeof(vabuf))); svs.perf_acc_realtime = svs.perf_acc_sleeptime = svs.perf_acc_lost = svs.perf_acc_offset = svs.perf_acc_offset_squared = svs.perf_acc_offset_max = svs.perf_acc_offset_samples = 0; } } if (slowmo.value < 0.00001 && slowmo.value != 0) Cvar_SetValue("slowmo", 0); if (host_framerate.value < 0.00001 && host_framerate.value != 0) Cvar_SetValue("host_framerate", 0); // keep the random time dependent, but not when playing demos/benchmarking if(!*sv_random_seed.string && !cls.demoplayback) rand(); // get new key events Key_EventQueue_Unblock(); SndSys_SendKeyEvents(); Sys_SendKeyEvents(); NetConn_UpdateSockets(); Log_DestBuffer_Flush(); // receive packets on each main loop iteration, as the main loop may // be undersleeping due to select() detecting a new packet if (sv.active && !svs.threaded) NetConn_ServerFrame(); Curl_Run(); // check for commands typed to the host Host_GetConsoleCommands(); // when a server is running we only execute console commands on server frames // (this mainly allows frikbot .way config files to work properly by staying in sync with the server qc) // otherwise we execute them on client frames if (sv.active ? sv_timer > 0 : cl_timer > 0) { // process console commands // R_TimeReport("preconsole"); CL_VM_PreventInformationLeaks(); Cbuf_Frame(); // R_TimeReport("console"); } //Con_Printf("%6.0f %6.0f\n", cl_timer * 1000000.0, sv_timer * 1000000.0); // if the accumulators haven't become positive yet, wait a while if (cls.state == ca_dedicated) wait = sv_timer * -1000000.0; else if (!sv.active || svs.threaded) wait = cl_timer * -1000000.0; else wait = max(cl_timer, sv_timer) * -1000000.0; if (!cls.timedemo && wait >= 1) { double time0, delta; if(host_maxwait.value <= 0) wait = min(wait, 1000000.0); else wait = min(wait, host_maxwait.value * 1000.0); if(wait < 1) wait = 1; // because we cast to int time0 = Sys_DirtyTime(); if (sv_checkforpacketsduringsleep.integer && !sys_usenoclockbutbenchmark.integer && !svs.threaded) { NetConn_SleepMicroseconds((int)wait); if (cls.state != ca_dedicated) NetConn_ClientFrame(); // helps server browser get good ping values // TODO can we do the same for ServerFrame? Probably not. } else Sys_Sleep((int)wait); delta = Sys_DirtyTime() - time0; if (delta < 0 || delta >= 1800) delta = 0; if (!svs.threaded) svs.perf_acc_sleeptime += delta; // R_TimeReport("sleep"); continue; } // limit the frametime steps to no more than 100ms each if (cl_timer > 0.1) cl_timer = 0.1; if (sv_timer > 0.1) { if (!svs.threaded) svs.perf_acc_lost += (sv_timer - 0.1); sv_timer = 0.1; } R_TimeReport("---"); //------------------- // // server operations // //------------------- // limit the frametime steps to no more than 100ms each if (sv.active && sv_timer > 0 && !svs.threaded) { // execute one or more server frames, with an upper limit on how much // execution time to spend on server frames to avoid freezing the game if // the server is overloaded, this execution time limit means the game will // slow down if the server is taking too long. int framecount, framelimit = 1; double advancetime, aborttime = 0; float offset; prvm_prog_t *prog = SVVM_prog; // run the world state // don't allow simulation to run too fast or too slow or logic glitches can occur // stop running server frames if the wall time reaches this value if (sys_ticrate.value <= 0) advancetime = sv_timer; else if (cl.islocalgame && !sv_fixedframeratesingleplayer.integer) { // synchronize to the client frametime, but no less than 10ms and no more than 100ms advancetime = bound(0.01, cl_timer, 0.1); } else { advancetime = sys_ticrate.value; // listen servers can run multiple server frames per client frame framelimit = cl_maxphysicsframesperserverframe.integer; aborttime = Sys_DirtyTime() + 0.1; } if(slowmo.value > 0 && slowmo.value < 1) advancetime = min(advancetime, 0.1 / slowmo.value); else advancetime = min(advancetime, 0.1); if(advancetime > 0) { offset = Sys_DirtyTime() - dirtytime;if (offset < 0 || offset >= 1800) offset = 0; offset += sv_timer; ++svs.perf_acc_offset_samples; svs.perf_acc_offset += offset; svs.perf_acc_offset_squared += offset * offset; if(svs.perf_acc_offset_max < offset) svs.perf_acc_offset_max = offset; } // only advance time if not paused // the game also pauses in singleplayer when menu or console is used sv.frametime = advancetime * slowmo.value; if (host_framerate.value) sv.frametime = host_framerate.value; if (sv.paused || (cl.islocalgame && (key_dest != key_game || key_consoleactive || cl.csqc_paused))) sv.frametime = 0; for (framecount = 0;framecount < framelimit && sv_timer > 0;framecount++) { sv_timer -= advancetime; // move things around and think unless paused if (sv.frametime) SV_Physics(); // if this server frame took too long, break out of the loop if (framelimit > 1 && Sys_DirtyTime() >= aborttime) break; } R_TimeReport("serverphysics"); // send all messages to the clients SV_SendClientMessages(); if (sv.paused == 1 && realtime > sv.pausedstart && sv.pausedstart > 0) { prog->globals.fp[OFS_PARM0] = realtime - sv.pausedstart; PRVM_serverglobalfloat(time) = sv.time; prog->ExecuteProgram(prog, PRVM_serverfunction(SV_PausedTic), "QC function SV_PausedTic is missing"); } // send an heartbeat if enough time has passed since the last one NetConn_Heartbeat(0); R_TimeReport("servernetwork"); } else if (!svs.threaded) { // don't let r_speeds display jump around R_TimeReport("serverphysics"); R_TimeReport("servernetwork"); } //------------------- // // client operations // //------------------- if (cls.state != ca_dedicated && (cl_timer > 0 || cls.timedemo || ((vid_activewindow ? cl_maxfps : cl_maxidlefps).value < 1))) { R_TimeReport("---"); Collision_Cache_NewFrame(); R_TimeReport("photoncache"); // decide the simulation time if (cls.capturevideo.active) { //*** if (cls.capturevideo.realtime) clframetime = cl.realframetime = max(cl_timer, 1.0 / cls.capturevideo.framerate); else { clframetime = 1.0 / cls.capturevideo.framerate; cl.realframetime = max(cl_timer, clframetime); } } else if (vid_activewindow && cl_maxfps.value >= 1 && !cls.timedemo) { clframetime = cl.realframetime = max(cl_timer, 1.0 / cl_maxfps.value); // when running slow, we need to sleep to keep input responsive wait = bound(0, cl_maxfps_alwayssleep.value * 1000, 100000); if (wait > 0) Sys_Sleep((int)wait); } else if (!vid_activewindow && cl_maxidlefps.value >= 1 && !cls.timedemo) clframetime = cl.realframetime = max(cl_timer, 1.0 / cl_maxidlefps.value); else clframetime = cl.realframetime = cl_timer; // apply slowmo scaling clframetime *= cl.movevars_timescale; // scale playback speed of demos by slowmo cvar if (cls.demoplayback) { clframetime *= slowmo.value; // if demo playback is paused, don't advance time at all if (cls.demopaused) clframetime = 0; } else { // host_framerate overrides all else if (host_framerate.value) clframetime = host_framerate.value; if (cl.paused || (cl.islocalgame && (key_dest != key_game || key_consoleactive || cl.csqc_paused))) clframetime = 0; } if (cls.timedemo) clframetime = cl.realframetime = cl_timer; // deduct the frame time from the accumulator cl_timer -= cl.realframetime; cl.oldtime = cl.time; cl.time += clframetime; // update video if (host_speeds.integer) time1 = Sys_DirtyTime(); R_TimeReport("pre-input"); // Collect input into cmd CL_Input(); R_TimeReport("input"); // check for new packets NetConn_ClientFrame(); // read a new frame from a demo if needed CL_ReadDemoMessage(); R_TimeReport("clientnetwork"); // now that packets have been read, send input to server CL_SendMove(); R_TimeReport("sendmove"); // update client world (interpolate entities, create trails, etc) CL_UpdateWorld(); R_TimeReport("lerpworld"); CL_Video_Frame(); R_TimeReport("client"); CL_UpdateScreen(); CL_MeshEntities_Reset(); R_TimeReport("render"); if (host_speeds.integer) time2 = Sys_DirtyTime(); // update audio if(cl.csqc_usecsqclistener) { S_Update(&cl.csqc_listenermatrix); cl.csqc_usecsqclistener = false; } else S_Update(&r_refdef.view.matrix); CDAudio_Update(); R_TimeReport("audio"); // reset gathering of mouse input in_mouse_x = in_mouse_y = 0; if (host_speeds.integer) { pass1 = (int)((time1 - time3)*1000000); time3 = Sys_DirtyTime(); pass2 = (int)((time2 - time1)*1000000); pass3 = (int)((time3 - time2)*1000000); Con_Printf("%6ius total %6ius server %6ius gfx %6ius snd\n", pass1+pass2+pass3, pass1, pass2, pass3); } } #if MEMPARANOIA Mem_CheckSentinelsGlobal(); #else if (developer_memorydebug.integer) Mem_CheckSentinelsGlobal(); #endif // if there is some time remaining from this frame, reset the timers if (cl_timer >= 0) cl_timer = 0; if (sv_timer >= 0) { if (!svs.threaded) svs.perf_acc_lost += sv_timer; sv_timer = 0; } host_framecount++; } }
/* ==================== Interactive line editing and console scrollback ==================== */ static void Key_Console (int key, int unicode) { // LordHavoc: copied most of this from Q2 to improve keyboard handling switch (key) { case K_KP_SLASH: key = '/'; break; case K_KP_MINUS: key = '-'; break; case K_KP_PLUS: key = '+'; break; case K_KP_HOME: key = '7'; break; case K_KP_UPARROW: key = '8'; break; case K_KP_PGUP: key = '9'; break; case K_KP_LEFTARROW: key = '4'; break; case K_KP_5: key = '5'; break; case K_KP_RIGHTARROW: key = '6'; break; case K_KP_END: key = '1'; break; case K_KP_DOWNARROW: key = '2'; break; case K_KP_PGDN: key = '3'; break; case K_KP_INS: key = '0'; break; case K_KP_DEL: key = '.'; break; } if ((key == 'v' && keydown[K_CTRL]) || ((key == K_INS || key == K_KP_INS) && keydown[K_SHIFT])) { char *cbd, *p; if ((cbd = Sys_GetClipboardData()) != 0) { int i; #if 1 p = cbd; while (*p) { if (*p == '\r' && *(p+1) == '\n') { *p++ = ';'; *p++ = ' '; } else if (*p == '\n' || *p == '\r' || *p == '\b') *p++ = ';'; p++; } #else strtok(cbd, "\n\r\b"); #endif i = (int)strlen(cbd); if (i + key_linepos >= MAX_INPUTLINE) i= MAX_INPUTLINE - key_linepos - 1; if (i > 0) { // terencehill: insert the clipboard text between the characters of the line /* char *temp = (char *) Z_Malloc(MAX_INPUTLINE); cbd[i]=0; temp[0]=0; if ( key_linepos < (int)strlen(key_line) ) strlcpy(temp, key_line + key_linepos, (int)strlen(key_line) - key_linepos +1); key_line[key_linepos] = 0; strlcat(key_line, cbd, sizeof(key_line)); if (temp[0]) strlcat(key_line, temp, sizeof(key_line)); Z_Free(temp); key_linepos += i; */ // blub: I'm changing this to use memmove() like the rest of the code does. cbd[i] = 0; memmove(key_line + key_linepos + i, key_line + key_linepos, sizeof(key_line) - key_linepos - i); memcpy(key_line + key_linepos, cbd, i); key_linepos += i; } Z_Free(cbd); } return; } if (key == 'l' && keydown[K_CTRL]) { Cbuf_AddText ("clear\n"); return; } if (key == 'u' && keydown[K_CTRL]) // like vi/readline ^u: delete currently edited line { // clear line key_line[0] = ']'; key_line[1] = 0; key_linepos = 1; return; } if (key == 'q' && keydown[K_CTRL]) // like zsh ^q: push line to history, don't execute, and clear { // clear line Key_History_Push(); key_line[0] = ']'; key_line[1] = 0; key_linepos = 1; return; } if (key == K_ENTER || key == K_KP_ENTER) { Cbuf_AddText (key_line+1); // skip the ] Cbuf_AddText ("\n"); Key_History_Push(); key_line[0] = ']'; key_line[1] = 0; // EvilTypeGuy: null terminate key_linepos = 1; // force an update, because the command may take some time if (cls.state == ca_disconnected) CL_UpdateScreen (); return; } if (key == K_TAB) { if(keydown[K_CTRL]) // append to the cvar its value { int cvar_len, cvar_str_len, chars_to_move; char k; char cvar[MAX_INPUTLINE]; const char *cvar_str; // go to the start of the variable while(--key_linepos) { k = key_line[key_linepos]; if(k == '\"' || k == ';' || k == ' ' || k == '\'') break; } key_linepos++; // save the variable name in cvar for(cvar_len=0; (k = key_line[key_linepos + cvar_len]) != 0; cvar_len++) { if(k == '\"' || k == ';' || k == ' ' || k == '\'') break; cvar[cvar_len] = k; } if (cvar_len==0) return; cvar[cvar_len] = 0; // go to the end of the cvar key_linepos += cvar_len; // save the content of the variable in cvar_str cvar_str = Cvar_VariableString(cvar); cvar_str_len = strlen(cvar_str); if (cvar_str_len==0) return; // insert space and cvar_str in key_line chars_to_move = strlen(&key_line[key_linepos]); if (key_linepos + 1 + cvar_str_len + chars_to_move < MAX_INPUTLINE) { if (chars_to_move) memmove(&key_line[key_linepos + 1 + cvar_str_len], &key_line[key_linepos], chars_to_move); key_line[key_linepos++] = ' '; memcpy(&key_line[key_linepos], cvar_str, cvar_str_len); key_linepos += cvar_str_len; key_line[key_linepos + chars_to_move] = 0; } else Con_Printf("Couldn't append cvar value, edit line too long.\n"); return; } // Enhanced command completion // by EvilTypeGuy [email protected] // Thanks to Fett, Taniwha Con_CompleteCommandLine(); return; } // Advanced Console Editing by Radix [email protected] // Added/Modified by EvilTypeGuy [email protected] // Enhanced by [515] // Enhanced by terencehill // move cursor to the previous character if (key == K_LEFTARROW || key == K_KP_LEFTARROW) { if (key_linepos < 2) return; if(keydown[K_CTRL]) // move cursor to the previous word { int pos; char k; pos = key_linepos-1; if(pos) // skip all "; ' after the word while(--pos) { k = key_line[pos]; if (!(k == '\"' || k == ';' || k == ' ' || k == '\'')) break; } if(pos) while(--pos) { k = key_line[pos]; if(k == '\"' || k == ';' || k == ' ' || k == '\'') break; } key_linepos = pos + 1; } else if(keydown[K_SHIFT]) // move cursor to the previous character ignoring colors { int pos; size_t inchar = 0; pos = u8_prevbyte(key_line, key_linepos); while (pos) if(pos-1 > 0 && key_line[pos-1] == STRING_COLOR_TAG && isdigit(key_line[pos])) pos-=2; else if(pos-4 > 0 && key_line[pos-4] == STRING_COLOR_TAG && key_line[pos-3] == STRING_COLOR_RGB_TAG_CHAR && isxdigit(key_line[pos-2]) && isxdigit(key_line[pos-1]) && isxdigit(key_line[pos])) pos-=5; else { if(pos-1 > 0 && key_line[pos-1] == STRING_COLOR_TAG && key_line[pos] == STRING_COLOR_TAG) // consider ^^ as a character pos--; pos--; break; } // we need to move to the beginning of the character when in a wide character: u8_charidx(key_line, pos + 1, &inchar); key_linepos = pos + 1 - inchar; } else { key_linepos = u8_prevbyte(key_line, key_linepos); } return; } // delete char before cursor if (key == K_BACKSPACE || (key == 'h' && keydown[K_CTRL])) { if (key_linepos > 1) { int newpos = u8_prevbyte(key_line, key_linepos); strlcpy(key_line + newpos, key_line + key_linepos, sizeof(key_line) + 1 - key_linepos); key_linepos = newpos; } return; } // delete char on cursor if (key == K_DEL || key == K_KP_DEL) { size_t linelen; linelen = strlen(key_line); if (key_linepos < (int)linelen) memmove(key_line + key_linepos, key_line + key_linepos + u8_bytelen(key_line + key_linepos, 1), linelen - key_linepos); return; } // move cursor to the next character if (key == K_RIGHTARROW || key == K_KP_RIGHTARROW) { if (key_linepos >= (int)strlen(key_line)) return; if(keydown[K_CTRL]) // move cursor to the next word { int pos, len; char k; len = (int)strlen(key_line); pos = key_linepos; while(++pos < len) { k = key_line[pos]; if(k == '\"' || k == ';' || k == ' ' || k == '\'') break; } if (pos < len) // skip all "; ' after the word while(++pos < len) { k = key_line[pos]; if (!(k == '\"' || k == ';' || k == ' ' || k == '\'')) break; } key_linepos = pos; } else if(keydown[K_SHIFT]) // move cursor to the next character ignoring colors { int pos, len; len = (int)strlen(key_line); pos = key_linepos; // go beyond all initial consecutive color tags, if any if(pos < len) while (key_line[pos] == STRING_COLOR_TAG) { if(isdigit(key_line[pos+1])) pos+=2; else if(key_line[pos+1] == STRING_COLOR_RGB_TAG_CHAR && isxdigit(key_line[pos+2]) && isxdigit(key_line[pos+3]) && isxdigit(key_line[pos+4])) pos+=5; else break; } // skip the char if (key_line[pos] == STRING_COLOR_TAG && key_line[pos+1] == STRING_COLOR_TAG) // consider ^^ as a character pos++; pos += u8_bytelen(key_line + pos, 1); // now go beyond all next consecutive color tags, if any if(pos < len) while (key_line[pos] == STRING_COLOR_TAG) { if(isdigit(key_line[pos+1])) pos+=2; else if(key_line[pos+1] == STRING_COLOR_RGB_TAG_CHAR && isxdigit(key_line[pos+2]) && isxdigit(key_line[pos+3]) && isxdigit(key_line[pos+4])) pos+=5; else break; } key_linepos = pos; } else key_linepos += u8_bytelen(key_line + key_linepos, 1); return; } if (key == K_INS || key == K_KP_INS) // toggle insert mode { key_insert ^= 1; return; } // End Advanced Console Editing if (key == K_UPARROW || key == K_KP_UPARROW || (key == 'p' && keydown[K_CTRL])) { Key_History_Up(); return; } if (key == K_DOWNARROW || key == K_KP_DOWNARROW || (key == 'n' && keydown[K_CTRL])) { Key_History_Down(); return; } // ~1.0795 = 82/76 using con_textsize 64 76 is height of the char, 6 is the distance between 2 lines if (key == K_PGUP || key == K_KP_PGUP) { if(keydown[K_CTRL]) { con_backscroll += ((vid_conheight.integer >> 2) / con_textsize.integer)-1; } else con_backscroll += ((vid_conheight.integer >> 1) / con_textsize.integer)-3; return; }
static void Host_Loadgame_f (void) { dstring_t *name = 0; QFile *f; char *mapname = 0; script_t *script = 0; plitem_t *game = 0; plitem_t *list; plitem_t *item; char *script_data = 0; int i; int entnum; int count; int version; float spawn_parms[NUM_SPAWN_PARMS]; if (cmd_source != src_command) goto end; if (Cmd_Argc () != 2) { Sys_Printf ("load <savename> : load a game\n"); goto end; } cls.demonum = -1; // stop demo loop in case this fails name = dstring_newstr (); dsprintf (name, "%s/%s", qfs_gamedir->dir.def, Cmd_Argv (1)); QFS_DefaultExtension (name, ".sav"); cl.loading = true; CL_UpdateScreen (cl.time); Sys_Printf ("Loading game from %s...\n", name->str); f = QFS_Open (name->str, "rz"); if (!f) { Sys_Printf ("ERROR: couldn't open.\n"); goto end; } script_data = malloc (Qfilesize (f) + 1); i = Qread (f, script_data, Qfilesize (f)); script_data[i] = 0; Qclose (f); script = Script_New (); script->single = ""; // disable {}()': lexing Script_Start (script, name->str, script_data); Script_GetToken (script, 1); if (strequal (script->token->str, PACKAGE_NAME)) { if (!Script_TokenAvailable (script, 1)) { Sys_Printf ("Unexpected EOF reading %s\n", name->str); goto end; } game = PL_GetPropertyList (script->p); } else { sscanf (script->token->str, "%i", &version); if (version != SAVEGAME_VERSION) { Sys_Printf ("Savegame is version %i, not %i\n", version, SAVEGAME_VERSION); goto end; } game = convert_to_game_dict (script); } item = PL_ObjectForKey (game, "spawn_parms"); for (i = 0; i < NUM_SPAWN_PARMS; i++) { if (i >= PL_A_NumObjects (item)) break; spawn_parms[i] = atof (PL_String (PL_ObjectAtIndex (item, i))); } current_skill = atoi (PL_String (PL_ObjectForKey (game, "current_skill"))); Cvar_SetValue (skill, current_skill); mapname = strdup (PL_String (PL_ObjectForKey (game, "name"))); CL_Disconnect_f (); SV_SpawnServer (mapname); if (!sv.active) { Sys_Printf ("Couldn't load map %s\n", mapname); goto end; } sv.paused = true; // pause until all clients connect sv.loadgame = true; list = PL_ObjectForKey (game, "lightstyles"); for (i = 0; i < MAX_LIGHTSTYLES; i++) { const char *style; char *str; if (i >= PL_A_NumObjects (list)) break; item = PL_ObjectAtIndex (list, i); style = PL_String (item); sv.lightstyles[i] = str = Hunk_Alloc (strlen (style) + 1); strcpy (str, style); } ED_InitGlobals (&sv_pr_state, PL_ObjectForKey (game, "globals")); list = PL_ObjectForKey (game, "entities"); entnum = 0; count = PL_A_NumObjects (list); if (count > sv.max_edicts) Host_Error ("too many entities in saved game. adjust max_edicts\n"); for (entnum = 0; entnum < count; entnum++) { plitem_t *entity = PL_ObjectAtIndex (list, entnum); edict_t *ent = EDICT_NUM (&sv_pr_state, entnum); memset (&ent->v, 0, sv_pr_state.progs->entityfields * 4); ent->free = false; ED_InitEntity (&sv_pr_state, entity, ent); // link it into the bsp tree if (!ent->free) SV_LinkEdict (ent, false); } sv.num_edicts = entnum; sv.time = atof (PL_String (PL_ObjectForKey (game, "time"))); for (i = 0; i < NUM_SPAWN_PARMS; i++) svs.clients->spawn_parms[i] = spawn_parms[i]; if (cls.state != ca_dedicated) { CL_EstablishConnection ("local"); Host_Reconnect_f (); } end: if (game) PL_Free (game); if (mapname) free (mapname); if (script) Script_Delete (script); if (script_data) free (script_data); if (name) dstring_delete (name); }