Ejemplo n.º 1
0
        public void HandleServerTick(SuperPacketReader reader, SuperPacket <PQ> superpacket)
        {
            // Update sizes
            recvKbpsEstimateAcc += reader.size();
            lastRecvSize        += reader.size();
            perTickSize.AddOrUpdate(reader.tickId(), reader.size(), (key, old) => old + reader.size());

            // Check handshake status
            if (!superpacket.HasFlag(SuperPacketFlags.Handshake))
            {
                LastServerId = Overflow.max(LastServerId, reader.tickId());

                // TODO(gpascualg): Make phase sync id diff optional
                int idDiff = Overflow.signed_diff(phaseSync.TickId, LastServerId);
                ServerTimeDiff = idDiff - (estimatedRTT / 50.0f + 1); //- (int)(estimatedRTT / 2.0f);
            }
            else
            {
                // Fix phase sync, otherwise we will get a huge spike
                LastServerId = reader.tickId();
                if (serverBasedSync)
                {
                    phaseSync.FixTickId(LastServerId);
                }
            }

            // Update PLL
            phaseSync.ServerPacket(reader.tickId(), LastServerId);
        }
Ejemplo n.º 2
0
        public void ServerPacket(ushort currentID, ushort maxID)
        {
            // Multipackets should not be counted towards PLL
            if (currentID == maxID)
            {
                return;
            }

            // Get current tick time
            ulong time = TickTime + (ulong)Integrator;

            // More than one packet in between?
            ushort packetDiff = Overflow.sub(maxID, lastPacketID);

            lastPacketID = maxID;
            if (packetDiff > 1)
            {
                NextTick += (ulong)(Integrator * (packetDiff - 1));
            }

            // Phase detector
            float err = (float)((long)time - (long)NextTick);

            // Loop filter
            // Integrator = 0.999f * Integrator + err;
            float Ki = 1e-3f;

            Integrator = Ki * err + Integrator;

            // NCO
            NextTick = time + (ulong)Integrator;
        }
Ejemplo n.º 3
0
        private void read_impl(IBaseClient client, SuperPacket <PQ> superpacket, IMarshal marshal)
        {
            SuperPacketReader reader = client.popPendingSuperPacket();

            // Handshake process skips all procedures, including order
            if (reader.HasFlag(SuperPacketFlags.Handshake))
            {
                // Nothing to do here, it's a handshake packet
                // TODO(gpascualg): We don't need to add them at all
                LastTickIdRead = reader.tickId();
                return;
            }

            Debug.Assert(!IsOutOfOrder(reader.tickId()), "Should never have out of order packets");

            if (Overflow.sub(ExpectedTickId, reader.tickId()) > Constants.MaximumBlocksUntilResync)
            {
                superpacket.SetFlag(SuperPacketFlags.Handshake);
            }

            if (LastTickIdRead > reader.tickId())
            {
                loopCounter = (byte)(loopCounter + 1);
            }

            LastTickIdRead = reader.tickId();
            reader.handlePackets <PQ, IBaseClient>(this, marshal, client);
        }
Ejemplo n.º 4
0
        public bool IsOutOfOrder(ushort id)
        {
            if (Constants.UseKumoQueues)
            {
                return(Overflow.le(id, Overflow.sub(LastTickIdRead, bufferSize)));
            }

            return(Overflow.le(id, LastTickIdRead));
        }
Ejemplo n.º 5
0
        public ulong blockTimestamp(ushort blockId)
        {
            if (Overflow.ge(blockId, timestampBlockId))
            {
                return(timestamp + (ulong)(blockId - timestampBlockId) * Constants.WorldHeartBeat);
            }

            return(timestamp - (ulong)(timestampBlockId - blockId) * Constants.WorldHeartBeat);
        }
Ejemplo n.º 6
0
        protected bool isPending(ushort blockId)
        {
            if (pending.Count == 0)
            {
                return(false);
            }

            return(hasNewPacket ||
                   Overflow.sub(blockId, lastBlock) >= Constants.ResendThreshold);
        }
Ejemplo n.º 7
0
 public bool isPending(List <ushort> blocks, ushort blockId, bool force)
 {
     if (blocks.Count != 0 && blocks[blocks.Count - 1] == blockId)
     {
         return(false);
     }
     return(force ||
            blocks.Count == 0 ||
            Overflow.sub(blockId, blocks[blocks.Count - 1]) >= Constants.ResendThreshold);
 }
Ejemplo n.º 8
0
        public int Compare(ushort x, ushort y)
        {
            // Handle equality as x > y
            if (x == y)
            {
                return(1);
            }

            if (Overflow.le(x, y))
            {
                return(-1);
            }

            return(1);
        }
Ejemplo n.º 9
0
        public void scheduleAck(ushort blockId)
        {
            if (Overflow.ge(blockId, _ackBase))
            {
                ushort ackDiff = Overflow.sub(blockId, _ackBase);
                _pendingAcks = (_pendingAcks << ackDiff) | 1; // | 1 because we are acking the base
                _ackBase     = blockId;
            }
            else
            {
                ushort ackDiff = Overflow.sub(_ackBase, blockId);
                _pendingAcks = _pendingAcks | (ushort)(1 << ackDiff);
            }

            _mustAck = true;
        }
