From b7c60554f0acb1af511401cbf356b4d1cd019c51 Mon Sep 17 00:00:00 2001 From: Eric Lin Date: Sat, 18 Jun 2022 12:56:06 -0400 Subject: [PATCH] fix: zarimanCycle logic (#400) Co-authored-by: Tobiah --- lib/WorldState.js | 5 ++- lib/ZarimanCycle.js | 43 ++++++++++++---------- test/integration/integration.spec.js | 55 +++++++++++++--------------- test/unit/zariman.spec.js | 11 +++--- 4 files changed, 59 insertions(+), 55 deletions(-) diff --git a/lib/WorldState.js b/lib/WorldState.js index 3c3e8b654..8440f7bef 100644 --- a/lib/WorldState.js +++ b/lib/WorldState.js @@ -242,11 +242,14 @@ module.exports = class WorldState { */ this.cambionCycle = new CambionCycle(this.cetusCycle, deps); + const zarimanSynd = safeArray(data.SyndicateMissions).filter((syndicate) => syndicate.Tag === 'ZarimanSyndicate'); + const zarimanBountyEnd = timeDate.parseDate(zarimanSynd.length > 0 ? zarimanSynd[0].Expiry : { $date: 0 }); + /** * The current Zariman cycle based off current time * @type {ZarimanCycle} */ - this.zarimanCycle = new ZarimanCycle(Date.now(), deps); + this.zarimanCycle = new ZarimanCycle(zarimanBountyEnd, deps); /** * Weekly challenges diff --git a/lib/ZarimanCycle.js b/lib/ZarimanCycle.js index e27e9982b..fae71d627 100644 --- a/lib/ZarimanCycle.js +++ b/lib/ZarimanCycle.js @@ -5,9 +5,9 @@ const WorldstateObject = require('./WorldstateObject'); // This is a confirmed starting time for Corpus (in millis) // All faction operation should use this as a calculation point // Unless there's a better logic -const corpusTimeMillis = 1654725600000; -const fullCycle = 28800000; -const stateMaximum = 14400000; +const corpusTimeMillis = 1655182800000; +const fullCycle = 18000000; +const stateMaximum = 9000000; /** * Represents the current Zariman Corpus/Grineer Cycle @@ -15,12 +15,12 @@ const stateMaximum = 14400000; */ module.exports = class ZarimanCycle extends WorldstateObject { /** - * @param {Date} currentTime The current time to calculate Zariman cycle for + * @param {Date} bountiesEndDate The current zariman cycle expiry * @param {Object} deps The dependencies object * @param {MarkdownSettings} deps.mdConfig The markdown settings * @param {TimeDateFunctions} deps.timeDate The time and date functions */ - constructor(currentTime, { mdConfig, timeDate }) { + constructor(bountiesEndDate, { mdConfig, timeDate }) { super({ _id: { $oid: 'zarimanCycle0' } }, { timeDate }); /** @@ -36,7 +36,7 @@ module.exports = class ZarimanCycle extends WorldstateObject { * @type {Date} * @private */ - this.currentTime = currentTime; + this.bountiesEndDate = bountiesEndDate; Object.defineProperty(this, 'currentTime', { enumerable: false, configurable: false }); /** @@ -68,10 +68,10 @@ module.exports = class ZarimanCycle extends WorldstateObject { this.activation = new Date(ec.start); /** - * Whether or not this it's daytime + * Whether or not this it's corpus or grineer * @type {boolean} */ - this.isCorpus = ec.corpusTime; + this.isCorpus = ec.isCorpus; /** * Current cycle state. One of `corpus`, `grineer` @@ -95,24 +95,29 @@ module.exports = class ZarimanCycle extends WorldstateObject { * @returns {boolean} */ getExpired() { - return this.timeDate.fromNow(this.expiry) < 0; + return this.expiry ? this.timeDate.fromNow(this.expiry) < 0 : true; } getCurrentZarimanCycle() { - // determine if it is corpus cycle or grineer cycle - const timeInCycle = (this.currentTime - corpusTimeMillis) % fullCycle; - // if timeInCycle is less than 4 hours, it is corpus, otherwise it is grineer - const corpusTime = timeInCycle <= stateMaximum; - - // cycles are offset by 2 hours from bounties - const millisLeft = fullCycle - timeInCycle; + const now = Date.now(); + // determine if it is corpus cycle or grineer cycle based on bounty end time + // we subtract 5000 millis (5 seconds) to ensure the corpus/grineer calculation is correct + const bountiesClone = this.bountiesEndDate.getTime() - 5000; + const millisLeft = this.timeDate.fromNow(new Date(bountiesClone)); + // the following line is a modulus operation + // this ensures that our calculation is correct if bountiesClone is before corpusTimeMillis + // if you really care, read https://torstencurdt.com/tech/posts/modulo-of-negative-numbers/ + const cycleTimeElapsed = (((bountiesClone - corpusTimeMillis) % fullCycle) + fullCycle) % fullCycle; + const cycleTimeLeft = fullCycle - cycleTimeElapsed; + // if timeInCycle is more than 2.5 hours, it is corpus, otherwise it is grineer + const isCorpus = cycleTimeLeft > stateMaximum; const minutesCoef = 1000 * 60; - const expiry = new Date(Math.round((this.currentTime + millisLeft) / minutesCoef) * minutesCoef); - const state = corpusTime ? 'corpus' : 'grineer'; + const expiry = new Date(Math.round((now + millisLeft) / minutesCoef) * minutesCoef); + const state = isCorpus ? 'corpus' : 'grineer'; return { - corpusTime, + isCorpus, timeLeft: this.timeDate.timeDeltaToString(millisLeft), expiry, expiresIn: millisLeft, diff --git a/test/integration/integration.spec.js b/test/integration/integration.spec.js index f90de6499..76634c7e9 100644 --- a/test/integration/integration.spec.js +++ b/test/integration/integration.spec.js @@ -3,9 +3,10 @@ const chai = require('chai'); const sinonChai = require('sinon-chai'); const rewire = require('rewire'); -const fs = require('fs'); +const fs = require('fs').promises; const fetch = require('node-fetch'); +const path = require('path'); const logger = { error: () => {}, @@ -21,34 +22,30 @@ const WorldState = rewire('../../lib/WorldState.js'); describe('WorldState (integration)', () => { describe('#constructor()', async () => { - ['pc', 'ps4', 'xb1', 'swi'].forEach((platform) => { - it('should parse live worldstate data', function (done) { - this.timeout = 10000; // allow 10 seconds to parse the worldstate - const url = `https://content.${platform === 'pc' ? '' : `${platform}.`}warframe.com/dynamic/worldState.php`; - fetch(url) - .then((d) => d.text()) - .then((ws) => { - let wsl; - (() => { - wsl = new WorldState(ws, { logger, locale: 'en' }); - }).should.not.throw(); - - wsl.news.forEach((article) => { - if (article.message.toLowerCase().includes('stream')) { - article.should.include({ stream: true }); - } - }); - - /* Easy debugging! */ - setTimeout(() => { - fs.writeFileSync( - `./data.${platform}.json`, - JSON.stringify(wsl.syndicateMissions.find((m) => m.syndicateKey === 'Ostrons')) - ); - done(); - }, 1000); + await Promise.all( + ['pc', 'ps4', 'xb1', 'swi'].map(async function (platform) { + it(`should parse live ${platform} worldstate data`, async function () { + this.timeout = 10000; // allow 10 seconds to parse the worldstate + const url = `https://content.${platform === 'pc' ? '' : `${platform}.`}warframe.com/dynamic/worldState.php`; + const ws = await fetch(url).then((d) => d.text()); + + let wsl; + (() => { + wsl = new WorldState(ws, { logger, locale: 'en' }); + }).should.not.throw(); + + wsl.news.forEach((article) => { + if (article.message.toLowerCase().includes('stream')) { + article.should.include({ stream: true }); + } }); - }); - }); + /* Easy debugging! */ + return fs.writeFile( + path.resolve(`./data.${platform}.json`), + JSON.stringify(wsl.syndicateMissions.find((m) => m.syndicateKey === 'Ostrons')) + ); + }); + }) + ); }); }); diff --git a/test/unit/zariman.spec.js b/test/unit/zariman.spec.js index f89b21704..a0c8687f6 100644 --- a/test/unit/zariman.spec.js +++ b/test/unit/zariman.spec.js @@ -7,9 +7,10 @@ const MarkdownSettings = require('../../lib/supporting/MarkdownSettings'); const should = chai.should(); const mdConfig = new MarkdownSettings(); -// this is a confirmed corpus cycle time -// the zariman cycle logic depends on the current time -const confirmedCorpus = 1654725600001; +// these are confirmed corpus and grineer cycle time +// the zariman cycle logic depends on zariman bounty expiry time +const confirmedCorpus = 1655059552000; +const confirmedGrineer = 1655181672000; describe('ZarimanCycle', function () { describe('#constructor()', function () { @@ -43,9 +44,7 @@ describe('ZarimanCycle', function () { }); it('should show grineer cycle string', () => { - // 14400000 is 4 hours in millis, by adding 4 hours, it should show - // a grineer cycle - const cycle = new ZarimanCycle(new Date(confirmedCorpus + 14400000), { + const cycle = new ZarimanCycle(new Date(confirmedGrineer), { timeDate, mdConfig, });