该项目的目的是同时测试和演示来自 Unity DOTS 技术堆栈的多个新包。
您最多选择25个主题 主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 
 

334 行
13 KiB

using System;
using System.Collections.Generic;
using System.Xml.Schema;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using Unity.Entities;
using Unity.Sample.Core;
using UnityEngine;
unsafe public struct NetworkWriter
{
public NetworkWriter(uint* buffer, int bufferSize, NetworkSchema schema, bool generateSchema = false)
{
m_Output = buffer;
m_BufferSize = bufferSize;
m_Position = 0;
m_Schema = schema;
m_CurrentField = null;
m_NextFieldIndex = 0;
m_GenerateSchema = generateSchema;
m_FieldMask = 0;
}
public int GetLength()
{
return m_Position;
}
void ValidateOrGenerateSchema(string name, NetworkSchema.FieldType type, int bits = 0, bool delta = false, int precision = 0, int arraySize = 0)
{
if (m_Position + arraySize >= m_BufferSize)
{
// This is a really hard error to recover from. So we just try to make sure everything stops...
GameDebug.Assert(false, "Out of buffer space in NetworkWriter.");
}
// Precision is amount of digits (10^-3)
GameDebug.Assert(precision < 4, "Precision has to be less than 4 digits. If you need more use unquantizised values");
if (m_GenerateSchema == true)
{
if (type == NetworkSchema.FieldType.Bool || type == NetworkSchema.FieldType.ByteArray || type == NetworkSchema.FieldType.String)
GameDebug.Assert(delta == false, "Delta compression of fields of type bool, bytearray and string not supported");
// TOULF m_Scheme will contain scheme for ALL of the *entity* (not component)
m_Schema.AddField(new NetworkSchema.FieldInfo()
{
name = name,
fieldType = type,
bits = bits,
delta = delta,
precision = precision,
arraySize = arraySize,
fieldMask = m_FieldMask,
startContext = m_Schema.numFields * NetworkConfig.maxContextsPerField + m_Schema.id * NetworkConfig.maxContextsPerSchema + NetworkConfig.firstSchemaContext
});
}
else if (m_Schema != null)
{
m_CurrentField = m_Schema.fields[m_NextFieldIndex];
GameDebug.Assert(m_CurrentField.name == name);
GameDebug.Assert(m_CurrentField.fieldType == type);
GameDebug.Assert(m_CurrentField.bits == bits);
GameDebug.Assert(m_CurrentField.delta == delta);
GameDebug.Assert(m_CurrentField.precision == precision);
GameDebug.Assert(m_CurrentField.arraySize == arraySize);
GameDebug.Assert(m_CurrentField.fieldMask == m_FieldMask);
++m_NextFieldIndex;
}
// TOULF when is it ok that m_Scheme being null?
}
public enum FieldSectionType
{
OnlyPredicting,
OnlyNotPredicting
}
public void SetFieldSection(FieldSectionType type)
{
GameDebug.Assert(m_FieldMask == 0, "Field masks cannot be combined.");
if (type == FieldSectionType.OnlyNotPredicting)
m_FieldMask = 0x1;
else
m_FieldMask = 0x2;
}
public void ClearFieldSection()
{
GameDebug.Assert(m_FieldMask != 0, "Trying to clear a field mask but none has been set.");
m_FieldMask = 0;
}
public void WriteBoolean(string name, bool value)
{
ValidateOrGenerateSchema(name, NetworkSchema.FieldType.Bool, 1, false);
m_Output[m_Position++] = value ? 1u : 0u;
}
public void WriteByte(string name, byte value)
{
ValidateOrGenerateSchema(name, NetworkSchema.FieldType.UInt, 8, true);
m_Output[m_Position++] = value;
}
public void WriteInt16(string name, short value)
{
ValidateOrGenerateSchema(name, NetworkSchema.FieldType.Int, 16, true);
m_Output[m_Position++] = (ushort)value;
}
public void WriteUInt16(string name, ushort value)
{
ValidateOrGenerateSchema(name, NetworkSchema.FieldType.UInt, 16, true);
m_Output[m_Position++] = value;
}
public void WriteInt32(string name, int value)
{
ValidateOrGenerateSchema(name, NetworkSchema.FieldType.Int, 32, true);
m_Output[m_Position++] = (uint)value;
}
public void WriteUInt32(string name, uint value)
{
ValidateOrGenerateSchema(name, NetworkSchema.FieldType.UInt, 32, true);
m_Output[m_Position++] = value;
}
public void WriteFloat(string name, float value)
{
ValidateOrGenerateSchema(name, NetworkSchema.FieldType.Float, 32, false);
m_Output[m_Position++] = NetworkUtils.FloatToUInt32(value);
}
public void WriteFloatQ(string name, float value, int precision = 3)
{
ValidateOrGenerateSchema(name, NetworkSchema.FieldType.Float, 32, true, precision);
m_Output[m_Position++] = (uint)Mathf.RoundToInt(value * NetworkConfig.encoderPrecisionScales[precision]);
}
public enum OverrunBehaviour
{
AssertMaxLength,
WarnAndTrunc,
SilentTrunc
}
static char[] _writeStringBuf = new char[64];
public void WriteString(string name, string value, int maxLength = 64, OverrunBehaviour overrunBehaviour = OverrunBehaviour.WarnAndTrunc)
{
if (value == null)
value = "";
if (value.Length > _writeStringBuf.Length)
_writeStringBuf = new char[_writeStringBuf.Length * 2];
value.CopyTo(0, _writeStringBuf, 0, value.Length);
WriteString(name, _writeStringBuf, value.Length, maxLength, overrunBehaviour);
}
public void WriteString(string name, char[] value, int length, int maxLength, OverrunBehaviour overrunBehaviour)
{
ValidateOrGenerateSchema(name, NetworkSchema.FieldType.String, 0, false, 0, maxLength);
GameDebug.Assert(maxLength <= s_ByteBuffer.Length, "NetworkWriter: Max length has to be less than {0}", s_ByteBuffer.Length);
GameDebug.Assert((maxLength & 0x3) == 0, "MaxLength has to be 32bit aligned");
var byteCount = 0;
if (length > 0)
{
// Ensure the (utf-8) *encoded* string is not too big. If it is, cut it off,
// convert back to unicode and then back again to utf-8. This little dance gives
// a valid utf-8 string within the buffer size.
byteCount = NetworkConfig.encoding.GetBytes(value, 0, length, s_ByteBuffer, 0);
if (byteCount > maxLength)
{
if (overrunBehaviour == OverrunBehaviour.AssertMaxLength)
{
GameDebug.Assert(false, "NetworkWriter : string {0} too long. (Using {1}/{2} allowed encoded bytes): ", value, byteCount, maxLength);
}
// truncate
var truncWithBadEnd = NetworkConfig.encoding.GetString(s_ByteBuffer, 0, maxLength);
var truncOk = truncWithBadEnd.Substring(0, truncWithBadEnd.Length - 1);
var newbyteCount = NetworkConfig.encoding.GetBytes(truncOk, 0, truncOk.Length, s_ByteBuffer, 0);
if (overrunBehaviour == OverrunBehaviour.WarnAndTrunc)
{
GameDebug.LogWarning(string.Format("NetworkWriter : truncated string with {0} bytes. (result: {1})", byteCount - newbyteCount, truncOk));
}
byteCount = newbyteCount;
GameDebug.Assert(byteCount <= maxLength, "String encoding failed");
}
}
m_Output[m_Position++] = (uint)byteCount;
byte* dst = (byte*)(m_Output + m_Position);
int i = 0;
for (; i < byteCount; ++i)
*dst++ = s_ByteBuffer[i];
for (; i < maxLength; ++i)
*dst++ = 0;
GameDebug.Assert(((uint)dst & 0x3) == 0, "Expected to stay aligned!");
m_Position += maxLength / 4;
}
public void WriteNativeString(string name, NativeString64 value, int maxLength = 64, OverrunBehaviour overrunBehaviour = OverrunBehaviour.WarnAndTrunc)
{
ValidateOrGenerateSchema(name, NetworkSchema.FieldType.String, 0, false, 0, maxLength);
GameDebug.Assert((maxLength & 0x3) == 0, "MaxLength has to be 32bit aligned");
var bytes = &value.buffer.byte0000;
var byteCount = maxLength;
if (value.LengthInBytes > maxLength)
{
if (overrunBehaviour == OverrunBehaviour.AssertMaxLength)
{
GameDebug.Assert(false, "NetworkWriter : string {0} too long. (Using {1}/{2} allowed encoded bytes): ", value, byteCount, maxLength);
}
// truncate
byteCount = maxLength;
// Find the last non-trailer code unit and attempt to get its code point
// If that fails truncate right before this non-trailer code unit
// TODO: NativeString will get API for truncating, replace with that when it's ready
var lastNonTrailerIndex = byteCount - 1;
while (Unicode.NotTrailer(bytes[lastNonTrailerIndex]))
{
lastNonTrailerIndex--;
}
var offset = lastNonTrailerIndex;
var conversionError = Unicode.Utf8ToUcs(out var rune, bytes, ref offset, maxLength);
if (conversionError != ConversionError.None)
{
byteCount = lastNonTrailerIndex;
}
}
m_Output[m_Position++] = (uint)byteCount;
byte* dst = (byte*)(m_Output + m_Position);
int i = 0;
for (; i < byteCount; ++i)
*dst++ = bytes[i];
for (; i < maxLength; ++i)
*dst++ = 0;
GameDebug.Assert(((uint)dst & 0x3) == 0, "Expected to stay aligned!");
m_Position += maxLength / 4;
}
public void WriteBytes(string name, byte[] value, int srcIndex, int count, int maxCount)
{
GameDebug.Assert((maxCount & 0x3) == 0, "MaxCount has to be 32bit aligned");
ValidateOrGenerateSchema(name, NetworkSchema.FieldType.ByteArray, 0, false, 0, maxCount);
if (count > ushort.MaxValue)
throw new System.ArgumentException("NetworkWriter : Byte buffer too big : " + count);
//m_Output.WriteByteArray(value, srcIndex, count, maxCount);
m_Output[m_Position++] = (uint)count;
byte* dst = (byte*)(m_Output + m_Position);
int i = 0;
for (; i < count; ++i)
*dst++ = value[i];
for (; i < maxCount; ++i)
*dst++ = 0;
m_Position += maxCount / 4;
}
public void WriteVector2(string name, Vector2 value)
{
ValidateOrGenerateSchema(name, NetworkSchema.FieldType.Vector2, 32);
m_Output[m_Position++] = NetworkUtils.FloatToUInt32(value.x);
m_Output[m_Position++] = NetworkUtils.FloatToUInt32(value.y);
}
public void WriteVector2Q(string name, Vector2 value, int precision = 3)
{
ValidateOrGenerateSchema(name, NetworkSchema.FieldType.Vector2, 32, true, precision);
m_Output[m_Position++] = (uint)Mathf.RoundToInt(value.x * NetworkConfig.encoderPrecisionScales[precision]);
m_Output[m_Position++] = (uint)Mathf.RoundToInt(value.y * NetworkConfig.encoderPrecisionScales[precision]);
}
public void WriteVector3(string name, Vector3 value)
{
ValidateOrGenerateSchema(name, NetworkSchema.FieldType.Vector3, 32);
m_Output[m_Position++] = NetworkUtils.FloatToUInt32(value.x);
m_Output[m_Position++] = NetworkUtils.FloatToUInt32(value.y);
m_Output[m_Position++] = NetworkUtils.FloatToUInt32(value.z);
}
public void WriteVector3Q(string name, Vector3 value, int precision = 3)
{
ValidateOrGenerateSchema(name, NetworkSchema.FieldType.Vector3, 32, true, precision);
m_Output[m_Position++] = (uint)Mathf.RoundToInt(value.x * NetworkConfig.encoderPrecisionScales[precision]);
m_Output[m_Position++] = (uint)Mathf.RoundToInt(value.y * NetworkConfig.encoderPrecisionScales[precision]);
m_Output[m_Position++] = (uint)Mathf.RoundToInt(value.z * NetworkConfig.encoderPrecisionScales[precision]);
}
public void WriteQuaternion(string name, Quaternion value)
{
ValidateOrGenerateSchema(name, NetworkSchema.FieldType.Quaternion, 32);
m_Output[m_Position++] = NetworkUtils.FloatToUInt32(value.x);
m_Output[m_Position++] = NetworkUtils.FloatToUInt32(value.y);
m_Output[m_Position++] = NetworkUtils.FloatToUInt32(value.z);
m_Output[m_Position++] = NetworkUtils.FloatToUInt32(value.w);
}
public void WriteQuaternionQ(string name, Quaternion value, int precision = 3)
{
ValidateOrGenerateSchema(name, NetworkSchema.FieldType.Quaternion, 32, true, precision);
m_Output[m_Position++] = (uint)Mathf.RoundToInt(value.x * NetworkConfig.encoderPrecisionScales[precision]);
m_Output[m_Position++] = (uint)Mathf.RoundToInt(value.y * NetworkConfig.encoderPrecisionScales[precision]);
m_Output[m_Position++] = (uint)Mathf.RoundToInt(value.z * NetworkConfig.encoderPrecisionScales[precision]);
m_Output[m_Position++] = (uint)Mathf.RoundToInt(value.w * NetworkConfig.encoderPrecisionScales[precision]);
}
public void Flush()
{
if (m_GenerateSchema)
m_Schema.Finalize();
}
static byte[] s_ByteBuffer = new byte[1024 * 32];
NetworkSchema m_Schema;
NetworkSchema.FieldInfo m_CurrentField;
uint* m_Output;
int m_BufferSize;
int m_Position;
int m_NextFieldIndex;
byte m_FieldMask;
bool m_GenerateSchema;
}