浏览代码

Adding in rate limit events for query operations. Now, the refresh button will be disabled while within the rate limit cooldown, and the lobby data refresh follows it as well (which it did previously, but it was less clear).

/main/staging/rate_limit_data
nathaniel.buck@unity3d.com 3 年前
当前提交
b9db2078
共有 8 个文件被更改,包括 169 次插入66 次删除
  1. 72
      Assets/Prefabs/UI/JoinContent.prefab
  2. 2
      Assets/Scripts/Game/GameManager.cs
  3. 89
      Assets/Scripts/Lobby/LobbyAsyncRequests.cs
  4. 9
      Assets/Scripts/UI/UIPanelBase.cs
  5. 2
      Assets/Scripts/UI/RateLimitVisibility.cs.meta
  6. 35
      Assets/Scripts/UI/RateLimitVisibility.cs
  7. 26
      Assets/Scripts/Lobby/LobbyListHeartbeat.cs
  8. 0
      /Assets/Scripts/UI/RateLimitVisibility.cs.meta

72
Assets/Prefabs/UI/JoinContent.prefab


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

m_EditorClassIdentifier:
m_onVisibilityChange:
m_PersistentCalls:
m_Calls:
- m_Target: {fileID: 7645291363343340383}
m_TargetAssemblyTypeName: LobbyRooms.RoomsListHeartbeat, LobbyRooms
m_MethodName: SetActive
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_LobbyButtonPrefab: {fileID: 7018369548608736188, guid: f6d35a456ba76a24587dce83bd088b7d, type: 3}
m_LobbyCodeField: {fileID: 8659642538454988273}
m_LobbyButtonParent: {fileID: 7824921818678239159}

m_StringArgument:
m_BoolArgument: 0
m_CallState: 2
--- !u!114 &7645291363343340383
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: 48ec34a3875818e4690f1bf0be69ccd9, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!1 &891284586109156510
GameObject:
m_ObjectHideFlags: 0

- component: {fileID: 7588080595629456861}
- component: {fileID: 6677898783497457153}
- component: {fileID: 2237434810424445170}
- component: {fileID: 3932890662139234867}
- component: {fileID: 3262820153109060702}
- component: {fileID: 1874295389668054338}
m_Layer: 5
m_Name: RefreshButton
m_TagString: Untagged

m_FlexibleWidth: -1
m_FlexibleHeight: -1
m_LayoutPriority: 1
--- !u!225 &3932890662139234867
CanvasGroup:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4272638571207087159}
m_Enabled: 1
m_Alpha: 1
m_Interactable: 1
m_BlocksRaycasts: 1
m_IgnoreParentGroups: 0
--- !u!114 &3262820153109060702
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4272638571207087159}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: ab355d5994635724dbde297a055fb586, type: 3}
m_Name:
m_EditorClassIdentifier:
m_onVisibilityChange:
m_PersistentCalls:
m_Calls: []
--- !u!114 &1874295389668054338
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4272638571207087159}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 771ddda4ce2ee2a4dad57866ecc1170d, type: 3}
m_Name:
m_EditorClassIdentifier:
m_target: {fileID: 3262820153109060702}
m_alphaWhenHidden: 0.5
m_requestType: 0
--- !u!1 &4581814564158191570
GameObject:
m_ObjectHideFlags: 0

2
Assets/Scripts/Game/GameManager.cs


qr => {
if (qr != null)
OnLobbiesQueried(lobby.ToLocalLobby.Convert(qr));
else
m_lobbyServiceData.State = LobbyQueryState.Error;
},
er => {
long errorLong = 0;

89
Assets/Scripts/Lobby/LobbyAsyncRequests.cs


public LobbyAsyncRequests()
{
Locator.Get.UpdateSlow.Subscribe(UpdateLobby, 1.5f); // Shouldn't need to unsubscribe since this instance won't be replaced.
Locator.Get.UpdateSlow.Subscribe(UpdateLobby, 0.5f); // Shouldn't need to unsubscribe since this instance won't be replaced. 0.5s is arbitrary; the rate limits are tracked later.
m_rateLimitQuery.onChanged += HandlePendingOperations;
}
private static bool IsSuccessful(Response response)

private Queue<Action> m_pendingOperations = new Queue<Action>();
private string m_currentLobbyId = null;
private Lobby m_lastKnownLobby;
private bool m_isMidRetrieve = false;
public Lobby CurrentLobby => m_lastKnownLobby;
public void BeginTracking(string lobbyId)

void OnComplete(Lobby lobby)
{
if (lobby != null)
{
m_isMidRetrieve = false;
HandlePendingOperations();
}
private void HandlePendingOperations()
private void HandlePendingOperations(RateLimitCooldown rateLimit)
if (rateLimit.IsInCooldown)
return;
while (m_pendingOperations.Count > 0)
m_pendingOperations.Dequeue()?.Invoke(); // Note: If this ends up enqueuing a bunch of operations, we might need to batch them and/or ensure they don't all execute at once.
}

#region Lobby API calls are rate limited, and some other operations might want to know when the rate limits have passed.
public enum RequestType { Query = 0 }
public RateLimitCooldown GetRateLimit(RequestType type)
{
return m_rateLimitQuery;
}
private RateLimitCooldown m_rateLimitQuery = new RateLimitCooldown(1.5f);
#endregion
private static Dictionary<string, PlayerDataObject> CreateInitialPlayerData(LobbyUser player)
{
Dictionary<string, PlayerDataObject> data = new Dictionary<string, PlayerDataObject>();

/// <param name="onListRetrieved">If called with null, retrieval was unsuccessful. Else, this will be given a list of contents to display, as pairs of a lobby code and a display string for that lobby.</param>
public void RetrieveLobbyListAsync(Action<QueryResponse> onListRetrieved, Action<Response<QueryResponse>> onError = null, LobbyColor limitToColor = LobbyColor.None)
{
if (!m_rateLimitQuery.CanCall())
{
onListRetrieved?.Invoke(null);
return;
}
List<QueryFilter> filters = new List<QueryFilter>();
if (limitToColor == LobbyColor.Orange)
filters.Add(new QueryFilter(QueryFilter.FieldOptions.N1, ((int)LobbyColor.Orange).ToString(), QueryFilter.OpOptions.EQ));

onError?.Invoke(response);
}
}
/// <param name="onComplete">If no lobby is retrieved, this is given null.</param>
/// <param name="onComplete">If no lobby is retrieved, or if this call hits the rate limit, this is given null.</param>
if (m_isMidRetrieve)
return; // Not calling onComplete since there's just the one point at which this is called.
m_isMidRetrieve = true;
if (!m_rateLimitQuery.CanCall())
{
onComplete?.Invoke(null);
return;
}
m_isMidRetrieve = false;
onComplete?.Invoke(response?.Result);
}
}

