From 7bc2e364a826d3c1903cf1beed4c7229a62ef361 Mon Sep 17 00:00:00 2001 From: ardent Date: Mon, 21 Oct 2024 20:40:46 +0200 Subject: [PATCH] First complete working implementation with time of day and play time rules --- .gitignore | 3 ++ src/Command/CheckCommand.php | 56 +++++++++++++++++++++ src/Command/TestCommand.php | 34 +++++++++++++ src/Dto/Player.php | 15 ++++++ src/Service/Factorio.php | 66 +++++++++++++++++++++++++ src/Service/RuleChecker.php | 29 +++++++++++ src/Service/Rules/AbstractRule.php | 12 +++++ src/Service/Rules/PlayTimeRule.php | 73 ++++++++++++++++++++++++++++ src/Service/Rules/RuleResult.php | 35 +++++++++++++ src/Service/Rules/RuleResultEnum.php | 19 ++++++++ src/Service/Rules/TimeRule.php | 35 +++++++++++++ src/Service/SimpleDataService.php | 38 +++++++++++++++ 12 files changed, 415 insertions(+) create mode 100644 src/Command/CheckCommand.php create mode 100644 src/Command/TestCommand.php create mode 100755 src/Dto/Player.php create mode 100755 src/Service/Factorio.php create mode 100755 src/Service/RuleChecker.php create mode 100755 src/Service/Rules/AbstractRule.php create mode 100755 src/Service/Rules/PlayTimeRule.php create mode 100755 src/Service/Rules/RuleResult.php create mode 100755 src/Service/Rules/RuleResultEnum.php create mode 100755 src/Service/Rules/TimeRule.php create mode 100755 src/Service/SimpleDataService.php diff --git a/.gitignore b/.gitignore index 4daae38..84e8623 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,6 @@ /public/assets/ /assets/vendor/ ###< symfony/asset-mapper ### + +data.json + diff --git a/src/Command/CheckCommand.php b/src/Command/CheckCommand.php new file mode 100644 index 0000000..babb11f --- /dev/null +++ b/src/Command/CheckCommand.php @@ -0,0 +1,56 @@ +factorio->getOnlinePlayers(); + $rules = $this->ruleChecker->getRules(); + foreach ($players as $player) { + $allowed = false; + foreach ($rules as $rule) { + $check = $rule->check($player); +// $this->factorio->whisper($player, sprintf('Checking %s, %s', $rule->getName(), $check)); + if ($check->isWarning()) { + $this->factorio->whisper($player, sprintf('Checked %s. %s', $rule->getName(), $check)); + } + if ($check->isAllowed()) { + $allowed = true; + break; + } + } + if (!$allowed) { + $this->factorio->kickPlayer($player, 'Time has expired on this server for today'); + $io->warning(sprintf('Kicked player %s', $player->getName())); + } + } + + return Command::SUCCESS; + } +} diff --git a/src/Command/TestCommand.php b/src/Command/TestCommand.php new file mode 100644 index 0000000..6710e3a --- /dev/null +++ b/src/Command/TestCommand.php @@ -0,0 +1,34 @@ +factorio->getOnlinePlayers(); + dump($players); + + $this->factorio->kickPlayer("Ardentsword"); + + return Command::SUCCESS; + } +} diff --git a/src/Dto/Player.php b/src/Dto/Player.php new file mode 100755 index 0000000..e98948c --- /dev/null +++ b/src/Dto/Player.php @@ -0,0 +1,15 @@ +name; + } +} \ No newline at end of file diff --git a/src/Service/Factorio.php b/src/Service/Factorio.php new file mode 100755 index 0000000..397bc06 --- /dev/null +++ b/src/Service/Factorio.php @@ -0,0 +1,66 @@ +initPath, ...$args]); + $process->run(); + + return explode(PHP_EOL, $process->getOutput()); + } + + private function runCmd(array|string $args): array + { + if (is_string($args)) { + $args = [$args]; + } + + return $this->runBase(['cmd', ...$args]); + } + + /** + * @return Player[] + */ + public function getOnlinePlayers(): array + { + $output = $this->runBase('players'); + array_shift($output); // Remove header + array_pop($output); // Remove empty line + + $players = []; + foreach ($output as $line) { + $players[] = new Player(explode(' ', trim($line))[0]); + } + + return $players; + } + + public function kickPlayer(Player $player, string $reason = ''): void + { + $this->runCmd(['/kick', $player->getName(), $reason]); + } + + public function whisper(Player $player, string $message): void + { + $this->runCmd(['/whisper', $player->getName(), $message]); + } + + public function say(string $message): void + { + $this->runCmd($message); + } +} \ No newline at end of file diff --git a/src/Service/RuleChecker.php b/src/Service/RuleChecker.php new file mode 100755 index 0000000..8647ac4 --- /dev/null +++ b/src/Service/RuleChecker.php @@ -0,0 +1,29 @@ +timeRule, + $this->playTimeRule, + ]; + } +} \ No newline at end of file diff --git a/src/Service/Rules/AbstractRule.php b/src/Service/Rules/AbstractRule.php new file mode 100755 index 0000000..2e47b3a --- /dev/null +++ b/src/Service/Rules/AbstractRule.php @@ -0,0 +1,12 @@ + "2:00", + ]; + + return $players[$player->getName()] ?? $this->defaultPlayTime; + } + + public function check(Player $player): RuleResult + { + $today = new \DateTimeImmutable(); + $playerData = $this->getPlayerData($player); + + // 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')) { + $this->storePlayerData($player, $today, $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 + $timeLeft = $playerData['timeLeft'] - $this->timeBetweenChecks; + $this->storePlayerData($player, $today, $timeLeft); + + if ($timeLeft <= 0) { + return new RuleResult(RuleResultEnum::DENIED); + } elseif ($timeLeft <= 5) { + return new RuleResult(RuleResultEnum::WARNING, sprintf('%d minute(s) left', $timeLeft)); + }elseif($timeLeft === 30) { + return new RuleResult(RuleResultEnum::WARNING, sprintf('30 minutes left')); + } + + return new RuleResult(RuleResultEnum::ALLOWED); + } + + public function getName(): string + { + 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()) + ); + } +} \ No newline at end of file diff --git a/src/Service/Rules/RuleResult.php b/src/Service/Rules/RuleResult.php new file mode 100755 index 0000000..194b711 --- /dev/null +++ b/src/Service/Rules/RuleResult.php @@ -0,0 +1,35 @@ +state->getString(); + if ($this->message !== '') { + $message .= ': ' . $this->message; + } + return $message; + } + + public function isAllowed(): bool + { + return $this->state !== RuleResultEnum::DENIED; + } + + public function isWarning(): bool + { + return $this->state === RuleResultEnum::WARNING; + } + + public function getMessage(): string + { + return $this->message; + } +} \ No newline at end of file diff --git a/src/Service/Rules/RuleResultEnum.php b/src/Service/Rules/RuleResultEnum.php new file mode 100755 index 0000000..371e84d --- /dev/null +++ b/src/Service/Rules/RuleResultEnum.php @@ -0,0 +1,19 @@ + 'Allowed', + self::WARNING => 'Warning', + self::DENIED => 'Denied', + }; + } +} diff --git a/src/Service/Rules/TimeRule.php b/src/Service/Rules/TimeRule.php new file mode 100755 index 0000000..6a23fe3 --- /dev/null +++ b/src/Service/Rules/TimeRule.php @@ -0,0 +1,35 @@ +start); + $end = new \DateTimeImmutable($this->end); + $fiveMinutesBeforeEnd = $end->sub(new \DateInterval('PT5M')); + + if ($now >= $start && $now <= $end) { + if ($now >= $fiveMinutesBeforeEnd) { + return new RuleResult(RuleResultEnum::WARNING, 'Less than 5 minutes remaining'); + } + return new RuleResult(RuleResultEnum::ALLOWED); + } + + return new RuleResult(RuleResultEnum::DENIED, 'After designed hours'); + } + + public function getName(): string + { + return sprintf('TimeRule, time is between %s and %s', $this->start, $this->end); + } +} \ No newline at end of file diff --git a/src/Service/SimpleDataService.php b/src/Service/SimpleDataService.php new file mode 100755 index 0000000..1d8d344 --- /dev/null +++ b/src/Service/SimpleDataService.php @@ -0,0 +1,38 @@ +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); + } +} \ No newline at end of file