/// <summary> /// Join a room found by the given query. The matchmaker's state must be <see cref="ConnectionState.Connected"/>. /// </summary> /// Note that Photon will be connected to any found room instantly. /// See <see cref="LoadBalancingClient.OpJoinRoom"/> for more information. /// /// <inheritdoc cref="IMatchmaker.FindGame"/> /// <param name="options">The <see cref="PhotonGameQuery"/> used to find a game</param> /// <returns>An observable resolving with the <see cref="PhotonAlreadyJoinedGame"/> that was joined with the given query.</returns> /// <exception cref="ArgumentException">If <see cref="query"/> is not of type <see cref="PhotonGameQuery"/></exception> public virtual IObservable <IGame> FindGame(IGameQuery query) { var photonOptions = query as PhotonGameQuery; if (photonOptions == null) { throw new ArgumentException($"{nameof(query)} must be of type {nameof(PhotonGameQuery)}.", nameof(query)); } if (string.IsNullOrEmpty(photonOptions.RoomName)) { throw new ArgumentException($"{nameof(photonOptions.RoomName)} must not be null or empty.", nameof(query)); } if (_photonClient.State != ClientState.ConnectedToMasterserver && _photonClient.State != ClientState.JoinedLobby) { return(Observable.Throw <IGame>(new InvalidOperationException($"Operation failed: Invalid state '{_photonClient.State}'." + " Please make sure you are connected to Photon."))); } var observable = PhotonUtils.CreateObservableForExpectedStateChange <IGame>(_photonClient, expectedState: ClientState.Joined, returnValue: new PhotonAlreadyJoinedGame(_photonClient)); State = ConnectionState.JoiningRoom; _photonClient.OpJoinRoom(new EnterRoomParams { RoomName = photonOptions.RoomName, ExpectedUsers = photonOptions.ExpectedPlayers }); return(observable); }