/* ================== SV_FinalMessage Used by SV_Shutdown to send a final message to all connected clients before the server goes down. The messages are sent immediately, not just stuck on the outgoing message list, because the server is going to totally exit after returning from this function. ================== */ void SV_FinalMessage( char *message, qboolean reconnect ) { sv_client_t *cl; byte msg_buf[1024]; sizebuf_t msg; int i; BF_Init( &msg, "FinalMessage", msg_buf, sizeof( msg_buf )); BF_WriteByte( &msg, svc_print ); BF_WriteByte( &msg, PRINT_HIGH ); BF_WriteString( &msg, va( "%s\n", message )); if( reconnect ) { BF_WriteByte( &msg, svc_changing ); if( sv.loadgame || sv_maxclients->integer > 1 || sv.changelevel ) BF_WriteOneBit( &msg, 1 ); // changelevel else BF_WriteOneBit( &msg, 0 ); } else { BF_WriteByte( &msg, svc_disconnect ); } // send it twice // stagger the packets to crutch operating system limited buffers for( i = 0, cl = svs.clients; i < sv_maxclients->integer; i++, cl++ ) if( cl->state >= cs_connected && !cl->fakeclient ) Netchan_Transmit( &cl->netchan, BF_GetNumBytesWritten( &msg ), BF_GetData( &msg )); for( i = 0, cl = svs.clients; i < sv_maxclients->integer; i++, cl++ ) if( cl->state >= cs_connected && !cl->fakeclient ) Netchan_Transmit( &cl->netchan, BF_GetNumBytesWritten( &msg ), BF_GetData( &msg )); }
/* ============= SV_EmitPings ============= */ void SV_EmitPings( sizebuf_t *msg ) { sv_client_t *cl; int packet_loss; int i, ping; BF_WriteByte( msg, svc_updatepings ); for( i = 0, cl = svs.clients; i < sv_maxclients->integer; i++, cl++ ) { if( cl->state != cs_spawned ) continue; SV_GetPlayerStats( cl, &ping, &packet_loss ); // there are 25 bits for each client BF_WriteOneBit( msg, 1 ); BF_WriteUBitLong( msg, i, MAX_CLIENT_BITS ); BF_WriteUBitLong( msg, ping, 12 ); BF_WriteUBitLong( msg, packet_loss, 7 ); } // end marker BF_WriteOneBit( msg, 0 ); }
/* ================== MSG_WriteWeaponData Writes current client data only for local client Other clients can grab the client state from entity_state_t ================== */ void MSG_WriteWeaponData( sizebuf_t *msg, weapon_data_t *from, weapon_data_t *to, float timebase, int index ) { delta_t *pField; delta_info_t *dt; int i, startBit; int numChanges = 0; dt = Delta_FindStruct( "weapon_data_t" ); if( !dt || !dt->bInitialized ) { Host_Error( "MSG_WriteWeaponData: delta not initialized!\n" ); } pField = dt->pFields; ASSERT( pField ); // activate fields and call custom encode func Delta_CustomEncode( dt, from, to ); startBit = msg->iCurBit; BF_WriteOneBit( msg, 1 ); BF_WriteUBitLong( msg, index, MAX_WEAPON_BITS ); // process fields for( i = 0; i < dt->numFields; i++, pField++ ) { if( Delta_WriteField( msg, pField, from, to, timebase )) numChanges++; } // if we have no changes - kill the message if( !numChanges ) BF_SeekToBit( msg, startBit ); }
void Delta_WriteTableField( sizebuf_t *msg, int tableIndex, const delta_t *pField ) { int nameIndex; delta_info_t *dt; ASSERT( pField ); if( !pField->name || !*pField->name ) return; // not initialized ? dt = Delta_FindStructByIndex( tableIndex ); ASSERT( dt && dt->bInitialized ); nameIndex = Delta_IndexForFieldInfo( dt->pInfo, pField->name ); ASSERT( nameIndex >= 0 && nameIndex < dt->maxFields ); BF_WriteByte( msg, svc_deltatable ); BF_WriteUBitLong( msg, tableIndex, 4 ); // assume we support 16 network tables BF_WriteUBitLong( msg, nameIndex, 8 ); // 255 fields by struct should be enough BF_WriteUBitLong( msg, pField->flags, 10 ); // flags are indicated various input types BF_WriteUBitLong( msg, pField->bits - 1, 5 ); // max received value is 32 (32 bit) // multipliers is null-compressed if( pField->multiplier != 1.0f ) { BF_WriteOneBit( msg, 1 ); BF_WriteFloat( msg, pField->multiplier ); } else BF_WriteOneBit( msg, 0 ); if( pField->post_multiplier != 1.0f ) { BF_WriteOneBit( msg, 1 ); BF_WriteFloat( msg, pField->post_multiplier ); } else BF_WriteOneBit( msg, 0 ); }
/* ================== SV_WriteClientdataToMessage ================== */ void SV_WriteClientdataToMessage( sv_client_t *cl, sizebuf_t *msg ) { clientdata_t nullcd; clientdata_t *from_cd, *to_cd; weapon_data_t nullwd; weapon_data_t *from_wd, *to_wd; client_frame_t *frame; edict_t *clent; int i; Q_memset( &nullcd, 0, sizeof( nullcd )); clent = cl->edict; frame = &cl->frames[cl->netchan.outgoing_sequence & SV_UPDATE_MASK]; frame->senttime = host.realtime; frame->raw_ping = -1.0f; frame->latency = -1.0f; if( cl->chokecount != 0 ) { BF_WriteByte( msg, svc_chokecount ); BF_WriteByte( msg, cl->chokecount ); cl->chokecount = 0; } // update client fixangle switch( clent->v.fixangle ) { case 1: BF_WriteByte( msg, svc_setangle ); BF_WriteBitAngle( msg, clent->v.angles[0], 16 ); BF_WriteBitAngle( msg, clent->v.angles[1], 16 ); BF_WriteBitAngle( msg, clent->v.angles[2], 16 ); clent->v.effects |= EF_NOINTERP; break; case 2: BF_WriteByte( msg, svc_addangle ); BF_WriteBitAngle( msg, clent->v.avelocity[1], 16 ); clent->v.avelocity[1] = 0.0f; break; } clent->v.fixangle = 0; // reset fixangle clent->v.pushmsec = 0; // reset net framenum Q_memset( &frame->clientdata, 0, sizeof( frame->clientdata )); // update clientdata_t svgame.dllFuncs.pfnUpdateClientData( clent, cl->local_weapons, &frame->clientdata ); BF_WriteByte( msg, svc_clientdata ); if( cl->hltv_proxy ) return; // don't send more nothing if( cl->delta_sequence == -1 ) from_cd = &nullcd; else from_cd = &cl->frames[cl->delta_sequence & SV_UPDATE_MASK].clientdata; to_cd = &frame->clientdata; if( cl->delta_sequence == -1 ) { BF_WriteOneBit( msg, 0 ); // no delta-compression } else { BF_WriteOneBit( msg, 1 ); // we are delta-ing from BF_WriteByte( msg, cl->delta_sequence ); } // write clientdata_t MSG_WriteClientData( msg, from_cd, to_cd, sv.time ); if( cl->local_weapons && svgame.dllFuncs.pfnGetWeaponData( clent, frame->weapondata )) { Q_memset( &nullwd, 0, sizeof( nullwd )); for( i = 0; i < 64; i++ ) { if( cl->delta_sequence == -1 ) from_wd = &nullwd; else from_wd = &cl->frames[cl->delta_sequence & SV_UPDATE_MASK].weapondata[i]; to_wd = &frame->weapondata[i]; MSG_WriteWeaponData( msg, from_wd, to_wd, sv.time, i ); } } // end marker BF_WriteOneBit( msg, 0 ); }
/* ============= SV_EmitEvents ============= */ static void SV_EmitEvents( sv_client_t *cl, client_frame_t *to, sizebuf_t *msg ) { event_state_t *es; event_info_t *info; entity_state_t *state; event_args_t nullargs; int ev_count = 0; int count, ent_index; int i, j, ev; Q_memset( &nullargs, 0, sizeof( nullargs )); es = &cl->events; // count events for( ev = 0; ev < MAX_EVENT_QUEUE; ev++ ) { if( es->ei[ev].index ) ev_count++; } // nothing to send if( !ev_count ) return; // nothing to send if( ev_count >= 31 ) ev_count = 31; for( i = 0; i < MAX_EVENT_QUEUE; i++ ) { info = &es->ei[i]; if( info->index == 0 ) continue; ent_index = info->entity_index; for( j = 0; j < to->num_entities; j++ ) { state = &svs.packet_entities[(to->first_entity+j)%svs.num_client_entities]; if( state->number == ent_index ) break; } if( j >= to->num_entities ) { // couldn't find info->packet_index = to->num_entities; info->args.entindex = ent_index; } else { info->packet_index = j; info->args.ducking = 0; if(!( info->args.flags & FEVENT_ORIGIN )) VectorClear( info->args.origin ); if(!( info->args.flags & FEVENT_ANGLES )) VectorClear( info->args.angles ); VectorClear( info->args.velocity ); } } BF_WriteByte( msg, svc_event ); // create message BF_WriteUBitLong( msg, ev_count, 5 ); // up to MAX_EVENT_QUEUE events for( count = i = 0; i < MAX_EVENT_QUEUE; i++ ) { info = &es->ei[i]; if( info->index == 0 ) { info->packet_index = -1; info->entity_index = -1; continue; } // only send if there's room if( count < ev_count ) { BF_WriteUBitLong( msg, info->index, MAX_EVENT_BITS ); // 1024 events if( info->packet_index == -1 ) { BF_WriteOneBit( msg, 0 ); } else { BF_WriteOneBit( msg, 1 ); BF_WriteUBitLong( msg, info->packet_index, MAX_ENTITY_BITS ); if( !Q_memcmp( &nullargs, &info->args, sizeof( event_args_t ))) { BF_WriteOneBit( msg, 0 ); } else { BF_WriteOneBit( msg, 1 ); MSG_WriteDeltaEvent( msg, &nullargs, &info->args ); } } if( info->fire_time ) { BF_WriteOneBit( msg, 1 ); BF_WriteWord( msg, Q_rint( info->fire_time * 100.0f )); } else BF_WriteOneBit( msg, 0 ); } info->index = 0; info->packet_index = -1; info->entity_index = -1; count++; } }
/* ================== MSG_WriteDeltaEntity Writes part of a packetentities message, including the entity number. Can delta from either a baseline or a previous packet_entity If to is NULL, a remove entity update will be sent If force is not set, then nothing at all will be generated if the entity is identical, under the assumption that the in-order delta code will catch it. ================== */ void MSG_WriteDeltaEntity( entity_state_t *from, entity_state_t *to, sizebuf_t *msg, qboolean force, qboolean player, float timebase ) { delta_info_t *dt = NULL; delta_t *pField; int i, startBit; int numChanges = 0; if( to == NULL ) { int fRemoveType; if( from == NULL ) return; // a NULL to is a delta remove message BF_WriteWord( msg, from->number ); // fRemoveType: // 0 - keep alive, has delta-update // 1 - remove from delta message (but keep states) // 2 - completely remove from server if( force ) fRemoveType = 2; else fRemoveType = 1; BF_WriteUBitLong( msg, fRemoveType, 2 ); return; } startBit = msg->iCurBit; if( to->number < 0 || to->number >= GI->max_edicts ) { MsgDev( D_ERROR, "MSG_WriteDeltaEntity: Bad entity number: %i\n", to->number ); return; } BF_WriteWord( msg, to->number ); BF_WriteUBitLong( msg, 0, 2 ); // alive if( to->entityType != from->entityType ) { BF_WriteOneBit( msg, 1 ); BF_WriteUBitLong( msg, to->entityType, 2 ); } else BF_WriteOneBit( msg, 0 ); if( to->entityType == ENTITY_NORMAL ) { if( player ) { dt = Delta_FindStruct( "entity_state_player_t" ); } else { dt = Delta_FindStruct( "entity_state_t" ); } } else if( to->entityType == ENTITY_BEAM ) { dt = Delta_FindStruct( "custom_entity_state_t" ); } ASSERT( dt && dt->bInitialized ); pField = dt->pFields; ASSERT( pField ); // activate fields and call custom encode func Delta_CustomEncode( dt, from, to ); // process fields for( i = 0; i < dt->numFields; i++, pField++ ) { if( Delta_WriteField( msg, pField, from, to, timebase )) numChanges++; } // if we have no changes - kill the message if( !numChanges && !force ) BF_SeekToBit( msg, startBit ); }
/* ===================== Delta_WriteField write fields by offsets assume from and to is valid ===================== */ qboolean Delta_WriteField( sizebuf_t *msg, delta_t *pField, void *from, void *to, float timebase ) { qboolean bSigned = ( pField->flags & DT_SIGNED ) ? true : false; float flValue, flAngle, flTime; uint iValue; const char *pStr; if( Delta_CompareField( pField, from, to, timebase )) { BF_WriteOneBit( msg, 0 ); // unchanged return false; } BF_WriteOneBit( msg, 1 ); // changed if( pField->flags & DT_BYTE ) { iValue = *(byte *)((byte *)to + pField->offset ); iValue = Delta_ClampIntegerField( iValue, bSigned, pField->bits ); if ( pField->multiplier != 1.0f ) iValue *= pField->multiplier; BF_WriteBitLong( msg, iValue, pField->bits, bSigned ); } else if( pField->flags & DT_SHORT ) { iValue = *(word *)((byte *)to + pField->offset ); iValue = Delta_ClampIntegerField( iValue, bSigned, pField->bits ); if ( pField->multiplier != 1.0f ) iValue *= pField->multiplier; BF_WriteBitLong( msg, iValue, pField->bits, bSigned ); } else if( pField->flags & DT_INTEGER ) { iValue = *(uint *)((byte *)to + pField->offset ); iValue = Delta_ClampIntegerField( iValue, bSigned, pField->bits ); if ( pField->multiplier != 1.0f ) iValue *= pField->multiplier; BF_WriteBitLong( msg, iValue, pField->bits, bSigned ); } else if( pField->flags & DT_FLOAT ) { #ifdef __arm__ memcpy(&flValue,(byte *)to + pField->offset, sizeof(float) ); #else flValue = *(float *)((byte *)to + pField->offset ); #endif iValue = (int)(flValue * pField->multiplier); BF_WriteBitLong( msg, iValue, pField->bits, bSigned ); } else if( pField->flags & DT_ANGLE ) { #ifdef __arm__ memcpy(&flAngle,(byte *)to + pField->offset, sizeof(float) ); #else flAngle = *(float *)((byte *)to + pField->offset ); #endif // NOTE: never applies multipliers to angle because // result may be wrong on client-side BF_WriteBitAngle( msg, flAngle, pField->bits ); } else if( pField->flags & DT_TIMEWINDOW_8 ) { #ifdef __arm__ memcpy(&flValue,(byte *)to + pField->offset, sizeof(float) ); #else flValue = *(float *)((byte *)to + pField->offset ); #endif flTime = (timebase * 100.0f) - (flValue * 100.0f); #if 1 iValue = (uint)fabs( flTime ); #else iValue = (uint)fabs( flTime ); if (flTime<0.0f) { iValue |= 0x80000000; } #endif BF_WriteBitLong( msg, iValue, pField->bits, bSigned ); } else if( pField->flags & DT_TIMEWINDOW_BIG ) { #ifdef __arm__ memcpy(&flValue,(byte *)to + pField->offset, sizeof(float) ); #else flValue = *(float *)((byte *)to + pField->offset ); #endif flTime = (timebase * pField->multiplier) - (flValue * pField->multiplier); #if 1 iValue = (uint)fabs( flTime ); #else iValue = (uint)fabs( flTime ); if (flTime<0.0f) { iValue |= 0x80000000; } #endif BF_WriteBitLong( msg, iValue, pField->bits, bSigned ); } else if( pField->flags & DT_STRING ) { pStr = (char *)((byte *)to + pField->offset ); BF_WriteString( msg, pStr ); } return true; }