Example #1
0
        public byte PrepGhostSecond(ushort entityid, ushort timestamp)
        {
            // if ``, can't possibly belong to this snapper
            if (entityid >= SecondEntityIdToInnerId.Length)
            {
                return(0);
            }

            // if ``, this snapper doesn't have this entity
            int innerid = SecondEntityIdToInnerId[entityid];

            if (innerid == -1)
            {
                return(0);
            }

            SnapHistory <TSnap, TStatic> h = SecondData.Values[SecondData.IdsToIndices[innerid]];

            TempWriteHistory = h;
            TempWriteIndex   = h.FindIndex(timestamp);

            if (TempWriteIndex < 0)
            {
                return(0);
            }

            return(Packer.PrepPackFull(
                       h.Shots[TempWriteIndex], h.StaticData, out PackInfo));
        }
Example #2
0
        private bool UnpackDelta(ReArrayIdPool <SnapHistory <TSnap, TStatic> > data,
                                 int innerid,
                                 byte[] blob, int blobstart, int blobcount,
                                 ushort timestamp, ushort basisTimestamp)
        {
            SnapHistory <TSnap, TStatic> sh = data.Values[data.IdsToIndices[innerid]];

            int index = sh.FindIndex(timestamp);

            if (index < 0 || index >= sh.Shots.Length)
            {
                return(false); // out of bounds
            }
            int basisIndex = sh.FindIndex(basisTimestamp);

            if (basisIndex < 0 || basisIndex >= sh.Shots.Length)
            {
                return(false); // out of bounds
            }
            Packer.UnpackDelta(ref sh.Shots[index], sh.Shots[basisIndex], blob, blobstart, blobcount);
            sh.Timestamps[index] = timestamp;
            sh.Flags[index]      = SnapHistory <TSnap, TStatic> .FlagGold;

            // handle flag rollover
            Rollover(sh, index, timestamp);

            return(true);
        }
Example #3
0
        private bool UnpackDelta(ReArrayIdPool <SnapHistory <TSnap, TStatic> > data,
                                 int innerid,
                                 byte[] blob, int blobstart, int blobcount,
                                 ushort timestamp, ushort basisTimestamp)
        {
            SnapHistory <TSnap, TStatic> sh = data.Values[data.IdsToIndices[innerid]];

            int index = sh.FindIndex(timestamp);

            if (index < 0 || index >= sh.Shots.Length)
            {
                return(false); // out of bounds
            }
            int basisIndex = sh.FindIndex(basisTimestamp);

            if (basisIndex < 0 || basisIndex >= sh.Shots.Length)
            {
                return(false); // out of bounds
            }
            Packer.UnpackDelta(ref sh.Shots[index], sh.Shots[basisIndex], blob, blobstart, blobcount);
            sh.Timestamps[index] = timestamp;
            sh.Flags[index]      = SnapHistory <TSnap, TStatic> .FlagGold;

            // ask the server to resimulate from this timestamp
            // to ensure our silver guesses get updated
            NetSnapper.RequestResimulate(timestamp);

            return(true);
        }
Example #4
0
        public bool ServerAddEntitySecond(TSnap initalSnap, TStatic staticData, out ushort eid,
                                          bool ghostAll = true)
        {
            if (!NetSnapper.ServerRequestEntitySecond(EntityType, out eid))
            {
                return(false); // NetSnapper says: no entity space available
            }

            if (SecondEntityIdToInnerId.Length <= eid)
            {
                ExpandSecondEntityIdMap(eid);
            }

            SnapHistory <TSnap, TStatic> h = SecondData.Request();

            h.StaticData = staticData;

            h.EntityId      = eid;
            h.Timestamps[0] = NetSnapper.CurrentTime;
            h.Flags[0]      = SnapHistory <TSnap, TStatic> .FlagGold;
            h.LeadingIndex  = 0;
            h.Shots[0]      = initalSnap;

            SecondEntityIdToInnerId[eid] = h.PoolId;

            if (ghostAll)
            {
                // ghost the new entity to all players
                NetSnapper.ServerSendGhostAllSecond(eid, EntityType);
            }

            return(true);
        }
Example #5
0
        public void ServerAdvance()
        {
            for (int d = 0; d < NentDatas.Length; d++)
            {
                ReArrayIdPool <SnapHistory <TSnap, TStatic> > data = NentDatas[d];
                for (int i = 0; i < data.Count; i++)
                {
                    SnapHistory <TSnap, TStatic> h = data.Values[i];

                    if (h.PrevFlag != SnapHistory <TSnap, TStatic> .FlagGold)
                    {
                        continue; // if the prevflag is not gold,
                    }
                    // the entity does not exist at this timestamp
                    // so we don't bother simulating it yet

                    // now we advance the snapshot forward to the current point in
                    // time, and then save it
                    h.Shots[h.CurrentIndex] = h.Shots[h.PrevIndex];
                    Advancer.AdvanceLogic(AdvancerConfig, h, ref h.Shots[h.CurrentIndex], NetSnapper.TickMSTarget);

                    Nents.ServerSaveSimIntoCurrent(h);
                }
            }
        }
