예제 #1
0
        public static bool Prefix(Player send_to_player)
        {
            m_snapshot_buffer.m_num_snapshots = 0;
            foreach (Player player in Overload.NetworkManager.m_Players)
            {
                if (!(player == null) && !player.m_spectator && !(player == send_to_player))
                {
                    NewPlayerSnapshot playerSnapshot = m_snapshot_buffer.m_snapshots[m_snapshot_buffer.m_num_snapshots++];
                    playerSnapshot.m_net_id = player.netId;
                    playerSnapshot.m_pos    = player.transform.position;
                    playerSnapshot.m_rot    = player.transform.rotation;
                    playerSnapshot.m_vel    = player.c_player_ship.c_rigidbody.velocity;
                    playerSnapshot.m_vrot   = player.c_player_ship.c_rigidbody.angularVelocity;
                }
            }
            if (m_snapshot_buffer.m_num_snapshots > 0)
            {
                if (!MPNoPositionCompression.enabled || !MPTweaks.ClientHasTweak(send_to_player.connectionToClient.connectionId, "nocompress_0_3_6"))
                {
                    send_to_player.connectionToClient.SendByChannel(64, m_snapshot_buffer.ToOldSnapshotMessage(), 1);
                    return(false);
                }

                send_to_player.connectionToClient.SendByChannel(MessageTypes.MsgNewPlayerSnapshotToClient, m_snapshot_buffer, 1);
            }
            return(false);
        }
 // extrapolate a single NewPlayerSnapshot (including the extra fields besides pos and rot)!
 // this is used for generating synthetic snapshot messages in case we detected missing packets
 private static void ExtrapolatePlayerSnapshot(ref NewPlayerSnapshot C, NewPlayerSnapshot B, float t)
 {
     C.m_pos = Vector3.LerpUnclamped(B.m_pos, B.m_pos + B.m_vel, t);
     C.m_rot = Quaternion.SlerpUnclamped(B.m_rot, B.m_rot * Quaternion.Euler(B.m_vrot), t);
     // assume the rest stays the same
     C.m_vel    = B.m_vel;
     C.m_vrot   = B.m_vrot;
     C.m_net_id = B.m_net_id;
 }
 public static void interpolatePlayer(Player player, NewPlayerSnapshot A, NewPlayerSnapshot B, float t)
 {
     if (HandlePlayerRespawn(player, A))
     {
         return;
     }
     player.c_player_ship.c_transform.localPosition           = Vector3.LerpUnclamped(A.m_pos, B.m_pos, t);
     player.c_player_ship.c_transform.rotation                = Quaternion.SlerpUnclamped(A.m_rot, B.m_rot, t);
     player.c_player_ship.c_mesh_collider_trans.localPosition = player.c_player_ship.c_transform.localPosition;
 }
예제 #4
0
 public static void extrapolatePlayer(Player player, NewPlayerSnapshot snapshot, float t)
 {
     if (HandlePlayerRespawn(player, snapshot))
     {
         return;
     }
     player.c_player_ship.c_transform.localPosition           = Vector3.LerpUnclamped(snapshot.m_pos, snapshot.m_pos + snapshot.m_vel, t);
     player.c_player_ship.c_transform.rotation                = Quaternion.SlerpUnclamped(snapshot.m_rot, snapshot.m_rot * Quaternion.Euler(snapshot.m_vrot), t);
     player.c_player_ship.c_mesh_collider_trans.localPosition = player.c_player_ship.c_transform.localPosition;
 }
        // interpolate a single NewPlayerSnapshot (including the extra fields besides pos and rot)!
        // this is used for generating synthetic snapshot messages in case we detected missing packets
        private static void InterpolatePlayerSnapshot(ref NewPlayerSnapshot C, NewPlayerSnapshot A, NewPlayerSnapshot B, float t)
        {
            C.m_pos = Vector3.LerpUnclamped(A.m_pos, B.m_pos, t);
            C.m_rot = Quaternion.SlerpUnclamped(A.m_rot, B.m_rot, t);
            C.m_vel = Vector3.LerpUnclamped(A.m_vel, B.m_vel, t);
            Quaternion A_vrot = Quaternion.Euler(A.m_vrot);
            Quaternion B_vrot = Quaternion.Euler(C.m_vrot);
            Quaternion C_vrot = Quaternion.SlerpUnclamped(A_vrot, B_vrot, t);

            C.m_vrot   = C_vrot.eulerAngles;
            C.m_net_id = A.m_net_id;
        }
        public static NewPlayerSnapshot GetPlayerSnapshot(NewPlayerSnapshotToClientMessage msg, Player p)
        {
            for (int i = 0; i < msg.m_num_snapshots; i++)
            {
                NewPlayerSnapshot playerSnapshot = msg.m_snapshots[i];
                Player            candidate      = (Player)_Client_GetPlayerFromNetId_Method.Invoke(null, new object[] { playerSnapshot.m_net_id });

                if (candidate == p)
                {
                    return(playerSnapshot);
                }
            }
            return(null);
        }
