Socket.js 9.7 KB

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