浏览代码

Merge pull request #14 from unity/multipeer

Use Multipeer Connectivity for collaborative sessions sample
/3.1
GitHub Enterprise 5 年前
当前提交
c81f5e11
共有 53 个文件被更改,包括 1202 次插入1725 次删除
  1. 811
      Assets/Scenes/ARCollaborationData/ARCollaborationDataExample.unity
  2. 141
      Assets/Scenes/ARCollaborationData/CollaborativeSession.cs
  3. 11
      Assets/Scenes/ARCollaborationData/CollaborativeSession.cs.meta
  4. 8
      Assets/Scripts/Multipeer.meta
  5. 100
      Assets/Scripts/Multipeer/MCSession.cs
  6. 11
      Assets/Scripts/Multipeer/MCSession.cs.meta
  7. 18
      Assets/Scripts/Multipeer/MCSessionSendDataMode.cs
  8. 11
      Assets/Scripts/Multipeer/MCSessionSendDataMode.cs.meta
  9. 68
      Assets/Scripts/Multipeer/NSData.cs
  10. 11
      Assets/Scripts/Multipeer/NSData.cs.meta
  11. 56
      Assets/Scripts/Multipeer/NSError.cs
  12. 11
      Assets/Scripts/Multipeer/NSError.cs.meta
  13. 18
      Assets/Scripts/Multipeer/NSErrorException.cs
  14. 11
      Assets/Scripts/Multipeer/NSErrorException.cs.meta
  15. 80
      Assets/Scripts/Multipeer/NSString.cs
  16. 11
      Assets/Scripts/Multipeer/NSString.cs.meta
  17. 17
      Assets/Scripts/Multipeer/NativeApi.cs
  18. 11
      Assets/Scripts/Multipeer/NativeApi.cs.meta
  19. 8
      Assets/Scripts/Multipeer/NativeCode.meta
  20. 37
      Assets/Scripts/Multipeer/NativeCode/MultipeerDelegate-C-Bridge.m.meta
  21. 13
      Assets/Scripts/Multipeer/NativeCode/MultipeerDelegate.h
  22. 27
      Assets/Scripts/Multipeer/NativeCode/MultipeerDelegate.h.meta
  23. 142
      Assets/Scripts/Multipeer/NativeCode/MultipeerDelegate.m
  24. 105
      Assets/Scripts/Multipeer/NativeCode/MultipeerDelegate.m.meta
  25. 30
      Assets/Scripts/Multipeer/NativeCode/NSData-C-Bridge.m
  26. 37
      Assets/Scripts/Multipeer/NativeCode/NSData-C-Bridge.m.meta
  27. 12
      Assets/Scripts/Multipeer/NativeCode/NSError-C-Bridge.m
  28. 37
      Assets/Scripts/Multipeer/NativeCode/NSError-C-Bridge.m.meta
  29. 52
      Assets/Scripts/Multipeer/NativeCode/NSString-C-Bridge.m
  30. 37
      Assets/Scripts/Multipeer/NativeCode/NSString-C-Bridge.m.meta
  31. 60
      Assets/Scripts/Multipeer/NativeCode/MultipeerDelegate-C-Bridge.m
  32. 103
      Assets/Scenes/ARCollaborationData/ClientServerSelector.cs
  33. 11
      Assets/Scenes/ARCollaborationData/ClientServerSelector.cs.meta
  34. 50
      Assets/Scenes/ARCollaborationData/TCPClient.cs
  35. 11
      Assets/Scenes/ARCollaborationData/TCPClient.cs.meta
  36. 315
      Assets/Scenes/ARCollaborationData/TCPConnection.cs
  37. 11
      Assets/Scenes/ARCollaborationData/TCPConnection.cs.meta
  38. 43
      Assets/Scenes/ARCollaborationData/TCPServer.cs
  39. 11
      Assets/Scenes/ARCollaborationData/TCPServer.cs.meta
  40. 4
      Assets/Scenes/ARCollaborationData/IMessage.cs
  41. 11
      Assets/Scenes/ARCollaborationData/IMessage.cs.meta
  42. 23
      Assets/Scenes/ARCollaborationData/MessageHeader.cs
  43. 11
      Assets/Scenes/ARCollaborationData/MessageHeader.cs.meta
  44. 6
      Assets/Scenes/ARCollaborationData/MessageType.cs
  45. 11
      Assets/Scenes/ARCollaborationData/MessageType.cs.meta
  46. 108
      Assets/Scenes/ARCollaborationData/NetworkBuffer.cs
  47. 11
      Assets/Scenes/ARCollaborationData/NetworkBuffer.cs.meta
  48. 86
      Assets/Scenes/ARCollaborationData/NetworkDataDecoder.cs
  49. 11
      Assets/Scenes/ARCollaborationData/NetworkDataDecoder.cs.meta
  50. 77
      Assets/Scenes/ARCollaborationData/NetworkDataEncoder.cs
  51. 11
      Assets/Scenes/ARCollaborationData/NetworkDataEncoder.cs.meta

811
Assets/Scenes/ARCollaborationData/ARCollaborationDataExample.unity


debug:
m_Flags: 0
m_NavMeshData: {fileID: 0}
--- !u!1 &258687577
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 258687578}
- component: {fileID: 258687580}
- component: {fileID: 258687579}
m_Layer: 5
m_Name: Text
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &258687578
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 258687577}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 2093015781}
m_RootOrder: 1
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 1, y: 1}
m_AnchoredPosition: {x: 0, y: -0.5}
m_SizeDelta: {x: -20, y: -13}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!114 &258687579
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 258687577}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 708705254, guid: f70555f144d8491a825f0804e09c671c, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1}
m_RaycastTarget: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI,
Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
m_FontData:
m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0}
m_FontSize: 50
m_FontStyle: 0
m_BestFit: 0
m_MinSize: 3
m_MaxSize: 50
m_Alignment: 0
m_AlignByGeometry: 0
m_RichText: 0
m_HorizontalOverflow: 1
m_VerticalOverflow: 0
m_LineSpacing: 1
m_Text:
--- !u!222 &258687580
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 258687577}
m_CullTransparentMesh: 0
--- !u!1 &275151379
GameObject:
m_ObjectHideFlags: 0

m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 275151379}
m_CullTransparentMesh: 0
--- !u!1 &287071174
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 287071175}
- component: {fileID: 287071177}
- component: {fileID: 287071176}
m_Layer: 5
m_Name: Text
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &287071175
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 287071174}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 1550501584}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 1, y: 1}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 0, y: 0}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!114 &287071176
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 287071174}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 708705254, guid: f70555f144d8491a825f0804e09c671c, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1}
m_RaycastTarget: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI,
Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
m_FontData:
m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0}
m_FontSize: 80
m_FontStyle: 0
m_BestFit: 0
m_MinSize: 0
m_MaxSize: 80
m_Alignment: 4
m_AlignByGeometry: 0
m_RichText: 1
m_HorizontalOverflow: 0
m_VerticalOverflow: 0
m_LineSpacing: 1
m_Text: Host
--- !u!222 &287071177
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 287071174}
m_CullTransparentMesh: 0
--- !u!1 &411184562
GameObject:
m_ObjectHideFlags: 0

