@yangyongtao/gaea 1.0.3 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (137) hide show
  1. package/README.md +158 -67
  2. package/package.json +28 -7
  3. package/public/Gaea.apple-touch-icon.png +0 -0
  4. package/public/Gaea.apple-touch-icon.png.import +35 -0
  5. package/public/Gaea.audio.worklet.js +211 -0
  6. package/public/Gaea.html +245 -0
  7. package/public/Gaea.icon.png +0 -0
  8. package/public/Gaea.icon.png.import +35 -0
  9. package/public/Gaea.js +782 -0
  10. package/public/Gaea.png +0 -0
  11. package/public/Gaea.png.import +35 -0
  12. package/public/Gaea.worker.js +1 -0
  13. package/public/GaeaApp.html +275 -0
  14. package/public/GaeaMethods.js +4342 -0
  15. package/public/GaeaModule.html +276 -0
  16. package/public/config.js +76 -0
  17. package/public/constants.js +185 -0
  18. package/public/favicon.ico +0 -0
  19. package/public/image/1.png +0 -0
  20. package/public/image/12-min-min.gif +0 -0
  21. package/public/image/3.png +0 -0
  22. package/public/image/labelImg//345/217/263/350/276/271.png +0 -0
  23. package/public/image/labelImg//345/267/246/350/276/271.png +0 -0
  24. package/public/image/labelImg//346/260/264/344/275/215.png +0 -0
  25. package/public/image/screenIcon//345/205/266/344/273/226.png +0 -0
  26. package/public/image/screenIcon//345/256/211/345/205/250/347/233/221/346/265/213/350/201/232/345/220/210-/347/272/242.png +0 -0
  27. package/public/image/screenIcon//345/256/211/345/205/250/347/233/221/346/265/213/350/201/232/345/220/210-/347/273/277.png +0 -0
  28. package/public/image/screenIcon//346/212/242/351/231/251/345/215/225/344/275/215.png +0 -0
  29. package/public/image/screenIcon//346/226/255/351/235/242.png +0 -0
  30. package/public/image/screenIcon//346/227/240/344/272/272/346/234/272.png +0 -0
  31. package/public/image/screenIcon//346/227/240/344/272/272/346/234/272/345/237/272/345/234/260.png +0 -0
  32. package/public/image/screenIcon//346/260/264/345/272/223/347/253/231.png +0 -0
  33. package/public/image/screenIcon//346/260/264/346/226/207/346/265/213/347/253/231.png +0 -0
  34. package/public/image/screenIcon//346/260/264/351/227/270.png +0 -0
  35. package/public/image/screenIcon//346/262/263/351/201/223/347/253/231-/346/227/240/346/225/260/346/215/256.png +0 -0
  36. package/public/image/screenIcon//346/262/263/351/201/223/347/253/231-/346/255/243/345/270/270.png +0 -0
  37. package/public/image/screenIcon//346/262/263/351/201/223/347/253/231-/350/266/205/344/277/235.png +0 -0
  38. package/public/image/screenIcon//346/262/263/351/201/223/347/253/231-/350/266/205/350/255/246.png +0 -0
  39. package/public/image/screenIcon//346/263/265/347/253/231-/347/201/260.png +0 -0
  40. package/public/image/screenIcon//346/263/265/347/253/231-/347/273/277.png +0 -0
  41. package/public/image/screenIcon//346/265/201/351/207/217/346/265/213/347/253/231.png +0 -0
  42. package/public/image/screenIcon//346/265/267/345/241/230-1/347/272/247.png +0 -0
  43. package/public/image/screenIcon//346/265/267/345/241/230-2/347/272/247.png +0 -0
  44. package/public/image/screenIcon//346/265/267/345/241/230-3/347/272/247.png +0 -0
  45. package/public/image/screenIcon//346/265/267/345/241/230-4/347/272/247.png +0 -0
  46. package/public/image/screenIcon//346/265/267/345/241/230-5/347/272/247.png +0 -0
  47. package/public/image/screenIcon//346/265/267/345/241/230-/346/251/231.png +0 -0
  48. package/public/image/screenIcon//346/265/267/345/241/230-/347/272/242.png +0 -0
  49. package/public/image/screenIcon//346/265/267/345/241/230-/347/273/277.png +0 -0
  50. package/public/image/screenIcon//346/265/267/345/241/230-/350/247/204/346/250/241/344/273/245/344/270/213.png +0 -0
  51. package/public/image/screenIcon//346/265/267/345/241/230-/351/273/204.png +0 -0
  52. package/public/image/screenIcon//346/265/267/345/241/230.png +0 -0
  53. package/public/image/screenIcon//346/275/256/344/275/215.png +0 -0
  54. package/public/image/screenIcon//346/275/256/344/275/215/347/253/231-/346/227/240/346/225/260/346/215/256.png +0 -0
  55. package/public/image/screenIcon//346/275/256/344/275/215/347/253/231-/346/255/243/345/270/270.png +0 -0
  56. package/public/image/screenIcon//346/275/256/344/275/215/347/253/231-/350/266/205/344/277/235.png +0 -0
  57. package/public/image/screenIcon//346/275/256/344/275/215/347/253/231-/350/266/205/350/255/246.png +0 -0
  58. package/public/image/screenIcon//347/211/251/350/265/204/344/273/223/345/272/223.png +0 -0
  59. package/public/image/screenIcon//347/220/203/346/234/272-/345/205/263.png +0 -0
  60. package/public/image/screenIcon//347/220/203/346/234/272-/345/274/200.png +0 -0
  61. package/public/image/screenIcon//347/220/203/346/234/272-/347/272/242.png +0 -0
  62. package/public/image/screenIcon//350/247/206/351/242/221/347/233/221/346/216/247.png +0 -0
  63. package/public/image/screenIcon//351/227/270/351/227/250-/347/201/260.png +0 -0
  64. package/public/image/screenIcon//351/227/270/351/227/250-/347/273/277.png +0 -0
  65. package/public/image/screenIcon//351/230/262/346/264/252/344/273/223/345/272/223.png +0 -0
  66. package/public/image/screenIcon//351/232/220/346/202/243.png +0 -0
  67. package/public/image/screenIcon//351/233/250/351/207/217/347/253/231-/346/265/205/350/223/235.png +0 -0
  68. package/public/image/screenIcon//351/233/250/351/207/217/347/253/231-/347/201/260.png +0 -0
  69. package/public/image/screenIcon//351/233/250/351/207/217/347/253/231-/347/262/211.png +0 -0
  70. package/public/image/screenIcon//351/233/250/351/207/217/347/253/231-/347/272/242.png +0 -0
  71. package/public/image/screenIcon//351/233/250/351/207/217/347/253/231-/347/273/277.png +0 -0
  72. package/public/image/screenIcon//351/233/250/351/207/217/347/253/231-/350/223/235.png +0 -0
  73. package/public/image/screenIcon//351/233/250/351/207/217/347/253/231-/351/273/204.png +0 -0
  74. package/public/image/screenIcon//351/233/250/351/207/217/347/253/231-/351/273/221.png +0 -0
  75. package/public/image/screenIcon//351/243/216/351/200/237.png +0 -0
  76. package/public/image/screenIcon//351/243/216/351/200/237/346/265/213/347/253/231.png +0 -0
  77. package/public/image//346/260/264/346/226/207.gif +0 -0
  78. package/public/image//350/211/262/345/270/246.png +0 -0
  79. package/public/index.html +22 -0
  80. package/public/index.js +185662 -0
  81. package/public/jsWebControl-1.0.0.min.js +31 -0
  82. package/public/jsencrypt.min.js +1 -0
  83. package/public/math/Box2.js +203 -0
  84. package/public/math/Box3.js +532 -0
  85. package/public/math/Color.js +605 -0
  86. package/public/math/ColorManagement.js +74 -0
  87. package/public/math/Cylindrical.js +61 -0
  88. package/public/math/EngineUtils.js +81 -0
  89. package/public/math/Euler.js +315 -0
  90. package/public/math/Frustum.js +162 -0
  91. package/public/math/Interpolant.js +241 -0
  92. package/public/math/Line3.js +115 -0
  93. package/public/math/MathUtils.js +345 -0
  94. package/public/math/Matrix3.js +365 -0
  95. package/public/math/Matrix4.js +858 -0
  96. package/public/math/Plane.js +205 -0
  97. package/public/math/Quaternion.js +724 -0
  98. package/public/math/Ray.js +493 -0
  99. package/public/math/Sphere.js +243 -0
  100. package/public/math/Spherical.js +86 -0
  101. package/public/math/SphericalHarmonics3.js +243 -0
  102. package/public/math/Triangle.js +299 -0
  103. package/public/math/Vector2.js +464 -0
  104. package/public/math/Vector3.js +715 -0
  105. package/public/math/Vector4.js +644 -0
  106. package/public/math/interpolants/CubicInterpolant.js +151 -0
  107. package/public/math/interpolants/DiscreteInterpolant.js +26 -0
  108. package/public/math/interpolants/LinearInterpolant.js +38 -0
  109. package/public/math/interpolants/QuaternionLinearInterpolant.js +39 -0
  110. package/public/static/Cloud.png +0 -0
  111. package/public/static/Global2.jpg +0 -0
  112. package/public/static/css/main.css +170 -0
  113. package/public/static/geojson//346/265/267/345/241/230.geojson +99 -0
  114. package/public/static/img/loading.gif +0 -0
  115. package/public/static/js/app.js +359 -0
  116. package/public/static/js/appcontroller.js +207 -0
  117. package/public/static/js/dist/game.dev.js +137 -0
  118. package/public/static/js/env.js +74 -0
  119. package/public/static/js/event/event.js +107 -0
  120. package/public/static/js/init.js +8 -0
  121. package/public/static/js/initcloudmorph.js +1 -0
  122. package/public/static/js/log.js +35 -0
  123. package/public/static/js/network/ajax.js +29 -0
  124. package/public/static/js/network/rtcp.js +172 -0
  125. package/public/static/js/network/socket.js +106 -0
  126. package/public/web-control.min.js +2 -0
  127. package/scripts/postinstall.mjs +127 -0
  128. package/src/GaeaMethods.js +68 -405
  129. package/src/components/FlyAlongRoute.vue +1 -0
  130. package/src/components/WeatherControl.vue +2 -0
  131. package/src/directives/vDrag.js +70 -0
  132. package/src/index.js +1 -8
  133. package/src/plugin.js +16 -0
  134. package/src/utils.js +10 -21
  135. package/src/vite-plugin.js +117 -0
  136. package/public/static/1111.res +0 -0
  137. package/src/config.js +0 -40
