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

chore: use mongodb for pending trades (#3908)

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

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

代码差异

8 个文件 +194 -146
Modified src/controllers/api/inventoryController.ts +9 -2
@@ -66,6 +66,8 @@ import { generateRewardSeed } from "../../services/rngService.ts";
66 66 import { getInvasionByOid, getWorldState } from "../../services/worldStateService.ts";
67 67 import { createMessage } from "../../services/inboxService.ts";
68 68 import gameToBuildVersion from "../../constants/gameToBuildVersion.ts";
69 import { PendingTrade } from "../../models/tradingModel.ts";
70 import { exportTrade } from "../../services/tradingService.ts";
69 71
70 72 export const inventoryController: RequestHandler = async (request, response) => {
71 73 const account = await getAccountForRequest(request);
@@ -361,13 +363,18 @@ export const getInventoryResponse = async (
361 363 buildLabel: string | undefined,
362 364 forWebui: boolean = false
363 365 ): Promise<IInventoryClient> => {
364 const [inventoryWithLoadOutPresets, ships, latestMessage] = await Promise.all([
366 const [inventoryWithLoadOutPresets, ships, latestMessage, pendingTrades] = await Promise.all([
365 367 inventory.populate<{ LoadOutPresets: ILoadoutDatabase }>("LoadOutPresets"),
366 368 Ship.find({ ShipOwnerId: inventory.accountOwnerId }),
367 Inbox.findOne({ ownerId: inventory.accountOwnerId }, "_id").sort({ date: -1 })
369 Inbox.findOne({ ownerId: inventory.accountOwnerId }, "_id").sort({ date: -1 }),
370 PendingTrade.find({ $or: [{ a: inventory.accountOwnerId }, { b: inventory.accountOwnerId }] })
368 371 ]);
369 372 const inventoryResponse = inventoryWithLoadOutPresets.toJSON<IInventoryClient>();
370 373 inventoryResponse.Ships = ships.map(x => x.toJSON<IShipInventory>());
374 inventoryResponse.PendingTrades = pendingTrades.map(trade => {
375 const us = trade.a.equals(inventory.accountOwnerId) ? "a" : "b";
376 return exportTrade(trade, us);
377 });
371 378
372 379 if (!forWebui) {
373 380 for (const key of accountCheatBooleans) {
Modified src/controllers/api/tradingController.ts +102 -118
@@ -11,7 +11,7 @@ import {
11 11 import { getAccountForRequest } from "../../services/loginService.ts";
12 12 import { GuildPermission } from "../../types/guildTypes.ts";
13 13 import type { RequestHandler } from "express";
14 import type { IPendingTrade, ITradeOffer } from "../../types/inventoryTypes/inventoryTypes.ts";
14 import type { IPendingTradeDatabase, ITradeOffer } from "../../types/tradingTypes.ts";
15 15 import { getJSONfromString } from "../../helpers/stringHelpers.ts";
16 16 import { logger } from "../../utils/logger.ts";
17 17 import { fromMongoDate, version_compare } from "../../helpers/inventoryHelpers.ts";
@@ -21,112 +21,100 @@ import { importUpgrade } from "../../services/importService.ts";
21 21 import { Guild } from "../../models/guildModel.ts";
22 22 import { ExportArcanes, ExportResources, ExportUpgrades, type TRarity } from "warframe-public-export-plus";
23 23 import { getInfNodes, getNemesisManifest } from "../../helpers/nemesisHelpers.ts";
24 import { PendingTrade } from "../../models/tradingModel.ts";
25 import type { HydratedDocument, QueryFilter } from "mongoose";
26 import { exportTrade } from "../../services/tradingService.ts";
24 27
25 28 export const tradingController: RequestHandler = async (req, res) => {
26 29 const account = await getAccountForRequest(req);
27 30 const accountId = account._id.toString();
28 const op = req.query.op as string;
29 if (op == "0" || op == "1") {
31 const op = req.query.op as string as TradingOperation;
32 if (op == TradingOperation.InitialOffer) {
30 33 const buddyId = req.query.buddyId as string;
31 const tradeId = getTradeId(accountId, buddyId);
32 34 const offer = getJSONfromString<ITradeOffer>(String(req.body));
33 pendingTrades[tradeId] ??= {
34 trader: accountId,
35 tradee: buddyId,
36 traderOffer: { _SlotOrderInfo: ["", "", "", "", "", ""] },
37 tradeeOffer: { _SlotOrderInfo: ["", "", "", "", "", ""] },
38 traderReady: false,
39 tradeeReady: false,
40 traderAccepted: false,
41 tradeeAccepted: false,
42 revision: -1,
43 clanTax: (req.query.guildId ? await Guild.findById(req.query.guildId as string) : undefined)?.TradeTax ?? 10 // When trading in Maroo's Bazaar, a 10% "Hub Tax" applies.
44 };
45 const us = accountId == pendingTrades[tradeId].trader ? "trader" : "tradee";
46 logger.debug(`${us} offer:`, offer);
47 {
48 const tax = calcuateTax(offer);
49 const clanTaxRate = pendingTrades[tradeId].clanTax ? 1 / pendingTrades[tradeId].clanTax : 0;
50 const clanTax = clanTaxRate * tax;
51 logger.debug(`calculated tax: ${tax} + ${clanTax}`);
52 }
53 pendingTrades[tradeId][`${us}Offer`] = offer;
54 pendingTrades[tradeId].traderReady = false;
55 pendingTrades[tradeId].tradeeReady = false;
56 pendingTrades[tradeId].traderAccepted = false;
57 pendingTrades[tradeId].tradeeAccepted = false;
58 pendingTrades[tradeId].revision += 1;
35 const us = getSide(accountId, buddyId);
36 const them = us == "a" ? "b" : "a";
37 const trade = await PendingTrade.create({
38 [us]: accountId,
39 [them]: buddyId,
40 [`${us}Offer`]: offer,
41 clanTax:
42 (req.query.guildId ? await Guild.findById(req.query.guildId as string, "TradeTax") : undefined)
43 ?.TradeTax ?? 10 // When trading in Maroo's Bazaar, a 10% "Hub Tax" applies.
44 });
59 45 res.json({
60 PendingTrades: [exportTrade(pendingTrades[tradeId], us)]
46 PendingTrades: [exportTrade(trade, us)]
61 47 });
62 } else if (op == "2") {
48 } else if (op == TradingOperation.CounterOffer) {
63 49 const buddyId = req.query.buddyId as string;
64 const tradeId = getTradeId(accountId, buddyId);
65 if (parseInt(req.query.revision as string) != pendingTrades[tradeId].revision) {
50 const offer = getJSONfromString<ITradeOffer>(String(req.body));
51 const us = getSide(accountId, buddyId);
52 const trade = (await PendingTrade.findOneAndUpdate(
53 getTradeFilter(accountId, buddyId),
54 {
55 $set: {
56 [`${us}Offer`]: offer
57 },
58 $inc: {
59 revision: 1
60 }
61 },
62 { returnDocument: "after" }
63 ))!;
64 res.json({
65 PendingTrades: [exportTrade(trade, us)]
66 });
67 } else if (op == TradingOperation.AcceptTrade) {
68 const buddyId = req.query.buddyId as string;
69 const trade = (await getPendingTrade(accountId, buddyId))!;
70 if (parseInt(req.query.revision as string) != trade.revision) {
66 71 throw new Error(`can't accept trade because it was unexpectedly revised`);
67 72 }
68 const us = accountId == pendingTrades[tradeId].trader ? "trader" : "tradee";
69 pendingTrades[tradeId][`${us}Accepted`] = true;
70 const them = us == "trader" ? "tradee" : "trader";
71 if (pendingTrades[tradeId][`${them}Accepted`]) {
72 const [traderInventory, tradeeInventory] = await Promise.all([
73 getInventory(pendingTrades[tradeId].trader),
74 getInventory(pendingTrades[tradeId].tradee)
75 ]);
73 const us = trade.a.equals(accountId) ? "a" : "b";
74 trade[`${us}Accepted`] = true;
75 const them = us == "a" ? "b" : "a";
76 if (trade[`${them}Accepted`]) {
77 const [aInventory, bInventory] = await Promise.all([getInventory(trade.a), getInventory(trade.b)]);
76 78
77 if (!traderInventory.tradesDontTouchInventory) {
78 applyOfferToInventory(traderInventory, pendingTrades[tradeId].traderOffer, -1); // Traders gives up their items
79 applyOfferToInventory(traderInventory, pendingTrades[tradeId].tradeeOffer, +1); // to get what tradee offered
80 // and pays tax for it
81 await chargeTax(
82 traderInventory,
83 pendingTrades[tradeId].tradeeOffer,
84 pendingTrades[tradeId].clanTax,
85 req.query.guildId as string | undefined
86 );
87 if (!traderInventory.infiniteTrades) {
88 traderInventory.TradesRemaining -= 1;
79 if (!aInventory.tradesDontTouchInventory) {
80 applyOfferToInventory(aInventory, trade.aOffer, -1); // A gives up their items
81 applyOfferToInventory(aInventory, trade.bOffer, +1); // to get what B offered
82 await chargeTax(aInventory, trade.bOffer, trade.clanTax, req.query.guildId as string | undefined); // and pays tax for it
83 if (!aInventory.infiniteTrades) {
84 aInventory.TradesRemaining -= 1;
89 85 }
90 86 }
91 if (!tradeeInventory.tradesDontTouchInventory) {
92 applyOfferToInventory(tradeeInventory, pendingTrades[tradeId].tradeeOffer, -1); // Conversely, tradee gives up their items
93 applyOfferToInventory(tradeeInventory, pendingTrades[tradeId].traderOffer, +1); // to get what trader offered
94 // and pays tax for it
95 await chargeTax(
96 tradeeInventory,
97 pendingTrades[tradeId].traderOffer,
98 pendingTrades[tradeId].clanTax,
99 req.query.guildId as string | undefined
100 );
101 if (!tradeeInventory.infiniteTrades) {
102 tradeeInventory.TradesRemaining -= 1;
87 if (!bInventory.tradesDontTouchInventory) {
88 applyOfferToInventory(bInventory, trade.bOffer, -1); // Conversely, B gives up their items
89 applyOfferToInventory(bInventory, trade.aOffer, +1); // to get what A offered
90 await chargeTax(bInventory, trade.aOffer, trade.clanTax, req.query.guildId as string | undefined); // and pays tax for it
91 if (!bInventory.infiniteTrades) {
92 bInventory.TradesRemaining -= 1;
103 93 }
104 94 }
105 95
106 await Promise.all([traderInventory.save(), tradeeInventory.save()]);
107 delete pendingTrades[tradeId];
96 await Promise.all([aInventory.save(), bInventory.save(), trade.deleteOne()]);
108 97 res.send(
109 98 account.BuildLabel && version_compare(account.BuildLabel, gameToBuildVersion["40.0.0"]) < 0 ? "9" : "12"
110 99 ).end();
111 100 } else {
101 await trade.save();
112 102 res.send(
113 103 account.BuildLabel && version_compare(account.BuildLabel, gameToBuildVersion["40.0.0"]) < 0 ? "8" : "11"
114 104 ).end();
115 105 }
116 } else if (op == "3") {
106 } else if (op == TradingOperation.CancelTrade) {
117 107 const buddyId = req.query.buddyId as string;
118 const tradeId = getTradeId(accountId, buddyId);
119 delete pendingTrades[tradeId];
120 res.json({
121 pendingTrades: []
122 });
123 } else if (op == "4") {
124 const tradeId = getTradeId(accountId, req.query.buddyId as string);
125 const us = accountId == pendingTrades[tradeId].trader ? "trader" : "tradee";
108 await PendingTrade.deleteOne(getTradeFilter(accountId, buddyId));
109 res.end();
110 } else if (op == TradingOperation.RefreshTradeInfo) {
111 const buddyId = req.query.buddyId as string;
112 const trade = (await getPendingTrade(accountId, buddyId))!;
113 const us = trade.a.equals(accountId) ? "a" : "b";
126 114 res.json({
127 PendingTrades: [exportTrade(pendingTrades[tradeId], us)]
115 PendingTrades: [exportTrade(trade, us)]
128 116 });
129 } else if (op == "5") {
117 } else if (op == TradingOperation.SetClanTax) {
130 118 const inventory = await getInventory(accountId, "GuildId LevelKeys");
131 119 const guild = await getGuildForRequestEx(req, inventory);
132 120 if (!hasAccessToDojo(inventory) || !(await hasGuildPermission(guild, accountId, GuildPermission.Treasurer))) {
@@ -136,25 +124,27 @@ export const tradingController: RequestHandler = async (req, res) => {
136 124 guild.TradeTax = parseInt(req.query.tax as string);
137 125 await guild.save();
138 126 res.send(guild.TradeTax).end();
139 } else if (op == "6") {
127 } else if (op == TradingOperation.SetReady) {
140 128 const buddyId = req.query.buddyId as string;
141 const tradeId = getTradeId(accountId, buddyId);
142 const us = accountId == pendingTrades[tradeId].trader ? "trader" : "tradee";
143 if (parseInt(req.query.revision as string) == pendingTrades[tradeId].revision) {
144 pendingTrades[tradeId][`${us}Ready`] = true;
129 const us = getSide(accountId, buddyId);
130 const trade = (await getPendingTrade(accountId, buddyId))!;
131 if (trade.revision == parseInt(req.query.revision as string)) {
132 trade[`${us}Ready`] = true;
133 await trade.save();
145 134 }
146 135 res.json({
147 PendingTrades: [exportTrade(pendingTrades[tradeId], us)]
136 PendingTrades: [exportTrade(trade, us)]
148 137 });
149 } else if (op == "7") {
138 } else if (op == TradingOperation.UnsetReady) {
150 139 const buddyId = req.query.buddyId as string;
151 const tradeId = getTradeId(accountId, buddyId);
152 const us = accountId == pendingTrades[tradeId].trader ? "trader" : "tradee";
153 pendingTrades[tradeId][`${us}Ready`] = false;
154 pendingTrades[tradeId].tradeeAccepted = false;
155 pendingTrades[tradeId].traderAccepted = false;
140 const us = getSide(accountId, buddyId);
141 const trade = (await PendingTrade.findOneAndUpdate(
142 getTradeFilter(accountId, buddyId),
143 { [`${us}Ready`]: false, aAccepted: false, bAccepted: false },
144 { returnDocument: "after" }
145 ))!;
156 146 res.json({
157 PendingTrades: [exportTrade(pendingTrades[tradeId], us)]
147 PendingTrades: [exportTrade(trade, us)]
158 148 });
159 149 } else {
160 150 if (req.body) {
@@ -164,37 +154,31 @@ export const tradingController: RequestHandler = async (req, res) => {
164 154 }
165 155 };
166 156
167 interface IPendingTradeMem {
168 trader: string;
169 tradee: string;
170 traderOffer: ITradeOffer;
171 tradeeOffer: ITradeOffer;
172 traderReady: boolean;
173 tradeeReady: boolean;
174 traderAccepted: boolean;
175 tradeeAccepted: boolean;
176 revision: number;
177 clanTax: number;
157 enum TradingOperation {
158 InitialOffer = "0",
159 CounterOffer = "1",
160 AcceptTrade = "2",
161 CancelTrade = "3",
162 RefreshTradeInfo = "4",
163 SetClanTax = "5",
164 SetReady = "6",
165 UnsetReady = "7",
166 NeverSayNever = "01189998819991197253"
178 167 }
179 168
180 const pendingTrades: Record<string, IPendingTradeMem> = {};
169 const getSide = (accountId: string, buddyId: string): "a" | "b" => {
170 return accountId.localeCompare(buddyId) < 0 ? "a" : "b";
171 };
181 172
182 const getTradeId = (accountId: string, buddyId: string): string => {
183 return accountId.localeCompare(buddyId) < 0 ? accountId + buddyId : buddyId + accountId;
173 const getTradeFilter = (accountId: string, buddyId: string): QueryFilter<IPendingTradeDatabase> => {
174 return accountId.localeCompare(buddyId) < 0 ? { a: accountId, b: buddyId } : { a: buddyId, b: accountId };
184 175 };
185 176
186 const exportTrade = (mem: IPendingTradeMem, us: "trader" | "tradee"): IPendingTrade => {
187 const them = us == "trader" ? "tradee" : "trader";
188 return {
189 ItemId: { $oid: mem[them] },
190 Giving: mem[`${us}Offer`],
191 Getting: mem[`${them}Offer`],
192 SelfReady: mem[`${us}Ready`],
193 BuddyReady: mem[`${them}Ready`],
194 State: mem[`${us}Accepted`] ? (mem[`${them}Accepted`] ? 4 : 3) : 2,
195 Revision: mem.revision,
196 ClanTax: mem.clanTax
197 };
177 const getPendingTrade = (
178 accountId: string,
179 buddyId: string
180 ): Promise<HydratedDocument<IPendingTradeDatabase> | null> => {
181 return PendingTrade.findOne(getTradeFilter(accountId, buddyId));
198 182 };
199 183
200 184 const applyOfferToInventory = (inventory: TInventoryDatabaseDocument, offer: ITradeOffer, factor: 1 | -1): void => {
Modified src/controllers/custom/deleteAccountController.ts +3 -0
@@ -14,6 +14,7 @@ import { Friendship } from "../../models/friendModel.ts";
14 14 import { sendWsBroadcastTo } from "../../services/wsService.ts";
15 15 import { config } from "../../services/configService.ts";
16 16 import { saveConfig } from "../../services/configWriterService.ts";
17 import { PendingTrade } from "../../models/tradingModel.ts";
17 18
18 19 export const deleteAccountController: RequestHandler = async (req, res) => {
19 20 const account = await getAccountForRequest(req);
@@ -44,6 +45,8 @@ export const deleteAccountController: RequestHandler = async (req, res) => {
44 45 Inventory.deleteOne({ accountOwnerId: account._id }),
45 46 Leaderboard.deleteMany({ ownerId: account._id }),
46 47 Loadout.deleteOne({ loadoutOwnerId: account._id }),
48 PendingTrade.deleteMany({ a: account._id }),
49 PendingTrade.deleteMany({ b: account._id }),
47 50 PersonalRooms.deleteOne({ personalRoomsOwnerId: account._id }),
48 51 Ship.deleteMany({ ShipOwnerId: account._id }),
49 52 Stats.deleteOne({ accountOwnerId: account._id })
Modified src/models/inventoryModels/inventoryModel.ts +0 -2
@@ -1671,8 +1671,6 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
1671 1671 TradesRemaining: { type: Number, default: 0 },
1672 1672 //How many gifts do you have left
1673 1673 GiftsRemaining: { type: Number, default: 8 },
1674 //Current trade info Giving or Getting items
1675 //PendingTrades: [Schema.Types.Mixed],
1676 1674
1677 1675 //Syndicate currently being pledged to.
1678 1676 SupportedSyndicate: String,
Added src/models/tradingModel.ts +21 -0
@@ -0,0 +1,21 @@
1 import { model, Schema, Types } from "mongoose";
2 import type { IPendingTradeDatabase, ITradeOffer } from "../types/tradingTypes.ts";
3
4 const defaultTradeOffer: ITradeOffer = { _SlotOrderInfo: ["", "", "", "", "", ""] };
5
6 const pendingTradeSchema = new Schema<IPendingTradeDatabase>({
7 a: { type: Types.ObjectId, required: true },
8 b: { type: Types.ObjectId, required: true },
9 aOffer: { type: Schema.Types.Mixed, default: defaultTradeOffer },
10 bOffer: { type: Schema.Types.Mixed, default: defaultTradeOffer },
11 aReady: { type: Boolean, default: false },
12 bReady: { type: Boolean, default: false },
13 aAccepted: { type: Boolean, default: false },
14 bAccepted: { type: Boolean, default: false },
15 revision: { type: Number, default: 0 },
16 clanTax: { type: Number, required: true }
17 });
18
19 pendingTradeSchema.index({ a: 1, b: 1 }, { unique: true });
20
21 export const PendingTrade = model<IPendingTradeDatabase>("PendingTrade", pendingTradeSchema);
Added src/services/tradingService.ts +16 -0
@@ -0,0 +1,16 @@
1 import { toOid } from "../helpers/inventoryHelpers.ts";
2 import type { IPendingTradeClient, IPendingTradeDatabase } from "../types/tradingTypes.ts";
3
4 export const exportTrade = (trade: IPendingTradeDatabase, us: "a" | "b"): IPendingTradeClient => {
5 const them = us == "a" ? "b" : "a";
6 return {
7 ItemId: toOid(trade[them]),
8 Giving: trade[`${us}Offer`],
9 Getting: trade[`${them}Offer`],
10 SelfReady: trade[`${us}Ready`],
11 BuddyReady: trade[`${them}Ready`],
12 State: trade[`${us}Accepted`] ? (trade[`${them}Accepted`] ? 4 : 3) : 2,
13 Revision: trade.revision,
14 ClanTax: trade.clanTax
15 };
16 };
Modified src/types/inventoryTypes/inventoryTypes.ts +3 -24
@@ -28,6 +28,7 @@ import type {
28 28 import type { ILoadoutConfigClientLegacy, ILoadOutPresets } from "../saveLoadoutTypes.ts";
29 29 import type { CalendarSeasonType } from "../worldStateTypes.ts";
30 30 import type { SlotNames } from "../purchaseTypes.ts";
31 import type { IPendingTradeClient } from "../tradingTypes.ts";
31 32
32 33 export type InventoryDatabaseEquipment = {
33 34 [_ in TEquipmentKey]: IEquipmentDatabase[];
@@ -154,6 +155,7 @@ export interface IInventoryDatabase
154 155 | "ChallengeInstanceStates"
155 156 | "PeriodicMissionCompletions"
156 157 | "FusionTreasures"
158 | "PendingTrades"
157 159 | TEquipmentKey
158 160 >,
159 161 InventoryDatabaseEquipment,
@@ -432,7 +434,7 @@ export interface IInventoryClient
432 434 KubrowPetEggs?: IKubrowPetEgg[];
433 435 LoreFragmentScans: ILoreFragmentScan[];
434 436 EquippedEmotes: string[];
435 //PendingTrades: IPendingTrade[];
437 PendingTrades: IPendingTradeClient[];
436 438 Boosters: IBooster[];
437 439 ActiveDojoColorResearch: string;
438 440 //SentientSpawnChanceBoosters: ISentientSpawnChanceBoosters;
@@ -1017,29 +1019,6 @@ export interface IAccolades {
1017 1019 Heirloom?: boolean;
1018 1020 }
1019 1021
1020 export interface IPendingTrade {
1021 State: number;
1022 SelfReady: boolean;
1023 BuddyReady: boolean;
1024 Giving?: ITradeOffer;
1025 Revision: number;
1026 Getting: ITradeOffer;
1027 ItemId: IOid;
1028 ClanTax?: number;
1029 }
1030
1031 export interface ITradeOffer {
1032 RandomUpgrades?: IUpgradeClient[];
1033 Upgrades?: IUpgradeClient[];
1034 RawUpgrades?: IRawUpgrade[];
1035 MiscItems?: IMiscItem[];
1036 Recipes?: ITypeCount[];
1037 FusionTreasures?: IFusionTreasure[];
1038 NemesisHistory?: INemesisClient;
1039 PremiumCredits?: number;
1040 _SlotOrderInfo: string[];
1041 }
1042
1043 1022 export interface IInnateDamageFingerprint {
1044 1023 compat: string;
1045 1024 buffs: IFingerprintStat[];
Added src/types/tradingTypes.ts +40 -0
@@ -0,0 +1,40 @@
1 import type { Types } from "mongoose";
2 import type { IOid, ITypeCount } from "./commonTypes.ts";
3 import type { IFusionTreasure, INemesisClient, IRawUpgrade, IUpgradeClient } from "./inventoryTypes/inventoryTypes.ts";
4
5 export interface ITradeOffer {
6 RandomUpgrades?: IUpgradeClient[];
7 Upgrades?: IUpgradeClient[];
8 RawUpgrades?: IRawUpgrade[];
9 MiscItems?: ITypeCount[];
10 Recipes?: ITypeCount[];
11 FusionTreasures?: IFusionTreasure[];
12 NemesisHistory?: INemesisClient;
13 PremiumCredits?: number;
14 _SlotOrderInfo: string[];
15 }
16
17 export interface IPendingTradeClient {
18 State: number;
19 SelfReady: boolean;
20 BuddyReady: boolean;
21 Giving?: ITradeOffer;
22 Revision: number;
23 Getting: ITradeOffer;
24 ItemId: IOid;
25 ClanTax?: number;
26 }
27
28 export interface IPendingTradeDatabase {
29 a: Types.ObjectId;
30 b: Types.ObjectId;
31 aOffer: ITradeOffer;
32 bOffer: ITradeOffer;
33 aReady: boolean;
34 bReady: boolean;
35 aAccepted: boolean;
36 bAccepted: boolean;
37 revision: number;
38 clanTax: number;
39 _id: Types.ObjectId;
40 }