It is a part of the Guiding Star project. Learn more about the overall architecture here.
- Not configurable is most customizable. Every contest has its unique rule, which is unlikely to be fully covered by a text file or SQL table. Our experience shows that these static configurations often disturb contest organizers when they want more customization. Instead, we trust them as decent programmers, leaving the customization to the logic in the source code that is easy to maintain and extend.
- Pure Python Logic. The core logic that handles submissions, scoreboards, and users are written in plain old Python. Only basic Object-Oriented Programming skill is required before you can start customization. No preknowledge of heavy third-party frameworks is needed.
- Type-checked. Every function in the codebase is type-annotated and checked by Mypy in strict mode. With type information, you can reliably auto-complete the code and check what breaks after a refactor in a capable IDE.
- A flexible state machine. The logic splits into one reducer that updates the game state and multiple workers that power read-only APIs and pass actions to the reducer. The state synchronizes in real-time with ZeroMQ sockets. It is easy to attach different kinds of workers that handle push notifications or background jobs to the reducer.
- Scalable performance. A bonus of the state machine architecture is that only raw submissions are in the database (so that the state can be restored after a service restart). The game state is kept in RAM, making it more performant when updating the scoreboard. The backend is also asynchronous, therefore a laggy OAuth provider will not block other requests.
The backend codebase is compatible with Linux and Windows.
Although nobody uses Windows on a server, it is a good news if you develop on Windows.
Setup:
- Install Python (≥3.8)
pip install -r requirements.txt
- Install MySQL server (≥5.7.8, or MariaDB ≥10.2.7 for the JSON datatype) and set up a database
CREATE USER 'username'@'localhost' IDENTIFIED BY 'password';
CREATE DATABASE database;
GRANT ALL PRIVILEGES ON database.* TO 'username'@'localhost';
FLUSH PRIVILEGES;
- Configure parameters
- Rename
src/secret_example.py
tosrc/secret.py
- Fill in everything in that file
- And create directories mentioned in the file
- Rename
- Prepare the database
- Set environment variable:
export FLASK_APP=src/admin/app
flask db init
flask db migrate
- The above command will complain about the circular dependency of two constraints (
SAWarning: Cannot correctly sort tables; there are unresolvable cycles between tables "user, user_profile" ...
). You need to manually tweak the generated Python file inmigrations/versions
- Comment out the line
sa.ForeignKeyConstraint(['profile_id'], ['user_profile.id'], ),
- Insert
op.create_foreign_key(None, 'user', 'user_profile', ['profile_id'], ['id'])
at the end of theupgrade
method
- Comment out the line
flask db upgrade
- Set environment variable:
Start the server:
python3 run_reducer_admin.py
- It will show
[success] reducer.mainloop: started to receive actions
- It will show
python3 run_worker_api.py
- It will show
[success] worker.mainloop: started to receive events
- It will show
It is a good idea to run them as a systemd service on the deployment server. You should put these services behind a reverse proxy. Refer to setup instruction here.
Development:
pip install -r requirements-dev.txt
- This will install
mypy
and type sheds
- This will install
- To type-check the code, run
python3 -m mypy.dmypy run
- We recommend Python IDEs that give accurate completions with type hints
- i.e. PyCharm
This repository is distributed under MIT License.