m_Name:
m_EditorClassIdentifier:
m_Session: {fileID: 1297001902}
--- !u!1 &498795603
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 498795604}
- component: {fileID: 498795606}
- component: {fileID: 498795605}
m_Layer: 5
m_Name: Text
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &498795604
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 498795603}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 1342067404}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 1, y: 1}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 0, y: 0}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!114 &498795605
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 498795603}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 708705254, guid: f70555f144d8491a825f0804e09c671c, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1}
m_RaycastTarget: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI,
Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
m_FontData:
m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0}
m_FontSize: 80
m_FontStyle: 0
m_BestFit: 0
m_MinSize: 0
m_MaxSize: 80
m_Alignment: 4
m_AlignByGeometry: 0
m_RichText: 1
m_HorizontalOverflow: 0
m_VerticalOverflow: 0
m_LineSpacing: 1
m_Text: Join
--- !u!222 &498795606
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 498795603}
m_CullTransparentMesh: 0
--- !u!1 &712737948
GameObject:
m_ObjectHideFlags: 0

m_LocalScale: {x: 0, y: 0, z: 0}
m_Children:
- {fileID: 1765577807}
- {fileID: 1342067404}
- {fileID: 1550501584}
- {fileID: 2093015781}
- {fileID: 2129958116}
- {fileID: 1989065127}
- {fileID: 2051619927}

m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 6
m_RootOrder: 5
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &908220284
MonoBehaviour:

m_Children:
- {fileID: 712737949}
m_Father: {fileID: 831156125}
m_RootOrder: 3
m_RootOrder: 2
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 1, y: 0}
m_AnchorMax: {x: 1, y: 0}

m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 935465590}
m_CullTransparentMesh: 0
--- !u!1 &988418385
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 988418386}
- component: {fileID: 988418388}
- component: {fileID: 988418387}
m_Layer: 5
m_Name: Placeholder
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &988418386
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 988418385}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 2093015781}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 1, y: 1}
m_AnchoredPosition: {x: 0, y: -0.5}
m_SizeDelta: {x: -20, y: -13}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!114 &988418387
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 988418385}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 708705254, guid: f70555f144d8491a825f0804e09c671c, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 0.5}
m_RaycastTarget: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI,
Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
m_FontData:
m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0}
m_FontSize: 50
m_FontStyle: 2
m_BestFit: 0
m_MinSize: 0
m_MaxSize: 50
m_Alignment: 0
m_AlignByGeometry: 0
m_RichText: 1
m_HorizontalOverflow: 0
m_VerticalOverflow: 0
m_LineSpacing: 1
m_Text: Enter IP Address...
--- !u!222 &988418388
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 988418385}
m_CullTransparentMesh: 0
--- !u!1 &1297001900
GameObject:
m_ObjectHideFlags: 0

- component: {fileID: 1297001902}
- component: {fileID: 1297001901}
- component: {fileID: 1297001904}
- component: {fileID: 1297001905}
m_Layer: 0
m_Name: AR Session
m_TagString: Untagged

m_Name:
m_EditorClassIdentifier:
m_Text: {fileID: 1888598222}
--- !u!1 &1342067403
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1342067404}
- component: {fileID: 1342067407}
- component: {fileID: 1342067406}
- component: {fileID: 1342067405}
m_Layer: 5
m_Name: Join
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &1342067404
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1342067403}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children:
- {fileID: 498795604}
m_Father: {fileID: 831156125}
m_RootOrder: 1
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 1, y: 0}
m_AnchorMax: {x: 1, y: 0}
m_AnchoredPosition: {x: -32, y: 460}
m_SizeDelta: {x: 417.54, y: 140}
m_Pivot: {x: 1, y: 0}
--- !u!114 &1342067405
--- !u!114 &1297001905
m_GameObject: {fileID: 1342067403}
m_GameObject: {fileID: 1297001900}
m_Script: {fileID: 1392445389, guid: f70555f144d8491a825f0804e09c671c, type: 3}
m_Script: {fileID: 11500000, guid: a7c1d01ad3d754ef788a30c12b724743, type: 3}
m_Navigation:
m_Mode: 3
m_SelectOnUp: {fileID: 0}
m_SelectOnDown: {fileID: 0}
m_SelectOnLeft: {fileID: 0}
m_SelectOnRight: {fileID: 0}
m_Transition: 1
m_Colors:
m_NormalColor: {r: 1, g: 1, b: 1, a: 1}
m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1}
m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1}
m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1}
m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608}
m_ColorMultiplier: 1
m_FadeDuration: 0.1
m_SpriteState:
m_HighlightedSprite: {fileID: 0}
m_PressedSprite: {fileID: 0}
m_SelectedSprite: {fileID: 0}
m_DisabledSprite: {fileID: 0}
m_AnimationTriggers:
m_NormalTrigger: Normal
m_HighlightedTrigger: Highlighted
m_PressedTrigger: Pressed
m_SelectedTrigger: Highlighted
m_DisabledTrigger: Disabled
m_Interactable: 1
m_TargetGraphic: {fileID: 1342067406}
m_OnClick:
m_PersistentCalls:
m_Calls:
- m_Target: {fileID: 1615366624}
m_MethodName: Join
m_Mode: 1
m_Arguments:
m_ObjectArgument: {fileID: 0}
m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine
m_IntArgument: 0
m_FloatArgument: 0
m_StringArgument:
m_BoolArgument: 0
m_CallState: 2
m_TypeName: UnityEngine.UI.Button+ButtonClickedEvent, UnityEngine.UI, Version=1.0.0.0,
Culture=neutral, PublicKeyToken=null
--- !u!114 &1342067406
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1342067403}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: -765806418, guid: f70555f144d8491a825f0804e09c671c, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI,
Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0}
m_Type: 1
m_PreserveAspect: 0
m_FillCenter: 1
m_FillMethod: 4
m_FillAmount: 1
m_FillClockwise: 1
m_FillOrigin: 0
m_UseSpriteMesh: 0
--- !u!222 &1342067407
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1342067403}
m_CullTransparentMesh: 0
--- !u!1 &1550501583
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1550501584}
- component: {fileID: 1550501587}
- component: {fileID: 1550501586}
- component: {fileID: 1550501585}
m_Layer: 5
m_Name: Host
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &1550501584
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1550501583}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children:
- {fileID: 287071175}
m_Father: {fileID: 831156125}
m_RootOrder: 4
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 1, y: 0}
m_AnchorMax: {x: 1, y: 0}
m_AnchoredPosition: {x: -32, y: 663}
m_SizeDelta: {x: 417.54, y: 140}
m_Pivot: {x: 1, y: 0}
--- !u!114 &1550501585
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1550501583}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 1392445389, guid: f70555f144d8491a825f0804e09c671c, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Navigation:
m_Mode: 3
m_SelectOnUp: {fileID: 0}
m_SelectOnDown: {fileID: 0}
m_SelectOnLeft: {fileID: 0}
m_SelectOnRight: {fileID: 0}
m_Transition: 1
m_Colors:
m_NormalColor: {r: 1, g: 1, b: 1, a: 1}
m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1}
m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1}
m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1}
m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608}
m_ColorMultiplier: 1
m_FadeDuration: 0.1
m_SpriteState:
m_HighlightedSprite: {fileID: 0}
m_PressedSprite: {fileID: 0}
m_SelectedSprite: {fileID: 0}
m_DisabledSprite: {fileID: 0}
m_AnimationTriggers:
m_NormalTrigger: Normal
m_HighlightedTrigger: Highlighted
m_PressedTrigger: Pressed
m_SelectedTrigger: Highlighted
m_DisabledTrigger: Disabled
m_Interactable: 1
m_TargetGraphic: {fileID: 1550501586}
m_OnClick:
m_PersistentCalls:
m_Calls:
- m_Target: {fileID: 1615366624}
m_MethodName: Host
m_Mode: 1
m_Arguments:
m_ObjectArgument: {fileID: 0}
m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine
m_IntArgument: 0
m_FloatArgument: 0
m_StringArgument:
m_BoolArgument: 0
m_CallState: 2
m_TypeName: UnityEngine.UI.Button+ButtonClickedEvent, UnityEngine.UI, Version=1.0.0.0,
Culture=neutral, PublicKeyToken=null
--- !u!114 &1550501586
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1550501583}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: -765806418, guid: f70555f144d8491a825f0804e09c671c, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI,
Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0}
m_Type: 1
m_PreserveAspect: 0
m_FillCenter: 1
m_FillMethod: 4
m_FillAmount: 1
m_FillClockwise: 1
m_FillOrigin: 0
m_UseSpriteMesh: 0
--- !u!222 &1550501587
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1550501583}
m_CullTransparentMesh: 0
m_ServiceType: arf-collab-demo
--- !u!1 &1564081113
GameObject:
m_ObjectHideFlags: 0

