379 lines
9.4 KiB
TypeScript
379 lines
9.4 KiB
TypeScript
import type { GameState } from '../core/GameState';
|
|
import type { Command, CommandExecutionResult } from '../commands/Command';
|
|
import { CommandExecutor } from '../commands/CommandExecutor';
|
|
import type {
|
|
Rule,
|
|
RuleContext,
|
|
RuleResult,
|
|
ValidationRule,
|
|
EffectRule,
|
|
TriggerRule,
|
|
RuleLogEntry,
|
|
} from './Rule';
|
|
import { isValidationRule, isEffectRule, isTriggerRule } from './Rule';
|
|
|
|
/**
|
|
* 规则引擎配置
|
|
*/
|
|
export interface RuleEngineOptions {
|
|
/** 游戏类型 */
|
|
gameType?: string;
|
|
/** 是否启用规则日志 */
|
|
enableLogging?: boolean;
|
|
/** 是否自动执行触发规则 */
|
|
autoExecuteTriggers?: boolean;
|
|
}
|
|
|
|
/**
|
|
* 规则引擎执行结果
|
|
*/
|
|
export interface RuleEngineExecutionResult extends CommandExecutionResult {
|
|
/** 执行的验证规则 */
|
|
validationRules: RuleLogEntry[];
|
|
/** 执行的效果规则 */
|
|
effectRules: RuleLogEntry[];
|
|
/** 触发的规则 */
|
|
triggerRules: RuleLogEntry[];
|
|
/** 触发的额外命令 */
|
|
triggeredCommands: Command[];
|
|
}
|
|
|
|
/**
|
|
* 规则引擎
|
|
* 负责在命令执行前后运行规则,并处理触发规则
|
|
*/
|
|
export class RuleEngine {
|
|
private gameState: GameState;
|
|
private executor: CommandExecutor;
|
|
private rules: Rule[] = [];
|
|
private options: RuleEngineOptions;
|
|
private logs: RuleLogEntry[] = [];
|
|
private isExecuting: boolean = false;
|
|
private triggerQueue: Command[] = [];
|
|
|
|
constructor(gameState: GameState, options: RuleEngineOptions = {}) {
|
|
this.gameState = gameState;
|
|
this.executor = new CommandExecutor(gameState);
|
|
this.options = {
|
|
enableLogging: true,
|
|
autoExecuteTriggers: true,
|
|
...options,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* 注册规则
|
|
*/
|
|
registerRule(rule: Rule): void {
|
|
// 如果指定了游戏类型,只有匹配时才注册
|
|
if (this.options.gameType && rule.gameType && rule.gameType !== this.options.gameType) {
|
|
return;
|
|
}
|
|
this.rules.push(rule);
|
|
// 按优先级排序
|
|
this.rules.sort((a, b) => a.priority - b.priority);
|
|
}
|
|
|
|
/**
|
|
* 注册多个规则
|
|
*/
|
|
registerRules(rules: Rule[]): void {
|
|
for (const rule of rules) {
|
|
this.registerRule(rule);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 移除规则
|
|
*/
|
|
unregisterRule(ruleId: string): void {
|
|
this.rules = this.rules.filter((r) => r.id !== ruleId);
|
|
}
|
|
|
|
/**
|
|
* 清除所有规则
|
|
*/
|
|
clearRules(): void {
|
|
this.rules = [];
|
|
}
|
|
|
|
/**
|
|
* 获取所有规则
|
|
*/
|
|
getRules(): Rule[] {
|
|
return [...this.rules];
|
|
}
|
|
|
|
/**
|
|
* 执行命令(带规则验证)
|
|
*/
|
|
async executeCommand(command: Command): Promise<RuleEngineExecutionResult> {
|
|
if (this.isExecuting) {
|
|
throw new Error('Rule engine is already executing a command');
|
|
}
|
|
|
|
this.isExecuting = true;
|
|
const validationLogs: RuleLogEntry[] = [];
|
|
const effectLogs: RuleLogEntry[] = [];
|
|
const triggerLogs: RuleLogEntry[] = [];
|
|
const triggeredCommands: Command[] = [];
|
|
|
|
try {
|
|
// 创建规则上下文
|
|
const context: RuleContext = {
|
|
gameState: this.gameState,
|
|
command,
|
|
metadata: {},
|
|
};
|
|
|
|
// 1. 执行验证规则
|
|
const validationRules = this.rules.filter(isValidationRule);
|
|
for (const rule of validationRules) {
|
|
if (!this.isRuleApplicable(rule, command)) {
|
|
continue;
|
|
}
|
|
|
|
const result = await rule.validate(context);
|
|
const logEntry = this.createLogEntry(rule, 'validation', result, command.id);
|
|
validationLogs.push(logEntry);
|
|
|
|
if (!result.success) {
|
|
return this.createFailedResult(validationLogs, effectLogs, triggerLogs, triggeredCommands, result.error);
|
|
}
|
|
|
|
if (result.blockCommand) {
|
|
return this.createFailedResult(
|
|
validationLogs,
|
|
effectLogs,
|
|
triggerLogs,
|
|
triggeredCommands,
|
|
`Command blocked by rule: ${rule.name}`
|
|
);
|
|
}
|
|
|
|
// 应用状态更新
|
|
if (result.stateUpdates) {
|
|
Object.assign(context.metadata, result.stateUpdates);
|
|
}
|
|
|
|
// 收集触发的命令
|
|
if (result.triggeredCommands) {
|
|
triggeredCommands.push(...result.triggeredCommands);
|
|
}
|
|
}
|
|
|
|
// 2. 执行命令
|
|
const executionResult = this.executor.execute(command);
|
|
context.executionResult = executionResult;
|
|
|
|
if (!executionResult.success) {
|
|
return this.createFailedResult(
|
|
validationLogs,
|
|
effectLogs,
|
|
triggerLogs,
|
|
triggeredCommands,
|
|
executionResult.error
|
|
);
|
|
}
|
|
|
|
// 3. 执行效果规则
|
|
const effectRules = this.rules.filter(isEffectRule);
|
|
for (const rule of effectRules) {
|
|
if (!this.isRuleApplicable(rule, command)) {
|
|
continue;
|
|
}
|
|
|
|
const result = await rule.apply(context);
|
|
const logEntry = this.createLogEntry(rule, 'effect', result, command.id);
|
|
effectLogs.push(logEntry);
|
|
|
|
if (!result.success) {
|
|
// 效果规则失败不影响命令执行,只记录日志
|
|
continue;
|
|
}
|
|
|
|
// 应用状态更新
|
|
if (result.stateUpdates) {
|
|
Object.assign(context.metadata, result.stateUpdates);
|
|
}
|
|
|
|
// 收集触发的命令
|
|
if (result.triggeredCommands) {
|
|
triggeredCommands.push(...result.triggeredCommands);
|
|
}
|
|
}
|
|
|
|
// 4. 执行触发规则
|
|
if (this.options.autoExecuteTriggers) {
|
|
const triggerRules = this.rules.filter(isTriggerRule);
|
|
for (const rule of triggerRules) {
|
|
const shouldTrigger = await rule.condition(context);
|
|
if (shouldTrigger) {
|
|
const result = await rule.action(context);
|
|
const logEntry = this.createLogEntry(rule, 'trigger', result, command.id);
|
|
triggerLogs.push(logEntry);
|
|
|
|
if (result.triggeredCommands) {
|
|
triggeredCommands.push(...result.triggeredCommands);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 5. 执行触发的命令(在循环外执行,避免递归)
|
|
} finally {
|
|
this.isExecuting = false;
|
|
}
|
|
|
|
// 在主要执行完成后执行触发的命令
|
|
for (const triggeredCommand of triggeredCommands) {
|
|
try {
|
|
const triggerResult = await this.executeCommand(triggeredCommand);
|
|
if (!triggerResult.success) {
|
|
// 触发命令失败,记录但不影响主命令
|
|
this.logs.push({
|
|
timestamp: Date.now(),
|
|
ruleId: 'triggered-command',
|
|
ruleName: 'Triggered Command',
|
|
ruleType: 'trigger',
|
|
result: { success: false, error: `Triggered command ${triggeredCommand.id} failed: ${triggerResult.error}` },
|
|
commandId: triggeredCommand.id,
|
|
});
|
|
}
|
|
} catch (error) {
|
|
// 忽略触发命令的异常
|
|
}
|
|
}
|
|
|
|
return {
|
|
success: true,
|
|
executedSteps: executionResult.executedSteps,
|
|
totalSteps: executionResult.totalSteps,
|
|
validationRules: validationLogs,
|
|
effectRules: effectLogs,
|
|
triggerRules: triggerLogs,
|
|
triggeredCommands,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* 检查规则是否适用于当前命令
|
|
*/
|
|
private isRuleApplicable(
|
|
rule: ValidationRule | EffectRule,
|
|
command: Command
|
|
): boolean {
|
|
// 检查游戏类型
|
|
if (rule.gameType && rule.gameType !== this.options.gameType) {
|
|
return false;
|
|
}
|
|
|
|
// 检查命令名称
|
|
if (rule.applicableCommands && !rule.applicableCommands.includes(command.name)) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* 创建日志条目
|
|
*/
|
|
private createLogEntry(
|
|
rule: Rule,
|
|
ruleType: 'validation' | 'effect' | 'trigger',
|
|
result: RuleResult,
|
|
commandId: string
|
|
): RuleLogEntry {
|
|
const entry: RuleLogEntry = {
|
|
timestamp: Date.now(),
|
|
ruleId: rule.id,
|
|
ruleName: rule.name,
|
|
ruleType,
|
|
result,
|
|
commandId,
|
|
};
|
|
|
|
if (this.options.enableLogging) {
|
|
this.logs.push(entry);
|
|
}
|
|
|
|
return entry;
|
|
}
|
|
|
|
/**
|
|
* 创建失败结果
|
|
*/
|
|
private createFailedResult(
|
|
validationLogs: RuleLogEntry[],
|
|
effectLogs: RuleLogEntry[],
|
|
triggerLogs: RuleLogEntry[],
|
|
triggeredCommands: Command[],
|
|
error?: string
|
|
): RuleEngineExecutionResult {
|
|
return {
|
|
success: false,
|
|
error,
|
|
executedSteps: 0,
|
|
totalSteps: 0,
|
|
validationRules: validationLogs,
|
|
effectRules: effectLogs,
|
|
triggerRules: triggerLogs,
|
|
triggeredCommands,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* 获取规则日志
|
|
*/
|
|
getLogs(): RuleLogEntry[] {
|
|
return [...this.logs];
|
|
}
|
|
|
|
/**
|
|
* 清除日志
|
|
*/
|
|
clearLogs(): void {
|
|
this.logs = [];
|
|
}
|
|
|
|
/**
|
|
* 获取游戏状态
|
|
*/
|
|
getGameState(): GameState {
|
|
return this.gameState;
|
|
}
|
|
|
|
/**
|
|
* 手动触发规则
|
|
*/
|
|
async triggerRules(): Promise<RuleLogEntry[]> {
|
|
const context: RuleContext = {
|
|
gameState: this.gameState,
|
|
command: { id: 'trigger-manual', name: 'manual-trigger', steps: [] },
|
|
metadata: {},
|
|
};
|
|
|
|
const logs: RuleLogEntry[] = [];
|
|
const triggerRules = this.rules.filter(isTriggerRule);
|
|
|
|
for (const rule of triggerRules) {
|
|
const shouldTrigger = await rule.condition(context);
|
|
if (shouldTrigger) {
|
|
const result = await rule.action(context);
|
|
const logEntry = this.createLogEntry(rule, 'trigger', result, 'manual');
|
|
logs.push(logEntry);
|
|
}
|
|
}
|
|
|
|
return logs;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 创建规则引擎
|
|
*/
|
|
export function createRuleEngine(gameState: GameState, options?: RuleEngineOptions): RuleEngine {
|
|
return new RuleEngine(gameState, options);
|
|
}
|