|-- bin/
|-- config/
|-- migrations/
|-- public/
|-- src/
|-- Action/
|-- Alert/
|-- Chat/
|-- Daedalus/
|-- config
|-- Controller
|-- ConfigData
|-- DataFixtures
|-- Entity
|-- Enum
|-- Factory
|-- Event
|-- Listener
|-- Normalizer
|-- Repository
|-- Service
|-- Validator
|-- Disease/
|-- Equipment/
|-- Exploration/
|-- Game/
|-- Hunter/
|-- MetaGame/
|-- Modifier/
|-- Place/
|-- Player/
|-- Project/
|-- RoomLog/
|-- Status/
|-- User/
|-- tests/ --> Test directory
|-- .env --> environment variables
|-- composer.json --> dependencies
Symfony/php commands, you can run for instance
bin/console
core config files, you will find every dependencies configuration, the routes definition, database configuration, etc...
We use ApiPlatform to generate a lot of routes, and the configuration for these can be found in this directory.
doctrine migrations, basically all the sql request to set-up and update the database
documentation: https://symfony.com/doc/master/bundles/DoctrineMigrationsBundle/index.html
entry point of Symfony, that are the public files and asset that apache can access to
It is very unlikely that you need to modify something there
In previous versions of Symfony that would have been bundles
Each folder manages a part, the Game folder is for all the services/entities that are shared across each module/folder.
Daedalus folder manage the Daedalus, Player folder manage the Player (etc...)
Config for the module/folder, mostly Symfony Dependency Injection.
Responsible for declaring the routes (with annotations), it receives the request and send the response
There should be no logic inside the controller except calling some services and verify the request
Store all the initialization data.
This folder can be easily change to tweak game parameters such as action cost, intensity of effects...
Same as ConfigData but for local development, it is used to populate the database with some data
The class that holds the data, some of them are stored in database
Store the strings, ex : key for the equipments
Events declaration.
Factory classes, there are mostly used to easily create entities with some default values for unit tests (but can be used in production code too).
Event listeners and suscribers.
Events are used to share information between modules. For example, when a project is finished, the event PROJECT_FINISHED is triggered from the Project module, so it can be handled by the Chat module to create a NERON announcement.
Documentation : https://symfony.com/doc/6.4/components/event_dispatcher.html#introduction
Normalize the data returned by the controller, basically it transforms an object into an array.
The normalization is where we decide which part of the object are retured or not.
For example, when a player is normalized, we don't return the satiety, as it is an hidden property for other players.
Documentation : https://symfony.com/doc/6.4/serializer/custom_normalizer.html
The interface between the database and the entities, they are tightly coupled to the ORM (doctrine).
If you have some complex SQL query to do it is the place to do them.
Some modules have a RepositoryInterface with an InMemoryRepository implementation that is used for unit testing. I highly recommend you implement them.
There you will find the core game logic, you call the Repository to retrieve the data in database and apply the transformations you need.
Let's follow a Command pattern : one service = one action on the system to perform.
There you validate the criteria for the Player to do an Action.
Example : Do not allow Participate to project action if the player is dirty.
There you valid the data you receive in the request, are all the required fields there? Do the character exist?
Let's take the example of a new player is created:
- The client send a POST request to /players
- The validator (in src/Player/Validator) verify that there is a daedalus and a character in the request They also verify that the character and daedalus exist, the daedalus is not already full, etc... If everything is fine then:
- The controller (in src/Player/Controller) receive the request, it might check that the user is authorized to access this daedalus, then it will call a service (in src/Player/Service) to create the Player with the argument passed in the request.
- The Service will perform the creation of the Player, call another Service to get the Game and Player configs, and the Random service to perform random stuff It might also trigger some events, like a new Player is created, this event might trigger the Event Daedalus is complete, etc... This event will also use the service to create a new room log for player awaken To finish that, once the entity player created, the service will use the Repository to save this Player in the database and returning this Player entity
- Once the service has finished performing the creating of the character, ot returns an entity Player, then the Controller will return this Player as response to the request
- While creating the Response the Normalizer will normalize the Player entity into an array, and won't return the satiety for instance as it is an hidden property
- Then the client should have his response
The difference between an action and an event (from a development perspective) is an Action is what a User want to do. An event is something that can be the result of an action, or the change of cycle. Obvious example:
- Action:
- Move: The player use a door
- Shoot a hunter
- Event:
- The cycle change
- A player dies
For instance a player can make the action 'hit' on another player, this will trigger the event 'player dies'. Less obvious example: A player makes the action eat, that trigger the event 'become Dirty'
- Action: handles actions performed by the player
- Alert: track Daedalus and crew critical points
- Chat: handle chat between players and NERON announcements
- Communications : handle communications center gameplay
- Daedalus : handles the Daedalus cycle of life
- Disease : handles diseases (physical diseases, troubles, injuries)
- Equipment : handles equipment, items and doors
- Exploration : handles exploration and planets gameplay
- Game : common logic for other modules
- Hunter : handles hunters, hostile NPCs which attacks Daedalus' hull
- MetaGame : handle Admin and Moderation actions
- Modifier : handle modifiers. Modifiers change events, for example reducing the cost of a specific action or triggering an extra event.
- Place : handles places, this is where equipment and players are located
- Player : handles players cycle of life
- Project : handles PILGRED, NERON projects and researches gameplay
- RoomLog : handles room logs showing the action / events history of a room
- Skill : handles skills cycle of life
- Status : handles statuses (like hungry, suicidal, broken etc.)
- User : handles users (mostly registering and login for now)
composer reset: Reset database, load data and fixtures and create a new Daedalus. Use this for a fresh startcomposer load-data/composer load-fixtures: Load data / fixturescomposer fill-daedalus: Fill a Daedalus with playerscomposer lint: Lint the codecomposer test: Run the testscomposer test:action: Run the tests for the Action modulecomposer generate-migrations/composer diff: Generate migrations by comparing the database schema with your code entitiescomposer apply-migrations: Run the migrations to update the database schema
Please find more commands in the composer.json file.
Accessing database from terminal :
On your host machine (not the docker container) run:
docker exec -it mush-database bash
psql --username mysql mush
List the tables with:
\dt
Get a table with:
select * from table;
I highly suggest you use a GUI client like DBeaver or SQL Tools VSCode plugin to access the database though.
The test folder is a mirror of the src directory.
You can mock classes/services with Mockery but this is highly discouraged. You should use real classes and services as much as possible, with Fake or InMemory implementations (example : Fake, InMemory and usage).
This will be easier to setup and way less prone to break the tests when we refactor the code.
You can run a unit test with
composer test tests/unit/path/to/your/test.php
You can run all tests with
composer test
but due to the great amount of (slow integration) tests, it is recommended to only run the tests of the files or module you are working on.
Ensure you have the following configuration

Prefix your command line with: XDEBUG_CONFIG="idekey=PHPSTORM"
example: XDEBUG_CONFIG="idekey=PHPSTORM" vendor/bin/codecept run
Add in the query parameter XDEBUG_SESSION_START=PHPSTORM
Example: http://localhost:8080/api/v1/player/1/action?XDEBUG_SESSION_START=PHPSTORM
Troobleshoting:
Context: running the command on docker ubuntu 20 and getting this error:
Xdebug: [Step Debug] Time-out connecting to debugging client, waited: 200 ms. Tried: 172.17.0.1:9003 (through xdebug.client_host/xdebug.client_port) :-(
Check what the folowing command returns:
sudo ufw status verbose
On the host machine (not the docker container) run :
sudo ufw allow from any to any port 9003 proto tcp
- A configuration file is provided in the
.vscode/launch.jsonto start a debugging session. Make sure to run it from the Run and Debug view.
- Run your tests with the following environment variables:
XDEBUG_MODE=debug XDEBUG_SESSION=1 composer test <your_test_file>
