浏览代码

Merge pull request #33 from Unity-Technologies/master-staging

Master staging
/main
GitHub 2 年前
当前提交
6a10872c
共有 202 个文件被更改,包括 3757 次插入2699 次删除
  1. 2
      .gitignore
  2. 21
      Assets/Art/Glyph/Symbols.png.meta
  3. 9
      Assets/Art/Icons/ClickParticleMat.mat
  4. 81
      Assets/Prefabs/GameManager.prefab
  5. 199
      Assets/Prefabs/NGO/InGameLogic.prefab
  6. 13
      Assets/Prefabs/Runes/icon_particle.mat
  7. 31
      Assets/Prefabs/UI/BackButtonBG.prefab
  8. 70
      Assets/Prefabs/UI/GameCanvas.prefab
  9. 88
      Assets/Prefabs/UI/JoinContent.prefab
  10. 89
      Assets/Prefabs/UI/JoinCreateCanvas.prefab
  11. 171
      Assets/Prefabs/UI/LobbyCanvas.prefab
  12. 34
      Assets/Prefabs/UI/LobbyCodeCanvas.prefab
  13. 106
      Assets/Prefabs/UI/LobbyUserList.prefab
  14. 41
      Assets/Prefabs/UI/MainMenuCanvas.prefab
  15. 34
      Assets/Prefabs/UI/RelayCodeCanvas.prefab
  16. 35
      Assets/Prefabs/UI/SpinnerUI.prefab
  17. 58
      Assets/Prefabs/UI/UserCardPanel.prefab
  18. 371
      Assets/Prefabs/UI/UserInteractionPanel.prefab
  19. 49
      Assets/Prefabs/UI/LobbyEntryUI.prefab
  20. 21
      Assets/Renderer/glyphGameURP.asset
  21. 14
      Assets/Renderer/glyphGameURP_Renderer.asset
  22. 423
      Assets/Scenes/mainScene.unity
  23. 2
      Assets/Scripts/GameLobby/Auth/Auth.cs.meta
  24. 49
      Assets/Scripts/GameLobby/Game/Countdown.cs
  25. 586
      Assets/Scripts/GameLobby/Game/GameManager.cs
  26. 339
      Assets/Scripts/GameLobby/Game/LocalLobby.cs
  27. 24
      Assets/Scripts/GameLobby/Game/ServerAddress.cs
  28. 26
      Assets/Scripts/GameLobby/Infrastructure/LogHandlerSettings.cs
  29. 2
      Assets/Scripts/GameLobby/Lobby/AsyncRequestLobby.cs
  30. 139
      Assets/Scripts/GameLobby/Lobby/LobbyConverters.cs
  31. 8
      Assets/Scripts/GameLobby/LobbyRelaySample.asmdef
  32. 104
      Assets/Scripts/GameLobby/NGO/InGameRunner.cs
  33. 12
      Assets/Scripts/GameLobby/NGO/IntroOutroRunner.cs
  34. 14
      Assets/Scripts/GameLobby/NGO/NetworkedDataStore.cs
  35. 52
      Assets/Scripts/GameLobby/NGO/PlayerCursor.cs
  36. 8
      Assets/Scripts/GameLobby/NGO/ResultsUserUI.cs
  37. 10
      Assets/Scripts/GameLobby/NGO/Scorer.cs
  38. 135
      Assets/Scripts/GameLobby/NGO/SequenceSelector.cs
  39. 171
      Assets/Scripts/GameLobby/NGO/SetupInGame.cs
  40. 45
      Assets/Scripts/GameLobby/NGO/SymbolContainer.cs
  41. 2
      Assets/Scripts/GameLobby/NGO/SymbolData.cs
  42. 4
      Assets/Scripts/GameLobby/NGO/SymbolKillVolume.cs
  43. 44
      Assets/Scripts/GameLobby/NGO/SymbolObject.cs
  44. 6
      Assets/Scripts/GameLobby/Relay/AsyncRequestRelay.cs
  45. 4
      Assets/Scripts/GameLobby/Relay/RelayAPIInterface.cs
  46. 12
      Assets/Scripts/GameLobby/Tests/Editor/MessengerTests.cs
  47. 217
      Assets/Scripts/GameLobby/Tests/PlayMode/LobbyRoundtripTests.cs
  48. 4
      Assets/Scripts/GameLobby/Tests/PlayMode/Tests.Play.asmdef
  49. 190
      Assets/Scripts/GameLobby/Tests/PlayMode/UtpTests.cs
  50. 7
      Assets/Scripts/GameLobby/UI/BackButtonUI.cs
  51. 11
      Assets/Scripts/GameLobby/UI/CountdownUI.cs
  52. 11
      Assets/Scripts/GameLobby/UI/CreateMenuUI.cs
  53. 20
      Assets/Scripts/GameLobby/UI/DisplayCodeUI.cs
  54. 7
      Assets/Scripts/GameLobby/UI/EmoteButtonUI.cs
  55. 6
      Assets/Scripts/GameLobby/UI/EndGameButtonUI.cs
  56. 22
      Assets/Scripts/GameLobby/UI/GameStateVisibilityUI.cs
  57. 99
      Assets/Scripts/GameLobby/UI/InLobbyUserUI.cs
  58. 22
      Assets/Scripts/GameLobby/UI/JoinCreateLobbyUI.cs
  59. 121
      Assets/Scripts/GameLobby/UI/JoinMenuUI.cs
  60. 7
      Assets/Scripts/GameLobby/UI/LobbyNameUI.cs
  61. 12
      Assets/Scripts/GameLobby/UI/LobbyUserVolumeUI.cs
  62. 2
      Assets/Scripts/GameLobby/UI/NameChangeUI.cs
  63. 19
      Assets/Scripts/GameLobby/UI/RateLimitVisibility.cs
  64. 10
      Assets/Scripts/GameLobby/UI/ReadyCheckUI.cs
  65. 14
      Assets/Scripts/GameLobby/UI/RelayAddressUI.cs
  66. 47
      Assets/Scripts/GameLobby/UI/ShowWhenLobbyStateUI.cs
  67. 38
      Assets/Scripts/GameLobby/UI/SpinnerUI.cs
  68. 6
      Assets/Scripts/GameLobby/UI/StartLobbyButtonUI.cs
  69. 12
      Assets/Scripts/GameLobby/UI/UIPanelBase.cs
  70. 15
      Assets/Scripts/GameLobby/UI/UserNameUI.cs
  71. 46
      Assets/Scripts/GameLobby/UI/UserStateVisibilityUI.cs
  72. 71
      Assets/Scripts/GameLobby/UI/LobbyUserListUI.cs
  73. 109
      Assets/Scripts/GameLobby/UI/LobbyEntryUI.cs
  74. 40
      Assets/Scripts/GameLobby/Vivox/VivoxSetup.cs
  75. 2
      Assets/StreamingAssets.meta
  76. 26
      Packages/manifest.json
  77. 102
      Packages/packages-lock.json
  78. 9
      ProjectSettings/GraphicsSettings.asset
  79. 20
      ProjectSettings/PackageManagerSettings.asset
  80. 15
      ProjectSettings/ProjectSettings.asset
  81. 4
      ProjectSettings/ProjectVersion.txt
  82. 3
      ProjectSettings/QualitySettings.asset
  83. 2
      ProjectSettings/URPProjectSettings.asset
  84. 15
      README.md
  85. 2
      Assets/Scripts/GameLobby/Infrastructure/CallbackValue.cs.meta
  86. 112
      Assets/Prefabs/NGO/NetworkManager.prefab
  87. 7
      Assets/Prefabs/NGO/NetworkManager.prefab.meta
  88. 117
      Assets/Scripts/GameLobby/Auth/Auth.cs
  89. 42
      Assets/Scripts/GameLobby/Game/LocalLobbyList.cs
  90. 53
      Assets/Scripts/GameLobby/Game/LocalPlayer.cs
  91. 45
      Assets/Scripts/GameLobby/Infrastructure/CallbackValue.cs
  92. 611
      Assets/Scripts/GameLobby/Lobby/LobbyManager.cs
  93. 179
      Assets/Scripts/GameLobby/Lobby/LobbySynchronizer.cs
  94. 27
      Assets/Scripts/GameLobby/Tests/PlayMode/AsyncTestHelper.cs
  95. 11
      Assets/Scripts/GameLobby/Tests/PlayMode/AsyncTestHelper.cs.meta
  96. 71
      Assets/Scripts/GameLobby/UI/ColorLobbyUI.cs
  97. 27
      Assets/UniversalRenderPipelineGlobalSettings.asset
  98. 8
      Assets/UniversalRenderPipelineGlobalSettings.asset.meta
  99. 3
      Packages/ParrelSync.meta

2
.gitignore


.UserSettings
.UserSettings/
UserSettings
ProjectSettings/RiderScriptEditorPersistedState.asset

21
Assets/Art/Glyph/Symbols.png.meta


streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMasterTextureLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0

edges: []
weights: []
secondaryTextures: []
nameFileIdTable:
17_elemental: -2657090017763772448
4_matter: -612294750349367285
5_gas: 4906567379933833492
7_star: 4588468031203458904
2_cold: -469525390435789673
entropy: 5531826651420068725
10_life: 6472110944188623208
9_planet: -5148847302637816175
13_mind: -2962330253262605572
16_evil: -5893568625793066583
1_heat: -3739395637988037281
15_good: -431797579490579562
0_gravity: 8720604468809159126
8_system: 4830219810074551288
6_water: 8796499958254778168
14_moon: -5670611702303752676
3_entanglement: -5196480314518052078
12_expansion: -5943722948072908530
11_radiation: -2449591561013552763
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0

9
Assets/Art/Icons/ClickParticleMat.mat


m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
m_Name:
m_EditorClassIdentifier:
version: 4
version: 5
serializedVersion: 6
serializedVersion: 8
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}

m_ShaderKeywords: _ALPHATEST_ON
m_ValidKeywords:
- _ALPHATEST_ON
m_InvalidKeywords: []
m_LightmapFlags: 4
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0

m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
m_Ints: []
m_Floats:
- _AlphaClip: 1
- _Blend: 0

81
Assets/Prefabs/GameManager.prefab


m_Component:
- component: {fileID: 802462301182945455}
- component: {fileID: 6265861362966661484}
- component: {fileID: 5235782363599194820}
- component: {fileID: 2812013971948110896}
m_Layer: 0
m_Name: SetupInGame
m_TagString: Untagged

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 7716713811812636911}
m_RootOrder: 0

m_Script: {fileID: 11500000, guid: 3f7533ddeca587549a9798a65a8670ba, type: 3}
m_Name:
m_EditorClassIdentifier:
m_prefabNetworkManager: {fileID: 238192747445020667, guid: 73173c97c128d614aa2a1167a2eaea68, type: 3}
m_IngameRunnerPrefab: {fileID: 238192747445020667, guid: 73173c97c128d614aa2a1167a2eaea68, type: 3}
--- !u!114 &5235782363599194820
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5395715167687395500}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 70dfc2fde0a9ef04eaff29a138f0bf45, type: 3}
m_Name:
m_EditorClassIdentifier:
OnObservedUpdated:
m_PersistentCalls:
m_Calls:
- m_Target: {fileID: 6265861362966661484}
m_TargetAssemblyTypeName: LobbyRelaySample.inGame.SetupInGame, LobbyRelaySample
m_MethodName: OnLobbyChange
m_Mode: 0
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
--- !u!114 &2812013971948110896
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5395715167687395500}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: a03b37d5b8df06948b36dfbc430a1ea5, type: 3}
m_Name:
m_EditorClassIdentifier:
OnObservedUpdated:
m_PersistentCalls:
m_Calls:
- m_Target: {fileID: 6265861362966661484}
m_TargetAssemblyTypeName: LobbyRelaySample.inGame.SetupInGame, LobbyRelaySample
m_MethodName: OnLocalUserChange
m_Mode: 0
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
--- !u!1 &7716713811812636896
GameObject:
m_ObjectHideFlags: 0

m_Component:
- component: {fileID: 7716713811812636911}
- component: {fileID: 7716713811812636910}
- component: {fileID: 5193415626965589893}
m_Layer: 0
m_Name: GameManager
m_TagString: Untagged

m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 624.1824, y: 320.50745, z: -29748.68}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children:
- {fileID: 802462301182945455}
m_Father: {fileID: 0}

m_Script: {fileID: 11500000, guid: b4f7225f73bfe6a4d9133ee45ac9cd73, type: 3}
m_Name:
m_EditorClassIdentifier:
m_GameStateObservers: []
m_LocalLobbyObservers: []
m_LocalUserObservers: []
m_LobbyServiceObservers: []
m_setupInGame: {fileID: 6265861362966661484}
m_countdown: {fileID: 0}
--- !u!114 &5193415626965589893
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7716713811812636896}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 9275e9be406280e4198464167a8b9186, type: 3}
m_Name:
m_EditorClassIdentifier:
m_durationToleranceMs: 50
m_doNotRemoveIfTooLong: 1

199
Assets/Prefabs/NGO/InGameLogic.prefab


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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 1250806127447164487}
m_RootOrder: 0

serializedVersion: 6
m_Component:
- component: {fileID: 485451675458297819}
- component: {fileID: 2273653745935453042}
- component: {fileID: 5378807363724719273}
m_Layer: 0
m_Name: InGameLogic
m_TagString: Untagged

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 6532331214593598572}
- {fileID: 5289034077109495657}

m_Father: {fileID: 0}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &2273653745935453042
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 238192747445020667}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 593a2fe42fa9d37498c96f9a383b6521, type: 3}
m_Name:
m_EditorClassIdentifier:
DontDestroy: 1
RunInBackground: 0
LogLevel: 1
NetworkConfig:
ProtocolVersion: 0
NetworkTransport: {fileID: 5378807363724719273}
PlayerPrefab: {fileID: 0}
NetworkPrefabs:
- Override: 0
Prefab: {fileID: 3227847727972158006, guid: 905594b4ee5bb864a84af916cc445d1b, type: 3}
SourcePrefabToOverride: {fileID: 0}
SourceHashToOverride: 0
OverridingTargetPrefab: {fileID: 0}
- Override: 0
Prefab: {fileID: 5240148789413552765, guid: f42ed38d10b57ec48870f76a7a63389e, type: 3}
SourcePrefabToOverride: {fileID: 0}
SourceHashToOverride: 0
OverridingTargetPrefab: {fileID: 0}
- Override: 0
Prefab: {fileID: 8828823320646980938, guid: e371ca3112f9e244ab574b472387b64b, type: 3}
SourcePrefabToOverride: {fileID: 0}
SourceHashToOverride: 0
OverridingTargetPrefab: {fileID: 0}
TickRate: 30
ClientConnectionBufferTimeout: 10
ConnectionApproval: 0
ConnectionData:
EnableTimeResync: 0
TimeResyncInterval: 30
EnsureNetworkVariableLengthSafety: 0
EnableSceneManagement: 1
ForceSamePrefabs: 1
RecycleNetworkIds: 1
NetworkIdRecycleDelay: 120
RpcHashSize: 0
LoadSceneTimeOut: 120
MessageBufferTimeout: 20
EnableNetworkLogs: 1
--- !u!114 &5378807363724719273
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 238192747445020667}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 6960e84d07fb87f47956e7a81d71c4e6, type: 3}
m_Name:
m_EditorClassIdentifier:
m_ProtocolType: 1
m_MaximumPacketSize: 1400
m_MaxPacketQueueSize: 512
m_SendQueueBatchSize: 6144
m_HeartbeatTimeoutMS: 500
m_ConnectTimeoutMS: 1000
m_MaxConnectAttempts: 60
m_DisconnectTimeoutMS: 30000
ConnectionData:
Address: 127.0.0.1
Port: 7777
--- !u!1 &413870477192997562
GameObject:
m_ObjectHideFlags: 0

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 9210454441405939118}
m_RootOrder: 0

m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: -10}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 485451675458297819}
m_RootOrder: 0

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 4271018735512598272}
- {fileID: 5754962845139143862}

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 5231970163014429165}
m_Father: {fileID: 3536358479196928666}

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 3812834876162241050}
m_RootOrder: 0

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 2884208290717289317}
m_RootOrder: 1

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 1093042331837590349}
- {fileID: 5557516240847477329}

serializedVersion: 6
m_Component:
- component: {fileID: 5289034077109495657}
- component: {fileID: 8928417086886337167}
- component: {fileID: 6636000542315549800}
m_Layer: 0
m_Name: InGameRunner
m_TagString: Untagged

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_ConstrainProportionsScale: 0
--- !u!114 &8928417086886337167
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1306704497370578788}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: d5a57f767e5e46a458fc5d3c628d0cbb, type: 3}
m_Name:
m_EditorClassIdentifier:
GlobalObjectIdHash: 951099334
AlwaysReplicateAsRoot: 0
DontDestroyWithOwner: 0
AutoObjectParentSync: 1
--- !u!114 &6673480979101889538
MonoBehaviour:
m_ObjectHideFlags: 0

m_Script: {fileID: 11500000, guid: e30fcbc5a3738e94a87d7a028a2a6fba, type: 3}
m_Name:
m_EditorClassIdentifier:
m_playerCursorPrefab: {fileID: -1321688216342888635, guid: 905594b4ee5bb864a84af916cc445d1b, type: 3}
m_symbolContainerPrefab: {fileID: 3984715711634906321, guid: f42ed38d10b57ec48870f76a7a63389e, type: 3}
m_playerCursorPrefab: {fileID: 4403165428829500588, guid: 905594b4ee5bb864a84af916cc445d1b, type: 3}
m_symbolContainerPrefab: {fileID: 210836793418873202, guid: f42ed38d10b57ec48870f76a7a63389e, type: 3}
m_symbolObjectPrefab: {fileID: -8192876538761676823, guid: e371ca3112f9e244ab574b472387b64b, type: 3}
m_sequenceSelector: {fileID: 6829526275642584874}
m_scorer: {fileID: 2250928641321586401}

m_collider: {fileID: 4478514896869440358}
m_symbolContainerInstance: {fileID: 0}
--- !u!114 &6829526275642584874
MonoBehaviour:
m_ObjectHideFlags: 0

m_Script: {fileID: 11500000, guid: 77910b5766924f044995dfe427e9eea3, type: 3}
m_Name:
m_EditorClassIdentifier:
m_symbolData: {fileID: 11400000, guid: 84a81a2f14d442a49b46eccefb73933f, type: 2}
m_targetSequenceOutput:
m_SymbolData: {fileID: 11400000, guid: 84a81a2f14d442a49b46eccefb73933f, type: 2}
m_TargetSequenceOutput:
- {fileID: 82602402459208677}
- {fileID: 2579125671427676891}
- {fileID: 6639276723346236408}

serializedVersion: 2
m_Size: {x: 16, y: 120, z: 1}
m_Center: {x: 0, y: 60, z: 0}
--- !u!114 &6636000542315549800
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1306704497370578788}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: d5a57f767e5e46a458fc5d3c628d0cbb, type: 3}
m_Name:
m_EditorClassIdentifier:
GlobalObjectIdHash: 951099334
AlwaysReplicateAsRoot: 0
DontDestroyWithOwner: 0
AutoObjectParentSync: 1
--- !u!1 &1912141680601921478
GameObject:
m_ObjectHideFlags: 0

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 4683486642073920110}
m_Father: {fileID: 6730446115375840747}

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 1061569962213594104}
m_RootOrder: 0

m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: -12, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 485451675458297819}
m_RootOrder: 5

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 198237009240628586}
- {fileID: 16037789917111781}

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 4905162368523321093}
m_Father: {fileID: 3719423043742445757}

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 5754962845139143862}
m_RootOrder: 1

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 5754962845139143862}
m_RootOrder: 0

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 7180346621296967487}
m_RootOrder: 0

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 1061569962213594104}
- {fileID: 2124973746581900278}

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 6347969710836929235}
m_Father: {fileID: 3536358479196928666}

m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 0, y: 0, z: 0}
m_ConstrainProportionsScale: 0
m_Children:
- {fileID: 8160705085328016364}
m_Father: {fileID: 485451675458297819}

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 4784983905519642549}
m_RootOrder: 0

serializedVersion: 6
m_Component:
- component: {fileID: 8722147382518631401}
- component: {fileID: 2654986621618399364}
- component: {fileID: 4210703794441540767}
m_Layer: 5
m_Name: ResultsSequence

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 6873234893560763838}
m_Father: {fileID: 8063659512749448051}

m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 0, y: 0}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!114 &2654986621618399364
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3687523182501472382}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: d5a57f767e5e46a458fc5d3c628d0cbb, type: 3}
m_Name:
m_EditorClassIdentifier:
GlobalObjectIdHash: 951099334
AlwaysReplicateAsRoot: 0
DontDestroyWithOwner: 0
AutoObjectParentSync: 1
--- !u!114 &4210703794441540767
MonoBehaviour:
m_ObjectHideFlags: 0

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 609939759751866630}
- {fileID: 8613470296925483228}

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 3698041764509804342}
m_RootOrder: 0

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 2124973746581900278}
m_RootOrder: 1

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 1250806127447164487}
- {fileID: 669834599338486315}

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 9208629700357753581}
m_RootOrder: 1

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 3374802268437532309}
m_Father: {fileID: 40575182689869500}

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 8939277680710968141}
m_Father: {fileID: 3719423043742445757}

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 762979378731227237}
m_Father: {fileID: 40575182689869500}

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 1900156070638843258}
m_RootOrder: 0

m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 30, y: 30, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 365796668369407178}
m_RootOrder: 0

m_CastShadows: 0
m_ReceiveShadows: 0
m_DynamicOccludee: 1
m_StaticShadowCaster: 0
m_MotionVectors: 1
m_LightProbeUsage: 1
m_ReflectionProbeUsage: 1

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 3350915488537301533}
- {fileID: 5055996223209802668}

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 4271018735512598272}
m_RootOrder: 0

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 9208629700357753581}
m_RootOrder: 0

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 3719423043742445757}
- {fileID: 40575182689869500}

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 1093042331837590349}
m_RootOrder: 0

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 5557516240847477329}
m_RootOrder: 0

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 5557516240847477329}
m_RootOrder: 1

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 2124973746581900278}
m_RootOrder: 0

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 8984575746100710798}
m_Father: {fileID: 6730446115375840747}

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 669834599338486315}
m_RootOrder: 0

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 7978301007878594822}
- {fileID: 7131533514903797874}

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 7461186955731390703}
m_Father: {fileID: 8897506674002061787}

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 5954072923278860053}
- {fileID: 7025050144556213061}

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 3983867301106818349}
m_RootOrder: 0

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 6183038953491067537}
- {fileID: 6730446115375840747}

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 2884208290717289317}
m_RootOrder: 3

m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1.3, y: 1.3, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 9210454441405939118}
m_RootOrder: 2

m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 0, y: 0, z: 0}
m_ConstrainProportionsScale: 0
m_Children:
- {fileID: 9210454441405939118}
- {fileID: 2884208290717289317}

m_PresetInfoIsWorld: 0
--- !u!95 &9205289577047239847
Animator:
serializedVersion: 3
serializedVersion: 4
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}

m_UpdateMode: 0
m_ApplyRootMotion: 0
m_LinearVelocityBlending: 0
m_StabilizeFeet: 0
m_WarningMessage:
m_HasTransformHierarchy: 1
m_AllowConstantClipSamplingOptimization: 1

m_Script: {fileID: 11500000, guid: 9f7a308a72bf6ce4a862a246eaed82cb, type: 3}
m_Name:
m_EditorClassIdentifier:
m_inGameRunner: {fileID: 6673480979101889538}
m_animator: {fileID: 9205289577047239847}
--- !u!1 &7760914309000979352
GameObject:

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 1061428408745274728}
m_Father: {fileID: 8897506674002061787}

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 2884208290717289317}
m_RootOrder: 2

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 2884208290717289317}
m_RootOrder: 0

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 3698041764509804342}
- {fileID: 9208629700357753581}

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 8262658662678090917}
m_Father: {fileID: 485451675458297819}

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 2281109580131452863}
m_Father: {fileID: 6730446115375840747}

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 5397289210807700292}
m_RootOrder: 0

m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1.3, y: 1.3, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 9210454441405939118}
m_RootOrder: 3

13
Assets/Prefabs/Runes/icon_particle.mat


%TAG !u! tag:unity3d.com,2011:
--- !u!21 &2100000
Material:
serializedVersion: 6
serializedVersion: 8
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}

m_ShaderKeywords: _ALPHATEST_ON _COLORADDSUBDIFF_ON
m_ValidKeywords:
- _ALPHATEST_ON
- _COLORADDSUBDIFF_ON
- _SURFACE_TYPE_TRANSPARENT
m_InvalidKeywords:
- _FLIPBOOKBLENDING_OFF
m_LightmapFlags: 6
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0

disabledShaderPasses:
- SHADOWCASTER
- DepthOnly
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:

m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
m_Ints: []
m_Floats:
- _AlphaClip: 1
- _Blend: 0

m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
m_Name:
m_EditorClassIdentifier:
version: 4
version: 5

31
Assets/Prefabs/UI/BackButtonBG.prefab


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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 8687266200334079465}
m_Father: {fileID: 21306902101918852}

- component: {fileID: 9211534540060107035}
- component: {fileID: 3548897939371032169}
- component: {fileID: 7262104930290195570}
- component: {fileID: 8907220659739637781}
- component: {fileID: 7728157518251256520}
m_Layer: 5
m_Name: BackButtonBG

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 6522473138835034473}
m_Father: {fileID: 0}

m_Script: {fileID: 11500000, guid: 3c93e82eef7d613418b85194aace7f69, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!114 &8907220659739637781
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5416637149047939654}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 51373dc3c6ac79b4f8e36ac7c4419205, type: 3}
m_Name:
m_EditorClassIdentifier:
OnObservedUpdated:
m_PersistentCalls:
m_Calls:
- m_Target: {fileID: 7728157518251256520}
m_TargetAssemblyTypeName: LobbyRooms.UI.GameStateVisibilityUI, LobbyRooms
m_MethodName: ObservedUpdated
m_Mode: 0
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
--- !u!114 &7728157518251256520
MonoBehaviour:
m_ObjectHideFlags: 0

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 6522473138835034473}
m_RootOrder: 0

70
Assets/Prefabs/UI/GameCanvas.prefab


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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 3862000843821052302}
m_RootOrder: 0

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 3168212744299139599}
m_Father: {fileID: 5323557791684491924}

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 1166753073431177940}
m_RootOrder: 0

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 4033840405646935}
- {fileID: 8211845410637438851}

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 8651475152227732998}
m_RootOrder: 0

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 3933490046232978046}
m_RootOrder: 0

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 3513144943005353968}
m_Father: {fileID: 5323557791684491924}

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 5265762631451841679}
m_RootOrder: 0

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 8628454958398271548}
m_Father: {fileID: 2437121395950827588}

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 6891852016103666983}
m_RootOrder: 0

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 8628454959107311804}
- {fileID: 8628454957908509635}

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 2017918237437651757}
- {fileID: 2761609676931020092}

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 8578661244454260921}
m_RootOrder: 0

m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 0, y: 0, z: 0}
m_ConstrainProportionsScale: 0
m_Children:
- {fileID: 4100836674054655770}
- {fileID: 5323557791684491924}

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 9059265328890417471}
m_RootOrder: 0

m_RemovedComponents:
- {fileID: 418819164531713121, guid: 247f79ab5aefc6d40bcbdade4d9467b7, type: 3}
m_SourcePrefab: {fileID: 100100000, guid: 247f79ab5aefc6d40bcbdade4d9467b7, type: 3}
--- !u!224 &3933490046232978046 stripped
--- !u!224 &1166753073431177940 stripped
m_CorrespondingSourceObject: {fileID: 6777302673485755179, guid: 247f79ab5aefc6d40bcbdade4d9467b7, type: 3}
m_CorrespondingSourceObject: {fileID: 8694871130870774657, guid: 247f79ab5aefc6d40bcbdade4d9467b7, type: 3}
--- !u!224 &8578661244454260921 stripped
--- !u!224 &3862000843821052302 stripped
m_CorrespondingSourceObject: {fileID: 2276224879528722924, guid: 247f79ab5aefc6d40bcbdade4d9467b7, type: 3}
m_CorrespondingSourceObject: {fileID: 6702435737022390491, guid: 247f79ab5aefc6d40bcbdade4d9467b7, type: 3}
--- !u!224 &6891852016103666983 stripped
--- !u!224 &3933490046232978046 stripped
m_CorrespondingSourceObject: {fileID: 3981048342783665266, guid: 247f79ab5aefc6d40bcbdade4d9467b7, type: 3}
m_CorrespondingSourceObject: {fileID: 6777302673485755179, guid: 247f79ab5aefc6d40bcbdade4d9467b7, type: 3}
--- !u!224 &9059265328890417471 stripped
RectTransform:
m_CorrespondingSourceObject: {fileID: 1523264875846598762, guid: 247f79ab5aefc6d40bcbdade4d9467b7, type: 3}
--- !u!114 &4174114467305540031 stripped
MonoBehaviour:
m_CorrespondingSourceObject: {fileID: 5869930269783152874, guid: 247f79ab5aefc6d40bcbdade4d9467b7, type: 3}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 9085046f02f69544eb97fd06b6048fe2, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!224 &1166753073431177940 stripped
--- !u!224 &6891852016103666983 stripped
m_CorrespondingSourceObject: {fileID: 8694871130870774657, guid: 247f79ab5aefc6d40bcbdade4d9467b7, type: 3}
m_CorrespondingSourceObject: {fileID: 3981048342783665266, guid: 247f79ab5aefc6d40bcbdade4d9467b7, type: 3}
--- !u!224 &3862000843821052302 stripped
--- !u!224 &8578661244454260921 stripped
m_CorrespondingSourceObject: {fileID: 6702435737022390491, guid: 247f79ab5aefc6d40bcbdade4d9467b7, type: 3}
m_CorrespondingSourceObject: {fileID: 2276224879528722924, guid: 247f79ab5aefc6d40bcbdade4d9467b7, type: 3}
--- !u!224 &8651475152227732998 stripped
--- !u!224 &8628454958398271548 stripped
m_CorrespondingSourceObject: {fileID: 1192176632984182611, guid: 247f79ab5aefc6d40bcbdade4d9467b7, type: 3}
m_CorrespondingSourceObject: {fileID: 2244251207921394025, guid: 247f79ab5aefc6d40bcbdade4d9467b7, type: 3}
--- !u!114 &4174114467305540031 stripped
MonoBehaviour:
m_CorrespondingSourceObject: {fileID: 5869930269783152874, guid: 247f79ab5aefc6d40bcbdade4d9467b7, type: 3}
--- !u!224 &8651475152227732998 stripped
RectTransform:
m_CorrespondingSourceObject: {fileID: 1192176632984182611, guid: 247f79ab5aefc6d40bcbdade4d9467b7, type: 3}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 9085046f02f69544eb97fd06b6048fe2, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!224 &8628454958398271548 stripped
--- !u!224 &9059265328890417471 stripped
m_CorrespondingSourceObject: {fileID: 2244251207921394025, guid: 247f79ab5aefc6d40bcbdade4d9467b7, type: 3}
m_CorrespondingSourceObject: {fileID: 1523264875846598762, guid: 247f79ab5aefc6d40bcbdade4d9467b7, type: 3}
m_PrefabInstance: {fileID: 7537689341060714837}
m_PrefabAsset: {fileID: 0}
--- !u!1001 &8628454958156142197

propertyPath: m_Name
value: RenameButtonCanvas
objectReference: {fileID: 0}
m_RemovedComponents: []
m_RemovedComponents:
- {fileID: 3817649292031598177, guid: 0828349f6319d084bbba8edd08991e62, type: 3}
m_SourcePrefab: {fileID: 100100000, guid: 0828349f6319d084bbba8edd08991e62, type: 3}
--- !u!224 &3513144943005353968 stripped
RectTransform:

88
Assets/Prefabs/UI/JoinContent.prefab


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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 3810288654714679559}
m_RootOrder: 0

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 476776760980644771}
m_RootOrder: 0

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 9152075987234541224}
m_RootOrder: 0

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 8242294458145102565}
m_RootOrder: 1

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 7955209533444849375}
m_RootOrder: 0

- component: {fileID: 785260762106121641}
- component: {fileID: 3931324176504405867}
- component: {fileID: 1462126939442648229}
- component: {fileID: 7550446569341709048}
m_Layer: 5
m_Name: JoinContent
m_TagString: Untagged

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 4325310634866704869}
- {fileID: 7970272424148618348}

m_onVisibilityChange:
m_PersistentCalls:
m_Calls:
- m_Target: {fileID: 1462126939442648229}
m_TargetAssemblyTypeName: LobbyRelaySample.UI.JoinMenuUI, LobbyRelaySample
m_MethodName: JoinMenuChangedVisibility
- m_Target: {fileID: 0}
m_TargetAssemblyTypeName: GameLobby.UI.UITinter, LobbyRelaySample
m_MethodName: SetToColor
m_Mode: 0
m_Arguments:
m_ObjectArgument: {fileID: 0}

m_StringArgument:
m_BoolArgument: 0
m_CallState: 2
m_LobbyButtonPrefab: {fileID: 7018369548608736188, guid: f6d35a456ba76a24587dce83bd088b7d, type: 3}
m_LobbyEntryPrefab: {fileID: 7018369548608736188, guid: f6d35a456ba76a24587dce83bd088b7d, type: 3}
--- !u!114 &7550446569341709048
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 785260762106121644}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: edfa4d53eaae84c43ba581088940700c, type: 3}
m_Name:
m_EditorClassIdentifier:
OnObservedUpdated:
m_PersistentCalls:
m_Calls:
- m_Target: {fileID: 1462126939442648229}
m_TargetAssemblyTypeName: LobbyRooms.UI.JoinMenuUI, LobbyRooms
m_MethodName: ObservedUpdated
m_Mode: 0
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_JoinCreateLobbyUI: {fileID: 0}
--- !u!1 &891284586109156510
GameObject:
m_ObjectHideFlags: 0

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 3864498296333289704}
m_RootOrder: 1

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 3864498296333289704}
m_RootOrder: 0

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 4279971184503703239}
m_RootOrder: 1

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 2072554153277161701}
m_RootOrder: 1

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 5676572735189107223}
m_RootOrder: 0

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 1839420744844123086}
- {fileID: 4260256627374184318}

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 785260762106121647}
m_RootOrder: 1

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 8298271775641329263}
- {fileID: 7143473272919870158}

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 476776760980644771}
m_Father: {fileID: 4100995143772770972}

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 9095321446247494771}
m_RootOrder: 0

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 4079924192455176000}
- {fileID: 6265314097236686696}

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 4260256627374184318}
m_RootOrder: 1

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 3632021558847075417}
m_RootOrder: 1

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 3755762060919842715}
m_RootOrder: 0

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 5676572735189107223}
m_RootOrder: 1

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 4100995143772770972}
m_Father: {fileID: 4325310634866704869}

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 9152075987234541224}
m_RootOrder: 1

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 3755762060919842715}
m_RootOrder: 1

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 3810288654714679559}
m_RootOrder: 1

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 2226373431972622546}
- {fileID: 7022454495411658382}

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 2072554153277161701}
- {fileID: 4178278345133798937}

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 7573825319354851387}
m_Father: {fileID: 1604043255686064399}

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 7955209533444849375}
- {fileID: 1604043255686064399}

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 796953684453886289}
m_RootOrder: 0

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 2735345665116889010}
- {fileID: 8807990684132234493}

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 3864498296333289704}
- {fileID: 8941623568823021572}

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 2072554153277161701}
m_RootOrder: 0

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 2716431726230942877}
- {fileID: 4270049585292890095}

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 796953684453886289}
- {fileID: 3165760063825440603}

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 4036064834031661316}
- {fileID: 7485750802846073613}

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 3309229614039675927}
m_RootOrder: 1

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 4200763249476693650}
- {fileID: 4051350118216901408}

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 796953684453886289}
m_RootOrder: 1

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 4279971184503703239}
m_RootOrder: 0

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 7315760059761538639}
m_Father: {fileID: 2826622903624280382}

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 9152075987234541224}
- {fileID: 6267811842759446846}

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 8891399108302556007}
- {fileID: 1608448316400021619}

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 7824921818678239159}
- {fileID: 8814352389997950082}

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 3075017331089573731}
m_Father: {fileID: 4100995143772770972}

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 7545758080679127068}
- {fileID: 7191491465445676253}

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 3075017331089573731}
m_RootOrder: 0

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 5676572735189107223}
- {fileID: 9095321446247494771}

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 1839420744844123086}
m_RootOrder: 1

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 8754000458150487275}
- {fileID: 2344857208759781651}

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 3810288654714679559}
- {fileID: 6936709559246390327}

89
Assets/Prefabs/UI/JoinCreateCanvas.prefab


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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 8221895778010294642}
m_Father: {fileID: 5919863887503833647}

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 6742761257817967520}
m_RootOrder: 0

- component: {fileID: 8158052573755836851}
- component: {fileID: 6665477889391988598}
- component: {fileID: 5354665607884969002}
- component: {fileID: 302397968031337808}
m_Layer: 5
m_Name: BackButton
m_TagString: Untagged

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 1659773181924692514}
m_Father: {fileID: 5919863887503833647}

m_Script: {fileID: 11500000, guid: 3c93e82eef7d613418b85194aace7f69, type: 3}
m_Name:
m_EditorClassIdentifier:
m_onVisibilityChange:
m_PersistentCalls:
m_Calls: []
--- !u!225 &302397968031337808
CanvasGroup:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3486344428529569277}
m_Enabled: 1
m_Alpha: 1
m_Interactable: 1
m_BlocksRaycasts: 1
m_IgnoreParentGroups: 0
--- !u!1 &3603267332317325680
GameObject:
m_ObjectHideFlags: 0

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 7885199827131071637}
m_RootOrder: 0

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 3691881777428488015}
- {fileID: 5836614389469498580}

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 8579668188475321874}
m_Father: {fileID: 5919863887503833647}

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 8636295960037856115}
m_RootOrder: 0

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 5919863887503833647}
m_RootOrder: 0

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 5919863887503833647}
- {fileID: 1119140321553661053}

- component: {fileID: 5836614391142406753}
- component: {fileID: 5836614391142406752}
- component: {fileID: 4578721078997909056}
- component: {fileID: 1512606419251751951}
- component: {fileID: 6102798993520257211}
m_Layer: 5
m_Name: JoinCreateCanvas

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 5836614390199223663}
m_Father: {fileID: 0}

m_StringArgument:
m_BoolArgument: 0
m_CallState: 2
m_CurrentTab: 0
--- !u!114 &1512606419251751951
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5836614391142406755}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 51373dc3c6ac79b4f8e36ac7c4419205, type: 3}
m_Name:
m_EditorClassIdentifier:
OnObservedUpdated:
m_OnTabChanged:
m_Calls:
- m_Target: {fileID: 4578721078997909056}
m_TargetAssemblyTypeName: LobbyRooms.UI.JoinCreateRoomUI, LobbyRooms
m_MethodName: ObservedUpdated
m_Mode: 0
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_Calls: []
m_CurrentTab: 0
--- !u!225 &6102798993520257211
CanvasGroup:
m_ObjectHideFlags: 0

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 5919863887503833647}
m_RootOrder: 4

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 8015712818246554090}
- {fileID: 6742761257817967520}

