/* * @brief */ void Sv_KickClient(sv_client_t *cl, const char *msg) { char buf[MAX_STRING_CHARS], name[32]; if (!cl) return; if (cl->state < SV_CLIENT_CONNECTED) return; if (*cl->name == '\0') // force a name to kick strcpy(name, "player"); else g_strlcpy(name, cl->name, sizeof(name)); memset(buf, 0, sizeof(buf)); if (msg && *msg != '\0') g_snprintf(buf, sizeof(buf), ": %s", msg); Sv_ClientPrint(cl->edict, PRINT_HIGH, "You were kicked%s\n", buf); Sv_DropClient(cl); Sv_BroadcastPrint(PRINT_HIGH, "%s was kicked%s\n", name, buf); }
/* * @brief */ static void Sv_CheckTimeouts(void) { const uint32_t timeout = 1000 * sv_timeout->value; if (timeout > quetoo.time) return; const uint32_t whence = quetoo.time - timeout; sv_client_t *cl = svs.clients; for (int32_t i = 0; i < sv_max_clients->integer; i++, cl++) { if (cl->state == SV_CLIENT_FREE) continue; if (cl->last_message < whence) { Sv_BroadcastPrint(PRINT_MEDIUM, "%s timed out\n", cl->name); Sv_DropClient(cl); } } }
/* * @brief */ static void Sv_CheckTimeouts(void) { sv_client_t * cl; int32_t i; const uint32_t timeout = svs.real_time - 1000 * sv_timeout->value; if (timeout > svs.real_time) { // the server is just starting, don't bother return; } for (i = 0, cl = svs.clients; i < sv_max_clients->integer; i++, cl++) { if (cl->state == SV_CLIENT_FREE) continue; // enforce timeouts by dropping the client if (cl->last_message < timeout) { Sv_BroadcastPrint(PRINT_HIGH, "%s timed out\n", cl->name); Sv_DropClient(cl); } } }
/* * Sv_ParseClientMessage * * The current net_message is parsed for the given client. */ void Sv_ParseClientMessage(sv_client_t *cl) { user_cmd_t null_cmd, oldest_cmd, old_cmd, new_cmd; int net_drop; int strings_issued; int moves_issued; int last_frame; int c; char *s; sv_client = cl; sv_player = sv_client->edict; // allow a finite number of moves and strings moves_issued = strings_issued = 0; while (true) { if (net_message.read > net_message.size) { Com_Warn("Sv_ParseClientMessage: Bad read from %s\n", Sv_NetaddrToString(sv_client)); Sv_DropClient(cl); return; } c = Msg_ReadByte(&net_message); if (c == -1) break; switch (c) { case CL_CMD_USER_INFO: strncpy(cl->user_info, Msg_ReadString(&net_message), sizeof(cl->user_info) - 1); Sv_UserInfoChanged(cl); break; case CL_CMD_MOVE: if (++moves_issued > CMD_MAX_MOVES) { return; // someone is trying to cheat } last_frame = Msg_ReadLong(&net_message); if (last_frame != cl->last_frame) { cl->last_frame = last_frame; if (cl->last_frame > -1) { cl->frame_latency[cl->last_frame & (CLIENT_LATENCY_COUNTS - 1)] = svs.real_time - cl->frames[cl->last_frame & UPDATE_MASK].sent_time; } } memset(&null_cmd, 0, sizeof(null_cmd)); Msg_ReadDeltaUsercmd(&net_message, &null_cmd, &oldest_cmd); Msg_ReadDeltaUsercmd(&net_message, &oldest_cmd, &old_cmd); Msg_ReadDeltaUsercmd(&net_message, &old_cmd, &new_cmd); // don't start delta compression until the client is spawned // TODO: should this be a little higher up? if (cl->state != SV_CLIENT_ACTIVE) { cl->last_frame = -1; break; } // catch extremely high msec movements if (null_cmd.msec > CMD_MAX_MSEC || oldest_cmd.msec > CMD_MAX_MSEC || old_cmd.msec > CMD_MAX_MSEC || new_cmd.msec > CMD_MAX_MSEC) { Com_Warn("Sv_ParseClientMessage: Illegal msec from %s\n", Sv_NetaddrToString(cl)); Sv_KickClient(cl, "Illegal movement"); return; } net_drop = cl->netchan.dropped; if (net_drop < 20) { while (net_drop > 2) { Sv_ClientThink(cl, &cl->last_cmd); net_drop--; } if (net_drop > 1) Sv_ClientThink(cl, &oldest_cmd); if (net_drop > 0) Sv_ClientThink(cl, &old_cmd); } Sv_ClientThink(cl, &new_cmd); cl->last_cmd = new_cmd; break; case CL_CMD_STRING: s = Msg_ReadString(&net_message); // malicious users may try using too many string commands if (++strings_issued < CMD_MAX_STRINGS) Sv_UserStringCommand(s); else { Com_Warn( "Sv_ParseClientMessage: CMD_MAX_STRINGS exceeded for %s\n", Sv_NetaddrToString(cl)); Sv_KickClient(cl, "Too many commands."); return; } if (cl->state == SV_CLIENT_FREE) return; // disconnect command break; default: Com_Print("Sv_ParseClientMessage: unknown command %d\n", c); Sv_DropClient(cl); return; } } }
/* * Sv_Disconnect_f * * The client is going to disconnect, so remove the connection immediately */ static void Sv_Disconnect_f(void) { Sv_DropClient(sv_client); }