Ejemplo n.º 1
0
        /// <summary>
        /// Update content of Resource, creating it if necessary.
        /// </summary>
        /// <param name="request"></param>
        private void UpdateResource(Request request)
        {
            // Get Unity Type of Resource
            Type commonType = this.injector.serializer.ToCommonType(request.resourceType);

            // If resource is not allowed to receive update, then drop request
            if (!RuleUtility.FindResourceRule(this.injector, commonType, UpdateType.RECEIVE))
            {
                return;
            }

            ResourceNode node;

            if (this.injector.resourceStore.TryGet(request.name, commonType, out node))
            {
                // If Resource exists, try to apply updates. If successful, request is fulfilled.
                if (request.resource.Apply(this.injector, node.resource))
                {
                    node.UpdateHash();
                    LogUtility.Log(this.injector, LogType.INFORMATION, "Resource update sucessful");
                    return;
                }
            }
            else
            {
                // If Resource does not exist, create it
                UnityEngine.Object resource = request.resource.Construct();
                resource.name = request.name;

                // Try to apply updates; if successful, request is fulfilled
                if (request.resource.Apply(this.injector, resource))
                {
                    node = this.injector.resourceStore.Add(request.name, resource);
                    node.UpdateHash();
                    LogUtility.Log(this.injector, LogType.INFORMATION, "Resource " + commonType + " update sucessful");
                    return;
                }
            }

            // Resource creation was not successful in this update

            // If this request was handled less times than allowed, then try in an later update loop
            if (request.iteration < Model.ATTEMPTS_TO_UPDATE)
            {
                request.iteration++;
                nextRequestQueue.Enqueue(request);
                LogUtility.Log(this.injector, LogType.INFORMATION, "Resource  " + commonType + " update unsucessful because of missing references");
            }
            // Else, report failed resource update
            else
            {
                LogUtility.Log(this.injector, LogType.WARNING, "Request to update resource " + commonType + " for " + request.name + " dropped");
            }
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Remove Resource from tracked store. Will only be garbage-collected if no other resource uses it.
        /// </summary>
        /// <param name="request"></param>
        private void DeleteResource(Request request)
        {
            // Get Unity Type of Resource
            Type commonType = this.injector.serializer.ToCommonType(request.resourceType);

            // If resource is not allowed to receive update, then drop request
            if (!RuleUtility.FindResourceRule(this.injector, commonType, UpdateType.RECEIVE))
            {
                return;
            }

            // If ResourceStore contains Resource, then remove it
            if (this.injector.resourceStore.Contains(name))
            {
                this.injector.resourceStore.Remove(name);
            }
        }
Ejemplo n.º 3
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);
            }
        }