Ejemplo n.º 10
0
        public bool resolve(PacketReader packet, ushort blockId)
        {
            // Check if this is an older block id
            if (Overflow.le(blockId, oldestResolutionBlockId))
            {
                ResetResolutionTable(blockId);
                //client->flag_desync();
                return(false);
            }

            // Otherwise, it might be newer
            ushort diff = Overflow.sub(blockId, oldestResolutionBlockId);
            ushort idx  = (ushort)(Overflow.add(oldestResolutionPosition, diff) % ResolutionTableSize);

            if (diff >= ResolutionTableSize)
            {
                // We have to move oldest so that newest points to blockId
                ushort move_amount = Overflow.sub(diff, ResolutionTableDiff);
                oldestResolutionBlockId  = Overflow.add(oldestResolutionBlockId, move_amount);
                oldestResolutionPosition = (ushort)(Overflow.add(oldestResolutionPosition, move_amount) % ResolutionTableSize);

                // Fix diff so we don't overrun the new position
                idx = (ushort)(Overflow.add(oldestResolutionPosition, Overflow.sub(diff, move_amount)) % ResolutionTableSize);

                // Clean position, as it is a newer packet that hasn't been parsed yet
                resolutionTable[idx] = 0;
            }

            // Compute packet mask
            ulong mask = (ulong)(1) << packet.getCounter();

            // Get blockId position, bitmask, and compute
            if ((resolutionTable[idx] & mask) != 0)
            {
                // The packet is already in
                return(false);
            }

            resolutionTable[idx] |= mask;
            return(true);
        }
Ejemplo n.º 11
0
        public bool read(IBaseClient client, SuperPacket <PQ> superpacket, IMarshal marshal)
        {
            timestampBlockId = ExpectedTickId;
            timestamp        = DateTimeExtensions.now();

            if (!client.hasPendingSuperPackets())
            {
                if (++sinceLastRecv >= Constants.MaxBlocksUntilDisconnection)
                {
                    client.disconnect();
                    return(false);
                }

                marshal.Update(client, ExpectedTickId);
                ExpectedTickId = Overflow.inc(ExpectedTickId);
                return(false);
            }

            sinceLastRecv = 0;
            ushort expectedId = ExpectedTickId;

            if (!Constants.UseKumoQueues)
            {
                ExpectedTickId = Overflow.sub(ExpectedTickId, bufferSize);
            }

            while (client.hasPendingSuperPackets() &&
                   !Overflow.ge(client.firstSuperPacketTickId(), expectedId))
            {
                read_impl(client, superpacket, marshal);
            }

            marshal.Update(client, ExpectedTickId);
            ExpectedTickId = Overflow.inc(ExpectedTickId);
            return(true);
        }
Ejemplo n.º 12
0
        public Buffer update(ushort tickId, IBaseClient client, SuperPacket <PQ> superpacket)
        {
            ++sinceLastPing;
            if (needsPing())
            {
                sinceLastPing = 0;
                superpacket.SetFlag(SuperPacketFlags.Ping);
            }

            // TODO(gpascualg): Lock superpacket
            bool first_packet = true;

            superpacket.prepare();
            if (superpacket.finish(tickId, first_packet))
            {
                Buffer buffer = new Buffer(superpacket.getBuffer());

                // Register time for ping purposes
                ushort packetIdDiff = Overflow.sub(buffer.readUshort(2), timestampsHeadId);
                timestampsHeadPosition = Overflow.mod(Overflow.add(timestampsHeadPosition, packetIdDiff), ResolutionTableSize);
                UnityEngine.Debug.Log($"< SET {buffer.readUshort(2)} AT {timestampsHeadPosition} WITH DIFF {packetIdDiff}");
                timestamps[timestampsHeadPosition] = DateTimeExtensions.now();
                timestampsHeadId = buffer.readUshort(2);

                // Update estimate
                sendKbpsEstimateAcc += buffer.getPosition();
                lastSendSize         = (ushort)buffer.getPosition();

                first_packet = false;

                return(buffer);
            }

            lastSendSize = 0;
            return(null);
        }
