This commit is contained in:
2026-08-20 19:45:38 +05:30
parent 74d9fae6e6
commit 78592e8d8e
87 changed files with 13154 additions and 223 deletions
+58 -2
View File
@@ -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
View File
@@ -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]
+20 -2
View File
@@ -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);
+2
View File
@@ -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);
+56 -22
View File
@@ -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")
+31 -9
View File
@@ -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);
}
+17
View File
@@ -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; }
+19
View File
@@ -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;
+229
View File
@@ -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