/// <summary> /// Message handler for <see cref="DataMessage"/> /// </summary> /// <param name="msg">The incoming message.</param> /// <param name="packet">The buffer containing the message.</param> /// <param name="reader">The reader from which the message came.</param> /// <returns><see cref="ErrorCode.UnsupportedFeature"/></returns> /// <remarks> /// At this level, the function is only supported for handling <see cref="Tes.Shapes.MultiShape"/> data packets. /// Otherwise it returns an error code. /// </remarks> protected virtual Error HandleMessage(DataMessage msg, PacketBuffer packet, BinaryReader reader) { // Retrieve the target shape. ShapeCache cache = (msg.ObjectID == 0) ? _transientCache : _shapeCache; int shapeIndex = (msg.ObjectID == 0) ? _lastTransientIndex : cache.GetShapeIndex(msg.ObjectID); if (shapeIndex < 0) { return(new Error(ErrorCode.InvalidObjectID, msg.ObjectID)); } // Check this is is a multi-shape. CreateMessage shape = cache.GetShapeByIndex(shapeIndex); if ((shape.Flags & (ushort)ObjectFlag.MultiShape) == 0) { return(new Error(ErrorCode.UnsupportedFeature)); } // Add to the multi-shape chain. int multiShapeHead = cache.GetMultiShapeChainByIndex(shapeIndex); // Read new multi-shape block. uint blockItemCount = (uint)reader.ReadUInt16(); int initialHead = multiShapeHead; Error error = ReadMultiShapeBlock(cache, shapeIndex, shape, packet, reader, blockItemCount, ref multiShapeHead); // Set the new multi shape chain head (even on failure for better clean up). if (multiShapeHead != -1) { cache.SetMultiShapeChainByIndex(shapeIndex, multiShapeHead); } if (error.Failed) { return(error); } return(new Error()); }
/// <summary> /// Read <see cref="Tes.Shapes.MultiShape"/> block. /// </summary> /// <param name="parentShape">The shape data defining the root of the multi-shape</param> /// <param name="packet">The buffer containing the message.</param> /// <param name="reader">The reader from which the message came.</param> /// <param name="blockItemCount">The number of child items to read.</param> /// <param name="multiChainHead">The head of the multi-shape linked list/chain.</param> /// <returns>An error code on failure.</returns> /// <remarks> /// The <paramref name="multiChainHead"/> is expected to mark the start of the child item linked list. This is -1 /// when starting the list. The value is updated to mark the new head item index. /// </remarks> protected Error ReadMultiShapeBlock(ShapeCache cache, int shapeIndex, CreateMessage parentShape, PacketBuffer packet, BinaryReader reader, uint blockItemCount, ref int multiChainHead) { Matrix4x4 parentTransform = Maths.Matrix4Ext.ToUnity(parentShape.Attributes.GetTransform()); CreateMessage multiShape = parentShape.Clone(); // Clear the multi-shape flag so these shapes are visualised. multiShape.Flags &= (ushort)(~ObjectFlag.MultiShape); multiShape.ObjectID = ShapeCache.MultiShapeID; // Read each shape attributes. for (uint i = 0; i < blockItemCount; ++i) { if (!multiShape.Attributes.Read(reader)) { return(new Error(ErrorCode.MalformedMessage, parentShape.ObjectID)); } multiChainHead = CreateShape(cache, multiShape, multiChainHead, parentTransform); } return(new Error()); }
/// <summary> /// Called at end of <see cref="HandleMessage(DestroyMessage, PacketBuffer, BinaryReader)"/> /// just prior to destroying the object. The object will still be destroyed. /// </summary> /// <param name="msg">The incoming message.</param> /// <param name="packet">The buffer containing the message.</param> /// <param name="reader">The reader from which the message came.</param> /// <returns>An error code on failure.</returns> /// <remarks> /// Neither the <see cref="PacketBuffer"/> nor the <see cref="BinaryReader"/> are provided as destroy message are /// not allowed to carry additional payloads. /// </remarks> protected virtual Error PostHandleMessage(DestroyMessage msg, ShapeCache cache, int shapeIndex) { return(new Error()); }
/// <summary> /// Called at end of <see cref="HandleMessage(UpdateMessage, PacketBuffer, BinaryReader)"/>, only on success. /// </summary> /// <param name="shapeIndex">Index of the shape in the _shapeCache which is being updated.</param> /// <param name="msg">The incoming message.</param> /// <param name="packet">The buffer containing the message.</param> /// <param name="reader">The reader from which the message came.</param> /// <returns>An error code on failure.</returns> protected virtual Error PostHandleMessage(UpdateMessage msg, PacketBuffer packet, BinaryReader reader, ShapeCache cache, int shapeIndex) { return(new Error()); }
/// <summary> /// Called after serialising the creation message for an object. This allows follow up /// data messages to be serialised. /// </summary> /// <param name="packet"></param> /// <param name="writer"></param> /// <param name="cache"></param> /// <param name="shapeIndex"></param> /// <returns></returns> protected virtual Error PostSerialiseCreateObject(PacketBuffer packet, BinaryWriter writer, ShapeCache cache, int shapeIndex) { return(new Error()); }
/// <summary> /// Serialises a list of objects to <paramref name="writer"/> /// </summary> /// <param name="writer">The writer to serialise to.</param> /// <param name="objects">The object to write.</param> /// <param name="processedCount">Number of objects processed.</param> /// <returns>An error code on failure.</returns> /// <remarks> /// Default serialisation uses the following logic on each object: /// <list type="bullet"> /// <item>Call <see cref="CreateSerialisationShape(ShapeCache, int, CreateMessage)"/> to /// create a temporary shape to match the unity object</item> /// <item>Call <see cref="Shapes.Shape.WriteCreate(PacketBuffer)"/> to generate the creation message /// and serialise the packet.</item> /// <item>For complex shapes, call <see cref="Shapes.Shape.WriteData(PacketBuffer, ref uint)"/> as required /// and serialise the packets.</item> /// </list> /// /// Using the <see cref="Shapes.Shape"/> classes ensures serialisation is consistent with the server code /// and reduces the code maintenance to one code path. /// </remarks> protected virtual Error SerialiseShapes(BinaryWriter writer, ShapeCache cache, ref uint processedCount) { // Serialise transient objects. PacketBuffer packet = new PacketBuffer(); Error err; Shapes.Shape tempShape = null; List <Shapes.Shape> multiShapeList = new List <Shapes.Shape>(); uint dataMarker = 0; int dataResult = 0; Debug.Assert(tempShape != null && tempShape.RoutingID == RoutingID); processedCount = 0; foreach (int shapeIndex in cache.ShapeIndices) { tempShape = null; ++processedCount; CreateMessage shapeData = cache.GetShapeByIndex(shapeIndex); if ((shapeData.Flags & (ushort)ObjectFlag.MultiShape) != 0) { // Multi-shape. Follow the link. multiShapeList.Clear(); int nextIndex = cache.GetMultiShapeChainByIndex(shapeIndex); while (nextIndex != -1) { CreateMessage multiShapeData = cache.GetShapeByIndex(nextIndex); tempShape = CreateSerialisationShape(cache, shapeIndex, multiShapeData); tempShape.ID = shapeData.ObjectID; if (tempShape != null) { // Successfully created the child. Add to the list. multiShapeList.Add(tempShape); } else { Debug.LogError($"{Name} failed to create multi-shape entry"); } nextIndex = cache.GetMultiShapeChainByIndex(nextIndex); } // Create the multi-shape tempShape = new Shapes.MultiShape(multiShapeList.ToArray()); tempShape.ID = shapeData.ObjectID; tempShape.Category = shapeData.Category; tempShape.SetAttributes(shapeData.Attributes); } else if (shapeData.ObjectID != ShapeCache.MultiShapeID) { tempShape = CreateSerialisationShape(cache, shapeIndex, shapeData); } if (tempShape != null) { tempShape.WriteCreate(packet); packet.FinalisePacket(); packet.ExportTo(writer); if (tempShape.IsComplex) { dataResult = 1; dataMarker = 0; while (dataResult > 0) { dataResult = tempShape.WriteData(packet, ref dataMarker); packet.FinalisePacket(); packet.ExportTo(writer); } if (dataResult < 0) { return(new Error(ErrorCode.SerialisationFailure)); } // Post serialisation extensions. err = PostSerialiseCreateObject(packet, writer, cache, shapeIndex); if (err.Failed) { return(err); } } } else if (shapeData.ObjectID != ShapeCache.MultiShapeID) { return(new Error(ErrorCode.SerialisationFailure)); } } return(new Error()); }
/// <summary> /// Create a dummy shape object used to generate serialisation messages. /// </summary> /// <param name="cache">The cache in which the shape data reside</param> /// <param name="shapeIndex">The index of the shape in <paramref name="cache"/>. /// <param name="shape">Creation message for the shape.</param> /// <returns>A shape instance suitable for configuring to generate serialisation messages.</returns> /// <remarks> /// Base classes should implement this method to return an instance of the appropriate /// <see cref="Shapes.Shape"/> derivation. For example, the <see cref="Shape3D.SphereHandler"/> /// should return a <see cref="Shapes.Sphere"/> object. See /// <see cref="SerialiseShapes(BinaryWriter, ShapeCache, ref uint)"/> for further /// details. /// </remarks> protected virtual Shapes.Shape CreateSerialisationShape(ShapeCache cache, int shapeIndex, CreateMessage shape) { return(new Shapes.Shape(RoutingID, shape)); }