/* * TV_Lobby_SendClientDatagram */ static qboolean TV_Lobby_SendClientDatagram( client_t *client ) { qbyte msg_buf[MAX_MSGLEN]; msg_t msg; assert( client ); assert( !client->relay ); TV_Downstream_InitClientMessage( client, &msg, msg_buf, sizeof( msg_buf ) ); TV_Downstream_AddReliableCommandsToMessage( client, &msg ); TV_Lobby_WriteFrameSnapToClient( client, &msg ); return TV_Downstream_SendMessageToClient( client, &msg ); }
/* * TV_Downstream_DenyDownload * Helper function for generating initdownload packets for denying download */ static void TV_Downstream_DenyDownload( client_t *client, const char *reason ) { msg_t message; uint8_t messageData[MAX_MSGLEN]; assert( client ); assert( reason && reason[0] ); // size -1 is used to signal that it's refused // URL field is used for deny reason TV_Downstream_InitClientMessage( client, &message, messageData, sizeof( messageData ) ); TV_Downstream_SendServerCommand( client, "initdownload \"%s\" %i %u %i \"%s\"", "", -1, 0, false, reason ); TV_Downstream_AddReliableCommandsToMessage( client, &message ); TV_Downstream_SendMessageToClient( client, &message ); }
/* * TV_Downstream_SendClientMessages */ void TV_Downstream_SendClientMessages( void ) { int i; client_t *client; msg_t message; qbyte messageData[MAX_MSGLEN]; // send a message to each connected client for( i = 0, client = tvs.clients; i < tv_maxclients->integer; i++, client++ ) { if( client->state == CS_FREE || client->state == CS_ZOMBIE ) continue; if( client->state < CS_SPAWNED ) { // send pending reliable commands, or send heartbeats for not timing out /* if( client->reliableSequence > client->reliableSent || (client->reliableSequence > client->reliableAcknowledge && tvs.realtime - client->lastPacketSentTime > 50) || tvs.realtime - client->lastPacketSentTime > 500 ) */ if( client->reliableSequence > client->reliableAcknowledge || tvs.realtime - client->lastPacketSentTime > 1000 ) { TV_Downstream_InitClientMessage( client, &message, messageData, sizeof( messageData ) ); TV_Downstream_AddReliableCommandsToMessage( client, &message ); if( !TV_Downstream_SendMessageToClient( client, &message ) ) { Com_Printf( "%s" S_COLOR_WHITE ": Error sending message: %s\n", client->name, NET_ErrorString() ); if( client->reliable ) { TV_Downstream_DropClient( client, DROP_TYPE_GENERAL, "Error sending message: %s\n", NET_ErrorString() ); } } } } } }
/* * TV_Relay_SendClientDatagram */ static bool TV_Relay_SendClientDatagram( relay_t *relay, client_t *client ) { uint8_t msg_buf[MAX_MSGLEN]; msg_t msg; snapshot_t *frame; assert( relay ); assert( client ); assert( relay == client->relay ); TV_Downstream_InitClientMessage( client, &msg, msg_buf, sizeof( msg_buf ) ); TV_Downstream_AddReliableCommandsToMessage( client, &msg ); // send over all the relevant entity_state_t // and the player_state_t TV_Relay_BuildClientFrameSnap( relay, client ); frame = relay->curFrame; SNAP_WriteFrameSnapToClient( &relay->gi, client, &msg, relay->framenum, relay->serverTime, relay->baselines, &relay->client_entities, frame->numgamecommands, frame->gamecommands, frame->gamecommandsData ); return TV_Downstream_SendMessageToClient( client, &msg ); }
/* * TV_Downstream_DropClient */ void TV_Downstream_DropClient( client_t *drop, int type, const char *format, ... ) { va_list argptr; char string[1024]; msg_t Message; qbyte MessageData[MAX_MSGLEN]; va_start( argptr, format ); Q_vsnprintfz( string, sizeof( string ), format, argptr ); va_end( argptr ); Com_Printf( "%s" S_COLOR_WHITE " dropped: %s\n", drop->name, string ); TV_Downstream_InitClientMessage( drop, &Message, MessageData, sizeof( MessageData ) ); TV_Downstream_SendServerCommand( drop, "disconnect %i \"%s\"", type, string ); TV_Downstream_AddReliableCommandsToMessage( drop, &Message ); TV_Downstream_SendMessageToClient( drop, &Message ); Netchan_PushAllFragments( &drop->netchan ); if( drop->relay && /*drop->relay->state == CA_ACTIVE && */drop->state >= CS_CONNECTING ) TV_Relay_ClientDisconnect( drop->relay, drop ); // make sure everything is clean TV_Downstream_ClientResetCommandBuffers( drop, qtrue ); SNAP_FreeClientFrames( drop ); if( drop->download.name ) { if( drop->download.data ) { FS_FreeBaseFile( drop->download.data ); drop->download.data = NULL; } Mem_ZoneFree( drop->download.name ); drop->download.name = NULL; drop->download.size = 0; drop->download.timeout = 0; } if( drop->individual_socket ) NET_CloseSocket( &drop->socket ); if( drop->mv ) { tvs.nummvclients--; drop->mv = qfalse; } memset( &drop->flood, 0, sizeof( drop->flood ) ); drop->edict = NULL; drop->relay = NULL; drop->tv = qfalse; drop->state = CS_ZOMBIE; // become free in a few seconds drop->name[0] = 0; }
/* * TV_Downstream_New_f * * Sends the first message from the server to a connected client. * This will be sent on the initial upstream and upon each server load. */ void TV_Downstream_New_f( client_t *client ) { int playernum, numpure; int tv_bitflags; purelist_t *iter; msg_t message; uint8_t messageData[MAX_MSGLEN]; // if in CS_AWAITING we have sended the response packet the new once already, // but client might have not got it so we send it again if( client->state >= CS_SPAWNED ) { Com_DPrintf( "New not valid -- already spawned\n" ); return; } // relay is not ready yet if( client->relay && client->relay->state < CA_ACTIVE ) { TV_Relay_DelayNew( client ); return; } // // serverdata needs to go over for all types of servers // to make sure the protocol is right, and to set the gamedir // TV_Downstream_InitClientMessage( client, &message, messageData, sizeof( messageData ) ); // send the serverdata MSG_WriteByte( &message, svc_serverdata ); MSG_WriteLong( &message, APP_PROTOCOL_VERSION ); if( !client->relay ) { MSG_WriteLong( &message, tvs.lobby.spawncount ); MSG_WriteShort( &message, tvs.lobby.snapFrameTime ); MSG_WriteString( &message, FS_BaseGameDirectory() ); MSG_WriteString( &message, FS_GameDirectory() ); } else { MSG_WriteLong( &message, client->relay->servercount ); MSG_WriteShort( &message, client->relay->snapFrameTime ); MSG_WriteString( &message, client->relay->basegame ); MSG_WriteString( &message, client->relay->game ); } if( client->relay ) { // we use our own playernum on the relay server MSG_WriteShort( &message, client->relay->playernum ); } else { playernum = client - tvs.clients; MSG_WriteShort( &message, playernum ); } // send full levelname if( !client->relay ) MSG_WriteString( &message, tv_name->string ); else MSG_WriteString( &message, client->relay->levelname ); memset( &client->lastcmd, 0, sizeof( client->lastcmd ) ); tv_bitflags = SV_BITFLAGS_TVSERVER; if( client->reliable ) tv_bitflags |= SV_BITFLAGS_RELIABLE; MSG_WriteByte( &message, tv_bitflags ); // sv_bitflags // purelist if( !client->relay ) { MSG_WriteShort( &message, 0 ); } else { numpure = Com_CountPureListFiles( client->relay->purelist ); MSG_WriteShort( &message, numpure ); iter = client->relay->purelist; while( iter ) { MSG_WriteString( &message, iter->filename ); MSG_WriteLong( &message, iter->checksum ); iter = iter->next; } } TV_Downstream_ClientResetCommandBuffers( client, true ); TV_Downstream_SendMessageToClient( client, &message ); Netchan_PushAllFragments( &client->netchan ); // don't let it send reliable commands until we get the first configstring request client->state = CS_CONNECTING; }
/* * TV_Downstream_Baselines_f */ static void TV_Downstream_Baselines_f( client_t *client ) { int start; entity_state_t nullstate; msg_t message; uint8_t messageData[MAX_MSGLEN]; if( client->state != CS_CONNECTED ) return; // relay is not ready yet if( client->relay && client->relay->state < CA_ACTIVE ) { TV_Downstream_SendServerCommand( client, "reconnect" ); return; } // handle the case of a level changing while a client was connecting if( atoi( Cmd_Argv( 1 ) ) != ( client->relay ? client->relay->servercount : tvs.lobby.spawncount ) ) { TV_Downstream_New_f( client ); return; } if( !client->relay ) { TV_Downstream_SendServerCommand( client, "precache %i", tvs.lobby.spawncount ); return; } start = atoi( Cmd_Argv( 2 ) ); if( start < 0 ) start = 0; memset( &nullstate, 0, sizeof( nullstate ) ); // write a packet full of data TV_Downstream_InitClientMessage( client, &message, messageData, sizeof( messageData ) ); while( message.cursize < FRAGMENT_SIZE * 3 && start < MAX_EDICTS ) { if( client->relay->baselines[start].number ) { MSG_WriteByte( &message, svc_spawnbaseline ); MSG_WriteDeltaEntity( &nullstate, &client->relay->baselines[start], &message, true, true ); } start++; } // send next command if( start == MAX_EDICTS ) { TV_Downstream_SendServerCommand( client, "precache %i", client->relay->servercount ); } else { TV_Downstream_SendServerCommand( client, "cmd baselines %i %i", client->relay->servercount, start ); } TV_Downstream_AddReliableCommandsToMessage( client, &message ); TV_Downstream_SendMessageToClient( client, &message ); }