forked from OpenWF/SpaceNinjaServer
		
	feat: dynamic void fissure missions (#2214)
Closes #1512 Reviewed-on: OpenWF/SpaceNinjaServer#2214 Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com> Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
This commit is contained in:
		
							parent
							
								
									3c64f17e34
								
							
						
					
					
						commit
						95136e6059
					
				@ -1,6 +1,15 @@
 | 
				
			|||||||
import { RequestHandler } from "express";
 | 
					import { RequestHandler } from "express";
 | 
				
			||||||
import { getWorldState } from "@/src/services/worldStateService";
 | 
					import { getWorldState, populateFissures } from "@/src/services/worldStateService";
 | 
				
			||||||
 | 
					import { version_compare } from "@/src/helpers/inventoryHelpers";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const worldStateController: RequestHandler = (req, res) => {
 | 
					export const worldStateController: RequestHandler = async (req, res) => {
 | 
				
			||||||
    res.json(getWorldState(req.query.buildLabel as string | undefined));
 | 
					    const buildLabel = req.query.buildLabel as string | undefined;
 | 
				
			||||||
 | 
					    const worldState = getWorldState(buildLabel);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Omitting void fissures for versions prior to Dante Unbound to avoid script errors.
 | 
				
			||||||
 | 
					    if (!buildLabel || version_compare(buildLabel, "2024.03.24.20.00") >= 0) {
 | 
				
			||||||
 | 
					        await populateFissures(worldState);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    res.json(worldState);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
				
			|||||||
@ -22,6 +22,7 @@ import { JSONStringify } from "json-with-bigint";
 | 
				
			|||||||
import { startWebServer } from "./services/webService";
 | 
					import { startWebServer } from "./services/webService";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { validateConfig } from "@/src/services/configWatcherService";
 | 
					import { validateConfig } from "@/src/services/configWatcherService";
 | 
				
			||||||
 | 
					import { updateWorldStateCollections } from "./services/worldStateService";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Patch JSON.stringify to work flawlessly with Bigints.
 | 
					// Patch JSON.stringify to work flawlessly with Bigints.
 | 
				
			||||||
JSON.stringify = JSONStringify;
 | 
					JSON.stringify = JSONStringify;
 | 
				
			||||||
@ -33,6 +34,11 @@ mongoose
 | 
				
			|||||||
    .then(() => {
 | 
					    .then(() => {
 | 
				
			||||||
        logger.info("Connected to MongoDB");
 | 
					        logger.info("Connected to MongoDB");
 | 
				
			||||||
        startWebServer();
 | 
					        startWebServer();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        void updateWorldStateCollections();
 | 
				
			||||||
 | 
					        setInterval(() => {
 | 
				
			||||||
 | 
					            void updateWorldStateCollections();
 | 
				
			||||||
 | 
					        }, 60_000);
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
    .catch(error => {
 | 
					    .catch(error => {
 | 
				
			||||||
        if (error instanceof Error) {
 | 
					        if (error instanceof Error) {
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										14
									
								
								src/models/worldStateModel.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								src/models/worldStateModel.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,14 @@
 | 
				
			|||||||
 | 
					import { IFissureDatabase } from "@/src/types/worldStateTypes";
 | 
				
			||||||
 | 
					import { model, Schema } from "mongoose";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const fissureSchema = new Schema<IFissureDatabase>({
 | 
				
			||||||
 | 
					    Activation: Date,
 | 
				
			||||||
 | 
					    Expiry: Date,
 | 
				
			||||||
 | 
					    Node: String, // must be unique
 | 
				
			||||||
 | 
					    Modifier: String,
 | 
				
			||||||
 | 
					    Hard: Boolean
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fissureSchema.index({ Expiry: 1 }, { expireAfterSeconds: 0 }); // With this, MongoDB will automatically delete expired entries.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const Fissure = model<IFissureDatabase>("Fissure", fissureSchema);
 | 
				
			||||||
@ -1,12 +1,13 @@
 | 
				
			|||||||
import staticWorldState from "@/static/fixed_responses/worldState/worldState.json";
 | 
					import staticWorldState from "@/static/fixed_responses/worldState/worldState.json";
 | 
				
			||||||
 | 
					import fissureMissions from "@/static/fixed_responses/worldState/fissureMissions.json";
 | 
				
			||||||
import sortieTilesets from "@/static/fixed_responses/worldState/sortieTilesets.json";
 | 
					import sortieTilesets from "@/static/fixed_responses/worldState/sortieTilesets.json";
 | 
				
			||||||
import sortieTilesetMissions from "@/static/fixed_responses/worldState/sortieTilesetMissions.json";
 | 
					import sortieTilesetMissions from "@/static/fixed_responses/worldState/sortieTilesetMissions.json";
 | 
				
			||||||
import syndicateMissions from "@/static/fixed_responses/worldState/syndicateMissions.json";
 | 
					import syndicateMissions from "@/static/fixed_responses/worldState/syndicateMissions.json";
 | 
				
			||||||
import { buildConfig } from "@/src/services/buildConfigService";
 | 
					import { buildConfig } from "@/src/services/buildConfigService";
 | 
				
			||||||
import { unixTimesInMs } from "@/src/constants/timeConstants";
 | 
					import { unixTimesInMs } from "@/src/constants/timeConstants";
 | 
				
			||||||
import { config } from "@/src/services/configService";
 | 
					import { config } from "@/src/services/configService";
 | 
				
			||||||
import { SRng } from "@/src/services/rngService";
 | 
					import { getRandomElement, getRandomInt, SRng } from "@/src/services/rngService";
 | 
				
			||||||
import { ExportRegions, ExportSyndicates, IRegion } from "warframe-public-export-plus";
 | 
					import { eMissionType, ExportRegions, ExportSyndicates, IRegion } from "warframe-public-export-plus";
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
    ICalendarDay,
 | 
					    ICalendarDay,
 | 
				
			||||||
    ICalendarEvent,
 | 
					    ICalendarEvent,
 | 
				
			||||||
@ -21,8 +22,9 @@ import {
 | 
				
			|||||||
    IWorldState,
 | 
					    IWorldState,
 | 
				
			||||||
    TCircuitGameMode
 | 
					    TCircuitGameMode
 | 
				
			||||||
} from "../types/worldStateTypes";
 | 
					} from "../types/worldStateTypes";
 | 
				
			||||||
import { version_compare } from "../helpers/inventoryHelpers";
 | 
					import { toMongoDate, toOid, version_compare } from "../helpers/inventoryHelpers";
 | 
				
			||||||
import { logger } from "../utils/logger";
 | 
					import { logger } from "../utils/logger";
 | 
				
			||||||
 | 
					import { Fissure } from "../models/worldStateModel";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const sortieBosses = [
 | 
					const sortieBosses = [
 | 
				
			||||||
    "SORTIE_BOSS_HYENA",
 | 
					    "SORTIE_BOSS_HYENA",
 | 
				
			||||||
@ -1110,6 +1112,7 @@ export const getWorldState = (buildLabel?: string): IWorldState => {
 | 
				
			|||||||
        Alerts: [],
 | 
					        Alerts: [],
 | 
				
			||||||
        Sorties: [],
 | 
					        Sorties: [],
 | 
				
			||||||
        LiteSorties: [],
 | 
					        LiteSorties: [],
 | 
				
			||||||
 | 
					        ActiveMissions: [],
 | 
				
			||||||
        GlobalUpgrades: [],
 | 
					        GlobalUpgrades: [],
 | 
				
			||||||
        VoidStorms: [],
 | 
					        VoidStorms: [],
 | 
				
			||||||
        EndlessXpChoices: [],
 | 
					        EndlessXpChoices: [],
 | 
				
			||||||
@ -1118,14 +1121,10 @@ export const getWorldState = (buildLabel?: string): IWorldState => {
 | 
				
			|||||||
        SyndicateMissions: [...staticWorldState.SyndicateMissions]
 | 
					        SyndicateMissions: [...staticWorldState.SyndicateMissions]
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Omit void fissures for versions prior to Dante Unbound to avoid script errors.
 | 
					 | 
				
			||||||
    if (buildLabel && version_compare(buildLabel, "2024.03.24.20.00") < 0) {
 | 
					 | 
				
			||||||
        worldState.ActiveMissions = [];
 | 
					 | 
				
			||||||
        if (version_compare(buildLabel, "2017.10.12.17.04") < 0) {
 | 
					 | 
				
			||||||
    // Old versions seem to really get hung up on not being able to load these.
 | 
					    // Old versions seem to really get hung up on not being able to load these.
 | 
				
			||||||
 | 
					    if (buildLabel && version_compare(buildLabel, "2017.10.12.17.04") < 0) {
 | 
				
			||||||
        worldState.PVPChallengeInstances = [];
 | 
					        worldState.PVPChallengeInstances = [];
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (config.worldState?.starDays) {
 | 
					    if (config.worldState?.starDays) {
 | 
				
			||||||
        worldState.Goals.push({
 | 
					        worldState.Goals.push({
 | 
				
			||||||
@ -1364,6 +1363,24 @@ export const getWorldState = (buildLabel?: string): IWorldState => {
 | 
				
			|||||||
    return worldState;
 | 
					    return worldState;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const populateFissures = async (worldState: IWorldState): Promise<void> => {
 | 
				
			||||||
 | 
					    const fissures = await Fissure.find({});
 | 
				
			||||||
 | 
					    for (const fissure of fissures) {
 | 
				
			||||||
 | 
					        const meta = ExportRegions[fissure.Node];
 | 
				
			||||||
 | 
					        worldState.ActiveMissions.push({
 | 
				
			||||||
 | 
					            _id: toOid(fissure._id),
 | 
				
			||||||
 | 
					            Region: meta.systemIndex + 1,
 | 
				
			||||||
 | 
					            Seed: 1337,
 | 
				
			||||||
 | 
					            Activation: toMongoDate(fissure.Activation),
 | 
				
			||||||
 | 
					            Expiry: toMongoDate(fissure.Expiry),
 | 
				
			||||||
 | 
					            Node: fissure.Node,
 | 
				
			||||||
 | 
					            MissionType: eMissionType[meta.missionIndex].tag,
 | 
				
			||||||
 | 
					            Modifier: fissure.Modifier,
 | 
				
			||||||
 | 
					            Hard: fissure.Hard
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const idToBountyCycle = (id: string): number => {
 | 
					export const idToBountyCycle = (id: string): number => {
 | 
				
			||||||
    return Math.trunc((parseInt(id.substring(0, 8), 16) * 1000) / 9000_000);
 | 
					    return Math.trunc((parseInt(id.substring(0, 8), 16) * 1000) / 9000_000);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
@ -1491,3 +1508,57 @@ const nightwaveTagToSeason: Record<string, number> = {
 | 
				
			|||||||
    RadioLegionIntermissionSyndicate: 1, // Intermission I
 | 
					    RadioLegionIntermissionSyndicate: 1, // Intermission I
 | 
				
			||||||
    RadioLegionSyndicate: 0 // The Wolf of Saturn Six
 | 
					    RadioLegionSyndicate: 0 // The Wolf of Saturn Six
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const updateWorldStateCollections = async (): Promise<void> => {
 | 
				
			||||||
 | 
					    const fissures = await Fissure.find();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const activeNodes = new Set<string>();
 | 
				
			||||||
 | 
					    const tierToFurthestExpiry: Record<string, number> = {
 | 
				
			||||||
 | 
					        VoidT1: 0,
 | 
				
			||||||
 | 
					        VoidT2: 0,
 | 
				
			||||||
 | 
					        VoidT3: 0,
 | 
				
			||||||
 | 
					        VoidT4: 0,
 | 
				
			||||||
 | 
					        VoidT5: 0,
 | 
				
			||||||
 | 
					        VoidT6: 0,
 | 
				
			||||||
 | 
					        VoidT1Hard: 0,
 | 
				
			||||||
 | 
					        VoidT2Hard: 0,
 | 
				
			||||||
 | 
					        VoidT3Hard: 0,
 | 
				
			||||||
 | 
					        VoidT4Hard: 0,
 | 
				
			||||||
 | 
					        VoidT5Hard: 0,
 | 
				
			||||||
 | 
					        VoidT6Hard: 0
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    for (const fissure of fissures) {
 | 
				
			||||||
 | 
					        activeNodes.add(fissure.Node);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const key = fissure.Modifier + (fissure.Hard ? "Hard" : "");
 | 
				
			||||||
 | 
					        tierToFurthestExpiry[key] = Math.max(tierToFurthestExpiry[key], fissure.Expiry.getTime());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const deadline = Date.now() - 6 * unixTimesInMs.minute;
 | 
				
			||||||
 | 
					    for (const [tier, expiry] of Object.entries(tierToFurthestExpiry)) {
 | 
				
			||||||
 | 
					        if (expiry < deadline) {
 | 
				
			||||||
 | 
					            const numFissures = getRandomInt(1, 3);
 | 
				
			||||||
 | 
					            for (let i = 0; i != numFissures; ++i) {
 | 
				
			||||||
 | 
					                const modifier = tier.replace("Hard", "") as
 | 
				
			||||||
 | 
					                    | "VoidT1"
 | 
				
			||||||
 | 
					                    | "VoidT2"
 | 
				
			||||||
 | 
					                    | "VoidT3"
 | 
				
			||||||
 | 
					                    | "VoidT4"
 | 
				
			||||||
 | 
					                    | "VoidT5"
 | 
				
			||||||
 | 
					                    | "VoidT6";
 | 
				
			||||||
 | 
					                let node: string;
 | 
				
			||||||
 | 
					                do {
 | 
				
			||||||
 | 
					                    node = getRandomElement(fissureMissions[modifier])!;
 | 
				
			||||||
 | 
					                } while (activeNodes.has(node));
 | 
				
			||||||
 | 
					                activeNodes.add(node);
 | 
				
			||||||
 | 
					                await Fissure.insertOne({
 | 
				
			||||||
 | 
					                    Activation: new Date(),
 | 
				
			||||||
 | 
					                    Expiry: new Date(Date.now() + getRandomInt(60, 120) * unixTimesInMs.minute),
 | 
				
			||||||
 | 
					                    Node: node,
 | 
				
			||||||
 | 
					                    Modifier: modifier,
 | 
				
			||||||
 | 
					                    Hard: tier.indexOf("Hard") != -1 ? true : undefined
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
				
			|||||||
@ -9,8 +9,8 @@ export interface IWorldState {
 | 
				
			|||||||
    Sorties: ISortie[];
 | 
					    Sorties: ISortie[];
 | 
				
			||||||
    LiteSorties: ILiteSortie[];
 | 
					    LiteSorties: ILiteSortie[];
 | 
				
			||||||
    SyndicateMissions: ISyndicateMissionInfo[];
 | 
					    SyndicateMissions: ISyndicateMissionInfo[];
 | 
				
			||||||
    GlobalUpgrades: IGlobalUpgrade[];
 | 
					 | 
				
			||||||
    ActiveMissions: IFissure[];
 | 
					    ActiveMissions: IFissure[];
 | 
				
			||||||
 | 
					    GlobalUpgrades: IGlobalUpgrade[];
 | 
				
			||||||
    NodeOverrides: INodeOverride[];
 | 
					    NodeOverrides: INodeOverride[];
 | 
				
			||||||
    VoidStorms: IVoidStorm[];
 | 
					    VoidStorms: IVoidStorm[];
 | 
				
			||||||
    PVPChallengeInstances: IPVPChallengeInstance[];
 | 
					    PVPChallengeInstances: IPVPChallengeInstance[];
 | 
				
			||||||
@ -86,6 +86,14 @@ export interface IFissure {
 | 
				
			|||||||
    Hard?: boolean;
 | 
					    Hard?: boolean;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface IFissureDatabase {
 | 
				
			||||||
 | 
					    Activation: Date;
 | 
				
			||||||
 | 
					    Expiry: Date;
 | 
				
			||||||
 | 
					    Node: string;
 | 
				
			||||||
 | 
					    Modifier: "VoidT1" | "VoidT2" | "VoidT3" | "VoidT4" | "VoidT5" | "VoidT6";
 | 
				
			||||||
 | 
					    Hard?: boolean;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface INodeOverride {
 | 
					export interface INodeOverride {
 | 
				
			||||||
    _id: IOid;
 | 
					    _id: IOid;
 | 
				
			||||||
    Activation?: IMongoDate;
 | 
					    Activation?: IMongoDate;
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										154
									
								
								static/fixed_responses/worldState/fissureMissions.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										154
									
								
								static/fixed_responses/worldState/fissureMissions.json
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,154 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					  "VoidT1": [
 | 
				
			||||||
 | 
					    "SolNode23",
 | 
				
			||||||
 | 
					    "SolNode66",
 | 
				
			||||||
 | 
					    "SolNode45",
 | 
				
			||||||
 | 
					    "SolNode41",
 | 
				
			||||||
 | 
					    "SolNode59",
 | 
				
			||||||
 | 
					    "SolNode39",
 | 
				
			||||||
 | 
					    "SolNode75",
 | 
				
			||||||
 | 
					    "SolNode113",
 | 
				
			||||||
 | 
					    "SolNode85",
 | 
				
			||||||
 | 
					    "SolNode58",
 | 
				
			||||||
 | 
					    "SolNode101",
 | 
				
			||||||
 | 
					    "SolNode109",
 | 
				
			||||||
 | 
					    "SolNode26",
 | 
				
			||||||
 | 
					    "SolNode15",
 | 
				
			||||||
 | 
					    "SolNode61",
 | 
				
			||||||
 | 
					    "SolNode123",
 | 
				
			||||||
 | 
					    "SolNode16",
 | 
				
			||||||
 | 
					    "SolNode79",
 | 
				
			||||||
 | 
					    "SolNode2",
 | 
				
			||||||
 | 
					    "SolNode22",
 | 
				
			||||||
 | 
					    "SolNode68",
 | 
				
			||||||
 | 
					    "SolNode89",
 | 
				
			||||||
 | 
					    "SolNode11",
 | 
				
			||||||
 | 
					    "SolNode46",
 | 
				
			||||||
 | 
					    "SolNode36",
 | 
				
			||||||
 | 
					    "SolNode27",
 | 
				
			||||||
 | 
					    "SolNode14",
 | 
				
			||||||
 | 
					    "SolNode106",
 | 
				
			||||||
 | 
					    "SolNode30",
 | 
				
			||||||
 | 
					    "SolNode107",
 | 
				
			||||||
 | 
					    "SolNode63",
 | 
				
			||||||
 | 
					    "SolNode128"
 | 
				
			||||||
 | 
					  ],
 | 
				
			||||||
 | 
					  "VoidT2": [
 | 
				
			||||||
 | 
					    "SolNode141",
 | 
				
			||||||
 | 
					    "SolNode149",
 | 
				
			||||||
 | 
					    "SolNode10",
 | 
				
			||||||
 | 
					    "SolNode93",
 | 
				
			||||||
 | 
					    "SettlementNode11",
 | 
				
			||||||
 | 
					    "SolNode137",
 | 
				
			||||||
 | 
					    "SolNode132",
 | 
				
			||||||
 | 
					    "SolNode73",
 | 
				
			||||||
 | 
					    "SolNode82",
 | 
				
			||||||
 | 
					    "SolNode25",
 | 
				
			||||||
 | 
					    "SolNode88",
 | 
				
			||||||
 | 
					    "SolNode126",
 | 
				
			||||||
 | 
					    "SolNode135",
 | 
				
			||||||
 | 
					    "SolNode74",
 | 
				
			||||||
 | 
					    "SettlementNode15",
 | 
				
			||||||
 | 
					    "SolNode147",
 | 
				
			||||||
 | 
					    "SolNode67",
 | 
				
			||||||
 | 
					    "SolNode20",
 | 
				
			||||||
 | 
					    "SolNode42",
 | 
				
			||||||
 | 
					    "SolNode18",
 | 
				
			||||||
 | 
					    "SolNode31",
 | 
				
			||||||
 | 
					    "SolNode139",
 | 
				
			||||||
 | 
					    "SettlementNode12",
 | 
				
			||||||
 | 
					    "SolNode100",
 | 
				
			||||||
 | 
					    "SolNode140",
 | 
				
			||||||
 | 
					    "SolNode70",
 | 
				
			||||||
 | 
					    "SettlementNode1",
 | 
				
			||||||
 | 
					    "SettlementNode14",
 | 
				
			||||||
 | 
					    "SolNode50",
 | 
				
			||||||
 | 
					    "SettlementNode2",
 | 
				
			||||||
 | 
					    "SolNode146",
 | 
				
			||||||
 | 
					    "SettlementNode3",
 | 
				
			||||||
 | 
					    "SolNode97",
 | 
				
			||||||
 | 
					    "SolNode125",
 | 
				
			||||||
 | 
					    "SolNode19",
 | 
				
			||||||
 | 
					    "SolNode121",
 | 
				
			||||||
 | 
					    "SolNode96",
 | 
				
			||||||
 | 
					    "SolNode131"
 | 
				
			||||||
 | 
					  ],
 | 
				
			||||||
 | 
					  "VoidT3": [
 | 
				
			||||||
 | 
					    "SolNode62",
 | 
				
			||||||
 | 
					    "SolNode17",
 | 
				
			||||||
 | 
					    "SolNode403",
 | 
				
			||||||
 | 
					    "SolNode6",
 | 
				
			||||||
 | 
					    "SolNode118",
 | 
				
			||||||
 | 
					    "SolNode211",
 | 
				
			||||||
 | 
					    "SolNode217",
 | 
				
			||||||
 | 
					    "SolNode401",
 | 
				
			||||||
 | 
					    "SolNode64",
 | 
				
			||||||
 | 
					    "SolNode405",
 | 
				
			||||||
 | 
					    "SolNode84",
 | 
				
			||||||
 | 
					    "SolNode402",
 | 
				
			||||||
 | 
					    "SolNode408",
 | 
				
			||||||
 | 
					    "SolNode122",
 | 
				
			||||||
 | 
					    "SolNode57",
 | 
				
			||||||
 | 
					    "SolNode216",
 | 
				
			||||||
 | 
					    "SolNode205",
 | 
				
			||||||
 | 
					    "SolNode215",
 | 
				
			||||||
 | 
					    "SolNode404",
 | 
				
			||||||
 | 
					    "SolNode209",
 | 
				
			||||||
 | 
					    "SolNode406",
 | 
				
			||||||
 | 
					    "SolNode204",
 | 
				
			||||||
 | 
					    "SolNode203",
 | 
				
			||||||
 | 
					    "SolNode409",
 | 
				
			||||||
 | 
					    "SolNode400",
 | 
				
			||||||
 | 
					    "SolNode212",
 | 
				
			||||||
 | 
					    "SolNode1",
 | 
				
			||||||
 | 
					    "SolNode412",
 | 
				
			||||||
 | 
					    "SolNode49",
 | 
				
			||||||
 | 
					    "SolNode78",
 | 
				
			||||||
 | 
					    "SolNode410",
 | 
				
			||||||
 | 
					    "SolNode407",
 | 
				
			||||||
 | 
					    "SolNode220"
 | 
				
			||||||
 | 
					  ],
 | 
				
			||||||
 | 
					  "VoidT4": [
 | 
				
			||||||
 | 
					    "SolNode188",
 | 
				
			||||||
 | 
					    "SolNode403",
 | 
				
			||||||
 | 
					    "SolNode189",
 | 
				
			||||||
 | 
					    "SolNode21",
 | 
				
			||||||
 | 
					    "SolNode102",
 | 
				
			||||||
 | 
					    "SolNode171",
 | 
				
			||||||
 | 
					    "SolNode196",
 | 
				
			||||||
 | 
					    "SolNode184",
 | 
				
			||||||
 | 
					    "SolNode185",
 | 
				
			||||||
 | 
					    "SolNode76",
 | 
				
			||||||
 | 
					    "SolNode195",
 | 
				
			||||||
 | 
					    "SolNode164",
 | 
				
			||||||
 | 
					    "SolNode401",
 | 
				
			||||||
 | 
					    "SolNode405",
 | 
				
			||||||
 | 
					    "SolNode56",
 | 
				
			||||||
 | 
					    "SolNode402",
 | 
				
			||||||
 | 
					    "SolNode408",
 | 
				
			||||||
 | 
					    "SolNode4",
 | 
				
			||||||
 | 
					    "SolNode181",
 | 
				
			||||||
 | 
					    "SolNode406",
 | 
				
			||||||
 | 
					    "SolNode162",
 | 
				
			||||||
 | 
					    "SolNode72",
 | 
				
			||||||
 | 
					    "SolNode407",
 | 
				
			||||||
 | 
					    "SolNode177",
 | 
				
			||||||
 | 
					    "SolNode404",
 | 
				
			||||||
 | 
					    "SolNode400",
 | 
				
			||||||
 | 
					    "SolNode409",
 | 
				
			||||||
 | 
					    "SolNode43",
 | 
				
			||||||
 | 
					    "SolNode166",
 | 
				
			||||||
 | 
					    "SolNode172",
 | 
				
			||||||
 | 
					    "SolNode412",
 | 
				
			||||||
 | 
					    "SolNode187",
 | 
				
			||||||
 | 
					    "SolNode38",
 | 
				
			||||||
 | 
					    "SolNode175",
 | 
				
			||||||
 | 
					    "SolNode81",
 | 
				
			||||||
 | 
					    "SolNode48",
 | 
				
			||||||
 | 
					    "SolNode410",
 | 
				
			||||||
 | 
					    "SolNode153",
 | 
				
			||||||
 | 
					    "SolNode173"
 | 
				
			||||||
 | 
					  ],
 | 
				
			||||||
 | 
					  "VoidT5": ["SolNode747", "SolNode743", "SolNode742", "SolNode744", "SolNode745", "SolNode748", "SolNode746", "SolNode741"],
 | 
				
			||||||
 | 
					  "VoidT6": ["SolNode717", "SolNode309", "SolNode718", "SolNode232", "SolNode230", "SolNode310"]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -327,195 +327,6 @@
 | 
				
			|||||||
      "Nodes": []
 | 
					      "Nodes": []
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  ],
 | 
					  ],
 | 
				
			||||||
  "ActiveMissions": [
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
      "_id": { "$oid": "663a7509d93367863785932d" },
 | 
					 | 
				
			||||||
      "Region": 15,
 | 
					 | 
				
			||||||
      "Seed": 80795,
 | 
					 | 
				
			||||||
      "Activation": { "$date": { "$numberLong": "1715107081517" } },
 | 
					 | 
				
			||||||
      "Expiry": { "$date": { "$numberLong": "2000000000000" } },
 | 
					 | 
				
			||||||
      "Node": "SolNode400",
 | 
					 | 
				
			||||||
      "MissionType": "MT_EXTERMINATION",
 | 
					 | 
				
			||||||
      "Modifier": "VoidT3",
 | 
					 | 
				
			||||||
      "Hard": true
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
      "_id": { "$oid": "663a75f959a5964cadb39879" },
 | 
					 | 
				
			||||||
      "Region": 19,
 | 
					 | 
				
			||||||
      "Seed": 32067,
 | 
					 | 
				
			||||||
      "Activation": { "$date": { "$numberLong": "1715107321237" } },
 | 
					 | 
				
			||||||
      "Expiry": { "$date": { "$numberLong": "2000000000000" } },
 | 
					 | 
				
			||||||
      "Node": "SolNode747",
 | 
					 | 
				
			||||||
      "MissionType": "MT_INTEL",
 | 
					 | 
				
			||||||
      "Modifier": "VoidT5",
 | 
					 | 
				
			||||||
      "Hard": true
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
      "_id": { "$oid": "663a779d3e347839ff301814" },
 | 
					 | 
				
			||||||
      "Region": 7,
 | 
					 | 
				
			||||||
      "Seed": 51739,
 | 
					 | 
				
			||||||
      "Activation": { "$date": { "$numberLong": "1715107741454" } },
 | 
					 | 
				
			||||||
      "Expiry": { "$date": { "$numberLong": "2000000000000" } },
 | 
					 | 
				
			||||||
      "Node": "SolNode64",
 | 
					 | 
				
			||||||
      "MissionType": "MT_TERRITORY",
 | 
					 | 
				
			||||||
      "Modifier": "VoidT3"
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
      "_id": { "$oid": "663a77d916c199f4644ee67d" },
 | 
					 | 
				
			||||||
      "Region": 17,
 | 
					 | 
				
			||||||
      "Seed": 61179,
 | 
					 | 
				
			||||||
      "Activation": { "$date": { "$numberLong": "1715107801647" } },
 | 
					 | 
				
			||||||
      "Expiry": { "$date": { "$numberLong": "2000000000000" } },
 | 
					 | 
				
			||||||
      "Node": "SolNode718",
 | 
					 | 
				
			||||||
      "MissionType": "MT_ALCHEMY",
 | 
					 | 
				
			||||||
      "Modifier": "VoidT6"
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
      "_id": { "$oid": "663a78c98a609b49b8410726" },
 | 
					 | 
				
			||||||
      "Region": 3,
 | 
					 | 
				
			||||||
      "Seed": 9520,
 | 
					 | 
				
			||||||
      "Activation": { "$date": { "$numberLong": "1715108041501" } },
 | 
					 | 
				
			||||||
      "Expiry": { "$date": { "$numberLong": "2000000000000" } },
 | 
					 | 
				
			||||||
      "Node": "SolNode79",
 | 
					 | 
				
			||||||
      "MissionType": "MT_INTEL",
 | 
					 | 
				
			||||||
      "Modifier": "VoidT1",
 | 
					 | 
				
			||||||
      "Hard": true
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
      "_id": { "$oid": "663a7df15eeabaac79b0a061" },
 | 
					 | 
				
			||||||
      "Region": 6,
 | 
					 | 
				
			||||||
      "Seed": 48861,
 | 
					 | 
				
			||||||
      "Activation": { "$date": { "$numberLong": "1715109361974" } },
 | 
					 | 
				
			||||||
      "Expiry": { "$date": { "$numberLong": "2000000000000" } },
 | 
					 | 
				
			||||||
      "Node": "SolNode67",
 | 
					 | 
				
			||||||
      "MissionType": "MT_INTEL",
 | 
					 | 
				
			||||||
      "Modifier": "VoidT2",
 | 
					 | 
				
			||||||
      "Hard": true
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
      "_id": { "$oid": "663a7df25eeabaac79b0a062" },
 | 
					 | 
				
			||||||
      "Region": 5,
 | 
					 | 
				
			||||||
      "Seed": 13550,
 | 
					 | 
				
			||||||
      "Activation": { "$date": { "$numberLong": "1715109361974" } },
 | 
					 | 
				
			||||||
      "Expiry": { "$date": { "$numberLong": "2000000000000" } },
 | 
					 | 
				
			||||||
      "Node": "SolNode10",
 | 
					 | 
				
			||||||
      "MissionType": "MT_SABOTAGE",
 | 
					 | 
				
			||||||
      "Modifier": "VoidT2",
 | 
					 | 
				
			||||||
      "Hard": true
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
      "_id": { "$oid": "663a83cdec0d5181435f1324" },
 | 
					 | 
				
			||||||
      "Region": 19,
 | 
					 | 
				
			||||||
      "Seed": 39392,
 | 
					 | 
				
			||||||
      "Activation": { "$date": { "$numberLong": "1715110861506" } },
 | 
					 | 
				
			||||||
      "Expiry": { "$date": { "$numberLong": "2000000000000" } },
 | 
					 | 
				
			||||||
      "Node": "SolNode742",
 | 
					 | 
				
			||||||
      "MissionType": "MT_DEFENSE",
 | 
					 | 
				
			||||||
      "Modifier": "VoidT5"
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
      "_id": { "$oid": "663a83cdec0d5181435f1325" },
 | 
					 | 
				
			||||||
      "Region": 19,
 | 
					 | 
				
			||||||
      "Seed": 88668,
 | 
					 | 
				
			||||||
      "Activation": { "$date": { "$numberLong": "1715110861506" } },
 | 
					 | 
				
			||||||
      "Expiry": { "$date": { "$numberLong": "2000000000000" } },
 | 
					 | 
				
			||||||
      "Node": "SolNode743",
 | 
					 | 
				
			||||||
      "MissionType": "MT_MOBILE_DEFENSE",
 | 
					 | 
				
			||||||
      "Modifier": "VoidT5"
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
      "_id": { "$oid": "663a83cdec0d5181435f1326" },
 | 
					 | 
				
			||||||
      "Region": 19,
 | 
					 | 
				
			||||||
      "Seed": 73823,
 | 
					 | 
				
			||||||
      "Activation": { "$date": { "$numberLong": "1715110861506" } },
 | 
					 | 
				
			||||||
      "Expiry": { "$date": { "$numberLong": "2000000000000" } },
 | 
					 | 
				
			||||||
      "Node": "SolNode741",
 | 
					 | 
				
			||||||
      "MissionType": "MT_ASSAULT",
 | 
					 | 
				
			||||||
      "Modifier": "VoidT5"
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
      "_id": { "$oid": "663a878d23d1514873170466" },
 | 
					 | 
				
			||||||
      "Region": 9,
 | 
					 | 
				
			||||||
      "Seed": 88696,
 | 
					 | 
				
			||||||
      "Activation": { "$date": { "$numberLong": "1715111821951" } },
 | 
					 | 
				
			||||||
      "Expiry": { "$date": { "$numberLong": "2000000000000" } },
 | 
					 | 
				
			||||||
      "Node": "SolNode4",
 | 
					 | 
				
			||||||
      "MissionType": "MT_EXTERMINATION",
 | 
					 | 
				
			||||||
      "Modifier": "VoidT4",
 | 
					 | 
				
			||||||
      "Hard": true
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
      "_id": { "$oid": "663a887d4903098c10992fe6" },
 | 
					 | 
				
			||||||
      "Region": 6,
 | 
					 | 
				
			||||||
      "Seed": 66337,
 | 
					 | 
				
			||||||
      "Activation": { "$date": { "$numberLong": "1715112061729" } },
 | 
					 | 
				
			||||||
      "Expiry": { "$date": { "$numberLong": "2000000000000" } },
 | 
					 | 
				
			||||||
      "Node": "SolNode18",
 | 
					 | 
				
			||||||
      "MissionType": "MT_TERRITORY",
 | 
					 | 
				
			||||||
      "Modifier": "VoidT2"
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
      "_id": { "$oid": "663a887d4903098c10992fe7" },
 | 
					 | 
				
			||||||
      "Region": 10,
 | 
					 | 
				
			||||||
      "Seed": 5135,
 | 
					 | 
				
			||||||
      "Activation": { "$date": { "$numberLong": "1715112061729" } },
 | 
					 | 
				
			||||||
      "Expiry": { "$date": { "$numberLong": "2000000000000" } },
 | 
					 | 
				
			||||||
      "Node": "SolNode149",
 | 
					 | 
				
			||||||
      "MissionType": "MT_DEFENSE",
 | 
					 | 
				
			||||||
      "Modifier": "VoidT2"
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
      "_id": { "$oid": "663a8931586c301b1fbe63d3" },
 | 
					 | 
				
			||||||
      "Region": 15,
 | 
					 | 
				
			||||||
      "Seed": 32180,
 | 
					 | 
				
			||||||
      "Activation": { "$date": { "$numberLong": "1715112241196" } },
 | 
					 | 
				
			||||||
      "Expiry": { "$date": { "$numberLong": "2000000000000" } },
 | 
					 | 
				
			||||||
      "Node": "SolNode408",
 | 
					 | 
				
			||||||
      "MissionType": "MT_DEFENSE",
 | 
					 | 
				
			||||||
      "Modifier": "VoidT4"
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
      "_id": { "$oid": "663a8931586c301b1fbe63d4" },
 | 
					 | 
				
			||||||
      "Region": 12,
 | 
					 | 
				
			||||||
      "Seed": 22521,
 | 
					 | 
				
			||||||
      "Activation": { "$date": { "$numberLong": "1715112241196" } },
 | 
					 | 
				
			||||||
      "Expiry": { "$date": { "$numberLong": "2000000000000" } },
 | 
					 | 
				
			||||||
      "Node": "SolNode181",
 | 
					 | 
				
			||||||
      "MissionType": "MT_EXTERMINATION",
 | 
					 | 
				
			||||||
      "Modifier": "VoidT4"
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
      "_id": { "$oid": "663a8931586c301b1fbe63d5" },
 | 
					 | 
				
			||||||
      "Region": 2,
 | 
					 | 
				
			||||||
      "Seed": 28500,
 | 
					 | 
				
			||||||
      "Activation": { "$date": { "$numberLong": "1715112241196" } },
 | 
					 | 
				
			||||||
      "Expiry": { "$date": { "$numberLong": "2000000000000" } },
 | 
					 | 
				
			||||||
      "Node": "SolNode128",
 | 
					 | 
				
			||||||
      "MissionType": "MT_EXTERMINATION",
 | 
					 | 
				
			||||||
      "Modifier": "VoidT1"
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
      "_id": { "$oid": "663a8931586c301b1fbe63d6" },
 | 
					 | 
				
			||||||
      "Region": 3,
 | 
					 | 
				
			||||||
      "Seed": 24747,
 | 
					 | 
				
			||||||
      "Activation": { "$date": { "$numberLong": "1715112241196" } },
 | 
					 | 
				
			||||||
      "Expiry": { "$date": { "$numberLong": "2000000000000" } },
 | 
					 | 
				
			||||||
      "Node": "SolNode26",
 | 
					 | 
				
			||||||
      "MissionType": "MT_DEFENSE",
 | 
					 | 
				
			||||||
      "Modifier": "VoidT1"
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
      "_id": { "$oid": "663a8931586c301b1fbe63d7" },
 | 
					 | 
				
			||||||
      "Region": 17,
 | 
					 | 
				
			||||||
      "Seed": 63914,
 | 
					 | 
				
			||||||
      "Activation": { "$date": { "$numberLong": "1715112241196" } },
 | 
					 | 
				
			||||||
      "Expiry": { "$date": { "$numberLong": "2000000000000" } },
 | 
					 | 
				
			||||||
      "Node": "SolNode717",
 | 
					 | 
				
			||||||
      "MissionType": "MT_SURVIVAL",
 | 
					 | 
				
			||||||
      "Modifier": "VoidT6",
 | 
					 | 
				
			||||||
      "Hard": true
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  ],
 | 
					 | 
				
			||||||
  "NodeOverrides": [
 | 
					  "NodeOverrides": [
 | 
				
			||||||
    { "_id": { "$oid": "549b18e9b029cef5991d6aec" }, "Node": "EuropaHUB", "Hide": true },
 | 
					    { "_id": { "$oid": "549b18e9b029cef5991d6aec" }, "Node": "EuropaHUB", "Hide": true },
 | 
				
			||||||
    { "_id": { "$oid": "54a1737aeb658f6cbccf70ff" }, "Node": "ErisHUB", "Hide": true },
 | 
					    { "_id": { "$oid": "54a1737aeb658f6cbccf70ff" }, "Node": "ErisHUB", "Hide": true },
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user