Example #6
0
        private bool Deghost(ReArrayIdPool <SnapHistory <TSnap, TStatic> > data,
                             int innerid, ushort timestamp)
        {
            SnapHistory <TSnap, TStatic> sh = data.Values[data.IdsToIndices[innerid]];

            int index = sh.FindIndex(timestamp);

            if (index < 0 || index >= sh.Shots.Length)
            {
                return(false); // out of bounds
            }
            sh.Flags[index] = SnapHistory <TSnap, TStatic> .FlagDeghosted;

            // scroll forward and deghost subsequent timestamps!

            // what to do if deghost and ghost arrive out of order?
            // simple: by only overwriting SILVER flags, we ensure that deghosting
            // will not overwrite fresh / more recent data

            int    nindex   = index + 1;
            ushort nextTime = timestamp;

            while (true)
            {
                if (nindex == sh.Shots.Length)
                {
                    nindex = 0;
                }

                if (nextTime == ushort.MaxValue)
                {
                    nextTime = 0;
                }
                else
                {
                    nextTime++;
                }

                // we can only rollover onto silver flags
                if (sh.Flags[nindex] != SnapHistory <TSnap, TStatic> .FlagSilver && sh.Flags[nindex] != SnapHistory <TSnap, TStatic> .FlagEmpty)
                {
                    break;
                }

                // can only rollover onto subsequent timestamps
                if (sh.Timestamps[nindex] != nextTime)
                {
                    break;
                }

                // now we're ready to roll
                sh.Flags[nindex] = SnapHistory <TSnap, TStatic> .FlagDeghosted;

                nindex++;
            }

            return(true);
        }
Example #7
0
        // Simulant Helpers
        public void ServerSaveSimIntoCurrent(SnapHistory <TSnap, TStatic> ent)
        {
            ent.Flags[ent.CurrentIndex] = SnapHistory <TSnap, TStatic> .FlagGold;

            if (ent.First)
            {
                NetSnapper.ServerSendDeltaAllFirst((byte)ent.EntityId, EntityType);
            }
            else
            {
                NetSnapper.ServerSendDeltaAllSecond(ent.EntityId, EntityType);
            }
        }
Example #8
0
        public bool PrepDeltaSecond(ushort entityid, ushort timestamp, ushort basisTimestamp,
                                    out byte len)
        {
            len = 0;

            // if ``, can't possibly belong to this snapper
            if (entityid >= SecondEntityIdToInnerId.Length)
            {
                return(false);
            }

            // if ``, this snapper doesn't have this entity
            int innerid = SecondEntityIdToInnerId[entityid];

            if (innerid == -1)
            {
                return(false);
            }

            SnapHistory <TSnap, TStatic> h = SecondData.Values[SecondData.IdsToIndices[innerid]];

            int index = h.FindIndex(timestamp);

            if (index == -1)
            {
                return(false);
            }

            int basisIndex = h.FindIndex(basisTimestamp);

            if (basisIndex == -1)
            {
                return(false);
            }

            TempWriteHistory = h;
            TempWriteIndex   = index;

            len = Packer.PrepPackDelta(
                h.Shots[index],
                h.Shots[basisIndex],
                out PackInfo);

            return(true);
        }
Example #9
0
        private bool UnpackFull(ReArrayIdPool <SnapHistory <TSnap, TStatic> > data,
                                int innerid,
                                byte[] blob, int blobstart, int blobcount,
                                ushort timestamp)
        {
            SnapHistory <TSnap, TStatic> sh = data.Values[data.IdsToIndices[innerid]];

            int index = sh.FindIndex(timestamp);

            if (index < 0)
            {
                return(false); // out of bounds-- too far in future or past
            }
            Packer.UnpackFull(ref sh.Shots[index], ref sh.StaticData, blob, blobstart, blobcount);
            sh.Timestamps[index] = timestamp;
            sh.Flags[index]      = SnapHistory <TSnap, TStatic> .FlagGold;

            // handle flag rollover
            Rollover(sh, index, timestamp);

            return(true);
        }
Example #10
0
        public void ServerAdvance()
        {
            for (int d = 0; d < NentDatas.Length; d++)
            {
                ReArrayIdPool<SnapHistory<TSnap, TStatic>> data = NentDatas[d];
                for (int i = 0; i < data.Count; i++)
                {
                    SnapHistory<TSnap, TStatic> h = data.Values[i];

                    if (h.PrevFlag != SnapHistory<TSnap, TStatic>.FlagGold)
                        continue; // if the prevflag is not gold,
                                  // the entity does not exist at this timestamp
                                  // so we don't bother simulating it yet

                    // now we advance the snapshot forward to the current point in
                    // time, and then save it
                    byte ownerPid = Advancer.GetInputPlayer(h.Shots[h.CurrentIndex], h.StaticData);
                    if (ownerPid != 0)
                    {
                        // if we have no inputs, do regular advancement
                        h.Shots[h.CurrentIndex] = h.Shots[h.PrevIndex];
                        if (!InputChecker(h, ref h.Shots[h.CurrentIndex], ownerPid, NetSnapper.TickMSTarget))
                        {
                            Advancer.AdvanceLogic(AdvancerConfig, h, ref h.Shots[h.CurrentIndex], NetSnapper.TickMSTarget);
                        }
                    }
                    else
                    {
                        // if this were client, we'd check the flag of the next.
                        // as it is, just create the next from the current
                        h.Shots[h.CurrentIndex] = h.Shots[h.PrevIndex];
                        Advancer.AdvanceLogic(AdvancerConfig, h, ref h.Shots[h.CurrentIndex], NetSnapper.TickMSTarget);
                    }

                    Nents.ServerSaveSimIntoCurrent(h);
                }
            }
        }
