/// <summary> /// After the response is received from the server, unpacks serialized parameters into human-readable parameters /// </summary> public void OnAfterDeserialize() { this.messageType = this.type.Substring(0, 1); this.updateType = this.type.Substring(1, 1); switch (type) { case Request.RESOURCE + Request.UPDATE: this.resourceType = Type.GetType(this.DecodeClass(string1[0])); this.resource = (AbstractResource)JsonUtility.FromJson(this.string2[0], this.resourceType); break; case Request.OBJECT + Request.UPDATE: this.parent = this.string1[0]; break; case Request.COMPONENT + Request.UPDATE: this.components = new List <AbstractComponent>(); for (int i = 0; i < this.string1.Count && i < this.string2.Count; i++) { Type type = Type.GetType(this.DecodeClass(this.string1[i])); this.components.Add((AbstractComponent)JsonUtility.FromJson(this.string2[i], type)); } break; case Request.COMPONENT + Request.DELETE: this.componentTypes = new List <Type>(); foreach (string typeName in string1) { this.componentTypes.Add(Type.GetType(this.DecodeClass(typeName))); } break; } }
/// <summary> /// Create Request to update a specific Resource /// </summary> /// <param name="injector"></param> /// <param name="name"></param> /// <param name="resourceType"></param> /// <param name="resource"></param> /// <returns>Request</returns> internal static Request UpdateResource(Injector injector, string name, Type resourceType, AbstractResource resource) { Request request = new Request(injector, Request.RESOURCE, Request.UPDATE, name); request.resource = resource; request.resourceType = resourceType; return(request); }
/// <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); } }