浏览代码

Changing the error pop-up to handle the LobbyServiceException with more player-facing error output. The same work for Relay will follow if the package is in a usable state.

/main/staging/error_codes_to_exceptions
nathaniel.buck@unity3d.com 3 年前
当前提交
9cc7b9d3
共有 14 个文件被更改,包括 302 次插入222 次删除
  1. 118
      Assets/Prefabs/UI/GameCanvas.prefab
  2. 71
      Assets/Prefabs/UI/PopUpUI.prefab
  3. 82
      Assets/Scenes/mainScene.unity
  4. 20
      Assets/Scripts/Infrastructure/AsyncRequest.cs
  5. 42
      Assets/Scripts/Infrastructure/LogHandler.cs
  6. 32
      Assets/Scripts/Infrastructure/LogHandlerSettings.cs
  7. 1
      Assets/Scripts/Infrastructure/Messenger.cs
  8. 20
      Assets/Scripts/Lobby/LobbyAPIInterface.cs
  9. 7
      Assets/Scripts/Relay/RelayAPIInterface.cs
  10. 55
      Assets/Scripts/UI/PopUpUI.cs
  11. 31
      Assets/Scripts/Lobby/AsyncRequestLobby.cs
  12. 11
      Assets/Scripts/Lobby/AsyncRequestLobby.cs.meta
  13. 23
      Assets/Scripts/Relay/AsyncRequestRelay.cs
  14. 11
      Assets/Scripts/Relay/AsyncRequestRelay.cs.meta

118
Assets/Prefabs/UI/GameCanvas.prefab


- {fileID: 2637199316546086228}
- {fileID: 5992334104032192704}
- {fileID: 6907879089325579755}
- {fileID: 7914629187891855367}
m_Father: {fileID: 2637199315671523625}
m_RootOrder: 3
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}

- target: {fileID: 1547097154637997990, guid: 65b3782fa4600fa4385f4eea3edd9865, type: 3}
propertyPath: m_OnClick.m_PersistentCalls.m_Calls.Array.data[1].m_Target
value:
objectReference: {fileID: 4928485776352161055}
objectReference: {fileID: 0}
- target: {fileID: 1547097154637997990, guid: 65b3782fa4600fa4385f4eea3edd9865, type: 3}
propertyPath: m_OnClick.m_PersistentCalls.m_Calls.Array.data[1].m_CallState
value: 2