Example #11
0
        private bool UnpackFull(ReArrayIdPool <SnapHistory <TSnap, TStatic> > data,
                                int innerid,
                                byte[] blob, int blobstart, int blobcount,
                                ushort timestamp)
        {
            SnapHistory <TSnap, TStatic> sh = data.Values[data.IdsToIndices[innerid]];

            int index = sh.FindIndex(timestamp);

            if (index < 0)
            {
                return(false); // out of bounds-- too far in future or past
            }
            Packer.UnpackFull(ref sh.Shots[index], ref sh.StaticData, blob, blobstart, blobcount);
            sh.Timestamps[index] = timestamp;
            sh.Flags[index]      = SnapHistory <TSnap, TStatic> .FlagGold;

            // ask the server to resimulate from this timestamp
            // to ensure our silver guesses get updated
            NetSnapper.RequestResimulate(timestamp);

            return(true);
        }
Example #12
0
        // Prime Logic

        // Advancement occurs every tick
        public void Advance(ushort currentTime)
        {
            CurrentTime = currentTime;

            // when we advance, push every leading edge forward
            for (int i = 0; i < FirstData.Count; i++)
            {
                SnapHistory <TSnap, TStatic> sh = FirstData.Values[i];
                ushort last       = sh.Timestamps[sh.LeadingIndex];
                ushort next       = last == ushort.MaxValue ? (ushort)0 : (ushort)(last + 1);
                int    startIndex = sh.LeadingIndex;

                sh.LeadingIndex++;
                if (sh.LeadingIndex == sh.Shots.Length)
                {
                    sh.LeadingIndex = 0;
                }

                if (sh.Timestamps[sh.LeadingIndex] != next)
                {
                    // we haven't received the next snap yet, so destroy it and
                    // ensure that it is empty for the simulator
                    sh.Timestamps[sh.LeadingIndex] = next;
                    sh.Flags[sh.LeadingIndex]      = SnapHistory <TSnap, TStatic> .FlagEmpty;

                    // note: we don't need to actually call Clear on the snapshot
                    // since the fact that it's marked with an empty flag means
                    // the simulator should avoid using it.

                    // if the previous snapshot was deghosted,
                    // deghost this one as well.
                    if (sh.Flags[startIndex] == SnapHistory <TSnap, TStatic> .FlagDeghosted)
                    {
                        sh.Flags[sh.LeadingIndex] = SnapHistory <TSnap, TStatic> .FlagDeghosted;
                    }
                }
            }

            // same logic as above but for second data
            // only reason we don't use a method for this is having that many method
            // calls seems like a waste.
            for (int i = 0; i < SecondData.Count; i++)
            {
                SnapHistory <TSnap, TStatic> sh = SecondData.Values[i];
                ushort last       = sh.Timestamps[sh.LeadingIndex];
                ushort next       = last == ushort.MaxValue ? (ushort)0 : (ushort)(last + 1);
                int    startIndex = sh.LeadingIndex;

                sh.LeadingIndex++;
                if (sh.LeadingIndex == sh.Shots.Length)
                {
                    sh.LeadingIndex = 0;
                }

                if (sh.Timestamps[sh.LeadingIndex] != next)
                {
                    sh.Timestamps[sh.LeadingIndex] = next;
                    sh.Flags[sh.LeadingIndex]      = SnapHistory <TSnap, TStatic> .FlagEmpty;

                    if (sh.Flags[startIndex] == SnapHistory <TSnap, TStatic> .FlagDeghosted)
                    {
                        sh.Flags[sh.LeadingIndex] = SnapHistory <TSnap, TStatic> .FlagDeghosted;
                    }
                }
            }

            // now check our destruct queues
            if (FirstDestructQueue.Count > 0)
            {
                for (int i = FirstDestructQueue.Count - 1; i >= 0; i--)
                {
                    ushort destructTime = FirstDestructQueueTime[i];

                    int dist = 0;
                    if (currentTime < destructTime)
                    {
                        dist = (ushort.MaxValue - destructTime) + currentTime;
                    }
                    else
                    {
                        dist = currentTime - destructTime;
                    }

                    if (dist < FirstWindowLength)
                    {
                        break;
                    }

                    NetSnapper.ServerDestructFirst(FirstDestructQueue[i]);
                    FirstDestructQueue.RemoveAt(i);
                    FirstDestructQueueTime.RemoveAt(i);
                }
            }

            if (SecondDestructQueue.Count > 0)
            {
                for (int i = SecondDestructQueue.Count - 1; i >= 0; i--)
                {
                    ushort destructTime = SecondDestructQueueTime[i];

                    int dist = 0;
                    if (currentTime < destructTime)
                    {
                        dist = (ushort.MaxValue - destructTime) + currentTime;
                    }
                    else
                    {
                        dist = currentTime - destructTime;
                    }

                    if (dist < FirstWindowLength)
                    {
                        break;
                    }

                    NetSnapper.ServerDestructSecond(SecondDestructQueue[i]);
                    SecondDestructQueue.RemoveAt(i);
                    SecondDestructQueueTime.RemoveAt(i);
                }
            }
        }
