/// <summary> /// CL_KeepaliveMessage /// When the client is taking a long time to load stuff, send keepalive messages /// so the server doesn't disconnect. /// </summary> static void KeepaliveMessage() { if (Server.IsActive) { return; // no need if server is local } if (cls.demoplayback) { return; } // read messages from server, should just be nops Net.Message.SaveState(ref _MsgState); int ret; do { ret = GetMessage(); switch (ret) { default: Host.Error("CL_KeepaliveMessage: CL_GetMessage failed"); break; case 0: break; // nothing waiting case 1: Host.Error("CL_KeepaliveMessage: received a message"); break; case 2: if (Net.Reader.ReadByte() != Protocol.svc_nop) { Host.Error("CL_KeepaliveMessage: datagram wasn't a nop"); } break; } } while (ret != 0); Net.Message.RestoreState(_MsgState); // check time float time = (float)Sys.GetFloatTime(); if (time - _LastMsg < 5) { return; } _LastMsg = time; // write out a nop Con.Print("--> client to server keepalive\n"); cls.message.WriteByte(Protocol.clc_nop); Net.SendMessage(cls.netcon, cls.message); cls.message.Clear(); }
/// <summary> /// Slist_Send /// </summary> static void SlistSend(object arg) { for (_DriverLevel = 0; _DriverLevel < _Drivers.Length; _DriverLevel++) { if (!Net.SlistLocal && _DriverLevel == 0) { continue; } if (!_Drivers[_DriverLevel].IsInitialized) { continue; } _Drivers[_DriverLevel].SearchForHosts(true); } if ((Sys.GetFloatTime() - _SlistStartTime) < 0.5) { SchedulePollProcedure(_SlistSendProcedure, 0.75); } }
/// <summary> /// NET_Slist_f /// </summary> public static void Slist_f() { if (_SlistInProgress) { return; } if (!Net.SlistSilent) { Con.Print("Looking for Quake servers...\n"); PrintSlistHeader(); } _SlistInProgress = true; _SlistStartTime = Sys.GetFloatTime(); SchedulePollProcedure(_SlistSendProcedure, 0.0); SchedulePollProcedure(_SlistPollProcedure, 0.1); Net.HostCacheCount = 0; }
// Host_Frame public static void Frame(double time) { if (_ServerProfile.Value == 0) { InternalFrame(time); return; } double time1 = Sys.GetFloatTime(); InternalFrame(time); double time2 = Sys.GetFloatTime(); _TimeTotal += time2 - time1; _TimeCount++; if (_TimeCount < 1000) { return; } int m = (int)(_TimeTotal * 1000 / _TimeCount); _TimeCount = 0; _TimeTotal = 0; int c = 0; foreach (client_t cl in Server.svs.clients) { if (cl.active) { c++; } } Con.Print("serverprofile: {0,2:d} clients {1,2:d} msec\n", c, m); }
/// <summary> /// SchedulePollProcedure /// </summary> static void SchedulePollProcedure(PollProcedure proc, double timeOffset) { proc.nextTime = Sys.GetFloatTime() + timeOffset; PollProcedure pp, prev; for (pp = _PollProcedureList, prev = null; pp != null; pp = pp.next) { if (pp.nextTime >= proc.nextTime) { break; } prev = pp; } if (prev == null) { proc.next = _PollProcedureList; _PollProcedureList = proc; return; } proc.next = pp; prev.next = proc; }
/// <summary> /// Slist_Poll /// </summary> static void SlistPoll(object arg) { for (_DriverLevel = 0; _DriverLevel < _Drivers.Length; _DriverLevel++) { if (!Net.SlistLocal && _DriverLevel == 0) { continue; } if (!_Drivers[_DriverLevel].IsInitialized) { continue; } _Drivers[_DriverLevel].SearchForHosts(false); } if (!Net.SlistSilent) { PrintSlist(); } if ((Sys.GetFloatTime() - _SlistStartTime) < 1.5) { SchedulePollProcedure(_SlistPollProcedure, 0.1); return; } if (!Net.SlistSilent) { PrintSlistTrailer(); } _SlistInProgress = false; Net.SlistSilent = false; Net.SlistLocal = true; }
// double SetNetTime public static double SetNetTime() { _Time = Sys.GetFloatTime(); return(_Time); }
/// <summary> /// NET_SendToAll /// This is a reliable *blocking* send to all attached clients. /// </summary> public static int SendToAll(MsgWriter data, int blocktime) { bool[] state1 = new bool[QDef.MAX_SCOREBOARD]; bool[] state2 = new bool[QDef.MAX_SCOREBOARD]; int count = 0; for (int i = 0; i < Server.svs.maxclients; i++) { Host.HostClient = Server.svs.clients[i]; if (Host.HostClient.netconnection == null) { continue; } if (Host.HostClient.active) { if (Host.HostClient.netconnection.driver == 0) { SendMessage(Host.HostClient.netconnection, data); state1[i] = true; state2[i] = true; continue; } count++; state1[i] = false; state2[i] = false; } else { state1[i] = true; state2[i] = true; } } double start = Sys.GetFloatTime(); while (count > 0) { count = 0; for (int i = 0; i < Server.svs.maxclients; i++) { Host.HostClient = Server.svs.clients[i]; if (!state1[i]) { if (CanSendMessage(Host.HostClient.netconnection)) { state1[i] = true; SendMessage(Host.HostClient.netconnection, data); } else { GetMessage(Host.HostClient.netconnection); } count++; continue; } if (!state2[i]) { if (CanSendMessage(Host.HostClient.netconnection)) { state2[i] = true; } else { GetMessage(Host.HostClient.netconnection); } count++; continue; } } if ((Sys.GetFloatTime() - start) > blocktime) { break; } } return(count); }
// _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> /// 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> /// 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; }