m_CorrespondingSourceObject: {fileID: 2244251207921394025, guid: 247f79ab5aefc6d40bcbdade4d9467b7, type: 3}
m_PrefabInstance: {fileID: 4304342123904612290}
m_PrefabAsset: {fileID: 0}
--- !u!1001 &4940523059930614354
PrefabInstance:
m_ObjectHideFlags: 0
serializedVersion: 2
m_Modification:
m_TransformParent: {fileID: 2637199316291327119}
m_Modifications:
- target: {fileID: 2974111728825125461, guid: 79d6084439b78bb4eaf5232cb953fd87, type: 3}
propertyPath: m_Pivot.x
value: 0.5
objectReference: {fileID: 0}
- target: {fileID: 2974111728825125461, guid: 79d6084439b78bb4eaf5232cb953fd87, type: 3}
propertyPath: m_Pivot.y
value: 0.5
objectReference: {fileID: 0}
- target: {fileID: 2974111728825125461, guid: 79d6084439b78bb4eaf5232cb953fd87, type: 3}
propertyPath: m_RootOrder
value: 3
objectReference: {fileID: 0}
- target: {fileID: 2974111728825125461, guid: 79d6084439b78bb4eaf5232cb953fd87, type: 3}
propertyPath: m_AnchorMax.x
value: 1
objectReference: {fileID: 0}
- target: {fileID: 2974111728825125461, guid: 79d6084439b78bb4eaf5232cb953fd87, type: 3}
propertyPath: m_AnchorMax.y
value: 1
objectReference: {fileID: 0}
- target: {fileID: 2974111728825125461, guid: 79d6084439b78bb4eaf5232cb953fd87, type: 3}
propertyPath: m_AnchorMin.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 2974111728825125461, guid: 79d6084439b78bb4eaf5232cb953fd87, type: 3}
propertyPath: m_AnchorMin.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 2974111728825125461, guid: 79d6084439b78bb4eaf5232cb953fd87, type: 3}
propertyPath: m_SizeDelta.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 2974111728825125461, guid: 79d6084439b78bb4eaf5232cb953fd87, type: 3}
propertyPath: m_SizeDelta.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 2974111728825125461, guid: 79d6084439b78bb4eaf5232cb953fd87, type: 3}
propertyPath: m_LocalPosition.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 2974111728825125461, guid: 79d6084439b78bb4eaf5232cb953fd87, type: 3}
propertyPath: m_LocalPosition.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 2974111728825125461, guid: 79d6084439b78bb4eaf5232cb953fd87, type: 3}
propertyPath: m_LocalPosition.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 2974111728825125461, guid: 79d6084439b78bb4eaf5232cb953fd87, type: 3}
propertyPath: m_LocalRotation.w
value: 1
objectReference: {fileID: 0}
- target: {fileID: 2974111728825125461, guid: 79d6084439b78bb4eaf5232cb953fd87, type: 3}
propertyPath: m_LocalRotation.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 2974111728825125461, guid: 79d6084439b78bb4eaf5232cb953fd87, type: 3}
propertyPath: m_LocalRotation.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 2974111728825125461, guid: 79d6084439b78bb4eaf5232cb953fd87, type: 3}
propertyPath: m_LocalRotation.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 2974111728825125461, guid: 79d6084439b78bb4eaf5232cb953fd87, type: 3}
propertyPath: m_AnchoredPosition.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 2974111728825125461, guid: 79d6084439b78bb4eaf5232cb953fd87, type: 3}
propertyPath: m_AnchoredPosition.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 2974111728825125461, guid: 79d6084439b78bb4eaf5232cb953fd87, type: 3}
propertyPath: m_LocalEulerAnglesHint.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 2974111728825125461, guid: 79d6084439b78bb4eaf5232cb953fd87, type: 3}
propertyPath: m_LocalEulerAnglesHint.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 2974111728825125461, guid: 79d6084439b78bb4eaf5232cb953fd87, type: 3}
propertyPath: m_LocalEulerAnglesHint.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 2974111728825125462, guid: 79d6084439b78bb4eaf5232cb953fd87, type: 3}
propertyPath: m_Name
value: PopUpUI
objectReference: {fileID: 0}
m_RemovedComponents: []
m_SourcePrefab: {fileID: 100100000, guid: 79d6084439b78bb4eaf5232cb953fd87, type: 3}
--- !u!224 &7914629187891855367 stripped
RectTransform:
m_CorrespondingSourceObject: {fileID: 2974111728825125461, guid: 79d6084439b78bb4eaf5232cb953fd87, type: 3}
m_PrefabInstance: {fileID: 4940523059930614354}
m_PrefabAsset: {fileID: 0}
--- !u!1001 &8387645425390731067
PrefabInstance:
m_ObjectHideFlags: 0

objectReference: {fileID: 0}
- target: {fileID: 3638764283281205412, guid: 404728f5cffe43940b290121bd31f601, type: 3}
propertyPath: m_Value
value: 0
value: 1
objectReference: {fileID: 0}
- target: {fileID: 3675426980811071808, guid: 404728f5cffe43940b290121bd31f601, type: 3}
propertyPath: m_AnchorMax.x

m_CorrespondingSourceObject: {fileID: 5836614391142406767, guid: 404728f5cffe43940b290121bd31f601, type: 3}
m_PrefabInstance: {fileID: 8387645425390731067}
m_PrefabAsset: {fileID: 0}
--- !u!114 &4928485776352161055 stripped
MonoBehaviour:
m_CorrespondingSourceObject: {fileID: 3459776754637410852, guid: 404728f5cffe43940b290121bd31f601, type: 3}
m_PrefabInstance: {fileID: 8387645425390731067}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 48ec34a3875818e4690f1bf0be69ccd9, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!1001 &8690587081894129692
PrefabInstance:
m_ObjectHideFlags: 0

71
Assets/Prefabs/UI/PopUpUI.prefab


- component: {fileID: 2974111728439300793}
- component: {fileID: 2974111728439300794}
- component: {fileID: 2974111728439300795}
- component: {fileID: 3559536739324575631}
m_Layer: 5
m_Name: ErrorExitButton
m_TagString: Untagged