Example #13
0
        private void Rollover(SnapHistory <TSnap, TStatic> sh, int index, ushort timestamp)
        {
            // this whole idea has to be reworked
            // I guess an issue is that one of our key assumptions
            // is that the indices will always be populated with timestamps
            // so we need to have some kind of rollover
            // but doing the extrap action here is not good
            // 1. it's not real simulated
            // 2. why not do interp when available?

            // one option would be to do simple extrap/interp here
            // or even just copy the same snapshot over and over
            // and then, when we get an older timestamp, flag the simulator
            // as "needs rollback calculation" (ONLY IF WE ACTUALLY HAVE ANY
            // SILVER SNAPSHOTS AHEAD OF THIS TIMESTAMP)

            // then, at the end of each processing, if the simulator has
            // this flag set, it will run again from whatever the oldest time
            // flagged was (need to track the timestamp of each flagging as well)
            // this will generate new silver snapshots, and they'll be accurate to boot.

            // RE: THIS DISCUSSION
            // we decided to do the "resimulate flagging" approach
            // so the way this works now is that if we receive a timestamp,
            // and there's unknowns in front of it,
            // the system gets asked to resimulate from that timestamp

            int    nindex   = index + 1;
            ushort nextTime = timestamp;

            if (nindex == sh.Shots.Length)
            {
                nindex = 0;
            }

            if (nextTime == ushort.MaxValue)
            {
                nextTime = 0;
            }
            else
            {
                nextTime++;
            }

            // we can only rollover onto silver flags
            if (sh.Flags[nindex] != SnapHistory <TSnap, TStatic> .FlagSilver && sh.Flags[nindex] != SnapHistory <TSnap, TStatic> .FlagEmpty)
            {
                return;
            }

            // can only rollover onto subsequent timestamps
            if (sh.Timestamps[nindex] != nextTime)
            {
                return;
            }

            // if we get here, then we hit a silver or an empty that must be filled out
            // tell the snap system to resimulate
            NetSnapper.RequestResimulate(timestamp);


            // VVVV OLD VVVV

            /*
             * // flags need to act a certain way to allow us to have
             * // snapshots roll over when no changes are needed
             * // e.g.:
             * // 1 = from server (GOLD STANDARD)
             * // 2 = roll over, this occurs when no snapshot is received
             * //     we assume shot is unchanged and use it again for next
             * //     timestamp
             * // now imagine we have some snaps:
             * // aaaaabb
             * // 1222212
             * //   ^ and here we receive a new snapshot c
             * // we SCROLL FORWARD and replace all 2s with the new snapshot:
             * // aacccbb
             * // 1212212
             * // this way we can let client continue to scroll forward but
             * // also correct itself properly
             * int nindex = index + 1;
             * ushort nextTime = timestamp;
             * int rolls = 1;
             * while (true)
             * {
             *  if (nindex == sh.Shots.Length)
             *      nindex = 0;
             *
             *  if (nextTime == ushort.MaxValue)
             *      nextTime = 0;
             *  else
             *      nextTime++;
             *
             *  // we can only rollover onto silver flags
             *  if (sh.Flags[nindex] != SnapHistory<T>.FlagSilver && sh.Flags[nindex] != SnapHistory<T>.FlagEmpty)
             *      break;
             *
             *  // can only rollover onto subsequent timestamps
             *  if (sh.Timestamps[nindex] != nextTime)
             *      break;
             *
             *  // now we're ready to roll
             *  ExtrapolateAction(ref sh.Shots[nindex], sh.Shots[index], TickMS * rolls);
             *  rolls++;
             *
             *  nindex++;
             * }*/
        }
