/// <summary> /// Tries reading a single packet from this stream. To read multiple available /// packets, repeatedly call this method until it returns <c>null</c>. /// </summary> /// <returns> /// A read packet, if one was available. /// </returns> /// <exception cref="System.IO.IOException">If the underlying stream fails.</exception> public IReadablePacket Read() { using (var packet = _stream.Read()) { if (packet != null) { var compressed = packet.ReadBoolean(); var data = packet.ReadByteArray(); if (compressed) { data = SimpleCompression.Decompress(data); } return(new Packet(data, false)); } } return(null); }
/// <summary> /// Create a new message, compressing it if it makes the message smaller, and encrypting it. Finally, we put the /// header in front of it. /// </summary> /// <param name="packet">the packet to make a message of.</param> /// <returns>the message data.</returns> private byte[] MakeMessage(IWritablePacket packet) { // Get the actual data in raw format. var data = packet.GetBuffer(); var length = packet.Length; // If packets are large, try compressing them, see if it helps. // Only start after a certain size. General overhead for gzip // seems to be around 130byte, so make sure we're well beyond that. var flag = false; if (length > 200) { var compressed = SimpleCompression.Compress(data, length); if (compressed.Length < length) { // OK, worth it, it's smaller than before. flag = true; data = compressed; length = compressed.Length; } } // Encrypt the message. data = Crypto.Encrypt(data, 0, length); if ((length & CompressedMask) > 0) { throw new ArgumentException("Packet too big.", "packet"); } // Build the final message: header, then length + compressed bit, then data. var result = new byte[_header.Length + sizeof(uint) + length]; _header.CopyTo(result, 0); BitConverter.GetBytes((uint)length | (flag ? CompressedMask : 0u)).CopyTo(result, _header.Length); data.CopyTo(result, _header.Length + sizeof(uint)); return(result); }
/// <summary> /// Writes the specified packet to the underlying stream. It can then be read /// byte a <c>PacketStream</c> on the other end of the stream. /// </summary> /// <param name="packet">The packet to write.</param> /// <returns>The number of bytesa actually written. This can differ /// from the length of the specified packet due to transforms from /// wrapper streams (encryption, compression, ...)</returns> /// <exception cref="System.IO.IOException">If the underlying stream fails.</exception> public int Write(IWritablePacket packet) { using (var data = new Packet()) { // If packets are large, try compressing them, see if it helps. // Only start after a certain size. General overhead for gzip // seems to be around 130byte, so make sure we're well beyond that. byte[] compressed; if (packet.Length > 200 && (compressed = SimpleCompression.Compress(packet.GetBuffer(), packet.Length)).Length < packet.Length) { // OK, worth it, it's smaller than before. data.Write(true); data.Write(compressed); } else { data.Write(false); data.Write(packet.GetBuffer(), 0, packet.Length); } return(_stream.Write(data)); } }
/// <summary>Parse received data to check if it's a message we can handle, and parse it.</summary> /// <param name="message">the data to parse.</param> /// <returns> /// the data parsed from the message, or <c>null</c> on failure. /// </returns> private IReadablePacket ParseMessage(byte[] message) { // Check the header. if (IsHeaderValid(message)) { // Get message length plus compressed bit. var info = BitConverter.ToUInt32(message, _header.Length); var length = (int)(info & ~CompressedMask); var flag = (info & CompressedMask) > 0; // OK, get the decrypted packet. var data = Crypto.Decrypt(message, _header.Length + sizeof(uint), length); // Is this a compressed message? if (flag) { data = SimpleCompression.Decompress(data); } // Return result as a packet. return(new Packet(data, false)); } return(null); }
/// <summary>Stores the profile to disk, under the specified profile name.</summary> public void Save() { // Get our plain data and hash it. var hasher = new Hasher(); hasher.Write((byte)PlayerClass); if (_data != null) { hasher.Write(_data); } // Write it to a packet, compress it, encrypt it and save it. using (var packet = new Packet()) { // Put our hash and plain data. packet.Write(Header); packet.Write(hasher.Value); packet.Write((byte)PlayerClass); packet.Write(_data); // Compress and encrypt, then save. var compressed = SimpleCompression.Compress(packet.GetBuffer(), packet.Length); var encrypted = Crypto.Encrypt(compressed); var profilePath = GetFullProfilePath(); try { var path = Path.GetDirectoryName(profilePath); if (path != null) { Directory.CreateDirectory(path); } // Make a backup if there's a save already. if (File.Exists(profilePath)) { var backupPath = profilePath + ".bak"; try { if (File.Exists(backupPath)) { File.Delete(backupPath); } File.Move(profilePath, backupPath); } catch (Exception ex) { Logger.WarnException("Failed backing-up saved profile.", ex); } } try { File.WriteAllBytes(profilePath, encrypted); #if DEBUG if (_data != null) { File.WriteAllBytes(profilePath + ".raw", (byte[])_data); } #endif } catch (Exception ex) { Logger.ErrorException("Failed saving profile.", ex); // If we have a backup, try to restore it. var backupPath = profilePath + ".bak"; if (File.Exists(backupPath)) { try { if (File.Exists(profilePath)) { File.Delete(profilePath); } File.Copy(backupPath, profilePath); } catch (Exception ex2) { Logger.WarnException("Failed restoring backed-up profile.", ex2); } } } } catch (Exception ex) { Logger.ErrorException("Failed saving.", ex); } } }
/// <summary> /// Loads this profile from disk. If loading fails this will default to a new profile with the fall-back character /// class. /// </summary> /// <exception cref="ArgumentException">Profile name is invalid.</exception> public void Load(string name) { if (InvalidCharPattern.IsMatch(name)) { throw new ArgumentException(@"Invalid profile name, contains invalid character.", "name"); } // Figure out the path, check if it's valid. Reset(); Name = name; var profilePath = GetFullProfilePath(); if (!File.Exists(profilePath)) { throw new ArgumentException(@"Invalid profile name, no such file.", "name"); } try { // Load the file contents, which are encrypted and compressed. var encrypted = File.ReadAllBytes(profilePath); var compressed = Crypto.Decrypt(encrypted); var plain = SimpleCompression.Decompress(compressed); // Now we have the plain data, handle it as a packet to read our // data from it. using (var packet = new Packet(plain, false)) { // Get file header. if (!CheckHeader(packet.ReadByteArray())) { Logger.Error("Failed loading profile, invalid header, using default."); return; } // Get the hash the data had when writing. var hash = packet.ReadInt32(); // Get the player class. var playerClass = (PlayerClassType)packet.ReadByte(); // And the actual data. var data = packet.ReadPacketizable <Packet>(); // Check if the hash matches. var hasher = new Hasher(); hasher.Write((byte)playerClass); if (data != null) { hasher.Write(data); } if (hasher.Value == hash) { // All is well, keep the data, drop our old data, if any. PlayerClass = playerClass; _data = data; } } } catch (Exception ex) { Logger.ErrorException("Failed loading profile, using default.", ex); } }
/// <summary>Called when the specified key was pressed.</summary> /// <param name="key">The key that was pressed.</param> private void OnKeyDown(Keys key) { switch (key) { case Keys.Right: // Next test. LoadTest(_currentTest + 1); break; case Keys.Left: // Previous test LoadTest(_currentTest - 1); break; case Keys.Space: // Toggle pause. _running = !_running; break; case Keys.Tab: case Keys.Enter: // Manual step. _runOnce = true; break; case Keys.R: // Reset current test but keep camera and run settings. LoadTest(_currentTest, false); break; case Keys.K: // Create a snapshot via serialization. if (_manager != null) { // Kill the mouse joint because deserializing it would // cause a joint that we do not control anymore. if (_mouseJoint >= 0) { _manager.RemoveJoint(_mouseJoint); _mouseJoint = -1; } using (var w = new StreamWriter("before.txt")) { w.Dump(_manager); } _snapshot = new Packet(); _snapshot.Write(_manager); var hasher = new Hasher(); hasher.Write(_manager); _snapshotHash = hasher.Value; _snapshotCompressedSize = SimpleCompression.Compress(_snapshot.GetBuffer(), _snapshot.Length).Length; } break; case Keys.L: // Load a previously created snapshot. if (_snapshot != null) { // Reset test and stuff to avoid invalid references. _mouseJoint = -1; _snapshot.Reset(); _snapshot.ReadPacketizableInto(_manager); var hasher = new Hasher(); hasher.Write(_manager); Debug.Assert(_snapshotHash == hasher.Value); } break; case Keys.C: // Test copy implementation. if (_manager != null) { var copy = new Manager(); copy.AddSystem(new ContentSystem(Content)); copy.AddSystem(new PhysicsSystem(1 / UpdatesPerSecond, new Vector2(0, -10f))); copy.AddSystem(new ContentSystem(Content)); copy.AddSystem(new GraphicsDeviceSystem(_graphics)); copy.AddSystem(new DebugPhysicsRenderSystem { Enabled = true, Scale = 0.1f, Offset = new WorldPoint(0, -12) }); _manager.CopyInto(copy); _manager.Clear(); copy.CopyInto(_manager); } break; case Keys.S: // Toggle whether sleeping is allowed. _physics.AllowSleep = !_physics.AllowSleep; break; case Keys.F1: // Toggle help display. _showHelp = !_showHelp; break; case Keys.F2: // Toggle profiler information. _showProfile = !_showProfile; break; case Keys.F3: // Toggle joints. _renderer.RenderJoints = !_renderer.RenderJoints; break; case Keys.F4: // Toggle contact point and normals. _renderer.RenderContactPoints = !_renderer.RenderContactPoints; _renderer.RenderContactNormals = !_renderer.RenderContactNormals; break; case Keys.F5: // Toggle contact point normal impulse. _renderer.RenderContactPointNormalImpulse = !_renderer.RenderContactPointNormalImpulse; break; case Keys.F7: // Toggle center of mass. _renderer.RenderCenterOfMass = !_renderer.RenderCenterOfMass; break; case Keys.F8: // Toggle fixture bounding boxes. _renderer.RenderFixtureBounds = !_renderer.RenderFixtureBounds; break; } if (_manager != null) { Tests[_currentTest].OnKeyDown(key); } }