XverseAvatarManager.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410
  1. import CoreBroadcastType from "./enum/CoreBroadcastType.js"
  2. import AvatarGroup from "./enum/AvatarGroup.js"
  3. import Broadcast from "./Broadcast.js"
  4. import {avatarLoader} from "./XAvatarLoader.js"
  5. import SyncEventType from "./enum/SyncEventType.js"
  6. import GetStateTypes from "./enum/GetStateTypes.js"
  7. import EAvatarRelationRank from "./enum/EAvatarRelationRank.js"
  8. import Person from "./enum/Person.js"
  9. import XverseAvatar from "./XverseAvatar.js"
  10. import MotionType from "./enum/MotionType.js"
  11. import TimeoutError from "./error/TimeoutError.js"
  12. import Logger from "./Logger.js"
  13. import QueueType from "./enum/QueueType.js"
  14. const logger = new Logger('4DMVS_CharacterManager')
  15. export default class XverseAvatarManager extends EventEmitter {
  16. constructor(room) {
  17. super();
  18. this.xAvatarManager = null
  19. this.avatars = new Map
  20. this.syncAvatarsLength = 0
  21. this._room = room;
  22. this._usersStatistics();
  23. this.broadcast = this.setupBroadcast();
  24. room.on("skinChanged", ()=>{
  25. this.avatars.forEach(t=>{
  26. avatar.disconnected && this.removeAvatar(avatar.userId, !0)
  27. })
  28. })
  29. }
  30. setupBroadcast() {
  31. return new Broadcast(this._room,async e=>{
  32. const {broadcastType: t, info: r} = e;
  33. if (t !== CoreBroadcastType.PlayAnimation)
  34. return;
  35. const {userId: n, animation: o, extra: a, loop: s=!1} = r
  36. , l = this.avatars.get(n);
  37. l && !l.isSelf && (l.emit("animationStart", {
  38. animationName: o,
  39. extra: decodeURIComponent(a)
  40. }),
  41. await (l == null ? void 0 : l._playAnimation(o, s)),
  42. l.emit("animationEnd", {
  43. animationName: o,
  44. extra: decodeURIComponent(a)
  45. }))
  46. }
  47. )
  48. }
  49. hideAll(e=!0) {
  50. this.xAvatarManager.hideAll(e)
  51. }
  52. showAll(e=!0) {
  53. this.xAvatarManager.showAll(e)
  54. }
  55. async init() {
  56. this.xAvatarManager = this._room.sceneManager.avatarComponent;
  57. try {
  58. const configList = await this._room.modelManager.getApplicationConfig()
  59. , {avatars} = configList;
  60. if (avatars) {
  61. await avatarLoader.parse(this._room.sceneManager, avatars);
  62. return
  63. }
  64. return Promise.reject("cannot find avatar config list")
  65. } catch (e) {
  66. return logger.error(e),
  67. Promise.reject("avatar mananger init error!")
  68. }
  69. }
  70. async handleAvatar(signal) {
  71. if (this._room.viewMode === "simple" || !this._room.joined || !signal.newUserStates)
  72. return;
  73. let newUserStates = signal.newUserStates;
  74. let userAvatar = this._room._userAvatar;
  75. if ((userAvatar == null ? void 0 : userAvatar.isMoving) && userAvatar.motionType === MotionType.Run)
  76. {
  77. const mainStates = newUserStates.filter(state => state.userId === this._room.userId)
  78. , o = newUserStates.filter(state => state.userId !== this._room.userId).slice(0, 2);
  79. newUserStates = mainStates.concat(o)
  80. }
  81. if (signal.getStateType === GetStateTypes.Event)
  82. {
  83. this.syncAvatarsLength = (newUserStates || []).length;
  84. const n = this._room.avatars.filter(avatar => avatar.group == AvatarGroup.User);
  85. n.filter(avatar =>
  86. !(newUserStates != null && newUserStates.find(state => state.userId == avatar.userId))
  87. ).forEach(avatar=>{
  88. this.removeAvatar(avatar.userId)
  89. });
  90. const a = newUserStates.filter(state => !n.find(avatar => avatar.userId == state.userId));
  91. this._handleAvatar(a)
  92. }
  93. this._handleAvatar(newUserStates)
  94. }
  95. async _handleAvatar(newUserStates) {
  96. newUserStates && newUserStates.forEach(state=>{
  97. const isMainAvatar = this._room.userId === state.userId;
  98. if (state.event && state.event.type === SyncEventType.ET_RemoveVisitor)
  99. {
  100. const removeVisitorEvent = state.event.removeVisitorEvent
  101. const event = removeVisitorEvent && removeVisitorEvent.removeVisitorEvent
  102. , extraInfo = JSON.parse(safeDecodeURIComponent((removeVisitorEvent && removeVisitorEvent.extraInfo) || ""))
  103. , { code, msg } = extraInfo;
  104. event === RemoveVisitorType.RVT_ChangeToObserver
  105. ? this._room.audienceViewModeHook()
  106. : event === RemoveVisitorType.RVT_MoveOutOfTheRoom && this._room.leave() // 这里会触发Actions.Exit
  107. this._room.emit("visitorStatusChanged", { code, msg })
  108. }
  109. if (state.event && [SyncEventType.Appear, SyncEventType.Reset].includes(state.event.type) || !state.event) {
  110. let avatar = this.avatars.get(state.userId);
  111. if(state.playerState.avatarId && avatar && avatar.avatarId !== state.playerState.avatarId) {
  112. avatar = void 0
  113. this.removeAvatar(state.userId)
  114. }
  115. if (avatar) {
  116. avatar.disconnected && avatar.setConnectionStatus(!1)
  117. state.event && state.event.id && this._room.actionsHandler.confirmEvent(state.event.id) // 这里会触发Actions.ConfirmEvent
  118. state.playerState.nickName && (avatar && avatar._setNickname(state.playerState.nickName))
  119. if (state.playerState.avatarComponents && !avatar.isSelf && avatar.xAvatar) {
  120. const avatarComponents = safeParseComponents(state.playerState.avatarComponents);
  121. // 这里会触发Actions.SetPlayerState
  122. avatar._changeComponents({
  123. avatarComponents,
  124. mode: ChangeComponentsMode.Preview
  125. })
  126. }
  127. } else {
  128. const {position, angle} = state.playerState.player
  129. , avatarId = state.playerState.avatarId
  130. , prioritySync = state.playerState.prioritySync
  131. , extraInfo = safelyJsonParse(state.playerState.extra);
  132. if (!avatarId) return;
  133. const avatarComponents = safeParseComponents(state.playerState.avatarComponents)
  134. , nickname = safeDecodeURIComponent(state.playerState.nickName)
  135. , priority = this.calculatePriority(state.userId, extraInfo);
  136. this.addAvatar({
  137. userId: state.userId,
  138. isHost: state.playerState.isHost,
  139. nickname,
  140. avatarPosition: position,
  141. avatarRotation: angle,
  142. avatarScale: state.playerState.avatarSize,
  143. avatarId,
  144. avatarComponents: state.playerState.person === Person.First ? [] : avatarComponents,
  145. priority,
  146. group: AvatarGroup.User,
  147. prioritySync,
  148. extraInfo
  149. }).then(()=>{
  150. state.event && state.event.id && this._room.actionsHandler.confirmEvent(state.event.id), // 这里会触发Actions.ConfirmEvent
  151. this.updateAvatarPositionAndRotation(state),
  152. isMainAvatar && (
  153. this.xAvatarManager.setMainAvatar(state.userId),
  154. this._room.emit("userAvatarLoaded"),
  155. logger.info("userAvatarLoaded")
  156. )
  157. }).catch(e=>{
  158. isMainAvatar && (
  159. this.xAvatarManager.setMainAvatar(state.userId),
  160. this._room.emit("userAvatarFailed", {error: e}),
  161. logger.error("userAvatarFailed", e)
  162. )
  163. })
  164. }
  165. }
  166. if(state.event && SyncEventType.Disappear === state.event.type) {
  167. state.event.id && this._room.actionsHandler.confirmEvent(state.event.id) // 这里会触发Actions.ConfirmEvent
  168. this.removeAvatar(state.userId)
  169. }
  170. if (state.event && [SyncEventType.Move, SyncEventType.ChangeRenderInfo].includes(state.event.type) || !state.event) {
  171. state.event && state.event.id && this._room.actionsHandler.confirmEvent(state.event.id); // 这里会触发Actions.ConfirmEvent
  172. const avatar = this.avatars.get(state.userId);
  173. avatar && avatar.withModel && !avatar.isLoading && this.updateAvatarPositionAndRotation(state)
  174. }
  175. if (!isMainAvatar && state.event && state.event.type === SyncEventType.Rotate) {
  176. const avatar = this.avatars.get(state.userId);
  177. avatar.statusSyncQueue.append({
  178. type: QueueType.Rotate,
  179. action: () => avatar.statusSync(state)
  180. })
  181. }
  182. })
  183. }
  184. calculatePriority(e, t) {
  185. var n;
  186. return e === this._room.userId ? EAvatarRelationRank.Self : (n = this._room.options.firends) != null && n.includes(e) ? EAvatarRelationRank.Friend : EAvatarRelationRank.Stranger
  187. }
  188. updateAvatarPositionAndRotation(userState)
  189. {
  190. if (userState.playerState && userState.playerState.player)
  191. {
  192. let {position, angle} = userState.playerState.player;
  193. const avatar = this.avatars.get(userState.userId);
  194. if (!avatar) return;
  195. position = positionPrecisionProtect(position)
  196. angle = rotationPrecisionProtect(angle)
  197. avatar.isSelf && !this._room.networkController.rtcp.workers.inPanoMode && (avatar.setPosition(position), avatar.setRotation(angle))
  198. userState.event && (userState.event.points.length || 0) > 1 && !avatar.isSelf && avatar.statusSyncQueue.append({
  199. type: QueueType.Move,
  200. action: () => avatar.statusSync(userState)
  201. })
  202. if (userState.renderInfo && avatar.isSelf) {
  203. const {isMoving, isRotating} = userState.renderInfo;
  204. this._updateAvatarMovingStatus({
  205. id: userState.userId,
  206. isMoving: !!isMoving,
  207. isRotating: !!isRotating
  208. })
  209. }
  210. }
  211. }
  212. async addAvatar({
  213. userId, isHost, avatarPosition, avatarId, avatarRotation, nickname, avatarComponents=[], priority, group=AvatarGroup.Npc,
  214. avatarScale=DEFAULT_AVATAR_SCALE, extraInfo, prioritySync
  215. }) {
  216. const isSelf = userId === this._room.userId;
  217. let avatar = this.avatars.get(userId);
  218. if (avatar) return Promise.resolve(avatar);
  219. avatar = new XverseAvatarManager.subAvatar({
  220. userId,
  221. isHost,
  222. isSelf,
  223. room: this._room,
  224. avatarComponents,
  225. avatarId,
  226. nickname,
  227. group
  228. });
  229. this.avatars.set(userId, avatar);
  230. if (!avatar.withModel) {
  231. avatar.isLoading = !1
  232. avatar.avatarLoadedHook()
  233. this._room.emit("avatarChanged", { avatars: this._room.avatars })
  234. return avatar;
  235. }
  236. const avatarData = (await this._room.modelManager.getAvatarModelList()).find(data => data.id === avatarId)
  237. , startTime = Date.now();
  238. if (!avatarData) {
  239. this._room.emit("avatarChanged", { avatars: this._room.avatars })
  240. this.avatars.delete(userId)
  241. return Promise.reject(`no such avatar model with id: ${avatarId}`);
  242. }
  243. try {
  244. let b = await avatarComponentsModify(avatarData, avatarComponents);
  245. b = b.filter(A=>A.type != "pendant");
  246. const assets = await avatarComponentsParser(avatarData, b)
  247. , xAvatar = await this.xAvatarManager.loadAvatar({
  248. id: userId,
  249. avatarType: avatarId,
  250. priority,
  251. avatarManager: this.xAvatarManager,
  252. assets,
  253. status: {
  254. avatarPosition,
  255. avatarRotation,
  256. avatarScale
  257. }
  258. })._timeout(8e3, new TimeoutError("loadAvatar timeout(8s)"));
  259. xAvatar.setPickBoxScale(userId === this._room.userId ? 0 : 1);
  260. avatar.xAvatar = xAvatar
  261. avatar.setScale(avatarScale)
  262. avatar.extraInfo = extraInfo
  263. avatar.priority = priority
  264. avatar.isLoading = !1
  265. avatar.prioritySync = !!prioritySync
  266. avatar._playAnimation("Idle", !0, !0)
  267. avatar.avatarLoadedHook()
  268. this._room.emit("avatarChanged", { avatars: this._room.avatars })
  269. nickname && avatar._setNickname(nickname)
  270. userId === this._room.userId && (
  271. logger.infoAndReportMeasurement({
  272. metric: "avatarLoadDuration",
  273. startTime,
  274. group: "costs"
  275. }),
  276. logger.infoAndReportMeasurement({
  277. metric: "avatarLoadAt",
  278. startTime: this._room._startTime,
  279. group: "costs"
  280. })
  281. )
  282. return avatar
  283. } catch (e) {
  284. return avatar.isLoading = !1,
  285. this._room.emit("avatarChanged", { avatars: this._room.avatars }),
  286. logger.error(e),
  287. Promise.reject(e)
  288. }
  289. }
  290. removeAvatar(userId, t=!1) {
  291. const avatar = this.avatars.get(userId);
  292. if (!!avatar) {
  293. if (avatar.removeWhenDisconnected || t) {
  294. avatar.xAvatar && this.xAvatarManager.deleteAvatar(avatar.xAvatar),
  295. this.avatars.delete(userId),
  296. this._room.emit("avatarChanged", {
  297. avatars: this._room.avatars
  298. });
  299. return
  300. }
  301. avatar.setConnectionStatus(!0)
  302. }
  303. }
  304. clearOtherUsers() {
  305. this.avatars.forEach(e=>{
  306. !e.isSelf && e.group === AvatarGroup.User && this.removeAvatar(e.userId)
  307. }
  308. )
  309. }
  310. async _updateAvatarMovingStatus(e) {
  311. const {id: t, isMoving: r, isRotating: n} = e
  312. , o = this.avatars.get(t);
  313. if (!!o) {
  314. if (o.isRotating !== n) {
  315. o.isRotating = n;
  316. let a = "Idle";
  317. n && (a = "Walking",
  318. o.motionType === MotionType.Run && (a = "Running")),
  319. o._playAnimation(a, !0, !0),
  320. logger.infoAndReportMeasurement({
  321. startTime: Date.now(),
  322. value: 0,
  323. metric: n ? "userAvatarStartRotating" : "userAvatarStopRotating",
  324. extra: {
  325. motionType: o.motionType,
  326. moveToExtra: this._room.moveToExtra
  327. }
  328. })
  329. }
  330. if (o.isMoving !== r) {
  331. o.isMoving = r;
  332. let a = "Idle";
  333. r && (a = "Walking",
  334. o.motionType === MotionType.Run && (a = "Running")),
  335. r ? (o.avatarStartMovingHook(),
  336. o.emit("startMoving", {
  337. target: o,
  338. extra: this._room.moveToExtra
  339. })) : (o.avatarStopMovingHook(),
  340. o.emit("stopMoving", {
  341. target: o,
  342. extra: this._room.moveToExtra
  343. })),
  344. o._playAnimation(a, !0, !0),
  345. logger.infoAndReportMeasurement({
  346. startTime: Date.now(),
  347. value: 0,
  348. metric: r ? "userAvatarStartMoving" : "userAvatarStopMoving",
  349. extra: {
  350. motionType: o.motionType,
  351. moveToExtra: this._room.moveToExtra
  352. }
  353. })
  354. }
  355. }
  356. }
  357. _usersStatistics() {
  358. this.on("userAvatarLoaded", ()=>{
  359. window.setInterval(()=>{
  360. const e = this._room.avatars.filter(r=>r.group === AvatarGroup.User).length || 0
  361. , t = this._room.avatars.filter(r=>r.group === AvatarGroup.User && r.isRender).length || 0;
  362. this._room.stats.assign({
  363. userNum: e,
  364. syncUserNum: this.syncAvatarsLength,
  365. renderedUserNum: t
  366. })
  367. }
  368. , 3e3)
  369. }
  370. )
  371. }
  372. };
  373. E(XverseAvatarManager, "subAvatar", XverseAvatar);