m_Father: {fileID: 411184564}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &1615366623
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1615366627}
- component: {fileID: 1615366626}
- component: {fileID: 1615366625}
- component: {fileID: 1615366624}
m_Layer: 0
m_Name: Networking
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!114 &1615366624
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1615366623}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: bbcf44277865e45d399575001e42f155, type: 3}
m_Name:
m_EditorClassIdentifier:
m_JoinButton: {fileID: 1342067405}
m_HostButton: {fileID: 1550501585}
m_IPAddressField: {fileID: 2093015782}
--- !u!114 &1615366625
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1615366623}
m_Enabled: 0
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 6fc24ee8011574921a7d764defd91305, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Session: {fileID: 1297001902}
m_Port: 8503
--- !u!114 &1615366626
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1615366623}
m_Enabled: 0
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: a4bbe7d8f963c43d7b710873f90b9b2d, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Session: {fileID: 1297001902}
m_Port: 8503
--- !u!4 &1615366627
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1615366623}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 5
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &1634621700
GameObject:
m_ObjectHideFlags: 0

m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 831156125}
m_RootOrder: 2
m_RootOrder: 1
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 1, y: 1}
m_AnchorMax: {x: 1, y: 1}

m_Children:
- {fileID: 1634621701}
m_Father: {fileID: 831156125}
m_RootOrder: 7
m_RootOrder: 4
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 1, y: 1}
m_AnchorMax: {x: 1, y: 1}

m_Children:
- {fileID: 275151380}
m_Father: {fileID: 831156125}
m_RootOrder: 8
m_RootOrder: 5
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 1, y: 1}
m_AnchorMax: {x: 1, y: 1}

m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2051619926}
m_CullTransparentMesh: 0
--- !u!1 &2093015780
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 2093015781}
- component: {fileID: 2093015784}
- component: {fileID: 2093015783}
- component: {fileID: 2093015782}
m_Layer: 5
m_Name: IPAddress
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &2093015781
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2093015780}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children:
- {fileID: 988418386}
- {fileID: 258687578}
m_Father: {fileID: 831156125}
m_RootOrder: 5
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 1, y: 0}
m_AnchorMax: {x: 1, y: 0}
m_AnchoredPosition: {x: -471, y: 460}
m_SizeDelta: {x: 417.54004, y: 140}
m_Pivot: {x: 1, y: 0}
--- !u!114 &2093015782
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2093015780}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 575553740, guid: f70555f144d8491a825f0804e09c671c, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Navigation:
m_Mode: 3
m_SelectOnUp: {fileID: 0}
m_SelectOnDown: {fileID: 0}
m_SelectOnLeft: {fileID: 0}
m_SelectOnRight: {fileID: 0}
m_Transition: 1
m_Colors:
m_NormalColor: {r: 1, g: 1, b: 1, a: 1}
m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1}
m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1}
m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1}
m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608}
m_ColorMultiplier: 1
m_FadeDuration: 0.1
m_SpriteState:
m_HighlightedSprite: {fileID: 0}
m_PressedSprite: {fileID: 0}
m_SelectedSprite: {fileID: 0}
m_DisabledSprite: {fileID: 0}
m_AnimationTriggers:
m_NormalTrigger: Normal
m_HighlightedTrigger: Highlighted
m_PressedTrigger: Pressed
m_SelectedTrigger: Selected
m_DisabledTrigger: Disabled
m_Interactable: 1
m_TargetGraphic: {fileID: 2093015783}
m_TextComponent: {fileID: 258687579}
m_Placeholder: {fileID: 988418387}
m_ContentType: 0
m_InputType: 0
m_AsteriskChar: 42
m_KeyboardType: 0
m_LineType: 0
m_HideMobileInput: 0
m_CharacterValidation: 0
m_CharacterLimit: 0
m_OnEndEdit:
m_PersistentCalls:
m_Calls: []
m_TypeName: UnityEngine.UI.InputField+SubmitEvent, UnityEngine.UI, Version=1.0.0.0,
Culture=neutral, PublicKeyToken=null
m_OnValueChanged:
m_PersistentCalls:
m_Calls: []
m_TypeName: UnityEngine.UI.InputField+OnChangeEvent, UnityEngine.UI, Version=1.0.0.0,
Culture=neutral, PublicKeyToken=null
m_CaretColor: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1}
m_CustomCaretColor: 0
m_SelectionColor: {r: 0.65882355, g: 0.80784315, b: 1, a: 0.7529412}
m_Text:
m_CaretBlinkRate: 0.85
m_CaretWidth: 1
m_ReadOnly: 0
--- !u!114 &2093015783
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2093015780}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: -765806418, guid: f70555f144d8491a825f0804e09c671c, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI,
Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
m_Sprite: {fileID: 10911, guid: 0000000000000000f000000000000000, type: 0}
m_Type: 1
m_PreserveAspect: 0
m_FillCenter: 1
m_FillMethod: 4
m_FillAmount: 1
m_FillClockwise: 1
m_FillOrigin: 0
m_UseSpriteMesh: 0
--- !u!222 &2093015784
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2093015780}
m_CullTransparentMesh: 0
--- !u!1 &2129958115
GameObject:
m_ObjectHideFlags: 0