Example #14
0
        private int Ghost(ReArrayIdPool <SnapHistory <TSnap, TStatic> > data,
                          ushort entityid,
                          byte[] blob, int blobstart, int blobcount,
                          ushort timestamp)
        {
            SnapHistory <TSnap, TStatic> sh = data.Request();

            sh.EntityId      = entityid;
            sh.LeadingIndex  = 0;
            sh.Timestamps[0] = CurrentTime;

            // setup the rest of the future timestamps
            ushort itime = sh.Timestamps[0];

            for (int i = 1; i <= sh.Shots.Length / 2; i++)
            {
                if (itime == ushort.MaxValue)
                {
                    itime = 0;
                }
                else
                {
                    itime++;
                }

                sh.Timestamps[i] = itime;
            }

            // and the past
            itime = sh.Timestamps[0];
            for (int i = 0; i < sh.Shots.Length / 2; i++)
            {
                if (itime == 0)
                {
                    itime = ushort.MaxValue;
                }
                else
                {
                    itime--;
                }

                sh.Timestamps[sh.Shots.Length - 1 - i] = itime;
            }

            int expIndex = sh.FindIndex(timestamp);

            if (expIndex == -1)
            {
                // in this case, we need to just abandon the snapshot we
                // received. This can obviously create issues since we'll
                // need a full snapshot again.

                // however, we still unpack it in full, so the static data
                // can be constructed
                Packer.UnpackFull(ref sh.Shots[0], ref sh.StaticData, blob, blobstart, blobcount);
                sh.Flags[0] = SnapHistory <TSnap, TStatic> .FlagEmpty;
            }
            else
            {
                // if the snapshot we received has a space in the current window,
                // so just unpack into there
                Packer.UnpackFull(ref sh.Shots[expIndex], ref sh.StaticData, blob, blobstart, blobcount);
                sh.Timestamps[expIndex] = timestamp;
                sh.Flags[expIndex]      = SnapHistory <TSnap, TStatic> .FlagGold;
            }

            // return the innerid
            return(sh.PoolId);
        }
Example #15
0
        // Prime Logic

        // Advancement occurs every tick
        public void Advance(ushort currentTime)
        {
            CurrentTime = currentTime;

            // when we advance, push every leading edge forward
            for (int i = 0; i < FirstData.Count; i++)
            {
                SnapHistory <TSnap, TStatic> sh = FirstData.Values[i];
                ushort last       = sh.Timestamps[sh.LeadingIndex];
                ushort next       = last == ushort.MaxValue ? (ushort)0 : (ushort)(last + 1);
                int    startIndex = sh.LeadingIndex;

                sh.LeadingIndex++;
                if (sh.LeadingIndex == sh.Shots.Length)
                {
                    sh.LeadingIndex = 0;
                }

                if (sh.Timestamps[sh.LeadingIndex] != next)
                {
                    // we haven't received the next snap yet, so destroy it and
                    // ensure that it is empty for the simulator
                    sh.Timestamps[sh.LeadingIndex] = next;
                    sh.Flags[sh.LeadingIndex]      = SnapHistory <TSnap, TStatic> .FlagEmpty;

                    // note: we don't need to actually call Clear on the snapshot
                    // since the fact that it's marked with an empty flag means
                    // the simulator should avoid using it.

                    // if the previous snapshot was deghosted,
                    // deghost this one as well.
                    if (sh.Flags[startIndex] == SnapHistory <TSnap, TStatic> .FlagDeghosted)
                    {
                        sh.Flags[sh.LeadingIndex] = SnapHistory <TSnap, TStatic> .FlagDeghosted;
                    }
                }
            }

            // same logic as above but for second data
            // only reason we don't use a method for this is having that many method
            // calls seems like a waste.
            for (int i = 0; i < SecondData.Count; i++)
            {
                SnapHistory <TSnap, TStatic> sh = SecondData.Values[i];
                ushort last       = sh.Timestamps[sh.LeadingIndex];
                ushort next       = last == ushort.MaxValue ? (ushort)0 : (ushort)(last + 1);
                int    startIndex = sh.LeadingIndex;

                sh.LeadingIndex++;
                if (sh.LeadingIndex == sh.Shots.Length)
                {
                    sh.LeadingIndex = 0;
                }

                if (sh.Timestamps[sh.LeadingIndex] != next)
                {
                    sh.Timestamps[sh.LeadingIndex] = next;
                    sh.Flags[sh.LeadingIndex]      = SnapHistory <TSnap, TStatic> .FlagEmpty;

                    if (sh.Flags[startIndex] == SnapHistory <TSnap, TStatic> .FlagDeghosted)
                    {
                        sh.Flags[sh.LeadingIndex] = SnapHistory <TSnap, TStatic> .FlagDeghosted;
                    }
                }
            }
        }
