Skip to content

An open source trivia game played in groups, in the style of Jeopardy. Written in Rust.

License

Notifications You must be signed in to change notification settings

JustAman62/OpenTriviaNight

Repository files navigation

Open Trivia Night

Open Trivia Night is a trivia game designed to be played in groups, heavily inspired by the likes of Jeopardy. Play now at OpenTriviaNight.com.

How to Play

The goal of the game is to answer trivia questions correctly before the other players. Questions have a points value associated, which are awarded to the player who first buzzes in with the correct answer.

One player designates themselves the Host, and creates the game. When a game is created, a 4 character Game ID is generated. Other players in the group can use this Game ID to join.

Once the Host starts the game, new contestants will no longer be able to join. Players already in the game can rejoin by using the same name, in case they get disconnected.

Detailed instructions on how to play are available on the homepage.

Question Sources

When the Host is creating a game, they have the choice of inputting questions themselves, or using one of the integrated question sources to generate categories of questions. These sources are The Trivia API and the Open Trivia Database. Both of these sources contain a mix of user-submitted questions and generated questions.

When using a question source, questions are generated one category at a time, with each category containing 5 questions. The points value of the question is calculated as <round-number> * <question-number> * 100. For example, each group of 5 questions in round 1 will have the points values 100, 200, 300, 400, 500. In round 2, these are all doubled.

When creating custom questions, there is no limit to the number of categories, or questions in each category, or points associated with the questions.

Contributions

Contributions of any kind are more than welcome. If you find a bug or need any help, please open a issue.

Self Hosting

Open Trivia Night is published as a simple docker image. The easiest way to self-host the site would be to simply pull the image from ghcr.io/brownknight/opentrivianight:latest. The image serves both the UI and the Rust backend.

Running with Docker Compose

Created a compose.yml file with the following contents:

services:
  opentrivianight-app:
    image: ghcr.io/brownknight/opentrivianight:latest
    container_name: opentrivianight-app
    ports:
      - "3000:3000"
    environment:
      NODE_ENV: production
      RUST_LOG: info

Then run docker compose up -d to start the application. Navigate to http://localhost:3000 to open the app.

Running with Docker

Simply pull and run the image, making sure to map to port 3000. Some optional environment variables are set to minimise logging.

docker run --pull always --name opentrivianight -e RUST_LOG=info -p 3000:3000 ghcr.io/brownknight/opentrivianight:latest

Once the app is running, navigate to http://localhost:3000 to open the app.

Running from Source

To run the app directly from source (e.g. during development) you'll need to run the UI and backend separately.

To run the UI, navigate to the ui directory and run npm run dev.

To run the server, navigate to the server-rs directory and run cargo run.

If you don't want to run in dev mode, you can manually compile the UI and backend and serve it in one executable call:

cd ui
npm run build # will build a static site to ui/dist

cd ../server-rs
# optional: set the build version based on git
export VERSION=$(git describe --tags | sed -e "s/-/./")
cargo build --release # will build the backend to server-rs/target/release/server-rs

cd ..

mkdir -p output/wwwroot
cp -r ui/dist/* output/wwwroot # the backend will serve static files from a wwwroot folder located in the working directory
cp server-rs/target/release/server-rs output

# set the backend logging level to somewhat minimal
export RUST_LOG=info

cd output
./server-rs

How the game works

To join a game, the client establishes a WebSocket connection to /api/stream/games/<game-id>/<player-role>/<username>, where:

  • <game-id> is the 4 character Game ID generated when the game was created
  • <player-role> is one of: Host, Contestant, or Spectator
  • <username> is their username which may be up to 20 characters long

When the connection is successfully established, the backend adds the player to the game, and broadcasts a message to all other players to tell them about the new player.

Internally, the WebSocket thread on the server subscribes to a Channel. Each Game has a single Channel associated to it, and all game actions are broadcast via this Channel. This way, all players are always kept up-to-date about the state of the game, as all actions completed in the game result in a message being sent to this Channel.

When the client wants to send a request, they send a UpdateGameRequest specifying the action they are requesting. This is sent fire-and-forget, there is no specific response generated and tracked for the request.

If the request is successful, the server will send an appropiate GameMessage on the WebSockets for all players.

If the request errors out, a GameMessage::ReportError will be generated, and the user who initiated the request will receive the message in their WebSocket. This usually includes a user-friendly error message that can be displayed. A common source of errors is when 2 players buzz in to answer a question at the same time, one of the players will always receive an error as their request will have been processed after the first player to buzz in.

On the server, all games are stored in a Arc<DashMap<K, V>>, where K is a String and V is a GameEntry. Using a DashMap means that all operations on a GameEntry require a lock, which makes operations on games completely thread-safe.

All requests are processed in the handle_game_request method. This method takes a RefMut of the game entry, which ensures that it is only called after a lock has been obtained on the whole game.

About

An open source trivia game played in groups, in the style of Jeopardy. Written in Rust.

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Languages