static void DoBroadcast ( const CPacket& Packet, const T& sendList ) { // Group players by bitstream version std::multimap < ushort, CPlayer* > groupMap; for ( typename T::const_iterator iter = sendList.begin () ; iter != sendList.end () ; ++iter ) { CPlayer* pPlayer = *iter; MapInsert ( groupMap, pPlayer->GetBitStreamVersion (), pPlayer ); } DoBroadcast( Packet, groupMap ); }
bool CPlayerListPacket::Write ( NetBitStreamInterface& BitStream ) const { // bool - show the "X has joined the game" messages? // [ following repeats <number of players joined> times ] // unsigned char (1) - assigned player id // unsigned char (1) - player nick length // unsigned char (X) - player nick (X = player nick length) // bool - is he dead? // bool - spawned? (following data only if this is TRUE) // unsigned char (1) - model id // unsigned char (1) - team id // bool - in a vehicle? // unsigned short (2) - vehicle id (if vehicle) // unsigned char (1) - vehicle seat (if vehicle) // CVector (12) - position (if player) // float (4) - rotation (if player) // bool - has a jetpack? // unsigned short (2) - dimension // Write the global flags BitStream.WriteBit ( m_bShowInChat ); CPlayer* pPlayer = NULL; // Put each player in our list into the packet list < CPlayer* > ::const_iterator iter = m_List.begin (); for ( ; iter != m_List.end (); ++iter ) { // Grab the real pointer pPlayer = *iter; // Write the player ID ElementID PlayerID = pPlayer->GetID (); BitStream.Write ( PlayerID ); // Time sync context BitStream.Write ( pPlayer->GetSyncTimeContext () ); // Write the nick length const char* szNickPointer = pPlayer->GetNick (); unsigned char ucNickLength = static_cast < unsigned char > ( strlen ( szNickPointer ) ); if ( ucNickLength < MIN_NICK_LENGTH || ucNickLength > MAX_NICK_LENGTH ) { BitStream.Write ( static_cast < unsigned char > ( 3 ) ); BitStream.Write ( "???", 3 ); } else { BitStream.Write ( ucNickLength ); BitStream.Write ( szNickPointer, ucNickLength ); } // Version info if ( BitStream.Version () >= 0x34 ) { BitStream.Write ( pPlayer->GetBitStreamVersion () ); SString strBuild = pPlayer->GetPlayerVersion ().SubStr ( 8 ); uint uiBuildNumber = atoi ( strBuild ); BitStream.Write ( uiBuildNumber ); } // Flags bool bInVehicle = ( pPlayer->GetOccupiedVehicle () != NULL ); BitStream.WriteBit ( pPlayer->IsDead () ); // Currently unused by the client BitStream.WriteBit ( true ); // (Was IsSpawned) Used by the client to determine if extra info was sent (in this packet) BitStream.WriteBit ( bInVehicle ); BitStream.WriteBit ( pPlayer->HasJetPack () ); BitStream.WriteBit ( pPlayer->IsNametagShowing () ); BitStream.WriteBit ( pPlayer->IsNametagColorOverridden () ); BitStream.WriteBit ( pPlayer->IsHeadless() ); BitStream.WriteBit ( pPlayer->IsFrozen() ); // Nametag stuff unsigned char ucNametagTextLength = 0; char* szNametagText = pPlayer->GetNametagText (); if ( szNametagText ) ucNametagTextLength = static_cast < unsigned char > ( strlen ( szNametagText ) ); BitStream.Write ( ucNametagTextLength ); if ( ucNametagTextLength > 0 ) BitStream.Write ( szNametagText, ucNametagTextLength ); // Write nametag color if it's overridden if ( pPlayer->IsNametagColorOverridden () ) { unsigned char ucR, ucG, ucB; pPlayer->GetNametagColor ( ucR, ucG, ucB ); BitStream.Write ( ucR ); BitStream.Write ( ucG ); BitStream.Write ( ucB ); } // Move anim if ( BitStream.Version() > 0x4B ) { uchar ucMoveAnim = pPlayer->GetMoveAnim(); BitStream.Write ( ucMoveAnim ); } // Always send extra info (Was: "Write spawn info if he's spawned") if ( true ) { // Player model ID BitStream.WriteCompressed ( pPlayer->GetModel () ); // Team id CTeam* pTeam = pPlayer->GetTeam (); if ( pTeam ) { BitStream.WriteBit ( true ); BitStream.Write ( pTeam->GetID () ); } else BitStream.WriteBit ( false ); if ( bInVehicle ) { // Grab the occupied vehicle CVehicle* pVehicle = pPlayer->GetOccupiedVehicle (); // Vehicle ID and seat BitStream.Write ( pVehicle->GetID () ); SOccupiedSeatSync seat; seat.data.ucSeat = pPlayer->GetOccupiedVehicleSeat (); BitStream.Write ( &seat ); } else { // Player position SPositionSync position ( false ); position.data.vecPosition = pPlayer->GetPosition (); BitStream.Write ( &position ); // Player rotation SPedRotationSync rotation; rotation.data.fRotation = pPlayer->GetRotation (); BitStream.Write ( &rotation ); } BitStream.WriteCompressed ( pPlayer->GetDimension () ); BitStream.Write ( pPlayer->GetFightingStyle () ); SEntityAlphaSync alpha; alpha.data.ucAlpha = pPlayer->GetAlpha (); BitStream.Write ( &alpha ); BitStream.Write ( pPlayer->GetInterior () ); // Write the weapons of the player weapon slots for ( unsigned int i = 0; i < 16; ++i ) { CWeapon* pWeapon = pPlayer->GetWeapon ( i ); if ( pWeapon && pWeapon->ucType != 0 ) { BitStream.WriteBit ( true ); SWeaponTypeSync weaponType; weaponType.data.ucWeaponType = pWeapon->ucType; BitStream.Write ( &weaponType ); } else BitStream.WriteBit ( false ); } } } return true; }
// Send one packet to a list of players, grouped by bitstream version static void DoBroadcast( const CPacket& Packet, const std::multimap < ushort, CPlayer* >& groupMap ) { if ( !CNetBufferWatchDog::CanSendPacket ( Packet.GetPacketID () ) ) return; // Use the flags to determine how to send it NetServerPacketReliability Reliability; unsigned long ulFlags = Packet.GetFlags (); if ( ulFlags & PACKET_RELIABLE ) { if ( ulFlags & PACKET_SEQUENCED ) { Reliability = PACKET_RELIABILITY_RELIABLE_ORDERED; } else { Reliability = PACKET_RELIABILITY_RELIABLE; } } else { if ( ulFlags & PACKET_SEQUENCED ) { Reliability = PACKET_RELIABILITY_UNRELIABLE_SEQUENCED; } else { Reliability = PACKET_RELIABILITY_UNRELIABLE; } } NetServerPacketPriority packetPriority = PACKET_PRIORITY_MEDIUM; if ( ulFlags & PACKET_HIGH_PRIORITY ) { packetPriority = PACKET_PRIORITY_HIGH; } else if ( ulFlags & PACKET_LOW_PRIORITY ) { packetPriority = PACKET_PRIORITY_LOW; } // For each bitstream version, make and send a packet typedef std::multimap < ushort, CPlayer* > ::const_iterator mapIter; mapIter m_it, s_it; for ( m_it = groupMap.begin () ; m_it != groupMap.end () ; m_it = s_it ) { ushort usBitStreamVersion = (*m_it).first; // Allocate a bitstream NetBitStreamInterface* pBitStream = g_pNetServer->AllocateNetServerBitStream ( usBitStreamVersion ); // Write the content if ( Packet.Write ( *pBitStream ) ) { g_pGame->SendPacketBatchBegin ( Packet.GetPacketID (), pBitStream ); // For each player, send the packet const pair < mapIter , mapIter > keyRange = groupMap.equal_range ( usBitStreamVersion ); for ( s_it = keyRange.first ; s_it != keyRange.second ; ++s_it ) { CPlayer* pPlayer = s_it->second; dassert ( usBitStreamVersion == pPlayer->GetBitStreamVersion () ); g_pGame->SendPacket ( Packet.GetPacketID (), pPlayer->GetSocket (), pBitStream, FALSE, packetPriority, Reliability, Packet.GetPacketOrdering() ); } g_pGame->SendPacketBatchEnd (); } else { // Skip const pair < mapIter , mapIter > keyRange = groupMap.equal_range ( usBitStreamVersion ); for ( s_it = keyRange.first ; s_it != keyRange.second ; ++s_it ) {} } // Destroy the bitstream g_pNetServer->DeallocateNetServerBitStream ( pBitStream ); } }