返回提交历史
Added
src/controllers/api/cancelGuildAdvertisementController.ts
+20
-0
Modified
src/controllers/api/contributeToDojoComponentController.ts
+2
-2
Modified
src/controllers/api/contributeToVaultController.ts
+6
-8
Modified
src/controllers/api/guildTechController.ts
+2
-2
Added
src/controllers/api/postGuildAdvertisementController.ts
+75
-0
Added
src/controllers/dynamic/getGuildAdsController.ts
+26
-0
Modified
src/models/guildModel.ts
+19
-1
Modified
src/routes/api.ts
+4
-0
Modified
src/routes/dynamic.ts
+2
-0
Modified
src/services/guildService.ts
+17
-1
Modified
src/types/guildTypes.ts
+25
-0
XFEstudio/XFESpaceNinjaServer
feat: guild ads (#1390)
Reviewed-on: https://onlyg.it/OpenWF/SpaceNinjaServer/pulls/1390 Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com> Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
48598c14
代码差异
11 个文件
+198
-14
@@ -0,0 +1,20 @@
1
import { GuildAd } from "@/src/models/guildModel";
2
import { getGuildForRequestEx, hasGuildPermission } from "@/src/services/guildService";
3
import { getInventory } from "@/src/services/inventoryService";
4
import { getAccountIdForRequest } from "@/src/services/loginService";
5
import { GuildPermission } from "@/src/types/guildTypes";
6
import { RequestHandler } from "express";
7
8
export const cancelGuildAdvertisementController: RequestHandler = async (req, res) => {
9
const accountId = await getAccountIdForRequest(req);
10
const inventory = await getInventory(accountId, "GuildId");
11
const guild = await getGuildForRequestEx(req, inventory);
12
if (!(await hasGuildPermission(guild, accountId, GuildPermission.Advertiser))) {
13
res.status(400).end();
14
return;
15
}
16
17
await GuildAd.deleteOne({ GuildId: guild._id });
18
19
res.end();
20
};
@@ -1,6 +1,7 @@
1
1
import { GuildMember, TGuildDatabaseDocument } from "@/src/models/guildModel";
2
2
import { TInventoryDatabaseDocument } from "@/src/models/inventoryModels/inventoryModel";
3
3
import {
4
addGuildMemberMiscItemContribution,
4
5
getDojoClient,
5
6
getGuildForRequestEx,
6
7
hasAccessToDojo,
@@ -143,8 +144,7 @@ const processContribution = (
143
144
ItemCount: ingredientContribution.ItemCount * -1
144
145
});
145
146
146
guildMember.MiscItemsContributed ??= [];
147
guildMember.MiscItemsContributed.push(ingredientContribution);
147
addGuildMemberMiscItemContribution(guildMember, ingredientContribution);
148
148
}
149
149
addMiscItems(inventory, miscItemChanges);
150
150
inventoryChanges.MiscItems = miscItemChanges;
@@ -1,5 +1,9 @@
1
1
import { GuildMember } from "@/src/models/guildModel";
2
import { addVaultMiscItems, getGuildForRequestEx } from "@/src/services/guildService";
2
import {
3
addGuildMemberMiscItemContribution,
4
addVaultMiscItems,
5
getGuildForRequestEx
6
} from "@/src/services/guildService";
3
7
import { addFusionTreasures, addMiscItems, addShipDecorations, getInventory } from "@/src/services/inventoryService";
4
8
import { getAccountIdForRequest } from "@/src/services/loginService";
5
9
import { IFusionTreasure, IMiscItem, ITypeCount } from "@/src/types/inventoryTypes/inventoryTypes";
@@ -25,14 +29,8 @@ export const contributeToVaultController: RequestHandler = async (req, res) => {
25
29
if (request.MiscItems.length) {
26
30
addVaultMiscItems(guild, request.MiscItems);
27
31
28
guildMember.MiscItemsContributed ??= [];
29
32
for (const item of request.MiscItems) {
30
const miscItemContribution = guildMember.MiscItemsContributed.find(x => x.ItemType == item.ItemType);
31
if (miscItemContribution) {
32
miscItemContribution.ItemCount += item.ItemCount;
33
} else {
34
guildMember.MiscItemsContributed.push(item);
35
}
33
addGuildMemberMiscItemContribution(guildMember, item);
36
34
37
35
addMiscItems(inventory, [{ ...item, ItemCount: item.ItemCount * -1 }]);
38
36
}
@@ -1,5 +1,6 @@
1
1
import { RequestHandler } from "express";
2
2
import {
3
addGuildMemberMiscItemContribution,
3
4
getGuildForRequestEx,
4
5
getGuildVault,
5
6
hasAccessToDojo,
@@ -146,8 +147,7 @@ export const guildTechController: RequestHandler = async (req, res) => {
146
147
ItemCount: miscItem.ItemCount * -1
147
148
});
148
149
149
guildMember.MiscItemsContributed ??= [];
150
guildMember.MiscItemsContributed.push(miscItem);
150
addGuildMemberMiscItemContribution(guildMember, miscItem);
151
151
}
152
152
}
153
153
addMiscItems(inventory, miscItemChanges);
@@ -0,0 +1,75 @@
1
import { getJSONfromString } from "@/src/helpers/stringHelpers";
2
import { GuildAd, GuildMember } from "@/src/models/guildModel";
3
import {
4
addGuildMemberMiscItemContribution,
5
addVaultMiscItems,
6
getGuildForRequestEx,
7
getVaultMiscItemCount,
8
hasGuildPermissionEx
9
} from "@/src/services/guildService";
10
import { getInventory } from "@/src/services/inventoryService";
11
import { getAccountIdForRequest } from "@/src/services/loginService";
12
import { getVendorManifestByTypeName, preprocessVendorManifest } from "@/src/services/serversideVendorsService";
13
import { GuildPermission } from "@/src/types/guildTypes";
14
import { IPurchaseParams } from "@/src/types/purchaseTypes";
15
import { RequestHandler } from "express";
16
17
export const postGuildAdvertisementController: RequestHandler = async (req, res) => {
18
const accountId = await getAccountIdForRequest(req);
19
const inventory = await getInventory(accountId, "GuildId MiscItems");
20
const guild = await getGuildForRequestEx(req, inventory);
21
const guildMember = (await GuildMember.findOne({ accountId, guildId: guild._id }))!;
22
if (!hasGuildPermissionEx(guild, guildMember, GuildPermission.Advertiser)) {
23
res.status(400).end();
24
return;
25
}
26
const payload = getJSONfromString<IPostGuildAdvertisementRequest>(String(req.body));
27
28
// Handle resource cost
29
const vendor = preprocessVendorManifest(
30
getVendorManifestByTypeName("/Lotus/Types/Game/VendorManifests/Hubs/GuildAdvertisementVendorManifest")!
31
);
32
const offer = vendor.VendorInfo.ItemManifest.find(x => x.StoreItem == payload.PurchaseParams.StoreItem)!;
33
if (getVaultMiscItemCount(guild, offer.ItemPrices![0].ItemType) >= offer.ItemPrices![0].ItemCount) {
34
addVaultMiscItems(guild, [
35
{
36
ItemType: offer.ItemPrices![0].ItemType,
37
ItemCount: offer.ItemPrices![0].ItemCount * -1
38
}
39
]);
40
} else {
41
const miscItem = inventory.MiscItems.find(x => x.ItemType == offer.ItemPrices![0].ItemType);
42
if (!miscItem || miscItem.ItemCount < offer.ItemPrices![0].ItemCount) {
43
res.status(400).json("Insufficient funds");
44
return;
45
}
46
miscItem.ItemCount -= offer.ItemPrices![0].ItemCount;
47
addGuildMemberMiscItemContribution(guildMember, offer.ItemPrices![0]);
48
await guildMember.save();
49
await inventory.save();
50
}
51
52
// Create or update ad
53
await GuildAd.findOneAndUpdate(
54
{ GuildId: guild._id },
55
{
56
Emblem: guild.Emblem,
57
Expiry: new Date(Date.now() + 12 * 3600 * 1000),
58
Features: payload.Features,
59
GuildName: guild.Name,
60
MemberCount: await GuildMember.countDocuments({ guildId: guild._id, status: 0 }),
61
RecruitMsg: payload.RecruitMsg,
62
Tier: guild.Tier
63
},
64
{ upsert: true }
65
);
66
67
res.end();
68
};
69
70
interface IPostGuildAdvertisementRequest {
71
Features: number;
72
RecruitMsg: string;
73
Languages: string[];
74
PurchaseParams: IPurchaseParams;
75
}
@@ -0,0 +1,26 @@
1
import { toMongoDate, toOid } from "@/src/helpers/inventoryHelpers";
2
import { GuildAd } from "@/src/models/guildModel";
3
import { IGuildAdInfoClient } from "@/src/types/guildTypes";
4
import { RequestHandler } from "express";
5
6
export const getGuildAdsController: RequestHandler = async (req, res) => {
7
const ads = await GuildAd.find(req.query.tier ? { Tier: req.query.tier } : {});
8
const guildAdInfos: IGuildAdInfoClient[] = [];
9
for (const ad of ads) {
10
guildAdInfos.push({
11
_id: toOid(ad.GuildId),
12
CrossPlatformEnabled: true,
13
Emblem: ad.Emblem,
14
Expiry: toMongoDate(ad.Expiry),
15
Features: ad.Features,
16
GuildName: ad.GuildName,
17
MemberCount: ad.MemberCount,
18
OriginalPlatform: 0,
19
RecruitMsg: ad.RecruitMsg,
20
Tier: ad.Tier
21
});
22
}
23
res.json({
24
GuildAdInfos: guildAdInfos
25
});
26
};
@@ -10,7 +10,8 @@ import {
10
10
IGuildLogRoomChange,
11
11
IGuildLogEntryRoster,
12
12
IGuildLogEntryContributable,
13
IDojoLeaderboardEntry
13
IDojoLeaderboardEntry,
14
IGuildAdDatabase
14
15
} from "@/src/types/guildTypes";
15
16
import { Document, Model, model, Schema, Types } from "mongoose";
16
17
import { fusionTreasuresSchema, typeCountSchema } from "./inventoryModels/inventoryModel";
@@ -165,6 +166,7 @@ const guildSchema = new Schema<IGuildDatabase>(
165
166
Ranks: { type: [guildRankSchema], default: defaultRanks },
166
167
TradeTax: { type: Number, default: 0 },
167
168
Tier: { type: Number, default: 1 },
169
Emblem: { type: Boolean },
168
170
DojoComponents: { type: [dojoComponentSchema], default: [] },
169
171
DojoCapacity: { type: Number, default: 100 },
170
172
DojoEnergy: { type: Number, default: 5 },
@@ -225,3 +227,19 @@ const guildMemberSchema = new Schema<IGuildMemberDatabase>({
225
227
guildMemberSchema.index({ accountId: 1, guildId: 1 }, { unique: true });
226
228
227
229
export const GuildMember = model<IGuildMemberDatabase>("GuildMember", guildMemberSchema);
230
231
const guildAdSchema = new Schema<IGuildAdDatabase>({
232
GuildId: { type: Schema.Types.ObjectId, required: true },
233
Emblem: Boolean,
234
Expiry: { type: Date, required: true },
235
Features: { type: Number, required: true },
236
GuildName: { type: String, required: true },
237
MemberCount: { type: Number, required: true },
238
RecruitMsg: { type: String, required: true },
239
Tier: { type: Number, required: true }
240
});
241
242
guildAdSchema.index({ GuildId: 1 }, { unique: true });
243
guildAdSchema.index({ Expiry: 1 }, { expireAfterSeconds: 0 });
244
245
export const GuildAd = model<IGuildAdDatabase>("GuildAd", guildAdSchema);
@@ -9,6 +9,7 @@ import { arcaneCommonController } from "@/src/controllers/api/arcaneCommonContro
9
9
import { archonFusionController } from "@/src/controllers/api/archonFusionController";
10
10
import { artifactsController } from "@/src/controllers/api/artifactsController";
11
11
import { artifactTransmutationController } from "@/src/controllers/api/artifactTransmutationController";
12
import { cancelGuildAdvertisementController } from "@/src/controllers/api/cancelGuildAdvertisementController";
12
13
import { changeDojoRootController } from "@/src/controllers/api/changeDojoRootController";
13
14
import { changeGuildRankController } from "@/src/controllers/api/changeGuildRankController";
14
15
import { checkDailyMissionBonusController } from "@/src/controllers/api/checkDailyMissionBonusController";
@@ -80,6 +81,7 @@ import { nameWeaponController } from "@/src/controllers/api/nameWeaponController
80
81
import { nemesisController } from "@/src/controllers/api/nemesisController";
81
82
import { placeDecoInComponentController } from "@/src/controllers/api/placeDecoInComponentController";
82
83
import { playerSkillsController } from "@/src/controllers/api/playerSkillsController";
84
import { postGuildAdvertisementController } from "@/src/controllers/api/postGuildAdvertisementController";
83
85
import { projectionManagerController } from "@/src/controllers/api/projectionManagerController";
84
86
import { purchaseController } from "@/src/controllers/api/purchaseController";
85
87
import { queueDojoComponentDestructionController } from "@/src/controllers/api/queueDojoComponentDestructionController";
@@ -131,6 +133,7 @@ const apiRouter = express.Router();
131
133
// get
132
134
apiRouter.get("/abandonLibraryDailyTask.php", abandonLibraryDailyTaskController);
133
135
apiRouter.get("/abortDojoComponentDestruction.php", abortDojoComponentDestructionController);
136
apiRouter.get("/cancelGuildAdvertisement.php", cancelGuildAdvertisementController);
134
137
apiRouter.get("/changeGuildRank.php", changeGuildRankController);
135
138
apiRouter.get("/checkDailyMissionBonus.php", checkDailyMissionBonusController);
136
139
apiRouter.get("/claimLibraryDailyTaskReward.php", claimLibraryDailyTaskRewardController);
@@ -228,6 +231,7 @@ apiRouter.post("/nameWeapon.php", nameWeaponController);
228
231
apiRouter.post("/nemesis.php", nemesisController);
229
232
apiRouter.post("/placeDecoInComponent.php", placeDecoInComponentController);
230
233
apiRouter.post("/playerSkills.php", playerSkillsController);
234
apiRouter.post("/postGuildAdvertisement.php", postGuildAdvertisementController);
231
235
apiRouter.post("/projectionManager.php", projectionManagerController);
232
236
apiRouter.post("/purchase.php", purchaseController);
233
237
apiRouter.post("/redeemPromoCode.php", redeemPromoCodeController);
@@ -1,11 +1,13 @@
1
1
import express from "express";
2
2
import { aggregateSessionsController } from "@/src/controllers/dynamic/aggregateSessionsController";
3
import { getGuildAdsController } from "@/src/controllers/dynamic/getGuildAdsController";
3
4
import { getProfileViewingDataController } from "@/src/controllers/dynamic/getProfileViewingDataController";
4
5
import { worldStateController } from "@/src/controllers/dynamic/worldStateController";
5
6
6
7
const dynamicController = express.Router();
7
8
8
9
dynamicController.get("/aggregateSessions.php", aggregateSessionsController);
10
dynamicController.get("/getGuildAds.php", getGuildAdsController);
9
11
dynamicController.get("/getProfileViewingData.php", getProfileViewingDataController);
10
12
dynamicController.get("/worldState.php", worldStateController);
11
13
@@ -1,7 +1,7 @@
1
1
import { Request } from "express";
2
2
import { getAccountIdForRequest } from "@/src/services/loginService";
3
3
import { addRecipes, getInventory } from "@/src/services/inventoryService";
4
import { Guild, GuildMember, TGuildDatabaseDocument } from "@/src/models/guildModel";
4
import { Guild, GuildAd, GuildMember, TGuildDatabaseDocument } from "@/src/models/guildModel";
5
5
import { TInventoryDatabaseDocument } from "@/src/models/inventoryModels/inventoryModel";
6
6
import {
7
7
GuildPermission,
@@ -298,6 +298,10 @@ const moveResourcesToVault = (guild: TGuildDatabaseDocument, component: IDojoCon
298
298
}
299
299
};
300
300
301
export const getVaultMiscItemCount = (guild: TGuildDatabaseDocument, itemType: string): number => {
302
return guild.VaultMiscItems?.find(x => x.ItemType == itemType)?.ItemCount ?? 0;
303
};
304
301
305
export const addVaultMiscItems = (guild: TGuildDatabaseDocument, miscItems: ITypeCount[]): void => {
302
306
guild.VaultMiscItems ??= [];
303
307
for (const miscItem of miscItems) {
@@ -310,6 +314,16 @@ export const addVaultMiscItems = (guild: TGuildDatabaseDocument, miscItems: ITyp
310
314
}
311
315
};
312
316
317
export const addGuildMemberMiscItemContribution = (guildMember: IGuildMemberDatabase, item: ITypeCount): void => {
318
guildMember.MiscItemsContributed ??= [];
319
const miscItemContribution = guildMember.MiscItemsContributed.find(x => x.ItemType == item.ItemType);
320
if (miscItemContribution) {
321
miscItemContribution.ItemCount += item.ItemCount;
322
} else {
323
guildMember.MiscItemsContributed.push(item);
324
}
325
};
326
313
327
export const processDojoBuildMaterialsGathered = (guild: TGuildDatabaseDocument, build: IDojoBuild): void => {
314
328
if (build.guildXpValue) {
315
329
guild.ClaimedXP ??= [];
@@ -513,4 +527,6 @@ export const deleteGuild = async (guildId: Types.ObjectId): Promise<void> => {
513
527
contextInfo: guildId.toString(),
514
528
acceptAction: "GUILD_INVITE"
515
529
});
530
531
await GuildAd.deleteOne({ GuildId: guildId });
516
532
};
@@ -28,6 +28,7 @@ export interface IGuildDatabase {
28
28
Ranks: IGuildRank[];
29
29
TradeTax: number;
30
30
Tier: number;
31
Emblem?: boolean;
31
32
32
33
DojoComponents: IDojoComponentDatabase[];
33
34
DojoCapacity: number;
@@ -223,3 +224,27 @@ export interface IDojoLeaderboardEntry {
223
224
r: number; // rank
224
225
n: string; // displayName
225
226
}
227
228
export interface IGuildAdInfoClient {
229
_id: IOid; // Guild ID
230
CrossPlatformEnabled: boolean;
231
Emblem?: boolean;
232
Expiry: IMongoDate;
233
Features: number;
234
GuildName: string;
235
MemberCount: number;
236
OriginalPlatform: number;
237
RecruitMsg: string;
238
Tier: number;
239
}
240
241
export interface IGuildAdDatabase {
242
GuildId: Types.ObjectId;
243
Emblem?: boolean;
244
Expiry: Date;
245
Features: number;
246
GuildName: string;
247
MemberCount: number;
248
RecruitMsg: string;
249
Tier: number;
250
}