Skip to content

Commit 1a4573a

Browse files
authored
Merge pull request #455 from CnCNet/develop
Points fix, RA2 2v2 Elo
2 parents 34e16d5 + 8fe76f1 commit 1a4573a

File tree

10 files changed

+86
-24
lines changed

10 files changed

+86
-24
lines changed

.gitignore

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -273,4 +273,8 @@ cncnet-api/.bash_history
273273
**/vendor/
274274

275275
# Claude Code local settings
276-
.claude/settings.local.json
276+
.claude/settings.local.json
277+
278+
# PR description (generated by /pr-description command)
279+
PR_DESCRIPTION.md
280+
pr-description.md

cncnet-api/app/Extensions/Qm/Matchup/ClanMatchupHandler.php

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,14 @@ public function matchup(): void
2525
# Fetch all entries who are currently in queue for this ladder
2626
$allQMQueueEntries = $this->quickMatchService->fetchQmQueueEntry($this->history);
2727

28-
// get all observers from qm queue entries
29-
$observersQmQueueEntries = $allQMQueueEntries->filter(function($qmQueueEntry) {
30-
return $qmQueueEntry->qmPlayer->isObserver();
31-
});
28+
// get all observers from qm queue entries (maximum of one observer per match)
29+
// Prioritize observers who have been waiting the longest
30+
$observersQmQueueEntries = $allQMQueueEntries
31+
->filter(function($qmQueueEntry) {
32+
return $qmQueueEntry->qmPlayer->isObserver();
33+
})
34+
->sortBy('created_at')
35+
->take(1);
3236
$this->matchHasObservers = $observersQmQueueEntries->count() > 0;
3337

3438
Log::info("ClanMatchupHandler ** Players Per Clan Required: " . $playerCountPerClanRequired);

cncnet-api/app/Extensions/Qm/Matchup/PlayerMatchupHandler.php

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,12 @@ public function matchup(): void
8282
return;
8383
}
8484

85-
// Add observers to the match if there is any
86-
$observers = $opponents->filter(fn (QmQueueEntry $qmQueueEntry) => $qmQueueEntry->qmPlayer->isObserver());
85+
// Add observers to the match if there is any (maximum of one observer per match)
86+
// Prioritize observers who have been waiting the longest
87+
$observers = $opponents
88+
->filter(fn (QmQueueEntry $qmQueueEntry) => $qmQueueEntry->qmPlayer->isObserver())
89+
->sortBy('created_at')
90+
->take(1);
8791
if ($observers->count() > 0)
8892
{
8993
$this->matchHasObservers = true;

cncnet-api/app/Extensions/Qm/Matchup/TeamMatchupHandler.php

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,12 @@ public function matchup(): void
9696
return;
9797
}
9898

99-
// Add observers to the match if there is any
100-
$observers = $opponents->filter(fn(QmQueueEntry $qmQueueEntry) => $qmQueueEntry->qmPlayer?->isObserver());
99+
// Add observers to the match if there is any (maximum of one observer per match)
100+
// Prioritize observers who have been waiting the longest
101+
$observers = $opponents
102+
->filter(fn(QmQueueEntry $qmQueueEntry) => $qmQueueEntry->qmPlayer?->isObserver())
103+
->sortBy('created_at')
104+
->take(1);
101105
if ($observers->count() > 0)
102106
{
103107
$this->matchHasObservers = true;

cncnet-api/app/Http/Controllers/ApiLadderController.php

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -493,15 +493,18 @@ public function getWinningTeamFromReports($playerGameReports): ?string
493493
}
494494

