{"id":27370172,"url":"https://github.com/datastaxdevs/workshop-streaming-game","last_synced_at":"2025-10-07T01:46:08.158Z","repository":{"id":45313704,"uuid":"415919839","full_name":"datastaxdevs/workshop-streaming-game","owner":"datastaxdevs","description":"A simple multiplayer online game (with in-game chat) featuring: Astra Streaming, Astra DB, WebSockets, React for the front-end and FastAPI for the back-end. Also spiders!","archived":false,"fork":false,"pushed_at":"2023-02-09T16:22:53.000Z","size":18247,"stargazers_count":31,"open_issues_count":1,"forks_count":15,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-04-13T08:57:07.939Z","etag":null,"topics":["datastax","game","gaming","pulsar","real-time","workshop"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/datastaxdevs.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2021-10-11T12:38:06.000Z","updated_at":"2024-08-25T10:36:17.000Z","dependencies_parsed_at":"2025-04-13T08:48:40.030Z","dependency_job_id":"321c4cbe-1126-4214-9d1b-2a76beedcaa7","html_url":"https://github.com/datastaxdevs/workshop-streaming-game","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/datastaxdevs/workshop-streaming-game","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/datastaxdevs%2Fworkshop-streaming-game","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/datastaxdevs%2Fworkshop-streaming-game/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/datastaxdevs%2Fworkshop-streaming-game/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/datastaxdevs%2Fworkshop-streaming-game/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/datastaxdevs","download_url":"https://codeload.github.com/datastaxdevs/workshop-streaming-game/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/datastaxdevs%2Fworkshop-streaming-game/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":278708036,"owners_count":26031932,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-06T02:00:05.630Z","response_time":65,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["datastax","game","gaming","pulsar","real-time","workshop"],"created_at":"2025-04-13T08:48:11.788Z","updated_at":"2025-10-07T01:46:08.114Z","avatar_url":"https://github.com/datastaxdevs.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003c!--- STARTEXCLUDE ---\u003e\n# Drapetisca: a multiplayer online game with Astra Streaming and Websockets\n\n[![Gitpod ready-to-code](https://img.shields.io/badge/Gitpod-ready--to--code-blue?logo=gitpod)](https://gitpod.io/#https://github.com/datastaxdevs/workshop-streaming-game)\n[![License Apache2](https://img.shields.io/hexpm/l/plug.svg)](http://www.apache.org/licenses/LICENSE-2.0)\n[![Discord](https://img.shields.io/discord/685554030159593522)](https://discord.com/widget?id=685554030159593522\u0026theme=dark)\n\nTime: *50 minutes*. Difficulty: *Intermediate*. [Start Building!](#lets-start)\n\nA simple multiplayer online game featuring\n* Astra Streaming (a messaging system in the cloud, built on top of Apache Pulsar)\n* Astra DB (a Database-as-a-service built on Apache Cassandra)\n* WebSockets\n* React.js for the front-end\n* the Python FastAPI framework for the back-end\n\n\u003c!--- ENDEXCLUDE ---\u003e\n\n![Drapetisca screenshot](images/drapetisca_intro_v2.png)\n\n## Objectives\n* Understand the architecture of a streaming-based application\n* Learn how Apache Pulsar works\n* See the interplay between streaming and persistent storage (a.k.a. database)\n* Learn about Websockets on client- and server-side\n* Understand how a FastAPI server can bridge Pulsar topics and WebSockets\n* Understand the structure of a Websocket React.js application\n* **get your very own online gaming platform to share with your friends!**\n\n## Frequently asked questions\n\n- *Can I run the workshop on my computer?*\n\n\u003e You don't have to, it's all already in the cloud! But there is nothing preventing you from running the workshop on your own machine.\n\u003e If you do so, you will need\n\u003e * git installed on your local system\n\u003e * [node 15 and npm 7 or later](https://www.whitesourcesoftware.com/free-developer-tools/blog/update-node-js/)\n\u003e * [Python v3.8+ installed on your local system](https://www.python.org/downloads/)\n\u003e\n\u003e In this readme, we try to provide instructions for local development as well - but keep in mind that\n\u003e the main focus is development on Gitpod, hence **We can't guarantee live support** about local development\n\u003e in order to keep on track with the schedule. However, we will do our best to give you the info you need to succeed.\n\n- *What other prerequisites are there?*\n\u003e * You will need a GitHub account\n\u003e * You will also need an Astra account: don't worry, we'll work through that in the following\n\n- *Do I need to pay for anything for this workshop?*\n\u003e * **No.** All tools and services we provide here are FREE.\n\n- *Will I get a certificate if I attend this workshop?*\n\n\u003e Attending the session is not enough. You need to complete the homeworks detailed below and you will get a nice participation certificate a.k.a. badge.\n\n- *Why \"Drapetisca\"?*\n\n\u003e _Drapetisca socialis_, known as \"invisible spider\", is a very small and hard-to-notice spider found throughout Europe.\n\u003e Since this is a multiplayer game that lets players have social interactions in the play area, why not choose a spider\n\u003e with \"socialis\" in its name?\n\n## Materials for the Session\n\nIt doesn't matter if you join our workshop live or you prefer to work at your own pace,\nwe have you covered. In this repository, you'll find everything you need for this workshop:\n\n- [Workshop Video](https://youtu.be/jfOBPlcd9eA)\n- [Slide deck](slides/DataStaxDevs-workshop-Build_a_Multiplayer_Game_with_Streaming.pdf)\n- [Discord chat](https://dtsx.io/discord)\n- [Questions and Answers](https://community.datastax.com/)\n\n## Homework\n\n\u003cimg src=\"images/streaming-workshop.png?raw=true\" width=\"200\" align=\"right\" /\u003e\n\nDon't forget to complete your assignment and get your verified skill badge! Finish and submit your homework!\n\n1. Complete the practice steps as described below until you have your own app running in Gitpod.\n2. Now roll up your sleeves and modify the code in two ways: (1) we want the API to send a greeting to each new player in the chat box, and (2) we want the player names in the game area to match the icon color. _Please read the detailed guidance found [below](#7-homework-instructions)_.\n3. Take a SCREENSHOT of the running app modified this way. _Note: you will have to restart the API and reload the client to see all changes!_\n4. Submit your homework [here](https://dtsx.io/streaming-game-homework).\n\nThat's it, you are done! Expect an email in a few days!\n\n# Let's start\n\n## Table of contents\n\n1. [Create your Astra Streaming instance](#1-create-your-astra-streaming-instance)\n2. [Create your Astra DB instance](#2-create-your-astra-db-instance)\n3. [Load the project into Gitpod](#3-load-the-project-into-gitpod)\n4. [Set up/start the API](#4-api-setup)\n5. [Set up/start the client](#5-client-setup)\n6. [Play!](#6-play-the-game)\n7. [Homework instructions](#7-homework-instructions)\n8. [Selected topics](#8-selected-topics)\n\n## Astra setup\n\n### 1. Create your Astra Streaming instance\n\n_**`Astra Streaming`** is the simplest way to get a streaming infrastructure based on Apache Pulsar\nwith zero operations at all - just push the button and get your streaming.\nNo credit card required - with the **free tier** comes a generous monthly-renewed credit for you to use._\n\n_**`Astra Streaming`** is tightly integrated with `Astra DB`, the database-as-a-service\nused in most of our workshops (see below, we will use it momentarily).\n**If you already have an Astra account for Astra DB, you can use that\none in the following (and jump to \"Create streaming\" below)!**_\n\nFor more information on Astra Streaming, look at [the docs](https://docs.datastax.com/en/astra-streaming/docs/).\nFor more information on Apache Pulsar, here is [the documentation](https://pulsar.apache.org/docs/en/concepts-overview/).\n\n#### 1a. Register\n\nRegister and sign in to Astra at `astra.datastax.com` by clicking this button (better in a new tab with Ctrl-click or right-click):\n\n\u003ca href=\"https://astra.dev/multiplayer-game\"\u003e\u003cimg src=\"images/create_astra_button.png\" /\u003e\u003c/a\u003e\n\n_you can use your `Github`, `Google` accounts or register with an `email`.\nChoose a password with minimum 8 characters, containing upper and lowercase letters, at least one number and special character.\nYou may be asked to verify your email, so make sure you have access to it._\n\n\u003cdetails\u003e\u003csummary\u003eShow me the steps\u003c/summary\u003e\n    \u003cimg src=\"https://github.com/datastaxdevs/workshop-streaming-game/raw/main/images/astra_signup.gif?raw=true\" /\u003e\n\u003c/details\u003e\n\n#### 1b. Create streaming\n\nOnce registered and logged in, you will be able to create a new Astra Streaming topic: it will convey all messages for this app.\n\nYou can find the instructions in [this wiki](https://github.com/datastaxdevs/awesome-astra/wiki/Create-an-AstraStreaming-Topic): in our case, the parameters to use are:\n\n- tenant name: `gameserver-\u003csomething\u003e` (you have to make it unique, so attach a suffix of your choice)\n- namespace: `default` (we will NOT need to create a new one)\n- topic name: `worldupdates` (persistent=yes, partitioned=no)\n\n\u003e Note: technically you can name your namespace and topic anything you want - but then you have to make sure\n\u003e the environment settings for your API code are changed accordingly (see later).\n\n\u003cdetails\u003e\u003csummary\u003eShow me the steps\u003c/summary\u003e\n    \u003cimg src=\"https://github.com/datastaxdevs/workshop-streaming-game/raw/main/images/astra_create_streaming_topic_v2.gif?raw=true\" /\u003e\n\u003c/details\u003e\n\n#### 1c. Retrieve streaming connection parameters\n\nWhile you are at it, you should get two pieces of information needed later to connect to the topic from the API code. Those are the \"Broker Service URL\" and the \"Token\",\nwhich can be obtained as described in [this wiki article](https://github.com/datastaxdevs/awesome-astra/wiki/Create-an-AstraStreaming-Topic#-step-4-retrieve-the-broker-url).\n\n\u003cdetails\u003e\u003csummary\u003eShow me how to get the topic connection parameters\u003c/summary\u003e\n    \u003cimg src=\"https://github.com/datastaxdevs/workshop-streaming-game/raw/main/images/streaming_secrets.png?raw=true\" /\u003e\n\u003c/details\u003e\n\n\u003e The service URL looks something like `pulsar+ssl://pulsar-[...].streaming.datastax.com:6651`,\nwhile the token is a very long string such as `eyJhbGci [...] cpNpX_qN68Q`.\n\u003e **The token is a secret string and you should keep it for yourself!**\n\n### 2. Create your Astra DB instance\n\nBesides the streaming platform, you'll also need a database for persistence of some\ngame data (the server-side representation of the \"game world\").\n\nCorrespondingly, you will need some connection parameters and secrets in order\nto later be able to access the database.\n\n#### 2a. Create the database\n\n_**`ASTRA DB`** is the simplest way to run Cassandra with zero operations at all - just push the button and get your cluster. No credit card required, $25.00 USD credit every month, roughly 20M read/write operations and 80GB storage monthly - sufficient to run small production workloads._\n\nYou will now create a database with a keyspace in it (a _keyspace_ can contain _tables_.\nToday's application needs just a single table: it will be created for you the first time you\nwill launch it, so don't worry too much).\n\n\u003cimg src=\"images/create_database_button.png\" width=\"250\" align=right /\u003e\n\nTo create the database, locate the \"Create database\" button on the navigation bar on the left of the Astra UI, click on it and fill the required\nvalues:\n\n- **For the database name** - use `workshops`. While Astra DB allows you to fill in these fields with values of your own choosing, please follow our recommendations to ensure the application runs properly.\n\n- **For the keyspace name** - use `drapetisca`. Please stick to this name, it will make the following steps much easier (you have to customize here and there otherwise). In short:\n\n- **For provider and region**: Choose **GCP**, which is immediately available to a fresh account (AWS and Azure would have to be unlocked, _for free_, by contacting Support). Region is where your database will reside physically (choose one close to you or your users).\n\n- **Create the database**. Review all the fields to make sure they are as shown, and click the `Create Database` button. You will be on the **Free** plan.\n\n| Parameter | Value \n|---|---|\n| Database name | workshops  |\n| Keyspace name | drapetisca |\n\nYou will see your new database as `Pending` in the Dashboard;\nthe status will change to `Active` when the database is ready. This will only take 2-3 minutes\n(you will also receive an email when it is ready).\n\n\u003cdetails\u003e\u003csummary\u003eShow me the how to create my Astra DB\u003c/summary\u003e\n    \u003cimg src=\"https://github.com/datastaxdevs/workshop-streaming-game/raw/main/images/astra_create_db.gif?raw=true\" /\u003e\nTo create the database, please note that _the `db_name` and `ks_name` in the above image are just placeholders_.\n\u003c/details\u003e\n\n\u003e _Note_: if you already have a `workshops` database, for instance from a previous workshop with us, you can simply create the keyspace with the `Add Keyspace` button in your Astra DB dashboard: the new keyspace will be available in few seconds.\n\n#### 2b. Create a DB Token\n\nYou need to create a **DB token**, which the API will later use as credentials to access the DB.\n\nFrom the Astra DB UI, [create a token](https://docs.datastax.com/en/astra/docs/manage-application-tokens.html)\nwith `Database Administrator` roles.\n\n\u003cimg src=\"images/orgsettings.png\" width=\"250\" align=right /\u003e\n\n- Locate the \"Current Organization\" menu in the top-left of the Astra UI and select `Organization Settings`\n- Go to `Token Management`\n- Pick the role `Database Administrator` on the select box\n- Click Generate token\n\n\u003cdetails\u003e\u003csummary\u003eShow me the Astra DB token creation\u003c/summary\u003e\n    \u003cimg src=\"https://github.com/datastaxdevs/workshop-streaming-game/raw/main/images/astra-create-token.gif?raw=true\" /\u003e\n\u003c/details\u003e\n\n\u003e **Tip**: you can quickly get to the \"Token Management\" also through the \"...\"\n\u003e menu next to the list of databases in your main Astra DB UI.\n\nThe \"token\" is composed by three parts:\n\n- `Client ID`: it plays the role of _username_ to connect to Astra DB;\n- `Client Secret`: it plays the role of a _password_;\n- `Token` (proper): _not needed today_. It would be used as API key to access Astra via the API Gateway.\n\n\u003e You can either copy and paste the values or download them as a CSV (you'll need the `Client ID` and `Client Secret`momentarily). _To copy the values you can click on the clipboard icons._\n\n\u003cdetails\u003e\u003csummary\u003eShow me the generated Astra DB token\u003c/summary\u003e\n    \u003cimg src=\"https://github.com/datastaxdevs/workshop-streaming-game/raw/main/images/astra-token.png?raw=true\" /\u003e\n\u003c/details\u003e\n\n\u003e Make sure you download the CSV or copy the token values you need: once this page is closed,\n\u003e you won't be able to see your token again for security reasons! (then again, you can always issue a new token).\n\n\u003e **⚠️ Important**\n\u003e ```\n\u003e The instructor will show token creation on screen,\n\u003e but will then immediately destroy the token for security reasons.\n\u003e ```\n\n#### 2c. Download the DB Secure Connection Bundle\n\nThere's a last missing piece needed for the application to successfully connect\nto Astra DB: the \"secure connect bundle\". You have to download it from the Astra UI\nand keep it ready for later usage.\n\n\u003e The secure bundle, a zipfile containing certificates and server address information,\n\u003e will have to be provided to the Cassandra driver when the connection is established\n\u003e (see later steps).\n\nGo to the Astra DB UI, find the `workshops` database and click on it:\n\n1. click on `Connect` tab;\n2. click on `Connect using a driver` (any language will do);\n3. click on the `Download Bundle` drop-down on the right;\n4. finally click on `Secure Connect Bundle (\u003cregion\u003e)` to start the download. The bundle file should have a name such as `secure-connect-workshops.zip` and be around 12KB in size. _Note: make sure you \"Save\" the zipfile whole, without unzipping/opening it (as some browser might suggest you to do._\n\n\u003cdetails\u003e\u003csummary\u003eShow me how to get the Astra DB bundle\u003c/summary\u003e\n    \u003cimg src=\"https://github.com/datastaxdevs/workshop-streaming-game/raw/main/images/astra_bundle.png?raw=true\" /\u003e\n\u003c/details\u003e\n\n\n## Configure and run the application\n\n### 3. Load the project into Gitpod\n\nDevelopment and running will be done within a Gitpod instance (more on that in a second).\n\n#### 3a. Open Gitpod\n\nTo load the whole project (API + client) in your personal Gitpod workspace, please\nCtrl-click (or right-click and open in new tab) on the following button:\n\n\u003ca href=\"https://gitpod.io/#https://github.com/datastaxdevs/workshop-streaming-game\"\u003e\u003cimg src=\"images/open_in_gitpod_button.svg\" /\u003e\u003c/a\u003e\n\n(You may have to authenticate with Github or other providers along the process).\nThen wait a couple of minutes for the installations to complete, at which point you\nwill see a message such as `CLIENT/API READY TO START` in the Gitpod console.\n\n\u003cdetails\u003e\u003csummary\u003eTell me what the Gitpod button does\u003c/summary\u003e\n\n- An IDE is started on a containerized machine image in the cloud\n- there, this repo is cloned\n- some initialization scripts are run (in particular, dependencies get installed)\n- Gitpod offers a full IDE: you can work there, edit files, run commands in the console, use an internal browser, etc.\n\n\u003c/details\u003e\n\n\u003e In case you prefer to work _on your local computer_, no fear! You can simply keep\n\u003e a console open to run the React client (`cd client`) and another for the\n\u003e Python API (`cd api`). For the former\n\u003e you will have to `npm install` and for the latter (preferrably in a virtual environment\n\u003e to keep things tidy and clean) you will have to install the required dependencies\n\u003e e.g. with `pip install -r requirements.txt`.\n\u003e (Mac users will also have to do a `brew install libpulsar` for the API to work.)\n\u003e The rest of this readme will draw your\n\u003e attention to the occasional differences between the Gitpod and the local routes, but\n\u003e we'll generally assume that if you work locally you know what you are doing. Good luck!\n\n#### 3b. Gitpod interface\n\nThis project is composed of two parts: client and API. For this reason, Gitpod\nis configured to spawn _three_ different consoles: the \"default\" one for\ngeneral-purpose actions, an \"api\" console and a \"client\" console (these two\nwill start in the `api` and `client` subdirectories for you).\n**You can switch between consoles by clicking on the items in the lower-right panels in your Gitpod**.\n\n\u003cdetails\u003e\u003csummary\u003eShow me a map of the Gitpod starting layout\u003c/summary\u003e\n\n\u003cimg src=\"https://github.com/datastaxdevs/workshop-streaming-game/raw/main/images/gitpod_view.png?raw=true\" /\u003e\n\n1. File explorer\n2. Editor\n3. Panel for console(s)\n4. Console switcher\n\u003c/details\u003e\n\n\u003e Note: for your convenience, you find this very README open within the Gitpod\n\u003e text editor.\n\n### 4. API setup\n\nBefore you can launch the API, you have to configure connection details to it:\nyou will do it through the dotenv file `.env`.\n\n#### 4a. Streaming environment variables (`.env`, part I)\n\nYou need to pass the streaming connection URL and streaming token to the API for\nit to be able to speak to the Streaming topic. To do so, first **go to the API console**\nand make sure you are in the `api` subdirectory.\n\n\u003e The `pwd` command should output `/workspace/workshop-streaming-game/api`.\n\n\u003e If you are working locally, make sure you are in the `/api` subdirectory\n\u003e of the project for the following commands to work properly. Later anyway,\n\u003e in order to have both the API and the client running, you will need two\n\u003e consoles, one in each of the two `api` and `client` subdirectories.\n\nThen create a file `.env` by copying the `.env.sample` in the same directory,\nwith the commands\n\n    # In the 'api' subdirectory\n    cp .env.sample .env\n    gp open .env\n\n(the second line will simply open the `.env` file in the editor: you can also simply locate the file in Gitpod's file explorer and click on it).\nFill the first lines in the file with the values found earlier\non your Astra Streaming \"Connect\" tab (_keep the quotes in the file_):\n\n- `STREAMING_TENANT`: your very own tenant name as chosen earlier when creating the topic (step `1b`); it should be something like `gameserver-abc`.\n- `STREAMING_SERVICE_URL`: it looks similar to `pulsar+ssl://pulsar-aws-useast2.streaming.datastax.com:6651`\n- `ASTRA_STREAMING_TOKEN`: a very long string (about 500 random-looking chars), see step `1c`. You can copy it on the Astra UI without showing it.\n\n\u003e Note: treat your token as a personal secret: do not share it, do not commit it to the repo, store it in a safe place!\n\n\u003e Note: in case you gave a different namespace/name to your topic, update `.env` correspondingly.\n\u003e If, moreover, you work locally you may have to check the `TRUST_CERTS` variable as well, depending\n\u003e on your OS distribution. Look into the `.env` file for some suggestions.\n\n#### 4b. Upload the DB secure bundle (`.env`, part II)\n\nRemember the secure connect bundle you downloaded earlier from the Astra DB UI?\nIt's time to upload it to Gitpod.\n\n\u003e If you work locally, skip the upload and just be aware of the full path to it for what comes next in the `.env` file.\n\nLocate the file on your computer using the \"finder/explorer\".\nDrag and drop the bundle into the Gitpod explorer window: _make sure you drop it on the\nfile explorer window in Gitpod._\n\n\u003cdetails\u003e\u003csummary\u003eShow me how to drag-and-drop the bundle to Gitpod\u003c/summary\u003e\n    \u003cimg src=\"https://github.com/datastaxdevs/workshop-streaming-game/raw/main/images/drag-and-drop-bundle.png?raw=true\" /\u003e\n\u003c/details\u003e\n\nAs a check, you may want to verify the file is available in the right location with:\n\n    ls -lh /workspace/workshop-streaming-game/secure-connect-*.zip\n\nThe output should tell you the exact path to the file (you can also make sure the file size is around 12KB\nwhile you are at it).\n\n**This exact path to the file must go to line `ASTRA_DB_SECURE_CONNECT_BUNDLE` of the `.env` file.**\nThe line has been pre-filled for you already, but if the bundle has a different name or is at a\ndifferent location (e.g. if you work locally, or your DB has another name),\nmake sure you change the value accordingly.\n\n#### 4c. Database access secrets (`.env`, part III)\n\nWith the secure bundle in place and set up in the `.env`, you can turn to the last missing\npiece: the secrets to access the DB.\n\nInsert the Astra DB `Client ID` and `Client Secret` you obtained earlier as parts of the \"Astra DB Token\"\nin the `.env` file (again, keep the quotes around the values):\n\n    ASTRA_DB_USERNAME=\"tByuQfj...\"\n    ASTRA_DB_PASSWORD=\"lGzF5,L...\"\n\n\u003e In case your keyspace has a name other than `drapetisca`, check the `ASTRA_DB_KEYSPACE` in your `.env` as well.\n\nCongratulations: you should now have completed the `.env` setup!\n\n#### 4d. Start the API\n\nMake sure you are in the API console and in the `api` subdirectory.\nYou can now **start the API**:\n\n    # In the 'api' subdirectory\n    uvicorn api:app\n\nYou should see the API start and log some messages in the console, in particular\n\n    INFO:     Application startup complete.\n    INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)\n\nCongratulations: the API is up and is ready to accept client requests.\nLeave it running and turn your attention to the client.\n\n\u003e Note: this is how you start the API in a development environment. To deploy\n\u003e to production, you should set up a multi-process system service for `uvicorn`\n\u003e with the `--workers` option and put the whole thing behind a\n\u003e reverse proxy. _This is not covered here_.\n\n### 5. Client setup\n\nMake sure you **go to the client console** for the following\n(to switch consoles, look at the lower-right panel in your Gitpod layout).\nYou should be in the `client` project subdirectory (i.e. the `pwd` command should print `/workspace/workshop-streaming-game/client`).\n\n#### 5a. Install dependencies\n\nFirst ensure all required dependencies are installed:\n\n    # In the 'client' subdirectory\n    npm install\n\n\u003e Note: the command would take a few minutes on a fresh directory; we secretly instructed Gitpod\n\u003e to preinstall them just to save you some time in this step - still, we want\n\u003e you to go through it. Obviously, if you are working on your local environment,\n\u003e this will be slower.\n\n#### 5b. Start the client\n\nThe client is ready to go! **Launch it** in development mode with:\n\n    # In the 'client' subdirectory\n    npm start\n\nLet's assume you are working within Gitpod, which wraps locally-exposed ports\nand makes them accessible through ordinary HTTPS domain names.\nAs the client is available, Gitpod will automatically open it in its \"simple browser\",\nusing a domain such as `https://3000-tan-swallow-2yz174hp.ws-eu17.gitpod.io`.\nThis URL can be obtained also by typing, in the general-purpose Gitpod console,\n\n    gp url 3000\n\n(3000 being the port number locally used by npm to serve the client).\nThis will match the URL shown in the address bar of your simple browser.\n\nNote that you can also take this URL and open the application in a new tab,\n**which you are encouraged to do to use your full screen**.\n\n\u003e Note: we set up Gitpod for this workshop so as to make this URL accessible by anyone, to allow you\n\u003e to paste the link to your friends, thereby inviting them to your own game instance!\n\n\u003e If you are running everything locally on your computer, instead, you can\n\u003e open the client on `http://localhost:3000` and use the\n\u003e default API location of `ws://localhost:8000` to enter the game.\n\n\u003e Note: this is how you launch the client in development mode. For deploying\n\u003e to production, you should first build the project and then serve it from\n\u003e a static Web server. _This is not covered here_.\n\n### 6. Play the game!\n\nWe finally have all pieces in place:\n\n- an Astra Streaming topic;\n- an API bridging it to ...\n- ... a client ready to establish WebSocket connections.\n- (and an Astra DB instance acting as persistent storage to back the API)\n\nIt is time to play!\n\n#### 6a. Enter the game\n\nChange your name if you desire (a spider name is drawn at random for you).\nYou will also see that you are given a (read-only) unique player ID and that an API address\nis configured for the client to establish WebSocket connections to.\n\n\u003e The API location points to the instance of the API running alongside the client:\n\u003e you should generally not have to change it (but please notice the protocol is\n\u003e either `ws` or `wss`, which stand for WebSocket and Secure WebSocket respectively).\n\nTo enter the game, click the \"Enter Game\" button.\n\n\u003cdetails\u003e\u003csummary\u003eShow me the \"Enter Game\" form\u003c/summary\u003e\n    \u003cimg src=\"https://github.com/datastaxdevs/workshop-streaming-game/raw/main/images/drapetisca_2.png?raw=true\" /\u003e\n\u003c/details\u003e\n\nWell done: you are in the game. You should see your player appear in the arena!\n\n- To control your player, either use the on-screen arrow buttons or, after bringing the game field into focus, your keyboard's arrow keys;\n- you can use the in-game chat box on the left.\n\n\u003cdetails\u003e\u003csummary\u003eShow me the player after entering the game\u003c/summary\u003e\n    \u003cimg src=\"https://github.com/datastaxdevs/workshop-streaming-game/raw/main/images/drapetisca_3_v2.png?raw=true\" /\u003e\n\u003c/details\u003e\n\n\u003e **Note**: if you experience a laggy game, especially with several players at once,\n\u003e it is probably due to the fact that you have no control over the physical location of your Gitpod instance:\n\u003e it may have been deployed far from the database. Remember that in a real-life online game great care\n\u003e is taken to keep all parts close to each other to keep latencies under control.\n\u003e If you want to play the game nevertheless, you can set the `USE_IN_MEMORY_STORAGE` variable\n\u003e to `\"1\"` in your `.env` and then stop-and-restart the API: this will replace usage of Astra DB\n\u003e with an in-memory local store; it is a development-only solution, however, that would of course\n\u003e not work were you to scale to several running instances of the API.\n\nAnything your player does is sent to the API through a WebSocket in the form of an \"update message\";\nfrom there, it is published to the Astra Streaming topic (and persisted to the database).\nThe API will then pick up the update\nand broadcast to all players, whose client will process it, eventually leading to a refresh of the game status\non the front-end. All this happens in a near-real-time fashion at every action by every player.\n\n\u003e Note that the game shows the last sent message and the last received messages for you to better inspect\n\u003e the messaging pattern at play.\n\n#### 6b. Try to cheat\n\nLet's be honest: there's no multiplayer game without cheaters - at least, cheat attempts.\nSo, for example, try to _walk beyond the boundaries of the play area_, to see what happens.\nNotice the \"Position\" caption on the left sidebar? If you keep an arrow key pressed\nlong enough, you will sure be able to bring that position to an illegal value such as `(-1, 0)`.\nBut as soon as you release the key, the position bounces back to a valid state.\n\nHere's the trick: this \"position\", shown in the client, is nothing more than a variable\nin the client's memory. Every update (including `(-1, 0)`) is sent to the API, which\nis the sole actor in charge of validation: an illegal value will be corrected and sent back\nto all clients (_consider the API has access to the game-world state persisted on DB\nand can handle collisions and the like_).\nIn particular, your own client will adjust knowledge of its own position\nbased on this feedback from the API - which is why you see the illegal value only briefly.\n\nAll of this must happen asynchronously, as is the nature of the communication between client\nand API. There is a lesson here, which has been hard-earned by online game devs over the years:\n_never leave validity checks in the hand of the client_.\n\n\u003e Remember the hordes of cheaters in ... er ... Diablo I ?\n\n**Implications on the architecture**\n\nUnfortunately such an all-server architecture is more complex to achieve.\nOne has to introduce a \"generation counter\" to avoid accidentally triggering\ninfinite loops of spurious player-position updates - you can see this\never-increasing generation counter (`generation`) if you inspect the\nplayer-position updates shown at the bottom of the application.\n\nIn the client code, the crucial bit is to accept updates to your-own-position\ncoming from the server, _only if they are recent enough_. For further inspection,\nhave a look at:\n\n- API: usage of `validatePosition` at line 66 of `api.py`;\n- Client: condition on `generation` at line 109 of `App.js` before invoking `setPlayerX` and `setPlayerY`.\n\n\u003e Note: in this architecture, we very much **want** to have a server between\n\u003e clients and Pulsar topic, with the responsibility of performing validations.\n\u003e Even more so in a complex game, where each client action (message) triggers\n\u003e potentially several actions in the world. But we want to mention, in passing by,\n\u003e that Pulsar also offers its own\n\u003e [native WebSocket interface](https://pulsar.apache.org/docs/en/client-libraries-websocket/)\n\u003e (and so does Astra Streaming),\n\u003e for clients to directly connect to topics using that protocol.\n\n#### 6c. Bring your friends\n\nBut wait ... this is a _multiplayer_ game, isn't it? So, go ahead and open a new\nbrowser tab, then enter the game as someone else.\n\nHooray! As soon as you move around with this new player,\nyou will see the whole architecture at work:\n\n1. client sends updates on your player's position through the \"player websocket\";\n2. API checks game state on DB and validates this update, taking action if needed;\n3. API (a) publishes the validated player update to the Astra Streaming topic and (b) persists new game-state to DB;\n4. API receives back new messages by listening to this same Astra Streaming topic;\n5. API broadcasts updates on any player to all connected clients through the \"world websocket\";\n6. at each such update, the client's game arena is adjusted (for all connected clients).\n\nWhat is really cool is that **you can give this URL to your friends** and have them\nenter your very own game!\n\n_Please do this and tell the world about how easy it is to build a multiplayer real-time\ngame with Astra Streaming!_\n\n#### 6d. Fun with the Streaming UI\n\nThe Astra Streaming interface makes it possible to eavesdrop on the topic and\nobserve the messages passing through it. This may prove very useful for\ndebugging.\n\nIn the Astra Streaming UI, head to the \"Try Me\" tab and make sure:\n\n- the namespace and the (producer, consumer) topics are set to the values used earlier;\n- connection type is \"Read\";\n- read position is \"Latest\"\n\nGood, now click \"Connect\".\n\n\u003cdetails\u003e\u003csummary\u003eShow me the \"Try Me\" interface\u003c/summary\u003e\n    \u003cimg src=\"https://github.com/datastaxdevs/workshop-streaming-game/raw/main/images/eavesdrop_streaming.gif?raw=true\" /\u003e\n\u003c/details\u003e\n\n\u003cdetails\u003e\u003csummary\u003eA demo video of the \"Try Me\" feature (Youtube)\u003c/summary\u003e\n    \u003ca href=\"https://youtu.be/WWYXeZh0S9k\"\u003e\n        \u003cimg src=\"https://github.com/datastaxdevs/workshop-streaming-game/raw/main/images/try-me-demo-video-thumbnail.png?raw=true\" /\u003e\n    \u003c/a\u003e\n\u003c/details\u003e\n\nYou now have a privileged view over the messages flowing through the Streaming\ntopic. Now try writing something in the Chat box: can you see the corresponding\nmessage in the Streaming UI?\n\nWhat kind of message do you see, instead, if you move you player?\n\nBut wait, there's more: now you can **hack the system**! Indeed, this same interface lets you\nproduce surreptitious messages into the topic (\"Send\" button on the Streaming UI).\nTry to insert a message such as:\n\n    {\n        \"messageType\": \"chat\",\n        \"payload\": {\n            \"id\": \"000\",\n            \"name\": \"Phantom!\",\n            \"text\": \"Booo!\"\n        },\n        \"playerID\": \"nonexistent\"\n    }\n\nand keep an eye on the chat box.\n\nEven better, try to inject a message such as _(you may have to adjust the `x`, `y` coordinates for this to be real fun)_:\n\n    {\n        \"messageType\": \"brick\",\n        \"payload\": {\n            \"name\": \"phantom brick!\",\n            \"x\": 0,\n            \"y\": 0\n        }\n    }\n\nwhat happens in the game UI when you to this? Can you walk to that spot? (Why?)\n\n\u003e Also, have you noticed that the \"Try Me\" interface shows how each message you publish to the topic is echoed back to you?\n\u003e This is done by the API logic and is part of the game design.\n\nNow, you just had a little fun: but, seriously speaking, this ability to manually intervene in the stream of messages makes for a valuable debugging tool.\n\n#### 6e. A quick look at the data on DB\n\nAny change to the game-world, either originated in the API or coming from\na player (and then just validated at API level) is persisted on database.\nIf you are curious, you can look at the raw data directly within the Astra DB UI.\n\nEach time the API starts, it will generate a new \"game ID\", under which all info\npertaining to this particular game will be stored. In fact, `game_id` plays the\nrole of\n[partition key](https://docs.datastax.com/en/astra-cql/doc/cql/ddl/dataModelingApproach.html) in the underlying `drapetisca.objects_by_game_id` table.\n\n\u003e The topic of data storage and data modeling in Cassandra is huge and we won't\n\u003e do it justice here. Just bear with us to see the game data, and if you want\n\u003e to know more you can start from [Data modeling by example](https://www.datastax.com/learn/data-modeling-by-example) and [What is Cassandra?](https://www.datastax.com/cassandra). You will embark on a long and exciting journey!\n\nLocate the \"CQL Console\" tab for the `workshops` database in your Astra DB dashboard\nand click on it. An interactive shell will be spawned for you, to type the following commands:\n\n```sql\nUSE drapetisca ;\nSELECT * FROM objects_by_game_id ;\n```\n\nYou should see several lines in the output, corresponding to the objects present in the game(s)\nand their properties.\n\n\u003e If you already started several games (e.g. by hitting Ctrl-C and restarting `uvicorn` in the API console), notice that the info for each of them is neatly grouped by the value of the `game_id` column.\n\n\u003cdetails\u003e\u003csummary\u003eShow me the game data in the Astra DB CQL Console\u003c/summary\u003e\n    \u003cimg src=\"https://github.com/datastaxdevs/workshop-streaming-game/raw/main/images/cql_console.gif?raw=true\" /\u003e\n\u003c/details\u003e\n\n### 7. Homework instructions\n\nHere are some more details on how to do the homework. We require two modifications\nto the code, one on the API and one on the client. Once you change both, and restart,\nyou will be able to take a screenshot showing the new game appearance and submit\nit to us!\n\n#### 7a. Server side\n\nWe want a greeting message to be sent from the API to a new client right after\nthey join. To do so, the `api.py` already imports a function `makeWelcomeUpdate`\nthat returns a \"chat message\" ready to be sent through the WebSocket.\nYou may also want to make use of variable `newPlayerName` that is made available\nin the API code.\n\n**You should add a line in the function `playerWSRoute` that creates the welcome\nchat message and sends it to the WebSocket**. _Suggestion: this is really not\nso different from the geometry update any new client receives upon connecting._\n\n#### 7b. Client side\n\nWe want the player names on the game field to have the same color as the player\nicons instead of always dark grey as they are now. If you look into `GameField.js`,\nyou'll notice that the SVG `text` element currently has a class name `\"player-name\"`.\n\n**Make it so that players (self/other) use different class names in their `text`\nelement and have a color matching their icon**. _Suggestion: the right class name\nis already calculated a few lines above for you to use (you can check in `App.css` as well)_.\n\n#### 7c. Restart, test and take a screenshot\n\nRemember to stop and restart the API: go to its console, hit Ctrl-C and\nre-run `uvicorn api:app` to do so. All current WebSocket connections will\nbe lost.\n\nThe client is running in development mode, so it should pick up any changes\nlive and be immediately ready to serve the new version: reloading the app page\n(and re-entering the game) should be enough.\n\nAt that point you will be playing the improved game: homework completed!\n\n\u003cdetails\u003e\u003csummary\u003eShow me the new features in the game\u003c/summary\u003e\n    \u003cimg src=\"https://github.com/datastaxdevs/workshop-streaming-game/raw/main/images/drapetisca_homework.png?raw=true\" /\u003e\n\u003c/details\u003e\n\n### 8. Selected topics\n\nLet us briefly mention some specific parts of the implementation of this game.\n\n#### 8a. WebSockets and React\n\nAPI and client communicate through WebSockets: in this way, we have a connection\nthat is kept open for a long time and allows for a fast exchange of messages\nwithout the overhead of establishing a new connection for each message;\nmoreover, this allows the server to push data without waiting for the client\nto initiate the exchange (as in the obsolete technique of client-side polling).\nWebSockets follow a robust and standardized [protocol](https://datatracker.ietf.org/doc/html/rfc6455)\nwhich makes it possible for us developers to concentrate on our game logic\ninstead of having to worry about the communication internals.\n\nIn particular, this game uses two WebSockets:\n\n- a \"player\" one for bidirectional client/server data transmission in a direct fashion;\n- a \"world\" one where the API route all messages picked up by the streaming topic. Most game status updates go through this route (with the exception of those directed at an individual player).\n\nYou can find the corresponding variables `pws` and `wws` in the client code, respectively.\n\nIn Javascript, one _subscribes to an event_ on an open WebSocket, providing\na callback function with `webSocket.onmessage = \u003ccallback\u003e`. But beware:\nif you simply try to read a React state (such as `generation`) from within\nthe callback, you will generally get a stale value, _corresponding to the\nstate when the subscription was made_. In practice, the state variable\nis \"closed over\". To overcome this problem, and be able to access the latest\nupdated value of the state, we declare a React \"reference\" with `useRef`\nand, after linking it to the state we want to read, we use this reference\nwithin the callback to dynamically retrieve the current value of the state.\n\n\u003cdetails\u003e\u003csummary\u003eLook at lines 49-50 and then 103 of `App.js`, for example.\u003c/summary\u003e\n    \u003cimg src=\"https://github.com/datastaxdevs/workshop-streaming-game/raw/main/images/ref_code_1.png?raw=true\" /\u003e\n\u003c/details\u003e\n\n#### 8b. FastAPI\n\nThis game's architecture involves a server. Indeed, we would not be able\nto implement it using only serverless functions, at least not in a way similar\nto what you see here, because of statefulness. We need a server able to sustain\nthe WebSocket connections for a long time, on one side, and to maintain\nlong-running subscriptions to the Pulsar topics on the other side.\n\nWe chose to create the API in Python, and to use\n[FastAPI](https://fastapi.tiangolo.com/), for a couple of very valid\nreasons. FastAPI integrates very well with the async/await features of modern\nPython, which results in a more efficient handling of concurrency. Moreover,\nit supports WebSockets (through its integration with\n[Starlette](https://www.starlette.io/)) with a natural syntax that reduces\nthe need for boilerplate code to near zero.\n\n\u003e There are other cool features of FastAPI (besides its namesake high performance),\n\u003e which we do not employ here but make it a prime choice. There is a clever\n\u003e mechanism to handle route dependencies aimed at reducing the amount of \"boring\"\n\u003e code one has to write; and there is native support for those small tasks\n\u003e that sometimes you have to trigger asynchronously right after a request completes,\n\u003e those that in other frameworks would have required to set up machinery like Celery.\n\nHave a look at `api.py` to see how a WebSocket connection is handled: decorating\na certain function with `@app.websocket(...)` is almost everything you need to\nget started. One of the arguments of the function will represent the WebSocket\nitself, supporting methods such as `send_text` and `receive_text`. Each\nactive WebSocket connection to the server will spawn an invocation of this\nfunction, which will run as long as the connection is maintained: the support\nfor async/await guarantees that these concurrent executions of the\nWebSocket function will be scheduled efficiently with no deadlocks.\n\n#### 8c. SVG Tricks\n\nOne of the React components in the client code is the `GameField`, which\nrepresents an area where we draw the players. This is a single large SVG\nelement, whose child elements are managed with the usual `jsx` syntax.\n\nA technique that proved useful in this game is that of defining, and then\nre-using multiple times, \"patterns\", basically as sprites. If you look\nat the `GameField.js` render code, you notice that the SVG first declares\nsome `pattern` elements with certain `id`s (such as `lyco_other`).\nThese patterns are then employed in various parts of the SVG to \"fill\"\nrectangles, which effectively makes it possible to use them as repeated sprites:\n\n        \u003crect .... fill=\"url(#lyco_other)\"\u003e\u003c/rect\u003e\n\n## The End\n\nCongratulations, you made it to the end! Please share the URL of your game with\nyour friends: who does not love a little cozy spider gathering?\n\n_(Please notice that after some inactivity your Gitpod instance will be hibernated:\nyou will need to re-start client and server to be able to play again.)_\n\nDon't forget to complete and submit your [homework](#homework) to claim\nyour badge, and see you next time!\n\n\u003e DataStax Developers\n\n![Theridion grallator](images/Theridion_grallator.png)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdatastaxdevs%2Fworkshop-streaming-game","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdatastaxdevs%2Fworkshop-streaming-game","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdatastaxdevs%2Fworkshop-streaming-game/lists"}