Files
RebootKit/Runtime/Engine/Code/Network/NetworkBufferWriter.cs
2025-07-21 09:04:43 +02:00

316 lines
11 KiB
C#

using System;
using Unity.Collections;
using UnityEngine;
using UnityEngine.Assertions;
using UnityEngine.Pool;
namespace RebootKit.Engine.Network {
// @NOTE: Data is written in a linear fashion, so the position is always at the end of the written data.
// We are writting everything in little-endian format.
public struct NetworkBufferWriter : IDisposable {
class WriterHandle {
public NativeArray<byte> Data;
public bool IsOwner; // Indicates if this handle owns the data and should dispose it.
public int Position;
public int Capacity;
}
static readonly IObjectPool<WriterHandle> s_WriterPool = new ObjectPool<WriterHandle>(
() => new WriterHandle(),
_ => { },
handle => {
if (handle.Data.IsCreated && handle.IsOwner) {
handle.Data.Dispose();
}
handle.Data = default;
handle.Position = 0;
handle.Capacity = 0;
},
handle => {
if (handle.Data.IsCreated && handle.IsOwner) {
handle.Data.Dispose();
}
},
true,
256
);
WriterHandle m_Handle;
public int Position {
get {
return m_Handle.Position;
}
set {
Assert.IsTrue(value >= 0 && value <= m_Handle.Capacity, "Position must be within the bounds of the buffer.");
m_Handle.Position = value;
}
}
public NetworkBufferWriter(int capacity, Allocator allocator) {
m_Handle = s_WriterPool.Get();
m_Handle.Data = new NativeArray<byte>(capacity, allocator);
m_Handle.IsOwner = true;
m_Handle.Capacity = capacity;
m_Handle.Position = 0;
}
public NetworkBufferWriter(NativeArray<byte> buffer, int position) {
m_Handle = s_WriterPool.Get();
m_Handle.Data = buffer;
m_Handle.IsOwner = false;
m_Handle.Capacity = buffer.Length;
m_Handle.Position = position;
}
public void Dispose() {
if (m_Handle != null) {
s_WriterPool.Release(m_Handle);
m_Handle = null;
}
}
public bool WillFit(int size) {
return m_Handle.Position + size <= m_Handle.Capacity;
}
public void Write(byte value) {
if (m_Handle.Position >= m_Handle.Capacity) {
throw new InvalidOperationException("Buffer overflow: Cannot write beyond capacity.");
}
m_Handle.Data[m_Handle.Position++] = value;
}
public void Write(byte[] values) {
Assert.IsNotNull(values, "Trying to write null byte array to the buffer.");
Assert.IsTrue(WillFit(values.Length), "Buffer overflow: Cannot write beyond capacity.");
if (values.Length == 0) {
return;
}
for (int i = 0; i < values.Length; i++) {
m_Handle.Data[m_Handle.Position++] = values[i];
}
}
public void Write(NativeArray<byte> values) {
Assert.IsTrue(values.IsCreated, "Trying to write uncreated NativeArray to the buffer.");
Assert.IsTrue(WillFit(values.Length), "Buffer overflow: Cannot write beyond capacity.");
if (values.Length == 0) {
return;
}
for (int i = 0; i < values.Length; i++) {
m_Handle.Data[m_Handle.Position++] = values[i];
}
}
public void Write(int value) {
Assert.IsTrue(sizeof(int) == 4, "Size of int must be 4 bytes.");
Assert.IsTrue(WillFit(sizeof(int)), "Buffer overflow: Cannot write beyond capacity.");
if (BitConverter.IsLittleEndian) {
Write((byte) (value & 0xFF));
Write((byte) ((value >> 8) & 0xFF));
Write((byte) ((value >> 16) & 0xFF));
Write((byte) ((value >> 24) & 0xFF));
} else {
Write((byte) ((value >> 24) & 0xFF));
Write((byte) ((value >> 16) & 0xFF));
Write((byte) ((value >> 8) & 0xFF));
Write((byte) (value & 0xFF));
}
}
public void Write(short value) {
Assert.IsTrue(sizeof(short) == 2, "Size of short must be 2 bytes.");
Assert.IsTrue(WillFit(sizeof(short)), "Buffer overflow: Cannot write beyond capacity.");
if (BitConverter.IsLittleEndian) {
Write((byte) (value & 0xFF));
Write((byte) ((value >> 8) & 0xFF));
} else {
Write((byte) ((value >> 8) & 0xFF));
Write((byte) (value & 0xFF));
}
}
public void Write(ushort value) {
Assert.IsTrue(sizeof(ushort) == 2, "Size of ushort must be 2 bytes.");
Assert.IsTrue(WillFit(sizeof(ushort)), "Buffer overflow: Cannot write beyond capacity.");
if (BitConverter.IsLittleEndian) {
Write((byte) (value & 0xFF));
Write((byte) ((value >> 8) & 0xFF));
} else {
Write((byte) ((value >> 8) & 0xFF));
Write((byte) (value & 0xFF));
}
}
public void Write(long value) {
Assert.IsTrue(sizeof(long) == 8, "Size of long must be 8 bytes.");
Assert.IsTrue(WillFit(sizeof(long)), "Buffer overflow: Cannot write beyond capacity.");
if (BitConverter.IsLittleEndian) {
Write((byte) (value & 0xFF));
Write((byte) ((value >> 8) & 0xFF));
Write((byte) ((value >> 16) & 0xFF));
Write((byte) ((value >> 24) & 0xFF));
Write((byte) ((value >> 32) & 0xFF));
Write((byte) ((value >> 40) & 0xFF));
Write((byte) ((value >> 48) & 0xFF));
Write((byte) ((value >> 56) & 0xFF));
} else {
Write((byte) ((value >> 56) & 0xFF));
Write((byte) ((value >> 48) & 0xFF));
Write((byte) ((value >> 40) & 0xFF));
Write((byte) ((value >> 32) & 0xFF));
Write((byte) ((value >> 24) & 0xFF));
Write((byte) ((value >> 16) & 0xFF));
Write((byte) ((value >> 8) & 0xFF));
Write((byte) (value & 0xFF));
}
}
public void Write(ulong value) {
Assert.IsTrue(sizeof(ulong) == 8, "Size of ulong must be 8 bytes.");
Assert.IsTrue(WillFit(sizeof(ulong)), "Buffer overflow: Cannot write beyond capacity.");
if (BitConverter.IsLittleEndian) {
Write((byte) (value & 0xFF));
Write((byte) ((value >> 8) & 0xFF));
Write((byte) ((value >> 16) & 0xFF));
Write((byte) ((value >> 24) & 0xFF));
Write((byte) ((value >> 32) & 0xFF));
Write((byte) ((value >> 40) & 0xFF));
Write((byte) ((value >> 48) & 0xFF));
Write((byte) ((value >> 56) & 0xFF));
} else {
Write((byte) ((value >> 56) & 0xFF));
Write((byte) ((value >> 48) & 0xFF));
Write((byte) ((value >> 40) & 0xFF));
Write((byte) ((value >> 32) & 0xFF));
Write((byte) ((value >> 24) & 0xFF));
Write((byte) ((value >> 16) & 0xFF));
Write((byte) ((value >> 8) & 0xFF));
Write((byte) (value & 0xFF));
}
}
public void Write(float value) {
Assert.IsTrue(sizeof(float) == 4, "Size of float must be 4 bytes.");
Assert.IsTrue(WillFit(sizeof(float)), "Buffer overflow: Cannot write beyond capacity.");
unsafe {
byte* bytes = (byte*) &value;
Write(bytes[0]);
Write(bytes[1]);
Write(bytes[2]);
Write(bytes[3]);
}
}
public void Write(bool value) {
Assert.IsTrue(WillFit(1), "Buffer overflow: Cannot write beyond capacity.");
Write((byte) (value ? 1 : 0));
}
public void Write(Vector2 value) {
Assert.IsTrue(WillFit(sizeof(float) * 2), "Buffer overflow: Cannot write beyond capacity.");
Write(value.x);
Write(value.y);
}
public void Write(Vector3 value) {
Assert.IsTrue(WillFit(sizeof(float) * 3), "Buffer overflow: Cannot write beyond capacity.");
Write(value.x);
Write(value.y);
Write(value.z);
}
public void Write(Vector4 value) {
Assert.IsTrue(WillFit(sizeof(float) * 4), "Buffer overflow: Cannot write beyond capacity.");
Write(value.x);
Write(value.y);
Write(value.z);
Write(value.w);
}
public void Write(Quaternion value) {
Assert.IsTrue(WillFit(sizeof(float) * 4), "Buffer overflow: Cannot write beyond capacity.");
Write(value.x);
Write(value.y);
Write(value.z);
Write(value.w);
}
public void Write(FixedString32Bytes value) {
Assert.IsTrue(WillFit(32));
for (int i = 0; i < 32; i++) {
if (i < value.Length) {
Write(value[i]);
} else {
Write((byte) 0); // Fill with zero if the string is shorter than 32 bytes
}
}
}
public void Write(FixedString64Bytes value) {
Assert.IsTrue(WillFit(64));
for (int i = 0; i < 64; i++) {
if (i < value.Length) {
Write(value[i]);
} else {
Write((byte) 0); // Fill with zero if the string is shorter than 64 bytes
}
}
}
public void Write(FixedString128Bytes value) {
Assert.IsTrue(WillFit(128));
for (int i = 0; i < 128; i++) {
if (i < value.Length) {
Write(value[i]);
} else {
Write((byte) 0); // Fill with zero if the string is shorter than 128 bytes
}
}
}
public void Write(FixedString512Bytes value) {
Assert.IsTrue(WillFit(512));
for (int i = 0; i < 512; i++) {
if (i < value.Length) {
Write(value[i]);
} else {
Write((byte) 0); // Fill with zero if the string is shorter than 512 bytes
}
}
}
public void Write(FixedString4096Bytes value) {
Assert.IsTrue(WillFit(4096));
for (int i = 0; i < 4096; i++) {
if (i < value.Length) {
Write(value[i]);
} else {
Write((byte) 0); // Fill with zero if the string is shorter than 4096 bytes
}
}
}
}
}