value:
objectReference: {fileID: 4578721078997909056}
- target: {fileID: 1462126939442648229, guid: c308ffc2a02e5ab4bbe70a8b2e8108c6, type: 3}
propertyPath: m_onVisibilityChange.m_PersistentCalls.m_Calls.Array.size
value: 2
objectReference: {fileID: 0}
- target: {fileID: 1462126939442648229, guid: c308ffc2a02e5ab4bbe70a8b2e8108c6, type: 3}
objectReference: {fileID: 0}
- target: {fileID: 1462126939442648229, guid: c308ffc2a02e5ab4bbe70a8b2e8108c6, type: 3}
propertyPath: m_onVisibilityChange.m_PersistentCalls.m_Calls.Array.data[0].m_Target
value:
objectReference: {fileID: 0}
- target: {fileID: 1462126939442648229, guid: c308ffc2a02e5ab4bbe70a8b2e8108c6, type: 3}
propertyPath: m_onVisibilityChange.m_PersistentCalls.m_Calls.Array.data[1].m_Target

objectReference: {fileID: 0}
m_RemovedComponents: []
m_SourcePrefab: {fileID: 100100000, guid: c308ffc2a02e5ab4bbe70a8b2e8108c6, type: 3}
--- !u!224 &5836614389469498580 stripped
RectTransform:
m_CorrespondingSourceObject: {fileID: 785260762106121647, guid: c308ffc2a02e5ab4bbe70a8b2e8108c6, type: 3}
m_PrefabInstance: {fileID: 6492536299820417403}
m_PrefabAsset: {fileID: 0}
--- !u!114 &5643181761482593758 stripped
--- !u!114 &2465479314273514634 stripped
m_CorrespondingSourceObject: {fileID: 1462126939442648229, guid: c308ffc2a02e5ab4bbe70a8b2e8108c6, type: 3}
m_CorrespondingSourceObject: {fileID: 8659642538454988273, guid: c308ffc2a02e5ab4bbe70a8b2e8108c6, type: 3}
m_Script: {fileID: 11500000, guid: 3b8c744e110596042b40ee73862efaab, type: 3}
m_Script: {fileID: 11500000, guid: 2da0c512f12947e489f739169773d7ca, type: 3}
--- !u!114 &2465479314273514634 stripped
--- !u!114 &5643181761482593758 stripped
m_CorrespondingSourceObject: {fileID: 8659642538454988273, guid: c308ffc2a02e5ab4bbe70a8b2e8108c6, type: 3}
m_CorrespondingSourceObject: {fileID: 1462126939442648229, guid: c308ffc2a02e5ab4bbe70a8b2e8108c6, type: 3}
m_Script: {fileID: 11500000, guid: 2da0c512f12947e489f739169773d7ca, type: 3}
m_Script: {fileID: 11500000, guid: 3b8c744e110596042b40ee73862efaab, type: 3}
--- !u!224 &5836614389469498580 stripped
RectTransform:
m_CorrespondingSourceObject: {fileID: 785260762106121647, guid: c308ffc2a02e5ab4bbe70a8b2e8108c6, type: 3}
m_PrefabInstance: {fileID: 6492536299820417403}
m_PrefabAsset: {fileID: 0}

171
Assets/Prefabs/UI/LobbyCanvas.prefab


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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 1906352097507614706}
m_RootOrder: 1

m_Component:
- component: {fileID: 2244251207921394025}
- component: {fileID: 5434623004655309427}
- component: {fileID: 3687075862038422248}
- component: {fileID: 3403950992349691351}
m_Layer: 5
m_Name: LobbyCanvas

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 6520888008628790540}
- {fileID: 2244251208239780121}

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: 7.4999876}
m_AnchoredPosition: {x: 0, y: 7.5}
m_SizeDelta: {x: -50, y: -14.999975}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!225 &5434623004655309427

m_Interactable: 1
m_BlocksRaycasts: 1
m_IgnoreParentGroups: 0
--- !u!114 &3687075862038422248
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2244251207921394026}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 51373dc3c6ac79b4f8e36ac7c4419205, type: 3}
m_Name:
m_EditorClassIdentifier:
OnObservedUpdated:
m_PersistentCalls:
m_Calls:
- m_Target: {fileID: 3403950992349691351}
m_TargetAssemblyTypeName: LobbyRooms.GameStatePanelUI, Assembly-CSharp
m_MethodName: ObservedUpdated
m_Mode: 0
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
--- !u!114 &3403950992349691351
MonoBehaviour:
m_ObjectHideFlags: 0

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 466693923092094802}
- {fileID: 1529153573702653751}

m_Component:
- component: {fileID: 466693923092094802}
- component: {fileID: 2177115228008557851}
- component: {fileID: 5843558592166708181}
- component: {fileID: 6860436446719602335}
- component: {fileID: 6749879306276389991}
m_Layer: 5

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 4212636328457333019}
m_Father: {fileID: 6520888008628790540}

m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4637522307789944801}
m_CullTransparentMesh: 1
--- !u!114 &5843558592166708181
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4637522307789944801}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 70dfc2fde0a9ef04eaff29a138f0bf45, type: 3}
m_Name:
m_EditorClassIdentifier:
OnObservedUpdated:
m_PersistentCalls:
m_Calls:
- m_Target: {fileID: 6860436446719602335}
m_TargetAssemblyTypeName: LobbyRooms.UI.ServerNameUI, Assembly-CSharp
m_MethodName: ObservedUpdated
m_Mode: 0
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
--- !u!114 &6860436446719602335
MonoBehaviour:
m_ObjectHideFlags: 0

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 1906352097507614706}
m_RootOrder: 0

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 466693923092094802}
m_RootOrder: 0

m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
m_IsActive: 0
--- !u!224 &8966797402027724594
RectTransform:
m_ObjectHideFlags: 0

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 1906352097507614706}
- {fileID: 2957117094117392419}

m_Component:
- component: {fileID: 1906352097507614706}
- component: {fileID: 8834330287924717091}
- component: {fileID: 7526558453095601547}
- component: {fileID: 279783410280127446}
- component: {fileID: 8630015524497407890}
m_Layer: 5

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 6532118317105062884}
- {fileID: 7445706979321241619}

m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7079534792968695919}
m_CullTransparentMesh: 1
--- !u!114 &7526558453095601547
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7079534792968695919}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 70dfc2fde0a9ef04eaff29a138f0bf45, type: 3}
m_Name:
m_EditorClassIdentifier:
OnObservedUpdated:
m_PersistentCalls:
m_Calls:
- m_Target: {fileID: 279783410280127446}
m_TargetAssemblyTypeName: LobbyRooms.UI.ServerAddressUI, LobbyRooms
m_MethodName: ObservedUpdated
m_Mode: 0
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
--- !u!114 &279783410280127446
MonoBehaviour:
m_ObjectHideFlags: 0

objectReference: {fileID: 0}
- target: {fileID: 1919168897190896396, guid: 2ff073ec9c74c8942bd90a541dc41bfc, type: 3}
propertyPath: m_AnchorMax.y
value: 0
value: 1
value: 0
value: 1
value: 0
value: 50
value: 0
value: 50
value: 0
value: 186.75
value: 0
value: -27.5
objectReference: {fileID: 0}
- target: {fileID: 2056817220376623591, guid: 2ff073ec9c74c8942bd90a541dc41bfc, type: 3}
propertyPath: m_AnchorMax.y

objectReference: {fileID: 3223430358889797224}
- target: {fileID: 3210254045315593125, guid: 2ff073ec9c74c8942bd90a541dc41bfc, type: 3}
propertyPath: m_AnchorMax.y
value: 0
value: 1
value: 0
value: 1
value: 0
value: 50
value: 0
value: 50
value: 0
value: 27.749992
value: 0
value: -27.5
value: 0
value: 1
value: 0
value: 1
value: 0
value: 50
value: 0
value: 50
value: 0
value: 80.74999
value: 0
value: -27.5
objectReference: {fileID: 0}
- target: {fileID: 3592018725093616804, guid: 2ff073ec9c74c8942bd90a541dc41bfc, type: 3}
propertyPath: m_fontColor.b

objectReference: {fileID: 0}
- target: {fileID: 4467363028704636643, guid: 2ff073ec9c74c8942bd90a541dc41bfc, type: 3}
propertyPath: m_fontSize
value: 13
value: 12
objectReference: {fileID: 0}
- target: {fileID: 4558362294547660329, guid: 2ff073ec9c74c8942bd90a541dc41bfc, type: 3}
propertyPath: m_AnchorMax.y

objectReference: {fileID: 0}
- target: {fileID: 6664205945102926799, guid: 2ff073ec9c74c8942bd90a541dc41bfc, type: 3}
propertyPath: m_AnchorMax.y
value: 0
value: 1
value: 0
value: 1
value: 0
value: 50
value: 0
value: 50
value: 0
value: 133.75
value: 0
value: -27.5
objectReference: {fileID: 0}
- target: {fileID: 6700616695629081094, guid: 2ff073ec9c74c8942bd90a541dc41bfc, type: 3}
propertyPath: m_fontSize

propertyPath: m_AnchoredPosition.y
value: 0
objectReference: {fileID: 0}
m_RemovedComponents: []
m_RemovedComponents:
- {fileID: 6887166789349504240, guid: 2ff073ec9c74c8942bd90a541dc41bfc, type: 3}
- {fileID: 3235578093204888586, guid: 2ff073ec9c74c8942bd90a541dc41bfc, type: 3}
- {fileID: 2586128159150077185, guid: 2ff073ec9c74c8942bd90a541dc41bfc, type: 3}
- {fileID: 2853056286508716223, guid: 2ff073ec9c74c8942bd90a541dc41bfc, type: 3}
- {fileID: 8850815072349184999, guid: 2ff073ec9c74c8942bd90a541dc41bfc, type: 3}
m_SourcePrefab: {fileID: 100100000, guid: 2ff073ec9c74c8942bd90a541dc41bfc, type: 3}
--- !u!224 &3566036481622844614 stripped
RectTransform:

objectReference: {fileID: 0}
m_RemovedComponents: []
m_SourcePrefab: {fileID: 100100000, guid: 81e22025e6659264a8d0bc8dfebe95a7, type: 3}
--- !u!224 &1529153573702653751 stripped
RectTransform:
m_CorrespondingSourceObject: {fileID: 1118541987231860827, guid: 81e22025e6659264a8d0bc8dfebe95a7, type: 3}
m_PrefabInstance: {fileID: 1926829325626138476}
m_PrefabAsset: {fileID: 0}
--- !u!114 &2768469221279893250 stripped
MonoBehaviour:
m_CorrespondingSourceObject: {fileID: 4383951900022528110, guid: 81e22025e6659264a8d0bc8dfebe95a7, type: 3}

m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!224 &1529153573702653751 stripped
RectTransform:
m_CorrespondingSourceObject: {fileID: 1118541987231860827, guid: 81e22025e6659264a8d0bc8dfebe95a7, type: 3}
m_PrefabInstance: {fileID: 1926829325626138476}
m_PrefabAsset: {fileID: 0}
--- !u!1001 &2510549639321509767
PrefabInstance:
m_ObjectHideFlags: 0

objectReference: {fileID: 0}
- target: {fileID: 840905996306701940, guid: e269788e17cbca145bf78e8971aeb223, type: 3}
propertyPath: m_fontSize
value: 25
value: 21.3
objectReference: {fileID: 0}
- target: {fileID: 929943731109783885, guid: e269788e17cbca145bf78e8971aeb223, type: 3}
propertyPath: m_AnchorMax.y

objectReference: {fileID: 0}
- target: {fileID: 3246194187207366366, guid: e269788e17cbca145bf78e8971aeb223, type: 3}
propertyPath: m_fontSize
value: 25
value: 21.3
objectReference: {fileID: 0}
- target: {fileID: 3253464371495375142, guid: e269788e17cbca145bf78e8971aeb223, type: 3}
propertyPath: m_AnchorMax.y

objectReference: {fileID: 0}
- target: {fileID: 4917538085660885383, guid: e269788e17cbca145bf78e8971aeb223, type: 3}
propertyPath: m_fontSize
value: 25
value: 21.3
objectReference: {fileID: 0}
- target: {fileID: 5186258928042496532, guid: e269788e17cbca145bf78e8971aeb223, type: 3}
propertyPath: m_fontSize

objectReference: {fileID: 0}
- target: {fileID: 7824963406237393945, guid: e269788e17cbca145bf78e8971aeb223, type: 3}
propertyPath: m_fontSize
value: 25
value: 21.3
objectReference: {fileID: 0}
- target: {fileID: 8020923114782963594, guid: e269788e17cbca145bf78e8971aeb223, type: 3}
propertyPath: m_fontSize

34
Assets/Prefabs/UI/LobbyCodeCanvas.prefab


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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 7356978206806771932}
m_RootOrder: 1

- component: {fileID: 1118541987231860831}
- component: {fileID: 6772704612072308136}
- component: {fileID: 1118541987231860826}
- component: {fileID: 699060394989383769}
m_Layer: 5
m_Name: LobbyCodeCanvas
m_TagString: Untagged

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 1060855158700719950}
- {fileID: 7974420753511511685}

m_Calls: []
m_outputText: {fileID: 5578852939709204548}
m_codeType: 0
--- !u!114 &699060394989383769
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1118541987231860824}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 70dfc2fde0a9ef04eaff29a138f0bf45, type: 3}
m_Name:
m_EditorClassIdentifier:
OnObservedUpdated:
m_PersistentCalls:
m_Calls:
- m_Target: {fileID: 1118541987231860826}
m_TargetAssemblyTypeName: LobbyRooms.UI.RoomCodeUI, LobbyRooms
m_MethodName: ObservedUpdated
m_Mode: 0
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
--- !u!1 &2798863108443093305
GameObject:
m_ObjectHideFlags: 0

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 7356978206806771932}
m_Father: {fileID: 1118541987231860827}

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 7356978206806771932}
m_RootOrder: 0

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 666358594839851073}
- {fileID: 2985101570411328694}

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 1118541987231860827}
m_RootOrder: 0

106
Assets/Prefabs/UI/LobbyUserList.prefab


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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 8268348179057556550}
m_Father: {fileID: 8316201547685935311}

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 1550284535874684459}
m_Father: {fileID: 8316201547685935311}

- component: {fileID: 4463750083940306577}
- component: {fileID: 4463750083940306576}
- component: {fileID: 4463750083940306591}
- component: {fileID: 1553725903561197374}
m_Layer: 5
m_Name: LobbyUserList
m_TagString: Untagged

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 7187078656748750641}
- {fileID: 8594180776620219635}

m_FallbackScreenDPI: 96
m_DefaultSpriteDPI: 96
m_DynamicPixelsPerUnit: 1
m_PresetInfoIsWorld: 1
m_PresetInfoIsWorld: 0
--- !u!114 &4463750083940306577
MonoBehaviour:
m_ObjectHideFlags: 0

- {fileID: 4503217775887893542}
- {fileID: 1646035575854440888}
- {fileID: 6290668908104815487}
--- !u!114 &1553725903561197374
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4463750083940306589}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 70dfc2fde0a9ef04eaff29a138f0bf45, type: 3}
m_Name:
m_EditorClassIdentifier:
OnObservedUpdated:
m_PersistentCalls:
m_Calls:
- m_Target: {fileID: 4463750083940306591}
m_TargetAssemblyTypeName: LobbyRooms.UI.RoomPlayersUI, LobbyRooms
m_MethodName: ObservedUpdated
m_Mode: 0
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
--- !u!1 &5373112041518163852
GameObject:
m_ObjectHideFlags: 0

- component: {fileID: 8853547886851310217}
- component: {fileID: 633533396086944350}
- component: {fileID: 8740825503606492738}
- component: {fileID: 7056422356970708528}
m_Layer: 5
m_Name: InGamePanel
m_TagString: Untagged

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 4463750083940306590}
m_RootOrder: 1

m_PersistentCalls:
m_Calls: []
m_ShowThisWhen: 4
--- !u!114 &7056422356970708528
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5373112041518163852}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 70dfc2fde0a9ef04eaff29a138f0bf45, type: 3}
m_Name:
m_EditorClassIdentifier:
OnObservedUpdated:
m_PersistentCalls:
m_Calls:
- m_Target: {fileID: 8740825503606492738}
m_TargetAssemblyTypeName: LobbyRooms.UI.LobbyStateVisibilityUI, LobbyRooms
m_MethodName: ObservedUpdated
m_Mode: 0
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
--- !u!1 &5447062805889817511
GameObject:
m_ObjectHideFlags: 0

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 6491466947543783896}
m_Father: {fileID: 8316201547685935311}

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 9211655745301838380}
- {fileID: 2630686559100583219}

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 3685075291402800769}
m_Father: {fileID: 8316201547685935311}

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 8316201547685935311}
m_Father: {fileID: 4463750083940306590}

objectReference: {fileID: 0}
m_RemovedComponents: []
m_SourcePrefab: {fileID: 100100000, guid: 9c09208dc6d58f54aabb57c12071b660, type: 3}
--- !u!224 &3685075291402800769 stripped
RectTransform:
m_CorrespondingSourceObject: {fileID: 3229036008637484624, guid: 9c09208dc6d58f54aabb57c12071b660, type: 3}
m_PrefabInstance: {fileID: 2300164376451760337}
m_PrefabAsset: {fileID: 0}
--- !u!114 &6290668908104815487 stripped
MonoBehaviour:
m_CorrespondingSourceObject: {fileID: 5235189765028238254, guid: 9c09208dc6d58f54aabb57c12071b660, type: 3}

m_Script: {fileID: 11500000, guid: 9557a5d232068a149987bc0753800f26, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!224 &3685075291402800769 stripped
RectTransform:
m_CorrespondingSourceObject: {fileID: 3229036008637484624, guid: 9c09208dc6d58f54aabb57c12071b660, type: 3}
m_PrefabInstance: {fileID: 2300164376451760337}
m_PrefabAsset: {fileID: 0}
--- !u!1001 &4128781293466929275
PrefabInstance:
m_ObjectHideFlags: 0

objectReference: {fileID: 0}
m_RemovedComponents: []
m_SourcePrefab: {fileID: 100100000, guid: 9c09208dc6d58f54aabb57c12071b660, type: 3}
--- !u!224 &1550284535874684459 stripped
RectTransform:
m_CorrespondingSourceObject: {fileID: 3229036008637484624, guid: 9c09208dc6d58f54aabb57c12071b660, type: 3}
m_PrefabInstance: {fileID: 4128781293466929275}
m_PrefabAsset: {fileID: 0}
--- !u!114 &8208727230702623701 stripped
MonoBehaviour:
m_CorrespondingSourceObject: {fileID: 5235189765028238254, guid: 9c09208dc6d58f54aabb57c12071b660, type: 3}

m_Script: {fileID: 11500000, guid: 9557a5d232068a149987bc0753800f26, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!224 &1550284535874684459 stripped
RectTransform:
m_CorrespondingSourceObject: {fileID: 3229036008637484624, guid: 9c09208dc6d58f54aabb57c12071b660, type: 3}
m_PrefabInstance: {fileID: 4128781293466929275}
m_PrefabAsset: {fileID: 0}
--- !u!1001 &6805156706942808598
PrefabInstance:
m_ObjectHideFlags: 0

objectReference: {fileID: 0}
m_RemovedComponents: []
m_SourcePrefab: {fileID: 100100000, guid: 9c09208dc6d58f54aabb57c12071b660, type: 3}
--- !u!224 &8268348179057556550 stripped
RectTransform:
m_CorrespondingSourceObject: {fileID: 3229036008637484624, guid: 9c09208dc6d58f54aabb57c12071b660, type: 3}
m_PrefabInstance: {fileID: 6805156706942808598}
m_PrefabAsset: {fileID: 0}
--- !u!114 &1646035575854440888 stripped
MonoBehaviour:
m_CorrespondingSourceObject: {fileID: 5235189765028238254, guid: 9c09208dc6d58f54aabb57c12071b660, type: 3}

m_Script: {fileID: 11500000, guid: 9557a5d232068a149987bc0753800f26, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!224 &8268348179057556550 stripped
RectTransform:
m_CorrespondingSourceObject: {fileID: 3229036008637484624, guid: 9c09208dc6d58f54aabb57c12071b660, type: 3}
m_PrefabInstance: {fileID: 6805156706942808598}
m_PrefabAsset: {fileID: 0}
--- !u!1001 &8564023431761303432
PrefabInstance:
m_ObjectHideFlags: 0

objectReference: {fileID: 0}
m_RemovedComponents: []
m_SourcePrefab: {fileID: 100100000, guid: 9c09208dc6d58f54aabb57c12071b660, type: 3}
--- !u!224 &6491466947543783896 stripped
RectTransform:
m_CorrespondingSourceObject: {fileID: 3229036008637484624, guid: 9c09208dc6d58f54aabb57c12071b660, type: 3}
m_PrefabInstance: {fileID: 8564023431761303432}
m_PrefabAsset: {fileID: 0}
--- !u!114 &4503217775887893542 stripped
MonoBehaviour:
m_CorrespondingSourceObject: {fileID: 5235189765028238254, guid: 9c09208dc6d58f54aabb57c12071b660, type: 3}

m_Script: {fileID: 11500000, guid: 9557a5d232068a149987bc0753800f26, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!224 &6491466947543783896 stripped
RectTransform:
m_CorrespondingSourceObject: {fileID: 3229036008637484624, guid: 9c09208dc6d58f54aabb57c12071b660, type: 3}
m_PrefabInstance: {fileID: 8564023431761303432}
m_PrefabAsset: {fileID: 0}

41
Assets/Prefabs/UI/MainMenuCanvas.prefab


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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 3838875500213396203}
m_RootOrder: 0

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 1547097154335127670}
m_RootOrder: 0

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 1547097154637997989}
m_RootOrder: 1

- component: {fileID: 1547097154335127667}
- component: {fileID: 1130581240334700700}
- component: {fileID: 5258679254951932468}
- component: {fileID: 575636749548035060}
m_Layer: 5
m_Name: MainMenuCanvas
m_TagString: Untagged

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 1547097153480224111}
- {fileID: 1547097155015051324}

m_PersistentCalls:
m_Calls: []
ShowThisWhen: 1
--- !u!114 &575636749548035060
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1547097154335127666}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 51373dc3c6ac79b4f8e36ac7c4419205, type: 3}
m_Name:
m_EditorClassIdentifier:
OnObservedUpdated:
m_PersistentCalls:
m_Calls:
- m_Target: {fileID: 5258679254951932468}
m_TargetAssemblyTypeName: LobbyRooms.GameStateVisibilityUI, Assembly-CSharp
m_MethodName: ObservedUpdated
m_Mode: 0
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
--- !u!1 &1547097154637997988
GameObject:
m_ObjectHideFlags: 0

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 32978559587113065}
- {fileID: 1547097153987463586}

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: 1547097154637997989}, {fileID: 3838875500213396203}]
m_ConstrainProportionsScale: 0
m_Children:
- {fileID: 1547097154637997989}
- {fileID: 3838875500213396203}
m_Father: {fileID: 1547097154335127670}
m_RootOrder: 1
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 2256322442099666709}
- {fileID: 3557122422878236895}

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 1547097154637997989}
m_RootOrder: 0

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 3838875500213396203}
m_RootOrder: 1

34
Assets/Prefabs/UI/RelayCodeCanvas.prefab


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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 5867107070128564602}
m_RootOrder: 0

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 4488536198637490663}
- {fileID: 2168532048885830928}

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 5867107070128564602}
m_Father: {fileID: 4102997489641105917}

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 4102997489641105917}
m_RootOrder: 0

- component: {fileID: 4102997489641105913}
- component: {fileID: 7676491730539518990}
- component: {fileID: 3340928240658051873}
- component: {fileID: 4523467532116611583}
m_Layer: 5
m_Name: RelayCodeCanvas
m_TagString: Untagged

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 5389159257502379616}
- {fileID: 6476122475963766563}

m_Calls: []
m_outputText: {fileID: 8798075752901962210}
m_codeType: 1
--- !u!114 &4523467532116611583
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4102997489641105918}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 70dfc2fde0a9ef04eaff29a138f0bf45, type: 3}
m_Name:
m_EditorClassIdentifier:
OnObservedUpdated:
m_PersistentCalls:
m_Calls:
- m_Target: {fileID: 3340928240658051873}
m_TargetAssemblyTypeName: LobbyRelaySample.UI.DisplayCodeUI, LobbyRelaySample
m_MethodName: ObservedUpdated
m_Mode: 0
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
--- !u!1 &4142193733183565639
GameObject:
m_ObjectHideFlags: 0

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 5867107070128564602}
m_RootOrder: 1

35
Assets/Prefabs/UI/SpinnerUI.prefab


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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 8141644855275361747}
m_RootOrder: 1

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 8141644855275361747}
m_RootOrder: 0

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 8141644855275361747}
m_RootOrder: 2

- component: {fileID: 8141644855275361747}
- component: {fileID: 8024795096463497524}
- component: {fileID: 6317050540274997421}
- component: {fileID: 2592976293271768794}
- component: {fileID: 7469848848124085693}
- component: {fileID: 3773418773225383442}
m_Layer: 5

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 2952266133262597715}
- {fileID: 8845592316474837655}

m_noServerText: {fileID: 1576294672583716881}
m_errorTextVisibility: {fileID: 8041986936720407558}
m_raycastBlocker: {fileID: 0}
--- !u!114 &2592976293271768794
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7144088886657378797}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: edfa4d53eaae84c43ba581088940700c, type: 3}
m_Name:
m_EditorClassIdentifier:
OnObservedUpdated:
m_PersistentCalls:
m_Calls:
- m_Target: {fileID: 6317050540274997421}
m_TargetAssemblyTypeName: LobbyRooms.UI.SpinnerUI, Assembly-CSharp
m_MethodName: ObservedUpdated
m_Mode: 0
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
serializedVersion: 3
serializedVersion: 4
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}

m_UpdateMode: 0
m_ApplyRootMotion: 0
m_LinearVelocityBlending: 0
m_StabilizeFeet: 0
m_WarningMessage:
m_HasTransformHierarchy: 1
m_AllowConstantClipSamplingOptimization: 1

58
Assets/Prefabs/UI/UserCardPanel.prefab


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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 5259773757747180216}
m_Father: {fileID: 4798292317257423901}

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 2130654709780603783}
- {fileID: 2263383039778102024}

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 2679964701276034275}
m_Father: {fileID: 3229036008637484624}

- component: {fileID: 1520530101828604145}
- component: {fileID: 6727759267638039682}
- component: {fileID: 5235189765028238254}
- component: {fileID: 788426075660952210}
- component: {fileID: 7885056472121154813}
- component: {fileID: 4826507163535553981}
m_Layer: 5

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 2599817509811505645}
- {fileID: 5804120253616419419}

- {fileID: 21300000, guid: bc8c0e2bc04ce93488efe4ea898047a9, type: 3}
- {fileID: 21300000, guid: f18a89c12a346a8488ed507a0bd79d2e, type: 3}
- {fileID: 21300000, guid: 0fca28892f34375439c32b1ee1c8d655, type: 3}
m_vivoxUserHandler: {fileID: 4826507163535553981}
--- !u!114 &788426075660952210
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1767503274657767312}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: a03b37d5b8df06948b36dfbc430a1ea5, type: 3}
m_Name:
m_EditorClassIdentifier:
OnObservedUpdated:
m_PersistentCalls:
m_Calls:
- m_Target: {fileID: 5235189765028238254}
m_TargetAssemblyTypeName: LobbyRooms.UI.PlayerCardUI, LobbyRooms
m_MethodName: ObservedUpdated
m_Mode: 0
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_VivoxUserHandler: {fileID: 4826507163535553981}
--- !u!114 &7885056472121154813
MonoBehaviour:
m_ObjectHideFlags: 0

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 5144918951328900891}
m_Father: {fileID: 3229036008637484624}

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 693969737454053021}
m_RootOrder: 0

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 4798292317257423901}
m_Father: {fileID: 3081577339344251787}

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 4798292317257423901}
m_RootOrder: 1

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 1122323841334202136}
- {fileID: 853418489715471536}

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 5804120253616419419}
m_RootOrder: 1

m_faceColor:
serializedVersion: 2
rgba: 4294967295
m_fontSize: 38.8
m_fontSize: 70.95
m_fontSizeBase: 36
m_fontWeight: 400
m_enableAutoSizing: 1

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 5804120253616419419}
m_RootOrder: 0

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 6576240096323944936}
m_Father: {fileID: 3229036008637484624}

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 693969737454053021}
m_Father: {fileID: 3229036008637484624}

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 814149882275566360}
m_RootOrder: 0

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 8775333608543036802}
- {fileID: 131896552161369554}

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 131896552161369554}
m_RootOrder: 0

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 2599817509811505645}
m_RootOrder: 0

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 7829259871691718144}
m_Father: {fileID: 693969737454053021}

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 7160561887711141280}
m_Father: {fileID: 3229036008637484624}

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 7147564553318460541}
m_RootOrder: 0

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 6125340877524701612}
m_Father: {fileID: 693969737454053021}

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 1122323841334202136}
m_RootOrder: 0

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 4798292317257423901}
m_RootOrder: 2

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 2889154384621986752}
m_RootOrder: 0

m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_text: '<color=#ADD8E6>Lobby
m_text: '<color=#ADD8E6>In Lobby
'
m_isRightToLeft: 0

371
Assets/Prefabs/UI/UserInteractionPanel.prefab


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: 0.999}
m_ConstrainProportionsScale: 0
m_Children:
- {fileID: 2119950809865779987}
m_Father: {fileID: 6664205945102926799}

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 474140199862162053}
m_RootOrder: 0

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 1611213509401803489}
m_RootOrder: 2

m_Script: {fileID: 11500000, guid: 5b3b588e7ae40ec4ca35fdb9404513ab, type: 3}
m_Name:
m_EditorClassIdentifier:
OnObservedUpdated:
m_onVisibilityChange:
m_PersistentCalls:
m_Calls: []
m_CountDownText: {fileID: 4467363028704636643}

- component: {fileID: 1611213509401803489}
- component: {fileID: 5811625806911023419}
- component: {fileID: 5977596652360143959}
- component: {fileID: 2113442049023149911}
- component: {fileID: 4811792094336013573}
- component: {fileID: 435124129857761185}
m_Layer: 5

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 8979361099148208042}
- {fileID: 1135759803389522016}

m_ChildScaleWidth: 0
m_ChildScaleHeight: 0
m_ReverseArrangement: 0
--- !u!114 &2113442049023149911
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 609596107351326384}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 70dfc2fde0a9ef04eaff29a138f0bf45, type: 3}
m_Name:
m_EditorClassIdentifier:
OnObservedUpdated:
m_PersistentCalls:
m_Calls:
- m_Target: {fileID: 435124129857761185}
m_TargetAssemblyTypeName: LobbyRooms.UI.LobbyStateVisibilityUI, LobbyRooms
m_MethodName: ObservedUpdated
m_Mode: 0
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
--- !u!225 &4811792094336013573
CanvasGroup:
m_ObjectHideFlags: 0

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: 0.999}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 1135759803389522016}
m_RootOrder: 0

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 8894954517847697079}
- {fileID: 506300669760714432}

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 6979352419326739443}
- {fileID: 7173726187766461959}

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 4215597292682722394}
- {fileID: 2758617697121517356}

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 6541171577144360681}
m_RootOrder: 1

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: 0.999}
m_ConstrainProportionsScale: 0
m_Children:
- {fileID: 4315664788983455717}
m_Father: {fileID: 1919168897190896396}

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 1309712272618711075}
m_RootOrder: 1

m_faceColor:
serializedVersion: 2
rgba: 4294967295
m_fontSize: 72
m_fontSize: 22.2
m_fontSizeBase: 24
m_fontWeight: 400
m_enableAutoSizing: 1

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 1135759803389522016}
m_RootOrder: 1

m_faceColor:
serializedVersion: 2
rgba: 4294967295
m_fontSize: 56.8
m_fontSize: 18
m_fontSizeBase: 24
m_fontWeight: 400
m_enableAutoSizing: 1

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 1611213509401803489}
m_RootOrder: 0

m_faceColor:
serializedVersion: 2
rgba: 4294967295
m_fontSize: 23
m_fontSize: 13
m_fontSizeBase: 36
m_fontWeight: 400
m_enableAutoSizing: 1

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 5151586559654887469}
m_RootOrder: 0

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 6870502395855781705}
- {fileID: 6666831914584290858}

m_Calls:
- m_Target: {fileID: 2987822160017223264}
m_TargetAssemblyTypeName: LobbyRelaySample.UI.RecolorForLobbyType, LobbyRelaySample
m_MethodName: ChangeColor
m_Mode: 3
m_MethodName: ToggleOrange
m_Mode: 0
m_Arguments:
m_ObjectArgument: {fileID: 0}
m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 4191270325501815023}
- {fileID: 1770059644204541294}

- component: {fileID: 6712653532449929400}
- component: {fileID: 8508575087870636667}
- component: {fileID: 6640185585145895720}
- component: {fileID: 4800227646832904480}
m_Layer: 5
m_Name: TongueIcon
m_TagString: Untagged

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 233260285268708796}
m_Father: {fileID: 5026269005358103012}

m_AnchorMax: {x: 0, y: 1}
m_AnchoredPosition: {x: 273.5, y: -376.5}
m_AnchoredPosition: {x: 208.5, y: -27.5}
m_SizeDelta: {x: 50, y: 50}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &5387756616337415243

m_Script: {fileID: 11500000, guid: 389b6bc508595ea4c920cbaa3d21fc54, type: 3}
m_Name:
m_EditorClassIdentifier:
m_onVisibilityChange:
m_PersistentCalls:
m_Calls: []
--- !u!225 &4800227646832904480
CanvasGroup:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2186823305087296033}
m_Enabled: 1
m_Alpha: 1
m_Interactable: 1
m_BlocksRaycasts: 1
m_IgnoreParentGroups: 0
--- !u!1 &2835147694615859225
GameObject:
m_ObjectHideFlags: 0

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 2848144350161010536}
- {fileID: 1150714971814560546}

m_Calls:
- m_Target: {fileID: 2987822160017223264}
m_TargetAssemblyTypeName: LobbyRelaySample.UI.RecolorForLobbyType, LobbyRelaySample
m_MethodName: ChangeColor
m_Mode: 3
m_MethodName: ToggleGreen
m_Mode: 0
m_Arguments:
m_ObjectArgument: {fileID: 0}
m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine

m_Component:
- component: {fileID: 794717087570398863}
- component: {fileID: 2987822160017223264}
- component: {fileID: 26462937623057251}
m_Layer: 5
m_Name: ToggleContainer
m_TagString: Untagged

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 5272370078138508777}
m_Father: {fileID: 5151586559654887469}

m_Script: {fileID: 11500000, guid: 4079cd003fcd20c40a3bac78acf44b55, type: 3}
m_Name:
m_EditorClassIdentifier:
m_UseLocalLobby: 1
--- !u!114 &26462937623057251
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2945500889587214856}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 70dfc2fde0a9ef04eaff29a138f0bf45, type: 3}
m_Name:
m_EditorClassIdentifier:
OnObservedUpdated:
m_PersistentCalls:
m_Calls:
- m_Target: {fileID: 2987822160017223264}
m_TargetAssemblyTypeName: LobbyRelaySample.UI.RecolorForLobbyType, LobbyRelaySample
m_MethodName: UpdateLobby
m_Mode: 0
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
--- !u!1 &2988716332856923472
GameObject:
m_ObjectHideFlags: 0

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 6004833744538984969}
m_RootOrder: 1

m_faceColor:
serializedVersion: 2
rgba: 4294967295
m_fontSize: 72
m_fontSize: 22.2
m_fontSizeBase: 24
m_fontWeight: 400
m_enableAutoSizing: 1

m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1.02, y: 1.02, z: 1.02}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 2848144350161010536}
m_RootOrder: 0

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 3197502779878208215}
m_RootOrder: 1

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 2677764702249492100}
- {fileID: 1568259630799150366}

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: 0.999}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 1309712272618711075}
m_RootOrder: 0

- component: {fileID: 5151586559654887469}
- component: {fileID: 8227063393050474168}
- component: {fileID: 38810562540460648}
- component: {fileID: 2853056286508716223}
- component: {fileID: 6834908889545701957}
m_Layer: 5
m_Name: LobbyTypeContainer

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 5543134037343114586}
- {fileID: 794717087570398863}

m_Interactable: 1
m_BlocksRaycasts: 1
m_IgnoreParentGroups: 0
--- !u!114 &2853056286508716223
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4330603859870806951}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: a03b37d5b8df06948b36dfbc430a1ea5, type: 3}
m_Name:
m_EditorClassIdentifier:
OnObservedUpdated:
m_PersistentCalls:
m_Calls:
- m_Target: {fileID: 6834908889545701957}
m_TargetAssemblyTypeName: LobbyRelaySample.UI.UserStateVisibilityUI, LobbyRelaySample
m_MethodName: ObservedUpdated
m_Mode: 0
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
--- !u!114 &6834908889545701957
MonoBehaviour:
m_ObjectHideFlags: 0

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 3620679801590011701}
m_RootOrder: 0

- component: {fileID: 6463354546789934473}
- component: {fileID: 1712688506883075726}
- component: {fileID: 1163834802805469247}
- component: {fileID: 4914536845037457902}
m_Layer: 5
m_Name: SmileIcon
m_TagString: Untagged

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 474140199862162053}
m_Father: {fileID: 5026269005358103012}

m_AnchorMax: {x: 0, y: 1}
m_AnchoredPosition: {x: 114.5, y: -376.5}
m_AnchoredPosition: {x: 49.5, y: -27.5}
m_SizeDelta: {x: 50, y: 50}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &8764544246360663793

m_Script: {fileID: 11500000, guid: 389b6bc508595ea4c920cbaa3d21fc54, type: 3}
m_Name:
m_EditorClassIdentifier:
m_onVisibilityChange:
m_PersistentCalls:
m_Calls: []
--- !u!225 &4914536845037457902
CanvasGroup:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4470000697835183986}
m_Enabled: 1
m_Alpha: 1
m_Interactable: 1
m_BlocksRaycasts: 1
m_IgnoreParentGroups: 0
--- !u!1 &4475720551491962531
GameObject:
m_ObjectHideFlags: 0

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 6075935838108688747}
m_RootOrder: 1

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 2056817220376623591}
- {fileID: 5151586559654887469}

- component: {fileID: 759037105273515699}
- component: {fileID: 8742429799833895606}
- component: {fileID: 3840037590007163527}
- component: {fileID: 3235578093204888586}
- component: {fileID: 9048520512602473890}
m_Layer: 5
m_Name: ReadyPanel

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 6004833744538984969}
m_Father: {fileID: 4558362294547660329}

m_Interactable: 1
m_BlocksRaycasts: 1
m_IgnoreParentGroups: 0
--- !u!114 &3235578093204888586
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4830900314091476641}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: a03b37d5b8df06948b36dfbc430a1ea5, type: 3}
m_Name:
m_EditorClassIdentifier:
OnObservedUpdated:
m_PersistentCalls:
m_Calls:
- m_Target: {fileID: 9048520512602473890}
m_TargetAssemblyTypeName: LobbyRooms.UI.UserStateVisibilityUI, LobbyRooms
m_MethodName: ObservedUpdated
m_Mode: 0
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
--- !u!114 &9048520512602473890
MonoBehaviour:
m_ObjectHideFlags: 0

- component: {fileID: 6687484736792569641}
- component: {fileID: 8495636767461452457}
- component: {fileID: 4681140549745913102}
- component: {fileID: 5360291397379197498}
m_Layer: 5
m_Name: UserButtonBG
m_TagString: Untagged

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 759037105273515699}
- {fileID: 1108938163892239274}