m_Children:
- {fileID: 1692479330}
m_Father: {fileID: 831156125}
m_RootOrder: 6
m_RootOrder: 3
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 1, y: 1}
m_AnchorMax: {x: 1, y: 1}

141
Assets/Scenes/ARCollaborationData/CollaborativeSession.cs


using UnityEngine;
using UnityEngine.XR.ARFoundation;
#if UNITY_IOS && !UNITY_EDITOR
using Unity.iOS.Multipeer;
using UnityEngine.XR.ARKit;
#endif
[RequireComponent(typeof(ARSession))]
public class CollaborativeSession : MonoBehaviour
{
[SerializeField]
[Tooltip("The name for this network service. It should be 15 characters or less and can contain ASCII, lowercase letters, numbers, and hyphens.")]
string m_ServiceType;
/// <summary>
/// The name for this network service.
/// See <a href="https://developer.apple.com/documentation/multipeerconnectivity/mcnearbyserviceadvertiser">MCNearbyServiceAdvertiser</a>
/// for the purpose of and restrictions on this name.
/// </summary>
public string serviceType
{
get { return m_ServiceType; }
set { m_ServiceType = value; }
}
ARSession m_ARSession;
void DisableNotSupported(string reason)
{
enabled = false;
Logger.Log(reason);
}
void OnEnable()
{
#if UNITY_IOS && !UNITY_EDITOR
var subsystem = GetSubsystem();
if (!ARKitSessionSubsystem.supportsCollaboration || subsystem == null)
{
DisableNotSupported("Collaborative sessions require iOS 13.");
return;
}
subsystem.collaborationEnabled = true;
m_MCSession.Enabled = true;
#else
DisableNotSupported("Collaborative sessions are an ARKit 3 feature; This platform does not support them.");
#endif
}
#if UNITY_IOS && !UNITY_EDITOR
MCSession m_MCSession;
ARKitSessionSubsystem GetSubsystem()
{
if (m_ARSession == null)
return null;
return m_ARSession.subsystem as ARKitSessionSubsystem;
}
void Awake()
{
m_ARSession = GetComponent<ARSession>();
m_MCSession = new MCSession(SystemInfo.deviceName, m_ServiceType);
}
void OnDisable()
{
m_MCSession.Enabled = false;
var subsystem = GetSubsystem();
if (subsystem != null)
subsystem.collaborationEnabled = false;
}
void Update()
{
var subsystem = GetSubsystem();
if (subsystem == null)
return;
// Check for new collaboration data
while (subsystem.collaborationDataCount > 0)
{
using (var collaborationData = subsystem.DequeueCollaborationData())
{
CollaborationNetworkingIndicator.NotifyHasCollaborationData();
if (m_MCSession.ConnectedPeerCount == 0)
continue;
using (var serializedData = collaborationData.ToSerialized())
using (var data = NSData.CreateWithBytesNoCopy(serializedData.bytes))
{
m_MCSession.SendToAllPeers(data, collaborationData.priority == ARCollaborationDataPriority.Critical
? MCSessionSendDataMode.Reliable
: MCSessionSendDataMode.Unreliable);
CollaborationNetworkingIndicator.NotifyOutgoingDataSent();
// Only log 'critical' data as 'optional' data tends to come every frame
if (collaborationData.priority == ARCollaborationDataPriority.Critical)
{
Logger.Log($"Sent {data.Length} bytes of collaboration data.");
}
}
}
}
// Check for incoming data
while (m_MCSession.ReceivedDataQueueSize > 0)
{
CollaborationNetworkingIndicator.NotifyIncomingDataReceived();
using (var data = m_MCSession.DequeueReceivedData())
using (var collaborationData = new ARCollaborationData(data.Bytes))
{
if (collaborationData.valid)
{
subsystem.UpdateWithCollaborationData(collaborationData);
if (collaborationData.priority == ARCollaborationDataPriority.Critical)
{
Logger.Log($"Received {data.Bytes.Length} bytes of collaboration data.");
}
}
else
{
Logger.Log($"Received {data.Bytes.Length} bytes from remote, but the collaboration data was not valid.");
}
}
}
}
void OnDestroy()
{
m_MCSession.Dispose();
}
#endif
}

11
Assets/Scenes/ARCollaborationData/CollaborativeSession.cs.meta


fileFormatVersion: 2
guid: a7c1d01ad3d754ef788a30c12b724743
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

8
Assets/Scripts/Multipeer.meta


fileFormatVersion: 2
guid: ab5ca8171efd6403fa3e0f422eccde1e
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

100
Assets/Scripts/Multipeer/MCSession.cs


using System;
using System.Runtime.InteropServices;
namespace Unity.iOS.Multipeer
{
[StructLayout(LayoutKind.Sequential)]
public struct MCSession : IDisposable, IEquatable<MCSession>
{
IntPtr m_Ptr;
public bool Created => m_Ptr != IntPtr.Zero;
public bool Enabled
{
get
{
if (!Created)
return false;
return GetEnabled(this);
}
set
{
if (!Created && value)
throw new InvalidOperationException();
SetEnabled(this, value);
}
}
public MCSession(string peerName, string serviceType)
{
if (peerName == null)
throw new ArgumentNullException(nameof(peerName));
if (serviceType == null)
throw new ArgumentNullException(nameof(serviceType));
using (var peerName_NSString = new NSString(peerName))
using (var serviceType_NSString = new NSString(serviceType))
{
m_Ptr = InitWithName(peerName_NSString, serviceType_NSString);
}
}
public void SendToAllPeers(NSData data, MCSessionSendDataMode mode)
{
if (!Created)
throw new InvalidOperationException($"The {typeof(MCSession).Name} has not been created.");
if (!data.Created)
throw new ArgumentException($"'{nameof(data)}' is not valid.", nameof(data));
using (var error = SendToAllPeers(this, data, mode))
{
if (error.Valid)
throw error.ToException();
}
}
public int ReceivedDataQueueSize => GetReceivedDataQueueSize(this);
public NSData DequeueReceivedData()
{
if (!Created)
throw new InvalidOperationException($"The {typeof(MCSession).Name} has not been created.");
return DequeueReceivedData(this);
}
public int ConnectedPeerCount => GetConnectedPeerCount(this);
public void Dispose() => NativeApi.CFRelease(ref m_Ptr);
public override int GetHashCode() => m_Ptr.GetHashCode();
public override bool Equals(object obj) => (obj is MCSession) && Equals((MCSession)obj);
public bool Equals(MCSession other) => m_Ptr == other.m_Ptr;
public static bool operator==(MCSession lhs, MCSession rhs) => lhs.Equals(rhs);
public static bool operator!=(MCSession lhs, MCSession rhs) => !lhs.Equals(rhs);
[DllImport("__Internal", EntryPoint="UnityMC_Delegate_sendToAllPeers")]
static extern NSError SendToAllPeers(MCSession self, NSData data, MCSessionSendDataMode mode);
[DllImport("__Internal", EntryPoint="UnityMC_Delegate_initWithName")]
static extern IntPtr InitWithName(NSString name, NSString serviceType);
[DllImport("__Internal", EntryPoint="UnityMC_Delegate_receivedDataQueueSize")]
static extern int GetReceivedDataQueueSize(MCSession self);
[DllImport("__Internal", EntryPoint="UnityMC_Delegate_dequeueReceivedData")]
static extern NSData DequeueReceivedData(MCSession self);
[DllImport("__Internal", EntryPoint="UnityMC_Delegate_connectedPeerCount")]
static extern int GetConnectedPeerCount(MCSession self);
[DllImport("__Internal", EntryPoint="UnityMC_Delegate_setEnabled")]
static extern void SetEnabled(MCSession self, bool enabled);
[DllImport("__Internal", EntryPoint="UnityMC_Delegate_getEnabled")]
static extern bool GetEnabled(MCSession self);
}
}

