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); }
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; }
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); }
public bool IsOutOfOrder(ushort id) { if (Constants.UseKumoQueues) { return(Overflow.le(id, Overflow.sub(LastTickIdRead, bufferSize))); } return(Overflow.le(id, LastTickIdRead)); }
public ulong blockTimestamp(ushort blockId) { if (Overflow.ge(blockId, timestampBlockId)) { return(timestamp + (ulong)(blockId - timestampBlockId) * Constants.WorldHeartBeat); } return(timestamp - (ulong)(timestampBlockId - blockId) * Constants.WorldHeartBeat); }
protected bool isPending(ushort blockId) { if (pending.Count == 0) { return(false); } return(hasNewPacket || Overflow.sub(blockId, lastBlock) >= Constants.ResendThreshold); }
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); }
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); }
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; }
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); }
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); }
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); }
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)); }
public void ResetResolutionTable(ushort blockId) { Array.Clear(resolutionTable, 0, resolutionTable.Length); oldestResolutionBlockId = Overflow.sub(blockId, ResolutionTableSize / 2); oldestResolutionPosition = 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); } } }