m_Script: {fileID: 11500000, guid: bd24e242d9385c244b4c0a635949b1a5, type: 3}
m_Name:
m_EditorClassIdentifier:
m_onVisibilityChange:
m_PersistentCalls:
m_Calls: []
--- !u!225 &5360291397379197498
CanvasGroup:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5340674743026918435}
m_Enabled: 1
m_Alpha: 1
m_Interactable: 1
m_BlocksRaycasts: 1
m_IgnoreParentGroups: 0
--- !u!1 &5409100620800317906
GameObject:
m_ObjectHideFlags: 0

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 233260285268708796}
m_RootOrder: 0

- component: {fileID: 6977435345365325268}
- component: {fileID: 2928554626579320494}
- component: {fileID: 6389975608916618572}
- component: {fileID: 2361201349482207691}
m_Layer: 5
m_Name: UnamusedIcon
m_TagString: Untagged

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 5671183532491584052}
m_Father: {fileID: 5026269005358103012}

m_AnchorMax: {x: 0, y: 1}
m_AnchoredPosition: {x: 220.5, y: -376.5}
m_AnchoredPosition: {x: 155.5, y: -27.5}
m_SizeDelta: {x: 50, y: 50}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &4202478155384707071

m_Script: {fileID: 11500000, guid: 389b6bc508595ea4c920cbaa3d21fc54, type: 3}
m_Name:
m_EditorClassIdentifier:
m_onVisibilityChange:
m_PersistentCalls:
m_Calls: []
--- !u!225 &2361201349482207691
CanvasGroup:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5411716139989892340}
m_Enabled: 1
m_Alpha: 1
m_Interactable: 1
m_BlocksRaycasts: 1
m_IgnoreParentGroups: 0
--- !u!1 &5626941668538444755
GameObject:
m_ObjectHideFlags: 0

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 8540831390494494413}
- {fileID: 2147051606546077830}

m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1.02, y: 1.02, z: 1.02}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 3197502779878208215}
m_RootOrder: 0

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 2848144350161010536}
m_RootOrder: 1

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 4080264242426406278}
- {fileID: 6424283572969246263}

m_Calls:
- m_Target: {fileID: 2987822160017223264}
m_TargetAssemblyTypeName: LobbyRelaySample.UI.RecolorForLobbyType, LobbyRelaySample
m_MethodName: ChangeColor
m_Mode: 3
m_MethodName: ToggleWhite
m_Mode: 0
m_Arguments:
m_ObjectArgument: {fileID: 0}
m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 1883476059105349212}
- {fileID: 7811003188552691971}

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: 0.999}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 6004833744538984969}
m_RootOrder: 0

- component: {fileID: 4844555698956727370}
- component: {fileID: 8638827337113238188}
- component: {fileID: 1619100575765837454}
- component: {fileID: 6887166789349504240}
- component: {fileID: 5998684818406589344}
m_Layer: 5
m_Name: CancelPanel

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 1611213509401803489}
m_Father: {fileID: 4558362294547660329}

m_Script: {fileID: 11500000, guid: bd24e242d9385c244b4c0a635949b1a5, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!114 &6887166789349504240
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6223322455083519911}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: a03b37d5b8df06948b36dfbc430a1ea5, type: 3}
m_Name:
m_EditorClassIdentifier:
OnObservedUpdated:
m_onVisibilityChange:
m_Calls:
- m_Target: {fileID: 5998684818406589344}
m_TargetAssemblyTypeName: LobbyRooms.UI.UserStateVisibilityUI, LobbyRooms
m_MethodName: ObservedUpdated
m_Mode: 0
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_Calls: []
--- !u!114 &5998684818406589344
MonoBehaviour:
m_ObjectHideFlags: 0

- component: {fileID: 5026269005358103012}
- component: {fileID: 6337293324110520399}
- component: {fileID: 8045047639899645949}
- component: {fileID: 8850815072349184999}
- component: {fileID: 7597047770691248045}
m_Layer: 5
m_Name: EmoteButtons

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 3210254045315593125}
- {fileID: 3214500912641965185}

m_Interactable: 1
m_BlocksRaycasts: 1
m_IgnoreParentGroups: 0
--- !u!114 &8850815072349184999
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6356789002927649657}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: a03b37d5b8df06948b36dfbc430a1ea5, type: 3}
m_Name:
m_EditorClassIdentifier:
OnObservedUpdated:
m_PersistentCalls:
m_Calls:
- m_Target: {fileID: 7597047770691248045}
m_TargetAssemblyTypeName: LobbyRelaySample.UI.UserStateVisibilityUI, LobbyRelaySample
m_MethodName: ObservedUpdated
m_Mode: 0
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
--- !u!114 &7597047770691248045
MonoBehaviour:
m_ObjectHideFlags: 0

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 5026269005358103012}
m_Father: {fileID: 8905267601204628791}

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 1568259630799150366}
m_RootOrder: 1

- component: {fileID: 6919891912281880971}
- component: {fileID: 1519148326278112938}
- component: {fileID: 3953612155158603721}
- component: {fileID: 2586128159150077185}
- component: {fileID: 5356355227050787427}
- component: {fileID: 900710893788416922}
m_Layer: 5

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 1309712272618711075}
m_Father: {fileID: 4558362294547660329}

m_Interactable: 0
m_BlocksRaycasts: 0
m_IgnoreParentGroups: 0
--- !u!114 &2586128159150077185
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6897031386683883020}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: a03b37d5b8df06948b36dfbc430a1ea5, type: 3}
m_Name:
m_EditorClassIdentifier:
OnObservedUpdated:
m_PersistentCalls:
m_Calls:
- m_Target: {fileID: 5356355227050787427}
m_TargetAssemblyTypeName: LobbyRooms.UI.UserStateVisibilityUI, LobbyRooms
m_MethodName: ObservedUpdated
m_Mode: 0
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
--- !u!114 &5356355227050787427
MonoBehaviour:
m_ObjectHideFlags: 0

m_Script: {fileID: 11500000, guid: 268bfa80f12de0345a051323f3279616, type: 3}
m_Name:
m_EditorClassIdentifier:
m_onVisibilityChange:
m_PersistentCalls:
m_Calls: []
--- !u!1 &6903556594047626237
GameObject:
m_ObjectHideFlags: 0

m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1.02, y: 1.02, z: 1.02}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 4080264242426406278}
m_RootOrder: 0

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 6870502395855781705}
m_RootOrder: 1

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 2677764702249492100}
m_RootOrder: 1

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 3197502779878208215}
- {fileID: 6159542077236467165}

m_Calls:
- m_Target: {fileID: 2987822160017223264}
m_TargetAssemblyTypeName: LobbyRelaySample.UI.RecolorForLobbyType, LobbyRelaySample
m_MethodName: ChangeColor
m_Mode: 3
m_MethodName: ToggleBlue
m_Mode: 0
m_Arguments:
m_ObjectArgument: {fileID: 0}
m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 8905267601204628791}
m_Father: {fileID: 0}

- component: {fileID: 7784475288331796339}
- component: {fileID: 588056481165772144}
- component: {fileID: 3023764238805683497}
- component: {fileID: 3726981029093688172}
m_Layer: 5
m_Name: FrownIcon
m_TagString: Untagged

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 3620679801590011701}
m_Father: {fileID: 5026269005358103012}

m_AnchorMax: {x: 0, y: 1}
m_AnchoredPosition: {x: 167.5, y: -376.5}
m_AnchoredPosition: {x: 102.5, y: -27.5}
m_SizeDelta: {x: 50, y: 50}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &4168724985671927024

m_Script: {fileID: 11500000, guid: 389b6bc508595ea4c920cbaa3d21fc54, type: 3}
m_Name:
m_EditorClassIdentifier:
m_onVisibilityChange:
m_PersistentCalls:
m_Calls: []
--- !u!225 &3726981029093688172
CanvasGroup:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7280943722809746719}
m_Enabled: 1
m_Alpha: 1
m_Interactable: 1
m_BlocksRaycasts: 1
m_IgnoreParentGroups: 0
--- !u!1 &7501509171414083009
GameObject:
m_ObjectHideFlags: 0

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: 0.999}
m_ConstrainProportionsScale: 0
m_Children:
- {fileID: 3404880114691434542}
m_Father: {fileID: 3210254045315593125}

m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1.02, y: 1.02, z: 1.02}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 6870502395855781705}
m_RootOrder: 0

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: 0.999}
m_ConstrainProportionsScale: 0
m_Children:
- {fileID: 6205733139118454793}
m_Father: {fileID: 3214500912641965185}

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 4080264242426406278}
m_RootOrder: 1

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 552927569557660881}
- {fileID: 7414980691921613268}

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 5671183532491584052}
m_RootOrder: 0

49
Assets/Prefabs/UI/LobbyEntryUI.prefab


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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 9095092682662508748}
m_RootOrder: 0

- component: {fileID: 4172744935978053658}
- component: {fileID: 5345794337443349844}
- component: {fileID: 7018369548608736188}
- component: {fileID: 843756277828315513}
m_Name: LobbyButtonUI
m_Name: LobbyEntryUI
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0

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_ConstrainProportionsScale: 0
m_Children:
- {fileID: 7388874116200379571}
- {fileID: 2246652449813468979}

m_Script: {fileID: 11500000, guid: b408c4b47ec1dd74e8708c04dc35a8fd, type: 3}
m_Name:
m_EditorClassIdentifier:
m_ColorLobbyUI: {fileID: 6515571473500817606}
--- !u!114 &843756277828315513
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6423115675995281648}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 70dfc2fde0a9ef04eaff29a138f0bf45, type: 3}
m_Name:
m_EditorClassIdentifier:
OnObservedUpdated:
m_PersistentCalls:
m_Calls:
- m_Target: {fileID: 7018369548608736188}
m_TargetAssemblyTypeName: LobbyRelaySample.UI.LobbyButtonUI, LobbyRelaySample
m_MethodName: OnLobbyUpdated
m_Mode: 0
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_Target: {fileID: 6515571473500817606}
m_TargetAssemblyTypeName: LobbyRelaySample.UI.RecolorForLobbyType, LobbyRelaySample
m_MethodName: UpdateLobby
m_Mode: 0
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
--- !u!114 &1059336587790472163
MonoBehaviour:
m_ObjectHideFlags: 0

m_Script: {fileID: 11500000, guid: 4079cd003fcd20c40a3bac78acf44b55, type: 3}
m_Name:
m_EditorClassIdentifier:
m_UseLocalLobby: 0
m_toRecolor:
- {fileID: 4172744935978053658}
--- !u!1 &8569242987132969498

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 9095092682662508748}
m_RootOrder: 1

m_faceColor:
serializedVersion: 2
rgba: 4294967295
m_fontSize: 32
m_fontSize: 31.65
m_fontSizeBase: 36
m_fontWeight: 400
m_enableAutoSizing: 1

21
Assets/Renderer/glyphGameURP.asset


m_Script: {fileID: 11500000, guid: bf2edee5c58d82540a51f03df9d42094, type: 3}
m_Name: glyphGameURP
m_EditorClassIdentifier:
k_AssetVersion: 5
k_AssetPreviousVersion: 5
k_AssetVersion: 9
k_AssetPreviousVersion: 9
m_RendererType: 1
m_RendererData: {fileID: 0}
m_RendererDataList:

m_RequireOpaqueTexture: 0
m_OpaqueDownsampling: 1
m_SupportsTerrainHoles: 1
m_StoreActionsOptimization: 0
m_UpscalingFilter: 0
m_FsrOverrideSharpness: 0
m_FsrSharpness: 0.92
m_MainLightRenderingMode: 1
m_MainLightShadowsSupported: 1
m_MainLightShadowmapResolution: 2048

m_AdditionalLightsShadowmapResolution: 512
m_AdditionalLightsShadowResolutionTierLow: 128
m_AdditionalLightsShadowResolutionTierMedium: 256
m_AdditionalLightsShadowResolutionTierHigh: 512
m_ReflectionProbeBlending: 0
m_ReflectionProbeBoxProjection: 0
m_CascadeBorder: 0.1
m_ConservativeEnclosingSphere: 0
m_NumIterationsEnclosingSphere: 64
m_AdditionalLightsCookieResolution: 2048
m_AdditionalLightsCookieFormat: 3
m_SupportsLightLayers: 0
m_UseFastSRGBLinearConversion: 0
m_ShadowType: 1
m_LocalShadowsSupported: 0
m_LocalShadowsAtlasResolution: 256

m_VolumeFrameworkUpdateMode: 0
m_ShadowCascades: 0

14
Assets/Renderer/glyphGameURP_Renderer.asset


m_Script: {fileID: 11500000, guid: de640fe3d0db1804a85f9fc8f5cadab6, type: 3}
m_Name: glyphGameURP_Renderer
m_EditorClassIdentifier:
debugShaders:
debugReplacementPS: {fileID: 4800000, guid: cf852408f2e174538bcd9b7fda1c5ae7, type: 3}
m_UseNativeRenderPass: 0
postProcessData: {fileID: 11400000, guid: 41439944d30ece34e96484bdb6645b55, type: 2}
xrSystemData: {fileID: 11400000, guid: 60e1133243b97e347b653163a8c01b64, type: 2}
shaders:

samplingPS: {fileID: 4800000, guid: 04c410c9937594faa893a11dceb85f7e, type: 3}
tileDepthInfoPS: {fileID: 0}
tileDeferredPS: {fileID: 0}
coreBlitPS: {fileID: 4800000, guid: 93446b5c5339d4f00b85c159e1159b7c, type: 3}
coreBlitColorAndDepthPS: {fileID: 4800000, guid: d104b2fc1ca6445babb8e90b0758136b, type: 3}
cameraMotionVector: {fileID: 4800000, guid: c56b7e0d4c7cb484e959caeeedae9bbf, type: 3}
objectMotionVector: {fileID: 4800000, guid: 7b3ede40266cd49a395def176e1bc486, type: 3}
m_AssetVersion: 1
m_OpaqueLayerMask:
serializedVersion: 2
m_Bits: 4294967295

zFailOperation: 0
m_ShadowTransparentReceive: 1
m_RenderingMode: 0
m_DepthPrimingMode: 0
m_ClusteredRendering: 0
m_TileSize: 32
m_IntermediateTextureMode: 1

423
Assets/Scenes/mainScene.unity


m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 0, y: 0, z: 0}
m_ConstrainProportionsScale: 0
m_Children:
- {fileID: 1853015250}
m_Father: {fileID: 0}

m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 0, y: 0}
m_Pivot: {x: 0, y: 0}
--- !u!114 &113557733 stripped
MonoBehaviour:
m_CorrespondingSourceObject: {fileID: 6055523672257132380, guid: 9aae991127b410c45a001ecd7f75311d, type: 3}
m_PrefabInstance: {fileID: 8628454959146822954}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: edfa4d53eaae84c43ba581088940700c, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!114 &151543605 stripped
MonoBehaviour:
m_CorrespondingSourceObject: {fileID: 2812013971948110896, guid: f80fc24bab3dcda459a2669321e2e5a4, type: 3}
m_PrefabInstance: {fileID: 7716713812904700119}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: a03b37d5b8df06948b36dfbc430a1ea5, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!1 &203192673
GameObject:
m_ObjectHideFlags: 0

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_ConstrainProportionsScale: 0
--- !u!114 &297599733 stripped
MonoBehaviour:
m_CorrespondingSourceObject: {fileID: 8905224144162831968, guid: 9aae991127b410c45a001ecd7f75311d, type: 3}
m_PrefabInstance: {fileID: 8628454959146822954}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 70dfc2fde0a9ef04eaff29a138f0bf45, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!114 &309485569 stripped
MonoBehaviour:
m_CorrespondingSourceObject: {fileID: 65572184039752926, guid: 9aae991127b410c45a001ecd7f75311d, type: 3}
m_PrefabInstance: {fileID: 8628454959146822954}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 70dfc2fde0a9ef04eaff29a138f0bf45, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!1 &457234252
GameObject:
m_ObjectHideFlags: 0

m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: -10}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 0

serializedVersion: 2
m_Bits: 1
m_VolumeTrigger: {fileID: 0}
m_VolumeFrameworkUpdateModeOption: 2
m_RenderPostProcessing: 0
m_Antialiasing: 0
m_AntialiasingQuality: 2

m_CorrespondingSourceObject: {fileID: 8628454958398271551, guid: 9aae991127b410c45a001ecd7f75311d, type: 3}
m_PrefabInstance: {fileID: 8628454959146822954}
m_PrefabAsset: {fileID: 0}
--- !u!1001 &794646441
PrefabInstance:
m_ObjectHideFlags: 0
serializedVersion: 2
m_Modification:
m_TransformParent: {fileID: 0}
m_Modifications:
- target: {fileID: 5481270937743757918, guid: b963f71e4874d4066bc72b9224e3ffce, type: 3}
propertyPath: m_Name
value: NetworkManager
objectReference: {fileID: 0}
- target: {fileID: 5665493986528408842, guid: b963f71e4874d4066bc72b9224e3ffce, type: 3}
propertyPath: m_RootOrder
value: 6
objectReference: {fileID: 0}
- target: {fileID: 5665493986528408842, guid: b963f71e4874d4066bc72b9224e3ffce, type: 3}
propertyPath: m_LocalPosition.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 5665493986528408842, guid: b963f71e4874d4066bc72b9224e3ffce, type: 3}
propertyPath: m_LocalPosition.y
value: 52.01943
objectReference: {fileID: 0}
- target: {fileID: 5665493986528408842, guid: b963f71e4874d4066bc72b9224e3ffce, type: 3}
propertyPath: m_LocalPosition.z
value: -6
objectReference: {fileID: 0}
- target: {fileID: 5665493986528408842, guid: b963f71e4874d4066bc72b9224e3ffce, type: 3}
propertyPath: m_LocalRotation.w
value: 1
objectReference: {fileID: 0}
- target: {fileID: 5665493986528408842, guid: b963f71e4874d4066bc72b9224e3ffce, type: 3}
propertyPath: m_LocalRotation.x
value: -0
objectReference: {fileID: 0}
- target: {fileID: 5665493986528408842, guid: b963f71e4874d4066bc72b9224e3ffce, type: 3}
propertyPath: m_LocalRotation.y
value: -0
objectReference: {fileID: 0}
- target: {fileID: 5665493986528408842, guid: b963f71e4874d4066bc72b9224e3ffce, type: 3}
propertyPath: m_LocalRotation.z
value: -0
objectReference: {fileID: 0}
- target: {fileID: 5665493986528408842, guid: b963f71e4874d4066bc72b9224e3ffce, type: 3}
propertyPath: m_LocalEulerAnglesHint.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 5665493986528408842, guid: b963f71e4874d4066bc72b9224e3ffce, type: 3}
propertyPath: m_LocalEulerAnglesHint.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 5665493986528408842, guid: b963f71e4874d4066bc72b9224e3ffce, type: 3}
propertyPath: m_LocalEulerAnglesHint.z
value: 0
objectReference: {fileID: 0}
m_RemovedComponents: []
m_SourcePrefab: {fileID: 100100000, guid: b963f71e4874d4066bc72b9224e3ffce, type: 3}
--- !u!114 &818919068 stripped
MonoBehaviour:
m_CorrespondingSourceObject: {fileID: 9110417292276624615, guid: 9aae991127b410c45a001ecd7f75311d, type: 3}

m_Script: {fileID: 11500000, guid: 78d292f3bd9f1614cb744dcb4fe3ac12, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!114 &883450645 stripped
--- !u!114 &921599257 stripped
m_CorrespondingSourceObject: {fileID: 3727964864104658339, guid: 9aae991127b410c45a001ecd7f75311d, type: 3}
m_CorrespondingSourceObject: {fileID: 6253235256787798393, guid: 9aae991127b410c45a001ecd7f75311d, type: 3}
m_Script: {fileID: 11500000, guid: 51373dc3c6ac79b4f8e36ac7c4419205, type: 3}
m_Script: {fileID: 11500000, guid: 78d292f3bd9f1614cb744dcb4fe3ac12, type: 3}
--- !u!114 &921599257 stripped
--- !u!114 &1056570786 stripped
m_CorrespondingSourceObject: {fileID: 6253235256787798393, guid: 9aae991127b410c45a001ecd7f75311d, type: 3}
m_CorrespondingSourceObject: {fileID: 3677991032586468434, guid: 9aae991127b410c45a001ecd7f75311d, type: 3}
m_Script: {fileID: 11500000, guid: 78d292f3bd9f1614cb744dcb4fe3ac12, type: 3}
m_Script: {fileID: 11500000, guid: 137b029a1106d7949a33106b02d1e837, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!1 &1176462898 stripped

m_PrefabAsset: {fileID: 0}
--- !u!114 &1217229506 stripped
MonoBehaviour:
m_CorrespondingSourceObject: {fileID: 6326316181187829680, guid: 9aae991127b410c45a001ecd7f75311d, type: 3}
m_PrefabInstance: {fileID: 8628454959146822954}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: a03b37d5b8df06948b36dfbc430a1ea5, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!114 &1412109061 stripped
MonoBehaviour:
m_CorrespondingSourceObject: {fileID: 3143918963127177442, guid: 9aae991127b410c45a001ecd7f75311d, type: 3}
m_PrefabInstance: {fileID: 8628454959146822954}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 70dfc2fde0a9ef04eaff29a138f0bf45, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!114 &1439850645 stripped
MonoBehaviour:
m_CorrespondingSourceObject: {fileID: 7716713811812636910, guid: f80fc24bab3dcda459a2669321e2e5a4, type: 3}

propertyPath: m_popUp
value:
objectReference: {fileID: 2637199315837045700}
- target: {fileID: 8425041362224744845, guid: 35249394896e31643ba0641c2a6f8e8d, type: 3}
propertyPath: m_editorLogVerbosity
value: 2
objectReference: {fileID: 0}
- target: {fileID: 8425041362224744849, guid: 35249394896e31643ba0641c2a6f8e8d, type: 3}
propertyPath: m_Pivot.x
value: 0

objectReference: {fileID: 0}
m_RemovedComponents: []
m_SourcePrefab: {fileID: 100100000, guid: 35249394896e31643ba0641c2a6f8e8d, type: 3}
--- !u!114 &1511612118 stripped
MonoBehaviour:
m_CorrespondingSourceObject: {fileID: 8895314912834654425, guid: 9aae991127b410c45a001ecd7f75311d, type: 3}
m_PrefabInstance: {fileID: 8628454959146822954}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 70dfc2fde0a9ef04eaff29a138f0bf45, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!114 &1583737884 stripped
MonoBehaviour:
m_CorrespondingSourceObject: {fileID: 6399412716226000571, guid: 9aae991127b410c45a001ecd7f75311d, type: 3}
m_PrefabInstance: {fileID: 8628454959146822954}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: a03b37d5b8df06948b36dfbc430a1ea5, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!114 &1793980663 stripped
--- !u!114 &1818474178 stripped
m_CorrespondingSourceObject: {fileID: 6680128790398423813, guid: 9aae991127b410c45a001ecd7f75311d, type: 3}
m_CorrespondingSourceObject: {fileID: 7029468144569480209, guid: 9aae991127b410c45a001ecd7f75311d, type: 3}
m_Script: {fileID: 11500000, guid: a03b37d5b8df06948b36dfbc430a1ea5, type: 3}
m_Script: {fileID: 11500000, guid: d125c6cac111c6442ac5b07a1f313fa4, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!1 &1853015249

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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 7866770}
m_RootOrder: 0

m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1853015249}
m_CullTransparentMesh: 1
--- !u!114 &1969944515 stripped
MonoBehaviour:
m_CorrespondingSourceObject: {fileID: 2646044666659190602, guid: 9aae991127b410c45a001ecd7f75311d, type: 3}
m_PrefabInstance: {fileID: 8628454959146822954}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: a03b37d5b8df06948b36dfbc430a1ea5, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!114 &2003557942 stripped
MonoBehaviour:
m_CorrespondingSourceObject: {fileID: 1683469940080917438, guid: 9aae991127b410c45a001ecd7f75311d, type: 3}

m_Script: {fileID: 11500000, guid: 78d292f3bd9f1614cb744dcb4fe3ac12, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!114 &2074106027 stripped
MonoBehaviour:
m_CorrespondingSourceObject: {fileID: 5162490130543683956, guid: 9aae991127b410c45a001ecd7f75311d, type: 3}
m_PrefabInstance: {fileID: 8628454959146822954}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 70dfc2fde0a9ef04eaff29a138f0bf45, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!114 &2126854580 stripped
MonoBehaviour:
m_CorrespondingSourceObject: {fileID: 6900625576974141932, guid: 9aae991127b410c45a001ecd7f75311d, type: 3}
m_PrefabInstance: {fileID: 8628454959146822954}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 70dfc2fde0a9ef04eaff29a138f0bf45, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!114 &2130620598 stripped
MonoBehaviour:
m_CorrespondingSourceObject: {fileID: 4144223224519676544, guid: 9aae991127b410c45a001ecd7f75311d, type: 3}
m_PrefabInstance: {fileID: 8628454959146822954}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 70dfc2fde0a9ef04eaff29a138f0bf45, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!114 &1547097153363664359 stripped
MonoBehaviour:
m_CorrespondingSourceObject: {fileID: 7294450586020064062, guid: 9aae991127b410c45a001ecd7f75311d, type: 3}
m_PrefabInstance: {fileID: 8628454959146822954}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 51373dc3c6ac79b4f8e36ac7c4419205, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!114 &2637199315837045694 stripped
MonoBehaviour:
m_CorrespondingSourceObject: {fileID: 1554643161126302255, guid: 9aae991127b410c45a001ecd7f75311d, type: 3}
m_PrefabInstance: {fileID: 8628454959146822954}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: edfa4d53eaae84c43ba581088940700c, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!114 &2637199315837045695 stripped
MonoBehaviour:
m_CorrespondingSourceObject: {fileID: 4847401366899542036, guid: 9aae991127b410c45a001ecd7f75311d, type: 3}
m_PrefabInstance: {fileID: 8628454959146822954}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: a03b37d5b8df06948b36dfbc430a1ea5, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!114 &2637199315837045696 stripped
MonoBehaviour:
m_CorrespondingSourceObject: {fileID: 5773825590554619550, guid: 9aae991127b410c45a001ecd7f75311d, type: 3}
m_PrefabInstance: {fileID: 8628454959146822954}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 51373dc3c6ac79b4f8e36ac7c4419205, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!114 &2637199315837045698 stripped
MonoBehaviour:
m_CorrespondingSourceObject: {fileID: 6606885600234288061, guid: 9aae991127b410c45a001ecd7f75311d, type: 3}
m_PrefabInstance: {fileID: 8628454959146822954}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 788267441}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 51373dc3c6ac79b4f8e36ac7c4419205, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!114 &2637199315837045699 stripped
MonoBehaviour:
m_CorrespondingSourceObject: {fileID: 142122066828140637, guid: 9aae991127b410c45a001ecd7f75311d, type: 3}
m_PrefabInstance: {fileID: 8628454959146822954}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: a03b37d5b8df06948b36dfbc430a1ea5, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!114 &2637199315837045700 stripped
MonoBehaviour:
m_CorrespondingSourceObject: {fileID: 8211845410637438850, guid: 9aae991127b410c45a001ecd7f75311d, type: 3}

value: 0
objectReference: {fileID: 0}
- target: {fileID: 7716713811812636910, guid: f80fc24bab3dcda459a2669321e2e5a4, type: 3}
propertyPath: m_countdown
value:
objectReference: {fileID: 1818474178}
- target: {fileID: 7716713811812636910, guid: f80fc24bab3dcda459a2669321e2e5a4, type: 3}
propertyPath: m_vivoxUserHandlers.Array.size
value: 4
objectReference: {fileID: 0}

objectReference: {fileID: 0}
- target: {fileID: 7716713811812636910, guid: f80fc24bab3dcda459a2669321e2e5a4, type: 3}
propertyPath: m_LocalUserObservers.Array.size
value: 7
value: 6
value: 8
value: 6
objectReference: {fileID: 0}
- target: {fileID: 7716713811812636910, guid: f80fc24bab3dcda459a2669321e2e5a4, type: 3}
propertyPath: m_vivoxUserHandlers.Array.data[0]

- target: {fileID: 7716713811812636910, guid: f80fc24bab3dcda459a2669321e2e5a4, type: 3}
propertyPath: m_GameStateObservers.Array.data[0]
value:
objectReference: {fileID: 2637199315837045698}
objectReference: {fileID: 0}
objectReference: {fileID: 1547097153363664359}
objectReference: {fileID: 0}
objectReference: {fileID: 2637199315837045696}
objectReference: {fileID: 0}
objectReference: {fileID: 883450645}
objectReference: {fileID: 0}
- target: {fileID: 7716713811812636910, guid: f80fc24bab3dcda459a2669321e2e5a4, type: 3}
propertyPath: m_GameStateObservers.Array.data[4]
value:

- target: {fileID: 7716713811812636910, guid: f80fc24bab3dcda459a2669321e2e5a4, type: 3}
propertyPath: m_LobbyDataObservers.Array.data[0]
value:
objectReference: {fileID: 297599733}
objectReference: {fileID: 0}
objectReference: {fileID: 2126854580}
objectReference: {fileID: 0}
objectReference: {fileID: 2130620598}
objectReference: {fileID: 0}
objectReference: {fileID: 2074106027}
objectReference: {fileID: 0}
objectReference: {fileID: 309485569}
objectReference: {fileID: 0}
- target: {fileID: 7716713811812636910, guid: f80fc24bab3dcda459a2669321e2e5a4, type: 3}
propertyPath: m_LobbyDataObservers.Array.data[5]
value:

value:
objectReference: {fileID: 1412109061}
objectReference: {fileID: 0}
- target: {fileID: 7716713811812636910, guid: f80fc24bab3dcda459a2669321e2e5a4, type: 3}
propertyPath: m_LobbyDataObservers.Array.data[7]
value:

- target: {fileID: 7716713811812636910, guid: f80fc24bab3dcda459a2669321e2e5a4, type: 3}
propertyPath: m_LocalUserObservers.Array.data[0]
value:
objectReference: {fileID: 2637199315837045695}
objectReference: {fileID: 0}
objectReference: {fileID: 1217229506}
objectReference: {fileID: 0}
objectReference: {fileID: 1969944515}
objectReference: {fileID: 0}
objectReference: {fileID: 1583737884}
objectReference: {fileID: 0}
objectReference: {fileID: 1793980663}
objectReference: {fileID: 0}
objectReference: {fileID: 2637199315837045699}
objectReference: {fileID: 0}
objectReference: {fileID: 151543605}
objectReference: {fileID: 0}
objectReference: {fileID: 1412109061}
objectReference: {fileID: 0}
objectReference: {fileID: 297599733}
objectReference: {fileID: 0}
objectReference: {fileID: 2130620598}
objectReference: {fileID: 0}
objectReference: {fileID: 2074106027}
objectReference: {fileID: 0}
objectReference: {fileID: 309485569}
objectReference: {fileID: 0}
objectReference: {fileID: 2126854580}
objectReference: {fileID: 0}
objectReference: {fileID: 1511612118}
objectReference: {fileID: 0}
objectReference: {fileID: 7716713812904700120}
objectReference: {fileID: 0}
objectReference: {fileID: 1511612118}
objectReference: {fileID: 0}
objectReference: {fileID: 1511612118}
objectReference: {fileID: 0}
objectReference: {fileID: 2637199315837045694}
objectReference: {fileID: 0}
- target: {fileID: 7716713811812636910, guid: f80fc24bab3dcda459a2669321e2e5a4, type: 3}
propertyPath: m_LocalGameStateObservers.Array.size
value: 6

value: 4
value: 0
objectReference: {fileID: 2637199315837045694}
objectReference: {fileID: 0}
objectReference: {fileID: 113557733}
objectReference: {fileID: 0}
objectReference: {fileID: 2637199315837045698}
objectReference: {fileID: 0}
objectReference: {fileID: 1547097153363664359}
objectReference: {fileID: 0}
objectReference: {fileID: 2637199315837045696}
objectReference: {fileID: 0}
objectReference: {fileID: 883450645}
objectReference: {fileID: 0}
- target: {fileID: 7716713811812636910, guid: f80fc24bab3dcda459a2669321e2e5a4, type: 3}
propertyPath: m_LocalGameStateObservers.Array.data[4]
value:

- target: {fileID: 7716713811812636910, guid: f80fc24bab3dcda459a2669321e2e5a4, type: 3}
propertyPath: m_LocalMenuStateObservers.Array.data[0]
value:
objectReference: {fileID: 2637199315837045698}
objectReference: {fileID: 0}
objectReference: {fileID: 1547097153363664359}
objectReference: {fileID: 0}
objectReference: {fileID: 2637199315837045696}
objectReference: {fileID: 0}
objectReference: {fileID: 883450645}
objectReference: {fileID: 0}
- target: {fileID: 7716713811812636911, guid: f80fc24bab3dcda459a2669321e2e5a4, type: 3}
propertyPath: m_RootOrder
value: 4

propertyPath: m_LocalEulerAnglesHint.z
value: 0
objectReference: {fileID: 0}
m_RemovedComponents: []
m_RemovedComponents:
- {fileID: 5193415626965589893, guid: f80fc24bab3dcda459a2669321e2e5a4, type: 3}
--- !u!114 &7716713812904700120 stripped
MonoBehaviour:
m_CorrespondingSourceObject: {fileID: 5235782363599194820, guid: f80fc24bab3dcda459a2669321e2e5a4, type: 3}
m_PrefabInstance: {fileID: 7716713812904700119}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 70dfc2fde0a9ef04eaff29a138f0bf45, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!1001 &8628454959146822954
PrefabInstance:
m_ObjectHideFlags: 0

objectReference: {fileID: 0}
- target: {fileID: 1791708704057470616, guid: 9aae991127b410c45a001ecd7f75311d, type: 3}
propertyPath: m_AnchorMax.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1791708704057470616, guid: 9aae991127b410c45a001ecd7f75311d, type: 3}
propertyPath: m_AnchorMin.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 2017918237437651757, guid: 9aae991127b410c45a001ecd7f75311d, type: 3}

propertyPath: onValueChanged.m_PersistentCalls.m_Calls.Array.data[0].m_Target
value:
objectReference: {fileID: 1439850645}
- target: {fileID: 5962742399928014509, guid: 9aae991127b410c45a001ecd7f75311d, type: 3}
propertyPath: m_fontSize
value: 40
objectReference: {fileID: 0}
- target: {fileID: 6062621839658634998, guid: 9aae991127b410c45a001ecd7f75311d, type: 3}
propertyPath: m_fontSize
value: 40
objectReference: {fileID: 0}
- target: {fileID: 6274653283217461060, guid: 9aae991127b410c45a001ecd7f75311d, type: 3}
propertyPath: m_AnchorMax.y
value: 0

propertyPath: m_AnchoredPosition.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6410570481816805232, guid: 9aae991127b410c45a001ecd7f75311d, type: 3}
propertyPath: m_Name
value: LobbySpace
objectReference: {fileID: 0}
- target: {fileID: 6468178321825623330, guid: 9aae991127b410c45a001ecd7f75311d, type: 3}
propertyPath: m_AnchorMax.y
value: 0

propertyPath: m_AnchoredPosition.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 7570872190661528690, guid: 9aae991127b410c45a001ecd7f75311d, type: 3}
propertyPath: m_onVisibilityChange.m_PersistentCalls.m_Calls.Array.data[0].m_Target
value:
objectReference: {fileID: 1056570786}
- target: {fileID: 7591092274140645142, guid: 9aae991127b410c45a001ecd7f75311d, type: 3}
propertyPath: onValueChanged.m_PersistentCalls.m_Calls.Array.data[0].m_Target
value:

propertyPath: m_AnchoredPosition.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 8354389055559135511, guid: 9aae991127b410c45a001ecd7f75311d, type: 3}
propertyPath: m_Alpha
value: 0
objectReference: {fileID: 0}
- target: {fileID: 8354389055559135511, guid: 9aae991127b410c45a001ecd7f75311d, type: 3}
propertyPath: m_Interactable
value: 0
objectReference: {fileID: 0}
- target: {fileID: 8354389055559135511, guid: 9aae991127b410c45a001ecd7f75311d, type: 3}
propertyPath: m_BlocksRaycasts
value: 0
objectReference: {fileID: 0}
- target: {fileID: 8426100386232923098, guid: 9aae991127b410c45a001ecd7f75311d, type: 3}
propertyPath: m_AnchorMax.y
value: 0

- target: {fileID: 8578661244454260921, guid: 9aae991127b410c45a001ecd7f75311d, type: 3}
propertyPath: m_AnchoredPosition.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 8628454959292210618, guid: 9aae991127b410c45a001ecd7f75311d, type: 3}
propertyPath: m_fontSize
value: 62
objectReference: {fileID: 0}
- target: {fileID: 8690778207903056580, guid: 9aae991127b410c45a001ecd7f75311d, type: 3}
propertyPath: m_AnchorMax.x

propertyPath: m_AnchoredPosition.y
value: 0
objectReference: {fileID: 0}
m_RemovedComponents: []
m_RemovedComponents:
- {fileID: 3143918963127177442, guid: 9aae991127b410c45a001ecd7f75311d, type: 3}
- {fileID: 6900625576974141932, guid: 9aae991127b410c45a001ecd7f75311d, type: 3}
- {fileID: 7383922579991741677, guid: 9aae991127b410c45a001ecd7f75311d, type: 3}
m_SourcePrefab: {fileID: 100100000, guid: 9aae991127b410c45a001ecd7f75311d, type: 3}

2
Assets/Scripts/GameLobby/Auth/Auth.cs.meta


fileFormatVersion: 2
guid: b98aae4acd443d24faedf0aa20e946f3
guid: 2d4f181652f814a93be872600da7fa0b
MonoImporter:
externalObjects: {}
serializedVersion: 2

49
Assets/Scripts/GameLobby/Game/Countdown.cs


/// since precise timing isn't necessary.
/// </summary>
[RequireComponent(typeof(UI.CountdownUI))]
public class Countdown : MonoBehaviour, IReceiveMessages
public class Countdown : MonoBehaviour
public class Data : Observed<Countdown.Data>
{
private float m_timeLeft;
public float TimeLeft
{
get => m_timeLeft;
set
{ m_timeLeft = value;
OnChanged(this);
}
}
public override void CopyObserved(Data oldObserved) { /*No-op, since this is unnecessary.*/ }
}
CallbackValue<float> TimeLeft = new CallbackValue<float>();
private Data m_data = new Data();
private UI.CountdownUI m_ui;
private const int k_countdownTime = 4;

m_ui = GetComponent<UI.CountdownUI>();
m_data.TimeLeft = -1;
Locator.Get.Messenger.Subscribe(this);
m_ui.BeginObserving(m_data);
TimeLeft.onChanged += m_ui.OnTimeChanged;
TimeLeft.Value = -1;
public void OnDisable()
public void StartCountDown()
Locator.Get.Messenger.Unsubscribe(this);
m_ui.EndObserving();
TimeLeft.Value = k_countdownTime;
public void OnReceiveMessage(MessageType type, object msg)
public void CancelCountDown()
if (type == MessageType.StartCountdown)
{
m_data.TimeLeft = k_countdownTime;
}
else if (type == MessageType.CancelCountdown)
{
m_data.TimeLeft = -1;
}
TimeLeft.Value = -1;
if (m_data.TimeLeft < 0)
if (TimeLeft.Value < 0)
m_data.TimeLeft -= Time.deltaTime;
if (m_data.TimeLeft < 0)
Locator.Get.Messenger.OnReceiveMessage(MessageType.CompleteCountdown, null);
TimeLeft.Value -= Time.deltaTime;
if (TimeLeft.Value < 0)
GameManager.Instance.FinishedCountDown();
}
}

