bool ClientCommand(client_t cl, NetBuffer msg) { int seq = msg.ReadInt32(); string s = msg.ReadString(); // see if we have already executed it if (cl.lastClientCommand >= seq) return true; Common.Instance.WriteLine("ClientCommand: {0}[s{1}]: {2}", cl.name, seq, s); // drop the connection if we have somehow lost commands if (seq > cl.lastClientCommand + 1) { Common.Instance.WriteLine("Client {0} lost {1} clientCommands", cl.name, seq-cl.lastClientCommand+1); DropClient(cl, "Lost reliable commands"); return false; } // don't allow another command for one second cl.nextReliableTime = (int)time + 1000; ExecuteClientCommand(cl, s); cl.lastClientCommand = seq; cl.lastClientCommandString = s; return true; // continue procesing }
public static void Host_Ping_f() { if (cmd_source == cmd_source_t.src_command) { Cmd_ForwardToServer(); return; } SV_ClientPrintf("Client ping times:\n"); for (int i = 0; i < svs.maxclients; i++) { client_t client = svs.clients[i]; if (!client.active) { continue; } float total = 0; for (int j = 0; j < q_shared.NUM_PING_TIMES; j++) { total += client.ping_times[j]; } total /= q_shared.NUM_PING_TIMES; SV_ClientPrintf("{0,4} {1}\n", (int)(total * 1000), client.name); } }
static void PF_lightstyle() { int style = (int)G_FLOAT(q_shared.OFS_PARM0); // Uze: ??? string val = G_STRING(q_shared.OFS_PARM1); // change the string in sv sv.lightstyles[style] = val; // send message to all clients on this server if (!sv.active) { return; } for (int j = 0; j < svs.maxclients; j++) { client_t client = svs.clients[j]; if (client.active || client.spawned) { client.message.MSG_WriteChar(q_shared.svc_lightstyle); client.message.MSG_WriteChar(style); client.message.MSG_WriteString(val); } } }
/* * ======================= * SV_SendClientDatagram * ======================= */ static bool SV_SendClientDatagram(client_t client) { byte[] buf = new byte[quakedef.MAX_DATAGRAM]; common.sizebuf_t msg = new common.sizebuf_t(); msg.data = buf; msg.maxsize = buf.Length; msg.cursize = 0; common.MSG_WriteByte(msg, net.svc_time); common.MSG_WriteFloat(msg, sv.time); // add the client specific data to the datagram SV_WriteClientdataToMessage(client.edict, msg); SV_WriteEntitiesToClient(client.edict, msg); // copy the server datagram if there is space if (msg.cursize + sv.datagram.cursize < msg.maxsize) { common.SZ_Write(msg, sv.datagram.data, sv.datagram.cursize); } // send the datagram if (net.NET_SendUnreliableMessage(client.netconnection, msg) == -1) { return(false); } return(true); }
public static void SV_ReadClientMove(ref usercmd_t move) { client_t client = host_client; // read ping time client.ping_times[client.num_pings % q_shared.NUM_PING_TIMES] = (float)(sv.time - Reader.MSG_ReadFloat()); client.num_pings++; // read current angles Vector3 angles = Reader.ReadAngles(); Mathlib.Copy(ref angles, out client.edict.v.v_angle); // read movement move.forwardmove = Reader.MSG_ReadShort(); move.sidemove = Reader.MSG_ReadShort(); move.upmove = Reader.MSG_ReadShort(); // read buttons int bits = Reader.MSG_ReadByte(); client.edict.v.button0 = bits & 1; client.edict.v.button2 = (bits & 2) >> 1; int i = Reader.MSG_ReadByte(); if (i != 0) { client.edict.v.impulse = i; } }
public static bool SV_SendClientDatagram(client_t client) { MsgWriter msg = new MsgWriter(q_shared.MAX_DATAGRAM); // Uze todo: make static? msg.MSG_WriteByte(q_shared.svc_time); msg.MSG_WriteFloat((float)sv.time); // add the client specific data to the datagram SV_WriteClientdataToMessage(client.edict, msg); SV_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) { SV_DropClient(true);// if the message couldn't send, kick off return(false); } return(true); }
/// <summary> /// SV_SendClientDatagram /// </summary> private bool SendClientDatagram(client_t client) { var msg = new MessageWriter(QDef.MAX_DATAGRAM); // Uze todo: make static? msg.WriteByte(ProtocolDef.svc_time); msg.WriteFloat(( float )this.sv.time); // add the client specific data to the datagram this.WriteClientDataToMessage(client.edict, msg); this.WriteEntitiesToClient(client.edict, msg); // copy the server datagram if there is space if (msg.Length + this.sv.datagram.Length < msg.Capacity) { msg.Write(this.sv.datagram.Data, 0, this.sv.datagram.Length); } // send the datagram if (this.Host.Network.SendUnreliableMessage(client.netconnection, msg) == -1) { this.DropClient(true); // if the message couldn't send, kick off return(false); } return(true); }
/* * ============================================================================= * * EVENT MESSAGES * * ============================================================================= */ /* * ============================================================================== * * CLIENT SPAWNING * * ============================================================================== */ /* * ================ * 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. * ================ */ static void SV_SendServerinfo(client_t client) { int i; string s; string message; common.MSG_WriteByte(client.message, net.svc_print); message = "\u0002\nVERSION " + quakedef.VERSION + " SERVER (" + prog.pr_crc + " CRC)"; common.MSG_WriteString(client.message, message); common.MSG_WriteByte(client.message, net.svc_serverinfo); common.MSG_WriteLong(client.message, net.PROTOCOL_VERSION); common.MSG_WriteByte(client.message, svs.maxclients); if (host.coop.value == 0 && host.deathmatch.value != 0) { common.MSG_WriteByte(client.message, net.GAME_DEATHMATCH); } else { common.MSG_WriteByte(client.message, net.GAME_COOP); } //sprintf(message, pr_strings + sv.edicts->v.message); message = prog.pr_string(sv.edicts[0].v.message); common.MSG_WriteString(client.message, message); for (i = 1, s = sv.model_precache[i]; s != null; i++) { s = sv.model_precache[i]; common.MSG_WriteString(client.message, s); } common.MSG_WriteByte(client.message, 0); for (i = 1, s = sv.sound_precache[i]; s != null; i++) { s = sv.sound_precache[i]; common.MSG_WriteString(client.message, s); } common.MSG_WriteByte(client.message, 0); // send music common.MSG_WriteByte(client.message, net.svc_cdtrack); common.MSG_WriteByte(client.message, (int)sv.edicts[0].v.sounds); common.MSG_WriteByte(client.message, (int)sv.edicts[0].v.sounds); // set view common.MSG_WriteByte(client.message, net.svc_setview); common.MSG_WriteShort(client.message, prog.NUM_FOR_EDICT(client.edict)); common.MSG_WriteByte(client.message, net.svc_signonnum); common.MSG_WriteByte(client.message, 1); client.sendsignon = true; client.spawned = false; // need prespawn, spawn, etc }
public static void SV_DropClient(bool crash) { client_t client = host_client; if (!crash) { // send any final messages (don't check for errors) if (NET_CanSendMessage(client.netconnection)) { MsgWriter msg = client.message; msg.MSG_WriteByte(q_shared.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 = pr_global_struct.self; pr_global_struct.self = EDICT_TO_PROG(client.edict); PR_ExecuteProgram(pr_global_struct.ClientDisconnect); pr_global_struct.self = saveSelf; } Con_DPrintf("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 < svs.maxclients; i++) { client_t cl = svs.clients[i]; if (!cl.active) { continue; } cl.message.MSG_WriteByte(q_shared.svc_updatename); cl.message.MSG_WriteByte(ClientNum); cl.message.MSG_WriteString(""); cl.message.MSG_WriteByte(q_shared.svc_updatefrags); cl.message.MSG_WriteByte(ClientNum); cl.message.MSG_WriteShort(0); cl.message.MSG_WriteByte(q_shared.svc_updatecolors); cl.message.MSG_WriteByte(ClientNum); cl.message.MSG_WriteByte(0); } }
void ClientThink(client_t cl, Input.UserCommand cmd) { cl.lastUsercmd = cmd; if (cl.state != clientState_t.CS_ACTIVE) return; // may have been kicked during the last usercmd Game.Instance.Client_Think(cl.id); }
/** * Initializes player structures after successfull connection. */ public static void gotnewcl(int i, int challenge, string userinfo, netadr_t adr, int qport) { // build a new connection // accept the new client // this is the only place a client_t is ever initialized SV_MAIN.sv_client = SV_INIT.svs.clients[i]; var edictnum = i + 1; var ent = GameBase.g_edicts[edictnum]; SV_INIT.svs.clients[i].edict = ent; // save challenge for checksumming SV_INIT.svs.clients[i].challenge = challenge; // get the game a chance to reject this connection or modify the // userinfo if (!PlayerClient.ClientConnect(ent, userinfo)) { if (Info.Info_ValueForKey(userinfo, "rejmsg") != null) { Netchan.OutOfBandPrint(Defines.NS_SERVER, adr, "print\n" + Info.Info_ValueForKey(userinfo, "rejmsg") + "\nConnection refused.\n"); } else { Netchan.OutOfBandPrint(Defines.NS_SERVER, adr, "print\nConnection refused.\n"); } Com.DPrintf("Game rejected a connection.\n"); return; } // parse some info from the info strings SV_INIT.svs.clients[i].userinfo = userinfo; SV_MAIN.SV_UserinfoChanged(SV_INIT.svs.clients[i]); // send the connect packet to the client Netchan.OutOfBandPrint(Defines.NS_SERVER, adr, "client_connect"); Netchan.Setup(Defines.NS_SERVER, SV_INIT.svs.clients[i].netchan, adr, qport); SV_INIT.svs.clients[i].state = Defines.cs_connected; SZ.Init(SV_INIT.svs.clients[i].datagram, SV_INIT.svs.clients[i].datagram_buf, SV_INIT.svs.clients[i].datagram_buf.Length); SV_INIT.svs.clients[i].datagram.allowoverflow = true; SV_INIT.svs.clients[i].lastmessage = SV_INIT.svs.realtime; // don't timeout SV_INIT.svs.clients[i].lastconnect = SV_INIT.svs.realtime; Com.DPrintf("new client added.\n"); }
public static void SV_SendNop(client_t client) { MsgWriter msg = new MsgWriter(4); msg.MSG_WriteChar(q_shared.svc_nop); if (NET_SendUnreliableMessage(client.netconnection, msg) == -1) { SV_DropClient(true); // if the message couldn't send, kick off } client.last_message = realtime; }
/// <summary> /// SV_SendNop /// Send a nop message without trashing or sending the accumulated client /// message buffer /// </summary> private void SendNop(client_t client) { var msg = new MessageWriter(4); msg.WriteChar(ProtocolDef.svc_nop); if (this.Host.Network.SendUnreliableMessage(client.netconnection, msg) == -1) { this.DropClient(true); // if the message couldn't send, kick off } client.last_message = this.Host.RealTime; }
/* * ======================= * SV_SendNop * * Send a nop message without trashing or sending the accumulated client * message buffer * ======================= */ static void SV_SendNop(client_t client) { common.sizebuf_t msg = new common.sizebuf_t(); byte[] buf = new byte[4]; msg.data = buf; msg.maxsize = buf.Length; msg.cursize = 0; common.MSG_WriteChar(msg, net.svc_nop); net.NET_SendUnreliableMessage(client.netconnection, msg); client.last_message = host.realtime; }
static void PF_setspawnparms() { edict_t ent = G_EDICT(q_shared.OFS_PARM0); int i = NUM_FOR_EDICT(ent); if (i < 1 || i > svs.maxclients) { PR_RunError("Entity is not a client"); } // copy spawn parms out of the client_t client_t client = svs.clients[i - 1]; pr_global_struct.SetParams(client.spawn_parms); }
/** * Writes a frame to a client system. */ public static void SV_WriteFrameToClient(client_t client, sizebuf_t msg) { //ptr client_frame_t frame, oldframe; int lastframe; //Com.Printf ("%i . %i\n", new // Vargs().add(client.lastframe).add(sv.framenum)); // this is the frame we are creating frame = client.frames[SV_INIT.sv.framenum & Defines.UPDATE_MASK]; if (client.lastframe <= 0) { // client is asking for a retransmit oldframe = null; lastframe = -1; } else if (SV_INIT.sv.framenum - client.lastframe >= Defines.UPDATE_BACKUP - 3) { // client hasn't gotten a good message through in a long time // Com_Printf ("%s: Delta request from out-of-date packet.\n", // client.name); oldframe = null; lastframe = -1; } else { // we have a valid message to delta from oldframe = client.frames[client.lastframe & Defines.UPDATE_MASK]; lastframe = client.lastframe; } MSG.WriteByte(msg, Defines.svc_frame); MSG.WriteLong(msg, SV_INIT.sv.framenum); MSG.WriteLong(msg, lastframe); // what we are delta'ing from MSG.WriteByte(msg, client.surpressCount); // rate dropped packets client.surpressCount = 0; // send over the areabits MSG.WriteByte(msg, frame.areabytes); SZ.Write(msg, frame.areabits, frame.areabytes); // delta encode the playerstate SV_ENTS.SV_WritePlayerstateToClient(oldframe, frame, msg); // delta encode the entities SV_ENTS.SV_EmitPacketEntities(oldframe, frame, msg); }
static void PF_centerprint() { int entnum = NUM_FOR_EDICT(G_EDICT(q_shared.OFS_PARM0)); string s = PF_VarString(1); if (entnum < 1 || entnum > svs.maxclients) { Con_Printf("tried to centerprint to a non-client\n"); return; } client_t client = svs.clients[entnum - 1]; client.message.MSG_WriteChar(q_shared.svc_centerprint); client.message.MSG_WriteString(s); }
static void PF_stuffcmd() { int entnum = NUM_FOR_EDICT(G_EDICT(q_shared.OFS_PARM0)); if (entnum < 1 || entnum > svs.maxclients) { PR_RunError("Parm 0 not a client"); } string str = G_STRING(q_shared.OFS_PARM1); client_t old = host_client; host_client = svs.clients[entnum - 1]; Host_ClientCommands("{0}", str); host_client = old; }
/** * Pull specific info from a newly changed userinfo string into a more C * freindly form. */ public static void SV_UserinfoChanged(client_t cl) { string val; int i; // call prog code to allow overrides PlayerClient.ClientUserinfoChanged(cl.edict, cl.userinfo); // name for C code cl.name = Info.Info_ValueForKey(cl.userinfo, "name"); // mask off high bit //TODO: masking for german umlaute //for (i=0 ; i<sizeof(cl.name) ; i++) // cl.name[i] &= 127; // rate command val = Info.Info_ValueForKey(cl.userinfo, "rate"); if (val.Length > 0) { i = Lib.atoi(val); cl.rate = i; if (cl.rate < 100) { cl.rate = 100; } if (cl.rate > 15000) { cl.rate = 15000; } } else { cl.rate = 5000; } // msg command val = Info.Info_ValueForKey(cl.userinfo, "msg"); if (val.Length > 0) { cl.messagelevel = Lib.atoi(val); } }
public static void Host_Tell_f() { if (cmd_source == cmd_source_t.src_command) { Cmd_ForwardToServer(); return; } if (cmd_argc < 3) { return; } string text = host_client.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_client; for (int j = 0; j < svs.maxclients; j++) { client_t client = svs.clients[j]; if (!client.active || !client.spawned) { continue; } if (client.name == Cmd_Argv(1)) { continue; } host_client = client; SV_ClientPrintf(text); break; } host_client = save; }
public static void SV_ConnectClient(int clientnum) { client_t client = svs.clients[clientnum]; Con_DPrintf("Client {0} connected\n", client.netconnection.address); int edictnum = clientnum + 1; edict_t ent = EDICT_NUM(edictnum); // set up the client_t qsocket_t netconnection = client.netconnection; float[] spawn_parms = new float[q_shared.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 PR_ExecuteProgram(pr_global_struct.SetNewParms); AssignGlobalSpawnparams(client); } SV_SendServerinfo(client); }
void ClientEnterWorld(client_t cl, Input.UserCommand cmd) { Common.Instance.WriteLine("Going from PRIMED to ACTIVE for {0}", cl.name); cl.state = clientState_t.CS_ACTIVE; // resend all configstrings using the cs commands since these are // no longer sent when the client is CS_PRIMED UpdateConfigStrings(cl); // set up the entity for the client int clientNum = cl.id; sharedEntity ent = sv.gentities[clientNum]; ent.s.number = clientNum; cl.gentity = ent; cl.deltaMessage = -1; cl.nextSnapshotTime = (int)time; // generate a snapshot immediately cl.lastUsercmd = cmd; Game.Instance.Client_Begin(clientNum); }
/** * Called when the player is totally leaving the server, either willingly or * unwillingly. This is NOT called if the entire server is quiting or * crashing. */ public static void SV_DropClient(client_t drop) { // add the disconnect MSG.WriteByte(drop.netchan.message, Defines.svc_disconnect); if (drop.state == Defines.cs_spawned) { // call the prog function for removing a client // this will remove the body, among other things PlayerClient.ClientDisconnect(drop.edict); } if (drop.download != null) { FS.FreeFile(drop.download); drop.download = null; } drop.state = Defines.cs_zombie; // become free in a few seconds drop.name = ""; }
public static void AssignGlobalSpawnparams(client_t client) { client.spawn_parms[0] = pr_global_struct.parm1; client.spawn_parms[1] = pr_global_struct.parm2; client.spawn_parms[2] = pr_global_struct.parm3; client.spawn_parms[3] = pr_global_struct.parm4; client.spawn_parms[4] = pr_global_struct.parm5; client.spawn_parms[5] = pr_global_struct.parm6; client.spawn_parms[6] = pr_global_struct.parm7; client.spawn_parms[7] = pr_global_struct.parm8; client.spawn_parms[8] = pr_global_struct.parm9; client.spawn_parms[9] = pr_global_struct.parm10; client.spawn_parms[10] = pr_global_struct.parm11; client.spawn_parms[11] = pr_global_struct.parm12; client.spawn_parms[12] = pr_global_struct.parm13; client.spawn_parms[13] = pr_global_struct.parm14; client.spawn_parms[14] = pr_global_struct.parm15; client.spawn_parms[15] = pr_global_struct.parm16; }
private void AssignGlobalSpawnparams(client_t client) { client.spawn_parms[0] = this.Host.Programs.GlobalStruct.parm1; client.spawn_parms[1] = this.Host.Programs.GlobalStruct.parm2; client.spawn_parms[2] = this.Host.Programs.GlobalStruct.parm3; client.spawn_parms[3] = this.Host.Programs.GlobalStruct.parm4; client.spawn_parms[4] = this.Host.Programs.GlobalStruct.parm5; client.spawn_parms[5] = this.Host.Programs.GlobalStruct.parm6; client.spawn_parms[6] = this.Host.Programs.GlobalStruct.parm7; client.spawn_parms[7] = this.Host.Programs.GlobalStruct.parm8; client.spawn_parms[8] = this.Host.Programs.GlobalStruct.parm9; client.spawn_parms[9] = this.Host.Programs.GlobalStruct.parm10; client.spawn_parms[10] = this.Host.Programs.GlobalStruct.parm11; client.spawn_parms[11] = this.Host.Programs.GlobalStruct.parm12; client.spawn_parms[12] = this.Host.Programs.GlobalStruct.parm13; client.spawn_parms[13] = this.Host.Programs.GlobalStruct.parm14; client.spawn_parms[14] = this.Host.Programs.GlobalStruct.parm15; client.spawn_parms[15] = this.Host.Programs.GlobalStruct.parm16; }
public static void SV_UpdateToReliableMessages() { // check for changes to be sent over the reliable streams for (int i = 0; i < svs.maxclients; i++) { host_client = svs.clients[i]; if (host_client.old_frags != host_client.edict.v.frags) { for (int j = 0; j < svs.maxclients; j++) { client_t client = svs.clients[j]; if (!client.active) { continue; } client.message.MSG_WriteByte(q_shared.svc_updatefrags); client.message.MSG_WriteByte(i); client.message.MSG_WriteShort((int)host_client.edict.v.frags); } host_client.old_frags = (int)host_client.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(); }
/* * ================= * SV_SendServerCommand * * Sends a reliable command string to be interpreted by * the client game module: "cp", "print", "chat", etc * A NULL client will broadcast to all clients * ================= */ static void SV_SendServerCommand(client_t cl, string fmt, params object[] args) { client_t client; int j; string message = va(fmt, args); if (cl != null) { SV_AddServerCommand(cl, message); return; } // hack to echo broadcast prints to console if (com_dedicated.integer && !strncmp(message, "print", 5)) { Com_Printf("broadcast: %s\n", SV_ExpandNewlines(message)); } // send the data to all relevent clients for (j = 0, client = svs.clients; j < sv_maxclients.integer; j++, client++) { if (client->state < CS_PRIMED) { continue; } // Ridah, don't need to send messages to AI if (client->gentity && client.gentity.r.svFlags & SVF_CASTAI) { continue; } // done. SV_AddServerCommand(client, message); } }
/* ================== SV_DirectConnect A "connect" OOB command has been received ================== */ void DirectConnect(IPEndPoint from, string[] tokens) { // Check banlist string userinfo = tokens[1]; string schal = Info.ValueForKey(userinfo, "challenge"); int challenge = int.Parse(schal); int qport = int.Parse(Info.ValueForKey(userinfo, "qport")); int i; // quick reject for (i = 0; i < clients.Count; i++) { client_t cl = clients[i]; if (cl.state == clientState_t.CS_FREE) continue; if (IPAddress.Equals(from.Address, cl.netchan.remoteAddress.Address) && (qport == cl.netchan.qport || from.Port == cl.netchan.remoteAddress.Port)) { if ((time - cl.lastConnectTime) < sv_reconnectlimit.Integer * 1000f) { Common.Instance.WriteLine("DirConnect: ({0})=>Reconnect rejected : too soon.", from.Address.ToString()); return; } break; } } string ip = from.Address.ToString(); Info.SetValueForKey(userinfo, "ip", ip); // see if the challenge is valid (LAN clients don't need to challenge) if (!Net.Instance.IsLanAddress(from.Address)) { for (i = 0; i < challenges.Count; i++) { if (IPEndPoint.Equals(from, challenges[i].adr)) { if (challenge == challenges[i].challenge) break; else Common.Instance.WriteLine("FIXFIX"); } } // No challenge found if (i == challenges.Count) { Net.Instance.OutOfBandMessage(Net.NetSource.SERVER, from, "print\nNo or bad challenge for your address\n"); return; } // Challenge found challenge_t chal = challenges[i]; if (chal.wasrefused) { // Return silently, so that error messages written by the server keep being displayed. return; } int ping = (int)time - chal.pingTime; // never reject a LAN client based on ping if (!Net.Instance.IsLanAddress(from.Address)) { if (sv_minping.Integer > 0 && ping < sv_minping.Value) { Net.Instance.OutOfBandMessage(Net.NetSource.SERVER, from, "print\nServer is for high pings only.\n"); Common.Instance.WriteLine("Client rejected due to low ping"); chal.wasrefused = true; return; } if (sv_maxping.Integer > 0 && ping > sv_maxping.Value) { Net.Instance.OutOfBandMessage(Net.NetSource.SERVER, from, "print\nServer is for low pings only.\n"); Common.Instance.WriteLine("Client rejected due to high ping"); chal.wasrefused = true; return; } } Common.Instance.WriteLine("Client {0} connecting with {0} challenge ping", i, ping); chal.connected = true; } bool gotcl = false; client_t newCl = new client_t(); // if there is already a slot for this ip, reuse it for (i = 0; i < clients.Count; i++) { client_t cl = clients[i]; if (cl.state == clientState_t.CS_FREE) continue; if (IPAddress.Equals(from.Address, cl.netchan.remoteAddress.Address) && (qport == cl.netchan.qport || from.Port == cl.netchan.remoteAddress.Port)) { Common.Instance.WriteLine("{0}:reconnect", from.Address.ToString()); newCl = cl; gotcl = true; break; } } if (!gotcl) { newCl = null; // find a client slot for (i = 0; i < clients.Count; i++) { client_t cl = clients[i]; if (cl.state == clientState_t.CS_FREE) { newCl = cl; break; } } if (newCl == null && i < sv_maxclients.Integer) { newCl = new client_t(); clients.Add(newCl); i = clients.Count - 1; newCl.id = i; } if (newCl == null) { Net.Instance.OutOfBandMessage(Net.NetSource.SERVER, from, "print\nServer is full\n"); Common.Instance.WriteLine("Rejected a connection"); return; } // we got a newcl, so reset the reliableSequence and reliableAcknowledge newCl.reliableAcknowledge = 0; newCl.reliableSequence = 0; } // build a new connection // accept the new client // this is the only place a client_t is ever initialized if (newCl.id != i) { int test = 2; } sharedEntity ent = sv.gentities[i]; newCl.gentity = ent; // save the challenge newCl.challenge = challenge; // save the address newCl.netchan = Net.Instance.NetChan_Setup(Net.NetSource.SERVER, from, qport); // save the userinfo newCl.userinfo = userinfo; // get the game a chance to reject this connection or modify the userinfo string denied = Game.Instance.Client_Connect(i, true); if (denied != null) { Net.Instance.OutOfBandMessage(Net.NetSource.SERVER, from, string.Format("print\n{0}\n", denied)); Common.Instance.WriteLine("Game rejected a connection: {0}", denied); return; } UserInfoChanged(newCl); // send the connect packet to the client Net.Instance.OutOfBandMessage(Net.NetSource.SERVER, from, "connectResponse"); Common.Instance.WriteLine("Going from CS_FREE to CS_CONNECTED for {0}", i); newCl.state = clientState_t.CS_CONNECTED; newCl.nextSnapshotTime = (int)time; newCl.lastPacketTime = time; newCl.lastConnectTime = (int)time; // when we receive the first packet from the client, we will // notice that it is from a different serverid and that the // gamestate message was not just sent, forcing a retransmit newCl.gamestateMessageNum = -1; }
/* ======================= SV_SendNop Send a nop message without trashing or sending the accumulated client message buffer ======================= */ static void SV_SendNop(client_t client) { common.sizebuf_t msg = new common.sizebuf_t(); byte[] buf = new byte[4]; msg.data = buf; msg.maxsize = buf.Length; msg.cursize = 0; common.MSG_WriteChar (msg, net.svc_nop); net.NET_SendUnreliableMessage (client.netconnection, msg); client.last_message = host.realtime; }
public static void Host_Say(bool teamonly) { bool fromServer = false; if (cmd_source == cmd_source_t.src_command) { if (cls.state == cactive_t.ca_dedicated) { fromServer = true; teamonly = false; } else { Cmd_ForwardToServer(); return; } } if (cmd_argc < 2) { return; } client_t save = host_client; 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 + "<" + hostname.@string + "> "; } text += p + "\n"; for (int j = 0; j < svs.maxclients; j++) { client_t client = svs.clients[j]; if (client == null || !client.active || !client.spawned) { continue; } if (teamplay.value != 0 && teamonly && client.edict.v.team != save.edict.v.team) { continue; } host_client = client; SV_ClientPrintf(text); } host_client = save; }
void UpdateUserInfo(client_t cl, string[] tokens) { if (tokens.Length < 0) { SendServerCommand(cl, "print \"Userinfo not changed: needs argument\"\n"); } cl.userinfo = tokens[1]; UserInfoChanged(cl); Game.Instance.ClientUserInfoChanged(cl.id); }
/* ================== SV_UserMove The message usually contains all the movement commands that were in the last three packets, so that the information in dropped packets can be recovered. On very fast clients, there may be multiple usercmd packed into each of the backup packets. ================== */ void UserMove(client_t cl, NetBuffer buf, bool deltaCompressed) { if (deltaCompressed) cl.deltaMessage = cl.messageAcknowledge; else cl.deltaMessage = -1; int cmdCount = buf.ReadByte(); if (cmdCount < 1) { Common.Instance.WriteLine("cmdCount < 1"); return; } if (cmdCount > 32) { Common.Instance.WriteLine("cmdCount > 32"); return; } List<Input.UserCommand> cmds = new List<Input.UserCommand>(); Input.UserCommand oldcmd = new Input.UserCommand(); for (int i = 0; i < cmdCount; i++) { Input.UserCommand cmd = Input.Instance.MSG_ReadDeltaUsercmdKey(buf, ref oldcmd); cmds.Add(cmd); oldcmd = cmd; } // save time for ping calculation cl.frames[cl.messageAcknowledge & 31].messageAcked = (int)time; // if this is the first usercmd we have received // this gamestate, put the client into the world if (cl.state == clientState_t.CS_PRIMED) { ClientEnterWorld(cl, cmds[cmds.Count-1]); // the moves can be processed normaly } if (cl.state != clientState_t.CS_ACTIVE) { cl.deltaMessage = -1; return; } // usually, the first couple commands will be duplicates // of ones we have previously received, but the servertimes // in the commands will cause them to be immediately discarded for (int i = 0; i < cmdCount; i++) { // if this is a cmd from before a map_restart ignore it if (cmds[i].serverTime > cmds[cmdCount - 1].serverTime) { continue; } // don't execute if this is an old cmd which is already executed // these old cmds are included when cl_packetdup > 0 if (cmds[i].serverTime <= cl.lastUsercmd.serverTime) { continue; } ClientThink(cl, cmds[i]); } }
void ExecuteClientCommand(client_t cl, string cmd) { string[] tokens = Commands.TokenizeString(cmd); // see if it is a server level command Common.Instance.WriteLine("ExecuteClientCommand: {0}", tokens[0]); switch (tokens[0]) { case "userinfo": UpdateUserInfo(cl, tokens); break; case "disconnect": DropClient(cl, "disconnected"); break; default: if (sv.state == serverState_t.SS_GAME) Game.Instance.Client_Command(cl.id, tokens); break; } //string[] ucmds = new string[] {"userinfo", "disconnect", "cp", "vdr", "download", "nextdl", "stopdl", "donedl" }; }
// Called when clients leaves server completely. Not called if server is crashing or quitting, handled in FinalMessage() private void DropClient(client_t cl, string reason) { if (cl.state == clientState_t.CS_ZOMBIE) return; // already dropped // see if we already have a challenge for this ip int i; IPEndPoint clEndPoint = cl.netchan.remoteAddress; for (i = 0; i < challenges.Count; i++) { if (clEndPoint.Equals(challenges[i])) break; } if (i != challenges.Count) challenges.RemoveAt(i); // Remove old challenge // tell everyone why they got dropped SendServerCommand(null, string.Format("print \"{0} {1}\"\n", cl.name, reason)); // call the prog function for removing a client // this will remove the body, among other things Game.Instance.Client_Disconnect(cl.gentity.s.number); // add the disconnect command SendServerCommand(cl, string.Format("disconnect \"{0}\"", reason)); // nuke user info SetUserInfo(cl, ""); cl.state = clientState_t.CS_ZOMBIE; // become free in a few seconds }
public static void SV_SendServerinfo(client_t client) { MsgWriter writer = client.message; writer.MSG_WriteByte(q_shared.svc_print); writer.MSG_WriteString(String.Format("{0}\nVERSION {1,4:F2} SERVER ({2} CRC)", (char)2, q_shared.VERSION, pr_crc)); writer.MSG_WriteByte(q_shared.svc_serverinfo); writer.MSG_WriteLong(q_shared.PROTOCOL_VERSION); writer.MSG_WriteByte(svs.maxclients); if (!(coop.value != 0) && deathmatch.value != 0) { writer.MSG_WriteByte(q_shared.GAME_DEATHMATCH); } else { writer.MSG_WriteByte(q_shared.GAME_COOP); } string message = GetString(sv.edicts[0].v.message); writer.MSG_WriteString(message); for (int i = 1; i < sv.model_precache.Length; i++) { string tmp = sv.model_precache[i]; if (String.IsNullOrEmpty(tmp)) { break; } writer.MSG_WriteString(tmp); } writer.MSG_WriteByte(0); for (int i = 1; i < sv.sound_precache.Length; i++) { string tmp = sv.sound_precache[i]; if (tmp == null) { break; } writer.MSG_WriteString(tmp); } writer.MSG_WriteByte(0); // send music writer.MSG_WriteByte(q_shared.svc_cdtrack); writer.MSG_WriteByte((int)sv.edicts[0].v.sounds); writer.MSG_WriteByte((int)sv.edicts[0].v.sounds); // set view writer.MSG_WriteByte(q_shared.svc_setview); writer.MSG_WriteShort(NUM_FOR_EDICT(client.edict)); writer.MSG_WriteByte(q_shared.svc_signonnum); writer.MSG_WriteByte(1); client.sendsignon = true; client.spawned = false; // need prespawn, spawn, etc }
void WriteSnapshotToClient(client_t cl, NetBuffer msg) { // this is the snapshot we are creating clientSnapshot_t frame = cl.frames[cl.netchan.outgoingSequence & 31]; // try to use a previous frame as the source for delta compressing the snapshot clientSnapshot_t oldframe = null; int lastframe; if (cl.deltaMessage <= 0 || cl.state != clientState_t.CS_ACTIVE) { // client is asking for a retransmit oldframe = null; lastframe = 0; } else if (cl.netchan.outgoingSequence - cl.deltaMessage >= (32 - 3)) { // client hasn't gotten a good message through in a long time Common.Instance.WriteLine("{0}: Delta request from out of date packet.", cl.name); oldframe = null; lastframe = 0; } else { // we have a valid snapshot to delta from oldframe = cl.frames[cl.deltaMessage & 31]; lastframe = cl.netchan.outgoingSequence - cl.deltaMessage; // the snapshot's entities may still have rolled off the buffer, though if (oldframe.first_entity <= nextSnapshotEntities - numSnapshotEntities) { Common.Instance.WriteLine("{0}: Delta request from out of date entities.", cl.name); oldframe = null; lastframe = 0; } } msg.Write((byte)svc_ops_e.svc_snapshot); // send over the current server time so the client can drift // its view of time to try to match if (cl.oldServerTime > 0) { // The server has not yet got an acknowledgement of the // new gamestate from this client, so continue to send it // a time as if the server has not restarted. Note from // the client's perspective this time is strictly speaking // incorrect, but since it'll be busy loading a map at // the time it doesn't really matter. msg.Write((int)(sv.time + cl.oldServerTime)); } else msg.Write((int)sv.time); // what we are delta'ing from msg.Write((byte)lastframe); int snapFlags = snapFlagServerBit; if (cl.rateDelayed) snapFlags |= SNAPFLAG_RATE_DELAYED; if (cl.state != clientState_t.CS_ACTIVE) snapFlags |= SNAPFLAG_NOT_ACTIVE; msg.Write((byte)snapFlags); // send over the areabits msg.Write((byte)frame.areabytes); msg.Write(frame.areabits); // delta encode the playerstate if (oldframe != null) Net.WriteDeltaPlayerstate(msg, oldframe.ps, frame.ps); else Net.WriteDeltaPlayerstate(msg, null, frame.ps); // delta encode the entities EmitPacketEntities(oldframe, frame, msg); }
/* ================== SV_UpdateServerCommandsToClient (re)send all server commands the client hasn't acknowledged yet ================== */ void UpdateServerCommandsToClient(client_t cl, NetBuffer msg) { // write any unacknowledged serverCommands for (int i = cl.reliableAcknowledge+1; i <= cl.reliableSequence; i++) { msg.Write((byte)svc_ops_e.svc_serverCommand); msg.Write(i); msg.Write(cl.reliableCommands[i & 63]); } cl.reliableSent = cl.reliableSequence; }
void SendMessageToClient(NetBuffer msg, client_t client) { // record information about the message client.frames[client.netchan.outgoingSequence & 31].messageSize = msg.LengthBytes; client.frames[client.netchan.outgoingSequence & 31].messageSent = (int)time; client.frames[client.netchan.outgoingSequence & 31].messageAcked = -1; // send the datagram NetChan_Transmit(client, msg); // set nextSnapshotTime based on rate and requested number of updates int rateMsec = RateMsec(client, msg.LengthBytes); if (rateMsec < client.snapshotMsec * Common.Instance.timescale.Value) { // never send more packets than this, no matter what the rate is at rateMsec = (int)(client.snapshotMsec * Common.Instance.timescale.Value); client.rateDelayed = false; } else client.rateDelayed = true; client.nextSnapshotTime = (int)(time + (float)rateMsec * Common.Instance.timescale.Value); // don't pile up empty snapshots while connecting if (client.state != clientState_t.CS_ACTIVE) { // a gigantic connection message may have already put the nextSnapshotTime // more than a second away, so don't shorten it // do shorten if client is downloading if (client.nextSnapshotTime < time + (int)(1000 * Common.Instance.timescale.Value)) client.nextSnapshotTime = (int)(time + 1000 * Common.Instance.timescale.Value); } }
void SendClientSnapshot(client_t client, int index) { // build the snapshot BuildClientSnapshot(client, index); NetBuffer msg = new NetBuffer(); // let the client know which reliable clientCommands we have received msg.Write(client.lastClientCommand); // (re)send any reliable server commands UpdateServerCommandsToClient(client, msg); // send over all the relevant entityState_t // and the playerState_t WriteSnapshotToClient(client, msg); SendMessageToClient(msg, client); }
/* ================ SV_SendClientGameState Sends the first message from the server to a connected client. This will be sent on the initial connection and upon each new map load. It will be resent if the client acknowledges a later message but has the wrong gamestate. ================ */ void SendClientGameState(client_t cl) { Common.Instance.WriteLine("SendClientGameState for {0}", cl.name); Common.Instance.WriteLine("Going from CONNECTED to PRIMED for {0}", cl.name); cl.state = clientState_t.CS_PRIMED; cl.pureAuthentic = 0; cl.gotCP = false; // when we receive the first packet from the client, we will // notice that it is from a different serverid and that the // gamestate message was not just sent, forcing a retransmit cl.gamestateMessageNum = cl.netchan.outgoingSequence; NetBuffer msg = new NetBuffer(); // NOTE, MRE: all server->client messages now acknowledge // let the client know which reliable clientCommands we have received msg.Write(cl.lastClientCommand); // send any server commands waiting to be sent first. // we have to do this cause we send the client->reliableSequence // with a gamestate and it sets the clc.serverCommandSequence at // the client side UpdateServerCommandsToClient(cl, msg); // send the gamestate msg.Write((byte)svc_ops_e.svc_gamestate); msg.Write(cl.reliableSequence); // write the configstrings foreach (int i in sv.configstrings.Keys) { msg.Write((byte)svc_ops_e.svc_configstring); msg.Write((short)i); msg.Write(sv.configstrings[i]); } // write the baselines Common.entityState_t nullstate = new Common.entityState_t(); for (int i = 0; i < 1024; i++) { Common.entityState_t bases = sv.svEntities[i].baseline; if (bases == null || bases.number <= 0) continue; msg.Write((byte)svc_ops_e.svc_baseline); Net.Instance.MSG_WriteDeltaEntity(msg, ref nullstate, ref bases, true); } msg.Write((byte)svc_ops_e.svc_EOF); msg.Write(cl.id); SendMessageToClient(msg, cl); }
/// <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> private void SendServerInfo(client_t client) { var writer = client.message; writer.WriteByte(ProtocolDef.svc_print); writer.WriteString(string.Format("{0}\nVERSION {1,4:F2} SERVER ({2} CRC)", ( char )2, QDef.VERSION, this.Host.Programs.Crc)); writer.WriteByte(ProtocolDef.svc_serverinfo); writer.WriteLong(ProtocolDef.PROTOCOL_VERSION); writer.WriteByte(this.svs.maxclients); if (!this.Host.Cvars.Coop.Get <bool>() && this.Host.Cvars.Deathmatch.Get <int>() != 0) { writer.WriteByte(ProtocolDef.GAME_DEATHMATCH); } else { writer.WriteByte(ProtocolDef.GAME_COOP); } var message = this.Host.Programs.GetString(this.sv.edicts[0].v.message); writer.WriteString(message); for (var i = 1; i < this.sv.model_precache.Length; i++) { var tmp = this.sv.model_precache[i]; if (string.IsNullOrEmpty(tmp)) { break; } writer.WriteString(tmp); } writer.WriteByte(0); for (var i = 1; i < this.sv.sound_precache.Length; i++) { var tmp = this.sv.sound_precache[i]; if (tmp == null) { break; } writer.WriteString(tmp); } writer.WriteByte(0); // send music writer.WriteByte(ProtocolDef.svc_cdtrack); writer.WriteByte(( int )this.sv.edicts[0].v.sounds); writer.WriteByte(( int )this.sv.edicts[0].v.sounds); // set view writer.WriteByte(ProtocolDef.svc_setview); writer.WriteShort(this.NumForEdict(client.edict)); writer.WriteByte(ProtocolDef.svc_signonnum); writer.WriteByte(1); client.sendsignon = true; client.spawned = false; // need prespawn, spawn, etc }
void NetChan_Transmit(client_t cl, NetBuffer msg) { msg.Write((byte)svc_ops_e.svc_EOF); Net.Instance.NetChan_Transmit(cl.netchan, msg); }
public void SendServerCommand(client_t cl, string fmt) { // Fix to http://aluigi.altervista.org/adv/q3msgboom-adv.txt // The actual cause of the bug is probably further downstream // and should maybe be addressed later, but this certainly // fixes the problem for now if (fmt.Length > 1022) { Common.Instance.WriteLine("SendServerCommand: too long :("); return; } if (cl != null) { AddServerCommand(cl, fmt); return; } // send the data to all relevent clients foreach (client_t client in clients) { AddServerCommand(client, fmt); } }
//public bool GetEntityToken(ref string result) //{ // string s = Common.Parse(sv.entityParseString, sv.entityParsePoint); // result = s; // if (sv.entityParsePoint >= sv.entityParseString.Length) // return false; // else // return true; //} void UserInfoChanged(client_t cl) { cl.name = Info.ValueForKey(cl.userinfo, "name"); string val; // rate command // if the client is on the same subnet as the server and we aren't running an // internet public server, assume they don't need a rate choke if (Net.Instance.IsLanAddress(cl.netchan.remoteAddress.Address)) { cl.rate = 99999; } else { cl.rate = 25000; val = Info.ValueForKey(cl.userinfo, "rate"); if (val != null) { int i; if (int.TryParse(val, out i)) { cl.rate = i; if (cl.rate < 1000) cl.rate = 1000; else if (cl.rate > 90000) cl.rate = 90000; } else cl.rate = 25000; } } val = Info.ValueForKey(cl.userinfo, "handicap"); if (val != null && val.Length > 0) { int handi; if (int.TryParse(val, out handi) && handi <= 0 && handi > 100) cl.userinfo = Info.SetValueForKey(cl.userinfo, "handicap", "100"); } // snaps command val = Info.ValueForKey(cl.userinfo, "cl_updaterate"); if (val != null && val.Length > 0) { int i = 1; int.TryParse(val, out i); if (i < 1) i = 1; else if (i > sv_fps.Integer) i = sv_fps.Integer; cl.snapshotMsec = (int)(1000f / i); } else { cl.snapshotMsec = 50; } }
public static void Host_Spawn_f() { if (cmd_source == cmd_source_t.src_command) { Con_Printf("spawn is not valid from the console\n"); return; } if (host_client.spawned) { Con_Printf("Spawn not valid -- allready spawned\n"); return; } edict_t ent; // run the entrance script if (sv.loadgame) { // loaded games are fully inited allready // if this is the last client to be connected, unpause sv.paused = false; } else { // set up the edict ent = host_client.edict; ent.Clear(); //memset(&ent.v, 0, progs.entityfields * 4); ent.v.colormap = NUM_FOR_EDICT(ent); ent.v.team = (host_client.colors & 15) + 1; ent.v.netname = ED_NewString(host_client.name); // copy spawn parms out of the client_t pr_global_struct.SetParams(host_client.spawn_parms); // call the spawn function pr_global_struct.time = (float)sv.time; pr_global_struct.self = EDICT_TO_PROG(sv_player); PR_ExecuteProgram(pr_global_struct.ClientConnect); if ((Sys_FloatTime() - host_client.netconnection.connecttime) <= sv.time) { Con_DPrintf("{0} entered the game\n", host_client.name); } PR_ExecuteProgram(pr_global_struct.PutClientInServer); } // send all current names, colors, and frag counts MsgWriter msg = host_client.message; msg.Clear(); // send time of update msg.MSG_WriteByte(q_shared.svc_time); msg.MSG_WriteFloat((float)sv.time); for (int i = 0; i < svs.maxclients; i++) { client_t client = svs.clients[i]; msg.MSG_WriteByte(q_shared.svc_updatename); msg.MSG_WriteByte(i); msg.MSG_WriteString(client.name); msg.MSG_WriteByte(q_shared.svc_updatefrags); msg.MSG_WriteByte(i); msg.MSG_WriteShort(client.old_frags); msg.MSG_WriteByte(q_shared.svc_updatecolors); msg.MSG_WriteByte(i); msg.MSG_WriteByte(client.colors); } // send all current light styles for (int i = 0; i < q_shared.MAX_LIGHTSTYLES; i++) { msg.MSG_WriteByte(q_shared.svc_lightstyle); msg.MSG_WriteByte((char)i); msg.MSG_WriteString(sv.lightstyles[i]); } // // send some stats // msg.MSG_WriteByte(q_shared.svc_updatestat); msg.MSG_WriteByte(q_shared.STAT_TOTALSECRETS); msg.MSG_WriteLong((int)pr_global_struct.total_secrets); msg.MSG_WriteByte(q_shared.svc_updatestat); msg.MSG_WriteByte(q_shared.STAT_TOTALMONSTERS); msg.MSG_WriteLong((int)pr_global_struct.total_monsters); msg.MSG_WriteByte(q_shared.svc_updatestat); msg.MSG_WriteByte(q_shared.STAT_SECRETS); msg.MSG_WriteLong((int)pr_global_struct.found_secrets); msg.MSG_WriteByte(q_shared.svc_updatestat); msg.MSG_WriteByte(q_shared.STAT_MONSTERS); msg.MSG_WriteLong((int)pr_global_struct.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 = EDICT_NUM(1 + ClientNum); msg.MSG_WriteByte(q_shared.svc_setangle); msg.MSG_WriteAngle(ent.v.angles.x); msg.MSG_WriteAngle(ent.v.angles.y); msg.MSG_WriteAngle(0); SV_WriteClientdataToMessage(sv_player, host_client.message); msg.MSG_WriteByte(q_shared.svc_signonnum); msg.MSG_WriteByte(3); host_client.sendsignon = true; }
/* ======================= SV_SendNop Send a nop message without trashing or sending the accumulated client message buffer ======================= */ static void SV_SendNop(client_t client) { common.sizebuf_t msg = new common.sizebuf_t(); Uint8Array buf = new Uint8Array(4); msg.data = buf; msg.maxsize = buf.Length; msg.cursize = 0; common.MSG_WriteChar (msg, net.svc_nop); if (net.NET_SendUnreliableMessage(client.netconnection, msg) == -1) host.SV_DropClient(true); // if the message couldn't send, kick off client.last_message = host.realtime; }
/* =============== SV_SendConfigstring Creates and sends the server command necessary to update the CS index for the given client =============== */ void SendConfigString(client_t client, int index) { SendServerCommand(client, string.Format("cs {0} \"{1}\"\n", index, sv.configstrings[index])); }
public static void Host_Kick_f() { if (cmd_source == cmd_source_t.src_command) { if (!sv.active) { Cmd_ForwardToServer(); return; } } else if (pr_global_struct.deathmatch != 0 && !host_client.privileged) { return; } client_t save = host_client; bool byNumber = false; int i; if (cmd_argc > 2 && Cmd_Argv(1) == "#") { i = (int)atof(Cmd_Argv(2)) - 1; if (i < 0 || i >= svs.maxclients) { return; } if (!svs.clients[i].active) { return; } host_client = svs.clients[i]; byNumber = true; } else { for (i = 0; i < svs.maxclients; i++) { host_client = svs.clients[i]; if (!host_client.active) { continue; } if (SameText(host_client.name, Cmd_Argv(1))) { break; } } } if (i < svs.maxclients) { string who; if (cmd_source == cmd_source_t.src_command) { if (cls.state == cactive_t.ca_dedicated) { who = "Console"; } else { who = cl_name.@string; } } else { who = save.name; } // can't kick yourself! if (host_client == save) { return; } string message = null; if (cmd_argc > 2) { message = COM_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)) { SV_ClientPrintf("Kicked by {0}: {1}\n", who, message); } else { SV_ClientPrintf("Kicked by {0}\n", who); } SV_DropClient(false); } host_client = save; }
/* ======================= SV_SendClientDatagram ======================= */ static bool SV_SendClientDatagram(client_t client) { byte[] buf = new byte[quakedef.MAX_DATAGRAM]; common.sizebuf_t msg = new common.sizebuf_t(); msg.data = buf; msg.maxsize = buf.Length; msg.cursize = 0; common.MSG_WriteByte (msg, net.svc_time); common.MSG_WriteFloat (msg, sv.time); // add the client specific data to the datagram SV_WriteClientdataToMessage(client.edict, msg); SV_WriteEntitiesToClient(client.edict, msg); // copy the server datagram if there is space if (msg.cursize + sv.datagram.cursize < msg.maxsize) common.SZ_Write(msg, sv.datagram.data, sv.datagram.cursize); // send the datagram if (net.NET_SendUnreliableMessage (client.netconnection, msg) == -1) { return false; } return true; }
public static void Host_Status_f() { bool flag = true; if (cmd_source == cmd_source_t.src_command) { if (!sv.active) { Cmd_ForwardToServer(); return; } } else { flag = false; } StringBuilder sb = new StringBuilder(256); sb.Append(String.Format("host: {0}\n", Cvar.Cvar_VariableString("hostname"))); sb.Append(String.Format("version: {0:F2}\n", q_shared.VERSION)); if (NetTcpIp.Instance.IsInitialized) { sb.Append("tcp/ip: "); sb.Append(my_tcpip_address); sb.Append('\n'); } sb.Append("map: "); sb.Append(sv.name); sb.Append('\n'); sb.Append(String.Format("players: {0} active ({1} max)\n\n", net_activeconnections, svs.maxclients)); for (int j = 0; j < svs.maxclients; j++) { client_t client = 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_Printf(sb.ToString()); } else { SV_ClientPrintf(sb.ToString()); } }
private void AddServerCommand(client_t client, string cmd) { // do not send commands until the gamestate has been sent if (client.state < clientState_t.CS_PRIMED) { return; } client.reliableSequence++; // if we would be losing an old command that hasn't been acknowledged, // we must drop the connection // we check == instead of >= so a broadcast print added by SV_DropClient() // doesn't cause a recursive drop client if (client.reliableSequence - client.reliableAcknowledge == 64 + 1) { Common.Instance.WriteLine("======== pending server commands ========="); int i; for (i = client.reliableAcknowledge+1; i <= client.reliableSequence; i++) { Common.Instance.WriteLine("cmd {0}: {1}", i, client.reliableCommands[i & 63]); } Common.Instance.WriteLine("cmd {0}: {1}", i, cmd); DropClient(client, "Server command overflow"); return; } int index = client.reliableSequence & 63; client.reliableCommands[index] = cmd; }
private void SetUserInfo(client_t cl, string p) { if (cl == null || p == null) return; foreach (client_t client in clients) { if (client.Equals(cl)) { client.userinfo = p; client.name = Info.ValueForKey(p, "name"); break; } } }
/* ============================================================================= EVENT MESSAGES ============================================================================= */ /* ============================================================================== CLIENT SPAWNING ============================================================================== */ /* ================ 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. ================ */ static void SV_SendServerinfo(client_t client) { int i; string s; string message; common.MSG_WriteByte (client.message, net.svc_print); message = "\u0002\nVERSION " + quakedef.VERSION + " SERVER (" + prog.pr_crc + " CRC)"; common.MSG_WriteString(client.message, message); common.MSG_WriteByte(client.message, net.svc_serverinfo); common.MSG_WriteLong(client.message, net.PROTOCOL_VERSION); common.MSG_WriteByte(client.message, svs.maxclients); if (host.coop.value == 0 && host.deathmatch.value != 0) common.MSG_WriteByte(client.message, net.GAME_DEATHMATCH); else common.MSG_WriteByte(client.message, net.GAME_COOP); //sprintf(message, pr_strings + sv.edicts->v.message); message = prog.pr_string(sv.edicts[0].v.message); common.MSG_WriteString(client.message, message); for (i = 1, s = sv.model_precache[i]; s != null; i++) { s = sv.model_precache[i]; common.MSG_WriteString(client.message, s); } common.MSG_WriteByte(client.message, 0); for (i = 1, s = sv.sound_precache[i]; s != null; i++) { s = sv.sound_precache[i]; common.MSG_WriteString(client.message, s); } common.MSG_WriteByte(client.message, 0); // send music common.MSG_WriteByte(client.message, net.svc_cdtrack); common.MSG_WriteByte(client.message, (int)sv.edicts[0].v.sounds); common.MSG_WriteByte(client.message, (int)sv.edicts[0].v.sounds); // set view common.MSG_WriteByte (client.message, net.svc_setview); common.MSG_WriteShort (client.message, prog.NUM_FOR_EDICT(client.edict)); common.MSG_WriteByte(client.message, net.svc_signonnum); common.MSG_WriteByte(client.message, 1); client.sendsignon = true; client.spawned = false; // need prespawn, spawn, etc }
/* ============= SV_BuildClientSnapshot Decides which entities are going to be visible to the client, and copies off the playerstate and areabits. This properly handles multiple recursive portals, but the render currently doesn't. For viewing through other player's eyes, clent can be something other than client->gentity ============= */ void BuildClientSnapshot(client_t client, int index) { List<int> snapshotEntityNumbers = new List<int>(); // bump the counter used to prevent double adding sv.snapshotCounter++; // this is the frame we are creating clientSnapshot_t frame = client.frames[client.netchan.outgoingSequence & 31]; // clear everything in this snapshot frame.num_entities = 0; frame.areabits = new byte[32]; sharedEntity clent = client.gentity; if (clent == null || client.state == clientState_t.CS_ZOMBIE) { return; } // grab the current playerState_t Common.PlayerState ps = GameClientNum(index); //frame.ps = ps; frame.ps = ps.Clone(); // never send client's own entity, because it can // be regenerated from the playerstate int clientnum = frame.ps.clientNum; if (clientnum < 0 || clientnum >= 1024) { Common.Instance.Error("bad gEnt"); } svEntity_t svEnt = sv.svEntities[clientnum]; svEnt.snapshotCounter = sv.snapshotCounter; // find the client's viewpoint Vector3 org = ps.origin; org[2] += ps.viewheight; // add all the entities directly visible to the eye, which // may include portal entities that merge other viewpoints AddEntitiesVisibleFromPoint(org, ref frame, snapshotEntityNumbers, false); // if there were portals visible, there may be out of order entities // in the list which will need to be resorted for the delta compression // to work correctly. This also catches the error condition // of an entity being included twice. snapshotEntityNumbers.Sort((a, b) => { return a.CompareTo(b); }); // now that all viewpoint's areabits have been OR'd together, invert // all of them to make it a mask vector, which is what the renderer wants for (int i = 0; i < 8; i++) { frame.areabits[i] = (byte)(frame.areabits[i] ^ -1); } // copy the entity states out frame.num_entities = 0; frame.first_entity = nextSnapshotEntities; for (int i = 0; i < snapshotEntityNumbers.Count; i++) { sharedEntity ent = sv.gentities[snapshotEntityNumbers[i]]; snapshotEntities[nextSnapshotEntities++ % numSnapshotEntities] = new Common.entityState_t() { angles = ent.s.angles, angles2 = ent.s.angles2, apos = ent.s.apos, clientNum = ent.s.clientNum, eFlags = ent.s.eFlags, eType = ent.s.eType , frame = ent.s.frame, groundEntityNum = ent.s.groundEntityNum, number = ent.s.number, pos = ent.s.pos, solid = ent.s.solid, origin = ent.s.origin, otherEntityNum = ent.s.otherEntityNum, time = ent.s.time, modelindex = ent.s.modelindex}; //Common.Instance.WriteLine("Sending ent {0} to client {1}", snapshotEntityNumbers[i], index); frame.num_entities++; } }
/* =============== SV_UpdateConfigstrings Called when a client goes from CS_PRIMED to CS_ACTIVE. Updates all Configstring indexes that have changed while the client was in CS_PRIMED =============== */ void UpdateConfigStrings(client_t cl) { for (int i = 0; i < cl.csUpdated.Length; i++) { // if the CS hasn't changed since we went to CS_PRIMED, ignore if (!cl.csUpdated[i]) continue; // do not always send server info to all clients if (i == (int)ConfigString.CS_SERVERINFO && cl.gentity != null && (cl.gentity.r.svFlags & Common.svFlags.NOSERVERINFO) == Common.svFlags.NOSERVERINFO) { continue; } SendConfigString(cl, i); cl.csUpdated[i] = false; } }
/* ==================== SV_RateMsec Return the number of msec a given size message is supposed to take to clear, based on the current rate ==================== */ int RateMsec(client_t client, int msgSize) { int headerSize = 48; //// individual messages will never be larger than fragment size //if (msgSize > 1500) // msgSize = 1500; int rate = client.rate; // FIX: variable rate int rateMsec = (int)((msgSize + headerSize) * 1000 / rate * Common.Instance.timescale.Value); return rateMsec; }
void ExecuteClientMessage(client_t cl, NetBuffer buf) { int serverId = buf.ReadInt32(); cl.messageAcknowledge = buf.ReadInt32(); if (cl.messageAcknowledge < 0) { // usually only hackers create messages like this // it is more annoying for them to let them hanging DropClient(cl, "Illegible client message"); return; } cl.reliableAcknowledge = buf.ReadInt32(); // NOTE: when the client message is fux0red the acknowledgement numbers // can be out of range, this could cause the server to send thousands of server // commands which the server thinks are not yet acknowledged in SV_UpdateServerCommandsToClient if (cl.reliableAcknowledge < cl.reliableSequence - 64) { DropClient(cl, "Illegible client message"); cl.reliableAcknowledge = cl.reliableSequence; return; } // if this is a usercmd from a previous gamestate, // ignore it or retransmit the current gamestate // if (serverId != sv.serverId && !cl.lastClientCommandString.Equals("nextdl")) { if (serverId >= sv.restartedServerId && serverId < sv.serverId) { // they just haven't caught the map_restart yet Common.Instance.WriteLine("{0} : ignoring pre map_restart / outdated client message", cl.name); return; } // if we can tell that the client has dropped the last // gamestate we sent them, resend it if (cl.messageAcknowledge > cl.gamestateMessageNum) { Common.Instance.WriteLine("{0} : dropped gamestate, resending", cl.name); SendClientGameState(cl); } return; } // this client has acknowledged the new gamestate so it's // safe to start sending it the real time again if (cl.oldServerTime > 0 && serverId == sv.serverId) { Common.Instance.WriteLine("{0} acknowledged gamestate", cl.name); cl.oldServerTime = 0; } // read optional clientCommand strings int c; do { c = buf.ReadByte(); if (c == (int)clc_ops_e.clc_EOF) break; if (c != (int)clc_ops_e.clc_clientCommand) break; if (!ClientCommand(cl, buf)) break; // we couldn't execute it because of the flood protection if (cl.state == clientState_t.CS_ZOMBIE) return; // disconnect command } while (true); // read the usercmd_t if (c == (int)clc_ops_e.clc_move) { UserMove(cl, buf, true); } else if (c == (int)clc_ops_e.clc_moveNoDelta) { UserMove(cl, buf, false); } else if (c != (int)clc_ops_e.clc_EOF) { Common.Instance.WriteLine("WARNING: bad command byte for client {0}", cl.name); } }