m_Calls:
- m_Target: {fileID: 2974111728825125460}
m_TargetAssemblyTypeName: LobbyRelaySample.PopUpUI, LobbyRelaySample
m_MethodName: Delete
m_MethodName: ClearPopup
m_Mode: 1
m_Arguments:
m_ObjectArgument: {fileID: 0}

m_StringArgument:
m_BoolArgument: 0
m_CallState: 2
--- !u!225 &3559536739324575631
CanvasGroup:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2974111728439300773}
m_Enabled: 1
m_Alpha: 1
m_Interactable: 1
m_BlocksRaycasts: 1
m_IgnoreParentGroups: 0
--- !u!1 &2974111728825125462
GameObject:
m_ObjectHideFlags: 0

- component: {fileID: 2974111728825125461}
- component: {fileID: 2974111728825125417}
- component: {fileID: 2974111728825125418}
- component: {fileID: 2974111728825125419}
- component: {fileID: 1969158100312030839}
- component: {fileID: 682510267401884749}
m_Layer: 5
m_Name: PopUpUI
m_TagString: Untagged

m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 0}
m_Color: {r: 0, g: 0, b: 0, a: 0.2}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1

m_FillOrigin: 0
m_UseSpriteMesh: 0
m_PixelsPerUnitMultiplier: 1
--- !u!225 &2974111728825125419
CanvasGroup:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2974111728825125462}
m_Enabled: 1
m_Alpha: 1
m_Interactable: 1
m_BlocksRaycasts: 1
m_IgnoreParentGroups: 0
--- !u!114 &2974111728825125460
MonoBehaviour:
m_ObjectHideFlags: 0

m_Name:
m_EditorClassIdentifier:
m_popupText: {fileID: 1837149117904640813}
--- !u!223 &1969158100312030839
Canvas:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2974111728825125462}
m_Enabled: 1
serializedVersion: 3
m_RenderMode: 2
m_Camera: {fileID: 0}
m_PlaneDistance: 100
m_PixelPerfect: 0
m_ReceivesEvents: 1
m_OverrideSorting: 0
m_OverridePixelPerfect: 0
m_SortingBucketNormalizedSize: 0
m_AdditionalShaderChannelsFlag: 25
m_SortingLayerID: 0
m_SortingOrder: 0
m_TargetDisplay: 0
--- !u!114 &682510267401884749
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2974111728825125462}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: dc42784cf147c0c48a680349fa168899, type: 3}
m_Name:
m_EditorClassIdentifier:
m_IgnoreReversedGraphics: 1
m_BlockingObjects: 3
m_BlockingMask:
serializedVersion: 2
m_Bits: 4294967295
m_buttonVisibility: {fileID: 3559536739324575631}
--- !u!1 &2974111728944057206
GameObject:
m_ObjectHideFlags: 0

82
Assets/Scenes/mainScene.unity


serializedVersion: 6
m_Component:
- component: {fileID: 1095306259}
- component: {fileID: 1095306258}
- component: {fileID: 1095306257}
- component: {fileID: 1095306256}
- component: {fileID: 1095306255}
m_Layer: 5
m_Name: LogManager

m_Name:
m_EditorClassIdentifier:
m_editorLogVerbosity: 0
m_popUpPrefab: {fileID: 2974111728825125460, guid: 79d6084439b78bb4eaf5232cb953fd87,
type: 3}
m_reaction:
m_popUp: {fileID: 1228229694}
m_errorReaction:
m_logMessageCallback:
m_PersistentCalls:
m_Calls:

m_StringArgument:
m_BoolArgument: 0
m_CallState: 2
--- !u!114 &1095306256
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1095306254}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: dc42784cf147c0c48a680349fa168899, type: 3}
m_Name:
m_EditorClassIdentifier:
m_IgnoreReversedGraphics: 1
m_BlockingObjects: 0
m_BlockingMask:
serializedVersion: 2
m_Bits: 4294967295
--- !u!114 &1095306257
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1095306254}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 0cd44c1031e13a943bb63640046fad76, type: 3}
m_Name:
m_EditorClassIdentifier:
m_UiScaleMode: 1
m_ReferencePixelsPerUnit: 100
m_ScaleFactor: 1
m_ReferenceResolution: {x: 800, y: 600}
m_ScreenMatchMode: 0
m_MatchWidthOrHeight: 0
m_PhysicalUnit: 3
m_FallbackScreenDPI: 96
m_DefaultSpriteDPI: 96
m_DynamicPixelsPerUnit: 1
m_PresetInfoIsWorld: 0
--- !u!223 &1095306258
Canvas:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1095306254}
m_Enabled: 1
serializedVersion: 3
m_RenderMode: 0
m_Camera: {fileID: 0}
m_PlaneDistance: 100
m_PixelPerfect: 0
m_ReceivesEvents: 1
m_OverrideSorting: 0
m_OverridePixelPerfect: 0
m_SortingBucketNormalizedSize: 0
m_AdditionalShaderChannelsFlag: 25
m_SortingLayerID: 0
m_SortingOrder: 1
m_TargetDisplay: 0
--- !u!224 &1095306259
RectTransform:
m_ObjectHideFlags: 0