@@ -0,0 +1,74 @@
1
+ const env = (() => {
2
+ const getOS = () => {
3
+ // linux? ios?
4
+ let OSName = 'unknown';
5
+ if (navigator.appVersion.indexOf('Win') !== -1) OSName = 'win';
6
+ else if (navigator.appVersion.indexOf('Mac') !== -1) OSName = 'mac';
7
+ else if (navigator.userAgent.indexOf('Linux') !== -1) OSName = 'linux';
8
+ else if (navigator.userAgent.indexOf('Android') !== -1) OSName = 'android';
9
+ return OSName;
10
+ };
11
+
12
+ const getBrowser = () => {
13
+ let browserName = 'unknown';
14
+ if (navigator.userAgent.indexOf('Firefox') !== -1) browserName = 'firefox';
15
+ if (navigator.userAgent.indexOf('Chrome') !== -1) browserName = 'chrome';
16
+ if (navigator.userAgent.indexOf('Edge') !== -1) browserName = 'edge';
17
+ if (navigator.userAgent.indexOf('Version/') !== -1) browserName = 'safari';
18
+ if (navigator.userAgent.indexOf('UCBrowser') !== -1) browserName = 'uc';
19
+ return browserName;
20
+ };
21
+
22
+ // !to use more sophisticated approach / lib
23
+ const isPortrait = () => {
24
+ // ios / mobile app
25
+ switch (window.orientation) {
26
+ case 0:
27
+ case 180:
28
+ return true;
29
+ }
30
+
31
+ // desktop
32
+ const orientation = screen.msOrientation || screen.mozOrientation || (screen.orientation || {}).type;
33
+ return orientation === 'portrait-primary';
34
+ };
35
+
36
+ const toggleFullscreen = (enable, element) => {
37
+ const el = enable ? element : document;
38
+
39
+ if (enable) {
40
+ if (el.requestFullscreen) {
41
+ el.requestFullscreen();
42
+ } else if (el.mozRequestFullScreen) { /* Firefox */
43
+ el.mozRequestFullScreen();
44
+ } else if (el.webkitRequestFullscreen) { /* Chrome, Safari and Opera */
45
+ el.webkitRequestFullscreen();
46
+ } else if (el.msRequestFullscreen) { /* IE/Edge */
47
+ el.msRequestFullscreen();
48
+ }
49
+ } else {
50
+ if (el.exitFullscreen) {
51
+ el.exitFullscreen();
52
+ } else if (el.mozCancelFullScreen) { /* Firefox */
53
+ el.mozCancelFullScreen();
54
+ } else if (el.webkitExitFullscreen) { /* Chrome, Safari and Opera */
55
+ el.webkitExitFullscreen();
56
+ } else if (el.msExitFullscreen) { /* IE/Edge */
57
+ el.msExitFullscreen();
58
+ }
59
+ }
60
+ };
61
+
62
+ // window.addEventListener('resize', fixScreenLayout);
63
+ // window.addEventListener('orientationchange', fixScreenLayout);
64
+ // document.addEventListener('DOMContentLoaded', () => fixScreenLayout(), false);
65
+
66
+ return {
67
+ getOs: getOS,
68
+ getBrowser: getBrowser,
69
+ display: () => ({
70
+ isPortrait: isPortrait,
71
+ toggleFullscreen: toggleFullscreen,
72
+ })
73
+ }
74
+ })(document, log, navigator, screen, window);
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Event publishing / subscribe module.
3
+ * Just a simple observer pattern.
4
+ * @version 1
5
+ */
6
+ const event = (() => {
7
+ const topics = {};
8
+
9
+ return {
10
+ /**
11
+ * Subscribes onto some event.
12
+ *
13
+ * @param topic The name of the event.
14
+ * @param listener A callback function to call during the event.
15
+ * @param order A number in a queue of event handlers to run callback in ordered manner.
16
+ * @returns {{unsub: unsub}} The function to remove this subscription.
17
+ * @example
18
+ * const sub01 = event.sub('rapture', () => {a}, 1)
19
+ * ...
20
+ * sub01.unsub()
21
+ */
22
+ sub: (topic, listener, order) => {
23
+ if (!topics[topic]) topics[topic] = [];
24
+
25
+ // order handling stuff
26
+ const value = { order: order || 0, listener: listener };
27
+ topics[topic].push(value);
28
+ topics[topic].sort((a, b) => a.order - b.order);
29
+ const index = topics[topic].indexOf(value);
30
+
31
+ return {
32
+ unsub: () => {
33
+ topics[topic].splice(index, 1);
34
+ },
35
+ };
36
+ },
37
+
38
+ /**
39
+ * Publishes some event for handling.
40
+ *
41
+ * @param topic The name of the event.
42
+ * @param data Additional data for the event handling.
43
+ * Because of compatibility we have to use a dumb obj wrapper {a: a, b: b} for params instead of (topic, ...data).
44
+ * @example
45
+ * event.pub('rapture', {time: now()})
46
+ */
47
+ pub: (topic, data) => {
48
+ if (!topics[topic] || topics[topic].length < 1) return;
49
+
50
+ topics[topic].forEach((listener) => {
51
+ listener.listener(data !== undefined ? data : {});
52
+ });
53
+ },
54
+ };
55
+ })();
56
+
57
+ // events
58
+ const LATENCY_CHECK_REQUESTED = "latencyCheckRequested";
59
+ const PING_REQUEST = "pingRequest";
60
+ const PING_RESPONSE = "pingResponse";
61
+
62
+ const GAME_ROOM_AVAILABLE = "gameRoomAvailable";
63
+ const GAME_SAVED = "gameSaved";
64
+ const GAME_LOADED = "gameLoaded";
65
+ const GAME_PLAYER_IDX = "gamePlayerIndex";
66
+
67
+ const CONNECTION_READY = "connectionReady";
68
+ const CONNECTION_CLOSED = "connectionClosed";
69
+
70
+ const CHAT = "chat";
71
+ const NUM_PLAYER = "num_player";
72
+
73
+ const MEDIA_STREAM_INITIALIZED = "mediaStreamInitialized";
74
+ const MEDIA_STREAM_SDP_AVAILABLE = "mediaStreamSdpAvailable";
75
+ const MEDIA_STREAM_CANDIDATE_ADD = "mediaStreamCandidateAdd";
76
+ const MEDIA_STREAM_CANDIDATE_FLUSH = "mediaStreamCandidateFlush";
77
+ const MEDIA_STREAM_READY = "mediaStreamReady";
78
+
79
+ const GAMEPAD_CONNECTED = "gamepadConnected";
80
+ const GAMEPAD_DISCONNECTED = "gamepadDisconnected";
81
+
82
+ const MENU_HANDLER_ATTACHED = "menuHandlerAttached";
83
+ const MENU_PRESSED = "menuPressed";
84
+ const MENU_RELEASED = "menuReleased";
85
+
86
+ const KEY_PRESSED = "keyPressed";
87
+ const KEY_RELEASED = "keyReleased";
88
+ const KEY_STATE_UPDATED = "keyStateUpdated";
89
+ const KEYBOARD_TOGGLE_FILTER_MODE = "keyboardToggleFilterMode";
90
+ const KEYBOARD_KEY_PRESSED = "keyboardKeyPressed";
91
+ const AXIS_CHANGED = "axisChanged";
92
+ const CONTROLLER_UPDATED = "controllerUpdated";
93
+
94
+ const MOUSE_DOWN = "mouseDown";
95
+ const MOUSE_UP = "mouseUp";
96
+ const MOUSE_MOVE = "mouseMove";
97
+ const MOUSE_WHEEL = "mouseWheel";
98
+
99
+ const DPAD_TOGGLE = "dpadToggle";
100
+ const STATS_TOGGLE = "statsToggle";
101
+ const HELP_OVERLAY_TOGGLED = "helpOverlayToggled";
102
+
103
+ const SETTINGS_CHANGED = "settingsChanged";
104
+ const SETTINGS_CLOSED = "settingsClosed";
105
+
106
+ const UPDATE_APP_LIST = "updateapplist";
107
+ const CLIENT_INIT = "clientInit";
@@ -0,0 +1,8 @@
1
+ document.getElementById('connectButton').addEventListener('click', function() {
2
+ const serverAddress = document.getElementById('serverAddress').value;
3
+ // if (!serverAddress) {
4
+ // alert('请输入服务器地址');
5
+ // return;
6
+ // }
7
+ socket.connect(location.protocol, serverAddress);
8
+ });
@@ -0,0 +1 @@
1
+ socket.connect(location.protocol, `${location.host}/wscloudmorph`);
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Logging module.
3
+ *
4
+ * @version 2
5
+ */
6
+ const log = (() => {
7
+ const noop = () => ({})
8
+
9
+ const _log = {
10
+ ASSERT: 1,
11
+ ERROR: 2,
12
+ WARN: 3,
13
+ INFO: 4,
14
+ DEBUG: 5,
15
+ TRACE: 6,
16
+
17
+ DEFAULT: 5,
18
+
19
+ set level(level) {
20
+ this.assert = level >= this.ASSERT ? console.assert.bind(window.console) : noop;
21
+ this.error = level >= this.ERROR ? console.error.bind(window.console) : noop;
22
+ this.warn = level >= this.WARN ? console.warn.bind(window.console) : noop;
23
+ this.info = level >= this.INFO ? console.info.bind(window.console) : noop;
24
+ this.debug = level >= this.DEBUG ? console.debug.bind(window.console) : noop;
25
+ this.trace = level >= this.TRACE ? console.log.bind(window.console) : noop;
26
+ this._level = level;
27
+ },
28
+ get level() {
29
+ return this._level;
30
+ }
31
+ }
32
+ _log.level = _log.DEFAULT;
33
+
34
+ return _log
35
+ })(console, window);
@@ -0,0 +1,29 @@
1
+ /**
2
+ * AJAX request module.
3
+ * @version 1
4
+ */
5
+ const ajax = (() => {
6
+ const defaultTimeout = 10000;
7
+
8
+ return {
9
+ fetch: (url, options, timeout = defaultTimeout) => new Promise((resolve, reject) => {
10
+ const controller = new AbortController();
11
+ const signal = controller.signal;
12
+ const allOptions = Object.assign({}, options, signal);
13
+
14
+ // fetch(url, {...options, signal})
15
+ fetch(url, allOptions)
16
+ .then(resolve, () => {
17
+ controller.abort();
18
+ return reject
19
+ });
20
+
21
+ // auto abort when a timeout reached
22
+ setTimeout(() => {
23
+ controller.abort();
24
+ reject();
25
+ }, timeout);
26
+ }),
27
+ defaultTimeoutMs: () => defaultTimeout
28
+ }
29
+ })();
@@ -0,0 +1,172 @@
1
+ /**
2
+ * RTCP connection module.
3
+ * @version 1
4
+ */
5
+ const rtcp = (() => {
6
+ let connection;
7
+ let inputChannel;
8
+ let mediaStream;
9
+ let candidates = Array();
10
+ let isAnswered = false;
11
+ let isFlushing = false;
12
+
13
+ let connected = false;
14
+ let inputReady = false;
15
+
16
+ const start = (iceservers) => {
17
+ log.info("[rtcp] <- received STUN/TURN config from the worker", iceservers);
18
+
19
+ let conf
20
+ if (iceservers !== "") {
21
+ conf = {
22
+ iceservers: {urls: iceservers}
23
+ }
24
+ }
25
+
26
+ connection = conf ? new RTCPeerConnection(conf) : new RTCPeerConnection();
27
+
28
+ mediaStream = new MediaStream();
29
+
30
+ connection.ondatachannel = (e) => {
31
+ log.debug(`[rtcp] ondatachannel: ${e.channel.label}`);
32
+ inputChannel = e.channel;
33
+ inputChannel.onopen = () => {
34
+ log.debug("[rtcp] the input channel has opened");
35
+ inputReady = true;
36
+ event.pub(CONNECTION_READY);
37
+ };
38
+ inputChannel.onclose = () => {
39
+ inputReady = false;
40
+ log.debug("[rtcp] the input channel has closed");
41
+ }
42
+ };
43
+
44
+ connection.oniceconnectionstatechange = ice.onIceConnectionStateChange;
45
+ connection.onicegatheringstatechange = ice.onIceStateChange;
46
+ connection.onicecandidate = ice.onIcecandidate;
47
+ connection.ontrack = (event) => {
48
+ mediaStream.addTrack(event.track);
49
+ };
50
+
51
+ socket.send({type: "initwebrtc"});
52
+ };
53
+
54
+ const ice = (() => {
55
+ let timeForIceGathering;
56
+ const ICE_TIMEOUT = 2000;
57
+
58
+ return {
59
+ onIcecandidate: (event) => {
60
+ // this trigger when setRemoteDesc success
61
+ // send any candidate to worker
62
+ if (event.candidate != null) {
63
+ const candidate = JSON.stringify(event.candidate);
64
+ log.info('[rtcp] got ice candidate', candidate);
65
+ socket.send({type: "candidate", data: btoa(candidate)});
66
+ }
67
+ },
68
+ onIceStateChange: (event) => {
69
+ switch (event.target.iceGatheringState) {
70
+ case "gathering":
71
+ log.info("[rtcp] ice gathering");
72
+ timeForIceGathering = setTimeout(() => {
73
+ log.info(`[rtcp] ice gathering was aborted due to timeout ${ICE_TIMEOUT}ms`);
74
+ }, ICE_TIMEOUT);
75
+ break;
76
+ case "complete":
77
+ log.info("[rtcp] ice gathering completed");
78
+ if (timeForIceGathering) {
79
+ clearTimeout(timeForIceGathering);
80
+ }
81
+ }
82
+ },
83
+ onIceConnectionStateChange: () => {
84
+ log.info(`[rtcp] <- iceConnectionState: ${connection.iceConnectionState}`);
85
+ switch (connection.iceConnectionState) {
86
+ case "connected": {
87
+ log.info("[rtcp] connected...");
88
+ connected = true;
89
+ break;
90
+ }
91
+ case "disconnected": {
92
+ log.info("[rtcp] disconnected...");
93
+ connected = false;
94
+ event.pub(CONNECTION_CLOSED);
95
+ break;
96
+ }
97
+ case "failed": {
98
+ log.error("[rtcp] connection failed, retry...");
99
+ connected = false;
100
+ connection
101
+ .createOffer({iceRestart: true})
102
+ .then((description) =>
103
+ connection.setLocalDescription(description).catch(log.error)
104
+ )
105
+ .catch(log.error);
106
+ break;
107
+ }
108
+ }
109
+ },
110
+ };
111
+ })();
112
+
113
+ return {
114
+ start: start,
115
+ setRemoteDescription: async (data, media) => {
116
+ try {
117
+ if (isAnswered) {
118
+ log.warn("[rtcp] Already answered, ignoring duplicate offer");
119
+ return;
120
+ }
121
+
122
+ const offer = new RTCSessionDescription(JSON.parse(atob(data)));
123
+
124
+ if (connection.signalingState === "stable" || connection.signalingState === "have-remote-offer") {
125
+ await connection.setRemoteDescription(offer);
126
+ log.info("[rtcp] Remote description set successfully");
127
+
128
+ const answer = await connection.createAnswer();
129
+ answer.sdp = answer.sdp.replace(/(a=fmtp:111 .*)/g, "$1;stereo=1;sprop-stereo=1");
130
+
131
+ await connection.setLocalDescription(answer);
132
+ isAnswered = true;
133
+ log.info("[rtcp] Local description set successfully");
134
+
135
+ event.pub(MEDIA_STREAM_CANDIDATE_FLUSH);
136
+ socket.send({type: "answer", data: btoa(JSON.stringify(answer))});
137
+ media.srcObject = mediaStream;
138
+ } else {
139
+ log.warn(`[rtcp] Invalid signaling state for setting remote description: ${connection.signalingState}`);
140
+ return;
141
+ }
142
+ } catch (error) {
143
+ log.error("[rtcp] Error in setRemoteDescription:", error);
144
+ isAnswered = false;
145
+ throw error;
146
+ }
147
+ },
148
+ addCandidate: (data) => {
149
+ if (data === "") {
150
+ event.pub(MEDIA_STREAM_CANDIDATE_FLUSH);
151
+ } else {
152
+ candidates.push(data);
153
+ }
154
+ },
155
+ flushCandidate: () => {
156
+ if (isFlushing || !isAnswered) return;
157
+ isFlushing = true;
158
+ candidates.forEach((data) => {
159
+ const d = atob(data);
160
+ const candidate = new RTCIceCandidate(JSON.parse(d));
161
+ log.debug("[rtcp] add candidate", d);
162
+ connection.addIceCandidate(candidate).catch(log.error);
163
+ });
164
+ isFlushing = false;
165
+ },
166
+ input: (data) => {
167
+ if (inputChannel) inputChannel.send(data);
168
+ },
169
+ isConnected: () => connected,
170
+ isInputReady: () => inputReady,
171
+ };
172
+ })(event, socket, log);
@@ -0,0 +1,106 @@
1
+ /**
2
+ * WebSocket connection module.
3
+ *
4
+ * Needs init() call.
5
+ *
6
+ * @version 1
7
+ */
8
+ const socket = (() => {
9
+ const pingIntervalMs = 2000; // 2 secs
10
+ let conn;
11
+ let curPacketId = "";
12
+
13
+ const connect = (protocol, addr) => {
14
+ const address = `${protocol !== "https:" ? "ws" : "wss"}://${addr}/ws`;
15
+ console.info(`[ws] connecting to ${address}`);
16
+ conn = new WebSocket(address);
17
+
18
+ let pingInterval;
19
+ conn.onerror = (error) => log.error(`[ws] ${error}`);
20
+ conn.onclose = () => log.info("[ws] closed");
21
+
22
+ conn.onmessage = (response) => {
23
+ const data = JSON.parse(response.data);
24
+ const message = data.type;
25
+
26
+ if (message !== "heartbeat")
27
+ log.debug(`[ws] <- message '${message}' `, data);
28
+
29
+ switch (message) {
30
+ case "init":
31
+ event.pub(MEDIA_STREAM_INITIALIZED, { stunturn: data.data });
32
+ break;
33
+ case "offer":
34
+ event.pub(MEDIA_STREAM_SDP_AVAILABLE, { sdp: data.data });
35
+ break;
36
+ case "candidate":
37
+ event.pub(MEDIA_STREAM_CANDIDATE_ADD, { candidate: data.data });
38
+ break;
39
+ case "heartbeat":
40
+ event.pub(PING_RESPONSE);
41
+ break;
42
+ case "checkLatency":
43
+ curPacketId = data.packet_id;
44
+ const addresses = data.data.split(",");
45
+ event.pub(LATENCY_CHECK_REQUESTED, {
46
+ packetId: curPacketId,
47
+ addresses: addresses,
48
+ });
49
+ break;
50
+ case "CHAT":
51
+ event.pub(CHAT, { chatrow: data.data });
52
+ break;
53
+ case "NUMPLAYER":
54
+ event.pub(NUM_PLAYER, { numplayers: data.data });
55
+ break;
56
+ case "INIT":
57
+ event.pub(CLIENT_INIT, { data: data.data });
58
+ break;
59
+ case "UPDATEAPPLIST":
60
+ event.pub(UPDATE_APP_LIST, { data: data.data });
61
+ break;
62
+ }
63
+ };
64
+
65
+ return new Promise((resolve, reject) => {
66
+ conn.onopen = () => {
67
+ log.info("[ws] <- open connection");
68
+ log.info(`[ws] -> setting ping interval to ${pingIntervalMs}ms`);
69
+ pingInterval = setInterval(ping, pingIntervalMs);
70
+ resolve(conn);
71
+ };
72
+ conn.onerror = (error) => {
73
+ log.error(`[ws] ${error}`);
74
+ reject(error);
75
+ };
76
+ });
77
+ };
78
+
79
+ const ping = () => {
80
+ const time = Date.now();
81
+ send({ id: "heartbeat", data: time.toString() });
82
+ event.pub(PING_REQUEST, { time: time });
83
+ };
84
+
85
+ const send = (data) => {
86
+ if (conn && conn.readyState === WebSocket.OPEN) {
87
+ conn.send(JSON.stringify(data));
88
+ } else {
89
+ log.error("WebSocket is not open. Current state:", conn ? conn.readyState : "not initialized");
90
+ }
91
+ };
92
+
93
+ const latency = (workers, packetId) =>
94
+ send({
95
+ id: "checkLatency",
96
+ data: JSON.stringify(workers),
97
+ packet_id: packetId,
98
+ });
99
+
100
+ return {
101
+ send: send,
102
+ latency: latency,
103
+ connect: connect,
104
+ };
105
+ })(event, log);
106
+ window.socketInstance = socket