Skip to content

Instantly share code, notes, and snippets.

@LukmanaAfif
Created January 8, 2021 02:31
Show Gist options
  • Save LukmanaAfif/d3c8a78bc6e4d4b7006da6573f1086bc to your computer and use it in GitHub Desktop.
Save LukmanaAfif/d3c8a78bc6e4d4b7006da6573f1086bc to your computer and use it in GitHub Desktop.
AccelByte Sample Game Unity - Matchmaking Logic
#region AccelByte MatchMaking Functions
/// <summary>
/// Find match function by calling lobby matchmaking
/// Get latencies from available game server regions from Quality of Service
/// Latencies can be used for matchmaking to determine which region has the lowest latency
/// </summary>
private void FindMatch()
{
if (connectToLocal)
{
lobbyLogic.abLobby.StartMatchmaking(gameMode, accelByteManager.LocalDSName, OnFindMatch);
}
else
{
var regionIndex = UIHandlerLobbyComponent.regionSelectorDropdown.value;
var selectedLatency = qosLatencies.Values.ToList()[regionIndex];
var selectedRegion = qosLatencies.Keys.ToList()[regionIndex];
Dictionary<string, int> latencies = new Dictionary<string, int>(1){{selectedRegion, selectedLatency}};
if (!selectedRegion.IsNullOrEmpty() && selectedLatency > 0)
{
#if FORCE_PROVIDER_AWS
Dictionary<string, int> availableRegion = new Dictionary<string, int>();
foreach (KeyValuePair<string, int> region in latencies)
{
Debug.Log("[LobbyLogic] FindMatch region :" + region.Key + " latency: " + region.Value);
if (region.Key == "ap-southeast-1" || region.Key == "us-west-2")
{
Debug.Log("[LobbyLogic] FindMatch FORCE connect to region :" + region.Key + " latency: " + region.Value);
availableRegion.Add(region.Key,region.Value);
}
}
lobbyLogic.abLobby.StartMatchmaking(gameMode, LightFantasticConfig.DS_TARGET_VERSION, availableRegion, OnFindMatch);
return;
#else
lobbyLogic.abLobby.StartMatchmaking(gameMode, "", LightFantasticConfig.DS_TARGET_VERSION, latencies, OnFindMatch);
#endif //FORCE_PROVIDER_AWS
}
else
{
lobbyLogic.abLobby.StartMatchmaking(gameMode, "", LightFantasticConfig.DS_TARGET_VERSION, OnFindMatch);
}
}
}
/// <summary>
/// Matchmaking required the player to a party
/// When the player does not included in a party yet, then create a party first
/// </summary>
private void FindMatchButtonClicked()
{
if (!lobbyLogic.partyLogic.GetIsLocalPlayerInParty())
{
lobbyLogic.abLobby.CreateParty(OnPartyCreatedFindMatch);
}
else
{
FindMatch();
}
}
/// <summary>
/// Cancel matchmaking bound on cancel matchmaking button
/// </summary>
private void FindMatchCancelClicked()
{
lobbyLogic.abLobby.CancelMatchmaking(gameMode, OnFindMatchCanceled);
}
/// <summary>
/// The game is using auto accept then ready consent callback thrown from lobby service
/// This function can be used for accepting ready consent by bind it to accept popup button
/// abMatchmakingNotif.MatchID will be filled when the match has been found
/// </summary>
public void OnAcceptReadyForMatchClicked()
{
if (abMatchmakingNotif != null)
{
lobbyLogic.abLobby.ConfirmReadyForMatch(abMatchmakingNotif.matchId, OnReadyForMatchConfirmation);
}
UIHandlerLobbyComponent.popupMatchConfirmation.gameObject.SetActive(false);
}
/// <summary>
/// The game is using auto accept then ready consent callback thrown from lobby service
/// This function can be used for decline ready consent by bind it to decline popup button
/// </summary>
public void OnDeclineReadyForMatchClicked()
{
lobbyLogic.abLobby.CancelMatchmaking(gameMode, OnFindMatchCanceled);
UIHandlerLobbyComponent.popupMatchConfirmation.gameObject.SetActive(false);
}
/// <summary>
/// Connect to game server once the game client knows once the game server is ready
/// </summary>
private bool allowToConnectServer = true;
IEnumerator WaitForGameServerReady(string ip, string port)
{
if (allowToConnectServer)
{
allowToConnectServer = false;
bool isActive = true;
while (isActive)
{
yield return new WaitForSecondsRealtime(1.0f);
if (connectToLocal)
{
ip = ipConnectToLocal;
port = portConnectToLocal;
}
multiplayerConnect.SetIPAddressPort(ip, port);
multiplayerConnect.Connect();
isActive = false;
}
}
}
/// <summary>
/// Show matchmaking board once the callback from find match triggered
/// </summary>
/// <param name="show"> switch either to show and hide UI </param>
/// <param name="gameFound"> show the state of the match has been found </param>
public void ShowMatchmakingBoard(bool show, bool gameFound = false)
{
// If there's matchmaking board shown, disable the [ONLINE] and [LOCAL] button
UIHandlerLobbyComponent.matchmakingButtonCollection.SetInteractable(PlayMatchButtonsScript.ButtonList.OnlineButton, !show);
UIHandlerLobbyComponent.matchmakingButtonCollection.SetInteractable(PlayMatchButtonsScript.ButtonList.LocalButton, !show);
UIHandlerLobbyComponent.matchmakingBoard.waitingTimerLayout.gameObject.SetActive(false);
UIHandlerLobbyComponent.matchmakingBoard.gameObject.SetActive(show);
if (!show)
{
UIHandlerLobbyComponent.matchmakingBoard.TerminateCountdown();
}
if (gameFound)
{
UIHandlerLobbyComponent.matchmakingBoard.gameObject.SetActive(true);
}
else
{
UIHandlerLobbyComponent.matchmakingBoard.waitingTimerLayout.gameObject.SetActive(true);
}
}
/// <summary>
/// Game server using this function to host a game match
/// multiplayerConnect referenced multiplayer menu from Forge Networking
/// </summary>
public void HostingGameServer()
{
multiplayerConnect.Host();
}
/// <summary>
/// Game client function to connect to game server
/// multiplayerConnect referenced multiplayer menu from Forge Networking
/// </summary>
public void ConnecttoGameServer()
{
multiplayerConnect.Connect();
}
/// <summary>
/// Show prompt panel to choose "RETRY" or "CANCEL" matchmaking
/// </summary>
private void OnFailedMatchmaking(string reason)
{
lobbyLogic.abLobby.CancelMatchmaking(gameMode, OnFindMatchCanceled);
UIHandlerLobbyComponent.matchmakingFailedPromptPanel.SetText("MATCHMAKING FAILED", reason);
UIHandlerLobbyComponent.matchmakingFailedPromptPanel.Show();
}
#endregion
#region AccelByte MatchMaking Notification Callbacks
/// <summary>
/// Callback from MatchmakingCompleted event of Lobby service
/// This will be triggered if the player is in a party and the party leader do matchmaking
/// </summary>
/// <param name="result"> callback result that consist of a match id and matchmaking status </param>
private void OnFindMatchCompleted(Result<MatchmakingNotif> result)
{
if (result.IsError)
{
Debug.Log("OnFindMatchCompleted failed:" + result.Error.Message);
Debug.Log("OnFindMatchCompleted Response Code::" + result.Error.Code);
}
else
{
Debug.Log("OnFindMatchCompleted Finding matchmaking Completed");
Debug.Log("OnFindMatchCompleted Match Found: " + result.Value.matchId);
Debug.Log(" Match status: " + result.Value.status);
Debug.Log(" Expected match status: " + MatchmakingNotifStatus.done.ToString());
abMatchmakingNotif = result.Value;
lobbyLogic.abLobby.ConfirmReadyForMatch(abMatchmakingNotif.matchId, OnReadyForMatchConfirmation);
UIHandlerLobbyComponent.matchmakingBoard.StartCountdown(MatchmakingWaitingPhase.ConfirmingMatch,
delegate
{
OnFailedMatchmaking("Timeout to confirm matchmaking");
});
// if the player is in a party and the match is complete
if (result.Value.status == MatchmakingNotifStatus.done.ToString())
{
lobbyLogic.WriteInDebugBox(" Match Found: " + result.Value.matchId);
}
// if the player is in a party and the party leader start a matchmaking
else if (result.Value.status == MatchmakingNotifStatus.start.ToString())
{
MainThreadTaskRunner.Instance.Run(delegate
{
ShowMatchmakingBoard(true);
});
UIHandlerLobbyComponent.matchmakingBoard.StartCountdown(MatchmakingWaitingPhase.FindMatch,
delegate
{
OnFailedMatchmaking("Timeout to finding match");
});
UIHandlerLobbyComponent.matchmakingBoard.SetGameMode(gameModeEnum);
}
// if the player is in a party and the party leader cancel the current matchmaking
else if (result.Value.status == MatchmakingNotifStatus.cancel.ToString())
{
MainThreadTaskRunner.Instance.Run(delegate
{
ShowMatchmakingBoard(false);
});
}
}
}
/// <summary>
/// Callback event from DSUpdated of Lobby service
/// The data are retrieved from DSM containing the current status of game server that being spawned
/// Will be triggered multiple times each time will be udpated with the most current status
/// CREATING : The DS is being spawned by DSM
/// READY : The DS is ready to use, the game client can start to jump in to the game server
/// BUSY : The DS is currently being used
/// </summary>
/// <param name="result"> Callback result of the current DS status </param>
private void OnSuccessMatch(Result<DsNotif> result)
{
if (result.IsError)
{
Debug.Log("OnSuccessMatch failed:" + result.Error.Message);
Debug.Log("OnSuccessMatch Response Code::" + result.Error.Code);
MainThreadTaskRunner.Instance.Run(delegate
{
OnFailedMatchmaking("An error occurs in the dedicated server manager");
});
}
else
{
if (result.Value.isOK == "false" && !connectToLocal)
{
MainThreadTaskRunner.Instance.Run(delegate
{
OnFailedMatchmaking("Failed to create a dedicated server");
});
return;
}
UIHandlerLobbyComponent.matchmakingBoard.StartCountdown(MatchmakingWaitingPhase.WaitingDSM,
delegate { OnFailedMatchmaking("Spawning a dedicated server timed out"); });
Debug.Log("OnSuccessMatch success match completed");
// DSM on process creating DS
if (result.Value.status == DSNotifStatus.CREATING.ToString())
{
Debug.Log("Waiting for the game server!");
}
// DS is ready
else if (result.Value.status == DSNotifStatus.READY.ToString())
{
// Set IP and port to persistent and connect to the game
Debug.Log("Entering the game!");
Debug.Log("Lobby OnSuccessMatch Connect");
MainThreadTaskRunner.Instance.Run(() => { StartCoroutine(WaitForGameServerReady(result.Value.ip, result.Value.port.ToString())); });
}
else if (result.Value.status == DSNotifStatus.BUSY.ToString())
{
Debug.Log("Entering the game!");
Debug.Log("Lobby OnSuccessMatch Connect");
Debug.Log("ip: " + result.Value.ip + "port: " + result.Value.port);
MainThreadTaskRunner.Instance.Run(() => { StartCoroutine(WaitForGameServerReady(result.Value.ip, result.Value.port.ToString())); });
}
Debug.Log("OnSuccessMatch ip: " + result.Value.ip + "port: " + result.Value.port);
lobbyLogic.WriteInDebugBox("Match Success status " + result.Value?.status + " isOK " + result.Value.isOK + " pod: " + result.Value.podName);
lobbyLogic.WriteInDebugBox("Match Success IP: " + result.Value.ip + " Port: " + result.Value.port);
ShowMatchmakingBoard(true, true);
abDSNotif = result.Value;
}
}
#endregion
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment