Added attribute injection

This commit is contained in:
Tim 2024-11-14 14:50:40 +01:00
parent 3522bf245f
commit 01d1cbed9c
8 changed files with 165 additions and 79 deletions

View File

@ -6,9 +6,7 @@ use App\Service\Factorio;
use App\Service\RuleChecker; use App\Service\RuleChecker;
use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle; use Symfony\Component\Console\Style\SymfonyStyle;

View File

@ -0,0 +1,40 @@
<?php
namespace App\Command;
use App\Service\Factorio;
use App\Service\SimpleDatabase\PlayTimeRepository;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
#[AsCommand(
name: 'app:users',
description: 'List factorio server users',
)]
class UsersCommand extends Command
{
public function __construct(
private readonly Factorio $factorio,
private readonly PlayTimeRepository $playTimeRepository,
)
{
parent::__construct();
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);
$players = $this->factorio->getOnlinePlayers();
$io->table(['Name', 'Time'], array_map(fn($player) => [
$player->getName(),
$this->playTimeRepository->getPlayTimeOnDay($player),
], $players));
return Command::SUCCESS;
}
}

View File

@ -43,7 +43,10 @@ class Factorio
$players = []; $players = [];
foreach ($output as $line) { foreach ($output as $line) {
$players[] = new Player(explode(' ', trim($line))[0]); $lineSegments = explode(' ', trim($line));
if (count($lineSegments) >= 2 && $lineSegments[1] === '(online)') {
$players[] = new Player($lineSegments[0]);
}
} }
return $players; return $players;
@ -56,7 +59,7 @@ class Factorio
public function whisper(Player $player, string $message): void public function whisper(Player $player, string $message): void
{ {
$this->runCmd(['/whisper', $player->getName(), $message]); $this->runCmd(['/whisper', $player->getName(), sprintf('[%s]', date('H:i:s')), $message]);
} }
public function say(string $message): void public function say(string $message): void

View File

