From bda640fb436a3cc5acb4fcf04a853fc156b45e33 Mon Sep 17 00:00:00 2001 From: Joseph Garcia Date: Sun, 14 Apr 2024 06:06:17 -0700 Subject: [PATCH] Services 2 (#96) * wip * wip * more * wip * better * cool * nice * prompt --- game_server.js | 77 +------ package-lock.json | 11 +- package.json | 5 +- src/GameSession.js | 11 + src/child_game_server.js | 21 +- src/common/squish-map.js | 3 +- src/games/animation-test/index.js | 299 ++++++++++++++++++++++++++ src/games/infinite-questions/index.js | 34 ++- src/services/index.js | 85 ++++++++ 9 files changed, 452 insertions(+), 94 deletions(-) create mode 100644 src/games/animation-test/index.js create mode 100644 src/services/index.js diff --git a/game_server.js b/game_server.js index 2c5d9fd8..7393e45f 100755 --- a/game_server.js +++ b/game_server.js @@ -5,6 +5,7 @@ const { socketServer } = require('./src/util/socket'); const Homenames = require('./src/Homenames'); const path = require('path'); const baseDir = path.dirname(require.main.filename); +const { getService } = require('./src/services/index'); // const viewtest = require('./src/games/view-test'); const { getConfigValue } = require('homegames-common'); @@ -33,83 +34,9 @@ const server = (certPath, squishMap, username) => { let services = {}; - const supportedServices = { - 'contentGenerator': { - requestContent: (request) => new Promise((resolve, reject) => { - const makePost = (path, _payload) => new Promise((resolve, reject) => { - const payload = JSON.stringify(_payload); - - let module, hostname, port; - - module = https; - port = 443; - hostname = 'api.homegames.io'; - - const headers = {}; - - Object.assign(headers, { - 'Content-Type': 'application/json', - 'Content-Length': payload.length - }); - - const options = { - hostname, - path, - port, - method: 'POST', - headers - }; - - let responseData = ''; - - const req = module.request(options, (res) => { - res.on('data', (chunk) => { - responseData += chunk; - }); - - res.on('end', () => { - resolve(responseData); - }); - }); - - req.write(payload); - req.end(); - }); - - makePost('https://api.homegames.io/services', request).then((response) => { - if (!response.startsWith('{')) { - reject(response); - } else { - const requestId = JSON.parse(response).requestId; - const interval = setInterval(() => { - https.get(`https://api.homegames.io/service_requests/${requestId}`, {}, (res) => { - let bufs = []; - res.on('data', (chunk) => { - bufs.push(chunk); - }); - - res.on('end', () => { - const fin = JSON.parse(Buffer.concat(bufs)); - const parsed = fin;//JSON.parse(fin); - if (parsed.response) { - clearInterval(interval); - resolve(parsed.response); - } - }); - }); - }, 5000); - } - }).catch(err => { - reject(err); - }); - }) - } - }; - if (customStartModule?.metadata) { const requestedServices = customStartModule.metadata().services || []; - services = {}; - requestedServices.forEach(s => services[s] = supportedServices[s]); + requestedServices.forEach(s => services[s] = getService(s)); } const dashboard = customStartModule ? new customStartModule({ diff --git a/package-lock.json b/package-lock.json index de6864fa..e7228abc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "homegames-core", - "version": "1.0.29", + "version": "1.0.30", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "homegames-core", - "version": "1.0.29", + "version": "1.0.30", "license": "ISC", "dependencies": { "acorn": "^8.11.3", @@ -21,6 +21,7 @@ "squish-1004": "npm:squishjs@1.0.4", "squish-1005": "npm:squishjs@1.0.5", "squish-1006": "npm:squishjs@1.0.6", + "squish-1007": "npm:squishjs@1.0.7", "ws": "7.4.6" } }, @@ -690,6 +691,12 @@ "resolved": "https://registry.npmjs.org/squishjs/-/squishjs-1.0.6.tgz", "integrity": "sha512-H+k0Dmtt5r3YWZIx2x4u3J7OG5mrtYTxA4dk3Mjf1/TEmb7pTdxbyi75sbY4VwvDaC+/I0u5+Cwz2WsBu+nbtg==" }, + "node_modules/squish-1007": { + "name": "squishjs", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/squishjs/-/squishjs-1.0.7.tgz", + "integrity": "sha512-sdWhY9fVtDkuDJQxQkP3B/Nqs2VUkFELjNUv70svlDUKxn5CMp+MGmgR7GN5uZiKJThidHxamjua3eFCNBZEnA==" + }, "node_modules/string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", diff --git a/package.json b/package.json index 90c95f30..1c009449 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "homegames-core", - "version": "1.0.29", + "version": "1.0.30", "description": "Play games at home", "directories": { "lib": "lib" @@ -28,6 +28,7 @@ "homepage": "https://github.com/homegamesio/homegames#readme", "dependencies": { "acorn": "^8.11.3", + "decompress": "^4.2.1", "homegames-common": "1.0.9", "squish-0756": "npm:squishjs@1.0.4", "squish-0762": "npm:squishjs@1.0.4", @@ -38,7 +39,7 @@ "squish-1004": "npm:squishjs@1.0.4", "squish-1005": "npm:squishjs@1.0.5", "squish-1006": "npm:squishjs@1.0.6", - "decompress": "^4.2.1", + "squish-1007": "npm:squishjs@1.0.7", "ws": "7.4.6" } } diff --git a/src/GameSession.js b/src/GameSession.js index 05ab8253..e2af4c70 100644 --- a/src/GameSession.js +++ b/src/GameSession.js @@ -275,6 +275,17 @@ class GameSession { // } } else if (input.type === 'mouseup') { this.game.handleMouseUp && this.game.handleMouseUp(playerId, input.data); + } else if (input.type === 'onhover') { + const node = this.game.findNode(input.nodeId) || this.customTopLayer.root.findChild(input.nodeId); + if (node && node.node?.onHover) { + node.node.onHover(playerId); + } + } else if (input.type === 'offhover') { + const node = this.game.findNode(input.nodeId) || this.customTopLayer.root.findChild(input.nodeId); + if (node && node.node?.offHover) { + node.node.offHover(playerId); + } + } else { log.info('Unknown input type: ', input.type); } diff --git a/src/child_game_server.js b/src/child_game_server.js index 74dcd0bb..fdb72e81 100644 --- a/src/child_game_server.js +++ b/src/child_game_server.js @@ -4,6 +4,7 @@ const crypto = require('crypto'); const fs = require('fs'); const { socketServer } = require('./util/socket'); const { reportBug } = require('./common/util'); +const { getService } = require('./services/index'); const process = require('process'); @@ -37,9 +38,17 @@ const startServer = (sessionInfo) => { try { log.info("THIS IS SESSION INFO"); log.info(sessionInfo); - + + let services = {}; + if (sessionInfo.gamePath) { const _gameClass = require(sessionInfo.gamePath); + + if (_gameClass.metadata) { + const requestedServices = _gameClass.metadata().services || []; + requestedServices.forEach(s => services[s] = getService(s)); + } + let saveData; const savePath = crypto.createHash('md5').update(sessionInfo.gamePath).digest('hex'); @@ -60,9 +69,15 @@ const startServer = (sessionInfo) => { console.log(err); } } - gameInstance = new _gameClass({ addAsset, saveGame, saveData }); + gameInstance = new _gameClass({ addAsset, saveGame, saveData, services }); } else { - gameInstance = new games[sessionInfo.key]({ addAsset }); + const _gameClass = games[sessionInfo.key]; + + if (_gameClass.metadata) { + const requestedServices = _gameClass.metadata().services || []; + requestedServices.forEach(s => services[s] = getService(s)); + } + gameInstance = new _gameClass({ addAsset, services }); } gameSession = new GameSession(gameInstance, sessionInfo.port, sessionInfo.username); diff --git a/src/common/squish-map.js b/src/common/squish-map.js index b7661573..a8b2d41a 100644 --- a/src/common/squish-map.js +++ b/src/common/squish-map.js @@ -21,5 +21,6 @@ module.exports = { '1000': isMain ? require.resolve('squish-1000') : electronOverrideVersion, '1004': require.resolve('squish-1004'), '1005': require.resolve('squish-1005'), - '1006': require.resolve('squish-1006') + '1006': require.resolve('squish-1006'), + '1007': require.resolve('squish-1007') }; diff --git a/src/games/animation-test/index.js b/src/games/animation-test/index.js new file mode 100644 index 00000000..bb512881 --- /dev/null +++ b/src/games/animation-test/index.js @@ -0,0 +1,299 @@ +const { Asset, Game, GameNode, Colors, Shapes, ShapeUtils } = require('squish-1007'); + +class AnimationTest extends Game { + static metadata() { + return { + aspectRatio: {x: 16, y: 9}, + author: 'Joseph Garcia', + thumbnail: 'f103961541614b68c503a9ae2fd4cc47', + squishVersion: '1007', + tickRate: 60 + }; + } + + constructor() { + super(); + + this.currentFrame = 1; + this.frameRate = 30; + + this.assets = { + 'frame_1': new Asset({ + 'id': 'b7d5c5720146b248f2968a5698d47d34', + 'type': 'image' + }), + 'frame_2': new Asset({ + 'id': '9c16e5fea84b3f1c9df0ad82792f7b5b', + 'type': 'image' + }), + 'frame_3': new Asset({ + 'id': '474967a53dfa25d7b9bd6e592fbd4cd0', + 'type': 'image' + }), + 'frame_4': new Asset({ + 'id': '74a2bdb81449877b4d2ff8526240b30f', + 'type': 'image' + }), + 'frame_5': new Asset({ + 'id': '6de58edbfd09a8249965c5ed5c401b76', + 'type': 'image' + }), + 'frame_6': new Asset({ + 'id': '82a341cee278824aa82ed98833727064', + 'type': 'image' + }), + 'frame_7': new Asset({ + 'id': 'a9d51822c7fe1440d9d1359f62cba260', + 'type': 'image' + }), + 'frame_8': new Asset({ + 'id': '4def94e5207eb6dbf7131c0efd01ded8', + 'type': 'image' + }), + 'frame_9': new Asset({ + 'id': '74112f7093e5d328ba84c9c512615a81', + 'type': 'image' + }), + 'frame_10': new Asset({ + 'id': '0caec7069d2847587dc7467e0104044a', + 'type': 'image' + }), + 'frame_11': new Asset({ + 'id': 'e9f6215f74a8075d0ee49ebc37b7ef53', + 'type': 'image' + }), + 'frame_12': new Asset({ + 'id': 'eda8942e37a94592179618f27d29f030', + 'type': 'image' + }), + 'frame_13': new Asset({ + 'id': 'ee28596dd872144b6f35b328c413c4ea', + 'type': 'image' + }), + 'frame_14': new Asset({ + 'id': 'cbfa874e0478b0ec725e48e9f84cd61d', + 'type': 'image' + }), + 'frame_15': new Asset({ + 'id': '075c025b39869556da5b34863a2fa08f', + 'type': 'image' + }), + 'frame_16': new Asset({ + 'id': '155d43678801b7595ab7d7f3e8515aff', + 'type': 'image' + }), + 'frame_17': new Asset({ + 'id': 'ba7cf5eda822e0da3fb515f3469906ec', + 'type': 'image' + }), + 'frame_18': new Asset({ + 'id': '347b8f82ccc82e8125f4fc054a9dd7ec', + 'type': 'image' + }), + 'frame_19': new Asset({ + 'id': '771654be56b6f8ac24e1c1f4330e0e82', + 'type': 'image' + }), + 'frame_20': new Asset({ + 'id': 'c71522cc122b550a0da14137ab22e445', + 'type': 'image' + }), + 'frame_21': new Asset({ + 'id': '6b52130645fe40af0489b72b5d4a9de8', + 'type': 'image' + }), + 'frame_22': new Asset({ + 'id': '82c07463d2510e1afa1132b6e63a878d', + 'type': 'image' + }), + 'frame_23': new Asset({ + 'id': '4fd5523c225ca78ed3f779d36417cd9e', + 'type': 'image' + }), + 'frame_24': new Asset({ + 'id': 'dc3f39a8a49146822c036bef6016ee23', + 'type': 'image' + }), + 'frame_25': new Asset({ + 'id': 'cdf359e561421393cb0575a8741b9e2f', + 'type': 'image' + }), + 'frame_26': new Asset({ + 'id': '782ee30969eb50c0fd4e79cb6a353706', + 'type': 'image' + }), + 'frame_27': new Asset({ + 'id': '5e7c4aa25db49e2581d0d2d9993f5f88', + 'type': 'image' + }), + 'frame_28': new Asset({ + 'id': '1344a16df085d05c6e4c0084e4f5e7de', + 'type': 'image' + }), + 'frame_29': new Asset({ + 'id': '22039e8f2cf4e5eddf6aecc8990e6058', + 'type': 'image' + }), + 'frame_30': new Asset({ + 'id': '437678f66b5d68a0894e0e0440881156', + 'type': 'image' + }) + }; + + this.base = new GameNode.Shape({ + shapeType: Shapes.POLYGON, + coordinates2d: [ + [0, 0], + [100, 0], + [100, 100], + [0, 100], + [0, 0] + ], + fill: Colors.COLORS.PURPLE + }); + + this.imageNode = new GameNode.Asset({ + coordinates2d: ShapeUtils.rectangle(0, 0, 100, 100), + assetInfo: { + 'frame_1': { + 'pos': { + x: 0, + y: 0 + }, + 'size': { + x: 100, + y: 100 + } + } + } + }); + + this.paused = false; + + const playPauseText = new GameNode.Text({ + textInfo: { + x: 92.5, + y: 76, + size: 1.2, + text: 'Pause', + color: Colors.COLORS.BLACK, + align: 'center' + } + }); + + this.playPause = new GameNode.Shape({ + shapeType: Shapes.POLYGON, + coordinates2d: ShapeUtils.rectangle(87.5, 73.5, 10, 8), + fill: Colors.COLORS.HG_BLUE, + onClick: () => { + if (this.paused) { + this.paused = false; + } else { + this.paused = true; + } + + const curLabel = Object.assign({}, playPauseText.node.text); + curLabel.text = this.paused ? 'Resume' : 'Pause'; + + playPauseText.node.text = curLabel; + playPauseText.node.onStateChange(); + } + }); + + const frameRateText = new GameNode.Text({ + textInfo: { + x: 82.5, + y: 75, + text: `${this.frameRate}fps`, + color: Colors.COLORS.BLACK, + align: 'center', + size: 2 + } + }); + + const frameRateUpText = new GameNode.Text({ + textInfo: { + x: 82.5, + y: 62.5, + text: '\u2191', + color: Colors.COLORS.BLACK, + align: 'center', + size: 4 + } + }); + + const frameRateDownText = new GameNode.Text({ + textInfo: { + x: 82.5, + y: 82.5, + text: '\u2193', + color: Colors.COLORS.BLACK, + align: 'center', + size: 4 + } + }); + + this.frameRateUp = new GameNode.Shape({ + shapeType: Shapes.POLYGON, + coordinates2d: ShapeUtils.rectangle(80, 62.5, 5, 8), + onClick: () => { + this.frameRate = Math.min(30, this.frameRate + 1); + const curTextInfo = Object.assign({}, frameRateText.node.text); + curTextInfo.text = `${this.frameRate}fps`; + frameRateText.node.text = curTextInfo; + frameRateText.node.onStateChange(); + } + }); + + this.frameRateDown = new GameNode.Shape({ + shapeType: Shapes.POLYGON, + coordinates2d: ShapeUtils.rectangle(80, 82.5, 5, 10), + onClick: () => { + this.frameRate = Math.max(1, this.frameRate - 1); + const curTextInfo = Object.assign({}, frameRateText.node.text); + curTextInfo.text = `${this.frameRate}fps`; + frameRateText.node.text = curTextInfo; + frameRateText.node.onStateChange(); + } + }); + + this.frameRateDown.addChild(frameRateDownText); + this.frameRateUp.addChild(frameRateUpText); + + this.playPause.addChild(playPauseText); + + this.base.addChildren(this.imageNode, this.playPause, this.frameRateDown, this.frameRateUp, frameRateText); + + this.layers = [ + { + root: this.base + } + ]; + + } + + getLayers() { + return this.layers; + } + + getAssets() { + return this.assets; + } + + tick() { + const now = Date.now(); + if (!this.paused && (!this.nextRenderTime || this.nextRenderTime <= now)) { + const newFrame = this.currentFrame + 1 > Object.keys(this.assets).length ? 1 : this.currentFrame + 1; + const currentAssetInfo = Object.values(this.imageNode.node.asset)[0]; + this.imageNode.node.asset = { + [`frame_${newFrame}`]: currentAssetInfo + }; + + this.currentFrame = newFrame; + this.imageNode.node.onStateChange(); + this.nextRenderTime = now + (1000 / this.frameRate); + } + } +} + +module.exports = AnimationTest; diff --git a/src/games/infinite-questions/index.js b/src/games/infinite-questions/index.js index d166af8f..c5f6a397 100644 --- a/src/games/infinite-questions/index.js +++ b/src/games/infinite-questions/index.js @@ -1,4 +1,4 @@ -const { Game, GameNode, Colors, Shapes, ShapeUtils } = require('squish-1006'); +const { Game, GameNode, Colors, Shapes, ShapeUtils } = require('squish-1007'); class InfiniteQuestions extends Game { static metadata() { @@ -6,7 +6,7 @@ class InfiniteQuestions extends Game { aspectRatio: {x: 16, y: 9}, author: 'Joseph Garcia', thumbnail: 'f103961541614b68c503a9ae2fd4cc47', - squishVersion: '1006', + squishVersion: '1007', tickRate: 60, services: ['contentGenerator'] }; @@ -34,7 +34,15 @@ class InfiniteQuestions extends Game { shapeType: Shapes.POLYGON, fill: Colors.COLORS.PINK, coordinates2d: ShapeUtils.rectangle(40, 15, 20, 10), - onClick: this.requestQuestions.bind(this) + onClick: this.requestQuestions.bind(this), + onHover: (playerId) => { + this.generateButton.node.fill = Colors.COLORS.WHITE; + this.generateButton.node.onStateChange(); + }, + offHover: (playerId) => { + this.generateButton.node.fill = Colors.COLORS.PINK; + this.generateButton.node.onStateChange(); + } }); const generateText = new GameNode.Text({ @@ -211,7 +219,6 @@ class InfiniteQuestions extends Game { this.loading = true; this.error = null; this.renderContent(); - const randFillers = ['sloths', 'gelato', 'basketball']; const allPlayerIds = Object.keys(this.playerStates); allPlayerIds.sort((a, b) => Math.random() - Math.random()); @@ -225,18 +232,23 @@ class InfiniteQuestions extends Game { } } if (keywords.length < 1) { - keywords = ['sloths', 'gelato']; + return; } else if (keywords.length < 2) { - keywords.push('basketball'); + keywords.push(keywords[0]); } else { keywords = keywords.slice(0, 2); } - console.log('key words'); - console.log(keywords); + + const keywordString = keywords.join(','); + this.contentGenerator.requestContent({ - type: 'trivia_questions', - keywords - }).then((contentJson) => { + model: 'mistral-7b-instruct-v0.2', + prompt: `Generate 5 conversation starter questions in JSON format. The response should contain only JSON with a single key "questions" containing a list of strings. The questions should be about the following topics: ${keywordString}.` + }).then((_contentJson) => { + // it doesnt _just_ give json of course + const leftParenIndex = _contentJson.response.lastIndexOf('{'); + const rightParenIndex = _contentJson.response.lastIndexOf('}'); + const contentJson = JSON.parse(_contentJson.response.substring(leftParenIndex, rightParenIndex + 1)); this.content = contentJson; this.error = null; this.loading = false; diff --git a/src/services/index.js b/src/services/index.js new file mode 100644 index 00000000..2dc7fc36 --- /dev/null +++ b/src/services/index.js @@ -0,0 +1,85 @@ +const http = require('http'); +const https = require('https'); + +const services = {}; + +const supportedServices = { + 'contentGenerator': { + requestContent: (request) => new Promise((resolve, reject) => { + const makePost = (path, _payload) => new Promise((resolve, reject) => { + const payload = JSON.stringify(_payload); + + let module, hostname, port; + + module = https; + port = 443; + hostname = 'api.homegames.io'; + + const headers = {}; + + Object.assign(headers, { + 'Content-Type': 'application/json', + 'Content-Length': payload.length + }); + + const options = { + hostname, + path, + port, + method: 'POST', + headers + }; + + let responseData = ''; + + const req = module.request(options, (res) => { + res.on('data', (chunk) => { + responseData += chunk; + }); + + res.on('end', () => { + resolve(responseData); + }); + }); + + req.write(payload); + req.end(); + }); + + makePost('https://api.homegames.io/services', { type: 'content-generation', ...request }).then((response) => { + if (!response.startsWith('{')) { + reject(response); + } else { + const requestId = JSON.parse(response).requestId; + const interval = setInterval(() => { + https.get(`https://api.homegames.io/service_requests/${requestId}`, {}, (res) => { + let bufs = []; + res.on('data', (chunk) => { + bufs.push(chunk); + }); + + res.on('end', () => { + const fin = JSON.parse(Buffer.concat(bufs)); + const parsed = fin;//JSON.parse(fin); + if (parsed.response) { + clearInterval(interval); + resolve(parsed.response); + } + }); + }); + }, 5000); + } + }).catch(err => { + reject(err); + }); + }) + } +}; + +const getService = (name) => { + return supportedServices[name] || null; +}; + +module.exports = { + getService +};