static void NET_SV_ParseGameDataACK(net_packet_t *packet, net_client_t *client) { unsigned int ackseq; NET_Log("server: processing game data ack packet"); if (server_state != SERVER_IN_GAME) { NET_Log("server: error: not in game state, server_state=%d", server_state); return; } // Read header if (!NET_ReadInt8(packet, &ackseq)) { NET_Log("server: error: missing acknowledgement field"); return; } // Expand 8-bit values to the full sequence number ackseq = NET_SV_ExpandTicNum(ackseq); // Higher acknowledgement point than we already have? if (ackseq > client->acknowledged) { NET_Log("server: acknowledged up to %d", ackseq); client->acknowledged = ackseq; } }
static void NET_Conn_ParseReliableACK(net_connection_t *conn, net_packet_t *packet) { unsigned int seq; if (!NET_ReadInt8(packet, &seq)) { return; } if (conn->reliable_packets == NULL) { return; } // Is this an acknowledgement for the first packet in the list? if (seq == (unsigned int)((conn->reliable_packets->seq + 1) & 0xff)) { net_reliable_packet_t *rp; // Discard it, then. // Unlink from the list. rp = conn->reliable_packets; conn->reliable_packets = rp->next; NET_FreePacket(rp->packet); free(rp); } }
static void NET_SV_ParseGameDataACK(net_packet_t *packet, net_client_t *client) { unsigned int ackseq; if (server_state != SERVER_IN_GAME) { return; } // Read header if (!NET_ReadInt8(packet, &ackseq)) { return; } // Expand 8-bit values to the full sequence number ackseq = NET_SV_ExpandTicNum(ackseq); // Higher acknowledgement point than we already have? if (ackseq > client->acknowledged) { client->acknowledged = ackseq; } }
static void NET_CL_ParseResendRequest(net_packet_t *packet) { static unsigned int start; static unsigned int end; static unsigned int num_tics; if (drone) { // Drones don't send gamedata. return; } if (!NET_ReadInt32(packet, &start) || !NET_ReadInt8(packet, &num_tics)) { return; } end = start + num_tics - 1; //printf("requested resend %i-%i .. ", start, end); // Check we have the tics being requested. If not, reduce the // window of tics to only what we have. while (start <= end && (!send_queue[start % BACKUPTICS].active || send_queue[start % BACKUPTICS].seq != start)) { ++start; } while (start <= end && (!send_queue[end % BACKUPTICS].active || send_queue[end % BACKUPTICS].seq != end)) { --end; } //printf("%i-%i\n", start, end); // Resend those tics if (start <= end) { //printf("CL: resend %i-%i\n", start, start+num_tics-1); NET_CL_SendTics(start, end); } }
boolean NET_ReadConnectData(net_packet_t *packet, net_connect_data_t *data) { return NET_ReadInt8(packet, (unsigned int *) &data->gamemode) && NET_ReadInt8(packet, (unsigned int *) &data->gamemission) && NET_ReadInt8(packet, (unsigned int *) &data->lowres_turn) && NET_ReadInt8(packet, (unsigned int *) &data->drone) && NET_ReadInt8(packet, (unsigned int *) &data->max_players) && NET_ReadInt8(packet, (unsigned int *) &data->is_freedoom) && NET_ReadSHA1Sum(packet, data->wad_sha1sum) && NET_ReadSHA1Sum(packet, data->deh_sha1sum) && NET_ReadInt8(packet, (unsigned int *) &data->player_class); }
static boolean NET_Conn_ReliablePacket(net_connection_t *conn, net_packet_t *packet) { unsigned int seq; net_packet_t *reply; boolean result; // Read the sequence number if (!NET_ReadInt8(packet, &seq)) { return true; } if (seq != (unsigned int)(conn->reliable_recv_seq & 0xff)) { // This is not the next expected packet in the sequence! // // Discard the packet. If we were smart, we would use a proper // sliding window protocol to do this, but I'm lazy. result = true; } else { // Now we can receive the next packet in the sequence. conn->reliable_recv_seq = (conn->reliable_recv_seq + 1) & 0xff; result = false; } // Send an acknowledgement // Note: this is braindead. It would be much more sensible to // include this in the next packet, rather than the overhead of // sending a complete packet just for one byte of information. reply = NET_NewPacket(10); NET_WriteInt16(reply, NET_PACKET_TYPE_RELIABLE_ACK); NET_WriteInt8(reply, conn->reliable_recv_seq & 0xff); NET_Conn_SendPacket(conn, reply); NET_FreePacket(reply); return result; }
boolean NET_ReadQueryData(net_packet_t *packet, net_querydata_t *query) { boolean result; query->version = NET_ReadString(packet); result = query->version != NULL && NET_ReadInt8(packet, (unsigned int *) &query->server_state) && NET_ReadInt8(packet, (unsigned int *) &query->num_players) && NET_ReadInt8(packet, (unsigned int *) &query->max_players) && NET_ReadInt8(packet, (unsigned int *) &query->gamemode) && NET_ReadInt8(packet, (unsigned int *) &query->gamemission); if (result) { query->description = NET_ReadString(packet); return query->description != NULL; } else { return false; } }
static boolean NET_ReadBlob(net_packet_t *packet, uint8_t *buf, size_t len) { unsigned int b; int i; for (i=0; i<len; ++i) { if (!NET_ReadInt8(packet, &b)) { return false; } buf[i] = b; } return true; }
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_CL_ParseLaunch(net_packet_t *packet) { unsigned int num_players; if (client_state != CLIENT_STATE_WAITING_LAUNCH) { return; } // The launch packet contains the number of players that will be // in the game when it starts, so that we can do the startup // progress indicator (the wait data is unreliable). if (!NET_ReadInt8(packet, &num_players)) { return; } net_client_wait_data.num_players = num_players; client_state = CLIENT_STATE_WAITING_START; }
boolean NET_ReadFullTiccmd(net_packet_t *packet, net_full_ticcmd_t *cmd, boolean lowres_turn) { unsigned int bitfield; int i; // Latency if (!NET_ReadSInt16(packet, &cmd->latency)) { return false; } // Regenerate playeringame from the "header" bitfield if (!NET_ReadInt8(packet, &bitfield)) { return false; } for (i=0; i<NET_MAXPLAYERS; ++i) { cmd->playeringame[i] = (bitfield & (1 << i)) != 0; } // Read cmds for (i=0; i<NET_MAXPLAYERS; ++i) { if (cmd->playeringame[i]) { if (!NET_ReadTiccmdDiff(packet, &cmd->cmds[i], lowres_turn)) { return false; } } } return true; }
boolean NET_ReadWaitData(net_packet_t *packet, net_waitdata_t *data) { int i; char *s; if (!NET_ReadInt8(packet, (unsigned int *) &data->num_players) || !NET_ReadInt8(packet, (unsigned int *) &data->num_drones) || !NET_ReadInt8(packet, (unsigned int *) &data->ready_players) || !NET_ReadInt8(packet, (unsigned int *) &data->max_players) || !NET_ReadInt8(packet, (unsigned int *) &data->is_controller) || !NET_ReadSInt8(packet, &data->consoleplayer)) { return false; } for (i = 0; i < data->num_players && i < NET_MAXPLAYERS; ++i) { s = NET_ReadString(packet); if (s == NULL || strlen(s) >= MAXPLAYERNAME) { return false; } strcpy(data->player_names[i], s); s = NET_ReadString(packet); if (s == NULL || strlen(s) >= MAXPLAYERNAME) { return false; } strcpy(data->player_addrs[i], s); } return NET_ReadSHA1Sum(packet, data->wad_sha1sum) && NET_ReadSHA1Sum(packet, data->deh_sha1sum) && NET_ReadInt8(packet, (unsigned int *) &data->is_freedoom); }
static void NET_SV_ParseGameData(net_packet_t *packet, net_client_t *client) { net_client_recv_t *recvobj; unsigned int seq; unsigned int ackseq; unsigned int num_tics; unsigned int nowtime; size_t i; int player; int resend_start, resend_end; int index; if (server_state != SERVER_IN_GAME) { return; } if (client->drone) { // Drones do not contribute any game data. return; } player = client->player_number; // Read header if (!NET_ReadInt8(packet, &ackseq) || !NET_ReadInt8(packet, &seq) || !NET_ReadInt8(packet, &num_tics)) { return; } // Get the current time nowtime = I_GetTimeMS(); // Expand 8-bit values to the full sequence number ackseq = NET_SV_ExpandTicNum(ackseq); seq = NET_SV_ExpandTicNum(seq); // Sanity checks for (i=0; i<num_tics; ++i) { net_ticdiff_t diff; signed int latency; if (!NET_ReadSInt16(packet, &latency) || !NET_ReadTiccmdDiff(packet, &diff, sv_settings.lowres_turn)) { return; } index = seq + i - recvwindow_start; if (index < 0 || index >= BACKUPTICS) { // Not in range of the recv window continue; } recvobj = &recvwindow[index][player]; recvobj->active = true; recvobj->diff = diff; recvobj->latency = latency; client->last_gamedata_time = nowtime; } // Higher acknowledgement point? if (ackseq > client->acknowledged) { client->acknowledged = ackseq; } // Has this been received out of sequence, ie. have we not received // all tics before the first tic in this packet? If so, send a // resend request. //printf("SV: %p: %i\n", client, seq); resend_end = seq - recvwindow_start; if (resend_end <= 0) return; if (resend_end >= BACKUPTICS) resend_end = BACKUPTICS - 1; index = resend_end - 1; resend_start = resend_end; while (index >= 0) { recvobj = &recvwindow[index][player]; if (recvobj->active) { // ended our run of unreceived tics break; } if (recvobj->resend_time != 0) { // Already sent a resend request for this tic break; } resend_start = index; --index; } // Possibly send a resend request if (resend_start < resend_end) { /* printf("missed %i-%i before %i, send resend\n", recvwindow_start + resend_start, recvwindow_start + resend_end - 1, seq); */ NET_SV_SendResendRequest(client, recvwindow_start + resend_start, recvwindow_start + resend_end - 1); } }
static void NET_SV_ParseSYN(net_packet_t *packet, net_client_t *client, net_addr_t *addr) { unsigned int magic; unsigned int cl_gamemode, cl_gamemission; unsigned int cl_recording_lowres; unsigned int cl_drone; unsigned int is_freedoom; md5_digest_t deh_md5sum, wad_md5sum; char *player_name; char *client_version; int i; // read the magic number if (!NET_ReadInt32(packet, &magic)) { return; } if (magic != NET_MAGIC_NUMBER) { // invalid magic number return; } // Check the client version is the same as the server client_version = NET_ReadString(packet); if (client_version == NULL) { return; } if (strcmp(client_version, PACKAGE_STRING) != 0) { //! // @category net // // When running a netgame server, ignore version mismatches between // the server and the client. Using this option may cause game // desyncs to occur, or differences in protocol may mean the netgame // will simply not function at all. // if (M_CheckParm("-ignoreversion") == 0) { NET_SV_SendReject(addr, "Version mismatch: server version is: " PACKAGE_STRING); return; } } // read the game mode and mission if (!NET_ReadInt16(packet, &cl_gamemode) || !NET_ReadInt16(packet, &cl_gamemission) || !NET_ReadInt8(packet, &cl_recording_lowres) || !NET_ReadInt8(packet, &cl_drone) || !NET_ReadMD5Sum(packet, wad_md5sum) || !NET_ReadMD5Sum(packet, deh_md5sum) || !NET_ReadInt8(packet, &is_freedoom)) { return; } if (!NET_ValidGameMode(cl_gamemode, cl_gamemission)) { return; } // read the player's name player_name = NET_ReadString(packet); if (player_name == NULL) { return; } // received a valid SYN // not accepting new connections? if (server_state != SERVER_WAITING_START) { NET_SV_SendReject(addr, "Server is not currently accepting connections"); return; } // allocate a client slot if there isn't one already if (client == NULL) { // find a slot, or return if none found for (i=0; i<MAXNETNODES; ++i) { if (!clients[i].active) { client = &clients[i]; break; } } if (client == NULL) { return; } } else { // If this is a recently-disconnected client, deactivate // to allow immediate reconnection if (client->connection.state == NET_CONN_STATE_DISCONNECTED) { client->active = false; } } // New client? if (!client->active) { int num_players; // Before accepting a new client, check that there is a slot // free NET_SV_AssignPlayers(); num_players = NET_SV_NumPlayers(); if ((!cl_drone && num_players >= MAXPLAYERS) || NET_SV_NumClients() >= MAXNETNODES) { NET_SV_SendReject(addr, "Server is full!"); return; } // TODO: Add server option to allow rejecting clients which // set lowres_turn. This is potentially desirable as the // presence of such clients affects turning resolution. // Adopt the game mode and mission of the first connecting client if (num_players == 0 && !cl_drone) { sv_gamemode = cl_gamemode; sv_gamemission = cl_gamemission; } // Save the MD5 checksums memcpy(client->wad_md5sum, wad_md5sum, sizeof(md5_digest_t)); memcpy(client->deh_md5sum, deh_md5sum, sizeof(md5_digest_t)); client->is_freedoom = is_freedoom; // Check the connecting client is playing the same game as all // the other clients if (cl_gamemode != sv_gamemode || cl_gamemission != sv_gamemission) { NET_SV_SendReject(addr, "You are playing the wrong game!"); return; } // Activate, initialize connection NET_SV_InitNewClient(client, addr, player_name); client->recording_lowres = cl_recording_lowres; client->drone = cl_drone; } if (client->connection.state == NET_CONN_STATE_WAITING_ACK) { // force an acknowledgement client->connection.last_send_time = -1; } }
boolean NET_ReadTiccmdDiff(net_packet_t *packet, net_ticdiff_t *diff, boolean lowres_turn) { unsigned int val; signed int sval; // Read header if (!NET_ReadInt8(packet, &diff->diff)) return false; // Read fields if (diff->diff & NET_TICDIFF_FORWARD) { if (!NET_ReadSInt8(packet, &sval)) return false; diff->cmd.forwardmove = sval; } if (diff->diff & NET_TICDIFF_SIDE) { if (!NET_ReadSInt8(packet, &sval)) return false; diff->cmd.sidemove = sval; } if (diff->diff & NET_TICDIFF_TURN) { if (lowres_turn) { if (!NET_ReadSInt8(packet, &sval)) return false; diff->cmd.angleturn = sval * 256; } else { if (!NET_ReadSInt16(packet, &sval)) return false; diff->cmd.angleturn = sval; } } if (diff->diff & NET_TICDIFF_BUTTONS) { if (!NET_ReadInt8(packet, &val)) return false; diff->cmd.buttons = val; } if (diff->diff & NET_TICDIFF_CONSISTANCY) { if (!NET_ReadInt8(packet, &val)) return false; diff->cmd.consistancy = val; } if (diff->diff & NET_TICDIFF_CHATCHAR) { if (!NET_ReadInt8(packet, &val)) return false; diff->cmd.chatchar = val; } if (diff->diff & NET_TICDIFF_RAVEN) { if (!NET_ReadInt8(packet, &val)) return false; diff->cmd.lookfly = val; if (!NET_ReadInt8(packet, &val)) return false; diff->cmd.arti = val; } if (diff->diff & NET_TICDIFF_STRIFE) { if (!NET_ReadInt8(packet, &val)) return false; diff->cmd.buttons2 = val; if (!NET_ReadInt8(packet, &val)) return false; diff->cmd.inventory = val; } return true; }
static void NET_CL_ParseGameData(net_packet_t *packet) { net_server_recv_t *recvobj; unsigned int seq, num_tics; unsigned int nowtime; int resend_start, resend_end; size_t i; int index; // Read header if (!NET_ReadInt8(packet, &seq) || !NET_ReadInt8(packet, &num_tics)) { return; } nowtime = I_GetTimeMS(); // Whatever happens, we now need to send an acknowledgement of our // current receive point. if (!need_to_acknowledge) { need_to_acknowledge = true; gamedata_recv_time = nowtime; } // Expand byte value into the full tic number seq = NET_CL_ExpandTicNum(seq); for (i=0; i<num_tics; ++i) { net_full_ticcmd_t cmd; index = seq - recvwindow_start + i; if (!NET_ReadFullTiccmd(packet, &cmd, lowres_turn)) { return; } if (index < 0 || index >= BACKUPTICS) { // Out of range of the recv window continue; } // Store in the receive window recvobj = &recvwindow[index]; recvobj->active = true; recvobj->cmd = cmd; } // Has this been received out of sequence, ie. have we not received // all tics before the first tic in this packet? If so, send a // resend request. //printf("CL: %p: %i\n", client, seq); resend_end = seq - recvwindow_start; if (resend_end <= 0) return; if (resend_end >= BACKUPTICS) resend_end = BACKUPTICS - 1; index = resend_end - 1; resend_start = resend_end; while (index >= 0) { recvobj = &recvwindow[index]; if (recvobj->active) { // ended our run of unreceived tics break; } if (recvobj->resend_time != 0) { // Already sent a resend request for this tic break; } resend_start = index; --index; } // Possibly send a resend request if (resend_start < resend_end) { NET_CL_SendResendRequest(recvwindow_start + resend_start, recvwindow_start + resend_end - 1); } }
static void NET_CL_ParseGameStart(net_packet_t *packet) { net_gamesettings_t settings; unsigned int num_players; signed int player_number; unsigned int i; if (!NET_ReadInt8(packet, &num_players) || !NET_ReadSInt8(packet, &player_number) || !NET_ReadSettings(packet, &settings)) { return; } if (client_state != CLIENT_STATE_WAITING_START) { return; } if (num_players > MAXPLAYERS || player_number >= (signed int) num_players) { // insane values return; } if ((drone && player_number >= 0) || (!drone && player_number < 0)) { // Invalid player number: must be positive for real players, // negative for drones return; } // Start the game if (!drone) { consoleplayer = player_number; } else { consoleplayer = 0; } for (i=0; i<MAXPLAYERS; ++i) { playeringame[i] = i < num_players; } client_state = CLIENT_STATE_IN_GAME; deathmatch = settings.deathmatch; ticdup = settings.ticdup; extratics = settings.extratics; startepisode = settings.episode; startmap = settings.map; startskill = settings.skill; startloadgame = settings.loadgame; lowres_turn = settings.lowres_turn; nomonsters = settings.nomonsters; fastparm = settings.fast_monsters; respawnparm = settings.respawn_monsters; net_cl_new_sync = settings.new_sync != 0; timelimit = settings.timelimit; if (net_cl_new_sync == false) { printf("Syncing netgames like Vanilla Doom.\n"); } if (lowres_turn) { printf("NOTE: Turning resolution is reduced; this is probably " "because there is a client recording a Vanilla demo.\n"); } // Clear the receive window memset(recvwindow, 0, sizeof(recvwindow)); recvwindow_start = 0; memset(&recvwindow_cmd_base, 0, sizeof(recvwindow_cmd_base)); // Clear the send queue memset(&send_queue, 0x00, sizeof(send_queue)); netgame = true; autostart = true; }
static void NET_CL_ParseWaitingData(net_packet_t *packet) { unsigned int num_players; unsigned int num_drones; unsigned int is_controller; signed int player_number; char *player_names[MAXPLAYERS]; char *player_addr[MAXPLAYERS]; md5_digest_t wad_md5sum, deh_md5sum; unsigned int server_is_freedoom; size_t i; if (!NET_ReadInt8(packet, &num_players) || !NET_ReadInt8(packet, &num_drones) || !NET_ReadInt8(packet, &is_controller) || !NET_ReadSInt8(packet, &player_number)) { // invalid packet return; } if (num_players > MAXPLAYERS) { // insane data return; } if ((player_number >= 0 && drone) || (player_number < 0 && !drone) || (player_number >= (signed int) num_players)) { // Invalid player number return; } // Read the player names for (i=0; i<num_players; ++i) { player_names[i] = NET_ReadString(packet); player_addr[i] = NET_ReadString(packet); if (player_names[i] == NULL || player_addr[i] == NULL) { return; } } if (!NET_ReadMD5Sum(packet, wad_md5sum) || !NET_ReadMD5Sum(packet, deh_md5sum) || !NET_ReadInt8(packet, &server_is_freedoom)) { return; } net_clients_in_game = num_players; net_drones_in_game = num_drones; net_client_controller = is_controller != 0; net_player_number = player_number; for (i=0; i<num_players; ++i) { strncpy(net_player_names[i], player_names[i], MAXPLAYERNAME); net_player_names[i][MAXPLAYERNAME-1] = '\0'; strncpy(net_player_addresses[i], player_addr[i], MAXPLAYERNAME); net_player_addresses[i][MAXPLAYERNAME-1] = '\0'; } memcpy(net_server_wad_md5sum, wad_md5sum, sizeof(md5_digest_t)); memcpy(net_server_deh_md5sum, deh_md5sum, sizeof(md5_digest_t)); net_server_is_freedoom = server_is_freedoom; net_client_received_wait_data = true; }
boolean NET_ReadSettings(net_packet_t *packet, net_gamesettings_t *settings) { boolean success; int i; success = NET_ReadInt8(packet, (unsigned int *) &settings->ticdup) && NET_ReadInt8(packet, (unsigned int *) &settings->extratics) && NET_ReadInt8(packet, (unsigned int *) &settings->deathmatch) && NET_ReadInt8(packet, (unsigned int *) &settings->nomonsters) && NET_ReadInt8(packet, (unsigned int *) &settings->fast_monsters) && NET_ReadInt8(packet, (unsigned int *) &settings->respawn_monsters) && NET_ReadInt8(packet, (unsigned int *) &settings->episode) && NET_ReadInt8(packet, (unsigned int *) &settings->map) && NET_ReadSInt8(packet, &settings->skill) && NET_ReadInt8(packet, (unsigned int *) &settings->gameversion) && NET_ReadInt8(packet, (unsigned int *) &settings->lowres_turn) && NET_ReadInt8(packet, (unsigned int *) &settings->new_sync) && NET_ReadInt32(packet, (unsigned int *) &settings->timelimit) && NET_ReadSInt8(packet, (signed int *) &settings->loadgame) && NET_ReadInt8(packet, (unsigned int *) &settings->random) && NET_ReadInt8(packet, (unsigned int *) &settings->num_players) && NET_ReadSInt8(packet, (signed int *) &settings->consoleplayer); if (!success) { return false; } for (i = 0; i < settings->num_players; ++i) { if (!NET_ReadInt8(packet, (unsigned int *) &settings->player_classes[i])) { return false; } } return true; }