您最多选择25个主题
主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
315 行
9.4 KiB
315 行
9.4 KiB
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
|
|
}
|