11
Assets/Scripts/Multipeer/MCSession.cs.meta


fileFormatVersion: 2
guid: f0dd3cf9695cb42f7b9ffa20d3a59011
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

18
Assets/Scripts/Multipeer/MCSessionSendDataMode.cs


namespace Unity.iOS.Multipeer
{
/// <summary>
/// MCSession send modes
/// </summary>
public enum MCSessionSendDataMode
{
/// <summary>
/// Guaranteed reliable and in-order delivery.
/// </summary>
Reliable,
/// <summary>
/// Sent immediately without queuing, no guaranteed delivery.
/// </summary>
Unreliable
}
}

11
Assets/Scripts/Multipeer/MCSessionSendDataMode.cs.meta


fileFormatVersion: 2
guid: 5673d45a5dae34743841658416feef52
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

68
Assets/Scripts/Multipeer/NSData.cs


using System;
using System.Runtime.InteropServices;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
namespace Unity.iOS.Multipeer
{
[StructLayout(LayoutKind.Sequential)]
public struct NSData : IDisposable, IEquatable<NSData>
{
IntPtr m_Ptr;
internal NSData(IntPtr existing) => m_Ptr = existing;
public bool Created => m_Ptr != IntPtr.Zero;
public int Length => Created ? GetLength(this) : 0;
public static unsafe NSData CreateWithBytes(NativeSlice<byte> bytes)
{
var ptr = bytes.GetUnsafePtr();
if (ptr == null)
throw new ArgumentException($"The {typeof(NativeSlice<byte>).Name} is not valid.", nameof(bytes));
return new NSData(CreateWithBytes(ptr, bytes.Length));
}
public static unsafe NSData CreateWithBytesNoCopy(NativeSlice<byte> bytes)
{
var ptr = bytes.GetUnsafePtr();
if (ptr == null)
throw new ArgumentException($"The {typeof(NativeSlice<byte>).Name} is not valid.", nameof(bytes));
return new NSData(CreateWithBytesNoCopy(ptr, bytes.Length, false));
}
public unsafe NativeSlice<byte> Bytes
{
get
{
if (!Created)
throw new InvalidOperationException($"The {typeof(NSData).Name} has not been created.");
return NativeSliceUnsafeUtility.ConvertExistingDataToNativeSlice<byte>(GetBytes(this), 1, GetLength(this));
}
}
public void Dispose() => NativeApi.CFRelease(ref m_Ptr);
public override int GetHashCode() => m_Ptr.GetHashCode();
public override bool Equals(object obj) => (obj is NSData) && Equals((NSData)obj);
public bool Equals(NSData other) => m_Ptr == other.m_Ptr;
public static bool operator==(NSData lhs, NSData rhs) => lhs.Equals(rhs);
public static bool operator!=(NSData lhs, NSData rhs) => !lhs.Equals(rhs);
[DllImport("__Internal", EntryPoint="UnityMC_NSData_getLength")]
static extern int GetLength(NSData self);
[DllImport("__Internal", EntryPoint="UnityMC_NSData_getBytes")]
static extern unsafe void* GetBytes(NSData self);
[DllImport("__Internal", EntryPoint="UnityMC_NSData_createWithBytes")]
static extern unsafe IntPtr CreateWithBytes(void* bytes, int length);
[DllImport("__Internal", EntryPoint="UnityMC_NSData_createWithBytesNoCopy")]
static extern unsafe IntPtr CreateWithBytesNoCopy(void* bytes, int length, bool freeWhenDone);
}
}

11
Assets/Scripts/Multipeer/NSData.cs.meta


fileFormatVersion: 2
guid: 0aac506fdc0254d79b2479e6735d7ba3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

56
Assets/Scripts/Multipeer/NSError.cs


using System;
using System.Runtime.InteropServices;
namespace Unity.iOS.Multipeer
{
[StructLayout(LayoutKind.Sequential)]
public struct NSError : IDisposable, IEquatable<NSError>
{
IntPtr m_Ptr;
public bool Valid => m_Ptr != IntPtr.Zero;
public NSErrorException ToException()
{
return new NSErrorException(Code, Description);
}
public long Code
{
get
{
if (!Valid)
throw new InvalidOperationException($"The {typeof(NSError).Name} is not valid.");
return GetCode(this);
}
}
public string Description
{
get
{
if (!Valid)
throw new InvalidOperationException($"The {typeof(NSError).Name} is not valid.");
using (var description = GetLocalizedDescription(this))
{
return description.ToString();
}
}
}
public void Dispose() => NativeApi.CFRelease(ref m_Ptr);
public override int GetHashCode() => m_Ptr.GetHashCode();
public override bool Equals(object obj) => (obj is NSError) && Equals((NSError)obj);
public bool Equals(NSError other) => m_Ptr == other.m_Ptr;
public static bool operator==(NSError lhs, NSError rhs) => lhs.Equals(rhs);
public static bool operator!=(NSError lhs, NSError rhs) => !lhs.Equals(rhs);
[DllImport("__Internal", EntryPoint="UnityMC_NSError_code")]
static extern long GetCode(NSError error);
[DllImport("__Internal", EntryPoint="UnityMC_NSError_localizedDescription")]
static extern NSString GetLocalizedDescription(NSError error);
}
}

11
Assets/Scripts/Multipeer/NSError.cs.meta


fileFormatVersion: 2
guid: 10a04c15626774337abc28e3ed0dc046
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

18
Assets/Scripts/Multipeer/NSErrorException.cs