586
Assets/Scripts/GameLobby/Game/GameManager.cs


using LobbyRelaySample.relay;
using System.Threading.Tasks;
using LobbyRelaySample.lobby;
using LobbyRelaySample.ngo;
using Unity.Services.Authentication;
using UnityEngine.Serialization;
#if UNITY_EDITOR
using ParrelSync;
#endif
/// Current state of the local game.
/// Set as a flag to allow for the Inspector to select multiple valid states for various UI features.
/// </summary>
[Flags]
public enum GameState
{
Menu = 1,
Lobby = 2,
JoinMenu = 4,
}
/// <summary>
public class GameManager : MonoBehaviour, IReceiveMessages
public class GameManager : MonoBehaviour
#region UI elements that observe the local state. These should be assigned the observers in the scene during Start.
public LocalLobby LocalLobby => m_LocalLobby;
public Action<GameState> onGameStateChanged;
public LocalLobbyList LobbyList { get; private set; } = new LocalLobbyList();
/// <summary>
/// The Observer/Observed Pattern is great for keeping the UI in Sync with the actual Values.
/// Each list below represents a single Observed class that gets updated by other parts of the code, and will
/// trigger the list of Observers that are looking for changes in that class.
///
/// The list is serialized, so you can navigate to the Observers via the Inspector to see who's watching.
/// </summary>
[SerializeField]
private List<LocalMenuStateObserver> m_LocalMenuStateObservers = new List<LocalMenuStateObserver>();
public GameState LocalGameState { get; private set; }
public LobbyManager LobbyManager { get; private set; }
private List<LocalLobbyObserver> m_LocalLobbyObservers = new List<LocalLobbyObserver>();
[SerializeField]
private List<LobbyUserObserver> m_LocalUserObservers = new List<LobbyUserObserver>();
SetupInGame m_setupInGame;
private List<LobbyServiceDataObserver> m_LobbyServiceObservers = new List<LobbyServiceDataObserver>();
Countdown m_countdown;
#endregion
LocalPlayer m_LocalUser;
LocalLobby m_LocalLobby;
private LocalMenuState m_LocalMenuState = new LocalMenuState();
private LobbyUser m_localUser;
private LocalLobby m_localLobby;
private LobbyServiceData m_lobbyServiceData = new LobbyServiceData();
private LobbyContentUpdater m_LobbyContentUpdater = new LobbyContentUpdater();
private RelayUtpSetup m_relaySetup;
private RelayUtpClient m_relayClient;
private vivox.VivoxSetup m_vivoxSetup = new vivox.VivoxSetup();
vivox.VivoxSetup m_VivoxSetup = new vivox.VivoxSetup();
private List<vivox.VivoxUserHandler> m_vivoxUserHandlers;
/// <summary>Rather than a setter, this is usable in-editor. It won't accept an enum, however.</summary>
public void SetLobbyColorFilter(int color)
{
m_lobbyColorFilter = (LobbyColor)color;
}
List<vivox.VivoxUserHandler> m_vivoxUserHandlers;
private LobbyColor m_lobbyColorFilter;
LobbyColor m_lobbyColorFilter;
#region Setup
static GameManager m_GameManagerInstance;
private void Awake()
public static GameManager Instance
// Do some arbitrary operations to instantiate singletons.
#pragma warning disable IDE0059 // Unnecessary assignment of a value
var unused = Locator.Get;
#pragma warning restore IDE0059
Locator.Get.Provide(new Auth.Identity(OnAuthSignIn));
Application.wantsToQuit += OnWantToQuit;
get
{
if (m_GameManagerInstance != null)
return m_GameManagerInstance;
m_GameManagerInstance = FindObjectOfType<GameManager>();
return m_GameManagerInstance;
}
private void Start()
/// <summary>Rather than a setter, this is usable in-editor. It won't accept an enum, however.</summary>
public void SetLobbyColorFilter(int color)
m_localLobby = new LocalLobby { State = LobbyState.Lobby };
m_localUser = new LobbyUser();
m_localUser.DisplayName = "New Player";
Locator.Get.Messenger.Subscribe(this);
BeginObservers();
m_lobbyColorFilter = (LobbyColor)color;
private void OnAuthSignIn()
public async Task<LocalPlayer> AwaitLocalUserInitialization()
Debug.Log("Signed in.");
m_localUser.ID = Locator.Get.Identity.GetSubIdentity(Auth.IIdentityType.Auth).GetContent("id");
m_localUser.DisplayName = NameGenerator.GetName(m_localUser.ID);
m_localLobby.AddPlayer(m_localUser); // The local LobbyUser object will be hooked into UI before the LocalLobby is populated during lobby join, so the LocalLobby must know about it already when that happens.
StartVivoxLogin();
while (m_LocalUser == null)
await Task.Delay(100);
return m_LocalUser;
/// <summary>
/// TODO Wire is a good update to remove the monolithic observers and move to observed values instead, on a Singleton gameManager
/// </summary>
private void BeginObservers()
public async void CreateLobby(string name, bool isPrivate, int maxPlayers = 4)
foreach (var gameStateObs in m_LocalMenuStateObservers)
gameStateObs.BeginObserving(m_LocalMenuState);
foreach (var serviceObs in m_LobbyServiceObservers)
serviceObs.BeginObserving(m_lobbyServiceData);
foreach (var lobbyObs in m_LocalLobbyObservers)
lobbyObs.BeginObserving(m_localLobby);
foreach (var userObs in m_LocalUserObservers)
userObs.BeginObserving(m_localUser);
}
#endregion
/// <summary>
/// The Messaging System handles most of the core Lobby Service calls, and catches the callbacks from those calls.
/// These In turn update the observed variables and propagates the events to the game.
/// When looking for the interactions, look up the MessageType and search for it in the code to see where it is used outside this script.
/// EG. Locator.Get.Messenger.OnReceiveMessage(MessageType.RenameRequest, name);
/// </summary>
public void OnReceiveMessage(MessageType type, object msg)
{
if (type == MessageType.CreateLobbyRequest)
try
LocalLobby.LobbyData createLobbyData = (LocalLobby.LobbyData)msg;
LobbyAsyncRequests.Instance.CreateLobbyAsync(createLobbyData.LobbyName, createLobbyData.MaxPlayerCount, createLobbyData.Private, m_localUser, (r) =>
{
lobby.LobbyConverters.RemoteToLocal(r, m_localLobby);
OnCreatedLobby();
},
OnFailedJoin);
var lobby = await LobbyManager.CreateLobbyAsync(
name,
maxPlayers,
isPrivate, m_LocalUser);
LobbyConverters.RemoteToLocal(lobby, m_LocalLobby);
await CreateLobby();
else if (type == MessageType.JoinLobbyRequest)
catch (Exception exception)
LocalLobby.LobbyData lobbyInfo = (LocalLobby.LobbyData)msg;
LobbyAsyncRequests.Instance.JoinLobbyAsync(lobbyInfo.LobbyID, lobbyInfo.LobbyCode, m_localUser, (r) =>
{
lobby.LobbyConverters.RemoteToLocal(r, m_localLobby);
OnJoinedLobby();
},
OnFailedJoin);
SetGameState(GameState.JoinMenu);
Debug.LogError($"Error creating lobby : {exception} ");
else if (type == MessageType.QueryLobbies)
}
public async void JoinLobby(string lobbyID, string lobbyCode)
{
try
m_lobbyServiceData.State = LobbyQueryState.Fetching;
LobbyAsyncRequests.Instance.RetrieveLobbyListAsync(
qr =>
{
if (qr != null)
OnLobbiesQueried(lobby.LobbyConverters.QueryToLocalList(qr));
},
er =>
{
OnLobbyQueryFailed();
},
m_lobbyColorFilter);
var lobby = await LobbyManager.JoinLobbyAsync(lobbyID, lobbyCode,
m_LocalUser);
LobbyConverters.RemoteToLocal(lobby, m_LocalLobby);
await JoinLobby();
else if (type == MessageType.QuickJoin)
catch (Exception exception)
LobbyAsyncRequests.Instance.QuickJoinLobbyAsync(m_localUser, m_lobbyColorFilter, (r) =>
{
lobby.LobbyConverters.RemoteToLocal(r, m_localLobby);
OnJoinedLobby();
},
OnFailedJoin);
SetGameState(GameState.JoinMenu);
Debug.LogError($"Error joining lobby : {exception} ");
else if (type == MessageType.RenameRequest)
{
string name = (string)msg;
if (string.IsNullOrWhiteSpace(name))
{
Locator.Get.Messenger.OnReceiveMessage(MessageType.DisplayErrorPopup, "Empty Name not allowed."); // Lobby error type, then HTTP error type.
return;
}
}
m_localUser.DisplayName = (string)msg;
}
else if (type == MessageType.ClientUserApproved)
public async void QueryLobbies()
{
LobbyList.QueryState.Value = LobbyQueryState.Fetching;
var qr = await LobbyManager.RetrieveLobbyListAsync(m_lobbyColorFilter);
if (qr == null)
ConfirmApproval();
return;
else if (type == MessageType.UserSetEmote)
SetCurrentLobbies(LobbyConverters.QueryToLocalList(qr));
}
public async void QuickJoin()
{
var lobby = await LobbyManager.QuickJoinLobbyAsync(m_LocalUser, m_lobbyColorFilter);
if (lobby != null)
EmoteType emote = (EmoteType)msg;
m_localUser.Emote = emote;
LobbyConverters.RemoteToLocal(lobby, m_LocalLobby);
await JoinLobby();
else if (type == MessageType.LobbyUserStatus)
else
m_localUser.UserStatus = (UserStatus)msg;
SetGameState(GameState.JoinMenu);
else if (type == MessageType.StartCountdown)
}
public void SetLocalUserName(string name)
{
if (string.IsNullOrWhiteSpace(name))
m_localLobby.State = LobbyState.CountDown;
LogHandlerSettings.Instance.SpawnErrorPopup(
"Empty Name not allowed."); // Lobby error type, then HTTP error type.
return;
else if (type == MessageType.CancelCountdown)
m_LocalUser.DisplayName.Value = name;
SendLocalUserData();
}
public void SetLocalUserEmote(EmoteType emote)
{
m_LocalUser.Emote.Value = emote;
SendLocalUserData();
}
public void SetLocalUserStatus(PlayerStatus status)
{
m_LocalUser.UserStatus.Value = status;
SendLocalUserData();
}
public void SetLocalLobbyColor(int color)
{
if (m_LocalLobby.PlayerCount < 1)
return;
m_LocalLobby.LocalLobbyColor.Value = (LobbyColor)color;
SendLocalLobbyData();
}
bool updatingLobby;
async void SendLocalLobbyData()
{
await LobbyManager.UpdateLobbyDataAsync(LobbyConverters.LocalToRemoteLobbyData(m_LocalLobby));
}
async void SendLocalUserData()
{
await LobbyManager.UpdatePlayerDataAsync(LobbyConverters.LocalToRemoteUserData(m_LocalUser));
}
public void UIChangeMenuState(GameState state)
{
var isQuittingGame = LocalGameState == GameState.Lobby &&
m_LocalLobby.LocalLobbyState.Value == LobbyState.InGame;
if (isQuittingGame)
m_localLobby.State = LobbyState.Lobby;
//If we were in-game, make sure we stop by the lobby first
state = GameState.Lobby;
ClientQuitGame();
else if (type == MessageType.CompleteCountdown)
SetGameState(state);
}
public void HostSetRelayCode(string code)
{
m_LocalLobby.RelayCode.Value = code;
SendLocalLobbyData();
}
//Only Host needs to listen to this and change state.
void OnPlayersReady(int readyCount)
{
if (readyCount == m_LocalLobby.PlayerCount &&
m_LocalLobby.LocalLobbyState.Value != LobbyState.CountDown)
if (m_relayClient is RelayUtpHost)
(m_relayClient as RelayUtpHost).SendInGameState();
m_LocalLobby.LocalLobbyState.Value = LobbyState.CountDown;
SendLocalLobbyData();
else if (type == MessageType.ChangeMenuState)
else if (m_LocalLobby.LocalLobbyState.Value == LobbyState.CountDown)
SetGameState((GameState)msg);
m_LocalLobby.LocalLobbyState.Value = LobbyState.Lobby;
SendLocalLobbyData();
else if (type == MessageType.ConfirmInGameState)
}
void OnLobbyStateChanged(LobbyState state)
{
if (state == LobbyState.Lobby)
CancelCountDown();
if (state == LobbyState.CountDown)
BeginCountDown();
}
void BeginCountDown()
{
Debug.Log("Beginning Countdown.");
m_countdown.StartCountDown();
}
void CancelCountDown()
{
Debug.Log("Countdown Cancelled.");
m_countdown.CancelCountDown();
}
public void FinishedCountDown()
{
m_LocalUser.UserStatus.Value = PlayerStatus.InGame;
m_LocalLobby.LocalLobbyState.Value = LobbyState.InGame;
m_setupInGame.StartNetworkedGame(m_LocalLobby, m_LocalUser);
}
public void BeginGame()
{
if (m_LocalUser.IsHost.Value)
m_localUser.UserStatus = UserStatus.InGame;
m_localLobby.State = LobbyState.InGame;
m_LocalLobby.LocalLobbyState.Value = LobbyState.InGame;
m_LocalLobby.Locked.Value = true;
SendLocalLobbyData();
else if (type == MessageType.EndGame)
}
public void ClientQuitGame()
{
EndGame();
m_setupInGame?.OnGameEnd();
}
public void EndGame()
{
if (m_LocalUser.IsHost.Value)
m_localLobby.State = LobbyState.Lobby;
SetUserLobbyState();
m_LocalLobby.LocalLobbyState.Value = LobbyState.Lobby;
m_LocalLobby.Locked.Value = false;
SendLocalLobbyData();
SetLobbyView();
private void SetGameState(GameState state)
#region Setup
async void Awake()
{
Application.wantsToQuit += OnWantToQuit;
m_LocalUser = new LocalPlayer("", 0, false, "LocalPlayer");
m_LocalLobby = new LocalLobby { LocalLobbyState = { Value = LobbyState.Lobby } };
LobbyManager = new LobbyManager();
await InitializeServices();
AuthenticatePlayer();
StartVivoxLogin();
}
async Task InitializeServices()
bool isLeavingLobby = (state == GameState.Menu || state == GameState.JoinMenu) && m_LocalMenuState.State == GameState.Lobby;
m_LocalMenuState.State = state;
if (isLeavingLobby)
OnLeftLobby();
string serviceProfileName = "player";
#if UNITY_EDITOR
serviceProfileName = $"{serviceProfileName}_{ClonesManager.GetCurrentProject().name}";
#endif
await Auth.Authenticate(serviceProfileName);
private void OnLobbiesQueried(IEnumerable<LocalLobby> lobbies)
void AuthenticatePlayer()
var newLobbyDict = new Dictionary<string, LocalLobby>();
foreach (var lobby in lobbies)
newLobbyDict.Add(lobby.LobbyID, lobby);
var localId = AuthenticationService.Instance.PlayerId;
var randomName = NameGenerator.GetName(localId);
m_lobbyServiceData.State = LobbyQueryState.Fetched;
m_lobbyServiceData.CurrentLobbies = newLobbyDict;
m_LocalUser.ID.Value = localId;
m_LocalUser.DisplayName.Value = randomName;
private void OnLobbyQueryFailed()
#endregion
void SetGameState(GameState state)
m_lobbyServiceData.State = LobbyQueryState.Error;
var isLeavingLobby = (state == GameState.Menu || state == GameState.JoinMenu) &&
LocalGameState == GameState.Lobby;
LocalGameState = state;
Debug.Log($"Switching Game State to : {LocalGameState}");
if (isLeavingLobby)
LeaveLobby();
onGameStateChanged.Invoke(LocalGameState);
private void OnCreatedLobby()
void SetCurrentLobbies(IEnumerable<LocalLobby> lobbies)
m_localUser.IsHost = true;
OnJoinedLobby();
var newLobbyDict = new Dictionary<string, LocalLobby>();
foreach (var lobby in lobbies)
newLobbyDict.Add(lobby.LobbyID.Value, lobby);
LobbyList.CurrentLobbies = newLobbyDict;
LobbyList.QueryState.Value = LobbyQueryState.Fetched;
private void OnJoinedLobby()
async Task CreateLobby()
m_LobbyContentUpdater.BeginTracking(m_localLobby, m_localUser);
SetUserLobbyState();
// The host has the opportunity to reject incoming players, but to do so the player needs to connect to Relay without having game logic available.
// In particular, we should prevent players from joining voice chat until they are approved.
OnReceiveMessage(MessageType.LobbyUserStatus, UserStatus.Connecting);
if (m_localUser.IsHost)
m_LocalUser.IsHost.Value = true;
m_LocalLobby.onUserReadyChange = OnPlayersReady;
try
StartRelayConnection();
StartVivoxJoin();
await BindLobby();
else
catch (Exception exception)
StartRelayConnection();
Debug.LogError($"Couldn't join Lobby: {exception}");
private void OnLeftLobby()
async Task JoinLobby()
m_localUser.ResetState();
LobbyAsyncRequests.Instance.LeaveLobbyAsync(m_localLobby.LobbyID, ResetLocalLobby);
m_LobbyContentUpdater.EndTracking();
m_vivoxSetup.LeaveLobbyChannel();
//Trigger UI Even when same value
m_LocalUser.IsHost.ForceSet(false);
await BindLobby();
}
if (m_relaySetup != null)
{
Component.Destroy(m_relaySetup);
m_relaySetup = null;
}
if (m_relayClient != null)
{
m_relayClient.Dispose();
StartCoroutine(FinishCleanup());
// We need to delay slightly to give the disconnect message sent during Dispose time to reach the host, so that we don't destroy the connection without it being flushed first.
IEnumerator FinishCleanup()
{
yield return null;
Component.Destroy(m_relayClient);
m_relayClient = null;
}
}
async Task BindLobby()
{
await LobbyManager.BindLocalLobbyToRemote(m_LocalLobby.LobbyID.Value, m_LocalLobby);
m_LocalLobby.LocalLobbyState.onChanged += OnLobbyStateChanged;
SetLobbyView();
StartVivoxJoin();
/// <summary>
/// Back to Join menu if we fail to join for whatever reason.
/// </summary>
private void OnFailedJoin()
public void LeaveLobby()
SetGameState(GameState.JoinMenu);
m_LocalUser.ResetState();
#pragma warning disable 4014
LobbyManager.LeaveLobbyAsync();
#pragma warning restore 4014
ResetLocalLobby();
m_VivoxSetup.LeaveLobbyChannel();
private void StartVivoxLogin()
void StartVivoxLogin()
m_vivoxSetup.Initialize(m_vivoxUserHandlers, OnVivoxLoginComplete);
m_VivoxSetup.Initialize(m_vivoxUserHandlers, OnVivoxLoginComplete);
void OnVivoxLoginComplete(bool didSucceed)
{

StartCoroutine(RetryConnection(StartVivoxLogin, m_localLobby.LobbyID));
StartCoroutine(RetryConnection(StartVivoxLogin, m_LocalLobby.LobbyID.Value));
private void StartVivoxJoin()
void StartVivoxJoin()
m_vivoxSetup.JoinLobbyChannel(m_localLobby.LobbyID, OnVivoxJoinComplete);
m_VivoxSetup.JoinLobbyChannel(m_LocalLobby.LobbyID.Value, OnVivoxJoinComplete);
void OnVivoxJoinComplete(bool didSucceed)
{

StartCoroutine(RetryConnection(StartVivoxJoin, m_localLobby.LobbyID));
}
}
}
private void StartRelayConnection()
{
if (m_localUser.IsHost)
m_relaySetup = gameObject.AddComponent<RelayUtpSetupHost>();
else
m_relaySetup = gameObject.AddComponent<RelayUtpSetupClient>();
m_relaySetup.BeginRelayJoin(m_localLobby, m_localUser, OnRelayConnected);
void OnRelayConnected(bool didSucceed, RelayUtpClient client)
{
Component.Destroy(m_relaySetup);
m_relaySetup = null;
if (!didSucceed)
{
Debug.LogError("Relay connection failed! Retrying in 5s...");
StartCoroutine(RetryConnection(StartRelayConnection, m_localLobby.LobbyID));
return;
StartCoroutine(RetryConnection(StartVivoxJoin, m_LocalLobby.LobbyID.Value));
m_relayClient = client;
if (m_localUser.IsHost)
CompleteRelayConnection();
else
Debug.Log("Client is now waiting for approval...");
private IEnumerator RetryConnection(Action doConnection, string lobbyId)
IEnumerator RetryConnection(Action doConnection, string lobbyId)
if (m_localLobby != null && m_localLobby.LobbyID == lobbyId && !string.IsNullOrEmpty(lobbyId)) // Ensure we didn't leave the lobby during this waiting period.
if (m_LocalLobby != null && m_LocalLobby.LobbyID.Value == lobbyId && !string.IsNullOrEmpty(lobbyId)
) // Ensure we didn't leave the lobby during this waiting period.
private void ConfirmApproval()
{
if (!m_localUser.IsHost && m_localUser.IsApproved)
{
CompleteRelayConnection();
StartVivoxJoin();
}
}
private void CompleteRelayConnection()
void SetLobbyView()
OnReceiveMessage(MessageType.LobbyUserStatus, UserStatus.Lobby);
}
private void SetUserLobbyState()
{
Debug.Log($"Setting Lobby user state {GameState.Lobby}");
OnReceiveMessage(MessageType.LobbyUserStatus, UserStatus.Lobby);
SetLocalUserStatus(PlayerStatus.Lobby);
private void ResetLocalLobby()
void ResetLocalLobby()
m_localLobby.CopyObserved(new LocalLobby.LobbyData(), new Dictionary<string, LobbyUser>());
m_localLobby.AddPlayer(m_localUser); // As before, the local player will need to be plugged into UI before the lobby join actually happens.
m_localLobby.RelayServer = null;
m_LocalLobby.ResetLobby();
m_LocalLobby.RelayServer = null;
}
#region Teardown

/// So, we need to delay just briefly to let the request happen (though we don't need to wait for the result).
/// </summary>
private IEnumerator LeaveBeforeQuit()
IEnumerator LeaveBeforeQuit()
{
ForceLeaveAttempt();
yield return null;

private bool OnWantToQuit()
bool OnWantToQuit()
bool canQuit = string.IsNullOrEmpty(m_localLobby?.LobbyID);
bool canQuit = string.IsNullOrEmpty(m_LocalLobby?.LobbyID.Value);
private void OnDestroy()
void OnDestroy()
LobbyManager.Dispose();
private void ForceLeaveAttempt()
void ForceLeaveAttempt()
Locator.Get.Messenger.Unsubscribe(this);
if (!string.IsNullOrEmpty(m_localLobby?.LobbyID))
if (!string.IsNullOrEmpty(m_LocalLobby?.LobbyID.Value))
LobbyAsyncRequests.Instance.LeaveLobbyAsync(m_localLobby?.LobbyID, null);
m_localLobby = null;
#pragma warning disable 4014
LobbyManager.LeaveLobbyAsync();
#pragma warning restore 4014
m_LocalLobby = null;
}
}

339
Assets/Scripts/GameLobby/Game/LocalLobby.cs


using System.Collections.Generic;
using System.Text;
using UnityEngine;
using UnityEngine.Serialization;
namespace LobbyRelaySample
{

/// (The way that the Lobby service handles its data doesn't necessarily match our needs, so we need to map from that to this LocalLobby for use in the sample code.)
/// </summary>
[System.Serializable]
public class LocalLobby : Observed<LocalLobby>
public class LocalLobby
Dictionary<string, LobbyUser> m_LobbyUsers = new Dictionary<string, LobbyUser>();
public Dictionary<string, LobbyUser> LobbyUsers => m_LobbyUsers;
public bool canPullUpdate;
#region LocalLobbyData
public Action<LocalPlayer> onUserJoined;
public struct LobbyData
{
public string LobbyID { get; set; }
public string LobbyCode { get; set; }
public string RelayCode { get; set; }
public string RelayNGOCode { get; set; }
public string LobbyName { get; set; }
public bool Private { get; set; }
public bool Locked { get; set; }
public int AvailableSlots { get; set; }
public int MaxPlayerCount { get; set; }
public LobbyState State { get; set; }
public LobbyColor Color { get; set; }
public long State_LastEdit { get; set; }
public long Color_LastEdit { get; set; }
public long RelayNGOCode_LastEdit { get; set; }
public Action<int> onUserLeft;
public LobbyData(LobbyData existing)
{
LobbyID = existing.LobbyID;
LobbyCode = existing.LobbyCode;
RelayCode = existing.RelayCode;
RelayNGOCode = existing.RelayNGOCode;
LobbyName = existing.LobbyName;
Private = existing.Private;
MaxPlayerCount = existing.MaxPlayerCount;
State = existing.State;
Color = existing.Color;
State_LastEdit = existing.State_LastEdit;
Color_LastEdit = existing.Color_LastEdit;
RelayNGOCode_LastEdit = existing.RelayNGOCode_LastEdit;
AvailableSlots = existing.AvailableSlots;
Locked = existing.Locked;
}
public Action<int> onUserReadyChange;
public LobbyData(string lobbyCode)
{
LobbyID = null;
LobbyCode = lobbyCode;
RelayCode = null;
RelayNGOCode = null;
LobbyName = null;
Private = false;
MaxPlayerCount = -1;
State = LobbyState.Lobby;
Color = LobbyColor.None;
State_LastEdit = 0;
Color_LastEdit = 0;
RelayNGOCode_LastEdit = 0;
AvailableSlots = 4;
Locked = false;
}
public CallbackValue<string> LobbyID = new CallbackValue<string>();
public override string ToString()
{
StringBuilder sb = new StringBuilder("Lobby : ");
sb.AppendLine(LobbyName);
sb.Append("ID: ");
sb.AppendLine(LobbyID);
sb.Append("Code: ");
sb.AppendLine(LobbyCode);
sb.Append("Private: ");
sb.AppendLine(Private.ToString());
sb.Append("Locked: ");
sb.AppendLine(Locked.ToString());
sb.Append("Max Players: ");
sb.AppendLine(MaxPlayerCount.ToString());
sb.Append("AvailableSlots: ");
sb.AppendLine(AvailableSlots.ToString());
sb.Append("LobbyState: ");
sb.AppendLine(State.ToString());
sb.Append("Lobby State Last Edit: ");
sb.AppendLine(new DateTime(State_LastEdit).ToString());
sb.Append("LobbyColor: ");
sb.AppendLine(Color.ToString());
sb.Append("Color Last Edit: ");
sb.AppendLine(new DateTime(Color_LastEdit).ToString());
sb.Append("RelayCode: ");
sb.AppendLine(RelayCode);
sb.Append("RelayNGO: ");
sb.AppendLine(RelayNGOCode);
sb.Append("Relay NGO last Edit: ");
sb.AppendLine(new DateTime(RelayNGOCode_LastEdit).ToString());
return sb.ToString();
}
}
public CallbackValue<string> LobbyCode = new CallbackValue<string>();
public LobbyData Data => m_Data;
LobbyData m_Data;
public CallbackValue<string> RelayCode = new CallbackValue<string>();
ServerAddress m_RelayServer;
public CallbackValue<ServerAddress> RelayServer = new CallbackValue<ServerAddress>();
public CallbackValue<string> LobbyName = new CallbackValue<string>();
/// <summary>Used only for visual output of the Relay connection info. The obfuscated Relay server IP is obtained during allocation in the RelayUtpSetup.</summary>
public ServerAddress RelayServer
{
get => m_RelayServer;
set
{
m_RelayServer = value;
OnChanged(this);
}
}
public CallbackValue<string> HostID = new CallbackValue<string>();
#endregion
public CallbackValue<LobbyState> LocalLobbyState = new CallbackValue<LobbyState>();
public void AddPlayer(LobbyUser user)
{
if (m_LobbyUsers.ContainsKey(user.ID))
{
Debug.LogError($"Cant add player {user.DisplayName}({user.ID}) to lobby: {LobbyID} twice");
return;
}
public CallbackValue<bool> Locked = new CallbackValue<bool>();
DoAddPlayer(user);
OnChanged(this);
}
public CallbackValue<bool> Private = new CallbackValue<bool>();
private void DoAddPlayer(LobbyUser user)
{
m_LobbyUsers.Add(user.ID, user);
user.onChanged += OnChangedUser;
}
public CallbackValue<int> AvailableSlots = new CallbackValue<int>();
public void RemovePlayer(LobbyUser user)
{
DoRemoveUser(user);
OnChanged(this);
}
public CallbackValue<int> MaxPlayerCount = new CallbackValue<int>();
private void DoRemoveUser(LobbyUser user)
{
if (!m_LobbyUsers.ContainsKey(user.ID))
{
Debug.LogWarning($"Player {user.DisplayName}({user.ID}) does not exist in lobby: {LobbyID}");
return;
}
public CallbackValue<LobbyColor> LocalLobbyColor = new CallbackValue<LobbyColor>();
m_LobbyUsers.Remove(user.ID);
user.onChanged -= OnChangedUser;
}
public CallbackValue<long> LastUpdated = new CallbackValue<long>();
private void OnChangedUser(LobbyUser user)
{
OnChanged(this);
}
public int PlayerCount => m_LocalPlayers.Count;
ServerAddress m_RelayServer;
public string LobbyID
{
get => m_Data.LobbyID;
set
{
m_Data.LobbyID = value;
OnChanged(this);
}
}
public List<LocalPlayer> LocalPlayers => m_LocalPlayers;
List<LocalPlayer> m_LocalPlayers = new List<LocalPlayer>();
public string LobbyCode
public void ResetLobby()
get => m_Data.LobbyCode;
set
{
m_Data.LobbyCode = value;
OnChanged(this);
}
}
m_LocalPlayers.Clear();
public string RelayCode
{
get => m_Data.RelayCode;
set
{
m_Data.RelayCode = value;
OnChanged(this);
}
LobbyName.Value = "";
LobbyID.Value = "";
LobbyCode.Value = "";
Locked.Value = false;
Private.Value = false;
LocalLobbyColor.Value = LobbyRelaySample.LobbyColor.None;
AvailableSlots.Value = 4;
MaxPlayerCount.Value = 4;
onUserJoined = null;
onUserLeft = null;
public string RelayNGOCode
public LocalLobby()
get => m_Data.RelayNGOCode;
set
{
m_Data.RelayNGOCode = value;
m_Data.RelayNGOCode_LastEdit = DateTime.Now.Ticks;
OnChanged(this);
}
LastUpdated.Value = DateTime.Now.ToFileTimeUtc();
public string LobbyName
public LocalPlayer GetLocalPlayer(int index)
get => m_Data.LobbyName;
set
{
m_Data.LobbyName = value;
OnChanged(this);
}
return PlayerCount > index ? m_LocalPlayers[index] : null;
public LobbyState State
public void AddPlayer(int index, LocalPlayer user)
get => m_Data.State;
set
{
m_Data.State = value;
m_Data.State_LastEdit = DateTime.Now.Ticks;
OnChanged(this);
}
m_LocalPlayers.Insert(index, user);
user.UserStatus.onChanged += OnUserChangedStatus;
onUserJoined?.Invoke(user);
Debug.Log($"Added User: {user.DisplayName.Value} - {user.ID.Value} to slot {index + 1}/{PlayerCount}");
public bool Private
public void RemovePlayer(int playerIndex)
get => m_Data.Private;
set
{
m_Data.Private = value;
OnChanged(this);
}
m_LocalPlayers[playerIndex].UserStatus.onChanged -= OnUserChangedStatus;
m_LocalPlayers.RemoveAt(playerIndex);
onUserLeft?.Invoke(playerIndex);
public int PlayerCount => m_LobbyUsers.Count;
public int MaxPlayerCount
void OnUserChangedStatus(PlayerStatus status)
get => m_Data.MaxPlayerCount;
set
int readyCount = 0;
foreach (var player in m_LocalPlayers)
m_Data.MaxPlayerCount = value;
OnChanged(this);
if (player.UserStatus.Value == PlayerStatus.Ready)
readyCount++;
}
public LobbyColor Color
{
get => m_Data.Color;
set
{
if (m_Data.Color != value)
{
m_Data.Color = value;
m_Data.Color_LastEdit = DateTime.Now.Ticks;
OnChanged(this);
}
}
onUserReadyChange?.Invoke(readyCount);
public void CopyObserved(LobbyData data, Dictionary<string, LobbyUser> currUsers)
public override string ToString()
// It's possible for the host to edit the lobby in between the time they last pushed lobby data and the time their pull for new lobby data completes.
// If that happens, the edit will be lost, so instead we maintain the time of last edit to detect that case.
var pendingState = data.State;
var pendingColor = data.Color;
var pendingNgoCode = data.RelayNGOCode;
if (m_Data.State_LastEdit > data.State_LastEdit)
pendingState = m_Data.State;
if (m_Data.Color_LastEdit > data.Color_LastEdit)
pendingColor = m_Data.Color;
if (m_Data.RelayNGOCode_LastEdit > data.RelayNGOCode_LastEdit)
pendingNgoCode = m_Data.RelayNGOCode;
m_Data = data;
m_Data.State = pendingState;
m_Data.Color = pendingColor;
m_Data.RelayNGOCode = pendingNgoCode;
if (currUsers == null)
m_LobbyUsers = new Dictionary<string, LobbyUser>();
else
{
List<LobbyUser> toRemove = new List<LobbyUser>();
foreach (var oldUser in m_LobbyUsers)
{
if (currUsers.ContainsKey(oldUser.Key))
oldUser.Value.CopyObserved(currUsers[oldUser.Key]);
else
toRemove.Add(oldUser.Value);
}
foreach (var remove in toRemove)
{
DoRemoveUser(remove);
}
foreach (var currUser in currUsers)
{
if (!m_LobbyUsers.ContainsKey(currUser.Key))
DoAddPlayer(currUser.Value);
}
}
OnChanged(this);
}
StringBuilder sb = new StringBuilder("Lobby : ");
sb.AppendLine(LobbyName.Value);
sb.Append("ID: ");
sb.AppendLine(LobbyID.Value);
sb.Append("Code: ");
sb.AppendLine(LobbyCode.Value);
sb.Append("Locked: ");
sb.AppendLine(Locked.Value.ToString());
sb.Append("Private: ");
sb.AppendLine(Private.Value.ToString());
sb.Append("AvailableSlots: ");
sb.AppendLine(AvailableSlots.Value.ToString());
sb.Append("Max Players: ");
sb.AppendLine(MaxPlayerCount.Value.ToString());
sb.Append("LocalLobbyState: ");
sb.AppendLine(LocalLobbyState.Value.ToString());
sb.Append("Lobby LocalLobbyState Last Edit: ");
sb.AppendLine(new DateTime(LastUpdated.Value).ToString());
sb.Append("LocalLobbyColor: ");
sb.AppendLine(LocalLobbyColor.Value.ToString());
sb.Append("RelayCode: ");
sb.AppendLine(RelayCode.Value);
// This ends up being called from the lobby list when we get data about a lobby without having joined it yet.
public override void CopyObserved(LocalLobby oldObserved)
{
CopyObserved(oldObserved.Data, oldObserved.m_LobbyUsers);
return sb.ToString();
}
}

24
Assets/Scripts/GameLobby/Game/ServerAddress.cs


using System;
public class ServerAddress
public class ServerAddress : IEquatable<ServerAddress>
{
string m_IP;
int m_Port;

{
return $"{m_IP}:{m_Port}";
}
public bool Equals(ServerAddress other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return m_IP == other.m_IP && m_Port == other.m_Port;
}
#pragma warning disable CS0659
public override bool Equals(object obj)
#pragma warning restore CS0659
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((ServerAddress)obj);
}
}
}

26
Assets/Scripts/GameLobby/Infrastructure/LogHandlerSettings.cs


/// <summary>
/// Acts as a buffer between receiving requests to display error messages to the player and running the pop-up UI to do so.
/// </summary>
public class LogHandlerSettings : MonoBehaviour, IReceiveMessages
public class LogHandlerSettings : MonoBehaviour
{
[SerializeField]
[Tooltip("Only logs of this level or higher will appear in the console.")]

private PopUpUI m_popUp;
private void Awake()
public static LogHandlerSettings Instance
LogHandler.Get().mode = m_editorLogVerbosity;
Locator.Get.Messenger.Subscribe(this);
get
{
if (s_LogHandlerSettings != null) return s_LogHandlerSettings;
return s_LogHandlerSettings = FindObjectOfType<LogHandlerSettings>();
}
private void OnDestroy()
static LogHandlerSettings s_LogHandlerSettings;
private void Awake()
Locator.Get.Messenger.Unsubscribe(this);
LogHandler.Get().mode = m_editorLogVerbosity;
Debug.Log($"Starting project with Log Level : {m_editorLogVerbosity.ToString()}");
/// <summary>
/// For convenience while in the Editor, update the log verbosity when its value is changed in the Inspector.

LogHandler.Get().mode = m_editorLogVerbosity;
}
public void OnReceiveMessage(MessageType type, object msg)
{
if (type == MessageType.DisplayErrorPopup && msg != null)
SpawnErrorPopup((string)msg);
}
private void SpawnErrorPopup(string errorMessage)
public void SpawnErrorPopup(string errorMessage)
{
m_popUp.ShowPopup(errorMessage);
}

2
Assets/Scripts/GameLobby/Lobby/AsyncRequestLobby.cs


var lobbyEx = e as LobbyServiceException;
if (lobbyEx.Reason == LobbyExceptionReason.RateLimited) // We have other ways of preventing players from hitting the rate limit, so the developer-facing 429 error is sufficient here.
return;
Locator.Get.Messenger.OnReceiveMessage(MessageType.DisplayErrorPopup, $"Lobby Error: {lobbyEx.Message} ({lobbyEx.InnerException.Message})"); // Lobby error type, then HTTP error type.
LogHandlerSettings.Instance.SpawnErrorPopup( $"Lobby Error: {lobbyEx.Message} ({lobbyEx.InnerException.Message})"); // Lobby error type, then HTTP error type.
}
}
}

139
Assets/Scripts/GameLobby/Lobby/LobbyConverters.cs