예제 #7
0
 public static bool Prefix(NetworkMessage msg)
 {
     if (NetworkMatch.GetMatchState() == MatchState.PREGAME || NetworkMatch.InGameplay())
     {
         PlayerSnapshotToClientMessage    item    = msg.ReadMessage <PlayerSnapshotToClientMessage>();
         NewPlayerSnapshotToClientMessage newItem = new NewPlayerSnapshotToClientMessage {
             m_num_snapshots    = item.m_num_snapshots,
             m_server_timestamp = 0, // Unused.
             m_snapshots        = item.m_snapshots.Select(m => NewPlayerSnapshot.FromOldSnapshot(m)).ToArray()
         };
         MPClientShipReckoning.AddNewPlayerSnapshot(newItem, true);
     }
     return(false);
 }
        public static void extrapolatePlayer(Player player, NewPlayerSnapshot snapshot, float t)
        {
            if (HandlePlayerRespawn(player, snapshot))
            {
                return;
            }
            Vector3 newPos = Vector3.LerpUnclamped(snapshot.m_pos, snapshot.m_pos + snapshot.m_vel, t);

            // limit ship dive-in if enabled:
            if (Menus.mms_lag_compensation_collision_limit > 0)
            {
                const float radius = 0.98f; /// the ship's collider is radius 1, we use a bit smaller one
                // how far the ship's enclosing sphere is allowed to dive in
                float   maxDive  = (100.0f - (float)Menus.mms_lag_compensation_collision_limit) / 50.0f * radius;
                Vector3 basePos  = snapshot.m_pos;
                Vector3 deltaPos = newPos - basePos;
                float   dist     = deltaPos.magnitude;
                if (dist > 0.05f && dist > maxDive)   // only if ship is moved by a significant amount
                // NOTE: we only test against LAVA and LEVEL, not other players, because that
                //       would have two drawbacks:
                //       - we would test against the player ship itslef, if speed and ping
                //         is high enough (I tried to disable that collider, but that didn't work)
                //       - if multipe opponents collide, the first one we processed here
                //         would get maximal movement and the others would be cut short, which
                //         is not correct either...
                {
                    const int  layerMask = (1 << (int)UnityObjectLayers.LEVEL) | (1 << (int)UnityObjectLayers.LAVA);
                    RaycastHit hitInfo;
                    Vector3    direction = (1.0f / dist) * deltaPos;

                    if (Physics.SphereCast(basePos, radius, direction, out hitInfo, dist, layerMask, QueryTriggerInteraction.Ignore))
                    {
                        // how far the ship's enclosing shpere dives into the collider
                        float diveIn = dist - hitInfo.distance;
                        if (diveIn > maxDive)
                        {
                            // limit the ship position
                            diveIn = maxDive;
                            newPos = basePos + (hitInfo.distance + diveIn) * direction;
                        }
                    }
                }
            }
            player.c_player_ship.c_transform.localPosition           = newPos;
            player.c_player_ship.c_transform.rotation                = Quaternion.SlerpUnclamped(snapshot.m_rot, snapshot.m_rot * Quaternion.Euler(snapshot.m_vrot), t);
            player.c_player_ship.c_mesh_collider_trans.localPosition = player.c_player_ship.c_transform.localPosition;
        }
        // Deal with the respawn. Return true if the player should not be moved around
        private static bool HandlePlayerRespawn(Player player, NewPlayerSnapshot snapshot)
        {
            // this logic was in vanilla Player.LerpRemotePlayer()
            if (player.m_lerp_wait_for_respawn_pos)
            {
                float num  = Vector3.Distance(snapshot.m_pos, player.m_lerp_respawn_pos);
                float num2 = Vector3.Distance(snapshot.m_pos, player.m_lerp_death_pos);
                if (num >= num2)
                {
                    // still special case for respawning
                    return(true);
                }

                player.m_lerp_wait_for_respawn_pos = false;
            }
            return(false);
        }
