/// <summary> /// CL_ParseServerInfo /// </summary> static void ParseServerInfo() { Con.DPrint("Serverinfo packet received.\n"); // // wipe the client_state_t struct // ClearState(); // parse protocol version number int i = Net.Reader.ReadLong(); if (i != Protocol.PROTOCOL_VERSION) { Con.Print("Server returned version {0}, not {1}", i, Protocol.PROTOCOL_VERSION); return; } // parse maxclients cl.maxclients = Net.Reader.ReadByte(); if (cl.maxclients < 1 || cl.maxclients > QDef.MAX_SCOREBOARD) { Con.Print("Bad maxclients ({0}) from server\n", cl.maxclients); return; } cl.scores = new scoreboard_t[cl.maxclients];// Hunk_AllocName (cl.maxclients*sizeof(*cl.scores), "scores"); for (i = 0; i < cl.scores.Length; i++) { cl.scores[i] = new scoreboard_t(); } // parse gametype cl.gametype = Net.Reader.ReadByte(); // parse signon message string str = Net.Reader.ReadString(); cl.levelname = Common.Copy(str, 40); // seperate the printfs so the server message can have a color Con.Print(ConsoleBar); Con.Print("{0}{1}\n", (char)2, str); // // first we go through and touch all of the precache data that still // happens to be in the cache, so precaching something else doesn't // needlessly purge it // // precache models Array.Clear(cl.model_precache, 0, cl.model_precache.Length); int nummodels; string[] model_precache = new string[QDef.MAX_MODELS]; for (nummodels = 1; ; nummodels++) { str = Net.Reader.ReadString(); if (String.IsNullOrEmpty(str)) { break; } if (nummodels == QDef.MAX_MODELS) { Con.Print("Server sent too many model precaches\n"); return; } model_precache[nummodels] = str; Mod.TouchModel(str); } // precache sounds Array.Clear(cl.sound_precache, 0, cl.sound_precache.Length); int numsounds; string[] sound_precache = new string[QDef.MAX_SOUNDS]; for (numsounds = 1; ; numsounds++) { str = Net.Reader.ReadString(); if (String.IsNullOrEmpty(str)) { break; } if (numsounds == QDef.MAX_SOUNDS) { Con.Print("Server sent too many sound precaches\n"); return; } sound_precache[numsounds] = str; Sound.TouchSound(str); } // // now we try to load everything else until a cache allocation fails // for (i = 1; i < nummodels; i++) { cl.model_precache[i] = Mod.ForName(model_precache[i], false); if (cl.model_precache[i] == null) { Con.Print("Model {0} not found\n", model_precache[i]); return; } KeepaliveMessage(); } Sound.BeginPrecaching(); for (i = 1; i < numsounds; i++) { cl.sound_precache[i] = Sound.PrecacheSound(sound_precache[i]); KeepaliveMessage(); } Sound.EndPrecaching(); // local state _Entities[0].model = cl.worldmodel = cl.model_precache[1]; Render.NewMap(); Host.NoClipAngleHack = false; // noclip is turned off at start GC.Collect(); }
/// <summary> /// SV_SpawnServer /// </summary> public static void SpawnServer(string server) { // let's not have any servers with no name if (String.IsNullOrEmpty(Net.HostName)) { Cvar.Set("hostname", "UNNAMED"); } Scr.CenterTimeOff = 0; Con.DPrint("SpawnServer: {0}\n", server); svs.changelevel_issued = false; // now safe to issue another // // tell all connected clients that we are going to a new level // if (sv.active) { SendReconnect(); } // // make cvars consistant // if (Host.IsCoop) { Cvar.Set("deathmatch", 0); } Host.CurrentSkill = (int)(Host.Skill + 0.5); if (Host.CurrentSkill < 0) { Host.CurrentSkill = 0; } if (Host.CurrentSkill > 3) { Host.CurrentSkill = 3; } Cvar.Set("skill", (float)Host.CurrentSkill); // // set up the new server // Host.ClearMemory(); sv.Clear(); sv.name = server; // load progs to get entity field count Progs.LoadProgs(); // allocate server memory sv.max_edicts = QDef.MAX_EDICTS; sv.edicts = new edict_t[sv.max_edicts]; for (int i = 0; i < sv.edicts.Length; i++) { sv.edicts[i] = new edict_t(); } // leave slots at start for clients only sv.num_edicts = svs.maxclients + 1; edict_t ent; for (int i = 0; i < svs.maxclients; i++) { ent = EdictNum(i + 1); svs.clients[i].edict = ent; } sv.state = server_state_t.Loading; sv.paused = false; sv.time = 1.0; sv.modelname = String.Format("maps/{0}.bsp", server); sv.worldmodel = Mod.ForName(sv.modelname, false); if (sv.worldmodel == null) { Con.Print("Couldn't spawn server {0}\n", sv.modelname); sv.active = false; return; } sv.models[1] = sv.worldmodel; // // clear world interaction links // ClearWorld(); sv.sound_precache[0] = String.Empty; sv.model_precache[0] = String.Empty; sv.model_precache[1] = sv.modelname; for (int i = 1; i < sv.worldmodel.numsubmodels; i++) { sv.model_precache[1 + i] = _LocalModels[i]; sv.models[i + 1] = Mod.ForName(_LocalModels[i], false); } // // load the rest of the entities // ent = EdictNum(0); ent.Clear(); ent.v.model = Progs.StringOffset(sv.worldmodel.name); if (ent.v.model == -1) { ent.v.model = Progs.NewString(sv.worldmodel.name); } ent.v.modelindex = 1; // world model ent.v.solid = Solids.SOLID_BSP; ent.v.movetype = Movetypes.MOVETYPE_PUSH; if (Host.IsCoop) { Progs.GlobalStruct.coop = 1; //coop.value; } else { Progs.GlobalStruct.deathmatch = Host.Deathmatch; } int offset = Progs.NewString(sv.name); Progs.GlobalStruct.mapname = offset; // serverflags are for cross level information (sigils) Progs.GlobalStruct.serverflags = svs.serverflags; Progs.LoadFromFile(sv.worldmodel.entities); sv.active = true; // all setup is completed, any further precache statements are errors sv.state = server_state_t.Active; // run two frames to allow everything to settle Host.FrameTime = 0.1; Physics(); Physics(); // create a baseline for more efficient communications CreateBaseline(); // send serverinfo to all connected clients for (int i = 0; i < svs.maxclients; i++) { Host.HostClient = svs.clients[i]; if (Host.HostClient.active) { SendServerInfo(Host.HostClient); } } GC.Collect(); Con.DPrint("Server spawned.\n"); }