/// </summary>
public static class LobbyConverters
{
public static Dictionary<string, string> LocalToRemoteData(LocalLobby lobby)
const string key_RelayCode = nameof(LocalLobby.RelayCode);
const string key_LobbyState = nameof(LocalLobby.LocalLobbyState);
const string key_LobbyColor = nameof(LocalLobby.LocalLobbyColor);
const string key_LastEdit = nameof(LocalLobby.LastUpdated);
const string key_Displayname = nameof(LocalPlayer.DisplayName);
const string key_Userstatus = nameof(LocalPlayer.UserStatus);
const string key_Emote = nameof(LocalPlayer.Emote);
public static Dictionary<string, string> LocalToRemoteLobbyData(LocalLobby lobby)
data.Add("RelayCode", lobby.RelayCode);
data.Add("RelayNGOCode", lobby.RelayNGOCode);
data.Add("State", ((int)lobby.State).ToString()); // Using an int is smaller than using the enum state's name.
data.Add("Color", ((int)lobby.Color).ToString());
data.Add("State_LastEdit", lobby.Data.State_LastEdit.ToString());
data.Add("Color_LastEdit", lobby.Data.Color_LastEdit.ToString());
data.Add("RelayNGOCode_LastEdit", lobby.Data.RelayNGOCode_LastEdit.ToString());
data.Add(key_RelayCode, lobby.RelayCode.Value);
data.Add(key_LobbyState, ((int)lobby.LocalLobbyState.Value).ToString());
data.Add(key_LobbyColor, ((int)lobby.LocalLobbyColor.Value).ToString());
data.Add(key_LastEdit, lobby.LastUpdated.Value.ToString());
public static Dictionary<string, string> LocalToRemoteUserData(LobbyUser user)
public static Dictionary<string, string> LocalToRemoteUserData(LocalPlayer user)
if (user == null || string.IsNullOrEmpty(user.ID))
if (user == null || string.IsNullOrEmpty(user.ID.Value))
data.Add("DisplayName", user.DisplayName); // The lobby doesn't need to know any data beyond the name and state; Relay will handle the rest.
data.Add("UserStatus", ((int)user.UserStatus).ToString());
data.Add(key_Displayname, user.DisplayName.Value);
data.Add(key_Userstatus, ((int)user.UserStatus.Value).ToString());
data.Add(key_Emote, ((int)user.Emote.Value).ToString());
return data;
}

public static void RemoteToLocal(Lobby lobby, LocalLobby lobbyToUpdate)
public static void RemoteToLocal(Lobby remoteLobby, LocalLobby localLobby)
//Copy Data from Lobby into Local lobby fields
LocalLobby.LobbyData info = new LocalLobby.LobbyData(lobbyToUpdate.Data)
if (remoteLobby == null)
{
Debug.LogError("Remote lobby is null, cannot convert.");
return;
}
if (localLobby == null)
LobbyID = lobby.Id,
LobbyCode = lobby.LobbyCode,
Private = lobby.IsPrivate,
LobbyName = lobby.Name,
MaxPlayerCount = lobby.MaxPlayers,
RelayCode = lobby.Data?.ContainsKey("RelayCode") == true ? lobby.Data["RelayCode"].Value : lobbyToUpdate.RelayCode, // By providing RelayCode through the lobby data with Member visibility, we ensure a client is connected to the lobby before they could attempt a relay connection, preventing timing issues between them.
RelayNGOCode = lobby.Data?.ContainsKey("RelayNGOCode") == true ? lobby.Data["RelayNGOCode"].Value : lobbyToUpdate.RelayNGOCode,
State = lobby.Data?.ContainsKey("State") == true ? (LobbyState)int.Parse(lobby.Data["State"].Value) : LobbyState.Lobby,
Color = lobby.Data?.ContainsKey("Color") == true ? (LobbyColor)int.Parse(lobby.Data["Color"].Value) : LobbyColor.None,
State_LastEdit = lobby.Data?.ContainsKey("State_LastEdit") == true ? long.Parse(lobby.Data["State_LastEdit"].Value) : lobbyToUpdate.Data.State_LastEdit,
Color_LastEdit = lobby.Data?.ContainsKey("Color_LastEdit") == true ? long.Parse(lobby.Data["Color_LastEdit"].Value) : lobbyToUpdate.Data.Color_LastEdit,
RelayNGOCode_LastEdit = lobby.Data?.ContainsKey("RelayNGOCode_LastEdit") == true ? long.Parse(lobby.Data["RelayNGOCode_LastEdit"].Value) : lobbyToUpdate.Data.RelayNGOCode_LastEdit
};
Debug.LogError("Local Lobby is null, cannot convert");
return;
}
localLobby.LobbyID.Value = remoteLobby.Id;
localLobby.HostID.Value = remoteLobby.HostId;
localLobby.LobbyName.Value = remoteLobby.Name;
localLobby.LobbyCode.Value = remoteLobby.LobbyCode;
localLobby.Private.Value = remoteLobby.IsPrivate;
localLobby.AvailableSlots.Value = remoteLobby.AvailableSlots;
localLobby.MaxPlayerCount.Value = remoteLobby.MaxPlayers;
localLobby.LastUpdated.Value = remoteLobby.LastUpdated.ToFileTimeUtc();
//Custom Lobby Data Conversions
localLobby.RelayCode.Value = remoteLobby.Data?.ContainsKey(key_RelayCode) == true
? remoteLobby.Data[key_RelayCode].Value
: localLobby.RelayCode.Value;
localLobby.LocalLobbyState.Value = remoteLobby.Data?.ContainsKey(key_LobbyState) == true
? (LobbyState)int.Parse(remoteLobby.Data[key_LobbyState].Value)
: LobbyState.Lobby;
localLobby.LocalLobbyColor.Value = remoteLobby.Data?.ContainsKey(key_LobbyColor) == true
? (LobbyColor)int.Parse(remoteLobby.Data[key_LobbyColor].Value)
: LobbyColor.None;
Dictionary<string, LobbyUser> lobbyUsers = new Dictionary<string, LobbyUser>();
foreach (var player in lobby.Players)
//Custom User Data Conversions
List<string> remotePlayerIDs = new List<string>();
int index = 0;
foreach (var player in remoteLobby.Players)
// If we already know about this player and this player is already connected to Relay, don't overwrite things that Relay might be changing.
if (player.Data?.ContainsKey("UserStatus") == true && int.TryParse(player.Data["UserStatus"].Value, out int status))
var id = player.Id;
remotePlayerIDs.Add(id);
var isHost = remoteLobby.HostId.Equals(player.Id);
var displayName = player.Data?.ContainsKey(key_Displayname) == true
? player.Data[key_Displayname].Value
: default;
var emote = player.Data?.ContainsKey(key_Emote) == true
? (EmoteType)int.Parse(player.Data[key_Emote].Value)
: EmoteType.None;
var userStatus = player.Data?.ContainsKey(key_Userstatus) == true
? (PlayerStatus)int.Parse(player.Data[key_Userstatus].Value)
: PlayerStatus.Lobby;
LocalPlayer localPlayer = localLobby.GetLocalPlayer(index);
if (localPlayer == null)
{
localPlayer = new LocalPlayer(id, index, isHost, displayName, emote, userStatus);
localLobby.AddPlayer(index, localPlayer);
}
else
if (status > (int)UserStatus.Connecting && lobbyToUpdate.LobbyUsers.ContainsKey(player.Id))
{
lobbyUsers.Add(player.Id, lobbyToUpdate.LobbyUsers[player.Id]);
continue;
}
localPlayer.ID.Value = id;
localPlayer.Index.Value = index;
localPlayer.IsHost.Value = isHost;
localPlayer.DisplayName.Value = displayName;
localPlayer.Emote.Value = emote;
localPlayer.UserStatus.Value = userStatus;
// If the player isn't connected to Relay, get the most recent data that the lobby knows.
// (If we haven't seen this player yet, a new local representation of the player will have already been added by the LocalLobby.)
LobbyUser incomingData = new LobbyUser
{
IsHost = lobby.HostId.Equals(player.Id),
DisplayName = player.Data?.ContainsKey("DisplayName") == true ? player.Data["DisplayName"].Value : default,
Emote = player.Data?.ContainsKey("Emote") == true ? (EmoteType)int.Parse(player.Data["Emote"].Value) : default,
UserStatus = player.Data?.ContainsKey("UserStatus") == true ? (UserStatus)int.Parse(player.Data["UserStatus"].Value) : UserStatus.Connecting,
ID = player.Id
};
lobbyUsers.Add(incomingData.ID, incomingData);
index++;
//Push all the data at once so we don't call OnChanged for each variable
lobbyToUpdate.CopyObserved(info, lobbyUsers);
}
/// <summary>

return retLst;
}
private static LocalLobby RemoteToNewLocal(Lobby lobby)
//This might be heavy handed,
static LocalLobby RemoteToNewLocal(Lobby lobby)
{
LocalLobby data = new LocalLobby();
RemoteToLocal(lobby, data);

}
}

8
Assets/Scripts/GameLobby/LobbyRelaySample.asmdef


"GUID:f2d49d9fa7e7eb3418e39723a7d3b92f",
"GUID:6087a74f6015aae4daed9a2577a7596c",
"GUID:4ebbcb26024b547159a86c39de1a8fa5",
"GUID:3bf5041814073ec4089849c425919d5a",
"GUID:3b8ed52f1b5c64994af4c4e0aa4b6c4b"
"GUID:3b8ed52f1b5c64994af4c4e0aa4b6c4b",
"GUID:894a6cc6ed5cd2645bb542978cbed6a9"
"precompiledReferences": [],
"precompiledReferences": [
""
],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],

104
Assets/Scripts/GameLobby/NGO/InGameRunner.cs


/// Once the NetworkManager has been spawned, we need something to manage the game state and setup other in-game objects
/// that is itself a networked object, to track things like network connect events.
/// </summary>
public class InGameRunner : NetworkBehaviour, IInGameInputHandler
public class InGameRunner : NetworkBehaviour
private Action m_onConnectionVerified, m_onGameEnd;
private int m_expectedPlayerCount; // Used by the host, but we can't call the RPC until the network connection completes.
private bool? m_canSpawnInGameObjects;
private Queue<Vector2> m_pendingSymbolPositions = new Queue<Vector2>();
private float m_symbolSpawnTimer = 0.5f; // Initial time buffer to ensure connectivity before loading objects.
private int m_remainingSymbolCount = 0; // Only used by the host.
private float m_timeout = 10;
private bool m_hasConnected = false;
private NetworkObject m_playerCursorPrefab = default;
private PlayerCursor m_playerCursorPrefab = default;
private NetworkObject m_symbolContainerPrefab = default;
private SymbolContainer m_symbolContainerPrefab = default;
[SerializeField]
private SymbolObject m_symbolObjectPrefab = default;
[SerializeField]

[SerializeField]
private BoxCollider m_collider;
private Transform m_symbolContainerInstance;
private PlayerData m_localUserData; // This has an ID that's not necessarily the OwnerClientId, since all clients will see all spawned objects regardless of ownership.
public Action onGameBeginning;
Action m_onConnectionVerified, m_onGameEnd;
private int
m_expectedPlayerCount; // Used by the host, but we can't call the RPC until the network connection completes.
private bool? m_canSpawnInGameObjects;
private Queue<Vector2> m_pendingSymbolPositions = new Queue<Vector2>();
private float m_symbolSpawnTimer = 0.5f; // Initial time buffer to ensure connectivity before loading objects.
private int m_remainingSymbolCount = 0; // Only used by the host.
private float m_timeout = 10;
private bool m_hasConnected = false;
public void Initialize(Action onConnectionVerified, int expectedPlayerCount, Action onGameEnd, LobbyUser localUser)
[SerializeField]
private SymbolContainer m_symbolContainerInstance;
private PlayerData
m_localUserData; // This has an ID that's not necessarily the OwnerClientId, since all clients will see all spawned objects regardless of ownership.
public static InGameRunner Instance
{
get
{
if (s_Instance!) return s_Instance;
return s_Instance = FindObjectOfType<InGameRunner>();
}
}
static InGameRunner s_Instance;
public void Initialize(Action onConnectionVerified, int expectedPlayerCount, Action onGameBegin,
Action onGameEnd,
LocalPlayer localUser)
onGameBeginning = onGameBegin;
m_localUserData = new PlayerData(localUser.DisplayName, 0);
Locator.Get.Provide(this); // Simplifies access since some networked objects can't easily communicate locally (e.g. the host might call a ClientRpc without that client knowing where the call originated).
m_localUserData = new PlayerData(localUser.DisplayName.Value, 0);
}
public override void OnNetworkSpawn()

private void FinishInitialize()
{
m_symbolContainerInstance = NetworkObject.Instantiate(m_symbolContainerPrefab).transform;
m_symbolContainerInstance = Instantiate(m_symbolContainerPrefab);
m_symbolContainerInstance.NetworkObject.Spawn();
ResetPendingSymbolPositions();
m_killVolume.Initialize(OnSymbolDeactivated);
}

m_pendingSymbolPositions.Clear();
Rect boxRext = new Rect(m_collider.bounds.min.x, m_collider.bounds.min.y, m_collider.bounds.size.x, m_collider.bounds.size.y);
Rect boxRext = new Rect(m_collider.bounds.min.x, m_collider.bounds.min.y, m_collider.bounds.size.x,
m_collider.bounds.size.y);
IList<Vector2> points = m_sequenceSelector.GenerateRandomSpawnPoints(boxRext, 2);
foreach (Vector2 point in points)
m_pendingSymbolPositions.Enqueue(point);

[ServerRpc(RequireOwnership = false)]
private void VerifyConnectionConfirm_ServerRpc(PlayerData clientData)
{
NetworkObject playerCursor = NetworkObject.Instantiate(m_playerCursorPrefab); // Note that the client will not receive the cursor object reference, so the cursor must handle initializing itself.
playerCursor.SpawnWithOwnership(clientData.id);
// Note that the client will not receive the cursor object reference, so the cursor must handle initializing itself.
PlayerCursor playerCursor = Instantiate(m_playerCursorPrefab);
playerCursor.NetworkObject.SpawnWithOwnership(clientData.id);
bool areAllPlayersConnected = NetworkManager.ConnectedClients.Count >= m_expectedPlayerCount; // The game will begin at this point, or else there's a timeout for booting any unconnected players.
// The game will begin at this point, or else there's a timeout for booting any unconnected players.
bool areAllPlayersConnected = NetworkManager.Singleton.ConnectedClients.Count >= m_expectedPlayerCount;
VerifyConnectionConfirm_ClientRpc(clientData.id, areAllPlayersConnected);
}

/// <summary>
/// The game will begin either when all players have connected successfully or after a timeout.
/// </summary>
private void BeginGame()
void BeginGame()
Locator.Get.Messenger.OnReceiveMessage(MessageType.MinigameBeginning, null);
m_introOutroRunner.DoIntro();
GameManager.Instance.BeginGame();
onGameBeginning?.Invoke();
m_introOutroRunner.DoIntro(StartMovingSymbols);
}
void StartMovingSymbols()
{
m_sequenceSelector.SetTargetsAnimatable();
if(IsHost)
m_symbolContainerInstance.StartMovingSymbols(); //TODO fix this for
}
public void Update()

void CheckIfCanSpawnNewSymbol()
{
if (!m_canSpawnInGameObjects.GetValueOrDefault() || m_remainingSymbolCount >= SequenceSelector.k_symbolCount || !IsHost)
if (!m_canSpawnInGameObjects.GetValueOrDefault() ||
m_remainingSymbolCount >= SequenceSelector.symbolCount || !IsHost)
return;
if (m_pendingSymbolPositions.Count > 0)
{

m_symbolSpawnTimer = 0.02f; // Space out the object spawning a little to prevent a lag spike.
SpawnNewSymbol();
if (m_remainingSymbolCount >= SequenceSelector.k_symbolCount)
if (m_remainingSymbolCount >= SequenceSelector.symbolCount)
m_canSpawnInGameObjects = false;
}
}

{
int index = SequenceSelector.k_symbolCount - m_pendingSymbolPositions.Count;
int index = SequenceSelector.symbolCount - m_pendingSymbolPositions.Count;
symbolObj.NetworkObject.TrySetParent(m_symbolContainerInstance, false);
symbolObj.SetPosition_Server(pendingPos);
symbolObj.GetComponent<SymbolObject>().symbolIndex.Value = m_sequenceSelector.GetNextSymbol(index);
symbolObj.SetParentAndPosition_Server(m_symbolContainerInstance.NetworkObject, pendingPos);
symbolObj.SetSymbolIndex_Server(m_sequenceSelector.GetNextSymbol(index));
m_remainingSymbolCount++;
}
}

if (selectedSymbol.Clicked)
return;
if (m_sequenceSelector.ConfirmSymbolCorrect(playerId, selectedSymbol.symbolIndex.Value))
if (m_sequenceSelector.ConfirmSymbolCorrect(playerId, selectedSymbol.SymbolIndex))
{
selectedSymbol.ClickedSequence_ServerRpc(playerId);
m_scorer.ScoreSuccess(playerId);

m_scorer.ScoreFailure(playerId);
}
public void OnSymbolDeactivated()
void OnSymbolDeactivated()
{
if (--m_remainingSymbolCount <= 0)
WaitForEndingSequence_ClientRpc();

private void SendLocalEndGameSignal()
{
Locator.Get.Messenger.OnReceiveMessage(MessageType.EndGame, null); // We only send this message if the game completes, since the player remains in the lobby in that case. If the player leaves with the back button, that instead sends them to the menu.
public void OnReProvided(IInGameInputHandler previousProvider)
{
/*No-op*/
}
}
}

12
Assets/Scripts/GameLobby/NGO/IntroOutroRunner.cs


/// </summary>
public class IntroOutroRunner : MonoBehaviour
{
[SerializeField] private Animator m_animator;
private Action m_onOutroComplete;
[SerializeField]
InGameRunner m_inGameRunner;
[SerializeField] Animator m_animator;
Action m_onIntroComplete, m_onOutroComplete;
public void DoIntro()
public void DoIntro(Action onIntroComplete)
m_onIntroComplete = onIntroComplete;
m_animator.SetTrigger("DoIntro");
}

/// </summary>
public void OnIntroComplete()
{
Locator.Get.Messenger.OnReceiveMessage(MessageType.InstructionsShown, null);
m_onIntroComplete?.Invoke();
}
/// <summary>
/// Called via an AnimationEvent.

14
Assets/Scripts/GameLobby/NGO/NetworkedDataStore.cs


// Using a singleton here since we need spawned PlayerCursors to be able to find it, but we don't need the flexibility offered by the Locator.
public static NetworkedDataStore Instance;
private Dictionary<ulong, PlayerData> m_playerData = new Dictionary<ulong, PlayerData>();
private ulong m_localId;
Dictionary<ulong, PlayerData> m_playerData = new Dictionary<ulong, PlayerData>();
ulong m_localId;
private Action<PlayerData> m_onGetCurrentCallback;
private UnityEvent<PlayerData> m_onEachPlayerCallback;
Action<PlayerData> m_onGetCurrentCallback;
UnityEvent<PlayerData> m_onEachPlayerCallback;
public void Awake()
{

}
[ServerRpc(RequireOwnership = false)]
private void GetAllPlayerData_ServerRpc(ulong callerId)
void GetAllPlayerData_ServerRpc(ulong callerId)
{
var sortedData = m_playerData.Select(kvp => kvp.Value).OrderByDescending(data => data.score);
GetAllPlayerData_ClientRpc(callerId, sortedData.ToArray());

private void GetAllPlayerData_ClientRpc(ulong callerId, PlayerData[] sortedData)
void GetAllPlayerData_ClientRpc(ulong callerId, PlayerData[] sortedData)
{
if (callerId != m_localId)
return;

}
[ServerRpc(RequireOwnership = false)]
private void GetPlayerData_ServerRpc(ulong id, ulong callerId)
void GetPlayerData_ServerRpc(ulong id, ulong callerId)
{
if (m_playerData.ContainsKey(id))
GetPlayerData_ClientRpc(callerId, m_playerData[id]);

52
Assets/Scripts/GameLobby/NGO/PlayerCursor.cs


/// The host will use this object's movement for detecting collision with symbol objects.
/// </summary>
[RequireComponent(typeof(Collider))]
public class PlayerCursor : NetworkBehaviour, IReceiveMessages
public class PlayerCursor : NetworkBehaviour
[SerializeField] private SpriteRenderer m_renderer = default;
[SerializeField] private ParticleSystem m_onClickParticles = default;
[SerializeField] private TMPro.TMP_Text m_nameOutput = default;
private Camera m_mainCamera;
private NetworkVariable<Vector3> m_position = new NetworkVariable<Vector3>(NetworkVariableReadPermission.Everyone, Vector3.zero); // (Using a NetworkTransform to sync position would also work.)
private ulong m_localId;
[SerializeField]
SpriteRenderer m_renderer = default;
[SerializeField]
ParticleSystem m_onClickParticles = default;
[SerializeField]
TMPro.TMP_Text m_nameOutput = default;
Camera m_mainCamera;
NetworkVariable<Vector3> m_position = new NetworkVariable<Vector3>(Vector3.zero);
ulong m_localId;
private Action<ulong, Action<PlayerData>> m_retrieveName;
Action<ulong, Action<PlayerData>> m_retrieveName;
private List<SymbolObject> m_currentlyCollidingSymbols;
public void Awake()
{
Locator.Get.Messenger.Subscribe(this);
}
List<SymbolObject> m_currentlyCollidingSymbols;
/// <summary>
/// This cursor is spawned in dynamically but needs references to some scene objects. Pushing full object references over RPC calls

{
m_retrieveName = NetworkedDataStore.Instance.GetPlayerData;
m_mainCamera = GameObject.Find("InGameCamera").GetComponent<Camera>();
InGameRunner.Instance.onGameBeginning += OnGameBegan;
{ m_renderer.transform.localScale *= 0.75f;
{
m_renderer.transform.localScale *= 0.75f;
{ m_renderer.enabled = false; // The local player should see their cursor instead of the simulated cursor object, since the object will appear laggy.
{
m_renderer.enabled =
false; // The local player should see their cursor instead of the simulated cursor object, since the object will appear laggy.
}
}

if (m_mainCamera == null || !IsOwner)
return;
Vector3 targetPos = (Vector2)m_mainCamera.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, -m_mainCamera.transform.position.z));
Vector3 targetPos = (Vector2)m_mainCamera.ScreenToWorldPoint(new Vector3(Input.mousePosition.x,
Input.mousePosition.y, -m_mainCamera.transform.position.z));
SetPosition_ServerRpc(targetPos); // Client can't set a network variable value.
if (IsSelectInputHit())
SendInput_ServerRpc(m_localId);

if (m_currentlyCollidingSymbols.Count > 0)
{
SymbolObject symbol = m_currentlyCollidingSymbols[0];
Locator.Get.InGameInputHandler.OnPlayerInput(id, symbol);
InGameRunner.Instance.OnPlayerInput(id, symbol);
OnInputVisuals_ClientRpc();
}

m_currentlyCollidingSymbols.Remove(symbol);
}
public void OnReceiveMessage(MessageType type, object msg)
public void OnGameBegan()
if (type == MessageType.MinigameBeginning)
{
m_retrieveName.Invoke(OwnerClientId, SetName_ClientRpc);
Locator.Get.Messenger.Unsubscribe(this);
}
m_retrieveName.Invoke(OwnerClientId, SetName_ClientRpc);
InGameRunner.Instance.onGameBeginning -= OnGameBegan;
}
}

8
Assets/Scripts/GameLobby/NGO/ResultsUserUI.cs


public class ResultsUserUI : NetworkBehaviour
{
[Tooltip("The containers for the player data outputs, in order, to be hidden until the game ends.")]
[SerializeField] private CanvasGroup[] m_containers;
[SerializeField] CanvasGroup[] m_containers;
[SerializeField] private TMPro.TMP_Text[] m_playerNameOutputs;
[SerializeField] TMPro.TMP_Text[] m_playerNameOutputs;
[SerializeField] private TMPro.TMP_Text[] m_playerScoreOutputs;
private int m_index = 0;
[SerializeField] TMPro.TMP_Text[] m_playerScoreOutputs;
int m_index = 0;
public void Start()
{

10
Assets/Scripts/GameLobby/NGO/Scorer.cs


/// </summary>
public class Scorer : NetworkBehaviour
{
[SerializeField] private NetworkedDataStore m_dataStore = default;
private ulong m_localId;
[SerializeField] private TMP_Text m_scoreOutputText = default;
[SerializeField] NetworkedDataStore m_dataStore = default;
ulong m_localId;
[SerializeField] TMP_Text m_scoreOutputText = default;
[SerializeField] private UnityEvent<PlayerData> m_onGameEnd = default;
[SerializeField] UnityEvent<PlayerData> m_onGameEnd = default;
public override void OnNetworkSpawn()
{

}
[ClientRpc]
private void UpdateScoreOutput_ClientRpc(ulong id, int score)
void UpdateScoreOutput_ClientRpc(ulong id, int score)
{
if (m_localId == id)
m_scoreOutputText.text = score.ToString("00");

135
Assets/Scripts/GameLobby/NGO/SequenceSelector.cs


/// Handles selecting the randomized sequence of symbols to spawn, choosing a subset to be the ordered target sequence that each player needs to select.
/// This also handles selecting randomized positions for the symbols, and it sets up the target sequence animation for the instruction sequence.
/// </summary>
public class SequenceSelector : NetworkBehaviour, IReceiveMessages
public class SequenceSelector : NetworkBehaviour
[SerializeField] private SymbolData m_symbolData = default;
[SerializeField] private Image[] m_targetSequenceOutput = default;
public const int k_symbolCount = 140;
private bool m_hasReceivedTargetSequence = false;
private ulong m_localId;
private bool m_canAnimateTargets = false;
[SerializeField]
SymbolData m_SymbolData = default;
[SerializeField]
Image[] m_TargetSequenceOutput = default;
public const int symbolCount = 140;
bool m_HasReceivedTargetSequence = false;
ulong m_LocalId;
bool m_CanAnimateTargets = true;
private List<int> m_fullSequence = new List<int>(); // This is owned by the host, and each index is assigned as a NetworkVariable to each SymbolObject.
private NetworkList<int> m_targetSequence; // This is owned by the host but needs to be available to all clients, so it's a NetworkedList here.
private Dictionary<ulong, int> m_targetSequenceIndexPerPlayer = new Dictionary<ulong, int>(); // Each player's current target. Also owned by the host, indexed by client ID.
// This is owned by the host, and each index is assigned as a NetworkVariable to each SymbolObject.
List<int> m_FullSequenceServer = new List<int>();
// This is owned by the host but needs to be available to all clients, so it's a NetworkedList here.
NetworkList<int> m_targetSequence;
// Each player's current target. Also owned by the host, indexed by client ID.
Dictionary<ulong, int> m_targetSequenceIndexPerPlayer = new Dictionary<ulong, int>();
Locator.Get.Messenger.Subscribe(this);
}
public override void OnDestroy()
{
base.OnDestroy();
Locator.Get.Messenger.Unsubscribe(this);
}
public override void OnNetworkSpawn()

m_localId = NetworkManager.Singleton.LocalClientId;
AddClient_ServerRpc(m_localId);
m_targetSequence.ResetDirty();
m_LocalId = NetworkManager.Singleton.LocalClientId;
AddClient_ServerRpc(m_LocalId);
}
private void ChooseSymbols()

List<int> symbolsForThisGame = SelectSymbols(m_symbolData.m_availableSymbols.Count, numSymbolTypes);
List<int> symbolsForThisGame = SelectSymbols(m_SymbolData.m_availableSymbols.Count, numSymbolTypes);
int numTargetSequences = (int)(k_symbolCount * 2 / 3f) / 3; // About 2/3 of the symbols will be definitely part of the target sequence.
int numTargetSequences =
(int)(symbolCount * 2 / 3f) /
3; // About 2/3 of the symbols will be definitely part of the target sequence.
m_fullSequence.Add(m_targetSequence[2]); // We want a List instead of a Queue or Stack for faster insertion, but we will remove indices backwards so as to not resize other entries.
m_fullSequence.Add(m_targetSequence[1]);
m_fullSequence.Add(m_targetSequence[0]);
m_FullSequenceServer.Add(
m_targetSequence[
2]); // We want a List instead of a Queue or Stack for faster insertion, but we will remove indices backwards so as to not resize other entries.
m_FullSequenceServer.Add(m_targetSequence[1]);
m_FullSequenceServer.Add(m_targetSequence[0]);
// Then, fill in with a good mix of the remaining symbols.
for (int n = 3; n < numSymbolTypes - 1; n++)
AddHalfRemaining(n, 2);

{
int remaining = k_symbolCount - m_fullSequence.Count;
int remaining = symbolCount - m_FullSequenceServer.Count;
int randomIndex = UnityEngine.Random.Range(0, m_fullSequence.Count);
m_fullSequence.Insert(randomIndex, symbolsForThisGame[symbolIndex]);
int randomIndex = UnityEngine.Random.Range(0, m_FullSequenceServer.Count);
m_FullSequenceServer.Insert(randomIndex, symbolsForThisGame[symbolIndex]);
}
}
}

public void Update()
{
// A client can't guarantee timing with the host's selection of the target sequence, so retrieve it once it's available.
if (!m_hasReceivedTargetSequence && m_targetSequence.Count > 0)
if (!m_HasReceivedTargetSequence && m_targetSequence.Count > 0)
m_targetSequenceOutput[n].sprite = m_symbolData.GetSymbolForIndex(m_targetSequence[n]);
m_hasReceivedTargetSequence = true;
ScaleTargetUi(m_localId, 0);
m_TargetSequenceOutput[n].sprite = m_SymbolData.GetSymbolForIndex(m_targetSequence[n]);
m_HasReceivedTargetSequence = true;
ScaleTargetUi(m_LocalId, 0);
}
}

{
ScaleTargetUi(id, sequenceIndex);
}
for (int i = 0; i < m_targetSequenceOutput.Length; i++)
m_targetSequenceOutput[i].transform.localScale = Vector3.one * (sequenceIndex == i || !m_canAnimateTargets ? 1 : 0.7f);
for (int i = 0; i < m_TargetSequenceOutput.Length; i++)
m_TargetSequenceOutput[i].transform.localScale =
Vector3.one * (sequenceIndex == i || !m_CanAnimateTargets ? 1 : 0.7f);
return m_fullSequence[symbolObjectIndex];
return m_FullSequenceServer[symbolObjectIndex];
public void OnReceiveMessage(MessageType type, object msg)
public void SetTargetsAnimatable()
if (type == MessageType.InstructionsShown)
{
m_canAnimateTargets = true;
ScaleTargetUi(m_localId, 0);
}
m_CanAnimateTargets = true;
ScaleTargetUi(m_LocalId, 0);
}
/// <summary>

private struct RectCut
{
public Rect rect;
public bool isVertCut { get { return cutIndex % 3 == 2; } }
public bool isVertCut
{
get { return cutIndex % 3 == 2; }
}
public RectCut(Rect rect, int cutIndex)
{
this.rect = rect;
this.cutIndex = cutIndex;
}
public RectCut(Rect rect, int cutIndex) { this.rect = rect; this.cutIndex = cutIndex; }
public RectCut(float xMin, float xMax, float yMin, float yMax, int cutIndex)
{
this.rect = new Rect(xMin, yMin, xMax - xMin, yMax - yMin);

/// <param name="extent">The minimum space between points, to ensure that spawned symbol objects won't overlap.</param>
/// <param name="count">How many positions to choose.</param>
/// <returns>Position list in arbitrary order.</returns>
public List<Vector2> GenerateRandomSpawnPoints(Rect bounds, float extent, int count = k_symbolCount)
public List<Vector2> GenerateRandomSpawnPoints(Rect bounds, float extent, int count = symbolCount)
{
int numTries = 3;
List<Vector2> points = new List<Vector2>();

points.Clear();
rects.Enqueue(new RectCut(bounds, -1)); // Start with an extra horizontal cut since the space is so tall.
rects.Enqueue(new RectCut(bounds,
-1)); // Start with an extra horizontal cut since the space is so tall.
bool isLargeEnough = (currRect.isVertCut && currRect.rect.width > extent * 2) || (!currRect.isVertCut && currRect.rect.height > extent * 2);
bool isLargeEnough = (currRect.isVertCut && currRect.rect.width > extent * 2) ||
(!currRect.isVertCut && currRect.rect.height > extent * 2);
{ points.Add(currRect.rect.center);
{
points.Add(currRect.rect.center);
float xMin = currRect.rect.xMin, xMax = currRect.rect.xMax, yMin = currRect.rect.yMin, yMax = currRect.rect.yMax;
float xMin = currRect.rect.xMin,
xMax = currRect.rect.xMax,
yMin = currRect.rect.yMin,
yMax = currRect.rect.yMax;
{ float cutPosX = Random.Range(xMin + extent, xMax - extent);
rects.Enqueue( new RectCut(xMin, cutPosX, yMin, yMax, currRect.cutIndex + 1) );
rects.Enqueue( new RectCut(cutPosX, xMax, yMin, yMax, currRect.cutIndex + 1) );
}
{
float cutPosX = Random.Range(xMin + extent, xMax - extent);
rects.Enqueue(new RectCut(xMin, cutPosX, yMin, yMax, currRect.cutIndex + 1));
rects.Enqueue(new RectCut(cutPosX, xMax, yMin, yMax, currRect.cutIndex + 1));
}
{ float cutPosY = Random.Range(yMin + extent, yMax - extent);
rects.Enqueue( new RectCut(xMin, xMax, yMin, cutPosY, currRect.cutIndex + 1) );
rects.Enqueue( new RectCut(xMin, xMax, cutPosY, yMax, currRect.cutIndex + 1) );
{
float cutPosY = Random.Range(yMin + extent, yMax - extent);
rects.Enqueue(new RectCut(xMin, xMax, yMin, cutPosY, currRect.cutIndex + 1));
rects.Enqueue(new RectCut(xMin, xMax, cutPosY, yMax, currRect.cutIndex + 1));
}
}

points.Clear();
int numPerLine = Mathf.CeilToInt(bounds.width / (extent * 1.5f));
for (int n = 0; n < count; n++)
points.Add(new Vector2(Mathf.Lerp(bounds.xMin, bounds.xMax, (n % numPerLine) / (numPerLine - 1f)), n / numPerLine * extent * 1.5f));
points.Add(new Vector2(Mathf.Lerp(bounds.xMin, bounds.xMax, (n % numPerLine) / (numPerLine - 1f)),
n / numPerLine * extent * 1.5f));
}
}

171
Assets/Scripts/GameLobby/NGO/SetupInGame.cs


using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Unity.Netcode.Transports.UTP;
using Unity.Networking.Transport;
using Unity.Services.Relay;
using Unity.Services.Relay.Models;
/// Once the local player is in a lobby and that lobby has entered the In-Game state, this will load in whatever is necessary to actually run the game part.
/// Once the local localPlayer is in a localLobby and that localLobby has entered the In-Game state, this will load in whatever is necessary to actually run the game part.
public class SetupInGame : MonoBehaviour, IReceiveMessages
public class SetupInGame : MonoBehaviour
[SerializeField] private GameObject m_prefabNetworkManager = default;
[SerializeField] private GameObject[] m_disableWhileInGame = default;
[SerializeField]
GameObject m_IngameRunnerPrefab = default;
[SerializeField]
private GameObject[] m_disableWhileInGame = default;
private GameObject m_inGameManagerObj;
private NetworkManager m_networkManager;
private Action<UnityTransport> m_initializeTransport;
private LobbyUser m_localUser;
public void Start()
{ Locator.Get.Messenger.Subscribe(this);
}
public void OnDestroy()
{ Locator.Get.Messenger.Unsubscribe(this);
}
private void SetMenuVisibility(bool areVisible)
{

/// The prefab with the NetworkManager contains all of the assets and logic needed to set up the NGO minigame.
/// The UnityTransport needs to also be set up with a new Allocation from Relay.
/// </summary>
private void CreateNetworkManager()
async Task CreateNetworkManager(LocalLobby localLobby, LocalPlayer localPlayer)
m_inGameManagerObj = GameObject.Instantiate(m_prefabNetworkManager);
m_networkManager = m_inGameManagerObj.GetComponentInChildren<NetworkManager>();
m_inGameRunner = m_inGameManagerObj.GetComponentInChildren<InGameRunner>();
m_inGameRunner.Initialize(OnConnectionVerified, m_lobby.PlayerCount, OnGameEnd, m_localUser);
UnityTransport transport = m_inGameManagerObj.GetComponentInChildren<UnityTransport>();
if (m_localUser.IsHost)
m_inGameManagerObj.AddComponent<RelayUtpNGOSetupHost>().Initialize(this, m_lobby, () => { m_initializeTransport(transport); m_networkManager.StartHost(); });
m_lobby = localLobby;
m_inGameRunner = Instantiate(m_IngameRunnerPrefab).GetComponentInChildren<InGameRunner>();
m_inGameRunner.Initialize(OnConnectionVerified, m_lobby.PlayerCount, OnGameBegin, OnGameEnd,
localPlayer);
if (localPlayer.IsHost.Value)
{
await SetRelayHostData();
NetworkManager.Singleton.StartHost();
}
m_inGameManagerObj.AddComponent<RelayUtpNGOSetupClient>().Initialize(this, m_lobby, () => { m_initializeTransport(transport); m_networkManager.StartClient(); });
{
await AwaitRelayCode(localLobby);
await SetRelayClientData();
NetworkManager.Singleton.StartClient();
}
private void OnConnectionVerified()
{ m_hasConnectedViaNGO = true;
async Task AwaitRelayCode(LocalLobby lobby)
{
string relayCode = lobby.RelayCode.Value;
lobby.RelayCode.onChanged += (code) => relayCode = code;
while (string.IsNullOrEmpty(relayCode))
{
await Task.Delay(100);
}
// These are public for use in the Inspector.
public void OnLobbyChange(LocalLobby lobby)
{ m_lobby = lobby; // Most of the time this is redundant, but we need to get multiple members of the lobby to the Relay setup components, so might as well just hold onto the whole thing.
async Task SetRelayHostData()
{
UnityTransport transport = NetworkManager.Singleton.GetComponentInChildren<UnityTransport>();
var allocation = await Relay.Instance.CreateAllocationAsync(m_lobby.MaxPlayerCount.Value);
var joincode = await Relay.Instance.GetJoinCodeAsync(allocation.AllocationId);
GameManager.Instance.HostSetRelayCode(joincode);
bool isSecure = false;
var endpoint = GetEndpointForAllocation(allocation.ServerEndpoints,
allocation.RelayServer.IpV4, allocation.RelayServer.Port, out isSecure);
transport.SetHostRelayData(AddressFromEndpoint(endpoint), endpoint.Port,
allocation.AllocationIdBytes, allocation.Key, allocation.ConnectionData, isSecure);
public void OnLocalUserChange(LobbyUser user)
{ m_localUser = user; // Same, regarding redundancy.
async Task SetRelayClientData()
{
UnityTransport transport = NetworkManager.Singleton.GetComponentInChildren<UnityTransport>();
var joinAllocation = await Relay.Instance.JoinAllocationAsync(m_lobby.RelayCode.Value);
bool isSecure = false;
var endpoint = GetEndpointForAllocation(joinAllocation.ServerEndpoints,
joinAllocation.RelayServer.IpV4, joinAllocation.RelayServer.Port, out isSecure);
transport.SetClientRelayData(AddressFromEndpoint(endpoint), endpoint.Port,
joinAllocation.AllocationIdBytes, joinAllocation.Key,
joinAllocation.ConnectionData, joinAllocation.HostConnectionData, isSecure);
/// Once the Relay Allocation is created, this passes its data to the UnityTransport.
/// Determine the server endpoint for connecting to the Relay server, for either an Allocation or a JoinAllocation.
/// If DTLS encryption is available, and there's a secure server endpoint available, use that as a secure connection. Otherwise, just connect to the Relay IP unsecured.
public void SetRelayServerData(string address, int port, byte[] allocationBytes, byte[] key, byte[] connectionData, byte[] hostConnectionData, bool isSecure)
NetworkEndPoint GetEndpointForAllocation(
List<RelayServerEndpoint> endpoints,
string ip,
int port,
out bool isSecure)
{
#if ENABLE_MANAGED_UNITYTLS
foreach (RelayServerEndpoint endpoint in endpoints)
{
if (endpoint.Secure && endpoint.Network == RelayServerEndpoint.NetworkOptions.Udp)
{
isSecure = true;
return NetworkEndPoint.Parse(endpoint.Host, (ushort)endpoint.Port);
}
}
#endif
isSecure = false;
return NetworkEndPoint.Parse(ip, (ushort)port);
}
string AddressFromEndpoint(NetworkEndPoint endpoint)
m_initializeTransport = (transport) => { transport.SetRelayServerData(address, (ushort)port, allocationBytes, key, connectionData, hostConnectionData, isSecure); };
return endpoint.Address.Split(':')[0];
public void OnReceiveMessage(MessageType type, object msg)
void OnConnectionVerified()
if (type == MessageType.ConfirmInGameState)
{
m_doesNeedCleanup = true;
SetMenuVisibility(false);
CreateNetworkManager();
}
m_hasConnectedViaNGO = true;
}
else if (type == MessageType.MinigameBeginning)
{
if (!m_hasConnectedViaNGO)
{
// If this player hasn't successfully connected via NGO, forcibly exit the minigame.
Locator.Get.Messenger.OnReceiveMessage(MessageType.DisplayErrorPopup, "Failed to join the game.");
OnGameEnd();
}
}
public void StartNetworkedGame(LocalLobby localLobby, LocalPlayer localPlayer)
{
m_doesNeedCleanup = true;
SetMenuVisibility(false);
#pragma warning disable 4014
CreateNetworkManager(localLobby, localPlayer);
#pragma warning restore 4014
}
else if (type == MessageType.ChangeMenuState)
public void OnGameBegin()
{
if (!m_hasConnectedViaNGO)
// Once we're in-game, any state change reflects the player leaving the game, so we should clean up.
// If this localPlayer hasn't successfully connected via NGO, forcibly exit the minigame.
LogHandlerSettings.Instance.SpawnErrorPopup("Failed to join the game.");
/// Return to the lobby after the game, whether due to the game ending or due to a failed connection.
/// Return to the localLobby after the game, whether due to the game ending or due to a failed connection.
private void OnGameEnd()
public void OnGameEnd()
GameObject.Destroy(m_inGameManagerObj); // Since this destroys the NetworkManager, that will kick off cleaning up networked objects.
NetworkManager.Singleton.Shutdown(true);
Destroy(m_inGameRunner
.transform.parent
.gameObject); // Since this destroys the NetworkManager, that will kick off cleaning up networked objects.
m_lobby.RelayNGOCode = null;
m_lobby.RelayCode.Value = "";
GameManager.Instance.EndGame();
}
}

45
Assets/Scripts/GameLobby/NGO/SymbolContainer.cs


/// It will not begin that movement until it both has been Spawned on the network and it has been informed that the game has started.
/// </summary>
[RequireComponent(typeof(NetworkTransform))]
public class SymbolContainer : NetworkBehaviour, IReceiveMessages
public class SymbolContainer : NetworkBehaviour
private float m_speed = 1;
private bool m_isConnected = false;
private bool m_hasGameStarted = false;
float m_speed = 1;
bool m_isConnected = false;
bool m_hasGameStarted = false;
private void OnGameStarted()
public void StartMovingSymbols()
{
m_hasGameStarted = true;
if (m_isConnected)

public void Awake() // If there's just one player, Start would occur after the GameBeginning message is sent, so use Awake/OnEnable instead.
{
Locator.Get.Messenger.Subscribe(this);
}
public void Start()
{
if (!IsHost)
{
this.enabled = false; // Just disabling this script, not the whole GameObject.
return;
}
GetComponent<NetworkObject>().Spawn();
}
public override void OnNetworkSpawn()
{
if (IsHost)

}
else
{
this.enabled = false; // Just disabling this script, not the whole GameObject.
}
}
void Update()

BeginMotion();
}
private void BeginMotion()
{
transform.position += Time.deltaTime * m_speed*Vector3.down;
}
public void OnReceiveMessage(MessageType type, object msg)
void BeginMotion()
if (type == MessageType.InstructionsShown)
{
Locator.Get.Messenger.Unsubscribe(this);
OnGameStarted();
}
transform.position += Time.deltaTime * m_speed * Vector3.down;
}
}

2
Assets/Scripts/GameLobby/NGO/SymbolData.cs


public Sprite GetSymbolForIndex(int index)
{
if (index < 0 || index >= m_availableSymbols.Count)
if (index < 0 || index >= SymbolCount)
index = 0;
return m_availableSymbols[index];
}

4
Assets/Scripts/GameLobby/NGO/SymbolKillVolume.cs


[RequireComponent(typeof(Collider))]
public class SymbolKillVolume : MonoBehaviour
{
private bool m_isInitialized = false;
private Action m_onSymbolCollided;
bool m_isInitialized = false;
Action m_onSymbolCollided;
public void Initialize(Action onSymbolCollided)
{

44
Assets/Scripts/GameLobby/NGO/SymbolObject.cs


namespace LobbyRelaySample.ngo
{
/// <summary>
/// This holds the logic and data for an individual symbol, which can be "clicked" if the server detects the collision with a player who sends a click input.
/// This holds the logic and data for an individual symbolIndex, which can be "clicked" if the server detects the collision with a player who sends a click input.
[SerializeField] private SymbolData m_symbolData;
[SerializeField] private SpriteRenderer m_renderer;
[SerializeField] private Animator m_animator;
[SerializeField]
SymbolData m_symbolData;
[SerializeField]
SpriteRenderer m_renderer;
[SerializeField]
private Animator m_animator;
[HideInInspector] public NetworkVariable<int> symbolIndex; // The index into SymbolData, not the index of this object.
public int SymbolIndex { get; private set; }
public override void OnNetworkSpawn()
public void SetSymbolIndex_Server(int symbolIndex)
symbolIndex.OnValueChanged += OnSymbolIndexSet;
SetSymbolSprite(symbolIndex);
SetSymbolIndex_ClientRpc(symbolIndex);
/// <summary>
/// Because of the need to distinguish host vs. client calls, we use the symbolIndex NetworkVariable to learn what symbol to display.
/// </summary>
private void OnSymbolIndexSet(int prevValue, int newValue)
[ClientRpc]
public void SetSymbolIndex_ClientRpc(int symbolIndex)
m_renderer.sprite = m_symbolData.GetSymbolForIndex(symbolIndex.Value);
symbolIndex.OnValueChanged -= OnSymbolIndexSet;
SetSymbolSprite(symbolIndex);
public void SetPosition_Server(Vector3 newPosition)
void SetSymbolSprite(int symbolIndex)
SymbolIndex = symbolIndex;
m_renderer.sprite = m_symbolData.GetSymbolForIndex(SymbolIndex);
}
public void SetParentAndPosition_Server(NetworkObject parentObject, Vector3 newPosition)
{
NetworkObject.TrySetParent(parentObject, false);
SetPosition_ClientRpc(newPosition);
}

transform.localPosition = newPosition;
}
[ServerRpc]
public void ClickedSequence_ServerRpc(ulong clickerPlayerId)

[ServerRpc]
public void HideSymbol_ServerRpc()
{
// Actually destroying the symbol objects can cause garbage collection and other delays that might lead to desyncs.
// Actually destroying the symbolIndex objects can cause garbage collection and other delays that might lead to desyncs.
// Disabling the networked object can also cause issues, so instead, just move the object, and it will be cleaned up once the NetworkManager is destroyed.
// (If we used object pooling, this is where we would instead return it to the pool.)
//The animation calls RemoveSymbol(only for server

//It's easier to have the post-animation symbol "deletion" happen entirely in server world rather than depend on client-side animation triggers.
//It's easier to have the post-animation symbolIndex "deletion" happen entirely in server world rather than depend on client-side animation triggers.
}
}

6
Assets/Scripts/GameLobby/Relay/AsyncRequestRelay.cs


{
public class AsyncRequestRelay : AsyncRequest
{
private static AsyncRequestRelay s_instance;
static AsyncRequestRelay s_instance;
public static AsyncRequestRelay Instance
{
get

return;
var relayEx = e as RelayServiceException;
if (relayEx.Reason == RelayExceptionReason.Unknown)
Locator.Get.Messenger.OnReceiveMessage(MessageType.DisplayErrorPopup, "Relay Error: Relay service had an unknown error.");
LogHandlerSettings.Instance.SpawnErrorPopup( "Relay Error: Relay service had an unknown error.");
Locator.Get.Messenger.OnReceiveMessage(MessageType.DisplayErrorPopup, $"Relay Error: {relayEx.Message}");
LogHandlerSettings.Instance.SpawnErrorPopup( $"Relay Error: {relayEx.Message}");
}
}
}

4
Assets/Scripts/GameLobby/Relay/RelayAPIInterface.cs


else
onComplete?.Invoke(response);
}
;
}
/// <summary>

else
onComplete?.Invoke(response);
}
;
}
}
}

