Socket.js 9.0 KB

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