// Callback for "Get Ticket" UnityWebRequestAsyncOperation completion void OnGetTicketAsyncCompleted(AsyncOperation obj) { if (!(obj is UnityWebRequestAsyncOperation getTicketOp)) { throw new ArgumentException(logPre + "Wrong AsyncOperation type in callback."); } Protobuf.GetTicketResponse getResponse; // Every return statement in here will trigger finally{} cleanup try { // Short-circuit if we're in a terminal state or have no Get call registered if (IsDone || m_GetTicketAsyncOperation == null) { return; } if (getTicketOp != m_GetTicketAsyncOperation) { throw new InvalidOperationException(logPre + $"Wrong operation object received by {nameof(OnGetTicketAsyncCompleted)}."); } // No need to log on an expected abort state if (State == MatchmakingRequestState.Canceling || MatchmakingClient.IsWebRequestAborted(getTicketOp.webRequest)) { return; } // This helps to ensure that GET calls aborted during a delete are not processed if (State != MatchmakingRequestState.Polling) { Debug.LogWarning(logPre + "Ignoring Matchmaking Get Ticket response while not in polling state."); return; } if (MatchmakingClient.IsWebRequestFailed(getTicketOp.webRequest)) { HandleError($"Error getting matchmaking ticket: {getTicketOp.webRequest.error}"); return; } // Parse the body of the response - only try if we actually have a body // Successful responses w/o bodies are not considered failures if (getTicketOp.webRequest?.downloadHandler?.data?.Length == 0) { return; } // Try to parse body if (!MatchmakingClient.TryParseGetTicketResponse(getTicketOp.webRequest, out getResponse)) { // Body was present but we couldn't parse it; this is probably a fatal error HandleError($"Error parsing GET response for ticket"); return; } } catch (Exception e) { HandleError($"Error getting information for ticket: {e.Message}"); return; } finally { // Allow the operation to get garbage collected if (getTicketOp == m_GetTicketAsyncOperation) { m_GetTicketAsyncOperation = null; m_LastPollTime = Time.unscaledTime; } getTicketOp.webRequest?.Dispose(); } // Consume the response if (getResponse.Status != null) { HandleError($"Error getting matchmaking ticket. Code {getResponse.Status.Code}: {getResponse.Status.Message}"); return; } // Check to see if this GET call has an assignment // If assignment is null, ticket hasn't completed matchmaking yet if (getResponse.Assignment != null) { var errorExists = !string.IsNullOrEmpty(getResponse.Assignment.Error); var connectionExists = !string.IsNullOrEmpty(getResponse.Assignment.Connection); var propertiesExists = !string.IsNullOrEmpty(getResponse.Assignment.Properties); // Note that assignment can have null fields Assignment = new Assignment(getResponse.Assignment.Connection, getResponse.Assignment.Error, getResponse.Assignment.Properties); // Set to ErrorRequest state if assignment has no real data if (!errorExists && !connectionExists && !propertiesExists) { HandleError("Error getting matchmaking ticket: assignment returned by service could not be processed"); return; } // Set to ErrorAssignment state if parsed assignment object contains an error entry if (errorExists) { State = MatchmakingRequestState.ErrorAssignment; Debug.LogError(logPre + $"Matchmaking completed with Assignment error: {Assignment.Error}"); SetTerminalState(); return; } // No error and valid connection and/or properties - set to AssignmentReceived State = MatchmakingRequestState.AssignmentReceived; Debug.Log(logPre + $"Matchmaking completed successfully; connection information received."); SetTerminalState(); } }
// Callback for "Create Ticket" UnityWebRequestAsyncOperation completion void OnCreateTicketAsyncCompleted(AsyncOperation obj) { if (!(obj is UnityWebRequestAsyncOperation createTicketOp)) { throw new ArgumentException(logPre + "Wrong AsyncOperation type in callback."); } Protobuf.CreateTicketResponse result; // Every return statement in here will trigger finally{} cleanup try { // Short-circuit if we're in a terminal state or have no creation registered if (IsDone || m_CreateTicketAsyncOperation == null) { return; } if (createTicketOp != m_CreateTicketAsyncOperation) { throw new InvalidOperationException(logPre + $"Wrong operation object received by {nameof(OnCreateTicketAsyncCompleted)}."); } if (State != MatchmakingRequestState.Creating && State != MatchmakingRequestState.Canceling) { Debug.LogWarning(logPre + "Ignoring Matchmaking Create Ticket response while not in creating state."); return; } if (MatchmakingClient.IsWebRequestFailed(createTicketOp.webRequest)) { // If we tried to cancel while the Create call was being constructed or in flight, // the Create request will be set to an aborted state (counts as an "IsFailed" state) if (State == MatchmakingRequestState.Canceling && MatchmakingClient.IsWebRequestAborted(createTicketOp.webRequest)) { //if (MatchmakingClient.IsWebRequestSent(createTicketOp)) //Debug.LogWarning(logPre + "Matchmaking call was aborted, but ticket may have been created on the service end."); Debug.Log(logPre + "Matchmaking was aborted during ticket creation"); HandleDelete(); return; } HandleError($"Error creating matchmaking ticket: {createTicketOp.webRequest.error}"); return; } // Parse the body of the response - only try if we actually have a body if (!MatchmakingClient.TryParseCreateTicketResponse(createTicketOp.webRequest, out result)) { // Could not parse the CREATE response; this is a fatal error HandleError($"Error parsing CREATE response for ticket; could not get Ticket ID from service"); } } catch (Exception e) { HandleError($"Error creating matchmaking ticket: {e.Message}"); return; } finally { // Allow the operation to get garbage collected if (createTicketOp == m_CreateTicketAsyncOperation) { m_CreateTicketAsyncOperation = null; } createTicketOp.webRequest?.Dispose(); } // Try to consume the parsed response if (result == null) { HandleError("Error creating matchmaking ticket."); return; } if (result.Status != null) { HandleError($"Error creating matchmaking ticket. Code {result.Status.Code}: {result.Status.Message}"); return; } if (string.IsNullOrEmpty(result.Id)) { HandleError("Error creating matchmaking ticket. Id not set."); return; } // We were able to parse the Ticket Id TicketId = result.Id; // If a cancellation request is queued, send the cancellation instead of polling for assignment if (State == MatchmakingRequestState.Canceling) { DeleteMatchRequestTicket(); return; } // Start polling for the assignment for the assigned Ticket Id Debug.Log(logPre + $"Ticket ID received; polling matchmaking for assignment"); StartGetTicketRequest(TicketId); }