Xverse_Room.js 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591
  1. import XverseAvatarManager from "./XverseAvatarManager.js"
  2. import Codes from "./enum/Codes.js"
  3. import PathManager from "./PathManager.js"
  4. import Camera from "./Camera.js"
  5. import Stats from "./Stats.js"
  6. import ActionsHandler from "./ActionsHandler.js"
  7. import Signal from "./Signal.js"
  8. import ModelManager from "./ModelManager.js"
  9. import {reporter} from "./Reporter.js"
  10. import util from "./util.js"
  11. import XverseEffectManager from "./XverseEffectManager.js"
  12. import TimeoutError from "./error/TimeoutError.js"
  13. import ParamError from "./error/ParamError.js"
  14. import MotionType from "./enum/MotionType.js"
  15. import NetworkController from "./NetworkController.js"
  16. import InitNetworkTimeoutError from "./error/InitNetworkTimeoutError.js"
  17. import InitConfigTimeoutError from "./error/InitConfigTimeoutError.js"
  18. import InitDecoderTimeoutError from "./error/InitDecoderTimeoutError.js"
  19. import InitEngineError from "./error/InitEngineError.js"
  20. import {eventsManager} from "./EventsManager.js"
  21. import EngineProxy from "./EngineProxy.js"
  22. import EventsController from "./EventsController.js"
  23. import EImageQuality from "./enum/EImageQuality.js"
  24. import Panorama from "./Panorama.js"
  25. import Debug from "./Debug.js"
  26. import Logger from "./Logger.js"
  27. import NewUserStateType from "./enum/NewUserStateType.js"
  28. const logger = new Logger('xverse-room')
  29. export default class Xverse_Room extends EventEmitter {
  30. constructor(e) {
  31. super();
  32. E(this, "disableAutoTurn", !1);
  33. E(this, "options");
  34. E(this, "_currentNetworkOptions");
  35. E(this, "lastSkinId");
  36. E(this, "debug");
  37. E(this, "isFirstDataUsed", !1);
  38. E(this, "userId", null);
  39. E(this, "pathManager", new PathManager);
  40. E(this, "networkController");
  41. E(this, "_startTime", Date.now());
  42. E(this, "canvas");
  43. E(this, "modelManager");
  44. E(this, "eventsController");
  45. E(this, "panorama");
  46. E(this, "engineProxy");
  47. E(this, "_id");
  48. E(this, "skinList", []);
  49. E(this, "isHost", !1);
  50. E(this, "avatarManager", new XverseAvatarManager(this));
  51. E(this, "effectManager", new XverseEffectManager(this));
  52. E(this, "sceneManager");
  53. E(this, "scene");
  54. E(this, "breathPointManager");
  55. E(this, "_currentState");
  56. E(this, "joined", !1);
  57. E(this, "disableRotate", !1);
  58. E(this, "isPano", !1);
  59. E(this, "movingByClick", !0);
  60. E(this, "camera", new Camera(this));
  61. E(this, "stats", new Stats(this));
  62. E(this, "isUpdatedRawYUVData", !1);
  63. E(this, "actionsHandler", new ActionsHandler(this));
  64. E(this, "_currentClickingState", null);
  65. E(this, "signal", new Signal(this));
  66. E(this, "firstFrameTimestamp");
  67. E(this, "receiveRtcData", async()=>{
  68. logger.info("Invoke receiveRtcData");
  69. let i = !1,
  70. o = !1,
  71. s = !1,
  72. c = !1;
  73. return this.viewMode === "serverless" ? (
  74. logger.warn("set view mode to serverless"),
  75. this.setViewMode("observer").then(()=>this, ()=>this)
  76. ) : new Promise((resolve, reject) => {
  77. const workers = this.networkController.rtcp.workers;
  78. workers.registerFunction("signal", data => {
  79. // 更新坐标数据
  80. this.signal.handleSignal(data, reject)
  81. }),
  82. workers.registerFunction("stream", data => {
  83. // 更新视频贴图数据
  84. this.emit("streamTimestamp", {
  85. timestamp: Date.now()
  86. })
  87. o || (o = !0, logger.info("Invoke stream event"))
  88. if (data.stream) {
  89. s || (s = !0, logger.info("Invoke updateRawYUVData"))
  90. this.isUpdatedRawYUVData = !1;
  91. const $ = this._currentState.skin == null ? void 0 : this._currentState.skin.fov;
  92. this.sceneManager.materialComponent.updateRawYUVData(data.stream, data.width, data.height, $)
  93. this.isUpdatedRawYUVData = !0
  94. }
  95. if(!i){
  96. logger.info("Invoke isAfterRenderRegistered")
  97. i = !0
  98. this.scene.registerAfterRender(() => {
  99. this.engineProxy.frameRenderNumber >= 2
  100. && (c || (
  101. c = !0,
  102. logger.info("Invoke registerAfterRender")),
  103. this.isFirstDataUsed || (logger.info("Invoke isStreamAvailable"),
  104. this.isFirstDataUsed = !0,
  105. this.firstFrameTimestamp = Date.now(),
  106. resolve(this),
  107. this.afterJoinRoom()
  108. ))
  109. })
  110. }
  111. }),
  112. this.panorama.bindListener(() => {
  113. resolve(this)
  114. this.afterJoinRoom()
  115. }),
  116. workers.registerFunction("reconnectedFrame", () => {}),
  117. logger.info("Invoke decoderWorker.postMessage"),
  118. workers.decoderWorker.postMessage({
  119. t: 5
  120. })
  121. })
  122. });
  123. E(this, "moveToExtra", "");
  124. this.options = e,
  125. this.options.wsServerUrl || (this.options.wsServerUrl = SERVER_URLS.DEV),
  126. this.modelManager = ModelManager.getInstance(e.appId, e.releaseId),
  127. this.updateReporter();
  128. const n = e
  129. , {canvas: t} = n
  130. , r = Oe(n, ["canvas"]);
  131. logger.infoAndReportMeasurement({
  132. metric: "startJoinRoomAt",
  133. startTime: Date.now(),
  134. group: "joinRoom",
  135. extra: r,
  136. value: 0
  137. })
  138. }
  139. get currentNetworkOptions() {
  140. return this._currentNetworkOptions
  141. }
  142. get viewMode() {
  143. var e;
  144. return ((e = this._currentState) == null ? void 0 : e.viewMode) || "full"
  145. }
  146. get id() {
  147. return this._id
  148. }
  149. get skinId() {
  150. return this._currentState.skinId
  151. }
  152. get skin() {
  153. return this._currentState.skin
  154. }
  155. get sessionId() {
  156. return this.currentNetworkOptions.sessionId
  157. }
  158. get pictureQualityLevel() {
  159. return this.currentState.pictureQualityLevel
  160. }
  161. get avatars() {
  162. return Array.from(this.avatarManager.avatars.values())
  163. }
  164. get currentState() {
  165. var e;
  166. return le(oe({}, this._currentState), {
  167. state: (e = this.networkController) == null ? void 0 : e._state
  168. })
  169. }
  170. get _userAvatar() {
  171. return this.avatars.find(e=>e.userId === this.userId)
  172. }
  173. get tvs() {
  174. return this.engineProxy._tvs
  175. }
  176. get tv() {
  177. return this.tvs[0]
  178. }
  179. get currentClickingState() {
  180. return this._currentClickingState
  181. }
  182. afterJoinRoomHook() {}
  183. beforeJoinRoomResolveHook() {}
  184. afterReconnectedHook() {}
  185. handleSignalHook(e) {}
  186. skinChangedHook() {}
  187. async beforeStartGameHook(e) {}
  188. loadAssetsHook() {}
  189. afterUserAvatarLoadedHook() {}
  190. audienceViewModeHook() {}
  191. setViewModeToObserver() {}
  192. handleVehicleHook(e) {}
  193. updateReporter() {
  194. const {avatarId, skinId, userId, roomId, role, appId, wsServerUrl} = this.options;
  195. reporter.updateHeader({ userId }),
  196. reporter.updateBody({ roomId, role, skinId, avatarId, appId, wsServerUrl })
  197. }
  198. async initRoom() {
  199. const {timeout: e=DEFAULT_JOINROOM_TIMEOUT} = this.options;
  200. if(util.isSupported()){
  201. return this._initRoom()._timeout(e, new TimeoutError("initRoom timeout"))
  202. }
  203. else{
  204. return Promise.reject(new UnsupportedError)
  205. }
  206. }
  207. async _initRoom() {
  208. const e = this.validateOptions(this.options);
  209. if(e) {
  210. return logger.error("initRoom param error", e),
  211. Promise.reject(e);
  212. }
  213. const {
  214. canvas, avatarId, skinId, userId, wsServerUrl, role, token, pageSession, rotationRenderType, isAllSync=!1, appId, camera, player,
  215. avatarComponents, nickname, avatarScale, firends=[], syncByEvent=!1, areaName, attitude=MotionType.Walk, pathName, viewMode="full",
  216. person, roomId, roomTypeId, hasAvatar=!1, syncToOthers=!1, prioritySync=!1, extra, removeWhenDisconnected=!0
  217. } = this.options;
  218. this.setCurrentNetworkOptions({
  219. avatarId, skinId, roomId, userId, wsServerUrl, role, token, pageSession, rotationRenderType, isAllSync, appId, camera, player,
  220. avatarComponents, nickname, avatarScale, firends, syncByEvent, areaName, attitude, pathName,
  221. person, roomTypeId, hasAvatar, syncToOthers, prioritySync, extra, removeWhenDisconnected
  222. });
  223. this.userId = userId;
  224. this.canvas = canvas;
  225. areaName && (this.pathManager.currentArea = areaName);
  226. this.networkController = new NetworkController(this);
  227. this.setCurrentState({ areaName, pathName, attitude, speed: 0, viewMode, state: this.networkController._state, skinId });
  228. try {
  229. await Promise.all([this.initNetwork(), this.initConfig(), this.initWasm()]),
  230. logger.info("network config wasm all ready, start to create game");
  231. const skin = await this.requestCreateRoom({skinId})
  232. , skinRoute = skin.routeList.find(route => route.areaName === areaName)
  233. , speed = ((skinRoute == null ? void 0 : skinRoute.step) || 7.5) * 30;
  234. this.updateCurrentState({ skin, skinId: skin.id, versionId: skin.versionId, speed }),
  235. await this.initEngine(skin)
  236. } catch (e) {
  237. return Promise.reject(e)
  238. }
  239. this.beforeJoinRoomResolve();
  240. return this.receiveRtcData()
  241. }
  242. beforeJoinRoomResolve() {
  243. this.setupStats(),
  244. this.eventsController = new EventsController(this),
  245. this.eventsController.bindEvents(),
  246. this.panorama = new Panorama(this),
  247. this.beforeJoinRoomResolveHook()
  248. }
  249. afterJoinRoom() {
  250. this.joined = !0,
  251. this.viewMode === "observer" && this.setViewModeToObserver(),
  252. logger.infoAndReportMeasurement({
  253. tag: this.viewMode,
  254. value: this.firstFrameTimestamp || Date.now() - this._startTime,
  255. startTime: Date.now(),
  256. metric: "joinRoom",
  257. reportOptions: {
  258. immediate: !0
  259. }
  260. }),
  261. this.camera.initialFov = this.sceneManager.cameraComponent.getCameraFov(),
  262. this.stats.on("stats", ({stats: e})=>{
  263. reporter.report("stats", oe({}, e))
  264. }
  265. ),
  266. this.debug = new Debug(this),
  267. this.afterJoinRoomHook();
  268. setInterval(() => {
  269. this.actionsHandler.getNewUserState(NewUserStateType.NUST_Undefined).then(i => {
  270. this.avatarManager.handleAvatar(i)
  271. }).catch(() => {})
  272. }, 2e3)
  273. }
  274. afterReconnected() {
  275. this.avatarManager.clearOtherUsers(),
  276. this.afterReconnectedHook()
  277. }
  278. leave() {
  279. return logger.info("Invoke room.leave"),
  280. this.eventsController == null || this.eventsController.clearEvents(),
  281. this.networkController == null || this.networkController.quit(),
  282. this
  283. }
  284. validateOptions(e) {
  285. const {canvas, avatarId, skinId, userId, role, roomId, token, appId, avatarComponents} = e || {}
  286. const h = [];
  287. canvas instanceof HTMLCanvasElement || h.push(new ParamError("`canvas` must be instanceof of HTMLCanvasElement"));
  288. (!userId || typeof userId != "string") && h.push(new ParamError("`userId` must be string"));
  289. (!token || typeof token != "string") && h.push(new ParamError("`token` must be string"));
  290. (!appId || typeof appId != "string") && h.push(new ParamError("`appId` must be string"));
  291. role == "audience" || (!avatarId || !skinId) && h.push(new ParamError("`avatarId` and `skinId` is required when create room"));
  292. return h[0]
  293. }
  294. async initNetwork() {
  295. if (this.viewMode === "serverless")
  296. return Promise.resolve();
  297. const e = Date.now();
  298. try {
  299. await this.networkController.connect()._timeout(8e3, new InitNetworkTimeoutError),
  300. logger.infoAndReportMeasurement({
  301. metric: "networkInitAt",
  302. startTime: this._startTime,
  303. group: "joinRoom"
  304. }),
  305. logger.infoAndReportMeasurement({
  306. metric: "networkInitCost",
  307. startTime: e,
  308. group: "joinRoom"
  309. })
  310. } catch (t) {
  311. throw logger.infoAndReportMeasurement({
  312. metric: "networkInitAt",
  313. startTime: e,
  314. group: "joinRoom",
  315. error: t
  316. }),
  317. t
  318. }
  319. }
  320. async initConfig() {
  321. const e = Date.now();
  322. try {
  323. await this.modelManager.getApplicationConfig()._timeout(8e3, new InitConfigTimeoutError),
  324. logger.infoAndReportMeasurement({
  325. metric: "configInitAt",
  326. startTime: this._startTime,
  327. group: "joinRoom"
  328. }),
  329. logger.infoAndReportMeasurement({
  330. metric: "configInitCost",
  331. startTime: e,
  332. group: "joinRoom"
  333. })
  334. } catch (t) {
  335. throw logger.infoAndReportMeasurement({
  336. metric: "configInitAt",
  337. startTime: e,
  338. group: "joinRoom",
  339. error: t
  340. }),
  341. t
  342. }
  343. }
  344. async initEngine(e) {
  345. const t = Date.now();
  346. try {
  347. this.engineProxy = new EngineProxy(this),
  348. await this.engineProxy.initEngine(e),
  349. logger.infoAndReportMeasurement({
  350. metric: "webglInitAt",
  351. startTime: this._startTime,
  352. group: "joinRoom"
  353. }),
  354. logger.infoAndReportMeasurement({
  355. metric: "webglInitCost",
  356. startTime: t,
  357. group: "joinRoom"
  358. });
  359. return
  360. } catch (r) {
  361. let n = r;
  362. return r.code !== Codes.InitEngineTimeout && (n = new InitEngineError),
  363. logger.error(r),
  364. logger.infoAndReportMeasurement({
  365. metric: "webglInitAt",
  366. startTime: t,
  367. group: "joinRoom",
  368. error: n
  369. }),
  370. Promise.reject(n)
  371. }
  372. }
  373. async initWasm() {
  374. if (this.viewMode === "serverless")
  375. return Promise.resolve();
  376. const i = Date.now();
  377. try {
  378. await this.networkController.rtcp.workers.init({
  379. width: 1920,
  380. height: 1080,
  381. userID: this.userId,
  382. pageSession: this.options.pageSession,
  383. serverSession: ""
  384. })._timeout(8e3, new InitDecoderTimeoutError),
  385. this.networkController.rtcp.workers.registerFunction("error", o=>{
  386. logger.error("decode error", o);
  387. const {code: s, message: c} = o;
  388. this.emit("error", {
  389. code: s,
  390. msg: c
  391. })
  392. }
  393. ),
  394. logger.infoAndReportMeasurement({
  395. metric: "wasmInitAt",
  396. group: "joinRoom",
  397. startTime: this._startTime
  398. }),
  399. logger.infoAndReportMeasurement({
  400. metric: "wasmInitCost",
  401. group: "joinRoom",
  402. startTime: i
  403. }),
  404. eventsManager.on("traceId", o=>{
  405. this.networkController.rtcp.workers.onTraceId(o)
  406. }
  407. )
  408. } catch (o) {
  409. throw logger.infoAndReportMeasurement({
  410. metric: "wasmInitAt",
  411. group: "joinRoom",
  412. startTime: i,
  413. error: o
  414. }),
  415. o
  416. }
  417. }
  418. async requestCreateRoom({skinId: e}) {
  419. let skin;
  420. if (e) {
  421. skin = await this.getSkin(e);
  422. this.updateCurrentState({
  423. skin: skin
  424. });
  425. const r = await this.modelManager.findRoute(e, this.options.pathName);
  426. this.updateCurrentNetworkOptions({
  427. areaName: r.areaName,
  428. attitude: r.attitude,
  429. versionId: skin.versionId
  430. });
  431. const {camera: n, player: player} = util.getRandomItem(r.birthPointList) || this.options;
  432. this.options.camera || this.updateCurrentNetworkOptions({
  433. camera: n
  434. }),
  435. this.options.player || this.updateCurrentNetworkOptions({
  436. player: player
  437. })
  438. }
  439. if (this.viewMode === "serverless")
  440. return skin;
  441. try {
  442. await this.beforeStartGameHook(this.options);
  443. const {room_id: room_id, data: n, session_id: session_id} = await this.networkController.startGame();
  444. this._id = room_id;
  445. const a = JSON.parse(n);
  446. this.isHost = a.IsHost,
  447. e = a.SkinID || e;
  448. const skin = await this.getSkin(e);
  449. this.updateCurrentNetworkOptions({
  450. roomId: room_id,
  451. sessionId: session_id
  452. });
  453. reporter.updateBody({
  454. roomId: room_id,
  455. skinId: e,
  456. serverSession: session_id
  457. });
  458. return skin
  459. } catch (r) {
  460. logger.error("requestCreateRoom error:", r);
  461. return Promise.reject(r)
  462. }
  463. }
  464. pause() {
  465. return this.engineProxy.pause()
  466. }
  467. resume() {
  468. return this.engineProxy.resume()
  469. }
  470. reconnect() {
  471. this.networkController.reconnect()
  472. }
  473. async setViewMode(e) {}
  474. handleRepetLogin() {
  475. logger.warn("receive " + Codes.RepeatLogin + " for repeat login"),
  476. this.emit("repeatLogin"),
  477. reporter.disable(),
  478. this.networkController.quit()
  479. }
  480. setPictureQualityLevel(e) {
  481. const t = {
  482. high: EImageQuality.high,
  483. low: EImageQuality.low,
  484. average: EImageQuality.mid
  485. };
  486. return this.updateCurrentState({
  487. pictureQualityLevel: e
  488. }),
  489. this.sceneManager.setImageQuality(t[e])
  490. }
  491. async getSkin(skinId) {
  492. let t = (this.skinList = await this.modelManager.getSkinsList()).find(skin=>skin.id === skinId || skin.id === skinId)
  493. if (t)
  494. return t;
  495. {
  496. const n = `skin is invalid: skinId: ${skinId}`;
  497. return Promise.reject(new ParamError(n))
  498. }
  499. }
  500. setupStats() {
  501. this.stats.assign({
  502. roomId: this.id,
  503. userId: this.userId
  504. }),
  505. setInterval(this.engineProxy.updateStats, 1e3)
  506. }
  507. proxyEvents(e, t) {
  508. this.emit(e, t)
  509. }
  510. setCurrentNetworkOptions(e) {
  511. this._currentNetworkOptions = e
  512. }
  513. updateCurrentNetworkOptions(e) {
  514. Object.assign(this._currentNetworkOptions, e),
  515. Object.assign(this.options, e)
  516. }
  517. setCurrentState(e) {
  518. this._currentState = e
  519. }
  520. updateCurrentState(e) {
  521. e.skinId && (this.lastSkinId = this.currentState.skinId,
  522. this.updateCurrentNetworkOptions({
  523. skinId: e.skinId
  524. })),
  525. e.versionId && this.updateCurrentNetworkOptions({
  526. versionId: e.versionId
  527. }),
  528. Object.assign(this._currentState, e)
  529. }
  530. afterSetUrlHook() {}
  531. afterTvStopedHook() {}
  532. afterTvPlayedHook() {}
  533. pageShowHandler() {
  534. this.engineProxy.setEnv(this.skin), this.allowRender = !0
  535. }
  536. pageHideHandler() {
  537. this.allowRender = !1
  538. }
  539. }