Files
RobustToolbox/SS14.Shared/GameStates/GameState.cs
T
Pieter-Jan Briers d1b29a6f94 Update NetSerializer, move to NuGet with it. (#311)
NetSerializer has been updated and moved to the latest version. This
means that the static nonsense it previously had is fixed and we can
properly use ISS14Serializer.
2017-08-08 12:47:18 +02:00

213 lines
7.1 KiB
C#

using Lidgren.Network;
using SS14.Shared.GameObjects;
using SS14.Shared.Interfaces.Serialization;
using SS14.Shared.IoC;
using SS14.Shared.Serialization;
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
namespace SS14.Shared.GameStates
{
[Serializable]
public class GameState : INetSerializableType
{
[NonSerialized] private bool _serialized;
[NonSerialized] private MemoryStream _serializedData;
[NonSerialized] public float GameTime;
/// <summary>
/// Constructor!
/// </summary>
/// <param name="sequence"></param>
public GameState(uint sequence)
{
Sequence = sequence;
}
public uint Sequence { get; private set; }
public List<EntityState> EntityStates { get; set; }
public List<PlayerState> PlayerStates { get; set; }
/// <summary>
/// Creates a delta from two game states
/// </summary>
/// <param name="toState"></param>
/// <param name="fromState"></param>
/// <returns></returns>
public static GameStateDelta operator -(GameState toState, GameState fromState)
{
return Delta(fromState, toState);
}
/// <summary>
/// Applies a delta to a game state
/// </summary>
/// <param name="fromState"></param>
/// <param name="delta"></param>
/// <returns></returns>
public static GameState operator +(GameState fromState, GameStateDelta delta)
{
return Patch(fromState, delta);
}
/// <summary>
/// Applies a delta to a game state
/// </summary>
/// <param name="delta"></param>
/// <param name="fromState"></param>
/// <returns></returns>
public static GameState operator +(GameStateDelta delta, GameState fromState)
{
return Patch(fromState, delta);
}
public static GameStateDelta Delta(GameState fromState, GameState toState)
{
var delta = new GameStateDelta();
delta.Sequence = toState.Sequence;
delta.Create(fromState, toState);
return delta;
}
public static GameState Patch(GameState fromState, GameStateDelta delta)
{
return delta.Apply(fromState);
}
/// <summary>
/// Serializes the game state to its buffer -- allows us to serialize only once, saving some processor time
/// </summary>
private void Serialize()
{
if (_serialized)
return;
_serializedData = new MemoryStream();
var serializer = IoCManager.Resolve<ISS14Serializer>();
serializer.Serialize(_serializedData, this);
_serialized = true;
}
/// <summary>
/// Returns the serialized game state data stream
/// </summary>
/// <returns></returns>
public MemoryStream GetSerializedDataStream()
{
if (!_serialized)
{
Serialize();
}
return _serializedData;
}
/// <summary>
/// Returns the serialized game state data as a byte array
/// </summary>
/// <returns></returns>
public byte[] GetSerializedDataBuffer()
{
if (!_serialized)
{
Serialize();
}
return _serializedData.ToArray();
}
/// <summary>
/// Deserializes a game state from a byte array and returns it
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
public static GameState Deserialize(byte[] data)
{
var serializer = IoCManager.Resolve<ISS14Serializer>();
using (var stream = new MemoryStream(data))
{
return serializer.Deserialize<GameState>(new MemoryStream(data));
}
}
/// <summary>
/// Writes a full game state into a NetOutgoingMessage
/// </summary>
/// <param name="message">NetOutgoingMessage to write to</param>
public int WriteStateMessage(NetOutgoingMessage message)
{
message.Write((byte)NetMessages.FullState);
byte[] stateData = Compress(GetSerializedDataBuffer());
message.Write(Sequence);
message.Write(stateData.Length);
message.Write(stateData);
return stateData.Length;
}
/// <summary>
/// Reads a full gamestate from a NetIncomingMessage
/// </summary>
/// <param name="message">NetIncomingMessage that contains the state data</param>
/// <returns></returns>
public static GameState ReadStateMessage(NetIncomingMessage message)
{
uint sequence = message.ReadUInt32();
int length = message.ReadInt32();
byte[] stateData = Decompress(message.ReadBytes(length));
using (var stateStream = new MemoryStream(stateData))
{
var serializer = IoCManager.Resolve<ISS14Serializer>();
return serializer.Deserialize<GameState>(stateStream);
}
}
/// <summary>
/// Compresses a decompressed state data byte array into a compressed one.
/// </summary>
/// <param name="stateData">full state data</param>
/// <returns></returns>
private static byte[] Compress(byte[] stateData)
{
using (var compressedDataStream = new MemoryStream())
{
using (var gzip = new GZipStream(compressedDataStream, CompressionMode.Compress, true))
{
gzip.Write(stateData, 0, stateData.Length);
}
return compressedDataStream.ToArray();
}
}
/// <summary>
/// Decompresses a compressed state data byte array into a decompressed one.
/// </summary>
/// <param name="compressedStateData">compressed state data</param>
/// <returns></returns>
private static byte[] Decompress(byte[] compressedStateData)
{
// Create a GZIP stream with decompression mode.
// ... Then create a buffer and write into while reading from the GZIP stream.
using (var compressedStream = new MemoryStream(compressedStateData))
using (var stream = new GZipStream(compressedStream, CompressionMode.Decompress))
{
const int size = 2048;
var buffer = new byte[size];
using (var memory = new MemoryStream())
{
int count = 0;
do
{
count = stream.Read(buffer, 0, size);
if (count > 0)
{
memory.Write(buffer, 0, count);
}
} while (count > 0);
return memory.ToArray();
}
}
}
}
}