/// <summary> /// Disconnects the entire Mx client group, disconnecting /// all clients inside it. /// </summary> /// <param name="clientGroup">The client group.</param> public void Disconnect(MxClientGroup clientGroup) { foreach (var client in clientGroup.RealtimeClients.ToArray()) { Disconnect(client); } }
/// <summary> /// Queue a packet for sending to the specified endpoint. /// </summary> /// <param name="group"> /// The group to send the message to. /// </param> /// <param name="packet"> /// The associated data to send. /// </param> /// <param name="reliable"> /// Whether or not this message should be sent reliably and intact. This also /// permits messages larger than 512 bytes to be sent. /// </param> public void Send(MxClientGroup group, byte[] packet, bool reliable = false) { if (group.Identifier == MxClientGroup.Ungrouped) { throw new InvalidOperationException( "You must group clients before sending packets to them. Either " + "call PlaceInGroup immediately after Connect, or call PlaceInGroup " + "in the ClientConnected handler."); } foreach (var client in group.RealtimeClients) { Send(client, packet, reliable); } }
/// <summary> /// Places the specified Mx client in the specified group. /// </summary> /// <param name="client">The Mx client.</param> /// <param name="identifier">The group identifier.</param> public MxClientGroup PlaceInGroup(MxClient client, string identifier) { if (client.Group.Identifier == identifier) { return(client.Group); } if (!_mxClientGroups.ContainsKey(identifier)) { _mxClientGroups[identifier] = new MxClientGroup(this, identifier); } var reliability = client.Group.ReliableClients.First(x => x.Client == client); client.Group.RealtimeClients.Remove(client); client.Group.ReliableClients.Remove(reliability); client.Group = _mxClientGroups[identifier]; client.Group.RealtimeClients.Add(client); client.Group.ReliableClients.Add(reliability); return(client.Group); }
public void Send <T>(MxDispatcher dispatcher, MxClientGroup target, T message, bool reliable = false) { if (target.RealtimeClients.Count == 0) { throw new InvalidOperationException( "Attempted to send message to group " + target + ", but it has no clients."); } var serialized = _networkMessageSerialization.Serialize(message); dispatcher.Send(target, serialized, reliable); var type = typeof(T); if (!_currentNetworkFrame.BytesSentByMessageType.ContainsKey(type)) { _currentNetworkFrame.BytesSentByMessageType[type] = 0; _currentNetworkFrame.MessagesSentByMessageType[type] = 0; } _currentNetworkFrame.BytesSentByMessageType[type] += serialized.Length; _currentNetworkFrame.MessagesSentByMessageType[type]++; }
/// <summary> /// Initializes a new instance of the <see cref="MxClient"/> class. /// </summary> /// <param name="dispatcher"> /// The dispatcher that is creating this client. /// </param> /// <param name="mxClientGroup"></param> /// <param name="target"> /// The target endpoint for the Mx client. /// </param> /// <param name="sharedUdpClient"> /// The shared UDP client with which to send messages. /// </param> public MxClient(MxDispatcher dispatcher, MxClientGroup mxClientGroup, IPEndPoint target, UdpClient sharedUdpClient) { _dispatcher = dispatcher; Group = mxClientGroup; _targetEndPoint = target; _sharedUdpClient = sharedUdpClient; _receivedPackets = new Queue <byte[]>(); _pendingRealtimeSendPackets = new Queue <byte[]>(); _pendingReliableSendPackets = new Queue <byte[]>(); _lastCall = DateTime.Now; _deltaTime = 1000.0 / 30.0; // Initialize connection information. _disconnectAccumulator = 0; _disconnectLimit = 900; _disconnectWarningLimit = 30; _receiveQueue = new List <bool>(); for (var i = 0; i < 32; i++) { _receiveQueue.Add(false); } _rttQueue = new List <ulong>(); _sendQueue = new Dictionary <uint, ulong>(); _sendMessageQueue = new Dictionary <uint, KeyValuePair <uint, byte[][]> >(); _localSequenceNumber = 0; _remoteSequenceNumber = uint.MaxValue; _sendAccumulator = 0; _rttThreshold = 250.0; _fcIsGoodSendMode = false; _fcPenaltyTime = 4.0; _fcGoodConditionsTime = 0; _fcPenaltyReductionAccumulator = 0; HasReceivedPacket = false; }
private void AssignSyncDataToMessage(List <SynchronisedData> dataList, EntityPropertiesMessage message, int frameTick, MxClientGroup endpoint, out bool mustBeReliable) { mustBeReliable = false; var totalString = 0; var currentString = 0; var totalInt16 = 0; var currentInt16 = 0; var totalInt32 = 0; var currentInt32 = 0; var totalSingle = 0; var currentSingle = 0; var totalDouble = 0; var currentDouble = 0; var totalBoolean = 0; var currentBoolean = 0; var totalSingleArray = 0; var currentSingleArray = 0; var totalTransform = 0; var currentTransform = 0; var typeLookup = new Dictionary <int, int>(); for (var i = 0; i < dataList.Count; i++) { if (dataList[i].CurrentValue == null) { typeLookup[i] = EntityPropertiesMessage.PropertyTypeNull; continue; } else if (dataList[i].CurrentValue is string) { typeLookup[i] = EntityPropertiesMessage.PropertyTypeString; totalString += 1; } else if (dataList[i].CurrentValue is short) { typeLookup[i] = EntityPropertiesMessage.PropertyTypeInt16; totalInt16 += 1; } else if (dataList[i].CurrentValue is int) { typeLookup[i] = EntityPropertiesMessage.PropertyTypeInt32; totalInt32 += 1; } else if (dataList[i].CurrentValue is float) { typeLookup[i] = EntityPropertiesMessage.PropertyTypeSingle; totalSingle += 1; } else if (dataList[i].CurrentValue is double) { typeLookup[i] = EntityPropertiesMessage.PropertyTypeDouble; totalDouble += 1; } else if (dataList[i].CurrentValue is bool) { typeLookup[i] = EntityPropertiesMessage.PropertyTypeBoolean; totalBoolean += 1; } else if (dataList[i].CurrentValue is Microsoft.Xna.Framework.Vector2) { typeLookup[i] = EntityPropertiesMessage.PropertyTypeVector2; totalSingleArray += 2; } else if (dataList[i].CurrentValue is Microsoft.Xna.Framework.Vector3) { typeLookup[i] = EntityPropertiesMessage.PropertyTypeVector3; totalSingleArray += 3; } else if (dataList[i].CurrentValue is Microsoft.Xna.Framework.Vector4) { typeLookup[i] = EntityPropertiesMessage.PropertyTypeVector4; totalSingleArray += 4; } else if (dataList[i].CurrentValue is Microsoft.Xna.Framework.Quaternion) { typeLookup[i] = EntityPropertiesMessage.PropertyTypeQuaternion; totalSingleArray += 4; } else if (dataList[i].CurrentValue is Microsoft.Xna.Framework.Matrix) { typeLookup[i] = EntityPropertiesMessage.PropertyTypeMatrix; totalSingleArray += 16; } else if (dataList[i].CurrentValue is Protogame.NetworkTransform) { typeLookup[i] = EntityPropertiesMessage.PropertyTypeTransform; totalTransform += 1; } else { throw new NotSupportedException("The type " + dataList[i].CurrentValue + " can not be synchronised as a network property."); } } if (totalString > 0) { message.PropertyValuesString = new string[totalString]; } if (totalInt16 > 0) { message.PropertyValuesInt16 = new short[totalInt16]; } if (totalInt32 > 0) { message.PropertyValuesInt32 = new int[totalInt32]; } if (totalSingle > 0) { message.PropertyValuesSingle = new float[totalSingle]; } if (totalDouble > 0) { message.PropertyValuesDouble = new double[totalDouble]; } if (totalBoolean > 0) { message.PropertyValuesBoolean = new bool[totalBoolean]; } if (totalSingleArray > 0) { message.PropertyValuesSingleArray = new float[totalSingleArray]; } if (totalTransform > 0) { message.PropertyValuesTransform = new Protogame.NetworkTransform[totalTransform]; } for (var ix = 0; ix < dataList.Count; ix++) { message.PropertyNames[ix] = dataList[ix].Name; message.PropertyTypes[ix] = typeLookup[ix]; // Update synchronisation data. dataList[ix].LastFrameSynced[endpoint] = frameTick; if (!dataList[ix].HasPerformedInitialSync.GetOrDefault(endpoint)) { dataList[ix].HasPerformedInitialSync[endpoint] = true; mustBeReliable = true; } object currentValue = dataList[ix].CurrentValue; switch (typeLookup[ix]) { case EntityPropertiesMessage.PropertyTypeNull: // Do nothing. break; case EntityPropertiesMessage.PropertyTypeString: { string value = (string)currentValue; message.PropertyValuesString[currentString++] = value; } break; case EntityPropertiesMessage.PropertyTypeInt16: { short value = (short)currentValue; message.PropertyValuesInt16[currentInt16++] = value; } break; case EntityPropertiesMessage.PropertyTypeInt32: { int value = (int)currentValue; message.PropertyValuesInt32[currentInt32++] = value; } break; case EntityPropertiesMessage.PropertyTypeSingle: { float value = (float)currentValue; message.PropertyValuesSingle[currentSingle++] = value; } break; case EntityPropertiesMessage.PropertyTypeDouble: { double value = (double)currentValue; message.PropertyValuesDouble[currentDouble++] = value; } break; case EntityPropertiesMessage.PropertyTypeBoolean: { bool value = (bool)currentValue; message.PropertyValuesBoolean[currentBoolean++] = value; } break; case EntityPropertiesMessage.PropertyTypeVector2: { var value = ConvertToVector2(currentValue); message.PropertyValuesSingleArray[currentSingleArray++] = value[0]; message.PropertyValuesSingleArray[currentSingleArray++] = value[1]; } break; case EntityPropertiesMessage.PropertyTypeVector3: { var value = ConvertToVector3(currentValue); message.PropertyValuesSingleArray[currentSingleArray++] = value[0]; message.PropertyValuesSingleArray[currentSingleArray++] = value[1]; message.PropertyValuesSingleArray[currentSingleArray++] = value[2]; } break; case EntityPropertiesMessage.PropertyTypeVector4: { var value = ConvertToVector4(currentValue); message.PropertyValuesSingleArray[currentSingleArray++] = value[0]; message.PropertyValuesSingleArray[currentSingleArray++] = value[1]; message.PropertyValuesSingleArray[currentSingleArray++] = value[2]; message.PropertyValuesSingleArray[currentSingleArray++] = value[3]; } break; case EntityPropertiesMessage.PropertyTypeQuaternion: { var value = ConvertToQuaternion(currentValue); message.PropertyValuesSingleArray[currentSingleArray++] = value[0]; message.PropertyValuesSingleArray[currentSingleArray++] = value[1]; message.PropertyValuesSingleArray[currentSingleArray++] = value[2]; message.PropertyValuesSingleArray[currentSingleArray++] = value[3]; } break; case EntityPropertiesMessage.PropertyTypeMatrix: { var value = ConvertToMatrix(currentValue); message.PropertyValuesSingleArray[currentSingleArray++] = value[0]; message.PropertyValuesSingleArray[currentSingleArray++] = value[1]; message.PropertyValuesSingleArray[currentSingleArray++] = value[2]; message.PropertyValuesSingleArray[currentSingleArray++] = value[3]; message.PropertyValuesSingleArray[currentSingleArray++] = value[4]; message.PropertyValuesSingleArray[currentSingleArray++] = value[5]; message.PropertyValuesSingleArray[currentSingleArray++] = value[6]; message.PropertyValuesSingleArray[currentSingleArray++] = value[7]; message.PropertyValuesSingleArray[currentSingleArray++] = value[8]; message.PropertyValuesSingleArray[currentSingleArray++] = value[9]; message.PropertyValuesSingleArray[currentSingleArray++] = value[10]; message.PropertyValuesSingleArray[currentSingleArray++] = value[11]; message.PropertyValuesSingleArray[currentSingleArray++] = value[12]; message.PropertyValuesSingleArray[currentSingleArray++] = value[13]; message.PropertyValuesSingleArray[currentSingleArray++] = value[14]; message.PropertyValuesSingleArray[currentSingleArray++] = value[15]; } break; case EntityPropertiesMessage.PropertyTypeTransform: { Protogame.NetworkTransform value = (Protogame.NetworkTransform)currentValue; message.PropertyValuesTransform[currentTransform++] = value; } break; } } }
private void AssignMessageToSyncData(EntityPropertiesMessage message, Dictionary <string, SynchronisedData> fullDataList, MxClientGroup endpoint) { var currentString = 0; var currentInt16 = 0; var currentInt32 = 0; var currentSingle = 0; var currentDouble = 0; var currentBoolean = 0; var currentSingleArray = 0; var currentTransform = 0; for (var i = 0; i < message.PropertyNames.Length; i++) { if (!fullDataList.ContainsKey(message.PropertyNames[i])) { continue; } var syncData = fullDataList[message.PropertyNames[i]]; var hasValue = false; object value = null; if (message.MessageOrder <= syncData.LastMessageOrder) { // This property is already at a later version. continue; } switch (message.PropertyTypes[i]) { case EntityPropertiesMessage.PropertyTypeNone: break; case EntityPropertiesMessage.PropertyTypeNull: value = null; hasValue = true; syncData.HasReceivedInitialSync[endpoint] = true; break; case EntityPropertiesMessage.PropertyTypeString: { value = message.PropertyValuesString[currentString]; hasValue = true; syncData.HasReceivedInitialSync[endpoint] = true; currentString++; break; } case EntityPropertiesMessage.PropertyTypeInt16: { value = message.PropertyValuesInt16[currentInt16]; hasValue = true; syncData.HasReceivedInitialSync[endpoint] = true; currentInt16++; break; } case EntityPropertiesMessage.PropertyTypeInt32: { value = message.PropertyValuesInt32[currentInt32]; hasValue = true; syncData.HasReceivedInitialSync[endpoint] = true; currentInt32++; break; } case EntityPropertiesMessage.PropertyTypeSingle: { value = message.PropertyValuesSingle[currentSingle]; hasValue = true; syncData.HasReceivedInitialSync[endpoint] = true; currentSingle++; break; } case EntityPropertiesMessage.PropertyTypeDouble: { value = message.PropertyValuesDouble[currentDouble]; hasValue = true; syncData.HasReceivedInitialSync[endpoint] = true; currentDouble++; break; } case EntityPropertiesMessage.PropertyTypeBoolean: { value = message.PropertyValuesBoolean[currentBoolean]; hasValue = true; syncData.HasReceivedInitialSync[endpoint] = true; currentBoolean++; break; } case EntityPropertiesMessage.PropertyTypeVector2: { value = ConvertFromVector2(message.PropertyValuesSingleArray, currentSingleArray); hasValue = true; syncData.HasReceivedInitialSync[endpoint] = true; currentSingleArray += 2; break; } case EntityPropertiesMessage.PropertyTypeVector3: { value = ConvertFromVector3(message.PropertyValuesSingleArray, currentSingleArray); hasValue = true; syncData.HasReceivedInitialSync[endpoint] = true; currentSingleArray += 3; break; } case EntityPropertiesMessage.PropertyTypeVector4: { value = ConvertFromVector4(message.PropertyValuesSingleArray, currentSingleArray); hasValue = true; syncData.HasReceivedInitialSync[endpoint] = true; currentSingleArray += 4; break; } case EntityPropertiesMessage.PropertyTypeQuaternion: { value = ConvertFromQuaternion(message.PropertyValuesSingleArray, currentSingleArray); hasValue = true; syncData.HasReceivedInitialSync[endpoint] = true; currentSingleArray += 4; break; } case EntityPropertiesMessage.PropertyTypeMatrix: { value = ConvertFromMatrix(message.PropertyValuesSingleArray, currentSingleArray); hasValue = true; syncData.HasReceivedInitialSync[endpoint] = true; currentSingleArray += 16; break; } case EntityPropertiesMessage.PropertyTypeTransform: { value = message.PropertyValuesTransform[currentTransform]; hasValue = true; syncData.HasReceivedInitialSync[endpoint] = true; currentTransform++; break; } } if (hasValue) { syncData.LastValueFromServer = value; syncData.LastMessageOrder = message.MessageOrder; if (syncData.TimeMachine == null) { syncData.SetValueDelegate(value); } else { syncData.TimeMachine.Set(message.FrameTick, value); } } } }