Update October 14, 2020: Both the server and the client app are now retired. Feel free to use the sources if you want to deploy your own server-client configuration of this project.
Open Live Trivia is an open-source multiplayer trivia game. This repo hosts the server-side part of the project. For the corresponding client app, please check this link.
- An entry is displayed every round
- Every player has 3 free attempts available to submit the correct answer
- Additional attempts will cost 1 point
- The answers are case insensitive
- Every 15 seconds, a new random character is revealed from the answer (defined as a split)
- Entries range from 10 to 100 points in value (based on their difficulty)
- Their value decreases as more characters are revealed
- The first player to submit the correct answer wins the prize and the round is over
- Users are able to request the game leaderboard
- There are 3 right levels that a user can have:
- Regular - type
0
- Moderator - type
1
- Admin - type
2
- Regular - type
- Users are able to report entries which they consider invalid / inappropriate while playing. Those can be reviewed later and then banned / dismissed by moderators or the admin.
- The admin is the only one able to grant or revoke extra rights to users
- All registered users are able to delete their accounts permanently
Player authentication is achieved through Firebase Auth. The client app will send a token to the server that is valid for one hour. That token is automatically refreshed by the client app when needed, so tokens sent to the server should always be up to date. Upon receiving an authorized request from the client app, the server sends that token through Firebase Admin in order to receive a decrypted payload that contains necessary info such as uid
(an unique id associated with the Google Account used to authenticate) and the exp
field (the expiration date of the currently used token). The following activity diagram showcases the authentication flow:
View the full resolution version
The communication of game-specific events between the server and the client is based on socket.io. This approach facilitates real-time bidirectional communication between the two systems, which is ideal for the purpose of an online multiplayer game. The following activity diagram showcases the flow of game-specific events between the client and the server, based on the rules mentioned above:
View the full resolution version
Base URL: https://releasetracker.app/open-live-trivia-api/v1/
For requests marked as π
, you need to have the Authorization
header set with your Firebase idToken
.
For requests marked as π
, the results will be paginated and pagination-specific query string can be passed to specify the page
number. Example: https://releasetracker.app/open-live-trivia-api/v1/user/leaderboard?page=2
.
For all POST / PUT requests that have a json body provided, you need to set the application/json
value for the Content-Type
header.
In the documentation, certain attributes displayed with a colon in the beginning (e.g. :id
) need to be replaced with a corresponding value when you are making the call.
Profile image access url:
https://releasetracker.app/open-live-trivia-api-static/user-thumbnails/:filename
, where:
filename
=userId
+.png
Example: https://releasetracker.app/open-live-trivia-api-static/user-thumbnails/5d1f77e3adc09e1fe5a9aa9e.png
Access url: https://releasetracker.app/open-live-trivia-api/socket.io
Event | Description |
---|---|
authentication |
The first event sent by the client after socket connection. Pass the Firebase idToken in order to authenticate. |
ATTEMPT |
An attempt to submit the correct answer for the ongoing round |
REACTION |
An emoji that will broadcast to all the current players |
REPORT_ENTRY |
Report the ongoing entry of this round for further review by moderators or admin |
REQUEST_PLAYER_LIST |
Request the list of currently playing users |
Note: Events written in CAPS are game-specific events.
- Example bodies:
authentication
:
{ "idToken": "YOUR_ID_TOKEN" }
ATTEMPT
:
{ "message": "Funcrusher Plus" }
REACTION
Note: The events not listed in the examples above don't require request bodies.{ "emoji": "π" }
Event | Description |
---|---|
authenticated |
Sent to the client which authenticated successfully |
unauthorized |
Sent to the client which failed to authenticate |
WELCOME |
The first event sent by the server when a client is connected and authenticated |
PEER_JOIN |
Broadcasted to all connected clients when a new client is connected and successfully authenticated |
PEER_ATTEMPT |
Broadcasted to all connected clients when a client sent an attempt to the server |
INSUFFICIENT_FUNDS |
Sent to the client who previously sent an attempt that he was unable to pay for |
COIN_DIFF |
Sent to the client who previously sent an attempt if there was a change in his coin bank (like the price paid for the attempt and / or the reward received for the correct answer) |
PEER_REACTION |
Broadcasted to all connected clients when a client sent a reaction to the server |
ROUND |
Broadcasted to all connected clients when a new round starts |
SPLIT |
Broadcasted to all connected clients when a new split starts |
REVEAL |
Broadcasted to all connected clients when the last split finishes and no one submitted the right answer |
ENTRY_REPORTED_OK |
Sent to the client who previously reported an entry, if the entry report was saved successfully |
ENTRY_REPORTED_ERROR |
Sent to the client who previously reported an entry, if the entry report failed to save |
PLAYER_LIST |
Sent to the client who previously requested a list of all the current players |
PEER_LEFT |
Broadcasted to all connected clients when a client left the game session |
- Examples bodies:
unauthorized
:
{ "message": "User id not found for the specified token, you need to register first" }
WELCOME
{ "gameState": 1, "userCoins": 100, "entryId": 121885, "category": "we governed that state", "clue": "Pat Brown,Pete Wilson", "answer": "C____o___a", "currentValue": 14, "elapsedSplitSeconds": 7, "totalSplitSeconds": 15, "freeAttemptsLeft": 2, "entryReported": false, "players": 1, "attempts": [ { "userId": "5d1f2052a93b8d38b87750d3", "username": "Radu", "message": "test attempt", "correct": false } ] }
PEER_JOIN
{ "userId": "5d1f2052a93b8d38b87750d3", "username": "Radu" }
PEER_ATTEMPT
{ "userId": "5d1f2052a93b8d38b87750d3", "username": "Radu", "message": "test attempt", "correct": false }
COIN_DIFF
{ "coinDiff": 6 }
PEER_REACTION
{ "userId": "5d1f2052a93b8d38b87750d3", "username": "Radu", "emoji": "πΎ" }
ROUND
{ "entryId": 121885, "category": "we governed that state", "clue": "Pat Brown,Pete Wilson", "answer": "__________", "currentValue": 20 }
SPLIT
{ "answer": "C_li_ornia", "currentValue": 4 }
REVEAL
{ "answer": "California" }
PLAYER_LIST
{ "players": [ { "_id": "5d1f2052a93b8d38b87750d3", "username": "Radu", "rights": 0, "coins": 106, "joined": "2019-07-05T10:41:44.203Z" } ] }
PEER_LEFT
{ "userId": "5d1f2052a93b8d38b87750d3", "username": "Radu" }
Request Body Parameters:
username
- String (required)
Example Request Body:
{
"username": "Radu"
}
Example Response Body 201 CREATED
:
{
"_id": "5d1f2052a93b8d38b87750d3",
"username": "Radu",
"rights": 0,
"coins": 100,
"joined": "2019-07-05T10:41:44.203Z"
}
Specific restrictions:
- Max username length: 50 characters
- Usernames must be unique (otherwise,
409 CONFLICT
will be returned) - Usernames are considered unique on a case insensitive basis (e.g. if
Radu
is registered, trying to registerradu
will result in a conflict error) - Note: spaces are allowed in usernames
Example Response Body 200 OK
:
{
"_id": "5d1f2052a93b8d38b87750d3",
"username": "Radu",
"rights": 0,
"coins": 100,
"joined": "2019-07-05T10:41:44.203Z"
}
Example Response Body 200 OK
:
{
"message": "Account removed successfully"
}
Specific restrictions:
- Admins are not allowed to remove their accounts
GET
user/availability/:username
Request URL parameters:
username
- candidate username (required)
Response codes:
200 OK
- Username is available for registration409 CONFLICT
- Username is unavailable for registration
PUT
user/rights/:user_id/:rights_level
Request URL parameters:
user_id
- the id of the target user (required)rights_level
- one of the following values: (required)0
- Regular1
- Moderator2
- Admin
Example Response Body 200 OK
:
{
"message": "Chad's rights changed to type 1"
}
Example Response Body 200 OK
:
{
"page": 1,
"pages": 1,
"itemsCount": 1,
"perPage": 20,
"items": [
{
"_id": "5d1f2968a93b8d38b87750d4",
"rights": 2,
"coins": 100,
"lastSeen": "2019-07-05T10:41:44.203Z",
"joined": "2019-07-05T10:41:44.203Z",
"username": "Radu",
"playing": true
}
]
}
Example Response Body 200 OK
:
{
"_id": "5d1f2052a93b8d38b87750d3",
"username": "Radu",
"rights": 0,
"coins": 100,
"joined": "2019-07-05T10:41:44.203Z"
}
GET
reported_entry/get_reports
Request URL query string parameters:
banned
- Boolean (optional)
Example Response Body 200 OK
:
{
"page": 1,
"pages": 1,
"itemsCount": 1,
"perPage": 10,
"items": [
{
"reporters": [
{
"_id": "5d1f2968a93b8d38b87750d4",
"username": "Radu"
}
],
"banned": false,
"_id": "5d1f379a78c6e7342c49488e",
"lastReported": "2019-07-05T11:42:18.216Z",
"entryId": 34770,
"category": "life science",
"clue": "The monera kingdom consists of bacteria & the blue-green species of this",
"answer": "Algae"
}
]
}
Note: Entries currently running in the game will be excluded from the results.
PUT
reported_entry/ban/:report_id
Request URL parameters:
report_id
- the id of the target report (required)
Example Response Body 200 OK
:
{
"message": "The entry has been banned successfully"
}
PUT
reported_entry/unban/:report_id
Request URL parameters:
report_id
- the id of the target report (required)
Example Response Body 200 OK
:
{
"message": "The entry has been unbanned successfully"
}
PUT
reported_entry/dismiss/:report_id
Request URL parameters:
report_id
- the id of the target report (required)
Example Response Body 200 OK
:
{
"message": "Entry report dismissed successfully"
}
POST
system/disconnect_everyone
Example Response Body 200 OK
:
{
"message": "Sent the disconnect signal to 10 clients"
}
Example Response Body 200 OK
:
{
"serverVersion": "1.0.0",
"minAppVersionCode": 1,
"latestAppVersionCode": 1,
"isTriviaServiceRunning": true
}
Apache License 2.0, see the LICENSE file for details.