void NET_SV_CheckDeadlock(net_client_t *client) { int nowtime; int i; // Don't expect game data from clients. if (client->drone) { return; } nowtime = I_GetTimeMS(); // If we haven't received anything for a long time, it may be a deadlock. if (nowtime - client->last_gamedata_time > 1000) { NET_Log("server: no gamedata from %s since %d - deadlock?", NET_AddrToString(client->addr), client->last_gamedata_time); // Search the receive window for the first tic we are expecting // from this player. for (i=0; i<BACKUPTICS; ++i) { if (!recvwindow[client->player_number][i].active) { NET_Log("server: deadlock: sending resend request for %d-%d", recvwindow_start + i, recvwindow_start + i + 5); // Found a tic we haven't received. Send a resend request. NET_SV_SendResendRequest(client, recvwindow_start + i, recvwindow_start + i + 5); client->last_gamedata_time = nowtime; break; } } // If we sent a resend request to break the deadlock, also trigger a // resend of any tics we have sitting in the send queue, in case the // client is blocked waiting on tics from us that have been lost. // This fixes deadlock with some older clients which do not send // resends to break deadlock. if (i < BACKUPTICS && client->sendseq > client->acknowledged) { NET_Log("server: also resending tics %d-%d to break deadlock", client->acknowledged, client->sendseq - 1); NET_SV_SendTics(client, client->acknowledged, client->sendseq - 1); } } }
static void NET_SV_ParseResendRequest(net_packet_t *packet, net_client_t *client) { unsigned int start, last; unsigned int num_tics; unsigned int i; NET_Log("server: processing resend request"); // Read the starting tic and number of tics if (!NET_ReadInt32(packet, &start) || !NET_ReadInt8(packet, &num_tics)) { NET_Log("server: error: missing fields for resend"); return; } //printf("SV: %p: resend %i-%i\n", client, start, start+num_tics-1); // Check we have all the requested tics last = start + num_tics - 1; for (i=start; i<=last; ++i) { net_full_ticcmd_t *cmd; cmd = &client->sendqueue[i % BACKUPTICS]; if (i != cmd->seq) { // We do not have the requested tic (any more) // This is pretty fatal. We could disconnect the client, // but then again this could be a spoofed packet. Just // ignore it. NET_Log("server: error: don't have tic %d any more, " "can't resend", i); return; } } // Resend those tics NET_Log("server: resending tics %d-%d", start, last); NET_SV_SendTics(client, start, last); }
static void NET_SV_PumpSendQueue(net_client_t *client) { net_full_ticcmd_t cmd; int recv_index; int i; int starttic, endtic; // If a client has not sent any acknowledgments for a while, // wait until they catch up. if (client->sendseq - NET_SV_LatestAcknowledged() > 40) { return; } // Work out the index into the receive window recv_index = client->sendseq - recvwindow_start; if (recv_index < 0 || recv_index >= BACKUPTICS) { return; } // Check if we can generate a new entry for the send queue // using the data in recvwindow. for (i=0; i<MAXPLAYERS; ++i) { if (sv_players[i] == client) { // Client does not rely on itself for data continue; } if (sv_players[i] == NULL || !ClientConnected(sv_players[i])) { continue; } if (!recvwindow[recv_index][i].active) { // We do not have this player's ticcmd, so we cannot // generate a complete command yet. return; } } //printf("SV: have complete ticcmd for %i\n", client->sendseq); // We have all data we need to generate a command for this tic. cmd.seq = client->sendseq; // Add ticcmds from all players cmd.latency = 0; for (i=0; i<MAXPLAYERS; ++i) { net_client_recv_t *recvobj; if (sv_players[i] == client) { // Not the player we are sending to cmd.playeringame[i] = false; continue; } if (sv_players[i] == NULL || !recvwindow[recv_index][i].active) { cmd.playeringame[i] = false; continue; } cmd.playeringame[i] = true; recvobj = &recvwindow[recv_index][i]; cmd.cmds[i] = recvobj->diff; if (recvobj->latency > cmd.latency) cmd.latency = recvobj->latency; } //printf("SV: %i: latency %i\n", client->player_number, cmd.latency); // Add into the queue client->sendqueue[client->sendseq % BACKUPTICS] = cmd; // Transmit the new tic to the client starttic = client->sendseq - sv_settings.extratics; endtic = client->sendseq; if (starttic < 0) starttic = 0; NET_SV_SendTics(client, starttic, endtic); ++client->sendseq; }
static void NET_SV_PumpSendQueue(net_client_t *client) { net_full_ticcmd_t cmd; int recv_index; int num_players; int i; int starttic, endtic; // If a client has not sent any acknowledgments for a while, // wait until they catch up. if (client->sendseq - NET_SV_LatestAcknowledged() > 40) { return; } // Work out the index into the receive window recv_index = client->sendseq - recvwindow_start; if (recv_index < 0 || recv_index >= BACKUPTICS) { return; } // Check if we can generate a new entry for the send queue // using the data in recvwindow. num_players = 0; for (i=0; i<NET_MAXPLAYERS; ++i) { if (sv_players[i] == client) { // Client does not rely on itself for data continue; } if (sv_players[i] == NULL || !ClientConnected(sv_players[i])) { continue; } if (!recvwindow[recv_index][i].active) { // We do not have this player's ticcmd, so we cannot // generate a complete command yet. return; } ++num_players; } // If this is a game with only a single player in it, we might // be sending a ticcmd set containing 0 ticcmds. This is fine; // however, there's nothing to stop the game running on ahead // and never stopping. Don't let the server get too far ahead // of the client. if (num_players == 0 && client->sendseq > recvwindow_start + 10) { return; } // We have all data we need to generate a command for this tic. cmd.seq = client->sendseq; // Add ticcmds from all players cmd.latency = 0; for (i=0; i<NET_MAXPLAYERS; ++i) { net_client_recv_t *recvobj; if (sv_players[i] == client) { // Not the player we are sending to cmd.playeringame[i] = false; continue; } if (sv_players[i] == NULL || !recvwindow[recv_index][i].active) { cmd.playeringame[i] = false; continue; } cmd.playeringame[i] = true; recvobj = &recvwindow[recv_index][i]; cmd.cmds[i] = recvobj->diff; if (recvobj->latency > cmd.latency) cmd.latency = recvobj->latency; } //printf("SV: %i: latency %i\n", client->player_number, cmd.latency); // Add into the queue client->sendqueue[client->sendseq % BACKUPTICS] = cmd; // Transmit the new tic to the client starttic = client->sendseq - sv_settings.extratics; endtic = client->sendseq; if (starttic < 0) starttic = 0; NET_Log("server: send tics %d-%d to %s", starttic, endtic, NET_AddrToString(client->addr)); NET_SV_SendTics(client, starttic, endtic); ++client->sendseq; }