@ -3,20 +3,21 @@
namespace App\Service\Rules; namespace App\Service\Rules;
use App\Dto\Player; use App\Dto\Player;
use App\Service\SimpleDataService; use App\Service\SimpleDatabase\PlayTimeRepository;
use App\Service\SimpleDatabase\SimpleDataService;
class PlayTimeRule extends AbstractRule class PlayTimeRule extends AbstractRule
{ {
public function __construct( public function __construct(
private readonly SimpleDataService $storeData, private readonly PlayTimeRepository $playTimeRepository,
private readonly int $defaultPlayTime = 60, private readonly int $defaultPlayTime = 60,
private readonly int $timeBetweenChecks = 1, private readonly int $timeBetweenChecks = 1,
) {} ) {}
private function timePerPlayer(Player $player) private function timePerPlayer(Player $player)
{ {
$players = [ $players = [
// "Ardentsword" => "2:00", // "Ardentsword" => "120",
]; ];
return $players[$player->getName()] ?? $this->defaultPlayTime; return $players[$player->getName()] ?? $this->defaultPlayTime;
@ -25,24 +26,22 @@ class PlayTimeRule extends AbstractRule
public function check(Player $player): RuleResult public function check(Player $player): RuleResult
{ {
$today = new \DateTimeImmutable(); $today = new \DateTimeImmutable();
$playerData = $this->getPlayerData($player); $playTime = $this->playTimeRepository->getPlayTimeOnDay($player, $today);
// If the player has no data or the data is from a different day, we reset the time // If the player has no data or the data is from a different day, we reset the time
if (!$playerData || $playerData['day'] !== $today->format('Y-m-d')) { if ($playTime === false) {
$this->storePlayerData($player, $today, $this->timePerPlayer($player)); $this->playTimeRepository->storePlayTime($player, $today, $this->timePerPlayer($player));
return new RuleResult(RuleResultEnum::WARNING, sprintf('Extra time activated, set to %d minutes', $this->timePerPlayer($player))); return new RuleResult(RuleResultEnum::WARNING, sprintf('Extra time activated, set to %d minutes', $this->timePerPlayer($player)));
} }
// If the player has time left, we decrease it and return denied if nothing left // If the player has time left, we decrease it and return denied if nothing left
$timeLeft = $playerData['timeLeft'] - $this->timeBetweenChecks; $playTime -= $this->timeBetweenChecks;
$this->storePlayerData($player, $today, $timeLeft); $this->playTimeRepository->storePlayTime($player, $today, $playTime);
if ($timeLeft <= 0) { if ($playTime <= 0) {
return new RuleResult(RuleResultEnum::DENIED); return new RuleResult(RuleResultEnum::DENIED);
} elseif ($timeLeft <= 5) { } elseif ($playTime <= 5 || $playTime === 30) {
return new RuleResult(RuleResultEnum::WARNING, sprintf('%d minute(s) left', $timeLeft)); return new RuleResult(RuleResultEnum::WARNING, sprintf('%d minute(s) left', $playTime));
}elseif($timeLeft === 30) {
return new RuleResult(RuleResultEnum::WARNING, sprintf('30 minutes left'));
} }
return new RuleResult(RuleResultEnum::ALLOWED); return new RuleResult(RuleResultEnum::ALLOWED);
@ -52,22 +51,4 @@ class PlayTimeRule extends AbstractRule
{ {
return sprintf('PlayTimeRule, limited play time'); return sprintf('PlayTimeRule, limited play time');
} }
private function storePlayerData(Player $player, \DateTimeImmutable $day, int $timeLeft): void
{
$this->storeData->set(
sprintf('playTime-%s', $player->getName()),
[
'player' => $player->getName(),
'timeLeft' => $timeLeft,
'day' => $day->format('Y-m-d'),
]);
}
private function getPlayerData(Player $player): ?array
{
return $this->storeData->get(
sprintf('playTime-%s', $player->getName())
);
}
} }

View File

@ -8,7 +8,7 @@ class TimeRule extends AbstractRule
{ {
public function __construct( public function __construct(
private readonly string $start = '19:00', private readonly string $start = '19:00',
private readonly string $end = '23:00', private readonly string $end = '20:00',
) {} ) {}
public function check(Player $player): RuleResult public function check(Player $player): RuleResult
@ -18,9 +18,12 @@ class TimeRule extends AbstractRule
$end = new \DateTimeImmutable($this->end); $end = new \DateTimeImmutable($this->end);
if ($now >= $start && $now <= $end) { if ($now >= $start && $now <= $end) {
$fiveMinutesBeforeEnd = $end->sub(new \DateInterval('PT5M')); // Always add 1 because we are a few seconds into the minute
if ($now >= $fiveMinutesBeforeEnd) { $timeLeft = $end->diff($now);
return new RuleResult(RuleResultEnum::WARNING, 'Only 5 minutes remaining'); $minutesLeft = $timeLeft->i + 1;
$hoursLeft = $timeLeft->h;
if ($hoursLeft == 0 && ($minutesLeft <= 5 || $minutesLeft === 30)) {
return new RuleResult(RuleResultEnum::WARNING, sprintf('%d minute(s) left', $minutesLeft));
} }
return new RuleResult(RuleResultEnum::ALLOWED); return new RuleResult(RuleResultEnum::ALLOWED);
} }

View File

@ -1,38 +0,0 @@
<?php
namespace App\Service;
use Symfony\Component\HttpKernel\KernelInterface;
class SimpleDataService
{
private string $file;
public function __construct(KernelInterface $kernel)
{
$this->file = $kernel->getProjectDir() . '/data.json';
}
public function set(string $key, array $data): self
{
$all = $this->getAll();
$all[$key] = $data;
file_put_contents($this->file, json_encode($all, JSON_PRETTY_PRINT));
return $this;
}
public function get(string $key): ?array
{
return $this->getAll()[$key] ?? null;
}
public function getAll(): array
{
if (!file_exists($this->file)) {
return [];
}
return json_decode(file_get_contents($this->file), true);
}
}

View File

@ -0,0 +1,47 @@
<?php
namespace App\Service\SimpleDatabase;
use App\Dto\Player;
readonly class PlayTimeRepository
{
public function __construct(
private SimpleDataService $dataService,
) {}
private function getKey(Player $player): string
{
return sprintf('playTime-%s', $player->getName());
}
public function storePlayTime(Player $player, \DateTimeImmutable $day, int $timeLeft): void
{
$this->dataService->set(
$this->getKey($player),
[
'player' => $player->getName(),
'timeLeft' => $timeLeft,
'day' => $day->format('Y-m-d'),
]
);
}
public function getPlayTimeRaw(Player $player): ?array
{
return $this->dataService->get($this->getKey($player));
}
public function getPlayTimeOnDay(Player $player, ?\DateTimeImmutable $day = null): false|int
{
if ($day === null) {
$day = new \DateTimeImmutable();
}
$data = $this->getPlayTimeRaw($player);
if (!$data || $data['day'] !== $day->format('Y-m-d')) {
return false;
}
return $data['timeLeft'];
}
}

View File

@ -0,0 +1,52 @@
<?php
namespace App\Service\SimpleDatabase;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
class SimpleDataService
{
private ?array $dataCache = null;
public function __construct(
#[Autowire('%kernel.project_dir%/data.json')]
private readonly string $dataFile
) {}
public function set(string $key, array $data): self
{
if ($this->dataCache === null) {
$this->dataCache = $this->getAll();
}
$this->dataCache[$key] = $data;
$this->writeCache();
return $this;
}
public function get(string $key): ?array
{
return $this->getAll()[$key] ?? null;
}
public function getAll(): array
{
if ($this->dataCache !== null) {
return $this->dataCache;
}
if (!file_exists($this->dataFile)) {
$this->dataCache = [];
} else {
$this->dataCache = json_decode(file_get_contents($this->dataFile), true);
}
return $this->dataCache;
}
private function writeCache(): void
{
file_put_contents($this->dataFile, json_encode($this->dataCache, JSON_PRETTY_PRINT));
}
}