XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFESpaceNinjaServer

A simple server for a small space ninja game

公开
关注 0 Fork 0 Star 0
返回提交历史

XFEstudio/XFESpaceNinjaServer

feat: orphix venom (#2637)

Without rotation on last mission Re #1103 Thanks to https://wiki.warframe.com/w/World_State/Example Reviewed-on: https://onlyg.it/OpenWF/SpaceNinjaServer/pulls/2637 Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com> Co-authored-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com> Co-committed-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>

d0743654
AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
提交于

代码差异

22 个文件 +254 -24
Modified config-vanilla.json +1 -0
@@ -67,6 +67,7 @@
67 67 "resourceBoost": false,
68 68 "tennoLiveRelay": false,
69 69 "wolfHunt": false,
70 "orphixVenom": false,
70 71 "longShadow": false,
71 72 "hallowedFlame": false,
72 73 "hallowedNightmares": false,
Modified src/models/leaderboardModel.ts +1 -1
@@ -8,7 +8,7 @@ const leaderboardEntrySchema = new Schema<ILeaderboardEntryDatabase>(
8 8 displayName: { type: String, required: true },
9 9 score: { type: Number, required: true },
10 10 guildId: Schema.Types.ObjectId,
11 expiry: { type: Date, required: true },
11 expiry: Date,
12 12 guildTier: Number
13 13 },
14 14 { id: false }
Modified src/models/statsModel.ts +13 -1
@@ -97,7 +97,19 @@ const statsSchema = new Schema<IStatsDatabase>({
97 97 SentinelGameScore: Number,
98 98 CaliberChicksScore: Number,
99 99 OlliesCrashCourseScore: Number,
100 DojoObstacleScore: Number
100 DojoObstacleScore: Number,
101
102 Halloween16: Number,
103 AmalgamEventScoreMax: Number,
104 Halloween19ScoreMax: Number,
105 FlotillaEventScore: Number,
106 FlotillaSpaceBadgesTier1: Number,
107 FlotillaSpaceBadgesTier2: Number,
108 FlotillaSpaceBadgesTier3: Number,
109 FlotillaGroundBadgesTier1: Number,
110 FlotillaGroundBadgesTier2: Number,
111 FlotillaGroundBadgesTier3: Number,
112 MechSurvivalScoreMax: Number
101 113 });
102 114
103 115 statsSchema.set("toJSON", {
Modified src/routes/stats.ts +1 -0
@@ -8,5 +8,6 @@ const statsRouter = express.Router();
8 8 statsRouter.get("/view.php", viewController);
9 9 statsRouter.post("/upload.php", uploadController);
10 10 statsRouter.post("/leaderboardWeekly.php", leaderboardController);
11 statsRouter.post("/leaderboardArchived.php", leaderboardController);
11 12
12 13 export { statsRouter };
Modified src/services/configService.ts +1 -0
@@ -79,6 +79,7 @@ export interface IConfig {
79 79 tennoLiveRelay?: boolean;
80 80 baroTennoConRelay?: boolean;
81 81 wolfHunt?: boolean;
82 orphixVenom?: boolean;
82 83 longShadow?: boolean;
83 84 hallowedFlame?: boolean;
84 85 hallowedNightmares?: boolean;
Modified src/services/guildService.ts +15 -0
@@ -896,5 +896,20 @@ export const goalGuildRewardByTag: Record<string, { guildGoals: number[][]; rewa
896 896 "/Lotus/Levels/ClanDojo/ComponentPropRecipes/DuviriMurmurEventSilverTrophyRecipe",
897 897 "/Lotus/Levels/ClanDojo/ComponentPropRecipes/DuviriMurmurEventGoldTrophyRecipe"
898 898 ]
899 },
900 MechSurvival: {
901 guildGoals: [
902 [1390, 5860, 13920, 18850],
903 [3510, 22275, 69120, 137250],
904 [11700, 75250, 230400, 457500],
905 [35100, 222750, 691200, 1372500],
906 [117000, 742500, 2304000, 4575000]
907 ],
908 rewards: [
909 "/Lotus/Levels/ClanDojo/ComponentPropRecipes/MechEventTrophyTerracottaRecipe",
910 "/Lotus/Levels/ClanDojo/ComponentPropRecipes/MechEventTrophyBronzeRecipe",
911 "/Lotus/Levels/ClanDojo/ComponentPropRecipes/MechEventTrophySilverRecipe",
912 "/Lotus/Levels/ClanDojo/ComponentPropRecipes/MechEventTrophyGoldRecipe"
913 ]
899 914 }
900 915 };
Modified src/services/leaderboardService.ts +40 -11
@@ -1,38 +1,66 @@
1 1 import { Guild } from "@/src/models/guildModel";
2 2 import { Leaderboard, TLeaderboardEntryDocument } from "@/src/models/leaderboardModel";
3 3 import { ILeaderboardEntryClient } from "@/src/types/leaderboardTypes";
4 import { handleGuildGoalProgress } from "@/src/services/guildService";
5 import { getWorldState } from "@/src/services/worldStateService";
6 import { Types } from "mongoose";
4 7
5 8 export const submitLeaderboardScore = async (
6 schedule: "weekly" | "daily",
9 schedule: "weekly" | "daily" | "events",
7 10 leaderboard: string,
8 11 ownerId: string,
9 12 displayName: string,
10 13 score: number,
11 14 guildId: string | undefined
12 15 ): Promise<void> => {
13 let expiry: Date;
16 let expiry: Date | undefined;
14 17 if (schedule == "daily") {
15 18 expiry = new Date(Math.trunc(Date.now() / 86400000) * 86400000 + 86400000);
16 } else {
19 } else if (schedule == "weekly") {
17 20 const EPOCH = 1734307200 * 1000; // Monday
18 21 const week = Math.trunc((Date.now() - EPOCH) / 604800000);
19 22 const weekStart = EPOCH + week * 604800000;
20 23 const weekEnd = weekStart + 604800000;
21 24 expiry = new Date(weekEnd);
22 25 }
26
27 if (guildId) {
28 const guild = (await Guild.findById(guildId, "Name Tier GoalProgress VaultDecoRecipes"))!;
29 if (schedule == "events") {
30 const prevAccount = await Leaderboard.findOne(
31 { leaderboard: `${schedule}.accounts.${leaderboard}`, ownerId },
32 "score"
33 );
34 const delta = score - (prevAccount?.score ?? 0);
35 if (delta > 0) {
36 await Leaderboard.findOneAndUpdate(
37 { leaderboard: `${schedule}.guilds.${leaderboard}`, ownerId: guildId },
38 { $inc: { score: delta }, $set: { displayName: guild.Name, guildTier: guild.Tier } },
39 { upsert: true }
40 );
41 const goal = getWorldState().Goals.find(x => x.ScoreMaxTag == leaderboard);
42 if (goal) {
43 await handleGuildGoalProgress(guild, {
44 Count: delta,
45 Tag: goal.Tag,
46 goalId: new Types.ObjectId(goal._id.$oid)
47 });
48 }
49 }
50 } else {
51 await Leaderboard.findOneAndUpdate(
52 { leaderboard: `${schedule}.guilds.${leaderboard}`, ownerId: guildId },
53 { $max: { score }, $set: { displayName: guild.Name, guildTier: guild.Tier, expiry } },
54 { upsert: true, new: true }
55 );
56 }
57 }
58
23 59 await Leaderboard.findOneAndUpdate(
24 60 { leaderboard: `${schedule}.accounts.${leaderboard}`, ownerId },
25 61 { $max: { score }, $set: { displayName, guildId, expiry } },
26 62 { upsert: true }
27 63 );
28 if (guildId) {
29 const guild = (await Guild.findById(guildId, "Name Tier"))!;
30 await Leaderboard.findOneAndUpdate(
31 { leaderboard: `${schedule}.guilds.${leaderboard}`, ownerId: guildId },
32 { $max: { score }, $set: { displayName: guild.Name, guildTier: guild.Tier, expiry } },
33 { upsert: true }
34 );
35 }
36 64 };
37 65
38 66 export const getLeaderboard = async (
@@ -43,6 +71,7 @@ export const getLeaderboard = async (
43 71 guildId: string | undefined,
44 72 guildTier: number | undefined
45 73 ): Promise<ILeaderboardEntryClient[]> => {
74 leaderboard = leaderboard.replace("archived", guildTier || guildId ? "events.guilds" : "events.accounts");
46 75 const filter: { leaderboard: string; guildId?: string; guildTier?: number } = { leaderboard };
47 76 if (guildId) {
48 77 filter.guildId = guildId;
Modified src/services/missionInventoryUpdateService.ts +45 -4
@@ -652,7 +652,12 @@ export const addMissionInventoryUpdates = async (
652 652 }
653 653 }
654 654 if (currentMissionKey && currentMissionKey in goalMessagesByKey) {
655 const totalCount = (goalProgress?.Count ?? 0) + uploadProgress.Count;
655 let countBeforeUpload = goalProgress?.Count ?? 0;
656 let totalCount = countBeforeUpload + uploadProgress.Count;
657 if (goal.Best) {
658 countBeforeUpload = goalProgress?.Best ?? 0;
659 totalCount = uploadProgress.Best;
660 }
656 661 let reward;
657 662
658 663 if (goal.InterimGoals && goal.InterimRewards) {
@@ -660,7 +665,7 @@ export const addMissionInventoryUpdates = async (
660 665 if (
661 666 goal.InterimGoals[i] &&
662 667 goal.InterimGoals[i] <= totalCount &&
663 (!goalProgress || goalProgress.Count < goal.InterimGoals[i]) &&
668 (!goalProgress || countBeforeUpload < goal.InterimGoals[i]) &&
664 669 goal.InterimRewards[i]
665 670 ) {
666 671 reward = goal.InterimRewards[i];
@@ -672,7 +677,7 @@ export const addMissionInventoryUpdates = async (
672 677 !reward &&
673 678 goal.Goal &&
674 679 goal.Goal <= totalCount &&
675 (!goalProgress || goalProgress.Count < goal.Goal) &&
680 (!goalProgress || countBeforeUpload < goal.Goal) &&
676 681 goal.Reward
677 682 ) {
678 683 reward = goal.Reward;
@@ -681,7 +686,7 @@ export const addMissionInventoryUpdates = async (
681 686 !reward &&
682 687 goal.BonusGoal &&
683 688 goal.BonusGoal <= totalCount &&
684 (!goalProgress || goalProgress.Count < goal.BonusGoal) &&
689 (!goalProgress || countBeforeUpload < goal.BonusGoal) &&
685 690 goal.BonusReward
686 691 ) {
687 692 reward = goal.BonusReward;
@@ -1091,6 +1096,12 @@ export const addMissionRewards = async (
1091 1096 }
1092 1097 }
1093 1098 }
1099 if (rewardInfo.GoalProgressAmount && goal.Tag.startsWith("MechSurvival")) {
1100 MissionRewards.push({
1101 StoreItem: "/Lotus/StoreItems/Types/Items/MiscItems/MechSurvivalEventCreds",
1102 ItemCount: Math.trunc(rewardInfo.GoalProgressAmount / 10)
1103 });
1104 }
1094 1105 }
1095 1106 }
1096 1107
@@ -2416,5 +2427,35 @@ const goalMessagesByKey: Record<string, { sndr: string; msg: string; sub: string
2416 2427 msg: "/Lotus/Language/G1Quests/ProjectNightwatchTacAlertMissionRewardBody",
2417 2428 sub: "/Lotus/Language/G1Quests/ProjectNightwatchTacAlertMissionFourTitle",
2418 2429 icon: "/Lotus/Interface/Icons/Npcs/Lotus_d.png"
2430 },
2431 "/Lotus/Types/Keys/MechSurvivalCorpusShip": {
2432 sndr: "/Lotus/Language/Bosses/DeimosFather",
2433 msg: "/Lotus/Language/Inbox/MechEvent2020Tier1CompleteDesc",
2434 sub: "/Lotus/Language/Inbox/MechEvent2020Tier1CompleteTitle",
2435 icon: "/Lotus/Interface/Icons/Npcs/Entrati/Father.png"
2436 },
2437 "/Lotus/Types/Keys/MechSurvivalGrineerGalleon": {
2438 sndr: "/Lotus/Language/Bosses/DeimosFather",
2439 msg: "/Lotus/Language/Inbox/MechEvent2020Tier2CompleteDesc",
2440 sub: "/Lotus/Language/Inbox/MechEvent2020Tier2CompleteTitle",
2441 icon: "/Lotus/Interface/Icons/Npcs/Entrati/Father.png"
2442 },
2443 "/Lotus/Types/Keys/MechSurvivalGasCity": {
2444 sndr: "/Lotus/Language/Bosses/DeimosFather",
2445 msg: "/Lotus/Language/Inbox/MechEvent2020Tier3CompleteDesc",
2446 sub: "/Lotus/Language/Inbox/MechEvent2020Tier3CompleteTitle",
2447 icon: "/Lotus/Interface/Icons/Npcs/Entrati/Father.png"
2448 },
2449 "/Lotus/Types/Keys/MechSurvivalCorpusShipEndurance": {
2450 sndr: "/Lotus/Language/Bosses/DeimosFather",
2451 msg: "/Lotus/Language/Inbox/MechEvent2020Tier3CompleteDesc",
2452 sub: "/Lotus/Language/Inbox/MechEvent2020Tier3CompleteTitle",
2453 icon: "/Lotus/Interface/Icons/Npcs/Entrati/Father.png"
2454 },
2455 "/Lotus/Types/Keys/MechSurvivalGrineerGalleonEndurance": {
2456 sndr: "/Lotus/Language/Bosses/DeimosFather",
2457 msg: "/Lotus/Language/Inbox/MechEvent2020Tier3CompleteDesc",
2458 sub: "/Lotus/Language/Inbox/MechEvent2020Tier3CompleteTitle",
2459 icon: "/Lotus/Interface/Icons/Npcs/Entrati/Father.png"
2419 2460 }
2420 2461 };
Modified src/services/statsService.ts +23 -0
@@ -382,6 +382,29 @@ export const updateStats = async (accountOwnerId: string, payload: IStatsUpdate)
382 382 );
383 383 break;
384 384
385 case "Halloween16":
386 case "AmalgamEventScoreMax":
387 case "Halloween19ScoreMax":
388 case "FlotillaEventScore":
389 case "FlotillaSpaceBadgesTier1":
390 case "FlotillaSpaceBadgesTier2":
391 case "FlotillaSpaceBadgesTier3":
392 case "FlotillaGroundBadgesTier1":
393 case "FlotillaGroundBadgesTier2":
394 case "FlotillaGroundBadgesTier3":
395 case "MechSurvivalScoreMax":
396 playerStats[category] ??= 0;
397 if (data > playerStats[category]) playerStats[category] = data as number;
398 await submitLeaderboardScore(
399 "events",
400 category,
401 accountOwnerId,
402 payload.displayName,
403 data as number,
404 payload.guildId
405 );
406 break;
407
385 408 default:
386 409 if (!ignoredCategories.includes(category)) {
387 410 unknownCategories[action] ??= [];
Modified src/services/worldStateService.ts +80 -3
@@ -1538,7 +1538,7 @@ export const getWorldState = (buildLabel?: string): IWorldState => {
1538 1538 Personal: true,
1539 1539 Bounty: true,
1540 1540 ClampNodeScores: true,
1541 Node: "EventNode28", // Incompatible with Wolf Hunt (2025)
1541 Node: "EventNode28", // Incompatible with Wolf Hunt (2025), Orphix Venom
1542 1542 MissionKeyName: "/Lotus/Types/Keys/GalleonRobberyAlertB",
1543 1543 Desc: "/Lotus/Language/Events/GalleonRobberyEventMissionTitle",
1544 1544 Icon: "/Lotus/Interface/Icons/Player/GalleonRobberiesEvent.png",
@@ -1964,7 +1964,7 @@ export const getWorldState = (buildLabel?: string): IWorldState => {
1964 1964 "/Lotus/Types/Keys/WolfTacAlertReduxD"
1965 1965 ],
1966 1966 ConcurrentNodeReqs: [1, 2, 3],
1967 ConcurrentNodes: ["EventNode28", "EventNode39", "EventNode40"], // Incompatible with Galleon Of Ghouls
1967 ConcurrentNodes: ["EventNode28", "EventNode39", "EventNode40"], // Incompatible with Galleon Of Ghouls, Orphix Venom
1968 1968 MissionKeyName: "/Lotus/Types/Keys/WolfTacAlertReduxA",
1969 1969 Faction: "FC_GRINEER",
1970 1970 Desc: "/Lotus/Language/Alerts/WolfAlert",
@@ -2216,7 +2216,7 @@ export const getWorldState = (buildLabel?: string): IWorldState => {
2216 2216 "/Lotus/Types/Keys/TacAlertKeyProxyRebellionFour"
2217 2217 ],
2218 2218 ConcurrentNodeReqs: [1, 2, 3],
2219 ConcurrentNodes: ["EventNode7", "EventNode4", "EventNode17"],
2219 ConcurrentNodes: ["EventNode7", "EventNode4", "EventNode17"], // Incompatible with Orphix venom
2220 2220 MissionKeyName: "/Lotus/Types/Keys/TacAlertKeyProxyRebellionOne",
2221 2221 Faction: "FC_CORPUS",
2222 2222 Desc: "/Lotus/Language/Alerts/TacAlertProxyRebellion",
@@ -2315,6 +2315,83 @@ export const getWorldState = (buildLabel?: string): IWorldState => {
2315 2315 });
2316 2316 }
2317 2317
2318 if (config.worldState?.orphixVenom) {
2319 worldState.Goals.push(
2320 {
2321 _id: { $oid: "5fdcccb875d5ad500dc477d0" },
2322 Activation: { $date: { $numberLong: "1608320400000" } },
2323 Expiry: { $date: { $numberLong: "2000000000000" } },
2324 Count: 0,
2325 Goal: 500,
2326 Success: 0,
2327 Personal: true,
2328 Best: true,
2329 Node: "EventNode17", // Incompatible with Proxy Rebellion
2330 MissionKeyName: "/Lotus/Types/Keys/MechSurvivalCorpusShip",
2331 Faction: "FC_SENTIENT",
2332 Desc: "/Lotus/Language/Events/MechEventMissionTier1",
2333 Icon: "/Lotus/Interface/Icons/Categories/IconMech256.png",
2334 Tag: "MechSurvivalA",
2335 ScoreVar: "MechSurvivalScore",
2336 Reward: {
2337 items: ["/Lotus/StoreItems/Upgrades/Skins/Clan/MechEventEmblemItem"]
2338 }
2339 },
2340 {
2341 _id: { $oid: "5fdcccb875d5ad500dc477d1" },
2342 Activation: { $date: { $numberLong: "1608320400000" } },
2343 Expiry: { $date: { $numberLong: "2000000000000" } },
2344 Count: 0,
2345 Goal: 1000,
2346 Success: 0,
2347 Personal: true,
2348 Best: true,
2349 Node: "EventNode28", // Incompatible with Galleon Of Ghouls, Wolf Hunt (2025)
2350 MissionKeyName: "/Lotus/Types/Keys/MechSurvivalGrineerGalleon",
2351 Faction: "FC_SENTIENT",
2352 Desc: "/Lotus/Language/Events/MechEventMissionTier2",
2353 Icon: "/Lotus/Interface/Icons/Categories/IconMech256.png",
2354 Tag: "MechSurvivalB",
2355 PrereqGoalTags: ["MechSurvivalA"],
2356 ScoreVar: "MechSurvivalScore",
2357 Reward: {
2358 items: ["/Lotus/StoreItems/Types/Items/FusionTreasures/OroFusexJ"]
2359 }
2360 },
2361 {
2362 _id: { $oid: "5fdcccb875d5ad500dc477d2" },
2363 Activation: { $date: { $numberLong: "1608320400000" } },
2364 Expiry: { $date: { $numberLong: "2000000000000" } },
2365 Count: 0,
2366 Goal: 2000,
2367 Success: 0,
2368 Personal: true,
2369 Best: true,
2370 Node: "EventNode32",
2371 MissionKeyName: "/Lotus/Types/Keys/MechSurvivalGasCity",
2372 MissionKeyRotation: [
2373 "/Lotus/Types/Keys/MechSurvivalGasCity",
2374 "/Lotus/Types/Keys/MechSurvivalCorpusShipEndurance",
2375 "/Lotus/Types/Keys/MechSurvivalGrineerGalleonEndurance"
2376 ],
2377 MissionKeyRotationInterval: 3600, // 1 hour
2378 Faction: "FC_SENTIENT",
2379 Desc: "/Lotus/Language/Events/MechEventMissionTier3",
2380 Icon: "/Lotus/Interface/Icons/Categories/IconMech256.png",
2381 Tag: "MechSurvival",
2382 PrereqGoalTags: ["MechSurvivalA", "MechSurvivalB"],
2383 ScoreVar: "MechSurvivalScore",
2384 ScoreMaxTag: "MechSurvivalScoreMax",
2385 Reward: {
2386 items: [
2387 "/Lotus/StoreItems/Types/Items/MiscItems/FormaAura",
2388 "/Lotus/StoreItems/Upgrades/Skins/Necramech/MechWeapon/MechEventMausolonSkin"
2389 ]
2390 }
2391 }
2392 );
2393 }
2394
2318 2395 // Nightwave Challenges
2319 2396 const nightwaveSyndicateTag = getNightwaveSyndicateTag(buildLabel);
2320 2397 if (nightwaveSyndicateTag) {
Modified src/types/leaderboardTypes.ts +1 -1
@@ -6,7 +6,7 @@ export interface ILeaderboardEntryDatabase {
6 6 displayName: string;
7 7 score: number;
8 8 guildId?: Types.ObjectId;
9 expiry: Date;
9 expiry?: Date;
10 10 guildTier?: number;
11 11 }
12 12
Modified src/types/requestTypes.ts +1 -0
@@ -206,6 +206,7 @@ export interface IRewardInfo {
206 206 Q?: boolean; // likely indicates that the bonus objective for this stage was completed
207 207 CheckpointCounter?: number; // starts at 1, is incremented with each job stage upload, and does not reset when starting a new job
208 208 challengeMissionId?: string;
209 GoalProgressAmount?: number;
209 210 }
210 211
211 212 export type IMissionStatus = "GS_SUCCESS" | "GS_FAILURE" | "GS_DUMPED" | "GS_QUIT" | "GS_INTERRUPTED";
Modified src/types/statTypes.ts +13 -0
Modified src/types/worldStateTypes.ts +5 -2
Modified static/webui/index.html +7 -1
Modified static/webui/translations/de.js +1 -0
Modified static/webui/translations/en.js +1 -0
Modified static/webui/translations/es.js +1 -0
Modified static/webui/translations/fr.js +1 -0
Modified static/webui/translations/ru.js +1 -0
Modified static/webui/translations/uk.js +1 -0
Modified static/webui/translations/zh.js +1 -0