Giter Site home page Giter Site logo

marein / php-gaming-website Goto Github PK

View Code? Open in Web Editor NEW
37.0 7.0 26.0 1.5 MB

A gaming website where people can play against each other.

License: MIT License

PHP 88.57% JavaScript 8.03% CSS 1.04% Shell 1.12% Dockerfile 0.19% Twig 1.04%
php-application symfony-application event-driven-architecture eventual-consistency messaging ports-and-adapters microservice monolith cqrs-application clean-architecture

php-gaming-website's People

Contributors

delwin444 avatar marein avatar samuraya avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

php-gaming-website's Issues

A player can resign a game

Problem description
Jane and John are playing. After a few moves, it looks like Jane will definitely win. John can now decide if he wants to resign the game. Resigning is only possible after the second move is done. Before that, the game can be aborted.

Improve documentation

The documentation should be improved regarding to the following steps.

  • Remove words like "just", "easy", "simple" and "not hard".
  • Add link to composer.json in the section "Chosen technologies".
  • Add one-click "Try in PWD" button.

Use web components

The JavaScript is already written with components in mind. Each class could be replaced sequentially with each component.

Following components needs to be updated.

  • Chat/Widget.js
  • Common/Notification.js
  • ConnectFour/AbortGameButton.js
  • ConnectFour/Game.js
  • ConnectFour/GameList.js
  • ConnectFour/JoinGameButton.js
  • ConnectFour/OpenGameButton.js
  • ConnectFour/ResignGameButton.js
  • ConnectFour/RunningGames.js

Then we have to clean up the following files (see todo with link to this issue).

  • Common/EventSource.js

The following files can be deleted after everything has been replaced.

  • Common/EventPublisher.js
  • bootstrap.js

Integrate domain logic for memory game

The next game to come will be memory. Following things needs to be done.

  • A player can open a game.
  • A dealer deal cards in.
  • Other players can join an open game.
  • Players can leave the game while it's in open state. If the current player leaves the game, the next player becomes the current player. If all players left, the game gets closed.
  • The current player can start the game. After the game is started, no one can leave or join.
  • The game can only be started with at least two players.
  • Players can resign in a started game. If the penultimate players leaves, the last player gets the rest of the points and the game gets closed.
  • Players flip the cards in pairs one after another. If a pair matches, the player gets a point and the card stays flipped. Other players can't flip the pair again.
  • If all matches are found, the game is finished and the winner is chosen.

Improve development environment

Developers should be able to access data or general information without having to install external dependencies.

Following interfaces should be provided.

Application Port
The application. 80
MySQL management interface. 8081
Redis management interface. 8082
RabbitMq management interface. 8083
Traefik management interface. 8084

The documentation should reflect that.

Refactor store of game query model

Problem description
The classes PredisGameProjection and PredisGameFinder have both knowledge about how the query model for a game is stored. PredisGameProjection knows how to retrieve and save it. PredisGameFinder knows how to retrieve it. It's a given that this code changes together. They're sharing a constant for the storage key prefix already. Furthermore, the class PredisGameProjection implements an in memory cache for games.

Possible solution
We should move the responsibility to store a game to the GameFinder. This leads to higher cohesion. While we're doing that, we should extract the in memory cache part from PredisGameProjection to a dedicated implementation of GameFinder. This implementation should have a cache size limit to prevent memory leaks.

Render connect four within canvas

The game should be rendered within canvas in web/assets/js/ConnectFour/Game.js. With that we should animate the fall of the stone.

Refactor store of games by player query model

Problem description
The classes PredisGamesByPlayerProjection and PredisGamesByPlayerFinder have both knowledge about how the query model for a game by player relation is stored. It's a given that this code changes together. They're sharing a constant for the storage key prefix already.

Possible solution
We should move the responsibility to store a running game to the PredisGamesByPlayerFinder. This leads to higher cohesion.

Performance improvements

Some improvements that are currently missing:

  • Dump the service container into a single file, see here.
  • Use apcu cache adapter

See here.

Implement CSRF protection

Problem description
Since we use cookie based authentication, this application is vulnerable to CSRF.

A user can sign up

Problem description
After playing for a while, Jane decides to sign up to the page. She must provide an username and a password. Additionally she'll be asked if she would like to add her already played games to the account.

Possible implementation
Assigning the played games to the account should be easy. If she says yes, then she gets the UUID of her anonymous account. If she says no, she gets a new UUID.

