public bool ReceiveMessage(ComponentizedEntity entity, IGameContext gameContext, IUpdateContext updateContext, MxDispatcher dispatcher, MxClient server,
                                   byte[] payload, uint protocolId)
        {
            if (!_enabled)
            {
                return(false);
            }

            if (_uniqueIdentifierForEntity == null)
            {
                return(false);
            }

            var propertyMessage = _networkMessageSerialization.Deserialize(payload) as EntityPropertiesMessage;

            if (propertyMessage == null || propertyMessage.EntityID != _uniqueIdentifierForEntity.Value)
            {
                return(false);
            }

            // If the entity is a synchronised entity, collect properties of the synchronised object
            // directly.
            var synchronisedEntity = entity as ISynchronisedObject;

            if (synchronisedEntity != null)
            {
                _synchronisationContext = synchronisedEntity;
                _synchronisationContext.DeclareSynchronisedProperties(this);
            }

            // Iterate through all the components on the entity and get their synchronisation data as well.
            foreach (var synchronisedComponent in entity.Components.OfType <ISynchronisedObject>())
            {
                _synchronisationContext = synchronisedComponent;
                _synchronisationContext.DeclareSynchronisedProperties(this);
            }

            AssignMessageToSyncData(propertyMessage, _synchronisedData, server.Group);
            return(true);
        }
        private void PrepareAndTransmitSynchronisation(ComponentizedEntity entity, int tick, bool isFromClient, ClientAuthoritiveMode clientAuthoritiveMode)
        {
            if (!_uniqueIdentifierForEntity.HasValue)
            {
                throw new InvalidOperationException("PrepareAndTransmit should not be called without an entity ID!");
            }

            // If the entity is a synchronised entity, collect properties of the synchronised object
            // directly.
            var synchronisedEntity = entity as ISynchronisedObject;

            if (synchronisedEntity != null)
            {
                _synchronisationContext = synchronisedEntity;
                _synchronisationContext.DeclareSynchronisedProperties(this);
            }

            // Iterate through all the components on the entity and get their synchronisation data as well.
            foreach (var synchronisedComponent in entity.Components.OfType <ISynchronisedObject>())
            {
                _synchronisationContext = synchronisedComponent;
                _synchronisationContext.DeclareSynchronisedProperties(this);
            }

            // Sync properties to each client.
            foreach (var dispatcher in _networkEngine.CurrentDispatchers)
            {
                foreach (var group in dispatcher.ValidClientGroups)
                {
                    if (ClientAuthoritiveMode != ClientAuthoritiveMode.None &&
                        ClientOwnership != null &&
                        OnlySendToAuthoritiveClient)
                    {
                        if (ClientOwnership != group)
                        {
                            // This client doesn't own the entity, and this entity is only
                            // synchronised with clients that own it.
                            continue;
                        }
                    }

                    if (isFromClient || _clientsEntityIsKnownOn.Contains(group))
                    {
                        if (_synchronisedData.Count > 0)
                        {
                            // Now calculate the delta to transmit over the network.
                            var currentTick = tick; // TODO: Use TimeTick
                            _synchronisedDataToTransmit.Clear();
                            foreach (var data in _synchronisedData.Values)
                            {
                                var needsSync = false;

                                // Check the target for this data to see whether or not we send it to
                                // this particular client.
                                if (data.SynchronisationTargets == SynchroniseTargets.OwningClient &&
                                    (ClientOwnership == null ||
                                     !(ClientOwnership == group)))
                                {
                                    // This data should only be synchronised to the owning client, and
                                    // we are not the owning client.
                                    continue;
                                }
                                else if (data.SynchronisationTargets == SynchroniseTargets.NonOwningClients &&
                                         (ClientOwnership == null ||
                                          ClientOwnership == group))
                                {
                                    // This data should only be synchronised to non-owning clients, and
                                    // we either are the owning client, or no client ownership has been set.
                                    continue;
                                }

                                // If we're on the client and we haven't had an initial piece of data from the server,
                                // we never synchronise because we don't know what the initial value is.
                                if (isFromClient && !data.HasReceivedInitialSync.GetOrDefault(group, false))
                                {
                                    continue;
                                }

                                // If we haven't performed the initial synchronisation, we always transmit the data.
                                if (!data.HasPerformedInitialSync.GetOrDefault(group, false))
                                {
                                    _networkEngine.LogSynchronisationEvent(
                                        "Must send property '" + data.Name + "' on entity ID " + _uniqueIdentifierForEntity +
                                        " because the endpoint " + group + " has not received it's initial sync.");
                                    needsSync = true;
                                }

                                // If we are on the client (i.e. the client assumes it's authoritive), or if the
                                // server knows that the client does not have authority, then allow this next section.
                                // Or to put it another way, if we're not on the client and we know the client has
                                // authority, only transmit data for the first time because the client will make
                                // decisions from that point onwards.
                                if (isFromClient || clientAuthoritiveMode != ClientAuthoritiveMode.TrustClient ||
                                    (clientAuthoritiveMode == ClientAuthoritiveMode.TrustClient &&
                                     group != ClientOwnership))
                                {
                                    var lastValue    = data.LastValue;
                                    var currentValue = data.CurrentValue;

                                    if (lastValue is ITransform)
                                    {
                                        throw new InvalidOperationException(
                                                  "Last value property got stored as ITransform, but should have been stored as NetworkTransform!");
                                    }

                                    if (currentValue is ITransform)
                                    {
                                        currentValue = ((ITransform)currentValue).SerializeToNetwork();
                                    }

                                    if (!Equals(lastValue, currentValue))
                                    {
                                        if (data.LastFrameSynced.GetOrDefault(group, 0) + data.FrameInterval < currentTick)
                                        {
                                            _networkEngine.LogSynchronisationEvent(
                                                "Sending property '" + data.Name + "' on entity ID " + _uniqueIdentifierForEntity +
                                                " because the value has changed (old value: " + lastValue + ", new value: " + currentValue + ")," +
                                                " and the next frame synced target for group " + group + "" +
                                                " is " + (data.LastFrameSynced.GetOrDefault(group, 0) + data.FrameInterval) + "" +
                                                " and the current tick is " + currentTick + ".");
                                            needsSync = true;
                                        }
                                    }
                                }

                                if (needsSync)
                                {
                                    _synchronisedDataToTransmit.Add(data);
                                }
                            }

                            if (_synchronisedDataToTransmit.Count > 0)
                            {
                                // Build up the synchronisation message.
                                var message = new EntityPropertiesMessage();
                                message.EntityID        = _uniqueIdentifierForEntity.Value;
                                message.FrameTick       = currentTick;
                                message.PropertyNames   = new string[_synchronisedDataToTransmit.Count];
                                message.PropertyTypes   = new int[_synchronisedDataToTransmit.Count];
                                message.IsClientMessage = isFromClient;
                                message.MessageOrder    = _messageOrder++;

                                bool reliable;
                                AssignSyncDataToMessage(_synchronisedDataToTransmit, message, currentTick, group, out reliable);

                                // Send an entity properties message to the client.
                                _networkEngine.Send(
                                    dispatcher,
                                    group,
                                    message,
                                    reliable);
                            }
                        }
                    }
                }
            }
        }
        public bool ReceiveMessage(ComponentizedEntity entity, IServerContext serverContext, IUpdateContext updateContext, MxDispatcher dispatcher, MxClient client, byte[] payload, uint protocolId)
        {
            if (!_enabled)
            {
                return(false);
            }

            if (_uniqueIdentifierForEntity == null)
            {
                return(false);
            }

            // See what kind of messages we accept, based on the client authority.
            switch (ClientAuthoritiveMode)
            {
            case ClientAuthoritiveMode.None:
                // We don't accept any client data about this entity, so ignore it.
                return(false);

            case ClientAuthoritiveMode.TrustClient:
            {
                // Check to see if the message is coming from a client that has authority.
                if (ClientOwnership != null && ClientOwnership != client.Group)
                {
                    // We don't trust this message.
                    return(false);
                }

                // We trust the client, so process this information like a client would.
                var propertyMessage = _networkMessageSerialization.Deserialize(payload) as EntityPropertiesMessage;

                if (propertyMessage == null || propertyMessage.EntityID != _uniqueIdentifierForEntity.Value)
                {
                    return(false);
                }

                // If the entity is a synchronised entity, collect properties of the synchronised object
                // directly.
                var synchronisedEntity = entity as ISynchronisedObject;
                if (synchronisedEntity != null)
                {
                    _synchronisationContext = synchronisedEntity;
                    _synchronisationContext.DeclareSynchronisedProperties(this);
                }

                // Iterate through all the components on the entity and get their synchronisation data as well.
                foreach (var synchronisedComponent in entity.Components.OfType <ISynchronisedObject>())
                {
                    _synchronisationContext = synchronisedComponent;
                    _synchronisationContext.DeclareSynchronisedProperties(this);
                }

                AssignMessageToSyncData(propertyMessage, _synchronisedData, client.Group);
                return(true);
            }

            case ClientAuthoritiveMode.ReplayInputs:
                // We don't implement this yet, but we don't want to allow client packets to cause
                // a server error, so silently consume it.
                return(false);
            }

            return(false);
        }