From 528a2b59e1b5519ddf0bebff6e92300705656952 Mon Sep 17 00:00:00 2001
From: chai <215380520@qq.com>
Date: Fri, 13 Oct 2023 20:28:43 +0800
Subject: + MultiplayerToolkit
---
"Documents/\350\265\204\346\226\231.xlsx" | Bin 1572027 -> 2453852 bytes
.../Assets/MultiplayerToolkit/IRecyclable.cs | 29 --
.../Assets/MultiplayerToolkit/Message.meta | 8 +
.../MultiplayerToolkit/Message/IRecyclable.cs | 29 ++
.../MultiplayerToolkit/Message/MessageReader.cs | 469 +++++++++++++++++++++
.../Message/MessageReader.cs.meta | 11 +
.../MultiplayerToolkit/Message/MessageWriter.cs | 318 ++++++++++++++
.../Message/MessageWriter.cs.meta | 11 +
.../MultiplayerToolkit/Message/ObjectPool.cs | 108 +++++
.../Assets/MultiplayerToolkit/MessageReader.cs | 456 --------------------
.../MultiplayerToolkit/MessageReader.cs.meta | 11 -
.../Assets/MultiplayerToolkit/MessageWriter.cs | 307 --------------
.../MultiplayerToolkit/MessageWriter.cs.meta | 11 -
.../Assets/MultiplayerToolkit/ObjectPool.cs | 108 -----
.../Assets/MultiplayerToolkit/Test/UnitTests.meta | 8 +
.../Test/UnitTests/MessageUnitTests.cs | 11 +
.../Test/UnitTests/MessageUnitTests.cs.meta | 11 +
Projects/Message/Message/.gitignore | 194 +++++++++
Projects/Message/Message/IRecyclable.cs | 29 ++
Projects/Message/Message/Message.csproj | 74 ++++
Projects/Message/Message/Message.lutconfig | 6 +
Projects/Message/Message/Message.sln | 25 ++
Projects/Message/Message/MessageReader.cs | 469 +++++++++++++++++++++
Projects/Message/Message/MessageWriter.cs | 318 ++++++++++++++
Projects/Message/Message/ObjectPool.cs | 108 +++++
.../Message/Message/Properties/AssemblyInfo.cs | 20 +
Projects/Message/Message/UnitTest1.cs | 16 +
Projects/Message/Message/packages.config | 5 +
.../Hazel.UnitTests/MessageReaderTests.cs | 29 +-
Tools/Hazel-Networking/Hazel.lutconfig | 6 +
Tools/Hazel-Networking/Hazel/MessageReader.cs | 74 ++--
Tools/Hazel-Networking/Hazel/MessageWriter.cs | 22 +-
.../Hazel/Udp/UdpClientConnection.cs | 6 +-
.../Hazel/Udp/UdpConnection.Reliable.cs | 15 +-
Tools/Hazel-Networking/Hazel/Udp/UdpConnection.cs | 4 +-
35 files changed, 2367 insertions(+), 959 deletions(-)
delete mode 100644 MultiplayerToolkit/Assets/MultiplayerToolkit/IRecyclable.cs
create mode 100644 MultiplayerToolkit/Assets/MultiplayerToolkit/Message.meta
create mode 100644 MultiplayerToolkit/Assets/MultiplayerToolkit/Message/IRecyclable.cs
create mode 100644 MultiplayerToolkit/Assets/MultiplayerToolkit/Message/MessageReader.cs
create mode 100644 MultiplayerToolkit/Assets/MultiplayerToolkit/Message/MessageReader.cs.meta
create mode 100644 MultiplayerToolkit/Assets/MultiplayerToolkit/Message/MessageWriter.cs
create mode 100644 MultiplayerToolkit/Assets/MultiplayerToolkit/Message/MessageWriter.cs.meta
create mode 100644 MultiplayerToolkit/Assets/MultiplayerToolkit/Message/ObjectPool.cs
delete mode 100644 MultiplayerToolkit/Assets/MultiplayerToolkit/MessageReader.cs
delete mode 100644 MultiplayerToolkit/Assets/MultiplayerToolkit/MessageReader.cs.meta
delete mode 100644 MultiplayerToolkit/Assets/MultiplayerToolkit/MessageWriter.cs
delete mode 100644 MultiplayerToolkit/Assets/MultiplayerToolkit/MessageWriter.cs.meta
delete mode 100644 MultiplayerToolkit/Assets/MultiplayerToolkit/ObjectPool.cs
create mode 100644 MultiplayerToolkit/Assets/MultiplayerToolkit/Test/UnitTests.meta
create mode 100644 MultiplayerToolkit/Assets/MultiplayerToolkit/Test/UnitTests/MessageUnitTests.cs
create mode 100644 MultiplayerToolkit/Assets/MultiplayerToolkit/Test/UnitTests/MessageUnitTests.cs.meta
create mode 100644 Projects/Message/Message/.gitignore
create mode 100644 Projects/Message/Message/IRecyclable.cs
create mode 100644 Projects/Message/Message/Message.csproj
create mode 100644 Projects/Message/Message/Message.lutconfig
create mode 100644 Projects/Message/Message/Message.sln
create mode 100644 Projects/Message/Message/MessageReader.cs
create mode 100644 Projects/Message/Message/MessageWriter.cs
create mode 100644 Projects/Message/Message/ObjectPool.cs
create mode 100644 Projects/Message/Message/Properties/AssemblyInfo.cs
create mode 100644 Projects/Message/Message/UnitTest1.cs
create mode 100644 Projects/Message/Message/packages.config
create mode 100644 Tools/Hazel-Networking/Hazel.lutconfig
diff --git "a/Documents/\350\265\204\346\226\231.xlsx" "b/Documents/\350\265\204\346\226\231.xlsx"
index 94d5672..f390a9a 100644
Binary files "a/Documents/\350\265\204\346\226\231.xlsx" and "b/Documents/\350\265\204\346\226\231.xlsx" differ
diff --git a/MultiplayerToolkit/Assets/MultiplayerToolkit/IRecyclable.cs b/MultiplayerToolkit/Assets/MultiplayerToolkit/IRecyclable.cs
deleted file mode 100644
index 17d3104..0000000
--- a/MultiplayerToolkit/Assets/MultiplayerToolkit/IRecyclable.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-
-namespace MultiplayerToolkit
-{
- ///
- /// Interface for all items that can be returned to an object pool.
- ///
- ///
- public interface IRecyclable
- {
- ///
- /// Returns this object back to the object pool.
- ///
- ///
- ///
- /// Calling this when you are done with the object returns the object back to a pool in order to be reused.
- /// This can reduce the amount of work the GC has to do dramatically but it is optional to call this.
- ///
- ///
- /// Calling this indicates to Hazel that this can be reused and thus you should only call this when you are
- /// completely finished with the object as the contents can be overwritten at any point after.
- ///
- ///
- void Recycle();
- }
-}
diff --git a/MultiplayerToolkit/Assets/MultiplayerToolkit/Message.meta b/MultiplayerToolkit/Assets/MultiplayerToolkit/Message.meta
new file mode 100644
index 0000000..b3bcd16
--- /dev/null
+++ b/MultiplayerToolkit/Assets/MultiplayerToolkit/Message.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 3227b22007e540546967afc661e59c92
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/MultiplayerToolkit/Assets/MultiplayerToolkit/Message/IRecyclable.cs b/MultiplayerToolkit/Assets/MultiplayerToolkit/Message/IRecyclable.cs
new file mode 100644
index 0000000..17d3104
--- /dev/null
+++ b/MultiplayerToolkit/Assets/MultiplayerToolkit/Message/IRecyclable.cs
@@ -0,0 +1,29 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace MultiplayerToolkit
+{
+ ///
+ /// Interface for all items that can be returned to an object pool.
+ ///
+ ///
+ public interface IRecyclable
+ {
+ ///
+ /// Returns this object back to the object pool.
+ ///
+ ///
+ ///
+ /// Calling this when you are done with the object returns the object back to a pool in order to be reused.
+ /// This can reduce the amount of work the GC has to do dramatically but it is optional to call this.
+ ///
+ ///
+ /// Calling this indicates to Hazel that this can be reused and thus you should only call this when you are
+ /// completely finished with the object as the contents can be overwritten at any point after.
+ ///
+ ///
+ void Recycle();
+ }
+}
diff --git a/MultiplayerToolkit/Assets/MultiplayerToolkit/Message/MessageReader.cs b/MultiplayerToolkit/Assets/MultiplayerToolkit/Message/MessageReader.cs
new file mode 100644
index 0000000..7ec134b
--- /dev/null
+++ b/MultiplayerToolkit/Assets/MultiplayerToolkit/Message/MessageReader.cs
@@ -0,0 +1,469 @@
+using System;
+using System.IO;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Text;
+
+namespace MultiplayerToolkit
+{
+ ///
+ ///
+ ///
+ public class MessageReader : IRecyclable
+ {
+ public static readonly ObjectPool ReaderPool = new ObjectPool(() => new MessageReader());
+
+ public byte[] Buffer; // √ 缓冲区,会被子协议共享这个缓冲区
+ public byte Tag; // √ 协议名
+
+ public int Length; // √ 协议长度,对应的是MessageWriter里写入的2bytes包长度(不含length和tag)
+ public int Offset; // √ length和tag后面的位置(在Buffer中的位置),不是游标,不会随读取发生改变。会影响readHead的值
+
+ //数据位于Buffer中Offset索引开始Length长度,协议位于Buffer中Offset-3开始的Length+3长度
+
+ public int BytesRemaining => this.Length - this.Position;
+
+ private MessageReader Parent; // 保存父协议索引,和父协议共享父协议的Buffer
+
+ public int Position
+ {
+ get { return this._position; }
+ set
+ {
+ this._position = value; // value是
+ this.readHead = value + Offset;
+ }
+ }
+
+ private int _position; // √ 读取游标,相对于协议内容部分的偏移,从0开始,子协议也是从0开始,不是在Buffer中的索引
+ private int readHead; // √ 读取游标,协议头部在Buffer中的位置,参考ReadMessage()
+
+ public static MessageReader GetSized(int minSize)
+ {
+ var output = ReaderPool.GetObject();
+
+ if (output.Buffer == null || output.Buffer.Length < minSize)
+ {
+ output.Buffer = new byte[minSize];
+ }
+ else
+ {
+ Array.Clear(output.Buffer, 0, output.Buffer.Length);
+ }
+
+ output.Offset = 0;
+ output.Position = 0;
+ output.Tag = byte.MaxValue;
+ return output;
+ }
+
+ public static MessageReader Get(byte[] buffer)
+ {
+ var output = ReaderPool.GetObject();
+
+ output.Buffer = buffer;
+ output.Offset = 0;
+ output.Position = 0;
+ output.Length = buffer.Length;
+ output.Tag = byte.MaxValue;
+
+ return output;
+ }
+
+ ///
+ /// 创建一个新的父message,保存source。用于缓存message,因为source所在的原父message可能会被销毁,如果需要延迟处理消息,需要拷贝一份新的
+ ///
+ ///
+ ///
+ public static MessageReader CopyMessageIntoParent(MessageReader source)
+ {
+ var output = MessageReader.GetSized(source.Length + 3); //3=2bytes(length) + 1byte(tag)
+ System.Buffer.BlockCopy(source.Buffer, source.Offset - 3, output.Buffer, 0, source.Length + 3);
+
+ output.Offset = 0;
+ output.Position = 0;
+ output.Length = source.Length + 3;
+
+ return output;
+ }
+
+ ///
+ /// 完全复制 一份
+ ///
+ ///
+ ///
+ public static MessageReader Get(MessageReader source)
+ {
+ var output = MessageReader.GetSized(source.Buffer.Length);
+ System.Buffer.BlockCopy(source.Buffer, 0, output.Buffer, 0, source.Buffer.Length);
+
+ output.Offset = source.Offset;
+
+ output._position = source._position;
+ output.readHead = source.readHead;
+
+ output.Length = source.Length;
+ output.Tag = source.Tag;
+
+ return output;
+ }
+
+ public static MessageReader Get(byte[] buffer, int offset)
+ {
+ // Ensure there is at least a header
+ if (offset + 3 > buffer.Length) return null;
+
+ var output = ReaderPool.GetObject();
+
+ output.Buffer = buffer;
+ output.Offset = offset;
+ output.Position = 0;
+
+ output.Length = output.ReadUInt16(); // 读到的值为length
+ output.Tag = output.ReadByte();
+
+ output.Offset += 3;
+ output.Position = 0;
+
+ return output;
+ }
+
+ ///
+ /// Produces a MessageReader using the parent's buffer. This MessageReader should **NOT** be recycled.
+ ///
+ public MessageReader ReadMessage()
+ {
+ // Ensure there is at least a header
+ if (this.BytesRemaining < 3) throw new InvalidDataException($"ReadMessage header is longer than message length: 3 of {this.BytesRemaining}");
+
+ MessageReader output = new MessageReader();
+
+ output.Parent = this;
+ output.Buffer = this.Buffer;
+ output.Offset = this.readHead; // 下面会读取
+ output.Position = 0;
+
+ // 读length和tag的值并保存在字段里
+ output.Length = output.ReadUInt16();
+ output.Tag = output.ReadByte();
+
+ // Offset, readHead齐步走,移到Length和Tag后面,_position=0
+ output.Offset += 3; // 3=length+tag
+ output.Position = 0;
+
+ if (this.BytesRemaining < output.Length + 3) throw new InvalidDataException($"Message Length at Position {this.readHead} is longer than message length: {output.Length + 3} of {this.BytesRemaining}");
+
+ this.Position += output.Length + 3; //跳过整个子协议
+ return output;
+ }
+
+ ///
+ /// Produces a MessageReader with a new buffer. This MessageReader should be recycled.
+ ///
+ public MessageReader ReadMessageAsNewBuffer()
+ {
+ if (this.BytesRemaining < 3) throw new InvalidDataException($"ReadMessage header is longer than message length: 3 of {this.BytesRemaining}");
+
+ var len = this.ReadUInt16();
+ var tag = this.ReadByte();
+
+ if (this.BytesRemaining < len) throw new InvalidDataException($"Message Length at Position {this.readHead} is longer than message length: {len} of {this.BytesRemaining}");
+
+ var output = MessageReader.GetSized(len);
+
+ Array.Copy(this.Buffer, this.readHead, output.Buffer, 0, len);
+
+ output.Length = len;
+ output.Tag = tag;
+
+ this.Position += output.Length;
+ return output;
+ }
+
+ public MessageWriter StartWriter()
+ {
+ var output = new MessageWriter(this.Buffer);
+ output.Position = this.readHead;
+ return output;
+ }
+
+ public MessageReader Duplicate()
+ {
+ var output = GetSized(this.Length);
+ Array.Copy(this.Buffer, this.Offset, output.Buffer, 0, this.Length);
+ output.Length = this.Length;
+ output.Offset = 0;
+ output.Position = 0;
+
+ return output;
+ }
+
+ public void RemoveMessage(MessageReader reader)
+ {
+ var temp = MessageReader.GetSized(reader.Buffer.Length);
+ try
+ {
+ var headerOffset = reader.Offset - 3;
+ var endOfMessage = reader.Offset + reader.Length;
+ var len = reader.Buffer.Length - endOfMessage;
+
+ Array.Copy(reader.Buffer, endOfMessage, temp.Buffer, 0, len);
+ Array.Copy(temp.Buffer, 0, this.Buffer, headerOffset, len);
+
+ this.AdjustLength(reader.Offset, reader.Length + 3);
+ }
+ finally
+ {
+ temp.Recycle();
+ }
+ }
+
+ //public void InsertMessage(MessageReader reader, MessageWriter writer)
+ //{
+ // var temp = MessageReader.GetSized(reader.Buffer.Length);
+ // try
+ // {
+ // var headerOffset = reader.Offset - 3;
+ // var startOfMessage = reader.Offset;
+ // var len = reader.Buffer.Length - startOfMessage;
+ // int writerOffset = 3;
+ // switch (writer.SendOption)
+ // {
+ // case SendOption.Reliable:
+ // writerOffset = 3;
+ // break;
+ // case SendOption.None:
+ // writerOffset = 1;
+ // break;
+ // }
+
+ // //store the original buffer in temp
+ // Array.Copy(reader.Buffer, headerOffset, temp.Buffer, 0, len);
+
+ // //put the contents of writer in at headerOffset
+ // Array.Copy(writer.Buffer, writerOffset, this.Buffer, headerOffset, writer.Length - writerOffset);
+
+ // //put the original buffer in after that
+ // Array.Copy(temp.Buffer, 0, this.Buffer, headerOffset + (writer.Length - writerOffset), len - writer.Length);
+
+ // this.AdjustLength(-1 * reader.Offset, -1 * (writer.Length - writerOffset));
+ // }
+ // finally
+ // {
+ // temp.Recycle();
+ // }
+ //}
+
+ private void AdjustLength(int offset, int amount)
+ {
+ if (this.readHead > offset)
+ {
+ this.Position -= amount;
+ }
+
+ if (Parent != null)
+ {
+ var lengthOffset = this.Offset - 3;
+ var curLen = this.Buffer[lengthOffset]
+ | (this.Buffer[lengthOffset + 1] << 8);
+
+ curLen -= amount;
+ this.Length -= amount;
+
+ this.Buffer[lengthOffset] = (byte)curLen;
+ this.Buffer[lengthOffset + 1] = (byte)(this.Buffer[lengthOffset + 1] >> 8);
+
+ Parent.AdjustLength(offset, amount);
+ }
+ }
+
+ public void Recycle()
+ {
+ this.Parent = null;
+ ReaderPool.PutObject(this);
+ }
+
+ #region Read Methods
+ public bool ReadBoolean()
+ {
+ byte val = this.FastByte();
+ return val != 0;
+ }
+
+ public sbyte ReadSByte()
+ {
+ return (sbyte)this.FastByte();
+ }
+
+ public byte ReadByte()
+ {
+ return this.FastByte();
+ }
+
+ public ushort ReadUInt16()
+ {
+ ushort output =
+ (ushort)(this.FastByte()
+ | this.FastByte() << 8);
+ return output;
+ }
+
+ public short ReadInt16()
+ {
+ short output =
+ (short)(this.FastByte()
+ | this.FastByte() << 8);
+ return output;
+ }
+
+ public uint ReadUInt32()
+ {
+ uint output = this.FastByte()
+ | (uint)this.FastByte() << 8
+ | (uint)this.FastByte() << 16
+ | (uint)this.FastByte() << 24;
+
+ return output;
+ }
+
+ public int ReadInt32()
+ {
+ int output = this.FastByte()
+ | this.FastByte() << 8
+ | this.FastByte() << 16
+ | this.FastByte() << 24;
+
+ return output;
+ }
+
+ public ulong ReadUInt64()
+ {
+ ulong output = (ulong)this.FastByte()
+ | (ulong)this.FastByte() << 8
+ | (ulong)this.FastByte() << 16
+ | (ulong)this.FastByte() << 24
+ | (ulong)this.FastByte() << 32
+ | (ulong)this.FastByte() << 40
+ | (ulong)this.FastByte() << 48
+ | (ulong)this.FastByte() << 56;
+
+ return output;
+ }
+
+ public long ReadInt64()
+ {
+ long output = (long)this.FastByte()
+ | (long)this.FastByte() << 8
+ | (long)this.FastByte() << 16
+ | (long)this.FastByte() << 24
+ | (long)this.FastByte() << 32
+ | (long)this.FastByte() << 40
+ | (long)this.FastByte() << 48
+ | (long)this.FastByte() << 56;
+
+ return output;
+ }
+
+ public unsafe float ReadSingle()
+ {
+ float output = 0;
+ fixed (byte* bufPtr = &this.Buffer[this.readHead])
+ {
+ byte* outPtr = (byte*)&output;
+
+ *outPtr = *bufPtr;
+ *(outPtr + 1) = *(bufPtr + 1);
+ *(outPtr + 2) = *(bufPtr + 2);
+ *(outPtr + 3) = *(bufPtr + 3);
+ }
+
+ this.Position += 4;
+ return output;
+ }
+
+ public string ReadString()
+ {
+ int len = this.ReadPackedInt32();
+ if (this.BytesRemaining < len) throw new InvalidDataException($"Read length is longer than message length: {len} of {this.BytesRemaining}");
+
+ string output = UTF8Encoding.UTF8.GetString(this.Buffer, this.readHead, len);
+
+ this.Position += len;
+ return output;
+ }
+
+ public byte[] ReadBytesAndSize()
+ {
+ int len = this.ReadPackedInt32();
+ if (this.BytesRemaining < len) throw new InvalidDataException($"Read length is longer than message length: {len} of {this.BytesRemaining}");
+
+ return this.ReadBytes(len);
+ }
+
+ public byte[] ReadBytes(int length)
+ {
+ if (this.BytesRemaining < length) throw new InvalidDataException($"Read length is longer than message length: {length} of {this.BytesRemaining}");
+
+ byte[] output = new byte[length];
+ Array.Copy(this.Buffer, this.readHead, output, 0, output.Length);
+ this.Position += output.Length;
+ return output;
+ }
+
+ ///
+ public int ReadPackedInt32()
+ {
+ return (int)this.ReadPackedUInt32();
+ }
+
+ ///
+ public uint ReadPackedUInt32()
+ {
+ bool readMore = true;
+ int shift = 0;
+ uint output = 0;
+
+ while (readMore)
+ {
+ if (this.BytesRemaining < 1) throw new InvalidDataException($"Read length is longer than message length.");
+
+ byte b = this.ReadByte();
+ if (b >= 0x80)
+ {
+ readMore = true;
+ b ^= 0x80;
+ }
+ else
+ {
+ readMore = false;
+ }
+
+ output |= (uint)(b << shift);
+ shift += 7;
+ }
+
+ return output;
+ }
+ #endregion
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private byte FastByte()
+ {
+ this._position++;
+ return this.Buffer[this.readHead++];
+ }
+
+ public unsafe static bool IsLittleEndian()
+ {
+ byte b;
+ unsafe
+ {
+ int i = 1;
+ byte* bp = (byte*)&i;
+ b = *bp;
+ }
+
+ return b == 1;
+ }
+ }
+}
diff --git a/MultiplayerToolkit/Assets/MultiplayerToolkit/Message/MessageReader.cs.meta b/MultiplayerToolkit/Assets/MultiplayerToolkit/Message/MessageReader.cs.meta
new file mode 100644
index 0000000..2f470c6
--- /dev/null
+++ b/MultiplayerToolkit/Assets/MultiplayerToolkit/Message/MessageReader.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 35714cf36eee0fc458f427f0a6cea532
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/MultiplayerToolkit/Assets/MultiplayerToolkit/Message/MessageWriter.cs b/MultiplayerToolkit/Assets/MultiplayerToolkit/Message/MessageWriter.cs
new file mode 100644
index 0000000..b23ce82
--- /dev/null
+++ b/MultiplayerToolkit/Assets/MultiplayerToolkit/Message/MessageWriter.cs
@@ -0,0 +1,318 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+
+namespace MultiplayerToolkit
+{
+
+ ///
+ /// AmongUsùȥ˰ͷش
+ /// ṹ
+ /// -------------------------------------
+ /// 2bytes (ushort)Эijȣ2bytes
+ /// -------------------------------------
+ /// 1byte ЭͣЭ0ԶЭ1
+ /// (1byte) (ԶЭ飬ṩһmodţSteam.UGC.Itemidulong 8bytesеţ֧255mod)
+ /// 2byte (ushort)ЭID
+ ///
+ /// -------------------------------------
+ ///
+ public class MessageWriter : IRecyclable
+ {
+ public const int BYTE_COUNT_OF_LENGTH = 2; // ռ2 bytes
+ public static int BufferSize = 64000; // Э֧64000ֽڣ62.5KB
+ public static readonly ObjectPool WriterPool = new ObjectPool(() => new MessageWriter(BufferSize));
+
+ public byte[] Buffer;
+ public int Length; // ЧbufferеijȣܰǶЭ
+ public int Position; // дλ
+
+ // lengthһڻĴСLengthһÿЭЭݲֵֽ
+
+ private Stack messageStarts = new Stack(); // ¼ЭbufferеʼλãǶṹ
+
+ public MessageWriter(byte[] buffer)
+ {
+ this.Buffer = buffer;
+ this.Length = this.Buffer.Length;
+ }
+
+ ///
+ public MessageWriter(int bufferSize)
+ {
+ this.Buffer = new byte[bufferSize];
+ }
+
+ public byte[] ToByteArray()
+ {
+ byte[] output = new byte[this.Length];
+ System.Buffer.BlockCopy(this.Buffer, 0, output, 0, this.Length);
+ return output;
+ }
+
+ /// The option specifying how the message should be sent.
+ public static MessageWriter Get()
+ {
+ MessageWriter output = WriterPool.GetObject();
+ output.Clear();
+
+ return output;
+ }
+
+ ///
+ /// Ƿexpectedȵ
+ ///
+ ///
+ ///
+ public bool HasBytes(int expected)
+ {
+ return this.Length >= expected;
+ }
+
+ ///
+ public void StartMessage()
+ {
+ var messageStart = this.Position;
+ messageStarts.Push(messageStart);
+ this.Buffer[messageStart] = 0; // length
+ this.Buffer[messageStart + 1] = 0; // length
+ this.Position += 2; // ushort length
+ }
+
+ ///
+ public void EndMessage()
+ {
+ var lastMessageStart = messageStarts.Pop();
+ ushort length = (ushort)(this.Position - lastMessageStart - 2); // Minus length
+ this.Buffer[lastMessageStart] = (byte)length;
+ this.Buffer[lastMessageStart + 1] = (byte)(length >> 8);
+ }
+
+ ///
+ /// ȡǰ༭messageصһ
+ ///
+ public void CancelMessage()
+ {
+ this.Position = this.messageStarts.Pop();
+ this.Length = this.Position;
+ }
+
+ public void Clear()
+ {
+ Array.Clear(this.Buffer, 0, this.Buffer.Length);
+ this.messageStarts.Clear();
+ this.Length = 0;
+ this.Position = 0;
+ }
+
+ ///
+ /// message
+ ///
+ public void Recycle()
+ {
+ this.Position = this.Length = 0;
+ WriterPool.PutObject(this);
+ }
+
+#region WriteMethods
+
+ public void CopyFrom(MessageReader target)
+ {
+ int offset, length;
+ if (target.Tag == byte.MaxValue)
+ {
+ offset = target.Offset;
+ length = target.Length;
+ }
+ else
+ {
+ offset = target.Offset - 3;
+ length = target.Length + 3;
+ }
+
+ System.Buffer.BlockCopy(target.Buffer, offset, this.Buffer, this.Position, length);
+ this.Position += length;
+ if (this.Position > this.Length) this.Length = this.Position;
+ }
+
+ public void Write(bool value)
+ {
+ this.Buffer[this.Position++] = (byte)(value ? 1 : 0);
+ if (this.Position > this.Length) this.Length = this.Position;
+ }
+
+ public void Write(sbyte value)
+ {
+ this.Buffer[this.Position++] = (byte)value;
+ if (this.Position > this.Length) this.Length = this.Position;
+ }
+
+ public void Write(byte value)
+ {
+ this.Buffer[this.Position++] = value;
+ if (this.Position > this.Length) this.Length = this.Position;
+ }
+
+ public void Write(short value)
+ {
+ this.Buffer[this.Position++] = (byte)value;
+ this.Buffer[this.Position++] = (byte)(value >> 8);
+ if (this.Position > this.Length) this.Length = this.Position;
+ }
+
+ public void Write(ushort value)
+ {
+ this.Buffer[this.Position++] = (byte)value;
+ this.Buffer[this.Position++] = (byte)(value >> 8);
+ if (this.Position > this.Length) this.Length = this.Position;
+ }
+
+ public void Write(uint value)
+ {
+ this.Buffer[this.Position++] = (byte)value;
+ this.Buffer[this.Position++] = (byte)(value >> 8);
+ this.Buffer[this.Position++] = (byte)(value >> 16);
+ this.Buffer[this.Position++] = (byte)(value >> 24);
+ if (this.Position > this.Length) this.Length = this.Position;
+ }
+
+ public void Write(int value)
+ {
+ this.Buffer[this.Position++] = (byte)value;
+ this.Buffer[this.Position++] = (byte)(value >> 8);
+ this.Buffer[this.Position++] = (byte)(value >> 16);
+ this.Buffer[this.Position++] = (byte)(value >> 24);
+ if (this.Position > this.Length) this.Length = this.Position;
+ }
+
+ public void Write(ulong value)
+ {
+ this.Buffer[this.Position++] = (byte)value;
+ this.Buffer[this.Position++] = (byte)(value >> 8);
+ this.Buffer[this.Position++] = (byte)(value >> 16);
+ this.Buffer[this.Position++] = (byte)(value >> 24);
+ this.Buffer[this.Position++] = (byte)(value >> 32);
+ this.Buffer[this.Position++] = (byte)(value >> 40);
+ this.Buffer[this.Position++] = (byte)(value >> 48);
+ this.Buffer[this.Position++] = (byte)(value >> 56);
+ if (this.Position > this.Length) this.Length = this.Position;
+ }
+
+ public void Write(long value)
+ {
+ this.Buffer[this.Position++] = (byte)value;
+ this.Buffer[this.Position++] = (byte)(value >> 8);
+ this.Buffer[this.Position++] = (byte)(value >> 16);
+ this.Buffer[this.Position++] = (byte)(value >> 24);
+ this.Buffer[this.Position++] = (byte)(value >> 32);
+ this.Buffer[this.Position++] = (byte)(value >> 40);
+ this.Buffer[this.Position++] = (byte)(value >> 48);
+ this.Buffer[this.Position++] = (byte)(value >> 56);
+ if (this.Position > this.Length) this.Length = this.Position;
+ }
+
+ public unsafe void Write(float value)
+ {
+ fixed (byte* ptr = &this.Buffer[this.Position])
+ {
+ byte* valuePtr = (byte*)&value;
+
+ *ptr = *valuePtr;
+ *(ptr + 1) = *(valuePtr + 1);
+ *(ptr + 2) = *(valuePtr + 2);
+ *(ptr + 3) = *(valuePtr + 3);
+ }
+
+ this.Position += 4;
+ if (this.Position > this.Length) this.Length = this.Position;
+ }
+
+ public void Write(string value)
+ {
+ var bytes = UTF8Encoding.UTF8.GetBytes(value);
+ this.WritePacked(bytes.Length);
+ this.Write(bytes);
+ }
+
+ public void WriteBytesAndSize(byte[] bytes)
+ {
+ this.WritePacked((uint)bytes.Length);
+ this.Write(bytes);
+ }
+
+ public void WriteBytesAndSize(byte[] bytes, int length)
+ {
+ this.WritePacked((uint)length);
+ this.Write(bytes, length);
+ }
+
+ public void WriteBytesAndSize(byte[] bytes, int offset, int length)
+ {
+ this.WritePacked((uint)length);
+ this.Write(bytes, offset, length);
+ }
+
+ public void Write(byte[] bytes)
+ {
+ Array.Copy(bytes, 0, this.Buffer, this.Position, bytes.Length);
+ this.Position += bytes.Length;
+ if (this.Position > this.Length) this.Length = this.Position;
+ }
+
+ public void Write(byte[] bytes, int offset, int length)
+ {
+ Array.Copy(bytes, offset, this.Buffer, this.Position, length);
+ this.Position += length;
+ if (this.Position > this.Length) this.Length = this.Position;
+ }
+
+ public void Write(byte[] bytes, int length)
+ {
+ Array.Copy(bytes, 0, this.Buffer, this.Position, length);
+ this.Position += length;
+ if (this.Position > this.Length) this.Length = this.Position;
+ }
+
+ ///
+ public void WritePacked(int value)
+ {
+ this.WritePacked((uint)value);
+ }
+
+ ///
+ public void WritePacked(uint value)
+ {
+ do
+ {
+ byte b = (byte)(value & 0xFF);
+ if (value >= 0x80)
+ {
+ b |= 0x80;
+ }
+
+ this.Write(b);
+ value >>= 7;
+ } while (value > 0);
+ }
+#endregion
+
+ public void Write(MessageWriter msg)
+ {
+ this.Write(msg.Buffer, 0, msg.Length);
+ }
+
+ public unsafe static bool IsLittleEndian()
+ {
+ byte b;
+ unsafe
+ {
+ int i = 1;
+ byte* bp = (byte*)&i;
+ b = *bp;
+ }
+
+ return b == 1;
+ }
+ }
+}
diff --git a/MultiplayerToolkit/Assets/MultiplayerToolkit/Message/MessageWriter.cs.meta b/MultiplayerToolkit/Assets/MultiplayerToolkit/Message/MessageWriter.cs.meta
new file mode 100644
index 0000000..ec9023e
--- /dev/null
+++ b/MultiplayerToolkit/Assets/MultiplayerToolkit/Message/MessageWriter.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 8d9ee77993c8eff4ba8b0a9026505b40
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/MultiplayerToolkit/Assets/MultiplayerToolkit/Message/ObjectPool.cs b/MultiplayerToolkit/Assets/MultiplayerToolkit/Message/ObjectPool.cs
new file mode 100644
index 0000000..e42cdd6
--- /dev/null
+++ b/MultiplayerToolkit/Assets/MultiplayerToolkit/Message/ObjectPool.cs
@@ -0,0 +1,108 @@
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Threading;
+
+namespace MultiplayerToolkit
+{
+ ///
+ /// A fairly simple object pool for items that will be created a lot.
+ ///
+ /// The type that is pooled.
+ ///
+ public sealed class ObjectPool where T : IRecyclable
+ {
+ private int numberCreated;
+ public int NumberCreated { get { return numberCreated; } }
+
+ public int NumberInUse { get { return this.inuse.Count; } }
+ public int NumberNotInUse { get { return this.pool.Count; } }
+ public int Size { get { return this.NumberInUse + this.NumberNotInUse; } }
+
+#if HAZEL_BAG
+ private readonly ConcurrentBag pool = new ConcurrentBag();
+#else
+ private readonly List pool = new List();
+#endif
+
+ // Unavailable objects
+ private readonly ConcurrentDictionary inuse = new ConcurrentDictionary();
+
+ ///
+ /// The generator for creating new objects.
+ ///
+ ///
+ private readonly Func objectFactory;
+
+ ///
+ /// Internal constructor for our ObjectPool.
+ ///
+ internal ObjectPool(Func objectFactory)
+ {
+ this.objectFactory = objectFactory;
+ }
+
+ ///
+ /// Returns a pooled object of type T, if none are available another is created.
+ ///
+ /// An instance of T.
+ internal T GetObject()
+ {
+#if HAZEL_BAG
+ if (!pool.TryTake(out T item))
+ {
+ Interlocked.Increment(ref numberCreated);
+ item = objectFactory.Invoke();
+ }
+#else
+ T item;
+ lock (this.pool)
+ {
+ if (this.pool.Count > 0)
+ {
+ var idx = this.pool.Count - 1;
+ item = this.pool[idx];
+ this.pool.RemoveAt(idx);
+ }
+ else
+ {
+ Interlocked.Increment(ref numberCreated);
+ item = objectFactory.Invoke();
+ }
+ }
+#endif
+
+ if (!inuse.TryAdd(item, true))
+ {
+ throw new Exception("Duplicate pull " + typeof(T).Name);
+ }
+
+ return item;
+ }
+
+ ///
+ /// Returns an object to the pool.
+ ///
+ /// The item to return.
+ internal void PutObject(T item)
+ {
+ if (inuse.TryRemove(item, out bool b))
+ {
+#if HAZEL_BAG
+ pool.Add(item);
+#else
+ lock (this.pool)
+ {
+ pool.Add(item);
+ }
+#endif
+ }
+ else
+ {
+#if DEBUG
+ throw new Exception("Duplicate add " + typeof(T).Name);
+#endif
+ }
+ }
+ }
+}
diff --git a/MultiplayerToolkit/Assets/MultiplayerToolkit/MessageReader.cs b/MultiplayerToolkit/Assets/MultiplayerToolkit/MessageReader.cs
deleted file mode 100644
index 3bd5db8..0000000
--- a/MultiplayerToolkit/Assets/MultiplayerToolkit/MessageReader.cs
+++ /dev/null
@@ -1,456 +0,0 @@
-using System;
-using System.IO;
-using System.Linq;
-using System.Runtime.CompilerServices;
-using System.Text;
-
-namespace MultiplayerToolkit
-{
- public class MessageReader : IRecyclable
- {
- public static readonly ObjectPool ReaderPool = new ObjectPool(() => new MessageReader());
-
- public byte[] Buffer;
- public byte Tag;
-
- public int Length;
-
- public int BytesRemaining => this.Length - this.Position;
-
- private MessageReader Parent;
-
- public int Position
- {
- get { return this._position; }
- set
- {
- this._position = value;
- this.readHead = value ;
- }
- }
-
- private int _position;
- private int readHead;
-
- public static MessageReader GetSized(int minSize)
- {
- var output = ReaderPool.GetObject();
-
- if (output.Buffer == null || output.Buffer.Length < minSize)
- {
- output.Buffer = new byte[minSize];
- }
- else
- {
- Array.Clear(output.Buffer, 0, output.Buffer.Length);
- }
-
- output.Offset = 0;
- output.Position = 0;
- output.Tag = byte.MaxValue;
- return output;
- }
-
- public static MessageReader Get(byte[] buffer)
- {
- var output = ReaderPool.GetObject();
-
- output.Buffer = buffer;
- output.Offset = 0;
- output.Position = 0;
- output.Length = buffer.Length;
- output.Tag = byte.MaxValue;
-
- return output;
- }
-
- public static MessageReader CopyMessageIntoParent(MessageReader source)
- {
- var output = MessageReader.GetSized(source.Length + 3);
- System.Buffer.BlockCopy(source.Buffer, source.Offset - 3, output.Buffer, 0, source.Length + 3);
-
- output.Offset = 0;
- output.Position = 0;
- output.Length = source.Length + 3;
-
- return output;
- }
-
- ///
- /// 复制一份
- ///
- ///
- ///
- public static MessageReader Get(MessageReader source)
- {
- var output = MessageReader.GetSized(source.Buffer.Length);
- System.Buffer.BlockCopy(source.Buffer, 0, output.Buffer, 0, source.Buffer.Length);
-
- output.Offset = source.Offset;
-
- output._position = source._position;
- output.readHead = source.readHead;
-
- output.Length = source.Length;
- output.Tag = source.Tag;
-
- return output;
- }
-
- public static MessageReader Get(byte[] buffer, int offset)
- {
- // Ensure there is at least a header
- if (offset + 3 > buffer.Length) return null;
-
- var output = ReaderPool.GetObject();
-
- output.Buffer = buffer;
- output.Offset = offset;
- output.Position = 0;
-
- output.Length = output.ReadUInt16();
- output.Tag = output.ReadByte();
-
- output.Offset += 3;
- output.Position = 0;
-
- return output;
- }
-
- ///
- /// Produces a MessageReader using the parent's buffer. This MessageReader should **NOT** be recycled.
- ///
- public MessageReader ReadMessage()
- {
- // Ensure there is at least a header
- if (this.BytesRemaining < 3) throw new InvalidDataException($"ReadMessage header is longer than message length: 3 of {this.BytesRemaining}");
-
- var output = new MessageReader();
-
- output.Parent = this;
- output.Buffer = this.Buffer;
- output.Offset = this.readHead;
- output.Position = 0;
-
- output.Length = output.ReadUInt16();
- output.Tag = output.ReadByte();
-
- output.Offset += 3;
- output.Position = 0;
-
- if (this.BytesRemaining < output.Length + 3) throw new InvalidDataException($"Message Length at Position {this.readHead} is longer than message length: {output.Length + 3} of {this.BytesRemaining}");
-
- this.Position += output.Length + 3;
- return output;
- }
-
- ///
- /// Produces a MessageReader with a new buffer. This MessageReader should be recycled.
- ///
- public MessageReader ReadMessageAsNewBuffer()
- {
- if (this.BytesRemaining < 3) throw new InvalidDataException($"ReadMessage header is longer than message length: 3 of {this.BytesRemaining}");
-
- var len = this.ReadUInt16();
- var tag = this.ReadByte();
-
- if (this.BytesRemaining < len) throw new InvalidDataException($"Message Length at Position {this.readHead} is longer than message length: {len} of {this.BytesRemaining}");
-
- var output = MessageReader.GetSized(len);
-
- Array.Copy(this.Buffer, this.readHead, output.Buffer, 0, len);
-
- output.Length = len;
- output.Tag = tag;
-
- this.Position += output.Length;
- return output;
- }
-
- public MessageWriter StartWriter()
- {
- var output = new MessageWriter(this.Buffer);
- output.Position = this.readHead;
- return output;
- }
-
- public MessageReader Duplicate()
- {
- var output = GetSized(this.Length);
- Array.Copy(this.Buffer, this.Offset, output.Buffer, 0, this.Length);
- output.Length = this.Length;
- output.Offset = 0;
- output.Position = 0;
-
- return output;
- }
-
- public void RemoveMessage(MessageReader reader)
- {
- var temp = MessageReader.GetSized(reader.Buffer.Length);
- try
- {
- var headerOffset = reader.Offset - 3;
- var endOfMessage = reader.Offset + reader.Length;
- var len = reader.Buffer.Length - endOfMessage;
-
- Array.Copy(reader.Buffer, endOfMessage, temp.Buffer, 0, len);
- Array.Copy(temp.Buffer, 0, this.Buffer, headerOffset, len);
-
- this.AdjustLength(reader.Offset, reader.Length + 3);
- }
- finally
- {
- temp.Recycle();
- }
- }
-
- public void InsertMessage(MessageReader reader, MessageWriter writer)
- {
- var temp = MessageReader.GetSized(reader.Buffer.Length);
- try
- {
- var headerOffset = reader.Offset - 3;
- var startOfMessage = reader.Offset;
- var len = reader.Buffer.Length - startOfMessage;
- int writerOffset = 0;
- //switch (writer.SendOption)
- //{
- // case SendOption.Reliable:
- // writerOffset = 3;
- // break;
- // case SendOption.None:
- // writerOffset = 1;
- // break;
- //}
-
- //store the original buffer in temp
- Array.Copy(reader.Buffer, headerOffset, temp.Buffer, 0, len);
-
- //put the contents of writer in at headerOffset
- Array.Copy(writer.Buffer, writerOffset, this.Buffer, headerOffset, writer.Length-writerOffset);
-
- //put the original buffer in after that
- Array.Copy(temp.Buffer, 0, this.Buffer, headerOffset + (writer.Length-writerOffset), len - writer.Length);
-
- this.AdjustLength(-1 * reader.Offset , -1 * (writer.Length - writerOffset));
- }
- finally
- {
- temp.Recycle();
- }
- }
-
- private void AdjustLength(int offset, int amount)
- {
- if (this.readHead > offset)
- {
- this.Position -= amount;
- }
-
- if (Parent != null)
- {
- var lengthOffset = this.Offset - 3;
- var curLen = this.Buffer[lengthOffset]
- | (this.Buffer[lengthOffset + 1] << 8);
-
- curLen -= amount;
- this.Length -= amount;
-
- this.Buffer[lengthOffset] = (byte)curLen;
- this.Buffer[lengthOffset + 1] = (byte)(this.Buffer[lengthOffset + 1] >> 8);
-
- Parent.AdjustLength(offset, amount);
- }
- }
-
- public void Recycle()
- {
- this.Parent = null;
- ReaderPool.PutObject(this);
- }
-
- #region Read Methods
- public bool ReadBoolean()
- {
- byte val = this.FastByte();
- return val != 0;
- }
-
- public sbyte ReadSByte()
- {
- return (sbyte)this.FastByte();
- }
-
- public byte ReadByte()
- {
- return this.FastByte();
- }
-
- public ushort ReadUInt16()
- {
- ushort output =
- (ushort)(this.FastByte()
- | this.FastByte() << 8);
- return output;
- }
-
- public short ReadInt16()
- {
- short output =
- (short)(this.FastByte()
- | this.FastByte() << 8);
- return output;
- }
-
- public uint ReadUInt32()
- {
- uint output = this.FastByte()
- | (uint)this.FastByte() << 8
- | (uint)this.FastByte() << 16
- | (uint)this.FastByte() << 24;
-
- return output;
- }
-
- public int ReadInt32()
- {
- int output = this.FastByte()
- | this.FastByte() << 8
- | this.FastByte() << 16
- | this.FastByte() << 24;
-
- return output;
- }
-
- public ulong ReadUInt64()
- {
- ulong output = (ulong)this.FastByte()
- | (ulong)this.FastByte() << 8
- | (ulong)this.FastByte() << 16
- | (ulong)this.FastByte() << 24
- | (ulong)this.FastByte() << 32
- | (ulong)this.FastByte() << 40
- | (ulong)this.FastByte() << 48
- | (ulong)this.FastByte() << 56;
-
- return output;
- }
-
- public long ReadInt64()
- {
- long output = (long)this.FastByte()
- | (long)this.FastByte() << 8
- | (long)this.FastByte() << 16
- | (long)this.FastByte() << 24
- | (long)this.FastByte() << 32
- | (long)this.FastByte() << 40
- | (long)this.FastByte() << 48
- | (long)this.FastByte() << 56;
-
- return output;
- }
-
- public unsafe float ReadSingle()
- {
- float output = 0;
- fixed (byte* bufPtr = &this.Buffer[this.readHead])
- {
- byte* outPtr = (byte*)&output;
-
- *outPtr = *bufPtr;
- *(outPtr + 1) = *(bufPtr + 1);
- *(outPtr + 2) = *(bufPtr + 2);
- *(outPtr + 3) = *(bufPtr + 3);
- }
-
- this.Position += 4;
- return output;
- }
-
- public string ReadString()
- {
- int len = this.ReadPackedInt32();
- if (this.BytesRemaining < len) throw new InvalidDataException($"Read length is longer than message length: {len} of {this.BytesRemaining}");
-
- string output = UTF8Encoding.UTF8.GetString(this.Buffer, this.readHead, len);
-
- this.Position += len;
- return output;
- }
-
- public byte[] ReadBytesAndSize()
- {
- int len = this.ReadPackedInt32();
- if (this.BytesRemaining < len) throw new InvalidDataException($"Read length is longer than message length: {len} of {this.BytesRemaining}");
-
- return this.ReadBytes(len);
- }
-
- public byte[] ReadBytes(int length)
- {
- if (this.BytesRemaining < length) throw new InvalidDataException($"Read length is longer than message length: {length} of {this.BytesRemaining}");
-
- byte[] output = new byte[length];
- Array.Copy(this.Buffer, this.readHead, output, 0, output.Length);
- this.Position += output.Length;
- return output;
- }
-
- ///
- public int ReadPackedInt32()
- {
- return (int)this.ReadPackedUInt32();
- }
-
- ///
- public uint ReadPackedUInt32()
- {
- bool readMore = true;
- int shift = 0;
- uint output = 0;
-
- while (readMore)
- {
- if (this.BytesRemaining < 1) throw new InvalidDataException($"Read length is longer than message length.");
-
- byte b = this.ReadByte();
- if (b >= 0x80)
- {
- readMore = true;
- b ^= 0x80;
- }
- else
- {
- readMore = false;
- }
-
- output |= (uint)(b << shift);
- shift += 7;
- }
-
- return output;
- }
- #endregion
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private byte FastByte()
- {
- this._position++;
- return this.Buffer[this.readHead++];
- }
-
- public unsafe static bool IsLittleEndian()
- {
- byte b;
- unsafe
- {
- int i = 1;
- byte* bp = (byte*)&i;
- b = *bp;
- }
-
- return b == 1;
- }
- }
-}
diff --git a/MultiplayerToolkit/Assets/MultiplayerToolkit/MessageReader.cs.meta b/MultiplayerToolkit/Assets/MultiplayerToolkit/MessageReader.cs.meta
deleted file mode 100644
index 2f470c6..0000000
--- a/MultiplayerToolkit/Assets/MultiplayerToolkit/MessageReader.cs.meta
+++ /dev/null
@@ -1,11 +0,0 @@
-fileFormatVersion: 2
-guid: 35714cf36eee0fc458f427f0a6cea532
-MonoImporter:
- externalObjects: {}
- serializedVersion: 2
- defaultReferences: []
- executionOrder: 0
- icon: {instanceID: 0}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/MultiplayerToolkit/Assets/MultiplayerToolkit/MessageWriter.cs b/MultiplayerToolkit/Assets/MultiplayerToolkit/MessageWriter.cs
deleted file mode 100644
index 226a2d4..0000000
--- a/MultiplayerToolkit/Assets/MultiplayerToolkit/MessageWriter.cs
+++ /dev/null
@@ -1,307 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Text;
-
-namespace MultiplayerToolkit
-{
-
- ///
- /// AmongUsùȥ˰ͷش
- /// ṹ
- /// -------------------------------------
- /// 2bytes(ushort) Эij
- /// Э
- /// -------------------------------------
- ///
- public class MessageWriter : IRecyclable
- {
- public static int BufferSize = 64000;
- public static readonly ObjectPool WriterPool = new ObjectPool(() => new MessageWriter(BufferSize));
-
- public byte[] Buffer;
- public int Length; // Эܳ
- public int Position; // дλ
-
- private Stack messageStarts = new Stack(); // ¼ЭbufferеʼλãǶṹ
-
- public MessageWriter(byte[] buffer)
- {
- this.Buffer = buffer;
- this.Length = this.Buffer.Length;
- }
-
- ///
- public MessageWriter(int bufferSize)
- {
- this.Buffer = new byte[bufferSize];
- }
-
- public byte[] ToByteArray()
- {
- byte[] output = new byte[this.Length];
- System.Buffer.BlockCopy(this.Buffer, 0, output, 0, this.Length);
- return output;
- }
-
- /// The option specifying how the message should be sent.
- public static MessageWriter Get()
- {
- MessageWriter output = WriterPool.GetObject();
- output.Clear();
-
- return output;
- }
-
- public bool HasBytes(int expected)
- {
- return this.Length >= 3 + expected;
- }
-
- ///
- public void StartMessage(/*byte typeFlag*/)
- {
- var messageStart = this.Position;
- messageStarts.Push(messageStart);
- this.Buffer[messageStart] = 0; // length
- this.Buffer[messageStart + 1] = 0; // length
- this.Position += 2; // ushort length
- //this.Write(typeFlag);
- }
-
- ///
- public void EndMessage()
- {
- var lastMessageStart = messageStarts.Pop();
- ushort length = (ushort)(this.Position - lastMessageStart - /*3*/2); // Minus length and type byte
- this.Buffer[lastMessageStart] = (byte)length;
- this.Buffer[lastMessageStart + 1] = (byte)(length >> 8);
- }
-
- ///
- /// ȡǰ༭messageصһ
- ///
- public void CancelMessage()
- {
- this.Position = this.messageStarts.Pop();
- this.Length = this.Position;
- }
-
- public void Clear()
- {
- Array.Clear(this.Buffer, 0, this.Buffer.Length);
- this.messageStarts.Clear();
- this.Length = 0;
- this.Position = 0;
- }
-
- ///
- /// message
- ///
- public void Recycle()
- {
- this.Position = this.Length = 0;
- WriterPool.PutObject(this);
- }
-
-#region WriteMethods
-
- public void CopyFrom(MessageReader target)
- {
- int offset, length;
- if (target.Tag == byte.MaxValue)
- {
- offset = target.Offset;
- length = target.Length;
- }
- else
- {
- offset = target.Offset - 3;
- length = target.Length + 3;
- }
-
- System.Buffer.BlockCopy(target.Buffer, offset, this.Buffer, this.Position, length);
- this.Position += length;
- if (this.Position > this.Length) this.Length = this.Position;
- }
-
- public void Write(bool value)
- {
- this.Buffer[this.Position++] = (byte)(value ? 1 : 0);
- if (this.Position > this.Length) this.Length = this.Position;
- }
-
- public void Write(sbyte value)
- {
- this.Buffer[this.Position++] = (byte)value;
- if (this.Position > this.Length) this.Length = this.Position;
- }
-
- public void Write(byte value)
- {
- this.Buffer[this.Position++] = value;
- if (this.Position > this.Length) this.Length = this.Position;
- }
-
- public void Write(short value)
- {
- this.Buffer[this.Position++] = (byte)value;
- this.Buffer[this.Position++] = (byte)(value >> 8);
- if (this.Position > this.Length) this.Length = this.Position;
- }
-
- public void Write(ushort value)
- {
- this.Buffer[this.Position++] = (byte)value;
- this.Buffer[this.Position++] = (byte)(value >> 8);
- if (this.Position > this.Length) this.Length = this.Position;
- }
-
- public void Write(uint value)
- {
- this.Buffer[this.Position++] = (byte)value;
- this.Buffer[this.Position++] = (byte)(value >> 8);
- this.Buffer[this.Position++] = (byte)(value >> 16);
- this.Buffer[this.Position++] = (byte)(value >> 24);
- if (this.Position > this.Length) this.Length = this.Position;
- }
-
- public void Write(int value)
- {
- this.Buffer[this.Position++] = (byte)value;
- this.Buffer[this.Position++] = (byte)(value >> 8);
- this.Buffer[this.Position++] = (byte)(value >> 16);
- this.Buffer[this.Position++] = (byte)(value >> 24);
- if (this.Position > this.Length) this.Length = this.Position;
- }
-
- public void Write(ulong value)
- {
- this.Buffer[this.Position++] = (byte)value;
- this.Buffer[this.Position++] = (byte)(value >> 8);
- this.Buffer[this.Position++] = (byte)(value >> 16);
- this.Buffer[this.Position++] = (byte)(value >> 24);
- this.Buffer[this.Position++] = (byte)(value >> 32);
- this.Buffer[this.Position++] = (byte)(value >> 40);
- this.Buffer[this.Position++] = (byte)(value >> 48);
- this.Buffer[this.Position++] = (byte)(value >> 56);
- if (this.Position > this.Length) this.Length = this.Position;
- }
-
- public void Write(long value)
- {
- this.Buffer[this.Position++] = (byte)value;
- this.Buffer[this.Position++] = (byte)(value >> 8);
- this.Buffer[this.Position++] = (byte)(value >> 16);
- this.Buffer[this.Position++] = (byte)(value >> 24);
- this.Buffer[this.Position++] = (byte)(value >> 32);
- this.Buffer[this.Position++] = (byte)(value >> 40);
- this.Buffer[this.Position++] = (byte)(value >> 48);
- this.Buffer[this.Position++] = (byte)(value >> 56);
- if (this.Position > this.Length) this.Length = this.Position;
- }
-
- public unsafe void Write(float value)
- {
- fixed (byte* ptr = &this.Buffer[this.Position])
- {
- byte* valuePtr = (byte*)&value;
-
- *ptr = *valuePtr;
- *(ptr + 1) = *(valuePtr + 1);
- *(ptr + 2) = *(valuePtr + 2);
- *(ptr + 3) = *(valuePtr + 3);
- }
-
- this.Position += 4;
- if (this.Position > this.Length) this.Length = this.Position;
- }
-
- public void Write(string value)
- {
- var bytes = UTF8Encoding.UTF8.GetBytes(value);
- this.WritePacked(bytes.Length);
- this.Write(bytes);
- }
-
- public void WriteBytesAndSize(byte[] bytes)
- {
- this.WritePacked((uint)bytes.Length);
- this.Write(bytes);
- }
-
- public void WriteBytesAndSize(byte[] bytes, int length)
- {
- this.WritePacked((uint)length);
- this.Write(bytes, length);
- }
-
- public void WriteBytesAndSize(byte[] bytes, int offset, int length)
- {
- this.WritePacked((uint)length);
- this.Write(bytes, offset, length);
- }
-
- public void Write(byte[] bytes)
- {
- Array.Copy(bytes, 0, this.Buffer, this.Position, bytes.Length);
- this.Position += bytes.Length;
- if (this.Position > this.Length) this.Length = this.Position;
- }
-
- public void Write(byte[] bytes, int offset, int length)
- {
- Array.Copy(bytes, offset, this.Buffer, this.Position, length);
- this.Position += length;
- if (this.Position > this.Length) this.Length = this.Position;
- }
-
- public void Write(byte[] bytes, int length)
- {
- Array.Copy(bytes, 0, this.Buffer, this.Position, length);
- this.Position += length;
- if (this.Position > this.Length) this.Length = this.Position;
- }
-
- ///
- public void WritePacked(int value)
- {
- this.WritePacked((uint)value);
- }
-
- ///
- public void WritePacked(uint value)
- {
- do
- {
- byte b = (byte)(value & 0xFF);
- if (value >= 0x80)
- {
- b |= 0x80;
- }
-
- this.Write(b);
- value >>= 7;
- } while (value > 0);
- }
-#endregion
-
- public void Write(MessageWriter msg)
- {
- this.Write(msg.Buffer, 0, msg.Length);
- }
-
- public unsafe static bool IsLittleEndian()
- {
- byte b;
- unsafe
- {
- int i = 1;
- byte* bp = (byte*)&i;
- b = *bp;
- }
-
- return b == 1;
- }
- }
-}
diff --git a/MultiplayerToolkit/Assets/MultiplayerToolkit/MessageWriter.cs.meta b/MultiplayerToolkit/Assets/MultiplayerToolkit/MessageWriter.cs.meta
deleted file mode 100644
index ec9023e..0000000
--- a/MultiplayerToolkit/Assets/MultiplayerToolkit/MessageWriter.cs.meta
+++ /dev/null
@@ -1,11 +0,0 @@
-fileFormatVersion: 2
-guid: 8d9ee77993c8eff4ba8b0a9026505b40
-MonoImporter:
- externalObjects: {}
- serializedVersion: 2
- defaultReferences: []
- executionOrder: 0
- icon: {instanceID: 0}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/MultiplayerToolkit/Assets/MultiplayerToolkit/ObjectPool.cs b/MultiplayerToolkit/Assets/MultiplayerToolkit/ObjectPool.cs
deleted file mode 100644
index e42cdd6..0000000
--- a/MultiplayerToolkit/Assets/MultiplayerToolkit/ObjectPool.cs
+++ /dev/null
@@ -1,108 +0,0 @@
-using System;
-using System.Collections.Concurrent;
-using System.Collections.Generic;
-using System.Threading;
-
-namespace MultiplayerToolkit
-{
- ///
- /// A fairly simple object pool for items that will be created a lot.
- ///
- /// The type that is pooled.
- ///
- public sealed class ObjectPool where T : IRecyclable
- {
- private int numberCreated;
- public int NumberCreated { get { return numberCreated; } }
-
- public int NumberInUse { get { return this.inuse.Count; } }
- public int NumberNotInUse { get { return this.pool.Count; } }
- public int Size { get { return this.NumberInUse + this.NumberNotInUse; } }
-
-#if HAZEL_BAG
- private readonly ConcurrentBag pool = new ConcurrentBag();
-#else
- private readonly List pool = new List();
-#endif
-
- // Unavailable objects
- private readonly ConcurrentDictionary inuse = new ConcurrentDictionary();
-
- ///
- /// The generator for creating new objects.
- ///
- ///
- private readonly Func objectFactory;
-
- ///
- /// Internal constructor for our ObjectPool.
- ///
- internal ObjectPool(Func objectFactory)
- {
- this.objectFactory = objectFactory;
- }
-
- ///
- /// Returns a pooled object of type T, if none are available another is created.
- ///
- /// An instance of T.
- internal T GetObject()
- {
-#if HAZEL_BAG
- if (!pool.TryTake(out T item))
- {
- Interlocked.Increment(ref numberCreated);
- item = objectFactory.Invoke();
- }
-#else
- T item;
- lock (this.pool)
- {
- if (this.pool.Count > 0)
- {
- var idx = this.pool.Count - 1;
- item = this.pool[idx];
- this.pool.RemoveAt(idx);
- }
- else
- {
- Interlocked.Increment(ref numberCreated);
- item = objectFactory.Invoke();
- }
- }
-#endif
-
- if (!inuse.TryAdd(item, true))
- {
- throw new Exception("Duplicate pull " + typeof(T).Name);
- }
-
- return item;
- }
-
- ///
- /// Returns an object to the pool.
- ///
- /// The item to return.
- internal void PutObject(T item)
- {
- if (inuse.TryRemove(item, out bool b))
- {
-#if HAZEL_BAG
- pool.Add(item);
-#else
- lock (this.pool)
- {
- pool.Add(item);
- }
-#endif
- }
- else
- {
-#if DEBUG
- throw new Exception("Duplicate add " + typeof(T).Name);
-#endif
- }
- }
- }
-}
diff --git a/MultiplayerToolkit/Assets/MultiplayerToolkit/Test/UnitTests.meta b/MultiplayerToolkit/Assets/MultiplayerToolkit/Test/UnitTests.meta
new file mode 100644
index 0000000..e1988f5
--- /dev/null
+++ b/MultiplayerToolkit/Assets/MultiplayerToolkit/Test/UnitTests.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 1bb97683c50825f4499202eb0d241254
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/MultiplayerToolkit/Assets/MultiplayerToolkit/Test/UnitTests/MessageUnitTests.cs b/MultiplayerToolkit/Assets/MultiplayerToolkit/Test/UnitTests/MessageUnitTests.cs
new file mode 100644
index 0000000..ca028cf
--- /dev/null
+++ b/MultiplayerToolkit/Assets/MultiplayerToolkit/Test/UnitTests/MessageUnitTests.cs
@@ -0,0 +1,11 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+
+
+public class MessageUnitTests
+{
+
+
+
+}
\ No newline at end of file
diff --git a/MultiplayerToolkit/Assets/MultiplayerToolkit/Test/UnitTests/MessageUnitTests.cs.meta b/MultiplayerToolkit/Assets/MultiplayerToolkit/Test/UnitTests/MessageUnitTests.cs.meta
new file mode 100644
index 0000000..a5ad157
--- /dev/null
+++ b/MultiplayerToolkit/Assets/MultiplayerToolkit/Test/UnitTests/MessageUnitTests.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 6c70f76fcb12ced489b49c7ac64eda2d
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Projects/Message/Message/.gitignore b/Projects/Message/Message/.gitignore
new file mode 100644
index 0000000..9bf6cfb
--- /dev/null
+++ b/Projects/Message/Message/.gitignore
@@ -0,0 +1,194 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+
+# User-specific files
+*.suo
+*.user
+*.sln.docstates
+.vs/
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+x64/
+build/
+bld/
+[Bb]in/
+[Oo]bj/
+
+#Sandcastle generated documentation
+[Hh]elp/
+
+# Roslyn cache directories
+*.ide/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+#NUNIT
+*.VisualState.xml
+TestResult.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+*_i.c
+*_p.c
+*_i.h
+*.ilk
+*.meta
+*.obj
+*.pch
+*.pdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opensdf
+*.sdf
+*.cachefile
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# JustCode is a .NET coding addin-in
+.JustCode
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+## TODO: Comment the next line if you want to checkin your
+## web deploy settings but do note that will include unencrypted
+## passwords
+#*.pubxml
+
+# NuGet Packages Directory
+packages/*
+## TODO: If the tool you use requires repositories.config
+## uncomment the next line
+#!packages/repositories.config
+
+# Enable "build/" folder in the NuGet Packages folder since
+# NuGet packages use it for MSBuild targets.
+# This line needs to be after the ignore of the build folder
+# (and the packages folder if the line above has been uncommented)
+!packages/build/
+
+# Windows Azure Build Output
+csx/
+*.build.csdef
+
+# Windows Store app package directory
+AppPackages/
+
+# Others
+sql/
+*.Cache
+ClientBin/
+[Ss]tyle[Cc]op.*
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.pfx
+*.publishsettings
+node_modules/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+
+# SQL Server files
+*.mdf
+*.ldf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# LightSwitch generated files
+GeneratedArtifacts/
+_Pvt_Extensions/
+ModelManifest.xml
+*.ide
diff --git a/Projects/Message/Message/IRecyclable.cs b/Projects/Message/Message/IRecyclable.cs
new file mode 100644
index 0000000..17d3104
--- /dev/null
+++ b/Projects/Message/Message/IRecyclable.cs
@@ -0,0 +1,29 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace MultiplayerToolkit
+{
+ ///
+ /// Interface for all items that can be returned to an object pool.
+ ///
+ ///
+ public interface IRecyclable
+ {
+ ///
+ /// Returns this object back to the object pool.
+ ///
+ ///
+ ///
+ /// Calling this when you are done with the object returns the object back to a pool in order to be reused.
+ /// This can reduce the amount of work the GC has to do dramatically but it is optional to call this.
+ ///
+ ///
+ /// Calling this indicates to Hazel that this can be reused and thus you should only call this when you are
+ /// completely finished with the object as the contents can be overwritten at any point after.
+ ///
+ ///
+ void Recycle();
+ }
+}
diff --git a/Projects/Message/Message/Message.csproj b/Projects/Message/Message/Message.csproj
new file mode 100644
index 0000000..f2f13d5
--- /dev/null
+++ b/Projects/Message/Message/Message.csproj
@@ -0,0 +1,74 @@
+
+
+
+
+
+ Debug
+ AnyCPU
+ {AADABFAD-53FB-43DA-BAC8-83D11CA36DEC}
+ Library
+ Properties
+ Message
+ Message
+ v4.7.2
+ 512
+ {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
+ 15.0
+ $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
+ $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages
+ False
+ UnitTest
+
+
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+ true
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+ true
+
+
+
+ packages\MSTest.TestFramework.2.2.7\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.dll
+
+
+ packages\MSTest.TestFramework.2.2.7\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 这台计算机上缺少此项目引用的 NuGet 程序包。使用“NuGet 程序包还原”可下载这些程序包。有关更多信息,请参见 http://go.microsoft.com/fwlink/?LinkID=322105。缺少的文件是 {0}。
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Projects/Message/Message/Message.lutconfig b/Projects/Message/Message/Message.lutconfig
new file mode 100644
index 0000000..596a860
--- /dev/null
+++ b/Projects/Message/Message/Message.lutconfig
@@ -0,0 +1,6 @@
+
+
+ true
+ true
+ 180000
+
\ No newline at end of file
diff --git a/Projects/Message/Message/Message.sln b/Projects/Message/Message/Message.sln
new file mode 100644
index 0000000..3381718
--- /dev/null
+++ b/Projects/Message/Message/Message.sln
@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.4.33213.308
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Message", "Message.csproj", "{AADABFAD-53FB-43DA-BAC8-83D11CA36DEC}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {AADABFAD-53FB-43DA-BAC8-83D11CA36DEC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {AADABFAD-53FB-43DA-BAC8-83D11CA36DEC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {AADABFAD-53FB-43DA-BAC8-83D11CA36DEC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {AADABFAD-53FB-43DA-BAC8-83D11CA36DEC}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {E601FAC4-CE34-4006-A518-979962273BFB}
+ EndGlobalSection
+EndGlobal
diff --git a/Projects/Message/Message/MessageReader.cs b/Projects/Message/Message/MessageReader.cs
new file mode 100644
index 0000000..7ec134b
--- /dev/null
+++ b/Projects/Message/Message/MessageReader.cs
@@ -0,0 +1,469 @@
+using System;
+using System.IO;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Text;
+
+namespace MultiplayerToolkit
+{
+ ///
+ ///
+ ///
+ public class MessageReader : IRecyclable
+ {
+ public static readonly ObjectPool ReaderPool = new ObjectPool(() => new MessageReader());
+
+ public byte[] Buffer; // √ 缓冲区,会被子协议共享这个缓冲区
+ public byte Tag; // √ 协议名
+
+ public int Length; // √ 协议长度,对应的是MessageWriter里写入的2bytes包长度(不含length和tag)
+ public int Offset; // √ length和tag后面的位置(在Buffer中的位置),不是游标,不会随读取发生改变。会影响readHead的值
+
+ //数据位于Buffer中Offset索引开始Length长度,协议位于Buffer中Offset-3开始的Length+3长度
+
+ public int BytesRemaining => this.Length - this.Position;
+
+ private MessageReader Parent; // 保存父协议索引,和父协议共享父协议的Buffer
+
+ public int Position
+ {
+ get { return this._position; }
+ set
+ {
+ this._position = value; // value是
+ this.readHead = value + Offset;
+ }
+ }
+
+ private int _position; // √ 读取游标,相对于协议内容部分的偏移,从0开始,子协议也是从0开始,不是在Buffer中的索引
+ private int readHead; // √ 读取游标,协议头部在Buffer中的位置,参考ReadMessage()
+
+ public static MessageReader GetSized(int minSize)
+ {
+ var output = ReaderPool.GetObject();
+
+ if (output.Buffer == null || output.Buffer.Length < minSize)
+ {
+ output.Buffer = new byte[minSize];
+ }
+ else
+ {
+ Array.Clear(output.Buffer, 0, output.Buffer.Length);
+ }
+
+ output.Offset = 0;
+ output.Position = 0;
+ output.Tag = byte.MaxValue;
+ return output;
+ }
+
+ public static MessageReader Get(byte[] buffer)
+ {
+ var output = ReaderPool.GetObject();
+
+ output.Buffer = buffer;
+ output.Offset = 0;
+ output.Position = 0;
+ output.Length = buffer.Length;
+ output.Tag = byte.MaxValue;
+
+ return output;
+ }
+
+ ///
+ /// 创建一个新的父message,保存source。用于缓存message,因为source所在的原父message可能会被销毁,如果需要延迟处理消息,需要拷贝一份新的
+ ///
+ ///
+ ///
+ public static MessageReader CopyMessageIntoParent(MessageReader source)
+ {
+ var output = MessageReader.GetSized(source.Length + 3); //3=2bytes(length) + 1byte(tag)
+ System.Buffer.BlockCopy(source.Buffer, source.Offset - 3, output.Buffer, 0, source.Length + 3);
+
+ output.Offset = 0;
+ output.Position = 0;
+ output.Length = source.Length + 3;
+
+ return output;
+ }
+
+ ///
+ /// 完全复制 一份
+ ///
+ ///
+ ///
+ public static MessageReader Get(MessageReader source)
+ {
+ var output = MessageReader.GetSized(source.Buffer.Length);
+ System.Buffer.BlockCopy(source.Buffer, 0, output.Buffer, 0, source.Buffer.Length);
+
+ output.Offset = source.Offset;
+
+ output._position = source._position;
+ output.readHead = source.readHead;
+
+ output.Length = source.Length;
+ output.Tag = source.Tag;
+
+ return output;
+ }
+
+ public static MessageReader Get(byte[] buffer, int offset)
+ {
+ // Ensure there is at least a header
+ if (offset + 3 > buffer.Length) return null;
+
+ var output = ReaderPool.GetObject();
+
+ output.Buffer = buffer;
+ output.Offset = offset;
+ output.Position = 0;
+
+ output.Length = output.ReadUInt16(); // 读到的值为length
+ output.Tag = output.ReadByte();
+
+ output.Offset += 3;
+ output.Position = 0;
+
+ return output;
+ }
+
+ ///
+ /// Produces a MessageReader using the parent's buffer. This MessageReader should **NOT** be recycled.
+ ///
+ public MessageReader ReadMessage()
+ {
+ // Ensure there is at least a header
+ if (this.BytesRemaining < 3) throw new InvalidDataException($"ReadMessage header is longer than message length: 3 of {this.BytesRemaining}");
+
+ MessageReader output = new MessageReader();
+
+ output.Parent = this;
+ output.Buffer = this.Buffer;
+ output.Offset = this.readHead; // 下面会读取
+ output.Position = 0;
+
+ // 读length和tag的值并保存在字段里
+ output.Length = output.ReadUInt16();
+ output.Tag = output.ReadByte();
+
+ // Offset, readHead齐步走,移到Length和Tag后面,_position=0
+ output.Offset += 3; // 3=length+tag
+ output.Position = 0;
+
+ if (this.BytesRemaining < output.Length + 3) throw new InvalidDataException($"Message Length at Position {this.readHead} is longer than message length: {output.Length + 3} of {this.BytesRemaining}");
+
+ this.Position += output.Length + 3; //跳过整个子协议
+ return output;
+ }
+
+ ///
+ /// Produces a MessageReader with a new buffer. This MessageReader should be recycled.
+ ///
+ public MessageReader ReadMessageAsNewBuffer()
+ {
+ if (this.BytesRemaining < 3) throw new InvalidDataException($"ReadMessage header is longer than message length: 3 of {this.BytesRemaining}");
+
+ var len = this.ReadUInt16();
+ var tag = this.ReadByte();
+
+ if (this.BytesRemaining < len) throw new InvalidDataException($"Message Length at Position {this.readHead} is longer than message length: {len} of {this.BytesRemaining}");
+
+ var output = MessageReader.GetSized(len);
+
+ Array.Copy(this.Buffer, this.readHead, output.Buffer, 0, len);
+
+ output.Length = len;
+ output.Tag = tag;
+
+ this.Position += output.Length;
+ return output;
+ }
+
+ public MessageWriter StartWriter()
+ {
+ var output = new MessageWriter(this.Buffer);
+ output.Position = this.readHead;
+ return output;
+ }
+
+ public MessageReader Duplicate()
+ {
+ var output = GetSized(this.Length);
+ Array.Copy(this.Buffer, this.Offset, output.Buffer, 0, this.Length);
+ output.Length = this.Length;
+ output.Offset = 0;
+ output.Position = 0;
+
+ return output;
+ }
+
+ public void RemoveMessage(MessageReader reader)
+ {
+ var temp = MessageReader.GetSized(reader.Buffer.Length);
+ try
+ {
+ var headerOffset = reader.Offset - 3;
+ var endOfMessage = reader.Offset + reader.Length;
+ var len = reader.Buffer.Length - endOfMessage;
+
+ Array.Copy(reader.Buffer, endOfMessage, temp.Buffer, 0, len);
+ Array.Copy(temp.Buffer, 0, this.Buffer, headerOffset, len);
+
+ this.AdjustLength(reader.Offset, reader.Length + 3);
+ }
+ finally
+ {
+ temp.Recycle();
+ }
+ }
+
+ //public void InsertMessage(MessageReader reader, MessageWriter writer)
+ //{
+ // var temp = MessageReader.GetSized(reader.Buffer.Length);
+ // try
+ // {
+ // var headerOffset = reader.Offset - 3;
+ // var startOfMessage = reader.Offset;
+ // var len = reader.Buffer.Length - startOfMessage;
+ // int writerOffset = 3;
+ // switch (writer.SendOption)
+ // {
+ // case SendOption.Reliable:
+ // writerOffset = 3;
+ // break;
+ // case SendOption.None:
+ // writerOffset = 1;
+ // break;
+ // }
+
+ // //store the original buffer in temp
+ // Array.Copy(reader.Buffer, headerOffset, temp.Buffer, 0, len);
+
+ // //put the contents of writer in at headerOffset
+ // Array.Copy(writer.Buffer, writerOffset, this.Buffer, headerOffset, writer.Length - writerOffset);
+
+ // //put the original buffer in after that
+ // Array.Copy(temp.Buffer, 0, this.Buffer, headerOffset + (writer.Length - writerOffset), len - writer.Length);
+
+ // this.AdjustLength(-1 * reader.Offset, -1 * (writer.Length - writerOffset));
+ // }
+ // finally
+ // {
+ // temp.Recycle();
+ // }
+ //}
+
+ private void AdjustLength(int offset, int amount)
+ {
+ if (this.readHead > offset)
+ {
+ this.Position -= amount;
+ }
+
+ if (Parent != null)
+ {
+ var lengthOffset = this.Offset - 3;
+ var curLen = this.Buffer[lengthOffset]
+ | (this.Buffer[lengthOffset + 1] << 8);
+
+ curLen -= amount;
+ this.Length -= amount;
+
+ this.Buffer[lengthOffset] = (byte)curLen;
+ this.Buffer[lengthOffset + 1] = (byte)(this.Buffer[lengthOffset + 1] >> 8);
+
+ Parent.AdjustLength(offset, amount);
+ }
+ }
+
+ public void Recycle()
+ {
+ this.Parent = null;
+ ReaderPool.PutObject(this);
+ }
+
+ #region Read Methods
+ public bool ReadBoolean()
+ {
+ byte val = this.FastByte();
+ return val != 0;
+ }
+
+ public sbyte ReadSByte()
+ {
+ return (sbyte)this.FastByte();
+ }
+
+ public byte ReadByte()
+ {
+ return this.FastByte();
+ }
+
+ public ushort ReadUInt16()
+ {
+ ushort output =
+ (ushort)(this.FastByte()
+ | this.FastByte() << 8);
+ return output;
+ }
+
+ public short ReadInt16()
+ {
+ short output =
+ (short)(this.FastByte()
+ | this.FastByte() << 8);
+ return output;
+ }
+
+ public uint ReadUInt32()
+ {
+ uint output = this.FastByte()
+ | (uint)this.FastByte() << 8
+ | (uint)this.FastByte() << 16
+ | (uint)this.FastByte() << 24;
+
+ return output;
+ }
+
+ public int ReadInt32()
+ {
+ int output = this.FastByte()
+ | this.FastByte() << 8
+ | this.FastByte() << 16
+ | this.FastByte() << 24;
+
+ return output;
+ }
+
+ public ulong ReadUInt64()
+ {
+ ulong output = (ulong)this.FastByte()
+ | (ulong)this.FastByte() << 8
+ | (ulong)this.FastByte() << 16
+ | (ulong)this.FastByte() << 24
+ | (ulong)this.FastByte() << 32
+ | (ulong)this.FastByte() << 40
+ | (ulong)this.FastByte() << 48
+ | (ulong)this.FastByte() << 56;
+
+ return output;
+ }
+
+ public long ReadInt64()
+ {
+ long output = (long)this.FastByte()
+ | (long)this.FastByte() << 8
+ | (long)this.FastByte() << 16
+ | (long)this.FastByte() << 24
+ | (long)this.FastByte() << 32
+ | (long)this.FastByte() << 40
+ | (long)this.FastByte() << 48
+ | (long)this.FastByte() << 56;
+
+ return output;
+ }
+
+ public unsafe float ReadSingle()
+ {
+ float output = 0;
+ fixed (byte* bufPtr = &this.Buffer[this.readHead])
+ {
+ byte* outPtr = (byte*)&output;
+
+ *outPtr = *bufPtr;
+ *(outPtr + 1) = *(bufPtr + 1);
+ *(outPtr + 2) = *(bufPtr + 2);
+ *(outPtr + 3) = *(bufPtr + 3);
+ }
+
+ this.Position += 4;
+ return output;
+ }
+
+ public string ReadString()
+ {
+ int len = this.ReadPackedInt32();
+ if (this.BytesRemaining < len) throw new InvalidDataException($"Read length is longer than message length: {len} of {this.BytesRemaining}");
+
+ string output = UTF8Encoding.UTF8.GetString(this.Buffer, this.readHead, len);
+
+ this.Position += len;
+ return output;
+ }
+
+ public byte[] ReadBytesAndSize()
+ {
+ int len = this.ReadPackedInt32();
+ if (this.BytesRemaining < len) throw new InvalidDataException($"Read length is longer than message length: {len} of {this.BytesRemaining}");
+
+ return this.ReadBytes(len);
+ }
+
+ public byte[] ReadBytes(int length)
+ {
+ if (this.BytesRemaining < length) throw new InvalidDataException($"Read length is longer than message length: {length} of {this.BytesRemaining}");
+
+ byte[] output = new byte[length];
+ Array.Copy(this.Buffer, this.readHead, output, 0, output.Length);
+ this.Position += output.Length;
+ return output;
+ }
+
+ ///
+ public int ReadPackedInt32()
+ {
+ return (int)this.ReadPackedUInt32();
+ }
+
+ ///
+ public uint ReadPackedUInt32()
+ {
+ bool readMore = true;
+ int shift = 0;
+ uint output = 0;
+
+ while (readMore)
+ {
+ if (this.BytesRemaining < 1) throw new InvalidDataException($"Read length is longer than message length.");
+
+ byte b = this.ReadByte();
+ if (b >= 0x80)
+ {
+ readMore = true;
+ b ^= 0x80;
+ }
+ else
+ {
+ readMore = false;
+ }
+
+ output |= (uint)(b << shift);
+ shift += 7;
+ }
+
+ return output;
+ }
+ #endregion
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private byte FastByte()
+ {
+ this._position++;
+ return this.Buffer[this.readHead++];
+ }
+
+ public unsafe static bool IsLittleEndian()
+ {
+ byte b;
+ unsafe
+ {
+ int i = 1;
+ byte* bp = (byte*)&i;
+ b = *bp;
+ }
+
+ return b == 1;
+ }
+ }
+}
diff --git a/Projects/Message/Message/MessageWriter.cs b/Projects/Message/Message/MessageWriter.cs
new file mode 100644
index 0000000..7646135
--- /dev/null
+++ b/Projects/Message/Message/MessageWriter.cs
@@ -0,0 +1,318 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+
+namespace MultiplayerToolkit
+{
+
+ ///
+ /// AmongUsùȥ˰ͷش
+ /// ṹ
+ /// -------------------------------------
+ /// 2bytes (ushort)Эijȣ2bytes
+ /// -------------------------------------
+ /// 1byte ЭͣЭ0ԶЭ1
+ /// (2byte) (ԶЭ飬ṩһmodţSteam.UGC.Itemidulong 8bytesеţ֧65535mod)
+ /// 2byte (ushort)ЭID 0-65535
+ ///
+ /// -------------------------------------
+ ///
+ public class MessageWriter : IRecyclable
+ {
+ public const int BYTE_COUNT_OF_LENGTH = 2; // ռ2 bytes
+ public static int BufferSize = 64000; // Э֧64000ֽڣ62.5KB
+ public static readonly ObjectPool WriterPool = new ObjectPool(() => new MessageWriter(BufferSize));
+
+ public byte[] Buffer;
+ public int Length; // ЧbufferеijȣܰǶЭ
+ public int Position; // дλ
+
+ // lengthһڻĴСLengthһÿЭЭݲֵֽ
+
+ private Stack messageStarts = new Stack(); // ¼ЭbufferеʼλãǶṹ
+
+ public MessageWriter(byte[] buffer)
+ {
+ this.Buffer = buffer;
+ this.Length = this.Buffer.Length;
+ }
+
+ ///
+ public MessageWriter(int bufferSize)
+ {
+ this.Buffer = new byte[bufferSize];
+ }
+
+ public byte[] ToByteArray()
+ {
+ byte[] output = new byte[this.Length];
+ System.Buffer.BlockCopy(this.Buffer, 0, output, 0, this.Length);
+ return output;
+ }
+
+ /// The option specifying how the message should be sent.
+ public static MessageWriter Get()
+ {
+ MessageWriter output = WriterPool.GetObject();
+ output.Clear();
+
+ return output;
+ }
+
+ ///
+ /// Ƿexpectedȵ
+ ///
+ ///
+ ///
+ public bool HasBytes(int expected)
+ {
+ return this.Length >= expected;
+ }
+
+ ///
+ public void StartMessage()
+ {
+ var messageStart = this.Position;
+ messageStarts.Push(messageStart);
+ this.Buffer[messageStart] = 0; // length
+ this.Buffer[messageStart + 1] = 0; // length
+ this.Position += 2; // ushort length
+ }
+
+ ///
+ public void EndMessage()
+ {
+ var lastMessageStart = messageStarts.Pop();
+ ushort length = (ushort)(this.Position - lastMessageStart - 2); // Minus length
+ this.Buffer[lastMessageStart] = (byte)length;
+ this.Buffer[lastMessageStart + 1] = (byte)(length >> 8);
+ }
+
+ ///
+ /// ȡǰ༭messageصһ
+ ///
+ public void CancelMessage()
+ {
+ this.Position = this.messageStarts.Pop();
+ this.Length = this.Position;
+ }
+
+ public void Clear()
+ {
+ Array.Clear(this.Buffer, 0, this.Buffer.Length);
+ this.messageStarts.Clear();
+ this.Length = 0;
+ this.Position = 0;
+ }
+
+ ///
+ /// message
+ ///
+ public void Recycle()
+ {
+ this.Position = this.Length = 0;
+ WriterPool.PutObject(this);
+ }
+
+#region WriteMethods
+
+ public void CopyFrom(MessageReader target)
+ {
+ int offset, length;
+ if (target.Tag == byte.MaxValue)
+ {
+ offset = target.Offset;
+ length = target.Length;
+ }
+ else
+ {
+ offset = target.Offset - 3;
+ length = target.Length + 3;
+ }
+
+ System.Buffer.BlockCopy(target.Buffer, offset, this.Buffer, this.Position, length);
+ this.Position += length;
+ if (this.Position > this.Length) this.Length = this.Position;
+ }
+
+ public void Write(bool value)
+ {
+ this.Buffer[this.Position++] = (byte)(value ? 1 : 0);
+ if (this.Position > this.Length) this.Length = this.Position;
+ }
+
+ public void Write(sbyte value)
+ {
+ this.Buffer[this.Position++] = (byte)value;
+ if (this.Position > this.Length) this.Length = this.Position;
+ }
+
+ public void Write(byte value)
+ {
+ this.Buffer[this.Position++] = value;
+ if (this.Position > this.Length) this.Length = this.Position;
+ }
+
+ public void Write(short value)
+ {
+ this.Buffer[this.Position++] = (byte)value;
+ this.Buffer[this.Position++] = (byte)(value >> 8);
+ if (this.Position > this.Length) this.Length = this.Position;
+ }
+
+ public void Write(ushort value)
+ {
+ this.Buffer[this.Position++] = (byte)value;
+ this.Buffer[this.Position++] = (byte)(value >> 8);
+ if (this.Position > this.Length) this.Length = this.Position;
+ }
+
+ public void Write(uint value)
+ {
+ this.Buffer[this.Position++] = (byte)value;
+ this.Buffer[this.Position++] = (byte)(value >> 8);
+ this.Buffer[this.Position++] = (byte)(value >> 16);
+ this.Buffer[this.Position++] = (byte)(value >> 24);
+ if (this.Position > this.Length) this.Length = this.Position;
+ }
+
+ public void Write(int value)
+ {
+ this.Buffer[this.Position++] = (byte)value;
+ this.Buffer[this.Position++] = (byte)(value >> 8);
+ this.Buffer[this.Position++] = (byte)(value >> 16);
+ this.Buffer[this.Position++] = (byte)(value >> 24);
+ if (this.Position > this.Length) this.Length = this.Position;
+ }
+
+ public void Write(ulong value)
+ {
+ this.Buffer[this.Position++] = (byte)value;
+ this.Buffer[this.Position++] = (byte)(value >> 8);
+ this.Buffer[this.Position++] = (byte)(value >> 16);
+ this.Buffer[this.Position++] = (byte)(value >> 24);
+ this.Buffer[this.Position++] = (byte)(value >> 32);
+ this.Buffer[this.Position++] = (byte)(value >> 40);
+ this.Buffer[this.Position++] = (byte)(value >> 48);
+ this.Buffer[this.Position++] = (byte)(value >> 56);
+ if (this.Position > this.Length) this.Length = this.Position;
+ }
+
+ public void Write(long value)
+ {
+ this.Buffer[this.Position++] = (byte)value;
+ this.Buffer[this.Position++] = (byte)(value >> 8);
+ this.Buffer[this.Position++] = (byte)(value >> 16);
+ this.Buffer[this.Position++] = (byte)(value >> 24);
+ this.Buffer[this.Position++] = (byte)(value >> 32);
+ this.Buffer[this.Position++] = (byte)(value >> 40);
+ this.Buffer[this.Position++] = (byte)(value >> 48);
+ this.Buffer[this.Position++] = (byte)(value >> 56);
+ if (this.Position > this.Length) this.Length = this.Position;
+ }
+
+ public unsafe void Write(float value)
+ {
+ fixed (byte* ptr = &this.Buffer[this.Position])
+ {
+ byte* valuePtr = (byte*)&value;
+
+ *ptr = *valuePtr;
+ *(ptr + 1) = *(valuePtr + 1);
+ *(ptr + 2) = *(valuePtr + 2);
+ *(ptr + 3) = *(valuePtr + 3);
+ }
+
+ this.Position += 4;
+ if (this.Position > this.Length) this.Length = this.Position;
+ }
+
+ public void Write(string value)
+ {
+ var bytes = UTF8Encoding.UTF8.GetBytes(value);
+ this.WritePacked(bytes.Length);
+ this.Write(bytes);
+ }
+
+ public void WriteBytesAndSize(byte[] bytes)
+ {
+ this.WritePacked((uint)bytes.Length);
+ this.Write(bytes);
+ }
+
+ public void WriteBytesAndSize(byte[] bytes, int length)
+ {
+ this.WritePacked((uint)length);
+ this.Write(bytes, length);
+ }
+
+ public void WriteBytesAndSize(byte[] bytes, int offset, int length)
+ {
+ this.WritePacked((uint)length);
+ this.Write(bytes, offset, length);
+ }
+
+ public void Write(byte[] bytes)
+ {
+ Array.Copy(bytes, 0, this.Buffer, this.Position, bytes.Length);
+ this.Position += bytes.Length;
+ if (this.Position > this.Length) this.Length = this.Position;
+ }
+
+ public void Write(byte[] bytes, int offset, int length)
+ {
+ Array.Copy(bytes, offset, this.Buffer, this.Position, length);
+ this.Position += length;
+ if (this.Position > this.Length) this.Length = this.Position;
+ }
+
+ public void Write(byte[] bytes, int length)
+ {
+ Array.Copy(bytes, 0, this.Buffer, this.Position, length);
+ this.Position += length;
+ if (this.Position > this.Length) this.Length = this.Position;
+ }
+
+ ///
+ public void WritePacked(int value)
+ {
+ this.WritePacked((uint)value);
+ }
+
+ ///
+ public void WritePacked(uint value)
+ {
+ do
+ {
+ byte b = (byte)(value & 0xFF);
+ if (value >= 0x80)
+ {
+ b |= 0x80;
+ }
+
+ this.Write(b);
+ value >>= 7;
+ } while (value > 0);
+ }
+#endregion
+
+ public void Write(MessageWriter msg)
+ {
+ this.Write(msg.Buffer, 0, msg.Length);
+ }
+
+ public unsafe static bool IsLittleEndian()
+ {
+ byte b;
+ unsafe
+ {
+ int i = 1;
+ byte* bp = (byte*)&i;
+ b = *bp;
+ }
+
+ return b == 1;
+ }
+ }
+}
diff --git a/Projects/Message/Message/ObjectPool.cs b/Projects/Message/Message/ObjectPool.cs
new file mode 100644
index 0000000..e42cdd6
--- /dev/null
+++ b/Projects/Message/Message/ObjectPool.cs
@@ -0,0 +1,108 @@
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Threading;
+
+namespace MultiplayerToolkit
+{
+ ///
+ /// A fairly simple object pool for items that will be created a lot.
+ ///
+ /// The type that is pooled.
+ ///
+ public sealed class ObjectPool where T : IRecyclable
+ {
+ private int numberCreated;
+ public int NumberCreated { get { return numberCreated; } }
+
+ public int NumberInUse { get { return this.inuse.Count; } }
+ public int NumberNotInUse { get { return this.pool.Count; } }
+ public int Size { get { return this.NumberInUse + this.NumberNotInUse; } }
+
+#if HAZEL_BAG
+ private readonly ConcurrentBag pool = new ConcurrentBag();
+#else
+ private readonly List pool = new List();
+#endif
+
+ // Unavailable objects
+ private readonly ConcurrentDictionary inuse = new ConcurrentDictionary();
+
+ ///
+ /// The generator for creating new objects.
+ ///
+ ///
+ private readonly Func objectFactory;
+
+ ///
+ /// Internal constructor for our ObjectPool.
+ ///
+ internal ObjectPool(Func objectFactory)
+ {
+ this.objectFactory = objectFactory;
+ }
+
+ ///
+ /// Returns a pooled object of type T, if none are available another is created.
+ ///
+ /// An instance of T.
+ internal T GetObject()
+ {
+#if HAZEL_BAG
+ if (!pool.TryTake(out T item))
+ {
+ Interlocked.Increment(ref numberCreated);
+ item = objectFactory.Invoke();
+ }
+#else
+ T item;
+ lock (this.pool)
+ {
+ if (this.pool.Count > 0)
+ {
+ var idx = this.pool.Count - 1;
+ item = this.pool[idx];
+ this.pool.RemoveAt(idx);
+ }
+ else
+ {
+ Interlocked.Increment(ref numberCreated);
+ item = objectFactory.Invoke();
+ }
+ }
+#endif
+
+ if (!inuse.TryAdd(item, true))
+ {
+ throw new Exception("Duplicate pull " + typeof(T).Name);
+ }
+
+ return item;
+ }
+
+ ///
+ /// Returns an object to the pool.
+ ///
+ /// The item to return.
+ internal void PutObject(T item)
+ {
+ if (inuse.TryRemove(item, out bool b))
+ {
+#if HAZEL_BAG
+ pool.Add(item);
+#else
+ lock (this.pool)
+ {
+ pool.Add(item);
+ }
+#endif
+ }
+ else
+ {
+#if DEBUG
+ throw new Exception("Duplicate add " + typeof(T).Name);
+#endif
+ }
+ }
+ }
+}
diff --git a/Projects/Message/Message/Properties/AssemblyInfo.cs b/Projects/Message/Message/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..9f03f92
--- /dev/null
+++ b/Projects/Message/Message/Properties/AssemblyInfo.cs
@@ -0,0 +1,20 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+[assembly: AssemblyTitle("Message")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("Message")]
+[assembly: AssemblyCopyright("Copyright © 2023")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+[assembly: ComVisible(false)]
+
+[assembly: Guid("aadabfad-53fb-43da-bac8-83d11ca36dec")]
+
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/Projects/Message/Message/UnitTest1.cs b/Projects/Message/Message/UnitTest1.cs
new file mode 100644
index 0000000..2e02c6e
--- /dev/null
+++ b/Projects/Message/Message/UnitTest1.cs
@@ -0,0 +1,16 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System;
+
+namespace Message
+{
+ [TestClass]
+ public class UnitTest1
+ {
+ [TestMethod]
+ public void TestMethod1()
+ {
+
+ Assert.AreEqual(1, 1);
+ }
+ }
+}
diff --git a/Projects/Message/Message/packages.config b/Projects/Message/Message/packages.config
new file mode 100644
index 0000000..54eb31a
--- /dev/null
+++ b/Projects/Message/Message/packages.config
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/Tools/Hazel-Networking/Hazel.UnitTests/MessageReaderTests.cs b/Tools/Hazel-Networking/Hazel.UnitTests/MessageReaderTests.cs
index 6cf4ba8..7a073ae 100644
--- a/Tools/Hazel-Networking/Hazel.UnitTests/MessageReaderTests.cs
+++ b/Tools/Hazel-Networking/Hazel.UnitTests/MessageReaderTests.cs
@@ -125,7 +125,7 @@ namespace Hazel.UnitTests
reader.Length = msg.Length;
var zero = reader.ReadMessage();
-
+
var one = reader.ReadMessage();
var two = one.ReadMessage();
var three = two.ReadMessage();
@@ -218,6 +218,27 @@ namespace Hazel.UnitTests
const byte Test5 = 55;
const byte TestInsert = 66;
+/*
+2 length=1
+1 tag=0
+1 Test0=11
+
+2 length=11
+1 tag=12
+ 2 length=8
+ 1 tag=23
+ 2 length=1
+ 1 tag=34
+ 1 Test3=33
+
+ 2 length=1
+ 1 tag=45
+ 1 Test4=44
+2 length=1
+1 tag=56
+1 Test5=55
+*/
+
var msg = new MessageWriter(2048);
msg.StartMessage(0);
msg.Write(Test0);
@@ -247,6 +268,12 @@ namespace Hazel.UnitTests
writer.StartMessage(5);
writer.Write(TestInsert);
writer.EndMessage();
+/*
+1 SendOption.None
+2 length=2
+1 tag=5
+1 TestInsert=66
+ */
reader.ReadMessage();
var one = reader.ReadMessage();
diff --git a/Tools/Hazel-Networking/Hazel.lutconfig b/Tools/Hazel-Networking/Hazel.lutconfig
new file mode 100644
index 0000000..87625ea
--- /dev/null
+++ b/Tools/Hazel-Networking/Hazel.lutconfig
@@ -0,0 +1,6 @@
+
+ ..\..\
+ true
+ true
+ 180000
+
\ No newline at end of file
diff --git a/Tools/Hazel-Networking/Hazel/MessageReader.cs b/Tools/Hazel-Networking/Hazel/MessageReader.cs
index bd3b0d8..b6e35de 100644
--- a/Tools/Hazel-Networking/Hazel/MessageReader.cs
+++ b/Tools/Hazel-Networking/Hazel/MessageReader.cs
@@ -1,4 +1,5 @@
-using System;
+#define UNIT_TEST
+using System;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
@@ -6,32 +7,37 @@ using System.Text;
namespace Hazel
{
+ ///
+ ///
+ ///
public class MessageReader : IRecyclable
{
public static readonly ObjectPool ReaderPool = new ObjectPool(() => new MessageReader());
- public byte[] Buffer;
- public byte Tag;
+ public byte[] Buffer; // √ 缓冲区,会被子协议共享这个缓冲区
+ public byte Tag; // √ 协议名
- public int Length; // 总长度
- public int Offset; // length和tag后面
+ public int Length; // √ 协议长度,对应的是MessageWriter里写入的2bytes包长度(不含length和tag)
+ public int Offset; // √ length和tag后面的位置(在Buffer中的位置),不是游标,不会随读取发生改变。会影响readHead的值
+
+ //数据位于Buffer中Offset索引开始Length长度,协议位于Buffer中Offset-3开始的Length+3长度
public int BytesRemaining => this.Length - this.Position;
- private MessageReader Parent;
+ private MessageReader Parent; // 保存父协议索引,和父协议共享父协议的Buffer
public int Position
{
get { return this._position; }
set
{
- this._position = value;
+ this._position = value; // value是
this.readHead = value + Offset;
}
}
- private int _position;
- private int readHead;
+ private int _position; // √ 读取游标,相对于协议内容部分的偏移,从0开始,子协议也是从0开始,不是在Buffer中的索引
+ private int readHead; // √ 读取游标,协议头部在Buffer中的位置,参考ReadMessage()
public static MessageReader GetSized(int minSize)
{
@@ -65,10 +71,15 @@ namespace Hazel
return output;
}
+ ///
+ /// 创建一个新的message,保存source。用于缓存message,因为source所在的原父message可能会被销毁,如果需要延迟处理消息,需要拷贝一份新的
+ ///
+ ///
+ ///
public static MessageReader CopyMessageIntoParent(MessageReader source)
{
- var output = MessageReader.GetSized(source.Length + 3);
- System.Buffer.BlockCopy(source.Buffer, source.Offset - 3, output.Buffer, 0, source.Length + 3);
+ var output = MessageReader.GetSized(source.Length + 3); //3=2bytes(length) + 1byte(tag)
+ System.Buffer.BlockCopy(source.Buffer, source.Offset - 3, output.Buffer, 0, source.Length + 3);
output.Offset = 0;
output.Position = 0;
@@ -77,6 +88,11 @@ namespace Hazel
return output;
}
+ ///
+ /// 完全复制 一份
+ ///
+ ///
+ ///
public static MessageReader Get(MessageReader source)
{
var output = MessageReader.GetSized(source.Buffer.Length);
@@ -96,7 +112,7 @@ namespace Hazel
public static MessageReader Get(byte[] buffer, int offset)
{
// Ensure there is at least a header
- if (offset + 3 > buffer.Length) return null;
+ if (offset + 3 > buffer.Length) return null; //3=length+tag
var output = ReaderPool.GetObject();
@@ -104,7 +120,7 @@ namespace Hazel
output.Offset = offset;
output.Position = 0;
- output.Length = output.ReadUInt16();
+ output.Length = output.ReadUInt16(); // 读到的值为length
output.Tag = output.ReadByte();
output.Offset += 3;
@@ -121,22 +137,24 @@ namespace Hazel
// Ensure there is at least a header
if (this.BytesRemaining < 3) throw new InvalidDataException($"ReadMessage header is longer than message length: 3 of {this.BytesRemaining}");
- var output = new MessageReader();
+ MessageReader output = new MessageReader();
output.Parent = this;
output.Buffer = this.Buffer;
- output.Offset = this.readHead;
+ output.Offset = this.readHead; // 下面会读取
output.Position = 0;
+ // 读length和tag的值并保存在字段里
output.Length = output.ReadUInt16();
output.Tag = output.ReadByte();
- output.Offset += 3;
+ // Offset, readHead齐步走,移到Length和Tag后面,_position=0
+ output.Offset += 3; // 3=length+tag
output.Position = 0;
if (this.BytesRemaining < output.Length + 3) throw new InvalidDataException($"Message Length at Position {this.readHead} is longer than message length: {output.Length + 3} of {this.BytesRemaining}");
- this.Position += output.Length + 3;
+ this.Position += output.Length + 3; //跳过整个子协议
return output;
}
@@ -201,22 +219,29 @@ namespace Hazel
}
}
+
+#if UNIT_TEST // 我自己加的,作为备注
+ ///
+ /// 仅用于单元测试的方法
+ ///
+ /// 父reader
+ ///
public void InsertMessage(MessageReader reader, MessageWriter writer)
{
var temp = MessageReader.GetSized(reader.Buffer.Length);
try
{
- var headerOffset = reader.Offset - 3;
- var startOfMessage = reader.Offset;
- var len = reader.Buffer.Length - startOfMessage;
- int writerOffset = 3;
+ var headerOffset = reader.Offset - 3; // headerOffset是length+tag,这个方法仅仅接受reader不含sendoption的情况
+ var startOfMessage = reader.Offset; // 头部后面的数据开始的索引
+ var len = reader.Buffer.Length - startOfMessage; // 疑似写错了,应该是headerOffset
+ int writerOffset = 3;// √ 跳过header
switch (writer.SendOption)
{
case SendOption.Reliable:
writerOffset = 3;
break;
case SendOption.None:
- writerOffset = 1;
+ writerOffset = 1;
break;
}
@@ -236,6 +261,7 @@ namespace Hazel
temp.Recycle();
}
}
+#endif
private void AdjustLength(int offset, int amount)
{
@@ -266,7 +292,7 @@ namespace Hazel
ReaderPool.PutObject(this);
}
- #region Read Methods
+#region Read Methods
public bool ReadBoolean()
{
byte val = this.FastByte();
@@ -427,7 +453,7 @@ namespace Hazel
return output;
}
- #endregion
+#endregion
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private byte FastByte()
diff --git a/Tools/Hazel-Networking/Hazel/MessageWriter.cs b/Tools/Hazel-Networking/Hazel/MessageWriter.cs
index cbae11c..68280cd 100644
--- a/Tools/Hazel-Networking/Hazel/MessageWriter.cs
+++ b/Tools/Hazel-Networking/Hazel/MessageWriter.cs
@@ -6,12 +6,15 @@ using System.Text;
namespace Hazel
{
///
- /// 嵌套结构的Message
+ /// 单向写入的Message,嵌套结构的Message,如果有嵌套,也只有一个header,子协议没有header
+ /// 整个结构都会被socket发送,参见UdpConnection.cs
/// 结构:
- /// ------------------------------------
- /// 2bytes (ushort) 包长度
- /// 1bytes (tag) 协议ID,在AmongUS里是tags.cs里定义的tag和subtag
- /// ------------------------------------
+ /// -------------header-----------------
+ /// 1bytes SendOption 是否是可靠传输
+ /// (2bytes) (可靠传输用到的)
+ /// --------------数据------------------
+ /// 2bytes 包长度(不含这2bytes和下面的tag 1byte)
+ /// 1bytes 协议ID,在AmongUS里是tags.cs里定义的tag和subtag
/// 数据 包括嵌套的子协议
/// ------------------------------------
///
@@ -20,8 +23,8 @@ namespace Hazel
public static int BufferSize = 64000;
public static readonly ObjectPool WriterPool = new ObjectPool(() => new MessageWriter(BufferSize));
- public byte[] Buffer;
- public int Length; // 总长度
+ public byte[] Buffer; // 缓冲区,保存了整个包体,包括头部和内容
+ public int Length; // 有效数据在buffer中的长度,可能包含多个嵌套子协议。Length>=Position
public int Position; // 写入游标
public SendOption SendOption { get; private set; }
@@ -86,6 +89,11 @@ namespace Hazel
return output;
}
+ ///
+ /// expected没有header
+ ///
+ ///
+ ///
public bool HasBytes(int expected)
{
if (this.SendOption == SendOption.None)
diff --git a/Tools/Hazel-Networking/Hazel/Udp/UdpClientConnection.cs b/Tools/Hazel-Networking/Hazel/Udp/UdpClientConnection.cs
index f6da329..90aeb0d 100644
--- a/Tools/Hazel-Networking/Hazel/Udp/UdpClientConnection.cs
+++ b/Tools/Hazel-Networking/Hazel/Udp/UdpClientConnection.cs
@@ -205,9 +205,10 @@ namespace Hazel.Udp
}
#endif
- var msg = MessageReader.GetSized(this.ReceiveBufferSize);
+ var msg = MessageReader.GetSized(this.ReceiveBufferSize);//һmessage
try
{
+ // BufferMessageWriterݣheader
socket.BeginReceive(msg.Buffer, 0, msg.Buffer.Length, SocketFlags.None, ReadCallback, msg);
}
catch
@@ -282,7 +283,7 @@ namespace Hazel.Udp
//Begin receiving again
try
{
- StartListeningForData();
+ StartListeningForData(); //Ϣûasync awaitһwhileѯҪǶ
}
catch (SocketException e)
{
@@ -305,6 +306,7 @@ namespace Hazel.Udp
DataReceivedRaw?.Invoke(msg.Buffer, msg.Length);
#endif
+ //c //! ص㿴泤ʲô
HandleReceive(msg, msg.Length);
}
diff --git a/Tools/Hazel-Networking/Hazel/Udp/UdpConnection.Reliable.cs b/Tools/Hazel-Networking/Hazel/Udp/UdpConnection.Reliable.cs
index bed4738..cff403b 100644
--- a/Tools/Hazel-Networking/Hazel/Udp/UdpConnection.Reliable.cs
+++ b/Tools/Hazel-Networking/Hazel/Udp/UdpConnection.Reliable.cs
@@ -280,7 +280,18 @@ namespace Hazel.Udp
ushort id;
if (ProcessReliableReceive(message.Buffer, 1, out id))
{
- InvokeDataReceived(SendOption.Reliable, message, 3, bytesReceived);
+ //c
+/*
+ void InvokeDataReceived(SendOption sendOption, MessageReader buffer, int dataOffset, int bytesReceived)
+ {
+ buffer.Offset = dataOffset;
+ buffer.Length = bytesReceived - dataOffset;
+ buffer.Position = 0;
+
+ InvokeDataReceived(buffer, sendOption);
+ }
+*/
+ InvokeDataReceived(SendOption.Reliable, message, 3, bytesReceived); // √ 3是header,不是length+tag
}
else
{
@@ -302,7 +313,7 @@ namespace Hazel.Udp
byte b2 = bytes[offset + 1];
//Get the ID form the packet
- id = (ushort)((b1 << 8) + b2);
+ id = (ushort)((b1 << 8) + b2); // 用于可靠传输的id
/*
* It gets a little complicated here (note the fact I'm actually using a multiline comment for once...)
diff --git a/Tools/Hazel-Networking/Hazel/Udp/UdpConnection.cs b/Tools/Hazel-Networking/Hazel/Udp/UdpConnection.cs
index e64576a..78f1788 100644
--- a/Tools/Hazel-Networking/Hazel/Udp/UdpConnection.cs
+++ b/Tools/Hazel-Networking/Hazel/Udp/UdpConnection.cs
@@ -77,7 +77,7 @@ namespace Hazel.Udp
case SendOption.Reliable:
ResetKeepAliveTimer();
- AttachReliableID(buffer, 1);
+ AttachReliableID(buffer, 1); // д֮ǰյڿɿID
WriteBytesToConnection(buffer, buffer.Length);
Statistics.LogReliableSend(buffer.Length - 3);
break;
@@ -132,6 +132,7 @@ namespace Hazel.Udp
{
//Handle reliable receives
case (byte)SendOption.Reliable:
+ //c //!
ReliableMessageReceive(message, bytesReceived);
break;
@@ -161,6 +162,7 @@ namespace Hazel.Udp
break;
case (byte)SendOption.None:
+ //c //!
InvokeDataReceived(SendOption.None, message, 1, bytesReceived);
Statistics.LogUnreliableReceive(bytesReceived - 1, bytesReceived);
break;
--
cgit v1.1-26-g67d0