{"id":20088257,"url":"https://github.com/alexistm/fun-together","last_synced_at":"2025-05-06T02:32:49.994Z","repository":{"id":63470479,"uuid":"564741909","full_name":"AlexisTM/fun-together","owner":"AlexisTM","description":"Rust Pet project acting as a Websocket Proxy to make serverless Jackbox-like game. Used in https://github.com/AlexisTM/just-party","archived":false,"fork":false,"pushed_at":"2023-06-04T08:51:01.000Z","size":248,"stargazers_count":5,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2024-04-14T22:39:52.129Z","etag":null,"topics":["game","multiplayer","rust","tokio-tungstenite","websocket"],"latest_commit_sha":null,"homepage":"","language":"Rust","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/AlexisTM.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}},"created_at":"2022-11-11T11:45:48.000Z","updated_at":"2024-03-18T15:43:27.000Z","dependencies_parsed_at":"2023-02-06T15:30:43.291Z","dependency_job_id":null,"html_url":"https://github.com/AlexisTM/fun-together","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AlexisTM%2Ffun-together","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AlexisTM%2Ffun-together/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AlexisTM%2Ffun-together/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AlexisTM%2Ffun-together/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/AlexisTM","download_url":"https://codeload.github.com/AlexisTM/fun-together/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":224480728,"owners_count":17318311,"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","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":["game","multiplayer","rust","tokio-tungstenite","websocket"],"created_at":"2024-11-13T16:12:58.849Z","updated_at":"2024-11-13T16:12:59.557Z","avatar_url":"https://github.com/AlexisTM.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"WMBP (Websocket multiplayer backend proxy)\n=========================================\n\nThis is a pet weekend project to learn \u0026 enjoy Rust. Use at your own risk.\n\n## Introduction\n\nWMBP is a project is to help more party games to sprout by providing a backend implementation.\nThe base implementation is a one (*Game*) to many (*Clients*) connection communication pattern, which is coordinated by the *Server*, allowing **serverless party games**.\n\nThe *Game* connects to the *Server* with a websocket and creates a room. Once the room is created, the *Game* will receive the **Room Code** which will be used by *Clients* to connect.\n\nWhile the *Game* has a mandatory communication pattern with the *Server*, the *Clients* have a **seemingly direct connection to the *Game***, both in **binary** and **text**.\n\n![Server example](doc/multiplayer_proxy.png)\n\n## Usage\n\n5. Spin this **Server**, `cargo run --release`\n5. **Game** creates a websocket connection to this server to create a game `new WebSocket(\"ws://127.0.0.1:8081/CREATE\")`\n5. **Game** sends Prepare with a certain amount of players.\n5. **Server** sends to **Game** the *Room Code* and refuses players if there are too many\n5. **Clients** creates a websocket connection to this server to create a game `new WebSocket(\"ws://127.0.0.1:8081/ROOM_CODE\")`\n5. **Game** sends Start once enough players are connected.\n5. **Game** sends data to **Clients** with To and ToStr messages.\n5. **Clients** sends data to **Game** with plain text or arraybuffer (for binary format).\n5. **Game** receives data from **Clients** with From and FromStr messages.\n\n![The game flow of the server](doc/flow.png)\n\n## In depth\n\nGame type:\n- `http://127.0.0.1:8081/ROOM` returns (in text/plain) the game type (started with Prepare)\n\nThen endpoint of the websocket server is defining if you are a Host client (Game) or a Player client by the websocket you created.\n- `ws://127.0.0.1:8081/CREATE` creates a new game\n- `ws://127.0.0.1:8081/ROOM` connects to a room\n\n### Messages as a Game (host)\n\nThe messages for the *Game* are **CBOR** encoded with the following format: `{ \"cmd\": \"snake_case_command\", \"data1\": 1, \"data2\": \"data2\"}`\n\nFor rust users, just take a look at the enum [src/comm.rs#Commands](src/comm.rs).\nFor Javascript users:\n- **\\\u003e Prepare**: `{\"cmd\": \"prepare\", \"max_players\": 8, \"name\": \"test\"}` # Prepares the game with the maximum number of clients\n- **\u003c PrepareReply**: `{\"cmd\": \"prepare_reply\", \"key\": \"ROOM\"}` # On successful game creation, provides the ROOM key\n- **\u003c PlayerJoined**: `{\"cmd\": \"player_joined\", \"player\": 12}` # A new player joined\n- **\u003c PlayerLeft**: `{\"cmd\": \"player_left\", \"player\": 12}` # A player left\n- **\\\u003e Start**: `{\"cmd\": \"start\"}` # Starts the game, prevents the clients to connect from this point on.\n- **\u003c State**: `{\"cmd\": \"state\", \"players\": [5,2,3], \"max_players\": 8, \"accept_conns\": true}` # Provides information about the game, players connected, etc.\n- **\\\u003e Kick**: `{\"cmd\": \"kick\", \"player\": 5}` # Kicks player with id 5 (from the State message)\n- **\u003c Stop**: `{\"cmd\": \"stop\"}` # Disconnect everybody\n- **\\\u003e To**: `{\"cmd\": \"to\", to: [2], \"data\": [1,2,3]}` # Sends binary data to the user 1\n- **\\\u003e ToStr**: `{\"cmd\": \"to_str\", to: [3, 5], \"data\": \"some string\"}` # Sends text data to the user 3 and 5\n- **\u003c From**: `{\"cmd\": \"from\", \"from\": 2, \"data\": [1,2,3]}` # Received when user 2 sent binary data\n- **\u003c FromStr**: `{\"cmd\": \"from\", \"from\": 5, \"data\": \"some string\"}` # Received when user 5 sent string data\n\n### Messages as a client\n\nThe *Client* has no specific message. Sending text (Text type for the websocket), the message will be transferred with `FromStr` to the *Game*, while sending binary data (such as CBOR encore data or images) will be forwarded with `From` to the *Game*.\n\nWhenever *Game* sends data with `To` and `ToStr`, only the data will be forwarded to the client (as everything else would be redundant) as binary or text.\n\nThis means the *Client* has a connection that seems to be directly to the game.\n\n### Optional features\n\n#### tls\n\nIn a wim, I quickly made a TLS feature based on `rustls`, following the example in [hyper-rustls](https://github.com/rustls/hyper-rustls/tree/main/examples). Only later on I understood this was the responsibilty of the cloud service (or nginx or other) in most cases.\nI have no clue what I am doing 🙈\n\n\n### Deploy\n\n#### Render.com\n\nEither you try the blueprint (render.yaml provided) or you create a new app with:\n- Set the repo to this one\n- Define the PORT environment variable as `10000`, this fasten the spinup of the machines\n- Set the build command as `cargo build --release`\n- Set the run command as `cargo run --release 0.0.0.0:10000`\n\n#### Google Cloud\n\nCreate a new project, and, using the PROJECT_ID you created:\n\n```bash\ngcloud config set project PROJECT_ID\ngcloud run deploy\n```\n\nYou can [Delete your ressources here](https://console.cloud.google.com/iam-admin/projects?utm_source=cloud.google.com)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falexistm%2Ffun-together","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Falexistm%2Ffun-together","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falexistm%2Ffun-together/lists"}