diff --git a/.env.example b/.env.example
index 821e903..f46c8c7 100644
--- a/.env.example
+++ b/.env.example
@@ -1,5 +1,6 @@
POSTGRES_PORT=5432
EXPRESS_PORT=3000
+FRONTEND_PORT=8080
POSTGRES_USER=postgres
POSTGRES_PASSWORD=postgres
diff --git a/README.md b/README.md
index 71ff3b6..82d8404 100644
--- a/README.md
+++ b/README.md
@@ -1,163 +1,30 @@
# RaspberryRocketeer
-## Class Diagram of all classes
+## How to run
-```mermaid
-classDiagram
-direction BT
-class Collidable {
- collides(o: Entity) boolean
-}
-class Database {
- any _db
- any db
-}
-class Entity {
- constructor(position: Position, width: number, height: number, fill: number)
- Position _position
- number _width
- number _height
- number fill
- boolean _showHitbox
- update() void
- draw() void
- Position position
- number width
- number height
- boolean showHitbox
-}
-class Game {
- number id
- number score
- string playtime
- Date date
- number userId
-}
-class GamePgPromiseRepository {
- insert(game: Game) Promise~Game~
- serialize(raw: any) Game
-}
-class GameRepository {
- insert(game: Game) Promise~Game~
-}
-class HighscoreLeaderboard
-class HighscoreLeaderboardPgPromiseRepository {
- getAll() Promise~HighscoreLeaderboard~
- serialize(raw: any) HighscoreLeaderboard
-}
-class HighscoreLeaderboardRepository {
- getAll() Promise~HighscoreLeaderboard~
-}
-class Leaderboard
-class LeaderboardEntry {
- number username
- number rank
- T score
-}
-class Obstacle {
- constructor(position: Position, obstacleWidth: number, obstacleHeight: number, pipeImagePath: string)
- Pipe pipeTop
- Pipe pipeBottom
- number padding
- number speed
- number _distanceBetweenPipes
- number _startX
- resetPosition() void
- randomizeHeight() void
- randomRange(min: number, max: number) number
- update() void
- draw() void
- collides(o: Entity) boolean
- any startX
- any distanceBetweenPipes
-}
-class Pipe {
- constructor(positionX: number, width: number, height: number)
- any _image
- update() void
- draw() void
- move(speed: number) void
- collides(o: Entity) boolean
- any image
-}
-class Position {
- constructor(x: number, y: number)
- number _x
- number _y
- number x
- number y
-}
-class Raspberry {
- constructor(image: string)
- number lift
- number gravity
- number _velocity
- any _image
- Position position
- number maxVelocity
- number WIDTH
- number HEIGHT
- number FILL
- update() void
- applyGravity() void
- forceBoundaries() void
- boost() void
- draw() void
- number velocity
- any image
-}
-class TimeLeaderboard
-class TimeLeaderboardPgPromiseRepository {
- getAll() Promise~TimeLeaderboard~
- serialize(raw: any) TimeLeaderboard
-}
-class TimeLeaderboardRepository {
- getAll() Promise~TimeLeaderboard~
-}
-class User {
- number id
- string name
-}
-class UserPgPromiseRepository {
- getById(id: number) Promise~User~
- getByName(name: string) Promise~User~
- withIdExists(id: number) Promise~boolean~
- withNameExists(name: string) Promise~boolean~
- insert(user: Omit~User, "id"~) Promise~User~
- serialize(raw: any) User
-}
-class UserRepository {
- getById(id: number) Promise~User~
- getByName(name: string) Promise~User~
- withIdExists(userId: number) Promise~boolean~
- withNameExists(username: string) Promise~boolean~
- insert(user: Omit~User, "id"~) Promise~User~
-}
-class UserScores {
- number userId
- number highscore
- number totalScore
- string totalPlaytime
- number averageScore
- number gamesPlayed
-}
-class UserScoresPgPromiseRepository {
- getById(id: number) Promise~UserScores~
- serialize(raw: any) UserScores
-}
-class UserScoresRepository {
- getById(userId: number) Promise~UserScores~
-}
-
-GamePgPromiseRepository --> GameRepository
-HighscoreLeaderboardPgPromiseRepository --> HighscoreLeaderboardRepository
-Obstacle ..> Collidable
-Obstacle --> Entity
-Pipe ..> Collidable
-Pipe --> Entity
-Raspberry --> Entity
-TimeLeaderboardPgPromiseRepository --> TimeLeaderboardRepository
-UserPgPromiseRepository --> UserRepository
-UserScoresPgPromiseRepository --> UserScoresRepository
+### Copy .env
+First you need to copy the `.env.example`.
+```shell
+cp .env.example .env
```
+
+Note: It is recommended to change the values for the database user.
+
+### Install node packages
+Go into the frontend folder using
+```shell
+cd frontend
+```
+and run:
+```shell
+npm install
+```
+
+### Start the container (in the project root)
+
+```shell
+docker compose up --build
+```
+
+You can then access the website on `localhost:8080`
diff --git a/backend/api/src/gameRoute.ts b/backend/api/src/gameRoute.ts
index 2ac9175..0de3e2c 100644
--- a/backend/api/src/gameRoute.ts
+++ b/backend/api/src/gameRoute.ts
@@ -9,6 +9,9 @@ export const gameRoute = express.Router()
gameRoute.use(express.json())
+/**
+ * Test
+ */
gameRoute.post(
'/add',
body('playtime')
@@ -18,6 +21,8 @@ gameRoute.post(
body('userId')
.isInt({min: 1})
.custom(userWithIdExists),
+ body('score')
+ .isInt({min: 0}),
/**
* After processing the errors of express-validator, inserts the game into the DB
* @param req
diff --git a/backend/api/src/leaderboardRoute.ts b/backend/api/src/leaderboardRoute.ts
index e77fb2e..699180c 100644
--- a/backend/api/src/leaderboardRoute.ts
+++ b/backend/api/src/leaderboardRoute.ts
@@ -1,4 +1,5 @@
import express from 'express';
+import {query, validationResult} from 'express-validator';
import {TimeLeaderboardRepository} from "./repositories/TimeLeaderboardRepository.js";
import {TimeLeaderboardPgPromiseRepository} from "./repositories/pgPromise/TimeLeaderboardPgPromiseRepository.js";
import {HighscoreLeaderboardPgPromiseRepository} from "./repositories/pgPromise/HighscoreLeaderboardPgPromiseRepository.js";
@@ -8,6 +9,9 @@ import {HighscoreLeaderboard, TimeLeaderboard} from "./model/Leaderboard.js";
export const leaderboardRoute = express.Router()
leaderboardRoute.get('/highscore',
+ query('pagination').toBoolean(),
+ query('entriesPerPage').optional().isInt({min: 1}).toInt(),
+ query('page').optional().isInt({min: 0}).toInt(),
/**
* Returns the highscore leaderboard as JSON response, fetched from DB
* @param req
@@ -15,8 +19,21 @@ leaderboardRoute.get('/highscore',
*/
async (req, res) => {
try {
+ //region validate parameters
+ const errors = validationResult(req);
+ if (!errors.isEmpty()) {
+ return res.status(400).json({ errors: errors.array() });
+ }
+ //endregion
const highscoreLeaderboardRepo: HighscoreLeaderboardRepository = new HighscoreLeaderboardPgPromiseRepository;
- const highscoreLeaderboard: HighscoreLeaderboard = await highscoreLeaderboardRepo.getAll();
+ let highscoreLeaderboard: HighscoreLeaderboard;
+ if (req.query.pagination == true) {
+ const entriesPerPage = req.query.entriesPerPage;
+ const page = req.query.page;
+ highscoreLeaderboard = await highscoreLeaderboardRepo.getPage(entriesPerPage, page);
+ } else {
+ highscoreLeaderboard = await highscoreLeaderboardRepo.getAll();
+ }
res.send(highscoreLeaderboard);
} catch (error) {
// handle errors
@@ -26,6 +43,9 @@ leaderboardRoute.get('/highscore',
})
leaderboardRoute.get('/totalplaytime',
+ query('pagination').toBoolean(),
+ query('entriesPerPage').optional().isInt({min: 1}).toInt(),
+ query('page').optional().isInt({min: 0}).toInt(),
/**
* Returns the total playtime leaderboard as JSON response, fetched from DB
* @param req
@@ -33,8 +53,23 @@ leaderboardRoute.get('/totalplaytime',
*/
async (req, res) => {
try {
+ console.log(req.query)
+ //region validate parameters
+ const errors = validationResult(req);
+ if (!errors.isEmpty()) {
+ return res.status(400).json({ errors: errors.array() });
+ }
+ //endregion
const timeLeaderboardRepo: TimeLeaderboardRepository = new TimeLeaderboardPgPromiseRepository;
- const timeLeaderboard: TimeLeaderboard = await timeLeaderboardRepo.getAll();
+ let timeLeaderboard: TimeLeaderboard;
+ console.log(req.query)
+ if (req.query.pagination == true) {
+ const entriesPerPage = req.query.entriesPerPage;
+ const page = req.query.page;
+ timeLeaderboard = await timeLeaderboardRepo.getPage(entriesPerPage, page);
+ } else {
+ timeLeaderboard = await timeLeaderboardRepo.getAll();
+ }
res.send(timeLeaderboard);
} catch (error) {
// handle errors
diff --git a/backend/api/src/repositories/HighscoreLeaderboardRepository.ts b/backend/api/src/repositories/HighscoreLeaderboardRepository.ts
index 204feff..d70d3d5 100644
--- a/backend/api/src/repositories/HighscoreLeaderboardRepository.ts
+++ b/backend/api/src/repositories/HighscoreLeaderboardRepository.ts
@@ -2,4 +2,5 @@ import {HighscoreLeaderboard} from "../model/Leaderboard.js";
export abstract class HighscoreLeaderboardRepository {
abstract getAll(): Promise Position. Whether the hitbox (rectangular surrounding) is shown, or not. Width. Color. Set height. Set position. Set the hitbox's visibility. Set width. Set height. Set position. Set the hitbox's visibility. Set width.
@@ -88,7 +88,7 @@
Returns Entity
Properties
Private _positionPrivate _showPrivate _widthPrivate fillAccessors
Returns number
+value: number
Returns void
position
@@ -159,7 +159,7 @@
Returns Position
+value: Position
Returns void
show
@@ -181,7 +181,7 @@
Returns boolean
+value: boolean
Returns void
width
@@ -203,7 +203,7 @@
Returns number
+value: number
Returns void
Methods
Returns void
Abstract update
@@ -237,7 +237,7 @@
Returns void
diff --git a/docs/classes/Obstacle.Obstacle.html b/docs/classes/Obstacle.Obstacle.html
index f99d782..dd3b12b 100644
--- a/docs/classes/Obstacle.Obstacle.html
+++ b/docs/classes/Obstacle.Obstacle.html
@@ -30,7 +30,7 @@
+
@@ -99,39 +99,39 @@
Returns Obstacle
Properties
Private Readonly paddingPrivate pipePrivate pipePrivate Readonly speedStatic Private _distanceStatic Private _startXAccessors
Returns number
+Returns void
+position
@@ -168,7 +168,7 @@
Returns Position
+Returns void
+show
@@ -192,7 +192,7 @@
Returns boolean
+Returns void
+width
@@ -216,7 +216,7 @@
Returns number
+Returns void
+Static distance
@@ -242,7 +242,7 @@
value: number
Returns void
Static startX
@@ -255,7 +255,7 @@
value: number
Returns void
Methods
Returns boolean
Private create
@@ -296,7 +296,7 @@
pipeImagePath: string
Returns void
draw
@@ -307,7 +307,7 @@
Returns void
Private random
@@ -328,7 +328,7 @@
Returns number
+randomize
@@ -338,7 +338,7 @@
Returns void
+reset
@@ -349,7 +349,7 @@ Randomises the height of the pipes using the padding variable
Pipe's image.
Sets the image.
@@ -149,7 +149,7 @@Set position.
@@ -173,7 +173,7 @@Set the hitbox's visibility.
@@ -197,7 +197,7 @@Set width.
@@ -221,7 +221,7 @@X coordinate.
Private _yY coordinate.
Set x.
@@ -106,7 +106,7 @@Set y.
@@ -128,7 +128,7 @@Image for the raspberry.
Private _velocityCurrent speed.
Private Readonly gravityGravity applied.
Private Readonly liftAmount of lift applied when boosting.
Static Private Readonly FILLColor.
Static Private Readonly HEIGHTHeight.
Static Private Readonly WIDTHWidth.
Static Private Readonly maxMaximum velocity, so the raspberry doesn't get to infinite speed when boosting.
Static Private positionPosition.
Sets the image by path.
@@ -203,7 +203,7 @@Set position.
@@ -227,7 +227,7 @@Set the hitbox's visibility.
@@ -251,7 +251,7 @@Sets the velocity.
@@ -273,7 +273,7 @@Private boundaryPrivate boundaryPrivate drawPrivate drawPrivate drawPrivate forcePrivate set
Height.