mm_os 3.3.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (380) hide show
  1. package/LICENSE +21 -201
  2. package/README.md +491 -99
  3. package/README_EN.md +498 -0
  4. package/adapter/adapter.js +431 -0
  5. package/adapter/custom_persistence.js +660 -0
  6. package/adapter/mqtt.js +273 -0
  7. package/adapter/socket.js +113 -0
  8. package/adapter/web.js +67 -0
  9. package/adapter/websocket.js +146 -0
  10. package/com/api/com.json +5 -0
  11. package/{core/com → com}/api/config.tpl.json +8 -8
  12. package/com/api/drive.js +708 -0
  13. package/com/api/index.js +198 -0
  14. package/com/api/oauth.js +200 -0
  15. package/com/api/script.tpl.js +32 -0
  16. package/com/cmd/README.md +11 -0
  17. package/com/cmd/com.json +5 -0
  18. package/com/cmd/config.tpl.json +122 -0
  19. package/com/cmd/drive.js +1548 -0
  20. package/com/cmd/index.js +1066 -0
  21. package/com/cmd/msg.json +48 -0
  22. package/com/cmd/nlp.js +525 -0
  23. package/com/cmd/script.tpl.js +32 -0
  24. package/com/db/com.json +5 -0
  25. package/com/db/drive.js +1999 -0
  26. package/com/db/index.js +242 -0
  27. package/com/event/com.json +5 -0
  28. package/{core/com → com}/event/config.tpl.json +8 -8
  29. package/com/event/drive.js +59 -0
  30. package/com/event/index.js +409 -0
  31. package/com/event/script.tpl.js +23 -0
  32. package/com/mqtt/com.json +5 -0
  33. package/{core/com → com}/mqtt/config.tpl.json +3 -5
  34. package/com/mqtt/drive.js +676 -0
  35. package/com/mqtt/index.js +822 -0
  36. package/com/mqtt/mm_mqtt.js +425 -0
  37. package/com/mqtt/script.tpl.js +723 -0
  38. package/com/nav/com.json +5 -0
  39. package/com/nav/config.tpl.json +84 -0
  40. package/com/nav/drive.js +702 -0
  41. package/com/nav/index.js +231 -0
  42. package/{core/com → com}/nav/tpl/admin_pc/page_config.vue +280 -280
  43. package/{core/com → com}/nav/tpl/admin_pc/page_config_form.vue +194 -194
  44. package/com/nav/tpl/admin_pc/page_form.vue +180 -0
  45. package/com/nav/tpl/admin_pc/page_view.vue +124 -0
  46. package/com/nav/tpl/dev_pc/page_default.vue +247 -0
  47. package/com/nav/tpl/dev_pc/page_type.vue +313 -0
  48. package/com/nav/tpl/home_pc/page_default.vue +234 -0
  49. package/com/nav/tpl/home_pc/page_form.vue +137 -0
  50. package/com/nav/tpl/home_pc/page_list.vue +234 -0
  51. package/com/nav/tpl/home_pc/page_nav.vue +221 -0
  52. package/com/nav/tpl/home_pc/page_type.vue +234 -0
  53. package/com/nav/tpl/home_pc/page_view.vue +125 -0
  54. package/com/nav/tpl/home_phone/page_channel.vue +234 -0
  55. package/com/nav/tpl/home_phone/page_default.vue +234 -0
  56. package/com/nav/tpl/home_phone/page_form.vue +137 -0
  57. package/com/nav/tpl/home_phone/page_nav.vue +237 -0
  58. package/com/nav/tpl/home_phone/page_type.vue +234 -0
  59. package/com/nav/tpl/home_phone/page_view.vue +125 -0
  60. package/com/nav/viewmodel.js +446 -0
  61. package/com/param/com.json +5 -0
  62. package/{core/com → com}/param/config.tpl.json +7 -1
  63. package/com/param/drive.js +502 -0
  64. package/com/param/index.js +155 -0
  65. package/com/param/script.tpl.js +12 -0
  66. package/com/pendant/com.json +5 -0
  67. package/{core/com/component → com/pendant}/config.tpl.json +15 -13
  68. package/com/pendant/drive.js +204 -0
  69. package/com/pendant/index.js +441 -0
  70. package/com/pendant/pendant.html +16 -0
  71. package/com/pendant/script.tpl.js +18 -0
  72. package/com/socket/com.json +5 -0
  73. package/com/socket/config.tpl.json +12 -0
  74. package/com/socket/drive.js +651 -0
  75. package/com/socket/index.js +351 -0
  76. package/com/socket/script.tpl.js +41 -0
  77. package/com/sql/com.json +5 -0
  78. package/{core/com → com}/sql/config.tpl.json +13 -9
  79. package/com/sql/drive.js +1259 -0
  80. package/com/sql/index.js +150 -0
  81. package/com/sql/script.tpl.js +47 -0
  82. package/com/static/com.json +5 -0
  83. package/{core/com → com}/static/config.tpl.json +10 -6
  84. package/com/static/drive.js +194 -0
  85. package/com/static/index.js +226 -0
  86. package/com/static/script.tpl.js +28 -0
  87. package/com/task/com.json +5 -0
  88. package/{core/com → com}/task/config.tpl.json +4 -6
  89. package/com/task/drive.js +405 -0
  90. package/com/task/index.js +148 -0
  91. package/com/task/script.tpl.js +37 -0
  92. package/com/template/com.json +5 -0
  93. package/com/template/config.tpl.json +16 -0
  94. package/com/template/drive.js +80 -0
  95. package/com/template/index.js +141 -0
  96. package/com.js +156 -0
  97. package/common/README.md +2 -0
  98. package/common/handler/msg/handler.json +22 -0
  99. package/common/handler/msg/index.js +23 -0
  100. package/common/handler/player/handler.json +22 -0
  101. package/common/handler/player/index.js +287 -0
  102. package/common/handler/user/handler.json +22 -0
  103. package/common/handler/user/index.js +23 -0
  104. package/common/middleware/web_after/index.js +29 -0
  105. package/common/middleware/web_after/middleware.json +9 -0
  106. package/common/middleware/web_base/index.js +113 -0
  107. package/common/middleware/web_base/middleware.json +19 -0
  108. package/common/middleware/web_before/index.js +33 -0
  109. package/common/middleware/web_before/middleware.json +9 -0
  110. package/common/middleware/web_cors/index.js +87 -0
  111. package/common/middleware/web_cors/middleware.json +24 -0
  112. package/common/middleware/web_error/index.js +119 -0
  113. package/common/middleware/web_error/middleware.json +18 -0
  114. package/common/middleware/web_ip/index.js +15 -0
  115. package/common/middleware/web_ip/middleware.json +14 -0
  116. package/common/middleware/web_logger/index.js +156 -0
  117. package/common/middleware/web_logger/middleware.json +14 -0
  118. package/common/middleware/web_main/index.js +24 -0
  119. package/common/middleware/web_main/middleware.json +9 -0
  120. package/common/middleware/web_static/index.js +73 -0
  121. package/common/middleware/web_static/middleware.json +54 -0
  122. package/common/middleware/web_waf/index.js +385 -0
  123. package/common/middleware/web_waf/middleware.json +13 -0
  124. package/common/model/msg/index.js +88 -0
  125. package/common/model/msg/model.json +401 -0
  126. package/common/model/player/index.js +63 -0
  127. package/common/model/player/model.json +185 -0
  128. package/common/model/user/index.js +11 -0
  129. package/common/model/user/model.json +219 -0
  130. package/core/app/config.tpl.json +67 -0
  131. package/core/app/index.js +632 -0
  132. package/core/app/script.tpl.js +52 -0
  133. package/core/channel/index.js +899 -0
  134. package/core/channel/matcher.js +585 -0
  135. package/core/com/config.tpl.json +16 -0
  136. package/core/com/index.js +74 -0
  137. package/core/com/script.tpl.js +5 -0
  138. package/core/component/component.js +42 -0
  139. package/core/component/config.tpl.json +63 -0
  140. package/core/component/index.js +273 -0
  141. package/core/component/script.tpl.js +19 -0
  142. package/core/controller/config.tpl.json +14 -0
  143. package/core/controller/index.js +373 -0
  144. package/core/controller/script.tpl.js +27 -0
  145. package/core/factory/config.tpl.json +14 -0
  146. package/core/factory/entity.js +275 -0
  147. package/core/factory/index.js +241 -0
  148. package/core/factory/script.tpl.js +16 -0
  149. package/core/game/bat/index.js +137 -0
  150. package/core/game/bat/world.js +622 -0
  151. package/core/game/config.tpl.json +16 -0
  152. package/core/game/entity_admin.js +230 -0
  153. package/core/game/index.js +186 -0
  154. package/core/handler/config.tpl.json +22 -0
  155. package/core/handler/index.js +181 -0
  156. package/core/handler/script.tpl.js +23 -0
  157. package/core/logic/config.tpl.json +14 -0
  158. package/core/logic/index.js +59 -0
  159. package/core/logic/script.tpl.js +19 -0
  160. package/core/middleware/config.tpl.json +16 -0
  161. package/core/middleware/index.js +125 -0
  162. package/core/middleware/script.tpl.js +37 -0
  163. package/core/mod/config.tpl.json +22 -0
  164. package/core/mod/index.js +130 -0
  165. package/core/mod/script.tpl.js +34 -0
  166. package/core/model/config.tpl.json +219 -0
  167. package/core/model/index.js +272 -0
  168. package/core/model/model.js +27 -0
  169. package/core/model/script.tpl.js +20 -0
  170. package/core/notifier/config.tpl.json +14 -0
  171. package/core/notifier/index.js +77 -0
  172. package/core/notifier/script.tpl.js +20 -0
  173. package/core/plugin/config.tpl.json +24 -0
  174. package/core/plugin/index.js +232 -0
  175. package/core/plugin/script.tpl.js +51 -0
  176. package/core/pusher/config.tpl.json +14 -0
  177. package/core/pusher/index.js +161 -0
  178. package/core/pusher/script.tpl.js +20 -0
  179. package/core/room/bat/index.js +170 -0
  180. package/core/room/bat/room.js +524 -0
  181. package/core/room/config.tpl.json +20 -0
  182. package/core/room/index.js +249 -0
  183. package/core/room/room.js +61 -0
  184. package/core/scene/config.tpl.json +14 -0
  185. package/core/scene/index.js +466 -0
  186. package/core/scene/loop.js +1255 -0
  187. package/core/scene/map.js +28 -0
  188. package/core/scene/script.tpl.js +22 -0
  189. package/core/sender/config.tpl.json +14 -0
  190. package/core/sender/index.js +79 -0
  191. package/core/sender/script.tpl.js +20 -0
  192. package/core/service/config.tpl.json +14 -0
  193. package/core/service/index.js +100 -0
  194. package/core/service/script.tpl.js +25 -0
  195. package/core/store/config.tpl.json +26 -0
  196. package/core/store/index.js +1755 -0
  197. package/core/store/script.tpl.js +22 -0
  198. package/core/store/sql.js +1464 -0
  199. package/core/system/config.tpl.json +18 -0
  200. package/core/system/index.js +312 -0
  201. package/core/system/script.tpl.js +77 -0
  202. package/core/view/config.tpl.json +14 -0
  203. package/core/view/index.js +91 -0
  204. package/core/view/script.tpl.js +20 -0
  205. package/core/zone/bat/index.js +725 -0
  206. package/core/zone/config.tpl.json +54 -0
  207. package/core/zone/index.js +614 -0
  208. package/core/zone/script.tpl.js +10 -0
  209. package/core/zone/zone_bat.js +136 -0
  210. package/core//345/237/272/347/261/273/346/250/241/345/235/227/346/270/205/345/215/225.md +24 -0
  211. package/index.js +17 -314
  212. package/os.js +57 -0
  213. package/package.json +60 -58
  214. package/server.js +598 -0
  215. package/README.en.md +0 -36
  216. package/conf.json +0 -3
  217. package/core/base/mqtt/index.js +0 -1107
  218. package/core/base/mqtt/lib.js +0 -40
  219. package/core/base/web/index.js +0 -243
  220. package/core/com/api/com.json +0 -4
  221. package/core/com/api/drive.js +0 -668
  222. package/core/com/api/index.js +0 -108
  223. package/core/com/api/oauth.js +0 -158
  224. package/core/com/api/script.js +0 -32
  225. package/core/com/app/README.md +0 -3
  226. package/core/com/app/com.json +0 -4
  227. package/core/com/app/config.tpl.json +0 -16
  228. package/core/com/app/drive.js +0 -309
  229. package/core/com/app/index.js +0 -211
  230. package/core/com/app/script.js +0 -155
  231. package/core/com/cmd/com.json +0 -4
  232. package/core/com/cmd/config.tpl.json +0 -66
  233. package/core/com/cmd/drive.js +0 -513
  234. package/core/com/cmd/index.js +0 -354
  235. package/core/com/cmd/old/5w2h.js +0 -54
  236. package/core/com/cmd/old/drive.js +0 -423
  237. package/core/com/cmd/script.js +0 -11
  238. package/core/com/component/README.md +0 -3
  239. package/core/com/component/com.json +0 -4
  240. package/core/com/component/component.html +0 -16
  241. package/core/com/component/drive.js +0 -197
  242. package/core/com/component/index.js +0 -312
  243. package/core/com/component/script.js +0 -18
  244. package/core/com/db/com.json +0 -4
  245. package/core/com/db/drive.js +0 -1160
  246. package/core/com/db/index.js +0 -176
  247. package/core/com/event/com.json +0 -4
  248. package/core/com/event/drive.js +0 -133
  249. package/core/com/event/index.js +0 -345
  250. package/core/com/event/script.js +0 -26
  251. package/core/com/eventer/com.js +0 -477
  252. package/core/com/eventer/com.json +0 -4
  253. package/core/com/middleware/com.js +0 -153
  254. package/core/com/middleware/com.json +0 -4
  255. package/core/com/middleware/config.tpl.json +0 -8
  256. package/core/com/middleware/script.js +0 -9
  257. package/core/com/mqtt/com.json +0 -4
  258. package/core/com/mqtt/drive.js +0 -600
  259. package/core/com/mqtt/index.js +0 -572
  260. package/core/com/mqtt/mm_mqtt.js +0 -330
  261. package/core/com/mqtt/script.js +0 -604
  262. package/core/com/msg/com.js +0 -296
  263. package/core/com/msg/com.json +0 -4
  264. package/core/com/nav/com.json +0 -4
  265. package/core/com/nav/config.tpl.json +0 -75
  266. package/core/com/nav/drive.js +0 -549
  267. package/core/com/nav/index.js +0 -182
  268. package/core/com/nav/tpl/admin_pc/page_form.vue +0 -180
  269. package/core/com/nav/tpl/admin_pc/page_view.vue +0 -124
  270. package/core/com/nav/tpl/dev_pc/page_default.vue +0 -247
  271. package/core/com/nav/tpl/dev_pc/page_type.vue +0 -313
  272. package/core/com/nav/tpl/home_pc/page_default.vue +0 -234
  273. package/core/com/nav/tpl/home_pc/page_form.vue +0 -137
  274. package/core/com/nav/tpl/home_pc/page_list.vue +0 -234
  275. package/core/com/nav/tpl/home_pc/page_nav.vue +0 -221
  276. package/core/com/nav/tpl/home_pc/page_type.vue +0 -234
  277. package/core/com/nav/tpl/home_pc/page_view.vue +0 -125
  278. package/core/com/nav/tpl/home_phone/page_channel.vue +0 -234
  279. package/core/com/nav/tpl/home_phone/page_default.vue +0 -234
  280. package/core/com/nav/tpl/home_phone/page_form.vue +0 -137
  281. package/core/com/nav/tpl/home_phone/page_nav.vue +0 -237
  282. package/core/com/nav/tpl/home_phone/page_type.vue +0 -234
  283. package/core/com/nav/tpl/home_phone/page_view.vue +0 -125
  284. package/core/com/nav/viewmodel.js +0 -296
  285. package/core/com/param/drive.js +0 -366
  286. package/core/com/param/index.js +0 -80
  287. package/core/com/param/script.js +0 -12
  288. package/core/com/param/test.js +0 -98
  289. package/core/com/plugin/README.md +0 -3
  290. package/core/com/plugin/com.json +0 -4
  291. package/core/com/plugin/config.tpl.json +0 -26
  292. package/core/com/plugin/drive.js +0 -536
  293. package/core/com/plugin/index.js +0 -259
  294. package/core/com/plugin/script.js +0 -213
  295. package/core/com/rpc/com.json +0 -4
  296. package/core/com/rpc/drive.js +0 -160
  297. package/core/com/rpc/index.js +0 -87
  298. package/core/com/rpc/rpc.js +0 -118
  299. package/core/com/socket/com.json +0 -4
  300. package/core/com/socket/config.tpl.json +0 -14
  301. package/core/com/socket/drive.js +0 -403
  302. package/core/com/socket/index.js +0 -62
  303. package/core/com/socket/script.js +0 -42
  304. package/core/com/sql/drive.js +0 -1087
  305. package/core/com/sql/index.js +0 -83
  306. package/core/com/sql/script.js +0 -48
  307. package/core/com/static/com.json +0 -4
  308. package/core/com/static/drive.js +0 -220
  309. package/core/com/static/index.js +0 -149
  310. package/core/com/static/script.js +0 -28
  311. package/core/com/task/com.json +0 -4
  312. package/core/com/task/drive.js +0 -403
  313. package/core/com/task/index.js +0 -110
  314. package/core/com/task/script.js +0 -37
  315. package/core/com/timer/com.js +0 -217
  316. package/core/com/timer/com.json +0 -4
  317. package/core/com/tpl/com.js +0 -19
  318. package/core/com/tpl/com.json +0 -4
  319. package/lib/actions.js +0 -50
  320. package/lib/base.js +0 -361
  321. package/lib/com.js +0 -29
  322. package/lib/ref.js +0 -121
  323. package/middleware/mqtt_base/index.js +0 -10
  324. package/middleware/mqtt_base/middleware.json +0 -10
  325. package/middleware/performance/index.js +0 -151
  326. package/middleware/performance/middleware.json +0 -16
  327. package/middleware/security_audit/index.js +0 -549
  328. package/middleware/security_audit/middleware.json +0 -48
  329. package/middleware/security_headers/index.js +0 -487
  330. package/middleware/security_headers/middleware.json +0 -45
  331. package/middleware/waf/index.js +0 -348
  332. package/middleware/waf/middleware.json +0 -10
  333. package/middleware/waf_ddos/index.js +0 -520
  334. package/middleware/waf_ddos/middleware.json +0 -38
  335. package/middleware/waf_ip/index.js +0 -379
  336. package/middleware/waf_ip/middleware.json +0 -49
  337. package/middleware/waf_xss/index.js +0 -269
  338. package/middleware/waf_xss/middleware.json +0 -18
  339. package/middleware/web_after/index.js +0 -33
  340. package/middleware/web_after/middleware.json +0 -9
  341. package/middleware/web_base/index.js +0 -90
  342. package/middleware/web_base/middleware.json +0 -9
  343. package/middleware/web_before/index.js +0 -27
  344. package/middleware/web_before/middleware.json +0 -9
  345. package/middleware/web_check/index.js +0 -28
  346. package/middleware/web_check/middleware.json +0 -9
  347. package/middleware/web_main/index.js +0 -28
  348. package/middleware/web_main/middleware.json +0 -9
  349. package/middleware/web_proxy/index.js +0 -37
  350. package/middleware/web_proxy/middleware.json +0 -9
  351. package/middleware/web_render/index.js +0 -87
  352. package/middleware/web_render/middleware.json +0 -9
  353. package/middleware/web_socket/index.js +0 -34
  354. package/middleware/web_socket/middleware.json +0 -9
  355. package/middleware/web_static/index.js +0 -115
  356. package/middleware/web_static/middleware.json +0 -9
  357. /package/{core/com → com}/api/README.md +0 -0
  358. /package/{core/com → com}/db/README.md +0 -0
  359. /package/{core/com → com}/event/README.md +0 -0
  360. /package/{core/com → com}/mqtt/README.md +0 -0
  361. /package/{core/com → com}/nav/README.md +0 -0
  362. /package/{core/com → com}/nav/tpl/admin_pc/page_channel.vue +0 -0
  363. /package/{core/com → com}/nav/tpl/admin_pc/page_default.vue +0 -0
  364. /package/{core/com → com}/nav/tpl/admin_pc/page_lang.vue +0 -0
  365. /package/{core/com → com}/nav/tpl/admin_pc/page_nav.vue +0 -0
  366. /package/{core/com → com}/nav/tpl/admin_pc/page_table.vue +0 -0
  367. /package/{core/com → com}/nav/tpl/admin_pc/page_type.vue +0 -0
  368. /package/{core/com → com}/nav/tpl/dev_pc/page_channel.vue +0 -0
  369. /package/{core/com → com}/nav/tpl/dev_pc/page_config.vue +0 -0
  370. /package/{core/com → com}/nav/tpl/dev_pc/page_form.vue +0 -0
  371. /package/{core/com → com}/nav/tpl/dev_pc/page_nav.vue +0 -0
  372. /package/{core/com → com}/nav/tpl/dev_pc/page_table.vue +0 -0
  373. /package/{core/com → com}/nav/tpl/home_pc/page_channel.vue +0 -0
  374. /package/{core/com → com}/nav/tpl/home_phone/page_list.vue +0 -0
  375. /package/{core/com → com}/param/README.md +0 -0
  376. /package/{core/com/cmd → com/pendant}/README.md +0 -0
  377. /package/{core/com → com}/socket/README.md +0 -0
  378. /package/{core/com → com}/sql/README.md +0 -0
  379. /package/{core/com → com}/static/README.md +0 -0
  380. /package/{core/com → com}/task/README.md +0 -0