m_GameObject: {fileID: 1095306254}
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_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 4

m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: a03b37d5b8df06948b36dfbc430a1ea5, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!114 &1228229694 stripped
MonoBehaviour:
m_CorrespondingSourceObject: {fileID: 7914629187891855366, guid: f1d618bdc6f1813449d428126e640aa5, type: 3}
m_PrefabInstance: {fileID: 2637199315837045693}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: e5f1bc94a7a7b5249a02001abc3096ef, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!114 &1412109061 stripped

20
Assets/Scripts/Infrastructure/AsyncRequest.cs


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace LobbyRelaySample

/// This will also permit parsing incoming exceptions for any service-specific errors that should be displayed to the player.
public static class AsyncRequest
public abstract class AsyncRequest
public static async void DoRequest(Task task, Action onComplete)
public async void DoRequest(Task task, Action onComplete)
{
string currentTrace = System.Environment.StackTrace; // For debugging. If we don't get the calling context here, it's lost once the async operation begins.
try

{ Exception eFull = new Exception($"Call stack before async call:\n{currentTrace}\n", e);
{
ParseServiceException(e);
Exception eFull = new Exception($"Call stack before async call:\n{currentTrace}\n", e); // TODO: Are we still missing Relay exceptions after the update?
throw eFull;
}
finally

public static async void DoRequest<T>(Task<T> task, Action<T> onComplete)
public async void DoRequest<T>(Task<T> task, Action<T> onComplete)
{
T result = default;
string currentTrace = System.Environment.StackTrace;

catch (Exception e)
{ Exception eFull = new Exception($"Call stack before async call:\n{currentTrace}\n", e);
{
ParseServiceException(e);
Exception eFull = new Exception($"Call stack before async call:\n{currentTrace}\n", e);
throw eFull;
}
finally

protected abstract void ParseServiceException(Exception e);
}
}

42
Assets/Scripts/Infrastructure/LogHandler.cs


using System;
using UnityEngine;
using UnityEngine.Events;
using Object = UnityEngine.Object;
namespace LobbyRelaySample

static LogHandler s_instance;
ILogHandler m_DefaultLogHandler = Debug.unityLogger.logHandler; // Store the default logger that prints to console.
ErrorReaction m_reaction;
public static LogHandler Get()
{

return s_instance;
}
public void SetLogReactions(ErrorReaction reactions)
{
m_reaction = reactions;
}
public void LogFormat(LogType logType, Object context, string format, params object[] args)
{
if (logType == LogType.Exception) // Exceptions are captured by LogException and should always be logged.

public void LogException(Exception exception, Object context)
{
LogReaction(exception);
}
private void LogReaction(Exception exception)
{
m_reaction?.Filter(exception);
}
}
/// <summary>
/// The idea here is to present the most relevant error first.
/// </summary>
[Serializable]
public class ErrorReaction
{
public UnityEvent<string> m_logMessageCallback;
public void Filter(Exception exception)
{
string message = "";
var rawExceptionMessage = "";
// We want to ensure the most relevant error message is on top.
if (exception.InnerException != null)
rawExceptionMessage = exception.InnerException.ToString();
else
rawExceptionMessage = exception.ToString();
var firstLineIndex = rawExceptionMessage.IndexOf("\n");
var firstRelayString = rawExceptionMessage.Substring(0, firstLineIndex);
message = firstRelayString;
if (string.IsNullOrEmpty(message))
return;
m_logMessageCallback?.Invoke(message);
}
}
}

32
Assets/Scripts/Infrastructure/LogHandlerSettings.cs


using System;
using System.Collections.Generic;
using UnityEngine.Serialization;
public class LogHandlerSettings : MonoBehaviour
/// <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
{
[SerializeField]
[Tooltip("Only logs of this level or higher will appear in the console.")]

private PopUpUI m_popUpPrefab;
[SerializeField]
private ErrorReaction m_errorReaction;
private PopUpUI m_popUp;
void Awake()
private void Awake()
LogHandler.Get().SetLogReactions(m_errorReaction);
Locator.Get.Messenger.Subscribe(this);
}
private void OnDestroy()
{
Locator.Get.Messenger.Unsubscribe(this);
public void SpawnErrorPopup(string errorMessage)
public void OnReceiveMessage(MessageType type, object msg)
var popupInstance = Instantiate(m_popUpPrefab, transform);
popupInstance.ShowPopup(errorMessage, Color.red);
if (type == MessageType.DisplayErrorPopup && msg != null)
SpawnErrorPopup((string)msg);
}
private void SpawnErrorPopup(string errorMessage)
{
m_popUp.ShowPopup(errorMessage);
}
}
}

