internal void EnqueueOutgoingMessage(TimelineMessage message) { lock (_outgoingMessages) { _outgoingMessages.Enqueue(message); } }
TimelineMessage CreateSetCachedMessageFromTemplate(TimelineMessage template, ushort remoteTimelineIndex) { byte[] cacheBytes = new byte[template.Data.Length]; Buffer.BlockCopy(BitConverter.GetBytes(remoteTimelineIndex), 0, cacheBytes, 0, sizeof(ushort)); Buffer.BlockCopy(template.Data, sizeof(ushort), cacheBytes, sizeof(ushort), template.Data.Length - sizeof(ushort)); TimelineMessage cacheMessage = new TimelineMessage( _relayToCacheMessageTypes[template.MessageType], cacheBytes, template.DeliveryMode); return(cacheMessage); }
public void OnNetworkReceive(NetPeer peer, NetDataReader reader) { try { var type = reader.GetByte(); var data = reader.GetBytes(); TimelineMessage timelineMessage = new TimelineMessage((TimelineMessageType)type, data); TimelineManager.Default.ProcessIncomingMessage(timelineMessage); } catch (Exception ex) { Console.WriteLine("[Client] Exception! " + ex.Message); } }
/// <summary> /// Connect a peer to a timeline. For internal use only. /// </summary> /// <param name="peerInfo">The peer which wants to connect to a timeline.</param> /// <param name="remoteTimelineIndex">The index used by the peer to refer to the timeline.</param> /// <param name="timelineId">The unique ID of the timeline.</param> void ConnectPeerToTimeline(PeerInfo peerInfo, ushort remoteTimelineIndex, byte[] timelineId) { // Check if timeline exists first. If not, create it. TimelineInfo timelineInfo; if (_timelinesById.ContainsKey(timelineId)) { timelineInfo = _timelinesById[timelineId]; if (TimelineUpdated != null) { TimelineUpdated(timelineInfo.ConnectedPeers.Count + 1, timelineId); } } else { timelineInfo = new TimelineInfo(); timelineInfo.ID = timelineId; _timelinesById.Add(timelineId, timelineInfo); if (TimelineCreated != null) { TimelineCreated(timelineId); } } timelineInfo.ConnectedPeers[peerInfo] = remoteTimelineIndex; peerInfo.ConnectedTimelines[remoteTimelineIndex] = timelineInfo; // Send cached messages if there are any foreach (TimelineMessage cachedMessage in timelineInfo.CachedEntries) { TimelineMessage relayMessage = CreateSetCachedMessageFromTemplate( cachedMessage, remoteTimelineIndex); peerInfo.OutgoingMessages.Enqueue(relayMessage); } if (TimelineConnected != null) { TimelineConnected(peerInfo.Index, remoteTimelineIndex, timelineId); } }
/// <summary> /// Connect a peer to the timeline synchronizer /// </summary> /// <param name="peerIndex">Index number for the peer that connected.</param> /// <param name="rtt">round trip time between the peer and the timeline synchronizer</param> public void ConnectPeer(ushort peerIndex, float rtt = 0f) { lock (_peerLock) { if (!_peersByIndex.ContainsKey(peerIndex)) { PeerInfo peerInfo = new PeerInfo() { Index = peerIndex, }; _peersByIndex.Add(peerIndex, peerInfo); // InitializePeer: // 8 bytes - initial time TimelineMessage initMessage = new TimelineMessage( TimelineMessageType.InitializePeer, BitConverter.GetBytes(_now + rtt / 2)); peerInfo.OutgoingMessages.Enqueue(initMessage); // do a quick ping TimelineMessage pingMessage = new TimelineMessage(TimelineMessageType.ClockSyncPing, BitConverter.GetBytes(_now), DeliveryMode.Unreliable); lock (_peerLock) { peerInfo.OutgoingMessages.Enqueue(pingMessage); } if (PeerConnected != null) { PeerConnected(peerIndex); } } } return; }
void PingPeers() { HashSet <PeerInfo> peerInfos; _pinging = true; lock (_peerLock) { peerInfos = new HashSet <PeerInfo>(_peersByIndex.Values); } foreach (PeerInfo peerInfo in peerInfos) { if (!_pinging) { return; } /*if (peerInfo.Pinging) * continue;*/ TimelineMessage pingMessage = new TimelineMessage(TimelineMessageType.ClockSyncPing, BitConverter.GetBytes(_now), DeliveryMode.Unreliable); lock (_peerLock) { peerInfo.OutgoingMessages.Enqueue(pingMessage); peerInfo.Pinging = true; } //Thread.Sleep(1); } _pinging = false; _pingThread = null; }
public void OnNetworkReceive(NetPeer peer, NetDataReader reader) { //echo //peer.Send(reader.Data, SendOptions.ReliableUnordered); Console.WriteLine("[Server] NetworkReceive: "); try { ushort peerIndx = 0; foreach (var peerConection in TimelineServer._peerConnections) { if (peerConection.Value.ConnectId == peer.ConnectId) { peerIndx = peerConection.Key; } } var type = reader.GetByte(); var data = reader.GetBytes(); TimelineMessage timelineMessage = new TimelineMessage((TimelineMessageType)type, data); TimelineServer.TimelineSynchronizer.ProcessIncomingMessage(peerIndx, timelineMessage); TimelineManager.Default.ProcessIncomingMessage(timelineMessage); } catch (Exception ex) { Console.WriteLine("[Server] Exception! " + ex.Message); } //fragment log if (reader.AvailableBytes == 13218) { Console.WriteLine("[Server] TestFrag: {0}, {1}", reader.Data[0], reader.Data[13217]); } }
/// <summary> /// Process incoming messages depending upon the message type /// </summary> public void ProcessIncomingMessage(TimelineMessage message) { if (message.MessageType == TimelineMessageType.SetAbsolute || message.MessageType == TimelineMessageType.SetCachedAbsolute) { // message format: // 2 bytes (ushort) TimelineIndex // 8 bytes (double) time // n bytes (byte[]) timelineData ushort timelineIndex = BitConverter.ToUInt16(message.Data, 0); lock (_timelineLock) { if (_timelinesByIndex.ContainsKey(timelineIndex)) { double time = BitConverter.ToDouble(message.Data, sizeof(ushort)); int valueBytesLength = message.Data.Length - sizeof(ushort) - sizeof(double); byte[] valueBytes = new byte[valueBytesLength]; Array.Copy(message.Data, sizeof(ushort) + sizeof(double), valueBytes, 0, valueBytesLength); _timelinesByIndex[timelineIndex].RemoteSet(time, valueBytes, true, message.MessageType == TimelineMessageType.SetCachedAbsolute); } } } else if (message.MessageType == TimelineMessageType.InitializePeer) { // message format: // 8 bytes (double) time // initial synchronizing of time to the synchronizer _now = BitConverter.ToDouble(message.Data, 0); lock (_timelineLock) { foreach (var timeline in _timelinesByIndex.Values) { timeline._now = _now; } } } else if (message.MessageType == TimelineMessageType.ClockSyncPing) { // message from synchronizer with current time on synchonizer // message format 8 bytes (double) // need to reply with synchronizer time and current local time (16 bytes) byte[] data = new byte[sizeof(double) + sizeof(double)]; BinaryWriter writer = new BinaryWriter(new MemoryStream(data)); writer.Write(message._data); writer.Write(_now - _timeOffset); writer.Close(); EnqueueOutgoingMessage(new TimelineMessage( TimelineMessageType.ClockSyncPong, data, DeliveryMode.Unreliable)); } else if (message.MessageType == TimelineMessageType.ClockSyncCorrection) { // message from synchronizer with adjustment to current time // message format 4 bytes (float) _targetOffset = BitConverter.ToSingle(message.Data, 0); } }
/// <summary> /// Process a message from one of the peers /// </summary> /// <param name="peerIndex">Index number for the peer.</param> /// <param name="message">The message to be processed.</param> public void ProcessIncomingMessage(ushort peerIndex, TimelineMessage message) { lock (_peerLock) { lock (_timelineLock) { PeerInfo peerInfo = null; if (!_peersByIndex.TryGetValue(peerIndex, out peerInfo)) { return; } peerInfo.BytesToTLS += message.Data.Length; // Be sure to arrange the message types in descending order of frequency. // For example, sets are most frequent, so they are checked first. if (message.MessageType == TimelineMessageType.RelayAbsolute) { BinaryReader reader = new BinaryReader(new MemoryStream(message.Data)); // We only need to read the timeline index, since we are only relaying, not receiving. ushort timelineIndex = reader.ReadUInt16(); reader.Close(); // Get the timeline this message was intended for. TimelineInfo timelineInfo = null; if (!peerInfo.ConnectedTimelines.TryGetValue(timelineIndex, out timelineInfo)) { return; } // Add message to cache queue timelineInfo.CachedEntries.Add(message); if (TimelineSet != null) { TimelineSet(timelineIndex, timelineInfo.ID); } while (timelineInfo.CachedEntries.Count > timelineInfo.EntryCacheSize) { timelineInfo.CachedEntries.RemoveAt(0); } // Loop through all the subscribers except for the sender... foreach (var otherPeer in timelineInfo.ConnectedPeers) { if (peerInfo == otherPeer.Key) { continue; } // Create a new message to relay. TimelineMessage relayMessage = CreateRelayMessageFromTemplate(message, otherPeer.Value); otherPeer.Key.OutgoingMessages.Enqueue(relayMessage); } } else if (message.MessageType == TimelineMessageType.ClockSyncPong) { BinaryReader reader = new BinaryReader(new MemoryStream(message.Data)); double localPingTime = reader.ReadDouble(); double remotePongTime = reader.ReadDouble(); double localPongTime = (localPingTime + _now) / 2; reader.Close(); ClockSyncData newData = new ClockSyncData() { RTT = (float)(_now - localPingTime), Offset = (float)(localPongTime - remotePongTime) }; AddClockSyncData(peerInfo, newData); float correction = GetClockSyncCorrection(peerInfo); TimelineMessage correctionMessage = new TimelineMessage(TimelineMessageType.ClockSyncCorrection, BitConverter.GetBytes(correction), DeliveryMode.Unreliable); peerInfo.OutgoingMessages.Enqueue(correctionMessage); peerInfo.Pinging = false; } else if (message.MessageType == TimelineMessageType.ConnectTimeline) { // Message format: // 2 bytes (ushort) remote timeline index // byte array (byte[]) timeline id BinaryReader reader = new BinaryReader(new MemoryStream(message.Data)); ushort remoteTimelineIndex = reader.ReadUInt16(); byte[] timelineId = reader.ReadBytes(message.Data.Length - sizeof(ushort)); reader.Close(); ConnectPeerToTimeline(peerInfo, remoteTimelineIndex, timelineId); } else if (message.MessageType == TimelineMessageType.DisconnectTimeline) { // Call disconnect method BinaryReader reader = new BinaryReader(new MemoryStream(message.Data)); ushort remoteTimelineIndex = reader.ReadUInt16(); reader.Close(); TimelineInfo timelineInfo; if (peerInfo.ConnectedTimelines.TryGetValue(remoteTimelineIndex, out timelineInfo)) { DisconnectPeerFromTimeline(peerInfo, timelineInfo); } } else if (message.MessageType == TimelineMessageType.CacheSize) { BinaryReader reader = new BinaryReader(new MemoryStream(message.Data)); ushort remoteTimelineIndex = reader.ReadUInt16(); ushort cacheSize = reader.ReadUInt16(); reader.Close(); TimelineInfo timelineInfo; if (peerInfo.ConnectedTimelines.TryGetValue(remoteTimelineIndex, out timelineInfo)) { timelineInfo.EntryCacheSize = cacheSize; while (timelineInfo.CachedEntries.Count > cacheSize) { timelineInfo.CachedEntries.RemoveAt(0); } } } } } }