mirror of
https://github.com/Deathgarden-Rebirth/Deathgarden_Rebirth-Rewrite.git
synced 2026-04-24 23:30:41 -05:00
Merge pull request #39 from Deathgarden-Rebirth/matchmaking-task-schedule
Moved the Matchmaking into scheduled tasks
This commit is contained in:
commit
7ffcc387dd
70
dist/app/Console/Commands/MatchmakingCleanup.php
vendored
Normal file
70
dist/app/Console/Commands/MatchmakingCleanup.php
vendored
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Enums\Game\Matchmaking\MatchStatus;
|
||||
use App\Models\Game\Matchmaking\Game;
|
||||
use App\Models\Game\Matchmaking\QueuedPlayer;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class MatchmakingCleanup extends Command
|
||||
{
|
||||
// After how many minutes a queued palyer gets removed from the queue or from an open match in Minutes.
|
||||
const PLAYER_HEARTBEAT_TIMEOUT = 1;
|
||||
|
||||
// After how many minutes a closed game gets deleted automatically when it hasn't been killed normally yet.
|
||||
const GAME_MAX_TIME = 15;
|
||||
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'matchmaking:cleanup';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Cleanup stale or bugged matchmaking games and queued users';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
// Delete queued players that haven't sent a queue resquest in the given period of time,
|
||||
// which means they crashed or closed the game without sending cancle.
|
||||
QueuedPlayer::where('updated_at', '<', Carbon::now()->subMinutes(static::PLAYER_HEARTBEAT_TIMEOUT))
|
||||
->delete();
|
||||
|
||||
Game::where('status', '=', MatchStatus::Closed->value)
|
||||
->where('updated_at', '<', Carbon::now()->subMinutes(static::GAME_MAX_TIME))
|
||||
->delete();
|
||||
|
||||
// Select User Ids that joined a game and haven't sent the match request for a period of time
|
||||
// This porpably means they crashed or never joined the game in the first place.
|
||||
$usersToRemove = DB::table('game_user')->join('games', 'game_user.game_id', '=', 'games.id')
|
||||
->whereIn('games.status', [
|
||||
MatchStatus::Created->value,
|
||||
MatchStatus::Opened->value,
|
||||
])
|
||||
->where('game_user.updated_at', '<', Carbon::now()->subMinutes(static::PLAYER_HEARTBEAT_TIMEOUT))
|
||||
->get(['user_id']);
|
||||
|
||||
$userIdArray = [];
|
||||
foreach ($usersToRemove as $user) {
|
||||
$userIdArray[] = $user->user_id;
|
||||
}
|
||||
|
||||
// Delete a game if one of the to be removed players is the host.
|
||||
Game::whereIn('creator_user_id', $userIdArray)->delete();
|
||||
|
||||
// Delete them afterwards.
|
||||
DB::table('game_user')->whereIn('user_id', $userIdArray)->delete();
|
||||
}
|
||||
}
|
||||
192
dist/app/Console/Commands/ProcessMatchmaking.php
vendored
Normal file
192
dist/app/Console/Commands/ProcessMatchmaking.php
vendored
Normal file
|
|
@ -0,0 +1,192 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Classes\Matchmaking\MatchmakingPlayerCount;
|
||||
use App\Enums\Game\Matchmaking\MatchmakingSide;
|
||||
use App\Enums\Game\Matchmaking\MatchStatus;
|
||||
use App\Models\Game\Matchmaking\Game;
|
||||
use App\Models\Game\Matchmaking\MatchConfiguration;
|
||||
use App\Models\Game\Matchmaking\QueuedPlayer;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
|
||||
class ProcessMatchmaking extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'matchmaking:process';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Command description';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle(): void
|
||||
{
|
||||
// Select all queued Players/party leaders, descending by party size
|
||||
$players = QueuedPlayer::withCount('followingUsers')
|
||||
->sharedLock()
|
||||
->whereNull('queued_player_id')
|
||||
->orderByDesc('following_users_count')
|
||||
->orderBy('created_at')
|
||||
->get();
|
||||
|
||||
// If there are no players in the queue, stop here.
|
||||
if($players->isEmpty())
|
||||
return;
|
||||
|
||||
$runners = new Collection();
|
||||
$hunters = new Collection();
|
||||
|
||||
// Split hunters and runners into separate collections
|
||||
$players->each(function (QueuedPlayer $player) use ($hunters, $runners) {
|
||||
if($player->side === MatchmakingSide::Hunter)
|
||||
$hunters->add($player);
|
||||
else
|
||||
$runners->add($player);
|
||||
});
|
||||
|
||||
$this->tryFillOpenGames($hunters, $runners);
|
||||
|
||||
$playerCount = $this->getTotalPlayersCount($players);
|
||||
$availableMatchConfigs = MatchConfiguration::getAvailableMatchConfigs($playerCount->runners, $playerCount->hunters);
|
||||
|
||||
if($availableMatchConfigs->isEmpty())
|
||||
return;
|
||||
|
||||
$selectedConfig = MatchConfiguration::selectRandomConfigByWeight($availableMatchConfigs);
|
||||
|
||||
// Should never happen, but just to be careful
|
||||
if($selectedConfig === null)
|
||||
return;
|
||||
|
||||
$hunterGroupsSet = $this->determineMatchingPlayers($hunters, $selectedConfig->hunters);
|
||||
$runnerGroupsSet = $this->determineMatchingPlayers($runners, $selectedConfig->runners);
|
||||
|
||||
// if we cannot create a match with our current player groups, stop
|
||||
if($runnerGroupsSet === false || $hunterGroupsSet === false)
|
||||
return;
|
||||
|
||||
rsort($runnerGroupsSet, SORT_NUMERIC);
|
||||
rsort($hunterGroupsSet, SORT_NUMERIC);
|
||||
|
||||
$newGame = new Game();
|
||||
$newGame->status = MatchStatus::Created;
|
||||
$newGame->matchConfiguration()->associate($selectedConfig);
|
||||
$newGame->save();
|
||||
|
||||
foreach ($hunterGroupsSet as $groupSize) {
|
||||
$foundQueuedPlayerIndex = $hunters->search(function (QueuedPlayer $hunter) use ($groupSize) {
|
||||
return ($hunter->following_users_count + 1) === $groupSize;
|
||||
});
|
||||
|
||||
$foundHunter = $hunters->pull($foundQueuedPlayerIndex);
|
||||
$newGame->addQueuedPlayer($foundHunter);
|
||||
}
|
||||
|
||||
foreach ($runnerGroupsSet as $groupSize) {
|
||||
$foundQueuedPlayerIndex = $runners->search(function (QueuedPlayer $runner) use ($groupSize) {
|
||||
return ($runner->following_users_count + 1) === $groupSize;
|
||||
});
|
||||
$foundRunner = $runners->pull($foundQueuedPlayerIndex);
|
||||
$newGame->addQueuedPlayer($foundRunner);
|
||||
}
|
||||
|
||||
$newGame->determineHost();
|
||||
}
|
||||
|
||||
protected function tryFillOpenGames(Collection|array &$hunters, Collection|array &$runners): void
|
||||
{
|
||||
$openGames = Game::where('status', '=', MatchStatus::Opened->value)->get();
|
||||
|
||||
foreach ($openGames as $game) {
|
||||
$neededPlayers = $game->remainingPlayerCount();
|
||||
|
||||
// game is full and doesn't need filling
|
||||
if($neededPlayers->getTotal() == 0)
|
||||
continue;
|
||||
|
||||
if($neededPlayers->hunters > 0) {
|
||||
$hunterGroupsSet = $this->determineMatchingPlayers($hunters, $neededPlayers->hunters);
|
||||
|
||||
// see if there are any group combinations possible to fill the game
|
||||
if($hunterGroupsSet === false)
|
||||
continue;
|
||||
|
||||
// use biggest groups first
|
||||
rsort($hunterGroupsSet, SORT_NUMERIC);
|
||||
|
||||
foreach ($hunterGroupsSet as $groupSize) {
|
||||
$foundQueuedPlayerIndex = $hunters->search(function (QueuedPlayer $hunter) use ($groupSize) {
|
||||
return ($hunter->following_users_count + 1) === $groupSize;
|
||||
});
|
||||
|
||||
$foundHunter = $hunters->pull($foundQueuedPlayerIndex);
|
||||
$game->addQueuedPlayer($foundHunter);
|
||||
}
|
||||
}
|
||||
|
||||
if($neededPlayers->runners > 0) {
|
||||
$runnerGroupSet = $this->determineMatchingPlayers($runners, $neededPlayers->runners);
|
||||
|
||||
// see if there are any group combinations possible to fill the game
|
||||
if($runnerGroupSet === false)
|
||||
continue;
|
||||
|
||||
// use biggest groups first
|
||||
rsort($runnerGroupSet, SORT_NUMERIC);
|
||||
|
||||
foreach ($runnerGroupSet as $groupSize) {
|
||||
$foundQueuedPlayerIndex = $runners->search(function (QueuedPlayer $runner) use ($groupSize) {
|
||||
return ($runner->following_users_count + 1) === $groupSize;
|
||||
});
|
||||
|
||||
$foundRunner = $runners->pull($foundQueuedPlayerIndex);
|
||||
$game->addQueuedPlayer($foundRunner);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function getTotalPlayersCount(Collection &$queuedPlayerCollection): MatchmakingPlayerCount
|
||||
{
|
||||
$count = new MatchmakingPlayerCount();
|
||||
foreach ($queuedPlayerCollection as $player) {
|
||||
/** @var QueuedPlayer $player */
|
||||
|
||||
if($player->side == MatchmakingSide::Hunter)
|
||||
$count->hunters += $player->following_users_count + 1;
|
||||
else
|
||||
$count->runners += $player->following_users_count + 1;
|
||||
}
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $queuedPlayers
|
||||
* @param int $target
|
||||
* @return array|false
|
||||
*/
|
||||
private function determineMatchingPlayers(Collection &$queuedPlayers, int $target): array|false
|
||||
{
|
||||
$availableNumbers = [];
|
||||
$queuedPlayers->each(function (QueuedPlayer $player) use (&$availableNumbers) {
|
||||
$availableNumbers[] = $player->following_users_count + 1;
|
||||
});
|
||||
|
||||
$result = MatchmakingPlayerCount::findSubsetsOfSum($availableNumbers, $target, true);
|
||||
|
||||
if(count($result) > 0)
|
||||
return $result;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
2
dist/app/Console/Kernel.php
vendored
2
dist/app/Console/Kernel.php
vendored
|
|
@ -13,6 +13,8 @@ class Kernel extends ConsoleKernel
|
|||
protected function schedule(Schedule $schedule): void
|
||||
{
|
||||
$schedule->command('model:prune')->daily();
|
||||
$schedule->command('matchmaking:process')->everyTenSeconds();
|
||||
$schedule->command('matchmaking:cleanup')->everyTwentySeconds();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -2,8 +2,6 @@
|
|||
|
||||
namespace App\Http\Controllers\Api\Matchmaking;
|
||||
|
||||
use App\Classes\Matchmaking\MatchmakingPlayerCount;
|
||||
use App\Enums\Game\Matchmaking\MatchmakingSide;
|
||||
use App\Enums\Game\Matchmaking\MatchStatus;
|
||||
use App\Enums\Game\Matchmaking\QueueStatus;
|
||||
use App\Http\Controllers\Controller;
|
||||
|
|
@ -18,13 +16,11 @@
|
|||
use App\Models\Admin\Archive\ArchivedGame;
|
||||
use App\Models\Admin\Archive\ArchivedPlayerProgression;
|
||||
use App\Models\Game\Matchmaking\Game;
|
||||
use App\Models\Game\Matchmaking\MatchConfiguration;
|
||||
use App\Models\Game\Matchmaking\QueuedPlayer;
|
||||
use App\Models\User\User;
|
||||
use Auth;
|
||||
use Illuminate\Auth\Access\AuthorizationException;
|
||||
use Illuminate\Contracts\Cache\LockTimeoutException;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
|
@ -32,14 +28,6 @@
|
|||
|
||||
class MatchmakingController extends Controller
|
||||
{
|
||||
const TRY_CREATE_MATCH_INTERVAL_SECONDS = 5;
|
||||
|
||||
// After how many minutes a queued palyer gets removed from the queue or from an open match in Minutes.
|
||||
const PLAYER_HEARTBEAT_TIMEOUT = 2;
|
||||
|
||||
// After how many minutes a closed game gets deleted automatically when it hasn't been killed normally yet.
|
||||
const GAME_MAX_TIME = 15;
|
||||
|
||||
const QUEUE_LOCK = 'queuedPlayers';
|
||||
|
||||
public function getRegions()
|
||||
|
|
@ -49,8 +37,6 @@ public function getRegions()
|
|||
|
||||
public function queue(QueueRequest $request)
|
||||
{
|
||||
$this->processQueue();
|
||||
$this->cleanupDeadPlayersAndGames();
|
||||
if($request->checkOnly)
|
||||
return json_encode($this->checkQueueStatus($request));
|
||||
|
||||
|
|
@ -76,7 +62,7 @@ public function cancelQueue(MatchmakingRequest $request)
|
|||
} catch (LockTimeoutException $e) {
|
||||
Log::channel('matchmaking')->emergency('Queue Cancel: Could not acquire Lock for canceling user '.$user->id.'('.$user->last_known_username.')');
|
||||
} finally {
|
||||
$lock?->release();
|
||||
$lock->release();
|
||||
}
|
||||
|
||||
return response('', 204);
|
||||
|
|
@ -97,8 +83,6 @@ public function matchInfo(string $matchId)
|
|||
$foundGame->updated_at = Carbon::now();
|
||||
$foundGame->save();
|
||||
|
||||
$this->cleanupDeadPlayersAndGames();
|
||||
|
||||
$response = MatchData::fromGame($foundGame);
|
||||
return json_encode($response);
|
||||
}
|
||||
|
|
@ -368,148 +352,6 @@ protected function addPlayerToQueue(QueueRequest $request)
|
|||
return $this->checkQueueStatus($request);
|
||||
}
|
||||
|
||||
protected function processQueue(): void
|
||||
{
|
||||
// skip when the last time this job run is not older than the interval seconds
|
||||
if(!(Cache::get('tryCreateMatch', 0) < time() - static::TRY_CREATE_MATCH_INTERVAL_SECONDS))
|
||||
return;
|
||||
|
||||
$lock = Cache::lock(static::QUEUE_LOCK, 20);
|
||||
|
||||
// If we cannot acquire the lock, do nothing
|
||||
if(!$lock->get())
|
||||
return;
|
||||
|
||||
// Select all queued Players/party leaders, descending by party size
|
||||
$players = QueuedPlayer::withCount('followingUsers')
|
||||
->sharedLock()
|
||||
->whereNull('queued_player_id')
|
||||
->orderByDesc('following_users_count')
|
||||
->orderBy('created_at')
|
||||
->get();
|
||||
|
||||
$runners = new Collection();
|
||||
$hunters = new Collection();
|
||||
|
||||
// Split hunters and runners into separate collections
|
||||
$players->each(function (QueuedPlayer $player) use ($hunters, $runners) {
|
||||
if($player->side === MatchmakingSide::Hunter)
|
||||
$hunters->add($player);
|
||||
else
|
||||
$runners->add($player);
|
||||
});
|
||||
|
||||
$this->tryFillOpenGames($hunters, $runners);
|
||||
|
||||
$playerCount = $this->getTotalPlayersCount($players);
|
||||
$availableMatchConfigs = MatchConfiguration::getAvailableMatchConfigs($playerCount->runners, $playerCount->hunters);
|
||||
|
||||
if($availableMatchConfigs->isEmpty()) {
|
||||
$lock->release();
|
||||
return;
|
||||
}
|
||||
|
||||
$selectedConfig = MatchConfiguration::selectRandomConfigByWeight($availableMatchConfigs);
|
||||
|
||||
// Should never happen, but just to be careful
|
||||
if($selectedConfig === null) {
|
||||
$lock->release();
|
||||
return;
|
||||
}
|
||||
|
||||
$hunterGroupsSet = $this->determineMatchingPlayers($hunters, $selectedConfig->hunters);
|
||||
$runnerGroupsSet = $this->determineMatchingPlayers($runners, $selectedConfig->runners);
|
||||
|
||||
// if we cannot create a match with our current player groups, stop
|
||||
if($runnerGroupsSet === false || $hunterGroupsSet === false) {
|
||||
$lock->release();
|
||||
return;
|
||||
}
|
||||
rsort($runnerGroupsSet, SORT_NUMERIC);
|
||||
rsort($hunterGroupsSet, SORT_NUMERIC);
|
||||
|
||||
$newGame = new Game();
|
||||
$newGame->status = MatchStatus::Created;
|
||||
$newGame->matchConfiguration()->associate($selectedConfig);
|
||||
$newGame->save();
|
||||
|
||||
foreach ($hunterGroupsSet as $groupSize) {
|
||||
$foundQueuedPlayerIndex = $hunters->search(function (QueuedPlayer $hunter) use ($groupSize) {
|
||||
return ($hunter->following_users_count + 1) === $groupSize;
|
||||
});
|
||||
|
||||
$foundHunter = $hunters->pull($foundQueuedPlayerIndex);
|
||||
$newGame->addQueuedPlayer($foundHunter);
|
||||
}
|
||||
|
||||
foreach ($runnerGroupsSet as $groupSize) {
|
||||
$foundQueuedPlayerIndex = $runners->search(function (QueuedPlayer $runner) use ($groupSize) {
|
||||
return ($runner->following_users_count + 1) === $groupSize;
|
||||
});
|
||||
$foundRunner = $runners->pull($foundQueuedPlayerIndex);
|
||||
$newGame->addQueuedPlayer($foundRunner);
|
||||
}
|
||||
|
||||
$newGame->determineHost();
|
||||
|
||||
// Set cached timeat the end of processing and release lock
|
||||
Cache::set('tryCreateMatch', time());
|
||||
$lock->release();
|
||||
}
|
||||
|
||||
protected function tryFillOpenGames(Collection|array &$hunters, Collection|array &$runners)
|
||||
{
|
||||
$openGames = Game::where('status', '=', MatchStatus::Opened->value)->get();
|
||||
|
||||
foreach ($openGames as $game) {
|
||||
$neededPlayers = $game->remainingPlayerCount();
|
||||
|
||||
// game is full and doesn't need filling
|
||||
if($neededPlayers->getTotal() == 0)
|
||||
continue;
|
||||
|
||||
if($neededPlayers->hunters > 0) {
|
||||
$hunterGroupsSet = $this->determineMatchingPlayers($hunters, $neededPlayers->hunters);
|
||||
|
||||
// see if there are any group combinations possible to fill the game
|
||||
if($hunterGroupsSet === false)
|
||||
continue;
|
||||
|
||||
// use biggest groups first
|
||||
rsort($hunterGroupsSet, SORT_NUMERIC);
|
||||
|
||||
foreach ($hunterGroupsSet as $groupSize) {
|
||||
$foundQueuedPlayerIndex = $hunters->search(function (QueuedPlayer $hunter) use ($groupSize) {
|
||||
return ($hunter->following_users_count + 1) === $groupSize;
|
||||
});
|
||||
|
||||
$foundHunter = $hunters->pull($foundQueuedPlayerIndex);
|
||||
$game->addQueuedPlayer($foundHunter);
|
||||
}
|
||||
}
|
||||
|
||||
if($neededPlayers->runners > 0) {
|
||||
$runnerGroupSet = $this->determineMatchingPlayers($runners, $neededPlayers->runners);
|
||||
|
||||
// see if there are any group combinations possible to fill the game
|
||||
if($runnerGroupSet === false)
|
||||
continue;
|
||||
|
||||
// use biggest groups first
|
||||
rsort($runnerGroupSet, SORT_NUMERIC);
|
||||
|
||||
foreach ($runnerGroupSet as $groupSize) {
|
||||
$foundQueuedPlayerIndex = $runners->search(function (QueuedPlayer $runner) use ($groupSize) {
|
||||
return ($runner->following_users_count + 1) === $groupSize;
|
||||
});
|
||||
|
||||
$foundRunner = $runners->pull($foundQueuedPlayerIndex);
|
||||
$game->addQueuedPlayer($foundRunner);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function removeUserFromGame(User $user, Game $game)
|
||||
{
|
||||
$game->players()->detach($user);
|
||||
|
|
@ -520,74 +362,4 @@ protected function removeUserFromGame(User $user, Game $game)
|
|||
$game->delete();
|
||||
}
|
||||
|
||||
protected function cleanupDeadPlayersAndGames(): void
|
||||
{
|
||||
// skip when the last time this job ran is not older than the interval seconds
|
||||
if(!(Cache::get('cleanupDeadPlayersAndGames', 0) < time() - static::PLAYER_HEARTBEAT_TIMEOUT))
|
||||
return;
|
||||
|
||||
// Delete queued players that haven't sent a queue resquest in the given period of time,
|
||||
// which means they crashed or closed the game without sending cancle.
|
||||
QueuedPlayer::where('updated_at', '<', Carbon::now()->subMinutes(static::PLAYER_HEARTBEAT_TIMEOUT))
|
||||
->delete();
|
||||
|
||||
Game::where('status', '=', MatchStatus::Closed->value)
|
||||
->where('updated_at', '<', Carbon::now()->subMinutes(static::GAME_MAX_TIME))
|
||||
->delete();
|
||||
|
||||
// Select User Ids that joined a game and haven't sent the match request for a period of time
|
||||
// This porpably means they crashed or never joined the game in the first place.
|
||||
$usersToRemove = DB::table('game_user')->join('games', 'game_user.game_id', '=', 'games.id')
|
||||
->whereIn('games.status', [
|
||||
MatchStatus::Created->value,
|
||||
MatchStatus::Opened->value,
|
||||
])
|
||||
->where('game_user.updated_at', '<', Carbon::now()->subMinutes(static::PLAYER_HEARTBEAT_TIMEOUT))
|
||||
->get(['user_id']);
|
||||
|
||||
$userIdArray = [];
|
||||
foreach ($usersToRemove as $user) {
|
||||
$userIdArray[] = $user->user_id;
|
||||
}
|
||||
|
||||
// Delete a game if one of the to be removed players is the host.
|
||||
Game::whereIn('creator_user_id', $userIdArray)->delete();
|
||||
|
||||
// Delete them afterwards.
|
||||
DB::table('game_user')->whereIn('user_id', $userIdArray)->delete();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $queuedPlayers
|
||||
* @param int $target
|
||||
* @return array|false
|
||||
*/
|
||||
private function determineMatchingPlayers(Collection &$queuedPlayers, int $target): array|false
|
||||
{
|
||||
$availableNumbers = [];
|
||||
$queuedPlayers->each(function (QueuedPlayer $player) use (&$availableNumbers) {
|
||||
$availableNumbers[] = $player->following_users_count + 1;
|
||||
});
|
||||
|
||||
$result = MatchmakingPlayerCount::findSubsetsOfSum($availableNumbers, $target, true);
|
||||
|
||||
if(count($result) > 0)
|
||||
return $result;
|
||||
return false;
|
||||
}
|
||||
|
||||
private function getTotalPlayersCount(Collection &$queuedPlayerCollection): MatchmakingPlayerCount
|
||||
{
|
||||
$count = new MatchmakingPlayerCount();
|
||||
foreach ($queuedPlayerCollection as $player) {
|
||||
/** @var QueuedPlayer $player */
|
||||
|
||||
if($player->side == MatchmakingSide::Hunter)
|
||||
$count->hunters += $player->following_users_count + 1;
|
||||
else
|
||||
$count->runners += $player->following_users_count + 1;
|
||||
}
|
||||
return $count;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user