Example #1
0
        /// <summary>
        /// Check synchronized part of scene for changes in GameObjects, Components or Resources.
        /// </summary>
        internal void TrackChanges()
        {
            // Create lists of resources and components that have pending requests
            List <NameTypePair> blockedResources  = new List <NameTypePair>();
            List <NameTypePair> blockedComponents = new List <NameTypePair>();

            // Check pending requests for components and resources
            foreach (Request request in requestQueue.ToArray())
            {
                switch (request.messageType)
                {
                // Check if request is pending for resource
                case Request.RESOURCE:
                    if (request.resource != null)
                    {
                        blockedResources.Add(new NameTypePair(request.name, request.resource.commonType));
                    }
                    else if (request.resourceType != null)
                    {
                        blockedResources.Add(new NameTypePair(request.name, request.resourceType));
                    }
                    break;

                // Check if request is pending for component
                case Request.COMPONENT:
                    if (request.components != null)
                    {
                        // Component requests can contain multiple components
                        foreach (AbstractComponent component in request.components)
                        {
                            blockedComponents.Add(new NameTypePair(request.name, component.commonType));
                        }
                    }
                    else if (request.componentTypes != null)
                    {
                        // Component requests can contain multiple components
                        foreach (Type type in request.componentTypes)
                        {
                            blockedComponents.Add(new NameTypePair(request.name, type));
                        }
                    }
                    break;
                }
            }

            // Add untracked GameObjects to ObjectStore as Objects without Components
            foreach (Transform transform in this.gameObject.GetComponentsInChildren <Transform>())
            {
                // Make sure that GameObject with NetworkModel attached does not get synced to Server
                if (transform != this.gameObject.transform)
                {
                    // Get existing reference or create a unique reference for GameObject
                    transform.gameObject.name = this.injector.objectStore.GetReferenceName(transform.gameObject);

                    // Check if GameObject is already in ObjectStore
                    if (!this.injector.objectStore.ContainsKey(transform.gameObject.name))
                    {
                        this.injector.objectStore.Add(transform.gameObject);
                    }
                }
            }

            // List of GameObjects that have been deleted on this client
            List <string> objectsToDelete = new List <string>();

            // Iterate over GameObject Store
            foreach (KeyValuePair <string, ObjectNode> objectEntry in this.injector.objectStore)
            {
                // Never synchronize the root gameobject containing the NetworkModel
                string referenceName = objectEntry.Key;
                if (referenceName == Model.ROOT_NAME)
                {
                    continue;
                }

                // If Element does not exist anymore (or the name was changed), then add it to the list to schedule the deletion
                // (When changing the name of a GameObject, the ObjectNode of that GameOBject gets destroyed and a new ObjectNode is created)
                ObjectNode node = objectEntry.Value;
                if (node.gameObject == null || node.gameObject.name != referenceName)
                {
                    objectsToDelete.Add(referenceName);
                }

                // Else, check for changes in hierarchy and components
                else
                {
                    // Check if parent of gameObject has changed compared to last known state
                    if (node.gameObject.transform.parent.GetInstanceID() != node.parentID)
                    {
                        // Send updated parent information to server
                        this.injector.connection.SendRequest(Request.UpdateObject(this.injector, node.gameObject));
                        node.gameObject.transform.hasChanged = false;
                    }

                    // Update Node with current values from represented GameObject
                    node.Update();

                    // List of Components that have been deleted on this client
                    List <Type> deletedComponents = new List <Type>();
                    // List of Components that have been deleted and inform the server about it
                    List <Type> sendDeletedComponents = new List <Type>();

                    // List of Components that have been changed on this client
                    List <AbstractComponent> updatedComponents = new List <AbstractComponent>();
                    // List of Components that have been changed and inform the server about it
                    List <AbstractComponent> sendUpdatedComponents = new List <AbstractComponent>();

                    // Iterate over tracked components of the node
                    foreach (KeyValuePair <Type, long> hashEntry in node.hashes)
                    {
                        // If component is blocked from tracking changes, then continue with next component
                        if (blockedComponents.Contains(new NameTypePair(node.gameObject.name, hashEntry.Key)))
                        {
                            LogUtility.Log(this.injector, LogType.INFORMATION, "Component " + hashEntry.Key + " update blocked");
                            continue;
                        }

                        // Get the component which is pointed at in this loop
                        Component component = node.gameObject.GetComponent(hashEntry.Key);

                        // If component is allowed to send, then track changes
                        bool send = RuleUtility.FindComponentRule(this.injector, node.gameObject, component.GetType(), UpdateType.SEND);

                        // If Component does not exist anymore, then schedule it for deletion
                        if (component == null)
                        {
                            // Delete references to component on client
                            Type type = this.injector.serializer.ToSerializableType(hashEntry.Key);
                            deletedComponents.Add(type);

                            // Inform the server about the change
                            if (send)
                            {
                                sendDeletedComponents.Add(type);
                            }

                            continue;
                        }

                        // Transform UnityEngine Component to NetworkModel AbstractComponent
                        AbstractComponent serializedComponent = this.injector.serializer.ToSerializableComponent(component);

                        // If Component does not exist as a serializable Component
                        if (serializedComponent == null)
                        {
                            continue;
                        }

                        // Generate hash of component and compare it to the previously stored value; if not equal, then add it to update list
                        if (serializedComponent.GetHash() != hashEntry.Value)
                        {
                            // Update hash stored for component on client
                            updatedComponents.Add(serializedComponent);

                            // Inform the server about the change
                            if (send)
                            {
                                sendUpdatedComponents.Add(serializedComponent);
                            }
                        }
                    }

                    // Components scheduled for Delete
                    foreach (Type type in deletedComponents)
                    {
                        node.RemoveComponent(this.injector.serializer.ToCommonType(type));
                    }

                    // Components scheduled for Update
                    foreach (AbstractComponent component in updatedComponents)
                    {
                        node.UpdateComponent(component.commonType);
                    }

                    // Check if there are deleted components the server needs to be informed about
                    if (sendDeletedComponents.Count > 0)
                    {
                        this.injector.connection.SendRequest(Request.DeleteComponents(this.injector, referenceName, sendDeletedComponents));
                    }

                    // Check if there are updated components the server needs to be informed about
                    if (sendUpdatedComponents.Count > 0)
                    {
                        this.injector.connection.SendRequest(Request.UpdateComponents(this.injector, referenceName, sendUpdatedComponents));
                        node.gameObject.transform.hasChanged = false;
                    }
                }
            }

            // Delete scheduled GameObjects and send Delete Request
            foreach (string referenceName in objectsToDelete)
            {
                this.injector.objectStore.Remove(referenceName);
                this.injector.connection.SendRequest(Request.DeleteObject(this.injector, referenceName));
            }

            // List of Resources that have been deleted on this client
            List <string> resourcesToDelete = new List <string>();

            // Iterate over Resource Store
            for (int i = 0; i < this.injector.resourceStore.Count; i++)
            {
                // Get the resource which is pointed at in this loop
                ResourceNode node         = (ResourceNode)this.injector.resourceStore[i];
                string       resourceName = node.name;

                // If resource name is null, then check next resource
                if (resourceName == "null")
                {
                    continue;
                }

                // If resource is blocked from tracking changes, then continue with next resource
                if (blockedResources.Contains(new NameTypePair(resourceName, node.type)))
                {
                    LogUtility.Log(this.injector, LogType.INFORMATION, "Resource " + node.type + " update blocked");
                    continue;
                }

                // If resource is allowed to send, then track changes
                bool send = RuleUtility.FindResourceRule(this.injector, node.type, UpdateType.SEND);

                // If Element does not exist anymore, then schedule it for deletion
                if (node.resource == null || node.resource.name != resourceName || node.resource.GetType() != node.type)
                {
                    // Delete references to resource on client
                    resourcesToDelete.Add(resourceName);

                    // Inform the server about the change
                    if (send)
                    {
                        this.injector.connection.SendRequest(Request.DeleteResource(this.injector, resourceName));
                    }
                }
                // Else, check for changes
                else
                {
                    // Transform UnityEngine Resource to NetworkModel AbstractResource
                    AbstractResource serializedResource = this.injector.serializer.ToSerializableResource(node.resource);
                    if (serializedResource.GetHash() != node.hash)
                    {
                        // Inform the server about the change
                        if (send)
                        {
                            this.injector.connection.SendRequest(Request.UpdateResource(this.injector, resourceName,
                                                                                        this.injector.serializer.ToSerializableType(node.type), serializedResource));
                        }

                        // Update hash stored for resource on client
                        node.UpdateHash();
                    }
                }
            }

            // Delete resources scheduled for deleting from ResourceStore
            foreach (string resourceName in resourcesToDelete)
            {
                this.injector.resourceStore.Remove(resourceName);
            }
        }
        /// <summary>
        /// Function to find valid rule for Component from GameObject
        /// </summary>
        /// <param name="injector"></param>
        /// <param name="gameObject"></param>
        /// <param name="type"></param>
        /// <param name="updateType"></param>
        /// <returns></returns>
        internal static bool FindComponentRule(Injector injector, GameObject gameObject, Type type, UpdateType updateType)
        {
            // Find relevant rule
            Transform pointer = gameObject.transform;

            do
            {
                // See if current GameObject has a NetworkModel Rule component
                NetworkModelRule networkModelRule = pointer.GetComponent <NetworkModelRule>();

                // If true, then a rule was found
                if (networkModelRule != null)
                {
                    // Check if the Rule  was found in the passed GameObject
                    if (gameObject.transform == pointer)
                    {
                        // Check if the Rule is active for the GameObject it is attached to
                        if (networkModelRule.applyToObject)
                        {
                            // Get the correct Rule
                            RuleType ruleType = CheckComponentRule(networkModelRule, type, updateType);

                            // Check, if Rule is enabled
                            if (ruleType != RuleType.DISABLED)
                            {
                                return(ruleType.ToBool());
                            }
                        }
                        else
                        {
                            // Rule does not apply, continue with parent GameObject
                            pointer = pointer.parent;
                            continue;
                        }
                    }
                    // ELse, the Rule was found in a parent GameObject
                    else
                    {
                        // Check if the Rule is active for the GameObject it is attached to
                        if (networkModelRule.applyToChildren)
                        {
                            // Get the correct Rule
                            RuleType ruleType = CheckComponentRule(networkModelRule, type, updateType);

                            // Check, if Rule is enabled
                            if (ruleType != RuleType.DISABLED)
                            {
                                return(ruleType.ToBool());
                            }
                        }
                        else
                        {
                            // Rule does not apply, continue with parent GameObject
                            pointer = pointer.parent;
                            continue;
                        }
                    }
                }

                // Go one GameObject up in Hierarchy
                pointer = pointer.parent;
            } while (pointer != null);

            LogUtility.Log(injector, LogType.INFORMATION, "No active rule found for Type " + type);

            // Default value
            return(RuleUtility.DEFAULT_RULE);
        }