1
Assets/Scripts/Infrastructure/Messenger.cs


StartCountdown = 9,
CancelCountdown = 10,
ConfirmInGameState = 11,
DisplayErrorPopup = 12,
}
/// <summary>

20
Assets/Scripts/Lobby/LobbyAPIInterface.cs


Player = new Player(id: requesterUASId, data: localUserData)
};
var task = Lobbies.Instance.CreateLobbyAsync(lobbyName, maxPlayers, createOptions);
AsyncRequest.DoRequest(task, onComplete);
AsyncRequestLobby.Instance.DoRequest(task, onComplete);
AsyncRequest.DoRequest(task, onComplete);
AsyncRequestLobby.Instance.DoRequest(task, onComplete);
}
public static void JoinLobbyAsync_ByCode(string requesterUASId, string lobbyCode, Dictionary<string, PlayerDataObject> localUserData, Action<Lobby> onComplete)

AsyncRequest.DoRequest(task, onComplete);
AsyncRequestLobby.Instance.DoRequest(task, onComplete);
}
public static void JoinLobbyAsync_ById(string requesterUASId, string lobbyId, Dictionary<string, PlayerDataObject> localUserData, Action<Lobby> onComplete)

AsyncRequest.DoRequest(task, onComplete);
AsyncRequestLobby.Instance.DoRequest(task, onComplete);
AsyncRequest.DoRequest(task, onComplete);
AsyncRequestLobby.Instance.DoRequest(task, onComplete);
}
public static void QueryAllLobbiesAsync(List<QueryFilter> filters, Action<QueryResponse> onComplete)

Filters = filters
};
var task = Lobbies.Instance.QueryLobbiesAsync(queryOptions);
AsyncRequest.DoRequest(task, onComplete);
AsyncRequestLobby.Instance.DoRequest(task, onComplete);
AsyncRequest.DoRequest(task, onComplete);
AsyncRequestLobby.Instance.DoRequest(task, onComplete);
}
public static void UpdateLobbyAsync(string lobbyId, Dictionary<string, DataObject> data, Action<Lobby> onComplete)

AsyncRequest.DoRequest(task, onComplete);
AsyncRequestLobby.Instance.DoRequest(task, onComplete);
}
public static void UpdatePlayerAsync(string lobbyId, string playerId, Dictionary<string, PlayerDataObject> data, Action<Lobby> onComplete, string allocationId, string connectionInfo)

ConnectionInfo = connectionInfo
};
var task = Lobbies.Instance.UpdatePlayerAsync(lobbyId, playerId, updateOptions);
AsyncRequest.DoRequest(task, onComplete);
AsyncRequestLobby.Instance.DoRequest(task, onComplete);
AsyncRequest.DoRequest(task, null);
AsyncRequestLobby.Instance.DoRequest(task, null);
}
}
}

7
Assets/Scripts/Relay/RelayAPIInterface.cs


using System;
using System.Threading.Tasks;
using Unity.Services.Relay;
using Unity.Services.Relay.Allocations;
using Unity.Services.Relay.Models;

