Skip to content

Commit 938d413

Browse files
authored
Merge pull request #3 from briancaffey/dev
feat(article): update article and add links
2 parents a5227fa + fcea5a7 commit 938d413

24 files changed

+142
-1144
lines changed

ARTICLE.md

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,41 +9,45 @@ tags: redishackathon, redis, flask, socketio
99

1010
I recreated the game "Red Light, Green Light" using Python, TypeScript and Redis!
1111

12-
One day in early August I was browsing Dev.to while re-watching MrBeast's Squid Game recreation video in the background when I cam across the DEV article about the Redis Hackathon. Then I got a crazy idea: **Redis Light, Green Light**! I became determined to create my own online, real-time, multi-player version of Red Light, Green Light powered by Redis and submit it to the Wacky Wildcard project category for my chance to win the hackathon!
12+
One day in early August I was browsing DEV while re-watching MrBeast's Squid Game recreation video in the background when I cam across the DEV article about the Redis Hackathon. Then I got a crazy idea: **Redis Light, Green Light**! I became determined to create my own online, real-time, multi-player version of Red Light, Green Light powered by Redis and submit it to the Wacky Wildcard project category for my chance to win the hackathon!
1313

1414
I used my favorite languages and frameworks for rapid prototyping: Python with Flask to power the backend and TypeScript with the Nuxt.js framework to build the frontend components for my game.
1515

16-
For real-time communication I added the `Flask-SocketIO` library to my Flask app and the `socket.io-client` library to my Nuxt app. I also added celery for scheduling and processing async tasks (more on this later). Redis was used as the message queue for websocket messages and it was also used as the broker for celery tasks.
16+
For real-time communication I added the `Flask-SocketIO` library to my Flask app and the `socket.io-client` library to my Nuxt app. I also added celery for scheduling and processing async tasks. Redis was used as the message queue for websocket messages and it was also used as the broker for celery tasks.
1717

18-
`redis-py` was used to get and set hash values that kept track of live game data in Redis. Redis streams were used as a way to track all events for a historical record of every action in every game.
19-
20-
Like I do with most of my projects, I used `docker-compose` to set up the backend application services and database and I used Nuxt's `npm run dev` command to work on the UI.
18+
This was my first project working with Redis Stack and Redis OM and I really liked using these tools. I stored most of my data in hashes, and the Redis OM library is perfect for using this data type. I also used Redis streams for the first time which was a lot of fun.
2119

2220
The backend application services include:
2321

2422
- Flask server (for API endpoints and Websocket handlers)
2523
- Celery beat task scheduler (for scheduling tasks to change the light color in each room)
2624
- Celery worker (to change the light color for a room and to update that players in that room via Websocket)
2725

26+
![Project Diagram](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0egpm1igfqyon5kjtujv.png)
27+
28+
Please check out the video below for more details about how the project works.
29+
2830
### Submission Category
2931

3032
Wacky Wildcards
3133

32-
### [Optional: Video Explainer of My Project]
34+
### Redis Light, Green Light YouTube Video
3335

34-
[Note]: # (This is where you can embed the optional bonus video you created to accompany your submission. Ensure your video is published publicly to YouTube and you’ve used the embed tag here to share it with us. By opting to include a video, you will be eligible for BONUS prizes. Learn more in the announcement post.)
36+
{ % youtube BoalZKmgoEU %}
3537

3638
### Language Used
3739

38-
Python
40+
Python. Honorable mention for JavaScript.
3941

4042
### Link to Code
4143

