XverseAvatar.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515
  1. import util from "./util.js"
  2. import InternalError from "./error/InternalError.js"
  3. import EAvatarRelationRank from "./enum/EAvatarRelationRank.js"
  4. import AvatarGroup from "./enum/AvatarGroup.js"
  5. import MotionType from "./enum/MotionType.js"
  6. import Queue from "./Queue.js"
  7. import Logger from "./Logger.js"
  8. import CoreBroadcastType from "./enum/CoreBroadcastType.js"
  9. const logger = new Logger('4DMVS_Character')
  10. export default class XverseAvatar extends EventEmitter {
  11. constructor({userId, isHost, room, avatarId, isSelf, group=AvatarGroup.Npc}) {
  12. super();
  13. this.xAvatar
  14. this.state = "idle"
  15. this.isLoading = !0
  16. this._isMoving = !1
  17. this._isRotating = !1
  18. this._failed = !1
  19. this.disconnected = !1
  20. this._avatarId
  21. this.prioritySync = !1
  22. this.priority = EAvatarRelationRank.Stranger
  23. this._avatarModel
  24. this._motionType = MotionType.Walk
  25. this._lastAnimTraceId = ""
  26. this.statusSyncQueue = new Queue
  27. this.extraInfo = {}
  28. this.sayTimer
  29. this._userId = userId,
  30. this._room = room,
  31. this.isSelf = isSelf || !1,
  32. this._withModel = !!avatarId,
  33. this._isHost = isHost || !1,
  34. this._avatarId = avatarId,
  35. this.group = group,
  36. this._room.modelManager.getAvatarModelList().then(list=>{
  37. const avatarModel = list.find(u=>u.id === avatarId);
  38. avatarModel && (this._avatarModel = avatarModel)
  39. })
  40. }
  41. setPosition = pos => {
  42. !this._room.signal.isUpdatedYUV || this.xAvatar == null || this.xAvatar.setPosition(positionPrecisionProtect(pos))
  43. }
  44. setRotation = rota => {
  45. !this._room.signal.isUpdatedYUV || this.xAvatar == null || this.xAvatar.setRotation(rotationPrecisionProtect(rota))
  46. }
  47. stopAnimation = ()=>{
  48. (this.xAvatar == null ? void 0 : this.xAvatar.controller) == null || this.xAvatar.controller.stopAnimation()
  49. }
  50. _playAnimation = async(animationName, isLoop=!0, r=!1)=>{
  51. if (!this._room.signal.isUpdatedYUV) return;
  52. if (this.state !== "idle" && !r)
  53. return logger.debug("_playAnimation", "state is not idle"),
  54. Promise.resolve("_playAnimation, state is not idle");
  55. const startTime = Date.now();
  56. try {
  57. if (!(this.xAvatar != null && this.xAvatar.controller))
  58. return Promise.reject(new InternalError(`[avatar: ${this.userId}] Play animation failed: ${animationName}, no controller`));
  59. this.isSelf && setTimeout(()=>{
  60. logger.infoAndReportMeasurement({
  61. tag: animationName,
  62. value: 0,
  63. type: "playAnimationStart"
  64. })
  65. });
  66. const uuid = util.uuid();
  67. this._lastAnimTraceId = uuid,
  68. await this.xAvatar.controller.playAnimation(animationName, isLoop),
  69. uuid === this._lastAnimTraceId && !this.isMoving && !isLoop && animationName !== "Idle" && this.xAvatar.controller.playAnimation("Idle", isLoop).catch(s=>{
  70. logger.error(`[avatar: ${this.userId}] Play animation failed [force idle]`, s)
  71. }
  72. ),
  73. this.isSelf && logger.infoAndReportMeasurement({
  74. tag: animationName,
  75. extraData: {
  76. loop: isLoop
  77. },
  78. type: "playAnimationEnd"
  79. })
  80. } catch (err) {
  81. return logger.error(`[avatar: ${this.userId}] Play animation failed: ${animationName}`, err),
  82. this.isSelf && logger.infoAndReportMeasurement({
  83. tag: animationName,
  84. type: "playAnimationEnd",
  85. error: err,
  86. extraData: {
  87. loop: isLoop
  88. }
  89. }),
  90. Promise.reject(err)
  91. }
  92. }
  93. changeComponents = async data => {
  94. const {mode, endAnimation=""} = data || {}
  95. , avatarComponents = JSON.parse(JSON.stringify(data.avatarComponents));
  96. let o = avatarComponentsValidate(avatarComponents, this._avatarModel);
  97. !ChangeComponentsMode[mode] && !o && (o = new ParamError(`changeComponents failed, mode: ${mode} is invalid`))
  98. return o ? (logger.error(o), Promise.reject(o))
  99. : this._changeComponents({ avatarComponents, mode, endAnimation }).then(()=>{
  100. this.isSelf && mode !== ChangeComponentsMode.Preview && this.avatarComponentsSync(this.avatarComponents)
  101. })
  102. }
  103. _changeComponents = async data=>{
  104. const {avatarComponents=[], mode} = data || {}
  105. , startTime = Date.now();
  106. try {
  107. if (!this.xAvatar) return Promise.reject(new InternalError("changeComponents failed, without instance: xAvatar"));
  108. const a = await avatarComponentsModify(this._avatarModel, avatarComponents)
  109. , s = []
  110. , l = await avatarComponentsParser(this._avatarModel, a, this.avatarComponents);
  111. if (l.length === 0) return this.avatarComponents;
  112. await this.beforeChangeComponentsHook(data);
  113. for (const u of l) {
  114. const {id, type, url, suitComb} = u;
  115. s.push(this.xAvatar == null ? void 0 : this.xAvatar.addComponent(id, type, url, suitComb))
  116. }
  117. await Promise.all(s)
  118. this.emit("componentsChanged", {
  119. components: this.avatarComponents,
  120. mode
  121. })
  122. this.isSelf && logger.infoAndReportMeasurement({
  123. tag: "changeComponents",
  124. type: "changeComponents",
  125. extraData: {
  126. inputComponents: avatarComponents,
  127. finalComponents: this.avatarComponents,
  128. mode: ChangeComponentsMode[mode]
  129. }
  130. })
  131. return this.avatarComponents
  132. } catch (error) {
  133. this.isSelf && logger.infoAndReportMeasurement({
  134. tag: "changeComponents",
  135. type: "changeComponents",
  136. error,
  137. extraData: {
  138. inputComponents: avatarComponents,
  139. finalComponents: this.avatarComponents,
  140. mode: ChangeComponentsMode[mode]
  141. }
  142. })
  143. return Promise.reject(error)
  144. }
  145. }
  146. avatarComponentsSync = e=>{
  147. e = e.map(t=>({
  148. type: t.type,
  149. id: t.id
  150. })),
  151. this._room.actionsHandler.avatarComponentsSync(e)
  152. }
  153. hide = ()=>{
  154. var e;
  155. if ((e = this.xAvatar) != null && e.hide())
  156. return Promise.resolve(`avatar: ${this.userId} hide success`);
  157. {
  158. const t = `avatar: ${this.userId} hide failed ${!this.xAvatar && "without instance: xAvatar"}`;
  159. return logger.warn(t),
  160. Promise.reject(t)
  161. }
  162. }
  163. show = ()=>{
  164. var e;
  165. if ((e = this.xAvatar) != null && e.show())
  166. return Promise.resolve(`avatar: ${this.userId} show success`);
  167. {
  168. const t = `avatar: ${this.userId} show failed ${!this.xAvatar && "without instance: xAvatar"}`;
  169. return logger.warn(t),
  170. Promise.reject(t)
  171. }
  172. }
  173. get avatarId() {
  174. return this._avatarId
  175. }
  176. get isRender() {
  177. var e;
  178. return !!((e = this.xAvatar) != null && e.isRender)
  179. }
  180. get isHidden() {
  181. var e;
  182. return !!((e = this.xAvatar) != null && e.isHide)
  183. }
  184. get motionType() {
  185. return this._motionType
  186. }
  187. set motionType(e) {
  188. this._motionType = e
  189. }
  190. get nickname() {
  191. var e;
  192. return (e = this.xAvatar) == null ? void 0 : e.nickName
  193. }
  194. get words() {
  195. var e;
  196. return (e = this.xAvatar) == null ? void 0 : e.words
  197. }
  198. get isHost() {
  199. return this._isHost
  200. }
  201. get failed() {
  202. return this._failed
  203. }
  204. get scale() {
  205. var e;
  206. return (e = this.xAvatar) == null ? void 0 : e.scale
  207. }
  208. get animations() {
  209. var e;
  210. return !this.xAvatar || !this.xAvatar.controller ? [] : ((e = this.xAvatar) == null ? void 0 : e.getAvaliableAnimations()) || []
  211. }
  212. get position() {
  213. var e;
  214. return (e = this.xAvatar) == null ? void 0 : e.position
  215. }
  216. get rotation() {
  217. var e;
  218. return (e = this.xAvatar) == null ? void 0 : e.rotation
  219. }
  220. get pose() {
  221. return {
  222. position: this.position,
  223. angle: this.rotation
  224. }
  225. }
  226. get id() {
  227. return this.userId
  228. }
  229. get isMoving() {
  230. return this._isMoving
  231. }
  232. set isMoving(e) {
  233. this._isMoving = e,
  234. this.state = e ? "moving" : "idle"
  235. }
  236. get isRotating() {
  237. return this._isRotating
  238. }
  239. set isRotating(e) {
  240. this._isRotating = e,
  241. this.state = e ? "rotating" : "idle"
  242. }
  243. get withModel() {
  244. return this._withModel
  245. }
  246. get avatarComponents() {
  247. var e;
  248. return JSON.parse(JSON.stringify(((e = this.xAvatar) == null ? void 0 : e.clothesList) || []))
  249. }
  250. get userId() {
  251. return this._userId
  252. }
  253. get removeWhenDisconnected() {
  254. return this.extraInfo && this.extraInfo.removeWhenDisconnected !== void 0 ? this.extraInfo.removeWhenDisconnected : !0
  255. }
  256. setConnectionStatus(e) {
  257. this.disconnected !== e && (this.disconnected = e,
  258. e ? this.emit("disconnected") : this.emit("reconnected"),
  259. logger.warn(`avatar ${this.userId} status changed, disconnected:`, e))
  260. }
  261. setScale(scaleNum) {
  262. this.xAvatar == null || this.xAvatar.setScale(scaleNum > 0 ? scaleNum : 1)
  263. }
  264. async playAnimation(aniData) {
  265. const {animationName, loop: isLoop, extra} = aniData || {};
  266. if (this.isSelf) {
  267. if (this.isMoving)
  268. try {
  269. await this.stopMoving()
  270. } catch (err) {
  271. return logger.error(`stopMoving error before playAnimation ${animationName}`, err),
  272. Promise.reject(`stopMoving error before playAnimation ${animationName}`)
  273. }
  274. const data = {
  275. info: {
  276. userId: this.userId,
  277. animation: animationName,
  278. loop: isLoop,
  279. extra: encodeURIComponent(extra || "")
  280. },
  281. broadcastType: CoreBroadcastType.PlayAnimation
  282. };
  283. this._room.avatarManager.broadcast.broadcast({ data })
  284. }
  285. return this.emit("animationStart", {
  286. animationName,
  287. extra: safeDecodeURIComponent(extra || "")
  288. }),
  289. this._playAnimation(animationName, isLoop).then(()=>{
  290. this.emit("animationEnd", {
  291. animationName,
  292. extra: safeDecodeURIComponent(extra || "")
  293. })
  294. })
  295. }
  296. async beforeChangeComponentsHook(e) {}
  297. turnTo(e) {
  298. if (this._room.viewMode === "observer") {
  299. this._room.sceneManager.cameraComponent.MainCamera.setTarget(ue4Position2Xverse(e.point));
  300. return
  301. }
  302. return this._room.actionsHandler.turnTo(e).then(()=>{
  303. this.emit("viewChanged", {
  304. extra: (e == null ? void 0 : e.extra) || ""
  305. })
  306. })
  307. }
  308. async moveTo(e) {
  309. const {point: t, extra: r=""} = e || {};
  310. if (!this.position)
  311. return Promise.reject(new ParamError("avatar position is empty"));
  312. if (typeof r != "string" || typeof r == "string" && r.length > 64) {
  313. const a = "extra shoud be string which length less than 64";
  314. return logger.warn(a),
  315. Promise.reject(new ParamError(a))
  316. }
  317. const o = util.getDistance(this.position, t) / 100 > 100 ? MotionType.Run : MotionType.Walk;
  318. return this._room.actionsHandler.moveTo({
  319. point: t,
  320. motionType: o,
  321. extra: r
  322. })
  323. }
  324. async stopMoving() {
  325. return this._room.actionsHandler.stopMoving()
  326. }
  327. rotateTo(e) {
  328. return this._room.actionsHandler.rotateTo(e)
  329. }
  330. setRayCast(e) {
  331. this.xAvatar && (this.xAvatar.isRayCastEnable = e)
  332. }
  333. say(e, t) {
  334. if (this.sayTimer && window.clearTimeout(this.sayTimer),
  335. !this.xAvatar) {
  336. logger.error("say failed, without instance: xAvatar");
  337. return
  338. }
  339. this.xAvatar.say(e, {
  340. scale: this.xAvatar.scale,
  341. isUser: this.group === AvatarGroup.User
  342. }),
  343. !(t === void 0 || t <= 0) && (this.sayTimer = window.setTimeout(()=>{
  344. this.silent()
  345. }
  346. , t))
  347. }
  348. silent() {
  349. var e;
  350. if (!this.xAvatar) {
  351. logger.error("silent failed, without instance: xAvatar");
  352. return
  353. }
  354. (e = this.xAvatar) == null || e.silent()
  355. }
  356. setMotionType({type: e=MotionType.Walk}) {
  357. return this.motionType === e ? Promise.resolve() : this._room.actionsHandler.setMotionType(e).then(()=>{
  358. this._motionType = e
  359. }
  360. )
  361. }
  362. setNickname(e) {
  363. return this._room.actionsHandler.setNickName(encodeURIComponent(e))
  364. }
  365. _setNickname(e) {
  366. var r, n;
  367. if (!e)
  368. return;
  369. const t = safeDecodeURIComponent(e);
  370. ((r = this.xAvatar) == null ? void 0 : r.nickName) !== t && (this.isSelf && (this._room.updateCurrentNetworkOptions({
  371. nickname: t
  372. }),
  373. this._room.options.nickname = t),
  374. (n = this.xAvatar) == null || n.setNickName(t, {
  375. scale: this.xAvatar.scale
  376. }))
  377. }
  378. _move(e) {
  379. var s;
  380. const {start: t, end: r, walkSpeed: n, moveAnimation: o="Walking", inter: a=[]} = e || {};
  381. return (s = this.xAvatar) == null ? void 0 : s.move(t, r, n, o, a)
  382. }
  383. setPickBoxScale(e=1) {
  384. return this.xAvatar ? (this.xAvatar.setPickBoxScale(e),
  385. !0) : (logger.error("setPickBoxScale failed, without instance: xAvatar"),
  386. !1)
  387. }
  388. transfer(e) {
  389. const {player: t, camera: r, areaName: n, attitude: o, pathName: a} = e;
  390. return this._room.actionsHandler.transfer({
  391. renderType: RenderType.RotationVideo,
  392. player: t,
  393. camera: r,
  394. areaName: n,
  395. attitude: o,
  396. pathName: a,
  397. tag: "transfer"
  398. })
  399. }
  400. avatarLoadedHook() {}
  401. avatarStartMovingHook() {}
  402. avatarStopMovingHook() {}
  403. // 用于更新非MainAvatar的其他角色
  404. async statusSync(userState) {
  405. try {
  406. // 旋转
  407. if (userState.event && userState.event.rotateEvent)
  408. {
  409. const { angle, speed } = userState.event.rotateEvent
  410. , moveAnimation = this.motionType === MotionType.Run ? "Running" : "Walking";
  411. this.rotation && (
  412. this.rotation.yaw = this.rotation.yaw % 360,
  413. angle.yaw - this.rotation.yaw > 180 && (angle.yaw = 180 - angle.yaw),
  414. this.isRotating = true,
  415. await this.xAvatar.rotateTo(angle, this.rotation, moveAnimation).then(()=>{
  416. this._playAnimation("Idle", true),
  417. this.isRotating = false
  418. })
  419. )
  420. }
  421. // 行走
  422. if (userState.event && ((userState.event.points && userState.event.points.length) || 0) > 1 && !this.isSelf)
  423. {
  424. this.isMoving = true,
  425. userState.playerState.attitude && (this._motionType = userState.playerState.attitude);
  426. const moveAnimation = this.motionType === MotionType.Run ? "Running" : "Walking"
  427. , skinRoute = this._room.skin.routeList.find(l => l.areaName === this._room.currentState.areaName)
  428. , walkSpeed = ((skinRoute == null ? void 0 : skinRoute.step) || 7.5) * 30 * (25 / 30);
  429. this.position && (
  430. await this._move({
  431. start: this.position,
  432. end: userState.event.points[userState.event.points.length - 1],
  433. walkSpeed,
  434. moveAnimation: moveAnimation,
  435. inter: userState.event && userState.event.points.slice(0, -1)
  436. }).then(()=>{
  437. this.isMoving = false
  438. })
  439. )
  440. }
  441. } catch {
  442. return
  443. }
  444. }
  445. }