diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..673b86c --- /dev/null +++ b/.dockerignore @@ -0,0 +1,6 @@ +**/node_modules +**/.cache +**/.DS_Store +**/storage +**/dist +**/.env diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..ff62d58 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,33 @@ +FROM node:18-alpine + +RUN apk add g++ make py3-pip + +RUN mkdir -p /usr/app +WORKDIR /usr/app + +ENV API_PATH="/api" + +COPY game/package*.json ./ +RUN npm install --quite + +COPY game . +RUN npm run build + +FROM node:18-alpine +RUN mkdir -p /usr/app/public +WORKDIR /usr/app + +ENV STATIC="/usr/app/public" +ENV PORT=80 +EXPOSE 80/tcp + +COPY --from=0 /usr/app/dist ./public + +COPY server/package*.json ./ +RUN npm install --quite + +COPY server . +COPY ./run.sh . + +CMD ./run.sh + diff --git a/README.md b/README.md index f1614c1..4417747 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,67 @@ Tested on node `v14.17.3` -You can play the game [here](#) +You can play the game [here](https://mystifying-hypatia-4b1cef.netlify.app/) (unfortunelly I can not setup server, so this instance does not provide a ranking πŸ˜₯). +Please be patient waiting for the game to load. My crappy, free hosting need some time to load assets. -In the `game` directory there game client. -In the `server` directory there are authentiaction and ranking server. +The game was created for the first edition of [Hackerspace TrΓ³jmiasto's Community](https://github.com/hs3city/hs3-jam) Jam. -To start the app locally, you need... *TODO* :) +![Screenshot](ss.jpg) + +## How to start a game + +You need the [NodeJS](https://nodejs.org/en/) installed. + +~~The game's server do not serve game client's files. They are separately apps.~~ +The server is responsible for authentication and storing records. +Data is stored as plain JSON files in `server/storage` directory. +You do not have to setup any external database :) + +The game *should* work without server, with some limitations. +Space Smasher works fine on desktop, and not on mobile at all. + +Keys: `WSAD + Mouse` + +## Use Docker + +### LETS GO +```sh +# run as a daemon +🐧 docker run -d -p 80:80 --restart=always --name smasher ghcr.io/kgrzeg/space-smasher-9001:latest +# or run in foreground +🐧 docker run -ti -p 80:80 --rm --name smasher ghcr.io/kgrzeg/space-smasher-9001:latest +``` + +### Build image locally +```sh +🐧 docker build -t space-smasher-9001:latest . +# run as a daemon +🐧 docker run -d -p 80:80 --restart=always --name smasher space-smasher-9001 +# or run in foreground +🐧 docker run -ti -p 80:80 --rm --name smasher space-smasher-9001 +``` + +## Setup for development +### run server +```sh +🐧 cd server +🐧 npm install # install dependencies +🐧 cp .env.example .env +🐧 # edit .env file - UPDATE THE SECRET! +🐧 npm start # the server listening on port 3000. +``` + +### Setup and run client +```sh +🐧 cd .. # only if your cwd is server directory +🐧 cd game +🐧 npm install # install dependencies +🐧 # edit first line of src/api.ts to match your server +🐧 npm start # to run in Dev mode (hot reloading and recompilling) +🐧 npm run build +🐧 # your app is in dist directory, you need to serve it via www server +``` + +The most knowledge you can get by reading the sources or dockerfile. I will fill the readme one day... maybe. + +## Enjoy! diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..87db10f --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,10 @@ +version: '3' + +services: + smasher: + image: ghcr.io/kgrzeg/space-smasher-9001:latest + # build: . + container_name: space-smasher-9001 + ports: + - "80:80" + restart: always diff --git a/game/README.md b/game/README.md new file mode 100644 index 0000000..4d2635f --- /dev/null +++ b/game/README.md @@ -0,0 +1,5 @@ +# Start Game Client + +For more info look at main README.md + +I used [phaser3-typescript-parcel-template](https://github.com/ourcade/phaser3-typescript-parcel-template) as startup project (but typescript config was messed up) diff --git a/game/package-lock.json b/game/package-lock.json index eeab3ab..fc0705a 100644 --- a/game/package-lock.json +++ b/game/package-lock.json @@ -12,6 +12,7 @@ "phaser": "^3.55.2" }, "devDependencies": { + "@types/node": "^18.14.2", "@typescript-eslint/eslint-plugin": "^2.29.0", "@typescript-eslint/parser": "^2.29.0", "eslint": "^6.8.0", @@ -1790,6 +1791,12 @@ "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", "dev": true }, + "node_modules/@types/node": { + "version": "18.14.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.14.2.tgz", + "integrity": "sha512-1uEQxww3DaghA0RxqHx0O0ppVlo43pJhepY51OxuQIKHpjbnYLA7vcdwioNPzIqmC2u3I/dmylcqjlh0e7AyUA==", + "dev": true + }, "node_modules/@types/q": { "version": "1.5.5", "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.5.tgz", @@ -11286,6 +11293,12 @@ "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", "dev": true }, + "@types/node": { + "version": "18.14.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.14.2.tgz", + "integrity": "sha512-1uEQxww3DaghA0RxqHx0O0ppVlo43pJhepY51OxuQIKHpjbnYLA7vcdwioNPzIqmC2u3I/dmylcqjlh0e7AyUA==", + "dev": true + }, "@types/q": { "version": "1.5.5", "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.5.tgz", diff --git a/game/package.json b/game/package.json index 1bc0222..1d1f09a 100644 --- a/game/package.json +++ b/game/package.json @@ -4,9 +4,7 @@ "description": "Simple Phaser 3 game for the Hackerspace TrΓ³jmiasto's little game jam", "scripts": { "start": "parcel src/index.html -p 8000", - "build": "parcel build src/index.html --out-dir dist --no-source-maps", - "test": "echo \"Error: no test specified\" && exit 1", - "lint": "eslint ./src --ext .js,.jsx,.ts,.tsx" + "build": "parcel build src/index.html --out-dir dist --no-source-maps" }, "author": "Grzegorz Kupczyk", "license": "MIT", @@ -16,6 +14,7 @@ }, "homepage": "https://github.com/ourcade/phaser3-parcel-template", "devDependencies": { + "@types/node": "^18.14.2", "@typescript-eslint/eslint-plugin": "^2.29.0", "@typescript-eslint/parser": "^2.29.0", "eslint": "^6.8.0", diff --git a/game/readme.md b/game/readme.md deleted file mode 100644 index e5be41c..0000000 --- a/game/readme.md +++ /dev/null @@ -1,138 +0,0 @@ -This is a fork of [phaser3-typescript-parcel-template](https://github.com/ourcade/phaser3-typescript-parcel-template) - -![phaser3-parceljs-template](https://user-images.githubusercontent.com/2236153/71606463-37a0da80-2b2e-11ea-9b5f-5d26ccc84f91.png) - -# Phaser 3 + TypeScript + Parcel Template -> For people who want to spend time making Phaser 3 games in TypeScript instead of configuring build tools. - -![License](https://img.shields.io/badge/license-MIT-green) - -This is a TypeScript specific fork of [phaser3-parcel-template](https://github.com/ourcade/phaser3-parcel-template). - -## Prerequisites - -You'll need [Node.js](https://nodejs.org/en/), [npm](https://www.npmjs.com/), and [Parcel](https://parceljs.org/) installed. - -It is highly recommended to use [Node Version Manager](https://github.com/nvm-sh/nvm) (nvm) to install Node.js and npm. - -For Windows users there is [Node Version Manager for Windows](https://github.com/coreybutler/nvm-windows). - -Install Node.js and `npm` with `nvm`: - -```bash -nvm install node - -nvm use node -``` - -Replace 'node' with 'latest' for `nvm-windows`. - -Then install Parcel: - -```bash -npm install -g parcel-bundler -``` - -## Getting Started - -Clone this repository to your local machine: - -```bash -git clone https://github.com/ourcade/phaser3-typescript-parcel-template.git -``` - -This will create a folder named `phaser3-typescript-parcel-template`. You can specify a different folder name like this: - -```bash -git clone https://github.com/ourcade/phaser3-typescript-parcel-template.git my-folder-name -``` - -Go into your new project folder and install dependencies: - -```bash -cd phaser3-typescript-parcel-template # or 'my-folder-name' -npm install -``` - -Start development server: - -``` -npm run start -``` - -To create a production build: - -``` -npm run build -``` - -Production files will be placed in the `dist` folder. Then upload those files to a web server. πŸŽ‰ - -## Project Structure - -``` - . - β”œβ”€β”€ dist - β”œβ”€β”€ node_modules - β”œβ”€β”€ public - β”œβ”€β”€ src - β”‚ β”œβ”€β”€ scenes - β”‚ β”‚ β”œβ”€β”€ HelloWorldScene.ts - β”‚ β”œβ”€β”€ index.html - β”‚ β”œβ”€β”€ main.ts - β”œβ”€β”€ package.json -``` - -The contents of this template is the basic [Phaser 3 getting started example](http://phaser.io/tutorials/getting-started-phaser3/part5). - -This template assumes you will want to organize your code into multiple files and use TypeScript. - -TypeScript files are intended for the `src` folder. `main.ts` is the entry point referenced by `index.html`. - -Other than that there is no opinion on how you should structure your project. There is a `scenes` folder in `src` where the `HelloWorldScene.ts` lives but you can do whatever you want. - -## Static Assets - -Any static assets like images or audio files should be placed in the `public` folder. It'll then be served at http://localhost:8000/images/my-image.png - -Example `public` structure: - -``` - public - β”œβ”€β”€ images - β”‚ β”œβ”€β”€ my-image.png - β”œβ”€β”€ music - β”‚ β”œβ”€β”€ ... - β”œβ”€β”€ sfx - β”‚ β”œβ”€β”€ ... -``` - -They can then be loaded by Phaser with `this.image.load('my-image', 'images/my-image.png')`. - -## TypeScript ESLint - -This template uses a basic `typescript-eslint` set up for code linting. - -It does not aim to be opinionated. - -## Dev Server Port - -You can change the dev server's port number by modifying the `start` script in `package.json`. We use Parcel's `-p` option to specify the port number. - -The script looks like this: - -``` -parcel src/index.html -p 8000 -``` - -Change 8000 to whatever you want. - -## Other Notes - -[parcel-plugin-clean-easy](https://github.com/lifuzhao100/parcel-plugin-clean-easy) is used to ensure only the latest files are in the `dist` folder. You can modify this behavior by changing `parcelCleanPaths` in `package.json`. - -[parcel-plugin-static-files](https://github.com/elwin013/parcel-plugin-static-files-copy#readme) is used to copy static files from `public` into the output directory and serve it. You can add additional paths by modifying `staticFiles` in `package.json`. - -## License - -[MIT License](https://github.com/ourcade/phaser3-typescript-parcel-template/blob/master/LICENSE) diff --git a/game/src/api.ts b/game/src/api.ts index 560e649..768b036 100644 --- a/game/src/api.ts +++ b/game/src/api.ts @@ -1,4 +1,4 @@ -const baseApiPath = "http://localhost:3000" +const baseApiPath = process.env.API_PATH ?? "http://localhost:3000" export default { async post(path: string, data?: any) { diff --git a/game/src/gui.ts b/game/src/gui.ts index d2fcd93..7e8c239 100644 --- a/game/src/gui.ts +++ b/game/src/gui.ts @@ -139,9 +139,9 @@ declare global { } function logout() { - const sure = confirm("Are you sure you want to logout? " + - "You won't be able to login again without #key. " + - "Make sure you copied #key before log out!\n\n" + + const sure = confirm("Are you sure you want to log out? " + + "You won't be able to login again without the #key. " + + "Make sure you have copied the #key before log out!\n\n" + "The #key: " + window.myStuff.key) if (!sure) diff --git a/run.sh b/run.sh new file mode 100755 index 0000000..be1003e --- /dev/null +++ b/run.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +mkdir -p storage + +if [ ! -f ".env" ]; then + cp .env.example .env + sed -i "s#HVuwh1dmaFhg4q1fef7xI4D6UeV7ImGK6NTp7i9eH6qiyJ9kxsvqPu29JXnhNGQY#$(head -c 12 /dev/random | base64)#" .env +fi + +npm run start + diff --git a/server/.env.example b/server/.env.example index 65735ce..82ee1e0 100644 --- a/server/.env.example +++ b/server/.env.example @@ -1,2 +1,3 @@ -secret = oIMzR4YvM9x9NoPrQfk4 +secret = HVuwh1dmaFhg4q1fef7xI4D6UeV7ImGK6NTp7i9eH6qiyJ9kxsvqPu29JXnhNGQY enableJusticeGuard = 1 +PORT = 3001 diff --git a/server/Auth.js b/server/Auth.js index 621c128..6256e0e 100644 --- a/server/Auth.js +++ b/server/Auth.js @@ -9,7 +9,7 @@ export default { error: "Incorrect name" } - const re = /^[a-zA-z0-9_$][a-zA-z0-9 _$]{,15}$/ + const re = /^[a-zA-Z0-9_$][a-zA-Z0-9 _$]{1,15}$/ if (!re.test(name)) return { error: "Incorrect name" diff --git a/server/README.md b/server/README.md new file mode 100644 index 0000000..8cf536e --- /dev/null +++ b/server/README.md @@ -0,0 +1,5 @@ +# Start Game Server + +For more info look at main README.md + +I used [phaser3-typescript-parcel-template](https://github.com/ourcade/phaser3-typescript-parcel-template) as startup project (but typescript config was messed up) diff --git a/server/package.json b/server/package.json index c786caf..20b1855 100644 --- a/server/package.json +++ b/server/package.json @@ -4,7 +4,8 @@ "description": "", "main": "index.js", "scripts": { - "start": "nodemon --ignore ./storage/ --exec babel-node server.js" + "dedv": "nodemon --ignore ./storage/ --exec babel-node server.js", + "start": "babel-node server.js" }, "type": "module", "keywords": [], diff --git a/server/server.js b/server/server.js index 713d437..5ae1cd3 100644 --- a/server/server.js +++ b/server/server.js @@ -9,6 +9,8 @@ import auth from './Auth.js' import db from './Database.js' import jg from './JusticeGuard.js' +const router = express.Router(); + const protect = (req, res, next) => { const authHeader = req.headers.authorization; @@ -42,19 +44,21 @@ const neededArguments = (args) => { const app = express() app.use(morgan('combined')) -app.use(express.json()) app.use(cors()) -app.get("/", (_, res) => { +router.use(express.json()) + +router.get("/", (_, res) => { res.json({ message: "Hello! API here" }) }); -app.get("/secured", protect, (req, res) => { +router.get("/secured", protect, (req, res) => { res.json({ message: "You can see it!", payload: req.user }) }) -app.post("/signup", neededArguments(['name']), async (req, res) => { +router.post("/signup", neededArguments(['name']), async (req, res) => { + const user = await auth.createAccount(req.body.name) if (user.error) @@ -65,7 +69,7 @@ app.post("/signup", neededArguments(['name']), async (req, res) => { res.json(user) }) -app.post("/login", neededArguments(['key']), async (req, res) => { +router.post("/login", neededArguments(['key']), async (req, res) => { const user = auth.login(req.body.key) if (user.error) @@ -76,7 +80,7 @@ app.post("/login", neededArguments(['key']), async (req, res) => { res.json(user) }) -app.post("/record", protect, neededArguments(['points', 'shoots', 'time']), jg, async (req, res) => { +router.post("/record", protect, neededArguments(['points', 'shoots', 'time']), jg, async (req, res) => { db.updateRecord(req.user.name, req.body.points) const rank = db.getRank(req.user.name) @@ -88,14 +92,14 @@ app.post("/record", protect, neededArguments(['points', 'shoots', 'time']), jg, }) }) -app.get("/top", (req, res) => { +router.get("/top", (req, res) => { res.json({ status: "ok", records: db.getTop() }) }) -app.get("/start", protect, async (req, res) => { +router.get("/start", protect, async (req, res) => { await db.setLastPlayedToNow(req.user.name) res.json({ @@ -103,11 +107,22 @@ app.get("/start", protect, async (req, res) => { }) }) +if (process.env.STATIC) { + app.use(express.static(process.env.STATIC)); + app.use("/api", router); +} else { + app.use("/", router); +} + app.use(function (err, req, res, next) { if (err.name === 'UnauthorizedError') { res.status(401).send('invalid token...'); } }); -await db.read() -app.listen(3000) +(async function () { await db.read() })() + +const port = process.env.PORT || 3000 +app.listen(port, () => { + console.log(`Server listening on port ${port}!`); +}) diff --git a/ss.jpg b/ss.jpg new file mode 100644 index 0000000..6492311 Binary files /dev/null and b/ss.jpg differ