// _Host_Frame // //Runs all active servers static void InternalFrame(double time) { // keep the random time dependent Sys.Random(); // decide the simulation time if (!FilterTime(time)) { return; // don't run too fast, or packets will flood out } // get new key events Sys.SendKeyEvents(); // allow mice or other external controllers to add commands Input.Commands(); // process console commands Cbuf.Execute(); Net.Poll(); // if running the server locally, make intentions now if (Server.sv.active) { Client.SendCmd(); } //------------------- // // server operations // //------------------- // check for commands typed to the host GetConsoleCommands(); if (Server.sv.active) { ServerFrame(); } //------------------- // // client operations // //------------------- // if running the server remotely, send intentions now after // the incoming messages have been read if (!Server.sv.active) { Client.SendCmd(); } _Time += FrameTime; // fetch results from server if (Client.cls.state == cactive_t.ca_connected) { Client.ReadFromServer(); } // update video if (_Speeds.Value != 0) { _Time1 = Sys.GetFloatTime(); } Scr.UpdateScreen(); if (_Speeds.Value != 0) { _Time2 = Sys.GetFloatTime(); } // update audio if (Client.cls.signon == Client.SIGNONS) { Sound.Update(ref Render.Origin, ref Render.ViewPn, ref Render.ViewRight, ref Render.ViewUp); Client.DecayLights(); } else { Sound.Update(ref Common.ZeroVector, ref Common.ZeroVector, ref Common.ZeroVector, ref Common.ZeroVector); } CDAudio.Update(); if (_Speeds.Value != 0) { int pass1 = (int)((_Time1 - _Time3) * 1000); _Time3 = Sys.GetFloatTime(); int pass2 = (int)((_Time2 - _Time1) * 1000); int pass3 = (int)((_Time3 - _Time2) * 1000); Con.Print("{0,3} tot {1,3} server {2,3} gfx {3,3} snd\n", pass1 + pass2 + pass3, pass1, pass2, pass3); } _FrameCount++; }
/// <summary> /// SCR_EndLoadingPlaque /// </summary> public static void EndLoadingPlaque() { Scr.IsDisabledForLoading = false; Scr.FullUpdate = 0; Con.ClearNotify(); }
/// <summary> /// Host_ShutdownServer /// This only happens at the end of a game, not between levels /// </summary> public static void ShutdownServer(bool crash) { if (!Server.IsActive) { return; } Server.sv.active = false; // stop all client sounds immediately if (Client.cls.state == cactive_t.ca_connected) { Client.Disconnect(); } // flush any pending messages - like the score!!! double start = Sys.GetFloatTime(); int count; do { count = 0; for (int i = 0; i < Server.svs.maxclients; i++) { HostClient = Server.svs.clients[i]; if (HostClient.active && !HostClient.message.IsEmpty) { if (Net.CanSendMessage(HostClient.netconnection)) { Net.SendMessage(HostClient.netconnection, HostClient.message); HostClient.message.Clear(); } else { Net.GetMessage(HostClient.netconnection); count++; } } } if ((Sys.GetFloatTime() - start) > 3.0) { break; } }while (count > 0); // make sure all the clients know we're disconnecting MsgWriter writer = new MsgWriter(4); writer.WriteByte(Protocol.svc_disconnect); count = Net.SendToAll(writer, 5); if (count != 0) { Con.Print("Host_ShutdownServer: NET_SendToAll failed for {0} clients\n", count); } for (int i = 0; i < Server.svs.maxclients; i++) { HostClient = Server.svs.clients[i]; if (HostClient.active) { Server.DropClient(crash); } } // // clear structures // Server.sv.Clear(); for (int i = 0; i < Server.svs.clients.Length; i++) { Server.svs.clients[i].Clear(); } }
/// <summary> /// UDP_Init /// </summary> public bool Init() { _IsInitialized = false; if (Common.HasParam("-noudp")) { return(false); } // determine my name string hostName; try { hostName = Dns.GetHostName(); } catch (SocketException se) { Con.DPrint("Cannot get host name: {0}\n", se.Message); return(false); } // if the quake hostname isn't set, set it to the machine name if (Net.HostName == "UNNAMED") { IPAddress addr; if (!IPAddress.TryParse(hostName, out addr)) { int i = hostName.IndexOf('.'); if (i != -1) { hostName = hostName.Substring(0, i); } } Cvar.Set("hostname", hostName); } int i2 = Common.CheckParm("-ip"); if (i2 > 0) { if (i2 < Common.Argc - 1) { string ipaddr = Common.Argv(i2 + 1); if (!IPAddress.TryParse(ipaddr, out _MyAddress)) { Sys.Error("{0} is not a valid IP address!", ipaddr); } Net.MyTcpIpAddress = ipaddr; } else { Sys.Error("Net.Init: you must specify an IP address after -ip"); } } else { _MyAddress = IPAddress.Any; Net.MyTcpIpAddress = "INADDR_ANY"; } _ControlSocket = OpenSocket(0); if (_ControlSocket == null) { Con.Print("TCP/IP: Unable to open control socket\n"); return(false); } _BroadcastAddress = new IPEndPoint(IPAddress.Broadcast, Net.HostPort); _IsInitialized = true; Con.Print("TCP/IP Initialized\n"); return(true); }
// void SCR_UpdateScreen (void); // This is called every frame, and can also be called explicitly to flush // text to the screen. // // WARNING: be very careful calling this from elsewhere, because the refresh // needs almost the entire 256k of stack space! public static void UpdateScreen() { if (BlockDrawing || !_IsInitialized || _InUpdate) { return; } _InUpdate = true; try { if (MainForm.Instance != null) { if ((MainForm.Instance.VSync == VSyncMode.On) != Vid.Wait) { MainForm.Instance.VSync = (Vid.Wait ? VSyncMode.On : VSyncMode.Off); } } _VidDef.numpages = 2 + (int)_glTripleBuffer.Value; CopyTop = false; _CopyEverything = false; if (IsDisabledForLoading) { if ((Host.RealTime - _DisabledTime) > 60) { IsDisabledForLoading = false; Con.Print("Load failed.\n"); } else { return; } } if (!Con.IsInitialized) { return; // not initialized yet } BeginRendering(); // // determine size of refresh window // if (_OldFov != _Fov.Value) { _OldFov = _Fov.Value; _VidDef.recalc_refdef = true; } if (_OldScreenSize != _ViewSize.Value) { _OldScreenSize = _ViewSize.Value; _VidDef.recalc_refdef = true; } if (_VidDef.recalc_refdef) { CalcRefdef(); } // // do 3D refresh drawing, and then update the screen // SetUpToDrawConsole(); View.RenderView(); Set2D(); // // draw any areas not covered by the refresh // Scr.TileClear(); if (_DrawDialog) { Sbar.Draw(); Drawer.FadeScreen(); DrawNotifyString(); _CopyEverything = true; } else if (_DrawLoading) { DrawLoading(); Sbar.Draw(); } else if (Client.cl.intermission == 1 && Key.Destination == keydest_t.key_game) { Sbar.IntermissionOverlay(); } else if (Client.cl.intermission == 2 && Key.Destination == keydest_t.key_game) { Sbar.FinaleOverlay(); CheckDrawCenterString(); } else { if (View.Crosshair > 0) { Drawer.DrawCharacter(_VRect.x + _VRect.width / 2, _VRect.y + _VRect.height / 2, '+'); } //Drawer.DrawString(8, vid.height - Sbar.SBAR_HEIGHT, $"{Math.Round(1.0 / Host.FrameTime)}fps"); ; DrawRam(); DrawNet(); DrawTurtle(); DrawPause(); CheckDrawCenterString(); Sbar.Draw(); DrawConsole(); Menu.Draw(); } View.UpdatePalette(); EndRendering(); } finally { _InUpdate = false; } }
/// <summary> /// R_SplitEntityOnNode /// </summary> static void SplitEntityOnNode(mnodebase_t node) { if (node.contents == Contents.CONTENTS_SOLID) return; // add an efrag if the node is a leaf if (node.contents < 0) { if (_EfragTopNode == null) _EfragTopNode = node as mnode_t; mleaf_t leaf = (mleaf_t)(object)node; // grab an efrag off the free list efrag_t ef = Client.cl.free_efrags; if (ef == null) { Con.Print("Too many efrags!\n"); return; // no free fragments... } Client.cl.free_efrags = Client.cl.free_efrags.entnext; ef.entity = _AddEnt; // add the entity link // *lastlink = ef; if (_LastObj is entity_t) { ((entity_t)_LastObj).efrag = ef; } else { ((efrag_t)_LastObj).entnext = ef; } _LastObj = ef; // lastlink = &ef->entnext; ef.entnext = null; // set the leaf links ef.leaf = leaf; ef.leafnext = leaf.efrags; leaf.efrags = ef; return; } // NODE_MIXED mnode_t n = node as mnode_t; if (n == null) return; mplane_t splitplane = n.plane; int sides = Mathlib.BoxOnPlaneSide(ref _EMins, ref _EMaxs, splitplane); if (sides == 3) { // split on this plane // if this is the first splitter of this bmodel, remember it if (_EfragTopNode == null) _EfragTopNode = n; } // recurse down the contacted sides if ((sides & 1) != 0) SplitEntityOnNode(n.children[0]); if ((sides & 2) != 0) SplitEntityOnNode(n.children[1]); }
/// <summary> /// SV_ReadClientMessage /// Returns false if the client should be killed /// </summary> static bool ReadClientMessage() { while (true) { int ret = Net.GetMessage(Host.HostClient.netconnection); if (ret == -1) { Con.DPrint("SV_ReadClientMessage: NET_GetMessage failed\n"); return(false); } if (ret == 0) { return(true); } Net.Reader.Reset(); bool flag = true; while (flag) { if (!Host.HostClient.active) { return(false); // a command caused an error } if (Net.Reader.IsBadRead) { Con.DPrint("SV_ReadClientMessage: badread\n"); return(false); } int cmd = Net.Reader.ReadChar(); switch (cmd) { case -1: flag = false; // end of message ret = 1; break; case Protocol.clc_nop: break; case Protocol.clc_stringcmd: string s = Net.Reader.ReadString(); if (Host.HostClient.privileged) { ret = 2; } else { ret = 0; } if (Common.SameText(s, "status", 6)) { ret = 1; } else if (Common.SameText(s, "god", 3)) { ret = 1; } else if (Common.SameText(s, "notarget", 8)) { ret = 1; } else if (Common.SameText(s, "fly", 3)) { ret = 1; } else if (Common.SameText(s, "name", 4)) { ret = 1; } else if (Common.SameText(s, "noclip", 6)) { ret = 1; } else if (Common.SameText(s, "say", 3)) { ret = 1; } else if (Common.SameText(s, "say_team", 8)) { ret = 1; } else if (Common.SameText(s, "tell", 4)) { ret = 1; } else if (Common.SameText(s, "color", 5)) { ret = 1; } else if (Common.SameText(s, "kill", 4)) { ret = 1; } else if (Common.SameText(s, "pause", 5)) { ret = 1; } else if (Common.SameText(s, "spawn", 5)) { ret = 1; } else if (Common.SameText(s, "begin", 5)) { ret = 1; } else if (Common.SameText(s, "prespawn", 8)) { ret = 1; } else if (Common.SameText(s, "kick", 4)) { ret = 1; } else if (Common.SameText(s, "ping", 4)) { ret = 1; } else if (Common.SameText(s, "give", 4)) { ret = 1; } else if (Common.SameText(s, "ban", 3)) { ret = 1; } if (ret == 2) { Cbuf.InsertText(s); } else if (ret == 1) { Cmd.ExecuteString(s, cmd_source_t.src_client); } else { Con.DPrint("{0} tried to {1}\n", Host.HostClient.name, s); } break; case Protocol.clc_disconnect: return(false); case Protocol.clc_move: ReadClientMove(ref Host.HostClient.cmd); break; default: Con.DPrint("SV_ReadClientMessage: unknown command char\n"); return(false); } } if (ret != 1) { break; } } return(true); }
// Key_Event (int key, qboolean down) // // Called by the system between frames for both key up and key down events // Should NOT be called during an interrupt! public static void Event(int key, bool down) { _KeyDown[key] = down; if (!down) { _Repeats[key] = 0; } _LastPress = key; KeyCount++; if (KeyCount <= 0) { return; // just catching keys for Con_NotifyBox } // update auto-repeat status if (down) { _Repeats[key]++; if (key != K_BACKSPACE && key != K_PAUSE && key != K_PGUP && key != K_PGDN && _Repeats[key] > 1) { return; // ignore most autorepeats } if (key >= 200 && String.IsNullOrEmpty(_Bindings[key])) { Con.Print("{0} is unbound, hit F4 to set.\n", KeynumToString(key)); } } if (key == K_SHIFT) { _ShiftDown = down; } // // handle escape specialy, so the user can never unbind it // if (key == K_ESCAPE) { if (!down) { return; } switch (_KeyDest) { case keydest_t.key_message: KeyMessage(key); break; case keydest_t.key_menu: Menu.KeyDown(key); break; case keydest_t.key_game: case keydest_t.key_console: Menu.ToggleMenu_f(); break; default: Sys.Error("Bad key_dest"); break; } return; } // // key up events only generate commands if the game key binding is // a button command (leading + sign). These will occur even in console mode, // to keep the character from continuing an action started before a console // switch. Button commands include the keynum as a parameter, so multiple // downs can be matched with ups // if (!down) { string kb = _Bindings[key]; if (!String.IsNullOrEmpty(kb) && kb.StartsWith("+")) { Cbuf.AddText(String.Format("-{0} {1}\n", kb.Substring(1), key)); } if (_KeyShift[key] != key) { kb = _Bindings[_KeyShift[key]]; if (!String.IsNullOrEmpty(kb) && kb.StartsWith("+")) { Cbuf.AddText(String.Format("-{0} {1}\n", kb.Substring(1), key)); } } return; } // // during demo playback, most keys bring up the main menu // if (Client.cls.demoplayback && down && _ConsoleKeys[key] && _KeyDest == keydest_t.key_game) { Menu.ToggleMenu_f(); return; } // // if not a consolekey, send to the interpreter no matter what mode is // if ((_KeyDest == keydest_t.key_menu && _MenuBound[key]) || (_KeyDest == keydest_t.key_console && !_ConsoleKeys[key]) || (_KeyDest == keydest_t.key_game && (!Con.ForcedUp || !_ConsoleKeys[key]))) { string kb = _Bindings[key]; if (!String.IsNullOrEmpty(kb)) { if (kb.StartsWith("+")) { // button commands add keynum as a parm Cbuf.AddText(String.Format("{0} {1}\n", kb, key)); } else { Cbuf.AddText(kb); Cbuf.AddText("\n"); } } return; } if (!down) { return; // other systems only care about key down events } if (_ShiftDown) { key = _KeyShift[key]; } switch (_KeyDest) { case keydest_t.key_message: KeyMessage(key); break; case keydest_t.key_menu: Menu.KeyDown(key); break; case keydest_t.key_game: case keydest_t.key_console: KeyConsole(key); break; default: Sys.Error("Bad key_dest"); break; } }
/// <summary> /// Key_Console /// Interactive line editing and console scrollback /// </summary> static void KeyConsole(int key) { if (key == K_ENTER) { string line = new String(_Lines[_EditLine]).TrimEnd('\0', ' '); string cmd = line.Substring(1); Cbuf.AddText(cmd); // skip the > Cbuf.AddText("\n"); Con.Print("{0}\n", line); _EditLine = (_EditLine + 1) & 31; _HistoryLine = _EditLine; _Lines[_EditLine][0] = ']'; Key.LinePos = 1; if (Client.cls.state == cactive_t.ca_disconnected) { Scr.UpdateScreen(); // force an update, because the command } // may take some time return; } if (key == K_TAB) { // command completion string txt = new String(_Lines[_EditLine], 1, MAXCMDLINE - 1).TrimEnd('\0', ' '); string[] cmds = Cmd.Complete(txt); string[] vars = Cvar.CompleteName(txt); string match = null; if (cmds != null) { if (cmds.Length > 1 || vars != null) { Con.Print("\nCommands:\n"); foreach (string s in cmds) { Con.Print(" {0}\n", s); } } else { match = cmds[0]; } } if (vars != null) { if (vars.Length > 1 || cmds != null) { Con.Print("\nVariables:\n"); foreach (string s in vars) { Con.Print(" {0}\n", s); } } else if (match == null) { match = vars[0]; } } if (!String.IsNullOrEmpty(match)) { int len = Math.Min(match.Length, MAXCMDLINE - 3); for (int i = 0; i < len; i++) { _Lines[_EditLine][i + 1] = match[i]; } Key.LinePos = len + 1; _Lines[_EditLine][Key.LinePos] = ' '; Key.LinePos++; _Lines[_EditLine][Key.LinePos] = '\0'; return; } } if (key == K_BACKSPACE || key == K_LEFTARROW) { if (Key.LinePos > 1) { Key.LinePos--; } return; } if (key == K_UPARROW) { do { _HistoryLine = (_HistoryLine - 1) & 31; } while (_HistoryLine != _EditLine && (_Lines[_HistoryLine][1] == 0)); if (_HistoryLine == _EditLine) { _HistoryLine = (_EditLine + 1) & 31; } Array.Copy(_Lines[_HistoryLine], _Lines[_EditLine], MAXCMDLINE); Key.LinePos = 0; while (_Lines[_EditLine][Key.LinePos] != '\0' && Key.LinePos < MAXCMDLINE) { Key.LinePos++; } return; } if (key == K_DOWNARROW) { if (_HistoryLine == _EditLine) { return; } do { _HistoryLine = (_HistoryLine + 1) & 31; }while (_HistoryLine != _EditLine && (_Lines[_HistoryLine][1] == '\0')); if (_HistoryLine == _EditLine) { _Lines[_EditLine][0] = ']'; Key.LinePos = 1; } else { Array.Copy(_Lines[_HistoryLine], _Lines[_EditLine], MAXCMDLINE); Key.LinePos = 0; while (_Lines[_EditLine][Key.LinePos] != '\0' && Key.LinePos < MAXCMDLINE) { Key.LinePos++; } } return; } if (key == K_PGUP || key == K_MWHEELUP) { Con.BackScroll += 2; if (Con.BackScroll > Con.TotalLines - (Scr.vid.height >> 3) - 1) { Con.BackScroll = Con.TotalLines - (Scr.vid.height >> 3) - 1; } return; } if (key == K_PGDN || key == K_MWHEELDOWN) { Con.BackScroll -= 2; if (Con.BackScroll < 0) { Con.BackScroll = 0; } return; } if (key == K_HOME) { Con.BackScroll = Con.TotalLines - (Scr.vid.height >> 3) - 1; return; } if (key == K_END) { Con.BackScroll = 0; return; } if (key < 32 || key > 127) { return; // non printable } if (Key.LinePos < MAXCMDLINE - 1) { _Lines[_EditLine][Key.LinePos] = (char)key; Key.LinePos++; _Lines[_EditLine][Key.LinePos] = '\0'; } }
/// <summary> /// Host_Status_f /// </summary> static void Status_f() { bool flag = true; if (Cmd.Source == cmd_source_t.src_command) { if (!Server.sv.active) { Cmd.ForwardToServer(); return; } } else { flag = false; } StringBuilder sb = new StringBuilder(256); sb.Append(String.Format("host: {0}\n", Cvar.GetString("hostname"))); sb.Append(String.Format("version: {0:F2}\n", QDef.VERSION)); if (Net.TcpIpAvailable) { sb.Append("tcp/ip: "); sb.Append(Net.MyTcpIpAddress); sb.Append('\n'); } sb.Append("map: "); sb.Append(Server.sv.name); sb.Append('\n'); sb.Append(String.Format("players: {0} active ({1} max)\n\n", Net.ActiveConnections, Server.svs.maxclients)); for (int j = 0; j < Server.svs.maxclients; j++) { client_t client = Server.svs.clients[j]; if (!client.active) { continue; } int seconds = (int)(Net.Time - client.netconnection.connecttime); int hours, minutes = seconds / 60; if (minutes > 0) { seconds -= (minutes * 60); hours = minutes / 60; if (hours > 0) { minutes -= (hours * 60); } } else { hours = 0; } sb.Append(String.Format("#{0,-2} {1,-16} {2} {2}:{4,2}:{5,2}", j + 1, client.name, (int)client.edict.v.frags, hours, minutes, seconds)); sb.Append(" "); sb.Append(client.netconnection.address); sb.Append('\n'); } if (flag) { Con.Print(sb.ToString()); } else { Server.ClientPrint(sb.ToString()); } }
// Host_Version_f static void Version_f() { Con.Print("Version {0}\n", QDef.VERSION); Con.Print("Exe hash code: {0}\n", System.Reflection.Assembly.GetExecutingAssembly().GetHashCode()); }
/// <summary> /// Host_Loadgame_f /// </summary> static void Loadgame_f() { if (Cmd.Source != cmd_source_t.src_command) { return; } if (Cmd.Argc != 2) { Con.Print("load <savename> : load a game\n"); return; } Client.cls.demonum = -1; // stop demo loop in case this fails string name = Path.ChangeExtension(Path.Combine(Common.GameDir, Cmd.Argv(1)), ".sav"); // we can't call SCR_BeginLoadingPlaque, because too much stack space has // been used. The menu calls it before stuffing loadgame command // SCR_BeginLoadingPlaque (); Con.Print("Loading game from {0}...\n", name); FileStream fs = Sys.FileOpenRead(name); if (fs == null) { Con.Print("ERROR: couldn't open.\n"); return; } using (StreamReader reader = new StreamReader(fs, Encoding.ASCII)) { string line = reader.ReadLine(); int version = Common.atoi(line); if (version != SAVEGAME_VERSION) { Con.Print("Savegame is version {0}, not {1}\n", version, SAVEGAME_VERSION); return; } line = reader.ReadLine(); float[] spawn_parms = new float[Server.NUM_SPAWN_PARMS]; for (int i = 0; i < spawn_parms.Length; i++) { line = reader.ReadLine(); spawn_parms[i] = Common.atof(line); } // this silliness is so we can load 1.06 save files, which have float skill values line = reader.ReadLine(); float tfloat = Common.atof(line); Host.CurrentSkill = (int)(tfloat + 0.1); Cvar.Set("skill", (float)Host.CurrentSkill); string mapname = reader.ReadLine(); line = reader.ReadLine(); float time = Common.atof(line); Client.Disconnect_f(); Server.SpawnServer(mapname); if (!Server.sv.active) { Con.Print("Couldn't load map\n"); return; } Server.sv.paused = true; // pause until all clients connect Server.sv.loadgame = true; // load the light styles for (int i = 0; i < QDef.MAX_LIGHTSTYLES; i++) { line = reader.ReadLine(); Server.sv.lightstyles[i] = line; } // load the edicts out of the savegame file int entnum = -1; // -1 is the globals StringBuilder sb = new StringBuilder(32768); while (!reader.EndOfStream) { line = reader.ReadLine(); if (line == null) { Sys.Error("EOF without closing brace"); } sb.AppendLine(line); int idx = line.IndexOf('}'); if (idx != -1) { int length = 1 + sb.Length - (line.Length - idx); string data = Common.Parse(sb.ToString(0, length)); if (String.IsNullOrEmpty(Common.Token)) { break; // end of file } if (Common.Token != "{") { Sys.Error("First token isn't a brace"); } if (entnum == -1) { // parse the global vars Progs.ParseGlobals(data); } else { // parse an edict edict_t ent = Server.EdictNum(entnum); ent.Clear(); Progs.ParseEdict(data, ent); // link it into the bsp tree if (!ent.free) { Server.LinkEdict(ent, false); } } entnum++; sb.Remove(0, length); } } Server.sv.num_edicts = entnum; Server.sv.time = time; for (int i = 0; i < Server.NUM_SPAWN_PARMS; i++) { Server.svs.clients[0].spawn_parms[i] = spawn_parms[i]; } } if (Client.cls.state != cactive_t.ca_dedicated) { Client.EstablishConnection("local"); Reconnect_f(); } }
/// <summary> /// Host_Savegame_f /// </summary> static void Savegame_f() { if (Cmd.Source != cmd_source_t.src_command) { return; } if (!Server.sv.active) { Con.Print("Not playing a local game.\n"); return; } if (Client.cl.intermission != 0) { Con.Print("Can't save in intermission.\n"); return; } if (Server.svs.maxclients != 1) { Con.Print("Can't save multiplayer games.\n"); return; } if (Cmd.Argc != 2) { Con.Print("save <savename> : save a game\n"); return; } if (Cmd.Argv(1).Contains("..")) { Con.Print("Relative pathnames are not allowed.\n"); return; } for (int i = 0; i < Server.svs.maxclients; i++) { if (Server.svs.clients[i].active && (Server.svs.clients[i].edict.v.health <= 0)) { Con.Print("Can't savegame with a dead player\n"); return; } } string name = Path.ChangeExtension(Path.Combine(Common.GameDir, Cmd.Argv(1)), ".sav"); Con.Print("Saving game to {0}...\n", name); FileStream fs = Sys.FileOpenWrite(name, true); if (fs == null) { Con.Print("ERROR: couldn't open.\n"); return; } using (StreamWriter writer = new StreamWriter(fs, Encoding.ASCII)) { writer.WriteLine(SAVEGAME_VERSION); writer.WriteLine(SavegameComment()); for (int i = 0; i < Server.NUM_SPAWN_PARMS; i++) { writer.WriteLine(Server.svs.clients[0].spawn_parms[i].ToString("F6", CultureInfo.InvariantCulture.NumberFormat)); } writer.WriteLine(Host.CurrentSkill); writer.WriteLine(Server.sv.name); writer.WriteLine(Server.sv.time.ToString("F6", CultureInfo.InvariantCulture.NumberFormat)); // write the light styles for (int i = 0; i < QDef.MAX_LIGHTSTYLES; i++) { if (!String.IsNullOrEmpty(Server.sv.lightstyles[i])) { writer.WriteLine(Server.sv.lightstyles[i]); } else { writer.WriteLine("m"); } } Progs.WriteGlobals(writer); for (int i = 0; i < Server.sv.num_edicts; i++) { Progs.WriteEdict(writer, Server.EdictNum(i)); writer.Flush(); } } Con.Print("done.\n"); }
/// <summary> /// Host_Spawn_f /// </summary> static void Spawn_f() { if (Cmd.Source == cmd_source_t.src_command) { Con.Print("spawn is not valid from the console\n"); return; } if (Host.HostClient.spawned) { Con.Print("Spawn not valid -- allready spawned\n"); return; } edict_t ent; // run the entrance script if (Server.sv.loadgame) { // loaded games are fully inited allready // if this is the last client to be connected, unpause Server.sv.paused = false; } else { // set up the edict ent = Host.HostClient.edict; ent.Clear(); //memset(&ent.v, 0, progs.entityfields * 4); ent.v.colormap = Server.NumForEdict(ent); ent.v.team = (Host.HostClient.colors & 15) + 1; ent.v.netname = Progs.NewString(Host.HostClient.name); // copy spawn parms out of the client_t Progs.GlobalStruct.SetParams(Host.HostClient.spawn_parms); // call the spawn function Progs.GlobalStruct.time = (float)Server.sv.time; Progs.GlobalStruct.self = Server.EdictToProg(Server.Player); Progs.Execute(Progs.GlobalStruct.ClientConnect); if ((Sys.GetFloatTime() - Host.HostClient.netconnection.connecttime) <= Server.sv.time) { Con.DPrint("{0} entered the game\n", Host.HostClient.name); } Progs.Execute(Progs.GlobalStruct.PutClientInServer); } // send all current names, colors, and frag counts MsgWriter msg = Host.HostClient.message; msg.Clear(); // send time of update msg.WriteByte(Protocol.svc_time); msg.WriteFloat((float)Server.sv.time); for (int i = 0; i < Server.svs.maxclients; i++) { client_t client = Server.svs.clients[i]; msg.WriteByte(Protocol.svc_updatename); msg.WriteByte(i); msg.WriteString(client.name); msg.WriteByte(Protocol.svc_updatefrags); msg.WriteByte(i); msg.WriteShort(client.old_frags); msg.WriteByte(Protocol.svc_updatecolors); msg.WriteByte(i); msg.WriteByte(client.colors); } // send all current light styles for (int i = 0; i < QDef.MAX_LIGHTSTYLES; i++) { msg.WriteByte(Protocol.svc_lightstyle); msg.WriteByte((char)i); msg.WriteString(Server.sv.lightstyles[i]); } // // send some stats // msg.WriteByte(Protocol.svc_updatestat); msg.WriteByte(QStats.STAT_TOTALSECRETS); msg.WriteLong((int)Progs.GlobalStruct.total_secrets); msg.WriteByte(Protocol.svc_updatestat); msg.WriteByte(QStats.STAT_TOTALMONSTERS); msg.WriteLong((int)Progs.GlobalStruct.total_monsters); msg.WriteByte(Protocol.svc_updatestat); msg.WriteByte(QStats.STAT_SECRETS); msg.WriteLong((int)Progs.GlobalStruct.found_secrets); msg.WriteByte(Protocol.svc_updatestat); msg.WriteByte(QStats.STAT_MONSTERS); msg.WriteLong((int)Progs.GlobalStruct.killed_monsters); // // send a fixangle // Never send a roll angle, because savegames can catch the server // in a state where it is expecting the client to correct the angle // and it won't happen if the game was just loaded, so you wind up // with a permanent head tilt ent = Server.EdictNum(1 + Host.ClientNum); msg.WriteByte(Protocol.svc_setangle); msg.WriteAngle(ent.v.angles.x); msg.WriteAngle(ent.v.angles.y); msg.WriteAngle(0); Server.WriteClientDataToMessage(Server.Player, Host.HostClient.message); msg.WriteByte(Protocol.svc_signonnum); msg.WriteByte(3); Host.HostClient.sendsignon = true; }