@@ -61,11 +61,26 @@ public function createQMPlayer($request, $player, $history)
6161
6262 $ qmPlayer ->chosen_side = $ request ->side ;
6363
64+ // Store preferred colors. If not set, players get yellow/red like usual.
65+ if (isset ($ request ->colors ) && is_array ($ request ->colors ))
66+ {
67+ $ qmPlayer ->colors_pref = json_encode (array_values ($ request ->colors ));
68+ }
69+ if (isset ($ request ->colors_opponent ) && is_array ($ request ->colors_opponent ))
70+ {
71+ $ qmPlayer ->colors_opponent_pref = json_encode (array_values ($ request ->colors_opponent ));
72+ }
73+
6474 if ($ request ->map_sides )
6575 {
6676 $ qmPlayer ->map_sides_id = \App \Models \MapSideString::findValue (join (', ' , $ request ->map_sides ))->id ;
6777 }
6878
79+ if ($ request ->client_version )
80+ {
81+ $ qmPlayer ->client_version = $ request ->client_version ;
82+ }
83+
6984 if ($ request ->version && $ request ->platform )
7085 {
7186 $ qmPlayer ->version_id = \App \Models \PlayerDataString::findValue ($ request ->version )->id ;
@@ -287,10 +302,21 @@ public function createOrUpdateQueueEntry($player, $qmPlayer, $history, $gameType
287302
288303 public function fetchQmQueueEntry (LadderHistory $ history , ?QmQueueEntry $ qmQueueEntry = null ): \Illuminate \Database \Eloquent \Collection |array
289304 {
290- return QmQueueEntry::query ()
291- ->when (isset ($ qmQueueEntry ), fn ($ q ) => $ q ->where ('qm_match_player_id ' , '!= ' , $ qmQueueEntry ->qmPlayer ->id ))
305+ $ query = QmQueueEntry::query ()
292306 ->where ('ladder_history_id ' , '= ' , $ history ->id )
293- ->get ();
307+ ->with ('qmPlayer ' );
308+
309+ if ($ qmQueueEntry )
310+ {
311+ // Client versions need to match. Otherwise players simply don't see each other.
312+ $ currentVersion = $ qmQueueEntry ->qmPlayer ->client_version ;
313+ $ query ->where ('qm_match_player_id ' , '!= ' , $ qmQueueEntry ->qmPlayer ->id )
314+ ->whereHas ('qmPlayer ' , function ($ sub ) use ($ currentVersion ) {
315+ $ sub ->where ('client_version ' , $ currentVersion );
316+ });
317+ }
318+
319+ return $ query ->get ();
294320 }
295321
296322 /**
@@ -935,6 +961,8 @@ private function set1v1QmSpawns($otherQmQueueEntries, $qmMatch, $qmPlayer, $expe
935961 $ numSpawns = $ qmMap ->map ->spawn_count ;
936962 $ spawnArr = [];
937963
964+ // Assign colour & spawn locations for current QM player
965+ // Then again for other players below
938966 for ($ i = 1 ; $ i <= $ numSpawns ; $ i ++)
939967 {
940968 $ spawnArr [] = $ i ;
@@ -947,20 +975,77 @@ private function set1v1QmSpawns($otherQmQueueEntries, $qmMatch, $qmPlayer, $expe
947975 Log::debug ("QuickMatchService ** Random spawns selected for qmMap: ' " . $ qmMap ->description . "', " . $ spawnOrder [0 ] . ", " . $ spawnOrder [1 ]);
948976 }
949977
950-
951- # Assign colour & spawn locations for current QM player
952- # Then again for other players below
953- $ colorsArr = $ this ->getColorsArr (8 , false );
978+ // Find the opponent player and check if both players submitted their preferred colors and make sure their is no observers.
979+ // Streamers might not be prepared for other colors than yellow/red. With someone observer and color info missing, we stick
980+ // to the old logic.
954981 $ i = 0 ;
982+ $ colorsArr = null ;
955983
956- if ($ qmPlayer -> isObserver () == false )
984+ foreach ($ otherQmQueueEntries as $ otherQmQueueEntry )
957985 {
958- $ qmPlayer ->color = $ colorsArr [$ i ];
959- $ qmPlayer ->location = $ spawnOrder [$ i ] - 1 ;
960- $ qmPlayer ->save ();
961- $ i ++;
986+ $ candidate = \App \Models \QmMatchPlayer::where ("id " , $ otherQmQueueEntry ->qmPlayer ->id )->first ();
987+ if ($ candidate && !$ candidate ->isObserver ())
988+ {
989+ $ opponentPlayer = $ candidate ;
990+ $ opponentsCount ++;
991+ }
992+ }
993+
994+ $ bothHaveColorPrefs = false ;
995+ $ p1Colors = null ;
996+ $ p2Colors = null ;
997+ if ($ opponentPlayer && $ opponentsCount === 1 )
998+ {
999+ $ p1Colors = isset ($ qmPlayer ->colors_pref ) ? json_decode ($ qmPlayer ->colors_pref , true ) : null ;
1000+ $ p2Colors = isset ($ opponentPlayer ->colors_pref ) ? json_decode ($ opponentPlayer ->colors_pref , true ) : null ;
1001+ $ p1OppColors = isset ($ qmPlayer ->colors_opponent_pref ) ? json_decode ($ qmPlayer ->colors_opponent_pref , true ) : null ;
1002+ $ p2OppColors = isset ($ opponentPlayer ->colors_opponent_pref ) ? json_decode ($ opponentPlayer ->colors_opponent_pref , true ) : null ;
1003+
1004+ $ qmPlayerIsAnonymous = $ qmPlayer ->player ->user ->userSettings ->getIsAnonymous ();
1005+ $ opponentPlayerIsAnonymous = $ opponentPlayer ->player ->user ->userSettings ->getIsAnonymous ();
1006+
1007+ if ($ qmPlayerIsAnonymous && !$ opponentPlayerIsAnonymous )
1008+ {
1009+ // Do what opponent wants.
1010+ $ p1Colors = $ p2OppColors ;
1011+ $ p1OppColors = $ p2Colors ;
1012+ }
1013+ else if (!$ qmPlayerIsAnonymous && $ opponentPlayerIsAnonymous )
1014+ {
1015+ // Do what player 1 wants.
1016+ $ p2Colors = $ p1OppColors ;
1017+ $ p2OppColors = $ p1Colors ;
1018+ }
9621019
963- Log::debug ("QuickMatchService ** Assigning Spot for " . $ qmPlayer ->player ->username . "Color: " . $ qmPlayer ->color . " Location: " . $ qmPlayer ->location );
1020+ $ bothHaveColorPrefs = is_array ($ p1Colors ) && is_array ($ p2Colors ) && is_array ($ p1OppColors ) && is_array ($ p2OppColors );
1021+ }
1022+
1023+ if (!$ matchHasObserver && $ bothHaveColorPrefs )
1024+ {
1025+ // Determine best matching colors.
1026+ [$ p1Color , $ p2Color ] = $ this ->setColors ($ p1Colors , $ p1OppColors , $ p2Colors , $ p2OppColors );
1027+ $ colorsArr = [$ p1Color , $ p2Color ];
1028+ if ($ qmPlayer ->isObserver () == false )
1029+ {
1030+ $ qmPlayer ->color = $ colorsArr [$ i ];
1031+ $ qmPlayer ->location = $ spawnOrder [$ i ] - 1 ;
1032+ $ qmPlayer ->save ();
1033+ $ i ++;
1034+ Log::debug ("QuickMatchService ** Assigning Spot (prefs) for " . $ qmPlayer ->player ->username . " Color: " . $ qmPlayer ->color . " Location: " . $ qmPlayer ->location );
1035+ }
1036+ }
1037+ else
1038+ {
1039+ // No preferred colors. Used old logic.
1040+ $ colorsArr = $ this ->getColorsArr (8 , false );
1041+ if ($ qmPlayer ->isObserver () == false )
1042+ {
1043+ $ qmPlayer ->color = $ colorsArr [$ i ];
1044+ $ qmPlayer ->location = $ spawnOrder [$ i ] - 1 ;
1045+ $ qmPlayer ->save ();
1046+ $ i ++;
1047+ Log::debug ("QuickMatchService ** Assigning Spot for " . $ qmPlayer ->player ->username . " Color: " . $ qmPlayer ->color . " Location: " . $ qmPlayer ->location );
1048+ }
9641049 }
9651050
9661051 foreach ($ otherQmQueueEntries as $ otherQmQueueEntry )
@@ -1006,7 +1091,7 @@ private function set1v1QmSpawns($otherQmQueueEntries, $qmMatch, $qmPlayer, $expe
10061091 $ otherQmPlayer ->tunnel_id = $ qmMatch ->seed + $ otherQmPlayer ->color ;
10071092 $ otherQmPlayer ->save ();
10081093
1009- if (!$ otherQmPlayer ->isObserver ())
1094+ if (!$ otherQmPlayer ->isObserver () && $ opponentPlayer === null )
10101095 {
10111096 $ opponentPlayer = $ otherQmPlayer ;
10121097 $ opponentsCount ++;
@@ -1526,6 +1611,69 @@ private function getColorsArr($numPlayers, $randomize)
15261611 return array_slice ($ possibleColors , 0 , $ numPlayers );
15271612 }
15281613
1614+ /**
1615+ * Selects final colors for player 1 and 2 based on their preferences.
1616+ * @param int[] $prefColorsP1 The preferred colors of player 1
1617+ * @param int[] $prefOpponentColorsP1 What player 1 prefers for their opponent.
1618+ * @param int[] $prefColorsP2 The preferred colors of player 2.
1619+ * @param int[] $prefOpponentColorsP2 What player 2 prefers for their opponent.
1620+ * @return array{int,int} [colorPlayer1, colorPlayer2]
1621+ */
1622+ private function setColors (array $ prefColorsP1 , array $ prefOpponentColorsP1 , array $ prefColorsP2 , array $ prefOpponentColorsP2 ): array
1623+ {
1624+ // Penalty mapping: position in preference array -> penalty points.
1625+ $ penalties = [0 , 2 , 5 , 10 ];
1626+
1627+ // Generate all possible color combinations (0-3).
1628+ $ bestCombinations = [];
1629+ $ lowestPenalty = PHP_INT_MAX ;
1630+
1631+ for ($ colorP1 = 0 ; $ colorP1 < 4 ; $ colorP1 ++)
1632+ {
1633+ for ($ colorP2 = 0 ; $ colorP2 < 4 ; $ colorP2 ++)
1634+ {
1635+ // Cannot assign same color to both players.
1636+ if ($ colorP1 === $ colorP2 )
1637+ {
1638+ continue ;
1639+ }
1640+
1641+ // Calculate penalty for player 1's color choice.
1642+ $ posP1 = array_search ($ colorP1 , $ prefColorsP1 );
1643+ $ penaltyP1Color = $ penalties [$ posP1 ];
1644+
1645+ // Calculate penalty for player 1's opponent color preference.
1646+ $ posP1Opponent = array_search ($ colorP2 , $ prefOpponentColorsP1 );
1647+ $ penaltyP1Opponent = $ penalties [$ posP1Opponent ];
1648+
1649+ // Calculate penalty for player 2's color choice.
1650+ $ posP2 = array_search ($ colorP2 , $ prefColorsP2 );
1651+ $ penaltyP2Color = $ penalties [$ posP2 ];
1652+
1653+ // Calculate penalty for player 2's opponent color preference.
1654+ $ posP2Opponent = array_search ($ colorP1 , $ prefOpponentColorsP2 );
1655+ $ penaltyP2Opponent = $ penalties [$ posP2Opponent ];
1656+
1657+ // Total penalty for this combination.
1658+ $ totalPenalty = $ penaltyP1Color + $ penaltyP1Opponent + $ penaltyP2Color + $ penaltyP2Opponent ;
1659+
1660+ // Track best combinations.
1661+ if ($ totalPenalty < $ lowestPenalty )
1662+ {
1663+ // Replace.
1664+ $ lowestPenalty = $ totalPenalty ;
1665+ $ bestCombinations = [[$ colorP1 , $ colorP2 ]];
1666+ }
1667+ elseif ($ totalPenalty === $ lowestPenalty )
1668+ {
1669+ // Add.
1670+ $ bestCombinations [] = [$ colorP1 , $ colorP2 ];
1671+ }
1672+ }
1673+ }
1674+
1675+ return $ bestCombinations [array_rand ($ bestCombinations )];
1676+ }
15291677
15301678 /**
15311679 *
0 commit comments