Ejemplo n.º 13
0
        public bool finish(ushort tickId, bool isFirst)
        {
            _buffer.reset();

            //  First two bytes are tick id, next two id, finally 1 byte for flags
            _buffer.write(tickId);
            _buffer.write(_id);
            _buffer.write(_flags);

            // Reset if there is something we must ack
            bool hasAcks = _mustAck;

            _mustAck = false;

            // Write acks and move for next id
            // Moving acks bitset only happens if no doing handshake (ie. if incrementing id)
            _buffer.write(_ackBase);
            _buffer.write(_pendingAcks);

            //  -1 is to account for the number of blocks
            ushort remaining = (ushort)(MAX_SIZE - _buffer.getPosition() - 1 - 1);

            // During handshake/resync do not include any packets
            bool hasData = false;

            _last_left_data = false;
            if (!HasFlag(SuperPacketFlags.Handshake))
            {
                // Organize packets that must be resent until ack'ed
                SortedDictionary <uint, List <Packet> > by_block = new SortedDictionary <uint, List <Packet> >();

                _queues.process(tickId, _id, ref remaining, ref _last_left_data, by_block);

                // Write number of blocks
                _buffer.write((byte)by_block.Count);

                // Has there been any overflow? That can be detected by, for example
                //  checking max-min>thr
                hasData = by_block.Count != 0;
                if (hasData)
                {
                    uint   max       = by_block.Keys.ToList()[by_block.Count - 1]; // TODO(gpascualg): Linq .Last()?
                    ushort threshold = ushort.MaxValue / 2;

                    foreach (var key in by_block.Keys.ToList())
                    {
                        if (max - key < threshold)
                        {
                            break;
                        }

                        // Overflows are masked higher, so that they get pushed to the end
                        //  of the map
                        uint masked = (Convert.ToUInt32(1) << 16) | key;
                        by_block.Add(masked, by_block[key]);
                        by_block.Remove(key);
                    }

                    // Write in packets
                    foreach (var entry in by_block)
                    {
                        _buffer.write((ushort)Convert.ToUInt16(entry.Key & 0xffff));
                        _buffer.write((byte)entry.Value.Count);

                        UnityEngine.Assertions.Assert.AreNotEqual(entry.Value.Count, 0);
                        foreach (var packet in entry.Value)
                        {
                            if (entry.Key == _id)
                            {
                                packet.finish(_counter++);
                            }

                            _buffer.write(packet);
                        }
                    }
                }
            }

            // Increment _id for next iter and acks
            _id = Overflow.inc(_id);
            return(hasAcks || hasData || (isFirst && _flags != 0));
        }
Ejemplo n.º 14
0
 public void ResetResolutionTable(ushort blockId)
 {
     Array.Clear(resolutionTable, 0, resolutionTable.Length);
     oldestResolutionBlockId  = Overflow.sub(blockId, ResolutionTableSize / 2);
     oldestResolutionPosition = 0;
 }
Ejemplo n.º 15
0
        public void HandleAcks(ushort tickId, SuperPacketReader reader, SuperPacket <PQ> superpacket, IMarshal marshal)
        {
            // Ack packets
            foreach (ushort ack in reader.getAcks())
            {
                // Check if this ack > lastAck
                if (Kaminari.Overflow.ge(ack, processedAckBase))
                {
                    int displace = Kaminari.Overflow.sub(ack, processedAckBase);
                    processedAcks    = processedAcks << displace;
                    processedAckBase = ack;
                }

                // Now, check if the ack has already been processed
                int ackPosition = Overflow.sub(processedAckBase, ack);
                if (ackPosition >= 64)
                {
                    ackPosition      = 0;
                    processedAcks    = 0;
                    processedAckBase = ack;
                }

                // If it is already masked, it means it has already been processed
                ulong ackMask = (ulong)1 << ackPosition;
                if ((processedAcks & ackMask) > 0)
                {
                    continue;
                }
                processedAcks = processedAcks | ackMask;

                // Otherwise, let superpacker handle the ack
                superpacket.Ack(ack);

                // Update lag estimation
                if (Overflow.geq(lastConfirmedTimestampId, ack) && Overflow.sub(timestampsHeadId, lastConfirmedTimestampId) < 100)
                {
                    // TODO(gpascualg): This can be used as a connection quality estimate
                    continue;
                }

                lastConfirmedTimestampId = ack;
                ushort position = Overflow.mod(Overflow.sub(timestampsHeadPosition, Overflow.sub(timestampsHeadId, ack)), ResolutionTableSize);
                ulong  diff     = reader.Timestamp - timestamps[position];
                UnityEngine.Debug.Log($"> ACK {ack} AT {position} +{diff}");
                const float w = 0.99f;
                estimatedRTT = estimatedRTT * w + diff * (1.0f - w);
            }

            // Schedule ack if necessary
            bool is_handshake = reader.HasFlag(SuperPacketFlags.Handshake);

            if (is_handshake || reader.hasData() || reader.isPingPacket())
            {
                superpacket.scheduleAck(reader.id());
            }

            // Handle flags already
            if (is_handshake)
            {
                // Check if there was too much of a difference, in which case, flag handshake again
                // TODO(gpascualg): Remove re-handshake max diff magic number
                if (Overflow.abs_diff(reader.tickId(), tickId) > 10)
                {
                    superpacket.SetFlag(SuperPacketFlags.Handshake);
                }

                // During handshake, we update our tick to match the other side
                ExpectedTickId = reader.tickId();
                LastServerId   = reader.tickId();

                // Reset all variables related to packet parsing
                timestampBlockId = ExpectedTickId;
                timestamp        = DateTimeExtensions.now();
                loopCounter      = 0;

                // Reset marshal
                ResetResolutionTable(reader.tickId());
                marshal.Reset();

                if (!reader.HasFlag(SuperPacketFlags.Ack))
                {
                    superpacket.SetFlag(SuperPacketFlags.Ack);
                    superpacket.SetFlag(SuperPacketFlags.Handshake);
                }
            }
        }