Example #16
0
        // predicts forward a set number of ms
        public void ClientPredictMS(float delta)
        {
            for (int d = 0; d < NentDatas.Length; d++)
            {
                ReArrayIdPool<SnapHistory<TSnap, TStatic>> data = NentDatas[d];
                for (int i = 0; i < data.Count; i++)
                {
                    SnapHistory<TSnap, TStatic> h = data.Values[i];

                    // if the impulse time has moved up, move up the entity's to match
                    if (h.HasImpulse &&
                        ((NetSnapper.ClientImpulseTime >= 500
                         && h.ImpulseTimestamp < NetSnapper.ClientImpulseTime
                         && h.ImpulseTimestamp >= NetSnapper.ClientImpulseTime - 500)
                        || (NetSnapper.ClientImpulseTime < 500
                         && h.ImpulseTimestamp < NetSnapper.ClientImpulseTime)
                        || (NetSnapper.ClientImpulseTime < 500
                         && h.ImpulseTimestamp >= ushort.MaxValue - 500)))
                        h.ImpulseTimestamp = NetSnapper.ClientImpulseTime;

                    if (h.PrevFlag == SnapHistory<TSnap, TStatic>.FlagEmpty
                        || h.PrevFlag == SnapHistory<TSnap, TStatic>.FlagDeghosted)
                        continue; // if the prev flag is empty, this entity does 
                                  // not exist yet

                    // if the entity has impulse, we may need to predict
                    // if the current timestamp is after the impulse timestamp
                    if (h.HasImpulse
                        && ((NetSnapper.SimulateTimestamp >= h.ImpulseTimestamp
                            && !(NetSnapper.SimulateTimestamp >= ushort.MaxValue - 500
                                 && h.ImpulseTimestamp <= 500))
                            || (NetSnapper.SimulateTimestamp <= 500
                                && h.ImpulseTimestamp >= ushort.MaxValue - 500)))
                    {
                        // first, check if we have an input
                        // we could only have an input if we are the owner of this object
                        byte inputPid = Advancer.GetInputPlayer(h.ImpulseShot, h.StaticData);
                        if (inputPid == Server.OurPlayerId)
                        {
                            // now check if we have any inputs
                            // if so, process them
                            if (!InputChecker(h, ref h.ImpulseShot, inputPid, delta))
                            {
                                // if not, do a normal advance
                                Advancer.AdvanceLogic(AdvancerConfig, h, ref h.ImpulseShot, delta);
                            }
                        }
                        else
                        {
                            // if we couldn't have an input, just do a normal advance
                            Advancer.AdvanceLogic(AdvancerConfig, h, ref h.ImpulseShot, delta);
                        }

                        // now check if we need to blend
                        if (!h.PermanentImpulse
                            && ((NetSnapper.SimulateTimestamp >= h.BlendTimestamp
                                && !(NetSnapper.SimulateTimestamp >= ushort.MaxValue - 500
                                     && h.BlendTimestamp <= 500))
                                || (NetSnapper.SimulateTimestamp <= 500 
                                    && h.BlendTimestamp >= ushort.MaxValue - 500)))
                        {
                            // we're in the Blend Zone
                            // find the snapshot that we would be displaying, if this were not
                            // an impulse.
                            TSnap blendTarget = h.Shots[h.CurrentIndex];
                            if (h.NextFlag == SnapHistory<TSnap, TStatic>.FlagGold
                                || h.NextFlag == SnapHistory<TSnap, TStatic>.FlagSilver)
                            {
                                Advancer.InterpMSLogic(h, ref blendTarget, delta, NetSnapper.TickMSTarget);
                            }
                            else
                            { 
                                Advancer.AdvanceLogic(AdvancerConfig, h, ref blendTarget, delta);
                            }

                            // determine the blend factor
                            int blendTicks = NetSnapper.SimulateTimestamp;
                            if (NetSnapper.SimulateTimestamp <= 500
                                && h.BlendTimestamp >= ushort.MaxValue - 500)
                            {
                                blendTicks = (ushort.MaxValue - h.BlendTimestamp) + 1 + NetSnapper.SimulateTimestamp;
                            }
                            else
                            {
                                blendTicks -= h.BlendTimestamp;
                            }

                            h.ActiveBlendFactor = blendTicks * h.BlendFactor
                                + h.BlendFactor * (delta/NetSnapper.TickMSTarget);
                            if (h.ActiveBlendFactor > 1)
                            {
                                h.ActiveBlendFactor = 1;

                                // special consideration:
                                // if the impulse time equals the simulate time (in other words,
                                // this is our first timestamp of the predictive window), and
                                // our blend factor is already over 1, then we are already
                                // back to being in-sync with the server, so we can remove impulse
                                // from this entity.
                                if (NetSnapper.ClientImpulseTime == NetSnapper.SimulateTimestamp)
                                {
                                    h.HasImpulse = false;
                                }
                            }

                            // now blend the shots together
                            Advancer.BlendLogic(h, ref h.ImpulseShot, blendTarget, h.ActiveBlendFactor);
                        }

                        // finally, continue so we don't hit the interp logic below
                        continue;
                    }

                    // if it's not impulse, we need to interp or extrap as normal

                    // so we know the prev is good for interp, but is the next?
                    // if the next is also good for interp, we'll just interp
                    h.ImpulseShot = h.Shots[h.CurrentIndex];
                    if (h.NextFlag == SnapHistory<TSnap, TStatic>.FlagGold
                        || h.NextFlag == SnapHistory<TSnap, TStatic>.FlagSilver)
                    {
                        Advancer.InterpMSLogic(h, ref h.ImpulseShot, delta, NetSnapper.TickMSTarget);
                    }
                    else
                    {
                        // otherwise, do extrapolation 
                        Advancer.AdvanceLogic(AdvancerConfig, h, ref h.ImpulseShot, delta);
                    }

                    // note: we don't save this, it is just for rendering
                }
            }
        }
Example #17
0
 public void ClientSaveSimIntoCurrent(SnapHistory <TSnap, TStatic> ent)
 {
     ent.Flags[ent.CurrentIndex] = SnapHistory <TSnap, TStatic> .FlagSilver;
 }
