返回提交历史
Modified
src/controllers/api/getShipController.ts
+4
-1
Modified
src/controllers/api/setShipFavouriteLoadoutController.ts
+23
-12
Added
src/controllers/api/setShipVignetteController.ts
+48
-0
Modified
src/models/personalRoomsModel.ts
+24
-20
Modified
src/routes/api.ts
+2
-0
Modified
src/types/personalRoomsTypes.ts
+8
-3
Modified
src/types/shipTypes.ts
+13
-2
XFEstudio/SpaceNinjaServer
feat: personal quarters loadout, stencil, vignette, & fish customisation (#1619)
Closes #1618 Reviewed-on: https://onlyg.it/OpenWF/SpaceNinjaServer/pulls/1619 Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com> Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
827ea474
代码差异
7 个文件
+122
-38
@@ -26,7 +26,10 @@ export const getShipController: RequestHandler = async (req, res) => {
26
26
Colors: personalRooms.ShipInteriorColors,
27
27
ShipAttachments: ship.ShipAttachments,
28
28
SkinFlavourItem: ship.SkinFlavourItem
29
}
29
},
30
FavouriteLoadoutId: personalRooms.Ship.FavouriteLoadoutId
31
? toOid(personalRooms.Ship.FavouriteLoadoutId)
32
: undefined
30
33
},
31
34
Apartment: personalRooms.Apartment,
32
35
TailorShop: personalRooms.TailorShop
@@ -3,29 +3,40 @@ import { RequestHandler } from "express";
3
3
import { getPersonalRooms } from "@/src/services/personalRoomsService";
4
4
import { IOid } from "@/src/types/commonTypes";
5
5
import { Types } from "mongoose";
6
import { IFavouriteLoadoutDatabase, TBootLocation } from "@/src/types/shipTypes";
6
7
7
8
export const setShipFavouriteLoadoutController: RequestHandler = async (req, res) => {
8
9
const accountId = await getAccountIdForRequest(req);
9
10
const personalRooms = await getPersonalRooms(accountId);
10
11
const body = JSON.parse(String(req.body)) as ISetShipFavouriteLoadoutRequest;
11
if (body.BootLocation != "SHOP") {
12
throw new Error(`unexpected BootLocation: ${body.BootLocation}`);
13
}
14
const display = personalRooms.TailorShop.FavouriteLoadouts.find(x => x.Tag == body.TagName);
15
if (display) {
16
display.LoadoutId = new Types.ObjectId(body.FavouriteLoadoutId.$oid);
12
if (body.BootLocation == "LISET") {
13
personalRooms.Ship.FavouriteLoadoutId = new Types.ObjectId(body.FavouriteLoadoutId.$oid);
14
} else if (body.BootLocation == "APARTMENT") {
15
updateTaggedDisplay(personalRooms.Apartment.FavouriteLoadouts, body);
16
} else if (body.BootLocation == "SHOP") {
17
updateTaggedDisplay(personalRooms.TailorShop.FavouriteLoadouts, body);
17
18
} else {
18
personalRooms.TailorShop.FavouriteLoadouts.push({
19
Tag: body.TagName,
20
LoadoutId: new Types.ObjectId(body.FavouriteLoadoutId.$oid)
21
});
19
console.log(body);
20
throw new Error(`unexpected BootLocation: ${body.BootLocation}`);
22
21
}
23
22
await personalRooms.save();
24
23
res.json({});
25
24
};
26
25
27
26
interface ISetShipFavouriteLoadoutRequest {
28
BootLocation: string;
27
BootLocation: TBootLocation;
29
28
FavouriteLoadoutId: IOid;
30
TagName: string;
29
TagName?: string;
31
30
}
31
32
const updateTaggedDisplay = (arr: IFavouriteLoadoutDatabase[], body: ISetShipFavouriteLoadoutRequest): void => {
33
const display = arr.find(x => x.Tag == body.TagName!);
34
if (display) {
35
display.LoadoutId = new Types.ObjectId(body.FavouriteLoadoutId.$oid);
36
} else {
37
arr.push({
38
Tag: body.TagName!,
39
LoadoutId: new Types.ObjectId(body.FavouriteLoadoutId.$oid)
40
});
41
}
42
};
@@ -0,0 +1,48 @@
1
import { addMiscItems, combineInventoryChanges, getInventory } from "@/src/services/inventoryService";
2
import { getAccountIdForRequest } from "@/src/services/loginService";
3
import { getPersonalRooms } from "@/src/services/personalRoomsService";
4
import { IInventoryChanges } from "@/src/types/purchaseTypes";
5
import { logger } from "@/src/utils/logger";
6
import { RequestHandler } from "express";
7
8
export const setShipVignetteController: RequestHandler = async (req, res) => {
9
const accountId = await getAccountIdForRequest(req);
10
const inventory = await getInventory(accountId, "MiscItems");
11
const personalRooms = await getPersonalRooms(accountId);
12
const body = JSON.parse(String(req.body)) as ISetShipVignetteRequest;
13
personalRooms.Ship.Wallpaper = body.Wallpaper;
14
personalRooms.Ship.Vignette = body.Vignette;
15
personalRooms.Ship.VignetteFish ??= [];
16
const inventoryChanges: IInventoryChanges = {};
17
for (let i = 0; i != body.Fish.length; ++i) {
18
if (body.Fish[i] && !personalRooms.Ship.VignetteFish[i]) {
19
logger.debug(`moving ${body.Fish[i]} from inventory to vignette slot ${i}`);
20
const miscItemsDelta = [{ ItemType: body.Fish[i], ItemCount: -1 }];
21
addMiscItems(inventory, miscItemsDelta);
22
combineInventoryChanges(inventoryChanges, { MiscItems: miscItemsDelta });
23
} else if (personalRooms.Ship.VignetteFish[i] && !body.Fish[i]) {
24
logger.debug(`moving ${personalRooms.Ship.VignetteFish[i]} from vignette slot ${i} to inventory`);
25
const miscItemsDelta = [{ ItemType: personalRooms.Ship.VignetteFish[i], ItemCount: +1 }];
26
addMiscItems(inventory, miscItemsDelta);
27
combineInventoryChanges(inventoryChanges, { MiscItems: miscItemsDelta });
28
}
29
}
30
personalRooms.Ship.VignetteFish = body.Fish;
31
if (body.VignetteDecos.length) {
32
logger.error(`setShipVignette request not fully handled:`, body);
33
}
34
await Promise.all([inventory.save(), personalRooms.save()]);
35
res.json({
36
Wallpaper: body.Wallpaper,
37
Vignette: body.Vignette,
38
VignetteFish: body.Fish,
39
InventoryChanges: inventoryChanges
40
});
41
};
42
43
interface ISetShipVignetteRequest {
44
Wallpaper: string;
45
Vignette: string;
46
Fish: string[];
47
VignetteDecos: unknown[];
48
}
@@ -2,13 +2,13 @@ import { toOid } from "@/src/helpers/inventoryHelpers";
2
2
import { colorSchema } from "@/src/models/inventoryModels/inventoryModel";
3
3
import { IOrbiter, IPersonalRoomsDatabase, PersonalRoomsModelType } from "@/src/types/personalRoomsTypes";
4
4
import {
5
IApartment,
6
5
IFavouriteLoadoutDatabase,
7
6
IGardening,
8
7
IPlacedDecosDatabase,
9
8
IPictureFrameInfo,
10
9
IRoom,
11
ITailorShopDatabase
10
ITailorShopDatabase,
11
IApartmentDatabase
12
12
} from "@/src/types/shipTypes";
13
13
import { Schema, model } from "mongoose";
14
14
@@ -62,19 +62,34 @@ const roomSchema = new Schema<IRoom>(
62
62
{ _id: false }
63
63
);
64
64
65
const favouriteLoadoutSchema = new Schema<IFavouriteLoadoutDatabase>(
66
{
67
Tag: String,
68
LoadoutId: Schema.Types.ObjectId
69
},
70
{ _id: false }
71
);
72
favouriteLoadoutSchema.set("toJSON", {
73
virtuals: true,
74
transform(_document, returnedObject) {
75
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
76
returnedObject.LoadoutId = toOid(returnedObject.LoadoutId);
77
}
78
});
79
65
80
const gardeningSchema = new Schema<IGardening>({
66
81
Planters: [Schema.Types.Mixed] //TODO: add when implementing gardening
67
82
});
68
83
69
const apartmentSchema = new Schema<IApartment>(
84
const apartmentSchema = new Schema<IApartmentDatabase>(
70
85
{
71
86
Rooms: [roomSchema],
72
FavouriteLoadouts: [Schema.Types.Mixed],
87
FavouriteLoadouts: [favouriteLoadoutSchema],
73
88
Gardening: gardeningSchema // TODO: ensure this is correct
74
89
},
75
90
{ _id: false }
76
91
);
77
const apartmentDefault: IApartment = {
92
const apartmentDefault: IApartmentDatabase = {
78
93
Rooms: [
79
94
{ Name: "ElevatorLanding", MaxCapacity: 1600 },
80
95
{ Name: "ApartmentRoomA", MaxCapacity: 1000 },
@@ -90,6 +105,10 @@ const orbiterSchema = new Schema<IOrbiter>(
90
105
{
91
106
Features: [String],
92
107
Rooms: [roomSchema],
108
VignetteFish: { type: [String], default: undefined },
109
FavouriteLoadoutId: Schema.Types.ObjectId,
110
Wallpaper: String,
111
Vignette: String,
93
112
ContentUrlSignature: { type: String, required: false },
94
113
BootLocation: String
95
114
},
@@ -107,21 +126,6 @@ const orbiterDefault: IOrbiter = {
107
126
]
108
127
};
109
128
110
const favouriteLoadoutSchema = new Schema<IFavouriteLoadoutDatabase>(
111
{
112
Tag: String,
113
LoadoutId: Schema.Types.ObjectId
114
},
115
{ _id: false }
116
);
117
favouriteLoadoutSchema.set("toJSON", {
118
virtuals: true,
119
transform(_document, returnedObject) {
120
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
121
returnedObject.LoadoutId = toOid(returnedObject.LoadoutId);
122
}
123
});
124
125
129
const tailorShopSchema = new Schema<ITailorShopDatabase>(
126
130
{
127
131
FavouriteLoadouts: [favouriteLoadoutSchema],
@@ -115,6 +115,7 @@ import { setGuildMotdController } from "@/src/controllers/api/setGuildMotdContro
115
115
import { setPlacedDecoInfoController } from "@/src/controllers/api/setPlacedDecoInfoController";
116
116
import { setShipCustomizationsController } from "@/src/controllers/api/setShipCustomizationsController";
117
117
import { setShipFavouriteLoadoutController } from "@/src/controllers/api/setShipFavouriteLoadoutController";
118
import { setShipVignetteController } from "@/src/controllers/api/setShipVignetteController";
118
119
import { setSupportedSyndicateController } from "@/src/controllers/api/setSupportedSyndicateController";
119
120
import { setWeaponSkillTreeController } from "@/src/controllers/api/setWeaponSkillTreeController";
120
121
import { shipDecorationsController } from "@/src/controllers/api/shipDecorationsController";
@@ -277,6 +278,7 @@ apiRouter.post("/setGuildMotd.php", setGuildMotdController);
277
278
apiRouter.post("/setPlacedDecoInfo.php", setPlacedDecoInfoController);
278
279
apiRouter.post("/setShipCustomizations.php", setShipCustomizationsController);
279
280
apiRouter.post("/setShipFavouriteLoadout.php", setShipFavouriteLoadoutController);
281
apiRouter.post("/setShipVignette.php", setShipVignetteController);
280
282
apiRouter.post("/setWeaponSkillTree.php", setWeaponSkillTreeController);
281
283
apiRouter.post("/shipDecorations.php", shipDecorationsController);
282
284
apiRouter.post("/startCollectibleEntry.php", startCollectibleEntryController);
@@ -5,13 +5,18 @@ import {
5
5
IPlacedDecosDatabase,
6
6
ITailorShop,
7
7
ITailorShopDatabase,
8
TBootLocation
8
TBootLocation,
9
IApartmentDatabase
9
10
} from "@/src/types/shipTypes";
10
11
import { Document, Model, Types } from "mongoose";
11
12
12
13
export interface IOrbiter {
13
14
Features: string[];
14
15
Rooms: IRoom[];
16
VignetteFish?: string[];
17
FavouriteLoadoutId?: Types.ObjectId;
18
Wallpaper?: string;
19
Vignette?: string;
15
20
ContentUrlSignature?: string;
16
21
BootLocation?: TBootLocation;
17
22
}
@@ -28,7 +33,7 @@ export interface IPersonalRoomsDatabase {
28
33
personalRoomsOwnerId: Types.ObjectId;
29
34
activeShipId: Types.ObjectId;
30
35
Ship: IOrbiter;
31
Apartment: IApartment;
36
Apartment: IApartmentDatabase;
32
37
TailorShop: ITailorShopDatabase;
33
38
}
34
39
@@ -38,7 +43,7 @@ export type PersonalRoomsDocumentProps = {
38
43
Ship: Omit<IOrbiter, "Rooms"> & {
39
44
Rooms: RoomsType[];
40
45
};
41
Apartment: Omit<IApartment, "Rooms"> & {
46
Apartment: Omit<IApartmentDatabase, "Rooms"> & {
42
47
Rooms: RoomsType[];
43
48
};
44
49
TailorShop: Omit<ITailorShopDatabase, "Rooms"> & {
@@ -28,8 +28,12 @@ export interface IShip {
28
28
ShipId: IOid;
29
29
ShipInterior: IShipInterior;
30
30
Rooms: IRoom[];
31
ContentUrlSignature?: string;
31
VignetteFish?: string[];
32
FavouriteLoadoutId?: IOid;
33
Wallpaper?: string;
34
Vignette?: string;
32
35
BootLocation?: TBootLocation;
36
ContentUrlSignature?: string;
33
37
}
34
38
35
39
export interface IShipDatabase {
@@ -60,10 +64,17 @@ export interface IPlanters {
60
64
export interface IGardening {
61
65
Planters?: IPlanters[];
62
66
}
67
63
68
export interface IApartment {
64
69
Gardening: IGardening;
65
70
Rooms: IRoom[];
66
FavouriteLoadouts: string[];
71
FavouriteLoadouts: IFavouriteLoadout[];
72
}
73
74
export interface IApartmentDatabase {
75
Gardening: IGardening;
76
Rooms: IRoom[];
77
FavouriteLoadouts: IFavouriteLoadoutDatabase[];
67
78
}
68
79
69
80
export interface IPlacedDecosDatabase {