using System;
namespace Unity.iOS.Multipeer
{
public class NSErrorException : Exception
{
public NSErrorException(long code, string description)
: base($"NSError {code}: {description}")
{
Code = code;
Description = description;
}
public long Code { get; private set; }
public string Description { get; private set; }
}
}

11
Assets/Scripts/Multipeer/NSErrorException.cs.meta


fileFormatVersion: 2
guid: 491dc7d106d7642499e0ccfe7d11bf70
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

80
Assets/Scripts/Multipeer/NSString.cs


using System;
using System.Runtime.InteropServices;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
namespace Unity.iOS.Multipeer
{
[StructLayout(LayoutKind.Sequential)]
public struct NSString : IDisposable, IEquatable<NSString>
{
IntPtr m_Ptr;
internal NSString(IntPtr existing) => m_Ptr = existing;
public NSString(string text) => m_Ptr = CreateWithString(text, text.Length);
public NSString(NSData serializedString)
{
if (!serializedString.Created)
throw new ArgumentException("The serialized string is not valid.", nameof(serializedString));
m_Ptr = Deserialize(serializedString);
}
public bool Created => m_Ptr != IntPtr.Zero;
public int Length => GetLength(this);
public override unsafe string ToString()
{
if (!Created)
return string.Empty;
using (var buffer = new NativeArray<byte>(GetLengthOfBytes(this), Allocator.TempJob, NativeArrayOptions.UninitializedMemory))
{
if (GetBytes(this, buffer.GetUnsafePtr(), buffer.Length))
{
return Marshal.PtrToStringUni(new IntPtr(buffer.GetUnsafePtr()), Length);
}
else
{
return string.Empty;
}
}
}
public NSData Serialize()
{
if (!Created)
throw new InvalidOperationException($"The {typeof(NSString).Name} has not been created.");
return Serialize(this);
}
public void Dispose() => NativeApi.CFRelease(ref m_Ptr);
public override int GetHashCode() => m_Ptr.GetHashCode();
public override bool Equals(object obj) => (obj is NSString) && Equals((NSString)obj);
public bool Equals(NSString other) => m_Ptr == other.m_Ptr;
public static bool operator==(NSString lhs, NSString rhs) => lhs.Equals(rhs);
public static bool operator!=(NSString lhs, NSString rhs) => !lhs.Equals(rhs);
[DllImport("__Internal", EntryPoint="UnityMC_NSString_createWithString")]
static extern IntPtr CreateWithString([MarshalAs(UnmanagedType.LPWStr)] string text, int length);
[DllImport("__Internal", EntryPoint="UnityMC_NSString_lengthOfBytesUsingEncoding")]
static extern int GetLengthOfBytes(NSString self);
[DllImport("__Internal", EntryPoint="UnityMC_NSString_getLength")]
static extern int GetLength(NSString self);
[DllImport("__Internal", EntryPoint="UnityMC_NSString_getBytes")]
static extern unsafe bool GetBytes(NSString self, void* buffer, int length);
[DllImport("__Internal", EntryPoint="UnityMC_NSString_serialize")]
static extern NSData Serialize(NSString self);
[DllImport("__Internal", EntryPoint="UnityMC_NSString_deserialize")]
static extern IntPtr Deserialize(NSData data);
}
}

11
Assets/Scripts/Multipeer/NSString.cs.meta


fileFormatVersion: 2
guid: 2a2bbe15074f94af4b35f25e435174f6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

17
Assets/Scripts/Multipeer/NativeApi.cs


using System;
using System.Runtime.InteropServices;
namespace Unity.iOS.Multipeer
{
internal static class NativeApi
{
public static void CFRelease(ref IntPtr ptr)
{
CFRelease(ptr);
ptr = IntPtr.Zero;
}
[DllImport("__Internal", EntryPoint="UnityMC_CFRelease")]
public static extern void CFRelease(IntPtr ptr);
}
}

11
Assets/Scripts/Multipeer/NativeApi.cs.meta


fileFormatVersion: 2
guid: 06e6b03cb95994416a492630b1c74fd0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

8
Assets/Scripts/Multipeer/NativeCode.meta


fileFormatVersion: 2
guid: 841a08ffbdce6452182dcbfc65a3054c
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

37
Assets/Scripts/Multipeer/NativeCode/MultipeerDelegate-C-Bridge.m.meta


fileFormatVersion: 2
guid: f94297f07eee14b96b55b6ed49779df0
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 0
settings:
DefaultValueInitialized: true
- first:
iPhone: iOS
second:
enabled: 1
settings: {}
- first:
tvOS: tvOS
second:
enabled: 1
settings: {}
userData:
assetBundleName:
assetBundleVariant:

13
Assets/Scripts/Multipeer/NativeCode/MultipeerDelegate.h


#import <MultipeerConnectivity/MultipeerConnectivity.h>
@interface MultipeerDelegate : NSObject<MCSessionDelegate, MCNearbyServiceAdvertiserDelegate, MCNearbyServiceBrowserDelegate>
- (nullable instancetype)initWithName:(nonnull NSString *)name serviceType:(nonnull NSString*)serviceType;
- (nullable NSError*)sendToAllPeers:(nonnull NSData*)data withMode:(MCSessionSendDataMode)mode;
- (NSUInteger)connectedPeerCount;
- (NSUInteger)queueSize;
- (nonnull NSData*)dequeue;
@property BOOL enabled;
@end

27
Assets/Scripts/Multipeer/NativeCode/MultipeerDelegate.h.meta


fileFormatVersion: 2
guid: 598b4c4bd189e49b4b17a87ba0cf5418
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
Any:
second:
enabled: 1
settings: {}
- first:
Editor: Editor
second:
enabled: 0
settings:
DefaultValueInitialized: true
userData:
assetBundleName:
assetBundleVariant:

142
Assets/Scripts/Multipeer/NativeCode/MultipeerDelegate.m