@@ -0,0 +1,1255 @@
1
+ const {
2
+ Drive
3
+ } = require('mm_machine');
4
+
5
+ /**
6
+ * 游戏循环管理器 - 通用增强版
7
+ * 支持各种游戏类型,提供灵活的更新策略和可扩展架构
8
+ */
9
+ class GameLoop extends Drive {
10
+ static config = {
11
+ type: 'general',
12
+ systems: [],
13
+ base: {
14
+ max_fps: 60,
15
+ min_fps: 20,
16
+ adaptive: true,
17
+ time_scale: 1.0,
18
+ stop_on_error: false,
19
+ name: 'GameLoop',
20
+ tick_count: 0
21
+ },
22
+ tick_rates: {
23
+ world: 30,
24
+ physics: 60,
25
+ anim: 30,
26
+ ai: 10,
27
+ network: 10,
28
+ cleanup: 1,
29
+ input: 60,
30
+ ui: 30,
31
+ default: 60
32
+ },
33
+ sync: {
34
+ lockstep: false,
35
+ rollback: false,
36
+ interp: true,
37
+ extrap: false,
38
+ timeout: 5000,
39
+ retry_count: 3
40
+ },
41
+ perf: {
42
+ monitor: true,
43
+ auto_adjust: true,
44
+ budget: {
45
+ world: 8,
46
+ physics: 4,
47
+ anim: 3,
48
+ ai: 2
49
+ },
50
+ profile: false
51
+ },
52
+ events: {
53
+ priority: 3,
54
+ queue_size: 1000,
55
+ max_queue_size: 1000,
56
+ immed_proc: false,
57
+ max_per_frame: 100
58
+ },
59
+ phases: {
60
+ pre_update: true,
61
+ update: true,
62
+ post_update: true,
63
+ render: true,
64
+ cleanup: true
65
+ },
66
+ // 持久化配置
67
+ persist: {
68
+ // 是否启用定时持久化
69
+ enabled: true,
70
+ // 持久化间隔(秒)
71
+ interval: 60,
72
+ // 是否只保存脏数据
73
+ dirty_only: true
74
+ },
75
+ // 空闲状态配置
76
+ idle: {
77
+ // 是否启用空闲优化
78
+ enabled: true,
79
+ // 空闲时目标帧率
80
+ fps: 5,
81
+ // 进入空闲状态的空闲帧数阈值
82
+ threshold: 60,
83
+ // 空闲计数器
84
+ counter: 0
85
+ }
86
+ };
87
+ /**
88
+ * 构造函数
89
+ * @param {object} config 配置参数
90
+ */
91
+ constructor(config = {}) {
92
+ super({ ...GameLoop.config, ...config || {} });
93
+
94
+ this.current_tick = 0;
95
+ // 持久化计数器
96
+ this._persist_counter = 0;
97
+ this.last_frame_time = 0;
98
+
99
+ // 初始化性能统计
100
+ this.perf_stats = {
101
+ frame_times: [],
102
+ update_times: {},
103
+ fps: 0,
104
+ load: 0,
105
+ phase_times: {}
106
+ };
107
+
108
+ // 初始化内部对象
109
+ this._system = {};
110
+ // 事件队列
111
+ this._event_queue = {};
112
+
113
+ this._accrual = {};
114
+ this._deps = {};
115
+ this._hook = {
116
+ pre_update: [],
117
+ post_update: [],
118
+ pre_render: [],
119
+ post_render: [],
120
+ pre_tick: [],
121
+ post_tick: []
122
+ };
123
+ // 循环状态
124
+ this.loop_status = 'running';
125
+ }
126
+ }
127
+
128
+ /**
129
+ * 获取模板目录
130
+ * @returns {string} 模板目录
131
+ */
132
+ GameLoop.prototype.getTplDir = function () {
133
+ return __dirname;
134
+ };
135
+
136
+ /**
137
+ * 初始化核心
138
+ * @param {object} world 游戏世界
139
+ * @param {object} eventer 事件总线
140
+ * @param {object} logger 日志管理器
141
+ * @returns {void}
142
+ */
143
+ GameLoop.prototype._initCore = async function (world, eventer, logger) {
144
+ if (logger) {
145
+ this.setLogger(logger);
146
+ }
147
+ if (eventer) {
148
+ this.getEventer = function () {
149
+ return eventer;
150
+ };
151
+ }
152
+ // 初始化依赖项
153
+ if (world) {
154
+ this._world = world;
155
+ this._system = world.system;
156
+ }
157
+
158
+ // 从配置中解构必要参数
159
+ let {
160
+ type = 'general',
161
+ sync = {},
162
+ phases = {}
163
+ } = this.config;
164
+
165
+
166
+ // 初始化事件队列
167
+ for (let i = 0; i < this.config.events.priority; i++) {
168
+ this._event_queue[i] = [];
169
+ }
170
+
171
+ // 初始化性能记录
172
+ for (let phase of Object.keys(phases)) {
173
+ this.perf_stats.phase_times[phase] = [];
174
+ }
175
+
176
+ // 根据游戏类型应用特定优化
177
+ this.applyTypeOpts(type, sync);
178
+ };
179
+
180
+ /**
181
+ * 游戏类型特定优化
182
+ * @param {string} type 游戏类型
183
+ * @param {object} sync_config 同步配置
184
+ * @returns {void}
185
+ */
186
+ GameLoop.prototype.applyTypeOpts = function (type, sync_config) {
187
+ // 保存原始配置用于重置
188
+ this.original_config = JSON.parse(JSON.stringify(this.config));
189
+
190
+ // 使用类型映射表处理不同的游戏类型
191
+ this._runType(type, sync_config);
192
+ };
193
+
194
+ /**
195
+ * 处理游戏类型优化
196
+ * @param {string} type 游戏类型
197
+ * @param {object} sync_config 同步配置
198
+ * @returns {void}
199
+ */
200
+ GameLoop.prototype._runType = function (type, sync_config) {
201
+ let map = {
202
+ comp: () => this.setupComp(sync_config),
203
+ mmorpg: () => this.setupMMORPG(),
204
+ casual: () => this.setupCasual(),
205
+ rts: () => this.setupRTS(sync_config),
206
+ sim: () => this.setupSimu(),
207
+ card: () => this.setupCard(sync_config),
208
+ board: () => this.setupBoard(sync_config),
209
+ sandbox: () => this.setupSandbox(),
210
+ turn: () => this.setupTurnBased(),
211
+ rhythm: () => this.setupRhythm(),
212
+ general: () => this.setupGeneral()
213
+ };
214
+
215
+ let handler = map[type];
216
+ if (handler) {
217
+ handler();
218
+ } else {
219
+ this.setupGeneral();
220
+ }
221
+ };
222
+
223
+ /**
224
+ * 设置棋牌游戏循环
225
+ * @param {object} sync_config 同步配置
226
+ * @returns {void}
227
+ */
228
+ GameLoop.prototype.setupBoard = function (sync_config) {
229
+ // 棋牌游戏:低频率、确定性更新
230
+ this.config.tick_rates.world = 10;
231
+ this.config.tick_rates.physics = 10;
232
+ this.fixed_time_step = 1 / 10;
233
+ // 关闭非必要系统
234
+ this.config.tick_rates.anim = 5;
235
+ // 棋牌游戏:支持锁步和回滚
236
+ if (sync_config.lockstep) {
237
+ this.setupLockstep();
238
+ }
239
+ // 棋牌游戏:支持回滚
240
+ if (sync_config.rollback) {
241
+ this.setupRollback();
242
+ }
243
+ // 开启确定性执行
244
+ this.determ = true;
245
+ };
246
+
247
+ /**
248
+ * 设置卡牌游戏循环
249
+ * @param {object} sync_config 同步配置
250
+ * @returns {void}
251
+ */
252
+ GameLoop.prototype.setupCard = function (sync_config) {
253
+ // 卡牌游戏:中等频率、确定性更新
254
+ this.config.tick_rates.world = 15;
255
+ this.config.tick_rates.physics = 10;
256
+ this.fixed_time_step = 1 / 15;
257
+ // 关闭非必要系统
258
+ this.config.tick_rates.anim = 20;
259
+ // 卡牌游戏通常只需要回滚同步
260
+ if (sync_config.rollback) {
261
+ this.setupRollback();
262
+ }
263
+ // 卡牌游戏特有:事件优先级调整
264
+ this.config.event_priority = true;
265
+ // 开启确定性执行
266
+ this.determ = true;
267
+ };
268
+
269
+ /**
270
+ * 设置沙盒游戏循环
271
+ * @returns {void}
272
+ */
273
+ GameLoop.prototype.setupSandbox = function () {
274
+ // 沙盒游戏:支持高自由度和物理模拟
275
+ this.config.tick_rates.world = 30;
276
+ this.config.tick_rates.physics = 60;
277
+ this.config.tick_rates.ai = 15;
278
+ this.fixed_time_step = 1 / 30;
279
+ // 沙盒游戏:增强性能适应性
280
+ this.config.perf.auto_adjust = true;
281
+ // 支持时间缩放
282
+ this.config.base.time_scale = 1.0;
283
+ };
284
+
285
+ /**
286
+ * 设置回合制游戏循环
287
+ * @returns {void}
288
+ */
289
+ GameLoop.prototype.setupTurnBased = function () {
290
+ // 回合制游戏:非常低的频率,事件驱动
291
+ this.config.tick_rates.world = 5;
292
+ this.config.tick_rates.physics = 5;
293
+ this.config.tick_rates.ai = 5;
294
+ this.fixed_time_step = 1 / 5;
295
+ // 回合制游戏:更精确的事件处理
296
+ this.config.events.immed_proc = true;
297
+ // 开启确定性执行
298
+ this.determ = true;
299
+ };
300
+
301
+ /**
302
+ * 设置音乐节奏游戏循环
303
+ * @returns {void}
304
+ */
305
+ GameLoop.prototype.setupRhythm = function () {
306
+ // 音乐节奏游戏:高频率、高精度
307
+ this.config.tick_rates.world = 120;
308
+ this.config.tick_rates.physics = 120;
309
+ this.config.tick_rates.input = 120;
310
+ this.fixed_time_step = 1 / 120;
311
+ // 关闭自适应以保证稳定帧率
312
+ this.config.base.adaptive = false;
313
+ };
314
+
315
+ /**
316
+ * 设置竞技游戏循环
317
+ * @param {object} sync_config 同步配置
318
+ * @returns {void}
319
+ */
320
+ GameLoop.prototype.setupComp = function (sync_config) {
321
+ // 竞技游戏:高频率、确定性更新
322
+ this.config.tick_rates.physics = 64;
323
+ this.config.tick_rates.world = 64;
324
+ this.fixed_time_step = 1 / 64;
325
+ // 竞技游戏:支持锁步和回滚
326
+ if (sync_config.lockstep) {
327
+ this.setupLockstep();
328
+ }
329
+ // 竞技游戏:支持回滚
330
+ if (sync_config.rollback) {
331
+ this.setupRollback();
332
+ }
333
+ };
334
+
335
+ /**
336
+ * 设置MMORPG游戏循环
337
+ * @returns {void}
338
+ */
339
+ GameLoop.prototype.setupMMORPG = function () {
340
+ // MMORPG:分层更新,性能优化
341
+ this.config.tick_rates.world = 10;
342
+ this.config.tick_rates.ai = 2;
343
+ this.config.tick_rates.anim = 30;
344
+ this.config.perf.auto_adjust = true;
345
+ };
346
+
347
+ /**
348
+ * 设置休闲游戏循环
349
+ * @returns {void}
350
+ */
351
+ GameLoop.prototype.setupCasual = function () {
352
+ // 休闲游戏:事件驱动,低频率
353
+ this.config.tick_rates.world = 10;
354
+ this.config.tick_rates.ai = 5;
355
+ this.config.base.adaptive = false;
356
+ };
357
+
358
+ /**
359
+ * 设置RTS游戏循环
360
+ * @param {object} sync_config 同步配置
361
+ * @returns {void}
362
+ */
363
+ GameLoop.prototype.setupRTS = function (sync_config) {
364
+ // RTS:锁步同步
365
+ this.config.tick_rates.world = 16; // ~60Hz
366
+ this.config.sync.lockstep = true;
367
+ this.cmdBuf = new Map();
368
+ };
369
+
370
+ /**
371
+ * 设置模拟游戏循环
372
+ * @returns {void}
373
+ */
374
+ GameLoop.prototype.setupSimu = function () {
375
+ // 模拟游戏:支持时间缩放
376
+ this.config.base.time_scale = 1.0;
377
+ this.config.tick_rates.world = 20;
378
+ this.config.perf.auto_adjust = true;
379
+ };
380
+
381
+ /**
382
+ * 设置通用游戏循环
383
+ * @returns {void}
384
+ */
385
+ GameLoop.prototype.setupGeneral = function () {
386
+ // 通用配置
387
+ this.config.tick_rates.world = 30;
388
+ this.config.tick_rates.physics = 50;
389
+ };
390
+
391
+ /**
392
+ * 设置锁步同步
393
+ * @returns {void}
394
+ */
395
+ GameLoop.prototype.setupLockstep = function () {
396
+ // 锁步同步配置
397
+ this.config.sync.lockstep = true;
398
+ // 锁步需要确定性执行
399
+ this.determ = true;
400
+ // 初始化锁步状态
401
+ this._lockstep = {
402
+ turn: 0,
403
+ readyPlayers: new Set(),
404
+ pendingActions: [],
405
+ confirmed: false
406
+ };
407
+ };
408
+
409
+ /**
410
+ * 设置回滚同步
411
+ * @returns {void}
412
+ */
413
+ GameLoop.prototype.setupRollback = function () {
414
+ // 回滚同步配置
415
+ this.config.sync.rollback = true;
416
+ // 回滚需要确定性执行
417
+ this.determ = true;
418
+ // 初始化回滚状态
419
+ this._rollback = {
420
+ frame: 0,
421
+ history: [],
422
+ max_history: 60,
423
+ predInputs: new Map()
424
+ };
425
+ };
426
+
427
+ /**
428
+ * 启动游戏循环具体实现
429
+ * @private
430
+ * @returns {Promise<void>}
431
+ */
432
+ GameLoop.prototype._startCore = async function () {
433
+ // 初始化时间变量
434
+ this.last_frame_time = performance.now();
435
+ this.last_second_time = this.last_frame_time;
436
+ this.frames_this_second = 0;
437
+
438
+ // 启动游戏循环
439
+ await this.gameLoop();
440
+ };
441
+
442
+ /**
443
+ * 停止游戏循环具体实现
444
+ * @private
445
+ * @returns {Promise<void>}
446
+ */
447
+ GameLoop.prototype._stop = async function () {
448
+ // 状态由基类stop方法设置,此处只记录日志和执行停止逻辑
449
+ this.log('info', '游戏循环已停止');
450
+ };
451
+
452
+ /**
453
+ * 错误处理状态设置
454
+ * @private
455
+ * @returns {void}
456
+ */
457
+ GameLoop.prototype._error = function () {
458
+ // 错误状态处理,基类没有提供统一管理error状态的方法,所以保留此处设置
459
+ this.loop_status = 'error';
460
+ this.log('error', '游戏循环进入错误状态');
461
+ };
462
+
463
+ /**
464
+ * 执行游戏阶段
465
+ * @param {number} delta_time 帧间隔时间
466
+ * @returns {Promise<void>}
467
+ */
468
+ GameLoop.prototype._execPhases = async function (delta_time) {
469
+ let phases = this.config.phases;
470
+ let phase_start = 0;
471
+
472
+ // 预更新阶段
473
+ if (phases.pre_update) {
474
+ phase_start = performance.now();
475
+ await this._runHooks('pre_update', delta_time);
476
+ this.recordPhaseTime('pre_update', performance.now() - phase_start);
477
+ }
478
+
479
+ // 更新阶段
480
+ if (phases.update) {
481
+ phase_start = performance.now();
482
+ await this.setSystems(delta_time);
483
+ this.recordPhaseTime('update', performance.now() - phase_start);
484
+ }
485
+
486
+ // 后更新阶段
487
+ if (phases.post_update) {
488
+ phase_start = performance.now();
489
+ await this._runHooks('post_update', delta_time);
490
+ this.recordPhaseTime('post_update', performance.now() - phase_start);
491
+ }
492
+
493
+ // 渲染阶段
494
+ if (phases.render) {
495
+ phase_start = performance.now();
496
+ await this._runHooks('pre_render', delta_time);
497
+ await this._render(delta_time);
498
+ await this._runHooks('post_render', delta_time);
499
+ this.recordPhaseTime('render', performance.now() - phase_start);
500
+ }
501
+
502
+ // 清理阶段
503
+ if (phases.cleanup) {
504
+ phase_start = performance.now();
505
+ await this._cleanup(delta_time);
506
+ this.recordPhaseTime('cleanup', performance.now() - phase_start);
507
+ }
508
+
509
+ // 处理事件队列
510
+ await this.runEvents();
511
+ this.checkFrameBudget();
512
+ };
513
+
514
+ /**
515
+ * 执行钩子函数
516
+ * @param {string} hook_name 钩子名称
517
+ * @param {number} delta_time 帧间隔时间
518
+ * @returns {Promise<void>}
519
+ */
520
+ GameLoop.prototype._runHooks = async function (hook_name, delta_time) {
521
+ let hooks = this._hook[hook_name] || [];
522
+ for (let hook of hooks) {
523
+ try {
524
+ await hook(delta_time);
525
+ } catch (error) {
526
+ this.log('error', `钩子 ${hook_name} 执行错误:`, error);
527
+ }
528
+ }
529
+ };
530
+
531
+ /**
532
+ * 渲染处理
533
+ * @param {number} delta_time 帧间隔时间
534
+ * @returns {Promise<void>}
535
+ */
536
+ GameLoop.prototype._render = async function (delta_time) {
537
+ if (this._world && this._world.render) {
538
+ await this._world.render(delta_time);
539
+ }
540
+ };
541
+
542
+ /**
543
+ * 清理处理
544
+ * @param {number} delta_time 帧间隔时间
545
+ * @returns {Promise<void>}
546
+ */
547
+ GameLoop.prototype._cleanup = async function (delta_time) {
548
+ // 清理无效实体
549
+ if (this._world && this._world.cleanup) {
550
+ await this._world.cleanup(delta_time);
551
+ }
552
+ // 定时持久化脏数据
553
+ await this._persistEntities(delta_time);
554
+ };
555
+
556
+ /**
557
+ * 定时持久化实体
558
+ * @param {number} delta_time 帧间隔时间
559
+ * @returns {Promise<void>}
560
+ */
561
+ GameLoop.prototype._persistEntities = async function (delta_time) {
562
+ var persist_config = this.config.persist;
563
+ if (!persist_config || !persist_config.enabled) {
564
+ return;
565
+ }
566
+ // 累计时间
567
+ this._persist_counter += delta_time;
568
+ var interval_ms = persist_config.interval * 1000;
569
+ // 检查是否到达持久化间隔
570
+ if (this._persist_counter >= interval_ms) {
571
+ this._persist_counter = 0;
572
+ // 保存脏数据实体
573
+ if (this._world && this._world._saveDirtyEntities) {
574
+ await this._world._saveDirtyEntities();
575
+ }
576
+ }
577
+ };
578
+
579
+ /**
580
+ * 处理循环错误
581
+ * @param {Error} error 错误对象
582
+ * @returns {Promise<void>}
583
+ */
584
+ GameLoop.prototype._handleLoopError = async function (error) {
585
+ if (this.config.base.stop_on_error) {
586
+ this.loop_status = 'error';
587
+ this._error();
588
+ }
589
+ };
590
+
591
+ /**
592
+ * 处理暂停状态
593
+ * @returns {Promise<void>}
594
+ */
595
+ GameLoop.prototype._handlePausedState = async function () {
596
+ // 暂停状态下只处理高优先级事件
597
+ await this.runHighPriEvents();
598
+
599
+ // 等待恢复
600
+ await this.sleep(100);
601
+
602
+ // 继续循环
603
+ if (this.loop_status === 'paused') {
604
+ setImmediate(() => this.gameLoop());
605
+ }
606
+ };
607
+
608
+ /**
609
+ * 游戏主循环
610
+ * @returns {Promise<void>}
611
+ */
612
+ GameLoop.prototype.gameLoop = async function () {
613
+ if (this.loop_status !== 'running') return;
614
+
615
+ // 检查暂停状态
616
+ if (this.loop_status === 'paused') {
617
+ await this._handlePausedState();
618
+ return;
619
+ }
620
+ let frame_start = performance.now();
621
+ let delta_time = Math.min(frame_start - this.last_frame_time, 100);
622
+ this.last_frame_time = frame_start;
623
+
624
+ this.startMon();
625
+
626
+ try {
627
+ await this._execPhases(delta_time);
628
+ } catch (error) {
629
+ await this._handleLoopError(error);
630
+ }
631
+
632
+ this.endMon(frame_start);
633
+
634
+ // 动态帧率控制
635
+ let sleep_time = this.calcSleepTime();
636
+ if (sleep_time > 1) {
637
+ await this.sleep(sleep_time);
638
+ }
639
+
640
+ if (this.loop_status === 'running') {
641
+ setImmediate(() => this.gameLoop());
642
+ }
643
+ };
644
+
645
+ /**
646
+ * 系统更新
647
+ * @param {number} delta_time 帧间隔时间
648
+ * @returns {Promise<void>}
649
+ */
650
+ GameLoop.prototype.setSystems = async function (delta_time) {
651
+ let scaled_delta_time = delta_time * this.config.base.time_scale;
652
+
653
+ // 获取有序的系统更新列表(考虑依赖关系)
654
+ let ordered_systems = this.getOrderedSystems();
655
+
656
+ // 更新各个系统(根据各自的tick_rate)
657
+ for (let [system_name] of ordered_systems) {
658
+ let tick_rate = this.config.tick_rates[system_name] || this.config.tick_rates.world;
659
+
660
+ if (tick_rate <= 0) continue; // 跳过禁用的系统
661
+
662
+ await this.updateSystem(
663
+ system_name,
664
+ scaled_delta_time,
665
+ tick_rate
666
+ );
667
+ }
668
+ };
669
+
670
+ /**
671
+ * 获取按依赖关系排序的系统列表
672
+ * @returns {Array} 有序的系统列表
673
+ */
674
+ GameLoop.prototype.getOrderedSystems = function () {
675
+ // 如果没有依赖关系,直接返回系统列表
676
+ if (Object.keys(this._deps).length === 0) {
677
+ return Object.entries(this._system);
678
+ }
679
+
680
+ // 简单的拓扑排序实现
681
+ let visited = new Set();
682
+ let result = [];
683
+ let graph = this._deps;
684
+ let visit = (system_name) => {
685
+ if (visited.has(system_name)) return;
686
+ visited.add(system_name);
687
+
688
+ // 先访问依赖项
689
+ let dependencies = graph[system_name] || [];
690
+ for (let dep of dependencies) {
691
+ if (this._system[dep]) {
692
+ visit(dep);
693
+ }
694
+ }
695
+
696
+ if (this._system[system_name]) {
697
+ result.push([system_name, this._system[system_name]]);
698
+ }
699
+ };
700
+
701
+ // 遍历所有系统
702
+ for (let system_name in this._system) {
703
+ visit(system_name);
704
+ }
705
+
706
+ return result;
707
+ };
708
+
709
+ /**
710
+ * 根据固定帧率更新系统
711
+ * @param {string} system_name 系统名称
712
+ * @param {number} delta_time 帧间隔时间
713
+ * @param {number} tick_rate 帧率
714
+ * @returns {Promise<void>}
715
+ */
716
+ GameLoop.prototype.updateSystem = async function (system_name, delta_time, tick_rate) {
717
+ let system = this._system[system_name];
718
+ if (!system || !system.isEnabled()) return;
719
+
720
+ // 获取累加器并累加帧时间
721
+ let acc = (this._accrual[system_name] || 0) + delta_time;
722
+ let system_delta = 1000 / tick_rate;
723
+
724
+ // 执行固定时间步长更新
725
+ while (acc >= system_delta) {
726
+ try {
727
+ let start_time = performance.now();
728
+ await system.update(system_delta);
729
+ let update_time = performance.now() - start_time;
730
+
731
+ // 记录性能数据
732
+ this.perf_stats.update_times[system_name] = update_time;
733
+
734
+ acc -= system_delta;
735
+ // 更新tick计数
736
+ this.config.tick_count++;
737
+ } catch (error) {
738
+ this.log('error', `系统 ${system_name} 更新错误:`, error);
739
+ // 处理系统错误
740
+ if (system.critical) {
741
+ throw error;
742
+ }
743
+ break;
744
+ }
745
+ }
746
+
747
+ // 更新累加器
748
+ this._accrual[system_name] = acc;
749
+ };
750
+
751
+ /**
752
+ * 处理事件队列
753
+ * @returns {Promise<void>}
754
+ */
755
+ GameLoop.prototype.runEvents = async function () {
756
+ // 按优先级处理事件
757
+ for (let priority = 0; priority < this.config.events.priority; priority++) {
758
+ let queue = this._event_queue[priority] || [];
759
+ let events_to_process = queue.splice(0, Math.min(queue.length, this.config.events
760
+ .max_per_frame)); // 限制每帧处理数量
761
+
762
+ for (let event of events_to_process) {
763
+ try {
764
+ await this.handleEvent(event, priority);
765
+ } catch (error) {
766
+ this.log('error', `事件处理错误 (优先级 ${priority}):`, error);
767
+ }
768
+ }
769
+ }
770
+ };
771
+
772
+ /**
773
+ * 仅处理高优先级事件(用于暂停状态)
774
+ * @returns {Promise<void>}
775
+ */
776
+ GameLoop.prototype.runHighPriEvents = async function () {
777
+ let queue = this._event_queue[0] || [];
778
+ let events_to_process = queue.splice(0, Math.min(queue.length, 20));
779
+
780
+ for (let event of events_to_process) {
781
+ try {
782
+ await this.handleEvent(event, 0);
783
+ } catch (error) {
784
+ this.log('error', `高优先级事件处理错误 (优先级 0):`, error);
785
+ }
786
+ }
787
+ };
788
+
789
+ /**
790
+ * 处理单个事件
791
+ * @param {object} event 事件对象
792
+ * @param {number} priority 优先级
793
+ * @returns {Promise<void>}
794
+ */
795
+ GameLoop.prototype.handleEvent = async function (event, priority) {
796
+ // 事件处理逻辑
797
+ this.log('debug', `处理事件 (优先级 ${priority}):`, event);
798
+ };
799
+
800
+ /**
801
+ * 性能监控开始
802
+ * @returns {void}
803
+ */
804
+ GameLoop.prototype.startMon = function () {
805
+ this.perf_stats.frame_start = performance.now();
806
+ };
807
+
808
+ /**
809
+ * 性能监控结束
810
+ * @param {number} frame_start 帧开始时间
811
+ * @returns {void}
812
+ */
813
+ GameLoop.prototype.endMon = function (frame_start) {
814
+ let frame_time = performance.now() - frame_start;
815
+
816
+ // 记录帧时间
817
+ this.perf_stats.frame_times.push(frame_time);
818
+ if (this.perf_stats.frame_times.length > 60) {
819
+ this.perf_stats.frame_times.shift();
820
+ }
821
+
822
+ // 计算FPS
823
+ this.frames_this_second++;
824
+ let now = performance.now();
825
+ if (now - this.last_second_time >= 1000) {
826
+ this.perf_stats.fps = this.frames_this_second;
827
+ this.frames_this_second = 0;
828
+ this.last_second_time = now;
829
+
830
+ // 计算系统负载
831
+ this.calcLoad();
832
+
833
+ // 触发性能统计更新事件
834
+ this.emitEvent('performance_stats_update', {
835
+ fps: this.perf_stats.fps,
836
+ load: this.perf_stats.load,
837
+ frame_time: frame_time,
838
+ timestamp: Date.now()
839
+ });
840
+
841
+ // 自适应调整
842
+ if (this.config.perf.auto_adjust) {
843
+ this.adjustPerf();
844
+ }
845
+
846
+ // 性能分析日志(如果启用)
847
+ if (this.config.perf.profile) {
848
+ this.logPerf();
849
+ }
850
+ }
851
+ };
852
+
853
+ /**
854
+ * 记录阶段执行时间
855
+ * @param {string} phase 阶段名称
856
+ * @param {number} time 执行时间(毫秒)
857
+ * @returns {void}
858
+ */
859
+ GameLoop.prototype.recordPhaseTime = function (phase, time) {
860
+ let times = this.perf_stats.phase_times[phase] || [];
861
+ times.push(time);
862
+ if (times.length > 60) times.shift();
863
+ this.perf_stats.phase_times[phase] = times;
864
+ };
865
+
866
+ /**
867
+ * 检查帧时间预算
868
+ * @returns {void}
869
+ */
870
+ GameLoop.prototype.checkFrameBudget = function () {
871
+ let target_frame_time = 1000 / this.config.base.max_fps;
872
+ let avg_frame_time = this.perf_stats.frame_times.reduce((a, b) => a + b, 0) /
873
+ this.perf_stats.frame_times.length;
874
+
875
+ if (avg_frame_time > target_frame_time * 1.2) {
876
+ this.log('warn', `帧时间超出预算: ${avg_frame_time.toFixed(2)}ms > ${target_frame_time.toFixed(2)}ms`);
877
+ // 触发帧时间超出预算事件
878
+ this.emitEvent('performance_frame_budget_exceeded', {
879
+ avg_frame_time: avg_frame_time,
880
+ target_frame_time: target_frame_time,
881
+ timestamp: Date.now()
882
+ });
883
+ if (this.config.perf.auto_adjust) {
884
+ this.adjustPerf();
885
+ }
886
+ }
887
+ };
888
+
889
+ /**
890
+ * 性能监控日志
891
+ * @returns {void}
892
+ */
893
+ GameLoop.prototype.logPerf = function () {
894
+ // 记录各阶段平均耗时
895
+ for (let phase in this.perf_stats.phase_times) {
896
+ let times = this.perf_stats.phase_times[phase];
897
+ if (times.length > 0) {
898
+ let avg_time = times.reduce((a, b) => a + b, 0) / times.length;
899
+ this.log('debug', ` ${phase}: ${avg_time.toFixed(2)}ms`);
900
+ }
901
+ }
902
+ };
903
+
904
+ /**
905
+ * 计算系统负载
906
+ * @returns {void}
907
+ */
908
+ GameLoop.prototype.calcLoad = function () {
909
+ let target_frame_time = 1000 / this.config.base.max_fps;
910
+ let avg_frame_time = this.perf_stats.frame_times.reduce((a, b) => a + b, 0) /
911
+ this.perf_stats.frame_times.length;
912
+ this.perf_stats.load = Math.min(avg_frame_time / target_frame_time, 1.0);
913
+ };
914
+
915
+ /**
916
+ * 自适应性能调整
917
+ * @returns {void}
918
+ */
919
+ GameLoop.prototype.adjustPerf = function () {
920
+ let load = this.perf_stats.load;
921
+ let entity_count = this._world ? this._world.entities.size : 0;
922
+ let player_count = this._world ? this._world.getPlayerNum() : 0;
923
+
924
+ if (load > 0.8) {
925
+ // 触发高负载事件
926
+ this.emitEvent('performance_high_load', {
927
+ load: load,
928
+ entity_count: entity_count,
929
+ player_count: player_count,
930
+ timestamp: Date.now()
931
+ });
932
+ this.reduceLoad(entity_count, player_count);
933
+ } else if (load < 0.5) {
934
+ // 触发低负载事件
935
+ this.emitEvent('performance_low_load', {
936
+ load: load,
937
+ entity_count: entity_count,
938
+ player_count: player_count,
939
+ timestamp: Date.now()
940
+ });
941
+ this.increaseQuality(entity_count, player_count);
942
+ }
943
+ };
944
+
945
+ /**
946
+ * 降低性能负载
947
+ * @param {number} entity_count 实体数量
948
+ * @param {number} player_count 玩家数量
949
+ * @returns {void}
950
+ */
951
+ GameLoop.prototype.reduceLoad = function (entity_count, player_count) {
952
+ if (player_count > 100) {
953
+ this.config.tick_rates.ai = Math.max(1, this.config.tick_rates.ai / 2);
954
+ this.config.tick_rates.anim = Math.max(10, this.config.tick_rates.anim / 1.5);
955
+ this.config.tick_rates.physics = Math.max(30, this.config.tick_rates.physics / 1.2);
956
+ } else if (entity_count > 1000) {
957
+ this.config.tick_rates.ai = Math.max(1, this.config.tick_rates.ai / 1.5);
958
+ this.config.tick_rates.physics = Math.max(30, this.config.tick_rates.physics / 1.2);
959
+ } else {
960
+ this.config.tick_rates.ai = Math.max(1, this.config.tick_rates.ai / 2);
961
+ this.config.tick_rates.anim = Math.max(10, this.config.tick_rates.anim / 1.5);
962
+ }
963
+ };
964
+
965
+ /**
966
+ * 提高性能质量
967
+ * @param {number} entity_count 实体数量
968
+ * @param {number} player_count 玩家数量
969
+ * @returns {void}
970
+ */
971
+ GameLoop.prototype.increaseQuality = function (entity_count, player_count) {
972
+ let load = this.perf_stats.load;
973
+ if (load < 0.3) {
974
+ this.config.tick_rates.ai = Math.min(30, this.config.tick_rates.ai * 1.5);
975
+ this.config.tick_rates.anim = Math.min(60, this.config.tick_rates.anim * 1.2);
976
+ this.config.tick_rates.physics = Math.min(60, this.config.tick_rates.physics * 1.1);
977
+ } else {
978
+ this.config.tick_rates.ai = Math.min(30, this.config.tick_rates.ai * 1.2);
979
+ this.config.tick_rates.anim = Math.min(60, this.config.tick_rates.anim * 1.1);
980
+ }
981
+ };
982
+
983
+ /**
984
+ * 休眠函数
985
+ * @param {number} ms 休眠时间(毫秒)
986
+ * @returns {Promise<void>}
987
+ */
988
+ GameLoop.prototype.sleep = function (ms) {
989
+ if (typeof ms !== 'number' || ms < 0) {
990
+ throw new Error('休眠时间必须是非负数字');
991
+ }
992
+
993
+ return new Promise(resolve => setTimeout(resolve, ms));
994
+ };
995
+
996
+ /**
997
+ * 检测是否处于空闲状态
998
+ * @returns {boolean} 是否空闲
999
+ */
1000
+ GameLoop.prototype.isIdle = function () {
1001
+ if (!this.config.idle.enabled) {
1002
+ return false;
1003
+ }
1004
+ if (!this._world) {
1005
+ return true;
1006
+ }
1007
+ let entity_count = this._world.entities ? this._world.entities.size : 0;
1008
+ let player_count = this._world._player_ids ? this._world._player_ids.size : 0;
1009
+
1010
+ let is_idle = entity_count === 0 && player_count === 0;
1011
+ if (is_idle) {
1012
+ this.config.idle.counter++;
1013
+ if (this.config.idle.counter >= this.config.idle.threshold) {
1014
+ return true;
1015
+ }
1016
+ } else {
1017
+ this.config.idle.counter = 0;
1018
+ }
1019
+ return false;
1020
+ };
1021
+
1022
+ /**
1023
+ * 计算动态休眠时间
1024
+ * @returns {number} 休眠时间(毫秒)
1025
+ */
1026
+ GameLoop.prototype.calcSleepTime = function () {
1027
+ let idle_config = this.config.idle;
1028
+ let is_idle = this.isIdle();
1029
+
1030
+ if (is_idle) {
1031
+ idle_config.counter++;
1032
+ if (idle_config.counter >= idle_config.threshold) {
1033
+ let idle_interval = 1000 / idle_config.fps;
1034
+ return Math.max(idle_interval, 1);
1035
+ }
1036
+ } else {
1037
+ idle_config.counter = 0;
1038
+ }
1039
+
1040
+ let target_interval = 1000 / this.config.base.max_fps;
1041
+ let elapsed = performance.now() - this.last_frame_time;
1042
+ let sleep_time = target_interval - elapsed;
1043
+
1044
+ return Math.max(0, sleep_time);
1045
+ };
1046
+
1047
+ /**
1048
+ * 注册生命周期钩子
1049
+ * @param {string} hook_name 钩子名称
1050
+ * @param {Function} callback 回调函数
1051
+ * @returns {void}
1052
+ */
1053
+ GameLoop.prototype.registerHook = function (hook_name, callback) {
1054
+ if (!hook_name || typeof hook_name !== 'string') {
1055
+ throw new Error('钩子名称不能为空且必须是字符串');
1056
+ }
1057
+
1058
+ if (typeof callback !== 'function') {
1059
+ throw new Error('回调必须是函数');
1060
+ }
1061
+
1062
+ if (!this._hook[hook_name]) {
1063
+ throw new Error(`未知的钩子类型: ${hook_name}`);
1064
+ }
1065
+
1066
+ this._hook[hook_name].push(callback);
1067
+ };
1068
+
1069
+ /**
1070
+ * 注销系统
1071
+ * @param {string} name 系统名称
1072
+ * @returns {Promise<void>}
1073
+ */
1074
+ GameLoop.prototype.unregSystem = async function (name) {
1075
+ // 参数验证
1076
+ if (!name || typeof name !== 'string') {
1077
+ throw new TypeError('系统名称不能为空且必须是字符串');
1078
+ }
1079
+
1080
+ try {
1081
+ // 清理系统资源
1082
+ var system = this._system[name];
1083
+ if (system && typeof system.cleanup === 'function') {
1084
+ await system.cleanup();
1085
+ }
1086
+
1087
+ // 删除系统引用
1088
+ delete this._system[name];
1089
+ delete this._accrual[name];
1090
+ delete this.perf_stats.update_times[name];
1091
+ delete this._deps[name];
1092
+
1093
+ this.log('info', `系统已注销: ${name}`);
1094
+ } catch (error) {
1095
+ this.log('error', `注销系统 ${name} 时发生错误`, error);
1096
+ throw error;
1097
+ }
1098
+ };
1099
+
1100
+ /**
1101
+ * 清理所有资源
1102
+ * @returns {Promise<void>}
1103
+ */
1104
+ GameLoop.prototype.cleanupAll = async function () {
1105
+ this.log('info', '开始清理所有游戏循环资源');
1106
+
1107
+ var cleanup_promises = [];
1108
+
1109
+ // 清理所有系统
1110
+ for (var system_name in this._system) {
1111
+ var system = this._system[system_name];
1112
+ if (system && typeof system.cleanup === 'function') {
1113
+ var promise = system.cleanup().catch(error => {
1114
+ this.log('error', `清理系统 ${system_name} 时发生错误`, error);
1115
+ });
1116
+ cleanup_promises.push(promise);
1117
+ }
1118
+ }
1119
+
1120
+ // 等待所有清理操作完成
1121
+ await Promise.all(cleanup_promises);
1122
+
1123
+ // 清空所有数据结构
1124
+ this._system = {};
1125
+ this._accrual = {};
1126
+ this._event_queue = {};
1127
+ this._deps = {};
1128
+ this.perf_stats = {
1129
+ frame_times: [],
1130
+ update_times: {},
1131
+ frame_start: 0
1132
+ };
1133
+
1134
+ this.log('info', '所有游戏循环资源已清理完成');
1135
+ };
1136
+
1137
+ /**
1138
+ * 记录系统性能
1139
+ * @param {string} system_name 系统名称
1140
+ * @param {number} time 执行时间
1141
+ * @returns {void}
1142
+ */
1143
+ GameLoop.prototype.recordPerf = function (system_name, time) {
1144
+ let times = this.perf_stats.update_times[system_name] || [];
1145
+ times.push(time);
1146
+ if (times.length > 60) times.shift();
1147
+ this.perf_stats.update_times[system_name] = times;
1148
+ };
1149
+
1150
+ /**
1151
+ * 处理游戏循环错误
1152
+ * @param {Error} error 错误对象
1153
+ * @returns {void}
1154
+ */
1155
+ GameLoop.prototype.runError = function (error) {
1156
+ // 设置错误状态
1157
+ this._error();
1158
+
1159
+ // 错误处理策略
1160
+ if (this.config.perf?.auto_adjust) {
1161
+ this.reduceLoad();
1162
+ }
1163
+
1164
+ // 根据配置决定是否停止游戏循环
1165
+ if (this.config.base?.stop_on_error) {
1166
+ this.log('warn', '游戏循环停止 (stop_on_error=true)');
1167
+ this._stop();
1168
+ }
1169
+
1170
+ // 记录错误日志
1171
+ this.log('error', '游戏循环错误处理:', error);
1172
+ };
1173
+
1174
+ /**
1175
+ * 获取性能统计
1176
+ * @returns {object} 性能统计数据
1177
+ */
1178
+ GameLoop.prototype.getStats = function () {
1179
+ return {
1180
+ fps: this.perf_stats.fps,
1181
+ load: this.perf_stats.load,
1182
+ sys_perf: { ...this.perf_stats.update_times },
1183
+ frame_times: [...this.perf_stats.frame_times],
1184
+ current_tick: this.current_tick
1185
+ };
1186
+ };
1187
+
1188
+ /**
1189
+ * 动态配置更新
1190
+ * @param {object} new_config 新配置
1191
+ * @returns {void}
1192
+ */
1193
+ GameLoop.prototype.setConfig = function (new_config) {
1194
+ if (!new_config || typeof new_config !== 'object') {
1195
+ throw new Error('新配置必须是对象');
1196
+ }
1197
+
1198
+ $.push(this.config, new_config, true);
1199
+ };
1200
+
1201
+ /**
1202
+ * 重置配置为初始状态
1203
+ * @returns {void}
1204
+ */
1205
+ GameLoop.prototype.resetConfig = function () {
1206
+ if (this.original_config) {
1207
+ this.config = JSON.parse(JSON.stringify(this.original_config));
1208
+ this._init();
1209
+ }
1210
+ };
1211
+
1212
+ /**
1213
+ * 设置游戏暂停状态
1214
+ * @param {boolean} paused 是否暂停
1215
+ * @returns {void}
1216
+ */
1217
+ GameLoop.prototype.setPaused = function (paused) {
1218
+ if (typeof paused !== 'boolean') {
1219
+ throw new Error('暂停状态必须是布尔值');
1220
+ }
1221
+
1222
+ let old_status = this.loop_status;
1223
+ this.loop_status = paused ? 'paused' : (old_status === 'error' ? 'error' : 'running');
1224
+ this.log('info', `游戏循环 ${paused ? '暂停' : '恢复'}`);
1225
+ };
1226
+
1227
+ /**
1228
+ * 清理方法(定期执行)
1229
+ * @returns {Promise<void>}
1230
+ */
1231
+ GameLoop.prototype.cleanup = async function () {
1232
+ // 清理过期事件队列
1233
+ for (let priority in this._event_queue) {
1234
+ let queue = this._event_queue[priority];
1235
+ if (queue.length > this.config.events.queue_size * 0.8) {
1236
+ this.log('warn', `事件队列 (优先级 ${priority}) 几乎已满,正在清理...`);
1237
+ // 只保留高优先级事件
1238
+ this._event_queue[priority] = queue.filter(event => event && event.persistent);
1239
+ }
1240
+ }
1241
+
1242
+ // 执行各系统的清理方法
1243
+ for (let system_name in this._system) {
1244
+ let system = this._system[system_name];
1245
+ if (typeof system.cleanup === 'function') {
1246
+ try {
1247
+ await system.cleanup();
1248
+ } catch (error) {
1249
+ this.log('error', `系统 ${system_name} 清理失败:`, error);
1250
+ }
1251
+ }
1252
+ }
1253
+ };
1254
+
1255
+ exports.GameLoop = GameLoop;