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: notify pre-U40 clients about friendship changes from U40+ clients (#3597)

Reviewed-on: https://onlyg.it/OpenWF/SpaceNinjaServer/pulls/3597 Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com> Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>

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

代码差异

5 个文件 +79 -10
Modified src/controllers/api/addFriendController.ts +16 -1
@@ -1,11 +1,14 @@
1 1 import type { Types } from "mongoose";
2 import { toOid } from "../../helpers/inventoryHelpers.ts";
2 import { fromOid, toOid, 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 { addAccountDataToFriendInfo, addInventoryDataToFriendInfo } from "../../services/friendService.ts";
6 6 import { getAccountForRequest, getAccountIdForRequest } from "../../services/loginService.ts";
7 7 import type { IFriendInfo } from "../../types/friendTypes.ts";
8 8 import type { RequestHandler } from "express";
9 import gameToBuildVersion from "../../constants/gameToBuildVersion.ts";
10 import { Account } from "../../models/loginModel.ts";
11 import { sendCustomMessageToNrs } from "../../helpers/udp.ts";
9 12
10 13 export const addFriendPostController: RequestHandler = async (req, res) => {
11 14 const account = await getAccountForRequest(req);
@@ -34,9 +37,21 @@ export const addFriendPostController: RequestHandler = async (req, res) => {
34 37 } else {
35 38 await acceptFriendRequest(account._id, payload.friend, newFriends);
36 39 }
40 const requesterUsesNrsNotifications =
41 account.BuildLabel && version_compare(account.BuildLabel, gameToBuildVersion["40.0.0"]) < 0;
37 42 for (const newFriend of newFriends) {
38 43 promises.push(addAccountDataToFriendInfo(newFriend, account.BuildLabel));
39 44 promises.push(addInventoryDataToFriendInfo(newFriend));
45 if (!requesterUsesNrsNotifications) {
46 void Account.findById(fromOid(newFriend._id), "BuildLabel").then(newFriendAcct => {
47 const requesteeUsesNrsNotifications =
48 newFriendAcct!.BuildLabel &&
49 version_compare(newFriendAcct!.BuildLabel, gameToBuildVersion["40.0.0"]) < 0;
50 if (requesteeUsesNrsNotifications) {
51 void sendCustomMessageToNrs(`addFriend,${account._id.toString()},${newFriendAcct!._id.toString()}`);
52 }
53 });
54 }
40 55 }
41 56 await Promise.all(promises);
42 57 res.json({
Modified src/controllers/api/addPendingFriendController.ts +11 -0
@@ -9,6 +9,7 @@ import type { IFriendInfo } from "../../types/friendTypes.ts";
9 9 import type { RequestHandler, Response } from "express";
10 10 import { logger } from "../../utils/logger.ts";
11 11 import gameToBuildVersion from "../../constants/gameToBuildVersion.ts";
12 import { sendCustomMessageToNrs } from "../../helpers/udp.ts";
12 13
13 14 export const addPendingFriendPostController: RequestHandler = async (req, res) => {
14 15 const account = await getAccountForRequest(req);
@@ -61,6 +62,16 @@ const sendFriendRequest = async (
61 62 return;
62 63 }
63 64
65 const requesterUsesNrsNotifications =
66 requesterAccount.BuildLabel && version_compare(requesterAccount.BuildLabel, gameToBuildVersion["40.0.0"]) < 0;
67 const requesteeUsesNrsNotifications =
68 requesteeAccount.BuildLabel && version_compare(requesteeAccount.BuildLabel, gameToBuildVersion["40.0.0"]) < 0;
69 if (requesteeUsesNrsNotifications && !requesterUsesNrsNotifications) {
70 void sendCustomMessageToNrs(
71 `addPendingFriend,${requesterAccount._id.toString()},${requesteeAccount._id.toString()}`
72 );
73 }
74
64 75 if (
65 76 !requesterAccount.BuildLabel ||
66 77 version_compare(requesterAccount.BuildLabel, gameToBuildVersion["13.0.0"]) >= 0
Modified src/controllers/api/removeFriendController.ts +13 -9
@@ -1,10 +1,11 @@
1 1 import gameToBuildVersion from "../../constants/gameToBuildVersion.ts";
2 2 import { fromOid, toOid2, version_compare } from "../../helpers/inventoryHelpers.ts";
3 3 import { getJSONfromString } from "../../helpers/stringHelpers.ts";
4 import { sendCustomMessageToNrs } from "../../helpers/udp.ts";
4 5 import { Friendship } from "../../models/friendModel.ts";
5 6 import { Account } from "../../models/loginModel.ts";
6 7 import { getInventory } from "../../services/inventoryService.ts";
7 import { getAccountForRequest, getUnicodeName } from "../../services/loginService.ts";
8 import { getAccountForRequest, getUnicodeName, type TAccountDocument } from "../../services/loginService.ts";
8 9 import type { IOidWithLegacySupport } from "../../types/commonTypes.ts";
9 10 import { parallelForeach } from "../../utils/async-utils.ts";
10 11 import type { RequestHandler } from "express";
@@ -27,14 +28,14 @@ export const removeFriendGetController: RequestHandler = async (req, res) => {
27 28 }
28 29 }
29 30 await Promise.all(promises);
30 res.json(await toRemoveFriendsResponse(account.BuildLabel, friends));
31 res.json(await toRemoveFriendsResponse(account, friends));
31 32 } else {
32 33 const friendId = req.query.friendId as string;
33 34 await Promise.all([
34 35 Friendship.deleteOne({ owner: accountId, friend: friendId }),
35 36 Friendship.deleteOne({ owner: friendId, friend: accountId })
36 37 ]);
37 res.json(await toRemoveFriendsResponse(account.BuildLabel, [{ $oid: friendId }]));
38 res.json(await toRemoveFriendsResponse(account, [{ $oid: friendId }]));
38 39 }
39 40 };
40 41
@@ -76,7 +77,7 @@ export const removeFriendPostController: RequestHandler = async (req, res) => {
76 77 }
77 78 }
78 79 await Promise.all(promises);
79 res.json(await toRemoveFriendsResponse(account.BuildLabel, removeFriendOids));
80 res.json(await toRemoveFriendsResponse(account, removeFriendOids));
80 81 };
81 82
82 83 // The friend ids format is a bit weird, e.g. when 6633b81e9dba0b714f28ff02 (A) is friends with 67cdac105ef1f4b49741c267 (B), A's friend id for B is 808000105ef1f40560ca079e and B's friend id for A is 8000b81e9dba0b06408a8075.
@@ -101,17 +102,20 @@ interface IRemoveFriendsResponseU39 {
101 102 }
102 103
103 104 const toRemoveFriendsResponse = async (
104 buildLabel: string | undefined,
105 account: TAccountDocument,
105 106 friends: IOidWithLegacySupport[]
106 107 ): Promise<IRemoveFriendsResponseU39 | IRemoveFriendsResponseU40> => {
107 if (buildLabel && version_compare(buildLabel, gameToBuildVersion["40.0.0"]) < 0) {
108 if (account.BuildLabel && version_compare(account.BuildLabel, gameToBuildVersion["40.0.0"]) < 0) {
108 109 return { Friends: friends } satisfies IRemoveFriendsResponseU39;
109 110 } else {
110 111 const response: IRemoveFriendsResponseU40 = { FriendNames: [] };
111 112 for (const friend of friends) {
112 const acct = await Account.findById(fromOid(friend));
113 if (acct) {
114 response.FriendNames.push(getUnicodeName(acct, buildLabel));
113 const friendAcct = await Account.findById(fromOid(friend));
114 if (friendAcct) {
115 response.FriendNames.push(getUnicodeName(friendAcct, account.BuildLabel));
116 if (friendAcct.BuildLabel && version_compare(friendAcct.BuildLabel, gameToBuildVersion["40.0.0"]) < 0) {
117 void sendCustomMessageToNrs(`removeFriend,${account._id.toString()},${friendAcct._id.toString()}`);
118 }
115 119 }
116 120 }
117 121 return response;
Added src/helpers/udp.ts +28 -0
@@ -0,0 +1,28 @@
1 import dgram from "node:dgram";
2 import { getNrsAddress } from "../services/configService.ts";
3 import { crc32 } from "node:zlib";
4
5 export const sendUdpMessage = (host: string, port: number, message: Buffer | string): Promise<void> => {
6 return new Promise<void>((resolve, reject) => {
7 const socket = dgram.createSocket("udp4");
8 socket.send(message, port, host, err => {
9 socket.close();
10 if (err) {
11 reject(err);
12 } else {
13 resolve();
14 }
15 });
16 });
17 };
18
19 export const sendCustomMessageToNrs = (message: string): Promise<void> => {
20 const [nrsAddr, nrsPort] = getNrsAddress();
21 const msgbuf = Buffer.from("\x00" + message, "utf8");
22 const crc = crc32(Buffer.from("b471e49539930dc9b5a131e6247c7387A", "utf8"), crc32(msgbuf));
23 return sendUdpMessage(
24 nrsAddr,
25 nrsPort,
26 Buffer.concat([Buffer.from([0x00, crc >> 24, crc >> 16, crc >> 8, crc]), msgbuf])
27 );
28 };
Modified src/services/configService.ts +11 -0
@@ -282,3 +282,14 @@ export const getWebServerParams = (): IWebServerParams => {
282 282 keyFile: config.httpsKeyFile || "static/cert/key.pem"
283 283 };
284 284 };
285
286 export const getNrsAddress = (): [string, number] => {
287 let nrsAddr = (config.nrsAddress || "%THIS_MACHINE%").split("%THIS_MACHINE%").join("127.0.0.1");
288 let nrsPort = 4950;
289 const colon = nrsAddr.indexOf(":");
290 if (colon != -1) {
291 nrsPort = parseInt(nrsAddr.substring(colon + 1));
292 nrsAddr = nrsAddr.substring(0, colon);
293 }
294 return [nrsAddr, nrsPort];
295 };