rc
This commit is contained in:
@@ -22,6 +22,7 @@ public class GameCanvas : MonoBehaviour
|
||||
|
||||
ShowWaitingForOpponentPanel();
|
||||
HideEmotesPanel();
|
||||
HideAllPostGamePanels();
|
||||
}
|
||||
|
||||
void OnDestroy()
|
||||
@@ -60,7 +61,9 @@ public class GameCanvas : MonoBehaviour
|
||||
bool isShowingEmotes=> emotesPanel.gameObject.activeSelf;
|
||||
|
||||
public RectTransform kickWarning;
|
||||
|
||||
public GameObject[] postGamePanels;
|
||||
public TMP_Text txtMatchNumber;
|
||||
const float PostGamePanelDuration = 3f;
|
||||
|
||||
|
||||
void Start(){
|
||||
@@ -93,6 +96,9 @@ public class GameCanvas : MonoBehaviour
|
||||
|
||||
if (txtRcPrize != null)
|
||||
txtRcPrize.text = CoinHelper.FormatString(GameManager.MatchRcPrizeCoins) + " RC";
|
||||
|
||||
if (txtMatchNumber != null && GameManager.DedicatedMatchId > 0)
|
||||
txtMatchNumber.text = "M" + GameManager.DedicatedMatchId;
|
||||
}
|
||||
|
||||
void OnEmoteTextPressed(string txtName){
|
||||
@@ -166,13 +172,36 @@ public class GameCanvas : MonoBehaviour
|
||||
}
|
||||
|
||||
void OnLeaveGamePressed()
|
||||
{
|
||||
if (MessageBoxDialog.IsAvailable)
|
||||
{
|
||||
MessageBoxDialog.Show(
|
||||
"Forfeit match?",
|
||||
"Leaving will result in a loss, making opponent the winner. Are you sure?",
|
||||
confirmed =>
|
||||
{
|
||||
if (confirmed)
|
||||
ConfirmLeaveMatch();
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
ConfirmLeaveMatch();
|
||||
}
|
||||
|
||||
void ConfirmLeaveMatch()
|
||||
{
|
||||
if (CupidLobby.instance != null)
|
||||
CupidLobby.instance.Cancel();
|
||||
LoginManager.ClearMatchYourTeam();
|
||||
|
||||
NetManager.StopNetworkingForSceneChange();
|
||||
if (GameManager.instance != null)
|
||||
{
|
||||
GameManager.instance.Leave();
|
||||
return;
|
||||
}
|
||||
|
||||
NetManager.StopNetworkingForSceneChange();
|
||||
LevelLoadManager.LoadLevel("MainMenu");
|
||||
}
|
||||
|
||||
@@ -314,4 +343,31 @@ public class GameCanvas : MonoBehaviour
|
||||
});
|
||||
}
|
||||
|
||||
public IEnumerator ShowRandomPostGamePanelThenHide()
|
||||
{
|
||||
HideAllPostGamePanels();
|
||||
if (postGamePanels == null || postGamePanels.Length == 0)
|
||||
yield break;
|
||||
|
||||
GameObject chosen = postGamePanels[Random.Range(0, postGamePanels.Length)];
|
||||
if (chosen == null)
|
||||
yield break;
|
||||
|
||||
chosen.SetActive(true);
|
||||
yield return new WaitForSeconds(PostGamePanelDuration);
|
||||
chosen.SetActive(false);
|
||||
}
|
||||
|
||||
void HideAllPostGamePanels()
|
||||
{
|
||||
if (postGamePanels == null)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < postGamePanels.Length; i++)
|
||||
{
|
||||
if (postGamePanels[i] != null)
|
||||
postGamePanels[i].SetActive(false);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+166
-16
@@ -16,6 +16,15 @@ public class GameManager : NetworkBehaviour
|
||||
public static MatchmadePlayer BluePlayer { get; set; }
|
||||
/// <summary>Coin units from matchmaker <c>rc_prize</c>; used for in-game prize UI.</summary>
|
||||
public static int MatchRcPrizeCoins { get; set; }
|
||||
/// <summary>Participation CC amount shown on game over. Actual credit is decided by the winner PATCH.</summary>
|
||||
public const int ParticipationCcAmount = 100;
|
||||
/// <summary>Server-reported: red was still connected when the match ended (eligible for participation CC).</summary>
|
||||
public static bool RedFinishedMatch { get; private set; } = true;
|
||||
/// <summary>Server-reported: blue was still connected when the match ended (eligible for participation CC).</summary>
|
||||
public static bool BlueFinishedMatch { get; private set; } = true;
|
||||
/// <summary>Local player stayed connected through match end and should see +CC on game over.</summary>
|
||||
public static bool LocalPlayerEarnedParticipationCc =>
|
||||
MyTeam == Team.Blue ? BlueFinishedMatch : RedFinishedMatch;
|
||||
/// <summary>Set by dedicated server startup (e.g. CupidConnector parsing <c>-matchId</c>). PATCH is skipped if id ≤ 0 or secret is empty.</summary>
|
||||
public static int DedicatedMatchId { get; private set; }
|
||||
/// <summary>Sent as <c>X-Dedicated-Server-Secret</c>. From <c>-dedicatedSecret</c> arg or <c>DEDICATED_SERVER_SECRET</c> env.</summary>
|
||||
@@ -504,10 +513,57 @@ public class GameManager : NetworkBehaviour
|
||||
}
|
||||
}
|
||||
|
||||
bool _abortLeaveStarted;
|
||||
|
||||
void AbortMatchCollectFailed()
|
||||
{
|
||||
RpcAbortMatch("Match cancelled", "The match could not start. Please try again.");
|
||||
StartCoroutine(CoDisconnectAfterAbortRpc());
|
||||
}
|
||||
|
||||
[ClientRpc]
|
||||
void RpcAbortMatch(string title, string message)
|
||||
{
|
||||
ShowMatchAbortMessage(title, message);
|
||||
// Host keeps the server up so the Rpc can reach the other client first.
|
||||
if (!NetworkServer.active)
|
||||
LeaveToMenuAfterAbort();
|
||||
}
|
||||
|
||||
IEnumerator CoDisconnectAfterAbortRpc()
|
||||
{
|
||||
yield return new WaitForSecondsRealtime(0.5f);
|
||||
if (NetworkServer.active)
|
||||
NetworkServer.DisconnectAll();
|
||||
if (isClient)
|
||||
LeaveToMenuAfterAbort();
|
||||
}
|
||||
|
||||
void NotifyAndLeaveMatch(string title, string message)
|
||||
{
|
||||
ShowMatchAbortMessage(title, message);
|
||||
LeaveToMenuAfterAbort();
|
||||
}
|
||||
|
||||
void ShowMatchAbortMessage(string title, string message)
|
||||
{
|
||||
NetManager.MarkIntentionalClientDisconnect();
|
||||
if (CupidLobby.instance != null)
|
||||
CupidLobby.instance.Cancel();
|
||||
LoginManager.ClearMatchYourTeam();
|
||||
MessageBoxDialog.Show(title, message);
|
||||
}
|
||||
|
||||
void LeaveToMenuAfterAbort()
|
||||
{
|
||||
if (_abortLeaveStarted)
|
||||
return;
|
||||
_abortLeaveStarted = true;
|
||||
|
||||
if (LevelLoadManager.instance != null)
|
||||
LevelLoadManager.instance.Leave();
|
||||
else
|
||||
LevelLoadManager.LoadLevel("MainMenu");
|
||||
}
|
||||
|
||||
IEnumerator DedicatedMatchPatch(string baseUrl, int matchId, string secret, string json)
|
||||
@@ -522,12 +578,12 @@ public class GameManager : NetworkBehaviour
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerator DedicatedMatchPatchWinner(string baseUrl, int matchId, string secret, string winnerLower)
|
||||
public IEnumerator DedicatedMatchPatchWinner(string baseUrl, int matchId, string secret, string winnerLower, bool redConnected, bool blueConnected)
|
||||
{
|
||||
if (matchId <= 0 || string.IsNullOrEmpty(secret))
|
||||
yield break;
|
||||
|
||||
string jsonBody = "{\"winner\":\"" + winnerLower + "\"}";
|
||||
string jsonBody = DedicatedMatschInternalApi.BuildWinnerPatchJson(winnerLower, redConnected, blueConnected);
|
||||
using (var req = DedicatedMatschInternalApi.BuildWinnerRequest(baseUrl, matchId, secret, jsonBody))
|
||||
{
|
||||
yield return req.SendWebRequest();
|
||||
@@ -535,7 +591,7 @@ public class GameManager : NetworkBehaviour
|
||||
}
|
||||
}
|
||||
|
||||
void ReportDedicatedMatchGameOver(Team winningTeam)
|
||||
void ReportDedicatedMatchGameOver(Team winningTeam, bool redConnected, bool blueConnected)
|
||||
{
|
||||
if (!isServer || DedicatedMatchId <= 0 || string.IsNullOrEmpty(DedicatedMatchSecret) || _winnerSettled)
|
||||
return;
|
||||
@@ -545,14 +601,14 @@ public class GameManager : NetworkBehaviour
|
||||
return;
|
||||
}
|
||||
_winnerSettled = true;
|
||||
StartCoroutine(CoReportDedicatedMatchGameOver(winningTeam));
|
||||
StartCoroutine(CoReportDedicatedMatchGameOver(winningTeam, redConnected, blueConnected));
|
||||
}
|
||||
|
||||
IEnumerator CoReportDedicatedMatchGameOver(Team winningTeam)
|
||||
IEnumerator CoReportDedicatedMatchGameOver(Team winningTeam, bool redConnected, bool blueConnected)
|
||||
{
|
||||
yield return DedicatedMatchPatch(DedicatedInternalApiBase, DedicatedMatchId, DedicatedMatchSecret, "{\"status\":3}");
|
||||
string winner = winningTeam == Team.Red ? "red" : "blue";
|
||||
yield return DedicatedMatchPatchWinner(DedicatedInternalApiBase, DedicatedMatchId, DedicatedMatchSecret, winner);
|
||||
yield return DedicatedMatchPatchWinner(DedicatedInternalApiBase, DedicatedMatchId, DedicatedMatchSecret, winner, redConnected, blueConnected);
|
||||
}
|
||||
|
||||
/// <summary>Score or forfeit end: latch game over, report winner if escrowed, notify clients.</summary>
|
||||
@@ -563,11 +619,58 @@ public class GameManager : NetworkBehaviour
|
||||
|
||||
isGameEnded = true;
|
||||
ReplayRecorder.StopAndFlush();
|
||||
ReportDedicatedMatchGameOver(winningTeam);
|
||||
RpcGameOver(winningTeam);
|
||||
bool redConnected = IsSideConnectedForParticipation(Team.Red);
|
||||
bool blueConnected = IsSideConnectedForParticipation(Team.Blue);
|
||||
Logger.Log($"Dedicated match: end winner={winningTeam} red_connected={redConnected} blue_connected={blueConnected}");
|
||||
ApplyParticipationCcFlags(redConnected, blueConnected);
|
||||
ReportDedicatedMatchGameOver(winningTeam, redConnected, blueConnected);
|
||||
RpcGameOver(winningTeam, redConnected, blueConnected);
|
||||
gameOver(winningTeam);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Server-side: participation CC is only for players still connected at settle.
|
||||
/// Voluntary leave clears the connected flag before <see cref="EndMatch"/>.
|
||||
/// </summary>
|
||||
bool IsSideConnectedForParticipation(Team team)
|
||||
{
|
||||
if (team == Team.Red)
|
||||
{
|
||||
if (!_redConnected)
|
||||
return false;
|
||||
}
|
||||
else if (team == Team.Blue)
|
||||
{
|
||||
if (!_blueConnected)
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (var player in FindObjectsByType<NetPlayer>(FindObjectsSortMode.None))
|
||||
{
|
||||
if (player == null || player.myTeam != team)
|
||||
continue;
|
||||
|
||||
if (player.connectionToClient != null)
|
||||
return player.connectionToClient.isReady;
|
||||
|
||||
// Listen-server host has no connectionToClient.
|
||||
if (player.isLocalPlayer)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void ApplyParticipationCcFlags(bool redConnected, bool blueConnected)
|
||||
{
|
||||
RedFinishedMatch = redConnected;
|
||||
BlueFinishedMatch = blueConnected;
|
||||
}
|
||||
|
||||
void HandleDisconnectForfeitTimers()
|
||||
{
|
||||
if (!_entriesCollected || _winnerSettled || !gameStarted || isGameEnded)
|
||||
@@ -620,9 +723,13 @@ public class GameManager : NetworkBehaviour
|
||||
return;
|
||||
|
||||
Team winningTeam = ResolveForfeitWinnerForShutdown();
|
||||
bool redConnected = IsSideConnectedForParticipation(Team.Red);
|
||||
bool blueConnected = IsSideConnectedForParticipation(Team.Blue);
|
||||
_winnerSettled = true;
|
||||
isGameEnded = true;
|
||||
Logger.Log("Dedicated match: blocking winner settle before shutdown → " + winningTeam);
|
||||
ApplyParticipationCcFlags(redConnected, blueConnected);
|
||||
Logger.Log("Dedicated match: blocking winner settle before shutdown → " + winningTeam
|
||||
+ " red_connected=" + redConnected + " blue_connected=" + blueConnected);
|
||||
|
||||
using (var statusReq = DedicatedMatschInternalApi.BuildRequest(
|
||||
DedicatedInternalApiBase, DedicatedMatchId, DedicatedMatchSecret, "{\"status\":3}"))
|
||||
@@ -634,7 +741,7 @@ public class GameManager : NetworkBehaviour
|
||||
}
|
||||
|
||||
string winner = winningTeam == Team.Red ? "red" : "blue";
|
||||
string jsonBody = "{\"winner\":\"" + winner + "\"}";
|
||||
string jsonBody = DedicatedMatschInternalApi.BuildWinnerPatchJson(winner, redConnected, blueConnected);
|
||||
using (var winReq = DedicatedMatschInternalApi.BuildWinnerRequest(
|
||||
DedicatedInternalApiBase, DedicatedMatchId, DedicatedMatchSecret, jsonBody))
|
||||
{
|
||||
@@ -1157,6 +1264,7 @@ public class GameManager : NetworkBehaviour
|
||||
RpcPlayGoalScoredSfx();
|
||||
|
||||
freezeInput=true;
|
||||
CancelInGoalPuckResets();
|
||||
Team concedingTeam = team;
|
||||
|
||||
if(hitCounter == 1){
|
||||
@@ -1236,7 +1344,8 @@ public class GameManager : NetworkBehaviour
|
||||
}
|
||||
|
||||
[ClientRpc]
|
||||
void RpcGameOver(Team team){
|
||||
void RpcGameOver(Team team, bool redConnected, bool blueConnected){
|
||||
ApplyParticipationCcFlags(redConnected, blueConnected);
|
||||
gameOver(team);
|
||||
}
|
||||
|
||||
@@ -1245,7 +1354,11 @@ public class GameManager : NetworkBehaviour
|
||||
}
|
||||
|
||||
IEnumerator CoroutineGameOver(Team team){
|
||||
yield return new WaitForSeconds(2f);
|
||||
if (GameCanvas.instance != null)
|
||||
yield return GameCanvas.instance.ShowRandomPostGamePanelThenHide();
|
||||
else
|
||||
yield return new WaitForSeconds(2f);
|
||||
|
||||
yield return CoroutineFetchBothPlayersL10();
|
||||
|
||||
// StopClient();
|
||||
@@ -1368,6 +1481,11 @@ public class GameManager : NetworkBehaviour
|
||||
if (leavingTeam != Team.Red && leavingTeam != Team.Blue)
|
||||
return;
|
||||
|
||||
if (leavingTeam == Team.Red)
|
||||
_redConnected = false;
|
||||
else if (leavingTeam == Team.Blue)
|
||||
_blueConnected = false;
|
||||
|
||||
Team winner = GetOpposingTeam(leavingTeam);
|
||||
Logger.Log($"Match: {leavingTeam} forfeited by leave — {winner} wins");
|
||||
EndMatch(winner);
|
||||
@@ -1383,10 +1501,7 @@ public class GameManager : NetworkBehaviour
|
||||
{
|
||||
_thirdPlayerLeaveTriggered = true;
|
||||
Logger.Log("Third player in a 2-player match (matchmaker bug); leaving to main menu.");
|
||||
if (LevelLoadManager.instance != null)
|
||||
LevelLoadManager.instance.Leave();
|
||||
else
|
||||
LevelLoadManager.LoadLevel("MainMenu");
|
||||
NotifyAndLeaveMatch("Unable to join", "This match is already full.");
|
||||
yield break;
|
||||
}
|
||||
}
|
||||
@@ -1456,6 +1571,15 @@ public class GameManager : NetworkBehaviour
|
||||
bool freezeInput = false;
|
||||
public bool CanProcessGoal => isServer && !freezeInput && !isGameEnded;
|
||||
|
||||
void CancelInGoalPuckResets()
|
||||
{
|
||||
foreach (Puck puck in pucks)
|
||||
{
|
||||
if (puck != null)
|
||||
puck.CancelInGoalReset();
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator CoroutineOnGoal(Team team, bool kickoff = false){
|
||||
if(coroutinePostLaunch!=null){
|
||||
StopCoroutine(coroutinePostLaunch);
|
||||
@@ -1521,6 +1645,16 @@ static class DedicatedMatschInternalApi
|
||||
return BuildPatchJson(url, secret, jsonBody);
|
||||
}
|
||||
|
||||
public static string BuildWinnerPatchJson(string winnerLower, bool redConnected, bool blueConnected)
|
||||
{
|
||||
return JsonUtility.ToJson(new DedicatedWinnerPatchRequest
|
||||
{
|
||||
winner = winnerLower,
|
||||
red_connected = redConnected,
|
||||
blue_connected = blueConnected
|
||||
});
|
||||
}
|
||||
|
||||
public static UnityWebRequest BuildRematchRequest(string baseUrl, string secret, string jsonBody)
|
||||
{
|
||||
string url = baseUrl.TrimEnd('/') + "/internal/rematch";
|
||||
@@ -1570,7 +1704,13 @@ static class DedicatedMatschInternalApi
|
||||
{
|
||||
var parsed = JsonUtility.FromJson<DedicatedWinnerPatchResponse>(body);
|
||||
if (parsed != null && parsed.ok && parsed.economy != null)
|
||||
{
|
||||
GameManager.MatchRcPrizeCoins = parsed.economy.rc_prize;
|
||||
Logger.Log("Dedicated winner PATCH economy: rc_prize=" + parsed.economy.rc_prize
|
||||
+ " participant_cc=" + parsed.economy.participant_cc
|
||||
+ " cc_awarded_red=" + parsed.economy.cc_awarded_red
|
||||
+ " cc_awarded_blue=" + parsed.economy.cc_awarded_blue);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -1602,12 +1742,22 @@ class DedicatedJoinPatchResponse
|
||||
public int escrow_user_id;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
class DedicatedWinnerPatchRequest
|
||||
{
|
||||
public string winner;
|
||||
public bool red_connected;
|
||||
public bool blue_connected;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
class DedicatedWinnerPatchEconomy
|
||||
{
|
||||
public int entry_fee_rc;
|
||||
public int rc_prize;
|
||||
public int participant_cc;
|
||||
public bool cc_awarded_red;
|
||||
public bool cc_awarded_blue;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
|
||||
@@ -455,8 +455,10 @@ public class GameOverCanvas : MonoBehaviour
|
||||
|
||||
yield return new WaitForSeconds(scaleTime * 0.5f);
|
||||
|
||||
//Rewards
|
||||
float ccReward = GameTutorialManager.tutorialModeEnabled ? 0f : 100f;
|
||||
//Rewards — participation CC only if this client stayed connected through match end
|
||||
float ccReward = GameTutorialManager.tutorialModeEnabled || !GameManager.LocalPlayerEarnedParticipationCc
|
||||
? 0f
|
||||
: GameManager.ParticipationCcAmount;
|
||||
StartCoroutine(CoroutineSetTextNumber(rcPrize, txtRewardRC, 10, "N1", "+", " RC"));
|
||||
StartCoroutine(CoroutineSetTextNumber(ccReward, txtRewardCC, 100, "N0", "+", " CC"));
|
||||
|
||||
@@ -533,6 +535,22 @@ public class GameOverCanvas : MonoBehaviour
|
||||
countdownCoroutine = StartCoroutine(CoroutineCountdown());
|
||||
}
|
||||
|
||||
public void OnRematchFailed(string errorMessage)
|
||||
{
|
||||
btnRematch.interactable = true;
|
||||
btnLeave.interactable = true;
|
||||
if (rematchWarningLoser != null)
|
||||
rematchWarningLoser.text = "Rematch failed.";
|
||||
StartCoroutine(CoroutineHideRematchPanel());
|
||||
|
||||
if (countdownCoroutine != null)
|
||||
StopCoroutine(countdownCoroutine);
|
||||
countdownCoroutine = StartCoroutine(CoroutineCountdown());
|
||||
|
||||
string body = string.IsNullOrEmpty(errorMessage) ? "Could not start a rematch." : errorMessage;
|
||||
MessageBoxDialog.Show("Rematch failed", body);
|
||||
}
|
||||
|
||||
IEnumerator CoroutineHideRematchPanel(){
|
||||
rematchPopup.DOScale(0, 0.2f).SetEase(Ease.InBack);
|
||||
yield return new WaitForSeconds(0.2f);
|
||||
|
||||
@@ -32,6 +32,8 @@ public class Goal : NetworkBehaviour
|
||||
Debug.Log(ball.name);
|
||||
GameManager.instance.OnGoal(team);
|
||||
}else if(collision.gameObject.CompareTag("Puck")){
|
||||
if(GameManager.instance == null || !GameManager.instance.CanProcessGoal)
|
||||
return;
|
||||
Debug.Log("Puck goal", gameObject);
|
||||
Puck puck = collision.gameObject.GetComponent<Puck>();
|
||||
Debug.Log(puck.name);
|
||||
|
||||
@@ -21,6 +21,7 @@ public class LoginManager : MonoBehaviour
|
||||
const int MAX_USERNAME_LENGTH = 16;
|
||||
const int MIN_PASSWORD_LENGTH = 8;
|
||||
const int MAX_PASSWORD_LENGTH = 20;
|
||||
const int ShortAuthMessageMaxLength = 36;
|
||||
|
||||
[Header("Login")]
|
||||
[SerializeField] private TMP_InputField usernameInputLogin;
|
||||
@@ -33,9 +34,6 @@ public class LoginManager : MonoBehaviour
|
||||
[SerializeField] private TMP_InputField passwordInputRegister;
|
||||
[SerializeField] private Button btnRegister;
|
||||
|
||||
[Header("UI")]
|
||||
[SerializeField] private TMP_Text error_txt;
|
||||
|
||||
[Header("Server messages (GET /table_settings, no auth)")]
|
||||
[Header("Update")]
|
||||
[SerializeField] private GameObject serverUpdatePanel;
|
||||
@@ -60,6 +58,7 @@ public class LoginManager : MonoBehaviour
|
||||
bool _appPaused;
|
||||
bool _appFocused = true;
|
||||
bool _handlingKeepaliveUnauthorized;
|
||||
string _pendingAuthMessage;
|
||||
|
||||
public static string AuthToken { get; private set; }
|
||||
|
||||
@@ -110,7 +109,6 @@ public class LoginManager : MonoBehaviour
|
||||
|
||||
void Awake()
|
||||
{
|
||||
error_txt.text = "";
|
||||
HideServerMessagePanels();
|
||||
AuthToken = PlayerPrefs.GetString(PlayerPrefsAuthKey, "");
|
||||
|
||||
@@ -120,6 +118,7 @@ public class LoginManager : MonoBehaviour
|
||||
}
|
||||
instance = this;
|
||||
DontDestroyOnLoad(gameObject);
|
||||
SceneManager.sceneLoaded += OnSceneLoaded;
|
||||
|
||||
if (string.IsNullOrEmpty(AuthToken))
|
||||
ClearLocalUserProfile();
|
||||
@@ -217,8 +216,7 @@ public class LoginManager : MonoBehaviour
|
||||
else
|
||||
{
|
||||
InvalidateStoredSession();
|
||||
if (error_txt != null)
|
||||
error_txt.text = string.IsNullOrEmpty(errMsg) ? "Session expired. Please sign in again." : errMsg;
|
||||
ShowAuthMessage(string.IsNullOrEmpty(errMsg) ? "Session expired. Please sign in again." : errMsg);
|
||||
}
|
||||
|
||||
SetBusy(false);
|
||||
@@ -269,6 +267,40 @@ public class LoginManager : MonoBehaviour
|
||||
if (btnRegister != null) btnRegister.interactable = !busy;
|
||||
}
|
||||
|
||||
void ShowAuthMessage(string message)
|
||||
{
|
||||
if (string.IsNullOrEmpty(message))
|
||||
return;
|
||||
|
||||
if (MessageBoxDialog.instance == null)
|
||||
{
|
||||
_pendingAuthMessage = message;
|
||||
return;
|
||||
}
|
||||
|
||||
_pendingAuthMessage = null;
|
||||
if (message.Length <= ShortAuthMessageMaxLength)
|
||||
MessageBoxDialog.instance.ShowMessageBox(message);
|
||||
else
|
||||
MessageBoxDialog.instance.ShowMessageBox("Error", message);
|
||||
}
|
||||
|
||||
void HideAuthMessage()
|
||||
{
|
||||
if (MessageBoxDialog.instance != null)
|
||||
MessageBoxDialog.instance.HideMessageBox();
|
||||
}
|
||||
|
||||
void OnSceneLoaded(Scene scene, LoadSceneMode mode)
|
||||
{
|
||||
if (string.IsNullOrEmpty(_pendingAuthMessage) || MessageBoxDialog.instance == null)
|
||||
return;
|
||||
|
||||
string message = _pendingAuthMessage;
|
||||
_pendingAuthMessage = null;
|
||||
ShowAuthMessage(message);
|
||||
}
|
||||
|
||||
void OnLogin()
|
||||
{
|
||||
if (_authInProgress) return;
|
||||
@@ -278,19 +310,19 @@ public class LoginManager : MonoBehaviour
|
||||
string password = passwordInputLogin.text;
|
||||
if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password))
|
||||
{
|
||||
error_txt.text = "Please fill in all fields";
|
||||
ShowAuthMessage("Please fill in all fields");
|
||||
return;
|
||||
}
|
||||
|
||||
if (username.Length < MIN_USERNAME_LENGTH || username.Length > MAX_USERNAME_LENGTH)
|
||||
{
|
||||
error_txt.text = "Username must be between " + MIN_USERNAME_LENGTH + " and " + MAX_USERNAME_LENGTH + " characters";
|
||||
ShowAuthMessage("Username must be between " + MIN_USERNAME_LENGTH + " and " + MAX_USERNAME_LENGTH + " characters");
|
||||
return;
|
||||
}
|
||||
|
||||
if (password.Length < MIN_PASSWORD_LENGTH || password.Length > MAX_PASSWORD_LENGTH)
|
||||
{
|
||||
error_txt.text = "Password must be between " + MIN_PASSWORD_LENGTH + " and " + MAX_PASSWORD_LENGTH + " characters";
|
||||
ShowAuthMessage("Password must be between " + MIN_PASSWORD_LENGTH + " and " + MAX_PASSWORD_LENGTH + " characters");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -308,26 +340,26 @@ public class LoginManager : MonoBehaviour
|
||||
string password = passwordInputRegister.text;
|
||||
if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(email) || string.IsNullOrEmpty(password))
|
||||
{
|
||||
error_txt.text = "Please fill in all fields";
|
||||
ShowAuthMessage("Please fill in all fields");
|
||||
return;
|
||||
}
|
||||
|
||||
email = email.Trim().ToLowerInvariant();
|
||||
if (!IsValidEmailFormat(email))
|
||||
{
|
||||
error_txt.text = "Please enter a valid email address";
|
||||
ShowAuthMessage("Please enter a valid email address");
|
||||
return;
|
||||
}
|
||||
|
||||
if (username.Length < MIN_USERNAME_LENGTH || username.Length > MAX_USERNAME_LENGTH)
|
||||
{
|
||||
error_txt.text = "Username must be between " + MIN_USERNAME_LENGTH + " and " + MAX_USERNAME_LENGTH + " characters";
|
||||
ShowAuthMessage("Username must be between " + MIN_USERNAME_LENGTH + " and " + MAX_USERNAME_LENGTH + " characters");
|
||||
return;
|
||||
}
|
||||
|
||||
if (password.Length < MIN_PASSWORD_LENGTH || password.Length > MAX_PASSWORD_LENGTH)
|
||||
{
|
||||
error_txt.text = "Password must be between " + MIN_PASSWORD_LENGTH + " and " + MAX_PASSWORD_LENGTH + " characters";
|
||||
ShowAuthMessage("Password must be between " + MIN_PASSWORD_LENGTH + " and " + MAX_PASSWORD_LENGTH + " characters");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -370,7 +402,7 @@ public class LoginManager : MonoBehaviour
|
||||
IEnumerator AuthPostCoroutine(string url, string jsonBody, string successMessage)
|
||||
{
|
||||
SetBusy(true);
|
||||
error_txt.text = "";
|
||||
HideAuthMessage();
|
||||
ClearLocalUserProfile();
|
||||
|
||||
using (var request = new UnityWebRequest(url, UnityWebRequest.kHttpVerbPOST))
|
||||
@@ -387,14 +419,14 @@ public class LoginManager : MonoBehaviour
|
||||
if (request.result != UnityWebRequest.Result.Success &&
|
||||
request.result != UnityWebRequest.Result.ProtocolError)
|
||||
{
|
||||
error_txt.text = string.IsNullOrEmpty(request.error) ? "Network error" : request.error;
|
||||
ShowAuthMessage(string.IsNullOrEmpty(request.error) ? "Network error" : request.error);
|
||||
SetBusy(false);
|
||||
yield break;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(text))
|
||||
{
|
||||
error_txt.text = "Empty response from server";
|
||||
ShowAuthMessage("Empty response from server");
|
||||
SetBusy(false);
|
||||
yield break;
|
||||
}
|
||||
@@ -418,14 +450,14 @@ public class LoginManager : MonoBehaviour
|
||||
|
||||
if (profileOk)
|
||||
ProceedToMainMenu();
|
||||
else if (error_txt != null)
|
||||
error_txt.text = string.IsNullOrEmpty(profileErr)
|
||||
else
|
||||
ShowAuthMessage(string.IsNullOrEmpty(profileErr)
|
||||
? "Signed in but could not load your profile. Check your connection and try again."
|
||||
: profileErr;
|
||||
: profileErr);
|
||||
}
|
||||
else
|
||||
{
|
||||
error_txt.text = string.IsNullOrEmpty(resp.error) ? "Request failed" : resp.error;
|
||||
ShowAuthMessage(string.IsNullOrEmpty(resp.error) ? "Request failed" : resp.error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -847,7 +879,10 @@ public class LoginManager : MonoBehaviour
|
||||
void OnDestroy()
|
||||
{
|
||||
if (instance == this)
|
||||
{
|
||||
SceneManager.sceneLoaded -= OnSceneLoaded;
|
||||
StopKeepalive();
|
||||
}
|
||||
}
|
||||
|
||||
void StartKeepalive()
|
||||
@@ -947,8 +982,7 @@ public class LoginManager : MonoBehaviour
|
||||
InvalidateStoredSession();
|
||||
SetBusy(false);
|
||||
|
||||
if (error_txt != null)
|
||||
error_txt.text = string.IsNullOrEmpty(message) ? "Session expired. Please sign in again." : message;
|
||||
ShowAuthMessage(string.IsNullOrEmpty(message) ? "Session expired. Please sign in again." : message);
|
||||
|
||||
Scene active = SceneManager.GetActiveScene();
|
||||
if (active.buildIndex != 0 && active.name != "Intro")
|
||||
|
||||
@@ -141,6 +141,24 @@ public class MainMenuManager : MonoBehaviour
|
||||
}
|
||||
|
||||
void OnLogout()
|
||||
{
|
||||
if (MessageBoxDialog.IsAvailable)
|
||||
{
|
||||
MessageBoxDialog.Show(
|
||||
"Log out?",
|
||||
"You will need to sign in again to play.",
|
||||
confirmed =>
|
||||
{
|
||||
if (confirmed)
|
||||
ConfirmLogout();
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
ConfirmLogout();
|
||||
}
|
||||
|
||||
void ConfirmLogout()
|
||||
{
|
||||
LoginManager.Logout();
|
||||
SceneManager.LoadScene(0);
|
||||
@@ -165,7 +183,17 @@ public class MainMenuManager : MonoBehaviour
|
||||
}
|
||||
if (LoginManager.CurrentUser.rc < _matchEntryFeeCoins)
|
||||
{
|
||||
Debug.LogError("Not enough RC to play");
|
||||
string fee = CoinHelper.FormatString(_matchEntryFeeCoins) + " RC";
|
||||
if (MessageBoxDialog.IsAvailable)
|
||||
{
|
||||
MessageBoxDialog.Show(
|
||||
"Not enough RC",
|
||||
"You need " + fee + " to play. Buy more RC and try again.");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("Not enough RC to play");
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (CupidLobby.instance == null)
|
||||
@@ -285,16 +313,10 @@ public class MainMenuManager : MonoBehaviour
|
||||
ccText.text = user.cc.ToString("N0") + " CC";
|
||||
rcText.text = CoinHelper.FormatString(user.rc) + " RC";
|
||||
|
||||
if (LoginManager.CurrentUser.rc < _matchEntryFeeCoins)
|
||||
{
|
||||
rcPlayText.text = "Not enough RC to play";
|
||||
btnPlay.interactable = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (rcPlayText != null)
|
||||
rcPlayText.text = "";
|
||||
if (btnPlay != null)
|
||||
btnPlay.interactable = true;
|
||||
}
|
||||
|
||||
UpdateWithdrawalStatus(user);
|
||||
}
|
||||
|
||||
@@ -486,6 +486,14 @@ public class NetPlayer : NetworkBehaviour
|
||||
Logger.Log(matchmadeResponseString);
|
||||
StartCoroutine(CoroutineOnRematchConfirmedServer());
|
||||
}
|
||||
else
|
||||
{
|
||||
string error = string.IsNullOrEmpty(resp.ErrorMessage)
|
||||
? "Could not start a rematch."
|
||||
: resp.ErrorMessage;
|
||||
Logger.Log("Rematch failed: " + error);
|
||||
RpcRematchFailed(error);
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator CoroutineOnRematchConfirmedServer(){
|
||||
@@ -545,6 +553,15 @@ public class NetPlayer : NetworkBehaviour
|
||||
LevelLoadManager.instance.Leave("Game");
|
||||
}
|
||||
|
||||
[ClientRpc]
|
||||
void RpcRematchFailed(string errorMessage)
|
||||
{
|
||||
if (GameOverCanvas.instance != null)
|
||||
GameOverCanvas.instance.OnRematchFailed(errorMessage);
|
||||
else
|
||||
MessageBoxDialog.Show("Rematch failed", string.IsNullOrEmpty(errorMessage) ? "Could not start a rematch." : errorMessage);
|
||||
}
|
||||
|
||||
void OnRematchCancelledServer(){
|
||||
if(!isServer){ return; }
|
||||
|
||||
|
||||
@@ -154,6 +154,7 @@ public class Puck : NetworkBehaviour
|
||||
bool holdingCollisionLock;
|
||||
public void Reset(Vector3? position = null, float duration = 0.5f)
|
||||
{
|
||||
UnscheduleReset();
|
||||
Vector3 pos = position ?? startPosition;
|
||||
if (coroutineReset != null)
|
||||
{
|
||||
@@ -175,20 +176,38 @@ public class Puck : NetworkBehaviour
|
||||
}
|
||||
Coroutine coroutineScheduleReset;
|
||||
public void ScheduleReset(Vector3 position, float duration = 0.5f){
|
||||
UnscheduleReset();
|
||||
coroutineScheduleReset = StartCoroutine(CoroutineScheduleReset(position, duration));
|
||||
}
|
||||
|
||||
public void UnscheduleReset(){
|
||||
if(coroutineScheduleReset != null){
|
||||
StopCoroutine(coroutineScheduleReset);
|
||||
coroutineScheduleReset = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void CancelInGoalReset()
|
||||
{
|
||||
UnscheduleReset();
|
||||
if (coroutineReset != null)
|
||||
{
|
||||
StopCoroutine(coroutineReset);
|
||||
RestoreAfterReset();
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator CoroutineScheduleReset(Vector3 position, float duration){
|
||||
while(GameManager.isMoving){
|
||||
if (GameManager.instance != null && !GameManager.instance.CanProcessGoal)
|
||||
yield break;
|
||||
yield return null;
|
||||
}
|
||||
|
||||
if (GameManager.instance != null && !GameManager.instance.CanProcessGoal)
|
||||
yield break;
|
||||
|
||||
coroutineScheduleReset = null;
|
||||
Debug.Log("Resetting puck", gameObject);
|
||||
Vector3 dir = position - transform.position;
|
||||
Vector3 centerDir = Vector3.zero - transform.position;
|
||||
|
||||
@@ -0,0 +1,229 @@
|
||||
using System;
|
||||
using DG.Tweening;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
using UnityEngine.UI;
|
||||
|
||||
public class MessageBoxDialog : MonoBehaviour
|
||||
{
|
||||
public const float ANIMATION_DURATION = 0.25f;
|
||||
/// <summary>Above game/menu/loader canvases; below reconnect (500).</summary>
|
||||
const int OverlaySortingOrder = 400;
|
||||
|
||||
public static MessageBoxDialog instance;
|
||||
public static bool IsAvailable => instance != null;
|
||||
|
||||
public RectTransform dimmer;
|
||||
public RectTransform basicMessageBox;
|
||||
|
||||
[Header("MessageBox")]
|
||||
public RectTransform messageBox;
|
||||
public TMP_Text txtTitle, txtMessage;
|
||||
public Button btnOk, btnYes, btnNo;
|
||||
|
||||
Image dimmerImg;
|
||||
TMP_Text basicMessageText;
|
||||
Action<bool> onYesOrNo;
|
||||
Sequence showHideSequence;
|
||||
Canvas overlayCanvas;
|
||||
|
||||
void Awake()
|
||||
{
|
||||
if (instance != null && instance != this)
|
||||
{
|
||||
Destroy(gameObject);
|
||||
return;
|
||||
}
|
||||
|
||||
instance = this;
|
||||
DontDestroyOnLoad(gameObject);
|
||||
|
||||
overlayCanvas = GetComponent<Canvas>();
|
||||
if (overlayCanvas != null)
|
||||
overlayCanvas.sortingOrder = OverlaySortingOrder;
|
||||
|
||||
if (dimmer != null)
|
||||
dimmerImg = dimmer.GetComponent<Image>();
|
||||
if (basicMessageBox != null)
|
||||
basicMessageText = basicMessageBox.GetComponentInChildren<TMP_Text>();
|
||||
}
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
if (instance != this)
|
||||
return;
|
||||
SceneManager.sceneLoaded += OnSceneLoaded;
|
||||
}
|
||||
|
||||
void OnDisable()
|
||||
{
|
||||
if (instance != this)
|
||||
return;
|
||||
SceneManager.sceneLoaded -= OnSceneLoaded;
|
||||
}
|
||||
|
||||
void OnSceneLoaded(Scene scene, LoadSceneMode mode)
|
||||
{
|
||||
transform.SetAsLastSibling();
|
||||
}
|
||||
|
||||
public static void ShowBasic(string title)
|
||||
{
|
||||
if (instance == null)
|
||||
{
|
||||
Debug.LogWarning("MessageBoxDialog missing: " + title);
|
||||
return;
|
||||
}
|
||||
|
||||
instance.ShowMessageBox(title);
|
||||
}
|
||||
|
||||
public static void Show(string title, string message, Action<bool> onResult = null)
|
||||
{
|
||||
if (instance == null)
|
||||
{
|
||||
Debug.LogWarning("MessageBoxDialog missing: " + title);
|
||||
return;
|
||||
}
|
||||
|
||||
instance.ShowMessageBox(title, message, onResult);
|
||||
}
|
||||
|
||||
void OnDestroy()
|
||||
{
|
||||
KillTweens();
|
||||
if (instance == this)
|
||||
instance = null;
|
||||
}
|
||||
|
||||
void Start()
|
||||
{
|
||||
Button basicButton = basicMessageBox != null ? basicMessageBox.GetComponent<Button>() : null;
|
||||
if (basicButton != null)
|
||||
basicButton.onClick.AddListener(HideMessageBox);
|
||||
|
||||
if (btnOk != null)
|
||||
btnOk.onClick.AddListener(HideMessageBox);
|
||||
if (btnYes != null)
|
||||
btnYes.onClick.AddListener(() => OnQuestionDialog(true));
|
||||
if (btnNo != null)
|
||||
btnNo.onClick.AddListener(() => OnQuestionDialog(false));
|
||||
}
|
||||
|
||||
void OnQuestionDialog(bool val)
|
||||
{
|
||||
Action<bool> callback = onYesOrNo;
|
||||
onYesOrNo = null;
|
||||
HideMessageBox();
|
||||
callback?.Invoke(val);
|
||||
}
|
||||
|
||||
public void ShowMessageBox(string title)
|
||||
{
|
||||
PrepareShow();
|
||||
HideBoxImmediate(messageBox);
|
||||
|
||||
if (basicMessageText != null)
|
||||
basicMessageText.text = title;
|
||||
|
||||
AnimateIn(basicMessageBox);
|
||||
}
|
||||
|
||||
public void ShowMessageBox(string title, string message, Action<bool> onResult = null)
|
||||
{
|
||||
PrepareShow();
|
||||
HideBoxImmediate(basicMessageBox);
|
||||
|
||||
txtTitle.text = title;
|
||||
txtMessage.text = message;
|
||||
|
||||
bool isQuestion = onResult != null;
|
||||
btnNo.gameObject.SetActive(isQuestion);
|
||||
btnYes.gameObject.SetActive(isQuestion);
|
||||
btnOk.gameObject.SetActive(!isQuestion);
|
||||
|
||||
onYesOrNo = onResult;
|
||||
AnimateIn(messageBox);
|
||||
}
|
||||
|
||||
public void HideMessageBox()
|
||||
{
|
||||
bool basicActive = basicMessageBox != null && basicMessageBox.gameObject.activeSelf;
|
||||
bool fullActive = messageBox != null && messageBox.gameObject.activeSelf;
|
||||
if (!basicActive && !fullActive)
|
||||
return;
|
||||
|
||||
if (basicActive && fullActive)
|
||||
HideBoxImmediate(basicMessageBox);
|
||||
|
||||
AnimateOut(basicActive && !fullActive ? basicMessageBox : messageBox);
|
||||
}
|
||||
|
||||
void PrepareShow()
|
||||
{
|
||||
KillTweens();
|
||||
onYesOrNo = null;
|
||||
transform.SetAsLastSibling();
|
||||
|
||||
dimmer.gameObject.SetActive(true);
|
||||
if (dimmerImg != null)
|
||||
{
|
||||
Color c = dimmerImg.color;
|
||||
dimmerImg.color = new Color(c.r, c.g, c.b, 0f);
|
||||
}
|
||||
}
|
||||
|
||||
void HideBoxImmediate(RectTransform box)
|
||||
{
|
||||
if (box == null)
|
||||
return;
|
||||
|
||||
box.DOKill();
|
||||
box.localScale = Vector3.zero;
|
||||
box.gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
void AnimateIn(RectTransform box)
|
||||
{
|
||||
box.localScale = Vector3.zero;
|
||||
box.gameObject.SetActive(true);
|
||||
|
||||
showHideSequence = DOTween.Sequence().SetUpdate(true);
|
||||
if (dimmerImg != null)
|
||||
showHideSequence.Join(dimmerImg.DOFade(0.8f, ANIMATION_DURATION));
|
||||
showHideSequence.Join(box.DOScale(1f, ANIMATION_DURATION).SetEase(Ease.OutBack));
|
||||
}
|
||||
|
||||
void AnimateOut(RectTransform box)
|
||||
{
|
||||
KillTweens();
|
||||
|
||||
RectTransform targetBox = box;
|
||||
showHideSequence = DOTween.Sequence().SetUpdate(true);
|
||||
if (dimmerImg != null)
|
||||
showHideSequence.Join(dimmerImg.DOFade(0f, ANIMATION_DURATION));
|
||||
showHideSequence.Join(targetBox.DOScale(0f, ANIMATION_DURATION).SetEase(Ease.InBack));
|
||||
showHideSequence.OnComplete(() =>
|
||||
{
|
||||
if (targetBox != null)
|
||||
targetBox.gameObject.SetActive(false);
|
||||
if (dimmer != null)
|
||||
dimmer.gameObject.SetActive(false);
|
||||
});
|
||||
}
|
||||
|
||||
void KillTweens()
|
||||
{
|
||||
if (showHideSequence != null && showHideSequence.IsActive())
|
||||
showHideSequence.Kill();
|
||||
showHideSequence = null;
|
||||
|
||||
if (dimmerImg != null)
|
||||
dimmerImg.DOKill();
|
||||
if (basicMessageBox != null)
|
||||
basicMessageBox.DOKill();
|
||||
if (messageBox != null)
|
||||
messageBox.DOKill();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c50f3cfa5a1c65b4c8b0a011d602717a
|
||||
Reference in New Issue
Block a user