From c6e6a60915c354f2fecd911e8e4ac35c74eda703 Mon Sep 17 00:00:00 2001 From: shkupfer Date: Thu, 4 Nov 2021 21:13:50 -0400 Subject: [PATCH 01/15] Trying to make stats work more nicely, lots of debug logs --- database/Redis.js | 47 +++++++++++++++++++++++------ docker-compose.yaml | 2 ++ global/Stats.js | 67 +++++++++++++++++++++++++++++++++++++++++ net/AreaMessages.js | 34 +++++++++++++++++++++ net/DatabaseMessages.js | 39 ++++++++++++++++++++++-- 5 files changed, 177 insertions(+), 12 deletions(-) create mode 100644 global/Stats.js diff --git a/database/Redis.js b/database/Redis.js index bb5aaa4..d612e65 100644 --- a/database/Redis.js +++ b/database/Redis.js @@ -3,6 +3,7 @@ const createLogger = require('logging').default; const ioredis = require("ioredis") const Areas = require('../global/Areas.js'); +const Stats = require('../global/Stats.js'); class Redis { constructor(config) { @@ -45,18 +46,10 @@ class Redis { this.logger.warn(`User ${userId} not found in Redis!`); return {}; } - let stats; - if (database == this) { - stats = (game == 'football' ? response['f_stats'] : response['b_stats']); - } else { - stats = response['stats']; - } return { 'id': Number(userId), 'user': response['user'], 'icon': Number(response['icon']), - 'stats': stats - .split(',').map(Number), 'game': response['game'], 'area': Number(response['area']), 'inGame': Number(response['inGame']), @@ -83,6 +76,9 @@ class Redis { await this.redis.hmset(`byonline:users:${userId}`, user); await this.redis.hset("byonline:users:nameToId", user['user'].toUpperCase(), userId); + + const baseballStats = Stats.DefaultStats.baseball; + await this.redis.hmset(`byonline:stats:baseball:${userId}`, baseballStats) } async getUser(username, password, game) { @@ -99,8 +95,6 @@ class Redis { user = { 'user': username, 'icon': 0, - 'f_stats': Array(42).fill(0), - 'b_stats': Array(29).fill(0), } this.addUser(userId, user, game); user['id'] = userId; @@ -122,6 +116,7 @@ class Redis { await this.redis.del(`byonline:users:${userId}`); await this.redis.hdel('byonline:users:nameToId', user.user.toUpperCase()); + // await this.redis.del(`byonline:stats:baseball:${userId}`); } else { await this.redis.hmset(`byonline:users:${userId}`, { 'game': '', @@ -129,6 +124,7 @@ class Redis { 'phone': 0, 'opponent': 0 }); + await this.redis.hmset(`byonline:stats:baseball:${userId}`, Stats.DefaultStats.baseball); } } @@ -209,6 +205,37 @@ class Redis { games: Math.floor(gamesPlaying) }); } + + async setStats(userId, game, stats) { + await this.redis.hmset(`byonline:stats:baseball:${userId}`, stats); + } + + async getStats(userId, game) { + const statsKey = `byonline:stats:baseball:${userId}`; + const hasStats = await this.redis.exists(statsKey); + let stats; + if (hasStats) { + stats = await this.redis.hgetall(statsKey); + } else { + this.logger.info("SETTING DEFAULT STATS"); + await this.setStats(userId, game, Stats.DefaultStats.baseball); + stats = Stats.DefaultStats.baseball; + } + return stats; + } + + async setOngoingResults(userId, game, ongoingResults) { + await this.redis.hmset(`byonline:ongoingResults:baseball:${userId}`, ongoingResults); + } + + async getOngoingResults(userId, game) { + ongoingResults = await this.redis.hgetall(`byonline:ongoingResults:baseball:${userId}`); + return ongoingResults; + } + + async removeOngoingResults(userId, game) { + await this.redis.del(`byonline:ongoingResults:baseball:${userId}`); + } } module.exports = Redis; diff --git a/docker-compose.yaml b/docker-compose.yaml index 62e67bc..4ce797b 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -12,6 +12,8 @@ services: image: redis expose: - 6379 + ports: + - "6379:6379" # This image must be built from the BYOnline session code (https://github.com/Backyard-Sports-Online/session) session-main: diff --git a/global/Stats.js b/global/Stats.js new file mode 100644 index 0000000..f8fb2a3 --- /dev/null +++ b/global/Stats.js @@ -0,0 +1,67 @@ +"use strict"; + +const StatsFormatters = { + "baseball": (stats) => { + const numericStats = Object.fromEntries( + Object.entries(stats).map(([k, stat]) => [k, Number(stat)]) + ); + const statsArray = [ + numericStats.wins, + numericStats.losses, + numericStats.disconnects, + numericStats.streak, + 0, + 0, + numericStats.games, + numericStats.atBats, + numericStats.hits, + 0, + numericStats.singles, + numericStats.doubles, + numericStats.triples, + numericStats.homeRuns, + 0, + numericStats.steals, + numericStats.strikeouts, + numericStats.walks, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + numericStats.longestHomeRun, + 0 + ]; + return statsArray.join(","); + }, + // TODO: Football +}; + +const DefaultStats = { + "baseball": { + wins: 0, + losses: 0, + disconnects: 0, + streak: 0, + games: 0, + atBats: 0, + hits: 0, + singles: 0, + doubles: 0, + triples: 0, + homeRuns: 0, + steals: 0, + strikeouts: 0, + walks: 0, + longestHomeRun: 0 + } +}; + +module.exports = { + StatsFormatters: StatsFormatters, + DefaultStats: DefaultStats +}; \ No newline at end of file diff --git a/net/AreaMessages.js b/net/AreaMessages.js index df609ee..c7a1c26 100644 --- a/net/AreaMessages.js +++ b/net/AreaMessages.js @@ -109,6 +109,40 @@ server.handleMessage('game_started', async (client, args) => { server.handleMessage('game_finished', async (client, args) => { await redis.setInGame(client.userId, 0); await redis.sendGamesPlayingInArea(client.areaId, client.game); + + // Get the most recent results data + const finalResultsAsStrings = await redis.getOngoingResults(client.userId, client.game); + const finalResults = Object.fromEntries( + Object.entries(finalResultsAsStrings).map(([k, stat]) => [k, Number(stat)]) + ); + const homeRuns = finalResults.hits - (finalResults.singles + finalResults.doubles + finalResults.triples); + const winSign = finalResults.winning * 2 - 1; + + // Then get this user's existing baseball stats + let stats = await redis.getStats(client.userId, client.game); + stats.wins += finalResults.winning; // Increment user's wins + stats.losses += (1 - finalResults.winning); // Increment user's losses + if (Math.sign(stats.streak) == winSign) { + // If user is continuing their streak, increment/decrement it. + stats.streak += winSign; + } else { + // If user is starting a new streak. + stats.streak = winSign; + } + // TODO (maybe): Wins in last 10 games and margin + stats.games += 1; + stats.atBats += finalResults.atBats; + stats.hits += finalResults.hits; + stats.singles += finalResults.singles; + stats.doubles += finalResults.doubles; + stats.triples += finalResults.triples; + stats.homeRuns += homeRuns; + stats.steals += finalResults.steals; + stats.strikeouts += finalResults.strikeouts; + stats.walks += finalResults.walks; + stats.longestHomeRun = Math.max(statslongestHomeRun, finalResults.longestHomeRun); + + await redis.setStats(client.userId, client.game, stats); }); process.on('update_games_playing', async (args) => { diff --git a/net/DatabaseMessages.js b/net/DatabaseMessages.js index 49d8731..2a1ed37 100644 --- a/net/DatabaseMessages.js +++ b/net/DatabaseMessages.js @@ -3,6 +3,7 @@ const createLogger = require('logging').default; const logger = createLogger('DatabaseMessages'); const Areas = require('../global/Areas.js'); +const Stats = require('../global/Stats.js'); server.handleMessage("login", async (client, args) => { const username = args.user; @@ -109,7 +110,12 @@ server.handleMessage('get_profile', async (client, args) => { if (user === {}) return; - const profile = [user.icon].concat(user.stats); + const stats = await redis.getStats(userId, client.game); + logger.info("STATS: " + stats); + const formattedStats = Stats.StatsFormatters.baseball(stats); + logger.info("FORMATTED STATS: " + formattedStats); + const profile = [user.icon].concat(formattedStats); + logger.info(profile); client.send("profile_info", {profile: profile}); }); @@ -162,5 +168,34 @@ server.handleMessage('locate_player', async (client, args) => { }); server.handleMessage("game_results", async (client, args) => { - // TODO + const resultsUserId = args.user; + const reportingUserId = client.userId; + let resultsSide; + // The home team always reports the game results, so we can use that + // to tell whether the results are for the home or away team. + // TODO: Verify that this is true for football (it is for baseball) + if (reportingUserId == resultsUserId) { + resultsSide = "home"; + } else { + resultsSide = "away"; + } + const resultsFields = args.fields.split(','); + const ongoingResults = { + winning: resultsFields[0], + runs: resultsFields[1], + atBats: resultsFields[2], + hits: resultsFields[3], + errors: resultsFields[4], + longestHomeRun: resultsFields[5], + singles: resultsFields[6], + doubles: resultsFields[7], + triples: resultsFields[8], + steals: resultsFields[9], + strikeouts: resultsFields[10], + walks: resultsFields[11], + quit: resultsFields[12], + completedInnings: resultsFields[13], + side: resultsSide + } + await redis.setOngoingResults(resultsUserId, client.game, ongoingResults); }); From 120c8ca4476fb1570e5ca92d17344ac82eea28a2 Mon Sep 17 00:00:00 2001 From: shkupfer Date: Thu, 4 Nov 2021 21:55:31 -0400 Subject: [PATCH 02/15] COuple small fixes --- database/Redis.js | 2 +- global/Stats.js | 2 +- net/AreaMessages.js | 6 ++++-- net/DatabaseMessages.js | 4 ++-- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/database/Redis.js b/database/Redis.js index d612e65..841c8bb 100644 --- a/database/Redis.js +++ b/database/Redis.js @@ -229,7 +229,7 @@ class Redis { } async getOngoingResults(userId, game) { - ongoingResults = await this.redis.hgetall(`byonline:ongoingResults:baseball:${userId}`); + const ongoingResults = await this.redis.hgetall(`byonline:ongoingResults:baseball:${userId}`); return ongoingResults; } diff --git a/global/Stats.js b/global/Stats.js index f8fb2a3..88af78f 100644 --- a/global/Stats.js +++ b/global/Stats.js @@ -36,7 +36,7 @@ const StatsFormatters = { numericStats.longestHomeRun, 0 ]; - return statsArray.join(","); + return statsArray; }, // TODO: Football }; diff --git a/net/AreaMessages.js b/net/AreaMessages.js index c7a1c26..c196fc5 100644 --- a/net/AreaMessages.js +++ b/net/AreaMessages.js @@ -70,7 +70,8 @@ server.handleMessage('get_players', async (client, args) => { // Don't add ourselves in. continue; } - players.push([user.user, user.id, user.icon, user.stats[0], user.stats[1], user.stats[2], user.phone, user.opponent]); + // TODO: Fix this + players.push([user.user, user.id, user.icon, 0, 0, 0, user.phone, user.opponent]); } client.send('players_list', {players: players.slice(client.sliceStart, client.sliceEnd)}); @@ -89,7 +90,8 @@ process.on('update_players_list', (args) => { // Don't add ourselves in. continue; } - players.push([user.user, user.id, user.icon, user.stats[0], user.stats[1], user.stats[2], user.phone, user.opponent]); + // TODO: Fix this + players.push([user.user, user.id, user.icon, 0, 0, 0, user.phone, user.opponent]); } client.send('players_list', {players: players.slice(client.sliceStart, client.sliceEnd)}); } diff --git a/net/DatabaseMessages.js b/net/DatabaseMessages.js index 2a1ed37..e3962e6 100644 --- a/net/DatabaseMessages.js +++ b/net/DatabaseMessages.js @@ -179,7 +179,7 @@ server.handleMessage("game_results", async (client, args) => { } else { resultsSide = "away"; } - const resultsFields = args.fields.split(','); + const resultsFields = args.fields; const ongoingResults = { winning: resultsFields[0], runs: resultsFields[1], @@ -196,6 +196,6 @@ server.handleMessage("game_results", async (client, args) => { quit: resultsFields[12], completedInnings: resultsFields[13], side: resultsSide - } + }; await redis.setOngoingResults(resultsUserId, client.game, ongoingResults); }); From 0e60f37dd3e416963b85ffaae4be6389c6bf2827 Mon Sep 17 00:00:00 2001 From: shkupfer Date: Sat, 6 Nov 2021 16:17:03 -0400 Subject: [PATCH 03/15] Cleanup, fixes, starting to football-proof --- database/Redis.js | 8 +++--- global/Stats.js | 59 ++++++++++++++++++++++++++++++++++++++++- net/AreaMessages.js | 32 +++++----------------- net/DatabaseMessages.js | 19 ++----------- 4 files changed, 72 insertions(+), 46 deletions(-) diff --git a/database/Redis.js b/database/Redis.js index 841c8bb..ed0c551 100644 --- a/database/Redis.js +++ b/database/Redis.js @@ -212,9 +212,8 @@ class Redis { async getStats(userId, game) { const statsKey = `byonline:stats:baseball:${userId}`; - const hasStats = await this.redis.exists(statsKey); let stats; - if (hasStats) { + if (await this.redis.exists(statsKey)) { stats = await this.redis.hgetall(statsKey); } else { this.logger.info("SETTING DEFAULT STATS"); @@ -234,7 +233,10 @@ class Redis { } async removeOngoingResults(userId, game) { - await this.redis.del(`byonline:ongoingResults:baseball:${userId}`); + const resultsKey = `byonline:ongoingResults:${game}:${userId}`; + if (await this.redis.exists(resultsKey)) { + await this.redis.del(resultsKey); + } } } diff --git a/global/Stats.js b/global/Stats.js index 88af78f..cc80c88 100644 --- a/global/Stats.js +++ b/global/Stats.js @@ -37,10 +37,64 @@ const StatsFormatters = { 0 ]; return statsArray; - }, + } + // TODO: Football +}; + +const ResultsMappers = { + "baseball": (resultsFields, resultsSide) => { + const ongoingResults = { + winning: resultsFields[0], + runs: resultsFields[1], + atBats: resultsFields[2], + hits: resultsFields[3], + errors: resultsFields[4], + longestHomeRun: resultsFields[5], + singles: resultsFields[6], + doubles: resultsFields[7], + triples: resultsFields[8], + steals: resultsFields[9], + strikeouts: resultsFields[10], + walks: resultsFields[11], + quit: resultsFields[12], + completedInnings: resultsFields[13], + side: resultsSide + }; + return ongoingResults; + } // TODO: Football }; +const Aggregators = { + "baseball": (finalResults, stats) => { + const homeRuns = finalResults.hits - (finalResults.singles + finalResults.doubles + finalResults.triples); + const winSign = finalResults.winning * 2 - 1; + stats.wins += finalResults.winning; // Increment user's wins + stats.losses += (1 - finalResults.winning); // Increment user's losses + if (Math.sign(stats.streak) == winSign) { + // If user is continuing their streak, increment/decrement it. + stats.streak += winSign; + } else { + // If user is starting a new streak. + stats.streak = winSign; + } + // TODO (maybe): Wins in last 10 games and margin + stats.games += 1; + stats.atBats += finalResults.atBats; + stats.hits += finalResults.hits; + stats.singles += finalResults.singles; + stats.doubles += finalResults.doubles; + stats.triples += finalResults.triples; + stats.homeRuns += homeRuns; + stats.steals += finalResults.steals; + stats.strikeouts += finalResults.strikeouts; + stats.walks += finalResults.walks; + stats.longestHomeRun = Math.max(stats.longestHomeRun, finalResults.longestHomeRun); + return stats; + } + // TODO: Football +} + const DefaultStats = { "baseball": { wins: 0, @@ -59,9 +113,12 @@ const DefaultStats = { walks: 0, longestHomeRun: 0 } + // TODO: Football }; module.exports = { StatsFormatters: StatsFormatters, + ResultsMappers: ResultsMappers, + Aggregators: Aggregators, DefaultStats: DefaultStats }; \ No newline at end of file diff --git a/net/AreaMessages.js b/net/AreaMessages.js index c196fc5..c2b01a2 100644 --- a/net/AreaMessages.js +++ b/net/AreaMessages.js @@ -99,6 +99,7 @@ process.on('update_players_list', (args) => { }); server.handleMessage('game_started', async (client, args) => { + logger.info("GAME STARTED " + client.userId + " " + args.user); const playerId = args.user; await redis.setInGame(client.userId, 1); @@ -106,9 +107,11 @@ server.handleMessage('game_started', async (client, args) => { await redis.sendUsersInArea(client.areaId, client.game); await redis.sendGamesPlayingInArea(client.areaId, client.game); + await redis.removeOngoingResults(client.userId, client.game); }); server.handleMessage('game_finished', async (client, args) => { + logger.info("GAME FINISHED " + client.userId); await redis.setInGame(client.userId, 0); await redis.sendGamesPlayingInArea(client.areaId, client.game); @@ -117,34 +120,13 @@ server.handleMessage('game_finished', async (client, args) => { const finalResults = Object.fromEntries( Object.entries(finalResultsAsStrings).map(([k, stat]) => [k, Number(stat)]) ); - const homeRuns = finalResults.hits - (finalResults.singles + finalResults.doubles + finalResults.triples); - const winSign = finalResults.winning * 2 - 1; - - // Then get this user's existing baseball stats + // Get this user's existing baseball stats let stats = await redis.getStats(client.userId, client.game); - stats.wins += finalResults.winning; // Increment user's wins - stats.losses += (1 - finalResults.winning); // Increment user's losses - if (Math.sign(stats.streak) == winSign) { - // If user is continuing their streak, increment/decrement it. - stats.streak += winSign; - } else { - // If user is starting a new streak. - stats.streak = winSign; - } - // TODO (maybe): Wins in last 10 games and margin - stats.games += 1; - stats.atBats += finalResults.atBats; - stats.hits += finalResults.hits; - stats.singles += finalResults.singles; - stats.doubles += finalResults.doubles; - stats.triples += finalResults.triples; - stats.homeRuns += homeRuns; - stats.steals += finalResults.steals; - stats.strikeouts += finalResults.strikeouts; - stats.walks += finalResults.walks; - stats.longestHomeRun = Math.max(statslongestHomeRun, finalResults.longestHomeRun); + // Calculate updated stats + stats = Stats.Aggregators.baseball(finalResults, stats); await redis.setStats(client.userId, client.game, stats); + await redis.removeOngoingResults(client.userId, client.game); }); process.on('update_games_playing', async (args) => { diff --git a/net/DatabaseMessages.js b/net/DatabaseMessages.js index e3962e6..343c8fd 100644 --- a/net/DatabaseMessages.js +++ b/net/DatabaseMessages.js @@ -180,22 +180,7 @@ server.handleMessage("game_results", async (client, args) => { resultsSide = "away"; } const resultsFields = args.fields; - const ongoingResults = { - winning: resultsFields[0], - runs: resultsFields[1], - atBats: resultsFields[2], - hits: resultsFields[3], - errors: resultsFields[4], - longestHomeRun: resultsFields[5], - singles: resultsFields[6], - doubles: resultsFields[7], - triples: resultsFields[8], - steals: resultsFields[9], - strikeouts: resultsFields[10], - walks: resultsFields[11], - quit: resultsFields[12], - completedInnings: resultsFields[13], - side: resultsSide - }; + const ongoingResults = Stats.ResultsMappers.baseball(resultsFields, resultsSide); + logger.info("ONGOINGRESULTS: " + JSON.stringify(ongoingResults)); await redis.setOngoingResults(resultsUserId, client.game, ongoingResults); }); From bd9338e0571ae5aba8a3a14649dd2672f7a84e22 Mon Sep 17 00:00:00 2001 From: shkupfer Date: Sun, 7 Nov 2021 10:12:14 -0500 Subject: [PATCH 04/15] Fixes, football-proofing --- database/Redis.js | 27 +++++++++++++++++---------- global/Stats.js | 20 ++++++++++++++++---- net/AreaMessages.js | 34 +++++++++++++++++++++------------- net/DatabaseMessages.js | 6 +++--- 4 files changed, 57 insertions(+), 30 deletions(-) diff --git a/database/Redis.js b/database/Redis.js index ed0c551..e621e28 100644 --- a/database/Redis.js +++ b/database/Redis.js @@ -77,8 +77,8 @@ class Redis { await this.redis.hmset(`byonline:users:${userId}`, user); await this.redis.hset("byonline:users:nameToId", user['user'].toUpperCase(), userId); - const baseballStats = Stats.DefaultStats.baseball; - await this.redis.hmset(`byonline:stats:baseball:${userId}`, baseballStats) + const defaultStats = Stats.DefaultStats[game]; + await this.redis.hmset(`byonline:stats:${game}:${userId}`, defaultStats) } async getUser(username, password, game) { @@ -116,7 +116,8 @@ class Redis { await this.redis.del(`byonline:users:${userId}`); await this.redis.hdel('byonline:users:nameToId', user.user.toUpperCase()); - // await this.redis.del(`byonline:stats:baseball:${userId}`); + // Is the below correct? Or should we remove stats for both games? + await this.redis.del(`byonline:stats:${game}:${userId}`); } else { await this.redis.hmset(`byonline:users:${userId}`, { 'game': '', @@ -124,7 +125,8 @@ class Redis { 'phone': 0, 'opponent': 0 }); - await this.redis.hmset(`byonline:stats:baseball:${userId}`, Stats.DefaultStats.baseball); + // Same question as above + await this.redis.hmset(`byonline:stats:${game}:${userId}`, Stats.DefaultStats[game]); } } @@ -207,31 +209,36 @@ class Redis { } async setStats(userId, game, stats) { - await this.redis.hmset(`byonline:stats:baseball:${userId}`, stats); + this.logger.info("WRITING STATS FOR " + userId + ": " + JSON.stringify(stats)); + await this.redis.hmset(`byonline:stats:${game}:${userId}`, stats); } async getStats(userId, game) { - const statsKey = `byonline:stats:baseball:${userId}`; + const statsKey = `byonline:stats:${game}:${userId}`; let stats; if (await this.redis.exists(statsKey)) { stats = await this.redis.hgetall(statsKey); } else { this.logger.info("SETTING DEFAULT STATS"); - await this.setStats(userId, game, Stats.DefaultStats.baseball); - stats = Stats.DefaultStats.baseball; + await this.setStats(userId, game, Stats.DefaultStats[game]); + stats = Stats.DefaultStats[game]; } return stats; } async setOngoingResults(userId, game, ongoingResults) { - await this.redis.hmset(`byonline:ongoingResults:baseball:${userId}`, ongoingResults); + await this.redis.hmset(`byonline:ongoingResults:${game}:${userId}`, ongoingResults); } async getOngoingResults(userId, game) { - const ongoingResults = await this.redis.hgetall(`byonline:ongoingResults:baseball:${userId}`); + const ongoingResults = await this.redis.hgetall(`byonline:ongoingResults:${game}:${userId}`); return ongoingResults; } + async hasOngoingResults(userId, game) { + return await this.redis.exists(`byonline:ongoingResults:${game}:${userId}`); + } + async removeOngoingResults(userId, game) { const resultsKey = `byonline:ongoingResults:${game}:${userId}`; if (await this.redis.exists(resultsKey)) { diff --git a/global/Stats.js b/global/Stats.js index cc80c88..6a1310f 100644 --- a/global/Stats.js +++ b/global/Stats.js @@ -37,8 +37,11 @@ const StatsFormatters = { 0 ]; return statsArray; - } + }, // TODO: Football + "football": (stats) => { + return Array(42).fill(0) + } }; const ResultsMappers = { @@ -61,8 +64,11 @@ const ResultsMappers = { side: resultsSide }; return ongoingResults; - } + }, // TODO: Football + "football": (resultsFields, resultsSide) => { + return {"not_yet_supported": 1} + } }; const Aggregators = { @@ -91,8 +97,11 @@ const Aggregators = { stats.walks += finalResults.walks; stats.longestHomeRun = Math.max(stats.longestHomeRun, finalResults.longestHomeRun); return stats; - } + }, // TODO: Football + "baseball": (finalResults, stats) => { + return {not_yet_supported: 1} + } } const DefaultStats = { @@ -112,8 +121,11 @@ const DefaultStats = { strikeouts: 0, walks: 0, longestHomeRun: 0 - } + }, // TODO: Football + "football": { + not_yet_supported: 1 + } }; module.exports = { diff --git a/net/AreaMessages.js b/net/AreaMessages.js index c2b01a2..8e13cb8 100644 --- a/net/AreaMessages.js +++ b/net/AreaMessages.js @@ -3,6 +3,7 @@ const createLogger = require('logging').default; const logger = createLogger('AreaMessages'); const Areas = require('../global/Areas.js'); +const Stats = require('../global/Stats.js'); server.handleMessage('get_population', async (client, args) => { const areaId = args.area; @@ -108,25 +109,32 @@ server.handleMessage('game_started', async (client, args) => { await redis.sendUsersInArea(client.areaId, client.game); await redis.sendGamesPlayingInArea(client.areaId, client.game); await redis.removeOngoingResults(client.userId, client.game); + await redis.removeOngoingResults(playerId, client.game); }); server.handleMessage('game_finished', async (client, args) => { - logger.info("GAME FINISHED " + client.userId); + logger.info("GAME FINISHED " + client.userId + " vs. " + client.opponentId); await redis.setInGame(client.userId, 0); await redis.sendGamesPlayingInArea(client.areaId, client.game); - // Get the most recent results data - const finalResultsAsStrings = await redis.getOngoingResults(client.userId, client.game); - const finalResults = Object.fromEntries( - Object.entries(finalResultsAsStrings).map(([k, stat]) => [k, Number(stat)]) - ); - // Get this user's existing baseball stats - let stats = await redis.getStats(client.userId, client.game); - // Calculate updated stats - stats = Stats.Aggregators.baseball(finalResults, stats); - - await redis.setStats(client.userId, client.game, stats); - await redis.removeOngoingResults(client.userId, client.game); + if (await redis.hasOngoingResults(client.userId, client.game)) { + logger.info("HAS ONGOING RESULTS"); + // Get the most recent results data + const finalResultsAsStrings = await redis.getOngoingResults(client.userId, client.game); + await redis.removeOngoingResults(client.userId, client.game); + const finalResults = Object.fromEntries( + Object.entries(finalResultsAsStrings).map(([k, stat]) => [k, Number(stat)]) + ); + // Get this user's existing stats + const statsStrings = await redis.getStats(client.userId, client.game); + let stats = Object.fromEntries( + Object.entries(statsStrings).map(([k, stat]) => [k, Number(stat)]) + ); + // Calculate updated stats + stats = Stats.Aggregators[client.game](finalResults, stats); + + await redis.setStats(client.userId, client.game, stats); + } }); process.on('update_games_playing', async (args) => { diff --git a/net/DatabaseMessages.js b/net/DatabaseMessages.js index 343c8fd..707290d 100644 --- a/net/DatabaseMessages.js +++ b/net/DatabaseMessages.js @@ -111,8 +111,8 @@ server.handleMessage('get_profile', async (client, args) => { return; const stats = await redis.getStats(userId, client.game); - logger.info("STATS: " + stats); - const formattedStats = Stats.StatsFormatters.baseball(stats); + logger.info("STATS: " + JSON.stringify(stats)); + const formattedStats = Stats.StatsFormatters["baseball"](stats); logger.info("FORMATTED STATS: " + formattedStats); const profile = [user.icon].concat(formattedStats); logger.info(profile); @@ -180,7 +180,7 @@ server.handleMessage("game_results", async (client, args) => { resultsSide = "away"; } const resultsFields = args.fields; - const ongoingResults = Stats.ResultsMappers.baseball(resultsFields, resultsSide); + const ongoingResults = Stats.ResultsMappers[client.game](resultsFields, resultsSide); logger.info("ONGOINGRESULTS: " + JSON.stringify(ongoingResults)); await redis.setOngoingResults(resultsUserId, client.game, ongoingResults); }); From e3ee24ac3812db57a2c299013a83eb6272c89412 Mon Sep 17 00:00:00 2001 From: shkupfer Date: Sun, 7 Nov 2021 10:53:59 -0500 Subject: [PATCH 05/15] More fixes, hopefully fixed double-counting for away team --- database/Redis.js | 1 + global/Stats.js | 2 +- net/AreaMessages.js | 41 ++++++++++++++++++++++------------------- net/DatabaseMessages.js | 2 +- 4 files changed, 25 insertions(+), 21 deletions(-) diff --git a/database/Redis.js b/database/Redis.js index e621e28..15d7954 100644 --- a/database/Redis.js +++ b/database/Redis.js @@ -242,6 +242,7 @@ class Redis { async removeOngoingResults(userId, game) { const resultsKey = `byonline:ongoingResults:${game}:${userId}`; if (await this.redis.exists(resultsKey)) { + this.logger.info("REMOVING ONGOING RESULTS FOR KEY " + resultsKey) await this.redis.del(resultsKey); } } diff --git a/global/Stats.js b/global/Stats.js index 6a1310f..cc00c88 100644 --- a/global/Stats.js +++ b/global/Stats.js @@ -99,7 +99,7 @@ const Aggregators = { return stats; }, // TODO: Football - "baseball": (finalResults, stats) => { + "football": (finalResults, stats) => { return {not_yet_supported: 1} } } diff --git a/net/AreaMessages.js b/net/AreaMessages.js index 8e13cb8..ed3f23e 100644 --- a/net/AreaMessages.js +++ b/net/AreaMessages.js @@ -114,26 +114,29 @@ server.handleMessage('game_started', async (client, args) => { server.handleMessage('game_finished', async (client, args) => { logger.info("GAME FINISHED " + client.userId + " vs. " + client.opponentId); - await redis.setInGame(client.userId, 0); await redis.sendGamesPlayingInArea(client.areaId, client.game); - - if (await redis.hasOngoingResults(client.userId, client.game)) { - logger.info("HAS ONGOING RESULTS"); - // Get the most recent results data - const finalResultsAsStrings = await redis.getOngoingResults(client.userId, client.game); - await redis.removeOngoingResults(client.userId, client.game); - const finalResults = Object.fromEntries( - Object.entries(finalResultsAsStrings).map(([k, stat]) => [k, Number(stat)]) - ); - // Get this user's existing stats - const statsStrings = await redis.getStats(client.userId, client.game); - let stats = Object.fromEntries( - Object.entries(statsStrings).map(([k, stat]) => [k, Number(stat)]) - ); - // Calculate updated stats - stats = Stats.Aggregators[client.game](finalResults, stats); - - await redis.setStats(client.userId, client.game, stats); + const user = await redis.getUserById(client.userId, client.game); + if (user.inGame) { + logger.info("USER " + client.userId + " IS IN GAME"); + await redis.setInGame(client.userId, 0); + if (await redis.hasOngoingResults(client.userId, client.game)) { + logger.info("HAS ONGOING RESULTS"); + // Get the most recent results data + const finalResultsAsStrings = await redis.getOngoingResults(client.userId, client.game); + await redis.removeOngoingResults(client.userId, client.game); + const finalResults = Object.fromEntries( + Object.entries(finalResultsAsStrings).map(([k, stat]) => [k, Number(stat)]) + ); + // Get this user's existing stats + const statsStrings = await redis.getStats(client.userId, client.game); + let stats = Object.fromEntries( + Object.entries(statsStrings).map(([k, stat]) => [k, Number(stat)]) + ); + // Calculate updated stats + stats = Stats.Aggregators[client.game](finalResults, stats); + + await redis.setStats(client.userId, client.game, stats); + } } }); diff --git a/net/DatabaseMessages.js b/net/DatabaseMessages.js index 707290d..6195b35 100644 --- a/net/DatabaseMessages.js +++ b/net/DatabaseMessages.js @@ -112,7 +112,7 @@ server.handleMessage('get_profile', async (client, args) => { const stats = await redis.getStats(userId, client.game); logger.info("STATS: " + JSON.stringify(stats)); - const formattedStats = Stats.StatsFormatters["baseball"](stats); + const formattedStats = Stats.StatsFormatters[client.game](stats); logger.info("FORMATTED STATS: " + formattedStats); const profile = [user.icon].concat(formattedStats); logger.info(profile); From 883f757181436b93b30b781993e0af6ea8567669 Mon Sep 17 00:00:00 2001 From: shkupfer Date: Wed, 10 Nov 2021 08:30:21 -0500 Subject: [PATCH 06/15] Update dockerfile and docker-compose to be able to optionally launch site and MongoDB. New creds file to hit site's API --- Dockerfile | 4 +++- credentials.localdev-with-site.yaml | 20 ++++++++++++++++++++ docker-compose.yaml | 26 +++++++++++++++++++++----- 3 files changed, 44 insertions(+), 6 deletions(-) create mode 100644 credentials.localdev-with-site.yaml diff --git a/Dockerfile b/Dockerfile index 2a88326..a60bd11 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,6 +7,8 @@ COPY package*.json ./ RUN npm install COPY . . -COPY credentials.localdev.yaml credentials.yaml + +ARG CREDENTIALS_FILE=credentials.localdev.yaml +COPY ${CREDENTIALS_FILE} credentials.yaml CMD [ "node", "run.js" ] diff --git a/credentials.localdev-with-site.yaml b/credentials.localdev-with-site.yaml new file mode 100644 index 0000000..edf8b72 --- /dev/null +++ b/credentials.localdev-with-site.yaml @@ -0,0 +1,20 @@ +redis: + # Redis server infomation goes here. + host: redis + port: 6379 + +# Web API infomation goes here, intended for production environments, +# comment out to use Redis as a development database. +web: + endpoint: http://site:3000/api + token: "" + +# Discord info goes here. This sends the server status and population +# to a specific channel. +# discord: + # Also known as "Application ID" + # client: '' + # Bot account token (must be string) + # token: '' + # Channel ID to post (must be string) + # channel: '' diff --git a/docker-compose.yaml b/docker-compose.yaml index 62e67bc..8a04408 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -8,13 +8,29 @@ services: ports: - "9130:9130" - redis: - image: redis - expose: - - 6379 - # This image must be built from the BYOnline session code (https://github.com/Backyard-Sports-Online/session) session-main: image: session-main:latest ports: - "9130:9130/udp" + + site: + image: site:latest + ports: + - "3000:3000" + command: + - npm + - run + - compose + + mongo: + image: mongo + ports: + - "27017:27017" + + redis: + image: redis + expose: + - 6379 + ports: + - "6379:6379" From 61733d0995c8bd43c84cd16a19a3cc3a4d6d7960 Mon Sep 17 00:00:00 2001 From: shkupfer Date: Wed, 10 Nov 2021 08:38:11 -0500 Subject: [PATCH 07/15] Update readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 88d61b0..851d307 100644 --- a/README.md +++ b/README.md @@ -11,5 +11,5 @@ docker-compose up server redis In order to use the server along with the [BYOnline session server](https://github.com/Backyard-Sports-Online/session), you'll have to build a session server image and tag it `session-main:latest`. Then, you can launch all three components (server, redis, and session server) with: ``` -docker-compose up +docker-compose up server redis session-main ``` From edc699bd65d16e6f244179ac2ab3bf552797b18d Mon Sep 17 00:00:00 2001 From: shkupfer Date: Thu, 9 Dec 2021 23:25:37 -0500 Subject: [PATCH 08/15] Use env vars instead of credentials files --- Dockerfile | 3 --- credentials.localdev-with-site.yaml | 20 ------------------ credentials.localdev.yaml | 20 ------------------ credentials.template.yaml | 20 ------------------ docker-compose.yaml | 6 ++++++ run.js | 32 ++++++++++++++++++++++------- 6 files changed, 31 insertions(+), 70 deletions(-) delete mode 100644 credentials.localdev-with-site.yaml delete mode 100644 credentials.localdev.yaml delete mode 100644 credentials.template.yaml diff --git a/Dockerfile b/Dockerfile index a60bd11..31acebc 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,7 +8,4 @@ RUN npm install COPY . . -ARG CREDENTIALS_FILE=credentials.localdev.yaml -COPY ${CREDENTIALS_FILE} credentials.yaml - CMD [ "node", "run.js" ] diff --git a/credentials.localdev-with-site.yaml b/credentials.localdev-with-site.yaml deleted file mode 100644 index edf8b72..0000000 --- a/credentials.localdev-with-site.yaml +++ /dev/null @@ -1,20 +0,0 @@ -redis: - # Redis server infomation goes here. - host: redis - port: 6379 - -# Web API infomation goes here, intended for production environments, -# comment out to use Redis as a development database. -web: - endpoint: http://site:3000/api - token: "" - -# Discord info goes here. This sends the server status and population -# to a specific channel. -# discord: - # Also known as "Application ID" - # client: '' - # Bot account token (must be string) - # token: '' - # Channel ID to post (must be string) - # channel: '' diff --git a/credentials.localdev.yaml b/credentials.localdev.yaml deleted file mode 100644 index 37890ee..0000000 --- a/credentials.localdev.yaml +++ /dev/null @@ -1,20 +0,0 @@ -redis: - # Redis server infomation goes here. - host: redis - port: 6379 - -# Web API infomation goes here, intended for production environments, -# comment out to use Redis as a development database. -# web: - # endpoint: http://localhost:3000/api - # token: "" - -# Discord info goes here. This sends the server status and population -# to a specific channel. -# discord: - # Also known as "Application ID" - # client: '' - # Bot account token (must be string) - # token: '' - # Channel ID to post (must be string) - # channel: '' diff --git a/credentials.template.yaml b/credentials.template.yaml deleted file mode 100644 index a84404c..0000000 --- a/credentials.template.yaml +++ /dev/null @@ -1,20 +0,0 @@ -redis: - # Redis server infomation goes here. - host: 127.0.0.1 - port: 6379 - -# Web API infomation goes here, intended for production environments, -# comment out to use Redis as a development database. -# web: - # endpoint: http://localhost:3000/api - # token: "" - -# Discord info goes here. This sends the server status and population -# to a specific channel. -# discord: - # Also known as "Application ID" - # client: '' - # Bot account token (must be string) - # token: '' - # Channel ID to post (must be string) - # channel: '' diff --git a/docker-compose.yaml b/docker-compose.yaml index 8a04408..6ccb9c2 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -7,6 +7,12 @@ services: - redis ports: - "9130:9130" + environment: + - DATABASE=${DATABASE:-redis} + - WEB_ENDPOINT=http://site:3000/api + - WEB_TOKEN= + - REDIS_HOST=redis + - REDIS_PORT=6379 # This image must be built from the BYOnline session code (https://github.com/Backyard-Sports-Online/session) session-main: diff --git a/run.js b/run.js index 3588d1e..120b5a1 100644 --- a/run.js +++ b/run.js @@ -7,7 +7,23 @@ const cluster = require('cluster'); // Read the configuration files. const config = yaml.load(fs.readFileSync('config.yaml')); -const credientals = yaml.load(fs.readFileSync('credentials.yaml')); + +// Get credentials and such from environment variables +const credentials = { + web: { + endpoint: process.env.WEB_ENDPOINT, + token: process.env.WEB_TOKEN, + }, + redis: { + host: process.env.REDIS_HOST, + port: process.env.REDIS_PORT, + }, + discord: { + client: process.env.DISCORD_CLIENT, + token: process.env.DISCORD_TOKEN, + channel: process.env.DISCORD_CHANNEL, + } +}; if (cluster.isMaster) { // Fork out workers @@ -28,12 +44,14 @@ if (cluster.isMaster) { const NetworkListener = require('./net/NetworkListener'); const Redis = require('./database/Redis'); const Discord = require('./discord/Discord'); - + const logger = createLogger.default("asdf"); global.server = new NetworkListener(config['network']) - global.redis = new Redis(credientals['redis']); - if (credientals.web) { + global.redis = new Redis(credentials.redis); + logger.info(process.env); + if (process.env.DATABASE == 'web') { + logger.info("ITS WEB") const WebAPI = require('./database/WebAPI'); - global.database = new WebAPI(credientals['web']); + global.database = new WebAPI(credentials.web); } else { global.database = global.redis; } @@ -44,8 +62,8 @@ if (cluster.isMaster) { require('./net/ChallengeMessages.js'); require('./net/SessionMessages.js'); - if (process.env.FIRST_WORKER && credientals.discord) { - global.discord = new Discord(credientals['discord']); + if (process.env.FIRST_WORKER && credentials.discord.client) { + global.discord = new Discord(credentials['discord']); } // Handle messages from other processes. From 51d616981097c60c65ab625f42223052ae40128b Mon Sep 17 00:00:00 2001 From: shkupfer Date: Sat, 11 Dec 2021 14:03:54 -0500 Subject: [PATCH 09/15] Some defaults --- docker-compose.yaml | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/docker-compose.yaml b/docker-compose.yaml index 6ccb9c2..ef95e62 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -3,16 +3,14 @@ version: "3.8" services: server: build: ./ - links: - - redis ports: - "9130:9130" environment: - DATABASE=${DATABASE:-redis} - - WEB_ENDPOINT=http://site:3000/api - - WEB_TOKEN= - - REDIS_HOST=redis - - REDIS_PORT=6379 + - WEB_ENDPOINT=${WEB_ENDPOINT:-http://site:3000/api} + - WEB_TOKEN=${WEB_TOKEN:-} + - REDIS_HOST=${REDIS_HOST:-redis} + - REDIS_PORT=${REDIS_PORT:-6379} # This image must be built from the BYOnline session code (https://github.com/Backyard-Sports-Online/session) session-main: From 4af2e7a65b638d94bfd60299df1d8a1ab170bbff Mon Sep 17 00:00:00 2001 From: shkupfer Date: Mon, 13 Dec 2021 11:12:28 -0500 Subject: [PATCH 10/15] Remove log statement, add discord env vars as optional docker-compose env vars --- docker-compose.yaml | 3 +++ run.js | 5 +---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docker-compose.yaml b/docker-compose.yaml index ef95e62..4cafb21 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -11,6 +11,9 @@ services: - WEB_TOKEN=${WEB_TOKEN:-} - REDIS_HOST=${REDIS_HOST:-redis} - REDIS_PORT=${REDIS_PORT:-6379} + - DISCORD_CLIENT + - DISCORD_CHANNEL + - DISCORD_TOKEN # This image must be built from the BYOnline session code (https://github.com/Backyard-Sports-Online/session) session-main: diff --git a/run.js b/run.js index 120b5a1..f47675e 100644 --- a/run.js +++ b/run.js @@ -2,7 +2,7 @@ const yaml = require('js-yaml'); const fs = require('fs'); -const createLogger = require('logging'); +const createLogger = require('logging').default; const cluster = require('cluster'); // Read the configuration files. @@ -44,12 +44,9 @@ if (cluster.isMaster) { const NetworkListener = require('./net/NetworkListener'); const Redis = require('./database/Redis'); const Discord = require('./discord/Discord'); - const logger = createLogger.default("asdf"); global.server = new NetworkListener(config['network']) global.redis = new Redis(credentials.redis); - logger.info(process.env); if (process.env.DATABASE == 'web') { - logger.info("ITS WEB") const WebAPI = require('./database/WebAPI'); global.database = new WebAPI(credentials.web); } else { From d3ecde6a4e93e7318bb5cf67523540f93f8e8a45 Mon Sep 17 00:00:00 2001 From: shkupfer Date: Tue, 14 Dec 2021 12:16:45 -0500 Subject: [PATCH 11/15] WebAPI get and set stats, more --- database/Redis.js | 4 ++-- database/WebAPI.js | 35 +++++++++++++++++++++++++++++++++++ global/Stats.js | 2 +- net/AreaMessages.js | 16 +++++++++++----- net/DatabaseMessages.js | 3 ++- 5 files changed, 51 insertions(+), 9 deletions(-) diff --git a/database/Redis.js b/database/Redis.js index 15d7954..8b79f26 100644 --- a/database/Redis.js +++ b/database/Redis.js @@ -116,7 +116,7 @@ class Redis { await this.redis.del(`byonline:users:${userId}`); await this.redis.hdel('byonline:users:nameToId', user.user.toUpperCase()); - // Is the below correct? Or should we remove stats for both games? + // TODO: Is the below correct? Or should we remove stats for both games? await this.redis.del(`byonline:stats:${game}:${userId}`); } else { await this.redis.hmset(`byonline:users:${userId}`, { @@ -209,7 +209,7 @@ class Redis { } async setStats(userId, game, stats) { - this.logger.info("WRITING STATS FOR " + userId + ": " + JSON.stringify(stats)); + this.logger.info("WRITING STATS FOR " + userId + " TO REDIS: " + JSON.stringify(stats)); await this.redis.hmset(`byonline:stats:${game}:${userId}`, stats); } diff --git a/database/WebAPI.js b/database/WebAPI.js index 1f3f0f0..e985dcf 100644 --- a/database/WebAPI.js +++ b/database/WebAPI.js @@ -1,6 +1,7 @@ "use strict"; const createLogger = require('logging').default; const bent = require('bent'); +const Stats = require('../global/Stats.js'); class WebAPI { constructor(config) { @@ -43,6 +44,40 @@ class WebAPI { // Set the icon in the Redis cache. redis.setIcon(userId, icon); } + + async setStats(userId, game, stats) { + this.logger.info("SENDING STATS FOR " + userId + " TO SITE: " + JSON.stringify(stats)); + const response = await this.post('/set_stats', { + token: this.token, + userId: userId, + game: game, + stats: stats, + }) + + if (response.error) { + this.logger.error("Failed to set stats!", { response }); + return; + } + } + + async getStats(userId, game) { + this.logger.info("GETTING STATS FOR " + userId + " FROM SITE"); + const response = await this.get('/get_stats', { + token: this.token, + userId: userId, + game: game, + }) + this.logger.info(JSON.stringify(response)); + let stats; + if (response['stats']) { + stats = response['stats']; + } else { + this.logger.info("SETTING DEFAULT STATS"); + await this.setStats(userId, game, Stats.DefaultStats[game]); + stats = Stats.DefaultStats[game]; + } + return stats; + } } module.exports = WebAPI; diff --git a/global/Stats.js b/global/Stats.js index cc00c88..14650ed 100644 --- a/global/Stats.js +++ b/global/Stats.js @@ -59,7 +59,7 @@ const ResultsMappers = { steals: resultsFields[9], strikeouts: resultsFields[10], walks: resultsFields[11], - quit: resultsFields[12], + disconnect: resultsFields[12], completedInnings: resultsFields[13], side: resultsSide }; diff --git a/net/AreaMessages.js b/net/AreaMessages.js index ed3f23e..b6747a8 100644 --- a/net/AreaMessages.js +++ b/net/AreaMessages.js @@ -108,12 +108,14 @@ server.handleMessage('game_started', async (client, args) => { await redis.sendUsersInArea(client.areaId, client.game); await redis.sendGamesPlayingInArea(client.areaId, client.game); + await redis.removeOngoingResults(client.userId, client.game); await redis.removeOngoingResults(playerId, client.game); }); server.handleMessage('game_finished', async (client, args) => { logger.info("GAME FINISHED " + client.userId + " vs. " + client.opponentId); + logger.info(args); await redis.sendGamesPlayingInArea(client.areaId, client.game); const user = await redis.getUserById(client.userId, client.game); if (user.inGame) { @@ -128,14 +130,18 @@ server.handleMessage('game_finished', async (client, args) => { Object.entries(finalResultsAsStrings).map(([k, stat]) => [k, Number(stat)]) ); // Get this user's existing stats - const statsStrings = await redis.getStats(client.userId, client.game); - let stats = Object.fromEntries( - Object.entries(statsStrings).map(([k, stat]) => [k, Number(stat)]) - ); + let stats; + stats = await database.getStats(client.userId, client.game); + if (database == redis) { + // If redis is the DB, we need to convert the values from strings to numbers + stats = Object.fromEntries( + Object.entries(stats).map(([k, stat]) => [k, Number(stat)]) + ); + } // Calculate updated stats stats = Stats.Aggregators[client.game](finalResults, stats); - await redis.setStats(client.userId, client.game, stats); + await database.setStats(client.userId, client.game, stats); } } }); diff --git a/net/DatabaseMessages.js b/net/DatabaseMessages.js index 6195b35..3c54efc 100644 --- a/net/DatabaseMessages.js +++ b/net/DatabaseMessages.js @@ -110,7 +110,7 @@ server.handleMessage('get_profile', async (client, args) => { if (user === {}) return; - const stats = await redis.getStats(userId, client.game); + const stats = await database.getStats(userId, client.game); logger.info("STATS: " + JSON.stringify(stats)); const formattedStats = Stats.StatsFormatters[client.game](stats); logger.info("FORMATTED STATS: " + formattedStats); @@ -180,6 +180,7 @@ server.handleMessage("game_results", async (client, args) => { resultsSide = "away"; } const resultsFields = args.fields; + logger.info("GAME_RESULTS FIELDS: " + resultsFields); const ongoingResults = Stats.ResultsMappers[client.game](resultsFields, resultsSide); logger.info("ONGOINGRESULTS: " + JSON.stringify(ongoingResults)); await redis.setOngoingResults(resultsUserId, client.game, ongoingResults); From ffe1141534adce69d3eeea3536358b2e40461bd7 Mon Sep 17 00:00:00 2001 From: shkupfer Date: Tue, 14 Dec 2021 19:35:25 -0500 Subject: [PATCH 12/15] Handle disconnects --- global/Stats.js | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/global/Stats.js b/global/Stats.js index 14650ed..6606277 100644 --- a/global/Stats.js +++ b/global/Stats.js @@ -1,6 +1,8 @@ "use strict"; const StatsFormatters = { + // These take a statistics object from redis or the site/Mongo + // and return an array in the order that the game expects "baseball": (stats) => { const numericStats = Object.fromEntries( Object.entries(stats).map(([k, stat]) => [k, Number(stat)]) @@ -45,6 +47,8 @@ const StatsFormatters = { }; const ResultsMappers = { + // These take an array that the game sends to `/game_results` + // and return an ongoing results object that can be stored in redis "baseball": (resultsFields, resultsSide) => { const ongoingResults = { winning: resultsFields[0], @@ -72,19 +76,11 @@ const ResultsMappers = { }; const Aggregators = { + // These combine a completed game's stats with that user's existing stats + // so that the user's stats can be updated to include that game "baseball": (finalResults, stats) => { const homeRuns = finalResults.hits - (finalResults.singles + finalResults.doubles + finalResults.triples); - const winSign = finalResults.winning * 2 - 1; - stats.wins += finalResults.winning; // Increment user's wins - stats.losses += (1 - finalResults.winning); // Increment user's losses - if (Math.sign(stats.streak) == winSign) { - // If user is continuing their streak, increment/decrement it. - stats.streak += winSign; - } else { - // If user is starting a new streak. - stats.streak = winSign; - } - // TODO (maybe): Wins in last 10 games and margin + // Update all of these counting stats and longest home run regardless of whether the user disconnected stats.games += 1; stats.atBats += finalResults.atBats; stats.hits += finalResults.hits; @@ -96,6 +92,22 @@ const Aggregators = { stats.strikeouts += finalResults.strikeouts; stats.walks += finalResults.walks; stats.longestHomeRun = Math.max(stats.longestHomeRun, finalResults.longestHomeRun); + // If the user disconnected, increment those. If not, update wins, losses, and streak as normal + if (finalResults.disconnect == 1) { + stats.disconnects += finalResults.disconnect; + } else { + stats.wins += finalResults.winning; // Increment user's wins + stats.losses += (1 - finalResults.winning); // Increment user's losses + const winSign = finalResults.winning * 2 - 1; + if (Math.sign(stats.streak) == winSign) { + // If user is continuing their streak, increment/decrement it. + stats.streak += winSign; + } else { + // If user is starting a new streak. + stats.streak = winSign; + } + // TODO (maybe): Wins in last 10 games and margin (what is that exactly?) + } return stats; }, // TODO: Football From 15b0059745e85de8afc064c58318ee0a9712bccd Mon Sep 17 00:00:00 2001 From: shkupfer Date: Thu, 23 Dec 2021 16:10:56 -0500 Subject: [PATCH 13/15] Removing some logs --- database/Redis.js | 15 ++++++--------- database/WebAPI.js | 4 ---- net/AreaMessages.js | 5 ----- net/DatabaseMessages.js | 5 ----- 4 files changed, 6 insertions(+), 23 deletions(-) diff --git a/database/Redis.js b/database/Redis.js index 8b79f26..fe55420 100644 --- a/database/Redis.js +++ b/database/Redis.js @@ -77,8 +77,10 @@ class Redis { await this.redis.hmset(`byonline:users:${userId}`, user); await this.redis.hset("byonline:users:nameToId", user['user'].toUpperCase(), userId); - const defaultStats = Stats.DefaultStats[game]; - await this.redis.hmset(`byonline:stats:${game}:${userId}`, defaultStats) + if (database == this) { + const defaultStats = Stats.DefaultStats[game]; + await this.redis.hmset(`byonline:stats:${game}:${userId}`, defaultStats); + } } async getUser(username, password, game) { @@ -116,8 +118,7 @@ class Redis { await this.redis.del(`byonline:users:${userId}`); await this.redis.hdel('byonline:users:nameToId', user.user.toUpperCase()); - // TODO: Is the below correct? Or should we remove stats for both games? - await this.redis.del(`byonline:stats:${game}:${userId}`); + await this.removeOngoingResults(client.userId, client.game); } else { await this.redis.hmset(`byonline:users:${userId}`, { 'game': '', @@ -125,8 +126,7 @@ class Redis { 'phone': 0, 'opponent': 0 }); - // Same question as above - await this.redis.hmset(`byonline:stats:${game}:${userId}`, Stats.DefaultStats[game]); + await this.removeOngoingResults(client.userId, client.game); } } @@ -209,7 +209,6 @@ class Redis { } async setStats(userId, game, stats) { - this.logger.info("WRITING STATS FOR " + userId + " TO REDIS: " + JSON.stringify(stats)); await this.redis.hmset(`byonline:stats:${game}:${userId}`, stats); } @@ -219,7 +218,6 @@ class Redis { if (await this.redis.exists(statsKey)) { stats = await this.redis.hgetall(statsKey); } else { - this.logger.info("SETTING DEFAULT STATS"); await this.setStats(userId, game, Stats.DefaultStats[game]); stats = Stats.DefaultStats[game]; } @@ -242,7 +240,6 @@ class Redis { async removeOngoingResults(userId, game) { const resultsKey = `byonline:ongoingResults:${game}:${userId}`; if (await this.redis.exists(resultsKey)) { - this.logger.info("REMOVING ONGOING RESULTS FOR KEY " + resultsKey) await this.redis.del(resultsKey); } } diff --git a/database/WebAPI.js b/database/WebAPI.js index e985dcf..c8219f4 100644 --- a/database/WebAPI.js +++ b/database/WebAPI.js @@ -46,7 +46,6 @@ class WebAPI { } async setStats(userId, game, stats) { - this.logger.info("SENDING STATS FOR " + userId + " TO SITE: " + JSON.stringify(stats)); const response = await this.post('/set_stats', { token: this.token, userId: userId, @@ -61,18 +60,15 @@ class WebAPI { } async getStats(userId, game) { - this.logger.info("GETTING STATS FOR " + userId + " FROM SITE"); const response = await this.get('/get_stats', { token: this.token, userId: userId, game: game, }) - this.logger.info(JSON.stringify(response)); let stats; if (response['stats']) { stats = response['stats']; } else { - this.logger.info("SETTING DEFAULT STATS"); await this.setStats(userId, game, Stats.DefaultStats[game]); stats = Stats.DefaultStats[game]; } diff --git a/net/AreaMessages.js b/net/AreaMessages.js index b6747a8..832f726 100644 --- a/net/AreaMessages.js +++ b/net/AreaMessages.js @@ -100,7 +100,6 @@ process.on('update_players_list', (args) => { }); server.handleMessage('game_started', async (client, args) => { - logger.info("GAME STARTED " + client.userId + " " + args.user); const playerId = args.user; await redis.setInGame(client.userId, 1); @@ -114,15 +113,11 @@ server.handleMessage('game_started', async (client, args) => { }); server.handleMessage('game_finished', async (client, args) => { - logger.info("GAME FINISHED " + client.userId + " vs. " + client.opponentId); - logger.info(args); await redis.sendGamesPlayingInArea(client.areaId, client.game); const user = await redis.getUserById(client.userId, client.game); if (user.inGame) { - logger.info("USER " + client.userId + " IS IN GAME"); await redis.setInGame(client.userId, 0); if (await redis.hasOngoingResults(client.userId, client.game)) { - logger.info("HAS ONGOING RESULTS"); // Get the most recent results data const finalResultsAsStrings = await redis.getOngoingResults(client.userId, client.game); await redis.removeOngoingResults(client.userId, client.game); diff --git a/net/DatabaseMessages.js b/net/DatabaseMessages.js index 3c54efc..b57453d 100644 --- a/net/DatabaseMessages.js +++ b/net/DatabaseMessages.js @@ -111,11 +111,8 @@ server.handleMessage('get_profile', async (client, args) => { return; const stats = await database.getStats(userId, client.game); - logger.info("STATS: " + JSON.stringify(stats)); const formattedStats = Stats.StatsFormatters[client.game](stats); - logger.info("FORMATTED STATS: " + formattedStats); const profile = [user.icon].concat(formattedStats); - logger.info(profile); client.send("profile_info", {profile: profile}); }); @@ -180,8 +177,6 @@ server.handleMessage("game_results", async (client, args) => { resultsSide = "away"; } const resultsFields = args.fields; - logger.info("GAME_RESULTS FIELDS: " + resultsFields); const ongoingResults = Stats.ResultsMappers[client.game](resultsFields, resultsSide); - logger.info("ONGOINGRESULTS: " + JSON.stringify(ongoingResults)); await redis.setOngoingResults(resultsUserId, client.game, ongoingResults); }); From 3b807c45aeb03c6a0dd2ff4272b41c028fd71efb Mon Sep 17 00:00:00 2001 From: shkupfer Date: Thu, 23 Dec 2021 18:17:59 -0500 Subject: [PATCH 14/15] Removing support for actual stats when using redis as DB --- database/Redis.js | 22 +++++----------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/database/Redis.js b/database/Redis.js index fe55420..8274b8f 100644 --- a/database/Redis.js +++ b/database/Redis.js @@ -50,6 +50,7 @@ class Redis { 'id': Number(userId), 'user': response['user'], 'icon': Number(response['icon']), + 'stats': '', // TODO 'game': response['game'], 'area': Number(response['area']), 'inGame': Number(response['inGame']), @@ -76,11 +77,6 @@ class Redis { await this.redis.hmset(`byonline:users:${userId}`, user); await this.redis.hset("byonline:users:nameToId", user['user'].toUpperCase(), userId); - - if (database == this) { - const defaultStats = Stats.DefaultStats[game]; - await this.redis.hmset(`byonline:stats:${game}:${userId}`, defaultStats); - } } async getUser(username, password, game) { @@ -118,7 +114,7 @@ class Redis { await this.redis.del(`byonline:users:${userId}`); await this.redis.hdel('byonline:users:nameToId', user.user.toUpperCase()); - await this.removeOngoingResults(client.userId, client.game); + } else { await this.redis.hmset(`byonline:users:${userId}`, { 'game': '', @@ -126,8 +122,8 @@ class Redis { 'phone': 0, 'opponent': 0 }); - await this.removeOngoingResults(client.userId, client.game); } + await this.removeOngoingResults(client.userId, client.game); } async setIcon(userId, icon) { @@ -209,19 +205,11 @@ class Redis { } async setStats(userId, game, stats) { - await this.redis.hmset(`byonline:stats:${game}:${userId}`, stats); + this.logger.warn("Stats not supported with Redis DB!"); } async getStats(userId, game) { - const statsKey = `byonline:stats:${game}:${userId}`; - let stats; - if (await this.redis.exists(statsKey)) { - stats = await this.redis.hgetall(statsKey); - } else { - await this.setStats(userId, game, Stats.DefaultStats[game]); - stats = Stats.DefaultStats[game]; - } - return stats; + return Stats.DefaultStats[game]; } async setOngoingResults(userId, game, ongoingResults) { From d9083b192863b7642a3c6c907eb7c60075303486 Mon Sep 17 00:00:00 2001 From: shkupfer Date: Thu, 23 Dec 2021 18:19:07 -0500 Subject: [PATCH 15/15] Removing support for actual stats when using redis as DB again --- database/Redis.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/database/Redis.js b/database/Redis.js index 8274b8f..284f26b 100644 --- a/database/Redis.js +++ b/database/Redis.js @@ -50,7 +50,6 @@ class Redis { 'id': Number(userId), 'user': response['user'], 'icon': Number(response['icon']), - 'stats': '', // TODO 'game': response['game'], 'area': Number(response['area']), 'inGame': Number(response['inGame']), @@ -114,7 +113,6 @@ class Redis { await this.redis.del(`byonline:users:${userId}`); await this.redis.hdel('byonline:users:nameToId', user.user.toUpperCase()); - } else { await this.redis.hmset(`byonline:users:${userId}`, { 'game': '',