Newer
Older
CGTrack / Assets / Oculus / Platform / Samples / VrVoiceChat / Scripts / RoomManager.cs
namespace Oculus.Platform.Samples.VrVoiceChat
{
	using UnityEngine;
	using System;
	using System.Collections.Generic;
	using Oculus.Platform;
	using Oculus.Platform.Models;

	// Helper class to manage Room creation, membership and invites.
	// Rooms are a mechanism to help Oculus users create a shared experience.
	// Users can only be in one Room at a time.  If the Owner of a room
	// leaves, then ownership is transferred to some other member.
	// Here we use rooms to create the notion of a 'call' to help us
	// invite a Friend and establish a VOIP and P2P connection.
	public class RoomManager
	{
		// the ID of the Room that I'm in
		private ulong m_roomID;

		// the other User in the Room
		private User m_remoteUser;

		// how often I should poll for invites
		private static readonly float INVITE_POLL_FREQ_SECONDS = 5.0f;

		// the next time I should poll Oculus Platform for valid Room Invite requests
		private float m_nextPollTime;

		public struct Invite
		{
			public readonly ulong RoomID;
			public readonly string OwnerID;

			public Invite(ulong roomID, string owner)
			{
				this.RoomID = roomID;
				this.OwnerID = owner;
			}
		}

		// cached list of rooms that I've been invited to and I'm waiting
		// for more information about
		private HashSet<ulong> m_pendingRoomRequests;

		// accumulation list of room invites and the room owner
		private List<Invite> m_invites;

		public RoomManager()
		{
			Rooms.SetRoomInviteAcceptedNotificationCallback(LaunchedFromAcceptingInviteCallback);
			Rooms.SetUpdateNotificationCallback(RoomUpdateCallback);
		}

		public ulong RemoteUserID
		{
			get
			{
				return m_remoteUser != null ? m_remoteUser.ID : 0;
			}
		}

		public String RemoteOculusID
		{
			get
			{
				return m_remoteUser != null ? m_remoteUser.OculusID : String.Empty;
			}
		}

		#region Launched Application from Accepting Invite

		// Callback to check whether the User accepted the invite as
		// a notification which caused the Application to launch.  If so, then
		// we know we need to try to join that room.
		void LaunchedFromAcceptingInviteCallback(Message<string> msg)
		{
			if (msg.IsError)
			{
				PlatformManager.TerminateWithError(msg);
				return;
			}

			Debug.Log("Launched Invite to join Room: " + msg.Data);

			m_roomID = Convert.ToUInt64(msg.GetString());
		}

		// Check to see if the App was launched by accepting the Notication from the main Oculus app.
		// If so, we can directly join that room.  (If it's still available.)
		public bool CheckForLaunchInvite()
		{
			if (m_roomID != 0)
			{
				JoinExistingRoom(m_roomID);
				return true;
			}
			else
			{
				return false;
			}
		}

		#endregion

		#region Create a Room and Invite Friend(s) from the Oculus Universal Menu

		public void CreateRoomAndLaunchInviteMenu()
		{
			Rooms.CreateAndJoinPrivate(RoomJoinPolicy.InvitedUsers, 2, true)
				 .OnComplete(CreateAndJoinPrivateRoomCallback);
		}

		void CreateAndJoinPrivateRoomCallback(Message<Room> msg)
		{
			if (msg.IsError)
			{
				PlatformManager.TerminateWithError(msg);
				return;
			}

			m_roomID = msg.Data.ID;
			m_remoteUser = null;
			PlatformManager.TransitionToState(PlatformManager.State.WAITING_FOR_ANSWER);

			// launch the Room Invite workflow in the Oculus Univeral Menu
			Rooms.LaunchInvitableUserFlow(m_roomID).OnComplete(OnLaunchInviteWorkflowComplete);
		}

		void OnLaunchInviteWorkflowComplete(Message msg)
		{
			if (msg.IsError)
			{
				PlatformManager.TerminateWithError(msg);
				return;
			}
		}

		#endregion

		#region Polling for Invites

		public bool ShouldPollInviteList
		{
			get
			{
				return m_pendingRoomRequests == null && Time.time >= m_nextPollTime;
			}
		}

		public void UpdateActiveInvitesList()
		{
			m_nextPollTime = Time.time + INVITE_POLL_FREQ_SECONDS;
			m_pendingRoomRequests = new HashSet<ulong>();
			m_invites = new List<Invite>();
			Notifications.GetRoomInviteNotifications().OnComplete(GetRoomInviteNotificationsCallback);
		}