Show how many users are currently online

Problem description
John is considered online while he's interacting with the page. John is considered offline if he hasn't performed a request within the next five minutes.

This could also be used to show which users are watching a particular game.

Possible implementation
We can leverage Redis and its Sorted Set for this feature.

Switch to phpredis/phpredis

Seems like the package predis/predis is not maintained anymore. We can switch to the php extension phpredis/phpredis.

Fix deprecations

  • Since symfony/dotenv 5.1: Passing a boolean to the constructor of Symfony\Component\Dotenv\Dotenv is deprecated, use Dotenv::usePutenv().
  • Since symfony/http-kernel 5.1: Referencing controllers with a single colon is deprecated. Use controller::action instead.

Decrease build time in dev environment

docker-compose build takes a long time because each PHP container specifies the build directive (even if we have docker's caching mechanism). The time increases with the number of containers and should be worked on before new features are added. This build time should be minimized.

Show meaningful error messages

Problem description
Currently exceptions are bubbling up to the event dispatcher from symfony. The registered listener Gaming\Common\ExceptionHandling\GamingExceptionListener is catching those exceptions and try to guess a error message for the user (currently via the name of the exception). Furthermore it has some logic to test if the name contains NotFound. In that case it sets the status code to 404. This might not always be valid and also enforces certain exception names.

Possible solution
Catching domain exceptions at the application level and re-throw an ApplicationException with violations. A violation contains a property path, an identifier and a context (that combined makes them translatable and mappable to the UI if needed). These violations are part of the contract of the application itself. Furthermore, these application exception can be thrown by a bus implementation which is validating the incoming message with a validation library before it's delegating the message to the next bus implementation. As soon as every exception which is actually a violation is handled properly, the Gaming\Common\ExceptionHandling\GamingExceptionListener can be deleted.

Tasks

  • Write bus implementation which uses a validation library
  • Transform domain exceptions to an ApplicationException
  • Write an exception listener which transforms an ApplicationException to a http message
  • Remove Gaming\Common\ExceptionHandling\GamingExceptionListener

Acceptance test suite is failing

[PHPUnit\Framework\Exception] Undefined index: ELEMENT

Probably because we always use the latest tag when fetching the selenium image.

Show real username

Problem description
Currently the username "Anonymous" is hard coded everywhere. When Jane signs up, her chosen username should be displayed instead. An anonymous user should still be called "Anonymous".

Possible solutions
With the first solution, the contexts are independent. Any context interested in usernames must subscribe to the events from the Identity context. These events (UserSignedUp and UsernameChanged) are applied to the local copies of the respective context. The WebInterface context has nothing todo but to forward the request to the respective context and return the response back to the user. I like the independence of this approach, but its a lot of common duplication. The second approach pushes these responsibilities to the WebInterface context (see below). Following is a diagram of how this process looks like with a closer look to the Chat context.
Solution 1

With the second solution, the contexts can't answer the username question themselves. It's the responsibility of the WebInterface context to assign usernames to the responses. The WebInterface is anyway the intermediate in every conversation. While the contexts are not independent, I think it's okay to couple the username question to the WebInterface context to gain a little less code. Of course, the first solution is more explicit and explicitness wins over "Don't repeat yourself". Following is a diagram of how this process looks like with a closer look to the Chat context.
Solution 2

Restrict read access for chats

Problem description
Jane and John are both explicitly assigned to their private chat. Eve shouldn't be able to read those messages.

Refactor store of open game query model

Problem description
The classes PredisOpenGamesProjection and PredisOpenGamesFinder have both knowledge about how the query model for an open game is stored. It's a given that this code changes together. They're sharing a constant for the storage key already.

Possible solution
We should move the responsibility to store an open game to the OpenGamesFinder. This leads to higher cohesion.

Use standard headers for CSRF protection

What to do is described at OWASP. This replaces our dependency dunglas/angular-csrf-bundle and removes its double submit cookie from the request. As described in OWASP, there are some edge cases in which the required headers are not available. But we're not live and never will be (side project).

Move libraries to organization

Libraries should be moved to the organization. They should be registered and loaded within this composer repository. This enables us to use them in other services and break the domains out of this project.

  • Application (should be deleted)
    315e3b9
  • Bus (rename the library to something more meaningful, e.g. application handler)
  • Clock (should be replaced, there's no need for a global static clock anymore, maybe wait for PSR-20)
    #163
  • DoctrineHeartbeatMiddleware -> this can be moved to the marein account.
  • Domain
  • EventStore
  • ForkPool -> this can be moved to the marein account.
  • IdempotentStorage
  • MessageBroker
  • ObjectMapper (should be deleted and replaced with something that exists already)
    #67
  • StandardHeadersCsrfBundle
    #56
  • Scheduler (also find a better name, "scheduler" is pretty generic, maybe KernelScheduler) -> this can be moved to the marein account.
  • Sharding

Remove parameter kernel.root_dir

The parameter was added because, for example, doctrine/doctrine-migrations-bundle still depends on it.

This issue is currently not resolvable and should be checked again from time to time.

The workaround was introduced in #51.

Set SameSite attribute for session cookie

We can use the Lax setting because we adhere to the HTTP protocol and don't use secure methods to change data. It looks like this will protect against CSRF attacks, but it doesn't. Normal forms, let's call them pre-auth, like a login or a contact form, still need to be protected.

Revisit db migrations during startup

The migrations were created just to make them work and make the application deployable with single-commands like docker-compose up or docker stack deploy. However, there're known issues with the current approach, some of them are:

  • The used migration package cannot safely run concurrently (not sure about that, needs to be investigated by revisiting the package again), but there are multiple containers which are in a race to migrate the database.
  • There's a new argument --allow-no-migration with the doctrine:migrations:migrate command that could be used.
  • Errors are suppressed by >/dev/null 2>/dev/null which hides all the problems with the current solution.
  • The container doesn't exit with an error if the migrations cannot be applied.
  • Migrations always run during startup. This should be configurable via environment variables. In the best case for each context. This would make the workaround of using entrypoint: docker-php-entrypoint in stack files obsolet. The feature can also be disabled if the deployment strategy wants to manually apply migrations.

The goal of this issue is to use a clean solution while considering all the points above. Executing the migrations manually is not an option because we want to make the application deployable with one-click applications like Play with Docker (at least the file docker-compose.production.yml) and docker-compose up should be enough for setting up the development environment.

Possible solutions:

  • Ditch the current package in favor of a new one which is capable to execute migrations concurrently (again, investigate if the current package cannot handle it itself).
  • Acquire a distributed lock. The first container that gets the lock at startup will execute the migrations. Others will wait until the lock is released.
  • Use a separate container for executing migrations. Other containers wait for the healthiness of this container before starting up.

Implement Elo rating

Problem description
Jane and John want to know how they perform.

Possible solution
Create a new class named Player inside the context ConnectFour. Below there's an example structure of this class. The username is there as an example of how this issue supplements issue #14.

final class Player
{
    /**
     * @var string
     */
    private $id;

    /**
     * @var string
     */
    private $username;

    /**
     * @var int
     */
    private $eloRating;
}

Refactor store of running games query model

Problem description
The classes PredisRunningGamesProjection and PredisRunningGamesFinder have both knowledge about how the query model for a running game is stored. It's a given that this code changes together. They're sharing a constant for the storage key already.

Possible solution
We should move the responsibility to store a running game to the RunningGamesFinder. This leads to higher cohesion.

Ability to filter games on the profile page

Problem description
Jane wants to study her own won, drawn and lost games.

Possible solution
Split the current view in 4 categories: running, won, drawn, lost.

Additional ideas

  • Search for games against specific players.

Disable controls if user isn't able to perform the action

Problem description
Currently the invariants are only captured on the server. Following needs to be done in the web interface.

  • Disable button in game list if a player joined a game or aborted a game.
  • Disable the fields in the game view if it's not the current player's turn or the column is already filled.
  • Disable resign game button in game view if the game isn't resignable or the current user is not a player within that game.
  • Disable abort game button in game view if the game isn't resignable or the current user is not a player within that game.

Move repository to organization

This repository should be moved to https://github.com/gaming-platform and the main application Docker image should be made accessible via ghcr.io.

Reason
Everything else about the project is already or will end up in the organization. There is no reason to leave this repository in my personal account.

Naming
The name php-gaming-website will change. Since this is a modular monolith, this will not match with the current repository naming conventions like gaming-platform/service-js-polyfill, gaming-platform/service-mailer, gaming-platform/docker-php-fpm or gaming-platform/php-mailer-client. It could be named to gaming-platform/application.

Use opcache.preload

Symfony provides a file from the generated container, see here. However, files in src should also be loaded.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.