12
Assets/Scripts/GameLobby/Tests/Editor/MessengerTests.cs


{
public class MessengerTests
{
#region Test classes
/*#region Test classes
private class Subscriber : IReceiveMessages
class Subscriber : IReceiveMessages
private Action m_thingToDo;
Action m_thingToDo;
public Subscriber(Action thingToDo) { m_thingToDo = thingToDo; }
public void OnReceiveMessage(MessageType type, object msg) { m_thingToDo?.Invoke(); }
}

{
Messenger messenger = new Messenger();
int msgCount = 0;
SubscriberArgs sub = new SubscriberArgs((type, msg) => {
SubscriberArgs sub = new SubscriberArgs((type, msg) => {
msgCount++; // These are just for simple detection of the intended behavior.
if (type == MessageType.RenameRequest) msgCount += 9;
if (msg is string) msgCount += int.Parse(msg as string);

messenger.OnReceiveMessage(MessageType.None, null);
Assert.AreEqual(1, msgCount, "Should have acted on the message.");
}
}*/
}
}

217
Assets/Scripts/GameLobby/Tests/PlayMode/LobbyRoundtripTests.cs


using NUnit.Framework;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text.RegularExpressions;
using Unity.Services.Lobbies;
using System.Threading.Tasks;
using Test.Tools;
using LobbyRelaySample;
using LobbyAPIInterface = LobbyRelaySample.lobby.LobbyAPIInterface;
using Debug = UnityEngine.Debug;
namespace Test
{

/// </summary>
public class LobbyRoundtripTests
{
private string m_workingLobbyId;
private LobbyRelaySample.Auth.SubIdentity_Authentication m_auth;
private bool m_didSigninComplete = false;
private Dictionary<string, PlayerDataObject> m_mockUserData; // This is handled in the LobbyAsyncRequest calls normally, but we need to supply this for the direct Lobby API calls.
string playerID;
Dictionary<string, PlayerDataObject>
m_mockUserData; // This is handled in the LobbyAsyncRequest calls normally, but we need to supply this for the direct Lobby API calls.
LocalPlayer m_LocalUser;
LobbyManager m_LobbyManager;
m_auth = new LobbyRelaySample.Auth.SubIdentity_Authentication(() => { m_didSigninComplete = true; });
m_mockUserData.Add("DisplayName", new PlayerDataObject(PlayerDataObject.VisibilityOptions.Member, "TestUser123"));
m_mockUserData.Add("DisplayName",
new PlayerDataObject(PlayerDataObject.VisibilityOptions.Member, "TestUser123"));
#pragma warning disable 4014
TestAuthSetup();
#pragma warning restore 4014
m_LocalUser = new LocalPlayer(Auth.ID(), 0, false, "TESTPLAYER");
m_LobbyManager = new LobbyManager();
}
async Task TestAuthSetup()
{
await Auth.Authenticate("test");
if (m_workingLobbyId != null)
{ LobbyAPIInterface.DeleteLobbyAsync(m_workingLobbyId, null);
m_workingLobbyId = null;
if (m_LobbyManager.CurrentLobby != null)
{
yield return AsyncTestHelper.Await(async () => await m_LobbyManager.LeaveLobbyAsync());
yield return new WaitForSeconds(0.5f); // We need a yield anyway, so wait long enough to probably delete the lobby. There currently (6/22/2021) aren't other tests that would have issues if this took longer.
}
[OneTimeTearDown]
public void Teardown()
{
m_auth?.Dispose();
}
/// <summary>

public IEnumerator DoRoundtrip()
{
#region Setup
// Wait a reasonable amount of time for sign-in to complete.
if (!m_didSigninComplete)
yield return new WaitForSeconds(3);
if (!m_didSigninComplete)
Assert.Fail("Did not sign in.");
yield return AsyncTestHelper.Await(async () => await Auth.Authenticating());
yield return new WaitForSeconds(1); // To prevent a possible 429 with the upcoming Query request, in case a previous test had one; Query requests can only occur at a rate of 1 per second.
yield return
new WaitForSeconds(
1); // To prevent a possible 429 with the upcoming Query request, in case a previous test had one; Query requests can only occur at a rate of 1 per second.
float timeout = 5;
LobbyAPIInterface.QueryAllLobbiesAsync(new List<QueryFilter>(), (qr) => { queryResponse = qr; });
while (queryResponse == null && timeout > 0)
{ yield return new WaitForSeconds(0.25f);
timeout -= 0.25f;
}
Assert.Greater(timeout, 0, "Timeout check (query #0)");
Debug.Log("Getting Lobby List 1");
yield return AsyncTestHelper.Await(
async () => queryResponse = await m_LobbyManager.RetrieveLobbyListAsync());
Lobby createResponse = null;
timeout = 5;
Lobby createLobby = null;
LobbyAPIInterface.CreateLobbyAsync(m_auth.GetContent("id"), lobbyName, 100, false, m_mockUserData, (r) => { createResponse = r; });
while (createResponse == null && timeout > 0)
{ yield return new WaitForSeconds(0.25f);
timeout -= 0.25f;
}
Assert.Greater(timeout, 0, "Timeout check (create)");
Assert.IsNotNull(createResponse, "CreateLobbyAsync should return a non-null result.");
m_workingLobbyId = createResponse.Id;
Assert.AreEqual(lobbyName, createResponse.Name, "Created lobby should match the provided name.");
yield return AsyncTestHelper.Await(async () =>
createLobby = await m_LobbyManager.CreateLobbyAsync(
lobbyName,
100,
false,
m_LocalUser));
Assert.IsNotNull(createLobby, "CreateLobbyAsync should return a non-null result.");
Assert.AreEqual(lobbyName, createLobby.Name, "Created lobby should match the provided name.");
var createLobbyId = createLobby.Id;
yield return new WaitForSeconds(1); // To prevent a possible 429 with the upcoming Query request.
queryResponse = null;
timeout = 5;
LobbyAPIInterface.QueryAllLobbiesAsync(new List<QueryFilter>(), (qr) => { queryResponse = qr; });
while (queryResponse == null && timeout > 0)
{ yield return new WaitForSeconds(0.25f);
timeout -= 0.25f;
}
Assert.Greater(timeout, 0, "Timeout check (query #1)");
Debug.Log("Getting Lobby List 2");
yield return AsyncTestHelper.Await(
async () => queryResponse = await m_LobbyManager.RetrieveLobbyListAsync());
Assert.AreEqual(1 + numLobbiesIni, queryResponse.Results.Count, "Queried lobbies list should contain the test lobby.");
Assert.IsTrue(queryResponse.Results.Where(r => r.Name == lobbyName).Count() == 1, "Checking queried lobby for name.");
Assert.IsTrue(queryResponse.Results.Where(r => r.Id == m_workingLobbyId).Count() == 1, "Checking queried lobby for ID.");
Assert.AreEqual(1 + numLobbiesIni, queryResponse.Results.Count,
"Queried lobbies list should contain the test lobby.");
Assert.IsTrue(queryResponse.Results.Where(r => r.Name == lobbyName).Count() == 1,
"Checking queried lobby for name.");
Assert.IsTrue(queryResponse.Results.Where(r => r.Id == createLobbyId).Count() == 1,
"Checking queried lobby for ID.");
// Query for solely the test lobby via GetLobby.
Lobby getResponse = null;
timeout = 5;
LobbyAPIInterface.GetLobbyAsync(createResponse.Id, (r) => { getResponse = r; });
while (getResponse == null && timeout > 0)
{ yield return new WaitForSeconds(0.25f);
timeout -= 0.25f;
}
Assert.Greater(timeout, 0, "Timeout check (get)");
Assert.IsNotNull(getResponse, "GetLobbyAsync should return a non-null result.");
Assert.AreEqual(lobbyName, getResponse.Name, "Checking the lobby we got for name.");
Assert.AreEqual(m_workingLobbyId, getResponse.Id, "Checking the lobby we got for ID.");
Debug.Log("Getting current Lobby");
Lobby currentLobby = m_LobbyManager.CurrentLobby;
Assert.IsNotNull(currentLobby, "GetLobbyAsync should return a non-null result.");
Assert.AreEqual(lobbyName, currentLobby.Name, "Checking the lobby we got for name.");
Assert.AreEqual(createLobbyId, currentLobby.Id, "Checking the lobby we got for ID.");
Debug.Log("Deleting current Lobby");
bool didDeleteFinish = false;
timeout = 5;
LobbyAPIInterface.DeleteLobbyAsync(m_workingLobbyId, () => { didDeleteFinish = true; });
while (timeout > 0 && !didDeleteFinish)
{ yield return new WaitForSeconds(0.25f);
timeout -= 0.25f;
}
Assert.Greater(timeout, 0, "Timeout check (delete)");
m_workingLobbyId = null;
yield return AsyncTestHelper.Await(async () => await m_LobbyManager.LeaveLobbyAsync());
// Query to ensure the lobby is gone.
yield return new WaitForSeconds(1); // To prevent a possible 429 with the upcoming Query request.
QueryResponse queryResponseTwo = null;
timeout = 5;
LobbyAPIInterface.QueryAllLobbiesAsync(new List<QueryFilter>(), (qr) => { queryResponseTwo = qr; });
while (queryResponseTwo == null && timeout > 0)
{ yield return new WaitForSeconds(0.25f);
timeout -= 0.25f;
}
Assert.Greater(timeout, 0, "Timeout check (query #2)");
Assert.IsNotNull(queryResponse, "QueryAllLobbiesAsync should return a non-null result. (#2)");
Assert.AreEqual(numLobbiesIni, queryResponseTwo.Results.Count, "Queried lobbies list should be empty.");
createLobbyId = null;
Debug.Log("Getting Lobby List 3");
yield return AsyncTestHelper.Await(
async () => queryResponse = await m_LobbyManager.RetrieveLobbyListAsync());
// Some error messages might be asynchronous, so to reduce spillover into other tests, just wait here for a bit before proceeding.
yield return new WaitForSeconds(3);
Assert.IsNotNull(queryResponse, "QueryAllLobbiesAsync should return a non-null result. (#2)");
Assert.AreEqual(numLobbiesIni, queryResponse.Results.Count, "Queried lobbies list should be empty.");
/// If the Lobby create call fails, we want to ensure we call onComplete so we can act on the failure.
/// If the Lobby create call fails, we return null
public IEnumerator OnCompletesOnFailure()
public IEnumerator CreateFailsWithNull()
if (!m_didSigninComplete)
yield return new WaitForSeconds(3);
if (!m_didSigninComplete)
Assert.Fail("Did not sign in.");
yield return AsyncTestHelper.Await(async () => await Auth.Authenticating());
bool? didComplete = null;
LobbyAPIInterface.CreateLobbyAsync("ThisStringIsInvalidHere", "lobby name", 123, false, m_mockUserData, (r) => { didComplete = (r == null); });
float timeout = 5;
while (didComplete == null && timeout > 0)
{ yield return new WaitForSeconds(0.25f);
timeout -= 0.25f;
}
Lobby createLobby = null;
yield return AsyncTestHelper.Await(async () =>
createLobby = await m_LobbyManager.CreateLobbyAsync(
"lobby name",
123,
false,
m_LocalUser));
Assert.Null(createLobby, "The returned object will be null, so expect to need to handle it.");
yield return
new WaitForSeconds(
3); //Since CreateLobby cannot be queued, we need to give this a buffer before moving on to other tests.
}
[UnityTest]
public IEnumerator CooldownTest()
{
var rateLimiter = new ServiceRateLimiter(1, 3);
Stopwatch timer = new Stopwatch();
timer.Start();
Assert.Greater(timeout, 0, "Timeout check");
Assert.NotNull(didComplete, "Should have called onComplete, even if the async request failed.");
Assert.True(didComplete, "The returned object will be null, so expect to need to handle it.");
//pass Through the first request, which triggers the cooldown.
yield return AsyncTestHelper.Await(async () => await rateLimiter.QueueUntilCooldown());
//Should wait for one second total
yield return AsyncTestHelper.Await(async () => await rateLimiter.QueueUntilCooldown());
timer.Stop();
var elapsedMS = timer.ElapsedMilliseconds;
Debug.Log($"Cooldown took {elapsedMS}/{rateLimiter.coolDownMS} milliseconds.");
var difference = Mathf.Abs(elapsedMS - rateLimiter.coolDownMS);
Assert.IsTrue(difference < 50 && difference >= 0);
}
}
}

4
Assets/Scripts/GameLobby/Tests/PlayMode/Tests.Play.asmdef


"LobbyRelaySample",
"Unity.Services.Lobbies",
"Unity.Services.Relay",
"Unity.Networking.Transport"
"Unity.Networking.Transport",
"Unity.Services.Core",
"Unity.Services.Authentication"
],
"includePlatforms": [],
"excludePlatforms": [],

190
Assets/Scripts/GameLobby/Tests/PlayMode/UtpTests.cs


using System;
using System.Collections;
using System.Threading.Tasks;
using LobbyRelaySample;
using Test.Tools;
using Unity.Services.Core;
namespace Test
{
public class UtpTests
{
private class RelayUtpTest : RelayUtpSetupHost
{
public Action<NetworkEndPoint, bool> OnGetEndpoint { private get; set; }
public void JoinRelayPublic()
{
JoinRelay();
}
protected override void JoinRelay()
{
RelayAPIInterface.AllocateAsync(1, OnAllocation);
void OnAllocation(Allocation allocation)
{
bool isSecure = false;
NetworkEndPoint endpoint = GetEndpointForAllocation(allocation.ServerEndpoints, out isSecure);
OnGetEndpoint?.Invoke(endpoint, isSecure);
// The allocation will be cleaned up automatically, since we won't be pinging it regularly.
}
}
}
private LobbyRelaySample.Auth.SubIdentity_Authentication m_auth;
GameObject m_dummy;
//Only used when testing DTLS
#pragma warning disable CS0414 // This is the "assigned but its value is never used" warning, which will otherwise appear when DTLS is unavailable.
private bool m_didSigninComplete = false;
#pragma warning restore CS0414
[OneTimeSetUp]
public void Setup()
{
m_dummy = new GameObject();
m_auth = new LobbyRelaySample.Auth.SubIdentity_Authentication(() => { m_didSigninComplete = true; });
}
[OneTimeTearDown]
public void Teardown()
{
m_auth?.Dispose();
GameObject.Destroy(m_dummy);
}
[UnityTest]
public IEnumerator DTLSCheck()
{
#if ENABLE_MANAGED_UNITYTLS
if (!m_didSigninComplete)
yield return new WaitForSeconds(3);
if (!m_didSigninComplete)
Assert.Fail("Did not sign in.");
yield return new WaitForSeconds(1); // To prevent a possible 429 after a previous test.
RelayUtpTest relaySetup = m_dummy.AddComponent<RelayUtpTest>();
relaySetup.OnGetEndpoint = OnGetEndpoint;
bool? isSecure = null;
NetworkEndPoint endpoint = default;
relaySetup.JoinRelayPublic();
float timeout = 5;
while (!isSecure.HasValue && timeout > 0)
{
timeout -= 0.25f;
yield return new WaitForSeconds(0.25f);
}
Component.Destroy(relaySetup);
Assert.IsTrue(timeout > 0, "Timeout check.");
Assert.IsTrue(isSecure, "Should have a secure server endpoint.");
Assert.IsTrue(endpoint.IsValid, "Endpoint should be valid.");
void OnGetEndpoint(NetworkEndPoint resultEndpoint, bool resultIsSecure)
{
endpoint = resultEndpoint;
isSecure = resultIsSecure;
}
#else
Assert.Ignore("DTLS encryption for Relay is not currently available for this version of Unity.");
yield break;
#endif
}
}
}
// namespace Test
// {
// public class UtpTests
// {
// class RelayUtpTest : RelayUtpSetupHost
// {
// public Action<NetworkEndPoint, bool> OnGetEndpoint { private get; set; }
//
// public void JoinRelayPublic()
// {
// JoinRelay();
// }
//
// protected override void JoinRelay()
// {
// RelayAPIInterface.AllocateAsync(1, OnAllocation);
//
// void OnAllocation(Allocation allocation)
// {
// bool isSecure = false;
// NetworkEndPoint endpoint = GetEndpointForAllocation(allocation.ServerEndpoints, allocation.RelayServer.IpV4, allocation.RelayServer.Port, out isSecure);
// OnGetEndpoint?.Invoke(endpoint, isSecure);
// // The allocation will be cleaned up automatically, since we won't be pinging it regularly.
// }
// }
// }
//
// GameObject m_dummy;
// //Only used when testing DTLS
// #pragma warning disable CS0414 // This is the "assigned but its value is never used" warning, which will otherwise appear when DTLS is unavailable.
// private bool m_didSigninComplete = false;
// #pragma warning restore CS0414
//
// [OneTimeSetUp]
// public void Setup()
// {
// m_dummy = new GameObject();
// #pragma warning disable 4014
// TestAuthSetup();
// #pragma warning restore 4014
// }
//
// async Task TestAuthSetup()
// {
// await Auth.Authenticate("test");
// }
//
// [OneTimeTearDown]
// public void Teardown()
// {
// GameObject.Destroy(m_dummy);
// }
//
// [UnityTest]
// public IEnumerator DTLSCheck()
// {
// #if ENABLE_MANAGED_UNITYTLS
//
// yield return AsyncTestHelper.Await(async () => await Auth.Authenticating());
//
//
// RelayUtpTest relaySetup = m_dummy.AddComponent<RelayUtpTest>();
// relaySetup.OnGetEndpoint = OnGetEndpoint;
// bool? isSecure = null;
// NetworkEndPoint endpoint = default;
//
// relaySetup.JoinRelayPublic();
// float timeout = 5;
// while (!isSecure.HasValue && timeout > 0)
// {
// timeout -= 0.25f;
// yield return new WaitForSeconds(0.25f);
// }
// Component.Destroy(relaySetup);
// Assert.IsTrue(timeout > 0, "Timeout check.");
//
// Assert.IsTrue(isSecure, "Should have a secure server endpoint.");
// Assert.IsTrue(endpoint.IsValid, "Endpoint should be valid.");
//
// void OnGetEndpoint(NetworkEndPoint resultEndpoint, bool resultIsSecure)
// {
// endpoint = resultEndpoint;
// isSecure = resultIsSecure;
// }
//
// #else
//
// Assert.Ignore("DTLS encryption for Relay is not currently available for this version of Unity.");
// yield break;
//
// #endif
// }
// }
// }

7
Assets/Scripts/GameLobby/UI/BackButtonUI.cs


/// <summary>
/// For navigating the main menu.
/// </summary>
public class BackButtonUI : MonoBehaviour
public class BackButtonUI : UIPanelBase
Locator.Get.Messenger.OnReceiveMessage(MessageType.ChangeMenuState, GameState.JoinMenu);
Manager.UIChangeMenuState(GameState.JoinMenu);
Locator.Get.Messenger.OnReceiveMessage(MessageType.ChangeMenuState, GameState.Menu);
Manager.UIChangeMenuState(GameState.Menu);
}
}
}

11
Assets/Scripts/GameLobby/UI/CountdownUI.cs


{
/// <summary>
/// After all players ready up for the game, this will show the countdown that occurs.
/// This countdown is purely visual, to give clients a moment if they need to un-ready before entering the game;
/// This countdown is purely visual, to give clients a moment if they need to un-ready before entering the game;
public class CountdownUI : ObserverBehaviour<Countdown.Data>
public class CountdownUI : UIPanelBase
protected override void UpdateObserver(Countdown.Data data)
public void OnTimeChanged(float time)
base.UpdateObserver(data);
if (observed.TimeLeft <= 0)
if (time <= 0)
m_CountDownText.SetText($"Starting in: {observed.TimeLeft:0}"); // Note that the ":0" formatting rounds, not truncates.
m_CountDownText.SetText($"Starting in: {time:0}"); // Note that the ":0" formatting rounds, not truncates.
}
}
}

11
Assets/Scripts/GameLobby/UI/CreateMenuUI.cs


public class CreateMenuUI : UIPanelBase
{
public JoinCreateLobbyUI m_JoinCreateLobbyUI;
private LocalLobby.LobbyData m_ServerRequestData = new LocalLobby.LobbyData { LobbyName = "New Lobby", MaxPlayerCount = 4 };
string m_ServerName;
bool m_IsServerPrivate;
public override void Start()
{

public void SetServerName(string serverName)
{
m_ServerRequestData.LobbyName = serverName;
m_ServerName = serverName;
m_ServerRequestData.Private = priv;
m_IsServerPrivate = priv;
Locator.Get.Messenger.OnReceiveMessage(MessageType.CreateLobbyRequest, m_ServerRequestData);
Manager.CreateLobby(m_ServerName, m_IsServerPrivate);
}
}

20
Assets/Scripts/GameLobby/UI/DisplayCodeUI.cs


using System;
using TMPro;
using UnityEngine;

/// Watches a lobby or relay code for updates, displaying the current code to lobby members.
/// </summary>
public class DisplayCodeUI : ObserverPanel<LocalLobby>
public class DisplayCodeUI : UIPanelBase
{
public enum CodeType { Lobby = 0, Relay = 1 }

CodeType m_codeType;
public override void ObservedUpdated(LocalLobby observed)
void LobbyCodeChanged(string newCode)
string code = m_codeType == CodeType.Lobby ? observed.LobbyCode : observed.RelayCode;
if (!string.IsNullOrEmpty(code))
if (!string.IsNullOrEmpty(newCode))
m_outputText.text = code;
m_outputText.text = newCode;
Show();
}
else

}
public override void Start()
{
base.Start();
if(m_codeType==CodeType.Lobby)
Manager.LocalLobby.LobbyCode.onChanged += LobbyCodeChanged;
if(m_codeType==CodeType.Relay)
Manager.LocalLobby.RelayCode.onChanged += LobbyCodeChanged;
}
}
}

7
Assets/Scripts/GameLobby/UI/EmoteButtonUI.cs


using TMPro;
using UnityEngine;
namespace LobbyRelaySample.UI

/// </summary>
public class EmoteButtonUI : MonoBehaviour
public class EmoteButtonUI : UIPanelBase
private EmoteType m_emoteType;
EmoteType m_emoteType;
Locator.Get.Messenger.OnReceiveMessage(MessageType.UserSetEmote, m_emoteType);
Manager.SetLocalUserEmote(m_emoteType);
}
}
}

6
Assets/Scripts/GameLobby/UI/EndGameButtonUI.cs


using UnityEngine;
public class EndGameButtonUI : MonoBehaviour
public class EndGameButtonUI : UIPanelBase
Locator.Get.Messenger.OnReceiveMessage(MessageType.EndGame, null);
Manager.EndGame();
}
}
}

22
Assets/Scripts/GameLobby/UI/GameStateVisibilityUI.cs


/// <summary>
/// Show or hide a UI element based on the current GameState (e.g. in a lobby).
/// </summary>
[RequireComponent(typeof(LocalMenuStateObserver))]
public class GameStateVisibilityUI : ObserverPanel<LocalMenuState>
public class GameStateVisibilityUI : UIPanelBase
public override void ObservedUpdated(LocalMenuState observed)
void GameStateChanged(GameState state)
if (!ShowThisWhen.HasFlag(observed.State))
if (!ShowThisWhen.HasFlag(state))
{
}
}
public override void Start()
{
base.Start();
Manager.onGameStateChanged += GameStateChanged;
}
void OnDestroy()
{
if (Manager == null)
return;
Manager.onGameStateChanged -= GameStateChanged;
}
}
}

99
Assets/Scripts/GameLobby/UI/InLobbyUserUI.cs


using System;
using JetBrains.Annotations;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

/// <summary>
/// When inside a lobby, this will show information about a player, whether local or remote.
/// </summary>
[RequireComponent(typeof(LobbyUserObserver))]
public class InLobbyUserUI : ObserverPanel<LobbyUser>
public class InLobbyUserUI : UIPanelBase
{
[SerializeField]
TMP_Text m_DisplayNameText;

[SerializeField]
Image m_EmoteImage;

[SerializeField]
vivox.VivoxUserHandler m_vivoxUserHandler;
vivox.VivoxUserHandler m_VivoxUserHandler;
public string UserId { get; set; }
LocalPlayer m_LocalPlayer;
public string UserId { get; private set; }
private LobbyUserObserver m_observer;
public void SetUser(LobbyUser myLobbyUser)
public void SetUser(LocalPlayer localPlayer)
if (m_observer == null)
m_observer = GetComponent<LobbyUserObserver>();
m_observer.BeginObserving(myLobbyUser);
UserId = myLobbyUser.ID;
m_vivoxUserHandler.SetId(UserId);
m_LocalPlayer = localPlayer;
UserId = localPlayer.ID.Value;
SetIsHost(localPlayer.IsHost.Value);
SetEmote(localPlayer.Emote.Value);
SetUserStatus(localPlayer.UserStatus.Value);
SetDisplayName(m_LocalPlayer.DisplayName.Value);
SubscribeToPlayerUpdates();
m_VivoxUserHandler.SetId(UserId);
}
void SubscribeToPlayerUpdates()
{
m_LocalPlayer.DisplayName.onChanged += SetDisplayName;
m_LocalPlayer.UserStatus.onChanged += SetUserStatus;
m_LocalPlayer.Emote.onChanged += SetEmote;
m_LocalPlayer.IsHost.onChanged += SetIsHost;
}
void UnsubscribeToPlayerUpdates()
{
if (m_LocalPlayer == null)
return;
if (m_LocalPlayer.DisplayName?.onChanged != null)
m_LocalPlayer.DisplayName.onChanged -= SetDisplayName;
if (m_LocalPlayer.UserStatus?.onChanged != null)
m_LocalPlayer.UserStatus.onChanged -= SetUserStatus;
if (m_LocalPlayer.Emote?.onChanged != null)
m_LocalPlayer.Emote.onChanged -= SetEmote;
if (m_LocalPlayer.IsHost?.onChanged != null)
m_LocalPlayer.IsHost.onChanged -= SetIsHost;
public void OnUserLeft()
public void ResetUI()
if (m_LocalPlayer == null)
return;
SetEmote(EmoteType.None);
SetUserStatus(PlayerStatus.Lobby);
m_observer.EndObserving();
UnsubscribeToPlayerUpdates();
m_LocalPlayer = null;
}
void SetDisplayName(string displayName)
{
m_DisplayNameText.SetText(displayName);
}
void SetUserStatus(PlayerStatus statusText)
{
m_StatusText.SetText(SetStatusFancy(statusText));
}
void SetEmote(EmoteType emote)
{
m_EmoteImage.sprite = EmoteIcon(emote);
public override void ObservedUpdated(LobbyUser observed)
void SetIsHost(bool isHost)
m_DisplayNameText.SetText(observed.DisplayName);
m_StatusText.SetText(SetStatusFancy(observed.UserStatus));
m_EmoteImage.sprite = EmoteIcon(observed.Emote);
m_HostIcon.enabled = observed.IsHost;
m_HostIcon.enabled = isHost;
/// <summary>
/// EmoteType to Icon Sprite
/// m_EmoteIcon[0] = Smile

}
}
string SetStatusFancy(UserStatus status)
string SetStatusFancy(PlayerStatus status)
case UserStatus.Lobby:
case PlayerStatus.Lobby:
case UserStatus.Ready:
case PlayerStatus.Ready:
case UserStatus.Connecting:
case PlayerStatus.Connecting:
case UserStatus.InGame:
case PlayerStatus.InGame:
return "<color=#005500>In Game</color>"; // Green
default:
return "";

}
}

22
Assets/Scripts/GameLobby/UI/JoinCreateLobbyUI.cs


/// <summary>
/// The panel that holds the lobby joining and creation panels.
/// </summary>
public class JoinCreateLobbyUI : ObserverPanel<LocalMenuState>
public class JoinCreateLobbyUI : UIPanelBase
[HideInInspector]
public UnityEvent<JoinCreateTabs> m_OnTabChanged;
[SerializeField] //Serialized for Visisbility in Editor

CurrentTab = JoinCreateTabs.Create;
}
public override void ObservedUpdated(LocalMenuState observed)
void GameStateChanged(GameState state)
if (observed.State == GameState.JoinMenu)
if (state == GameState.JoinMenu)
{
m_OnTabChanged?.Invoke(m_CurrentTab);
Show(false);

Hide();
}
}
public override void Start()
{
base.Start();
Manager.onGameStateChanged += GameStateChanged;
}
void OnDestroy()
{
if (Manager == null)
return;
Manager.onGameStateChanged -= GameStateChanged;
}
}
}

121
Assets/Scripts/GameLobby/UI/JoinMenuUI.cs


using System;
using System.Collections.Generic;
using System.Linq;
using TMPro;
using UnityEngine;

/// Handles the list of LobbyButtons and ensures it stays synchronized with the lobby list from the service.
/// </summary>
public class JoinMenuUI : ObserverPanel<LobbyServiceData>
public class JoinMenuUI : UIPanelBase
LobbyButtonUI m_LobbyButtonPrefab;
LobbyEntryUI m_LobbyEntryPrefab;
[SerializeField]
RectTransform m_LobbyButtonParent;

/// <summary>
/// Key: Lobby ID, Value Lobby UI
/// </summary>
Dictionary<string, LobbyButtonUI> m_LobbyButtons = new Dictionary<string, LobbyButtonUI>();
Dictionary<string, LocalLobby> m_LocalLobby = new Dictionary<string, LocalLobby>();
Dictionary<string, LobbyEntryUI> m_LobbyButtons = new Dictionary<string, LobbyEntryUI>();
LocalLobby.LobbyData m_LocalLobbySelected;
LocalLobby m_LocalLobbySelected;
string m_InputLobbyCode;
Manager.LobbyList.onLobbyListChange += OnLobbyListChanged;
}
void OnDestroy()
{
if (Manager == null)
return;
Manager.LobbyList.onLobbyListChange -= OnLobbyListChanged;
}
void OnTabChanged(JoinCreateTabs tabState)

public void LobbyButtonSelected(LocalLobby lobby)
{
m_LocalLobbySelected = lobby.Data;
m_LocalLobbySelected = lobby;
m_LocalLobbySelected = new LocalLobby.LobbyData(newCode.ToUpper());
m_InputLobbyCode = newCode.ToUpper();
Locator.Get.Messenger.OnReceiveMessage(MessageType.JoinLobbyRequest, m_LocalLobbySelected);
m_LocalLobbySelected = default;
if (m_LocalLobbySelected == null)
return;
var selectedLobbyID = m_LocalLobbySelected.LobbyID.Value;
Manager.JoinLobby(selectedLobbyID, m_InputLobbyCode);
m_LocalLobbySelected = null;
Locator.Get.Messenger.OnReceiveMessage(MessageType.QueryLobbies, null);
Manager.QueryLobbies();
public override void ObservedUpdated(LobbyServiceData observed)
void OnLobbyListChanged(Dictionary<string, LocalLobby> lobbyList)
///Check for new entries, We take CurrentLobbies as the source of truth
List<string> previousKeys = new List<string>(m_LobbyButtons.Keys);
foreach (var codeLobby in observed.CurrentLobbies)
{
var lobbyCodeKey = codeLobby.Key;
var lobbyData = codeLobby.Value;
if (!m_LobbyButtons.ContainsKey(lobbyCodeKey))
{
if (CanDisplay(lobbyData))
AddNewLobbyButton(lobbyCodeKey, lobbyData);
}
else
{
if (CanDisplay(lobbyData))
UpdateLobbyButton(lobbyCodeKey, lobbyData);
else
RemoveLobbyButton(lobbyData);
}
previousKeys.Remove(lobbyCodeKey);
}
foreach (string key in previousKeys) // Need to remove any lobbies from the list that no longer exist.
RemoveLobbyButton(m_LocalLobby[key]);
PruneMissingLobbies(lobbyList.Keys.ToList());
PopulateLobbyButtonList(lobbyList);
}
public void JoinMenuChangedVisibility(bool show)

