feat(framework): server testing utils, transaction storage simplify, pathfinding tests (#384)
## Server Testing Utils - Add TestServer, TestClient, MockRoom for unit testing - Export testing utilities from @esengine/server/testing ## Transaction Storage (BREAKING) - Simplify RedisStorage/MongoStorage to factory pattern only - Remove DI client injection option - Add lazy connection and Symbol.asyncDispose support - Add 161 unit tests with full coverage ## Pathfinding Tests - Add 150 unit tests covering all components - BinaryHeap, Heuristics, AStarPathfinder, GridMap, NavMesh, PathSmoother ## Docs - Update storage.md for new factory pattern API
This commit is contained in:
@@ -6,8 +6,8 @@
|
||||
import type {
|
||||
ITransactionOperation,
|
||||
ITransactionContext,
|
||||
OperationResult,
|
||||
} from '../core/types.js'
|
||||
OperationResult
|
||||
} from '../core/types.js';
|
||||
|
||||
/**
|
||||
* @zh 操作基类
|
||||
@@ -17,13 +17,13 @@ import type {
|
||||
* @en Provides common operation implementation template
|
||||
*/
|
||||
export abstract class BaseOperation<TData = unknown, TResult = unknown>
|
||||
implements ITransactionOperation<TData, TResult>
|
||||
implements ITransactionOperation<TData, TResult>
|
||||
{
|
||||
abstract readonly name: string
|
||||
readonly data: TData
|
||||
readonly data: TData;
|
||||
|
||||
constructor(data: TData) {
|
||||
this.data = data
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -31,7 +31,7 @@ export abstract class BaseOperation<TData = unknown, TResult = unknown>
|
||||
* @en Validate preconditions (passes by default)
|
||||
*/
|
||||
async validate(_ctx: ITransactionContext): Promise<boolean> {
|
||||
return true
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -51,7 +51,7 @@ export abstract class BaseOperation<TData = unknown, TResult = unknown>
|
||||
* @en Create success result
|
||||
*/
|
||||
protected success(data?: TResult): OperationResult<TResult> {
|
||||
return { success: true, data }
|
||||
return { success: true, data };
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -59,6 +59,6 @@ export abstract class BaseOperation<TData = unknown, TResult = unknown>
|
||||
* @en Create failure result
|
||||
*/
|
||||
protected failure(error: string, errorCode?: string): OperationResult<TResult> {
|
||||
return { success: false, error, errorCode }
|
||||
return { success: false, error, errorCode };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
* @en Currency operation
|
||||
*/
|
||||
|
||||
import type { ITransactionContext, OperationResult } from '../core/types.js'
|
||||
import { BaseOperation } from './BaseOperation.js'
|
||||
import type { ITransactionContext, OperationResult } from '../core/types.js';
|
||||
import { BaseOperation } from './BaseOperation.js';
|
||||
|
||||
/**
|
||||
* @zh 货币操作类型
|
||||
@@ -112,89 +112,89 @@ export interface ICurrencyProvider {
|
||||
* ```
|
||||
*/
|
||||
export class CurrencyOperation extends BaseOperation<CurrencyOperationData, CurrencyOperationResult> {
|
||||
readonly name = 'currency'
|
||||
readonly name = 'currency';
|
||||
|
||||
private _provider: ICurrencyProvider | null = null
|
||||
private _beforeBalance: number = 0
|
||||
private _provider: ICurrencyProvider | null = null;
|
||||
private _beforeBalance: number = 0;
|
||||
|
||||
/**
|
||||
* @zh 设置货币数据提供者
|
||||
* @en Set currency data provider
|
||||
*/
|
||||
setProvider(provider: ICurrencyProvider): this {
|
||||
this._provider = provider
|
||||
return this
|
||||
this._provider = provider;
|
||||
return this;
|
||||
}
|
||||
|
||||
async validate(ctx: ITransactionContext): Promise<boolean> {
|
||||
if (this.data.amount <= 0) {
|
||||
return false
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.data.type === 'deduct') {
|
||||
const balance = await this._getBalance(ctx)
|
||||
return balance >= this.data.amount
|
||||
const balance = await this._getBalance(ctx);
|
||||
return balance >= this.data.amount;
|
||||
}
|
||||
|
||||
return true
|
||||
return true;
|
||||
}
|
||||
|
||||
async execute(ctx: ITransactionContext): Promise<OperationResult<CurrencyOperationResult>> {
|
||||
const { type, playerId, currency, amount } = this.data
|
||||
const { type, playerId, currency, amount } = this.data;
|
||||
|
||||
this._beforeBalance = await this._getBalance(ctx)
|
||||
this._beforeBalance = await this._getBalance(ctx);
|
||||
|
||||
let afterBalance: number
|
||||
let afterBalance: number;
|
||||
|
||||
if (type === 'add') {
|
||||
afterBalance = this._beforeBalance + amount
|
||||
afterBalance = this._beforeBalance + amount;
|
||||
} else {
|
||||
if (this._beforeBalance < amount) {
|
||||
return this.failure('Insufficient balance', 'INSUFFICIENT_BALANCE')
|
||||
return this.failure('Insufficient balance', 'INSUFFICIENT_BALANCE');
|
||||
}
|
||||
afterBalance = this._beforeBalance - amount
|
||||
afterBalance = this._beforeBalance - amount;
|
||||
}
|
||||
|
||||
await this._setBalance(ctx, afterBalance)
|
||||
await this._setBalance(ctx, afterBalance);
|
||||
|
||||
ctx.set(`currency:${playerId}:${currency}:before`, this._beforeBalance)
|
||||
ctx.set(`currency:${playerId}:${currency}:after`, afterBalance)
|
||||
ctx.set(`currency:${playerId}:${currency}:before`, this._beforeBalance);
|
||||
ctx.set(`currency:${playerId}:${currency}:after`, afterBalance);
|
||||
|
||||
return this.success({
|
||||
beforeBalance: this._beforeBalance,
|
||||
afterBalance,
|
||||
})
|
||||
afterBalance
|
||||
});
|
||||
}
|
||||
|
||||
async compensate(ctx: ITransactionContext): Promise<void> {
|
||||
await this._setBalance(ctx, this._beforeBalance)
|
||||
await this._setBalance(ctx, this._beforeBalance);
|
||||
}
|
||||
|
||||
private async _getBalance(ctx: ITransactionContext): Promise<number> {
|
||||
const { playerId, currency } = this.data
|
||||
const { playerId, currency } = this.data;
|
||||
|
||||
if (this._provider) {
|
||||
return this._provider.getBalance(playerId, currency)
|
||||
return this._provider.getBalance(playerId, currency);
|
||||
}
|
||||
|
||||
if (ctx.storage) {
|
||||
const balance = await ctx.storage.get<number>(`player:${playerId}:currency:${currency}`)
|
||||
return balance ?? 0
|
||||
const balance = await ctx.storage.get<number>(`player:${playerId}:currency:${currency}`);
|
||||
return balance ?? 0;
|
||||
}
|
||||
|
||||
return 0
|
||||
return 0;
|
||||
}
|
||||
|
||||
private async _setBalance(ctx: ITransactionContext, amount: number): Promise<void> {
|
||||
const { playerId, currency } = this.data
|
||||
const { playerId, currency } = this.data;
|
||||
|
||||
if (this._provider) {
|
||||
await this._provider.setBalance(playerId, currency, amount)
|
||||
return
|
||||
await this._provider.setBalance(playerId, currency, amount);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ctx.storage) {
|
||||
await ctx.storage.set(`player:${playerId}:currency:${currency}`, amount)
|
||||
await ctx.storage.set(`player:${playerId}:currency:${currency}`, amount);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -204,5 +204,5 @@ export class CurrencyOperation extends BaseOperation<CurrencyOperationData, Curr
|
||||
* @en Create currency operation
|
||||
*/
|
||||
export function createCurrencyOperation(data: CurrencyOperationData): CurrencyOperation {
|
||||
return new CurrencyOperation(data)
|
||||
return new CurrencyOperation(data);
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
* @en Inventory operation
|
||||
*/
|
||||
|
||||
import type { ITransactionContext, OperationResult } from '../core/types.js'
|
||||
import { BaseOperation } from './BaseOperation.js'
|
||||
import type { ITransactionContext, OperationResult } from '../core/types.js';
|
||||
import { BaseOperation } from './BaseOperation.js';
|
||||
|
||||
/**
|
||||
* @zh 背包操作类型
|
||||
@@ -147,136 +147,136 @@ export interface IInventoryProvider {
|
||||
* ```
|
||||
*/
|
||||
export class InventoryOperation extends BaseOperation<InventoryOperationData, InventoryOperationResult> {
|
||||
readonly name = 'inventory'
|
||||
readonly name = 'inventory';
|
||||
|
||||
private _provider: IInventoryProvider | null = null
|
||||
private _beforeItem: ItemData | null = null
|
||||
private _provider: IInventoryProvider | null = null;
|
||||
private _beforeItem: ItemData | null = null;
|
||||
|
||||
/**
|
||||
* @zh 设置背包数据提供者
|
||||
* @en Set inventory data provider
|
||||
*/
|
||||
setProvider(provider: IInventoryProvider): this {
|
||||
this._provider = provider
|
||||
return this
|
||||
this._provider = provider;
|
||||
return this;
|
||||
}
|
||||
|
||||
async validate(ctx: ITransactionContext): Promise<boolean> {
|
||||
const { type, quantity } = this.data
|
||||
const { type, quantity } = this.data;
|
||||
|
||||
if (quantity <= 0) {
|
||||
return false
|
||||
return false;
|
||||
}
|
||||
|
||||
if (type === 'remove') {
|
||||
const item = await this._getItem(ctx)
|
||||
return item !== null && item.quantity >= quantity
|
||||
const item = await this._getItem(ctx);
|
||||
return item !== null && item.quantity >= quantity;
|
||||
}
|
||||
|
||||
if (type === 'add' && this._provider?.hasCapacity) {
|
||||
return this._provider.hasCapacity(this.data.playerId, 1)
|
||||
return this._provider.hasCapacity(this.data.playerId, 1);
|
||||
}
|
||||
|
||||
return true
|
||||
return true;
|
||||
}
|
||||
|
||||
async execute(ctx: ITransactionContext): Promise<OperationResult<InventoryOperationResult>> {
|
||||
const { type, playerId, itemId, quantity, properties } = this.data
|
||||
const { type, playerId, itemId, quantity, properties } = this.data;
|
||||
|
||||
this._beforeItem = await this._getItem(ctx)
|
||||
this._beforeItem = await this._getItem(ctx);
|
||||
|
||||
let afterItem: ItemData | null = null
|
||||
let afterItem: ItemData | null = null;
|
||||
|
||||
switch (type) {
|
||||
case 'add': {
|
||||
if (this._beforeItem) {
|
||||
afterItem = {
|
||||
...this._beforeItem,
|
||||
quantity: this._beforeItem.quantity + quantity,
|
||||
}
|
||||
quantity: this._beforeItem.quantity + quantity
|
||||
};
|
||||
} else {
|
||||
afterItem = {
|
||||
itemId,
|
||||
quantity,
|
||||
properties,
|
||||
}
|
||||
properties
|
||||
};
|
||||
}
|
||||
break
|
||||
break;
|
||||
}
|
||||
|
||||
case 'remove': {
|
||||
if (!this._beforeItem || this._beforeItem.quantity < quantity) {
|
||||
return this.failure('Insufficient item quantity', 'INSUFFICIENT_ITEM')
|
||||
return this.failure('Insufficient item quantity', 'INSUFFICIENT_ITEM');
|
||||
}
|
||||
|
||||
const newQuantity = this._beforeItem.quantity - quantity
|
||||
const newQuantity = this._beforeItem.quantity - quantity;
|
||||
if (newQuantity > 0) {
|
||||
afterItem = {
|
||||
...this._beforeItem,
|
||||
quantity: newQuantity,
|
||||
}
|
||||
quantity: newQuantity
|
||||
};
|
||||
} else {
|
||||
afterItem = null
|
||||
afterItem = null;
|
||||
}
|
||||
break
|
||||
break;
|
||||
}
|
||||
|
||||
case 'update': {
|
||||
if (!this._beforeItem) {
|
||||
return this.failure('Item not found', 'ITEM_NOT_FOUND')
|
||||
return this.failure('Item not found', 'ITEM_NOT_FOUND');
|
||||
}
|
||||
|
||||
afterItem = {
|
||||
...this._beforeItem,
|
||||
quantity: quantity > 0 ? quantity : this._beforeItem.quantity,
|
||||
properties: properties ?? this._beforeItem.properties,
|
||||
}
|
||||
break
|
||||
properties: properties ?? this._beforeItem.properties
|
||||
};
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
await this._setItem(ctx, afterItem)
|
||||
await this._setItem(ctx, afterItem);
|
||||
|
||||
ctx.set(`inventory:${playerId}:${itemId}:before`, this._beforeItem)
|
||||
ctx.set(`inventory:${playerId}:${itemId}:after`, afterItem)
|
||||
ctx.set(`inventory:${playerId}:${itemId}:before`, this._beforeItem);
|
||||
ctx.set(`inventory:${playerId}:${itemId}:after`, afterItem);
|
||||
|
||||
return this.success({
|
||||
beforeItem: this._beforeItem ?? undefined,
|
||||
afterItem: afterItem ?? undefined,
|
||||
})
|
||||
afterItem: afterItem ?? undefined
|
||||
});
|
||||
}
|
||||
|
||||
async compensate(ctx: ITransactionContext): Promise<void> {
|
||||
await this._setItem(ctx, this._beforeItem)
|
||||
await this._setItem(ctx, this._beforeItem);
|
||||
}
|
||||
|
||||
private async _getItem(ctx: ITransactionContext): Promise<ItemData | null> {
|
||||
const { playerId, itemId } = this.data
|
||||
const { playerId, itemId } = this.data;
|
||||
|
||||
if (this._provider) {
|
||||
return this._provider.getItem(playerId, itemId)
|
||||
return this._provider.getItem(playerId, itemId);
|
||||
}
|
||||
|
||||
if (ctx.storage) {
|
||||
return ctx.storage.get<ItemData>(`player:${playerId}:inventory:${itemId}`)
|
||||
return ctx.storage.get<ItemData>(`player:${playerId}:inventory:${itemId}`);
|
||||
}
|
||||
|
||||
return null
|
||||
return null;
|
||||
}
|
||||
|
||||
private async _setItem(ctx: ITransactionContext, item: ItemData | null): Promise<void> {
|
||||
const { playerId, itemId } = this.data
|
||||
const { playerId, itemId } = this.data;
|
||||
|
||||
if (this._provider) {
|
||||
await this._provider.setItem(playerId, itemId, item)
|
||||
return
|
||||
await this._provider.setItem(playerId, itemId, item);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ctx.storage) {
|
||||
if (item) {
|
||||
await ctx.storage.set(`player:${playerId}:inventory:${itemId}`, item)
|
||||
await ctx.storage.set(`player:${playerId}:inventory:${itemId}`, item);
|
||||
} else {
|
||||
await ctx.storage.delete(`player:${playerId}:inventory:${itemId}`)
|
||||
await ctx.storage.delete(`player:${playerId}:inventory:${itemId}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -287,5 +287,5 @@ export class InventoryOperation extends BaseOperation<InventoryOperationData, In
|
||||
* @en Create inventory operation
|
||||
*/
|
||||
export function createInventoryOperation(data: InventoryOperationData): InventoryOperation {
|
||||
return new InventoryOperation(data)
|
||||
return new InventoryOperation(data);
|
||||
}
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
* @en Trade operation
|
||||
*/
|
||||
|
||||
import type { ITransactionContext, OperationResult } from '../core/types.js'
|
||||
import { BaseOperation } from './BaseOperation.js'
|
||||
import { CurrencyOperation, type CurrencyOperationData, type ICurrencyProvider } from './CurrencyOperation.js'
|
||||
import { InventoryOperation, type InventoryOperationData, type IInventoryProvider, type ItemData } from './InventoryOperation.js'
|
||||
import type { ITransactionContext, OperationResult } from '../core/types.js';
|
||||
import { BaseOperation } from './BaseOperation.js';
|
||||
import { CurrencyOperation, type CurrencyOperationData, type ICurrencyProvider } from './CurrencyOperation.js';
|
||||
import { InventoryOperation, type InventoryOperationData, type IInventoryProvider, type ItemData } from './InventoryOperation.js';
|
||||
|
||||
/**
|
||||
* @zh 交易物品
|
||||
@@ -148,67 +148,67 @@ export interface ITradeProvider {
|
||||
* ```
|
||||
*/
|
||||
export class TradeOperation extends BaseOperation<TradeOperationData, TradeOperationResult> {
|
||||
readonly name = 'trade'
|
||||
readonly name = 'trade';
|
||||
|
||||
private _provider: ITradeProvider | null = null
|
||||
private _subOperations: (CurrencyOperation | InventoryOperation)[] = []
|
||||
private _executedCount = 0
|
||||
private _provider: ITradeProvider | null = null;
|
||||
private _subOperations: (CurrencyOperation | InventoryOperation)[] = [];
|
||||
private _executedCount = 0;
|
||||
|
||||
/**
|
||||
* @zh 设置交易数据提供者
|
||||
* @en Set trade data provider
|
||||
*/
|
||||
setProvider(provider: ITradeProvider): this {
|
||||
this._provider = provider
|
||||
return this
|
||||
this._provider = provider;
|
||||
return this;
|
||||
}
|
||||
|
||||
async validate(ctx: ITransactionContext): Promise<boolean> {
|
||||
this._buildSubOperations()
|
||||
this._buildSubOperations();
|
||||
|
||||
for (const op of this._subOperations) {
|
||||
const isValid = await op.validate(ctx)
|
||||
const isValid = await op.validate(ctx);
|
||||
if (!isValid) {
|
||||
return false
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
return true;
|
||||
}
|
||||
|
||||
async execute(ctx: ITransactionContext): Promise<OperationResult<TradeOperationResult>> {
|
||||
this._buildSubOperations()
|
||||
this._executedCount = 0
|
||||
this._buildSubOperations();
|
||||
this._executedCount = 0;
|
||||
|
||||
try {
|
||||
for (const op of this._subOperations) {
|
||||
const result = await op.execute(ctx)
|
||||
const result = await op.execute(ctx);
|
||||
if (!result.success) {
|
||||
await this._compensateExecuted(ctx)
|
||||
return this.failure(result.error ?? 'Trade operation failed', 'TRADE_FAILED')
|
||||
await this._compensateExecuted(ctx);
|
||||
return this.failure(result.error ?? 'Trade operation failed', 'TRADE_FAILED');
|
||||
}
|
||||
this._executedCount++
|
||||
this._executedCount++;
|
||||
}
|
||||
|
||||
return this.success({
|
||||
tradeId: this.data.tradeId,
|
||||
completed: true,
|
||||
})
|
||||
completed: true
|
||||
});
|
||||
} catch (error) {
|
||||
await this._compensateExecuted(ctx)
|
||||
const errorMessage = error instanceof Error ? error.message : String(error)
|
||||
return this.failure(errorMessage, 'TRADE_ERROR')
|
||||
await this._compensateExecuted(ctx);
|
||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||
return this.failure(errorMessage, 'TRADE_ERROR');
|
||||
}
|
||||
}
|
||||
|
||||
async compensate(ctx: ITransactionContext): Promise<void> {
|
||||
await this._compensateExecuted(ctx)
|
||||
await this._compensateExecuted(ctx);
|
||||
}
|
||||
|
||||
private _buildSubOperations(): void {
|
||||
if (this._subOperations.length > 0) return
|
||||
if (this._subOperations.length > 0) return;
|
||||
|
||||
const { partyA, partyB } = this.data
|
||||
const { partyA, partyB } = this.data;
|
||||
|
||||
if (partyA.items) {
|
||||
for (const item of partyA.items) {
|
||||
@@ -217,22 +217,22 @@ export class TradeOperation extends BaseOperation<TradeOperationData, TradeOpera
|
||||
playerId: partyA.playerId,
|
||||
itemId: item.itemId,
|
||||
quantity: item.quantity,
|
||||
reason: `trade:${this.data.tradeId}:give`,
|
||||
})
|
||||
reason: `trade:${this.data.tradeId}:give`
|
||||
});
|
||||
const addOp = new InventoryOperation({
|
||||
type: 'add',
|
||||
playerId: partyB.playerId,
|
||||
itemId: item.itemId,
|
||||
quantity: item.quantity,
|
||||
reason: `trade:${this.data.tradeId}:receive`,
|
||||
})
|
||||
reason: `trade:${this.data.tradeId}:receive`
|
||||
});
|
||||
|
||||
if (this._provider?.inventoryProvider) {
|
||||
removeOp.setProvider(this._provider.inventoryProvider)
|
||||
addOp.setProvider(this._provider.inventoryProvider)
|
||||
removeOp.setProvider(this._provider.inventoryProvider);
|
||||
addOp.setProvider(this._provider.inventoryProvider);
|
||||
}
|
||||
|
||||
this._subOperations.push(removeOp, addOp)
|
||||
this._subOperations.push(removeOp, addOp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -243,22 +243,22 @@ export class TradeOperation extends BaseOperation<TradeOperationData, TradeOpera
|
||||
playerId: partyA.playerId,
|
||||
currency: curr.currency,
|
||||
amount: curr.amount,
|
||||
reason: `trade:${this.data.tradeId}:give`,
|
||||
})
|
||||
reason: `trade:${this.data.tradeId}:give`
|
||||
});
|
||||
const addOp = new CurrencyOperation({
|
||||
type: 'add',
|
||||
playerId: partyB.playerId,
|
||||
currency: curr.currency,
|
||||
amount: curr.amount,
|
||||
reason: `trade:${this.data.tradeId}:receive`,
|
||||
})
|
||||
reason: `trade:${this.data.tradeId}:receive`
|
||||
});
|
||||
|
||||
if (this._provider?.currencyProvider) {
|
||||
deductOp.setProvider(this._provider.currencyProvider)
|
||||
addOp.setProvider(this._provider.currencyProvider)
|
||||
deductOp.setProvider(this._provider.currencyProvider);
|
||||
addOp.setProvider(this._provider.currencyProvider);
|
||||
}
|
||||
|
||||
this._subOperations.push(deductOp, addOp)
|
||||
this._subOperations.push(deductOp, addOp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -269,22 +269,22 @@ export class TradeOperation extends BaseOperation<TradeOperationData, TradeOpera
|
||||
playerId: partyB.playerId,
|
||||
itemId: item.itemId,
|
||||
quantity: item.quantity,
|
||||
reason: `trade:${this.data.tradeId}:give`,
|
||||
})
|
||||
reason: `trade:${this.data.tradeId}:give`
|
||||
});
|
||||
const addOp = new InventoryOperation({
|
||||
type: 'add',
|
||||
playerId: partyA.playerId,
|
||||
itemId: item.itemId,
|
||||
quantity: item.quantity,
|
||||
reason: `trade:${this.data.tradeId}:receive`,
|
||||
})
|
||||
reason: `trade:${this.data.tradeId}:receive`
|
||||
});
|
||||
|
||||
if (this._provider?.inventoryProvider) {
|
||||
removeOp.setProvider(this._provider.inventoryProvider)
|
||||
addOp.setProvider(this._provider.inventoryProvider)
|
||||
removeOp.setProvider(this._provider.inventoryProvider);
|
||||
addOp.setProvider(this._provider.inventoryProvider);
|
||||
}
|
||||
|
||||
this._subOperations.push(removeOp, addOp)
|
||||
this._subOperations.push(removeOp, addOp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -295,29 +295,29 @@ export class TradeOperation extends BaseOperation<TradeOperationData, TradeOpera
|
||||
playerId: partyB.playerId,
|
||||
currency: curr.currency,
|
||||
amount: curr.amount,
|
||||
reason: `trade:${this.data.tradeId}:give`,
|
||||
})
|
||||
reason: `trade:${this.data.tradeId}:give`
|
||||
});
|
||||
const addOp = new CurrencyOperation({
|
||||
type: 'add',
|
||||
playerId: partyA.playerId,
|
||||
currency: curr.currency,
|
||||
amount: curr.amount,
|
||||
reason: `trade:${this.data.tradeId}:receive`,
|
||||
})
|
||||
reason: `trade:${this.data.tradeId}:receive`
|
||||
});
|
||||
|
||||
if (this._provider?.currencyProvider) {
|
||||
deductOp.setProvider(this._provider.currencyProvider)
|
||||
addOp.setProvider(this._provider.currencyProvider)
|
||||
deductOp.setProvider(this._provider.currencyProvider);
|
||||
addOp.setProvider(this._provider.currencyProvider);
|
||||
}
|
||||
|
||||
this._subOperations.push(deductOp, addOp)
|
||||
this._subOperations.push(deductOp, addOp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async _compensateExecuted(ctx: ITransactionContext): Promise<void> {
|
||||
for (let i = this._executedCount - 1; i >= 0; i--) {
|
||||
await this._subOperations[i].compensate(ctx)
|
||||
await this._subOperations[i].compensate(ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -327,5 +327,5 @@ export class TradeOperation extends BaseOperation<TradeOperationData, TradeOpera
|
||||
* @en Create trade operation
|
||||
*/
|
||||
export function createTradeOperation(data: TradeOperationData): TradeOperation {
|
||||
return new TradeOperation(data)
|
||||
return new TradeOperation(data);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* @en Operations module exports
|
||||
*/
|
||||
|
||||
export { BaseOperation } from './BaseOperation.js'
|
||||
export { BaseOperation } from './BaseOperation.js';
|
||||
|
||||
export {
|
||||
CurrencyOperation,
|
||||
@@ -11,8 +11,8 @@ export {
|
||||
type CurrencyOperationType,
|
||||
type CurrencyOperationData,
|
||||
type CurrencyOperationResult,
|
||||
type ICurrencyProvider,
|
||||
} from './CurrencyOperation.js'
|
||||
type ICurrencyProvider
|
||||
} from './CurrencyOperation.js';
|
||||
|
||||
export {
|
||||
InventoryOperation,
|
||||
@@ -21,8 +21,8 @@ export {
|
||||
type InventoryOperationData,
|
||||
type InventoryOperationResult,
|
||||
type IInventoryProvider,
|
||||
type ItemData,
|
||||
} from './InventoryOperation.js'
|
||||
type ItemData
|
||||
} from './InventoryOperation.js';
|
||||
|
||||
export {
|
||||
TradeOperation,
|
||||
@@ -32,5 +32,5 @@ export {
|
||||
type TradeItem,
|
||||
type TradeCurrency,
|
||||
type TradeParty,
|
||||
type ITradeProvider,
|
||||
} from './TradeOperation.js'
|
||||
type ITradeProvider
|
||||
} from './TradeOperation.js';
|
||||
|
||||
Reference in New Issue
Block a user