Compare commits

..

10 commits

Author SHA1 Message Date
Laureηt 7a0db79477
fix: forgot to change an hardcoded path 2022-03-05 00:47:05 +01:00
Laureηt 14aa100ab2
feat: divided by 5 the size of the emulator image thanks to docker multi-stage 2022-03-05 00:42:43 +01:00
Laureηt 7d73a1b99f
feat: dependencies optimizations 2022-03-04 23:46:38 +01:00
Laureηt 352ed37e30
fix: cannot name auxillary file "redis" 2022-03-04 23:46:14 +01:00
Laureηt 39d095aae1
feat: even more tasks 2022-03-04 23:45:36 +01:00
Laureηt 6330221fdb
fix: gave names to workspaces 2022-03-04 14:21:16 +01:00
Laureηt 3b123b8ce0
fix(emulator): changed names of auxilary files 2022-03-04 14:21:02 +01:00
Laureηt 285999225b
feat: setup code-workspace 2022-03-03 23:35:45 +01:00
Laureηt dbfead57b7
refactor!wip: cleaved the emulator and websocket server 2022-03-03 22:41:13 +01:00
Laureηt 02a6e1e435
feat(states): no more magic numbers 2022-02-24 22:15:20 +01:00
35 changed files with 1199 additions and 364 deletions

View file

@ -1,21 +0,0 @@
{
"name": "B00playsGBA (docker)",
"context": "..",
"dockerFile": "../Dockerfile",
"settings": {},
"extensions": [
"ms-azuretools.vscode-docker",
"editorconfig.editorconfig",
"eamodio.gitlens",
"ms-kubernetes-tools.vscode-kubernetes-tools",
"esbenp.prettier-vscode",
"ms-python.python",
"ms-python.vscode-pylance",
"njpwerner.autodocstring",
"ms-vscode-remote.remote-containers",
"vivaxy.vscode-conventional-commits"
],
"build": {
"target": "dev"
}
}

2
.env
View file

@ -4,3 +4,5 @@ WEBSOCKET_SERVE=0.0.0.0
WEBSOCKET_PORT=6789 WEBSOCKET_PORT=6789
RTMP_HOST=rtmp RTMP_HOST=rtmp
RTMP_PORT=1935 RTMP_PORT=1935
EMULATOR_ROM_PATH=/roms/pokemon.gba
EMULATOR_STATES_PATH=/states/

2
.gitmodules vendored
View file

@ -1,3 +1,3 @@
[submodule "mgba"] [submodule "mgba"]
path = mgba path = src/mgba
url = https://github.com/mgba-emu/mgba.git url = https://github.com/mgba-emu/mgba.git

View file

@ -1,4 +1,9 @@
repos: repos:
# - repo: https://github.com/asottile/pyupgrade
# rev: v2.31.0
# hooks:
# - id: pyupgrade
- repo: https://github.com/pre-commit/pre-commit-hooks - repo: https://github.com/pre-commit/pre-commit-hooks
rev: "v4.1.0" rev: "v4.1.0"
hooks: hooks:
@ -18,12 +23,12 @@ repos:
hooks: hooks:
- id: isort - id: isort
name: isort (python) name: isort (python)
- id: isort # - id: isort
name: isort (cython) # name: isort (cython)
types: [cython] # types: [cython]
- id: isort # - id: isort
name: isort (pyi) # name: isort (pyi)
types: [pyi] # types: [pyi]
- repo: https://github.com/psf/black - repo: https://github.com/psf/black
rev: "22.1.0" rev: "22.1.0"

26
.vscode/launch.json vendored
View file

@ -1,30 +1,14 @@
{ {
"version": "0.2.0", "version": "0.2.0",
"configurations": [
{
"name": "Server",
"type": "python",
"request": "launch",
"program": "${workspaceFolder}/src/server.py",
"console": "integratedTerminal",
"envFile": ""
},
{
"name": "Emulator",
"type": "python",
"request": "launch",
"program": "${workspaceFolder}/src/emulator.py",
"console": "integratedTerminal",
"envFile": ""
}
],
"compounds": [ "compounds": [
{ {
"name": "Server/Emulator", "name": "Launch",
"configurations": [ "configurations": [
"Server", "Launch (server)",
"Emulator" "Launch (emulator)"
], ],
"preLaunchTask": "Start NGINX+Redis",
// "postLaunchask": "Stop NGINX+Redis", // not yet supported by vscode, launch the task manually
"stopAll": true "stopAll": true
} }
] ]

View file

@ -1,5 +1,4 @@
{ {
"python.defaultInterpreterPath": "${workspaceFolder}/.venv/bin/python",
"python.formatting.provider": "black", "python.formatting.provider": "black",
"editor.formatOnSave": true, "editor.formatOnSave": true,
"python.linting.enabled": true, "python.linting.enabled": true,
@ -13,6 +12,6 @@
} }
}, },
"python.analysis.extraPaths": [ "python.analysis.extraPaths": [
"${workspaceFolder}/mgba/src/platform/python", "${workspaceFolder}/src/mgba/src/platform/python",
], ],
} }

68
.vscode/tasks.json vendored
View file

@ -2,14 +2,68 @@
"version": "2.0.0", "version": "2.0.0",
"tasks": [ "tasks": [
{ {
"label": "Redis server", "label": "Start NGINX",
"command": "redis-server", "type": "docker-run",
"type": "process", "dockerRun": {
"isBackground": true, "containerName": "nginx-rtmp",
"problemMatcher": [], "image": "tiangolo/nginx-rtmp",
"presentation": { "ports": [
"reveal": "silent" {
"containerPort": 1935,
"hostPort": 1935
}
],
"remove": true
}, },
"problemMatcher": []
},
{
"label": "Start Redis",
"type": "docker-run",
"dockerRun": {
"containerName": "redis",
"image": "redis:alpine",
"ports": [
{
"containerPort": 6379,
"hostPort": 6379
}
],
"remove": true
},
"problemMatcher": []
},
{
"label": "Start NGINX+Redis",
"dependsOn": [
"Start NGINX",
"Start Redis"
],
"problemMatcher": []
},
{
"label": "Stop NGINX",
"type": "shell",
"command": "docker stop nginx-rtmp",
"problemMatcher": []
},
{
"label": "Stop NGINX",
"type": "shell",
"command": "docker stop redis",
"problemMatcher": []
},
{
"label": "Stop NGINX+Redis",
"type": "shell",
"command": "docker stop redis nginx-rtmp",
"problemMatcher": []
},
{
"label": "Build mGBA",
"type": "shell",
"command": "mkdir src/mgba/build; cd src/mgba/build && cmake -DBUILD_PYTHON=ON -DBUILD_QT=OFF -DBUILD_SDL=OFF -DUSE_DISCORD_RPC=OFF . && make",
"problemMatcher": []
} }
] ]
} }

View file

@ -1,60 +0,0 @@
FROM python:alpine AS base
# set /code as the work directory
WORKDIR /code
RUN \
# update alpine repositories
apk update \
# build tools dependencies
&& apk add --no-cache build-base cmake git \
# mgba dependencies
&& apk add --no-cache libffi-dev elfutils-dev libzip-tools minizip-dev libedit-dev sqlite-dev libepoxy-dev ffmpeg ffmpeg-dev libpng-dev jpeg-dev \
&& pip install cffi
RUN \
cd /code \
# clone mgba
&& git clone https://github.com/mgba-emu/mgba.git mgba \
# create build directory
&& mkdir mgba/build \
# go to the build directory
&& cd mgba/build \
# configure the build
&& cmake -DBUILD_PYTHON=ON -DBUILD_QT=OFF -DBUILD_SDL=OFF -DUSE_DISCORD_RPC=OFF -DCMAKE_INSTALL_PREFIX:PATH=/usr/local .. \
# build mGBA
&& make \
# install mGBA, TODO: is install needed ?
&& make install
# copy poetry config file
COPY ./pyproject.toml /code
RUN \
cd /code \
# install poetry
&& pip install poetry \
# config poetry to not create a .venv
&& poetry config virtualenvs.create false \
# upgrade pip
&& poetry run pip install --upgrade pip
RUN \
cd /code/mgba/src/platform/python \
# install mGBA bindings, TODO: can delete everything else ?
&& BINDIR=/code/mgba/build/include LIBDIR=/code/mgba/build/include python setup.py install
# copy the src files
COPY ./src /code/src
FROM base AS prod
RUN poetry install --no-interaction --no-ansi --no-dev
FROM prod as dev
RUN poetry install --no-interaction --no-ansi
FROM prod AS server
CMD [ "poetry", "run", "python", "src/server.py" ]
FROM prod AS emulator
CMD [ "poetry", "run", "python", "src/emulator.py" ]

View file

@ -31,7 +31,6 @@ B00 plays GBA is an interactive emulator made to entertain people during waiting
- [Remote container](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) - [Remote container](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers)
- [EditorConfig](https://marketplace.visualstudio.com/items?itemName=EditorConfig.EditorConfig) - [EditorConfig](https://marketplace.visualstudio.com/items?itemName=EditorConfig.EditorConfig)
- [Kubernetes](https://marketplace.visualstudio.com/items?itemName=ms-kubernetes-tools.vscode-kubernetes-tools) - [Kubernetes](https://marketplace.visualstudio.com/items?itemName=ms-kubernetes-tools.vscode-kubernetes-tools)
- [Prettier](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode)
- [Docker](https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-docker) - [Docker](https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-docker)

View file

@ -2,9 +2,8 @@ version: "3.8"
services: services:
server: server:
build: build:
context: . context: src/server/
dockerfile: Dockerfile dockerfile: Dockerfile
target: server
environment: environment:
- REDIS_HOST=$REDIS_HOST - REDIS_HOST=$REDIS_HOST
- REDIS_PORT=$REDIS_PORT - REDIS_PORT=$REDIS_PORT
@ -18,21 +17,22 @@ services:
emulator: emulator:
build: build:
context: . context: src/emulator/
dockerfile: Dockerfile dockerfile: Dockerfile
target: emulator
environment: environment:
- REDIS_HOST=$REDIS_HOST - REDIS_HOST=$REDIS_HOST
- REDIS_PORT=$REDIS_PORT - REDIS_PORT=$REDIS_PORT
- RTMP_HOST=$RTMP_HOST - RTMP_HOST=$RTMP_HOST
- RTMP_PORT=$RTMP_PORT - RTMP_PORT=$RTMP_PORT
- EMULATOR_RAND_RATE=0.2 - EMULATOR_RAND_RATE=0.2
- EMULATOR_STATES_PATH=$EMULATOR_STATES_PATH
- EMULATOR_ROM_PATH=$EMULATOR_ROM_PATH
depends_on: depends_on:
- rtmp - rtmp
- redis - redis
volumes: volumes:
- ./states/:/code/states/ - ./states/:$EMULATOR_STATES_PATH
- ./roms/:/code/roms/ - ./roms/:/roms/
restart: unless-stopped restart: unless-stopped
redis: redis:

13
src/emulator/.vscode/launch.json vendored Normal file
View file

@ -0,0 +1,13 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Launch (emulator)",
"type": "python",
"request": "launch",
"program": "${workspaceFolder}/main.py",
"console": "integratedTerminal",
"envFile": ""
}
]
}

14
src/emulator/.vscode/setttings.json vendored Normal file
View file

@ -0,0 +1,14 @@
{
"python.formatting.provider": "black",
"editor.formatOnSave": true,
"python.linting.enabled": true,
"python.linting.lintOnSave": true,
"python.linting.flake8Enabled": true,
"python.linting.mypyEnabled": true,
"python.linting.banditEnabled": true,
"[python]": {
"editor.codeActionsOnSave": {
"source.organizeImports": true
}
}
}

17
src/emulator/.vscode/tasks.json vendored Normal file
View file

@ -0,0 +1,17 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "Generate requirements.txt",
"type": "shell",
"command": "$HOME/.local/bin/poetry export --format requirements.txt --output requirements.txt --without-hashes && sed -i '/mgba/d' requirements.txt",
"problemMatcher": []
},
{
"label": "Poetry install",
"type": "shell",
"command": "BINDIR=../mgba/build/ LIBDIR=../mgba/build/ $HOME/.local/bin/poetry install",
"problemMatcher": []
}
]
}

73
src/emulator/Dockerfile Normal file
View file