495495
// Step 2: Fallback — no winner marked; use a non-defeated player (disconnected game case)
496+
// Have observed matches where disconnected=true on players who did not disconnect, but were in a dc'd game due to another player dc'ing
496497
foreach ($playerGameReports as $pgr)
497498
{
498-
if (!$pgr->defeated && !$pgr->disconnected && $pgr->spectator == false)
499+
if (!$pgr->defeated && $pgr->spectator == false)
499500
{
500501
Log::info("Fallback to 'defeated' logic for disconnected game.", [
501502
'game_id' => $pgr->game_id,
502503
'game_report_id' => $pgr->gameReport->id,
503504
'player_id' => $pgr->player_id,
504505
'team' => $pgr->team,
506+
'defeated' => $pgr->defeated,
507+
'disconnected' => $pgr->disconnected,
505508
]);
506509
return $pgr->team;
507510
}
@@ -677,7 +680,21 @@ public function awardTeamPoints(GameReport $gameReport, LadderHistory $history)
677680
// Get correct cache type
678681
$cache = $playerGR->player->playerCache($history->id);
679682

680-
if ($playerGR->points < 0 && ($cache === null || $cache->points < 0))
683+
// Prevent players from going below zero total points
684+
if ($cache !== null && ($cache->points + $playerGR->points) < 0)
685+
{
686+
// Cap the loss: set game points to negative of current total
687+
// Example: if cache = 10, set game points to -10, so new total = 10 + (-10) = 0
688+
$playerGR->points = -1 * $cache->points;
689+
}
690+
elseif ($cache === null && $playerGR->points < 0)
691+
{
692+
// No cache exists, don't allow negative points
693+
$playerGR->points = 0;
694+
}
695+
696+
// Safety check: losers should NEVER get positive points. An edge case has been observed where players with negative or very low pts, earned ~+1 pts when winning, lets prevent that.
697+
if (!$playerGRTeamWonTheGame && !$playerGR->draw && $playerGR->points > 0)
681698
{
682699
$playerGR->points = 0;
683700
}
@@ -824,7 +841,21 @@ public function awardPlayerPoints(GameReport $gameReport, LadderHistory $history
824841
// Get correct cache type
825842
$cache = $playerGR->player->playerCache($history->id);
826843

827-
if ($playerGR->points < 0 && ($cache === null || $cache->points < 0))
844+
// Prevent players from going below zero total points
845+
if ($cache !== null && ($cache->points + $playerGR->points) < 0)
846+
{
847+
// Cap the loss: set game points to negative of current total
848+
// Example: if cache = 10, set game points to -10, so new total = 10 + (-10) = 0
849+
$playerGR->points = -1 * $cache->points;
850+
}
851+
elseif ($cache === null && $playerGR->points < 0)
852+
{
853+
// No cache exists, don't allow negative points
854+
$playerGR->points = 0;
855+
}
856+
857+
// Safety check: losers should NEVER get positive points
858+
if (!$playerGR->wonOrDisco() && !$playerGR->draw && $playerGR->points > 0)
828859
{
829860
$playerGR->points = 0;
830861
}

cncnet-api/app/Http/Controllers/RankingController.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ class RankingController extends Controller
1212
{
1313
public function getIndex(Request $request)
1414
{
15-
$gameModes = ["Blitz", "Blitz 2v2", "Red Alert", "Red Alert 2", "Yuri's Revenge"];
15+
$gameModes = ["Blitz", "Blitz 2v2", "Red Alert", "Red Alert 2", "Red Alert 2 2v2", "Yuri's Revenge"];
1616

1717
$players = ["Active", "New", "All time best", "All players" ];
1818
$upsets = ["All time", "Last 12 month", "Last 30 days"];
@@ -23,6 +23,7 @@ public function getIndex(Request $request)
2323
GameHelper::$GAME_BLITZ . "-2v2",
2424
GameHelper::$GAME_RA,
2525
GameHelper::$GAME_RA2,
26+
GameHelper::$GAME_RA2 . "-2v2",
2627
GameHelper::$GAME_YR,
2728
];
2829

cncnet-api/app/Http/Services/LadderService.php

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,7 @@ function ($q) use ($search)
280280
$filterBy == 'games',
281281
function ($q) use ($orderBy)
282282
{
283-
return $q->orderBy("games", $orderBy ?? 'desc');
283+
return $q->orderBy("games", in_array($orderBy, ['asc', 'desc']) ? $orderBy : 'desc');
284284
},
285285
function ($q)
286286
{
@@ -315,7 +315,7 @@ function ($q) use ($search)
315315
$filterBy == 'games',
316316
function ($q) use ($orderBy)
317317
{
318-
return $q->orderBy("games", $orderBy ?? 'desc');
318+
return $q->orderBy("games", in_array($orderBy, ['asc', 'desc']) ? $orderBy : 'desc');
319319
},
320320
function ($q)
321321
{
@@ -730,7 +730,8 @@ public function undoCache($gameReport)
730730
$pc->mark();
731731
$pc->points -= $playerGR->points;
732732
$pc->games--;
733-
if ($playerGR->won)
733+
// Use points > 0 instead of won flag to handle 2v2 cases where a player dies but team wins
734+
if ($playerGR->points > 0)
734735
$pc->wins--;
735736

736737
$pc->save();
@@ -799,7 +800,8 @@ private function saveClanCache($playerGameReport, $history)
799800

800801
$clanCache->points += $playerGameReport->points;
801802
$clanCache->games++;
802-
if ($playerGameReport->won)
803+
// Use points > 0 instead of won flag to handle 2v2 cases where a player dies but team wins
804+
if ($playerGameReport->points > 0)
803805
$clanCache->wins++;
804806

805807
$clanCache->save();
@@ -826,7 +828,8 @@ private function savePlayerCache($playerGameReport, $history)
826828

827829
$pc->points += $playerGameReport->points;
828830
$pc->games++;
829-
if ($playerGameReport->won)
831+
// Use points > 0 instead of won flag to handle 2v2 cases where a player dies but team wins
832+
if ($playerGameReport->points > 0)
830833
$pc->wins++;
831834

832835
$pc->save();

cncnet-api/app/Http/Services/QuickMatchService.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1017,15 +1017,23 @@ private function set1v1QmSpawns($otherQmQueueEntries, $qmMatch, $qmPlayer, $expe
10171017
$p2OppColors = $p1Colors;
10181018
}
10191019

1020+
Log::debug('QuickMatchService ** Colors prefs', [
1021+
'player' => $qmPlayer->player->username,
1022+
'p1Colors' => $p1Colors,
1023+
'p1OppColors' => $p1OppColors,
1024+
'p2Colors' => $p2Colors,
1025+
'p2OppColors' => $p2OppColors,
1026+
]);
10201027
$bothHaveColorPrefs = is_array($p1Colors) && is_array($p2Colors) && is_array($p1OppColors) && is_array($p2OppColors);
1028+
Log::debug("QuickMatchService ** Both have colors prefs: " . $bothHaveColorPrefs . " Match has observer: " . $matchHasObserver . " qmPlayer is observer: " . $qmPlayer->isObserver());
10211029
}
10221030

10231031
if (!$matchHasObserver && $bothHaveColorPrefs)
10241032
{
10251033
// Determine best matching colors.
10261034
[$p1Color, $p2Color] = $this->setColors($p1Colors, $p1OppColors, $p2Colors, $p2OppColors);
10271035
$colorsArr = [$p1Color, $p2Color];
1028-
if ($qmPlayer->isObserver() == false)
1036+
if (!$qmPlayer->isObserver())
10291037
{
10301038
$qmPlayer->color = $colorsArr[$i];
10311039
$qmPlayer->location = $spawnOrder[$i] - 1;

cncnet-api/app/Http/Services/StatsService.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -391,7 +391,8 @@ public function getPlayerMatchups($player, $history)
391391
$matchupResults[$opponentName]["total"] = 0;
392392
}
393393

394-
if ($pgr->won)
394+
// Use points > 0 instead of won flag to handle DCs and 2v2 cases correctly
395+
if ($pgr->points > 0)
395396
{
396397
$matchupResults[$opponentName]["won"] = $matchupResults[$opponentName]["won"] + 1;
397398
}
@@ -489,8 +490,8 @@ public function getTeamMatchups($player, $history)
489490
// Initialize matchup stats if not already set
490491
$matchupResults[$teammateName] ??= ["won" => 0, "lost" => 0, "total" => 0];
491492

492-
// Update win/loss count
493-
if ($myPlayerGameReport->won || $teamMatePlayerGameReport->won)
493+
// Update win/loss count - use points > 0 to handle DCs and cases where both players die but team wins
494+
if ($myPlayerGameReport->points > 0 || $teamMatePlayerGameReport->points > 0)
494495
{
495496
$matchupResults[$teammateName]["won"]++;
496497
}

cncnet-api/resources/views/ladders/components/_games-player-row.blade.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,11 @@
2828
@php $playerStats2 = \App\Models\Stats2::where("id", $playerGameReport->stats->id)->first(); @endphp
2929
@php $playerCountry = $playerStats2->faction($history->ladder, $playerGameReport->stats->cty); @endphp
3030

31-
<div class="game-status status-{{ $playerGameReport->won ? 'won' : 'lost' }}">
31+
{{-- Check points > 0 in addition to won flag because in team games (2v2), a player may have won=false
32+
if they died during the match, but their team still won and they received positive points --}}
33+
<div class="game-status status-{{ ($playerGameReport->won || $playerGameReport->points > 0) ? 'won' : 'lost' }}">
3234
<span class="status-text">
33-
{{ $playerGameReport->won == true ? 'Won' : 'Lost' }}
35+
{{ ($playerGameReport->won == true || $playerGameReport->points > 0) ? 'Won' : 'Lost' }}
3436
</span>
3537

3638
<span class="points">

0 commit comments

Comments
 (0)