Socket.js 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. import Heartbeat from "./Heartbeat.js"
  2. import Timeout from "./Timeout.js"
  3. import InitNetworkTimeoutError from "./error/InitNetworkTimeoutError.js"
  4. import {reporter} from "./Reporter.js"
  5. import util from "./util.js"
  6. import InternalError from "./error/InternalError.js"
  7. import Logger from "./Logger.js"
  8. const logger = new Logger('ws')
  9. export default class Socket extends EventEmitter {
  10. constructor(e) {
  11. super();
  12. E(this, "_ws");
  13. E(this, "_openTimer");
  14. E(this, "connected", !1);
  15. E(this, "_hasTimeout", !1);
  16. E(this, "heartbeat");
  17. E(this, "latency", (e,t)=>this.send({
  18. id: "checkLatency",
  19. data: JSON.stringify(e),
  20. packet_id: t
  21. }));
  22. E(this, "send", e=>{
  23. if (this.wsNoReady())
  24. return;
  25. const t = JSON.stringify(e);
  26. e.id !== "heartbeat" && logger.info("send ws frame", t),
  27. this._ws.send(t)
  28. }
  29. );
  30. E(this, "startGame", ()=>{
  31. const {roomId: e, userId: t, avatarId: r, skinId: n, role: o, avatarComponents: a, versionId: s, rotationRenderType: l, isAllSync: u, nickname: c, avatarScale: h, appId: f, camera: d, player: _, firends: g, syncByEvent: m, areaName: v, attitude: y, pathName: b, person: T, roomTypeId: C="", syncToOthers: A, hasAvatar: S, prioritySync: P, extra: R={}, removeWhenDisconnected: M} = this.network.room.currentNetworkOptions;
  32. R.removeWhenDisconnected = M;
  33. const x = {
  34. id: "start",
  35. room_id: e,
  36. user_id: t,
  37. trace_id: util.uuid(),
  38. data: JSON.stringify({
  39. avatar_components: JSON.stringify(a),
  40. avatar_id: r,
  41. skin_id: n,
  42. is_host: o ? o == "host" : !0,
  43. skin_data_version: n !== void 0 && s !== void 0 ? n + s : void 0,
  44. rotation_render_type: l,
  45. is_all_sync: u,
  46. nick_name: encodeURIComponent(c || ""),
  47. app_id: f,
  48. camera: d,
  49. player: _,
  50. person: T,
  51. firends: JSON.stringify(g),
  52. sync_by_event: m,
  53. area_name: v,
  54. path_name: b,
  55. attitude: y,
  56. room_type_id: C,
  57. syncToOthers: A,
  58. hasAvatar: S,
  59. avatarSize: h,
  60. prioritySync: P,
  61. extra: JSON.stringify(R)
  62. })
  63. };
  64. this.send(x),
  65. logger.warn("startGame", le(oe({}, x), {
  66. data: JSON.parse(x.data)
  67. }))
  68. }
  69. );
  70. this.network = e,
  71. this.heartbeat = new Heartbeat({
  72. ping: t=>{
  73. var r;
  74. if (!this.connected) {
  75. this.heartbeat.stop(),
  76. (r = e.room.stats) == null || r.assign({
  77. rtt: 0
  78. });
  79. return
  80. }
  81. this.send({
  82. id: "heartbeat",
  83. data: t
  84. })
  85. }
  86. ,
  87. pong(t) {
  88. var r;
  89. (r = e.room.stats) == null || r.assign({
  90. rtt: t
  91. })
  92. }
  93. })
  94. }
  95. get connection() {
  96. return this._ws
  97. }
  98. start() {
  99. this._hasTimeout = !1;
  100. const e = this.getAddress();
  101. logger.info(`connecting to ${e}`);
  102. const t = Date.now();
  103. this._ws = new WebSocket(e),
  104. this._openTimer = new Timeout(()=>{
  105. const r = `Failed to open websocket in ${DEFAULT_OPEN_TIMEOUT_MS} ms`;
  106. this._hasTimeout = !0,
  107. this.emit("socketClosed", new InitNetworkTimeoutError(r))
  108. }
  109. ,DEFAULT_OPEN_TIMEOUT_MS),
  110. this._ws.onopen = ()=>{
  111. var r;
  112. (r = this._openTimer) == null || r.clear(),
  113. this.connected = !0,
  114. this.heartbeat.start(),
  115. this.network.room.currentNetworkOptions.reconnect || (logger.infoAndReportMeasurement({
  116. metric: "wsOpenedAt",
  117. group: "joinRoom",
  118. startTime: this.network.room._startTime
  119. }),
  120. logger.infoAndReportMeasurement({
  121. metric: "wsOpenedCost",
  122. group: "joinRoom",
  123. startTime: t
  124. }))
  125. }
  126. ,
  127. this.handleWSEvent()
  128. }
  129. getAddress() {
  130. const {wsServerUrl: e, reconnect: t, sessionId: r, token: n, roomId: o, userId: a, pageSession: s} = this.network.room.currentNetworkOptions
  131. , l = this.network.room.skinId;
  132. let u = e;
  133. t && (u = u + `?reconnect=true&lastSessionID=${r}`);
  134. const c = `userId=${a}&roomId=${o}&pageSession=${s}` + (this.network.room.isHost ? `&skinId=${l}` : "") + (n ? `&token=${n}` : "");
  135. return u = u.indexOf("?") > -1 ? u + "&" + c : u + "?" + c,
  136. u
  137. }
  138. handleWSEvent() {
  139. const e = this._ws;
  140. e.addEventListener("error", t=>{
  141. this.connected = !1,
  142. logger.error("webscoket error", t),
  143. this.emit("socketClosed", new InternalError("connect to address error: " + this.network.room.currentNetworkOptions.wsServerUrl))
  144. }
  145. ),
  146. e.addEventListener("close", t=>{
  147. this.connected = !1,
  148. this._onClose(t)
  149. }
  150. ),
  151. e.addEventListener("message", t=>{
  152. if (!t || this._hasTimeout || !this.connected)
  153. return;
  154. let r = null;
  155. try {
  156. r = JSON.parse(t.data)
  157. } catch (o) {
  158. logger.error(o);
  159. return
  160. }
  161. if (!r)
  162. return;
  163. const n = r.id;
  164. if (!!n)
  165. switch (n !== "heartbeat" && logger.info(`receive ws frame: ${t.data}`),
  166. n) {
  167. case "fail":
  168. break;
  169. case "init":
  170. try {
  171. const o = r.data.slice(-37, -1);
  172. reporter.updateBody({
  173. serverSession: o
  174. })
  175. } catch (o) {
  176. console.error(o)
  177. }
  178. this.network.rtcp.start();
  179. break;
  180. case "heartbeat":
  181. this.heartbeat.pong(r.data);
  182. break;
  183. case "offer":
  184. this.network.rtcp.setRemoteDescription(r.data, this.network.stream.el);
  185. break;
  186. case "ice_candidate":
  187. this.network.rtcp.addCandidate(r.data);
  188. break;
  189. case "start":
  190. this.emit("gameRoomAvailable", r);
  191. break;
  192. case "error":
  193. try {
  194. const {Code: o, Msg: a} = JSON.parse(r.data);
  195. if (o) {
  196. if (o == 3003)
  197. return this.emit("socketClosed", new TokenExpiredError);
  198. if (authenticationErrorCodes.indexOf(o) > -1)
  199. return this.emit("socketClosed", new AuthenticationError("\u9274\u6743\u9519\u8BEF:" + a));
  200. {
  201. const s = util.getErrorByCode(o);
  202. this.emit("socketClosed", new s(a))
  203. }
  204. }
  205. } catch (o) {
  206. logger.error(o),
  207. this.emit("socketClosed", new InternalError(r.data))
  208. }
  209. break;
  210. case "checkLatency":
  211. {
  212. const o = r.packet_id
  213. , a = r.data.split(",");
  214. this.onLatencyCheck({
  215. packetId: o,
  216. addresses: a
  217. });
  218. break
  219. }
  220. default:
  221. logger.warn("unkown ws message type", n, r)
  222. }
  223. }
  224. )
  225. }
  226. onLatencyCheck(e) {
  227. const t = [...new Set(e.addresses || [])];
  228. Promise.all(t.map(r=>({
  229. [r]: 9999
  230. }))).then(r=>{
  231. const n = Object.assign({}, ...r);
  232. this.latency(n, e.packetId)
  233. }
  234. )
  235. }
  236. wsNoReady() {
  237. return this._ws.readyState == WebSocket.CLOSED || this._ws.readyState == WebSocket.CLOSING || this._ws.readyState == WebSocket.CONNECTING
  238. }
  239. prepareReconnect() {
  240. this._close({
  241. code: WS_CLOSE_RECONNECT,
  242. reason: "reconnect"
  243. })
  244. }
  245. _onClose({code: e, reason: t}) {
  246. this._openTimer && this._openTimer.clear(),
  247. logger.warn(`ws closed: ${e} ` + t),
  248. [WS_CLOSE_RECONNECT, WS_CLOSE_NORMAL].includes(e) || this.emit("socketClosed", new InternalError("Websocket error"))
  249. }
  250. _close({code: e, reason: t}) {
  251. var r;
  252. (r = this._ws) == null || r.close(e, t)
  253. }
  254. quit() {
  255. this._close({
  256. code: WS_CLOSE_NORMAL,
  257. reason: "quit"
  258. })
  259. }
  260. }