/// <summary> /// SV_SendClientDatagram /// </summary> static bool SendClientDatagram(client_t client) { MsgWriter msg = new MsgWriter(QDef.MAX_DATAGRAM); // Uze todo: make static? msg.WriteByte(Protocol.svc_time); msg.WriteFloat((float)sv.time); // add the client specific data to the datagram WriteClientDataToMessage(client.edict, msg); WriteEntitiesToClient(client.edict, msg); // copy the server datagram if there is space if (msg.Length + sv.datagram.Length < msg.Capacity) { msg.Write(sv.datagram.Data, 0, sv.datagram.Length); } // send the datagram if (Net.SendUnreliableMessage(client.netconnection, msg) == -1) { DropClient(true);// if the message couldn't send, kick off return(false); } return(true); }
/// <summary> /// SV_ReadClientMove /// </summary> static void ReadClientMove(ref usercmd_t move) { client_t client = Host.HostClient; // read ping time client.ping_times[client.num_pings % NUM_PING_TIMES] = (float)(sv.time - Net.Reader.ReadFloat()); client.num_pings++; // read current angles Vector3 angles = Net.Reader.ReadAngles(); Mathlib.Copy(ref angles, out client.edict.v.v_angle); // read movement move.forwardmove = Net.Reader.ReadShort(); move.sidemove = Net.Reader.ReadShort(); move.upmove = Net.Reader.ReadShort(); // read buttons int bits = Net.Reader.ReadByte(); client.edict.v.button0 = bits & 1; client.edict.v.button2 = (bits & 2) >> 1; int i = Net.Reader.ReadByte(); if (i != 0) { client.edict.v.impulse = i; } }
/// <summary> /// Host_Ping_f /// </summary> static void Ping_f() { if (Cmd.Source == cmd_source_t.src_command) { Cmd.ForwardToServer(); return; } Server.ClientPrint("Client ping times:\n"); for (int i = 0; i < Server.svs.maxclients; i++) { client_t client = Server.svs.clients[i]; if (!client.active) { continue; } float total = 0; for (int j = 0; j < Server.NUM_PING_TIMES; j++) { total += client.ping_times[j]; } total /= Server.NUM_PING_TIMES; Server.ClientPrint("{0,4} {1}\n", (int)(total * 1000), client.name); } }
/* * =============== * PF_lightstyle * * void(float style, string value) lightstyle * =============== */ static void PF_lightstyle() { int style = (int)GetFloat(OFS.OFS_PARM0); // Uze: ??? string val = GetString(OFS.OFS_PARM1); // change the string in sv Server.sv.lightstyles[style] = val; // send message to all clients on this server if (!Server.IsActive) { return; } for (int j = 0; j < Server.svs.maxclients; j++) { client_t client = Server.svs.clients[j]; if (client.active || client.spawned) { client.message.WriteChar(Protocol.svc_lightstyle); client.message.WriteChar(style); client.message.WriteString(val); } } }
/// <summary> /// SV_DropClient /// Called when the player is getting totally kicked off the host /// if (crash = true), don't bother sending signofs /// </summary> public static void DropClient(bool crash) { client_t client = Host.HostClient; if (!crash) { // send any final messages (don't check for errors) if (Net.CanSendMessage(client.netconnection)) { MsgWriter msg = client.message; msg.WriteByte(Protocol.svc_disconnect); Net.SendMessage(client.netconnection, msg); } if (client.edict != null && client.spawned) { // call the prog function for removing a client // this will set the body to a dead frame, among other things int saveSelf = Progs.GlobalStruct.self; Progs.GlobalStruct.self = EdictToProg(client.edict); Progs.Execute(Progs.GlobalStruct.ClientDisconnect); Progs.GlobalStruct.self = saveSelf; } Con.DPrint("Client {0} removed\n", client.name); } // break the net connection Net.Close(client.netconnection); client.netconnection = null; // free the client (the body stays around) client.active = false; client.name = null; client.old_frags = -999999; Net.ActiveConnections--; // send notification to all clients for (int i = 0; i < Server.svs.maxclients; i++) { client_t cl = Server.svs.clients[i]; if (!cl.active) { continue; } cl.message.WriteByte(Protocol.svc_updatename); cl.message.WriteByte(Host.ClientNum); cl.message.WriteString(""); cl.message.WriteByte(Protocol.svc_updatefrags); cl.message.WriteByte(Host.ClientNum); cl.message.WriteShort(0); cl.message.WriteByte(Protocol.svc_updatecolors); cl.message.WriteByte(Host.ClientNum); cl.message.WriteByte(0); } }
/// <summary> /// SV_SendNop /// Send a nop message without trashing or sending the accumulated client /// message buffer /// </summary> static void SendNop(client_t client) { MsgWriter msg = new MsgWriter(4); msg.WriteChar(Protocol.svc_nop); if (Net.SendUnreliableMessage(client.netconnection, msg) == -1) { DropClient(true); // if the message couldn't send, kick off } client.last_message = Host.RealTime; }
/* * ============== * PF_setspawnparms * ============== */ static void PF_setspawnparms() { edict_t ent = GetEdict(OFS.OFS_PARM0); int i = Server.NumForEdict(ent); if (i < 1 || i > Server.svs.maxclients) { Progs.RunError("Entity is not a client"); } // copy spawn parms out of the client_t client_t client = Server.svs.clients[i - 1]; Progs.GlobalStruct.SetParams(client.spawn_parms); }
/* * ================= * PF_centerprint * * single print to a specific client * * centerprint(clientent, value) * ================= */ static void PF_centerprint() { int entnum = Server.NumForEdict(GetEdict(OFS.OFS_PARM0)); string s = PF_VarString(1); if (entnum < 1 || entnum > Server.svs.maxclients) { Con.Print("tried to centerprint to a non-client\n"); return; } client_t client = Server.svs.clients[entnum - 1]; client.message.WriteChar(Protocol.svc_centerprint); client.message.WriteString(s); }
//============================================================================ /// <summary> /// PF_stuffcmd /// Sends text over to the client's execution buffer /// stuffcmd (clientent, value) /// </summary> static void PF_stuffcmd() { int entnum = Server.NumForEdict(GetEdict(OFS.OFS_PARM0)); if (entnum < 1 || entnum > Server.svs.maxclients) { Progs.RunError("Parm 0 not a client"); } string str = GetString(OFS.OFS_PARM1); client_t old = Host.HostClient; Host.HostClient = Server.svs.clients[entnum - 1]; Host.ClientCommands("{0}", str); Host.HostClient = old; }
// Host_Tell_f static void Tell_f() { if (Cmd.Source == cmd_source_t.src_command) { Cmd.ForwardToServer(); return; } if (Cmd.Argc < 3) { return; } string text = Host.HostClient.name + ": "; string p = Cmd.Args; // remove quotes if present if (p.StartsWith("\"")) { p = p.Substring(1, p.Length - 2); } text += p + "\n"; client_t save = Host.HostClient; for (int j = 0; j < Server.svs.maxclients; j++) { client_t client = Server.svs.clients[j]; if (!client.active || !client.spawned) { continue; } if (client.name == Cmd.Argv(1)) { continue; } Host.HostClient = client; Server.ClientPrint(text); break; } Host.HostClient = save; }
/// <summary> /// SV_ConnectClient /// Initializes a client_t for a new net connection. This will only be called /// once for a player each game, not once for each level change. /// </summary> static void ConnectClient(int clientnum) { client_t client = svs.clients[clientnum]; Con.DPrint("Client {0} connected\n", client.netconnection.address); int edictnum = clientnum + 1; edict_t ent = EdictNum(edictnum); // set up the client_t qsocket_t netconnection = client.netconnection; float[] spawn_parms = new float[NUM_SPAWN_PARMS]; if (sv.loadgame) { Array.Copy(client.spawn_parms, spawn_parms, spawn_parms.Length); } client.Clear(); client.netconnection = netconnection; client.name = "unconnected"; client.active = true; client.spawned = false; client.edict = ent; client.message.AllowOverflow = true; // we can catch it client.privileged = false; if (sv.loadgame) { Array.Copy(spawn_parms, client.spawn_parms, spawn_parms.Length); } else { // call the progs to get default spawn parms for the new client Progs.Execute(Progs.GlobalStruct.SetNewParms); AssignGlobalSpawnparams(client); } SendServerInfo(client); }
private static void AssignGlobalSpawnparams(client_t client) { client.spawn_parms[0] = Progs.GlobalStruct.parm1; client.spawn_parms[1] = Progs.GlobalStruct.parm2; client.spawn_parms[2] = Progs.GlobalStruct.parm3; client.spawn_parms[3] = Progs.GlobalStruct.parm4; client.spawn_parms[4] = Progs.GlobalStruct.parm5; client.spawn_parms[5] = Progs.GlobalStruct.parm6; client.spawn_parms[6] = Progs.GlobalStruct.parm7; client.spawn_parms[7] = Progs.GlobalStruct.parm8; client.spawn_parms[8] = Progs.GlobalStruct.parm9; client.spawn_parms[9] = Progs.GlobalStruct.parm10; client.spawn_parms[10] = Progs.GlobalStruct.parm11; client.spawn_parms[11] = Progs.GlobalStruct.parm12; client.spawn_parms[12] = Progs.GlobalStruct.parm13; client.spawn_parms[13] = Progs.GlobalStruct.parm14; client.spawn_parms[14] = Progs.GlobalStruct.parm15; client.spawn_parms[15] = Progs.GlobalStruct.parm16; }
/// <summary> /// SV_UpdateToReliableMessages /// </summary> static void UpdateToReliableMessages() { // check for changes to be sent over the reliable streams for (int i = 0; i < svs.maxclients; i++) { Host.HostClient = svs.clients[i]; if (Host.HostClient.old_frags != Host.HostClient.edict.v.frags) { for (int j = 0; j < svs.maxclients; j++) { client_t client = svs.clients[j]; if (!client.active) { continue; } client.message.WriteByte(Protocol.svc_updatefrags); client.message.WriteByte(i); client.message.WriteShort((int)Host.HostClient.edict.v.frags); } Host.HostClient.old_frags = (int)Host.HostClient.edict.v.frags; } } for (int j = 0; j < svs.maxclients; j++) { client_t client = svs.clients[j]; if (!client.active) { continue; } client.message.Write(sv.reliable_datagram.Data, 0, sv.reliable_datagram.Length); } sv.reliable_datagram.Clear(); }
/// <summary> /// Host_Say /// </summary> static void Say(bool teamonly) { bool fromServer = false; if (Cmd.Source == cmd_source_t.src_command) { if (Client.cls.state == cactive_t.ca_dedicated) { fromServer = true; teamonly = false; } else { Cmd.ForwardToServer(); return; } } if (Cmd.Argc < 2) { return; } client_t save = Host.HostClient; string p = Cmd.Args; // remove quotes if present if (p.StartsWith("\"")) { p = p.Substring(1, p.Length - 2); } // turn on color set 1 string text; if (!fromServer) { text = (char)1 + save.name + ": "; } else { text = (char)1 + "<" + Net.HostName + "> "; } text += p + "\n"; for (int j = 0; j < Server.svs.maxclients; j++) { client_t client = Server.svs.clients[j]; if (client == null || !client.active || !client.spawned) { continue; } if (Host.TeamPlay != 0 && teamonly && client.edict.v.team != save.edict.v.team) { continue; } Host.HostClient = client; Server.ClientPrint(text); } Host.HostClient = save; }
/// <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()); } }
/// <summary> /// Host_Kick_f /// Kicks a user off of the server /// </summary> static void Kick_f() { if (Cmd.Source == cmd_source_t.src_command) { if (!Server.sv.active) { Cmd.ForwardToServer(); return; } } else if (Progs.GlobalStruct.deathmatch != 0 && !Host.HostClient.privileged) { return; } client_t save = Host.HostClient; bool byNumber = false; int i; if (Cmd.Argc > 2 && Cmd.Argv(1) == "#") { i = (int)Common.atof(Cmd.Argv(2)) - 1; if (i < 0 || i >= Server.svs.maxclients) { return; } if (!Server.svs.clients[i].active) { return; } Host.HostClient = Server.svs.clients[i]; byNumber = true; } else { for (i = 0; i < Server.svs.maxclients; i++) { Host.HostClient = Server.svs.clients[i]; if (!Host.HostClient.active) { continue; } if (Common.SameText(Host.HostClient.name, Cmd.Argv(1))) { break; } } } if (i < Server.svs.maxclients) { string who; if (Cmd.Source == cmd_source_t.src_command) { if (Client.cls.state == cactive_t.ca_dedicated) { who = "Console"; } else { who = Client.Name; } } else { who = save.name; } // can't kick yourself! if (Host.HostClient == save) { return; } string message = null; if (Cmd.Argc > 2) { message = Common.Parse(Cmd.Args); if (byNumber) { message = message.Substring(1); // skip the # message = message.Trim(); // skip white space message = message.Substring(Cmd.Argv(2).Length); // skip the number } message = message.Trim(); } if (!String.IsNullOrEmpty(message)) { Server.ClientPrint("Kicked by {0}: {1}\n", who, message); } else { Server.ClientPrint("Kicked by {0}\n", who); } Server.DropClient(false); } Host.HostClient = save; }
/// <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> /// _Datagram_CheckNewConnections /// </summary> public qsocket_t InternalCheckNewConnections() { Socket acceptsock = Net.LanDriver.CheckNewConnections(); if (acceptsock == null) { return(null); } EndPoint clientaddr = new IPEndPoint(IPAddress.Any, 0); Net.Message.FillFrom(acceptsock, ref clientaddr); if (Net.Message.Length < sizeof(int)) { return(null); } Net.Reader.Reset(); int control = Common.BigLong(Net.Reader.ReadLong()); if (control == -1) { return(null); } if ((control & (~NetFlags.NETFLAG_LENGTH_MASK)) != NetFlags.NETFLAG_CTL) { return(null); } if ((control & NetFlags.NETFLAG_LENGTH_MASK) != Net.Message.Length) { return(null); } int command = Net.Reader.ReadByte(); if (command == CCReq.CCREQ_SERVER_INFO) { string tmp = Net.Reader.ReadString(); if (tmp != "QUAKE") { return(null); } Net.Message.Clear(); // save space for the header, filled in later Net.Message.WriteLong(0); Net.Message.WriteByte(CCRep.CCREP_SERVER_INFO); EndPoint newaddr = acceptsock.LocalEndPoint; //dfunc.GetSocketAddr(acceptsock, &newaddr); Net.Message.WriteString(newaddr.ToString()); // dfunc.AddrToString(&newaddr)); Net.Message.WriteString(Net.HostName); Net.Message.WriteString(Server.sv.name); Net.Message.WriteByte(Net.ActiveConnections); Net.Message.WriteByte(Server.svs.maxclients); Net.Message.WriteByte(Net.NET_PROTOCOL_VERSION); Common.WriteInt(Net.Message.Data, 0, Common.BigLong(NetFlags.NETFLAG_CTL | (Net.Message.Length & NetFlags.NETFLAG_LENGTH_MASK))); Net.LanDriver.Write(acceptsock, Net.Message.Data, Net.Message.Length, clientaddr); Net.Message.Clear(); return(null); } if (command == CCReq.CCREQ_PLAYER_INFO) { int playerNumber = Net.Reader.ReadByte(); int clientNumber, activeNumber = -1; client_t client = null; for (clientNumber = 0; clientNumber < Server.svs.maxclients; clientNumber++) { client = Server.svs.clients[clientNumber]; if (client.active) { activeNumber++; if (activeNumber == playerNumber) { break; } } } if (clientNumber == Server.svs.maxclients) { return(null); } Net.Message.Clear(); // save space for the header, filled in later Net.Message.WriteLong(0); Net.Message.WriteByte(CCRep.CCREP_PLAYER_INFO); Net.Message.WriteByte(playerNumber); Net.Message.WriteString(client.name); Net.Message.WriteLong(client.colors); Net.Message.WriteLong((int)client.edict.v.frags); Net.Message.WriteLong((int)(Net.Time - client.netconnection.connecttime)); Net.Message.WriteString(client.netconnection.address); Common.WriteInt(Net.Message.Data, 0, Common.BigLong(NetFlags.NETFLAG_CTL | (Net.Message.Length & NetFlags.NETFLAG_LENGTH_MASK))); Net.LanDriver.Write(acceptsock, Net.Message.Data, Net.Message.Length, clientaddr); Net.Message.Clear(); return(null); } if (command == CCReq.CCREQ_RULE_INFO) { // find the search start location string prevCvarName = Net.Reader.ReadString(); Cvar var; if (!String.IsNullOrEmpty(prevCvarName)) { var = Cvar.Find(prevCvarName); if (var == null) { return(null); } var = var.Next; } else { var = Cvar.First; } // search for the next server cvar while (var != null) { if (var.IsServer) { break; } var = var.Next; } // send the response Net.Message.Clear(); // save space for the header, filled in later Net.Message.WriteLong(0); Net.Message.WriteByte(CCRep.CCREP_RULE_INFO); if (var != null) { Net.Message.WriteString(var.Name); Net.Message.WriteString(var.String); } Common.WriteInt(Net.Message.Data, 0, Common.BigLong(NetFlags.NETFLAG_CTL | (Net.Message.Length & NetFlags.NETFLAG_LENGTH_MASK))); Net.LanDriver.Write(acceptsock, Net.Message.Data, Net.Message.Length, clientaddr); Net.Message.Clear(); return(null); } if (command != CCReq.CCREQ_CONNECT) { return(null); } if (Net.Reader.ReadString() != "QUAKE") { return(null); } if (Net.Reader.ReadByte() != Net.NET_PROTOCOL_VERSION) { Net.Message.Clear(); // save space for the header, filled in later Net.Message.WriteLong(0); Net.Message.WriteByte(CCRep.CCREP_REJECT); Net.Message.WriteString("Incompatible version.\n"); Common.WriteInt(Net.Message.Data, 0, Common.BigLong(NetFlags.NETFLAG_CTL | (Net.Message.Length & NetFlags.NETFLAG_LENGTH_MASK))); Net.LanDriver.Write(acceptsock, Net.Message.Data, Net.Message.Length, clientaddr); Net.Message.Clear(); return(null); } #if BAN_TEST // check for a ban if (clientaddr.sa_family == AF_INET) { unsigned long testAddr; testAddr = ((struct sockaddr_in *)&clientaddr)->sin_addr.s_addr;
/// <summary> /// SV_SendServerinfo /// Sends the first message from the server to a connected client. /// This will be sent on the initial connection and upon each server load. /// </summary> static void SendServerInfo(client_t client) { MsgWriter writer = client.message; writer.WriteByte(Protocol.svc_print); writer.WriteString(String.Format("{0}\nVERSION {1,4:F2} SERVER ({2} CRC)", (char)2, QDef.VERSION, Progs.Crc)); writer.WriteByte(Protocol.svc_serverinfo); writer.WriteLong(Protocol.PROTOCOL_VERSION); writer.WriteByte(svs.maxclients); if (!Host.IsCoop && Host.Deathmatch != 0) { writer.WriteByte(Protocol.GAME_DEATHMATCH); } else { writer.WriteByte(Protocol.GAME_COOP); } string message = Progs.GetString(sv.edicts[0].v.message); writer.WriteString(message); for (int i = 1; i < sv.model_precache.Length; i++) { string tmp = sv.model_precache[i]; if (String.IsNullOrEmpty(tmp)) { break; } writer.WriteString(tmp); } writer.WriteByte(0); for (int i = 1; i < sv.sound_precache.Length; i++) { string tmp = sv.sound_precache[i]; if (tmp == null) { break; } writer.WriteString(tmp); } writer.WriteByte(0); // send music writer.WriteByte(Protocol.svc_cdtrack); writer.WriteByte((int)sv.edicts[0].v.sounds); writer.WriteByte((int)sv.edicts[0].v.sounds); // set view writer.WriteByte(Protocol.svc_setview); writer.WriteShort(NumForEdict(client.edict)); writer.WriteByte(Protocol.svc_signonnum); writer.WriteByte(1); client.sendsignon = true; client.spawned = false; // need prespawn, spawn, etc }
/// <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; }