diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..4d8a0c5 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,11 @@ +FROM node:18-alpine +WORKDIR /usr/src/app +COPY package*.json ./ +RUN npm ci --omit=dev + +COPY html ./html +COPY views ./views +COPY src ./src + +CMD ["node", "src/index.js"] +EXPOSE 7865 8080 \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..ca2df0c --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,13 @@ +version: '3.3' + +services: + pixelbattle: + image: pixelbattle + build: . + ports: + - 8080:8080 + - 7865:7865 + volumes: + - ./board.png:/usr/src/app/board.png + - ./settings.json:/usr/src/app/settings.json + container_name: pixelbattle \ No newline at end of file diff --git a/package.json b/package.json index 154ef51..c5d6b85 100644 --- a/package.json +++ b/package.json @@ -6,8 +6,8 @@ "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "start": "node index.js", - "dev": "nodemon index.js", - "prod": "node index.js" + "dev": "nodemon src/index.js", + "prod": "node src/index.js" }, "repository": { "type": "git", diff --git a/settings.json b/settings.json index b82f23f..6e87484 100644 --- a/settings.json +++ b/settings.json @@ -3,6 +3,6 @@ "httpPort": 8080, "boardWidth": 1920, "boardHeight": 1080, - "serverAddress": "localhost", - "saveFile": "board.png" + "serverAddress": "foxarmy.org", + "saveFile": "../board.png" } \ No newline at end of file diff --git a/src/index.js b/src/index.js new file mode 100644 index 0000000..a17b094 --- /dev/null +++ b/src/index.js @@ -0,0 +1,156 @@ +const WebSocket = require('ws'); +const express = require('express'); +const http = express(); +const path = require('path'); +const pug = require('pug'); +const Jimp = require('jimp'); +const fs = require('fs'); + +http.use(express.static(path.join(__dirname, '../html'))); +http.set('view engine', 'pug'); + +const config = require("../settings.json"); +const httpPort = config.httpPort; +const appPort = config.appPort; +const serverAddress = config.serverAddress; +const saveFile = config.saveFile; +const boardWidth = config.boardWidth; +const boardHeight = config.boardHeight; + +var toQuit = false; + +var board = new Array(boardWidth * boardHeight * 4); + +if (!fs.existsSync(saveFile)) { + console.log("No save file found, creating blank board."); + board.fill(255); +} else { + console.log("Save file found, loading") + let image = Jimp.read(`./${saveFile}`, (err, image) => { + for (let x = 0; x < boardWidth; x ++) { + for (let y = 0; y < boardHeight; y ++) { + pixelNumber = evaulatePixelNumber(x * 4, y * 4); + let pixel = Jimp.intToRGBA(image.getPixelColor(x, y)) + board[pixelNumber + 0] = pixel.r + board[pixelNumber + 1] = pixel.g + board[pixelNumber + 2] = pixel.b + board[pixelNumber + 3] = 255; + } + } + console.log("Loaded") + }); + +} + + + + +const server = new WebSocket.Server({ + port: appPort +}); + +let clients = []; + +const evaulatePixelNumber = (x, y) => { + let pixelNumber; + if (y > 0) + pixelNumber = (y) * boardWidth + x; + if (y == 0) + pixelNumber = x; + return pixelNumber; +} + +server.on('connection', function(client) { + clients.push(client); + + // When you receive a message, send that message to every socket. + client.on('message', function(msg) { + let packet, content, code; + try { + packet = JSON.parse(msg.toString()); + content = packet.content; + code = packet.code; + } catch (e) {console.log(e)} + let response = {}; + switch(code) { + case 0: + response.code = 0; + response.content = board; + client.send(Buffer.from(JSON.stringify(response))); + break; + case 1: + response.code = 1; + response.content = content; + console.log(`response content ${response.content}`); + contentJson = JSON.parse(content); + let pixelNumber = evaulatePixelNumber(contentJson.x * 4, contentJson.y * 4); + if (contentJson.x < 0 || contentJson.y < 0) { + break; + } + board[pixelNumber + 0] = contentJson.r; + board[pixelNumber + 1] = contentJson.g; + board[pixelNumber + 2] = contentJson.b; + board[pixelNumber + 3] = 255; + clients.forEach(c => c.send(Buffer.from(JSON.stringify(response)))); + break; + default: + console.log("Packet cannot be understood: ", packet); + client.send("{\"code\":-1}"); + } + }); + + client.on('close', function() { + clients = clients.filter(s => s !== client); + }); +}); + +http.get('/', (req, res) => { + res.render('index.pug', {root: __dirname, server: serverAddress, port:appPort}); +}) + +http.listen(httpPort, "0.0.0.0", () => { + console.log(`Starting pixelbattle http server on port ${httpPort}`); +}) + +const save = (err, image) => { + if (err) throw err; + console.log() + for (let x = 0; x < boardWidth; x ++) { + for (let y = 0; y < boardHeight; y ++) { + let pixelNumber = evaulatePixelNumber(x * 4 , y * 4); + image.setPixelColor( + Jimp.rgbaToInt( + Number(board[pixelNumber + 0]), + Number(board[pixelNumber + 1]), + Number(board[pixelNumber + 2]), + Number(board[pixelNumber + 3]) + ), + x, y + ); + } + } + + image.write(`./${saveFile}`, (err) => { + if (err) throw err; + console.log("Saved") + if (toQuit) process.exit(); + }); +} + + +process.stdin.resume(); + +process.on('SIGUSR1', () => { + //save + console.log(`Caught SIGUSR1, saving ${boardWidth}x${boardHeight} image`) + toQuit = false; + let image = new Jimp(boardWidth, boardHeight, save); +}); + +process.on('SIGINT', () => { + //save + console.log(`Caught SIGINT, saving ${boardWidth}x${boardHeight} image and quitting`) + toQuit = true; + let image = new Jimp(boardWidth, boardHeight, save); +}); + \ No newline at end of file