/// <summary> /// Create an encoder with the target initial buffer size. /// </summary> /// <param name="compress">True to compress collated data.</param> /// <param name="initialBufferSize">The initial buffer size (bytes).</param> public CollatedPacketEncoder(bool compress, int initialBufferSize = 64 * 1024) { CollatedBytes = 0; _collationStream = _dataStream = new CompressionBuffer(initialBufferSize); _packet = new PacketBuffer(); // Prime the output stream, writing the initial header. PacketHeader header = PacketHeader.Create((ushort)RoutingID.CollatedPacket, 0); CollatedPacketMessage msg = new CollatedPacketMessage(); msg.Flags = (ushort)((compress) ? CollatedPacketFlag.GZipCompressed : 0u); msg.Reserved = 0; msg.UncompressedBytes = 0; NetworkWriter writer = new NetworkWriter(_dataStream); header.Write(writer); msg.Write(writer); _resetPosition = (int)_dataStream.Position; CompressionEnabled = compress; if (compress) { _collationStream = new GZipStream(_dataStream, CompressionMode.Compress, CompressionLevel.BestCompression); } }
private void OnWorldSendGameNumber(byte[] packet, Channel channel) { var game = Serialize <WorldSendGameNumber>(packet); _myPlayer = game.Name; Send(Deserialize <PacketHeader>(PacketHeader.Create(PacketCommand.C2S_QueryStatusReq, 0))); }
private void OnSyncVersionAns(byte[] packet, Channel channel) { if (!_gameStarted) { var answer = Serialize <SynchVersionAns>(packet); //Log.Write("Setting up for game mode: {0}", answer.GameMode); _isOdin = answer.GameMode.Equals("ODIN"); // yay linq! var player = answer.players.ToList().Find(p => p.userId == _mySummonerId); _isOrder = player.teamId == 0x64; Send(Deserialize <PacketHeader>(PacketHeader.Create(PacketCommand.C2S_ClientReady, 0)), Channel.LoadingScreen); var ping = PingLoadInfo.Create(100, 2, _mySummonerId); Send(Deserialize <PingLoadInfo>(ping)); Send(Deserialize <PacketHeader>(PacketHeader.Create(PacketCommand.C2S_CharLoaded, 0))); Send(Deserialize <StartGame>(StartGame.Create())); } }
private void FinaliseOutput(BinaryWriter writer, uint frameCount) { // Rewind the stream to the beginning and find the first RoutingID.ServerInfo message // and RoutingID.Control message with a ControlMessageID.FrameCount ID. These should be // the first and second messages in the stream. // We'll limit searching to the first 5 messages. long serverInfoMessageStart = -1; long frameCountMessageStart = -1; writer.Flush(); // Extract the stream from the writer. The first stream may be a GZip stream, in which case we must // extract the stream that is writing to instead as we can't rewind compression streams // and we wrote the header raw. writer.BaseStream.Flush(); Stream outStream = null; CollationStream zipStream = writer.BaseStream as CollationStream; if (zipStream != null) { outStream = zipStream.BaseStream; zipStream.Flush(); } else { outStream = writer.BaseStream; } // Check we are allowed to seek the stream. if (!outStream.CanSeek) { // Stream does not support seeking. The frame count will not be fixed. return; } // Record the initial stream position to restore later. long restorePos = outStream.Position; outStream.Seek(0, SeekOrigin.Begin); long streamPos = 0; byte[] headerBuffer = new byte[PacketHeader.Size]; PacketHeader header = new PacketHeader(); byte[] markerValidationBytes = BitConverter.GetBytes(Tes.IO.Endian.ToNetwork(PacketHeader.PacketMarker)); byte[] markerBytes = new byte[markerValidationBytes.Length]; bool markerValid = false; int attemptsRemaining = 5; int byteReadLimit = 0; markerBytes[0] = 0; while ((frameCountMessageStart < 0 || serverInfoMessageStart < 0) && attemptsRemaining > 0 && outStream.CanRead) { --attemptsRemaining; markerValid = false; // Limit the number of bytes we try read in each attempt. byteReadLimit = 1024; while (byteReadLimit > 0) { --byteReadLimit; outStream.Read(markerBytes, 0, 1); if (markerBytes[0] == markerValidationBytes[0]) { markerValid = true; int i = 1; for (i = 1; markerValid && outStream.CanRead && i < markerValidationBytes.Length; ++i) { outStream.Read(markerBytes, i, 1); markerValid = markerValid && markerBytes[i] == markerValidationBytes[i]; } if (markerValid) { break; } else { // We've failed to fully validate the maker. However, we did read and validate // one byte in the marker, then continued reading until the failure. It's possible // that the last byte read, the failed byte, may be the start of the actual marker. // We check this below, and if so, we rewind the stream one byte in order to // start validation from there on the next iteration. We can ignore the byte if // it is does not match the first validation byte. We are unlikely to ever make this // match though. --i; // Go back to the last read byte. if (markerBytes[i] == markerValidationBytes[0]) { // Potentially the start of a new marker. Rewind the stream to attempt to validate it. outStream.Seek(-1, SeekOrigin.Current); } } } } if (markerValid && outStream.CanRead) { // Potential packet target. Record the stream position at the start of the marker. streamPos = outStream.Position - markerBytes.Length; outStream.Seek(streamPos, SeekOrigin.Begin); // Test the packet. int bytesRead = outStream.Read(headerBuffer, 0, headerBuffer.Length); if (bytesRead == headerBuffer.Length) { // Create a packet. if (header.Read(new NetworkReader(new MemoryStream(headerBuffer, false)))) { // Header is OK. Looking for RoutingID.Control if (header.RoutingID == (ushort)RoutingID.ServerInfo) { serverInfoMessageStart = streamPos; } else if (header.RoutingID == (ushort)RoutingID.Control) { // It's control message. Complete and validate the packet. // Read the header. Determine the expected size and read that much more data. PacketBuffer packet = new PacketBuffer(header.PacketSize + Crc16.CrcSize); packet.Emplace(headerBuffer, bytesRead); packet.Emplace(outStream, header.PacketSize + Crc16.CrcSize - bytesRead); if (packet.Status == PacketBufferStatus.Complete) { // Packet complete. Extract the control message. NetworkReader packetReader = new NetworkReader(packet.CreateReadStream(true)); ControlMessage message = new ControlMessage(); if (message.Read(packetReader) && header.MessageID == (ushort)ControlMessageID.FrameCount) { // Found the message location. frameCountMessageStart = streamPos; } } } else { // At this point, we've failed to find the right kind of header. We could use the payload size to // skip ahead in the stream which should align exactly to the next message. // Not done for initial testing. } } } } } if (serverInfoMessageStart >= 0) { // Found the correct location. Seek the stream to here and write a new FrameCount control message. outStream.Seek(serverInfoMessageStart, SeekOrigin.Begin); PacketBuffer packet = new PacketBuffer(); header = PacketHeader.Create((ushort)RoutingID.ServerInfo, 0); packet.WriteHeader(header); _serverInfo.Write(packet); packet.FinalisePacket(); BinaryWriter patchWriter = new Tes.IO.NetworkWriter(outStream); packet.ExportTo(patchWriter); patchWriter.Flush(); } if (frameCountMessageStart >= 0) { // Found the correct location. Seek the stream to here and write a new FrameCount control message. outStream.Seek(frameCountMessageStart, SeekOrigin.Begin); PacketBuffer packet = new PacketBuffer(); ControlMessage frameCountMsg = new ControlMessage(); header = PacketHeader.Create((ushort)RoutingID.Control, (ushort)ControlMessageID.FrameCount); frameCountMsg.ControlFlags = 0; frameCountMsg.Value32 = frameCount; // Placeholder. Frame count is currently unknown. frameCountMsg.Value64 = 0; packet.WriteHeader(header); frameCountMsg.Write(packet); packet.FinalisePacket(); BinaryWriter patchWriter = new Tes.IO.NetworkWriter(outStream); packet.ExportTo(patchWriter); patchWriter.Flush(); } if (outStream.Position != restorePos) { outStream.Seek(restorePos, SeekOrigin.Begin); } }