Xverse_Room.js 18 KB

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