Example #18
0
        private int Ghost(ReArrayIdPool <SnapHistory <TSnap, TStatic> > data,
                          ushort entityid,
                          byte[] blob, int blobstart, int blobcount,
                          ushort timestamp)
        {
            SnapHistory <TSnap, TStatic> sh = data.Request();

            sh.EntityId = entityid;
            Packer.UnpackFull(ref sh.Shots[0], ref sh.StaticData, blob, blobstart, blobcount);
            sh.Timestamps[0] = timestamp;
            sh.Flags[0]      = SnapHistory <TSnap, TStatic> .FlagGold;
            sh.LeadingIndex  = 0;

            // now, figure how close we are to current time
            // under normal conditions, we should be receiving snapshots
            // very close to the CurrentTime
            int expIndex = sh.FindIndex(CurrentTime);

            // however, if the expIndex is -1, it means the snapshot
            // we just received is outside the current window
            // (because CurrentTime relative to it is outside the window)
            if (expIndex == -1)
            {
                // in this case, we need to just abandon the snapshot we
                // received. This can obviously create issues since we'll
                // need a full snapshot again.
                sh.Timestamps[0] = CurrentTime;
                sh.Flags[0]      = SnapHistory <TSnap, TStatic> .FlagEmpty;
            }
            else
            {
                // in this case, we're within the window, so let's
                // just set the leading index properly.
                sh.LeadingIndex = expIndex;
                sh.Timestamps[sh.LeadingIndex] = CurrentTime;
            }

            // setup the rest of the future timestamps
            ushort itime = sh.Timestamps[0];

            for (int i = 1; i <= sh.Shots.Length / 2; i++)
            {
                if (itime == ushort.MaxValue)
                {
                    itime = 0;
                }
                else
                {
                    itime++;
                }

                sh.Timestamps[i] = itime;
            }

            // and the past
            itime = sh.Timestamps[0];
            for (int i = 0; i < sh.Shots.Length / 2; i++)
            {
                if (itime == 0)
                {
                    itime = ushort.MaxValue;
                }
                else
                {
                    itime--;
                }

                sh.Timestamps[sh.Shots.Length - 1 - i] = itime;
            }

            // return the innerid
            return(sh.PoolId);
        }
Example #19
0
        // predicts forward a whole tick
        // this returns true if all impulse entities have conf snapshots
        // indicating that we can turn up the impulseTimestamp
        public bool ClientPredictTick(ushort confTimestamp)
        {
            ushort prevConfTimestamp = confTimestamp;
            if (prevConfTimestamp == 0)
                prevConfTimestamp = ushort.MaxValue;
            else
                prevConfTimestamp--;

            bool noMissingImpulse = true;

            for (int d = 0; d < NentDatas.Length; d++)
            {
                ReArrayIdPool<SnapHistory<TSnap, TStatic>> data = NentDatas[d];
                for (int i = 0; i < data.Count; i++)
                {
                    SnapHistory<TSnap, TStatic> h = data.Values[i];

                    if (h.PrevFlag == SnapHistory<TSnap, TStatic>.FlagEmpty
                        || h.PrevFlag == SnapHistory<TSnap, TStatic>.FlagDeghosted)
                        continue; // if the prev flag is empty, this entity does 
                                  // not exist yet 

                    // if the entity has impulse, we may need to predict
                    // if the current timestamp is after the impulse timestamp
                    if (h.HasImpulse
                        && ((NetSnapper.SimulateTimestamp >= h.ImpulseTimestamp
                            && !(NetSnapper.SimulateTimestamp >= ushort.MaxValue - 500
                                 && h.ImpulseTimestamp <= 500))
                            || (NetSnapper.SimulateTimestamp <= 500
                                && h.ImpulseTimestamp >= ushort.MaxValue - 500)))
                    {
                        // cases:
                        // 0. if SimulateTimestamp is before h.ImpulseTimestamp, don't
                        //    predict, do normal interp
                        // 1. if this entity has a gold snapshot in SimulateTimestamp + I,
                        //    then don't predict; use the server result
                        // 2. if not, advance a predicted result

                        // case 0 is accounted for by the if statement above
                        
                        // case 1
                        int confIndex = h.FindIndex(confTimestamp);
                        if (confIndex != -1 
                            && h.Flags[confIndex] == SnapHistory<TSnap, TStatic>.FlagGold
                            && h.ImpulseTimestamp == NetSnapper.SimulateTimestamp)
                        {
                            // in this case, our job is easy, we have a server confirmed snap
                            // so we just use that
                            h.ImpulseShot = h.Shots[confIndex];
                            //NetSnapper.Server.NetLogger.Log(NetSnapper.SimulateTimestamp + " / " + confTimestamp + " F");
                        }
                        else
                        {
                            // case 2
                            // in this case, we must predict based on the current ImpulseShot

                            // because we got here, we are missing a conf for this impulse
                            // so we can't move up the NetSnapper's ImpulseTimestamp
                            noMissingImpulse = false;

                            if (h.ImpulseTimestamp == NetSnapper.SimulateTimestamp)
                            {
                                // special consideration: if this is our first impulse,
                                // we must populate ImpulseShot

                                // if we have a confirmed shot for the previous timestamp,
                                // we should use that
                                // if we don't, this must be our first prediction, so we
                                // can simply use the previous snapshot
                                int prevConfIndex = h.FindIndex(prevConfTimestamp);
                                if (prevConfIndex != -1
                                    && h.Flags[prevConfIndex] == SnapHistory<TSnap, TStatic>.FlagGold)
                                {
                                    h.ImpulseShot = h.Shots[prevConfIndex];
                                    //NetSnapper.Server.NetLogger.Log(h.ImpulseTimestamp + " / " + prevConfTimestamp + " C");
                                }
                                else
                                {
                                    h.ImpulseShot = h.Shots[h.PrevIndex];
                                    //NetSnapper.Server.NetLogger.Log(h.ImpulseTimestamp + " / " + prevConfTimestamp + " P");
                                }
                            }

                            // first, check if we have an input
                            // we could only have an input if we are the owner of this object
                            byte inputPid = Advancer.GetInputPlayer(h.ImpulseShot, h.StaticData);
                            if (inputPid == Server.OurPlayerId)
                            {
                                // now check if we have any inputs
                                // if so, process them
                                if (!InputChecker(h, ref h.ImpulseShot, inputPid, NetSnapper.TickMSTarget))
                                {
                                    // if not, do a normal advance
                                    Advancer.AdvanceLogic(AdvancerConfig, h, ref h.ImpulseShot, NetSnapper.TickMSTarget);
                                }
                            }
                            else
                            {
                                // if we couldn't have an input, just do a normal advance
                                Advancer.AdvanceLogic(AdvancerConfig, h, ref h.ImpulseShot, NetSnapper.TickMSTarget);
                            }
                        }
                    }

                    // even if we did impulse above, we still want to interp
                    // because we may need to create silver snapshots to use for
                    // blending or impulse later

                    // if we have a gold from the server, we don't need to do anything
                    // because the current shot is already good
                    if (h.CurrentFlag == SnapHistory<TSnap, TStatic>.FlagGold)
                        continue;

                    // (you might notice from the following, that we resimulate silver
                    //  flags [client guesses] each tick; this way they continue to update
                    //  their guesses if new server info arrives)

                    // if not, we need to create a silver for the current shot
                    // we know the previous is gold or silver already, since we checked
                    // for empty or ghost way back above

                    // so we know the prev is good for interp, but is the next?
                    // if the next is also good for interp, we'll just interp
                    h.Shots[h.CurrentIndex] = h.Shots[h.PrevIndex];
                    if (h.NextFlag == SnapHistory<TSnap, TStatic>.FlagGold
                        || h.NextFlag == SnapHistory<TSnap, TStatic>.FlagSilver)
                    {
                        Advancer.InterpTickLogic(h);
                    }
                    else
                    {
                        // otherwise, do extrapolation 
                        Advancer.AdvanceLogic(AdvancerConfig, h, ref h.Shots[h.CurrentIndex], NetSnapper.TickMSTarget);
                    }

                    // save our guess snapshot as a new silver
                    Nents.ClientSaveSimIntoCurrent(h);
                }
            }

            return noMissingImpulse;
        }
