/* ================= CL_SendDefaultCmd ================= */ static void CL_SendDefaultCmd( void ) { size_t cursize, checksumIndex; usercmd_t *cmd, *oldcmd; client_history_t *history; // archive this packet history = &cl.history[cls.netchan->outgoing_sequence & CMD_MASK]; history->cmdNumber = cl.cmdNumber; history->sent = cls.realtime; // for ping calculation history->rcvd = 0; cl.lastTransmitCmdNumber = cl.cmdNumber; // see if we are ready to send this packet if( !ready_to_send_hacked() ) { cls.netchan->outgoing_sequence++; // just drop the packet return; } cl.lastTransmitTime = cls.realtime; cl.lastTransmitCmdNumberReal = cl.cmdNumber; // begin a client move command MSG_WriteByte( clc_move ); // save the position for a checksum byte checksumIndex = 0; if( cls.serverProtocol <= PROTOCOL_VERSION_DEFAULT ) { checksumIndex = msg_write.cursize; SZ_GetSpace( &msg_write, 1 ); } // let the server know what the last frame we // got was, so the next message can be delta compressed if( cl_nodelta->integer || !cl.frame.valid /*|| cls.demowaiting*/ ) { MSG_WriteLong( -1 ); // no compression } else { MSG_WriteLong( cl.frame.number ); } // send this and the previous cmds in the message, so // if the last packet was dropped, it can be recovered cmd = &cl.cmds[( cl.cmdNumber - 2 ) & CMD_MASK]; MSG_WriteDeltaUsercmd( NULL, cmd, cls.protocolVersion ); MSG_WriteByte( cl.lightlevel ); oldcmd = cmd; cmd = &cl.cmds[( cl.cmdNumber - 1 ) & CMD_MASK]; MSG_WriteDeltaUsercmd( oldcmd, cmd, cls.protocolVersion ); MSG_WriteByte( cl.lightlevel ); oldcmd = cmd; cmd = &cl.cmds[cl.cmdNumber & CMD_MASK]; MSG_WriteDeltaUsercmd( oldcmd, cmd, cls.protocolVersion ); MSG_WriteByte( cl.lightlevel ); if( cls.serverProtocol <= PROTOCOL_VERSION_DEFAULT ) { // calculate a checksum over the move commands msg_write.data[checksumIndex] = COM_BlockSequenceCRCByte( msg_write.data + checksumIndex + 1, msg_write.cursize - checksumIndex - 1, cls.netchan->outgoing_sequence ); } P_FRAMES++; // // deliver the message // cursize = cls.netchan->Transmit( cls.netchan, msg_write.cursize, msg_write.data, 1 ); #ifdef _DEBUG if( cl_showpackets->integer ) { Com_Printf( "%"PRIz" ", cursize ); } #endif SZ_Clear( &msg_write ); }
int Datagram_GetMessage (qsocket_t *sock) { unsigned int length; unsigned int flags; int ret = 0; struct qsockaddr readaddr; unsigned int sequence; unsigned int count; if (!sock->canSend) if ((net_time - sock->lastSendTime) > 1.0) ReSendMessage (sock); while(1) { length = sfunc.Read (sock->socket, (byte *)&packetBuffer, NET_DATAGRAMSIZE, &readaddr); // if ((rand() & 255) > 220) // continue; if (length == 0) break; // >>> FIX: For Nintendo Wii using devkitPPC / libogc // sfunc.Read might return error values other than -1. Changing: //if (length == -1) if (length < 0) // <<< FIX { Con_Printf("Read error\n"); return -1; } if (sfunc.AddrCompare(&readaddr, &sock->addr) != 0) { #ifdef DEBUG Con_DPrintf("Forged packet received\n"); Con_DPrintf("Expected: %s\n", StrAddr (&sock->addr)); Con_DPrintf("Received: %s\n", StrAddr (&readaddr)); #endif continue; } if (length < NET_HEADERSIZE) { shortPacketCount++; continue; } length = BigLong(packetBuffer.length); flags = length & (~NETFLAG_LENGTH_MASK); length &= NETFLAG_LENGTH_MASK; if (flags & NETFLAG_CTL) continue; sequence = BigLong(packetBuffer.sequence); packetsReceived++; if (flags & NETFLAG_UNRELIABLE) { if (sequence < sock->unreliableReceiveSequence) { Con_DPrintf("Got a stale datagram\n"); ret = 0; break; } if (sequence != sock->unreliableReceiveSequence) { count = sequence - sock->unreliableReceiveSequence; droppedDatagrams += count; Con_DPrintf("Dropped %u datagram(s)\n", count); } sock->unreliableReceiveSequence = sequence + 1; length -= NET_HEADERSIZE; SZ_Clear (&net_message); SZ_Write (&net_message, packetBuffer.data, length); ret = 2; break; } if (flags & NETFLAG_ACK) { if (sequence != (sock->sendSequence - 1)) { Con_DPrintf("Stale ACK received\n"); continue; } if (sequence == sock->ackSequence) { sock->ackSequence++; if (sock->ackSequence != sock->sendSequence) Con_DPrintf("ack sequencing error\n"); } else { Con_DPrintf("Duplicate ACK received\n"); continue; } sock->sendMessageLength -= MAX_DATAGRAM; if (sock->sendMessageLength > 0) { Q_memcpy(sock->sendMessage, sock->sendMessage+MAX_DATAGRAM, sock->sendMessageLength); sock->sendNext = true; } else { sock->sendMessageLength = 0; sock->canSend = true; } continue; } if (flags & NETFLAG_DATA) { packetBuffer.length = BigLong(NET_HEADERSIZE | NETFLAG_ACK); packetBuffer.sequence = BigLong(sequence); sfunc.Write (sock->socket, (byte *)&packetBuffer, NET_HEADERSIZE, &readaddr); if (sequence != sock->receiveSequence) { receivedDuplicateCount++; continue; } sock->receiveSequence++; length -= NET_HEADERSIZE; if (flags & NETFLAG_EOM) { SZ_Clear(&net_message); SZ_Write(&net_message, sock->receiveMessage, sock->receiveMessageLength); SZ_Write(&net_message, packetBuffer.data, length); sock->receiveMessageLength = 0; ret = 1; break; } Q_memcpy(sock->receiveMessage + sock->receiveMessageLength, packetBuffer.data, length); sock->receiveMessageLength += length; continue; } } if (sock->sendNext) SendMessageNext (sock); return ret; }
static qsocket_t *_Datagram_CheckNewConnections (void) { struct qsockaddr clientaddr; struct qsockaddr newaddr; int newsock; int acceptsock; qsocket_t *sock; qsocket_t *s; int len; int command; int control; int ret; acceptsock = dfunc.CheckNewConnections(); if (acceptsock == -1) return NULL; SZ_Clear(&net_message); len = dfunc.Read (acceptsock, net_message.data, net_message.maxsize, &clientaddr); if (len < sizeof(int)) return NULL; net_message.cursize = len; MSG_BeginReading (); control = BigLong(*((int *)net_message.data)); MSG_ReadLong(); if (control == -1) return NULL; if ((control & (~NETFLAG_LENGTH_MASK)) != NETFLAG_CTL) return NULL; if ((control & NETFLAG_LENGTH_MASK) != len) return NULL; command = MSG_ReadByte(); if (command == CCREQ_SERVER_INFO) { if (Q_strcmp(MSG_ReadString(), "QUAKE") != 0) return NULL; SZ_Clear(&net_message); // save space for the header, filled in later MSG_WriteLong(&net_message, 0); MSG_WriteByte(&net_message, CCREP_SERVER_INFO); dfunc.GetSocketAddr(acceptsock, &newaddr); MSG_WriteString(&net_message, dfunc.AddrToString(&newaddr)); MSG_WriteString(&net_message, hostname.string); MSG_WriteString(&net_message, sv.name); MSG_WriteByte(&net_message, net_activeconnections); MSG_WriteByte(&net_message, svs.maxclients); MSG_WriteByte(&net_message, NET_PROTOCOL_VERSION); *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr); SZ_Clear(&net_message); return NULL; } if (command == CCREQ_PLAYER_INFO) { int playerNumber; int activeNumber; int clientNumber; client_t *client; playerNumber = MSG_ReadByte(); activeNumber = -1; for (clientNumber = 0, client = svs.clients; clientNumber < svs.maxclients; clientNumber++, client++) { if (client->active) { activeNumber++; if (activeNumber == playerNumber) break; } } if (clientNumber == svs.maxclients) return NULL; SZ_Clear(&net_message); // save space for the header, filled in later MSG_WriteLong(&net_message, 0); MSG_WriteByte(&net_message, CCREP_PLAYER_INFO); MSG_WriteByte(&net_message, playerNumber); MSG_WriteString(&net_message, client->name); MSG_WriteLong(&net_message, client->colors); MSG_WriteLong(&net_message, (int)client->edict->v.frags); MSG_WriteLong(&net_message, (int)(net_time - client->netconnection->connecttime)); MSG_WriteString(&net_message, client->netconnection->address); *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr); SZ_Clear(&net_message); return NULL; } if (command == CCREQ_RULE_INFO) { char *prevCvarName; cvar_t *var; // find the search start location prevCvarName = MSG_ReadString(); if (*prevCvarName) { var = Cvar_FindVar (prevCvarName); if (!var) return NULL; var = var->next; } else var = cvar_vars; // search for the next server cvar while (var) { if (var->server) break; var = var->next; } // send the response SZ_Clear(&net_message); // save space for the header, filled in later MSG_WriteLong(&net_message, 0); MSG_WriteByte(&net_message, CCREP_RULE_INFO); if (var) { MSG_WriteString(&net_message, var->name); MSG_WriteString(&net_message, var->string); } *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr); SZ_Clear(&net_message); return NULL; } if (command != CCREQ_CONNECT) return NULL; if (Q_strcmp(MSG_ReadString(), "QUAKE") != 0) return NULL; if (MSG_ReadByte() != NET_PROTOCOL_VERSION) { SZ_Clear(&net_message); // save space for the header, filled in later MSG_WriteLong(&net_message, 0); MSG_WriteByte(&net_message, CCREP_REJECT); MSG_WriteString(&net_message, "Incompatible version.\n"); *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr); SZ_Clear(&net_message); return NULL; } #ifdef BAN_TEST // check for a ban if (clientaddr.sa_family == AF_INET) { unsigned long testAddr; testAddr = ((struct sockaddr_in *)&clientaddr)->sin_addr.s_addr; if ((testAddr & banMask) == banAddr) { SZ_Clear(&net_message); // save space for the header, filled in later MSG_WriteLong(&net_message, 0); MSG_WriteByte(&net_message, CCREP_REJECT); MSG_WriteString(&net_message, "You have been banned.\n"); *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr); SZ_Clear(&net_message); return NULL; } } #endif // see if this guy is already connected for (s = net_activeSockets; s; s = s->next) { if (s->driver != net_driverlevel) continue; ret = dfunc.AddrCompare(&clientaddr, &s->addr); if (ret >= 0) { // is this a duplicate connection reqeust? if (ret == 0 && net_time - s->connecttime < 2.0) { // yes, so send a duplicate reply SZ_Clear(&net_message); // save space for the header, filled in later MSG_WriteLong(&net_message, 0); MSG_WriteByte(&net_message, CCREP_ACCEPT); dfunc.GetSocketAddr(s->socket, &newaddr); MSG_WriteLong(&net_message, dfunc.GetSocketPort(&newaddr)); *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr); SZ_Clear(&net_message); return NULL; } // it's somebody coming back in from a crash/disconnect // so close the old qsocket and let their retry get them back in NET_Close(s); return NULL; } } // allocate a QSocket sock = NET_NewQSocket (); if (sock == NULL) { // no room; try to let him know SZ_Clear(&net_message); // save space for the header, filled in later MSG_WriteLong(&net_message, 0); MSG_WriteByte(&net_message, CCREP_REJECT); MSG_WriteString(&net_message, "Server is full.\n"); *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr); SZ_Clear(&net_message); return NULL; } // allocate a network socket newsock = dfunc.OpenSocket(0); if (newsock == -1) { NET_FreeQSocket(sock); return NULL; } // connect to the client if (dfunc.Connect (newsock, &clientaddr) == -1) { dfunc.CloseSocket(newsock); NET_FreeQSocket(sock); return NULL; } // everything is allocated, just fill in the details sock->socket = newsock; sock->landriver = net_landriverlevel; sock->addr = clientaddr; Q_strcpy(sock->address, dfunc.AddrToString(&clientaddr)); // send him back the info about the server connection he has been allocated SZ_Clear(&net_message); // save space for the header, filled in later MSG_WriteLong(&net_message, 0); MSG_WriteByte(&net_message, CCREP_ACCEPT); dfunc.GetSocketAddr(newsock, &newaddr); MSG_WriteLong(&net_message, dfunc.GetSocketPort(&newaddr)); // MSG_WriteString(&net_message, dfunc.AddrToString(&newaddr)); *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr); SZ_Clear(&net_message); return sock; }
int Datagram_GetMessage (qsocket_t *sock) { unsigned int length; unsigned int flags; int ret = 0; struct qsockaddr readaddr; unsigned int sequence; unsigned int count; if (!sock->canSend) if ((net_time - sock->lastSendTime) > 1.0) ReSendMessage (sock); while(1) { length = sock->landriver->Read(sock->socket, (byte *)&packetBuffer, NET_DATAGRAMSIZE, &readaddr); /* // for testing packet loss effects if ((rand() & 255) > 220) continue; */ if (length == 0) break; if ((int)length == -1) { Con_Printf("Read error\n"); return -1; } // added !sock->net_wait (NAT fix) if (!sock->net_wait && sock->landriver->AddrCompare(&readaddr, &sock->addr) != 0) { #ifdef DEBUG Con_DPrintf("Forged packet received\n"); Con_DPrintf("Expected: %s\n", StrAddr (&sock->addr)); Con_DPrintf("Received: %s\n", StrAddr (&readaddr)); #endif continue; } if (length < NET_HEADERSIZE) { shortPacketCount++; continue; } length = BigLong(packetBuffer.length); flags = length & (~NETFLAG_LENGTH_MASK); length &= NETFLAG_LENGTH_MASK; // ProQuake extra protection if (length > NET_DATAGRAMSIZE) { Con_Warning ("Datagram_GetMessage: excessive datagram length (%d, max = %d)\n", length, NET_DATAGRAMSIZE); return -1; } if (flags & NETFLAG_CTL) continue; sequence = BigLong(packetBuffer.sequence); packetsReceived++; // NAT fix if (sock->net_wait) { sock->addr = readaddr; strcpy(sock->address, sock->landriver->AddrToString(&readaddr)); sock->net_wait = false; } if (flags & NETFLAG_UNRELIABLE) { if (sequence < sock->unreliableReceiveSequence) { Con_DPrintf("Got a stale datagram\n"); ret = 0; break; } if (sequence != sock->unreliableReceiveSequence) { count = sequence - sock->unreliableReceiveSequence; droppedDatagrams += count; Con_DPrintf("Dropped %u datagram(s)\n", count); } sock->unreliableReceiveSequence = sequence + 1; length -= NET_HEADERSIZE; SZ_Clear (net_message->message); SZ_Write (net_message->message, packetBuffer.data, length); ret = 2; break; } if (flags & NETFLAG_ACK) { if (sequence != (sock->sendSequence - 1)) { Con_DPrintf("Stale ACK received\n"); continue; } if (sequence == sock->ackSequence) { sock->ackSequence++; if (sock->ackSequence != sock->sendSequence) Con_DPrintf("ack sequencing error\n"); } else { Con_DPrintf("Duplicate ACK received\n"); continue; } sock->sendMessageLength -= MAX_DATAGRAM; if (sock->sendMessageLength > 0) { // FIXME: was memcpy memmove(sock->sendMessage, sock->sendMessage + MAX_DATAGRAM, sock->sendMessageLength); sock->sendNext = true; } else { sock->sendMessageLength = 0; sock->canSend = true; } continue; } if (flags & NETFLAG_DATA) { packetBuffer.length = BigLong(NET_HEADERSIZE | NETFLAG_ACK); packetBuffer.sequence = BigLong(sequence); sock->landriver->Write(sock->socket, (byte *)&packetBuffer, NET_HEADERSIZE, &readaddr); if (sequence != sock->receiveSequence) { receivedDuplicateCount++; continue; } sock->receiveSequence++; length -= NET_HEADERSIZE; if (flags & NETFLAG_EOM) { SZ_Clear(net_message->message); SZ_Write(net_message->message, sock->receiveMessage, sock->receiveMessageLength); SZ_Write(net_message->message, packetBuffer.data, length); sock->receiveMessageLength = 0; ret = 1; break; } memcpy(sock->receiveMessage + sock->receiveMessageLength, packetBuffer.data, length); sock->receiveMessageLength += length; continue; } } if (sock->sendNext) SendMessageNext (sock); return ret; }
static void _Datagram_SearchForHosts (qboolean xmit) { int ret; int n; int i; struct qsockaddr readaddr; struct qsockaddr myaddr; int control; dfunc.GetSocketAddr (dfunc.controlSock, &myaddr); if (xmit) { SZ_Clear(&net_message); // save space for the header, filled in later MSG_WriteLong(&net_message, 0); MSG_WriteByte(&net_message, CCREQ_SERVER_INFO); MSG_WriteString(&net_message, "QUAKE"); MSG_WriteByte(&net_message, NET_PROTOCOL_VERSION); *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); dfunc.Broadcast(dfunc.controlSock, net_message.data, net_message.cursize); SZ_Clear(&net_message); } while ((ret = dfunc.Read (dfunc.controlSock, net_message.data, net_message.maxsize, &readaddr)) > 0) { if (ret < sizeof(int)) continue; net_message.cursize = ret; // don't answer our own query if (dfunc.AddrCompare(&readaddr, &myaddr) >= 0) continue; // is the cache full? if (hostCacheCount == HOSTCACHESIZE) continue; MSG_BeginReading (); control = BigLong(*((int *)net_message.data)); MSG_ReadLong(); if (control == -1) continue; if ((control & (~NETFLAG_LENGTH_MASK)) != NETFLAG_CTL) continue; if ((control & NETFLAG_LENGTH_MASK) != ret) continue; if (MSG_ReadByte() != CCREP_SERVER_INFO) continue; dfunc.GetAddrFromName(MSG_ReadString(), &readaddr); // search the cache for this server for (n = 0; n < hostCacheCount; n++) if (dfunc.AddrCompare(&readaddr, &hostcache[n].addr) == 0) break; // is it already there? if (n < hostCacheCount) continue; // add it hostCacheCount++; Q_strcpy(hostcache[n].name, MSG_ReadString()); Q_strcpy(hostcache[n].map, MSG_ReadString()); hostcache[n].users = MSG_ReadByte(); hostcache[n].maxusers = MSG_ReadByte(); if (MSG_ReadByte() != NET_PROTOCOL_VERSION) { Q_strcpy(hostcache[n].cname, hostcache[n].name); hostcache[n].cname[14] = 0; Q_strcpy(hostcache[n].name, "*"); Q_strcat(hostcache[n].name, hostcache[n].cname); } Q_memcpy(&hostcache[n].addr, &readaddr, sizeof(struct qsockaddr)); hostcache[n].driver = net_driverlevel; hostcache[n].ldriver = net_landriverlevel; Q_strcpy(hostcache[n].cname, dfunc.AddrToString(&readaddr)); // check for a name conflict for (i = 0; i < hostCacheCount; i++) { if (i == n) continue; if (Q_strcasecmp (hostcache[n].name, hostcache[i].name) == 0) { i = Q_strlen(hostcache[n].name); if (i < 15 && hostcache[n].name[i-1] > '8') { hostcache[n].name[i] = '0'; hostcache[n].name[i+1] = 0; } else hostcache[n].name[i-1]++; i = -1; } } } }
void EXT_FUNC SZ_Clear_api(sizebuf_t *buf) { SZ_Clear(buf); }
static qbool SV_MVD_Record (mvddest_t *dest) { sizebuf_t buf; byte buf_data[MAX_MSGLEN]; int n, i; char *s, info[MAX_INFO_STRING]; client_t *player; char *gamedir; if (!dest) return false; DestFlush(true); if (!sv.mvdrecording) { memset(&demo, 0, sizeof(demo)); for (i = 0; i < UPDATE_BACKUP; i++) { demo.recorder.frames[i] = demo_frames[i]; demo.recorder.frames[i].entities.entities = demo_entities[i]; } MVDBuffer_Init(&demo.dbuffer, demo.buffer, sizeof(demo.buffer)); MVDSetMsgBuf(NULL, &demo.frames[0].buf); demo.datagram.maxsize = sizeof(demo.datagram_data); demo.datagram.data = demo.datagram_data; sv.mvdrecording = true; } // else // SV_WriteRecordMVDMessage(&buf); demo.pingtime = demo.time = sv.time; dest->nextdest = demo.dest; demo.dest = dest; singledest = dest; /*-------------------------------------------------*/ // serverdata // send the info about the new client to all connected clients SZ_Init (&buf, buf_data, sizeof(buf_data)); // send the serverdata gamedir = Info_ValueForKey (svs.info, "*gamedir"); if (!gamedir[0]) gamedir = "qw"; MSG_WriteByte (&buf, svc_serverdata); MSG_WriteLong (&buf, PROTOCOL_VERSION); MSG_WriteLong (&buf, svs.spawncount); MSG_WriteString (&buf, gamedir); MSG_WriteFloat (&buf, sv.time); // send full levelname MSG_WriteString (&buf, sv.mapname); // send the movevars MSG_WriteFloat(&buf, sv.movevars.gravity); MSG_WriteFloat(&buf, sv.movevars.stopspeed); MSG_WriteFloat(&buf, sv.movevars.maxspeed); MSG_WriteFloat(&buf, sv.movevars.spectatormaxspeed); MSG_WriteFloat(&buf, sv.movevars.accelerate); MSG_WriteFloat(&buf, sv.movevars.airaccelerate); MSG_WriteFloat(&buf, sv.movevars.wateraccelerate); MSG_WriteFloat(&buf, sv.movevars.friction); MSG_WriteFloat(&buf, sv.movevars.waterfriction); MSG_WriteFloat(&buf, sv.movevars.entgravity); // send music MSG_WriteByte (&buf, svc_cdtrack); MSG_WriteByte (&buf, 0); // none in demos // send server info string MSG_WriteByte (&buf, svc_stufftext); MSG_WriteString (&buf, va("fullserverinfo \"%s\"\n", svs.info) ); // flush packet SV_WriteRecordMVDMessage (&buf); SZ_Clear (&buf); // soundlist MSG_WriteByte (&buf, svc_soundlist); MSG_WriteByte (&buf, 0); n = 0; s = sv.sound_name[n+1]; while (s) { MSG_WriteString (&buf, s); if (buf.cursize > MAX_MSGLEN/2) { MSG_WriteByte (&buf, 0); MSG_WriteByte (&buf, n); SV_WriteRecordMVDMessage (&buf); SZ_Clear (&buf); MSG_WriteByte (&buf, svc_soundlist); MSG_WriteByte (&buf, n + 1); } n++; s = sv.sound_name[n+1]; } if (buf.cursize) { MSG_WriteByte (&buf, 0); MSG_WriteByte (&buf, 0); SV_WriteRecordMVDMessage (&buf); SZ_Clear (&buf); } // modellist MSG_WriteByte (&buf, svc_modellist); MSG_WriteByte (&buf, 0); n = 0; s = sv.model_name[n+1]; while (s) { MSG_WriteString (&buf, s); if (buf.cursize > MAX_MSGLEN/2) { MSG_WriteByte (&buf, 0); MSG_WriteByte (&buf, n); SV_WriteRecordMVDMessage (&buf); SZ_Clear (&buf); MSG_WriteByte (&buf, svc_modellist); MSG_WriteByte (&buf, n + 1); } n++; s = sv.model_name[n+1]; } if (buf.cursize) { MSG_WriteByte (&buf, 0); MSG_WriteByte (&buf, 0); SV_WriteRecordMVDMessage (&buf); SZ_Clear (&buf); } // baselines { entity_state_t from; edict_t *ent; entity_state_t *state; memset(&from, 0, sizeof(from)); for (n = 0; n < sv.num_edicts; n++) { ent = EDICT_NUM(n); state = &ent->baseline; if (!state->number || !state->modelindex) { //ent doesn't have a baseline continue; } if (!ent) { MSG_WriteByte(&buf, svc_spawnbaseline); MSG_WriteShort (&buf, n); MSG_WriteByte (&buf, 0); MSG_WriteByte (&buf, 0); MSG_WriteByte (&buf, 0); MSG_WriteByte (&buf, 0); for (i=0 ; i<3 ; i++) { MSG_WriteCoord(&buf, 0); MSG_WriteAngle(&buf, 0); } } else { MSG_WriteByte(&buf, svc_spawnbaseline); MSG_WriteShort (&buf, n); MSG_WriteByte (&buf, state->modelindex&255); MSG_WriteByte (&buf, state->frame); MSG_WriteByte (&buf, (int)state->colormap); MSG_WriteByte (&buf, (int)state->skinnum); for (i=0 ; i<3 ; i++) { MSG_WriteCoord(&buf, state->s_origin[i]); MSG_WriteAngle(&buf, state->s_angles[i]); } } if (buf.cursize > MAX_MSGLEN/2) { SV_WriteRecordMVDMessage (&buf); SZ_Clear (&buf); } } } //prespawn for (n = 0; n < sv.num_signon_buffers; n++) { if (buf.cursize+sv.signon_buffer_size[n] > MAX_MSGLEN/2) { SV_WriteRecordMVDMessage (&buf); SZ_Clear (&buf); } SZ_Write (&buf, sv.signon_buffers[n], sv.signon_buffer_size[n]); } if (buf.cursize > MAX_MSGLEN/2) { SV_WriteRecordMVDMessage (&buf); SZ_Clear (&buf); } MSG_WriteByte (&buf, svc_stufftext); MSG_WriteString (&buf, va("cmd spawn %i\n",svs.spawncount) ); if (buf.cursize) { SV_WriteRecordMVDMessage (&buf); SZ_Clear (&buf); } // send current status of all other players for (i = 0; i < MAX_CLIENTS; i++) { player = svs.clients + i; MSG_WriteByte (&buf, svc_updatefrags); MSG_WriteByte (&buf, i); MSG_WriteShort (&buf, player->old_frags); MSG_WriteByte (&buf, svc_updateping); MSG_WriteByte (&buf, i); MSG_WriteShort (&buf, SV_CalcPing(player)); MSG_WriteByte (&buf, svc_updatepl); MSG_WriteByte (&buf, i); MSG_WriteByte (&buf, player->lossage); MSG_WriteByte (&buf, svc_updateentertime); MSG_WriteByte (&buf, i); MSG_WriteFloat (&buf, svs.realtime - player->connection_started); Q_strncpyz (info, player->userinfo, MAX_INFO_STRING); Info_RemovePrefixedKeys (info, '_'); // server passwords, etc MSG_WriteByte (&buf, svc_updateuserinfo); MSG_WriteByte (&buf, i); MSG_WriteLong (&buf, player->userid); MSG_WriteString (&buf, info); if (buf.cursize > MAX_MSGLEN/2) { SV_WriteRecordMVDMessage (&buf); SZ_Clear (&buf); } } // send all current light styles for (i=0 ; i<MAX_LIGHTSTYLES ; i++) { MSG_WriteByte (&buf, svc_lightstyle); MSG_WriteByte (&buf, (char)i); MSG_WriteString (&buf, sv.lightstyles[i]); } // get the client to check and download skins // when that is completed, a begin command will be issued MSG_WriteByte (&buf, svc_stufftext); MSG_WriteString (&buf, "skins\n"); SV_WriteRecordMVDMessage (&buf); SV_WriteSetMVDMessage(); singledest = NULL; // done return true; }
/* ==================== CL_Record Called by CL_Record_f and CL_EasyRecord_f ==================== */ static void CL_Record (void) { sizebuf_t buf; byte buf_data[MAX_MSGLEN]; int n, i, j; char *s; entity_t *ent; entity_state_t *es, blankes; player_info_t *player; int seq = 1; #ifdef MVDPLAY if (cls.mvdplayback) { Com_Printf ("Can't record while playing MVD demo.\n"); return; } #endif cls.demorecording = true; /*-------------------------------------------------*/ // serverdata // send the info about the new client to all connected clients SZ_Init (&buf, buf_data, sizeof(buf_data)); // send the serverdata MSG_WriteByte (&buf, svc_serverdata); MSG_WriteLong (&buf, PROTOCOL_VERSION); MSG_WriteLong (&buf, cl.servercount); MSG_WriteString (&buf, cls.gamedirfile); if (cl.spectator) MSG_WriteByte (&buf, cl.playernum | 128); else MSG_WriteByte (&buf, cl.playernum); // send full levelname MSG_WriteString (&buf, cl.levelname); // send the movevars MSG_WriteFloat(&buf, movevars.gravity); MSG_WriteFloat(&buf, movevars.stopspeed); MSG_WriteFloat(&buf, cl.maxspeed); MSG_WriteFloat(&buf, movevars.spectatormaxspeed); MSG_WriteFloat(&buf, movevars.accelerate); MSG_WriteFloat(&buf, movevars.airaccelerate); MSG_WriteFloat(&buf, movevars.wateraccelerate); MSG_WriteFloat(&buf, movevars.friction); MSG_WriteFloat(&buf, movevars.waterfriction); MSG_WriteFloat(&buf, cl.entgravity); // send music MSG_WriteByte (&buf, svc_cdtrack); MSG_WriteByte (&buf, 0); // none in demos // send server info string MSG_WriteByte (&buf, svc_stufftext); MSG_WriteString (&buf, va("fullserverinfo \"%s\"\n", cl.serverinfo) ); // flush packet CL_WriteRecordDemoMessage (&buf, seq++); SZ_Clear (&buf); // soundlist MSG_WriteByte (&buf, svc_soundlist); MSG_WriteByte (&buf, 0); n = 0; s = cl.sound_name[n+1]; while (*s) { MSG_WriteString (&buf, s); if (buf.cursize > MAX_MSGLEN/2) { MSG_WriteByte (&buf, 0); MSG_WriteByte (&buf, n); CL_WriteRecordDemoMessage (&buf, seq++); SZ_Clear (&buf); MSG_WriteByte (&buf, svc_soundlist); MSG_WriteByte (&buf, n + 1); } n++; s = cl.sound_name[n+1]; } if (buf.cursize) { MSG_WriteByte (&buf, 0); MSG_WriteByte (&buf, 0); CL_WriteRecordDemoMessage (&buf, seq++); SZ_Clear (&buf); } #ifdef VWEP_TEST // vwep modellist if ((cl.z_ext & Z_EXT_VWEP) && cl.vw_model_name[0][0]) { // send VWep precaches // pray we don't overflow for (i = 0; i < MAX_VWEP_MODELS; i++) { s = cl.vw_model_name[i]; if (!*s) continue; MSG_WriteByte (&buf, svc_serverinfo); MSG_WriteString (&buf, "#vw"); MSG_WriteString (&buf, va("%i %s", i, TrimModelName(s))); } // send end-of-list messsage MSG_WriteByte (&buf, svc_serverinfo); MSG_WriteString (&buf, "#vw"); MSG_WriteString (&buf, ""); } // don't bother flushing, the vwep list is not that large (I hope) #endif // modellist MSG_WriteByte (&buf, svc_modellist); MSG_WriteByte (&buf, 0); n = 0; s = cl.model_name[n+1]; while (*s) { MSG_WriteString (&buf, s); if (buf.cursize > MAX_MSGLEN/2) { MSG_WriteByte (&buf, 0); MSG_WriteByte (&buf, n); CL_WriteRecordDemoMessage (&buf, seq++); SZ_Clear (&buf); MSG_WriteByte (&buf, svc_modellist); MSG_WriteByte (&buf, n + 1); } n++; s = cl.model_name[n+1]; } if (buf.cursize) { MSG_WriteByte (&buf, 0); MSG_WriteByte (&buf, 0); CL_WriteRecordDemoMessage (&buf, seq++); SZ_Clear (&buf); } // spawnstatic for (i = 0; i < cl.num_statics; i++) { ent = cl_static_entities + i; MSG_WriteByte (&buf, svc_spawnstatic); for (j = 1; j < MAX_MODELS; j++) if (ent->model == cl.model_precache[j]) break; if (j == MAX_MODELS) MSG_WriteByte (&buf, 0); else MSG_WriteByte (&buf, j); MSG_WriteByte (&buf, ent->frame); MSG_WriteByte (&buf, 0); MSG_WriteByte (&buf, ent->skinnum); for (j=0 ; j<3 ; j++) { MSG_WriteCoord (&buf, ent->origin[j]); MSG_WriteAngle (&buf, ent->angles[j]); } if (buf.cursize > MAX_MSGLEN/2) { CL_WriteRecordDemoMessage (&buf, seq++); SZ_Clear (&buf); } } // spawnstaticsound for (i = 0; i < cl.num_static_sounds; i++) { static_sound_t *ss = &cl.static_sounds[i]; MSG_WriteByte (&buf, svc_spawnstaticsound); for (j = 0; j < 3; j++) MSG_WriteCoord (&buf, ss->org[j]); MSG_WriteByte (&buf, ss->sound_num); MSG_WriteByte (&buf, ss->vol); MSG_WriteByte (&buf, ss->atten); if (buf.cursize > MAX_MSGLEN/2) { CL_WriteRecordDemoMessage (&buf, seq++); SZ_Clear (&buf); } } // baselines memset(&blankes, 0, sizeof(blankes)); for (i = 0; i < MAX_CL_EDICTS; i++) { es = &cl_entities[i].baseline; if (memcmp(es, &blankes, sizeof(blankes))) { MSG_WriteByte (&buf,svc_spawnbaseline); MSG_WriteShort (&buf, i); MSG_WriteByte (&buf, es->modelindex); MSG_WriteByte (&buf, es->frame); MSG_WriteByte (&buf, es->colormap); MSG_WriteByte (&buf, es->skinnum); for (j=0 ; j<3 ; j++) { MSG_WriteShort (&buf, es->s_origin[j]); MSG_WriteByte (&buf, es->s_angles[j]); } if (buf.cursize > MAX_MSGLEN/2) { CL_WriteRecordDemoMessage (&buf, seq++); SZ_Clear (&buf); } } } MSG_WriteByte (&buf, svc_stufftext); MSG_WriteString (&buf, va("cmd spawn %i 0\n", cl.servercount) ); if (buf.cursize) { CL_WriteRecordDemoMessage (&buf, seq++); SZ_Clear (&buf); } // send current status of all other players for (i = 0; i < MAX_CLIENTS; i++) { player = cl.players + i; MSG_WriteByte (&buf, svc_updatefrags); MSG_WriteByte (&buf, i); MSG_WriteShort (&buf, player->frags); MSG_WriteByte (&buf, svc_updateping); MSG_WriteByte (&buf, i); MSG_WriteShort (&buf, player->ping); MSG_WriteByte (&buf, svc_updatepl); MSG_WriteByte (&buf, i); MSG_WriteByte (&buf, player->pl); MSG_WriteByte (&buf, svc_updateentertime); MSG_WriteByte (&buf, i); MSG_WriteFloat (&buf, cls.realtime - player->entertime); MSG_WriteByte (&buf, svc_updateuserinfo); MSG_WriteByte (&buf, i); MSG_WriteLong (&buf, player->userid); MSG_WriteString (&buf, player->userinfo); if (buf.cursize > MAX_MSGLEN/2) { CL_WriteRecordDemoMessage (&buf, seq++); SZ_Clear (&buf); } } // send all current light styles for (i=0 ; i<MAX_LIGHTSTYLES ; i++) { if (!cl_lightstyle[i].length) continue; // don't send empty lightstyle strings MSG_WriteByte (&buf, svc_lightstyle); MSG_WriteByte (&buf, (char)i); MSG_WriteString (&buf, cl_lightstyle[i].map); } for (i = 0; i < MAX_CL_STATS; i++) { if (!cl.stats[i]) continue; // no need to send zero values if (cl.stats[i] >= 0 && cl.stats[i] <= 255) { MSG_WriteByte (&buf, svc_updatestat); MSG_WriteByte (&buf, i); MSG_WriteByte (&buf, cl.stats[i]); } else { MSG_WriteByte (&buf, svc_updatestatlong); MSG_WriteByte (&buf, i); MSG_WriteLong (&buf, cl.stats[i]); } if (buf.cursize > MAX_MSGLEN/2) { CL_WriteRecordDemoMessage (&buf, seq++); SZ_Clear (&buf); } } // get the client to check and download skins // when that is completed, a begin command will be issued MSG_WriteByte (&buf, svc_stufftext); MSG_WriteString (&buf, va("skins\n") ); CL_WriteRecordDemoMessage (&buf, seq++); CL_WriteSetDemoMessage(); // done }
// clears both cl->netchan.message and backbuf void SV_ClearReliable (client_t *cl) { SZ_Clear (&cl->netchan.message); SV_ClearBackbuf (cl); }
bool NetDemo::startRecording(const std::string &filename) { this->filename = filename; if (isPlaying() || isPaused()) { error("Cannot record a netdemo while not connected to a server."); return false; } // Already recording so just ignore the command if (isRecording()) return true; if (demofp != NULL) // file is already open for some reason { fclose(demofp); demofp = NULL; } demofp = fopen(filename.c_str(), "wb"); if (!demofp) { error("Unable to create netdemo file " + filename + "."); return false; } memset(&header, 0, sizeof(header)); // Note: The header is not finalized at this point. Write it anyway to // reserve space in the output file for it and overwrite it later. if (!writeHeader()) { error("Unable to write netdemo header."); return false; } state = NetDemo::st_recording; header.starting_gametic = gametic; Printf(PRINT_HIGH, "Recording netdemo %s.\n", filename.c_str()); if (connected) { // write a simulation of the connection sequence since the server // has already sent it to the client and it wasn't captured static buf_t tempbuf(MAX_UDP_PACKET); // Fake the launcher query response SZ_Clear(&tempbuf); writeLauncherSequence(&tempbuf); capture(&tempbuf); writeMessages(); // Fake the server's side of the connection sequence SZ_Clear(&tempbuf); writeConnectionSequence(&tempbuf); capture(&tempbuf); writeMessages(); // Record any additional messages (usually a full update if auto-recording)) capture(&net_message); writeMessages(); SZ_Clear(&tempbuf); MSG_WriteMarker(&tempbuf, svc_netdemoloadsnap); capture(&tempbuf); writeMessages(); } return true; }
/* ==================== CL_GetDemoMessage FIXME... ==================== */ qbool CL_GetDemoMessage (void) { int r, i, j; float demotime; byte c; usercmd_t *pcmd; #ifdef MVDPLAY byte msec; #endif if (qwz_unpacking) return false; if (cl.paused & PAUSED_DEMO) return false; readnext: // read the time from the packet #ifdef MVDPLAY if (cls.mvdplayback) { fread(&msec, sizeof(msec), 1, cls.demofile); demotime = cls.mvd_newtime + msec * 0.001; } else #endif { fread(&demotime, sizeof(demotime), 1, cls.demofile); demotime = LittleFloat(demotime); } // decide if it is time to grab the next message if (cls.timedemo) { if (cls.td_lastframe < 0) cls.td_lastframe = demotime; else if (demotime > cls.td_lastframe) { cls.td_lastframe = demotime; // rewind back to time #ifdef MVDPLAY if (cls.mvdplayback) { fseek(cls.demofile, ftell(cls.demofile) - sizeof(msec), SEEK_SET); } else #endif fseek(cls.demofile, ftell(cls.demofile) - sizeof(demotime), SEEK_SET); return false; // already read this frame's message } if (!cls.td_starttime && cls.state == ca_active) { cls.td_starttime = Sys_DoubleTime(); cls.td_startframe = cls.framecount; } cls.demotime = demotime; // warp } else if (!(cl.paused & PAUSED_SERVER) && cls.state >= ca_active) { // always grab until active #ifdef MVDPLAY if (cls.mvdplayback) { if (msec/* a hack! */ && cls.demotime < cls.mvd_newtime) { fseek(cls.demofile, ftell(cls.demofile) - sizeof(msec), SEEK_SET); return false; } } else #endif if (cls.demotime < demotime) { if (cls.demotime + 1.0 < demotime) cls.demotime = demotime - 1.0; // too far back // rewind back to time fseek(cls.demofile, ftell(cls.demofile) - sizeof(demotime), SEEK_SET); return false; // don't need another message yet } } else cls.demotime = demotime; // we're warping #ifdef MVDPLAY if (cls.mvdplayback) { if (msec) { extern void CL_ParseClientdata (); cls.mvd_oldtime = cls.mvd_newtime; cls.mvd_newtime = demotime; cls.netchan.incoming_sequence++; cls.netchan.incoming_acknowledged++; CL_ParseClientdata(); } } #endif if (cls.state < ca_demostart) Host_Error ("CL_GetDemoMessage: cls.state < ca_demostart"); // get the msg type r = fread (&c, sizeof(c), 1, cls.demofile); if (r != 1) Host_Error ("Unexpected end of demo"); #ifdef MVDPLAY switch (cls.mvdplayback ? c&7 : c) { #else switch (c) { #endif case dem_cmd : // user sent input i = cls.netchan.outgoing_sequence & UPDATE_MASK; pcmd = &cl.frames[i].cmd; r = fread (pcmd, sizeof(*pcmd), 1, cls.demofile); if (r != 1) Host_Error ("Unexpected end of demo"); // byte order stuff for (j = 0; j < 3; j++) pcmd->angles[j] = LittleFloat(pcmd->angles[j]); pcmd->forwardmove = LittleShort(pcmd->forwardmove); pcmd->sidemove = LittleShort(pcmd->sidemove); pcmd->upmove = LittleShort(pcmd->upmove); cl.frames[i].senttime = demotime + (cls.realtime - cls.demotime); cl.frames[i].receivedtime = -1; // we haven't gotten a reply yet cls.netchan.outgoing_sequence++; fread (cl.viewangles, 12, 1, cls.demofile); for (i = 0; i < 3; i++) cl.viewangles[i] = LittleFloat (cl.viewangles[i]); if (cl.spectator) Cam_TryLock(); goto readnext; case dem_read: #ifdef MVDPLAY readit: #endif // get the next message fread (&net_message.cursize, 4, 1, cls.demofile); net_message.cursize = LittleLong (net_message.cursize); if (net_message.cursize > MAX_BIG_MSGLEN) Host_Error ("Demo message > MAX_BIG_MSGLEN"); r = fread (net_message.data, net_message.cursize, 1, cls.demofile); if (r != 1) Host_Error ("Unexpected end of demo"); #ifdef MVDPLAY if (cls.mvdplayback) { switch(cls.mvd_lasttype) { case dem_multiple: if (!cam_locked || !(cls.mvd_lastto & (1 << cam_curtarget))) goto readnext; break; case dem_single: if (!cam_locked || cls.mvd_lastto != cam_curtarget) goto readnext; break; } } #endif return true; case dem_set: fread (&i, 4, 1, cls.demofile); cls.netchan.outgoing_sequence = LittleLong(i); fread (&i, 4, 1, cls.demofile); cls.netchan.incoming_sequence = LittleLong(i); #ifdef MVDPLAY if (cls.mvdplayback) cls.netchan.incoming_acknowledged = cls.netchan.incoming_sequence; #endif goto readnext; #ifdef MVDPLAY case dem_multiple: r = fread (&i, 4, 1, cls.demofile); if (r != 1) Host_Error ("Unexpected end of demo"); cls.mvd_lastto = LittleLong(i); cls.mvd_lasttype = dem_multiple; goto readit; case dem_single: cls.mvd_lastto = c>>3; cls.mvd_lasttype = dem_single; goto readit; case dem_stats: cls.mvd_lastto = c>>3; cls.mvd_lasttype = dem_stats; goto readit; case dem_all: cls.mvd_lastto = 0; cls.mvd_lasttype = dem_all; goto readit; #endif default: Host_Error ("Corrupted demo 3 -> c='%d'", c); return false; } } /* ==================== CL_Stop_f stop recording a demo ==================== */ void CL_Stop_f (void) { if (!cls.demorecording) { Com_Printf ("Not recording a demo.\n"); return; } // write a disconnect message to the demo file SZ_Clear (&net_message); MSG_WriteLong (&net_message, -1); // -1 sequence means out of band MSG_WriteByte (&net_message, svc_disconnect); MSG_WriteString (&net_message, "EndOfDemo"); CL_WriteDemoMessage (&net_message); // finish up fclose (cls.demofile); cls.demofile = NULL; cls.demorecording = false; Com_Printf ("Completed demo\n"); } /* ==================== CL_WriteRecordDemoMessage ==================== */ void CL_WriteRecordDemoMessage (sizebuf_t *msg, int seq) { int len; int i; float fl; byte c; //Com_Printf ("write: %ld bytes, %4.4f\n", msg->cursize, cls.realtime); if (!cls.demorecording) return; fl = LittleFloat((float)cls.realtime); fwrite (&fl, sizeof(fl), 1, cls.demofile); c = dem_read; fwrite (&c, sizeof(c), 1, cls.demofile); len = LittleLong (msg->cursize + 8); fwrite (&len, 4, 1, cls.demofile); i = LittleLong(seq); fwrite (&i, 4, 1, cls.demofile); fwrite (&i, 4, 1, cls.demofile); fwrite (msg->data, msg->cursize, 1, cls.demofile); fflush (cls.demofile); }
// // IntQrySendResponse() // // Sends information regarding the type of information we received (ie: it will // send data that is wanted by the enquirer program) static DWORD IntQrySendResponse(const WORD &TagId, const BYTE &TagApplication, const BYTE &TagQRId, const WORD &TagPacketType) { // It isn't a query, throw it away if (TagQRId != 1) { //Printf("Query/Response Id is not valid"); return 0; } // Decipher the program that sent the query switch (TagApplication) { case 1: { //Printf(PRINT_HIGH, "Application is Enquirer"); } break; case 2: { //Printf(PRINT_HIGH, "Application is Client"); } break; case 3: { //Printf(PRINT_HIGH, "Application is Server"); } break; case 4: { //Printf(PRINT_HIGH, "Application is Master Server"); } break; default: { //Printf("Application is Unknown"); } break; } DWORD ReTag = 0; WORD ReId = TAG_ID; BYTE ReApplication = 3; BYTE ReQRId = 2; WORD RePacketType = 0; switch (TagPacketType) { // Request version case 1: { RePacketType = 1; } break; // Information request case 2: { RePacketType = 3; } break; } // Begin enquirer version translation DWORD EqVersion = MSG_ReadLong(); DWORD EqProtocolVersion = MSG_ReadLong(); DWORD EqTime = 0; EqTime = MSG_ReadLong(); // Override other packet types for older enquirer version response if (VERSIONMAJOR(EqVersion) < VERSIONMAJOR(GAMEVER) || (VERSIONMAJOR(EqVersion) <= VERSIONMAJOR(GAMEVER) && VERSIONMINOR(EqVersion) < VERSIONMINOR(GAMEVER))) { RePacketType = 2; } // Encode our tag ReTag = (((ReId << 20) & 0xFFF00000) | ((ReApplication << 16) & 0x000F0000) | ((ReQRId << 12) & 0x0000F000) | (RePacketType & 0x00000FFF)); // Clear our message buffer for a response SZ_Clear(&ml_message); MSG_WriteLong(&ml_message, ReTag); MSG_WriteLong(&ml_message, GAMEVER); // Enquirer requested the version info of the server or the server // determined it is an old version if (RePacketType == 1 || RePacketType == 2) { // bond - real protocol MSG_WriteLong(&ml_message, PROTOCOL_VERSION); MSG_WriteLong(&ml_message, EqTime); NET_SendPacket(ml_message, net_from); //Printf(PRINT_HIGH, "Application is old version\n"); return 0; } // If the enquirer protocol is newer, send the latest information the // server supports, otherwise send information built to the their version if (EqProtocolVersion > PROTOCOL_VERSION) { EqProtocolVersion = PROTOCOL_VERSION; MSG_WriteLong(&ml_message, PROTOCOL_VERSION); } else MSG_WriteLong(&ml_message, EqProtocolVersion); IntQryBuildInformation(EqProtocolVersion, EqTime); NET_SendPacket(ml_message, net_from); //Printf(PRINT_HIGH, "Success, data sent\n"); return 0; }
// // SV_SendServerInfo // // Sends server info to a launcher // TODO: Clean up and reinvent. void SV_SendServerInfo() { size_t i; SZ_Clear(&ml_message); MSG_WriteLong(&ml_message, CHALLENGE); MSG_WriteLong(&ml_message, SV_NewToken()); // if master wants a key to be presented, present it we will if(MSG_BytesLeft() == 4) MSG_WriteLong(&ml_message, MSG_ReadLong()); MSG_WriteString(&ml_message, (char *)sv_hostname.cstring()); byte playersingame = 0; for (Players::iterator it = players.begin();it != players.end();++it) { if (it->ingame()) playersingame++; } MSG_WriteByte(&ml_message, playersingame); MSG_WriteByte(&ml_message, sv_maxclients.asInt()); MSG_WriteString(&ml_message, level.mapname); size_t numwads = wadfiles.size(); if(numwads > 0xff)numwads = 0xff; MSG_WriteByte(&ml_message, numwads - 1); for (i = 1; i < numwads; ++i) MSG_WriteString(&ml_message, D_CleanseFileName(wadfiles[i], "wad").c_str()); MSG_WriteBool(&ml_message, (sv_gametype == GM_DM || sv_gametype == GM_TEAMDM)); MSG_WriteByte(&ml_message, sv_skill.asInt()); MSG_WriteBool(&ml_message, (sv_gametype == GM_TEAMDM)); MSG_WriteBool(&ml_message, (sv_gametype == GM_CTF)); for (Players::iterator it = players.begin();it != players.end();++it) { if (it->ingame()) { MSG_WriteString(&ml_message, it->userinfo.netname.c_str()); MSG_WriteShort(&ml_message, it->fragcount); MSG_WriteLong(&ml_message, it->ping); if (sv_gametype == GM_TEAMDM || sv_gametype == GM_CTF) MSG_WriteByte(&ml_message, it->userinfo.team); else MSG_WriteByte(&ml_message, TEAM_NONE); } } for (i = 1; i < numwads; ++i) MSG_WriteString(&ml_message, wadhashes[i].c_str()); MSG_WriteString(&ml_message, sv_website.cstring()); if (sv_gametype == GM_TEAMDM || sv_gametype == GM_CTF) { MSG_WriteLong(&ml_message, sv_scorelimit.asInt()); for(size_t i = 0; i < NUMTEAMS; i++) { if ((sv_gametype == GM_CTF && i < 2) || (sv_gametype != GM_CTF && i < sv_teamsinplay)) { MSG_WriteByte(&ml_message, 1); MSG_WriteLong(&ml_message, TEAMpoints[i]); } else { MSG_WriteByte(&ml_message, 0); } } } MSG_WriteShort(&ml_message, VERSION); //bond=========================== MSG_WriteString(&ml_message, (char *)sv_email.cstring()); int timeleft = (int)(sv_timelimit - level.time/(TICRATE*60)); if (timeleft<0) timeleft=0; MSG_WriteShort(&ml_message,sv_timelimit.asInt()); MSG_WriteShort(&ml_message,timeleft); MSG_WriteShort(&ml_message,sv_fraglimit.asInt()); MSG_WriteBool(&ml_message, (sv_itemsrespawn ? true : false)); MSG_WriteBool(&ml_message, (sv_weaponstay ? true : false)); MSG_WriteBool(&ml_message, (sv_friendlyfire ? true : false)); MSG_WriteBool(&ml_message, (sv_allowexit ? true : false)); MSG_WriteBool(&ml_message, (sv_infiniteammo ? true : false)); MSG_WriteBool(&ml_message, (sv_nomonsters ? true : false)); MSG_WriteBool(&ml_message, (sv_monstersrespawn ? true : false)); MSG_WriteBool(&ml_message, (sv_fastmonsters ? true : false)); MSG_WriteBool(&ml_message, (sv_allowjump ? true : false)); MSG_WriteBool(&ml_message, (sv_freelook ? true : false)); MSG_WriteBool(&ml_message, (sv_waddownload ? true : false)); MSG_WriteBool(&ml_message, (sv_emptyreset ? true : false)); MSG_WriteBool(&ml_message, false); // used to be sv_cleanmaps MSG_WriteBool(&ml_message, (sv_fragexitswitch ? true : false)); for (Players::iterator it = players.begin();it != players.end();++it) { if (it->ingame()) { MSG_WriteShort(&ml_message, it->killcount); MSG_WriteShort(&ml_message, it->deathcount); int timeingame = (time(NULL) - it->JoinTime)/60; if (timeingame<0) timeingame=0; MSG_WriteShort(&ml_message, timeingame); } } //bond=========================== MSG_WriteLong(&ml_message, (DWORD)0x01020304); MSG_WriteShort(&ml_message, sv_maxplayers.asInt()); for (Players::iterator it = players.begin();it != players.end();++it) { if (it->ingame()) { MSG_WriteBool(&ml_message, (it->spectator ? true : false)); } } MSG_WriteLong(&ml_message, (DWORD)0x01020305); MSG_WriteShort(&ml_message, strlen(join_password.cstring()) ? 1 : 0); // GhostlyDeath -- Send Game Version info MSG_WriteLong(&ml_message, GAMEVER); MSG_WriteByte(&ml_message, patchfiles.size()); for (size_t i = 0; i < patchfiles.size(); ++i) MSG_WriteString(&ml_message, D_CleanseFileName(patchfiles[i]).c_str()); NET_SendPacket(ml_message, net_from); }
/* ================= CL_SendBatchedCmd ================= */ static void CL_SendBatchedCmd( void ) { int i, j, seq, bits; int numCmds, numDups; int totalCmds, totalMsec; size_t cursize; usercmd_t *cmd, *oldcmd; client_history_t *history, *oldest; byte *patch; // see if we are ready to send this packet if( !ready_to_send() ) { return; } // archive this packet seq = cls.netchan->outgoing_sequence; history = &cl.history[seq & CMD_MASK]; history->cmdNumber = cl.cmdNumber; history->sent = cls.realtime; // for ping calculation history->rcvd = 0; cl.lastTransmitTime = cls.realtime; cl.lastTransmitCmdNumber = cl.cmdNumber; cl.lastTransmitCmdNumberReal = cl.cmdNumber; // begin a client move command patch = SZ_GetSpace( &msg_write, 1 ); // let the server know what the last frame we // got was, so the next message can be delta compressed if( cl_nodelta->integer || !cl.frame.valid /*|| cls.demowaiting*/ ) { *patch = clc_move_nodelta; // no compression } else { *patch = clc_move_batched; MSG_WriteLong( cl.frame.number ); } Cvar_ClampInteger( cl_packetdup, 0, MAX_PACKET_FRAMES - 1 ); numDups = cl_packetdup->integer; *patch |= numDups << SVCMD_BITS; // send lightlevel MSG_WriteByte( cl.lightlevel ); // send this and the previous cmds in the message, so // if the last packet was dropped, it can be recovered oldcmd = NULL; totalCmds = 0; totalMsec = 0; for( i = seq - numDups; i <= seq; i++ ) { oldest = &cl.history[( i - 1 ) & CMD_MASK]; history = &cl.history[i & CMD_MASK]; numCmds = history->cmdNumber - oldest->cmdNumber; if( numCmds >= MAX_PACKET_USERCMDS ) { Com_WPrintf( "%s: MAX_PACKET_USERCMDS exceeded\n", __func__ ); SZ_Clear( &msg_write ); break; } totalCmds += numCmds; MSG_WriteBits( numCmds, 5 ); for( j = oldest->cmdNumber + 1; j <= history->cmdNumber; j++ ) { cmd = &cl.cmds[j & CMD_MASK]; totalMsec += cmd->msec; bits = MSG_WriteDeltaUsercmd_Enhanced( oldcmd, cmd, cls.protocolVersion ); #ifdef _DEBUG if( cl_showpackets->integer == 3 ) { MSG_ShowDeltaUsercmdBits_Enhanced( bits ); } #endif oldcmd = cmd; } } P_FRAMES++; // // deliver the message // cursize = cls.netchan->Transmit( cls.netchan, msg_write.cursize, msg_write.data, 1 ); #ifdef _DEBUG if( cl_showpackets->integer == 1 ) { Com_Printf( "%"PRIz"(%i) ", cursize, totalCmds ); } else if( cl_showpackets->integer == 2 ) { Com_Printf( "%"PRIz"(%i) ", cursize, totalMsec ); } else if( cl_showpackets->integer == 3 ) { Com_Printf( " | " ); } #endif SZ_Clear( &msg_write ); }
// // SV_SendPacket // bool SV_SendPacket(player_t &pl) { int bps = 0; // bytes per second, not bits per second client_t *cl = &pl.client; if (cl->reliablebuf.overflowed) { SZ_Clear(&cl->netbuf); SZ_Clear(&cl->reliablebuf); SV_DropClient(pl); return false; } else if (cl->netbuf.overflowed) SZ_Clear(&cl->netbuf); sendd.clear(); // save the reliable message // it will be retransmited, if it's missed // the end of the buffer is reached if (cl->relpackets.cursize + cl->reliablebuf.cursize >= cl->relpackets.maxsize()) cl->relpackets.cursize = 0; // copy the beginning and the size of a packet to the buffer cl->packetbegin[cl->packetnum] = cl->relpackets.cursize; cl->packetsize[cl->packetnum] = cl->reliablebuf.cursize; cl->packetseq[cl->packetnum] = cl->sequence; if (cl->reliablebuf.cursize) SZ_Write (&cl->relpackets, cl->reliablebuf.data, cl->reliablebuf.cursize); cl->packetnum++; // packetnum will never be more than 255 // because sizeof(packetnum) == 1. Don't need // to use &0xff. Cool, eh? ;-) // copy sequence MSG_WriteLong(&sendd, cl->sequence++); // copy the reliable message to the packet first if (cl->reliablebuf.cursize) { SZ_Write (&sendd, cl->reliablebuf.data, cl->reliablebuf.cursize); cl->reliable_bps += cl->reliablebuf.cursize; } // add the unreliable part if space is available and rate value // allows it if (gametic % 35) bps = (int)((double)( (cl->unreliable_bps + cl->reliable_bps) * TICRATE)/(double)(gametic%35)); if (bps < cl->rate*1000) if (cl->netbuf.cursize && (sendd.maxsize() - sendd.cursize > cl->netbuf.cursize) ) { SZ_Write (&sendd, cl->netbuf.data, cl->netbuf.cursize); cl->unreliable_bps += cl->netbuf.cursize; } SZ_Clear(&cl->netbuf); SZ_Clear(&cl->reliablebuf); // compress the packet, but not the sequence id if(sv_networkcompression && sendd.size() > sizeof(int)) SV_CompressPacket(sendd, sizeof(int), cl); NET_SendPacket(sendd, cl->address); return true; }
void Netchan_CreateFragments_(qboolean server, netchan_t *chan, sizebuf_t *msg) { fragbuf_t *buf; int chunksize; int send; int remaining; int pos; int bufferid = 1; fragbufwaiting_t *wait, *p; if (msg->cursize == 0) { return; } // Compress if not done already if (*(uint32 *)msg->data != MAKEID('B', 'Z', '2', '\0')) { unsigned char compressed[65536]; char hdr[4] = "BZ2"; unsigned int compressedSize = msg->cursize - sizeof(hdr); // we should fit in same data buffer minus 4 bytes for a header if (!BZ2_bzBuffToBuffCompress((char *)compressed, &compressedSize, (char *)msg->data, msg->cursize, 9, 0, 30)) { Con_DPrintf("Compressing split packet (%d -> %d bytes)\n", msg->cursize, compressedSize); Q_memcpy(msg->data, hdr, sizeof(hdr)); Q_memcpy(msg->data + sizeof(hdr), compressed, compressedSize); msg->cursize = compressedSize + sizeof(hdr); } } chunksize = chan->pfnNetchan_Blocksize(chan->connection_status); wait = (fragbufwaiting_t *)Mem_ZeroMalloc(sizeof(fragbufwaiting_t)); remaining = msg->cursize; pos = 0; while (remaining > 0) { send = min(remaining, chunksize); remaining -= send; buf = Netchan_AllocFragbuf(); if (!buf) { return; } buf->bufferid = bufferid++; // Copy in data SZ_Clear(&buf->frag_message); SZ_Write(&buf->frag_message, &msg->data[pos], send); pos += send; Netchan_AddFragbufToTail(wait, buf); } // Now add waiting list item to the end of buffer queue if (!chan->waitlist[FRAG_NORMAL_STREAM]) { chan->waitlist[FRAG_NORMAL_STREAM] = wait; } else { p = chan->waitlist[FRAG_NORMAL_STREAM]; while (p->next) { p = p->next; } p->next = wait; } }
static void Test2_Poll(void) { struct qsockaddr clientaddr; int control; int len; char name[32]; char value[32]; net_landriverlevel = test2Driver; name[0] = 0; len = dfunc.Read(test2Socket, net_message.data, net_message.maxsize, &clientaddr); if (len < sizeof(int)) goto Reschedule; net_message.cursize = len; MSG_BeginReading(); control = BigLong(*((int *) net_message.data)); MSG_ReadLong(); if (control == -1) goto Error; if ((control & (~NETFLAG_LENGTH_MASK)) != NETFLAG_CTL) goto Error; if ((control & NETFLAG_LENGTH_MASK) != len) goto Error; if (MSG_ReadByte() != CCREP_RULE_INFO) goto Error; Q_strcpy(name, MSG_ReadString()); if (name[0] == 0) goto Done; Q_strcpy(value, MSG_ReadString()); Con_Printf("%-16.16s %-16.16s\n", name, value); SZ_Clear(&net_message); // save space for the header, filled in later MSG_WriteLong(&net_message, 0); MSG_WriteByte(&net_message, CCREQ_RULE_INFO); MSG_WriteString(&net_message, name); *((int *) net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); dfunc.Write(test2Socket, net_message.data, net_message.cursize, &clientaddr); SZ_Clear(&net_message); Reschedule: SchedulePollProcedure(&test2PollProcedure, 0.05); return; Error: Con_Printf("Unexpected repsonse to Rule Info request\n"); Done: dfunc.CloseSocket(test2Socket); test2InProgress = false; return; }
void Netchan_CreateFileFragmentsFromBuffer(qboolean server, netchan_t *chan, const char *filename, unsigned char *uncompressed_pbuf, int uncompressed_size) { int chunksize; int send; fragbufwaiting_t *p; fragbuf_t *buf; unsigned char *pbuf; qboolean bCompressed; qboolean firstfragment; signed int bufferid; int remaining; int pos; unsigned int size; fragbufwaiting_t *wait; if (!uncompressed_size) return; bufferid = 1; firstfragment = TRUE; size = uncompressed_size; pbuf = (unsigned char *)Mem_Malloc(uncompressed_size); if (BZ2_bzBuffToBuffCompress((char*)pbuf, &size, (char*)uncompressed_pbuf, uncompressed_size, 9, 0, 30)) { bCompressed = FALSE; Mem_Free(pbuf); pbuf = uncompressed_pbuf; size = uncompressed_size; } else { bCompressed = TRUE; Con_DPrintf("Compressed %s for transmission (%d -> %d)\n", filename, uncompressed_size, size); } chunksize = chan->pfnNetchan_Blocksize(chan->connection_status); send = chunksize; wait = (fragbufwaiting_t *)Mem_ZeroMalloc(0xCu); remaining = size; pos = 0; while (remaining > 0) { send = min(remaining, chunksize); buf = (fragbuf_t *)Netchan_AllocFragbuf(); if (!buf) { Con_Printf("Couldn't allocate fragbuf_t\n"); Mem_Free(wait); if (server) SV_DropClient(host_client, 0, "Malloc problem"); else rehlds_syserror("%s:Reverse me: client-side code", __func__); #ifdef REHLDS_FIXES if (bCompressed) { Mem_Free(pbuf); } #endif return; } buf->bufferid = bufferid++; SZ_Clear(&buf->frag_message); if (firstfragment) { firstfragment = FALSE; MSG_WriteString(&buf->frag_message, filename); MSG_WriteString(&buf->frag_message, bCompressed ? "bz2" : "uncompressed"); MSG_WriteLong(&buf->frag_message, uncompressed_size); send -= buf->frag_message.cursize; } buf->isbuffer = TRUE; buf->isfile = TRUE; buf->size = send; buf->foffset = pos; MSG_WriteBuf(&buf->frag_message, send, &pbuf[pos]); pos += send; remaining -= send; Netchan_AddFragbufToTail(wait, buf); } if (!chan->waitlist[FRAG_FILE_STREAM]) { chan->waitlist[FRAG_FILE_STREAM] = wait; } else { p = chan->waitlist[FRAG_FILE_STREAM]; while (p->next) p = p->next; p->next = wait; } #ifdef REHLDS_FIXES if (bCompressed) { Mem_Free(pbuf); } #endif }
// // SV_SendServerInfo // // Sends server info to a launcher // TODO: Clean up and reinvent. void SV_SendServerInfo() { size_t i; SZ_Clear(&ml_message); MSG_WriteLong(&ml_message, CHALLENGE); MSG_WriteLong(&ml_message, SV_NewToken()); // if master wants a key to be presented, present it we will if(MSG_BytesLeft() == 4) MSG_WriteLong(&ml_message, MSG_ReadLong()); MSG_WriteString(&ml_message, (char *)hostname.cstring()); byte playersingame = 0; for (i = 0; i < players.size(); ++i) { if (players[i].ingame()) playersingame++; } MSG_WriteByte(&ml_message, playersingame); MSG_WriteByte(&ml_message, maxclients); MSG_WriteString(&ml_message, level.mapname); size_t numwads = wadnames.size(); if(numwads > 0xff)numwads = 0xff; MSG_WriteByte(&ml_message, numwads - 1); for (i = 1; i < numwads; ++i) MSG_WriteString(&ml_message, wadnames[i].c_str()); MSG_WriteByte(&ml_message, (int)deathmatch); MSG_WriteByte(&ml_message, (int)skill); MSG_WriteByte(&ml_message, (int)teamplay); MSG_WriteByte(&ml_message, (int)ctfmode); for (i = 0; i < players.size(); ++i) { if (players[i].ingame()) { MSG_WriteString(&ml_message, players[i].userinfo.netname); MSG_WriteShort(&ml_message, players[i].fragcount); MSG_WriteLong(&ml_message, players[i].ping); if (teamplay || ctfmode) MSG_WriteByte(&ml_message, players[i].userinfo.team); else MSG_WriteByte(&ml_message, TEAM_NONE); } } for (i = 1; i < numwads; ++i) MSG_WriteString(&ml_message, wadhashes[i].c_str()); MSG_WriteString(&ml_message, website.cstring()); if (ctfmode || teamplay) { MSG_WriteLong(&ml_message, scorelimit); for(size_t i = 0; i < NUMTEAMS; i++) { MSG_WriteByte(&ml_message, TEAMenabled[i]); if (TEAMenabled[i]) MSG_WriteLong(&ml_message, TEAMpoints[i]); } } MSG_WriteShort(&ml_message, VERSION); //bond=========================== MSG_WriteString(&ml_message, (char *)email.cstring()); int timeleft = (int)(timelimit - level.time/(TICRATE*60)); if (timeleft<0) timeleft=0; MSG_WriteShort(&ml_message,(int)timelimit); MSG_WriteShort(&ml_message,timeleft); MSG_WriteShort(&ml_message,(int)fraglimit); MSG_WriteByte(&ml_message,(BOOL)itemsrespawn); MSG_WriteByte(&ml_message,(BOOL)weaponstay); MSG_WriteByte(&ml_message,(BOOL)friendlyfire); MSG_WriteByte(&ml_message,(BOOL)allowexit); MSG_WriteByte(&ml_message,(BOOL)infiniteammo); MSG_WriteByte(&ml_message,(BOOL)nomonsters); MSG_WriteByte(&ml_message,(BOOL)monstersrespawn); MSG_WriteByte(&ml_message,(BOOL)fastmonsters); MSG_WriteByte(&ml_message,(BOOL)allowjump); MSG_WriteByte(&ml_message,(BOOL)allowfreelook); MSG_WriteByte(&ml_message,(BOOL)waddownload); MSG_WriteByte(&ml_message,(BOOL)emptyreset); MSG_WriteByte(&ml_message,(BOOL)cleanmaps); MSG_WriteByte(&ml_message,(BOOL)fragexitswitch); for (i = 0; i < players.size(); ++i) { if (players[i].ingame()) { MSG_WriteShort(&ml_message, players[i].killcount); MSG_WriteShort(&ml_message, players[i].deathcount); int timeingame = (time(NULL) - players[i].JoinTime)/60; if (timeingame<0) timeingame=0; MSG_WriteShort(&ml_message, timeingame); } } //bond=========================== MSG_WriteLong(&ml_message, (DWORD)0x01020304); MSG_WriteShort(&ml_message, (WORD)maxplayers); for (i = 0; i < players.size(); ++i) { if (players[i].ingame()) { MSG_WriteByte(&ml_message, (players[i].spectator ? true : false)); } } MSG_WriteLong(&ml_message, (DWORD)0x01020305); MSG_WriteShort(&ml_message, strlen(password.cstring()) ? 1 : 0); NET_SendPacket(ml_message, net_from); }
int Netchan_CreateFileFragments_(qboolean server, netchan_t *chan, const char *filename) #endif // REHLDS_FIXES { int chunksize; int compressedFileTime; FileHandle_t hfile; signed int filesize; int remaining; fragbufwaiting_t *p; int send; fragbuf_t *buf; char compressedfilename[MAX_PATH]; qboolean firstfragment; int bufferid; qboolean bCompressed; int pos; fragbufwaiting_t *wait; int uncompressed_size; bufferid = 1; firstfragment = TRUE; bCompressed = FALSE; chunksize = chan->pfnNetchan_Blocksize(chan->connection_status); Q_snprintf(compressedfilename, sizeof compressedfilename, "%s.ztmp", filename); compressedFileTime = FS_GetFileTime(compressedfilename); if (compressedFileTime >= FS_GetFileTime(filename) && (hfile = FS_Open(compressedfilename, "rb"))) { filesize = FS_Size(hfile); FS_Close(hfile); bCompressed = TRUE; hfile = FS_Open(filename, "rb"); if (!hfile) { Con_Printf("Warning: Unable to open %s for transfer\n", filename); return 0; } uncompressed_size = FS_Size(hfile); if (uncompressed_size > sv_filetransfermaxsize.value) { FS_Close(hfile); Con_Printf("Warning: File %s is too big to transfer from host %s\n", filename, NET_AdrToString(chan->remote_address)); return 0; } } else { hfile = FS_Open(filename, "rb"); if (!hfile) { Con_Printf("Warning: Unable to open %s for transfer\n", filename); return 0; } filesize = FS_Size(hfile); if (filesize > sv_filetransfermaxsize.value) { FS_Close(hfile); Con_Printf("Warning: File %s is too big to transfer from host %s\n", filename, NET_AdrToString(chan->remote_address)); return 0; } uncompressed_size = filesize; if (sv_filetransfercompression.value != 0.0) { unsigned char* uncompressed = (unsigned char*)Mem_Malloc(filesize); unsigned char* compressed = (unsigned char*)Mem_Malloc(filesize); unsigned int compressedSize = filesize; FS_Read(uncompressed, filesize, 1, hfile); if (BZ_OK == BZ2_bzBuffToBuffCompress((char*)compressed, &compressedSize, (char*)uncompressed, filesize, 9, 0, 30)) { FileHandle_t destFile = FS_Open(compressedfilename, "wb"); if (destFile) { Con_DPrintf("Creating compressed version of file %s (%d -> %d)\n", filename, filesize, compressedSize); FS_Write(compressed, compressedSize, 1, destFile); FS_Close(destFile); filesize = compressedSize; bCompressed = TRUE; } } Mem_Free(uncompressed); Mem_Free(compressed); } } FS_Close(hfile); wait = (fragbufwaiting_t *)Mem_ZeroMalloc(0xCu); remaining = filesize; pos = 0; while (remaining) { send = min(chunksize, remaining); buf = Netchan_AllocFragbuf(); if (!buf) { Con_Printf("Couldn't allocate fragbuf_t\n"); Mem_Free(wait); if (server) { #ifdef REHLDS_FIXES SV_DropClient(&g_psvs.clients[chan->player_slot - 1], 0, "Malloc problem"); #else // REHLDS_FIXES SV_DropClient(host_client, 0, "Malloc problem"); #endif // REHLDS_FIXES return 0; } else { rehlds_syserror("%s: Reverse clientside code", __func__); //return 0; } } buf->bufferid = bufferid++; SZ_Clear(&buf->frag_message); if (firstfragment) { firstfragment = FALSE; MSG_WriteString(&buf->frag_message, filename); MSG_WriteString(&buf->frag_message, bCompressed ? "bz2" : "uncompressed"); MSG_WriteLong(&buf->frag_message, uncompressed_size); send -= buf->frag_message.cursize; } buf->isfile = TRUE; buf->iscompressed = bCompressed; buf->size = send; buf->foffset = pos; Q_strncpy(buf->filename, filename, MAX_PATH - 1); buf->filename[MAX_PATH - 1] = 0; pos += send; remaining -= send; Netchan_AddFragbufToTail(wait, buf); } if (!chan->waitlist[FRAG_FILE_STREAM]) { chan->waitlist[FRAG_FILE_STREAM] = wait; } else { p = chan->waitlist[FRAG_FILE_STREAM]; while (p->next) p = p->next; p->next = wait; } return 1; }
qbool SV_MVDWritePackets (int num) { demo_frame_t *frame, *nextframe; demo_client_t *cl, *nextcl = NULL; int i, j, flags; qbool valid; double time1, playertime, nexttime; vec3_t origin, angles; sizebuf_t msg; byte msg_buf[MAX_MSGLEN]; demoinfo_t *demoinfo; if (!sv.mvdrecording) return false; SZ_Init(&msg, msg_buf, sizeof(msg_buf)); if (num > demo.parsecount - demo.lastwritten + 1) num = demo.parsecount - demo.lastwritten + 1; // 'num' frames to write for ( ; num; num--, demo.lastwritten++) { SZ_Clear(&msg); frame = &demo.frames[demo.lastwritten&DEMO_FRAMES_MASK]; time1 = frame->time; nextframe = frame; demo.dbuf = &frame->buf; // find two frames // one before the exact time (time - msec) and one after, // then we can interpolte exact position for current frame for (i = 0, cl = frame->clients, demoinfo = demo.info; i < MAX_CLIENTS; i++, cl++, demoinfo++) { if (cl->parsecount != demo.lastwritten) continue; // not valid nexttime = playertime = time1 - cl->sec; valid = false; for (j = demo.lastwritten+1; nexttime < time1 && j < demo.parsecount; j++) { nextframe = &demo.frames[j&DEMO_FRAMES_MASK]; nextcl = &nextframe->clients[i]; if (nextcl->parsecount != j) break; // disconnected? if (nextcl->fixangle) break; // respawned, or walked into teleport, do not interpolate! if (!(nextcl->flags & DF_DEAD) && (cl->flags & DF_DEAD)) break; // respawned, do not interpolate nexttime = nextframe->time - nextcl->sec; if (nexttime >= time1) { // good, found what we were looking for valid = true; break; } } if (valid) { float f = 0; float z = nexttime - playertime; if ( z ) f = (time1 - nexttime) / z; for (j = 0; j < 3; j++) { angles[j] = adjustangle(cl->info.angles[j], nextcl->info.angles[j], 1.0 + f); origin[j] = nextcl->info.origin[j] + f * (nextcl->info.origin[j] - cl->info.origin[j]); } } else { VectorCopy(cl->info.origin, origin); VectorCopy(cl->info.angles, angles); } // now write it to buf flags = cl->flags; for (j=0; j < 3; j++) if (origin[j] != demoinfo->origin[i]) flags |= DF_ORIGIN << j; for (j=0; j < 3; j++) if (angles[j] != demoinfo->angles[j]) flags |= DF_ANGLES << j; if (cl->info.model != demoinfo->model) flags |= DF_MODEL; if (cl->info.effects != demoinfo->effects) flags |= DF_EFFECTS; if (cl->info.skinnum != demoinfo->skinnum) flags |= DF_SKINNUM; if (cl->info.weaponframe != demoinfo->weaponframe) flags |= DF_WEAPONFRAME; MSG_WriteByte (&msg, svc_playerinfo); MSG_WriteByte (&msg, i); MSG_WriteShort (&msg, flags); MSG_WriteByte (&msg, cl->frame); for (j=0 ; j<3 ; j++) if (flags & (DF_ORIGIN << j)) MSG_WriteCoord (&msg, origin[j]); for (j=0 ; j<3 ; j++) if (flags & (DF_ANGLES << j)) MSG_WriteAngle16 (&msg, angles[j]); if (flags & DF_MODEL) MSG_WriteByte (&msg, cl->info.model); if (flags & DF_SKINNUM) MSG_WriteByte (&msg, cl->info.skinnum); if (flags & DF_EFFECTS) MSG_WriteByte (&msg, cl->info.effects); if (flags & DF_WEAPONFRAME) MSG_WriteByte (&msg, cl->info.weaponframe); VectorCopy(cl->info.origin, demoinfo->origin); VectorCopy(cl->info.angles, demoinfo->angles); demoinfo->skinnum = cl->info.skinnum; demoinfo->effects = cl->info.effects; demoinfo->weaponframe = cl->info.weaponframe; demoinfo->model = cl->info.model; } SV_MVDWriteToDisk(demo.lasttype,demo.lastto, (float)time1); // this goes first to reduce demo size a bit SV_MVDWriteToDisk(0,0, (float)time1); // now goes the rest if (msg.cursize) SV_WriteMVDMessage(&msg, dem_all, 0, (float)time1); if (!sv.mvdrecording) { Com_DPrintf("SV_MVDWritePackets: error: in sv.mvdrecording\n"); return false; // ERROR } } if (!sv.mvdrecording) return false; // ERROR if (demo.lastwritten > demo.parsecount) demo.lastwritten = demo.parsecount; demo.dbuf = &demo.frames[demo.parsecount&DEMO_FRAMES_MASK].buf; demo.dbuf->maxsize = MAXSIZE + demo.dbuf->bufsize; return true; }
qboolean Netchan_CopyNormalFragments(netchan_t *chan) { fragbuf_t *p, *n; if (!chan->incomingready[FRAG_NORMAL_STREAM]) return FALSE; if (!chan->incomingbufs[FRAG_NORMAL_STREAM]) { Con_Printf("%s: Called with no fragments readied\n", __func__); chan->incomingready[FRAG_NORMAL_STREAM] = FALSE; return FALSE; } p = chan->incomingbufs[FRAG_NORMAL_STREAM]; SZ_Clear(&net_message); MSG_BeginReading(); #ifdef REHLDS_FIXES bool overflowed = false; #endif // REHLDS_FIXES while (p) { n = p->next; #ifdef REHLDS_FIXES if (net_message.cursize + p->frag_message.cursize <= net_message.maxsize) SZ_Write(&net_message, p->frag_message.data, p->frag_message.cursize); else overflowed = true; #else // REHLDS_FIXES SZ_Write(&net_message, p->frag_message.data, p->frag_message.cursize); #endif // REHLDS_FIXES Mem_Free(p); p = n; } #ifdef REHLDS_FIXES if (overflowed) { if (chan->player_slot == 0) { Con_Printf("%s: Incoming overflowed\n", __func__); } else { Con_Printf("%s: Incoming overflowed from %s\n", __func__, g_psvs.clients[chan->player_slot - 1].name); } SZ_Clear(&net_message); chan->incomingbufs[FRAG_NORMAL_STREAM] = nullptr; chan->incomingready[FRAG_NORMAL_STREAM] = FALSE; return FALSE; } #endif // REHLDS_FIXES if (*(uint32 *)net_message.data == MAKEID('B', 'Z', '2', '\0')) { char uncompressed[65536]; unsigned int uncompressedSize = 65536; BZ2_bzBuffToBuffDecompress(uncompressed, &uncompressedSize, (char*)net_message.data + 4, net_message.cursize - 4, 1, 0); Q_memcpy(net_message.data, uncompressed, uncompressedSize); net_message.cursize = uncompressedSize; } chan->incomingbufs[FRAG_NORMAL_STREAM] = nullptr; chan->incomingready[FRAG_NORMAL_STREAM] = FALSE; return TRUE; }
/* ================== SV_ClearDatagram ================== */ void SV_ClearDatagram (void) { SZ_Clear (&sv.datagram); }
qboolean Netchan_CopyFileFragments(netchan_t *chan) { fragbuf_t *p; int nsize; unsigned char *buffer; int pos; signed int cursize; char filename[MAX_PATH]; char compressor[32]; fragbuf_s *n; qboolean bCompressed; unsigned int uncompressedSize; if (!chan->incomingready[FRAG_FILE_STREAM]) return FALSE; p = chan->incomingbufs[FRAG_FILE_STREAM]; if (!p) { Con_Printf("%s: Called with no fragments readied\n", __func__); chan->incomingready[FRAG_FILE_STREAM] = FALSE; return FALSE; } bCompressed = FALSE; SZ_Clear(&net_message); MSG_BeginReading(); SZ_Write(&net_message, p->frag_message.data, p->frag_message.cursize); Q_strncpy(filename, MSG_ReadString(), sizeof(filename) - 1); filename[sizeof(filename) - 1] = 0; Q_strncpy(compressor, MSG_ReadString(), sizeof(compressor) - 1); compressor[sizeof(compressor) - 1] = 0; if (!Q_stricmp(compressor, "bz2")) bCompressed = TRUE; uncompressedSize = (unsigned int)MSG_ReadLong(); #ifdef REHLDS_FIXES // TODO: this condition is invalid for server->client // TODO: add console message for client // TODO: add client name to message if (uncompressedSize > 1024 * 64) { Con_Printf("Received too large file (size=%u)\nFlushing input queue\n", uncompressedSize); Netchan_FlushIncoming(chan, 1); return FALSE; } #endif if (Q_strlen(filename) <= 0) { Con_Printf("File fragment received with no filename\nFlushing input queue\n"); Netchan_FlushIncoming(chan, 1); return FALSE; } if (Q_strstr(filename, "..")) { Con_Printf("File fragment received with relative path, ignoring\n"); Netchan_FlushIncoming(chan, 1); return FALSE; } if (filename[0] != '!' && !IsSafeFileToDownload(filename)) { Con_Printf("File fragment received with bad path, ignoring\n"); Netchan_FlushIncoming(chan, 1); return FALSE; } // This prohibits to write files to FS on server if (g_pcls.state == ca_dedicated && filename[0] != '!') { Con_Printf("File fragment received with bad path, ignoring (2)\n"); Netchan_FlushIncoming(chan, 1); return FALSE; } Q_strncpy(chan->incomingfilename, filename, MAX_PATH - 1); chan->incomingfilename[MAX_PATH - 1] = 0; if (filename[0] != '!' && FS_FileExists(filename)) { Con_Printf("Can't download %s, already exists\n", filename); Netchan_FlushIncoming(chan, 1); return TRUE; } nsize = 0; while (p) { nsize += p->frag_message.cursize; if (p == chan->incomingbufs[FRAG_FILE_STREAM]) nsize -= msg_readcount; p = p->next; } buffer = (unsigned char*)Mem_ZeroMalloc(nsize + 1); if (!buffer) { Con_Printf("Buffer allocation failed on %i bytes\n", nsize + 1); Netchan_FlushIncoming(chan, 1); return FALSE; } p = chan->incomingbufs[FRAG_FILE_STREAM]; pos = 0; while (p) { n = p->next; cursize = p->frag_message.cursize; // First message has the file name, don't write that into the data stream, just write the rest of the actual data if (p == chan->incomingbufs[FRAG_FILE_STREAM]) { // Copy it in cursize -= msg_readcount; Q_memcpy(&buffer[pos], &p->frag_message.data[msg_readcount], cursize); p->frag_message.cursize = cursize; } else { Q_memcpy(&buffer[pos], p->frag_message.data, cursize); } pos += p->frag_message.cursize; Mem_Free(p); p = n; } if (bCompressed) { unsigned char* uncompressedBuffer = (unsigned char*)Mem_Malloc(uncompressedSize); Con_DPrintf("Decompressing file %s (%d -> %d)\n", filename, nsize, uncompressedSize); BZ2_bzBuffToBuffDecompress((char*)uncompressedBuffer, &uncompressedSize, (char*)buffer, nsize, 1, 0); Mem_Free(buffer); pos = uncompressedSize; buffer = uncompressedBuffer; } if (filename[0] == '!') { if (chan->tempbuffer) { Con_DPrintf("Netchan_CopyFragments: Freeing holdover tempbuffer\n"); Mem_Free(chan->tempbuffer); } chan->tempbuffer = buffer; chan->tempbuffersize = pos; } else { char filedir[MAX_PATH]; char *pszFileName; FileHandle_t handle; #ifdef REHLDS_CHECKS Q_strncpy(filedir, filename, sizeof(filedir) - 1); filedir[sizeof(filedir) - 1] = 0; #else Q_strncpy(filedir, filename, sizeof(filedir)); #endif // REHLDS_CHECKS COM_FixSlashes(filedir); pszFileName = Q_strrchr(filedir, '\\'); if (pszFileName) { *pszFileName = 0; #ifdef REHLDS_FIXES FS_CreateDirHierarchy(filedir, "GAMEDOWNLOAD"); #endif } #ifndef REHLDS_FIXES FS_CreateDirHierarchy(filedir, "GAMEDOWNLOAD"); #endif handle = FS_OpenPathID(filename, "wb", "GAMEDOWNLOAD"); if (!handle) { Con_Printf("File open failed %s\n", filename); Netchan_FlushIncoming(chan, 1); #ifdef REHLDS_FIXES Mem_Free(buffer); #endif return FALSE; } Sys_Printf("COM_WriteFile: %s\n", filename); FS_Write(buffer, pos, 1, handle); FS_Close(handle); Mem_Free(buffer); } SZ_Clear(&net_message); chan->incomingbufs[FRAG_FILE_STREAM] = nullptr; chan->incomingready[FRAG_FILE_STREAM] = FALSE; msg_readcount = 0; return TRUE; }
static qsocket_t *_Datagram_Connect (char *host) { struct qsockaddr sendaddr; struct qsockaddr readaddr; qsocket_t *sock; int newsock; int ret; int reps; double start_time; int control; char *reason; // see if we can resolve the host name if (dfunc.GetAddrFromName(host, &sendaddr) == -1) return NULL; newsock = dfunc.OpenSocket (0); if (newsock == -1) return NULL; sock = NET_NewQSocket (); if (sock == NULL) goto ErrorReturn2; sock->socket = newsock; sock->landriver = net_landriverlevel; // connect to the host if (dfunc.Connect (newsock, &sendaddr) == -1) goto ErrorReturn; // send the connection request Con_Printf("trying...\n"); SCR_UpdateScreen (); start_time = net_time; for (reps = 0; reps < 3; reps++) { SZ_Clear(&net_message); // save space for the header, filled in later MSG_WriteLong(&net_message, 0); MSG_WriteByte(&net_message, CCREQ_CONNECT); MSG_WriteString(&net_message, "QUAKE"); MSG_WriteByte(&net_message, NET_PROTOCOL_VERSION); *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); dfunc.Write (newsock, net_message.data, net_message.cursize, &sendaddr); SZ_Clear(&net_message); do { ret = dfunc.Read (newsock, net_message.data, net_message.maxsize, &readaddr); // if we got something, validate it if (ret > 0) { // is it from the right place? if (sfunc.AddrCompare(&readaddr, &sendaddr) != 0) { #ifdef DEBUG Con_Printf("wrong reply address\n"); Con_Printf("Expected: %s\n", StrAddr (&sendaddr)); Con_Printf("Received: %s\n", StrAddr (&readaddr)); SCR_UpdateScreen (); #endif ret = 0; continue; } if (ret < sizeof(int)) { ret = 0; continue; } net_message.cursize = ret; MSG_BeginReading (); control = BigLong(*((int *)net_message.data)); MSG_ReadLong(); if (control == -1) { ret = 0; continue; } if ((control & (~NETFLAG_LENGTH_MASK)) != NETFLAG_CTL) { ret = 0; continue; } if ((control & NETFLAG_LENGTH_MASK) != ret) { ret = 0; continue; } } } while (ret == 0 && (SetNetTime() - start_time) < 2.5); if (ret) break; Con_Printf("still trying...\n"); SCR_UpdateScreen (); start_time = SetNetTime(); } if (ret == 0) { reason = "No Response"; Con_Printf("%s\n", reason); Q_strcpy(m_return_reason, reason); goto ErrorReturn; } if (ret == -1) { reason = "Network Error"; Con_Printf("%s\n", reason); Q_strcpy(m_return_reason, reason); goto ErrorReturn; } ret = MSG_ReadByte(); if (ret == CCREP_REJECT) { reason = MSG_ReadString(); Con_Printf(reason); Q_strncpy(m_return_reason, reason, 31); goto ErrorReturn; } if (ret == CCREP_ACCEPT) { Q_memcpy(&sock->addr, &sendaddr, sizeof(struct qsockaddr)); dfunc.SetSocketPort (&sock->addr, MSG_ReadLong()); } else { reason = "Bad Response"; Con_Printf("%s\n", reason); Q_strcpy(m_return_reason, reason); goto ErrorReturn; } dfunc.GetNameFromAddr (&sendaddr, sock->address); Con_Printf ("Connection accepted\n"); sock->lastMessageTime = SetNetTime(); // switch the connection to the specified address if (dfunc.Connect (newsock, &sock->addr) == -1) { reason = "Connect to Game failed"; Con_Printf("%s\n", reason); Q_strcpy(m_return_reason, reason); goto ErrorReturn; } m_return_onerror = false; return sock; ErrorReturn: NET_FreeQSocket(sock); ErrorReturn2: dfunc.CloseSocket(newsock); if (m_return_onerror) { key_dest = key_menu; m_state = m_return_state; m_return_onerror = false; } return NULL; }
void Netchan_Transmit(netchan_t *chan, int length, byte *data) { #ifdef REHLDS_FIXES byte send_buf[MAX_UDP_PACKET]; #else byte send_buf[NET_MAX_MESSAGE]; #endif qboolean send_reliable; qboolean send_reliable_fragment; qboolean send_resending = false; unsigned w1, w2; int i, j; float fRate; sizebuf_t sb_send; sb_send.data = send_buf; sb_send.buffername = "Netchan_Transmit"; sb_send.maxsize = sizeof(send_buf); sb_send.flags = 0; sb_send.cursize = 0; // check for message overflow if (chan->message.flags & 2) { Con_Printf("%s:Outgoing message overflow\n", NET_AdrToString(chan->remote_address)); return; } // if the remote side dropped the last reliable message, resend it send_reliable = false; if (chan->incoming_acknowledged > chan->last_reliable_sequence && chan->incoming_reliable_acknowledged != chan->reliable_sequence) { send_reliable = true; send_resending = true; } // // A packet can have "reliable payload + frag payload + unreliable payload // frag payload can be a file chunk, if so, it needs to be parsed on the receiving end and reliable payload + unreliable payload need // to be passed on to the message queue. The processing routine needs to be able to handle the case where a message comes in and a file // transfer completes // // // if the reliable transmit buffer is empty, copy the current message out if (!chan->reliable_length) { qboolean send_frag = false; fragbuf_t *pbuf; // Will be true if we are active and should let chan->message get some bandwidth int send_from_frag[MAX_STREAMS] = { 0, 0 }; int send_from_regular = 0; #ifdef REHLDS_FIXES if (chan->message.cursize > MAX_MSGLEN) { Netchan_CreateFragments_(chan == &g_pcls.netchan ? 1 : 0, chan, &chan->message); SZ_Clear(&chan->message); } #endif // If we have data in the waiting list(s) and we have cleared the current queue(s), then // push the waitlist(s) into the current queue(s) Netchan_FragSend(chan); // Sending regular payload send_from_regular = (chan->message.cursize) ? 1 : 0; // Check to see if we are sending a frag payload // for (i = 0; i < MAX_STREAMS; i++) { if (chan->fragbufs[i]) { send_from_frag[i] = 1; } } // Stall reliable payloads if sending from frag buffer if (send_from_regular && (send_from_frag[FRAG_NORMAL_STREAM])) { send_from_regular = false; // If the reliable buffer has gotten too big, queue it at the end of everything and clear out buffer // if (chan->message.cursize > MAX_RELIABLE_PAYLOAD) { Netchan_CreateFragments_(chan == &g_pcls.netchan ? 1 : 0, chan, &chan->message); SZ_Clear(&chan->message); } } // Startpos will be zero if there is no regular payload for (i = 0; i < MAX_STREAMS; i++) { chan->frag_startpos[i] = 0; // Assume no fragment is being sent chan->reliable_fragment[i] = 0; chan->reliable_fragid[i] = 0; chan->frag_length[i] = 0; if (send_from_frag[i]) { send_frag = true; } } if (send_from_regular || send_frag) { chan->reliable_sequence ^= 1; send_reliable = true; } if (send_from_regular) { #ifdef REHLDS_FIXES Q_memcpy(chan->reliable_buf, chan->message.data, chan->message.cursize); #else Q_memcpy(chan->reliable_buf, chan->message_buf, chan->message.cursize); #endif chan->reliable_length = chan->message.cursize; SZ_Clear(&chan->message); // If we send fragments, this is where they'll start for (i = 0; i < MAX_STREAMS; i++) { chan->frag_startpos[i] = chan->reliable_length; } } for (i = 0; i < MAX_STREAMS; i++) { int fragment_size; // Is there something in the fragbuf? pbuf = chan->fragbufs[i]; fragment_size = 0; // Compiler warning. if (pbuf) { fragment_size = pbuf->frag_message.cursize; // Files set size a bit differently. if (pbuf->isfile && !pbuf->isbuffer) { fragment_size = pbuf->size; } } // Make sure we have enought space left if (send_from_frag[i] && pbuf && ((chan->reliable_length + fragment_size) < MAX_RELIABLE_PAYLOAD)) { chan->reliable_fragid[i] = MAKE_FRAGID(pbuf->bufferid, chan->fragbufcount[i]); // Which buffer are we sending? // If it's not in-memory, then we'll need to copy it in frame the file handle. if (pbuf->isfile && !pbuf->isbuffer) { char compressedfilename[MAX_PATH]; FileHandle_t hfile; if (pbuf->iscompressed) { Q_snprintf(compressedfilename, sizeof(compressedfilename), "%s.ztmp", pbuf->filename); hfile = FS_Open(compressedfilename, "rb"); } else { hfile = FS_Open(pbuf->filename, "rb"); } FS_Seek(hfile, pbuf->foffset, FILESYSTEM_SEEK_HEAD); FS_Read(&pbuf->frag_message.data[pbuf->frag_message.cursize], pbuf->size, 1, hfile); pbuf->frag_message.cursize += pbuf->size; FS_Close(hfile); } Q_memcpy(chan->reliable_buf + chan->reliable_length, pbuf->frag_message.data, pbuf->frag_message.cursize); chan->reliable_length += pbuf->frag_message.cursize; chan->frag_length[i] = pbuf->frag_message.cursize; // Unlink pbuf Netchan_UnlinkFragment(pbuf, &chan->fragbufs[i]); chan->reliable_fragment[i] = 1; // Offset the rest of the starting positions for (j = i + 1; j < MAX_STREAMS; j++) { chan->frag_startpos[j] += chan->frag_length[i]; } } } } // Prepare the packet header w1 = chan->outgoing_sequence | (send_reliable << 31); w2 = chan->incoming_sequence | (chan->incoming_reliable_sequence << 31); send_reliable_fragment = false; for (i = 0; i < MAX_STREAMS; i++) { if (chan->reliable_fragment[i]) { send_reliable_fragment = true; break; } } if (send_reliable && send_reliable_fragment) { w1 |= (1 << 30); } chan->outgoing_sequence++; MSG_WriteLong(&sb_send, w1); MSG_WriteLong(&sb_send, w2); if (send_reliable && send_reliable_fragment) { for (i = 0; i < MAX_STREAMS; i++) { if (chan->reliable_fragment[i]) { MSG_WriteByte(&sb_send, 1); MSG_WriteLong(&sb_send, chan->reliable_fragid[i]); MSG_WriteShort(&sb_send, chan->frag_startpos[i]); MSG_WriteShort(&sb_send, chan->frag_length[i]); } else { MSG_WriteByte(&sb_send, 0); } } } // Copy the reliable message to the packet first if (send_reliable) { SZ_Write(&sb_send, chan->reliable_buf, chan->reliable_length); chan->last_reliable_sequence = chan->outgoing_sequence - 1; } // Is there room for the unreliable payload? int max_send_size = MAX_ROUTEABLE_PACKET; if (!send_resending) max_send_size = sb_send.maxsize; if ((max_send_size - sb_send.cursize) >= length) { SZ_Write(&sb_send, data, length); } else { Con_DPrintf("Netchan_Transmit: Unreliable would overfow, ignoring\n"); } // Deal with packets that are too small for some networks if (sb_send.cursize < 16) // Packet too small for some networks { // Go ahead and pad a full 16 extra bytes -- this only happens during authentication / signon for (int i = sb_send.cursize; i < 16; i++) { // Note that the server can parse svc_nop, too. MSG_WriteByte(&sb_send, svc_nop); } } int statId = chan->flow[FLOW_OUTGOING].current & 0x1F; chan->flow[FLOW_OUTGOING].stats[statId].size = sb_send.cursize + UDP_HEADER_SIZE; chan->flow[FLOW_OUTGOING].stats[statId].time = realtime; chan->flow[FLOW_OUTGOING].current++; Netchan_UpdateFlow(chan); if (!g_pcls.demoplayback) { COM_Munge2(sb_send.data + 8, sb_send.cursize - 8, (unsigned char)(chan->outgoing_sequence - 1)); if (g_modfuncs.m_pfnProcessOutgoingNet) g_modfuncs.m_pfnProcessOutgoingNet(chan, &sb_send); NET_SendPacket(chan->sock, sb_send.cursize, sb_send.data, chan->remote_address); } if (g_psv.active && sv_lan.value != 0.0f && sv_lan_rate.value > MIN_RATE) fRate = 1.0 / sv_lan_rate.value; else fRate = 1.0 / chan->rate; if (chan->cleartime < realtime) { chan->cleartime = realtime; } chan->cleartime += (sb_send.cursize + UDP_HEADER_SIZE) * fRate; if (net_showpackets.value != 0.0f && net_showpackets.value != 2.0f) { char c = (chan == &g_pcls.netchan) ? 'c' : 's'; Con_Printf(" %c --> sz=%i seq=%i ack=%i rel=%i tm=%f\n" , c , sb_send.cursize , chan->outgoing_sequence - 1 , chan->incoming_sequence , send_reliable ? 1 : 0 , (float)(chan == &g_pcls.netchan ? g_pcl.time : g_psv.time)); } }
static void Test_f (void) { char *host; int n; int max = MAX_SCOREBOARD; struct qsockaddr sendaddr; if (testInProgress) return; host = Cmd_Argv (1); if (host && hostCacheCount) { for (n = 0; n < hostCacheCount; n++) if (Q_strcasecmp (host, hostcache[n].name) == 0) { if (hostcache[n].driver != myDriverLevel) continue; net_landriverlevel = hostcache[n].ldriver; max = hostcache[n].maxusers; Q_memcpy(&sendaddr, &hostcache[n].addr, sizeof(struct qsockaddr)); break; } if (n < hostCacheCount) goto JustDoIt; } for (net_landriverlevel = 0; net_landriverlevel < net_numlandrivers; net_landriverlevel++) { if (!net_landrivers[net_landriverlevel].initialized) continue; // see if we can resolve the host name if (dfunc.GetAddrFromName(host, &sendaddr) != -1) break; } if (net_landriverlevel == net_numlandrivers) return; JustDoIt: testSocket = dfunc.OpenSocket(0); if (testSocket == -1) return; testInProgress = true; testPollCount = 20; testDriver = net_landriverlevel; for (n = 0; n < max; n++) { SZ_Clear(&net_message); // save space for the header, filled in later MSG_WriteLong(&net_message, 0); MSG_WriteByte(&net_message, CCREQ_PLAYER_INFO); MSG_WriteByte(&net_message, n); *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); dfunc.Write (testSocket, net_message.data, net_message.cursize, &sendaddr); } SZ_Clear(&net_message); SchedulePollProcedure(&testPollProcedure, 0.1); }
qboolean Netchan_Process(netchan_t *chan) { int i; unsigned int sequence, sequence_ack; unsigned int reliable_ack, reliable_message; unsigned int fragid[MAX_STREAMS] = { 0, 0 }; qboolean frag_message[MAX_STREAMS] = { false, false }; int frag_offset[MAX_STREAMS] = { 0, 0 }; int frag_length[MAX_STREAMS] = { 0, 0 }; qboolean message_contains_fragments; if (!g_pcls.demoplayback && !g_pcls.passive) { if (!NET_CompareAdr(net_from, chan->remote_address)) return FALSE; } chan->last_received = realtime; // get sequence numbers MSG_BeginReading(); sequence = MSG_ReadLong(); sequence_ack = MSG_ReadLong(); if (sequence_ack & 0x40000000) { if (!g_modfuncs.m_pfnProcessIncomingNet) return FALSE; } if (g_modfuncs.m_pfnProcessIncomingNet) { if (!g_modfuncs.m_pfnProcessIncomingNet(chan, &net_message)) return FALSE; } reliable_message = sequence >> 31; reliable_ack = sequence_ack >> 31; message_contains_fragments = sequence & (1 << 30) ? true : false; COM_UnMunge2(&net_message.data[8], net_message.cursize - 8, sequence & 0xFF); if (message_contains_fragments) { for (i = 0; i < MAX_STREAMS; i++) { if (MSG_ReadByte()) { frag_message[i] = true; fragid[i] = MSG_ReadLong(); frag_offset[i] = MSG_ReadShort(); frag_length[i] = MSG_ReadShort(); } } if (!Netchan_Validate(chan, frag_message, fragid, frag_offset, frag_length)) return FALSE; } sequence &= ~(1 << 31); sequence &= ~(1 << 30); sequence_ack &= ~(1 << 31); sequence_ack &= ~(1 << 30); if (net_showpackets.value != 0.0 && net_showpackets.value != 3.0) { char c = (chan == &g_pcls.netchan) ? 'c' : 's'; Con_Printf( " %c <-- sz=%i seq=%i ack=%i rel=%i tm=%f\n", c, net_message.cursize, sequence, sequence_ack, reliable_message, (chan == &g_pcls.netchan) ? g_pcl.time : g_psv.time); } if (sequence <= (unsigned)chan->incoming_sequence) { if (net_showdrop.value != 0.0) { if (sequence == (unsigned)chan->incoming_sequence) Con_Printf("%s:duplicate packet %i at %i\n", NET_AdrToString(chan->remote_address), sequence, chan->incoming_sequence); else Con_Printf("%s:out of order packet %i at %i\n", NET_AdrToString(chan->remote_address), sequence, chan->incoming_sequence); } return FALSE; } // // dropped packets don't keep the message from being used // net_drop = sequence - (chan->incoming_sequence + 1); if (net_drop > 0 && net_showdrop.value != 0.0) { Con_Printf("%s:Dropped %i packets at %i\n", NET_AdrToString(chan->remote_address), net_drop, sequence); } // // if the current outgoing reliable message has been acknowledged // clear the buffer to make way for the next // if (reliable_ack == (unsigned)chan->reliable_sequence) { // Make sure we actually could have ack'd this message #ifdef REHLDS_FIXES if (sequence_ack >= (unsigned)chan->last_reliable_sequence) #else // REHLDS_FIXES if (chan->incoming_acknowledged + 1 >= chan->last_reliable_sequence) #endif // REHLDS_FIXES { chan->reliable_length = 0; // it has been received } } // // if this message contains a reliable message, bump incoming_reliable_sequence // chan->incoming_sequence = sequence; chan->incoming_acknowledged = sequence_ack; chan->incoming_reliable_acknowledged = reliable_ack; if (reliable_message) { chan->incoming_reliable_sequence ^= 1; } int statId = chan->flow[FLOW_INCOMING].current & 0x1F; chan->flow[FLOW_INCOMING].stats[statId].size = net_message.cursize + UDP_HEADER_SIZE; chan->flow[FLOW_INCOMING].stats[statId].time = realtime; chan->flow[FLOW_INCOMING].current++; Netchan_UpdateFlow(chan); if (message_contains_fragments) { for (i = 0; i < MAX_STREAMS; i++) { int j; fragbuf_t *pbuf; int inbufferid; int intotalbuffers; if (!frag_message[i]) continue; inbufferid = FRAG_GETID(fragid[i]); intotalbuffers = FRAG_GETCOUNT(fragid[i]); if (fragid[i] != 0) { pbuf = Netchan_FindBufferById(&chan->incomingbufs[i], fragid[i], true); if (pbuf) { int len = frag_length[i]; SZ_Clear(&pbuf->frag_message); SZ_Write(&pbuf->frag_message, &net_message.data[msg_readcount + frag_offset[i]], len); } else { Con_Printf("Netchan_Process: Couldn't allocate or find buffer %i\n", inbufferid); } // Count # of incoming bufs we've queued? are we done? Netchan_CheckForCompletion(chan, i, intotalbuffers); } // Rearrange incoming data to not have the frag stuff in the middle of it int wpos = msg_readcount + frag_offset[i]; int rpos = wpos + frag_length[i]; Q_memmove(net_message.data + wpos, net_message.data + rpos, net_message.cursize - rpos); net_message.cursize -= frag_length[i]; for (j = i + 1; j < MAX_STREAMS; j++) { frag_offset[j] -= frag_length[i]; // fragments order already validated } } // Is there anything left to process? if (net_message.cursize <= 16) return FALSE; } return TRUE; }
/* ================== Host_ShutdownServer This only happens at the end of a game, not between levels ================== */ void Host_ShutdownServer(qboolean crash) { int i; int count; sizebuf_t buf; char message[4]; double start; if (!sv.active) return; sv.active = false; // stop all client sounds immediately if (cls.state == ca_connected) CL_Disconnect (); // flush any pending messages - like the score!!! start = Sys_FloatTime(); do { count = 0; for (i=0, host_client = svs.clients ; i<svs.maxclients ; i++, host_client++) { if (host_client->active && host_client->message.cursize) { if (NET_CanSendMessage (host_client->netconnection)) { NET_SendMessage(host_client->netconnection, &host_client->message); SZ_Clear (&host_client->message); } else { NET_GetMessage(host_client->netconnection); count++; } } } if ((Sys_FloatTime() - start) > 3.0) break; } while (count); // make sure all the clients know we're disconnecting buf.data = message; buf.maxsize = 4; buf.cursize = 0; MSG_WriteByte(&buf, svc_disconnect); count = NET_SendToAll(&buf, 5); if (count) Con_Printf("Host_ShutdownServer: NET_SendToAll failed for %u clients\n", count); for (i=0, host_client = svs.clients ; i<svs.maxclients ; i++, host_client++) if (host_client->active) SV_DropClient(crash); // // clear structures // memset (&sv, 0, sizeof(sv)); memset (svs.clients, 0, svs.maxclientslimit*sizeof(client_t)); }
// All changes need to be in SV_SendEffect(), SV_ParseEffect(), // SV_SaveEffects(), SV_LoadEffects(), CL_ParseEffect() void SV_SendEffect (sizebuf_t *sb, int idx) { qboolean DoTest; vec3_t TestO1; int TestDistance; int i; if (sv_ce_scale.value > 0) DoTest = true; else DoTest = false; VectorClear(TestO1); switch (sv.Effects[idx].type) { case CE_HWSHEEPINATOR: case CE_HWXBOWSHOOT: VectorCopy(sv.Effects[idx].ef.Xbow.origin[5], TestO1); TestDistance = 900; break; case CE_SCARABCHAIN: VectorCopy(sv.Effects[idx].ef.Chain.origin, TestO1); TestDistance = 900; break; case CE_TRIPMINE: VectorCopy(sv.Effects[idx].ef.Chain.origin, TestO1); // DoTest = false; break; //ACHTUNG!!!!!!! setting DoTest to false here does not insure // that effect will be sent to everyone! case CE_TRIPMINESTILL: TestDistance = 10000; DoTest = false; break; case CE_RAIN: TestDistance = 10000; DoTest = false; break; case CE_FOUNTAIN: TestDistance = 10000; DoTest = false; break; case CE_QUAKE: VectorCopy(sv.Effects[idx].ef.Quake.origin, TestO1); TestDistance = 700; break; case CE_WHITE_SMOKE: case CE_GREEN_SMOKE: case CE_GREY_SMOKE: case CE_RED_SMOKE: case CE_SLOW_WHITE_SMOKE: case CE_TELESMK1: case CE_TELESMK2: case CE_GHOST: case CE_REDCLOUD: case CE_FLAMESTREAM: case CE_ACID_MUZZFL: case CE_FLAMEWALL: case CE_FLAMEWALL2: case CE_ONFIRE: case CE_RIPPLE: VectorCopy(sv.Effects[idx].ef.Smoke.origin, TestO1); TestDistance = 250; break; case CE_SM_WHITE_FLASH: case CE_YELLOWRED_FLASH: case CE_BLUESPARK: case CE_YELLOWSPARK: case CE_SM_CIRCLE_EXP: case CE_BG_CIRCLE_EXP: case CE_SM_EXPLOSION: case CE_SM_EXPLOSION2: case CE_LG_EXPLOSION: case CE_FLOOR_EXPLOSION: case CE_BLUE_EXPLOSION: case CE_REDSPARK: case CE_GREENSPARK: case CE_ICEHIT: case CE_MEDUSA_HIT: case CE_MEZZO_REFLECT: case CE_FLOOR_EXPLOSION2: case CE_XBOW_EXPLOSION: case CE_NEW_EXPLOSION: case CE_MAGIC_MISSILE_EXPLOSION: case CE_BONE_EXPLOSION: case CE_BLDRN_EXPL: case CE_ACID_HIT: case CE_LBALL_EXPL: case CE_FIREWALL_SMALL: case CE_FIREWALL_MEDIUM: case CE_FIREWALL_LARGE: case CE_ACID_SPLAT: case CE_ACID_EXPL: case CE_FBOOM: case CE_BRN_BOUNCE: case CE_LSHOCK: case CE_BOMB: case CE_FLOOR_EXPLOSION3: VectorCopy(sv.Effects[idx].ef.Smoke.origin, TestO1); TestDistance = 250; break; case CE_WHITE_FLASH: case CE_BLUE_FLASH: case CE_SM_BLUE_FLASH: case CE_HWSPLITFLASH: case CE_RED_FLASH: VectorCopy(sv.Effects[idx].ef.Smoke.origin, TestO1); TestDistance = 250; break; case CE_RIDER_DEATH: DoTest = false; break; case CE_TELEPORTERPUFFS: VectorCopy(sv.Effects[idx].ef.Teleporter.origin, TestO1); TestDistance = 350; break; case CE_TELEPORTERBODY: VectorCopy(sv.Effects[idx].ef.Teleporter.origin, TestO1); TestDistance = 350; break; case CE_DEATHBUBBLES: if (sv.Effects[idx].ef.Bubble.owner < 0 || sv.Effects[idx].ef.Bubble.owner >= sv.num_edicts) { return; } VectorCopy(PROG_TO_EDICT(sv.Effects[idx].ef.Bubble.owner)->v.origin, TestO1); TestDistance = 400; break; case CE_HWDRILLA: case CE_BONESHARD: case CE_BONESHRAPNEL: case CE_HWBONEBALL: case CE_HWRAVENSTAFF: case CE_HWRAVENPOWER: VectorCopy(sv.Effects[idx].ef.Missile.origin, TestO1); TestDistance = 900; break; case CE_HWMISSILESTAR: case CE_HWEIDOLONSTAR: VectorCopy(sv.Effects[idx].ef.Missile.origin, TestO1); TestDistance = 600; break; default: // Sys_Error ("%s: bad type", __thisfunc__); PR_RunError ("%s: bad type", __thisfunc__); break; } MSG_WriteByte (&sv.multicast, svc_start_effect); MSG_WriteByte (&sv.multicast, idx); MSG_WriteByte (&sv.multicast, sv.Effects[idx].type); switch (sv.Effects[idx].type) { case CE_RAIN: MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Rain.min_org[0]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Rain.min_org[1]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Rain.min_org[2]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Rain.max_org[0]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Rain.max_org[1]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Rain.max_org[2]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Rain.e_size[0]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Rain.e_size[1]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Rain.e_size[2]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Rain.dir[0]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Rain.dir[1]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Rain.dir[2]); MSG_WriteShort(&sv.multicast, sv.Effects[idx].ef.Rain.color); MSG_WriteShort(&sv.multicast, sv.Effects[idx].ef.Rain.count); MSG_WriteFloat(&sv.multicast, sv.Effects[idx].ef.Rain.wait); break; case CE_FOUNTAIN: MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Fountain.pos[0]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Fountain.pos[1]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Fountain.pos[2]); MSG_WriteAngle(&sv.multicast, sv.Effects[idx].ef.Fountain.angle[0]); MSG_WriteAngle(&sv.multicast, sv.Effects[idx].ef.Fountain.angle[1]); MSG_WriteAngle(&sv.multicast, sv.Effects[idx].ef.Fountain.angle[2]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Fountain.movedir[0]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Fountain.movedir[1]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Fountain.movedir[2]); MSG_WriteShort(&sv.multicast, sv.Effects[idx].ef.Fountain.color); MSG_WriteByte(&sv.multicast, sv.Effects[idx].ef.Fountain.cnt); break; case CE_QUAKE: MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Quake.origin[0]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Quake.origin[1]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Quake.origin[2]); MSG_WriteFloat(&sv.multicast, sv.Effects[idx].ef.Quake.radius); break; case CE_WHITE_SMOKE: case CE_GREEN_SMOKE: case CE_GREY_SMOKE: case CE_RED_SMOKE: case CE_SLOW_WHITE_SMOKE: case CE_TELESMK1: case CE_TELESMK2: case CE_GHOST: case CE_REDCLOUD: case CE_FLAMESTREAM: case CE_ACID_MUZZFL: case CE_FLAMEWALL: case CE_FLAMEWALL2: case CE_ONFIRE: case CE_RIPPLE: MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Smoke.origin[0]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Smoke.origin[1]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Smoke.origin[2]); MSG_WriteFloat(&sv.multicast, sv.Effects[idx].ef.Smoke.velocity[0]); MSG_WriteFloat(&sv.multicast, sv.Effects[idx].ef.Smoke.velocity[1]); MSG_WriteFloat(&sv.multicast, sv.Effects[idx].ef.Smoke.velocity[2]); MSG_WriteFloat(&sv.multicast, sv.Effects[idx].ef.Smoke.framelength); break; case CE_SM_WHITE_FLASH: case CE_YELLOWRED_FLASH: case CE_BLUESPARK: case CE_YELLOWSPARK: case CE_SM_CIRCLE_EXP: case CE_BG_CIRCLE_EXP: case CE_SM_EXPLOSION: case CE_SM_EXPLOSION2: case CE_LG_EXPLOSION: case CE_FLOOR_EXPLOSION: case CE_BLUE_EXPLOSION: case CE_REDSPARK: case CE_GREENSPARK: case CE_ICEHIT: case CE_MEDUSA_HIT: case CE_MEZZO_REFLECT: case CE_FLOOR_EXPLOSION2: case CE_XBOW_EXPLOSION: case CE_NEW_EXPLOSION: case CE_MAGIC_MISSILE_EXPLOSION: case CE_BONE_EXPLOSION: case CE_BLDRN_EXPL: case CE_ACID_HIT: case CE_ACID_SPLAT: case CE_ACID_EXPL: case CE_LBALL_EXPL: case CE_FIREWALL_SMALL: case CE_FIREWALL_MEDIUM: case CE_FIREWALL_LARGE: case CE_FBOOM: case CE_BOMB: case CE_BRN_BOUNCE: case CE_LSHOCK: MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Smoke.origin[0]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Smoke.origin[1]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Smoke.origin[2]); break; case CE_WHITE_FLASH: case CE_BLUE_FLASH: case CE_SM_BLUE_FLASH: case CE_HWSPLITFLASH: case CE_RED_FLASH: MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Smoke.origin[0]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Smoke.origin[1]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Smoke.origin[2]); break; case CE_RIDER_DEATH: MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.RD.origin[0]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.RD.origin[1]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.RD.origin[2]); break; case CE_TELEPORTERPUFFS: MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Teleporter.origin[0]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Teleporter.origin[1]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Teleporter.origin[2]); break; case CE_TELEPORTERBODY: MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Teleporter.origin[0]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Teleporter.origin[1]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Teleporter.origin[2]); MSG_WriteFloat(&sv.multicast, sv.Effects[idx].ef.Teleporter.velocity[0][0]); MSG_WriteFloat(&sv.multicast, sv.Effects[idx].ef.Teleporter.velocity[0][1]); MSG_WriteFloat(&sv.multicast, sv.Effects[idx].ef.Teleporter.velocity[0][2]); MSG_WriteFloat(&sv.multicast, sv.Effects[idx].ef.Teleporter.skinnum); break; case CE_BONESHRAPNEL: case CE_HWBONEBALL: MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Missile.origin[0]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Missile.origin[1]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Missile.origin[2]); MSG_WriteFloat(&sv.multicast, sv.Effects[idx].ef.Missile.velocity[0]); MSG_WriteFloat(&sv.multicast, sv.Effects[idx].ef.Missile.velocity[1]); MSG_WriteFloat(&sv.multicast, sv.Effects[idx].ef.Missile.velocity[2]); MSG_WriteFloat(&sv.multicast, sv.Effects[idx].ef.Missile.angle[0]); MSG_WriteFloat(&sv.multicast, sv.Effects[idx].ef.Missile.angle[1]); MSG_WriteFloat(&sv.multicast, sv.Effects[idx].ef.Missile.angle[2]); MSG_WriteFloat(&sv.multicast, sv.Effects[idx].ef.Missile.avelocity[0]); MSG_WriteFloat(&sv.multicast, sv.Effects[idx].ef.Missile.avelocity[1]); MSG_WriteFloat(&sv.multicast, sv.Effects[idx].ef.Missile.avelocity[2]); break; case CE_BONESHARD: case CE_HWRAVENSTAFF: case CE_HWMISSILESTAR: case CE_HWEIDOLONSTAR: case CE_HWRAVENPOWER: MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Missile.origin[0]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Missile.origin[1]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Missile.origin[2]); MSG_WriteFloat(&sv.multicast, sv.Effects[idx].ef.Missile.velocity[0]); MSG_WriteFloat(&sv.multicast, sv.Effects[idx].ef.Missile.velocity[1]); MSG_WriteFloat(&sv.multicast, sv.Effects[idx].ef.Missile.velocity[2]); break; case CE_HWDRILLA: MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Missile.origin[0]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Missile.origin[1]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Missile.origin[2]); MSG_WriteAngle(&sv.multicast, sv.Effects[idx].ef.Missile.angle[0]); MSG_WriteAngle(&sv.multicast, sv.Effects[idx].ef.Missile.angle[1]); MSG_WriteShort(&sv.multicast, sv.Effects[idx].ef.Missile.speed); break; case CE_DEATHBUBBLES: MSG_WriteShort(&sv.multicast, sv.Effects[idx].ef.Bubble.owner); MSG_WriteByte(&sv.multicast, sv.Effects[idx].ef.Bubble.offset[0]); MSG_WriteByte(&sv.multicast, sv.Effects[idx].ef.Bubble.offset[1]); MSG_WriteByte(&sv.multicast, sv.Effects[idx].ef.Bubble.offset[2]); MSG_WriteByte(&sv.multicast, sv.Effects[idx].ef.Bubble.count); break; case CE_SCARABCHAIN: MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Chain.origin[0]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Chain.origin[1]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Chain.origin[2]); MSG_WriteShort(&sv.multicast, sv.Effects[idx].ef.Chain.owner + sv.Effects[idx].ef.Chain.material); MSG_WriteByte(&sv.multicast, sv.Effects[idx].ef.Chain.tag); break; case CE_TRIPMINESTILL: case CE_TRIPMINE: MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Chain.origin[0]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Chain.origin[1]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Chain.origin[2]); MSG_WriteFloat(&sv.multicast, sv.Effects[idx].ef.Chain.velocity[0]); MSG_WriteFloat(&sv.multicast, sv.Effects[idx].ef.Chain.velocity[1]); MSG_WriteFloat(&sv.multicast, sv.Effects[idx].ef.Chain.velocity[2]); break; case CE_HWSHEEPINATOR: MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Xbow.origin[5][0]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Xbow.origin[5][1]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Xbow.origin[5][2]); MSG_WriteAngle(&sv.multicast, sv.Effects[idx].ef.Xbow.angle[0]); MSG_WriteAngle(&sv.multicast, sv.Effects[idx].ef.Xbow.angle[1]); //now send the guys that have turned MSG_WriteByte(&sv.multicast, sv.Effects[idx].ef.Xbow.turnedbolts); MSG_WriteByte(&sv.multicast, sv.Effects[idx].ef.Xbow.activebolts); for (i = 0 ; i < 5 ; i++) { if ((1<<i)&sv.Effects[idx].ef.Xbow.turnedbolts) { MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Xbow.origin[i][0]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Xbow.origin[i][1]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Xbow.origin[i][2]); MSG_WriteAngle(&sv.multicast, sv.Effects[idx].ef.Xbow.vel[i][0]); MSG_WriteAngle(&sv.multicast, sv.Effects[idx].ef.Xbow.vel[i][1]); } } break; case CE_HWXBOWSHOOT: MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Xbow.origin[5][0]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Xbow.origin[5][1]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Xbow.origin[5][2]); MSG_WriteAngle(&sv.multicast, sv.Effects[idx].ef.Xbow.angle[0]); MSG_WriteAngle(&sv.multicast, sv.Effects[idx].ef.Xbow.angle[1]); // MSG_WriteFloat(&sv.multicast, sv.Effects[idx].Xbow.angle[2]); MSG_WriteByte(&sv.multicast, sv.Effects[idx].ef.Xbow.bolts); MSG_WriteByte(&sv.multicast, sv.Effects[idx].ef.Xbow.randseed); //now send the guys that have turned MSG_WriteByte(&sv.multicast, sv.Effects[idx].ef.Xbow.turnedbolts); MSG_WriteByte(&sv.multicast, sv.Effects[idx].ef.Xbow.activebolts); for (i = 0 ; i < 5 ; i++) { if ( (1<<i) & sv.Effects[idx].ef.Xbow.turnedbolts ) { MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Xbow.origin[i][0]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Xbow.origin[i][1]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Xbow.origin[i][2]); MSG_WriteAngle(&sv.multicast, sv.Effects[idx].ef.Xbow.vel[i][0]); MSG_WriteAngle(&sv.multicast, sv.Effects[idx].ef.Xbow.vel[i][1]); } } break; default: // Sys_Error ("%s: bad type", __thisfunc__); PR_RunError ("%s: bad type", __thisfunc__); break; } if (sb) { SZ_Write (sb, sv.multicast.data, sv.multicast.cursize); SZ_Clear (&sv.multicast); } else { if (DoTest) { SV_Multicast (TestO1, MULTICAST_PVS_R); } else { SV_Multicast (TestO1, MULTICAST_ALL_R); } sv.Effects[idx].client_list = clients_multicast; } }