		// task 13572454: add the type to callback definition
		void GetRoomInviteNotificationsCallback(Message msg_untyped)
		{
			Message<RoomInviteNotificationList> msg = (Message<RoomInviteNotificationList>)msg_untyped;

			if (msg.IsError)
			{
				PlatformManager.TerminateWithError(msg);
				return;
			}

			// loop over all the rooms we're invited to and request more info
			foreach (RoomInviteNotification invite in msg.Data)
			{
				m_pendingRoomRequests.Add(invite.RoomID);
				Rooms.Get(invite.RoomID).OnComplete(GetRoomInfoCallback);
			}

			if (msg.Data.Count == 0)
			{
				m_pendingRoomRequests = null;
				PlatformManager.SetActiveInvites(m_invites);
			}
		}

		void GetRoomInfoCallback(Message<Room> msg)
		{
			if (msg.IsError)
			{
				PlatformManager.TerminateWithError(msg);
				return;
			}

			if (msg.Data.OwnerOptional != null)
			{
				Invite invite = new Invite(msg.Data.ID, msg.Data.OwnerOptional.OculusID);
				m_pendingRoomRequests.Remove(invite.RoomID);

				// make sure the room still looks usable
				// (e.g. they aren't currently talking to someone)
				if (msg.Data.UsersOptional != null && msg.Data.UsersOptional.Count == 1)
				{
					m_invites.Add(invite);
				}
			}

			// once we're received all the room info, let the platform update
			// its display
			if (m_pendingRoomRequests.Count == 0)
			{
				m_pendingRoomRequests = null;
				PlatformManager.SetActiveInvites(m_invites);
			}
		}

		#endregion

		#region Accept Invite

		public void JoinExistingRoom(ulong roomID)
		{
			Rooms.Join(roomID, true).OnComplete(JoinRoomCallback);
		}

		void JoinRoomCallback(Message<Room> msg)
		{
			if (msg.IsError)
			{
				// is reasonable if caller called more than 1 person, and I didn't answer first
				return;
			}

			string oculusOwnerID = msg.Data.OwnerOptional != null ? msg.Data.OwnerOptional.OculusID : "";
			int numUsers = msg.Data.UsersOptional != null ? msg.Data.UsersOptional.Count : 0;

			Debug.LogFormat("Joined room: {0} owner: {1} count: ",
				msg.Data.ID, oculusOwnerID, numUsers);

			m_roomID = msg.Data.ID;

			// if the caller left while I was in the process of joining, just hangup
			if (msg.Data.UsersOptional == null || msg.Data.UsersOptional.Count != 2)
			{
				PlatformManager.TransitionToState(PlatformManager.State.HANGUP);
			}
			else
			{
				foreach (User user in msg.Data.UsersOptional)
				{
					if (user.ID != PlatformManager.MyID)
					{
						m_remoteUser = user;
					}
				}

				PlatformManager.TransitionToState(PlatformManager.State.CONNECTED_IN_A_ROOM);
			}

			// update the invite list sooner
			m_nextPollTime = Time.time;
		}

		#endregion

		#region Room Updates

		void RoomUpdateCallback(Message<Room> msg)
		{
			if (msg.IsError)
			{
				PlatformManager.TerminateWithError(msg);
				return;
			}

			string oculusOwnerID = msg.Data.OwnerOptional != null ? msg.Data.OwnerOptional.OculusID : "";
			int numUsers = msg.Data.UsersOptional != null ? msg.Data.UsersOptional.Count : 0;

			Debug.LogFormat("Room {0} Update: {1} owner: {2} count: ",
				msg.Data.ID, oculusOwnerID, numUsers);

			// if the Room count is not 2 then the other party has left.
			// We'll just hangup the connection here.
			// If the other User created then room, ownership would switch to me.
			if (msg.Data.UsersOptional == null || msg.Data.UsersOptional.Count != 2)
			{
				PlatformManager.TransitionToState(PlatformManager.State.HANGUP);
			}
			else
			{
				foreach (User user in msg.Data.UsersOptional)
				{
					if (user.ID != PlatformManager.MyID)
					{
						m_remoteUser = user;
					}
				}

				PlatformManager.TransitionToState(PlatformManager.State.CONNECTED_IN_A_ROOM);
			}
		}

		#endregion

		#region Room Exit

		public void LeaveCurrentRoom()
		{
			if (m_roomID != 0)
			{
				Rooms.Leave(m_roomID);
				m_roomID = 0;
				m_remoteUser = null;
			}
			PlatformManager.TransitionToState(PlatformManager.State.WAITING_TO_CALL_OR_ANSWER);
		}

		#endregion
	}
}