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: initial cross-platform stuff (#3533)

Basically just adding more layers of abstraction and hoping it's an improvement for later down the line. :) Reviewed-on: https://onlyg.it/OpenWF/SpaceNinjaServer/pulls/3533 Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com> Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>

ca815c6b
Sainan <63328889+Sainan@users.noreply.github.com>
提交于

代码差异

8 个文件 +51 -28
Modified src/controllers/api/addPendingFriendController.ts +1 -1
@@ -62,7 +62,7 @@ const sendFriendRequest = async (
62 62
63 63 const friendInfo: IFriendInfo = {
64 64 _id: toOid(requesteeAccount._id),
65 DisplayName: getUnicodeName(requesteeAccount.DisplayName, requesterAccount.BuildLabel),
65 DisplayName: getUnicodeName(requesteeAccount, requesterAccount.BuildLabel),
66 66 LastLogin: toMongoDate(requesteeAccount.LastLogin),
67 67 Note: message
68 68 };
Modified src/controllers/api/loginController.ts +7 -1
@@ -5,7 +5,12 @@ import { buildConfig } from "../../services/buildConfigService.ts";
5 5
6 6 import { Account } from "../../models/loginModel.ts";
7 7 import { createAccount, createNonce, getUsernameFromEmail, isCorrectPassword } from "../../services/loginService.ts";
8 import type { IDatabaseAccountJson, ILoginRequest, ILoginResponse } from "../../types/loginTypes.ts";
8 import {
9 Platform,
10 type IDatabaseAccountJson,
11 type ILoginRequest,
12 type ILoginResponse
13 } from "../../types/loginTypes.ts";
9 14 import { logger } from "../../utils/logger.ts";
10 15 import { version_compare } from "../../helpers/inventoryHelpers.ts";
11 16 import { handleNonceInvalidation } from "../../services/wsService.ts";
@@ -113,6 +118,7 @@ export const loginController: RequestHandler = async (request, response) => {
113 118 account.CountryCode = loginRequest.lang?.toUpperCase() ?? "EN";
114 119 account.BuildLabel = buildLabel;
115 120 account.LastLogin = new Date();
121 account.LastPlatform = isAndroid ? Platform.Android : Platform.Windows;
116 122 account.Dropped = undefined;
117 123 await account.save();
118 124
Modified src/controllers/api/removeFriendController.ts +10 -10
@@ -1,11 +1,11 @@
1 1 import gameToBuildVersion from "../../constants/gameToBuildVersion.ts";
2 import { toOid, version_compare } from "../../helpers/inventoryHelpers.ts";
2 import { fromOid, toOid2, version_compare } from "../../helpers/inventoryHelpers.ts";
3 3 import { getJSONfromString } from "../../helpers/stringHelpers.ts";
4 4 import { Friendship } from "../../models/friendModel.ts";
5 5 import { Account } from "../../models/loginModel.ts";
6 6 import { getInventory } from "../../services/inventoryService.ts";
7 7 import { getAccountForRequest, getUnicodeName } from "../../services/loginService.ts";
8 import type { IOid } from "../../types/commonTypes.ts";
8 import type { IOidWithLegacySupport } from "../../types/commonTypes.ts";
9 9 import { parallelForeach } from "../../utils/async-utils.ts";
10 10 import type { RequestHandler } from "express";
11 11 import type { Types } from "mongoose";
@@ -19,11 +19,11 @@ export const removeFriendGetController: RequestHandler = async (req, res) => {
19 19 Friendship.find({ friend: accountId }, "owner")
20 20 ]);
21 21 const promises: Promise<void>[] = [];
22 const friends: IOid[] = [];
22 const friends: IOidWithLegacySupport[] = [];
23 23 for (const externalFriendship of externalFriendships) {
24 24 if (!internalFriendships.find(x => x.friend.equals(externalFriendship.owner))) {
25 25 promises.push(Friendship.deleteOne({ _id: externalFriendship._id }) as unknown as Promise<void>);
26 friends.push(toOid(externalFriendship.owner));
26 friends.push(toOid2(externalFriendship.owner, account.BuildLabel));
27 27 }
28 28 }
29 29 await Promise.all(promises);
@@ -67,12 +67,12 @@ export const removeFriendPostController: RequestHandler = async (req, res) => {
67 67
68 68 // Remove all remaining friends that aren't in SkipFriendIds & give response.
69 69 const promises = [];
70 const removeFriendOids: IOid[] = [];
70 const removeFriendOids: IOidWithLegacySupport[] = [];
71 71 for (const friend of friends) {
72 72 if (!data.SkipFriendIds.find(skipFriendId => checkFriendId(skipFriendId, friend))) {
73 73 promises.push(Friendship.deleteOne({ owner: account._id, friend: friend }));
74 74 promises.push(Friendship.deleteOne({ owner: friend, friend: account._id }));
75 removeFriendOids.push(toOid(friend));
75 removeFriendOids.push(toOid2(friend, account.BuildLabel));
76 76 }
77 77 }
78 78 await Promise.all(promises);
@@ -97,21 +97,21 @@ interface IRemoveFriendsResponseU40 {
97 97
98 98 // < U40
99 99 interface IRemoveFriendsResponseU39 {
100 Friends: IOid[];
100 Friends: IOidWithLegacySupport[];
101 101 }
102 102
103 103 const toRemoveFriendsResponse = async (
104 104 buildLabel: string | undefined,
105 friends: IOid[]
105 friends: IOidWithLegacySupport[]
106 106 ): Promise<IRemoveFriendsResponseU39 | IRemoveFriendsResponseU40> => {
107 107 if (buildLabel && version_compare(buildLabel, gameToBuildVersion["40.0.0"]) < 0) {
108 108 return { Friends: friends } satisfies IRemoveFriendsResponseU39;
109 109 } else {
110 110 const response: IRemoveFriendsResponseU40 = { FriendNames: [] };
111 111 for (const friend of friends) {
112 const acct = await Account.findById(friend.$oid, "DisplayName");
112 const acct = await Account.findById(fromOid(friend));
113 113 if (acct) {
114 response.FriendNames.push(getUnicodeName(acct.DisplayName, buildLabel));
114 response.FriendNames.push(getUnicodeName(acct, buildLabel));
115 115 }
116 116 }
117 117 return response;
Modified src/models/loginModel.ts +2 -1
@@ -25,8 +25,9 @@ const databaseAccountSchema = new Schema<IDatabaseAccountJson>(
25 25 TrackedSettings: { type: [String], default: [] },
26 26 Nonce: { type: Number, default: 0 },
27 27 BuildLabel: String,
28 Dropped: Boolean,
29 28 LastLogin: { type: Date, default: 0 },
29 LastPlatform: Number,
30 Dropped: Boolean,
30 31 LatestEventMessageDate: { type: Date, default: 0 },
31 32 LastLoginRewardDate: { type: Number, default: 0 },
32 33 LoginDays: { type: Number, default: 1 },
Modified src/services/friendService.ts +5 -4
@@ -3,13 +3,14 @@ import { getInventory } from "./inventoryService.ts";
3 3 import { Account } from "../models/loginModel.ts";
4 4 import type { Types } from "mongoose";
5 5 import { Friendship } from "../models/friendModel.ts";
6 import { fromOid, toMongoDate } from "../helpers/inventoryHelpers.ts";
6 import { fromOid, toMongoDate2 } from "../helpers/inventoryHelpers.ts";
7 7 import { getUnicodeName } from "./loginService.ts";
8 8
9 9 export const addAccountDataToFriendInfo = async (info: IFriendInfo, buildLabel: string | undefined): Promise<void> => {
10 const account = (await Account.findById(fromOid(info._id), "DisplayName LastLogin"))!;
11 info.DisplayName = getUnicodeName(account.DisplayName, buildLabel);
12 info.LastLogin = toMongoDate(account.LastLogin);
10 const account = (await Account.findById(fromOid(info._id)))!;
11 info.DisplayName = getUnicodeName(account, buildLabel);
12 info.LastLogin = toMongoDate2(account.LastLogin, buildLabel);
13 info.LastPlatform = account.LastPlatform;
13 14 };
14 15
15 16 export const addInventoryDataToFriendInfo = async (info: IFriendInfo): Promise<void> => {
Modified src/services/loginService.ts +11 -7
@@ -1,6 +1,6 @@
1 1 import { Account } from "../models/loginModel.ts";
2 2 import { createInventory } from "./inventoryService.ts";
3 import type { IDatabaseAccountJson, IDatabaseAccountRequiredFields } from "../types/loginTypes.ts";
3 import { Platform, type IDatabaseAccountJson, type IDatabaseAccountRequiredFields } from "../types/loginTypes.ts";
4 4 import { createShip } from "./shipService.ts";
5 5 import type { Document, Types } from "mongoose";
6 6 import { Loadout, type TLoadoutDatabaseDocument } from "../models/inventoryModels/loadoutModel.ts";
@@ -128,10 +128,14 @@ export const isAdministrator = (account: TAccountDocument): boolean => {
128 128 return config.administratorNames?.indexOf(account.DisplayName) != -1;
129 129 };
130 130
131 const platform_magics = [753, 639, 247, 37, 60];
131 export const getOriginalPlatform = (account: TAccountDocument): number => {
132 return account.GoogleTokenId ? Platform.Android : Platform.Windows;
133 };
134
135 const platform_magics = [753, 639, 247, 37, 60, 161];
132 136 export const getSuffixedName = (account: TAccountDocument): string => {
133 137 const name = account.DisplayName;
134 const platformId = 0;
138 const platformId = getOriginalPlatform(account); // Name suffix is based on the original platform, so cross-save does not change it.
135 139 const suffix = ((crc32.str(name.toLowerCase() + "595") >>> 0) + platform_magics[platformId]) % 1000;
136 140 return name + "#" + suffix.toString().padStart(3, "0");
137 141 };
@@ -140,10 +144,10 @@ export const getAccountFromSuffixedName = (name: string): Promise<TAccountDocume
140 144 return Account.findOne({ DisplayName: name.split("#")[0] });
141 145 };
142 146
143 export const getUnicodeName = (DisplayName: string, buildLabel: string | undefined): string => {
147 export const getUnicodeName = (account: TAccountDocument, buildLabel: string | undefined): string => {
144 148 if (buildLabel && version_compare(buildLabel, gameToBuildVersion["32.0.0"]) < 0) {
145 return DisplayName;
149 return account.DisplayName;
146 150 }
147 const platformId = 0; // TODO
148 return DisplayName + String.fromCharCode(0xe000 + platformId);
151 const platformId = getOriginalPlatform(account);
152 return account.DisplayName + String.fromCharCode(0xe000 + platformId);
149 153 };
Modified src/types/friendTypes.ts +4 -3
@@ -1,5 +1,6 @@
1 1 import type { Types } from "mongoose";
2 import type { IMongoDate, IOidWithLegacySupport } from "./commonTypes.ts";
2 import type { IMongoDateWithLegacySupport, IOidWithLegacySupport } from "./commonTypes.ts";
3 import type { Platform } from "./loginTypes.ts";
3 4
4 5 export interface IFriendInfo {
5 6 _id: IOidWithLegacySupport;
@@ -9,13 +10,13 @@ export interface IFriendInfo {
9 10 Status?: number;
10 11 ActiveAvatarImageType?: string;
11 12 TitleType?: string;
12 LastLogin?: IMongoDate;
13 LastLogin?: IMongoDateWithLegacySupport;
13 14 PlayerLevel?: number;
14 15 Suffix?: number;
15 16 Note?: string;
16 17 Favorite?: boolean;
17 18 NewRequest?: boolean;
18 LastPlatform?: number;
19 LastPlatform?: Platform;
19 20 }
20 21
21 22 export interface IFriendship {
Modified src/types/loginTypes.ts +11 -1
@@ -1,5 +1,14 @@
1 1 import type { Types } from "mongoose";
2 2
3 export enum Platform {
4 Windows = 0,
5 PlayStation = 1,
6 Xbox = 2,
7 Switch = 3,
8 iOS = 4,
9 Android = 5
10 }
11
3 12 export interface IAccountAndLoginResponseCommons {
4 13 DisplayName: string;
5 14 CountryCode?: string;
@@ -22,7 +31,8 @@ export interface IDatabaseAccountRequiredFields extends IAccountAndLoginResponse
22 31 }
23 32
24 33 export interface IDatabaseAccount extends IDatabaseAccountRequiredFields {
25 Dropped?: boolean;
34 LastPlatform?: Platform;
35 Dropped?: true;
26 36 LatestEventMessageDate: Date;
27 37 LastLoginRewardDate: number;
28 38 LoginDays: number;