예제 #1
0
        /// <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());
        }
예제 #2
0
        /// <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());
        }
예제 #3
0
 /// <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());
 }
예제 #4
0
 /// <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());
 }
예제 #5
0
 /// <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());
 }
예제 #6
0
        /// <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());
        }
예제 #7
0
 /// <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));
 }