import CoreBroadcastType from "./enum/CoreBroadcastType.js" import AvatarGroup from "./enum/AvatarGroup.js" import Broadcast from "./Broadcast.js" import {avatarLoader} from "./XAvatarLoader.js" import SyncEventType from "./enum/SyncEventType.js" import GetStateTypes from "./enum/GetStateTypes.js" import EAvatarRelationRank from "./enum/EAvatarRelationRank.js" import Person from "./enum/Person.js" import XverseAvatar from "./XverseAvatar.js" import MotionType from "./enum/MotionType.js" import TimeoutError from "./error/TimeoutError.js" import Logger from "./Logger.js" const logger = new Logger('xverse-avatar-manager') export default class XverseAvatarManager extends EventEmitter { constructor(e) { super(); this.xAvatarManager = null this.avatars = new Map this.syncAvatarsLength = 0 this._room = e; this._usersStatistics(); this.broadcast = this.setupBroadcast(); e.on("skinChanged", ()=>{ this.avatars.forEach(t=>{ t.disconnected && this.removeAvatar(t.userId, !0) } ) } ) } setupBroadcast() { return new Broadcast(this._room,async e=>{ const {broadcastType: t, info: r} = e; if (t !== CoreBroadcastType.PlayAnimation) return; const {userId: n, animation: o, extra: a, loop: s=!1} = r , l = this.avatars.get(n); l && !l.isSelf && (l.emit("animationStart", { animationName: o, extra: decodeURIComponent(a) }), await (l == null ? void 0 : l._playAnimation(o, s)), l.emit("animationEnd", { animationName: o, extra: decodeURIComponent(a) })) } ) } hideAll(e=!0) { this.xAvatarManager.hideAll(e) } showAll(e=!0) { this.xAvatarManager.showAll(e) } async init() { this.xAvatarManager = this._room.sceneManager.avatarComponent; try { const e = await this._room.modelManager.getApplicationConfig() , {avatars: t} = e; if (t) { await avatarLoader.parse(this._room.sceneManager, t); return } return Promise.reject("cannot find avatar config list") } catch (e) { return logger.error(e), Promise.reject("avatar mananger init error!") } } async handleAvatar(e) { var r; if (this._room.viewMode === "simple" || !this._room.joined || !e.newUserStates) return; let t = e.newUserStates; if (((r = this._room._userAvatar) == null ? void 0 : r.isMoving) && this._room._userAvatar.motionType === MotionType.Run) { const n = t.filter(a=>a.userId === this._room.userId) , o = t.filter(a=>a.userId !== this._room.userId).slice(0, 2); t = n.concat(o) } if (e.getStateType === GetStateTypes.Event) { this.syncAvatarsLength = (t || []).length; const n = this._room.avatars.filter(s=>s.group == AvatarGroup.User); n.filter(s=>!(t != null && t.find(l=>l.userId == s.userId))).forEach(s=>{ this.removeAvatar(s.userId) } ); const a = t.filter(s=>!n.find(l=>l.userId == s.userId)); this._handleAvatar(a) } this._handleAvatar(t) } async _handleAvatar(e) { e == null || e.forEach(t=>{ var n, o, a, s, l, u, c, h, f; const r = this._room.userId === t.userId; if (((n = t.event) == null ? void 0 : n.type) === SyncEventType.ET_RemoveVisitor) { const d = (a = (o = t.event) == null ? void 0 : o.removeVisitorEvent) == null ? void 0 : a.removeVisitorEvent , _ = JSON.parse(safeDecodeURIComponent(((l = (s = t.event) == null ? void 0 : s.removeVisitorEvent) == null ? void 0 : l.extraInfo) || "")) , {code: g, msg: m} = _; d === RemoveVisitorType.RVT_ChangeToObserver ? this._room.audienceViewModeHook() : d === RemoveVisitorType.RVT_MoveOutOfTheRoom && this._room.leave(), this._room.emit("visitorStatusChanged", { code: g, msg: m }) } if (t.event && [SyncEventType.Appear, SyncEventType.Reset].includes(t.event.type) || !t.event) { let d = this.avatars.get(t.userId); t.playerState.avatarId && (d == null ? void 0 : d.avatarId) !== t.playerState.avatarId && (d = void 0,this.removeAvatar(t.userId)); if (d) { if (d.disconnected && d.setConnectionStatus(!1), (u = t.event) != null && u.id && this._room.actionsHandler.confirmEvent(t.event.id), t.playerState.nickName && (d == null || d._setNickname(t.playerState.nickName)), t.playerState.avatarComponents && !d.isSelf && d.xAvatar) { const _ = safeParseComponents(t.playerState.avatarComponents); d._changeComponents({ avatarComponents: _, mode: ChangeComponentsMode.Preview }) } } else { const {position: _, angle: g} = t.playerState.player , m = t.playerState.avatarId , v = t.playerState.prioritySync , y = safelyJsonParse(t.playerState.extra); if (!m) return; const b = safeParseComponents(t.playerState.avatarComponents) , T = safeDecodeURIComponent(t.playerState.nickName) , C = this.calculatePriority(t.userId, y); this.addAvatar({ userId: t.userId, isHost: t.playerState.isHost, nickname: T, avatarPosition: _, avatarRotation: g, avatarScale: t.playerState.avatarSize, avatarId: m, avatarComponents: t.playerState.person === Person.First ? [] : b, priority: C, group: AvatarGroup.User, prioritySync: v, extraInfo: y }).then(()=>{ var A; (A = t.event) != null && A.id && this._room.actionsHandler.confirmEvent(t.event.id), this.updateAvatarPositionAndRotation(t), r && (this.xAvatarManager.setMainAvatar(t.userId), this._room.emit("userAvatarLoaded"), logger.info("userAvatarLoaded")) } ).catch(A=>{ r && (this.xAvatarManager.setMainAvatar(t.userId), this._room.emit("userAvatarFailed", { error: A }), logger.error("userAvatarFailed", A)) } ) } } if (t.event && SyncEventType.Disappear === t.event.type && ((c = t == null ? void 0 : t.event) != null && c.id && this._room.actionsHandler.confirmEvent(t.event.id), this.removeAvatar(t.userId)), t.event && [SyncEventType.Move, SyncEventType.ChangeRenderInfo].includes(t.event.type) || !t.event) { (h = t == null ? void 0 : t.event) != null && h.id && this._room.actionsHandler.confirmEvent(t.event.id); const d = this.avatars.get(t.userId); d && d.withModel && !d.isLoading && this.updateAvatarPositionAndRotation(t) } if (!r && ((f = t.event) == null ? void 0 : f.type) === SyncEventType.Rotate) { const d = this.avatars.get(t.userId); d.statusSyncQueue.append({ type: QueueType.Rotate, action: ()=>d.statusSync(t) }) } } ) } calculatePriority(e, t) { var n; return e === this._room.userId ? EAvatarRelationRank.Self : (n = this._room.options.firends) != null && n.includes(e) ? EAvatarRelationRank.Friend : EAvatarRelationRank.Stranger } updateAvatarPositionAndRotation(e) { var t, r; if ((t = e == null ? void 0 : e.playerState) != null && t.player) { let {position: n, angle: o} = e.playerState.player; const a = this.avatars.get(e.userId); if (!a) return; if (n = positionPrecisionProtect(n), o = rotationPrecisionProtect(o), a.isSelf && !this._room.networkController.rtcp.workers.inPanoMode && (a.setPosition(n), a.setRotation(o)), e.event && (((r = e.event) == null ? void 0 : r.points.length) || 0) > 1 && !a.isSelf && a.statusSyncQueue.append({ type: QueueType.Move, action: ()=>a.statusSync(e) }), e.renderInfo && a.isSelf) { const {isMoving: s, isRotating: l} = e.renderInfo; this._updateAvatarMovingStatus({ id: e.userId, isMoving: !!s, isRotating: !!l }) } } } async addAvatar(e) { const {userId: t, isHost: r, avatarPosition: n, avatarId: o, avatarRotation: a, nickname: s, avatarComponents: l=[], priority: u, group: c=AvatarGroup.Npc, avatarScale: h=DEFAULT_AVATAR_SCALE, extraInfo: f, prioritySync: d} = e , _ = t === this._room.userId; let g = this.avatars.get(t); if (g) { return Promise.resolve(g); } g = new XverseAvatarManager.subAvatar({ userId: t, isHost: r, isSelf: _, room: this._room, avatarComponents: l, avatarId: o, nickname: s, group: c }); this.avatars.set(t, g); if (!g.withModel) return g.isLoading = !1, g.avatarLoadedHook(), this._room.emit("avatarChanged", { avatars: this._room.avatars }), g; const v = (await this._room.modelManager.getAvatarModelList()).find(b=>b.id === o) , y = Date.now(); if (!v) return this._room.emit("avatarChanged", { avatars: this._room.avatars }), this.avatars.delete(t), Promise.reject(`no such avatar model with id: ${o}`); try { let b = await avatarComponentsModify(v, l); b = b.filter(A=>A.type != "pendant"); const T = await avatarComponentsParser(v, b) , C = await this.xAvatarManager.loadAvatar({ id: t, avatarType: o, priority: u, avatarManager: this.xAvatarManager, assets: T, status: { avatarPosition: n, avatarRotation: a, avatarScale: h } })._timeout(8e3, new TimeoutError("loadAvatar timeout(8s)")); return C.setPickBoxScale(t === this._room.userId ? 0 : 1), g.xAvatar = C, g.setScale(h), g.extraInfo = f, g.priority = u, g.isLoading = !1, g.prioritySync = !!d, g._playAnimation("Idle", !0, !0), g.avatarLoadedHook(), this._room.emit("avatarChanged", { avatars: this._room.avatars }), s && g._setNickname(s), t === this._room.userId && (logger.infoAndReportMeasurement({ metric: "avatarLoadDuration", startTime: y, group: "costs" }), logger.infoAndReportMeasurement({ metric: "avatarLoadAt", startTime: this._room._startTime, group: "costs" })), g } catch (b) { return g.isLoading = !1, this._room.emit("avatarChanged", { avatars: this._room.avatars }), logger.error(b), Promise.reject(b) } } removeAvatar(e, t=!1) { const r = this.avatars.get(e); if (!!r) { if (r.removeWhenDisconnected || t) { r.xAvatar && this.xAvatarManager.deleteAvatar(r.xAvatar), this.avatars.delete(e), this._room.emit("avatarChanged", { avatars: this._room.avatars }); return } r.setConnectionStatus(!0) } } clearOtherUsers() { this.avatars.forEach(e=>{ !e.isSelf && e.group === AvatarGroup.User && this.removeAvatar(e.userId) } ) } async _updateAvatarMovingStatus(e) { const {id: t, isMoving: r, isRotating: n} = e , o = this.avatars.get(t); if (!!o) { if (o.isRotating !== n) { o.isRotating = n; let a = "Idle"; n && (a = "Walking", o.motionType === MotionType.Run && (a = "Running")), o._playAnimation(a, !0, !0), logger.infoAndReportMeasurement({ startTime: Date.now(), value: 0, metric: n ? "userAvatarStartRotating" : "userAvatarStopRotating", extra: { motionType: o.motionType, moveToExtra: this._room.moveToExtra } }) } if (o.isMoving !== r) { o.isMoving = r; let a = "Idle"; r && (a = "Walking", o.motionType === MotionType.Run && (a = "Running")), r ? (o.avatarStartMovingHook(), o.emit("startMoving", { target: o, extra: this._room.moveToExtra })) : (o.avatarStopMovingHook(), o.emit("stopMoving", { target: o, extra: this._room.moveToExtra })), o._playAnimation(a, !0, !0), logger.infoAndReportMeasurement({ startTime: Date.now(), value: 0, metric: r ? "userAvatarStartMoving" : "userAvatarStopMoving", extra: { motionType: o.motionType, moveToExtra: this._room.moveToExtra } }) } } } _usersStatistics() { this.on("userAvatarLoaded", ()=>{ window.setInterval(()=>{ const e = this._room.avatars.filter(r=>r.group === AvatarGroup.User).length || 0 , t = this._room.avatars.filter(r=>r.group === AvatarGroup.User && r.isRender).length || 0; this._room.stats.assign({ userNum: e, syncUserNum: this.syncAvatarsLength, renderedUserNum: t }) } , 3e3) } ) } }; E(XverseAvatarManager, "subAvatar", XverseAvatar);