/// </summary>
private bool ShouldUpdateData(Action caller, Action onComplete, bool shouldRetryIfLobbyNull)
{
if (m_isMidRetrieve)
if (m_rateLimitQuery.IsInCooldown)
{ m_pendingOperations.Enqueue(caller);
return false;
}

m_heartbeatTime -= k_heartbeatPeriod;
LobbyAPIInterface.HeartbeatPlayerAsync(m_lastKnownLobby.Id);
}
}
public class RateLimitCooldown : Observed<RateLimitCooldown>
{
private float m_timeSinceLastCall = float.MaxValue;
private readonly float m_cooldownTime;
private bool m_isInCooldown = false;
public bool IsInCooldown
{
get => m_isInCooldown;
set
{ if (m_isInCooldown != value)
{ m_isInCooldown = value;
OnChanged(this);
}
}
}
public RateLimitCooldown(float cooldownTime)
{
m_cooldownTime = cooldownTime;
}
public bool CanCall()
{
if (m_timeSinceLastCall < m_cooldownTime)
return false;
else
{
Locator.Get.UpdateSlow.Subscribe(OnUpdate, m_cooldownTime);
m_timeSinceLastCall = 0;
IsInCooldown = true;
return true;
}
}
private void OnUpdate(float dt)
{
m_timeSinceLastCall += dt;
if (m_timeSinceLastCall >= m_cooldownTime)
{
IsInCooldown = false;
Locator.Get.UpdateSlow.Unsubscribe(OnUpdate); // Note that this is after IsInCooldown is set, to prevent an Observer from kicking off CanCall again immediately.
}
}
public override void CopyObserved(RateLimitCooldown oldObserved) { /* This behavior isn't needed; we're just here for the OnChanged event management. */ }
}
}
}

9
Assets/Scripts/UI/UIPanelBase.cs


child.m_onVisibilityChange?.Invoke(true);
}
public void Hide()
public void Hide() // Called by some serialized events, so we can't just have targetAlpha as an optional parameter.
{
Hide(0);
}
public void Hide(float targetAlpha)
MyCanvasGroup.alpha = 0;
MyCanvasGroup.alpha = targetAlpha;
MyCanvasGroup.interactable = false;
MyCanvasGroup.blocksRaycasts = false;
showing = false;

2
Assets/Scripts/UI/RateLimitVisibility.cs.meta


fileFormatVersion: 2
guid: 48ec34a3875818e4690f1bf0be69ccd9
guid: 771ddda4ce2ee2a4dad57866ecc1170d
MonoImporter:
externalObjects: {}
serializedVersion: 2

35
Assets/Scripts/UI/RateLimitVisibility.cs


using UnityEngine;
namespace LobbyRelaySample.UI
{
/// <summary>
/// Observes the Lobby request rate limits and changes the visibility of a UIPanelBase to suit.
/// E.g. the refresh button on the Join menu should be inactive after a refresh for long enough to avoid the lobby query rate limit.
/// </summary>
public class RateLimitVisibility : MonoBehaviour
{
[SerializeField]
UIPanelBase m_target;
[SerializeField]
float m_alphaWhenHidden = 0.5f;
[SerializeField]
LobbyAsyncRequests.RequestType m_requestType;
private void Start()
{
LobbyAsyncRequests.Instance.GetRateLimit(m_requestType).onChanged += UpdateVisibility;
}
private void OnDestroy()
{
LobbyAsyncRequests.Instance.GetRateLimit(m_requestType).onChanged -= UpdateVisibility;
}
private void UpdateVisibility(LobbyAsyncRequests.RateLimitCooldown rateLimit)
{
if (rateLimit.IsInCooldown)
m_target.Hide(m_alphaWhenHidden);
else
m_target.Show();
}
}
}

26
Assets/Scripts/Lobby/LobbyListHeartbeat.cs


using UnityEngine;
namespace LobbyRelaySample
{
/// <summary>
/// Keeps the lobby list updated automatically.
/// </summary>
public class LobbyListHeartbeat : MonoBehaviour
{
private const float k_refreshRate = 5;
// This is called in-editor via events.
public void SetActive(bool isActive)
{
if (isActive)
Locator.Get.UpdateSlow.Subscribe(OnUpdate, k_refreshRate);
else
Locator.Get.UpdateSlow.Unsubscribe(OnUpdate);
}
private void OnUpdate(float dt)
{
Locator.Get.Messenger.OnReceiveMessage(MessageType.QueryLobbies, null);
}
}
}

/Assets/Scripts/Lobby/LobbyListHeartbeat.cs.meta → /Assets/Scripts/UI/RateLimitVisibility.cs.meta

正在加载...
取消
保存