4244
{% embed https://github.com/briancaffey/redis-light-green-light-dev-to-hackathon %}
4345

4446
### Additional Resources / Info
4547

46-
[Note:] # Screenshots/demo videos are encouraged!
48+
![Redis Light, Green Light](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m3rqy7eqz40nqk1e04jj.png)
49+
50+
![Redis Light, Green Light gameplay](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wkmgiz8wtq484rn6dmfc.png)
4751

4852
- - -
4953

Makefile

Lines changed: 57 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,61 @@
1-
# for local development
2-
3-
dcud:
4-
docker compose -f docker-compose.dev.yml up
5-
6-
dcdd:
7-
docker compose -f docker-compose.dev.yml down
8-
9-
dcug:
10-
docker compose -f docker-compose.gunicorn.yml up
11-
12-
dcdg:
13-
docker compose -f docker-compose.gunicorn.yml down
14-
15-
# for terraform
16-
17-
tf-init:
18-
terraform -chdir=terraform init
19-
20-
tf-plan:
21-
terraform -chdir=terraform plan
22-
23-
tf-apply:
24-
terraform -chdir=terraform apply
25-
26-
tf-fmt:
27-
terraform fmt -recursive
28-
29-
tf-destroy:
30-
terraform -chdir=terraform destroy
31-
1+
# docker-compose
2+
3+
# start the backend services (flask, celery, celerybeat, redis-stack)
4+
docker-compose:
5+
@docker-compose up --build
6+
7+
# start the client
8+
nuxt:
9+
@cd client && npm i && npm run dev
10+
11+
# for local development if not using docker-compose to stat the backend services
12+
13+
# check versions of locally installed tools
14+
check:
15+
@docker -v; echo
16+
@docker-compose -v; echo
17+
@python3 --version; echo
18+
@echo Node `node -v`; echo
19+
20+
# stand up only redis stack
21+
redis-stack-up:
22+
@docker-compose -f redis-stack.yml up
23+
24+
# install dependencies in a python virtual environment
25+
install_dev:
26+
@rm -rf app/.env
27+
@python3 -m venv app/.env
28+
@. app/.env/bin/activate
29+
@python3 -m pip install --upgrade pip
30+
@pip install -r app/requirements.txt
31+
@pip install -r app/requirements_dev.txt
32+
33+
# delete the python virtual environment
34+
clean:
35+
@rm -rf .env
36+
37+
# start the flask app (api and websockets)
38+
flask:
39+
@. app/.env/bin/activate
40+
@cd app && gunicorn -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker -w 1 wsgi:app --reload
41+
42+
# start the celery worker
43+
celery:
44+
@. app/.env/bin/activate
45+
@cd app && celery --app app.celery worker --loglevel=info
46+
47+
# start the celerybeat process
48+
celerybeat:
49+
@. app/.env/bin/activate
50+
@cd app && celery --app app.celery beat --loglevel=info
51+
52+
# delete all keys from all redis databases
53+
flushall:
54+
@docker exec -it redis redis-cli flushall
55+
56+
# count lines of code
3257
cloc:
3358
cloc \
3459
--exclude-dir=$$(tr '\n' ',' < .clocignore) \
3560
--exclude-ext=json \
36-
.
61+
.

README.md

Lines changed: 67 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,49 @@
11
# Redis Light, Green Light
22

3-
This project is an online multiplayer implementation of the game "Red Light, Green Light" using Python, Javascript and Redis. This is my submission for the 2022 [Redis Hackathon on DEV](https://dev.to/devteam/announcing-the-redis-hackathon-on-dev-3248)
3+
This project is an online, multiplayer implementation of "Red Light, Green Light" from Squid Game built with Python, Javascript and Redis. This is my submission for the 2022 [Redis Hackathon on DEV](https://dev.to/devteam/announcing-the-redis-hackathon-on-dev-3248)!
44

55
### Gameplay
66

77
![Redis Light, Green Light Gameplay](/images/gameplay.png)
88

9-
### Game event log
9+
### Game event log built with redis streams
1010

1111
![Redis Stream data](/images/events.png)
1212

13+
### Architecture Overview
14+
15+
![Project Architecture Diagram](/images/rlgl.drawio.png)
16+
17+
## Code Overview with `cloc` (count lines of code)
18+
19+
```
20+
make cloc
21+
22+
github.com/AlDanial/cloc v 1.94 T=0.03 s (1102.7 files/s, 67098.7 lines/s)
23+
-------------------------------------------------------------------------------
24+
Language files blank comment code
25+
-------------------------------------------------------------------------------
26+
Vuejs Component 13 104 14 528
27+
Python 3 178 104 410
28+
Markdown 5 139 0 272
29+
YAML 3 10 0 83
30+
make 1 10 2 34
31+
SVG 2 0 0 22
32+
TypeScript 1 1 1 22
33+
CSS 1 4 0 18
34+
JavaScript 1 0 1 18
35+
Text 2 0 0 13
36+
Dockerfile 1 8 0 12
37+
-------------------------------------------------------------------------------
38+
SUM: 33 454 122 1432
39+
-------------------------------------------------------------------------------
40+
```
41+
1342
# Overview video
1443

1544
Here's a short video that explains the project and how it uses Redis:
1645

17-
[![Embed your YouTube video](https://i.ytimg.com/vi/vyxdC1qK4NE/maxresdefault.jpg)](https://www.youtube.com/watch?v=vyxdC1qK4NE)
46+
<iframe width="560" height="315" src="https://www.youtube.com/embed/BoalZKmgoEU" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
1847

1948
## How it works
2049

@@ -37,7 +66,7 @@ class Position(HashModel):
3766
room: uuid.UUID = Field(index=True)
3867
```
3968

40-
RedisOM is used to perform CRUD (create, read, update and delete) operations on these hashes in API requests, websocket handlers and celery tasks. Here are some examples:
69+
Redis OM is used to perform CRUD (create, read, update and delete) operations on these hashes in API requests, websocket handlers and celery tasks. Here are some examples:
4170

4271
**Creating a new room**: `CREATE operation` on `Room` hash
4372

@@ -61,17 +90,17 @@ There are **eight** types of events that can happen in the lifecycle of a game:
6190

6291
```py
6392
class EventType:
64-
CREATED = "created"
65-
LIGHT = "light"
66-
JOIN = "join"
67-
MOVE = "move"
68-
WIN = "win"
69-
DIE = "die"
70-
LEAVE = "leave"
71-
END = "end"
93+
CREATED = "created" # new game room is created
94+
LIGHT = "light" # the light color is updated
95+
JOIN = "join" # player joins a room
96+
MOVE = "move" # player moves successfully when the light is green
97+
WIN = "win" # player wins by moving 100 steps
98+
DIE = "die" # player tries to move when the light is read
99+
LEAVE = "leave" # player leaves or is disconnected from the game
100+
END = "end" # game ends because there are no more players in the room
72101
```
73102

74-
I use streams as append-only logs to persist very action that happens during the course of a game. Here are some examples of how I store game event data in streams:
103+
I use streams as append-only logs to persist very action that happens during the course of a game. Each event in the stream has an `event` property that stores one of the `EventType`s listed above. Here are some examples of how I store game event data in streams:
75104

76105
**Record a room creation event**
77106

@@ -115,9 +144,13 @@ To display all events for a given room, the `XRANGE` command is used to fetch al
115144
events = om_redis_conn.xrange(f"stream:room:{room}", min="-", max="+")
116145
```
117146

147+
### Indirect usage of Redis
148+
149+
In addition to storing temporary game state and append-only event logs, Redis also supports the application as a message broker for the celery worker and scheduler, and it supports the SocketIO as a message queue which is required when there are multiple servers process (Flask, celery, celerybeat). Main application data is stored on DB index `0`, and these other services use other DB indexes for isolation and separation of concerns to the extent that it makes sense.
150+
118151
## How to run it locally?
119152

120-
Running the application in a local development environment involves starting the web client and also starting multiple backend services. Backend services can be brought up using a `docker-compose.yml` file or they can be started by running commands in Python virtual environment.
153+
Running the application in a local development environment involves starting the web client and also starting multiple backend services. Backend services can be brought up using a `docker-compose.yml` file or they can be started by running commands in a Python virtual environment.
121154

122155
### Prerequisites
123156

@@ -129,11 +162,25 @@ To run the backend locally with docker and docker-compose you will need:
129162

130163
- docker 20.10.14+
131164
- docker-compose 1.29.2
132-
- Python 3.9 (if not using docker)
165+
- Python 3.9+ (if not using docker)
166+
167+
Run the following command to check your local versions:
133168

134-
Recommended:
169+
```
170+
make check
171+
```
172+
173+
It should show something like:
174+
175+
```
176+
Docker version 20.10.14, build a224086
135177
136-
- Redis Insights
178+
docker-compose version 1.29.2, build 5becea4c
179+
180+
Python 3.10.2
181+
182+
Node v16.16.0
183+
```
137184

138185
### Local installation
139186

@@ -179,6 +226,8 @@ Next you can start the three services in different windows. Before starting each
179226
source .env/bin/activate
180227
```
181228

229+
For advanced usage, please see the [`Makefile`](/Makefile) which has some helpful targets for starting different parts of the application (redis-stack, backend services and client).
230+
182231
**Start the Flask API server**
183232

184233
```
@@ -253,6 +302,4 @@ Before starting `celerybeat`, make sure that you have deleted a file called `cel
253302

254303
The client runs on `http://localhost:3000`. It makes API and websocket connections with the backend which runs on `http://localhost:8000`.
255304

256-
## Deployment
257-
258-
To make deploys work, you need to create free account on [Redis Cloud](https://redis.info/try-free-dev-to)
305+
Please see the [Makefile](/Makefile) for a full list of commands for running the application locally.

app/celerybeat-schedule.db

16 KB
Binary file not shown.

docker-compose.yml

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -61,17 +61,5 @@ services:
6161
- "8001:8001"
6262
container_name: redis
6363

64-
nginx:
65-
container_name: nginx
66-
build:
67-
context: ./nginx
68-
dockerfile: dev/Dockerfile
69-
ports:
70-
- "80:80"
71-
depends_on:
72-
- app
73-
volumes:
74-
- ./nginx/dev/dev.conf:/etc/nginx/nginx.conf:ro
75-
7664
volumes:
7765
redis-data1:

images/rlgl.drawio.png

943 KB
Loading

marketplace.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,12 @@
1313
"redis_features": ["queue", "messaging", "streams"],
1414
"redis_modules": ["RedisSearch"],
1515
"app_image_urls": [
16+
"https://res.cloudinary.com/practicaldev/image/fetch/s--391EQ1p1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m3rqy7eqz40nqk1e04jj.png",
17+
"https://res.cloudinary.com/practicaldev/image/fetch/s--ezf1ssyt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0egpm1igfqyon5kjtujv.png",
18+
"https://res.cloudinary.com/practicaldev/image/fetch/s--uD8xn_Xp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wkmgiz8wtq484rn6dmfc.png"
1619
],
17-
"youtube_url": "",
18-
"special_tags": ["Hackathon", "RedisOM"],
20+
"youtube_url": "https://www.youtube.com/watch?v=BoalZKmgoEU",
21+
"special_tags": ["Hackathon", "Redis OM"],
1922
"verticals": ["Gaming"],
2023
"markdown": "https://raw.githubusercontent.com/briancaffey/redis-light-green-light-dev-to-hackathon/main/README.md"
2124
}

nginx/dev/Dockerfile

Lines changed: 0 additions & 4 deletions
This file was deleted.

nginx/dev/dev.conf

Lines changed: 0 additions & 41 deletions
This file was deleted.

terraform/.gitignore

Lines changed: 0 additions & 5 deletions
This file was deleted.

0 commit comments

Comments
 (0)