Example #20
0
        // returns false if we have no inputs
        private bool InputChecker(SnapHistory<TSnap, TStatic> h, ref TSnap snap,
            byte pid, float maxms)
        {
            TInput[] actions = Input.GetPlayerInputs(pid,
                    out int index, out int count,
                    out ushort[] timestamps, out float[] tickms);

            if (count == 0)
                return false;

            ushort simstamp = NetSnapper.SimulateTimestamp;

            // timestamps are ordered from oldest --> latest
            // loop through seeking our current timestamp
            float lastms = 0;
            TInput lastAction = new TInput();
            bool hasLastAction = false;
            for (int i = 0; i < count; i++)
            {
                // if timestamps[index] is later than our timestamp, stop
                // if timestamps[index] is earlier than our timestamp, keep going
                // if timestamps[index] is equal to our timestamp, process it
                if (timestamps[index] == simstamp)
                {
                    // if we are past the maxms we should process to, then stop
                    if (tickms[index] >= maxms)
                    {
                        break;
                    }

                    // process it
                    if (hasLastAction)
                    {
                        Advancer.InputLogic(AdvancerConfig, lastAction, h, ref snap, pid, tickms[index] - lastms);
                    }

                    hasLastAction = true;
                    lastms = tickms[index];
                    lastAction = actions[index];
                }
                else if (simstamp >= ushort.MaxValue / 2)
                {
                    if (timestamps[index] > simstamp
                        || timestamps[index] < simstamp - (ushort.MaxValue / 2))
                    {
                        // later
                        break;
                    }
                }
                else
                {
                    if (timestamps[index] > simstamp
                        && timestamps[index] < simstamp + (ushort.MaxValue / 2))
                    {
                        // later
                        break;
                    }
                }

                index++;
                if (index >= actions.Length)
                    index -= actions.Length;
            }

            // process the last action, now that we're done
            if (hasLastAction)
            {
                // we use the tickrate here bbecause this action stretches until the end
                // of the tick, since there are no subsequent actions in this tick
                Advancer.InputLogic(AdvancerConfig, lastAction, h, ref snap, pid, maxms - lastms);
            }

            return true;
        }