From b21316248572cb27ed1f504529ad6680a473022e Mon Sep 17 00:00:00 2001 From: Daniel Weipert Date: Tue, 2 Jan 2024 20:42:01 +0100 Subject: gemini --- src/App.php | 32 +++-- src/Controller/Building.php | 47 ------- src/Controller/Event.php | 85 ------------- src/Controller/Login.php | 53 -------- src/Controller/Map.php | 43 ------- src/Controller/Unit.php | 242 ------------------------------------- src/Controller/Village.php | 146 ---------------------- src/DB.php | 3 +- src/EventRunner.php | 2 +- src/Router.php | 80 ------------ src/Support/RouteLoader.php | 24 ---- src/View.php | 12 +- src/gemini/Controller/Building.php | 44 +++++++ src/gemini/Controller/Unit.php | 60 +++++++++ src/gemini/Controller/User.php | 118 ++++++++++++++++++ src/gemini/Controller/Village.php | 167 +++++++++++++++++++++++++ src/gemini/Gemini.php | 120 ++++++++++++++++++ src/http/Controller/Building.php | 47 +++++++ src/http/Controller/Event.php | 85 +++++++++++++ src/http/Controller/Login.php | 55 +++++++++ src/http/Controller/Map.php | 43 +++++++ src/http/Controller/Unit.php | 242 +++++++++++++++++++++++++++++++++++++ src/http/Controller/Village.php | 146 ++++++++++++++++++++++ src/http/Http.php | 37 ++++++ src/http/Router.php | 80 ++++++++++++ src/http/Support/RouteLoader.php | 24 ++++ 26 files changed, 1295 insertions(+), 742 deletions(-) delete mode 100644 src/Controller/Building.php delete mode 100644 src/Controller/Event.php delete mode 100644 src/Controller/Login.php delete mode 100644 src/Controller/Map.php delete mode 100644 src/Controller/Unit.php delete mode 100644 src/Controller/Village.php delete mode 100644 src/Router.php delete mode 100644 src/Support/RouteLoader.php create mode 100644 src/gemini/Controller/Building.php create mode 100644 src/gemini/Controller/Unit.php create mode 100644 src/gemini/Controller/User.php create mode 100644 src/gemini/Controller/Village.php create mode 100644 src/gemini/Gemini.php create mode 100644 src/http/Controller/Building.php create mode 100644 src/http/Controller/Event.php create mode 100644 src/http/Controller/Login.php create mode 100644 src/http/Controller/Map.php create mode 100644 src/http/Controller/Unit.php create mode 100644 src/http/Controller/Village.php create mode 100644 src/http/Http.php create mode 100644 src/http/Router.php create mode 100644 src/http/Support/RouteLoader.php (limited to 'src') diff --git a/src/App.php b/src/App.php index 7524d38..9516a06 100644 --- a/src/App.php +++ b/src/App.php @@ -2,34 +2,32 @@ namespace App; -use Symfony\Component\HttpFoundation\Request; +use App\gemini\Gemini; +use App\http\Http; class App { + private $appRunner; + public function __construct() { if ($_ENV['APP_ENV'] === 'development') { error_reporting(E_ALL); } - // Session - session_start(); - - // DB - DB::init(); - - // Router - Router::init(Request::createFromGlobals()); - - // View - View::init(); - - // Events - new EventRunner(); + if (isset($_ENV['GEMINI'])) { + $this->appRunner = new Gemini([ + 'file' => dirname(__DIR__) . '/cert.pem', + 'key' => dirname(__DIR__) . '/key.rsa', + 'passphrase' => '', + ], $_ENV['APP_HOST']); + } + else { + $this->appRunner = new Http(); + } } public function run(): void { - $response = Router::execute(); - $response->send(); + $this->appRunner->run(); } } diff --git a/src/Controller/Building.php b/src/Controller/Building.php deleted file mode 100644 index e141113..0000000 --- a/src/Controller/Building.php +++ /dev/null @@ -1,47 +0,0 @@ -get('x'), $request->get('y')); - $building = Model::getByVillage($village->id, $request->get('type')) ?? Model::getEmpty($village->id, $request->get('type')); - - // resources - foreach ($building->getResourceRequirements() as $resourceType => $resourceValue) { - $village->{$resourceType} -= $resourceValue; - } - $village->updateResources(); - - // event - $event = new Event(); - $event->time = (new \DateTime())->add(\DateInterval::createFromDateString( - $building->getBuildTimeForLevel($building->getEffectiveLevel() + 1) . ' seconds' - )); - $event->villageId = $building->villageId; - $upgradeBuildingEvent = new UpgradeBuilding(); - $upgradeBuildingEvent->event = $event; - $upgradeBuildingEvent->type = $building->type; - $upgradeBuildingEvent->dbInsert(); - - return new RedirectResponse( - Router::generate( - 'village.show', - ['x' => $request->get('x'), 'y' => $request->get('y')] - ) - ); - } -} diff --git a/src/Controller/Event.php b/src/Controller/Event.php deleted file mode 100644 index 1fd304d..0000000 --- a/src/Controller/Event.php +++ /dev/null @@ -1,85 +0,0 @@ - $request->get('x'), 'y' => $request->get('y')] - ) - ); - } - - #[Route(path: '/event/{id}/cancel', methods: ['POST'])] - public function cancel(Request $request): Response - { - $event = DB::fetch(Model::class, 'select * from events where id=:id', ['id' => $request->get('id')])[0] ?? null; - $village = Village::get($event->villageId); - - /**@var SendUnits $sendUnitsEvent*/ - $sendUnitsEvent = DB::fetch(SendUnits::class, 'select * from events_send_units where event_id=:id', ['id' => $event->id])[0] ?? null; - if (! empty($sendUnitsEvent)) { - if ($sendUnitsEvent->type === 'SendBack') { - $cancelTimeDiff = $event->createdAt->diff(new \DateTime()); - $cancelTime = (new \DateTime())->add($cancelTimeDiff); - - $sendUnitsEvent->isCanceled = true; - $sendUnitsEvent->home = $sendUnitsEvent->destination; - $sendUnitsEvent->residence = $sendUnitsEvent->source; - - DB::query( - 'update events set time=:time where id=:id', - ['time' => $cancelTime->format('c'), 'id' => $request->get('id')] - ); - DB::query( - 'update events_send_units set is_canceled=:is_canceled, home=:home, residence=:residence where id=:id', - ['is_canceled' => $sendUnitsEvent->isCanceled, 'home' => $sendUnitsEvent->home, 'residence' => $sendUnitsEvent->residence, 'id' => $sendUnitsEvent->id] - ); - } - - else if ($sendUnitsEvent->type === 'Recall') { - $cancelTimeDiff = $event->createdAt->diff(new \DateTime()); - $cancelTime = (new \DateTime())->add($cancelTimeDiff); - - $sendUnitsEvent->isCanceled = true; - $sendUnitsEvent->home = $sendUnitsEvent->destination; - $sendUnitsEvent->residence = $sendUnitsEvent->source; - - DB::query( - 'update events set time=:time where id=:id', - ['time' => $cancelTime->format('c'), 'id' => $request->get('id')] - ); - DB::query( - 'update events_send_units set is_canceled=:is_canceled, home=:home, residence=:residence where id=:id', - ['is_canceled' => $sendUnitsEvent->isCanceled, 'home' => $sendUnitsEvent->home, 'residence' => $sendUnitsEvent->residence, 'id' => $sendUnitsEvent->id] - ); - } - } - - else { - DB::query('delete from event where id=:id', ['id' => $request->get('id')]); - } - - return new RedirectResponse( - Router::generate( - 'village.show', - ['x' => $village->x, 'y' => $village->y] - ) - ); - } -} diff --git a/src/Controller/Login.php b/src/Controller/Login.php deleted file mode 100644 index 0f360ae..0000000 --- a/src/Controller/Login.php +++ /dev/null @@ -1,53 +0,0 @@ -get('email'); - $user = DB::query('select id,username,password from users where email=:email', ['email' => $email])->fetch(); - - if (empty($user)) { - $password = password_hash($request->get('password'), PASSWORD_DEFAULT); - DB::query('insert into users (username, password, email) values (:username, :password, :email)', ['username' => $email, 'password' => $password, 'email' => $email]); - } else { - $password = $user['password']; - } - - if (password_verify($request->get('password'), $password)) { - $_SESSION['user'] = [ - 'id' => $user['id'], - 'username' => $user['username'], - ]; - - return new RedirectResponse('/villages'); - } - - return new RedirectResponse('/login'); - } - - #[Route(path: '/logout', methods: ['GET'])] - public function logout(Request $request): Response - { - session_unset(); - session_destroy(); - - return new RedirectResponse('/login'); - } -} diff --git a/src/Controller/Map.php b/src/Controller/Map.php deleted file mode 100644 index 6967470..0000000 --- a/src/Controller/Map.php +++ /dev/null @@ -1,43 +0,0 @@ - 1], methods: ['GET'])] - public function region(Request $request): Response - { - $x = $request->get('x'); - $y = $request->get('y'); - $range = $request->get('range'); - - $statement = DB::query( - 'select * from villages where x>=:x1 and x<=:x2 and y>=:y1 and y<=:y2', - [ - 'x1' => $x - $range, - 'x2' => $x + $range, - 'y1' => $y - $range, - 'y2' => $y + $range, - ] - ); - $villages = $statement->fetchAll(); - - $map = []; - foreach ($villages as $village) { - $map[$village['x']][$village['y']] = $village; - } - - return new Response(View::render('map.twig', [ - 'x' => $x, - 'y' => $y, - 'range' => $range, - 'map' => $map, - ])); - } -} diff --git a/src/Controller/Unit.php b/src/Controller/Unit.php deleted file mode 100644 index 0508249..0000000 --- a/src/Controller/Unit.php +++ /dev/null @@ -1,242 +0,0 @@ -get('x'), $request->get('y')); - - /**@var Model $unit*/ - $unit = new (Model::resolveType($request->get('type')))(); - $unit->type = $request->get('type'); - $unit->homeVillageId = $village->id; - - $amount = intval($request->get('amount')); - - if (! Village::canTrain($village, $unit, $amount)) { - return new RedirectResponse( - Router::generate( - 'village.show', - ['x' => $request->get('x'), 'y' => $request->get('y')] - ) - ); - } - - // resources - foreach (Model::getResourceRequirements($unit, $amount) as $resourceType => $resourceValue) { - $village->{$resourceType} -= $resourceValue; - } - $village->updateResources(); - - // event - $event = new Event(); - $event->time = (new \DateTime())->add(\DateInterval::createFromDateString($unit->getBuildTime($amount) . ' seconds')); - $event->villageId = $village->id; - $trainUnitsEvent = new TrainUnits(); - $trainUnitsEvent->event = $event; - $trainUnitsEvent->type = $request->get('type'); - $trainUnitsEvent->amount = $amount; - $trainUnitsEvent->dbInsert(); - - return new RedirectResponse( - Router::generate( - 'village.show', - ['x' => $request->get('x'), 'y' => $request->get('y')] - ) - ); - } - - #[Route(path: '/village/{x}/{y}/unit/{type}/location/{lx}/{ly}/recall', methods: ['POST'])] - public function recall(Request $request): Response - { - $village = Village::getByCoordinates($request->get('x'), $request->get('y')); - $location = Village::getByCoordinates($request->get('lx'), $request->get('ly')); - - /**@var Model $unit*/ - $unit = new (Model::resolveType($request->get('type')))(); - - $amount = intval($request->get('amount')); - $amountUnits = DB::query( - 'select amount from village_units where home_village_id=:home and residence_village_id=:residence and type=:type', - ['home' => $village->id, 'residence' => $location->id, 'type' => $request->get('type')] - )->fetchColumn(); - - if ($amountUnits - $amount > 0) { - $statement = DB::query( - << $amountUnits - $amount, 'home' => $village->id, 'residence' => $location->id, 'type' => $request->get('type')] - ); - } else if ($amountUnits - $amount === 0) { - DB::query( - << $village->id, 'residence' => $location->id, 'type' => $request->get('type')] - ); - } - - // event - $event = new Event(); - $event->time = (new \DateTime())->add( - \DateInterval::createFromDateString( - Model::getTravelTime($unit, Village::getDistance($village->x, $village->y, $location->x, $location->y)) - . ' seconds' - ) - ); - $event->villageId = $village->id; - $sendUnitsEvent = new SendUnits(); - $sendUnitsEvent->event = $event; - $sendUnitsEvent->type = 'Recall'; - $sendUnitsEvent->unit = $request->get('type'); - $sendUnitsEvent->amount = $amount; - $sendUnitsEvent->source = $location->id; - $sendUnitsEvent->destination = $village->id; - $sendUnitsEvent->dbInsert(); - - - return new RedirectResponse( - Router::generate( - 'village.show', - ['x' => $request->get('x'), 'y' => $request->get('y')] - ) - ); - } - - #[Route(path: '/village/{x}/{y}/unit/{type}/location/{lx}/{ly}/send-back', methods: ['POST'])] - public function sendBack(Request $request): Response - { - $village = Village::getByCoordinates($request->get('x'), $request->get('y')); - $location = Village::getByCoordinates($request->get('lx'), $request->get('ly')); - - /**@var Model $unit*/ - $unit = new (Model::resolveType($request->get('type')))(); - - $amount = intval($request->get('amount')); - $amountUnits = DB::query( - 'select amount from village_units where home_village_id=:home and residence_village_id=:residence and type=:type', - ['home' => $location->id, 'residence' => $village->id, 'type' => $request->get('type')] - )->fetchColumn(); - - if ($amountUnits - $amount > 0) { - $statement = DB::query( - << $amountUnits - $amount, 'home' => $location->id, 'residence' => $village->id, 'type' => $request->get('type')] - ); - } else if ($amountUnits - $amount === 0) { - DB::query( - << $location->id, 'residence' => $village->id, 'type' => $request->get('type')] - ); - } - - // event - $event = new Event(); - $event->time = (new \DateTime())->add( - \DateInterval::createFromDateString( - Model::getTravelTime($unit, Village::getDistance($village->x, $village->y, $location->x, $location->y)) - . ' seconds' - ) - ); - $event->villageId = $village->id; - $sendUnitsEvent = new SendUnits(); - $sendUnitsEvent->event = $event; - $sendUnitsEvent->type = 'SendBack'; - $sendUnitsEvent->unit = $request->get('type'); - $sendUnitsEvent->amount = $amount; - $sendUnitsEvent->source = $village->id; - $sendUnitsEvent->destination = $location->id; - $sendUnitsEvent->dbInsert(); - - - return new RedirectResponse( - Router::generate( - 'village.show', - ['x' => $request->get('x'), 'y' => $request->get('y')] - ) - ); - } - - #[Route(path: '/village/{x}/{y}/send-units', methods: ['POST'])] - public function sendUnits(Request $request): Response - { - $village = Village::getByCoordinates($request->get('x'), $request->get('y')); - $destination = Village::get($request->get('village')); - - /**@var Model $unit*/ - $unit = new (Model::resolveType($request->get('unit')))(); - - $amount = intval($request->get('amount')); - $amountUnits = DB::query( - 'select amount from village_units where home_village_id=:home and residence_village_id=:home and type=:type', - ['home' => $village->id, 'type' => $request->get('unit')] - )->fetchColumn(); - - if ($amountUnits - $amount > 0) { - $statement = DB::query( - << $amountUnits - $amount, 'home' => $village->id, 'type' => $request->get('unit')] - ); - } else if ($amountUnits - $amount === 0) { - DB::query( - << $village->id, 'type' => $request->get('unit')] - ); - } - - DB::query( - 'insert into village_units (amount, type, home_village_id, residence_village_id, is_traveling) values (:amount, :type, :home, :home, true)', - ['amount' => $amount, 'type' => $request->get('unit'), 'home' => $village->id] - ); - - // event - $event = new Event(); - $event->time = (new \DateTime())->add( - \DateInterval::createFromDateString( - Model::getTravelTime($unit, Village::getDistance($village->x, $village->y, $destination->x, $destination->y)) - . ' seconds' - ) - ); - $event->villageId = $village->id; - $sendUnitsEvent = new SendUnits(); - $sendUnitsEvent->event = $event; - $sendUnitsEvent->type = $request->get('type'); - $sendUnitsEvent->unit = $request->get('unit'); - $sendUnitsEvent->amount = $amount; - $sendUnitsEvent->source = $village->id; - $sendUnitsEvent->destination = $destination->id; - $sendUnitsEvent->dbInsert(); - - - return new RedirectResponse( - Router::generate( - 'village.show', - ['x' => $request->get('x'), 'y' => $request->get('y')] - ) - ); - } -} diff --git a/src/Controller/Village.php b/src/Controller/Village.php deleted file mode 100644 index c678779..0000000 --- a/src/Controller/Village.php +++ /dev/null @@ -1,146 +0,0 @@ - $_SESSION['user']['id']] - ); - - return new Response(View::render('villages.twig', [ - 'villages' => $villages, - ])); - } - - #[Route(path: '/village/{x}/{y}', methods: ['GET'])] - public function show(Request $request): Response - { - $village = Model::getByCoordinates($request->get('x'), $request->get('y')); - - if (! Guard::ownsVillage($village->id)) { - return new Response(View::render('error.twig', ['message' => 'Insufficient permission']), 403); - } - - $events = []; - - $eventsBuilding = DB::query( - << $village->id] - )->fetchAll(); - - foreach ($eventsBuilding as $row) { - $events['UpgradeBuilding'][$row['type']][] = DB::convertToModel(UpgradeBuilding::class, $row); - } - - $eventsUnits = DB::query( - << $village->id] - )->fetchAll(); - - foreach ($eventsUnits as $row) { - $events['TrainUnits'][] = DB::convertToModel(TrainUnits::class, $row); - } - - $eventsUnitsSendOwn = DB::query( - << $village->id] - )->fetchAll(); - - $eventsUnitsSendOther = DB::query( - << $village->id] - )->fetchAll(); - - foreach ([...$eventsUnitsSendOwn, ...$eventsUnitsSendOther] as $row) { - $events['SendUnits'][] = DB::convertToModel(SendUnits::class, $row);; - } - - $buildings = []; - foreach (Model::getBuildings($village->id, true) as $building) { - $buildings[$building->type] = $building; - } - - - return new Response(View::render('village.twig', [ - 'village' => $village, - 'events' => $events, - 'buildings' => $buildings, - 'villages' => DB::fetch(Model::class, "select * from villages where id!=:id", ['id' => $village->id]), - ])); - } - - #[Route(path: '/village/{x}/{y}/storage/config', methods: ['POST'])] - public function storageConfig(Request $request): Response - { - $village = Model::getByCoordinates($request->get('x'), $request->get('y')); - - // calculate to max 100% - $wood = intval($request->get('wood')); - $clay = intval($request->get('clay')); - $iron = intval($request->get('iron')); - $food = intval($request->get('food')); - $total = $wood + $clay + $iron + $food; - $woodPercent = $wood / $total; - $clayPercent = $clay / $total; - $ironPercent = $iron / $total; - $foodPercent = $food / $total; - - $wood = round($woodPercent * 100); - $clay = round($clayPercent * 100); - $iron = round($ironPercent * 100); - $food = round($foodPercent * 100); - $newTotal = $wood+$clay+$iron+$food; - $food += (100 - $newTotal); - - DB::query( - << $wood, 'clay' => $clay, 'iron' => $iron, 'food' => $food, 'id' => $village->id] - ); - - return new RedirectResponse( - Router::generate( - 'village.show', - ['x' => $request->get('x'), 'y' => $request->get('y')] - ) - ); - } -} diff --git a/src/DB.php b/src/DB.php index 82a81c4..f91e934 100644 --- a/src/DB.php +++ b/src/DB.php @@ -9,11 +9,12 @@ class DB { { $driver = $_ENV['DB_DRIVER'] ?? 'pgsql'; $host = $_ENV['DB_HOST'] ?? 'db'; + $port = $_ENV['DB_PORT'] ?? 5432; $dbname = $_ENV['DB_NAME']; $user = $_ENV['DB_USER']; $password = $_ENV['DB_PASSWORD']; - self::$connection = new \PDO("pgsql:host=$host;dbname=$dbname", $user, $password); + self::$connection = new \PDO("pgsql:host=$host;port=$port;dbname=$dbname", $user, $password); } /** diff --git a/src/EventRunner.php b/src/EventRunner.php index 7dd773d..2f2fd8b 100644 --- a/src/EventRunner.php +++ b/src/EventRunner.php @@ -82,7 +82,7 @@ class EventRunner } $diff = (new \DateTime())->diff($lastTick); - $tickMultiplier = $diff->i; + $tickMultiplier = $diff->i + 60*$diff->h + 60*24*$diff->d; if ($tickMultiplier > 0) { $villages = DB::fetch(Village::class, 'select id,wood,clay,iron,food from villages'); diff --git a/src/Router.php b/src/Router.php deleted file mode 100644 index 8b24000..0000000 --- a/src/Router.php +++ /dev/null @@ -1,80 +0,0 @@ -fromRequest($request); - - self::$routes = new RouteCollection(); - $loader = new AnnotationFileLoader(new FileLocator(), new RouteLoader()); - $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator(__DIR__ . '/Controller')); - foreach ($iterator as $file) { - /**@var \SplFileInfo $file*/ - if (in_array($file->getFilename(), ['.', '..'])) continue; - - $collection = $loader->load($file->getPathname(), 'attribute'); - self::$routes->addCollection($collection); - } - } - - public static function execute(): Response - { - try { - $matcher = new UrlMatcher(self::$routes, self::$context); - $match = $matcher->matchRequest(self::$request); - - foreach ($match as $key => $value) { - if (str_starts_with($key, '_')) continue; - - self::$request->query->set($key, $value); - } - - /**@var \ReflectionClass $class*/ - $class = $match['_']['class']; - /**@var \ReflectionMethod $method*/ - $method = $match['_']['method']; - - return ($class->newInstance())->{$method->getName()}(self::$request); - } catch (ResourceNotFoundException $exception) { - return new Response('404', 404); - } catch (MethodNotAllowedException $exception) { - return new Response('403', 403); - } catch (\Exception $exception) { - return new Response('500: ' . $exception->getMessage(), 500); - } - } - - /** - * @param string $name - * @param array $parameters - * @param int $referenceType - */ - public static function generate(string $name, array $parameters = [], int $referenceType = 1): string - { - $generator = new UrlGenerator(self::$routes, self::$context); - - return $generator->generate($name, $parameters, $referenceType); - } -} diff --git a/src/Support/RouteLoader.php b/src/Support/RouteLoader.php deleted file mode 100644 index ba124c5..0000000 --- a/src/Support/RouteLoader.php +++ /dev/null @@ -1,24 +0,0 @@ -setDefault('_', compact('class', 'method', 'annotation')); - } - - protected function getDefaultRouteName(\ReflectionClass $class, \ReflectionMethod $method) - { - $name = parent::getDefaultRouteName($class, $method); - - return str_replace( - '_', - '.', - str_replace('app_controller_', '', $name) - ); - } -} diff --git a/src/View.php b/src/View.php index 7862b12..1c29a67 100644 --- a/src/View.php +++ b/src/View.php @@ -9,11 +9,14 @@ use Twig\TwigFilter; class View { - private static Environment $twig; + public static Environment $twig; public static function init(): void { - $loader = new FilesystemLoader(dirname(__DIR__) . '/views'); + $loader = new FilesystemLoader( + dirname(__DIR__) . '/views/' . + (isset($_ENV['GEMINI']) ? 'gemini' : 'http') + ); self::$twig = new Environment($loader, [ 'debug' => $_ENV['APP_ENV'] === 'development', ]); @@ -24,8 +27,11 @@ class View self::$twig->addFilter(new TwigFilter('buildTime', function ($buildTime) { return @sprintf('%02d:%02d:%02d', $buildTime / 3600, ($buildTime / 60) % 60, $buildTime % 60); })); + } - self::$twig->addGlobal('session', $_SESSION); + public static function addGlobal(string $name, mixed $value): void + { + self::$twig->addGlobal($name, $value); } /** diff --git a/src/gemini/Controller/Building.php b/src/gemini/Controller/Building.php new file mode 100644 index 0000000..9827d00 --- /dev/null +++ b/src/gemini/Controller/Building.php @@ -0,0 +1,44 @@ +get('x'), $request->get('y')); + $building = Model::getByVillage($village->id, $request->get('type')) ?? Model::getEmpty($village->id, $request->get('type')); + + // resources + foreach ($building->getResourceRequirements() as $resourceType => $resourceValue) { + $village->{$resourceType} -= $resourceValue; + } + $village->updateResources(); + + // event + $event = new Event(); + $event->time = (new \DateTime())->add(\DateInterval::createFromDateString( + $building->getBuildTimeForLevel($building->getEffectiveLevel() + 1) . ' seconds' + )); + $event->villageId = $building->villageId; + $upgradeBuildingEvent = new UpgradeBuilding(); + $upgradeBuildingEvent->event = $event; + $upgradeBuildingEvent->type = $building->type; + $upgradeBuildingEvent->dbInsert(); + + + return new Response( + statusCode: Status::REDIRECT_TEMPORARY, # correct response code? + meta: "/village/{$village->x}/{$village->y}" + ); + } +} diff --git a/src/gemini/Controller/Unit.php b/src/gemini/Controller/Unit.php new file mode 100644 index 0000000..c04079a --- /dev/null +++ b/src/gemini/Controller/Unit.php @@ -0,0 +1,60 @@ +get('input'))) { + return new Response(statusCode: Status::INPUT, meta: 'Amount'); + } + + $village = Village::getByCoordinates($request->get('x'), $request->get('y')); + + /**@var Model $unit*/ + $unit = new (Model::resolveType($request->get('type')))(); + $unit->type = $request->get('type'); + $unit->homeVillageId = $village->id; + + $amount = intval($request->get('input')); + + if (! Village::canTrain($village, $unit, $amount)) { + return new Response( + statusCode: Status::REDIRECT_TEMPORARY, + meta: "/village/{$village->x}/{$village->y}" + ); + } + + // resources + foreach (Model::getResourceRequirements($unit, $amount) as $resourceType => $resourceValue) { + $village->{$resourceType} -= $resourceValue; + } + $village->updateResources(); + + // event + $event = new Event(); + $event->time = (new \DateTime())->add(\DateInterval::createFromDateString($unit->getBuildTime($amount) . ' seconds')); + $event->villageId = $village->id; + $trainUnitsEvent = new TrainUnits(); + $trainUnitsEvent->event = $event; + $trainUnitsEvent->type = $request->get('type'); + $trainUnitsEvent->amount = $amount; + $trainUnitsEvent->dbInsert(); + + + return new Response( + statusCode: Status::REDIRECT_TEMPORARY, + meta: "/village/{$village->x}/{$village->y}" + ); + } +} diff --git a/src/gemini/Controller/User.php b/src/gemini/Controller/User.php new file mode 100644 index 0000000..a870b88 --- /dev/null +++ b/src/gemini/Controller/User.php @@ -0,0 +1,118 @@ +getClientCertificate() === null) { + return false; + } + + return DB::query( + << $request->getClientCertificate()->getFingerprint()] + )->fetch(); + } + + public function create(Request $request): array|bool + { + DB::query( + 'insert into users (username, password, email) values (:username, :fingerprint, :email)', + [ + 'username' => md5($request->getClientCertificate()->getFingerprint()), + 'fingerprint' => $request->getClientCertificate()->getFingerprint(), + 'email' => '(no email)', + ] + ); + $userId = DB::query('select id from users where password=:password', ['password' => $request->getClientCertificate()->getFingerprint()])->fetchColumn(); + + DB::query( + 'insert into users_gemini (certificate, user_id) values (:fingerprint, :userId)', + ['fingerprint' => $request->getClientCertificate()->getFingerprint(), 'userId' => $userId] + ); + + // also insert new village at random free coordinates + DB::query( + 'insert into villages (name, x, y, wood, clay, iron, food, satisfaction) values (:name, :x, :y, :wood, :clay, :iron, :food, :satisfaction)', + [ + 'name' => substr(md5(rand()), 0, 6), + 'x' => rand(0, 100), + 'y' => rand(0, 100), + 'wood' => 500, + 'clay' => 500, + 'iron' => 500, + 'food' => 500, + 'satisfaction' => 100, + ] + ); + $villageId = DB::query('select id from villages order by id desc limit 1')->fetchColumn(); + + DB::query( + 'insert into user_villages (user_id, village_id) values (:userId, :villageId)', + ['userId' => $userId, 'villageId' => $villageId] + ); + + // insert base buildings + DB::query( + 'insert into village_buildings (level, type, village_id) values (:level, :type, :villageId)', + ['level' => 1, 'type' => 'TownHall', 'villageId' => $villageId] + ); + + DB::query( + 'insert into village_buildings (level, type, village_id) values (:level, :type, :villageId)', + ['level' => 1, 'type' => 'Storage', 'villageId' => $villageId] + ); + DB::query( + 'insert into village_storage_config (wood, clay, iron, food, village_id) values (:wood, :clay, :iron, :food, :villageId)', + ['wood' => 25, 'clay' => 25, 'iron' => 25, 'food' => 25, 'villageId' => $villageId] + ); + + DB::query( + 'insert into village_buildings (level, type, village_id) values (:level, :type, :villageId)', + ['level' => 1, 'type' => 'WoodCutter', 'villageId' => $villageId] + ); + DB::query( + 'insert into village_units (amount, type, is_traveling, home_village_id, residence_village_id) values (:amount, :type, false, :villageId, :villageId)', + ['amount' => 1, 'type' => 'WoodCutter', 'villageId' => $villageId] + ); + + DB::query( + 'insert into village_buildings (level, type, village_id) values (:level, :type, :villageId)', + ['level' => 1, 'type' => 'ClayPit', 'villageId' => $villageId] + ); + DB::query( + 'insert into village_units (amount, type, is_traveling, home_village_id, residence_village_id) values (:amount, :type, false, :villageId, :villageId)', + ['amount' => 1, 'type' => 'PitWorker', 'villageId' => $villageId] + ); + + DB::query( + 'insert into village_buildings (level, type, village_id) values (:level, :type, :villageId)', + ['level' => 1, 'type' => 'IronMine', 'villageId' => $villageId] + ); + DB::query( + 'insert into village_units (amount, type, is_traveling, home_village_id, residence_village_id) values (:amount, :type, false, :villageId, :villageId)', + ['amount' => 1, 'type' => 'Miner', 'villageId' => $villageId] + ); + + DB::query( + 'insert into village_buildings (level, type, village_id) values (:level, :type, :villageId)', + ['level' => 1, 'type' => 'Farm', 'villageId' => $villageId] + ); + DB::query( + 'insert into village_units (amount, type, is_traveling, home_village_id, residence_village_id) values (:amount, :type, false, :villageId, :villageId)', + ['amount' => 1, 'type' => 'Farmer', 'villageId' => $villageId] + ); + + + return $this->get($request); + } +} diff --git a/src/gemini/Controller/Village.php b/src/gemini/Controller/Village.php new file mode 100644 index 0000000..9b27561 --- /dev/null +++ b/src/gemini/Controller/Village.php @@ -0,0 +1,167 @@ + $_SESSION['user']['id']] + ); + + + return new Response(body: View::render('villages.twig', [ + 'villages' => $villages, + ])); + } + + //#[Route(path: '/village/{x}/{y}', methods: ['GET'])] + public function show(Request $request): Response + { + $village = Model::getByCoordinates($request->get('x'), $request->get('y')); + + if (! Guard::ownsVillage($village->id)) { + return new Response(body: View::render('error.twig', ['message' => 'Insufficient permission'])); + } + + $events = []; + + $eventsBuilding = DB::query( + << $village->id] + )->fetchAll(); + + foreach ($eventsBuilding as $row) { + $events['UpgradeBuilding'][$row['type']][] = DB::convertToModel(UpgradeBuilding::class, $row); + } + + $eventsUnits = DB::query( + << $village->id] + )->fetchAll(); + + foreach ($eventsUnits as $row) { + $events['TrainUnits'][] = DB::convertToModel(TrainUnits::class, $row); + } + + $eventsUnitsSendOwn = DB::query( + << $village->id] + )->fetchAll(); + + $eventsUnitsSendOther = DB::query( + << $village->id] + )->fetchAll(); + + foreach ([...$eventsUnitsSendOwn, ...$eventsUnitsSendOther] as $row) { + $events['SendUnits'][] = DB::convertToModel(SendUnits::class, $row);; + } + + $buildings = []; + foreach (Model::getBuildings($village->id, true) as $building) { + $buildings[$building->type] = $building; + } + + + return new Response(body: View::render('village.twig', [ + 'village' => $village, + 'events' => $events, + 'buildings' => $buildings, + 'villages' => DB::fetch(Model::class, "select * from villages where id!=:id", ['id' => $village->id]), + ])); + } + + // #[Route(path: '/village/{x}/{y}/storage/config', methods: ['POST'])] + public function storageConfig(Request $request): Response + { + $village = Model::getByCoordinates($request->get('x'), $request->get('y')); + $type = $request->get('type'); + + if (empty($type)) { + return new Response(body: View::render('storage.twig', [ + 'village' => $village, + ])); + } + + if (empty($request->get('input'))) { + return new Response(statusCode: Status::INPUT, meta: "$type percent?"); + } + + $input = intval($request->get('input')); + + // calculate to max 100% + $allTypes = ['wood', 'clay', 'iron', 'food']; + $allOtherTypes = array_diff($allTypes, [$type]); + + $storageConfig = $village->getStorageConfig($village->id); + + $values = []; + foreach ($allTypes as $resourceType) { + $values[$resourceType] = $storageConfig->$resourceType; + } + $values[$type] = $input; + + $total = 0; + foreach ($values as $value) { + $total += $value; + } + + foreach ($values as $resourceType => $value) { + $values[$resourceType] = round(($value / $total) * 100); + } + + if ($values[$type] !== $input) { + $values[$type] = $input; + } + + $newTotal = array_sum($values); + $values['food'] += (100 - $newTotal); + + DB::query( + << $values['wood'], 'clay' => $values['clay'], 'iron' => $values['iron'], 'food' => $values['food'], 'id' => $village->id] + ); + + + return new Response( + statusCode: Status::REDIRECT_TEMPORARY, + meta: "/village/{$village->x}/{$village->y}/storage/config" + ); + } +} diff --git a/src/gemini/Gemini.php b/src/gemini/Gemini.php new file mode 100644 index 0000000..41b8716 --- /dev/null +++ b/src/gemini/Gemini.php @@ -0,0 +1,120 @@ +certificate = $certificate; + $this->hostname = $hostname; + + global $_SESSION; + $_SESSION = []; + + DB::init(); + + View::init(); + View::addGlobal('session', $_SESSION); + } + + public function run(): void + { + $server = new Server($this->certificate, $this->hostname, [ + 'client_certificate_support_workaround' => true, + ]); + + $server->onRequest(function (Response $response, Request $request) { + new EventRunner(); + + // auth + if ($request->getClientCertificate() === null) { + return new Response( + statusCode: Status::CLIENT_CERTIFICATE_REQUIRED, + meta: 'Attach a client certificate to log in' + ); + } + + $userController = new User(); + $user = $userController->get($request); + if (empty($user)) { + $user = $userController->create($request); + } + + global $_SESSION; + $_SESSION['user'] = [ + 'id' => $user['id'], + 'username' => $user['username'], + ]; + View::addGlobal('session', $_SESSION); + + + // routes + if ($request->getPath() == '/villages') { + $villageController = new Village(); + $response = $villageController->list($request); + } + + else if (preg_match('@village/(\d+)/(\d+)/storage/config/?(\w+)?@', $request->getPath(), $routeMatch)) { + $request + ->set('x', $routeMatch[1]) + ->set('y', $routeMatch[2]); + + if (isset($routeMatch[3])) { + $request->set('type', $routeMatch[3]); + } + + $villageController = new Village(); + $response = $villageController->storageConfig($request); + } + + else if (preg_match('@village/(\d+)/(\d+)/building/(\w+)/level-up@', $request->getPath(), $routeMatch)) { + $request + ->set('x', $routeMatch[1]) + ->set('y', $routeMatch[2]) + ->set('type', $routeMatch[3]); + + $buildingController = new Building(); + $response = $buildingController->levelUp($request); + } + + else if (preg_match('@village/(\d+)/(\d+)/unit/(\w+)/create@', $request->getPath(), $routeMatch)) { + $request + ->set('x', $routeMatch[1]) + ->set('y', $routeMatch[2]) + ->set('type', $routeMatch[3]); + + $unitController = new Unit(); + $response = $unitController->train($request); + } + + else if (preg_match('@village/(\d+)/(\d+)@', $request->getPath(), $routeMatch)) { + $request + ->set('x', $routeMatch[1]) + ->set('y', $routeMatch[2]); + + $villageController = new Village(); + $response = $villageController->show($request); + } + + return $response; + }); + + $server->listen(); + } +} diff --git a/src/http/Controller/Building.php b/src/http/Controller/Building.php new file mode 100644 index 0000000..4a59f0e --- /dev/null +++ b/src/http/Controller/Building.php @@ -0,0 +1,47 @@ +get('x'), $request->get('y')); + $building = Model::getByVillage($village->id, $request->get('type')) ?? Model::getEmpty($village->id, $request->get('type')); + + // resources + foreach ($building->getResourceRequirements() as $resourceType => $resourceValue) { + $village->{$resourceType} -= $resourceValue; + } + $village->updateResources(); + + // event + $event = new Event(); + $event->time = (new \DateTime())->add(\DateInterval::createFromDateString( + $building->getBuildTimeForLevel($building->getEffectiveLevel() + 1) . ' seconds' + )); + $event->villageId = $building->villageId; + $upgradeBuildingEvent = new UpgradeBuilding(); + $upgradeBuildingEvent->event = $event; + $upgradeBuildingEvent->type = $building->type; + $upgradeBuildingEvent->dbInsert(); + + return new RedirectResponse( + Router::generate( + 'village.show', + ['x' => $request->get('x'), 'y' => $request->get('y')] + ) + ); + } +} diff --git a/src/http/Controller/Event.php b/src/http/Controller/Event.php new file mode 100644 index 0000000..070d449 --- /dev/null +++ b/src/http/Controller/Event.php @@ -0,0 +1,85 @@ + $request->get('x'), 'y' => $request->get('y')] + ) + ); + } + + #[Route(path: '/event/{id}/cancel', methods: ['POST'])] + public function cancel(Request $request): Response + { + $event = DB::fetch(Model::class, 'select * from events where id=:id', ['id' => $request->get('id')])[0] ?? null; + $village = Village::get($event->villageId); + + /**@var SendUnits $sendUnitsEvent*/ + $sendUnitsEvent = DB::fetch(SendUnits::class, 'select * from events_send_units where event_id=:id', ['id' => $event->id])[0] ?? null; + if (! empty($sendUnitsEvent)) { + if ($sendUnitsEvent->type === 'SendBack') { + $cancelTimeDiff = $event->createdAt->diff(new \DateTime()); + $cancelTime = (new \DateTime())->add($cancelTimeDiff); + + $sendUnitsEvent->isCanceled = true; + $sendUnitsEvent->home = $sendUnitsEvent->destination; + $sendUnitsEvent->residence = $sendUnitsEvent->source; + + DB::query( + 'update events set time=:time where id=:id', + ['time' => $cancelTime->format('c'), 'id' => $request->get('id')] + ); + DB::query( + 'update events_send_units set is_canceled=:is_canceled, home=:home, residence=:residence where id=:id', + ['is_canceled' => $sendUnitsEvent->isCanceled, 'home' => $sendUnitsEvent->home, 'residence' => $sendUnitsEvent->residence, 'id' => $sendUnitsEvent->id] + ); + } + + else if ($sendUnitsEvent->type === 'Recall') { + $cancelTimeDiff = $event->createdAt->diff(new \DateTime()); + $cancelTime = (new \DateTime())->add($cancelTimeDiff); + + $sendUnitsEvent->isCanceled = true; + $sendUnitsEvent->home = $sendUnitsEvent->destination; + $sendUnitsEvent->residence = $sendUnitsEvent->source; + + DB::query( + 'update events set time=:time where id=:id', + ['time' => $cancelTime->format('c'), 'id' => $request->get('id')] + ); + DB::query( + 'update events_send_units set is_canceled=:is_canceled, home=:home, residence=:residence where id=:id', + ['is_canceled' => $sendUnitsEvent->isCanceled, 'home' => $sendUnitsEvent->home, 'residence' => $sendUnitsEvent->residence, 'id' => $sendUnitsEvent->id] + ); + } + } + + else { + DB::query('delete from event where id=:id', ['id' => $request->get('id')]); + } + + return new RedirectResponse( + Router::generate( + 'village.show', + ['x' => $village->x, 'y' => $village->y] + ) + ); + } +} diff --git a/src/http/Controller/Login.php b/src/http/Controller/Login.php new file mode 100644 index 0000000..8c04d85 --- /dev/null +++ b/src/http/Controller/Login.php @@ -0,0 +1,55 @@ +get('email'); + $user = DB::query('select id,username,password from users where email=:email or username=:email', ['email' => $email])->fetch(); + + if (empty($user)) { + $password = password_hash($request->get('password'), PASSWORD_DEFAULT); + DB::query('insert into users (username, password, email) values (:username, :password, :email)', ['username' => $email, 'password' => $password, 'email' => $email]); + + // TODO: also insert new village at random free coordinates + } else { + $password = $user['password']; + } + + if (password_verify($request->get('password'), $password)) { + $_SESSION['user'] = [ + 'id' => $user['id'], + 'username' => $user['username'], + ]; + + return new RedirectResponse('/villages'); + } + + return new RedirectResponse('/login'); + } + + #[Route(path: '/logout', methods: ['GET'])] + public function logout(Request $request): Response + { + session_unset(); + session_destroy(); + + return new RedirectResponse('/login'); + } +} diff --git a/src/http/Controller/Map.php b/src/http/Controller/Map.php new file mode 100644 index 0000000..69d23e1 --- /dev/null +++ b/src/http/Controller/Map.php @@ -0,0 +1,43 @@ + 1], methods: ['GET'])] + public function region(Request $request): Response + { + $x = $request->get('x'); + $y = $request->get('y'); + $range = $request->get('range'); + + $statement = DB::query( + 'select * from villages where x>=:x1 and x<=:x2 and y>=:y1 and y<=:y2', + [ + 'x1' => $x - $range, + 'x2' => $x + $range, + 'y1' => $y - $range, + 'y2' => $y + $range, + ] + ); + $villages = $statement->fetchAll(); + + $map = []; + foreach ($villages as $village) { + $map[$village['x']][$village['y']] = $village; + } + + return new Response(View::render('map.twig', [ + 'x' => $x, + 'y' => $y, + 'range' => $range, + 'map' => $map, + ])); + } +} diff --git a/src/http/Controller/Unit.php b/src/http/Controller/Unit.php new file mode 100644 index 0000000..c314cda --- /dev/null +++ b/src/http/Controller/Unit.php @@ -0,0 +1,242 @@ +get('x'), $request->get('y')); + + /**@var Model $unit*/ + $unit = new (Model::resolveType($request->get('type')))(); + $unit->type = $request->get('type'); + $unit->homeVillageId = $village->id; + + $amount = intval($request->get('amount')); + + if (! Village::canTrain($village, $unit, $amount)) { + return new RedirectResponse( + Router::generate( + 'village.show', + ['x' => $request->get('x'), 'y' => $request->get('y')] + ) + ); + } + + // resources + foreach (Model::getResourceRequirements($unit, $amount) as $resourceType => $resourceValue) { + $village->{$resourceType} -= $resourceValue; + } + $village->updateResources(); + + // event + $event = new Event(); + $event->time = (new \DateTime())->add(\DateInterval::createFromDateString($unit->getBuildTime($amount) . ' seconds')); + $event->villageId = $village->id; + $trainUnitsEvent = new TrainUnits(); + $trainUnitsEvent->event = $event; + $trainUnitsEvent->type = $request->get('type'); + $trainUnitsEvent->amount = $amount; + $trainUnitsEvent->dbInsert(); + + return new RedirectResponse( + Router::generate( + 'village.show', + ['x' => $request->get('x'), 'y' => $request->get('y')] + ) + ); + } + + #[Route(path: '/village/{x}/{y}/unit/{type}/location/{lx}/{ly}/recall', methods: ['POST'])] + public function recall(Request $request): Response + { + $village = Village::getByCoordinates($request->get('x'), $request->get('y')); + $location = Village::getByCoordinates($request->get('lx'), $request->get('ly')); + + /**@var Model $unit*/ + $unit = new (Model::resolveType($request->get('type')))(); + + $amount = intval($request->get('amount')); + $amountUnits = DB::query( + 'select amount from village_units where home_village_id=:home and residence_village_id=:residence and type=:type', + ['home' => $village->id, 'residence' => $location->id, 'type' => $request->get('type')] + )->fetchColumn(); + + if ($amountUnits - $amount > 0) { + $statement = DB::query( + << $amountUnits - $amount, 'home' => $village->id, 'residence' => $location->id, 'type' => $request->get('type')] + ); + } else if ($amountUnits - $amount === 0) { + DB::query( + << $village->id, 'residence' => $location->id, 'type' => $request->get('type')] + ); + } + + // event + $event = new Event(); + $event->time = (new \DateTime())->add( + \DateInterval::createFromDateString( + Model::getTravelTime($unit, Village::getDistance($village->x, $village->y, $location->x, $location->y)) + . ' seconds' + ) + ); + $event->villageId = $village->id; + $sendUnitsEvent = new SendUnits(); + $sendUnitsEvent->event = $event; + $sendUnitsEvent->type = 'Recall'; + $sendUnitsEvent->unit = $request->get('type'); + $sendUnitsEvent->amount = $amount; + $sendUnitsEvent->source = $location->id; + $sendUnitsEvent->destination = $village->id; + $sendUnitsEvent->dbInsert(); + + + return new RedirectResponse( + Router::generate( + 'village.show', + ['x' => $request->get('x'), 'y' => $request->get('y')] + ) + ); + } + + #[Route(path: '/village/{x}/{y}/unit/{type}/location/{lx}/{ly}/send-back', methods: ['POST'])] + public function sendBack(Request $request): Response + { + $village = Village::getByCoordinates($request->get('x'), $request->get('y')); + $location = Village::getByCoordinates($request->get('lx'), $request->get('ly')); + + /**@var Model $unit*/ + $unit = new (Model::resolveType($request->get('type')))(); + + $amount = intval($request->get('amount')); + $amountUnits = DB::query( + 'select amount from village_units where home_village_id=:home and residence_village_id=:residence and type=:type', + ['home' => $location->id, 'residence' => $village->id, 'type' => $request->get('type')] + )->fetchColumn(); + + if ($amountUnits - $amount > 0) { + $statement = DB::query( + << $amountUnits - $amount, 'home' => $location->id, 'residence' => $village->id, 'type' => $request->get('type')] + ); + } else if ($amountUnits - $amount === 0) { + DB::query( + << $location->id, 'residence' => $village->id, 'type' => $request->get('type')] + ); + } + + // event + $event = new Event(); + $event->time = (new \DateTime())->add( + \DateInterval::createFromDateString( + Model::getTravelTime($unit, Village::getDistance($village->x, $village->y, $location->x, $location->y)) + . ' seconds' + ) + ); + $event->villageId = $village->id; + $sendUnitsEvent = new SendUnits(); + $sendUnitsEvent->event = $event; + $sendUnitsEvent->type = 'SendBack'; + $sendUnitsEvent->unit = $request->get('type'); + $sendUnitsEvent->amount = $amount; + $sendUnitsEvent->source = $village->id; + $sendUnitsEvent->destination = $location->id; + $sendUnitsEvent->dbInsert(); + + + return new RedirectResponse( + Router::generate( + 'village.show', + ['x' => $request->get('x'), 'y' => $request->get('y')] + ) + ); + } + + #[Route(path: '/village/{x}/{y}/send-units', methods: ['POST'])] + public function sendUnits(Request $request): Response + { + $village = Village::getByCoordinates($request->get('x'), $request->get('y')); + $destination = Village::get($request->get('village')); + + /**@var Model $unit*/ + $unit = new (Model::resolveType($request->get('unit')))(); + + $amount = intval($request->get('amount')); + $amountUnits = DB::query( + 'select amount from village_units where home_village_id=:home and residence_village_id=:home and type=:type', + ['home' => $village->id, 'type' => $request->get('unit')] + )->fetchColumn(); + + if ($amountUnits - $amount > 0) { + $statement = DB::query( + << $amountUnits - $amount, 'home' => $village->id, 'type' => $request->get('unit')] + ); + } else if ($amountUnits - $amount === 0) { + DB::query( + << $village->id, 'type' => $request->get('unit')] + ); + } + + DB::query( + 'insert into village_units (amount, type, home_village_id, residence_village_id, is_traveling) values (:amount, :type, :home, :home, true)', + ['amount' => $amount, 'type' => $request->get('unit'), 'home' => $village->id] + ); + + // event + $event = new Event(); + $event->time = (new \DateTime())->add( + \DateInterval::createFromDateString( + Model::getTravelTime($unit, Village::getDistance($village->x, $village->y, $destination->x, $destination->y)) + . ' seconds' + ) + ); + $event->villageId = $village->id; + $sendUnitsEvent = new SendUnits(); + $sendUnitsEvent->event = $event; + $sendUnitsEvent->type = $request->get('type'); + $sendUnitsEvent->unit = $request->get('unit'); + $sendUnitsEvent->amount = $amount; + $sendUnitsEvent->source = $village->id; + $sendUnitsEvent->destination = $destination->id; + $sendUnitsEvent->dbInsert(); + + + return new RedirectResponse( + Router::generate( + 'village.show', + ['x' => $request->get('x'), 'y' => $request->get('y')] + ) + ); + } +} diff --git a/src/http/Controller/Village.php b/src/http/Controller/Village.php new file mode 100644 index 0000000..cd38442 --- /dev/null +++ b/src/http/Controller/Village.php @@ -0,0 +1,146 @@ + $_SESSION['user']['id']] + ); + + return new Response(View::render('villages.twig', [ + 'villages' => $villages, + ])); + } + + #[Route(path: '/village/{x}/{y}', methods: ['GET'])] + public function show(Request $request): Response + { + $village = Model::getByCoordinates($request->get('x'), $request->get('y')); + + if (! Guard::ownsVillage($village->id)) { + return new Response(View::render('error.twig', ['message' => 'Insufficient permission']), 403); + } + + $events = []; + + $eventsBuilding = DB::query( + << $village->id] + )->fetchAll(); + + foreach ($eventsBuilding as $row) { + $events['UpgradeBuilding'][$row['type']][] = DB::convertToModel(UpgradeBuilding::class, $row); + } + + $eventsUnits = DB::query( + << $village->id] + )->fetchAll(); + + foreach ($eventsUnits as $row) { + $events['TrainUnits'][] = DB::convertToModel(TrainUnits::class, $row); + } + + $eventsUnitsSendOwn = DB::query( + << $village->id] + )->fetchAll(); + + $eventsUnitsSendOther = DB::query( + << $village->id] + )->fetchAll(); + + foreach ([...$eventsUnitsSendOwn, ...$eventsUnitsSendOther] as $row) { + $events['SendUnits'][] = DB::convertToModel(SendUnits::class, $row);; + } + + $buildings = []; + foreach (Model::getBuildings($village->id, true) as $building) { + $buildings[$building->type] = $building; + } + + + return new Response(View::render('village.twig', [ + 'village' => $village, + 'events' => $events, + 'buildings' => $buildings, + 'villages' => DB::fetch(Model::class, "select * from villages where id!=:id", ['id' => $village->id]), + ])); + } + + #[Route(path: '/village/{x}/{y}/storage/config', methods: ['POST'])] + public function storageConfig(Request $request): Response + { + $village = Model::getByCoordinates($request->get('x'), $request->get('y')); + + // calculate to max 100% + $wood = intval($request->get('wood')); + $clay = intval($request->get('clay')); + $iron = intval($request->get('iron')); + $food = intval($request->get('food')); + $total = $wood + $clay + $iron + $food; + $woodPercent = $wood / $total; + $clayPercent = $clay / $total; + $ironPercent = $iron / $total; + $foodPercent = $food / $total; + + $wood = round($woodPercent * 100); + $clay = round($clayPercent * 100); + $iron = round($ironPercent * 100); + $food = round($foodPercent * 100); + $newTotal = $wood+$clay+$iron+$food; + $food += (100 - $newTotal); + + DB::query( + << $wood, 'clay' => $clay, 'iron' => $iron, 'food' => $food, 'id' => $village->id] + ); + + return new RedirectResponse( + Router::generate( + 'village.show', + ['x' => $request->get('x'), 'y' => $request->get('y')] + ) + ); + } +} diff --git a/src/http/Http.php b/src/http/Http.php new file mode 100644 index 0000000..1867eb4 --- /dev/null +++ b/src/http/Http.php @@ -0,0 +1,37 @@ +send(); + } +} diff --git a/src/http/Router.php b/src/http/Router.php new file mode 100644 index 0000000..db75f81 --- /dev/null +++ b/src/http/Router.php @@ -0,0 +1,80 @@ +fromRequest($request); + + self::$routes = new RouteCollection(); + $loader = new AnnotationFileLoader(new FileLocator(), new RouteLoader()); + $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator(__DIR__ . '/Controller')); + foreach ($iterator as $file) { + /**@var \SplFileInfo $file*/ + if (in_array($file->getFilename(), ['.', '..'])) continue; + + $collection = $loader->load($file->getPathname(), 'attribute'); + self::$routes->addCollection($collection); + } + } + + public static function execute(): Response + { + try { + $matcher = new UrlMatcher(self::$routes, self::$context); + $match = $matcher->matchRequest(self::$request); + + foreach ($match as $key => $value) { + if (str_starts_with($key, '_')) continue; + + self::$request->query->set($key, $value); + } + + /**@var \ReflectionClass $class*/ + $class = $match['_']['class']; + /**@var \ReflectionMethod $method*/ + $method = $match['_']['method']; + + return ($class->newInstance())->{$method->getName()}(self::$request); + } catch (ResourceNotFoundException $exception) { + return new Response('404', 404); + } catch (MethodNotAllowedException $exception) { + return new Response('403', 403); + } catch (\Exception $exception) { + return new Response('500: ' . $exception->getMessage(), 500); + } + } + + /** + * @param string $name + * @param array $parameters + * @param int $referenceType + */ + public static function generate(string $name, array $parameters = [], int $referenceType = 1): string + { + $generator = new UrlGenerator(self::$routes, self::$context); + + return $generator->generate($name, $parameters, $referenceType); + } +} diff --git a/src/http/Support/RouteLoader.php b/src/http/Support/RouteLoader.php new file mode 100644 index 0000000..b0e74cb --- /dev/null +++ b/src/http/Support/RouteLoader.php @@ -0,0 +1,24 @@ +setDefault('_', compact('class', 'method', 'annotation')); + } + + protected function getDefaultRouteName(\ReflectionClass $class, \ReflectionMethod $method) + { + $name = parent::getDefaultRouteName($class, $method); + + return str_replace( + '_', + '.', + str_replace('app_controller_', '', $name) + ); + } +} -- cgit v1.2.3