1
Fork 0
mirror of https://github.com/Steffo99/better-tee.git synced 2024-11-22 15:24:18 +00:00
better-tee/Assets/Packages/Mirror/Runtime/MessagePacker.cs
2019-09-17 17:43:32 +02:00

136 lines
4.7 KiB
C#

using System;
using System.ComponentModel;
using UnityEngine;
namespace Mirror
{
// message packing all in one place, instead of constructing headers in all
// kinds of different places
//
// MsgType (1-n bytes)
// Content (ContentSize bytes)
//
// -> we use varint for headers because most messages will result in 1 byte
// type/size headers then instead of always
// using 2 bytes for shorts.
// -> this reduces bandwidth by 10% if average message size is 20 bytes
// (probably even shorter)
public static class MessagePacker
{
public static int GetId<T>() where T : IMessageBase
{
// paul: 16 bits is enough to avoid collisions
// - keeps the message size small because it gets varinted
// - in case of collisions, Mirror will display an error
return typeof(T).FullName.GetStableHashCode() & 0xFFFF;
}
// pack message before sending
// -> pass writer instead of byte[] so we can reuse it
[EditorBrowsable(EditorBrowsableState.Never), Obsolete("Use Pack<T> instead")]
public static byte[] PackMessage(int msgType, MessageBase msg)
{
NetworkWriter writer = NetworkWriterPool.GetWriter();
try
{
// write message type
writer.WriteInt16((short)msgType);
// serialize message into writer
msg.Serialize(writer);
// return byte[]
return writer.ToArray();
}
finally
{
NetworkWriterPool.Recycle(writer);
}
}
// pack message before sending
public static byte[] Pack<T>(T message) where T : IMessageBase
{
NetworkWriter writer = NetworkWriterPool.GetWriter();
try
{
// write message type
int msgType = GetId<T>();
writer.WriteUInt16((ushort)msgType);
// serialize message into writer
message.Serialize(writer);
// return byte[]
return writer.ToArray();
}
finally
{
NetworkWriterPool.Recycle(writer);
}
}
// unpack a message we received
public static T Unpack<T>(byte[] data) where T : IMessageBase, new()
{
NetworkReader reader = new NetworkReader(data);
int msgType = GetId<T>();
int id = reader.ReadUInt16();
if (id != msgType)
throw new FormatException("Invalid message, could not unpack " + typeof(T).FullName);
T message = new T();
message.Deserialize(reader);
return message;
}
// unpack message after receiving
// -> pass NetworkReader so it's less strange if we create it in here
// and pass it upwards.
// -> NetworkReader will point at content afterwards!
public static bool UnpackMessage(NetworkReader messageReader, out int msgType)
{
// read message type (varint)
try
{
msgType = messageReader.ReadUInt16();
return true;
}
catch (System.IO.EndOfStreamException)
{
msgType = 0;
return false;
}
}
internal static NetworkMessageDelegate MessageHandler<T>(Action<NetworkConnection, T> handler) where T : IMessageBase, new() => networkMessage =>
{
// protect against DOS attacks if attackers try to send invalid
// data packets to crash the server/client. there are a thousand
// ways to cause an exception in data handling:
// - invalid headers
// - invalid message ids
// - invalid data causing exceptions
// - negative ReadBytesAndSize prefixes
// - invalid utf8 strings
// - etc.
//
// let's catch them all and then disconnect that connection to avoid
// further attacks.
T message = default;
try
{
message = networkMessage.ReadMessage<T>();
}
catch (Exception exception)
{
Debug.LogError("Closed connection: " + networkMessage.conn.connectionId + ". This can happen if the other side accidentally (or an attacker intentionally) sent invalid data. Reason: " + exception);
networkMessage.conn.Disconnect();
return;
}
handler(networkMessage.conn, message);
};
}
}