mm_os
Version:
MM_OS服务端架构,用于快速构建应用程序,支持网站建设、小程序后台、AI应用、物联网(IOT/AIOT)、游戏服务端等多种场景。
1,255 lines (1,125 loc) • 33 kB
JavaScript
const {
Drive
} = require('mm_machine');
/**
* 游戏循环管理器 - 通用增强版
* 支持各种游戏类型,提供灵活的更新策略和可扩展架构
*/
class GameLoop extends Drive {
static config = {
type: 'general',
systems: [],
base: {
max_fps: 60,
min_fps: 20,
adaptive: true,
time_scale: 1.0,
stop_on_error: false,
name: 'GameLoop',
tick_count: 0
},
tick_rates: {
world: 30,
physics: 60,
anim: 30,
ai: 10,
network: 10,
cleanup: 1,
input: 60,
ui: 30,
default: 60
},
sync: {
lockstep: false,
rollback: false,
interp: true,
extrap: false,
timeout: 5000,
retry_count: 3
},
perf: {
monitor: true,
auto_adjust: true,
budget: {
world: 8,
physics: 4,
anim: 3,
ai: 2
},
profile: false
},
events: {
priority: 3,
queue_size: 1000,
max_queue_size: 1000,
immed_proc: false,
max_per_frame: 100
},
phases: {
pre_update: true,
update: true,
post_update: true,
render: true,
cleanup: true
},
// 持久化配置
persist: {
// 是否启用定时持久化
enabled: true,
// 持久化间隔(秒)
interval: 60,
// 是否只保存脏数据
dirty_only: true
},
// 空闲状态配置
idle: {
// 是否启用空闲优化
enabled: true,
// 空闲时目标帧率
fps: 5,
// 进入空闲状态的空闲帧数阈值
threshold: 60,
// 空闲计数器
counter: 0
}
};
/**
* 构造函数
* @param {object} config 配置参数
*/
constructor(config = {}) {
super({ ...GameLoop.config, ...config || {} });
this.current_tick = 0;
// 持久化计数器
this._persist_counter = 0;
this.last_frame_time = 0;
// 初始化性能统计
this.perf_stats = {
frame_times: [],
update_times: {},
fps: 0,
load: 0,
phase_times: {}
};
// 初始化内部对象
this._system = {};
// 事件队列
this._event_queue = {};
this._accrual = {};
this._deps = {};
this._hook = {
pre_update: [],
post_update: [],
pre_render: [],
post_render: [],
pre_tick: [],
post_tick: []
};
// 循环状态
this.loop_status = 'running';
}
}
/**
* 获取模板目录
* @returns {string} 模板目录
*/
GameLoop.prototype.getTplDir = function () {
return __dirname;
};
/**
* 初始化核心
* @param {object} world 游戏世界
* @param {object} eventer 事件总线
* @param {object} logger 日志管理器
* @returns {void}
*/
GameLoop.prototype._initCore = async function (world, eventer, logger) {
if (logger) {
this.setLogger(logger);
}
if (eventer) {
this.getEventer = function () {
return eventer;
};
}
// 初始化依赖项
if (world) {
this._world = world;
this._system = world.system;
}
// 从配置中解构必要参数
let {
type = 'general',
sync = {},
phases = {}
} = this.config;
// 初始化事件队列
for (let i = 0; i < this.config.events.priority; i++) {
this._event_queue[i] = [];
}
// 初始化性能记录
for (let phase of Object.keys(phases)) {
this.perf_stats.phase_times[phase] = [];
}
// 根据游戏类型应用特定优化
this.applyTypeOpts(type, sync);
};
/**
* 游戏类型特定优化
* @param {string} type 游戏类型
* @param {object} sync_config 同步配置
* @returns {void}
*/
GameLoop.prototype.applyTypeOpts = function (type, sync_config) {
// 保存原始配置用于重置
this.original_config = JSON.parse(JSON.stringify(this.config));
// 使用类型映射表处理不同的游戏类型
this._runType(type, sync_config);
};
/**
* 处理游戏类型优化
* @param {string} type 游戏类型
* @param {object} sync_config 同步配置
* @returns {void}
*/
GameLoop.prototype._runType = function (type, sync_config) {
let map = {
comp: () => this.setupComp(sync_config),
mmorpg: () => this.setupMMORPG(),
casual: () => this.setupCasual(),
rts: () => this.setupRTS(sync_config),
sim: () => this.setupSimu(),
card: () => this.setupCard(sync_config),
board: () => this.setupBoard(sync_config),
sandbox: () => this.setupSandbox(),
turn: () => this.setupTurnBased(),
rhythm: () => this.setupRhythm(),
general: () => this.setupGeneral()
};
let handler = map[type];
if (handler) {
handler();
} else {
this.setupGeneral();
}
};
/**
* 设置棋牌游戏循环
* @param {object} sync_config 同步配置
* @returns {void}
*/
GameLoop.prototype.setupBoard = function (sync_config) {
// 棋牌游戏:低频率、确定性更新
this.config.tick_rates.world = 10;
this.config.tick_rates.physics = 10;
this.fixed_time_step = 1 / 10;
// 关闭非必要系统
this.config.tick_rates.anim = 5;
// 棋牌游戏:支持锁步和回滚
if (sync_config.lockstep) {
this.setupLockstep();
}
// 棋牌游戏:支持回滚
if (sync_config.rollback) {
this.setupRollback();
}
// 开启确定性执行
this.determ = true;
};
/**
* 设置卡牌游戏循环
* @param {object} sync_config 同步配置
* @returns {void}
*/
GameLoop.prototype.setupCard = function (sync_config) {
// 卡牌游戏:中等频率、确定性更新
this.config.tick_rates.world = 15;
this.config.tick_rates.physics = 10;
this.fixed_time_step = 1 / 15;
// 关闭非必要系统
this.config.tick_rates.anim = 20;
// 卡牌游戏通常只需要回滚同步
if (sync_config.rollback) {
this.setupRollback();
}
// 卡牌游戏特有:事件优先级调整
this.config.event_priority = true;
// 开启确定性执行
this.determ = true;
};
/**
* 设置沙盒游戏循环
* @returns {void}
*/
GameLoop.prototype.setupSandbox = function () {
// 沙盒游戏:支持高自由度和物理模拟
this.config.tick_rates.world = 30;
this.config.tick_rates.physics = 60;
this.config.tick_rates.ai = 15;
this.fixed_time_step = 1 / 30;
// 沙盒游戏:增强性能适应性
this.config.perf.auto_adjust = true;
// 支持时间缩放
this.config.base.time_scale = 1.0;
};
/**
* 设置回合制游戏循环
* @returns {void}
*/
GameLoop.prototype.setupTurnBased = function () {
// 回合制游戏:非常低的频率,事件驱动
this.config.tick_rates.world = 5;
this.config.tick_rates.physics = 5;
this.config.tick_rates.ai = 5;
this.fixed_time_step = 1 / 5;
// 回合制游戏:更精确的事件处理
this.config.events.immed_proc = true;
// 开启确定性执行
this.determ = true;
};
/**
* 设置音乐节奏游戏循环
* @returns {void}
*/
GameLoop.prototype.setupRhythm = function () {
// 音乐节奏游戏:高频率、高精度
this.config.tick_rates.world = 120;
this.config.tick_rates.physics = 120;
this.config.tick_rates.input = 120;
this.fixed_time_step = 1 / 120;
// 关闭自适应以保证稳定帧率
this.config.base.adaptive = false;
};
/**
* 设置竞技游戏循环
* @param {object} sync_config 同步配置
* @returns {void}
*/
GameLoop.prototype.setupComp = function (sync_config) {
// 竞技游戏:高频率、确定性更新
this.config.tick_rates.physics = 64;
this.config.tick_rates.world = 64;
this.fixed_time_step = 1 / 64;
// 竞技游戏:支持锁步和回滚
if (sync_config.lockstep) {
this.setupLockstep();
}
// 竞技游戏:支持回滚
if (sync_config.rollback) {
this.setupRollback();
}
};
/**
* 设置MMORPG游戏循环
* @returns {void}
*/
GameLoop.prototype.setupMMORPG = function () {
// MMORPG:分层更新,性能优化
this.config.tick_rates.world = 10;
this.config.tick_rates.ai = 2;
this.config.tick_rates.anim = 30;
this.config.perf.auto_adjust = true;
};
/**
* 设置休闲游戏循环
* @returns {void}
*/
GameLoop.prototype.setupCasual = function () {
// 休闲游戏:事件驱动,低频率
this.config.tick_rates.world = 10;
this.config.tick_rates.ai = 5;
this.config.base.adaptive = false;
};
/**
* 设置RTS游戏循环
* @param {object} sync_config 同步配置
* @returns {void}
*/
GameLoop.prototype.setupRTS = function (sync_config) {
// RTS:锁步同步
this.config.tick_rates.world = 16; // ~60Hz
this.config.sync.lockstep = true;
this.cmdBuf = new Map();
};
/**
* 设置模拟游戏循环
* @returns {void}
*/
GameLoop.prototype.setupSimu = function () {
// 模拟游戏:支持时间缩放
this.config.base.time_scale = 1.0;
this.config.tick_rates.world = 20;
this.config.perf.auto_adjust = true;
};
/**
* 设置通用游戏循环
* @returns {void}
*/
GameLoop.prototype.setupGeneral = function () {
// 通用配置
this.config.tick_rates.world = 30;
this.config.tick_rates.physics = 50;
};
/**
* 设置锁步同步
* @returns {void}
*/
GameLoop.prototype.setupLockstep = function () {
// 锁步同步配置
this.config.sync.lockstep = true;
// 锁步需要确定性执行
this.determ = true;
// 初始化锁步状态
this._lockstep = {
turn: 0,
readyPlayers: new Set(),
pendingActions: [],
confirmed: false
};
};
/**
* 设置回滚同步
* @returns {void}
*/
GameLoop.prototype.setupRollback = function () {
// 回滚同步配置
this.config.sync.rollback = true;
// 回滚需要确定性执行
this.determ = true;
// 初始化回滚状态
this._rollback = {
frame: 0,
history: [],
max_history: 60,
predInputs: new Map()
};
};
/**
* 启动游戏循环具体实现
* @private
* @returns {Promise<void>}
*/
GameLoop.prototype._startCore = async function () {
// 初始化时间变量
this.last_frame_time = performance.now();
this.last_second_time = this.last_frame_time;
this.frames_this_second = 0;
// 启动游戏循环
await this.gameLoop();
};
/**
* 停止游戏循环具体实现
* @private
* @returns {Promise<void>}
*/
GameLoop.prototype._stop = async function () {
// 状态由基类stop方法设置,此处只记录日志和执行停止逻辑
this.log('info', '游戏循环已停止');
};
/**
* 错误处理状态设置
* @private
* @returns {void}
*/
GameLoop.prototype._error = function () {
// 错误状态处理,基类没有提供统一管理error状态的方法,所以保留此处设置
this.loop_status = 'error';
this.log('error', '游戏循环进入错误状态');
};
/**
* 执行游戏阶段
* @param {number} delta_time 帧间隔时间
* @returns {Promise<void>}
*/
GameLoop.prototype._execPhases = async function (delta_time) {
let phases = this.config.phases;
let phase_start = 0;
// 预更新阶段
if (phases.pre_update) {
phase_start = performance.now();
await this._runHooks('pre_update', delta_time);
this.recordPhaseTime('pre_update', performance.now() - phase_start);
}
// 更新阶段
if (phases.update) {
phase_start = performance.now();
await this.setSystems(delta_time);
this.recordPhaseTime('update', performance.now() - phase_start);
}
// 后更新阶段
if (phases.post_update) {
phase_start = performance.now();
await this._runHooks('post_update', delta_time);
this.recordPhaseTime('post_update', performance.now() - phase_start);
}
// 渲染阶段
if (phases.render) {
phase_start = performance.now();
await this._runHooks('pre_render', delta_time);
await this._render(delta_time);
await this._runHooks('post_render', delta_time);
this.recordPhaseTime('render', performance.now() - phase_start);
}
// 清理阶段
if (phases.cleanup) {
phase_start = performance.now();
await this._cleanup(delta_time);
this.recordPhaseTime('cleanup', performance.now() - phase_start);
}
// 处理事件队列
await this.runEvents();
this.checkFrameBudget();
};
/**
* 执行钩子函数
* @param {string} hook_name 钩子名称
* @param {number} delta_time 帧间隔时间
* @returns {Promise<void>}
*/
GameLoop.prototype._runHooks = async function (hook_name, delta_time) {
let hooks = this._hook[hook_name] || [];
for (let hook of hooks) {
try {
await hook(delta_time);
} catch (error) {
this.log('error', `钩子 ${hook_name} 执行错误:`, error);
}
}
};
/**
* 渲染处理
* @param {number} delta_time 帧间隔时间
* @returns {Promise<void>}
*/
GameLoop.prototype._render = async function (delta_time) {
if (this._world && this._world.render) {
await this._world.render(delta_time);
}
};
/**
* 清理处理
* @param {number} delta_time 帧间隔时间
* @returns {Promise<void>}
*/
GameLoop.prototype._cleanup = async function (delta_time) {
// 清理无效实体
if (this._world && this._world.cleanup) {
await this._world.cleanup(delta_time);
}
// 定时持久化脏数据
await this._persistEntities(delta_time);
};
/**
* 定时持久化实体
* @param {number} delta_time 帧间隔时间
* @returns {Promise<void>}
*/
GameLoop.prototype._persistEntities = async function (delta_time) {
var persist_config = this.config.persist;
if (!persist_config || !persist_config.enabled) {
return;
}
// 累计时间
this._persist_counter += delta_time;
var interval_ms = persist_config.interval * 1000;
// 检查是否到达持久化间隔
if (this._persist_counter >= interval_ms) {
this._persist_counter = 0;
// 保存脏数据实体
if (this._world && this._world._saveDirtyEntities) {
await this._world._saveDirtyEntities();
}
}
};
/**
* 处理循环错误
* @param {Error} error 错误对象
* @returns {Promise<void>}
*/
GameLoop.prototype._handleLoopError = async function (error) {
if (this.config.base.stop_on_error) {
this.loop_status = 'error';
this._error();
}
};
/**
* 处理暂停状态
* @returns {Promise<void>}
*/
GameLoop.prototype._handlePausedState = async function () {
// 暂停状态下只处理高优先级事件
await this.runHighPriEvents();
// 等待恢复
await this.sleep(100);
// 继续循环
if (this.loop_status === 'paused') {
setImmediate(() => this.gameLoop());
}
};
/**
* 游戏主循环
* @returns {Promise<void>}
*/
GameLoop.prototype.gameLoop = async function () {
if (this.loop_status !== 'running') return;
// 检查暂停状态
if (this.loop_status === 'paused') {
await this._handlePausedState();
return;
}
let frame_start = performance.now();
let delta_time = Math.min(frame_start - this.last_frame_time, 100);
this.last_frame_time = frame_start;
this.startMon();
try {
await this._execPhases(delta_time);
} catch (error) {
await this._handleLoopError(error);
}
this.endMon(frame_start);
// 动态帧率控制
let sleep_time = this.calcSleepTime();
if (sleep_time > 1) {
await this.sleep(sleep_time);
}
if (this.loop_status === 'running') {
setImmediate(() => this.gameLoop());
}
};
/**
* 系统更新
* @param {number} delta_time 帧间隔时间
* @returns {Promise<void>}
*/
GameLoop.prototype.setSystems = async function (delta_time) {
let scaled_delta_time = delta_time * this.config.base.time_scale;
// 获取有序的系统更新列表(考虑依赖关系)
let ordered_systems = this.getOrderedSystems();
// 更新各个系统(根据各自的tick_rate)
for (let [system_name] of ordered_systems) {
let tick_rate = this.config.tick_rates[system_name] || this.config.tick_rates.world;
if (tick_rate <= 0) continue; // 跳过禁用的系统
await this.updateSystem(
system_name,
scaled_delta_time,
tick_rate
);
}
};
/**
* 获取按依赖关系排序的系统列表
* @returns {Array} 有序的系统列表
*/
GameLoop.prototype.getOrderedSystems = function () {
// 如果没有依赖关系,直接返回系统列表
if (Object.keys(this._deps).length === 0) {
return Object.entries(this._system);
}
// 简单的拓扑排序实现
let visited = new Set();
let result = [];
let graph = this._deps;
let visit = (system_name) => {
if (visited.has(system_name)) return;
visited.add(system_name);
// 先访问依赖项
let dependencies = graph[system_name] || [];
for (let dep of dependencies) {
if (this._system[dep]) {
visit(dep);
}
}
if (this._system[system_name]) {
result.push([system_name, this._system[system_name]]);
}
};
// 遍历所有系统
for (let system_name in this._system) {
visit(system_name);
}
return result;
};
/**
* 根据固定帧率更新系统
* @param {string} system_name 系统名称
* @param {number} delta_time 帧间隔时间
* @param {number} tick_rate 帧率
* @returns {Promise<void>}
*/
GameLoop.prototype.updateSystem = async function (system_name, delta_time, tick_rate) {
let system = this._system[system_name];
if (!system || !system.isEnabled()) return;
// 获取累加器并累加帧时间
let acc = (this._accrual[system_name] || 0) + delta_time;
let system_delta = 1000 / tick_rate;
// 执行固定时间步长更新
while (acc >= system_delta) {
try {
let start_time = performance.now();
await system.update(system_delta);
let update_time = performance.now() - start_time;
// 记录性能数据
this.perf_stats.update_times[system_name] = update_time;
acc -= system_delta;
// 更新tick计数
this.config.tick_count++;
} catch (error) {
this.log('error', `系统 ${system_name} 更新错误:`, error);
// 处理系统错误
if (system.critical) {
throw error;
}
break;
}
}
// 更新累加器
this._accrual[system_name] = acc;
};
/**
* 处理事件队列
* @returns {Promise<void>}
*/
GameLoop.prototype.runEvents = async function () {
// 按优先级处理事件
for (let priority = 0; priority < this.config.events.priority; priority++) {
let queue = this._event_queue[priority] || [];
let events_to_process = queue.splice(0, Math.min(queue.length, this.config.events
.max_per_frame)); // 限制每帧处理数量
for (let event of events_to_process) {
try {
await this.handleEvent(event, priority);
} catch (error) {
this.log('error', `事件处理错误 (优先级 ${priority}):`, error);
}
}
}
};
/**
* 仅处理高优先级事件(用于暂停状态)
* @returns {Promise<void>}
*/
GameLoop.prototype.runHighPriEvents = async function () {
let queue = this._event_queue[0] || [];
let events_to_process = queue.splice(0, Math.min(queue.length, 20));
for (let event of events_to_process) {
try {
await this.handleEvent(event, 0);
} catch (error) {
this.log('error', `高优先级事件处理错误 (优先级 0):`, error);
}
}
};
/**
* 处理单个事件
* @param {object} event 事件对象
* @param {number} priority 优先级
* @returns {Promise<void>}
*/
GameLoop.prototype.handleEvent = async function (event, priority) {
// 事件处理逻辑
this.log('debug', `处理事件 (优先级 ${priority}):`, event);
};
/**
* 性能监控开始
* @returns {void}
*/
GameLoop.prototype.startMon = function () {
this.perf_stats.frame_start = performance.now();
};
/**
* 性能监控结束
* @param {number} frame_start 帧开始时间
* @returns {void}
*/
GameLoop.prototype.endMon = function (frame_start) {
let frame_time = performance.now() - frame_start;
// 记录帧时间
this.perf_stats.frame_times.push(frame_time);
if (this.perf_stats.frame_times.length > 60) {
this.perf_stats.frame_times.shift();
}
// 计算FPS
this.frames_this_second++;
let now = performance.now();
if (now - this.last_second_time >= 1000) {
this.perf_stats.fps = this.frames_this_second;
this.frames_this_second = 0;
this.last_second_time = now;
// 计算系统负载
this.calcLoad();
// 触发性能统计更新事件
this.emitEvent('performance_stats_update', {
fps: this.perf_stats.fps,
load: this.perf_stats.load,
frame_time: frame_time,
timestamp: Date.now()
});
// 自适应调整
if (this.config.perf.auto_adjust) {
this.adjustPerf();
}
// 性能分析日志(如果启用)
if (this.config.perf.profile) {
this.logPerf();
}
}
};
/**
* 记录阶段执行时间
* @param {string} phase 阶段名称
* @param {number} time 执行时间(毫秒)
* @returns {void}
*/
GameLoop.prototype.recordPhaseTime = function (phase, time) {
let times = this.perf_stats.phase_times[phase] || [];
times.push(time);
if (times.length > 60) times.shift();
this.perf_stats.phase_times[phase] = times;
};
/**
* 检查帧时间预算
* @returns {void}
*/
GameLoop.prototype.checkFrameBudget = function () {
let target_frame_time = 1000 / this.config.base.max_fps;
let avg_frame_time = this.perf_stats.frame_times.reduce((a, b) => a + b, 0) /
this.perf_stats.frame_times.length;
if (avg_frame_time > target_frame_time * 1.2) {
this.log('warn', `帧时间超出预算: ${avg_frame_time.toFixed(2)}ms > ${target_frame_time.toFixed(2)}ms`);
// 触发帧时间超出预算事件
this.emitEvent('performance_frame_budget_exceeded', {
avg_frame_time: avg_frame_time,
target_frame_time: target_frame_time,
timestamp: Date.now()
});
if (this.config.perf.auto_adjust) {
this.adjustPerf();
}
}
};
/**
* 性能监控日志
* @returns {void}
*/
GameLoop.prototype.logPerf = function () {
// 记录各阶段平均耗时
for (let phase in this.perf_stats.phase_times) {
let times = this.perf_stats.phase_times[phase];
if (times.length > 0) {
let avg_time = times.reduce((a, b) => a + b, 0) / times.length;
this.log('debug', ` ${phase}: ${avg_time.toFixed(2)}ms`);
}
}
};
/**
* 计算系统负载
* @returns {void}
*/
GameLoop.prototype.calcLoad = function () {
let target_frame_time = 1000 / this.config.base.max_fps;
let avg_frame_time = this.perf_stats.frame_times.reduce((a, b) => a + b, 0) /
this.perf_stats.frame_times.length;
this.perf_stats.load = Math.min(avg_frame_time / target_frame_time, 1.0);
};
/**
* 自适应性能调整
* @returns {void}
*/
GameLoop.prototype.adjustPerf = function () {
let load = this.perf_stats.load;
let entity_count = this._world ? this._world.entities.size : 0;
let player_count = this._world ? this._world.getPlayerNum() : 0;
if (load > 0.8) {
// 触发高负载事件
this.emitEvent('performance_high_load', {
load: load,
entity_count: entity_count,
player_count: player_count,
timestamp: Date.now()
});
this.reduceLoad(entity_count, player_count);
} else if (load < 0.5) {
// 触发低负载事件
this.emitEvent('performance_low_load', {
load: load,
entity_count: entity_count,
player_count: player_count,
timestamp: Date.now()
});
this.increaseQuality(entity_count, player_count);
}
};
/**
* 降低性能负载
* @param {number} entity_count 实体数量
* @param {number} player_count 玩家数量
* @returns {void}
*/
GameLoop.prototype.reduceLoad = function (entity_count, player_count) {
if (player_count > 100) {
this.config.tick_rates.ai = Math.max(1, this.config.tick_rates.ai / 2);
this.config.tick_rates.anim = Math.max(10, this.config.tick_rates.anim / 1.5);
this.config.tick_rates.physics = Math.max(30, this.config.tick_rates.physics / 1.2);
} else if (entity_count > 1000) {
this.config.tick_rates.ai = Math.max(1, this.config.tick_rates.ai / 1.5);
this.config.tick_rates.physics = Math.max(30, this.config.tick_rates.physics / 1.2);
} else {
this.config.tick_rates.ai = Math.max(1, this.config.tick_rates.ai / 2);
this.config.tick_rates.anim = Math.max(10, this.config.tick_rates.anim / 1.5);
}
};
/**
* 提高性能质量
* @param {number} entity_count 实体数量
* @param {number} player_count 玩家数量
* @returns {void}
*/
GameLoop.prototype.increaseQuality = function (entity_count, player_count) {
let load = this.perf_stats.load;
if (load < 0.3) {
this.config.tick_rates.ai = Math.min(30, this.config.tick_rates.ai * 1.5);
this.config.tick_rates.anim = Math.min(60, this.config.tick_rates.anim * 1.2);
this.config.tick_rates.physics = Math.min(60, this.config.tick_rates.physics * 1.1);
} else {
this.config.tick_rates.ai = Math.min(30, this.config.tick_rates.ai * 1.2);
this.config.tick_rates.anim = Math.min(60, this.config.tick_rates.anim * 1.1);
}
};
/**
* 休眠函数
* @param {number} ms 休眠时间(毫秒)
* @returns {Promise<void>}
*/
GameLoop.prototype.sleep = function (ms) {
if (typeof ms !== 'number' || ms < 0) {
throw new Error('休眠时间必须是非负数字');
}
return new Promise(resolve => setTimeout(resolve, ms));
};
/**
* 检测是否处于空闲状态
* @returns {boolean} 是否空闲
*/
GameLoop.prototype.isIdle = function () {
if (!this.config.idle.enabled) {
return false;
}
if (!this._world) {
return true;
}
let entity_count = this._world.entities ? this._world.entities.size : 0;
let player_count = this._world._player_ids ? this._world._player_ids.size : 0;
let is_idle = entity_count === 0 && player_count === 0;
if (is_idle) {
this.config.idle.counter++;
if (this.config.idle.counter >= this.config.idle.threshold) {
return true;
}
} else {
this.config.idle.counter = 0;
}
return false;
};
/**
* 计算动态休眠时间
* @returns {number} 休眠时间(毫秒)
*/
GameLoop.prototype.calcSleepTime = function () {
let idle_config = this.config.idle;
let is_idle = this.isIdle();
if (is_idle) {
idle_config.counter++;
if (idle_config.counter >= idle_config.threshold) {
let idle_interval = 1000 / idle_config.fps;
return Math.max(idle_interval, 1);
}
} else {
idle_config.counter = 0;
}
let target_interval = 1000 / this.config.base.max_fps;
let elapsed = performance.now() - this.last_frame_time;
let sleep_time = target_interval - elapsed;
return Math.max(0, sleep_time);
};
/**
* 注册生命周期钩子
* @param {string} hook_name 钩子名称
* @param {Function} callback 回调函数
* @returns {void}
*/
GameLoop.prototype.registerHook = function (hook_name, callback) {
if (!hook_name || typeof hook_name !== 'string') {
throw new Error('钩子名称不能为空且必须是字符串');
}
if (typeof callback !== 'function') {
throw new Error('回调必须是函数');
}
if (!this._hook[hook_name]) {
throw new Error(`未知的钩子类型: ${hook_name}`);
}
this._hook[hook_name].push(callback);
};
/**
* 注销系统
* @param {string} name 系统名称
* @returns {Promise<void>}
*/
GameLoop.prototype.unregSystem = async function (name) {
// 参数验证
if (!name || typeof name !== 'string') {
throw new TypeError('系统名称不能为空且必须是字符串');
}
try {
// 清理系统资源
var system = this._system[name];
if (system && typeof system.cleanup === 'function') {
await system.cleanup();
}
// 删除系统引用
delete this._system[name];
delete this._accrual[name];
delete this.perf_stats.update_times[name];
delete this._deps[name];
this.log('info', `系统已注销: ${name}`);
} catch (error) {
this.log('error', `注销系统 ${name} 时发生错误`, error);
throw error;
}
};
/**
* 清理所有资源
* @returns {Promise<void>}
*/
GameLoop.prototype.cleanupAll = async function () {
this.log('info', '开始清理所有游戏循环资源');
var cleanup_promises = [];
// 清理所有系统
for (var system_name in this._system) {
var system = this._system[system_name];
if (system && typeof system.cleanup === 'function') {
var promise = system.cleanup().catch(error => {
this.log('error', `清理系统 ${system_name} 时发生错误`, error);
});
cleanup_promises.push(promise);
}
}
// 等待所有清理操作完成
await Promise.all(cleanup_promises);
// 清空所有数据结构
this._system = {};
this._accrual = {};
this._event_queue = {};
this._deps = {};
this.perf_stats = {
frame_times: [],
update_times: {},
frame_start: 0
};
this.log('info', '所有游戏循环资源已清理完成');
};
/**
* 记录系统性能
* @param {string} system_name 系统名称
* @param {number} time 执行时间
* @returns {void}
*/
GameLoop.prototype.recordPerf = function (system_name, time) {
let times = this.perf_stats.update_times[system_name] || [];
times.push(time);
if (times.length > 60) times.shift();
this.perf_stats.update_times[system_name] = times;
};
/**
* 处理游戏循环错误
* @param {Error} error 错误对象
* @returns {void}
*/
GameLoop.prototype.runError = function (error) {
// 设置错误状态
this._error();
// 错误处理策略
if (this.config.perf?.auto_adjust) {
this.reduceLoad();
}
// 根据配置决定是否停止游戏循环
if (this.config.base?.stop_on_error) {
this.log('warn', '游戏循环停止 (stop_on_error=true)');
this._stop();
}
// 记录错误日志
this.log('error', '游戏循环错误处理:', error);
};
/**
* 获取性能统计
* @returns {object} 性能统计数据
*/
GameLoop.prototype.getStats = function () {
return {
fps: this.perf_stats.fps,
load: this.perf_stats.load,
sys_perf: { ...this.perf_stats.update_times },
frame_times: [...this.perf_stats.frame_times],
current_tick: this.current_tick
};
};
/**
* 动态配置更新
* @param {object} new_config 新配置
* @returns {void}
*/
GameLoop.prototype.setConfig = function (new_config) {
if (!new_config || typeof new_config !== 'object') {
throw new Error('新配置必须是对象');
}
$.push(this.config, new_config, true);
};
/**
* 重置配置为初始状态
* @returns {void}
*/
GameLoop.prototype.resetConfig = function () {
if (this.original_config) {
this.config = JSON.parse(JSON.stringify(this.original_config));
this._init();
}
};
/**
* 设置游戏暂停状态
* @param {boolean} paused 是否暂停
* @returns {void}
*/
GameLoop.prototype.setPaused = function (paused) {
if (typeof paused !== 'boolean') {
throw new Error('暂停状态必须是布尔值');
}
let old_status = this.loop_status;
this.loop_status = paused ? 'paused' : (old_status === 'error' ? 'error' : 'running');
this.log('info', `游戏循环 ${paused ? '暂停' : '恢复'}`);
};
/**
* 清理方法(定期执行)
* @returns {Promise<void>}
*/
GameLoop.prototype.cleanup = async function () {
// 清理过期事件队列
for (let priority in this._event_queue) {
let queue = this._event_queue[priority];
if (queue.length > this.config.events.queue_size * 0.8) {
this.log('warn', `事件队列 (优先级 ${priority}) 几乎已满,正在清理...`);
// 只保留高优先级事件
this._event_queue[priority] = queue.filter(event => event && event.persistent);
}
}
// 执行各系统的清理方法
for (let system_name in this._system) {
let system = this._system[system_name];
if (typeof system.cleanup === 'function') {
try {
await system.cleanup();
} catch (error) {
this.log('error', `系统 ${system_name} 清理失败:`, error);
}
}
}
};
exports.GameLoop = GameLoop;