static boolean NET_OpenSocket(void) { memset(clientaddress, 0, sizeof (clientaddress)); //CONS_Printf("SDL_Net Code starting up\n"); I_NetSend = NET_Send; I_NetGet = NET_Get; I_NetCloseSocket = NET_CloseSocket; I_NetFreeNodenum = NET_FreeNodenum; I_NetMakeNode = NET_NetMakeNode; //I_NetCanSend = NET_CanSend; // build the socket but close it first NET_CloseSocket(); mysocket = NET_Socket(); if (!mysocket) return false; // for select myset = SDLNet_AllocSocketSet(1); if (!myset) { CONS_Printf("SDL_Net: %s",SDLNet_GetError()); return false; } if (SDLNet_UDP_AddSocket(myset,mysocket) == -1) { CONS_Printf("SDL_Net: %s",SDLNet_GetError()); return false; } return true; }
/* ================== NET_Shutdown ================== */ void NET_Shutdown (){ if (!net.initialized) return; // Remove commands Cmd_RemoveCommand("showIP"); Cmd_RemoveCommand("showTraffic"); Cmd_RemoveCommand("restartNetwork"); // Close the UDP sockets NET_CloseSocket(NS_SERVER); NET_CloseSocket(NS_CLIENT); // Shutdown WinSock WSACleanup(); Mem_Fill(&net, 0, sizeof(network_t)); }
/* * SV_ShutdownGame * * Called when each game quits */ void SV_ShutdownGame( const char *finalmsg, qboolean reconnect ) { if( !svs.initialized ) return; if( svs.demo.file ) SV_Demo_Stop_f(); if( svs.clients ) SV_FinalMessage( finalmsg, reconnect ); SV_ShutdownGameProgs(); // SV_MM_Shutdown(); NET_CloseSocket( &svs.socket_loopback ); NET_CloseSocket( &svs.socket_udp ); NET_CloseSocket( &svs.socket_udp6 ); #ifdef TCP_ALLOW_CONNECT if( sv_tcp->integer ) NET_CloseSocket( &svs.socket_tcp ); #endif // get any latched variable changes (sv_maxclients, etc) Cvar_GetLatchedVars( CVAR_LATCH ); if( svs.clients ) { Mem_Free( svs.clients ); svs.clients = NULL; } if( svs.client_entities.entities ) { Mem_Free( svs.client_entities.entities ); memset( &svs.client_entities, 0, sizeof( svs.client_entities ) ); } if( svs.cms ) { CM_Free( svs.cms ); svs.cms = NULL; } memset( &sv, 0, sizeof( sv ) ); Com_SetServerState( sv.state ); Com_FreePureList( &svs.purelist ); if( svs.motd ) { Mem_Free( svs.motd ); svs.motd = NULL; } if( sv_mempool ) Mem_EmptyPool( sv_mempool ); memset( &svs, 0, sizeof( svs ) ); svs.initialized = qfalse; }
/* * SV_InitGame * A brand new game has been started */ void SV_InitGame( void ) { int i; edict_t *ent; netadr_t address, ipv6_address; // make sure the client is down CL_Disconnect( NULL ); SCR_BeginLoadingPlaque(); if( svs.initialized ) { // cause any connected clients to reconnect SV_ShutdownGame( "Server restarted", qtrue ); // SV_ShutdownGame will also call Cvar_GetLatchedVars } else { // get any latched variable changes (sv_maxclients, etc) Cvar_GetLatchedVars( CVAR_LATCH ); } svs.initialized = qtrue; if( sv_skilllevel->integer > 2 ) Cvar_ForceSet( "sv_skilllevel", "2" ); if( sv_skilllevel->integer < 0 ) Cvar_ForceSet( "sv_skilllevel", "0" ); // init clients if( sv_maxclients->integer < 1 ) Cvar_FullSet( "sv_maxclients", "8", CVAR_SERVERINFO | CVAR_LATCH, qtrue ); else if( sv_maxclients->integer > MAX_CLIENTS ) Cvar_FullSet( "sv_maxclients", va( "%i", MAX_CLIENTS ), CVAR_SERVERINFO | CVAR_LATCH, qtrue ); svs.spawncount = rand(); svs.clients = Mem_Alloc( sv_mempool, sizeof( client_t )*sv_maxclients->integer ); svs.client_entities.num_entities = sv_maxclients->integer * UPDATE_BACKUP * MAX_SNAP_ENTITIES; svs.client_entities.entities = Mem_Alloc( sv_mempool, sizeof( entity_state_t ) * svs.client_entities.num_entities ); // init network stuff address.type = NA_NOTRANSMIT; ipv6_address.type = NA_NOTRANSMIT; if( !dedicated->integer ) { NET_InitAddress( &address, NA_LOOPBACK ); if( !NET_OpenSocket( &svs.socket_loopback, SOCKET_LOOPBACK, &address, qtrue ) ) Com_Error( ERR_FATAL, "Couldn't open loopback socket: %s\n", NET_ErrorString() ); } if( dedicated->integer || sv_maxclients->integer > 1 ) { qboolean socket_opened = qfalse; // IPv4 NET_StringToAddress( sv_ip->string, &address ); NET_SetAddressPort( &address, sv_port->integer ); if( !NET_OpenSocket( &svs.socket_udp, SOCKET_UDP, &address, qtrue ) ) Com_Printf( "Error: Couldn't open UDP socket: %s\n", NET_ErrorString() ); else socket_opened = qtrue; // IPv6 NET_StringToAddress( sv_ip6->string, &ipv6_address ); if( ipv6_address.type == NA_IP6 ) { NET_SetAddressPort( &ipv6_address, sv_port6->integer ); if( !NET_OpenSocket( &svs.socket_udp6, SOCKET_UDP, &ipv6_address, qtrue ) ) Com_Printf( "Error: Couldn't open UDP6 socket: %s\n", NET_ErrorString() ); else socket_opened = qtrue; } else Com_Printf( "Error: invalid IPv6 address: %s\n", sv_ip6->string ); if( dedicated->integer && !socket_opened ) Com_Error( ERR_FATAL, "Couldn't open any socket\n" ); } #ifdef TCP_ALLOW_CONNECT if( sv_tcp->integer && ( dedicated->integer || sv_maxclients->integer > 1 ) ) { if( !NET_OpenSocket( &svs.socket_tcp, SOCKET_TCP, &address, qtrue ) ) { Com_Printf( "Error: Couldn't open TCP socket: %s", NET_ErrorString() ); Cvar_ForceSet( "sv_tcp", "0" ); } else { if( !NET_Listen( &svs.socket_tcp ) ) { Com_Printf( "Error: Couldn't listen to TCP socket: %s", NET_ErrorString() ); NET_CloseSocket( &svs.socket_tcp ); Cvar_ForceSet( "sv_tcp", "0" ); } } } #endif // init mm // SV_MM_Init(); // init game SV_InitGameProgs(); for( i = 0; i < sv_maxclients->integer; i++ ) { ent = EDICT_NUM( i+1 ); ent->s.number = i+1; svs.clients[i].edict = ent; } // load the map assert( !svs.cms ); svs.cms = CM_New( NULL ); }
/* * SV_ShutdownGame * * Called when each game quits */ void SV_ShutdownGame( const char *finalmsg, bool reconnect ) { if( !svs.initialized ) { return; } if( svs.demo.file ) { SV_Demo_Stop_f(); } if( svs.clients ) { SV_FinalMessage( finalmsg, reconnect ); } SV_ShutdownGameProgs(); // SV_MM_Shutdown(); SV_MasterSendQuit(); NET_CloseSocket( &svs.socket_loopback ); NET_CloseSocket( &svs.socket_udp ); NET_CloseSocket( &svs.socket_udp6 ); #ifdef TCP_ALLOW_CONNECT if( sv_tcp->integer ) { NET_CloseSocket( &svs.socket_tcp ); NET_CloseSocket( &svs.socket_tcp6 ); } #endif // get any latched variable changes (sv_maxclients, etc) Cvar_GetLatchedVars( CVAR_LATCH ); if( svs.clients ) { Mem_Free( svs.clients ); svs.clients = NULL; } if( svs.client_entities.entities ) { Mem_Free( svs.client_entities.entities ); memset( &svs.client_entities, 0, sizeof( svs.client_entities ) ); } if( svs.cms ) { // CM_ReleaseReference will take care of freeing up the memory // if there are no other modules referencing the collision model CM_ReleaseReference( svs.cms ); svs.cms = NULL; } Com_SetServerCM( NULL, 0 ); memset( &sv, 0, sizeof( sv ) ); Com_SetServerState( sv.state ); Com_FreePureList( &svs.purelist ); if( svs.motd ) { Mem_Free( svs.motd ); svs.motd = NULL; } if( sv_mempool ) { Mem_EmptyPool( sv_mempool ); } if( svs.wakelock ) { Sys_ReleaseWakeLock( svs.wakelock ); svs.wakelock = NULL; } memset( &svs, 0, sizeof( svs ) ); svs.initialized = false; }
/* * SV_DropClient * * Called when the player is totally leaving the server, either willingly * or unwillingly. This is NOT called if the entire server is quiting * or crashing. */ void SV_DropClient( client_t *drop, int type, const char *format, ... ) { va_list argptr; char *reason; char string[1024]; if( format ) { va_start( argptr, format ); Q_vsnprintfz( string, sizeof( string ), format, argptr ); va_end( argptr ); reason = string; } else { Q_strncpyz( string, "User disconnected", sizeof( string ) ); reason = NULL; } // remove the rating of the client if( drop->edict ) ge->RemoveRating( drop->edict ); // add the disconnect if( drop->edict && ( drop->edict->r.svflags & SVF_FAKECLIENT ) ) { ge->ClientDisconnect( drop->edict, reason ); SV_ClientResetCommandBuffers( drop ); // make sure everything is clean } else { SV_InitClientMessage( drop, &tmpMessage, NULL, 0 ); SV_SendServerCommand( drop, "disconnect %i \"%s\"", type, string ); SV_AddReliableCommandsToMessage( drop, &tmpMessage ); SV_SendMessageToClient( drop, &tmpMessage ); Netchan_PushAllFragments( &drop->netchan ); if( drop->state >= CS_CONNECTED ) { // call the prog function for removing a client // this will remove the body, among other things ge->ClientDisconnect( drop->edict, reason ); } else if( drop->name[0] ) { Com_Printf( "Connecting client %s%s disconnected (%s%s)\n", drop->name, S_COLOR_WHITE, reason, S_COLOR_WHITE ); } } SV_MM_ClientDisconnect( drop ); 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 ) { sv.num_mv_clients--; drop->mv = qfalse; } drop->tvclient = qfalse; drop->state = CS_ZOMBIE; // become free in a few seconds drop->name[0] = 0; }
/* * SV_CheckTimeouts * * If a packet has not been received from a client for timeout->value * seconds, drop the conneciton. Server frames are used instead of * realtime to avoid dropping the local client while debugging. * * When a client is normally dropped, the client_t goes into a zombie state * for a few seconds to make sure any final reliable message gets resent * if necessary */ static void SV_CheckTimeouts( void ) { client_t *cl; int i; #ifdef TCP_SUPPORT // timeout incoming connections for( i = 0; i < MAX_INCOMING_CONNECTIONS; i++ ) { if( svs.incoming[i].active && svs.incoming[i].time + 1000 * 15 < svs.realtime ) { Com_Printf( "Incoming TCP connection from %s timed out\n", NET_AddressToString( &svs.incoming[i].address ) ); NET_CloseSocket( &svs.incoming[i].socket ); svs.incoming[i].active = qfalse; } } #endif // timeout clients for( i = 0, cl = svs.clients; i < sv_maxclients->integer; i++, cl++ ) { // fake clients do not timeout if( cl->edict && ( cl->edict->r.svflags & SVF_FAKECLIENT ) ) cl->lastPacketReceivedTime = svs.realtime; // message times may be wrong across a changelevel else if( cl->lastPacketReceivedTime > svs.realtime ) cl->lastPacketReceivedTime = svs.realtime; if( cl->state == CS_ZOMBIE && cl->lastPacketReceivedTime + 1000 * sv_zombietime->value < svs.realtime ) { cl->state = CS_FREE; // can now be reused if( cl->individual_socket ) NET_CloseSocket( &cl->socket ); continue; } if( ( cl->state != CS_FREE && cl->state != CS_ZOMBIE ) && ( cl->lastPacketReceivedTime + 1000 * sv_timeout->value < svs.realtime ) ) { SV_DropClient( cl, DROP_TYPE_GENERAL, "Error: Connection timed out" ); cl->state = CS_FREE; // don't bother with zombie state if( cl->socket.open ) NET_CloseSocket( &cl->socket ); } // timeout downloads left open if( ( cl->state != CS_FREE && cl->state != CS_ZOMBIE ) && ( cl->download.name && cl->download.timeout < svs.realtime ) ) { Com_Printf( "Download of %s to %s%s timed out\n", cl->download.name, cl->name, S_COLOR_WHITE ); if( cl->download.data ) { FS_FreeBaseFile( cl->download.data ); cl->download.data = NULL; } Mem_ZoneFree( cl->download.name ); cl->download.name = NULL; cl->download.size = 0; cl->download.timeout = 0; } } }
/* * SV_ReadPackets */ static void SV_ReadPackets( void ) { int i, socketind, ret; client_t *cl; #ifdef TCP_SUPPORT socket_t newsocket; netadr_t mmserver; #endif int game_port; socket_t *socket; netadr_t address; static msg_t msg; static qbyte msgData[MAX_MSGLEN]; socket_t* sockets [] = { &svs.socket_loopback, &svs.socket_udp, &svs.socket_udp6, }; #ifdef TCP_SUPPORT if( SV_MM_Initialized() ) SV_MM_NetAddress( &mmserver ); if( svs.socket_tcp.open ) { while( qtrue ) { // find a free slot for( i = 0; i < MAX_INCOMING_CONNECTIONS; i++ ) { if( !svs.incoming[i].active ) break; } if( i == MAX_INCOMING_CONNECTIONS ) break; if( ( ret = NET_Accept( &svs.socket_tcp, &newsocket, &address ) ) == 0 ) break; if( ret == -1 ) { Com_Printf( "NET_Accept: Error: %s\n", NET_ErrorString() ); continue; } Com_Printf( "New TCP connection from %s\n", NET_AddressToString( &address ) ); svs.incoming[i].active = qtrue; svs.incoming[i].socket = newsocket; svs.incoming[i].address = address; svs.incoming[i].time = svs.realtime; } } for( i = 0; i < MAX_INCOMING_CONNECTIONS; i++ ) { if( !svs.incoming[i].active ) continue; ret = NET_GetPacket( &svs.incoming[i].socket, &address, &msg ); if( ret == -1 ) { Com_Printf( "NET_GetPacket: Error: %s\n", NET_ErrorString() ); NET_CloseSocket( &svs.incoming[i].socket ); svs.incoming[i].active = qfalse; } else if( ret == 1 ) { if( SV_MM_Initialized() && NET_CompareBaseAddress( &mmserver, &address ) ) { Com_DPrintf( "TCP packet from matchmaker\n" ); SV_MM_SetConnection( &svs.incoming[i] ); SV_MM_Packet( &msg ); SV_MM_SetConnection( NULL ); continue; } if( *(int *)msg.data != -1 ) { Com_Printf( "Sequence packet without connection\n" ); NET_CloseSocket( &svs.incoming[i].socket ); svs.incoming[i].active = qfalse; continue; } Com_Printf( "Connectionless TCP packet from: %s\n", NET_AddressToString( &address ) ); SV_ConnectionlessPacket( &svs.incoming[i].socket, &address, &msg ); } } #endif MSG_Init( &msg, msgData, sizeof( msgData ) ); for( socketind = 0; socketind < sizeof( sockets ) / sizeof( sockets[0] ); socketind++ ) { socket = sockets[socketind]; if( !socket->open ) continue; while( ( ret = NET_GetPacket( socket, &address, &msg ) ) != 0 ) { if( ret == -1 ) { Com_Printf( "NET_GetPacket: Error: %s\n", NET_ErrorString() ); continue; } // check for connectionless packet (0xffffffff) first if( *(int *)msg.data == -1 ) { SV_ConnectionlessPacket( socket, &address, &msg ); continue; } // read the game port out of the message so we can fix up // stupid address translating routers MSG_BeginReading( &msg ); MSG_ReadLong( &msg ); // sequence number MSG_ReadLong( &msg ); // sequence number game_port = MSG_ReadShort( &msg ) & 0xffff; // data follows // check for packets from connected clients for( i = 0, cl = svs.clients; i < sv_maxclients->integer; i++, cl++ ) { unsigned short addr_port; if( cl->state == CS_FREE || cl->state == CS_ZOMBIE ) continue; if( cl->edict && ( cl->edict->r.svflags & SVF_FAKECLIENT ) ) continue; if( !NET_CompareBaseAddress( &address, &cl->netchan.remoteAddress ) ) continue; if( cl->netchan.game_port != game_port ) continue; addr_port = NET_GetAddressPort( &address ); if( NET_GetAddressPort( &cl->netchan.remoteAddress ) != addr_port ) { Com_Printf( "SV_ReadPackets: fixing up a translated port\n" ); NET_SetAddressPort( &cl->netchan.remoteAddress, addr_port ); } if( SV_ProcessPacket( &cl->netchan, &msg ) ) // this is a valid, sequenced packet, so process it { cl->lastPacketReceivedTime = svs.realtime; SV_ParseClientMessage( cl, &msg ); } break; } } } // handle clients with individual sockets for( i = 0; i < sv_maxclients->integer; i++ ) { cl = &svs.clients[i]; if( cl->state == CS_ZOMBIE || cl->state == CS_FREE ) continue; if( !cl->individual_socket ) continue; // not while, we only handle one packet per client at a time here if( ( ret = NET_GetPacket( cl->netchan.socket, &address, &msg ) ) != 0 ) { if( ret == -1 ) { Com_Printf( "Error receiving packet from %s: %s\n", NET_AddressToString( &cl->netchan.remoteAddress ), NET_ErrorString() ); if( cl->reliable ) SV_DropClient( cl, DROP_TYPE_GENERAL, "Error receiving packet: %s", NET_ErrorString() ); } else { if( SV_ProcessPacket( &cl->netchan, &msg ) ) { // this is a valid, sequenced packet, so process it cl->lastPacketReceivedTime = svs.realtime; SV_ParseClientMessage( cl, &msg ); } } } } }
/* * TV_Downstream_CheckTimeouts */ void TV_Downstream_CheckTimeouts( void ) { client_t *client; int i; #ifdef TCP_SUPPORT // timeout incoming upstreams for( i = 0; i < MAX_INCOMING_CONNECTIONS; i++ ) { if( tvs.incoming[i].active && tvs.incoming[i].time + 1000 * 15 < tvs.realtime ) { Com_Printf( "Incoming TCP upstream from %s timed out\n", NET_AddressToString( &tvs.incoming[i].address ) ); NET_CloseSocket( &tvs.incoming[i].socket ); tvs.incoming[i].active = qfalse; } } #endif // timeout clients for( i = 0, client = tvs.clients; i < tv_maxclients->integer; i++, client++ ) { // message times may be wrong across a changelevel if( client->lastPacketReceivedTime > tvs.realtime ) client->lastPacketReceivedTime = tvs.realtime; if( client->state == CS_ZOMBIE && client->lastPacketReceivedTime + 1000 * tv_zombietime->value < tvs.realtime ) { client->state = CS_FREE; // can now be reused if( client->individual_socket ) NET_CloseSocket( &client->socket ); continue; } if( ( client->state != CS_FREE && client->state != CS_ZOMBIE ) && ( client->lastPacketReceivedTime + 1000 * tv_timeout->value < tvs.realtime ) ) { TV_Downstream_DropClient( client, DROP_TYPE_GENERAL, "Upstream timed out" ); client->state = CS_FREE; // don't bother with zombie state if( client->socket.open ) NET_CloseSocket( &client->socket ); } // timeout downloads left open if( ( client->state != CS_FREE && client->state != CS_ZOMBIE ) && ( client->download.name && client->download.timeout < tvs.realtime ) ) { Com_Printf( "Download of %s to %s" S_COLOR_WHITE " timed out\n", client->download.name, client->name ); if( client->download.data ) { FS_FreeBaseFile( client->download.data ); client->download.data = NULL; } Mem_ZoneFree( client->download.name ); client->download.name = NULL; client->download.size = 0; client->download.timeout = 0; } } }
/* * TV_Downstream_ReadPackets */ void TV_Downstream_ReadPackets( void ) { int i, socketind, ret, game_port; client_t *cl; #ifdef TCP_SUPPORT socket_t newsocket; #endif socket_t *socket; netadr_t address; msg_t msg; qbyte msgData[MAX_MSGLEN]; socket_t* sockets [] = { &tvs.socket_udp, &tvs.socket_udp6, }; #ifdef TCP_SUPPORT if( tvs.socket_tcp.open ) { while( qtrue ) { // find a free slot for( i = 0; i < MAX_INCOMING_CONNECTIONS; i++ ) { if( !tvs.incoming[i].active ) break; } if( i == MAX_INCOMING_CONNECTIONS ) break; if( ( ret = NET_Accept( &tvs.socket_tcp, &newsocket, &address ) ) == 0 ) break; if( ret == -1 ) { Com_Printf( "NET_Accept: Error: %s\n", NET_ErrorString() ); continue; } tvs.incoming[i].active = qtrue; tvs.incoming[i].socket = newsocket; tvs.incoming[i].address = address; tvs.incoming[i].time = tvs.realtime; } } for( i = 0; i < MAX_INCOMING_CONNECTIONS; i++ ) { if( !tvs.incoming[i].active ) continue; ret = NET_GetPacket( &tvs.incoming[i].socket, &address, &msg ); if( ret == -1 ) { NET_CloseSocket( &tvs.incoming[i].socket ); tvs.incoming[i].active = qfalse; } else if( ret == 1 ) { if( *(int *)msg.data != -1 ) { // sequence packet without upstreams NET_CloseSocket( &tvs.incoming[i].socket ); tvs.incoming[i].active = qfalse; continue; } TV_Downstream_UpstreamlessPacket( &tvs.incoming[i].socket, &address, &msg ); } } #endif MSG_Init( &msg, msgData, sizeof( msgData ) ); for( socketind = 0; socketind < sizeof( sockets ) / sizeof( sockets[0] ); socketind++ ) { socket = sockets[socketind]; while( socket->open && ( ret = NET_GetPacket( socket, &address, &msg ) ) != 0 ) { if( ret == -1 ) { Com_Printf( "NET_GetPacket: Error: %s\n", NET_ErrorString() ); continue; } // check for upstreamless packet (0xffffffff) first if( *(int *)msg.data == -1 ) { TV_Downstream_UpstreamlessPacket( socket, &address, &msg ); continue; } // read the game port out of the message so we can fix up // stupid address translating routers MSG_BeginReading( &msg ); MSG_ReadLong( &msg ); // sequence number MSG_ReadLong( &msg ); // sequence number game_port = MSG_ReadShort( &msg ) & 0xffff; // data follows // check for packets from connected clients for( i = 0, cl = tvs.clients; i < tv_maxclients->integer; i++, cl++ ) { unsigned short remoteaddr_port, addr_port; if( cl->state == CS_FREE || cl->state == CS_ZOMBIE ) continue; if( !NET_CompareBaseAddress( &address, &cl->netchan.remoteAddress ) ) continue; if( cl->netchan.game_port != game_port ) continue; remoteaddr_port = NET_GetAddressPort( &cl->netchan.remoteAddress ); addr_port = NET_GetAddressPort( &address ); if( remoteaddr_port != addr_port ) { Com_DPrintf( "%s" S_COLOR_WHITE ": Fixing up a translated port from %i to %i\n", cl->name, remoteaddr_port, addr_port ); NET_SetAddressPort( &cl->netchan.remoteAddress, addr_port ); } if( TV_Downstream_ProcessPacket( &cl->netchan, &msg ) ) { // this is a valid, sequenced packet, so process it cl->lastPacketReceivedTime = tvs.realtime; TV_Downstream_ParseClientMessage( cl, &msg ); } break; } } } // handle clients with individual sockets for( i = 0; i < tv_maxclients->integer; i++ ) { cl = &tvs.clients[i]; if( cl->state == CS_ZOMBIE || cl->state == CS_FREE ) continue; if( !cl->individual_socket ) continue; // not while, we only handle one packet per client at a time here if( ( ret = NET_GetPacket( cl->netchan.socket, &address, &msg ) ) != 0 ) { if( ret == -1 ) { Com_Printf( "%s" S_COLOR_WHITE ": Error receiving packet: %s\n", cl->name, NET_ErrorString() ); if( cl->reliable ) TV_Downstream_DropClient( cl, DROP_TYPE_GENERAL, "Error receiving packet: %s", NET_ErrorString() ); } else { if( *(int *)msg.data == -1 ) { TV_Downstream_UpstreamlessPacket( cl->netchan.socket, &address, &msg ); } else { if( TV_Downstream_ProcessPacket( &cl->netchan, &msg ) ) { cl->lastPacketReceivedTime = tvs.realtime; TV_Downstream_ParseClientMessage( cl, &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; }