bool CPickup::CanUse ( CPlayer& Player, bool bOnfootCheck ) { // Is the player on foot? if ( !bOnfootCheck || !Player.GetOccupiedVehicle () ) { // Check if he can pick it up depending on the type switch ( m_ucType ) { case CPickup::HEALTH: // TODO: calc max health from max_health stat return ( Player.GetHealth () < 200.0f ); case CPickup::ARMOR: return ( Player.GetArmor () < 100.0f ); case CPickup::WEAPON: return true; case CPickup::CUSTOM: return true; } } return false; }
bool CLightsyncPacket::Write(NetBitStreamInterface& BitStream) const { bool bSyncPosition; if (Count() == 0) return false; for (std::vector<CPlayer*>::const_iterator iter = m_players.begin(); iter != m_players.end(); ++iter) { CPlayer* pPlayer = *iter; CPlayer::SLightweightSyncData& data = pPlayer->GetLightweightSyncData(); CVehicle* pVehicle = pPlayer->GetOccupiedVehicle(); // Find the difference between now and the time the position last changed for the player long long llTicksDifference = GetTickCount64_() - pPlayer->GetPositionLastChanged(); // Right we need to sync the position if there is no vehicle or he's in a vehicle and the difference between setPosition is less than or equal to the // slow sync rate i.e. make sure his position has been updated more than 0.001f in the last 1500ms plus a small margin for error (probably not needed). // This will ensure we only send positions when the position has changed. bSyncPosition = (!pVehicle || pPlayer->GetOccupiedVehicleSeat() == 0) && llTicksDifference <= g_TickRateSettings.iLightSync + 100; BitStream.Write(pPlayer->GetID()); BitStream.Write((unsigned char)pPlayer->GetSyncTimeContext()); unsigned short usLatency = pPlayer->GetPing(); BitStream.WriteCompressed(usLatency); BitStream.WriteBit(data.health.bSync); if (data.health.bSync) { SPlayerHealthSync health; health.data.fValue = pPlayer->GetHealth(); BitStream.Write(&health); SPlayerArmorSync armor; armor.data.fValue = pPlayer->GetArmor(); BitStream.Write(&armor); } BitStream.WriteBit(bSyncPosition); if (bSyncPosition) { SLowPrecisionPositionSync pos; pos.data.vecPosition = pPlayer->GetPosition(); BitStream.Write(&pos); bool bSyncVehicleHealth = data.vehicleHealth.bSync && pVehicle; BitStream.WriteBit(bSyncVehicleHealth); if (bSyncVehicleHealth) { SLowPrecisionVehicleHealthSync health; health.data.fValue = pVehicle->GetHealth(); BitStream.Write(&health); } } } return true; }
void CLightsyncManager::DoPulse () { // The point of this method is to get all players that should receive right now the lightweight // sync from all the other players. To make this efficient, we are using a queue in what the // players at the front have are more inminent to receive this sync. As all players have the same // far sync rate, just taking the players from the front, processing them and pushing them to the // back will keep the order of priority to be processed. // // We also want to perform delta sync on stuff like the health, but it would require to know the last // value synced from every player to every player, because we are getting players from the front and // sending them all the other players data. This would be crazily inefficient and would require a lot // of mainteinance, so we will leave special entries in the queue to mark in what moment a player // health changed. This way, we will force the health sync for that player until the queue cycle // reaches that special entry again. As there might be multiple changes in a complete queue cycle, we // are also storing the delta context in what it happened, so we only consider the health unchanged // when we find the marker with the same context value. if ( g_pBandwidthSettings->bLightSyncEnabled == false ) return; // For counting stats long iPacketsSent = 0; long iBitsSent = 0; // For limiting light sync processing long iLimitCounter = Max < uint > ( 10, g_pGame->GetPlayerManager ()->Count () / 25 ); int iLightsyncRate = g_TickRateSettings.iLightSync; long long llTickCountNow = GetTickCount64_ (); while ( m_Queue.size() > 0 && m_Queue.front().ullTime + iLightsyncRate <= llTickCountNow && iLimitCounter > 0 ) { SEntry entry = m_Queue.front (); m_Queue.pop_front (); CPlayer* pPlayer = entry.pPlayer; CPlayer::SLightweightSyncData& data = pPlayer->GetLightweightSyncData (); switch ( entry.eType ) { case SYNC_PLAYER: { CLightsyncPacket packet; // Use this players far list const SViewerMapType& farList = pPlayer->GetFarPlayerList (); // For each far player for ( SViewerMapType ::const_iterator it = farList.begin (); it != farList.end (); ++it ) { CPlayer* pCurrent = it->first; dassert ( pPlayer != pCurrent ); // Only send if he isn't network troubled. if ( pCurrent->UhOhNetworkTrouble ( ) == false ) { CPlayer::SLightweightSyncData& currentData = pCurrent->GetLightweightSyncData (); packet.AddPlayer ( pCurrent ); // Calculate the delta sync if ( fabs(currentData.health.fLastHealth - pCurrent->GetHealth()) > LIGHTSYNC_HEALTH_THRESHOLD || fabs(currentData.health.fLastArmor - pCurrent->GetArmor()) > LIGHTSYNC_HEALTH_THRESHOLD ) { currentData.health.fLastHealth = pCurrent->GetHealth (); currentData.health.fLastArmor = pCurrent->GetArmor (); currentData.health.bSync = true; currentData.health.uiContext++; // Generate the health marker SEntry marker; marker.ullTime = 0; marker.pPlayer = pCurrent; marker.eType = DELTA_MARKER_HEALTH; marker.uiContext = currentData.health.uiContext; m_Queue.push_back ( marker ); } CVehicle* pVehicle = pCurrent->GetOccupiedVehicle (); if ( pVehicle && pCurrent->GetOccupiedVehicleSeat() == 0 ) { if ( currentData.vehicleHealth.lastVehicle != pVehicle || fabs(currentData.vehicleHealth.fLastHealth - pVehicle->GetHealth ()) > LIGHTSYNC_VEHICLE_HEALTH_THRESHOLD ) { currentData.vehicleHealth.fLastHealth = pVehicle->GetHealth (); currentData.vehicleHealth.lastVehicle = pVehicle; currentData.vehicleHealth.bSync = true; currentData.vehicleHealth.uiContext++; // Generate the vehicle health marker SEntry marker; marker.ullTime = 0; marker.pPlayer = pCurrent; marker.eType = DELTA_MARKER_VEHICLE_HEALTH; marker.uiContext = currentData.vehicleHealth.uiContext; m_Queue.push_back ( marker ); } } if ( packet.Count () == LIGHTSYNC_MAX_PLAYERS ) { iBitsSent += pPlayer->Send ( packet ); iPacketsSent++; packet.Reset (); } } } if ( packet.Count () > 0 ) { iBitsSent += pPlayer->Send ( packet ); iPacketsSent++; } RegisterPlayer ( pPlayer ); iLimitCounter--; break; } case DELTA_MARKER_HEALTH: { if ( data.health.uiContext == entry.uiContext ) data.health.bSync = false; break; } case DELTA_MARKER_VEHICLE_HEALTH: { if ( data.vehicleHealth.uiContext == entry.uiContext ) data.vehicleHealth.bSync = false; break; } } } // Update stats g_pStats->lightsync.llLightSyncPacketsSent += iPacketsSent; g_pStats->lightsync.llLightSyncBytesSent += iBitsSent / 8; // Subtract lightsync usage from skipped accumulators g_pStats->lightsync.llSyncPacketsSkipped -= iPacketsSent; g_pStats->lightsync.llSyncBytesSkipped -= iBitsSent / 8; }
bool CVehiclePuresyncPacket::Write ( NetBitStreamInterface& BitStream ) const { // Got a player to send? if ( m_pSourceElement ) { CPlayer * pSourcePlayer = static_cast < CPlayer * > ( m_pSourceElement ); // Player is in a vehicle and is the driver? CVehicle* pVehicle = pSourcePlayer->GetOccupiedVehicle (); if ( pVehicle ) { // Player ID ElementID PlayerID = pSourcePlayer->GetID (); BitStream.Write ( PlayerID ); // Write the time context of that player BitStream.Write ( pSourcePlayer->GetSyncTimeContext () ); // Write his ping divided with 2 plus a small number so the client can find out when this packet was sent unsigned short usLatency = pSourcePlayer->GetPing (); BitStream.WriteCompressed ( usLatency ); // Write the keysync data CControllerState ControllerState = pSourcePlayer->GetPad ()->GetCurrentControllerState (); WriteFullKeysync ( ControllerState, BitStream ); // Write the vehicle matrix only if he's the driver CVector vecTemp; unsigned int uiSeat = pSourcePlayer->GetOccupiedVehicleSeat (); if ( uiSeat == 0 ) { // Vehicle position SPositionSync position ( false ); position.data.vecPosition = pVehicle->GetPosition (); BitStream.Write ( &position ); // Vehicle rotation SRotationDegreesSync rotation; pVehicle->GetRotationDegrees ( rotation.data.vecRotation ); BitStream.Write ( &rotation ); // Move speed vector SVelocitySync velocity; velocity.data.vecVelocity = pVehicle->GetVelocity (); BitStream.Write ( &velocity ); // Turn speed vector SVelocitySync turnSpeed; turnSpeed.data.vecVelocity = pVehicle->GetTurnSpeed (); BitStream.Write ( &turnSpeed ); // Health SVehicleHealthSync health; health.data.fValue = pVehicle->GetHealth (); BitStream.Write ( &health ); } // Player health and armor SPlayerHealthSync health; health.data.fValue = pSourcePlayer->GetHealth (); BitStream.Write ( &health ); SPlayerArmorSync armor; armor.data.fValue = pSourcePlayer->GetArmor (); BitStream.Write ( &armor ); // Weapon unsigned char ucWeaponType = pSourcePlayer->GetWeaponType (); // Flags SVehiclePuresyncFlags flags; flags.data.bIsWearingGoggles = pSourcePlayer->IsWearingGoggles (); flags.data.bIsDoingGangDriveby = pSourcePlayer->IsDoingGangDriveby (); flags.data.bIsSirenOrAlarmActive = pVehicle->IsSirenActive (); flags.data.bIsSmokeTrailEnabled = pVehicle->IsSmokeTrailEnabled (); flags.data.bIsLandingGearDown = pVehicle->IsLandingGearDown (); flags.data.bIsOnGround = pVehicle->IsOnGround (); flags.data.bIsInWater = pVehicle->IsInWater (); flags.data.bIsDerailed = pVehicle->IsDerailed (); flags.data.bIsAircraft = ( pVehicle->GetVehicleType () == VEHICLE_PLANE || pVehicle->GetVehicleType () == VEHICLE_HELI ); flags.data.bHasAWeapon = ( ucWeaponType != 0 ); flags.data.bIsHeliSearchLightVisible = pVehicle->IsHeliSearchLightVisible (); BitStream.Write ( &flags ); // Write the weapon stuff if ( flags.data.bHasAWeapon ) { // Write the weapon slot SWeaponSlotSync slot; slot.data.uiSlot = pSourcePlayer->GetWeaponSlot (); BitStream.Write ( &slot ); if ( flags.data.bIsDoingGangDriveby && CWeaponNames::DoesSlotHaveAmmo ( slot.data.uiSlot ) ) { // Write the ammo states SWeaponAmmoSync ammo ( ucWeaponType, false, true ); ammo.data.usAmmoInClip = pSourcePlayer->GetWeaponAmmoInClip (); BitStream.Write ( &ammo ); // Sync aim data SWeaponAimSync aim ( 0.0f, true ); aim.data.vecOrigin = pSourcePlayer->GetSniperSourceVector (); pSourcePlayer->GetTargettingVector ( aim.data.vecTarget ); aim.data.fArm = pSourcePlayer->GetAimDirection (); BitStream.Write ( &aim ); // Sync driveby direction SDrivebyDirectionSync driveby; driveby.data.ucDirection = pSourcePlayer->GetDriveByDirection (); BitStream.Write ( &driveby ); } } // Vehicle specific data only if he's the driver if ( uiSeat == 0 ) { WriteVehicleSpecific ( pVehicle, BitStream ); } // Write vehicle_look_left and vehicle_look_right control states when // it's an aircraft. if ( flags.data.bIsAircraft ) { BitStream.WriteBit ( ControllerState.LeftShoulder2 != 0 ); BitStream.WriteBit ( ControllerState.RightShoulder2 != 0 ); } // Success return true; } } return false; }
bool CVehiclePuresyncPacket::Read ( NetBitStreamInterface& BitStream ) { // Got a player to read? if ( m_pSourceElement ) { CPlayer * pSourcePlayer = static_cast < CPlayer * > ( m_pSourceElement ); // Player is in a vehicle? CVehicle* pVehicle = pSourcePlayer->GetOccupiedVehicle (); if ( pVehicle ) { // Read out the time context unsigned char ucTimeContext = 0; if ( !BitStream.Read ( ucTimeContext ) ) return false; // Only read this packet if it matches the current time context that // player is in. if ( !pSourcePlayer->CanUpdateSync ( ucTimeContext ) ) { return false; } // Read out the keysync data CControllerState ControllerState; if ( !ReadFullKeysync ( ControllerState, BitStream ) ) return false; // Read out its position SPositionSync position ( false ); if ( !BitStream.Read ( &position ) ) return false; pSourcePlayer->SetPosition ( position.data.vecPosition ); // Jax: don't allow any outdated packets through unsigned char ucSeat; if ( !BitStream.Read ( ucSeat ) ) return false; if ( ucSeat != pSourcePlayer->GetOccupiedVehicleSeat () ) { // Mis-matching seats can happen when we warp into a different one, // which will screw up the whole packet return false; } // Read out the vehicle matrix only if he's the driver unsigned int uiSeat = pSourcePlayer->GetOccupiedVehicleSeat (); if ( uiSeat == 0 ) { // Read out the vehicle rotation in degrees SRotationDegreesSync rotation; if( !BitStream.Read ( &rotation ) ) return false; // Set it pVehicle->SetPosition ( position.data.vecPosition ); pVehicle->SetRotationDegrees ( rotation.data.vecRotation ); // Move speed vector SVelocitySync velocity; if ( !BitStream.Read ( &velocity ) ) return false; pVehicle->SetVelocity ( velocity.data.vecVelocity ); pSourcePlayer->SetVelocity ( velocity.data.vecVelocity ); // Turn speed vector SVelocitySync turnSpeed; if ( !BitStream.Read ( &turnSpeed ) ) return false; pVehicle->SetTurnSpeed ( turnSpeed.data.vecVelocity ); // Health SVehicleHealthSync health; if ( !BitStream.Read ( &health ) ) return false; float fPreviousHealth = pVehicle->GetHealth (); float fHealth = health.data.fValue; // Less than last time? if ( fHealth < fPreviousHealth ) { // Grab the delta health float fDeltaHealth = fPreviousHealth - fHealth; if ( fDeltaHealth > 0.0f ) { // Call the onVehicleDamage event CLuaArguments Arguments; Arguments.PushNumber ( fDeltaHealth ); pVehicle->CallEvent ( "onVehicleDamage", Arguments ); } } pVehicle->SetHealth ( fHealth ); // Trailer chain CVehicle* pTowedByVehicle = pVehicle; CVehicle* pTrailer = NULL; ElementID TrailerID; bool bHasTrailer; if ( !BitStream.ReadBit ( bHasTrailer ) ) return false; while ( bHasTrailer ) { BitStream.ReadCompressed ( TrailerID ); CElement* pElement = CElementIDs::GetElement ( TrailerID ); if ( pElement ) pTrailer = static_cast < CVehicle* > ( pElement ); // Read out the trailer position and rotation SPositionSync trailerPosition ( false ); if ( !BitStream.Read ( &trailerPosition ) ) return false; SRotationDegreesSync trailerRotation; if ( !BitStream.Read ( &trailerRotation ) ) return false; // If we found the trailer if ( pTrailer ) { // Set its position and rotation pTrailer->SetPosition ( trailerPosition.data.vecPosition ); pTrailer->SetRotationDegrees ( trailerRotation.data.vecRotation ); // Is this a new trailer, attached? CVehicle* pCurrentTrailer = pTowedByVehicle->GetTowedVehicle (); if ( pCurrentTrailer != pTrailer ) { // If theres a trailer already attached if ( pCurrentTrailer ) { pTowedByVehicle->SetTowedVehicle ( NULL ); pCurrentTrailer->SetTowedByVehicle ( NULL ); // Tell everyone to detach them CVehicleTrailerPacket AttachPacket ( pTowedByVehicle, pCurrentTrailer, false ); g_pGame->GetPlayerManager ()->BroadcastOnlyJoined ( AttachPacket ); // Execute the attach trailer script function CLuaArguments Arguments; Arguments.PushElement ( pTowedByVehicle ); pCurrentTrailer->CallEvent ( "onTrailerDetach", Arguments ); } // If something else is towing this trailer CVehicle* pCurrentVehicle = pTrailer->GetTowedByVehicle (); if ( pCurrentVehicle ) { pCurrentVehicle->SetTowedVehicle ( NULL ); pTrailer->SetTowedByVehicle ( NULL ); // Tell everyone to detach them CVehicleTrailerPacket AttachPacket ( pCurrentVehicle, pTrailer, false ); g_pGame->GetPlayerManager ()->BroadcastOnlyJoined ( AttachPacket ); // Execute the attach trailer script function CLuaArguments Arguments; Arguments.PushElement ( pCurrentVehicle ); pTrailer->CallEvent ( "onTrailerDetach", Arguments ); } pTowedByVehicle->SetTowedVehicle ( pTrailer ); pTrailer->SetTowedByVehicle ( pTowedByVehicle ); // Execute the attach trailer script function CLuaArguments Arguments; Arguments.PushElement ( pTowedByVehicle ); bool bContinue = pTrailer->CallEvent ( "onTrailerAttach", Arguments ); // Attach or detach trailers depending on the event outcome CVehicleTrailerPacket TrailerPacket ( pTowedByVehicle, pTrailer, bContinue ); g_pGame->GetPlayerManager ()->BroadcastOnlyJoined ( TrailerPacket ); } } else break; pTowedByVehicle = pTrailer; if ( BitStream.ReadBit ( bHasTrailer ) == false ) return false; } // If there was a trailer before CVehicle* pCurrentTrailer = pTowedByVehicle->GetTowedVehicle (); if ( pCurrentTrailer ) { pTowedByVehicle->SetTowedVehicle ( NULL ); pCurrentTrailer->SetTowedByVehicle ( NULL ); // Tell everyone else to detach them CVehicleTrailerPacket AttachPacket ( pTowedByVehicle, pCurrentTrailer, false ); g_pGame->GetPlayerManager ()->BroadcastOnlyJoined ( AttachPacket ); // Execute the detach trailer script function CLuaArguments Arguments; Arguments.PushElement ( pTowedByVehicle ); pCurrentTrailer->CallEvent ( "onTrailerDetach", Arguments ); } } // Player health SPlayerHealthSync health; if ( !BitStream.Read ( &health ) ) return false; float fHealth = health.data.fValue; float fOldHealth = pSourcePlayer->GetHealth (); float fHealthLoss = fOldHealth - fHealth; // Less than last packet's frame? if ( fHealth < fOldHealth && fHealthLoss > 0 ) { // Call the onPlayerDamage event CLuaArguments Arguments; Arguments.PushNil (); Arguments.PushNumber ( false ); Arguments.PushNumber ( false ); Arguments.PushNumber ( fHealthLoss ); pSourcePlayer->CallEvent ( "onPlayerDamage", Arguments ); } pSourcePlayer->SetHealth ( fHealth ); // Armor SPlayerArmorSync armor; if ( !BitStream.Read ( &armor ) ) return false; float fArmor = armor.data.fValue; float fOldArmor = pSourcePlayer->GetArmor (); float fArmorLoss = fOldArmor - fArmor; // Less than last packet's frame? if ( fArmor < fOldArmor && fArmorLoss > 0 ) { // Call the onPlayerDamage event CLuaArguments Arguments; Arguments.PushNil (); Arguments.PushNumber ( false ); Arguments.PushNumber ( false ); Arguments.PushNumber ( fArmorLoss ); pSourcePlayer->CallEvent ( "onPlayerDamage", Arguments ); } pSourcePlayer->SetArmor ( fArmor ); // Flags SVehiclePuresyncFlags flags; if ( !BitStream.Read ( &flags ) ) return false; pSourcePlayer->SetWearingGoggles ( flags.data.bIsWearingGoggles ); pSourcePlayer->SetDoingGangDriveby ( flags.data.bIsDoingGangDriveby ); // Weapon sync if ( flags.data.bHasAWeapon ) { SWeaponSlotSync slot; if ( !BitStream.Read ( &slot ) ) return false; pSourcePlayer->SetWeaponSlot ( slot.data.uiSlot ); if ( flags.data.bIsDoingGangDriveby && CWeaponNames::DoesSlotHaveAmmo ( slot.data.uiSlot ) ) { // Read the ammo states SWeaponAmmoSync ammo ( pSourcePlayer->GetWeaponType (), false, true ); if ( !BitStream.Read ( &ammo ) ) return false; pSourcePlayer->SetWeaponAmmoInClip ( ammo.data.usAmmoInClip ); // Read aim data SWeaponAimSync aim ( pSourcePlayer->GetWeaponRange (), true ); if ( !BitStream.Read ( &aim ) ) return false; pSourcePlayer->SetAimDirection ( aim.data.fArm ); pSourcePlayer->SetSniperSourceVector ( aim.data.vecOrigin ); pSourcePlayer->SetTargettingVector ( aim.data.vecTarget ); // Read the driveby direction SDrivebyDirectionSync driveby; if ( !BitStream.Read ( &driveby ) ) return false; pSourcePlayer->SetDriveByDirection ( driveby.data.ucDirection ); } } else pSourcePlayer->SetWeaponSlot ( 0 ); // Vehicle specific data if he's the driver if ( uiSeat == 0 ) { ReadVehicleSpecific ( pVehicle, BitStream ); // Set vehicle specific stuff if he's the driver pVehicle->SetSirenActive ( flags.data.bIsSirenOrAlarmActive ); pVehicle->SetSmokeTrailEnabled ( flags.data.bIsSmokeTrailEnabled ); pVehicle->SetLandingGearDown ( flags.data.bIsLandingGearDown ); pVehicle->SetOnGround ( flags.data.bIsOnGround ); pVehicle->SetInWater ( flags.data.bIsInWater ); pVehicle->SetDerailed ( flags.data.bIsDerailed ); pVehicle->SetHeliSearchLightVisible ( flags.data.bIsHeliSearchLightVisible ); } // Read the vehicle_look_left and vehicle_look_right control states // if it's an aircraft. if ( flags.data.bIsAircraft ) { ControllerState.LeftShoulder2 = BitStream.ReadBit () * 255; ControllerState.RightShoulder2 = BitStream.ReadBit () * 255; } pSourcePlayer->GetPad ()->NewControllerState ( ControllerState ); // Success return true; } } return false; }
bool CPlayerPuresyncPacket::Write ( NetBitStreamInterface& BitStream ) const { if ( m_pSourceElement ) { CPlayer * pSourcePlayer = static_cast < CPlayer * > ( m_pSourceElement ); ElementID PlayerID = pSourcePlayer->GetID (); unsigned short usLatency = pSourcePlayer->GetPing (); const CControllerState& ControllerState = pSourcePlayer->GetPad ()->GetCurrentControllerState (); CElement* pContactElement = pSourcePlayer->GetContactElement (); // Get current weapon slot unsigned char ucWeaponSlot = pSourcePlayer->GetWeaponSlot (); // Flags SPlayerPuresyncFlags flags; flags.data.bIsInWater = ( pSourcePlayer->IsInWater () == true ); flags.data.bIsOnGround = ( pSourcePlayer->IsOnGround () == true ); flags.data.bHasJetPack = ( pSourcePlayer->HasJetPack () == true ); flags.data.bIsDucked = ( pSourcePlayer->IsDucked () == true ); flags.data.bWearsGoogles = ( pSourcePlayer->IsWearingGoggles () == true ); flags.data.bHasContact = ( pContactElement != NULL ); flags.data.bIsChoking = ( pSourcePlayer->IsChoking () == true ); flags.data.bAkimboTargetUp = ( pSourcePlayer->IsAkimboArmUp () == true ); flags.data.bIsOnFire = ( pSourcePlayer->IsOnFire () == true ); flags.data.bHasAWeapon = ( ucWeaponSlot != 0 ); flags.data.bSyncingVelocity = ( !flags.data.bIsOnGround || pSourcePlayer->IsSyncingVelocity () ); flags.data.bStealthAiming = ( pSourcePlayer->IsStealthAiming () == true ); CVector vecPosition = pSourcePlayer->GetPosition (); if ( pContactElement ) pSourcePlayer->GetContactPosition ( vecPosition ); float fCameraRotation = pSourcePlayer->GetCameraRotation (); BitStream.WriteCompressed ( PlayerID ); // Write the time context BitStream.Write ( pSourcePlayer->GetSyncTimeContext () ); BitStream.WriteCompressed ( usLatency ); WriteFullKeysync ( ControllerState, BitStream ); /* // Figure out what to send SPlayerPuresyncSentHeader sent; sent.bFlags = CompareAndSet ( usFlags, pSourcePlayer->lastSent.usFlags ); sent.bPosition = CompareAndSet ( vecPosition, pSourcePlayer->lastSent.vecPosition ); sent.bRotation = CompareAndSet ( fRotation, pSourcePlayer->lastSent.fRotation ); sent.bVelocity = CompareAndSet ( vecVelocity, pSourcePlayer->lastSent.vecVelocity ); sent.bHealth = CompareAndSet ( ucHealth, pSourcePlayer->lastSent.ucHealth ); sent.bArmor = CompareAndSet ( ucArmor, pSourcePlayer->lastSent.ucArmor ); sent.bCameraRotation = CompareAndSet ( fCameraRotation, pSourcePlayer->lastSent.fCameraRotation ); sent.bWeaponType = CompareAndSet ( ucWeaponType, pSourcePlayer->lastSent.ucWeaponType ); sent.Write ( BitStream ); if ( sent.bPosition ) { BitStream.Write ( vecPosition.fX ); BitStream.Write ( vecPosition.fY ); BitStream.Write ( vecPosition.fZ ); } if ( sent.bRotation ) BitStream.Write ( fRotation ); etc... Could also do a 'sent' header in WriteFullKeysync */ BitStream.Write ( &flags ); if ( pContactElement ) BitStream.WriteCompressed ( pContactElement->GetID () ); SPositionSync position ( false ); position.data.vecPosition = vecPosition; BitStream.Write ( &position ); SPedRotationSync rotation; rotation.data.fRotation = pSourcePlayer->GetRotation (); BitStream.Write ( &rotation ); if ( flags.data.bSyncingVelocity ) { SVelocitySync velocity; pSourcePlayer->GetVelocity ( velocity.data.vecVelocity ); BitStream.Write ( &velocity ); } // Player health and armor SPlayerHealthSync health; health.data.fValue = pSourcePlayer->GetHealth (); BitStream.Write ( &health ); SPlayerArmorSync armor; armor.data.fValue = pSourcePlayer->GetArmor (); BitStream.Write ( &armor ); BitStream.Write ( fCameraRotation ); if ( flags.data.bHasAWeapon ) { unsigned int uiSlot = ucWeaponSlot; SWeaponSlotSync slot; slot.data.uiSlot = uiSlot; BitStream.Write ( &slot ); if ( CWeaponNames::DoesSlotHaveAmmo ( uiSlot ) ) { unsigned short usWeaponAmmoInClip = pSourcePlayer->GetWeaponAmmoInClip (); /* // Figure out what to send SPlayerPuresyncWeaponSentHeader sent; sent.bWeaponAmmoInClip = CompareAndSet ( usWeaponAmmoInClip, pSourcePlayer->lastSent.usWeaponAmmoInClip ); sent.bAimDirectionX = CompareAndSet ( fAimDirectionX, pSourcePlayer->lastSent.fAimDirectionX ); sent.bAimDirectionY = CompareAndSet ( fAimDirectionY, pSourcePlayer->lastSent.fAimDirectionY ); sent.bSniperSource = CompareAndSet ( vecSniperSource, pSourcePlayer->lastSent.vecSniperSource ); sent.bTargetting = CompareAndSet ( vecTargetting, pSourcePlayer->lastSent.vecTargetting ); sent.Write ( BitStream ); if ( sent.bWeaponAmmoInClip ) BitStream.Write ( usWeaponAmmoInClip ); if ( sent.bAimDirectionX ) BitStream.Write ( fAimDirectionX ); if ( sent.bAimDirectionY ) BitStream.Write ( fAimDirectionY ); etc... */ SWeaponAmmoSync ammo ( pSourcePlayer->GetWeaponType (), false, true ); ammo.data.usAmmoInClip = usWeaponAmmoInClip; BitStream.Write ( &ammo ); SWeaponAimSync aim ( 0.0f, ( ControllerState.RightShoulder1 || ControllerState.ButtonCircle ) ); aim.data.fArm = pSourcePlayer->GetAimDirection (); // Write the aim data only if he's aiming or shooting if ( aim.isFull() ) { aim.data.vecOrigin = pSourcePlayer->GetSniperSourceVector (); pSourcePlayer->GetTargettingVector ( aim.data.vecTarget ); } BitStream.Write ( &aim ); } } // Success return true; } return false; }
bool CPlayerPuresyncPacket::Read ( NetBitStreamInterface& BitStream ) { if ( m_pSourceElement ) { CPlayer * pSourcePlayer = static_cast < CPlayer * > ( m_pSourceElement ); // Read out the time context unsigned char ucTimeContext = 0; if ( !BitStream.Read ( ucTimeContext ) ) return false; // Only read this packet if it matches the current time context that // player is in. if ( !pSourcePlayer->CanUpdateSync ( ucTimeContext ) ) { return false; } // Read out keys CControllerState ControllerState; ReadFullKeysync ( ControllerState, BitStream ); pSourcePlayer->GetPad ()->NewControllerState ( ControllerState ); // Read the flags SPlayerPuresyncFlags flags; if ( !BitStream.Read ( &flags ) ) return false; pSourcePlayer->SetInWater ( flags.data.bIsInWater ); pSourcePlayer->SetOnGround ( flags.data.bIsOnGround ); pSourcePlayer->SetHasJetPack ( flags.data.bHasJetPack ); pSourcePlayer->SetDucked ( flags.data.bIsDucked ); pSourcePlayer->SetWearingGoggles ( flags.data.bWearsGoogles ); pSourcePlayer->SetChoking ( flags.data.bIsChoking ); pSourcePlayer->SetAkimboArmUp ( flags.data.bAkimboTargetUp ); pSourcePlayer->SetOnFire ( flags.data.bIsOnFire ); pSourcePlayer->SetStealthAiming ( flags.data.bStealthAiming ); // Contact element CElement* pContactElement = NULL; if ( flags.data.bHasContact ) { ElementID Temp; if ( !BitStream.ReadCompressed ( Temp ) ) return false; pContactElement = CElementIDs::GetElement ( Temp ); } CElement * pPreviousContactElement = pSourcePlayer->GetContactElement (); pSourcePlayer->SetContactElement ( pContactElement ); if ( pPreviousContactElement != pContactElement ) { // Call our onPlayerContact event CLuaArguments Arguments; if ( pPreviousContactElement ) Arguments.PushElement ( pPreviousContactElement ); else Arguments.PushNil (); if ( pContactElement ) Arguments.PushElement ( pContactElement ); else Arguments.PushNil (); pSourcePlayer->CallEvent ( "onPlayerContact", Arguments ); } // Player position SPositionSync position ( false ); if ( !BitStream.Read ( &position ) ) return false; if ( pContactElement ) { pSourcePlayer->SetContactPosition ( position.data.vecPosition ); // Get the true position CVector vecTempPos = pContactElement->GetPosition (); position.data.vecPosition += vecTempPos; } pSourcePlayer->SetPosition ( position.data.vecPosition ); // Player rotation SPedRotationSync rotation; if ( !BitStream.Read ( &rotation ) ) return false; pSourcePlayer->SetRotation ( rotation.data.fRotation ); // Move speed vector if ( flags.data.bSyncingVelocity ) { SVelocitySync velocity; if ( !BitStream.Read ( &velocity ) ) return false; pSourcePlayer->SetVelocity ( velocity.data.vecVelocity ); } // Health ( stored with damage ) SPlayerHealthSync health; if ( !BitStream.Read ( &health ) ) return false; float fHealth = health.data.fValue; // Armor SPlayerArmorSync armor; if ( !BitStream.Read ( &armor ) ) return false; float fArmor = armor.data.fValue; float fOldArmor = pSourcePlayer->GetArmor (); float fArmorLoss = fOldArmor - fArmor; pSourcePlayer->SetArmor ( fArmor ); // Read out and set the camera rotation float fCameraRotation; if ( !BitStream.Read ( fCameraRotation ) ) return false; pSourcePlayer->SetCameraRotation ( fCameraRotation ); if ( flags.data.bHasAWeapon ) { if ( BitStream.Version () >= 0x0d ) { // Check client has the weapon we think he has unsigned char ucWeaponType; if ( !BitStream.Read ( ucWeaponType ) ) return false; if ( pSourcePlayer->GetWeaponType () != ucWeaponType ) return false; } // Current weapon slot SWeaponSlotSync slot; if ( !BitStream.Read ( &slot ) ) return false; unsigned int uiSlot = slot.data.uiSlot; pSourcePlayer->SetWeaponSlot ( uiSlot ); if ( CWeaponNames::DoesSlotHaveAmmo ( uiSlot ) ) { // Read out the ammo states SWeaponAmmoSync ammo ( pSourcePlayer->GetWeaponType (), true, true ); if ( !BitStream.Read ( &ammo ) ) return false; pSourcePlayer->SetWeaponAmmoInClip ( ammo.data.usAmmoInClip ); pSourcePlayer->SetWeaponTotalAmmo ( ammo.data.usTotalAmmo ); // Read out the aim data SWeaponAimSync sync ( pSourcePlayer->GetWeaponRange (), ( ControllerState.RightShoulder1 || ControllerState.ButtonCircle ) ); if ( !BitStream.Read ( &sync ) ) return false; // Set the arm directions and whether or not arms are up pSourcePlayer->SetAimDirection ( sync.data.fArm ); // Read the aim data only if he's shooting or aiming if ( sync.isFull() ) { pSourcePlayer->SetSniperSourceVector ( sync.data.vecOrigin ); pSourcePlayer->SetTargettingVector ( sync.data.vecTarget ); } } else { pSourcePlayer->SetWeaponAmmoInClip ( 1 ); pSourcePlayer->SetWeaponTotalAmmo ( 1 ); } } else { pSourcePlayer->SetWeaponSlot ( 0 ); pSourcePlayer->SetWeaponAmmoInClip ( 1 ); pSourcePlayer->SetWeaponTotalAmmo ( 1 ); } // Read out damage info if changed if ( BitStream.ReadBit () == true ) { ElementID DamagerID; if ( !BitStream.ReadCompressed ( DamagerID ) ) return false; SWeaponTypeSync weaponType; if ( !BitStream.Read ( &weaponType ) ) return false; SBodypartSync bodyPart; if ( !BitStream.Read ( &bodyPart ) ) return false; pSourcePlayer->SetDamageInfo ( DamagerID, weaponType.data.ucWeaponType, bodyPart.data.uiBodypart ); } // If we know the player's dead, make sure the health we send on is 0 if ( pSourcePlayer->IsDead () ) fHealth = 0.0f; float fOldHealth = pSourcePlayer->GetHealth (); float fHealthLoss = fOldHealth - fHealth; pSourcePlayer->SetHealth ( fHealth ); // Less than last packet's frame? if ( fHealthLoss > 0 || fArmorLoss > 0 ) { float fDamage = 0.0f; if ( fHealthLoss > 0 ) fDamage += fHealthLoss; if ( fArmorLoss > 0 ) fDamage += fArmorLoss; // Call the onPlayerDamage event CLuaArguments Arguments; CElement* pKillerElement = CElementIDs::GetElement ( pSourcePlayer->GetPlayerAttacker () ); if ( pKillerElement ) Arguments.PushElement ( pKillerElement ); else Arguments.PushNil (); Arguments.PushNumber ( pSourcePlayer->GetAttackWeapon () ); Arguments.PushNumber ( pSourcePlayer->GetAttackBodyPart () ); Arguments.PushNumber ( fDamage ); pSourcePlayer->CallEvent ( "onPlayerDamage", Arguments ); } // Success return true; } return false; }
void CPickup::Use ( CPlayer& Player ) { // Call the onPickupUse event CLuaArguments Arguments; Arguments.PushElement ( &Player ); if ( !CallEvent ( "onPickupUse", Arguments ) ) { CLuaArguments Arguments2; Arguments2.PushElement ( this ); // pickup Player.CallEvent ( "onPlayerPickupUse", Arguments2 ); } else { CLuaArguments Arguments2; Arguments2.PushElement ( this ); // pickup if ( Player.CallEvent ( "onPlayerPickupUse", Arguments2 ) ) { // Tell all the other players to hide it if the respawn intervals are bigger than 0 if ( m_ulRespawnIntervals > 0 ) { // Save our last used time m_ulLastUsedTime = GetTime (); // Mark us as not spawned m_bSpawned = false; // Mark us as hidden SetVisible ( false ); } // Tell him to play the sound and hide/show it Player.Send ( CPickupHitConfirmPacket ( this, true ) ); // Tell everyone else to hide/show it as neccessary g_pGame->GetPlayerManager ()->BroadcastOnlyJoined ( CPickupHitConfirmPacket ( this, false ), &Player ); // Handle it depending on the type switch ( m_ucType ) { // Health pickup? case CPickup::HEALTH: { float fHealth = Player.GetHealth (); float fNewHealth = fHealth + m_fAmount; if ( fNewHealth > 200.0f ) fNewHealth = 200.0f; CStaticFunctionDefinitions::SetElementHealth ( &Player, fNewHealth ); break; } // Armor pickup? case CPickup::ARMOR: { float fArmor = Player.GetArmor (); float fNewArmor = fArmor + m_fAmount; if ( fNewArmor > 100.0f ) fNewArmor = 100.0f; CStaticFunctionDefinitions::SetPedArmor ( &Player, fNewArmor ); break; } // Weapon pickup? case CPickup::WEAPON: { // Give him the weapon CStaticFunctionDefinitions::GiveWeapon ( &Player, m_ucWeaponType, m_usAmmo ); break; } default: break; } } } }