Tim Mowrer
6 年前
当前提交
5a15be14
共有 20 个文件被更改,包括 0 次插入 和 925 次删除
-
103Assets/Scenes/ARCollaborationData/ClientServerSelector.cs
-
11Assets/Scenes/ARCollaborationData/ClientServerSelector.cs.meta
-
50Assets/Scenes/ARCollaborationData/TCPClient.cs
-
11Assets/Scenes/ARCollaborationData/TCPClient.cs.meta
-
315Assets/Scenes/ARCollaborationData/TCPConnection.cs
-
11Assets/Scenes/ARCollaborationData/TCPConnection.cs.meta
-
43Assets/Scenes/ARCollaborationData/TCPServer.cs
-
11Assets/Scenes/ARCollaborationData/TCPServer.cs.meta
-
4Assets/Scenes/ARCollaborationData/IMessage.cs
-
11Assets/Scenes/ARCollaborationData/IMessage.cs.meta
-
23Assets/Scenes/ARCollaborationData/MessageHeader.cs
-
11Assets/Scenes/ARCollaborationData/MessageHeader.cs.meta
-
6Assets/Scenes/ARCollaborationData/MessageType.cs
-
11Assets/Scenes/ARCollaborationData/MessageType.cs.meta
-
108Assets/Scenes/ARCollaborationData/NetworkBuffer.cs
-
11Assets/Scenes/ARCollaborationData/NetworkBuffer.cs.meta
-
86Assets/Scenes/ARCollaborationData/NetworkDataDecoder.cs
-
11Assets/Scenes/ARCollaborationData/NetworkDataDecoder.cs.meta
-
77Assets/Scenes/ARCollaborationData/NetworkDataEncoder.cs
-
11Assets/Scenes/ARCollaborationData/NetworkDataEncoder.cs.meta
|
|||
using System; |
|||
using System.Collections; |
|||
using System.Collections.Generic; |
|||
using System.IO; |
|||
using UnityEngine; |
|||
using UnityEngine.UI; |
|||
|
|||
[RequireComponent(typeof(TCPClient))] |
|||
[RequireComponent(typeof(TCPServer))] |
|||
public class ClientServerSelector : MonoBehaviour |
|||
{ |
|||
[SerializeField] |
|||
Button m_JoinButton; |
|||
|
|||
public Button joinButton |
|||
{ |
|||
get { return m_JoinButton; } |
|||
set { m_JoinButton = value; } |
|||
} |
|||
|
|||
[SerializeField] |
|||
Button m_HostButton; |
|||
|
|||
public Button hostButton |
|||
{ |
|||
get { return m_HostButton; } |
|||
set { m_HostButton = value; } |
|||
} |
|||
|
|||
[SerializeField] |
|||
InputField m_IPAddressField; |
|||
|
|||
public InputField ipAddressField |
|||
{ |
|||
get { return m_IPAddressField; } |
|||
set { m_IPAddressField = value; } |
|||
} |
|||
|
|||
public void Join() |
|||
{ |
|||
var client = GetComponent<TCPClient>(); |
|||
var ipAddress = m_IPAddressField.text; |
|||
try |
|||
{ |
|||
File.WriteAllText(GetIPAddressPath(), ipAddress); |
|||
} |
|||
catch (Exception e) |
|||
{ |
|||
Logger.Log($"Could not save IP address because {e.ToString()}"); |
|||
} |
|||
|
|||
client.serverIP = ipAddress; |
|||
client.enabled = true; |
|||
enabled = false; |
|||
} |
|||
|
|||
public void Host() |
|||
{ |
|||
GetComponent<TCPClient>().enabled = false; |
|||
GetComponent<TCPServer>().enabled = true; |
|||
enabled = false; |
|||
} |
|||
|
|||
string GetIPAddressPath() |
|||
{ |
|||
return Path.Combine(Application.persistentDataPath, "ipaddress.txt"); |
|||
} |
|||
|
|||
void OnEnable() |
|||
{ |
|||
if (File.Exists(GetIPAddressPath())) |
|||
{ |
|||
var storedIPAddress = File.ReadAllText(GetIPAddressPath()); |
|||
if (storedIPAddress != null) |
|||
{ |
|||
Logger.Log($"Found stored IP address {storedIPAddress}"); |
|||
m_IPAddressField.text = storedIPAddress; |
|||
} |
|||
else |
|||
{ |
|||
Logger.Log($"No IP address tored at {GetIPAddressPath()}"); |
|||
} |
|||
} |
|||
|
|||
if (m_JoinButton != null) |
|||
m_JoinButton.gameObject.SetActive(true); |
|||
|
|||
if (m_HostButton != null) |
|||
m_HostButton.gameObject.SetActive(true); |
|||
} |
|||
|
|||
void OnDisable() |
|||
{ |
|||
if (m_JoinButton != null) |
|||
m_JoinButton.gameObject.SetActive(false); |
|||
|
|||
if (m_HostButton != null) |
|||
m_HostButton.gameObject.SetActive(false); |
|||
|
|||
if (m_IPAddressField != null) |
|||
m_IPAddressField.gameObject.SetActive(false); |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: bbcf44277865e45d399575001e42f155 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using System; |
|||
using System.Collections; |
|||
using System.Collections.Generic; |
|||
using System.Net.Sockets; |
|||
using System.Text; |
|||
using System.Threading; |
|||
using UnityEngine; |
|||
using UnityEngine.UI; |
|||
using UnityEngine.XR.ARFoundation; |
|||
|
|||
[RequireComponent(typeof(ClientServerSelector))] |
|||
public class TCPClient : TCPConnection |
|||
{ |
|||
string m_ServerIP; |
|||
|
|||
public string serverIP |
|||
{ |
|||
get { return m_ServerIP; } |
|||
set |
|||
{ |
|||
if (enabled) |
|||
throw new InvalidOperationException("Cannot change server IP address while enabled."); |
|||
|
|||
m_ServerIP = value; |
|||
} |
|||
} |
|||
|
|||
public void Connect() |
|||
{ |
|||
Logger.Log($"Connecting to {serverIP} on port {port}"); |
|||
|
|||
try |
|||
{ |
|||
m_TcpClient = new TcpClient(serverIP, port); |
|||
Logger.Log("Connected!"); |
|||
} |
|||
catch (SocketException e) |
|||
{ |
|||
Logger.Log(e.Message); |
|||
enabled = false; |
|||
GetComponent<ClientServerSelector>().enabled = true; |
|||
} |
|||
} |
|||
|
|||
protected override void OnEnable() |
|||
{ |
|||
base.OnEnable(); |
|||
Connect(); |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: a4bbe7d8f963c43d7b710873f90b9b2d |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using System; |
|||
using System.Collections; |
|||
using System.Collections.Generic; |
|||
using System.IO; |
|||
using System.Net; |
|||
using System.Net.Sockets; |
|||
using System.Runtime.InteropServices; |
|||
using System.Threading; |
|||
using Unity.Collections; |
|||
using Unity.Collections.LowLevel.Unsafe; |
|||
using UnityEngine; |
|||
using UnityEngine.UI; |
|||
using UnityEngine.XR.ARFoundation; |
|||
using UnityEngine.XR.ARSubsystems; |
|||
#if UNITY_IOS
|
|||
using UnityEngine.XR.ARKit; |
|||
#endif
|
|||
|
|||
public abstract class TCPConnection : MonoBehaviour |
|||
{ |
|||
[SerializeField] |
|||
ARSession m_Session; |
|||
|
|||
public ARSession session |
|||
{ |
|||
get { return m_Session; } |
|||
set { m_Session = value; } |
|||
} |
|||
|
|||
[SerializeField] |
|||
int m_Port = 8502; |
|||
|
|||
public int port |
|||
{ |
|||
get { return m_Port; } |
|||
set { m_Port = value; } |
|||
} |
|||
|
|||
public bool connected |
|||
{ |
|||
get |
|||
{ |
|||
return |
|||
(m_TcpClient != null) && |
|||
(m_TcpClient.Connected); |
|||
} |
|||
} |
|||
|
|||
protected TcpClient m_TcpClient; |
|||
|
|||
protected virtual void OnEnable() |
|||
{ |
|||
#if UNITY_IOS
|
|||
if (ARKitSessionSubsystem.supportsCollaboration) |
|||
{ |
|||
m_ExitRequested = false; |
|||
} |
|||
else |
|||
#endif
|
|||
{ |
|||
Logger.Log("Collaboration is not supported by this device."); |
|||
enabled = false; |
|||
} |
|||
} |
|||
|
|||
protected virtual void OnDisable() |
|||
{ |
|||
#if UNITY_IOS
|
|||
// Shutdown running threads
|
|||
m_ExitRequested = true; |
|||
|
|||
if (m_ReadThread.IsAlive) |
|||
m_ReadThread.Join(); |
|||
|
|||
if (m_SendThread.IsAlive) |
|||
m_SendThread.Join(); |
|||
#endif
|
|||
|
|||
// Close down TCP connection
|
|||
if (m_TcpClient != null) |
|||
{ |
|||
m_TcpClient.Close(); |
|||
Logger.Log("Connection closed"); |
|||
} |
|||
|
|||
m_TcpClient = null; |
|||
} |
|||
|
|||
protected virtual void Update() |
|||
{ |
|||
#if UNITY_IOS
|
|||
if (session == null) |
|||
return; |
|||
|
|||
var subsystem = session.subsystem as ARKitSessionSubsystem; |
|||
if (subsystem == null) |
|||
return; |
|||
|
|||
// Disable collaboration if we aren't connected to anyone
|
|||
subsystem.collaborationEnabled = connected; |
|||
|
|||
if (connected) |
|||
{ |
|||
// Make sure threads are running
|
|||
if (!m_ReadThread.IsAlive) |
|||
m_ReadThread.Start(); |
|||
if (!m_SendThread.IsAlive) |
|||
m_SendThread.Start(); |
|||
|
|||
ProcessRemoteCollaborationData(subsystem); |
|||
CheckForLocalCollaborationData(subsystem); |
|||
} |
|||
#endif
|
|||
} |
|||
|
|||
#if UNITY_IOS
|
|||
Queue<ARCollaborationData> m_CollaborationDataSendQueue; |
|||
|
|||
Queue<ARCollaborationData> m_CollaborationDataReadQueue; |
|||
|
|||
Thread m_ReadThread; |
|||
|
|||
Thread m_SendThread; |
|||
|
|||
bool m_ExitRequested; |
|||
|
|||
void Awake() |
|||
{ |
|||
m_CollaborationDataSendQueue = new Queue<ARCollaborationData>(); |
|||
m_CollaborationDataReadQueue = new Queue<ARCollaborationData>(); |
|||
m_ReadThread = new Thread(ReadThreadProc); |
|||
m_SendThread = new Thread(SendThreadProc); |
|||
} |
|||
|
|||
void SendThreadProc() |
|||
{ |
|||
var stream = m_TcpClient.GetStream(); |
|||
while (!m_ExitRequested) |
|||
{ |
|||
var collaborationData = new ARCollaborationData(); |
|||
int queueSize = 0; |
|||
lock (m_CollaborationDataSendQueue) |
|||
{ |
|||
if (m_CollaborationDataSendQueue.Count > 0) |
|||
{ |
|||
collaborationData = m_CollaborationDataSendQueue.Dequeue(); |
|||
} |
|||
queueSize = m_CollaborationDataSendQueue.Count; |
|||
} |
|||
|
|||
if (collaborationData.valid) |
|||
{ |
|||
// Serialize the collaboration data to a byte array
|
|||
SerializedARCollaborationData serializedData; |
|||
using (collaborationData) |
|||
{ |
|||
// ARCollaborationData can be diposed after being serialized to bytes.
|
|||
serializedData = collaborationData.ToSerialized(); |
|||
} |
|||
|
|||
using (serializedData) |
|||
{ |
|||
// Get the raw data as a NativeSlice
|
|||
var collaborationBytes = serializedData.bytes; |
|||
|
|||
// Construct the message header
|
|||
var header = new MessageHeader |
|||
{ |
|||
messageSize = collaborationBytes.Length, |
|||
messageType = MessageType.CollaborationData |
|||
}; |
|||
|
|||
// Send the header followed by the ARCollaborationData bytes
|
|||
m_WriteBuffer.Send(stream, header); |
|||
m_WriteBuffer.Send(stream, collaborationBytes); |
|||
Logger.Log($"Sent {collaborationBytes.Length} bytes of collaboration data."); |
|||
} |
|||
} |
|||
|
|||
if (queueSize == 0) |
|||
{ |
|||
// If there's nothing else in the queue at the moment,
|
|||
// then go to sleep for a bit.
|
|||
// Otherwise, immediately try to send the next one.
|
|||
Thread.Sleep(1); |
|||
} |
|||
} |
|||
} |
|||
|
|||
unsafe void ReadThreadProc() |
|||
{ |
|||
var stream = m_TcpClient.GetStream(); |
|||
while (!m_ExitRequested) |
|||
{ |
|||
// Loop until there is data available
|
|||
if (!stream.DataAvailable) |
|||
{ |
|||
Thread.Sleep(1); |
|||
continue; |
|||
} |
|||
|
|||
// Read the header
|
|||
var messageHeader = ReadMessageHeader(stream); |
|||
|
|||
// Handle the message
|
|||
switch (messageHeader.messageType) |
|||
{ |
|||
case MessageType.CollaborationData: |
|||
var collaborationData = ReadCollaborationData(stream, messageHeader.messageSize); |
|||
if (collaborationData.valid) |
|||
{ |
|||
// Only store critical data updates; optional updates can come every frame.
|
|||
if (collaborationData.priority == ARCollaborationDataPriority.Critical) |
|||
{ |
|||
lock (m_CollaborationDataReadQueue) |
|||
{ |
|||
m_CollaborationDataReadQueue.Enqueue(collaborationData); |
|||
} |
|||
Logger.Log($"Received {messageHeader.messageSize} bytes of collaboration data."); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
Logger.Log($"Received {messageHeader.messageSize} bytes from remote, but the collaboration data was not valid."); |
|||
} |
|||
break; |
|||
|
|||
default: |
|||
Logger.Log($"Unhandled message type '{messageHeader.messageType}'. Ignoring."); |
|||
|
|||
// We don't understand this message, but read it out anyway
|
|||
// so we can process the next message
|
|||
int bytesRemaining = messageHeader.messageSize; |
|||
while (bytesRemaining > 0) |
|||
{ |
|||
bytesRemaining -= m_ReadBuffer.Read(stream, 0, Mathf.Min(bytesRemaining, m_ReadBuffer.bufferSize)); |
|||
} |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
|
|||
void CheckForLocalCollaborationData(ARKitSessionSubsystem subsystem) |
|||
{ |
|||
// Exit if no new data is available
|
|||
if (subsystem.collaborationDataCount == 0) |
|||
return; |
|||
|
|||
lock (m_CollaborationDataSendQueue) |
|||
{ |
|||
// Enqueue all new collaboration data with critical priority
|
|||
while (subsystem.collaborationDataCount > 0) |
|||
{ |
|||
var collaborationData = subsystem.DequeueCollaborationData(); |
|||
|
|||
// As all data in this sample is sent over TCP, only send critical data
|
|||
if (collaborationData.priority == ARCollaborationDataPriority.Critical) |
|||
{ |
|||
m_CollaborationDataSendQueue.Enqueue(collaborationData); |
|||
CollaborationNetworkingIndicator.NotifyHasCollaborationData(); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
unsafe void ProcessRemoteCollaborationData(ARKitSessionSubsystem subsystem) |
|||
{ |
|||
// Check for remote data and apply it
|
|||
lock (m_CollaborationDataReadQueue) |
|||
{ |
|||
while (m_CollaborationDataReadQueue.Count > 0) |
|||
{ |
|||
using (var collaborationData = m_CollaborationDataReadQueue.Dequeue()) |
|||
{ |
|||
// Assume we only put in valid collaboration data into the queue.
|
|||
subsystem.UpdateWithCollaborationData(collaborationData); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
const int k_BufferSize = 10240; |
|||
|
|||
NetworkBuffer m_ReadBuffer = new NetworkBuffer(k_BufferSize); |
|||
|
|||
NetworkBuffer m_WriteBuffer = new NetworkBuffer(k_BufferSize); |
|||
|
|||
MessageHeader ReadMessageHeader(NetworkStream stream) |
|||
{ |
|||
int bytesRead = m_ReadBuffer.Read(stream, 0, MessageHeader.k_EncodedSize); |
|||
return new MessageHeader(m_ReadBuffer.buffer, bytesRead); |
|||
} |
|||
|
|||
ARCollaborationData ReadCollaborationData(NetworkStream stream, int size) |
|||
{ |
|||
var builder = new ARCollaborationDataBuilder(); |
|||
try |
|||
{ |
|||
int bytesRemaining = size; |
|||
while (bytesRemaining > 0) |
|||
{ |
|||
int bytesRead = m_ReadBuffer.Read(stream, 0, Mathf.Min(bytesRemaining, m_ReadBuffer.bufferSize)); |
|||
builder.Append(m_ReadBuffer.buffer, 0, bytesRead); |
|||
bytesRemaining -= bytesRead; |
|||
} |
|||
|
|||
return builder.ToCollaborationData(); |
|||
} |
|||
finally |
|||
{ |
|||
builder.Dispose(); |
|||
} |
|||
} |
|||
#endif
|
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 5a4eb4ee5b17a4cafa6d0e5cdb157fde |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using System; |
|||
using System.Collections; |
|||
using System.Collections.Generic; |
|||
using System.Net; |
|||
using System.Net.Sockets; |
|||
using System.Text; |
|||
using System.Threading; |
|||
using UnityEngine; |
|||
using Unity.Collections; |
|||
|
|||
public class TCPServer : TCPConnection |
|||
{ |
|||
TcpListener m_TcpListener; |
|||
|
|||
protected override void OnEnable() |
|||
{ |
|||
base.OnEnable(); |
|||
|
|||
m_TcpListener = new TcpListener(IPAddress.Any, port); |
|||
m_TcpListener.Start(); |
|||
Logger.Log($"Listening for connection on port {port}..."); |
|||
} |
|||
|
|||
protected override void Update() |
|||
{ |
|||
if (m_TcpClient == null && m_TcpListener.Pending()) |
|||
{ |
|||
Logger.Log("Connection pending..."); |
|||
m_TcpClient = m_TcpListener.AcceptTcpClient(); |
|||
Logger.Log($"Connection established. {((IPEndPoint)m_TcpClient.Client.RemoteEndPoint).Address}"); |
|||
} |
|||
|
|||
base.Update(); |
|||
} |
|||
|
|||
protected override void OnDisable() |
|||
{ |
|||
base.OnDisable(); |
|||
|
|||
m_TcpListener.Stop(); |
|||
m_TcpListener = null; |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 6fc24ee8011574921a7d764defd91305 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
public interface IMessage |
|||
{ |
|||
int EncodeTo(byte[] bytes); |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 48716d5221a70425aa3409ef5a24a10d |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
public struct MessageHeader : IMessage |
|||
{ |
|||
public int messageSize; |
|||
|
|||
public MessageType messageType; |
|||
|
|||
public int EncodeTo(byte[] bytes) |
|||
{ |
|||
var encoder = new NetworkDataEncoder(bytes); |
|||
encoder.Encode(messageSize); |
|||
encoder.Encode((byte)messageType); |
|||
return encoder.length; |
|||
} |
|||
|
|||
public const int k_EncodedSize = sizeof(int) + sizeof(byte); |
|||
|
|||
public MessageHeader(byte[] bytes, int size) |
|||
{ |
|||
var decoder = new NetworkDataDecoder(bytes, size); |
|||
messageSize = decoder.DecodeInt(); |
|||
messageType = (MessageType)decoder.DecodeByte(); |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 392beb324d6f3400ab0b5ffe1ae43b70 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
public enum MessageType : byte |
|||
{ |
|||
None, |
|||
|
|||
CollaborationData |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: b16d841d0e5dd48d0a70c79beff77499 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using System; |
|||
using System.Net.Sockets; |
|||
using UnityEngine; |
|||
using Unity.Collections; |
|||
using Unity.Collections.LowLevel.Unsafe; |
|||
|
|||
/// <summary>
|
|||
/// Sends and receives data using a fixed size byte[] buffer. Because the
|
|||
/// buffer is reused, no additional GC allocations are made after construction.
|
|||
/// </summary>
|
|||
public struct NetworkBuffer |
|||
{ |
|||
byte[] m_Buffer; |
|||
|
|||
public NetworkBuffer(int bufferSize) |
|||
{ |
|||
m_Buffer = new byte[bufferSize]; |
|||
} |
|||
|
|||
public byte[] buffer => m_Buffer; |
|||
|
|||
public int bufferSize => (m_Buffer == null) ? 0 : m_Buffer.Length; |
|||
|
|||
public int Read(NetworkStream stream, int offset, int size) |
|||
{ |
|||
ValidateAndThrow(stream); |
|||
|
|||
if (offset < 0) |
|||
throw new ArgumentOutOfRangeException(nameof(offset), offset, $"{nameof(offset)} must be greater than or equal to zero."); |
|||
|
|||
if (size < 0) |
|||
throw new ArgumentOutOfRangeException(nameof(size), size, $"{nameof(size)} must be greater than or equal to zero."); |
|||
|
|||
if (offset + size > m_Buffer.Length) |
|||
throw new InvalidOperationException($"Reading {size} bytes starting at offset {offset} would read past the end of the buffer (buffer length = {m_Buffer.Length})."); |
|||
|
|||
int bytesRemaining = size; |
|||
while (bytesRemaining > 0) |
|||
{ |
|||
int bytesRead = stream.Read(m_Buffer, offset, bytesRemaining); |
|||
CollaborationNetworkingIndicator.NotifyIncomingDataReceived(); |
|||
offset += bytesRead; |
|||
bytesRemaining -= bytesRead; |
|||
} |
|||
|
|||
return size; |
|||
} |
|||
|
|||
public void Send(NetworkStream stream, int offset, int size) |
|||
{ |
|||
ValidateAndThrow(stream); |
|||
|
|||
if (offset + size > m_Buffer.Length) |
|||
throw new InvalidOperationException($"Writing {size} bytes starting at offset {offset} would write past the end of the buffer (buffer length = {m_Buffer.Length})."); |
|||
|
|||
try |
|||
{ |
|||
stream.Write(m_Buffer, offset, size); |
|||
CollaborationNetworkingIndicator.NotifyOutgoingDataSent(); |
|||
} |
|||
catch (SocketException socketException) |
|||
{ |
|||
Logger.Log($"Socket exception: {socketException}"); |
|||
} |
|||
} |
|||
|
|||
public unsafe void Send(NetworkStream stream, NativeSlice<byte> bytes) |
|||
{ |
|||
ValidateAndThrow(stream); |
|||
|
|||
var basePtr = new IntPtr(bytes.GetUnsafeReadOnlyPtr()); |
|||
int bytesRemaining = bytes.Length; |
|||
int offset = 0; |
|||
|
|||
while (bytesRemaining > 0) |
|||
{ |
|||
// Memcpy next chunk into destinationBuffer
|
|||
int size = Mathf.Min(m_Buffer.Length, bytesRemaining); |
|||
fixed(byte* dst = m_Buffer) |
|||
{ |
|||
var src = basePtr + offset; |
|||
UnsafeUtility.MemCpy(dst, (void*)src, size); |
|||
} |
|||
|
|||
bytesRemaining -= size; |
|||
offset += size; |
|||
|
|||
Send(stream, 0, size); |
|||
} |
|||
} |
|||
|
|||
public void Send<T>(NetworkStream stream, T message) where T : struct, IMessage |
|||
{ |
|||
ValidateAndThrow(stream); |
|||
|
|||
int size = message.EncodeTo(m_Buffer); |
|||
Send(stream, 0, size); |
|||
} |
|||
|
|||
void ValidateAndThrow(NetworkStream stream) |
|||
{ |
|||
if (stream == null) |
|||
throw new ArgumentNullException(nameof(stream)); |
|||
|
|||
if (m_Buffer == null) |
|||
throw new InvalidOperationException($"{nameof(NetworkBuffer)} has not been initialized."); |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 6159b84b308ec4356bdc9ae17d26964b |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using System; |
|||
using System.Net; |
|||
|
|||
public struct NetworkDataDecoder |
|||
{ |
|||
byte[] m_Buffer; |
|||
|
|||
int m_Offset; |
|||
|
|||
int m_Length; |
|||
|
|||
public NetworkDataDecoder(byte[] buffer, int size) |
|||
{ |
|||
if (buffer == null) |
|||
throw new ArgumentNullException(nameof(buffer)); |
|||
|
|||
if (size > buffer.Length) |
|||
throw new ArgumentOutOfRangeException(nameof(size), size, $"'{nameof(size)}' is greater than the length of {nameof(buffer)} ({buffer.Length})."); |
|||
|
|||
m_Buffer = buffer; |
|||
m_Offset = 0; |
|||
m_Length = size; |
|||
} |
|||
|
|||
public unsafe float DecodeFloat() |
|||
{ |
|||
var value = DecodeInt(); |
|||
return *(float*)&value; |
|||
} |
|||
|
|||
public unsafe double DecodeDouble() |
|||
{ |
|||
var value = DecodeLong(); |
|||
return *(double*)&value; |
|||
} |
|||
|
|||
public ushort DecodeUShort() => (ushort)DecodeShort(); |
|||
|
|||
public uint DecodeUInt() => (uint)DecodeInt(); |
|||
|
|||
public ulong DecodeULong() => (ulong)DecodeLong(); |
|||
|
|||
public byte DecodeByte() |
|||
{ |
|||
if (m_Offset >= m_Length) |
|||
throw new InvalidOperationException("Buffer is exhausted. Cannot decode more data."); |
|||
|
|||
return m_Buffer[m_Offset++]; |
|||
} |
|||
|
|||
public unsafe short DecodeShort() |
|||
{ |
|||
if (m_Offset + 2 > m_Length) |
|||
throw new InvalidOperationException("Buffer is exhausted. Cannot decode more data."); |
|||
|
|||
fixed(byte* ptr = &m_Buffer[m_Offset]) |
|||
{ |
|||
m_Offset += 2; |
|||
return IPAddress.NetworkToHostOrder(*(short*)ptr); |
|||
} |
|||
} |
|||
|
|||
public unsafe int DecodeInt() |
|||
{ |
|||
if (m_Offset + 4 > m_Length) |
|||
throw new InvalidOperationException("Buffer is exhausted. Cannot decode more data."); |
|||
|
|||
fixed(byte* ptr = &m_Buffer[m_Offset]) |
|||
{ |
|||
m_Offset += 4; |
|||
return IPAddress.NetworkToHostOrder(*(int*)ptr); |
|||
} |
|||
} |
|||
|
|||
public unsafe long DecodeLong() |
|||
{ |
|||
if (m_Offset + 8 > m_Length) |
|||
throw new InvalidOperationException("Buffer is exhausted. Cannot decode more data."); |
|||
|
|||
fixed(byte* ptr = &m_Buffer[m_Offset]) |
|||
{ |
|||
m_Offset += 8; |
|||
return IPAddress.NetworkToHostOrder(*(long*)ptr); |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: d3331be3e25f44589a8a9895cdb60f02 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using System; |
|||
using System.Net; |
|||
|
|||
public struct NetworkDataEncoder |
|||
{ |
|||
byte[] m_Buffer; |
|||
|
|||
int m_Offset; |
|||
|
|||
public NetworkDataEncoder(byte[] buffer) |
|||
{ |
|||
if (buffer == null) |
|||
throw new ArgumentNullException(nameof(buffer)); |
|||
|
|||
m_Buffer = buffer; |
|||
m_Offset = 0; |
|||
} |
|||
|
|||
public int length => m_Offset; |
|||
|
|||
public unsafe void Encode(float value) => Encode(*(int*)&value); |
|||
|
|||
public unsafe void Encode(double value) => Encode(*(long*)&value); |
|||
|
|||
public void Encode(ushort value) => Encode((short)value); |
|||
|
|||
public void Encode(uint value) => Encode((int)value); |
|||
|
|||
public void Encode(ulong value) => Encode((long)value); |
|||
|
|||
public void Encode(byte value) |
|||
{ |
|||
if (m_Offset + 1 > m_Buffer.Length) |
|||
throw new InvalidOperationException("Buffer is full. Cannot write more data."); |
|||
|
|||
m_Buffer[m_Offset++] = value; |
|||
} |
|||
|
|||
public unsafe void Encode(short value) |
|||
{ |
|||
int newOffset = m_Offset + 2; |
|||
if (newOffset > m_Buffer.Length) |
|||
throw new InvalidOperationException("Buffer is full. Cannot write more data."); |
|||
|
|||
fixed(byte* ptr = &m_Buffer[m_Offset]) |
|||
{ |
|||
*(short*)ptr = IPAddress.HostToNetworkOrder(value); |
|||
} |
|||
m_Offset = newOffset; |
|||
} |
|||
|
|||
public unsafe void Encode(int value) |
|||
{ |
|||
int newOffset = m_Offset + 4; |
|||
if (newOffset > m_Buffer.Length) |
|||
throw new InvalidOperationException("Buffer is full. Cannot write more data."); |
|||
|
|||
fixed(byte* ptr = &m_Buffer[m_Offset]) |
|||
{ |
|||
*(int*)ptr = IPAddress.HostToNetworkOrder(value); |
|||
} |
|||
m_Offset = newOffset; |
|||
} |
|||
|
|||
public unsafe void Encode(long value) |
|||
{ |
|||
int newOffset = m_Offset + 8; |
|||
if (newOffset > m_Buffer.Length) |
|||
throw new InvalidOperationException("Buffer is full. Cannot write more data."); |
|||
|
|||
fixed(byte* ptr = &m_Buffer[m_Offset]) |
|||
{ |
|||
*(long*)ptr = IPAddress.HostToNetworkOrder(value); |
|||
} |
|||
m_Offset = newOffset; |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 03435c1a2d82b46f98fe7980c199a46f |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
撰写
预览
正在加载...
取消
保存
Reference in new issue