/// <summary> /// Send an object over the network with a specific packet code. /// </summary> /// <param name="code">The code for the packet.</param> /// <param name="data">The object itself to transmit over the network.</param> /// <param name="sendItself">Also send the object to itself.</param> /// <param name="excludeList">A list of clients who will not receive the packet.</param> public void SendEvent(int code, object data, bool sendItself, object[] excludeList, params object[] arguments) { string message; //Check to see if we have a custom parser to handle this type of data. if (_packetParsers.ContainsKey(code)) message = _packetParsers[code].ParseObject(data, arguments); else //No special parser so we serialise the object like we normally do. message = new Serialiser().Serialise(data); //In some rare cases the packet parser can fail and in those cases it //will return a null value. Here we check if the parser was successfull. if (message != null) SendEvent(new NetworkPacket(code, message, null), sendItself, excludeList); }
/// <summary> /// Seralise an object. /// </summary> /// <param name="data">The object itself that is being serialised.</param> /// <returns>A string representing a sreialised value of the object.</returns> public string Serialise(object data) { /* * This serialiser works a little different that normal serialiser. All objects are formatted as * following and recursively: * Basic value types: * FullNameOfTheObject:TheValue * * If we are trying to parse an integer of value 4, the output would be: * Int32:4 * If we were trying to parse a double value of 4.3645 the output would be: * Double:4.3645 * Before the semicolon is the name of the type without the "System." in front * and after the semicolon comes the value parsed using the ToString() method. * * Arrays: * FullNameOfTheObjectPlusNamespace:[...] * * The dots are comma seperated values that contain the serialised value of the objects * inside the array. If we have an array of integers containing the values of 4, 6 and 3 * the output would be like so: * System.Int32[]:[Int32:4,Int32:6,Int32:3] * The value of each contains the name of the type and it is so because then we * can have an aray of objects which contain an integer of 5, a double value of 6.43 and * a custom object. With this the result would be like so: * System.Object[]:[Int32:5,Double:6.43,NameOfNamespace.OurObject:{...}] * For more information on how objects are parsed look below. * * Custom objects and such: * FullNameOfTheObjectPlusNamespace:{...} * * Inside the brackets comes all properties that are supported in the following serialised format: * NameOfProperty=xxx; * The xxx represent the value run through the serialiser recursively, so if we have a property * called 'NumberOfUnits' and has an integer value of 2, the output would be following: * NumberOfUnits=Int32:2; * * Objects registered in the library: * {NetworkId} * * If you have an object which implements INetwordData, the story is different. Instead of the * object being recursively serialised like above, INetworkData that have been registered are * treated differently. The output of an object which implements INetworkData and contains the * NetworkId of 'Tile03' would be parsed as following: * {Tile03} * No type name or nothing like that. This is why the Serialiser is million times faster than * normal serialiser when working with registered objects. Instead of parsing and serialising * each property and so on, all it does is send a name with '{' and '}' around it. * If we have an Array which contain a reference to an object that has been registered into * the Library, it also get's the same treatment. * */ //Basic check to see if the data really is data. if (data != null) { //Create a local copy of the data. _data = data; //Check to see if the object implements INetworkData if (_data is INetworkData) //Make sure the NetworkId is not empty if (!string.IsNullOrEmpty((_data as INetworkData).NetworkId)) //If we are set to ignore the library, the next statement will be skipped and the //the object will be parsed like every other object. if (!_ignoreLibrary) if (ObjectFactory.TryGetInstance<INetworkDataHandler>() != null) //Check to see if the object has been registered in the library. if (ObjectFactory.GetInstance<INetworkDataHandler>().RegisteredObjects.ContainsKey((_data as INetworkData).NetworkId)) //The object implements INetworkData and is registered. Return the name and stop //with the serialiser. return "{" + (_data as INetworkData).NetworkId + "}"; //Check to make sure we are not serialising an already serialised objects if (_serialisedObjects.Contains(data)) { //We have already serialised this object. //Then we only need to save a link to it and exit. if (data is INetworkData) { if (!string.IsNullOrEmpty((_data as INetworkData).NetworkId)) { //Return a link to the object return "{" + (_data as INetworkData).NetworkId + "}"; } else //The object had not been registered into our collection and //contains a reference to itself. Let know about it. throw new SerialiserException("A valid INetworkData object contained a reference to itself but had not yet been registered.", data); } else //Object contained a reference to itself but was not an INetworkData object. //In order to fully support circular reference, the object must be INetworkData. //This can also happen if 2 different objects contain a reference to the same object. throw new SerialiserException("A circular reference was detected on an object that was not of type INetworkData. Make sure all circular reference objects are of type INetworkData. This also applies if 2 different objects contain a reference to the same object then that object must also be an INetworkData object.", data); } //Create a local copy of the type of the object. _dataType = _data.GetType(); //The serialised value of the object string output = ""; //If the object being serialised is a basic System type we don't need to write //the full name of it. Instead for example 'System.Int32' we get 'Int32', short //and simple. if (_dataType.FullName == "System." + _dataType.Name) output += _dataType.Name + ":"; else if (_dataType.UnderlyingSystemType != null && _dataType.UnderlyingSystemType.Name.Contains("ReadOnly")) output += _dataType.DeclaringType.FullName + ":"; else output += _dataType.FullName + ":"; //If the object is a basic value type, all we have to do is run ToString method //and be done with it. if (_data is string) return output + "\"" + (_data as string).Replace("\"", "\\\"") + "\""; else if (_data is ValueType) return output + (_data).ToString(); //Check to see if the object is an array or a collection of some sort. if (_data is Array) { //We are parsing an array, prepare a new serialiser and parse all of it's values. output += "["; Serialiser ser = new Serialiser(); for (int i = 0; i < (_data as IList).Count; i++) //The contents of an array is a comma seperated value of the contents. if (i > 0) output += "," + ser.Serialise((_data as IList)[i]); else output += ser.Serialise((_data as IList)[i]); return output + "]"; } else { //We are parsing a normal object. Parse all supported properties inside the object. output += "{"; //We create a cache of all valid properties of a given type due to the nature that //checking whether a property is supported or not is a slow process. if (!_cachedProperties.ContainsKey(_dataType)) { CachePropertiesFromType(_dataType); } //Save our object into the collection. If any property contains a reference to itself //then we will know about it by checking if it exists in the collection. _serialisedObjects.Add(data); //If the object is of INetworkData type then we add that property first. if (data is INetworkData) { //Find the property foreach (var propInfo in _cachedProperties[_dataType]) //The only time the INetworkData property is hardcoded if (propInfo.Name == "NetworkId") { //Add it first to the output output += new PropertySerialiser(_data, propInfo).Serialise() + ";"; break; } } //Run through each property of the object and parse it's value foreach (var propInfo in _cachedProperties[_dataType]) { if (propInfo.Name == "NetworkId" && data is INetworkData) continue; //PropertSerialiser takes care of this for us. PropertySerialiser serProp = new PropertySerialiser(_data, propInfo); if (_dataType == typeof(RequestNetworkData)) //Pass forward the rule of whether to ignore the Library serProp.IgnoreLibrary = this._ignoreLibrary; //Pass in a reference to already serialised objects serProp.SerialisedObjects = this._serialisedObjects; output += serProp.Serialise() + ";"; } return output + "}"; } } else return "null"; }
public string Serialise() { if (_propertyInfo == null) throw new NullReferenceException("An attempt was made serialise a null referenced property."); Serialiser ser = new Serialiser(_serialisedObjects, _ignoreLibrary); switch (_propertyInfo.GetIndexParameters().Length) { case 0: return _propertyInfo.Name + "=" + ser.Serialise(_propertyInfo.GetValue(_data, null)); case 1: if (_data is IList) { string output = ""; for (int i = 0; i < (_data as IList).Count; i++) if (i > 0) output += "," + ser.Serialise((_data as IList)[i]); else output += ser.Serialise((_data as IList)[i]); return _propertyInfo.Name + "=[" + output + "]"; } throw new PropertySerialiseException("An attempt was made to serialise a property that required one index parameter but the object was not of an IList interface.", _propertyInfo); default: throw new PropertySerialiseException("An attempt was made to serialise a property that required more than one index parameter. This is currently unsupported.", _propertyInfo); } }