@ -0,0 +1,73 @@
FROM python:alpine AS base
# set /src as the work directory
WORKDIR /src
# update alpine repositories
RUN apk update
# build tools dependencies
RUN apk add --no-cache build-base cmake git
# mgba dependencies
RUN apk add --no-cache libffi-dev ffmpeg ffmpeg-dev libpng-dev jpeg-dev
RUN pip install --user cffi
# clone mgba
RUN git clone https://github.com/mgba-emu/mgba.git
# create build directory
WORKDIR /src/mgba/build/
# configure the build
RUN cmake \
-DBUILD_PYTHON=ON \
-DBUILD_QT=OFF \
-DBUILD_SDL=OFF \
-DBUILD_GL=OFF \
-DBUILD_GLES2=OFF \
-DBUILD_GLES3=OFF \
-DBUILD_SHARED=OFF \
-DUSE_DEBUGGERS=ON \
-DUSE_DISCORD_RPC=OFF \
-DUSE_EDITLINE=OFF \
-DUSE_ELF=OFF \
-DUSE_EPOXYNG=OFF \
-DUSE_FFMPEG=ON \
-DUSE_GDB_STUB=ON \
-DUSE_LIBZIP=OFF \
-DUSE_LZMA=OFF \
-DUSE_MINIZIP=OFF \
-DUSE_PNG=ON \
-DUSE_SQLITE3=OFF \
-DUSE_ZLIB=OFF \
-DCMAKE_INSTALL_PREFIX:PATH=/usr/local \
..
# build mGBA
RUN make
# install mGBA
RUN make install
WORKDIR /src
# upgrade pip
RUN pip install --upgrade pip
# copy requirements.txt for installation
COPY requirements.txt /src/
# install python dependencies
RUN pip install --user -r /src/requirements.txt
# install mGBA bindings
WORKDIR /src/mgba/src/platform/python
RUN BINDIR=/src/mgba/build/include LIBDIR=/src/mgba/build/include python setup.py install --user
FROM python:alpine
# install required libraries
RUN apk add --no-cache ffmpeg
# copy necessary mgba files
COPY --from=base /root/.local /root/.local
COPY --from=base /usr/local/lib/libmgba.so* /usr/local/lib/
# copy the src files
WORKDIR /src
COPY *.py /src/
# run the application
CMD ["python", "/src/main.py" ]

View file