#import "MultipeerDelegate.h"
@implementation MultipeerDelegate
MCSession* m_Session;
MCPeerID* m_PeerID;
NSMutableArray* m_Queue;
MCNearbyServiceAdvertiser* m_ServiceAdvertiser;
MCNearbyServiceBrowser* m_ServiceBrowser;
BOOL m_Enabled;
- (instancetype)initWithName:(nonnull NSString *)name serviceType:(nonnull NSString *)serviceType
{
if (self = [super init])
{
m_Enabled = false;
m_Queue = [[NSMutableArray alloc] init];
m_PeerID = [[MCPeerID alloc] initWithDisplayName: name];
m_Session = [[MCSession alloc] initWithPeer:m_PeerID
securityIdentity:nil
encryptionPreference:MCEncryptionRequired];
m_Session.delegate = self;
m_ServiceAdvertiser = [[MCNearbyServiceAdvertiser alloc] initWithPeer:m_PeerID
discoveryInfo:nil
serviceType:serviceType];
m_ServiceAdvertiser.delegate = self;
m_ServiceBrowser = [[MCNearbyServiceBrowser alloc] initWithPeer:m_PeerID
serviceType:serviceType];
m_ServiceBrowser.delegate = self;
}
return self;
}
- (BOOL)enabled
{
return m_Enabled;
}
- (void)setEnabled:(BOOL)enabled
{
if (enabled)
{
[m_ServiceAdvertiser startAdvertisingPeer];
[m_ServiceBrowser startBrowsingForPeers];
}
else
{
[m_ServiceAdvertiser stopAdvertisingPeer];
[m_ServiceBrowser stopBrowsingForPeers];
@synchronized (m_Queue)
{
[m_Queue removeAllObjects];
}
}
m_Enabled = enabled;
}
- (NSError*)sendToAllPeers:(nonnull NSData*)data withMode:(MCSessionSendDataMode)mode
{
if (m_Session.connectedPeers.count == 0)
return nil;
NSError* error = nil;
[m_Session sendData:data
toPeers:m_Session.connectedPeers
withMode:mode
error:&error];
return error;
}
- (NSUInteger)queueSize
{
@synchronized (m_Queue)
{
return m_Queue.count;
}
}
- (nonnull NSData*)dequeue
{
@synchronized (m_Queue)
{
NSData* data = [m_Queue objectAtIndex:0];
[m_Queue removeObjectAtIndex:0];
return data;
}
}
- (NSUInteger)connectedPeerCount
{
return m_Session.connectedPeers.count;
}
- (void)session:(nonnull MCSession *)session didFinishReceivingResourceWithName:(nonnull NSString *)resourceName fromPeer:(nonnull MCPeerID *)peerID atURL:(nullable NSURL *)localURL withError:(nullable NSError *)error {
// Not used.
}
- (void)session:(nonnull MCSession *)session didReceiveData:(nonnull NSData *)data fromPeer:(nonnull MCPeerID *)peerID
{
@synchronized (m_Queue)
{
[m_Queue addObject:data];
}
}
- (void)session:(nonnull MCSession *)session didReceiveStream:(nonnull NSInputStream *)stream withName:(nonnull NSString *)streamName fromPeer:(nonnull MCPeerID *)peerID {
// Not used.
}
- (void)session:(nonnull MCSession *)session didStartReceivingResourceWithName:(nonnull NSString *)resourceName fromPeer:(nonnull MCPeerID *)peerID withProgress:(nonnull NSProgress *)progress {
// Not used.
}
- (void)session:(nonnull MCSession *)session peer:(nonnull MCPeerID *)peerID didChangeState:(MCSessionState)state {
// Not used.
}
- (void)advertiser:(nonnull MCNearbyServiceAdvertiser *)advertiser didReceiveInvitationFromPeer:(nonnull MCPeerID *)peerID withContext:(nullable NSData *)context invitationHandler:(nonnull void (^)(BOOL, MCSession * _Nullable))invitationHandler
{
invitationHandler(YES, m_Session);
}
- (void)browser:(nonnull MCNearbyServiceBrowser *)browser foundPeer:(nonnull MCPeerID *)peerID withDiscoveryInfo:(nullable NSDictionary<NSString *,NSString *> *)info
{
// Invite the peer to join our session
[browser invitePeer:peerID
toSession:m_Session
withContext:nil
timeout:10];
}
- (void)browser:(nonnull MCNearbyServiceBrowser *)browser lostPeer:(nonnull MCPeerID *)peerID
{
// Not used
}
@end

105
Assets/Scripts/Multipeer/NativeCode/MultipeerDelegate.m.meta


fileFormatVersion: 2
guid: cba1a95445f3e40ac8bbf00450e84734
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
'': Any
second:
enabled: 0
settings:
Exclude Android: 1
Exclude Editor: 1
Exclude Linux: 1
Exclude Linux64: 1
Exclude LinuxUniversal: 1
Exclude Lumin: 1
Exclude OSXUniversal: 1
Exclude Win: 1
Exclude Win64: 1
Exclude iOS: 0
- first:
Android: Android
second:
enabled: 0
settings:
CPU: ARMv7
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 0
settings:
CPU: AnyCPU
DefaultValueInitialized: true
OS: AnyOS
- first:
Facebook: Win
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
Facebook: Win64
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
Standalone: Linux
second:
enabled: 0
settings:
CPU: x86
- first:
Standalone: Linux64
second:
enabled: 0
settings:
CPU: x86_64
- first:
Standalone: OSXUniversal
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
Standalone: Win
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
Standalone: Win64
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
iPhone: iOS
second:
enabled: 1
settings:
AddToEmbeddedBinaries: false
CompileFlags:
FrameworkDependencies: MultipeerConnectivity;
- first:
tvOS: tvOS
second:
enabled: 1
settings: {}
userData:
assetBundleName:
assetBundleVariant:

30
Assets/Scripts/Multipeer/NativeCode/NSData-C-Bridge.m


#import <Foundation/Foundation.h>
int UnityMC_NSData_getLength(void* self)
{
NSData* data = (__bridge NSData*)self;
return (int)data.length;
}
void* UnityMC_NSData_createWithBytes(void* bytes, int length)
{
NSData* data = [[NSData alloc] initWithBytes:bytes
length:length];
return (__bridge_retained void*)data;
}
void* UnityMC_NSData_createWithBytesNoCopy(void* bytes, int length, bool freeWhenDone)
{
NSData* data = [[NSData alloc] initWithBytesNoCopy:bytes
length:length
freeWhenDone:freeWhenDone];
return (__bridge_retained void*)data;
}
const void* UnityMC_NSData_getBytes(void* self)
{
NSData* data = (__bridge NSData*)self;
return data.bytes;
}

37
Assets/Scripts/Multipeer/NativeCode/NSData-C-Bridge.m.meta


fileFormatVersion: 2
guid: a452ea1f1d87e4256a5af3b742f78e18
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 0
settings:
DefaultValueInitialized: true
- first:
iPhone: iOS
second:
enabled: 1
settings: {}
- first:
tvOS: tvOS
second:
enabled: 1
settings: {}
userData:
assetBundleName:
assetBundleVariant:

12
Assets/Scripts/Multipeer/NativeCode/NSError-C-Bridge.m


#import <Foundation/Foundation.h>
long UnityMC_NSError_code(void* ptr)
{
return ((__bridge NSError*)ptr).code;
}
void* UnityMC_NSError_localizedDescription(void* ptr)
{
NSString* desc = ((__bridge NSError*)ptr).localizedDescription;
return (__bridge_retained void*)desc;
}

37
Assets/Scripts/Multipeer/NativeCode/NSError-C-Bridge.m.meta


fileFormatVersion: 2
guid: a2cf20442c9d3412f9b1978da0184a7f
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 0
settings:
DefaultValueInitialized: true
- first:
iPhone: iOS
second:
enabled: 1
settings: {}
- first:
tvOS: tvOS
second:
enabled: 1
settings: {}
userData:
assetBundleName:
assetBundleVariant:

52
Assets/Scripts/Multipeer/NativeCode/NSString-C-Bridge.m


