├── .clang-format
├── .devcontainer
├── Dockerfile
├── devcontainer.json
└── docker-compose.yml
├── .gitattributes
├── .github
├── FUNDING.yml
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
├── codeql
│ └── config.yml
└── workflows
│ ├── codeql.yml
│ ├── deploy-auth-server.yml
│ ├── deploy-game.yml
│ ├── deploy-nginx.yml
│ ├── deploy-random.yml
│ ├── deploy-ssh-gateway.yml
│ ├── graphviz.yml
│ └── upload-assets.yml
├── .gitignore
├── .gitmodules
├── .vscode
└── settings.json
├── LICENSE
├── README.md
├── assets
├── achievements
│ ├── 3x_doubles.png
│ ├── 5x_doubles.png
│ ├── 7x_doubles.png
│ ├── addicted.png
│ ├── astronaut.png
│ ├── beginners_luck.png
│ ├── getting_started.png
│ ├── good_job.png
│ ├── negative.png
│ ├── oops.png
│ ├── perfect.png
│ ├── rude.png
│ └── thief.png
├── ammo.js
└── badges
│ ├── admin.png
│ └── premium.gif
├── auth
├── .cargo
│ └── config.toml
├── .gitignore
├── Cargo.lock
├── Cargo.toml
├── Dockerfile
├── achievements.json
├── badges.json
├── build.rs
├── devdb
│ └── docker-compose.yml
├── docker-compose.yml
├── migrations
│ ├── V10__donors.sql
│ ├── V11__payments.sql
│ ├── V12__pubkeys.sql
│ ├── V13__pubkey_settings.sql
│ ├── V14__dice_type_strings.sql
│ ├── V15__histogram_tracking.sql
│ ├── V16__bigger_ints.sql
│ ├── V17__winner_histograms.sql
│ ├── V1__initial.sql
│ ├── V2__drop_passwords.sql
│ ├── V3__snake_case_everything.sql
│ ├── V4__drop_achievements.sql
│ ├── V5__default_username.sql
│ ├── V6__anon_identity.sql
│ ├── V7__cascades.sql
│ ├── V8__settings.sql
│ └── V9__player_colors.sql
├── src
│ ├── generated.rs
│ ├── lib.rs
│ ├── main.rs
│ ├── migrations.rs
│ └── routes
│ │ ├── server_routes.rs
│ │ ├── user_routes.rs
│ │ └── webhook_routes.rs
└── tasks.toml
├── client
├── .gitignore
├── .prettierrc
├── functions
│ ├── room
│ │ └── [room_id].ts
│ └── tsconfig.json
├── index.html
├── package-lock.json
├── package.json
├── public
│ ├── Icosphere.png
│ ├── crown.png
│ ├── default_player.png
│ ├── dice.png
│ ├── emissive.png
│ ├── favicon.ico
│ ├── gear.png
│ ├── gold6.png
│ ├── hands.png
│ ├── itsboats.png
│ ├── logo192.png
│ ├── manifest.json
│ ├── normal6.png
│ ├── normal6_2.png
│ ├── robots.txt
│ ├── simulated.txt
│ └── skybox
│ │ ├── room.hdr
│ │ ├── skybox_nx.jpg
│ │ ├── skybox_ny.jpg
│ │ ├── skybox_nz.jpg
│ │ ├── skybox_px.jpg
│ │ ├── skybox_py.jpg
│ │ └── skybox_pz.jpg
├── src
│ ├── 3d
│ │ └── main.ts
│ ├── App.css
│ ├── App.tsx
│ ├── actions
│ │ └── settings.ts
│ ├── api
│ │ └── auth.ts
│ ├── connection.tsx
│ ├── constants.ts
│ ├── hooks
│ │ └── window_size.ts
│ ├── index.css
│ ├── index.tsx
│ ├── pages
│ │ ├── game_page.tsx
│ │ ├── home_page.tsx
│ │ ├── login.css
│ │ ├── onboard_page.tsx
│ │ ├── settings_page.tsx
│ │ ├── tab_error_page.tsx
│ │ └── twitch_oauth_page.tsx
│ ├── providers
│ │ ├── achievements.tsx
│ │ ├── auth.tsx
│ │ └── badges.tsx
│ ├── react-app-env.d.ts
│ ├── reducers
│ │ ├── auth.ts
│ │ ├── chat.ts
│ │ ├── connection.ts
│ │ ├── game.ts
│ │ ├── pop_text.ts
│ │ ├── settings.ts
│ │ └── themes.ts
│ ├── selectors
│ │ └── game_selectors.ts
│ ├── stitches.config.ts
│ ├── store.ts
│ ├── textmods.css
│ ├── toastify.css
│ ├── twitch.tsx
│ ├── types
│ │ └── api.ts
│ └── ui
│ │ ├── achievement.css
│ │ ├── achievement.tsx
│ │ ├── auth_menu.tsx
│ │ ├── avatar.tsx
│ │ ├── badge.tsx
│ │ ├── buttons
│ │ ├── add_sub_button.tsx
│ │ ├── button.tsx
│ │ ├── buttons.css
│ │ ├── octocat.tsx
│ │ ├── restart_button.tsx
│ │ ├── roll_button.tsx
│ │ ├── slider.tsx
│ │ ├── textarea.tsx
│ │ └── toggle.tsx
│ │ ├── chat.tsx
│ │ ├── cheat_sheet.tsx
│ │ ├── conn_banner.tsx
│ │ ├── dice.css
│ │ ├── dice.png
│ │ ├── dice.tsx
│ │ ├── die.tsx
│ │ ├── floating_bar.tsx
│ │ ├── game_panel.tsx
│ │ ├── icons
│ │ ├── disconnected.tsx
│ │ ├── help.tsx
│ │ └── kick.tsx
│ │ ├── player.tsx
│ │ ├── players.tsx
│ │ ├── poptext.tsx
│ │ └── settings.tsx
├── tasks.toml
├── tsconfig.json
├── types
│ └── react-inline-editing.d.ts
└── vite.config.ts
├── docs
├── .nojekyll
├── CNAME
├── README.md
├── _sidebar.md
├── gen.md
├── gen
│ ├── .nojekyll
│ ├── interfaces
│ │ ├── server_messages.ChatMsg.md
│ │ ├── server_messages.DisconnectMsg.md
│ │ ├── server_messages.GameError.md
│ │ ├── server_messages.GameState.md
│ │ ├── server_messages.IGameState.md
│ │ ├── server_messages.JoinMsg.md
│ │ ├── server_messages.KickMsg.md
│ │ ├── server_messages.ReconnectMsg.md
│ │ ├── server_messages.Redirect.md
│ │ ├── server_messages.RestartMsg.md
│ │ ├── server_messages.RollAgainMsg.md
│ │ ├── server_messages.RollMsg.md
│ │ ├── server_messages.Room.md
│ │ ├── server_messages.RoomList.md
│ │ ├── server_messages.UpdateMsg.md
│ │ ├── server_messages.UpdateNameMsg.md
│ │ ├── server_messages.UpdateTurnMsg.md
│ │ ├── server_messages.WelcomeMsg.md
│ │ ├── server_messages.WinMsg.md
│ │ ├── store_types.Achievement.md
│ │ ├── store_types.AchievementProgress.md
│ │ ├── store_types.AchievementUnlock.md
│ │ ├── store_types.DieRoll.md
│ │ ├── store_types.Player.md
│ │ ├── store_types.ReportStats.md
│ │ ├── store_types.ServerPlayer.md
│ │ ├── store_types.UserData.md
│ │ └── store_types.UserStats.md
│ ├── modules.md
│ └── modules
│ │ ├── server_messages.md
│ │ └── store_types.md
├── index.html
└── system-graph.png
├── flake.lock
├── flake.nix
├── game
├── .ccls
├── .ccls-root
├── .gitignore
├── Dockerfile
├── Makefile
├── compile_flags.txt
├── docker-compose.yml
├── includes
│ ├── HTTPRequest.hpp
│ ├── base.h
│ ├── defaults.h
│ ├── json.hpp
│ ├── jwt.h
│ └── traits.h
├── run-server.sh
├── src
│ ├── API.cpp
│ ├── API.h
│ ├── AuthServerRequestQueue.cpp
│ ├── AuthServerRequestQueue.h
│ ├── Consts.h
│ ├── Game.cpp
│ ├── Game.h
│ ├── GameCoordinator.cpp
│ ├── GameCoordinator.h
│ ├── GameServer.cpp
│ ├── HTTPClient.cpp
│ ├── HTTPClient.h
│ ├── JWTVerifier.cpp
│ ├── JWTVerifier.h
│ ├── Metrics.h
│ ├── RequestQueue.h
│ ├── RichTextStream.h
│ ├── RngOverTcp.h
│ ├── StringUtils.cpp
│ ├── StringUtils.h
│ ├── achievements
│ │ ├── All.cpp
│ │ ├── All.h
│ │ ├── Astronaut.h
│ │ ├── BaseAchievement.h
│ │ ├── Doubles.h
│ │ ├── GettingStarted.h
│ │ ├── Negative.h
│ │ ├── Oops.h
│ │ ├── Perfect.h
│ │ ├── Rude.h
│ │ ├── Thief.h
│ │ └── WinGames.h
│ └── api
│ │ ├── API.cpp
│ │ ├── API.hpp
│ │ ├── Achievement.hpp
│ │ ├── AchievementData.hpp
│ │ ├── AchievementProgress.hpp
│ │ ├── AchievementProgressType.hpp
│ │ ├── AchievementUnlock.hpp
│ │ ├── AchievementUnlockType.hpp
│ │ ├── Alignment.hpp
│ │ ├── AuthDonateResponse.hpp
│ │ ├── AuthRefreshTokenResponse.hpp
│ │ ├── AuthSettingsRequest.hpp
│ │ ├── ChatMsg.hpp
│ │ ├── ChatMsgType.hpp
│ │ ├── Color.hpp
│ │ ├── Dice.hpp
│ │ ├── DiceType.hpp
│ │ ├── DieRoll.hpp
│ │ ├── DisconnectMsg.hpp
│ │ ├── DisconnectMsgType.hpp
│ │ ├── GameError.hpp
│ │ ├── GameErrorType.hpp
│ │ ├── GameState.hpp
│ │ ├── GameStateType.hpp
│ │ ├── IGameState.hpp
│ │ ├── JoinMsg.hpp
│ │ ├── JoinMsgType.hpp
│ │ ├── KickMsg.hpp
│ │ ├── KickMsgType.hpp
│ │ ├── LoginRequest.hpp
│ │ ├── Modifier.hpp
│ │ ├── MsgElement.hpp
│ │ ├── Player.hpp
│ │ ├── ReconnectMsg.hpp
│ │ ├── ReconnectMsgType.hpp
│ │ ├── Redirect.hpp
│ │ ├── RedirectType.hpp
│ │ ├── RefetchPlayerMsg.hpp
│ │ ├── RefetchPlayerMsgType.hpp
│ │ ├── ReportStats.hpp
│ │ ├── RestartMsg.hpp
│ │ ├── RestartMsgType.hpp
│ │ ├── RichTextChunk.hpp
│ │ ├── RichTextChunkType.hpp
│ │ ├── RichTextMsg.hpp
│ │ ├── RichTextMsgType.hpp
│ │ ├── RollAgainMsg.hpp
│ │ ├── RollAgainMsgType.hpp
│ │ ├── RollMsg.hpp
│ │ ├── RollMsgType.hpp
│ │ ├── Room.hpp
│ │ ├── RoomListMsg.hpp
│ │ ├── RoomListMsgType.hpp
│ │ ├── ServerMsg.hpp
│ │ ├── ServerMsgMsg.hpp
│ │ ├── ServerMsgType.hpp
│ │ ├── ServerPlayer.hpp
│ │ ├── Setting.hpp
│ │ ├── SpectatorsMsg.hpp
│ │ ├── SpectatorsMsgType.hpp
│ │ ├── UpdateMsg.hpp
│ │ ├── UpdateMsgType.hpp
│ │ ├── UpdateNameMsg.hpp
│ │ ├── UpdateNameMsgType.hpp
│ │ ├── UpdateTurnMsg.hpp
│ │ ├── UpdateTurnMsgType.hpp
│ │ ├── UserData.hpp
│ │ ├── UserId.hpp
│ │ ├── UserIdType.hpp
│ │ ├── UserStats.hpp
│ │ ├── WelcomeMsg.hpp
│ │ ├── WelcomeMsgType.hpp
│ │ ├── WinMsg.hpp
│ │ ├── WinMsgType.hpp
│ │ └── helper.hpp
├── tasks.toml
└── uWebSockets
│ ├── LICENSE
│ ├── README.md
│ ├── src
│ ├── App.h
│ ├── AsyncSocket.h
│ ├── AsyncSocketData.h
│ ├── BloomFilter.h
│ ├── HttpContext.h
│ ├── HttpContextData.h
│ ├── HttpParser.h
│ ├── HttpResponse.h
│ ├── HttpResponseData.h
│ ├── HttpRouter.h
│ ├── Loop.h
│ ├── LoopData.h
│ ├── MessageParser.h
│ ├── MoveOnlyFunction.h
│ ├── Multipart.h
│ ├── PerMessageDeflate.h
│ ├── ProxyParser.h
│ ├── QueryParser.h
│ ├── TopicTree.h
│ ├── Utilities.h
│ ├── WebSocket.h
│ ├── WebSocketContext.h
│ ├── WebSocketContextData.h
│ ├── WebSocketData.h
│ ├── WebSocketExtensions.h
│ ├── WebSocketHandshake.h
│ └── WebSocketProtocol.h
│ └── uSockets
│ ├── LICENSE
│ ├── Makefile
│ ├── README.md
│ └── src
│ ├── bsd.c
│ ├── context.c
│ ├── crypto
│ ├── openssl.c
│ ├── sni_tree.cpp
│ └── wolfssl.c
│ ├── eventing
│ ├── asio.cpp
│ ├── epoll_kqueue.c
│ ├── gcd.c
│ └── libuv.c
│ ├── internal
│ ├── eventing
│ │ ├── asio.h
│ │ ├── epoll_kqueue.h
│ │ ├── gcd.h
│ │ └── libuv.h
│ ├── internal.h
│ ├── loop_data.h
│ └── networking
│ │ └── bsd.h
│ ├── libusockets.h
│ ├── loop.c
│ └── socket.c
├── nginx
├── config
│ ├── auth.conf
│ ├── game-beta.conf
│ ├── game-prod.conf
│ └── status.conf
├── docker-compose.yml
├── nginx-certbot.env
├── rolly_cubes_live
└── rolly_cubes_live_auth
├── package-lock.json
├── package.json
├── random
├── .gitignore
├── Dockerfile
├── LICENSES
├── Makefile
├── README.md
├── docker-build.sh
├── docker-compose.yml
├── docker-run.sh
└── src
│ ├── DemoClient.f90
│ ├── Random.f90
│ ├── RandomServer.f90
│ └── mod_dill.f90
├── schema.json
├── scripts
├── codespace-setup
├── create-dev-keys
├── db_backup
├── gen-types
└── setup-nix
├── ssh-gateway
├── .gitignore
├── Dockerfile
├── TODO.txt
├── api
│ ├── api_methods.go
│ └── generated.go
├── docker-compose.yml
├── go.mod
├── go.sum
├── main.go
└── user
│ ├── context.go
│ └── user.go
├── stats
└── simulation.js
├── system.dot
└── tasks.toml
/.clang-format:
--------------------------------------------------------------------------------
1 | ---
2 | AllowShortIfStatementsOnASingleLine: 'true'
3 | ColumnLimit: '0'
4 | IndentCaseLabels: 'false'
5 | IndentWidth: '4'
6 | NamespaceIndentation: All
7 |
8 | ...
9 |
--------------------------------------------------------------------------------
/.devcontainer/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM mcr.microsoft.com/devcontainers/base:ubuntu
2 |
3 | # install stuff
4 | RUN apt update && \
5 | apt install -y manpages-dev software-properties-common inotify-tools netcat
6 |
7 | RUN apt install curl -y
8 | RUN curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- install linux \
9 | --extra-conf "sandbox = false" \
10 | --init none \
11 | --no-confirm
12 | ENV PATH="${PATH}:/nix/var/nix/profiles/default/bin"
13 |
--------------------------------------------------------------------------------
/.devcontainer/devcontainer.json:
--------------------------------------------------------------------------------
1 | {
2 | "dockerComposeFile": "docker-compose.yml",
3 | "service": "devcontainer",
4 | "workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}",
5 | "updateContentCommand": "bash -c '/workspaces/${localWorkspaceFolderBasename}/scripts/codespace-setup 2>&1 | tee /workspaces/${localWorkspaceFolderBasename}/.setup-log.log'",
6 | "postCreateCommand": "bash -c '/workspaces/${localWorkspaceFolderBasename}/scripts/create-dev-keys &>> /workspaces/${localWorkspaceFolderBasename}/.setup-log.log'; git pull origin main --rebase",
7 | "forwardPorts": [3000]
8 | }
9 |
--------------------------------------------------------------------------------
/.devcontainer/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '3.8'
2 | services:
3 | devcontainer:
4 | build:
5 | context: .
6 | dockerfile: Dockerfile
7 | volumes:
8 | - ../..:/workspaces:cached
9 | network_mode: service:db
10 | command: sleep infinity
11 |
12 | db:
13 | image: postgres:latest
14 | restart: unless-stopped
15 | ports:
16 | - '5432:5432'
17 | volumes:
18 | - postgres-data:/var/lib/postgresql/data
19 | environment:
20 | POSTGRES_PASSWORD: test
21 | POSTGRES_USER: test
22 | POSTGRES_DB: test
23 |
24 | volumes:
25 | postgres-data:
26 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | game/uWebSockets/** linguist-vendored
2 | game/includes/** linguist-vendored
3 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: cgsdev0
2 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: bug
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Screenshots**
24 | If applicable, add screenshots to help explain your problem.
25 |
26 | **Desktop (please complete the following information):**
27 | - OS: [e.g. iOS]
28 | - Browser [e.g. chrome, safari]
29 | - Version [e.g. 22]
30 |
31 | **Smartphone (please complete the following information):**
32 | - Device: [e.g. iPhone6]
33 | - OS: [e.g. iOS8.1]
34 | - Browser [e.g. stock browser, safari]
35 | - Version [e.g. 22]
36 |
37 | **Additional context**
38 | Add any other context about the problem here.
39 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: enhancement
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/.github/codeql/config.yml:
--------------------------------------------------------------------------------
1 | name: "CodeQL config"
2 |
3 | paths-ignore:
4 | - game/includes
5 | - game/uWebSockets
6 | - game/api
7 | - game/prometheus-cpp
8 |
--------------------------------------------------------------------------------
/.github/workflows/codeql.yml:
--------------------------------------------------------------------------------
1 | name: "CodeQL"
2 |
3 | on:
4 | workflow_dispatch: {}
5 | # push:
6 | # branches: [ 'main' ]
7 | # pull_request:
8 | # The branches below must be a subset of the branches above
9 | # branches: [ 'main' ]
10 | # schedule:
11 | # - cron: '44 14 * * 3'
12 |
13 | jobs:
14 | analyze:
15 | name: Analyze
16 | runs-on: ubuntu-latest
17 | permissions:
18 | actions: read
19 | contents: read
20 | security-events: write
21 |
22 | strategy:
23 | fail-fast: false
24 | matrix:
25 | language: [ 'cpp', 'javascript' ]
26 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
27 | # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
28 |
29 | steps:
30 | - name: Checkout repository
31 | uses: actions/checkout@v3
32 | with:
33 | submodules: recursive
34 |
35 | # Initializes the CodeQL tools for scanning.
36 | - name: Initialize CodeQL
37 | uses: github/codeql-action/init@v2
38 | with:
39 | languages: ${{ matrix.language }}
40 | # If you wish to specify custom queries, you can do so here or in a config file.
41 | # By default, queries listed here will override any specified in a config file.
42 | # Prefix the list here with "+" to use these queries and those in the config file.
43 |
44 | # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
45 | queries: +security-and-quality
46 | - run: scripts/setup-dev-env
47 | - name: Build
48 | working-directory: game
49 | run: make CXX=g++-10 CC=gcc-10 -j$(nproc)
50 |
51 | - name: Perform CodeQL Analysis
52 | uses: github/codeql-action/analyze@v2
53 | with:
54 | category: "/language:${{matrix.language}}"
55 | config-file: ./.github/codeql/config.yml
56 |
--------------------------------------------------------------------------------
/.github/workflows/deploy-auth-server.yml:
--------------------------------------------------------------------------------
1 | name: Deploy Auth Server
2 |
3 | on:
4 | workflow_dispatch
5 |
6 | env:
7 | REGISTRY: ghcr.io
8 | CHANNEL: latest
9 | IMAGE_NAME: ${{ github.repository }}
10 |
11 | jobs:
12 | build:
13 | runs-on: ubuntu-latest
14 | environment:
15 | name: auth
16 | url: https://auth.rollycubes.com
17 | permissions:
18 | contents: read
19 | packages: write
20 |
21 | steps:
22 | - uses: actions/checkout@v3
23 | - uses: docker/setup-buildx-action@v2
24 | - name: Log in to the Container registry
25 | uses: docker/login-action@v2
26 | with:
27 | registry: ${{ env.REGISTRY }}
28 | username: ${{ github.actor }}
29 | password: ${{ secrets.GITHUB_TOKEN }}
30 |
31 | - run: cp schema.json auth/schema.json
32 | - name: Build and push Docker image
33 | uses: docker/build-push-action@v4
34 | with:
35 | context: auth
36 | push: true
37 | tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-auth:${{ env.CHANNEL }}
38 | cache-from: type=gha
39 | cache-to: type=gha,mode=max
40 |
41 |
42 | - name: Install SSH key
43 | uses: shimataro/ssh-key-action@v2
44 | with:
45 | key: ${{ secrets.DO_SSH_KEY }}
46 | known_hosts: ${{ secrets.DO_KNOWN_HOSTS }}
47 |
48 | - run: "ssh -p 22222 root@${{ secrets.DO_SERVER_IP }} mkdir -p /root/auth"
49 | - run: "scp -P 22222 auth/docker-compose.yml root@${{ secrets.DO_SERVER_IP }}:/root/auth/."
50 | - run: "ssh -p 22222 root@${{ secrets.DO_SERVER_IP }} bash -c \"'docker-compose -f auth/docker-compose.yml pull && docker-compose -f auth/docker-compose.yml --env-file secrets/.env up -d'\""
51 |
--------------------------------------------------------------------------------
/.github/workflows/deploy-nginx.yml:
--------------------------------------------------------------------------------
1 | name: Deploy Nginx
2 |
3 | on:
4 | workflow_dispatch
5 |
6 | jobs:
7 | build:
8 | runs-on: ubuntu-latest
9 | permissions:
10 | contents: read
11 | packages: write
12 |
13 | steps:
14 | - uses: actions/checkout@v2
15 |
16 | - name: Install SSH key
17 | uses: shimataro/ssh-key-action@v2
18 | with:
19 | key: ${{ secrets.DO_SSH_KEY }}
20 | known_hosts: ${{ secrets.DO_KNOWN_HOSTS }}
21 |
22 | - run: "ssh -p 22222 root@${{ secrets.DO_SERVER_IP }} bash -c \"'rm -rf /root/nginx; mkdir -p /root/nginx'\""
23 | - run: "scp -P 22222 -r nginx/* root@${{ secrets.DO_SERVER_IP }}:/root/nginx/."
24 | - run: "ssh -p 22222 root@${{ secrets.DO_SERVER_IP }} docker-compose -f nginx/docker-compose.yml up -d"
25 |
--------------------------------------------------------------------------------
/.github/workflows/deploy-random.yml:
--------------------------------------------------------------------------------
1 | name: Deploy Random
2 |
3 | on:
4 | workflow_dispatch:
5 |
6 | defaults:
7 | run:
8 | shell: bash
9 |
10 | env:
11 | REGISTRY: ghcr.io
12 | IMAGE_NAME: ${{ github.repository }}-random
13 |
14 | jobs:
15 | build:
16 | runs-on: ubuntu-latest
17 | permissions:
18 | contents: read
19 | packages: write
20 |
21 | steps:
22 | - uses: actions/checkout@v2
23 |
24 | - name: Log in to the Container registry
25 | uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9
26 | with:
27 | registry: ${{ env.REGISTRY }}
28 | username: ${{ github.actor }}
29 | password: ${{ secrets.GITHUB_TOKEN }}
30 |
31 | - name: Build and push Docker image
32 | uses: docker/build-push-action@ad44023a93711e3deb337508980b4b5e9bcdc5dc
33 | with:
34 | context: random
35 | push: true
36 | tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
37 |
38 | # Deploy it
39 | - name: Install SSH key
40 | uses: shimataro/ssh-key-action@v2
41 | with:
42 | key: ${{ secrets.DO_SSH_KEY }}
43 | known_hosts: ${{ secrets.DO_KNOWN_HOSTS }}
44 |
45 | - name: copy the random server
46 | run: "scp -P 22222 random/docker-compose.yml root@${{ secrets.DO_SERVER_IP }}:/root/random"
47 | - name: start the new server
48 | run: "ssh -p 22222 root@${{ secrets.DO_SERVER_IP }} bash -c \"'docker-compose -f random/docker-compose.yml pull && docker-compose -f random/docker-compose.yml up -d'\""
49 |
--------------------------------------------------------------------------------
/.github/workflows/deploy-ssh-gateway.yml:
--------------------------------------------------------------------------------
1 | name: Deploy SSH Gateway
2 |
3 | on:
4 | workflow_dispatch
5 |
6 | env:
7 | REGISTRY: ghcr.io
8 | CHANNEL: latest
9 | IMAGE_NAME: ${{ github.repository }}
10 |
11 | jobs:
12 | build:
13 | runs-on: ubuntu-latest
14 | permissions:
15 | contents: read
16 | packages: write
17 |
18 | steps:
19 | - uses: actions/checkout@v3
20 | - uses: docker/setup-buildx-action@v2
21 | - name: Log in to the Container registry
22 | uses: docker/login-action@v2
23 | with:
24 | registry: ${{ env.REGISTRY }}
25 | username: ${{ github.actor }}
26 | password: ${{ secrets.GITHUB_TOKEN }}
27 |
28 | - name: Build and push Docker image
29 | uses: docker/build-push-action@v4
30 | with:
31 | context: ssh-gateway
32 | push: true
33 | tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-ssh-gateway:${{ env.CHANNEL }}
34 | cache-from: type=gha
35 | cache-to: type=gha,mode=max
36 |
37 | - name: Install SSH key
38 | uses: shimataro/ssh-key-action@v2
39 | with:
40 | key: ${{ secrets.DO_SSH_KEY }}
41 | known_hosts: ${{ secrets.DO_KNOWN_HOSTS }}
42 |
43 | - run: "ssh -p 22222 root@${{ secrets.DO_SERVER_IP }} mkdir -p /root/ssh-gateway"
44 | - run: "scp -P 22222 ssh-gateway/docker-compose.yml root@${{ secrets.DO_SERVER_IP }}:/root/ssh-gateway/."
45 | - run: "ssh -p 22222 root@${{ secrets.DO_SERVER_IP }} bash -c \"'docker-compose -f ssh-gateway/docker-compose.yml pull && docker-compose -f ssh-gateway/docker-compose.yml --env-file secrets/.env up -d'\""
46 |
--------------------------------------------------------------------------------
/.github/workflows/graphviz.yml:
--------------------------------------------------------------------------------
1 | # This is a basic workflow to help you get started with Actions
2 |
3 | name: Build Graphviz
4 |
5 | # Controls when the workflow will run
6 | on:
7 | push:
8 | branches:
9 | - 'main'
10 | paths:
11 | - 'system.dot'
12 |
13 | # Allows you to run this workflow manually from the Actions tab
14 | workflow_dispatch:
15 |
16 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel
17 | jobs:
18 | # This workflow contains a single job called "build"
19 | build:
20 | # The type of runner that the job will run on
21 | runs-on: ubuntu-latest
22 |
23 | # Steps represent a sequence of tasks that will be executed as part of the job
24 | steps:
25 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
26 | - uses: actions/checkout@v3
27 | - run: mkdir -p docs
28 | - run: cat system.dot | docker run --rm -i nshine/dot > docs/system-graph.png
29 | - uses: stefanzweifel/git-auto-commit-action@v4
30 | with:
31 | commit_message: Generate diagrams (graphviz)
32 |
--------------------------------------------------------------------------------
/.github/workflows/upload-assets.yml:
--------------------------------------------------------------------------------
1 | name: Upload static assets
2 | on:
3 | push:
4 | branches:
5 | - main
6 | paths:
7 | - 'assets/'
8 | workflow_dispatch: {}
9 |
10 | jobs:
11 | upload:
12 | runs-on: ubuntu-latest
13 | steps:
14 | - name: Checkout Repository
15 | uses: actions/checkout@master
16 | - uses: BetaHuhn/do-spaces-action@v2
17 | with:
18 | access_key: ${{ secrets.SPACES_ACCESS_KEY}}
19 | secret_key: ${{ secrets.SPACES_SECRET_KEY }}
20 | space_name: "cdn.rollycubes.com"
21 | space_region: "sfo3"
22 | cdn_domain: "cdn.rollycubes.com"
23 | source: assets
24 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | GameServer
2 | *.o
3 | *.a
4 | terminal/env
5 | *.pyc
6 | node_modules
7 | server_state.json
8 | .ccls-cache/
9 | gcm.cache/
10 | *.d
11 | **/.pre-shared-key
12 | auth/.twitch_secrets
13 |
14 | .setup-log.log
15 |
16 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "game/prometheus-cpp"]
2 | path = game/prometheus-cpp
3 | url = https://github.com/jupp0r/prometheus-cpp.git
4 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "files.associations": {
3 | "array": "cpp",
4 | "atomic": "cpp",
5 | "bit": "cpp",
6 | "*.tcc": "cpp",
7 | "bitset": "cpp",
8 | "cctype": "cpp",
9 | "chrono": "cpp",
10 | "clocale": "cpp",
11 | "cmath": "cpp",
12 | "codecvt": "cpp",
13 | "condition_variable": "cpp",
14 | "cstdarg": "cpp",
15 | "cstddef": "cpp",
16 | "cstdint": "cpp",
17 | "cstdio": "cpp",
18 | "cstdlib": "cpp",
19 | "cstring": "cpp",
20 | "ctime": "cpp",
21 | "cwchar": "cpp",
22 | "cwctype": "cpp",
23 | "deque": "cpp",
24 | "forward_list": "cpp",
25 | "list": "cpp",
26 | "map": "cpp",
27 | "set": "cpp",
28 | "unordered_map": "cpp",
29 | "unordered_set": "cpp",
30 | "vector": "cpp",
31 | "exception": "cpp",
32 | "algorithm": "cpp",
33 | "functional": "cpp",
34 | "iterator": "cpp",
35 | "memory": "cpp",
36 | "memory_resource": "cpp",
37 | "numeric": "cpp",
38 | "optional": "cpp",
39 | "random": "cpp",
40 | "ratio": "cpp",
41 | "regex": "cpp",
42 | "string": "cpp",
43 | "string_view": "cpp",
44 | "system_error": "cpp",
45 | "tuple": "cpp",
46 | "type_traits": "cpp",
47 | "utility": "cpp",
48 | "fstream": "cpp",
49 | "initializer_list": "cpp",
50 | "iomanip": "cpp",
51 | "iosfwd": "cpp",
52 | "iostream": "cpp",
53 | "istream": "cpp",
54 | "limits": "cpp",
55 | "mutex": "cpp",
56 | "new": "cpp",
57 | "ostream": "cpp",
58 | "shared_mutex": "cpp",
59 | "sstream": "cpp",
60 | "stdexcept": "cpp",
61 | "streambuf": "cpp",
62 | "thread": "cpp",
63 | "cinttypes": "cpp",
64 | "typeinfo": "cpp",
65 | "valarray": "cpp"
66 | }
67 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Sarah Schulte
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
Rolly Cubes
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | Rolling the dice since 2019.
14 | A game of luck and... well, actually, it's mostly luck.
15 |
16 |
17 |
18 | Check out this project in production or in beta,
19 | and follow development on Twitch.tv!
20 |
21 |
22 |
23 | Want to contribute?
24 |
25 | [Developer Documentation]
26 |
27 |
--------------------------------------------------------------------------------
/assets/achievements/3x_doubles.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cgsdev0/rollycubes/4106af80fcc07634c20fd8c8851c1260d933d9f9/assets/achievements/3x_doubles.png
--------------------------------------------------------------------------------
/assets/achievements/5x_doubles.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cgsdev0/rollycubes/4106af80fcc07634c20fd8c8851c1260d933d9f9/assets/achievements/5x_doubles.png
--------------------------------------------------------------------------------
/assets/achievements/7x_doubles.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cgsdev0/rollycubes/4106af80fcc07634c20fd8c8851c1260d933d9f9/assets/achievements/7x_doubles.png
--------------------------------------------------------------------------------
/assets/achievements/addicted.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cgsdev0/rollycubes/4106af80fcc07634c20fd8c8851c1260d933d9f9/assets/achievements/addicted.png
--------------------------------------------------------------------------------
/assets/achievements/astronaut.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cgsdev0/rollycubes/4106af80fcc07634c20fd8c8851c1260d933d9f9/assets/achievements/astronaut.png
--------------------------------------------------------------------------------
/assets/achievements/beginners_luck.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cgsdev0/rollycubes/4106af80fcc07634c20fd8c8851c1260d933d9f9/assets/achievements/beginners_luck.png
--------------------------------------------------------------------------------
/assets/achievements/getting_started.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cgsdev0/rollycubes/4106af80fcc07634c20fd8c8851c1260d933d9f9/assets/achievements/getting_started.png
--------------------------------------------------------------------------------
/assets/achievements/good_job.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cgsdev0/rollycubes/4106af80fcc07634c20fd8c8851c1260d933d9f9/assets/achievements/good_job.png
--------------------------------------------------------------------------------
/assets/achievements/negative.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cgsdev0/rollycubes/4106af80fcc07634c20fd8c8851c1260d933d9f9/assets/achievements/negative.png
--------------------------------------------------------------------------------
/assets/achievements/oops.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cgsdev0/rollycubes/4106af80fcc07634c20fd8c8851c1260d933d9f9/assets/achievements/oops.png
--------------------------------------------------------------------------------
/assets/achievements/perfect.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cgsdev0/rollycubes/4106af80fcc07634c20fd8c8851c1260d933d9f9/assets/achievements/perfect.png
--------------------------------------------------------------------------------
/assets/achievements/rude.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cgsdev0/rollycubes/4106af80fcc07634c20fd8c8851c1260d933d9f9/assets/achievements/rude.png
--------------------------------------------------------------------------------
/assets/achievements/thief.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cgsdev0/rollycubes/4106af80fcc07634c20fd8c8851c1260d933d9f9/assets/achievements/thief.png
--------------------------------------------------------------------------------
/assets/badges/admin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cgsdev0/rollycubes/4106af80fcc07634c20fd8c8851c1260d933d9f9/assets/badges/admin.png
--------------------------------------------------------------------------------
/assets/badges/premium.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cgsdev0/rollycubes/4106af80fcc07634c20fd8c8851c1260d933d9f9/assets/badges/premium.gif
--------------------------------------------------------------------------------
/auth/.cargo/config.toml:
--------------------------------------------------------------------------------
1 | [registries.crates-io]
2 | protocol = "sparse"
3 |
--------------------------------------------------------------------------------
/auth/.gitignore:
--------------------------------------------------------------------------------
1 | target/
2 | secrets/
3 | db_backup
4 |
--------------------------------------------------------------------------------
/auth/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "auth"
3 | version = "0.1.0"
4 | edition = "2021"
5 |
6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
7 |
8 | [dependencies]
9 | base64 = "0.21.2"
10 | ring = "0.16.20"
11 | hyper = "0.14.26"
12 | uuid = { version = "1.3.1", features = [ "serde", "v4" ] }
13 | tokio = { version = "1", features = ["full"] }
14 | tokio-postgres = { version = "0.7.8", features = [ "with-uuid-1", "with-chrono-0_4" ]}
15 | chrono = { version = "0.4.28", features = [ "serde", "std", "clock" ] }
16 | refinery = { version = "0.8", features = ["tokio-postgres"]}
17 | axum = { version = "0.6.16", features=[ "macros" ] }
18 | tracing = "0.1"
19 | tracing-subscriber = { version = "0.3", features = ["env-filter"] }
20 | serde = { version = "1.0.105", features = ["derive"] }
21 | serde_repr = "0.1"
22 | axum-extra = { version = "0.7.4", features = ["cookie"] }
23 | serde_json = "1.0.105"
24 | reqwest = { version = "0.11", features = ["json"] }
25 | openssl-sys = { version = "0.9.86", features = [ "vendored" ] }
26 | twitch_oauth2 = {version = "0.10", features = [ "reqwest" ] }
27 | twitch_api = {version="0.7.0-rc.4", features=["helix", "reqwest", "twitch_oauth2", "client"] }
28 | bb8-postgres = { version = "0.8.1", features = [ "with-uuid-1", "with-chrono-0_4" ]}
29 | bb8 = "0.8.0"
30 | jsonwebtoken = "8.3.0"
31 | tower-http = { version = "0.4.0", features = [ "cors", "catch-panic" ] }
32 | lazy_static = "1.4.0"
33 | tracing-log = "0.1.3"
34 | thiserror = "1.0"
35 | anyhow = "1.0"
36 | int-enum = "0.5"
37 | openssh-keys = "0.6.2"
38 |
39 | [build-dependencies]
40 | prettyplease = "0.2"
41 | schemars = "0.8"
42 | serde_json = "1.0.105"
43 | syn = "2.0.29"
44 | typify = "0.0.13"
45 |
--------------------------------------------------------------------------------
/auth/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM lukemathwalker/cargo-chef:latest-rust-alpine as planner
2 | RUN apk add musl-dev pkgconfig make perl
3 |
4 | WORKDIR /usr/build
5 |
6 | COPY . .
7 | RUN cargo chef prepare --recipe-path recipe.json
8 |
9 | FROM lukemathwalker/cargo-chef:latest-rust-alpine as builder
10 | RUN apk add musl-dev pkgconfig make perl
11 |
12 | WORKDIR /usr/build
13 |
14 | ENV SCHEMA_PATH .
15 | COPY --from=planner /usr/build/recipe.json recipe.json
16 | RUN cargo chef cook --recipe-path recipe.json --release
17 |
18 | COPY . .
19 | RUN cargo build --release
20 |
21 | FROM alpine:edge as prod
22 |
23 | COPY --from=builder /usr/build/target/release/auth .
24 |
25 | ENTRYPOINT ["./auth"]
26 |
--------------------------------------------------------------------------------
/auth/badges.json:
--------------------------------------------------------------------------------
1 | {
2 | "premium": {
3 | "id": "premium",
4 | "name": "Premium",
5 | "description": "This user has upgraded to premium.",
6 | "image_url": "https://cdn.rollycubes.com/badges/premium.gif"
7 | },
8 | "admin": {
9 | "id": "admin",
10 | "name": "Admin",
11 | "description": "This user helps run the website.",
12 | "image_url": "https://cdn.rollycubes.com/badges/admin.png"
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/auth/build.rs:
--------------------------------------------------------------------------------
1 | // Copyright 2023 Oxide Computer Company
2 |
3 | use std::{env, fs, path::Path};
4 |
5 | use typify::{TypeSpace, TypeSpaceImpl, TypeSpaceSettings};
6 |
7 | fn main() {
8 | println!("cargo:rerun-if-changed=../schema.json");
9 | let mut ofile = Path::new(&env::var("SCHEMA_PATH").map_or_else(|_| "..".to_string(), |ok| ok))
10 | .to_path_buf();
11 | ofile.push("schema.json");
12 | let content = std::fs::read_to_string(ofile).unwrap();
13 | let schema = serde_json::from_str::(&content).unwrap();
14 |
15 | let mut type_space = TypeSpace::new(
16 | TypeSpaceSettings::default()
17 | .with_struct_builder(true)
18 | .with_conversion(
19 | schemars::schema::SchemaObject {
20 | instance_type: Some(schemars::schema::InstanceType::String.into()),
21 | format: Some("date".to_string()),
22 | ..Default::default()
23 | },
24 | "chrono::DateTime",
25 | [TypeSpaceImpl::Display].into_iter(),
26 | ),
27 | );
28 | type_space.add_root_schema(schema).unwrap();
29 |
30 | let contents = format!(
31 | "{}\n{}",
32 | "use serde::{Deserialize, Serialize};",
33 | prettyplease::unparse(&syn::parse2::(type_space.to_stream()).unwrap())
34 | );
35 |
36 | let mut out_file = Path::new(&env::var("OUT_DIR").unwrap()).to_path_buf();
37 | out_file.push("generated.rs");
38 | fs::write(out_file, contents).unwrap();
39 | }
40 |
--------------------------------------------------------------------------------
/auth/devdb/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '3'
2 | services:
3 | postgres:
4 | image: "postgres:9.6.1"
5 | ports:
6 | - '5432:5432'
7 | environment:
8 | POSTGRES_USER: "test"
9 | POSTGRES_PASSWORD: "test"
10 | POSTGRES_DB: "test"
11 |
12 | volumes:
13 | - postgres-data-auth:/var/lib/postgresql/data
14 |
15 | volumes:
16 | postgres-data-auth:
17 |
--------------------------------------------------------------------------------
/auth/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '3'
2 | services:
3 | auth:
4 | image: ghcr.io/cgsdev0/rollycubes-auth
5 | restart: always
6 | environment:
7 | DB_HOST: "db"
8 | RUST_LOG: "debug"
9 | depends_on:
10 | - "postgres"
11 | links:
12 | - "postgres:db"
13 | volumes:
14 | - ../secrets/:/secrets
15 | postgres:
16 | image: "postgres:9.6.1"
17 | restart: always
18 | environment:
19 | POSTGRES_USER: "test"
20 | POSTGRES_PASSWORD: "test"
21 | POSTGRES_DB: "test"
22 |
23 | volumes:
24 | - postgres-data-auth:/var/lib/postgresql/data
25 |
26 | networks:
27 | default:
28 | name: rollycubes
29 | external: true
30 |
31 | volumes:
32 | postgres-data-auth:
33 |
--------------------------------------------------------------------------------
/auth/migrations/V10__donors.sql:
--------------------------------------------------------------------------------
1 | ALTER TABLE users
2 | ADD COLUMN donor boolean DEFAULT false NOT NULL;
3 |
--------------------------------------------------------------------------------
/auth/migrations/V11__payments.sql:
--------------------------------------------------------------------------------
1 | CREATE TABLE IF NOT EXISTS payment (
2 | user_id uuid NOT NULL,
3 | payment_id character varying NOT NULL
4 | );
5 |
6 | ALTER TABLE ONLY payment
7 | ADD CONSTRAINT "FK_user_to_payment" FOREIGN KEY (user_id) REFERENCES users(id);
8 |
--------------------------------------------------------------------------------
/auth/migrations/V12__pubkeys.sql:
--------------------------------------------------------------------------------
1 | CREATE TABLE IF NOT EXISTS pubkey_to_user (
2 | pubkey character varying NOT NULL PRIMARY KEY,
3 | user_id uuid NOT NULL
4 | );
5 |
6 | ALTER TABLE ONLY pubkey_to_user DROP CONSTRAINT IF EXISTS "FK_pubkey_to_userid";
7 | ALTER TABLE ONLY pubkey_to_user
8 | ADD CONSTRAINT "FK_pubkey_to_userid" FOREIGN KEY (user_id) REFERENCES users(id);
9 |
--------------------------------------------------------------------------------
/auth/migrations/V13__pubkey_settings.sql:
--------------------------------------------------------------------------------
1 | ALTER TABLE user_settings
2 | ADD COLUMN pubkey_text TEXT DEFAULT '' NOT NULL;
3 |
--------------------------------------------------------------------------------
/auth/migrations/V14__dice_type_strings.sql:
--------------------------------------------------------------------------------
1 | BEGIN;
2 | ALTER TABLE user_settings ADD COLUMN dice_type_new VARCHAR(20) DEFAULT 'Default';
3 | UPDATE user_settings SET dice_type_new = CASE WHEN dice_type = 0 THEN 'Default' WHEN dice_type = 1 THEN 'D20' WHEN dice_type = 2 THEN 'Golden' END;
4 | ALTER TABLE user_settings DROP COLUMN dice_type;
5 | ALTER TABLE user_settings RENAME COLUMN dice_type_new TO dice_type;
6 | COMMIT;
7 |
--------------------------------------------------------------------------------
/auth/migrations/V15__histogram_tracking.sql:
--------------------------------------------------------------------------------
1 | ALTER TABLE player_stats
2 | ADD COLUMN dice_values integer[6] DEFAULT '{0,0,0,0,0,0}' NOT NULL,
3 | ADD COLUMN roll_totals integer[12] DEFAULT '{0,0,0,0,0,0,0,0,0,0,0,0}' NOT NULL;
4 |
--------------------------------------------------------------------------------
/auth/migrations/V16__bigger_ints.sql:
--------------------------------------------------------------------------------
1 | ALTER TABLE player_stats
2 | ALTER COLUMN dice_values TYPE bigint[6],
3 | ALTER COLUMN dice_values SET DEFAULT '{0,0,0,0,0,0}',
4 | ALTER COLUMN rolls TYPE bigint,
5 | ALTER COLUMN doubles TYPE bigint,
6 | ALTER COLUMN wins TYPE bigint,
7 | ALTER COLUMN games TYPE bigint,
8 | ALTER COLUMN roll_totals TYPE bigint[12],
9 | ALTER COLUMN roll_totals SET DEFAULT '{0,0,0,0,0,0,0,0,0,0,0,0}';
10 |
--------------------------------------------------------------------------------
/auth/migrations/V17__winner_histograms.sql:
--------------------------------------------------------------------------------
1 | ALTER TABLE player_stats
2 | ADD COLUMN winning_scores bigint[6] DEFAULT '{0,0,0,0,0,0}' NOT NULL;
3 |
--------------------------------------------------------------------------------
/auth/migrations/V2__drop_passwords.sql:
--------------------------------------------------------------------------------
1 | ALTER TABLE "user"
2 | DROP COLUMN IF EXISTS hashed_password;
3 |
--------------------------------------------------------------------------------
/auth/migrations/V3__snake_case_everything.sql:
--------------------------------------------------------------------------------
1 | ALTER TABLE player_stats RENAME COLUMN "userId" TO user_id;
2 | ALTER TABLE refresh_token RENAME COLUMN "userId" TO user_id;
3 | ALTER TABLE twitch_identity RENAME COLUMN "userId" TO user_id;
4 | ALTER TABLE user_to_achievement RENAME COLUMN "userId" TO user_id;
5 | ALTER TABLE user_to_achievement RENAME COLUMN "achievementId" TO achievement_id;
6 | ALTER TABLE public."user" RENAME TO users;
7 | ALTER TABLE users RENAME COLUMN "createdDate" TO created_date;
8 |
--------------------------------------------------------------------------------
/auth/migrations/V4__drop_achievements.sql:
--------------------------------------------------------------------------------
1 | ALTER TABLE ONLY user_to_achievement DROP CONSTRAINT IF EXISTS "FK_02102504ee06fb44948906ec7d6";
2 | DROP TABLE IF EXISTS achievement;
3 |
--------------------------------------------------------------------------------
/auth/migrations/V5__default_username.sql:
--------------------------------------------------------------------------------
1 | ALTER TABLE users ALTER COLUMN username SET DEFAULT 'Guest';
2 |
--------------------------------------------------------------------------------
/auth/migrations/V6__anon_identity.sql:
--------------------------------------------------------------------------------
1 | CREATE TABLE IF NOT EXISTS anon_identity (
2 | anon_id character varying NOT NULL PRIMARY KEY,
3 | user_id uuid UNIQUE
4 | );
5 |
6 | ALTER TABLE ONLY anon_identity
7 | ADD CONSTRAINT "FK_anon_identity_to_users" FOREIGN KEY (user_id) REFERENCES users(id);
8 |
--------------------------------------------------------------------------------
/auth/migrations/V7__cascades.sql:
--------------------------------------------------------------------------------
1 | ALTER TABLE ONLY twitch_identity DROP CONSTRAINT IF EXISTS "FK_096ac80126c38d3866baf1ba7a8";
2 | ALTER TABLE ONLY twitch_identity
3 | ADD CONSTRAINT "FK_096ac80126c38d3866baf1ba7a8" FOREIGN KEY (user_id) REFERENCES users(id)
4 | ON DELETE CASCADE;
5 |
6 | ALTER TABLE ONLY user_to_achievement DROP CONSTRAINT IF EXISTS "FK_5d9a7b213a82555acbeb6d62bbe";
7 | ALTER TABLE ONLY user_to_achievement
8 | ADD CONSTRAINT "FK_5d9a7b213a82555acbeb6d62bbe" FOREIGN KEY (user_id) REFERENCES users(id)
9 | ON DELETE CASCADE;
10 |
11 | ALTER TABLE ONLY refresh_token DROP CONSTRAINT IF EXISTS "FK_8e913e288156c133999341156ad";
12 | ALTER TABLE ONLY refresh_token
13 | ADD CONSTRAINT "FK_8e913e288156c133999341156ad" FOREIGN KEY (user_id) REFERENCES users(id)
14 | ON DELETE CASCADE;
15 |
16 | ALTER TABLE ONLY player_stats DROP CONSTRAINT IF EXISTS "FK_a14e90bda5a40cf0b150c6dc87f";
17 | ALTER TABLE ONLY player_stats
18 | ADD CONSTRAINT "FK_a14e90bda5a40cf0b150c6dc87f" FOREIGN KEY (user_id) REFERENCES users(id)
19 | ON DELETE CASCADE;
20 |
21 | ALTER TABLE ONLY anon_identity DROP CONSTRAINT IF EXISTS "FK_anon_identity_to_users";
22 | ALTER TABLE ONLY anon_identity
23 | ADD CONSTRAINT "FK_anon_identity_to_users" FOREIGN KEY (user_id) REFERENCES users(id)
24 | ON DELETE CASCADE;
25 |
--------------------------------------------------------------------------------
/auth/migrations/V8__settings.sql:
--------------------------------------------------------------------------------
1 | CREATE TABLE IF NOT EXISTS user_settings(
2 | user_id uuid NOT NULL,
3 | dice_type int DEFAULT 0,
4 | PRIMARY KEY(user_id),
5 | CONSTRAINT fk_user
6 | FOREIGN KEY(user_id)
7 | REFERENCES users(id)
8 | );
9 |
10 | INSERT INTO user_settings(user_id)
11 | (SELECT id AS user_id FROM users);
12 |
--------------------------------------------------------------------------------
/auth/migrations/V9__player_colors.sql:
--------------------------------------------------------------------------------
1 | ALTER TABLE user_settings
2 | ADD COLUMN color_hue double precision DEFAULT 0 NOT NULL,
3 | ADD COLUMN color_sat double precision DEFAULT 80 NOT NULL;
4 |
--------------------------------------------------------------------------------
/auth/src/generated.rs:
--------------------------------------------------------------------------------
1 | include!(concat!(env!("OUT_DIR"), "/generated.rs"));
2 |
--------------------------------------------------------------------------------
/auth/src/migrations.rs:
--------------------------------------------------------------------------------
1 | use anyhow::Error;
2 | use tokio_postgres::Client;
3 |
4 | mod embedded {
5 | use refinery::embed_migrations;
6 | embed_migrations!("migrations");
7 | }
8 |
9 | pub async fn run_migrations(client: &mut Client) -> std::result::Result<(), Error> {
10 | println!("Running DB migrations...");
11 | let migration_report = embedded::migrations::runner().run_async(client).await?;
12 |
13 | for migration in migration_report.applied_migrations() {
14 | println!(
15 | "Migration Applied - Name: {}, Version: {}",
16 | migration.name(),
17 | migration.version()
18 | );
19 | }
20 |
21 | println!("DB migrations finished!");
22 |
23 | Ok(())
24 | }
25 |
--------------------------------------------------------------------------------
/auth/tasks.toml:
--------------------------------------------------------------------------------
1 | [[task]]
2 | id = "run"
3 | type = "long"
4 | cmd = "cargo watch -x run"
5 | dependencies = [ "db" ]
6 |
7 | [[task]]
8 | id = "db"
9 | type = "short"
10 | cmd = "nc -zv localhost 5432 || (cd devdb; docker-compose up -d)"
11 |
--------------------------------------------------------------------------------
/client/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
--------------------------------------------------------------------------------
/client/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "trailingComma": "es5",
3 | "tabWidth": 2,
4 | "useTabs": false,
5 | "semi": true,
6 | "singleQuote": true
7 | }
8 |
--------------------------------------------------------------------------------
/client/functions/room/[room_id].ts:
--------------------------------------------------------------------------------
1 | export const onRequest: PagesFunction<{}> = async (context) => {
2 | const { request, env } = context;
3 |
4 | const res = await env.ASSETS.fetch(request);
5 | return new HTMLRewriter().on('meta', new MetaHandler()).transform(res);
6 | };
7 |
8 | class MetaHandler implements HTMLRewriterElementContentHandlers {
9 | element(element: Element): void | Promise {
10 | if (element.getAttribute('name') === 'description') {
11 | element.setAttribute('content', 'Join my rolly cubes session!');
12 | return;
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/client/functions/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "esnext",
4 | "module": "esnext",
5 | "lib": ["esnext"],
6 | "types": ["@cloudflare/workers-types"]
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/client/public/Icosphere.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cgsdev0/rollycubes/4106af80fcc07634c20fd8c8851c1260d933d9f9/client/public/Icosphere.png
--------------------------------------------------------------------------------
/client/public/crown.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cgsdev0/rollycubes/4106af80fcc07634c20fd8c8851c1260d933d9f9/client/public/crown.png
--------------------------------------------------------------------------------
/client/public/default_player.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cgsdev0/rollycubes/4106af80fcc07634c20fd8c8851c1260d933d9f9/client/public/default_player.png
--------------------------------------------------------------------------------
/client/public/dice.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cgsdev0/rollycubes/4106af80fcc07634c20fd8c8851c1260d933d9f9/client/public/dice.png
--------------------------------------------------------------------------------
/client/public/emissive.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cgsdev0/rollycubes/4106af80fcc07634c20fd8c8851c1260d933d9f9/client/public/emissive.png
--------------------------------------------------------------------------------
/client/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cgsdev0/rollycubes/4106af80fcc07634c20fd8c8851c1260d933d9f9/client/public/favicon.ico
--------------------------------------------------------------------------------
/client/public/gear.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cgsdev0/rollycubes/4106af80fcc07634c20fd8c8851c1260d933d9f9/client/public/gear.png
--------------------------------------------------------------------------------
/client/public/gold6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cgsdev0/rollycubes/4106af80fcc07634c20fd8c8851c1260d933d9f9/client/public/gold6.png
--------------------------------------------------------------------------------
/client/public/hands.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cgsdev0/rollycubes/4106af80fcc07634c20fd8c8851c1260d933d9f9/client/public/hands.png
--------------------------------------------------------------------------------
/client/public/itsboats.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cgsdev0/rollycubes/4106af80fcc07634c20fd8c8851c1260d933d9f9/client/public/itsboats.png
--------------------------------------------------------------------------------
/client/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cgsdev0/rollycubes/4106af80fcc07634c20fd8c8851c1260d933d9f9/client/public/logo192.png
--------------------------------------------------------------------------------
/client/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "Dice Game",
3 | "name": "The Dice Game",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | }
15 | ],
16 | "start_url": ".",
17 | "display": "standalone",
18 | "theme_color": "#000000",
19 | "background_color": "#ffffff"
20 | }
21 |
--------------------------------------------------------------------------------
/client/public/normal6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cgsdev0/rollycubes/4106af80fcc07634c20fd8c8851c1260d933d9f9/client/public/normal6.png
--------------------------------------------------------------------------------
/client/public/normal6_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cgsdev0/rollycubes/4106af80fcc07634c20fd8c8851c1260d933d9f9/client/public/normal6_2.png
--------------------------------------------------------------------------------
/client/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 |
--------------------------------------------------------------------------------
/client/public/skybox/room.hdr:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cgsdev0/rollycubes/4106af80fcc07634c20fd8c8851c1260d933d9f9/client/public/skybox/room.hdr
--------------------------------------------------------------------------------
/client/public/skybox/skybox_nx.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cgsdev0/rollycubes/4106af80fcc07634c20fd8c8851c1260d933d9f9/client/public/skybox/skybox_nx.jpg
--------------------------------------------------------------------------------
/client/public/skybox/skybox_ny.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cgsdev0/rollycubes/4106af80fcc07634c20fd8c8851c1260d933d9f9/client/public/skybox/skybox_ny.jpg
--------------------------------------------------------------------------------
/client/public/skybox/skybox_nz.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cgsdev0/rollycubes/4106af80fcc07634c20fd8c8851c1260d933d9f9/client/public/skybox/skybox_nz.jpg
--------------------------------------------------------------------------------
/client/public/skybox/skybox_px.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cgsdev0/rollycubes/4106af80fcc07634c20fd8c8851c1260d933d9f9/client/public/skybox/skybox_px.jpg
--------------------------------------------------------------------------------
/client/public/skybox/skybox_py.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cgsdev0/rollycubes/4106af80fcc07634c20fd8c8851c1260d933d9f9/client/public/skybox/skybox_py.jpg
--------------------------------------------------------------------------------
/client/public/skybox/skybox_pz.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cgsdev0/rollycubes/4106af80fcc07634c20fd8c8851c1260d933d9f9/client/public/skybox/skybox_pz.jpg
--------------------------------------------------------------------------------
/client/src/actions/settings.ts:
--------------------------------------------------------------------------------
1 | import { Dispatch } from '@reduxjs/toolkit';
2 | import { ReduxState } from 'store';
3 |
4 | export function cheatsAction() {
5 | return Object.assign(
6 | (dispatch: Dispatch, getState: () => ReduxState) => {
7 | const cheats = getState().settings.cheats;
8 |
9 | const action = { type: 'CHEATS' as 'CHEATS', newState: !cheats };
10 | dispatch(action);
11 | return action;
12 | },
13 | { type: 'CHEATS_THUNK' }
14 | );
15 | }
16 |
17 | export type CheatsAction = ReturnType>;
18 |
--------------------------------------------------------------------------------
/client/src/constants.ts:
--------------------------------------------------------------------------------
1 | export const TARGET_SCORES = [33, 66, 67, 98, 99, 100];
2 |
--------------------------------------------------------------------------------
/client/src/hooks/window_size.ts:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from 'react';
2 | // Hook
3 | export function useWindowSize() {
4 | // Initialize state with undefined width/height so server and client renders match
5 | // Learn more here: https://joshwcomeau.com/react/the-perils-of-rehydration/
6 | const [windowSize, setWindowSize] = useState<{
7 | width?: number;
8 | height?: number;
9 | }>({
10 | width: undefined,
11 | height: undefined,
12 | });
13 |
14 | useEffect(() => {
15 | // Handler to call on window resize
16 | function handleResize() {
17 | // Set window width/height to state
18 | const size = {
19 | width: Math.min(window.screen.width, window.innerWidth),
20 | height: Math.min(window.screen.height, window.innerHeight),
21 | };
22 | setWindowSize(size);
23 | }
24 | // Add event listener
25 | window.addEventListener('resize', handleResize);
26 | // Call handler right away so state gets updated with initial window size
27 | handleResize();
28 | // Remove event listener on cleanup
29 | return () => window.removeEventListener('resize', handleResize);
30 | }, []); // Empty array ensures that effect is only run on mount
31 | return windowSize;
32 | }
33 |
--------------------------------------------------------------------------------
/client/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
5 | sans-serif;
6 | -webkit-font-smoothing: antialiased;
7 | -moz-osx-font-smoothing: grayscale;
8 | }
9 |
10 | code {
11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
12 | monospace;
13 | }
14 |
--------------------------------------------------------------------------------
/client/src/index.tsx:
--------------------------------------------------------------------------------
1 | import 'regenerator-runtime';
2 | import App from './App';
3 |
4 | import { createRoot } from 'react-dom/client';
5 |
6 | const container = document.getElementById('root');
7 | const root = createRoot(container!);
8 | root.render();
9 |
--------------------------------------------------------------------------------
/client/src/pages/login.css:
--------------------------------------------------------------------------------
1 | div.loginForm {
2 | display: flex;
3 | flex-direction: column;
4 | }
5 |
6 | .loginForm input {
7 | margin-bottom: 10px;
8 | height: 24px;
9 | font-size: 16pt;
10 | }
11 | .loginContainer button {
12 | width: 100%;
13 | margin: 0px;
14 | margin-bottom: 12px;
15 | padding: 6px;
16 | }
17 |
18 |
--------------------------------------------------------------------------------
/client/src/pages/tab_error_page.tsx:
--------------------------------------------------------------------------------
1 | import { Link } from 'react-router-dom';
2 | import { css } from 'stitches.config';
3 |
4 | const centered = css({
5 | width: '100%',
6 | height: '100%',
7 | display: 'flex',
8 | justifyContent: 'center',
9 | alignItems: 'flex-end',
10 | });
11 |
12 | interface Props {
13 | error: string;
14 | }
15 | export const GenericErrorPage: React.FC = ({ error }) => {
16 | return (
17 |
18 |
Error
19 |
{error}
20 |
21 | Back to Home
22 |
23 |
24 | );
25 | };
26 |
--------------------------------------------------------------------------------
/client/src/providers/achievements.tsx:
--------------------------------------------------------------------------------
1 | import { useGetAchievementListQuery } from 'api/auth';
2 | import React from 'react';
3 | import { useDispatch } from 'react-redux';
4 |
5 | export const AchievementProvider: React.FC<{}> = ({ children }) => {
6 | const { isLoading, data, error } = useGetAchievementListQuery();
7 | const dispatch = useDispatch();
8 |
9 | const preloaded = React.useRef([]);
10 |
11 | React.useEffect(() => {
12 | if (isLoading) return;
13 | if (error) {
14 | console.error(error);
15 | } else if (data) {
16 | dispatch({ type: 'GOT_ACHIEVEMENTS', achievements: data });
17 | preloaded.current = Object.values(data)
18 | .map((a) => {
19 | if (!a.image_url) return null;
20 | const img = new Image();
21 | img.src = a.image_url;
22 | return img;
23 | })
24 | .filter(Boolean);
25 | }
26 | }, [isLoading, data, error, preloaded]);
27 |
28 | if (isLoading) return null;
29 | return <>{children}>;
30 | };
31 |
--------------------------------------------------------------------------------
/client/src/providers/auth.tsx:
--------------------------------------------------------------------------------
1 | import { useGetRefreshTokenQuery } from 'api/auth';
2 | import React from 'react';
3 | import { useDispatch, useSelector } from 'react-redux';
4 | import { ReduxState } from 'store';
5 |
6 | export const AuthProvider: React.FC<{}> = ({ children }) => {
7 | const { isLoading, data, error, refetch } = useGetRefreshTokenQuery();
8 | const dispatch = useDispatch();
9 | const loaded = useSelector(
10 | (state) => state.auth.authToken !== undefined
11 | );
12 |
13 | React.useEffect(() => {
14 | if (isLoading) return;
15 | if (error) {
16 | dispatch({ type: 'AUTHENTICATE', access_token: null });
17 | } else if (data) {
18 | dispatch({ type: 'AUTHENTICATE', access_token: data.access_token });
19 | // refetch the access token after 45 minutes
20 | const interval = setInterval(refetch, 1000 * 60 * 45);
21 | return () => clearInterval(interval);
22 | }
23 | }, [isLoading, data, error]);
24 |
25 | if (!loaded) return null;
26 | return <>{children}>;
27 | };
28 |
--------------------------------------------------------------------------------
/client/src/providers/badges.tsx:
--------------------------------------------------------------------------------
1 | import { useGetBadgeListQuery } from 'api/auth';
2 | import React from 'react';
3 | import { useDispatch } from 'react-redux';
4 |
5 | export const BadgeProvider: React.FC<{}> = ({ children }) => {
6 | const { isLoading, data, error } = useGetBadgeListQuery();
7 | const dispatch = useDispatch();
8 |
9 | const preloaded = React.useRef([]);
10 |
11 | React.useEffect(() => {
12 | if (isLoading) return;
13 | if (error) {
14 | console.error(error);
15 | } else if (data) {
16 | dispatch({ type: 'GOT_BADGES', badges: data });
17 | preloaded.current = Object.values(data)
18 | .map((a) => {
19 | if (!a.image_url) return null;
20 | const img = new Image();
21 | img.src = a.image_url;
22 | return img;
23 | })
24 | .filter(Boolean);
25 | }
26 | }, [isLoading, data, error, preloaded]);
27 |
28 | if (isLoading) return null;
29 | return <>{children}>;
30 | };
31 |
--------------------------------------------------------------------------------
/client/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | declare module 'react-horizontal-scrolling';
3 |
--------------------------------------------------------------------------------
/client/src/reducers/auth.ts:
--------------------------------------------------------------------------------
1 | import { createReducer } from '@reduxjs/toolkit';
2 | import { AchievementList, BadgeList } from 'api/auth';
3 | import decode from 'jwt-decode';
4 |
5 | interface TokenUserData {
6 | exp: number;
7 | user_id: string;
8 | display_name: string;
9 | }
10 |
11 | export interface AuthState {
12 | authToken?: string | null;
13 | userData?: TokenUserData;
14 | achievements?: AchievementList;
15 | badges?: BadgeList;
16 | }
17 |
18 | export interface AuthenticateAction {
19 | type: 'AUTHENTICATE';
20 | access_token: string;
21 | }
22 |
23 | export interface GotAchievementsAction {
24 | type: 'GOT_ACHIEVEMENTS';
25 | achievements: AchievementList;
26 | }
27 |
28 | export interface GotBadgesAction {
29 | type: 'GOT_BADGES';
30 | badges: BadgeList;
31 | }
32 |
33 | export const authReducer = createReducer({}, (builder) => {
34 | builder
35 | .addCase('GOT_ACHIEVEMENTS', (state, action: GotAchievementsAction) => {
36 | state.achievements = action.achievements;
37 | })
38 | .addCase('GOT_BADGES', (state, action: GotBadgesAction) => {
39 | state.badges = action.badges;
40 | })
41 | .addCase('AUTHENTICATE', (state, action: AuthenticateAction) => {
42 | try {
43 | const decoded = decode(action.access_token);
44 | state.authToken = action.access_token;
45 | state.userData = decoded;
46 | } catch (e) {
47 | state.authToken = null;
48 | state.userData = undefined;
49 | }
50 | })
51 | .addCase('LOGOUT', (state, action) => {
52 | state.authToken = null;
53 | state.userData = undefined;
54 | });
55 | });
56 |
--------------------------------------------------------------------------------
/client/src/reducers/chat.ts:
--------------------------------------------------------------------------------
1 | import { createReducer } from '@reduxjs/toolkit';
2 | import { CheatsAction } from 'actions/settings';
3 | import { RichTextMsg, WelcomeMsg } from '../types/api';
4 |
5 | export interface ChatState {
6 | chat: RichTextMsg[];
7 | }
8 | const CHAT_BUFFER_LENGTH = 200;
9 |
10 | export const chatReducer = createReducer({ chat: [] }, (builder) => {
11 | builder
12 | .addCase('welcome', (state, action: WelcomeMsg) => {
13 | state.chat = action.richChatLog;
14 | })
15 | .addCase('CHEATS', (state, action: CheatsAction) => {
16 | state.chat.unshift({
17 | type: 'chat_v2',
18 | msg: ['Hints ' + (action.newState ? 'enabled.' : 'disabled.')],
19 | });
20 | state.chat.length = Math.max(state.chat.length, CHAT_BUFFER_LENGTH);
21 | })
22 | .addCase('chat_v2', (state, action: RichTextMsg) => {
23 | state.chat.unshift(action);
24 | state.chat.length = Math.max(state.chat.length, CHAT_BUFFER_LENGTH);
25 | });
26 | });
27 |
--------------------------------------------------------------------------------
/client/src/reducers/connection.ts:
--------------------------------------------------------------------------------
1 | import { createReducer } from '@reduxjs/toolkit';
2 |
3 | export interface ConnectionState {
4 | socket?: WebSocket;
5 | connected: boolean;
6 | }
7 |
8 | export interface WebsocketAction {
9 | type: 'WEBSOCKET';
10 | socket?: WebSocket;
11 | }
12 |
13 | export const connectionReducer = createReducer(
14 | { connected: false },
15 | (builder) => {
16 | builder
17 | .addCase('WEBSOCKET', (state, action: WebsocketAction) => {
18 | state.socket = action.socket;
19 | })
20 | .addCase('socket_open', (state, action) => {
21 | state.connected = true;
22 | })
23 | .addCase('socket_close', (state, action) => {
24 | state.connected = false;
25 | });
26 | }
27 | );
28 |
--------------------------------------------------------------------------------
/client/src/reducers/pop_text.ts:
--------------------------------------------------------------------------------
1 | import { createReducer } from '@reduxjs/toolkit';
2 | import { UpdateMsg, UpdateTurnMsg } from '../types/api';
3 |
4 | export interface PopTextState {
5 | rollCount: number;
6 | reset: boolean;
7 | popText: Array<{ text: string; color: string; id: number }>;
8 | has69: boolean;
9 | popTextId: number;
10 | }
11 |
12 | export const popTextReducer = createReducer(
13 | {
14 | rollCount: 0,
15 | reset: false,
16 | has69: false,
17 | popText: [],
18 | popTextId: 0,
19 | },
20 | (builder) => {
21 | builder
22 | .addCase('DOUBLES', (state, action) => {
23 | state.popText.push({
24 | text: 'Doubles!',
25 | color: 'cyan',
26 | id: state.popTextId++,
27 | });
28 | })
29 | .addCase('win', (state, action) => {
30 | state.popText.push({
31 | text: '{winner} wins!',
32 | color: 'lime',
33 | id: state.popTextId++,
34 | });
35 | })
36 | .addCase('POP_NEXT', (state, action) => {
37 | state.popText.shift();
38 | })
39 | .addCase('update', (state, action: UpdateMsg) => {
40 | if (action.reset) {
41 | state.popText.push({
42 | text: 'Reset!',
43 | color: 'red',
44 | id: state.popTextId++,
45 | });
46 | } else {
47 | // ignore reset packets; they're irrelevant
48 | state.has69 = action.score === 69;
49 | }
50 | })
51 | .addCase('update_turn', (state, action: UpdateTurnMsg) => {
52 | if (state.has69) {
53 | state.popText.push({
54 | text: 'Nice.',
55 | color: 'yellow',
56 | id: state.popTextId++,
57 | });
58 | state.has69 = false;
59 | }
60 | })
61 | .addCase('roll', (state, action) => {
62 | state.rollCount++;
63 | });
64 | }
65 | );
66 |
--------------------------------------------------------------------------------
/client/src/reducers/themes.ts:
--------------------------------------------------------------------------------
1 | import { createReducer, isAllOf, isFulfilled } from '@reduxjs/toolkit';
2 | import { endpoints } from 'api/auth';
3 | import { createTheme } from 'stitches.config';
4 |
5 | export interface ThemeState {
6 | themes: Record;
7 | }
8 |
9 | export const themesReducer = createReducer(
10 | { themes: {} },
11 | (builder) => {
12 | builder.addCase('authApi/executeQuery/fulfilled', (state, action: any) => {
13 | if (endpoints.getUserById.matchFulfilled(action)) {
14 | if (action.payload.username !== 'badcop_') return;
15 | const { hue, sat } = action.payload.color;
16 | state.themes[action.payload.id] = createTheme({
17 | colors: {
18 | bad: '#ff0000',
19 | primary: `hsl(${hue}, ${sat}, 90%)`,
20 | primaryDimmed: `hsl(${hue}, ${sat}, 60%)`,
21 | brand: `hsl(${hue}, ${sat}, 50%)`,
22 | brandFaded: `hsl(${hue}, ${sat}, 40%)`,
23 | gray400: `hsl(${hue}, ${sat}, 40%)`,
24 | gray500: `hsl(${hue}, ${sat}, 35%)`,
25 | gray600: `hsl(${hue}, ${sat}, 30%)`,
26 | gray700: `hsl(${hue}, ${sat}, 25%)`,
27 | gray750: `hsl(${hue}, ${sat}, 15%)`,
28 | gray800: `hsl(${hue}, ${sat}, 20%)`,
29 | gray900: `hsl(${hue}, ${sat}, 12%)`,
30 | },
31 | });
32 | }
33 | });
34 | }
35 | );
36 |
--------------------------------------------------------------------------------
/client/src/textmods.css:
--------------------------------------------------------------------------------
1 | .-textmod-bold {
2 | font-weight: bold;
3 | }
4 | .-textmod-underline {
5 | text-decoration: underline;
6 | }
7 | .-textmod-italic {
8 | font-style: italic;
9 | }
10 | .-textmod-strikethrough {
11 | text-decoration: line-through;
12 | }
13 |
--------------------------------------------------------------------------------
/client/src/toastify.css:
--------------------------------------------------------------------------------
1 | #toastcontainer {
2 | --toastify-toast-width: 360px;
3 | --toastify-color-dark: var(--colors-gray700);
4 | --toastify-color-progress-dark: var(--colors-brand);
5 | }
6 |
--------------------------------------------------------------------------------
/client/src/ui/achievement.css:
--------------------------------------------------------------------------------
1 | .achievement {
2 | display: flex;
3 | align-items: center;
4 | }
5 |
6 | .achievement img {
7 | margin-right: 12px;
8 | }
9 |
10 | .achievement p {
11 | margin: 4px;
12 | }
13 |
14 | .achievement .header {
15 | color: #aaa;
16 | }
17 |
18 | .achievement .name {
19 | font-weight: bold;
20 | }
21 |
--------------------------------------------------------------------------------
/client/src/ui/achievement.tsx:
--------------------------------------------------------------------------------
1 | import { styled } from 'stitches.config';
2 | import { AchievementUnlock } from 'types/api';
3 |
4 | const AchievementDiv = styled('div', {
5 | display: 'flex',
6 | gap: 16,
7 | '& img': {
8 | borderRadius: 4,
9 | imageRendering: 'pixelated',
10 | },
11 | });
12 |
13 | export const Achievement = (props: AchievementUnlock) => {
14 | return (
15 |
16 |
22 |
23 |
Achievement Unlocked
24 |
{props.name}
25 |
26 |
27 | );
28 | };
29 |
--------------------------------------------------------------------------------
/client/src/ui/avatar.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { css, styled } from 'stitches.config';
3 | import defaultIcon from '/default_player.png';
4 | import crownIcon from '/crown.png';
5 | import { DisconnectedIcon } from './icons/disconnected';
6 |
7 | interface Props {
8 | imageUrl?: string | null;
9 | n?: number;
10 | size?: number;
11 | crown?: boolean;
12 | disconnected?: boolean;
13 | isSignedIn?: boolean;
14 | }
15 |
16 | const avatarWrapper = (isSignedIn: boolean) =>
17 | css({
18 | alignItems: 'center',
19 | '@bp1': {
20 | marginRight: 8,
21 | },
22 | position: 'relative',
23 | cursor: isSignedIn ? 'pointer' : 'inherit',
24 | '& .avatar': {
25 | borderRadius: '50%',
26 | },
27 | })();
28 |
29 | const disconnectedWrapper = css({
30 | position: 'absolute',
31 | bottom: -5,
32 | zIndex: 5,
33 | left: -3,
34 | '@bp0': {
35 | bottom: 0,
36 | width: 24,
37 | height: 24,
38 | },
39 | transform: 'scale(80%)',
40 | backgroundColor: '#000000aa',
41 | borderRadius: '50%',
42 | });
43 | const Crown = styled('img', {
44 | position: 'absolute',
45 | top: -6,
46 | zIndex: 5,
47 | left: 6,
48 | });
49 |
50 | const Avatar = React.forwardRef((props, ref) => {
51 | const { size, imageUrl, n, crown, disconnected, isSignedIn } = props;
52 |
53 | const forSureImageUrl = imageUrl || defaultIcon;
54 |
55 | const size2 = size || 36;
56 |
57 | return (
58 |
59 |
66 | {crown ? (
67 |
68 | ) : null}
69 | {disconnected ? (
70 |
71 |
72 |
73 | ) : null}
74 |
75 | );
76 | });
77 |
78 | export default Avatar;
79 |
--------------------------------------------------------------------------------
/client/src/ui/badge.tsx:
--------------------------------------------------------------------------------
1 | import { useSelector } from 'react-redux';
2 | import { styled } from 'stitches.config';
3 | import { ReduxState } from '../store';
4 | import { usePopperTooltip } from 'react-popper-tooltip';
5 |
6 | import React from 'react';
7 | import { AchievementTooltip } from './player';
8 |
9 | const BImg = styled('img', {
10 | imageRendering: 'pixelated',
11 | });
12 |
13 | const HistogramTooltip = styled('div', {
14 | lineHeight: 'initial',
15 | fontSize: 16,
16 | });
17 |
18 | export const BadgeImg = (props: { id: string; click?: boolean }) => {
19 | const [tooltipVisible, setTooltipVisible] = React.useState(false);
20 | const { getTooltipProps, setTooltipRef, setTriggerRef } = usePopperTooltip({
21 | visible: tooltipVisible,
22 | onVisibleChange: setTooltipVisible,
23 | trigger: props.click ? 'click' : undefined,
24 | });
25 | const badges = useSelector((state: ReduxState) => state.auth.badges) || {};
26 | const badge = badges[props.id];
27 | return (
28 | <>
29 | {tooltipVisible ? (
30 |
34 |
35 |
36 | {badge?.description}
37 |
38 |
39 | ) : null}
40 |
47 | >
48 | );
49 | };
50 |
--------------------------------------------------------------------------------
/client/src/ui/buttons/add_sub_button.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { connect } from 'react-redux';
3 | import { getAddSubButtonClassSelector } from '../../selectors/game_selectors';
4 | import { ReduxState } from '../../store';
5 | import { Button } from './button';
6 | import './buttons.css';
7 |
8 | interface OwnProps {
9 | n?: number;
10 | }
11 |
12 | interface StateProps {
13 | socket?: WebSocket;
14 | addClass: string;
15 | subClass: string;
16 | }
17 |
18 | type Props = OwnProps & StateProps;
19 |
20 | const AddSubButton: React.FC = ({ socket, addClass, subClass, n }) => {
21 | const onClick = (a: string) => {
22 | if (socket) {
23 | if (n === undefined) {
24 | socket.send(JSON.stringify({ type: a }));
25 | } else {
26 | socket.send(JSON.stringify({ type: `${a}_nth`, n }));
27 | }
28 | }
29 | };
30 |
31 | return (
32 |
33 |
36 |
39 |
40 | );
41 | };
42 |
43 | const mapStateToProps = (state: ReduxState, ownProps: OwnProps): StateProps => {
44 | return {
45 | socket: state.connection.socket,
46 | addClass: getAddSubButtonClassSelector(
47 | typeof ownProps.n === 'number' ? ownProps.n + 1 : 'add'
48 | )(state),
49 | subClass: getAddSubButtonClassSelector(
50 | typeof ownProps.n === 'number' ? -(ownProps.n + 1) : 'sub'
51 | )(state),
52 | };
53 | };
54 |
55 | export default connect(mapStateToProps)(AddSubButton);
56 |
--------------------------------------------------------------------------------
/client/src/ui/buttons/button.tsx:
--------------------------------------------------------------------------------
1 | import { customTheme, styled } from 'stitches.config';
2 |
3 | export const Button = styled('button', {
4 | maxHeight: 48,
5 | height: 48,
6 | minHeight: 48,
7 | backgroundColor: '$gray400',
8 | textShadow: 'rgba(0, 0, 0, 0.1) 0px 1px 2px',
9 | boxShadow:
10 | 'rgba(255, 255, 255, 0.2) 0px 2px 1px 0px inset, rgba(0,0,0,0.3) 0px -5px 1px 0px inset, rgba(0, 0, 0, 0.15) 0px 6px 10px -3px',
11 | paddingBottom: 4,
12 | paddingLeft: 8,
13 | paddingRight: 8,
14 | '&:hover': {
15 | transition: 'unset',
16 | opacity: 0.8,
17 | },
18 | '&:active': {
19 | marginTop: 3,
20 | maxHeight: 45,
21 | height: 45,
22 | minHeight: 45,
23 | boxShadow:
24 | 'rgba(255, 255, 255, 0.2) 0px 2px 1px 0px inset, rgba(0,0,0,0.3) 0px -1px 1px 0px inset, rgba(0, 0, 0, 0.15) 0px 6px 10px -3px',
25 | paddingBottom: 1,
26 | transition: 'unset',
27 | },
28 | color: '$gray900',
29 | border: '1px solid rgba(0,0,0,0.3)',
30 | borderRadius: 8,
31 | fontSize: '24px',
32 | fontFamily: 'Montserrat',
33 | [`.${customTheme} &`]: {
34 | backgroundColor: '$brand',
35 | color: '$primary',
36 | },
37 | });
38 |
--------------------------------------------------------------------------------
/client/src/ui/buttons/buttons.css:
--------------------------------------------------------------------------------
1 | button {
2 | flex-basis: 100%;
3 | background-color: lightgray;
4 | border: 0;
5 | margin: 0px 5px;
6 | border-radius: 8px;
7 | font-family: 'Montserrat';
8 | font-size: 20px;
9 | font-weight: bold;
10 | }
11 |
12 | button:focus {
13 | outline: 0;
14 | }
15 |
16 | .buttonColumn {
17 | display: flex;
18 | flex-direction: row;
19 | align-items: stretch;
20 | justify-content: space-between;
21 | flex-grow: 1;
22 | align-content: stretch;
23 | flex-basis: 100%;
24 | }
25 |
26 | .topButton {
27 | min-height: 40px;
28 | max-height: 40px;
29 | z-index: 40;
30 | }
31 |
32 | button.Add {
33 | background-color: #007bff !important;
34 | color: white;
35 | font-size: 18pt;
36 | font-weight: bold;
37 | }
38 |
39 | button.Subtract {
40 | background-color: #6c757d !important;
41 | color: white;
42 | font-size: 18pt;
43 | font-weight: bold;
44 | }
45 |
46 | button.Reset {
47 | background-color: #dc3545 !important;
48 | color: white;
49 | }
50 |
51 | button.Victory {
52 | background-color: #ffc107 !important;
53 | position: relative;
54 | overflow: hidden;
55 | }
56 |
57 | button.Victory::before {
58 | content: '';
59 | position: absolute;
60 | top: 0;
61 | left: 0;
62 | z-index: 2;
63 | background: white;
64 | opacity: 0.3;
65 | height: 100%;
66 | width: 100%;
67 | background: linear-gradient(to right, white, white 70%, transparent 20px);
68 | transform-origin: left bottom;
69 | animation: shine 2s ease-in-out infinite;
70 | }
71 |
72 | @keyframes shine {
73 | 0% {
74 | transform: skewX(-45deg) translateX(-150%);
75 | }
76 | 80% {
77 | transform: skewX(-45deg) translateX(150%);
78 | }
79 | 100% {
80 | transform: skewX(-45deg) translateX(150%);
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/client/src/ui/buttons/restart_button.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { connect } from 'react-redux';
3 | import { selectIsSpectator } from 'selectors/game_selectors';
4 | import { ReduxState } from '../../store';
5 | import { Button } from './button';
6 |
7 | interface Props {
8 | socket?: WebSocket;
9 | isSpectator: boolean;
10 | }
11 |
12 | const RollButton: React.FC = ({ socket, isSpectator }) => {
13 | const onClick = () => {
14 | if (socket) {
15 | socket.send(JSON.stringify({ type: 'restart' }));
16 | }
17 | };
18 | return isSpectator ? null : ;
19 | };
20 |
21 | const mapStateToProps = (state: ReduxState) => {
22 | return {
23 | socket: state.connection.socket,
24 | isSpectator: selectIsSpectator(state),
25 | };
26 | };
27 |
28 | export default connect(mapStateToProps)(RollButton);
29 |
--------------------------------------------------------------------------------
/client/src/ui/buttons/slider.tsx:
--------------------------------------------------------------------------------
1 | import { styled } from 'stitches.config';
2 |
3 | const SliderContainer = styled('div', {
4 | '& input': {
5 | appearance: 'none',
6 | background: '$gray500',
7 | height: 16,
8 | borderRadius: 16,
9 | display: 'flex',
10 | '&::-webkit-slider-thumb': {
11 | appearance: 'none',
12 | background: '$primary',
13 | width: 26,
14 | height: 26,
15 | borderRadius: '50%',
16 | },
17 | },
18 | });
19 |
20 | const Fieldset = styled('fieldset', {
21 | border: 0,
22 | display: 'flex',
23 | alignItems: 'center',
24 | gap: 8,
25 | flex: 1,
26 | maxHeight: 34,
27 | transition: 'none',
28 | '& label': {
29 | color: '$primary',
30 | display: 'flex',
31 | },
32 | '&:has(:focus-visible)': {
33 | border: '2px solid $primary',
34 | margin: -2,
35 | },
36 | });
37 |
38 | interface Props {
39 | id: string;
40 | desc: string;
41 | min: number;
42 | max: number;
43 | value: number;
44 | onChange: React.ChangeEventHandler;
45 | }
46 |
47 | export const Slider: React.FC = ({
48 | id,
49 | desc,
50 | min,
51 | max,
52 | value,
53 | onChange,
54 | }) => {
55 | return (
56 |
69 | );
70 | };
71 |
--------------------------------------------------------------------------------
/client/src/ui/buttons/textarea.tsx:
--------------------------------------------------------------------------------
1 | import { styled } from 'stitches.config';
2 |
3 | const Fieldset = styled('fieldset', {
4 | border: 0,
5 | display: 'flex',
6 | flexDirection: 'column',
7 | gap: 8,
8 | flex: 1,
9 | transition: 'none',
10 | '& label': {
11 | color: '$primary',
12 | },
13 | '&:has(:focus-visible)': {
14 | border: '2px solid $primary',
15 | margin: -2,
16 | },
17 | });
18 |
19 | const Textarea = styled('textarea', {
20 | flex: 1,
21 | backgroundColor: '$gray900',
22 | '@bp0': {
23 | backgroundColor: '$gray800',
24 | },
25 | border: 0,
26 | resize: 'none',
27 | fontFamily: 'Amiko',
28 | padding: 8,
29 | borderRadius: 8,
30 | color: '$primaryDimmed',
31 | '&:focus-visible': {
32 | color: '$primary',
33 | },
34 | });
35 |
36 | interface Props {
37 | id: string;
38 | desc: string;
39 | value: string;
40 | onChange: React.ChangeEventHandler;
41 | placeholder?: string;
42 | }
43 |
44 | export const TextArea: React.FC = ({
45 | placeholder,
46 | id,
47 | desc,
48 | value,
49 | onChange,
50 | }) => {
51 | return (
52 |
61 | );
62 | };
63 |
--------------------------------------------------------------------------------
/client/src/ui/dice.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cgsdev0/rollycubes/4106af80fcc07634c20fd8c8851c1260d933d9f9/client/src/ui/dice.png
--------------------------------------------------------------------------------
/client/src/ui/dice.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { connect } from 'react-redux';
3 | import {
4 | selectDiceRolls,
5 | selectIs3d,
6 | selectShouldShowDiceBoxes,
7 | } from '../selectors/game_selectors';
8 | import { ReduxState } from '../store';
9 | import { DieRoll } from '../types/api';
10 | import Die from './die';
11 | import './dice.css';
12 |
13 | interface Props {
14 | dice: ReduxState['game']['rolls'];
15 | rolling: 'rolled' | 'rolling';
16 | is3d: boolean;
17 | showDiceBoxes: boolean;
18 | }
19 |
20 | const Dice: React.FC = ({ dice, rolling, is3d, showDiceBoxes }) => {
21 | if (!showDiceBoxes) {
22 | return ;
23 | }
24 | return (
25 |
26 | {dice.map((die: DieRoll, i: number) => (
27 |
28 | ))}
29 |
30 | );
31 | };
32 |
33 | const mapStateToProps = (state: ReduxState) => {
34 | return {
35 | dice: selectDiceRolls(state),
36 | is3d: selectIs3d(state),
37 | showDiceBoxes: selectShouldShowDiceBoxes(state),
38 | };
39 | };
40 |
41 | export default connect(mapStateToProps)(Dice);
42 |
--------------------------------------------------------------------------------
/client/src/ui/die.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { DieRoll } from '../types/api';
3 | import './dice.css';
4 |
5 | interface Props {
6 | roll: DieRoll;
7 | rolling: 'rolling' | 'rolled';
8 | n: number;
9 | is3d: boolean;
10 | }
11 |
12 | const Die: React.FC = ({ roll, rolling, n, is3d }) => {
13 | if (is3d) {
14 | return {`${roll.value}`}
;
15 | }
16 | return (
17 |
22 | );
23 | };
24 |
25 | export default Die;
26 |
--------------------------------------------------------------------------------
/client/src/ui/icons/disconnected.tsx:
--------------------------------------------------------------------------------
1 | export const DisconnectedIcon = () => (
2 |
8 | );
9 |
--------------------------------------------------------------------------------
/client/src/ui/icons/kick.tsx:
--------------------------------------------------------------------------------
1 | import { css } from 'stitches.config';
2 |
3 | const hover = css({
4 | '&:hover': {
5 | '& path': {
6 | fill: '#881111',
7 | },
8 | },
9 | '@bp0': {
10 | display: 'none',
11 | },
12 | });
13 | export const KickIcon = () => {
14 | return (
15 |
27 | );
28 | };
29 |
--------------------------------------------------------------------------------
/client/tasks.toml:
--------------------------------------------------------------------------------
1 | [[task]]
2 | id = "run"
3 | type = "long"
4 | env = { "NODE_OPTIONS" = "--no-network-family-autoselection" }
5 | cmd = "npm run start"
6 |
7 | [[task]]
8 | id = "prod"
9 | type = "long"
10 | env = { "NODE_OPTIONS" = "--no-network-family-autoselection" }
11 | cmd = "VITE_USE_PROD=true npm run no-auth"
12 |
--------------------------------------------------------------------------------
/client/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "lib": ["dom", "dom.iterable", "esnext"],
5 | "types": [
6 | "vite/client",
7 | "vite-plugin-svgr/client"
8 | ],
9 | "baseUrl": "./src",
10 | "allowJs": true,
11 | "skipLibCheck": true,
12 | "esModuleInterop": true,
13 | "allowSyntheticDefaultImports": true,
14 | "strict": true,
15 | "forceConsistentCasingInFileNames": true,
16 | "module": "esnext",
17 | "moduleResolution": "node",
18 | "resolveJsonModule": true,
19 | "isolatedModules": true,
20 | "noEmit": true,
21 | "jsx": "react-jsx",
22 | "noFallthroughCasesInSwitch": true
23 | },
24 | "paths": {
25 | "*": ["types/*"]
26 | },
27 | "include": ["src"],
28 | "exclude": ["functions/**/*"],
29 | }
30 |
--------------------------------------------------------------------------------
/client/types/react-inline-editing.d.ts:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cgsdev0/rollycubes/4106af80fcc07634c20fd8c8851c1260d933d9f9/client/types/react-inline-editing.d.ts
--------------------------------------------------------------------------------
/client/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite';
2 | import react from '@vitejs/plugin-react';
3 | import svgrPlugin from 'vite-plugin-svgr';
4 | import tsconfigPaths from 'vite-tsconfig-paths';
5 | import path from 'path';
6 |
7 | let http_host = 'http://localhost:3001';
8 | let ws_host = 'ws://localhost:3001';
9 | if (process.env.VITE_USE_PROD) {
10 | http_host = 'https://prod.rollycubes.com';
11 | ws_host = 'wss://prod.rollycubes.com';
12 | }
13 | // https://vitejs.dev/config/
14 | export default defineConfig({
15 | resolve: {
16 | alias: {
17 | 'stitches.config': path.resolve(__dirname, './src/stitches.config.ts'),
18 | },
19 | },
20 | build: {
21 | outDir: 'build',
22 | },
23 | plugins: [
24 | tsconfigPaths(),
25 | react(),
26 | svgrPlugin({
27 | svgrOptions: {
28 | icon: true,
29 | // ...svgr options (https://react-svgr.com/docs/options/)
30 | },
31 | }),
32 | ],
33 | server: {
34 | port: 3000,
35 | proxy: {
36 | '/metrics': {
37 | target: http_host,
38 | changeOrigin: true,
39 | secure: false,
40 | },
41 | '/cookie': {
42 | target: http_host,
43 | changeOrigin: true,
44 | secure: false,
45 | },
46 | '/list': {
47 | target: http_host,
48 | changeOrigin: true,
49 | secure: false,
50 | },
51 | '/create': {
52 | target: http_host,
53 | changeOrigin: true,
54 | secure: false,
55 | },
56 | '/ws/': {
57 | target: ws_host,
58 | changeOrigin: true,
59 | ws: true,
60 | secure: false,
61 | },
62 | },
63 | },
64 | });
65 |
--------------------------------------------------------------------------------
/docs/.nojekyll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cgsdev0/rollycubes/4106af80fcc07634c20fd8c8851c1260d933d9f9/docs/.nojekyll
--------------------------------------------------------------------------------
/docs/CNAME:
--------------------------------------------------------------------------------
1 | docs.rollycubes.com
--------------------------------------------------------------------------------
/docs/_sidebar.md:
--------------------------------------------------------------------------------
1 | * [Home](/)
2 | * [WebSocket API](/gen)
3 |
--------------------------------------------------------------------------------
/docs/gen/.nojekyll:
--------------------------------------------------------------------------------
1 | TypeDoc added this file to prevent GitHub Pages from using Jekyll. You can turn off this behavior by setting the `githubPages` option to false.
--------------------------------------------------------------------------------
/docs/gen/interfaces/server_messages.ChatMsg.md:
--------------------------------------------------------------------------------
1 | [client](/) / [Modules](/gen/modules.md) / [server\_messages](/gen/modules/server_messages.md) / ChatMsg
2 |
3 | # Interface: ChatMsg
4 |
5 | [server_messages](/gen/modules/server_messages.md).ChatMsg
6 |
7 | ## Table of contents
8 |
9 | ### Properties
10 |
11 | - [msg](/gen/interfaces/server_messages.ChatMsg.md#msg)
12 | - [type](/gen/interfaces/server_messages.ChatMsg.md#type)
13 |
14 | ## Properties
15 |
16 | ### msg
17 |
18 | • **msg**: `string`
19 |
20 | #### Defined in
21 |
22 | [server_messages.ts:89](https://github.com/cgsdev0/rollycubes/blob/1c25446/client/src/types/server_messages.ts#L89)
23 |
24 | ___
25 |
26 | ### type
27 |
28 | • **type**: ``"chat"``
29 |
30 | #### Defined in
31 |
32 | [server_messages.ts:88](https://github.com/cgsdev0/rollycubes/blob/1c25446/client/src/types/server_messages.ts#L88)
33 |
--------------------------------------------------------------------------------
/docs/gen/interfaces/server_messages.DisconnectMsg.md:
--------------------------------------------------------------------------------
1 | [client](/) / [Modules](/gen/modules.md) / [server\_messages](/gen/modules/server_messages.md) / DisconnectMsg
2 |
3 | # Interface: DisconnectMsg
4 |
5 | [server_messages](/gen/modules/server_messages.md).DisconnectMsg
6 |
7 | ## Table of contents
8 |
9 | ### Properties
10 |
11 | - [id](/gen/interfaces/server_messages.DisconnectMsg.md#id)
12 | - [type](/gen/interfaces/server_messages.DisconnectMsg.md#type)
13 |
14 | ## Properties
15 |
16 | ### id
17 |
18 | • **id**: `number`
19 |
20 | #### Defined in
21 |
22 | [server_messages.ts:74](https://github.com/cgsdev0/rollycubes/blob/1c25446/client/src/types/server_messages.ts#L74)
23 |
24 | ___
25 |
26 | ### type
27 |
28 | • **type**: ``"disconnect"``
29 |
30 | #### Defined in
31 |
32 | [server_messages.ts:73](https://github.com/cgsdev0/rollycubes/blob/1c25446/client/src/types/server_messages.ts#L73)
33 |
--------------------------------------------------------------------------------
/docs/gen/interfaces/server_messages.GameError.md:
--------------------------------------------------------------------------------
1 | [client](/) / [Modules](/gen/modules.md) / [server\_messages](/gen/modules/server_messages.md) / GameError
2 |
3 | # Interface: GameError
4 |
5 | [server_messages](/gen/modules/server_messages.md).GameError
6 |
7 | ## Table of contents
8 |
9 | ### Properties
10 |
11 | - [error](/gen/interfaces/server_messages.GameError.md#error)
12 | - [type](/gen/interfaces/server_messages.GameError.md#type)
13 |
14 | ## Properties
15 |
16 | ### error
17 |
18 | • **error**: `string`
19 |
20 | #### Defined in
21 |
22 | [server_messages.ts:5](https://github.com/cgsdev0/rollycubes/blob/1c25446/client/src/types/server_messages.ts#L5)
23 |
24 | ___
25 |
26 | ### type
27 |
28 | • **type**: ``"error"``
29 |
30 | #### Defined in
31 |
32 | [server_messages.ts:4](https://github.com/cgsdev0/rollycubes/blob/1c25446/client/src/types/server_messages.ts#L4)
33 |
--------------------------------------------------------------------------------
/docs/gen/interfaces/server_messages.JoinMsg.md:
--------------------------------------------------------------------------------
1 | [client](/) / [Modules](/gen/modules.md) / [server\_messages](/gen/modules/server_messages.md) / JoinMsg
2 |
3 | # Interface: JoinMsg
4 |
5 | [server_messages](/gen/modules/server_messages.md).JoinMsg
6 |
7 | ## Table of contents
8 |
9 | ### Properties
10 |
11 | - [id](/gen/interfaces/server_messages.JoinMsg.md#id)
12 | - [name](/gen/interfaces/server_messages.JoinMsg.md#name)
13 | - [type](/gen/interfaces/server_messages.JoinMsg.md#type)
14 | - [user\_id](/gen/interfaces/server_messages.JoinMsg.md#user_id)
15 |
16 | ## Properties
17 |
18 | ### id
19 |
20 | • **id**: `number`
21 |
22 | #### Defined in
23 |
24 | [server_messages.ts:66](https://github.com/cgsdev0/rollycubes/blob/1c25446/client/src/types/server_messages.ts#L66)
25 |
26 | ___
27 |
28 | ### name
29 |
30 | • `Optional` **name**: `string`
31 |
32 | #### Defined in
33 |
34 | [server_messages.ts:68](https://github.com/cgsdev0/rollycubes/blob/1c25446/client/src/types/server_messages.ts#L68)
35 |
36 | ___
37 |
38 | ### type
39 |
40 | • **type**: ``"join"``
41 |
42 | #### Defined in
43 |
44 | [server_messages.ts:65](https://github.com/cgsdev0/rollycubes/blob/1c25446/client/src/types/server_messages.ts#L65)
45 |
46 | ___
47 |
48 | ### user\_id
49 |
50 | • `Optional` **user\_id**: `string`
51 |
52 | #### Defined in
53 |
54 | [server_messages.ts:69](https://github.com/cgsdev0/rollycubes/blob/1c25446/client/src/types/server_messages.ts#L69)
55 |
--------------------------------------------------------------------------------
/docs/gen/interfaces/server_messages.KickMsg.md:
--------------------------------------------------------------------------------
1 | [client](/) / [Modules](/gen/modules.md) / [server\_messages](/gen/modules/server_messages.md) / KickMsg
2 |
3 | # Interface: KickMsg
4 |
5 | [server_messages](/gen/modules/server_messages.md).KickMsg
6 |
7 | ## Table of contents
8 |
9 | ### Properties
10 |
11 | - [id](/gen/interfaces/server_messages.KickMsg.md#id)
12 | - [type](/gen/interfaces/server_messages.KickMsg.md#type)
13 |
14 | ## Properties
15 |
16 | ### id
17 |
18 | • **id**: `number`
19 |
20 | #### Defined in
21 |
22 | [server_messages.ts:84](https://github.com/cgsdev0/rollycubes/blob/1c25446/client/src/types/server_messages.ts#L84)
23 |
24 | ___
25 |
26 | ### type
27 |
28 | • **type**: ``"kick"``
29 |
30 | #### Defined in
31 |
32 | [server_messages.ts:83](https://github.com/cgsdev0/rollycubes/blob/1c25446/client/src/types/server_messages.ts#L83)
33 |
--------------------------------------------------------------------------------
/docs/gen/interfaces/server_messages.ReconnectMsg.md:
--------------------------------------------------------------------------------
1 | [client](/) / [Modules](/gen/modules.md) / [server\_messages](/gen/modules/server_messages.md) / ReconnectMsg
2 |
3 | # Interface: ReconnectMsg
4 |
5 | [server_messages](/gen/modules/server_messages.md).ReconnectMsg
6 |
7 | ## Table of contents
8 |
9 | ### Properties
10 |
11 | - [id](/gen/interfaces/server_messages.ReconnectMsg.md#id)
12 | - [type](/gen/interfaces/server_messages.ReconnectMsg.md#type)
13 |
14 | ## Properties
15 |
16 | ### id
17 |
18 | • **id**: `number`
19 |
20 | #### Defined in
21 |
22 | [server_messages.ts:79](https://github.com/cgsdev0/rollycubes/blob/1c25446/client/src/types/server_messages.ts#L79)
23 |
24 | ___
25 |
26 | ### type
27 |
28 | • **type**: ``"reconnect"``
29 |
30 | #### Defined in
31 |
32 | [server_messages.ts:78](https://github.com/cgsdev0/rollycubes/blob/1c25446/client/src/types/server_messages.ts#L78)
33 |
--------------------------------------------------------------------------------
/docs/gen/interfaces/server_messages.Redirect.md:
--------------------------------------------------------------------------------
1 | [client](/) / [Modules](/gen/modules.md) / [server\_messages](/gen/modules/server_messages.md) / Redirect
2 |
3 | # Interface: Redirect
4 |
5 | [server_messages](/gen/modules/server_messages.md).Redirect
6 |
7 | ## Table of contents
8 |
9 | ### Properties
10 |
11 | - [room](/gen/interfaces/server_messages.Redirect.md#room)
12 | - [type](/gen/interfaces/server_messages.Redirect.md#type)
13 |
14 | ## Properties
15 |
16 | ### room
17 |
18 | • **room**: `string`
19 |
20 | #### Defined in
21 |
22 | [server_messages.ts:10](https://github.com/cgsdev0/rollycubes/blob/1c25446/client/src/types/server_messages.ts#L10)
23 |
24 | ___
25 |
26 | ### type
27 |
28 | • **type**: ``"redirect"``
29 |
30 | #### Defined in
31 |
32 | [server_messages.ts:9](https://github.com/cgsdev0/rollycubes/blob/1c25446/client/src/types/server_messages.ts#L9)
33 |
--------------------------------------------------------------------------------
/docs/gen/interfaces/server_messages.RestartMsg.md:
--------------------------------------------------------------------------------
1 | [client](/) / [Modules](/gen/modules.md) / [server\_messages](/gen/modules/server_messages.md) / RestartMsg
2 |
3 | # Interface: RestartMsg
4 |
5 | [server_messages](/gen/modules/server_messages.md).RestartMsg
6 |
7 | ## Table of contents
8 |
9 | ### Properties
10 |
11 | - [id](/gen/interfaces/server_messages.RestartMsg.md#id)
12 | - [type](/gen/interfaces/server_messages.RestartMsg.md#type)
13 |
14 | ## Properties
15 |
16 | ### id
17 |
18 | • **id**: `number`
19 |
20 | #### Defined in
21 |
22 | [server_messages.ts:47](https://github.com/cgsdev0/rollycubes/blob/1c25446/client/src/types/server_messages.ts#L47)
23 |
24 | ___
25 |
26 | ### type
27 |
28 | • **type**: ``"restart"``
29 |
30 | #### Defined in
31 |
32 | [server_messages.ts:46](https://github.com/cgsdev0/rollycubes/blob/1c25446/client/src/types/server_messages.ts#L46)
33 |
--------------------------------------------------------------------------------
/docs/gen/interfaces/server_messages.RollAgainMsg.md:
--------------------------------------------------------------------------------
1 | [client](/) / [Modules](/gen/modules.md) / [server\_messages](/gen/modules/server_messages.md) / RollAgainMsg
2 |
3 | # Interface: RollAgainMsg
4 |
5 | [server_messages](/gen/modules/server_messages.md).RollAgainMsg
6 |
7 | ## Table of contents
8 |
9 | ### Properties
10 |
11 | - [type](/gen/interfaces/server_messages.RollAgainMsg.md#type)
12 |
13 | ## Properties
14 |
15 | ### type
16 |
17 | • **type**: ``"roll_again"``
18 |
19 | #### Defined in
20 |
21 | [server_messages.ts:61](https://github.com/cgsdev0/rollycubes/blob/1c25446/client/src/types/server_messages.ts#L61)
22 |
--------------------------------------------------------------------------------
/docs/gen/interfaces/server_messages.RollMsg.md:
--------------------------------------------------------------------------------
1 | [client](/) / [Modules](/gen/modules.md) / [server\_messages](/gen/modules/server_messages.md) / RollMsg
2 |
3 | # Interface: RollMsg
4 |
5 | [server_messages](/gen/modules/server_messages.md).RollMsg
6 |
7 | ## Table of contents
8 |
9 | ### Properties
10 |
11 | - [rolls](/gen/interfaces/server_messages.RollMsg.md#rolls)
12 | - [type](/gen/interfaces/server_messages.RollMsg.md#type)
13 |
14 | ## Properties
15 |
16 | ### rolls
17 |
18 | • **rolls**: `number`[]
19 |
20 | #### Defined in
21 |
22 | [server_messages.ts:57](https://github.com/cgsdev0/rollycubes/blob/1c25446/client/src/types/server_messages.ts#L57)
23 |
24 | ___
25 |
26 | ### type
27 |
28 | • **type**: ``"roll"``
29 |
30 | #### Defined in
31 |
32 | [server_messages.ts:56](https://github.com/cgsdev0/rollycubes/blob/1c25446/client/src/types/server_messages.ts#L56)
33 |
--------------------------------------------------------------------------------
/docs/gen/interfaces/server_messages.Room.md:
--------------------------------------------------------------------------------
1 | [client](/) / [Modules](/gen/modules.md) / [server\_messages](/gen/modules/server_messages.md) / Room
2 |
3 | # Interface: Room
4 |
5 | [server_messages](/gen/modules/server_messages.md).Room
6 |
7 | ## Table of contents
8 |
9 | ### Properties
10 |
11 | - [code](/gen/interfaces/server_messages.Room.md#code)
12 | - [host\_name](/gen/interfaces/server_messages.Room.md#host_name)
13 | - [last\_updated](/gen/interfaces/server_messages.Room.md#last_updated)
14 | - [player\_count](/gen/interfaces/server_messages.Room.md#player_count)
15 |
16 | ## Properties
17 |
18 | ### code
19 |
20 | • **code**: `string`
21 |
22 | #### Defined in
23 |
24 | [server_messages.ts:14](https://github.com/cgsdev0/rollycubes/blob/1c25446/client/src/types/server_messages.ts#L14)
25 |
26 | ___
27 |
28 | ### host\_name
29 |
30 | • **host\_name**: `string`
31 |
32 | #### Defined in
33 |
34 | [server_messages.ts:15](https://github.com/cgsdev0/rollycubes/blob/1c25446/client/src/types/server_messages.ts#L15)
35 |
36 | ___
37 |
38 | ### last\_updated
39 |
40 | • **last\_updated**: `string`
41 |
42 | #### Defined in
43 |
44 | [server_messages.ts:16](https://github.com/cgsdev0/rollycubes/blob/1c25446/client/src/types/server_messages.ts#L16)
45 |
46 | ___
47 |
48 | ### player\_count
49 |
50 | • **player\_count**: `number`
51 |
52 | #### Defined in
53 |
54 | [server_messages.ts:17](https://github.com/cgsdev0/rollycubes/blob/1c25446/client/src/types/server_messages.ts#L17)
55 |
--------------------------------------------------------------------------------
/docs/gen/interfaces/server_messages.RoomList.md:
--------------------------------------------------------------------------------
1 | [client](/) / [Modules](/gen/modules.md) / [server\_messages](/gen/modules/server_messages.md) / RoomList
2 |
3 | # Interface: RoomList
4 |
5 | [server_messages](/gen/modules/server_messages.md).RoomList
6 |
7 | ## Table of contents
8 |
9 | ### Properties
10 |
11 | - [rooms](/gen/interfaces/server_messages.RoomList.md#rooms)
12 |
13 | ## Properties
14 |
15 | ### rooms
16 |
17 | • **rooms**: [`Room`](/gen/interfaces/server_messages.Room.md)[]
18 |
19 | #### Defined in
20 |
21 | [server_messages.ts:21](https://github.com/cgsdev0/rollycubes/blob/1c25446/client/src/types/server_messages.ts#L21)
22 |
--------------------------------------------------------------------------------
/docs/gen/interfaces/server_messages.UpdateMsg.md:
--------------------------------------------------------------------------------
1 | [client](/) / [Modules](/gen/modules.md) / [server\_messages](/gen/modules/server_messages.md) / UpdateMsg
2 |
3 | # Interface: UpdateMsg
4 |
5 | [server_messages](/gen/modules/server_messages.md).UpdateMsg
6 |
7 | ## Table of contents
8 |
9 | ### Properties
10 |
11 | - [id](/gen/interfaces/server_messages.UpdateMsg.md#id)
12 | - [reset](/gen/interfaces/server_messages.UpdateMsg.md#reset)
13 | - [score](/gen/interfaces/server_messages.UpdateMsg.md#score)
14 | - [type](/gen/interfaces/server_messages.UpdateMsg.md#type)
15 | - [used](/gen/interfaces/server_messages.UpdateMsg.md#used)
16 |
17 | ## Properties
18 |
19 | ### id
20 |
21 | • **id**: `number`
22 |
23 | #### Defined in
24 |
25 | [server_messages.ts:105](https://github.com/cgsdev0/rollycubes/blob/1c25446/client/src/types/server_messages.ts#L105)
26 |
27 | ___
28 |
29 | ### reset
30 |
31 | • `Optional` **reset**: `boolean`
32 |
33 | #### Defined in
34 |
35 | [server_messages.ts:108](https://github.com/cgsdev0/rollycubes/blob/1c25446/client/src/types/server_messages.ts#L108)
36 |
37 | ___
38 |
39 | ### score
40 |
41 | • **score**: `number`
42 |
43 | #### Defined in
44 |
45 | [server_messages.ts:106](https://github.com/cgsdev0/rollycubes/blob/1c25446/client/src/types/server_messages.ts#L106)
46 |
47 | ___
48 |
49 | ### type
50 |
51 | • **type**: ``"update"``
52 |
53 | #### Defined in
54 |
55 | [server_messages.ts:104](https://github.com/cgsdev0/rollycubes/blob/1c25446/client/src/types/server_messages.ts#L104)
56 |
57 | ___
58 |
59 | ### used
60 |
61 | • `Optional` **used**: `boolean`[]
62 |
63 | #### Defined in
64 |
65 | [server_messages.ts:107](https://github.com/cgsdev0/rollycubes/blob/1c25446/client/src/types/server_messages.ts#L107)
66 |
--------------------------------------------------------------------------------
/docs/gen/interfaces/server_messages.UpdateNameMsg.md:
--------------------------------------------------------------------------------
1 | [client](/) / [Modules](/gen/modules.md) / [server\_messages](/gen/modules/server_messages.md) / UpdateNameMsg
2 |
3 | # Interface: UpdateNameMsg
4 |
5 | [server_messages](/gen/modules/server_messages.md).UpdateNameMsg
6 |
7 | ## Table of contents
8 |
9 | ### Properties
10 |
11 | - [id](/gen/interfaces/server_messages.UpdateNameMsg.md#id)
12 | - [name](/gen/interfaces/server_messages.UpdateNameMsg.md#name)
13 | - [type](/gen/interfaces/server_messages.UpdateNameMsg.md#type)
14 |
15 | ## Properties
16 |
17 | ### id
18 |
19 | • **id**: `number`
20 |
21 | #### Defined in
22 |
23 | [server_messages.ts:100](https://github.com/cgsdev0/rollycubes/blob/1c25446/client/src/types/server_messages.ts#L100)
24 |
25 | ___
26 |
27 | ### name
28 |
29 | • **name**: `string`
30 |
31 | #### Defined in
32 |
33 | [server_messages.ts:99](https://github.com/cgsdev0/rollycubes/blob/1c25446/client/src/types/server_messages.ts#L99)
34 |
35 | ___
36 |
37 | ### type
38 |
39 | • **type**: ``"update_name"``
40 |
41 | #### Defined in
42 |
43 | [server_messages.ts:98](https://github.com/cgsdev0/rollycubes/blob/1c25446/client/src/types/server_messages.ts#L98)
44 |
--------------------------------------------------------------------------------
/docs/gen/interfaces/server_messages.UpdateTurnMsg.md:
--------------------------------------------------------------------------------
1 | [client](/) / [Modules](/gen/modules.md) / [server\_messages](/gen/modules/server_messages.md) / UpdateTurnMsg
2 |
3 | # Interface: UpdateTurnMsg
4 |
5 | [server_messages](/gen/modules/server_messages.md).UpdateTurnMsg
6 |
7 | ## Table of contents
8 |
9 | ### Properties
10 |
11 | - [id](/gen/interfaces/server_messages.UpdateTurnMsg.md#id)
12 | - [type](/gen/interfaces/server_messages.UpdateTurnMsg.md#type)
13 |
14 | ## Properties
15 |
16 | ### id
17 |
18 | • **id**: `number`
19 |
20 | #### Defined in
21 |
22 | [server_messages.ts:94](https://github.com/cgsdev0/rollycubes/blob/1c25446/client/src/types/server_messages.ts#L94)
23 |
24 | ___
25 |
26 | ### type
27 |
28 | • **type**: ``"update_turn"``
29 |
30 | #### Defined in
31 |
32 | [server_messages.ts:93](https://github.com/cgsdev0/rollycubes/blob/1c25446/client/src/types/server_messages.ts#L93)
33 |
--------------------------------------------------------------------------------
/docs/gen/interfaces/server_messages.WinMsg.md:
--------------------------------------------------------------------------------
1 | [client](/) / [Modules](/gen/modules.md) / [server\_messages](/gen/modules/server_messages.md) / WinMsg
2 |
3 | # Interface: WinMsg
4 |
5 | [server_messages](/gen/modules/server_messages.md).WinMsg
6 |
7 | ## Table of contents
8 |
9 | ### Properties
10 |
11 | - [id](/gen/interfaces/server_messages.WinMsg.md#id)
12 | - [type](/gen/interfaces/server_messages.WinMsg.md#type)
13 |
14 | ## Properties
15 |
16 | ### id
17 |
18 | • **id**: `number`
19 |
20 | #### Defined in
21 |
22 | [server_messages.ts:52](https://github.com/cgsdev0/rollycubes/blob/1c25446/client/src/types/server_messages.ts#L52)
23 |
24 | ___
25 |
26 | ### type
27 |
28 | • **type**: ``"win"``
29 |
30 | #### Defined in
31 |
32 | [server_messages.ts:51](https://github.com/cgsdev0/rollycubes/blob/1c25446/client/src/types/server_messages.ts#L51)
33 |
--------------------------------------------------------------------------------
/docs/gen/interfaces/store_types.Achievement.md:
--------------------------------------------------------------------------------
1 | [client](/) / [Modules](/gen/modules.md) / [store\_types](/gen/modules/store_types.md) / Achievement
2 |
3 | # Interface: Achievement
4 |
5 | [store_types](/gen/modules/store_types.md).Achievement
6 |
7 | ## Table of contents
8 |
9 | ### Properties
10 |
11 | - [achievement](/gen/interfaces/store_types.Achievement.md#achievement)
12 | - [progress](/gen/interfaces/store_types.Achievement.md#progress)
13 | - [unlocked](/gen/interfaces/store_types.Achievement.md#unlocked)
14 |
15 | ## Properties
16 |
17 | ### achievement
18 |
19 | • **achievement**: `Object`
20 |
21 | #### Type declaration
22 |
23 | | Name | Type |
24 | | :------ | :------ |
25 | | `description` | `string` |
26 | | `id` | `string` |
27 | | `image_url` | ``null`` \| `string` |
28 | | `max_progress` | `number` |
29 | | `name` | `string` |
30 |
31 | #### Defined in
32 |
33 | [store_types.ts:51](https://github.com/cgsdev0/rollycubes/blob/1c25446/client/src/types/store_types.ts#L51)
34 |
35 | ___
36 |
37 | ### progress
38 |
39 | • **progress**: `number`
40 |
41 | #### Defined in
42 |
43 | [store_types.ts:49](https://github.com/cgsdev0/rollycubes/blob/1c25446/client/src/types/store_types.ts#L49)
44 |
45 | ___
46 |
47 | ### unlocked
48 |
49 | • **unlocked**: `string`
50 |
51 | #### Defined in
52 |
53 | [store_types.ts:50](https://github.com/cgsdev0/rollycubes/blob/1c25446/client/src/types/store_types.ts#L50)
54 |
--------------------------------------------------------------------------------
/docs/gen/interfaces/store_types.AchievementProgress.md:
--------------------------------------------------------------------------------
1 | [client](/) / [Modules](/gen/modules.md) / [store\_types](/gen/modules/store_types.md) / AchievementProgress
2 |
3 | # Interface: AchievementProgress
4 |
5 | [store_types](/gen/modules/store_types.md).AchievementProgress
6 |
7 | ## Table of contents
8 |
9 | ### Properties
10 |
11 | - [achievement\_id](/gen/interfaces/store_types.AchievementProgress.md#achievement_id)
12 | - [progress](/gen/interfaces/store_types.AchievementProgress.md#progress)
13 | - [type](/gen/interfaces/store_types.AchievementProgress.md#type)
14 | - [user\_id](/gen/interfaces/store_types.AchievementProgress.md#user_id)
15 |
16 | ## Properties
17 |
18 | ### achievement\_id
19 |
20 | • **achievement\_id**: `string`
21 |
22 | #### Defined in
23 |
24 | [store_types.ts:12](https://github.com/cgsdev0/rollycubes/blob/1c25446/client/src/types/store_types.ts#L12)
25 |
26 | ___
27 |
28 | ### progress
29 |
30 | • **progress**: `number`
31 |
32 | #### Defined in
33 |
34 | [store_types.ts:14](https://github.com/cgsdev0/rollycubes/blob/1c25446/client/src/types/store_types.ts#L14)
35 |
36 | ___
37 |
38 | ### type
39 |
40 | • **type**: ``"achievement_progress"``
41 |
42 | #### Defined in
43 |
44 | [store_types.ts:15](https://github.com/cgsdev0/rollycubes/blob/1c25446/client/src/types/store_types.ts#L15)
45 |
46 | ___
47 |
48 | ### user\_id
49 |
50 | • **user\_id**: `string`
51 |
52 | #### Defined in
53 |
54 | [store_types.ts:13](https://github.com/cgsdev0/rollycubes/blob/1c25446/client/src/types/store_types.ts#L13)
55 |
--------------------------------------------------------------------------------
/docs/gen/interfaces/store_types.AchievementUnlock.md:
--------------------------------------------------------------------------------
1 | [client](/) / [Modules](/gen/modules.md) / [store\_types](/gen/modules/store_types.md) / AchievementUnlock
2 |
3 | # Interface: AchievementUnlock
4 |
5 | [store_types](/gen/modules/store_types.md).AchievementUnlock
6 |
7 | ## Table of contents
8 |
9 | ### Properties
10 |
11 | - [description](/gen/interfaces/store_types.AchievementUnlock.md#description)
12 | - [id](/gen/interfaces/store_types.AchievementUnlock.md#id)
13 | - [image\_url](/gen/interfaces/store_types.AchievementUnlock.md#image_url)
14 | - [max\_progress](/gen/interfaces/store_types.AchievementUnlock.md#max_progress)
15 | - [name](/gen/interfaces/store_types.AchievementUnlock.md#name)
16 | - [type](/gen/interfaces/store_types.AchievementUnlock.md#type)
17 |
18 | ## Properties
19 |
20 | ### description
21 |
22 | • **description**: `string`
23 |
24 | #### Defined in
25 |
26 | [store_types.ts:22](https://github.com/cgsdev0/rollycubes/blob/1c25446/client/src/types/store_types.ts#L22)
27 |
28 | ___
29 |
30 | ### id
31 |
32 | • **id**: `string`
33 |
34 | #### Defined in
35 |
36 | [store_types.ts:19](https://github.com/cgsdev0/rollycubes/blob/1c25446/client/src/types/store_types.ts#L19)
37 |
38 | ___
39 |
40 | ### image\_url
41 |
42 | • **image\_url**: `string`
43 |
44 | #### Defined in
45 |
46 | [store_types.ts:20](https://github.com/cgsdev0/rollycubes/blob/1c25446/client/src/types/store_types.ts#L20)
47 |
48 | ___
49 |
50 | ### max\_progress
51 |
52 | • **max\_progress**: `number`
53 |
54 | #### Defined in
55 |
56 | [store_types.ts:23](https://github.com/cgsdev0/rollycubes/blob/1c25446/client/src/types/store_types.ts#L23)
57 |
58 | ___
59 |
60 | ### name
61 |
62 | • **name**: `string`
63 |
64 | #### Defined in
65 |
66 | [store_types.ts:21](https://github.com/cgsdev0/rollycubes/blob/1c25446/client/src/types/store_types.ts#L21)
67 |
68 | ___
69 |
70 | ### type
71 |
72 | • **type**: ``"achievement_unlock"``
73 |
74 | #### Defined in
75 |
76 | [store_types.ts:24](https://github.com/cgsdev0/rollycubes/blob/1c25446/client/src/types/store_types.ts#L24)
77 |
--------------------------------------------------------------------------------
/docs/gen/interfaces/store_types.DieRoll.md:
--------------------------------------------------------------------------------
1 | [client](/) / [Modules](/gen/modules.md) / [store\_types](/gen/modules/store_types.md) / DieRoll
2 |
3 | # Interface: DieRoll
4 |
5 | [store_types](/gen/modules/store_types.md).DieRoll
6 |
7 | ## Table of contents
8 |
9 | ### Properties
10 |
11 | - [used](/gen/interfaces/store_types.DieRoll.md#used)
12 | - [value](/gen/interfaces/store_types.DieRoll.md#value)
13 |
14 | ## Properties
15 |
16 | ### used
17 |
18 | • **used**: `boolean`
19 |
20 | #### Defined in
21 |
22 | [store_types.ts:70](https://github.com/cgsdev0/rollycubes/blob/1c25446/client/src/types/store_types.ts#L70)
23 |
24 | ___
25 |
26 | ### value
27 |
28 | • **value**: `number`
29 |
30 | #### Defined in
31 |
32 | [store_types.ts:71](https://github.com/cgsdev0/rollycubes/blob/1c25446/client/src/types/store_types.ts#L71)
33 |
--------------------------------------------------------------------------------
/docs/gen/interfaces/store_types.ReportStats.md:
--------------------------------------------------------------------------------
1 | [client](/) / [Modules](/gen/modules.md) / [store\_types](/gen/modules/store_types.md) / ReportStats
2 |
3 | # Interface: ReportStats
4 |
5 | [store_types](/gen/modules/store_types.md).ReportStats
6 |
7 | ## Table of contents
8 |
9 | ### Properties
10 |
11 | - [doubles](/gen/interfaces/store_types.ReportStats.md#doubles)
12 | - [games](/gen/interfaces/store_types.ReportStats.md#games)
13 | - [id](/gen/interfaces/store_types.ReportStats.md#id)
14 | - [rolls](/gen/interfaces/store_types.ReportStats.md#rolls)
15 | - [wins](/gen/interfaces/store_types.ReportStats.md#wins)
16 |
17 | ## Properties
18 |
19 | ### doubles
20 |
21 | • **doubles**: `number`
22 |
23 | #### Defined in
24 |
25 | [store_types.ts:32](https://github.com/cgsdev0/rollycubes/blob/1c25446/client/src/types/store_types.ts#L32)
26 |
27 | ___
28 |
29 | ### games
30 |
31 | • **games**: `number`
32 |
33 | #### Defined in
34 |
35 | [store_types.ts:31](https://github.com/cgsdev0/rollycubes/blob/1c25446/client/src/types/store_types.ts#L31)
36 |
37 | ___
38 |
39 | ### id
40 |
41 | • **id**: `string`
42 |
43 | #### Defined in
44 |
45 | [store_types.ts:28](https://github.com/cgsdev0/rollycubes/blob/1c25446/client/src/types/store_types.ts#L28)
46 |
47 | ___
48 |
49 | ### rolls
50 |
51 | • **rolls**: `number`
52 |
53 | #### Defined in
54 |
55 | [store_types.ts:29](https://github.com/cgsdev0/rollycubes/blob/1c25446/client/src/types/store_types.ts#L29)
56 |
57 | ___
58 |
59 | ### wins
60 |
61 | • **wins**: `number`
62 |
63 | #### Defined in
64 |
65 | [store_types.ts:30](https://github.com/cgsdev0/rollycubes/blob/1c25446/client/src/types/store_types.ts#L30)
66 |
--------------------------------------------------------------------------------
/docs/gen/interfaces/store_types.UserStats.md:
--------------------------------------------------------------------------------
1 | [client](/) / [Modules](/gen/modules.md) / [store\_types](/gen/modules/store_types.md) / UserStats
2 |
3 | # Interface: UserStats
4 |
5 | [store_types](/gen/modules/store_types.md).UserStats
6 |
7 | ## Table of contents
8 |
9 | ### Properties
10 |
11 | - [doubles](/gen/interfaces/store_types.UserStats.md#doubles)
12 | - [games](/gen/interfaces/store_types.UserStats.md#games)
13 | - [rolls](/gen/interfaces/store_types.UserStats.md#rolls)
14 | - [wins](/gen/interfaces/store_types.UserStats.md#wins)
15 |
16 | ## Properties
17 |
18 | ### doubles
19 |
20 | • **doubles**: `number`
21 |
22 | #### Defined in
23 |
24 | [store_types.ts:43](https://github.com/cgsdev0/rollycubes/blob/1c25446/client/src/types/store_types.ts#L43)
25 |
26 | ___
27 |
28 | ### games
29 |
30 | • **games**: `number`
31 |
32 | #### Defined in
33 |
34 | [store_types.ts:44](https://github.com/cgsdev0/rollycubes/blob/1c25446/client/src/types/store_types.ts#L44)
35 |
36 | ___
37 |
38 | ### rolls
39 |
40 | • **rolls**: `number`
41 |
42 | #### Defined in
43 |
44 | [store_types.ts:42](https://github.com/cgsdev0/rollycubes/blob/1c25446/client/src/types/store_types.ts#L42)
45 |
46 | ___
47 |
48 | ### wins
49 |
50 | • **wins**: `number`
51 |
52 | #### Defined in
53 |
54 | [store_types.ts:45](https://github.com/cgsdev0/rollycubes/blob/1c25446/client/src/types/store_types.ts#L45)
55 |
--------------------------------------------------------------------------------
/docs/gen/modules.md:
--------------------------------------------------------------------------------
1 | [client](/) / Modules
2 |
3 | # client
4 |
5 | ## Table of contents
6 |
7 | ### Modules
8 |
9 | - [server\_messages](/gen/modules/server_messages.md)
10 | - [store\_types](/gen/modules/store_types.md)
11 |
--------------------------------------------------------------------------------
/docs/gen/modules/server_messages.md:
--------------------------------------------------------------------------------
1 | [client](/) / [Modules](/gen/modules.md) / server\_messages
2 |
3 | # Module: server\_messages
4 |
5 | ## Table of contents
6 |
7 | ### Interfaces
8 |
9 | - [ChatMsg](/gen/interfaces/server_messages.ChatMsg.md)
10 | - [DisconnectMsg](/gen/interfaces/server_messages.DisconnectMsg.md)
11 | - [GameError](/gen/interfaces/server_messages.GameError.md)
12 | - [GameState](/gen/interfaces/server_messages.GameState.md)
13 | - [IGameState](/gen/interfaces/server_messages.IGameState.md)
14 | - [JoinMsg](/gen/interfaces/server_messages.JoinMsg.md)
15 | - [KickMsg](/gen/interfaces/server_messages.KickMsg.md)
16 | - [ReconnectMsg](/gen/interfaces/server_messages.ReconnectMsg.md)
17 | - [Redirect](/gen/interfaces/server_messages.Redirect.md)
18 | - [RestartMsg](/gen/interfaces/server_messages.RestartMsg.md)
19 | - [RollAgainMsg](/gen/interfaces/server_messages.RollAgainMsg.md)
20 | - [RollMsg](/gen/interfaces/server_messages.RollMsg.md)
21 | - [Room](/gen/interfaces/server_messages.Room.md)
22 | - [RoomList](/gen/interfaces/server_messages.RoomList.md)
23 | - [UpdateMsg](/gen/interfaces/server_messages.UpdateMsg.md)
24 | - [UpdateNameMsg](/gen/interfaces/server_messages.UpdateNameMsg.md)
25 | - [UpdateTurnMsg](/gen/interfaces/server_messages.UpdateTurnMsg.md)
26 | - [WelcomeMsg](/gen/interfaces/server_messages.WelcomeMsg.md)
27 | - [WinMsg](/gen/interfaces/server_messages.WinMsg.md)
28 |
--------------------------------------------------------------------------------
/docs/gen/modules/store_types.md:
--------------------------------------------------------------------------------
1 | [client](/) / [Modules](/gen/modules.md) / store\_types
2 |
3 | # Module: store\_types
4 |
5 | ## Table of contents
6 |
7 | ### Interfaces
8 |
9 | - [Achievement](/gen/interfaces/store_types.Achievement.md)
10 | - [AchievementProgress](/gen/interfaces/store_types.AchievementProgress.md)
11 | - [AchievementUnlock](/gen/interfaces/store_types.AchievementUnlock.md)
12 | - [DieRoll](/gen/interfaces/store_types.DieRoll.md)
13 | - [Player](/gen/interfaces/store_types.Player.md)
14 | - [ReportStats](/gen/interfaces/store_types.ReportStats.md)
15 | - [ServerPlayer](/gen/interfaces/store_types.ServerPlayer.md)
16 | - [UserData](/gen/interfaces/store_types.UserData.md)
17 | - [UserStats](/gen/interfaces/store_types.UserStats.md)
18 |
--------------------------------------------------------------------------------
/docs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Document
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/docs/system-graph.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cgsdev0/rollycubes/4106af80fcc07634c20fd8c8851c1260d933d9f9/docs/system-graph.png
--------------------------------------------------------------------------------
/flake.nix:
--------------------------------------------------------------------------------
1 | {
2 | inputs = {
3 | nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
4 | rust-overlay.url = "github:oxalica/rust-overlay";
5 | flake-utils.url = "github:numtide/flake-utils";
6 | };
7 | outputs = { self, nixpkgs, flake-utils, rust-overlay }:
8 | flake-utils.lib.eachDefaultSystem (system:
9 | let
10 | overlays = [ (import rust-overlay) ];
11 | pkgs = import nixpkgs { inherit system overlays; };
12 | in rec
13 | {
14 | devShells.default = pkgs.mkShell {
15 | packages = [ packages.default pkgs.gcc13 pkgs.rust-bin.beta.latest.default pkgs.nodejs_20 pkgs.inotify-tools pkgs.cmake pkgs.zlib pkgs.openssl pkgs.prometheus-cpp pkgs.go ];
16 | };
17 | packages.default = pkgs.stdenv.mkDerivation {
18 | name = "run";
19 | src = pkgs.fetchurl {
20 | url = "https://github.com/amonks/run/releases/download/v1.0.0-beta.24/run_Linux_x86_64.tar.gz";
21 | sha256 = "bf7748242892f227a45ecc1ad6fcc65ddc9f6863f3a8cd05a5d5457ff5da3976";
22 | };
23 | setSourceRoot = "sourceRoot=`pwd`";
24 | installPhase = "mkdir -p $out/bin && cp run $out/bin";
25 | };
26 | }
27 | );
28 | }
29 |
--------------------------------------------------------------------------------
/game/.ccls:
--------------------------------------------------------------------------------
1 | gcc
2 | %cpp -std=c++20
3 | %h -x
4 | %h c++-header
5 | -Iincludes
6 | -IuWebSockets/src
7 | -IuWebSockets/uSockets/src
8 |
--------------------------------------------------------------------------------
/game/.ccls-root:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cgsdev0/rollycubes/4106af80fcc07634c20fd8c8851c1260d933d9f9/game/.ccls-root
--------------------------------------------------------------------------------
/game/.gitignore:
--------------------------------------------------------------------------------
1 | secrets/
2 |
--------------------------------------------------------------------------------
/game/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM alpine:edge as build
2 | RUN apk add g++ libc-dev zlib-dev make openssl-dev
3 | RUN apk add cmake wget tar linux-headers curl-dev
4 |
5 | RUN mkdir build
6 |
7 | ARG civetweb_civetweb_ver=v1.15
8 | ARG jupp0r_prometheuscpp_ver=v1.1.0
9 |
10 | ADD uWebSockets /build/uWebSockets
11 | ADD includes /build/includes
12 | COPY Makefile /build/Makefile
13 | ADD src /build/src
14 |
15 | RUN set -x && \
16 | wget https://github.com/jupp0r/prometheus-cpp/archive/refs/tags/${jupp0r_prometheuscpp_ver}.tar.gz && \
17 | tar xvf ${jupp0r_prometheuscpp_ver}.tar.gz && cd prometheus-cpp*/3rdparty && \
18 | wget https://github.com/civetweb/civetweb/archive/refs/tags/${civetweb_civetweb_ver}.tar.gz && \
19 | tar xvf ${civetweb_civetweb_ver}.tar.gz && mv civetweb-*/* civetweb && cd .. && \
20 | mkdir build && cd build && cmake -DCMAKE_BUILD_TYPE=Release -DENABLE_TESTING=OFF .. && \
21 | make -j4 && make install && \
22 | cd .. && rm -rf * && \
23 | set +x
24 |
25 | RUN cd /build; ls -la .; make -j4 release
26 |
27 | FROM alpine:edge as prod
28 | RUN apk add libstdc++ libgcc openssl-dev
29 | COPY --from=build /build/GameServer .
30 |
31 | ENTRYPOINT ["./GameServer"]
32 |
--------------------------------------------------------------------------------
/game/Makefile:
--------------------------------------------------------------------------------
1 | CXX=g++
2 | CXXFLAGS=-std=c++20 -W -IuWebSockets/src -IuWebSockets/uSockets/src -Iincludes -Iprometheus-cpp/core/include
3 | LDFLAGS=-lprometheus-cpp-pull -lprometheus-cpp-core -lz -lpthread -lssl -lcrypto
4 | TARGET=GameServer
5 |
6 | SRC=$(shell find src/ -type f -name '*.cpp')
7 | OBJ=$(SRC:.cpp=.o)
8 | DEP=$(OBJ:.o=.d)
9 |
10 | $(TARGET): $(OBJ) $(UWS)
11 | cd uWebSockets/uSockets && WITH_SSL=0 make CC=$(CC)
12 | $(CXX) -flto $(CXXFLAGS) $(OBJ) -o $(TARGET) $(LDFLAGS) uWebSockets/uSockets/*.o;
13 |
14 | %.d: %.cpp
15 | @$(CXX) $(CXXFLAGS) $< -MM -MT $(@:.d=.o) >$@
16 |
17 | src/%.o: %.cpp
18 | $(CXX) -c $(CXXFLAGS) -o $@ $<
19 |
20 | -include $(DEP)
21 |
22 | .PHONY: clean
23 | clean:
24 | rm -f **/*.o
25 | rm -f GameServer
26 | rm -f **/*.d
27 | cd uWebSockets/uSockets && make clean
28 |
29 | debug: clean
30 | debug: CXXFLAGS += -g
31 | debug: $(TARGET)
32 |
33 | release: clean
34 | release: CXXFLAGS += -O2
35 | release: $(TARGET)
36 |
37 | all: $(TARGET)
38 |
--------------------------------------------------------------------------------
/game/compile_flags.txt:
--------------------------------------------------------------------------------
1 | -xc++
2 | -std=c++20
3 | -Iincludes
4 | -IuWebSockets/src
5 | -IuWebSockets/uSockets/src
6 | -Iprometheus-cpp/core/include
7 |
--------------------------------------------------------------------------------
/game/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: "3.9" # optional since v1.27.0
2 | services:
3 | game-server:
4 | restart: always
5 | hostname: "game-server-${CHANNEL}"
6 | image: "ghcr.io/cgsdev0/rollycubes:${CHANNEL}"
7 | volumes:
8 | - ../../secrets:/secrets
9 | - data:/data
10 | environment:
11 | - CHANNEL
12 | external_links:
13 | - "auth:auth"
14 | - "random:random"
15 | networks:
16 | default:
17 | name: rollycubes
18 | external: true
19 |
20 | volumes:
21 | data:
22 | name: ${CHANNEL}gamedata
23 | external: true
24 |
--------------------------------------------------------------------------------
/game/run-server.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | mkdir -p ./secrets
4 | mkdir -p ./data
5 | touch ./secrets/.pre-shared-key
6 |
7 | while true;
8 | do
9 | pkill GameServer
10 | echo "==============================================="
11 | make -j$(( $(nproc) / 2 ))
12 | DEV=true ./GameServer &
13 | inotifywait -r src -e MODIFY
14 | done
15 |
--------------------------------------------------------------------------------
/game/src/AuthServerRequestQueue.h:
--------------------------------------------------------------------------------
1 | #include "Loop.h"
2 | #include
3 |
4 | typedef std::function Callback;
5 |
6 | class AuthServerRequestQueue {
7 | public:
8 | AuthServerRequestQueue(std::string baseUrl, uWS::Loop *loop);
9 | ~AuthServerRequestQueue();
10 |
11 | void send(std::string url, std::string json);
12 | void send(std::string url, std::string json, Callback cb);
13 |
14 | std::string baseUrl;
15 |
16 | private:
17 | class AuthServerRequestQueueImpl;
18 | AuthServerRequestQueueImpl *impl;
19 | };
20 |
--------------------------------------------------------------------------------
/game/src/Consts.h:
--------------------------------------------------------------------------------
1 | #ifndef INCLUDE_CONSTS_H
2 | #define INCLUDE_CONSTS_H
3 |
4 | #include
5 | #include
6 | #include
7 |
8 | using string = std::string;
9 | typedef unsigned int uint;
10 |
11 | static const uint MAX_PLAYERS = 8;
12 | static const uint MAX_CHAT_LOG = 100;
13 | static const uint DICE_COUNT = 2;
14 | static const uint SESSION_BYTES = 16;
15 | static const uint ROOM_LEN = 6;
16 | static const uint DEFAULT_PORT = 3001;
17 | static const auto EVICT_AFTER = std::chrono::minutes(10);
18 | static const uint EVICTION_LIMIT = 10;
19 | static const std::set TARGET_SCORES = {33, 66, 67, 98, 99, 100};
20 | static const uint MAX_PLAYER_NAME = 25;
21 | static const uint MAX_CHAT_LEN = 402 + MAX_PLAYER_NAME;
22 |
23 | const string PLAYER_COLORS[] = {
24 | "#e6194B",
25 | "#3cb44b",
26 | "#ffe119",
27 | "#4363d8",
28 | "#f58231",
29 | "#42d4f4",
30 | "#f032e6",
31 | "#fabed4",
32 | };
33 |
34 | /* ws->getUserData returns one of these (only for the home page) */
35 | struct HomeSocketData {
36 | };
37 |
38 | /* ws->getUserData returns one of these */
39 | struct PerSocketData {
40 | string session;
41 | string session_from_cookie;
42 | string room;
43 | string display_name;
44 | string user_id;
45 | bool is_verified;
46 | bool spectator;
47 | bool dedupe_conns;
48 | };
49 |
50 | #endif
51 |
--------------------------------------------------------------------------------
/game/src/GameCoordinator.h:
--------------------------------------------------------------------------------
1 | #ifndef INCLUDE_GAME_COORDINATOR_H
2 | #define INCLUDE_GAME_COORDINATOR_BASE_H
3 |
4 | #include "App.h"
5 | #include "Game.h"
6 | #include "api/API.hpp"
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 |
13 | class Metrics;
14 |
15 | class GameCoordinator {
16 |
17 | public:
18 | GameCoordinator(Metrics *_metrics) : metrics(_metrics) { }
19 | std::unordered_map games;
20 |
21 | std::unordered_set
22 | eviction_set;
23 | std::queue>
24 | eviction_queue;
25 |
26 | API::RoomListMsg list_rooms();
27 | void load_persistence();
28 | void runEviction(bool limited = true);
29 | void save_to_disk();
30 | std::string createRoom(bool isPrivate, std::string seed = "");
31 | void queue_eviction(std::string room);
32 |
33 | private:
34 | Metrics *metrics;
35 | };
36 |
37 | #endif // INCLUDE_GAME_COORDINATOR_H
38 |
--------------------------------------------------------------------------------
/game/src/HTTPClient.cpp:
--------------------------------------------------------------------------------
1 | #include "HTTPClient.h"
2 | #include "HTTPRequest.hpp"
3 |
4 | #include
5 |
6 | namespace http {
7 | std::string post(std::string url, std::string json, std::string preSharedKey) {
8 | http::Request request{url};
9 | http::Response response;
10 | if (preSharedKey.length()) {
11 | response = request.send("POST", json, {"Content-Type: application/json", "Authorization: Bearer " + preSharedKey});
12 | } else {
13 | response = request.send("POST", json, {"Content-Type: application/json"});
14 | }
15 | return std::string{response.body.begin(), response.body.end()};
16 | }
17 |
18 | std::string get(std::string url) {
19 | http::Request request{url};
20 | const auto response = request.send("GET");
21 | return std::string{response.body.begin(), response.body.end()};
22 | }
23 | } // namespace http
24 |
--------------------------------------------------------------------------------
/game/src/HTTPClient.h:
--------------------------------------------------------------------------------
1 |
2 | #include
3 |
4 | namespace http {
5 | std::string post(std::string url, std::string json, std::string preSharedKey = "");
6 | std::string get(std::string url);
7 | } // namespace http
8 |
--------------------------------------------------------------------------------
/game/src/JWTVerifier.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | struct ValidClaim {
6 | std::string user_id;
7 | std::string display_name;
8 | };
9 |
10 | struct JWTVerifier {
11 |
12 | JWTVerifier();
13 | ~JWTVerifier();
14 |
15 | // Must be called in order to fetch public key.
16 | void init(const std::string &baseUrl);
17 |
18 | ValidClaim decode_and_verify(const std::string &token) const;
19 |
20 | private:
21 | class VerifierImpl;
22 | VerifierImpl *impl;
23 | };
24 |
--------------------------------------------------------------------------------
/game/src/RequestQueue.h:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 |
5 | template
6 | class RequestQueue {
7 | private:
8 | std::mutex d_mutex;
9 | std::condition_variable d_condition;
10 | std::deque d_queue;
11 |
12 | public:
13 | void push(T const &value) {
14 | {
15 | std::unique_lock lock(this->d_mutex);
16 | d_queue.push_front(value);
17 | }
18 | this->d_condition.notify_one();
19 | }
20 |
21 | T pop() {
22 | std::unique_lock lock(this->d_mutex);
23 | this->d_condition.wait(lock, [this] { return !this->d_queue.empty(); });
24 | T rc(std::move(this->d_queue.back()));
25 | this->d_queue.pop_back();
26 | return rc;
27 | }
28 | };
29 |
--------------------------------------------------------------------------------
/game/src/RngOverTcp.h:
--------------------------------------------------------------------------------
1 | #ifndef INCLUDE_RNG_OVER_TCP_H
2 | #define INCLUDE_RNG_OVER_TCP_H
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 |
13 | #include
14 |
15 | void error(const char *msg)
16 | {
17 | std::cerr << "get rekt lol " << msg << std::endl;
18 | throw API::GameError({.error = "the dice are currently unavailable, because " + std::string(msg) });
19 | }
20 |
21 | struct FortranRngServer {
22 | std::vector roll() {
23 | int sockfd, portno, n;
24 | struct sockaddr_in serv_addr;
25 | struct hostent *server;
26 |
27 | char buffer;
28 | portno = 5456;
29 | sockfd = socket(AF_INET, SOCK_STREAM, 0);
30 | if (sockfd < 0) {
31 | error("ERROR opening socket");
32 |
33 | }
34 | server = gethostbyname("random");
35 | if (server == NULL) {
36 | error("ERROR, no such host");
37 | }
38 | bzero((char *) &serv_addr, sizeof(serv_addr));
39 | serv_addr.sin_family = AF_INET;
40 | bcopy((char *)server->h_addr,
41 | (char *)&serv_addr.sin_addr.s_addr,
42 | server->h_length);
43 | serv_addr.sin_port = htons(portno);
44 | if (connect(sockfd,(struct sockaddr *) &serv_addr,sizeof(serv_addr)) < 0) {
45 | error("ERROR connecting");
46 | }
47 | n = read(sockfd,&buffer,1);
48 | if (n < 0) {
49 | error("ERROR reading from socket");
50 | }
51 | printf("%d\n",buffer);
52 | int a = buffer % 6 + 1;
53 | int b = buffer / 6 + 1;
54 | close(sockfd);
55 | return {a, b};
56 | }
57 | };
58 | #endif
--------------------------------------------------------------------------------
/game/src/StringUtils.h:
--------------------------------------------------------------------------------
1 | #ifndef STRING_UTILS_H
2 | #define STRING_UTILS_H
3 |
4 | #include
5 |
6 | typedef unsigned int uint;
7 |
8 | // Trims a string to a certain amount of characters (unicode aware).
9 | std::string trimString(const std::string &a, int len, bool strict = false);
10 |
11 | std::string generateCode(const unsigned int len, std::string seed = "");
12 |
13 | #endif
14 |
--------------------------------------------------------------------------------
/game/src/achievements/All.cpp:
--------------------------------------------------------------------------------
1 | #include "All.h"
2 | #include "Astronaut.h"
3 | #include "Doubles.h"
4 | #include "GettingStarted.h"
5 | #include "Negative.h"
6 | #include "Oops.h"
7 | #include "Perfect.h"
8 | #include "Rude.h"
9 | #include "WinGames.h"
10 | #include "Thief.h"
11 |
12 | std::vector initAchievements() {
13 | std::vector achievements{
14 | new GettingStarted(),
15 | new WinGames("1"),
16 | new WinGames("2"),
17 | new WinGames("3"),
18 | new Doubles(1),
19 | new Doubles(2),
20 | new Doubles(3),
21 | new Negative(),
22 | new Rude(),
23 | new Oops(),
24 | new Perfect(),
25 | new Astronaut(),
26 | new Thief(),
27 | };
28 | return achievements;
29 | }
30 |
--------------------------------------------------------------------------------
/game/src/achievements/All.h:
--------------------------------------------------------------------------------
1 | #ifndef INCLUDE_ACHIEVEMENTS_ALL_H
2 | #define INCLUDE_ACHIEVEMENTS_ALL_H
3 |
4 | #include "BaseAchievement.h"
5 | #include
6 |
7 | std::vector initAchievements();
8 |
9 | #endif
10 |
--------------------------------------------------------------------------------
/game/src/achievements/Astronaut.h:
--------------------------------------------------------------------------------
1 |
2 | #ifndef INCLUDE_ACHIEVEMENTS_ASTRONAUT_H
3 | #define INCLUDE_ACHIEVEMENTS_ASTRONAUT_H
4 |
5 | #include "BaseAchievement.h"
6 |
7 | class Astronaut : public BaseAchievement {
8 | public:
9 | virtual int processEvent(const json &event, const API::GameState &before, const API::GameState &after, const std::string &session) {
10 | if (!before.victory && after.victory && getPlayer(after, session).score == 100) {
11 | return 1;
12 | }
13 | return 0;
14 | }
15 | // Get the achievement ID
16 | virtual std::string getAchievementID() {
17 | return "astronaut:1";
18 | }
19 | };
20 |
21 | #endif
22 |
--------------------------------------------------------------------------------
/game/src/achievements/BaseAchievement.h:
--------------------------------------------------------------------------------
1 | #ifndef INCLUDE_ACHIEVEMENTS_BASE_H
2 | #define INCLUDE_ACHIEVEMENTS_BASE_H
3 |
4 | #include "../Consts.h"
5 | #include "../api/API.hpp"
6 | #include
7 | #include
8 |
9 | using json = nlohmann::json;
10 |
11 | inline const API::ServerPlayer &getPlayer(const API::GameState &state, const std::string &session) {
12 | for (const auto &player : state.players) {
13 | if (player.session == session) {
14 | return player;
15 | }
16 | }
17 | throw API::GameError({.error = "unknown player"});
18 | }
19 |
20 | class BaseAchievement {
21 | public:
22 | virtual void removePlayer(const std::string &session) {}
23 | // Return the progress on this achievement
24 | virtual int processEvent(const json &event, const API::GameState &before, const API::GameState &after, const std::string &session) = 0;
25 | // Get the achievement ID
26 | virtual std::string getAchievementID() = 0;
27 |
28 | virtual ~BaseAchievement() {}
29 | };
30 |
31 | #endif
32 |
--------------------------------------------------------------------------------
/game/src/achievements/Doubles.h:
--------------------------------------------------------------------------------
1 |
2 | #ifndef INCLUDE_ACHIEVEMENTS_DOUBLES_H
3 | #define INCLUDE_ACHIEVEMENTS_DOUBLES_H
4 |
5 | #include "BaseAchievement.h"
6 | #include