{
CreateAllocationRequest createAllocationRequest = new CreateAllocationRequest(new AllocationRequest(maxConnections));
var task = RelayService.AllocationsApiClient.CreateAllocationAsync(createAllocationRequest);
AsyncRequest.DoRequest(task, OnResponse);
AsyncRequestRelay.Instance.DoRequest(task, OnResponse);
void OnResponse(Response<AllocateResponseBody> response)
{

{
CreateJoincodeRequest joinCodeRequest = new CreateJoincodeRequest(new JoinCodeRequest(hostAllocationId));
var task = RelayService.AllocationsApiClient.CreateJoincodeAsync(joinCodeRequest);
AsyncRequest.DoRequest(task, onComplete);
AsyncRequestRelay.Instance.DoRequest(task, onComplete);
}
/// <summary>

{
JoinRelayRequest joinRequest = new JoinRelayRequest(new JoinRequest(joinCode));
var task = RelayService.AllocationsApiClient.JoinRelayAsync(joinRequest);
AsyncRequest.DoRequest(task, onComplete);
AsyncRequestRelay.Instance.DoRequest(task, onComplete);
}
}
}

55
Assets/Scripts/UI/PopUpUI.cs


using LobbyRelaySample.UI;
using System.Text;
using UnityEngine.Serialization;
using UnityEngine.UI;
/// <summary>
/// Controls a pop-up message that lays over the rest of the UI, with a button to dismiss. Used for displaying player-facing error messages.
/// </summary>
TMP_InputField m_popupText;
private TMP_InputField m_popupText;
[SerializeField]
private CanvasGroup m_buttonVisibility;
private float m_buttonVisibilityTimeout = -1;
private StringBuilder m_currentText = new StringBuilder();
private void Awake()
{
gameObject.SetActive(false);
}
/// <summary>
/// If the pop-up is not currently visible, display it. If it is, append the incoming text to the existing pop-up.
/// </summary>
public void ShowPopup(string newText)
{
if (!gameObject.activeSelf)
{ m_currentText.Clear();
gameObject.SetActive(true);
}
m_currentText.AppendLine(newText);
m_popupText.SetTextWithoutNotify(m_currentText.ToString());
DisableButton();
}
private void DisableButton()
{
m_buttonVisibilityTimeout = 0.5f; // Briefly prevent the popup from being dismissed, to ensure the player doesn't accidentally click past it without seeing it.
m_buttonVisibility.alpha = 0.5f;
m_buttonVisibility.interactable = false;
}
private void ReenableButton()
{
m_buttonVisibility.alpha = 1;
m_buttonVisibility.interactable = true;
}
public void ShowPopup(string newText, Color textColor = default)
private void Update()
m_popupText.SetTextWithoutNotify(newText);
m_popupText.textComponent.color = textColor;
if (m_buttonVisibilityTimeout >= 0 && m_buttonVisibilityTimeout - Time.deltaTime < 0)
ReenableButton();
m_buttonVisibilityTimeout -= Time.deltaTime;
public void Delete()
public void ClearPopup()
Destroy(gameObject);
gameObject.SetActive(false);
}
}
}

31
Assets/Scripts/Lobby/AsyncRequestLobby.cs


using System;
using Unity.Services.Lobbies;
namespace LobbyRelaySample.lobby
{
public class AsyncRequestLobby : AsyncRequest
{
private static AsyncRequestLobby s_instance;
public static AsyncRequestLobby Instance
{
get
{ if (s_instance == null)
s_instance = new AsyncRequestLobby();
return s_instance;
}
}
/// <summary>
/// The Lobby service will wrap HTTP errors in LobbyServiceExceptions. We can filter on LobbyServiceException.Reason for custom behavior.
/// </summary>
protected override void ParseServiceException(Exception e)
{
if (!(e is LobbyServiceException))
return;
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, $"Error: {lobbyEx.Message} ({lobbyEx.InnerException.Message})"); // Lobby error type, then HTTP error type.
}
}
}

11
Assets/Scripts/Lobby/AsyncRequestLobby.cs.meta


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

23
Assets/Scripts/Relay/AsyncRequestRelay.cs


using System;
using Unity.Services.Relay;
namespace LobbyRelaySample.Relay
{
public class AsyncRequestRelay : AsyncRequest
{
private static AsyncRequestRelay s_instance;
public static AsyncRequestRelay Instance
{
get
{ if (s_instance == null)
s_instance = new AsyncRequestRelay();
return s_instance;
}
}
protected override void ParseServiceException(Exception e)
{
// TODO: Implement
}
}
}

11
Assets/Scripts/Relay/AsyncRequestRelay.cs.meta


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