#import <Foundation/Foundation.h>
int UnityMC_NSString_lengthOfBytesUsingEncoding(void* self)
{
if (self == NULL)
return 0;
NSString* string = (__bridge NSString*)self;
return (int)[string lengthOfBytesUsingEncoding:NSUTF16LittleEndianStringEncoding];
}
bool UnityMC_NSString_getBytes(void* self, void* buffer, int length)
{
NSString* string = (__bridge NSString*)self;
const NSRange range = NSMakeRange(0, string.length);
return [string getBytes:buffer
maxLength:length
usedLength:NULL
encoding:NSUTF16LittleEndianStringEncoding
options:0
range:range
remainingRange:NULL];
}
int UnityMC_NSString_getLength(void* self)
{
NSString* string = (__bridge NSString*)self;
return (int)string.length;
}
void* UnityMC_NSString_createWithString(void* bytes, int length)
{
NSString* string = [[NSString alloc] initWithBytes: bytes
length: 2 * length
encoding: NSUTF16LittleEndianStringEncoding];
return (__bridge_retained void*)string;
}
void* UnityMC_NSString_serialize(void* self)
{
NSString* string = (__bridge NSString*)self;
NSData* data = [NSKeyedArchiver archivedDataWithRootObject:string];
return (__bridge_retained void*)data;
}
void* UnityMC_NSString_deserialize(void* serializedString)
{
NSData* data = (__bridge NSData*)serializedString;
NSString* string = [NSKeyedUnarchiver unarchiveObjectWithData:data];
return (__bridge_retained void*)string;
}

37
Assets/Scripts/Multipeer/NativeCode/NSString-C-Bridge.m.meta


fileFormatVersion: 2
guid: f45e73c2d697643c898c3f259d718667
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 0
settings:
DefaultValueInitialized: true
- first:
iPhone: iOS
second:
enabled: 1
settings: {}
- first:
tvOS: tvOS
second:
enabled: 1
settings: {}
userData:
assetBundleName:
assetBundleVariant:

60
Assets/Scripts/Multipeer/NativeCode/MultipeerDelegate-C-Bridge.m


#include "MultipeerDelegate.h"
typedef void* ManagedMultipeerDelegate;
typedef void* ManagedNSError;
ManagedMultipeerDelegate UnityMC_Delegate_initWithName(void* name, void* serviceType)
{
MultipeerDelegate* delegate = [[MultipeerDelegate alloc] initWithName:(__bridge NSString*)name
serviceType:(__bridge NSString*)serviceType];
return (__bridge_retained void*)delegate;
}
ManagedNSError UnityMC_Delegate_sendToAllPeers(void* self, void* nsdata, int length, int mode)
{
NSData* data = (__bridge NSData*)nsdata;
MultipeerDelegate* delegate = (__bridge MultipeerDelegate*)self;
NSError* error = [delegate sendToAllPeers:data withMode:(MCSessionSendDataMode)mode];
return (__bridge_retained void*)error;
}
int UnityMC_Delegate_receivedDataQueueSize(void* self)
{
if (self == NULL)
return 0;
MultipeerDelegate* delegate = (__bridge MultipeerDelegate*)self;
return (int)delegate.queueSize;
}
void* UnityMC_Delegate_dequeueReceivedData(void* self)
{
MultipeerDelegate* delegate = (__bridge MultipeerDelegate*)self;
return (__bridge_retained void*)delegate.dequeue;
}
int UnityMC_Delegate_connectedPeerCount(void* self)
{
MultipeerDelegate* delegate = (__bridge MultipeerDelegate*)self;
return (int)delegate.connectedPeerCount;
}
void UnityMC_Delegate_setEnabled(void* self, bool enabled)
{
MultipeerDelegate* delegate = (__bridge MultipeerDelegate*)self;
delegate.enabled = enabled;
}
bool UnityMC_Delegate_getEnabled(void* self)
{
MultipeerDelegate* delegate = (__bridge MultipeerDelegate*)self;
return delegate.enabled;
}
void UnityMC_CFRelease(void* ptr)
{
if (ptr)
{
CFRelease(ptr);
}
}

103
Assets/Scenes/ARCollaborationData/ClientServerSelector.cs


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);
}
}

11
Assets/Scenes/ARCollaborationData/ClientServerSelector.cs.meta


fileFormatVersion: 2
guid: bbcf44277865e45d399575001e42f155
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

50
Assets/Scenes/ARCollaborationData/TCPClient.cs


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();
}
}

11
Assets/Scenes/ARCollaborationData/TCPClient.cs.meta


fileFormatVersion: 2
guid: a4bbe7d8f963c43d7b710873f90b9b2d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

315
Assets/Scenes/ARCollaborationData/TCPConnection.cs


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
}

11
Assets/Scenes/ARCollaborationData/TCPConnection.cs.meta


fileFormatVersion: 2
guid: 5a4eb4ee5b17a4cafa6d0e5cdb157fde
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

43
Assets/Scenes/ARCollaborationData/TCPServer.cs


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;
}
}

11
Assets/Scenes/ARCollaborationData/TCPServer.cs.meta


fileFormatVersion: 2
guid: 6fc24ee8011574921a7d764defd91305
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

4
Assets/Scenes/ARCollaborationData/IMessage.cs


public interface IMessage
{
int EncodeTo(byte[] bytes);
}

11
Assets/Scenes/ARCollaborationData/IMessage.cs.meta


fileFormatVersion: 2
guid: 48716d5221a70425aa3409ef5a24a10d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

23
Assets/Scenes/ARCollaborationData/MessageHeader.cs


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();
}
}

11
Assets/Scenes/ARCollaborationData/MessageHeader.cs.meta


fileFormatVersion: 2
guid: 392beb324d6f3400ab0b5ffe1ae43b70
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

6
Assets/Scenes/ARCollaborationData/MessageType.cs


public enum MessageType : byte
{
None,
CollaborationData
}

11
Assets/Scenes/ARCollaborationData/MessageType.cs.meta


fileFormatVersion: 2
guid: b16d841d0e5dd48d0a70c79beff77499
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

108
Assets/Scenes/ARCollaborationData/NetworkBuffer.cs


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.");
}
}

11
Assets/Scenes/ARCollaborationData/NetworkBuffer.cs.meta


fileFormatVersion: 2
guid: 6159b84b308ec4356bdc9ae17d26964b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

86
Assets/Scenes/ARCollaborationData/NetworkDataDecoder.cs


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);
}
}
}

11
Assets/Scenes/ARCollaborationData/NetworkDataDecoder.cs.meta


fileFormatVersion: 2
guid: d3331be3e25f44589a8a9895cdb60f02
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

77
Assets/Scenes/ARCollaborationData/NetworkDataEncoder.cs


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;
}
}

11
Assets/Scenes/ARCollaborationData/NetworkDataEncoder.cs.meta


fileFormatVersion: 2
guid: 03435c1a2d82b46f98fe7980c199a46f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
正在加载...
取消
保存