new API and rematch WIP

This commit is contained in:
2026-05-04 17:48:09 +05:30
parent fb0d386ba1
commit 82287d74fb
17 changed files with 5534 additions and 1258 deletions
@@ -22,6 +22,7 @@ public class LevelLoadManager : MonoBehaviour
public TMP_Text p1_l10, p2_l10;
public Sprite redIcon, blueIcon;
public TMP_Text txtMiddle;
public TMP_Text txtRcPrize;
[Header("Game Over UI")]
public GameObject p1_winner;
public GameObject p2_winner;
@@ -84,7 +85,7 @@ public class LevelLoadManager : MonoBehaviour
}
Team myTeam = Team.Red;
public void SetupMatchMade(Team myTeam_, string opponentName, string myName, string myL10, string opponentL10){
public void SetupMatchMade(Team myTeam_, string opponentName, string myName, string myL10, string opponentL10, string rcPrize){
myTeam = myTeam_;
matchmadeUI.SetActive(true);
p1_winner.SetActive(false);
@@ -113,23 +114,24 @@ public class LevelLoadManager : MonoBehaviour
p2_name.text = myName;
p1_l10.text = myL10;
p2_l10.text = opponentL10;
txtRcPrize.text = rcPrize;
}
public static MatchmadePlayer myPlayer;
public static MatchmadePlayer opponentPlayer;
public void SetupMatchMade(Team myTeam_, MatchmadePlayer myPlayer_, MatchmadePlayer opponentPlayer_){
public void SetupMatchMade(Team myTeam_, MatchmadePlayer myPlayer_, MatchmadePlayer opponentPlayer_, string rcPrize){
myTeam = myTeam_;
myPlayer = myPlayer_;
opponentPlayer = opponentPlayer_;
string opponentName = opponentPlayer_.Name;
string myName = myPlayer_.Name;
string myL10 = $"L10 {myPlayer.l10_wins} -{myPlayer.l10_losses}";
string opponentL10 = $"L10 {opponentPlayer.l10_wins} -{opponentPlayer.l10_losses}";
SetupMatchMade(myTeam, myName, opponentName, myL10, opponentL10);
SetupMatchMade(myTeam, myName, opponentName, myL10, opponentL10, rcPrize);
}
bool gameOver = false;
public void SetupGameOver(Team winningTeam, int redScore, int blueScore){
@@ -9,15 +9,246 @@ public class MatchmadePlayer
public int l10_wins;
public int l10_losses;
public int UserId;
/// <summary>Rocket Credits from server (e.g. GET /players/:id/rc). Cached client-side.</summary>
public float rc;
}
/// <summary>Per-team block from matchmaker GET / when a side is in queue or match is ready.</summary>
[Serializable]
public class MatchmadeResponse
public class MatchmadeTeamPayload
{
public MatchmadePlayer[] Players;
public string GameName;
public int Port;
public long InitTime;
public bool instance_started;
public int match_id;
public int entry_fee;
public int rc_prize;
public string your_team;
}
/// <summary>Envelope from matchmaker fill / rematch success (and in-progress queue state).</summary>
[Serializable]
public class MatchmadeResponse
{
public bool ok;
public int entry_fee;
public int rc_prize;
public MatchmadeTeamPayload for_red;
public MatchmadeTeamPayload for_blue;
/// <summary>Optional root field: which nested block is for this client when both <see cref="for_red"/> and <see cref="for_blue"/> list the same players.</summary>
public string your_team;
public static bool TeamPayloadHasPort(MatchmadeTeamPayload p) =>
p != null && p.Port > 0;
/// <summary>True when both sides are present on the same dedicated instance (2v2-style duplicate rosters).</summary>
public static bool IsMatchReadyDuplicateRosters(MatchmadeResponse e)
{
if (e == null || !e.ok)
return false;
if (!TeamPayloadHasPort(e.for_red) || !TeamPayloadHasPort(e.for_blue))
return false;
if (e.for_red.Players == null || e.for_blue.Players == null)
return false;
if (e.for_red.match_id != e.for_blue.match_id || e.for_red.Port != e.for_blue.Port)
return false;
return e.for_red.Players.Length >= 2 && e.for_blue.Players.Length >= 2;
}
/// <summary>True when each side lists at least one player and together they form a full 1v1 (disjoint user sets, same instance).</summary>
public static bool IsMatchReadySplitRosters(MatchmadeResponse e)
{
if (e == null || !e.ok)
return false;
if (!TeamPayloadHasPort(e.for_red) || !TeamPayloadHasPort(e.for_blue))
return false;
if (e.for_red.Players == null || e.for_blue.Players == null)
return false;
if (e.for_red.match_id != e.for_blue.match_id || e.for_red.Port != e.for_blue.Port)
return false;
if (e.for_red.Players.Length < 1 || e.for_blue.Players.Length < 1)
return false;
int a = e.for_red.Players[0].UserId;
int b = e.for_blue.Players[0].UserId;
if (a == b)
return false;
if (e.for_red.Players.Length == 1 && e.for_blue.Players.Length == 1)
return true;
// Wider payloads: require no user id appearing on both sides
foreach (var pr in e.for_red.Players)
{
foreach (var pb in e.for_blue.Players)
{
if (pr.UserId == pb.UserId)
return false;
}
}
return true;
}
public static bool IsMatchReady(MatchmadeResponse e) =>
IsMatchReadyDuplicateRosters(e) || IsMatchReadySplitRosters(e);
static bool PayloadContainsUserId(MatchmadeTeamPayload p, int userId)
{
if (p?.Players == null || userId == 0)
return false;
for (int i = 0; i < p.Players.Length; i++)
{
if (p.Players[i].UserId == userId)
return true;
}
return false;
}
static int IndexOfUserId(MatchmadePlayer[] players, int userId)
{
if (players == null || userId == 0)
return -1;
for (int i = 0; i < players.Length; i++)
{
if (players[i].UserId == userId)
return i;
}
return -1;
}
/// <summary>Duplicate roster payloads: same users in the same order on red and blue blocks.</summary>
static bool SamePlayerOrder(MatchmadePlayer[] a, MatchmadePlayer[] b)
{
if (a == null || b == null || a.Length != b.Length)
return false;
for (int i = 0; i < a.Length; i++)
{
if (a[i].UserId != b[i].UserId)
return false;
}
return true;
}
/// <summary>
/// When <see cref="for_red"/> and <see cref="for_blue"/> list the same roster in the same order (1v1),
/// matchmaker convention: index 0 = red team, index 1 = blue team.
/// </summary>
static MatchmadeTeamPayload SelectDuplicateRosterBySlot(MatchmadeResponse env, int userId)
{
MatchmadePlayer[] r = env.for_red?.Players;
MatchmadePlayer[] b = env.for_blue?.Players;
if (r == null || b == null || r.Length != 2 || b.Length != 2)
return null;
if (!SamePlayerOrder(r, b))
return null;
int idx = IndexOfUserId(r, userId);
if (idx == 0)
return env.for_red;
if (idx == 1)
return env.for_blue;
return null;
}
/// <summary>Pick the nested block for this client (port, your_team, players for UI).</summary>
public static MatchmadeTeamPayload SelectTeamPayload(MatchmadeResponse env, int userId, Team? teamDisambiguation = null)
{
if (env == null)
return null;
if (!string.IsNullOrEmpty(env.your_team))
{
string y = env.your_team.Trim().ToLowerInvariant();
if (y == "red")
return env.for_red;
if (y == "blue")
return env.for_blue;
}
bool onRed = PayloadContainsUserId(env.for_red, userId);
bool onBlue = PayloadContainsUserId(env.for_blue, userId);
if (onRed && !onBlue)
return env.for_red;
if (onBlue && !onRed)
return env.for_blue;
if (onRed && onBlue)
{
MatchmadeTeamPayload bySlot = SelectDuplicateRosterBySlot(env, userId);
if (bySlot != null)
return bySlot;
if (teamDisambiguation.HasValue)
{
if (teamDisambiguation.Value == Team.Red)
return env.for_red;
if (teamDisambiguation.Value == Team.Blue)
return env.for_blue;
}
Debug.LogWarning("MatchmadeResponse: both sides list this user but roster is not 1v1 duplicate order; set root your_team on the server or pass team disambiguation.");
}
return env.for_red;
}
/// <summary>Resolve self and opponent from the selected branch (duplicate full roster) or from both sides (split 1v1 rosters).</summary>
public static bool TryResolveMyAndOpponent(MatchmadeResponse env, MatchmadeTeamPayload branch, int userId, out MatchmadePlayer myPlayer, out MatchmadePlayer opponentPlayer)
{
myPlayer = null;
opponentPlayer = null;
if (env == null || branch == null || branch.Players == null || userId == 0)
return false;
MatchmadeTeamPayload other = ReferenceEquals(branch, env.for_blue) ? env.for_red : env.for_blue;
if (branch.Players.Length >= 2)
{
for (int i = 0; i < branch.Players.Length; i++)
{
if (branch.Players[i].UserId == userId)
{
myPlayer = branch.Players[i];
break;
}
}
if (myPlayer == null)
return false;
for (int i = 0; i < branch.Players.Length; i++)
{
if (branch.Players[i].UserId != myPlayer.UserId)
{
opponentPlayer = branch.Players[i];
break;
}
}
return opponentPlayer != null;
}
for (int i = 0; i < branch.Players.Length; i++)
{
if (branch.Players[i].UserId == userId)
{
myPlayer = branch.Players[i];
break;
}
}
if (myPlayer == null && branch.Players.Length == 1 && branch.Players[0].UserId == userId)
myPlayer = branch.Players[0];
if (other?.Players == null)
return false;
for (int i = 0; i < other.Players.Length; i++)
{
if (other.Players[i].UserId != userId)
{
opponentPlayer = other.Players[i];
break;
}
}
if (opponentPlayer == null && other.Players.Length == 1)
opponentPlayer = other.Players[0];
return myPlayer != null && opponentPlayer != null && myPlayer.UserId != opponentPlayer.UserId;
}
}