/// <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;
 }
Beispiel #2
0
        /// <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."));
        }