/// <summary> /// Initialise a new instance of RequestNetworkData and copy all members of another specified request. /// </summary> /// <param name="baseCopy">An instance of Request to copy all members over to this new request.</param> public RequestNetworkData(RequestNetworkData baseCopy) : this(baseCopy.RequestType, baseCopy.Data) { _internalName = baseCopy._internalName; _name = baseCopy._name; _typeName = baseCopy._typeName; _target = baseCopy._target; }
/// <summary> /// Deserialise a string value into it's given object that it represents. /// </summary> /// <param name="t">The type of the data that is being deserialised.</param> /// <param name="data">A serialised object that should be deserialised.</param> /// <returns>The original object the serialised string represented.</returns> /// <exception cref="System.NotSupportedException" /> /// <exception cref="System.NullReferenceException" /> /// <exception cref="NetworkLibrary.Exceptions.ParsingException" /> /// <exception cref="NetworkLibrary.Exceptions.NetworkDataCollectionException" /> protected object Deserialise(Type dataType, string data) { object output = null; //Check to see if the data is longer than zero letters. this is done to prevent unnecessary exceptions //that could be thrown by the system. if (data.Length > 0) { //Check to see if we are parsing a serialised value of null if (data == "null") return null; //Retreave the local instance of NetworkHandler. We use this to get types registered //into the library as well as sending requests and such. INetworkDataHandler networkHandler = ObjectFactory.GetInstance<INetworkDataHandler>(); //Check to see if the object is a reference to an INetworkData object if (data[0] == '{') { //Get the name of the NetworkId of the object being referenced here. string name = data.Substring(1, data.Length - 2); //Check to see if we have it in our collection. if (networkHandler.RegisteredObjects.ContainsKey(name)) //Return a reference to the object that is being contained in our collection. return networkHandler.RegisteredObjects[name]; else { //It might be this object we are trying to deserialise has already been deserialised //and this is only a reference to it. Check to see if we have maybe already deserialised it. for (int i = 0; i < _serialisedObjects.Count; i++) { if (_serialisedObjects[i] is INetworkData) if (((INetworkData)_serialisedObjects[i]).NetworkId == name) return _serialisedObjects[i]; } //The object was not found in our library. Send request to the source and ask //for the object that was being referenced. RequestNetworkData request = new RequestNetworkData(RequestNetworkDataType.RequestData, name); lock (request) { networkHandler.RequestSend(request); //The request has been sent. Now we lock this thread until the reply has //been received. Once an reply has been recieved, it will send a pulse to //the request waking this thread and allows it to finish it's job. Monitor.Wait(request); } //Make sure the object is finally in our collection. if (networkHandler.RegisteredObjects.ContainsKey(name)) return networkHandler.RegisteredObjects[name]; else //The object was not found even after sending an request to the source. throw new NetworkDataCollectionException("The data contained a reference to a network id but was not found in the collection and was neither found at the source of the packet.", name); } } //Create a reference to the type being deserialised here. Type t; string[] split; if (dataType == null) { //Split the name of the type and the value which is being seperated by a semicolon. split = data.Split(new char[] { ':' }, 2); //Check to see if we were able to split the value. if (split.Length == 2) { //Check to see if the type has been registered in our library. if (networkHandler.RegisteredTypes.ContainsKey(split[0])) t = networkHandler.RegisteredTypes[split[0]]; else { //If the type is a basic value type, the namespace 'System' is ommitted to save space. //Here we check to see if such ommitation has been done and if so, add the System into //the front of the name before we search for the type using name search. if (split[0].IndexOf('.') == -1) t = Type.GetType("System." + split[0]); else if (split[0].EndsWith("[]")) { //Because this is an array we could be dealing with an array of special objects. //These kinds of arrays objects can't be created directly but need to be passed //through the Array.CreateInstance. if (networkHandler.RegisteredTypes.ContainsKey(split[0].Remove(split[0].Length - 2, 2))) //Create an array of special objects. t = Array.CreateInstance(networkHandler.RegisteredTypes[split[0].Remove(split[0].Length - 2, 2)], 0).GetType(); else //If it's an array of basic value types then we can create those directly. t = Type.GetType(split[0]); } else t = Type.GetType(split[0]); } } else throw new ParsingException("A serialised string was of an invalid format. Expecting a value of 'type:content'."); } else { t = dataType; split = new string[] { "", data}; } //If we still don't have the type of the object there is nothing to be done. //Most likely cause for this is if the programmer forgot to pre-register the type //of the object. if (t == null) throw new NullReferenceException(string.Format("Unable to create an object of name '{0}'. Did you forget to register the type?.", split[0])); //Check to see if the type is array if (t.IsArray) //Because it's an array we can't dynamicly add to it, we therefore create //a list which we can add and remove at will and later, copy it into an array. output = new List<object>(); //If you try to run Activator and create an instance of string it will fail. else if (t != typeof(string)) //Create a default instance of the object being deserialised, we fill it with //data later on. output = Activator.CreateInstance(t); else //The type is string, since we only need a basic value in the correct format, //we make our output "become" string like so output = ""; int skipIdentifier = 0, length = split[1].Length - 1; bool insideParenthis = false; string identifiers = "{[]}", buffer = "", property = ""; //Check to see if we are working with basic value type if (output is ValueType || output is string) //Since this is just a basic value type, all we have to do is parse the value //into it's correct format. return ParseValueToType(t, split[1]); //Check to see if the Activator was succesfull in creating an instance for us. else if (output == null) throw new NullReferenceException(string.Format("While creating an instance of '{0}', the activator returned null value.", t.FullName)); else if (output is IList) length++; //Add our new object to the collection _serialisedObjects.Add(output); //Checkmark for enabling listener. bool enableListener = false; //Run through the value, feed the buffer and parse if necessary. for (int i = 1; i < length; i++) { switch (split[1][i]) { //The following is checking whether we have reached an endmark that resembles //an end when parsing objects and a next step is necessary. case ';': case '=': //Here we check if the endmark is really an endmark and not a character inside the value if (skipIdentifier == 0 && !insideParenthis) //Check to see if we have the name of the property or not. if (string.IsNullOrEmpty(property)) { //The buffer contains the name of the property, retrieve it and flush //the buffer so it can grab the value of the property. property = buffer; buffer = ""; } else { //We have the name of the property we are trying to fill and the value //is inside the buffer. Prepare to deserialise the value and feed it into //the property of the object. //Grap a local info copy of the property. PropertyInfo info = t.GetProperty(property); //Deserialise the value object value; if (buffer[0] == '[') value = this.Deserialise(t, buffer); else value = this.Deserialise(buffer); //Check if the property is of an array or collection that can't be overriden. if (info.PropertyType.IsArray || !info.CanWrite) //Make sure the collection or array inside the object is not null. if (info.GetValue(output, null) == null) { //Because the collection or array inside is null and we can't override //it we stop what we are doing and continue with the rest of the deserialising. property = buffer = ""; break; } //Check if we are working with INetworkData object and if we haven't disabled the listener already. if (output is INetworkData && !enableListener) //Check to see if the object has it's INetworkId if (!string.IsNullOrEmpty((output as INetworkData).NetworkId)) //Check if we have it registered in our collection if (networkHandler.RegisteredObjects.ContainsKey((output as INetworkData).NetworkId)) //Since we have it in our collection there is a chance that changing properties can cause //NotifyPropertyChanged to run. We disable any listeners while we finish adding all the properties. { networkHandler.ListenerDisable((INetworkData)output); enableListener = true; } //If we have direct write access, we can just pass the value directly //into the object and be done with it. if (info.CanWrite) { //Check to see if the property has index parameter if (info.GetIndexParameters().Length > 0) { //The property requires index parameter. This usually means we are working //with the contents of a collection. If so, then the value should also be a //collection of the contents. if (output is IList && !t.IsArray && value is IList) //Add each item into the collection for (int addRange = 0; addRange < (value as IList).Count; addRange++) (output as IList).Add((value as IList)[addRange]); else //It required an index parameter but it wasn't a collection throw new NotSupportedException("Encountered a property that required index parameter but either the object or the value was not of an IList type."); } else { //No index property, then we just write the value directly info.SetValue(output, value, null); //Check to see if the property is NetworkData and if the object is NetworkId if (property == "NetworkId" && output is INetworkData && !string.IsNullOrEmpty((string)value)) //Since the incoming object is INetworkData, we check to see //if we already have it. If we don't, we register it. if (!networkHandler.RegisteredObjects.ContainsKey((string)value)) networkHandler.Register((INetworkData)output); } } else if (value is IList) //We don't have direct write access but it is a collection //so instead of overwriting the array with new array, we fill //the array with new information from the value. if (info.PropertyType.IsArray) { //Grab the array from the collection. IList objectArray = info.GetValue(output, null) as IList; //Go over both arraies and make sure we don't go overboard. for (int arrayIndex = 0; arrayIndex < objectArray.Count && arrayIndex < (value as IList).Count; arrayIndex++) //Assign the value to the array in the object. objectArray[arrayIndex] = (value as IList)[arrayIndex]; } else { //Grab the collection from the object. IList collection = info.GetValue(output, null) as IList; //Check to see if we have all the properties for this object cached. if (!_cachedProperties.ContainsKey(value.GetType())) //We don't have the properties for this object cached. We need to cache it //before we continue. CachePropertiesFromType(value.GetType()); //Go over all the properties and merge them with the one inside the object. //By doing this, properties such as NetworkId will be passed along. foreach (var propInfo in _cachedProperties[value.GetType()]) //Check if it's a basic property which we can write to. if (propInfo.GetIndexParameters().Length == 0 && propInfo.CanWrite) //Write the property from our network packet collection //into the collection inside the object. propInfo.SetValue(collection, propInfo.GetValue(value, null), null); //Add the contents into the collection in the object. for (int item = 0; item < (value as IList).Count; item++) collection.Add((value as IList)[item]); if (collection is INetworkData && value is INetworkData) //This is an important step. If the collection is INetworkData then the collection //from the network packet has been registered into the RegisteredObjects instead of //of the collection from the object. We therefore have to unregister the incorrect one //and register the correct one from the object. if (networkHandler.RegisteredObjects.ContainsKey((value as INetworkData).NetworkId)) { //Unregister the incorrect collection that we just now deserialised. networkHandler.Unregister(value as INetworkData); //Register the correct one from inside the object we are assign the values to. networkHandler.Register(collection as INetworkData); } } else throw new ParsingException("Encountered a property that is unsupported."); property = buffer = ""; } else //Since this is not an endmark, make sure we add it to the buffer. buffer += split[1][i]; break; //The following is checking whether we have reached an endmark that resembles //an end when parsing arrays and such. case ']': case ',': //Here we check if the endmark is really an endmark and not a character inside the value if (skipIdentifier == 0 && output is IList && !insideParenthis) { if (buffer != "") //Since this is an array, we add the current value inside the buffer //into the output array and continue on. (output as IList).Add(this.Deserialise(buffer)); buffer = ""; } else { if (!insideParenthis && split[1][i] == ']') skipIdentifier--; //Since this is not an endmark, make sure we add it to the buffer. buffer += split[1][i]; } break; //This checks to see if we have reached a string value, if so, all characters //that resemble an endmark are automatically ignored. This is done by marking the //insideParenthis boolean. case '"': //Check to see if the parenthis has been escaped or not. if (split[1][i - 1] != '\\') //The parenthis has not been escaped, this means it's a start or an end //of a string value, mark this by changing the marked. insideParenthis = !insideParenthis; //Add the current character to the buffer. buffer += split[1][i]; break; default: //Check to see if we have to skip some identifiers because we are adding //items into the buffer that contain recursive objects. if (identifiers.IndexOf(split[1][i]) > -1 && !insideParenthis) //Check to see if we have to skip an extra identifier/endmark of if we //are finishing skipping an identifier/endmark if (identifiers.IndexOf(split[1][i]) < identifiers.Length / 2) skipIdentifier += 1; else skipIdentifier -= 1; buffer += split[1][i]; break; } } //Since the output is array, here we copy the content of the collection into an array if (output is IList && t.IsArray) { object temp = Activator.CreateInstance(t, (output as IList).Count); Array.Copy((output as List<object>).ToArray(), temp as Array, (output as IList).Count); return temp; } //Check to see if we need to reenable the listener. if (enableListener) //We need to reenable the listener since we disabled it during the deserialisation. networkHandler.ListenerEnable((INetworkData)output); } return output; }
/// <summary> /// Event that is called automatically when a NetworkRequest was recieved. /// </summary> /// <param name="packet">The base packet containing the raw data.</param> /// <param name="data">NetworkEvent data containing the contents of the NetworkPacket in a parsed format.</param> private void ReceivedNetworkRequest(NetworkPacket packet, NetworkEventArgs data) { //Prelimenary checking on whether the data is of correct type if (data.DataType == typeof(RequestNetworkData)) { // The data passed over the network is a real Request. Processing the request. try { //Create the response for the request and pass in all data of the request over to the //the response. This is done so the InternalName as well as other data is passed over. RequestNetworkData response = new RequestNetworkData(data.Data as RequestNetworkData); //Start processing the request. switch (response.RequestType) { case RequestNetworkDataType.RequestType: //The connection is requesting a data of specific type. Search the registered //objects for any objects that fit the description. for (int i = 0; i < _registeredObjects.Count; i++) if (_registeredObjects.ElementAt(i).Value.GetType().FullName == response.ClassTypeName) { response.Data = _registeredObjects.ElementAt(i).Value; break; } break; case RequestNetworkDataType.RequestName: //The connection is requesting name for it's object. Pass in a random //generated identifier. response.NetworkId = GetNewNetworkId(response.ClassTypeName); break; case RequestNetworkDataType.RequestData: //The connection is requesting a data with specific NetworkId. //Return the object containing that NetworkId. response.Data = this._registeredObjects[response.NetworkId]; break; } //Send the response over the network to the client/host. _network.SendSingleRawEvent((int)CorePacketCode.NetworkDataRequestResponse, new Serialiser(true).Serialise(response), packet.Source); } catch (Exception e) { //An unknown error occured. Since this is not critical part of the NetworkLibrary we //pass it on as a Warning. if (OnWarningOccured != null) OnWarningOccured(this, new Warning("A network request was received but an unknown error occured while processing the request.", e)); } } //Received a packet containing the code for a Request but the data was of an invalid type. //Since this is not a critical part of the NetworkLibrary we pass it on as a Warning. else if (OnWarningOccured != null) OnWarningOccured(this, new Warning("A network request was received but was of an invalid format. Request was truncated.")); }