public void OnQuickJoin()
{
Locator.Get.Messenger.OnReceiveMessage(MessageType.QuickJoin, null);
Manager.QuickJoin();
private bool CanDisplay(LocalLobby lobby)
void PruneMissingLobbies(List<string> lobbyIDs)
return lobby.Data.State == LobbyState.Lobby && !lobby.Private;
var removalList = new List<string>();
foreach (var lobbyID in lobbyIDs)
{
if (!lobbyIDs.Contains(lobbyID))
removalList.Add(lobbyID);
}
foreach (var lobbyID in removalList)
RemoveLobbyButton(lobbyID);
}
void PopulateLobbyButtonList(Dictionary<string, LocalLobby> lobbyList)
{
///Check for new entries, We take CurrentLobbies as the source of truth
foreach (var lobbyKVP in lobbyList)
{
var lobbyID = lobbyKVP.Key;
var lobby = lobbyKVP.Value;
if (!m_LobbyButtons.ContainsKey(lobbyID))
{
if (CanDisplay(lobby))
AddNewLobbyButton(lobbyID, lobby);
}
else
{
if (CanDisplay(lobby))
SetLobbyButton(lobbyID, lobby);
else
RemoveLobbyButton(lobbyID);
}
}
}
bool CanDisplay(LocalLobby lobby)
{
return lobby.LocalLobbyState.Value == LobbyState.Lobby && !lobby.Private.Value;
private void AddNewLobbyButton(string lobbyCode, LocalLobby lobby)
void AddNewLobbyButton(string lobbyCode, LocalLobby lobby)
var lobbyButtonInstance = Instantiate(m_LobbyButtonPrefab, m_LobbyButtonParent);
lobbyButtonInstance.GetComponent<LocalLobbyObserver>().BeginObserving(lobby);
lobby.onDestroyed += RemoveLobbyButton; // Set up to clean itself
var lobbyButtonInstance = Instantiate(m_LobbyEntryPrefab, m_LobbyButtonParent);
m_LocalLobby.Add(lobbyCode, lobby);
lobbyButtonInstance.SetLobby(lobby);
private void UpdateLobbyButton(string lobbyCode, LocalLobby lobby)
void SetLobbyButton(string lobbyCode, LocalLobby lobby)
m_LobbyButtons[lobbyCode].UpdateLobby(lobby);
m_LobbyButtons[lobbyCode].SetLobby(lobby);
private void RemoveLobbyButton(LocalLobby lobby)
void RemoveLobbyButton(string lobbyID)
var lobbyID = lobby.LobbyID;
lobbyButton.GetComponent<LocalLobbyObserver>().EndObserving();
m_LocalLobby.Remove(lobbyID);
}
}

7
Assets/Scripts/GameLobby/UI/LobbyNameUI.cs


/// <summary>
/// Displays the name of the lobby.
/// </summary>
public class LobbyNameUI : ObserverPanel<LocalLobby>
public class LobbyNameUI : UIPanelBase
public override void ObservedUpdated(LocalLobby observed)
public override void Start()
m_lobbyNameText.SetText(observed.LobbyName);
base.Start();
Manager.LocalLobby.LobbyName.onChanged += (s) => { m_lobbyNameText.SetText(s); };
}
}
}

12
Assets/Scripts/GameLobby/UI/LobbyUserVolumeUI.cs


public class LobbyUserVolumeUI : MonoBehaviour
{
[SerializeField]
private UIPanelBase m_volumeSliderContainer;
UIPanelBase m_volumeSliderContainer;
private UIPanelBase m_muteToggleContainer;
UIPanelBase m_muteToggleContainer;
private GameObject m_muteIcon;
GameObject m_muteIcon;
private GameObject m_micMuteIcon;
GameObject m_micMuteIcon;
private Slider m_volumeSlider;
Slider m_volumeSlider;
private Toggle m_muteToggle;
Toggle m_muteToggle;
/// <param name="shouldResetUi">
/// When the user is being added, we want the UI to reset to the default values.

2
Assets/Scripts/GameLobby/UI/NameChangeUI.cs


{
public void OnEndNameEdit(string name)
{
Locator.Get.Messenger.OnReceiveMessage(MessageType.RenameRequest, name);
Manager.SetLocalUserName(name);
}
}
}

19
Assets/Scripts/GameLobby/UI/RateLimitVisibility.cs


[SerializeField]
float m_alphaWhenHidden = 0.5f;
[SerializeField]
LobbyAsyncRequests.RequestType m_requestType;
LobbyManager.RequestType m_requestType;
private void Start()
void Start()
LobbyAsyncRequests.Instance.GetRateLimit(m_requestType).onChanged += UpdateVisibility;
GameManager.Instance.LobbyManager.GetRateLimit(m_requestType).onCooldownChange += UpdateVisibility;
private void OnDestroy()
void OnDestroy()
LobbyAsyncRequests.Instance.GetRateLimit(m_requestType).onChanged -= UpdateVisibility;
if (GameManager.Instance == null || GameManager.Instance.LobbyManager == null)
return;
GameManager.Instance.LobbyManager.GetRateLimit(m_requestType).onCooldownChange -= UpdateVisibility;
private void UpdateVisibility(LobbyAsyncRequests.RateLimitCooldown rateLimit)
void UpdateVisibility(bool isCoolingDown)
if (rateLimit.IsInCooldown)
if (isCoolingDown)
}
}

10
Assets/Scripts/GameLobby/UI/ReadyCheckUI.cs


/// <summary>
/// Button callbacks for the "Ready"/"Not Ready" buttons used to indicate the local player is ready/not ready.
/// </summary>
public class ReadyCheckUI : MonoBehaviour
public class ReadyCheckUI : UIPanelBase
ChangeState(UserStatus.Ready);
ChangeState(PlayerStatus.Ready);
ChangeState(UserStatus.Lobby);
ChangeState(PlayerStatus.Lobby);
private void ChangeState(UserStatus status)
void ChangeState(PlayerStatus status)
Locator.Get.Messenger.OnReceiveMessage(MessageType.LobbyUserStatus, status);
Manager.SetLocalUserStatus(status);
}
}
}

14
Assets/Scripts/GameLobby/UI/RelayAddressUI.cs


/// <summary>
/// Displays the IP when connected to Relay.
/// </summary>
public class RelayAddressUI : ObserverPanel<LocalLobby>
public class RelayAddressUI : UIPanelBase
public override void ObservedUpdated(LocalLobby observed)
public override void Start()
m_IPAddressText.SetText(observed.RelayServer?.ToString());
base.Start();
GameManager.Instance.LocalLobby.RelayServer.onChanged += GotRelayAddress;
}
void GotRelayAddress(ServerAddress address)
{
m_IPAddressText.SetText(address.ToString());
}
}

47
Assets/Scripts/GameLobby/UI/ShowWhenLobbyStateUI.cs


using UnityEngine;
namespace LobbyRelaySample.UI
{
using System;
using UnityEngine;
namespace LobbyRelaySample.UI
{
/// </summary>
public class ShowWhenLobbyStateUI : ObserverPanel<LocalLobby>
{
[SerializeField]
private LobbyState m_ShowThisWhen;
public override void ObservedUpdated(LocalLobby observed)
{
if (m_ShowThisWhen.HasFlag(observed.State))
Show();
else
Hide();
}
}
}
/// </summary>
public class ShowWhenLobbyStateUI : UIPanelBase
{
[SerializeField]
LobbyState m_ShowThisWhen;
public void LobbyChanged(LobbyState lobbyState)
{
if (m_ShowThisWhen.HasFlag(lobbyState))
Show();
else
Hide();
}
public override void Start()
{
base.Start();
Manager.LocalLobby.LocalLobbyState.onChanged += LobbyChanged;
}
}
}

38
Assets/Scripts/GameLobby/UI/SpinnerUI.cs


using System;
using System.Text;
using TMPro;
using UnityEngine;

/// <summary>
/// Controls a simple throbber that is displayed when the lobby list is being refreshed.
/// </summary>
public class SpinnerUI : ObserverPanel<LobbyServiceData>
public class SpinnerUI : UIPanelBase
[SerializeField] private TMP_Text m_errorText;
[SerializeField] private UIPanelBase m_spinnerImage;
[SerializeField] private UIPanelBase m_noServerText;
[SerializeField] private UIPanelBase m_errorTextVisibility;
[SerializeField] TMP_Text m_errorText;
[SerializeField] UIPanelBase m_spinnerImage;
[SerializeField] UIPanelBase m_noServerText;
[SerializeField] UIPanelBase m_errorTextVisibility;
[SerializeField] private UIPanelBase m_raycastBlocker;
[SerializeField] UIPanelBase m_raycastBlocker;
public override void ObservedUpdated(LobbyServiceData observed)
public override void Start()
if (observed.State == LobbyQueryState.Fetching)
base.Start();
Manager.LobbyList.QueryState.onChanged += QueryStateChanged;
}
void OnDestroy()
{
if (Manager == null)
return;
Manager.LobbyList.QueryState.onChanged -= QueryStateChanged;
}
void QueryStateChanged(LobbyQueryState state)
{
if (state == LobbyQueryState.Fetching)
{
Show();
m_spinnerImage.Show();

}
else if (observed.State == LobbyQueryState.Error)
else if (state == LobbyQueryState.Error)
{
m_spinnerImage.Hide();
m_raycastBlocker.Hide();

else if (observed.State == LobbyQueryState.Fetched)
else if (state == LobbyQueryState.Fetched)
if (observed.CurrentLobbies.Count < 1)
if (Manager.LobbyList.CurrentLobbies.Count < 1)
{
m_noServerText.Show();
}

6
Assets/Scripts/GameLobby/UI/StartLobbyButtonUI.cs


using UnityEngine;
using LobbyRelaySample.UI;
namespace LobbyRelaySample
{

public class StartLobbyButtonUI : MonoBehaviour
public class StartLobbyButtonUI : UIPanelBase
Locator.Get.Messenger.OnReceiveMessage(MessageType.ChangeMenuState, GameState.JoinMenu);
Manager.UIChangeMenuState(GameState.JoinMenu);
}
}
}

12
Assets/Scripts/GameLobby/UI/UIPanelBase.cs


private UnityEvent<bool> m_onVisibilityChange;
bool showing;
protected GameManager Manager
{
get
{
if (m_gameManager != null) return m_gameManager;
return m_gameManager = GameManager.Instance;
}
}
GameManager m_gameManager;
CanvasGroup m_canvasGroup;
List<UIPanelBase> m_uiPanelsInChildren = new List<UIPanelBase>(); // Otherwise, when this Shows/Hides, the children won't know to update their own visibility.

{
Show(true);
}
public void Show(bool propagateToChildren)
{
MyCanvasGroup.alpha = 1;

15
Assets/Scripts/GameLobby/UI/UserNameUI.cs


using LobbyRelaySample.ngo;
using TMPro;
using UnityEngine;

/// Displays the player's name.
/// </summary>
public class UserNameUI : ObserverPanel<LobbyUser>
public class UserNameUI : UIPanelBase
public override void ObservedUpdated(LobbyUser observed)
public override async void Start()
{
base.Start();
var localUser = await GameManager.Instance.AwaitLocalUserInitialization();
localUser.DisplayName.onChanged += SetText;
}
void SetText(string text)
m_TextField.SetText(observed.DisplayName);
m_TextField.SetText(text);
}
}
}

46
Assets/Scripts/GameLobby/UI/UserStateVisibilityUI.cs


}
/// <summary>
/// Shows the UI when the LobbyUser matches some conditions, including having the target permissions.
/// Shows the UI when the LocalPlayer matches some conditions, including having the target permissions.
[RequireComponent(typeof(LobbyUserObserver))]
public class UserStateVisibilityUI : ObserverPanel<LobbyUser>
public class UserStateVisibilityUI : UIPanelBase
public UserStatus ShowThisWhen;
public PlayerStatus ShowThisWhen;
bool m_HasStatusFlags = false;
bool m_HasPermissions;
public override void ObservedUpdated(LobbyUser observed)
public override async void Start()
var hasStatusFlags = ShowThisWhen.HasFlag(observed.UserStatus);
base.Start();
var localUser = await Manager.AwaitLocalUserInitialization();
var hasPermissions = false;
localUser.IsHost.onChanged += OnUserHostChanged;
localUser.UserStatus.onChanged += OnUserStatusChanged;
}
if (Permissions.HasFlag(UserPermission.Host) && observed.IsHost)
void OnUserStatusChanged(PlayerStatus observedStatus)
{
m_HasStatusFlags = ShowThisWhen.HasFlag(observedStatus);
CheckVisibility();
}
void OnUserHostChanged(bool isHost)
{
m_HasPermissions = false;
if (Permissions.HasFlag(UserPermission.Host) && isHost)
hasPermissions = true;
m_HasPermissions = true;
else if (Permissions.HasFlag(UserPermission.Client) && !observed.IsHost)
if (Permissions.HasFlag(UserPermission.Client) && !isHost)
hasPermissions = true;
m_HasPermissions = true;
if (hasStatusFlags && hasPermissions)
CheckVisibility();
}
void CheckVisibility()
{
if (m_HasStatusFlags && m_HasPermissions)
}
}

71
Assets/Scripts/GameLobby/UI/LobbyUserListUI.cs


namespace LobbyRelaySample.UI
{
/// <summary>
/// Contains the InLobbyUserUI instances while showing the UI for a lobby.
/// </summary>
[RequireComponent(typeof(LocalLobbyObserver))]
public class InLobbyUserList : ObserverPanel<LocalLobby>
public class LobbyUserListUI : UIPanelBase
List<string> m_CurrentUsers = new List<string>(); // Just for keeping track more easily of which users are already displayed.
/// <summary>
/// When the observed data updates, we need to detect changes to the list of players.
/// </summary>
public override void ObservedUpdated(LocalLobby observed)
LocalLobby m_LocalLobby;
public override void Start()
for (int id = m_CurrentUsers.Count - 1; id >= 0; id--) // We might remove users if they aren't in the new data, so iterate backwards.
{
string userId = m_CurrentUsers[id];
if (!observed.LobbyUsers.ContainsKey(userId))
{
foreach (var ui in m_UserUIObjects)
{
if (ui.UserId == userId)
{
ui.OnUserLeft();
OnUserLeft(userId);
}
}
}
}
base.Start();
m_LocalLobby = GameManager.Instance.LocalLobby;
m_LocalLobby.onUserJoined += OnUserJoined;
m_LocalLobby.onUserLeft += OnUserLeft;
}
foreach (var lobbyUserKvp in observed.LobbyUsers) // If there are new players, we need to hook them into the UI.
{
if (m_CurrentUsers.Contains(lobbyUserKvp.Key))
continue;
m_CurrentUsers.Add(lobbyUserKvp.Key);
void OnUserJoined(LocalPlayer localPlayer)
{
SynchPlayerUI();
}
foreach (var pcu in m_UserUIObjects)
{
if (pcu.IsAssigned)
continue;
pcu.SetUser(lobbyUserKvp.Value);
break;
}
}
void OnUserLeft(int i)
{
SynchPlayerUI();
void OnUserLeft(string userID)
void SynchPlayerUI()
if (!m_CurrentUsers.Contains(userID))
return;
m_CurrentUsers.Remove(userID);
foreach (var ui in m_UserUIObjects)
ui.ResetUI();
for (int i = 0; i < m_LocalLobby.PlayerCount; i++)
{
var lobbySlot = m_UserUIObjects[i];
var player = m_LocalLobby.GetLocalPlayer(i);
if (player == null)
continue;
lobbySlot.SetUser(player);
}
}
}

109
Assets/Scripts/GameLobby/UI/LobbyEntryUI.cs


using TMPro;
using UnityEngine;
using UnityEngine.Events;
namespace LobbyRelaySample.UI
{
/// <summary>
/// Controls an entry in the join menu's list of lobbies, acting as a clickable button as well as displaying info about the lobby.
/// </summary>
[RequireComponent(typeof(LocalLobbyObserver))]
public class LobbyButtonUI : MonoBehaviour
{
[SerializeField]
TMP_Text lobbyNameText;
[SerializeField]
TMP_Text lobbyCountText;
/// <summary>
/// Subscribed to on instantiation to pass our lobby data back
/// </summary>
public UnityEvent<LocalLobby> onLobbyPressed;
LocalLobbyObserver m_DataObserver;
void Awake()
{
m_DataObserver = GetComponent<LocalLobbyObserver>();
}
/// <summary>
/// UI CallBack
/// </summary>
public void OnLobbyClicked()
{
onLobbyPressed?.Invoke(m_DataObserver.observed);
}
public void UpdateLobby(LocalLobby lobby)
{
m_DataObserver.observed.CopyObserved(lobby);
}
public void OnLobbyUpdated(LocalLobby data)
{
lobbyNameText.SetText(data.LobbyName);
lobbyCountText.SetText($"{data.PlayerCount}/{data.MaxPlayerCount}");
}
}
}
using System;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
namespace LobbyRelaySample.UI
{
/// <summary>
/// Controls an entry in the join menu's list of lobbies, acting as a clickable button as well as displaying info about the lobby.
/// </summary>
public class LobbyEntryUI : MonoBehaviour
{
[SerializeField]
ColorLobbyUI m_ColorLobbyUI;
[SerializeField]
TMP_Text lobbyNameText;
[SerializeField]
TMP_Text lobbyCountText;
/// <summary>
/// Subscribed to on instantiation to pass our lobby data back
/// </summary>
public UnityEvent<LocalLobby> onLobbyPressed;
LocalLobby m_Lobby;
/// <summary>
/// UI CallBack
/// </summary>
public void OnLobbyClicked()
{
onLobbyPressed.Invoke(m_Lobby);
}
public void SetLobby(LocalLobby lobby)
{
m_Lobby = lobby;
SetLobbyname(m_Lobby.LobbyName.Value);
SetLobbyCount(m_Lobby.PlayerCount);
m_ColorLobbyUI.SetLobby(lobby);
m_Lobby.LobbyName.onChanged += SetLobbyname;
m_Lobby.onUserJoined += (_) =>
{
SetLobbyCount(m_Lobby.PlayerCount);
};
m_Lobby.onUserLeft += (_) =>
{
SetLobbyCount(m_Lobby.PlayerCount);
};
}
void SetLobbyname(string lobbyName)
{
lobbyNameText.SetText(m_Lobby.LobbyName.Value);
}
void SetLobbyCount(int count)
{
lobbyCountText.SetText($"{count}/{m_Lobby.MaxPlayerCount.Value}");
}
}
}

40
Assets/Scripts/GameLobby/Vivox/VivoxSetup.cs


using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Unity.Services.Authentication;
using Unity.Services.Vivox;
using VivoxUnity;

m_userHandlers = userHandlers;
VivoxService.Instance.Initialize();
Account account = new Account(Locator.Get.Identity.GetSubIdentity(Auth.IIdentityType.Auth).GetContent("id"));
Account account = new Account(AuthenticationService.Instance.PlayerId);
m_loginSession = VivoxService.Instance.Client.GetLoginSession(account);
string token = m_loginSession.GetLoginToken();

onComplete?.Invoke(true);
}
catch (Exception ex)
{ UnityEngine.Debug.LogWarning("Vivox failed to login: " + ex.Message);
{
UnityEngine.Debug.LogWarning("Vivox failed to login: " + ex.Message);
onComplete?.Invoke(false);
}
finally

{
// Special case: It's possible for the player to leave the lobby between the time we called BeginConnect and the time we hit this callback.
// If that's the case, we should abort the rest of the connection process.
if (m_channelSession.ChannelState == ConnectionState.Disconnecting || m_channelSession.ChannelState == ConnectionState.Disconnected)
if (m_channelSession.ChannelState == ConnectionState.Disconnecting ||
m_channelSession.ChannelState == ConnectionState.Disconnected)
UnityEngine.Debug.LogWarning("Vivox channel is already disconnecting. Terminating the channel connect sequence.");
UnityEngine.Debug.LogWarning(
"Vivox channel is already disconnecting. Terminating the channel connect sequence.");
HandleEarlyDisconnect();
return;
}

userHandler.OnChannelJoined(m_channelSession);
}
catch (Exception ex)
{ UnityEngine.Debug.LogWarning("Vivox failed to connect: " + ex.Message);
{
UnityEngine.Debug.LogWarning("Vivox failed to connect: " + ex.Message);
onComplete?.Invoke(false);
m_channelSession?.Disconnect();
}

// in the lobby. So, wait until the connection is completed before disconnecting in that case.
if (m_channelSession.ChannelState == ConnectionState.Connecting)
{
UnityEngine.Debug.LogWarning("Vivox channel is trying to disconnect while trying to complete its connection. Will wait until connection completes.");
UnityEngine.Debug.LogWarning(
"Vivox channel is trying to disconnect while trying to complete its connection. Will wait until connection completes.");
HandleEarlyDisconnect();
return;
}

(result) => { m_loginSession.DeleteChannelSession(id); m_channelSession = null; });
(result) =>
{
m_loginSession.DeleteChannelSession(id);
m_channelSession = null;
});
foreach (VivoxUserHandler userHandler in m_userHandlers)
userHandler.OnChannelLeft();
}

Locator.Get.UpdateSlow.Subscribe(DisconnectOnceConnected, 0.2f);
DisconnectOnceConnected();
private void DisconnectOnceConnected(float unused)
async void DisconnectOnceConnected()
if (m_channelSession?.ChannelState == ConnectionState.Connecting)
while (m_channelSession?.ChannelState == ConnectionState.Connecting)
{
await Task.Delay(200);
Locator.Get.UpdateSlow.Unsubscribe(DisconnectOnceConnected);
}
LeaveLobbyChannel();
}

m_loginSession.Logout();
}
}
}
}

2
Assets/StreamingAssets.meta


fileFormatVersion: 2
guid: 9d011d4adccc3c842a6972b8feb956d5
guid: 9c0ea21ed066647a7a55a0c8959022ea
folderAsset: yes
DefaultImporter:
externalObjects: {}

26
Packages/manifest.json


{
"dependencies": {
"com.unity.2d.sprite": "1.0.0",
"com.unity.collab-proxy": "1.15.13",
"com.unity.collections": "1.0.0-pre.6",
"com.unity.ide.rider": "3.0.7",
"com.unity.ide.visualstudio": "2.0.14",
"com.unity.collab-proxy": "1.17.2",
"com.unity.collections": "1.2.4",
"com.unity.ide.rider": "3.0.15",
"com.unity.ide.visualstudio": "2.0.16",
"com.unity.netcode.adapter.utp": "1.0.0-pre.3",
"com.unity.netcode.gameobjects": "1.0.0-pre.2",
"com.unity.nuget.newtonsoft-json": "2.0.0",
"com.unity.render-pipelines.universal": "10.8.1",
"com.unity.services.authentication": "2.3.1",
"com.unity.services.core": "1.2.0",
"com.unity.netcode.gameobjects": "1.0.2",
"com.unity.nuget.newtonsoft-json": "3.0.2",
"com.unity.render-pipelines.universal": "12.1.6",
"com.unity.services.authentication": "2.2.0",
"com.unity.services.core": "1.4.2",
"com.unity.services.vivox": "15.1.190000-pre.1",
"com.unity.services.wire": "1.1.0",
"com.unity.services.vivox": "15.1.170000-pre.1",
"com.unity.services.wire": "1.1.1",
"com.unity.toolchain.macos-x86_64-linux-x86_64": "2.0.1",
"com.unity.transport": "1.0.0-pre.9",
"com.unity.transport": "1.1.0",
"com.unity.ugui": "1.0.0",
"com.unity.modules.ai": "1.0.0",
"com.unity.modules.androidjni": "1.0.0",

102
Packages/packages-lock.json


},
"com.unity.burst": {
"version": "1.6.6",
"depth": 2,
"depth": 1,
"source": "registry",
"dependencies": {
"com.unity.mathematics": "1.2.1"

"com.unity.collab-proxy": {
"version": "1.15.13",
"version": "1.17.2",
"depth": 0,
"source": "registry",
"dependencies": {

},
"com.unity.collections": {
"version": "1.2.4",
"depth": 2,
"depth": 0,
"source": "registry",
"dependencies": {
"com.unity.burst": "1.6.6",

"url": "https://packages.unity.com"
},
"com.unity.ide.rider": {
"version": "3.0.7",
"version": "3.0.15",
"depth": 0,
"source": "registry",
"dependencies": {

},
"com.unity.ide.visualstudio": {
"version": "2.0.14",
"version": "2.0.16",
"depth": 0,
"source": "registry",
"dependencies": {

},
"com.unity.mathematics": {
"version": "1.2.6",
"depth": 2,
"depth": 1,
"com.unity.netcode.adapter.utp": {
"version": "1.0.0-pre.3",
"com.unity.netcode.gameobjects": {
"version": "1.0.2",
"com.unity.netcode.gameobjects": "1.0.0-pre.3",
"com.unity.transport": "1.0.0-pre.7"
},
"url": "https://packages.unity.com"
},
"com.unity.netcode.gameobjects": {
"version": "1.0.0-pre.3",
"depth": 1,
"source": "registry",
"dependencies": {
"com.unity.modules.ai": "1.0.0",
"com.unity.modules.animation": "1.0.0",
"com.unity.collections": "1.0.0-pre.5"
"com.unity.transport": "1.2.0"
},
"url": "https://packages.unity.com"
},

},
"com.unity.nuget.newtonsoft-json": {
"version": "3.0.2",
"depth": 1,
"depth": 0,
"version": "10.8.1",
"version": "12.1.6",
"source": "registry",
"source": "builtin",
},
"url": "https://packages.unity.com"
}
"version": "10.8.1",
"version": "12.1.6",
"source": "registry",
"source": "builtin",
"com.unity.mathematics": "1.1.0",
"com.unity.render-pipelines.core": "10.8.1",
"com.unity.shadergraph": "10.8.1"
},
"url": "https://packages.unity.com"
"com.unity.mathematics": "1.2.1",
"com.unity.burst": "1.5.0",
"com.unity.render-pipelines.core": "12.1.6",
"com.unity.shadergraph": "12.1.6"
}
"version": "4.3.2",
"version": "4.9.1",
"depth": 2,
"source": "registry",
"dependencies": {},

"version": "2.3.1",
"version": "2.2.0",
"depth": 0,
"source": "registry",
"dependencies": {

"url": "https://packages.unity.com"
},
"com.unity.services.vivox": {
"version": "15.1.190000-pre.1",
"version": "15.1.170000-pre.1",
"com.unity.services.core": "1.3.1",
"com.unity.nuget.newtonsoft-json": "3.0.2"
"com.unity.services.core": "1.1.0-pre.10",
"com.unity.nuget.newtonsoft-json": "2.0.0"
"version": "1.1.0",
"version": "1.1.1",
"depth": 0,
"source": "registry",
"dependencies": {

"url": "https://packages.unity.com"
},
"com.unity.shadergraph": {
"version": "10.8.1",
"version": "12.1.6",
"source": "registry",
"source": "builtin",
"com.unity.render-pipelines.core": "10.8.1",
"com.unity.searcher": "4.3.2"
},
"url": "https://packages.unity.com"
"com.unity.render-pipelines.core": "12.1.6",
"com.unity.searcher": "4.9.1"
}
"version": "0.1.19-preview",
"version": "2.0.2",
"depth": 1,
"source": "registry",
"dependencies": {},

"version": "0.1.15-preview",
"depth": 0,
"version": "2.0.1",
"depth": 1,
"com.unity.sysroot": "0.1.19-preview"
"com.unity.sysroot": "2.0.2"
},
"url": "https://packages.unity.com"
},

},
"url": "https://packages.unity.com"
},
"com.unity.toolchain.macos-x86_64-linux-x86_64": {
"version": "2.0.1",
"depth": 0,
"source": "registry",
"dependencies": {
"com.unity.sysroot": "2.0.2",
"com.unity.sysroot.linux-x86_64": "2.0.1"
},
"url": "https://packages.unity.com"
},
"com.unity.toolchain.win-x86_64-linux-x86_64": {
"version": "0.1.20-preview",
"depth": 0,

"url": "https://packages.unity.com"
},
"com.unity.transport": {
"version": "1.1.0",
"version": "1.2.0",
"depth": 1,
"source": "registry",
"dependencies": {

"com.unity.modules.ui": "1.0.0",
"com.unity.modules.imgui": "1.0.0"
}
},
"com.veriorpies.parrelsync": {
"version": "file:ParrelSync",
"depth": 0,
"source": "embedded",
"dependencies": {}
},
"com.unity.modules.ai": {
"version": "1.0.0",

9
ProjectSettings/GraphicsSettings.asset


--- !u!30 &1
GraphicsSettings:
m_ObjectHideFlags: 0
serializedVersion: 13
serializedVersion: 14
m_Deferred:
m_Mode: 1
m_Shader: {fileID: 69, guid: 0000000000000000f000000000000000, type: 0}

- {fileID: 10770, guid: 0000000000000000f000000000000000, type: 0}
- {fileID: 10783, guid: 0000000000000000f000000000000000, type: 0}
m_PreloadedShaders: []
m_PreloadShadersBatchTimeLimit: -1
m_CustomRenderPipeline: {fileID: 0}
m_CustomRenderPipeline: {fileID: 11400000, guid: f54ab92f70892f44eba32f1c414f5b4a, type: 2}
m_TransparencySortMode: 0
m_TransparencySortAxis: {x: 0, y: 0, z: 1}
m_DefaultRenderingPath: 1

m_FogKeepExp2: 1
m_AlbedoSwatchInfos: []
m_LightsUseLinearIntensity: 0
m_LightsUseColorTemperature: 0
m_LightsUseColorTemperature: 1
m_SRPDefaultSettings:
UnityEngine.Rendering.Universal.UniversalRenderPipeline: {fileID: 11400000, guid: c96e53b1d712ac24eaccfd7f849c709d, type: 2}

20
ProjectSettings/PackageManagerSettings.asset


m_Script: {fileID: 13964, guid: 0000000000000000e000000000000000, type: 0}
m_Name:
m_EditorClassIdentifier:
m_EnablePreviewPackages: 1
m_EnablePreReleasePackages: 1
m_SeeAllPackageVersions: 0
oneTimeWarningShown: 1
m_Registries:
- m_Id: main

m_UserSelectedRegistryName:
m_UserAddingNewScopedRegistry: 0
m_RegistryInfoDraft:
m_ErrorMessage:
m_Original:
m_Id:
m_Name:
m_Url:
m_Scopes: []
m_IsDefault: 0
m_Capabilities: 0
m_Name:
m_Url:
m_Scopes:
-
m_SelectedScopeIndex: 0
m_ErrorMessage:
m_UserModificationsInstanceId: -822
m_OriginalInstanceId: -826
m_LoadAssets: 0

15
ProjectSettings/ProjectSettings.asset


--- !u!129 &1
PlayerSettings:
m_ObjectHideFlags: 0
serializedVersion: 22
serializedVersion: 23
productGUID: 5cdb00278111a2a4885ff1a3fcb6c043
AndroidProfiler: 0
AndroidFilterTouchesWhenObscured: 0

iOSLaunchScreeniPadCustomStoryboardPath:
iOSDeviceRequirements: []
iOSURLSchemes: []
macOSURLSchemes: []
iOSBackgroundModes: 0
iOSMetalForceHardShadows: 0
metalEditorSupport: 1

m_BuildTargetGraphicsJobMode: []
m_BuildTargetGraphicsAPIs:
- m_BuildTarget: AndroidPlayer
m_APIs: 150000000b000000
m_Automatic: 1
m_APIs: 0b00000008000000
m_Automatic: 0
- m_BuildTarget: iOSSupport
m_APIs: 10000000
m_Automatic: 1

m_BuildTargetGroupLightmapEncodingQuality: []
m_BuildTargetGroupLightmapSettings: []
m_BuildTargetNormalMapEncoding: []
m_BuildTargetDefaultTextureCompressionFormat: []
playModeTestRunnerEnabled: 0
runPlayModeTestAsEditModeTest: 0
actionOnDotNetUnhandledException: 1

switchScreenResolutionBehavior: 2
switchUseCPUProfiler: 0
switchUseGOLDLinker: 0
switchLTOSetting: 0
switchApplicationID: 0x01004b9000490000
switchNSODependencies:
switchTitleNames_0:

ps4videoRecordingFeaturesUsed: 0
ps4contentSearchFeaturesUsed: 0
ps4CompatibilityPS5: 0
ps4AllowPS5Detection: 0
ps4GPU800MHz: 1
ps4attribEyeToEyeDistanceSettingVR: 0
ps4IncludedModules: []

webGLThreadsSupport: 0
webGLDecompressionFallback: 0
scriptingDefineSymbols:
1: UGS_BETA_LOBBY_EVENTS
Standalone: UGS_BETA_LOBBY_EVENTS;UGS_LOBBY_EVENTS
additionalCompilerArguments: {}
platformArchitecture: {}
scriptingBackend: {}

suppressCommonWarnings: 1
allowUnsafeCode: 0
useDeterministicCompilation: 1
useReferenceAssemblies: 1
enableRoslynAnalyzers: 1
additionalIl2CppArgs:
scriptingRuntimeVersion: 1

organizationId:
cloudEnabled: 0
legacyClampBlendShapeWeights: 0
playerDataPath:
forceSRGBBlit: 1
virtualTexturingSupportEnabled: 0

4
ProjectSettings/ProjectVersion.txt


m_EditorVersion: 2020.3.31f1
m_EditorVersionWithRevision: 2020.3.31f1 (6b54b7616050)
m_EditorVersion: 2021.2.19f1
m_EditorVersionWithRevision: 2021.2.19f1 (602ecdbb2fb0)

3
ProjectSettings/QualitySettings.asset


shadowResolution: 2
shadowProjection: 1
shadowCascades: 4
shadowDistance: 150
shadowDistance: 160
shadowNearPlaneOffset: 3
shadowCascade2Split: 0.33333334
shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667}

Lumin: 5
Nintendo Switch: 5
PS4: 5
Server: 0
Stadia: 5
Standalone: 5
WebGL: 3

2
ProjectSettings/URPProjectSettings.asset


m_Script: {fileID: 11500000, guid: 247994e1f5a72c2419c26a37e9334c01, type: 3}
m_Name:
m_EditorClassIdentifier:
m_LastMaterialVersion: 4
m_LastMaterialVersion: 5

15
README.md


* **Lobby creation**: Players host lobbies for others to join.
* **Lobby query**: Find a list of lobbies with filters, or use lobby codes.
* **Relay obfuscation**: Players in a lobby are connected through an anonymous IP.
* **UTP communication**: Players transmit basic data to lobby members in real time.
* **Lobby + Relay connection management**: Together, the services automatically handle new connections and disconnections.
* **Vivox Voice**: Create a voice channel for the lobby to allow for voice communication, with per-user volume control and muting.

### Running the sample
You will need two “players” to demonstrate the full sample functionality. Create a standalone build to run alongside the Editor in Play mode. Although Auth creates anonymous credentials using your machine’s registry, your Editor and your build have different credentials because they create different registry entries.
You will need two “players” to demonstrate the full sample functionality. You can either use the included Parrelsync package to create duplicate projects for testing, or create a standalone build to run alongside the Editor in Play mode. Although Auth creates anonymous credentials using your machine’s registry, multiple Editor projects and client builds allow for multiple anonmyous auth tokens.
#### **Lobby Join Menu**

&nbsp;&nbsp;&nbsp;&nbsp;**C**. &nbsp;&nbsp; **Lobby user**: A player in the lobby. The player’s name, state, and emote are displayed; this data is synchronized through Relay + UTP, so any changes that a player makes will appear immediately for all connected players. Incoming players will be sent the current data once they have connected.\
&nbsp;&nbsp;&nbsp;&nbsp;**D**. &nbsp;&nbsp; **Emotes**: Shows the player’s Emote, as well as controls for voice chat if the user has a mic connected. \
&nbsp;&nbsp;&nbsp;&nbsp;**E**. &nbsp;&nbsp; **Vivox Voice Controls**: Clicking the audio icon will mute/unmute that user.\
&nbsp;&nbsp;&nbsp;&nbsp;**F**. &nbsp;&nbsp; **Relay IP** :The anonymous server IP that Relay generates. This does not need to be shown to players and is displayed here simply to indicate that Relay is functioning.\
&nbsp;&nbsp;&nbsp;&nbsp;**G**. &nbsp;&nbsp; **Relay Code**: An internal join code generated by Relay that is used during Relay connection. This does not need to be shown to players and is displayed here simply to indicate that Relay is functioning.\
&nbsp;&nbsp;&nbsp;&nbsp;**F**. &nbsp;&nbsp; **Relay IP** : No longer used here with the new Lobby-relay integration.\
&nbsp;&nbsp;&nbsp;&nbsp;**G**. &nbsp;&nbsp; **Relay Code**: No longer used here with the new Lobby-relay integration.\
&nbsp;&nbsp;&nbsp;&nbsp;**H**. &nbsp;&nbsp; **Emote buttons:**: Sets the player’s emote and is synchronized using UTP.\
&nbsp;&nbsp;&nbsp;&nbsp;**I**. &nbsp;&nbsp; **Lobby color**: (Host only) Sets the lobby color for filtering in the Lobby List. This is synchronized through Lobby, so changes won’t appear immediately for all players because Lobby queries are rate limited. See Rate Limits.\
&nbsp;&nbsp;&nbsp;&nbsp;**J**. &nbsp;&nbsp; **Ready button**: Sets a ready state on the player. When all players are ready, the host initiates a countdown to an “in-game” state, and the lobby becomes hidden from the public lobby list.\

* Logic for using the Auth, Lobby, and Relay services are encapsulated in their own directories. Much of the API usage is abstracted away for convenience.
* For example, **LobbyAPIInterface** contains the actual calls into the Lobby API, but **LobbyAsyncRequests** has additional processing on the results of those calls as well as some structures necessary for timing the API calls properly.
* The Relay directory also contains logic for using the Unity Transport (UTP) because it requires a transport to function.
* For example, **LobbyAPIInterface** contains the actual calls into the Lobby API
* **Locator** mimics a Service Locator pattern, allowing for behaviors that might otherwise be Singletons to easily be swapped in and out.
* **Messenger** creates a simple messaging system used to keep unrelated classes decoupled, letting them instead message arbitrary listeners when interesting things happen. For example, a lobby is updated, or a player disconnects from a relay.
* An **Observer** pattern is used for all UI elements and for local copies of remote Lobby and Relay data. An Observer is alerted whenever its observed data changes, and the owner of that data doesn’t need to know who is observing.
* Multiple **Tests** directories are included to demonstrate core behavior and edge cases for some of the code. In particular, the Play mode tests for Lobby and Relay can be used to ensure your connection to the services is functioning correctly. (Run tests using the Test Runner, accessible under **Window > General > Test Runner**.)
* In the Editor, the project assets are broken into nested prefabs for convenience when making changes during sample development. Their details should not be considered vital, although there are UI elements that depend on event handlers that are serialized.

