sync
This commit is contained in:
@@ -45,6 +45,20 @@ public class GameOverCanvas : MonoBehaviour
|
||||
/// <summary>Each player needs at least 2.0 RC (20 coin units) to rematch.</summary>
|
||||
const int MinimumRematchRcCoins = 20;
|
||||
|
||||
[Header("Bet Limit")]
|
||||
public TMP_Text txtBetLimitWarning;
|
||||
public CanvasGroup betLimitAdjustPanel;
|
||||
//minVal: 2, max Val 500, when its 500, show ∞ on the text
|
||||
public Slider betLimitSlider;
|
||||
public TMP_Text txtBetLimitSliderValue;
|
||||
public Button btnBetLimitAdjustAccept;
|
||||
public Button btnBetLimitAdjustCancel;
|
||||
|
||||
const string PersonalBetLimitPrefsKey = "WalkInvest.PersonalBetLimit";
|
||||
const int BetLimitMin = 2;
|
||||
const int BetLimitMax = 500;
|
||||
const int BetLimitUnlimited = 500;
|
||||
|
||||
[Header("Rematch panel")]
|
||||
public CanvasGroup rematchPanel;
|
||||
public RectTransform rematchPopup;
|
||||
@@ -85,6 +99,107 @@ public class GameOverCanvas : MonoBehaviour
|
||||
|
||||
btnConfirmBet.onClick.AddListener(OnBetAccepted);
|
||||
btnCancelBet.onClick.AddListener(OnBetCancelled);
|
||||
|
||||
SetupBetLimitUi();
|
||||
}
|
||||
|
||||
void SetupBetLimitUi()
|
||||
{
|
||||
if (betLimitSlider != null)
|
||||
{
|
||||
betLimitSlider.minValue = BetLimitMin;
|
||||
betLimitSlider.maxValue = BetLimitMax;
|
||||
betLimitSlider.wholeNumbers = true;
|
||||
betLimitSlider.onValueChanged.AddListener(OnBetLimitSliderValueChanged);
|
||||
}
|
||||
|
||||
if (btnBetLimitAdjustAccept != null)
|
||||
btnBetLimitAdjustAccept.onClick.AddListener(OnBetLimitAdjustAccepted);
|
||||
if (btnBetLimitAdjustCancel != null)
|
||||
btnBetLimitAdjustCancel.onClick.AddListener(CloseBetLimitAdjuster);
|
||||
|
||||
var warningButton = txtBetLimitWarning != null
|
||||
? txtBetLimitWarning.GetComponentInChildren<Button>()
|
||||
: null;
|
||||
if (warningButton != null)
|
||||
warningButton.onClick.AddListener(OpenBetLimitAdjuster);
|
||||
}
|
||||
|
||||
public static int GetPersonalBetLimit()
|
||||
{
|
||||
return PlayerPrefs.GetInt(PersonalBetLimitPrefsKey, BetLimitUnlimited);
|
||||
}
|
||||
|
||||
static void SetPersonalBetLimit(int limitCoins)
|
||||
{
|
||||
limitCoins = Mathf.Clamp(limitCoins, BetLimitMin, BetLimitMax);
|
||||
PlayerPrefs.SetInt(PersonalBetLimitPrefsKey, limitCoins);
|
||||
PlayerPrefs.Save();
|
||||
}
|
||||
|
||||
static string FormatBetLimitLabel(int limitCoins)
|
||||
{
|
||||
if (limitCoins >= BetLimitUnlimited)
|
||||
return "∞";
|
||||
return CoinHelper.FormatString(limitCoins) + " RC";
|
||||
}
|
||||
|
||||
static string FormatBetLimitWarningText(int limitCoins) =>
|
||||
"Max Bet : " + FormatBetLimitLabel(limitCoins);
|
||||
|
||||
void UpdateBetLimitWarningText()
|
||||
{
|
||||
if (txtBetLimitWarning != null)
|
||||
txtBetLimitWarning.text = FormatBetLimitWarningText(GetPersonalBetLimit());
|
||||
}
|
||||
|
||||
void OnBetLimitSliderValueChanged(float value)
|
||||
{
|
||||
if (txtBetLimitSliderValue != null)
|
||||
txtBetLimitSliderValue.text = FormatBetLimitLabel(Mathf.RoundToInt(value));
|
||||
}
|
||||
|
||||
void ApplyBetLimitVisibility()
|
||||
{
|
||||
bool isLoser = myTeam != winningTeam;
|
||||
if (txtBetLimitWarning != null)
|
||||
txtBetLimitWarning.gameObject.SetActive(isLoser);
|
||||
if (!isLoser)
|
||||
return;
|
||||
UpdateBetLimitWarningText();
|
||||
}
|
||||
|
||||
public void OpenBetLimitAdjuster()
|
||||
{
|
||||
if (betLimitAdjustPanel == null)
|
||||
return;
|
||||
|
||||
int limit = GetPersonalBetLimit();
|
||||
if (betLimitSlider != null)
|
||||
betLimitSlider.SetValueWithoutNotify(limit);
|
||||
OnBetLimitSliderValueChanged(limit);
|
||||
|
||||
betLimitAdjustPanel.DOFade(1, 0.25f);
|
||||
betLimitAdjustPanel.blocksRaycasts = true;
|
||||
betLimitAdjustPanel.interactable = true;
|
||||
}
|
||||
|
||||
public void CloseBetLimitAdjuster()
|
||||
{
|
||||
if (betLimitAdjustPanel == null)
|
||||
return;
|
||||
|
||||
betLimitAdjustPanel.DOFade(0, 0.25f);
|
||||
betLimitAdjustPanel.blocksRaycasts = false;
|
||||
betLimitAdjustPanel.interactable = false;
|
||||
}
|
||||
|
||||
void OnBetLimitAdjustAccepted()
|
||||
{
|
||||
if (betLimitSlider != null)
|
||||
SetPersonalBetLimit(Mathf.RoundToInt(betLimitSlider.value));
|
||||
UpdateBetLimitWarningText();
|
||||
CloseBetLimitAdjuster();
|
||||
}
|
||||
|
||||
void OnBetAccepted(){
|
||||
@@ -181,6 +296,13 @@ public class GameOverCanvas : MonoBehaviour
|
||||
rematchPanel.alpha=0;
|
||||
rematchPanel.blocksRaycasts = false;
|
||||
rematchPanel.interactable = false;
|
||||
|
||||
if (betLimitAdjustPanel != null)
|
||||
{
|
||||
betLimitAdjustPanel.alpha = 0;
|
||||
betLimitAdjustPanel.blocksRaycasts = false;
|
||||
betLimitAdjustPanel.interactable = false;
|
||||
}
|
||||
}
|
||||
|
||||
Team winningTeam;
|
||||
@@ -220,6 +342,8 @@ public class GameOverCanvas : MonoBehaviour
|
||||
p1_winner.gameObject.SetActive(false);
|
||||
p2_winner.gameObject.SetActive(true);
|
||||
}
|
||||
|
||||
ApplyBetLimitVisibility();
|
||||
}
|
||||
|
||||
float rcPrize = 0;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
#if UNITY_EDITOR
|
||||
@@ -27,12 +28,31 @@ public class LoginManager : MonoBehaviour
|
||||
[SerializeField] private Button btnLogin;
|
||||
[Header("Register")]
|
||||
[SerializeField] private TMP_InputField usernameInputRegister;
|
||||
[SerializeField] private TMP_InputField emailInputRegister;
|
||||
|
||||
[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;
|
||||
[SerializeField] private Button btnServerUpdate;
|
||||
[Tooltip("Opened when the user taps Update on the forced-update panel (e.g. Telegram channel).")]
|
||||
public string telegramUpdateChannelUrl = "";
|
||||
|
||||
[Header("Error")]
|
||||
[SerializeField] private GameObject errorPanel;
|
||||
[SerializeField] private TMP_Text serverErrorMessageTxt;
|
||||
[SerializeField] private Button btnErrorClose;
|
||||
|
||||
[Header("Warning")]
|
||||
[SerializeField] private GameObject warningPanel;
|
||||
[SerializeField] private TMP_Text serverWarningMessageTxt;
|
||||
[SerializeField] private Button btnWarningClose;
|
||||
|
||||
bool _authInProgress;
|
||||
|
||||
public static string AuthToken { get; private set; }
|
||||
@@ -85,6 +105,7 @@ public class LoginManager : MonoBehaviour
|
||||
void Awake()
|
||||
{
|
||||
error_txt.text = "";
|
||||
HideServerMessagePanels();
|
||||
AuthToken = PlayerPrefs.GetString(PlayerPrefsAuthKey, "");
|
||||
|
||||
if(instance !=null){
|
||||
@@ -102,6 +123,26 @@ public class LoginManager : MonoBehaviour
|
||||
{
|
||||
btnLogin.onClick.AddListener(OnLogin);
|
||||
btnRegister.onClick.AddListener(OnRegister);
|
||||
if (btnServerUpdate != null)
|
||||
btnServerUpdate.onClick.AddListener(OnServerUpdateClick);
|
||||
if (btnErrorClose != null)
|
||||
btnErrorClose.onClick.AddListener(OnServerErrorCloseClick);
|
||||
|
||||
SetBusy(true);
|
||||
StartCoroutine(StartupSequenceCoroutine());
|
||||
}
|
||||
|
||||
IEnumerator StartupSequenceCoroutine()
|
||||
{
|
||||
bool mayContinue = false;
|
||||
yield return StartCoroutine(CoApplyTableSettingsGate(r => mayContinue = r));
|
||||
|
||||
if (!mayContinue)
|
||||
{
|
||||
// Blocked by update or error panel — keep login UI disabled.
|
||||
SetBusy(false);
|
||||
yield break;
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
// Dev: log in with clone-aware credentials instead of resuming a saved session.
|
||||
@@ -117,12 +158,12 @@ public class LoginManager : MonoBehaviour
|
||||
|
||||
string loginJson = "{\"username\":\"" + EscapeJsonString(devUser) + "\",\"password\":\"" + EscapeJsonString(devPass) + "\"}";
|
||||
// StartCoroutine(AuthPostCoroutine(AuthBaseUrl + "/auth/login", loginJson, "Signed in."));
|
||||
SetBusy(false);
|
||||
#else
|
||||
if (!string.IsNullOrEmpty(AuthToken))
|
||||
{
|
||||
SetBusy(true);
|
||||
StartCoroutine(ResumeSessionCoroutine());
|
||||
}
|
||||
yield return StartCoroutine(ResumeSessionCoroutine());
|
||||
else
|
||||
SetBusy(false);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -231,13 +272,21 @@ public class LoginManager : MonoBehaviour
|
||||
if (_authInProgress) return;
|
||||
|
||||
string username = usernameInputRegister.text;
|
||||
string email = emailInputRegister != null ? emailInputRegister.text : "";
|
||||
string password = passwordInputRegister.text;
|
||||
if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password))
|
||||
if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(email) || string.IsNullOrEmpty(password))
|
||||
{
|
||||
error_txt.text = "Please fill in all fields";
|
||||
return;
|
||||
}
|
||||
|
||||
email = email.Trim().ToLowerInvariant();
|
||||
if (!IsValidEmailFormat(email))
|
||||
{
|
||||
error_txt.text = "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";
|
||||
@@ -250,11 +299,36 @@ public class LoginManager : MonoBehaviour
|
||||
return;
|
||||
}
|
||||
|
||||
// Server register endpoint accepts username + password only (email field is optional UI-only).
|
||||
string json = "{\"username\":\"" + EscapeJsonString(username) + "\",\"password\":\"" + EscapeJsonString(password) + "\"}";
|
||||
string json = "{\"username\":\"" + EscapeJsonString(username) + "\",\"email\":\"" + EscapeJsonString(email) + "\",\"password\":\"" + EscapeJsonString(password) + "\"}";
|
||||
StartCoroutine(AuthPostCoroutine(AuthBaseUrl + "/auth/register", json, "Account created."));
|
||||
}
|
||||
|
||||
static bool IsValidEmailFormat(string email)
|
||||
{
|
||||
if (string.IsNullOrEmpty(email))
|
||||
return false;
|
||||
|
||||
int at = email.IndexOf('@');
|
||||
if (at <= 0)
|
||||
return false;
|
||||
|
||||
if (email.IndexOf('@', at + 1) >= 0)
|
||||
return false;
|
||||
|
||||
string domain = email.Substring(at + 1);
|
||||
int dot = domain.LastIndexOf('.');
|
||||
if (dot <= 0 || dot >= domain.Length - 1)
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < email.Length; i++)
|
||||
{
|
||||
if (char.IsWhiteSpace(email[i]))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static string EscapeJsonString(string s)
|
||||
{
|
||||
if (string.IsNullOrEmpty(s)) return s;
|
||||
@@ -325,6 +399,149 @@ public class LoginManager : MonoBehaviour
|
||||
SetBusy(false);
|
||||
}
|
||||
|
||||
void HideServerMessagePanels()
|
||||
{
|
||||
if (serverUpdatePanel != null) serverUpdatePanel.SetActive(false);
|
||||
if (errorPanel != null) errorPanel.SetActive(false);
|
||||
if (warningPanel != null) warningPanel.SetActive(false);
|
||||
}
|
||||
|
||||
void OnServerUpdateClick()
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(telegramUpdateChannelUrl))
|
||||
Application.OpenURL(telegramUpdateChannelUrl.Trim());
|
||||
}
|
||||
|
||||
void OnServerErrorCloseClick()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
EditorApplication.ExitPlaymode();
|
||||
#else
|
||||
Application.Quit();
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// GET <c>/table_settings</c> (public) and applies version / error / warning gates.
|
||||
/// Callback receives <c>true</c> when login may continue to the main menu.
|
||||
/// </summary>
|
||||
IEnumerator CoApplyTableSettingsGate(Action<bool> onComplete)
|
||||
{
|
||||
CupidTableSettings table = null;
|
||||
yield return StartCoroutine(FetchTableSettingsCoroutine(t => table = t));
|
||||
|
||||
if (table == null)
|
||||
{
|
||||
onComplete?.Invoke(true);
|
||||
yield break;
|
||||
}
|
||||
|
||||
if (IsClientBelowRequiredVersion(table.version))
|
||||
{
|
||||
if (serverUpdatePanel != null)
|
||||
serverUpdatePanel.SetActive(true);
|
||||
onComplete?.Invoke(false);
|
||||
yield break;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(table.error_msg))
|
||||
{
|
||||
if (serverErrorMessageTxt != null)
|
||||
serverErrorMessageTxt.text = table.error_msg.Trim();
|
||||
if (errorPanel != null)
|
||||
errorPanel.SetActive(true);
|
||||
onComplete?.Invoke(false);
|
||||
yield break;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(table.warning_msg))
|
||||
yield return CoWaitForWarningDismiss(table.warning_msg.Trim());
|
||||
|
||||
onComplete?.Invoke(true);
|
||||
}
|
||||
|
||||
IEnumerator FetchTableSettingsCoroutine(Action<CupidTableSettings> onResult)
|
||||
{
|
||||
string url = AuthBaseUrl + "/table_settings";
|
||||
using (var request = UnityWebRequest.Get(url))
|
||||
{
|
||||
request.downloadHandler = new DownloadHandlerBuffer();
|
||||
yield return request.SendWebRequest();
|
||||
|
||||
string text = request.downloadHandler != null ? request.downloadHandler.text : "";
|
||||
|
||||
if (request.result != UnityWebRequest.Result.Success &&
|
||||
request.result != UnityWebRequest.Result.ProtocolError)
|
||||
{
|
||||
onResult?.Invoke(null);
|
||||
yield break;
|
||||
}
|
||||
|
||||
if (request.responseCode < 200 || request.responseCode >= 300 || string.IsNullOrWhiteSpace(text))
|
||||
{
|
||||
onResult?.Invoke(null);
|
||||
yield break;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
CupidTableSettings parsed = JsonUtility.FromJson<CupidTableSettings>(text);
|
||||
onResult?.Invoke(parsed);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Log("table_settings JSON error: " + e.Message + " body=" + text);
|
||||
onResult?.Invoke(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool IsClientBelowRequiredVersion(string requiredVersion)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(requiredVersion))
|
||||
return false;
|
||||
if (!TryParseVersionFloat(requiredVersion, out float required))
|
||||
return false;
|
||||
if (!TryParseVersionFloat(Application.version, out float client))
|
||||
return false;
|
||||
return client < required;
|
||||
}
|
||||
|
||||
static bool TryParseVersionFloat(string version, out float value)
|
||||
{
|
||||
value = 0f;
|
||||
if (string.IsNullOrWhiteSpace(version))
|
||||
return false;
|
||||
return float.TryParse(version.Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out value);
|
||||
}
|
||||
|
||||
IEnumerator CoWaitForWarningDismiss(string message)
|
||||
{
|
||||
if (serverWarningMessageTxt != null)
|
||||
serverWarningMessageTxt.text = message;
|
||||
if (warningPanel != null)
|
||||
warningPanel.SetActive(true);
|
||||
|
||||
bool dismissed = false;
|
||||
void OnDismiss()
|
||||
{
|
||||
dismissed = true;
|
||||
if (warningPanel != null)
|
||||
warningPanel.SetActive(false);
|
||||
}
|
||||
|
||||
if (btnWarningClose != null)
|
||||
{
|
||||
btnWarningClose.onClick.RemoveListener(OnDismiss);
|
||||
btnWarningClose.onClick.AddListener(OnDismiss);
|
||||
}
|
||||
|
||||
yield return new WaitUntil(() => dismissed);
|
||||
|
||||
if (btnWarningClose != null)
|
||||
btnWarningClose.onClick.RemoveListener(OnDismiss);
|
||||
}
|
||||
|
||||
static void ApplyUserData(UserData u)
|
||||
{
|
||||
_currentUser = u;
|
||||
|
||||
@@ -6,6 +6,7 @@ public class UserData
|
||||
{
|
||||
public int id;
|
||||
public string username;
|
||||
public string email;
|
||||
public int cc;
|
||||
public int rc;
|
||||
public string created_at;
|
||||
|
||||
Reference in New Issue
Block a user