@ -1,10 +1,5 @@
from os import getenv from os import getenv
WEBSOCKET_HOST: str = getenv("WEBSOCKET_HOST", "localhost")
WEBSOCKET_PORT: int = int(getenv("WEBSOCKET_PORT", 6789))
WEBSOCKET_URI: str = f"ws://{WEBSOCKET_HOST}:{WEBSOCKET_PORT}/"
WEBSOCKET_SERVE: str = getenv("WEBSOCKET_SERVE", "localhost")
RTMP_HOST: str = getenv("RTMP_HOST", "localhost") RTMP_HOST: str = getenv("RTMP_HOST", "localhost")
RTMP_PORT: int = int(getenv("RTMP_PORT", 1935)) RTMP_PORT: int = int(getenv("RTMP_PORT", 1935))
RTMP_URI: str = f"rtmp://{RTMP_HOST}:{RTMP_PORT}/" RTMP_URI: str = f"rtmp://{RTMP_HOST}:{RTMP_PORT}/"
@ -21,7 +16,8 @@ EMULATOR_FPS: int = int(getenv("EMULATOR_FPS", 60))
EMULATOR_SPF: float = 1.0 / EMULATOR_FPS EMULATOR_SPF: float = 1.0 / EMULATOR_FPS
EMULATOR_INPUT_HZ: int = int(getenv("EMULATOR_INPUT_HZ", 10)) EMULATOR_INPUT_HZ: int = int(getenv("EMULATOR_INPUT_HZ", 10))
EMULATOR_POLLING_RATE: int = EMULATOR_FPS // EMULATOR_INPUT_HZ EMULATOR_POLLING_RATE: int = EMULATOR_FPS // EMULATOR_INPUT_HZ
EMULATOR_ROM_PATH: str = getenv("EMULATOR_ROM_PATH", "roms/pokemon.gba") EMULATOR_ROM_PATH: str = getenv("EMULATOR_ROM_PATH", "../../roms/pokemon.gba")
EMULATOR_STATES_PATH: str = getenv("EMULATOR_STATES_PATH", "../../states/")
EMULATOR_RAND_RATE: float = float(getenv("EMULATOR_RAND_RATE", 0.0)) EMULATOR_RAND_RATE: float = float(getenv("EMULATOR_RAND_RATE", 0.0))
FFMPEG_WIDTH: int = int(getenv("FFMPEG_WIDTH", EMULATOR_WIDTH)) FFMPEG_WIDTH: int = int(getenv("FFMPEG_WIDTH", EMULATOR_WIDTH))
@ -29,10 +25,6 @@ FFMPEG_HEIGHT: int = int(getenv("FFMPEG_HEIGHT", EMULATOR_HEIGHT))
FFMPEG_FPS: int = int(getenv("FFMPEG_FPS", 30)) FFMPEG_FPS: int = int(getenv("FFMPEG_FPS", 30))
FFMPEG_BITRATE: str = getenv("FFMPEG_BIRATE", "2M") FFMPEG_BITRATE: str = getenv("FFMPEG_BIRATE", "2M")
PASSWORD_ADMIN: str = getenv("PASSWORD_ADMIN", "password_admin")
USER_TIMEOUT: float = float(getenv("USER_TIMEOUT", 0.5))
KEYMAP: dict[str, int] = { KEYMAP: dict[str, int] = {
"a": 0, "a": 0,
"b": 1, "b": 1,
@ -47,4 +39,4 @@ KEYMAP: dict[str, int] = {
} }
KEYS_ID: tuple[str, ...] = tuple(KEYMAP.keys()) KEYS_ID: tuple[str, ...] = tuple(KEYMAP.keys())
KEYS_MGBA: tuple[int, ...] = tuple(KEYMAP.values()) KEYS_MGBA: tuple[int, ...] = tuple(KEYMAP.values())
KEYS_RESET: dict[str, int] = dict([(x, 0) for x in KEYS_ID]) KEYS_RESET: dict[str, int] = {x: 0 for x in KEYS_ID}

48
src/emulator/ffmpeg.py Normal file
View file

@ -0,0 +1,48 @@
import subprocess
from env import (
EMULATOR_FPS,
EMULATOR_HEIGHT,
EMULATOR_WIDTH,
FFMPEG_BITRATE,
FFMPEG_FPS,
FFMPEG_HEIGHT,
FFMPEG_WIDTH,
RTMP_STREAM_URI,
)
# launch ffmpeg process
ffmpeg_stream = subprocess.Popen(
[
"/usr/bin/ffmpeg",
"-y",
"-f",
"image2pipe",
"-vcodec",
"png",
"-r",
f"{EMULATOR_FPS}",
"-s",
f"{EMULATOR_WIDTH}x{EMULATOR_HEIGHT}",
"-i",
"-",
"-f",
"flv",
"-s",
f"{FFMPEG_WIDTH}x{FFMPEG_HEIGHT}",
"-r",
f"{FFMPEG_FPS}",
"-b:v",
FFMPEG_BITRATE,
"-fflags",
"nobuffer",
"-flags",
"low_delay",
"-strict",
"experimental",
RTMP_STREAM_URI,
],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
)

View file

@ -4,18 +4,13 @@ import asyncio
import logging import logging
import random import random
import time import time
from math import ceil
import mgba.audio
import mgba.core import mgba.core
import mgba.image import mgba.image
import mgba.log import mgba.log
import redis
from ffmpeg_manager import ffmpeg_audio_stream, ffmpeg_video_stream import redis
from redis_manager import RedisManager from env import (
from settings import (
EMULATOR_FPS,
EMULATOR_HEIGHT, EMULATOR_HEIGHT,
EMULATOR_POLLING_RATE, EMULATOR_POLLING_RATE,
EMULATOR_RAND_RATE, EMULATOR_RAND_RATE,
@ -28,7 +23,9 @@ from settings import (
REDIS_HOST, REDIS_HOST,
REDIS_PORT, REDIS_PORT,
) )
from state_manager import StateManager from ffmpeg import ffmpeg_stream
from redis_manager import RedisManager
from states import StateManager
def next_action(core: mgba.core.Core) -> None: def next_action(core: mgba.core.Core) -> None:
@ -61,14 +58,10 @@ async def emulator() -> None:
# save frame to PNG image # save frame to PNG image
image = screen.to_pil().convert("RGB") image = screen.to_pil().convert("RGB")
image.save(ffmpeg_video_stream.stdin, "PNG") image.save(ffmpeg_stream.stdin, "PNG")
n = ceil(96000 / EMULATOR_FPS) # TODO: get audio
audio_buffer = audio_channels.read(n)
for short in audio_buffer:
bidule = short.to_bytes(2, byteorder="little", signed=True)
ffmpeg_audio_stream.write(bidule)
# sleep until next frame, if necessary # sleep until next frame, if necessary
sleep_t = last_frame_t - time.time() + EMULATOR_SPF sleep_t = last_frame_t - time.time() + EMULATOR_SPF
if sleep_t > 0: if sleep_t > 0:
@ -98,12 +91,13 @@ if __name__ == "__main__":
core: mgba.core.Core = mgba.core.load_path(EMULATOR_ROM_PATH) core: mgba.core.Core = mgba.core.load_path(EMULATOR_ROM_PATH)
screen: mgba.image.Image = mgba.image.Image(EMULATOR_WIDTH, EMULATOR_HEIGHT) screen: mgba.image.Image = mgba.image.Image(EMULATOR_WIDTH, EMULATOR_HEIGHT)
core.set_video_buffer(screen) core.set_video_buffer(screen)
audio_channels: mgba.audio.StereoBuffer = core.get_audio_channels()
core.reset() core.reset()
# setup logging format # setup logging format
logging.basicConfig( logging.basicConfig(
level=logging.DEBUG, format="%(asctime)s %(name)s %(levelname)-8s %(message)s", datefmt="(%F %T)" level=logging.DEBUG,
format="%(asctime)s %(name)s %(levelname)-8s %(message)s",
datefmt="(%F %T)",
) )
# change log levels for some libs # change log levels for some libs

View file

@ -75,7 +75,7 @@ python-versions = ">=3.6.1"
[[package]] [[package]]
name = "click" name = "click"
version = "8.0.3" version = "8.0.4"
description = "Composable command line interface toolkit" description = "Composable command line interface toolkit"
category = "dev" category = "dev"
optional = false optional = false
@ -102,7 +102,7 @@ python-versions = "*"
[[package]] [[package]]
name = "filelock" name = "filelock"
version = "3.4.2" version = "3.6.0"
description = "A platform independent file lock." description = "A platform independent file lock."
category = "dev" category = "dev"
optional = false optional = false
@ -150,7 +150,7 @@ smmap = ">=3.0.1,<6"
[[package]] [[package]]
name = "gitpython" name = "gitpython"
version = "3.1.26" version = "3.1.27"
description = "GitPython is a python library used to interact with Git repositories" description = "GitPython is a python library used to interact with Git repositories"
category = "dev" category = "dev"
optional = false optional = false
@ -161,7 +161,7 @@ gitdb = ">=4.0.1,<5"
[[package]] [[package]]
name = "identify" name = "identify"
version = "2.4.9" version = "2.4.11"
description = "File identification library for Python" description = "File identification library for Python"
category = "dev" category = "dev"
optional = false optional = false
@ -211,7 +211,7 @@ pil = ["Pillow (>=2.3)"]
[package.source] [package.source]
type = "directory" type = "directory"
url = "mgba/src/platform/python" url = "../mgba/src/platform/python"
[[package]] [[package]]
name = "mypy" name = "mypy"
@ -272,7 +272,7 @@ python-versions = ">=3.6"
[[package]] [[package]]
name = "platformdirs" name = "platformdirs"
version = "2.5.0" version = "2.5.1"
description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
category = "dev" category = "dev"
optional = false optional = false
@ -416,7 +416,7 @@ python-versions = "*"
[[package]] [[package]]
name = "typing-extensions" name = "typing-extensions"
version = "4.0.1" version = "4.1.1"
description = "Backported and Experimental Type Hints for Python 3.6+" description = "Backported and Experimental Type Hints for Python 3.6+"
category = "dev" category = "dev"
optional = false optional = false
@ -424,7 +424,7 @@ python-versions = ">=3.6"
[[package]] [[package]]
name = "virtualenv" name = "virtualenv"
version = "20.13.1" version = "20.13.2"
description = "Virtual Python Environment builder" description = "Virtual Python Environment builder"
category = "dev" category = "dev"
optional = false optional = false
@ -451,18 +451,10 @@ python-versions = ">=3.6"
[package.extras] [package.extras]
watchmedo = ["PyYAML (>=3.10)"] watchmedo = ["PyYAML (>=3.10)"]
[[package]]
name = "websockets"
version = "10.1"
description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)"
category = "main"
optional = false
python-versions = ">=3.7"
[metadata] [metadata]
lock-version = "1.1" lock-version = "1.1"
python-versions = "^3.10" python-versions = "^3.10"
content-hash = "e199192c3a65de46d43975d91f086a08e7918a9d3147dea63e7a065bb168504b" content-hash = "c35170783ecc20a16fcb8937e5fd5ff1cb0f846d78a11dd1755b08fdc6b09395"
[metadata.files] [metadata.files]
asyncio = [ asyncio = [
@ -561,8 +553,8 @@ cfgv = [
{file = "cfgv-3.3.1.tar.gz", hash = "sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736"}, {file = "cfgv-3.3.1.tar.gz", hash = "sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736"},
] ]
click = [ click = [
{file = "click-8.0.3-py3-none-any.whl", hash = "sha256:353f466495adaeb40b6b5f592f9f91cb22372351c84caeb068132442a4518ef3"}, {file = "click-8.0.4-py3-none-any.whl", hash = "sha256:6a7a62563bbfabfda3a38f3023a1db4a35978c0abd76f6c9605ecd6554d6d9b1"},
{file = "click-8.0.3.tar.gz", hash = "sha256:410e932b050f5eed773c4cda94de75971c89cdb3155a72a0831139a79e5ecb5b"}, {file = "click-8.0.4.tar.gz", hash = "sha256:8458d7b1287c5fb128c90e23381cf99dcde74beaf6c7ff6384ce84d6fe090adb"},
] ]
colorama = [ colorama = [
{file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"},
@ -573,8 +565,8 @@ distlib = [
{file = "distlib-0.3.4.zip", hash = "sha256:e4b58818180336dc9c529bfb9a0b58728ffc09ad92027a3f30b7cd91e3458579"}, {file = "distlib-0.3.4.zip", hash = "sha256:e4b58818180336dc9c529bfb9a0b58728ffc09ad92027a3f30b7cd91e3458579"},
] ]
filelock = [ filelock = [
{file = "filelock-3.4.2-py3-none-any.whl", hash = "sha256:cf0fc6a2f8d26bd900f19bf33915ca70ba4dd8c56903eeb14e1e7a2fd7590146"}, {file = "filelock-3.6.0-py3-none-any.whl", hash = "sha256:f8314284bfffbdcfa0ff3d7992b023d4c628ced6feb957351d4c48d059f56bc0"},
{file = "filelock-3.4.2.tar.gz", hash = "sha256:38b4f4c989f9d06d44524df1b24bd19e167d851f19b50bf3e3559952dddc5b80"}, {file = "filelock-3.6.0.tar.gz", hash = "sha256:9cd540a9352e432c7246a48fe4e8712b10acb1df2ad1f30e8c070b82ae1fed85"},
] ]
flake8 = [ flake8 = [
{file = "flake8-4.0.1-py2.py3-none-any.whl", hash = "sha256:479b1304f72536a55948cb40a32dce8bb0ffe3501e26eaf292c7e60eb5e0428d"}, {file = "flake8-4.0.1-py2.py3-none-any.whl", hash = "sha256:479b1304f72536a55948cb40a32dce8bb0ffe3501e26eaf292c7e60eb5e0428d"},
@ -589,12 +581,12 @@ gitdb = [
{file = "gitdb-4.0.9.tar.gz", hash = "sha256:bac2fd45c0a1c9cf619e63a90d62bdc63892ef92387424b855792a6cabe789aa"}, {file = "gitdb-4.0.9.tar.gz", hash = "sha256:bac2fd45c0a1c9cf619e63a90d62bdc63892ef92387424b855792a6cabe789aa"},
] ]
gitpython = [ gitpython = [
{file = "GitPython-3.1.26-py3-none-any.whl", hash = "sha256:26ac35c212d1f7b16036361ca5cff3ec66e11753a0d677fb6c48fa4e1a9dd8d6"}, {file = "GitPython-3.1.27-py3-none-any.whl", hash = "sha256:5b68b000463593e05ff2b261acff0ff0972df8ab1b70d3cdbd41b546c8b8fc3d"},
{file = "GitPython-3.1.26.tar.gz", hash = "sha256:fc8868f63a2e6d268fb25f481995ba185a85a66fcad126f039323ff6635669ee"}, {file = "GitPython-3.1.27.tar.gz", hash = "sha256:1c885ce809e8ba2d88a29befeb385fcea06338d3640712b59ca623c220bb5704"},
] ]
identify = [ identify = [
{file = "identify-2.4.9-py2.py3-none-any.whl", hash = "sha256:bff7c4959d68510bc28b99d664b6a623e36c6eadc933f89a4e0a9ddff9b4fee4"}, {file = "identify-2.4.11-py2.py3-none-any.whl", hash = "sha256:fd906823ed1db23c7a48f9b176a1d71cb8abede1e21ebe614bac7bdd688d9213"},
{file = "identify-2.4.9.tar.gz", hash = "sha256:e926ae3b3dc142b6a7a9c65433eb14ccac751b724ee255f7c2ed3b5970d764fb"}, {file = "identify-2.4.11.tar.gz", hash = "sha256:2986942d3974c8f2e5019a190523b0b0e2a07cb8e89bf236727fb4b26f27f8fd"},
] ]
isort = [ isort = [
{file = "isort-5.10.1-py3-none-any.whl", hash = "sha256:6f62d78e2f89b4500b080fe3a81690850cd254227f27f75c3a0c491a1f351ba7"}, {file = "isort-5.10.1-py3-none-any.whl", hash = "sha256:6f62d78e2f89b4500b080fe3a81690850cd254227f27f75c3a0c491a1f351ba7"},
@ -687,8 +679,8 @@ pillow = [
{file = "Pillow-8.4.0.tar.gz", hash = "sha256:b8e2f83c56e141920c39464b852de3719dfbfb6e3c99a2d8da0edf4fb33176ed"}, {file = "Pillow-8.4.0.tar.gz", hash = "sha256:b8e2f83c56e141920c39464b852de3719dfbfb6e3c99a2d8da0edf4fb33176ed"},
] ]
platformdirs = [ platformdirs = [
{file = "platformdirs-2.5.0-py3-none-any.whl", hash = "sha256:30671902352e97b1eafd74ade8e4a694782bd3471685e78c32d0fdfd3aa7e7bb"}, {file = "platformdirs-2.5.1-py3-none-any.whl", hash = "sha256:bcae7cab893c2d310a711b70b24efb93334febe65f8de776ee320b517471e227"},
{file = "platformdirs-2.5.0.tar.gz", hash = "sha256:8ec11dfba28ecc0715eb5fb0147a87b1bf325f349f3da9aab2cd6b50b96b692b"}, {file = "platformdirs-2.5.1.tar.gz", hash = "sha256:7535e70dfa32e84d4b34996ea99c5e432fa29a708d0f4e394bbcb2a8faa4f16d"},
] ]
pre-commit = [ pre-commit = [
{file = "pre_commit-2.17.0-py2.py3-none-any.whl", hash = "sha256:725fa7459782d7bec5ead072810e47351de01709be838c2ce1726b9591dad616"}, {file = "pre_commit-2.17.0-py2.py3-none-any.whl", hash = "sha256:725fa7459782d7bec5ead072810e47351de01709be838c2ce1726b9591dad616"},
@ -778,12 +770,12 @@ types-redis = [
{file = "types_redis-3.5.18-py3-none-any.whl", hash = "sha256:5c55c4b9e8ebdc6d57d4e47900b77d99f19ca0a563264af3f701246ed0926335"}, {file = "types_redis-3.5.18-py3-none-any.whl", hash = "sha256:5c55c4b9e8ebdc6d57d4e47900b77d99f19ca0a563264af3f701246ed0926335"},
] ]
typing-extensions = [ typing-extensions = [
{file = "typing_extensions-4.0.1-py3-none-any.whl", hash = "sha256:7f001e5ac290a0c0401508864c7ec868be4e701886d5b573a9528ed3973d9d3b"}, {file = "typing_extensions-4.1.1-py3-none-any.whl", hash = "sha256:21c85e0fe4b9a155d0799430b0ad741cdce7e359660ccbd8b530613e8df88ce2"},
{file = "typing_extensions-4.0.1.tar.gz", hash = "sha256:4ca091dea149f945ec56afb48dae714f21e8692ef22a395223bcd328961b6a0e"}, {file = "typing_extensions-4.1.1.tar.gz", hash = "sha256:1a9462dcc3347a79b1f1c0271fbe79e844580bb598bafa1ed208b94da3cdcd42"},
] ]
virtualenv = [ virtualenv = [
{file = "virtualenv-20.13.1-py2.py3-none-any.whl", hash = "sha256:45e1d053cad4cd453181ae877c4ffc053546ae99e7dd049b9ff1d9be7491abf7"}, {file = "virtualenv-20.13.2-py2.py3-none-any.whl", hash = "sha256:e7b34c9474e6476ee208c43a4d9ac1510b041c68347eabfe9a9ea0c86aa0a46b"},
{file = "virtualenv-20.13.1.tar.gz", hash = "sha256:e0621bcbf4160e4e1030f05065c8834b4e93f4fcc223255db2a823440aca9c14"}, {file = "virtualenv-20.13.2.tar.gz", hash = "sha256:01f5f80744d24a3743ce61858123488e91cb2dd1d3bdf92adaf1bba39ffdedf0"},
] ]
watchdog = [ watchdog = [
{file = "watchdog-2.1.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:9693f35162dc6208d10b10ddf0458cc09ad70c30ba689d9206e02cd836ce28a3"}, {file = "watchdog-2.1.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:9693f35162dc6208d10b10ddf0458cc09ad70c30ba689d9206e02cd836ce28a3"},
@ -810,53 +802,3 @@ watchdog = [
{file = "watchdog-2.1.6-py3-none-win_ia64.whl", hash = "sha256:a0f1c7edf116a12f7245be06120b1852275f9506a7d90227648b250755a03923"}, {file = "watchdog-2.1.6-py3-none-win_ia64.whl", hash = "sha256:a0f1c7edf116a12f7245be06120b1852275f9506a7d90227648b250755a03923"},
{file = "watchdog-2.1.6.tar.gz", hash = "sha256:a36e75df6c767cbf46f61a91c70b3ba71811dfa0aca4a324d9407a06a8b7a2e7"}, {file = "watchdog-2.1.6.tar.gz", hash = "sha256:a36e75df6c767cbf46f61a91c70b3ba71811dfa0aca4a324d9407a06a8b7a2e7"},
] ]
websockets = [
{file = "websockets-10.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:38db6e2163b021642d0a43200ee2dec8f4980bdbda96db54fde72b283b54cbfc"},
{file = "websockets-10.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e1b60fd297adb9fc78375778a5220da7f07bf54d2a33ac781319650413fc6a60"},
{file = "websockets-10.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3477146d1f87ead8df0f27e8960249f5248dceb7c2741e8bbec9aa5338d0c053"},
{file = "websockets-10.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb01ea7b5f52e7125bdc3c5807aeaa2d08a0553979cf2d96a8b7803ea33e15e7"},
{file = "websockets-10.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:9fd62c6dc83d5d35fb6a84ff82ec69df8f4657fff05f9cd6c7d9bec0dd57f0f6"},
{file = "websockets-10.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3bbf080f3892ba1dc8838786ec02899516a9d227abe14a80ef6fd17d4fb57127"},
{file = "websockets-10.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:5560558b0dace8312c46aa8915da977db02738ac8ecffbc61acfbfe103e10155"},
{file = "websockets-10.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:667c41351a6d8a34b53857ceb8343a45c85d438ee4fd835c279591db8aeb85be"},
{file = "websockets-10.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:468f0031fdbf4d643f89403a66383247eb82803430b14fa27ce2d44d2662ca37"},
{file = "websockets-10.1-cp310-cp310-win32.whl", hash = "sha256:d0d81b46a5c87d443e40ce2272436da8e6092aa91f5fbeb60d1be9f11eff5b4c"},
{file = "websockets-10.1-cp310-cp310-win_amd64.whl", hash = "sha256:b68b6caecb9a0c6db537aa79750d1b592a841e4f1a380c6196091e65b2ad35f9"},
{file = "websockets-10.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a249139abc62ef333e9e85064c27fefb113b16ffc5686cefc315bdaef3eefbc8"},
{file = "websockets-10.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8877861e3dee38c8d302eee0d5dbefa6663de3b46dc6a888f70cd7e82562d1f7"},
{file = "websockets-10.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:e3872ae57acd4306ecf937d36177854e218e999af410a05c17168cd99676c512"},
{file = "websockets-10.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b66e6d514f12c28d7a2d80bb2a48ef223342e99c449782d9831b0d29a9e88a17"},
{file = "websockets-10.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:9f304a22ece735a3da8a51309bc2c010e23961a8f675fae46fdf62541ed62123"},
{file = "websockets-10.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:189ed478395967d6a98bb293abf04e8815349e17456a0a15511f1088b6cb26e4"},
{file = "websockets-10.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:08a42856158307e231b199671c4fce52df5786dd3d703f36b5d8ac76b206c485"},
{file = "websockets-10.1-cp37-cp37m-win32.whl", hash = "sha256:3ef6f73854cded34e78390dbdf40dfdcf0b89b55c0e282468ef92646fce8d13a"},
{file = "websockets-10.1-cp37-cp37m-win_amd64.whl", hash = "sha256:89e985d40d407545d5f5e2e58e1fdf19a22bd2d8cd54d20a882e29f97e930a0a"},
{file = "websockets-10.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:002071169d2e44ce8eb9e5ebac9fbce142ba4b5146eef1cfb16b177a27662657"},
{file = "websockets-10.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cfae282c2aa7f0c4be45df65c248481f3509f8c40ca8b15ed96c35668ae0ff69"},
{file = "websockets-10.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:97b4b68a2ddaf5c4707ae79c110bfd874c5be3c6ac49261160fb243fa45d8bbb"},
{file = "websockets-10.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7c9407719f42cb77049975410490c58a705da6af541adb64716573e550e5c9db"},
{file = "websockets-10.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:1d858fb31e5ac992a2cdf17e874c95f8a5b1e917e1fb6b45ad85da30734b223f"},
{file = "websockets-10.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:7bdd3d26315db0a9cf8a0af30ca95e0aa342eda9c1377b722e71ccd86bc5d1dd"},
{file = "websockets-10.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:e259be0863770cb91b1a6ccf6907f1ac2f07eff0b7f01c249ed751865a70cb0d"},
{file = "websockets-10.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:6b014875fae19577a392372075e937ebfebf53fd57f613df07b35ab210f31534"},
{file = "websockets-10.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:98de71f86bdb29430fd7ba9997f47a6b10866800e3ea577598a786a785701bb0"},
{file = "websockets-10.1-cp38-cp38-win32.whl", hash = "sha256:3a02ab91d84d9056a9ee833c254895421a6333d7ae7fff94b5c68e4fa8095519"},
{file = "websockets-10.1-cp38-cp38-win_amd64.whl", hash = "sha256:7d6673b2753f9c5377868a53445d0c321ef41ff3c8e3b6d57868e72054bfce5f"},
{file = "websockets-10.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:ddab2dc69ee5ae27c74dbfe9d7bb6fee260826c136dca257faa1a41d1db61a89"},
{file = "websockets-10.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:14e9cf68a08d1a5d42109549201aefba473b1d925d233ae19035c876dd845da9"},
{file = "websockets-10.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e4819c6fb4f336fd5388372cb556b1f3a165f3f68e66913d1a2fc1de55dc6f58"},
{file = "websockets-10.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:05e7f098c76b0a4743716590bb8f9706de19f1ef5148d61d0cf76495ec3edb9c"},
{file = "websockets-10.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5bb6256de5a4fb1d42b3747b4e2268706c92965d75d0425be97186615bf2f24f"},
{file = "websockets-10.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:888a5fa2a677e0c2b944f9826c756475980f1b276b6302e606f5c4ff5635be9e"},
{file = "websockets-10.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:6fdec1a0b3e5630c58e3d8704d2011c678929fce90b40908c97dfc47de8dca72"},
{file = "websockets-10.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:531d8eb013a9bc6b3ad101588182aa9b6dd994b190c56df07f0d84a02b85d530"},
{file = "websockets-10.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0d93b7cadc761347d98da12ec1930b5c71b2096f1ceed213973e3cda23fead9c"},
{file = "websockets-10.1-cp39-cp39-win32.whl", hash = "sha256:d9b245db5a7e64c95816e27d72830e51411c4609c05673d1ae81eb5d23b0be54"},
{file = "websockets-10.1-cp39-cp39-win_amd64.whl", hash = "sha256:882c0b8bdff3bf1bd7f024ce17c6b8006042ec4cceba95cf15df57e57efa471c"},
{file = "websockets-10.1-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:10edd9d7d3581cfb9ff544ac09fc98cab7ee8f26778a5a8b2d5fd4b0684c5ba5"},
{file = "websockets-10.1-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:baa83174390c0ff4fc1304fbe24393843ac7a08fdd59295759c4b439e06b1536"},
{file = "websockets-10.1-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:483edee5abed738a0b6a908025be47f33634c2ad8e737edd03ffa895bd600909"},
{file = "websockets-10.1-pp37-pypy37_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:816ae7dac2c6522cfa620947ead0ca95ac654916eebf515c94d7c28de5601a6e"},
{file = "websockets-10.1-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:1dafe98698ece09b8ccba81b910643ff37198e43521d977be76caf37709cf62b"},
{file = "websockets-10.1.tar.gz", hash = "sha256:181d2b25de5a437b36aefedaf006ecb6fa3aa1328ec0236cdde15f32f9d3ff6d"},
]

View file

@ -1,5 +1,5 @@
[tool.poetry] [tool.poetry]
name = "booplaysgba" name = "booplaysgba_emulator"
version = "0.1.0" version = "0.1.0"
description = "Émulateur collaboratif pour patienter dans le B00" description = "Émulateur collaboratif pour patienter dans le B00"
authors = ["Laureηt <laurentfainsin@protonmail.com>"] authors = ["Laureηt <laurentfainsin@protonmail.com>"]
@ -8,12 +8,10 @@ license = "MIT"
[tool.poetry.dependencies] [tool.poetry.dependencies]
python = "^3.10" python = "^3.10"
asyncio = "^3.4.3" asyncio = "^3.4.3"
websockets = "^10.1"
cffi = "^1.15.0" cffi = "^1.15.0"
cached-property = "^1.5.2" cached-property = "^1.5.2"
Pillow = "^8.4.0" Pillow = "^8.4.0"
redis = "^3.5.3" redis = "^3.5.3"
mgba = { path="mgba/src/platform/python" }
watchdog = "^2.1.6" watchdog = "^2.1.6"
[tool.poetry.dev-dependencies] [tool.poetry.dev-dependencies]
@ -25,6 +23,7 @@ bandit = "^1.7.0"
isort = "^5.9.3" isort = "^5.9.3"
mypy = "^0.931" mypy = "^0.931"
types-redis = "^3.5.15" types-redis = "^3.5.15"
mgba = { path="../mgba/src/platform/python" }
[build-system] [build-system]
requires = ["poetry-core>=1.0.0"] requires = ["poetry-core>=1.0.0"]

View file

@ -4,26 +4,26 @@ import threading
import time import time
import mgba.core import mgba.core
import redis
from mgba._pylib import ffi from mgba._pylib import ffi
import redis
from env import EMULATOR_STATES_PATH
async def save(core: mgba.core.Core) -> None: async def save(core: mgba.core.Core) -> None:
state = core.save_raw_state() state = core.save_raw_state()
current_time = time.strftime("%Y-%m-%dT%H:%M:%S") current_time = time.strftime("%Y-%m-%dT%H:%M:%S")
with open(f"states/{current_time}.state", "wb") as state_file: with open(f"{EMULATOR_STATES_PATH}/{current_time}.state", "wb") as state_file:
for byte in state: state_file.write(bytes(state))
state_file.write(byte.to_bytes(4, byteorder="big", signed=False))
logging.debug(f"state saved : {current_time}.state") logging.debug(f"state saved : {current_time}.state")
async def load(core: mgba.core.Core, filename: str) -> None: async def load(core: mgba.core.Core, filename: str) -> None:
state = ffi.new("unsigned char[397312]") # pulled 397312 straight from my ass, TODO: check mGBA sources ? state = ffi.new("unsigned char[]", core._core.stateSize(core._core))
with open(f"states/{filename}.state", "rb") as state_file: with open(f"{EMULATOR_STATES_PATH}/{filename}.state", "rb") as state_file:
for i in range(len(state)): state_file.readinto(ffi.buffer(state))
state[i] = int.from_bytes(state_file.read(4), byteorder="big", signed=False)
core.load_raw_state(state) core.load_raw_state(state)
logging.debug(f"state loaded : {filename}") logging.debug(f"state loaded : {filename}.state")
class RedisManager(threading.Thread): class RedisManager(threading.Thread):

View file

@ -0,0 +1,7 @@
asyncio==3.4.3
cached-property==1.5.2
cffi==1.15.0
pillow==8.4.0; python_version >= "3.6"
pycparser==2.21; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.4.0"
redis==3.5.3; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.5.0")
watchdog==2.1.6; python_version >= "3.6"

View file

@ -1,10 +1,12 @@
import logging import logging
import os import os
import redis
import watchdog.observers import watchdog.observers
from watchdog.events import FileCreatedEvent, FileDeletedEvent, FileSystemEventHandler from watchdog.events import FileCreatedEvent, FileDeletedEvent, FileSystemEventHandler
import redis
from env import EMULATOR_STATES_PATH
class StateHandler(FileSystemEventHandler): class StateHandler(FileSystemEventHandler):
def __init__(self, redis: redis.Redis) -> None: def __init__(self, redis: redis.Redis) -> None:
@ -39,11 +41,11 @@ class StateManager(watchdog.observers.Observer):
super().__init__() super().__init__()
# setup states in redis # setup states in redis
files = os.listdir("./states") files = os.listdir(EMULATOR_STATES_PATH)
statefiles = list(filter(lambda x: x.endswith(".state"), files)) statefiles = list(filter(lambda x: x.endswith(".state"), files))
states = list(map(lambda x: x.removesuffix(".state"), statefiles)) states = list(map(lambda x: x.removesuffix(".state"), statefiles))
redis.sadd("states", *states) redis.sadd("states", *states)
logging.debug("redis server populated with states") logging.debug("redis server populated with states")
state_handler = StateHandler(redis) state_handler = StateHandler(redis)
self.schedule(state_handler, "./states", recursive=False) self.schedule(state_handler, EMULATOR_STATES_PATH, recursive=False)

View file

@ -1,111 +0,0 @@
import subprocess
from settings import (
EMULATOR_FPS,
EMULATOR_HEIGHT,
EMULATOR_WIDTH,
FFMPEG_BITRATE,
FFMPEG_FPS,
FFMPEG_HEIGHT,
FFMPEG_WIDTH,
RTMP_STREAM_URI,
)
# launch ffmpeg process
ffmpeg_video_stream = subprocess.Popen(
[
"/usr/bin/ffmpeg", # ffmpeg binary location
"-y", # overwrite output files without asking
"-f", # force input file format
"image2pipe", # allows to pipe images to ffmpeg
"-vcodec", # set the video codec
"png", # input images are PNGs
"-r", # set input frame rate
f"{EMULATOR_FPS}",
"-s", # set input frame size
f"{EMULATOR_WIDTH}x{EMULATOR_HEIGHT}",
"-i", # input file url
"pipe:0", # use stdin (pipe n°0) for input
"-f", # force input or output file format
"flv", # output an flv video
"-s", # set output frame size
f"{FFMPEG_WIDTH}x{FFMPEG_HEIGHT}",
"-r", # set output frame rate
f"{FFMPEG_FPS}",
"-b:v", # set video output bitrate
FFMPEG_BITRATE,
"-fflags", # set format flags
"nobuffer", # reduce the latency introduced by buffering during initial input streams analysis
"-flags", # set generic flags
"low_delay", # force low delay
"-strict", # specify how strictly to follow the standards
"experimental", # allow non standardized experimental encoders...
RTMP_STREAM_URI, # where to output the video
],
stdin=subprocess.PIPE,
stdout=subprocess.DEVNULL,
stderr=subprocess.STDOUT,
)
ffmpeg_audio_stream = subprocess.Popen(
# [
# "/usr/bin/ffmpeg", # FFMPEG_BIN
# "-i",
# "-",
# "-f",
# "s24le",
# "-acodec",
# "pcm_s24le",
# "-ar",
# "44100", # ouput will have 44100 Hz
# "-ac",
# "2", # stereo (set to '1' for mono)
# RTMP_STREAM_URI,
# ],
# [
# "/usr/bin/ffmpeg", # FFMPEG_BIN
# "-f", # audio format
# "s24le",
# "-acodec", # audio codec
# "pcm_s24le",
# "-ar", # audio sampling rate
# "44100",
# "-ac", # number of audio canals
# "2", # stereo (set to '1' for mono)
# "-i",
# "pipe:",
# "-y",
# "-f",
# "flv",
# # "-tune",
# # "zerolatency",
# "-fflags", # set format flags
# "nobuffer", # reduce the latency introduced by buffering during initial input streams analysis
# "-flags", # set generic flags
# "low_delay", # force low delay
# "-strict", # specify how strictly to follow the standards
# "experimental", # allow non standardized experimental things
# RTMP_STREAM_URI,
# ],
[
"/usr/bin/ffmpeg", # FFMPEG_BIN
"-f",
"-y",
"s24le",
"-acodec",
"pcm_s24le",
"-ar",
"44100", # ouput will have 44100 Hz
"-ac",
"2", # stereo (set to '1' for mono)
"-i",
"-",
"-f",
"bonjour",
],
stdin=subprocess.PIPE,
# stdout=subprocess.STDOUT,
# stderr=subprocess.STDOUT,
)
# https://stackoverflow.com/questions/67388548/multiple-named-pipes-in-ffmpeg

View file

13
src/server/.vscode/launch.json vendored Normal file
View file

@ -0,0 +1,13 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Launch (server)",
"type": "python",
"request": "launch",
"program": "${workspaceFolder}/main.py",
"console": "integratedTerminal",
"envFile": ""
}
]
}

14
src/server/.vscode/setttings.json vendored Normal file
View file

@ -0,0 +1,14 @@
{
"python.formatting.provider": "black",
"editor.formatOnSave": true,
"python.linting.enabled": true,
"python.linting.lintOnSave": true,
"python.linting.flake8Enabled": true,
"python.linting.mypyEnabled": true,
"python.linting.banditEnabled": true,
"[python]": {
"editor.codeActionsOnSave": {
"source.organizeImports": true
}
}
}

17
src/server/.vscode/tasks.json vendored Normal file
View file

@ -0,0 +1,17 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "Generate requirements.txt",
"type": "shell",
"command": "$HOME/.local/bin/poetry export --format requirements.txt --output requirements.txt --without-hashes",
"problemMatcher": []
},
{
"label": "Poetry install",
"type": "shell",
"command": "$HOME/.local/bin/poetry install",
"problemMatcher": []
}
]
}

22
src/server/Dockerfile Normal file
View file

@ -0,0 +1,22 @@
FROM python:alpine AS base
# set /src as the work directory
WORKDIR /src
# copy requirements.txt for installation
COPY requirements.txt /src/
# upgrade pip
RUN pip install --upgrade pip
# install python dependencies
RUN pip install -r /src/requirements.txt
# remove requirements.txt
RUN rm /src/requirements.txt
# copy the src files
COPY *.py /src/
# run the application
CMD [ "python", "/src/main.py" ]

27
src/server/env.py Normal file
View file

@ -0,0 +1,27 @@
from os import getenv
WEBSOCKET_HOST: str = getenv("WEBSOCKET_HOST", "localhost")
WEBSOCKET_PORT: int = int(getenv("WEBSOCKET_PORT", 6789))
WEBSOCKET_URI: str = f"ws://{WEBSOCKET_HOST}:{WEBSOCKET_PORT}/"
WEBSOCKET_SERVE: str = getenv("WEBSOCKET_SERVE", "localhost")
REDIS_HOST: str = getenv("REDIS_HOST", "localhost")
REDIS_PORT: int = int(getenv("REDIS_PORT", 6379))
USER_TIMEOUT: float = float(getenv("USER_TIMEOUT", 0.5))
KEYMAP: dict[str, int] = {
"a": 0,
"b": 1,
"select": 2,
"start": 3,
"right": 4,
"left": 5,
"up": 6,
"down": 7,
"r": 8,
"l": 9,
}
KEYS_ID: tuple[str, ...] = tuple(KEYMAP.keys())
KEYS_MGBA: tuple[int, ...] = tuple(KEYMAP.values())
KEYS_RESET: dict[str, int] = {x: 0 for x in KEYS_ID}

View file

@ -9,8 +9,9 @@ import websockets
import websockets.exceptions import websockets.exceptions
import websockets.server import websockets.server
import websockets.typing import websockets.typing
from utils import User, Users
from settings import ( from env import (
KEYS_ID, KEYS_ID,
KEYS_RESET, KEYS_RESET,
REDIS_HOST, REDIS_HOST,
@ -19,7 +20,6 @@ from settings import (
WEBSOCKET_PORT, WEBSOCKET_PORT,
WEBSOCKET_SERVE, WEBSOCKET_SERVE,
) )
from utils import User, Users
async def parse_message(user: User, message: websockets.typing.Data) -> None: async def parse_message(user: User, message: websockets.typing.Data) -> None:
@ -40,7 +40,9 @@ async def parse_message(user: User, message: websockets.typing.Data) -> None:
logging.error(f"unsupported action: {msg} from {user}") logging.error(f"unsupported action: {msg} from {user}")
async def handler(websocket: websockets.server.WebSocketServerProtocol, path: str) -> None: async def handler(
websocket: websockets.server.WebSocketServerProtocol, path: str
) -> None:
"""Handle the messages sent by a user. """Handle the messages sent by a user.
Args: Args:
@ -73,7 +75,9 @@ if __name__ == "__main__":
# setup logging format # setup logging format
logging.basicConfig( logging.basicConfig(
level=logging.DEBUG, format="%(asctime)s %(name)s %(levelname)-8s %(message)s", datefmt="(%F %T)" level=logging.DEBUG,
format="%(asctime)s %(name)s %(levelname)-8s %(message)s",
datefmt="(%F %T)",
) )
# change log levels for some libs # change log levels for some libs

666
src/server/poetry.lock generated Normal file
View file

@ -0,0 +1,666 @@
[[package]]
name = "asyncio"
version = "3.4.3"
description = "reference implementation of PEP 3156"
category = "main"
optional = false
python-versions = "*"
[[package]]
name = "bandit"
version = "1.7.2"
description = "Security oriented static analyser for python code."
category = "dev"
optional = false
python-versions = ">=3.7"
[package.dependencies]
colorama = {version = ">=0.3.9", markers = "platform_system == \"Windows\""}
GitPython = ">=1.0.1"
PyYAML = ">=5.3.1"
stevedore = ">=1.20.0"
[package.extras]
test = ["beautifulsoup4 (>=4.8.0)", "coverage (>=4.5.4)", "fixtures (>=3.0.0)", "flake8 (>=4.0.0)", "pylint (==1.9.4)", "stestr (>=2.5.0)", "testscenarios (>=0.5.0)", "testtools (>=2.3.0)", "toml"]
toml = ["toml"]
yaml = ["pyyaml"]
[[package]]
name = "black"
version = "22.1.0"
description = "The uncompromising code formatter."
category = "dev"
optional = false
python-versions = ">=3.6.2"
[package.dependencies]
click = ">=8.0.0"
mypy-extensions = ">=0.4.3"
pathspec = ">=0.9.0"
platformdirs = ">=2"
tomli = ">=1.1.0"
[package.extras]
colorama = ["colorama (>=0.4.3)"]
d = ["aiohttp (>=3.7.4)"]
jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"]
uvloop = ["uvloop (>=0.15.2)"]
[[package]]
name = "cfgv"
version = "3.3.1"
description = "Validate configuration and produce human readable error messages."
category = "dev"
optional = false
python-versions = ">=3.6.1"
[[package]]
name = "click"
version = "8.0.4"
description = "Composable command line interface toolkit"
category = "dev"
optional = false
python-versions = ">=3.6"
[package.dependencies]
colorama = {version = "*", markers = "platform_system == \"Windows\""}
[[package]]
name = "colorama"
version = "0.4.4"
description = "Cross-platform colored terminal text."
category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
[[package]]
name = "distlib"
version = "0.3.4"
description = "Distribution utilities"
category = "dev"
optional = false
python-versions = "*"
[[package]]
name = "filelock"
version = "3.6.0"
description = "A platform independent file lock."
category = "dev"
optional = false
python-versions = ">=3.7"
[package.extras]
docs = ["furo (>=2021.8.17b43)", "sphinx (>=4.1)", "sphinx-autodoc-typehints (>=1.12)"]
testing = ["covdefaults (>=1.2.0)", "coverage (>=4)", "pytest (>=4)", "pytest-cov", "pytest-timeout (>=1.4.2)"]
[[package]]
name = "flake8"
version = "4.0.1"
description = "the modular source code checker: pep8 pyflakes and co"
category = "dev"
optional = false
python-versions = ">=3.6"
[package.dependencies]
mccabe = ">=0.6.0,<0.7.0"
pycodestyle = ">=2.8.0,<2.9.0"
pyflakes = ">=2.4.0,<2.5.0"
[[package]]
name = "flake8-docstrings"
version = "1.6.0"
description = "Extension for flake8 which uses pydocstyle to check docstrings"
category = "dev"
optional = false
python-versions = "*"
[package.dependencies]
flake8 = ">=3"
pydocstyle = ">=2.1"
[[package]]
name = "gitdb"
version = "4.0.9"
description = "Git Object Database"
category = "dev"
optional = false
python-versions = ">=3.6"
[package.dependencies]
smmap = ">=3.0.1,<6"
[[package]]
name = "gitpython"
version = "3.1.27"
description = "GitPython is a python library used to interact with Git repositories"
category = "dev"
optional = false
python-versions = ">=3.7"
[package.dependencies]
gitdb = ">=4.0.1,<5"
[[package]]
name = "identify"
version = "2.4.11"
description = "File identification library for Python"
category = "dev"
optional = false
python-versions = ">=3.7"
[package.extras]
license = ["ukkonen"]
[[package]]
name = "isort"
version = "5.10.1"
description = "A Python utility / library to sort Python imports."
category = "dev"
optional = false
python-versions = ">=3.6.1,<4.0"
[package.extras]
pipfile_deprecated_finder = ["pipreqs", "requirementslib"]
requirements_deprecated_finder = ["pipreqs", "pip-api"]
colors = ["colorama (>=0.4.3,<0.5.0)"]
plugins = ["setuptools"]
[[package]]
name = "mccabe"
version = "0.6.1"
description = "McCabe checker, plugin for flake8"
category = "dev"
optional = false
python-versions = "*"
[[package]]
name = "mypy"
version = "0.931"
description = "Optional static typing for Python"
category = "dev"
optional = false
python-versions = ">=3.6"
[package.dependencies]
mypy-extensions = ">=0.4.3"
tomli = ">=1.1.0"
typing-extensions = ">=3.10"
[package.extras]
dmypy = ["psutil (>=4.0)"]
python2 = ["typed-ast (>=1.4.0,<2)"]
[[package]]
name = "mypy-extensions"
version = "0.4.3"
description = "Experimental type system extensions for programs checked with the mypy typechecker."
category = "dev"
optional = false
python-versions = "*"
[[package]]
name = "nodeenv"
version = "1.6.0"
description = "Node.js virtual environment builder"
category = "dev"
optional = false
python-versions = "*"
[[package]]
name = "pathspec"
version = "0.9.0"
description = "Utility library for gitignore style pattern matching of file paths."
category = "dev"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7"
[[package]]
name = "pbr"
version = "5.8.1"
description = "Python Build Reasonableness"
category = "dev"
optional = false
python-versions = ">=2.6"
[[package]]
name = "platformdirs"
version = "2.5.1"
description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
category = "dev"
optional = false
python-versions = ">=3.7"
[package.extras]
docs = ["Sphinx (>=4)", "furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx-autodoc-typehints (>=1.12)"]
test = ["appdirs (==1.4.4)", "pytest (>=6)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)"]
[[package]]
name = "pre-commit"
version = "2.17.0"
description = "A framework for managing and maintaining multi-language pre-commit hooks."
category = "dev"
optional = false
python-versions = ">=3.6.1"
[package.dependencies]
cfgv = ">=2.0.0"
identify = ">=1.0.0"
nodeenv = ">=0.11.1"
pyyaml = ">=5.1"
toml = "*"
virtualenv = ">=20.0.8"
[[package]]
name = "pycodestyle"
version = "2.8.0"
description = "Python style guide checker"
category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
[[package]]
name = "pydocstyle"
version = "6.1.1"
description = "Python docstring style checker"
category = "dev"
optional = false
python-versions = ">=3.6"
[package.dependencies]
snowballstemmer = "*"
[package.extras]
toml = ["toml"]
[[package]]
name = "pyflakes"
version = "2.4.0"
description = "passive checker of Python programs"
category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
[[package]]
name = "pyyaml"
version = "6.0"
description = "YAML parser and emitter for Python"
category = "dev"
optional = false
python-versions = ">=3.6"
[[package]]
name = "redis"
version = "3.5.3"
description = "Python client for Redis key-value store"
category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
[package.extras]
hiredis = ["hiredis (>=0.1.3)"]
[[package]]
name = "six"
version = "1.16.0"
description = "Python 2 and 3 compatibility utilities"
category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
[[package]]
name = "smmap"
version = "5.0.0"
description = "A pure Python implementation of a sliding window memory map manager"
category = "dev"
optional = false
python-versions = ">=3.6"
[[package]]
name = "snowballstemmer"
version = "2.2.0"
description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms."
category = "dev"
optional = false
python-versions = "*"
[[package]]
name = "stevedore"
version = "3.5.0"
description = "Manage dynamic plugins for Python applications"
category = "dev"
optional = false
python-versions = ">=3.6"
[package.dependencies]
pbr = ">=2.0.0,<2.1.0 || >2.1.0"
[[package]]
name = "toml"
version = "0.10.2"
description = "Python Library for Tom's Obvious, Minimal Language"
category = "dev"
optional = false
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
[[package]]
name = "tomli"
version = "2.0.1"
description = "A lil' TOML parser"
category = "dev"
optional = false
python-versions = ">=3.7"
[[package]]
name = "types-redis"
version = "3.5.18"
description = "Typing stubs for redis"
category = "dev"
optional = false
python-versions = "*"
[[package]]
name = "typing-extensions"
version = "4.1.1"
description = "Backported and Experimental Type Hints for Python 3.6+"
category = "dev"
optional = false
python-versions = ">=3.6"
[[package]]
name = "virtualenv"
version = "20.13.2"
description = "Virtual Python Environment builder"
category = "dev"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7"
[package.dependencies]
distlib = ">=0.3.1,<1"
filelock = ">=3.2,<4"
platformdirs = ">=2,<3"
six = ">=1.9.0,<2"
[package.extras]
docs = ["proselint (>=0.10.2)", "sphinx (>=3)", "sphinx-argparse (>=0.2.5)", "sphinx-rtd-theme (>=0.4.3)", "towncrier (>=21.3)"]
testing = ["coverage (>=4)", "coverage-enable-subprocess (>=1)", "flaky (>=3)", "pytest (>=4)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.1)", "pytest-mock (>=2)", "pytest-randomly (>=1)", "pytest-timeout (>=1)", "packaging (>=20.0)"]
[[package]]
name = "websockets"
version = "10.2"
description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)"
category = "main"
optional = false
python-versions = ">=3.7"
[metadata]
lock-version = "1.1"
python-versions = "^3.10"
content-hash = "1f0ba9315addf55ee17a0466f3b86388304919bae5ba89dd4daa3c4cc3256bc1"
[metadata.files]
asyncio = [
{file = "asyncio-3.4.3-cp33-none-win32.whl", hash = "sha256:b62c9157d36187eca799c378e572c969f0da87cd5fc42ca372d92cdb06e7e1de"},
{file = "asyncio-3.4.3-cp33-none-win_amd64.whl", hash = "sha256:c46a87b48213d7464f22d9a497b9eef8c1928b68320a2fa94240f969f6fec08c"},
{file = "asyncio-3.4.3-py3-none-any.whl", hash = "sha256:c4d18b22701821de07bd6aea8b53d21449ec0ec5680645e5317062ea21817d2d"},
{file = "asyncio-3.4.3.tar.gz", hash = "sha256:83360ff8bc97980e4ff25c964c7bd3923d333d177aa4f7fb736b019f26c7cb41"},
]
bandit = [
{file = "bandit-1.7.2-py3-none-any.whl", hash = "sha256:e20402cadfd126d85b68ed4c8862959663c8c372dbbb1fca8f8e2c9f55a067ec"},
{file = "bandit-1.7.2.tar.gz", hash = "sha256:6d11adea0214a43813887bfe71a377b5a9955e4c826c8ffd341b494e3ab25260"},
]
black = [
{file = "black-22.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:1297c63b9e1b96a3d0da2d85d11cd9bf8664251fd69ddac068b98dc4f34f73b6"},
{file = "black-22.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2ff96450d3ad9ea499fc4c60e425a1439c2120cbbc1ab959ff20f7c76ec7e866"},
{file = "black-22.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e21e1f1efa65a50e3960edd068b6ae6d64ad6235bd8bfea116a03b21836af71"},
{file = "black-22.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2f69158a7d120fd641d1fa9a921d898e20d52e44a74a6fbbcc570a62a6bc8ab"},
{file = "black-22.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:228b5ae2c8e3d6227e4bde5920d2fc66cc3400fde7bcc74f480cb07ef0b570d5"},
{file = "black-22.1.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:b1a5ed73ab4c482208d20434f700d514f66ffe2840f63a6252ecc43a9bc77e8a"},
{file = "black-22.1.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:35944b7100af4a985abfcaa860b06af15590deb1f392f06c8683b4381e8eeaf0"},
{file = "black-22.1.0-cp36-cp36m-win_amd64.whl", hash = "sha256:7835fee5238fc0a0baf6c9268fb816b5f5cd9b8793423a75e8cd663c48d073ba"},
{file = "black-22.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:dae63f2dbf82882fa3b2a3c49c32bffe144970a573cd68d247af6560fc493ae1"},
{file = "black-22.1.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5fa1db02410b1924b6749c245ab38d30621564e658297484952f3d8a39fce7e8"},
{file = "black-22.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:c8226f50b8c34a14608b848dc23a46e5d08397d009446353dad45e04af0c8e28"},
{file = "black-22.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:2d6f331c02f0f40aa51a22e479c8209d37fcd520c77721c034517d44eecf5912"},
{file = "black-22.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:742ce9af3086e5bd07e58c8feb09dbb2b047b7f566eb5f5bc63fd455814979f3"},
{file = "black-22.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:fdb8754b453fb15fad3f72cd9cad3e16776f0964d67cf30ebcbf10327a3777a3"},
{file = "black-22.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5660feab44c2e3cb24b2419b998846cbb01c23c7fe645fee45087efa3da2d61"},
{file = "black-22.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:6f2f01381f91c1efb1451998bd65a129b3ed6f64f79663a55fe0e9b74a5f81fd"},
{file = "black-22.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:efbadd9b52c060a8fc3b9658744091cb33c31f830b3f074422ed27bad2b18e8f"},
{file = "black-22.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8871fcb4b447206904932b54b567923e5be802b9b19b744fdff092bd2f3118d0"},
{file = "black-22.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ccad888050f5393f0d6029deea2a33e5ae371fd182a697313bdbd835d3edaf9c"},
{file = "black-22.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:07e5c049442d7ca1a2fc273c79d1aecbbf1bc858f62e8184abe1ad175c4f7cc2"},
{file = "black-22.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:373922fc66676133ddc3e754e4509196a8c392fec3f5ca4486673e685a421321"},
{file = "black-22.1.0-py3-none-any.whl", hash = "sha256:3524739d76b6b3ed1132422bf9d82123cd1705086723bc3e235ca39fd21c667d"},
{file = "black-22.1.0.tar.gz", hash = "sha256:a7c0192d35635f6fc1174be575cb7915e92e5dd629ee79fdaf0dcfa41a80afb5"},
]
cfgv = [
{file = "cfgv-3.3.1-py2.py3-none-any.whl", hash = "sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426"},
{file = "cfgv-3.3.1.tar.gz", hash = "sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736"},
]
click = [
{file = "click-8.0.4-py3-none-any.whl", hash = "sha256:6a7a62563bbfabfda3a38f3023a1db4a35978c0abd76f6c9605ecd6554d6d9b1"},
{file = "click-8.0.4.tar.gz", hash = "sha256:8458d7b1287c5fb128c90e23381cf99dcde74beaf6c7ff6384ce84d6fe090adb"},
]
colorama = [
{file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"},
{file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"},
]
distlib = [
{file = "distlib-0.3.4-py2.py3-none-any.whl", hash = "sha256:6564fe0a8f51e734df6333d08b8b94d4ea8ee6b99b5ed50613f731fd4089f34b"},
{file = "distlib-0.3.4.zip", hash = "sha256:e4b58818180336dc9c529bfb9a0b58728ffc09ad92027a3f30b7cd91e3458579"},
]
filelock = [
{file = "filelock-3.6.0-py3-none-any.whl", hash = "sha256:f8314284bfffbdcfa0ff3d7992b023d4c628ced6feb957351d4c48d059f56bc0"},
{file = "filelock-3.6.0.tar.gz", hash = "sha256:9cd540a9352e432c7246a48fe4e8712b10acb1df2ad1f30e8c070b82ae1fed85"},
]
flake8 = [
{file = "flake8-4.0.1-py2.py3-none-any.whl", hash = "sha256:479b1304f72536a55948cb40a32dce8bb0ffe3501e26eaf292c7e60eb5e0428d"},
{file = "flake8-4.0.1.tar.gz", hash = "sha256:806e034dda44114815e23c16ef92f95c91e4c71100ff52813adf7132a6ad870d"},
]
flake8-docstrings = [
{file = "flake8-docstrings-1.6.0.tar.gz", hash = "sha256:9fe7c6a306064af8e62a055c2f61e9eb1da55f84bb39caef2b84ce53708ac34b"},
{file = "flake8_docstrings-1.6.0-py2.py3-none-any.whl", hash = "sha256:99cac583d6c7e32dd28bbfbef120a7c0d1b6dde4adb5a9fd441c4227a6534bde"},
]
gitdb = [
{file = "gitdb-4.0.9-py3-none-any.whl", hash = "sha256:8033ad4e853066ba6ca92050b9df2f89301b8fc8bf7e9324d412a63f8bf1a8fd"},
{file = "gitdb-4.0.9.tar.gz", hash = "sha256:bac2fd45c0a1c9cf619e63a90d62bdc63892ef92387424b855792a6cabe789aa"},
]
gitpython = [
{file = "GitPython-3.1.27-py3-none-any.whl", hash = "sha256:5b68b000463593e05ff2b261acff0ff0972df8ab1b70d3cdbd41b546c8b8fc3d"},
{file = "GitPython-3.1.27.tar.gz", hash = "sha256:1c885ce809e8ba2d88a29befeb385fcea06338d3640712b59ca623c220bb5704"},
]
identify = [
{file = "identify-2.4.11-py2.py3-none-any.whl", hash = "sha256:fd906823ed1db23c7a48f9b176a1d71cb8abede1e21ebe614bac7bdd688d9213"},
{file = "identify-2.4.11.tar.gz", hash = "sha256:2986942d3974c8f2e5019a190523b0b0e2a07cb8e89bf236727fb4b26f27f8fd"},
]
isort = [
{file = "isort-5.10.1-py3-none-any.whl", hash = "sha256:6f62d78e2f89b4500b080fe3a81690850cd254227f27f75c3a0c491a1f351ba7"},
{file = "isort-5.10.1.tar.gz", hash = "sha256:e8443a5e7a020e9d7f97f1d7d9cd17c88bcb3bc7e218bf9cf5095fe550be2951"},
]
mccabe = [
{file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"},
{file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"},
]
mypy = [
{file = "mypy-0.931-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3c5b42d0815e15518b1f0990cff7a705805961613e701db60387e6fb663fe78a"},
{file = "mypy-0.931-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c89702cac5b302f0c5d33b172d2b55b5df2bede3344a2fbed99ff96bddb2cf00"},
{file = "mypy-0.931-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:300717a07ad09525401a508ef5d105e6b56646f7942eb92715a1c8d610149714"},
{file = "mypy-0.931-cp310-cp310-win_amd64.whl", hash = "sha256:7b3f6f557ba4afc7f2ce6d3215d5db279bcf120b3cfd0add20a5d4f4abdae5bc"},
{file = "mypy-0.931-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:1bf752559797c897cdd2c65f7b60c2b6969ffe458417b8d947b8340cc9cec08d"},
{file = "mypy-0.931-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4365c60266b95a3f216a3047f1d8e3f895da6c7402e9e1ddfab96393122cc58d"},
{file = "mypy-0.931-cp36-cp36m-win_amd64.whl", hash = "sha256:1b65714dc296a7991000b6ee59a35b3f550e0073411ac9d3202f6516621ba66c"},
{file = "mypy-0.931-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e839191b8da5b4e5d805f940537efcaa13ea5dd98418f06dc585d2891d228cf0"},
{file = "mypy-0.931-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:50c7346a46dc76a4ed88f3277d4959de8a2bd0a0fa47fa87a4cde36fe247ac05"},
{file = "mypy-0.931-cp37-cp37m-win_amd64.whl", hash = "sha256:d8f1ff62f7a879c9fe5917b3f9eb93a79b78aad47b533911b853a757223f72e7"},
{file = "mypy-0.931-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f9fe20d0872b26c4bba1c1be02c5340de1019530302cf2dcc85c7f9fc3252ae0"},
{file = "mypy-0.931-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1b06268df7eb53a8feea99cbfff77a6e2b205e70bf31743e786678ef87ee8069"},
{file = "mypy-0.931-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8c11003aaeaf7cc2d0f1bc101c1cc9454ec4cc9cb825aef3cafff8a5fdf4c799"},
{file = "mypy-0.931-cp38-cp38-win_amd64.whl", hash = "sha256:d9d2b84b2007cea426e327d2483238f040c49405a6bf4074f605f0156c91a47a"},
{file = "mypy-0.931-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ff3bf387c14c805ab1388185dd22d6b210824e164d4bb324b195ff34e322d166"},
{file = "mypy-0.931-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5b56154f8c09427bae082b32275a21f500b24d93c88d69a5e82f3978018a0266"},
{file = "mypy-0.931-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8ca7f8c4b1584d63c9a0f827c37ba7a47226c19a23a753d52e5b5eddb201afcd"},
{file = "mypy-0.931-cp39-cp39-win_amd64.whl", hash = "sha256:74f7eccbfd436abe9c352ad9fb65872cc0f1f0a868e9d9c44db0893440f0c697"},
{file = "mypy-0.931-py3-none-any.whl", hash = "sha256:1171f2e0859cfff2d366da2c7092b06130f232c636a3f7301e3feb8b41f6377d"},
{file = "mypy-0.931.tar.gz", hash = "sha256:0038b21890867793581e4cb0d810829f5fd4441aa75796b53033af3aa30430ce"},
]
mypy-extensions = [
{file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"},
{file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"},
]
nodeenv = [
{file = "nodeenv-1.6.0-py2.py3-none-any.whl", hash = "sha256:621e6b7076565ddcacd2db0294c0381e01fd28945ab36bcf00f41c5daf63bef7"},
{file = "nodeenv-1.6.0.tar.gz", hash = "sha256:3ef13ff90291ba2a4a7a4ff9a979b63ffdd00a464dbe04acf0ea6471517a4c2b"},
]
pathspec = [
{file = "pathspec-0.9.0-py2.py3-none-any.whl", hash = "sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a"},
{file = "pathspec-0.9.0.tar.gz", hash = "sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1"},
]
pbr = [
{file = "pbr-5.8.1-py2.py3-none-any.whl", hash = "sha256:27108648368782d07bbf1cb468ad2e2eeef29086affd14087a6d04b7de8af4ec"},
{file = "pbr-5.8.1.tar.gz", hash = "sha256:66bc5a34912f408bb3925bf21231cb6f59206267b7f63f3503ef865c1a292e25"},
]
platformdirs = [
{file = "platformdirs-2.5.1-py3-none-any.whl", hash = "sha256:bcae7cab893c2d310a711b70b24efb93334febe65f8de776ee320b517471e227"},
{file = "platformdirs-2.5.1.tar.gz", hash = "sha256:7535e70dfa32e84d4b34996ea99c5e432fa29a708d0f4e394bbcb2a8faa4f16d"},
]
pre-commit = [
{file = "pre_commit-2.17.0-py2.py3-none-any.whl", hash = "sha256:725fa7459782d7bec5ead072810e47351de01709be838c2ce1726b9591dad616"},
{file = "pre_commit-2.17.0.tar.gz", hash = "sha256:c1a8040ff15ad3d648c70cc3e55b93e4d2d5b687320955505587fd79bbaed06a"},
]
pycodestyle = [
{file = "pycodestyle-2.8.0-py2.py3-none-any.whl", hash = "sha256:720f8b39dde8b293825e7ff02c475f3077124006db4f440dcbc9a20b76548a20"},
{file = "pycodestyle-2.8.0.tar.gz", hash = "sha256:eddd5847ef438ea1c7870ca7eb78a9d47ce0cdb4851a5523949f2601d0cbbe7f"},
]
pydocstyle = [
{file = "pydocstyle-6.1.1-py3-none-any.whl", hash = "sha256:6987826d6775056839940041beef5c08cc7e3d71d63149b48e36727f70144dc4"},
{file = "pydocstyle-6.1.1.tar.gz", hash = "sha256:1d41b7c459ba0ee6c345f2eb9ae827cab14a7533a88c5c6f7e94923f72df92dc"},
]
pyflakes = [
{file = "pyflakes-2.4.0-py2.py3-none-any.whl", hash = "sha256:3bb3a3f256f4b7968c9c788781e4ff07dce46bdf12339dcda61053375426ee2e"},
{file = "pyflakes-2.4.0.tar.gz", hash = "sha256:05a85c2872edf37a4ed30b0cce2f6093e1d0581f8c19d7393122da7e25b2b24c"},
]
pyyaml = [
{file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"},
{file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"},
{file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"},
{file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b"},
{file = "PyYAML-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"},
{file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"},
{file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"},
{file = "PyYAML-6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86"},
{file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f"},
{file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92"},
{file = "PyYAML-6.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4"},
{file = "PyYAML-6.0-cp36-cp36m-win32.whl", hash = "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293"},
{file = "PyYAML-6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57"},
{file = "PyYAML-6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c"},
{file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0"},
{file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4"},
{file = "PyYAML-6.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9"},
{file = "PyYAML-6.0-cp37-cp37m-win32.whl", hash = "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737"},
{file = "PyYAML-6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d"},
{file = "PyYAML-6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b"},
{file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba"},
{file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34"},
{file = "PyYAML-6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287"},
{file = "PyYAML-6.0-cp38-cp38-win32.whl", hash = "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78"},
{file = "PyYAML-6.0-cp38-cp38-win_amd64.whl", hash = "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07"},
{file = "PyYAML-6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b"},
{file = "PyYAML-6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174"},
{file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803"},
{file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3"},
{file = "PyYAML-6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0"},
{file = "PyYAML-6.0-cp39-cp39-win32.whl", hash = "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb"},
{file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"},
{file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"},
]
redis = [
{file = "redis-3.5.3-py2.py3-none-any.whl", hash = "sha256:432b788c4530cfe16d8d943a09d40ca6c16149727e4afe8c2c9d5580c59d9f24"},
{file = "redis-3.5.3.tar.gz", hash = "sha256:0e7e0cfca8660dea8b7d5cd8c4f6c5e29e11f31158c0b0ae91a397f00e5a05a2"},
]
six = [
{file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
{file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
]
smmap = [
{file = "smmap-5.0.0-py3-none-any.whl", hash = "sha256:2aba19d6a040e78d8b09de5c57e96207b09ed71d8e55ce0959eeee6c8e190d94"},
{file = "smmap-5.0.0.tar.gz", hash = "sha256:c840e62059cd3be204b0c9c9f74be2c09d5648eddd4580d9314c3ecde0b30936"},
]
snowballstemmer = [
{file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"},
{file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"},
]
stevedore = [
{file = "stevedore-3.5.0-py3-none-any.whl", hash = "sha256:a547de73308fd7e90075bb4d301405bebf705292fa90a90fc3bcf9133f58616c"},
{file = "stevedore-3.5.0.tar.gz", hash = "sha256:f40253887d8712eaa2bb0ea3830374416736dc8ec0e22f5a65092c1174c44335"},
]
toml = [
{file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"},
{file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"},
]
tomli = [
{file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"},
{file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
]
types-redis = [
{file = "types-redis-3.5.18.tar.gz", hash = "sha256:15482304e8848c63b383b938ffaba7ebe0b7f8f33381ecc450ee03935213e166"},
{file = "types_redis-3.5.18-py3-none-any.whl", hash = "sha256:5c55c4b9e8ebdc6d57d4e47900b77d99f19ca0a563264af3f701246ed0926335"},
]
typing-extensions = [
{file = "typing_extensions-4.1.1-py3-none-any.whl", hash = "sha256:21c85e0fe4b9a155d0799430b0ad741cdce7e359660ccbd8b530613e8df88ce2"},
{file = "typing_extensions-4.1.1.tar.gz", hash = "sha256:1a9462dcc3347a79b1f1c0271fbe79e844580bb598bafa1ed208b94da3cdcd42"},
]
virtualenv = [
{file = "virtualenv-20.13.2-py2.py3-none-any.whl", hash = "sha256:e7b34c9474e6476ee208c43a4d9ac1510b041c68347eabfe9a9ea0c86aa0a46b"},
{file = "virtualenv-20.13.2.tar.gz", hash = "sha256:01f5f80744d24a3743ce61858123488e91cb2dd1d3bdf92adaf1bba39ffdedf0"},
]
websockets = [
{file = "websockets-10.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d5396710f86a306cf52f87fd8ea594a0e894ba0cc5a36059eaca3a477dc332aa"},
{file = "websockets-10.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b22bdc795e62e71118b63e14a08bacfa4f262fd2877de7e5b950f5ac16b0348f"},
{file = "websockets-10.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5b04270b5613f245ec84bb2c6a482a9d009aefad37c0575f6cda8499125d5d5c"},
{file = "websockets-10.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f5c335dc0e7dc271ef36df3f439868b3c790775f345338c2f61a562f1074187b"},
{file = "websockets-10.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6a009eb551c46fd79737791c0c833fc0e5b56bcd1c3057498b262d660b92e9cd"},
{file = "websockets-10.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a10c0c1ee02164246f90053273a42d72a3b2452a7e7486fdae781138cf7fbe2d"},
{file = "websockets-10.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7b38a5c9112e3dbbe45540f7b60c5204f49b3cb501b40950d6ab34cd202ab1d0"},
{file = "websockets-10.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:2aa9b91347ecd0412683f28aabe27f6bad502d89bd363b76e0a3508b1596402e"},
{file = "websockets-10.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b7fe45ae43ac814beb8ca09d6995b56800676f2cfa8e23f42839dc69bba34a42"},
{file = "websockets-10.2-cp310-cp310-win32.whl", hash = "sha256:cef40a1b183dcf39d23b392e9dd1d9b07ab9c46aadf294fff1350fb79146e72b"},
{file = "websockets-10.2-cp310-cp310-win_amd64.whl", hash = "sha256:c21a67ab9a94bd53e10bba21912556027fea944648a09e6508415ad14e37c325"},
{file = "websockets-10.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:cb316b87cbe3c0791c2ad92a5a36bf6adc87c457654335810b25048c1daa6fd5"},
{file = "websockets-10.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f14bd10e170abc01682a9f8b28b16e6f20acf6175945ef38db6ffe31b0c72c3f"},
{file = "websockets-10.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:fa35c5d1830d0fb7b810324e9eeab9aa92e8f273f11fdbdc0741dcded6d72b9f"},
{file = "websockets-10.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:71a4491cfe7a9f18ee57d41163cb6a8a3fa591e0f0564ca8b0ed86b2a30cced4"},
{file = "websockets-10.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:6193bbc1ee63aadeb9a4d81de0e19477401d150d506aee772d8380943f118186"},
{file = "websockets-10.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:8beac786a388bb99a66c3be4ab0fb38273c0e3bc17f612a4e0a47c4fc8b9c045"},
{file = "websockets-10.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c67d9cacb3f6537ca21e9b224d4fd08481538e43bcac08b3d93181b0816def39"},
{file = "websockets-10.2-cp37-cp37m-win32.whl", hash = "sha256:a03a25d95cc7400bd4d61a63460b5d85a7761c12075ee2f51de1ffe73aa593d3"},
{file = "websockets-10.2-cp37-cp37m-win_amd64.whl", hash = "sha256:f8296b8408ec6853b26771599990721a26403e62b9de7e50ac0a056772ac0b5e"},
{file = "websockets-10.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:7bb9d8a6beca478c7e9bdde0159bd810cc1006ad6a7cb460533bae39da692ca2"},
{file = "websockets-10.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:05f6e9757017270e7a92a2975e2ae88a9a582ffc4629086fd6039aa80e99cd86"},
{file = "websockets-10.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1c9031e90ebfc486e9cdad532b94004ade3aa39a31d3c46c105bb0b579cd2490"},
{file = "websockets-10.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82bc33db6d8309dc27a3bee11f7da2288ad925fcbabc2a4bb78f7e9c56249baf"},
{file = "websockets-10.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:24b879ba7db12bb525d4e58089fcbe6a3df3ce4666523183654170e86d372cbe"},
{file = "websockets-10.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cf931c33db9c87c53d009856045dd524e4a378445693382a920fa1e0eb77c36c"},
{file = "websockets-10.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:669e54228a4d9457abafed27cbf0e2b9f401445c4dfefc12bf8e4db9751703b8"},
{file = "websockets-10.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:bffc65442dd35c473ca9790a3fa3ba06396102a950794f536783f4b8060af8dd"},
{file = "websockets-10.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d4d110a84b63c5cfdd22485acc97b8b919aefeecd6300c0c9d551e055b9a88ea"},
{file = "websockets-10.2-cp38-cp38-win32.whl", hash = "sha256:117383d0a17a0dda349f7a8790763dde75c1508ff8e4d6e8328b898b7df48397"},
{file = "websockets-10.2-cp38-cp38-win_amd64.whl", hash = "sha256:0b66421f9f13d4df60cd48ab977ed2c2b6c9147ae1a33caf5a9f46294422fda1"},
{file = "websockets-10.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:ac081aa0307f263d63c5ff0727935c736c8dad51ddf2dc9f5d0c4759842aefaa"},
{file = "websockets-10.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b4059e2ccbe6587b6dc9a01db5fc49ead9a884faa4076eea96c5ec62cb32f42a"},
{file = "websockets-10.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9ca2ca05a4c29179f06cf6727b45dba5d228da62623ec9df4184413d8aae6cb9"},
{file = "websockets-10.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:97950c7c844ec6f8d292440953ae18b99e3a6a09885e09d20d5e7ecd9b914cf8"},
{file = "websockets-10.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:98f57b3120f8331cd7440dbe0e776474f5e3632fdaa474af1f6b754955a47d71"},
{file = "websockets-10.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a72b92f96e5e540d5dda99ee3346e199ade8df63152fa3c737260da1730c411f"},
{file = "websockets-10.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:038afef2a05893578d10dadbdbb5f112bd115c46347e1efe99f6a356ff062138"},
{file = "websockets-10.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f09f46b1ff6d09b01c7816c50bd1903cf7d02ebbdb63726132717c2fcda835d5"},
{file = "websockets-10.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2349fa81b6b959484bb2bda556ccb9eb70ba68987646a0f8a537a1a18319fb03"},
{file = "websockets-10.2-cp39-cp39-win32.whl", hash = "sha256:bef03a51f9657fb03d8da6ccd233fe96e04101a852f0ffd35f5b725b28221ff3"},
{file = "websockets-10.2-cp39-cp39-win_amd64.whl", hash = "sha256:1c1f3b18c8162e3b09761d0c6a0305fd642934202541cc511ef972cb9463261e"},
{file = "websockets-10.2-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:5a38a0175ae82e4a8c4bac29fc01b9ee26d7d5a614e5ee11e7813c68a7d938ce"},
{file = "websockets-10.2-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c6e56606842bb24e16e36ae7eb308d866b4249cf0be8f63b212f287eeb76b124"},
{file = "websockets-10.2-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0f73cb2526d6da268e86977b2c4b58f2195994e53070fe567d5487c6436047e6"},
{file = "websockets-10.2-pp37-pypy37_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:0cd02f36d37e503aca88ab23cc0a1a0e92a263d37acf6331521eb38040dcf77b"},
{file = "websockets-10.2-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:56d48eebe9e39ce0d68701bce3b21df923aa05dcc00f9fd8300de1df31a7c07c"},
{file = "websockets-10.2.tar.gz", hash = "sha256:8351c3c86b08156337b0e4ece0e3c5ec3e01fcd14e8950996832a23c99416098"},
]

47
src/server/pyproject.toml Normal file
View file

@ -0,0 +1,47 @@
[tool.poetry]
name = "booplaysgba_server"
version = "0.1.0"
description = "Émulateur collaboratif pour patienter dans le B00"
authors = ["Laureηt <laurentfainsin@protonmail.com>"]
license = "MIT"
[tool.poetry.dependencies]
python = "^3.10"
asyncio = "^3.4.3"
websockets = "^10.1"
redis = "^3.5.3"
[tool.poetry.dev-dependencies]
black = "^22.1"
flake8 = "^4.0.1"
flake8-docstrings = "^1.6.0"
pre-commit = "^2.15.0"
bandit = "^1.7.0"
isort = "^5.9.3"
mypy = "^0.931"
types-redis = "^3.5.15"
[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"
[tool.black]
line-length = 120
target-version = ["py310"]
include = '\.pyi?$'
exclude = '''
/(
\.git
\.venv
)/
'''
[tool.isort]
profile = "black"
multi_line_output = 3
[tool.mypy]
python_version = "3.10"
warn_return_any = true
warn_unused_configs = true

View file

@ -0,0 +1,57 @@
asyncio==3.4.3 \
--hash=sha256:b62c9157d36187eca799c378e572c969f0da87cd5fc42ca372d92cdb06e7e1de \
--hash=sha256:c46a87b48213d7464f22d9a497b9eef8c1928b68320a2fa94240f969f6fec08c \
--hash=sha256:c4d18b22701821de07bd6aea8b53d21449ec0ec5680645e5317062ea21817d2d \
--hash=sha256:83360ff8bc97980e4ff25c964c7bd3923d333d177aa4f7fb736b019f26c7cb41
redis==3.5.3; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.5.0") \
--hash=sha256:432b788c4530cfe16d8d943a09d40ca6c16149727e4afe8c2c9d5580c59d9f24 \
--hash=sha256:0e7e0cfca8660dea8b7d5cd8c4f6c5e29e11f31158c0b0ae91a397f00e5a05a2
websockets==10.2; python_version >= "3.7" \
--hash=sha256:d5396710f86a306cf52f87fd8ea594a0e894ba0cc5a36059eaca3a477dc332aa \
--hash=sha256:b22bdc795e62e71118b63e14a08bacfa4f262fd2877de7e5b950f5ac16b0348f \
--hash=sha256:5b04270b5613f245ec84bb2c6a482a9d009aefad37c0575f6cda8499125d5d5c \
--hash=sha256:f5c335dc0e7dc271ef36df3f439868b3c790775f345338c2f61a562f1074187b \
--hash=sha256:6a009eb551c46fd79737791c0c833fc0e5b56bcd1c3057498b262d660b92e9cd \
--hash=sha256:a10c0c1ee02164246f90053273a42d72a3b2452a7e7486fdae781138cf7fbe2d \
--hash=sha256:7b38a5c9112e3dbbe45540f7b60c5204f49b3cb501b40950d6ab34cd202ab1d0 \
--hash=sha256:2aa9b91347ecd0412683f28aabe27f6bad502d89bd363b76e0a3508b1596402e \
--hash=sha256:b7fe45ae43ac814beb8ca09d6995b56800676f2cfa8e23f42839dc69bba34a42 \
--hash=sha256:cef40a1b183dcf39d23b392e9dd1d9b07ab9c46aadf294fff1350fb79146e72b \
--hash=sha256:c21a67ab9a94bd53e10bba21912556027fea944648a09e6508415ad14e37c325 \
--hash=sha256:cb316b87cbe3c0791c2ad92a5a36bf6adc87c457654335810b25048c1daa6fd5 \
--hash=sha256:f14bd10e170abc01682a9f8b28b16e6f20acf6175945ef38db6ffe31b0c72c3f \
--hash=sha256:fa35c5d1830d0fb7b810324e9eeab9aa92e8f273f11fdbdc0741dcded6d72b9f \
--hash=sha256:71a4491cfe7a9f18ee57d41163cb6a8a3fa591e0f0564ca8b0ed86b2a30cced4 \
--hash=sha256:6193bbc1ee63aadeb9a4d81de0e19477401d150d506aee772d8380943f118186 \
--hash=sha256:8beac786a388bb99a66c3be4ab0fb38273c0e3bc17f612a4e0a47c4fc8b9c045 \
--hash=sha256:c67d9cacb3f6537ca21e9b224d4fd08481538e43bcac08b3d93181b0816def39 \
--hash=sha256:a03a25d95cc7400bd4d61a63460b5d85a7761c12075ee2f51de1ffe73aa593d3 \
--hash=sha256:f8296b8408ec6853b26771599990721a26403e62b9de7e50ac0a056772ac0b5e \
--hash=sha256:7bb9d8a6beca478c7e9bdde0159bd810cc1006ad6a7cb460533bae39da692ca2 \
--hash=sha256:05f6e9757017270e7a92a2975e2ae88a9a582ffc4629086fd6039aa80e99cd86 \
--hash=sha256:1c9031e90ebfc486e9cdad532b94004ade3aa39a31d3c46c105bb0b579cd2490 \
--hash=sha256:82bc33db6d8309dc27a3bee11f7da2288ad925fcbabc2a4bb78f7e9c56249baf \
--hash=sha256:24b879ba7db12bb525d4e58089fcbe6a3df3ce4666523183654170e86d372cbe \
--hash=sha256:cf931c33db9c87c53d009856045dd524e4a378445693382a920fa1e0eb77c36c \
--hash=sha256:669e54228a4d9457abafed27cbf0e2b9f401445c4dfefc12bf8e4db9751703b8 \
--hash=sha256:bffc65442dd35c473ca9790a3fa3ba06396102a950794f536783f4b8060af8dd \
--hash=sha256:d4d110a84b63c5cfdd22485acc97b8b919aefeecd6300c0c9d551e055b9a88ea \
--hash=sha256:117383d0a17a0dda349f7a8790763dde75c1508ff8e4d6e8328b898b7df48397 \
--hash=sha256:0b66421f9f13d4df60cd48ab977ed2c2b6c9147ae1a33caf5a9f46294422fda1 \
--hash=sha256:ac081aa0307f263d63c5ff0727935c736c8dad51ddf2dc9f5d0c4759842aefaa \
--hash=sha256:b4059e2ccbe6587b6dc9a01db5fc49ead9a884faa4076eea96c5ec62cb32f42a \
--hash=sha256:9ca2ca05a4c29179f06cf6727b45dba5d228da62623ec9df4184413d8aae6cb9 \
--hash=sha256:97950c7c844ec6f8d292440953ae18b99e3a6a09885e09d20d5e7ecd9b914cf8 \
--hash=sha256:98f57b3120f8331cd7440dbe0e776474f5e3632fdaa474af1f6b754955a47d71 \
--hash=sha256:a72b92f96e5e540d5dda99ee3346e199ade8df63152fa3c737260da1730c411f \
--hash=sha256:038afef2a05893578d10dadbdbb5f112bd115c46347e1efe99f6a356ff062138 \
--hash=sha256:f09f46b1ff6d09b01c7816c50bd1903cf7d02ebbdb63726132717c2fcda835d5 \
--hash=sha256:2349fa81b6b959484bb2bda556ccb9eb70ba68987646a0f8a537a1a18319fb03 \
--hash=sha256:bef03a51f9657fb03d8da6ccd233fe96e04101a852f0ffd35f5b725b28221ff3 \
--hash=sha256:1c1f3b18c8162e3b09761d0c6a0305fd642934202541cc511ef972cb9463261e \
--hash=sha256:5a38a0175ae82e4a8c4bac29fc01b9ee26d7d5a614e5ee11e7813c68a7d938ce \
--hash=sha256:c6e56606842bb24e16e36ae7eb308d866b4249cf0be8f63b212f287eeb76b124 \
--hash=sha256:0f73cb2526d6da268e86977b2c4b58f2195994e53070fe567d5487c6436047e6 \
--hash=sha256:0cd02f36d37e503aca88ab23cc0a1a0e92a263d37acf6331521eb38040dcf77b \
--hash=sha256:56d48eebe9e39ce0d68701bce3b21df923aa05dcc00f9fd8300de1df31a7c07c \
--hash=sha256:8351c3c86b08156337b0e4ece0e3c5ec3e01fcd14e8950996832a23c99416098

16
workspace.code-workspace Normal file
View file

@ -0,0 +1,16 @@
{
"folders": [
{
"path": "src/emulator",
"name": "Emulator"
},
{
"path": "src/server",
"name": "Server"
},
{
"path": ".",
"name": "B00 Plays GBA"
}
]
}