/* Relay -- a tool to record and play Quake2 demos Copyright (C) 2000 Conor Davis This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. Conor Davis cedavis@planetquake.com */ #include #include "sv_local.h" void ProcessConnectionlessPacket(block_t *packet, struct sockaddr_in *addr) { char *s, *start; start = packet->buffer + packet->readoffset; for (s = start; s < packet->buffer + packet->writeoffset; s++) { if (*s == '\n') *s = 0; if (!*s) { if (*start) { Cmd_TokenizeString(start); ConnectionlessCommand(addr); } start = s + 1; } } } void ProcessClientPacket(block_t *packet, client_t *client, size_t seq, size_t ack) { qboolean in_reliable; size_t ack_bit; int id; client->timeout_time = mstime() + 30000; if (seq & RELIABLE_FLAG) { in_reliable = true; seq &= ~RELIABLE_FLAG; } else in_reliable = false; ack_bit = ack & RELIABLE_FLAG; ack &= ~RELIABLE_FLAG; // ignore stale unreliable packets if (!in_reliable && seq <= client->net.in_seq) return; // ignore packets with weird sequence numbers if (seq > client->net.in_seq + 3000) return; if (in_reliable) { // received duplicate reliable packet? if (seq <= client->net.in_reliable_seq) return; client->net.in_reliable_seq = seq; client->net.out_bit ^= RELIABLE_FLAG; } if (seq > client->net.in_seq) client->net.in_seq = seq; if (ack_bit != client->net.in_bit) { // client has received the reliable packet if (!client->net.out_reliable_seq) printf("%s ack'ed unknown reliable packet\n", client->netname); else { client->net.out_reliable_seq = 0; BlockRewind(&client->reliable); } client->net.in_bit = ack_bit; } else if (client->net.out_reliable_seq) { // reliable packet is still in transit // packet was probably lost, so resend it if (ack > client->net.out_reliable_seq) UDP_ResendReliable(server.socket, client->reliable.buffer, client->reliable.writeoffset, &client->net); } #if 0 printf("RECV %08x%c %08x%c\n", seq, (in_reliable ? 'R' : ' '), ack, (ack_bit ? 'R' : ' ')); #endif while(packet->readoffset < packet->writeoffset) { id = ReadByte(packet); if (ReadOverflow(packet)) { printf("Illegible client packet (CLC id overflow)\n"); return; } switch(id) { case CLC_NOP: break; case CLC_MOVE: { int mask, i; usercmd_t ucmd; memset(&ucmd, 0, sizeof(usercmd_t)); ReadByte(packet); // checksum client->delta_frame = ReadLong(packet); for (i = 0; i < 3; i++) { mask = ReadByte(packet); if (mask & CM_ANGLE1) ucmd.angles[0] = ReadShort(packet); if (mask & CM_ANGLE2) ucmd.angles[1] = ReadShort(packet); if (mask & CM_ANGLE3) ucmd.angles[2] = ReadShort(packet); if (mask & CM_FORWARD) ucmd.forwardmove = ReadShort(packet); if (mask & CM_SIDE) ucmd.sidemove = ReadShort(packet); if (mask & CM_UP) ucmd.upmove = ReadShort(packet); if (mask & CM_BUTTONS) ucmd.buttons = ReadByte(packet); if (mask & CM_IMPULSE) ucmd.impulse = ReadByte(packet); ucmd.msec = ReadByte(packet); ucmd.lightlevel = ReadByte(packet); } ClientThink(client, &ucmd); if (ReadOverflow(packet)) { printf("Illegible client packet (CLC_MOVE overflow)\n"); DropClient(client, NULL); return; } } break; case CLC_USERINFO: { const char *s; s = ReadString(packet); if (ReadOverflow(packet)) { printf("Illegible client packet (CLC_USERINFO overflow)\n"); DropClient(client, NULL); return; } ClientUserinfoChanged(client, s); } break; case CLC_STRINGCMD: { const char *s; // why god why? if (packet->writeoffset == 21 && !memcmp(packet->buffer + packet->readoffset, "disconnect", 10)) { ClientDisconnect(client); return; } s = ReadString(packet); if (ReadOverflow(packet)) { printf("Illegible client packet (CLC_STRINGCMD overflow)\n"); DropClient(client, NULL); return; } // hack! Cmd_TokenizeString(s); CL_StringCommand(client); } break; default: printf("Illegible client packet (unknown CLC id %u)\n", id); DropClient(client, NULL); return; } } } void ProcessPacket(block_t *packet, struct sockaddr_in *addr) { size_t seq, ack; int i; client_t *client; unsigned short qport; seq = ReadLong(packet); if (ReadOverflow(packet)) return; if (seq == 0xffffffff) { ProcessConnectionlessPacket(packet, addr); return; } ack = ReadLong(packet); qport = ReadShort(packet); if (ReadOverflow(packet)) return; for (i = 0; i < server.maxclients; i++) { client = &server.clients[i]; if (qport == client->net.qport && !UDP_AddrCompare(addr, &client->net.addr)) { ProcessClientPacket(packet, client, seq, ack); return; } } } void ReadPackets() { block_t packet; char packet_buffer[MAX_SVSLEN]; struct sockaddr_in addr; BlockInit(&packet, packet_buffer, sizeof(packet_buffer)); for (;;) { BlockRewind(&packet); packet.writeoffset = UDP_Read(server.socket, packet.buffer, packet.size, &addr); if ((signed)packet.writeoffset < 0) { Error("Error reading from socket\n"); return; } // non-fatal error (empty packet, size too big, etc.) if (packet.writeoffset == 0) return; ProcessPacket(&packet, &addr); } } void WritePackets() { int i; client_t *client; block_t out; char out_buffer[MAX_MSGLEN]; size_t len, area_count; byte areas[MAX_MAP_AREAS/8]; qboolean reliable; static player_state_t null_ps; BlockInit(&out, out_buffer, sizeof(out_buffer)); memset(areas, 0xff, sizeof(areas)); area_count = sizeof(areas); for (i = 0, client = server.clients; i < server.maxclients; i++, client++) { if (client->status == CL_UNCONNECTED) continue; if (client->timeout_time <= mstime()) { ClientTimeout(client); continue; } BlockRewind(&out); reliable = false; len = 0; if (client->net.out_reliable_seq) { // reliable packet is in transit, check // if it needs to be resent if (client->net.keepalive_time < mstime()) { UDP_ResendReliable(server.socket, client->reliable.buffer, client->reliable.writeoffset, &client->net); } } else if (client->nextreliable.writeoffset) { // send the queued reliable packet if (WriteOverflow(&client->nextreliable)) { printf("%s overflowed (%d > %d)\n", client->netname, client->nextreliable.writelen, client->nextreliable.size); DropClient(client, "You overflowed\n"); continue; } BlockWrite(&client->reliable, client->nextreliable.buffer, client->nextreliable.writeoffset); BlockWrite(&out, client->nextreliable.buffer, client->nextreliable.writeoffset); BlockRewind(&client->nextreliable); len = out.writeoffset; reliable = true; } if (client->status == CL_CONNECTED) { // write frame information (SVC_FRAME, SVC_PLAYERINFO, SVC_PACKETENTITIES) if (dm2in.states[client->delta_frame & UPDATE_MASK].frame != client->delta_frame) client->delta_frame = BASELINES_FRAME; WriteByte(&out, SVC_FRAME); DM2_WriteFrame(&out, &server.outsvd, dm2in.current_frame, client->delta_frame, area_count, areas, 0, NULL); if (client->delta_frame == BASELINES_FRAME) { WriteByte(&out, SVC_PLAYERINFO); DM2_WritePS(&out, &client->ps, &null_ps); } else { WriteByte(&out, SVC_PLAYERINFO); DM2_WritePS(&out, &client->ps, &client->old[client->delta_frame & UPDATE_MASK].ps); } CL_WriteEntities(&out, client, client->delta_frame); if (!WriteOverflow(&out)) { len = out.writeoffset; client->old[dm2in.current_frame & UPDATE_MASK].ps = client->ps; memcpy(client->old[dm2in.current_frame & UPDATE_MASK].active, client->active, sizeof(client->active)); } } // write unreliable messages if (client->unreliable.writeoffset) { BlockWrite(&out, client->unreliable.buffer, client->unreliable.writeoffset); BlockRewind(&client->unreliable); } if (WriteOverflow(&out)) { printf("%s overflowed unreliable (%u > %u)\n", client->netname, out.writelen, out.size); } else len = out.writeoffset; if (len) { if (reliable) UDP_SendReliable(server.socket, out.buffer, len, &client->net); else UDP_SendUnreliable(server.socket, out.buffer, len, &client->net); } if (client->net.keepalive_time < mstime()) { // send an empty keepalive packet UDP_SendUnreliable(server.socket, NULL, 0, &client->net); } } }