Exemple #1
0
 /// <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);
     }
 }
Exemple #2
0
        /// <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);
            }
        }
Exemple #3
0
        /// <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);
        }
Exemple #4
0
        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]++;
        }
Exemple #5
0
        /// <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;
        }
Exemple #6
0
        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;
                }
            }
        }
Exemple #7
0
        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);
                    }
                }
            }
        }