您最多选择25个主题
主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
252 行
9.8 KiB
252 行
9.8 KiB
using System;
|
|
using System.Collections.Generic;
|
|
using System.Xml.Schema;
|
|
using UnityEngine;
|
|
|
|
|
|
public struct NetworkWriter
|
|
{
|
|
public NetworkWriter(byte[] buffer, NetworkSchema schema, bool generateSchema = false)
|
|
{
|
|
m_Output = new ByteOutputStream(buffer);
|
|
m_Schema = schema;
|
|
m_CurrentField = null;
|
|
m_NextFieldIndex = 0;
|
|
m_GenerateSchema = generateSchema;
|
|
m_FieldMask = 0;
|
|
}
|
|
|
|
public int GetLength()
|
|
{
|
|
return m_Output.GetBytePosition();
|
|
}
|
|
|
|
void ValidateOrGenerateSchema(string name, NetworkSchema.FieldType type, int bits = 0, bool delta = false, int precision = 0, int arraySize = 0)
|
|
{
|
|
// TOULF precision is amount of digits (10^-3)
|
|
GameDebug.Assert(precision < 4);
|
|
if (m_GenerateSchema == true)
|
|
{
|
|
// 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.fields.Count * 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.WriteUInt8(value ? (byte)1 : (byte)0);
|
|
}
|
|
|
|
public void WriteByte(string name, byte value)
|
|
{
|
|
ValidateOrGenerateSchema(name, NetworkSchema.FieldType.UInt, 8, true);
|
|
m_Output.WriteUInt8(value);
|
|
}
|
|
|
|
public void WriteInt16(string name, short value)
|
|
{
|
|
ValidateOrGenerateSchema(name, NetworkSchema.FieldType.Int, 16, true);
|
|
m_Output.WriteUInt16((ushort)value);
|
|
}
|
|
|
|
public void WriteUInt16(string name, ushort value)
|
|
{
|
|
ValidateOrGenerateSchema(name, NetworkSchema.FieldType.UInt, 16, true);
|
|
m_Output.WriteUInt16(value);
|
|
}
|
|
|
|
public void WriteInt32(string name, int value)
|
|
{
|
|
ValidateOrGenerateSchema(name, NetworkSchema.FieldType.Int, 32, true);
|
|
m_Output.WriteUInt32((uint)value);
|
|
}
|
|
|
|
public void WriteUInt32(string name, uint value)
|
|
{
|
|
ValidateOrGenerateSchema(name, NetworkSchema.FieldType.UInt, 32, true);
|
|
m_Output.WriteUInt32(value);
|
|
}
|
|
|
|
public void WriteFloat(string name, float value)
|
|
{
|
|
ValidateOrGenerateSchema(name, NetworkSchema.FieldType.Float, 32, false);
|
|
m_Output.WriteUInt32(NetworkUtils.FloatToUInt32(value));
|
|
}
|
|
|
|
public void WriteFloatQ(string name, float value, int precision = 3)
|
|
{
|
|
ValidateOrGenerateSchema(name, NetworkSchema.FieldType.Float, 32, true, precision);
|
|
m_Output.WriteUInt32((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);
|
|
if (length == 0)
|
|
{
|
|
m_Output.WriteByteArray(null, 0, 0, maxLength);
|
|
return;
|
|
}
|
|
|
|
GameDebug.Assert(maxLength <= s_ByteBuffer.Length, "NetworkWriter: Max length has to be less than {0}", s_ByteBuffer.Length);
|
|
|
|
// 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.
|
|
var 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);
|
|
}
|
|
|
|
m_Output.WriteByteArray(s_ByteBuffer, 0, byteCount, maxLength);
|
|
}
|
|
|
|
public void WriteVector2(string name, Vector2 value)
|
|
{
|
|
ValidateOrGenerateSchema(name, NetworkSchema.FieldType.Vector2, 32);
|
|
m_Output.WriteUInt32(NetworkUtils.FloatToUInt32(value.x));
|
|
m_Output.WriteUInt32(NetworkUtils.FloatToUInt32(value.y));
|
|
}
|
|
|
|
public void WriteVector2Q(string name, Vector2 value, int precision = 3)
|
|
{
|
|
ValidateOrGenerateSchema(name, NetworkSchema.FieldType.Vector2, 32, true, precision);
|
|
m_Output.WriteUInt32((uint)Mathf.RoundToInt(value.x * NetworkConfig.encoderPrecisionScales[precision]));
|
|
m_Output.WriteUInt32((uint)Mathf.RoundToInt(value.y * NetworkConfig.encoderPrecisionScales[precision]));
|
|
}
|
|
|
|
public void WriteVector3(string name, Vector3 value)
|
|
{
|
|
ValidateOrGenerateSchema(name, NetworkSchema.FieldType.Vector3, 32);
|
|
m_Output.WriteUInt32(NetworkUtils.FloatToUInt32(value.x));
|
|
m_Output.WriteUInt32(NetworkUtils.FloatToUInt32(value.y));
|
|
m_Output.WriteUInt32(NetworkUtils.FloatToUInt32(value.z));
|
|
}
|
|
|
|
public void WriteVector3Q(string name, Vector3 value, int precision = 3)
|
|
{
|
|
ValidateOrGenerateSchema(name, NetworkSchema.FieldType.Vector3, 32, true, precision);
|
|
m_Output.WriteUInt32((uint)Mathf.RoundToInt(value.x * NetworkConfig.encoderPrecisionScales[precision]));
|
|
m_Output.WriteUInt32((uint)Mathf.RoundToInt(value.y * NetworkConfig.encoderPrecisionScales[precision]));
|
|
m_Output.WriteUInt32((uint)Mathf.RoundToInt(value.z * NetworkConfig.encoderPrecisionScales[precision]));
|
|
}
|
|
|
|
public void WriteQuaternion(string name, Quaternion value)
|
|
{
|
|
ValidateOrGenerateSchema(name, NetworkSchema.FieldType.Quaternion, 32);
|
|
m_Output.WriteUInt32(NetworkUtils.FloatToUInt32(value.x));
|
|
m_Output.WriteUInt32(NetworkUtils.FloatToUInt32(value.y));
|
|
m_Output.WriteUInt32(NetworkUtils.FloatToUInt32(value.z));
|
|
m_Output.WriteUInt32(NetworkUtils.FloatToUInt32(value.w));
|
|
}
|
|
public void WriteQuaternionQ(string name, Quaternion value, int precision = 3)
|
|
{
|
|
ValidateOrGenerateSchema(name, NetworkSchema.FieldType.Quaternion, 32, true, precision);
|
|
m_Output.WriteUInt32((uint)Mathf.RoundToInt(value.x * NetworkConfig.encoderPrecisionScales[precision]));
|
|
m_Output.WriteUInt32((uint)Mathf.RoundToInt(value.y * NetworkConfig.encoderPrecisionScales[precision]));
|
|
m_Output.WriteUInt32((uint)Mathf.RoundToInt(value.z * NetworkConfig.encoderPrecisionScales[precision]));
|
|
m_Output.WriteUInt32((uint)Mathf.RoundToInt(value.w * NetworkConfig.encoderPrecisionScales[precision]));
|
|
}
|
|
|
|
public void WriteBytes(string name, byte[] value, int srcIndex, int count, int maxCount)
|
|
{
|
|
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);
|
|
}
|
|
|
|
public void Flush()
|
|
{
|
|
}
|
|
|
|
static byte[] s_ByteBuffer = new byte[1024 * 32];
|
|
|
|
NetworkSchema m_Schema;
|
|
NetworkSchema.FieldInfo m_CurrentField;
|
|
ByteOutputStream m_Output;
|
|
int m_NextFieldIndex;
|
|
byte m_FieldMask;
|
|
bool m_GenerateSchema;
|
|
}
|