예제 #10
0
 /// <summary>
 /// Deserialize the snapshot without compression.
 /// </summary>
 /// <param name="reader"></param>
 public override void Deserialize(NetworkReader reader)
 {
     m_server_timestamp = reader.ReadSingle();
     m_num_snapshots    = (int)reader.ReadByte();
     for (int i = 0; i < m_num_snapshots; i++)
     {
         NetworkInstanceId net_id = reader.ReadNetworkId();
         Vector3           pos    = default(Vector3);
         pos.x = reader.ReadSingle();
         pos.y = reader.ReadSingle();
         pos.z = reader.ReadSingle();
         Quaternion rot = NetworkCompress.DecompressQuaternion(reader.ReadUInt32());
         Vector3    vel = default(Vector3);
         vel.x = HalfHelper.Decompress(reader.ReadUInt16());
         vel.y = HalfHelper.Decompress(reader.ReadUInt16());
         vel.z = HalfHelper.Decompress(reader.ReadUInt16());
         Vector3 vrot = default(Vector3);
         vrot.x         = HalfHelper.Decompress(reader.ReadUInt16());
         vrot.y         = HalfHelper.Decompress(reader.ReadUInt16());
         vrot.z         = HalfHelper.Decompress(reader.ReadUInt16());
         m_snapshots[i] = new NewPlayerSnapshot(net_id, pos, rot,
                                                vel, vrot);
     }
 }
        // Called per frame, moves ships along their interpolation/extrapolation motions
        public static void updatePlayerPositions()
        {
            float now = Time.time;                        // needs to be the same time source we use for m_last_update_time
            NewPlayerSnapshotToClientMessage msgA = null; // interpolation: start
            NewPlayerSnapshotToClientMessage msgB = null; // interpolation: end, extrapolation start
            float interpolate_factor = 0.0f;              // interpolation: factor in [0,1]
            float delta_t            = 0.0f;
            int   interpolate_ticks  = 0;
            bool  do_interpolation   = false;

            // find out which case we have, and get the relevant snapshot message(s)
            lock (m_last_messages_lock) {
                /*
                 * for (int xxx=0; xxx<m_last_messages_ring_count; xxx++) {
                 *  Debug.LogFormat("having snapshot from {0} represents {1}", m_last_messages_ring[(m_last_messages_ring_pos_last + 4 - xxx)&3].m_timestamp, m_last_update_time - xxx* Time.fixedDeltaTime);
                 * }
                 */
                if (m_last_messages_ring_count < 1)
                {
                    // we do not have any snapshot messages...
                    return;
                }

                // make sure m_last_update_time is up-to-date
                ResyncTime();

                // NOTE: now and m_last_update_time indirectly have timeScale already applied, as they are based on Time.time
                //       we need to adjust just the ping and the mms_ship_max_interpolate_frames offset...
                //       Also note that the server still sends the unscaled velocities.
                delta_t = now + Time.timeScale * MPClientExtrapolation.GetShipExtrapolationTime() - m_last_update_time;
                // if we want interpolation, add this as a _negative_ offset
                // we use delta_t=0  as the base for from which we extrapolate into the future
                delta_t -= (Menus.mms_lag_compensation_ship_added_lag / 1000f) * Time.timeScale;
                // it might sound absurd, but after this point, the Time.fixedDeltaTime is correct
                // and MUST NOT be scaled by timeScale. The data packets do contain 16.67ms of
                // movement each, we already have taken the time dilation into account above...
                // time difference in physics ticks
                float delta_ticks = delta_t / Time.fixedDeltaTime;
                // the number of frames we need to interpolate into
                // <= 0 means no interpolation at all,
                // 1 would mean we use the second most recent and the most recent snapshot, and so on...
                interpolate_ticks = -(int)Mathf.Floor(delta_ticks);
                // do we need to do interpolation?
                do_interpolation = (interpolate_ticks > 0);

                if (do_interpolation)
                {
                    // we need interpolate_ticks + 1 elements in the ring buffer
                    // NOTE: in the code below, the index [(m_last_messages_ring_pos_last + 4 - i) &3]
                    //       effectively acceses the i-ith most recent element (i starting by 0)
                    //       since 4-(i-1) == 4-i+ 1 = 5-i, 5-i references the next older one
                    if (interpolate_ticks < m_last_messages_ring_count)
                    {
                        msgA = m_last_messages_ring[(m_last_messages_ring_pos_last + 4 - interpolate_ticks) & 3];
                        msgB = m_last_messages_ring[(m_last_messages_ring_pos_last + 5 - interpolate_ticks) & 3];
                        interpolate_factor = delta_ticks - Mathf.Floor(delta_ticks);
                    }
                    else
                    {
                        // not enough packets received so far
                        // "extrapolate" into the past
                        do_interpolation = false;
                        // get the oldest snapshot we have
                        msgB = m_last_messages_ring[(m_last_messages_ring_pos_last + 5 - m_last_messages_ring_count) & 3];
                        // offset the time for the extrapolation
                        // delta_t is currently relative to the most recent element we have,
                        // but we need it relative to msgA
                        delta_t += Time.fixedDeltaTime * (m_last_messages_ring_count - 1);
                    }
                }
                else
                {
                    // extrapolation case
                    // use the most recently received snapshot
                    msgB = m_last_messages_ring[m_last_messages_ring_pos_last];
                }
            } // lock
            m_last_frame_time = now;

            /*
             * Debug.LogFormat("At: {0} Setting: {1} IntFrames: {2}, dt: {3}, IntFact {4}",now,Menus.mms_ship_max_interpolate_frames, interpolate_ticks, delta_t, interpolate_factor);
             * if (interpolate_ticks > 0) {
             *  Debug.LogFormat("Using A from {0}", msgA.m_timestamp);
             *  Debug.LogFormat("Using B from {0}", msgB.m_timestamp);
             * } else {
             *  Debug.LogFormat("Using B from {0}", msgB.m_timestamp);
             * }
             */

            // keep statistics
            m_compensation_sum += delta_t;
            m_compensation_count++;
            // NOTE: one can't replace(interpolate_ticks > 0) by do_interpolation here,
            //       because even in the (interpolate_ticks > 0) case the code above could
            //       have reset do_interpolation to false because we technically want
            //       the "extrapolation" into the past thing, but we don't want to count that
            //       as extrapolation...
            m_compensation_interpol_count += (interpolate_ticks > 0)?1:0;
            if (Time.time >= m_compensation_last + 5.0 && m_compensation_count > 0)
            {
                // Debug.LogFormat("ship lag compensation over last {0} frames: {1}ms / {2} physics ticks, {3} interpolation ({4}%) packets: {5} received / {6} missing / {7} old ignored",
                //                 m_compensation_count, 1000.0f* (m_compensation_sum/ m_compensation_count),
                //                 (m_compensation_sum/m_compensation_count)/Time.fixedDeltaTime,
                //                 m_compensation_interpol_count,
                //                 100.0f*((float)m_compensation_interpol_count/(float)m_compensation_count),
                //                 m_received_packets_count, m_missing_packets_count, m_ignored_packets_count);
                m_compensation_sum            = 0.0f;
                m_compensation_count          = 0;
                m_compensation_interpol_count = 0;
                m_received_packets_count      = 0;
                m_missing_packets_count       = 0;
                m_ignored_packets_count       = 0;
                m_compensation_last           = Time.time;
            }

            // actually apply the operation to each player
            foreach (Player player in Overload.NetworkManager.m_Players)
            {
                if (player != null && !player.isLocalPlayer && !player.m_spectator)
                {
                    // do the actual interpolation or extrapolation, as calculated above
                    if (do_interpolation)
                    {
                        NewPlayerSnapshot A = GetPlayerSnapshot(msgA, player);
                        NewPlayerSnapshot B = GetPlayerSnapshot(msgB, player);
                        if (A != null && B != null)
                        {
                            interpolatePlayer(player, A, B, interpolate_factor);
                        }
                    }
                    else
                    {
                        NewPlayerSnapshot snapshot = GetPlayerSnapshot(msgB, player);
                        if (snapshot != null)
                        {
                            extrapolatePlayer(player, snapshot, delta_t);
                        }
                    }
                }
            }
        }