/// <summary> /// Helper function to write a packet to a stream with an optional <see cref="T:CollatedPacketEncoder"/>. /// </summary> /// <param name="stream">Stream to write <paramref name="packet"/> to.</param> /// <param name="encoder">Optional collated packet encoder. May be null for no collation.</param> /// <param name="packet">The packet data to write.</param> /// <remarks> /// The <paramref name="encoder"/> is flushed if required. /// </remarks> static void WritePacket(Stream stream, CollatedPacketEncoder encoder, PacketBuffer packet) { if (packet.Status != PacketBufferStatus.Complete) { if (!packet.FinalisePacket()) { throw new IOException("Packet finalisation failed."); } } if (encoder == null) { stream.Write(packet.Data, 0, packet.Count); } else { if (encoder.Add(packet) == -1) { // Failed to add. Try flushing the packet first. Flush(stream, encoder); if (encoder.Add(packet) == -1) { // Failed after flush. Raise exception. throw new IOException("Failed to collate data packet after flush."); } } } }
/// <summary> /// Instantiates a <code>TcpClient</code> using the given socket and flags. /// </summary> /// <param name="socket">Socket.</param> /// <param name="serverFlags">Server flags.</param> public TcpConnection(TcpClient socket, ServerFlag serverFlags) { _client = socket; EndPoint = _client.Client.RemoteEndPoint as IPEndPoint; ServerFlags = serverFlags; _collator = new CollatedPacketEncoder((serverFlags & ServerFlag.Compress) == ServerFlag.Compress); //_collator.Reset(); _currentResourceProgress.Reset(); }
/// <summary> /// A helper function for flushing <paramref name="encoder"/> into <paramref name="stream"/>. /// </summary> /// <param name="stream">The stream to flush into.</param> /// <param name="encoder">The collated packet encoder to flush and reset. May be null (noop).</param> static void Flush(Stream stream, CollatedPacketEncoder encoder) { if (encoder != null) { // Ensure we have some data to flush. if (encoder.CollatedBytes > 0) { if (!encoder.FinaliseEncoding()) { throw new IOException("Collated encoding finalisation failed."); } stream.Write(encoder.Buffer, 0, encoder.Count); encoder.Reset(); } } }
/// <summary> /// Populates <paramref name="stream"/> with a series of data packets describing a simple, multi-frame scene. /// </summary> /// <param name="stream">The stream to write the scene packets to.</param> public static void PopulateScene(Stream stream, bool collate, bool compress) { CollatedPacketEncoder collator = (collate) ? new CollatedPacketEncoder(compress) : null; List <Shape> shapes = new List <Shape>(); PacketBuffer packet = new PacketBuffer(16 * 1024); uint objId = 1; Vector3 pos = new Vector3(0, 0, 0); Vector3 posDelta = new Vector3(0.5f, 0.5f, 0.5f); ControlMessage endFrameMsg = new ControlMessage(); endFrameMsg.Value32 = 1000000 / 30; // ~30Hz // Initialise scene objects. shapes.Add(new Arrow(objId++, pos, pos + new Vector3(0.2f, 0, 1), 0.05f)); pos += posDelta; shapes.Add(new Box(objId++, pos, new Vector3(0.2f, 0.3f, 0.5f))); pos += posDelta; shapes.Add(new Capsule(objId++, pos, pos + new Vector3(0, 0, 2.0f), 0.2f)); pos += posDelta; shapes.Add(new Cone(objId++, pos, pos + new Vector3(0, 0, 1.0f), 0.2f)); pos += posDelta; shapes.Add(new Cylinder(objId++, pos, pos + new Vector3(0, 0, 2.0f), 0.2f)); pos += posDelta; shapes.Add(new Sphere(objId++, pos)); pos += posDelta; shapes.Add(new Plane(objId++, pos, new Vector3(1, 0.5f, 0.3f).Normalised)); pos += posDelta; shapes.Add(new Star(objId++, pos)); pos += posDelta; // Serialise creation packets. uint progress = 0; int res = 0; foreach (Shape shape in shapes) { shape.WriteCreate(packet); WritePacket(stream, collator, packet); if (shape.IsComplex) { while ((res = shape.WriteData(packet, ref progress)) >= 0) { WritePacket(stream, collator, packet); } } } // Write a frame flush. packet.Reset((ushort)RoutingID.Control, (ushort)ControlMessageID.EndFrame); endFrameMsg.Write(packet); WritePacket(stream, collator, packet); Flush(stream, collator); // Move things around. for (int i = 0; i < 100; ++i) { posDelta = new Vector3(3.0f * (float)Math.Sin(i * (5.0 / Math.PI * 180)), 0, 0); foreach (Shape s in shapes) { s.Position = s.Position + posDelta; s.WriteUpdate(packet); WritePacket(stream, collator, packet); } // Write a frame flush. packet.Reset((ushort)RoutingID.Control, (ushort)ControlMessageID.EndFrame); endFrameMsg.Write(packet); WritePacket(stream, collator, packet); Flush(stream, collator); } Flush(stream, collator); stream.Flush(); }
public void CollationTest(bool compress) { // Allocate encoder. CollatedPacketEncoder encoder = new CollatedPacketEncoder(compress); // Create a mesh object to generate some messages. List <Vector3> vertices = new List <Vector3>(); List <Vector3> normals = new List <Vector3>(); List <int> indices = new List <int>(); Common.MakeLowResSphere(vertices, indices, normals); MeshShape mesh = new MeshShape(Net.MeshDrawType.Triangles, vertices.ToArray(), indices.ToArray()); mesh.ID = 42; mesh.Normals = normals.ToArray(); // Use the encoder as a connection. // The Create() call will pack the mesh create message and multiple data messages. int wroteBytes = encoder.Create(mesh); Assert.True(wroteBytes > 0); Assert.True(encoder.FinaliseEncoding()); // Allocate a reader. Contains a CollatedPacketDecoder. System.IO.MemoryStream memoryStream = new System.IO.MemoryStream(encoder.Buffer, 0, encoder.Count); PacketStreamReader decoder = new PacketStreamReader(memoryStream); PacketBuffer packet; MeshShape readMesh = new MeshShape(); int packetCount = 0; long processedBytes = 0; while ((packet = decoder.NextPacket(ref processedBytes)) != null) { NetworkReader reader = new NetworkReader(packet.CreateReadStream(true)); ++packetCount; Assert.True(packet.ValidHeader); Assert.Equal(packet.Header.Marker, PacketHeader.PacketMarker); Assert.Equal(packet.Header.VersionMajor, PacketHeader.PacketVersionMajor); Assert.Equal(packet.Header.VersionMinor, PacketHeader.PacketVersionMinor); Assert.Equal(packet.Header.RoutingID, mesh.RoutingID); // Peek the shape ID. uint shapeId = packet.PeekUInt32(PacketHeader.Size); Assert.Equal(shapeId, mesh.ID); switch ((ObjectMessageID)packet.Header.MessageID) { case ObjectMessageID.Create: Assert.True(readMesh.ReadCreate(packet, reader)); break; case ObjectMessageID.Update: Assert.True(readMesh.ReadUpdate(packet, reader)); break; case ObjectMessageID.Data: Assert.True(readMesh.ReadData(packet, reader)); break; } } Assert.True(packetCount > 0); // FIXME: Does not match, but results are fine. processedBytes is 10 greater than wroteBytes. //Assert.Equal(processedBytes, wroteBytes); // Validate what we've read back. ShapeTestFramework.ValidateShape(readMesh, mesh, new Dictionary <ulong, Resource>()); }