* All operations using Lobby and Relay rely on asynchronous API calls. The sample code has some logic for handling issues that can result from receiving the results at arbitrary times, but it doesn’t have logic for enqueuing calls that the user initiates during setup and cleanup. Rapid operations when entering and exiting lobbies can result in unexpected behavior.

2
Assets/Scripts/GameLobby/Infrastructure/CallbackValue.cs.meta


fileFormatVersion: 2
guid: edfa4d53eaae84c43ba581088940700c
guid: 3167ee4f02a6e4b4bb4d4e8830ea69c5
MonoImporter:
externalObjects: {}
serializedVersion: 2

112
Assets/Prefabs/NGO/NetworkManager.prefab


%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &5481270937743757918
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 5665493986528408842}
- component: {fileID: 5021303663353436182}
- component: {fileID: 1765689956051871792}
m_Layer: 0
m_Name: NetworkManager
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &5665493986528408842
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5481270937743757918}
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 52.01943, z: -6}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &5021303663353436182
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5481270937743757918}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 593a2fe42fa9d37498c96f9a383b6521, type: 3}
m_Name:
m_EditorClassIdentifier:
RunInBackground: 0
LogLevel: 1
NetworkConfig:
ProtocolVersion: 0
NetworkTransport: {fileID: 1765689956051871792}
PlayerPrefab: {fileID: 0}
NetworkPrefabs:
- Override: 0
Prefab: {fileID: 3227847727972158006, guid: 905594b4ee5bb864a84af916cc445d1b, type: 3}
SourcePrefabToOverride: {fileID: 0}
SourceHashToOverride: 0
OverridingTargetPrefab: {fileID: 0}
- Override: 0
Prefab: {fileID: 5240148789413552765, guid: f42ed38d10b57ec48870f76a7a63389e, type: 3}
SourcePrefabToOverride: {fileID: 0}
SourceHashToOverride: 0
OverridingTargetPrefab: {fileID: 0}
- Override: 0
Prefab: {fileID: 8828823320646980938, guid: e371ca3112f9e244ab574b472387b64b, type: 3}
SourcePrefabToOverride: {fileID: 0}
SourceHashToOverride: 0
OverridingTargetPrefab: {fileID: 0}
TickRate: 30
ClientConnectionBufferTimeout: 10
ConnectionApproval: 0
ConnectionData:
EnableTimeResync: 0
TimeResyncInterval: 30
EnsureNetworkVariableLengthSafety: 0
EnableSceneManagement: 1
ForceSamePrefabs: 1
RecycleNetworkIds: 1
NetworkIdRecycleDelay: 120
RpcHashSize: 0
LoadSceneTimeOut: 120
SpawnTimeout: 1
EnableNetworkLogs: 1
--- !u!114 &1765689956051871792
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5481270937743757918}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 6960e84d07fb87f47956e7a81d71c4e6, type: 3}
m_Name:
m_EditorClassIdentifier:
m_ProtocolType: 1
m_MaxPacketQueueSize: 512
m_MaxPayloadSize: 6144
m_MaxSendQueueSize: 98304
m_HeartbeatTimeoutMS: 500
m_ConnectTimeoutMS: 1000
m_MaxConnectAttempts: 60
m_DisconnectTimeoutMS: 30000
ConnectionData:
Address: 127.0.0.1
Port: 7777
ServerListenAddress:
DebugSimulator:
PacketDelayMS: 0
PacketJitterMS: 0
PacketDropRate: 0

7
Assets/Prefabs/NGO/NetworkManager.prefab.meta


fileFormatVersion: 2
guid: b963f71e4874d4066bc72b9224e3ffce
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

117
Assets/Scripts/GameLobby/Auth/Auth.cs


using System.Threading.Tasks;
using Unity.Services.Authentication;
using Unity.Services.Core;
using UnityEngine;
namespace LobbyRelaySample
{
public enum AuthState
{
Initialized,
Authenticating,
Authenticated,
Error,
TimedOut
}
public static class Auth
{
public static AuthState AuthenticationState { get; private set; } = AuthState.Initialized;
public static async Task<AuthState> Authenticate(string profile,int tries = 5)
{
//If we are already authenticated, just return Auth
if (AuthenticationState == AuthState.Authenticated)
{
return AuthenticationState;
}
if (AuthenticationState == AuthState.Authenticating)
{
Debug.LogWarning("Cant Authenticate if we are authenticating or authenticated");
await Authenticating();
return AuthenticationState;
}
var profileOptions = new InitializationOptions();
profileOptions.SetProfile(profile);
await UnityServices.InitializeAsync(profileOptions);
await SignInAnonymouslyAsync(tries);
return AuthenticationState;
}
//Awaitable task that will pass the clientID once authentication is done.
public static string ID()
{
return AuthenticationService.Instance.PlayerId;
}
//Awaitable task that will pass once authentication is done.
public static async Task<AuthState> Authenticating()
{
while (AuthenticationState == AuthState.Authenticating || AuthenticationState == AuthState.Initialized)
{
await Task.Delay(200);
}
return AuthenticationState;
}
public static bool DoneAuthenticating()
{
return AuthenticationState != AuthState.Authenticating &&
AuthenticationState != AuthState.Initialized;
}
static async Task SignInAnonymouslyAsync(int maxRetries)
{
AuthenticationState = AuthState.Authenticating;
var tries = 0;
while (AuthenticationState == AuthState.Authenticating && tries < maxRetries)
{
try
{
//To ensure staging login vs non staging
await AuthenticationService.Instance.SignInAnonymouslyAsync();
if (AuthenticationService.Instance.IsSignedIn && AuthenticationService.Instance.IsAuthorized)
{
AuthenticationState = AuthState.Authenticated;
break;
}
}
catch (AuthenticationException ex)
{
// Compare error code to AuthenticationErrorCodes
// Notify the player with the proper error message
Debug.LogError(ex);
AuthenticationState = AuthState.Error;
}
catch (RequestFailedException exception)
{
// Compare error code to CommonErrorCodes
// Notify the player with the proper error message
Debug.LogError(exception);
AuthenticationState = AuthState.Error;
}
tries++;
await Task.Delay(1000);
}
if (AuthenticationState != AuthState.Authenticated)
{
Debug.LogWarning($"Player was not signed in successfully after {tries} attempts");
AuthenticationState = AuthState.TimedOut;
}
}
public static void SignOut()
{
AuthenticationService.Instance.SignOut(false);
AuthenticationState = AuthState.Initialized;
}
}
}

42
Assets/Scripts/GameLobby/Game/LocalLobbyList.cs


using System;
using System.Collections.Generic;
namespace LobbyRelaySample
{
/// <summary>
/// Used when displaying the lobby list, to indicate when we are awaiting an updated lobby query.
/// </summary>
public enum LobbyQueryState
{
Empty,
Fetching,
Error,
Fetched
}
/// <summary>
/// Holds data related to the Lobby service itself - The latest retrieved lobby list, the state of retrieval.
/// </summary>
[System.Serializable]
public class LocalLobbyList
{
public CallbackValue<LobbyQueryState> QueryState = new CallbackValue<LobbyQueryState>();
public Action<Dictionary<string, LocalLobby>> onLobbyListChange;
Dictionary<string, LocalLobby> m_currentLobbies = new Dictionary<string, LocalLobby>();
/// <summary>
/// Maps from a lobby's ID to the local representation of it. This allows us to remember which remote lobbies are which LocalLobbies.
/// Will only trigger if the dictionary is set wholesale. Changes in the size or contents will not trigger OnChanged.
/// </summary>
public Dictionary<string, LocalLobby> CurrentLobbies
{
get { return m_currentLobbies; }
set
{
m_currentLobbies = value;
onLobbyListChange?.Invoke(m_currentLobbies);
}
}
}
}

53
Assets/Scripts/GameLobby/Game/LocalPlayer.cs


using System;
namespace LobbyRelaySample
{
/// <summary>
/// Current state of the user in the lobby.
/// This is a Flags enum to allow for the Inspector to select multiples for various UI features.
/// </summary>
[Flags]
public enum PlayerStatus
{
None = 0,
Connecting = 1, // User has joined a lobby but has not yet connected to Relay.
Lobby = 2, // User is in a lobby and connected to Relay.
Ready = 4, // User has selected the ready button, to ready for the "game" to start.
InGame = 8, // User is part of a "game" that has started.
Menu = 16 // User is not in a lobby, in one of the main menus.
}
/// <summary>
/// Data for a local player instance. This will update data and is observed to know when to push local player changes to the entire lobby.
/// </summary>
[Serializable]
public class LocalPlayer
{
public CallbackValue<bool> IsHost = new CallbackValue<bool>(false);
public CallbackValue<string> DisplayName = new CallbackValue<string>("");
public CallbackValue<EmoteType> Emote = new CallbackValue<EmoteType>(EmoteType.None);
public CallbackValue<PlayerStatus> UserStatus = new CallbackValue<PlayerStatus>((PlayerStatus)0);
public CallbackValue<string> ID = new CallbackValue<string>("");
public CallbackValue<int> Index = new CallbackValue<int>(0);
public DateTime LastUpdated;
public LocalPlayer(string id, int index, bool isHost, string displayName = default,
EmoteType emote = default, PlayerStatus status = default)
{
ID.Value = id;
IsHost.Value = isHost;
Index.Value = index;
DisplayName.Value = displayName;
Emote.Value = emote;
UserStatus.Value = status;
}
public void ResetState()
{
IsHost.Value = false;
Emote.Value = EmoteType.None;
UserStatus.Value = PlayerStatus.Menu;
}
}
}

45
Assets/Scripts/GameLobby/Infrastructure/CallbackValue.cs


using System;
using UnityEngine;
namespace LobbyRelaySample
{
public class CallbackValue<T>
{
public Action<T> onChanged;
public CallbackValue()
{
}
public CallbackValue(T cachedValue)
{
m_CachedValue = cachedValue;
}
public T Value
{
get => m_CachedValue;
set
{
if (m_CachedValue!=null&&m_CachedValue.Equals(value))
return;
m_CachedValue = value;
onChanged?.Invoke(m_CachedValue);
}
}
public void ForceSet(T value)
{
m_CachedValue = value;
onChanged?.Invoke(m_CachedValue);
}
public void SetNoCallback(T value)
{
m_CachedValue = value;
}
T m_CachedValue = default;
}
}

611
Assets/Scripts/GameLobby/Lobby/LobbyManager.cs


using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Unity.Services.Authentication;
using Unity.Services.Lobbies;
using Unity.Services.Lobbies.Models;
using UnityEngine;
namespace LobbyRelaySample
{
/// <summary>
/// An abstraction layer between the direct calls into the Lobby API and the outcomes you actually want. E.g. you can request to get a readable list of
/// current lobbies and not need to make the query call directly.
/// </summary>
///
/// Manages one Lobby at a time, Only entry points to a lobby with ID is via JoinAsync, CreateAsync, and QuickJoinAsync
public class LobbyManager : IDisposable
{
const string key_RelayCode = nameof(LocalLobby.RelayCode);
const string key_LobbyState = nameof(LocalLobby.LocalLobbyState);
const string key_LobbyColor = nameof(LocalLobby.LocalLobbyColor);
const string key_Displayname = nameof(LocalPlayer.DisplayName);
const string key_Userstatus = nameof(LocalPlayer.UserStatus);
const string key_Emote = nameof(LocalPlayer.Emote);
//Once connected to a lobby, cache the local lobby object so we don't query for it for every lobby operation.
// (This assumes that the game will be actively in just one lobby at a time, though they could be in more on the service side.)
public Lobby CurrentLobby => m_CurrentLobby;
Lobby m_CurrentLobby;
LobbyEventCallbacks m_LobbyEventCallbacks = new LobbyEventCallbacks();
const int
k_maxLobbiesToShow = 16; // If more are necessary, consider retrieving paginated results or using filters.
Task m_HeartBeatTask;
#region Rate Limiting
public enum RequestType
{
Query = 0,
Join,
QuickJoin,
Host
}
public bool InLobby()
{
if (m_CurrentLobby == null)
{
Debug.LogWarning("LobbyManager not currently in a lobby. Did you CreateLobbyAsync or JoinLobbyAsync?");
return false;
}
return true;
}
public ServiceRateLimiter GetRateLimit(RequestType type)
{
if (type == RequestType.Join)
return m_JoinCooldown;
else if (type == RequestType.QuickJoin)
return m_QuickJoinCooldown;
else if (type == RequestType.Host)
return m_CreateCooldown;
return m_QueryCooldown;
}
// Rate Limits are posted here: https://docs.unity.com/lobby/rate-limits.html
ServiceRateLimiter m_QueryCooldown = new ServiceRateLimiter(1, 1f);
ServiceRateLimiter m_CreateCooldown = new ServiceRateLimiter(2, 6f);
ServiceRateLimiter m_JoinCooldown = new ServiceRateLimiter(2, 6f);
ServiceRateLimiter m_QuickJoinCooldown = new ServiceRateLimiter(1, 10f);
ServiceRateLimiter m_GetLobbyCooldown = new ServiceRateLimiter(1, 1f);
ServiceRateLimiter m_DeleteLobbyCooldown = new ServiceRateLimiter(2, 1f);
ServiceRateLimiter m_UpdateLobbyCooldown = new ServiceRateLimiter(5, 5f);
ServiceRateLimiter m_UpdatePlayerCooldown = new ServiceRateLimiter(5, 5f);
ServiceRateLimiter m_LeaveLobbyOrRemovePlayer = new ServiceRateLimiter(5, 1);
ServiceRateLimiter m_HeartBeatCooldown = new ServiceRateLimiter(5, 30);
#endregion
Dictionary<string, PlayerDataObject> CreateInitialPlayerData(LocalPlayer user)
{
Dictionary<string, PlayerDataObject> data = new Dictionary<string, PlayerDataObject>();
var displayNameObject =
new PlayerDataObject(PlayerDataObject.VisibilityOptions.Member, user.DisplayName.Value);
data.Add("DisplayName", displayNameObject);
return data;
}
public async Task<Lobby> CreateLobbyAsync(string lobbyName, int maxPlayers, bool isPrivate,
LocalPlayer localUser)
{
if (m_CreateCooldown.IsCoolingDown)
{
Debug.LogWarning("Create Lobby hit the rate limit.");
return null;
}
await m_CreateCooldown.QueueUntilCooldown();
try
{
string uasId = AuthenticationService.Instance.PlayerId;
CreateLobbyOptions createOptions = new CreateLobbyOptions
{
IsPrivate = isPrivate,
Player = new Player(id: uasId, data: CreateInitialPlayerData(localUser))
};
m_CurrentLobby = await LobbyService.Instance.CreateLobbyAsync(lobbyName, maxPlayers, createOptions);
StartHeartBeat();
return m_CurrentLobby;
}
catch (Exception ex)
{
Debug.LogError($"Lobby Create failed:\n{ex}");
return null;
}
}
public async Task<Lobby> JoinLobbyAsync(string lobbyId, string lobbyCode, LocalPlayer localUser)
{
if (m_JoinCooldown.IsCoolingDown ||
(lobbyId == null && lobbyCode == null))
{
return null;
}
await m_JoinCooldown.QueueUntilCooldown();
string uasId = AuthenticationService.Instance.PlayerId;
var playerData = CreateInitialPlayerData(localUser);
if (!string.IsNullOrEmpty(lobbyId))
{
JoinLobbyByIdOptions joinOptions = new JoinLobbyByIdOptions
{ Player = new Player(id: uasId, data: playerData) };
m_CurrentLobby = await LobbyService.Instance.JoinLobbyByIdAsync(lobbyId, joinOptions);
}
else
{
JoinLobbyByCodeOptions joinOptions = new JoinLobbyByCodeOptions
{ Player = new Player(id: uasId, data: playerData) };
m_CurrentLobby = await LobbyService.Instance.JoinLobbyByCodeAsync(lobbyCode, joinOptions);
}
return m_CurrentLobby;
}
public async Task<Lobby> QuickJoinLobbyAsync(LocalPlayer localUser, LobbyColor limitToColor = LobbyColor.None)
{
//We dont want to queue a quickjoin
if (m_QuickJoinCooldown.IsCoolingDown)
{
UnityEngine.Debug.LogWarning("Quick Join Lobby hit the rate limit.");
return null;
}
await m_QuickJoinCooldown.QueueUntilCooldown();
var filters = LobbyColorToFilters(limitToColor);
string uasId = AuthenticationService.Instance.PlayerId;
var joinRequest = new QuickJoinLobbyOptions
{
Filter = filters,
Player = new Player(id: uasId, data: CreateInitialPlayerData(localUser))
};
return m_CurrentLobby = await LobbyService.Instance.QuickJoinLobbyAsync(joinRequest);
}
public async Task<QueryResponse> RetrieveLobbyListAsync(LobbyColor limitToColor = LobbyColor.None)
{
var filters = LobbyColorToFilters(limitToColor);
if (m_QueryCooldown.TaskQueued)
return null;
await m_QueryCooldown.QueueUntilCooldown();
QueryLobbiesOptions queryOptions = new QueryLobbiesOptions
{
Count = k_maxLobbiesToShow,
Filters = filters
};
return await LobbyService.Instance.QueryLobbiesAsync(queryOptions);
}
public async Task BindLocalLobbyToRemote(string lobbyID, LocalLobby localLobby)
{
m_LobbyEventCallbacks.LobbyChanged += async changes =>
{
if (changes.LobbyDeleted)
{
await LeaveLobbyAsync();
return;
}
//Lobby Fields
if (changes.Name.Changed)
localLobby.LobbyName.Value = changes.Name.Value;
if (changes.HostId.Changed)
localLobby.HostID.Value = changes.HostId.Value;
if (changes.IsPrivate.Changed)
localLobby.Private.Value = changes.IsPrivate.Value;
if (changes.IsLocked.Changed)
localLobby.Locked.Value = changes.IsLocked.Value;
if (changes.AvailableSlots.Changed)
localLobby.AvailableSlots.Value = changes.AvailableSlots.Value;
if (changes.MaxPlayers.Changed)
localLobby.MaxPlayerCount.Value = changes.MaxPlayers.Value;
if (changes.LastUpdated.Changed)
localLobby.LastUpdated.Value = changes.LastUpdated.Value.ToFileTimeUtc();
//Custom Lobby Fields
if (changes.Data.Changed)
LobbyChanged();
if (changes.PlayerJoined.Changed)
PlayersJoined();
if (changes.PlayerLeft.Changed)
PlayersLeft();
if (changes.PlayerData.Changed)
PlayerDataChanged();
void LobbyChanged()
{
foreach (var change in changes.Data.Value)
{
var changedValue = change.Value;
var changedKey = change.Key;
if (changedValue.Removed)
{
RemoveCustomLobbyData(changedKey);
}
if (changedValue.Changed)
{
ParseCustomLobbyData(changedKey, changedValue.Value);
}
}
void ParseCustomLobbyData(string changedKey, DataObject playerDataObject)
{
if (changedKey == key_RelayCode)
localLobby.RelayCode.Value = playerDataObject.Value;
if (changedKey == key_LobbyState)
localLobby.LocalLobbyState.Value = (LobbyState)int.Parse(playerDataObject.Value);
if (changedKey == key_LobbyColor)
localLobby.LocalLobbyColor.Value = (LobbyColor)int.Parse(playerDataObject.Value);
}
void RemoveCustomLobbyData(string changedKey)
{
if (changedKey == key_RelayCode)
localLobby.RelayCode.Value = "";
}
}
void PlayersJoined()
{
foreach (var playerChanges in changes.PlayerJoined.Value)
{
Player joinedPlayer = playerChanges.Player;
var id = joinedPlayer.Id;
var index = playerChanges.PlayerIndex;
var isHost = localLobby.HostID.Value == id;
var newPlayer = new LocalPlayer(id, index, isHost);
foreach (var dataEntry in joinedPlayer.Data)
{
var dataObject = dataEntry.Value;
ParseCustomPlayerData(newPlayer, dataEntry.Key, dataObject.Value);
}
localLobby.AddPlayer(index, newPlayer);
}
}
void PlayersLeft()
{
foreach (var leftPlayerIndex in changes.PlayerLeft.Value)
{
localLobby.RemovePlayer(leftPlayerIndex);
}
}
void PlayerDataChanged()
{
foreach (var lobbyPlayerChanges in changes.PlayerData.Value)
{
var playerIndex = lobbyPlayerChanges.Key;
var localPlayer = localLobby.GetLocalPlayer(playerIndex);
if (localPlayer == null)
continue;
var playerChanges = lobbyPlayerChanges.Value;
if (playerChanges.ConnectionInfoChanged.Changed)
{
var connectionInfo = playerChanges.ConnectionInfoChanged.Value;
Debug.Log(
$"ConnectionInfo for player {playerIndex} changed to {connectionInfo}");
}
if (playerChanges.LastUpdatedChanged.Changed) { }
//There are changes on the Player
if (playerChanges.ChangedData.Changed)
{
foreach (var playerChange in playerChanges.ChangedData.Value)
{
var changedValue = playerChange.Value;
//There are changes on some of the changes in the player list of changes
if (changedValue.Changed)
{
if (changedValue.Removed)
{
Debug.LogWarning("This Sample does not remove Player Values currently.");
continue;
}
var playerDataObject = changedValue.Value;
ParseCustomPlayerData(localPlayer, playerChange.Key, playerDataObject.Value);
}
}
}
}
}
};
m_LobbyEventCallbacks.LobbyEventConnectionStateChanged += lobbyEventConnectionState =>
{
Debug.Log($"Lobby ConnectionState Changed to {lobbyEventConnectionState}");
};
m_LobbyEventCallbacks.KickedFromLobby += () =>
{
Debug.Log("Left Lobby");
Dispose();
};
await LobbyService.Instance.SubscribeToLobbyEventsAsync(lobbyID, m_LobbyEventCallbacks);
}
void ParseCustomPlayerData(LocalPlayer player, string dataKey, string playerDataValue)
{
if (dataKey == key_Emote)
player.Emote.Value = (EmoteType)int.Parse(playerDataValue);
else if (dataKey == key_Userstatus)
player.UserStatus.Value = (PlayerStatus)int.Parse(playerDataValue);
else if (dataKey == key_Displayname)
player.DisplayName.Value = playerDataValue;
}
public async Task<Lobby> GetLobbyAsync(string lobbyId = null)
{
if (!InLobby())
return null;
await m_GetLobbyCooldown.QueueUntilCooldown();
lobbyId ??= m_CurrentLobby.Id;
return m_CurrentLobby = await LobbyService.Instance.GetLobbyAsync(lobbyId);
}
public async Task LeaveLobbyAsync()
{
await m_LeaveLobbyOrRemovePlayer.QueueUntilCooldown();
if (!InLobby())
return;
string playerId = AuthenticationService.Instance.PlayerId;
await LobbyService.Instance.RemovePlayerAsync(m_CurrentLobby.Id, playerId);
m_CurrentLobby = null;
}
public async Task UpdatePlayerDataAsync(Dictionary<string, string> data)
{
if (!InLobby())
return;
string playerId = AuthenticationService.Instance.PlayerId;
Dictionary<string, PlayerDataObject> dataCurr = new Dictionary<string, PlayerDataObject>();
foreach (var dataNew in data)
{
PlayerDataObject dataObj = new PlayerDataObject(visibility: PlayerDataObject.VisibilityOptions.Member,
value: dataNew.Value);
if (dataCurr.ContainsKey(dataNew.Key))
dataCurr[dataNew.Key] = dataObj;
else
dataCurr.Add(dataNew.Key, dataObj);
}
if (m_UpdatePlayerCooldown.TaskQueued)
return;
await m_UpdatePlayerCooldown.QueueUntilCooldown();
UpdatePlayerOptions updateOptions = new UpdatePlayerOptions
{
Data = dataCurr,
AllocationId = null,
ConnectionInfo = null
};
m_CurrentLobby = await LobbyService.Instance.UpdatePlayerAsync(m_CurrentLobby.Id, playerId, updateOptions);
}
public async Task UpdatePlayerRelayInfoAsync(string lobbyID, string allocationId, string connectionInfo)
{
if (!InLobby())
return;
string playerId = AuthenticationService.Instance.PlayerId;
if (m_UpdatePlayerCooldown.TaskQueued)
return;
await m_UpdatePlayerCooldown.QueueUntilCooldown();
UpdatePlayerOptions updateOptions = new UpdatePlayerOptions
{
Data = new Dictionary<string, PlayerDataObject>(),
AllocationId = allocationId,
ConnectionInfo = connectionInfo
};
m_CurrentLobby = await LobbyService.Instance.UpdatePlayerAsync(lobbyID, playerId, updateOptions);
}
public async Task UpdateLobbyDataAsync(Dictionary<string, string> data)
{
if (!InLobby())
return;
Dictionary<string, DataObject> dataCurr = m_CurrentLobby.Data ?? new Dictionary<string, DataObject>();
var shouldLock = false;
foreach (var dataNew in data)
{
// Special case: We want to be able to filter on our color data, so we need to supply an arbitrary index to retrieve later. Uses N# for numerics, instead of S# for strings.
DataObject.IndexOptions index = dataNew.Key == "LocalLobbyColor" ? DataObject.IndexOptions.N1 : 0;
DataObject dataObj = new DataObject(DataObject.VisibilityOptions.Public, dataNew.Value,
index); // Public so that when we request the list of lobbies, we can get info about them for filtering.
if (dataCurr.ContainsKey(dataNew.Key))
dataCurr[dataNew.Key] = dataObj;
else
dataCurr.Add(dataNew.Key, dataObj);
//Special Use: Get the state of the Local lobby so we can lock it from appearing in queries if it's not in the "Lobby" LocalLobbyState
if (dataNew.Key == "LocalLobbyState")
{
Enum.TryParse(dataNew.Value, out LobbyState lobbyState);
shouldLock = lobbyState != LobbyState.Lobby;
}
}
//We can still update the latest data to send to the service, but we will not send multiple UpdateLobbySyncCalls
if (m_UpdateLobbyCooldown.TaskQueued)
return;
await m_UpdateLobbyCooldown.QueueUntilCooldown();
UpdateLobbyOptions updateOptions = new UpdateLobbyOptions { Data = dataCurr, IsLocked = shouldLock };
m_CurrentLobby = await LobbyService.Instance.UpdateLobbyAsync(m_CurrentLobby.Id, updateOptions);
}
public async Task DeleteLobbyAsync()
{
if (!InLobby())
return;
await m_DeleteLobbyCooldown.QueueUntilCooldown();
await LobbyService.Instance.DeleteLobbyAsync(m_CurrentLobby.Id);
}
public void Dispose()
{
m_CurrentLobby = null;
m_LobbyEventCallbacks = new LobbyEventCallbacks();
}
#region HeartBeat
List<QueryFilter> LobbyColorToFilters(LobbyColor limitToColor)
{
List<QueryFilter> filters = new List<QueryFilter>();
if (limitToColor == LobbyColor.Orange)
filters.Add(new QueryFilter(QueryFilter.FieldOptions.N1, ((int)LobbyColor.Orange).ToString(),
QueryFilter.OpOptions.EQ));
else if (limitToColor == LobbyColor.Green)
filters.Add(new QueryFilter(QueryFilter.FieldOptions.N1, ((int)LobbyColor.Green).ToString(),
QueryFilter.OpOptions.EQ));
else if (limitToColor == LobbyColor.Blue)
filters.Add(new QueryFilter(QueryFilter.FieldOptions.N1, ((int)LobbyColor.Blue).ToString(),
QueryFilter.OpOptions.EQ));
return filters;
}
//Since the LobbyManager maintains the "connection" to the lobby, we will continue to heartbeat until host leaves.
async Task SendHeartbeatPingAsync()
{
if (!InLobby())
return;
if (m_HeartBeatCooldown.IsCoolingDown)
return;
await m_HeartBeatCooldown.QueueUntilCooldown();
await LobbyService.Instance.SendHeartbeatPingAsync(m_CurrentLobby.Id);
}
void StartHeartBeat()
{
#pragma warning disable 4014
m_HeartBeatTask = HeartBeatLoop();
#pragma warning restore 4014
}
async Task HeartBeatLoop()
{
while (m_CurrentLobby != null)
{
await SendHeartbeatPingAsync();
await Task.Delay(8000);
}
}
#endregion
}
//Manages the Amount of times you can hit a service call.
//Adds a buffer to account for ping times.
//Will Queue the latest overflow task for when the cooldown ends.
//Created to mimic the way rate limits are implemented Here: https://docs.unity.com/lobby/rate-limits.html
public class ServiceRateLimiter
{
public Action<bool> onCooldownChange;
public readonly int coolDownMS;
public bool TaskQueued { get; private set; } = false;
readonly int m_ServiceCallTimes;
bool m_CoolingDown = false;
int m_TaskCounter;
//(If you're still getting rate limit errors, try increasing the pingBuffer)
public ServiceRateLimiter(int callTimes, float coolDown, int pingBuffer = 100)
{
m_ServiceCallTimes = callTimes;
m_TaskCounter = m_ServiceCallTimes;
coolDownMS =
Mathf.CeilToInt(coolDown * 1000) +
pingBuffer;
}
public async Task QueueUntilCooldown()
{
if (!m_CoolingDown)
{
#pragma warning disable 4014
ParallelCooldownAsync();
#pragma warning restore 4014
}
m_TaskCounter--;
if (m_TaskCounter > 0)
{
return;
}
if (!TaskQueued)
TaskQueued = true;
else
return;
while (m_CoolingDown)
{
await Task.Delay(10);
}
}
async Task ParallelCooldownAsync()
{
IsCoolingDown = true;
await Task.Delay(coolDownMS);
IsCoolingDown = false;
TaskQueued = false;
m_TaskCounter = m_ServiceCallTimes;
}
public bool IsCoolingDown
{
get => m_CoolingDown;
private set
{
if (m_CoolingDown != value)
{
m_CoolingDown = value;
onCooldownChange?.Invoke(m_CoolingDown);
}
}
}
}
}

179
Assets/Scripts/GameLobby/Lobby/LobbySynchronizer.cs


using System;
using System.Threading.Tasks;
using LobbyRelaySample.lobby;
using Unity.Services.Lobbies.Models;
using UnityEngine;
// namespace LobbyRelaySample
// {
// /// <summary>
// /// Keep updated on changes to a joined lobby, at a speed compliant with Lobby's rate limiting.
// /// </summary>
// public class LobbySynchronizer : IDisposable
// {
// LocalLobby m_LocalLobby;
// LocalPlayer m_LocalUser;
// LobbyManager m_LobbyManager;
// bool m_LocalChanges = false;
//
// const int k_approvalMaxMS = 10000; // Used for determining if a user should timeout if they are unable to connect.
//
// int m_lifetime = 0;
// const int k_UpdateIntervalMS = 1000;
//
// public LobbySynchronizer(LobbyManager lobbyManager)
// {
// m_LobbyManager = lobbyManager;
// }
//
// public void StartSynch(LocalLobby localLobby, LocalPlayer localUser)
// {
// m_LocalUser = localUser;
// m_LocalLobby = localLobby;
// m_LocalLobby.LobbyID.onChanged += OnLobbyIdChanged;
// m_LocalChanges = true;
// #pragma warning disable 4014
// UpdateLoopAsync();
// #pragma warning restore 4014
// m_lifetime = 0;
// }
//
//
// public void EndSynch()
// {
// m_LocalChanges = false;
//
// if (m_LocalLobby != null)
// m_LocalLobby.LobbyID.onChanged -= OnLobbyIdChanged;
//
// m_LocalLobby = null;
// }
//
// /// <summary>
// /// If there have been any data changes since the last update, push them to Lobby. Regardless, pull for the most recent data.
// /// (Unless we're already awaiting a query, in which case continue waiting.)
// /// </summary>
// async Task UpdateLoopAsync()
// {
// Lobby latestLobby = null;
//
// while (m_LocalLobby != null)
// {
// latestLobby = await GetLatestRemoteLobby();
//
// if (IfRemoteLobbyChanged(latestLobby))
// {
// //Pulling remote changes, and applying them to the local lobby usually flags it as changed,
// //Causing another pull, the RemoteToLocal converter ensures this does not happen by flagging the lobby.
// LobbyConverters.RemoteToLocal(latestLobby, m_LocalLobby, false);
// }
// Debug.Log(m_LocalLobby.ToString());
//
// if (!LobbyHasHost())
// {
// LeaveLobbyBecauseNoHost();
// break;
// }
//
// var areAllusersReady = AreAllUsersReady();
// if (areAllusersReady && m_LocalLobby.LocalLobbyState.Value == LobbyState.Lobby)
// {
// GameManager.Instance.BeginCountDown();
// }
// else if (!areAllusersReady && m_LocalLobby.LocalLobbyState.Value == LobbyState.CountDown)
// {
// GameManager.Instance.CancelCountDown();
// }
//
// m_lifetime += k_UpdateIntervalMS;
// await Task.Delay(k_UpdateIntervalMS);
// }
// }
//
// async Task<Lobby> GetLatestRemoteLobby()
// {
// Lobby latestLobby = null;
// if (m_LocalLobby.IsLobbyChanged())
// {
// latestLobby = await PushDataToLobby();
// }
// else
// {
// latestLobby = await m_LobbyManager.GetLobbyAsync();
// }
//
// return latestLobby;
// }
//
// bool IfRemoteLobbyChanged(Lobby remoteLobby)
// {
// var remoteLobbyTime = remoteLobby.LastUpdated.ToFileTimeUtc();
// var localLobbyTime = m_LocalLobby.LastUpdated.Value;
// var isLocalOutOfDate = remoteLobbyTime > localLobbyTime;
// return isLocalOutOfDate;
// }
//
// async Task<Lobby> PushDataToLobby()
// {
// m_LocalChanges = false;
//
// if (m_LocalUser.IsHost.Value)
// await m_LobbyManager.UpdateLobbyDataAsync(
// LobbyConverters.LocalToRemoteLobbyData(m_LocalLobby));
//
// return await m_LobbyManager.UpdatePlayerDataAsync(
// LobbyConverters.LocalToRemoteUserData(m_LocalUser));
// }
//
// bool AreAllUsersReady()
// {
// foreach (var lobbyUser in m_LocalLobby.LocalPlayers.Values)
// {
// if (lobbyUser.PlayerStatus.Value != PlayerStatus.Ready)
// {
// return false;
// }
// }
//
// return true;
// }
//
// bool LobbyHasHost()
// {
// if (!m_LocalUser.IsHost.Value)
// {
// foreach (var lobbyUser in m_LocalLobby.LocalPlayers)
// {
// if (lobbyUser.Value.IsHost.Value)
// return true;
// }
//
// return false;
// }
//
// return true;
// }
//
// void LeaveLobbyBecauseNoHost()
// {
// LogHandlerSettings.Instance.SpawnErrorPopup(
// "Host left the lobby! Disconnecting...");
// Locator.Get.Messenger.OnReceiveMessage(MessageType.EndGame, null);
// GameManager.Instance.UIChangeMenuState(GameState.JoinMenu);
// }
//
// public void OnLobbyIdChanged(string lobbyID)
// {
// if (string.IsNullOrEmpty(lobbyID)
// ) // When the player leaves, their LocalLobby is cleared out.
// {
// EndSynch();
// }
// }
//
// public void Dispose()
// {
// EndSynch();
// }
// }
// }

27
Assets/Scripts/GameLobby/Tests/PlayMode/AsyncTestHelper.cs


using System;
using System.Collections;
using System.Threading.Tasks;
namespace Test.Tools
{
public class AsyncTestHelper
{
public static IEnumerator Await(Task task)
{
while (!task.IsCompleted)
{
yield return null;
}
if (task.IsFaulted)
{
throw task.Exception;
}
}
public static IEnumerator Await(Func<Task> taskDelegate)
{
return Await(taskDelegate.Invoke());
}
}
}

11
Assets/Scripts/GameLobby/Tests/PlayMode/AsyncTestHelper.cs.meta


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

71
Assets/Scripts/GameLobby/UI/ColorLobbyUI.cs


using UnityEngine;
using UnityEngine.Serialization;
using UnityEngine.UI;
namespace LobbyRelaySample.UI
{
/// <summary>
/// We want to illustrate filtering the lobby list by some arbitrary variable. This will allow the lobby host to choose a color for the lobby, and will display a lobby's current color.
/// (Note that this isn't sent over Relay to other clients for realtime updates.)
/// </summary>
public class ColorLobbyUI : MonoBehaviour
{
public bool m_UseLocalLobby;
static readonly Color s_orangeColor = new Color(0.83f, 0.36f, 0);
static readonly Color s_greenColor = new Color(0, 0.61f, 0.45f);
static readonly Color s_blueColor = new Color(0.0f, 0.44f, 0.69f);
static readonly Color[] s_colorsOrdered = new Color[]
{ new Color(0.9f, 0.9f, 0.9f, 0.7f), s_orangeColor, s_greenColor, s_blueColor };
[SerializeField]
Graphic[] m_toRecolor;
LocalLobby m_lobby;
void Start()
{
if (m_UseLocalLobby)
SetLobby(GameManager.Instance.LocalLobby);
}
public void SetLobby(LocalLobby lobby)
{
ChangeColors(lobby.LocalLobbyColor.Value);
lobby.LocalLobbyColor.onChanged += ChangeColors;
}
public void ToggleWhite(bool toggle)
{
if (!toggle)
return;
GameManager.Instance.SetLocalLobbyColor(0);
}
public void ToggleOrange(bool toggle)
{
if (!toggle)
return;
GameManager.Instance.SetLocalLobbyColor(1);
}
public void ToggleGreen(bool toggle)
{
if (!toggle)
return;
GameManager.Instance.SetLocalLobbyColor(2);
}
public void ToggleBlue(bool toggle)
{
if (!toggle)
return;
GameManager.Instance.SetLocalLobbyColor(3);
}
void ChangeColors(LobbyColor lobbyColor)
{
Color color = s_colorsOrdered[(int)lobbyColor];
foreach (Graphic graphic in m_toRecolor)
graphic.color = new Color(color.r, color.g, color.b, graphic.color.a);
}
}
}

27
Assets/UniversalRenderPipelineGlobalSettings.asset


%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 2ec995e51a6e251468d2a3fd8a686257, type: 3}
m_Name: UniversalRenderPipelineGlobalSettings
m_EditorClassIdentifier:
k_AssetVersion: 2
lightLayerName0: Light Layer default
lightLayerName1: Light Layer 1
lightLayerName2: Light Layer 2
lightLayerName3: Light Layer 3
lightLayerName4: Light Layer 4
lightLayerName5: Light Layer 5
lightLayerName6: Light Layer 6
lightLayerName7: Light Layer 7
m_StripDebugVariants: 1
m_StripUnusedPostProcessingVariants: 0
m_StripUnusedVariants: 1
supportRuntimeDebugDisplay: 0

8
Assets/UniversalRenderPipelineGlobalSettings.asset.meta


fileFormatVersion: 2
guid: c96e53b1d712ac24eaccfd7f849c709d
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

3
Packages/ParrelSync.meta


fileFormatVersion: 2
guid: 520a1688a3c84604b0e7836ecc15cee3
timeCreated: 1654698051

部分文件因为文件数量过多而无法显示

正在加载...
取消
保存